cclaw-cli 0.48.12 → 0.48.13

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.
@@ -59,6 +59,17 @@ This is the only progression command the user needs to drive the entire flow. St
59
59
  → Load **\`${RUNTIME_ROOT}/skills/<skillFolder>/SKILL.md\`** and **\`${RUNTIME_ROOT}/commands/<currentStage>.md\`** for the current stage.
60
60
  → Execute that stage's protocol. The stage skill handles the full interaction including STOP points and gate tracking.
61
61
  → Stage completion must use \`node .cclaw/hooks/stage-complete.mjs <currentStage>\` (canonical), which validates delegations + gate evidence before mutating \`flow-state.json\`.
62
+ → **Ralph Loop (tdd only).** When \`currentStage === "tdd"\`, also read
63
+ \`${RUNTIME_ROOT}/state/ralph-loop.json\` (refreshed on every session-start
64
+ while the flow is in tdd). Use it as a ground-truth progress indicator:
65
+ - \`loopIteration\` tells you how many RED → GREEN cycles already landed.
66
+ - \`acClosed\` lists the distinct acceptance-criterion IDs a GREEN row has
67
+ closed so far — if your plan tasks map to ACs, this is the "tasks
68
+ remaining" signal without needing a separate counter.
69
+ - \`redOpenSlices\` is the set of slices with an unsatisfied RED. Do not
70
+ advance to review while this is non-empty.
71
+ - Stage advancement to \`review\` still requires the normal gates in
72
+ \`flow-state.json\`; Ralph Loop status is a soft nudge, not a gate.
62
73
 
63
74
  ### Path B: Current stage IS complete (all gates passed, all delegations satisfied)
64
75
 
@@ -190,6 +201,15 @@ Load the current stage's skill and command contract:
190
201
 
191
202
  Execute the stage protocol. The stage skill handles interaction, STOP points, gate tracking, and stage completion via \`node .cclaw/hooks/stage-complete.mjs <stage>\` (canonical flow-state mutation path).
192
203
 
204
+ **Ralph Loop (tdd only).** When the current stage is \`tdd\`, pair the
205
+ normal gate-evidence view with \`${RUNTIME_ROOT}/state/ralph-loop.json\`:
206
+ \`loopIteration\` is the running count of RED → GREEN cycles,
207
+ \`acClosed\` lists distinct acceptance-criterion IDs already closed by
208
+ GREEN rows (populated from \`acIds\` in \`tdd-cycle-log.jsonl\`), and
209
+ \`redOpenSlices\` is the "tasks remaining" indicator. Advance only when
210
+ every planned slice is in \`acClosed\` (or explicitly deferred) and
211
+ \`redOpenSlices\` is empty.
212
+
193
213
  Special-case for review: if \`review_criticals_resolved\` is in \`blocked\`, route to rework instead of looping review forever — recommend \`/cc-ops rewind tdd "review_blocked_by_critical"\`.
194
214
 
195
215
  **Path B — stage IS complete (all gates met, all delegations done):**
@@ -705,6 +705,26 @@ async function handleSessionStart(runtime) {
705
705
  const contextWarning = await readLatestContextWarningLine(contextWarningsFile);
706
706
  const knowledge = await buildKnowledgeDigest(runtime.root, state.currentStage);
707
707
 
708
+ // Refresh Ralph Loop status each session-start so /cc-next and the model
709
+ // both read a consistent "iter=N, acClosed=[...]" snapshot. Runs only when
710
+ // we are in tdd — other stages skip the write to keep the file stable.
711
+ let ralphLoopLine = "";
712
+ if (state.currentStage === "tdd") {
713
+ try {
714
+ const ralphStatus = await computeRalphLoopStatusInline(stateDir, state.activeRunId);
715
+ await writeJsonFile(path.join(stateDir, "ralph-loop.json"), ralphStatus);
716
+ const redOpen = ralphStatus.redOpenSlices.length > 0
717
+ ? ralphStatus.redOpenSlices.join(",")
718
+ : "none";
719
+ ralphLoopLine = "Ralph Loop: iter=" + String(ralphStatus.loopIteration) +
720
+ ", slices=" + String(ralphStatus.sliceCount) +
721
+ ", acClosed=" + String(ralphStatus.acClosed.length) +
722
+ ", redOpen=" + redOpen;
723
+ } catch (_err) {
724
+ // best-effort — a malformed cycle log should never break session-start.
725
+ }
726
+ }
727
+
708
728
  const suggestionMemory = toObject(await readJsonFile(suggestionMemoryFile, {})) || {};
709
729
  const suggestionsEnabled = suggestionMemory.enabled !== false;
710
730
  const mutedStages = Array.isArray(suggestionMemory.mutedStages)
@@ -769,6 +789,9 @@ async function handleSessionStart(runtime) {
769
789
  if (activitySummary.length > 0) {
770
790
  parts.push("Recent stage activity:\\n" + activitySummary.join("\\n"));
771
791
  }
792
+ if (ralphLoopLine.length > 0) {
793
+ parts.push(ralphLoopLine);
794
+ }
772
795
  if (contextWarning.length > 0) {
773
796
  parts.push("Latest context warning:\\n" + contextWarning);
774
797
  }
@@ -1089,6 +1112,66 @@ async function tddCycleCounts(stateDir, runId) {
1089
1112
  return { red, green };
1090
1113
  }
1091
1114
 
1115
+ // Mirrors src/tdd-cycle.ts::computeRalphLoopStatus — kept inline so the
1116
+ // SessionStart hook can write ralph-loop.json without depending on the CLI
1117
+ // binary being installed globally. Any schema change must update both copies.
1118
+ async function computeRalphLoopStatusInline(stateDir, runId) {
1119
+ const filePath = path.join(stateDir, "tdd-cycle-log.jsonl");
1120
+ const raw = await readTextFile(filePath, "");
1121
+ const sliceMap = new Map();
1122
+ const acClosed = new Set();
1123
+ const redOpenSlices = [];
1124
+ let loopIteration = 0;
1125
+ for (const rawLine of raw.split(/\\r?\\n/gu)) {
1126
+ const line = rawLine.trim();
1127
+ if (line.length === 0) continue;
1128
+ let row;
1129
+ try { row = JSON.parse(line); } catch { continue; }
1130
+ if (!row || typeof row !== "object" || Array.isArray(row)) continue;
1131
+ const rowRun = typeof row.runId === "string" && row.runId.length > 0 ? row.runId : runId;
1132
+ if (rowRun !== runId) continue;
1133
+ const slice = typeof row.slice === "string" && row.slice.length > 0 ? row.slice : "S-unknown";
1134
+ let state = sliceMap.get(slice);
1135
+ if (!state) {
1136
+ state = { slice, redCount: 0, greenCount: 0, refactorCount: 0, redOpen: false, acIds: [] };
1137
+ sliceMap.set(slice, state);
1138
+ }
1139
+ const exitCode = typeof row.exitCode === "number" ? row.exitCode : undefined;
1140
+ if (row.phase === "red") {
1141
+ state.redCount += 1;
1142
+ if (exitCode !== undefined && exitCode !== 0) state.redOpen = true;
1143
+ } else if (row.phase === "green") {
1144
+ state.greenCount += 1;
1145
+ state.redOpen = false;
1146
+ loopIteration += 1;
1147
+ if (Array.isArray(row.acIds)) {
1148
+ for (const acId of row.acIds) {
1149
+ if (typeof acId !== "string" || acId.length === 0) continue;
1150
+ acClosed.add(acId);
1151
+ if (!state.acIds.includes(acId)) state.acIds.push(acId);
1152
+ }
1153
+ }
1154
+ } else if (row.phase === "refactor") {
1155
+ state.refactorCount += 1;
1156
+ }
1157
+ }
1158
+ for (const state of sliceMap.values()) {
1159
+ if (state.redOpen) redOpenSlices.push(state.slice);
1160
+ }
1161
+ const slices = Array.from(sliceMap.values()).sort((a, b) => a.slice.localeCompare(b.slice, "en"));
1162
+ return {
1163
+ schemaVersion: 1,
1164
+ runId,
1165
+ loopIteration,
1166
+ redOpen: redOpenSlices.length > 0,
1167
+ redOpenSlices,
1168
+ acClosed: Array.from(acClosed).sort(),
1169
+ sliceCount: slices.length,
1170
+ slices,
1171
+ lastUpdatedAt: new Date().toISOString()
1172
+ };
1173
+ }
1174
+
1092
1175
  function tddCycleStateFromCounts(counts) {
1093
1176
  if (counts.red <= 0) return "need_red";
1094
1177
  if (counts.red > counts.green) return "red_open";
@@ -20,7 +20,7 @@ export const TDD = {
20
20
  "The stage intent is review/ship sign-off rather than implementation"
21
21
  ],
22
22
  checklist: [
23
- "Select plan slice — pick one task from the plan. Do not batch multiple tasks.",
23
+ "Select plan slice — pick one task from the plan. Do not batch multiple tasks. Before starting, read `.cclaw/state/ralph-loop.json` (`loopIteration`, `acClosed[]`, `redOpenSlices[]`) so you skip cycles already closed.",
24
24
  "Map to acceptance criterion — identify the specific spec criterion this test proves.",
25
25
  "Dispatch mandatory `tdd-red` execution (or `test-author` in TEST_RED_ONLY mode) — produce failing behavior tests and RED evidence only (no production edits). Set `CCLAW_ACTIVE_AGENT=tdd-red` when supported.",
26
26
  "RED: Capture failure output — copy the exact failure output as RED evidence. Record in artifact.",
@@ -29,7 +29,7 @@ export const TDD = {
29
29
  "GREEN: Verify no regressions — if any existing test breaks, fix the regression before proceeding.",
30
30
  "Run verification-before-completion discipline for the slice — capture a fresh test command, commit SHA, and explicit PASS/FAIL status before completion claims.",
31
31
  "REFACTOR: Dispatch `tdd-refactor` execution (or dedicated refactor mode) to improve code quality without behavior changes. Set `CCLAW_ACTIVE_AGENT=tdd-refactor` when supported.",
32
- "Record evidence — capture RED failure, GREEN output, and REFACTOR notes in the TDD artifact.",
32
+ "Record evidence — capture RED failure, GREEN output, and REFACTOR notes in the TDD artifact. When logging the `green` row via `/cc-ops tdd-log green`, attach the closed acceptance-criterion IDs in `acIds` so Ralph Loop status counts them.",
33
33
  "Annotate traceability — link to plan task ID and spec criterion.",
34
34
  "Per-Slice Review (conditional) — if `.cclaw/config.yaml::sliceReview.enabled` is true and the slice meets any trigger (touchCount >= filesChangedThreshold, touchPaths match touchTriggers, or highRisk=true), append a `## Per-Slice Review` entry for this slice before moving on (see the dedicated section below).",
35
35
  "Repeat for each slice — return to step 1 for the next plan slice."
@@ -35,7 +35,9 @@ Each JSON line must include:
35
35
  - \`slice\` (e.g. \`S-1\`)
36
36
  - \`phase\` (\`red\` | \`green\` | \`refactor\`)
37
37
  - \`command\`
38
- - optional: \`files\`, \`exitCode\`, \`note\`
38
+ - optional: \`files\`, \`exitCode\`, \`note\`, \`acIds\` (array of acceptance
39
+ criterion IDs like \`["AC-1"]\` — GREEN rows use this to drive the Ralph
40
+ Loop status summary at \`.cclaw/state/ralph-loop.json\`).
39
41
 
40
42
  ## Primary skill
41
43
 
@@ -64,8 +66,16 @@ Do not fake RED evidence. A \`red\` entry must correspond to a failing test comm
64
66
  - \`slice\`: user-provided slice id
65
67
  - \`phase\`: red|green|refactor
66
68
  - \`command\`: test command or refactor verification command
69
+ - \`acIds\` (optional, recommended on \`green\`): the acceptance-criterion
70
+ IDs this GREEN row closes (e.g. \`["AC-1","AC-3"]\`). The SessionStart
71
+ hook aggregates distinct \`acIds\` from green rows into \`acClosed\`
72
+ inside \`.cclaw/state/ralph-loop.json\` so \`/cc-next\` can answer
73
+ "is the Ralph Loop done?" without parsing the artifact.
67
74
  3. Append one line to \`${logPath()}\`.
68
- 4. \`show\`: print the last 20 lines grouped by slice.
75
+ 4. After append, refresh Ralph Loop status with
76
+ \`cclaw internal tdd-loop-status --quiet\` (the SessionStart hook also
77
+ refreshes it, but a manual refresh is safe and idempotent).
78
+ 5. \`show\`: print the last 20 lines grouped by slice.
69
79
 
70
80
  ## Validation
71
81
 
@@ -14,6 +14,7 @@ import { readFlowState, writeFlowState } from "../runs.js";
14
14
  import { FLOW_STAGES } from "../types.js";
15
15
  import { runEnvelopeValidateCommand } from "./envelope-validate.js";
16
16
  import { runKnowledgeDigestCommand } from "./knowledge-digest.js";
17
+ import { runTddLoopStatusCommand } from "./tdd-loop-status.js";
17
18
  import { runTddRedEvidenceCommand } from "./tdd-red-evidence.js";
18
19
  function unique(values) {
19
20
  return [...new Set(values)];
@@ -672,7 +673,7 @@ async function runHookCommand(projectRoot, args, io) {
672
673
  export async function runInternalCommand(projectRoot, argv, io) {
673
674
  const [subcommand, ...tokens] = argv;
674
675
  if (!subcommand) {
675
- io.stderr.write("cclaw internal requires a subcommand: advance-stage | verify-flow-state-diff | verify-current-state | knowledge-digest | envelope-validate | tdd-red-evidence | hook\n");
676
+ io.stderr.write("cclaw internal requires a subcommand: advance-stage | verify-flow-state-diff | verify-current-state | knowledge-digest | envelope-validate | tdd-red-evidence | tdd-loop-status | hook\n");
676
677
  return 1;
677
678
  }
678
679
  try {
@@ -694,10 +695,13 @@ export async function runInternalCommand(projectRoot, argv, io) {
694
695
  if (subcommand === "tdd-red-evidence") {
695
696
  return await runTddRedEvidenceCommand(projectRoot, tokens, io);
696
697
  }
698
+ if (subcommand === "tdd-loop-status") {
699
+ return await runTddLoopStatusCommand(projectRoot, tokens, io);
700
+ }
697
701
  if (subcommand === "hook") {
698
702
  return await runHookCommand(projectRoot, parseHookArgs(tokens), io);
699
703
  }
700
- io.stderr.write(`Unknown internal subcommand: ${subcommand}. Expected advance-stage | verify-flow-state-diff | verify-current-state | knowledge-digest | envelope-validate | tdd-red-evidence | hook\n`);
704
+ io.stderr.write(`Unknown internal subcommand: ${subcommand}. Expected advance-stage | verify-flow-state-diff | verify-current-state | knowledge-digest | envelope-validate | tdd-red-evidence | tdd-loop-status | hook\n`);
701
705
  return 1;
702
706
  }
703
707
  catch (err) {
@@ -0,0 +1,14 @@
1
+ import type { Writable } from "node:stream";
2
+ import { type RalphLoopStatus } from "../tdd-cycle.js";
3
+ interface InternalIo {
4
+ stdout: Writable;
5
+ stderr: Writable;
6
+ }
7
+ /**
8
+ * Produces a one-line "Ralph Loop: iter=X, slices=Y, acClosed=Z, redOpen=..."
9
+ * summary — suitable for session-digest / bootstrap surfaces where the user
10
+ * just needs a progress indicator, not the full slice breakdown.
11
+ */
12
+ export declare function formatRalphLoopStatusLine(status: RalphLoopStatus): string;
13
+ export declare function runTddLoopStatusCommand(projectRoot: string, argv: string[], io: InternalIo): Promise<number>;
14
+ export {};
@@ -0,0 +1,68 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { RUNTIME_ROOT } from "../constants.js";
4
+ import { writeFileSafe } from "../fs-utils.js";
5
+ import { readFlowState } from "../runs.js";
6
+ import { computeRalphLoopStatus, parseTddCycleLog } from "../tdd-cycle.js";
7
+ function parseArgs(tokens) {
8
+ const args = { json: false, quiet: false, write: true };
9
+ for (const token of tokens) {
10
+ if (token === "--json")
11
+ args.json = true;
12
+ else if (token === "--quiet")
13
+ args.quiet = true;
14
+ else if (token === "--no-write")
15
+ args.write = false;
16
+ else if (token === "--write")
17
+ args.write = true;
18
+ else
19
+ throw new Error(`Unknown tdd-loop-status flag: ${token}`);
20
+ }
21
+ return args;
22
+ }
23
+ function stateDir(projectRoot) {
24
+ return path.join(projectRoot, RUNTIME_ROOT, "state");
25
+ }
26
+ async function readCycleLog(projectRoot) {
27
+ const filePath = path.join(stateDir(projectRoot), "tdd-cycle-log.jsonl");
28
+ try {
29
+ return await fs.readFile(filePath, "utf8");
30
+ }
31
+ catch (err) {
32
+ if (err.code === "ENOENT")
33
+ return "";
34
+ throw err;
35
+ }
36
+ }
37
+ /**
38
+ * Produces a one-line "Ralph Loop: iter=X, slices=Y, acClosed=Z, redOpen=..."
39
+ * summary — suitable for session-digest / bootstrap surfaces where the user
40
+ * just needs a progress indicator, not the full slice breakdown.
41
+ */
42
+ export function formatRalphLoopStatusLine(status) {
43
+ const redOpen = status.redOpenSlices.length > 0
44
+ ? status.redOpenSlices.join(",")
45
+ : "none";
46
+ return `Ralph Loop: iter=${status.loopIteration}, slices=${status.sliceCount}, acClosed=${status.acClosed.length}, redOpen=${redOpen}`;
47
+ }
48
+ export async function runTddLoopStatusCommand(projectRoot, argv, io) {
49
+ const args = parseArgs(argv);
50
+ const flow = await readFlowState(projectRoot).catch(() => null);
51
+ const runId = flow?.activeRunId ?? "active";
52
+ const text = await readCycleLog(projectRoot);
53
+ const entries = parseTddCycleLog(text);
54
+ const status = computeRalphLoopStatus(entries, { runId });
55
+ if (args.write) {
56
+ const target = path.join(stateDir(projectRoot), "ralph-loop.json");
57
+ await writeFileSafe(target, `${JSON.stringify(status, null, 2)}\n`);
58
+ }
59
+ if (!args.quiet) {
60
+ if (args.json) {
61
+ io.stdout.write(`${JSON.stringify(status, null, 2)}\n`);
62
+ }
63
+ else {
64
+ io.stdout.write(`${formatRalphLoopStatusLine(status)}\n`);
65
+ }
66
+ }
67
+ return 0;
68
+ }
@@ -9,6 +9,12 @@ export interface TddCycleEntry {
9
9
  files?: string[];
10
10
  exitCode?: number;
11
11
  note?: string;
12
+ /**
13
+ * Optional acceptance-criterion IDs this log line relates to (e.g. `["AC-1"]`).
14
+ * Used by the Ralph Loop status summary to surface how many ACs have been
15
+ * closed by a GREEN cycle without forcing the user to track them manually.
16
+ */
17
+ acIds?: string[];
12
18
  }
13
19
  export interface TddCycleValidation {
14
20
  ok: boolean;
@@ -20,6 +26,40 @@ export declare function parseTddCycleLog(text: string): TddCycleEntry[];
20
26
  export declare function validateTddCycleOrder(entries: TddCycleEntry[], options?: {
21
27
  runId?: string;
22
28
  }): TddCycleValidation;
29
+ export interface RalphLoopSliceState {
30
+ slice: string;
31
+ redCount: number;
32
+ greenCount: number;
33
+ refactorCount: number;
34
+ redOpen: boolean;
35
+ acIds: string[];
36
+ }
37
+ export interface RalphLoopStatus {
38
+ schemaVersion: 1;
39
+ runId: string;
40
+ /**
41
+ * Number of RED -> GREEN cycles observed for the run — a rough "Ralph Loop"
42
+ * iteration counter that mirrors how many passing tests the loop has
43
+ * delivered so far.
44
+ */
45
+ loopIteration: number;
46
+ redOpen: boolean;
47
+ redOpenSlices: string[];
48
+ acClosed: string[];
49
+ sliceCount: number;
50
+ slices: RalphLoopSliceState[];
51
+ lastUpdatedAt: string;
52
+ }
53
+ /**
54
+ * Derive a lightweight Ralph Loop summary from parsed tdd-cycle-log entries.
55
+ * The goal is to give the model a single source of truth for "am I done
56
+ * iterating?" — it collapses per-slice progress and distinct closed AC IDs
57
+ * (from GREEN rows) into a single artifact the next-command contract reads.
58
+ */
59
+ export declare function computeRalphLoopStatus(entries: TddCycleEntry[], options?: {
60
+ runId?: string;
61
+ now?: Date;
62
+ }): RalphLoopStatus;
23
63
  /**
24
64
  * Checks whether the log contains a failing RED record associated with
25
65
  * `productionPath` for the active run.
package/dist/tdd-cycle.js CHANGED
@@ -25,7 +25,11 @@ export function parseTddCycleLog(text) {
25
25
  ? parsed.files.filter((item) => typeof item === "string")
26
26
  : undefined,
27
27
  exitCode: typeof parsed.exitCode === "number" ? parsed.exitCode : undefined,
28
- note: typeof parsed.note === "string" ? parsed.note : undefined
28
+ note: typeof parsed.note === "string" ? parsed.note : undefined,
29
+ acIds: Array.isArray(parsed.acIds)
30
+ ? parsed.acIds
31
+ .filter((item) => typeof item === "string" && item.length > 0)
32
+ : undefined
29
33
  };
30
34
  out.push(entry);
31
35
  }
@@ -122,6 +126,72 @@ export function validateTddCycleOrder(entries, options = {}) {
122
126
  function normalizePath(value) {
123
127
  return value.replace(/\\/gu, "/").toLowerCase();
124
128
  }
129
+ /**
130
+ * Derive a lightweight Ralph Loop summary from parsed tdd-cycle-log entries.
131
+ * The goal is to give the model a single source of truth for "am I done
132
+ * iterating?" — it collapses per-slice progress and distinct closed AC IDs
133
+ * (from GREEN rows) into a single artifact the next-command contract reads.
134
+ */
135
+ export function computeRalphLoopStatus(entries, options = {}) {
136
+ const runId = options.runId ?? "active";
137
+ const filtered = entries.filter((entry) => options.runId ? entry.runId === options.runId : true);
138
+ const slicesMap = new Map();
139
+ const acClosedSet = new Set();
140
+ let loopIteration = 0;
141
+ const redOpenSlices = [];
142
+ for (const slice of Array.from(new Set(filtered.map((entry) => entry.slice)))) {
143
+ slicesMap.set(slice, {
144
+ slice,
145
+ redCount: 0,
146
+ greenCount: 0,
147
+ refactorCount: 0,
148
+ redOpen: false,
149
+ acIds: []
150
+ });
151
+ }
152
+ for (const entry of filtered) {
153
+ const state = slicesMap.get(entry.slice);
154
+ if (!state)
155
+ continue;
156
+ if (entry.phase === "red") {
157
+ state.redCount += 1;
158
+ if (entry.exitCode !== undefined && entry.exitCode !== 0) {
159
+ state.redOpen = true;
160
+ }
161
+ continue;
162
+ }
163
+ if (entry.phase === "green") {
164
+ state.greenCount += 1;
165
+ state.redOpen = false;
166
+ loopIteration += 1;
167
+ if (Array.isArray(entry.acIds)) {
168
+ for (const acId of entry.acIds) {
169
+ acClosedSet.add(acId);
170
+ if (!state.acIds.includes(acId))
171
+ state.acIds.push(acId);
172
+ }
173
+ }
174
+ continue;
175
+ }
176
+ state.refactorCount += 1;
177
+ }
178
+ for (const state of slicesMap.values()) {
179
+ if (state.redOpen)
180
+ redOpenSlices.push(state.slice);
181
+ }
182
+ const slices = Array.from(slicesMap.values()).sort((a, b) => a.slice.localeCompare(b.slice, "en"));
183
+ return {
184
+ schemaVersion: 1,
185
+ runId,
186
+ loopIteration,
187
+ redOpen: redOpenSlices.length > 0,
188
+ redOpenSlices,
189
+ acClosed: Array.from(acClosedSet).sort(),
190
+ sliceCount: slices.length,
191
+ slices,
192
+ lastUpdatedAt: (options.now ?? new Date()).toISOString()
193
+ };
194
+ }
125
195
  /**
126
196
  * Checks whether the log contains a failing RED record associated with
127
197
  * `productionPath` for the active run.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cclaw-cli",
3
- "version": "0.48.12",
3
+ "version": "0.48.13",
4
4
  "description": "Installer-first flow toolkit for coding agents",
5
5
  "type": "module",
6
6
  "bin": {