aiwcli 0.10.3 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/bin/run.js +1 -1
  2. package/dist/commands/clear.js +28 -131
  3. package/dist/commands/init/index.js +3 -3
  4. package/dist/lib/gitignore-manager.d.ts +32 -0
  5. package/dist/lib/gitignore-manager.js +141 -2
  6. package/dist/templates/CLAUDE.md +8 -8
  7. package/dist/templates/_shared/.claude/commands/handoff-resume.md +64 -0
  8. package/dist/templates/_shared/.claude/commands/handoff.md +16 -10
  9. package/dist/templates/_shared/.claude/settings.json +7 -7
  10. package/dist/templates/_shared/hooks-ts/_utils/git-state.ts +2 -0
  11. package/dist/templates/_shared/hooks-ts/archive_plan.ts +159 -0
  12. package/dist/templates/_shared/hooks-ts/context_monitor.ts +147 -0
  13. package/dist/templates/_shared/hooks-ts/file-suggestion.ts +130 -0
  14. package/dist/templates/_shared/hooks-ts/pre_compact.ts +49 -0
  15. package/dist/templates/_shared/hooks-ts/session_end.ts +107 -0
  16. package/dist/templates/_shared/hooks-ts/session_start.ts +144 -0
  17. package/dist/templates/_shared/hooks-ts/task_create_capture.ts +48 -0
  18. package/dist/templates/_shared/hooks-ts/task_update_capture.ts +74 -0
  19. package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +83 -0
  20. package/dist/templates/_shared/lib-ts/CLAUDE.md +318 -0
  21. package/dist/templates/_shared/lib-ts/base/atomic-write.ts +12 -12
  22. package/dist/templates/_shared/lib-ts/base/constants.ts +22 -15
  23. package/dist/templates/_shared/lib-ts/base/git-state.ts +1 -1
  24. package/dist/templates/_shared/lib-ts/base/hook-utils.ts +129 -50
  25. package/dist/templates/_shared/lib-ts/base/inference.ts +28 -21
  26. package/dist/templates/_shared/lib-ts/base/logger.ts +15 -2
  27. package/dist/templates/_shared/lib-ts/base/state-io.ts +9 -7
  28. package/dist/templates/_shared/lib-ts/base/stop-words.ts +131 -131
  29. package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +142 -0
  30. package/dist/templates/_shared/lib-ts/base/utils.ts +69 -69
  31. package/dist/templates/_shared/lib-ts/context/context-formatter.ts +30 -24
  32. package/dist/templates/_shared/lib-ts/context/context-selector.ts +50 -32
  33. package/dist/templates/_shared/lib-ts/context/context-store.ts +76 -48
  34. package/dist/templates/_shared/lib-ts/context/plan-manager.ts +43 -23
  35. package/dist/templates/_shared/lib-ts/context/task-tracker.ts +10 -6
  36. package/dist/templates/_shared/lib-ts/handoff/document-generator.ts +11 -10
  37. package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +158 -0
  38. package/dist/templates/_shared/lib-ts/templates/formatters.ts +6 -4
  39. package/dist/templates/_shared/lib-ts/types.ts +68 -55
  40. package/dist/templates/_shared/scripts/resolve_context.ts +24 -0
  41. package/dist/templates/_shared/scripts/resume_handoff.ts +345 -0
  42. package/dist/templates/_shared/scripts/save_handoff.ts +3 -3
  43. package/dist/templates/_shared/scripts/status_line.ts +687 -0
  44. package/dist/templates/cc-native/.claude/settings.json +175 -185
  45. package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +15 -17
  46. package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +0 -2
  47. package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +109 -135
  48. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +119 -0
  49. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +1027 -0
  50. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +61 -0
  51. package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +156 -0
  52. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +792 -0
  53. package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +199 -0
  54. package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +144 -0
  55. package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +57 -0
  56. package/dist/templates/cc-native/_cc-native/lib-ts/constants.ts +83 -0
  57. package/dist/templates/cc-native/_cc-native/lib-ts/corroboration.ts +115 -0
  58. package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +80 -0
  59. package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +120 -0
  60. package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +168 -0
  61. package/dist/templates/cc-native/_cc-native/lib-ts/nul +3 -0
  62. package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +250 -0
  63. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +275 -0
  64. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +130 -0
  65. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +107 -0
  66. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +10 -0
  67. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +23 -0
  68. package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +240 -0
  69. package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +18 -0
  70. package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +385 -0
  71. package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +72 -0
  72. package/dist/templates/cc-native/_cc-native/plan-review.config.json +14 -1
  73. package/oclif.manifest.json +1 -1
  74. package/package.json +2 -2
  75. package/dist/templates/_shared/hooks/__init__.py +0 -16
  76. package/dist/templates/_shared/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  77. package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
  78. package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
  79. package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
  80. package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
  81. package/dist/templates/_shared/hooks/__pycache__/pre_compact.cpython-313.pyc +0 -0
  82. package/dist/templates/_shared/hooks/__pycache__/session_end.cpython-313.pyc +0 -0
  83. package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
  84. package/dist/templates/_shared/hooks/__pycache__/task_create_atomicity.cpython-313.pyc +0 -0
  85. package/dist/templates/_shared/hooks/__pycache__/task_create_capture.cpython-313.pyc +0 -0
  86. package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
  87. package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
  88. package/dist/templates/_shared/hooks/archive_plan.py +0 -177
  89. package/dist/templates/_shared/hooks/context_monitor.py +0 -270
  90. package/dist/templates/_shared/hooks/file-suggestion.py +0 -215
  91. package/dist/templates/_shared/hooks/pre_compact.py +0 -104
  92. package/dist/templates/_shared/hooks/session_end.py +0 -173
  93. package/dist/templates/_shared/hooks/session_start.py +0 -206
  94. package/dist/templates/_shared/hooks/task_create_capture.py +0 -108
  95. package/dist/templates/_shared/hooks/task_update_capture.py +0 -145
  96. package/dist/templates/_shared/hooks/user_prompt_submit.py +0 -139
  97. package/dist/templates/_shared/lib/__init__.py +0 -1
  98. package/dist/templates/_shared/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  99. package/dist/templates/_shared/lib/base/__init__.py +0 -65
  100. package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
  101. package/dist/templates/_shared/lib/base/__pycache__/atomic_write.cpython-313.pyc +0 -0
  102. package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
  103. package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
  104. package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
  105. package/dist/templates/_shared/lib/base/__pycache__/logger.cpython-313.pyc +0 -0
  106. package/dist/templates/_shared/lib/base/__pycache__/stop_words.cpython-313.pyc +0 -0
  107. package/dist/templates/_shared/lib/base/__pycache__/subprocess_utils.cpython-313.pyc +0 -0
  108. package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
  109. package/dist/templates/_shared/lib/base/atomic_write.py +0 -180
  110. package/dist/templates/_shared/lib/base/constants.py +0 -358
  111. package/dist/templates/_shared/lib/base/hook_utils.py +0 -339
  112. package/dist/templates/_shared/lib/base/inference.py +0 -307
  113. package/dist/templates/_shared/lib/base/logger.py +0 -305
  114. package/dist/templates/_shared/lib/base/stop_words.py +0 -221
  115. package/dist/templates/_shared/lib/base/subprocess_utils.py +0 -46
  116. package/dist/templates/_shared/lib/base/utils.py +0 -263
  117. package/dist/templates/_shared/lib/context/__init__.py +0 -102
  118. package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
  119. package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
  120. package/dist/templates/_shared/lib/context/__pycache__/context_extractor.cpython-313.pyc +0 -0
  121. package/dist/templates/_shared/lib/context/__pycache__/context_formatter.cpython-313.pyc +0 -0
  122. package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
  123. package/dist/templates/_shared/lib/context/__pycache__/context_selector.cpython-313.pyc +0 -0
  124. package/dist/templates/_shared/lib/context/__pycache__/context_store.cpython-313.pyc +0 -0
  125. package/dist/templates/_shared/lib/context/__pycache__/discovery.cpython-313.pyc +0 -0
  126. package/dist/templates/_shared/lib/context/__pycache__/event_log.cpython-313.pyc +0 -0
  127. package/dist/templates/_shared/lib/context/__pycache__/plan_archive.cpython-313.pyc +0 -0
  128. package/dist/templates/_shared/lib/context/__pycache__/plan_manager.cpython-313.pyc +0 -0
  129. package/dist/templates/_shared/lib/context/__pycache__/task_sync.cpython-313.pyc +0 -0
  130. package/dist/templates/_shared/lib/context/__pycache__/task_tracker.cpython-313.pyc +0 -0
  131. package/dist/templates/_shared/lib/context/context_formatter.py +0 -317
  132. package/dist/templates/_shared/lib/context/context_selector.py +0 -508
  133. package/dist/templates/_shared/lib/context/context_store.py +0 -653
  134. package/dist/templates/_shared/lib/context/plan_manager.py +0 -303
  135. package/dist/templates/_shared/lib/context/task_tracker.py +0 -188
  136. package/dist/templates/_shared/lib/handoff/__init__.py +0 -22
  137. package/dist/templates/_shared/lib/handoff/__pycache__/__init__.cpython-313.pyc +0 -0
  138. package/dist/templates/_shared/lib/handoff/__pycache__/document_generator.cpython-313.pyc +0 -0
  139. package/dist/templates/_shared/lib/handoff/document_generator.py +0 -278
  140. package/dist/templates/_shared/lib/templates/README.md +0 -206
  141. package/dist/templates/_shared/lib/templates/__init__.py +0 -36
  142. package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
  143. package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
  144. package/dist/templates/_shared/lib/templates/__pycache__/persona_questions.cpython-313.pyc +0 -0
  145. package/dist/templates/_shared/lib/templates/__pycache__/plan_context.cpython-313.pyc +0 -0
  146. package/dist/templates/_shared/lib/templates/formatters.py +0 -146
  147. package/dist/templates/_shared/lib/templates/plan_context.py +0 -73
  148. package/dist/templates/_shared/scripts/__pycache__/save_handoff.cpython-313.pyc +0 -0
  149. package/dist/templates/_shared/scripts/__pycache__/status_line.cpython-313.pyc +0 -0
  150. package/dist/templates/_shared/scripts/save_handoff.py +0 -357
  151. package/dist/templates/_shared/scripts/status_line.py +0 -716
  152. package/dist/templates/cc-native/.claude/commands/cc-native/fresh-perspective.md +0 -8
  153. package/dist/templates/cc-native/.windsurf/workflows/cc-native/fresh-perspective.md +0 -8
  154. package/dist/templates/cc-native/MIGRATION.md +0 -86
  155. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
  156. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
  157. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/mark_questions_asked.cpython-313.pyc +0 -0
  158. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_accepted.cpython-313.pyc +0 -0
  159. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_questions_early.cpython-313.pyc +0 -0
  160. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
  161. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +0 -130
  162. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +0 -954
  163. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.py +0 -81
  164. package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +0 -340
  165. package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +0 -265
  166. package/dist/templates/cc-native/_cc-native/lib/__init__.py +0 -53
  167. package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  168. package/dist/templates/cc-native/_cc-native/lib/__pycache__/atomic_write.cpython-313.pyc +0 -0
  169. package/dist/templates/cc-native/_cc-native/lib/__pycache__/constants.cpython-313.pyc +0 -0
  170. package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
  171. package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
  172. package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
  173. package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
  174. package/dist/templates/cc-native/_cc-native/lib/constants.py +0 -45
  175. package/dist/templates/cc-native/_cc-native/lib/debug.py +0 -139
  176. package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +0 -362
  177. package/dist/templates/cc-native/_cc-native/lib/reviewers/__init__.py +0 -28
  178. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
  179. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
  180. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
  181. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
  182. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
  183. package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +0 -215
  184. package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +0 -88
  185. package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +0 -124
  186. package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +0 -108
  187. package/dist/templates/cc-native/_cc-native/lib/state.py +0 -268
  188. package/dist/templates/cc-native/_cc-native/lib/utils.py +0 -1071
  189. package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
  190. package/dist/templates/cc-native/_cc-native/scripts/aggregate_agents.py +0 -168
  191. package/dist/templates/cc-native/_cc-native/workflows/fresh-perspective.md +0 -134
@@ -1,53 +0,0 @@
1
- """CC-Native shared library modules.
2
-
3
- This package contains shared utilities for cc-native hooks:
4
- - utils: Core utilities (eprint, sanitize, JSON parsing, artifact writing)
5
- - state: Plan state file management and iteration tracking
6
- - orchestrator: Plan complexity analysis and agent selection
7
- - reviewers: CLI and agent-based plan review implementations
8
- """
9
-
10
- from .utils import (
11
- eprint,
12
- sanitize_filename,
13
- sanitize_title,
14
- find_plan_file,
15
- ReviewerResult,
16
- OrchestratorResult,
17
- CombinedReviewResult,
18
- REVIEW_SCHEMA,
19
- )
20
-
21
- from .state import (
22
- get_state_file_path,
23
- load_state,
24
- save_state,
25
- delete_state,
26
- get_iteration_state,
27
- update_iteration_state,
28
- should_continue_iterating,
29
- DEFAULT_REVIEW_ITERATIONS,
30
- )
31
-
32
- __all__ = [
33
- # Core utilities
34
- "eprint",
35
- "sanitize_filename",
36
- "sanitize_title",
37
- "find_plan_file",
38
- # Dataclasses
39
- "ReviewerResult",
40
- "OrchestratorResult",
41
- "CombinedReviewResult",
42
- # Constants
43
- "REVIEW_SCHEMA",
44
- "DEFAULT_REVIEW_ITERATIONS",
45
- # State management
46
- "get_state_file_path",
47
- "load_state",
48
- "save_state",
49
- "delete_state",
50
- "get_iteration_state",
51
- "update_iteration_state",
52
- "should_continue_iterating",
53
- ]
@@ -1,45 +0,0 @@
1
- """Security and configuration constants."""
2
- from pathlib import Path
3
- import os
4
-
5
- # Feature flags
6
- ENABLE_ROBUST_PLAN_WRITES = os.getenv('CC_NATIVE_ROBUST_WRITES', 'true').lower() == 'true'
7
- ENABLE_PLAN_NOTIFICATIONS = os.getenv('CC_NATIVE_NOTIFICATIONS', 'false').lower() == 'true'
8
-
9
- # Security constants
10
- PLANS_DIR = Path.home() / ".claude" / "plans"
11
- MAX_PLAN_PATH_LENGTH = 4096
12
- MAX_ERROR_FILE_SIZE = 10 * 1024 # 10KB
13
-
14
- # Performance constants
15
- MAX_RETRY_ATTEMPTS = 2 # Fast-fail: 2 attempts max
16
- RETRY_BACKOFF_MS = [500, 1000] # 0.5s, 1s (total 1.5s max)
17
- MAX_TOTAL_RETRY_TIME_MS = 3000 # 3 seconds total, well under 5s hook timeout
18
-
19
- def validate_plan_path(plan_path: str) -> Path:
20
- """
21
- Validate and sanitize plan path to prevent traversal attacks.
22
-
23
- Raises:
24
- ValueError: If path is invalid, too long, or outside allowed directory
25
- """
26
- # Input validation
27
- if not plan_path or len(plan_path) > MAX_PLAN_PATH_LENGTH:
28
- raise ValueError(f"Invalid plan path length: {len(plan_path) if plan_path else 0}")
29
-
30
- if '\x00' in plan_path:
31
- raise ValueError("Null bytes not allowed in path")
32
-
33
- # Normalize and resolve to absolute canonical path
34
- try:
35
- resolved = Path(plan_path).resolve(strict=False)
36
- except (OSError, RuntimeError) as e:
37
- raise ValueError(f"Path resolution failed: {e}")
38
-
39
- # Verify path is within allowed directory
40
- try:
41
- resolved.relative_to(PLANS_DIR)
42
- except ValueError:
43
- raise ValueError(f"Path outside allowed directory: {PLANS_DIR}")
44
-
45
- return resolved
@@ -1,139 +0,0 @@
1
- """
2
- Permanent debug logging for cc-native hooks.
3
-
4
- Thin delegation layer over the unified logger (_shared/lib/base/logger.py).
5
- Logs are written to context folder: _output/contexts/<context-id>/debug/hook-log.jsonl
6
- Append-only, cleaned up when context is archived.
7
- Can be disabled via CCNATIVE_DEBUG_DISABLE=1 environment variable.
8
- """
9
-
10
- import os
11
- from pathlib import Path
12
- from typing import Any, Optional
13
-
14
- # Feature flag - set CCNATIVE_DEBUG_DISABLE=1 to turn off
15
- DEBUG_ENABLED = os.environ.get("CCNATIVE_DEBUG_DISABLE", "").lower() not in ("1", "true", "yes")
16
-
17
- # Import unified logger
18
- try:
19
- from _shared.lib.base.logger import hook_log
20
- except ImportError:
21
- # Fallback: try relative import path used by hooks
22
- try:
23
- import sys
24
- _shared = Path(__file__).parent.parent.parent.parent / "_shared"
25
- if str(_shared) not in sys.path:
26
- sys.path.insert(0, str(_shared))
27
- from lib.base.logger import hook_log
28
- except ImportError:
29
- # Last resort: no-op
30
- def hook_log(*args, **kwargs):
31
- pass
32
-
33
-
34
- def get_debug_dir(context_path: Path) -> Path:
35
- """Get or create debug directory within context folder.
36
-
37
- Args:
38
- context_path: Path to context folder (e.g., _output/contexts/<context-id>/)
39
-
40
- Returns:
41
- Path to debug folder: <context_path>/debug/
42
- """
43
- debug_dir = context_path / "debug"
44
- debug_dir.mkdir(parents=True, exist_ok=True)
45
- return debug_dir
46
-
47
-
48
- def get_log_path(context_path: Path, session_name: str) -> Path:
49
- """Get log file path for this session.
50
-
51
- Args:
52
- context_path: Path to context folder
53
- session_name: Session name/ID (will be sanitized)
54
-
55
- Returns:
56
- Path to log file: <context_path>/_output/debug/<session-name>.log
57
- """
58
- safe_name = "".join(c if c.isalnum() or c in "-_" else "_" for c in session_name)[:64]
59
- return get_debug_dir(context_path) / f"{safe_name}.log"
60
-
61
-
62
- def debug_log(
63
- context_path: Path,
64
- session_name: str,
65
- component: str,
66
- message: str,
67
- data: Optional[Any] = None
68
- ) -> None:
69
- """Write a debug log entry. Delegates to unified logger.
70
-
71
- Args:
72
- context_path: Path to context folder
73
- session_name: Session name/ID
74
- component: Component name (e.g., "agent", "orchestrator", "parse")
75
- message: Log message
76
- data: Optional data to include (will be JSON serialized)
77
- """
78
- if not DEBUG_ENABLED:
79
- return
80
-
81
- hook_log(
82
- "debug",
83
- session_name,
84
- message,
85
- component=component,
86
- data=data,
87
- context_path=context_path,
88
- stderr=False,
89
- )
90
-
91
-
92
- def debug_raw(
93
- context_path: Path,
94
- session_name: str,
95
- component: str,
96
- label: str,
97
- raw: str,
98
- max_len: int = 10000
99
- ) -> None:
100
- """Log raw output (stdout, stderr, etc). Delegates to unified logger.
101
-
102
- Args:
103
- context_path: Path to context folder
104
- session_name: Session name/ID
105
- component: Component name
106
- label: Label for the raw content (e.g., "stdout", "stderr")
107
- raw: Raw string content
108
- max_len: Maximum characters to log (default 10000)
109
- """
110
- if not DEBUG_ENABLED:
111
- return
112
-
113
- truncated = raw[:max_len] if len(raw) > max_len else raw
114
- suffix = f" [TRUNCATED from {len(raw)} chars]" if len(raw) > max_len else ""
115
- hook_log(
116
- "debug",
117
- session_name,
118
- f"{label}{suffix}: {truncated}",
119
- component=component,
120
- context_path=context_path,
121
- stderr=False,
122
- )
123
-
124
-
125
- def cleanup_debug_folder(context_path: Path) -> None:
126
- """Remove debug folder during context archive.
127
-
128
- Called by archive_plan.py when archiving a context.
129
-
130
- Args:
131
- context_path: Path to context folder being archived
132
- """
133
- try:
134
- debug_dir = context_path / "debug"
135
- if debug_dir.exists():
136
- import shutil
137
- shutil.rmtree(debug_dir)
138
- except Exception:
139
- pass # Best effort cleanup
@@ -1,362 +0,0 @@
1
- """
2
- CC-Native Plan Orchestrator Module.
3
-
4
- Analyzes plan complexity and selects appropriate reviewers.
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, List, Optional
13
-
14
- # Import from parent lib
15
- _lib_dir = Path(__file__).resolve().parent
16
- sys.path.insert(0, str(_lib_dir))
17
-
18
- from utils import OrchestratorResult, parse_json_maybe
19
- from reviewers.base import AgentConfig, OrchestratorConfig
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
-
26
- # Import shared subprocess utilities
27
- _shared_lib = Path(__file__).resolve().parent.parent.parent / "_shared" / "lib" / "base"
28
- sys.path.insert(0, str(_shared_lib))
29
- from subprocess_utils import get_internal_subprocess_env
30
-
31
-
32
- # ---------------------------
33
- # Constants
34
- # ---------------------------
35
-
36
- DEFAULT_AGENT_SELECTION: Dict[str, Any] = {
37
- "simple": {"min": 3, "max": 3},
38
- "medium": {"min": 8, "max": 8},
39
- "high": {"min": 12, "max": 12},
40
- "fallbackCount": 3,
41
- }
42
-
43
- DEFAULT_COMPLEXITY_CATEGORIES: List[str] = [
44
- "code",
45
- "infrastructure",
46
- "documentation",
47
- "life",
48
- "business",
49
- "design",
50
- "research",
51
- ]
52
-
53
- ORCHESTRATOR_SCHEMA: Dict[str, Any] = {
54
- "type": "object",
55
- "properties": {
56
- "complexity": {"type": "string", "enum": ["simple", "medium", "high"]},
57
- "category": {"type": "string", "enum": DEFAULT_COMPLEXITY_CATEGORIES},
58
- "selectedAgents": {"type": "array", "items": {"type": "string"}},
59
- "reasoning": {"type": "string"},
60
- "skipReason": {"type": "string"},
61
- },
62
- "required": ["complexity", "category", "selectedAgents", "reasoning"],
63
- "additionalProperties": False,
64
- }
65
-
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
-
105
- # ---------------------------
106
- # Output Parsing
107
- # ---------------------------
108
-
109
- def _parse_claude_output(raw: str) -> Optional[Dict[str, Any]]:
110
- """Parse Claude CLI JSON output, handling various formats.
111
-
112
- Claude CLI can output in several formats:
113
- - Direct structured_output dict
114
- - Assistant message with StructuredOutput tool use
115
- - List of events with assistant messages
116
-
117
- Args:
118
- raw: Raw stdout from Claude CLI
119
-
120
- Returns:
121
- Parsed JSON dict or None if parsing failed
122
- """
123
- try:
124
- result = json.loads(raw)
125
- if isinstance(result, dict):
126
- if "structured_output" in result:
127
- log_debug("orchestrator", "Found structured_output in root dict", component="parse")
128
- return result["structured_output"]
129
- if result.get("type") == "assistant":
130
- message = result.get("message", {})
131
- content = message.get("content", [])
132
- for item in content:
133
- if isinstance(item, dict) and item.get("name") == "StructuredOutput":
134
- log_debug("orchestrator", "Found StructuredOutput in assistant message content", component="parse")
135
- return item.get("input", {})
136
- log_debug("orchestrator", "Assistant message found but no StructuredOutput tool use in content", component="parse")
137
- elif isinstance(result, list):
138
- log_debug("orchestrator", f"Received list of {len(result)} events, searching for assistant message", component="parse")
139
- for i, event in enumerate(result):
140
- if not isinstance(event, dict):
141
- continue
142
- if event.get("type") == "assistant":
143
- message = event.get("message", {})
144
- content = message.get("content", [])
145
- for item in content:
146
- if isinstance(item, dict) and item.get("name") == "StructuredOutput":
147
- log_debug("orchestrator", f"Found StructuredOutput in event[{i}] assistant message", component="parse")
148
- return item.get("input", {})
149
- log_debug("orchestrator", "No StructuredOutput found in any assistant message in event list", component="parse")
150
- except json.JSONDecodeError as e:
151
- log_warn("orchestrator", f"JSON decode error: {e}", component="parse")
152
- except Exception as e:
153
- log_error("orchestrator", f"Unexpected error during structured parsing: {e}", component="parse")
154
-
155
- # Fallback to heuristic extraction
156
- log_debug("orchestrator", "No structured output found, falling back to heuristic JSON extraction", component="parse")
157
- return parse_json_maybe(raw)
158
-
159
-
160
- # ---------------------------
161
- # Orchestrator
162
- # ---------------------------
163
-
164
- def run_orchestrator(
165
- plan: str,
166
- agent_library: List[AgentConfig],
167
- config: OrchestratorConfig,
168
- settings: Dict[str, Any],
169
- mandatory_names: Optional[set] = None,
170
- ) -> OrchestratorResult:
171
- """Run the orchestrator agent to analyze plan complexity and select reviewers.
172
-
173
- Args:
174
- plan: The plan content to analyze
175
- agent_library: List of available agents
176
- config: Orchestrator configuration (model, timeout)
177
- settings: Agent review settings (agentSelection, complexityCategories)
178
- mandatory_names: Set of agent names that always run (excluded from selection)
179
-
180
- Returns:
181
- OrchestratorResult with complexity, category, and selected agents
182
- """
183
- log_info("orchestrator", "Starting plan analysis...")
184
-
185
- if mandatory_names is None:
186
- mandatory_names = set()
187
-
188
- selection = settings.get("agentSelection", DEFAULT_AGENT_SELECTION)
189
- categories = settings.get("complexityCategories", DEFAULT_COMPLEXITY_CATEGORIES)
190
- fallback_count = selection.get("fallbackCount", 2)
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
-
199
- claude_path = shutil.which("claude")
200
- if claude_path is None:
201
- log_warn("orchestrator", "Claude CLI not found on PATH, falling back to medium complexity")
202
- return OrchestratorResult(
203
- complexity="medium",
204
- category="code",
205
- selected_agents=[a.name for a in non_mandatory][:fallback_count],
206
- reasoning="Orchestrator skipped - Claude CLI not found",
207
- error="claude CLI not found on PATH",
208
- )
209
-
210
- log_debug("orchestrator", f"Found Claude CLI at: {claude_path}")
211
-
212
- # Build agent list from non-mandatory agents only
213
- agent_list = "\n".join([
214
- f"- {a.name} [{', '.join(a.categories)}]\n"
215
- f" Focus: {a.focus}\n"
216
- f" Expertise: {a.description}"
217
- for a in non_mandatory
218
- ])
219
- category_list = "/".join(categories)
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)
226
-
227
- # System prompt with orchestrator instructions
228
- system_prompt = """You are a plan orchestrator for code review. Your job is to analyze plans and select appropriate reviewer agents.
229
-
230
- You MUST call StructuredOutput immediately with your analysis. Do NOT ask questions or use any other tools.
231
-
232
- When selecting agents:
233
- - Match agent expertise to plan requirements
234
- - Consider what each agent specializes in
235
- - Only select agents whose categories match the plan category
236
- - Fewer agents for simple plans, more for complex plans"""
237
-
238
- # User prompt with plan and agent list
239
- prompt = f"""Analyze this plan and select appropriate reviewer agents.
240
-
241
- Available agents (select ONLY from this list):
242
- {agent_list}
243
-
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
248
- - Only select agents whose categories match the plan category ({category_list})
249
- - Non-technical plans (life, business) typically need 0 code-focused agents
250
- - Note: mandatory agents run separately and are NOT listed above
251
-
252
- PLAN:
253
- <<<
254
- {plan}
255
- >>>
256
-
257
- Call StructuredOutput now with: complexity, category, selectedAgents, reasoning"""
258
-
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)
262
-
263
- cmd_args = [
264
- claude_path,
265
- "-p", # Enable print mode to read prompt from stdin
266
- "--model", config.model,
267
- "--output-format", "json",
268
- "--json-schema", schema_json,
269
- "--max-turns", "3", # Single-turn with buffer for tool call + result
270
- "--setting-sources", "", # Disable PAI context interference
271
- "--system-prompt", system_prompt,
272
- ]
273
-
274
- log_info("orchestrator", f"Running with model: {config.model}, timeout: {config.timeout}s")
275
-
276
- # Get environment for internal subprocess (bypasses hooks)
277
- env = get_internal_subprocess_env()
278
-
279
- try:
280
- p = subprocess.run(
281
- cmd_args,
282
- input=prompt,
283
- text=True,
284
- capture_output=True,
285
- timeout=config.timeout,
286
- encoding="utf-8",
287
- errors="replace",
288
- env=env,
289
- )
290
- except subprocess.TimeoutExpired:
291
- log_warn("orchestrator", f"TIMEOUT after {config.timeout}s, falling back to medium complexity")
292
- return OrchestratorResult(
293
- complexity="medium",
294
- category="code",
295
- selected_agents=[a.name for a in non_mandatory][:fallback_count],
296
- reasoning="Orchestrator timed out - defaulting to medium complexity",
297
- error=f"Orchestrator timed out after {config.timeout}s",
298
- )
299
- except Exception as ex:
300
- log_error("orchestrator", f"Exception: {ex}, falling back to medium complexity")
301
- return OrchestratorResult(
302
- complexity="medium",
303
- category="code",
304
- selected_agents=[a.name for a in non_mandatory][:fallback_count],
305
- reasoning=f"Orchestrator failed: {ex}",
306
- error=str(ex),
307
- )
308
-
309
- log_debug("orchestrator", f"Exit code: {p.returncode}")
310
-
311
- raw = (p.stdout or "").strip()
312
- if p.stderr:
313
- log_debug("orchestrator", f"stderr: {p.stderr[:300]}")
314
-
315
- obj = _parse_claude_output(raw)
316
-
317
- # Debug logging to diagnose empty selectedAgents issue
318
- log_debug("orchestrator", f"Raw output length: {len(raw)} chars")
319
- if raw:
320
- log_debug("orchestrator", f"Raw output (first 500 chars): {raw[:500]}")
321
- log_debug("orchestrator", f"Parsed obj: {obj}")
322
- if obj:
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')}")
326
-
327
- if not obj:
328
- log_warn("orchestrator", "Failed to parse output, falling back to medium complexity")
329
- return OrchestratorResult(
330
- complexity="medium",
331
- category="code",
332
- selected_agents=[a.name for a in non_mandatory][:fallback_count],
333
- reasoning="Orchestrator output could not be parsed",
334
- error="Failed to parse orchestrator output",
335
- )
336
-
337
- # Extract and validate fields
338
- complexity = obj.get("complexity", "medium")
339
- if complexity not in ("simple", "medium", "high"):
340
- complexity = "medium"
341
-
342
- category = obj.get("category", "code")
343
- if category not in categories:
344
- category = "code"
345
-
346
- selected_agents = obj.get("selectedAgents", [])
347
- if not isinstance(selected_agents, list):
348
- selected_agents = []
349
-
350
- reasoning = str(obj.get("reasoning", "")).strip() or "No reasoning provided"
351
- skip_reason = obj.get("skipReason")
352
-
353
- log_info("orchestrator", f"Result: complexity={complexity}, category={category}, agents={selected_agents}")
354
- log_debug("orchestrator", f"Reasoning: {reasoning}")
355
-
356
- return OrchestratorResult(
357
- complexity=complexity,
358
- category=category,
359
- selected_agents=selected_agents,
360
- reasoning=reasoning,
361
- skip_reason=skip_reason if skip_reason else None,
362
- )
@@ -1,28 +0,0 @@
1
- """CC-Native Plan Reviewers Module.
2
-
3
- Provides CLI and agent-based plan review implementations.
4
- """
5
-
6
- from .base import (
7
- ReviewerResult,
8
- REVIEW_SCHEMA,
9
- REVIEW_PROMPT_PREFIX,
10
- AGENT_REVIEW_PROMPT_PREFIX,
11
- AgentConfig,
12
- OrchestratorConfig,
13
- )
14
- from .codex import run_codex_review
15
- from .gemini import run_gemini_review
16
- from .agent import run_agent_review
17
-
18
- __all__ = [
19
- "ReviewerResult",
20
- "REVIEW_SCHEMA",
21
- "REVIEW_PROMPT_PREFIX",
22
- "AGENT_REVIEW_PROMPT_PREFIX",
23
- "AgentConfig",
24
- "OrchestratorConfig",
25
- "run_codex_review",
26
- "run_gemini_review",
27
- "run_agent_review",
28
- ]