cclaw-cli 0.51.5 → 0.51.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ export declare function startFlowScript(): string;
1
2
  export declare function stageCompleteScript(): string;
2
3
  export declare function runHookCmdScript(): string;
3
4
  export { claudeHooksJsonWithObservation as claudeHooksJson } from "./observe.js";
@@ -16,6 +16,128 @@ function resolveCliEntrypointForGeneratedHook() {
16
16
  }
17
17
  return null;
18
18
  }
19
+ function internalHelperScript(helperName, internalSubcommand, usage) {
20
+ const cliEntrypoint = resolveCliEntrypointForGeneratedHook();
21
+ return `#!/usr/bin/env node
22
+ import fs from "node:fs/promises";
23
+ import path from "node:path";
24
+ import process from "node:process";
25
+ import { spawn } from "node:child_process";
26
+
27
+ const RUNTIME_ROOT = ${JSON.stringify(RUNTIME_ROOT)};
28
+ const CCLAW_CLI_ENTRYPOINT = ${JSON.stringify(cliEntrypoint)};
29
+ const HELPER_NAME = ${JSON.stringify(helperName)};
30
+ const INTERNAL_SUBCOMMAND = ${JSON.stringify(internalSubcommand)};
31
+ const USAGE = ${JSON.stringify(usage)};
32
+
33
+ async function detectRoot() {
34
+ const candidates = [
35
+ process.env.CCLAW_PROJECT_ROOT,
36
+ process.env.CLAUDE_PROJECT_DIR,
37
+ process.env.CURSOR_PROJECT_DIR,
38
+ process.env.CURSOR_PROJECT_ROOT,
39
+ process.env.OPENCODE_PROJECT_DIR,
40
+ process.env.OPENCODE_PROJECT_ROOT,
41
+ process.cwd()
42
+ ].filter((value) => typeof value === "string" && value.length > 0);
43
+
44
+ for (const candidate of candidates) {
45
+ try {
46
+ const runtimePath = path.join(candidate, RUNTIME_ROOT);
47
+ const stat = await fs.stat(runtimePath);
48
+ if (stat.isDirectory()) return candidate;
49
+ } catch {
50
+ // continue
51
+ }
52
+ }
53
+ return candidates[0] || process.cwd();
54
+ }
55
+
56
+ function printUsage() {
57
+ process.stderr.write(USAGE + "\\n");
58
+ }
59
+
60
+ async function main() {
61
+ const [, , ...flags] = process.argv;
62
+ if (flags.includes("--help") || flags.includes("-h")) {
63
+ printUsage();
64
+ return;
65
+ }
66
+
67
+ const root = await detectRoot();
68
+ const runtimePath = path.join(root, RUNTIME_ROOT);
69
+ try {
70
+ const stat = await fs.stat(runtimePath);
71
+ if (!stat.isDirectory()) throw new Error("not-dir");
72
+ } catch {
73
+ process.stderr.write("[cclaw] " + HELPER_NAME + ": runtime root not found at " + runtimePath + "\\n");
74
+ process.exitCode = 1;
75
+ return;
76
+ }
77
+
78
+ const cliEntrypoint = process.env.CCLAW_CLI_JS || CCLAW_CLI_ENTRYPOINT;
79
+ if (!cliEntrypoint || cliEntrypoint.trim().length === 0) {
80
+ process.stderr.write(
81
+ "[cclaw] " + HELPER_NAME + ": local Node runtime entrypoint is missing. Re-run npx cclaw-cli sync or npx cclaw-cli upgrade to regenerate hooks.\\n"
82
+ );
83
+ process.exitCode = 1;
84
+ return;
85
+ }
86
+
87
+ try {
88
+ const stat = await fs.stat(cliEntrypoint);
89
+ if (!stat.isFile()) throw new Error("not-file");
90
+ } catch {
91
+ process.stderr.write(
92
+ "[cclaw] " + HELPER_NAME + ": local Node runtime entrypoint not found at " + cliEntrypoint + ". Re-run npx cclaw-cli sync or npx cclaw-cli upgrade to regenerate hooks.\\n"
93
+ );
94
+ process.exitCode = 1;
95
+ return;
96
+ }
97
+
98
+ const child = spawn(process.execPath, [cliEntrypoint, "internal", INTERNAL_SUBCOMMAND, ...flags], {
99
+ cwd: root,
100
+ env: process.env,
101
+ stdio: "inherit"
102
+ });
103
+ let spawnErrored = false;
104
+
105
+ child.on("error", (error) => {
106
+ spawnErrored = true;
107
+ const code = error && typeof error === "object" && "code" in error ? String(error.code) : "";
108
+ if (code === "ENOENT") {
109
+ process.stderr.write(
110
+ "[cclaw] " + HELPER_NAME + ": node executable not found while invoking local runtime. Re-run npx cclaw-cli doctor.\\n"
111
+ );
112
+ } else {
113
+ process.stderr.write(
114
+ "[cclaw] " + HELPER_NAME + ": failed to invoke local Node runtime (" +
115
+ (error instanceof Error ? error.message : String(error)) +
116
+ ").\\n"
117
+ );
118
+ }
119
+ process.exitCode = 1;
120
+ });
121
+
122
+ child.on("close", (code, signal) => {
123
+ if (spawnErrored) {
124
+ process.exitCode = 1;
125
+ return;
126
+ }
127
+ if (signal) {
128
+ process.exitCode = 1;
129
+ return;
130
+ }
131
+ process.exitCode = typeof code === "number" && code >= 0 ? code : 1;
132
+ });
133
+ }
134
+
135
+ void main();
136
+ `;
137
+ }
138
+ export function startFlowScript() {
139
+ return internalHelperScript("start-flow", "start-flow", "Usage: node " + RUNTIME_ROOT + "/hooks/start-flow.mjs --track=<standard|medium|quick> [--class=...] [--prompt=...] [--stack=...] [--reason=...] [--reclassify] [--force-reset]");
140
+ }
19
141
  export function stageCompleteScript() {
20
142
  const cliEntrypoint = resolveCliEntrypointForGeneratedHook();
21
143
  return `#!/usr/bin/env node
@@ -45,37 +45,34 @@ export const SCOPE = {
45
45
  },
46
46
  executionModel: {
47
47
  checklist: [
48
- "**Default path first** — read brainstorm, challenge premise, recommend one mode, draft 3-5 key in/out boundaries plus deferred items, then seek approval.",
49
- "**Optional audits by trigger** — run the pre-scope system audit only when configured; use deep-mode prime directives, dream-state mapping, and temporal interrogation only for complex/high-risk scope.",
50
- "**Premise and leverage check** — test whether this is the right problem, what happens if nothing changes, and what existing code can be reused.",
51
- "**Calibrate ambition** — for EXPAND/SELECTIVE candidates, do a brief landscape scan and align the quality bar to 2-3 strong in-repo modules.",
52
- "**Compare implementation alternatives** — give 2-3 distinct options with effort, risk, pros/cons, and explicit reuse; include minimal viable and ideal architecture options.",
53
- "**Select scope mode explicitly** — present expand/selective/hold/reduce with a recommendation and default heuristic justification.",
54
- "**Run mode-specific analysis** — expand, selective, hold, or reduce according to the selected mode; do not silently add or trim scope.",
55
- "**Handle deferred upside** — optionally park high-upside deferred/out-of-scope ideas in `.cclaw/seeds/`.",
56
- `**Outside voice when warranted** — run/reconcile the loop for complex/high-risk or configured scope; otherwise do a concise adversarial self-check. ${reviewLoopPolicySummary("scope")} ${reviewLoopSecondOpinionSummary("scope")}`,
57
- "**Write the scope contract** — include in-scope/out-of-scope, discretion areas, deferred items, locked decisions, error/rescue notes, completion dashboard, and explicit approval."
48
+ "**CEO pass first** — read brainstorm, name the job-to-be-done, challenge whether this is the right product slice, and propose the highest-leverage scope in one pass.",
49
+ "**Pick one of four gstack modes** — SCOPE EXPANSION, SELECTIVE EXPANSION, HOLD SCOPE, or SCOPE REDUCTION. State why and what user signal would change the mode.",
50
+ "**Draft the 10-star vs v1 boundary** — show what would make the product meaningfully better, then explicitly choose what ships now, what is deferred, and what is excluded.",
51
+ "**Premise and leverage check** — test right problem, direct path, no-action outcome, existing-code leverage, and reversibility before asking the user anything.",
52
+ "**Compare implementation alternatives** — include minimum viable, product-grade, and ideal architecture options with effort/risk/reuse, then recommend one.",
53
+ "**Run outside voice before final approval** — always do at least a concise adversarial self-check for scope; for complex/high-risk/configured scope, iterate until threshold. Record the loop summary in `## Spec Review Loop` before asking approval.",
54
+ "**Ask only one decision-changing question** — if the user rejects the contract but is unsure, offer 3-4 concrete scope moves instead of open-ended interrogation.",
55
+ "**Write the scope contract** — include in-scope/out-of-scope, discretion areas, deferred items, locked decisions, error/rescue notes, completion dashboard, scope summary, and explicit approval."
58
56
  ],
59
57
  interactionProtocol: [
60
58
  decisionProtocolInstruction("scope mode selection", "present expand/selective/hold/reduce as labeled options with trade-offs and mark one as (recommended)", "recommend the option that best covers the prime-directive failure modes, four data-flow paths, observability, and deferred handling for the in-scope set with the smallest blast radius. Base your recommendation on default heuristics: greenfield -> expand, enhancement -> selective, bugfix/hotfix/refactor -> hold, broad blast radius -> reduce"),
61
- "Do not walk the full checklist by default. Lead with the default scope contract; ask only when the answer changes in/out/deferred boundaries.",
59
+ "Do not walk the full checklist by default. Lead with an opinionated scope contract and the one decision that matters most.",
62
60
  "Challenge premise first, take a firm position, and name one concrete condition that would change it.",
63
61
  "Push back on weak framing: vague scope needs a specific user/problem, platform vision needs a narrow wedge, social proof needs behavioral evidence.",
64
62
  "Resolve one structural scope issue at a time; otherwise state the assumption and move on.",
65
- "After acceptance/rejection, commit fully and do not re-argue.",
66
- `Before final approval, reconcile outside-voice findings when the loop runs and bound retries with ${reviewLoopPolicySummary("scope")}`,
63
+ "If the user says no but cannot name the change, offer concrete moves: keep scope, add one obvious adjacent capability, reduce to wedge, or re-open stack/product direction.",
64
+ `Before final approval, record outside-voice findings and a \`## Spec Review Loop\` table using ${reviewLoopPolicySummary("scope")}`,
67
65
  "**STOP.** Wait for explicit approval of the scope contract before advancing.",
68
- "**STOP BEFORE ADVANCE.** Mandatory delegation `planner` must be completed or explicitly waived, then close via `node .cclaw/hooks/stage-complete.mjs scope`."
66
+ "**STOP BEFORE ADVANCE.** Mandatory delegation `planner` must be completed or explicitly waived. Then close with `node .cclaw/hooks/stage-complete.mjs scope --passed=scope_mode_selected,scope_contract_written,scope_user_approved --evidence-json '{\"scope_mode_selected\":\"<mode + rationale>\",\"scope_contract_written\":\"<artifact path + sections>\"}'`. Do not include `scope_user_approved` manually; stage-complete auto-hydrates its review-loop envelope from `## Spec Review Loop`."
69
67
  ],
70
68
  process: [
71
69
  "Run configured pre-scope audit only when enabled.",
72
- "Challenge premise, check existing-code leverage, and calibrate ambition/quality bar.",
73
- "Compare structured scope alternatives with minimum viable and ideal architecture options.",
74
- "Select scope mode with explicit user approval.",
75
- "Run the selected mode analysis and park high-upside deferred ideas when useful.",
76
- `Use outside-voice review only when complex/high-risk or configured; otherwise run a short adversarial self-check. If loop runs, enforce ${reviewLoopPolicySummary("scope")}`,
77
- "Write explicit scope contract, discretion areas, deferred items, and D-XX locked decisions.",
78
- "Produce scope summary and completion dashboard."
70
+ "Run the gstack-style CEO scope pass: job-to-be-done, premise challenge, 10-star upside, smallest useful wedge, and what would change the recommendation.",
71
+ "Compare minimum viable, product-grade, and ideal architecture scope alternatives with explicit reuse/effort/risk.",
72
+ "Select scope mode with explicit rationale; ask for user opt-in only when changing or expanding scope.",
73
+ "Run outside voice / adversarial self-check before final approval and record a valid `## Spec Review Loop` table.",
74
+ "Write explicit scope contract, discretion areas, deferred items, error/rescue registry, and D-XX locked decisions.",
75
+ "Produce scope summary and completion dashboard before asking final approval."
79
76
  ],
80
77
  requiredGates: [
81
78
  { id: "scope_mode_selected", description: "One scope mode was explicitly selected." },
@@ -90,8 +87,8 @@ export const SCOPE = {
90
87
  "Selected mode and rationale are documented.",
91
88
  "Locked Decisions section lists stable D-XX IDs for non-negotiable boundaries.",
92
89
  "Premise challenge findings documented.",
93
- "Outside Voice findings and dispositions are recorded (accept/reject/defer with rationale).",
94
- `Spec review loop summary includes iteration count and quality score trajectory per ${reviewLoopPolicySummary("scope")}`,
90
+ "Outside Voice findings and dispositions are recorded (accept/reject/defer with rationale) before final approval.",
91
+ `Spec review loop summary includes a table with columns Iteration, Quality Score, Findings, plus Stop reason, Target score, and Max iterations. stage-complete auto-hydrates scope_user_approved evidence from this section. ${reviewLoopPolicySummary("scope")}`,
95
92
  reviewLoopSecondOpinionSummary("scope"),
96
93
  "Deferred items list with one-line rationale for each.",
97
94
  "When an upside deferred idea is parked, a seed file is created under `.cclaw/seeds/` and referenced in the artifact.",
@@ -86,7 +86,7 @@ ${conversationLanguagePolicyMarkdown()}
86
86
  > Override? (A) keep \`<recommended>\` (B) switch track (C) cancel.
87
87
  If the harness's native ask tool is available (\`AskUserQuestion\` / \`AskQuestion\` / \`question\` / \`request_user_input\`), send exactly ONE question; on schema error, fall back to a plain-text lettered list.
88
88
  10. Start the tracked flow only through the managed helper:
89
- \`cclaw internal start-flow --track=<quick|medium|standard> --class=<class> --prompt=<prompt> --stack=<stack> --reason=<matched heuristic>\`
89
+ \`node .cclaw/hooks/start-flow.mjs --track=<quick|medium|standard> --class=<class> --prompt=<prompt> --stack=<stack> --reason=<matched heuristic>\`
90
90
  If this helper fails, STOP and report the exact command/output. Do **not** manually edit \`${flowPath}\`.
91
91
  11. The helper persists \`${flowPath}\`, computes \`skippedStages\`, sets the first stage for the track, resets the gate catalog, and writes \`.cclaw/artifacts/00-idea.md\`.
92
92
  12. Load the **first-stage skill for the chosen track** and its command file:
@@ -102,7 +102,7 @@ If during any stage the agent discovers evidence that contradicts the initial Ph
102
102
  1. Surface the new evidence in plain text.
103
103
  2. Propose the updated \`Class\` + \`Track\` with a one-line reason.
104
104
  3. Use the Decision Protocol to let the user accept, override, or cancel.
105
- 4. On acceptance: run \`cclaw internal start-flow --reclassify --track=<new-track> --class=<new-class> --reason=<why>\`. The helper appends a \`Reclassification:\` entry to \`00-idea.md\` and updates flow state atomically. If it fails, STOP and report the exact output; do NOT manually edit \`flow-state.json\`.
105
+ 4. On acceptance: run \`node .cclaw/hooks/start-flow.mjs --reclassify --track=<new-track> --class=<new-class> --reason=<why>\`. The helper appends a \`Reclassification:\` entry to \`00-idea.md\` and updates flow state atomically. If it fails, STOP and report the exact output; do NOT manually edit \`flow-state.json\`.
106
106
 
107
107
  ### Without prompt (\`/cc\`)
108
108
 
@@ -181,12 +181,12 @@ ${conversationLanguagePolicyMarkdown()}
181
181
 
182
182
  - On conflict, prefer \`standard\` over \`medium\`, and \`medium\` over \`quick\`.
183
183
  - Always state the recommendation as a one-line reason citing matched triggers.
184
- 8. Run the managed start helper: \`cclaw internal start-flow --track=<quick|medium|standard> --class=<class> --prompt=<prompt> --stack=<stack> --reason=<matched heuristic>\`. The helper writes \`${flowPath}\`, computes \`skippedStages\`, resets the gate catalog, and writes \`${RUNTIME_ROOT}/artifacts/00-idea.md\`. If it fails, STOP and report the exact command/output; do not manually edit flow state.
184
+ 8. Run the managed start helper: \`node .cclaw/hooks/start-flow.mjs --track=<quick|medium|standard> --class=<class> --prompt=<prompt> --stack=<stack> --reason=<matched heuristic>\`. The helper writes \`${flowPath}\`, computes \`skippedStages\`, resets the gate catalog, and writes \`${RUNTIME_ROOT}/artifacts/00-idea.md\`. If it fails, STOP and report the exact command/output; do not manually edit flow state.
185
185
  9. Load and execute the **first stage skill of the chosen track** (\`brainstorming\` for medium/standard, \`specification-authoring\` for quick) plus its matching command file.
186
186
 
187
187
  ### Reclassification on discovery
188
188
 
189
- If mid-stage evidence contradicts the initial Class/Track decision (the "trivial" change needs a migration, the "quick" bug fix needs architecture work, an origin doc multiplies scope), STOP and re-classify using the Decision Protocol. On acceptance, run \`cclaw internal start-flow --reclassify --track=<new-track> --class=<new-class> --reason=<why>\`; the helper records \`Reclassification:\` in \`00-idea.md\` and updates state atomically. Do NOT rewrite prior artifacts or manually edit flow-state.
189
+ If mid-stage evidence contradicts the initial Class/Track decision (the "trivial" change needs a migration, the "quick" bug fix needs architecture work, an origin doc multiplies scope), STOP and re-classify using the Decision Protocol. On acceptance, run \`node .cclaw/hooks/start-flow.mjs --reclassify --track=<new-track> --class=<new-class> --reason=<why>\`; the helper records \`Reclassification:\` in \`00-idea.md\` and updates state atomically. Do NOT rewrite prior artifacts or manually edit flow-state.
190
190
 
191
191
  ### Path B: \`/cc\` (no arguments)
192
192
 
package/dist/doctor.js CHANGED
@@ -92,7 +92,7 @@ function extractGeneratedCliEntrypoints(scriptContent) {
92
92
  return paths;
93
93
  }
94
94
  async function generatedCliEntrypointsOk(projectRoot) {
95
- const hookScripts = ["stage-complete.mjs", "run-hook.mjs"];
95
+ const hookScripts = ["stage-complete.mjs", "start-flow.mjs", "run-hook.mjs"];
96
96
  const problems = [];
97
97
  const checked = [];
98
98
  for (const script of hookScripts) {
@@ -644,6 +644,7 @@ export async function doctorChecks(projectRoot, options = {}) {
644
644
  "run-hook.mjs",
645
645
  "run-hook.cmd",
646
646
  "stage-complete.mjs",
647
+ "start-flow.mjs",
647
648
  "opencode-plugin.mjs"
648
649
  ]) {
649
650
  const scriptPath = path.join(projectRoot, RUNTIME_ROOT, "hooks", script);
package/dist/install.js CHANGED
@@ -12,7 +12,7 @@ import { viewCommandContract, viewCommandSkillMarkdown } from "./content/view-co
12
12
  import { subagentDrivenDevSkill, parallelAgentsSkill } from "./content/subagents.js";
13
13
  import { sessionHooksSkillMarkdown } from "./content/session-hooks.js";
14
14
  import { ironLawRuntimeDocument, ironLawsSkillMarkdown } from "./content/iron-laws.js";
15
- import { stageCompleteScript, runHookCmdScript, opencodePluginJs, claudeHooksJson, codexHooksJson, cursorHooksJson } from "./content/hooks.js";
15
+ import { stageCompleteScript, startFlowScript, runHookCmdScript, opencodePluginJs, claudeHooksJson, codexHooksJson, cursorHooksJson } from "./content/hooks.js";
16
16
  import { nodeHookRuntimeScript } from "./content/node-hooks.js";
17
17
  import { META_SKILL_NAME, usingCclawSkillMarkdown } from "./content/meta-skill.js";
18
18
  import { ARTIFACT_TEMPLATES, CURSOR_WORKFLOW_RULE_MDC, RULEBOOK_MARKDOWN, buildRulesJson } from "./content/templates.js";
@@ -705,6 +705,7 @@ async function writeHooks(projectRoot, config) {
705
705
  strictLaws: config.ironLaws?.strictLaws
706
706
  }), null, 2)}\n`);
707
707
  await writeFileSafe(path.join(hooksDir, "stage-complete.mjs"), stageCompleteScript());
708
+ await writeFileSafe(path.join(hooksDir, "start-flow.mjs"), startFlowScript());
708
709
  await writeFileSafe(path.join(hooksDir, "run-hook.mjs"), nodeHookRuntimeScript({
709
710
  strictness: effectiveStrictness,
710
711
  tddTestPathPatterns: config.tdd?.testPathPatterns ?? config.tddTestGlobs,
@@ -717,6 +718,7 @@ async function writeHooks(projectRoot, config) {
717
718
  try {
718
719
  for (const script of [
719
720
  "stage-complete.mjs",
721
+ "start-flow.mjs",
720
722
  "run-hook.mjs",
721
723
  "run-hook.cmd",
722
724
  "opencode-plugin.mjs"
@@ -359,9 +359,15 @@ async function hydrateReviewLoopEvidenceFromArtifact(projectRoot, stage, track,
359
359
  return;
360
360
  if (!selectedGateIds.includes(gateId))
361
361
  return;
362
- const existing = evidenceByGate[gateId];
363
- if (typeof existing === "string" && existing.trim().length > 0)
362
+ const reviewStage = stage === "scope" || stage === "design" ? stage : null;
363
+ if (!reviewStage)
364
364
  return;
365
+ const existing = evidenceByGate[gateId];
366
+ if (typeof existing === "string" && existing.trim().length > 0) {
367
+ const existingIssue = validateGateEvidenceShape(stage, gateId, existing);
368
+ if (!existingIssue)
369
+ return;
370
+ }
365
371
  const resolved = await resolveArtifactPath(stage, {
366
372
  projectRoot,
367
373
  track,
@@ -374,9 +380,6 @@ async function hydrateReviewLoopEvidenceFromArtifact(projectRoot, stage, track,
374
380
  catch {
375
381
  return;
376
382
  }
377
- const reviewStage = stage === "scope" || stage === "design" ? stage : null;
378
- if (!reviewStage)
379
- return;
380
383
  const envelope = extractReviewLoopEnvelopeFromArtifact(raw, reviewStage, resolved.relPath);
381
384
  if (!envelope)
382
385
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cclaw-cli",
3
- "version": "0.51.5",
3
+ "version": "0.51.7",
4
4
  "description": "Installer-first flow toolkit for coding agents",
5
5
  "type": "module",
6
6
  "bin": {