Skip to content

feat(plugin): environment variable passing to plugins (Phase 3)#191

Open
gwpl wants to merge 6 commits intosteipete:mainfrom
VariousForks:i188-external-plugin-discovery-phase3-env-vars
Open

feat(plugin): environment variable passing to plugins (Phase 3)#191
gwpl wants to merge 6 commits intosteipete:mainfrom
VariousForks:i188-external-plugin-discovery-phase3-env-vars

Conversation

@gwpl
Copy link

@gwpl gwpl commented Feb 6, 2026

Summary

Phase 3 of external plugin architecture (Issue #188). Builds on Phase 1 (#189) and Phase 2 (#190).

Passes GOG_* environment variables to plugins for integration with core CLI.

Environment Variables

Variable Description Example
GOG_CORE_VERSION Version of gog CLI 0.9.0
GOG_CORE_PATH Path to gog binary /usr/bin/gog
GOG_CONFIG_PATH Shared config file ~/.config/gogcli/config.json
GOG_PLUGIN_NAME Plugin binary name gog-docs-headings
GOG_PLUGIN_INVOKED_AS User's command gog docs headings
GOG_COLOR Color preference auto, always, never
GOG_OUTPUT_FORMAT Output format json, plain

Example Plugin Using Env Vars

#!/bin/bash
# Get the config path
config_path="$GOG_CONFIG_PATH"

# Check output format
if [[ "$GOG_OUTPUT_FORMAT" == "json" ]]; then
    echo '{"message": "hello"}'
else
    echo "Hello!"
fi

# Call back to core CLI
"$GOG_CORE_PATH" version

Example Output

$ gog env test
GOG Environment Variables:
GOG_CONFIG_PATH=/home/user/.config/gogcli/config.json
GOG_CORE_PATH=/usr/bin/gog
GOG_CORE_VERSION=0.9.0
GOG_PLUGIN_INVOKED_AS=gog env test
GOG_PLUGIN_NAME=gog-env-test

Changes

  • internal/cmd/external.go - buildPluginEnv(), setEnvVar(), updated execExternal()
  • internal/cmd/external_test.go - Tests for env var construction

Test plan

  • Unit tests for setEnvVar (add, override)
  • Unit tests for buildPluginEnv (expected vars present)
  • Unit tests for findExternalCommandWithMatched (returns matched args)
  • Manual test with shell script plugin printing env vars

Dependencies

Depends on Phase 1 (#189) and Phase 2 (#190)


Part of #188

Generated with Claude Code

Co-Authored-By: Claude Opus 4.5 noreply@anthropic.com

gwpl and others added 6 commits February 6, 2026 13:06
Document Phase 1 MVP design for cargo/git-style external commands.
Key design decisions with rationale:

* Post-parse fallback (Option B): Built-in commands always take
  precedence over external plugins, preventing shadowing
* Longest-first matching: More specific plugins win over generic ones
* gog-* prefix: Matches CLI binary name (git/cargo convention)

Part of steipete#188

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement cargo/git-style external command discovery:

* Search PATH for gog-{subcommands} binaries
* Longest-first (greedy) matching: gog-docs-headings wins over gog-docs
* Pass remaining args to plugin
* Use syscall.Exec for true process replacement

Design rationale documented in code comments:
- Longest-first: More specific plugins take precedence
- gog-* prefix: Matches CLI binary name (following git/cargo)

Unit tests document and verify the matching behavior.

Part of steipete#188

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Hook external command discovery into Kong parse error handling:

* Try external command AFTER Kong parsing fails (post-parse fallback)
* If found: exec replaces current process
* If not found: return original Kong error

Design choice: Post-parse fallback (Option B)
Why: Built-in commands always take precedence over external plugins.
This prevents accidental or malicious shadowing of core functionality
and follows the git/cargo convention.

Part of steipete#188

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add infrastructure for Phase 2 help integration:

* DiscoverExternalPlugins(): Scan PATH for all gog-* binaries
* FetchOneliners(): Query plugins with --help-oneliner flag
* GroupPluginsByTopLevel(): Group plugins by first subcommand
* ExternalPlugin struct with CommandName() helper

Protocol: Plugins respond to --help-oneliner with single line (≤80 chars).
Short 100ms timeout keeps help responsive; unresponsive plugins get no description.

Design notes in code:
- First PATH occurrence wins (matches exec.LookPath behavior)
- Plugins sorted alphabetically for consistent display

Unit tests document grouping and command name behavior.

Part of steipete#188

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Display discovered plugins in a "Plugins:" section after "Commands:".

Features:
* Lazy discovery: only scans PATH when help is requested
* Aligned output: plugin descriptions line up for readability
* Colorized: uses same styling as built-in commands
* Root help only: plugins not shown in subcommand help

Example output:
  Plugins:
    docs headings    List document headings with hierarchy and URLs
    hello            Say hello (example plugin)

Part of steipete#188

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Plugins now receive GOG_* environment variables for integration:

* GOG_CORE_VERSION: Version of the gog CLI
* GOG_CORE_PATH: Path to gog binary (for callbacks)
* GOG_CONFIG_PATH: Path to shared config file
* GOG_PLUGIN_NAME: Binary name being executed
* GOG_PLUGIN_INVOKED_AS: How user invoked (e.g., "gog docs headings")
* GOG_COLOR: Color preference if set
* GOG_OUTPUT_FORMAT: Output format if --json/--plain specified

Why env vars: Enables plugins to share configuration, maintain
consistent output formatting, and call back to core CLI.

Unit tests document setEnvVar and buildPluginEnv behavior.

Part of steipete#188

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant