aiwcli 0.11.1 → 0.12.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/dist/commands/clear.d.ts +8 -0
- package/dist/commands/clear.js +86 -0
- package/dist/lib/bmad-installer.d.ts +2 -27
- package/dist/lib/bmad-installer.js +3 -43
- package/dist/lib/claude-settings-types.d.ts +2 -1
- package/dist/lib/env-compat.d.ts +0 -8
- package/dist/lib/env-compat.js +0 -12
- package/dist/lib/git/index.d.ts +0 -1
- package/dist/lib/gitignore-manager.d.ts +0 -2
- package/dist/lib/gitignore-manager.js +1 -1
- package/dist/lib/hooks-merger.d.ts +1 -15
- package/dist/lib/hooks-merger.js +1 -1
- package/dist/lib/index.d.ts +3 -7
- package/dist/lib/index.js +3 -11
- package/dist/lib/output.d.ts +2 -1
- package/dist/lib/settings-hierarchy.d.ts +1 -13
- package/dist/lib/settings-hierarchy.js +1 -1
- package/dist/lib/template-installer.d.ts +5 -9
- package/dist/lib/template-installer.js +3 -13
- package/dist/lib/template-linter.d.ts +3 -10
- package/dist/lib/template-linter.js +2 -2
- package/dist/lib/template-resolver.d.ts +6 -0
- package/dist/lib/template-resolver.js +10 -0
- package/dist/lib/template-settings-reconstructor.d.ts +1 -1
- package/dist/lib/template-settings-reconstructor.js +17 -24
- package/dist/lib/terminal.d.ts +3 -14
- package/dist/lib/terminal.js +0 -4
- package/dist/lib/version.d.ts +2 -11
- package/dist/lib/version.js +3 -3
- package/dist/lib/windsurf-hooks-merger.d.ts +1 -15
- package/dist/lib/windsurf-hooks-merger.js +1 -1
- package/dist/templates/_shared/.codex/workflows/handoff.md +1 -1
- package/dist/templates/_shared/.windsurf/workflows/handoff.md +1 -1
- package/dist/templates/_shared/hooks-ts/session_end.ts +75 -4
- package/dist/templates/_shared/hooks-ts/session_start.ts +11 -13
- package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +6 -8
- package/dist/templates/_shared/lib-ts/CLAUDE.md +56 -7
- package/dist/templates/_shared/lib-ts/base/hook-utils.ts +176 -29
- package/dist/templates/_shared/lib-ts/base/logger.ts +1 -1
- package/dist/templates/_shared/lib-ts/base/state-io.ts +11 -2
- package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +181 -165
- package/dist/templates/_shared/lib-ts/context/plan-manager.ts +14 -13
- package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +3 -2
- package/dist/templates/_shared/lib-ts/package.json +1 -2
- package/dist/templates/_shared/lib-ts/templates/plan-context.ts +27 -34
- package/dist/templates/_shared/lib-ts/types.ts +17 -2
- package/dist/templates/_shared/scripts/resume_handoff.ts +4 -4
- package/dist/templates/_shared/scripts/save_handoff.ts +7 -7
- package/dist/templates/_shared/scripts/status_line.ts +104 -71
- package/dist/templates/_shared/workflows/handoff.md +1 -1
- package/dist/templates/cc-native/.claude/settings.json +182 -175
- package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +23 -1
- package/dist/templates/cc-native/_cc-native/agents/plan-questions/PLAN-QUESTIONER.md +70 -0
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +6 -1
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +142 -111
- package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_subagent.ts +54 -0
- package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_write.ts +52 -0
- package/dist/templates/cc-native/_cc-native/hooks/mark_questions_asked.ts +53 -0
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +19 -19
- package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +6 -5
- package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +114 -83
- package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +107 -10
- package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +1 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/corroboration.ts +6 -2
- package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +0 -4
- package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +40 -219
- package/dist/templates/cc-native/_cc-native/lib-ts/plan-enhancement.ts +41 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/plan-questions.ts +102 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +26 -227
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/base/base-agent.ts +217 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +4 -2
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/claude-agent.ts +65 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/codex-agent.ts +185 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/gemini-agent.ts +39 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/orchestrator-claude-agent.ts +195 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/schemas.ts +201 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +2 -2
- package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +17 -16
- package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +13 -108
- package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +3 -3
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +2 -14
- package/oclif.manifest.json +1 -1
- package/package.json +1 -2
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +0 -119
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +0 -130
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +0 -107
- /package/dist/templates/cc-native/_cc-native/agents/{ARCH-EVOLUTION.md → plan-review/ARCH-EVOLUTION.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{ARCH-PATTERNS.md → plan-review/ARCH-PATTERNS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{ARCH-STRUCTURE.md → plan-review/ARCH-STRUCTURE.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{ASSUMPTION-TRACER.md → plan-review/ASSUMPTION-TRACER.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{CLARITY-AUDITOR.md → plan-review/CLARITY-AUDITOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{COMPLETENESS-FEASIBILITY.md → plan-review/COMPLETENESS-FEASIBILITY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{COMPLETENESS-GAPS.md → plan-review/COMPLETENESS-GAPS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{COMPLETENESS-ORDERING.md → plan-review/COMPLETENESS-ORDERING.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{CONSTRAINT-VALIDATOR.md → plan-review/CONSTRAINT-VALIDATOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{DESIGN-ADR-VALIDATOR.md → plan-review/DESIGN-ADR-VALIDATOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{DESIGN-SCALE-MATCHER.md → plan-review/DESIGN-SCALE-MATCHER.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{DEVILS-ADVOCATE.md → plan-review/DEVILS-ADVOCATE.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{DOCUMENTATION-PHILOSOPHY.md → plan-review/DOCUMENTATION-PHILOSOPHY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{HANDOFF-READINESS.md → plan-review/HANDOFF-READINESS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{HIDDEN-COMPLEXITY.md → plan-review/HIDDEN-COMPLEXITY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{INCREMENTAL-DELIVERY.md → plan-review/INCREMENTAL-DELIVERY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{RISK-DEPENDENCY.md → plan-review/RISK-DEPENDENCY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{RISK-FMEA.md → plan-review/RISK-FMEA.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{RISK-PREMORTEM.md → plan-review/RISK-PREMORTEM.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{RISK-REVERSIBILITY.md → plan-review/RISK-REVERSIBILITY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{SCOPE-BOUNDARY.md → plan-review/SCOPE-BOUNDARY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{SIMPLICITY-GUARDIAN.md → plan-review/SIMPLICITY-GUARDIAN.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{SKEPTIC.md → plan-review/SKEPTIC.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-BEHAVIOR-AUDITOR.md → plan-review/TESTDRIVEN-BEHAVIOR-AUDITOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-CHARACTERIZATION.md → plan-review/TESTDRIVEN-CHARACTERIZATION.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-FIRST-VALIDATOR.md → plan-review/TESTDRIVEN-FIRST-VALIDATOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-PYRAMID-ANALYZER.md → plan-review/TESTDRIVEN-PYRAMID-ANALYZER.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TRADEOFF-COSTS.md → plan-review/TRADEOFF-COSTS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TRADEOFF-STAKEHOLDERS.md → plan-review/TRADEOFF-STAKEHOLDERS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{VERIFY-COVERAGE.md → plan-review/VERIFY-COVERAGE.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{VERIFY-STRENGTH.md → plan-review/VERIFY-STRENGTH.md} +0 -0
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
/**
|
|
3
|
-
* UserPromptSubmit hook — injects
|
|
3
|
+
* UserPromptSubmit hook — injects post-exploration clarification prompt in plan mode.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* After explore agents finish examining the codebase, injects a system-reminder
|
|
6
|
+
* telling Claude to ask clarification questions via AskUserQuestion to narrow
|
|
7
|
+
* the approach before drafting the plan.
|
|
8
8
|
*
|
|
9
9
|
* Skips if questions were already asked this session.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { getProjectRoot } from "../../_shared/lib-ts/base/constants.js";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
13
|
+
import { loadHookInput, runHook, logDebug, logInfo, emitContext } from "../../_shared/lib-ts/base/hook-utils.js";
|
|
14
|
+
import { wasEarlyQuestionsAsked } from "../lib-ts/cc-native-state.js";
|
|
15
15
|
|
|
16
|
-
const PHASE_A_PROMPT = `## Plan Mode:
|
|
16
|
+
const PHASE_A_PROMPT = `## Plan Mode: Narrow the Approach After Exploration
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
After exploring the codebase, use AskUserQuestion — one call, 3-4 questions — before drafting the plan.
|
|
19
19
|
|
|
20
20
|
### Why This Matters
|
|
21
|
-
Once you
|
|
21
|
+
Once you've explored the codebase, you'll understand what exists — but not which direction the user prefers. That's a branch point: multiple viable approaches, and the user's priorities determine which is best. Questions asked after exploration have maximum steering value: they narrow your path before you commit to an implementation direction.
|
|
22
22
|
|
|
23
23
|
### What to Ask About
|
|
24
|
-
Only ask about
|
|
24
|
+
Only ask about decisions that exploration will surface but can't resolve — where human judgment is needed to choose between viable options:
|
|
25
25
|
|
|
26
|
-
- **
|
|
27
|
-
- **
|
|
28
|
-
- **
|
|
29
|
-
- **
|
|
26
|
+
- **Approach selection:** If exploration reveals 2-3 viable implementation paths, ask which the user prefers. Present each option with its trade-offs as concrete choices.
|
|
27
|
+
- **Scope boundaries:** What's in scope vs. out of scope for this change? Which areas of the codebase should be left untouched? How far should the change ripple?
|
|
28
|
+
- **Trade-off preferences:** Where exploration reveals tensions (simplicity vs. flexibility, speed vs. thoroughness, minimal change vs. full refactor), ask which side the user leans toward.
|
|
29
|
+
- **Success criteria beyond the literal ask:** What would make this a 10? What non-obvious quality matters most — performance, readability, extensibility, consistency with existing patterns?
|
|
30
30
|
|
|
31
31
|
### How to Select Questions
|
|
32
|
-
1. Generate 5+ candidate questions across the
|
|
33
|
-
2. For each, evaluate: "If they answered A vs B, would I
|
|
34
|
-
3. Keep the 3-4 where different answers lead to meaningfully different
|
|
32
|
+
1. Generate 5+ candidate questions across the categories above
|
|
33
|
+
2. For each, evaluate: "If they answered A vs B, would I take a different approach or write different code?" If no — discard it.
|
|
34
|
+
3. Keep the 3-4 where different answers lead to meaningfully different implementation strategies
|
|
35
35
|
4. Frame each with 2-3 concrete options so the user can react rather than generate from scratch`;
|
|
36
36
|
|
|
37
37
|
function main(): void {
|
|
@@ -49,8 +49,8 @@ function main(): void {
|
|
|
49
49
|
|
|
50
50
|
const projectRoot = getProjectRoot(payload.cwd);
|
|
51
51
|
|
|
52
|
-
if (
|
|
53
|
-
logDebug("plan_questions_early", "
|
|
52
|
+
if (wasEarlyQuestionsAsked(sessionId, projectRoot)) {
|
|
53
|
+
logDebug("plan_questions_early", "Early questions already asked, skipping Phase A prompt");
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import * as fs from "node:fs";
|
|
7
|
-
import * as path from "node:path";
|
|
8
|
-
|
|
7
|
+
import * as path from "node:path";
|
|
8
|
+
|
|
9
9
|
import type { AgentConfig } from "./types.js";
|
|
10
|
+
import { logDebug, logInfo, logWarn } from "../../_shared/lib-ts/base/logger.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Extract simple YAML frontmatter from markdown content.
|
|
@@ -47,7 +48,7 @@ export function extractFrontmatter(
|
|
|
47
48
|
value = value
|
|
48
49
|
.slice(1, -1)
|
|
49
50
|
.split(",")
|
|
50
|
-
.map((s) => s.trim().
|
|
51
|
+
.map((s) => s.trim().replaceAll(/^["']|["']$/g, ""))
|
|
51
52
|
.filter(Boolean);
|
|
52
53
|
}
|
|
53
54
|
// Handle booleans
|
|
@@ -114,8 +115,8 @@ export function aggregateAgents(agentsDir?: string): AgentConfig[] {
|
|
|
114
115
|
let content: string;
|
|
115
116
|
try {
|
|
116
117
|
content = fs.readFileSync(filePath, "utf-8");
|
|
117
|
-
} catch (
|
|
118
|
-
logWarn("aggregate", `Failed to read ${file}: ${
|
|
118
|
+
} catch (error: unknown) {
|
|
119
|
+
logWarn("aggregate", `Failed to read ${file}: ${error}`);
|
|
119
120
|
continue;
|
|
120
121
|
}
|
|
121
122
|
|
|
@@ -4,11 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import * as fs from "node:fs";
|
|
7
|
-
import * as path from "node:path";
|
|
8
|
-
|
|
9
|
-
import { logDebug, logWarn, logError } from "../../_shared/lib-ts/base/logger.js";
|
|
10
|
-
import { nowIso } from "../../_shared/lib-ts/base/utils.js";
|
|
11
|
-
import { sanitizeFilename } from "../../_shared/lib-ts/base/constants.js";
|
|
7
|
+
import * as path from "node:path";
|
|
8
|
+
|
|
12
9
|
import { ENABLE_ROBUST_PLAN_WRITES } from "./constants.js";
|
|
13
10
|
import type {
|
|
14
11
|
CombinedReviewResult,
|
|
@@ -17,6 +14,10 @@ import type {
|
|
|
17
14
|
CorroborationResult,
|
|
18
15
|
} from "./types.js";
|
|
19
16
|
import { DEFAULT_DISPLAY } from "./types.js";
|
|
17
|
+
import { atomicWrite } from "../../_shared/lib-ts/base/atomic-write.js";
|
|
18
|
+
import { sanitizeFilename } from "../../_shared/lib-ts/base/constants.js";
|
|
19
|
+
import { logDebug, logWarn, logError } from "../../_shared/lib-ts/base/logger.js";
|
|
20
|
+
import { nowIso } from "../../_shared/lib-ts/base/utils.js";
|
|
20
21
|
|
|
21
22
|
// ---------------------------------------------------------------------------
|
|
22
23
|
// Markdown Formatting
|
|
@@ -81,7 +82,7 @@ export function formatCombinedMarkdown(
|
|
|
81
82
|
if (corroboration.blocking.length > 0) {
|
|
82
83
|
lines.push("### Blocking Dimensions\n");
|
|
83
84
|
for (const group of corroboration.blocking) {
|
|
84
|
-
lines.push(`- **${group.dimension}**: ${group.issues.length} issues from ${group.agentCount} agents (threshold:
|
|
85
|
+
lines.push(`- **${group.dimension}**: ${group.issues.length} issues from ${group.agentCount} agents (threshold: ≥${group.threshold})`);
|
|
85
86
|
}
|
|
86
87
|
lines.push("");
|
|
87
88
|
}
|
|
@@ -99,22 +100,6 @@ export function formatCombinedMarkdown(
|
|
|
99
100
|
|
|
100
101
|
lines.push("---\n");
|
|
101
102
|
|
|
102
|
-
// CLI Reviewers section
|
|
103
|
-
if (Object.keys(result.cli_reviewers).length > 0) {
|
|
104
|
-
lines.push("## CLI Reviewers\n");
|
|
105
|
-
for (const [name, r] of Object.entries(result.cli_reviewers)) {
|
|
106
|
-
lines.push(`### ${titleCase(name)}\n`);
|
|
107
|
-
lines.push(`- verdict: \`${r.verdict}\``);
|
|
108
|
-
if (r.data && Object.keys(r.data).length > 0) {
|
|
109
|
-
appendSummaryLine(lines, r.data);
|
|
110
|
-
appendReviewDetails(lines, r.data, display);
|
|
111
|
-
} else if (r.err) {
|
|
112
|
-
lines.push(`- error: ${r.err}`);
|
|
113
|
-
}
|
|
114
|
-
lines.push("");
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
103
|
// Orchestration section
|
|
119
104
|
if (result.orchestration) {
|
|
120
105
|
lines.push("---\n");
|
|
@@ -170,10 +155,7 @@ export function buildInlineReviewSummary(
|
|
|
170
155
|
maxChars = 800,
|
|
171
156
|
corroboration?: CorroborationResult,
|
|
172
157
|
): string {
|
|
173
|
-
const allReviewers =
|
|
174
|
-
...Object.values(combined.cli_reviewers),
|
|
175
|
-
...Object.values(combined.agents),
|
|
176
|
-
];
|
|
158
|
+
const allReviewers = Object.values(combined.agents);
|
|
177
159
|
|
|
178
160
|
// Build set of blocking dimensions for annotation
|
|
179
161
|
const blockingDims = new Set(
|
|
@@ -257,10 +239,7 @@ export function extractTopIssuesText(
|
|
|
257
239
|
maxCount = 3,
|
|
258
240
|
severity = "high",
|
|
259
241
|
): string {
|
|
260
|
-
const allReviewers =
|
|
261
|
-
...Object.values(combined.cli_reviewers),
|
|
262
|
-
...Object.values(combined.agents),
|
|
263
|
-
];
|
|
242
|
+
const allReviewers = Object.values(combined.agents);
|
|
264
243
|
|
|
265
244
|
const issues: string[] = [];
|
|
266
245
|
for (const r of allReviewers) {
|
|
@@ -318,10 +297,7 @@ export function buildHighIssuesDocument(
|
|
|
318
297
|
|
|
319
298
|
// Fallback: no corroboration data — show all high-severity issues
|
|
320
299
|
const lines = ["# High-Severity Issues\n"];
|
|
321
|
-
const allReviewers =
|
|
322
|
-
...Object.values(combined.cli_reviewers),
|
|
323
|
-
...Object.values(combined.agents),
|
|
324
|
-
];
|
|
300
|
+
const allReviewers = Object.values(combined.agents);
|
|
325
301
|
|
|
326
302
|
let foundAny = false;
|
|
327
303
|
for (const r of allReviewers) {
|
|
@@ -406,11 +382,6 @@ export function generateReviewIndex(
|
|
|
406
382
|
"| [plan.md](./plan.md) | Plan snapshot at review time |",
|
|
407
383
|
);
|
|
408
384
|
|
|
409
|
-
for (const name of Object.keys(result.cli_reviewers)) {
|
|
410
|
-
lines.push(
|
|
411
|
-
`| [${name}.json](./reviewer-output/${name}.json) | ${titleCase(name)} reviewer output |`,
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
385
|
for (const name of Object.keys(result.agents)) {
|
|
415
386
|
const safeName = sanitizeFilename(name);
|
|
416
387
|
lines.push(
|
|
@@ -426,9 +397,6 @@ export function generateReviewIndex(
|
|
|
426
397
|
"|----------|---------|",
|
|
427
398
|
);
|
|
428
399
|
|
|
429
|
-
for (const [name, r] of Object.entries(result.cli_reviewers)) {
|
|
430
|
-
lines.push(`| ${titleCase(name)} | \`${r.verdict}\` |`);
|
|
431
|
-
}
|
|
432
400
|
for (const [name, r] of Object.entries(result.agents)) {
|
|
433
401
|
lines.push(`| ${name} | \`${r.verdict}\` |`);
|
|
434
402
|
}
|
|
@@ -457,26 +425,6 @@ export function buildCombinedJson(
|
|
|
457
425
|
},
|
|
458
426
|
};
|
|
459
427
|
|
|
460
|
-
// CLI reviewers
|
|
461
|
-
if (Object.keys(result.cli_reviewers).length > 0) {
|
|
462
|
-
const cliReviewers: Record<string, unknown> = {};
|
|
463
|
-
output.cliReviewers = cliReviewers;
|
|
464
|
-
for (const [name, r] of Object.entries(result.cli_reviewers)) {
|
|
465
|
-
cliReviewers[name] = {
|
|
466
|
-
verdict: r.verdict,
|
|
467
|
-
summary: r.data?.summary ?? null,
|
|
468
|
-
summarySource: r.data?.summary_source ?? null,
|
|
469
|
-
issues: r.data
|
|
470
|
-
? ((r.data.issues as Array<Record<string, unknown>>) ?? []).filter(
|
|
471
|
-
(i) => i.severity !== "low",
|
|
472
|
-
)
|
|
473
|
-
: [],
|
|
474
|
-
ok: r.ok,
|
|
475
|
-
error: r.err || null,
|
|
476
|
-
};
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
428
|
// Orchestration
|
|
481
429
|
if (result.orchestration) {
|
|
482
430
|
output.orchestration = {
|
|
@@ -543,9 +491,9 @@ export function writeCombinedArtifacts(
|
|
|
543
491
|
// Create directory
|
|
544
492
|
try {
|
|
545
493
|
fs.mkdirSync(outDir, { recursive: true });
|
|
546
|
-
} catch (
|
|
547
|
-
logError("utils", `Cannot create directory ${outDir}: ${
|
|
548
|
-
throw
|
|
494
|
+
} catch (error: unknown) {
|
|
495
|
+
logError("utils", `Cannot create directory ${outDir}: ${error}`);
|
|
496
|
+
throw error;
|
|
549
497
|
}
|
|
550
498
|
|
|
551
499
|
// JSON write
|
|
@@ -565,14 +513,6 @@ export function writeCombinedArtifacts(
|
|
|
565
513
|
} catch {
|
|
566
514
|
// Best-effort — non-critical
|
|
567
515
|
}
|
|
568
|
-
for (const [name, r] of Object.entries(result.cli_reviewers)) {
|
|
569
|
-
if (r.data) {
|
|
570
|
-
writeFileNonCritical(
|
|
571
|
-
path.join(reviewerOutputDir, `${name}.json`),
|
|
572
|
-
JSON.stringify(r.data, null, 2),
|
|
573
|
-
);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
516
|
for (const [name, r] of Object.entries(result.agents)) {
|
|
577
517
|
if (r.data) {
|
|
578
518
|
writeFileNonCritical(
|
|
@@ -658,9 +598,9 @@ function writeFile(filePath: string, content: string): void {
|
|
|
658
598
|
} else {
|
|
659
599
|
fs.writeFileSync(filePath, content, "utf-8");
|
|
660
600
|
}
|
|
661
|
-
} catch (
|
|
662
|
-
logError("utils", `Failed to write ${path.basename(filePath)}: ${
|
|
663
|
-
throw
|
|
601
|
+
} catch (error: unknown) {
|
|
602
|
+
logError("utils", `Failed to write ${path.basename(filePath)}: ${error}`);
|
|
603
|
+
throw error;
|
|
664
604
|
}
|
|
665
605
|
}
|
|
666
606
|
|
|
@@ -674,8 +614,8 @@ function writeFileNonCritical(filePath: string, content: string): void {
|
|
|
674
614
|
} else {
|
|
675
615
|
fs.writeFileSync(filePath, content, "utf-8");
|
|
676
616
|
}
|
|
677
|
-
} catch (
|
|
678
|
-
logWarn("utils", `Failed to write ${path.basename(filePath)}: ${
|
|
617
|
+
} catch (error: unknown) {
|
|
618
|
+
logWarn("utils", `Failed to write ${path.basename(filePath)}: ${error}`);
|
|
679
619
|
}
|
|
680
620
|
}
|
|
681
621
|
|
|
@@ -731,13 +671,13 @@ export function writeReviewTracker(
|
|
|
731
671
|
// Parse existing entries to detect plan changes
|
|
732
672
|
const previousHashes = extractPreviousHashes(existingContent);
|
|
733
673
|
const hashChanged = previousHashes.length > 0 &&
|
|
734
|
-
previousHashes
|
|
674
|
+
previousHashes.at(-1) !== entry.planHash;
|
|
735
675
|
|
|
736
676
|
// Build the new entry section
|
|
737
677
|
const lines: string[] = [];
|
|
738
|
-
const verdictEmoji = entry.decision === "allow" ? "\u2705" : "\
|
|
678
|
+
const verdictEmoji = entry.decision === "allow" ? "\u2705" : "\u274C";
|
|
739
679
|
const changeNote = previousHashes.length > 0
|
|
740
|
-
? (hashChanged ? "\u2705 Plan was revised (hash changed)" : "\
|
|
680
|
+
? (hashChanged ? "\u2705 Plan was revised (hash changed)" : "\u26A0\uFE0F Plan unchanged since last review")
|
|
741
681
|
: "Initial review";
|
|
742
682
|
|
|
743
683
|
lines.push(`## Iteration ${entry.iteration} \u2014 ${entry.timestamp} \u2014 ${verdictEmoji} ${entry.verdict.toUpperCase()}`);
|
|
@@ -776,8 +716,8 @@ export function writeReviewTracker(
|
|
|
776
716
|
|
|
777
717
|
try {
|
|
778
718
|
fs.writeFileSync(trackerPath, output, "utf-8");
|
|
779
|
-
} catch (
|
|
780
|
-
logWarn("artifacts", `Failed to write review tracker: ${
|
|
719
|
+
} catch (error) {
|
|
720
|
+
logWarn("artifacts", `Failed to write review tracker: ${error}`);
|
|
781
721
|
}
|
|
782
722
|
}
|
|
783
723
|
|
|
@@ -790,3 +730,94 @@ function extractPreviousHashes(content: string): string[] {
|
|
|
790
730
|
}
|
|
791
731
|
return hashes;
|
|
792
732
|
}
|
|
733
|
+
|
|
734
|
+
// ---------------------------------------------------------------------------
|
|
735
|
+
// Corroboration Report
|
|
736
|
+
// ---------------------------------------------------------------------------
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* Build a detailed markdown report of the corroboration analysis.
|
|
740
|
+
* Shows blocking vs solo findings with threshold comparison.
|
|
741
|
+
*/
|
|
742
|
+
export function buildCorroborationReport(
|
|
743
|
+
corroborationResult: CorroborationResult,
|
|
744
|
+
): string {
|
|
745
|
+
const lines: string[] = [
|
|
746
|
+
"# Corroboration Analysis",
|
|
747
|
+
"",
|
|
748
|
+
"## Verdict: " + corroborationResult.verdict.toUpperCase(),
|
|
749
|
+
"",
|
|
750
|
+
];
|
|
751
|
+
|
|
752
|
+
// Blocking groups table
|
|
753
|
+
if (corroborationResult.blocking.length > 0) {
|
|
754
|
+
lines.push("## Blocking Issues (Corroborated)");
|
|
755
|
+
lines.push("");
|
|
756
|
+
lines.push("| Dimension | Issues | Agents | Threshold | Status |");
|
|
757
|
+
lines.push("|-----------|--------|--------|-----------|--------|");
|
|
758
|
+
|
|
759
|
+
for (const group of corroborationResult.blocking) {
|
|
760
|
+
lines.push(
|
|
761
|
+
`| ${group.dimension} | ${group.issues.length} | ${group.agentCount} | ${group.threshold} | ⛔ EXCEEDED |`
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
lines.push("");
|
|
765
|
+
|
|
766
|
+
// Issue details
|
|
767
|
+
for (const group of corroborationResult.blocking) {
|
|
768
|
+
lines.push(`### ${group.dimension} (${group.issues.length} issues)`);
|
|
769
|
+
lines.push("");
|
|
770
|
+
for (const {agent, issue} of group.issues) {
|
|
771
|
+
lines.push(`- **[${agent}]** ${issue.description || issue.issue || "No description"}`);
|
|
772
|
+
}
|
|
773
|
+
lines.push("");
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// Solo findings table
|
|
778
|
+
if (corroborationResult.solo.length > 0) {
|
|
779
|
+
lines.push("## Solo Findings (Below Threshold)");
|
|
780
|
+
lines.push("");
|
|
781
|
+
lines.push("| Dimension | Issues | Agents | Threshold | Status |");
|
|
782
|
+
lines.push("|-----------|--------|--------|-----------|--------|");
|
|
783
|
+
|
|
784
|
+
for (const group of corroborationResult.solo) {
|
|
785
|
+
lines.push(
|
|
786
|
+
`| ${group.dimension} | ${group.issues.length} | ${group.agentCount} | ${group.threshold} | ℹ️ SOLO |`
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
lines.push("");
|
|
790
|
+
|
|
791
|
+
// Issue details
|
|
792
|
+
for (const group of corroborationResult.solo) {
|
|
793
|
+
lines.push(`### ${group.dimension} (${group.issues.length} issues)`);
|
|
794
|
+
lines.push("");
|
|
795
|
+
for (const {agent, issue} of group.issues) {
|
|
796
|
+
lines.push(`- **[${agent}]** ${issue.description || issue.issue || "No description"}`);
|
|
797
|
+
}
|
|
798
|
+
lines.push("");
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// Unclassified issues
|
|
803
|
+
if (corroborationResult.unclassified.length > 0) {
|
|
804
|
+
lines.push("## Unclassified Issues (No Dimension)");
|
|
805
|
+
lines.push("");
|
|
806
|
+
for (const {agent, issue} of corroborationResult.unclassified) {
|
|
807
|
+
lines.push(`- **[${agent}]** ${issue.description || issue.issue || "No description"}`);
|
|
808
|
+
}
|
|
809
|
+
lines.push("");
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// Summary
|
|
813
|
+
lines.push("## Summary");
|
|
814
|
+
lines.push("");
|
|
815
|
+
lines.push(`- **Blocking groups**: ${corroborationResult.blocking.length}`);
|
|
816
|
+
lines.push(`- **Solo findings**: ${corroborationResult.solo.length}`);
|
|
817
|
+
lines.push(`- **Unclassified**: ${corroborationResult.unclassified.length}`);
|
|
818
|
+
lines.push(`- **Final verdict**: ${corroborationResult.verdict}`);
|
|
819
|
+
lines.push("");
|
|
820
|
+
lines.push("**Threshold rule**: Issues in a dimension block when count ≥ 2× distinct agents in that dimension.");
|
|
821
|
+
|
|
822
|
+
return lines.join("\n");
|
|
823
|
+
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CC-native state accessor for context state.json.
|
|
3
|
-
* Deduplicates state access patterns
|
|
3
|
+
* Deduplicates state access patterns from utils.py and suggest-fresh-perspective.py.
|
|
4
4
|
* See cc-native-plan-review-spec.md §4.5
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type {
|
|
8
8
|
CcNativeState,
|
|
9
|
-
IterationState,
|
|
10
9
|
PlanReviewState,
|
|
11
|
-
QuestionsAskedState
|
|
10
|
+
QuestionsAskedState,
|
|
11
|
+
IterationState,
|
|
12
|
+
StuckDetectionState,
|
|
12
13
|
} from "./types.js";
|
|
13
14
|
import { logInfo, logWarn } from "../../_shared/lib-ts/base/logger.js";
|
|
14
15
|
import { nowIso } from "../../_shared/lib-ts/base/utils.js";
|
|
@@ -42,7 +43,6 @@ export function getCcNativeState(
|
|
|
42
43
|
} catch {
|
|
43
44
|
// Fail-safe: return null
|
|
44
45
|
}
|
|
45
|
-
|
|
46
46
|
return null;
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -65,7 +65,6 @@ export function saveCcNativeState(
|
|
|
65
65
|
} catch (error: unknown) {
|
|
66
66
|
logWarn("utils", `Failed to save cc_native state: ${error}`);
|
|
67
67
|
}
|
|
68
|
-
|
|
69
68
|
return false;
|
|
70
69
|
}
|
|
71
70
|
|
|
@@ -104,6 +103,22 @@ export function wasPlanPreviouslyDenied(
|
|
|
104
103
|
return decision === "deny" || decision.startsWith("hook_deny");
|
|
105
104
|
}
|
|
106
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Get the last plan review state for this session.
|
|
108
|
+
* Returns null if no review state exists or plan hash doesn't match.
|
|
109
|
+
*/
|
|
110
|
+
export function getLastPlanReview(
|
|
111
|
+
sessionId: string,
|
|
112
|
+
planHash: string,
|
|
113
|
+
projectRoot: string,
|
|
114
|
+
): PlanReviewState | null {
|
|
115
|
+
const ccNative = getCcNativeState(sessionId, projectRoot);
|
|
116
|
+
if (!ccNative) return null;
|
|
117
|
+
const reviewState = ccNative.plan_review;
|
|
118
|
+
if (reviewState?.plan_hash !== planHash) return null;
|
|
119
|
+
return reviewState;
|
|
120
|
+
}
|
|
121
|
+
|
|
107
122
|
/**
|
|
108
123
|
* Mark this plan as reviewed (stores hash and decision in state.json).
|
|
109
124
|
*/
|
|
@@ -175,20 +190,69 @@ export function wasQuestionsAsked(
|
|
|
175
190
|
}
|
|
176
191
|
|
|
177
192
|
/**
|
|
178
|
-
*
|
|
193
|
+
* Check if early questions (Phase A: AskUserQuestion) were asked this session.
|
|
194
|
+
* Returns false on any error (fail-safe: allow feature to work).
|
|
195
|
+
*/
|
|
196
|
+
export function wasEarlyQuestionsAsked(
|
|
197
|
+
sessionId: string,
|
|
198
|
+
projectRoot: string,
|
|
199
|
+
): boolean {
|
|
200
|
+
const ccNative = getCcNativeState(sessionId, projectRoot);
|
|
201
|
+
if (!ccNative) return false;
|
|
202
|
+
return ccNative.questions_asked?.early_questions_asked?.asked === true;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Check if plan questions agent (Phase B: independent question agent) ran this session.
|
|
207
|
+
* Returns false on any error (fail-safe: allow feature to work).
|
|
208
|
+
*/
|
|
209
|
+
export function wasPlanQuestionsAgentAsked(
|
|
210
|
+
sessionId: string,
|
|
211
|
+
projectRoot: string,
|
|
212
|
+
): boolean {
|
|
213
|
+
const ccNative = getCcNativeState(sessionId, projectRoot);
|
|
214
|
+
if (!ccNative) return false;
|
|
215
|
+
return ccNative.questions_asked?.plan_questions_agent_asked?.asked === true;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Mark that questions were asked in a specific phase. Returns true on success.
|
|
179
220
|
* Only stores timestamp, no user data.
|
|
221
|
+
* @param phase - 'early' for Phase A (AskUserQuestion), 'agent' for Phase B (independent question agent)
|
|
180
222
|
*/
|
|
181
223
|
export function markQuestionsAsked(
|
|
182
224
|
sessionId: string,
|
|
183
225
|
projectRoot: string,
|
|
226
|
+
phase: "early" | "agent",
|
|
184
227
|
): boolean {
|
|
185
228
|
try {
|
|
186
229
|
const ccNative = getCcNativeState(sessionId, projectRoot) ?? {};
|
|
230
|
+
const timestamp = nowIso();
|
|
187
231
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
232
|
+
// Initialize questions_asked if it doesn't exist
|
|
233
|
+
if (!ccNative.questions_asked) {
|
|
234
|
+
ccNative.questions_asked = {
|
|
235
|
+
asked: false,
|
|
236
|
+
asked_at: "",
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Mark phase-specific gate
|
|
241
|
+
if (phase === "early") {
|
|
242
|
+
ccNative.questions_asked.early_questions_asked = {
|
|
243
|
+
asked: true,
|
|
244
|
+
asked_at: timestamp,
|
|
245
|
+
};
|
|
246
|
+
} else {
|
|
247
|
+
ccNative.questions_asked.plan_questions_agent_asked = {
|
|
248
|
+
asked: true,
|
|
249
|
+
asked_at: timestamp,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Update backward-compatible fields
|
|
254
|
+
ccNative.questions_asked.asked = true;
|
|
255
|
+
ccNative.questions_asked.asked_at = timestamp;
|
|
192
256
|
|
|
193
257
|
return saveCcNativeState(sessionId, projectRoot, ccNative);
|
|
194
258
|
} catch (error: unknown) {
|
|
@@ -197,3 +261,36 @@ export function markQuestionsAsked(
|
|
|
197
261
|
}
|
|
198
262
|
}
|
|
199
263
|
|
|
264
|
+
// ---------------------------------------------------------------------------
|
|
265
|
+
// Stuck Detection State
|
|
266
|
+
// ---------------------------------------------------------------------------
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Get stuck detection state from cc_native.
|
|
270
|
+
*/
|
|
271
|
+
export function getStuckDetectionState(
|
|
272
|
+
sessionId: string,
|
|
273
|
+
projectRoot: string,
|
|
274
|
+
): StuckDetectionState | null {
|
|
275
|
+
const ccNative = getCcNativeState(sessionId, projectRoot);
|
|
276
|
+
return ccNative?.stuck_detection ?? null;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Update stuck detection state.
|
|
281
|
+
*/
|
|
282
|
+
export function updateStuckDetectionState(
|
|
283
|
+
sessionId: string,
|
|
284
|
+
projectRoot: string,
|
|
285
|
+
stuckState: StuckDetectionState,
|
|
286
|
+
): boolean {
|
|
287
|
+
try {
|
|
288
|
+
const ccNative = getCcNativeState(sessionId, projectRoot) ?? {};
|
|
289
|
+
ccNative.stuck_detection = stuckState;
|
|
290
|
+
return saveCcNativeState(sessionId, projectRoot, ccNative);
|
|
291
|
+
} catch (error: unknown) {
|
|
292
|
+
logWarn("utils", `Failed to update stuck detection state: ${error}`);
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
@@ -89,7 +89,7 @@ export function computeCorroboratedDecision(
|
|
|
89
89
|
const agentCount = group.agentNames.size;
|
|
90
90
|
const threshold = 2 * agentCount;
|
|
91
91
|
|
|
92
|
-
if (group.issues.length
|
|
92
|
+
if (group.issues.length >= threshold) {
|
|
93
93
|
blocking.push({
|
|
94
94
|
dimension,
|
|
95
95
|
issues: group.issues,
|
|
@@ -110,6 +110,10 @@ export function computeCorroboratedDecision(
|
|
|
110
110
|
blocking,
|
|
111
111
|
solo,
|
|
112
112
|
unclassified,
|
|
113
|
-
verdict: blocking.length > 0
|
|
113
|
+
verdict: blocking.length > 0
|
|
114
|
+
? "fail"
|
|
115
|
+
: solo.length > 0
|
|
116
|
+
? "warn"
|
|
117
|
+
: "pass",
|
|
114
118
|
};
|
|
115
119
|
}
|
|
@@ -64,11 +64,7 @@ export { buildOrchestratorSchema, runOrchestrator } from "./orchestrator.js";
|
|
|
64
64
|
// Reviewers
|
|
65
65
|
export {
|
|
66
66
|
AgentReviewer,
|
|
67
|
-
CodexReviewer,
|
|
68
|
-
GeminiReviewer,
|
|
69
67
|
runAgentReview,
|
|
70
|
-
runCodexReview,
|
|
71
|
-
runGeminiReview,
|
|
72
68
|
} from "./reviewers/index.js";
|
|
73
69
|
|
|
74
70
|
// Iteration state
|