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.
- package/dist/cli.js +333 -47
- 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.
|
|
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
|
|
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
|
-
|
|
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
|
|
10960
|
-
import { createInterface as
|
|
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 =
|
|
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 =
|
|
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
|
|
17028
|
-
import { createInterface as
|
|
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
|
|
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 =
|
|
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 =
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
17791
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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