cclaw-cli 0.51.29 → 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 +2 -18
- package/dist/cli.js +8 -246
- 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 +13 -10
- package/dist/content/core-agents.d.ts +18 -0
- package/dist/content/core-agents.js +51 -7
- 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 +11 -13
- package/dist/content/node-hooks.d.ts +10 -0
- package/dist/content/node-hooks.js +45 -11
- package/dist/content/opencode-plugin.js +3 -3
- package/dist/content/session-hooks.js +1 -1
- package/dist/content/skills.js +19 -7
- package/dist/content/stage-command.js +1 -1
- 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 +9 -6
- 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.d.ts +2 -2
- package/dist/content/start-command.js +24 -21
- package/dist/content/status-command.js +8 -8
- package/dist/content/subagents.js +61 -7
- package/dist/content/templates.d.ts +1 -1
- package/dist/content/templates.js +104 -152
- 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/flow-state.d.ts +1 -1
- package/dist/flow-state.js +1 -1
- 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 +54 -122
- package/dist/harness-selection.d.ts +31 -0
- package/dist/harness-selection.js +214 -0
- package/dist/install.js +166 -38
- 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 +5 -7
- package/dist/run-archive.d.ts +1 -1
- package/dist/run-archive.js +16 -16
- package/dist/run-persistence.js +112 -12
- 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/finish-command.d.ts +0 -2
- package/dist/content/finish-command.js +0 -26
- 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 -2206
- package/dist/internal/hook-manifest.d.ts +0 -16
- package/dist/internal/hook-manifest.js +0 -77
|
@@ -5,7 +5,7 @@ interface InternalIo {
|
|
|
5
5
|
stderr: Writable;
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
|
-
* Count archived runs as sub-directories under `.cclaw/
|
|
8
|
+
* Count archived runs as sub-directories under `.cclaw/archive/`. Missing
|
|
9
9
|
* dir / ENOENT is interpreted as zero — callers should NOT conflate
|
|
10
10
|
* that with "unknown" (undefined); we only return undefined on
|
|
11
11
|
* unexpected errors so the caller can choose to skip the relaxation
|
|
@@ -42,10 +42,10 @@ function stateDir(projectRoot) {
|
|
|
42
42
|
return path.join(projectRoot, RUNTIME_ROOT, "state");
|
|
43
43
|
}
|
|
44
44
|
function archiveRunsDir(projectRoot) {
|
|
45
|
-
return path.join(projectRoot, RUNTIME_ROOT, "
|
|
45
|
+
return path.join(projectRoot, RUNTIME_ROOT, "archive");
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
48
|
-
* Count archived runs as sub-directories under `.cclaw/
|
|
48
|
+
* Count archived runs as sub-directories under `.cclaw/archive/`. Missing
|
|
49
49
|
* dir / ENOENT is interpreted as zero — callers should NOT conflate
|
|
50
50
|
* that with "unknown" (undefined); we only return undefined on
|
|
51
51
|
* unexpected errors so the caller can choose to skip the relaxation
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { readConfig } from "../config.js";
|
|
3
|
+
import { RUNTIME_ROOT } from "../constants.js";
|
|
4
|
+
import { computeEarlyLoopStatus, formatEarlyLoopStatusLine, isEarlyLoopStage } from "../early-loop.js";
|
|
5
|
+
import { writeFileSafe } from "../fs-utils.js";
|
|
6
|
+
import { readFlowState } from "../runs.js";
|
|
7
|
+
function parseArgs(tokens) {
|
|
8
|
+
const args = { json: false, quiet: false, write: true };
|
|
9
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
10
|
+
const token = tokens[index];
|
|
11
|
+
const nextToken = tokens[index + 1];
|
|
12
|
+
if (token === "--json") {
|
|
13
|
+
args.json = true;
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
if (token === "--quiet") {
|
|
17
|
+
args.quiet = true;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (token === "--write") {
|
|
21
|
+
args.write = true;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (token === "--no-write") {
|
|
25
|
+
args.write = false;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (token === "--stage") {
|
|
29
|
+
if (!nextToken || nextToken.startsWith("--")) {
|
|
30
|
+
throw new Error("--stage requires a value: brainstorm | scope | design");
|
|
31
|
+
}
|
|
32
|
+
if (!isEarlyLoopStage(nextToken)) {
|
|
33
|
+
throw new Error("--stage must be one of: brainstorm, scope, design");
|
|
34
|
+
}
|
|
35
|
+
args.stage = nextToken;
|
|
36
|
+
index += 1;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (token.startsWith("--stage=")) {
|
|
40
|
+
const stageRaw = token.slice("--stage=".length);
|
|
41
|
+
if (!isEarlyLoopStage(stageRaw)) {
|
|
42
|
+
throw new Error("--stage must be one of: brainstorm, scope, design");
|
|
43
|
+
}
|
|
44
|
+
args.stage = stageRaw;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (token === "--run-id") {
|
|
48
|
+
if (!nextToken || nextToken.startsWith("--")) {
|
|
49
|
+
throw new Error("--run-id requires a value.");
|
|
50
|
+
}
|
|
51
|
+
args.runId = nextToken.trim();
|
|
52
|
+
index += 1;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (token.startsWith("--run-id=")) {
|
|
56
|
+
args.runId = token.slice("--run-id=".length).trim();
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
throw new Error(`Unknown early-loop-status flag: ${token}`);
|
|
60
|
+
}
|
|
61
|
+
return args;
|
|
62
|
+
}
|
|
63
|
+
function stateDir(projectRoot) {
|
|
64
|
+
return path.join(projectRoot, RUNTIME_ROOT, "state");
|
|
65
|
+
}
|
|
66
|
+
export async function runEarlyLoopStatusCommand(projectRoot, argv, io) {
|
|
67
|
+
const args = parseArgs(argv);
|
|
68
|
+
const flow = await readFlowState(projectRoot).catch(() => null);
|
|
69
|
+
const config = await readConfig(projectRoot).catch(() => null);
|
|
70
|
+
const stage = args.stage ?? flow?.currentStage;
|
|
71
|
+
if (!isEarlyLoopStage(stage)) {
|
|
72
|
+
io.stderr.write("cclaw internal early-loop-status: current stage is not an early-loop stage. Pass --stage=brainstorm|scope|design.\n");
|
|
73
|
+
return 1;
|
|
74
|
+
}
|
|
75
|
+
const runId = (args.runId ?? flow?.activeRunId ?? "active").trim() || "active";
|
|
76
|
+
const status = await computeEarlyLoopStatus(stage, runId, path.join(stateDir(projectRoot), "early-loop-log.jsonl"), { maxIterations: config?.earlyLoop?.maxIterations });
|
|
77
|
+
if (args.write) {
|
|
78
|
+
const target = path.join(stateDir(projectRoot), "early-loop.json");
|
|
79
|
+
await writeFileSafe(target, `${JSON.stringify(status, null, 2)}\n`);
|
|
80
|
+
}
|
|
81
|
+
if (!args.quiet) {
|
|
82
|
+
if (args.json) {
|
|
83
|
+
io.stdout.write(`${JSON.stringify(status, null, 2)}\n`);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
io.stdout.write(`${formatEarlyLoopStatusLine(status)}\n`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { readConfig } from "../config.js";
|
|
4
|
+
import { RUNTIME_ROOT } from "../constants.js";
|
|
5
|
+
import { classifyCodexHooksFlag, codexConfigPath, readCodexConfig } from "../codex-feature-flag.js";
|
|
6
|
+
import { exists } from "../fs-utils.js";
|
|
7
|
+
import { HARNESS_ADAPTERS, harnessShimFileNames, harnessShimSkillNames } from "../harness-adapters.js";
|
|
8
|
+
import { validateHookDocument } from "../hook-schema.js";
|
|
9
|
+
import { MANAGED_RESOURCE_MANIFEST_REL_PATH, validateManagedResourceManifest } from "../managed-resources.js";
|
|
10
|
+
import { CorruptFlowStateError, readFlowState } from "../runs.js";
|
|
11
|
+
function parseArgs(tokens) {
|
|
12
|
+
const args = { json: false, quiet: false };
|
|
13
|
+
for (const token of tokens) {
|
|
14
|
+
if (token === "--json") {
|
|
15
|
+
args.json = true;
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (token === "--quiet") {
|
|
19
|
+
args.quiet = true;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
throw new Error(`Unknown runtime-integrity flag: ${token}`);
|
|
23
|
+
}
|
|
24
|
+
return args;
|
|
25
|
+
}
|
|
26
|
+
function stripJsonCommentsOutsideStrings(input) {
|
|
27
|
+
let out = "";
|
|
28
|
+
let i = 0;
|
|
29
|
+
let inString = false;
|
|
30
|
+
let escape = false;
|
|
31
|
+
while (i < input.length) {
|
|
32
|
+
const c = input[i];
|
|
33
|
+
if (inString) {
|
|
34
|
+
out += c;
|
|
35
|
+
if (escape) {
|
|
36
|
+
escape = false;
|
|
37
|
+
}
|
|
38
|
+
else if (c === "\\") {
|
|
39
|
+
escape = true;
|
|
40
|
+
}
|
|
41
|
+
else if (c === "\"") {
|
|
42
|
+
inString = false;
|
|
43
|
+
}
|
|
44
|
+
i += 1;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (c === "\"") {
|
|
48
|
+
inString = true;
|
|
49
|
+
out += c;
|
|
50
|
+
i += 1;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const next = input[i + 1];
|
|
54
|
+
if (c === "/" && next === "/") {
|
|
55
|
+
while (i < input.length && input[i] !== "\n" && input[i] !== "\r")
|
|
56
|
+
i += 1;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (c === "/" && next === "*") {
|
|
60
|
+
i += 2;
|
|
61
|
+
while (i < input.length - 1 && !(input[i] === "*" && input[i + 1] === "/"))
|
|
62
|
+
i += 1;
|
|
63
|
+
i = Math.min(i + 2, input.length);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
out += c;
|
|
67
|
+
i += 1;
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
function parseJsonWithRecovery(raw) {
|
|
72
|
+
try {
|
|
73
|
+
return JSON.parse(raw);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// Continue with comment/trailing-comma recovery.
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
const normalized = stripJsonCommentsOutsideStrings(raw).replace(/,\s*([}\]])/gu, "$1");
|
|
80
|
+
return JSON.parse(normalized);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function okFinding(id, message) {
|
|
87
|
+
return {
|
|
88
|
+
id,
|
|
89
|
+
severity: "error",
|
|
90
|
+
ok: true,
|
|
91
|
+
message
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function errorFinding(id, message, details) {
|
|
95
|
+
return {
|
|
96
|
+
id,
|
|
97
|
+
severity: "error",
|
|
98
|
+
ok: false,
|
|
99
|
+
message,
|
|
100
|
+
...(details && details.length > 0 ? { details } : {})
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function warningFinding(id, ok, message, details) {
|
|
104
|
+
return {
|
|
105
|
+
id,
|
|
106
|
+
severity: "warning",
|
|
107
|
+
ok,
|
|
108
|
+
message,
|
|
109
|
+
...(details && details.length > 0 ? { details } : {})
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
async function checkStaleSentinel(projectRoot) {
|
|
113
|
+
const sentinelPath = path.join(projectRoot, RUNTIME_ROOT, "state", ".init-in-progress");
|
|
114
|
+
if (!(await exists(sentinelPath))) {
|
|
115
|
+
return warningFinding("stale_init_sentinel", true, "No stale init sentinel detected.");
|
|
116
|
+
}
|
|
117
|
+
let startedAt = "unknown time";
|
|
118
|
+
try {
|
|
119
|
+
const raw = await fs.readFile(sentinelPath, "utf8");
|
|
120
|
+
const parsed = JSON.parse(raw);
|
|
121
|
+
if (parsed && typeof parsed.startedAt === "string" && parsed.startedAt.trim().length > 0) {
|
|
122
|
+
startedAt = parsed.startedAt;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// best-effort parse only
|
|
127
|
+
}
|
|
128
|
+
return warningFinding("stale_init_sentinel", false, "Detected stale .init-in-progress sentinel from a previous interrupted sync/init run.", [`startedAt: ${startedAt}`, `path: ${sentinelPath}`]);
|
|
129
|
+
}
|
|
130
|
+
async function checkFlowState(projectRoot) {
|
|
131
|
+
try {
|
|
132
|
+
await readFlowState(projectRoot);
|
|
133
|
+
return okFinding("flow_state", "Flow state is readable.");
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
if (error instanceof CorruptFlowStateError) {
|
|
137
|
+
return errorFinding("flow_state", "Corrupt flow-state detected.", [error.message]);
|
|
138
|
+
}
|
|
139
|
+
return errorFinding("flow_state", "Flow-state read failed.", [
|
|
140
|
+
error instanceof Error ? error.message : String(error)
|
|
141
|
+
]);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function checkManagedManifest(projectRoot) {
|
|
145
|
+
const manifestPath = path.join(projectRoot, MANAGED_RESOURCE_MANIFEST_REL_PATH);
|
|
146
|
+
if (!(await exists(manifestPath))) {
|
|
147
|
+
return errorFinding("managed_manifest", "Managed resource manifest is missing.", [`missing: ${manifestPath}`]);
|
|
148
|
+
}
|
|
149
|
+
const rawText = await fs.readFile(manifestPath, "utf8");
|
|
150
|
+
let parsed;
|
|
151
|
+
try {
|
|
152
|
+
parsed = JSON.parse(rawText);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
return errorFinding("managed_manifest", "Managed resource manifest is not valid JSON.", [error instanceof Error ? error.message : String(error)]);
|
|
156
|
+
}
|
|
157
|
+
const issues = validateManagedResourceManifest(parsed);
|
|
158
|
+
if (issues.length > 0) {
|
|
159
|
+
const detail = issues.slice(0, 12).map((issue) => {
|
|
160
|
+
const scope = issue.path ?? (issue.index !== undefined ? `resources[${issue.index}]` : "manifest");
|
|
161
|
+
return `${scope}.${issue.field}: ${issue.message}`;
|
|
162
|
+
});
|
|
163
|
+
return errorFinding("managed_manifest", "Managed resource manifest schema validation failed.", detail);
|
|
164
|
+
}
|
|
165
|
+
return okFinding("managed_manifest", "Managed resource manifest is valid.");
|
|
166
|
+
}
|
|
167
|
+
function hookFilePath(projectRoot, harness) {
|
|
168
|
+
if (harness === "claude")
|
|
169
|
+
return path.join(projectRoot, ".claude/hooks/hooks.json");
|
|
170
|
+
if (harness === "cursor")
|
|
171
|
+
return path.join(projectRoot, ".cursor/hooks.json");
|
|
172
|
+
return path.join(projectRoot, ".codex/hooks.json");
|
|
173
|
+
}
|
|
174
|
+
async function checkHookDocument(projectRoot, harness) {
|
|
175
|
+
const hookPath = hookFilePath(projectRoot, harness);
|
|
176
|
+
if (!(await exists(hookPath))) {
|
|
177
|
+
return errorFinding(`hook_document_${harness}`, `Hook document is missing for ${harness}.`, [`missing: ${hookPath}`]);
|
|
178
|
+
}
|
|
179
|
+
const raw = await fs.readFile(hookPath, "utf8");
|
|
180
|
+
const parsed = parseJsonWithRecovery(raw);
|
|
181
|
+
if (parsed === null) {
|
|
182
|
+
return errorFinding(`hook_document_${harness}`, `Hook document for ${harness} is unparseable JSON.`, [`path: ${hookPath}`]);
|
|
183
|
+
}
|
|
184
|
+
const validation = validateHookDocument(harness, parsed);
|
|
185
|
+
if (!validation.ok) {
|
|
186
|
+
return errorFinding(`hook_document_${harness}`, `Hook document for ${harness} is invalid.`, validation.errors);
|
|
187
|
+
}
|
|
188
|
+
return okFinding(`hook_document_${harness}`, `Hook document for ${harness} is valid.`);
|
|
189
|
+
}
|
|
190
|
+
async function checkHarnessShims(projectRoot, harnesses) {
|
|
191
|
+
const findings = [];
|
|
192
|
+
const expectedFiles = harnessShimFileNames();
|
|
193
|
+
const expectedSkillFolders = harnessShimSkillNames();
|
|
194
|
+
for (const harness of harnesses) {
|
|
195
|
+
const adapter = HARNESS_ADAPTERS[harness];
|
|
196
|
+
const base = path.join(projectRoot, adapter.commandDir);
|
|
197
|
+
const missing = [];
|
|
198
|
+
for (const fileName of expectedFiles) {
|
|
199
|
+
const target = adapter.shimKind === "skill"
|
|
200
|
+
? path.join(base, fileName.replace(/\.md$/u, ""), "SKILL.md")
|
|
201
|
+
: path.join(base, fileName);
|
|
202
|
+
if (!(await exists(target))) {
|
|
203
|
+
missing.push(target);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (adapter.shimKind === "skill") {
|
|
207
|
+
for (const folder of expectedSkillFolders) {
|
|
208
|
+
const target = path.join(base, folder, "SKILL.md");
|
|
209
|
+
if (!(await exists(target))) {
|
|
210
|
+
missing.push(target);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (missing.length > 0) {
|
|
215
|
+
findings.push(errorFinding(`shim_drift_${harness}`, `Harness shim drift detected for ${harness}.`, missing));
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
findings.push(okFinding(`shim_drift_${harness}`, `Harness shims for ${harness} are present.`));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return findings;
|
|
222
|
+
}
|
|
223
|
+
async function checkCodexHooksFlag(harnesses) {
|
|
224
|
+
if (!harnesses.includes("codex")) {
|
|
225
|
+
return warningFinding("codex_hooks_flag", true, "Codex harness is not enabled.");
|
|
226
|
+
}
|
|
227
|
+
const configTomlPath = codexConfigPath();
|
|
228
|
+
let existing;
|
|
229
|
+
try {
|
|
230
|
+
existing = await readCodexConfig(configTomlPath);
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
return warningFinding("codex_hooks_flag", false, "Could not read Codex config.toml to validate codex_hooks.", [error instanceof Error ? error.message : String(error)]);
|
|
234
|
+
}
|
|
235
|
+
const state = classifyCodexHooksFlag(existing);
|
|
236
|
+
if (state === "enabled") {
|
|
237
|
+
return warningFinding("codex_hooks_flag", true, "Codex hooks feature flag is enabled.");
|
|
238
|
+
}
|
|
239
|
+
return warningFinding("codex_hooks_flag", false, "Codex hooks file is present, but [features] codex_hooks is not true in Codex config.", [`configPath: ${configTomlPath}`, `state: ${state}`]);
|
|
240
|
+
}
|
|
241
|
+
function buildReport(findings) {
|
|
242
|
+
const errors = findings.filter((finding) => !finding.ok && finding.severity === "error").length;
|
|
243
|
+
const warnings = findings.filter((finding) => !finding.ok && finding.severity === "warning").length;
|
|
244
|
+
return {
|
|
245
|
+
ok: errors === 0,
|
|
246
|
+
generatedAt: new Date().toISOString(),
|
|
247
|
+
findings,
|
|
248
|
+
summary: { errors, warnings }
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
function writeTextReport(io, report) {
|
|
252
|
+
io.stdout.write(`runtime-integrity: ${report.ok ? "ok" : "failed"}\n`);
|
|
253
|
+
io.stdout.write(`errors=${report.summary.errors} warnings=${report.summary.warnings}\n`);
|
|
254
|
+
for (const finding of report.findings) {
|
|
255
|
+
if (finding.ok)
|
|
256
|
+
continue;
|
|
257
|
+
io.stdout.write(`[${finding.severity}] ${finding.id}: ${finding.message}\n`);
|
|
258
|
+
for (const detail of finding.details ?? []) {
|
|
259
|
+
io.stdout.write(` - ${detail}\n`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
export async function runRuntimeIntegrityCommand(projectRoot, argv, io) {
|
|
264
|
+
const args = parseArgs(argv);
|
|
265
|
+
const config = await readConfig(projectRoot);
|
|
266
|
+
const harnesses = config.harnesses;
|
|
267
|
+
const findings = [];
|
|
268
|
+
findings.push(await checkStaleSentinel(projectRoot));
|
|
269
|
+
findings.push(await checkFlowState(projectRoot));
|
|
270
|
+
findings.push(await checkManagedManifest(projectRoot));
|
|
271
|
+
findings.push(...await checkHarnessShims(projectRoot, harnesses));
|
|
272
|
+
for (const harness of harnesses) {
|
|
273
|
+
if (harness === "claude" || harness === "cursor" || harness === "codex") {
|
|
274
|
+
findings.push(await checkHookDocument(projectRoot, harness));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
findings.push(await checkCodexHooksFlag(harnesses));
|
|
278
|
+
const report = buildReport(findings);
|
|
279
|
+
if (!args.quiet) {
|
|
280
|
+
if (args.json) {
|
|
281
|
+
io.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
writeTextReport(io, report);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return report.ok ? 0 : 1;
|
|
288
|
+
}
|
|
@@ -95,7 +95,7 @@ export async function runTddRedEvidenceCommand(projectRoot, tokens, io) {
|
|
|
95
95
|
if (!effectiveRunId || effectiveRunId.trim().length === 0) {
|
|
96
96
|
const reason = "tdd-red-evidence: cannot scope check — no --runId provided and " +
|
|
97
97
|
"flow-state.json has no activeRunId. Pass --runId=<id> explicitly " +
|
|
98
|
-
"or run `cclaw
|
|
98
|
+
"or run `npx cclaw-cli sync` to reconcile state.";
|
|
99
99
|
if (!args.quiet) {
|
|
100
100
|
io.stdout.write(`${JSON.stringify({
|
|
101
101
|
ok: false,
|
|
@@ -4,7 +4,7 @@ export type KnowledgeEntryConfidence = "high" | "medium" | "low";
|
|
|
4
4
|
export type KnowledgeEntrySeverity = "critical" | "important" | "suggestion";
|
|
5
5
|
export type KnowledgeEntryUniversality = "project" | "personal" | "universal";
|
|
6
6
|
export type KnowledgeEntryMaturity = "raw" | "lifted-to-rule" | "lifted-to-enforcement";
|
|
7
|
-
export type KnowledgeEntrySource = "stage" | "retro" | "compound" | "
|
|
7
|
+
export type KnowledgeEntrySource = "stage" | "retro" | "compound" | "idea" | "manual";
|
|
8
8
|
export interface KnowledgeEntry {
|
|
9
9
|
type: KnowledgeEntryType;
|
|
10
10
|
trigger: string;
|
|
@@ -36,8 +36,6 @@ export interface KnowledgeSeedEntry {
|
|
|
36
36
|
stage?: FlowStage | null;
|
|
37
37
|
origin_stage?: FlowStage | null;
|
|
38
38
|
origin_run?: string | null;
|
|
39
|
-
/** @deprecated Use `origin_run`. Accepted only for legacy JSONL/backfill inputs. */
|
|
40
|
-
origin_feature?: string | null;
|
|
41
39
|
frequency?: number;
|
|
42
40
|
universality?: KnowledgeEntryUniversality;
|
|
43
41
|
maturity?: KnowledgeEntryMaturity;
|
|
@@ -143,7 +141,7 @@ export interface ComputeCompoundReadinessOptions {
|
|
|
143
141
|
maxReady?: number;
|
|
144
142
|
now?: Date;
|
|
145
143
|
/**
|
|
146
|
-
* Count of archived runs under `.cclaw/
|
|
144
|
+
* Count of archived runs under `.cclaw/archive/`. When supplied and
|
|
147
145
|
* `< SMALL_PROJECT_ARCHIVE_RUNS_THRESHOLD`, the effective threshold
|
|
148
146
|
* is lowered to `min(threshold, SMALL_PROJECT_RECURRENCE_THRESHOLD)`.
|
|
149
147
|
* Matches the rule documented in `docs/config.md`.
|
|
@@ -174,10 +172,7 @@ export declare function effectiveCompoundThreshold(baseThreshold: number, archiv
|
|
|
174
172
|
* as ready.
|
|
175
173
|
*/
|
|
176
174
|
export declare function computeCompoundReadiness(entries: KnowledgeEntry[], options?: ComputeCompoundReadinessOptions): CompoundReadiness;
|
|
177
|
-
export
|
|
178
|
-
allowLegacyOriginFeature?: boolean;
|
|
179
|
-
}
|
|
180
|
-
export declare function validateKnowledgeEntry(entry: unknown, options?: ValidateKnowledgeEntryOptions): {
|
|
175
|
+
export declare function validateKnowledgeEntry(entry: unknown): {
|
|
181
176
|
ok: boolean;
|
|
182
177
|
errors: string[];
|
|
183
178
|
};
|
package/dist/knowledge-store.js
CHANGED
|
@@ -149,7 +149,7 @@ const KNOWLEDGE_SOURCE_SET = new Set([
|
|
|
149
149
|
"stage",
|
|
150
150
|
"retro",
|
|
151
151
|
"compound",
|
|
152
|
-
"
|
|
152
|
+
"idea",
|
|
153
153
|
"manual"
|
|
154
154
|
]);
|
|
155
155
|
const FLOW_STAGE_SET = new Set(FLOW_STAGES);
|
|
@@ -175,8 +175,8 @@ KNOWLEDGE_ALLOWED_KEYS.add("source");
|
|
|
175
175
|
KNOWLEDGE_ALLOWED_KEYS.add("severity");
|
|
176
176
|
KNOWLEDGE_ALLOWED_KEYS.add("supersedes");
|
|
177
177
|
KNOWLEDGE_ALLOWED_KEYS.add("superseded_by");
|
|
178
|
-
function keyAllowedInKnowledgeEntry(key
|
|
179
|
-
return KNOWLEDGE_ALLOWED_KEYS.has(key)
|
|
178
|
+
function keyAllowedInKnowledgeEntry(key) {
|
|
179
|
+
return KNOWLEDGE_ALLOWED_KEYS.has(key);
|
|
180
180
|
}
|
|
181
181
|
function knowledgePath(projectRoot) {
|
|
182
182
|
return path.join(projectRoot, RUNTIME_ROOT, "knowledge.jsonl");
|
|
@@ -219,13 +219,6 @@ function emptyKnowledgeSnapshot() {
|
|
|
219
219
|
entryByIndex: new Map()
|
|
220
220
|
};
|
|
221
221
|
}
|
|
222
|
-
function normalizeLegacyKnowledgeEntry(entry) {
|
|
223
|
-
const { origin_feature: legacyOriginRun, ...rest } = entry;
|
|
224
|
-
return {
|
|
225
|
-
...rest,
|
|
226
|
-
origin_run: entry.origin_run ?? legacyOriginRun ?? null
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
222
|
function parseKnowledgeSnapshot(raw) {
|
|
230
223
|
const lines = stripBom(raw).split(/\r?\n/u);
|
|
231
224
|
const entries = [];
|
|
@@ -238,12 +231,12 @@ function parseKnowledgeSnapshot(raw) {
|
|
|
238
231
|
continue;
|
|
239
232
|
try {
|
|
240
233
|
const parsed = JSON.parse(trimmed);
|
|
241
|
-
const validated = validateKnowledgeEntry(parsed
|
|
234
|
+
const validated = validateKnowledgeEntry(parsed);
|
|
242
235
|
if (!validated.ok) {
|
|
243
236
|
malformedLines += 1;
|
|
244
237
|
continue;
|
|
245
238
|
}
|
|
246
|
-
const entry =
|
|
239
|
+
const entry = parsed;
|
|
247
240
|
entries.push(entry);
|
|
248
241
|
const key = dedupeKey(entry);
|
|
249
242
|
if (!keyToIndex.has(key)) {
|
|
@@ -296,24 +289,20 @@ function isNullableString(value) {
|
|
|
296
289
|
function isNullableStage(value) {
|
|
297
290
|
return value === null || (typeof value === "string" && FLOW_STAGE_SET.has(value));
|
|
298
291
|
}
|
|
299
|
-
export function validateKnowledgeEntry(entry
|
|
292
|
+
export function validateKnowledgeEntry(entry) {
|
|
300
293
|
const errors = [];
|
|
301
294
|
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
302
295
|
return { ok: false, errors: ["Knowledge entry must be a JSON object."] };
|
|
303
296
|
}
|
|
304
297
|
const obj = entry;
|
|
305
298
|
for (const key of Object.keys(obj)) {
|
|
306
|
-
if (!keyAllowedInKnowledgeEntry(key
|
|
299
|
+
if (!keyAllowedInKnowledgeEntry(key)) {
|
|
307
300
|
errors.push(`Unknown key "${key}" in knowledge entry.`);
|
|
308
301
|
}
|
|
309
302
|
}
|
|
310
303
|
for (const key of KNOWLEDGE_REQUIRED_KEYS) {
|
|
311
304
|
if (!Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
312
|
-
|
|
313
|
-
options.allowLegacyOriginFeature !== true ||
|
|
314
|
-
!Object.prototype.hasOwnProperty.call(obj, "origin_feature")) {
|
|
315
|
-
errors.push(`Missing required key "${key}".`);
|
|
316
|
-
}
|
|
305
|
+
errors.push(`Missing required key "${key}".`);
|
|
317
306
|
}
|
|
318
307
|
}
|
|
319
308
|
if (!KNOWLEDGE_TYPE_SET.has(obj.type)) {
|
|
@@ -341,11 +330,7 @@ export function validateKnowledgeEntry(entry, options = {}) {
|
|
|
341
330
|
if (!isNullableStage(obj.origin_stage)) {
|
|
342
331
|
errors.push(`origin_stage must be one of ${FLOW_STAGES.join(", ")} or null.`);
|
|
343
332
|
}
|
|
344
|
-
const originRun =
|
|
345
|
-
? obj.origin_run
|
|
346
|
-
: options.allowLegacyOriginFeature === true
|
|
347
|
-
? obj.origin_feature
|
|
348
|
-
: undefined;
|
|
333
|
+
const originRun = obj.origin_run;
|
|
349
334
|
if (!isNullableString(originRun)) {
|
|
350
335
|
errors.push("origin_run must be string or null.");
|
|
351
336
|
}
|
|
@@ -380,10 +365,12 @@ export function validateKnowledgeEntry(entry, options = {}) {
|
|
|
380
365
|
(typeof obj.superseded_by !== "string" || obj.superseded_by.trim().length === 0)) {
|
|
381
366
|
errors.push("superseded_by must be a non-empty string when present.");
|
|
382
367
|
}
|
|
383
|
-
|
|
384
|
-
obj.source
|
|
385
|
-
(typeof obj.source
|
|
386
|
-
|
|
368
|
+
const sourceAllowed = obj.source === undefined ||
|
|
369
|
+
obj.source === null ||
|
|
370
|
+
(typeof obj.source === "string" &&
|
|
371
|
+
KNOWLEDGE_SOURCE_SET.has(obj.source));
|
|
372
|
+
if (!sourceAllowed) {
|
|
373
|
+
errors.push("source must be one of: stage, retro, compound, idea, manual, or null.");
|
|
387
374
|
}
|
|
388
375
|
return { ok: errors.length === 0, errors };
|
|
389
376
|
}
|
|
@@ -391,7 +378,7 @@ export function materializeKnowledgeEntry(seed, defaults = {}) {
|
|
|
391
378
|
const now = normalizeUtcIso(defaults.nowIso ?? nowUtcIso());
|
|
392
379
|
const stage = seed.stage ?? defaults.stage ?? null;
|
|
393
380
|
const originStage = seed.origin_stage ?? defaults.originStage ?? stage ?? null;
|
|
394
|
-
const originRun = seed.origin_run ??
|
|
381
|
+
const originRun = seed.origin_run ?? defaults.originRun ?? null;
|
|
395
382
|
const source = seed.source ?? defaults.source ?? null;
|
|
396
383
|
const entry = {
|
|
397
384
|
type: seed.type,
|
|
@@ -60,7 +60,7 @@ export function isManagedGeneratedPath(relPath) {
|
|
|
60
60
|
return false;
|
|
61
61
|
if (relPath.startsWith(`${RUNTIME_ROOT}/artifacts/`))
|
|
62
62
|
return false;
|
|
63
|
-
if (relPath.startsWith(`${RUNTIME_ROOT}/
|
|
63
|
+
if (relPath.startsWith(`${RUNTIME_ROOT}/archive/`))
|
|
64
64
|
return false;
|
|
65
65
|
if (relPath === `${RUNTIME_ROOT}/state/flow-state.json`)
|
|
66
66
|
return false;
|
|
@@ -209,7 +209,29 @@ export class ManagedResourceSession {
|
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
211
|
static async create(options) {
|
|
212
|
-
const
|
|
212
|
+
const manifestPath = path.join(options.projectRoot, MANAGED_RESOURCE_MANIFEST_REL_PATH);
|
|
213
|
+
let previous = null;
|
|
214
|
+
if (await exists(manifestPath)) {
|
|
215
|
+
let raw;
|
|
216
|
+
try {
|
|
217
|
+
raw = JSON.parse(await fs.readFile(manifestPath, "utf8"));
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
throw new Error(`[sync fail-fast] Managed resource manifest is corrupt JSON (${MANAGED_RESOURCE_MANIFEST_REL_PATH}): ${error instanceof Error ? error.message : String(error)}. Fix/remove the manifest and rerun \`npx cclaw-cli sync\`.`);
|
|
221
|
+
}
|
|
222
|
+
const issues = validateManagedResourceManifest(raw);
|
|
223
|
+
if (issues.length > 0) {
|
|
224
|
+
const detail = issues.slice(0, 12)
|
|
225
|
+
.map((issue) => {
|
|
226
|
+
const scope = issue.path ?? (issue.index !== undefined ? `resources[${issue.index}]` : "manifest");
|
|
227
|
+
return `${scope}.${issue.field}: ${issue.message}`;
|
|
228
|
+
})
|
|
229
|
+
.join("; ");
|
|
230
|
+
throw new Error(`[sync fail-fast] Managed resource manifest is malformed (${MANAGED_RESOURCE_MANIFEST_REL_PATH}): ${detail}. ` +
|
|
231
|
+
`Fix/remove the manifest and rerun \`npx cclaw-cli sync\`.`);
|
|
232
|
+
}
|
|
233
|
+
previous = await readManagedResourceManifest(options.projectRoot).catch(() => null);
|
|
234
|
+
}
|
|
213
235
|
return new ManagedResourceSession(options, previous);
|
|
214
236
|
}
|
|
215
237
|
shouldManage(filePath) {
|
package/dist/policy.js
CHANGED
|
@@ -62,11 +62,9 @@ export async function policyChecks(projectRoot, options = {}) {
|
|
|
62
62
|
{ file: runtimeFile("skills/learnings/SKILL.md"), needle: "## Manual Actions", name: "utility_skill:learnings:manual_actions" },
|
|
63
63
|
{ file: runtimeFile("skills/learnings/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:learnings:hard_gate" },
|
|
64
64
|
{ file: runtimeFile("commands/start.md"), needle: "## Algorithm", name: "utility_command:start:algorithm" },
|
|
65
|
-
{ file: runtimeFile("commands/
|
|
66
|
-
{ file: runtimeFile("
|
|
67
|
-
{ file: runtimeFile("
|
|
68
|
-
{ file: runtimeFile("skills/flow-ideate/SKILL.md"), needle: "## Protocol", name: "utility_skill:ideate:protocol" },
|
|
69
|
-
{ file: runtimeFile("skills/flow-ideate/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:ideate:hard_gate" },
|
|
65
|
+
{ file: runtimeFile("commands/idea.md"), needle: "## Algorithm", name: "utility_command:idea:algorithm" },
|
|
66
|
+
{ file: runtimeFile("skills/flow-idea/SKILL.md"), needle: "## Protocol", name: "utility_skill:idea:protocol" },
|
|
67
|
+
{ file: runtimeFile("skills/flow-idea/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:idea:hard_gate" },
|
|
70
68
|
{ file: runtimeFile("commands/view.md"), needle: "## Routing", name: "utility_command:view:routing" },
|
|
71
69
|
{ file: runtimeFile("skills/flow-view/SKILL.md"), needle: "## Status Subcommand", name: "utility_skill:view:status_section" },
|
|
72
70
|
{ file: runtimeFile("skills/flow-view/SKILL.md"), needle: "## Tree Subcommand", name: "utility_skill:view:tree_section" },
|
|
@@ -96,7 +94,7 @@ export async function policyChecks(projectRoot, options = {}) {
|
|
|
96
94
|
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Protocol Behavior", name: "meta_skill:protocol_behavior" },
|
|
97
95
|
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Failure guardrails", name: "meta_skill:failure_guardrails" },
|
|
98
96
|
{ file: runtimeFile("skills/session/SKILL.md"), needle: "## Session Resume Protocol", name: "utility_skill:session:resume" },
|
|
99
|
-
{ file: runtimeFile("skills/
|
|
97
|
+
{ file: runtimeFile("skills/brainstorm/SKILL.md"), needle: "## Shared Stage Guidance", name: "stage_skill:shared_guidance_inline" },
|
|
100
98
|
{ file: runtimeFile("hooks/run-hook.mjs"), needle: "activeRunId", name: "hooks:session_start:active_run" },
|
|
101
99
|
{ file: runtimeFile("hooks/run-hook.mjs"), needle: "write_to_cclaw_runtime", name: "hooks:guard:risky_write_advisory" },
|
|
102
100
|
{ file: runtimeFile("hooks/run-hook.mjs"), needle: "stage_invocation_without_recent_flow_read", name: "hooks:workflow_guard:flow_read_reason" },
|
|
@@ -127,7 +125,7 @@ export async function policyChecks(projectRoot, options = {}) {
|
|
|
127
125
|
});
|
|
128
126
|
utilitySkillChecks.push({
|
|
129
127
|
file: ".cursor/rules/cclaw-workflow.mdc",
|
|
130
|
-
needle: "/cc
|
|
128
|
+
needle: "/cc",
|
|
131
129
|
name: "rules:cursor:next_command_guidance"
|
|
132
130
|
});
|
|
133
131
|
}
|
package/dist/run-archive.d.ts
CHANGED
|
@@ -56,6 +56,6 @@ export declare function archiveRun(projectRoot: string, runName?: string, option
|
|
|
56
56
|
* Counts entries in the canonical JSONL knowledge store. An "active" entry is one
|
|
57
57
|
* non-empty line that parses as JSON with the required `type` field belonging to the
|
|
58
58
|
* allowed set. Malformed lines are ignored (not counted) but do not throw so that a
|
|
59
|
-
* hand-edited file cannot break
|
|
59
|
+
* hand-edited file cannot break sync/archive flows.
|
|
60
60
|
*/
|
|
61
61
|
export declare function countActiveKnowledgeEntries(text: string): number;
|