aiwcli 0.9.0

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.
Files changed (204) hide show
  1. package/README.md +1248 -0
  2. package/bin/dev.cmd +3 -0
  3. package/bin/dev.js +16 -0
  4. package/bin/run.cmd +3 -0
  5. package/bin/run.js +19 -0
  6. package/dist/commands/branch.d.ts +45 -0
  7. package/dist/commands/branch.js +488 -0
  8. package/dist/commands/clean.d.ts +34 -0
  9. package/dist/commands/clean.js +186 -0
  10. package/dist/commands/clear.d.ts +51 -0
  11. package/dist/commands/clear.js +835 -0
  12. package/dist/commands/init/index.d.ts +107 -0
  13. package/dist/commands/init/index.js +565 -0
  14. package/dist/commands/launch.d.ts +21 -0
  15. package/dist/commands/launch.js +108 -0
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.js +1 -0
  18. package/dist/lib/base-command.d.ts +114 -0
  19. package/dist/lib/base-command.js +153 -0
  20. package/dist/lib/bmad-installer.d.ts +38 -0
  21. package/dist/lib/bmad-installer.js +145 -0
  22. package/dist/lib/claude-settings-types.d.ts +102 -0
  23. package/dist/lib/claude-settings-types.js +5 -0
  24. package/dist/lib/config.d.ts +25 -0
  25. package/dist/lib/config.js +46 -0
  26. package/dist/lib/debug.d.ts +39 -0
  27. package/dist/lib/debug.js +74 -0
  28. package/dist/lib/env-compat.d.ts +26 -0
  29. package/dist/lib/env-compat.js +35 -0
  30. package/dist/lib/errors.d.ts +126 -0
  31. package/dist/lib/errors.js +145 -0
  32. package/dist/lib/generic-merge.d.ts +74 -0
  33. package/dist/lib/generic-merge.js +105 -0
  34. package/dist/lib/git/branch.d.ts +67 -0
  35. package/dist/lib/git/branch.js +155 -0
  36. package/dist/lib/git/index.d.ts +11 -0
  37. package/dist/lib/git/index.js +13 -0
  38. package/dist/lib/git/safety-checks.d.ts +44 -0
  39. package/dist/lib/git/safety-checks.js +102 -0
  40. package/dist/lib/git/types.d.ts +31 -0
  41. package/dist/lib/git/types.js +6 -0
  42. package/dist/lib/git/worktree.d.ts +67 -0
  43. package/dist/lib/git/worktree.js +220 -0
  44. package/dist/lib/gitignore-manager.d.ts +10 -0
  45. package/dist/lib/gitignore-manager.js +60 -0
  46. package/dist/lib/hooks-merger.d.ts +28 -0
  47. package/dist/lib/hooks-merger.js +94 -0
  48. package/dist/lib/ide-path-resolver.d.ts +102 -0
  49. package/dist/lib/ide-path-resolver.js +129 -0
  50. package/dist/lib/index.d.ts +13 -0
  51. package/dist/lib/index.js +22 -0
  52. package/dist/lib/output.d.ts +51 -0
  53. package/dist/lib/output.js +76 -0
  54. package/dist/lib/paths.d.ts +66 -0
  55. package/dist/lib/paths.js +136 -0
  56. package/dist/lib/quiet.d.ts +12 -0
  57. package/dist/lib/quiet.js +17 -0
  58. package/dist/lib/settings-hierarchy.d.ts +42 -0
  59. package/dist/lib/settings-hierarchy.js +105 -0
  60. package/dist/lib/spawn.d.ts +105 -0
  61. package/dist/lib/spawn.js +157 -0
  62. package/dist/lib/spinner.d.ts +19 -0
  63. package/dist/lib/spinner.js +34 -0
  64. package/dist/lib/stdin.d.ts +48 -0
  65. package/dist/lib/stdin.js +60 -0
  66. package/dist/lib/template-installer.d.ts +92 -0
  67. package/dist/lib/template-installer.js +375 -0
  68. package/dist/lib/template-linter.d.ts +49 -0
  69. package/dist/lib/template-linter.js +173 -0
  70. package/dist/lib/template-merger.d.ts +47 -0
  71. package/dist/lib/template-merger.js +173 -0
  72. package/dist/lib/template-resolver.d.ts +20 -0
  73. package/dist/lib/template-resolver.js +60 -0
  74. package/dist/lib/terminal.d.ts +102 -0
  75. package/dist/lib/terminal.js +245 -0
  76. package/dist/lib/tty-detection.d.ts +62 -0
  77. package/dist/lib/tty-detection.js +83 -0
  78. package/dist/lib/user-utils.d.ts +5 -0
  79. package/dist/lib/user-utils.js +23 -0
  80. package/dist/lib/version.d.ts +99 -0
  81. package/dist/lib/version.js +144 -0
  82. package/dist/lib/watch-templates.d.ts +6 -0
  83. package/dist/lib/watch-templates.js +73 -0
  84. package/dist/lib/windsurf-hooks-hierarchy.d.ts +30 -0
  85. package/dist/lib/windsurf-hooks-hierarchy.js +66 -0
  86. package/dist/lib/windsurf-hooks-merger.d.ts +26 -0
  87. package/dist/lib/windsurf-hooks-merger.js +53 -0
  88. package/dist/lib/windsurf-hooks-types.d.ts +33 -0
  89. package/dist/lib/windsurf-hooks-types.js +5 -0
  90. package/dist/templates/CLAUDE.md +174 -0
  91. package/dist/templates/_shared/.claude/commands/handoff.md +14 -0
  92. package/dist/templates/_shared/.claude/settings.json +61 -0
  93. package/dist/templates/_shared/.codex/workflows/handoff.md +14 -0
  94. package/dist/templates/_shared/.windsurf/workflows/handoff.md +14 -0
  95. package/dist/templates/_shared/hooks/__init__.py +16 -0
  96. package/dist/templates/_shared/hooks/archive_plan.py +270 -0
  97. package/dist/templates/_shared/hooks/context_enforcer.py +621 -0
  98. package/dist/templates/_shared/hooks/context_monitor.py +322 -0
  99. package/dist/templates/_shared/hooks/file-suggestion.py +188 -0
  100. package/dist/templates/_shared/hooks/task_create_capture.py +194 -0
  101. package/dist/templates/_shared/hooks/task_update_capture.py +254 -0
  102. package/dist/templates/_shared/hooks/user_prompt_submit.py +157 -0
  103. package/dist/templates/_shared/lib/__init__.py +1 -0
  104. package/dist/templates/_shared/lib/base/__init__.py +49 -0
  105. package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
  106. package/dist/templates/_shared/lib/base/atomic_write.py +180 -0
  107. package/dist/templates/_shared/lib/base/constants.py +299 -0
  108. package/dist/templates/_shared/lib/base/inference.py +189 -0
  109. package/dist/templates/_shared/lib/base/utils.py +216 -0
  110. package/dist/templates/_shared/lib/context/__init__.py +119 -0
  111. package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
  112. package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
  113. package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
  114. package/dist/templates/_shared/lib/context/__pycache__/event_log.cpython-313.pyc +0 -0
  115. package/dist/templates/_shared/lib/context/cache.py +446 -0
  116. package/dist/templates/_shared/lib/context/context_manager.py +1171 -0
  117. package/dist/templates/_shared/lib/context/discovery.py +486 -0
  118. package/dist/templates/_shared/lib/context/event_log.py +308 -0
  119. package/dist/templates/_shared/lib/context/plan_archive.py +247 -0
  120. package/dist/templates/_shared/lib/context/task_sync.py +367 -0
  121. package/dist/templates/_shared/lib/handoff/__init__.py +22 -0
  122. package/dist/templates/_shared/lib/handoff/document_generator.py +307 -0
  123. package/dist/templates/_shared/lib/templates/README.md +215 -0
  124. package/dist/templates/_shared/lib/templates/__init__.py +40 -0
  125. package/dist/templates/_shared/lib/templates/formatters.py +147 -0
  126. package/dist/templates/_shared/lib/templates/plan_context.py +119 -0
  127. package/dist/templates/_shared/scripts/save_handoff.py +99 -0
  128. package/dist/templates/_shared/workflows/handoff.md +212 -0
  129. package/dist/templates/cc-native/.claude/agents/cc-native/ACCESSIBILITY-TESTER.md +80 -0
  130. package/dist/templates/cc-native/.claude/agents/cc-native/ARCHITECT-REVIEWER.md +75 -0
  131. package/dist/templates/cc-native/.claude/agents/cc-native/ASSUMPTION-CHAIN-TRACER.md +239 -0
  132. package/dist/templates/cc-native/.claude/agents/cc-native/CLARITY-AUDITOR.md +109 -0
  133. package/dist/templates/cc-native/.claude/agents/cc-native/CODE-REVIEWER.md +71 -0
  134. package/dist/templates/cc-native/.claude/agents/cc-native/COMPLETENESS-CHECKER.md +104 -0
  135. package/dist/templates/cc-native/.claude/agents/cc-native/CONTEXT-EXTRACTOR.md +93 -0
  136. package/dist/templates/cc-native/.claude/agents/cc-native/DEVILS-ADVOCATE.md +223 -0
  137. package/dist/templates/cc-native/.claude/agents/cc-native/DOCUMENTATION-REVIEWER.md +73 -0
  138. package/dist/templates/cc-native/.claude/agents/cc-native/FEASIBILITY-ANALYST.md +93 -0
  139. package/dist/templates/cc-native/.claude/agents/cc-native/FRESH-PERSPECTIVE.md +103 -0
  140. package/dist/templates/cc-native/.claude/agents/cc-native/HANDOFF-READINESS.md +145 -0
  141. package/dist/templates/cc-native/.claude/agents/cc-native/HIDDEN-COMPLEXITY-DETECTOR.md +248 -0
  142. package/dist/templates/cc-native/.claude/agents/cc-native/INCENTIVE-MAPPER.md +235 -0
  143. package/dist/templates/cc-native/.claude/agents/cc-native/PENETRATION-TESTER.md +80 -0
  144. package/dist/templates/cc-native/.claude/agents/cc-native/PERFORMANCE-ENGINEER.md +76 -0
  145. package/dist/templates/cc-native/.claude/agents/cc-native/PLAN-ORCHESTRATOR.md +141 -0
  146. package/dist/templates/cc-native/.claude/agents/cc-native/PRECEDENT-FINDER.md +240 -0
  147. package/dist/templates/cc-native/.claude/agents/cc-native/REVERSIBILITY-ANALYST.md +211 -0
  148. package/dist/templates/cc-native/.claude/agents/cc-native/RISK-ASSESSOR.md +101 -0
  149. package/dist/templates/cc-native/.claude/agents/cc-native/SECOND-ORDER-ANALYST.md +197 -0
  150. package/dist/templates/cc-native/.claude/agents/cc-native/SIMPLICITY-GUARDIAN.md +97 -0
  151. package/dist/templates/cc-native/.claude/agents/cc-native/SKEPTIC.md +349 -0
  152. package/dist/templates/cc-native/.claude/agents/cc-native/STAKEHOLDER-ADVOCATE.md +106 -0
  153. package/dist/templates/cc-native/.claude/agents/cc-native/TRADE-OFF-ILLUMINATOR.md +205 -0
  154. package/dist/templates/cc-native/.claude/commands/cc-native/fresh-perspective.md +8 -0
  155. package/dist/templates/cc-native/.claude/commands/cc-native/specdev.md +10 -0
  156. package/dist/templates/cc-native/.claude/settings.json +119 -0
  157. package/dist/templates/cc-native/.windsurf/workflows/cc-native/fix.md +8 -0
  158. package/dist/templates/cc-native/.windsurf/workflows/cc-native/fresh-perspective.md +8 -0
  159. package/dist/templates/cc-native/.windsurf/workflows/cc-native/implement.md +8 -0
  160. package/dist/templates/cc-native/.windsurf/workflows/cc-native/research.md +8 -0
  161. package/dist/templates/cc-native/CC-NATIVE-README.md +192 -0
  162. package/dist/templates/cc-native/MIGRATION.md +86 -0
  163. package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +331 -0
  164. package/dist/templates/cc-native/_cc-native/docs/PERMISSION_REQUEST_VERIFICATION.md +147 -0
  165. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
  166. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
  167. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-agent-review.cpython-313.pyc +0 -0
  168. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
  169. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/test_permission_request.cpython-313.pyc +0 -0
  170. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +150 -0
  171. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +746 -0
  172. package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +339 -0
  173. package/dist/templates/cc-native/_cc-native/lib/__init__.py +57 -0
  174. package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  175. package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
  176. package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
  177. package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
  178. package/dist/templates/cc-native/_cc-native/lib/async_archive.py +68 -0
  179. package/dist/templates/cc-native/_cc-native/lib/atomic_write.py +98 -0
  180. package/dist/templates/cc-native/_cc-native/lib/constants.py +45 -0
  181. package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +273 -0
  182. package/dist/templates/cc-native/_cc-native/lib/reviewers/__init__.py +28 -0
  183. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
  184. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
  185. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
  186. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
  187. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
  188. package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +164 -0
  189. package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +89 -0
  190. package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +119 -0
  191. package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +103 -0
  192. package/dist/templates/cc-native/_cc-native/lib/state.py +251 -0
  193. package/dist/templates/cc-native/_cc-native/lib/utils.py +830 -0
  194. package/dist/templates/cc-native/_cc-native/plan-review.config.json +76 -0
  195. package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
  196. package/dist/templates/cc-native/_cc-native/scripts/aggregate_agents.py +151 -0
  197. package/dist/templates/cc-native/_cc-native/workflows/fresh-perspective.md +134 -0
  198. package/dist/templates/cc-native/_cc-native/workflows/specdev.md +9 -0
  199. package/dist/types/exit-codes.d.ts +11 -0
  200. package/dist/types/exit-codes.js +10 -0
  201. package/dist/types/index.d.ts +5 -0
  202. package/dist/types/index.js +7 -0
  203. package/oclif.manifest.json +405 -0
  204. package/package.json +109 -0
@@ -0,0 +1,103 @@
1
+ """
2
+ CC-Native Gemini Reviewer Module.
3
+
4
+ Runs Gemini CLI to review plans.
5
+ """
6
+
7
+ import json
8
+ import shutil
9
+ import subprocess
10
+ import sys
11
+ from pathlib import Path
12
+ from typing import Any, Dict
13
+
14
+ # Import from parent lib
15
+ _lib_dir = Path(__file__).resolve().parent.parent
16
+ sys.path.insert(0, str(_lib_dir))
17
+
18
+ from utils import ReviewerResult, eprint, parse_json_maybe, coerce_to_review
19
+
20
+
21
+ def run_gemini_review(
22
+ plan: str,
23
+ schema: Dict[str, Any],
24
+ settings: Dict[str, Any],
25
+ ) -> ReviewerResult:
26
+ """Run Gemini CLI to review the plan.
27
+
28
+ Args:
29
+ plan: The plan content to review
30
+ schema: JSON schema for the review output
31
+ settings: Gemini reviewer settings (timeout, model)
32
+
33
+ Returns:
34
+ ReviewerResult with the review output
35
+ """
36
+ gemini_settings = settings.get("reviewers", {}).get("gemini", {})
37
+ timeout = gemini_settings.get("timeout", 120)
38
+ model = gemini_settings.get("model", "")
39
+
40
+ gemini_path = shutil.which("gemini")
41
+ if gemini_path is None:
42
+ eprint("[gemini] CLI not found on PATH")
43
+ return ReviewerResult(
44
+ name="gemini",
45
+ ok=False,
46
+ verdict="skip",
47
+ data={},
48
+ raw="",
49
+ err="gemini CLI not found on PATH",
50
+ )
51
+
52
+ eprint(f"[gemini] Found CLI at: {gemini_path}")
53
+
54
+ instruction = f"""
55
+
56
+ Review the PLAN above as a senior staff software engineer. Focus on:
57
+ - missing steps, unclear assumptions, edge cases
58
+ - security/privacy concerns
59
+ - testing/rollout/rollback completeness
60
+ - operational concerns (observability, failure modes)
61
+
62
+ Return ONLY a JSON object that matches this JSON Schema (no markdown, no code fences):
63
+ {json.dumps(schema, ensure_ascii=False)}
64
+ """
65
+
66
+ cmd = [
67
+ gemini_path,
68
+ "-y", # YOLO mode - auto-approve all actions
69
+ "-p",
70
+ instruction,
71
+ ]
72
+
73
+ if model:
74
+ cmd.extend(["--model", model])
75
+
76
+ eprint("[gemini] Running command: gemini -y -p <instruction>")
77
+
78
+ try:
79
+ p = subprocess.run(
80
+ cmd,
81
+ input=plan,
82
+ text=True,
83
+ capture_output=True,
84
+ timeout=timeout,
85
+ encoding='utf-8',
86
+ errors='replace',
87
+ )
88
+ except subprocess.TimeoutExpired:
89
+ eprint(f"[gemini] TIMEOUT after {timeout}s")
90
+ return ReviewerResult("gemini", False, "error", {}, "", f"gemini timed out after {timeout}s")
91
+ except Exception as ex:
92
+ eprint(f"[gemini] EXCEPTION: {ex}")
93
+ return ReviewerResult("gemini", False, "error", {}, "", f"gemini failed to run: {ex}")
94
+
95
+ eprint(f"[gemini] Exit code: {p.returncode}")
96
+
97
+ raw = (p.stdout or "").strip()
98
+ err = (p.stderr or "").strip()
99
+
100
+ obj = parse_json_maybe(raw)
101
+ ok, verdict, norm = coerce_to_review(obj, "Retry or check CLI auth/config.")
102
+
103
+ return ReviewerResult("gemini", ok, verdict, norm, raw, err)
@@ -0,0 +1,251 @@
1
+ """
2
+ CC-Native State Management Module.
3
+
4
+ Handles plan state file operations and iteration tracking for the review process.
5
+
6
+ State files are stored adjacent to plan files (e.g., ~/.claude/plans/foo.state.json)
7
+ to prevent state loss when session IDs change or temp files are cleaned up.
8
+ """
9
+
10
+ import json
11
+ import sys
12
+ from datetime import datetime
13
+ from pathlib import Path
14
+ from typing import Any, Dict, Optional
15
+
16
+ try:
17
+ from .constants import validate_plan_path, PLANS_DIR
18
+ from .atomic_write import atomic_write
19
+ except ImportError:
20
+ # When imported directly via sys.path (not as a package)
21
+ from constants import validate_plan_path, PLANS_DIR
22
+ from atomic_write import atomic_write
23
+
24
+
25
+ # ---------------------------
26
+ # Constants
27
+ # ---------------------------
28
+
29
+ STATE_SCHEMA_VERSION = "1.0.0"
30
+
31
+ DEFAULT_REVIEW_ITERATIONS: Dict[str, int] = {
32
+ "simple": 1,
33
+ "medium": 1,
34
+ "high": 2,
35
+ }
36
+
37
+
38
+ # ---------------------------
39
+ # Utilities
40
+ # ---------------------------
41
+
42
+ def eprint(*args: Any) -> None:
43
+ """Print to stderr."""
44
+ print(*args, file=sys.stderr)
45
+
46
+
47
+ # ---------------------------
48
+ # State File Management
49
+ # ---------------------------
50
+
51
+ def get_state_file_path(plan_path: str) -> Path:
52
+ """Derive state file path from plan file path with security validation.
53
+
54
+ The state file is stored adjacent to the plan file with a .state.json extension.
55
+ This prevents state loss when session IDs change or temp files are cleaned up.
56
+
57
+ Example: ~/.claude/plans/foo.md -> ~/.claude/plans/foo.state.json
58
+
59
+ Raises:
60
+ ValueError: If plan_path is invalid or insecure
61
+ """
62
+ # SECURITY: Validate path before any operations
63
+ validated_path = validate_plan_path(plan_path)
64
+
65
+ # State file is always adjacent to plan file
66
+ return validated_path.with_suffix('.state.json')
67
+
68
+
69
+ def load_state(plan_path: str) -> Optional[Dict[str, Any]]:
70
+ """Load state file with schema validation and migration."""
71
+ try:
72
+ state_file = get_state_file_path(plan_path) # Validates path
73
+
74
+ if not state_file.exists():
75
+ return None
76
+
77
+ state = json.loads(state_file.read_text(encoding="utf-8"))
78
+
79
+ # Handle schema version (backward compatible)
80
+ schema_version = state.get("schema_version")
81
+
82
+ if schema_version is None:
83
+ # Existing state files without version - auto-migrate
84
+ state["schema_version"] = STATE_SCHEMA_VERSION
85
+ eprint(f"[state] Migrated state file to schema v{STATE_SCHEMA_VERSION}")
86
+ elif schema_version != STATE_SCHEMA_VERSION:
87
+ eprint(f"[state] WARNING: Schema mismatch (expected {STATE_SCHEMA_VERSION}, got {schema_version})")
88
+ # For now, accept with warning - add migration logic here if schema changes
89
+
90
+ return state
91
+
92
+ except ValueError as e:
93
+ eprint(f"[state] SECURITY: Invalid plan path: {e}")
94
+ return None
95
+ except Exception as e:
96
+ eprint(f"[state] ERROR: Failed to load state: {e}")
97
+ return None
98
+
99
+
100
+ def save_state(plan_path: str, state: Dict[str, Any]) -> bool:
101
+ """Save state file with schema version and validation.
102
+
103
+ Returns True on success, False on failure.
104
+ """
105
+ try:
106
+ state_file = get_state_file_path(plan_path) # Validates path
107
+
108
+ state_with_version = {
109
+ "schema_version": STATE_SCHEMA_VERSION,
110
+ **state
111
+ }
112
+
113
+ # Use atomic write
114
+ success, error = atomic_write(
115
+ state_file,
116
+ json.dumps(state_with_version, indent=2)
117
+ )
118
+
119
+ if not success:
120
+ eprint(f"[state] Failed to save state: {error}")
121
+ return False
122
+
123
+ return True
124
+
125
+ except ValueError as e:
126
+ eprint(f"[state] SECURITY: Invalid plan path: {e}")
127
+ return False
128
+ except Exception as e:
129
+ eprint(f"[state] ERROR: {e}")
130
+ return False
131
+
132
+
133
+ def delete_state(plan_path: str) -> bool:
134
+ """Delete state file after successful archive.
135
+
136
+ Returns True if deleted or didn't exist, False on error.
137
+ """
138
+ try:
139
+ state_file = get_state_file_path(plan_path)
140
+ if state_file.exists():
141
+ state_file.unlink()
142
+ eprint(f"[state] Deleted state file: {state_file}")
143
+ return True
144
+ except ValueError as e:
145
+ eprint(f"[state] SECURITY: Invalid plan path in delete: {e}")
146
+ return False
147
+ except Exception as e:
148
+ eprint(f"[state] Warning: failed to delete state file: {e}")
149
+ return False
150
+
151
+
152
+ # ---------------------------
153
+ # Iteration State Management
154
+ # ---------------------------
155
+
156
+ def get_iteration_state(
157
+ state: Dict[str, Any],
158
+ complexity: str,
159
+ config: Optional[Dict[str, Any]] = None,
160
+ ) -> Dict[str, Any]:
161
+ """Get or initialize iteration state based on complexity.
162
+
163
+ Args:
164
+ state: The current plan state dict
165
+ complexity: Plan complexity level (simple/medium/high)
166
+ config: Optional config dict with reviewIterations settings
167
+
168
+ Returns:
169
+ Iteration dict with: current, max, complexity, history
170
+ """
171
+ if "iteration" in state:
172
+ # Return existing iteration state
173
+ return state["iteration"]
174
+
175
+ # Initialize new iteration state
176
+ review_iterations = DEFAULT_REVIEW_ITERATIONS.copy()
177
+ if config:
178
+ review_iterations.update(config.get("reviewIterations", {}))
179
+ max_iterations = review_iterations.get(complexity, 1)
180
+
181
+ return {
182
+ "current": 1,
183
+ "max": max_iterations,
184
+ "complexity": complexity,
185
+ "history": [],
186
+ }
187
+
188
+
189
+ def update_iteration_state(
190
+ state: Dict[str, Any],
191
+ iteration: Dict[str, Any],
192
+ plan_hash: str,
193
+ verdict: str,
194
+ ) -> Dict[str, Any]:
195
+ """Record review result in iteration history and update state.
196
+
197
+ Args:
198
+ state: The current plan state dict
199
+ iteration: The iteration state dict
200
+ plan_hash: Hash of the current plan content
201
+ verdict: Review verdict (pass/warn/fail)
202
+
203
+ Returns:
204
+ Updated state dict with iteration data
205
+ """
206
+ # Add this review to history
207
+ iteration["history"].append({
208
+ "hash": plan_hash,
209
+ "verdict": verdict,
210
+ "timestamp": datetime.now().isoformat(),
211
+ })
212
+
213
+ # Update state with iteration data
214
+ state["iteration"] = iteration
215
+ return state
216
+
217
+
218
+ def should_continue_iterating(
219
+ iteration: Dict[str, Any],
220
+ verdict: str,
221
+ config: Optional[Dict[str, Any]] = None,
222
+ ) -> bool:
223
+ """Determine if more review iterations are needed.
224
+
225
+ Args:
226
+ iteration: The iteration state dict
227
+ verdict: Current review verdict
228
+ config: Optional config dict with earlyExitOnAllPass setting
229
+
230
+ Returns:
231
+ True if more iterations needed, False otherwise
232
+ """
233
+ current = iteration.get("current", 1)
234
+ max_iter = iteration.get("max", 1)
235
+
236
+ # At or past max iterations - no more iterations
237
+ if current >= max_iter:
238
+ eprint(f"[state] At max iterations ({current}/{max_iter}), no more iterations")
239
+ return False
240
+
241
+ # Check early exit on all pass
242
+ early_exit = True
243
+ if config:
244
+ early_exit = config.get("earlyExitOnAllPass", True)
245
+ if early_exit and verdict == "pass":
246
+ eprint(f"[state] All reviewers passed and earlyExitOnAllPass=true, exiting early")
247
+ return False
248
+
249
+ # More iterations available and verdict is not pass (or early exit disabled)
250
+ eprint(f"[state] Continuing to next iteration ({current + 1}/{max_iter}), verdict={verdict}")
251
+ return True