aiwcli 0.10.3 → 0.11.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 +1 -1
- package/dist/commands/clear.js +28 -131
- package/dist/commands/init/index.js +3 -3
- package/dist/lib/gitignore-manager.d.ts +32 -0
- package/dist/lib/gitignore-manager.js +141 -2
- package/dist/templates/CLAUDE.md +8 -8
- package/dist/templates/_shared/.claude/commands/handoff-resume.md +64 -0
- package/dist/templates/_shared/.claude/commands/handoff.md +16 -10
- package/dist/templates/_shared/.claude/settings.json +7 -7
- package/dist/templates/_shared/hooks-ts/_utils/git-state.ts +2 -0
- package/dist/templates/_shared/hooks-ts/archive_plan.ts +159 -0
- package/dist/templates/_shared/hooks-ts/context_monitor.ts +147 -0
- package/dist/templates/_shared/hooks-ts/file-suggestion.ts +130 -0
- package/dist/templates/_shared/hooks-ts/pre_compact.ts +49 -0
- package/dist/templates/_shared/hooks-ts/session_end.ts +104 -0
- package/dist/templates/_shared/hooks-ts/session_start.ts +144 -0
- package/dist/templates/_shared/hooks-ts/task_create_capture.ts +48 -0
- package/dist/templates/_shared/hooks-ts/task_update_capture.ts +74 -0
- package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +83 -0
- package/dist/templates/_shared/lib-ts/CLAUDE.md +318 -0
- package/dist/templates/_shared/lib-ts/base/atomic-write.ts +12 -12
- package/dist/templates/_shared/lib-ts/base/constants.ts +22 -15
- package/dist/templates/_shared/lib-ts/base/hook-utils.ts +129 -50
- package/dist/templates/_shared/lib-ts/base/inference.ts +28 -21
- package/dist/templates/_shared/lib-ts/base/logger.ts +31 -15
- package/dist/templates/_shared/lib-ts/base/state-io.ts +9 -7
- package/dist/templates/_shared/lib-ts/base/stop-words.ts +131 -131
- package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +139 -0
- package/dist/templates/_shared/lib-ts/base/utils.ts +69 -69
- package/dist/templates/_shared/lib-ts/context/context-formatter.ts +30 -24
- package/dist/templates/_shared/lib-ts/context/context-selector.ts +50 -32
- package/dist/templates/_shared/lib-ts/context/context-store.ts +76 -48
- package/dist/templates/_shared/lib-ts/context/plan-manager.ts +61 -37
- package/dist/templates/_shared/lib-ts/context/task-tracker.ts +10 -6
- package/dist/templates/_shared/lib-ts/handoff/document-generator.ts +11 -10
- package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +159 -0
- package/dist/templates/_shared/lib-ts/templates/formatters.ts +6 -4
- package/dist/templates/_shared/lib-ts/types.ts +68 -55
- package/dist/templates/_shared/scripts/resolve_context.ts +24 -0
- package/dist/templates/_shared/scripts/resume_handoff.ts +321 -0
- package/dist/templates/_shared/scripts/save_handoff.ts +21 -21
- package/dist/templates/_shared/scripts/status_line.ts +733 -0
- package/dist/templates/cc-native/.claude/settings.json +175 -185
- package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +15 -17
- package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +0 -2
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +109 -135
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +119 -0
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +921 -0
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +61 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +157 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +709 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +199 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +124 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +57 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/constants.ts +83 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +80 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +119 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +162 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/nul +3 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +249 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +155 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +130 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +106 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +10 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +23 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +243 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +18 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +310 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +72 -0
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +1 -9
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/dist/templates/_shared/hooks/__init__.py +0 -16
- 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 +0 -177
- package/dist/templates/_shared/hooks/context_monitor.py +0 -270
- package/dist/templates/_shared/hooks/file-suggestion.py +0 -215
- package/dist/templates/_shared/hooks/pre_compact.py +0 -104
- package/dist/templates/_shared/hooks/session_end.py +0 -173
- package/dist/templates/_shared/hooks/session_start.py +0 -206
- package/dist/templates/_shared/hooks/task_create_capture.py +0 -108
- package/dist/templates/_shared/hooks/task_update_capture.py +0 -145
- package/dist/templates/_shared/hooks/user_prompt_submit.py +0 -139
- package/dist/templates/_shared/lib/__init__.py +0 -1
- package/dist/templates/_shared/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__init__.py +0 -65
- package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/atomic_write.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__/stop_words.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/subprocess_utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/atomic_write.py +0 -180
- package/dist/templates/_shared/lib/base/constants.py +0 -358
- package/dist/templates/_shared/lib/base/hook_utils.py +0 -339
- package/dist/templates/_shared/lib/base/inference.py +0 -307
- package/dist/templates/_shared/lib/base/logger.py +0 -305
- package/dist/templates/_shared/lib/base/stop_words.py +0 -221
- package/dist/templates/_shared/lib/base/subprocess_utils.py +0 -46
- package/dist/templates/_shared/lib/base/utils.py +0 -263
- package/dist/templates/_shared/lib/context/__init__.py +0 -102
- package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_extractor.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__/event_log.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/plan_archive.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_sync.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 +0 -317
- package/dist/templates/_shared/lib/context/context_selector.py +0 -508
- package/dist/templates/_shared/lib/context/context_store.py +0 -653
- package/dist/templates/_shared/lib/context/plan_manager.py +0 -303
- package/dist/templates/_shared/lib/context/task_tracker.py +0 -188
- package/dist/templates/_shared/lib/handoff/__init__.py +0 -22
- 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 +0 -278
- package/dist/templates/_shared/lib/templates/README.md +0 -206
- package/dist/templates/_shared/lib/templates/__init__.py +0 -36
- 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__/persona_questions.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/formatters.py +0 -146
- package/dist/templates/_shared/lib/templates/plan_context.py +0 -73
- 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 +0 -357
- package/dist/templates/_shared/scripts/status_line.py +0 -716
- package/dist/templates/cc-native/.claude/commands/cc-native/fresh-perspective.md +0 -8
- package/dist/templates/cc-native/.windsurf/workflows/cc-native/fresh-perspective.md +0 -8
- package/dist/templates/cc-native/MIGRATION.md +0 -86
- 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 +0 -130
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +0 -954
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.py +0 -81
- package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +0 -340
- package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +0 -265
- package/dist/templates/cc-native/_cc-native/lib/__init__.py +0 -53
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/atomic_write.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/constants.py +0 -45
- package/dist/templates/cc-native/_cc-native/lib/debug.py +0 -139
- package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +0 -362
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__init__.py +0 -28
- 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 +0 -215
- package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +0 -88
- package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +0 -124
- package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +0 -108
- package/dist/templates/cc-native/_cc-native/lib/state.py +0 -268
- package/dist/templates/cc-native/_cc-native/lib/utils.py +0 -1071
- package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/scripts/aggregate_agents.py +0 -168
- package/dist/templates/cc-native/_cc-native/workflows/fresh-perspective.md +0 -134
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
CC-Native Agent Reviewer Module.
|
|
3
|
-
|
|
4
|
-
Runs Claude Code agents 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, Optional
|
|
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, parse_json_maybe, coerce_to_review
|
|
19
|
-
from debug import debug_log, debug_raw
|
|
20
|
-
from .base import AgentConfig, AGENT_REVIEW_PROMPT_PREFIX
|
|
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
|
-
|
|
27
|
-
# Import shared subprocess utilities
|
|
28
|
-
_shared_lib = Path(__file__).resolve().parent.parent.parent.parent / "_shared" / "lib" / "base"
|
|
29
|
-
sys.path.insert(0, str(_shared_lib))
|
|
30
|
-
from subprocess_utils import get_internal_subprocess_env
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def _parse_claude_output(raw: str) -> Optional[Dict[str, Any]]:
|
|
34
|
-
"""Parse Claude CLI JSON output, handling various formats.
|
|
35
|
-
|
|
36
|
-
Claude CLI can output in several formats:
|
|
37
|
-
- Direct structured_output dict
|
|
38
|
-
- Assistant message with StructuredOutput tool use
|
|
39
|
-
- List of events with assistant messages
|
|
40
|
-
|
|
41
|
-
Args:
|
|
42
|
-
raw: Raw stdout from Claude CLI
|
|
43
|
-
|
|
44
|
-
Returns:
|
|
45
|
-
Parsed JSON dict or None if parsing failed
|
|
46
|
-
"""
|
|
47
|
-
try:
|
|
48
|
-
result = json.loads(raw)
|
|
49
|
-
if isinstance(result, dict):
|
|
50
|
-
if "structured_output" in result:
|
|
51
|
-
log_debug("agent", "Found structured_output in root dict", component="parse")
|
|
52
|
-
return result["structured_output"]
|
|
53
|
-
if result.get("type") == "assistant":
|
|
54
|
-
message = result.get("message", {})
|
|
55
|
-
content = message.get("content", [])
|
|
56
|
-
for item in content:
|
|
57
|
-
if isinstance(item, dict) and item.get("name") == "StructuredOutput":
|
|
58
|
-
log_debug("agent", "Found StructuredOutput in assistant message content", component="parse")
|
|
59
|
-
return item.get("input", {})
|
|
60
|
-
log_debug("agent", "Assistant message found but no StructuredOutput tool use in content", component="parse")
|
|
61
|
-
elif isinstance(result, list):
|
|
62
|
-
log_debug("agent", f"Received list of {len(result)} events, searching for assistant message", component="parse")
|
|
63
|
-
for i, event in enumerate(result):
|
|
64
|
-
if not isinstance(event, dict):
|
|
65
|
-
continue
|
|
66
|
-
if event.get("type") == "assistant":
|
|
67
|
-
message = event.get("message", {})
|
|
68
|
-
content = message.get("content", [])
|
|
69
|
-
for item in content:
|
|
70
|
-
if isinstance(item, dict) and item.get("name") == "StructuredOutput":
|
|
71
|
-
log_debug("agent", f"Found StructuredOutput in event[{i}] assistant message", component="parse")
|
|
72
|
-
return item.get("input", {})
|
|
73
|
-
log_debug("agent", "No StructuredOutput found in any assistant message in event list", component="parse")
|
|
74
|
-
except json.JSONDecodeError as e:
|
|
75
|
-
log_warn("agent", f"JSON decode error: {e}", component="parse")
|
|
76
|
-
except Exception as e:
|
|
77
|
-
log_error("agent", f"Unexpected error during structured parsing: {e}", component="parse")
|
|
78
|
-
|
|
79
|
-
# Fallback to heuristic extraction with required field validation
|
|
80
|
-
log_debug("agent", "No structured output found, falling back to heuristic JSON extraction", component="parse")
|
|
81
|
-
return parse_json_maybe(raw, require_fields=["verdict", "summary"])
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def run_agent_review(
|
|
85
|
-
plan: str,
|
|
86
|
-
agent: AgentConfig,
|
|
87
|
-
schema: Dict[str, Any],
|
|
88
|
-
timeout: int,
|
|
89
|
-
context_path: Optional[Path] = None,
|
|
90
|
-
session_name: str = "unknown",
|
|
91
|
-
) -> ReviewerResult:
|
|
92
|
-
"""Run a single Claude Code agent to review the plan.
|
|
93
|
-
|
|
94
|
-
Args:
|
|
95
|
-
plan: The plan content to review
|
|
96
|
-
agent: Agent configuration (name, model, etc.)
|
|
97
|
-
schema: JSON schema for the review output
|
|
98
|
-
timeout: Timeout in seconds
|
|
99
|
-
context_path: Optional path to context folder for debug logging
|
|
100
|
-
session_name: Session name for debug logging
|
|
101
|
-
|
|
102
|
-
Returns:
|
|
103
|
-
ReviewerResult with the review output
|
|
104
|
-
"""
|
|
105
|
-
claude_path = shutil.which("claude")
|
|
106
|
-
if claude_path is None:
|
|
107
|
-
log_warn(agent.name, "Claude CLI not found on PATH")
|
|
108
|
-
return ReviewerResult(
|
|
109
|
-
name=agent.name,
|
|
110
|
-
ok=False,
|
|
111
|
-
verdict="skip",
|
|
112
|
-
data={},
|
|
113
|
-
raw="",
|
|
114
|
-
err="claude CLI not found on PATH",
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
log_debug(agent.name, f"Found Claude CLI at: {claude_path}")
|
|
118
|
-
|
|
119
|
-
# User prompt - direct instruction to call StructuredOutput immediately
|
|
120
|
-
prompt = f"""IMMEDIATELY call StructuredOutput with your review of the plan below.
|
|
121
|
-
Do NOT output any text before calling StructuredOutput.
|
|
122
|
-
|
|
123
|
-
PLAN:
|
|
124
|
-
<<<
|
|
125
|
-
{plan}
|
|
126
|
-
>>>
|
|
127
|
-
"""
|
|
128
|
-
|
|
129
|
-
schema_json = json.dumps(schema, ensure_ascii=False)
|
|
130
|
-
|
|
131
|
-
# Build command args - use --system-prompt with the markdown body as persona
|
|
132
|
-
cmd_args = [
|
|
133
|
-
claude_path,
|
|
134
|
-
"-p", # Enable print mode to read prompt from stdin
|
|
135
|
-
"--model", agent.model,
|
|
136
|
-
"--output-format", "json",
|
|
137
|
-
"--json-schema", schema_json,
|
|
138
|
-
"--max-turns", "3", # Allow buffer for tool call + result (usually completes in 2)
|
|
139
|
-
"--setting-sources", "", # Disable user/project settings to avoid PAI context interference
|
|
140
|
-
]
|
|
141
|
-
|
|
142
|
-
# Add system prompt: prefix with single-turn instructions, then agent's persona
|
|
143
|
-
if agent.system_prompt:
|
|
144
|
-
full_prompt = AGENT_REVIEW_PROMPT_PREFIX + "\n\n---\n\n" + agent.system_prompt
|
|
145
|
-
cmd_args.extend(["--system-prompt", full_prompt])
|
|
146
|
-
|
|
147
|
-
log_info(agent.name, f"Running with model: {agent.model}, timeout: {timeout}s")
|
|
148
|
-
|
|
149
|
-
# Get environment for internal subprocess (bypasses hooks)
|
|
150
|
-
env = get_internal_subprocess_env()
|
|
151
|
-
|
|
152
|
-
try:
|
|
153
|
-
p = subprocess.run(
|
|
154
|
-
cmd_args,
|
|
155
|
-
input=prompt,
|
|
156
|
-
text=True,
|
|
157
|
-
capture_output=True,
|
|
158
|
-
timeout=timeout,
|
|
159
|
-
encoding="utf-8",
|
|
160
|
-
errors="replace",
|
|
161
|
-
env=env,
|
|
162
|
-
)
|
|
163
|
-
except subprocess.TimeoutExpired:
|
|
164
|
-
log_warn(agent.name, f"TIMEOUT after {timeout}s")
|
|
165
|
-
return ReviewerResult(agent.name, False, "error", {}, "", f"{agent.name} timed out after {timeout}s")
|
|
166
|
-
except Exception as ex:
|
|
167
|
-
log_error(agent.name, f"Exception: {ex}")
|
|
168
|
-
return ReviewerResult(agent.name, False, "error", {}, "", f"{agent.name} failed to run: {ex}")
|
|
169
|
-
|
|
170
|
-
log_debug(agent.name, f"Exit code: {p.returncode}")
|
|
171
|
-
log_debug(agent.name, f"stdout length: {len(p.stdout or '')} chars")
|
|
172
|
-
if p.stderr:
|
|
173
|
-
log_debug(agent.name, f"stderr: {p.stderr[:500]}")
|
|
174
|
-
|
|
175
|
-
raw = (p.stdout or "").strip()
|
|
176
|
-
err = (p.stderr or "").strip()
|
|
177
|
-
|
|
178
|
-
# Debug logging - capture full raw output for diagnosis
|
|
179
|
-
if context_path:
|
|
180
|
-
debug_raw(context_path, session_name, f"agent:{agent.name}", "stdout", raw)
|
|
181
|
-
if err:
|
|
182
|
-
debug_raw(context_path, session_name, f"agent:{agent.name}", "stderr", err)
|
|
183
|
-
debug_log(context_path, session_name, f"agent:{agent.name}", "subprocess_info", {
|
|
184
|
-
"exit_code": p.returncode,
|
|
185
|
-
"stdout_len": len(raw),
|
|
186
|
-
"stderr_len": len(err),
|
|
187
|
-
"model": agent.model,
|
|
188
|
-
"timeout": timeout,
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
if raw:
|
|
192
|
-
log_debug(agent.name, f"stdout preview: {raw[:500]}")
|
|
193
|
-
|
|
194
|
-
obj = _parse_claude_output(raw)
|
|
195
|
-
|
|
196
|
-
# Debug logging - capture parsed result details
|
|
197
|
-
if context_path:
|
|
198
|
-
debug_log(context_path, session_name, f"agent:{agent.name}", "parsed_result", {
|
|
199
|
-
"parsed_keys": list(obj.keys()) if obj else None,
|
|
200
|
-
"verdict": obj.get("verdict") if obj else None,
|
|
201
|
-
"has_summary": bool(obj.get("summary")) if obj else False,
|
|
202
|
-
"summary_preview": (obj.get("summary", "")[:200] + "...") if obj and obj.get("summary") and len(obj.get("summary", "")) > 200 else (obj.get("summary") if obj else None),
|
|
203
|
-
"issues_count": len(obj.get("issues", [])) if obj else 0,
|
|
204
|
-
"missing_sections_count": len(obj.get("missing_sections", [])) if obj else 0,
|
|
205
|
-
"questions_count": len(obj.get("questions", [])) if obj else 0,
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
if obj:
|
|
209
|
-
log_info(agent.name, f"Parsed JSON successfully, verdict: {obj.get('verdict', 'N/A')}")
|
|
210
|
-
else:
|
|
211
|
-
log_warn(agent.name, "Failed to parse JSON from output")
|
|
212
|
-
|
|
213
|
-
ok, verdict, norm = coerce_to_review(obj, "Retry or check agent configuration.")
|
|
214
|
-
|
|
215
|
-
return ReviewerResult(agent.name, ok, verdict, norm, raw, err)
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
CC-Native Reviewers Base Module.
|
|
3
|
-
|
|
4
|
-
Provides shared constants and types for plan reviewers.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import sys
|
|
8
|
-
from dataclasses import dataclass, field
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
from typing import Any, Dict, List
|
|
11
|
-
|
|
12
|
-
# Import from parent lib
|
|
13
|
-
_lib_dir = Path(__file__).resolve().parent.parent
|
|
14
|
-
sys.path.insert(0, str(_lib_dir))
|
|
15
|
-
|
|
16
|
-
from utils import ReviewerResult, REVIEW_SCHEMA
|
|
17
|
-
|
|
18
|
-
# Re-export for convenience
|
|
19
|
-
__all__ = [
|
|
20
|
-
"ReviewerResult",
|
|
21
|
-
"REVIEW_SCHEMA",
|
|
22
|
-
"REVIEW_PROMPT_PREFIX",
|
|
23
|
-
"AGENT_REVIEW_PROMPT_PREFIX",
|
|
24
|
-
"AgentConfig",
|
|
25
|
-
"OrchestratorConfig",
|
|
26
|
-
]
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
# ---------------------------
|
|
30
|
-
# Agent Configuration
|
|
31
|
-
# ---------------------------
|
|
32
|
-
|
|
33
|
-
@dataclass
|
|
34
|
-
class AgentConfig:
|
|
35
|
-
"""Configuration for a Claude Code review agent."""
|
|
36
|
-
name: str
|
|
37
|
-
model: str = "sonnet"
|
|
38
|
-
focus: str = ""
|
|
39
|
-
enabled: bool = True
|
|
40
|
-
categories: List[str] = field(default_factory=lambda: ["code"])
|
|
41
|
-
description: str = ""
|
|
42
|
-
system_prompt: str = "" # Markdown body content for --system-prompt
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
@dataclass
|
|
46
|
-
class OrchestratorConfig:
|
|
47
|
-
"""Configuration for the plan orchestrator."""
|
|
48
|
-
enabled: bool = True
|
|
49
|
-
model: str = "haiku"
|
|
50
|
-
timeout: int = 30
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
# ---------------------------
|
|
54
|
-
# Shared Review Prompt Text
|
|
55
|
-
# ---------------------------
|
|
56
|
-
|
|
57
|
-
REVIEW_PROMPT_PREFIX = """You are a senior staff software engineer acting as a strict plan reviewer.
|
|
58
|
-
|
|
59
|
-
Review the PLAN below. Focus on:
|
|
60
|
-
- missing steps, unclear assumptions, edge cases
|
|
61
|
-
- security/privacy concerns
|
|
62
|
-
- testing/rollout/rollback completeness
|
|
63
|
-
- operational concerns (observability, failure modes)
|
|
64
|
-
"""
|
|
65
|
-
|
|
66
|
-
AGENT_REVIEW_PROMPT_PREFIX = """# SINGLE-TURN PLAN REVIEW
|
|
67
|
-
|
|
68
|
-
## CRITICAL: ONE TURN ONLY
|
|
69
|
-
You have exactly ONE response to complete this review. Do NOT attempt multi-step workflows, context queries, or phased analysis. Analyze the plan and output your review immediately.
|
|
70
|
-
|
|
71
|
-
## YOUR TASK
|
|
72
|
-
Review the plan below from your area of expertise. Then call StructuredOutput with your assessment.
|
|
73
|
-
|
|
74
|
-
## REQUIRED OUTPUT (all fields must have content)
|
|
75
|
-
Call StructuredOutput with:
|
|
76
|
-
- **verdict**: "pass" (no concerns), "warn" (some concerns), or "fail" (critical issues)
|
|
77
|
-
- **summary**: 2-3 sentences with your overall assessment and key findings (REQUIRED)
|
|
78
|
-
- **issues**: Array of concerns found. Format each as:
|
|
79
|
-
{"severity": "high/medium/low", "category": "...", "issue": "...", "suggested_fix": "..."}
|
|
80
|
-
- **missing_sections**: Topics the plan should address but doesn't
|
|
81
|
-
- **questions**: Things that need clarification before implementation
|
|
82
|
-
|
|
83
|
-
## IMPORTANT RULES
|
|
84
|
-
1. A "warn" verdict MUST include at least one issue explaining why
|
|
85
|
-
2. Summary MUST explain your reasoning, not just "looks good" or empty
|
|
86
|
-
3. Focus on your expertise area (architecture, security, performance, etc.)
|
|
87
|
-
4. Output StructuredOutput NOW - no other tools, no questions, no delays
|
|
88
|
-
"""
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
CC-Native Codex Reviewer Module.
|
|
3
|
-
|
|
4
|
-
Runs Codex CLI to review plans.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import json
|
|
8
|
-
import shutil
|
|
9
|
-
import subprocess
|
|
10
|
-
import sys
|
|
11
|
-
import tempfile
|
|
12
|
-
from pathlib import Path
|
|
13
|
-
from typing import Any, Dict
|
|
14
|
-
|
|
15
|
-
# Import from parent lib
|
|
16
|
-
_lib_dir = Path(__file__).resolve().parent.parent
|
|
17
|
-
sys.path.insert(0, str(_lib_dir))
|
|
18
|
-
|
|
19
|
-
from utils import ReviewerResult, parse_json_maybe, coerce_to_review
|
|
20
|
-
from .base import REVIEW_PROMPT_PREFIX
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
def run_codex_review(
|
|
29
|
-
plan: str,
|
|
30
|
-
schema: Dict[str, Any],
|
|
31
|
-
settings: Dict[str, Any],
|
|
32
|
-
) -> ReviewerResult:
|
|
33
|
-
"""Run Codex CLI to review the plan.
|
|
34
|
-
|
|
35
|
-
Args:
|
|
36
|
-
plan: The plan content to review
|
|
37
|
-
schema: JSON schema for the review output
|
|
38
|
-
settings: Codex reviewer settings (timeout, model)
|
|
39
|
-
|
|
40
|
-
Returns:
|
|
41
|
-
ReviewerResult with the review output
|
|
42
|
-
"""
|
|
43
|
-
codex_settings = settings.get("reviewers", {}).get("codex", {})
|
|
44
|
-
timeout = codex_settings.get("timeout", 120)
|
|
45
|
-
model = codex_settings.get("model", "")
|
|
46
|
-
|
|
47
|
-
codex_path = shutil.which("codex")
|
|
48
|
-
if codex_path is None:
|
|
49
|
-
log_warn("codex", "CLI not found on PATH")
|
|
50
|
-
return ReviewerResult(
|
|
51
|
-
name="codex",
|
|
52
|
-
ok=False,
|
|
53
|
-
verdict="skip",
|
|
54
|
-
data={},
|
|
55
|
-
raw="",
|
|
56
|
-
err="codex CLI not found on PATH",
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
log_debug("codex", f"Found CLI at: {codex_path}")
|
|
60
|
-
|
|
61
|
-
prompt = f"""{REVIEW_PROMPT_PREFIX}
|
|
62
|
-
Return ONLY a JSON object that matches this JSON Schema:
|
|
63
|
-
{json.dumps(schema, ensure_ascii=False)}
|
|
64
|
-
|
|
65
|
-
PLAN:
|
|
66
|
-
<<<
|
|
67
|
-
{plan}
|
|
68
|
-
>>>
|
|
69
|
-
"""
|
|
70
|
-
|
|
71
|
-
with tempfile.TemporaryDirectory() as td:
|
|
72
|
-
td_path = Path(td)
|
|
73
|
-
schema_path = td_path / "schema.json"
|
|
74
|
-
out_path = td_path / "codex_review.json"
|
|
75
|
-
|
|
76
|
-
schema_path.write_text(json.dumps(schema, indent=2), encoding="utf-8")
|
|
77
|
-
|
|
78
|
-
cmd = [
|
|
79
|
-
codex_path,
|
|
80
|
-
"exec",
|
|
81
|
-
"--full-auto",
|
|
82
|
-
"--sandbox",
|
|
83
|
-
"read-only",
|
|
84
|
-
"--output-schema",
|
|
85
|
-
str(schema_path),
|
|
86
|
-
"-o",
|
|
87
|
-
str(out_path),
|
|
88
|
-
"-",
|
|
89
|
-
]
|
|
90
|
-
|
|
91
|
-
if model:
|
|
92
|
-
cmd.insert(2, "--model")
|
|
93
|
-
cmd.insert(3, model)
|
|
94
|
-
|
|
95
|
-
log_debug("codex", f"Running command: {' '.join(cmd)}")
|
|
96
|
-
|
|
97
|
-
try:
|
|
98
|
-
p = subprocess.run(
|
|
99
|
-
cmd,
|
|
100
|
-
input=prompt,
|
|
101
|
-
text=True,
|
|
102
|
-
capture_output=True,
|
|
103
|
-
timeout=timeout,
|
|
104
|
-
encoding='utf-8',
|
|
105
|
-
errors='replace',
|
|
106
|
-
)
|
|
107
|
-
except subprocess.TimeoutExpired:
|
|
108
|
-
log_warn("codex", f"TIMEOUT after {timeout}s")
|
|
109
|
-
return ReviewerResult("codex", False, "error", {}, "", f"codex timed out after {timeout}s")
|
|
110
|
-
except Exception as ex:
|
|
111
|
-
log_error("codex", f"Exception: {ex}")
|
|
112
|
-
return ReviewerResult("codex", False, "error", {}, "", f"codex failed to run: {ex}")
|
|
113
|
-
|
|
114
|
-
log_debug("codex", f"Exit code: {p.returncode}")
|
|
115
|
-
|
|
116
|
-
raw = ""
|
|
117
|
-
if out_path.exists():
|
|
118
|
-
raw = out_path.read_text(encoding="utf-8", errors="replace")
|
|
119
|
-
|
|
120
|
-
obj = parse_json_maybe(raw) or parse_json_maybe(p.stdout)
|
|
121
|
-
ok, verdict, norm = coerce_to_review(obj, "Retry or check CLI auth/config.")
|
|
122
|
-
|
|
123
|
-
err = (p.stderr or "").strip()
|
|
124
|
-
return ReviewerResult("codex", ok, verdict, norm, raw or p.stdout, err)
|
|
@@ -1,108 +0,0 @@
|
|
|
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, 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
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def run_gemini_review(
|
|
27
|
-
plan: str,
|
|
28
|
-
schema: Dict[str, Any],
|
|
29
|
-
settings: Dict[str, Any],
|
|
30
|
-
) -> ReviewerResult:
|
|
31
|
-
"""Run Gemini CLI to review the plan.
|
|
32
|
-
|
|
33
|
-
Args:
|
|
34
|
-
plan: The plan content to review
|
|
35
|
-
schema: JSON schema for the review output
|
|
36
|
-
settings: Gemini reviewer settings (timeout, model)
|
|
37
|
-
|
|
38
|
-
Returns:
|
|
39
|
-
ReviewerResult with the review output
|
|
40
|
-
"""
|
|
41
|
-
gemini_settings = settings.get("reviewers", {}).get("gemini", {})
|
|
42
|
-
timeout = gemini_settings.get("timeout", 120)
|
|
43
|
-
model = gemini_settings.get("model", "")
|
|
44
|
-
|
|
45
|
-
gemini_path = shutil.which("gemini")
|
|
46
|
-
if gemini_path is None:
|
|
47
|
-
log_warn("gemini", "CLI not found on PATH")
|
|
48
|
-
return ReviewerResult(
|
|
49
|
-
name="gemini",
|
|
50
|
-
ok=False,
|
|
51
|
-
verdict="skip",
|
|
52
|
-
data={},
|
|
53
|
-
raw="",
|
|
54
|
-
err="gemini CLI not found on PATH",
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
log_debug("gemini", f"Found CLI at: {gemini_path}")
|
|
58
|
-
|
|
59
|
-
instruction = f"""
|
|
60
|
-
|
|
61
|
-
Review the PLAN above as a senior staff software engineer. Focus on:
|
|
62
|
-
- missing steps, unclear assumptions, edge cases
|
|
63
|
-
- security/privacy concerns
|
|
64
|
-
- testing/rollout/rollback completeness
|
|
65
|
-
- operational concerns (observability, failure modes)
|
|
66
|
-
|
|
67
|
-
Return ONLY a JSON object that matches this JSON Schema (no markdown, no code fences):
|
|
68
|
-
{json.dumps(schema, ensure_ascii=False)}
|
|
69
|
-
"""
|
|
70
|
-
|
|
71
|
-
cmd = [
|
|
72
|
-
gemini_path,
|
|
73
|
-
"-y", # YOLO mode - auto-approve all actions
|
|
74
|
-
"-p",
|
|
75
|
-
instruction,
|
|
76
|
-
]
|
|
77
|
-
|
|
78
|
-
if model:
|
|
79
|
-
cmd.extend(["--model", model])
|
|
80
|
-
|
|
81
|
-
log_debug("gemini", "Running command: gemini -y -p <instruction>")
|
|
82
|
-
|
|
83
|
-
try:
|
|
84
|
-
p = subprocess.run(
|
|
85
|
-
cmd,
|
|
86
|
-
input=plan,
|
|
87
|
-
text=True,
|
|
88
|
-
capture_output=True,
|
|
89
|
-
timeout=timeout,
|
|
90
|
-
encoding='utf-8',
|
|
91
|
-
errors='replace',
|
|
92
|
-
)
|
|
93
|
-
except subprocess.TimeoutExpired:
|
|
94
|
-
log_warn("gemini", f"TIMEOUT after {timeout}s")
|
|
95
|
-
return ReviewerResult("gemini", False, "error", {}, "", f"gemini timed out after {timeout}s")
|
|
96
|
-
except Exception as ex:
|
|
97
|
-
log_error("gemini", f"Exception: {ex}")
|
|
98
|
-
return ReviewerResult("gemini", False, "error", {}, "", f"gemini failed to run: {ex}")
|
|
99
|
-
|
|
100
|
-
log_debug("gemini", f"Exit code: {p.returncode}")
|
|
101
|
-
|
|
102
|
-
raw = (p.stdout or "").strip()
|
|
103
|
-
err = (p.stderr or "").strip()
|
|
104
|
-
|
|
105
|
-
obj = parse_json_maybe(raw)
|
|
106
|
-
ok, verdict, norm = coerce_to_review(obj, "Retry or check CLI auth/config.")
|
|
107
|
-
|
|
108
|
-
return ReviewerResult("gemini", ok, verdict, norm, raw, err)
|