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.
Files changed (115) hide show
  1. package/bin/run.js +5 -2
  2. package/dist/lib/claude-settings-types.d.ts +2 -0
  3. package/dist/templates/CLAUDE.md +3 -3
  4. package/dist/templates/_shared/.claude/settings.json +4 -0
  5. package/dist/templates/_shared/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  6. package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
  7. package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
  8. package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
  9. package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
  10. package/dist/templates/_shared/hooks/__pycache__/pre_compact.cpython-313.pyc +0 -0
  11. package/dist/templates/_shared/hooks/__pycache__/session_end.cpython-313.pyc +0 -0
  12. package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
  13. package/dist/templates/_shared/hooks/__pycache__/task_create_capture.cpython-313.pyc +0 -0
  14. package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
  15. package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
  16. package/dist/templates/_shared/hooks/archive_plan.py +87 -178
  17. package/dist/templates/_shared/hooks/context_monitor.py +104 -247
  18. package/dist/templates/_shared/hooks/file-suggestion.py +26 -23
  19. package/dist/templates/_shared/hooks/pre_compact.py +47 -32
  20. package/dist/templates/_shared/hooks/session_end.py +114 -60
  21. package/dist/templates/_shared/hooks/session_start.py +127 -81
  22. package/dist/templates/_shared/hooks/task_create_capture.py +26 -50
  23. package/dist/templates/_shared/hooks/task_update_capture.py +42 -115
  24. package/dist/templates/_shared/hooks/user_prompt_submit.py +47 -81
  25. package/dist/templates/_shared/lib/base/__init__.py +16 -0
  26. package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
  27. package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
  28. package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
  29. package/dist/templates/_shared/lib/base/__pycache__/logger.cpython-313.pyc +0 -0
  30. package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
  31. package/dist/templates/_shared/lib/base/hook_utils.py +207 -11
  32. package/dist/templates/_shared/lib/base/inference.py +121 -0
  33. package/dist/templates/_shared/lib/base/logger.py +291 -0
  34. package/dist/templates/_shared/lib/base/utils.py +42 -9
  35. package/dist/templates/_shared/lib/context/__init__.py +72 -80
  36. package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
  37. package/dist/templates/_shared/lib/context/__pycache__/context_formatter.cpython-313.pyc +0 -0
  38. package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
  39. package/dist/templates/_shared/lib/context/__pycache__/context_selector.cpython-313.pyc +0 -0
  40. package/dist/templates/_shared/lib/context/__pycache__/context_store.cpython-313.pyc +0 -0
  41. package/dist/templates/_shared/lib/context/__pycache__/discovery.cpython-313.pyc +0 -0
  42. package/dist/templates/_shared/lib/context/__pycache__/plan_manager.cpython-313.pyc +0 -0
  43. package/dist/templates/_shared/lib/context/__pycache__/task_tracker.cpython-313.pyc +0 -0
  44. package/dist/templates/_shared/lib/context/context_formatter.py +317 -0
  45. package/dist/templates/_shared/lib/context/context_selector.py +508 -0
  46. package/dist/templates/_shared/lib/context/context_store.py +653 -0
  47. package/dist/templates/_shared/lib/context/plan_manager.py +204 -0
  48. package/dist/templates/_shared/lib/context/task_tracker.py +188 -0
  49. package/dist/templates/_shared/lib/handoff/__pycache__/__init__.cpython-313.pyc +0 -0
  50. package/dist/templates/_shared/lib/handoff/__pycache__/document_generator.cpython-313.pyc +0 -0
  51. package/dist/templates/_shared/lib/handoff/document_generator.py +14 -40
  52. package/dist/templates/_shared/lib/templates/README.md +5 -13
  53. package/dist/templates/_shared/lib/templates/__init__.py +2 -6
  54. package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
  55. package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
  56. package/dist/templates/_shared/lib/templates/__pycache__/plan_context.cpython-313.pyc +0 -0
  57. package/dist/templates/_shared/lib/templates/plan_context.py +22 -37
  58. package/dist/templates/_shared/scripts/__pycache__/save_handoff.cpython-313.pyc +0 -0
  59. package/dist/templates/_shared/scripts/__pycache__/status_line.cpython-313.pyc +0 -0
  60. package/dist/templates/_shared/scripts/save_handoff.py +31 -19
  61. package/dist/templates/_shared/scripts/status_line.py +701 -0
  62. package/dist/templates/_shared/workflows/handoff.md +9 -3
  63. package/dist/templates/cc-native/.claude/settings.json +37 -14
  64. package/dist/templates/cc-native/CC-NATIVE-README.md +25 -28
  65. package/dist/templates/cc-native/MIGRATION.md +1 -1
  66. package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +14 -39
  67. package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +54 -21
  68. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
  69. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
  70. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/mark_questions_asked.cpython-313.pyc +0 -0
  71. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_accepted.cpython-313.pyc +0 -0
  72. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_questions_early.cpython-313.pyc +0 -0
  73. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
  74. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +76 -89
  75. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +163 -131
  76. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.py +81 -0
  77. package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +26 -25
  78. package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +6 -4
  79. package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  80. package/dist/templates/cc-native/_cc-native/lib/__pycache__/constants.cpython-313.pyc +0 -0
  81. package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
  82. package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
  83. package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
  84. package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
  85. package/dist/templates/cc-native/_cc-native/lib/debug.py +37 -22
  86. package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +34 -29
  87. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
  88. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
  89. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
  90. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
  91. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
  92. package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +26 -21
  93. package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +12 -7
  94. package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +12 -7
  95. package/dist/templates/cc-native/_cc-native/lib/state.py +31 -16
  96. package/dist/templates/cc-native/_cc-native/lib/utils.py +207 -40
  97. package/dist/templates/cc-native/_cc-native/plan-review.config.json +1 -2
  98. package/oclif.manifest.json +1 -1
  99. package/package.json +1 -1
  100. package/dist/templates/_shared/hooks/context_enforcer.py +0 -625
  101. package/dist/templates/_shared/hooks/task_create_atomicity.py +0 -177
  102. package/dist/templates/_shared/lib/context/auto_state.py +0 -167
  103. package/dist/templates/_shared/lib/context/cache.py +0 -444
  104. package/dist/templates/_shared/lib/context/context_extractor.py +0 -115
  105. package/dist/templates/_shared/lib/context/context_manager.py +0 -1057
  106. package/dist/templates/_shared/lib/context/discovery.py +0 -554
  107. package/dist/templates/_shared/lib/context/event_log.py +0 -316
  108. package/dist/templates/_shared/lib/context/plan_archive.py +0 -101
  109. package/dist/templates/_shared/lib/context/task_sync.py +0 -407
  110. package/dist/templates/_shared/lib/templates/persona_questions.py +0 -113
  111. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
  112. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-agent-review.cpython-313.pyc +0 -0
  113. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/test_permission_request.cpython-313.pyc +0 -0
  114. package/dist/templates/cc-native/_cc-native/lib/async_archive.py +0 -68
  115. 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, eprint, parse_json_maybe, coerce_to_review
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
- eprint("[parse] Found structured_output in root dict")
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
- eprint("[parse] Found StructuredOutput in assistant message content")
58
+ log_debug("agent", "Found StructuredOutput in assistant message content", component="parse")
54
59
  return item.get("input", {})
55
- eprint("[parse] Assistant message found but no StructuredOutput tool use in content")
60
+ log_debug("agent", "Assistant message found but no StructuredOutput tool use in content", component="parse")
56
61
  elif isinstance(result, list):
57
- eprint(f"[parse] Received list of {len(result)} events, searching for assistant message")
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
- eprint(f"[parse] Found StructuredOutput in event[{i}] assistant message")
71
+ log_debug("agent", f"Found StructuredOutput in event[{i}] assistant message", component="parse")
67
72
  return item.get("input", {})
68
- eprint("[parse] No StructuredOutput found in any assistant message in event list")
73
+ log_debug("agent", "No StructuredOutput found in any assistant message in event list", component="parse")
69
74
  except json.JSONDecodeError as e:
70
- eprint(f"[parse] JSON decode error: {e}")
75
+ log_warn("agent", f"JSON decode error: {e}", component="parse")
71
76
  except Exception as e:
72
- eprint(f"[parse] Unexpected error during structured parsing: {e}")
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
- eprint("[parse] No structured output found, falling back to heuristic JSON extraction")
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
- eprint(f"[{agent.name}] Claude CLI not found on PATH")
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
- eprint(f"[{agent.name}] Found Claude CLI at: {claude_path}")
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
- eprint(f"[{agent.name}] Running with model: {agent.model}, timeout: {timeout}s")
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
- eprint(f"[{agent.name}] TIMEOUT after {timeout}s")
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
- eprint(f"[{agent.name}] EXCEPTION: {ex}")
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
- eprint(f"[{agent.name}] Exit code: {p.returncode}")
166
- eprint(f"[{agent.name}] stdout length: {len(p.stdout or '')} chars")
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
- eprint(f"[{agent.name}] stderr: {p.stderr[:500]}")
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
- eprint(f"[{agent.name}] stdout preview: {raw[:500]}")
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
- eprint(f"[{agent.name}] Parsed JSON successfully, verdict: {obj.get('verdict', 'N/A')}")
209
+ log_info(agent.name, f"Parsed JSON successfully, verdict: {obj.get('verdict', 'N/A')}")
205
210
  else:
206
- eprint(f"[{agent.name}] Failed to parse JSON from output")
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, eprint, parse_json_maybe, coerce_to_review
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
- eprint("[codex] CLI not found on PATH")
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
- eprint(f"[codex] Found CLI at: {codex_path}")
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
- eprint(f"[codex] Running command: {' '.join(cmd)}")
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
- eprint(f"[codex] TIMEOUT after {timeout}s")
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
- eprint(f"[codex] EXCEPTION: {ex}")
111
+ log_error("codex", f"Exception: {ex}")
107
112
  return ReviewerResult("codex", False, "error", {}, "", f"codex failed to run: {ex}")
108
113
 
109
- eprint(f"[codex] Exit code: {p.returncode}")
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, eprint, parse_json_maybe, coerce_to_review
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
- eprint("[gemini] CLI not found on PATH")
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
- eprint(f"[gemini] Found CLI at: {gemini_path}")
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
- eprint("[gemini] Running command: gemini -y -p <instruction>")
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
- eprint(f"[gemini] TIMEOUT after {timeout}s")
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
- eprint(f"[gemini] EXCEPTION: {ex}")
97
+ log_error("gemini", f"Exception: {ex}")
93
98
  return ReviewerResult("gemini", False, "error", {}, "", f"gemini failed to run: {ex}")
94
99
 
95
- eprint(f"[gemini] Exit code: {p.returncode}")
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 canonical eprint from shared lib
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
- eprint(f"[state] Migrated state file to schema v{STATE_SCHEMA_VERSION}")
102
+ log_info("state", f"Migrated state file to schema v{STATE_SCHEMA_VERSION}")
88
103
  elif schema_version != STATE_SCHEMA_VERSION:
89
- eprint(f"[state] WARNING: Schema mismatch (expected {STATE_SCHEMA_VERSION}, got {schema_version})")
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
- eprint(f"[state] SECURITY: Invalid plan path: {e}")
110
+ log_error("state", f"SECURITY: Invalid plan path: {e}")
96
111
  return None
97
112
  except Exception as e:
98
- eprint(f"[state] ERROR: Failed to load state: {e}")
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
- eprint(f"[state] Failed to save state: {error}")
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
- eprint(f"[state] SECURITY: Invalid plan path: {e}")
143
+ log_error("state", f"SECURITY: Invalid plan path: {e}")
129
144
  return False
130
145
  except Exception as e:
131
- eprint(f"[state] ERROR: {e}")
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
- eprint(f"[state] Deleted state file: {state_file}")
159
+ log_info("state", f"Deleted state file: {state_file}")
145
160
  return True
146
161
  except ValueError as e:
147
- eprint(f"[state] SECURITY: Invalid plan path in delete: {e}")
162
+ log_error("state", f"SECURITY: Invalid plan path in delete: {e}")
148
163
  return False
149
164
  except Exception as e:
150
- eprint(f"[state] Warning: failed to delete state file: {e}")
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
- eprint(f"[state] At max iterations ({current}/{max_iter}), no more iterations")
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
- eprint(f"[state] All reviewers passed and earlyExitOnAllPass=true, exiting early")
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
- eprint(f"[state] Continuing to next iteration ({current + 1}/{max_iter}), verdict={verdict}")
267
+ log_info("state", f"Continuing to next iteration ({current + 1}/{max_iter}), verdict={verdict}")
253
268
  return True