aiwcli 0.9.5 → 0.9.7

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 (102) hide show
  1. package/dist/commands/init/index.d.ts +0 -8
  2. package/dist/commands/init/index.js +5 -35
  3. package/dist/lib/index.d.ts +3 -4
  4. package/dist/lib/index.js +3 -5
  5. package/dist/lib/settings-hierarchy.js +5 -16
  6. package/dist/lib/template-installer.d.ts +9 -0
  7. package/dist/lib/template-installer.js +3 -14
  8. package/dist/lib/template-merger.js +1 -12
  9. package/dist/lib/windsurf-hooks-hierarchy.js +2 -13
  10. package/dist/templates/_shared/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  11. package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
  12. package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
  13. package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
  14. package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
  15. package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
  16. package/dist/templates/_shared/hooks/__pycache__/task_create_atomicity.cpython-313.pyc +0 -0
  17. package/dist/templates/_shared/hooks/__pycache__/task_create_capture.cpython-313.pyc +0 -0
  18. package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
  19. package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
  20. package/dist/templates/_shared/hooks/context_enforcer.py +18 -68
  21. package/dist/templates/_shared/hooks/user_prompt_submit.py +1 -1
  22. package/dist/templates/_shared/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  23. package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
  24. package/dist/templates/_shared/lib/base/__pycache__/atomic_write.cpython-313.pyc +0 -0
  25. package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
  26. package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
  27. package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
  28. package/dist/templates/_shared/lib/base/__pycache__/stop_words.cpython-313.pyc +0 -0
  29. package/dist/templates/_shared/lib/base/__pycache__/subprocess_utils.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 +0 -24
  32. package/dist/templates/_shared/lib/base/stop_words.py +55 -0
  33. package/dist/templates/_shared/lib/base/utils.py +5 -20
  34. package/dist/templates/_shared/lib/context/__init__.py +0 -8
  35. package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
  36. package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
  37. package/dist/templates/_shared/lib/context/__pycache__/context_extractor.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__/discovery.cpython-313.pyc +0 -0
  40. package/dist/templates/_shared/lib/context/__pycache__/event_log.cpython-313.pyc +0 -0
  41. package/dist/templates/_shared/lib/context/__pycache__/plan_archive.cpython-313.pyc +0 -0
  42. package/dist/templates/_shared/lib/context/__pycache__/task_sync.cpython-313.pyc +0 -0
  43. package/dist/templates/_shared/lib/context/plan_archive.py +0 -146
  44. package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
  45. package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
  46. package/dist/templates/_shared/lib/templates/__pycache__/persona_questions.cpython-313.pyc +0 -0
  47. package/dist/templates/_shared/lib/templates/__pycache__/plan_context.cpython-313.pyc +0 -0
  48. package/dist/templates/cc-native/.claude/settings.json +12 -12
  49. package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/ACCESSIBILITY-TESTER.md +0 -1
  50. package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +56 -0
  51. package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/CODE-REVIEWER.md +0 -1
  52. package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/CONTEXT-EXTRACTOR.md +0 -1
  53. package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/PENETRATION-TESTER.md +0 -1
  54. package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/PERFORMANCE-ENGINEER.md +0 -1
  55. package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/PLAN-ORCHESTRATOR.md +0 -1
  56. package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/TRADE-OFF-ILLUMINATOR.md +0 -1
  57. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
  58. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
  59. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
  60. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +1 -6
  61. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +10 -11
  62. package/dist/templates/cc-native/_cc-native/lib/__init__.py +0 -4
  63. package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  64. package/dist/templates/cc-native/_cc-native/lib/__pycache__/atomic_write.cpython-313.pyc +0 -0
  65. package/dist/templates/cc-native/_cc-native/lib/__pycache__/constants.cpython-313.pyc +0 -0
  66. package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
  67. package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
  68. package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
  69. package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
  70. package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +36 -18
  71. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
  72. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
  73. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
  74. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
  75. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
  76. package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +13 -9
  77. package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +1 -2
  78. package/dist/templates/cc-native/_cc-native/lib/state.py +11 -9
  79. package/dist/templates/cc-native/_cc-native/lib/utils.py +23 -106
  80. package/dist/templates/cc-native/_cc-native/plan-review.config.json +2 -4
  81. package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
  82. package/dist/templates/cc-native/_cc-native/scripts/aggregate_agents.py +25 -8
  83. package/oclif.manifest.json +1 -1
  84. package/package.json +1 -1
  85. /package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/ARCHITECT-REVIEWER.md +0 -0
  86. /package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/ASSUMPTION-CHAIN-TRACER.md +0 -0
  87. /package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/CLARITY-AUDITOR.md +0 -0
  88. /package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/COMPLETENESS-CHECKER.md +0 -0
  89. /package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/DEVILS-ADVOCATE.md +0 -0
  90. /package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/DOCUMENTATION-REVIEWER.md +0 -0
  91. /package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/FEASIBILITY-ANALYST.md +0 -0
  92. /package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/FRESH-PERSPECTIVE.md +0 -0
  93. /package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/HANDOFF-READINESS.md +0 -0
  94. /package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/HIDDEN-COMPLEXITY-DETECTOR.md +0 -0
  95. /package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/INCENTIVE-MAPPER.md +0 -0
  96. /package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/PRECEDENT-FINDER.md +0 -0
  97. /package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/REVERSIBILITY-ANALYST.md +0 -0
  98. /package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/RISK-ASSESSOR.md +0 -0
  99. /package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/SECOND-ORDER-ANALYST.md +0 -0
  100. /package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/SIMPLICITY-GUARDIAN.md +0 -0
  101. /package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/SKEPTIC.md +0 -0
  102. /package/dist/templates/cc-native/{.claude/agents/cc-native → _cc-native/agents}/STAKEHOLDER-ADVOCATE.md +0 -0
@@ -129,7 +129,7 @@ def run_orchestrator(
129
129
  Args:
130
130
  plan: The plan content to analyze
131
131
  agent_library: List of available agents
132
- config: Orchestrator configuration (model, timeout, max_turns)
132
+ config: Orchestrator configuration (model, timeout)
133
133
  settings: Agent review settings (agentSelection, complexityCategories)
134
134
 
135
135
  Returns:
@@ -154,9 +154,11 @@ def run_orchestrator(
154
154
 
155
155
  eprint(f"[orchestrator] Found Claude CLI at: {claude_path}")
156
156
 
157
- # Build agent list for prompt
157
+ # Build agent list for prompt with rich descriptions
158
158
  agent_list = "\n".join([
159
- f"- {a.name}: {a.focus} (categories: {', '.join(a.categories)})"
159
+ f"- {a.name} [{', '.join(a.categories)}]\n"
160
+ f" Focus: {a.focus}\n"
161
+ f" Expertise: {a.description}"
160
162
  for a in agent_library if a.enabled
161
163
  ])
162
164
  category_list = "/".join(categories)
@@ -164,43 +166,48 @@ def run_orchestrator(
164
166
  medium_range = f"{selection.get('medium', {}).get('min', 1)}-{selection.get('medium', {}).get('max', 2)}"
165
167
  high_range = f"{selection.get('high', {}).get('min', 2)}-{selection.get('high', {}).get('max', 4)}"
166
168
 
167
- prompt = f"""IMPORTANT: Analyze this plan and output your decision immediately using StructuredOutput. Do NOT ask questions.
169
+ # System prompt with orchestrator instructions
170
+ system_prompt = """You are a plan orchestrator for code review. Your job is to analyze plans and select appropriate reviewer agents.
168
171
 
169
- You are a plan orchestrator. Analyze the plan below and determine:
170
- 1. Complexity level (simple/medium/high)
171
- 2. Category ({category_list})
172
- 3. Which agents (if any) should review this plan
172
+ You MUST call StructuredOutput immediately with your analysis. Do NOT ask questions or use any other tools.
173
+
174
+ When selecting agents:
175
+ - Match agent expertise to plan requirements
176
+ - Consider what each agent specializes in
177
+ - Only select agents whose categories match the plan category
178
+ - Fewer agents for simple plans, more for complex plans"""
179
+
180
+ # User prompt with plan and agent list
181
+ prompt = f"""Analyze this plan and select appropriate reviewer agents.
173
182
 
174
183
  Available agents:
175
184
  {agent_list}
176
185
 
177
- Rules:
178
- - simple complexity = {simple_range} agents (CLI review sufficient)
186
+ Selection rules:
187
+ - simple complexity = {simple_range} agents
179
188
  - medium complexity = {medium_range} agents
180
189
  - high complexity = {high_range} agents
181
- - Only select agents whose categories match the plan category
190
+ - Only select agents whose categories match the plan category ({category_list})
182
191
  - Non-technical plans (life, business) typically need 0 code-focused agents
183
192
 
184
- Analyze and call StructuredOutput with your decision now.
185
-
186
193
  PLAN:
187
194
  <<<
188
195
  {plan}
189
196
  >>>
190
- """
197
+
198
+ Call StructuredOutput now with: complexity, category, selectedAgents, reasoning"""
191
199
 
192
200
  schema_json = json.dumps(ORCHESTRATOR_SCHEMA, ensure_ascii=False)
193
201
 
194
202
  cmd_args = [
195
203
  claude_path,
196
204
  "-p", # Enable print mode to read prompt from stdin
197
- "--agent", "plan-orchestrator",
198
205
  "--model", config.model,
199
- "--permission-mode", "bypassPermissions",
200
206
  "--output-format", "json",
201
- "--max-turns", str(config.max_turns),
202
207
  "--json-schema", schema_json,
203
- "--settings", "{}",
208
+ "--max-turns", "3", # Single-turn with buffer for tool call + result
209
+ "--setting-sources", "", # Disable PAI context interference
210
+ "--system-prompt", system_prompt,
204
211
  ]
205
212
 
206
213
  eprint(f"[orchestrator] Running with model: {config.model}, timeout: {config.timeout}s")
@@ -245,6 +252,17 @@ PLAN:
245
252
  eprint(f"[orchestrator] stderr: {p.stderr[:300]}")
246
253
 
247
254
  obj = _parse_claude_output(raw)
255
+
256
+ # Debug logging to diagnose empty selectedAgents issue
257
+ eprint(f"[orchestrator:debug] Raw output length: {len(raw)} chars")
258
+ if raw:
259
+ eprint(f"[orchestrator:debug] Raw output (first 500 chars): {raw[:500]}")
260
+ eprint(f"[orchestrator:debug] Parsed obj: {obj}")
261
+ if obj:
262
+ eprint(f"[orchestrator:debug] obj keys: {list(obj.keys())}")
263
+ eprint(f"[orchestrator:debug] selectedAgents value: {obj.get('selectedAgents', 'MISSING')}")
264
+ eprint(f"[orchestrator:debug] reasoning value: {obj.get('reasoning', 'MISSING')}")
265
+
248
266
  if not obj:
249
267
  eprint("[orchestrator] Failed to parse output, falling back to medium complexity")
250
268
  return OrchestratorResult(
@@ -81,7 +81,6 @@ def run_agent_review(
81
81
  agent: AgentConfig,
82
82
  schema: Dict[str, Any],
83
83
  timeout: int,
84
- max_turns: int = 3,
85
84
  context_path: Optional[Path] = None,
86
85
  session_name: str = "unknown",
87
86
  ) -> ReviewerResult:
@@ -92,7 +91,6 @@ def run_agent_review(
92
91
  agent: Agent configuration (name, model, etc.)
93
92
  schema: JSON schema for the review output
94
93
  timeout: Timeout in seconds
95
- max_turns: Maximum agent turns
96
94
  context_path: Optional path to context folder for debug logging
97
95
  session_name: Session name for debug logging
98
96
 
@@ -113,7 +111,9 @@ def run_agent_review(
113
111
 
114
112
  eprint(f"[{agent.name}] Found Claude CLI at: {claude_path}")
115
113
 
116
- prompt = f"""{AGENT_REVIEW_PROMPT_PREFIX}
114
+ # User prompt - direct instruction to call StructuredOutput immediately
115
+ prompt = f"""IMMEDIATELY call StructuredOutput with your review of the plan below.
116
+ Do NOT output any text before calling StructuredOutput.
117
117
 
118
118
  PLAN:
119
119
  <<<
@@ -122,19 +122,24 @@ PLAN:
122
122
  """
123
123
 
124
124
  schema_json = json.dumps(schema, ensure_ascii=False)
125
+
126
+ # Build command args - use --system-prompt with the markdown body as persona
125
127
  cmd_args = [
126
128
  claude_path,
127
129
  "-p", # Enable print mode to read prompt from stdin
128
- "--agent", agent.name,
129
130
  "--model", agent.model,
130
- "--permission-mode", "plan",
131
131
  "--output-format", "json",
132
- "--max-turns", str(max_turns),
133
132
  "--json-schema", schema_json,
134
- "--settings", "{}",
133
+ "--max-turns", "3", # Allow buffer for tool call + result (usually completes in 2)
134
+ "--setting-sources", "", # Disable user/project settings to avoid PAI context interference
135
135
  ]
136
136
 
137
- eprint(f"[{agent.name}] Running with model: {agent.model}, timeout: {timeout}s, max-turns: {max_turns}")
137
+ # Add system prompt: prefix with single-turn instructions, then agent's persona
138
+ if agent.system_prompt:
139
+ full_prompt = AGENT_REVIEW_PROMPT_PREFIX + "\n\n---\n\n" + agent.system_prompt
140
+ cmd_args.extend(["--system-prompt", full_prompt])
141
+
142
+ eprint(f"[{agent.name}] Running with model: {agent.model}, timeout: {timeout}s")
138
143
 
139
144
  # Get environment for internal subprocess (bypasses hooks)
140
145
  env = get_internal_subprocess_env()
@@ -176,7 +181,6 @@ PLAN:
176
181
  "stderr_len": len(err),
177
182
  "model": agent.model,
178
183
  "timeout": timeout,
179
- "max_turns": max_turns,
180
184
  })
181
185
 
182
186
  if raw:
@@ -39,7 +39,7 @@ class AgentConfig:
39
39
  enabled: bool = True
40
40
  categories: List[str] = field(default_factory=lambda: ["code"])
41
41
  description: str = ""
42
- tools: str = ""
42
+ system_prompt: str = "" # Markdown body content for --system-prompt
43
43
 
44
44
 
45
45
  @dataclass
@@ -48,7 +48,6 @@ class OrchestratorConfig:
48
48
  enabled: bool = True
49
49
  model: str = "haiku"
50
50
  timeout: int = 30
51
- max_turns: int = 3
52
51
 
53
52
 
54
53
  # ---------------------------
@@ -21,6 +21,17 @@ except ImportError:
21
21
  from constants import validate_plan_path, PLANS_DIR
22
22
  from atomic_write import atomic_write
23
23
 
24
+ # Import canonical eprint from shared lib
25
+ try:
26
+ from ...lib.base.utils import eprint
27
+ except ImportError:
28
+ # Fallback for direct execution
29
+ import sys as _sys
30
+ from pathlib import Path as _Path
31
+ _shared_lib = _Path(__file__).resolve().parent.parent.parent / "_shared" / "lib"
32
+ _sys.path.insert(0, str(_shared_lib))
33
+ from base.utils import eprint
34
+
24
35
 
25
36
  # ---------------------------
26
37
  # Constants
@@ -35,15 +46,6 @@ DEFAULT_REVIEW_ITERATIONS: Dict[str, int] = {
35
46
  }
36
47
 
37
48
 
38
- # ---------------------------
39
- # Utilities
40
- # ---------------------------
41
-
42
- def eprint(*args: Any) -> None:
43
- """Print to stderr."""
44
- print(*args, file=sys.stderr)
45
-
46
-
47
49
  # ---------------------------
48
50
  # State File Management
49
51
  # ---------------------------
@@ -29,6 +29,29 @@ except ImportError:
29
29
  from atomic_write import atomic_write
30
30
  from constants import ENABLE_ROBUST_PLAN_WRITES
31
31
 
32
+ # Import canonical utilities from shared lib (with Windows bug fixes)
33
+ try:
34
+ from ...lib.base.utils import (
35
+ eprint,
36
+ now_local,
37
+ project_dir,
38
+ sanitize_filename,
39
+ sanitize_title,
40
+ )
41
+ except ImportError:
42
+ # Fallback for direct execution
43
+ import sys
44
+ from pathlib import Path
45
+ _shared_lib = Path(__file__).resolve().parent.parent.parent / "_shared" / "lib"
46
+ sys.path.insert(0, str(_shared_lib))
47
+ from base.utils import (
48
+ eprint,
49
+ now_local,
50
+ project_dir,
51
+ sanitize_filename,
52
+ sanitize_title,
53
+ )
54
+
32
55
 
33
56
  # ---------------------------
34
57
  # Constants
@@ -87,68 +110,6 @@ class ReviewerResult:
87
110
  err: str
88
111
 
89
112
 
90
- # ---------------------------
91
- # Core utilities
92
- # ---------------------------
93
-
94
- def eprint(*args: Any) -> None:
95
- """Print to stderr."""
96
- print(*args, file=sys.stderr)
97
-
98
-
99
- def now_local() -> datetime:
100
- """Get current local datetime."""
101
- return datetime.now()
102
-
103
-
104
- def project_dir(payload: Dict[str, Any]) -> Path:
105
- """Get project directory from payload or environment."""
106
- p = os.environ.get("CLAUDE_PROJECT_DIR") or payload.get("cwd") or os.getcwd()
107
- return Path(p)
108
-
109
-
110
- def sanitize_filename(s: str, max_len: int = 32) -> str:
111
- """Sanitize string for use in filename."""
112
- s = re.sub(r"[^A-Za-z0-9._-]+", "_", s)
113
- return s.strip("._-")[:max_len] or "unknown"
114
-
115
-
116
- def sanitize_title(s: str, max_len: int = 50) -> str:
117
- """Sanitize title for use in filename (with space-to-dash conversion)."""
118
- s = s.replace(' ', '-')
119
- s = re.sub(r"[^A-Za-z0-9._-]+", "_", s)
120
- s = re.sub(r"[-_]+", "-", s)
121
- return s.strip("._-")[:max_len] or "unknown"
122
-
123
-
124
- def extract_plan_title(plan: str) -> Optional[str]:
125
- """Extract title from '# Plan: <title>' line in plan content."""
126
- for line in plan.split('\n'):
127
- line = line.strip()
128
- if line.startswith('# Plan:'):
129
- title = line[7:].strip()
130
- return title if title else None
131
- return None
132
-
133
-
134
- def extract_task_from_context(plan: str) -> Optional[str]:
135
- """Extract Task from Evaluation Context section as fallback title."""
136
- # Look for **Task**: ... or **Task Summary**: ... patterns
137
- patterns = [
138
- r'\*\*Task\*\*:\s*(.+?)(?:\n|$)',
139
- r'\*\*Task Summary\*\*:\s*(.+?)(?:\n|$)',
140
- ]
141
- for pattern in patterns:
142
- match = re.search(pattern, plan)
143
- if match:
144
- task = match.group(1).strip()
145
- # Truncate to reasonable title length
146
- if len(task) > 50:
147
- task = task[:47] + "..."
148
- return task
149
- return None
150
-
151
-
152
113
  # ---------------------------
153
114
  # Plan hash deduplication
154
115
  # ---------------------------
@@ -398,50 +359,6 @@ def get_state_path_from_plan(plan_path: str) -> Path:
398
359
  return plan_file.with_suffix('.state.json')
399
360
 
400
361
 
401
- def load_state(plan_path: str) -> Optional[Dict[str, Any]]:
402
- """Load state file for this plan if it exists."""
403
- state_file = get_state_path_from_plan(plan_path)
404
-
405
- if not state_file.exists():
406
- return None
407
-
408
- try:
409
- return json.loads(state_file.read_text(encoding="utf-8"))
410
- except Exception as e:
411
- eprint(f"[utils] Failed to read state file: {e}")
412
- return None
413
-
414
-
415
- def save_state(plan_path: str, state: Dict[str, Any]) -> bool:
416
- """Save state file for this plan.
417
-
418
- Returns True on success, False on failure.
419
- """
420
- state_file = get_state_path_from_plan(plan_path)
421
- try:
422
- state_file.write_text(json.dumps(state, indent=2), encoding="utf-8")
423
- return True
424
- except Exception as e:
425
- eprint(f"[utils] Failed to save state file: {e}")
426
- return False
427
-
428
-
429
- def delete_state(plan_path: str) -> bool:
430
- """Delete state file after successful archive.
431
-
432
- Returns True if deleted or didn't exist, False on error.
433
- """
434
- state_file = get_state_path_from_plan(plan_path)
435
- try:
436
- if state_file.exists():
437
- state_file.unlink()
438
- eprint(f"[utils] Deleted state file: {state_file}")
439
- return True
440
- except Exception as e:
441
- eprint(f"[utils] Warning: failed to delete state file: {e}")
442
- return False
443
-
444
-
445
362
  def format_review_markdown(
446
363
  results: List[ReviewerResult],
447
364
  overall: str,
@@ -22,14 +22,12 @@
22
22
  },
23
23
  "agentReview": {
24
24
  "enabled": true,
25
- "timeout": 120,
25
+ "timeout": 180,
26
26
  "blockOnFail": true,
27
- "maxTurns": 2,
28
27
  "orchestrator": {
29
28
  "enabled": true,
30
29
  "model": "haiku",
31
- "timeout": 30,
32
- "maxTurns": 6
30
+ "timeout": 30
33
31
  },
34
32
  "legacyMode": false,
35
33
  "mandatoryAgents": ["handoff-readiness", "clarity-auditor", "skeptic"],
@@ -93,16 +93,31 @@ def extract_frontmatter(content: str) -> dict[str, Any] | None:
93
93
  return None
94
94
 
95
95
 
96
+ def extract_body(content: str) -> str:
97
+ """Extract markdown body (after frontmatter) from content.
98
+
99
+ Args:
100
+ content: Raw markdown file content
101
+
102
+ Returns:
103
+ Markdown body content (everything after the closing ---)
104
+ """
105
+ match = re.match(r'^---\s*\n.*?\n---\s*\n(.*)$', content, re.DOTALL)
106
+ if match:
107
+ return match.group(1).strip()
108
+ return content.strip()
109
+
110
+
96
111
  def aggregate_agents(agents_dir: Path | None = None) -> list[dict[str, Any]]:
97
112
  """Read all agent files and return aggregated metadata.
98
-
113
+
99
114
  Scans the agents directory for .md files, extracts frontmatter from each,
100
115
  and returns a list of agent configurations.
101
-
116
+
102
117
  Args:
103
118
  agents_dir: Path to agents directory. If None, uses default
104
- .claude/agents relative to project root.
105
-
119
+ _cc-native/agents/ relative to this script.
120
+
106
121
  Returns:
107
122
  List of agent configuration dicts with fields:
108
123
  - name: Agent identifier (lowercase)
@@ -111,14 +126,14 @@ def aggregate_agents(agents_dir: Path | None = None) -> list[dict[str, Any]]:
111
126
  - focus: Brief focus for orchestrator
112
127
  - enabled: Whether agent is available
113
128
  - categories: Work categories for filtering
114
- - tools: Available tools
129
+ - system_prompt: Full markdown body (persona content)
115
130
  """
116
131
  if agents_dir is None:
117
- # Default to .claude/agents/cc-native relative to this script's location
132
+ # Default to _cc-native/agents/ relative to this script's location
118
133
  # Script is at: _cc-native/scripts/aggregate_agents.py
119
- # Agents are at: .claude/agents/cc-native/
134
+ # Agents are at: _cc-native/agents/
120
135
  script_dir = Path(__file__).parent
121
- agents_dir = script_dir.parent.parent / ".claude" / "agents" / "cc-native"
136
+ agents_dir = script_dir.parent / "agents"
122
137
 
123
138
  agents = []
124
139
 
@@ -135,6 +150,8 @@ def aggregate_agents(agents_dir: Path | None = None) -> list[dict[str, Any]]:
135
150
  frontmatter["categories"] = ["code"]
136
151
  elif isinstance(frontmatter["categories"], str):
137
152
  frontmatter["categories"] = [frontmatter["categories"]]
153
+ # Extract markdown body as system_prompt
154
+ frontmatter["system_prompt"] = extract_body(content)
138
155
  agents.append(frontmatter)
139
156
  except Exception:
140
157
  # Skip files that can't be read or parsed
@@ -401,5 +401,5 @@
401
401
  ]
402
402
  }
403
403
  },
404
- "version": "0.9.5"
404
+ "version": "0.9.7"
405
405
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aiwcli",
3
3
  "description": "AI Workflow CLI - Command-line interface for AI-powered workflows",
4
- "version": "0.9.5",
4
+ "version": "0.9.7",
5
5
  "author": "jofu-tofu",
6
6
  "bin": {
7
7
  "aiw": "./bin/run.js"