cclaw-cli 3.0.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifact-linter/brainstorm.js +51 -2
- package/dist/artifact-linter/design.js +14 -3
- package/dist/artifact-linter/review-army.d.ts +25 -0
- package/dist/artifact-linter/review-army.js +155 -0
- package/dist/artifact-linter/review.js +13 -0
- package/dist/artifact-linter/scope.js +27 -48
- package/dist/artifact-linter/shared.d.ts +98 -11
- package/dist/artifact-linter/shared.js +280 -113
- package/dist/artifact-linter.d.ts +12 -2
- package/dist/artifact-linter.js +29 -13
- package/dist/content/core-agents.js +6 -1
- package/dist/content/examples.js +8 -0
- package/dist/content/hooks.js +2 -1
- package/dist/content/idea.js +14 -2
- package/dist/content/review-prompts.js +3 -3
- package/dist/content/skills-elicitation.js +61 -20
- package/dist/content/skills.js +19 -6
- package/dist/content/stage-schema.js +46 -18
- package/dist/content/stages/_lint-metadata/index.js +1 -2
- package/dist/content/stages/brainstorm.js +6 -3
- package/dist/content/stages/design.js +13 -12
- package/dist/content/stages/plan.js +1 -1
- package/dist/content/stages/review.js +21 -21
- package/dist/content/stages/schema-types.d.ts +9 -0
- package/dist/content/stages/scope.js +22 -20
- package/dist/content/stages/spec.js +3 -3
- package/dist/content/stages/tdd.js +1 -0
- package/dist/content/templates.d.ts +8 -1
- package/dist/content/templates.js +115 -43
- package/dist/flow-state.d.ts +12 -0
- package/dist/gate-evidence.d.ts +37 -1
- package/dist/gate-evidence.js +37 -3
- package/dist/harness-adapters.js +8 -0
- package/dist/install.js +22 -11
- package/dist/internal/advance-stage/advance.d.ts +1 -0
- package/dist/internal/advance-stage/advance.js +5 -2
- package/dist/internal/advance-stage/parsers.d.ts +8 -0
- package/dist/internal/advance-stage/parsers.js +27 -1
- package/dist/internal/advance-stage/start-flow.js +13 -0
- package/dist/run-persistence.js +14 -2
- package/package.json +1 -1
package/dist/flow-state.d.ts
CHANGED
|
@@ -93,6 +93,18 @@ export interface StageInteractionHint {
|
|
|
93
93
|
skipQuestions?: boolean;
|
|
94
94
|
sourceStage?: FlowStage;
|
|
95
95
|
recordedAt?: string;
|
|
96
|
+
/**
|
|
97
|
+
* Wave 23 (v5.0.0) — `/cc-ideate` handoff carry-forward.
|
|
98
|
+
* When a brainstorm run is started from a `/cc-ideate` recommendation,
|
|
99
|
+
* `start-flow` records the originating idea artifact so brainstorm can
|
|
100
|
+
* reuse the divergent + critique + rank work instead of re-generating it.
|
|
101
|
+
*
|
|
102
|
+
* `fromIdeaArtifact` is a workspace-relative POSIX path to
|
|
103
|
+
* `.cclaw/ideas/idea-YYYY-MM-DD-<slug>.md` (or wherever `/cc-ideate`
|
|
104
|
+
* wrote its artifact). `fromIdeaCandidateId` is the chosen `I-#` row.
|
|
105
|
+
*/
|
|
106
|
+
fromIdeaArtifact?: string;
|
|
107
|
+
fromIdeaCandidateId?: string;
|
|
96
108
|
}
|
|
97
109
|
export interface InitialFlowStateOptions {
|
|
98
110
|
activeRunId?: string;
|
package/dist/gate-evidence.d.ts
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
import type { FlowState, StageGateState } from "./flow-state.js";
|
|
2
2
|
import { type FlowStage } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Structured signal for the harness UI describing the adaptive
|
|
5
|
+
* elicitation Q&A floor for the current stage. Always present on
|
|
6
|
+
* brainstorm/scope/design verifications; null on other stages.
|
|
7
|
+
*
|
|
8
|
+
* Mirrors the `evaluateQaLogFloor` linter helper. Harness can render
|
|
9
|
+
* `count / min` progress, surface stop-signal/skip-questions hints, and
|
|
10
|
+
* differentiate between blocking and advisory.
|
|
11
|
+
*/
|
|
12
|
+
export interface QaLogFloorSignal {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
count: number;
|
|
15
|
+
/**
|
|
16
|
+
* Wave 23 (v5.0.0): always 0. The convergence floor no longer enforces
|
|
17
|
+
* a fixed count. Harness UIs may render `questionBudgetHint(track,
|
|
18
|
+
* stage).recommended` separately as a soft hint.
|
|
19
|
+
*/
|
|
20
|
+
min: number;
|
|
21
|
+
hasStopSignal: boolean;
|
|
22
|
+
/** Wave 23: always false. See `min` note above. */
|
|
23
|
+
liteShortCircuit: boolean;
|
|
24
|
+
skipQuestionsAdvisory: boolean;
|
|
25
|
+
blocking: boolean;
|
|
26
|
+
/** Forcing-question topics deemed addressed in `## Q&A Log`. */
|
|
27
|
+
forcingCovered: string[];
|
|
28
|
+
/** Forcing-question topics still pending (no Q&A row matched). */
|
|
29
|
+
forcingPending: string[];
|
|
30
|
+
/** Ralph-Loop convergence detector verdict for the last 2 rows. */
|
|
31
|
+
noNewDecisions: boolean;
|
|
32
|
+
}
|
|
3
33
|
export interface GateEvidenceCheckResult {
|
|
4
34
|
ok: boolean;
|
|
5
35
|
stage: FlowStage;
|
|
@@ -18,6 +48,8 @@ export interface GateEvidenceCheckResult {
|
|
|
18
48
|
missingRecommended: string[];
|
|
19
49
|
/** Triggered conditional gates that are not yet passed. */
|
|
20
50
|
missingTriggeredConditional: string[];
|
|
51
|
+
/** Q&A floor signal for adaptive elicitation stages, null otherwise. */
|
|
52
|
+
qaLogFloor: QaLogFloorSignal | null;
|
|
21
53
|
}
|
|
22
54
|
export interface CompletedStagesClosureResult {
|
|
23
55
|
ok: boolean;
|
|
@@ -29,7 +61,11 @@ export interface CompletedStagesClosureResult {
|
|
|
29
61
|
blocked: string[];
|
|
30
62
|
}>;
|
|
31
63
|
}
|
|
32
|
-
export
|
|
64
|
+
export interface VerifyCurrentStageGateEvidenceOptions {
|
|
65
|
+
/** Extra stage flags propagated from the in-flight CLI args (e.g. `--skip-questions`). */
|
|
66
|
+
extraStageFlags?: string[];
|
|
67
|
+
}
|
|
68
|
+
export declare function verifyCurrentStageGateEvidence(projectRoot: string, flowState: FlowState, options?: VerifyCurrentStageGateEvidenceOptions): Promise<GateEvidenceCheckResult>;
|
|
33
69
|
export declare function verifyCompletedStagesGateClosure(flowState: FlowState): CompletedStagesClosureResult;
|
|
34
70
|
export interface GateReconciliationResult {
|
|
35
71
|
stage: FlowStage;
|
package/dist/gate-evidence.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { checkReviewSecurityNoChangeAttestation, checkReviewVerdictConsistency, extractMarkdownSectionBody, lintArtifact, validateReviewArmy } from "./artifact-linter.js";
|
|
4
|
+
import { ELICITATION_STAGES, evaluateQaLogFloor } from "./artifact-linter/shared.js";
|
|
4
5
|
import { resolveArtifactPath } from "./artifact-paths.js";
|
|
5
6
|
import { RUNTIME_ROOT } from "./constants.js";
|
|
6
7
|
import { stageSchema } from "./content/stage-schema.js";
|
|
@@ -207,7 +208,7 @@ const DESIGN_RESEARCH_REQUIRED_SECTIONS = [
|
|
|
207
208
|
"Pitfalls & Risks",
|
|
208
209
|
"Synthesis"
|
|
209
210
|
];
|
|
210
|
-
export async function verifyCurrentStageGateEvidence(projectRoot, flowState) {
|
|
211
|
+
export async function verifyCurrentStageGateEvidence(projectRoot, flowState, options = {}) {
|
|
211
212
|
const stage = flowState.currentStage;
|
|
212
213
|
const schema = stageSchema(stage, flowState.track);
|
|
213
214
|
const catalog = flowState.stageGateCatalog[stage];
|
|
@@ -283,7 +284,9 @@ export async function verifyCurrentStageGateEvidence(projectRoot, flowState) {
|
|
|
283
284
|
const artifactPresent = await currentStageArtifactExists(projectRoot, stage, flowState.track);
|
|
284
285
|
const shouldValidateArtifact = artifactPresent || catalog.passed.length > 0 || flowState.completedStages.includes(stage);
|
|
285
286
|
if (shouldValidateArtifact) {
|
|
286
|
-
const lint = await lintArtifact(projectRoot, stage, flowState.track
|
|
287
|
+
const lint = await lintArtifact(projectRoot, stage, flowState.track, {
|
|
288
|
+
extraStageFlags: options.extraStageFlags
|
|
289
|
+
});
|
|
287
290
|
if (!lint.passed) {
|
|
288
291
|
const failedRequiredFindings = lint.findings
|
|
289
292
|
.filter((finding) => finding.required && !finding.found);
|
|
@@ -444,6 +447,36 @@ export async function verifyCurrentStageGateEvidence(projectRoot, flowState) {
|
|
|
444
447
|
issues.push(`stage "${stage}" is marked completed but has blocking blocked gates: ${blockingBlocked.join(", ")}.`);
|
|
445
448
|
}
|
|
446
449
|
}
|
|
450
|
+
let qaLogFloor = null;
|
|
451
|
+
if (ELICITATION_STAGES.has(stage)) {
|
|
452
|
+
let qaLogBody = null;
|
|
453
|
+
try {
|
|
454
|
+
const stageMd = await readStageArtifactMarkdown(projectRoot, stage, flowState.track);
|
|
455
|
+
qaLogBody = stageMd
|
|
456
|
+
? extractMarkdownSectionBody(stageMd, "Q&A Log")
|
|
457
|
+
: null;
|
|
458
|
+
}
|
|
459
|
+
catch {
|
|
460
|
+
qaLogBody = null;
|
|
461
|
+
}
|
|
462
|
+
const skipQuestionsHint = flowState.interactionHints?.[stage]?.skipQuestions === true ||
|
|
463
|
+
(options.extraStageFlags ?? []).includes("--skip-questions");
|
|
464
|
+
const floor = evaluateQaLogFloor(qaLogBody, flowState.track, stage, {
|
|
465
|
+
skipQuestions: skipQuestionsHint
|
|
466
|
+
});
|
|
467
|
+
qaLogFloor = {
|
|
468
|
+
ok: floor.ok,
|
|
469
|
+
count: floor.count,
|
|
470
|
+
min: floor.min,
|
|
471
|
+
hasStopSignal: floor.hasStopSignal,
|
|
472
|
+
liteShortCircuit: floor.liteShortCircuit,
|
|
473
|
+
skipQuestionsAdvisory: floor.skipQuestionsAdvisory,
|
|
474
|
+
blocking: !floor.ok && !floor.skipQuestionsAdvisory,
|
|
475
|
+
forcingCovered: floor.forcingCovered,
|
|
476
|
+
forcingPending: floor.forcingPending,
|
|
477
|
+
noNewDecisions: floor.noNewDecisions
|
|
478
|
+
};
|
|
479
|
+
}
|
|
447
480
|
return {
|
|
448
481
|
ok: issues.length === 0,
|
|
449
482
|
stage,
|
|
@@ -457,7 +490,8 @@ export async function verifyCurrentStageGateEvidence(projectRoot, flowState) {
|
|
|
457
490
|
complete,
|
|
458
491
|
missingRequired,
|
|
459
492
|
missingRecommended,
|
|
460
|
-
missingTriggeredConditional
|
|
493
|
+
missingTriggeredConditional,
|
|
494
|
+
qaLogFloor
|
|
461
495
|
};
|
|
462
496
|
}
|
|
463
497
|
export function verifyCompletedStagesGateClosure(flowState) {
|
package/dist/harness-adapters.js
CHANGED
|
@@ -345,6 +345,14 @@ Before responding to a coding request:
|
|
|
345
345
|
2. Use \`/cc\` to start, resume, or continue the flow.
|
|
346
346
|
3. If no stage applies, respond normally.
|
|
347
347
|
|
|
348
|
+
### Cclaw Baseline (always-on)
|
|
349
|
+
|
|
350
|
+
Three rules apply to every cclaw stage in this project, regardless of which skills loaded:
|
|
351
|
+
|
|
352
|
+
1. **Q&A convergence before drafting** — for brainstorm / scope / design, walk the stage forcing questions one at a time via the harness-native question tool (Claude \`AskUserQuestion\`, Cursor \`AskQuestion\`, Codex \`request_user_input\`, Gemini \`ask_user\`). The \`qa_log_unconverged\` linter rule will block \`stage-complete\` when convergence has not been reached. Convergence is satisfied when ANY of: (a) all forcing-question topics are addressed in \`## Q&A Log\`, (b) the last 2 substantive rows produce no decision-changing impact (Ralph-Loop), or (c) an explicit user stop-signal row is recorded. The fixed count floor (10 for standard) was removed in Wave 23.
|
|
353
|
+
2. **Subagents run after Q&A approval** — mandatory subagents in brainstorm / scope / design (\`product-discovery\`, \`critic\`, \`planner\`, \`architect\`, \`test-author\`) run only AFTER the user approves the elicitation outcome. See each stage's "Run Phase: post-elicitation" rows in the materialized Automatic Subagent Dispatch table.
|
|
354
|
+
3. **No command-line echo to chat** — the user does not run cclaw helpers manually. Never paste \`node .cclaw/hooks/...\` invocations, \`--evidence-json '{...}'\` payloads, or shell hash commands (\`shasum\`, \`sha256sum\`, \`Get-FileHash\`, \`certutil\`, etc.) into chat. Run helpers via the tool layer; report only the resulting summary.
|
|
355
|
+
|
|
348
356
|
${ironLawsAgentsMdBlock()}
|
|
349
357
|
|
|
350
358
|
### Task Classification (before \`/cc\`)
|
package/dist/install.js
CHANGED
|
@@ -16,7 +16,7 @@ import { ironLawsSkillMarkdown } from "./content/iron-laws.js";
|
|
|
16
16
|
import { stageCompleteScript, startFlowScript, cancelRunScript, runHookCmdScript, delegationRecordScript, opencodePluginJs, claudeHooksJson, codexHooksJson, cursorHooksJson } from "./content/hooks.js";
|
|
17
17
|
import { nodeHookRuntimeScript } from "./content/node-hooks.js";
|
|
18
18
|
import { META_SKILL_NAME, usingCclawSkillMarkdown } from "./content/meta-skill.js";
|
|
19
|
-
import { ARTIFACT_TEMPLATES, CURSOR_WORKFLOW_RULE_MDC, RULEBOOK_MARKDOWN, buildRulesJson } from "./content/templates.js";
|
|
19
|
+
import { ARTIFACT_TEMPLATES, CURSOR_GUIDELINES_RULE_MDC, CURSOR_WORKFLOW_RULE_MDC, RULEBOOK_MARKDOWN, buildRulesJson } from "./content/templates.js";
|
|
20
20
|
import { STATE_CONTRACTS } from "./content/state-contracts.js";
|
|
21
21
|
import { REVIEW_PROMPTS } from "./content/review-prompts.js";
|
|
22
22
|
import { stageSkillFolder, stageSkillMarkdown, executingWavesSkillMarkdown } from "./content/skills.js";
|
|
@@ -37,6 +37,7 @@ import { CorruptFlowStateError, ensureRunSystem } from "./runs.js";
|
|
|
37
37
|
import { FLOW_STAGES } from "./types.js";
|
|
38
38
|
const OPENCODE_PLUGIN_REL_PATH = ".opencode/plugins/cclaw-plugin.mjs";
|
|
39
39
|
const CURSOR_RULE_REL_PATH = ".cursor/rules/cclaw-workflow.mdc";
|
|
40
|
+
const CURSOR_GUIDELINES_REL_PATH = ".cursor/rules/cclaw-guidelines.mdc";
|
|
40
41
|
const GIT_HOOK_MANAGED_MARKER = "cclaw-managed-git-hook";
|
|
41
42
|
const GIT_HOOK_RUNTIME_REL_DIR = `${RUNTIME_ROOT}/hooks/git`;
|
|
42
43
|
const INIT_SENTINEL_FILE = ".init-in-progress";
|
|
@@ -942,17 +943,22 @@ async function writeRulebook(projectRoot) {
|
|
|
942
943
|
}
|
|
943
944
|
async function writeCursorWorkflowRule(projectRoot, harnesses) {
|
|
944
945
|
const rulePath = path.join(projectRoot, CURSOR_RULE_REL_PATH);
|
|
946
|
+
const guidelinesPath = path.join(projectRoot, CURSOR_GUIDELINES_REL_PATH);
|
|
945
947
|
if (!harnesses.includes("cursor")) {
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
948
|
+
for (const target of [rulePath, guidelinesPath]) {
|
|
949
|
+
try {
|
|
950
|
+
await fs.rm(target, { force: true });
|
|
951
|
+
}
|
|
952
|
+
catch {
|
|
953
|
+
// best-effort cleanup
|
|
954
|
+
}
|
|
951
955
|
}
|
|
952
956
|
return;
|
|
953
957
|
}
|
|
954
958
|
await ensureDir(path.dirname(rulePath));
|
|
955
959
|
await writeFileSafe(rulePath, CURSOR_WORKFLOW_RULE_MDC);
|
|
960
|
+
await ensureDir(path.dirname(guidelinesPath));
|
|
961
|
+
await writeFileSafe(guidelinesPath, CURSOR_GUIDELINES_RULE_MDC);
|
|
956
962
|
}
|
|
957
963
|
async function syncDisabledHarnessArtifacts(projectRoot, harnesses) {
|
|
958
964
|
const enabled = new Set(harnesses);
|
|
@@ -1464,11 +1470,16 @@ export async function uninstallCclaw(projectRoot) {
|
|
|
1464
1470
|
}
|
|
1465
1471
|
}
|
|
1466
1472
|
await removeManagedOpenCodePluginConfig(projectRoot, OPENCODE_PLUGIN_REL_PATH);
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1473
|
+
for (const target of [
|
|
1474
|
+
path.join(projectRoot, CURSOR_RULE_REL_PATH),
|
|
1475
|
+
path.join(projectRoot, CURSOR_GUIDELINES_REL_PATH)
|
|
1476
|
+
]) {
|
|
1477
|
+
try {
|
|
1478
|
+
await fs.rm(target, { force: true });
|
|
1479
|
+
}
|
|
1480
|
+
catch {
|
|
1481
|
+
// best-effort cleanup
|
|
1482
|
+
}
|
|
1472
1483
|
}
|
|
1473
1484
|
const managedDirs = [
|
|
1474
1485
|
".claude/hooks",
|
|
@@ -35,6 +35,7 @@ interface InternalValidationReport {
|
|
|
35
35
|
export declare function hydrateReviewLoopEvidenceFromArtifact(projectRoot: string, stage: FlowStage, track: FlowState["track"], selectedGateIds: string[], evidenceByGate: Record<string, string>): Promise<void>;
|
|
36
36
|
export declare function buildValidationReport(projectRoot: string, flowState: FlowState, options?: {
|
|
37
37
|
allowBlockedReviewRoute?: boolean;
|
|
38
|
+
extraStageFlags?: string[];
|
|
38
39
|
}): Promise<InternalValidationReport>;
|
|
39
40
|
interface HarvestLearningsResult {
|
|
40
41
|
ok: boolean;
|
|
@@ -89,7 +89,9 @@ export async function hydrateReviewLoopEvidenceFromArtifact(projectRoot, stage,
|
|
|
89
89
|
}
|
|
90
90
|
export async function buildValidationReport(projectRoot, flowState, options = {}) {
|
|
91
91
|
const delegation = await checkMandatoryDelegations(projectRoot, flowState.currentStage);
|
|
92
|
-
const gates = await verifyCurrentStageGateEvidence(projectRoot, flowState
|
|
92
|
+
const gates = await verifyCurrentStageGateEvidence(projectRoot, flowState, {
|
|
93
|
+
extraStageFlags: options.extraStageFlags
|
|
94
|
+
});
|
|
93
95
|
const completedStages = verifyCompletedStagesGateClosure(flowState);
|
|
94
96
|
const blockedReviewRouteComplete = options.allowBlockedReviewRoute === true
|
|
95
97
|
&& flowState.currentStage === "review"
|
|
@@ -332,7 +334,8 @@ export async function runAdvanceStage(projectRoot, args, io) {
|
|
|
332
334
|
}
|
|
333
335
|
};
|
|
334
336
|
const validation = await buildValidationReport(projectRoot, candidateState, {
|
|
335
|
-
allowBlockedReviewRoute: blockedReviewRoute
|
|
337
|
+
allowBlockedReviewRoute: blockedReviewRoute,
|
|
338
|
+
extraStageFlags: args.skipQuestions ? ["--skip-questions"] : undefined
|
|
336
339
|
});
|
|
337
340
|
if (!validation.ok) {
|
|
338
341
|
const ledgerForDiag = await readDelegationLedger(projectRoot).catch(() => ({ entries: [] }));
|
|
@@ -39,6 +39,14 @@ export interface StartFlowArgs {
|
|
|
39
39
|
forceReset: boolean;
|
|
40
40
|
reclassify: boolean;
|
|
41
41
|
quiet: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Wave 23 (v5.0.0) — `/cc-ideate` handoff carry-forward.
|
|
44
|
+
* Workspace-relative POSIX path to `.cclaw/ideas/idea-YYYY-MM-DD-<slug>.md`
|
|
45
|
+
* (or wherever `/cc-ideate` wrote its artifact).
|
|
46
|
+
*/
|
|
47
|
+
fromIdeaArtifact?: string;
|
|
48
|
+
/** Optional `I-#` row id chosen from the idea artifact's ranked list. */
|
|
49
|
+
fromIdeaCandidateId?: string;
|
|
42
50
|
}
|
|
43
51
|
export interface CancelRunArgs {
|
|
44
52
|
reason: string;
|
|
@@ -211,6 +211,8 @@ export function parseStartFlowArgs(tokens) {
|
|
|
211
211
|
let forceReset = false;
|
|
212
212
|
let reclassify = false;
|
|
213
213
|
let quiet = false;
|
|
214
|
+
let fromIdeaArtifact;
|
|
215
|
+
let fromIdeaCandidateId;
|
|
214
216
|
for (let i = 0; i < tokens.length; i += 1) {
|
|
215
217
|
const token = tokens[i];
|
|
216
218
|
const nextToken = tokens[i + 1];
|
|
@@ -259,12 +261,36 @@ export function parseStartFlowArgs(tokens) {
|
|
|
259
261
|
stack = readValue("--stack").trim();
|
|
260
262
|
continue;
|
|
261
263
|
}
|
|
264
|
+
if (token === "--from-idea-artifact" || token.startsWith("--from-idea-artifact=")) {
|
|
265
|
+
const raw = readValue("--from-idea-artifact").trim();
|
|
266
|
+
fromIdeaArtifact = raw.length > 0 ? raw : undefined;
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
if (token === "--from-idea-candidate" || token.startsWith("--from-idea-candidate=")) {
|
|
270
|
+
const raw = readValue("--from-idea-candidate").trim();
|
|
271
|
+
fromIdeaCandidateId = raw.length > 0 ? raw : undefined;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
262
274
|
throw new Error(`Unknown flag for internal start-flow: ${token}`);
|
|
263
275
|
}
|
|
264
276
|
if (!track) {
|
|
265
277
|
throw new Error("internal start-flow requires --track=<standard|medium|quick>.");
|
|
266
278
|
}
|
|
267
|
-
|
|
279
|
+
if (fromIdeaCandidateId && !fromIdeaArtifact) {
|
|
280
|
+
throw new Error("--from-idea-candidate requires --from-idea-artifact=<path> to be set as well.");
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
track,
|
|
284
|
+
className,
|
|
285
|
+
prompt,
|
|
286
|
+
reason,
|
|
287
|
+
stack,
|
|
288
|
+
forceReset,
|
|
289
|
+
reclassify,
|
|
290
|
+
quiet,
|
|
291
|
+
fromIdeaArtifact,
|
|
292
|
+
fromIdeaCandidateId
|
|
293
|
+
};
|
|
268
294
|
}
|
|
269
295
|
export function parseCancelRunArgs(tokens) {
|
|
270
296
|
let reason;
|
|
@@ -119,6 +119,19 @@ export async function runStartFlow(projectRoot, args, io) {
|
|
|
119
119
|
else {
|
|
120
120
|
nextState = createInitialFlowState({ track: args.track });
|
|
121
121
|
}
|
|
122
|
+
if (args.fromIdeaArtifact) {
|
|
123
|
+
const existingHints = nextState.interactionHints ?? {};
|
|
124
|
+
const existingBrainstorm = existingHints.brainstorm ?? {};
|
|
125
|
+
nextState.interactionHints = {
|
|
126
|
+
...existingHints,
|
|
127
|
+
brainstorm: {
|
|
128
|
+
...existingBrainstorm,
|
|
129
|
+
fromIdeaArtifact: args.fromIdeaArtifact,
|
|
130
|
+
...(args.fromIdeaCandidateId ? { fromIdeaCandidateId: args.fromIdeaCandidateId } : {}),
|
|
131
|
+
recordedAt: new Date().toISOString()
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
122
135
|
await writeFlowState(projectRoot, nextState, { allowReset: true });
|
|
123
136
|
await appendIdeaArtifact(projectRoot, args, current);
|
|
124
137
|
if (!args.quiet) {
|
package/dist/run-persistence.js
CHANGED
|
@@ -246,13 +246,25 @@ function sanitizeInteractionHints(value) {
|
|
|
246
246
|
const skipQuestions = typed.skipQuestions === true ? true : undefined;
|
|
247
247
|
const sourceStage = isFlowStage(typed.sourceStage) ? typed.sourceStage : undefined;
|
|
248
248
|
const recordedAt = typeof typed.recordedAt === "string" ? typed.recordedAt : undefined;
|
|
249
|
-
|
|
249
|
+
const fromIdeaArtifact = typeof typed.fromIdeaArtifact === "string" && typed.fromIdeaArtifact.trim().length > 0
|
|
250
|
+
? typed.fromIdeaArtifact.trim()
|
|
251
|
+
: undefined;
|
|
252
|
+
const fromIdeaCandidateId = typeof typed.fromIdeaCandidateId === "string" && typed.fromIdeaCandidateId.trim().length > 0
|
|
253
|
+
? typed.fromIdeaCandidateId.trim()
|
|
254
|
+
: undefined;
|
|
255
|
+
if (skipQuestions !== true &&
|
|
256
|
+
!sourceStage &&
|
|
257
|
+
!recordedAt &&
|
|
258
|
+
!fromIdeaArtifact &&
|
|
259
|
+
!fromIdeaCandidateId) {
|
|
250
260
|
continue;
|
|
251
261
|
}
|
|
252
262
|
out[stage] = {
|
|
253
263
|
...(skipQuestions ? { skipQuestions } : {}),
|
|
254
264
|
...(sourceStage ? { sourceStage } : {}),
|
|
255
|
-
...(recordedAt ? { recordedAt } : {})
|
|
265
|
+
...(recordedAt ? { recordedAt } : {}),
|
|
266
|
+
...(fromIdeaArtifact ? { fromIdeaArtifact } : {}),
|
|
267
|
+
...(fromIdeaCandidateId ? { fromIdeaCandidateId } : {})
|
|
256
268
|
};
|
|
257
269
|
}
|
|
258
270
|
return out;
|