cc-claw 0.18.2 → 0.18.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 +333 -47
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -33,7 +33,7 @@ var VERSION;
33
33
  var init_version = __esm({
34
34
  "src/version.ts"() {
35
35
  "use strict";
36
- VERSION = true ? "0.18.2" : (() => {
36
+ VERSION = true ? "0.18.3" : (() => {
37
37
  try {
38
38
  return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
39
39
  } catch {
@@ -9717,6 +9717,14 @@ function parseAnalysisOutput(raw) {
9717
9717
  if (!VALID_CATEGORIES.includes(category)) continue;
9718
9718
  const confidence = confidenceMatch ? parseFloat(confidenceMatch[1].trim()) : 0.5;
9719
9719
  if (isNaN(confidence)) continue;
9720
+ let proposedDiff = diffMatch?.[1]?.trim() ?? "";
9721
+ if (proposedDiff.startsWith("```")) {
9722
+ proposedDiff = proposedDiff.replace(/^```[a-zA-Z0-9-]*\r?\n/, "");
9723
+ if (proposedDiff.endsWith("```")) {
9724
+ proposedDiff = proposedDiff.replace(/\r?\n```$/, "");
9725
+ }
9726
+ proposedDiff = proposedDiff.trim();
9727
+ }
9720
9728
  results.push({
9721
9729
  insight: insightMatch[1].trim(),
9722
9730
  category,
@@ -9724,7 +9732,7 @@ function parseAnalysisOutput(raw) {
9724
9732
  targetFile: targetMatch?.[1]?.trim() ?? "",
9725
9733
  confidence: Math.max(0, Math.min(1, confidence)),
9726
9734
  proposedAction: actionMatch?.[1]?.trim() ?? "",
9727
- proposedDiff: diffMatch?.[1]?.trim() ?? "",
9735
+ proposedDiff,
9728
9736
  conflictsWith: conflictsMatch?.[1]?.trim() ?? "none"
9729
9737
  });
9730
9738
  }
@@ -10128,6 +10136,181 @@ var init_analyze = __esm({
10128
10136
  }
10129
10137
  });
10130
10138
 
10139
+ // src/reflection/ai-apply.ts
10140
+ var ai_apply_exports = {};
10141
+ __export(ai_apply_exports, {
10142
+ applyWithAI: () => applyWithAI
10143
+ });
10144
+ import { spawn as spawn5 } from "child_process";
10145
+ import { createInterface as createInterface4 } from "readline";
10146
+ function buildApplyPrompt(originalContent, instruction, proposedDiff, targetFile) {
10147
+ return `You are a precise markdown document editor. Your ONLY job is to apply the requested change to the document while preserving its structural integrity.
10148
+
10149
+ === CURRENT FILE: ${targetFile} ===
10150
+ ${originalContent}
10151
+ === END FILE ===
10152
+
10153
+ === CHANGE TO APPLY ===
10154
+ Instruction: ${instruction}
10155
+
10156
+ Diff guidance:
10157
+ ${proposedDiff}
10158
+ === END CHANGE ===
10159
+
10160
+ Rules:
10161
+ 1. Place new content under the semantically correct section heading (## heading)
10162
+ 2. Maintain consistent formatting (bullet style, indentation) with the target section
10163
+ 3. Do NOT add, remove, or modify any content other than what the change requires
10164
+ 4. Do NOT wrap your output in markdown code fences
10165
+ 5. Do NOT add explanations, commentary, or notes
10166
+ 6. Preserve all existing headings and their order
10167
+ 7. Return ONLY the complete updated file content
10168
+
10169
+ Output the complete updated file now:`;
10170
+ }
10171
+ async function spawnApplyLLM(adapter, model2, prompt) {
10172
+ const config2 = adapter.buildSpawnConfig({
10173
+ prompt,
10174
+ model: model2,
10175
+ permMode: "yolo",
10176
+ allowedTools: []
10177
+ });
10178
+ const env = adapter.getEnv();
10179
+ let resultText = "";
10180
+ let accumulatedText = "";
10181
+ await new Promise((resolve) => {
10182
+ const proc = spawn5(config2.executable, config2.args, {
10183
+ env,
10184
+ stdio: ["ignore", "pipe", "pipe"],
10185
+ ...config2.cwd ? { cwd: config2.cwd } : {}
10186
+ });
10187
+ proc.stderr?.resume();
10188
+ const rl2 = createInterface4({ input: proc.stdout });
10189
+ const timeout = setTimeout(() => {
10190
+ warn("[ai-apply] LLM spawn timeout");
10191
+ rl2.close();
10192
+ proc.kill("SIGTERM");
10193
+ setTimeout(() => proc.kill("SIGKILL"), 2e3);
10194
+ }, AI_APPLY_TIMEOUT_MS);
10195
+ rl2.on("line", (line) => {
10196
+ if (!line.trim()) return;
10197
+ let msg;
10198
+ try {
10199
+ msg = JSON.parse(line);
10200
+ } catch {
10201
+ return;
10202
+ }
10203
+ const events = adapter.parseLine(msg);
10204
+ for (const ev of events) {
10205
+ if (ev.type === "text" && ev.text) accumulatedText = appendTextChunk(accumulatedText, ev.text);
10206
+ if (ev.type === "result") {
10207
+ resultText = ev.resultText || accumulatedText;
10208
+ if (adapter.shouldKillOnResult()) {
10209
+ rl2.close();
10210
+ proc.kill("SIGTERM");
10211
+ }
10212
+ }
10213
+ }
10214
+ });
10215
+ proc.on("error", () => {
10216
+ clearTimeout(timeout);
10217
+ resolve();
10218
+ });
10219
+ proc.on("close", () => {
10220
+ clearTimeout(timeout);
10221
+ resolve();
10222
+ });
10223
+ });
10224
+ if (!resultText) resultText = accumulatedText;
10225
+ return resultText;
10226
+ }
10227
+ function resolveApplyAdapter(chatId) {
10228
+ try {
10229
+ const adapter = getAdapterForChat(chatId);
10230
+ return { adapter, model: adapter.defaultModel };
10231
+ } catch {
10232
+ try {
10233
+ const adapter = getAdapter("claude");
10234
+ return { adapter, model: adapter.defaultModel };
10235
+ } catch {
10236
+ return null;
10237
+ }
10238
+ }
10239
+ }
10240
+ function extractHeadings(content) {
10241
+ return content.split("\n").filter((line) => /^#{1,6}\s/.test(line)).map((line) => line.trim());
10242
+ }
10243
+ function validateOutput(original, output2) {
10244
+ if (!output2.trim()) {
10245
+ return { valid: false, reason: "LLM returned empty output" };
10246
+ }
10247
+ const originalLines = original.split("\n").length;
10248
+ const outputLines = output2.split("\n").length;
10249
+ if (originalLines > 5 && outputLines < originalLines * MIN_LINE_RETENTION) {
10250
+ return {
10251
+ valid: false,
10252
+ reason: `Output has ${outputLines} lines vs original ${originalLines} (${Math.round(outputLines / originalLines * 100)}% retained, min ${MIN_LINE_RETENTION * 100}%)`
10253
+ };
10254
+ }
10255
+ const originalHeadings = extractHeadings(original);
10256
+ const outputHeadings = extractHeadings(output2);
10257
+ for (const heading4 of originalHeadings) {
10258
+ if (!outputHeadings.includes(heading4)) {
10259
+ return {
10260
+ valid: false,
10261
+ reason: `Missing heading: "${heading4}"`
10262
+ };
10263
+ }
10264
+ }
10265
+ if (output2.trim() === original.trim()) {
10266
+ return { valid: false, reason: "Output is identical to original (no changes applied)" };
10267
+ }
10268
+ return { valid: true };
10269
+ }
10270
+ function stripMarkdownFences(text) {
10271
+ const fencePattern = /^```(?:markdown|md)?\s*\n([\s\S]*?)\n```\s*$/;
10272
+ const match = text.match(fencePattern);
10273
+ if (match) return match[1];
10274
+ return text;
10275
+ }
10276
+ async function applyWithAI(chatId, originalContent, instruction, proposedDiff, targetFile) {
10277
+ const resolved = resolveApplyAdapter(chatId);
10278
+ if (!resolved) {
10279
+ return { success: false, newContent: "", error: "No backend adapter available" };
10280
+ }
10281
+ const { adapter, model: model2 } = resolved;
10282
+ log(`[ai-apply] Using ${adapter.id}:${model2} for structural apply on ${targetFile}`);
10283
+ try {
10284
+ const prompt = buildApplyPrompt(originalContent, instruction, proposedDiff, targetFile);
10285
+ const rawOutput = await spawnApplyLLM(adapter, model2, prompt);
10286
+ if (!rawOutput.trim()) {
10287
+ return { success: false, newContent: "", error: "LLM returned empty response" };
10288
+ }
10289
+ const cleanOutput = stripMarkdownFences(rawOutput);
10290
+ const validation = validateOutput(originalContent, cleanOutput);
10291
+ if (!validation.valid) {
10292
+ return { success: false, newContent: "", error: `Validation failed: ${validation.reason}` };
10293
+ }
10294
+ log(`[ai-apply] Successfully applied change to ${targetFile}`);
10295
+ return { success: true, newContent: cleanOutput };
10296
+ } catch (err) {
10297
+ const msg = err instanceof Error ? err.message : String(err);
10298
+ warn(`[ai-apply] LLM apply failed: ${msg}`);
10299
+ return { success: false, newContent: "", error: msg };
10300
+ }
10301
+ }
10302
+ var AI_APPLY_TIMEOUT_MS, MIN_LINE_RETENTION;
10303
+ var init_ai_apply = __esm({
10304
+ "src/reflection/ai-apply.ts"() {
10305
+ "use strict";
10306
+ init_log();
10307
+ init_text_utils();
10308
+ init_backends();
10309
+ AI_APPLY_TIMEOUT_MS = 6e4;
10310
+ MIN_LINE_RETENTION = 0.7;
10311
+ }
10312
+ });
10313
+
10131
10314
  // src/reflection/apply.ts
10132
10315
  var apply_exports = {};
10133
10316
  __export(apply_exports, {
@@ -10258,7 +10441,31 @@ async function applyInsight(insightId) {
10258
10441
  writeFileSync5(backupPath, original, "utf-8");
10259
10442
  pruneBackups(absolutePath);
10260
10443
  }
10261
- const newContent = applyDiff(original, insight.proposedDiff, insight.proposedAction);
10444
+ let newContent;
10445
+ if (insight.proposedAction !== "create") {
10446
+ try {
10447
+ const { applyWithAI: applyWithAI2 } = await Promise.resolve().then(() => (init_ai_apply(), ai_apply_exports));
10448
+ const aiResult = await applyWithAI2(
10449
+ chatId,
10450
+ original,
10451
+ insight.insight,
10452
+ insight.proposedDiff,
10453
+ insight.targetFile
10454
+ );
10455
+ if (aiResult.success) {
10456
+ newContent = aiResult.newContent;
10457
+ log(`[reflection/apply] AI apply succeeded for insight #${insightId}`);
10458
+ } else {
10459
+ warn(`[reflection/apply] AI apply failed (${aiResult.error}), falling back to static diff`);
10460
+ newContent = applyDiff(original, insight.proposedDiff, insight.proposedAction);
10461
+ }
10462
+ } catch (aiErr) {
10463
+ warn(`[reflection/apply] AI apply threw (${aiErr}), falling back to static diff`);
10464
+ newContent = applyDiff(original, insight.proposedDiff, insight.proposedAction);
10465
+ }
10466
+ } else {
10467
+ newContent = applyDiff(original, insight.proposedDiff, insight.proposedAction);
10468
+ }
10262
10469
  writeFileSync5(absolutePath, newContent, "utf-8");
10263
10470
  const rollbackData = JSON.stringify({ original, backupPath, appliedAt: (/* @__PURE__ */ new Date()).toISOString() });
10264
10471
  updateInsightRollback(db3, insightId, rollbackData);
@@ -10956,8 +11163,8 @@ __export(agent_exports, {
10956
11163
  stopAgent: () => stopAgent,
10957
11164
  stopAllActiveAgents: () => stopAllActiveAgents
10958
11165
  });
10959
- import { spawn as spawn5 } from "child_process";
10960
- import { createInterface as createInterface4 } from "readline";
11166
+ import { spawn as spawn6 } from "child_process";
11167
+ import { createInterface as createInterface5 } from "readline";
10961
11168
  function isSyntheticChatId(chatId) {
10962
11169
  return chatId.startsWith("sq:") || chatId.startsWith("cron:");
10963
11170
  }
@@ -11021,7 +11228,7 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
11021
11228
  const env = opts?.envOverride ? thinkingConfig?.envOverrides ? { ...opts.envOverride, ...thinkingConfig.envOverrides } : opts.envOverride : adapter.getEnv(thinkingConfig?.envOverrides);
11022
11229
  const finalArgs = thinkingConfig?.extraArgs ? [...config2.args, ...thinkingConfig.extraArgs] : config2.args;
11023
11230
  log(`[agent:spawn] backend=${adapter.id} exe=${config2.executable} model=${model2} timeout=${effectiveTimeout / 1e3}s cwd=${config2.cwd ?? "(inherited)"}`);
11024
- const proc = spawn5(config2.executable, finalArgs, {
11231
+ const proc = spawn6(config2.executable, finalArgs, {
11025
11232
  env,
11026
11233
  stdio: ["ignore", "pipe", "pipe"],
11027
11234
  detached: true,
@@ -11054,7 +11261,7 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
11054
11261
  const pendingTools = /* @__PURE__ */ new Map();
11055
11262
  const stderrChunks = [];
11056
11263
  proc.stderr?.on("data", (chunk) => stderrChunks.push(chunk));
11057
- const rl2 = createInterface4({ input: proc.stdout });
11264
+ const rl2 = createInterface5({ input: proc.stdout });
11058
11265
  let firstLine = true;
11059
11266
  const frTimeoutMs = adapter.id === "gemini" ? opts?.firstResponseTimeoutMs ?? FIRST_RESPONSE_TIMEOUT_MS : 0;
11060
11267
  let firstResponseTimer;
@@ -17024,8 +17231,8 @@ __export(analyze_exports2, {
17024
17231
  runIdentityAudit: () => runIdentityAudit,
17025
17232
  runSkillAudit: () => runSkillAudit
17026
17233
  });
17027
- import { spawn as spawn6 } from "child_process";
17028
- import { createInterface as createInterface5 } from "readline";
17234
+ import { spawn as spawn7 } from "child_process";
17235
+ import { createInterface as createInterface6 } from "readline";
17029
17236
  import { readFileSync as readFileSync12, existsSync as existsSync21, readdirSync as readdirSync11 } from "fs";
17030
17237
  import { join as join22 } from "path";
17031
17238
  import { homedir as homedir7 } from "os";
@@ -17047,6 +17254,14 @@ function parseOptimizeOutput(raw, validAreas) {
17047
17254
  if (!validAreas.includes(area)) continue;
17048
17255
  const severity = severityMatch?.[1]?.trim().toLowerCase() ?? "info";
17049
17256
  if (!VALID_SEVERITIES.includes(severity)) continue;
17257
+ let proposedDiff = diffMatch?.[1]?.trim() ?? "";
17258
+ if (proposedDiff.startsWith("```")) {
17259
+ proposedDiff = proposedDiff.replace(/^```[a-zA-Z0-9-]*\r?\n/, "");
17260
+ if (proposedDiff.endsWith("```")) {
17261
+ proposedDiff = proposedDiff.replace(/\r?\n```$/, "");
17262
+ }
17263
+ proposedDiff = proposedDiff.trim();
17264
+ }
17050
17265
  results.push({
17051
17266
  title: findingMatch[1].trim().slice(0, 80),
17052
17267
  area,
@@ -17054,7 +17269,7 @@ function parseOptimizeOutput(raw, validAreas) {
17054
17269
  detail: detailMatch?.[1]?.trim() ?? "",
17055
17270
  location: locationMatch?.[1]?.trim() ?? "",
17056
17271
  suggestion: suggestionMatch?.[1]?.trim() ?? "",
17057
- proposedDiff: diffMatch?.[1]?.trim() ?? ""
17272
+ proposedDiff
17058
17273
  });
17059
17274
  }
17060
17275
  return results;
@@ -17070,13 +17285,13 @@ async function spawnAnalysis2(adapter, model2, prompt, timeoutMs = ANALYSIS_TIME
17070
17285
  let resultText = "";
17071
17286
  let accumulatedText = "";
17072
17287
  await new Promise((resolve) => {
17073
- const proc = spawn6(config2.executable, config2.args, {
17288
+ const proc = spawn7(config2.executable, config2.args, {
17074
17289
  env,
17075
17290
  stdio: ["ignore", "pipe", "pipe"],
17076
17291
  ...config2.cwd ? { cwd: config2.cwd } : {}
17077
17292
  });
17078
17293
  proc.stderr?.resume();
17079
- const rl2 = createInterface5({ input: proc.stdout });
17294
+ const rl2 = createInterface6({ input: proc.stdout });
17080
17295
  const timeout = setTimeout(() => {
17081
17296
  warn(`[optimizer] Analysis timeout (${adapter.id}:${model2})`);
17082
17297
  rl2.close();
@@ -17558,8 +17773,8 @@ var init_ui2 = __esm({
17558
17773
  });
17559
17774
 
17560
17775
  // src/router/optimize.ts
17561
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync7, existsSync as existsSync22 } from "fs";
17562
- import { join as join23 } from "path";
17776
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync7, existsSync as existsSync22, readdirSync as readdirSync12, unlinkSync as unlinkSync6 } from "fs";
17777
+ import { join as join23, dirname as dirname4 } from "path";
17563
17778
  import { homedir as homedir8 } from "os";
17564
17779
  async function handleOptimizeCommand(chatId, channel, _args) {
17565
17780
  const { getModelDisplayInfo: getModelDisplayInfo2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
@@ -17664,13 +17879,38 @@ async function runIdentityAuditFlow(chatId, channel) {
17664
17879
  } = await Promise.resolve().then(() => (init_ui2(), ui_exports));
17665
17880
  const modelInfo = getModelDisplayInfo2(chatId);
17666
17881
  if (!modelInfo) return;
17667
- await channel.sendText(
17882
+ const progressMsgId = typeof channel.sendTextReturningId === "function" ? await channel.sendTextReturningId(
17668
17883
  chatId,
17669
17884
  buildProgressMessage2("identity files", modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel),
17670
- { parseMode: "plain" }
17671
- );
17885
+ "plain"
17886
+ ) : void 0;
17887
+ if (!progressMsgId) {
17888
+ await channel.sendText(
17889
+ chatId,
17890
+ buildProgressMessage2("identity files", modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel),
17891
+ { parseMode: "plain" }
17892
+ );
17893
+ }
17894
+ const startTime = Date.now();
17895
+ const progressInterval = setInterval(async () => {
17896
+ const elapsed = Math.round((Date.now() - startTime) / 1e3);
17897
+ try {
17898
+ if (channel.sendTyping) await channel.sendTyping(chatId);
17899
+ if (progressMsgId && channel.editText) {
17900
+ await channel.editText(
17901
+ chatId,
17902
+ progressMsgId,
17903
+ buildProgressMessage2("identity files", modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel) + `
17904
+ \u23F3 Analyzing... (${elapsed}s)`,
17905
+ "plain"
17906
+ );
17907
+ }
17908
+ } catch {
17909
+ }
17910
+ }, 5e3);
17672
17911
  try {
17673
17912
  const result = await runIdentityAudit2(chatId);
17913
+ clearInterval(progressInterval);
17674
17914
  activeSessions.set(chatId, {
17675
17915
  chatId,
17676
17916
  result,
@@ -17685,6 +17925,7 @@ async function runIdentityAuditFlow(chatId, channel) {
17685
17925
  await channel.sendText(chatId, summary, { parseMode: "plain" });
17686
17926
  }
17687
17927
  } catch (e) {
17928
+ clearInterval(progressInterval);
17688
17929
  await channel.sendText(chatId, `Identity audit failed: ${e}`, { parseMode: "plain" });
17689
17930
  }
17690
17931
  }
@@ -17715,13 +17956,38 @@ async function runSkillAuditFlow(chatId, channel, skillName) {
17715
17956
  const modelInfo = getModelDisplayInfo2(chatId);
17716
17957
  if (!modelInfo) return;
17717
17958
  const skillPath = join23(homedir8(), ".cc-claw", "workspace", "skills", skillName, "SKILL.md");
17718
- await channel.sendText(
17959
+ const progressMsgId = typeof channel.sendTextReturningId === "function" ? await channel.sendTextReturningId(
17719
17960
  chatId,
17720
17961
  buildProgressMessage2(`skill: ${skillName}`, modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel),
17721
- { parseMode: "plain" }
17722
- );
17962
+ "plain"
17963
+ ) : void 0;
17964
+ if (!progressMsgId) {
17965
+ await channel.sendText(
17966
+ chatId,
17967
+ buildProgressMessage2(`skill: ${skillName}`, modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel),
17968
+ { parseMode: "plain" }
17969
+ );
17970
+ }
17971
+ const startTime = Date.now();
17972
+ const progressInterval = setInterval(async () => {
17973
+ const elapsed = Math.round((Date.now() - startTime) / 1e3);
17974
+ try {
17975
+ if (channel.sendTyping) await channel.sendTyping(chatId);
17976
+ if (progressMsgId && channel.editText) {
17977
+ await channel.editText(
17978
+ chatId,
17979
+ progressMsgId,
17980
+ buildProgressMessage2(`skill: ${skillName}`, modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel) + `
17981
+ \u23F3 Analyzing... (${elapsed}s)`,
17982
+ "plain"
17983
+ );
17984
+ }
17985
+ } catch {
17986
+ }
17987
+ }, 5e3);
17723
17988
  try {
17724
17989
  const result = await runSkillAudit2(chatId, skillPath);
17990
+ clearInterval(progressInterval);
17725
17991
  activeSessions.set(chatId, {
17726
17992
  chatId,
17727
17993
  result,
@@ -17736,6 +18002,7 @@ async function runSkillAuditFlow(chatId, channel, skillName) {
17736
18002
  await channel.sendText(chatId, summary, { parseMode: "plain" });
17737
18003
  }
17738
18004
  } catch (e) {
18005
+ clearInterval(progressInterval);
17739
18006
  await channel.sendText(chatId, `Skill audit failed: ${e}`, { parseMode: "plain" });
17740
18007
  }
17741
18008
  }
@@ -17787,8 +18054,28 @@ async function applyFinding(chatId, channel, index) {
17787
18054
  const backupPath = targetPath + `.bak.${Date.now()}`;
17788
18055
  writeFileSync7(backupPath, original, "utf-8");
17789
18056
  pruneBackups2(targetPath);
17790
- const { applyDiff: applyDiff2 } = await Promise.resolve().then(() => (init_apply(), apply_exports));
17791
- const newContent = applyDiff2(original, finding.proposedDiff, "replace");
18057
+ let newContent;
18058
+ try {
18059
+ const { applyWithAI: applyWithAI2 } = await Promise.resolve().then(() => (init_ai_apply(), ai_apply_exports));
18060
+ const aiResult = await applyWithAI2(
18061
+ chatId,
18062
+ original,
18063
+ finding.suggestion || finding.title,
18064
+ finding.proposedDiff,
18065
+ finding.location.split(":")[0] || "unknown"
18066
+ );
18067
+ if (aiResult.success) {
18068
+ newContent = aiResult.newContent;
18069
+ } else {
18070
+ warn(`[optimizer] AI apply failed (${aiResult.error}), falling back to static diff`);
18071
+ const { applyDiff: applyDiff2 } = await Promise.resolve().then(() => (init_apply(), apply_exports));
18072
+ newContent = applyDiff2(original, finding.proposedDiff, "replace");
18073
+ }
18074
+ } catch (aiErr) {
18075
+ warn(`[optimizer] AI apply threw (${aiErr}), falling back to static diff`);
18076
+ const { applyDiff: applyDiff2 } = await Promise.resolve().then(() => (init_apply(), apply_exports));
18077
+ newContent = applyDiff2(original, finding.proposedDiff, "replace");
18078
+ }
17792
18079
  if (newContent === original) {
17793
18080
  await channel.sendText(chatId, `\u26A0\uFE0F Diff produced no changes. The content may have already been modified.`, { parseMode: "plain" });
17794
18081
  session2.skipped.push(index);
@@ -17850,16 +18137,14 @@ function resolveTargetFile(location, auditTarget) {
17850
18137
  return null;
17851
18138
  }
17852
18139
  function pruneBackups2(absolutePath) {
17853
- const { readdirSync: readDir, unlinkSync: unlink4 } = __require("fs");
17854
- const { dirname: dirName } = __require("path");
17855
- const dir = dirName(absolutePath);
18140
+ const dir = dirname4(absolutePath);
17856
18141
  const baseName = absolutePath.split("/").pop() ?? "";
17857
18142
  try {
17858
- const backups = readDir(dir).filter((f) => f.startsWith(baseName + ".bak.")).sort().map((f) => join23(dir, f));
18143
+ const backups = readdirSync12(dir).filter((f) => f.startsWith(baseName + ".bak.")).sort().map((f) => join23(dir, f));
17859
18144
  while (backups.length > 3) {
17860
18145
  const oldest = backups.shift();
17861
18146
  try {
17862
- unlink4(oldest);
18147
+ unlinkSync6(oldest);
17863
18148
  } catch {
17864
18149
  }
17865
18150
  }
@@ -17870,6 +18155,7 @@ var activeSessions;
17870
18155
  var init_optimize = __esm({
17871
18156
  "src/router/optimize.ts"() {
17872
18157
  "use strict";
18158
+ init_log();
17873
18159
  activeSessions = /* @__PURE__ */ new Map();
17874
18160
  }
17875
18161
  });
@@ -20658,9 +20944,9 @@ __export(session_log_exports2, {
20658
20944
  startSessionLogCleanupTimer: () => startSessionLogCleanupTimer,
20659
20945
  tailSessionLog: () => tailSessionLog
20660
20946
  });
20661
- import { existsSync as existsSync23, mkdirSync as mkdirSync9, appendFileSync, readdirSync as readdirSync12, unlinkSync as unlinkSync6, statSync as statSync7, createReadStream } from "fs";
20947
+ import { existsSync as existsSync23, mkdirSync as mkdirSync9, appendFileSync, readdirSync as readdirSync13, unlinkSync as unlinkSync7, statSync as statSync7, createReadStream } from "fs";
20662
20948
  import { join as join24, basename as basename3 } from "path";
20663
- import { createInterface as createInterface6 } from "readline";
20949
+ import { createInterface as createInterface7 } from "readline";
20664
20950
  function getRetentionDays() {
20665
20951
  const env = process.env.SESSION_LOG_RETENTION_DAYS;
20666
20952
  if (env) {
@@ -20675,13 +20961,13 @@ function cleanupSessionLogs(retentionDays) {
20675
20961
  const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
20676
20962
  let cleaned = 0;
20677
20963
  try {
20678
- for (const file of readdirSync12(SESSION_LOGS_PATH)) {
20964
+ for (const file of readdirSync13(SESSION_LOGS_PATH)) {
20679
20965
  if (!file.startsWith("session-") || !file.endsWith(".log")) continue;
20680
20966
  const filePath = join24(SESSION_LOGS_PATH, file);
20681
20967
  try {
20682
20968
  const { mtimeMs } = statSync7(filePath);
20683
20969
  if (mtimeMs < cutoff) {
20684
- unlinkSync6(filePath);
20970
+ unlinkSync7(filePath);
20685
20971
  cleaned++;
20686
20972
  }
20687
20973
  } catch {
@@ -20705,7 +20991,7 @@ function startSessionLogCleanupTimer() {
20705
20991
  function listSessionLogs() {
20706
20992
  if (!existsSync23(SESSION_LOGS_PATH)) return [];
20707
20993
  const logs = [];
20708
- for (const file of readdirSync12(SESSION_LOGS_PATH)) {
20994
+ for (const file of readdirSync13(SESSION_LOGS_PATH)) {
20709
20995
  if (!file.startsWith("session-") || !file.endsWith(".log")) continue;
20710
20996
  const filePath = join24(SESSION_LOGS_PATH, file);
20711
20997
  try {
@@ -20731,7 +21017,7 @@ async function* tailSessionLog(filePath, lines = 50) {
20731
21017
  return;
20732
21018
  }
20733
21019
  const allLines = [];
20734
- const rl2 = createInterface6({
21020
+ const rl2 = createInterface7({
20735
21021
  input: createReadStream(filePath, { encoding: "utf-8" }),
20736
21022
  crlfDelay: Infinity
20737
21023
  });
@@ -22158,7 +22444,7 @@ var init_wrap_backend = __esm({
22158
22444
  });
22159
22445
 
22160
22446
  // src/agents/runners/config-loader.ts
22161
- import { readFileSync as readFileSync14, readdirSync as readdirSync13, existsSync as existsSync24, mkdirSync as mkdirSync10, watchFile, unwatchFile } from "fs";
22447
+ import { readFileSync as readFileSync14, readdirSync as readdirSync14, existsSync as existsSync24, mkdirSync as mkdirSync10, watchFile, unwatchFile } from "fs";
22162
22448
  import { join as join26 } from "path";
22163
22449
  import { execFileSync as execFileSync2 } from "child_process";
22164
22450
  function resolveExecutable(config2) {
@@ -22312,7 +22598,7 @@ function loadAllRunnerConfigs() {
22312
22598
  mkdirSync10(RUNNERS_PATH, { recursive: true });
22313
22599
  return [];
22314
22600
  }
22315
- const files = readdirSync13(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
22601
+ const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
22316
22602
  const configs = [];
22317
22603
  for (const file of files) {
22318
22604
  const config2 = loadRunnerConfig(join26(RUNNERS_PATH, file));
@@ -22343,7 +22629,7 @@ function watchRunnerConfigs(onChange) {
22343
22629
  watchedFiles.delete(prev);
22344
22630
  }
22345
22631
  }
22346
- const files = readdirSync13(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
22632
+ const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
22347
22633
  for (const file of files) {
22348
22634
  const fullPath = join26(RUNNERS_PATH, file);
22349
22635
  if (watchedFiles.has(fullPath)) continue;
@@ -24333,7 +24619,7 @@ __export(service_exports, {
24333
24619
  serviceStatus: () => serviceStatus,
24334
24620
  uninstallService: () => uninstallService
24335
24621
  });
24336
- import { existsSync as existsSync29, mkdirSync as mkdirSync13, writeFileSync as writeFileSync9, unlinkSync as unlinkSync7 } from "fs";
24622
+ import { existsSync as existsSync29, mkdirSync as mkdirSync13, writeFileSync as writeFileSync9, unlinkSync as unlinkSync8 } from "fs";
24337
24623
  import { execFileSync as execFileSync3, execSync as execSync6 } from "child_process";
24338
24624
  import { homedir as homedir10, platform } from "os";
24339
24625
  import { join as join30, dirname as dirname6 } from "path";
@@ -24442,7 +24728,7 @@ function uninstallMacOS() {
24442
24728
  execFileSync3("launchctl", ["unload", PLIST_PATH]);
24443
24729
  } catch {
24444
24730
  }
24445
- unlinkSync7(PLIST_PATH);
24731
+ unlinkSync8(PLIST_PATH);
24446
24732
  console.log(" Service uninstalled.");
24447
24733
  }
24448
24734
  function formatUptime(seconds) {
@@ -24531,7 +24817,7 @@ function uninstallLinux() {
24531
24817
  execFileSync3("systemctl", ["--user", "disable", "cc-claw"]);
24532
24818
  } catch {
24533
24819
  }
24534
- unlinkSync7(UNIT_PATH);
24820
+ unlinkSync8(UNIT_PATH);
24535
24821
  execFileSync3("systemctl", ["--user", "daemon-reload"]);
24536
24822
  console.log(" Service uninstalled.");
24537
24823
  }
@@ -25297,7 +25583,7 @@ __export(gemini_exports, {
25297
25583
  });
25298
25584
  import { existsSync as existsSync34, mkdirSync as mkdirSync14, writeFileSync as writeFileSync10, readFileSync as readFileSync22, chmodSync } from "fs";
25299
25585
  import { join as join31 } from "path";
25300
- import { createInterface as createInterface7 } from "readline";
25586
+ import { createInterface as createInterface8 } from "readline";
25301
25587
  function requireDb() {
25302
25588
  if (!existsSync34(DB_PATH)) {
25303
25589
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
@@ -25370,7 +25656,7 @@ async function geminiList(globalOpts) {
25370
25656
  }
25371
25657
  async function geminiAddKey(globalOpts, opts) {
25372
25658
  await requireWriteDb();
25373
- const rl2 = createInterface7({ input: process.stdin, output: process.stdout });
25659
+ const rl2 = createInterface8({ input: process.stdin, output: process.stdout });
25374
25660
  const ask2 = (q) => new Promise((r) => rl2.question(q, r));
25375
25661
  const key = await ask2("Paste your Gemini API key: ");
25376
25662
  rl2.close();
@@ -25562,7 +25848,7 @@ __export(backend_cmd_factory_exports, {
25562
25848
  });
25563
25849
  import { existsSync as existsSync35, mkdirSync as mkdirSync15, readFileSync as readFileSync23 } from "fs";
25564
25850
  import { join as join32 } from "path";
25565
- import { createInterface as createInterface8 } from "readline";
25851
+ import { createInterface as createInterface9 } from "readline";
25566
25852
  function requireDb2() {
25567
25853
  if (!existsSync35(DB_PATH)) {
25568
25854
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
@@ -25623,7 +25909,7 @@ Add one with: cc-claw ${backend2} add-account or cc-claw ${backend2} add-key`)
25623
25909
  function makeAddKey(backend2, displayName) {
25624
25910
  return async function addKey(_globalOpts, opts) {
25625
25911
  await requireWriteDb2();
25626
- const rl2 = createInterface8({ input: process.stdin, output: process.stdout });
25912
+ const rl2 = createInterface9({ input: process.stdin, output: process.stdout });
25627
25913
  const ask2 = (q) => new Promise((r) => rl2.question(q, r));
25628
25914
  const key = await ask2(`Paste your ${displayName} API key: `);
25629
25915
  rl2.close();
@@ -27838,7 +28124,7 @@ var tui_exports = {};
27838
28124
  __export(tui_exports, {
27839
28125
  tuiCommand: () => tuiCommand
27840
28126
  });
27841
- import { createInterface as createInterface9 } from "readline";
28127
+ import { createInterface as createInterface10 } from "readline";
27842
28128
  import pc2 from "picocolors";
27843
28129
  async function tuiCommand(globalOpts, cmdOpts) {
27844
28130
  const { isDaemonRunning: isDaemonRunning2, apiPost: apiPost2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
@@ -27848,7 +28134,7 @@ async function tuiCommand(globalOpts, cmdOpts) {
27848
28134
  }
27849
28135
  const chatId = resolveChatId(globalOpts);
27850
28136
  const { chatSend: chatSend2 } = await Promise.resolve().then(() => (init_chat2(), chat_exports2));
27851
- const rl2 = createInterface9({
28137
+ const rl2 = createInterface10({
27852
28138
  input: process.stdin,
27853
28139
  output: process.stdout,
27854
28140
  prompt: pc2.cyan("you > "),
@@ -28649,7 +28935,7 @@ var init_optimize2 = __esm({
28649
28935
  var setup_exports = {};
28650
28936
  import { existsSync as existsSync55, writeFileSync as writeFileSync12, readFileSync as readFileSync26, copyFileSync as copyFileSync4, mkdirSync as mkdirSync18, statSync as statSync12 } from "fs";
28651
28937
  import { execFileSync as execFileSync5 } from "child_process";
28652
- import { createInterface as createInterface10 } from "readline";
28938
+ import { createInterface as createInterface11 } from "readline";
28653
28939
  import { join as join34 } from "path";
28654
28940
  function divider2() {
28655
28941
  console.log(dim("\u2500".repeat(55)));
@@ -29012,7 +29298,7 @@ var init_setup = __esm({
29012
29298
  "src/setup.ts"() {
29013
29299
  "use strict";
29014
29300
  init_paths();
29015
- rl = createInterface10({ input: process.stdin, output: process.stdout });
29301
+ rl = createInterface11({ input: process.stdin, output: process.stdout });
29016
29302
  ask = (q) => new Promise((resolve) => rl.question(q, resolve));
29017
29303
  bold = (s) => `\x1B[1m${s}\x1B[0m`;
29018
29304
  green = (s) => `\x1B[32m${s}\x1B[0m`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-claw",
3
- "version": "0.18.2",
3
+ "version": "0.18.3",
4
4
  "description": "CC-Claw: Personal AI assistant on Telegram — multi-backend (Claude, Gemini, Codex, Cursor), sub-agent orchestration, MCP management",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",