aiwcli 0.13.8 → 0.15.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.
- package/README.md +11 -1
- package/dist/commands/launch.d.ts +8 -0
- package/dist/commands/launch.js +96 -5
- package/dist/templates/_shared/.claude/skills/codex/SKILL.md +42 -0
- package/dist/templates/_shared/.claude/skills/codex/prompt.md +30 -0
- package/dist/templates/_shared/lib-ts/agent-exec/backends/headless.ts +33 -0
- package/dist/templates/_shared/lib-ts/agent-exec/backends/index.ts +6 -0
- package/dist/templates/_shared/lib-ts/agent-exec/backends/tmux.ts +145 -0
- package/dist/templates/_shared/lib-ts/agent-exec/base-agent.ts +229 -0
- package/dist/templates/_shared/lib-ts/agent-exec/execution-backend.ts +50 -0
- package/dist/templates/_shared/lib-ts/agent-exec/index.ts +6 -0
- package/dist/templates/_shared/lib-ts/agent-exec/structured-output.ts +166 -0
- package/dist/templates/_shared/lib-ts/base/cli-args.ts +287 -0
- package/dist/templates/_shared/lib-ts/base/inference.ts +53 -47
- package/dist/templates/_shared/lib-ts/base/models.ts +16 -0
- package/dist/templates/_shared/lib-ts/base/preflight.ts +98 -0
- package/dist/templates/_shared/lib-ts/base/state-io.ts +1 -1
- package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +4 -3
- package/dist/templates/_shared/lib-ts/base/tmux-driver.ts +381 -0
- package/dist/templates/_shared/lib-ts/base/utils.ts +8 -0
- package/dist/templates/_shared/lib-ts/context/context-formatter.ts +35 -11
- package/dist/templates/_shared/lib-ts/context/context-store.ts +3 -0
- package/dist/templates/_shared/lib-ts/types.ts +17 -0
- package/dist/templates/_shared/scripts/status_line.ts +93 -47
- package/dist/templates/_shared/skills/prompt-codex/CLAUDE.md +71 -0
- package/dist/templates/_shared/skills/prompt-codex/scripts/launch-codex.ts +387 -0
- package/dist/templates/_shared/skills/prompt-codex/scripts/watch-codex.ts +257 -0
- package/dist/templates/cc-native/.claude/settings.json +121 -1
- package/dist/templates/cc-native/_cc-native/CLAUDE.md +73 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/CLAUDE.md +70 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +9 -133
- package/dist/templates/cc-native/_cc-native/lib-ts/settings.ts +120 -43
- package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +1 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +66 -12
- package/dist/templates/cc-native/_cc-native/plan-review/lib/agent-selection.ts +5 -4
- package/dist/templates/cc-native/_cc-native/plan-review/lib/orchestrator.ts +4 -4
- package/dist/templates/cc-native/_cc-native/plan-review/lib/preflight.ts +14 -80
- package/dist/templates/cc-native/_cc-native/plan-review/lib/review-pipeline.ts +16 -13
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/agent.ts +19 -7
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/base/base-agent.ts +4 -215
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/index.ts +1 -1
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/claude-agent.ts +9 -39
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/codex-agent.ts +19 -22
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/gemini-agent.ts +2 -1
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/orchestrator-claude-agent.ts +65 -36
- package/oclif.manifest.json +21 -3
- package/package.json +1 -1
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
{
|
|
2
|
+
"statusLine": {
|
|
3
|
+
"type": "command",
|
|
4
|
+
"command": "NO_COLOR= FORCE_COLOR=2 bun ~/.aiwcli/bin/resolve-run.ts .aiwcli/_shared/scripts/status_line.ts"
|
|
5
|
+
},
|
|
6
|
+
"fileSuggestion": {
|
|
7
|
+
"type": "command",
|
|
8
|
+
"command": "bun ~/.aiwcli/bin/resolve-run.ts .aiwcli/_shared/hooks-ts/file-suggestion.ts"
|
|
9
|
+
},
|
|
2
10
|
"hooks": {
|
|
3
11
|
"UserPromptSubmit": [
|
|
12
|
+
{
|
|
13
|
+
"hooks": [
|
|
14
|
+
{
|
|
15
|
+
"type": "command",
|
|
16
|
+
"command": "bun ~/.aiwcli/bin/resolve-run.ts .aiwcli/_shared/hooks-ts/user_prompt_submit.ts",
|
|
17
|
+
"timeout": 10000
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
},
|
|
4
21
|
{
|
|
5
22
|
"hooks": [
|
|
6
23
|
{
|
|
@@ -12,6 +29,56 @@
|
|
|
12
29
|
}
|
|
13
30
|
],
|
|
14
31
|
"PostToolUse": [
|
|
32
|
+
{
|
|
33
|
+
"matcher": "*",
|
|
34
|
+
"hooks": [
|
|
35
|
+
{
|
|
36
|
+
"type": "command",
|
|
37
|
+
"command": "bun ~/.aiwcli/bin/resolve-run.ts .aiwcli/_shared/hooks-ts/context_monitor.ts",
|
|
38
|
+
"timeout": 5000
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"matcher": "TaskCreate",
|
|
44
|
+
"hooks": [
|
|
45
|
+
{
|
|
46
|
+
"type": "command",
|
|
47
|
+
"command": "bun ~/.aiwcli/bin/resolve-run.ts .aiwcli/_shared/hooks-ts/task_create_capture.ts",
|
|
48
|
+
"timeout": 3000
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"matcher": "TaskUpdate",
|
|
54
|
+
"hooks": [
|
|
55
|
+
{
|
|
56
|
+
"type": "command",
|
|
57
|
+
"command": "bun ~/.aiwcli/bin/resolve-run.ts .aiwcli/_shared/hooks-ts/task_update_capture.ts",
|
|
58
|
+
"timeout": 3000
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"matcher": "ExitPlanMode",
|
|
64
|
+
"hooks": [
|
|
65
|
+
{
|
|
66
|
+
"type": "command",
|
|
67
|
+
"command": "bun ~/.aiwcli/bin/resolve-run.ts .aiwcli/_shared/hooks-ts/archive_plan.ts",
|
|
68
|
+
"timeout": 5000
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"matcher": "Write|Edit",
|
|
74
|
+
"hooks": [
|
|
75
|
+
{
|
|
76
|
+
"type": "command",
|
|
77
|
+
"command": "bun ~/.aiwcli/bin/resolve-run.ts .aiwcli/_shared/hooks-ts/lint_after_edit.ts",
|
|
78
|
+
"timeout": 10000
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
},
|
|
15
82
|
{
|
|
16
83
|
"matcher": "AskUserQuestion",
|
|
17
84
|
"hooks": [
|
|
@@ -44,6 +111,51 @@
|
|
|
44
111
|
]
|
|
45
112
|
}
|
|
46
113
|
],
|
|
114
|
+
"SessionStart": [
|
|
115
|
+
{
|
|
116
|
+
"hooks": [
|
|
117
|
+
{
|
|
118
|
+
"type": "command",
|
|
119
|
+
"command": "bun ~/.aiwcli/bin/resolve-run.ts .aiwcli/_shared/hooks-ts/session_start.ts",
|
|
120
|
+
"timeout": 5000
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
],
|
|
125
|
+
"SessionEnd": [
|
|
126
|
+
{
|
|
127
|
+
"hooks": [
|
|
128
|
+
{
|
|
129
|
+
"type": "command",
|
|
130
|
+
"command": "bun ~/.aiwcli/bin/resolve-run.ts .aiwcli/_shared/hooks-ts/session_end.ts",
|
|
131
|
+
"timeout": 5000
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
],
|
|
136
|
+
"PreCompact": [
|
|
137
|
+
{
|
|
138
|
+
"hooks": [
|
|
139
|
+
{
|
|
140
|
+
"type": "command",
|
|
141
|
+
"command": "bun ~/.aiwcli/bin/resolve-run.ts .aiwcli/_shared/hooks-ts/pre_compact.ts",
|
|
142
|
+
"timeout": 5000
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
],
|
|
147
|
+
"PermissionRequest": [
|
|
148
|
+
{
|
|
149
|
+
"matcher": "ExitPlanMode",
|
|
150
|
+
"hooks": [
|
|
151
|
+
{
|
|
152
|
+
"type": "command",
|
|
153
|
+
"command": "bun ~/.aiwcli/bin/resolve-run.ts .aiwcli/_shared/hooks-ts/archive_plan.ts",
|
|
154
|
+
"timeout": 5000
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
],
|
|
47
159
|
"PreToolUse": [
|
|
48
160
|
{
|
|
49
161
|
"matcher": "^TaskCreate$",
|
|
@@ -83,5 +195,13 @@
|
|
|
83
195
|
"env": {
|
|
84
196
|
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"
|
|
85
197
|
},
|
|
86
|
-
"enabledPlugins": {}
|
|
198
|
+
"enabledPlugins": {},
|
|
199
|
+
"methods": {
|
|
200
|
+
"cc-native": {
|
|
201
|
+
"ides": [
|
|
202
|
+
"claude"
|
|
203
|
+
],
|
|
204
|
+
"installedAt": "2026-02-21T16:59:33.520Z"
|
|
205
|
+
}
|
|
206
|
+
}
|
|
87
207
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# CC-Native Method
|
|
2
|
+
|
|
3
|
+
**Location:** `.aiwcli/_cc-native/` — Claude Code-specific plan review, artifacts, and agent orchestration.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Subsystems
|
|
8
|
+
|
|
9
|
+
| Directory | Purpose | CLAUDE.md |
|
|
10
|
+
|-----------|---------|-----------|
|
|
11
|
+
| `agents/` | Plan review agent roster and specs | `agents/CLAUDE.md` |
|
|
12
|
+
| `artifacts/` | Review artifact generation and tracking | `artifacts/CLAUDE.md` |
|
|
13
|
+
| `hooks/` | CC-native hook entry points (plan review triggers) | `hooks/CLAUDE.md` |
|
|
14
|
+
| `lib-ts/` | Shared TypeScript library for cc-native subsystems | `lib-ts/CLAUDE.md` |
|
|
15
|
+
| `lib-ts/rlm/` | Retrieval-augmented learning memory | `lib-ts/rlm/CLAUDE.md` |
|
|
16
|
+
| `plan-review/` | Multi-agent plan review pipeline | `plan-review/CLAUDE.md` |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Shared Infrastructure (`_shared/lib-ts/`)
|
|
21
|
+
|
|
22
|
+
CC-native code depends heavily on shared infrastructure. Full API details: `_shared/lib-ts/CLAUDE.md`.
|
|
23
|
+
|
|
24
|
+
| Module | Capability | Use When |
|
|
25
|
+
|--------|-----------|----------|
|
|
26
|
+
| `base/hook-utils` | Hook lifecycle (load input, run, emit context) | Writing hooks |
|
|
27
|
+
| `base/logger` | Structured logging (debug/info/warn/error) | Any hook or lib module |
|
|
28
|
+
| `base/constants` | Project paths, context dirs, sanitization | Resolving file locations |
|
|
29
|
+
| `base/subprocess-utils` | Find executables, exec with env, shell quoting | Spawning agent CLIs |
|
|
30
|
+
| `base/cli-args` | CLI invocation builder, review spec construction | Launching review agents |
|
|
31
|
+
| `base/atomic-write` | Crash-safe file writes | Writing state or artifacts |
|
|
32
|
+
| `base/state-io` | State read/write helpers | Context state persistence |
|
|
33
|
+
| `base/inference` | Claude CLI subprocess calls | AI inference from hooks |
|
|
34
|
+
| `context/context-store` | Context CRUD (get by session, list all) | Session/context binding |
|
|
35
|
+
| `context/plan-manager` | Plan lifecycle (archive, hash, sign) | Plan discovery and hashing |
|
|
36
|
+
| `types` | Shared type definitions (`ContextState`, etc.) | Type imports |
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Import Patterns
|
|
41
|
+
|
|
42
|
+
**Import direction:** `hooks/` → `lib-ts/` → `_shared/lib-ts/`. Never the reverse.
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
// From hooks/ (2 levels up to _shared):
|
|
46
|
+
import { runHook, logInfo } from "../../_shared/lib-ts/base/hook-utils.js";
|
|
47
|
+
import { loadConfig } from "../lib-ts/config.js";
|
|
48
|
+
|
|
49
|
+
// From lib-ts/ (2 levels up to _shared):
|
|
50
|
+
import { logDebug } from "../../_shared/lib-ts/base/logger.js";
|
|
51
|
+
import { atomicWrite } from "../../_shared/lib-ts/base/atomic-write.js";
|
|
52
|
+
|
|
53
|
+
// From plan-review/lib/ (3 levels up to _shared):
|
|
54
|
+
import { logInfo } from "../../../_shared/lib-ts/base/logger.js";
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Context Maintenance
|
|
60
|
+
|
|
61
|
+
**After modifying files in this directory:** scan the entries above — if any claim is now
|
|
62
|
+
false or incomplete, update this file before ending the task. Do not defer.
|
|
63
|
+
|
|
64
|
+
**Add** an entry only if an agent would fail without knowing it, it is not obvious from
|
|
65
|
+
the code, and it belongs at this scope.
|
|
66
|
+
|
|
67
|
+
**Remove** any entry that fails the falsifiability test: if removing it would not change
|
|
68
|
+
how an agent acts here, remove it.
|
|
69
|
+
|
|
70
|
+
**Staleness anchor:** This file assumes `lib-ts/index.ts` exists. If it doesn't, this file
|
|
71
|
+
is stale — update or regenerate before relying on it.
|
|
72
|
+
|
|
73
|
+
<!-- context-layer: generated=2026-03-01 | last-audited=2026-03-01 | version=1 | dir-commits-at-audit=15 -->
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# CC-Native Library
|
|
2
|
+
|
|
3
|
+
**Location:** `_cc-native/lib-ts/` — TypeScript modules for plan review, state, and agent orchestration.
|
|
4
|
+
|
|
5
|
+
**Import direction:** `hooks/` → `lib-ts/` → `_shared/lib-ts/`. Never the reverse.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Module Reference
|
|
10
|
+
|
|
11
|
+
The barrel `index.ts` re-exports most modules. Three files are **not** re-exported and must be imported directly: `plan-discovery.ts`, `plan-enhancement.ts`, `settings.ts`.
|
|
12
|
+
|
|
13
|
+
| File | Purpose | Key Exports |
|
|
14
|
+
|------|---------|-------------|
|
|
15
|
+
| `aggregate-agents.ts` | Agent frontmatter parser — loads agent configs from markdown | `aggregateAgents`, `extractBody`, `extractFrontmatter` |
|
|
16
|
+
| `cc-native-state.ts` | CC-native state accessor for context `state.json` | `getCcNativeState`, `saveCcNativeState`, `isPlanAlreadyReviewed`, `markPlanReviewed`, `markQuestionsAsked` |
|
|
17
|
+
| `cli-output-parser.ts` | Unified Claude CLI JSON output parser | `parseCliOutput` |
|
|
18
|
+
| `config.ts` | Configuration loading from `cc-native.config.json` | `loadConfig`, `getDisplaySettings` |
|
|
19
|
+
| `constants.ts` | Feature flags, security limits, path validation | `ENABLE_ROBUST_PLAN_WRITES`, `PLANS_DIR`, `validatePlanPath`, `MAX_RETRY_ATTEMPTS` |
|
|
20
|
+
| `debug.ts` | Per-context debug logging (thin layer over shared logger) | `debugLog`, `debugRaw`, `getDebugDir`, `cleanupDebugFolder` |
|
|
21
|
+
| `index.ts` | Barrel — re-exports public API from all modules | (see individual modules) |
|
|
22
|
+
| `json-parser.ts` | JSON parsing with recovery for LLM responses | `parseJsonMaybe`, `coerceToReview` |
|
|
23
|
+
| `plan-discovery.ts` | Plan file discovery, reading, and hashing | *(not re-exported)* — import directly |
|
|
24
|
+
| `plan-enhancement.ts` | Plan quality guidance prompt for context emission | *(not re-exported)* — import directly |
|
|
25
|
+
| `settings.ts` | Settings loading, defaults, agent library management | *(not re-exported)* — import directly |
|
|
26
|
+
| `state.ts` | Iteration state management for plan review cycles | `loadState`, `saveStateToPlan`, `getIterationState`, `shouldContinueIterating` |
|
|
27
|
+
| `types.ts` | All cc-native type definitions and prompt constants | `Verdict`, `ReviewData`, `AgentConfig`, `PlanReviewConfig`, `REVIEW_SCHEMA` |
|
|
28
|
+
|
|
29
|
+
**Subfolder:** `rlm/` — retrieval-augmented learning memory. Has its own `rlm/CLAUDE.md`.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Shared Dependencies
|
|
34
|
+
|
|
35
|
+
These `_shared/lib-ts` modules are used across cc-native lib-ts:
|
|
36
|
+
|
|
37
|
+
| Shared Module | Used By |
|
|
38
|
+
|--------------|---------|
|
|
39
|
+
| `base/logger` | All modules (logging) |
|
|
40
|
+
| `base/atomic-write` | `state.ts` (crash-safe writes) |
|
|
41
|
+
| `base/utils` | `cc-native-state.ts` (`nowIso`) |
|
|
42
|
+
| `context/context-store` | `cc-native-state.ts` (state access) |
|
|
43
|
+
| `context/plan-manager` | `plan-discovery.ts` (plan path lookup) |
|
|
44
|
+
| `types` | `types.ts` (re-exports `ContextState`, `HookInput`, `HookOutput`) |
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Import Direction
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
hooks/ (entry points — import from lib-ts/ and _shared/)
|
|
52
|
+
↓
|
|
53
|
+
lib-ts/ (this directory — import from _shared/ only)
|
|
54
|
+
↓
|
|
55
|
+
_shared/lib-ts/ (cross-method infrastructure — no reverse imports)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Never import from `hooks/` or `plan-review/` into `lib-ts/`. The one exception noted in `aggregate-agents.ts`: it stays in `lib-ts/` because both `settings.ts` and `plan-review/` depend on it.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Context Maintenance
|
|
63
|
+
|
|
64
|
+
**After modifying files in this directory:** scan the entries above — if any claim is now
|
|
65
|
+
false or incomplete, update this file before ending the task. Do not defer.
|
|
66
|
+
|
|
67
|
+
**Staleness anchor:** This file assumes `index.ts` exists with 13 sibling `.ts` files. If the
|
|
68
|
+
count changes, update the Module Reference table.
|
|
69
|
+
|
|
70
|
+
<!-- context-layer: generated=2026-03-01 | last-audited=2026-03-01 | version=1 | dir-commits-at-audit=15 -->
|
|
@@ -1,144 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* See cc-native-plan-review-spec.md §4.6
|
|
2
|
+
* CC-native wrapper around shared structured-output parsing.
|
|
3
|
+
* Keeps existing import surface stable for provider implementations.
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
|
-
import {
|
|
8
|
-
import { logDebug, logError, logWarn } from "../../_shared/lib-ts/base/logger.js";
|
|
6
|
+
import { parseStructuredOutput } from "../../_shared/lib-ts/agent-exec/structured-output.js";
|
|
9
7
|
|
|
10
8
|
/**
|
|
11
|
-
* Parse
|
|
12
|
-
*
|
|
13
|
-
* Claude CLI can output in several formats:
|
|
14
|
-
* - Direct structured_output dict
|
|
15
|
-
* - Assistant message with StructuredOutput tool use
|
|
16
|
-
* - List of events with assistant messages
|
|
17
|
-
* - Raw text with embedded JSON (heuristic fallback)
|
|
18
|
-
*
|
|
19
|
-
* @param raw - Raw stdout from Claude CLI
|
|
20
|
-
* @param requireFields - Optional fields to validate in heuristic fallback
|
|
21
|
-
* @returns Parsed JSON dict or null if parsing failed
|
|
9
|
+
* Parse CLI JSON output into a structured object.
|
|
10
|
+
* Delegates to shared parser with cc-native logging tag.
|
|
22
11
|
*/
|
|
23
12
|
export function parseCliOutput(
|
|
24
13
|
raw: string,
|
|
25
14
|
requireFields?: string[],
|
|
26
15
|
): null | Record<string, unknown> {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const dict = result as Record<string, unknown>;
|
|
32
|
-
|
|
33
|
-
// Strategy 1: Direct structured_output key
|
|
34
|
-
if ("structured_output" in dict) {
|
|
35
|
-
logDebug("cli_parser", "Found structured_output in root dict");
|
|
36
|
-
return dict.structured_output as Record<string, unknown>;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Strategy 1.5: Session result envelope (type: "result")
|
|
40
|
-
// When the model fails to call StructuredOutput, the CLI returns a
|
|
41
|
-
// session metadata object with type/subtype/duration_ms/usage/etc.
|
|
42
|
-
// but no structured_output key. Check for the `result` text field
|
|
43
|
-
// (model may have written review as text) and for error states.
|
|
44
|
-
if (dict.type === "result" || ("duration_ms" in dict && "session_id" in dict)) {
|
|
45
|
-
if (dict.is_error === true || (Array.isArray(dict.errors) && (dict.errors as unknown[]).length > 0)) {
|
|
46
|
-
logWarn("cli_parser", `CLI returned error result: ${JSON.stringify(dict.errors ?? "is_error=true")}`);
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
if (typeof dict.result === "string" && dict.result) {
|
|
50
|
-
logDebug("cli_parser", "Found result text in session envelope, attempting JSON extraction");
|
|
51
|
-
const extracted = parseJsonMaybe(dict.result as string, requireFields);
|
|
52
|
-
if (extracted) return extracted;
|
|
53
|
-
logWarn("cli_parser", "Session envelope result text contained no extractable JSON");
|
|
54
|
-
}
|
|
55
|
-
logDebug("cli_parser", "Session result envelope with no structured_output or extractable result");
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Strategy 2: Assistant message with StructuredOutput tool use
|
|
60
|
-
if (dict.type === "assistant") {
|
|
61
|
-
const message = dict.message as Record<string, unknown> | undefined;
|
|
62
|
-
const content = message?.content;
|
|
63
|
-
if (Array.isArray(content)) {
|
|
64
|
-
for (const item of content) {
|
|
65
|
-
if (
|
|
66
|
-
item !== null &&
|
|
67
|
-
typeof item === "object" &&
|
|
68
|
-
(item as Record<string, unknown>).name === "StructuredOutput"
|
|
69
|
-
) {
|
|
70
|
-
logDebug(
|
|
71
|
-
"cli_parser",
|
|
72
|
-
"Found StructuredOutput in assistant message content",
|
|
73
|
-
);
|
|
74
|
-
return (item as Record<string, unknown>).input as Record<
|
|
75
|
-
string,
|
|
76
|
-
unknown
|
|
77
|
-
>;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
logDebug(
|
|
83
|
-
"cli_parser",
|
|
84
|
-
"Assistant message found but no StructuredOutput tool use in content",
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
} else if (Array.isArray(result)) {
|
|
88
|
-
// Strategy 3: List of events with assistant messages
|
|
89
|
-
logDebug(
|
|
90
|
-
"cli_parser",
|
|
91
|
-
`Received list of ${(result as unknown[]).length} events, searching for assistant message`,
|
|
92
|
-
);
|
|
93
|
-
for (let i = 0; i < (result as unknown[]).length; i++) {
|
|
94
|
-
const event = (result as unknown[])[i];
|
|
95
|
-
if (event === null || typeof event !== "object") continue;
|
|
96
|
-
|
|
97
|
-
const dict = event as Record<string, unknown>;
|
|
98
|
-
if (dict.type === "assistant") {
|
|
99
|
-
const message = dict.message as Record<string, unknown> | undefined;
|
|
100
|
-
const content = message?.content;
|
|
101
|
-
if (Array.isArray(content)) {
|
|
102
|
-
for (const item of content) {
|
|
103
|
-
if (
|
|
104
|
-
item !== null &&
|
|
105
|
-
typeof item === "object" &&
|
|
106
|
-
(item as Record<string, unknown>).name === "StructuredOutput"
|
|
107
|
-
) {
|
|
108
|
-
logDebug(
|
|
109
|
-
"cli_parser",
|
|
110
|
-
`Found StructuredOutput in event[${i}] assistant message`,
|
|
111
|
-
);
|
|
112
|
-
return (item as Record<string, unknown>).input as Record<
|
|
113
|
-
string,
|
|
114
|
-
unknown
|
|
115
|
-
>;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
logDebug(
|
|
123
|
-
"cli_parser",
|
|
124
|
-
"No StructuredOutput found in any assistant message in event list",
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
} catch (error: unknown) {
|
|
128
|
-
if (error instanceof SyntaxError) {
|
|
129
|
-
logWarn("cli_parser", `JSON decode error: ${error.message}`);
|
|
130
|
-
} else {
|
|
131
|
-
logError(
|
|
132
|
-
"cli_parser",
|
|
133
|
-
`Unexpected error during structured parsing: ${error}`,
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Strategy 4: Heuristic {…} extraction fallback
|
|
139
|
-
logDebug(
|
|
140
|
-
"cli_parser",
|
|
141
|
-
"No structured output found, falling back to heuristic JSON extraction",
|
|
142
|
-
);
|
|
143
|
-
return parseJsonMaybe(raw, requireFields);
|
|
16
|
+
return parseStructuredOutput(raw, {
|
|
17
|
+
requireFields,
|
|
18
|
+
loggerTag: "cli_parser",
|
|
19
|
+
});
|
|
144
20
|
}
|
|
@@ -11,11 +11,16 @@ import { loadConfig, getDisplaySettings } from "./config.js";
|
|
|
11
11
|
import { DEFAULT_REVIEW_ITERATIONS } from "./state.js";
|
|
12
12
|
import type {
|
|
13
13
|
AgentConfig,
|
|
14
|
-
|
|
14
|
+
AgentReviewSettings,
|
|
15
|
+
AgentSelectionConfig,
|
|
16
|
+
LoadedSettings,
|
|
15
17
|
ModelsConfig,
|
|
18
|
+
PlanReviewSettings,
|
|
19
|
+
ProviderConfig,
|
|
16
20
|
} from "./types.js";
|
|
17
21
|
import { DEFAULT_DISPLAY, DEFAULT_SANITIZATION } from "./types.js";
|
|
18
22
|
import { logInfo } from "../../_shared/lib-ts/base/logger.js";
|
|
23
|
+
import { CODEX_MODELS } from "../../_shared/lib-ts/base/models.js";
|
|
19
24
|
|
|
20
25
|
const HOOK = "settings";
|
|
21
26
|
|
|
@@ -56,10 +61,10 @@ export const DEFAULT_AGENTS: Array<{ name: string; model: string; provider: stri
|
|
|
56
61
|
{ ...AGENT_DEFAULTS, name: "constraint-validator", focus: "constraint identification and satisfaction", categories: ALL_CATEGORIES },
|
|
57
62
|
];
|
|
58
63
|
|
|
59
|
-
export const DEFAULT_ORCHESTRATOR
|
|
64
|
+
export const DEFAULT_ORCHESTRATOR = { enabled: true, model: CODEX_MODELS.codex, provider: "codex", timeout: 60 } as const;
|
|
60
65
|
export const DEFAULT_AGENT_MODEL = "sonnet";
|
|
61
66
|
|
|
62
|
-
export const DEFAULT_AGENT_SELECTION:
|
|
67
|
+
export const DEFAULT_AGENT_SELECTION: AgentSelectionConfig = {
|
|
63
68
|
simple: { min: 3, max: 3 },
|
|
64
69
|
medium: { min: 5, max: 5 },
|
|
65
70
|
high: { min: 7, max: 7 },
|
|
@@ -71,7 +76,7 @@ export const DEFAULT_COMPLEXITY_CATEGORIES = ["code", "infrastructure", "documen
|
|
|
71
76
|
export const DEFAULT_MODELS_CONFIG: ModelsConfig = {
|
|
72
77
|
providers: {
|
|
73
78
|
claude: { enabled: false, models: ["sonnet"] },
|
|
74
|
-
codex: { enabled: true, models: [
|
|
79
|
+
codex: { enabled: true, models: [CODEX_MODELS.codex] },
|
|
75
80
|
},
|
|
76
81
|
};
|
|
77
82
|
|
|
@@ -79,58 +84,131 @@ export const DEFAULT_MODELS_CONFIG: ModelsConfig = {
|
|
|
79
84
|
// Settings Loading
|
|
80
85
|
// ---------------------------------------------------------------------------
|
|
81
86
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
agentDefaults: { model: DEFAULT_AGENT_MODEL },
|
|
101
|
-
complexityCategories: [...DEFAULT_COMPLEXITY_CATEGORIES],
|
|
102
|
-
sanitization: { ...DEFAULT_SANITIZATION },
|
|
87
|
+
function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
88
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
89
|
+
? value as Record<string, unknown>
|
|
90
|
+
: undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function asStringArray(value: unknown): string[] | undefined {
|
|
94
|
+
if (!Array.isArray(value)) return undefined;
|
|
95
|
+
const filtered = value.filter((item): item is string => typeof item === "string" && item.trim().length > 0);
|
|
96
|
+
return filtered.length > 0 ? filtered : undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function loadSettings(projDir: string): LoadedSettings {
|
|
100
|
+
const defaultPlan: PlanReviewSettings = {
|
|
101
|
+
enabled: true,
|
|
102
|
+
reviewers: {
|
|
103
|
+
codex: { enabled: true, model: "", timeout: 120 },
|
|
104
|
+
gemini: { enabled: false, model: "", timeout: 120 },
|
|
103
105
|
},
|
|
106
|
+
display: { ...DEFAULT_DISPLAY },
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const defaultAgent: AgentReviewSettings = {
|
|
110
|
+
enabled: true,
|
|
111
|
+
orchestrator: { ...DEFAULT_ORCHESTRATOR },
|
|
112
|
+
timeout: 180,
|
|
113
|
+
highIssueThreshold: 3,
|
|
114
|
+
legacyMode: false,
|
|
115
|
+
display: { ...DEFAULT_DISPLAY },
|
|
116
|
+
agentSelection: { ...DEFAULT_AGENT_SELECTION },
|
|
117
|
+
agentDefaults: { model: DEFAULT_AGENT_MODEL },
|
|
118
|
+
complexityCategories: [...DEFAULT_COMPLEXITY_CATEGORIES],
|
|
119
|
+
sanitization: { ...DEFAULT_SANITIZATION },
|
|
104
120
|
};
|
|
105
121
|
|
|
106
122
|
const config = loadConfig(projDir);
|
|
107
|
-
if (!config || Object.keys(config).length === 0)
|
|
123
|
+
if (!config || Object.keys(config).length === 0) {
|
|
124
|
+
return { planReview: defaultPlan, agentReview: defaultAgent, models: {} };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Cast raw config to access arbitrary keys from JSON
|
|
128
|
+
const raw = config as Record<string, unknown>;
|
|
108
129
|
|
|
109
130
|
// Merge planReview
|
|
110
|
-
const
|
|
111
|
-
const mergedPlan = { ...
|
|
112
|
-
if (
|
|
113
|
-
mergedPlan.reviewers = { ...
|
|
131
|
+
const planReviewRaw = (asRecord(raw.planReview) ?? {}) as Partial<PlanReviewSettings>;
|
|
132
|
+
const mergedPlan: PlanReviewSettings = { ...defaultPlan, ...planReviewRaw };
|
|
133
|
+
if (planReviewRaw.reviewers) {
|
|
134
|
+
mergedPlan.reviewers = { ...defaultPlan.reviewers, ...planReviewRaw.reviewers };
|
|
114
135
|
}
|
|
115
136
|
mergedPlan.display = getDisplaySettings(config, "planReview");
|
|
116
137
|
|
|
117
138
|
// Merge agentReview
|
|
118
|
-
const
|
|
119
|
-
const
|
|
139
|
+
const agentReviewRawRecord = asRecord(raw.agentReview);
|
|
140
|
+
const agentReviewRaw = (agentReviewRawRecord ?? {}) as Partial<AgentReviewSettings>;
|
|
141
|
+
const mergedAgent: AgentReviewSettings = { ...defaultAgent, ...agentReviewRaw };
|
|
120
142
|
if (!mergedAgent.orchestrator || typeof mergedAgent.orchestrator !== "object") {
|
|
121
143
|
mergedAgent.orchestrator = { ...DEFAULT_ORCHESTRATOR };
|
|
122
144
|
} else {
|
|
123
145
|
mergedAgent.orchestrator = { ...DEFAULT_ORCHESTRATOR, ...mergedAgent.orchestrator };
|
|
124
146
|
}
|
|
125
147
|
mergedAgent.display = getDisplaySettings(config, "agentReview");
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
mergedAgent.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
148
|
+
|
|
149
|
+
const nestedAgentSelection = asRecord(agentReviewRawRecord?.agentSelection) as AgentSelectionConfig | undefined;
|
|
150
|
+
const topLevelAgentSelection = asRecord(raw.agentSelection) as AgentSelectionConfig | undefined;
|
|
151
|
+
mergedAgent.agentSelection = {
|
|
152
|
+
...DEFAULT_AGENT_SELECTION,
|
|
153
|
+
...nestedAgentSelection,
|
|
154
|
+
...topLevelAgentSelection,
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const nestedAgentDefaults = asRecord(agentReviewRawRecord?.agentDefaults) as { model?: string } | undefined;
|
|
158
|
+
const topLevelAgentDefaults = asRecord(raw.agentDefaults) as { model?: string } | undefined;
|
|
159
|
+
mergedAgent.agentDefaults = {
|
|
160
|
+
model: DEFAULT_AGENT_MODEL,
|
|
161
|
+
...nestedAgentDefaults,
|
|
162
|
+
...topLevelAgentDefaults,
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const nestedComplexityCategories = asStringArray(agentReviewRawRecord?.complexityCategories);
|
|
166
|
+
const topLevelComplexityCategories = asStringArray(raw.complexityCategories);
|
|
167
|
+
mergedAgent.complexityCategories = topLevelComplexityCategories
|
|
168
|
+
?? nestedComplexityCategories
|
|
169
|
+
?? [...DEFAULT_COMPLEXITY_CATEGORIES];
|
|
170
|
+
|
|
171
|
+
const nestedSanitization = asRecord(agentReviewRawRecord?.sanitization);
|
|
172
|
+
const topLevelSanitization = asRecord(raw.sanitization);
|
|
173
|
+
mergedAgent.sanitization = {
|
|
174
|
+
...DEFAULT_SANITIZATION,
|
|
175
|
+
...nestedSanitization,
|
|
176
|
+
...topLevelSanitization,
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const nestedFallbackByComplexity = asRecord(agentReviewRawRecord?.fallbackByComplexity) as Record<string, number> | undefined;
|
|
180
|
+
const topLevelFallbackByComplexity = asRecord(raw.fallbackByComplexity) as Record<string, number> | undefined;
|
|
181
|
+
if (nestedFallbackByComplexity || topLevelFallbackByComplexity) {
|
|
182
|
+
mergedAgent.fallbackByComplexity = {
|
|
183
|
+
...(nestedFallbackByComplexity ?? {}),
|
|
184
|
+
...(topLevelFallbackByComplexity ?? {}),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const nestedMandatoryAgents = agentReviewRawRecord?.mandatoryAgents as AgentReviewSettings["mandatoryAgents"] | undefined;
|
|
189
|
+
const topLevelMandatoryAgents = raw.mandatoryAgents as AgentReviewSettings["mandatoryAgents"] | undefined;
|
|
190
|
+
if (nestedMandatoryAgents !== undefined || topLevelMandatoryAgents !== undefined) {
|
|
191
|
+
mergedAgent.mandatoryAgents = topLevelMandatoryAgents ?? nestedMandatoryAgents;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const nestedPreflight = asRecord(agentReviewRawRecord?.preflight);
|
|
195
|
+
const topLevelPreflight = asRecord(raw.preflight);
|
|
196
|
+
if (nestedPreflight || topLevelPreflight) {
|
|
197
|
+
mergedAgent.preflight = {
|
|
198
|
+
...(nestedPreflight ?? {}),
|
|
199
|
+
...(topLevelPreflight ?? {}),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const nestedReviewIterations = asRecord(agentReviewRawRecord?.reviewIterations) as Record<string, number> | undefined;
|
|
204
|
+
const topLevelReviewIterations = asRecord(raw.reviewIterations) as Record<string, number> | undefined;
|
|
205
|
+
mergedAgent.reviewIterations = {
|
|
206
|
+
...DEFAULT_REVIEW_ITERATIONS,
|
|
207
|
+
...(nestedReviewIterations ?? {}),
|
|
208
|
+
...(topLevelReviewIterations ?? {}),
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const modelsRaw = (raw.models ?? {}) as Record<string, unknown>;
|
|
134
212
|
return { planReview: mergedPlan, agentReview: mergedAgent, models: modelsRaw };
|
|
135
213
|
}
|
|
136
214
|
|
|
@@ -138,7 +216,7 @@ export function loadSettings(projDir: string): Record<string, unknown> {
|
|
|
138
216
|
// Models Config
|
|
139
217
|
// ---------------------------------------------------------------------------
|
|
140
218
|
|
|
141
|
-
export function loadModelsConfig(settings:
|
|
219
|
+
export function loadModelsConfig(settings: LoadedSettings): ModelsConfig {
|
|
142
220
|
const raw = settings.models as Record<string, unknown> | undefined;
|
|
143
221
|
if (!raw?.providers || typeof raw.providers !== "object") {
|
|
144
222
|
return DEFAULT_MODELS_CONFIG;
|
|
@@ -149,7 +227,6 @@ export function loadModelsConfig(settings: Record<string, unknown>): ModelsConfi
|
|
|
149
227
|
providers[name] = {
|
|
150
228
|
enabled: c.enabled !== false,
|
|
151
229
|
models: Array.isArray(c.models) ? (c.models as string[]).filter(Boolean) : [],
|
|
152
|
-
...(typeof c.reasoningEffort === "string" && { reasoningEffort: c.reasoningEffort }),
|
|
153
230
|
};
|
|
154
231
|
}
|
|
155
232
|
return { providers };
|
|
@@ -161,7 +238,7 @@ export function loadModelsConfig(settings: Record<string, unknown>): ModelsConfi
|
|
|
161
238
|
|
|
162
239
|
export function loadAgentLibrary(
|
|
163
240
|
projDir: string,
|
|
164
|
-
settings?:
|
|
241
|
+
settings?: AgentReviewSettings,
|
|
165
242
|
): AgentConfig[] {
|
|
166
243
|
const agentsData = aggregateAgents(path.join(projDir, "_cc-native", "plan-review", "agents", "plan-review"));
|
|
167
244
|
const defaultModel = settings?.agentDefaults?.model ?? DEFAULT_AGENT_MODEL;
|