anvil-dev-framework 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +719 -0
- package/VERSION +1 -0
- package/docs/ANVIL-REPO-IMPLEMENTATION-PLAN.md +441 -0
- package/docs/FIRST-SKILL-TUTORIAL.md +408 -0
- package/docs/INSTALLATION-RETRO-NOTES.md +458 -0
- package/docs/INSTALLATION.md +984 -0
- package/docs/anvil-hud.md +469 -0
- package/docs/anvil-init.md +255 -0
- package/docs/anvil-state.md +210 -0
- package/docs/boris-cherny-ralph-wiggum-insights.md +608 -0
- package/docs/command-reference.md +2022 -0
- package/docs/hooks-tts.md +368 -0
- package/docs/implementation-guide.md +810 -0
- package/docs/linear-github-integration.md +247 -0
- package/docs/local-issues.md +677 -0
- package/docs/patterns/README.md +419 -0
- package/docs/planning-responsibilities.md +139 -0
- package/docs/session-workflow.md +573 -0
- package/docs/simplification-plan-template.md +297 -0
- package/docs/simplification-principles.md +129 -0
- package/docs/specifications/CCS-RALPH-INTEGRATION-DESIGN.md +633 -0
- package/docs/specifications/CCS-RESEARCH-REPORT.md +169 -0
- package/docs/specifications/PLAN-ANV-verification-ralph-wiggum.md +403 -0
- package/docs/specifications/PLAN-parallel-tracks-anvil-memory-ccs.md +494 -0
- package/docs/specifications/SPEC-ANV-VRW/component-01-verify.md +208 -0
- package/docs/specifications/SPEC-ANV-VRW/component-02-stop-gate.md +226 -0
- package/docs/specifications/SPEC-ANV-VRW/component-03-posttooluse.md +209 -0
- package/docs/specifications/SPEC-ANV-VRW/component-04-ralph-wiggum.md +604 -0
- package/docs/specifications/SPEC-ANV-VRW/component-05-atomic-actions.md +311 -0
- package/docs/specifications/SPEC-ANV-VRW/component-06-verify-subagent.md +264 -0
- package/docs/specifications/SPEC-ANV-VRW/component-07-claude-md.md +363 -0
- package/docs/specifications/SPEC-ANV-VRW/index.md +182 -0
- package/docs/specifications/SPEC-ANV-anvil-memory.md +573 -0
- package/docs/specifications/SPEC-ANV-context-checkpoints.md +781 -0
- package/docs/specifications/SPEC-ANV-verification-ralph-wiggum.md +789 -0
- package/docs/sync.md +122 -0
- package/global/CLAUDE.md +140 -0
- package/global/agents/verify-app.md +164 -0
- package/global/commands/anvil-settings.md +527 -0
- package/global/commands/anvil-sync.md +121 -0
- package/global/commands/change.md +197 -0
- package/global/commands/clarify.md +252 -0
- package/global/commands/cleanup.md +292 -0
- package/global/commands/commit-push-pr.md +207 -0
- package/global/commands/decay-review.md +127 -0
- package/global/commands/discover.md +158 -0
- package/global/commands/doc-coverage.md +122 -0
- package/global/commands/evidence.md +307 -0
- package/global/commands/explore.md +121 -0
- package/global/commands/force-exit.md +135 -0
- package/global/commands/handoff.md +191 -0
- package/global/commands/healthcheck.md +302 -0
- package/global/commands/hud.md +84 -0
- package/global/commands/insights.md +319 -0
- package/global/commands/linear-setup.md +184 -0
- package/global/commands/lint-fix.md +198 -0
- package/global/commands/orient.md +510 -0
- package/global/commands/plan.md +228 -0
- package/global/commands/ralph.md +346 -0
- package/global/commands/ready.md +182 -0
- package/global/commands/release.md +305 -0
- package/global/commands/retro.md +96 -0
- package/global/commands/shard.md +166 -0
- package/global/commands/spec.md +227 -0
- package/global/commands/sprint.md +184 -0
- package/global/commands/tasks.md +228 -0
- package/global/commands/test-and-commit.md +151 -0
- package/global/commands/validate.md +132 -0
- package/global/commands/verify.md +251 -0
- package/global/commands/weekly-review.md +156 -0
- package/global/hooks/__pycache__/ralph_context_monitor.cpython-314.pyc +0 -0
- package/global/hooks/__pycache__/statusline_agent_sync.cpython-314.pyc +0 -0
- package/global/hooks/anvil_memory_observe.ts +322 -0
- package/global/hooks/anvil_memory_session.ts +166 -0
- package/global/hooks/anvil_memory_stop.ts +187 -0
- package/global/hooks/parse_transcript.py +116 -0
- package/global/hooks/post_merge_cleanup.sh +132 -0
- package/global/hooks/post_tool_format.sh +215 -0
- package/global/hooks/ralph_context_monitor.py +240 -0
- package/global/hooks/ralph_stop.sh +502 -0
- package/global/hooks/statusline.sh +1110 -0
- package/global/hooks/statusline_agent_sync.py +224 -0
- package/global/hooks/stop_gate.sh +250 -0
- package/global/lib/.claude/anvil-state.json +21 -0
- package/global/lib/__pycache__/agent_registry.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/claim_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/coderabbit_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/config_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/coordination_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/doc_coverage_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/gate_logger.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/github_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/hygiene_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/issue_models.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/issue_provider.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/linear_data_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/linear_provider.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/local_provider.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/quality_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/ralph_state.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/state_manager.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/transcript_parser.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/verification_runner.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/verify_iteration.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/verify_subagent.cpython-314.pyc +0 -0
- package/global/lib/agent_registry.py +995 -0
- package/global/lib/anvil-state.sh +435 -0
- package/global/lib/claim_service.py +515 -0
- package/global/lib/coderabbit_service.py +314 -0
- package/global/lib/config_service.py +423 -0
- package/global/lib/coordination_service.py +331 -0
- package/global/lib/doc_coverage_service.py +1305 -0
- package/global/lib/gate_logger.py +316 -0
- package/global/lib/github_service.py +310 -0
- package/global/lib/handoff_generator.py +775 -0
- package/global/lib/hygiene_service.py +712 -0
- package/global/lib/issue_models.py +257 -0
- package/global/lib/issue_provider.py +339 -0
- package/global/lib/linear_data_service.py +210 -0
- package/global/lib/linear_provider.py +987 -0
- package/global/lib/linear_provider.py.backup +671 -0
- package/global/lib/local_provider.py +486 -0
- package/global/lib/orient_fast.py +457 -0
- package/global/lib/quality_service.py +470 -0
- package/global/lib/ralph_prompt_generator.py +563 -0
- package/global/lib/ralph_state.py +1202 -0
- package/global/lib/state_manager.py +417 -0
- package/global/lib/transcript_parser.py +597 -0
- package/global/lib/verification_runner.py +557 -0
- package/global/lib/verify_iteration.py +490 -0
- package/global/lib/verify_subagent.py +250 -0
- package/global/skills/README.md +155 -0
- package/global/skills/quality-gates/SKILL.md +252 -0
- package/global/skills/skill-template/SKILL.md +109 -0
- package/global/skills/testing-strategies/SKILL.md +337 -0
- package/global/templates/CHANGE-template.md +105 -0
- package/global/templates/HANDOFF-template.md +63 -0
- package/global/templates/PLAN-template.md +111 -0
- package/global/templates/SPEC-template.md +93 -0
- package/global/templates/ralph/PROMPT.md.template +89 -0
- package/global/templates/ralph/fix_plan.md.template +31 -0
- package/global/templates/ralph/progress.txt.template +23 -0
- package/global/tests/__pycache__/test_doc_coverage.cpython-314.pyc +0 -0
- package/global/tests/test_doc_coverage.py +520 -0
- package/global/tests/test_issue_models.py +299 -0
- package/global/tests/test_local_provider.py +323 -0
- package/global/tools/README.md +178 -0
- package/global/tools/__pycache__/anvil-hud.cpython-314.pyc +0 -0
- package/global/tools/anvil-hud.py +3622 -0
- package/global/tools/anvil-hud.py.bak +3318 -0
- package/global/tools/anvil-issue.py +432 -0
- package/global/tools/anvil-memory/CLAUDE.md +49 -0
- package/global/tools/anvil-memory/README.md +42 -0
- package/global/tools/anvil-memory/bun.lock +25 -0
- package/global/tools/anvil-memory/bunfig.toml +9 -0
- package/global/tools/anvil-memory/package.json +23 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/context-monitor.test.ts +535 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/edge-cases.test.ts +645 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/fixtures.ts +363 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/index.ts +8 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/integration.test.ts +417 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/prompt-generator.test.ts +571 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/ralph-stop.test.ts +440 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/test-utils.ts +252 -0
- package/global/tools/anvil-memory/src/__tests__/commands.test.ts +657 -0
- package/global/tools/anvil-memory/src/__tests__/db.test.ts +641 -0
- package/global/tools/anvil-memory/src/__tests__/hooks.test.ts +272 -0
- package/global/tools/anvil-memory/src/__tests__/performance.test.ts +427 -0
- package/global/tools/anvil-memory/src/__tests__/test-utils.ts +113 -0
- package/global/tools/anvil-memory/src/commands/checkpoint.ts +197 -0
- package/global/tools/anvil-memory/src/commands/get.ts +115 -0
- package/global/tools/anvil-memory/src/commands/init.ts +94 -0
- package/global/tools/anvil-memory/src/commands/observe.ts +163 -0
- package/global/tools/anvil-memory/src/commands/search.ts +112 -0
- package/global/tools/anvil-memory/src/db.ts +638 -0
- package/global/tools/anvil-memory/src/index.ts +205 -0
- package/global/tools/anvil-memory/src/types.ts +122 -0
- package/global/tools/anvil-memory/tsconfig.json +29 -0
- package/global/tools/ralph-loop.sh +359 -0
- package/package.json +45 -0
- package/scripts/anvil +822 -0
- package/scripts/extract_patterns.py +222 -0
- package/scripts/init-project.sh +541 -0
- package/scripts/install.sh +229 -0
- package/scripts/postinstall.js +41 -0
- package/scripts/rollback.sh +188 -0
- package/scripts/sync.sh +623 -0
- package/scripts/test-statusline.sh +248 -0
- package/scripts/update_claude_md.py +224 -0
- package/scripts/verify.sh +255 -0
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
state_manager.py - Anvil Session State Management (ANV-176)
|
|
4
|
+
|
|
5
|
+
Manages the local .claude/anvil-state.json file and syncs changes
|
|
6
|
+
to the global agent registry for multi-agent visibility.
|
|
7
|
+
|
|
8
|
+
This module solves the "stale statusline" problem where issue/phase
|
|
9
|
+
indicators show outdated information. By calling update_state() from
|
|
10
|
+
workflow commands (/orient, /explore, /spec, etc.), the statusline
|
|
11
|
+
always reflects the current workflow state.
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
from global.lib.state_manager import update_state, get_state
|
|
15
|
+
|
|
16
|
+
# Update phase when running /explore
|
|
17
|
+
update_state(phase="explore", last_command="/explore")
|
|
18
|
+
|
|
19
|
+
# Update issue when claiming Linear work
|
|
20
|
+
update_state(active_issue="ANV-180", phase="implement")
|
|
21
|
+
|
|
22
|
+
# Clear issue when starting fresh
|
|
23
|
+
update_state(active_issue=None, phase="orient")
|
|
24
|
+
|
|
25
|
+
# Read current state
|
|
26
|
+
state = get_state()
|
|
27
|
+
print(state.get("session", {}).get("phase"))
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
import json
|
|
31
|
+
import os
|
|
32
|
+
import tempfile
|
|
33
|
+
from datetime import datetime, timezone
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
from typing import Any, Dict, Optional
|
|
36
|
+
|
|
37
|
+
# Try to import agent registry for sync
|
|
38
|
+
try:
|
|
39
|
+
from agent_registry import update_agent
|
|
40
|
+
REGISTRY_AVAILABLE = True
|
|
41
|
+
except ImportError:
|
|
42
|
+
try:
|
|
43
|
+
# Try relative import for when run as module
|
|
44
|
+
from .agent_registry import update_agent
|
|
45
|
+
REGISTRY_AVAILABLE = True
|
|
46
|
+
except ImportError:
|
|
47
|
+
REGISTRY_AVAILABLE = False
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# =============================================================================
|
|
51
|
+
# Configuration
|
|
52
|
+
# =============================================================================
|
|
53
|
+
|
|
54
|
+
STATE_FILE = Path(".claude/anvil-state.json")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# =============================================================================
|
|
58
|
+
# State Operations
|
|
59
|
+
# =============================================================================
|
|
60
|
+
|
|
61
|
+
def _atomic_write(filepath: Path, data: Dict[str, Any]) -> None:
|
|
62
|
+
"""Write data to file atomically (write temp, then rename)."""
|
|
63
|
+
dir_path = filepath.parent
|
|
64
|
+
dir_path.mkdir(parents=True, exist_ok=True)
|
|
65
|
+
|
|
66
|
+
with tempfile.NamedTemporaryFile(
|
|
67
|
+
mode='w',
|
|
68
|
+
dir=str(dir_path),
|
|
69
|
+
delete=False,
|
|
70
|
+
suffix='.tmp'
|
|
71
|
+
) as tmp:
|
|
72
|
+
json.dump(data, tmp, indent=2)
|
|
73
|
+
tmp_path = tmp.name
|
|
74
|
+
|
|
75
|
+
os.rename(tmp_path, str(filepath))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _read_state() -> Dict[str, Any]:
|
|
79
|
+
"""Read state file, creating default structure if missing."""
|
|
80
|
+
if not STATE_FILE.exists():
|
|
81
|
+
return {
|
|
82
|
+
"version": "1.0",
|
|
83
|
+
"session": {},
|
|
84
|
+
"cache": {"git": {}},
|
|
85
|
+
"meta": {
|
|
86
|
+
"createdAt": datetime.now(timezone.utc).isoformat(),
|
|
87
|
+
"updatedAt": datetime.now(timezone.utc).isoformat(),
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
with open(STATE_FILE, 'r') as f:
|
|
93
|
+
return json.load(f)
|
|
94
|
+
except (json.JSONDecodeError, IOError):
|
|
95
|
+
# Corrupted file, return fresh
|
|
96
|
+
return {
|
|
97
|
+
"version": "1.0",
|
|
98
|
+
"session": {},
|
|
99
|
+
"cache": {"git": {}},
|
|
100
|
+
"meta": {
|
|
101
|
+
"createdAt": datetime.now(timezone.utc).isoformat(),
|
|
102
|
+
"updatedAt": datetime.now(timezone.utc).isoformat(),
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _write_state(state: Dict[str, Any]) -> None:
|
|
108
|
+
"""Write state file atomically."""
|
|
109
|
+
state.setdefault("meta", {})
|
|
110
|
+
state["meta"]["updatedAt"] = datetime.now(timezone.utc).isoformat()
|
|
111
|
+
_atomic_write(STATE_FILE, state)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _sync_to_registry(session: Dict[str, Any]) -> None:
|
|
115
|
+
"""Sync session state to agent registry for HUD visibility."""
|
|
116
|
+
if not REGISTRY_AVAILABLE:
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
agent_id = session.get("agentId")
|
|
120
|
+
if not agent_id:
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
update_agent(
|
|
125
|
+
agent_id=agent_id,
|
|
126
|
+
phase=session.get("phase"),
|
|
127
|
+
issue=session.get("activeIssue"),
|
|
128
|
+
)
|
|
129
|
+
except Exception:
|
|
130
|
+
# Don't fail if registry update fails
|
|
131
|
+
pass
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def get_state() -> Dict[str, Any]:
|
|
135
|
+
"""Get the current state.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Complete state dictionary
|
|
139
|
+
"""
|
|
140
|
+
return _read_state()
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def get_session() -> Dict[str, Any]:
|
|
144
|
+
"""Get the session portion of state.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
Session dictionary with agentId, phase, activeIssue, etc.
|
|
148
|
+
"""
|
|
149
|
+
state = _read_state()
|
|
150
|
+
return state.get("session", {})
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def update_state(
|
|
154
|
+
phase: Optional[str] = None,
|
|
155
|
+
active_issue: Optional[str] = None,
|
|
156
|
+
active_spec: Optional[str] = None,
|
|
157
|
+
active_plan: Optional[str] = None,
|
|
158
|
+
last_command: Optional[str] = None,
|
|
159
|
+
clear_issue: bool = False,
|
|
160
|
+
) -> None:
|
|
161
|
+
"""Update anvil-state.json with new values and sync to registry.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
phase: Workflow phase (orient, explore, spec, plan, tasks, implement, verify, handoff)
|
|
165
|
+
active_issue: Linear issue key (e.g., "ANV-180") or None to clear
|
|
166
|
+
active_spec: Active spec file path or None to clear
|
|
167
|
+
active_plan: Active plan file path or None to clear
|
|
168
|
+
last_command: Name of command that triggered this update
|
|
169
|
+
clear_issue: If True, explicitly clears activeIssue (vs None meaning no change)
|
|
170
|
+
|
|
171
|
+
Example:
|
|
172
|
+
# When running /explore
|
|
173
|
+
update_state(phase="explore", last_command="/explore")
|
|
174
|
+
|
|
175
|
+
# When claiming an issue
|
|
176
|
+
update_state(active_issue="ANV-180", phase="implement")
|
|
177
|
+
|
|
178
|
+
# When starting fresh with /orient
|
|
179
|
+
update_state(phase="orient", clear_issue=True)
|
|
180
|
+
"""
|
|
181
|
+
state = _read_state()
|
|
182
|
+
session = state.setdefault("session", {})
|
|
183
|
+
|
|
184
|
+
# Update fields if provided
|
|
185
|
+
if phase is not None:
|
|
186
|
+
session["phase"] = phase
|
|
187
|
+
|
|
188
|
+
if active_issue is not None:
|
|
189
|
+
session["activeIssue"] = active_issue
|
|
190
|
+
elif clear_issue:
|
|
191
|
+
session["activeIssue"] = None
|
|
192
|
+
|
|
193
|
+
if active_spec is not None:
|
|
194
|
+
session["activeSpec"] = active_spec if active_spec else None
|
|
195
|
+
|
|
196
|
+
if active_plan is not None:
|
|
197
|
+
session["activePlan"] = active_plan if active_plan else None
|
|
198
|
+
|
|
199
|
+
if last_command is not None:
|
|
200
|
+
session["lastCommand"] = last_command
|
|
201
|
+
session["lastCommandAt"] = datetime.now(timezone.utc).isoformat()
|
|
202
|
+
|
|
203
|
+
# Write state and sync to registry
|
|
204
|
+
_write_state(state)
|
|
205
|
+
_sync_to_registry(session)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def set_issue(issue_key: str) -> None:
|
|
209
|
+
"""Convenience function to set the active issue.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
issue_key: Linear issue key (e.g., "ANV-180")
|
|
213
|
+
"""
|
|
214
|
+
update_state(active_issue=issue_key)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def clear_issue() -> None:
|
|
218
|
+
"""Convenience function to clear the active issue."""
|
|
219
|
+
update_state(clear_issue=True)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def set_phase(phase: str) -> None:
|
|
223
|
+
"""Convenience function to set the workflow phase.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
phase: Workflow phase name
|
|
227
|
+
"""
|
|
228
|
+
update_state(phase=phase)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# =============================================================================
|
|
232
|
+
# Command-specific helpers
|
|
233
|
+
# =============================================================================
|
|
234
|
+
|
|
235
|
+
def on_orient() -> None:
|
|
236
|
+
"""Called when /orient command runs."""
|
|
237
|
+
update_state(phase="orient", clear_issue=True, last_command="/orient")
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def on_explore() -> None:
|
|
241
|
+
"""Called when /explore command runs."""
|
|
242
|
+
update_state(phase="explore", last_command="/explore")
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def on_spec(spec_path: Optional[str] = None) -> None:
|
|
246
|
+
"""Called when /spec command runs.
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
spec_path: Path to the created spec file
|
|
250
|
+
"""
|
|
251
|
+
update_state(phase="spec", active_spec=spec_path, last_command="/spec")
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def on_plan(plan_path: Optional[str] = None) -> None:
|
|
255
|
+
"""Called when /plan command runs.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
plan_path: Path to the created plan file
|
|
259
|
+
"""
|
|
260
|
+
update_state(phase="plan", active_plan=plan_path, last_command="/plan")
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def on_tasks() -> None:
|
|
264
|
+
"""Called when /tasks command runs."""
|
|
265
|
+
update_state(phase="tasks", last_command="/tasks")
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def on_evidence() -> None:
|
|
269
|
+
"""Called when /evidence command runs."""
|
|
270
|
+
update_state(phase="verify", last_command="/evidence")
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def on_handoff() -> None:
|
|
274
|
+
"""Called when /handoff command runs."""
|
|
275
|
+
update_state(phase="handoff", last_command="/handoff")
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def on_claim_issue(issue_key: str) -> None:
|
|
279
|
+
"""Called when an issue is claimed from Linear.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
issue_key: Linear issue key (e.g., "ANV-180")
|
|
283
|
+
"""
|
|
284
|
+
update_state(active_issue=issue_key, phase="implement")
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def on_doc_coverage(
|
|
288
|
+
percent: float,
|
|
289
|
+
total: int,
|
|
290
|
+
documented: int,
|
|
291
|
+
status: str,
|
|
292
|
+
) -> None:
|
|
293
|
+
"""Called when /doc-coverage command runs.
|
|
294
|
+
|
|
295
|
+
Stores coverage snapshot for tracking over time.
|
|
296
|
+
|
|
297
|
+
Note: This function directly manipulates state rather than using update_state()
|
|
298
|
+
because doc-coverage is a reporting command (not a workflow phase change) and
|
|
299
|
+
requires storing structured data (docCoverage object) not supported by update_state().
|
|
300
|
+
Registry sync is intentionally skipped since coverage reports don't affect agent status.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
percent: Coverage percentage (0-100)
|
|
304
|
+
total: Total number of exports
|
|
305
|
+
documented: Number of documented exports
|
|
306
|
+
status: Coverage status (healthy, warning, critical)
|
|
307
|
+
"""
|
|
308
|
+
state = _read_state()
|
|
309
|
+
session = state.setdefault("session", {})
|
|
310
|
+
|
|
311
|
+
session["docCoverage"] = {
|
|
312
|
+
"percent": percent,
|
|
313
|
+
"total": total,
|
|
314
|
+
"documented": documented,
|
|
315
|
+
"status": status,
|
|
316
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
317
|
+
}
|
|
318
|
+
session["lastCommand"] = "/doc-coverage"
|
|
319
|
+
session["lastCommandAt"] = datetime.now(timezone.utc).isoformat()
|
|
320
|
+
|
|
321
|
+
_write_state(state)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
# =============================================================================
|
|
325
|
+
# CLI Interface
|
|
326
|
+
# =============================================================================
|
|
327
|
+
|
|
328
|
+
if __name__ == "__main__":
|
|
329
|
+
import sys
|
|
330
|
+
|
|
331
|
+
if len(sys.argv) < 2:
|
|
332
|
+
print("Usage: state_manager.py <command> [args...]")
|
|
333
|
+
print("Commands:")
|
|
334
|
+
print(" get - Show current state")
|
|
335
|
+
print(" set-phase <phase> - Set workflow phase")
|
|
336
|
+
print(" set-issue <key> - Set active issue")
|
|
337
|
+
print(" clear-issue - Clear active issue")
|
|
338
|
+
print(" orient - Run orient handler")
|
|
339
|
+
print(" explore - Run explore handler")
|
|
340
|
+
print(" spec [path] - Run spec handler")
|
|
341
|
+
print(" plan [path] - Run plan handler")
|
|
342
|
+
print(" tasks - Run tasks handler")
|
|
343
|
+
print(" evidence - Run evidence handler")
|
|
344
|
+
print(" handoff - Run handoff handler")
|
|
345
|
+
print(" doc-coverage <p> <t> <d> <s> - Store coverage snapshot")
|
|
346
|
+
sys.exit(1)
|
|
347
|
+
|
|
348
|
+
cmd = sys.argv[1]
|
|
349
|
+
|
|
350
|
+
if cmd == "get":
|
|
351
|
+
state = get_state()
|
|
352
|
+
print(json.dumps(state, indent=2))
|
|
353
|
+
|
|
354
|
+
elif cmd == "set-phase":
|
|
355
|
+
if len(sys.argv) < 3:
|
|
356
|
+
print("Error: phase required")
|
|
357
|
+
sys.exit(1)
|
|
358
|
+
set_phase(sys.argv[2])
|
|
359
|
+
print(f"Phase set to: {sys.argv[2]}")
|
|
360
|
+
|
|
361
|
+
elif cmd == "set-issue":
|
|
362
|
+
if len(sys.argv) < 3:
|
|
363
|
+
print("Error: issue key required")
|
|
364
|
+
sys.exit(1)
|
|
365
|
+
set_issue(sys.argv[2])
|
|
366
|
+
print(f"Active issue set to: {sys.argv[2]}")
|
|
367
|
+
|
|
368
|
+
elif cmd == "clear-issue":
|
|
369
|
+
clear_issue()
|
|
370
|
+
print("Active issue cleared")
|
|
371
|
+
|
|
372
|
+
elif cmd == "orient":
|
|
373
|
+
on_orient()
|
|
374
|
+
print("Orient handler executed")
|
|
375
|
+
|
|
376
|
+
elif cmd == "explore":
|
|
377
|
+
on_explore()
|
|
378
|
+
print("Explore handler executed")
|
|
379
|
+
|
|
380
|
+
elif cmd == "spec":
|
|
381
|
+
path = sys.argv[2] if len(sys.argv) > 2 else None
|
|
382
|
+
on_spec(path)
|
|
383
|
+
print(f"Spec handler executed{f' with path: {path}' if path else ''}")
|
|
384
|
+
|
|
385
|
+
elif cmd == "plan":
|
|
386
|
+
path = sys.argv[2] if len(sys.argv) > 2 else None
|
|
387
|
+
on_plan(path)
|
|
388
|
+
print(f"Plan handler executed{f' with path: {path}' if path else ''}")
|
|
389
|
+
|
|
390
|
+
elif cmd == "tasks":
|
|
391
|
+
on_tasks()
|
|
392
|
+
print("Tasks handler executed")
|
|
393
|
+
|
|
394
|
+
elif cmd == "evidence":
|
|
395
|
+
on_evidence()
|
|
396
|
+
print("Evidence handler executed")
|
|
397
|
+
|
|
398
|
+
elif cmd == "handoff":
|
|
399
|
+
on_handoff()
|
|
400
|
+
print("Handoff handler executed")
|
|
401
|
+
|
|
402
|
+
elif cmd == "doc-coverage":
|
|
403
|
+
if len(sys.argv) < 6:
|
|
404
|
+
print("Error: doc-coverage requires percent total documented status")
|
|
405
|
+
print("Usage: state_manager.py doc-coverage <percent> <total> <documented> <status>")
|
|
406
|
+
sys.exit(1)
|
|
407
|
+
on_doc_coverage(
|
|
408
|
+
percent=float(sys.argv[2]),
|
|
409
|
+
total=int(sys.argv[3]),
|
|
410
|
+
documented=int(sys.argv[4]),
|
|
411
|
+
status=sys.argv[5],
|
|
412
|
+
)
|
|
413
|
+
print(f"Doc coverage recorded: {sys.argv[2]}%")
|
|
414
|
+
|
|
415
|
+
else:
|
|
416
|
+
print(f"Unknown command: {cmd}")
|
|
417
|
+
sys.exit(1)
|