cclaw-cli 0.51.30 → 0.55.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -16
- package/dist/artifact-linter/brainstorm.d.ts +2 -0
- package/dist/artifact-linter/brainstorm.js +245 -0
- package/dist/artifact-linter/design.d.ts +2 -0
- package/dist/artifact-linter/design.js +323 -0
- package/dist/artifact-linter/plan.d.ts +2 -0
- package/dist/artifact-linter/plan.js +162 -0
- package/dist/artifact-linter/review-army.d.ts +24 -0
- package/dist/artifact-linter/review-army.js +365 -0
- package/dist/artifact-linter/review.d.ts +2 -0
- package/dist/artifact-linter/review.js +65 -0
- package/dist/artifact-linter/scope.d.ts +2 -0
- package/dist/artifact-linter/scope.js +115 -0
- package/dist/artifact-linter/shared.d.ts +246 -0
- package/dist/artifact-linter/shared.js +1488 -0
- package/dist/artifact-linter/ship.d.ts +2 -0
- package/dist/artifact-linter/ship.js +46 -0
- package/dist/artifact-linter/spec.d.ts +2 -0
- package/dist/artifact-linter/spec.js +108 -0
- package/dist/artifact-linter/tdd.d.ts +2 -0
- package/dist/artifact-linter/tdd.js +124 -0
- package/dist/artifact-linter.d.ts +4 -76
- package/dist/artifact-linter.js +56 -2949
- package/dist/cli.d.ts +1 -6
- package/dist/cli.js +4 -159
- package/dist/codex-feature-flag.d.ts +1 -1
- package/dist/codex-feature-flag.js +1 -1
- package/dist/config.d.ts +3 -2
- package/dist/config.js +67 -3
- package/dist/constants.d.ts +1 -7
- package/dist/constants.js +9 -15
- package/dist/content/cancel-command.js +2 -2
- package/dist/content/closeout-guidance.js +10 -7
- package/dist/content/core-agents.d.ts +18 -0
- package/dist/content/core-agents.js +46 -2
- package/dist/content/decision-protocol.d.ts +1 -1
- package/dist/content/decision-protocol.js +1 -1
- package/dist/content/examples.js +6 -6
- package/dist/content/harness-doc.js +20 -2
- package/dist/content/hook-inline-snippets.d.ts +17 -4
- package/dist/content/hook-inline-snippets.js +218 -5
- package/dist/content/hook-manifest.d.ts +2 -2
- package/dist/content/hook-manifest.js +2 -2
- package/dist/content/hooks.d.ts +1 -0
- package/dist/content/hooks.js +32 -137
- package/dist/content/idea-command.d.ts +8 -0
- package/dist/content/{ideate-command.js → idea-command.js} +57 -50
- package/dist/content/idea-frames.d.ts +31 -0
- package/dist/content/{ideate-frames.js → idea-frames.js} +9 -9
- package/dist/content/idea-ranking.d.ts +25 -0
- package/dist/content/{ideate-ranking.js → idea-ranking.js} +5 -5
- package/dist/content/iron-laws.d.ts +0 -1
- package/dist/content/iron-laws.js +31 -16
- package/dist/content/learnings.js +1 -1
- package/dist/content/meta-skill.js +7 -7
- package/dist/content/node-hooks.d.ts +10 -0
- package/dist/content/node-hooks.js +43 -9
- package/dist/content/opencode-plugin.js +3 -3
- package/dist/content/skills.js +19 -7
- package/dist/content/stage-schema.js +44 -2
- package/dist/content/stages/_lint-metadata/index.js +26 -2
- package/dist/content/stages/brainstorm.js +13 -7
- package/dist/content/stages/design.js +16 -11
- package/dist/content/stages/plan.js +7 -4
- package/dist/content/stages/review.js +4 -4
- package/dist/content/stages/schema-types.d.ts +1 -1
- package/dist/content/stages/scope.js +15 -12
- package/dist/content/stages/ship.js +2 -2
- package/dist/content/stages/spec.js +9 -3
- package/dist/content/stages/tdd.js +14 -4
- package/dist/content/start-command.js +11 -10
- package/dist/content/status-command.js +3 -3
- package/dist/content/subagents.js +60 -6
- package/dist/content/templates.d.ts +1 -1
- package/dist/content/templates.js +102 -150
- package/dist/content/tree-command.js +2 -2
- package/dist/content/utility-skills.d.ts +2 -2
- package/dist/content/utility-skills.js +2 -2
- package/dist/content/view-command.js +4 -2
- package/dist/delegation.d.ts +2 -0
- package/dist/delegation.js +2 -1
- package/dist/early-loop.d.ts +66 -0
- package/dist/early-loop.js +275 -0
- package/dist/gate-evidence.d.ts +8 -0
- package/dist/gate-evidence.js +141 -5
- package/dist/harness-adapters.d.ts +2 -2
- package/dist/harness-adapters.js +47 -18
- package/dist/install.js +153 -29
- package/dist/internal/advance-stage/advance.d.ts +50 -0
- package/dist/internal/advance-stage/advance.js +480 -0
- package/dist/internal/advance-stage/cancel-run.d.ts +8 -0
- package/dist/internal/advance-stage/cancel-run.js +19 -0
- package/dist/internal/advance-stage/flow-state-coercion.d.ts +3 -0
- package/dist/internal/advance-stage/flow-state-coercion.js +81 -0
- package/dist/internal/advance-stage/helpers.d.ts +14 -0
- package/dist/internal/advance-stage/helpers.js +145 -0
- package/dist/internal/advance-stage/hook.d.ts +8 -0
- package/dist/internal/advance-stage/hook.js +40 -0
- package/dist/internal/advance-stage/parsers.d.ts +54 -0
- package/dist/internal/advance-stage/parsers.js +307 -0
- package/dist/internal/advance-stage/review-loop.d.ts +7 -0
- package/dist/internal/advance-stage/review-loop.js +170 -0
- package/dist/internal/advance-stage/rewind.d.ts +14 -0
- package/dist/internal/advance-stage/rewind.js +108 -0
- package/dist/internal/advance-stage/start-flow.d.ts +11 -0
- package/dist/internal/advance-stage/start-flow.js +136 -0
- package/dist/internal/advance-stage/verify.d.ts +29 -0
- package/dist/internal/advance-stage/verify.js +225 -0
- package/dist/internal/advance-stage.js +21 -1470
- package/dist/internal/compound-readiness.d.ts +1 -1
- package/dist/internal/compound-readiness.js +2 -2
- package/dist/internal/early-loop-status.d.ts +7 -0
- package/dist/internal/early-loop-status.js +90 -0
- package/dist/internal/runtime-integrity.d.ts +7 -0
- package/dist/internal/runtime-integrity.js +288 -0
- package/dist/internal/tdd-red-evidence.js +1 -1
- package/dist/knowledge-store.d.ts +3 -8
- package/dist/knowledge-store.js +16 -29
- package/dist/managed-resources.js +24 -2
- package/dist/policy.js +4 -6
- package/dist/run-archive.d.ts +1 -1
- package/dist/run-archive.js +12 -12
- package/dist/run-persistence.js +111 -11
- package/dist/tdd-cycle.d.ts +3 -3
- package/dist/tdd-cycle.js +1 -1
- package/dist/types.d.ts +18 -10
- package/package.json +1 -1
- package/dist/content/ideate-command.d.ts +0 -8
- package/dist/content/ideate-frames.d.ts +0 -31
- package/dist/content/ideate-ranking.d.ts +0 -25
- package/dist/content/next-command.d.ts +0 -20
- package/dist/content/next-command.js +0 -298
- package/dist/content/seed-shelf.d.ts +0 -36
- package/dist/content/seed-shelf.js +0 -301
- package/dist/content/stage-common-guidance.d.ts +0 -1
- package/dist/content/stage-common-guidance.js +0 -106
- package/dist/doctor-registry.d.ts +0 -10
- package/dist/doctor-registry.js +0 -186
- package/dist/doctor.d.ts +0 -17
- package/dist/doctor.js +0 -2201
- package/dist/internal/hook-manifest.d.ts +0 -16
- package/dist/internal/hook-manifest.js +0 -77
package/dist/harness-adapters.js
CHANGED
|
@@ -3,7 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import { RUNTIME_ROOT } from "./constants.js";
|
|
4
4
|
import { conversationLanguagePolicyMarkdown } from "./content/language-policy.js";
|
|
5
5
|
import { CCLAW_AGENTS, agentMarkdown } from "./content/core-agents.js";
|
|
6
|
-
import {
|
|
6
|
+
import { IRON_LAWS } from "./content/iron-laws.js";
|
|
7
7
|
import { ensureDir, exists, writeFileSafe } from "./fs-utils.js";
|
|
8
8
|
export const CCLAW_MARKER_START = "<!-- cclaw-start -->";
|
|
9
9
|
export const CCLAW_MARKER_END = "<!-- cclaw-end -->";
|
|
@@ -15,11 +15,11 @@ const RUNTIME_AGENTS_BLOCK_PATTERN = new RegExp(RUNTIME_AGENTS_BLOCK_SOURCE, "u"
|
|
|
15
15
|
const RUNTIME_AGENTS_BLOCK_GLOBAL_PATTERN = new RegExp(RUNTIME_AGENTS_BLOCK_SOURCE, "gu");
|
|
16
16
|
const UTILITY_SHIMS = [
|
|
17
17
|
{
|
|
18
|
-
fileName: "cc-
|
|
19
|
-
skillName: "cc-
|
|
20
|
-
command: "
|
|
21
|
-
skillFolder: "flow-
|
|
22
|
-
commandFile: "
|
|
18
|
+
fileName: "cc-idea.md",
|
|
19
|
+
skillName: "cc-idea",
|
|
20
|
+
command: "idea",
|
|
21
|
+
skillFolder: "flow-idea",
|
|
22
|
+
commandFile: "idea.md"
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
fileName: "cc-cancel.md",
|
|
@@ -163,9 +163,9 @@ export function harnessDispatchSurface(harnessId) {
|
|
|
163
163
|
case "cursor":
|
|
164
164
|
return "Use Cursor Subagent/Task with a generic subagent_type (explore for read-only mapping, generalPurpose for broader work, shell/browser-use when specifically needed) and paste the cclaw role prompt; record fulfillmentMode: \"generic-dispatch\" with evidenceRefs.";
|
|
165
165
|
case "opencode":
|
|
166
|
-
return "Use OpenCode subagents: invoke the generated .opencode/agents/<agent>.md agent via Task or @<agent>; if agents or plugin registration are missing, run `cclaw sync` and check opencode.json(.c) plugin registration with `cclaw
|
|
166
|
+
return "Use OpenCode subagents: invoke the generated .opencode/agents/<agent>.md agent via Task or @<agent>; if agents or plugin registration are missing, run `cclaw sync` and check opencode.json(.c) plugin registration with `npx cclaw-cli sync`; record scheduled/launched/acknowledged/completed events with spanId+dispatchId before claiming fulfillmentMode: \"isolated\".";
|
|
167
167
|
case "codex":
|
|
168
|
-
return "Use Codex native subagents: ask Codex to spawn the generated .codex/agents/<agent>.toml agent(s) by name; if hooks are inert, set `[features] codex_hooks = true` in ~/.codex/config.toml or rerun init/sync repair, then `cclaw
|
|
168
|
+
return "Use Codex native subagents: ask Codex to spawn the generated .codex/agents/<agent>.toml agent(s) by name; if hooks are inert, set `[features] codex_hooks = true` in ~/.codex/config.toml or rerun init/sync repair, then `npx cclaw-cli sync`; record scheduled/launched/acknowledged/completed events with spanId+dispatchId before claiming fulfillmentMode: \"isolated\".";
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
171
|
/**
|
|
@@ -292,6 +292,35 @@ export function harnessesByTier() {
|
|
|
292
292
|
return tierOrder[harnessTier(a)] - tierOrder[harnessTier(b)];
|
|
293
293
|
});
|
|
294
294
|
}
|
|
295
|
+
function ironLawsAgentsMdBlock() {
|
|
296
|
+
const enforcedLawIds = new Set([
|
|
297
|
+
"stop-clean-or-handoff",
|
|
298
|
+
"review-coverage-complete-before-ship"
|
|
299
|
+
]);
|
|
300
|
+
const enforcedRows = IRON_LAWS
|
|
301
|
+
.filter((law) => enforcedLawIds.has(law.id))
|
|
302
|
+
.map((law) => `| \`${law.id}\` | ${law.rule} | ${law.enforcement} |`)
|
|
303
|
+
.join("\n");
|
|
304
|
+
const advisoryRows = IRON_LAWS
|
|
305
|
+
.filter((law) => !enforcedLawIds.has(law.id))
|
|
306
|
+
.map((law) => {
|
|
307
|
+
const appliesTo = law.appliesTo === "all" ? "all stages" : law.appliesTo.join(", ");
|
|
308
|
+
return `- \`${law.id}\` (applies to: ${appliesTo})`;
|
|
309
|
+
})
|
|
310
|
+
.join("\n");
|
|
311
|
+
return `### Iron Laws
|
|
312
|
+
|
|
313
|
+
These rules are always-on. The hook-enforced runtime laws are:
|
|
314
|
+
|
|
315
|
+
| ID | Rule | Enforced by |
|
|
316
|
+
|---|---|---|
|
|
317
|
+
${enforcedRows}
|
|
318
|
+
|
|
319
|
+
Advisory laws are stage-owned through each stage's HARD-GATE block:
|
|
320
|
+
|
|
321
|
+
${advisoryRows}
|
|
322
|
+
`;
|
|
323
|
+
}
|
|
295
324
|
function agentsMdBlock() {
|
|
296
325
|
return `${CCLAW_MARKER_START}
|
|
297
326
|
## Cclaw — Workflow Adapter
|
|
@@ -343,7 +372,7 @@ When in doubt, prefer **non-trivial** — the quick track is opt-in and only saf
|
|
|
343
372
|
| Command | Purpose |
|
|
344
373
|
|---|---|
|
|
345
374
|
| \`/cc\` | **Entry point.** No args = resume or progress current flow. With prompt = classify task and start the right flow. |
|
|
346
|
-
| \`/cc-
|
|
375
|
+
| \`/cc-idea\` | **Idea mode.** Generates a ranked repo-improvement backlog before implementation. |
|
|
347
376
|
| \`/cc-cancel\` | **Non-completion closeout.** Archives a cancelled/abandoned run with a required reason. |
|
|
348
377
|
|
|
349
378
|
Knowledge capture and curation run automatically as part of stage completion
|
|
@@ -372,11 +401,11 @@ If the same approach fails three times in a row (same command, same finding, sam
|
|
|
372
401
|
### Codex users
|
|
373
402
|
|
|
374
403
|
OpenAI Codex CLI has **no native \`/cc\` slash command** (custom prompts
|
|
375
|
-
were deprecated in v0.89, Jan 2026). The \`/cc\`, \`/cc-
|
|
404
|
+
were deprecated in v0.89, Jan 2026). The \`/cc\`, \`/cc-idea\`, and
|
|
376
405
|
\`/cc-cancel\` tokens above describe intent — in Codex they map onto skills cclaw installs at
|
|
377
406
|
\`.agents/skills/cc*/SKILL.md\`. Activate one of two ways:
|
|
378
407
|
|
|
379
|
-
- Type \`/use cc\` (or \`cc-
|
|
408
|
+
- Type \`/use cc\` (or \`cc-idea\` / \`cc-cancel\`) at Codex's prompt.
|
|
380
409
|
- Type \`/cc …\` as plain text — Codex matches the skill \`description\`
|
|
381
410
|
frontmatter (which spells out the token verbatim) and loads the right
|
|
382
411
|
skill body automatically.
|
|
@@ -385,9 +414,9 @@ Codex CLI v0.114+ (Mar 2026) **does** expose lifecycle hooks via
|
|
|
385
414
|
\`.codex/hooks.json\`, gated by the \`[features] codex_hooks = true\` flag
|
|
386
415
|
in \`~/.codex/config.toml\`. cclaw generates \`.codex/hooks.json\` on
|
|
387
416
|
sync; if the feature flag is off, hooks are inert and cclaw's
|
|
388
|
-
session-start rehydration simply does not fire. Run \`cclaw
|
|
417
|
+
session-start rehydration simply does not fire. Run \`npx cclaw-cli sync\` to
|
|
389
418
|
see if the flag is missing. \`.codex/commands/*\` is still unused by
|
|
390
|
-
Codex CLI and is removed on every sync. Run \`cclaw
|
|
419
|
+
Codex CLI and is removed on every sync. Run \`npx cclaw-cli sync\` for
|
|
391
420
|
hook coverage details (Bash-only \`PreToolUse\`/\`PostToolUse\`; other events are full).
|
|
392
421
|
${CCLAW_MARKER_END}`;
|
|
393
422
|
}
|
|
@@ -449,7 +478,7 @@ function utilityShimBehavior(command) {
|
|
|
449
478
|
switch (command) {
|
|
450
479
|
case "cc":
|
|
451
480
|
return "This is the entry command, not a flow stage. It may initialize or resume flow state after confirmation.";
|
|
452
|
-
case "
|
|
481
|
+
case "idea":
|
|
453
482
|
return "This is an ideation command, not a flow stage. It may write ideation artifacts/seeds but does not advance flow state.";
|
|
454
483
|
case "cancel":
|
|
455
484
|
return "This is a non-completion closeout utility, not a flow stage. It requires a reason and archives cancelled or abandoned work without presenting it as completed.";
|
|
@@ -484,8 +513,8 @@ function codexSkillDescription(command) {
|
|
|
484
513
|
switch (command) {
|
|
485
514
|
case "cc":
|
|
486
515
|
return `Entry point for the cclaw track-aware workflow ending in ship plus auto-closeout (retro → compound → archive). Use whenever the user types \`/cc\`, \`/cclaw\`, or asks to "start the flow", "begin cclaw", "kick off the workflow", "classify this task", or wants to start/resume a non-trivial software change. No args = resume the active stage from \`.cclaw/state/flow-state.json\`. With a prompt = classify and pick a track (quick/medium/standard).`;
|
|
487
|
-
case "
|
|
488
|
-
return `Read-only repo-improvement
|
|
516
|
+
case "idea":
|
|
517
|
+
return `Read-only repo-improvement idea mode for cclaw. Use when the user types \`/cc-idea\` or asks to "scan the repo for TODOs/tech debt", "generate a backlog", "brainstorm improvement ideas", or wants a ranked list of candidate ideas before committing to a single flow. Does not mutate \`.cclaw/state/flow-state.json\`.`;
|
|
489
518
|
case "cancel":
|
|
490
519
|
return `Cancel or abandon the active cclaw run. Use when the user types \`/cc-cancel\` or asks to cancel, abandon, stop, discard, or reset an unfinished run. Requires a reason and archives with cancelled/abandoned disposition.`;
|
|
491
520
|
default:
|
|
@@ -518,7 +547,7 @@ under \`.agents/skills/${skillSlug}/\` so the user can either:
|
|
|
518
547
|
|
|
519
548
|
Lifecycle hooks **are** available in Codex CLI v0.114+ (behind the
|
|
520
549
|
\`[features] codex_hooks = true\` flag in \`~/.codex/config.toml\`) and
|
|
521
|
-
cclaw installs a matching \`.codex/hooks.json\`; run \`cclaw
|
|
550
|
+
cclaw installs a matching \`.codex/hooks.json\`; run \`npx cclaw-cli sync\`
|
|
522
551
|
for the current hook surface and limitations.
|
|
523
552
|
|
|
524
553
|
## Protocol
|
|
@@ -542,7 +571,7 @@ for the current hook surface and limitations.
|
|
|
542
571
|
unavailable or disabled, and then include non-empty \`evidenceRefs\`.
|
|
543
572
|
- Codex's \`PreToolUse\` / \`PostToolUse\` hooks currently only intercept
|
|
544
573
|
the \`Bash\` tool. \`Write\`, \`Edit\`, \`WebSearch\`, and MCP tool calls
|
|
545
|
-
are **not** gated by hooks — use \`cclaw
|
|
574
|
+
are **not** gated by hooks — use \`npx cclaw-cli sync\` for what cclaw
|
|
546
575
|
substitutes with in-turn agent steps for those call classes.
|
|
547
576
|
- Codex's \`SessionStart\` matcher only supports \`startup|resume\`. Claude
|
|
548
577
|
and Cursor also fire on \`clear\` and \`compact\`, so mid-session
|
package/dist/install.js
CHANGED
|
@@ -5,16 +5,15 @@ import { promisify } from "node:util";
|
|
|
5
5
|
import { CCLAW_VERSION, FLOW_VERSION, REQUIRED_DIRS, RUNTIME_ROOT } from "./constants.js";
|
|
6
6
|
import { writeConfig, createDefaultConfig, readConfig, configPath, detectLanguageRulePacks, detectAdvancedKeys } from "./config.js";
|
|
7
7
|
import { learnSkillMarkdown } from "./content/learnings.js";
|
|
8
|
-
import { nextCommandContract, nextCommandSkillMarkdown } from "./content/next-command.js";
|
|
9
8
|
import { stageCommandShimMarkdown } from "./content/stage-command.js";
|
|
10
|
-
import {
|
|
9
|
+
import { ideaCommandContract, ideaCommandSkillMarkdown } from "./content/idea-command.js";
|
|
11
10
|
import { startCommandContract, startCommandSkillMarkdown } from "./content/start-command.js";
|
|
12
11
|
import { viewCommandContract, viewCommandSkillMarkdown } from "./content/view-command.js";
|
|
13
12
|
import { cancelCommandContract, cancelCommandSkillMarkdown } from "./content/cancel-command.js";
|
|
14
13
|
import { subagentDrivenDevSkill, parallelAgentsSkill } from "./content/subagents.js";
|
|
15
14
|
import { sessionHooksSkillMarkdown } from "./content/session-hooks.js";
|
|
16
15
|
import { ironLawRuntimeDocument, ironLawsSkillMarkdown } from "./content/iron-laws.js";
|
|
17
|
-
import { stageCompleteScript, startFlowScript, runHookCmdScript, delegationRecordScript, opencodePluginJs, claudeHooksJson, codexHooksJson, cursorHooksJson } from "./content/hooks.js";
|
|
16
|
+
import { stageCompleteScript, startFlowScript, cancelRunScript, runHookCmdScript, delegationRecordScript, opencodePluginJs, claudeHooksJson, codexHooksJson, cursorHooksJson } from "./content/hooks.js";
|
|
18
17
|
import { nodeHookRuntimeScript } from "./content/node-hooks.js";
|
|
19
18
|
import { META_SKILL_NAME, usingCclawSkillMarkdown } from "./content/meta-skill.js";
|
|
20
19
|
import { ARTIFACT_TEMPLATES, CURSOR_WORKFLOW_RULE_MDC, RULEBOOK_MARKDOWN, buildRulesJson } from "./content/templates.js";
|
|
@@ -29,10 +28,11 @@ import { createInitialFlowState } from "./flow-state.js";
|
|
|
29
28
|
import { ensureDir, exists, writeFileSafe } from "./fs-utils.js";
|
|
30
29
|
import { ManagedResourceSession, setActiveManagedResourceSession } from "./managed-resources.js";
|
|
31
30
|
import { ensureGitignore, removeGitignorePatterns } from "./gitignore.js";
|
|
32
|
-
import { HARNESS_ADAPTERS, harnessShimFileNames, syncHarnessShims, removeCclawFromAgentsMd } from "./harness-adapters.js";
|
|
31
|
+
import { HARNESS_ADAPTERS, harnessShimFileNames, harnessShimSkillNames, syncHarnessShims, removeCclawFromAgentsMd } from "./harness-adapters.js";
|
|
33
32
|
import { validateHookDocument } from "./hook-schema.js";
|
|
34
33
|
import { detectHarnesses } from "./init-detect.js";
|
|
35
|
-
import {
|
|
34
|
+
import { classifyCodexHooksFlag, codexConfigPath, readCodexConfig } from "./codex-feature-flag.js";
|
|
35
|
+
import { CorruptFlowStateError, ensureRunSystem } from "./runs.js";
|
|
36
36
|
import { FLOW_STAGES } from "./types.js";
|
|
37
37
|
const OPENCODE_PLUGIN_REL_PATH = ".opencode/plugins/cclaw-plugin.mjs";
|
|
38
38
|
const CURSOR_RULE_REL_PATH = ".cursor/rules/cclaw-workflow.mdc";
|
|
@@ -49,6 +49,23 @@ async function writeInitSentinel(projectRoot, operation) {
|
|
|
49
49
|
await writeFileSafe(sentinelPath, `${JSON.stringify({ operation, startedAt: new Date().toISOString() }, null, 2)}\n`);
|
|
50
50
|
return sentinelPath;
|
|
51
51
|
}
|
|
52
|
+
async function warnStaleInitSentinel(projectRoot, operation) {
|
|
53
|
+
const sentinelPath = runtimePath(projectRoot, "state", INIT_SENTINEL_FILE);
|
|
54
|
+
if (!(await exists(sentinelPath)))
|
|
55
|
+
return;
|
|
56
|
+
let startedAt = "unknown time";
|
|
57
|
+
try {
|
|
58
|
+
const raw = await fs.readFile(sentinelPath, "utf8");
|
|
59
|
+
const parsed = JSON.parse(raw);
|
|
60
|
+
if (parsed && typeof parsed.startedAt === "string" && parsed.startedAt.trim().length > 0) {
|
|
61
|
+
startedAt = parsed.startedAt;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// best-effort parse of stale sentinel metadata
|
|
66
|
+
}
|
|
67
|
+
process.stderr.write(`[${operation}] Detected stale .init-in-progress sentinel from ${startedAt}; previous run may have crashed. Continuing.\n`);
|
|
68
|
+
}
|
|
52
69
|
async function removeBestEffort(targetPath, recursive = false) {
|
|
53
70
|
try {
|
|
54
71
|
await fs.rm(targetPath, { recursive, force: true });
|
|
@@ -89,6 +106,16 @@ const DEPRECATED_UTILITY_SKILL_FOLDERS = [
|
|
|
89
106
|
"flow-tree",
|
|
90
107
|
"flow-diff"
|
|
91
108
|
];
|
|
109
|
+
const DEPRECATED_STAGE_SKILL_FOLDERS = [
|
|
110
|
+
"brainstorming",
|
|
111
|
+
"scope-shaping",
|
|
112
|
+
"engineering-design-lock",
|
|
113
|
+
"specification-authoring",
|
|
114
|
+
"planning-and-task-breakdown",
|
|
115
|
+
"test-driven-development",
|
|
116
|
+
"two-layer-review",
|
|
117
|
+
"shipping-and-handoff"
|
|
118
|
+
];
|
|
92
119
|
const DEPRECATED_AGENT_FILES = [
|
|
93
120
|
"securityer.md",
|
|
94
121
|
"spec-reviewer.md",
|
|
@@ -451,8 +478,7 @@ async function writeSkills(projectRoot, config) {
|
|
|
451
478
|
}
|
|
452
479
|
// Utility skills (not flow stages)
|
|
453
480
|
await writeFileSafe(runtimePath(projectRoot, "skills", "learnings", "SKILL.md"), learnSkillMarkdown());
|
|
454
|
-
await writeFileSafe(runtimePath(projectRoot, "skills", "flow-
|
|
455
|
-
await writeFileSafe(runtimePath(projectRoot, "skills", "flow-ideate", "SKILL.md"), ideateCommandSkillMarkdown());
|
|
481
|
+
await writeFileSafe(runtimePath(projectRoot, "skills", "flow-idea", "SKILL.md"), ideaCommandSkillMarkdown());
|
|
456
482
|
await writeFileSafe(runtimePath(projectRoot, "skills", "flow-start", "SKILL.md"), startCommandSkillMarkdown());
|
|
457
483
|
await writeFileSafe(runtimePath(projectRoot, "skills", "flow-view", "SKILL.md"), viewCommandSkillMarkdown());
|
|
458
484
|
await writeFileSafe(runtimePath(projectRoot, "skills", "flow-cancel", "SKILL.md"), cancelCommandSkillMarkdown());
|
|
@@ -516,8 +542,7 @@ async function writeSkills(projectRoot, config) {
|
|
|
516
542
|
}
|
|
517
543
|
async function writeEntryCommands(projectRoot) {
|
|
518
544
|
await writeFileSafe(runtimePath(projectRoot, "commands", "start.md"), startCommandContract());
|
|
519
|
-
await writeFileSafe(runtimePath(projectRoot, "commands", "
|
|
520
|
-
await writeFileSafe(runtimePath(projectRoot, "commands", "ideate.md"), ideateCommandContract());
|
|
545
|
+
await writeFileSafe(runtimePath(projectRoot, "commands", "idea.md"), ideaCommandContract());
|
|
521
546
|
await writeFileSafe(runtimePath(projectRoot, "commands", "view.md"), viewCommandContract());
|
|
522
547
|
await writeFileSafe(runtimePath(projectRoot, "commands", "cancel.md"), cancelCommandContract());
|
|
523
548
|
for (const stage of FLOW_STAGES) {
|
|
@@ -858,14 +883,16 @@ async function writeMergedHookJson(projectRoot, hookFilePath, generatedJson) {
|
|
|
858
883
|
if (harness) {
|
|
859
884
|
const generatedSchema = validateHookDocument(harness, generatedDoc);
|
|
860
885
|
if (!generatedSchema.ok) {
|
|
861
|
-
throw new Error(`
|
|
886
|
+
throw new Error(`[sync fail-fast] Hook document drift detected for ${harness}: generated hook document is invalid (${generatedSchema.errors.join("; ")}). ` +
|
|
887
|
+
"Run `npx cclaw-cli sync` to regenerate managed hooks or repair the generated hook shape manually.");
|
|
862
888
|
}
|
|
863
889
|
}
|
|
864
890
|
const mergedDoc = mergeHookDocuments(existingDoc, generatedDoc);
|
|
865
891
|
if (harness) {
|
|
866
892
|
const mergedSchema = validateHookDocument(harness, mergedDoc);
|
|
867
893
|
if (!mergedSchema.ok) {
|
|
868
|
-
throw new Error(`
|
|
894
|
+
throw new Error(`[sync fail-fast] Hook document drift detected for ${harness}: merged hook document is invalid (${mergedSchema.errors.join("; ")}). ` +
|
|
895
|
+
"Run `npx cclaw-cli sync` after fixing the custom hook entry or remove the malformed user-authored hook block.");
|
|
869
896
|
}
|
|
870
897
|
}
|
|
871
898
|
await writeFileSafe(hookFilePath, `${JSON.stringify(mergedDoc, null, 2)}\n`);
|
|
@@ -883,11 +910,14 @@ async function writeHooks(projectRoot, config) {
|
|
|
883
910
|
}), null, 2)}\n`);
|
|
884
911
|
await writeFileSafe(path.join(hooksDir, "stage-complete.mjs"), stageCompleteScript());
|
|
885
912
|
await writeFileSafe(path.join(hooksDir, "start-flow.mjs"), startFlowScript());
|
|
913
|
+
await writeFileSafe(path.join(hooksDir, "cancel-run.mjs"), cancelRunScript());
|
|
886
914
|
await writeFileSafe(path.join(hooksDir, "run-hook.mjs"), nodeHookRuntimeScript({
|
|
887
915
|
strictness: effectiveStrictness,
|
|
888
916
|
tddTestPathPatterns: config.tdd?.testPathPatterns ?? config.tddTestGlobs,
|
|
889
917
|
tddProductionPathPatterns: config.tdd?.productionPathPatterns,
|
|
890
|
-
compoundRecurrenceThreshold: config.compound?.recurrenceThreshold
|
|
918
|
+
compoundRecurrenceThreshold: config.compound?.recurrenceThreshold,
|
|
919
|
+
earlyLoopEnabled: config.earlyLoop?.enabled,
|
|
920
|
+
earlyLoopMaxIterations: config.earlyLoop?.maxIterations
|
|
891
921
|
}));
|
|
892
922
|
await writeFileSafe(path.join(hooksDir, "run-hook.cmd"), runHookCmdScript());
|
|
893
923
|
await writeFileSafe(path.join(hooksDir, "delegation-record.mjs"), delegationRecordScript());
|
|
@@ -900,7 +930,8 @@ async function writeHooks(projectRoot, config) {
|
|
|
900
930
|
"run-hook.mjs",
|
|
901
931
|
"run-hook.cmd",
|
|
902
932
|
"delegation-record.mjs",
|
|
903
|
-
"opencode-plugin.mjs"
|
|
933
|
+
"opencode-plugin.mjs",
|
|
934
|
+
"cancel-run.mjs"
|
|
904
935
|
]) {
|
|
905
936
|
await fs.chmod(path.join(hooksDir, script), 0o755);
|
|
906
937
|
}
|
|
@@ -938,8 +969,8 @@ async function writeHooks(projectRoot, config) {
|
|
|
938
969
|
// flag in `~/.codex/config.toml`. cclaw always writes the file so
|
|
939
970
|
// the moment the flag flips on, the cclaw hooks start firing. See
|
|
940
971
|
// `codexHooksJsonWithObservation` for the Bash-only caveat on
|
|
941
|
-
// PreToolUse/PostToolUse.
|
|
942
|
-
//
|
|
972
|
+
// PreToolUse/PostToolUse. If the feature flag is off, hooks remain
|
|
973
|
+
// inert until the user enables codex_hooks in ~/.codex/config.toml.
|
|
943
974
|
const codexDir = path.join(projectRoot, ".codex");
|
|
944
975
|
await ensureDir(codexDir);
|
|
945
976
|
await writeMergedHookJson(projectRoot, path.join(codexDir, "hooks.json"), codexHooksJson());
|
|
@@ -989,7 +1020,7 @@ async function syncDisabledHarnessArtifacts(projectRoot, harnesses) {
|
|
|
989
1020
|
for (const entry of managedHookFiles) {
|
|
990
1021
|
if (enabled.has(entry.harness))
|
|
991
1022
|
continue;
|
|
992
|
-
await removeManagedHookEntries(entry.hookPath);
|
|
1023
|
+
await removeManagedHookEntries(entry.hookPath, { failOnParseError: true });
|
|
993
1024
|
}
|
|
994
1025
|
if (!enabled.has("opencode")) {
|
|
995
1026
|
try {
|
|
@@ -998,8 +1029,22 @@ async function syncDisabledHarnessArtifacts(projectRoot, harnesses) {
|
|
|
998
1029
|
catch {
|
|
999
1030
|
// best-effort cleanup
|
|
1000
1031
|
}
|
|
1032
|
+
try {
|
|
1033
|
+
await fs.rm(path.join(projectRoot, ".opencode/agents"), { recursive: true, force: true });
|
|
1034
|
+
}
|
|
1035
|
+
catch {
|
|
1036
|
+
// best-effort cleanup
|
|
1037
|
+
}
|
|
1001
1038
|
await removeManagedOpenCodePluginConfig(projectRoot, OPENCODE_PLUGIN_REL_PATH);
|
|
1002
1039
|
}
|
|
1040
|
+
if (!enabled.has("codex")) {
|
|
1041
|
+
try {
|
|
1042
|
+
await fs.rm(path.join(projectRoot, ".codex/agents"), { recursive: true, force: true });
|
|
1043
|
+
}
|
|
1044
|
+
catch {
|
|
1045
|
+
// best-effort cleanup
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1003
1048
|
}
|
|
1004
1049
|
async function writeState(projectRoot, config, forceReset = false) {
|
|
1005
1050
|
const statePath = runtimePath(projectRoot, "state", "flow-state.json");
|
|
@@ -1013,6 +1058,9 @@ async function cleanLegacyArtifacts(projectRoot) {
|
|
|
1013
1058
|
for (const legacyFolder of DEPRECATED_UTILITY_SKILL_FOLDERS) {
|
|
1014
1059
|
await removeBestEffort(runtimePath(projectRoot, "skills", legacyFolder), true);
|
|
1015
1060
|
}
|
|
1061
|
+
for (const legacyFolder of DEPRECATED_STAGE_SKILL_FOLDERS) {
|
|
1062
|
+
await removeBestEffort(runtimePath(projectRoot, "skills", legacyFolder), true);
|
|
1063
|
+
}
|
|
1016
1064
|
for (const legacyAgentFile of DEPRECATED_AGENT_FILES) {
|
|
1017
1065
|
await removeBestEffort(runtimePath(projectRoot, "agents", legacyAgentFile));
|
|
1018
1066
|
}
|
|
@@ -1037,16 +1085,30 @@ async function cleanLegacyArtifacts(projectRoot) {
|
|
|
1037
1085
|
for (const legacyRuntimeDir of DEPRECATED_RUNTIME_DIRS) {
|
|
1038
1086
|
await removeBestEffort(runtimePath(projectRoot, legacyRuntimeDir), true);
|
|
1039
1087
|
}
|
|
1040
|
-
//
|
|
1041
|
-
//
|
|
1088
|
+
// Archive storage migration: `.cclaw/runs` is legacy and no longer a valid
|
|
1089
|
+
// archive root. Remove only when empty; otherwise keep it so users can
|
|
1090
|
+
// manually migrate or inspect old data.
|
|
1091
|
+
const legacyRunsDir = runtimePath(projectRoot, "runs");
|
|
1092
|
+
try {
|
|
1093
|
+
const entries = await fs.readdir(legacyRunsDir);
|
|
1094
|
+
if (entries.length === 0) {
|
|
1095
|
+
await fs.rm(legacyRunsDir, { recursive: true, force: true });
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
catch {
|
|
1099
|
+
// missing or unreadable legacy dir; keep best-effort behavior
|
|
1100
|
+
}
|
|
1101
|
+
// D-4 terminology migration: rename historical ideation artifact prefixes to
|
|
1102
|
+
// the canonical idea-* naming without deleting user-authored content.
|
|
1103
|
+
const legacyIdeaArtifactPattern = /^ideation-(.+\.md)$/u;
|
|
1042
1104
|
const artifactsDir = runtimePath(projectRoot, "artifacts");
|
|
1043
1105
|
try {
|
|
1044
1106
|
const entries = await fs.readdir(artifactsDir);
|
|
1045
1107
|
for (const entry of entries) {
|
|
1046
|
-
const match =
|
|
1108
|
+
const match = legacyIdeaArtifactPattern.exec(entry);
|
|
1047
1109
|
if (!match)
|
|
1048
1110
|
continue;
|
|
1049
|
-
const nextName = `
|
|
1111
|
+
const nextName = `idea-${match[1]}`;
|
|
1050
1112
|
const from = path.join(artifactsDir, entry);
|
|
1051
1113
|
const to = path.join(artifactsDir, nextName);
|
|
1052
1114
|
if (await exists(to)) {
|
|
@@ -1094,7 +1156,34 @@ async function cleanStaleFiles(projectRoot) {
|
|
|
1094
1156
|
// Keep user-owned custom assets under .cclaw/agents and .cclaw/skills.
|
|
1095
1157
|
// Legacy managed removals happen in cleanLegacyArtifacts() with explicit paths.
|
|
1096
1158
|
}
|
|
1159
|
+
async function assertExpectedHarnessShims(projectRoot, harnesses) {
|
|
1160
|
+
const expectedFiles = harnessShimFileNames();
|
|
1161
|
+
const expectedSkillFolders = harnessShimSkillNames();
|
|
1162
|
+
for (const harness of harnesses) {
|
|
1163
|
+
const adapter = HARNESS_ADAPTERS[harness];
|
|
1164
|
+
const base = path.join(projectRoot, adapter.commandDir);
|
|
1165
|
+
for (const fileName of expectedFiles) {
|
|
1166
|
+
const target = adapter.shimKind === "skill"
|
|
1167
|
+
? path.join(base, fileName.replace(/\.md$/u, ""), "SKILL.md")
|
|
1168
|
+
: path.join(base, fileName);
|
|
1169
|
+
if (!(await exists(target))) {
|
|
1170
|
+
throw new Error(`[sync fail-fast] Harness shim drift detected for ${harness}: missing ${target}. ` +
|
|
1171
|
+
`Run \`npx cclaw-cli sync\` again; if the file is still missing, inspect harness permissions/paths.`);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
if (adapter.shimKind === "skill") {
|
|
1175
|
+
for (const folder of expectedSkillFolders) {
|
|
1176
|
+
const skillPath = path.join(base, folder, "SKILL.md");
|
|
1177
|
+
if (!(await exists(skillPath))) {
|
|
1178
|
+
throw new Error(`[sync fail-fast] Harness skill shim drift detected for ${harness}: missing ${skillPath}. ` +
|
|
1179
|
+
`Run \`npx cclaw-cli sync\` again; if the issue persists, inspect generated .agents/skills surfaces.`);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1097
1185
|
async function materializeRuntime(projectRoot, config, forceStateReset, operation = "sync") {
|
|
1186
|
+
await warnStaleInitSentinel(projectRoot, operation);
|
|
1098
1187
|
const sentinelPath = await writeInitSentinel(projectRoot, operation);
|
|
1099
1188
|
const managedSession = await ManagedResourceSession.create({ projectRoot, operation });
|
|
1100
1189
|
setActiveManagedResourceSession(managedSession);
|
|
@@ -1110,25 +1199,51 @@ async function materializeRuntime(projectRoot, config, forceStateReset, operatio
|
|
|
1110
1199
|
writeRulebook(projectRoot)
|
|
1111
1200
|
]);
|
|
1112
1201
|
await writeState(projectRoot, config, forceStateReset);
|
|
1113
|
-
|
|
1202
|
+
try {
|
|
1203
|
+
await ensureRunSystem(projectRoot, { createIfMissing: false });
|
|
1204
|
+
}
|
|
1205
|
+
catch (error) {
|
|
1206
|
+
if (error instanceof CorruptFlowStateError) {
|
|
1207
|
+
throw new Error(`[sync fail-fast] Corrupt flow state detected: ${error.message} ` +
|
|
1208
|
+
`Resolve the quarantined flow-state file and re-run \`npx cclaw-cli sync\`.`);
|
|
1209
|
+
}
|
|
1210
|
+
throw error;
|
|
1211
|
+
}
|
|
1114
1212
|
await ensureKnowledgeStore(projectRoot);
|
|
1115
1213
|
await writeHooks(projectRoot, config);
|
|
1116
1214
|
await syncDisabledHarnessArtifacts(projectRoot, harnesses);
|
|
1117
1215
|
await syncManagedGitHooks(projectRoot, config);
|
|
1118
1216
|
await syncHarnessShims(projectRoot, harnesses);
|
|
1217
|
+
await assertExpectedHarnessShims(projectRoot, harnesses);
|
|
1119
1218
|
await writeCursorWorkflowRule(projectRoot, harnesses);
|
|
1120
1219
|
await ensureGitignore(projectRoot);
|
|
1121
1220
|
await managedSession.commit();
|
|
1122
1221
|
await fs.unlink(sentinelPath).catch(() => undefined);
|
|
1123
1222
|
}
|
|
1124
1223
|
catch (error) {
|
|
1125
|
-
// Leave the sentinel in place so
|
|
1224
|
+
// Leave the sentinel in place so the interrupted run is visible.
|
|
1126
1225
|
throw error;
|
|
1127
1226
|
}
|
|
1128
1227
|
finally {
|
|
1129
1228
|
setActiveManagedResourceSession(null);
|
|
1130
1229
|
}
|
|
1131
1230
|
}
|
|
1231
|
+
async function warnCodexHooksFeatureFlagIfDisabled(harnesses) {
|
|
1232
|
+
if (!harnesses.includes("codex"))
|
|
1233
|
+
return;
|
|
1234
|
+
const codexTomlPath = codexConfigPath();
|
|
1235
|
+
let existing;
|
|
1236
|
+
try {
|
|
1237
|
+
existing = await readCodexConfig(codexTomlPath);
|
|
1238
|
+
}
|
|
1239
|
+
catch (error) {
|
|
1240
|
+
process.stderr.write(`cclaw: could not read ${codexTomlPath} to validate codex_hooks flag: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
if (classifyCodexHooksFlag(existing) === "enabled")
|
|
1244
|
+
return;
|
|
1245
|
+
process.stderr.write(`cclaw: Codex hooks file written, but [features] codex_hooks is not true in ${codexTomlPath} — hooks are inert until you enable it.\n`);
|
|
1246
|
+
}
|
|
1132
1247
|
export async function initCclaw(options) {
|
|
1133
1248
|
if (options.harnesses !== undefined && options.harnesses.length === 0) {
|
|
1134
1249
|
throw new Error("Select at least one harness.");
|
|
@@ -1157,7 +1272,7 @@ export async function syncCclaw(projectRoot, options = {}) {
|
|
|
1157
1272
|
// Prefer detected harness markers over the hardcoded default list.
|
|
1158
1273
|
// Without this, a user running `cclaw sync` in a `.claude`-only
|
|
1159
1274
|
// project ends up with a config that also enables cursor/opencode/
|
|
1160
|
-
// codex, which then
|
|
1275
|
+
// codex, which then creates invalid harness expectations.
|
|
1161
1276
|
// Fall back to the previous default (config.harnesses) if no markers
|
|
1162
1277
|
// are found so brand-new projects still bootstrap cleanly.
|
|
1163
1278
|
const detected = await detectHarnesses(projectRoot);
|
|
@@ -1177,6 +1292,7 @@ export async function syncCclaw(projectRoot, options = {}) {
|
|
|
1177
1292
|
});
|
|
1178
1293
|
}
|
|
1179
1294
|
await materializeRuntime(projectRoot, config, false, "sync");
|
|
1295
|
+
await warnCodexHooksFeatureFlagIfDisabled(config.harnesses);
|
|
1180
1296
|
}
|
|
1181
1297
|
/**
|
|
1182
1298
|
* Refresh generated files in `.cclaw/` without touching user-authored
|
|
@@ -1274,20 +1390,28 @@ function isManagedRuntimeHookCommand(command) {
|
|
|
1274
1390
|
// Codex UserPromptSubmit non-blocking state nudge.
|
|
1275
1391
|
return /internal verify-current-state(?:\s|$)/u.test(normalized);
|
|
1276
1392
|
}
|
|
1277
|
-
async function removeManagedHookEntries(hookFilePath) {
|
|
1393
|
+
async function removeManagedHookEntries(hookFilePath, options = {}) {
|
|
1278
1394
|
if (!(await exists(hookFilePath)))
|
|
1279
1395
|
return;
|
|
1280
1396
|
let parsed = null;
|
|
1281
1397
|
try {
|
|
1282
1398
|
const raw = await fs.readFile(hookFilePath, "utf8");
|
|
1283
1399
|
const recovered = tryParseHookDocument(raw);
|
|
1284
|
-
|
|
1400
|
+
if (recovered === null) {
|
|
1401
|
+
if (options.failOnParseError === true) {
|
|
1402
|
+
throw new Error(`[sync fail-fast] Cannot strip managed hook entries from ${hookFilePath} — JSON is unparseable. ` +
|
|
1403
|
+
`Run \`rm ${hookFilePath}\` and rerun \`npx cclaw-cli sync\`.`);
|
|
1404
|
+
}
|
|
1405
|
+
return;
|
|
1406
|
+
}
|
|
1407
|
+
parsed = recovered.parsed;
|
|
1285
1408
|
}
|
|
1286
|
-
catch {
|
|
1409
|
+
catch (error) {
|
|
1410
|
+
if (options.failOnParseError === true) {
|
|
1411
|
+
throw new Error(`[sync fail-fast] Cannot strip managed hook entries from ${hookFilePath} — ${error instanceof Error ? error.message : String(error)}. Run \`rm ${hookFilePath}\` and rerun \`npx cclaw-cli sync\`.`);
|
|
1412
|
+
}
|
|
1287
1413
|
return;
|
|
1288
1414
|
}
|
|
1289
|
-
if (parsed === null)
|
|
1290
|
-
return;
|
|
1291
1415
|
const { updated, changed } = stripManagedHookCommands(parsed);
|
|
1292
1416
|
if (!changed)
|
|
1293
1417
|
return;
|
|
@@ -1369,7 +1493,7 @@ export async function uninstallCclaw(projectRoot) {
|
|
|
1369
1493
|
try {
|
|
1370
1494
|
const entries = await fs.readdir(codexSkillsRoot);
|
|
1371
1495
|
for (const entry of entries) {
|
|
1372
|
-
if (/^(?:cclaw-)?cc(?:-(?:next|view|finish|cancel|ops|
|
|
1496
|
+
if (/^(?:cclaw-)?cc(?:-(?:next|view|finish|cancel|ops|idea|brainstorm|scope|design|spec|plan|tdd|review|ship))?$/u.test(entry)) {
|
|
1373
1497
|
await fs.rm(path.join(codexSkillsRoot, entry), { recursive: true, force: true });
|
|
1374
1498
|
}
|
|
1375
1499
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type FlowState } from "../../flow-state.js";
|
|
2
|
+
import { type FlowStage } from "../../types.js";
|
|
3
|
+
import type { AdvanceStageArgs } from "./parsers.js";
|
|
4
|
+
import type { Writable } from "node:stream";
|
|
5
|
+
interface InternalIo {
|
|
6
|
+
stdout: Writable;
|
|
7
|
+
stderr: Writable;
|
|
8
|
+
}
|
|
9
|
+
interface InternalValidationReport {
|
|
10
|
+
ok: boolean;
|
|
11
|
+
stage: FlowStage;
|
|
12
|
+
delegation: {
|
|
13
|
+
satisfied: boolean;
|
|
14
|
+
missing: string[];
|
|
15
|
+
waived: string[];
|
|
16
|
+
missingEvidence: string[];
|
|
17
|
+
missingDispatchProof: string[];
|
|
18
|
+
legacyInferredCompletions: string[];
|
|
19
|
+
corruptEventLines: number[];
|
|
20
|
+
staleWorkers: string[];
|
|
21
|
+
expectedMode: string;
|
|
22
|
+
};
|
|
23
|
+
gates: {
|
|
24
|
+
ok: boolean;
|
|
25
|
+
complete: boolean;
|
|
26
|
+
issues: string[];
|
|
27
|
+
missingRequired: string[];
|
|
28
|
+
missingTriggeredConditional: string[];
|
|
29
|
+
};
|
|
30
|
+
completedStages: {
|
|
31
|
+
ok: boolean;
|
|
32
|
+
issues: string[];
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export declare function hydrateReviewLoopEvidenceFromArtifact(projectRoot: string, stage: FlowStage, track: FlowState["track"], selectedGateIds: string[], evidenceByGate: Record<string, string>): Promise<void>;
|
|
36
|
+
export declare function buildValidationReport(projectRoot: string, flowState: FlowState, options?: {
|
|
37
|
+
allowBlockedReviewRoute?: boolean;
|
|
38
|
+
}): Promise<InternalValidationReport>;
|
|
39
|
+
interface HarvestLearningsResult {
|
|
40
|
+
ok: boolean;
|
|
41
|
+
markerWritten: boolean;
|
|
42
|
+
parsedEntries: number;
|
|
43
|
+
appendedEntries: number;
|
|
44
|
+
skippedDuplicates: number;
|
|
45
|
+
details: string;
|
|
46
|
+
}
|
|
47
|
+
export declare function withLearningsHarvestMarker(artifactMarkdown: string, appendedEntries: number, skippedDuplicates: number): string;
|
|
48
|
+
export declare function harvestStageLearnings(projectRoot: string, stage: FlowStage, track: FlowState["track"]): Promise<HarvestLearningsResult>;
|
|
49
|
+
export declare function runAdvanceStage(projectRoot: string, args: AdvanceStageArgs, io: InternalIo): Promise<number>;
|
|
50
|
+
export {};
|