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
|
@@ -5,12 +5,13 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import * as fs from "node:fs";
|
|
8
|
-
import * as path from "node:path";
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
|
|
10
|
+
import { validatePlanPath } from "./constants.js";
|
|
11
|
+
import type { IterationState, IterationEntry } from "./types.js";
|
|
9
12
|
import { atomicWrite } from "../../_shared/lib-ts/base/atomic-write.js";
|
|
10
13
|
import { logInfo, logWarn, logError } from "../../_shared/lib-ts/base/logger.js";
|
|
11
14
|
import { nowIso } from "../../_shared/lib-ts/base/utils.js";
|
|
12
|
-
import { validatePlanPath } from "./constants.js";
|
|
13
|
-
import type { IterationState, IterationEntry } from "./types.js";
|
|
14
15
|
|
|
15
16
|
// ---------------------------------------------------------------------------
|
|
16
17
|
// Constants
|
|
@@ -72,11 +73,11 @@ export function loadState(planPath: string): Record<string, unknown> | null {
|
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
return state;
|
|
75
|
-
} catch (
|
|
76
|
-
if (
|
|
77
|
-
logError("state", `SECURITY: Invalid plan path: ${
|
|
76
|
+
} catch (error: unknown) {
|
|
77
|
+
if (error instanceof Error && error.message.includes("Invalid plan path")) {
|
|
78
|
+
logError("state", `SECURITY: Invalid plan path: ${error}`);
|
|
78
79
|
} else {
|
|
79
|
-
logError("state", `Failed to load state: ${
|
|
80
|
+
logError("state", `Failed to load state: ${error}`);
|
|
80
81
|
}
|
|
81
82
|
return null;
|
|
82
83
|
}
|
|
@@ -109,11 +110,11 @@ export function saveStateToPlan(
|
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
return true;
|
|
112
|
-
} catch (
|
|
113
|
-
if (
|
|
114
|
-
logError("state", `SECURITY: Invalid plan path: ${
|
|
113
|
+
} catch (error: unknown) {
|
|
114
|
+
if (error instanceof Error && error.message.includes("Invalid plan path")) {
|
|
115
|
+
logError("state", `SECURITY: Invalid plan path: ${error}`);
|
|
115
116
|
} else {
|
|
116
|
-
logError("state", String(
|
|
117
|
+
logError("state", String(error));
|
|
117
118
|
}
|
|
118
119
|
return false;
|
|
119
120
|
}
|
|
@@ -131,12 +132,12 @@ export function deleteState(planPath: string): boolean {
|
|
|
131
132
|
logInfo("state", `Deleted state file: ${stateFile}`);
|
|
132
133
|
}
|
|
133
134
|
return true;
|
|
134
|
-
} catch (
|
|
135
|
-
if (
|
|
136
|
-
logError("state", `SECURITY: Invalid plan path in delete: ${
|
|
135
|
+
} catch (error: unknown) {
|
|
136
|
+
if (error instanceof Error && error.message.includes("Invalid plan path")) {
|
|
137
|
+
logError("state", `SECURITY: Invalid plan path in delete: ${error}`);
|
|
137
138
|
return false;
|
|
138
139
|
}
|
|
139
|
-
logWarn("state", `Failed to delete state file: ${
|
|
140
|
+
logWarn("state", `Failed to delete state file: ${error}`);
|
|
140
141
|
return false;
|
|
141
142
|
}
|
|
142
143
|
}
|
|
@@ -207,7 +208,7 @@ export function shouldContinueIterating(
|
|
|
207
208
|
verdict: string,
|
|
208
209
|
config?: Record<string, unknown>,
|
|
209
210
|
): boolean {
|
|
210
|
-
const current = iteration
|
|
211
|
+
const {current} = iteration;
|
|
211
212
|
const maxIter = iteration.max;
|
|
212
213
|
|
|
213
214
|
// At or past max iterations
|
|
@@ -65,7 +65,7 @@ export interface CorroborationResult {
|
|
|
65
65
|
blocking: CorroboratedGroup[];
|
|
66
66
|
solo: SoloFinding[];
|
|
67
67
|
unclassified: Array<{ agent: string; issue: ReviewIssue }>;
|
|
68
|
-
verdict: "pass" | "fail";
|
|
68
|
+
verdict: "pass" | "warn" | "fail";
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/** Normalized review data from any reviewer */
|
|
@@ -102,7 +102,6 @@ export interface OrchestratorResult {
|
|
|
102
102
|
export interface CombinedReviewResult {
|
|
103
103
|
plan_hash: string;
|
|
104
104
|
overall_verdict: Verdict;
|
|
105
|
-
cli_reviewers: Record<string, ReviewerResult>;
|
|
106
105
|
orchestration: OrchestratorResult | null;
|
|
107
106
|
agents: Record<string, ReviewerResult>;
|
|
108
107
|
timestamp: string;
|
|
@@ -194,8 +193,16 @@ export interface PlanReviewState {
|
|
|
194
193
|
|
|
195
194
|
/** Questions-asked tracking state */
|
|
196
195
|
export interface QuestionsAskedState {
|
|
197
|
-
asked: boolean;
|
|
196
|
+
asked: boolean; // Backward-compatible: true if either phase asked
|
|
198
197
|
asked_at: string;
|
|
198
|
+
early_questions_asked?: {
|
|
199
|
+
asked: boolean;
|
|
200
|
+
asked_at: string;
|
|
201
|
+
};
|
|
202
|
+
plan_questions_agent_asked?: {
|
|
203
|
+
asked: boolean;
|
|
204
|
+
asked_at: string;
|
|
205
|
+
};
|
|
199
206
|
}
|
|
200
207
|
|
|
201
208
|
/** Stuck detection state — tracks repeated errors, file edits, and test failures */
|
|
@@ -262,112 +269,10 @@ export interface Reviewer {
|
|
|
262
269
|
}
|
|
263
270
|
|
|
264
271
|
// ---------------------------------------------------------------------------
|
|
265
|
-
// JSON Schemas
|
|
266
|
-
// ---------------------------------------------------------------------------
|
|
267
|
-
|
|
268
|
-
/** JSON schema for review structured output */
|
|
269
|
-
export const REVIEW_SCHEMA: Record<string, unknown> = {
|
|
270
|
-
type: "object",
|
|
271
|
-
properties: {
|
|
272
|
-
verdict: { type: "string", enum: ["pass", "warn", "fail"] },
|
|
273
|
-
summary: { type: "string", minLength: 20 },
|
|
274
|
-
issues: {
|
|
275
|
-
type: "array",
|
|
276
|
-
items: {
|
|
277
|
-
type: "object",
|
|
278
|
-
properties: {
|
|
279
|
-
severity: { type: "string", enum: ["high", "medium", "low"] },
|
|
280
|
-
category: { type: "string" },
|
|
281
|
-
issue: { type: "string" },
|
|
282
|
-
suggested_fix: { type: "string" },
|
|
283
|
-
dimension: {
|
|
284
|
-
type: "string",
|
|
285
|
-
enum: [
|
|
286
|
-
"completeness", "simplicity", "security", "performance",
|
|
287
|
-
"reliability", "maintainability", "testability", "scope",
|
|
288
|
-
"feasibility", "clarity",
|
|
289
|
-
],
|
|
290
|
-
},
|
|
291
|
-
},
|
|
292
|
-
required: ["severity", "category", "issue", "suggested_fix"],
|
|
293
|
-
additionalProperties: false,
|
|
294
|
-
},
|
|
295
|
-
},
|
|
296
|
-
missing_sections: { type: "array", items: { type: "string" } },
|
|
297
|
-
questions: { type: "array", items: { type: "string" } },
|
|
298
|
-
},
|
|
299
|
-
required: ["verdict", "summary", "issues", "missing_sections", "questions"],
|
|
300
|
-
additionalProperties: false,
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
/** JSON schema for orchestrator structured output */
|
|
304
|
-
export const ORCHESTRATOR_SCHEMA: Record<string, unknown> = {
|
|
305
|
-
type: "object",
|
|
306
|
-
properties: {
|
|
307
|
-
complexity: { type: "string", enum: ["simple", "medium", "high"] },
|
|
308
|
-
category: {
|
|
309
|
-
type: "string",
|
|
310
|
-
enum: [
|
|
311
|
-
"code",
|
|
312
|
-
"infrastructure",
|
|
313
|
-
"documentation",
|
|
314
|
-
"life",
|
|
315
|
-
"business",
|
|
316
|
-
"design",
|
|
317
|
-
"research",
|
|
318
|
-
],
|
|
319
|
-
},
|
|
320
|
-
selectedAgents: { type: "array", items: { type: "string" } },
|
|
321
|
-
reasoning: { type: "string" },
|
|
322
|
-
skipReason: { type: "string" },
|
|
323
|
-
},
|
|
324
|
-
required: ["complexity", "category", "selectedAgents", "reasoning"],
|
|
325
|
-
additionalProperties: false,
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
// ---------------------------------------------------------------------------
|
|
329
|
-
// Prompt Constants
|
|
272
|
+
// JSON Schemas (moved to reviewers/schemas.ts)
|
|
330
273
|
// ---------------------------------------------------------------------------
|
|
331
|
-
|
|
332
|
-
export
|
|
333
|
-
|
|
334
|
-
Review the PLAN below. Focus on:
|
|
335
|
-
- missing steps, unclear assumptions, edge cases
|
|
336
|
-
- security/privacy concerns
|
|
337
|
-
- testing/rollout/rollback completeness
|
|
338
|
-
- operational concerns (observability, failure modes)
|
|
339
|
-
`;
|
|
340
|
-
|
|
341
|
-
export const AGENT_REVIEW_PROMPT_PREFIX = `# SINGLE-TURN PLAN REVIEW
|
|
342
|
-
|
|
343
|
-
## CRITICAL: ONE TURN ONLY
|
|
344
|
-
You have exactly ONE response to complete this review. Do NOT attempt multi-step workflows, context queries, or phased analysis. Analyze the plan and output your review immediately.
|
|
345
|
-
|
|
346
|
-
## YOUR TASK
|
|
347
|
-
Review the plan below from your area of expertise. Then call StructuredOutput with your assessment.
|
|
348
|
-
|
|
349
|
-
## REQUIRED OUTPUT (all fields must have content)
|
|
350
|
-
Call StructuredOutput with:
|
|
351
|
-
- **verdict**: "pass" (no concerns), "warn" (some concerns), or "fail" (critical issues)
|
|
352
|
-
- **summary**: 2-3 sentences with your overall assessment and key findings (REQUIRED)
|
|
353
|
-
- **issues**: Array of concerns found. Format each as:
|
|
354
|
-
{"severity": "high/medium/low", "category": "...", "issue": "...", "suggested_fix": "...", "dimension": "..."}
|
|
355
|
-
- **dimension**: Classify each issue into exactly one dimension:
|
|
356
|
-
completeness, simplicity, security, performance, reliability,
|
|
357
|
-
maintainability, testability, scope, feasibility, or clarity.
|
|
358
|
-
Examples: "missing error handling" → reliability, "excessive abstraction" → simplicity,
|
|
359
|
-
"no test strategy" → testability, "missing deployment steps" → completeness,
|
|
360
|
-
"unclear interaction between components" → clarity.
|
|
361
|
-
- **missing_sections**: Topics the plan should address but doesn't
|
|
362
|
-
- **questions**: Things that need clarification before implementation
|
|
363
|
-
|
|
364
|
-
## IMPORTANT RULES
|
|
365
|
-
1. A "warn" verdict MUST include at least one issue explaining why
|
|
366
|
-
2. Summary MUST explain your reasoning, not just "looks good" or empty
|
|
367
|
-
3. Focus on your expertise area (architecture, security, performance, etc.)
|
|
368
|
-
4. Output StructuredOutput NOW - no other tools, no questions, no delays
|
|
369
|
-
5. Return ONLY your top 3 most critical issues. Prioritize high-severity over medium/low. Quality over quantity.
|
|
370
|
-
`;
|
|
274
|
+
// Re-export for backwards compatibility
|
|
275
|
+
export { AGENT_REVIEW_PROMPT_PREFIX, ORCHESTRATOR_SCHEMA, REVIEW_PROMPT_PREFIX, REVIEW_SCHEMA } from "./reviewers/schemas.js";
|
|
371
276
|
|
|
372
277
|
// ---------------------------------------------------------------------------
|
|
373
278
|
// Display Defaults
|
|
@@ -52,13 +52,13 @@ export function computeReviewDecision(
|
|
|
52
52
|
);
|
|
53
53
|
|
|
54
54
|
if (signalVerdicts.length === 0) {
|
|
55
|
-
return { should_deny: false, reason: "no_signal", score: 0
|
|
55
|
+
return { should_deny: false, reason: "no_signal", score: 0 };
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
// Fail blocks unconditionally
|
|
59
59
|
const failCount = signalVerdicts.filter((v) => v === "fail").length;
|
|
60
60
|
if (failCount > 0) {
|
|
61
|
-
return { should_deny: true, reason: "fail_veto", score: 1
|
|
61
|
+
return { should_deny: true, reason: "fail_veto", score: 1 };
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// Warn also blocks — reviewers flagged concerns worth addressing
|
|
@@ -68,5 +68,5 @@ export function computeReviewDecision(
|
|
|
68
68
|
return { should_deny: true, reason: "warn_block", score: warnRatio };
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
return { should_deny: false, reason: "acceptable", score: 0
|
|
71
|
+
return { should_deny: false, reason: "acceptable", score: 0 };
|
|
72
72
|
}
|
|
@@ -13,18 +13,6 @@
|
|
|
13
13
|
},
|
|
14
14
|
"planReview": {
|
|
15
15
|
"enabled": true,
|
|
16
|
-
"reviewers": {
|
|
17
|
-
"codex": {
|
|
18
|
-
"enabled": true,
|
|
19
|
-
"model": "gpt-5.1-codex-mini",
|
|
20
|
-
"timeout": 120
|
|
21
|
-
},
|
|
22
|
-
"gemini": {
|
|
23
|
-
"enabled": false,
|
|
24
|
-
"model": "",
|
|
25
|
-
"timeout": 120
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
16
|
"display": {
|
|
29
17
|
"maxIssues": 12,
|
|
30
18
|
"maxMissingSections": 12,
|
|
@@ -53,8 +41,8 @@
|
|
|
53
41
|
},
|
|
54
42
|
"reviewIterations": {
|
|
55
43
|
"simple": 1,
|
|
56
|
-
"medium":
|
|
57
|
-
"high":
|
|
44
|
+
"medium": 3,
|
|
45
|
+
"high": 5
|
|
58
46
|
},
|
|
59
47
|
"highIssueThreshold": 2,
|
|
60
48
|
"earlyExitOnAllPass": true,
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiwcli",
|
|
3
3
|
"description": "AI Workflow CLI - Command-line interface for AI-powered workflows",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.12.1",
|
|
5
5
|
"author": "jofu-tofu",
|
|
6
6
|
"bin": {
|
|
7
7
|
"aiw": "bin/run.js"
|
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@eslint/compat": "^1",
|
|
24
|
-
"@oclif/prettier-config": "^0.2.1",
|
|
25
24
|
"@oclif/test": "^4",
|
|
26
25
|
"@types/chai": "^4",
|
|
27
26
|
"@types/mocha": "^10",
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* Plan context hook — handles question marking and advisory question nudges.
|
|
4
|
-
*
|
|
5
|
-
* Registered for two events:
|
|
6
|
-
* - PostToolUse: AskUserQuestion — marks that questions were asked this session.
|
|
7
|
-
* - PreToolUse: Task — nudges Plan subagent to ask questions first (advisory, never blocks).
|
|
8
|
-
*
|
|
9
|
-
* All enforcement is advisory: injects additionalContext to guide Claude toward asking
|
|
10
|
-
* non-obvious questions, but never blocks the tool call. Claude can proceed regardless.
|
|
11
|
-
*
|
|
12
|
-
* Fail-safe: Any error allows the action silently.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { getProjectRoot } from "../../_shared/lib-ts/base/constants.js";
|
|
16
|
-
import {
|
|
17
|
-
emitContext,
|
|
18
|
-
loadHookInput,
|
|
19
|
-
logDebug,
|
|
20
|
-
logDiagnostic,
|
|
21
|
-
logInfo,
|
|
22
|
-
runHook,
|
|
23
|
-
} from "../../_shared/lib-ts/base/hook-utils.js";
|
|
24
|
-
import { isInternalCall } from "../../_shared/lib-ts/base/subprocess-utils.js";
|
|
25
|
-
import { getEvaluationContextReminder } from "../../_shared/lib-ts/templates/plan-context.js";
|
|
26
|
-
import { markQuestionsAsked, wasQuestionsAsked } from "../lib-ts/cc-native-state.js";
|
|
27
|
-
|
|
28
|
-
const CONTEXT_REMINDER = getEvaluationContextReminder();
|
|
29
|
-
|
|
30
|
-
const TASK_ENFORCEMENT_CONTEXT =
|
|
31
|
-
"Before spawning a Plan agent, consider asking the user non-obvious questions " +
|
|
32
|
-
"via AskUserQuestion. Code exploration reveals WHAT exists — questions reveal WHAT MATTERS.\n\n" +
|
|
33
|
-
"Generate 5+ candidate questions across these categories, then keep only 3-4 where " +
|
|
34
|
-
"different answers would lead to meaningfully different plans:\n\n" +
|
|
35
|
-
"1. INTENT & SUCCESS CRITERIA — What does 'done well' look like? Are there multiple " +
|
|
36
|
-
"interpretations of this request? What's a 10 vs a 6?\n\n" +
|
|
37
|
-
"2. CONSTRAINTS & HISTORY — Has this been attempted before? Are there off-limits areas, " +
|
|
38
|
-
"performance requirements, or security considerations not visible in the code?\n\n" +
|
|
39
|
-
"3. TRADE-OFF PREFERENCES — Speed vs thoroughness? Minimal change vs clean architecture? " +
|
|
40
|
-
"Backward compatibility vs clean break?\n\n" +
|
|
41
|
-
"Frame each question with 2-3 concrete options so the user can choose rather than compose. " +
|
|
42
|
-
"Use AskUserQuestion with structured options — never ask questions as inline text.";
|
|
43
|
-
|
|
44
|
-
function isPlanTask(payload: Record<string, unknown>): boolean {
|
|
45
|
-
const toolInput = payload.tool_input;
|
|
46
|
-
if (!toolInput || typeof toolInput !== "object") return false;
|
|
47
|
-
return String((toolInput as Record<string, unknown>).subagent_type ?? "") === "Plan";
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function main(): void {
|
|
51
|
-
// Guard: skip for internal subprocess calls (prevents recursive hook execution)
|
|
52
|
-
if (isInternalCall()) return;
|
|
53
|
-
|
|
54
|
-
const payload = loadHookInput();
|
|
55
|
-
if (!payload) return;
|
|
56
|
-
|
|
57
|
-
const toolName = payload.tool_name;
|
|
58
|
-
const hookEvent = payload.hook_event_name ?? "unknown";
|
|
59
|
-
logDiagnostic(
|
|
60
|
-
"add_plan_context",
|
|
61
|
-
"receive",
|
|
62
|
-
`tool=${toolName}, event=${hookEvent}`,
|
|
63
|
-
{ inputs: { tool_name: toolName, hook_event: hookEvent } },
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
const projectRoot = getProjectRoot(payload.cwd);
|
|
67
|
-
|
|
68
|
-
// PostToolUse: AskUserQuestion — mark that questions were asked
|
|
69
|
-
if (toolName === "AskUserQuestion") {
|
|
70
|
-
const sessionId = String(payload.session_id ?? "");
|
|
71
|
-
if (sessionId) {
|
|
72
|
-
markQuestionsAsked(sessionId, projectRoot);
|
|
73
|
-
logInfo("add_plan_context", `Marked questions asked for session ${sessionId.slice(0, 8)}...`);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// PreToolUse: Task — nudge Plan subagent to ask questions first (advisory)
|
|
80
|
-
if (toolName === "Task") {
|
|
81
|
-
if (!isPlanTask(payload)) return; // Only gate Plan subagent spawns
|
|
82
|
-
|
|
83
|
-
const permissionMode = payload.permission_mode ?? "";
|
|
84
|
-
if (permissionMode !== "plan") return; // Only enforce during plan mode
|
|
85
|
-
|
|
86
|
-
const sessionId = payload.session_id;
|
|
87
|
-
if (!sessionId) {
|
|
88
|
-
logDebug("add_plan_context", "No session_id for Task gate, skipping enforcement");
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const sessionIdStr = String(sessionId);
|
|
93
|
-
|
|
94
|
-
if (wasQuestionsAsked(sessionIdStr, projectRoot)) {
|
|
95
|
-
logInfo("add_plan_context", "Questions asked, allowing Plan Task with eval context");
|
|
96
|
-
logDiagnostic(
|
|
97
|
-
"add_plan_context",
|
|
98
|
-
"decide",
|
|
99
|
-
"Questions asked, allowing Plan Task",
|
|
100
|
-
{ decision: "allow_with_context", reasoning: "was_questions_asked=True" },
|
|
101
|
-
);
|
|
102
|
-
emitContext(CONTEXT_REMINDER);
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Questions NOT asked: nudge toward asking questions (advisory only)
|
|
107
|
-
logInfo("add_plan_context", "Questions not asked - nudging Plan Task to ask first");
|
|
108
|
-
logDiagnostic(
|
|
109
|
-
"add_plan_context",
|
|
110
|
-
"decide",
|
|
111
|
-
"Questions not asked, nudging Plan Task",
|
|
112
|
-
{ decision: "nudge", reasoning: "was_questions_asked=False, advisory context" },
|
|
113
|
-
);
|
|
114
|
-
emitContext(TASK_ENFORCEMENT_CONTEXT);
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
runHook(main, "add_plan_context");
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Codex CLI plan reviewer.
|
|
3
|
-
* Runs Codex in non-interactive mode with read-only sandbox.
|
|
4
|
-
* See cc-native-plan-review-spec.md §4.11
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import * as fs from "node:fs";
|
|
8
|
-
import * as os from "node:os";
|
|
9
|
-
import * as path from "node:path";
|
|
10
|
-
import { logDebug, logInfo, logWarn, logError } from "../../../_shared/lib-ts/base/logger.js";
|
|
11
|
-
import { findExecutable, execFileAsync } from "../../../_shared/lib-ts/base/subprocess-utils.js";
|
|
12
|
-
import { parseJsonMaybe, coerceToReview } from "../json-parser.js";
|
|
13
|
-
import { REVIEW_PROMPT_PREFIX } from "../types.js";
|
|
14
|
-
import type { ReviewerResult, ReviewOptions } from "../types.js";
|
|
15
|
-
import { makeResult } from "./types.js";
|
|
16
|
-
import type { Reviewer } from "./types.js";
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Codex reviewer — runs codex exec --sandbox read-only.
|
|
20
|
-
*/
|
|
21
|
-
export class CodexReviewer implements Reviewer {
|
|
22
|
-
private settings: Record<string, unknown>;
|
|
23
|
-
|
|
24
|
-
constructor(settings: Record<string, unknown>) {
|
|
25
|
-
this.settings = settings;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async review(
|
|
29
|
-
plan: string,
|
|
30
|
-
schema: Record<string, unknown>,
|
|
31
|
-
options: ReviewOptions,
|
|
32
|
-
): Promise<ReviewerResult> {
|
|
33
|
-
return runCodexReview(plan, schema, this.settings);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Run Codex CLI to review the plan.
|
|
39
|
-
* Never throws — returns error ReviewerResult on failure.
|
|
40
|
-
*/
|
|
41
|
-
export async function runCodexReview(
|
|
42
|
-
plan: string,
|
|
43
|
-
schema: Record<string, unknown>,
|
|
44
|
-
settings: Record<string, unknown>,
|
|
45
|
-
): Promise<ReviewerResult> {
|
|
46
|
-
const codexSettings =
|
|
47
|
-
((settings.reviewers as Record<string, unknown> | undefined)?.codex as
|
|
48
|
-
| Record<string, unknown>
|
|
49
|
-
| undefined) ?? {};
|
|
50
|
-
const timeout = (codexSettings.timeout as number) ?? 120;
|
|
51
|
-
const model = (codexSettings.model as string) ?? "";
|
|
52
|
-
|
|
53
|
-
const codexPath = findExecutable("codex");
|
|
54
|
-
if (!codexPath) {
|
|
55
|
-
logWarn("codex", "CLI not found on PATH");
|
|
56
|
-
return makeResult("codex", false, "skip", {}, "", "codex CLI not found on PATH");
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
logDebug("codex", `Found CLI at: ${codexPath}`);
|
|
60
|
-
|
|
61
|
-
const prompt = `${REVIEW_PROMPT_PREFIX}
|
|
62
|
-
Return ONLY a JSON object that matches this JSON Schema:
|
|
63
|
-
${JSON.stringify(schema)}
|
|
64
|
-
|
|
65
|
-
PLAN:
|
|
66
|
-
<<<
|
|
67
|
-
${plan}
|
|
68
|
-
>>>
|
|
69
|
-
`;
|
|
70
|
-
|
|
71
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "codex-review-"));
|
|
72
|
-
|
|
73
|
-
try {
|
|
74
|
-
const schemaPath = path.join(tmpDir, "schema.json");
|
|
75
|
-
const outPath = path.join(tmpDir, "codex_review.json");
|
|
76
|
-
|
|
77
|
-
fs.writeFileSync(schemaPath, JSON.stringify(schema, null, 2), "utf-8");
|
|
78
|
-
|
|
79
|
-
const cmdArgs = ["exec", "--sandbox", "read-only"];
|
|
80
|
-
|
|
81
|
-
if (model) {
|
|
82
|
-
cmdArgs.push("--model", model);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
cmdArgs.push("--output-schema", schemaPath, "-o", outPath, "-");
|
|
86
|
-
|
|
87
|
-
logDebug("codex", `Running command: codex ${cmdArgs.join(" ")}`);
|
|
88
|
-
|
|
89
|
-
const result = await execFileAsync(codexPath, cmdArgs, {
|
|
90
|
-
input: prompt,
|
|
91
|
-
timeout: timeout * 1000,
|
|
92
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
93
|
-
shell: process.platform === "win32",
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
if (result.killed || result.signal === "SIGTERM") {
|
|
97
|
-
logWarn("codex", `TIMEOUT after ${timeout}s`);
|
|
98
|
-
return makeResult("codex", false, "error", {}, "", `codex timed out after ${timeout}s`);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (!result.stdout && !result.stderr && !fs.existsSync(outPath) && result.exitCode !== 0) {
|
|
102
|
-
logError("codex", `Process exited with code ${result.exitCode} and no output`);
|
|
103
|
-
return makeResult("codex", false, "error", {}, "", `codex failed to run (exit ${result.exitCode})`);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
logDebug("codex", `Exit code: ${result.exitCode}`);
|
|
107
|
-
|
|
108
|
-
let raw = "";
|
|
109
|
-
if (fs.existsSync(outPath)) {
|
|
110
|
-
raw = fs.readFileSync(outPath, "utf-8");
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const obj = parseJsonMaybe(raw) ?? parseJsonMaybe(result.stdout);
|
|
114
|
-
const [ok, verdict, norm] = coerceToReview(
|
|
115
|
-
obj,
|
|
116
|
-
"Retry or check CLI auth/config.",
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
const err = result.stderr.trim();
|
|
120
|
-
return makeResult("codex", ok, verdict, norm, raw || result.stdout, err);
|
|
121
|
-
} finally {
|
|
122
|
-
// Clean up temp directory
|
|
123
|
-
try {
|
|
124
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
125
|
-
} catch {
|
|
126
|
-
// Best effort cleanup
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Gemini CLI plan reviewer.
|
|
3
|
-
* Runs Gemini CLI in YOLO mode (auto-approve).
|
|
4
|
-
* See cc-native-plan-review-spec.md §4.12
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { logDebug, logInfo, logWarn, logError } from "../../../_shared/lib-ts/base/logger.js";
|
|
8
|
-
import { findExecutable, execFileAsync } from "../../../_shared/lib-ts/base/subprocess-utils.js";
|
|
9
|
-
import { parseJsonMaybe, coerceToReview } from "../json-parser.js";
|
|
10
|
-
import type { ReviewerResult, ReviewOptions } from "../types.js";
|
|
11
|
-
import { makeResult } from "./types.js";
|
|
12
|
-
import type { Reviewer } from "./types.js";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Gemini reviewer — runs gemini -y -p <instruction>.
|
|
16
|
-
*/
|
|
17
|
-
export class GeminiReviewer implements Reviewer {
|
|
18
|
-
private settings: Record<string, unknown>;
|
|
19
|
-
|
|
20
|
-
constructor(settings: Record<string, unknown>) {
|
|
21
|
-
this.settings = settings;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async review(
|
|
25
|
-
plan: string,
|
|
26
|
-
schema: Record<string, unknown>,
|
|
27
|
-
options: ReviewOptions,
|
|
28
|
-
): Promise<ReviewerResult> {
|
|
29
|
-
return runGeminiReview(plan, schema, this.settings);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Run Gemini CLI to review the plan.
|
|
35
|
-
* Never throws — returns error ReviewerResult on failure.
|
|
36
|
-
*/
|
|
37
|
-
export async function runGeminiReview(
|
|
38
|
-
plan: string,
|
|
39
|
-
schema: Record<string, unknown>,
|
|
40
|
-
settings: Record<string, unknown>,
|
|
41
|
-
): Promise<ReviewerResult> {
|
|
42
|
-
const geminiSettings =
|
|
43
|
-
((settings.reviewers as Record<string, unknown> | undefined)?.gemini as
|
|
44
|
-
| Record<string, unknown>
|
|
45
|
-
| undefined) ?? {};
|
|
46
|
-
const timeout = (geminiSettings.timeout as number) ?? 120;
|
|
47
|
-
const model = (geminiSettings.model as string) ?? "";
|
|
48
|
-
|
|
49
|
-
const geminiPath = findExecutable("gemini");
|
|
50
|
-
if (!geminiPath) {
|
|
51
|
-
logWarn("gemini", "CLI not found on PATH");
|
|
52
|
-
return makeResult("gemini", false, "skip", {}, "", "gemini CLI not found on PATH");
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
logDebug("gemini", `Found CLI at: ${geminiPath}`);
|
|
56
|
-
|
|
57
|
-
const instruction = `
|
|
58
|
-
|
|
59
|
-
Review the PLAN above as a senior staff software engineer. Focus on:
|
|
60
|
-
- missing steps, unclear assumptions, edge cases
|
|
61
|
-
- security/privacy concerns
|
|
62
|
-
- testing/rollout/rollback completeness
|
|
63
|
-
- operational concerns (observability, failure modes)
|
|
64
|
-
|
|
65
|
-
Return ONLY a JSON object that matches this JSON Schema (no markdown, no code fences):
|
|
66
|
-
${JSON.stringify(schema)}
|
|
67
|
-
`;
|
|
68
|
-
|
|
69
|
-
const cmdArgs = ["-y", "-p", instruction];
|
|
70
|
-
|
|
71
|
-
if (model) {
|
|
72
|
-
cmdArgs.push("--model", model);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
logDebug("gemini", "Running command: gemini -y -p <instruction>");
|
|
76
|
-
|
|
77
|
-
const result = await execFileAsync(geminiPath, cmdArgs, {
|
|
78
|
-
input: plan,
|
|
79
|
-
timeout: timeout * 1000,
|
|
80
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
81
|
-
shell: process.platform === "win32",
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
if (result.killed || result.signal === "SIGTERM") {
|
|
85
|
-
logWarn("gemini", `TIMEOUT after ${timeout}s`);
|
|
86
|
-
return makeResult("gemini", false, "error", {}, "", `gemini timed out after ${timeout}s`);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (!result.stdout && !result.stderr && result.exitCode !== 0) {
|
|
90
|
-
logError("gemini", `Process exited with code ${result.exitCode}`);
|
|
91
|
-
return makeResult("gemini", false, "error", {}, "", `gemini failed to run (exit ${result.exitCode})`);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
logDebug("gemini", `Exit code: ${result.exitCode}`);
|
|
95
|
-
|
|
96
|
-
const raw = result.stdout.trim();
|
|
97
|
-
const err = result.stderr.trim();
|
|
98
|
-
|
|
99
|
-
const obj = parseJsonMaybe(raw);
|
|
100
|
-
const [ok, verdict, norm] = coerceToReview(
|
|
101
|
-
obj,
|
|
102
|
-
"Retry or check CLI auth/config.",
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
return makeResult("gemini", ok, verdict, norm, raw, err);
|
|
106
|
-
}
|
|
107
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|