Skip to content

fix(agent-context): support multiple context files safely#2969

Open
AustinZ21 wants to merge 1 commit into
github:mainfrom
AustinZ21:fix/agent-context-multiple-files
Open

fix(agent-context): support multiple context files safely#2969
AustinZ21 wants to merge 1 commit into
github:mainfrom
AustinZ21:fix/agent-context-multiple-files

Conversation

@AustinZ21

Copy link
Copy Markdown

Summary

This PR lets the bundled agent-context extension manage more than one coding-agent context file from the same Spec Kit run.

The motivating workflow is a repo that uses Claude Code and Codex interchangeably and wants Spec Kit's generated context block synced to both CLAUDE.md and AGENTS.md. The implementation keeps the existing singular context_file behavior, while adding an optional context_files list for projects that intentionally keep multiple agent anchors in sync.

What Changed

  • Added optional context_files support to the agent-context config template.
  • Updated the bash and PowerShell update scripts to refresh every configured context file.
  • Updated Python integration setup/removal paths to upsert and remove the managed block across configured files.
  • Rendered command templates with a readable multi-file context target when context_files is configured.
  • Preserved existing non-empty context_files during integration switches.
  • Added project-root containment validation for context file paths:
    • rejects absolute paths
    • rejects Windows drive paths
    • rejects backslash separators
    • rejects .. path segments
    • confirms the resolved target stays inside the project root
  • Kept specify extension disable agent-context as a full opt-out: disabled projects skip upsert/removal and ignore stale context_files during command rendering.
  • Updated agent-context docs and command help to describe multi-file configuration and safety behavior.

Why

Today Spec Kit stores one managed context block target in context_file. That works well for a single coding agent, but mixed-agent projects can need more than one context anchor. For example, Codex reads AGENTS.md, while Claude-oriented setups often use CLAUDE.md. Without first-class multi-file support, teams have to choose one anchor or maintain duplicate context manually.

This change makes the multi-agent case explicit and configurable without hard-coding any specific agent names or file paths.

Safety Notes

The script and Python integration paths now enforce matching path constraints so a configured context file cannot escape the project root. The disabled-extension path also avoids validating stale config before the opt-out gate, so disabling agent-context remains a complete opt-out even if old config contains invalid paths.

Validation

  • bash -n extensions/agent-context/scripts/bash/update-agent-context.sh
  • python -m compileall -q src\specify_cli
  • pytest tests/extensions/test_extension_agent_context.py -q
    • 50 passed
  • pytest tests/integrations/test_integration_codex.py::TestCodexInitFlow::test_plan_skill_references_configured_context_files tests/integrations/test_integration_codex.py::TestCodexInitFlow::test_plan_skill_ignores_context_files_when_agent_context_disabled -q
    • 2 passed
  • pytest tests/test_agent_config_consistency.py -q
    • 28 passed
  • specify --help
  • Manual PowerShell smoke test:
    • configured context_files: [AGENTS.md, CLAUDE.md]
    • ran update-agent-context.ps1 specs/123-test/plan.md
    • verified both files were updated
  • Manual bash smoke test:
    • configured context_files: [AGENTS.md, CLAUDE.md]
    • ran update-agent-context.sh specs/123-test/plan.md
    • verified both files were updated

Known Existing Test Failure

The broader Codex integration slice still has two inventory failures:

  • TestCodexIntegration.test_complete_file_inventory_sh
  • TestCodexIntegration.test_complete_file_inventory_ps

Both failures reproduce on a clean upstream/main worktree at 1b0556c, before this PR's changes. They expect bundled agent-context extension artifacts in the generated project, but the generated project does not include them. The focused tests added by this PR pass.

AI Assistance Disclosure

I used Codex/ChatGPT to inspect the integration paths, draft the implementation, and generate test coverage. I reviewed the code paths and validated the behavior locally with automated and manual checks.

@AustinZ21 AustinZ21 marked this pull request as ready for review June 14, 2026 21:07
@AustinZ21 AustinZ21 requested a review from mnriem as a code owner June 14, 2026 21:07
@AustinZ21

Copy link
Copy Markdown
Author

Hi maintainers, I've marked this PR as ready for review. It looks like the workflows are awaiting approval before the checks can run. Thanks for taking a look.

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 extends the bundled agent-context extension and related integration plumbing to support syncing the managed Spec Kit context block into multiple configured context files (via optional context_files), while keeping the legacy single context_file behavior as a fallback.

Changes:

  • Add context_files support across Python integration context upsert/remove, command/template rendering, and init-option updates (preserve non-empty lists across integration switches).
  • Update bash and PowerShell agent-context update scripts to iterate over all configured context files.
  • Expand tests to cover multi-file upsert/remove, opt-out behavior, and path-safety rejection cases.
Show a summary per file
File Description
tests/integrations/test_integration_codex.py Adds coverage ensuring Codex skill templates render multi-file context targets and ignore context_files when agent-context is disabled.
tests/extensions/test_extension_agent_context.py Adds tests for multi-file upsert/remove behavior, invalid-path rejection, and opt-out behavior with stale config.
src/specify_cli/integrations/hermes/init.py Switches template rendering to use the computed context-file display string.
src/specify_cli/integrations/generic/init.py Switches template rendering to use the computed context-file display string.
src/specify_cli/integrations/forge/init.py Switches template rendering to use the computed context-file display string.
src/specify_cli/integrations/copilot/init.py Switches template rendering to use the computed context-file display string.
src/specify_cli/integrations/base.py Implements context_files resolution + validation and updates context upsert/remove to operate over multiple files.
src/specify_cli/integrations/_helpers.py Ensures integration switching/clearing handles context_files preservation/clearing appropriately.
src/specify_cli/agents.py Updates SKILL placeholder resolution to prefer context_files when agent-context is enabled.
src/specify_cli/init.py Extends agent-context config load/update helpers to understand/preserve context_files.
extensions/agent-context/scripts/powershell/update-agent-context.ps1 Updates the updater to parse context_files and iterate updates over multiple files.
extensions/agent-context/scripts/bash/update-agent-context.sh Updates the updater to parse context_files and iterate updates over multiple files.
extensions/agent-context/README.md Documents multi-file configuration and opt-out behavior.
extensions/agent-context/commands/speckit.agent-context.update.md Updates command docs to include context_files semantics and validation notes.
extensions/agent-context/agent-context-config.yml Adds context_files to the default config template and clarifies semantics.

Copilot's findings

Tip

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

  • Files reviewed: 15/15 changed files
  • Comments generated: 5

Comment on lines +759 to +763
def _context_file_display(self, project_root: Path) -> str:
"""Return human-readable context file target(s) for templates."""
if not self._agent_context_extension_enabled(project_root):
return self.context_file
return ", ".join(self._resolve_context_files(project_root))
Comment thread src/specify_cli/agents.py
Comment on lines +412 to +422
ac_cfg = _load_agent_context_config(project_root)
context_files = ac_cfg.get("context_files")
if isinstance(context_files, list):
context_file_values = [
value.strip()
for value in context_files
if isinstance(value, str) and value.strip()
]
context_file = ", ".join(dict.fromkeys(context_file_values))
if not context_file:
context_file = ac_cfg.get("context_file") or ""
Comment on lines +165 to 176
foreach ($ContextFile in $ContextFiles) {
# Reject absolute paths and '..' path segments in context files
if ([System.IO.Path]::IsPathRooted($ContextFile)) {
Write-Warning "agent-context: context files must be project-relative paths; got '$ContextFile'."
exit 1
}
$cfSegments = $ContextFile -split '[/\\]'
if ($cfSegments -contains '..') {
Write-Warning "agent-context: context files must not contain '..' path segments; got '$ContextFile'."
exit 1
}
}
Comment on lines +194 to +198
for CONTEXT_FILE in "${CONTEXT_FILES[@]}"; do
CTX_PATH="$PROJECT_ROOT/$CONTEXT_FILE"
mkdir -p "$(dirname "$CTX_PATH")"

"$_python" - "$CTX_PATH" "$MARKER_START" "$MARKER_END" "$TMP_SECTION" <<'PY'
Comment on lines +220 to +227
foreach ($ContextFile in $ContextFiles) {
$CtxPath = Join-Path $ProjectRoot $ContextFile
$CtxDir = Split-Path -Parent $CtxPath
if ($CtxDir -and -not (Test-Path -LiteralPath $CtxDir)) {
New-Item -ItemType Directory -Path $CtxDir -Force | Out-Null
}

if (Test-Path -LiteralPath $CtxPath) {
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.

3 participants