cclaw-cli 0.48.35 → 0.51.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/README.md +54 -82
- package/dist/artifact-linter.d.ts +4 -0
- package/dist/artifact-linter.js +24 -3
- package/dist/cli.d.ts +1 -19
- package/dist/cli.js +49 -495
- package/dist/constants.d.ts +2 -13
- package/dist/constants.js +1 -46
- package/dist/content/closeout-guidance.d.ts +14 -0
- package/dist/content/closeout-guidance.js +42 -0
- package/dist/content/core-agents.js +51 -9
- package/dist/content/decision-protocol.d.ts +12 -0
- package/dist/content/decision-protocol.js +20 -0
- package/dist/content/diff-command.d.ts +1 -2
- package/dist/content/diff-command.js +8 -94
- package/dist/content/examples.d.ts +4 -10
- package/dist/content/examples.js +10 -20
- package/dist/content/hook-events.js +2 -2
- package/dist/content/hook-inline-snippets.d.ts +5 -2
- package/dist/content/hook-inline-snippets.js +33 -1
- package/dist/content/hook-manifest.d.ts +3 -4
- package/dist/content/hook-manifest.js +11 -12
- package/dist/content/hooks.js +2 -0
- package/dist/content/ideate-command.d.ts +2 -0
- package/dist/content/ideate-command.js +31 -25
- package/dist/content/iron-laws.d.ts +5 -5
- package/dist/content/iron-laws.js +5 -5
- package/dist/content/learnings.d.ts +3 -4
- package/dist/content/learnings.js +24 -50
- package/dist/content/meta-skill.js +31 -24
- package/dist/content/next-command.js +38 -38
- package/dist/content/node-hooks.js +17 -343
- package/dist/content/opencode-plugin.js +2 -100
- package/dist/content/research-playbooks.js +14 -14
- package/dist/content/review-loop.d.ts +2 -0
- package/dist/content/review-loop.js +8 -0
- package/dist/content/session-hooks.js +14 -46
- package/dist/content/skills.d.ts +0 -5
- package/dist/content/skills.js +53 -128
- package/dist/content/stage-common-guidance.d.ts +0 -1
- package/dist/content/stage-common-guidance.js +15 -14
- package/dist/content/stage-schema.d.ts +26 -1
- package/dist/content/stage-schema.js +121 -40
- package/dist/content/stages/_lint-metadata/index.js +9 -15
- package/dist/content/stages/brainstorm.js +22 -43
- package/dist/content/stages/design.js +37 -57
- package/dist/content/stages/plan.js +22 -13
- package/dist/content/stages/review.js +24 -27
- package/dist/content/stages/scope.js +34 -46
- package/dist/content/stages/ship.js +7 -4
- package/dist/content/stages/spec.js +20 -9
- package/dist/content/stages/tdd.js +64 -44
- package/dist/content/start-command.js +10 -12
- package/dist/content/status-command.d.ts +2 -7
- package/dist/content/status-command.js +19 -146
- package/dist/content/subagents.d.ts +0 -5
- package/dist/content/subagents.js +47 -28
- package/dist/content/templates.d.ts +1 -1
- package/dist/content/templates.js +126 -135
- package/dist/content/track-render-context.d.ts +17 -0
- package/dist/content/track-render-context.js +44 -0
- package/dist/content/tree-command.d.ts +1 -2
- package/dist/content/tree-command.js +4 -87
- package/dist/content/utility-skills.d.ts +2 -29
- package/dist/content/utility-skills.js +2 -1533
- package/dist/content/view-command.js +29 -11
- package/dist/delegation.d.ts +1 -1
- package/dist/delegation.js +5 -15
- package/dist/doctor-registry.js +20 -21
- package/dist/doctor.js +88 -408
- package/dist/flow-state.d.ts +3 -0
- package/dist/flow-state.js +2 -0
- package/dist/harness-adapters.d.ts +1 -1
- package/dist/harness-adapters.js +48 -57
- package/dist/install.js +128 -520
- package/dist/internal/advance-stage.js +3 -9
- package/dist/internal/compound-readiness.d.ts +1 -1
- package/dist/internal/compound-readiness.js +1 -1
- package/dist/internal/tdd-loop-status.d.ts +1 -1
- package/dist/internal/tdd-loop-status.js +1 -1
- package/dist/knowledge-store.d.ts +16 -10
- package/dist/knowledge-store.js +51 -15
- package/dist/policy.js +16 -109
- package/dist/run-archive.d.ts +4 -6
- package/dist/run-archive.js +15 -20
- package/dist/run-persistence.d.ts +2 -2
- package/dist/run-persistence.js +3 -9
- package/package.json +1 -2
- package/dist/content/archive-command.d.ts +0 -2
- package/dist/content/archive-command.js +0 -124
- package/dist/content/compound-command.d.ts +0 -5
- package/dist/content/compound-command.js +0 -193
- package/dist/content/contexts.d.ts +0 -9
- package/dist/content/contexts.js +0 -65
- package/dist/content/contracts.d.ts +0 -2
- package/dist/content/contracts.js +0 -51
- package/dist/content/doctor-references.d.ts +0 -2
- package/dist/content/doctor-references.js +0 -150
- package/dist/content/eval-scaffold.d.ts +0 -15
- package/dist/content/eval-scaffold.js +0 -370
- package/dist/content/feature-command.d.ts +0 -2
- package/dist/content/feature-command.js +0 -123
- package/dist/content/flow-map.d.ts +0 -23
- package/dist/content/flow-map.js +0 -134
- package/dist/content/harness-doc.d.ts +0 -2
- package/dist/content/harness-doc.js +0 -202
- package/dist/content/harness-playbooks.d.ts +0 -24
- package/dist/content/harness-playbooks.js +0 -393
- package/dist/content/harness-tool-refs.d.ts +0 -20
- package/dist/content/harness-tool-refs.js +0 -268
- package/dist/content/ops-command.d.ts +0 -2
- package/dist/content/ops-command.js +0 -71
- package/dist/content/protocols.d.ts +0 -7
- package/dist/content/protocols.js +0 -215
- package/dist/content/retro-command.d.ts +0 -2
- package/dist/content/retro-command.js +0 -165
- package/dist/content/rewind-command.d.ts +0 -2
- package/dist/content/rewind-command.js +0 -106
- package/dist/content/tdd-log-command.d.ts +0 -2
- package/dist/content/tdd-log-command.js +0 -85
- package/dist/eval/agents/single-shot.d.ts +0 -27
- package/dist/eval/agents/single-shot.js +0 -79
- package/dist/eval/agents/with-tools.d.ts +0 -44
- package/dist/eval/agents/with-tools.js +0 -261
- package/dist/eval/agents/workflow.d.ts +0 -31
- package/dist/eval/agents/workflow.js +0 -155
- package/dist/eval/baseline.d.ts +0 -38
- package/dist/eval/baseline.js +0 -282
- package/dist/eval/config-loader.d.ts +0 -14
- package/dist/eval/config-loader.js +0 -395
- package/dist/eval/corpus.d.ts +0 -30
- package/dist/eval/corpus.js +0 -330
- package/dist/eval/cost-guard.d.ts +0 -102
- package/dist/eval/cost-guard.js +0 -190
- package/dist/eval/diff.d.ts +0 -64
- package/dist/eval/diff.js +0 -323
- package/dist/eval/llm-client.d.ts +0 -176
- package/dist/eval/llm-client.js +0 -267
- package/dist/eval/mode.d.ts +0 -28
- package/dist/eval/mode.js +0 -61
- package/dist/eval/progress.d.ts +0 -83
- package/dist/eval/progress.js +0 -59
- package/dist/eval/report.d.ts +0 -11
- package/dist/eval/report.js +0 -181
- package/dist/eval/rubric-loader.d.ts +0 -20
- package/dist/eval/rubric-loader.js +0 -143
- package/dist/eval/runner.d.ts +0 -81
- package/dist/eval/runner.js +0 -746
- package/dist/eval/runs.d.ts +0 -41
- package/dist/eval/runs.js +0 -114
- package/dist/eval/sandbox.d.ts +0 -38
- package/dist/eval/sandbox.js +0 -137
- package/dist/eval/tools/glob.d.ts +0 -2
- package/dist/eval/tools/glob.js +0 -163
- package/dist/eval/tools/grep.d.ts +0 -2
- package/dist/eval/tools/grep.js +0 -152
- package/dist/eval/tools/index.d.ts +0 -7
- package/dist/eval/tools/index.js +0 -35
- package/dist/eval/tools/read.d.ts +0 -2
- package/dist/eval/tools/read.js +0 -122
- package/dist/eval/tools/types.d.ts +0 -49
- package/dist/eval/tools/types.js +0 -41
- package/dist/eval/tools/write.d.ts +0 -2
- package/dist/eval/tools/write.js +0 -92
- package/dist/eval/types.d.ts +0 -561
- package/dist/eval/types.js +0 -47
- package/dist/eval/verifiers/judge.d.ts +0 -40
- package/dist/eval/verifiers/judge.js +0 -256
- package/dist/eval/verifiers/rules.d.ts +0 -24
- package/dist/eval/verifiers/rules.js +0 -218
- package/dist/eval/verifiers/structural.d.ts +0 -14
- package/dist/eval/verifiers/structural.js +0 -171
- package/dist/eval/verifiers/traceability.d.ts +0 -23
- package/dist/eval/verifiers/traceability.js +0 -84
- package/dist/eval/verifiers/workflow-consistency.d.ts +0 -21
- package/dist/eval/verifiers/workflow-consistency.js +0 -225
- package/dist/eval/workflow-corpus.d.ts +0 -7
- package/dist/eval/workflow-corpus.js +0 -207
- package/dist/feature-system.d.ts +0 -42
- package/dist/feature-system.js +0 -432
- package/dist/internal/knowledge-digest.d.ts +0 -7
- package/dist/internal/knowledge-digest.js +0 -93
package/dist/cli.js
CHANGED
|
@@ -1,29 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { createReadStream, existsSync, realpathSync } from "node:fs";
|
|
3
|
-
import { spawn } from "node:child_process";
|
|
4
|
-
import fs from "node:fs/promises";
|
|
5
2
|
import process from "node:process";
|
|
6
3
|
import path from "node:path";
|
|
4
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
7
5
|
import { createInterface } from "node:readline/promises";
|
|
8
6
|
import { fileURLToPath } from "node:url";
|
|
9
|
-
import { FLOW_TRACKS, HARNESS_IDS } from "./types.js";
|
|
10
7
|
import { doctorChecks, doctorSucceeded } from "./doctor.js";
|
|
11
8
|
import { initCclaw, syncCclaw, uninstallCclaw, upgradeCclaw } from "./install.js";
|
|
12
9
|
import { error, info } from "./logger.js";
|
|
10
|
+
import { FLOW_TRACKS, HARNESS_IDS } from "./types.js";
|
|
13
11
|
import { archiveRun } from "./runs.js";
|
|
14
12
|
import { CCLAW_VERSION, RUNTIME_ROOT } from "./constants.js";
|
|
15
13
|
import { createDefaultConfig } from "./config.js";
|
|
16
14
|
import { detectHarnesses } from "./init-detect.js";
|
|
17
15
|
import { HARNESS_ADAPTERS } from "./harness-adapters.js";
|
|
18
16
|
import { classifyCodexHooksFlag, codexConfigPath, patchCodexHooksFlag, readCodexConfig, writeCodexConfig } from "./codex-feature-flag.js";
|
|
19
|
-
import { runEval } from "./eval/runner.js";
|
|
20
|
-
import { createStderrProgressLogger } from "./eval/progress.js";
|
|
21
|
-
import { writeBaselinesFromReport } from "./eval/baseline.js";
|
|
22
|
-
import { writeJsonReport, writeMarkdownReport } from "./eval/report.js";
|
|
23
|
-
import { formatDiffMarkdown, runEvalDiff } from "./eval/diff.js";
|
|
24
|
-
import { ensureRunDir, generateRunId, isRunAlive, listRuns, readRunStatus, resolveRunId, runLogPath, writeRunStatus } from "./eval/runs.js";
|
|
25
|
-
import { parseModeInput } from "./eval/mode.js";
|
|
26
|
-
import { FLOW_STAGES } from "./types.js";
|
|
27
17
|
import { runInternalCommand } from "./internal/advance-stage.js";
|
|
28
18
|
const INSTALLER_COMMANDS = [
|
|
29
19
|
"init",
|
|
@@ -32,7 +22,6 @@ const INSTALLER_COMMANDS = [
|
|
|
32
22
|
"upgrade",
|
|
33
23
|
"uninstall",
|
|
34
24
|
"archive",
|
|
35
|
-
"eval",
|
|
36
25
|
"internal"
|
|
37
26
|
];
|
|
38
27
|
export function usage() {
|
|
@@ -49,14 +38,18 @@ Commands:
|
|
|
49
38
|
Flags: --harnesses=<list> Comma list of harnesses (claude,cursor,opencode,codex).
|
|
50
39
|
--no-interactive Skip interactive prompts even on TTY (for CI/scripts).
|
|
51
40
|
sync Reconcile generated runtime files with the current config.
|
|
41
|
+
doctor Check install/runtime wiring and print concrete fixes for failures.
|
|
42
|
+
Flags: --explain Include docs pointers for every check.
|
|
43
|
+
--json Emit machine-readable check results.
|
|
44
|
+
--quiet Show only failing checks.
|
|
45
|
+
--only=<filter> Limit displayed checks (error,warning,hook:,state:,...).
|
|
46
|
+
--reconcile-gates Refresh derived gate status before checking.
|
|
52
47
|
upgrade Refresh generated files in .cclaw. Preserves your config.yaml.
|
|
53
|
-
archive Archive the active run and reset flow state for next
|
|
48
|
+
archive Archive the active run and reset flow state for the next run.
|
|
54
49
|
Flags: --name=<slug> Override archive folder suffix.
|
|
55
50
|
--skip-retro Skip retro gate only when runtime allows it.
|
|
56
51
|
--retro-reason=<txt> Required rationale with --skip-retro.
|
|
57
52
|
uninstall Remove .cclaw runtime and the generated harness shim files.
|
|
58
|
-
eval Run cclaw evals. Maintainer surface — see docs/evals.md.
|
|
59
|
-
Full flag reference: \`npx cclaw-cli eval --help\` or docs/evals.md.
|
|
60
53
|
|
|
61
54
|
Global flags:
|
|
62
55
|
-h, --help Show this help message and exit 0.
|
|
@@ -66,15 +59,16 @@ Examples:
|
|
|
66
59
|
npx cclaw-cli
|
|
67
60
|
npx cclaw-cli init --harnesses=claude,cursor --no-interactive
|
|
68
61
|
npx cclaw-cli sync
|
|
69
|
-
npx cclaw-cli archive --name=my-
|
|
62
|
+
npx cclaw-cli archive --name=my-run
|
|
70
63
|
npx cclaw-cli upgrade
|
|
71
|
-
npx cclaw-cli eval --dry-run
|
|
72
64
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
65
|
+
Happy-path work happens inside your harness via /cc, /cc-next,
|
|
66
|
+
/cc-ideate, and /cc-view. Doctor is an operator/support surface:
|
|
67
|
+
it verifies install/runtime wiring, but a real harness smoke test is
|
|
68
|
+
still needed to prove provider auth and model execution.
|
|
76
69
|
|
|
77
70
|
Docs: https://github.com/zuevrs/cclaw
|
|
71
|
+
Local: docs/config.md and docs/harnesses.md
|
|
78
72
|
Issues: https://github.com/zuevrs/cclaw/issues
|
|
79
73
|
`;
|
|
80
74
|
}
|
|
@@ -96,25 +90,6 @@ function parseTrack(raw) {
|
|
|
96
90
|
}
|
|
97
91
|
return trimmed;
|
|
98
92
|
}
|
|
99
|
-
function parseLegacyTier(raw) {
|
|
100
|
-
return parseModeInput(raw.toUpperCase(), {
|
|
101
|
-
source: "cli",
|
|
102
|
-
raw: `--tier=${raw}`
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
function parseEvalMode(raw) {
|
|
106
|
-
return parseModeInput(raw, {
|
|
107
|
-
source: "cli",
|
|
108
|
-
raw: `--mode=${raw}`
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
function parseEvalStage(raw) {
|
|
112
|
-
const trimmed = raw.trim();
|
|
113
|
-
if (!FLOW_STAGES.includes(trimmed)) {
|
|
114
|
-
throw new Error(`Unknown eval stage: ${raw}. Supported: ${FLOW_STAGES.join(", ")}`);
|
|
115
|
-
}
|
|
116
|
-
return trimmed;
|
|
117
|
-
}
|
|
118
93
|
function isInitPromptAllowed(ctx) {
|
|
119
94
|
return Boolean(process.stdin.isTTY && ctx.stdout.isTTY);
|
|
120
95
|
}
|
|
@@ -143,20 +118,14 @@ function buildInitSurfacePreview(harnesses) {
|
|
|
143
118
|
".cclaw/config.yaml",
|
|
144
119
|
".cclaw/commands/*.md",
|
|
145
120
|
".cclaw/skills/*/SKILL.md",
|
|
146
|
-
".cclaw/contexts/*.md",
|
|
147
121
|
".cclaw/templates/*",
|
|
148
122
|
".cclaw/agents/*.md",
|
|
149
123
|
".cclaw/hooks/*",
|
|
150
124
|
".cclaw/rules/**",
|
|
151
|
-
".cclaw/adapters/*.md",
|
|
152
|
-
".cclaw/custom-skills/README.md",
|
|
153
|
-
".cclaw/worktrees/**",
|
|
154
|
-
".cclaw/features/** (legacy snapshots, read-only migration)",
|
|
155
125
|
".cclaw/runs/**",
|
|
156
126
|
".cclaw/artifacts/**",
|
|
157
127
|
".cclaw/knowledge.jsonl",
|
|
158
128
|
".cclaw/state/*.json|*.jsonl",
|
|
159
|
-
".cclaw/references/**",
|
|
160
129
|
"AGENTS.md (managed block)"
|
|
161
130
|
];
|
|
162
131
|
for (const harness of harnesses) {
|
|
@@ -380,7 +349,7 @@ function printDoctorText(ctx, checks, options) {
|
|
|
380
349
|
if (!options.quiet) {
|
|
381
350
|
ctx.stdout.write(` details: ${check.details}\n`);
|
|
382
351
|
}
|
|
383
|
-
if (options.explain) {
|
|
352
|
+
if (!check.ok || options.explain) {
|
|
384
353
|
ctx.stdout.write(` fix: ${check.fix}\n`);
|
|
385
354
|
if (check.docRef) {
|
|
386
355
|
ctx.stdout.write(` docs: ${check.docRef}\n`);
|
|
@@ -400,18 +369,6 @@ function printDoctorText(ctx, checks, options) {
|
|
|
400
369
|
ctx.stdout.write("Doctor status: HEALTHY (no failing error checks)\n");
|
|
401
370
|
}
|
|
402
371
|
}
|
|
403
|
-
function resolveMaxCostOption(fromCli, env) {
|
|
404
|
-
if (fromCli !== undefined)
|
|
405
|
-
return { maxCostUsd: fromCli };
|
|
406
|
-
const raw = env.CCLAW_EVAL_MAX_COST_USD;
|
|
407
|
-
if (raw === undefined || raw.trim() === "")
|
|
408
|
-
return {};
|
|
409
|
-
const value = Number(raw);
|
|
410
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
411
|
-
throw new Error(`CCLAW_EVAL_MAX_COST_USD must be a positive number, got: ${raw}`);
|
|
412
|
-
}
|
|
413
|
-
return { maxCostUsd: value };
|
|
414
|
-
}
|
|
415
372
|
function parseArgs(argv) {
|
|
416
373
|
const parsed = {};
|
|
417
374
|
const helpFlag = argv.find((arg) => arg === "--help" || arg === "-h");
|
|
@@ -433,41 +390,34 @@ function parseArgs(argv) {
|
|
|
433
390
|
parsed.internalArgs = [...rest];
|
|
434
391
|
return parsed;
|
|
435
392
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
sawSubcommand = true;
|
|
458
|
-
}
|
|
459
|
-
else {
|
|
460
|
-
evalArgs.push(token);
|
|
461
|
-
}
|
|
462
|
-
continue;
|
|
463
|
-
}
|
|
464
|
-
evalArgs.push(token);
|
|
393
|
+
const flags = rest;
|
|
394
|
+
const isAllowedForCommand = (flag) => {
|
|
395
|
+
if (parsed.command === "init") {
|
|
396
|
+
return flag.startsWith("--harnesses=") ||
|
|
397
|
+
flag.startsWith("--track=") ||
|
|
398
|
+
flag.startsWith("--profile=") ||
|
|
399
|
+
flag === "--interactive" ||
|
|
400
|
+
flag === "--no-interactive" ||
|
|
401
|
+
flag === "--dry-run";
|
|
402
|
+
}
|
|
403
|
+
if (parsed.command === "doctor") {
|
|
404
|
+
return flag === "--reconcile-gates" ||
|
|
405
|
+
flag === "--json" ||
|
|
406
|
+
flag === "--explain" ||
|
|
407
|
+
flag === "--quiet" ||
|
|
408
|
+
flag.startsWith("--only=");
|
|
409
|
+
}
|
|
410
|
+
if (parsed.command === "archive") {
|
|
411
|
+
return flag.startsWith("--name=") ||
|
|
412
|
+
flag === "--skip-retro" ||
|
|
413
|
+
flag.startsWith("--retro-reason=");
|
|
465
414
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
flags = remainder;
|
|
469
|
-
}
|
|
415
|
+
return false;
|
|
416
|
+
};
|
|
470
417
|
for (const flag of flags) {
|
|
418
|
+
if (!isAllowedForCommand(flag)) {
|
|
419
|
+
throw new Error(`Flag ${flag} is not supported for ${parsed.command ?? "this command"}.`);
|
|
420
|
+
}
|
|
471
421
|
if (flag.startsWith("--harnesses=")) {
|
|
472
422
|
parsed.harnesses = parseHarnesses(flag.replace("--harnesses=", ""));
|
|
473
423
|
continue;
|
|
@@ -523,281 +473,9 @@ function parseArgs(argv) {
|
|
|
523
473
|
parsed.archiveSkipRetroReason = flag.replace("--retro-reason=", "").trim();
|
|
524
474
|
continue;
|
|
525
475
|
}
|
|
526
|
-
if (flag.startsWith("--stage=")) {
|
|
527
|
-
parsed.evalStage = parseEvalStage(flag.replace("--stage=", ""));
|
|
528
|
-
continue;
|
|
529
|
-
}
|
|
530
|
-
if (flag.startsWith("--mode=")) {
|
|
531
|
-
parsed.evalMode = parseEvalMode(flag.replace("--mode=", ""));
|
|
532
|
-
continue;
|
|
533
|
-
}
|
|
534
|
-
if (flag.startsWith("--tier=")) {
|
|
535
|
-
parsed.evalMode = parseLegacyTier(flag.replace("--tier=", ""));
|
|
536
|
-
continue;
|
|
537
|
-
}
|
|
538
|
-
if (flag === "--schema-only") {
|
|
539
|
-
parsed.evalSchemaOnly = true;
|
|
540
|
-
continue;
|
|
541
|
-
}
|
|
542
|
-
if (flag === "--rules") {
|
|
543
|
-
parsed.evalRules = true;
|
|
544
|
-
continue;
|
|
545
|
-
}
|
|
546
|
-
if (flag === "--judge") {
|
|
547
|
-
parsed.evalJudge = true;
|
|
548
|
-
continue;
|
|
549
|
-
}
|
|
550
|
-
if (flag === "--no-write") {
|
|
551
|
-
parsed.evalNoWrite = true;
|
|
552
|
-
continue;
|
|
553
|
-
}
|
|
554
|
-
if (flag === "--update-baseline") {
|
|
555
|
-
parsed.evalUpdateBaseline = true;
|
|
556
|
-
continue;
|
|
557
|
-
}
|
|
558
|
-
if (flag === "--confirm") {
|
|
559
|
-
parsed.evalConfirm = true;
|
|
560
|
-
continue;
|
|
561
|
-
}
|
|
562
|
-
if (flag === "--background") {
|
|
563
|
-
parsed.evalBackground = true;
|
|
564
|
-
continue;
|
|
565
|
-
}
|
|
566
|
-
if (flag.startsWith("--compare-model=")) {
|
|
567
|
-
const value = flag.replace("--compare-model=", "").trim();
|
|
568
|
-
if (value.length === 0) {
|
|
569
|
-
throw new Error(`--compare-model requires a non-empty model id (e.g. --compare-model=gpt-4o-mini).`);
|
|
570
|
-
}
|
|
571
|
-
parsed.evalCompareModel = value;
|
|
572
|
-
continue;
|
|
573
|
-
}
|
|
574
|
-
if (flag.startsWith("--max-cost-usd=")) {
|
|
575
|
-
const raw = flag.replace("--max-cost-usd=", "").trim();
|
|
576
|
-
const value = Number(raw);
|
|
577
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
578
|
-
throw new Error(`--max-cost-usd requires a positive number, got: ${raw}`);
|
|
579
|
-
}
|
|
580
|
-
parsed.evalMaxCostUsd = value;
|
|
581
|
-
continue;
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
// `--json` is shared between doctor and eval. Disambiguate by command.
|
|
585
|
-
if (parsed.command === "eval" && parsed.doctorJson === true) {
|
|
586
|
-
parsed.evalJson = true;
|
|
587
|
-
parsed.doctorJson = undefined;
|
|
588
|
-
}
|
|
589
|
-
// `--quiet` on `eval` silences the stderr progress logger. On doctor it
|
|
590
|
-
// continues to mean "print only failing checks" — the flag slot is the
|
|
591
|
-
// same, the semantics depend on which command owns the invocation.
|
|
592
|
-
if (parsed.command === "eval" && parsed.doctorQuiet === true) {
|
|
593
|
-
parsed.evalQuiet = true;
|
|
594
|
-
parsed.doctorQuiet = undefined;
|
|
595
476
|
}
|
|
596
477
|
return parsed;
|
|
597
478
|
}
|
|
598
|
-
/**
|
|
599
|
-
* Spawn `cclaw eval` (without `--background`) in a detached child process
|
|
600
|
-
* and return immediately. The child's stdout+stderr are piped to
|
|
601
|
-
* `.cclaw/evals/runs/<id>/run.log` so the user can attach later with
|
|
602
|
-
* `cclaw eval runs tail`. We do NOT wait for the child — the whole point
|
|
603
|
-
* is to free the terminal while a multi-minute workflow-mode run
|
|
604
|
-
* proceeds in the background.
|
|
605
|
-
*/
|
|
606
|
-
async function spawnBackgroundEval(parsed, ctx) {
|
|
607
|
-
const id = generateRunId();
|
|
608
|
-
await ensureRunDir(ctx.cwd, id);
|
|
609
|
-
const logPath = runLogPath(ctx.cwd, id);
|
|
610
|
-
const childArgv = process.argv.slice(2).filter((a) => a !== "--background");
|
|
611
|
-
const cliEntry = process.argv[1];
|
|
612
|
-
if (!cliEntry) {
|
|
613
|
-
error(ctx, "Could not resolve cclaw entrypoint for --background.");
|
|
614
|
-
return 1;
|
|
615
|
-
}
|
|
616
|
-
const logHandle = await fs.open(logPath, "a");
|
|
617
|
-
try {
|
|
618
|
-
const child = spawn(process.execPath, [cliEntry, ...childArgv], {
|
|
619
|
-
cwd: ctx.cwd,
|
|
620
|
-
detached: true,
|
|
621
|
-
stdio: ["ignore", logHandle.fd, logHandle.fd],
|
|
622
|
-
env: process.env
|
|
623
|
-
});
|
|
624
|
-
const pid = child.pid ?? -1;
|
|
625
|
-
await writeRunStatus(ctx.cwd, {
|
|
626
|
-
id,
|
|
627
|
-
startedAt: new Date().toISOString(),
|
|
628
|
-
pid,
|
|
629
|
-
argv: childArgv,
|
|
630
|
-
cwd: ctx.cwd,
|
|
631
|
-
state: "running"
|
|
632
|
-
});
|
|
633
|
-
child.unref();
|
|
634
|
-
const finalize = async (code) => {
|
|
635
|
-
const current = await readRunStatus(ctx.cwd, id);
|
|
636
|
-
if (!current)
|
|
637
|
-
return;
|
|
638
|
-
const exitCode = typeof code === "number" ? code : -1;
|
|
639
|
-
await writeRunStatus(ctx.cwd, {
|
|
640
|
-
...current,
|
|
641
|
-
endedAt: new Date().toISOString(),
|
|
642
|
-
exitCode,
|
|
643
|
-
state: exitCode === 0 ? "succeeded" : "failed"
|
|
644
|
-
});
|
|
645
|
-
};
|
|
646
|
-
child.on("exit", (code) => {
|
|
647
|
-
void finalize(code);
|
|
648
|
-
});
|
|
649
|
-
child.on("error", (err) => {
|
|
650
|
-
void writeRunStatus(ctx.cwd, {
|
|
651
|
-
id,
|
|
652
|
-
startedAt: new Date().toISOString(),
|
|
653
|
-
pid,
|
|
654
|
-
argv: childArgv,
|
|
655
|
-
cwd: ctx.cwd,
|
|
656
|
-
endedAt: new Date().toISOString(),
|
|
657
|
-
exitCode: -1,
|
|
658
|
-
state: "failed"
|
|
659
|
-
});
|
|
660
|
-
error(ctx, `Background eval failed to start: ${err.message}`);
|
|
661
|
-
});
|
|
662
|
-
ctx.stdout.write(`cclaw eval: background run id=${id} pid=${pid}\n` +
|
|
663
|
-
` log: ${logPath}\n` +
|
|
664
|
-
` tail: cclaw eval runs tail ${id}\n` +
|
|
665
|
-
` status: cclaw eval runs status ${id}\n`);
|
|
666
|
-
return 0;
|
|
667
|
-
}
|
|
668
|
-
finally {
|
|
669
|
-
await logHandle.close();
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
function formatRunRow(status) {
|
|
673
|
-
const ended = status.endedAt ? ` ended=${status.endedAt}` : "";
|
|
674
|
-
const exitCode = status.exitCode !== undefined ? ` exit=${status.exitCode}` : "";
|
|
675
|
-
const alive = status.state === "running" ? (isRunAlive(status) ? "" : " (stale)") : "";
|
|
676
|
-
return `${status.id} state=${status.state}${alive} pid=${status.pid} started=${status.startedAt}${ended}${exitCode}`;
|
|
677
|
-
}
|
|
678
|
-
async function runEvalRunsSubcommand(parsed, ctx) {
|
|
679
|
-
const args = parsed.evalArgs ?? [];
|
|
680
|
-
const action = args[0] ?? "list";
|
|
681
|
-
if (action === "list") {
|
|
682
|
-
const runs = await listRuns(ctx.cwd);
|
|
683
|
-
if (runs.length === 0) {
|
|
684
|
-
ctx.stdout.write("No eval runs recorded under .cclaw/evals/runs/.\n");
|
|
685
|
-
return 0;
|
|
686
|
-
}
|
|
687
|
-
if (parsed.evalJson === true) {
|
|
688
|
-
ctx.stdout.write(`${JSON.stringify(runs, null, 2)}\n`);
|
|
689
|
-
return 0;
|
|
690
|
-
}
|
|
691
|
-
for (const run of runs)
|
|
692
|
-
ctx.stdout.write(`${formatRunRow(run)}\n`);
|
|
693
|
-
return 0;
|
|
694
|
-
}
|
|
695
|
-
if (action === "status") {
|
|
696
|
-
const id = await resolveRunId(ctx.cwd, args[1]);
|
|
697
|
-
if (!id) {
|
|
698
|
-
error(ctx, `No such run: ${args[1] ?? "(none recorded)"}`);
|
|
699
|
-
return 1;
|
|
700
|
-
}
|
|
701
|
-
const status = await readRunStatus(ctx.cwd, id);
|
|
702
|
-
if (!status) {
|
|
703
|
-
error(ctx, `Run ${id} has no status file.`);
|
|
704
|
-
return 1;
|
|
705
|
-
}
|
|
706
|
-
if (parsed.evalJson === true) {
|
|
707
|
-
ctx.stdout.write(`${JSON.stringify(status, null, 2)}\n`);
|
|
708
|
-
}
|
|
709
|
-
else {
|
|
710
|
-
ctx.stdout.write(`${formatRunRow(status)}\n`);
|
|
711
|
-
ctx.stdout.write(`log: ${runLogPath(ctx.cwd, id)}\n`);
|
|
712
|
-
}
|
|
713
|
-
return status.state === "failed" ? 1 : 0;
|
|
714
|
-
}
|
|
715
|
-
if (action === "tail") {
|
|
716
|
-
const id = await resolveRunId(ctx.cwd, args[1]);
|
|
717
|
-
if (!id) {
|
|
718
|
-
error(ctx, `No such run: ${args[1] ?? "(none recorded)"}`);
|
|
719
|
-
return 1;
|
|
720
|
-
}
|
|
721
|
-
const logFile = runLogPath(ctx.cwd, id);
|
|
722
|
-
const stream = createReadStream(logFile, { encoding: "utf8" });
|
|
723
|
-
await new Promise((resolve, reject) => {
|
|
724
|
-
stream.on("data", (chunk) => ctx.stdout.write(chunk));
|
|
725
|
-
stream.on("end", () => resolve());
|
|
726
|
-
stream.on("error", reject);
|
|
727
|
-
});
|
|
728
|
-
return 0;
|
|
729
|
-
}
|
|
730
|
-
error(ctx, `Unknown \`cclaw eval runs\` action: ${action}. Use list | status | tail.`);
|
|
731
|
-
return 1;
|
|
732
|
-
}
|
|
733
|
-
/**
|
|
734
|
-
* Run the same corpus twice — once against the configured model, once
|
|
735
|
-
* against `--compare-model=<id>` — and print a summary comparing the
|
|
736
|
-
* two. Both reports are written to `.cclaw/evals/reports/` (unless
|
|
737
|
-
* `--no-write` is set) and a unified diff is emitted to stdout. Exit
|
|
738
|
-
* code is 1 when the override model regressed against the baseline
|
|
739
|
-
* model, 0 otherwise.
|
|
740
|
-
*/
|
|
741
|
-
async function runCompareModel(parsed, ctx, progress) {
|
|
742
|
-
const baselineOpts = {
|
|
743
|
-
projectRoot: ctx.cwd,
|
|
744
|
-
stage: parsed.evalStage,
|
|
745
|
-
mode: parsed.evalMode,
|
|
746
|
-
schemaOnly: parsed.evalSchemaOnly === true,
|
|
747
|
-
rules: parsed.evalRules === true,
|
|
748
|
-
judge: parsed.evalJudge === true,
|
|
749
|
-
...(progress ? { progress } : {}),
|
|
750
|
-
...resolveMaxCostOption(parsed.evalMaxCostUsd, process.env)
|
|
751
|
-
};
|
|
752
|
-
ctx.stderr.write(`[cclaw eval] compare: running baseline model...\n`);
|
|
753
|
-
const baseline = await runEval(baselineOpts);
|
|
754
|
-
if ("kind" in baseline) {
|
|
755
|
-
error(ctx, "--compare-model is incompatible with --dry-run.");
|
|
756
|
-
return 1;
|
|
757
|
-
}
|
|
758
|
-
ctx.stderr.write(`[cclaw eval] compare: running ${parsed.evalCompareModel} ...\n`);
|
|
759
|
-
const candidate = await runEval({
|
|
760
|
-
...baselineOpts,
|
|
761
|
-
modelOverride: parsed.evalCompareModel
|
|
762
|
-
});
|
|
763
|
-
if ("kind" in candidate) {
|
|
764
|
-
error(ctx, "--compare-model received an unexpected dry-run response.");
|
|
765
|
-
return 1;
|
|
766
|
-
}
|
|
767
|
-
if (parsed.evalNoWrite !== true) {
|
|
768
|
-
await writeJsonReport(ctx.cwd, baseline);
|
|
769
|
-
await writeMarkdownReport(ctx.cwd, baseline);
|
|
770
|
-
await writeJsonReport(ctx.cwd, candidate);
|
|
771
|
-
await writeMarkdownReport(ctx.cwd, candidate);
|
|
772
|
-
}
|
|
773
|
-
const passDelta = candidate.summary.passed - baseline.summary.passed;
|
|
774
|
-
const failDelta = candidate.summary.failed - baseline.summary.failed;
|
|
775
|
-
const costDelta = candidate.summary.totalCostUsd - baseline.summary.totalCostUsd;
|
|
776
|
-
if (parsed.evalJson === true) {
|
|
777
|
-
ctx.stdout.write(`${JSON.stringify({
|
|
778
|
-
baseline: {
|
|
779
|
-
model: baseline.model,
|
|
780
|
-
summary: baseline.summary
|
|
781
|
-
},
|
|
782
|
-
candidate: {
|
|
783
|
-
model: candidate.model,
|
|
784
|
-
summary: candidate.summary
|
|
785
|
-
},
|
|
786
|
-
delta: { passed: passDelta, failed: failDelta, costUsd: costDelta }
|
|
787
|
-
}, null, 2)}\n`);
|
|
788
|
-
}
|
|
789
|
-
else {
|
|
790
|
-
ctx.stdout.write(`cclaw eval compare-model:\n` +
|
|
791
|
-
` baseline ${baseline.model}: pass=${baseline.summary.passed}/${baseline.summary.totalCases} ` +
|
|
792
|
-
`fail=${baseline.summary.failed} cost=$${baseline.summary.totalCostUsd.toFixed(4)}\n` +
|
|
793
|
-
` candidate ${candidate.model}: pass=${candidate.summary.passed}/${candidate.summary.totalCases} ` +
|
|
794
|
-
`fail=${candidate.summary.failed} cost=$${candidate.summary.totalCostUsd.toFixed(4)}\n` +
|
|
795
|
-
` delta: passed=${passDelta >= 0 ? "+" : ""}${passDelta} ` +
|
|
796
|
-
`failed=${failDelta >= 0 ? "+" : ""}${failDelta} ` +
|
|
797
|
-
`cost=${costDelta >= 0 ? "+" : ""}$${costDelta.toFixed(4)}\n`);
|
|
798
|
-
}
|
|
799
|
-
return failDelta > 0 ? 1 : 0;
|
|
800
|
-
}
|
|
801
479
|
async function runCommand(parsed, ctx) {
|
|
802
480
|
if (parsed.showHelp) {
|
|
803
481
|
ctx.stdout.write(usage());
|
|
@@ -868,7 +546,8 @@ async function runCommand(parsed, ctx) {
|
|
|
868
546
|
if (parsed.doctorJson === true) {
|
|
869
547
|
const counts = doctorCountsBySeverity(filteredChecks);
|
|
870
548
|
ctx.stdout.write(`${JSON.stringify({
|
|
871
|
-
ok: doctorSucceeded(
|
|
549
|
+
ok: doctorSucceeded(filteredChecks),
|
|
550
|
+
globalOk: doctorSucceeded(checks),
|
|
872
551
|
filters: parsed.doctorOnly ?? [],
|
|
873
552
|
counts,
|
|
874
553
|
checks: filteredChecks
|
|
@@ -882,138 +561,13 @@ async function runCommand(parsed, ctx) {
|
|
|
882
561
|
printDoctorText(ctx, filteredChecks, { explain, quiet });
|
|
883
562
|
}
|
|
884
563
|
}
|
|
885
|
-
return doctorSucceeded(
|
|
564
|
+
return doctorSucceeded(filteredChecks) ? 0 : 2;
|
|
886
565
|
}
|
|
887
566
|
if (command === "upgrade") {
|
|
888
567
|
await upgradeCclaw(ctx.cwd);
|
|
889
568
|
info(ctx, "Upgraded .cclaw runtime and regenerated generated files");
|
|
890
569
|
return 0;
|
|
891
570
|
}
|
|
892
|
-
if (command === "eval" && parsed.evalSubcommand === "runs") {
|
|
893
|
-
return runEvalRunsSubcommand(parsed, ctx);
|
|
894
|
-
}
|
|
895
|
-
if (command === "eval" && parsed.evalBackground === true) {
|
|
896
|
-
return spawnBackgroundEval(parsed, ctx);
|
|
897
|
-
}
|
|
898
|
-
if (command === "eval" && parsed.evalSubcommand === "diff") {
|
|
899
|
-
const args = parsed.evalArgs ?? [];
|
|
900
|
-
if (args.length !== 2) {
|
|
901
|
-
error(ctx, `\`cclaw eval diff\` requires two arguments: <old> <new>. ` +
|
|
902
|
-
`Example: cclaw eval diff 0.26.0 latest`);
|
|
903
|
-
return 1;
|
|
904
|
-
}
|
|
905
|
-
const [oldSel, newSel] = args;
|
|
906
|
-
try {
|
|
907
|
-
const diff = await runEvalDiff({
|
|
908
|
-
projectRoot: ctx.cwd,
|
|
909
|
-
old: oldSel,
|
|
910
|
-
new: newSel
|
|
911
|
-
});
|
|
912
|
-
if (parsed.evalJson === true) {
|
|
913
|
-
ctx.stdout.write(`${JSON.stringify(diff, null, 2)}\n`);
|
|
914
|
-
}
|
|
915
|
-
else {
|
|
916
|
-
ctx.stdout.write(formatDiffMarkdown(diff));
|
|
917
|
-
}
|
|
918
|
-
return diff.regressed ? 1 : 0;
|
|
919
|
-
}
|
|
920
|
-
catch (err) {
|
|
921
|
-
error(ctx, err instanceof Error ? err.message : String(err));
|
|
922
|
-
return 1;
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
if (command === "eval") {
|
|
926
|
-
const wantProgress = parsed.evalQuiet !== true &&
|
|
927
|
-
parsed.dryRun !== true &&
|
|
928
|
-
parsed.evalJson !== true;
|
|
929
|
-
const progress = wantProgress
|
|
930
|
-
? createStderrProgressLogger({ writer: (s) => ctx.stderr.write(s) })
|
|
931
|
-
: undefined;
|
|
932
|
-
if (parsed.evalCompareModel !== undefined) {
|
|
933
|
-
return runCompareModel(parsed, ctx, progress);
|
|
934
|
-
}
|
|
935
|
-
const result = await runEval({
|
|
936
|
-
projectRoot: ctx.cwd,
|
|
937
|
-
stage: parsed.evalStage,
|
|
938
|
-
mode: parsed.evalMode,
|
|
939
|
-
schemaOnly: parsed.evalSchemaOnly === true,
|
|
940
|
-
rules: parsed.evalRules === true,
|
|
941
|
-
judge: parsed.evalJudge === true,
|
|
942
|
-
dryRun: parsed.dryRun === true,
|
|
943
|
-
...(progress ? { progress } : {}),
|
|
944
|
-
...resolveMaxCostOption(parsed.evalMaxCostUsd, process.env)
|
|
945
|
-
});
|
|
946
|
-
if ("kind" in result) {
|
|
947
|
-
if (parsed.evalJson === true) {
|
|
948
|
-
ctx.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
949
|
-
return 0;
|
|
950
|
-
}
|
|
951
|
-
ctx.stdout.write(`cclaw eval dry-run\n`);
|
|
952
|
-
ctx.stdout.write(` provider: ${result.config.provider}\n`);
|
|
953
|
-
ctx.stdout.write(` baseUrl: ${result.config.baseUrl}\n`);
|
|
954
|
-
ctx.stdout.write(` model: ${result.config.model}\n`);
|
|
955
|
-
ctx.stdout.write(` source: ${result.config.source}\n`);
|
|
956
|
-
ctx.stdout.write(` apiKey: ${result.config.apiKey ? "set" : "unset"}\n`);
|
|
957
|
-
ctx.stdout.write(` mode: ${result.plannedMode}\n`);
|
|
958
|
-
ctx.stdout.write(` corpus: ${result.corpus.total} case(s)\n`);
|
|
959
|
-
for (const [stage, count] of Object.entries(result.corpus.byStage)) {
|
|
960
|
-
ctx.stdout.write(` - ${stage}: ${count}\n`);
|
|
961
|
-
}
|
|
962
|
-
if (result.workflowCorpus.total > 0 || result.plannedMode === "workflow") {
|
|
963
|
-
ctx.stdout.write(` workflow corpus: ${result.workflowCorpus.total} case(s)\n`);
|
|
964
|
-
for (const wf of result.workflowCorpus.cases) {
|
|
965
|
-
ctx.stdout.write(` - ${wf.id}: ${wf.stages.join(" → ")}\n`);
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
ctx.stdout.write(` verifiers available:\n`);
|
|
969
|
-
for (const [key, value] of Object.entries(result.verifiersAvailable)) {
|
|
970
|
-
ctx.stdout.write(` - ${key}: ${value ? "yes" : "no"}\n`);
|
|
971
|
-
}
|
|
972
|
-
if (result.notes.length > 0) {
|
|
973
|
-
ctx.stdout.write(` notes:\n`);
|
|
974
|
-
for (const note of result.notes) {
|
|
975
|
-
ctx.stdout.write(` - ${note}\n`);
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
return 0;
|
|
979
|
-
}
|
|
980
|
-
if (parsed.evalUpdateBaseline === true && parsed.evalConfirm !== true) {
|
|
981
|
-
error(ctx, "--update-baseline requires --confirm to prevent accidental baseline resets.");
|
|
982
|
-
return 1;
|
|
983
|
-
}
|
|
984
|
-
if (parsed.evalUpdateBaseline === true) {
|
|
985
|
-
if (result.summary.failed > 0) {
|
|
986
|
-
error(ctx, `Refusing to update baselines: ${result.summary.failed} case(s) currently failing. Fix structural checks first.`);
|
|
987
|
-
return 1;
|
|
988
|
-
}
|
|
989
|
-
const written = await writeBaselinesFromReport(ctx.cwd, result);
|
|
990
|
-
for (const file of written) {
|
|
991
|
-
info(ctx, `Baseline written: ${path.relative(ctx.cwd, file)}`);
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
if (parsed.evalNoWrite !== true) {
|
|
995
|
-
const jsonPath = await writeJsonReport(ctx.cwd, result);
|
|
996
|
-
const mdPath = await writeMarkdownReport(ctx.cwd, result);
|
|
997
|
-
info(ctx, `Report written: ${path.relative(ctx.cwd, jsonPath)}`);
|
|
998
|
-
info(ctx, `Report written: ${path.relative(ctx.cwd, mdPath)}`);
|
|
999
|
-
}
|
|
1000
|
-
const regressionCount = result.baselineDelta?.criticalFailures ?? 0;
|
|
1001
|
-
if (parsed.evalJson === true) {
|
|
1002
|
-
ctx.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
1003
|
-
}
|
|
1004
|
-
else {
|
|
1005
|
-
const regressionNote = regressionCount > 0 ? `, ${regressionCount} regression(s)` : "";
|
|
1006
|
-
ctx.stdout.write(`cclaw eval: ${result.summary.totalCases} case(s), ` +
|
|
1007
|
-
`${result.summary.passed} passed, ` +
|
|
1008
|
-
`${result.summary.failed} failed, ` +
|
|
1009
|
-
`${result.summary.skipped} skipped${regressionNote}\n`);
|
|
1010
|
-
}
|
|
1011
|
-
if (result.summary.failed > 0)
|
|
1012
|
-
return 1;
|
|
1013
|
-
if (regressionCount > 0)
|
|
1014
|
-
return 1;
|
|
1015
|
-
return 0;
|
|
1016
|
-
}
|
|
1017
571
|
if (command === "archive") {
|
|
1018
572
|
const archived = await archiveRun(ctx.cwd, parsed.archiveName, {
|
|
1019
573
|
skipRetro: parsed.archiveSkipRetro === true,
|
|
@@ -1025,13 +579,13 @@ async function runCommand(parsed, ctx) {
|
|
|
1025
579
|
info(ctx, `Archived active artifacts to ${archived.archivePath}. Flow state reset to brainstorm.${snapshotSummary}`);
|
|
1026
580
|
const k = archived.knowledge;
|
|
1027
581
|
if (k.overThreshold) {
|
|
1028
|
-
info(ctx, `Knowledge curation recommended: ${k.knowledgePath} now has ${k.activeEntryCount} active entries (soft threshold ${k.softThreshold}).
|
|
582
|
+
info(ctx, `Knowledge curation recommended: ${k.knowledgePath} now has ${k.activeEntryCount} active entries (soft threshold ${k.softThreshold}). Ask your harness to curate cclaw knowledge and plan a soft-archive of stale/duplicate entries to ${RUNTIME_ROOT}/knowledge.archive.jsonl.`);
|
|
1029
583
|
}
|
|
1030
584
|
else if (k.activeEntryCount > 0) {
|
|
1031
|
-
info(ctx, `Knowledge: ${k.activeEntryCount}/${k.softThreshold} active entries.
|
|
585
|
+
info(ctx, `Knowledge: ${k.activeEntryCount}/${k.softThreshold} active entries. Ask your harness for a cclaw knowledge curation sweep before the next run if needed.`);
|
|
1032
586
|
}
|
|
1033
587
|
else {
|
|
1034
|
-
info(ctx, `Knowledge: 0 active entries in ${k.knowledgePath}. Capture lessons from this run
|
|
588
|
+
info(ctx, `Knowledge: 0 active entries in ${k.knowledgePath}. Capture lessons from this run through the learnings skill before they fade.`);
|
|
1035
589
|
}
|
|
1036
590
|
return 0;
|
|
1037
591
|
}
|
|
@@ -1040,13 +594,13 @@ async function runCommand(parsed, ctx) {
|
|
|
1040
594
|
return 0;
|
|
1041
595
|
}
|
|
1042
596
|
async function main() {
|
|
1043
|
-
const parsed = parseArgs(process.argv.slice(2));
|
|
1044
597
|
const ctx = {
|
|
1045
598
|
cwd: process.cwd(),
|
|
1046
599
|
stdout: process.stdout,
|
|
1047
600
|
stderr: process.stderr
|
|
1048
601
|
};
|
|
1049
602
|
try {
|
|
603
|
+
const parsed = parseArgs(process.argv.slice(2));
|
|
1050
604
|
const code = await runCommand(parsed, ctx);
|
|
1051
605
|
process.exitCode = code;
|
|
1052
606
|
}
|