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
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
import { RUNTIME_ROOT } from "../constants.js";
|
|
2
|
-
import { nextStage as nextStageForTrack } from "../flow-state.js";
|
|
3
|
-
import { conversationLanguagePolicyMarkdown } from "./language-policy.js";
|
|
4
|
-
import { stageSchema } from "./stage-schema.js";
|
|
5
|
-
import { closeoutChainInline, closeoutNextCommandGuidance, closeoutSubstateInline, closeoutSubstateProtocolBullets } from "./closeout-guidance.js";
|
|
6
|
-
import { stageSkillFolder } from "./skills.js";
|
|
7
|
-
const NEXT_SKILL_FOLDER = "flow-next-step";
|
|
8
|
-
const NEXT_SKILL_NAME = "flow-next-step";
|
|
9
|
-
function flowStatePath() {
|
|
10
|
-
return `${RUNTIME_ROOT}/state/flow-state.json`;
|
|
11
|
-
}
|
|
12
|
-
function delegationLogPathLine() {
|
|
13
|
-
return `${RUNTIME_ROOT}/state/delegation-log.json`;
|
|
14
|
-
}
|
|
15
|
-
function reconciliationNoticesPathLine() {
|
|
16
|
-
return `${RUNTIME_ROOT}/state/reconciliation-notices.json`;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Single source of truth for how /cc-next should treat Ralph Loop status.
|
|
20
|
-
*
|
|
21
|
-
* IMPORTANT: Ralph Loop is a **progress indicator + soft pre-advance nudge**,
|
|
22
|
-
* not a hard gate. Hard enforcement always flows through flow-state.json
|
|
23
|
-
* gates via `stage-complete.mjs`. Both the command contract and the skill
|
|
24
|
-
* document render this same paragraph to prevent drift — see
|
|
25
|
-
* `tests/e2e/next-command-ralph-loop-contract.test.ts`.
|
|
26
|
-
*/
|
|
27
|
-
export const RALPH_LOOP_CONTRACT_MARKER = "ralph-loop-contract:v1";
|
|
28
|
-
export function ralphLoopContractSnippet() {
|
|
29
|
-
return `**Ralph Loop (tdd only).** When \`currentStage === "tdd"\`, read
|
|
30
|
-
\`${RUNTIME_ROOT}/state/ralph-loop.json\` (refreshed on every session-start
|
|
31
|
-
while the flow is in tdd) as a **progress indicator**:
|
|
32
|
-
|
|
33
|
-
- \`loopIteration\` — running count of RED → GREEN cycles already landed.
|
|
34
|
-
- \`acClosed\` — distinct acceptance-criterion IDs closed by GREEN rows
|
|
35
|
-
(populated from \`acIds\` in \`tdd-cycle-log.jsonl\`).
|
|
36
|
-
- \`redOpenSlices\` — slices with an unsatisfied RED.
|
|
37
|
-
|
|
38
|
-
Ralph Loop is a **soft pre-advance nudge**, not a gate: do not advance
|
|
39
|
-
toward review while \`redOpenSlices\` is non-empty unless the user
|
|
40
|
-
explicitly defers a slice. Hard gate enforcement always flows through
|
|
41
|
-
\`flow-state.json\` gates via \`node .cclaw/hooks/stage-complete.mjs <stage>\`;
|
|
42
|
-
Ralph Loop fields never gate-check on their own.
|
|
43
|
-
|
|
44
|
-
<!-- ${RALPH_LOOP_CONTRACT_MARKER} -->`;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Internal compatibility contract for /cc-next.
|
|
48
|
-
* /cc is the promoted public progression command; this remains as an internal/legacy alias.
|
|
49
|
-
*/
|
|
50
|
-
export function nextCommandContract() {
|
|
51
|
-
const flowPath = flowStatePath();
|
|
52
|
-
const skillRel = `${RUNTIME_ROOT}/skills/${NEXT_SKILL_FOLDER}/SKILL.md`;
|
|
53
|
-
const delegationPath = delegationLogPathLine();
|
|
54
|
-
const reconciliationNoticesPath = reconciliationNoticesPathLine();
|
|
55
|
-
return `# /cc-next
|
|
56
|
-
|
|
57
|
-
## Purpose
|
|
58
|
-
|
|
59
|
-
**Internal compatibility progression command.** \`/cc\` is the promoted public start/resume/continue command. This contract remains for legacy shims and internal references; read flow state, determine what to do:
|
|
60
|
-
|
|
61
|
-
- **Current stage not started / in progress** → load its skill and execute it.
|
|
62
|
-
- **Current stage complete (all gates passed)** → advance \`currentStage\` and load the next skill.
|
|
63
|
-
- **Ship complete** → continue the resumable ${closeoutChainInline()} closeout. Public habit: use \`/cc\`.
|
|
64
|
-
- **Flow complete** → report done after closeout has archived the run.
|
|
65
|
-
|
|
66
|
-
The public habit is \`/cc\`; this command is a compatibility/internal route for the same progression behavior. Stage command contracts are internal implementation details.
|
|
67
|
-
|
|
68
|
-
## HARD-GATE
|
|
69
|
-
|
|
70
|
-
${conversationLanguagePolicyMarkdown()}
|
|
71
|
-
- **Do not** invent gate completion: use only \`${flowPath}\` plus observable evidence in repo artifacts.
|
|
72
|
-
- **Do not** skip stages: advance only from \`currentStage\` to its configured successor.
|
|
73
|
-
- ${closeoutNextCommandGuidance()}
|
|
74
|
-
|
|
75
|
-
## Algorithm (mandatory)
|
|
76
|
-
|
|
77
|
-
1. Read **\`${flowPath}\`**. If missing → **BLOCKED** (state missing). Next action: run \`cclaw sync\` to safely regenerate generated runtime files, then \`cclaw doctor --explain\`; do not hand-edit state unless doctor says user repair is required.
|
|
78
|
-
2. Parse JSON. Capture \`currentStage\` and \`stageGateCatalog[currentStage]\`.
|
|
79
|
-
3. If \`staleStages[currentStage]\` exists, do not advance automatically. Report \`Blocked by: stale stage\`, the marker reason/rewindId, the stage artifact work to re-run, and clear only the current stage marker with \`cclaw internal rewind --ack <currentStage>\` after rework.
|
|
80
|
-
4. Read **\`${reconciliationNoticesPath}\`** when present. If it contains entries for \`activeRunId + currentStage\` and the listed gate is still blocked in \`stageGateCatalog[currentStage].blocked\`, emit \`Blocked by: reconciliation notice\` with gate id, reason, and next action \`cclaw doctor --reconcile-gates --explain\`. Clarify that reconciliation refreshes derived gate status only; it does not repair missing artifacts or tests.
|
|
81
|
-
5. Let \`G\` = \`requiredGates\` for **\`currentStage\`** from the stage schema.
|
|
82
|
-
6. Let \`catalog\` = \`stageGateCatalog[currentStage]\` from flow state.
|
|
83
|
-
7. **Satisfied** for gate id \`g\`: \`g\` in \`catalog.passed\` and \`g\` not in \`catalog.blocked\`.
|
|
84
|
-
8. Let \`M\` = \`mandatoryDelegations\` for \`currentStage\`.
|
|
85
|
-
9. If \`M\` is non-empty, inspect **\`${delegationPath}\`**. Treat as satisfied only if each mandatory agent is **completed** or **waived**.
|
|
86
|
-
10. For each satisfied mandatory delegation row, verify \`evidenceRefs\` is a non-empty array (unless status is \`waived\` with rationale). Missing evidenceRefs means delegation is unresolved.
|
|
87
|
-
11. If any mandatory delegation is missing and no waiver exists: **STOP** and ask the user whether to dispatch \`<agent>\` now or waive with rationale. State who must run, why the role is mandatory, whether the gap is ledger status, event-log dispatch proof, or artifact \`evidenceRefs\`, and do not mark gates passed while delegation is unresolved.
|
|
88
|
-
12. If \`currentStage === "review"\` and \`catalog.blocked\` includes \`review_criticals_resolved\`, treat this as a hard remediation branch: recommend the managed command \`cclaw internal rewind tdd "review_blocked_by_critical <finding-ids>"\`, and do not attempt to advance toward ship. After TDD rework, require \`cclaw internal rewind --ack tdd\` before continuing.
|
|
89
|
-
|
|
90
|
-
### Path A: Current stage is NOT complete (any gate unmet or delegation missing)
|
|
91
|
-
|
|
92
|
-
→ Load **\`${RUNTIME_ROOT}/skills/<skillFolder>/SKILL.md\`** for the current stage.
|
|
93
|
-
→ Execute that stage's protocol. The stage skill handles the full interaction including STOP points and gate tracking.
|
|
94
|
-
→ Stage completion must use \`node .cclaw/hooks/stage-complete.mjs <currentStage>\` (canonical), which validates delegations + gate evidence before mutating \`flow-state.json\`.
|
|
95
|
-
|
|
96
|
-
${ralphLoopContractSnippet()}
|
|
97
|
-
|
|
98
|
-
### Path B: Current stage IS complete (all gates passed, all delegations satisfied)
|
|
99
|
-
|
|
100
|
-
→ If current stage's \`next\` is **\`done\`**:
|
|
101
|
-
|
|
102
|
-
${closeoutSubstateProtocolBullets()}
|
|
103
|
-
|
|
104
|
-
Otherwise report **"Flow complete. All stages finished."** and stop.
|
|
105
|
-
|
|
106
|
-
→ Otherwise: load **\`${RUNTIME_ROOT}/skills/<skillFolder>/SKILL.md\`** for the successor stage. Execute that stage's protocol.
|
|
107
|
-
|
|
108
|
-
### Track-aware successor resolution
|
|
109
|
-
|
|
110
|
-
\`flow-state.json\` carries a \`track\` field (\`"quick"\`, \`"medium"\`, or \`"standard"\`) and a \`skippedStages\` array.
|
|
111
|
-
|
|
112
|
-
- If \`track === "quick"\`, the critical path is **spec → tdd → review → ship**. Quick skips ceremony, not safety: spec approval, RED/GREEN/REFACTOR evidence, review, and ship gates still apply. When advancing, skip any stage listed in \`skippedStages\` — i.e. after the current stage completes, pick the next stage that is NOT in \`skippedStages\`.
|
|
113
|
-
- If \`track === "medium"\`, the critical path is **brainstorm → spec → plan → tdd → review → ship**. Scope and design are intentionally skipped unless the run is reclassified to standard.
|
|
114
|
-
- If \`track === "standard"\`, advance through all 8 stages in their natural order.
|
|
115
|
-
- Never manually reintroduce a skipped stage mid-run. If evidence shows the track was wrong, stop and use the managed start-flow helper with \`--reclassify\`; only that managed reclassification may add upstream stages back into the active track.
|
|
116
|
-
|
|
117
|
-
## Resume Semantics
|
|
118
|
-
|
|
119
|
-
\`/cc\` in a **new session** resumes from where you left off; \`/cc-next\` is a compatibility alias:
|
|
120
|
-
- Flow-state records \`currentStage\` and which gates have passed.
|
|
121
|
-
- The stage skill reads upstream artifacts and picks up context.
|
|
122
|
-
- ${closeoutSubstateInline()} carries the post-ship substate, so a crashed
|
|
123
|
-
session during retro/compound/archive resumes at the exact step without
|
|
124
|
-
regenerating the retro draft.
|
|
125
|
-
- No special resume command needed — \`/cc\` is the public resume/progression command.
|
|
126
|
-
|
|
127
|
-
## Headless mode
|
|
128
|
-
|
|
129
|
-
When orchestrated by another skill/subagent, emit exactly one JSON envelope and
|
|
130
|
-
no narrative text:
|
|
131
|
-
|
|
132
|
-
\`\`\`json
|
|
133
|
-
{"version":"1","kind":"gate-result","stage":"<currentStage>","payload":{"command":"/cc-next","decision":"resume_or_advance","nextStage":"<nextStage>"},"emittedAt":"<ISO-8601>"}
|
|
134
|
-
\`\`\`
|
|
135
|
-
|
|
136
|
-
Validate envelopes with:
|
|
137
|
-
\`cclaw internal envelope-validate --stdin\`
|
|
138
|
-
|
|
139
|
-
## Primary skill
|
|
140
|
-
|
|
141
|
-
**${skillRel}** — full protocol and stage table.
|
|
142
|
-
|
|
143
|
-
## Surface reference
|
|
144
|
-
|
|
145
|
-
Use the flow-start skill plus \`.cclaw/state/flow-state.json\` for orientation when needed.
|
|
146
|
-
`;
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Skill body for /cc-next — the primary flow progression command.
|
|
150
|
-
*/
|
|
151
|
-
export function nextCommandSkillMarkdown() {
|
|
152
|
-
const flowPath = flowStatePath();
|
|
153
|
-
const delegationPath = delegationLogPathLine();
|
|
154
|
-
const reconciliationNoticesPath = reconciliationNoticesPathLine();
|
|
155
|
-
const stageRows = ["brainstorm", "scope", "design", "spec", "plan", "tdd", "review", "ship"]
|
|
156
|
-
.map((stage) => {
|
|
157
|
-
const skillMd = `${RUNTIME_ROOT}/skills/${stageSkillFolder(stage)}/SKILL.md`;
|
|
158
|
-
const standardNext = nextStageForTrack(stage, "standard") ?? "(terminal)";
|
|
159
|
-
const mediumNext = nextStageForTrack(stage, "medium") ?? "not in track";
|
|
160
|
-
const quickNext = nextStageForTrack(stage, "quick") ?? "not in track";
|
|
161
|
-
return `| \`${stage}\` | \`${standardNext}\` | \`${mediumNext}\` | \`${quickNext}\` | \`${skillMd}\` |`;
|
|
162
|
-
})
|
|
163
|
-
.join("\n");
|
|
164
|
-
const naturalStageRows = ["brainstorm", "scope", "design", "spec", "plan", "tdd", "review", "ship"]
|
|
165
|
-
.map((stage) => {
|
|
166
|
-
const schema = stageSchema(stage);
|
|
167
|
-
const next = schema.next === "done" ? "(terminal)" : schema.next;
|
|
168
|
-
const skillMd = `${RUNTIME_ROOT}/skills/${stageSkillFolder(stage)}/SKILL.md`;
|
|
169
|
-
return `| \`${stage}\` | \`${next}\` | \`${skillMd}\` |`;
|
|
170
|
-
})
|
|
171
|
-
.join("\n");
|
|
172
|
-
return `---
|
|
173
|
-
name: ${NEXT_SKILL_NAME}
|
|
174
|
-
description: "Internal compatibility progression command. Prefer /cc for start, resume, continue, and closeout."
|
|
175
|
-
---
|
|
176
|
-
|
|
177
|
-
# /cc-next — Flow Progression Compatibility
|
|
178
|
-
|
|
179
|
-
## Overview
|
|
180
|
-
|
|
181
|
-
\`/cc\` is the public command to drive the cclaw flow. \`/cc-next\` remains as an internal/compatibility alias for progression.
|
|
182
|
-
|
|
183
|
-
## Operator Output Contract
|
|
184
|
-
|
|
185
|
-
${conversationLanguagePolicyMarkdown()}
|
|
186
|
-
Default output should be compact, like OMC/OMX operator surfaces:
|
|
187
|
-
|
|
188
|
-
\`\`\`
|
|
189
|
-
Current: <currentStage or closeout.shipSubstate> (<track>)
|
|
190
|
-
Stage: <currentStage>
|
|
191
|
-
Gates: <passed>/<required> passed, <blocked> blocked
|
|
192
|
-
Delegations: <done>/<mandatory> done
|
|
193
|
-
Blocked by: <none | gate/delegation/reconciliation/stale/TDD/review/closeout ids>
|
|
194
|
-
Blocker category: <sync-recovery | user-decision | stage-work | delegation-proof | review-rework | closeout>
|
|
195
|
-
Next: <exact next action, usually /cc or one named remediation>
|
|
196
|
-
Evidence needed: <artifact/test/review/delegation evidence required to unblock>
|
|
197
|
-
\`\`\`
|
|
198
|
-
|
|
199
|
-
Only expand beyond this when blocked, when asking a structured question, or when
|
|
200
|
-
the user explicitly requests detail. When blocked, name the blocker category,
|
|
201
|
-
why it blocks progression, the one next command/action, and the exact proof that
|
|
202
|
-
would unblock it. Do not dump full artifacts in progression output.
|
|
203
|
-
|
|
204
|
-
**How it works:**
|
|
205
|
-
1. Reads \`flow-state.json\` to find \`currentStage\`
|
|
206
|
-
2. Checks if all gates for that stage are satisfied
|
|
207
|
-
3. If **not** → loads the stage skill and starts/resumes execution
|
|
208
|
-
4. If **yes** → advances to the next stage and loads its skill
|
|
209
|
-
|
|
210
|
-
**Resume:** \`/cc\` in a new session picks up from where \`flow-state.json\` says you are.
|
|
211
|
-
|
|
212
|
-
## HARD-GATE
|
|
213
|
-
|
|
214
|
-
Do **not** mark gates satisfied from memory alone. Cite **artifact evidence** (paths, excerpts). If evidence is missing, list the gate as **unmet**. Do **not** skip stages.
|
|
215
|
-
|
|
216
|
-
## Algorithm
|
|
217
|
-
|
|
218
|
-
### Step 1: Read state
|
|
219
|
-
|
|
220
|
-
1. Open **\`${flowPath}\`**.
|
|
221
|
-
2. Record \`currentStage\` and \`stageGateCatalog[currentStage]\`.
|
|
222
|
-
3. If \`staleStages[currentStage]\` exists, show \`Blocked by: stale stage\`, the marker reason/rewindId, re-run the stage, and clear only the current marker via \`cclaw internal rewind --ack <currentStage>\` before advancing.
|
|
223
|
-
4. If the file is missing or invalid JSON → **BLOCKED** (state missing/corrupt). Next action: run \`cclaw sync\` for safe regeneration of generated runtime files, then \`cclaw doctor --explain\`; do not hand-edit flow state unless doctor says user repair is required.
|
|
224
|
-
5. Read \`${reconciliationNoticesPath}\` when present. For entries matching \`activeRunId + currentStage\` whose gate is still in \`stageGateCatalog[currentStage].blocked\`, show \`Blocked by: reconciliation notice\` with gate id + reason before proceeding; \`cclaw doctor --reconcile-gates --explain\` refreshes derived gate-state only; it does not repair missing artifacts or tests.
|
|
225
|
-
|
|
226
|
-
### Step 2: Evaluate gates
|
|
227
|
-
|
|
228
|
-
For each gate id in \`requiredGates\` for \`currentStage\`:
|
|
229
|
-
- **Met** if in \`catalog.passed\` and not in \`catalog.blocked\`.
|
|
230
|
-
- **Unmet** otherwise.
|
|
231
|
-
|
|
232
|
-
Check \`mandatoryDelegations\` via **\`${delegationPath}\`** — satisfied only if **completed** or **waived**.
|
|
233
|
-
Also verify each completed mandatory delegation row has non-empty \`evidenceRefs\` (waived rows must include rationale).
|
|
234
|
-
If a mandatory delegation is missing and no waiver exists, **STOP** and ask:
|
|
235
|
-
(A) dispatch \`<agent>\` now, (B) waive with rationale, (C) cancel stage advance.
|
|
236
|
-
Explain who must run, why that role is mandatory for this stage, and whether the missing proof is ledger status, event-log dispatch proof, or artifact \`evidenceRefs\`. Waivers must include a user-visible safety reason.
|
|
237
|
-
|
|
238
|
-
If reconciliation warnings were emitted in Step 1, treat them as a pre-advance stop point: require explicit acknowledgement before continuing Path A or Path B.
|
|
239
|
-
|
|
240
|
-
### Step 3: Act
|
|
241
|
-
|
|
242
|
-
**Path A — stage NOT complete (any gate unmet):**
|
|
243
|
-
|
|
244
|
-
Load the current stage skill:
|
|
245
|
-
- \`${RUNTIME_ROOT}/skills/<skillFolder>/SKILL.md\`
|
|
246
|
-
|
|
247
|
-
Execute the stage protocol. The stage skill handles interaction, STOP points, gate tracking, and stage completion via \`node .cclaw/hooks/stage-complete.mjs <stage>\` (canonical flow-state mutation path).
|
|
248
|
-
|
|
249
|
-
${ralphLoopContractSnippet()}
|
|
250
|
-
|
|
251
|
-
Special-case for review: if \`review_criticals_resolved\` is in \`blocked\`, route to rework instead of looping review forever - recommend \`cclaw internal rewind tdd "review_blocked_by_critical <finding-ids>"\`, then \`cclaw internal rewind --ack tdd\` after TDD rework.
|
|
252
|
-
|
|
253
|
-
Special-case for TDD blockers: when \`06-tdd.md\` records \`NO_SOURCE_CONTEXT\`, \`NO_TEST_SURFACE\`, \`NO_IMPLEMENTABLE_SLICE\`, \`RED_NOT_EXPRESSIBLE\`, or \`NO_VCS_MODE\`, keep status BLOCKED and print \`Current\`, \`Blocked by\`, \`Next\`, \`Repair path\`, and \`Evidence needed\` instead of retrying speculative RED/GREEN work. RED blockers need a runnable failing test surface, GREEN blockers need passing full-suite evidence, REFACTOR blockers need behavior-preservation evidence.
|
|
254
|
-
|
|
255
|
-
**Path B — stage IS complete (all gates met, all delegations done):**
|
|
256
|
-
|
|
257
|
-
If \`next\` is \`done\`:
|
|
258
|
-
|
|
259
|
-
When \`currentStage\` is \`ship\`, automatically drive the **closeout chain**
|
|
260
|
-
by inspecting ${closeoutSubstateInline()}:
|
|
261
|
-
|
|
262
|
-
| shipSubstate | Action |
|
|
263
|
-
|-----------------------|-----------------------------------------------------|
|
|
264
|
-
| \`idle\` / missing | Flip to \`retro_review\` and start retro protocol |
|
|
265
|
-
| \`retro_review\` | Draft/update \`09-retro.md\`, ask accept/edit/skip |
|
|
266
|
-
| \`compound_review\` | Compound closeout: overlap scan, refresh/supersede, ask approve/skip |
|
|
267
|
-
| \`ready_to_archive\` | Run \`npx cclaw-cli archive\`; reset flow-state on success |
|
|
268
|
-
| \`archived\` | Report "run archived"; stop |
|
|
269
|
-
|
|
270
|
-
Each step owns its own state transition. \`/cc\` keeps retro and compound
|
|
271
|
-
in-session, then uses the archive runtime only at \`ready_to_archive\`.
|
|
272
|
-
|
|
273
|
-
Otherwise report **"Flow complete. All stages finished."** and stop.
|
|
274
|
-
|
|
275
|
-
Otherwise (non-terminal \`next\`): load the next stage skill and begin execution.
|
|
276
|
-
|
|
277
|
-
## Stage order
|
|
278
|
-
|
|
279
|
-
This table is the track-aware critical path. It must match \`flow-state.json.track\`; do not follow the natural schema edge when the active track skips a stage. Quick skips ceremony, not safety: spec approval, RED/GREEN/REFACTOR evidence, review, and ship gates still apply. After \`ship\`, \`/cc\` continues closeout via ${closeoutSubstateInline()}: ${closeoutChainInline()}.
|
|
280
|
-
|
|
281
|
-
| Stage | Standard next | Medium next | Quick next | Skill path |
|
|
282
|
-
|---|---|---|---|---|
|
|
283
|
-
${stageRows}
|
|
284
|
-
|
|
285
|
-
Natural schema edge reference for diagnostics only:
|
|
286
|
-
|
|
287
|
-
| Stage | Natural next | Skill path |
|
|
288
|
-
|---|---|---|
|
|
289
|
-
${naturalStageRows}
|
|
290
|
-
|
|
291
|
-
## Anti-patterns
|
|
292
|
-
|
|
293
|
-
- Advancing when \`blocked\` is non-empty for the current stage.
|
|
294
|
-
- Treating \`passed\` as trusted when artifact evidence contradicts it.
|
|
295
|
-
- Skipping **review** or **ship** because "the code looks fine".
|
|
296
|
-
- Loading a stage skill directly instead of using \`/cc\` for progression.
|
|
297
|
-
`;
|
|
298
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
export interface SeedShelfEntry {
|
|
2
|
-
fileName: string;
|
|
3
|
-
absPath: string;
|
|
4
|
-
relPath: string;
|
|
5
|
-
createdOn: string;
|
|
6
|
-
title: string;
|
|
7
|
-
triggerWhen: string[];
|
|
8
|
-
sourceStage: string | null;
|
|
9
|
-
sourceArtifact: string | null;
|
|
10
|
-
hypothesis: string | null;
|
|
11
|
-
action: string | null;
|
|
12
|
-
summary: string;
|
|
13
|
-
raw: string;
|
|
14
|
-
}
|
|
15
|
-
export interface SeedTemplateInput {
|
|
16
|
-
title: string;
|
|
17
|
-
triggerWhen: readonly string[];
|
|
18
|
-
hypothesis: string;
|
|
19
|
-
action: string;
|
|
20
|
-
sourceStage?: string;
|
|
21
|
-
sourceArtifact?: string;
|
|
22
|
-
createdAt?: Date;
|
|
23
|
-
}
|
|
24
|
-
export interface ResolvedSeedPath {
|
|
25
|
-
fileName: string;
|
|
26
|
-
absPath: string;
|
|
27
|
-
relPath: string;
|
|
28
|
-
}
|
|
29
|
-
export declare function seedShelfDir(projectRoot: string): string;
|
|
30
|
-
export declare function seedSlug(title: string): string;
|
|
31
|
-
export declare function seedFileName(title: string, createdAt?: Date): string;
|
|
32
|
-
export declare function resolveSeedPathForWrite(projectRoot: string, title: string, createdAt?: Date): Promise<ResolvedSeedPath>;
|
|
33
|
-
export declare function readSeedShelf(projectRoot: string): Promise<SeedShelfEntry[]>;
|
|
34
|
-
export declare function seedMatchesPrompt(seed: SeedShelfEntry, prompt: string): boolean;
|
|
35
|
-
export declare function findMatchingSeeds(projectRoot: string, prompt: string, maxMatches?: number): Promise<SeedShelfEntry[]>;
|
|
36
|
-
export declare function renderSeedTemplate(input: SeedTemplateInput): string;
|
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { parse } from "yaml";
|
|
4
|
-
import { RUNTIME_ROOT } from "../constants.js";
|
|
5
|
-
const SEED_FILE_NAME_PATTERN = /^SEED-(\d{4}-\d{2}-\d{2})-([a-z0-9]+(?:-[a-z0-9]+)*)(?:-(\d+))?\.md$/u;
|
|
6
|
-
const DEFAULT_MAX_MATCHES = 3;
|
|
7
|
-
const MAX_SEED_MATCHES = 10;
|
|
8
|
-
const MIN_TOKEN_OVERLAP = 2;
|
|
9
|
-
function isRecord(value) {
|
|
10
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11
|
-
}
|
|
12
|
-
function normalizeTriggerList(value) {
|
|
13
|
-
if (Array.isArray(value)) {
|
|
14
|
-
return value
|
|
15
|
-
.map((item) => typeof item === "string" || typeof item === "number" ? String(item).trim() : "")
|
|
16
|
-
.filter((item) => item.length > 0);
|
|
17
|
-
}
|
|
18
|
-
if (typeof value === "string") {
|
|
19
|
-
return value
|
|
20
|
-
.split(/,\s*/u)
|
|
21
|
-
.map((item) => item.trim())
|
|
22
|
-
.filter((item) => item.length > 0);
|
|
23
|
-
}
|
|
24
|
-
return [];
|
|
25
|
-
}
|
|
26
|
-
function parseSeedFrontmatter(raw) {
|
|
27
|
-
if (!raw.startsWith("---")) {
|
|
28
|
-
return { values: {}, body: raw };
|
|
29
|
-
}
|
|
30
|
-
const lines = raw.split(/\r?\n/u);
|
|
31
|
-
let closingIndex = -1;
|
|
32
|
-
for (let index = 1; index < lines.length; index += 1) {
|
|
33
|
-
if (lines[index]?.trim() === "---") {
|
|
34
|
-
closingIndex = index;
|
|
35
|
-
break;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
if (closingIndex < 0) {
|
|
39
|
-
return { values: {}, body: raw };
|
|
40
|
-
}
|
|
41
|
-
const frontmatterRaw = lines.slice(1, closingIndex).join("\n");
|
|
42
|
-
const body = lines.slice(closingIndex + 1).join("\n");
|
|
43
|
-
try {
|
|
44
|
-
const parsed = parse(frontmatterRaw);
|
|
45
|
-
if (isRecord(parsed)) {
|
|
46
|
-
return { values: parsed, body };
|
|
47
|
-
}
|
|
48
|
-
return { values: {}, body };
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
return { values: {}, body };
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
function firstHeading(body) {
|
|
55
|
-
const match = /^#\s+(.+)$/mu.exec(body);
|
|
56
|
-
if (!match)
|
|
57
|
-
return null;
|
|
58
|
-
const title = match[1]?.trim() ?? "";
|
|
59
|
-
return title.length > 0 ? title : null;
|
|
60
|
-
}
|
|
61
|
-
function firstNonEmptyParagraph(body) {
|
|
62
|
-
const lines = body.split(/\r?\n/u);
|
|
63
|
-
for (const line of lines) {
|
|
64
|
-
const trimmed = line.trim();
|
|
65
|
-
if (trimmed.length === 0)
|
|
66
|
-
continue;
|
|
67
|
-
if (/^#\s+/u.test(trimmed))
|
|
68
|
-
continue;
|
|
69
|
-
if (/^[-*]\s+/u.test(trimmed))
|
|
70
|
-
continue;
|
|
71
|
-
return trimmed;
|
|
72
|
-
}
|
|
73
|
-
return "";
|
|
74
|
-
}
|
|
75
|
-
function fromFileNameFallbackTitle(fileName) {
|
|
76
|
-
const stem = fileName.replace(/\.md$/u, "");
|
|
77
|
-
const withoutPrefix = stem.replace(/^SEED-\d{4}-\d{2}-\d{2}-/u, "");
|
|
78
|
-
return withoutPrefix
|
|
79
|
-
.split("-")
|
|
80
|
-
.filter((part) => part.length > 0 && !/^\d+$/u.test(part))
|
|
81
|
-
.map((part) => part[0]?.toUpperCase() + part.slice(1))
|
|
82
|
-
.join(" ")
|
|
83
|
-
.trim();
|
|
84
|
-
}
|
|
85
|
-
export function seedShelfDir(projectRoot) {
|
|
86
|
-
return path.join(projectRoot, RUNTIME_ROOT, "seeds");
|
|
87
|
-
}
|
|
88
|
-
export function seedSlug(title) {
|
|
89
|
-
const normalized = title
|
|
90
|
-
.toLowerCase()
|
|
91
|
-
.trim()
|
|
92
|
-
.replace(/[`"'“”‘’()[\]{}<>]/gu, " ")
|
|
93
|
-
.replace(/[^a-z0-9]+/gu, "-")
|
|
94
|
-
.replace(/^-+/u, "")
|
|
95
|
-
.replace(/-+$/u, "");
|
|
96
|
-
if (normalized.length === 0) {
|
|
97
|
-
return "seed";
|
|
98
|
-
}
|
|
99
|
-
return normalized.slice(0, 48);
|
|
100
|
-
}
|
|
101
|
-
function isoDate(value) {
|
|
102
|
-
return value.toISOString().slice(0, 10);
|
|
103
|
-
}
|
|
104
|
-
export function seedFileName(title, createdAt = new Date()) {
|
|
105
|
-
return `SEED-${isoDate(createdAt)}-${seedSlug(title)}.md`;
|
|
106
|
-
}
|
|
107
|
-
async function pathExists(absPath) {
|
|
108
|
-
try {
|
|
109
|
-
await fs.stat(absPath);
|
|
110
|
-
return true;
|
|
111
|
-
}
|
|
112
|
-
catch {
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
export async function resolveSeedPathForWrite(projectRoot, title, createdAt = new Date()) {
|
|
117
|
-
const seedsDir = seedShelfDir(projectRoot);
|
|
118
|
-
await fs.mkdir(seedsDir, { recursive: true });
|
|
119
|
-
const baseFile = seedFileName(title, createdAt);
|
|
120
|
-
const baseStem = baseFile.replace(/\.md$/u, "");
|
|
121
|
-
let candidate = baseFile;
|
|
122
|
-
let index = 2;
|
|
123
|
-
while (await pathExists(path.join(seedsDir, candidate))) {
|
|
124
|
-
candidate = `${baseStem}-${index}.md`;
|
|
125
|
-
index += 1;
|
|
126
|
-
}
|
|
127
|
-
return {
|
|
128
|
-
fileName: candidate,
|
|
129
|
-
absPath: path.join(seedsDir, candidate),
|
|
130
|
-
relPath: path.join(RUNTIME_ROOT, "seeds", candidate)
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
export async function readSeedShelf(projectRoot) {
|
|
134
|
-
const dir = seedShelfDir(projectRoot);
|
|
135
|
-
let names = [];
|
|
136
|
-
try {
|
|
137
|
-
names = await fs.readdir(dir);
|
|
138
|
-
}
|
|
139
|
-
catch {
|
|
140
|
-
return [];
|
|
141
|
-
}
|
|
142
|
-
const entries = [];
|
|
143
|
-
for (const fileName of names) {
|
|
144
|
-
if (!SEED_FILE_NAME_PATTERN.test(fileName))
|
|
145
|
-
continue;
|
|
146
|
-
const absPath = path.join(dir, fileName);
|
|
147
|
-
let raw = "";
|
|
148
|
-
try {
|
|
149
|
-
raw = await fs.readFile(absPath, "utf8");
|
|
150
|
-
}
|
|
151
|
-
catch {
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
const frontmatter = parseSeedFrontmatter(raw);
|
|
155
|
-
const title = (typeof frontmatter.values.title === "string" && frontmatter.values.title.trim().length > 0
|
|
156
|
-
? frontmatter.values.title.trim()
|
|
157
|
-
: null) ??
|
|
158
|
-
firstHeading(frontmatter.body) ??
|
|
159
|
-
fromFileNameFallbackTitle(fileName) ??
|
|
160
|
-
"Untitled seed";
|
|
161
|
-
const triggerWhen = normalizeTriggerList(frontmatter.values.trigger_when ?? frontmatter.values.triggerWhen);
|
|
162
|
-
const sourceStage = typeof frontmatter.values.source_stage === "string"
|
|
163
|
-
? frontmatter.values.source_stage
|
|
164
|
-
: typeof frontmatter.values.sourceStage === "string"
|
|
165
|
-
? frontmatter.values.sourceStage
|
|
166
|
-
: null;
|
|
167
|
-
const sourceArtifact = typeof frontmatter.values.source_artifact === "string"
|
|
168
|
-
? frontmatter.values.source_artifact
|
|
169
|
-
: typeof frontmatter.values.sourceArtifact === "string"
|
|
170
|
-
? frontmatter.values.sourceArtifact
|
|
171
|
-
: null;
|
|
172
|
-
const hypothesis = typeof frontmatter.values.hypothesis === "string" ? frontmatter.values.hypothesis : null;
|
|
173
|
-
const action = typeof frontmatter.values.action === "string" ? frontmatter.values.action : null;
|
|
174
|
-
const createdOn = SEED_FILE_NAME_PATTERN.exec(fileName)?.[1] ?? "1970-01-01";
|
|
175
|
-
const summary = firstNonEmptyParagraph(frontmatter.body);
|
|
176
|
-
entries.push({
|
|
177
|
-
fileName,
|
|
178
|
-
absPath,
|
|
179
|
-
relPath: path.join(RUNTIME_ROOT, "seeds", fileName),
|
|
180
|
-
createdOn,
|
|
181
|
-
title,
|
|
182
|
-
triggerWhen,
|
|
183
|
-
sourceStage,
|
|
184
|
-
sourceArtifact,
|
|
185
|
-
hypothesis,
|
|
186
|
-
action,
|
|
187
|
-
summary,
|
|
188
|
-
raw
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
entries.sort((a, b) => b.fileName.localeCompare(a.fileName));
|
|
192
|
-
return entries;
|
|
193
|
-
}
|
|
194
|
-
function normalizeMatchText(value) {
|
|
195
|
-
return value.toLowerCase().trim().replace(/\s+/gu, " ");
|
|
196
|
-
}
|
|
197
|
-
function tokenizeSeedText(value) {
|
|
198
|
-
if (!value)
|
|
199
|
-
return [];
|
|
200
|
-
return value
|
|
201
|
-
.toLowerCase()
|
|
202
|
-
.split(/[^a-z0-9]+/u)
|
|
203
|
-
.map((token) => token.trim())
|
|
204
|
-
.filter((token) => token.length >= 3);
|
|
205
|
-
}
|
|
206
|
-
function uniqueTokens(values) {
|
|
207
|
-
return new Set(values);
|
|
208
|
-
}
|
|
209
|
-
function exactTriggerMatch(seed, normalizedPrompt) {
|
|
210
|
-
if (normalizedPrompt.length === 0 || seed.triggerWhen.length === 0)
|
|
211
|
-
return false;
|
|
212
|
-
return seed.triggerWhen.some((trigger) => {
|
|
213
|
-
const normalizedTrigger = normalizeMatchText(trigger);
|
|
214
|
-
return normalizedTrigger.length > 0 && normalizedPrompt.includes(normalizedTrigger);
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
function seedContentTokens(seed) {
|
|
218
|
-
return uniqueTokens([
|
|
219
|
-
...tokenizeSeedText(seed.title),
|
|
220
|
-
...tokenizeSeedText(seed.summary),
|
|
221
|
-
...tokenizeSeedText(seed.hypothesis),
|
|
222
|
-
...tokenizeSeedText(seed.action)
|
|
223
|
-
]);
|
|
224
|
-
}
|
|
225
|
-
function tokenOverlap(seed, promptTokens) {
|
|
226
|
-
if (promptTokens.size === 0)
|
|
227
|
-
return 0;
|
|
228
|
-
const contentTokens = seedContentTokens(seed);
|
|
229
|
-
let overlap = 0;
|
|
230
|
-
for (const token of promptTokens) {
|
|
231
|
-
if (contentTokens.has(token))
|
|
232
|
-
overlap += 1;
|
|
233
|
-
}
|
|
234
|
-
return overlap;
|
|
235
|
-
}
|
|
236
|
-
export function seedMatchesPrompt(seed, prompt) {
|
|
237
|
-
const normalizedPrompt = normalizeMatchText(prompt);
|
|
238
|
-
if (exactTriggerMatch(seed, normalizedPrompt))
|
|
239
|
-
return true;
|
|
240
|
-
return tokenOverlap(seed, uniqueTokens(tokenizeSeedText(prompt))) >= MIN_TOKEN_OVERLAP;
|
|
241
|
-
}
|
|
242
|
-
export async function findMatchingSeeds(projectRoot, prompt, maxMatches = DEFAULT_MAX_MATCHES) {
|
|
243
|
-
const seeds = await readSeedShelf(projectRoot);
|
|
244
|
-
const normalizedPrompt = normalizeMatchText(prompt);
|
|
245
|
-
if (normalizedPrompt.length === 0)
|
|
246
|
-
return [];
|
|
247
|
-
const promptTokens = uniqueTokens(tokenizeSeedText(prompt));
|
|
248
|
-
const cappedMax = typeof maxMatches === "number" && Number.isFinite(maxMatches) && maxMatches > 0
|
|
249
|
-
? Math.min(MAX_SEED_MATCHES, Math.max(1, Math.floor(maxMatches)))
|
|
250
|
-
: DEFAULT_MAX_MATCHES;
|
|
251
|
-
const ranked = seeds
|
|
252
|
-
.map((seed, index) => {
|
|
253
|
-
const exact = exactTriggerMatch(seed, normalizedPrompt);
|
|
254
|
-
const overlap = tokenOverlap(seed, promptTokens);
|
|
255
|
-
return { seed, index, exact, overlap };
|
|
256
|
-
})
|
|
257
|
-
.filter((row) => row.exact || row.overlap >= MIN_TOKEN_OVERLAP);
|
|
258
|
-
ranked.sort((a, b) => {
|
|
259
|
-
if (a.exact !== b.exact)
|
|
260
|
-
return a.exact ? -1 : 1;
|
|
261
|
-
if (b.overlap !== a.overlap)
|
|
262
|
-
return b.overlap - a.overlap;
|
|
263
|
-
const recency = b.seed.createdOn.localeCompare(a.seed.createdOn);
|
|
264
|
-
if (recency !== 0)
|
|
265
|
-
return recency;
|
|
266
|
-
return a.index - b.index;
|
|
267
|
-
});
|
|
268
|
-
return ranked.slice(0, cappedMax).map((row) => row.seed);
|
|
269
|
-
}
|
|
270
|
-
export function renderSeedTemplate(input) {
|
|
271
|
-
const triggerWhen = [...input.triggerWhen].map((item) => item.trim()).filter((item) => item.length > 0);
|
|
272
|
-
const createdAt = input.createdAt ?? new Date();
|
|
273
|
-
const sourceStage = input.sourceStage?.trim() || "unknown";
|
|
274
|
-
const sourceArtifact = input.sourceArtifact?.trim() || "unknown";
|
|
275
|
-
return `---
|
|
276
|
-
title: ${input.title.trim()}
|
|
277
|
-
created_at: ${createdAt.toISOString()}
|
|
278
|
-
source_stage: ${sourceStage}
|
|
279
|
-
source_artifact: ${sourceArtifact}
|
|
280
|
-
trigger_when:
|
|
281
|
-
${triggerWhen.length > 0 ? triggerWhen.map((trigger) => ` - ${trigger}`).join("\n") : " - <trigger token>"}
|
|
282
|
-
hypothesis: ${input.hypothesis.trim()}
|
|
283
|
-
action: ${input.action.trim()}
|
|
284
|
-
---
|
|
285
|
-
|
|
286
|
-
# ${input.title.trim()}
|
|
287
|
-
|
|
288
|
-
## Why capture this seed
|
|
289
|
-
${input.hypothesis.trim()}
|
|
290
|
-
|
|
291
|
-
## Trigger when
|
|
292
|
-
${triggerWhen.length > 0 ? triggerWhen.map((trigger) => `- ${trigger}`).join("\n") : "- <trigger token>"}
|
|
293
|
-
|
|
294
|
-
## Suggested action
|
|
295
|
-
${input.action.trim()}
|
|
296
|
-
|
|
297
|
-
## Notes
|
|
298
|
-
- Expected payoff:
|
|
299
|
-
- Risks:
|
|
300
|
-
`;
|
|
301
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function stageCommonGuidanceMarkdown(): string;
|