cc-claw 0.12.1 → 0.12.3

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.
Files changed (2) hide show
  1. package/dist/cli.js +685 -220
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -72,7 +72,7 @@ var VERSION;
72
72
  var init_version = __esm({
73
73
  "src/version.ts"() {
74
74
  "use strict";
75
- VERSION = true ? "0.12.1" : (() => {
75
+ VERSION = true ? "0.12.3" : (() => {
76
76
  try {
77
77
  return JSON.parse(readFileSync(join2(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
78
78
  } catch {
@@ -153,6 +153,9 @@ function migrateAgentColumns(db3) {
153
153
  if (!colNames.has("name")) db3.exec("ALTER TABLE agents ADD COLUMN name TEXT");
154
154
  if (!colNames.has("description")) db3.exec("ALTER TABLE agents ADD COLUMN description TEXT");
155
155
  if (!colNames.has("persona")) db3.exec("ALTER TABLE agents ADD COLUMN persona TEXT");
156
+ if (!colNames.has("stderr")) db3.exec("ALTER TABLE agents ADD COLUMN stderr TEXT");
157
+ if (!colNames.has("durationMs")) db3.exec("ALTER TABLE agents ADD COLUMN durationMs INTEGER");
158
+ if (!colNames.has("signal")) db3.exec("ALTER TABLE agents ADD COLUMN signal TEXT");
156
159
  }
157
160
  function initAgentTables(db3) {
158
161
  db3.exec(`
@@ -329,10 +332,21 @@ function updateAgentOutput(db3, id) {
329
332
  function updateAgentResult(db3, id, opts) {
330
333
  db3.prepare(`
331
334
  UPDATE agents SET exitCode = ?, resultSummary = ?, tokenInput = ?, tokenOutput = ?,
335
+ stderr = ?, durationMs = ?, signal = ?,
332
336
  status = CASE WHEN ? = 0 THEN 'completed' ELSE 'failed' END,
333
337
  completedAt = datetime('now')
334
338
  WHERE id = ?
335
- `).run(opts.exitCode, opts.resultSummary ?? null, opts.tokenInput ?? 0, opts.tokenOutput ?? 0, opts.exitCode, id);
339
+ `).run(
340
+ opts.exitCode,
341
+ opts.resultSummary ?? null,
342
+ opts.tokenInput ?? 0,
343
+ opts.tokenOutput ?? 0,
344
+ opts.stderr ?? null,
345
+ opts.durationMs ?? null,
346
+ opts.signal ?? null,
347
+ opts.exitCode,
348
+ id
349
+ );
336
350
  }
337
351
  function updateAgentMcpsAdded(db3, id, mcps2) {
338
352
  db3.prepare("UPDATE agents SET mcpsAdded = ? WHERE id = ?").run(JSON.stringify(mcps2), id);
@@ -678,8 +692,12 @@ var init_identity = __esm({
678
692
  // src/reflection/store.ts
679
693
  var store_exports4 = {};
680
694
  __export(store_exports4, {
695
+ advanceReviewSession: () => advanceReviewSession,
681
696
  cleanupBackupFiles: () => cleanupBackupFiles,
682
697
  cleanupReflectionData: () => cleanupReflectionData,
698
+ createReviewSession: () => createReviewSession,
699
+ deleteReviewSession: () => deleteReviewSession,
700
+ getAppliedCountTodayForFile: () => getAppliedCountTodayForFile,
683
701
  getAppliedInsightCountToday: () => getAppliedInsightCountToday,
684
702
  getAppliedInsights: () => getAppliedInsights,
685
703
  getGrowthMetrics: () => getGrowthMetrics,
@@ -688,8 +706,10 @@ __export(store_exports4, {
688
706
  getPendingInsightCount: () => getPendingInsightCount,
689
707
  getPendingInsights: () => getPendingInsights,
690
708
  getReflectionModelConfig: () => getReflectionModelConfig,
709
+ getReflectionSettings: () => getReflectionSettings,
691
710
  getReflectionStatus: () => getReflectionStatus,
692
711
  getRejectedInsights: () => getRejectedInsights,
712
+ getReviewSession: () => getReviewSession,
693
713
  getSignalCountForChat: () => getSignalCountForChat,
694
714
  getUnprocessedSignalCount: () => getUnprocessedSignalCount,
695
715
  getUnprocessedSignals: () => getUnprocessedSignals,
@@ -698,6 +718,7 @@ __export(store_exports4, {
698
718
  logSignal: () => logSignal,
699
719
  markSignalsProcessed: () => markSignalsProcessed,
700
720
  setReflectionModelConfig: () => setReflectionModelConfig,
721
+ setReflectionSettings: () => setReflectionSettings,
701
722
  setReflectionStatus: () => setReflectionStatus,
702
723
  updateInsightEffectiveness: () => updateInsightEffectiveness,
703
724
  updateInsightProposal: () => updateInsightProposal,
@@ -707,16 +728,37 @@ __export(store_exports4, {
707
728
  });
708
729
  import { existsSync, readdirSync, statSync, unlinkSync } from "fs";
709
730
  import { join as join3 } from "path";
710
- function cleanupBackupFiles(ccClawHome) {
711
- const identityDir = join3(ccClawHome, "identity");
712
- if (!existsSync(identityDir)) return;
713
- const thirtyDaysAgo = Date.now() - 30 * 24 * 60 * 60 * 1e3;
714
- for (const file of readdirSync(identityDir)) {
715
- if (!file.includes(".bak.")) continue;
716
- const fullPath = join3(identityDir, file);
731
+ function cleanupBackupFiles(ccClawHome, retentionDays = 7) {
732
+ const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
733
+ const dirs = [
734
+ join3(ccClawHome, "identity"),
735
+ join3(ccClawHome, "workspace", "context")
736
+ ];
737
+ const skillsRoot = join3(ccClawHome, "workspace", "skills");
738
+ try {
739
+ if (existsSync(skillsRoot)) {
740
+ for (const entry of readdirSync(skillsRoot)) {
741
+ const p = join3(skillsRoot, entry);
742
+ try {
743
+ if (statSync(p).isDirectory()) dirs.push(p);
744
+ } catch {
745
+ }
746
+ }
747
+ }
748
+ } catch {
749
+ }
750
+ for (const dir of dirs) {
751
+ if (!existsSync(dir)) continue;
717
752
  try {
718
- const stat2 = statSync(fullPath);
719
- if (stat2.mtimeMs < thirtyDaysAgo) unlinkSync(fullPath);
753
+ for (const file of readdirSync(dir)) {
754
+ if (!file.includes(".bak.")) continue;
755
+ const fullPath = join3(dir, file);
756
+ try {
757
+ const stat2 = statSync(fullPath);
758
+ if (stat2.mtimeMs < cutoffMs) unlinkSync(fullPath);
759
+ } catch {
760
+ }
761
+ }
720
762
  } catch {
721
763
  }
722
764
  }
@@ -792,9 +834,25 @@ function initReflectionTables(db3) {
792
834
  model TEXT,
793
835
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
794
836
  );
837
+
838
+ CREATE TABLE IF NOT EXISTS review_sessions (
839
+ chatId TEXT PRIMARY KEY,
840
+ insightIds TEXT NOT NULL,
841
+ currentIndex INTEGER NOT NULL DEFAULT 0,
842
+ results TEXT NOT NULL DEFAULT '{}',
843
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
844
+ );
845
+
846
+ CREATE TABLE IF NOT EXISTS reflection_settings (
847
+ chatId TEXT PRIMARY KEY,
848
+ perFileCap INTEGER NOT NULL DEFAULT 3,
849
+ backupRetentionDays INTEGER NOT NULL DEFAULT 7,
850
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
851
+ );
795
852
  `);
796
853
  cleanupReflectionData(db3);
797
- cleanupBackupFiles(CC_CLAW_HOME);
854
+ const settings = getReflectionSettings(db3, "global");
855
+ cleanupBackupFiles(CC_CLAW_HOME, settings.backupRetentionDays);
798
856
  }
799
857
  function logSignal(db3, params) {
800
858
  const result = db3.prepare(`
@@ -939,6 +997,13 @@ function getAppliedInsightCountToday(db3, chatId) {
939
997
  `).get(chatId);
940
998
  return row.count;
941
999
  }
1000
+ function getAppliedCountTodayForFile(db3, chatId, targetFile) {
1001
+ const row = db3.prepare(`
1002
+ SELECT COUNT(*) as count FROM insights
1003
+ WHERE chatId = ? AND status = 'applied' AND targetFile = ? AND appliedAt >= date('now') AND appliedAt < date('now', '+1 day')
1004
+ `).get(chatId, targetFile);
1005
+ return row.count;
1006
+ }
942
1007
  function upsertGrowthMetric(db3, chatId, period, data) {
943
1008
  db3.prepare(`
944
1009
  INSERT OR REPLACE INTO growth_metrics (chatId, period, corrections, praises, errors, insightsApplied)
@@ -966,6 +1031,58 @@ function setReflectionModelConfig(db3, chatId, mode, backend2, model2) {
966
1031
  VALUES (?, ?, ?, ?)
967
1032
  `).run(chatId, mode, backend2 ?? null, model2 ?? null);
968
1033
  }
1034
+ function createReviewSession(db3, chatId, insightIds) {
1035
+ db3.prepare(`
1036
+ INSERT OR REPLACE INTO review_sessions (chatId, insightIds, currentIndex, results)
1037
+ VALUES (?, ?, 0, '{}')
1038
+ `).run(chatId, JSON.stringify(insightIds));
1039
+ }
1040
+ function getReviewSession(db3, chatId) {
1041
+ const row = db3.prepare(
1042
+ "SELECT * FROM review_sessions WHERE chatId = ?"
1043
+ ).get(chatId);
1044
+ if (!row) return null;
1045
+ return {
1046
+ chatId: row.chatId,
1047
+ insightIds: JSON.parse(row.insightIds),
1048
+ currentIndex: row.currentIndex,
1049
+ results: JSON.parse(row.results),
1050
+ created_at: row.created_at
1051
+ };
1052
+ }
1053
+ function advanceReviewSession(db3, chatId, insightId, action) {
1054
+ const session2 = getReviewSession(db3, chatId);
1055
+ if (!session2) return null;
1056
+ session2.results[insightId] = action;
1057
+ session2.currentIndex = session2.currentIndex + 1;
1058
+ db3.prepare(`
1059
+ UPDATE review_sessions SET currentIndex = ?, results = ? WHERE chatId = ?
1060
+ `).run(session2.currentIndex, JSON.stringify(session2.results), chatId);
1061
+ return session2;
1062
+ }
1063
+ function deleteReviewSession(db3, chatId) {
1064
+ db3.prepare("DELETE FROM review_sessions WHERE chatId = ?").run(chatId);
1065
+ }
1066
+ function getReflectionSettings(db3, chatId) {
1067
+ const row = db3.prepare(
1068
+ "SELECT perFileCap, backupRetentionDays FROM reflection_settings WHERE chatId = ?"
1069
+ ).get(chatId);
1070
+ return {
1071
+ perFileCap: row?.perFileCap ?? 3,
1072
+ backupRetentionDays: row?.backupRetentionDays ?? 7
1073
+ };
1074
+ }
1075
+ function setReflectionSettings(db3, chatId, settings) {
1076
+ const current = getReflectionSettings(db3, chatId);
1077
+ db3.prepare(`
1078
+ INSERT OR REPLACE INTO reflection_settings (chatId, perFileCap, backupRetentionDays)
1079
+ VALUES (?, ?, ?)
1080
+ `).run(
1081
+ chatId,
1082
+ settings.perFileCap ?? current.perFileCap,
1083
+ settings.backupRetentionDays ?? current.backupRetentionDays
1084
+ );
1085
+ }
969
1086
  function cleanupReflectionData(db3) {
970
1087
  db3.prepare(`
971
1088
  DELETE FROM feedback_signals WHERE created_at < datetime('now', '-90 days')
@@ -976,6 +1093,9 @@ function cleanupReflectionData(db3) {
976
1093
  db3.prepare(`
977
1094
  DELETE FROM growth_metrics WHERE created_at < datetime('now', '-365 days')
978
1095
  `).run();
1096
+ db3.prepare(`
1097
+ DELETE FROM review_sessions WHERE created_at < datetime('now', '-1 day')
1098
+ `).run();
979
1099
  }
980
1100
  var init_store4 = __esm({
981
1101
  "src/reflection/store.ts"() {
@@ -3357,13 +3477,25 @@ var init_claude = __esm({
3357
3477
  import { existsSync as existsSync3 } from "fs";
3358
3478
  import { execSync as execSync2 } from "child_process";
3359
3479
  import { join as join5 } from "path";
3360
- var GeminiAdapter;
3480
+ function stripThinkingContent(text) {
3481
+ let result = text;
3482
+ for (const pattern of THINKING_PATTERNS) {
3483
+ result = result.replace(pattern, "");
3484
+ }
3485
+ return result.trim();
3486
+ }
3487
+ var THINKING_PATTERNS, GeminiAdapter;
3361
3488
  var init_gemini = __esm({
3362
3489
  "src/backends/gemini.ts"() {
3363
3490
  "use strict";
3364
3491
  init_store5();
3365
3492
  init_env();
3366
3493
  init_paths();
3494
+ THINKING_PATTERNS = [
3495
+ /<think>[\s\S]*?<\/think>/gi,
3496
+ /<thinking>[\s\S]*?<\/thinking>/gi,
3497
+ /\[Thought:\s*true\][\s\S]*?(?:\[\/Thought\]|\[Thought:\s*false\])/gi
3498
+ ];
3367
3499
  GeminiAdapter = class {
3368
3500
  id = "gemini";
3369
3501
  displayName = "Gemini";
@@ -3473,8 +3605,12 @@ var init_gemini = __esm({
3473
3605
  if (line.type === "init") {
3474
3606
  events.push({ type: "init", sessionId: line.session_id });
3475
3607
  } else if (line.type === "message" && line.role === "assistant") {
3476
- if (line.content) {
3477
- events.push({ type: "text", text: line.content });
3608
+ if (line.thought === true) {
3609
+ } else if (line.content) {
3610
+ const cleaned = stripThinkingContent(line.content);
3611
+ if (cleaned) {
3612
+ events.push({ type: "text", text: cleaned });
3613
+ }
3478
3614
  }
3479
3615
  } else if (line.type === "tool_use") {
3480
3616
  events.push({
@@ -5328,6 +5464,7 @@ function spawnAgentProcess(runner, opts, callbacks) {
5328
5464
  const rl2 = createInterface2({ input: child.stdout });
5329
5465
  rl2.on("line", (line) => {
5330
5466
  try {
5467
+ callbacks.onRawLine?.(line);
5331
5468
  const raw = JSON.parse(line);
5332
5469
  const events = runner.parseLine(raw);
5333
5470
  for (const event of events) {
@@ -5682,6 +5819,103 @@ var init_loader2 = __esm({
5682
5819
  }
5683
5820
  });
5684
5821
 
5822
+ // src/agents/agent-log.ts
5823
+ import { writeFileSync as writeFileSync3, readdirSync as readdirSync5, statSync as statSync3, unlinkSync as unlinkSync4, mkdirSync as mkdirSync3 } from "fs";
5824
+ import { join as join10 } from "path";
5825
+ function truncate(text, maxBytes) {
5826
+ if (Buffer.byteLength(text, "utf-8") <= maxBytes) return text;
5827
+ let lo = 0, hi = text.length;
5828
+ while (lo < hi) {
5829
+ const mid = lo + hi + 1 >>> 1;
5830
+ if (Buffer.byteLength(text.slice(0, mid), "utf-8") <= maxBytes) lo = mid;
5831
+ else hi = mid - 1;
5832
+ }
5833
+ const truncated = text.slice(0, lo);
5834
+ const droppedBytes = Buffer.byteLength(text, "utf-8") - Buffer.byteLength(truncated, "utf-8");
5835
+ return truncated + `
5836
+ [...truncated ${droppedBytes} bytes...]`;
5837
+ }
5838
+ function writeAgentLog(data) {
5839
+ const logPath = join10(AGENTS_PATH, `${data.agentId}.log`);
5840
+ const exitDisplay = data.exitCodeForced ? `${data.exitCode} (forced \u2014 no output from original exit code 0)` : String(data.exitCode ?? "null");
5841
+ const stderrContent = truncate(data.stderr || "(empty)", MAX_STDERR_BYTES);
5842
+ const stdoutContent = truncate(data.rawStdoutLines.join("\n") || "(empty)", MAX_STDOUT_BYTES);
5843
+ const lines = [
5844
+ "=== Agent Execution Log ===",
5845
+ `Agent ID: ${data.agentId}`,
5846
+ `Runner: ${data.runner}`,
5847
+ `Model: ${data.model}`,
5848
+ `Task: ${data.task.slice(0, 200)}${data.task.length > 200 ? "..." : ""}`,
5849
+ `Started: ${data.startedAt.toISOString()}`,
5850
+ `Duration: ${data.durationMs}ms`,
5851
+ `Exit Code: ${exitDisplay}`,
5852
+ `Signal: ${data.signal ?? "null"}`,
5853
+ `Tokens: ${data.tokenInput} in / ${data.tokenOutput} out`,
5854
+ "",
5855
+ "--- STDERR ---",
5856
+ stderrContent,
5857
+ "",
5858
+ "--- STDOUT (raw NDJSON lines) ---",
5859
+ stdoutContent,
5860
+ ""
5861
+ ];
5862
+ try {
5863
+ mkdirSync3(AGENTS_PATH, { recursive: true });
5864
+ writeFileSync3(logPath, lines.join("\n"), "utf-8");
5865
+ } catch (err) {
5866
+ log(`[agent-log] Failed to write log for ${data.agentId}: ${err}`);
5867
+ }
5868
+ return logPath;
5869
+ }
5870
+ function pruneAgentLogs() {
5871
+ try {
5872
+ mkdirSync3(AGENTS_PATH, { recursive: true });
5873
+ const files = readdirSync5(AGENTS_PATH).filter((f) => f.endsWith(".log")).map((f) => {
5874
+ const fullPath = join10(AGENTS_PATH, f);
5875
+ const stat2 = statSync3(fullPath);
5876
+ return { path: fullPath, mtime: stat2.mtimeMs, size: stat2.size };
5877
+ });
5878
+ const now = Date.now();
5879
+ const remaining = files.filter((f) => {
5880
+ if (now - f.mtime > MAX_AGE_MS) {
5881
+ try {
5882
+ unlinkSync4(f.path);
5883
+ } catch {
5884
+ }
5885
+ return false;
5886
+ }
5887
+ return true;
5888
+ });
5889
+ remaining.sort((a, b) => a.mtime - b.mtime);
5890
+ let totalSize = remaining.reduce((sum, f) => sum + f.size, 0);
5891
+ for (const f of remaining) {
5892
+ if (totalSize <= MAX_DIR_BYTES) break;
5893
+ try {
5894
+ unlinkSync4(f.path);
5895
+ totalSize -= f.size;
5896
+ } catch {
5897
+ }
5898
+ }
5899
+ if (files.length > remaining.length) {
5900
+ log(`[agent-log] Pruned ${files.length - remaining.length} old log file(s)`);
5901
+ }
5902
+ } catch (err) {
5903
+ log(`[agent-log] Prune error: ${err}`);
5904
+ }
5905
+ }
5906
+ var MAX_STDERR_BYTES, MAX_STDOUT_BYTES, MAX_AGE_MS, MAX_DIR_BYTES;
5907
+ var init_agent_log = __esm({
5908
+ "src/agents/agent-log.ts"() {
5909
+ "use strict";
5910
+ init_paths();
5911
+ init_log();
5912
+ MAX_STDERR_BYTES = 50 * 1024;
5913
+ MAX_STDOUT_BYTES = 100 * 1024;
5914
+ MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
5915
+ MAX_DIR_BYTES = 50 * 1024 * 1024;
5916
+ }
5917
+ });
5918
+
5685
5919
  // src/agents/orchestrator.ts
5686
5920
  import { existsSync as existsSync10 } from "fs";
5687
5921
  async function withRunnerLock(runnerId, fn) {
@@ -5751,6 +5985,19 @@ async function spawnSubAgent(chatId, opts) {
5751
5985
  const orchestrationId = getOrCreateOrchestration(chatId);
5752
5986
  const runner = getRunner(opts.runner);
5753
5987
  if (!runner) throw new Error(`Unknown runner: ${opts.runner}`);
5988
+ if (opts.model) {
5989
+ try {
5990
+ const { getAdapter: getAdapter2 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
5991
+ const adapter = getAdapter2(opts.runner);
5992
+ if (adapter.availableModels && !adapter.availableModels[opts.model]) {
5993
+ const validModels = Object.keys(adapter.availableModels).join(", ");
5994
+ throw new Error(`Unknown model "${opts.model}" for ${opts.runner}. Available: ${validModels}`);
5995
+ }
5996
+ } catch (err) {
5997
+ if (err instanceof Error && err.message.startsWith("Unknown model")) throw err;
5998
+ warn(`[orchestrator] Could not validate model "${opts.model}" for ${opts.runner}: ${err}`);
5999
+ }
6000
+ }
5754
6001
  const agentId = createAgent(db3, {
5755
6002
  orchestrationId,
5756
6003
  runnerId: opts.runner,
@@ -5873,7 +6120,9 @@ async function startAgent(agentId, chatId, opts) {
5873
6120
  }
5874
6121
  updateAgentStatus(db3, agentId, "starting");
5875
6122
  const stderrChunks = [];
6123
+ const rawStdoutLines = [];
5876
6124
  let gotResult = false;
6125
+ const startedAt = Date.now();
5877
6126
  const child = spawnAgentProcess(runner, {
5878
6127
  task: opts.task,
5879
6128
  cwd: opts.cwd,
@@ -5890,9 +6139,20 @@ async function startAgent(agentId, chatId, opts) {
5890
6139
  onText: () => {
5891
6140
  updateAgentOutput(db3, agentId);
5892
6141
  },
6142
+ onRawLine: (line) => rawStdoutLines.push(line),
5893
6143
  onResult: (text, usage2) => {
5894
6144
  gotResult = true;
5895
- handleAgentComplete(agentId, chatId, text, usage2, mcpsAdded);
6145
+ const durationMs = Date.now() - startedAt;
6146
+ const stderr = Buffer.concat(stderrChunks).toString().trim();
6147
+ handleAgentComplete(agentId, chatId, text, usage2, mcpsAdded, {
6148
+ stderr,
6149
+ durationMs,
6150
+ rawStdoutLines,
6151
+ startedAt: new Date(startedAt),
6152
+ runner: opts.runner,
6153
+ model: opts.model ?? "default",
6154
+ task: opts.task
6155
+ });
5896
6156
  },
5897
6157
  onError: (err) => {
5898
6158
  const detail = diagnoseSpawnError(err, exePath, opts.cwd);
@@ -5943,18 +6203,46 @@ async function startAgent(agentId, chatId, opts) {
5943
6203
  { runner: opts.runner, task: opts.task, model: opts.model },
5944
6204
  agentId
5945
6205
  );
5946
- child.on("close", (code) => {
6206
+ child.on("close", (code, signal) => {
5947
6207
  if (gotResult) return;
5948
6208
  const agent = getAgent(db3, agentId);
5949
6209
  if (!agent || agent.status === "completed" || agent.status === "failed" || agent.status === "cancelled") return;
5950
6210
  const stderr = Buffer.concat(stderrChunks).toString().trim();
5951
6211
  if (stderr) warn(`[orchestrator] Agent ${agentId.slice(0, 8)} stderr: ${stderr.slice(0, 500)}`);
5952
- const exitCode = code ?? 1;
5953
- const errMsg = exitCode !== 0 ? `CLI exited with code ${exitCode}${stderr ? `: ${stderr.slice(0, 200)}` : ""}` : "Process exited with no output";
5954
- updateAgentResult(db3, agentId, { exitCode });
6212
+ const durationMs = Date.now() - startedAt;
6213
+ const signalStr = signal ?? null;
6214
+ let exitCode = code ?? 1;
6215
+ let exitCodeForced = false;
6216
+ if (exitCode === 0) {
6217
+ exitCode = 1;
6218
+ exitCodeForced = true;
6219
+ warn(`[orchestrator] Agent ${agentId.slice(0, 8)}: exit code 0 with no output \u2014 forced to 1 (likely auth/model issue)`);
6220
+ }
6221
+ const errMsg = exitCodeForced ? `CLI exited cleanly but produced no output (${durationMs}ms) \u2014 likely auth failure or invalid model${stderr ? `: ${stderr.slice(0, 200)}` : ""}` : `CLI exited with code ${exitCode}${stderr ? `: ${stderr.slice(0, 200)}` : ""}`;
6222
+ updateAgentResult(db3, agentId, {
6223
+ exitCode,
6224
+ stderr: stderr || void 0,
6225
+ durationMs,
6226
+ signal: signalStr ?? void 0
6227
+ });
5955
6228
  clearTimeout(timeoutTimers.get(agentId));
5956
6229
  timeoutTimers.delete(agentId);
5957
6230
  activeProcesses.delete(agentId);
6231
+ writeAgentLog({
6232
+ agentId,
6233
+ runner: opts.runner,
6234
+ model: opts.model ?? "default",
6235
+ task: opts.task,
6236
+ startedAt: new Date(startedAt),
6237
+ durationMs,
6238
+ exitCode,
6239
+ exitCodeForced,
6240
+ signal: signalStr,
6241
+ tokenInput: 0,
6242
+ tokenOutput: 0,
6243
+ stderr,
6244
+ rawStdoutLines
6245
+ });
5958
6246
  const crashedAgent = getAgent(db3, agentId);
5959
6247
  if (crashedAgent) {
5960
6248
  const mcpsCrashed = crashedAgent.mcpsAdded ? JSON.parse(crashedAgent.mcpsAdded) : [];
@@ -5980,13 +6268,13 @@ async function startAgent(agentId, chatId, opts) {
5980
6268
  toAgentId: "main",
5981
6269
  fromAgentId: agentId,
5982
6270
  messageType: "task_result",
5983
- content: exitCode === 0 ? "(Completed with no text output)" : `Failed: ${errMsg}`
6271
+ content: `Failed: ${errMsg}`
5984
6272
  });
5985
6273
  notifyWithActivity(
5986
6274
  chatId,
5987
- `Agent ${agentId.slice(0, 8)} exited (code ${exitCode})${stderr ? ` \u2014 ${stderr.slice(0, 100)}` : ""}`,
5988
- exitCode === 0 ? "agent_completed" : "agent_failed",
5989
- { exitCode, stderr: stderr?.slice(0, 200) },
6275
+ `Agent ${agentId.slice(0, 8)} failed (code ${exitCode}, ${durationMs}ms)${stderr ? ` \u2014 ${stderr.slice(0, 100)}` : ""}`,
6276
+ "agent_failed",
6277
+ { exitCode, exitCodeForced, durationMs, signal: signalStr, stderr: stderr?.slice(0, 500) },
5990
6278
  agentId
5991
6279
  );
5992
6280
  const activeAgents = listActiveAgents(db3);
@@ -6019,7 +6307,7 @@ function diagnoseSpawnError(err, exePath, cwd) {
6019
6307
  }
6020
6308
  return err.message;
6021
6309
  }
6022
- async function handleAgentComplete(agentId, chatId, resultText, usage2, mcpsAdded) {
6310
+ async function handleAgentComplete(agentId, chatId, resultText, usage2, mcpsAdded, diagnostics) {
6023
6311
  const db3 = getDb();
6024
6312
  const agent = getAgent(db3, agentId);
6025
6313
  if (!agent) return;
@@ -6030,8 +6318,26 @@ async function handleAgentComplete(agentId, chatId, resultText, usage2, mcpsAdde
6030
6318
  exitCode: 0,
6031
6319
  resultSummary: resultText.slice(0, 1e4),
6032
6320
  tokenInput: usage2?.input ?? 0,
6033
- tokenOutput: usage2?.output ?? 0
6321
+ tokenOutput: usage2?.output ?? 0,
6322
+ stderr: diagnostics?.stderr || void 0,
6323
+ durationMs: diagnostics?.durationMs
6034
6324
  });
6325
+ if (diagnostics) {
6326
+ writeAgentLog({
6327
+ agentId,
6328
+ runner: diagnostics.runner,
6329
+ model: diagnostics.model,
6330
+ task: diagnostics.task,
6331
+ startedAt: diagnostics.startedAt,
6332
+ durationMs: diagnostics.durationMs,
6333
+ exitCode: 0,
6334
+ signal: null,
6335
+ tokenInput: usage2?.input ?? 0,
6336
+ tokenOutput: usage2?.output ?? 0,
6337
+ stderr: diagnostics.stderr,
6338
+ rawStdoutLines: diagnostics.rawStdoutLines
6339
+ });
6340
+ }
6035
6341
  if (usage2) {
6036
6342
  const runner = getRunner(agent.runnerId);
6037
6343
  if (runner) {
@@ -6087,7 +6393,15 @@ async function handleAgentComplete(agentId, chatId, resultText, usage2, mcpsAdde
6087
6393
  chatId,
6088
6394
  `Agent ${agentId.slice(0, 8)} completed \u2192 ${usage2?.input ?? 0} in / ${usage2?.output ?? 0} out tokens`,
6089
6395
  "agent_completed",
6090
- { runner: agent.runnerId, task: agent.task, tokenInput: usage2?.input, tokenOutput: usage2?.output, resultPreview: resultText.slice(0, 200) },
6396
+ {
6397
+ runner: agent.runnerId,
6398
+ task: agent.task,
6399
+ tokenInput: usage2?.input,
6400
+ tokenOutput: usage2?.output,
6401
+ resultPreview: resultText.slice(0, 200),
6402
+ durationMs: diagnostics?.durationMs,
6403
+ stderr: diagnostics?.stderr?.slice(0, 500) || void 0
6404
+ },
6091
6405
  agentId
6092
6406
  );
6093
6407
  const activeAgents = listActiveAgents(db3);
@@ -6213,6 +6527,7 @@ function shutdownOrchestrator() {
6213
6527
  }
6214
6528
  function initOrchestrator() {
6215
6529
  cleanupOrphanedMcpConfigs();
6530
+ pruneAgentLogs();
6216
6531
  const db3 = getDb();
6217
6532
  const staleCount = cleanupStaleAgents(db3);
6218
6533
  if (staleCount > 0) {
@@ -6235,6 +6550,7 @@ var init_orchestrator = __esm({
6235
6550
  init_log();
6236
6551
  init_store3();
6237
6552
  init_loader2();
6553
+ init_agent_log();
6238
6554
  activeProcesses = /* @__PURE__ */ new Map();
6239
6555
  timeoutTimers = /* @__PURE__ */ new Map();
6240
6556
  runnerLocks = /* @__PURE__ */ new Map();
@@ -6628,19 +6944,19 @@ __export(analyze_exports, {
6628
6944
  });
6629
6945
  import { spawn as spawn4 } from "child_process";
6630
6946
  import { createInterface as createInterface3 } from "readline";
6631
- import { readFileSync as readFileSync6, existsSync as existsSync11, readdirSync as readdirSync5, statSync as statSync3 } from "fs";
6632
- import { join as join10 } from "path";
6947
+ import { readFileSync as readFileSync6, existsSync as existsSync11, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
6948
+ import { join as join11 } from "path";
6633
6949
  import { homedir as homedir4 } from "os";
6634
6950
  function discoverReflectionTargets() {
6635
- const ccClawHome = join10(homedir4(), ".cc-claw");
6951
+ const ccClawHome = join11(homedir4(), ".cc-claw");
6636
6952
  const targets = [];
6637
6953
  try {
6638
- const skillsDir = join10(ccClawHome, "workspace", "skills");
6954
+ const skillsDir = join11(ccClawHome, "workspace", "skills");
6639
6955
  if (existsSync11(skillsDir)) {
6640
- for (const entry of readdirSync5(skillsDir)) {
6641
- const entryPath = join10(skillsDir, entry);
6642
- if (!statSync3(entryPath).isDirectory()) continue;
6643
- const skillFile = join10(entryPath, "SKILL.md");
6956
+ for (const entry of readdirSync6(skillsDir)) {
6957
+ const entryPath = join11(skillsDir, entry);
6958
+ if (!statSync4(entryPath).isDirectory()) continue;
6959
+ const skillFile = join11(entryPath, "SKILL.md");
6644
6960
  if (!existsSync11(skillFile)) continue;
6645
6961
  let desc = "skill";
6646
6962
  try {
@@ -6655,9 +6971,9 @@ function discoverReflectionTargets() {
6655
6971
  } catch {
6656
6972
  }
6657
6973
  try {
6658
- const contextDir = join10(ccClawHome, "workspace", "context");
6974
+ const contextDir = join11(ccClawHome, "workspace", "context");
6659
6975
  if (existsSync11(contextDir)) {
6660
- for (const entry of readdirSync5(contextDir)) {
6976
+ for (const entry of readdirSync6(contextDir)) {
6661
6977
  if (!entry.endsWith(".md")) continue;
6662
6978
  const name = entry.replace(/\.md$/, "");
6663
6979
  targets.push({ path: `workspace/context/${entry}`, description: `context file: ${name}` });
@@ -6689,6 +7005,7 @@ Rules:
6689
7005
  - WHY field max 2 sentences
6690
7006
  - Only propose changes you are confident about (confidence >= 0.6)
6691
7007
  - Never re-propose previously rejected insights
7008
+ - Do not propose more than 3 changes to the same target file in a single analysis (spread across files to avoid drift)
6692
7009
 
6693
7010
  Valid categories:
6694
7011
  ${categoryList}`);
@@ -6837,7 +7154,7 @@ function resolveReflectionAdapter(chatId) {
6837
7154
  }
6838
7155
  function readIdentityFile(filename) {
6839
7156
  try {
6840
- return readFileSync6(join10(IDENTITY_PATH, filename), "utf-8");
7157
+ return readFileSync6(join11(IDENTITY_PATH, filename), "utf-8");
6841
7158
  } catch {
6842
7159
  return "";
6843
7160
  }
@@ -7073,8 +7390,8 @@ __export(apply_exports, {
7073
7390
  isTargetAllowed: () => isTargetAllowed,
7074
7391
  rollbackInsight: () => rollbackInsight
7075
7392
  });
7076
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync3 } from "fs";
7077
- import { join as join11, dirname as dirname2 } from "path";
7393
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync12, mkdirSync as mkdirSync4, readdirSync as readdirSync7, unlinkSync as unlinkSync5 } from "fs";
7394
+ import { join as join12, dirname as dirname2 } from "path";
7078
7395
  function isTargetAllowed(relativePath) {
7079
7396
  if (relativePath.includes("..")) return false;
7080
7397
  if (!relativePath.endsWith(".md")) return false;
@@ -7124,6 +7441,21 @@ function applyDiff(original, diff, action) {
7124
7441
  warn(`[reflection/apply] Unknown diff action "${action}", returning original`);
7125
7442
  return original;
7126
7443
  }
7444
+ function pruneBackups(absolutePath) {
7445
+ const dir = dirname2(absolutePath);
7446
+ const baseName = absolutePath.split("/").pop() ?? "";
7447
+ try {
7448
+ const backups = readdirSync7(dir).filter((f) => f.startsWith(baseName + ".bak.")).sort().map((f) => join12(dir, f));
7449
+ while (backups.length > MAX_BACKUPS_PER_FILE) {
7450
+ const oldest = backups.shift();
7451
+ try {
7452
+ unlinkSync5(oldest);
7453
+ } catch {
7454
+ }
7455
+ }
7456
+ } catch {
7457
+ }
7458
+ }
7127
7459
  async function applyInsight(insightId) {
7128
7460
  const db3 = getDb();
7129
7461
  const insight = getInsightById(db3, insightId);
@@ -7139,7 +7471,7 @@ async function applyInsight(insightId) {
7139
7471
  if (!isTargetAllowed(insight.targetFile)) {
7140
7472
  return { success: false, message: `Target file "${insight.targetFile}" is not in the allowed list` };
7141
7473
  }
7142
- const absolutePath = join11(CC_CLAW_HOME, insight.targetFile);
7474
+ const absolutePath = join12(CC_CLAW_HOME, insight.targetFile);
7143
7475
  if (insight.proposedAction === "append" && insight.targetFile === "identity/SOUL.md") {
7144
7476
  if (existsSync12(absolutePath)) {
7145
7477
  const currentContent = readFileSync7(absolutePath, "utf-8");
@@ -7153,11 +7485,13 @@ async function applyInsight(insightId) {
7153
7485
  }
7154
7486
  }
7155
7487
  const chatId = insight.chatId ?? "global";
7156
- const appliedToday = getAppliedInsightCountToday(db3, chatId);
7157
- if (appliedToday >= DAILY_APPLY_CAP) {
7488
+ const settings = getReflectionSettings(db3, chatId);
7489
+ const perFileCap = settings.perFileCap;
7490
+ const appliedTodayForFile = getAppliedCountTodayForFile(db3, chatId, insight.targetFile);
7491
+ if (appliedTodayForFile >= perFileCap) {
7158
7492
  return {
7159
7493
  success: false,
7160
- message: `Daily apply cap reached (${appliedToday}/${DAILY_APPLY_CAP}). Try again tomorrow.`
7494
+ message: `Reflection cap reached for ${insight.targetFile} (${appliedTodayForFile}/${perFileCap} today). Try again tomorrow.`
7161
7495
  };
7162
7496
  }
7163
7497
  let original = "";
@@ -7171,13 +7505,14 @@ async function applyInsight(insightId) {
7171
7505
  try {
7172
7506
  const parentDir = dirname2(absolutePath);
7173
7507
  if (!existsSync12(parentDir)) {
7174
- mkdirSync3(parentDir, { recursive: true });
7508
+ mkdirSync4(parentDir, { recursive: true });
7175
7509
  }
7176
7510
  if (original) {
7177
- writeFileSync3(backupPath, original, "utf-8");
7511
+ writeFileSync4(backupPath, original, "utf-8");
7512
+ pruneBackups(absolutePath);
7178
7513
  }
7179
7514
  const newContent = applyDiff(original, insight.proposedDiff, insight.proposedAction);
7180
- writeFileSync3(absolutePath, newContent, "utf-8");
7515
+ writeFileSync4(absolutePath, newContent, "utf-8");
7181
7516
  const rollbackData = JSON.stringify({ original, backupPath, appliedAt: (/* @__PURE__ */ new Date()).toISOString() });
7182
7517
  updateInsightRollback(db3, insightId, rollbackData);
7183
7518
  updateInsightStatus(db3, insightId, "applied");
@@ -7205,7 +7540,7 @@ async function applyInsight(insightId) {
7205
7540
  warn(`[reflection/apply] Failed to apply insight #${insightId}: ${msg}`);
7206
7541
  try {
7207
7542
  if (original) {
7208
- writeFileSync3(absolutePath, original, "utf-8");
7543
+ writeFileSync4(absolutePath, original, "utf-8");
7209
7544
  }
7210
7545
  } catch {
7211
7546
  }
@@ -7233,9 +7568,9 @@ async function rollbackInsight(insightId) {
7233
7568
  } catch {
7234
7569
  return { success: false, message: `Insight #${insightId} has malformed rollback data` };
7235
7570
  }
7236
- const absolutePath = join11(CC_CLAW_HOME, insight.targetFile);
7571
+ const absolutePath = join12(CC_CLAW_HOME, insight.targetFile);
7237
7572
  try {
7238
- writeFileSync3(absolutePath, rollback.original, "utf-8");
7573
+ writeFileSync4(absolutePath, rollback.original, "utf-8");
7239
7574
  updateInsightStatus(db3, insightId, "rolled_back");
7240
7575
  syncNativeCliFiles();
7241
7576
  const chatId = insight.chatId ?? "global";
@@ -7266,8 +7601,8 @@ function calculateDrift(chatId) {
7266
7601
  if (!row || !row.baselineSoulMd && !row.baselineUserMd) {
7267
7602
  return null;
7268
7603
  }
7269
- const soulPath = join11(CC_CLAW_HOME, "identity/SOUL.md");
7270
- const userPath = join11(CC_CLAW_HOME, "identity/USER.md");
7604
+ const soulPath = join12(CC_CLAW_HOME, "identity/SOUL.md");
7605
+ const userPath = join12(CC_CLAW_HOME, "identity/USER.md");
7271
7606
  const soulDrift = computeLineDrift(row.baselineSoulMd, soulPath);
7272
7607
  const userDrift = computeLineDrift(row.baselineUserMd, userPath);
7273
7608
  return { soulDrift, userDrift };
@@ -7295,7 +7630,7 @@ function computeLineDrift(baseline, absolutePath) {
7295
7630
  const changed = totalBaseline - unchanged;
7296
7631
  return changed / totalBaseline;
7297
7632
  }
7298
- var ALLOWED_TARGETS, ALLOWED_PREFIXES, SOUL_LINE_CAP, DAILY_APPLY_CAP;
7633
+ var ALLOWED_TARGETS, ALLOWED_PREFIXES, SOUL_LINE_CAP, MAX_BACKUPS_PER_FILE;
7299
7634
  var init_apply = __esm({
7300
7635
  "src/reflection/apply.ts"() {
7301
7636
  "use strict";
@@ -7314,7 +7649,7 @@ var init_apply = __esm({
7314
7649
  "workspace/skills/"
7315
7650
  ];
7316
7651
  SOUL_LINE_CAP = 100;
7317
- DAILY_APPLY_CAP = 3;
7652
+ MAX_BACKUPS_PER_FILE = 3;
7318
7653
  }
7319
7654
  });
7320
7655
 
@@ -7328,7 +7663,7 @@ __export(server_exports, {
7328
7663
  });
7329
7664
  import { createServer } from "http";
7330
7665
  import { randomBytes } from "crypto";
7331
- import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync13 } from "fs";
7666
+ import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync13 } from "fs";
7332
7667
  function createSubAgentToken(agentId) {
7333
7668
  const token = `sub:${agentId.slice(0, 8)}:${randomBytes(16).toString("hex")}`;
7334
7669
  subAgentTokens.set(token, agentId);
@@ -8097,11 +8432,11 @@ data: ${JSON.stringify(data)}
8097
8432
  const body = JSON.parse(await readBody(req));
8098
8433
  const { setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
8099
8434
  const { existsSync: fileExists, readFileSync: fileRead } = await import("fs");
8100
- const { join: join26 } = await import("path");
8435
+ const { join: join27 } = await import("path");
8101
8436
  const { CC_CLAW_HOME: home } = await Promise.resolve().then(() => (init_paths(), paths_exports));
8102
8437
  const chatId = body.chatId || (process.env.ALLOWED_CHAT_ID ?? "").split(",")[0]?.trim() || "default";
8103
- const soulPath = join26(home, "identity/SOUL.md");
8104
- const userPath = join26(home, "identity/USER.md");
8438
+ const soulPath = join27(home, "identity/SOUL.md");
8439
+ const userPath = join27(home, "identity/USER.md");
8105
8440
  const soul = fileExists(soulPath) ? fileRead(soulPath, "utf-8") : "";
8106
8441
  const user = fileExists(userPath) ? fileRead(userPath, "utf-8") : "";
8107
8442
  setReflectionStatus2(getDb(), chatId, "active", soul, user);
@@ -8132,6 +8467,21 @@ data: ${JSON.stringify(data)}
8132
8467
  return jsonResponse(res, { error: errorMessage(err) }, 400);
8133
8468
  }
8134
8469
  }
8470
+ if (url.pathname === "/api/evolve/settings" && req.method === "POST") {
8471
+ try {
8472
+ const body = JSON.parse(await readBody(req));
8473
+ const { setReflectionSettings: setReflectionSettings2, getReflectionSettings: getReflectionSettings2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
8474
+ const chatId = body.chatId || (process.env.ALLOWED_CHAT_ID ?? "").split(",")[0]?.trim() || "default";
8475
+ const updates = {};
8476
+ if (body.perFileCap !== void 0) updates.perFileCap = body.perFileCap;
8477
+ if (body.backupRetentionDays !== void 0) updates.backupRetentionDays = body.backupRetentionDays;
8478
+ setReflectionSettings2(getDb(), chatId, updates);
8479
+ const current = getReflectionSettings2(getDb(), chatId);
8480
+ return jsonResponse(res, { success: true, ...current });
8481
+ } catch (err) {
8482
+ return jsonResponse(res, { error: errorMessage(err) }, 400);
8483
+ }
8484
+ }
8135
8485
  if (url.pathname === "/" || url.pathname === "/index.html") {
8136
8486
  return htmlResponse(res, DASHBOARD_HTML.replace("__TOKEN__", DASHBOARD_TOKEN));
8137
8487
  }
@@ -8149,9 +8499,9 @@ data: ${JSON.stringify(data)}
8149
8499
  });
8150
8500
  server.listen(PORT, "127.0.0.1");
8151
8501
  try {
8152
- mkdirSync4(DATA_PATH, { recursive: true });
8502
+ mkdirSync5(DATA_PATH, { recursive: true });
8153
8503
  const tokenPath = `${DATA_PATH}/api-token`;
8154
- writeFileSync4(tokenPath, DASHBOARD_TOKEN, { mode: 384 });
8504
+ writeFileSync5(tokenPath, DASHBOARD_TOKEN, { mode: 384 });
8155
8505
  } catch (err) {
8156
8506
  log(`[api] Warning: could not write api-token file: ${errorMessage(err)}`);
8157
8507
  }
@@ -9362,11 +9712,13 @@ __export(propose_exports, {
9362
9712
  buildEvolveOnboardingKeyboard: () => buildEvolveOnboardingKeyboard,
9363
9713
  buildModelKeyboard: () => buildModelKeyboard,
9364
9714
  buildProposalKeyboard: () => buildProposalKeyboard,
9715
+ buildReviewCompleteMessage: () => buildReviewCompleteMessage,
9365
9716
  buildUndoKeyboard: () => buildUndoKeyboard,
9366
9717
  formatDiffCodeBlock: () => formatDiffCodeBlock,
9367
9718
  formatGrowthReport: () => formatGrowthReport,
9368
9719
  formatNightlySummary: () => formatNightlySummary,
9369
- formatProposalCard: () => formatProposalCard
9720
+ formatProposalCard: () => formatProposalCard,
9721
+ formatProposalCardWithProgress: () => formatProposalCardWithProgress
9370
9722
  });
9371
9723
  function pct(ratio) {
9372
9724
  return `${Math.round(ratio * 100)}%`;
@@ -9387,6 +9739,28 @@ function formatProposalCard(params) {
9387
9739
  }
9388
9740
  return lines.join("\n");
9389
9741
  }
9742
+ function formatProposalCardWithProgress(params, index, total) {
9743
+ const progress = `\u{1F4CB} Proposal ${index + 1} of ${total}`;
9744
+ const separator = "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500";
9745
+ const card = formatProposalCard(params);
9746
+ return `${progress}
9747
+ ${separator}
9748
+ ${card}`;
9749
+ }
9750
+ function buildReviewCompleteMessage(results) {
9751
+ const values = Object.values(results);
9752
+ const applied = values.filter((v) => v === "applied").length;
9753
+ const rejected = values.filter((v) => v === "rejected").length;
9754
+ const skipped = values.filter((v) => v === "skipped").length;
9755
+ const total = values.length;
9756
+ const parts = [];
9757
+ if (applied > 0) parts.push(`${applied} applied`);
9758
+ if (rejected > 0) parts.push(`${rejected} rejected`);
9759
+ if (skipped > 0) parts.push(`${skipped} skipped`);
9760
+ return `\u2705 Review complete \u2014 ${total} proposal${total === 1 ? "" : "s"}: ${parts.join(", ")}
9761
+
9762
+ Skipped proposals will appear in your next review.`;
9763
+ }
9390
9764
  function formatNightlySummary(insights) {
9391
9765
  const count = insights.length;
9392
9766
  const header2 = `Nightly Reflection \u2014 ${count} proposal${count === 1 ? "" : "s"} ready`;
@@ -9430,7 +9804,10 @@ function buildProposalKeyboard(insightId, category) {
9430
9804
  return [
9431
9805
  [
9432
9806
  { label: "Show Diff", data: `evolve:diff:${insightId}`, style: "primary" },
9433
- { label: "Discuss", data: `evolve:discuss:${insightId}` },
9807
+ { label: "Discuss", data: `evolve:discuss:${insightId}` }
9808
+ ],
9809
+ [
9810
+ { label: "Skip \u23ED", data: `evolve:skip:${insightId}` },
9434
9811
  { label: "Reject", data: `evolve:reject:${insightId}`, style: "danger" }
9435
9812
  ]
9436
9813
  ];
@@ -9438,7 +9815,10 @@ function buildProposalKeyboard(insightId, category) {
9438
9815
  return [
9439
9816
  [
9440
9817
  { label: "Apply", data: `evolve:apply:${insightId}`, style: "success" },
9441
- { label: "Discuss", data: `evolve:discuss:${insightId}` },
9818
+ { label: "Discuss", data: `evolve:discuss:${insightId}` }
9819
+ ],
9820
+ [
9821
+ { label: "Skip \u23ED", data: `evolve:skip:${insightId}` },
9442
9822
  { label: "Reject", data: `evolve:reject:${insightId}`, style: "danger" }
9443
9823
  ]
9444
9824
  ];
@@ -9814,7 +10194,7 @@ var init_cron = __esm({
9814
10194
  });
9815
10195
 
9816
10196
  // src/agents/runners/wrap-backend.ts
9817
- import { join as join12 } from "path";
10197
+ import { join as join13 } from "path";
9818
10198
  function buildMcpCommands(backendId) {
9819
10199
  const exe = backendId === "cursor" ? "agent" : backendId;
9820
10200
  return {
@@ -9908,7 +10288,7 @@ function wrapBackendAdapter(adapter) {
9908
10288
  const configPath = writeMcpConfigFile(server);
9909
10289
  return ["--mcp-config", configPath];
9910
10290
  },
9911
- getSkillPath: () => join12(SKILLS_PATH, `agent-${adapter.id}.md`)
10291
+ getSkillPath: () => join13(SKILLS_PATH, `agent-${adapter.id}.md`)
9912
10292
  };
9913
10293
  }
9914
10294
  var BACKEND_CAPABILITIES;
@@ -9959,8 +10339,8 @@ var init_wrap_backend = __esm({
9959
10339
  });
9960
10340
 
9961
10341
  // src/agents/runners/config-loader.ts
9962
- import { readFileSync as readFileSync8, readdirSync as readdirSync6, existsSync as existsSync14, mkdirSync as mkdirSync5, watchFile, unwatchFile } from "fs";
9963
- import { join as join13 } from "path";
10342
+ import { readFileSync as readFileSync8, readdirSync as readdirSync8, existsSync as existsSync14, mkdirSync as mkdirSync6, watchFile, unwatchFile } from "fs";
10343
+ import { join as join14 } from "path";
9964
10344
  import { execFileSync } from "child_process";
9965
10345
  function resolveExecutable(config2) {
9966
10346
  if (existsSync14(config2.executable)) return config2.executable;
@@ -10096,7 +10476,7 @@ function configToRunner(config2) {
10096
10476
  prepareMcpInjection() {
10097
10477
  return [];
10098
10478
  },
10099
- getSkillPath: () => join13(SKILLS_PATH, `agent-${config2.id}.md`)
10479
+ getSkillPath: () => join14(SKILLS_PATH, `agent-${config2.id}.md`)
10100
10480
  };
10101
10481
  }
10102
10482
  function loadRunnerConfig(filePath) {
@@ -10110,13 +10490,13 @@ function loadRunnerConfig(filePath) {
10110
10490
  }
10111
10491
  function loadAllRunnerConfigs() {
10112
10492
  if (!existsSync14(RUNNERS_PATH)) {
10113
- mkdirSync5(RUNNERS_PATH, { recursive: true });
10493
+ mkdirSync6(RUNNERS_PATH, { recursive: true });
10114
10494
  return [];
10115
10495
  }
10116
- const files = readdirSync6(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
10496
+ const files = readdirSync8(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
10117
10497
  const configs = [];
10118
10498
  for (const file of files) {
10119
- const config2 = loadRunnerConfig(join13(RUNNERS_PATH, file));
10499
+ const config2 = loadRunnerConfig(join14(RUNNERS_PATH, file));
10120
10500
  if (config2) configs.push(config2);
10121
10501
  }
10122
10502
  return configs;
@@ -10144,9 +10524,9 @@ function watchRunnerConfigs(onChange) {
10144
10524
  watchedFiles.delete(prev);
10145
10525
  }
10146
10526
  }
10147
- const files = readdirSync6(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
10527
+ const files = readdirSync8(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
10148
10528
  for (const file of files) {
10149
- const fullPath = join13(RUNNERS_PATH, file);
10529
+ const fullPath = join14(RUNNERS_PATH, file);
10150
10530
  if (watchedFiles.has(fullPath)) continue;
10151
10531
  watchedFiles.add(fullPath);
10152
10532
  watchFile(fullPath, { interval: 5e3 }, () => {
@@ -10854,7 +11234,7 @@ __export(discover_exports, {
10854
11234
  import { readdir, readFile as readFile2 } from "fs/promises";
10855
11235
  import { createHash } from "crypto";
10856
11236
  import { homedir as homedir5 } from "os";
10857
- import { join as join14 } from "path";
11237
+ import { join as join15 } from "path";
10858
11238
  function invalidateSkillCache() {
10859
11239
  cachedSkills = null;
10860
11240
  cacheTimestamp = 0;
@@ -10872,7 +11252,7 @@ async function discoverAllSkills() {
10872
11252
  const rawSkills = [];
10873
11253
  rawSkills.push(...await scanSkillDir(SKILLS_PATH, "cc-claw"));
10874
11254
  for (const backendId of getAllBackendIds()) {
10875
- const dirs = BACKEND_SKILL_DIRS[backendId] ?? [join14(homedir5(), `.${backendId}`, "skills")];
11255
+ const dirs = BACKEND_SKILL_DIRS[backendId] ?? [join15(homedir5(), `.${backendId}`, "skills")];
10876
11256
  for (const dir of dirs) {
10877
11257
  rawSkills.push(...await scanSkillDir(dir, backendId));
10878
11258
  }
@@ -10900,7 +11280,7 @@ async function scanSkillDir(skillsDir, source) {
10900
11280
  let content;
10901
11281
  let resolvedPath;
10902
11282
  for (const candidate of SKILL_FILE_CANDIDATES) {
10903
- const p = join14(skillsDir, entry.name, candidate);
11283
+ const p = join15(skillsDir, entry.name, candidate);
10904
11284
  try {
10905
11285
  content = await readFile2(p, "utf-8");
10906
11286
  resolvedPath = p;
@@ -11003,15 +11383,15 @@ var init_discover = __esm({
11003
11383
  init_backends();
11004
11384
  SKILL_FILE_CANDIDATES = ["SKILL.md", "skill.md"];
11005
11385
  BACKEND_SKILL_DIRS = {
11006
- claude: [join14(homedir5(), ".claude", "skills")],
11007
- gemini: [join14(homedir5(), ".gemini", "skills")],
11386
+ claude: [join15(homedir5(), ".claude", "skills")],
11387
+ gemini: [join15(homedir5(), ".gemini", "skills")],
11008
11388
  codex: [
11009
- join14(homedir5(), ".agents", "skills"),
11010
- join14(homedir5(), ".codex", "skills")
11389
+ join15(homedir5(), ".agents", "skills"),
11390
+ join15(homedir5(), ".codex", "skills")
11011
11391
  ],
11012
11392
  cursor: [
11013
- join14(homedir5(), ".cursor", "skills"),
11014
- join14(homedir5(), ".cursor", "skills-cursor")
11393
+ join15(homedir5(), ".cursor", "skills"),
11394
+ join15(homedir5(), ".cursor", "skills-cursor")
11015
11395
  ]
11016
11396
  };
11017
11397
  CACHE_TTL_MS2 = 3e5;
@@ -11028,7 +11408,7 @@ __export(install_exports, {
11028
11408
  });
11029
11409
  import { mkdir, readdir as readdir2, readFile as readFile3, cp } from "fs/promises";
11030
11410
  import { existsSync as existsSync15 } from "fs";
11031
- import { join as join15, basename } from "path";
11411
+ import { join as join16, basename } from "path";
11032
11412
  import { execSync as execSync6 } from "child_process";
11033
11413
  async function installSkillFromGitHub(urlOrShorthand) {
11034
11414
  let repoUrl;
@@ -11039,23 +11419,23 @@ async function installSkillFromGitHub(urlOrShorthand) {
11039
11419
  }
11040
11420
  repoUrl = parsed.cloneUrl;
11041
11421
  subPath = parsed.subPath;
11042
- const tmpDir = join15("/tmp", `cc-claw-skill-${Date.now()}`);
11422
+ const tmpDir = join16("/tmp", `cc-claw-skill-${Date.now()}`);
11043
11423
  try {
11044
11424
  log(`[skill-install] Cloning ${repoUrl} to ${tmpDir}`);
11045
11425
  execSync6(`git clone --depth 1 ${repoUrl} ${tmpDir}`, {
11046
11426
  stdio: "pipe",
11047
11427
  timeout: 3e4
11048
11428
  });
11049
- if (!existsSync15(join15(tmpDir, ".git"))) {
11429
+ if (!existsSync15(join16(tmpDir, ".git"))) {
11050
11430
  return { success: false, error: "Git clone failed: no .git directory produced" };
11051
11431
  }
11052
- const searchRoot = subPath ? join15(tmpDir, subPath) : tmpDir;
11432
+ const searchRoot = subPath ? join16(tmpDir, subPath) : tmpDir;
11053
11433
  const skillDir = await findSkillDir(searchRoot);
11054
11434
  if (!skillDir) {
11055
11435
  return { success: false, error: "No SKILL.md found in the repository." };
11056
11436
  }
11057
11437
  const skillFolderName = basename(skillDir);
11058
- const destDir = join15(SKILLS_PATH, skillFolderName);
11438
+ const destDir = join16(SKILLS_PATH, skillFolderName);
11059
11439
  if (existsSync15(destDir)) {
11060
11440
  log(`[skill-install] Overwriting existing skill at ${destDir}`);
11061
11441
  }
@@ -11063,12 +11443,12 @@ async function installSkillFromGitHub(urlOrShorthand) {
11063
11443
  await cp(skillDir, destDir, { recursive: true });
11064
11444
  let skillName = skillFolderName;
11065
11445
  try {
11066
- const content = await readFile3(join15(destDir, "SKILL.md"), "utf-8");
11446
+ const content = await readFile3(join16(destDir, "SKILL.md"), "utf-8");
11067
11447
  const nameMatch = content.match(/^name:\s*(.+)$/m);
11068
11448
  if (nameMatch) skillName = nameMatch[1].trim().replace(/^["']|["']$/g, "");
11069
11449
  } catch {
11070
11450
  try {
11071
- const content = await readFile3(join15(destDir, "skill.md"), "utf-8");
11451
+ const content = await readFile3(join16(destDir, "skill.md"), "utf-8");
11072
11452
  const nameMatch = content.match(/^name:\s*(.+)$/m);
11073
11453
  if (nameMatch) skillName = nameMatch[1].trim().replace(/^["']|["']$/g, "");
11074
11454
  } catch {
@@ -11103,15 +11483,15 @@ function parseGitHubUrl(input) {
11103
11483
  async function findSkillDir(root) {
11104
11484
  const candidates = ["SKILL.md", "skill.md"];
11105
11485
  for (const c of candidates) {
11106
- if (existsSync15(join15(root, c))) return root;
11486
+ if (existsSync15(join16(root, c))) return root;
11107
11487
  }
11108
11488
  try {
11109
11489
  const entries = await readdir2(root, { withFileTypes: true });
11110
11490
  for (const entry of entries) {
11111
11491
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
11112
11492
  for (const c of candidates) {
11113
- if (existsSync15(join15(root, entry.name, c))) {
11114
- return join15(root, entry.name);
11493
+ if (existsSync15(join16(root, entry.name, c))) {
11494
+ return join16(root, entry.name);
11115
11495
  }
11116
11496
  }
11117
11497
  }
@@ -11123,15 +11503,15 @@ async function findSkillDir(root) {
11123
11503
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
11124
11504
  let subEntries;
11125
11505
  try {
11126
- subEntries = await readdir2(join15(root, entry.name), { withFileTypes: true });
11506
+ subEntries = await readdir2(join16(root, entry.name), { withFileTypes: true });
11127
11507
  } catch {
11128
11508
  continue;
11129
11509
  }
11130
11510
  for (const sub of subEntries) {
11131
11511
  if (!sub.isDirectory() || sub.name.startsWith(".")) continue;
11132
11512
  for (const c of candidates) {
11133
- if (existsSync15(join15(root, entry.name, sub.name, c))) {
11134
- return join15(root, entry.name, sub.name);
11513
+ if (existsSync15(join16(root, entry.name, sub.name, c))) {
11514
+ return join16(root, entry.name, sub.name);
11135
11515
  }
11136
11516
  }
11137
11517
  }
@@ -11149,8 +11529,8 @@ var init_install = __esm({
11149
11529
  });
11150
11530
 
11151
11531
  // src/bootstrap/profile.ts
11152
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, existsSync as existsSync16 } from "fs";
11153
- import { join as join16 } from "path";
11532
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, existsSync as existsSync16 } from "fs";
11533
+ import { join as join17 } from "path";
11154
11534
  function hasActiveProfile(chatId) {
11155
11535
  return activeProfiles.has(chatId);
11156
11536
  }
@@ -11253,7 +11633,7 @@ async function finalizeProfile(chatId, state, channel) {
11253
11633
  "<!-- Add any additional preferences below this line -->",
11254
11634
  ""
11255
11635
  ].join("\n");
11256
- writeFileSync5(USER_PATH2, content, "utf-8");
11636
+ writeFileSync6(USER_PATH2, content, "utf-8");
11257
11637
  activeProfiles.delete(chatId);
11258
11638
  log(`[profile] User profile saved for chat ${chatId}`);
11259
11639
  await channel.sendText(
@@ -11286,7 +11666,7 @@ function appendToUserProfile(key, value) {
11286
11666
  const updated = content.trimEnd() + `
11287
11667
  ${line}
11288
11668
  `;
11289
- writeFileSync5(USER_PATH2, updated, "utf-8");
11669
+ writeFileSync6(USER_PATH2, updated, "utf-8");
11290
11670
  log(`[profile] Appended preference: ${key}=${value}`);
11291
11671
  }
11292
11672
  var USER_PATH2, activeProfiles;
@@ -11295,14 +11675,14 @@ var init_profile = __esm({
11295
11675
  "use strict";
11296
11676
  init_paths();
11297
11677
  init_log();
11298
- USER_PATH2 = join16(IDENTITY_PATH, "USER.md");
11678
+ USER_PATH2 = join17(IDENTITY_PATH, "USER.md");
11299
11679
  activeProfiles = /* @__PURE__ */ new Map();
11300
11680
  }
11301
11681
  });
11302
11682
 
11303
11683
  // src/bootstrap/heartbeat.ts
11304
11684
  import { readFileSync as readFileSync10, existsSync as existsSync17 } from "fs";
11305
- import { join as join17 } from "path";
11685
+ import { join as join18 } from "path";
11306
11686
  function initHeartbeat(channelReg) {
11307
11687
  registry2 = channelReg;
11308
11688
  }
@@ -11488,7 +11868,7 @@ var init_heartbeat = __esm({
11488
11868
  init_backends();
11489
11869
  init_health2();
11490
11870
  init_log();
11491
- HEARTBEAT_MD_PATH = join17(WORKSPACE_PATH, "HEARTBEAT.md");
11871
+ HEARTBEAT_MD_PATH = join18(WORKSPACE_PATH, "HEARTBEAT.md");
11492
11872
  HEARTBEAT_OK = "HEARTBEAT_OK";
11493
11873
  registry2 = null;
11494
11874
  activeTimers2 = /* @__PURE__ */ new Map();
@@ -11671,9 +12051,9 @@ var init_classify = __esm({
11671
12051
  });
11672
12052
 
11673
12053
  // src/media/image-gen.ts
11674
- import { mkdirSync as mkdirSync6, existsSync as existsSync18 } from "fs";
12054
+ import { mkdirSync as mkdirSync7, existsSync as existsSync18 } from "fs";
11675
12055
  import { writeFile as writeFile2 } from "fs/promises";
11676
- import { join as join18 } from "path";
12056
+ import { join as join19 } from "path";
11677
12057
  async function generateImage(prompt) {
11678
12058
  const apiKey = process.env.GEMINI_API_KEY;
11679
12059
  if (!apiKey) {
@@ -11721,11 +12101,11 @@ async function generateImage(prompt) {
11721
12101
  throw new Error(textResponse ?? "Gemini did not generate an image. The prompt may have been filtered.");
11722
12102
  }
11723
12103
  if (!existsSync18(IMAGE_OUTPUT_DIR)) {
11724
- mkdirSync6(IMAGE_OUTPUT_DIR, { recursive: true });
12104
+ mkdirSync7(IMAGE_OUTPUT_DIR, { recursive: true });
11725
12105
  }
11726
12106
  const ext = mimeType.includes("jpeg") || mimeType.includes("jpg") ? "jpg" : "png";
11727
12107
  const filename = `img_${Date.now()}.${ext}`;
11728
- const filePath = join18(IMAGE_OUTPUT_DIR, filename);
12108
+ const filePath = join19(IMAGE_OUTPUT_DIR, filename);
11729
12109
  const buffer = Buffer.from(imageData, "base64");
11730
12110
  await writeFile2(filePath, buffer);
11731
12111
  log(`[image-gen] Saved ${buffer.length} bytes to ${filePath}`);
@@ -11740,8 +12120,8 @@ var init_image_gen = __esm({
11740
12120
  "use strict";
11741
12121
  init_log();
11742
12122
  IMAGE_MODEL = "gemini-3.1-flash-image-preview";
11743
- IMAGE_OUTPUT_DIR = join18(
11744
- process.env.CC_CLAW_HOME ?? join18(process.env.HOME ?? "/tmp", ".cc-claw"),
12123
+ IMAGE_OUTPUT_DIR = join19(
12124
+ process.env.CC_CLAW_HOME ?? join19(process.env.HOME ?? "/tmp", ".cc-claw"),
11745
12125
  "data",
11746
12126
  "images"
11747
12127
  );
@@ -12980,7 +13360,7 @@ var init_pagination = __esm({
12980
13360
  import { readFile as readFile5, writeFile as writeFile3, unlink as unlink2, mkdir as mkdir2, readdir as readdir3, stat } from "fs/promises";
12981
13361
  import { existsSync as existsSync19 } from "fs";
12982
13362
  import { randomUUID as randomUUID3 } from "crypto";
12983
- import { resolve as resolvePath, join as join19 } from "path";
13363
+ import { resolve as resolvePath, join as join20 } from "path";
12984
13364
  import { homedir as homedir6 } from "os";
12985
13365
  function parseMcpListOutput(output2) {
12986
13366
  const results = [];
@@ -13223,7 +13603,7 @@ function getMediaRetentionMs() {
13223
13603
  async function saveMedia(buffer, prefix, ext) {
13224
13604
  await mkdir2(MEDIA_INCOMING_PATH, { recursive: true });
13225
13605
  const filename = `${prefix}-${Date.now()}.${ext}`;
13226
- const fullPath = join19(MEDIA_INCOMING_PATH, filename);
13606
+ const fullPath = join20(MEDIA_INCOMING_PATH, filename);
13227
13607
  await writeFile3(fullPath, buffer);
13228
13608
  return fullPath;
13229
13609
  }
@@ -13237,7 +13617,7 @@ async function cleanupOldMedia() {
13237
13617
  let removed = 0;
13238
13618
  for (const file of files) {
13239
13619
  try {
13240
- const filePath = join19(MEDIA_INCOMING_PATH, file);
13620
+ const filePath = join20(MEDIA_INCOMING_PATH, file);
13241
13621
  const s = await stat(filePath);
13242
13622
  if (now - s.mtimeMs > retentionMs) {
13243
13623
  await unlink2(filePath);
@@ -13288,6 +13668,28 @@ function stopAllSideQuests(chatId) {
13288
13668
  }
13289
13669
  }
13290
13670
  }
13671
+ async function sendCurrentProposal(chatId, channel) {
13672
+ const { getReviewSession: getReviewSession2, getInsightById: getInsightById2, deleteReviewSession: deleteReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
13673
+ const { formatProposalCardWithProgress: formatProposalCardWithProgress2, buildProposalKeyboard: buildProposalKeyboard2, buildReviewCompleteMessage: buildReviewCompleteMessage2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
13674
+ const session2 = getReviewSession2(getDb(), chatId);
13675
+ if (!session2) return;
13676
+ if (session2.currentIndex >= session2.insightIds.length) {
13677
+ const summary = buildReviewCompleteMessage2(session2.results);
13678
+ deleteReviewSession2(getDb(), chatId);
13679
+ await channel.sendText(chatId, summary, { parseMode: "plain" });
13680
+ return;
13681
+ }
13682
+ const insightId = session2.insightIds[session2.currentIndex];
13683
+ const insight = getInsightById2(getDb(), insightId);
13684
+ if (!insight || insight.status !== "pending") {
13685
+ const { advanceReviewSession: advanceReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
13686
+ advanceReviewSession2(getDb(), chatId, insightId, "skipped");
13687
+ return sendCurrentProposal(chatId, channel);
13688
+ }
13689
+ const card = formatProposalCardWithProgress2(insight, session2.currentIndex, session2.insightIds.length);
13690
+ const kb = buildProposalKeyboard2(insight.id, insight.category);
13691
+ await channel.sendKeyboard(chatId, card, kb);
13692
+ }
13291
13693
  async function handleResponseExhaustion(responseText, chatId, msg, channel) {
13292
13694
  const raw = responseText.replace(/\n\n🧠 \[.+$/, "").trim();
13293
13695
  if (raw.length > 300 || !isExhaustedMessage(raw)) return false;
@@ -14915,22 +15317,16 @@ Message: "${testMsg}"`, { parseMode: "plain" });
14915
15317
  await channel.sendText(chatId, "Step 2/3: Analyzing for insights...", { parseMode: "plain" });
14916
15318
  try {
14917
15319
  const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_analyze(), analyze_exports));
14918
- const { formatProposalCard: formatProposalCard2, buildProposalKeyboard: buildProposalKeyboard2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
14919
15320
  const insights = await runAnalysis2(chatId, { force: true });
14920
15321
  if (insights.length === 0) {
14921
15322
  await channel.sendText(chatId, "Step 3/3: No actionable improvements found in this session.", { parseMode: "plain" });
14922
15323
  } else {
14923
- await channel.sendText(chatId, `Step 3/3: Found ${insights.length} proposal(s).`, { parseMode: "plain" });
14924
- const { getPendingInsights: getPendingInsights2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
15324
+ await channel.sendText(chatId, `Step 3/3: Found ${insights.length} proposal(s). Let's review them one by one.`, { parseMode: "plain" });
15325
+ const { getPendingInsights: getPendingInsights2, createReviewSession: createReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
14925
15326
  const pending = getPendingInsights2(getDb(), chatId);
14926
- for (const insight of pending.slice(0, 5)) {
14927
- const card = formatProposalCard2(insight);
14928
- const kb = buildProposalKeyboard2(insight.id, insight.category);
14929
- await channel.sendKeyboard(chatId, card, kb);
14930
- }
14931
- if (insights.length > 5) {
14932
- await channel.sendText(chatId, `${insights.length - 5} more proposals. Use /evolve to review all.`, { parseMode: "plain" });
14933
- }
15327
+ const insightIds = pending.slice(0, 5).map((p) => p.id);
15328
+ createReviewSession2(getDb(), chatId, insightIds);
15329
+ await sendCurrentProposal(chatId, channel);
14934
15330
  }
14935
15331
  } catch (e) {
14936
15332
  await channel.sendText(chatId, `Analysis failed: ${e}. You can review any pending proposals with /evolve.`, { parseMode: "plain" });
@@ -16677,21 +17073,16 @@ Result: ${task.result.slice(0, 500)}` : ""
16677
17073
  break;
16678
17074
  }
16679
17075
  case "review": {
16680
- const { getPendingInsights: getPendingInsights2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
16681
- const { formatProposalCard: formatProposalCard2, buildProposalKeyboard: buildProposalKeyboard2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
17076
+ const { getPendingInsights: getPendingInsights2, createReviewSession: createReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
16682
17077
  const { getDb: getDb2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
16683
17078
  const pending = getPendingInsights2(getDb2(), chatId);
16684
17079
  if (pending.length === 0) {
16685
17080
  await channel.sendText(chatId, "No pending proposals.", { parseMode: "plain" });
16686
17081
  } else {
16687
- for (const insight of pending.slice(0, 5)) {
16688
- const card = formatProposalCard2(insight);
16689
- const kb = buildProposalKeyboard2(insight.id, insight.category);
16690
- await channel.sendKeyboard(chatId, card, kb);
16691
- }
16692
- if (pending.length > 5) {
16693
- await channel.sendText(chatId, `${pending.length - 5} more proposals. Run /evolve again to see next batch.`, { parseMode: "plain" });
16694
- }
17082
+ const insightIds = pending.slice(0, 5).map((p) => p.id);
17083
+ createReviewSession2(getDb2(), chatId, insightIds);
17084
+ await channel.sendText(chatId, `${pending.length} proposal(s) ready. Let's review them one by one.`, { parseMode: "plain" });
17085
+ await sendCurrentProposal(chatId, channel);
16695
17086
  }
16696
17087
  break;
16697
17088
  }
@@ -16709,10 +17100,16 @@ Result: ${task.result.slice(0, 500)}` : ""
16709
17100
  const { applyInsight: applyInsight2 } = await Promise.resolve().then(() => (init_apply(), apply_exports));
16710
17101
  const result = await applyInsight2(parseInt(idStr, 10));
16711
17102
  await channel.sendText(chatId, result.message, { parseMode: "plain" });
17103
+ const { advanceReviewSession: arAdvance } = await Promise.resolve().then(() => (init_store4(), store_exports4));
17104
+ arAdvance(getDb(), chatId, parseInt(idStr, 10), "applied");
17105
+ await sendCurrentProposal(chatId, channel);
16712
17106
  break;
16713
17107
  }
16714
17108
  case "skip": {
16715
17109
  await channel.sendText(chatId, "Skipped \u2014 will show again next review.", { parseMode: "plain" });
17110
+ const { advanceReviewSession: skAdvance } = await Promise.resolve().then(() => (init_store4(), store_exports4));
17111
+ skAdvance(getDb(), chatId, parseInt(idStr, 10), "skipped");
17112
+ await sendCurrentProposal(chatId, channel);
16716
17113
  break;
16717
17114
  }
16718
17115
  case "discuss": {
@@ -16781,14 +17178,14 @@ Result: ${task.result.slice(0, 500)}` : ""
16781
17178
  { label: "USER.md", path: "identity/USER.md" }
16782
17179
  ];
16783
17180
  const skillDirs = [
16784
- join19(homedir6(), ".cc-claw", "workspace", "skills")
17181
+ join20(homedir6(), ".cc-claw", "workspace", "skills")
16785
17182
  ];
16786
17183
  try {
16787
- const { readdirSync: readdirSync7, statSync: statSync9 } = await import("fs");
17184
+ const { readdirSync: readdirSync9, statSync: statSync10 } = await import("fs");
16788
17185
  for (const dir of skillDirs) {
16789
17186
  if (!existsSync19(dir)) continue;
16790
- for (const entry of readdirSync7(dir)) {
16791
- if (statSync9(join19(dir, entry)).isDirectory()) {
17187
+ for (const entry of readdirSync9(dir)) {
17188
+ if (statSync10(join20(dir, entry)).isDirectory()) {
16792
17189
  targets.push({ label: `skills/${entry}`, path: `workspace/skills/${entry}/SKILL.md` });
16793
17190
  }
16794
17191
  }
@@ -16862,14 +17259,20 @@ Pick a different file for this change. Identity files (SOUL/USER) shape personal
16862
17259
  break;
16863
17260
  }
16864
17261
  case "discuss-back": {
16865
- const { getInsightById: bkIns } = await Promise.resolve().then(() => (init_store4(), store_exports4));
16866
- const { formatProposalCard: bkCard, buildProposalKeyboard: bkKb } = await Promise.resolve().then(() => (init_propose(), propose_exports));
17262
+ const { getInsightById: bkIns, getReviewSession: bkSession } = await Promise.resolve().then(() => (init_store4(), store_exports4));
17263
+ const { formatProposalCardWithProgress: formatProposalCardWithProgress2, formatProposalCard: bkCardFn, buildProposalKeyboard: bkKb } = await Promise.resolve().then(() => (init_propose(), propose_exports));
16867
17264
  const { getDb: bkDb } = await Promise.resolve().then(() => (init_store5(), store_exports5));
16868
17265
  const bkInsight = bkIns(bkDb(), parseInt(idStr, 10));
16869
17266
  if (bkInsight && bkInsight.status === "pending") {
16870
- const card = bkCard(bkInsight);
17267
+ const session2 = bkSession(bkDb(), chatId);
16871
17268
  const kb = bkKb(bkInsight.id, bkInsight.category);
16872
- await channel.sendKeyboard(chatId, card, kb);
17269
+ if (session2) {
17270
+ const card = formatProposalCardWithProgress2(bkInsight, session2.currentIndex, session2.insightIds.length);
17271
+ await channel.sendKeyboard(chatId, card, kb);
17272
+ } else {
17273
+ const card = bkCardFn(bkInsight);
17274
+ await channel.sendKeyboard(chatId, card, kb);
17275
+ }
16873
17276
  }
16874
17277
  break;
16875
17278
  }
@@ -16878,6 +17281,9 @@ Pick a different file for this change. Identity files (SOUL/USER) shape personal
16878
17281
  const { getDb: getDb2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
16879
17282
  updateInsightStatus2(getDb2(), parseInt(idStr, 10), "rejected");
16880
17283
  await channel.sendText(chatId, "Rejected. Won't propose similar changes.", { parseMode: "plain" });
17284
+ const { advanceReviewSession: rjAdvance } = await Promise.resolve().then(() => (init_store4(), store_exports4));
17285
+ rjAdvance(getDb2(), chatId, parseInt(idStr, 10), "rejected");
17286
+ await sendCurrentProposal(chatId, channel);
16881
17287
  break;
16882
17288
  }
16883
17289
  case "stats": {
@@ -16932,10 +17338,10 @@ Pick a different file for this change. Identity files (SOUL/USER) shape personal
16932
17338
  const current = getReflectionStatus2(getDb2(), chatId);
16933
17339
  if (current === "frozen") {
16934
17340
  const { readFileSync: readFileSync21, existsSync: existsSync47 } = await import("fs");
16935
- const { join: join26 } = await import("path");
17341
+ const { join: join27 } = await import("path");
16936
17342
  const { CC_CLAW_HOME: CC_CLAW_HOME3 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
16937
- const soulPath = join26(CC_CLAW_HOME3, "identity/SOUL.md");
16938
- const userPath = join26(CC_CLAW_HOME3, "identity/USER.md");
17343
+ const soulPath = join27(CC_CLAW_HOME3, "identity/SOUL.md");
17344
+ const userPath = join27(CC_CLAW_HOME3, "identity/USER.md");
16939
17345
  const soul = existsSync47(soulPath) ? readFileSync21(soulPath, "utf-8") : "";
16940
17346
  const user = existsSync47(userPath) ? readFileSync21(userPath, "utf-8") : "";
16941
17347
  setReflectionStatus2(getDb2(), chatId, "active", soul, user);
@@ -17638,7 +18044,7 @@ var init_router = __esm({
17638
18044
  ]
17639
18045
  };
17640
18046
  USAGE_WINDOW_MAP = { "24h": "daily", "7d": "weekly" };
17641
- MEDIA_INCOMING_PATH = join19(MEDIA_PATH, "incoming");
18047
+ MEDIA_INCOMING_PATH = join20(MEDIA_PATH, "incoming");
17642
18048
  TONE_PATTERNS = [
17643
18049
  // Humor / laughter
17644
18050
  { pattern: /\b(lol|lmao|rofl|haha|hehe|😂|🤣|funny|hilarious|joke)\b/i, emoji: "\u{1F923}" },
@@ -17775,7 +18181,7 @@ var init_router = __esm({
17775
18181
  // src/skills/bootstrap.ts
17776
18182
  import { existsSync as existsSync20 } from "fs";
17777
18183
  import { readdir as readdir4, readFile as readFile6, writeFile as writeFile4, copyFile } from "fs/promises";
17778
- import { join as join20, dirname as dirname3 } from "path";
18184
+ import { join as join21, dirname as dirname3 } from "path";
17779
18185
  import { fileURLToPath as fileURLToPath2 } from "url";
17780
18186
  async function copyAgentManifestSkills() {
17781
18187
  if (!existsSync20(PKG_SKILLS)) return;
@@ -17783,8 +18189,8 @@ async function copyAgentManifestSkills() {
17783
18189
  const entries = await readdir4(PKG_SKILLS, { withFileTypes: true });
17784
18190
  for (const entry of entries) {
17785
18191
  if (!entry.isFile() || !entry.name.startsWith("agent-") || !entry.name.endsWith(".md")) continue;
17786
- const src = join20(PKG_SKILLS, entry.name);
17787
- const dest = join20(SKILLS_PATH, entry.name);
18192
+ const src = join21(PKG_SKILLS, entry.name);
18193
+ const dest = join21(SKILLS_PATH, entry.name);
17788
18194
  if (existsSync20(dest)) continue;
17789
18195
  await copyFile(src, dest);
17790
18196
  log(`[skills] Bootstrapped ${entry.name} to ${SKILLS_PATH}`);
@@ -17795,7 +18201,7 @@ async function copyAgentManifestSkills() {
17795
18201
  }
17796
18202
  async function bootstrapSkills() {
17797
18203
  await copyAgentManifestSkills();
17798
- const usmDir = join20(SKILLS_PATH, USM_DIR_NAME);
18204
+ const usmDir = join21(SKILLS_PATH, USM_DIR_NAME);
17799
18205
  if (existsSync20(usmDir)) return;
17800
18206
  try {
17801
18207
  const entries = await readdir4(SKILLS_PATH);
@@ -17818,7 +18224,7 @@ async function bootstrapSkills() {
17818
18224
  }
17819
18225
  }
17820
18226
  async function patchUsmForCcClaw(usmDir) {
17821
- const skillPath = join20(usmDir, "SKILL.md");
18227
+ const skillPath = join21(usmDir, "SKILL.md");
17822
18228
  if (!existsSync20(skillPath)) return;
17823
18229
  try {
17824
18230
  let content = await readFile6(skillPath, "utf-8");
@@ -17864,8 +18270,8 @@ var init_bootstrap = __esm({
17864
18270
  USM_REPO = "jacob-bd/universal-skills-manager";
17865
18271
  USM_DIR_NAME = "universal-skills-manager";
17866
18272
  CC_CLAW_ECOSYSTEM_PATCH = `| **CC-Claw** | \`~/.cc-claw/workspace/skills/\` | N/A (daemon, no project scope) |`;
17867
- PKG_ROOT = join20(dirname3(fileURLToPath2(import.meta.url)), "..", "..");
17868
- PKG_SKILLS = join20(PKG_ROOT, "skills");
18273
+ PKG_ROOT = join21(dirname3(fileURLToPath2(import.meta.url)), "..", "..");
18274
+ PKG_SKILLS = join21(PKG_ROOT, "skills");
17869
18275
  }
17870
18276
  });
17871
18277
 
@@ -18079,8 +18485,8 @@ __export(ai_skill_exports, {
18079
18485
  generateAiSkill: () => generateAiSkill,
18080
18486
  installAiSkill: () => installAiSkill
18081
18487
  });
18082
- import { existsSync as existsSync21, writeFileSync as writeFileSync6, mkdirSync as mkdirSync7 } from "fs";
18083
- import { join as join21 } from "path";
18488
+ import { existsSync as existsSync21, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8 } from "fs";
18489
+ import { join as join22 } from "path";
18084
18490
  import { homedir as homedir7 } from "os";
18085
18491
  function generateAiSkill() {
18086
18492
  const version = VERSION;
@@ -18481,11 +18887,11 @@ function installAiSkill() {
18481
18887
  const failed = [];
18482
18888
  for (const [backend2, dirs] of Object.entries(BACKEND_SKILL_DIRS2)) {
18483
18889
  for (const dir of dirs) {
18484
- const skillDir = join21(dir, "cc-claw-cli");
18485
- const skillPath = join21(skillDir, "SKILL.md");
18890
+ const skillDir = join22(dir, "cc-claw-cli");
18891
+ const skillPath = join22(skillDir, "SKILL.md");
18486
18892
  try {
18487
- mkdirSync7(skillDir, { recursive: true });
18488
- writeFileSync6(skillPath, skill, "utf-8");
18893
+ mkdirSync8(skillDir, { recursive: true });
18894
+ writeFileSync7(skillPath, skill, "utf-8");
18489
18895
  installed.push(skillPath);
18490
18896
  } catch {
18491
18897
  failed.push(skillPath);
@@ -18501,11 +18907,11 @@ var init_ai_skill = __esm({
18501
18907
  init_paths();
18502
18908
  init_version();
18503
18909
  BACKEND_SKILL_DIRS2 = {
18504
- "cc-claw": [join21(homedir7(), ".cc-claw", "workspace", "skills")],
18505
- claude: [join21(homedir7(), ".claude", "skills")],
18506
- gemini: [join21(homedir7(), ".gemini", "skills")],
18507
- codex: [join21(homedir7(), ".agents", "skills")],
18508
- cursor: [join21(homedir7(), ".cursor", "skills"), join21(homedir7(), ".cursor", "skills-cursor")]
18910
+ "cc-claw": [join22(homedir7(), ".cc-claw", "workspace", "skills")],
18911
+ claude: [join22(homedir7(), ".claude", "skills")],
18912
+ gemini: [join22(homedir7(), ".gemini", "skills")],
18913
+ codex: [join22(homedir7(), ".agents", "skills")],
18914
+ cursor: [join22(homedir7(), ".cursor", "skills"), join22(homedir7(), ".cursor", "skills-cursor")]
18509
18915
  };
18510
18916
  }
18511
18917
  });
@@ -18515,18 +18921,18 @@ var index_exports = {};
18515
18921
  __export(index_exports, {
18516
18922
  main: () => main
18517
18923
  });
18518
- import { mkdirSync as mkdirSync8, existsSync as existsSync22, renameSync, statSync as statSync4, readFileSync as readFileSync12 } from "fs";
18519
- import { join as join22 } from "path";
18924
+ import { mkdirSync as mkdirSync9, existsSync as existsSync22, renameSync, statSync as statSync5, readFileSync as readFileSync12 } from "fs";
18925
+ import { join as join23 } from "path";
18520
18926
  import dotenv from "dotenv";
18521
18927
  function migrateLayout() {
18522
18928
  const moves = [
18523
- [join22(CC_CLAW_HOME, "cc-claw.db"), join22(DATA_PATH, "cc-claw.db")],
18524
- [join22(CC_CLAW_HOME, "cc-claw.db-shm"), join22(DATA_PATH, "cc-claw.db-shm")],
18525
- [join22(CC_CLAW_HOME, "cc-claw.db-wal"), join22(DATA_PATH, "cc-claw.db-wal")],
18526
- [join22(CC_CLAW_HOME, "cc-claw.log"), join22(LOGS_PATH, "cc-claw.log")],
18527
- [join22(CC_CLAW_HOME, "cc-claw.log.1"), join22(LOGS_PATH, "cc-claw.log.1")],
18528
- [join22(CC_CLAW_HOME, "cc-claw.error.log"), join22(LOGS_PATH, "cc-claw.error.log")],
18529
- [join22(CC_CLAW_HOME, "cc-claw.error.log.1"), join22(LOGS_PATH, "cc-claw.error.log.1")]
18929
+ [join23(CC_CLAW_HOME, "cc-claw.db"), join23(DATA_PATH, "cc-claw.db")],
18930
+ [join23(CC_CLAW_HOME, "cc-claw.db-shm"), join23(DATA_PATH, "cc-claw.db-shm")],
18931
+ [join23(CC_CLAW_HOME, "cc-claw.db-wal"), join23(DATA_PATH, "cc-claw.db-wal")],
18932
+ [join23(CC_CLAW_HOME, "cc-claw.log"), join23(LOGS_PATH, "cc-claw.log")],
18933
+ [join23(CC_CLAW_HOME, "cc-claw.log.1"), join23(LOGS_PATH, "cc-claw.log.1")],
18934
+ [join23(CC_CLAW_HOME, "cc-claw.error.log"), join23(LOGS_PATH, "cc-claw.error.log")],
18935
+ [join23(CC_CLAW_HOME, "cc-claw.error.log.1"), join23(LOGS_PATH, "cc-claw.error.log.1")]
18530
18936
  ];
18531
18937
  for (const [from, to] of moves) {
18532
18938
  if (existsSync22(from) && !existsSync22(to)) {
@@ -18540,7 +18946,7 @@ function migrateLayout() {
18540
18946
  function rotateLogs() {
18541
18947
  for (const file of [LOG_PATH, ERROR_LOG_PATH]) {
18542
18948
  try {
18543
- const { size } = statSync4(file);
18949
+ const { size } = statSync5(file);
18544
18950
  if (size > LOG_MAX_BYTES) {
18545
18951
  const archivePath = `${file}.1`;
18546
18952
  try {
@@ -18663,11 +19069,11 @@ async function main() {
18663
19069
  bootstrapSkills().catch((err) => error("[cc-claw] Skill bootstrap failed:", err));
18664
19070
  try {
18665
19071
  const { generateAiSkill: generateAiSkill2 } = await Promise.resolve().then(() => (init_ai_skill(), ai_skill_exports));
18666
- const { writeFileSync: writeFileSync10, mkdirSync: mkdirSync13 } = await import("fs");
18667
- const { join: join26 } = await import("path");
18668
- const skillDir = join26(SKILLS_PATH, "cc-claw-cli");
18669
- mkdirSync13(skillDir, { recursive: true });
18670
- writeFileSync10(join26(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
19072
+ const { writeFileSync: writeFileSync11, mkdirSync: mkdirSync14 } = await import("fs");
19073
+ const { join: join27 } = await import("path");
19074
+ const skillDir = join27(SKILLS_PATH, "cc-claw-cli");
19075
+ mkdirSync14(skillDir, { recursive: true });
19076
+ writeFileSync11(join27(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
18671
19077
  log("[cc-claw] AI skill updated");
18672
19078
  } catch {
18673
19079
  }
@@ -18738,7 +19144,7 @@ var init_index = __esm({
18738
19144
  init_bootstrap2();
18739
19145
  init_health3();
18740
19146
  for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
18741
- if (!existsSync22(dir)) mkdirSync8(dir, { recursive: true });
19147
+ if (!existsSync22(dir)) mkdirSync9(dir, { recursive: true });
18742
19148
  }
18743
19149
  migrateLayout();
18744
19150
  if (existsSync22(ENV_PATH)) {
@@ -18862,10 +19268,10 @@ __export(service_exports, {
18862
19268
  serviceStatus: () => serviceStatus,
18863
19269
  uninstallService: () => uninstallService
18864
19270
  });
18865
- import { existsSync as existsSync24, mkdirSync as mkdirSync9, writeFileSync as writeFileSync7, unlinkSync as unlinkSync4 } from "fs";
19271
+ import { existsSync as existsSync24, mkdirSync as mkdirSync10, writeFileSync as writeFileSync8, unlinkSync as unlinkSync6 } from "fs";
18866
19272
  import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
18867
19273
  import { homedir as homedir8, platform } from "os";
18868
- import { join as join23, dirname as dirname4 } from "path";
19274
+ import { join as join24, dirname as dirname4 } from "path";
18869
19275
  function resolveExecutable2(name) {
18870
19276
  try {
18871
19277
  return execFileSync2("which", [name], { encoding: "utf-8" }).trim();
@@ -18878,14 +19284,14 @@ function getPathDirs() {
18878
19284
  const home = homedir8();
18879
19285
  const dirs = /* @__PURE__ */ new Set([
18880
19286
  nodeBin,
18881
- join23(home, ".local", "bin"),
19287
+ join24(home, ".local", "bin"),
18882
19288
  "/usr/local/bin",
18883
19289
  "/usr/bin",
18884
19290
  "/bin"
18885
19291
  ]);
18886
19292
  try {
18887
19293
  const prefix = execSync7("npm config get prefix", { encoding: "utf-8" }).trim();
18888
- if (prefix) dirs.add(join23(prefix, "bin"));
19294
+ if (prefix) dirs.add(join24(prefix, "bin"));
18889
19295
  } catch {
18890
19296
  }
18891
19297
  return [...dirs].join(":");
@@ -18937,15 +19343,15 @@ function generatePlist() {
18937
19343
  }
18938
19344
  function installMacOS() {
18939
19345
  const agentsDir = dirname4(PLIST_PATH);
18940
- if (!existsSync24(agentsDir)) mkdirSync9(agentsDir, { recursive: true });
18941
- if (!existsSync24(LOGS_PATH)) mkdirSync9(LOGS_PATH, { recursive: true });
19346
+ if (!existsSync24(agentsDir)) mkdirSync10(agentsDir, { recursive: true });
19347
+ if (!existsSync24(LOGS_PATH)) mkdirSync10(LOGS_PATH, { recursive: true });
18942
19348
  if (existsSync24(PLIST_PATH)) {
18943
19349
  try {
18944
19350
  execFileSync2("launchctl", ["unload", PLIST_PATH]);
18945
19351
  } catch {
18946
19352
  }
18947
19353
  }
18948
- writeFileSync7(PLIST_PATH, generatePlist());
19354
+ writeFileSync8(PLIST_PATH, generatePlist());
18949
19355
  console.log(` Installed: ${PLIST_PATH}`);
18950
19356
  execFileSync2("launchctl", ["load", PLIST_PATH]);
18951
19357
  console.log(" Service loaded and starting.");
@@ -18959,7 +19365,7 @@ function uninstallMacOS() {
18959
19365
  execFileSync2("launchctl", ["unload", PLIST_PATH]);
18960
19366
  } catch {
18961
19367
  }
18962
- unlinkSync4(PLIST_PATH);
19368
+ unlinkSync6(PLIST_PATH);
18963
19369
  console.log(" Service uninstalled.");
18964
19370
  }
18965
19371
  function formatUptime(seconds) {
@@ -19025,9 +19431,9 @@ WantedBy=default.target
19025
19431
  `;
19026
19432
  }
19027
19433
  function installLinux() {
19028
- if (!existsSync24(SYSTEMD_DIR)) mkdirSync9(SYSTEMD_DIR, { recursive: true });
19029
- if (!existsSync24(LOGS_PATH)) mkdirSync9(LOGS_PATH, { recursive: true });
19030
- writeFileSync7(UNIT_PATH, generateUnit());
19434
+ if (!existsSync24(SYSTEMD_DIR)) mkdirSync10(SYSTEMD_DIR, { recursive: true });
19435
+ if (!existsSync24(LOGS_PATH)) mkdirSync10(LOGS_PATH, { recursive: true });
19436
+ writeFileSync8(UNIT_PATH, generateUnit());
19031
19437
  console.log(` Installed: ${UNIT_PATH}`);
19032
19438
  execFileSync2("systemctl", ["--user", "daemon-reload"]);
19033
19439
  execFileSync2("systemctl", ["--user", "enable", "cc-claw"]);
@@ -19047,7 +19453,7 @@ function uninstallLinux() {
19047
19453
  execFileSync2("systemctl", ["--user", "disable", "cc-claw"]);
19048
19454
  } catch {
19049
19455
  }
19050
- unlinkSync4(UNIT_PATH);
19456
+ unlinkSync6(UNIT_PATH);
19051
19457
  execFileSync2("systemctl", ["--user", "daemon-reload"]);
19052
19458
  console.log(" Service uninstalled.");
19053
19459
  }
@@ -19060,7 +19466,7 @@ function statusLinux() {
19060
19466
  }
19061
19467
  }
19062
19468
  function installService() {
19063
- if (!existsSync24(join23(CC_CLAW_HOME, ".env"))) {
19469
+ if (!existsSync24(join24(CC_CLAW_HOME, ".env"))) {
19064
19470
  console.error(` Config not found at ${CC_CLAW_HOME}/.env`);
19065
19471
  console.error(" Run 'cc-claw setup' before installing the service.");
19066
19472
  process.exitCode = 1;
@@ -19089,9 +19495,9 @@ var init_service = __esm({
19089
19495
  "use strict";
19090
19496
  init_paths();
19091
19497
  PLIST_LABEL = "com.cc-claw";
19092
- PLIST_PATH = join23(homedir8(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
19093
- SYSTEMD_DIR = join23(homedir8(), ".config", "systemd", "user");
19094
- UNIT_PATH = join23(SYSTEMD_DIR, "cc-claw.service");
19498
+ PLIST_PATH = join24(homedir8(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
19499
+ SYSTEMD_DIR = join24(homedir8(), ".config", "systemd", "user");
19500
+ UNIT_PATH = join24(SYSTEMD_DIR, "cc-claw.service");
19095
19501
  }
19096
19502
  });
19097
19503
 
@@ -19288,7 +19694,7 @@ var status_exports = {};
19288
19694
  __export(status_exports, {
19289
19695
  statusCommand: () => statusCommand
19290
19696
  });
19291
- import { existsSync as existsSync25, statSync as statSync5 } from "fs";
19697
+ import { existsSync as existsSync25, statSync as statSync6 } from "fs";
19292
19698
  async function statusCommand(globalOpts, localOpts) {
19293
19699
  try {
19294
19700
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
@@ -19328,7 +19734,7 @@ async function statusCommand(globalOpts, localOpts) {
19328
19734
  const cwdRow = readDb.prepare("SELECT cwd FROM chat_cwd WHERE chat_id = ?").get(chatId);
19329
19735
  const voiceRow = readDb.prepare("SELECT enabled FROM chat_voice WHERE chat_id = ?").get(chatId);
19330
19736
  const usageRow = readDb.prepare("SELECT * FROM chat_usage WHERE chat_id = ?").get(chatId);
19331
- const dbStat = existsSync25(DB_PATH) ? statSync5(DB_PATH) : null;
19737
+ const dbStat = existsSync25(DB_PATH) ? statSync6(DB_PATH) : null;
19332
19738
  let daemonRunning = false;
19333
19739
  let daemonInfo = {};
19334
19740
  if (localOpts.deep) {
@@ -19419,12 +19825,12 @@ var doctor_exports = {};
19419
19825
  __export(doctor_exports, {
19420
19826
  doctorCommand: () => doctorCommand
19421
19827
  });
19422
- import { existsSync as existsSync26, statSync as statSync6, accessSync, constants } from "fs";
19828
+ import { existsSync as existsSync26, statSync as statSync7, accessSync, constants } from "fs";
19423
19829
  import { execFileSync as execFileSync3 } from "child_process";
19424
19830
  async function doctorCommand(globalOpts, localOpts) {
19425
19831
  const checks = [];
19426
19832
  if (existsSync26(DB_PATH)) {
19427
- const size = statSync6(DB_PATH).size;
19833
+ const size = statSync7(DB_PATH).size;
19428
19834
  checks.push({ name: "Database", status: "ok", message: `${DB_PATH} (${(size / 1024).toFixed(0)}KB)` });
19429
19835
  try {
19430
19836
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
@@ -19714,8 +20120,8 @@ __export(gemini_exports, {
19714
20120
  geminiReorder: () => geminiReorder,
19715
20121
  geminiRotation: () => geminiRotation
19716
20122
  });
19717
- import { existsSync as existsSync28, mkdirSync as mkdirSync10, writeFileSync as writeFileSync8, readFileSync as readFileSync17, chmodSync } from "fs";
19718
- import { join as join24 } from "path";
20123
+ import { existsSync as existsSync28, mkdirSync as mkdirSync11, writeFileSync as writeFileSync9, readFileSync as readFileSync17, chmodSync } from "fs";
20124
+ import { join as join25 } from "path";
19719
20125
  import { createInterface as createInterface5 } from "readline";
19720
20126
  function requireDb() {
19721
20127
  if (!existsSync28(DB_PATH)) {
@@ -19743,7 +20149,7 @@ async function resolveSlotId(idOrLabel) {
19743
20149
  function resolveOAuthEmail(configHome) {
19744
20150
  if (!configHome) return null;
19745
20151
  try {
19746
- const accountsPath = join24(configHome, ".gemini", "google_accounts.json");
20152
+ const accountsPath = join25(configHome, ".gemini", "google_accounts.json");
19747
20153
  if (!existsSync28(accountsPath)) return null;
19748
20154
  const accounts = JSON.parse(readFileSync17(accountsPath, "utf-8"));
19749
20155
  return accounts.active || null;
@@ -19827,14 +20233,14 @@ async function geminiAddKey(globalOpts, opts) {
19827
20233
  }
19828
20234
  async function geminiAddAccount(globalOpts, opts) {
19829
20235
  await requireWriteDb();
19830
- const slotsDir = join24(CC_CLAW_HOME, "gemini-slots");
19831
- if (!existsSync28(slotsDir)) mkdirSync10(slotsDir, { recursive: true });
20236
+ const slotsDir = join25(CC_CLAW_HOME, "gemini-slots");
20237
+ if (!existsSync28(slotsDir)) mkdirSync11(slotsDir, { recursive: true });
19832
20238
  const { addGeminiSlot: addGeminiSlot2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
19833
20239
  const tempId = Date.now();
19834
- const slotDir = join24(slotsDir, `slot-${tempId}`);
19835
- mkdirSync10(slotDir, { recursive: true, mode: 448 });
19836
- mkdirSync10(join24(slotDir, ".gemini"), { recursive: true });
19837
- writeFileSync8(join24(slotDir, ".gemini", "settings.json"), JSON.stringify({
20240
+ const slotDir = join25(slotsDir, `slot-${tempId}`);
20241
+ mkdirSync11(slotDir, { recursive: true, mode: 448 });
20242
+ mkdirSync11(join25(slotDir, ".gemini"), { recursive: true });
20243
+ writeFileSync9(join25(slotDir, ".gemini", "settings.json"), JSON.stringify({
19838
20244
  security: { auth: { selectedType: "oauth-personal" } }
19839
20245
  }, null, 2));
19840
20246
  console.log("");
@@ -19851,7 +20257,7 @@ async function geminiAddAccount(globalOpts, opts) {
19851
20257
  });
19852
20258
  } catch {
19853
20259
  }
19854
- const oauthPath = join24(slotDir, ".gemini", "oauth_creds.json");
20260
+ const oauthPath = join25(slotDir, ".gemini", "oauth_creds.json");
19855
20261
  if (!existsSync28(oauthPath)) {
19856
20262
  console.log(error2("\n No OAuth credentials found. Sign-in may have failed."));
19857
20263
  console.log(" The slot directory is preserved at: " + slotDir);
@@ -19860,7 +20266,7 @@ async function geminiAddAccount(globalOpts, opts) {
19860
20266
  }
19861
20267
  let accountEmail = "unknown";
19862
20268
  try {
19863
- const accounts = JSON.parse(__require("fs").readFileSync(join24(slotDir, ".gemini", "google_accounts.json"), "utf-8"));
20269
+ const accounts = JSON.parse(__require("fs").readFileSync(join25(slotDir, ".gemini", "google_accounts.json"), "utf-8"));
19864
20270
  accountEmail = accounts.active || accountEmail;
19865
20271
  } catch {
19866
20272
  }
@@ -20677,7 +21083,7 @@ __export(db_exports, {
20677
21083
  dbPath: () => dbPath,
20678
21084
  dbStats: () => dbStats
20679
21085
  });
20680
- import { existsSync as existsSync34, statSync as statSync7, copyFileSync as copyFileSync2, mkdirSync as mkdirSync11 } from "fs";
21086
+ import { existsSync as existsSync34, statSync as statSync8, copyFileSync as copyFileSync2, mkdirSync as mkdirSync12 } from "fs";
20681
21087
  import { dirname as dirname5 } from "path";
20682
21088
  async function dbStats(globalOpts) {
20683
21089
  if (!existsSync34(DB_PATH)) {
@@ -20686,9 +21092,9 @@ async function dbStats(globalOpts) {
20686
21092
  }
20687
21093
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
20688
21094
  const readDb = openDatabaseReadOnly2();
20689
- const mainSize = statSync7(DB_PATH).size;
21095
+ const mainSize = statSync8(DB_PATH).size;
20690
21096
  const walPath = DB_PATH + "-wal";
20691
- const walSize = existsSync34(walPath) ? statSync7(walPath).size : 0;
21097
+ const walSize = existsSync34(walPath) ? statSync8(walPath).size : 0;
20692
21098
  const tableNames = readDb.prepare(
20693
21099
  "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '%_fts%' ORDER BY name"
20694
21100
  ).all();
@@ -20728,11 +21134,11 @@ async function dbBackup(globalOpts, destPath) {
20728
21134
  }
20729
21135
  const dest = destPath ?? `${DB_PATH}.backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
20730
21136
  try {
20731
- mkdirSync11(dirname5(dest), { recursive: true });
21137
+ mkdirSync12(dirname5(dest), { recursive: true });
20732
21138
  copyFileSync2(DB_PATH, dest);
20733
21139
  const walPath = DB_PATH + "-wal";
20734
21140
  if (existsSync34(walPath)) copyFileSync2(walPath, dest + "-wal");
20735
- output({ path: dest, sizeBytes: statSync7(dest).size }, (d) => {
21141
+ output({ path: dest, sizeBytes: statSync8(dest).size }, (d) => {
20736
21142
  const b = d;
20737
21143
  return `
20738
21144
  ${success("Backup created:")} ${b.path} (${(b.sizeBytes / 1024).toFixed(0)}KB)
@@ -22136,6 +22542,7 @@ __export(evolve_exports, {
22136
22542
  evolveOff: () => evolveOff,
22137
22543
  evolveOn: () => evolveOn,
22138
22544
  evolveReject: () => evolveReject,
22545
+ evolveSettings: () => evolveSettings,
22139
22546
  evolveStats: () => evolveStats,
22140
22547
  evolveStatus: () => evolveStatus,
22141
22548
  evolveUndo: () => evolveUndo
@@ -22492,6 +22899,60 @@ async function evolveHistory(globalOpts, opts) {
22492
22899
  return lines.join("\n");
22493
22900
  });
22494
22901
  }
22902
+ async function evolveSettings(globalOpts, opts) {
22903
+ ensureDb3();
22904
+ const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
22905
+ const chatId = resolveChatId(globalOpts);
22906
+ const hasUpdates = opts.perFileCap !== void 0 || opts.backupRetentionDays !== void 0;
22907
+ if (hasUpdates) {
22908
+ await ensureDaemon();
22909
+ const { apiPost: apiPost2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
22910
+ const body = {};
22911
+ if (opts.perFileCap !== void 0) {
22912
+ const val = parseInt(opts.perFileCap, 10);
22913
+ if (isNaN(val) || val < 1) {
22914
+ outputError("INVALID_VALUE", "per-file-cap must be a positive integer.");
22915
+ process.exit(1);
22916
+ }
22917
+ body.perFileCap = val;
22918
+ }
22919
+ if (opts.backupRetentionDays !== void 0) {
22920
+ const val = parseInt(opts.backupRetentionDays, 10);
22921
+ if (isNaN(val) || val < 1) {
22922
+ outputError("INVALID_VALUE", "backup-retention-days must be a positive integer.");
22923
+ process.exit(1);
22924
+ }
22925
+ body.backupRetentionDays = val;
22926
+ }
22927
+ const res = await apiPost2("/api/evolve/settings", { chatId, ...body });
22928
+ if (res.ok) {
22929
+ output(res.data, () => `
22930
+ ${success("Reflection settings updated.")}
22931
+ `);
22932
+ } else {
22933
+ outputError("SETTINGS_FAILED", `Failed: ${JSON.stringify(res.data)}`);
22934
+ process.exit(1);
22935
+ }
22936
+ } else {
22937
+ const readDb = openDatabaseReadOnly2();
22938
+ const { getReflectionSettings: getReflectionSettings2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
22939
+ const settings = getReflectionSettings2(readDb, chatId);
22940
+ readDb.close();
22941
+ output(settings, () => {
22942
+ const lines = [
22943
+ "",
22944
+ divider("Reflection Settings"),
22945
+ "",
22946
+ kvLine("Per-file cap", `${settings.perFileCap} changes/day per file`),
22947
+ kvLine("Backup retention", `${settings.backupRetentionDays} days`),
22948
+ "",
22949
+ muted("Update with: cc-claw evolve settings --per-file-cap <n> --backup-retention-days <n>"),
22950
+ ""
22951
+ ];
22952
+ return lines.join("\n");
22953
+ });
22954
+ }
22955
+ }
22495
22956
  var init_evolve = __esm({
22496
22957
  "src/cli/commands/evolve.ts"() {
22497
22958
  "use strict";
@@ -22504,10 +22965,10 @@ var init_evolve = __esm({
22504
22965
 
22505
22966
  // src/setup.ts
22506
22967
  var setup_exports = {};
22507
- import { existsSync as existsSync46, writeFileSync as writeFileSync9, readFileSync as readFileSync20, copyFileSync as copyFileSync3, mkdirSync as mkdirSync12, statSync as statSync8 } from "fs";
22968
+ import { existsSync as existsSync46, writeFileSync as writeFileSync10, readFileSync as readFileSync20, copyFileSync as copyFileSync3, mkdirSync as mkdirSync13, statSync as statSync9 } from "fs";
22508
22969
  import { execFileSync as execFileSync4 } from "child_process";
22509
22970
  import { createInterface as createInterface7 } from "readline";
22510
- import { join as join25 } from "path";
22971
+ import { join as join26 } from "path";
22511
22972
  function divider2() {
22512
22973
  console.log(dim("\u2500".repeat(55)));
22513
22974
  }
@@ -22582,7 +23043,7 @@ async function setup() {
22582
23043
  }
22583
23044
  console.log("");
22584
23045
  for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
22585
- if (!existsSync46(dir)) mkdirSync12(dir, { recursive: true });
23046
+ if (!existsSync46(dir)) mkdirSync13(dir, { recursive: true });
22586
23047
  }
22587
23048
  const env = {};
22588
23049
  const envSource = existsSync46(ENV_PATH) ? ENV_PATH : existsSync46(".env") ? ".env" : null;
@@ -22595,9 +23056,9 @@ async function setup() {
22595
23056
  if (match) env[match[1].trim()] = match[2].trim();
22596
23057
  }
22597
23058
  }
22598
- const cwdDb = join25(process.cwd(), "cc-claw.db");
23059
+ const cwdDb = join26(process.cwd(), "cc-claw.db");
22599
23060
  if (existsSync46(cwdDb) && !existsSync46(DB_PATH)) {
22600
- const { size } = statSync8(cwdDb);
23061
+ const { size } = statSync9(cwdDb);
22601
23062
  console.log(yellow(` Found existing database at ${cwdDb} (${(size / 1024).toFixed(0)}KB)`));
22602
23063
  const migrate = await confirm("Copy database to ~/.cc-claw/? (preserves memories & history)", true);
22603
23064
  if (migrate) {
@@ -22813,7 +23274,7 @@ async function setup() {
22813
23274
  envLines.push("", "# Video Analysis", `GEMINI_API_KEY=${env.GEMINI_API_KEY}`);
22814
23275
  }
22815
23276
  const envContent = envLines.join("\n") + "\n";
22816
- writeFileSync9(ENV_PATH, envContent, { mode: 384 });
23277
+ writeFileSync10(ENV_PATH, envContent, { mode: 384 });
22817
23278
  console.log(green(` Config saved to ${ENV_PATH} (permissions: owner-only)`));
22818
23279
  header(6, TOTAL_STEPS, "Run on Startup (Daemon)");
22819
23280
  console.log(" CC-Claw can run automatically in the background, starting");
@@ -23407,6 +23868,10 @@ evolve.command("history").description("Insight history").option("--status <statu
23407
23868
  const { evolveHistory: evolveHistory2 } = await Promise.resolve().then(() => (init_evolve(), evolve_exports));
23408
23869
  await evolveHistory2(program.opts(), opts);
23409
23870
  });
23871
+ evolve.command("settings").description("View or update reflection settings").option("--per-file-cap <n>", "Max applies per file per day").option("--backup-retention-days <n>", "Days to keep backup files").action(async (opts) => {
23872
+ const { evolveSettings: evolveSettings2 } = await Promise.resolve().then(() => (init_evolve(), evolve_exports));
23873
+ await evolveSettings2(program.opts(), opts);
23874
+ });
23410
23875
  program.command("start", { hidden: true }).description("Run the bot in the foreground (use 'service start' for background daemon)").action(async () => {
23411
23876
  await Promise.resolve().then(() => (init_index(), index_exports));
23412
23877
  });