Skip to content

Extract agent context updates into bundled agent-context extension#2546

Open
Copilot wants to merge 27 commits into
mainfrom
copilot/add-agent-context-extension
Open

Extract agent context updates into bundled agent-context extension#2546
Copilot wants to merge 27 commits into
mainfrom
copilot/add-agent-context-extension

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 13, 2026

New Feature

Coding agent context file management (<!-- SPECKIT START --> / <!-- SPECKIT END --> injection into CLAUDE.md, .github/copilot-instructions.md, etc.) was hardcoded into IntegrationBase, with no way to opt out or customize markers. This change moves that behavior into a bundled agent-context extension driven by .specify/extensions/agent-context/agent-context-config.yml.

What does this feature do?

Adds extensions/agent-context/ (bundled: true, opt-out) that owns the lifecycle of the managed context section. Both the Python paths and the new shell/PowerShell scripts read context_file and context_markers from .specify/extensions/agent-context/agent-context-config.yml — single source of truth, no per-agent logic, user-customizable markers.

# .specify/extensions/agent-context/agent-context-config.yml
context_file: CLAUDE.md
context_markers:
  start: "<!-- SPECKIT START -->"
  end: "<!-- SPECKIT END -->"

Implementation details

  • integrations/base.py

    • _resolve_context_markers(project_root) — reads context_markers from agent-context-config.yml, falls back to CONTEXT_MARKER_START / CONTEXT_MARKER_END constants per side.
    • _agent_context_extension_enabled(project_root) — reads .specify/extensions/.registry; returns True when registry/entry absent (backwards compat) or enabled is not literally False.
    • upsert_context_section() / remove_context_section() now use the resolved markers and short-circuit when the extension is disabled. upsert_context_section() emits a deprecation warning (v0.12.0) since inline context updates are being replaced by the extension.
  • specify_cli/commands/init.py

    • specify init writes context_file to the agent-context extension config (not init-options.json).
    • Auto-installs the bundled agent-context extension during specify init (after init-options.json is saved, so skill registration works).
    • ensure_executable_scripts runs after extension install so extension scripts get execute bits.
  • specify_cli/__init__.py

    • _update_init_options_for_integration() writes context_file to the extension config, preserving user-customized markers.
    • _clear_init_options_for_integration() clears context_file in the extension config and removes legacy keys from init-options.json.
    • _load_agent_context_config() / _save_agent_context_config() — helpers for reading/writing the extension config.
  • extensions/agent-context/extension.yml (id agent-context, hooks after_specify / after_plan), README.md, commands/speckit.agent-context.update.md, and bash + PowerShell update-agent-context scripts. Both scripts parse agent-context-config.yml, resolve markers (with default fallback), auto-detect the most recent specs/*/plan.md when no plan path is supplied, and perform the same upsert algorithm as the Python path (CRLF normalization, BOM strip, marker-corruption recovery). Scripts include path traversal guards rejecting absolute paths and .. segments.

  • extensions/catalog.jsonagent-context registered as bundled: true, alphabetized first.

  • AGENTS.md — wrapper-script and dispatcher-script guidance removed; replaced with the agent-context-config.yml flow and the specify extension disable agent-context opt-out.

  • Command templates — All 8 non-constitution commands (specify, clarify, plan, tasks, checklist, implement, analyze, taskstoissues) now include a directive to load /memory/constitution.md for project principles and governance constraints.

  • tests/extensions/test_extension_agent_context.py — 34 tests covering extension layout, catalog entry, marker resolution (defaults / custom / partial / invalid / corrupt YAML), upsert+remove with custom markers, the disabled-extension gate (upsert returns None, remove returns False and leaves file untouched), deprecation warning output, and extension config writers.

  • tests/integrations/test_integration_generic.py — Parametrized test verifying all 8 commands reference constitution.md.

Backwards compatibility

  • Missing agent-context-config.yml or missing context_markers → class-constant defaults, identical to current behavior.
  • Legacy context_file in init-options.jsonagents.py falls back to reading it when extension config is absent.
  • Missing extension registry or missing agent-context entry → treated as enabled, so existing projects continue to receive context updates without re-init.
  • All 30+ existing integrations are unchanged; markers remain defined only on IntegrationBase.

Copilot AI requested review from Copilot and removed request for Copilot May 13, 2026 21:47
Copilot AI linked an issue May 13, 2026 that may be closed by this pull request
12 tasks
Copilot AI requested review from Copilot and removed request for Copilot May 13, 2026 22:01
Comment thread tests/extensions/test_extension_agent_context.py Fixed
Comment thread tests/extensions/test_extension_agent_context.py Fixed
Copilot AI changed the title [WIP] Add agent-context extension for managing context file updates Extract agent context updates into bundled agent-context extension May 13, 2026
Copilot finished work on behalf of mnriem May 13, 2026 22:03
Copilot AI requested a review from mnriem May 13, 2026 22:03
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 13, 2026 22:15
Comment thread tests/extensions/test_extension_agent_context.py Fixed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 refactors agent-context-file management (the <!-- SPECKIT START --> / <!-- SPECKIT END --> block injected into files like CLAUDE.md) out of IntegrationBase and into a new bundled agent-context extension. Configuration now flows through .specify/init-options.json (context_file, context_markers), giving users opt-out (specify extension disable agent-context) and customizable markers, with parallel bash/PowerShell scripts that mirror the Python upsert logic.

Changes:

  • Adds a bundled agent-context extension (manifest, README, command, bash + PowerShell scripts) and registers it in extensions/catalog.json.
  • Extends IntegrationBase with _resolve_context_markers() and _agent_context_extension_enabled(), gating both upsert_context_section() and remove_context_section() on the registry, and seeds/clears context_markers in init-options.json from specify_cli.__init__.
  • Updates AGENTS.md and adds 25 tests covering layout, marker resolution, custom-marker upsert/remove, the disabled-gate, and init-options writers.
Show a summary per file
File Description
src/specify_cli/integrations/base.py New marker-resolution + extension-enabled helpers; upsert/remove use them.
src/specify_cli/__init__.py New tracker step that auto-installs bundled agent-context; seeds/clears context_markers.
extensions/catalog.json Registers agent-context as bundled: true.
extensions/agent-context/extension.yml Extension manifest with after_specify / after_plan hooks.
extensions/agent-context/README.md User-facing docs for opt-out and configuration.
extensions/agent-context/commands/speckit.agent-context.update.md Slash-command spec referencing the bash/ps1 scripts.
extensions/agent-context/scripts/bash/update-agent-context.sh Bash hook that re-renders the managed section.
extensions/agent-context/scripts/powershell/update-agent-context.ps1 PowerShell counterpart.
AGENTS.md Removes wrapper-script guidance; documents the new init-options.json flow and opt-out.
tests/extensions/test_extension_agent_context.py 25 new tests for the extension and plumbing.

Copilot's findings

Tip

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

  • Files reviewed: 10/10 changed files
  • Comments generated: 7

Comment thread extensions/agent-context/scripts/bash/update-agent-context.sh Outdated
Comment thread src/specify_cli/__init__.py Outdated
Comment thread AGENTS.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 src/specify_cli/__init__.py Outdated
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 13, 2026 22:27
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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: 10/10 changed files
  • Comments generated: 6

Comment thread extensions/agent-context/scripts/bash/update-agent-context.sh Outdated
Comment thread src/specify_cli/__init__.py Outdated
Comment thread src/specify_cli/__init__.py Outdated
Comment thread extensions/agent-context/scripts/bash/update-agent-context.sh Outdated
Comment thread src/specify_cli/integrations/base.py Outdated
- bash: parse init-options.json with a single python3 invocation instead
  of three separate read_json_field calls, for parity with the PowerShell
  ConvertFrom-Json approach and to avoid divergent error semantics
- bash: use parameter expansion to strip PROJECT_ROOT prefix from plan
  path instead of sed interpolation, avoiding special-character fragility
- powershell: limit Get-ChildItem to -Depth 1 so plan.md discovery matches
  the bash glob specs/*/plan.md (one level deep) — fixes cross-platform
  inconsistency with nested plan.md files
- powershell: replace Substring+Length relative-path with
  [System.IO.Path]::GetRelativePath for robustness across case/PSDrive
  differences
- __init__.py: move agent-context extension install to after
  save_init_options so init-options.json is present when hooks run
- __init__.py: seed context_markers in init-options only when
  context_file is truthy; avoids noise for integrations without a context
  file
- integrations/base.py: narrow blanket except Exception in
  _resolve_context_markers to ImportError / (OSError, ValueError) so
  unexpected bugs surface instead of being silently swallowed
…context_file

Apply the same gating logic used during `specify init`: only write
context_markers to init-options.json when the integration actually has a
context_file set.  When switching to an integration without a context file
the stale markers are removed, keeping the two init paths consistent.
Copilot finished work on behalf of mnriem May 14, 2026 12:16
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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: 10/10 changed files
  • Comments generated: 9

Comment thread src/specify_cli/__init__.py Outdated
Comment thread src/specify_cli/__init__.py Outdated
Comment thread extensions/agent-context/scripts/bash/update-agent-context.sh Outdated
Comment thread extensions/agent-context/scripts/bash/update-agent-context.sh Outdated
Comment thread src/specify_cli/__init__.py Outdated
Comment thread extensions/agent-context/README.md Outdated
Comment thread src/specify_cli/integrations/base.py Outdated
Comment thread src/specify_cli/integrations/base.py Outdated
Comment thread extensions/agent-context/scripts/powershell/update-agent-context.ps1 Outdated
Copilot AI requested review from Copilot and removed request for Copilot May 14, 2026 18:32
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 14, 2026

@copilot apply changes based on the comments in this thread

Applied all four changes in commits 8e92e6f and 2c33e47:

  1. agents.py:386-388 — dropped the try/except ImportError entirely. _load_agent_context_config already catches OSError and yaml.YAMLError internally and returns defaults, so the exception guard was dead code.

  2. __init__.py:1595-1599 — added an existence check before calling _update_agent_context_config_file in _clear_init_options_for_integration. Uninstalling an integration no longer creates .specify/extensions/agent-context/agent-context-config.yml (and its parent dirs) in projects where the extension was never installed.

  3. update-agent-context.ps1:66-120 — replaced exception-driven dispatch with an explicit Get-Command ConvertFrom-Yaml -ErrorAction SilentlyContinue check. ConvertFrom-Yaml is now only called when confirmed present, making the control flow explicit and avoiding catch-triggered fallback on non-terminating warnings.

  4. update-agent-context.ps1 — changed Write-Host to Write-Warning for skip/diagnostic messages ("not found" and "context_file not set") to match the bash script's use of stderr for the same messages.

Copilot finished work on behalf of mnriem May 14, 2026 18:33
mnriem added 2 commits May 29, 2026 11:54
# Conflicts:
#	src/specify_cli/__init__.py
#	src/specify_cli/integrations/base.py
…install, and init flow fix

- Add constitution-loading directive to specify, clarify, tasks, checklist, taskstoissues commands
- Add deprecation warning (v0.12.0) in upsert_context_section()
- Auto-install agent-context extension during specify init
- Move context_file from init-options.json to agent-context extension config
- Add tests: deprecation warning, corrupt config, constitution directives
- Update file inventories across all integration tests
Copilot AI review requested due to automatic review settings May 29, 2026 18:13
@mnriem mnriem marked this pull request as ready for review May 29, 2026 18:18
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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: 26/26 changed files
  • Comments generated: 6

Comment thread src/specify_cli/commands/init.py Outdated
Comment thread tests/extensions/test_extension_agent_context.py
Comment thread extensions/catalog.json
Comment thread src/specify_cli/integrations/base.py
Comment thread src/specify_cli/__init__.py
Comment thread src/specify_cli/commands/init.py
- Move agent-context extension install after init-options.json is saved
  so skill registration can read ai_skills + integration key
- Write extension config after install (avoids template overwriting context_file)
- Fix test_defaults_when_markers_field_missing to truly test missing markers key
- Update hermes tests to allow extension-installed agent-context skill
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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: 27/27 changed files
  • Comments generated: 4

Comment thread src/specify_cli/commands/init.py
Comment thread src/specify_cli/commands/init.py
Comment thread src/specify_cli/__init__.py
…AML key order

- Move ensure_executable_scripts after agent-context extension install
  so extension scripts get execute bits set
- Use preserve_markers=True on reinit to keep user-customized markers
- Add Python 3 version check in PowerShell fallback (matching bash behavior)
- Add sort_keys=False to yaml.safe_dump for stable config output
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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: 27/27 changed files
  • Comments generated: 3

Comment thread extensions/agent-context/scripts/bash/update-agent-context.sh
Comment thread src/specify_cli/__init__.py
- Reject absolute paths and '..' segments in context_file in both bash and
  PowerShell scripts to prevent writes outside the project root
- Fix docstring in _update_init_options_for_integration to accurately
  describe marker preservation behavior
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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: 27/27 changed files
  • Comments generated: 4

Comment thread src/specify_cli/integrations/base.py Outdated
Comment thread src/specify_cli/integrations/base.py 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
…raversal

- Use 'is not False' for enabled check so only literal False disables
- Update upsert_context_section docstring to mention disabled-extension return
- Fix path traversal guards to check actual path segments, not substrings
  (allows filenames like 'notes..md' while rejecting '../' traversal)
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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: 27/27 changed files
  • Comments generated: 6

Comment on lines +553 to +570
# --- agent-context extension (bundled, auto-installed) ---
# Installed after init-options.json is written so that skill
# registration can read ai_skills + integration key.
try:
from ..extensions import ExtensionManager as _ExtMgr
bundled_ac = _locate_bundled_extension("agent-context")
if bundled_ac:
ac_mgr = _ExtMgr(project_path)
if ac_mgr.registry.is_installed("agent-context"):
tracker.complete("agent-context", "already installed")
else:
ac_mgr.install_from_directory(
bundled_ac, get_speckit_version()
)
tracker.complete("agent-context", "extension installed")
else:
tracker.skip("agent-context", "bundled extension not found")
except Exception as ac_err:
Comment thread src/specify_cli/commands/init.py
Comment thread src/specify_cli/integrations/base.py
Comment thread src/specify_cli/__init__.py
Comment thread extensions/agent-context/README.md
Comment thread extensions/catalog.json
- Add UnicodeError to exception tuples in _load_agent_context_config and
  _resolve_context_markers so garbled UTF-8 config files fall back to defaults
- Emit error (with reinstall command) instead of silent skip when bundled
  agent-context extension is not found during init
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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: 27/27 changed files
  • Comments generated: 2

Comment on lines +102 to +114
# Reject absolute paths and '..' path segments in context_file
if [[ "$CONTEXT_FILE" == /* ]]; then
echo "agent-context: context_file must be a project-relative path; got '$CONTEXT_FILE'." >&2
exit 1
fi
IFS='/' read -ra _cf_parts <<< "$CONTEXT_FILE"
for _seg in "${_cf_parts[@]}"; do
if [[ "$_seg" == ".." ]]; then
echo "agent-context: context_file must not contain '..' path segments; got '$CONTEXT_FILE'." >&2
exit 1
fi
done
unset _cf_parts _seg
Comment on lines +553 to +560
# --- agent-context extension (bundled, auto-installed) ---
# Installed after init-options.json is written so that skill
# registration can read ai_skills + integration key.
try:
from ..extensions import ExtensionManager as _ExtMgr
bundled_ac = _locate_bundled_extension("agent-context")
if bundled_ac:
ac_mgr = _ExtMgr(project_path)
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.

[Feature] Extract agent context updates into an agent-context extension

3 participants