Skip to content

Commit 0fbd99d

Browse files
authored
feat(copilot): default integration to skills (#3976)
* feat(copilot): default integration to skills Make Copilot skills the default while retaining the commands layout behind --integration-options="--commands". Preserve historical project layouts and validate conflicting mode flags before switch teardown. Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 930d846b-8921-44ef-9f45-3e77c036b6b5 * fix(copilot): preserve layout state during migration Keep target integration options isolated from fallback state, prefer the Copilot manifest when resolving layouts, and update dispatch coverage for the skills-first default. Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 930d846b-8921-44ef-9f45-3e77c036b6b5 --------- Copilot-Session: 930d846b-8921-44ef-9f45-3e77c036b6b5
1 parent ab468c4 commit 0fbd99d

12 files changed

Lines changed: 564 additions & 263 deletions

AGENTS.md

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -260,13 +260,13 @@ The base classes handle most work automatically. Override only when the agent de
260260
| Override | When to use | Example |
261261
|---|---|---|
262262
| `command_filename(template_name)` | Custom file naming or extension | Copilot → `speckit.{name}.agent.md` |
263-
| `options()` | Integration-specific CLI flags via `--integration-options` | Codex → `--skills` flag, Copilot → `--skills` flag |
264-
| `setup()` | Custom install logic (companion files, settings merge) | Copilot → `.agent.md` + `.prompt.md` + `.vscode/settings.json` (default) or `speckit-<name>/SKILL.md` (skills mode) |
263+
| `options()` | Integration-specific CLI flags via `--integration-options` | Codex → `--skills` flag, Copilot → `--commands` flag |
264+
| `setup()` | Custom install logic (companion files, settings merge) | Copilot → `speckit-<name>/SKILL.md` (default) or `.agent.md` + `.prompt.md` + `.vscode/settings.json` (`--commands`) |
265265
| `teardown()` | Custom uninstall logic | Rarely needed; base handles manifest-tracked files |
266266

267267
**Example — Copilot (fully custom `setup`):**
268268

269-
Copilot extends `IntegrationBase` directly because it creates `.agent.md` commands, companion `.prompt.md` files, and merges `.vscode/settings.json`. It also supports a `--skills` mode that scaffolds `speckit-<name>/SKILL.md` under `.github/skills/` using composition with an internal `_CopilotSkillsHelper`. See `src/specify_cli/integrations/copilot/__init__.py` for the full implementation.
269+
Copilot extends `IntegrationBase` directly because it supports two layouts. It scaffolds `speckit-<name>/SKILL.md` under `.github/skills/` by default using composition with an internal `_CopilotSkillsHelper`. Its `--commands` mode creates `.agent.md` commands, companion `.prompt.md` files, and merges `.vscode/settings.json`. See `src/specify_cli/integrations/copilot/__init__.py` for the full implementation.
270270

271271
### 7. Update Devcontainer files (Optional)
272272

@@ -415,36 +415,28 @@ Some agents require custom processing beyond the standard template transformatio
415415

416416
### Copilot Integration
417417

418-
GitHub Copilot has unique requirements:
418+
GitHub Copilot uses skills by default, scaffolded as
419+
`speckit-<name>/SKILL.md` under `.github/skills/`.
419420

420-
- Commands use `.agent.md` extension (not `.md`)
421-
- Each command gets a companion `.prompt.md` file in `.github/prompts/`
422-
- Installs `.vscode/settings.json` with prompt file recommendations
423-
- Context file lives at `.github/copilot-instructions.md`
424-
425-
Implementation: Extends `IntegrationBase` with custom `setup()` method that:
421+
**Commands mode (`--commands`):** Copilot also supports a commands-based layout
422+
via `--integration-options="--commands"`. When enabled:
426423

427-
1. Processes templates with `process_template()`
428-
2. Generates companion `.prompt.md` files
429-
3. Merges VS Code settings
430-
431-
**Skills mode (`--skills`):** Copilot also supports an alternative skills-based layout
432-
via `--integration-options="--skills"`. When enabled:
424+
- Commands use `.agent.md` extension under `.github/agents/`
425+
- Each command gets a companion `.prompt.md` file in `.github/prompts/`
426+
- `.vscode/settings.json` is merged with prompt file recommendations
427+
- `build_command_invocation()` returns bare args for `--agent` dispatch
433428

434-
- Commands are scaffolded as `speckit-<name>/SKILL.md` under `.github/skills/`
435-
- No companion `.prompt.md` files are generated
436-
- No `.vscode/settings.json` merge
437-
- `post_process_skill_content()` injects a `mode: speckit.<stem>` frontmatter field
438-
- `build_command_invocation()` returns `/speckit-<stem>` instead of bare args
429+
In the default skills mode, no companion prompts or VS Code settings merge are
430+
created, and `build_command_invocation()` returns `/speckit-<stem>`.
439431

440432
The two modes are mutually exclusive — a project uses one or the other:
441433

442434
```bash
443-
# Default mode: .agent.md agents + .prompt.md companions + settings merge
435+
# Default skills mode: speckit-<name>/SKILL.md under .github/skills/
444436
specify init my-project --integration copilot
445437
446-
# Skills mode: speckit-<name>/SKILL.md under .github/skills/
447-
specify init my-project --integration copilot --integration-options="--skills"
438+
# Commands mode: .agent.md agents + .prompt.md companions + settings merge
439+
specify init my-project --integration copilot --integration-options="--commands"
448440
```
449441

450442
### Forge Integration

docs/reference/integrations.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ The Specify CLI supports a wide range of AI coding agents. When you run `specify
2020
| [Firebender](https://firebender.com/) | `firebender` | IDE-based agent for Android Studio / IntelliJ |
2121
| [Forge](https://forgecode.dev/) | `forge` | |
2222
| [Gemini CLI](https://github.com/google-gemini/gemini-cli) | `gemini` | |
23-
| [GitHub Copilot](https://code.visualstudio.com/) | `copilot` | Defaults to legacy markdown mode: `.agent.md` command files under `.github/agents/`, companion `.prompt.md` files under `.github/prompts/`, and a `.vscode/settings.json` merge. Pass `--integration-options="--skills"` to scaffold skills as `speckit-<command>/SKILL.md` under `.github/skills/` instead. Legacy markdown mode is deprecated and will stop being the default in a future release. |
23+
| [GitHub Copilot](https://code.visualstudio.com/) | `copilot` | Skills-based by default; installs `speckit-<command>/SKILL.md` under `.github/skills/`. Pass `--integration-options="--commands"` to use the supported commands layout: `.agent.md` files under `.github/agents/`, companion `.prompt.md` files under `.github/prompts/`, and a `.vscode/settings.json` merge. |
2424
| [Goose](https://goose-docs.ai/) | `goose` | Uses YAML recipe format in `.goose/recipes/` |
2525
| [Grok Build](https://docs.x.ai/build/overview) | `grok` | Skills-based integration; installs skills into `.grok/skills` and invokes them as `/speckit-<command>` |
2626
| [Hermes](https://github.com/NousResearch/hermes-agent) | `hermes` | Skills-based integration; installs skills globally into `~/.hermes/skills/` |
@@ -234,7 +234,8 @@ Some integrations accept additional options via `--integration-options`:
234234
| ----------- | ------------------- | -------------------------------------------------------------- |
235235
| `generic` | `--commands-dir` | Required. Directory for command files |
236236
| `kimi` | `--migrate-legacy` | Migrate legacy `.kimi/skills/` installs to `.kimi-code/skills/` (including dotted→hyphenated skill naming, e.g. `speckit.xxx``speckit-xxx`) |
237-
| `copilot` | `--skills` | Scaffold commands as agent skills (`speckit-<command>/SKILL.md` under `.github/skills/`, invoked as `/speckit-<command>`) instead of the default legacy markdown mode (`.github/agents/*.agent.md` plus `.github/prompts/*.prompt.md` and a `.vscode/settings.json` merge). Without this flag, install warns that legacy markdown mode is deprecated. |
237+
| `copilot` | `--commands` | Scaffold `.github/agents/*.agent.md` commands with `.github/prompts/*.prompt.md` companions and merge `.vscode/settings.json` instead of using the default skills layout. |
238+
| `copilot` | `--skills` | Force the default skills layout, overriding an existing commands layout during an explicit migration. |
238239

239240
Example:
240241

src/specify_cli/integration_runtime.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ def with_integration_setting(
7070
# ``script_type`` changes (``parsed_options`` and ``raw_options`` both
7171
# None), the previously-stored ``parsed_options`` are retained above, so
7272
# deriving the separator from the argument (None) would drop an
73-
# options-dependent separator (e.g. Copilot ``--skills`` -> "-") back to
74-
# the default ".".
73+
# options-dependent separator (e.g. Copilot ``--commands`` -> ".") back to
74+
# the default "-".
7575
current["invoke_separator"] = integration.effective_invoke_separator(
7676
current.get("parsed_options"), project_root
7777
)

src/specify_cli/integrations/_migrate_commands.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,14 @@ def integration_switch(
331331

332332
selected_script = _resolve_script_type(project_root, script)
333333

334+
# Resolve and validate target options before uninstalling the current
335+
# integration. Invalid options must not leave the project partially
336+
# switched with the previous integration already removed.
337+
target_raw_options, target_parsed_options = _resolve_integration_options(
338+
target_integration, current, target, integration_options
339+
)
340+
target_integration.is_skills_mode(target_parsed_options, project_root)
341+
334342
# Phase 1: Uninstall current integration (if any)
335343
if installed_key:
336344
current_integration = get_integration(installed_key)
@@ -403,7 +411,10 @@ def integration_switch(
403411
fallback_key = installed_keys[0]
404412
fallback_integration = get_integration(fallback_key)
405413
if fallback_integration is not None:
406-
raw_options, parsed_options = _resolve_integration_options(
414+
(
415+
fallback_raw_options,
416+
fallback_parsed_options,
417+
) = _resolve_integration_options(
407418
fallback_integration, current, fallback_key, None
408419
)
409420
_set_default_integration_or_exit(
@@ -412,8 +423,8 @@ def integration_switch(
412423
fallback_key,
413424
fallback_integration,
414425
installed_keys,
415-
raw_options=raw_options,
416-
parsed_options=parsed_options,
426+
raw_options=fallback_raw_options,
427+
parsed_options=fallback_parsed_options,
417428
)
418429
else:
419430
_write_integration_json(
@@ -423,13 +434,6 @@ def integration_switch(
423434
_remove_integration_json(project_root)
424435
current = _read_integration_json(project_root)
425436

426-
# Build parsed options from --integration-options so the integration
427-
# can determine its effective invoke separator before shared infra
428-
# is installed.
429-
raw_options, parsed_options = _resolve_integration_options(
430-
target_integration, current, target, integration_options
431-
)
432-
433437
# Refresh shared infrastructure to the current CLI version. Switching
434438
# integrations is exactly when stale vendored shared scripts (e.g.
435439
# update-agent-context.sh that pre-dates the target integration's
@@ -445,11 +449,11 @@ def integration_switch(
445449
force=refresh_shared_infra,
446450
refresh_managed=True,
447451
invoke_separator=_invoke_separator_for_integration(
448-
target_integration, current, target, parsed_options,
452+
target_integration, current, target, target_parsed_options,
449453
project_root=project_root,
450454
),
451455
invoke_prefix=_invoke_prefix_for_integration(
452-
target_integration, target, parsed_options, project_root
456+
target_integration, target, target_parsed_options, project_root
453457
),
454458
refresh_hint=(
455459
"To overwrite customizations, re-run with "
@@ -471,14 +475,14 @@ def integration_switch(
471475
target_integration.key,
472476
target_integration.config,
473477
project_root,
474-
parsed_options,
478+
target_parsed_options,
475479
)
476480
try:
477481
target_integration.setup(
478482
project_root, manifest,
479-
parsed_options=parsed_options,
483+
parsed_options=target_parsed_options,
480484
script_type=selected_script,
481-
raw_options=raw_options,
485+
raw_options=target_raw_options,
482486
events=events_map,
483487
)
484488
manifest.save()
@@ -489,8 +493,8 @@ def integration_switch(
489493
target_integration,
490494
_dedupe_integration_keys([*installed_keys, target_integration.key]),
491495
script_type=selected_script,
492-
raw_options=raw_options,
493-
parsed_options=parsed_options,
496+
raw_options=target_raw_options,
497+
parsed_options=target_parsed_options,
494498
)
495499

496500
except Exception as exc:

src/specify_cli/integrations/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,8 @@ def is_skills_mode(
225225
on-disk layout to avoid silently migrating an existing project to a
226226
different mode. The default ignores it.
227227
228-
The default (command-first integrations, e.g. Copilot's default
229-
layout) is skills mode only when ``--skills`` was requested.
228+
The default for command-first integrations is skills mode only when
229+
``--skills`` was requested.
230230
``SkillsIntegration`` overrides this to return ``True`` by default;
231231
skills-first integrations that expose a legacy opt-out (e.g. Bob)
232232
override it to honor their own flag.

0 commit comments

Comments
 (0)