Skip to content

feat(hosts): add GitHub Copilot CLI as a first-class host#1436

Closed
andrey-esipov wants to merge 1 commit into
garrytan:mainfrom
andrey-esipov:copilot-cli-host
Closed

feat(hosts): add GitHub Copilot CLI as a first-class host#1436
andrey-esipov wants to merge 1 commit into
garrytan:mainfrom
andrey-esipov:copilot-cli-host

Conversation

@andrey-esipov
Copy link
Copy Markdown

@andrey-esipov andrey-esipov commented May 11, 2026

Summary

Adds support for installing gstack as flat .agent.md files under ~/.copilot/agents/, per Copilot CLI's custom agents schema. After ./setup --host copilot, all 46 gstack skills are discoverable via copilot --agent <name>.

Purely additive — existing hosts (Codex, Cursor, Factory, OpenCode, Kiro, Slate, OpenClaw, Hermes, GBrain) are untouched.

What changed

  • New outputLayout?: 'per-skill-dir' | 'flat-agent-md' field on HostConfig. Default is 'per-skill-dir' (every existing host). Copilot uses 'flat-agent-md' to emit one file per skill, flat under <hostSubdir>/agents/. Copilot CLI does not recurse into subdirectories under ~/.copilot/agents/, so the flat layout is required.
  • processExternalHost branches on the new field to write to <hostSubdir>/agents/<name>.agent.md instead of the default <hostSubdir>/skills/<name>/SKILL.md.
  • hosts/copilot.ts — new HostConfig:
    • Frontmatter allowlist (name, description)
    • Injects target: github-copilot and tools: [\"*\"] per the Copilot agents schema
    • skipSkills: ['codex', 'copilot'] — matches every external host's codex skip, plus self-skip to avoid future recursion if a /copilot skill is ever added to the source tree
    • Suppressed resolvers match the codex pattern (DESIGN_OUTSIDE_VOICES, ADVERSARIAL_STEP, CODEX_SECOND_OPINION, CODEX_PLAN_REVIEW, REVIEW_ARMY, GBRAIN_CONTEXT_LOAD, GBRAIN_SAVE_RESULTS)
  • setup — adds INSTALL_COPILOT flag, build trigger, install block. Symlinks generated gstack-*.agent.md files into ~/.copilot/agents/, creates ~/.copilot/gstack/ runtime root with bin/, browse-dist/, gstack-upgrade/, ETHOS.md symlinks for `$GSTACK_ROOT` resolution.
  • .gitignore — adds .copilot/ for generated agent files.
  • Teststest/host-config.test.ts and test/gen-skill-docs.test.ts updated:
    • Parameterized smoke tests made layout-aware (handle both per-skill-dir and flat-agent-md)
    • Host count assertion updated 10 → 11
    • --host all test asserts the correct output subdir per host
    • The pass-through case in setup includes `copilot`

Verification (macOS, on this branch)

  • `./setup --host copilot` → produces 46 agents under `~/.copilot/agents/`
  • `copilot --agent ` lists every gstack skill (autoplan, qa, review, codex, design-shotgun, …)
  • `bun test test/host-config.test.ts test/gen-skill-docs.test.ts` → 459 pass, 0 fail
  • `bun run gen:skill-docs --host all` → all 10 existing host outputs + new `.copilot/agents/` generate cleanly; `--dry-run` reports FRESH for every host on a re-run

Test plan

  • All existing host outputs unchanged on `--host all`
  • `--host copilot` emits one `.agent.md` per skill (excluding skipSkills)
  • Copilot CLI discovers all agents via `copilot --agent `
  • Frontmatter is valid YAML (parsed by Copilot CLI without warning)
  • Parameterized smoke tests pass for all 10 external hosts including copilot
  • `./setup --host copilot` is idempotent (re-run preserves state)
  • CI on Linux + Windows (need maintainer to trigger)

Notes for reviewers

  • The /codex gstack skill in this repo wraps the OpenAI `codex` CLI binary. The copilot host `skipSkills: ['codex']` for the same reason every external host already skips it: that skill wraps a binary, and there's no use for it on a Copilot CLI install.
  • The setup script prints a reminder to set `export GSTACK_ROOT=$HOME/.copilot/gstack` because agent prompts reference `$GSTACK_ROOT/bin/...` for support tooling. Could be replaced by Copilot CLI's `secret-env-vars` in a follow-up if there's appetite.
  • I'm a happy gstack user; this is my first upstream contribution. Happy to iterate on scope, naming (`outputLayout` vs `outputFormat` vs something else), or split the change differently if that lands better.

Sources:

🤖 Generated with Claude Code


View in Codesmith
Need help on this PR? Tag @codesmith with what you need.

  • Let Codesmith autofix CI failures and bot reviews

@andrey-esipov
Copy link
Copy Markdown
Author

Self-review pass via gstack /review skill — applied 3 of 5 fixes inline (force-pushed 60c724f2). Two remain as known follow-ups, noted below.

Applied

  • setup — clean stale gstack-*.agent.md symlinks in ~/.copilot/agents/ before relinking, so renamed/removed skills don't leave orphans across reruns.
  • hosts/copilot.ts — dropped target: github-copilot from extraFields. Generated agents now default to target: both (VS Code Copilot + Copilot CLI), broader reach for the same files.
  • hosts/copilot.ts — fixed pathRewrites to point all three .claude/skills* patterns at $GSTACK_ROOT (the actual runtime root for Copilot installs), instead of an unbuilt .copilot/skills/gstack/ path.

Verified bun test test/host-config.test.ts test/gen-skill-docs.test.ts → 459/459 pass after these changes; bun run gen:skill-docs --host all clean.

Known follow-ups (not in this PR)

  • gstack-upgrade: Step 2 install-type detection probes .codex/, .factory/, .opencode/ — no .copilot branch. After ./setup --host copilot, future /gstack-upgrade runs default back to claude. Users on copilot-only installs need to manually pass --host copilot on upgrade. Happy to send a follow-up PR adding .copilot/agents/ to the detection ladder if you want.
  • tools: '[\"*\"]': the YAML array is emitted via string-interpolation through transformFrontmatter's extraFields path. Works today, but a future refactor that switches extraFields to JSON serialization would silently break it. Real fix is native array support in transformFrontmatter; in-line comment in hosts/copilot.ts flags the dependency.

andrey-esipov pushed a commit to andrey-esipov/astack that referenced this pull request May 11, 2026
…x pathRewrites, stale symlink cleanup

Mirrors the three review fixes applied to upstream PR garrytan#1436:

- hosts/copilot.ts: drop `target: github-copilot` so generated agents default
  to target=both (Copilot CLI + VS Code Copilot extension)
- hosts/copilot.ts: rewrite all `.claude/skills*` references to $GSTACK_ROOT
  instead of the dead `.copilot/skills/gstack/` path
- setup: clean stale gstack-*.agent.md symlinks before relinking

Verified live: ./setup --host copilot now produces 46 fresh agents pointing
at ~/.copilot/gstack via $GSTACK_ROOT, with stale entries cleaned up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ilot)

Adds support for installing gstack as flat .agent.md files under
~/.copilot/agents/, per Copilot CLI's custom agents schema:
https://docs.github.com/en/copilot/reference/custom-agents-configuration

The change is purely additive — existing hosts are untouched:

- New `outputLayout?: 'per-skill-dir' | 'flat-agent-md'` field on HostConfig.
  Default is 'per-skill-dir' (every existing host). Copilot uses
  'flat-agent-md' to emit one file per skill, flat under <hostSubdir>/agents/.
- processExternalHost branches on the new field to write to
  <hostSubdir>/agents/<name>.agent.md instead of the default
  <hostSubdir>/skills/<name>/SKILL.md.
- hosts/copilot.ts: frontmatter allowlist (name/description), injects
  target=github-copilot + tools=["*"] per the schema. skipSkills includes
  'codex' (per the every-external-host convention) and 'copilot' (to avoid
  recursing if a /copilot skill is ever added to the source tree).
- setup: INSTALL_COPILOT flag, build trigger, install block that symlinks
  generated gstack-*.agent.md into ~/.copilot/agents/ and creates
  ~/.copilot/gstack/ runtime root with bin/browse-dist/upgrade symlinks.
- Parameterized host smoke tests updated to handle both layouts; --host all
  test asserts the right output subdir per host. Two assertions updated for
  the new host count (10 → 11) and the new --host pass-through list.

Verified end-to-end on macOS:
- `./setup --host copilot` produces 46 agents under ~/.copilot/agents/.
- `copilot --agent <name>` lists every gstack skill (autoplan, qa, review,
  codex, design-shotgun, …).
- `bun test test/host-config.test.ts test/gen-skill-docs.test.ts` →
  459 pass, 0 fail.

Use: `copilot --agent qa "test the staging site"`.
First-time setup: `export GSTACK_ROOT=$HOME/.copilot/gstack`.
@andrey-esipov
Copy link
Copy Markdown
Author

Additional refinement pushed (force-update on copilot-cli-host): expanded skipSkills to exclude five gstack skills that don't translate cleanly to Copilot CLI's stateless agent model.

Skipped in this update

Copilot CLI agents are stateless single-invocation tools — each copilot --agent X starts fresh. Skills that rely on session-scoped state, or that drive an interactive per-skill config UI, don't fit. Adding to skipSkills:

  • freeze, unfreeze — toggle directory-scoped edit boundaries for the session.
  • careful, guard — install destructive-command guardrails for the session.
  • plan-tune — interactive UI for tuning AskUserQuestion sensitivity per skill; only meaningful inside Claude Code's persistent skill system.

After this change, --host copilot emits 41 gstack-*.agent.md files instead of 46. The five skipped skills remain fully functional on the other hosts that have a persistent skill loop (claude, codex, factory, etc.).

Follow-up I'm happy to do separately

The rules from freeze/careful/guard could be injected into a project-scoped AGENTS.md template, so their guardrails are ambient across every Copilot CLI invocation (instead of explicit). That would recover the protection we drop here. Out of scope for this PR but worth filing as an enhancement issue if you'd like.

Test counts unchanged: 459/459 pass on bun test test/host-config.test.ts test/gen-skill-docs.test.ts.

@andrey-esipov
Copy link
Copy Markdown
Author

Closing this PR — wrong architectural primitive.

After it merged into my local fork I tried invoking /office-hours and /autoplan from inside copilot interactively and they didn't appear. Investigating, Copilot CLI exposes two customization surfaces (per the docs):

  • Skills (SKILL.md files, invoked as /<name> slash commands by the user) — scanned from ~/.copilot/skills/<name>/SKILL.md, .github/skills/, .claude/skills/, .agents/skills/.
  • Agents (.agent.md files, invoked as tools by the model) — scanned from ~/.copilot/agents/<name>.agent.md.

gstack's content is overwhelmingly skills (/qa, /review, /ship, /autoplan, etc. — workflows the user invokes by typing /<name>), not agents (specialist personas the model dispatches as a tool). I emitted them as .agent.md files, which means the user has to either invoke them via --agent <name> non-interactively, or rely on the model to pick them — they don't show up in the interactive /skills list.

Verified: writing a SKILL.md at ~/.copilot/skills/test-probe/SKILL.md makes /test-probe invokable in interactive mode. The .agent.md files I shipped don't.

Will reopen with the right shape: emit SKILL.md files via the existing per-skill-dir layout, install one-level deep at ~/.copilot/skills/<name>/SKILL.md so Copilot CLI's native skill discovery picks them up. That also lets me drop the outputLayout HostConfig field entirely — the PR shrinks to a pure additive: new hosts/copilot.ts + setup install branch + 2-line test updates. No host-config.ts or gen-skill-docs.ts changes needed.

Sorry for the noise.

Sources:

andrey-esipov pushed a commit to andrey-esipov/astack that referenced this pull request May 12, 2026
Adds support for installing gstack as Copilot CLI Skills (per the docs at
https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-skills),
so users can invoke `/qa`, `/review`, `/ship`, `/autoplan`, etc. directly in
an interactive `copilot` session.

Copilot CLI exposes two customization surfaces — Skills (SKILL.md, invoked as
`/<name>` by the user) and Agents (.agent.md, invoked as tools by the model).
gstack content is overwhelmingly Skills (workflows the user invokes), so this
host emits SKILL.md via the existing per-skill-dir layout. No changes to
`host-config.ts` or `gen-skill-docs.ts` — purely additive.

Changes:
  - `hosts/copilot.ts` (new) — HostConfig: frontmatter allowlist (name +
    description), globalRoot `.copilot/skills/gstack`, skipSkills covers the
    five skills that don't translate to stateless single-invocation skills:
    freeze, unfreeze, careful, guard, plan-tune (plus the standard codex skip).
  - `hosts/index.ts` — register copilot in ALL_HOST_CONFIGS.
  - `setup` — INSTALL_COPILOT flag, build trigger, install block. Symlinks
    each generated skill under the collection subdir
    `~/.copilot/skills/gstack/<name>/` (bare name; the `gstack-` prefix is
    stripped during link creation) so users invoke `/<name>`, not
    `/gstack-<name>`. Creates `~/.copilot/gstack/` runtime root with
    bin/browse-dist/upgrade symlinks for $GSTACK_ROOT-relative tooling.
  - `.gitignore` — adds `.copilot/` for generated skill files.
  - Tests — host count assertion 10 → 11; --host pass-through string includes
    `copilot`.

Verified on macOS:
  - `./setup --host copilot` → 41 skills installed under
    `~/.copilot/skills/gstack/`.
  - `copilot -p "/qa ..."`, `copilot -p "/office-hours ..."`,
    `copilot -p "/review ..."` all invoke the correct gstack skill.
  - `bun test test/host-config.test.ts test/gen-skill-docs.test.ts` →
    459 pass, 0 fail.

Replaces garrytan#1436 (which used the wrong Copilot primitive — Agents instead of
Skills, making `/<name>` invocations not work).
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.

2 participants