Skip to content

Commit 22d6ef5

Browse files
authored
Fix follow-up agent-context review issues
1 parent 84df982 commit 22d6ef5

5 files changed

Lines changed: 87 additions & 22 deletions

File tree

extensions/agent-context/agent-context-config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# Path (relative to the project root) to the coding agent context file
66
# managed by this extension (e.g. CLAUDE.md, AGENTS.md,
77
# .github/copilot-instructions.md). Set automatically from the active
8-
# integration; edit to override.
8+
# integration and regenerated during `specify init` or integration switches.
99
context_file: ""
1010

1111
# Delimiters for the managed Spec Kit section.

extensions/agent-context/scripts/bash/update-agent-context.sh

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,25 @@ fi
4141

4242
# Parse extension config once; emit three newline-separated fields:
4343
# context_file, context_markers.start, context_markers.end
44-
_raw_opts="$("$_python" - "$EXT_CONFIG" <<'PY'
44+
if ! _raw_opts="$("$_python" - "$EXT_CONFIG" <<'PY'
4545
import sys
4646
try:
4747
import yaml
4848
except ImportError:
49-
yaml = None
49+
print(
50+
"agent-context: PyYAML is required to parse extension config; cannot update context.",
51+
file=sys.stderr,
52+
)
53+
sys.exit(2)
5054
try:
5155
with open(sys.argv[1], "r", encoding="utf-8") as fh:
52-
data = yaml.safe_load(fh) if yaml else {}
53-
except Exception:
54-
data = {}
56+
data = yaml.safe_load(fh)
57+
except Exception as exc:
58+
print(
59+
f"agent-context: unable to parse {sys.argv[1]} ({exc}); cannot update context.",
60+
file=sys.stderr,
61+
)
62+
sys.exit(2)
5563
if not isinstance(data, dict):
5664
data = {}
5765
def get_str(obj, *keys):
@@ -66,7 +74,10 @@ print(get_str(data, "context_file"))
6674
print(get_str(data, "context_markers", "start"))
6775
print(get_str(data, "context_markers", "end"))
6876
PY
69-
)"
77+
)"; then
78+
echo "agent-context: failed to read extension config; skipping update." >&2
79+
exit 0
80+
fi
7081

7182
{
7283
IFS= read -r CONTEXT_FILE

extensions/agent-context/scripts/powershell/update-agent-context.ps1

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,25 @@ param(
1616
[string]$PlanPath
1717
)
1818

19+
function Get-ConfigValue {
20+
param(
21+
[AllowNull()][object]$Object,
22+
[Parameter(Mandatory = $true)][string]$Key
23+
)
24+
25+
if ($null -eq $Object) {
26+
return $null
27+
}
28+
if ($Object -is [System.Collections.IDictionary]) {
29+
return $Object[$Key]
30+
}
31+
$prop = $Object.PSObject.Properties[$Key]
32+
if ($prop) {
33+
return $prop.Value
34+
}
35+
return $null
36+
}
37+
1938
$ErrorActionPreference = 'Stop'
2039
$DefaultStart = '<!-- SPECKIT START -->'
2140
$DefaultEnd = '<!-- SPECKIT END -->'
@@ -47,22 +66,30 @@ import json
4766
import sys
4867
try:
4968
import yaml
50-
except Exception:
51-
yaml = None
69+
except ImportError:
70+
print(
71+
"agent-context: PyYAML is required to parse extension config; cannot update context.",
72+
file=sys.stderr,
73+
)
74+
sys.exit(2)
5275
5376
try:
5477
with open(sys.argv[1], "r", encoding="utf-8") as fh:
55-
data = yaml.safe_load(fh) if yaml else {}
56-
except Exception:
57-
data = {}
78+
data = yaml.safe_load(fh)
79+
except Exception as exc:
80+
print(
81+
f"agent-context: unable to parse {sys.argv[1]} ({exc}); cannot update context.",
82+
file=sys.stderr,
83+
)
84+
sys.exit(2)
5885
5986
if not isinstance(data, dict):
6087
data = {}
6188
6289
print(json.dumps(data))
6390
'@ $ExtConfig
6491
if ($LASTEXITCODE -eq 0 -and $jsonOut) {
65-
$Options = $jsonOut | ConvertFrom-Json -AsHashtable -ErrorAction Stop
92+
$Options = $jsonOut | ConvertFrom-Json -ErrorAction Stop
6693
}
6794
} catch {
6895
$Options = $null
@@ -75,21 +102,23 @@ print(json.dumps(data))
75102
}
76103
}
77104

78-
$ContextFile = $Options['context_file']
105+
$ContextFile = Get-ConfigValue -Object $Options -Key 'context_file'
79106
if (-not $ContextFile) {
80107
Write-Host 'agent-context: context_file not set in extension config; nothing to do.'
81108
exit 0
82109
}
83110

84111
$MarkerStart = $DefaultStart
85112
$MarkerEnd = $DefaultEnd
86-
$cm = $Options['context_markers']
113+
$cm = Get-ConfigValue -Object $Options -Key 'context_markers'
87114
if ($cm) {
88-
if ($cm['start'] -is [string] -and $cm['start']) {
89-
$MarkerStart = $cm['start']
115+
$cmStart = Get-ConfigValue -Object $cm -Key 'start'
116+
if ($cmStart -is [string] -and $cmStart) {
117+
$MarkerStart = $cmStart
90118
}
91-
if ($cm['end'] -is [string] -and $cm['end']) {
92-
$MarkerEnd = $cm['end']
119+
$cmEnd = Get-ConfigValue -Object $cm -Key 'end'
120+
if ($cmEnd -is [string] -and $cmEnd) {
121+
$MarkerEnd = $cmEnd
93122
}
94123
}
95124

src/specify_cli/__init__.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,20 +1549,24 @@ def _clear_init_options_for_integration(project_root: Path, integration_key: str
15491549
no stale path is left behind when the integration is uninstalled.
15501550
"""
15511551
opts = load_init_options(project_root)
1552+
has_legacy_context_keys = ("context_file" in opts) or ("context_markers" in opts)
1553+
# Remove legacy fields that older versions may have written.
1554+
opts.pop("context_file", None)
1555+
opts.pop("context_markers", None)
1556+
15521557
if opts.get("integration") == integration_key or opts.get("ai") == integration_key:
15531558
opts.pop("integration", None)
15541559
opts.pop("ai", None)
15551560
opts.pop("ai_skills", None)
1556-
# Remove legacy fields that older versions may have written.
1557-
opts.pop("context_file", None)
1558-
opts.pop("context_markers", None)
15591561
save_init_options(project_root, opts)
15601562
# Clear context_file in the extension config too.
15611563
ext_cfg_path = project_root / _AGENT_CTX_EXT_CONFIG
15621564
if ext_cfg_path.exists():
15631565
_update_agent_context_config_file(
15641566
project_root, "", preserve_markers=True
15651567
)
1568+
elif has_legacy_context_keys:
1569+
save_init_options(project_root, opts)
15661570

15671571

15681572
def _remove_integration_json(project_root: Path) -> None:

tests/extensions/test_extension_agent_context.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,27 @@ def test_clear_init_options_clears_ext_config_context_file(self, tmp_path):
294294
cfg = _load_agent_context_config(tmp_path)
295295
assert cfg.get("context_file") == ""
296296

297+
def test_clear_init_options_removes_legacy_context_keys_even_when_not_active(
298+
self, tmp_path
299+
):
300+
from specify_cli import _clear_init_options_for_integration
301+
302+
save_init_options(
303+
tmp_path,
304+
{
305+
"integration": "copilot",
306+
"ai": "copilot",
307+
"context_file": "CLAUDE.md",
308+
"context_markers": {"start": "<!-- X -->", "end": "<!-- Y -->"},
309+
},
310+
)
311+
_clear_init_options_for_integration(tmp_path, "claude")
312+
opts = load_init_options(tmp_path)
313+
assert opts["integration"] == "copilot"
314+
assert opts["ai"] == "copilot"
315+
assert "context_file" not in opts
316+
assert "context_markers" not in opts
317+
297318
def test_update_init_options_writes_context_file_to_ext_config(self, tmp_path):
298319
from specify_cli import _update_init_options_for_integration
299320

0 commit comments

Comments
 (0)