Skip to content

Commit 8073e08

Browse files
authored
Address latest agent-context review feedback
1 parent d4a1754 commit 8073e08

5 files changed

Lines changed: 78 additions & 19 deletions

File tree

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,14 @@ PY
8282
exit 0
8383
fi
8484

85-
{
86-
IFS= read -r CONTEXT_FILE
87-
IFS= read -r MARKER_START
88-
IFS= read -r MARKER_END
89-
} <<< "$_raw_opts"
85+
mapfile -t _opts_lines <<< "$_raw_opts"
86+
if (( ${#_opts_lines[@]} < 3 )); then
87+
echo "agent-context: malformed config parser output; expected 3 lines, got ${#_opts_lines[@]}; skipping update." >&2
88+
exit 0
89+
fi
90+
CONTEXT_FILE="${_opts_lines[0]}"
91+
MARKER_START="${_opts_lines[1]}"
92+
MARKER_END="${_opts_lines[2]}"
9093

9194
if [[ -z "$CONTEXT_FILE" ]]; then
9295
echo "agent-context: context_file not set in extension config; nothing to do." >&2

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,23 @@ function Get-ConfigValue {
3535
return $null
3636
}
3737

38+
function Test-ConfigObject {
39+
param(
40+
[AllowNull()][object]$Object
41+
)
42+
43+
if ($null -eq $Object) {
44+
return $false
45+
}
46+
if ($Object -is [System.Collections.IDictionary]) {
47+
return $true
48+
}
49+
if ($Object -is [System.Management.Automation.PSCustomObject]) {
50+
return $true
51+
}
52+
return $false
53+
}
54+
3855
$ErrorActionPreference = 'Stop'
3956
$DefaultStart = '<!-- SPECKIT START -->'
4057
$DefaultEnd = '<!-- SPECKIT END -->'
@@ -102,6 +119,11 @@ print(json.dumps(data))
102119
}
103120
}
104121

122+
if (-not (Test-ConfigObject -Object $Options)) {
123+
Write-Warning "agent-context: $ExtConfig must contain a YAML mapping; skipping update."
124+
exit 0
125+
}
126+
105127
$ContextFile = Get-ConfigValue -Object $Options -Key 'context_file'
106128
if (-not $ContextFile) {
107129
Write-Host 'agent-context: context_file not set in extension config; nothing to do.'

src/specify_cli/__init__.py

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -990,26 +990,35 @@ def init(
990990
# Install bundled agent-context extension (opt-out via
991991
# `specify extension disable agent-context`). Owns the managed
992992
# section in coding agent context files (CLAUDE.md, etc.).
993-
# Installed AFTER init-options are saved so hooks can read from
994-
# the project. After install, the extension config is updated
995-
# with the active integration's context_file.
993+
# Installed after init-options are saved so project metadata
994+
# reflects the selected integration before extension setup.
995+
# The extension config is then updated (in finally) with the
996+
# active integration's context_file.
996997
tracker.start("agent-context")
997998
_ac_bundled: Path | None = None
998999
_ac_err_msg: str | None = None
1000+
_ac_terminal: str | None = None
1001+
_ac_terminal_detail = ""
9991002
try:
10001003
from .extensions import ExtensionManager as _AgentCtxMgr
10011004
_ac_bundled = _locate_bundled_extension("agent-context")
10021005
if _ac_bundled:
10031006
ac_manager = _AgentCtxMgr(project_path)
10041007
if ac_manager.registry.is_installed("agent-context"):
1005-
tracker.complete("agent-context", "already installed")
1008+
_ac_terminal = "complete"
1009+
_ac_terminal_detail = "already installed"
1010+
tracker.complete("agent-context", _ac_terminal_detail)
10061011
else:
10071012
ac_manager.install_from_directory(
10081013
_ac_bundled, get_speckit_version()
10091014
)
1010-
tracker.complete("agent-context", "installed")
1015+
_ac_terminal = "complete"
1016+
_ac_terminal_detail = "installed"
1017+
tracker.complete("agent-context", _ac_terminal_detail)
10111018
else:
1012-
tracker.skip("agent-context", "bundled extension not found")
1019+
_ac_terminal = "skip"
1020+
_ac_terminal_detail = "bundled extension not found"
1021+
tracker.skip("agent-context", _ac_terminal_detail)
10131022
except Exception as ac_err:
10141023
sanitized_ac = str(ac_err).replace('\n', ' ').strip()
10151024
_ac_err_msg = f"install failed: {sanitized_ac[:120]}"
@@ -1037,7 +1046,22 @@ def init(
10371046
else:
10381047
_ac_err_msg = cfg_msg
10391048
if _ac_err_msg is not None:
1040-
tracker.error("agent-context", _ac_err_msg)
1049+
if _ac_terminal == "complete":
1050+
tracker.complete(
1051+
"agent-context",
1052+
f"{_ac_terminal_detail}; {_ac_err_msg}"
1053+
if _ac_terminal_detail
1054+
else _ac_err_msg,
1055+
)
1056+
elif _ac_terminal == "skip":
1057+
tracker.skip(
1058+
"agent-context",
1059+
f"{_ac_terminal_detail}; {_ac_err_msg}"
1060+
if _ac_terminal_detail
1061+
else _ac_err_msg,
1062+
)
1063+
else:
1064+
tracker.error("agent-context", _ac_err_msg)
10411065

10421066
# Fix permissions after all installs (scripts + extensions)
10431067
ensure_executable_scripts(project_path, tracker=tracker)
@@ -1568,12 +1592,11 @@ def _clear_init_options_for_integration(project_root: Path, integration_key: str
15681592
opts.pop("ai", None)
15691593
opts.pop("ai_skills", None)
15701594
save_init_options(project_root, opts)
1571-
# Clear context_file in the extension config too.
1572-
ext_cfg_path = project_root / _AGENT_CTX_EXT_CONFIG
1573-
if ext_cfg_path.exists():
1574-
_update_agent_context_config_file(
1575-
project_root, "", preserve_markers=True
1576-
)
1595+
# Clear context_file in the extension config too. If the config file
1596+
# does not exist yet, create it so no stale target can persist.
1597+
_update_agent_context_config_file(
1598+
project_root, "", preserve_markers=True
1599+
)
15771600
elif has_legacy_context_keys:
15781601
save_init_options(project_root, opts)
15791602

src/specify_cli/agents.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ def resolve_skill_placeholders(
383383
from . import _load_agent_context_config
384384
ac_cfg = _load_agent_context_config(project_root)
385385
context_file = ac_cfg.get("context_file") or ""
386-
except (ImportError, OSError, yaml.YAMLError):
386+
except ImportError:
387387
# Best-effort read: ignore extension config load/parse errors and
388388
# fall back to init-options.json context_file below.
389389
context_file = ""

tests/extensions/test_extension_agent_context.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,17 @@ 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_creates_ext_config_when_missing(self, tmp_path):
298+
from specify_cli import _clear_init_options_for_integration
299+
300+
save_init_options(
301+
tmp_path,
302+
{"integration": "claude", "ai": "claude"},
303+
)
304+
_clear_init_options_for_integration(tmp_path, "claude")
305+
cfg = _load_agent_context_config(tmp_path)
306+
assert cfg.get("context_file") == ""
307+
297308
def test_clear_init_options_removes_legacy_context_keys_even_when_not_active(
298309
self, tmp_path
299310
):

0 commit comments

Comments
 (0)