Skip to content

feat: make agent-context extension a full opt-in#3097

Open
mnriem wants to merge 16 commits into
github:mainfrom
mnriem:mnriem-agent-context-optin-spec
Open

feat: make agent-context extension a full opt-in#3097
mnriem wants to merge 16 commits into
github:mainfrom
mnriem:mnriem-agent-context-optin-spec

Conversation

@mnriem

@mnriem mnriem commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

Makes the bundled agent-context extension a full opt-in and removes every agent-context concern from the Specify CLI (Python). The extension now fully owns the agent context/instruction file lifecycle; the CLI does nothing to it.

Concretely, the CLI no longer:

  • installs the agent-context extension during specify init,
  • writes agent-context-config.yml,
  • creates / updates / removes the managed Spec Kit section in agent context files (CLAUDE.md, AGENTS.md, .github/copilot-instructions.md, …),
  • performs marker resolution, extension-enabled gating, or context_file config plumbing,
  • emits the obsolete inline agent-context deprecation warning (the v0.12.0 message).

Integration context_file declarations are retained as inert metadata (consumed by templates and the extension); __CONTEXT_FILE__ now resolves purely from registry metadata. The bundled bash/PowerShell update scripts self-seed the context file from the active integration in .specify/init-options.json via the integration registry, so the extension is self-contained.

Breaking change: projects that relied on the CLI auto-managing the context section must now install/enable the agent-context extension. Existing projects keep working — previously written sections and config are left intact and only updated by the extension when run.

Built with Spec Kit (dogfooding)

This change was produced end-to-end with the Spec Kit SDD pipeline: specify → plan → tasks → implement → converge. The artifacts live under specs/001-agent-context-full-optin/ (spec, plan, research, data-model, contracts, quickstart, tasks). They are intentionally included to show "Spec Kit for Spec Kit" and will be purged before mergespecs/ is gitignored and force-added here.

Verification

  • Full suite green: 4361 passed, 4 skipped (skips are pwsh-only).
  • ruff check src/ clean; changed docs lint-clean.
  • New static guard tests/extensions/test_agent_context_cli_free.py asserts the CLI source contains zero agent-context lifecycle symbols.
  • New backward-compatibility tests confirm legacy projects (pre-seeded managed section + config) are untouched by init / switch / uninstall.
  • Quickstart Validations 1–5 pass, including a real specify init that creates no agent-context files, no config, and no context file.

Requirements coverage

FR-001–FR-010 and SC-001–SC-006 from the spec all map to concrete evidence; convergence assessment found no remaining gaps.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

mnriem and others added 8 commits June 22, 2026 13:11
Use Spec Kit's own specify workflow to author the spec that makes the
agent-context extension a full opt-in, removing all agent-context
configuration/support from the Python codebase and removing the
deprecation message. Force-added despite specs/ being gitignored; the
generated artifact will be purged prior to merge.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Phase 0/1 of the SDD plan workflow: plan.md, research.md, data-model.md,
quickstart.md, and contracts/cli-behavior.md. Constitution Check is a
documented no-op (repo has no ratified constitution). Force-added despite
specs/ being gitignored; generated artifacts will be purged prior to merge.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Earlier draft wrongly treated the gate as a no-op; the fork's main is 16
commits behind upstream/main, which carries .specify/memory/constitution.md.
Re-evaluate the feature against Principles I-V (all PASS) and note that
Principle I mandates keeping context_file as a declared class attribute,
validating the R1 metadata decision.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
After syncing fork main to upstream and rebasing, re-scan the current
agent-context surface. Upstream generalized the single context_file into a
plural context_files concept with new resolver helpers
(_resolve_context_files, _resolve_context_file_values,
_format_context_file_values) and upsert/remove now loop over multiple
files. Update research.md, data-model.md, contracts, quickstart grep
guards, and the plan summary to cover the expanded removal scope.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Phase 2 of SDD: dependency-ordered tasks.md (30 tasks) organized by the
three user stories, with mandatory test tasks (Constitution Principle II)
and a foundational phase decoupling __CONTEXT_FILE__ resolution from the
extension config. Includes the extension self-seeding task (T015) and a
static guard test (T002) enforcing zero agent-context references in the CLI.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Make the agent-context extension a full opt-in. The CLI no longer
installs the extension during init, writes agent-context-config.yml,
or creates/updates/removes the managed Spec Kit section in agent
context files. Context-section upsert/remove, marker resolution,
extension-enabled gating, the config helpers, and the obsolete inline
deprecation warning are all removed. Integration context_file stays as
inert metadata; __CONTEXT_FILE__ now resolves from registry metadata.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When agent-context-config.yml has no context_file/context_files, the
bundled bash and PowerShell update scripts now resolve the context file
from the active integration in .specify/init-options.json via the
integration registry, so the extension no longer depends on the CLI
writing its config.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update integration/extension tests to expect no agent-context install,
config, or context-section writes during init. Add a static guard test
(test_agent_context_cli_free.py) asserting the CLI source is free of
agent-context lifecycle symbols, plus backward-compatibility tests for
legacy projects. Refresh AGENTS.md, the extension README, and add a
CHANGELOG entry describing the opt-in behavior change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 22, 2026 19:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes the bundled agent-context extension fully opt-in by removing all CLI-side (Python) ownership of agent context/instruction files. Context file lifecycle (create/update/remove + config/markers) is now extension-owned; the CLI retains context_file only as inert metadata for template substitution.

Changes:

  • Removed CLI code paths that auto-install agent-context, write agent-context-config.yml, or upsert/remove managed context sections during integration setup/teardown/init/switch.
  • Updated __CONTEXT_FILE__ placeholder rendering to resolve purely from integration metadata (no extension-config reads).
  • Refactored and expanded tests to assert “CLI does not touch context files/config”, added a static guard test, and updated extension scripts to self-seed when config targets are empty.
Show a summary per file
File Description
tests/integrations/test_integration_rovodev.py Updates expectations: skills inventory is core-only (no extension auto-install).
tests/integrations/test_integration_generic.py Removes assertions about context section/config being created; updates file inventories accordingly.
tests/integrations/test_integration_forge.py Asserts setup does not create/touch context files.
tests/integrations/test_integration_cursor_agent.py Removes .mdc context upsert/remove/frontmatter tests tied to CLI lifecycle.
tests/integrations/test_integration_copilot.py Removes agent-context command/context/config expectations across default + skills modes.
tests/integrations/test_integration_codex.py Updates plan skill tests to resolve context file from metadata and ignore extension config.
tests/integrations/test_integration_cline.py Removes bundled extension/context file expectations from inventory helper.
tests/integrations/test_integration_claude.py Removes upsert/remove/BOM tests; adds teardown “leave user context intact” assertion.
tests/integrations/test_integration_base_yaml.py Reworks base integration tests to ensure setup/teardown do not manage context files/config.
tests/integrations/test_integration_base_toml.py Same as above for TOML-based integrations.
tests/integrations/test_integration_base_skills.py Ensures init doesn’t create agent-context config; inventories drop agent-context artifacts.
tests/integrations/test_integration_base_markdown.py Ensures markdown integrations do not create/touch context files/config.
tests/integrations/test_cli.py Updates init test: no agent-context config or context file created.
tests/extensions/test_extension_agent_context.py Prunes CLI-side marker/gating/config-writer coverage; adds metadata-only resolution + self-seed/back-compat checks.
tests/extensions/test_agent_context_cli_free.py Adds static guard ensuring CLI source contains no agent-context lifecycle/config symbols.
src/specify_cli/integrations/hermes/init.py Stops calling CLI upsert/remove and switches to metadata-only context display.
src/specify_cli/integrations/generic/init.py Stops calling CLI upsert/remove; metadata-only context display.
src/specify_cli/integrations/forge/init.py Stops calling CLI upsert/remove; metadata-only context display.
src/specify_cli/integrations/copilot/init.py Removes CLI upsert call from default setup; metadata-only context display.
src/specify_cli/integrations/base.py Deletes context section lifecycle machinery and replaces display helper with metadata-only accessor.
src/specify_cli/integrations/_helpers.py Removes extension-config write/clear side effects from init-options update/clear helpers.
src/specify_cli/commands/init.py Removes bundled agent-context auto-install step and config write during init.
src/specify_cli/agents.py Resolves __CONTEXT_FILE__ from integration metadata (registry), not extension config.
src/specify_cli/init.py Removes agent-context extension config helper functions/constants from the CLI package.
specs/001-agent-context-full-optin/tasks.md Adds tasks doc for the opt-in refactor (intended temporary artifact per PR description).
specs/001-agent-context-full-optin/spec.md Feature spec describing requirements/acceptance criteria for opt-in boundary.
specs/001-agent-context-full-optin/research.md Documents design decisions and rationale for removing CLI lifecycle/config I/O.
specs/001-agent-context-full-optin/quickstart.md Adds validation steps (tests/grep/init/extension update/back-compat).
specs/001-agent-context-full-optin/plan.md Implementation plan outlining code/test removals and checkpoints.
specs/001-agent-context-full-optin/data-model.md Captures ownership boundary + artifacts and what’s removed from CLI.
specs/001-agent-context-full-optin/contracts/cli-behavior.md Defines behavioral contracts mapped to tests.
specs/001-agent-context-full-optin/checklists/requirements.md Spec-quality checklist artifact (temporary per PR description).
extensions/agent-context/scripts/powershell/update-agent-context.ps1 Adds self-seeding from init-options via integration registry when config targets are empty.
extensions/agent-context/scripts/bash/update-agent-context.sh Same self-seeding behavior for bash script; refactors config parsing helpers.
extensions/agent-context/README.md Updates docs to reflect opt-in behavior and extension-owned lifecycle.
CHANGELOG.md Adds breaking-change entry explaining opt-in shift and removal of CLI lifecycle/deprecation warning.
AGENTS.md Updates integration documentation: context_file is inert metadata; extension fully owns lifecycle.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 37/37 changed files
  • Comments generated: 4

Comment thread extensions/agent-context/README.md Outdated
Comment thread extensions/agent-context/scripts/bash/update-agent-context.sh Outdated
Comment thread extensions/agent-context/scripts/powershell/update-agent-context.ps1 Outdated
Comment thread tests/extensions/test_agent_context_cli_free.py Outdated
… guard test

Address PR review feedback:
- Self-seed scripts (bash + PowerShell) now emit an actionable warning when
  an active integration is configured but specify_cli cannot be imported by
  the chosen Python (e.g. pipx installs), or when the integration declares no
  context file, instead of silently falling through to 'nothing to do'.
- Correct the extension README disable note: command rendering never reads the
  extension config; __CONTEXT_FILE__ is always substituted from integration
  metadata, so a stale context_files value cannot affect rendering.
- Cache CLI source reads in the static guard test via a module-scoped fixture
  so the directory walk happens once instead of once per forbidden symbol.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mnriem

mnriem commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks for the review — addressed all four findings in 56ee0df.

  • README disable note (misleading "command rendering" claim): Fixed. Reworded to state that the CLI never reads the extension config during command rendering — __CONTEXT_FILE__ is always substituted from the active integration's metadata — so a stale context_files value cannot affect rendering.
  • bash self-seed silently swallows import failure: Fixed. When an active integration is configured but specify_cli isn't importable by the chosen Python (e.g. pipx), the script now prints an actionable stderr warning naming the interpreter and pointing at context_file / SPECKIT_PYTHON, plus a separate warning when the integration declares no context file.
  • PowerShell self-seed same issue: Fixed symmetrically — the embedded probe now exits 3 on import failure so the caller can distinguish "can't import" from "no context file" and Write-Warning accordingly (also warns when no Python is found).
  • Static guard test re-reads every file per symbol: Fixed. Source texts are now read once via a module-scoped fixture and reused across the parametrized cases.

Were these "gaps we missed"? Mostly minor: #1 was a genuine doc-accuracy miss (a carried-over sentence that's no longer true), and #4 is a test-efficiency nit. #2/#3 aren't functional bugs — the spec treats self-seed as best-effort — but the warnings make the best-effort path diagnosable, which is a fair improvement. No correctness gaps in the feature itself.

Posted on behalf of @mnriem by GitHub Copilot (model: Claude Opus 4.8).

mnriem and others added 4 commits June 22, 2026 16:05
The extension now bundles agent-context-defaults.json (key→context_file
map) and self-seeds from it, dropping any dependency on the Specify CLI
registry. Both the bash and PowerShell update scripts read the bundled
JSON map keyed by the active integration from init-options.json.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Strip every context_file reference from the CLI: the field on all 35
integration classes, the IntegrationBase plumbing (process_template
param/step, _context_file_display, docstrings), the __CONTEXT_FILE__
resolution in agents.py, the legacy context_file/context_markers
popping in _helpers.py, and the context_file template in
integration_scaffold.py. Also drop the Agent context update step and
__CONTEXT_FILE__ placeholder from templates/commands/plan.md.

The agent-context extension now solely owns all context-file knowledge,
including the per-agent default mapping.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove CONTEXT_FILE attrs and context_file assertions across the base
mixins, all 35 per-integration test files, shared integration tests, and
conftest stubs. Rewrite the base-mixin context tests to assert no managed
section is written and no __CONTEXT_FILE__ placeholder survives. Extend
the CLI-free static guard to forbid context_file, __CONTEXT_FILE__, and
_context_file_display in src/specify_cli, and have the extension tests
copy the bundled defaults JSON so self-seed runs without the CLI.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update AGENTS.md (integration examples, required-fields table, context
behavior section, pitfalls), CHANGELOG, and the SDD spec artifacts
(FR-007, SC-002, data-model) to state that the CLI carries no
context_file and the extension fully owns the per-agent default mapping
via agent-context-defaults.json.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 22, 2026 21:06
@mnriem

mnriem commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed Phase 2 (commits 4f51e37, 55dda37, d8014dd, 114379f), which fully removes agent-context state from the Specify CLI per the review.

What changed:

  • No more context_file in the CLI. Removed the field from all 35 integration classes, the IntegrationBase plumbing (process_template param/step, _context_file_display, docstrings), the __CONTEXT_FILE__ resolution in agents.py, the legacy popping in _helpers.py, and the scaffold template. Also dropped the Agent context update step + __CONTEXT_FILE__ from templates/commands/plan.md. A grep confirms zero context_file/__CONTEXT_FILE__ references remain under src/ and templates/.
  • The extension self-owns the per-agent mapping. New extensions/agent-context/agent-context-defaults.json ships the key→context_file map; both the bash and PowerShell update scripts self-seed from it via init-options.json, with no specify_cli import or registry probe.
  • Static guard hardened. tests/extensions/test_agent_context_cli_free.py now forbids context_file, __CONTEXT_FILE__, and _context_file_display in src/specify_cli.
  • Docs (AGENTS.md, CHANGELOG, README) and the SDD spec artifacts (FR-007, SC-002, data-model) updated to state the CLI carries no context-file knowledge.

Full suite: 3818 passed, 4 skipped; ruff clean on src/.

Posted on behalf of @mnriem by GitHub Copilot (model: Claude Opus 4.8).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 102/102 changed files
  • Comments generated: 2

Comment thread specs/001-agent-context-full-optin/research.md Outdated
Comment thread specs/001-agent-context-full-optin/contracts/cli-behavior.md
Update research.md (R1, R2, R4, summary table), contracts/cli-behavior.md
(C3, C5), tasks.md (Phase 2, T026, notes), plan.md (Principle I, source
map), and checklists/requirements.md so the spec artifacts reflect the
implemented decision: the CLI carries no context_file attribute or
__CONTEXT_FILE__ resolution, and the per-agent defaults map lives in the
extension. Resolves PR review #4548130110.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mnriem

mnriem commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator Author

Both findings were real spec/implementation inconsistencies — fixed in f64dc36.

The flagged sections were holdovers from the earlier Phase 1 "keep context_file as inert metadata" decision, which the implementation has since superseded with full removal. Updated the SDD artifacts to match what shipped:

  • research.md — rewrote R1 (remove context_file entirely; extension owns the defaults map), R2 (remove __CONTEXT_FILE__ resolution from the CLI; drop the placeholder from templates), R4 (no metadata formatter survives), and the summary table.
  • contracts/cli-behavior.md — C3 now states the CLI performs no __CONTEXT_FILE__ substitution and integrations declare no context_file; C5 forbids the context_file attribute, __CONTEXT_FILE__, and _context_file_display.
  • Also swept the remaining artifacts for consistency: tasks.md (Phase 2 tasks, T026, notes), plan.md (Principle I rationale + source map), and checklists/requirements.md.

No code changes in this round — spec-artifact alignment only.

Posted on behalf of @mnriem by GitHub Copilot (model: Claude Opus 4.8).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 102/102 changed files
  • Comments generated: 1

Comment thread src/specify_cli/integrations/base.py
Update the multi_install_safe docstring (drop the removed "context file"
invariant), the RovoDev setup docstring (no longer upserts a context
section), the Copilot module docstring (drop the context-file line), and
tighten the _update_init_options_for_integration note. Pure docstring
changes — no behavioral impact. Resolves PR review #4548237085.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mnriem

mnriem commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator Author

Fixed in 2737616. The flagged multi_install_safe docstring was real — it still listed "context file" as an enforced invariant after context_file was removed.

While in there I swept the rest of src/ for stale context-file prose and fixed three more:

  • multi_install_safe docstring — dropped the "and context file" invariant (only static unique agent root + commands dir remain enforced).
  • rovodev/__init__.py setup docstring — no longer claims SkillsIntegration.setup() "upserts the context section."
  • copilot/__init__.py module docstring — removed the "Context file lives at .github/copilot-instructions.md" line.
  • _helpers.py — tightened the _update_init_options_for_integration note to stop referencing now-nonexistent context-file metadata.

Pure docstring changes, no behavioral impact. Targeted tests (copilot, rovodev, registry, CLI-free guard) pass; ruff clean.

Posted on behalf of @mnriem by GitHub Copilot (model: Claude Opus 4.8).

@mnriem mnriem changed the title feat!: make agent-context extension a full opt-in (remove CLI-side lifecycle) feat: make agent-context extension a full opt-in Jun 22, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 102/102 changed files
  • Comments generated: 3

Comment thread tests/integrations/test_cli.py Outdated
@@ -80,20 +80,14 @@ def test_integration_copilot_creates_files(self, tmp_path):
# context_file lives in the agent-context extension config, not init-options.json
Comment on lines +135 to +149
def _install_agent_context_config(project_root: Path, **overrides: object) -> None:
_write_ext_config(project_root, **overrides)
# Mirror the real install layout: the extension ships its own
# agent->context-file defaults map alongside the config.
defaults_src = EXT_DIR / "agent-context-defaults.json"
if defaults_src.is_file():
defaults_dst = (
project_root
/ ".specify"
/ "extensions"
/ "agent-context"
/ "agent-context-defaults.json"
)
defaults_dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(defaults_src, defaults_dst)
Comment on lines 91 to 94
And may optionally set:

* ``context_file`` — path (relative to project root) of the agent
context/instructions file (e.g. ``"CLAUDE.md"``)

Projects may additionally opt into managing multiple context files by
setting ``context_files`` in the agent-context extension config. The
integration class still declares one default ``context_file`` for backwards
compatibility and command-template rendering.
* ``invoke_separator`` — slash-command separator (defaults to ``"."``)
"""
- base.py: document multi_install_safe as an optional subclass attribute
  in the IntegrationBase docstring.
- test_cli.py: clarify the init-options assertion is guarding against
  leftover legacy agent-context keys, not relocation.
- test_extension_agent_context.py: _install_agent_context_config now
  asserts the bundled agent-context-defaults.json exists and always
  copies it, so self-seeding tests fail loudly instead of silently
  skipping when the map is missing.
- test_integration_cursor_agent.py: drop Path/IntegrationManifest imports
  left unused after removing the context-section frontmatter tests.

Resolves PR review #4548293116.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mnriem

mnriem commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator Author

All three addressed in 11bf1d3.

  • base.py:94 — added multi_install_safe to the IntegrationBase "may optionally set" docstring list.
  • test_cli.py:80 — reworded the comment: the assertion guards against leftover legacy agent-context keys in init-options.json, not relocation.
  • test_extension_agent_context.py:149_install_agent_context_config() now asserts the bundled agent-context-defaults.json exists and always copies it (mirroring a real extension install), so self-seeding tests fail loudly rather than silently skipping when the map is missing. This is the "initialize the extension properly" path you flagged — the defaults map is part of the extension's install footprint, so the helper now provisions it unconditionally.

While verifying, ruff surfaced two now-unused imports (Path, IntegrationManifest) in test_integration_cursor_agent.py — they were left dead after this PR removed the .mdc context-section tests, so I cleaned them up.

Full check: ruff clean on src/ and tests/; affected suites (test_cli, test_integration_cursor_agent, test_extension_agent_context) = 142 passed, 1 skipped.

Posted on behalf of @mnriem by GitHub Copilot (model: Claude Opus 4.8).

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