aiwcli 0.9.8 → 0.10.1
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/bin/run.js +5 -2
- package/dist/lib/claude-settings-types.d.ts +2 -0
- package/dist/templates/CLAUDE.md +3 -3
- package/dist/templates/_shared/.claude/settings.json +4 -0
- package/dist/templates/_shared/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/pre_compact.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/session_end.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_create_capture.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/archive_plan.py +87 -178
- package/dist/templates/_shared/hooks/context_monitor.py +104 -247
- package/dist/templates/_shared/hooks/file-suggestion.py +26 -23
- package/dist/templates/_shared/hooks/pre_compact.py +47 -32
- package/dist/templates/_shared/hooks/session_end.py +114 -60
- package/dist/templates/_shared/hooks/session_start.py +127 -81
- package/dist/templates/_shared/hooks/task_create_capture.py +26 -50
- package/dist/templates/_shared/hooks/task_update_capture.py +42 -115
- package/dist/templates/_shared/hooks/user_prompt_submit.py +47 -81
- package/dist/templates/_shared/lib/base/__init__.py +16 -0
- package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/logger.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/hook_utils.py +207 -11
- package/dist/templates/_shared/lib/base/inference.py +121 -0
- package/dist/templates/_shared/lib/base/logger.py +291 -0
- package/dist/templates/_shared/lib/base/utils.py +42 -9
- package/dist/templates/_shared/lib/context/__init__.py +72 -80
- package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_formatter.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_selector.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_store.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/discovery.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/plan_manager.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/task_tracker.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/context_formatter.py +317 -0
- package/dist/templates/_shared/lib/context/context_selector.py +508 -0
- package/dist/templates/_shared/lib/context/context_store.py +653 -0
- package/dist/templates/_shared/lib/context/plan_manager.py +204 -0
- package/dist/templates/_shared/lib/context/task_tracker.py +188 -0
- package/dist/templates/_shared/lib/handoff/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/handoff/__pycache__/document_generator.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/handoff/document_generator.py +14 -40
- package/dist/templates/_shared/lib/templates/README.md +5 -13
- package/dist/templates/_shared/lib/templates/__init__.py +2 -6
- package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/plan_context.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/plan_context.py +22 -37
- package/dist/templates/_shared/scripts/__pycache__/save_handoff.cpython-313.pyc +0 -0
- package/dist/templates/_shared/scripts/__pycache__/status_line.cpython-313.pyc +0 -0
- package/dist/templates/_shared/scripts/save_handoff.py +31 -19
- package/dist/templates/_shared/scripts/status_line.py +701 -0
- package/dist/templates/_shared/workflows/handoff.md +9 -3
- package/dist/templates/cc-native/.claude/settings.json +37 -14
- package/dist/templates/cc-native/CC-NATIVE-README.md +25 -28
- package/dist/templates/cc-native/MIGRATION.md +1 -1
- package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +14 -39
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +54 -21
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/mark_questions_asked.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_accepted.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_questions_early.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +76 -89
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +163 -131
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.py +81 -0
- package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +26 -25
- package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +6 -4
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/constants.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/debug.py +37 -22
- package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +34 -29
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +26 -21
- package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +12 -7
- package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +12 -7
- package/dist/templates/cc-native/_cc-native/lib/state.py +31 -16
- package/dist/templates/cc-native/_cc-native/lib/utils.py +207 -40
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +1 -2
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/dist/templates/_shared/hooks/context_enforcer.py +0 -625
- package/dist/templates/_shared/hooks/task_create_atomicity.py +0 -177
- package/dist/templates/_shared/lib/context/auto_state.py +0 -167
- package/dist/templates/_shared/lib/context/cache.py +0 -444
- package/dist/templates/_shared/lib/context/context_extractor.py +0 -115
- package/dist/templates/_shared/lib/context/context_manager.py +0 -1057
- package/dist/templates/_shared/lib/context/discovery.py +0 -554
- package/dist/templates/_shared/lib/context/event_log.py +0 -316
- package/dist/templates/_shared/lib/context/plan_archive.py +0 -101
- package/dist/templates/_shared/lib/context/task_sync.py +0 -407
- package/dist/templates/_shared/lib/templates/persona_questions.py +0 -113
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-agent-review.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/test_permission_request.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/async_archive.py +0 -68
- package/dist/templates/cc-native/_cc-native/lib/atomic_write.py +0 -98
|
@@ -15,10 +15,15 @@ from typing import Any, Dict, Optional
|
|
|
15
15
|
_lib_dir = Path(__file__).resolve().parent.parent
|
|
16
16
|
sys.path.insert(0, str(_lib_dir))
|
|
17
17
|
|
|
18
|
-
from utils import ReviewerResult,
|
|
18
|
+
from utils import ReviewerResult, parse_json_maybe, coerce_to_review
|
|
19
19
|
from debug import debug_log, debug_raw
|
|
20
20
|
from .base import AgentConfig, AGENT_REVIEW_PROMPT_PREFIX
|
|
21
21
|
|
|
22
|
+
# Import logger
|
|
23
|
+
_shared_logger = Path(__file__).resolve().parent.parent.parent.parent / "_shared" / "lib"
|
|
24
|
+
sys.path.insert(0, str(_shared_logger))
|
|
25
|
+
from base.logger import log_debug, log_info, log_warn, log_error
|
|
26
|
+
|
|
22
27
|
# Import shared subprocess utilities
|
|
23
28
|
_shared_lib = Path(__file__).resolve().parent.parent.parent.parent / "_shared" / "lib" / "base"
|
|
24
29
|
sys.path.insert(0, str(_shared_lib))
|
|
@@ -43,18 +48,18 @@ def _parse_claude_output(raw: str) -> Optional[Dict[str, Any]]:
|
|
|
43
48
|
result = json.loads(raw)
|
|
44
49
|
if isinstance(result, dict):
|
|
45
50
|
if "structured_output" in result:
|
|
46
|
-
|
|
51
|
+
log_debug("agent", "Found structured_output in root dict", component="parse")
|
|
47
52
|
return result["structured_output"]
|
|
48
53
|
if result.get("type") == "assistant":
|
|
49
54
|
message = result.get("message", {})
|
|
50
55
|
content = message.get("content", [])
|
|
51
56
|
for item in content:
|
|
52
57
|
if isinstance(item, dict) and item.get("name") == "StructuredOutput":
|
|
53
|
-
|
|
58
|
+
log_debug("agent", "Found StructuredOutput in assistant message content", component="parse")
|
|
54
59
|
return item.get("input", {})
|
|
55
|
-
|
|
60
|
+
log_debug("agent", "Assistant message found but no StructuredOutput tool use in content", component="parse")
|
|
56
61
|
elif isinstance(result, list):
|
|
57
|
-
|
|
62
|
+
log_debug("agent", f"Received list of {len(result)} events, searching for assistant message", component="parse")
|
|
58
63
|
for i, event in enumerate(result):
|
|
59
64
|
if not isinstance(event, dict):
|
|
60
65
|
continue
|
|
@@ -63,16 +68,16 @@ def _parse_claude_output(raw: str) -> Optional[Dict[str, Any]]:
|
|
|
63
68
|
content = message.get("content", [])
|
|
64
69
|
for item in content:
|
|
65
70
|
if isinstance(item, dict) and item.get("name") == "StructuredOutput":
|
|
66
|
-
|
|
71
|
+
log_debug("agent", f"Found StructuredOutput in event[{i}] assistant message", component="parse")
|
|
67
72
|
return item.get("input", {})
|
|
68
|
-
|
|
73
|
+
log_debug("agent", "No StructuredOutput found in any assistant message in event list", component="parse")
|
|
69
74
|
except json.JSONDecodeError as e:
|
|
70
|
-
|
|
75
|
+
log_warn("agent", f"JSON decode error: {e}", component="parse")
|
|
71
76
|
except Exception as e:
|
|
72
|
-
|
|
77
|
+
log_error("agent", f"Unexpected error during structured parsing: {e}", component="parse")
|
|
73
78
|
|
|
74
79
|
# Fallback to heuristic extraction with required field validation
|
|
75
|
-
|
|
80
|
+
log_debug("agent", "No structured output found, falling back to heuristic JSON extraction", component="parse")
|
|
76
81
|
return parse_json_maybe(raw, require_fields=["verdict", "summary"])
|
|
77
82
|
|
|
78
83
|
|
|
@@ -99,7 +104,7 @@ def run_agent_review(
|
|
|
99
104
|
"""
|
|
100
105
|
claude_path = shutil.which("claude")
|
|
101
106
|
if claude_path is None:
|
|
102
|
-
|
|
107
|
+
log_warn(agent.name, "Claude CLI not found on PATH")
|
|
103
108
|
return ReviewerResult(
|
|
104
109
|
name=agent.name,
|
|
105
110
|
ok=False,
|
|
@@ -109,7 +114,7 @@ def run_agent_review(
|
|
|
109
114
|
err="claude CLI not found on PATH",
|
|
110
115
|
)
|
|
111
116
|
|
|
112
|
-
|
|
117
|
+
log_debug(agent.name, f"Found Claude CLI at: {claude_path}")
|
|
113
118
|
|
|
114
119
|
# User prompt - direct instruction to call StructuredOutput immediately
|
|
115
120
|
prompt = f"""IMMEDIATELY call StructuredOutput with your review of the plan below.
|
|
@@ -139,7 +144,7 @@ PLAN:
|
|
|
139
144
|
full_prompt = AGENT_REVIEW_PROMPT_PREFIX + "\n\n---\n\n" + agent.system_prompt
|
|
140
145
|
cmd_args.extend(["--system-prompt", full_prompt])
|
|
141
146
|
|
|
142
|
-
|
|
147
|
+
log_info(agent.name, f"Running with model: {agent.model}, timeout: {timeout}s")
|
|
143
148
|
|
|
144
149
|
# Get environment for internal subprocess (bypasses hooks)
|
|
145
150
|
env = get_internal_subprocess_env()
|
|
@@ -156,16 +161,16 @@ PLAN:
|
|
|
156
161
|
env=env,
|
|
157
162
|
)
|
|
158
163
|
except subprocess.TimeoutExpired:
|
|
159
|
-
|
|
164
|
+
log_warn(agent.name, f"TIMEOUT after {timeout}s")
|
|
160
165
|
return ReviewerResult(agent.name, False, "error", {}, "", f"{agent.name} timed out after {timeout}s")
|
|
161
166
|
except Exception as ex:
|
|
162
|
-
|
|
167
|
+
log_error(agent.name, f"Exception: {ex}")
|
|
163
168
|
return ReviewerResult(agent.name, False, "error", {}, "", f"{agent.name} failed to run: {ex}")
|
|
164
169
|
|
|
165
|
-
|
|
166
|
-
|
|
170
|
+
log_debug(agent.name, f"Exit code: {p.returncode}")
|
|
171
|
+
log_debug(agent.name, f"stdout length: {len(p.stdout or '')} chars")
|
|
167
172
|
if p.stderr:
|
|
168
|
-
|
|
173
|
+
log_debug(agent.name, f"stderr: {p.stderr[:500]}")
|
|
169
174
|
|
|
170
175
|
raw = (p.stdout or "").strip()
|
|
171
176
|
err = (p.stderr or "").strip()
|
|
@@ -184,7 +189,7 @@ PLAN:
|
|
|
184
189
|
})
|
|
185
190
|
|
|
186
191
|
if raw:
|
|
187
|
-
|
|
192
|
+
log_debug(agent.name, f"stdout preview: {raw[:500]}")
|
|
188
193
|
|
|
189
194
|
obj = _parse_claude_output(raw)
|
|
190
195
|
|
|
@@ -201,9 +206,9 @@ PLAN:
|
|
|
201
206
|
})
|
|
202
207
|
|
|
203
208
|
if obj:
|
|
204
|
-
|
|
209
|
+
log_info(agent.name, f"Parsed JSON successfully, verdict: {obj.get('verdict', 'N/A')}")
|
|
205
210
|
else:
|
|
206
|
-
|
|
211
|
+
log_warn(agent.name, "Failed to parse JSON from output")
|
|
207
212
|
|
|
208
213
|
ok, verdict, norm = coerce_to_review(obj, "Retry or check agent configuration.")
|
|
209
214
|
|
|
@@ -16,9 +16,14 @@ from typing import Any, Dict
|
|
|
16
16
|
_lib_dir = Path(__file__).resolve().parent.parent
|
|
17
17
|
sys.path.insert(0, str(_lib_dir))
|
|
18
18
|
|
|
19
|
-
from utils import ReviewerResult,
|
|
19
|
+
from utils import ReviewerResult, parse_json_maybe, coerce_to_review
|
|
20
20
|
from .base import REVIEW_PROMPT_PREFIX
|
|
21
21
|
|
|
22
|
+
# Import logger
|
|
23
|
+
_shared_logger = Path(__file__).resolve().parent.parent.parent.parent / "_shared" / "lib"
|
|
24
|
+
sys.path.insert(0, str(_shared_logger))
|
|
25
|
+
from base.logger import log_debug, log_info, log_warn, log_error
|
|
26
|
+
|
|
22
27
|
|
|
23
28
|
def run_codex_review(
|
|
24
29
|
plan: str,
|
|
@@ -41,7 +46,7 @@ def run_codex_review(
|
|
|
41
46
|
|
|
42
47
|
codex_path = shutil.which("codex")
|
|
43
48
|
if codex_path is None:
|
|
44
|
-
|
|
49
|
+
log_warn("codex", "CLI not found on PATH")
|
|
45
50
|
return ReviewerResult(
|
|
46
51
|
name="codex",
|
|
47
52
|
ok=False,
|
|
@@ -51,7 +56,7 @@ def run_codex_review(
|
|
|
51
56
|
err="codex CLI not found on PATH",
|
|
52
57
|
)
|
|
53
58
|
|
|
54
|
-
|
|
59
|
+
log_debug("codex", f"Found CLI at: {codex_path}")
|
|
55
60
|
|
|
56
61
|
prompt = f"""{REVIEW_PROMPT_PREFIX}
|
|
57
62
|
Return ONLY a JSON object that matches this JSON Schema:
|
|
@@ -87,7 +92,7 @@ PLAN:
|
|
|
87
92
|
cmd.insert(2, "--model")
|
|
88
93
|
cmd.insert(3, model)
|
|
89
94
|
|
|
90
|
-
|
|
95
|
+
log_debug("codex", f"Running command: {' '.join(cmd)}")
|
|
91
96
|
|
|
92
97
|
try:
|
|
93
98
|
p = subprocess.run(
|
|
@@ -100,13 +105,13 @@ PLAN:
|
|
|
100
105
|
errors='replace',
|
|
101
106
|
)
|
|
102
107
|
except subprocess.TimeoutExpired:
|
|
103
|
-
|
|
108
|
+
log_warn("codex", f"TIMEOUT after {timeout}s")
|
|
104
109
|
return ReviewerResult("codex", False, "error", {}, "", f"codex timed out after {timeout}s")
|
|
105
110
|
except Exception as ex:
|
|
106
|
-
|
|
111
|
+
log_error("codex", f"Exception: {ex}")
|
|
107
112
|
return ReviewerResult("codex", False, "error", {}, "", f"codex failed to run: {ex}")
|
|
108
113
|
|
|
109
|
-
|
|
114
|
+
log_debug("codex", f"Exit code: {p.returncode}")
|
|
110
115
|
|
|
111
116
|
raw = ""
|
|
112
117
|
if out_path.exists():
|
|
@@ -15,7 +15,12 @@ from typing import Any, Dict
|
|
|
15
15
|
_lib_dir = Path(__file__).resolve().parent.parent
|
|
16
16
|
sys.path.insert(0, str(_lib_dir))
|
|
17
17
|
|
|
18
|
-
from utils import ReviewerResult,
|
|
18
|
+
from utils import ReviewerResult, parse_json_maybe, coerce_to_review
|
|
19
|
+
|
|
20
|
+
# Import logger
|
|
21
|
+
_shared_logger = Path(__file__).resolve().parent.parent.parent.parent / "_shared" / "lib"
|
|
22
|
+
sys.path.insert(0, str(_shared_logger))
|
|
23
|
+
from base.logger import log_debug, log_info, log_warn, log_error
|
|
19
24
|
|
|
20
25
|
|
|
21
26
|
def run_gemini_review(
|
|
@@ -39,7 +44,7 @@ def run_gemini_review(
|
|
|
39
44
|
|
|
40
45
|
gemini_path = shutil.which("gemini")
|
|
41
46
|
if gemini_path is None:
|
|
42
|
-
|
|
47
|
+
log_warn("gemini", "CLI not found on PATH")
|
|
43
48
|
return ReviewerResult(
|
|
44
49
|
name="gemini",
|
|
45
50
|
ok=False,
|
|
@@ -49,7 +54,7 @@ def run_gemini_review(
|
|
|
49
54
|
err="gemini CLI not found on PATH",
|
|
50
55
|
)
|
|
51
56
|
|
|
52
|
-
|
|
57
|
+
log_debug("gemini", f"Found CLI at: {gemini_path}")
|
|
53
58
|
|
|
54
59
|
instruction = f"""
|
|
55
60
|
|
|
@@ -73,7 +78,7 @@ Return ONLY a JSON object that matches this JSON Schema (no markdown, no code fe
|
|
|
73
78
|
if model:
|
|
74
79
|
cmd.extend(["--model", model])
|
|
75
80
|
|
|
76
|
-
|
|
81
|
+
log_debug("gemini", "Running command: gemini -y -p <instruction>")
|
|
77
82
|
|
|
78
83
|
try:
|
|
79
84
|
p = subprocess.run(
|
|
@@ -86,13 +91,13 @@ Return ONLY a JSON object that matches this JSON Schema (no markdown, no code fe
|
|
|
86
91
|
errors='replace',
|
|
87
92
|
)
|
|
88
93
|
except subprocess.TimeoutExpired:
|
|
89
|
-
|
|
94
|
+
log_warn("gemini", f"TIMEOUT after {timeout}s")
|
|
90
95
|
return ReviewerResult("gemini", False, "error", {}, "", f"gemini timed out after {timeout}s")
|
|
91
96
|
except Exception as ex:
|
|
92
|
-
|
|
97
|
+
log_error("gemini", f"Exception: {ex}")
|
|
93
98
|
return ReviewerResult("gemini", False, "error", {}, "", f"gemini failed to run: {ex}")
|
|
94
99
|
|
|
95
|
-
|
|
100
|
+
log_debug("gemini", f"Exit code: {p.returncode}")
|
|
96
101
|
|
|
97
102
|
raw = (p.stdout or "").strip()
|
|
98
103
|
err = (p.stderr or "").strip()
|
|
@@ -15,15 +15,29 @@ from typing import Any, Dict, Optional
|
|
|
15
15
|
|
|
16
16
|
try:
|
|
17
17
|
from .constants import validate_plan_path, PLANS_DIR
|
|
18
|
-
from .atomic_write import atomic_write
|
|
19
18
|
except ImportError:
|
|
20
19
|
# When imported directly via sys.path (not as a package)
|
|
21
20
|
from constants import validate_plan_path, PLANS_DIR
|
|
22
|
-
from atomic_write import atomic_write
|
|
23
21
|
|
|
24
|
-
# Import
|
|
22
|
+
# Import atomic_write from shared lib (canonical copy)
|
|
23
|
+
try:
|
|
24
|
+
from ...lib.base.atomic_write import atomic_write
|
|
25
|
+
except ImportError:
|
|
26
|
+
# Fallback for direct execution
|
|
27
|
+
from pathlib import Path as _P
|
|
28
|
+
_shared_lib = _P(__file__).resolve().parent.parent.parent / "_shared" / "lib"
|
|
29
|
+
import importlib.util
|
|
30
|
+
_spec = importlib.util.spec_from_file_location(
|
|
31
|
+
"atomic_write", str(_shared_lib / "base" / "atomic_write.py")
|
|
32
|
+
)
|
|
33
|
+
_mod = importlib.util.module_from_spec(_spec)
|
|
34
|
+
_spec.loader.exec_module(_mod)
|
|
35
|
+
atomic_write = _mod.atomic_write
|
|
36
|
+
|
|
37
|
+
# Import canonical eprint and logger from shared lib
|
|
25
38
|
try:
|
|
26
39
|
from ...lib.base.utils import eprint
|
|
40
|
+
from ...lib.base.logger import log_debug, log_info, log_warn, log_error
|
|
27
41
|
except ImportError:
|
|
28
42
|
# Fallback for direct execution
|
|
29
43
|
import sys as _sys
|
|
@@ -31,6 +45,7 @@ except ImportError:
|
|
|
31
45
|
_shared_lib = _Path(__file__).resolve().parent.parent.parent / "_shared" / "lib"
|
|
32
46
|
_sys.path.insert(0, str(_shared_lib))
|
|
33
47
|
from base.utils import eprint
|
|
48
|
+
from base.logger import log_debug, log_info, log_warn, log_error
|
|
34
49
|
|
|
35
50
|
|
|
36
51
|
# ---------------------------
|
|
@@ -84,18 +99,18 @@ def load_state(plan_path: str) -> Optional[Dict[str, Any]]:
|
|
|
84
99
|
if schema_version is None:
|
|
85
100
|
# Existing state files without version - auto-migrate
|
|
86
101
|
state["schema_version"] = STATE_SCHEMA_VERSION
|
|
87
|
-
|
|
102
|
+
log_info("state", f"Migrated state file to schema v{STATE_SCHEMA_VERSION}")
|
|
88
103
|
elif schema_version != STATE_SCHEMA_VERSION:
|
|
89
|
-
|
|
104
|
+
log_warn("state", f"Schema mismatch (expected {STATE_SCHEMA_VERSION}, got {schema_version})")
|
|
90
105
|
# For now, accept with warning - add migration logic here if schema changes
|
|
91
106
|
|
|
92
107
|
return state
|
|
93
108
|
|
|
94
109
|
except ValueError as e:
|
|
95
|
-
|
|
110
|
+
log_error("state", f"SECURITY: Invalid plan path: {e}")
|
|
96
111
|
return None
|
|
97
112
|
except Exception as e:
|
|
98
|
-
|
|
113
|
+
log_error("state", f"Failed to load state: {e}")
|
|
99
114
|
return None
|
|
100
115
|
|
|
101
116
|
|
|
@@ -119,16 +134,16 @@ def save_state(plan_path: str, state: Dict[str, Any]) -> bool:
|
|
|
119
134
|
)
|
|
120
135
|
|
|
121
136
|
if not success:
|
|
122
|
-
|
|
137
|
+
log_error("state", f"Failed to save state: {error}")
|
|
123
138
|
return False
|
|
124
139
|
|
|
125
140
|
return True
|
|
126
141
|
|
|
127
142
|
except ValueError as e:
|
|
128
|
-
|
|
143
|
+
log_error("state", f"SECURITY: Invalid plan path: {e}")
|
|
129
144
|
return False
|
|
130
145
|
except Exception as e:
|
|
131
|
-
|
|
146
|
+
log_error("state", str(e))
|
|
132
147
|
return False
|
|
133
148
|
|
|
134
149
|
|
|
@@ -141,13 +156,13 @@ def delete_state(plan_path: str) -> bool:
|
|
|
141
156
|
state_file = get_state_file_path(plan_path)
|
|
142
157
|
if state_file.exists():
|
|
143
158
|
state_file.unlink()
|
|
144
|
-
|
|
159
|
+
log_info("state", f"Deleted state file: {state_file}")
|
|
145
160
|
return True
|
|
146
161
|
except ValueError as e:
|
|
147
|
-
|
|
162
|
+
log_error("state", f"SECURITY: Invalid plan path in delete: {e}")
|
|
148
163
|
return False
|
|
149
164
|
except Exception as e:
|
|
150
|
-
|
|
165
|
+
log_warn("state", f"Failed to delete state file: {e}")
|
|
151
166
|
return False
|
|
152
167
|
|
|
153
168
|
|
|
@@ -237,7 +252,7 @@ def should_continue_iterating(
|
|
|
237
252
|
|
|
238
253
|
# At or past max iterations - no more iterations
|
|
239
254
|
if current >= max_iter:
|
|
240
|
-
|
|
255
|
+
log_info("state", f"At max iterations ({current}/{max_iter}), no more iterations")
|
|
241
256
|
return False
|
|
242
257
|
|
|
243
258
|
# Check early exit on all pass
|
|
@@ -245,9 +260,9 @@ def should_continue_iterating(
|
|
|
245
260
|
if config:
|
|
246
261
|
early_exit = config.get("earlyExitOnAllPass", True)
|
|
247
262
|
if early_exit and verdict == "pass":
|
|
248
|
-
|
|
263
|
+
log_info("state", "All reviewers passed and earlyExitOnAllPass=true, exiting early")
|
|
249
264
|
return False
|
|
250
265
|
|
|
251
266
|
# More iterations available and verdict is not pass (or early exit disabled)
|
|
252
|
-
|
|
267
|
+
log_info("state", f"Continuing to next iteration ({current + 1}/{max_iter}), verdict={verdict}")
|
|
253
268
|
return True
|