aiwcli 0.9.7 → 0.10.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.
- package/bin/run.js +5 -2
- package/dist/lib/claude-settings-types.d.ts +2 -0
- package/dist/templates/CLAUDE.md +49 -18
- 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_atomicity.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 +128 -194
- package/dist/templates/_shared/hooks/file-suggestion.py +26 -23
- package/dist/templates/_shared/hooks/pre_compact.py +104 -0
- package/dist/templates/_shared/hooks/session_end.py +154 -0
- package/dist/templates/_shared/hooks/session_start.py +145 -59
- package/dist/templates/_shared/hooks/task_create_capture.py +26 -49
- package/dist/templates/_shared/hooks/task_update_capture.py +42 -100
- package/dist/templates/_shared/hooks/user_prompt_submit.py +63 -77
- 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__/constants.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/constants.py +18 -4
- package/dist/templates/_shared/lib/base/hook_utils.py +199 -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 +49 -11
- 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 +316 -0
- package/dist/templates/_shared/lib/context/context_selector.py +491 -0
- package/dist/templates/_shared/lib/context/context_store.py +636 -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 +25 -79
- 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 +39 -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 +64 -9
- 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/agents/CLAUDE.md +1 -1
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +57 -22
- 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 +57 -57
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +208 -158
- package/dist/templates/cc-native/_cc-native/hooks/plan_accepted.py +127 -0
- 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 +35 -10
- 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 +103 -42
- 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 +210 -43
- 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 -205
- 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 -1054
- package/dist/templates/_shared/lib/context/discovery.py +0 -444
- package/dist/templates/_shared/lib/context/event_log.py +0 -308
- package/dist/templates/_shared/lib/context/plan_archive.py +0 -101
- package/dist/templates/_shared/lib/context/task_sync.py +0 -290
- 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,9 +15,14 @@ from typing import Any, Dict, List, Optional
|
|
|
15
15
|
_lib_dir = Path(__file__).resolve().parent
|
|
16
16
|
sys.path.insert(0, str(_lib_dir))
|
|
17
17
|
|
|
18
|
-
from utils import OrchestratorResult,
|
|
18
|
+
from utils import OrchestratorResult, parse_json_maybe
|
|
19
19
|
from reviewers.base import AgentConfig, OrchestratorConfig
|
|
20
20
|
|
|
21
|
+
# Import logger
|
|
22
|
+
_shared_logger = Path(__file__).resolve().parent.parent.parent / "_shared" / "lib"
|
|
23
|
+
sys.path.insert(0, str(_shared_logger))
|
|
24
|
+
from base.logger import log_debug, log_info, log_warn, log_error
|
|
25
|
+
|
|
21
26
|
# Import shared subprocess utilities
|
|
22
27
|
_shared_lib = Path(__file__).resolve().parent.parent.parent / "_shared" / "lib" / "base"
|
|
23
28
|
sys.path.insert(0, str(_shared_lib))
|
|
@@ -59,6 +64,44 @@ ORCHESTRATOR_SCHEMA: Dict[str, Any] = {
|
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
|
|
67
|
+
def build_orchestrator_schema(
|
|
68
|
+
valid_agent_names: List[str],
|
|
69
|
+
categories: List[str],
|
|
70
|
+
) -> Dict[str, Any]:
|
|
71
|
+
"""Build orchestrator JSON schema with enum-constrained agent names.
|
|
72
|
+
|
|
73
|
+
When valid_agent_names is non-empty, selectedAgents items are constrained
|
|
74
|
+
to only those names via JSON schema enum. This prevents the LLM from
|
|
75
|
+
hallucinating or misspelling agent names.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
valid_agent_names: List of valid agent names for enum constraint.
|
|
79
|
+
categories: List of valid complexity categories.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
JSON schema dict for orchestrator structured output.
|
|
83
|
+
"""
|
|
84
|
+
items_schema: Dict[str, Any] = {"type": "string"}
|
|
85
|
+
if valid_agent_names:
|
|
86
|
+
items_schema["enum"] = valid_agent_names
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
"type": "object",
|
|
90
|
+
"properties": {
|
|
91
|
+
"complexity": {"type": "string", "enum": ["simple", "medium", "high"]},
|
|
92
|
+
"category": {"type": "string", "enum": categories},
|
|
93
|
+
"selectedAgents": {
|
|
94
|
+
"type": "array",
|
|
95
|
+
"items": items_schema,
|
|
96
|
+
},
|
|
97
|
+
"reasoning": {"type": "string"},
|
|
98
|
+
"skipReason": {"type": "string"},
|
|
99
|
+
},
|
|
100
|
+
"required": ["complexity", "category", "selectedAgents", "reasoning"],
|
|
101
|
+
"additionalProperties": False,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
62
105
|
# ---------------------------
|
|
63
106
|
# Output Parsing
|
|
64
107
|
# ---------------------------
|
|
@@ -81,18 +124,18 @@ def _parse_claude_output(raw: str) -> Optional[Dict[str, Any]]:
|
|
|
81
124
|
result = json.loads(raw)
|
|
82
125
|
if isinstance(result, dict):
|
|
83
126
|
if "structured_output" in result:
|
|
84
|
-
|
|
127
|
+
log_debug("orchestrator", "Found structured_output in root dict", component="parse")
|
|
85
128
|
return result["structured_output"]
|
|
86
129
|
if result.get("type") == "assistant":
|
|
87
130
|
message = result.get("message", {})
|
|
88
131
|
content = message.get("content", [])
|
|
89
132
|
for item in content:
|
|
90
133
|
if isinstance(item, dict) and item.get("name") == "StructuredOutput":
|
|
91
|
-
|
|
134
|
+
log_debug("orchestrator", "Found StructuredOutput in assistant message content", component="parse")
|
|
92
135
|
return item.get("input", {})
|
|
93
|
-
|
|
136
|
+
log_debug("orchestrator", "Assistant message found but no StructuredOutput tool use in content", component="parse")
|
|
94
137
|
elif isinstance(result, list):
|
|
95
|
-
|
|
138
|
+
log_debug("orchestrator", f"Received list of {len(result)} events, searching for assistant message", component="parse")
|
|
96
139
|
for i, event in enumerate(result):
|
|
97
140
|
if not isinstance(event, dict):
|
|
98
141
|
continue
|
|
@@ -101,16 +144,16 @@ def _parse_claude_output(raw: str) -> Optional[Dict[str, Any]]:
|
|
|
101
144
|
content = message.get("content", [])
|
|
102
145
|
for item in content:
|
|
103
146
|
if isinstance(item, dict) and item.get("name") == "StructuredOutput":
|
|
104
|
-
|
|
147
|
+
log_debug("orchestrator", f"Found StructuredOutput in event[{i}] assistant message", component="parse")
|
|
105
148
|
return item.get("input", {})
|
|
106
|
-
|
|
149
|
+
log_debug("orchestrator", "No StructuredOutput found in any assistant message in event list", component="parse")
|
|
107
150
|
except json.JSONDecodeError as e:
|
|
108
|
-
|
|
151
|
+
log_warn("orchestrator", f"JSON decode error: {e}", component="parse")
|
|
109
152
|
except Exception as e:
|
|
110
|
-
|
|
153
|
+
log_error("orchestrator", f"Unexpected error during structured parsing: {e}", component="parse")
|
|
111
154
|
|
|
112
155
|
# Fallback to heuristic extraction
|
|
113
|
-
|
|
156
|
+
log_debug("orchestrator", "No structured output found, falling back to heuristic JSON extraction", component="parse")
|
|
114
157
|
return parse_json_maybe(raw)
|
|
115
158
|
|
|
116
159
|
|
|
@@ -123,6 +166,7 @@ def run_orchestrator(
|
|
|
123
166
|
agent_library: List[AgentConfig],
|
|
124
167
|
config: OrchestratorConfig,
|
|
125
168
|
settings: Dict[str, Any],
|
|
169
|
+
mandatory_names: Optional[set] = None,
|
|
126
170
|
) -> OrchestratorResult:
|
|
127
171
|
"""Run the orchestrator agent to analyze plan complexity and select reviewers.
|
|
128
172
|
|
|
@@ -131,40 +175,54 @@ def run_orchestrator(
|
|
|
131
175
|
agent_library: List of available agents
|
|
132
176
|
config: Orchestrator configuration (model, timeout)
|
|
133
177
|
settings: Agent review settings (agentSelection, complexityCategories)
|
|
178
|
+
mandatory_names: Set of agent names that always run (excluded from selection)
|
|
134
179
|
|
|
135
180
|
Returns:
|
|
136
181
|
OrchestratorResult with complexity, category, and selected agents
|
|
137
182
|
"""
|
|
138
|
-
|
|
183
|
+
log_info("orchestrator", "Starting plan analysis...")
|
|
184
|
+
|
|
185
|
+
if mandatory_names is None:
|
|
186
|
+
mandatory_names = set()
|
|
139
187
|
|
|
140
188
|
selection = settings.get("agentSelection", DEFAULT_AGENT_SELECTION)
|
|
141
189
|
categories = settings.get("complexityCategories", DEFAULT_COMPLEXITY_CATEGORIES)
|
|
142
190
|
fallback_count = selection.get("fallbackCount", 2)
|
|
143
191
|
|
|
192
|
+
# Filter out mandatory agents — they always run, no need for orchestrator to select them
|
|
193
|
+
non_mandatory = [a for a in agent_library if a.enabled and a.name not in mandatory_names]
|
|
194
|
+
valid_names = [a.name for a in non_mandatory]
|
|
195
|
+
|
|
196
|
+
log_debug("orchestrator", f"Mandatory agents (always run): {sorted(mandatory_names)}")
|
|
197
|
+
log_debug("orchestrator", f"Non-mandatory agents for selection: {valid_names}")
|
|
198
|
+
|
|
144
199
|
claude_path = shutil.which("claude")
|
|
145
200
|
if claude_path is None:
|
|
146
|
-
|
|
201
|
+
log_warn("orchestrator", "Claude CLI not found on PATH, falling back to medium complexity")
|
|
147
202
|
return OrchestratorResult(
|
|
148
203
|
complexity="medium",
|
|
149
204
|
category="code",
|
|
150
|
-
selected_agents=[a.name for a in
|
|
205
|
+
selected_agents=[a.name for a in non_mandatory][:fallback_count],
|
|
151
206
|
reasoning="Orchestrator skipped - Claude CLI not found",
|
|
152
207
|
error="claude CLI not found on PATH",
|
|
153
208
|
)
|
|
154
209
|
|
|
155
|
-
|
|
210
|
+
log_debug("orchestrator", f"Found Claude CLI at: {claude_path}")
|
|
156
211
|
|
|
157
|
-
# Build agent list
|
|
212
|
+
# Build agent list from non-mandatory agents only
|
|
158
213
|
agent_list = "\n".join([
|
|
159
214
|
f"- {a.name} [{', '.join(a.categories)}]\n"
|
|
160
215
|
f" Focus: {a.focus}\n"
|
|
161
216
|
f" Expertise: {a.description}"
|
|
162
|
-
for a in
|
|
217
|
+
for a in non_mandatory
|
|
163
218
|
])
|
|
164
219
|
category_list = "/".join(categories)
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
220
|
+
|
|
221
|
+
# Compute additional agent counts (total minus mandatory count)
|
|
222
|
+
mandatory_count = len([a for a in agent_library if a.name in mandatory_names])
|
|
223
|
+
simple_additional = max(0, selection.get("simple", {}).get("max", 3) - mandatory_count)
|
|
224
|
+
medium_additional = max(0, selection.get("medium", {}).get("max", 8) - mandatory_count)
|
|
225
|
+
high_additional = max(0, selection.get("high", {}).get("max", 12) - mandatory_count)
|
|
168
226
|
|
|
169
227
|
# System prompt with orchestrator instructions
|
|
170
228
|
system_prompt = """You are a plan orchestrator for code review. Your job is to analyze plans and select appropriate reviewer agents.
|
|
@@ -180,15 +238,16 @@ When selecting agents:
|
|
|
180
238
|
# User prompt with plan and agent list
|
|
181
239
|
prompt = f"""Analyze this plan and select appropriate reviewer agents.
|
|
182
240
|
|
|
183
|
-
Available agents:
|
|
241
|
+
Available agents (select ONLY from this list):
|
|
184
242
|
{agent_list}
|
|
185
243
|
|
|
186
|
-
Selection rules:
|
|
187
|
-
- simple complexity = {
|
|
188
|
-
- medium complexity = {
|
|
189
|
-
- high complexity = {
|
|
244
|
+
Selection rules (number of ADDITIONAL agents to select from the list above):
|
|
245
|
+
- simple complexity = {simple_additional} agents
|
|
246
|
+
- medium complexity = {medium_additional} agents
|
|
247
|
+
- high complexity = {high_additional} agents
|
|
190
248
|
- Only select agents whose categories match the plan category ({category_list})
|
|
191
249
|
- Non-technical plans (life, business) typically need 0 code-focused agents
|
|
250
|
+
- Note: mandatory agents run separately and are NOT listed above
|
|
192
251
|
|
|
193
252
|
PLAN:
|
|
194
253
|
<<<
|
|
@@ -197,7 +256,9 @@ PLAN:
|
|
|
197
256
|
|
|
198
257
|
Call StructuredOutput now with: complexity, category, selectedAgents, reasoning"""
|
|
199
258
|
|
|
200
|
-
|
|
259
|
+
# Use dynamic schema with enum constraint when we have valid agent names
|
|
260
|
+
schema = build_orchestrator_schema(valid_names, categories) if valid_names else ORCHESTRATOR_SCHEMA
|
|
261
|
+
schema_json = json.dumps(schema, ensure_ascii=False)
|
|
201
262
|
|
|
202
263
|
cmd_args = [
|
|
203
264
|
claude_path,
|
|
@@ -210,7 +271,7 @@ Call StructuredOutput now with: complexity, category, selectedAgents, reasoning"
|
|
|
210
271
|
"--system-prompt", system_prompt,
|
|
211
272
|
]
|
|
212
273
|
|
|
213
|
-
|
|
274
|
+
log_info("orchestrator", f"Running with model: {config.model}, timeout: {config.timeout}s")
|
|
214
275
|
|
|
215
276
|
# Get environment for internal subprocess (bypasses hooks)
|
|
216
277
|
env = get_internal_subprocess_env()
|
|
@@ -227,48 +288,48 @@ Call StructuredOutput now with: complexity, category, selectedAgents, reasoning"
|
|
|
227
288
|
env=env,
|
|
228
289
|
)
|
|
229
290
|
except subprocess.TimeoutExpired:
|
|
230
|
-
|
|
291
|
+
log_warn("orchestrator", f"TIMEOUT after {config.timeout}s, falling back to medium complexity")
|
|
231
292
|
return OrchestratorResult(
|
|
232
293
|
complexity="medium",
|
|
233
294
|
category="code",
|
|
234
|
-
selected_agents=[a.name for a in
|
|
295
|
+
selected_agents=[a.name for a in non_mandatory][:fallback_count],
|
|
235
296
|
reasoning="Orchestrator timed out - defaulting to medium complexity",
|
|
236
297
|
error=f"Orchestrator timed out after {config.timeout}s",
|
|
237
298
|
)
|
|
238
299
|
except Exception as ex:
|
|
239
|
-
|
|
300
|
+
log_error("orchestrator", f"Exception: {ex}, falling back to medium complexity")
|
|
240
301
|
return OrchestratorResult(
|
|
241
302
|
complexity="medium",
|
|
242
303
|
category="code",
|
|
243
|
-
selected_agents=[a.name for a in
|
|
304
|
+
selected_agents=[a.name for a in non_mandatory][:fallback_count],
|
|
244
305
|
reasoning=f"Orchestrator failed: {ex}",
|
|
245
306
|
error=str(ex),
|
|
246
307
|
)
|
|
247
308
|
|
|
248
|
-
|
|
309
|
+
log_debug("orchestrator", f"Exit code: {p.returncode}")
|
|
249
310
|
|
|
250
311
|
raw = (p.stdout or "").strip()
|
|
251
312
|
if p.stderr:
|
|
252
|
-
|
|
313
|
+
log_debug("orchestrator", f"stderr: {p.stderr[:300]}")
|
|
253
314
|
|
|
254
315
|
obj = _parse_claude_output(raw)
|
|
255
316
|
|
|
256
317
|
# Debug logging to diagnose empty selectedAgents issue
|
|
257
|
-
|
|
318
|
+
log_debug("orchestrator", f"Raw output length: {len(raw)} chars")
|
|
258
319
|
if raw:
|
|
259
|
-
|
|
260
|
-
|
|
320
|
+
log_debug("orchestrator", f"Raw output (first 500 chars): {raw[:500]}")
|
|
321
|
+
log_debug("orchestrator", f"Parsed obj: {obj}")
|
|
261
322
|
if obj:
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
323
|
+
log_debug("orchestrator", f"obj keys: {list(obj.keys())}")
|
|
324
|
+
log_debug("orchestrator", f"selectedAgents value: {obj.get('selectedAgents', 'MISSING')}")
|
|
325
|
+
log_debug("orchestrator", f"reasoning value: {obj.get('reasoning', 'MISSING')}")
|
|
265
326
|
|
|
266
327
|
if not obj:
|
|
267
|
-
|
|
328
|
+
log_warn("orchestrator", "Failed to parse output, falling back to medium complexity")
|
|
268
329
|
return OrchestratorResult(
|
|
269
330
|
complexity="medium",
|
|
270
331
|
category="code",
|
|
271
|
-
selected_agents=[a.name for a in
|
|
332
|
+
selected_agents=[a.name for a in non_mandatory][:fallback_count],
|
|
272
333
|
reasoning="Orchestrator output could not be parsed",
|
|
273
334
|
error="Failed to parse orchestrator output",
|
|
274
335
|
)
|
|
@@ -289,8 +350,8 @@ Call StructuredOutput now with: complexity, category, selectedAgents, reasoning"
|
|
|
289
350
|
reasoning = str(obj.get("reasoning", "")).strip() or "No reasoning provided"
|
|
290
351
|
skip_reason = obj.get("skipReason")
|
|
291
352
|
|
|
292
|
-
|
|
293
|
-
|
|
353
|
+
log_info("orchestrator", f"Result: complexity={complexity}, category={category}, agents={selected_agents}")
|
|
354
|
+
log_debug("orchestrator", f"Reasoning: {reasoning}")
|
|
294
355
|
|
|
295
356
|
return OrchestratorResult(
|
|
296
357
|
complexity=complexity,
|
package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc
CHANGED
|
Binary file
|
|
@@ -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()
|