cc-claw 0.20.12 → 0.20.14

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 +1047 -479
  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.20.12" : (() => {
36
+ VERSION = true ? "0.20.14" : (() => {
37
37
  try {
38
38
  return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
39
39
  } catch {
@@ -827,8 +827,8 @@ function cleanupBackupFiles(ccClawHome, retentionDays = 7) {
827
827
  if (!file.includes(".bak.")) continue;
828
828
  const fullPath = join3(dir, file);
829
829
  try {
830
- const stat3 = statSync(fullPath);
831
- if (stat3.mtimeMs < cutoffMs) unlinkSync(fullPath);
830
+ const stat4 = statSync(fullPath);
831
+ if (stat4.mtimeMs < cutoffMs) unlinkSync(fullPath);
832
832
  } catch {
833
833
  }
834
834
  }
@@ -2722,7 +2722,7 @@ function getSkillSuggestionsEnabled(chatId) {
2722
2722
  const row = getDb().prepare(
2723
2723
  "SELECT value FROM chat_skill_suggestions WHERE chat_id = ?"
2724
2724
  ).get(SKILL_SUGGESTIONS_GLOBAL_KEY);
2725
- return (row?.value ?? 1) === 1;
2725
+ return (row?.value ?? 0) === 1;
2726
2726
  }
2727
2727
  function setSkillSuggestionsEnabled(chatIdOrEnabled, enabled) {
2728
2728
  const value = typeof chatIdOrEnabled === "boolean" ? chatIdOrEnabled : enabled;
@@ -7501,8 +7501,21 @@ var init_loader = __esm({
7501
7501
  function appendTextChunk(accumulated, chunk) {
7502
7502
  if (!accumulated) return chunk;
7503
7503
  if (!chunk) return accumulated;
7504
- const needsSpace = !/\s$/.test(accumulated) && !/^\s/.test(chunk);
7505
- return needsSpace ? accumulated + " " + chunk : accumulated + chunk;
7504
+ if (/\s$/.test(accumulated) || /^\s/.test(chunk)) {
7505
+ return accumulated + chunk;
7506
+ }
7507
+ const tailChar = accumulated[accumulated.length - 1];
7508
+ if ("/:@.=?&#%-_~+".includes(tailChar)) {
7509
+ return accumulated + chunk;
7510
+ }
7511
+ const headChar = chunk[0];
7512
+ if (`/:@.=?&#%-_~+,;)]}>'"`.includes(headChar)) {
7513
+ return accumulated + chunk;
7514
+ }
7515
+ if (/\d$/.test(accumulated) && /^\d/.test(chunk)) {
7516
+ return accumulated + chunk;
7517
+ }
7518
+ return accumulated + " " + chunk;
7506
7519
  }
7507
7520
  var init_text_utils = __esm({
7508
7521
  "src/text-utils.ts"() {
@@ -8075,8 +8088,11 @@ function revokeSubAgentToken(agentId) {
8075
8088
  function authenticateRequest(req, url) {
8076
8089
  const authHeader = req.headers.authorization ?? "";
8077
8090
  const bearerToken = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : "";
8078
- const isMainToken = bearerToken === DASHBOARD_TOKEN;
8079
- const subEntry = subAgentTokens.get(bearerToken);
8091
+ const queryToken = url.searchParams.get("token") ?? "";
8092
+ const isBrowserRoute = BROWSER_ROUTES.has(url.pathname) || url.pathname.startsWith("/files");
8093
+ const effectiveToken = bearerToken || (isBrowserRoute ? queryToken : "");
8094
+ const isMainToken = effectiveToken === DASHBOARD_TOKEN;
8095
+ const subEntry = subAgentTokens.get(effectiveToken);
8080
8096
  const isSubAgentToken = !!subEntry && subEntry.expiresAt > Date.now();
8081
8097
  if (!isMainToken && !isSubAgentToken) {
8082
8098
  return { authenticated: false, isSubAgent: false };
@@ -8123,7 +8139,7 @@ function validateAgentIdentity(req, body) {
8123
8139
  throw new Error(`IDENTITY_MISMATCH: Token bound to agent ${boundAgentId}, but request claims ${callerAgentId}`);
8124
8140
  }
8125
8141
  }
8126
- var PORT, DASHBOARD_TOKEN, SUB_AGENT_TOKEN_TTL_MS, subAgentTokens, SUB_AGENT_ALLOWED_PATHS, MAX_BODY_BYTES;
8142
+ var PORT, DASHBOARD_TOKEN, SUB_AGENT_TOKEN_TTL_MS, subAgentTokens, SUB_AGENT_ALLOWED_PATHS, BROWSER_ROUTES, MAX_BODY_BYTES;
8127
8143
  var init_middleware = __esm({
8128
8144
  "src/dashboard/middleware.ts"() {
8129
8145
  "use strict";
@@ -8159,6 +8175,7 @@ var init_middleware = __esm({
8159
8175
  "/api/memory/history",
8160
8176
  "/api/memory/summaries"
8161
8177
  ]);
8178
+ BROWSER_ROUTES = /* @__PURE__ */ new Set(["/", "/index.html", "/upload"]);
8162
8179
  MAX_BODY_BYTES = 1048576;
8163
8180
  }
8164
8181
  });
@@ -9471,8 +9488,8 @@ function pruneAgentLogs() {
9471
9488
  mkdirSync6(AGENTS_PATH, { recursive: true });
9472
9489
  const files = readdirSync6(AGENTS_PATH).filter((f) => f.endsWith(".log")).map((f) => {
9473
9490
  const fullPath = join11(AGENTS_PATH, f);
9474
- const stat3 = statSync4(fullPath);
9475
- return { path: fullPath, mtime: stat3.mtimeMs, size: stat3.size };
9491
+ const stat4 = statSync4(fullPath);
9492
+ return { path: fullPath, mtime: stat4.mtimeMs, size: stat4.size };
9476
9493
  });
9477
9494
  const now = Date.now();
9478
9495
  const remaining = files.filter((f) => {
@@ -12720,12 +12737,12 @@ var init_evolve = __esm({
12720
12737
  const body = JSON.parse(await readBody(req));
12721
12738
  const { setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
12722
12739
  const { existsSync: fileExists, readFileSync: fileRead } = await import("fs");
12723
- const { join: join36 } = await import("path");
12740
+ const { join: join37 } = await import("path");
12724
12741
  const { CC_CLAW_HOME: home } = await Promise.resolve().then(() => (init_paths(), paths_exports));
12725
12742
  const chatId = resolveChatId(body);
12726
12743
  if (!chatId) return jsonResponse(res, { error: "No chatId provided and ALLOWED_CHAT_ID is not set" }, 400);
12727
- const soulPath = join36(home, "identity/SOUL.md");
12728
- const userPath = join36(home, "identity/USER.md");
12744
+ const soulPath = join37(home, "identity/SOUL.md");
12745
+ const userPath = join37(home, "identity/USER.md");
12729
12746
  const soul = fileExists(soulPath) ? fileRead(soulPath, "utf-8") : "";
12730
12747
  const user = fileExists(userPath) ? fileRead(userPath, "utf-8") : "";
12731
12748
  setReflectionStatus2(getDb(), chatId, "active", soul, user);
@@ -12777,6 +12794,346 @@ var init_evolve = __esm({
12777
12794
  }
12778
12795
  });
12779
12796
 
12797
+ // src/dashboard/routes/files.ts
12798
+ import { createWriteStream, existsSync as existsSync14 } from "fs";
12799
+ import { readdir, stat, unlink, mkdir } from "fs/promises";
12800
+ import { join as join14, extname } from "path";
12801
+ function getUploadHtml(token, host) {
12802
+ return `<!DOCTYPE html>
12803
+ <html lang="en">
12804
+ <head>
12805
+ <meta charset="utf-8">
12806
+ <meta name="viewport" content="width=device-width, initial-scale=1">
12807
+ <title>CC-Claw File Upload</title>
12808
+ <style>
12809
+ * { margin: 0; padding: 0; box-sizing: border-box; }
12810
+ body { font-family: -apple-system, system-ui, sans-serif; background: #0d1117; color: #e6edf3; display: flex; align-items: center; justify-content: center; min-height: 100vh; }
12811
+ .container { max-width: 520px; width: 100%; padding: 24px; }
12812
+ h1 { font-size: 1.4rem; margin-bottom: 8px; }
12813
+ .subtitle { color: #8b949e; margin-bottom: 24px; font-size: 0.9rem; }
12814
+ .drop-zone { border: 2px dashed #30363d; border-radius: 12px; padding: 48px 24px; text-align: center; cursor: pointer; transition: all 0.2s; }
12815
+ .drop-zone:hover, .drop-zone.drag-over { border-color: #58a6ff; background: rgba(88,166,255,0.05); }
12816
+ .drop-zone p { color: #8b949e; margin-top: 8px; font-size: 0.85rem; }
12817
+ .drop-zone .icon { font-size: 2.5rem; margin-bottom: 8px; }
12818
+ input[type="file"] { display: none; }
12819
+ .progress-wrap { margin-top: 16px; display: none; }
12820
+ .progress-bar { height: 6px; background: #21262d; border-radius: 3px; overflow: hidden; }
12821
+ .progress-fill { height: 100%; width: 0%; background: #58a6ff; transition: width 0.3s; }
12822
+ .progress-text { font-size: 0.8rem; color: #8b949e; margin-top: 6px; text-align: center; }
12823
+ .result { margin-top: 16px; padding: 12px; border-radius: 8px; font-size: 0.85rem; display: none; }
12824
+ .result.success { background: rgba(63,185,80,0.1); border: 1px solid #238636; color: #3fb950; }
12825
+ .result.error { background: rgba(248,81,73,0.1); border: 1px solid #da3633; color: #f85149; }
12826
+ .file-link { color: #58a6ff; text-decoration: none; word-break: break-all; }
12827
+ .file-link:hover { text-decoration: underline; }
12828
+ .files-list { margin-top: 24px; }
12829
+ .files-list h3 { font-size: 0.9rem; color: #8b949e; margin-bottom: 8px; }
12830
+ .files-list ul { list-style: none; }
12831
+ .files-list li { padding: 6px 0; border-bottom: 1px solid #21262d; font-size: 0.85rem; }
12832
+ .files-list li:last-child { border: none; }
12833
+ </style>
12834
+ </head>
12835
+ <body>
12836
+ <div class="container">
12837
+ <h1>CC-Claw Upload</h1>
12838
+ <p class="subtitle">Upload files for CC-Claw to use. Max 200MB.</p>
12839
+ <div class="drop-zone" id="dropZone">
12840
+ <div class="icon">\u{1F4C1}</div>
12841
+ <strong>Drop file here or click to browse</strong>
12842
+ <p>Videos, images, documents \u2014 anything the bot needs</p>
12843
+ </div>
12844
+ <input type="file" id="fileInput">
12845
+ <div class="progress-wrap" id="progressWrap">
12846
+ <div class="progress-bar"><div class="progress-fill" id="progressFill"></div></div>
12847
+ <div class="progress-text" id="progressText">Uploading...</div>
12848
+ </div>
12849
+ <div class="result" id="result"></div>
12850
+ <div class="files-list" id="filesList"></div>
12851
+ </div>
12852
+ <script>
12853
+ const TOKEN = "${token}";
12854
+ const HOST = "${host}";
12855
+ const dropZone = document.getElementById("dropZone");
12856
+ const fileInput = document.getElementById("fileInput");
12857
+ const progressWrap = document.getElementById("progressWrap");
12858
+ const progressFill = document.getElementById("progressFill");
12859
+ const progressText = document.getElementById("progressText");
12860
+ const resultDiv = document.getElementById("result");
12861
+
12862
+ dropZone.addEventListener("click", () => fileInput.click());
12863
+ dropZone.addEventListener("dragover", e => { e.preventDefault(); dropZone.classList.add("drag-over"); });
12864
+ dropZone.addEventListener("dragleave", () => dropZone.classList.remove("drag-over"));
12865
+ dropZone.addEventListener("drop", e => { e.preventDefault(); dropZone.classList.remove("drag-over"); if (e.dataTransfer.files.length) uploadFile(e.dataTransfer.files[0]); });
12866
+ fileInput.addEventListener("change", () => { if (fileInput.files.length) uploadFile(fileInput.files[0]); });
12867
+
12868
+ function uploadFile(file) {
12869
+ if (file.size > 200 * 1024 * 1024) { showResult("File too large (max 200MB)", true); return; }
12870
+ progressWrap.style.display = "block";
12871
+ resultDiv.style.display = "none";
12872
+ progressFill.style.width = "0%";
12873
+ progressText.textContent = "Uploading " + file.name + "...";
12874
+
12875
+ const xhr = new XMLHttpRequest();
12876
+ xhr.open("POST", HOST + "/upload");
12877
+ xhr.setRequestHeader("Authorization", "Bearer " + TOKEN);
12878
+
12879
+ xhr.upload.addEventListener("progress", e => {
12880
+ if (e.lengthComputable) {
12881
+ const pct = Math.round(e.loaded / e.total * 100);
12882
+ progressFill.style.width = pct + "%";
12883
+ progressText.textContent = pct + "% \u2014 " + (e.loaded / 1024 / 1024).toFixed(1) + " / " + (e.total / 1024 / 1024).toFixed(1) + " MB";
12884
+ }
12885
+ });
12886
+ xhr.addEventListener("load", () => {
12887
+ progressWrap.style.display = "none";
12888
+ try {
12889
+ const resp = JSON.parse(xhr.responseText);
12890
+ if (resp.ok && resp.data) {
12891
+ const link = HOST + "/files/" + resp.data.filename;
12892
+ showResult('Uploaded: <a class="file-link" href="' + link + '" target="_blank">' + resp.data.filename + '</a><br>Size: ' + resp.data.sizeMB + ' MB<br>Path: ' + resp.data.path, false);
12893
+ loadFileList();
12894
+ } else {
12895
+ showResult(resp.error || "Upload failed", true);
12896
+ }
12897
+ } catch { showResult("Upload failed: " + xhr.statusText, true); }
12898
+ });
12899
+ xhr.addEventListener("error", () => { progressWrap.style.display = "none"; showResult("Connection failed", true); });
12900
+ xhr.addEventListener("abort", () => { progressWrap.style.display = "none"; showResult("Upload cancelled", true); });
12901
+
12902
+ const formData = new FormData();
12903
+ formData.append("file", file);
12904
+ xhr.send(formData);
12905
+ }
12906
+
12907
+ function showResult(html, isError) {
12908
+ resultDiv.innerHTML = html;
12909
+ resultDiv.className = "result " + (isError ? "error" : "success");
12910
+ resultDiv.style.display = "block";
12911
+ }
12912
+
12913
+ function loadFileList() {
12914
+ fetch(HOST + "/files", { headers: { "Authorization": "Bearer " + TOKEN } })
12915
+ .then(r => r.json()).then(resp => {
12916
+ if (!resp.ok || !resp.data?.length) { document.getElementById("filesList").innerHTML = ""; return; }
12917
+ const items = resp.data.slice(0, 20).map(f =>
12918
+ '<li><a class="file-link" href="' + HOST + '/files/' + f.name + '" target="_blank">' + f.name + '</a> (' + f.sizeMB + ' MB)</li>'
12919
+ ).join("");
12920
+ document.getElementById("filesList").innerHTML = "<h3>Recent Files</h3><ul>" + items + "</ul>";
12921
+ }).catch(() => {});
12922
+ }
12923
+ loadFileList();
12924
+ </script>
12925
+ </body>
12926
+ </html>`;
12927
+ }
12928
+ function handleUploadPage(req, res, _url, token) {
12929
+ const host = `http://${req.headers.host ?? "localhost:3141"}`;
12930
+ htmlResponse(res, getUploadHtml(token, host));
12931
+ }
12932
+ async function handleUpload(req, res) {
12933
+ const contentType = req.headers["content-type"] ?? "";
12934
+ const boundaryMatch = contentType.match(/boundary=(.+?)(?:;|$)/);
12935
+ if (!boundaryMatch) {
12936
+ return jsonResponse(res, { ok: false, error: "Missing multipart boundary" }, 400);
12937
+ }
12938
+ const boundary = boundaryMatch[1];
12939
+ const contentLength = parseInt(req.headers["content-length"] ?? "0", 10);
12940
+ if (contentLength > MAX_UPLOAD_BYTES) {
12941
+ return jsonResponse(res, { ok: false, error: `File too large (max ${MAX_UPLOAD_BYTES / 1024 / 1024}MB)` }, 413);
12942
+ }
12943
+ await mkdir(INCOMING_PATH, { recursive: true });
12944
+ try {
12945
+ const result = await parseMultipartUpload(req, boundary);
12946
+ if (!result) {
12947
+ return jsonResponse(res, { ok: false, error: "No file found in upload" }, 400);
12948
+ }
12949
+ const ext = extname(result.originalName).toLowerCase();
12950
+ if (BLOCKED_EXTENSIONS.has(ext)) {
12951
+ try {
12952
+ await unlink(result.path);
12953
+ } catch {
12954
+ }
12955
+ return jsonResponse(res, { ok: false, error: `File type ${ext} is not allowed` }, 400);
12956
+ }
12957
+ const fileInfo = await stat(result.path);
12958
+ const sizeMB = (fileInfo.size / 1024 / 1024).toFixed(1);
12959
+ log(`[upload] Saved ${result.filename} (${sizeMB} MB) from ${result.originalName}`);
12960
+ return jsonResponse(res, {
12961
+ ok: true,
12962
+ data: {
12963
+ filename: result.filename,
12964
+ originalName: result.originalName,
12965
+ path: result.path,
12966
+ sizeMB
12967
+ }
12968
+ });
12969
+ } catch (err) {
12970
+ warn(`[upload] Failed: ${err.message}`);
12971
+ return jsonResponse(res, { ok: false, error: `Upload failed: ${err.message}` }, 500);
12972
+ }
12973
+ }
12974
+ async function handleFileList(req, res) {
12975
+ await mkdir(INCOMING_PATH, { recursive: true });
12976
+ try {
12977
+ const entries = await readdir(INCOMING_PATH);
12978
+ const files = [];
12979
+ for (const name of entries) {
12980
+ try {
12981
+ const s = await stat(join14(INCOMING_PATH, name));
12982
+ if (!s.isFile()) continue;
12983
+ files.push({
12984
+ name,
12985
+ sizeMB: (s.size / 1024 / 1024).toFixed(1),
12986
+ modified: s.mtime.toISOString()
12987
+ });
12988
+ } catch {
12989
+ continue;
12990
+ }
12991
+ }
12992
+ files.sort((a, b) => b.modified.localeCompare(a.modified));
12993
+ return jsonResponse(res, { ok: true, data: files });
12994
+ } catch {
12995
+ return jsonResponse(res, { ok: true, data: [] });
12996
+ }
12997
+ }
12998
+ async function handleFileServe(req, res, url) {
12999
+ const filename = decodeURIComponent(url.pathname.slice("/files/".length));
13000
+ if (filename.includes("..") || filename.includes("/") || filename.includes("\\")) {
13001
+ res.writeHead(400, { "Content-Type": "text/plain" });
13002
+ res.end("Invalid filename");
13003
+ return;
13004
+ }
13005
+ const filePath = join14(INCOMING_PATH, filename);
13006
+ if (!existsSync14(filePath)) {
13007
+ res.writeHead(404, { "Content-Type": "text/plain" });
13008
+ res.end("File not found");
13009
+ return;
13010
+ }
13011
+ const fileInfo = await stat(filePath);
13012
+ const ext = extname(filename).toLowerCase();
13013
+ const mimeTypes = {
13014
+ ".mp4": "video/mp4",
13015
+ ".mov": "video/quicktime",
13016
+ ".avi": "video/x-msvideo",
13017
+ ".webm": "video/webm",
13018
+ ".jpg": "image/jpeg",
13019
+ ".jpeg": "image/jpeg",
13020
+ ".png": "image/png",
13021
+ ".gif": "image/gif",
13022
+ ".webp": "image/webp",
13023
+ ".pdf": "application/pdf",
13024
+ ".txt": "text/plain",
13025
+ ".json": "application/json",
13026
+ ".mp3": "audio/mpeg",
13027
+ ".wav": "audio/wav",
13028
+ ".ogg": "audio/ogg"
13029
+ };
13030
+ const mime = mimeTypes[ext] ?? "application/octet-stream";
13031
+ res.writeHead(200, {
13032
+ "Content-Type": mime,
13033
+ "Content-Length": fileInfo.size,
13034
+ "Content-Disposition": `inline; filename="${filename}"`,
13035
+ "Cache-Control": "public, max-age=3600"
13036
+ });
13037
+ const { createReadStream: createReadStream2 } = await import("fs");
13038
+ const stream = createReadStream2(filePath);
13039
+ stream.pipe(res);
13040
+ stream.on("error", () => {
13041
+ if (!res.headersSent) {
13042
+ res.writeHead(500);
13043
+ }
13044
+ res.end();
13045
+ });
13046
+ }
13047
+ function parseMultipartUpload(req, boundary) {
13048
+ return new Promise((resolve, reject) => {
13049
+ const chunks = [];
13050
+ let totalBytes = 0;
13051
+ let cleanupPath = null;
13052
+ req.on("data", (chunk) => {
13053
+ totalBytes += chunk.length;
13054
+ if (totalBytes > MAX_UPLOAD_BYTES) {
13055
+ req.destroy(new Error(`Upload exceeds ${MAX_UPLOAD_BYTES / 1024 / 1024}MB limit`));
13056
+ return;
13057
+ }
13058
+ chunks.push(chunk);
13059
+ });
13060
+ req.on("error", async (err) => {
13061
+ if (cleanupPath) {
13062
+ try {
13063
+ await unlink(cleanupPath);
13064
+ } catch {
13065
+ }
13066
+ }
13067
+ reject(err);
13068
+ });
13069
+ req.on("end", async () => {
13070
+ try {
13071
+ const body = Buffer.concat(chunks);
13072
+ const boundaryBuf = Buffer.from(`--${boundary}`);
13073
+ const headerEnd = body.indexOf(Buffer.from("\r\n\r\n"));
13074
+ if (headerEnd === -1) return resolve(null);
13075
+ const headerSection = body.subarray(0, headerEnd).toString("utf-8");
13076
+ const nameMatch = headerSection.match(/filename="(.+?)"/);
13077
+ if (!nameMatch) return resolve(null);
13078
+ const originalName = nameMatch[1].replace(/[^\w.-]/g, "_");
13079
+ const ext = extname(originalName) || ".bin";
13080
+ const filename = `upload-${Date.now()}${ext}`;
13081
+ const filePath = join14(INCOMING_PATH, filename);
13082
+ cleanupPath = filePath;
13083
+ const fileStart = headerEnd + 4;
13084
+ const closingBoundary = Buffer.from(`\r
13085
+ --${boundary}`);
13086
+ let fileEnd = body.indexOf(closingBoundary, fileStart);
13087
+ if (fileEnd === -1) fileEnd = body.length;
13088
+ const fileContent = body.subarray(fileStart, fileEnd);
13089
+ const ws = createWriteStream(filePath);
13090
+ await new Promise((res, rej) => {
13091
+ ws.write(fileContent, (err) => {
13092
+ if (err) return rej(err);
13093
+ ws.end(res);
13094
+ });
13095
+ });
13096
+ cleanupPath = null;
13097
+ resolve({ filename, originalName, path: filePath });
13098
+ } catch (err) {
13099
+ if (cleanupPath) {
13100
+ try {
13101
+ await unlink(cleanupPath);
13102
+ } catch {
13103
+ }
13104
+ }
13105
+ reject(err);
13106
+ }
13107
+ });
13108
+ });
13109
+ }
13110
+ var INCOMING_PATH, MAX_UPLOAD_BYTES, BLOCKED_EXTENSIONS;
13111
+ var init_files = __esm({
13112
+ "src/dashboard/routes/files.ts"() {
13113
+ "use strict";
13114
+ init_paths();
13115
+ init_middleware();
13116
+ init_log();
13117
+ INCOMING_PATH = join14(MEDIA_PATH, "incoming");
13118
+ MAX_UPLOAD_BYTES = 200 * 1024 * 1024;
13119
+ BLOCKED_EXTENSIONS = /* @__PURE__ */ new Set([
13120
+ ".exe",
13121
+ ".bat",
13122
+ ".cmd",
13123
+ ".com",
13124
+ ".scr",
13125
+ ".pif",
13126
+ ".msi",
13127
+ ".sh",
13128
+ ".bash",
13129
+ ".csh",
13130
+ ".ksh",
13131
+ ".ps1",
13132
+ ".psm1"
13133
+ ]);
13134
+ }
13135
+ });
13136
+
12780
13137
  // src/dashboard/server.ts
12781
13138
  var server_exports = {};
12782
13139
  __export(server_exports, {
@@ -12876,6 +13233,16 @@ function startDashboard() {
12876
13233
  if (url.pathname === "/api/evolve/off" && req.method === "POST") return handleEvolveOff(req, res, url);
12877
13234
  if (url.pathname === "/api/evolve/model" && req.method === "POST") return handleEvolveModel(req, res, url);
12878
13235
  if (url.pathname === "/api/evolve/settings" && req.method === "POST") return handleEvolveSettings(req, res, url);
13236
+ if (url.pathname === "/upload" && req.method === "GET") return handleUploadPage(req, res, url, getDashboardToken());
13237
+ if (url.pathname === "/upload" && req.method === "POST") {
13238
+ await handleUpload(req, res);
13239
+ return;
13240
+ }
13241
+ if (url.pathname === "/files" && req.method === "GET") return handleFileList(req, res);
13242
+ if (url.pathname.startsWith("/files/") && req.method === "GET") {
13243
+ await handleFileServe(req, res, url);
13244
+ return;
13245
+ }
12879
13246
  if (url.pathname === "/" || url.pathname === "/index.html") {
12880
13247
  return htmlResponse(res, DASHBOARD_HTML.replace("__TOKEN__", getDashboardToken()));
12881
13248
  }
@@ -12891,7 +13258,8 @@ function startDashboard() {
12891
13258
  console.error(`[api] Server error:`, err);
12892
13259
  }
12893
13260
  });
12894
- server.listen(PORT, "127.0.0.1");
13261
+ const bindHost = process.env.CC_CLAW_DASHBOARD_HOST ?? "0.0.0.0";
13262
+ server.listen(PORT, bindHost);
12895
13263
  persistToken();
12896
13264
  return server;
12897
13265
  }
@@ -12908,6 +13276,7 @@ var init_server = __esm({
12908
13276
  init_scheduler();
12909
13277
  init_config();
12910
13278
  init_evolve();
13279
+ init_files();
12911
13280
  }
12912
13281
  });
12913
13282
 
@@ -14779,10 +15148,10 @@ var init_telegram_throttle = __esm({
14779
15148
  });
14780
15149
 
14781
15150
  // src/health/checks.ts
14782
- import { existsSync as existsSync14, statSync as statSync6, readFileSync as readFileSync8 } from "fs";
15151
+ import { existsSync as existsSync15, statSync as statSync6, readFileSync as readFileSync8 } from "fs";
14783
15152
  import { execFileSync, execSync as execSync3 } from "child_process";
14784
15153
  function getRecentErrors() {
14785
- if (!existsSync14(ERROR_LOG_PATH)) return null;
15154
+ if (!existsSync15(ERROR_LOG_PATH)) return null;
14786
15155
  const logContent = readFileSync8(ERROR_LOG_PATH, "utf-8");
14787
15156
  const allLines = logContent.split("\n").filter(Boolean).slice(-500);
14788
15157
  const last24h = Date.now() - 864e5;
@@ -14804,7 +15173,7 @@ function getRecentErrors() {
14804
15173
  }
14805
15174
  function checkDatabase() {
14806
15175
  const checks = [];
14807
- if (existsSync14(DB_PATH)) {
15176
+ if (existsSync15(DB_PATH)) {
14808
15177
  const size = statSync6(DB_PATH).size;
14809
15178
  checks.push({ name: "Database", status: "ok", message: `${(size / 1024).toFixed(0)}KB` });
14810
15179
  } else {
@@ -14836,18 +15205,18 @@ function checkErrorLog() {
14836
15205
  return checks;
14837
15206
  }
14838
15207
  if (errors.rate429.length > 10) {
14839
- checks.push({ name: "Rate limits", status: "error", message: `${errors.rate429.length} rate-limit (429) errors in last 24h` });
15208
+ checks.push({ name: "Rate limits", status: "error", message: `${errors.rate429.length} rate-limit (429) errors in last 24h`, fix: "cc-claw doctor errors \u2014 view details; cc-claw doctor --fix \u2014 clear log" });
14840
15209
  } else if (errors.rate429.length > 0) {
14841
- checks.push({ name: "Rate limits", status: "warning", message: `${errors.rate429.length} rate-limit errors in last 24h` });
15210
+ checks.push({ name: "Rate limits", status: "warning", message: `${errors.rate429.length} rate-limit errors in last 24h`, fix: "cc-claw doctor errors \u2014 view details; cc-claw doctor --fix \u2014 clear log" });
14842
15211
  }
14843
15212
  if (errors.contentSilence.length > 0) {
14844
- checks.push({ name: "Content silence", status: "warning", message: `${errors.contentSilence.length} silence timeout(s) in last 24h` });
15213
+ checks.push({ name: "Content silence", status: "warning", message: `${errors.contentSilence.length} silence timeout(s) in last 24h`, fix: "cc-claw doctor errors \u2014 view details; cc-claw service restart \u2014 if persistent" });
14845
15214
  }
14846
15215
  if (errors.spawnTimeout.length > 0) {
14847
- checks.push({ name: "Spawn timeouts", status: "warning", message: `${errors.spawnTimeout.length} backend timeout(s) in last 24h` });
15216
+ checks.push({ name: "Spawn timeouts", status: "warning", message: `${errors.spawnTimeout.length} backend timeout(s) in last 24h`, fix: "cc-claw doctor errors \u2014 view details; cc-claw status \u2014 check backends" });
14848
15217
  }
14849
15218
  if (errors.other.length > 0) {
14850
- checks.push({ name: "Other errors", status: "warning", message: `${errors.other.length} error(s) in last 24h` });
15219
+ checks.push({ name: "Other errors", status: "warning", message: `${errors.other.length} error(s) in last 24h`, fix: "cc-claw doctor errors \u2014 view details; cc-claw doctor --fix \u2014 clear log" });
14851
15220
  }
14852
15221
  if (checks.length === 0) {
14853
15222
  checks.push({ name: "Recent errors", status: "ok", message: "none in last 24h" });
@@ -15012,8 +15381,8 @@ __export(heartbeat_exports, {
15012
15381
  stopHeartbeatForChat: () => stopHeartbeatForChat,
15013
15382
  updateHeartbeatConfig: () => updateHeartbeatConfig
15014
15383
  });
15015
- import { readFileSync as readFileSync9, existsSync as existsSync15 } from "fs";
15016
- import { join as join14 } from "path";
15384
+ import { readFileSync as readFileSync9, existsSync as existsSync16 } from "fs";
15385
+ import { join as join15 } from "path";
15017
15386
  function findHeartbeatJob() {
15018
15387
  try {
15019
15388
  const { getDb: getDb2 } = (init_store5(), __toCommonJS(store_exports5));
@@ -15144,7 +15513,7 @@ ${healthText}`);
15144
15513
  sections.push(`[Active Watches \u2014 execute these checks using your tools]
15145
15514
  ${watchLines.join("\n")}`);
15146
15515
  }
15147
- if (existsSync15(HEARTBEAT_MD_PATH)) {
15516
+ if (existsSync16(HEARTBEAT_MD_PATH)) {
15148
15517
  try {
15149
15518
  const custom = readFileSync9(HEARTBEAT_MD_PATH, "utf-8").trim();
15150
15519
  if (custom) {
@@ -15206,7 +15575,7 @@ var init_heartbeat2 = __esm({
15206
15575
  init_store5();
15207
15576
  init_checks();
15208
15577
  init_log();
15209
- HEARTBEAT_MD_PATH = join14(WORKSPACE_PATH, "HEARTBEAT.md");
15578
+ HEARTBEAT_MD_PATH = join15(WORKSPACE_PATH, "HEARTBEAT.md");
15210
15579
  HEARTBEAT_OK = "HEARTBEAT_OK";
15211
15580
  HEARTBEAT_JOB_TYPE = "heartbeat";
15212
15581
  DEFAULT_INTERVAL_MS = 30 * 60 * 1e3;
@@ -15215,8 +15584,8 @@ var init_heartbeat2 = __esm({
15215
15584
  });
15216
15585
 
15217
15586
  // src/bootstrap/profile.ts
15218
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync16 } from "fs";
15219
- import { join as join15 } from "path";
15587
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync17 } from "fs";
15588
+ import { join as join16 } from "path";
15220
15589
  function hasActiveProfile(chatId) {
15221
15590
  return activeProfiles.has(chatId);
15222
15591
  }
@@ -15345,7 +15714,7 @@ function extractUserUpdates(text) {
15345
15714
  return { cleanText, updates };
15346
15715
  }
15347
15716
  function appendToUserProfile(key, value) {
15348
- if (!existsSync16(USER_PATH2)) return;
15717
+ if (!existsSync17(USER_PATH2)) return;
15349
15718
  const content = readFileSync10(USER_PATH2, "utf-8");
15350
15719
  const line = `- **${key}**: ${value}`;
15351
15720
  if (content.includes(line)) return;
@@ -15361,7 +15730,7 @@ var init_profile = __esm({
15361
15730
  "use strict";
15362
15731
  init_paths();
15363
15732
  init_log();
15364
- USER_PATH2 = join15(IDENTITY_PATH, "USER.md");
15733
+ USER_PATH2 = join16(IDENTITY_PATH, "USER.md");
15365
15734
  activeProfiles = /* @__PURE__ */ new Map();
15366
15735
  }
15367
15736
  });
@@ -16658,8 +17027,8 @@ __export(session_log_exports2, {
16658
17027
  startSessionLogCleanupTimer: () => startSessionLogCleanupTimer,
16659
17028
  tailSessionLog: () => tailSessionLog
16660
17029
  });
16661
- import { existsSync as existsSync17, mkdirSync as mkdirSync8, appendFileSync, readdirSync as readdirSync9, unlinkSync as unlinkSync6, statSync as statSync7, createReadStream } from "fs";
16662
- import { join as join16, basename } from "path";
17030
+ import { existsSync as existsSync18, mkdirSync as mkdirSync8, appendFileSync, readdirSync as readdirSync9, unlinkSync as unlinkSync6, statSync as statSync7, createReadStream } from "fs";
17031
+ import { join as join17, basename } from "path";
16663
17032
  import { createInterface as createInterface6 } from "readline";
16664
17033
  function getRetentionDays() {
16665
17034
  const env = process.env.SESSION_LOG_RETENTION_DAYS;
@@ -16671,13 +17040,13 @@ function getRetentionDays() {
16671
17040
  }
16672
17041
  function cleanupSessionLogs(retentionDays) {
16673
17042
  const days = retentionDays ?? getRetentionDays();
16674
- if (!existsSync17(SESSION_LOGS_PATH)) return 0;
17043
+ if (!existsSync18(SESSION_LOGS_PATH)) return 0;
16675
17044
  const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
16676
17045
  let cleaned = 0;
16677
17046
  try {
16678
17047
  for (const file of readdirSync9(SESSION_LOGS_PATH)) {
16679
17048
  if (!file.startsWith("session-") || !file.endsWith(".log")) continue;
16680
- const filePath = join16(SESSION_LOGS_PATH, file);
17049
+ const filePath = join17(SESSION_LOGS_PATH, file);
16681
17050
  try {
16682
17051
  const { mtimeMs } = statSync7(filePath);
16683
17052
  if (mtimeMs < cutoff) {
@@ -16703,21 +17072,21 @@ function startSessionLogCleanupTimer() {
16703
17072
  return timer;
16704
17073
  }
16705
17074
  function listSessionLogs() {
16706
- if (!existsSync17(SESSION_LOGS_PATH)) return [];
17075
+ if (!existsSync18(SESSION_LOGS_PATH)) return [];
16707
17076
  const logs = [];
16708
17077
  for (const file of readdirSync9(SESSION_LOGS_PATH)) {
16709
17078
  if (!file.startsWith("session-") || !file.endsWith(".log")) continue;
16710
- const filePath = join16(SESSION_LOGS_PATH, file);
17079
+ const filePath = join17(SESSION_LOGS_PATH, file);
16711
17080
  try {
16712
- const stat3 = statSync7(filePath);
17081
+ const stat4 = statSync7(filePath);
16713
17082
  const match = file.match(/^session-(.+?)-(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2})\.log$/);
16714
17083
  logs.push({
16715
17084
  filename: file,
16716
17085
  filePath,
16717
17086
  chatId: match?.[1] ?? "unknown",
16718
17087
  timestamp: match?.[2]?.replace(/-/g, (m, i) => i > 9 ? ":" : m) ?? "unknown",
16719
- sizeBytes: stat3.size,
16720
- modifiedAt: stat3.mtime
17088
+ sizeBytes: stat4.size,
17089
+ modifiedAt: stat4.mtime
16721
17090
  });
16722
17091
  } catch {
16723
17092
  }
@@ -16726,7 +17095,7 @@ function listSessionLogs() {
16726
17095
  return logs;
16727
17096
  }
16728
17097
  async function* tailSessionLog(filePath, lines = 50) {
16729
- if (!existsSync17(filePath)) {
17098
+ if (!existsSync18(filePath)) {
16730
17099
  yield `File not found: ${filePath}`;
16731
17100
  return;
16732
17101
  }
@@ -16754,12 +17123,12 @@ var init_session_log2 = __esm({
16754
17123
  constructor(chatId, backend2, model2) {
16755
17124
  this.backend = backend2;
16756
17125
  this.model = model2;
16757
- if (!existsSync17(SESSION_LOGS_PATH)) {
17126
+ if (!existsSync18(SESSION_LOGS_PATH)) {
16758
17127
  mkdirSync8(SESSION_LOGS_PATH, { recursive: true });
16759
17128
  }
16760
17129
  const ts2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
16761
17130
  const sanitizedChatId = chatId.replace(/[^a-zA-Z0-9_-]/g, "_");
16762
- this.filePath = join16(SESSION_LOGS_PATH, `session-${sanitizedChatId}-${ts2}.log`);
17131
+ this.filePath = join17(SESSION_LOGS_PATH, `session-${sanitizedChatId}-${ts2}.log`);
16763
17132
  const header2 = [
16764
17133
  "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
16765
17134
  `CC-Claw Agent Session \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`,
@@ -17162,7 +17531,7 @@ var init_gate = __esm({
17162
17531
  // src/voice/stt.ts
17163
17532
  import crypto from "crypto";
17164
17533
  import { execFile as execFile2, execFileSync as execFileSync2 } from "child_process";
17165
- import { readFile as readFile2, unlink } from "fs/promises";
17534
+ import { readFile as readFile2, unlink as unlink2 } from "fs/promises";
17166
17535
  import { promisify as promisify2 } from "util";
17167
17536
  function ensureFfmpeg() {
17168
17537
  if (ffmpegAvailable === true) return;
@@ -17316,10 +17685,10 @@ async function mp3ToOgg(mp3Buffer) {
17316
17685
  await writeFile6(tmpMp3, mp3Buffer);
17317
17686
  await execFileAsync2("ffmpeg", ["-y", "-i", tmpMp3, "-c:a", "libopus", "-b:a", "64k", tmpOgg]);
17318
17687
  const oggBuffer = await readFile2(tmpOgg);
17319
- unlink(tmpMp3).catch((err) => {
17688
+ unlink2(tmpMp3).catch((err) => {
17320
17689
  error("[tts] cleanup failed:", err);
17321
17690
  });
17322
- unlink(tmpOgg).catch((err) => {
17691
+ unlink2(tmpOgg).catch((err) => {
17323
17692
  error("[tts] cleanup failed:", err);
17324
17693
  });
17325
17694
  return oggBuffer;
@@ -17332,10 +17701,10 @@ async function macOsTts(text, voice2 = "Samantha") {
17332
17701
  await execFileAsync2("say", ["-v", voice2, "-o", tmpAiff, text]);
17333
17702
  await execFileAsync2("ffmpeg", ["-y", "-i", tmpAiff, "-c:a", "libopus", "-b:a", "64k", tmpOgg]);
17334
17703
  const oggBuffer = await readFile2(tmpOgg);
17335
- unlink(tmpAiff).catch((err) => {
17704
+ unlink2(tmpAiff).catch((err) => {
17336
17705
  error("[tts] cleanup failed:", err);
17337
17706
  });
17338
- unlink(tmpOgg).catch((err) => {
17707
+ unlink2(tmpOgg).catch((err) => {
17339
17708
  error("[tts] cleanup failed:", err);
17340
17709
  });
17341
17710
  return oggBuffer;
@@ -17367,9 +17736,9 @@ var init_stt = __esm({
17367
17736
  });
17368
17737
 
17369
17738
  // src/media/image-gen.ts
17370
- import { mkdirSync as mkdirSync9, existsSync as existsSync18, unlink as unlink2, readdir, stat } from "fs";
17739
+ import { mkdirSync as mkdirSync9, existsSync as existsSync19, unlink as unlink3, readdir as readdir2, stat as stat2 } from "fs";
17371
17740
  import { writeFile } from "fs/promises";
17372
- import { join as join17 } from "path";
17741
+ import { join as join18 } from "path";
17373
17742
  async function generateImage(prompt) {
17374
17743
  const apiKey = process.env.GEMINI_API_KEY;
17375
17744
  if (!apiKey) {
@@ -17416,12 +17785,12 @@ async function generateImage(prompt) {
17416
17785
  if (!imageData) {
17417
17786
  throw new Error(textResponse ?? "Gemini did not generate an image. The prompt may have been filtered.");
17418
17787
  }
17419
- if (!existsSync18(IMAGE_OUTPUT_DIR)) {
17788
+ if (!existsSync19(IMAGE_OUTPUT_DIR)) {
17420
17789
  mkdirSync9(IMAGE_OUTPUT_DIR, { recursive: true });
17421
17790
  }
17422
17791
  const ext = mimeType.includes("jpeg") || mimeType.includes("jpg") ? "jpg" : "png";
17423
17792
  const filename = `img_${Date.now()}.${ext}`;
17424
- const filePath = join17(IMAGE_OUTPUT_DIR, filename);
17793
+ const filePath = join18(IMAGE_OUTPUT_DIR, filename);
17425
17794
  const buffer = Buffer.from(imageData, "base64");
17426
17795
  await writeFile(filePath, buffer);
17427
17796
  log(`[image-gen] Saved ${buffer.length} bytes to ${filePath}`);
@@ -17431,21 +17800,21 @@ function isImageGenAvailable() {
17431
17800
  return !!process.env.GEMINI_API_KEY;
17432
17801
  }
17433
17802
  function cleanupGeneratedImage(filePath) {
17434
- unlink2(filePath, (err) => {
17803
+ unlink3(filePath, (err) => {
17435
17804
  if (err) warn(`[image-gen] cleanup failed for ${filePath}: ${err.message}`);
17436
17805
  else log(`[image-gen] cleaned up ${filePath}`);
17437
17806
  });
17438
17807
  }
17439
17808
  function pruneImageCache() {
17440
- readdir(IMAGE_OUTPUT_DIR, (err, files) => {
17809
+ readdir2(IMAGE_OUTPUT_DIR, (err, files) => {
17441
17810
  if (err || !files) return;
17442
- const imageFiles = files.filter((f) => /\.(png|jpg)$/.test(f)).map((f) => join17(IMAGE_OUTPUT_DIR, f));
17811
+ const imageFiles = files.filter((f) => /\.(png|jpg)$/.test(f)).map((f) => join18(IMAGE_OUTPUT_DIR, f));
17443
17812
  if (imageFiles.length === 0) return;
17444
17813
  const now = Date.now();
17445
17814
  let statsPending = imageFiles.length;
17446
17815
  const fileStats = [];
17447
17816
  for (const fp of imageFiles) {
17448
- stat(fp, (serr, s) => {
17817
+ stat2(fp, (serr, s) => {
17449
17818
  statsPending--;
17450
17819
  if (!serr && s) fileStats.push({ path: fp, mtime: s.mtimeMs });
17451
17820
  if (statsPending === 0) {
@@ -17454,7 +17823,7 @@ function pruneImageCache() {
17454
17823
  (f, idx) => idx >= MAX_GENERATED_IMAGES || now - f.mtime > IMAGE_MAX_AGE_MS
17455
17824
  );
17456
17825
  for (const { path } of toDelete) {
17457
- unlink2(path, (derr) => {
17826
+ unlink3(path, (derr) => {
17458
17827
  if (!derr) log(`[image-gen] pruned old image: ${path}`);
17459
17828
  });
17460
17829
  }
@@ -17471,8 +17840,8 @@ var init_image_gen = __esm({
17471
17840
  MAX_GENERATED_IMAGES = 20;
17472
17841
  IMAGE_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
17473
17842
  IMAGE_MODEL = "gemini-3.1-flash-image-preview";
17474
- IMAGE_OUTPUT_DIR = join17(
17475
- process.env.CC_CLAW_HOME ?? join17(process.env.HOME ?? "/tmp", ".cc-claw"),
17843
+ IMAGE_OUTPUT_DIR = join18(
17844
+ process.env.CC_CLAW_HOME ?? join18(process.env.HOME ?? "/tmp", ".cc-claw"),
17476
17845
  "data",
17477
17846
  "images"
17478
17847
  );
@@ -17506,7 +17875,7 @@ function makeToolActionCallback(chatId, channel, level) {
17506
17875
  };
17507
17876
  }
17508
17877
  async function processFileSends2(chatId, channel, text) {
17509
- const fileSendPattern = /\[SEND_FILE:(.+?)\]/g;
17878
+ const fileSendPattern = /\[\s*SEND_FILE:\s*(.+?)\s*\]/g;
17510
17879
  const filePaths = [];
17511
17880
  for (const match of text.matchAll(fileSendPattern)) {
17512
17881
  filePaths.push(match[1].trim());
@@ -17527,7 +17896,7 @@ async function processFileSends2(chatId, channel, text) {
17527
17896
  return text.replace(fileSendPattern, "").trim();
17528
17897
  }
17529
17898
  async function processImageGenerations(chatId, channel, text) {
17530
- const pattern = /\[GENERATE_IMAGE:(.+?)\]/g;
17899
+ const pattern = /\[\s*GENERATE_IMAGE:\s*(.+?)\s*\]/g;
17531
17900
  const prompts = [];
17532
17901
  for (const match of text.matchAll(pattern)) {
17533
17902
  prompts.push(match[1].trim());
@@ -17555,7 +17924,7 @@ async function processImageGenerations(chatId, channel, text) {
17555
17924
  return text.replace(pattern, "").trim();
17556
17925
  }
17557
17926
  async function processReaction(chatId, channel, text, messageId) {
17558
- const reactPatternGlobal = /\[REACT:(.+?)\]/g;
17927
+ const reactPatternGlobal = /\[\s*REACT:\s*(.+?)\s*\]/g;
17559
17928
  if (!reactPatternGlobal.test(text)) return text;
17560
17929
  let reacted = false;
17561
17930
  reactPatternGlobal.lastIndex = 0;
@@ -17569,7 +17938,7 @@ async function processReaction(chatId, channel, text, messageId) {
17569
17938
  reacted = true;
17570
17939
  }
17571
17940
  }
17572
- return text.replace(/\[REACT:(.+?)\]/g, "").trim();
17941
+ return text.replace(/\[\s*REACT:\s*(.+?)\s*\]/g, "").trim();
17573
17942
  }
17574
17943
  async function sendResponse(chatId, channel, text, messageId, replyToMessageId) {
17575
17944
  text = await processReaction(chatId, channel, text, messageId);
@@ -17582,9 +17951,9 @@ async function sendResponse(chatId, channel, text, messageId, replyToMessageId)
17582
17951
  }
17583
17952
  }
17584
17953
  let afterHistory = afterUpdates;
17585
- const historySearchMatch = afterHistory.match(/\[HISTORY_SEARCH:([^\]]+)\]/);
17954
+ const historySearchMatch = afterHistory.match(/\[\s*HISTORY_SEARCH:\s*([^\]]+?)\s*\]/);
17586
17955
  if (historySearchMatch) {
17587
- afterHistory = afterHistory.replace(/\[HISTORY_SEARCH:[^\]]+\]/g, "").trim();
17956
+ afterHistory = afterHistory.replace(/\[\s*HISTORY_SEARCH:\s*[^\]]+?\s*\]/g, "").trim();
17588
17957
  const hsQuery = historySearchMatch[1].trim();
17589
17958
  const hsResults = searchMessageLog(chatId, hsQuery, 10);
17590
17959
  if (hsResults.length > 0) {
@@ -17929,33 +18298,33 @@ var init_video = __esm({
17929
18298
  });
17930
18299
 
17931
18300
  // src/router/media.ts
17932
- import { join as join18 } from "path";
17933
- import { mkdir, writeFile as writeFile2, readdir as readdir2, stat as stat2, unlink as unlink3 } from "fs/promises";
18301
+ import { join as join19 } from "path";
18302
+ import { mkdir as mkdir2, writeFile as writeFile2, readdir as readdir3, stat as stat3, unlink as unlink4 } from "fs/promises";
17934
18303
  function getMediaRetentionMs() {
17935
18304
  const hours = parseInt(process.env.MEDIA_RETENTION_HOURS ?? "24", 10);
17936
18305
  return (isNaN(hours) || hours < 1 ? 24 : hours) * 60 * 60 * 1e3;
17937
18306
  }
17938
18307
  async function saveMedia(buffer, prefix, ext) {
17939
- await mkdir(MEDIA_INCOMING_PATH, { recursive: true });
18308
+ await mkdir2(MEDIA_INCOMING_PATH, { recursive: true });
17940
18309
  const filename = `${prefix}-${Date.now()}.${ext}`;
17941
- const fullPath = join18(MEDIA_INCOMING_PATH, filename);
18310
+ const fullPath = join19(MEDIA_INCOMING_PATH, filename);
17942
18311
  await writeFile2(fullPath, buffer);
17943
18312
  return fullPath;
17944
18313
  }
17945
18314
  async function cleanupOldMedia() {
17946
18315
  try {
17947
- await mkdir(MEDIA_INCOMING_PATH, { recursive: true });
18316
+ await mkdir2(MEDIA_INCOMING_PATH, { recursive: true });
17948
18317
  const retentionMs = getMediaRetentionMs();
17949
18318
  const retentionHours = Math.round(retentionMs / (60 * 60 * 1e3));
17950
- const files = await readdir2(MEDIA_INCOMING_PATH);
18319
+ const files = await readdir3(MEDIA_INCOMING_PATH);
17951
18320
  const now = Date.now();
17952
18321
  let removed = 0;
17953
18322
  for (const file of files) {
17954
18323
  try {
17955
- const filePath = join18(MEDIA_INCOMING_PATH, file);
17956
- const s = await stat2(filePath);
18324
+ const filePath = join19(MEDIA_INCOMING_PATH, file);
18325
+ const s = await stat3(filePath);
17957
18326
  if (now - s.mtimeMs > retentionMs) {
17958
- await unlink3(filePath);
18327
+ await unlink4(filePath);
17959
18328
  removed++;
17960
18329
  }
17961
18330
  } catch {
@@ -18170,7 +18539,21 @@ ${content}
18170
18539
  await sendResponse(chatId, channel, mediaResponse, msg.messageId);
18171
18540
  } catch (err) {
18172
18541
  error("[router] Media error:", err);
18173
- await channel.sendText(chatId, `Error processing file: ${errorMessage(err)}`, { parseMode: "plain" });
18542
+ const errStr = errorMessage(err);
18543
+ if (errStr.includes("file is too big") || errStr.includes("too big")) {
18544
+ const host = process.env.CC_CLAW_API_HOST ?? "192.168.68.10";
18545
+ const port = process.env.CC_CLAW_PORT ?? "3141";
18546
+ await channel.sendText(
18547
+ chatId,
18548
+ `File too large for Telegram (max 20MB via Bot API).
18549
+
18550
+ Upload directly instead:
18551
+ http://${host}:${port}/upload`,
18552
+ { parseMode: "plain" }
18553
+ );
18554
+ } else {
18555
+ await channel.sendText(chatId, `Error processing file: ${errStr}`, { parseMode: "plain" });
18556
+ }
18174
18557
  }
18175
18558
  }
18176
18559
  var MEDIA_INCOMING_PATH;
@@ -18188,7 +18571,7 @@ var init_media = __esm({
18188
18571
  init_helpers();
18189
18572
  init_response();
18190
18573
  init_live_status();
18191
- MEDIA_INCOMING_PATH = join18(MEDIA_PATH, "incoming");
18574
+ MEDIA_INCOMING_PATH = join19(MEDIA_PATH, "incoming");
18192
18575
  }
18193
18576
  });
18194
18577
 
@@ -18500,9 +18883,9 @@ var install_exports = {};
18500
18883
  __export(install_exports, {
18501
18884
  installSkillFromGitHub: () => installSkillFromGitHub
18502
18885
  });
18503
- import { mkdir as mkdir2, readdir as readdir3, readFile as readFile4, cp } from "fs/promises";
18504
- import { existsSync as existsSync19 } from "fs";
18505
- import { join as join19, basename as basename2 } from "path";
18886
+ import { mkdir as mkdir3, readdir as readdir4, readFile as readFile4, cp } from "fs/promises";
18887
+ import { existsSync as existsSync20 } from "fs";
18888
+ import { join as join20, basename as basename2 } from "path";
18506
18889
  import { execSync as execSync4 } from "child_process";
18507
18890
  async function installSkillFromGitHub(urlOrShorthand) {
18508
18891
  let repoUrl;
@@ -18513,36 +18896,36 @@ async function installSkillFromGitHub(urlOrShorthand) {
18513
18896
  }
18514
18897
  repoUrl = parsed.cloneUrl;
18515
18898
  subPath = parsed.subPath;
18516
- const tmpDir = join19("/tmp", `cc-claw-skill-${Date.now()}`);
18899
+ const tmpDir = join20("/tmp", `cc-claw-skill-${Date.now()}`);
18517
18900
  try {
18518
18901
  log(`[skill-install] Cloning ${repoUrl} to ${tmpDir}`);
18519
18902
  execSync4(`git clone --depth 1 ${repoUrl} ${tmpDir}`, {
18520
18903
  stdio: "pipe",
18521
18904
  timeout: 3e4
18522
18905
  });
18523
- if (!existsSync19(join19(tmpDir, ".git"))) {
18906
+ if (!existsSync20(join20(tmpDir, ".git"))) {
18524
18907
  return { success: false, error: "Git clone failed: no .git directory produced" };
18525
18908
  }
18526
- const searchRoot = subPath ? join19(tmpDir, subPath) : tmpDir;
18909
+ const searchRoot = subPath ? join20(tmpDir, subPath) : tmpDir;
18527
18910
  const skillDir = await findSkillDir(searchRoot);
18528
18911
  if (!skillDir) {
18529
18912
  return { success: false, error: "No SKILL.md found in the repository." };
18530
18913
  }
18531
18914
  const skillFolderName = basename2(skillDir);
18532
- const destDir = join19(SKILLS_PATH, skillFolderName);
18533
- if (existsSync19(destDir)) {
18915
+ const destDir = join20(SKILLS_PATH, skillFolderName);
18916
+ if (existsSync20(destDir)) {
18534
18917
  log(`[skill-install] Overwriting existing skill at ${destDir}`);
18535
18918
  }
18536
- await mkdir2(destDir, { recursive: true });
18919
+ await mkdir3(destDir, { recursive: true });
18537
18920
  await cp(skillDir, destDir, { recursive: true });
18538
18921
  let skillName = skillFolderName;
18539
18922
  try {
18540
- const content = await readFile4(join19(destDir, "SKILL.md"), "utf-8");
18923
+ const content = await readFile4(join20(destDir, "SKILL.md"), "utf-8");
18541
18924
  const nameMatch = content.match(/^name:\s*(.+)$/m);
18542
18925
  if (nameMatch) skillName = nameMatch[1].trim().replace(/^["']|["']$/g, "");
18543
18926
  } catch {
18544
18927
  try {
18545
- const content = await readFile4(join19(destDir, "skill.md"), "utf-8");
18928
+ const content = await readFile4(join20(destDir, "skill.md"), "utf-8");
18546
18929
  const nameMatch = content.match(/^name:\s*(.+)$/m);
18547
18930
  if (nameMatch) skillName = nameMatch[1].trim().replace(/^["']|["']$/g, "");
18548
18931
  } catch {
@@ -18577,35 +18960,35 @@ function parseGitHubUrl(input) {
18577
18960
  async function findSkillDir(root) {
18578
18961
  const candidates = ["SKILL.md", "skill.md"];
18579
18962
  for (const c of candidates) {
18580
- if (existsSync19(join19(root, c))) return root;
18963
+ if (existsSync20(join20(root, c))) return root;
18581
18964
  }
18582
18965
  try {
18583
- const entries = await readdir3(root, { withFileTypes: true });
18966
+ const entries = await readdir4(root, { withFileTypes: true });
18584
18967
  for (const entry of entries) {
18585
18968
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
18586
18969
  for (const c of candidates) {
18587
- if (existsSync19(join19(root, entry.name, c))) {
18588
- return join19(root, entry.name);
18970
+ if (existsSync20(join20(root, entry.name, c))) {
18971
+ return join20(root, entry.name);
18589
18972
  }
18590
18973
  }
18591
18974
  }
18592
18975
  } catch {
18593
18976
  }
18594
18977
  try {
18595
- const entries = await readdir3(root, { withFileTypes: true });
18978
+ const entries = await readdir4(root, { withFileTypes: true });
18596
18979
  for (const entry of entries) {
18597
18980
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
18598
18981
  let subEntries;
18599
18982
  try {
18600
- subEntries = await readdir3(join19(root, entry.name), { withFileTypes: true });
18983
+ subEntries = await readdir4(join20(root, entry.name), { withFileTypes: true });
18601
18984
  } catch {
18602
18985
  continue;
18603
18986
  }
18604
18987
  for (const sub of subEntries) {
18605
18988
  if (!sub.isDirectory() || sub.name.startsWith(".")) continue;
18606
18989
  for (const c of candidates) {
18607
- if (existsSync19(join19(root, entry.name, sub.name, c))) {
18608
- return join19(root, entry.name, sub.name);
18990
+ if (existsSync20(join20(root, entry.name, sub.name, c))) {
18991
+ return join20(root, entry.name, sub.name);
18609
18992
  }
18610
18993
  }
18611
18994
  }
@@ -18629,10 +19012,10 @@ __export(discover_exports, {
18629
19012
  invalidateSkillCache: () => invalidateSkillCache,
18630
19013
  stripFrontmatter: () => stripFrontmatter2
18631
19014
  });
18632
- import { readdir as readdir4, readFile as readFile5 } from "fs/promises";
19015
+ import { readdir as readdir5, readFile as readFile5 } from "fs/promises";
18633
19016
  import { createHash } from "crypto";
18634
19017
  import { homedir as homedir5 } from "os";
18635
- import { join as join20 } from "path";
19018
+ import { join as join21 } from "path";
18636
19019
  function invalidateSkillCache() {
18637
19020
  cachedSkills = null;
18638
19021
  cacheTimestamp = 0;
@@ -18650,7 +19033,7 @@ async function discoverAllSkills() {
18650
19033
  const rawSkills = [];
18651
19034
  rawSkills.push(...await scanSkillDir(SKILLS_PATH, "cc-claw"));
18652
19035
  for (const backendId of getAllBackendIds()) {
18653
- const dirs = BACKEND_SKILL_DIRS[backendId] ?? [join20(homedir5(), `.${backendId}`, "skills")];
19036
+ const dirs = BACKEND_SKILL_DIRS[backendId] ?? [join21(homedir5(), `.${backendId}`, "skills")];
18654
19037
  for (const dir of dirs) {
18655
19038
  rawSkills.push(...await scanSkillDir(dir, backendId));
18656
19039
  }
@@ -18669,7 +19052,7 @@ async function scanSkillDir(skillsDir, source) {
18669
19052
  const results = [];
18670
19053
  let entries;
18671
19054
  try {
18672
- entries = await readdir4(skillsDir, { withFileTypes: true });
19055
+ entries = await readdir5(skillsDir, { withFileTypes: true });
18673
19056
  } catch {
18674
19057
  return results;
18675
19058
  }
@@ -18678,7 +19061,7 @@ async function scanSkillDir(skillsDir, source) {
18678
19061
  let content;
18679
19062
  let resolvedPath;
18680
19063
  for (const candidate of SKILL_FILE_CANDIDATES) {
18681
- const p = join20(skillsDir, entry.name, candidate);
19064
+ const p = join21(skillsDir, entry.name, candidate);
18682
19065
  try {
18683
19066
  content = await readFile5(p, "utf-8");
18684
19067
  resolvedPath = p;
@@ -18695,9 +19078,7 @@ async function scanSkillDir(skillsDir, source) {
18695
19078
  description: frontmatter.description,
18696
19079
  filePath: resolvedPath,
18697
19080
  source,
18698
- contentHash: hash,
18699
- compatibleBackends: frontmatter.compatibleBackends,
18700
- recommendedModel: frontmatter.recommendedModel
19081
+ contentHash: hash
18701
19082
  });
18702
19083
  }
18703
19084
  return results;
@@ -18726,9 +19107,7 @@ function mergeAndDeduplicate(raw) {
18726
19107
  filePath: primary.filePath,
18727
19108
  source: primary.source,
18728
19109
  sources: [...new Set(allSources)],
18729
- contentHash: primary.contentHash,
18730
- compatibleBackends: primary.compatibleBackends,
18731
- recommendedModel: primary.recommendedModel
19110
+ contentHash: primary.contentHash
18732
19111
  });
18733
19112
  } else {
18734
19113
  for (const [hash, copies] of byHash) {
@@ -18740,9 +19119,7 @@ function mergeAndDeduplicate(raw) {
18740
19119
  filePath: primary.filePath,
18741
19120
  source: primary.source,
18742
19121
  sources: [...new Set(allSources)],
18743
- contentHash: hash,
18744
- compatibleBackends: primary.compatibleBackends,
18745
- recommendedModel: primary.recommendedModel
19122
+ contentHash: hash
18746
19123
  });
18747
19124
  }
18748
19125
  }
@@ -18762,13 +19139,9 @@ function parseFrontmatter2(content, fallbackName) {
18762
19139
  const fm = fmMatch[1];
18763
19140
  const nameMatch = fm.match(/^name:\s*(.+)$/m);
18764
19141
  const descMatch = fm.match(/^description:\s*(.+)$/m);
18765
- const compatMatch = fm.match(/^compatible_backends:\s*(.+)$/m);
18766
- const modelMatch = fm.match(/^recommended_model:\s*(.+)$/m);
18767
19142
  return {
18768
19143
  name: nameMatch?.[1]?.trim().replace(/^["']|["']$/g, "") ?? fallbackName,
18769
- description: descMatch?.[1]?.trim().replace(/^["']|["']$/g, "") ?? "",
18770
- compatibleBackends: compatMatch ? compatMatch[1].split(",").map((s) => s.trim().toLowerCase()) : void 0,
18771
- recommendedModel: modelMatch?.[1]?.trim() ?? void 0
19144
+ description: descMatch?.[1]?.trim().replace(/^["']|["']$/g, "") ?? ""
18772
19145
  };
18773
19146
  }
18774
19147
  function stripFrontmatter2(content) {
@@ -18782,15 +19155,15 @@ var init_discover = __esm({
18782
19155
  init_backends();
18783
19156
  SKILL_FILE_CANDIDATES = ["SKILL.md", "skill.md"];
18784
19157
  BACKEND_SKILL_DIRS = {
18785
- claude: [join20(homedir5(), ".claude", "skills")],
18786
- gemini: [join20(homedir5(), ".gemini", "skills")],
19158
+ claude: [join21(homedir5(), ".claude", "skills")],
19159
+ gemini: [join21(homedir5(), ".gemini", "skills")],
18787
19160
  codex: [
18788
- join20(homedir5(), ".agents", "skills"),
18789
- join20(homedir5(), ".codex", "skills")
19161
+ join21(homedir5(), ".agents", "skills"),
19162
+ join21(homedir5(), ".codex", "skills")
18790
19163
  ],
18791
19164
  cursor: [
18792
- join20(homedir5(), ".cursor", "skills"),
18793
- join20(homedir5(), ".cursor", "skills-cursor")
19165
+ join21(homedir5(), ".cursor", "skills"),
19166
+ join21(homedir5(), ".cursor", "skills-cursor")
18794
19167
  ]
18795
19168
  };
18796
19169
  CACHE_TTL_MS2 = 3e5;
@@ -20014,13 +20387,13 @@ async function handleEvolveCallback(chatId, data, channel) {
20014
20387
  const { getReflectionStatus: getReflectionStatus2, setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
20015
20388
  const current = getReflectionStatus2(getDb(), chatId);
20016
20389
  if (current === "frozen") {
20017
- const { readFileSync: readFileSync29, existsSync: existsSync57 } = await import("fs");
20018
- const { join: join36 } = await import("path");
20390
+ const { readFileSync: readFileSync29, existsSync: existsSync58 } = await import("fs");
20391
+ const { join: join37 } = await import("path");
20019
20392
  const { CC_CLAW_HOME: CC_CLAW_HOME3 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
20020
- const soulPath = join36(CC_CLAW_HOME3, "identity/SOUL.md");
20021
- const userPath = join36(CC_CLAW_HOME3, "identity/USER.md");
20022
- const soul = existsSync57(soulPath) ? readFileSync29(soulPath, "utf-8") : "";
20023
- const user = existsSync57(userPath) ? readFileSync29(userPath, "utf-8") : "";
20393
+ const soulPath = join37(CC_CLAW_HOME3, "identity/SOUL.md");
20394
+ const userPath = join37(CC_CLAW_HOME3, "identity/USER.md");
20395
+ const soul = existsSync58(soulPath) ? readFileSync29(soulPath, "utf-8") : "";
20396
+ const user = existsSync58(userPath) ? readFileSync29(userPath, "utf-8") : "";
20024
20397
  setReflectionStatus2(getDb(), chatId, "active", soul, user);
20025
20398
  const { logActivity: logActivity2 } = await Promise.resolve().then(() => (init_store3(), store_exports3));
20026
20399
  logActivity2(getDb(), { chatId, source: "telegram", eventType: "reflection_unfrozen", summary: "Reflection enabled" });
@@ -20097,11 +20470,11 @@ var init_evolve2 = __esm({
20097
20470
  });
20098
20471
 
20099
20472
  // src/optimizer/identity-audit.ts
20100
- import { readFileSync as readFileSync11, existsSync as existsSync20, readdirSync as readdirSync10, statSync as statSync8 } from "fs";
20101
- import { join as join21 } from "path";
20473
+ import { readFileSync as readFileSync11, existsSync as existsSync21, readdirSync as readdirSync10, statSync as statSync8 } from "fs";
20474
+ import { join as join22 } from "path";
20102
20475
  function readIdentityFile2(filename) {
20103
20476
  try {
20104
- return readFileSync11(join21(IDENTITY_PATH, filename), "utf-8");
20477
+ return readFileSync11(join22(IDENTITY_PATH, filename), "utf-8");
20105
20478
  } catch {
20106
20479
  return "";
20107
20480
  }
@@ -20116,13 +20489,13 @@ function getMtime(filepath) {
20116
20489
  function findBackupFiles() {
20117
20490
  const backups = [];
20118
20491
  const dirs = [IDENTITY_PATH];
20119
- const contextDir = join21(IDENTITY_PATH, "..", "workspace", "context");
20120
- if (existsSync20(contextDir)) dirs.push(contextDir);
20492
+ const contextDir = join22(IDENTITY_PATH, "..", "workspace", "context");
20493
+ if (existsSync21(contextDir)) dirs.push(contextDir);
20121
20494
  for (const dir of dirs) {
20122
20495
  try {
20123
20496
  for (const entry of readdirSync10(dir)) {
20124
20497
  if (entry.endsWith(".bak") || /\.bak\.\d{4}-\d{2}-\d{2}/.test(entry)) {
20125
- backups.push(join21(dir, entry));
20498
+ backups.push(join22(dir, entry));
20126
20499
  }
20127
20500
  }
20128
20501
  } catch {
@@ -20143,9 +20516,9 @@ function computeIdentityStats(pendingProposals, driftPercent) {
20143
20516
  userChars,
20144
20517
  ccClawChars,
20145
20518
  boilerplateChars,
20146
- soulMtime: getMtime(join21(IDENTITY_PATH, "SOUL.md")),
20147
- userMtime: getMtime(join21(IDENTITY_PATH, "USER.md")),
20148
- ccClawMtime: getMtime(join21(IDENTITY_PATH, "CC-CLAW.md")),
20519
+ soulMtime: getMtime(join22(IDENTITY_PATH, "SOUL.md")),
20520
+ userMtime: getMtime(join22(IDENTITY_PATH, "USER.md")),
20521
+ ccClawMtime: getMtime(join22(IDENTITY_PATH, "CC-CLAW.md")),
20149
20522
  backupFiles: findBackupFiles(),
20150
20523
  estimatedTokens: Math.ceil(ccClawChars / 4),
20151
20524
  pendingEvolveProposals: pendingProposals,
@@ -20254,8 +20627,8 @@ var init_identity_audit = __esm({
20254
20627
  });
20255
20628
 
20256
20629
  // src/optimizer/skill-audit.ts
20257
- import { readFileSync as readFileSync12, existsSync as existsSync21 } from "fs";
20258
- import { join as join22, basename as basename3 } from "path";
20630
+ import { readFileSync as readFileSync12, existsSync as existsSync22 } from "fs";
20631
+ import { join as join23, basename as basename3 } from "path";
20259
20632
  function parseFrontmatter3(content) {
20260
20633
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
20261
20634
  if (!fmMatch) return {};
@@ -20270,10 +20643,6 @@ function parseFrontmatter3(content) {
20270
20643
  const singleDesc = fm.match(/^description:\s*(.+)/m);
20271
20644
  if (singleDesc) result.description = singleDesc[1].trim().replace(/^["']|["']$/g, "");
20272
20645
  }
20273
- const backendsMatch = fm.match(/^compatible_backends:\s*(.+)/m);
20274
- if (backendsMatch) result.compatibleBackends = backendsMatch[1].trim();
20275
- const modelMatch = fm.match(/^recommended_model:\s*(.+)/m);
20276
- if (modelMatch) result.recommendedModel = modelMatch[1].trim();
20277
20646
  return result;
20278
20647
  }
20279
20648
  function detectDependentSkills(content) {
@@ -20298,7 +20667,7 @@ function computeSkillStats(skillPath) {
20298
20667
  const content = readFileSync12(skillPath, "utf-8");
20299
20668
  const lines = content.split("\n");
20300
20669
  return {
20301
- skillName: basename3(skillPath, ".md") === "SKILL" ? basename3(join22(skillPath, "..")) : basename3(skillPath, ".md"),
20670
+ skillName: basename3(skillPath, ".md") === "SKILL" ? basename3(join23(skillPath, "..")) : basename3(skillPath, ".md"),
20302
20671
  skillPath,
20303
20672
  lineCount: lines.length,
20304
20673
  charCount: content.length,
@@ -20318,11 +20687,11 @@ function loadDependentSkillContents(depNames, ccClawSkillsDir) {
20318
20687
  const results = [];
20319
20688
  for (const name of depNames) {
20320
20689
  const candidates = [
20321
- join22(ccClawSkillsDir, name, "SKILL.md"),
20322
- join22(ccClawSkillsDir, `${name}-skill`, "SKILL.md")
20690
+ join23(ccClawSkillsDir, name, "SKILL.md"),
20691
+ join23(ccClawSkillsDir, `${name}-skill`, "SKILL.md")
20323
20692
  ];
20324
20693
  for (const candidate of candidates) {
20325
- if (existsSync21(candidate)) {
20694
+ if (existsSync22(candidate)) {
20326
20695
  try {
20327
20696
  const content = readFileSync12(candidate, "utf-8");
20328
20697
  results.push({
@@ -20353,8 +20722,6 @@ Lines: ${stats.lineCount} (cap: 500 \u2014 over 500 = progressive disclosure nee
20353
20722
  Chars: ${stats.charCount} (~${stats.estimatedTokens} tokens)
20354
20723
  Frontmatter name: ${stats.frontmatter.name || "(missing)"}
20355
20724
  Frontmatter description: ${stats.frontmatter.description ? stats.frontmatter.description.slice(0, 200) : "(missing)"}
20356
- Compatible backends: ${stats.frontmatter.compatibleBackends || "(not specified)"}
20357
- Recommended model: ${stats.frontmatter.recommendedModel || "(not specified)"}
20358
20725
  Referenced skills: ${stats.dependentSkills.length > 0 ? stats.dependentSkills.join(", ") : "none"}`);
20359
20726
  sections.push(`[Rubric \u2014 Evaluate Each Area]
20360
20727
 
@@ -20362,7 +20729,6 @@ Referenced skills: ${stats.dependentSkills.length > 0 ? stats.dependentSkills.jo
20362
20729
  - Is 'name' present and descriptive?
20363
20730
  - Does 'description' include BOTH what the skill does AND trigger info (when to use it)?
20364
20731
  - A good description tells the AI exactly when to activate this skill
20365
- - Should 'compatible_backends' or 'recommended_model' be specified?
20366
20732
 
20367
20733
  2. BODY STRUCTURE
20368
20734
  - Over 500 lines? \u2192 needs progressive disclosure (essentials in body, details in references/)
@@ -20439,8 +20805,8 @@ __export(analyze_exports2, {
20439
20805
  });
20440
20806
  import { spawn as spawn7 } from "child_process";
20441
20807
  import { createInterface as createInterface7 } from "readline";
20442
- import { readFileSync as readFileSync13, existsSync as existsSync22, readdirSync as readdirSync12 } from "fs";
20443
- import { join as join23 } from "path";
20808
+ import { readFileSync as readFileSync13, existsSync as existsSync23, readdirSync as readdirSync12 } from "fs";
20809
+ import { join as join24 } from "path";
20444
20810
  import { homedir as homedir7 } from "os";
20445
20811
  function parseOptimizeOutput(raw, validAreas) {
20446
20812
  if (!raw || raw.includes("NO_FINDINGS")) return [];
@@ -20570,20 +20936,20 @@ function getModelDisplayInfo(chatId) {
20570
20936
  }
20571
20937
  function readIdentityFile3(filename) {
20572
20938
  try {
20573
- return readFileSync13(join23(IDENTITY_PATH, filename), "utf-8");
20939
+ return readFileSync13(join24(IDENTITY_PATH, filename), "utf-8");
20574
20940
  } catch {
20575
20941
  return "";
20576
20942
  }
20577
20943
  }
20578
20944
  function loadContextFiles2() {
20579
- const contextDir = join23(homedir7(), ".cc-claw", "workspace", "context");
20945
+ const contextDir = join24(homedir7(), ".cc-claw", "workspace", "context");
20580
20946
  const results = [];
20581
- if (!existsSync22(contextDir)) return results;
20947
+ if (!existsSync23(contextDir)) return results;
20582
20948
  try {
20583
20949
  for (const entry of readdirSync12(contextDir)) {
20584
20950
  if (!entry.endsWith(".md")) continue;
20585
20951
  try {
20586
- const content = readFileSync13(join23(contextDir, entry), "utf-8");
20952
+ const content = readFileSync13(join24(contextDir, entry), "utf-8");
20587
20953
  results.push({ name: entry, content });
20588
20954
  } catch {
20589
20955
  }
@@ -20634,7 +21000,7 @@ async function runSkillAudit(chatId, skillPath) {
20634
21000
  const stats = computeSkillStats(skillPath);
20635
21001
  log(`[optimizer] Running skill audit on ${stats.skillName} with ${adapter.id}:${model2}`);
20636
21002
  const soulMd = readIdentityFile3("SOUL.md");
20637
- const ccClawSkillsDir = join23(homedir7(), ".cc-claw", "workspace", "skills");
21003
+ const ccClawSkillsDir = join24(homedir7(), ".cc-claw", "workspace", "skills");
20638
21004
  const skillContent = readFileSync13(skillPath, "utf-8");
20639
21005
  const prompt = buildSkillAuditPrompt(skillContent, stats, soulMd, ccClawSkillsDir);
20640
21006
  const raw = await spawnAnalysis2(adapter, model2, prompt);
@@ -20649,13 +21015,13 @@ async function runSkillAudit(chatId, skillPath) {
20649
21015
  };
20650
21016
  }
20651
21017
  function listCcClawSkills() {
20652
- const skillsDir = join23(homedir7(), ".cc-claw", "workspace", "skills");
21018
+ const skillsDir = join24(homedir7(), ".cc-claw", "workspace", "skills");
20653
21019
  const entries = [];
20654
- if (!existsSync22(skillsDir)) return entries;
21020
+ if (!existsSync23(skillsDir)) return entries;
20655
21021
  try {
20656
21022
  for (const dir of readdirSync12(skillsDir)) {
20657
- const skillFile = join23(skillsDir, dir, "SKILL.md");
20658
- if (!existsSync22(skillFile)) continue;
21023
+ const skillFile = join24(skillsDir, dir, "SKILL.md");
21024
+ if (!existsSync23(skillFile)) continue;
20659
21025
  let description = "skill";
20660
21026
  try {
20661
21027
  const content = readFileSync13(skillFile, "utf-8");
@@ -20978,8 +21344,8 @@ var init_ui2 = __esm({
20978
21344
  });
20979
21345
 
20980
21346
  // src/router/optimize.ts
20981
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync7, existsSync as existsSync23, readdirSync as readdirSync13, unlinkSync as unlinkSync7 } from "fs";
20982
- import { join as join24, dirname as dirname4 } from "path";
21347
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync7, existsSync as existsSync24, readdirSync as readdirSync13, unlinkSync as unlinkSync7 } from "fs";
21348
+ import { join as join25, dirname as dirname4 } from "path";
20983
21349
  import { homedir as homedir8 } from "os";
20984
21350
  async function handleOptimizeCommand(chatId, channel, _args) {
20985
21351
  const { getModelDisplayInfo: getModelDisplayInfo2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
@@ -21160,7 +21526,7 @@ async function runSkillAuditFlow(chatId, channel, skillName) {
21160
21526
  } = await Promise.resolve().then(() => (init_ui2(), ui_exports));
21161
21527
  const modelInfo = getModelDisplayInfo2(chatId);
21162
21528
  if (!modelInfo) return;
21163
- const skillPath = join24(homedir8(), ".cc-claw", "workspace", "skills", skillName, "SKILL.md");
21529
+ const skillPath = join25(homedir8(), ".cc-claw", "workspace", "skills", skillName, "SKILL.md");
21164
21530
  const progressMsgId = typeof channel.sendTextReturningId === "function" ? await channel.sendTextReturningId(
21165
21531
  chatId,
21166
21532
  buildProgressMessage2(`skill: ${skillName}`, modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel),
@@ -21249,7 +21615,7 @@ async function applyFinding(chatId, channel, index) {
21249
21615
  await showFinding(chatId, channel, index + 1);
21250
21616
  return;
21251
21617
  }
21252
- if (!existsSync23(targetPath)) {
21618
+ if (!existsSync24(targetPath)) {
21253
21619
  await channel.sendText(chatId, `Target file not found: ${targetPath}`, { parseMode: "plain" });
21254
21620
  session2.skipped.push(index);
21255
21621
  await showFinding(chatId, channel, index + 1);
@@ -21331,14 +21697,14 @@ async function finishReview(chatId, channel) {
21331
21697
  activeSessions.delete(chatId);
21332
21698
  }
21333
21699
  function resolveTargetFile(location, auditTarget) {
21334
- const ccClawHome = join24(homedir8(), ".cc-claw");
21700
+ const ccClawHome = join25(homedir8(), ".cc-claw");
21335
21701
  const filePart = location.split(":")[0]?.trim();
21336
21702
  if (!filePart) return null;
21337
- if (filePart === "SOUL.md") return join24(ccClawHome, "identity", "SOUL.md");
21338
- if (filePart === "USER.md") return join24(ccClawHome, "identity", "USER.md");
21339
- if (filePart === "CC-CLAW.md") return join24(ccClawHome, "identity", "CC-CLAW.md");
21703
+ if (filePart === "SOUL.md") return join25(ccClawHome, "identity", "SOUL.md");
21704
+ if (filePart === "USER.md") return join25(ccClawHome, "identity", "USER.md");
21705
+ if (filePart === "CC-CLAW.md") return join25(ccClawHome, "identity", "CC-CLAW.md");
21340
21706
  if (filePart === "SKILL.md" && auditTarget !== "identity") {
21341
- return join24(ccClawHome, "workspace", "skills", auditTarget, "SKILL.md");
21707
+ return join25(ccClawHome, "workspace", "skills", auditTarget, "SKILL.md");
21342
21708
  }
21343
21709
  return null;
21344
21710
  }
@@ -21346,7 +21712,7 @@ function pruneBackups2(absolutePath) {
21346
21712
  const dir = dirname4(absolutePath);
21347
21713
  const baseName = absolutePath.split("/").pop() ?? "";
21348
21714
  try {
21349
- const backups = readdirSync13(dir).filter((f) => f.startsWith(baseName + ".bak.")).sort().map((f) => join24(dir, f));
21715
+ const backups = readdirSync13(dir).filter((f) => f.startsWith(baseName + ".bak.")).sort().map((f) => join25(dir, f));
21350
21716
  while (backups.length > 3) {
21351
21717
  const oldest = backups.shift();
21352
21718
  try {
@@ -21368,6 +21734,193 @@ var init_optimize = __esm({
21368
21734
  }
21369
21735
  });
21370
21736
 
21737
+ // src/skills/auto-create.ts
21738
+ var auto_create_exports = {};
21739
+ __export(auto_create_exports, {
21740
+ buildSessionExtractionPrompt: () => buildSessionExtractionPrompt,
21741
+ buildSkillExtractionPrompt: () => buildSkillExtractionPrompt,
21742
+ clearPendingDraft: () => clearPendingDraft,
21743
+ getPendingDraft: () => getPendingDraft,
21744
+ isSkillWorthy: () => isSkillWorthy,
21745
+ parseExtractedSkill: () => parseExtractedSkill,
21746
+ saveSkill: () => saveSkill,
21747
+ storePendingDraft: () => storePendingDraft
21748
+ });
21749
+ import { join as join26 } from "path";
21750
+ import { writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
21751
+ function isSkillWorthy(signals) {
21752
+ const { toolUseCount, tokenOutput, elapsedMs, userMessage } = signals;
21753
+ if (toolUseCount < 12) return false;
21754
+ if (elapsedMs < 3e5) return false;
21755
+ if (tokenOutput < 3e3) return false;
21756
+ const words = userMessage.split(/\s+/).length;
21757
+ if (words < 5) return false;
21758
+ const skillPatterns = /\b(use|using|run|with|the)\s+(the\s+)?[\w-]+\s+skill\b/i;
21759
+ if (skillPatterns.test(userMessage)) {
21760
+ log(`[auto-skill] Skipping \u2014 message references an existing skill`);
21761
+ return false;
21762
+ }
21763
+ log(`[auto-skill] Skill-worthy: tools=${toolUseCount}, tokens=${tokenOutput}, elapsed=${elapsedMs}ms`);
21764
+ return true;
21765
+ }
21766
+ function buildSkillExtractionPrompt(userMessage, assistantResponse) {
21767
+ const cappedResponse = assistantResponse.length > 8e3 ? assistantResponse.slice(0, 8e3) + "\n\n[...truncated...]" : assistantResponse;
21768
+ return [
21769
+ "You are a skill extraction assistant. Analyze the following completed task and create a reusable SKILL.md file.",
21770
+ "",
21771
+ "A skill is a set of instructions that an AI agent can follow to accomplish a similar task in the future.",
21772
+ "Focus on the PROCESS and APPROACH, not the specific details of this particular task.",
21773
+ "",
21774
+ "## Completed Task",
21775
+ "",
21776
+ `**User request:** ${userMessage}`,
21777
+ "",
21778
+ `**Assistant response:**`,
21779
+ cappedResponse,
21780
+ "",
21781
+ "## Output Format",
21782
+ "",
21783
+ "Output ONLY the SKILL.md content with this exact format:",
21784
+ "",
21785
+ "```",
21786
+ "---",
21787
+ 'name: "skill-name-in-kebab-case"',
21788
+ 'description: "What this skill does and when to use it"',
21789
+ "---",
21790
+ "",
21791
+ "## [Skill Title]",
21792
+ "",
21793
+ "[Step-by-step instructions for accomplishing this type of task]",
21794
+ "[Include decision points, best practices, and common pitfalls]",
21795
+ "[Keep it general enough to be reusable, specific enough to be helpful]",
21796
+ "```",
21797
+ "",
21798
+ "Rules:",
21799
+ "- The skill name should be descriptive and kebab-case",
21800
+ "- The description should explain WHAT it does AND WHEN to use it",
21801
+ "- Instructions should be general (not tied to specific file names or projects)",
21802
+ "- Include any important caveats or prerequisites",
21803
+ "- Keep it under 100 lines"
21804
+ ].join("\n");
21805
+ }
21806
+ function buildSessionExtractionPrompt(sessionMessages) {
21807
+ const transcript = [];
21808
+ let totalChars = 0;
21809
+ const CAP = 12e3;
21810
+ for (const msg of sessionMessages) {
21811
+ const prefix = msg.role === "user" ? "USER" : "ASSISTANT";
21812
+ const text = msg.text.length > 2e3 ? msg.text.slice(0, 2e3) + " [...]" : msg.text;
21813
+ if (totalChars + text.length > CAP) {
21814
+ transcript.push(`[...earlier messages truncated...]`);
21815
+ break;
21816
+ }
21817
+ transcript.push(`[${prefix}]: ${text}`);
21818
+ totalChars += text.length;
21819
+ }
21820
+ return [
21821
+ "You are a skill extraction assistant. Review the following session transcript and create a reusable SKILL.md file.",
21822
+ "",
21823
+ "A skill is a set of instructions that an AI agent can follow to accomplish a similar task in the future.",
21824
+ "Analyze the ENTIRE session to understand:",
21825
+ "- What the user was trying to accomplish",
21826
+ "- What tools and approaches the AI used",
21827
+ "- Any feedback the user gave (corrections, preferences, refinements)",
21828
+ "- The overall workflow pattern",
21829
+ "",
21830
+ "Focus on the PROCESS and APPROACH, not the specific details of this session.",
21831
+ "Incorporate user feedback as best practices or decision points in the skill.",
21832
+ "",
21833
+ "## Session Transcript",
21834
+ "",
21835
+ ...transcript,
21836
+ "",
21837
+ "## Output Format",
21838
+ "",
21839
+ "Output ONLY the SKILL.md content with this exact format:",
21840
+ "",
21841
+ "```",
21842
+ "---",
21843
+ 'name: "skill-name-in-kebab-case"',
21844
+ 'description: "What this skill does and when to use it"',
21845
+ "---",
21846
+ "",
21847
+ "## [Skill Title]",
21848
+ "",
21849
+ "[Step-by-step instructions for accomplishing this type of task]",
21850
+ "[Include decision points, best practices, and common pitfalls]",
21851
+ "[Incorporate any user preferences or corrections from the session]",
21852
+ "[Keep it general enough to be reusable, specific enough to be helpful]",
21853
+ "```",
21854
+ "",
21855
+ "Rules:",
21856
+ "- The skill name should be descriptive and kebab-case",
21857
+ "- The description should explain WHAT it does AND WHEN to use it",
21858
+ "- Instructions should be general (not tied to specific file names or projects)",
21859
+ "- If the user corrected the AI during the session, bake those corrections into the skill as best practices",
21860
+ "- Include any important caveats or prerequisites",
21861
+ "- Keep it under 100 lines"
21862
+ ].join("\n");
21863
+ }
21864
+ function parseExtractedSkill(llmResponse) {
21865
+ let content = llmResponse;
21866
+ const fenceMatch = llmResponse.match(/```(?:markdown|md)?\s*\n([\s\S]*?)```/);
21867
+ if (fenceMatch) content = fenceMatch[1].trim();
21868
+ let fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
21869
+ if (!fmMatch) {
21870
+ const greedyFence = llmResponse.match(/```(?:markdown|md)?\s*\n([\s\S]*)```/);
21871
+ if (greedyFence) {
21872
+ content = greedyFence[1].trim();
21873
+ fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
21874
+ }
21875
+ }
21876
+ if (!fmMatch) {
21877
+ fmMatch = llmResponse.match(/---\s*\n([\s\S]*?)\n---/);
21878
+ if (fmMatch) {
21879
+ const fmStart = llmResponse.indexOf(fmMatch[0]);
21880
+ content = llmResponse.slice(fmStart).replace(/```\s*$/, "").trim();
21881
+ }
21882
+ }
21883
+ if (!fmMatch) {
21884
+ warn("[auto-skill] No frontmatter found in extracted skill");
21885
+ return null;
21886
+ }
21887
+ const nameMatch = fmMatch[1].match(/^name:\s*["']?([^"'\n]+)["']?\s*$/m);
21888
+ if (!nameMatch) {
21889
+ warn("[auto-skill] No name field in skill frontmatter");
21890
+ return null;
21891
+ }
21892
+ const name = nameMatch[1].trim().toLowerCase().replace(/\s+/g, "-");
21893
+ return { name, content };
21894
+ }
21895
+ async function saveSkill(name, content) {
21896
+ const dir = join26(SKILLS_PATH, name);
21897
+ await mkdir4(dir, { recursive: true });
21898
+ const filePath = join26(dir, "SKILL.md");
21899
+ await writeFile4(filePath, content, "utf-8");
21900
+ invalidateSkillCache();
21901
+ log(`[auto-skill] Saved skill "${name}" to ${filePath}`);
21902
+ return { path: filePath };
21903
+ }
21904
+ function storePendingDraft(chatId, draft) {
21905
+ pendingDrafts.set(chatId, draft);
21906
+ }
21907
+ function getPendingDraft(chatId) {
21908
+ return pendingDrafts.get(chatId);
21909
+ }
21910
+ function clearPendingDraft(chatId) {
21911
+ pendingDrafts.delete(chatId);
21912
+ }
21913
+ var pendingDrafts;
21914
+ var init_auto_create = __esm({
21915
+ "src/skills/auto-create.ts"() {
21916
+ "use strict";
21917
+ init_paths();
21918
+ init_discover();
21919
+ init_log();
21920
+ pendingDrafts = /* @__PURE__ */ new Map();
21921
+ }
21922
+ });
21923
+
21371
21924
  // src/council/types.ts
21372
21925
  var COUNCIL_MIN_PARTICIPANTS, COUNCIL_MAX_ROUNDS, COUNCIL_WIZARD_TIMEOUT_MS;
21373
21926
  var init_types4 = __esm({
@@ -21720,6 +22273,49 @@ Use /skills to see it.`, { parseMode: "plain" });
21720
22273
  await channel.sendText(chatId, `Installation failed: ${result.error}`, { parseMode: "plain" });
21721
22274
  }
21722
22275
  }
22276
+ async function handleExtractSkillCommand(chatId, commandArgs, msg, channel) {
22277
+ const { getLog: getLog2 } = await Promise.resolve().then(() => (init_session_log(), session_log_exports));
22278
+ const sessionMessages = getLog2(chatId);
22279
+ if (sessionMessages.length < 2) {
22280
+ await channel.sendText(chatId, "No session history to extract from. Have a conversation first, then run /extract_skill.", { parseMode: "plain" });
22281
+ return;
22282
+ }
22283
+ await channel.sendText(chatId, `Reviewing session (${Math.floor(sessionMessages.length / 2)} exchanges)...`, { parseMode: "plain" });
22284
+ const { buildSessionExtractionPrompt: buildSessionExtractionPrompt2, parseExtractedSkill: parseExtractedSkill2, storePendingDraft: storePendingDraft2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
22285
+ const { askAgent: askAgent3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
22286
+ const { getMode: getMode3 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
22287
+ const prompt = buildSessionExtractionPrompt2(sessionMessages);
22288
+ const response = await askAgent3(chatId, prompt, { permMode: getMode3(chatId) });
22289
+ const extracted = parseExtractedSkill2(response.text);
22290
+ if (!extracted) {
22291
+ await channel.sendText(chatId, "Could not extract a skill from this session. The session may be too short or unfocused.", { parseMode: "plain" });
22292
+ return;
22293
+ }
22294
+ storePendingDraft2(chatId, {
22295
+ name: extracted.name,
22296
+ content: extracted.content,
22297
+ userMessage: sessionMessages.filter((m) => m.role === "user").map((m) => m.text).join("\n"),
22298
+ assistantResponse: sessionMessages.filter((m) => m.role === "assistant").map((m) => m.text).join("\n")
22299
+ });
22300
+ const preview = extracted.content.length > 3500 ? extracted.content.slice(0, 3500) + "\n\n[...truncated...]" : extracted.content;
22301
+ if (typeof channel.sendKeyboard === "function") {
22302
+ await channel.sendText(chatId, `Extracted skill: "${extracted.name}"
22303
+
22304
+ ${preview}`, { parseMode: "plain" });
22305
+ await channel.sendKeyboard(
22306
+ chatId,
22307
+ `Save this skill?`,
22308
+ [[
22309
+ { label: "\u2705 Save Skill", data: "skill:confirm-save", style: "success" },
22310
+ { label: "\u2715 Discard", data: "skill:discard" }
22311
+ ]]
22312
+ );
22313
+ } else {
22314
+ const { saveSkill: saveSkill2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
22315
+ const result = await saveSkill2(extracted.name, extracted.content);
22316
+ await channel.sendText(chatId, `Skill "${extracted.name}" saved to ${result.path}`, { parseMode: "plain" });
22317
+ }
22318
+ }
21723
22319
  async function handleSetupProfileCommand(chatId, commandArgs, msg, channel) {
21724
22320
  await startProfileWizard(chatId, channel);
21725
22321
  }
@@ -23364,6 +23960,10 @@ async function handleCommand(msg, channel) {
23364
23960
  case "skill-install":
23365
23961
  await handleSkillInstallCommand(chatId, commandArgs, msg, channel);
23366
23962
  break;
23963
+ case "extract_skill":
23964
+ case "extractskill":
23965
+ await handleExtractSkillCommand(chatId, commandArgs, msg, channel);
23966
+ break;
23367
23967
  case "setup-profile":
23368
23968
  await handleSetupProfileCommand(chatId, commandArgs, msg, channel);
23369
23969
  break;
@@ -23450,134 +24050,6 @@ var init_commands = __esm({
23450
24050
  }
23451
24051
  });
23452
24052
 
23453
- // src/skills/auto-create.ts
23454
- var auto_create_exports = {};
23455
- __export(auto_create_exports, {
23456
- buildSkillExtractionPrompt: () => buildSkillExtractionPrompt,
23457
- clearPendingDraft: () => clearPendingDraft,
23458
- getPendingDraft: () => getPendingDraft,
23459
- isSkillWorthy: () => isSkillWorthy,
23460
- parseExtractedSkill: () => parseExtractedSkill,
23461
- saveSkill: () => saveSkill,
23462
- storePendingDraft: () => storePendingDraft
23463
- });
23464
- import { join as join25 } from "path";
23465
- import { writeFile as writeFile4, mkdir as mkdir3 } from "fs/promises";
23466
- function isSkillWorthy(signals) {
23467
- const { toolUseCount, tokenOutput, elapsedMs, userMessage } = signals;
23468
- if (toolUseCount < 8) return false;
23469
- const supplementary = [
23470
- tokenOutput >= 3e3,
23471
- // substantial output
23472
- elapsedMs >= 45e3
23473
- // took real effort (45s+)
23474
- ].filter(Boolean).length;
23475
- if (supplementary < 1) return false;
23476
- const words = userMessage.split(/\s+/).length;
23477
- if (words < 5) return false;
23478
- log(`[auto-skill] Skill-worthy: tools=${toolUseCount}, tokens=${tokenOutput}, elapsed=${elapsedMs}ms`);
23479
- return true;
23480
- }
23481
- function buildSkillExtractionPrompt(userMessage, assistantResponse) {
23482
- const cappedResponse = assistantResponse.length > 8e3 ? assistantResponse.slice(0, 8e3) + "\n\n[...truncated...]" : assistantResponse;
23483
- return [
23484
- "You are a skill extraction assistant. Analyze the following completed task and create a reusable SKILL.md file.",
23485
- "",
23486
- "A skill is a set of instructions that an AI agent can follow to accomplish a similar task in the future.",
23487
- "Focus on the PROCESS and APPROACH, not the specific details of this particular task.",
23488
- "",
23489
- "## Completed Task",
23490
- "",
23491
- `**User request:** ${userMessage}`,
23492
- "",
23493
- `**Assistant response:**`,
23494
- cappedResponse,
23495
- "",
23496
- "## Output Format",
23497
- "",
23498
- "Output ONLY the SKILL.md content with this exact format:",
23499
- "",
23500
- "```",
23501
- "---",
23502
- 'name: "skill-name-in-kebab-case"',
23503
- 'description: "What this skill does and when to use it"',
23504
- "---",
23505
- "",
23506
- "## [Skill Title]",
23507
- "",
23508
- "[Step-by-step instructions for accomplishing this type of task]",
23509
- "[Include decision points, best practices, and common pitfalls]",
23510
- "[Keep it general enough to be reusable, specific enough to be helpful]",
23511
- "```",
23512
- "",
23513
- "Rules:",
23514
- "- The skill name should be descriptive and kebab-case",
23515
- "- The description should explain WHAT it does AND WHEN to use it",
23516
- "- Instructions should be general (not tied to specific file names or projects)",
23517
- "- Include any important caveats or prerequisites",
23518
- "- Keep it under 100 lines"
23519
- ].join("\n");
23520
- }
23521
- function parseExtractedSkill(llmResponse) {
23522
- let content = llmResponse;
23523
- const fenceMatch = llmResponse.match(/```(?:markdown|md)?\s*\n([\s\S]*?)```/);
23524
- if (fenceMatch) content = fenceMatch[1].trim();
23525
- let fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
23526
- if (!fmMatch) {
23527
- const greedyFence = llmResponse.match(/```(?:markdown|md)?\s*\n([\s\S]*)```/);
23528
- if (greedyFence) {
23529
- content = greedyFence[1].trim();
23530
- fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
23531
- }
23532
- }
23533
- if (!fmMatch) {
23534
- fmMatch = llmResponse.match(/---\s*\n([\s\S]*?)\n---/);
23535
- if (fmMatch) {
23536
- const fmStart = llmResponse.indexOf(fmMatch[0]);
23537
- content = llmResponse.slice(fmStart).replace(/```\s*$/, "").trim();
23538
- }
23539
- }
23540
- if (!fmMatch) {
23541
- warn("[auto-skill] No frontmatter found in extracted skill");
23542
- return null;
23543
- }
23544
- const nameMatch = fmMatch[1].match(/^name:\s*["']?([^"'\n]+)["']?\s*$/m);
23545
- if (!nameMatch) {
23546
- warn("[auto-skill] No name field in skill frontmatter");
23547
- return null;
23548
- }
23549
- const name = nameMatch[1].trim().toLowerCase().replace(/\s+/g, "-");
23550
- return { name, content };
23551
- }
23552
- async function saveSkill(name, content) {
23553
- const dir = join25(SKILLS_PATH, name);
23554
- await mkdir3(dir, { recursive: true });
23555
- const filePath = join25(dir, "SKILL.md");
23556
- await writeFile4(filePath, content, "utf-8");
23557
- invalidateSkillCache();
23558
- log(`[auto-skill] Saved skill "${name}" to ${filePath}`);
23559
- return { path: filePath };
23560
- }
23561
- function storePendingDraft(chatId, draft) {
23562
- pendingDrafts.set(chatId, draft);
23563
- }
23564
- function getPendingDraft(chatId) {
23565
- return pendingDrafts.get(chatId);
23566
- }
23567
- function clearPendingDraft(chatId) {
23568
- pendingDrafts.delete(chatId);
23569
- }
23570
- var pendingDrafts;
23571
- var init_auto_create = __esm({
23572
- "src/skills/auto-create.ts"() {
23573
- "use strict";
23574
- init_paths();
23575
- init_discover();
23576
- init_log();
23577
- pendingDrafts = /* @__PURE__ */ new Map();
23578
- }
23579
- });
23580
-
23581
24053
  // src/router/callbacks.ts
23582
24054
  import { readFile as readFile7 } from "fs/promises";
23583
24055
  async function handleCallback(chatId, data, channel, messageId) {
@@ -23980,7 +24452,107 @@ This cannot be undone.`,
23980
24452
  }
23981
24453
  } else if (rest.startsWith("edit:")) {
23982
24454
  const id = parseInt(rest.slice(5), 10);
24455
+ const editJob = getJobById(id);
24456
+ if (!editJob) {
24457
+ await channel.sendText(chatId, `Job #${id} not found.`, { parseMode: "plain" });
24458
+ return;
24459
+ }
24460
+ if (typeof channel.sendKeyboard === "function") {
24461
+ const backendLabel = editJob.backend ? `${editJob.backend}/${editJob.model ?? "default"}` : "chat default";
24462
+ await channel.sendKeyboard(
24463
+ chatId,
24464
+ `Edit Job #${id}
24465
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
24466
+ What do you want to change?`,
24467
+ [
24468
+ [
24469
+ { label: "\u{1F4C5} Schedule", data: `job:editwiz:${id}` },
24470
+ { label: "\u{1F527} Backend/Model", data: `job:editbackend:${id}` }
24471
+ ],
24472
+ [
24473
+ { label: "\u23F1 Timeout", data: `job:edittimeout:${id}` },
24474
+ { label: "\u{1F4DD} Full Edit", data: `job:editwiz:${id}` }
24475
+ ],
24476
+ [{ label: "\u2190 Back to Job", data: `job:view:${id}` }]
24477
+ ]
24478
+ );
24479
+ } else {
24480
+ await startEditWizard(chatId, id, channel);
24481
+ }
24482
+ } else if (rest.startsWith("editwiz:")) {
24483
+ const id = parseInt(rest.slice(8), 10);
23983
24484
  await startEditWizard(chatId, id, channel);
24485
+ } else if (rest.startsWith("editbackend:")) {
24486
+ const id = parseInt(rest.slice(12), 10);
24487
+ const { getAllAdapters: getAllAdapters5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
24488
+ const adapters2 = getAllAdapters5().filter((a) => a.id !== "ollama");
24489
+ const buttons = adapters2.map((a) => ({
24490
+ label: a.displayName,
24491
+ data: `job:setbackend:${id}:${a.id}`
24492
+ }));
24493
+ const rows = [];
24494
+ for (let i = 0; i < buttons.length; i += 2) {
24495
+ rows.push(buttons.slice(i, i + 2));
24496
+ }
24497
+ rows.push([{ label: "\u2190 Back", data: `job:edit:${id}` }]);
24498
+ if (typeof channel.sendKeyboard === "function") {
24499
+ await channel.sendKeyboard(chatId, `Pick a backend for Job #${id}:`, rows);
24500
+ }
24501
+ } else if (rest.startsWith("setbackend:")) {
24502
+ const parts = rest.slice(11).split(":");
24503
+ const id = parseInt(parts[0], 10);
24504
+ const backend2 = parts[1];
24505
+ const { getAdapter: getAdapter4 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
24506
+ const adapter = getAdapter4(backend2);
24507
+ const models = Object.entries(adapter.availableModels);
24508
+ const buttons = models.map(([modelId, info]) => ({
24509
+ label: info.label?.split("\u2014")[0]?.trim() ?? modelId,
24510
+ data: `job:setmodel:${id}:${backend2}:${modelId}`
24511
+ }));
24512
+ const rows = [];
24513
+ for (let i = 0; i < buttons.length; i += 2) {
24514
+ rows.push(buttons.slice(i, i + 2));
24515
+ }
24516
+ rows.push([{ label: "\u2190 Back", data: `job:editbackend:${id}` }]);
24517
+ if (typeof channel.sendKeyboard === "function") {
24518
+ await channel.sendKeyboard(chatId, `Pick a model for ${adapter.displayName}:`, rows);
24519
+ }
24520
+ } else if (rest.startsWith("setmodel:")) {
24521
+ const parts = rest.slice(9).split(":");
24522
+ const id = parseInt(parts[0], 10);
24523
+ const backend2 = parts[1];
24524
+ const model2 = parts.slice(2).join(":");
24525
+ const { updateJob: updateJobFields } = await Promise.resolve().then(() => (init_store5(), store_exports5));
24526
+ updateJobFields(id, { backend: backend2, model: model2 });
24527
+ const { getAdapter: getAdapter4 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
24528
+ const adapter = getAdapter4(backend2);
24529
+ await channel.sendText(chatId, `Job #${id} updated: ${adapter.displayName} / ${model2}`, { parseMode: "plain" });
24530
+ await sendJobDetail(chatId, id, channel);
24531
+ } else if (rest.startsWith("edittimeout:")) {
24532
+ const id = parseInt(rest.slice(12), 10);
24533
+ if (typeof channel.sendKeyboard === "function") {
24534
+ await channel.sendKeyboard(chatId, `Set timeout for Job #${id}:`, [
24535
+ [
24536
+ { label: "2 min", data: `job:settimeout:${id}:120` },
24537
+ { label: "5 min", data: `job:settimeout:${id}:300` },
24538
+ { label: "10 min", data: `job:settimeout:${id}:600` }
24539
+ ],
24540
+ [
24541
+ { label: "15 min", data: `job:settimeout:${id}:900` },
24542
+ { label: "30 min", data: `job:settimeout:${id}:1800` },
24543
+ { label: "60 min", data: `job:settimeout:${id}:3600` }
24544
+ ],
24545
+ [{ label: "\u2190 Back", data: `job:edit:${id}` }]
24546
+ ]);
24547
+ }
24548
+ } else if (rest.startsWith("settimeout:")) {
24549
+ const parts = rest.slice(11).split(":");
24550
+ const id = parseInt(parts[0], 10);
24551
+ const timeout = parseInt(parts[1], 10);
24552
+ const { updateJob: updateJobFields } = await Promise.resolve().then(() => (init_store5(), store_exports5));
24553
+ updateJobFields(id, { timeout });
24554
+ await channel.sendText(chatId, `Job #${id} timeout updated: ${timeout}s (${Math.round(timeout / 60)} min)`, { parseMode: "plain" });
24555
+ await sendJobDetail(chatId, id, channel);
23984
24556
  } else if (rest === "back") {
23985
24557
  await sendJobsBoard(chatId, channel, 1, messageId);
23986
24558
  } else {
@@ -24786,14 +25358,6 @@ Use /skills to see all available skills.`, { parseMode: "plain" });
24786
25358
  await channel.sendText(chatId, `Skill "${skillName}" not found.`, { parseMode: "plain" });
24787
25359
  return;
24788
25360
  }
24789
- const activeBackend = getBackend(chatId) ?? "claude";
24790
- if (skill.compatibleBackends && !skill.compatibleBackends.includes(activeBackend)) {
24791
- await channel.sendText(
24792
- chatId,
24793
- `Note: "${skillName}" lists compatible backends as [${skill.compatibleBackends.join(", ")}], but active backend is ${activeBackend}. Proceeding anyway.`,
24794
- { parseMode: "plain" }
24795
- );
24796
- }
24797
25361
  const raw = await readFile7(skill.filePath, "utf-8");
24798
25362
  const skillContent = stripFrontmatter2(raw);
24799
25363
  const tags = skill.sources.join(", ");
@@ -26322,7 +26886,7 @@ var init_cron = __esm({
26322
26886
  });
26323
26887
 
26324
26888
  // src/agents/runners/wrap-backend.ts
26325
- import { join as join26 } from "path";
26889
+ import { join as join27 } from "path";
26326
26890
  function buildMcpCommands(backendId) {
26327
26891
  const exe = backendId === BACKEND.CURSOR ? "agent" : backendId;
26328
26892
  return {
@@ -26416,7 +26980,7 @@ function wrapBackendAdapter(adapter) {
26416
26980
  const configPath = writeMcpConfigFile(server);
26417
26981
  return ["--mcp-config", configPath];
26418
26982
  },
26419
- getSkillPath: () => join26(SKILLS_PATH, `agent-${adapter.id}.md`)
26983
+ getSkillPath: () => join27(SKILLS_PATH, `agent-${adapter.id}.md`)
26420
26984
  };
26421
26985
  }
26422
26986
  var BACKEND_CAPABILITIES;
@@ -26478,18 +27042,18 @@ var init_wrap_backend = __esm({
26478
27042
  });
26479
27043
 
26480
27044
  // src/agents/runners/config-loader.ts
26481
- import { readFileSync as readFileSync15, readdirSync as readdirSync14, existsSync as existsSync24, mkdirSync as mkdirSync10, watchFile, unwatchFile } from "fs";
26482
- import { join as join27 } from "path";
27045
+ import { readFileSync as readFileSync15, readdirSync as readdirSync14, existsSync as existsSync25, mkdirSync as mkdirSync10, watchFile, unwatchFile } from "fs";
27046
+ import { join as join28 } from "path";
26483
27047
  import { execFileSync as execFileSync3 } from "child_process";
26484
27048
  function resolveExecutable2(config2) {
26485
- if (existsSync24(config2.executable)) return config2.executable;
27049
+ if (existsSync25(config2.executable)) return config2.executable;
26486
27050
  try {
26487
27051
  return execFileSync3("which", [config2.executable], { encoding: "utf-8" }).trim();
26488
27052
  } catch {
26489
27053
  }
26490
27054
  for (const fallback of config2.executableFallbacks ?? []) {
26491
27055
  const resolved = fallback.replace(/^~/, process.env.HOME ?? "");
26492
- if (existsSync24(resolved)) return resolved;
27056
+ if (existsSync25(resolved)) return resolved;
26493
27057
  }
26494
27058
  return config2.executable;
26495
27059
  }
@@ -26615,7 +27179,7 @@ function configToRunner(config2) {
26615
27179
  prepareMcpInjection() {
26616
27180
  return [];
26617
27181
  },
26618
- getSkillPath: () => join27(SKILLS_PATH, `agent-${config2.id}.md`)
27182
+ getSkillPath: () => join28(SKILLS_PATH, `agent-${config2.id}.md`)
26619
27183
  };
26620
27184
  }
26621
27185
  function loadRunnerConfig(filePath) {
@@ -26628,14 +27192,14 @@ function loadRunnerConfig(filePath) {
26628
27192
  }
26629
27193
  }
26630
27194
  function loadAllRunnerConfigs() {
26631
- if (!existsSync24(RUNNERS_PATH)) {
27195
+ if (!existsSync25(RUNNERS_PATH)) {
26632
27196
  mkdirSync10(RUNNERS_PATH, { recursive: true });
26633
27197
  return [];
26634
27198
  }
26635
27199
  const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
26636
27200
  const configs = [];
26637
27201
  for (const file of files) {
26638
- const config2 = loadRunnerConfig(join27(RUNNERS_PATH, file));
27202
+ const config2 = loadRunnerConfig(join28(RUNNERS_PATH, file));
26639
27203
  if (config2) configs.push(config2);
26640
27204
  }
26641
27205
  return configs;
@@ -26656,16 +27220,16 @@ function registerConfigRunners() {
26656
27220
  return count;
26657
27221
  }
26658
27222
  function watchRunnerConfigs(onChange) {
26659
- if (!existsSync24(RUNNERS_PATH)) return;
27223
+ if (!existsSync25(RUNNERS_PATH)) return;
26660
27224
  for (const prev of watchedFiles) {
26661
- if (!existsSync24(prev)) {
27225
+ if (!existsSync25(prev)) {
26662
27226
  unwatchFile(prev);
26663
27227
  watchedFiles.delete(prev);
26664
27228
  }
26665
27229
  }
26666
27230
  const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
26667
27231
  for (const file of files) {
26668
- const fullPath = join27(RUNNERS_PATH, file);
27232
+ const fullPath = join28(RUNNERS_PATH, file);
26669
27233
  if (watchedFiles.has(fullPath)) continue;
26670
27234
  watchedFiles.add(fullPath);
26671
27235
  watchFile(fullPath, { interval: 5e3 }, () => {
@@ -27100,6 +27664,8 @@ var init_telegram2 = __esm({
27100
27664
  { command: "newchat", description: "Start a fresh conversation" },
27101
27665
  { command: "summarize", description: "Save session to memory (or 'all' for pre-restart)" },
27102
27666
  { command: "stop", description: "Cancel the current running task" },
27667
+ { command: "debug", description: "Toggle session debug logging" },
27668
+ { command: "imagine", description: "Generate an image from a prompt" },
27103
27669
  // Backend & model
27104
27670
  { command: "backend", description: "Switch AI backend (Claude/Gemini/Codex/Cursor)" },
27105
27671
  { command: "claude", description: "Switch to Claude backend" },
@@ -27148,6 +27714,8 @@ var init_telegram2 = __esm({
27148
27714
  { command: "mcp", description: "List MCP servers across all backends" },
27149
27715
  // Skills & profile
27150
27716
  { command: "skills", description: "List and invoke skills" },
27717
+ { command: "extract_skill", description: "Extract a reusable skill from this session" },
27718
+ { command: "skill_install", description: "Install a skill from GitHub" },
27151
27719
  { command: "voice", description: "Toggle voice responses" },
27152
27720
  { command: "voice_config", description: "Configure voice provider and voice" },
27153
27721
  { command: "response_style", description: "Set the AI response style (concise/normal/detailed)" },
@@ -27731,19 +28299,19 @@ var init_telegram2 = __esm({
27731
28299
  });
27732
28300
 
27733
28301
  // src/skills/bootstrap.ts
27734
- import { existsSync as existsSync25 } from "fs";
27735
- import { readdir as readdir5, readFile as readFile8, writeFile as writeFile5, copyFile } from "fs/promises";
27736
- import { join as join28, dirname as dirname5 } from "path";
28302
+ import { existsSync as existsSync26 } from "fs";
28303
+ import { readdir as readdir6, readFile as readFile8, writeFile as writeFile5, copyFile } from "fs/promises";
28304
+ import { join as join29, dirname as dirname5 } from "path";
27737
28305
  import { fileURLToPath as fileURLToPath2 } from "url";
27738
28306
  async function copyAgentManifestSkills() {
27739
- if (!existsSync25(PKG_SKILLS)) return;
28307
+ if (!existsSync26(PKG_SKILLS)) return;
27740
28308
  try {
27741
- const entries = await readdir5(PKG_SKILLS, { withFileTypes: true });
28309
+ const entries = await readdir6(PKG_SKILLS, { withFileTypes: true });
27742
28310
  for (const entry of entries) {
27743
28311
  if (!entry.isFile() || !entry.name.startsWith("agent-") || !entry.name.endsWith(".md")) continue;
27744
- const src = join28(PKG_SKILLS, entry.name);
27745
- const dest = join28(SKILLS_PATH, entry.name);
27746
- if (existsSync25(dest)) continue;
28312
+ const src = join29(PKG_SKILLS, entry.name);
28313
+ const dest = join29(SKILLS_PATH, entry.name);
28314
+ if (existsSync26(dest)) continue;
27747
28315
  await copyFile(src, dest);
27748
28316
  log(`[skills] Bootstrapped ${entry.name} to ${SKILLS_PATH}`);
27749
28317
  }
@@ -27753,10 +28321,10 @@ async function copyAgentManifestSkills() {
27753
28321
  }
27754
28322
  async function bootstrapSkills() {
27755
28323
  await copyAgentManifestSkills();
27756
- const usmDir = join28(SKILLS_PATH, USM_DIR_NAME);
27757
- if (existsSync25(usmDir)) return;
28324
+ const usmDir = join29(SKILLS_PATH, USM_DIR_NAME);
28325
+ if (existsSync26(usmDir)) return;
27758
28326
  try {
27759
- const entries = await readdir5(SKILLS_PATH);
28327
+ const entries = await readdir6(SKILLS_PATH);
27760
28328
  const dirs = entries.filter((e) => !e.startsWith("."));
27761
28329
  if (dirs.length > 0) return;
27762
28330
  } catch (err) {
@@ -27777,8 +28345,8 @@ async function bootstrapSkills() {
27777
28345
  }
27778
28346
  }
27779
28347
  async function patchUsmForCcClaw(usmDir) {
27780
- const skillPath = join28(usmDir, "SKILL.md");
27781
- if (!existsSync25(skillPath)) return;
28348
+ const skillPath = join29(usmDir, "SKILL.md");
28349
+ if (!existsSync26(skillPath)) return;
27782
28350
  try {
27783
28351
  let content = await readFile8(skillPath, "utf-8");
27784
28352
  let patched = false;
@@ -27823,8 +28391,8 @@ var init_bootstrap = __esm({
27823
28391
  USM_REPO = "jacob-bd/universal-skills-manager";
27824
28392
  USM_DIR_NAME = "universal-skills-manager";
27825
28393
  CC_CLAW_ECOSYSTEM_PATCH = `| **CC-Claw** | \`~/.cc-claw/workspace/skills/\` | N/A (daemon, no project scope) |`;
27826
- PKG_ROOT = join28(dirname5(fileURLToPath2(import.meta.url)), "..", "..");
27827
- PKG_SKILLS = join28(PKG_ROOT, "skills");
28394
+ PKG_ROOT = join29(dirname5(fileURLToPath2(import.meta.url)), "..", "..");
28395
+ PKG_SKILLS = join29(PKG_ROOT, "skills");
27828
28396
  }
27829
28397
  });
27830
28398
 
@@ -28046,13 +28614,13 @@ __export(ai_skill_exports, {
28046
28614
  generateAiSkill: () => generateAiSkill,
28047
28615
  installAiSkill: () => installAiSkill
28048
28616
  });
28049
- import { existsSync as existsSync26, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
28050
- import { join as join29 } from "path";
28617
+ import { existsSync as existsSync27, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
28618
+ import { join as join30 } from "path";
28051
28619
  import { homedir as homedir9 } from "os";
28052
28620
  function generateAiSkill() {
28053
28621
  const version = VERSION;
28054
28622
  let systemState = "";
28055
- if (existsSync26(DB_PATH)) {
28623
+ if (existsSync27(DB_PATH)) {
28056
28624
  try {
28057
28625
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = (init_store5(), __toCommonJS(store_exports5));
28058
28626
  const readDb = openDatabaseReadOnly2();
@@ -28492,8 +29060,8 @@ function installAiSkill() {
28492
29060
  const failed = [];
28493
29061
  for (const [backend2, dirs] of Object.entries(BACKEND_SKILL_DIRS2)) {
28494
29062
  for (const dir of dirs) {
28495
- const skillDir = join29(dir, "cc-claw-cli");
28496
- const skillPath = join29(skillDir, "SKILL.md");
29063
+ const skillDir = join30(dir, "cc-claw-cli");
29064
+ const skillPath = join30(skillDir, "SKILL.md");
28497
29065
  try {
28498
29066
  mkdirSync11(skillDir, { recursive: true });
28499
29067
  writeFileSync8(skillPath, skill, "utf-8");
@@ -28512,11 +29080,11 @@ var init_ai_skill = __esm({
28512
29080
  init_paths();
28513
29081
  init_version();
28514
29082
  BACKEND_SKILL_DIRS2 = {
28515
- "cc-claw": [join29(homedir9(), ".cc-claw", "workspace", "skills")],
28516
- claude: [join29(homedir9(), ".claude", "skills")],
28517
- gemini: [join29(homedir9(), ".gemini", "skills")],
28518
- codex: [join29(homedir9(), ".agents", "skills")],
28519
- cursor: [join29(homedir9(), ".cursor", "skills"), join29(homedir9(), ".cursor", "skills-cursor")]
29083
+ "cc-claw": [join30(homedir9(), ".cc-claw", "workspace", "skills")],
29084
+ claude: [join30(homedir9(), ".claude", "skills")],
29085
+ gemini: [join30(homedir9(), ".gemini", "skills")],
29086
+ codex: [join30(homedir9(), ".agents", "skills")],
29087
+ cursor: [join30(homedir9(), ".cursor", "skills"), join30(homedir9(), ".cursor", "skills-cursor")]
28520
29088
  };
28521
29089
  }
28522
29090
  });
@@ -28526,21 +29094,21 @@ var index_exports = {};
28526
29094
  __export(index_exports, {
28527
29095
  main: () => main
28528
29096
  });
28529
- import { mkdirSync as mkdirSync12, existsSync as existsSync27, renameSync as renameSync2, statSync as statSync9, readFileSync as readFileSync17 } from "fs";
28530
- import { join as join30 } from "path";
29097
+ import { mkdirSync as mkdirSync12, existsSync as existsSync28, renameSync as renameSync2, statSync as statSync9, readFileSync as readFileSync17 } from "fs";
29098
+ import { join as join31 } from "path";
28531
29099
  import dotenv from "dotenv";
28532
29100
  function migrateLayout() {
28533
29101
  const moves = [
28534
- [join30(CC_CLAW_HOME, "cc-claw.db"), join30(DATA_PATH, "cc-claw.db")],
28535
- [join30(CC_CLAW_HOME, "cc-claw.db-shm"), join30(DATA_PATH, "cc-claw.db-shm")],
28536
- [join30(CC_CLAW_HOME, "cc-claw.db-wal"), join30(DATA_PATH, "cc-claw.db-wal")],
28537
- [join30(CC_CLAW_HOME, "cc-claw.log"), join30(LOGS_PATH, "cc-claw.log")],
28538
- [join30(CC_CLAW_HOME, "cc-claw.log.1"), join30(LOGS_PATH, "cc-claw.log.1")],
28539
- [join30(CC_CLAW_HOME, "cc-claw.error.log"), join30(LOGS_PATH, "cc-claw.error.log")],
28540
- [join30(CC_CLAW_HOME, "cc-claw.error.log.1"), join30(LOGS_PATH, "cc-claw.error.log.1")]
29102
+ [join31(CC_CLAW_HOME, "cc-claw.db"), join31(DATA_PATH, "cc-claw.db")],
29103
+ [join31(CC_CLAW_HOME, "cc-claw.db-shm"), join31(DATA_PATH, "cc-claw.db-shm")],
29104
+ [join31(CC_CLAW_HOME, "cc-claw.db-wal"), join31(DATA_PATH, "cc-claw.db-wal")],
29105
+ [join31(CC_CLAW_HOME, "cc-claw.log"), join31(LOGS_PATH, "cc-claw.log")],
29106
+ [join31(CC_CLAW_HOME, "cc-claw.log.1"), join31(LOGS_PATH, "cc-claw.log.1")],
29107
+ [join31(CC_CLAW_HOME, "cc-claw.error.log"), join31(LOGS_PATH, "cc-claw.error.log")],
29108
+ [join31(CC_CLAW_HOME, "cc-claw.error.log.1"), join31(LOGS_PATH, "cc-claw.error.log.1")]
28541
29109
  ];
28542
29110
  for (const [from, to] of moves) {
28543
- if (existsSync27(from) && !existsSync27(to)) {
29111
+ if (existsSync28(from) && !existsSync28(to)) {
28544
29112
  try {
28545
29113
  renameSync2(from, to);
28546
29114
  } catch {
@@ -28717,10 +29285,10 @@ async function main() {
28717
29285
  try {
28718
29286
  const { generateAiSkill: generateAiSkill2 } = await Promise.resolve().then(() => (init_ai_skill(), ai_skill_exports));
28719
29287
  const { writeFileSync: writeFileSync13, mkdirSync: mkdirSync19 } = await import("fs");
28720
- const { join: join36 } = await import("path");
28721
- const skillDir = join36(SKILLS_PATH, "cc-claw-cli");
29288
+ const { join: join37 } = await import("path");
29289
+ const skillDir = join37(SKILLS_PATH, "cc-claw-cli");
28722
29290
  mkdirSync19(skillDir, { recursive: true });
28723
- writeFileSync13(join36(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
29291
+ writeFileSync13(join37(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
28724
29292
  log("[cc-claw] AI skill updated");
28725
29293
  } catch {
28726
29294
  }
@@ -28820,10 +29388,10 @@ var init_index = __esm({
28820
29388
  init_health3();
28821
29389
  init_image_gen();
28822
29390
  for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SESSION_LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
28823
- if (!existsSync27(dir)) mkdirSync12(dir, { recursive: true });
29391
+ if (!existsSync28(dir)) mkdirSync12(dir, { recursive: true });
28824
29392
  }
28825
29393
  migrateLayout();
28826
- if (existsSync27(ENV_PATH)) {
29394
+ if (existsSync28(ENV_PATH)) {
28827
29395
  dotenv.config({ path: ENV_PATH });
28828
29396
  } else {
28829
29397
  console.error(`[cc-claw] Config not found at ${ENV_PATH} \u2014 run 'cc-claw setup' first`);
@@ -28844,12 +29412,12 @@ __export(api_client_exports, {
28844
29412
  apiPost: () => apiPost,
28845
29413
  isDaemonRunning: () => isDaemonRunning
28846
29414
  });
28847
- import { readFileSync as readFileSync18, existsSync as existsSync28 } from "fs";
29415
+ import { readFileSync as readFileSync18, existsSync as existsSync29 } from "fs";
28848
29416
  import { request as httpRequest, Agent } from "http";
28849
29417
  function getToken() {
28850
29418
  if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
28851
29419
  try {
28852
- if (existsSync28(TOKEN_PATH)) return readFileSync18(TOKEN_PATH, "utf-8").trim();
29420
+ if (existsSync29(TOKEN_PATH)) return readFileSync18(TOKEN_PATH, "utf-8").trim();
28853
29421
  } catch {
28854
29422
  }
28855
29423
  return null;
@@ -28948,10 +29516,10 @@ __export(service_exports2, {
28948
29516
  serviceStatus: () => serviceStatus,
28949
29517
  uninstallService: () => uninstallService
28950
29518
  });
28951
- import { existsSync as existsSync29, mkdirSync as mkdirSync13, writeFileSync as writeFileSync9, unlinkSync as unlinkSync8 } from "fs";
29519
+ import { existsSync as existsSync30, mkdirSync as mkdirSync13, writeFileSync as writeFileSync9, unlinkSync as unlinkSync8 } from "fs";
28952
29520
  import { execFileSync as execFileSync4, execSync as execSync5 } from "child_process";
28953
29521
  import { homedir as homedir10, platform } from "os";
28954
- import { join as join31, dirname as dirname6 } from "path";
29522
+ import { join as join32, dirname as dirname6 } from "path";
28955
29523
  function xmlEscape(s) {
28956
29524
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
28957
29525
  }
@@ -28960,7 +29528,7 @@ function resolveExecutable3(name) {
28960
29528
  return execFileSync4("which", [name], { encoding: "utf-8" }).trim();
28961
29529
  } catch {
28962
29530
  const fallback = process.argv[1];
28963
- if (fallback && existsSync29(fallback)) return fallback;
29531
+ if (fallback && existsSync30(fallback)) return fallback;
28964
29532
  throw new Error(`Cannot find '${name}' executable. Install globally: npm install -g cc-claw`);
28965
29533
  }
28966
29534
  }
@@ -28969,14 +29537,14 @@ function getPathDirs() {
28969
29537
  const home = homedir10();
28970
29538
  const dirs = /* @__PURE__ */ new Set([
28971
29539
  nodeBin,
28972
- join31(home, ".local", "bin"),
29540
+ join32(home, ".local", "bin"),
28973
29541
  "/usr/local/bin",
28974
29542
  "/usr/bin",
28975
29543
  "/bin"
28976
29544
  ]);
28977
29545
  try {
28978
29546
  const prefix = execSync5("npm config get prefix", { encoding: "utf-8" }).trim();
28979
- if (prefix) dirs.add(join31(prefix, "bin"));
29547
+ if (prefix) dirs.add(join32(prefix, "bin"));
28980
29548
  } catch {
28981
29549
  }
28982
29550
  return [...dirs].join(":");
@@ -29035,9 +29603,9 @@ function generatePlist() {
29035
29603
  }
29036
29604
  function installMacOS() {
29037
29605
  const agentsDir = dirname6(PLIST_PATH);
29038
- if (!existsSync29(agentsDir)) mkdirSync13(agentsDir, { recursive: true });
29039
- if (!existsSync29(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
29040
- if (existsSync29(PLIST_PATH)) {
29606
+ if (!existsSync30(agentsDir)) mkdirSync13(agentsDir, { recursive: true });
29607
+ if (!existsSync30(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
29608
+ if (existsSync30(PLIST_PATH)) {
29041
29609
  try {
29042
29610
  execFileSync4("launchctl", ["unload", PLIST_PATH]);
29043
29611
  } catch {
@@ -29049,7 +29617,7 @@ function installMacOS() {
29049
29617
  console.log(" Service loaded and starting.");
29050
29618
  }
29051
29619
  function uninstallMacOS() {
29052
- if (!existsSync29(PLIST_PATH)) {
29620
+ if (!existsSync30(PLIST_PATH)) {
29053
29621
  console.log(" No service found to uninstall.");
29054
29622
  return;
29055
29623
  }
@@ -29124,8 +29692,8 @@ WantedBy=default.target
29124
29692
  `;
29125
29693
  }
29126
29694
  function installLinux() {
29127
- if (!existsSync29(SYSTEMD_DIR)) mkdirSync13(SYSTEMD_DIR, { recursive: true });
29128
- if (!existsSync29(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
29695
+ if (!existsSync30(SYSTEMD_DIR)) mkdirSync13(SYSTEMD_DIR, { recursive: true });
29696
+ if (!existsSync30(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
29129
29697
  writeFileSync9(UNIT_PATH, generateUnit());
29130
29698
  console.log(` Installed: ${UNIT_PATH}`);
29131
29699
  execFileSync4("systemctl", ["--user", "daemon-reload"]);
@@ -29134,7 +29702,7 @@ function installLinux() {
29134
29702
  console.log(" Service enabled and started.");
29135
29703
  }
29136
29704
  function uninstallLinux() {
29137
- if (!existsSync29(UNIT_PATH)) {
29705
+ if (!existsSync30(UNIT_PATH)) {
29138
29706
  console.log(" No service found to uninstall.");
29139
29707
  return;
29140
29708
  }
@@ -29159,7 +29727,7 @@ function statusLinux() {
29159
29727
  }
29160
29728
  }
29161
29729
  function installService() {
29162
- if (!existsSync29(join31(CC_CLAW_HOME, ".env"))) {
29730
+ if (!existsSync30(join32(CC_CLAW_HOME, ".env"))) {
29163
29731
  console.error(` Config not found at ${CC_CLAW_HOME}/.env`);
29164
29732
  console.error(" Run 'cc-claw setup' before installing the service.");
29165
29733
  process.exitCode = 1;
@@ -29188,9 +29756,9 @@ var init_service2 = __esm({
29188
29756
  "use strict";
29189
29757
  init_paths();
29190
29758
  PLIST_LABEL = "com.cc-claw";
29191
- PLIST_PATH = join31(homedir10(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
29192
- SYSTEMD_DIR = join31(homedir10(), ".config", "systemd", "user");
29193
- UNIT_PATH = join31(SYSTEMD_DIR, "cc-claw.service");
29759
+ PLIST_PATH = join32(homedir10(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
29760
+ SYSTEMD_DIR = join32(homedir10(), ".config", "systemd", "user");
29761
+ UNIT_PATH = join32(SYSTEMD_DIR, "cc-claw.service");
29194
29762
  }
29195
29763
  });
29196
29764
 
@@ -29387,7 +29955,7 @@ var status_exports = {};
29387
29955
  __export(status_exports, {
29388
29956
  statusCommand: () => statusCommand
29389
29957
  });
29390
- import { existsSync as existsSync30, statSync as statSync10 } from "fs";
29958
+ import { existsSync as existsSync31, statSync as statSync10 } from "fs";
29391
29959
  async function statusCommand(globalOpts, localOpts) {
29392
29960
  try {
29393
29961
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
@@ -29427,7 +29995,7 @@ async function statusCommand(globalOpts, localOpts) {
29427
29995
  const cwdRow = readDb.prepare("SELECT cwd FROM chat_cwd WHERE chat_id = ?").get(chatId);
29428
29996
  const voiceRow = readDb.prepare("SELECT enabled FROM chat_voice WHERE chat_id = ?").get(chatId);
29429
29997
  const usageRow = readDb.prepare("SELECT * FROM chat_usage WHERE chat_id = ?").get(chatId);
29430
- const dbStat = existsSync30(DB_PATH) ? statSync10(DB_PATH) : null;
29998
+ const dbStat = existsSync31(DB_PATH) ? statSync10(DB_PATH) : null;
29431
29999
  let daemonRunning = false;
29432
30000
  let daemonInfo = {};
29433
30001
  try {
@@ -29539,13 +30107,13 @@ __export(doctor_exports, {
29539
30107
  doctorCommand: () => doctorCommand,
29540
30108
  doctorErrors: () => doctorErrors
29541
30109
  });
29542
- import { existsSync as existsSync31, accessSync, constants } from "fs";
30110
+ import { existsSync as existsSync32, accessSync, constants } from "fs";
29543
30111
  import { execFileSync as execFileSync5 } from "child_process";
29544
30112
  async function doctorCommand(globalOpts, localOpts) {
29545
30113
  const checks = [];
29546
30114
  const dbChecks = checkDatabase();
29547
30115
  checks.push(...dbChecks);
29548
- if (existsSync31(DB_PATH)) {
30116
+ if (existsSync32(DB_PATH)) {
29549
30117
  try {
29550
30118
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
29551
30119
  const readDb = openDatabaseReadOnly2();
@@ -29571,7 +30139,7 @@ async function doctorCommand(globalOpts, localOpts) {
29571
30139
  checks.push({ name: "Database health", status: "error", message: err.message });
29572
30140
  }
29573
30141
  }
29574
- if (existsSync31(ENV_PATH)) {
30142
+ if (existsSync32(ENV_PATH)) {
29575
30143
  checks.push({ name: "Environment", status: "ok", message: `.env loaded` });
29576
30144
  } else {
29577
30145
  checks.push({ name: "Environment", status: "error", message: "No .env found", fix: "cc-claw setup" });
@@ -29614,7 +30182,7 @@ async function doctorCommand(globalOpts, localOpts) {
29614
30182
  } catch {
29615
30183
  }
29616
30184
  const tokenPath = `${DATA_PATH}/api-token`;
29617
- if (existsSync31(tokenPath)) {
30185
+ if (existsSync32(tokenPath)) {
29618
30186
  try {
29619
30187
  accessSync(tokenPath, constants.R_OK);
29620
30188
  checks.push({ name: "API token", status: "ok", message: "token file readable" });
@@ -29680,7 +30248,7 @@ async function doctorCommand(globalOpts, localOpts) {
29680
30248
  const errorChecks = checks.filter(
29681
30249
  (c) => ["Rate limits", "Content silence", "Spawn timeouts", "Other errors"].includes(c.name) && c.status !== "ok"
29682
30250
  );
29683
- if (errorChecks.length > 0 && existsSync31(ERROR_LOG_PATH)) {
30251
+ if (errorChecks.length > 0 && existsSync32(ERROR_LOG_PATH)) {
29684
30252
  try {
29685
30253
  const { writeFileSync: writeFileSync13 } = await import("fs");
29686
30254
  writeFileSync13(ERROR_LOG_PATH, "");
@@ -29809,10 +30377,10 @@ var logs_exports = {};
29809
30377
  __export(logs_exports, {
29810
30378
  logsCommand: () => logsCommand
29811
30379
  });
29812
- import { existsSync as existsSync32, readFileSync as readFileSync22, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
30380
+ import { existsSync as existsSync33, readFileSync as readFileSync22, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
29813
30381
  async function logsCommand(opts) {
29814
30382
  const logFile = opts.error ? ERROR_LOG_PATH : LOG_PATH;
29815
- if (!existsSync32(logFile)) {
30383
+ if (!existsSync33(logFile)) {
29816
30384
  outputError("LOG_NOT_FOUND", `Log file not found: ${logFile}`);
29817
30385
  process.exit(1);
29818
30386
  }
@@ -29965,11 +30533,11 @@ __export(gemini_exports, {
29965
30533
  geminiReorder: () => geminiReorder,
29966
30534
  geminiRotation: () => geminiRotation
29967
30535
  });
29968
- import { existsSync as existsSync34, mkdirSync as mkdirSync14, writeFileSync as writeFileSync10, readFileSync as readFileSync24, chmodSync } from "fs";
29969
- import { join as join32 } from "path";
30536
+ import { existsSync as existsSync35, mkdirSync as mkdirSync14, writeFileSync as writeFileSync10, readFileSync as readFileSync24, chmodSync } from "fs";
30537
+ import { join as join33 } from "path";
29970
30538
  import { createInterface as createInterface8 } from "readline";
29971
30539
  function requireDb() {
29972
- if (!existsSync34(DB_PATH)) {
30540
+ if (!existsSync35(DB_PATH)) {
29973
30541
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
29974
30542
  process.exit(1);
29975
30543
  }
@@ -29994,8 +30562,8 @@ async function resolveSlotId(idOrLabel) {
29994
30562
  function resolveOAuthEmail(configHome) {
29995
30563
  if (!configHome) return null;
29996
30564
  try {
29997
- const accountsPath = join32(configHome, ".gemini", "google_accounts.json");
29998
- if (!existsSync34(accountsPath)) return null;
30565
+ const accountsPath = join33(configHome, ".gemini", "google_accounts.json");
30566
+ if (!existsSync35(accountsPath)) return null;
29999
30567
  const accounts = JSON.parse(readFileSync24(accountsPath, "utf-8"));
30000
30568
  return accounts.active || null;
30001
30569
  } catch {
@@ -30078,14 +30646,14 @@ async function geminiAddKey(globalOpts, opts) {
30078
30646
  }
30079
30647
  async function geminiAddAccount(globalOpts, opts) {
30080
30648
  await requireWriteDb();
30081
- const slotsDir = join32(CC_CLAW_HOME, "gemini-slots");
30082
- if (!existsSync34(slotsDir)) mkdirSync14(slotsDir, { recursive: true });
30649
+ const slotsDir = join33(CC_CLAW_HOME, "gemini-slots");
30650
+ if (!existsSync35(slotsDir)) mkdirSync14(slotsDir, { recursive: true });
30083
30651
  const { addGeminiSlot: addGeminiSlot2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
30084
30652
  const tempId = Date.now();
30085
- const slotDir = join32(slotsDir, `slot-${tempId}`);
30653
+ const slotDir = join33(slotsDir, `slot-${tempId}`);
30086
30654
  mkdirSync14(slotDir, { recursive: true, mode: 448 });
30087
- mkdirSync14(join32(slotDir, ".gemini"), { recursive: true });
30088
- writeFileSync10(join32(slotDir, ".gemini", "settings.json"), JSON.stringify({
30655
+ mkdirSync14(join33(slotDir, ".gemini"), { recursive: true });
30656
+ writeFileSync10(join33(slotDir, ".gemini", "settings.json"), JSON.stringify({
30089
30657
  security: { auth: { selectedType: "oauth-personal" } }
30090
30658
  }, null, 2));
30091
30659
  console.log("");
@@ -30102,8 +30670,8 @@ async function geminiAddAccount(globalOpts, opts) {
30102
30670
  });
30103
30671
  } catch {
30104
30672
  }
30105
- const oauthPath = join32(slotDir, ".gemini", "oauth_creds.json");
30106
- if (!existsSync34(oauthPath)) {
30673
+ const oauthPath = join33(slotDir, ".gemini", "oauth_creds.json");
30674
+ if (!existsSync35(oauthPath)) {
30107
30675
  console.log(error2("\n No OAuth credentials found. Sign-in may have failed."));
30108
30676
  console.log(" The slot directory is preserved at: " + slotDir);
30109
30677
  console.log(" Re-run: cc-claw gemini add-account\n");
@@ -30111,7 +30679,7 @@ async function geminiAddAccount(globalOpts, opts) {
30111
30679
  }
30112
30680
  let accountEmail = "unknown";
30113
30681
  try {
30114
- const accounts = JSON.parse(__require("fs").readFileSync(join32(slotDir, ".gemini", "google_accounts.json"), "utf-8"));
30682
+ const accounts = JSON.parse(__require("fs").readFileSync(join33(slotDir, ".gemini", "google_accounts.json"), "utf-8"));
30115
30683
  accountEmail = accounts.active || accountEmail;
30116
30684
  } catch {
30117
30685
  }
@@ -30222,9 +30790,9 @@ async function geminiRelogin(globalOpts, idOrLabel) {
30222
30790
  outputError("NO_CONFIG", `Slot "${idOrLabel}" has no config directory \u2014 cannot re-login.`);
30223
30791
  return;
30224
30792
  }
30225
- const settingsPath = join32(slot.configHome, ".gemini", "settings.json");
30226
- if (!existsSync34(settingsPath)) {
30227
- mkdirSync14(join32(slot.configHome, ".gemini"), { recursive: true });
30793
+ const settingsPath = join33(slot.configHome, ".gemini", "settings.json");
30794
+ if (!existsSync35(settingsPath)) {
30795
+ mkdirSync14(join33(slot.configHome, ".gemini"), { recursive: true });
30228
30796
  writeFileSync10(settingsPath, JSON.stringify({
30229
30797
  security: { auth: { selectedType: "oauth-personal" } }
30230
30798
  }, null, 2));
@@ -30248,8 +30816,8 @@ async function geminiRelogin(globalOpts, idOrLabel) {
30248
30816
  });
30249
30817
  } catch {
30250
30818
  }
30251
- const oauthPath = join32(slot.configHome, ".gemini", "oauth_creds.json");
30252
- if (!existsSync34(oauthPath)) {
30819
+ const oauthPath = join33(slot.configHome, ".gemini", "oauth_creds.json");
30820
+ if (!existsSync35(oauthPath)) {
30253
30821
  console.log(error2("\n Re-login failed \u2014 no OAuth credentials found."));
30254
30822
  console.log(` Try again: cc-claw gemini re-login ${idOrLabel}
30255
30823
  `);
@@ -30259,7 +30827,7 @@ async function geminiRelogin(globalOpts, idOrLabel) {
30259
30827
  setGeminiSlotEnabled2(slotId, true);
30260
30828
  let accountEmail = slot.label;
30261
30829
  try {
30262
- const accounts = JSON.parse(readFileSync24(join32(slot.configHome, ".gemini", "google_accounts.json"), "utf-8"));
30830
+ const accounts = JSON.parse(readFileSync24(join33(slot.configHome, ".gemini", "google_accounts.json"), "utf-8"));
30263
30831
  if (accounts.active) accountEmail = accounts.active;
30264
30832
  } catch {
30265
30833
  }
@@ -30318,11 +30886,11 @@ __export(backend_cmd_factory_exports, {
30318
30886
  makeReorder: () => makeReorder,
30319
30887
  registerBackendSlotCommands: () => registerBackendSlotCommands
30320
30888
  });
30321
- import { existsSync as existsSync35, mkdirSync as mkdirSync15, readFileSync as readFileSync25 } from "fs";
30322
- import { join as join33 } from "path";
30889
+ import { existsSync as existsSync36, mkdirSync as mkdirSync15, readFileSync as readFileSync25 } from "fs";
30890
+ import { join as join34 } from "path";
30323
30891
  import { createInterface as createInterface9 } from "readline";
30324
30892
  function requireDb2() {
30325
- if (!existsSync35(DB_PATH)) {
30893
+ if (!existsSync36(DB_PATH)) {
30326
30894
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
30327
30895
  process.exit(1);
30328
30896
  }
@@ -30411,10 +30979,10 @@ function makeAddAccount(backend2, displayName) {
30411
30979
  process.exit(1);
30412
30980
  }
30413
30981
  await requireWriteDb2();
30414
- const slotsDir = join33(CC_CLAW_HOME, config2.slotsSubdir);
30415
- if (!existsSync35(slotsDir)) mkdirSync15(slotsDir, { recursive: true });
30982
+ const slotsDir = join34(CC_CLAW_HOME, config2.slotsSubdir);
30983
+ if (!existsSync36(slotsDir)) mkdirSync15(slotsDir, { recursive: true });
30416
30984
  const tempId = Date.now();
30417
- const slotDir = join33(slotsDir, `slot-${tempId}`);
30985
+ const slotDir = join34(slotsDir, `slot-${tempId}`);
30418
30986
  mkdirSync15(slotDir, { recursive: true, mode: 448 });
30419
30987
  if (config2.preSetup) config2.preSetup(slotDir);
30420
30988
  console.log("");
@@ -30665,12 +31233,12 @@ var init_backend_cmd_factory = __esm({
30665
31233
  envValue: (slotDir) => slotDir,
30666
31234
  envOverrides: { ANTHROPIC_API_KEY: void 0 },
30667
31235
  preSetup: (slotDir) => {
30668
- mkdirSync15(join33(slotDir, ".claude"), { recursive: true });
31236
+ mkdirSync15(join34(slotDir, ".claude"), { recursive: true });
30669
31237
  },
30670
31238
  verifyCredentials: (slotDir) => {
30671
- const claudeJson = join33(slotDir, ".claude.json");
30672
- const claudeJsonNested = join33(slotDir, ".claude", ".claude.json");
30673
- if (existsSync35(claudeJson)) {
31239
+ const claudeJson = join34(slotDir, ".claude.json");
31240
+ const claudeJsonNested = join34(slotDir, ".claude", ".claude.json");
31241
+ if (existsSync36(claudeJson)) {
30674
31242
  try {
30675
31243
  const data = JSON.parse(readFileSync25(claudeJson, "utf-8"));
30676
31244
  return Boolean(data.oauthAccount);
@@ -30678,7 +31246,7 @@ var init_backend_cmd_factory = __esm({
30678
31246
  return false;
30679
31247
  }
30680
31248
  }
30681
- if (existsSync35(claudeJsonNested)) {
31249
+ if (existsSync36(claudeJsonNested)) {
30682
31250
  try {
30683
31251
  const data = JSON.parse(readFileSync25(claudeJsonNested, "utf-8"));
30684
31252
  return Boolean(data.oauthAccount);
@@ -30701,8 +31269,8 @@ var init_backend_cmd_factory = __esm({
30701
31269
  } catch {
30702
31270
  }
30703
31271
  try {
30704
- const claudeJson = join33(slotDir, ".claude.json");
30705
- if (existsSync35(claudeJson)) {
31272
+ const claudeJson = join34(slotDir, ".claude.json");
31273
+ if (existsSync36(claudeJson)) {
30706
31274
  const data = JSON.parse(readFileSync25(claudeJson, "utf-8"));
30707
31275
  if (data.oauthAccount?.emailAddress) return data.oauthAccount.emailAddress;
30708
31276
  }
@@ -30718,11 +31286,11 @@ var init_backend_cmd_factory = __esm({
30718
31286
  envValue: (slotDir) => slotDir,
30719
31287
  envOverrides: { OPENAI_API_KEY: void 0 },
30720
31288
  verifyCredentials: (slotDir) => {
30721
- return existsSync35(join33(slotDir, "auth.json"));
31289
+ return existsSync36(join34(slotDir, "auth.json"));
30722
31290
  },
30723
31291
  extractLabel: (slotDir) => {
30724
31292
  try {
30725
- const authData = JSON.parse(readFileSync25(join33(slotDir, "auth.json"), "utf-8"));
31293
+ const authData = JSON.parse(readFileSync25(join34(slotDir, "auth.json"), "utf-8"));
30726
31294
  if (authData.email) return authData.email;
30727
31295
  if (authData.account_name) return authData.account_name;
30728
31296
  if (authData.user?.email) return authData.user.email;
@@ -30746,9 +31314,9 @@ __export(ollama_exports3, {
30746
31314
  ollamaRemove: () => ollamaRemove,
30747
31315
  ollamaTest: () => ollamaTest
30748
31316
  });
30749
- import { existsSync as existsSync36 } from "fs";
31317
+ import { existsSync as existsSync37 } from "fs";
30750
31318
  function requireDb3() {
30751
- if (!existsSync36(DB_PATH)) {
31319
+ if (!existsSync37(DB_PATH)) {
30752
31320
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
30753
31321
  process.exit(1);
30754
31322
  }
@@ -31007,12 +31575,12 @@ __export(backend_exports, {
31007
31575
  backendList: () => backendList,
31008
31576
  backendSet: () => backendSet
31009
31577
  });
31010
- import { existsSync as existsSync37 } from "fs";
31578
+ import { existsSync as existsSync38 } from "fs";
31011
31579
  async function backendList(globalOpts) {
31012
31580
  const { getAvailableAdapters: getAvailableAdapters3 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
31013
31581
  const chatId = resolveChatId2(globalOpts);
31014
31582
  let activeBackend = null;
31015
- if (existsSync37(DB_PATH)) {
31583
+ if (existsSync38(DB_PATH)) {
31016
31584
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
31017
31585
  const readDb = openDatabaseReadOnly2();
31018
31586
  try {
@@ -31043,7 +31611,7 @@ async function backendList(globalOpts) {
31043
31611
  }
31044
31612
  async function backendGet(globalOpts) {
31045
31613
  const chatId = resolveChatId2(globalOpts);
31046
- if (!existsSync37(DB_PATH)) {
31614
+ if (!existsSync38(DB_PATH)) {
31047
31615
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
31048
31616
  process.exit(1);
31049
31617
  }
@@ -31087,13 +31655,13 @@ __export(model_exports, {
31087
31655
  modelList: () => modelList,
31088
31656
  modelSet: () => modelSet
31089
31657
  });
31090
- import { existsSync as existsSync38 } from "fs";
31658
+ import { existsSync as existsSync39 } from "fs";
31091
31659
  async function modelList(globalOpts) {
31092
31660
  const chatId = resolveChatId2(globalOpts);
31093
31661
  const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
31094
31662
  const { getAdapter: getAdapter4, getAllAdapters: getAllAdapters5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
31095
31663
  let backendId = "claude";
31096
- if (existsSync38(DB_PATH)) {
31664
+ if (existsSync39(DB_PATH)) {
31097
31665
  const readDb = openDatabaseReadOnly2();
31098
31666
  try {
31099
31667
  const row = readDb.prepare("SELECT backend FROM chat_backend WHERE chat_id = ?").get(chatId);
@@ -31126,7 +31694,7 @@ async function modelList(globalOpts) {
31126
31694
  }
31127
31695
  async function modelGet(globalOpts) {
31128
31696
  const chatId = resolveChatId2(globalOpts);
31129
- if (!existsSync38(DB_PATH)) {
31697
+ if (!existsSync39(DB_PATH)) {
31130
31698
  outputError("DB_NOT_FOUND", "Database not found.");
31131
31699
  process.exit(1);
31132
31700
  }
@@ -31170,9 +31738,9 @@ __export(memory_exports2, {
31170
31738
  memoryList: () => memoryList,
31171
31739
  memorySearch: () => memorySearch
31172
31740
  });
31173
- import { existsSync as existsSync39 } from "fs";
31741
+ import { existsSync as existsSync40 } from "fs";
31174
31742
  async function memoryList(globalOpts) {
31175
- if (!existsSync39(DB_PATH)) {
31743
+ if (!existsSync40(DB_PATH)) {
31176
31744
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
31177
31745
  process.exit(1);
31178
31746
  }
@@ -31196,7 +31764,7 @@ async function memoryList(globalOpts) {
31196
31764
  });
31197
31765
  }
31198
31766
  async function memorySearch(globalOpts, query) {
31199
- if (!existsSync39(DB_PATH)) {
31767
+ if (!existsSync40(DB_PATH)) {
31200
31768
  outputError("DB_NOT_FOUND", "Database not found.");
31201
31769
  process.exit(1);
31202
31770
  }
@@ -31218,7 +31786,7 @@ async function memorySearch(globalOpts, query) {
31218
31786
  });
31219
31787
  }
31220
31788
  async function memoryHistory(globalOpts, opts) {
31221
- if (!existsSync39(DB_PATH)) {
31789
+ if (!existsSync40(DB_PATH)) {
31222
31790
  outputError("DB_NOT_FOUND", "Database not found.");
31223
31791
  process.exit(1);
31224
31792
  }
@@ -31266,7 +31834,7 @@ __export(cron_exports2, {
31266
31834
  cronList: () => cronList,
31267
31835
  cronRuns: () => cronRuns
31268
31836
  });
31269
- import { existsSync as existsSync40 } from "fs";
31837
+ import { existsSync as existsSync41 } from "fs";
31270
31838
  function parseFallbacks(raw) {
31271
31839
  return raw.slice(0, 3).map((f) => {
31272
31840
  const [backend2, ...rest] = f.split(":");
@@ -31287,7 +31855,7 @@ function parseAndValidateTimeout(raw) {
31287
31855
  return val;
31288
31856
  }
31289
31857
  async function cronList(globalOpts) {
31290
- if (!existsSync40(DB_PATH)) {
31858
+ if (!existsSync41(DB_PATH)) {
31291
31859
  outputError("DB_NOT_FOUND", "Database not found.");
31292
31860
  process.exit(1);
31293
31861
  }
@@ -31325,7 +31893,7 @@ async function cronList(globalOpts) {
31325
31893
  });
31326
31894
  }
31327
31895
  async function cronHealth(globalOpts) {
31328
- if (!existsSync40(DB_PATH)) {
31896
+ if (!existsSync41(DB_PATH)) {
31329
31897
  outputError("DB_NOT_FOUND", "Database not found.");
31330
31898
  process.exit(1);
31331
31899
  }
@@ -31486,7 +32054,7 @@ async function cronEdit(globalOpts, id, opts) {
31486
32054
  }
31487
32055
  }
31488
32056
  async function cronRuns(globalOpts, jobId, opts) {
31489
- if (!existsSync40(DB_PATH)) {
32057
+ if (!existsSync41(DB_PATH)) {
31490
32058
  outputError("DB_NOT_FOUND", "Database not found.");
31491
32059
  process.exit(1);
31492
32060
  }
@@ -31533,9 +32101,9 @@ __export(agents_exports, {
31533
32101
  runnersList: () => runnersList,
31534
32102
  tasksList: () => tasksList
31535
32103
  });
31536
- import { existsSync as existsSync41 } from "fs";
32104
+ import { existsSync as existsSync42 } from "fs";
31537
32105
  async function agentsList(globalOpts) {
31538
- if (!existsSync41(DB_PATH)) {
32106
+ if (!existsSync42(DB_PATH)) {
31539
32107
  outputError("DB_NOT_FOUND", "Database not found.");
31540
32108
  process.exit(1);
31541
32109
  }
@@ -31566,7 +32134,7 @@ async function agentsList(globalOpts) {
31566
32134
  });
31567
32135
  }
31568
32136
  async function tasksList(globalOpts) {
31569
- if (!existsSync41(DB_PATH)) {
32137
+ if (!existsSync42(DB_PATH)) {
31570
32138
  outputError("DB_NOT_FOUND", "Database not found.");
31571
32139
  process.exit(1);
31572
32140
  }
@@ -31694,10 +32262,10 @@ __export(db_exports, {
31694
32262
  dbPath: () => dbPath,
31695
32263
  dbStats: () => dbStats
31696
32264
  });
31697
- import { existsSync as existsSync42, statSync as statSync11, copyFileSync as copyFileSync3, mkdirSync as mkdirSync16 } from "fs";
32265
+ import { existsSync as existsSync43, statSync as statSync11, copyFileSync as copyFileSync3, mkdirSync as mkdirSync16 } from "fs";
31698
32266
  import { dirname as dirname7 } from "path";
31699
32267
  async function dbStats(globalOpts) {
31700
- if (!existsSync42(DB_PATH)) {
32268
+ if (!existsSync43(DB_PATH)) {
31701
32269
  outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
31702
32270
  process.exit(1);
31703
32271
  }
@@ -31705,7 +32273,7 @@ async function dbStats(globalOpts) {
31705
32273
  const readDb = openDatabaseReadOnly2();
31706
32274
  const mainSize = statSync11(DB_PATH).size;
31707
32275
  const walPath = DB_PATH + "-wal";
31708
- const walSize = existsSync42(walPath) ? statSync11(walPath).size : 0;
32276
+ const walSize = existsSync43(walPath) ? statSync11(walPath).size : 0;
31709
32277
  const tableNames = readDb.prepare(
31710
32278
  "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '%_fts%' ORDER BY name"
31711
32279
  ).all();
@@ -31739,7 +32307,7 @@ async function dbPath(globalOpts) {
31739
32307
  output({ path: DB_PATH }, (d) => d.path);
31740
32308
  }
31741
32309
  async function dbBackup(globalOpts, destPath) {
31742
- if (!existsSync42(DB_PATH)) {
32310
+ if (!existsSync43(DB_PATH)) {
31743
32311
  outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
31744
32312
  process.exit(1);
31745
32313
  }
@@ -31748,7 +32316,7 @@ async function dbBackup(globalOpts, destPath) {
31748
32316
  mkdirSync16(dirname7(dest), { recursive: true });
31749
32317
  copyFileSync3(DB_PATH, dest);
31750
32318
  const walPath = DB_PATH + "-wal";
31751
- if (existsSync42(walPath)) copyFileSync3(walPath, dest + "-wal");
32319
+ if (existsSync43(walPath)) copyFileSync3(walPath, dest + "-wal");
31752
32320
  output({ path: dest, sizeBytes: statSync11(dest).size }, (d) => {
31753
32321
  const b = d;
31754
32322
  return `
@@ -31777,9 +32345,9 @@ __export(usage_exports, {
31777
32345
  usageCost: () => usageCost,
31778
32346
  usageTokens: () => usageTokens
31779
32347
  });
31780
- import { existsSync as existsSync43 } from "fs";
32348
+ import { existsSync as existsSync44 } from "fs";
31781
32349
  function ensureDb() {
31782
- if (!existsSync43(DB_PATH)) {
32350
+ if (!existsSync44(DB_PATH)) {
31783
32351
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
31784
32352
  process.exit(1);
31785
32353
  }
@@ -31969,9 +32537,9 @@ __export(config_exports2, {
31969
32537
  configList: () => configList,
31970
32538
  configSet: () => configSet
31971
32539
  });
31972
- import { existsSync as existsSync44, readFileSync as readFileSync26 } from "fs";
32540
+ import { existsSync as existsSync45, readFileSync as readFileSync26 } from "fs";
31973
32541
  async function configList(globalOpts) {
31974
- if (!existsSync44(DB_PATH)) {
32542
+ if (!existsSync45(DB_PATH)) {
31975
32543
  outputError("DB_NOT_FOUND", "Database not found.");
31976
32544
  process.exit(1);
31977
32545
  }
@@ -32005,7 +32573,7 @@ async function configGet(globalOpts, key) {
32005
32573
  outputError("INVALID_KEY", `Unknown config key "${key}". Valid keys: ${RUNTIME_KEYS.join(", ")}`);
32006
32574
  process.exit(1);
32007
32575
  }
32008
- if (!existsSync44(DB_PATH)) {
32576
+ if (!existsSync45(DB_PATH)) {
32009
32577
  outputError("DB_NOT_FOUND", "Database not found.");
32010
32578
  process.exit(1);
32011
32579
  }
@@ -32051,7 +32619,7 @@ async function configSet(globalOpts, key, value) {
32051
32619
  }
32052
32620
  }
32053
32621
  async function configEnv(_globalOpts) {
32054
- if (!existsSync44(ENV_PATH)) {
32622
+ if (!existsSync45(ENV_PATH)) {
32055
32623
  outputError("ENV_NOT_FOUND", `No .env file at ${ENV_PATH}. Run cc-claw setup.`);
32056
32624
  process.exit(1);
32057
32625
  }
@@ -32105,9 +32673,9 @@ __export(session_exports, {
32105
32673
  sessionGet: () => sessionGet,
32106
32674
  sessionNew: () => sessionNew
32107
32675
  });
32108
- import { existsSync as existsSync45 } from "fs";
32676
+ import { existsSync as existsSync46 } from "fs";
32109
32677
  async function sessionGet(globalOpts) {
32110
- if (!existsSync45(DB_PATH)) {
32678
+ if (!existsSync46(DB_PATH)) {
32111
32679
  outputError("DB_NOT_FOUND", "Database not found.");
32112
32680
  process.exit(1);
32113
32681
  }
@@ -32168,9 +32736,9 @@ __export(permissions_exports, {
32168
32736
  verboseGet: () => verboseGet,
32169
32737
  verboseSet: () => verboseSet
32170
32738
  });
32171
- import { existsSync as existsSync46 } from "fs";
32739
+ import { existsSync as existsSync47 } from "fs";
32172
32740
  function ensureDb2() {
32173
- if (!existsSync46(DB_PATH)) {
32741
+ if (!existsSync47(DB_PATH)) {
32174
32742
  outputError("DB_NOT_FOUND", "Database not found.");
32175
32743
  process.exit(1);
32176
32744
  }
@@ -32317,9 +32885,9 @@ __export(cwd_exports, {
32317
32885
  cwdGet: () => cwdGet,
32318
32886
  cwdSet: () => cwdSet
32319
32887
  });
32320
- import { existsSync as existsSync47 } from "fs";
32888
+ import { existsSync as existsSync48 } from "fs";
32321
32889
  async function cwdGet(globalOpts) {
32322
- if (!existsSync47(DB_PATH)) {
32890
+ if (!existsSync48(DB_PATH)) {
32323
32891
  outputError("DB_NOT_FOUND", "Database not found.");
32324
32892
  process.exit(1);
32325
32893
  }
@@ -32381,9 +32949,9 @@ __export(voice_exports, {
32381
32949
  voiceGet: () => voiceGet,
32382
32950
  voiceSet: () => voiceSet
32383
32951
  });
32384
- import { existsSync as existsSync48 } from "fs";
32952
+ import { existsSync as existsSync49 } from "fs";
32385
32953
  async function voiceGet(globalOpts) {
32386
- if (!existsSync48(DB_PATH)) {
32954
+ if (!existsSync49(DB_PATH)) {
32387
32955
  outputError("DB_NOT_FOUND", "Database not found.");
32388
32956
  process.exit(1);
32389
32957
  }
@@ -32432,9 +33000,9 @@ __export(heartbeat_exports2, {
32432
33000
  heartbeatGet: () => heartbeatGet,
32433
33001
  heartbeatSet: () => heartbeatSet
32434
33002
  });
32435
- import { existsSync as existsSync49 } from "fs";
33003
+ import { existsSync as existsSync50 } from "fs";
32436
33004
  async function heartbeatGet(globalOpts) {
32437
- if (!existsSync49(DB_PATH)) {
33005
+ if (!existsSync50(DB_PATH)) {
32438
33006
  outputError("DB_NOT_FOUND", "Database not found.");
32439
33007
  process.exit(1);
32440
33008
  }
@@ -32645,9 +33213,9 @@ __export(summarizer_exports, {
32645
33213
  summarizerGet: () => summarizerGet,
32646
33214
  summarizerSet: () => summarizerSet
32647
33215
  });
32648
- import { existsSync as existsSync50 } from "fs";
33216
+ import { existsSync as existsSync51 } from "fs";
32649
33217
  async function summarizerGet(globalOpts) {
32650
- if (!existsSync50(DB_PATH)) {
33218
+ if (!existsSync51(DB_PATH)) {
32651
33219
  outputError("DB_NOT_FOUND", "Database not found.");
32652
33220
  process.exit(1);
32653
33221
  }
@@ -32691,9 +33259,9 @@ __export(thinking_exports, {
32691
33259
  thinkingGet: () => thinkingGet,
32692
33260
  thinkingSet: () => thinkingSet
32693
33261
  });
32694
- import { existsSync as existsSync51 } from "fs";
33262
+ import { existsSync as existsSync52 } from "fs";
32695
33263
  async function thinkingGet(globalOpts) {
32696
- if (!existsSync51(DB_PATH)) {
33264
+ if (!existsSync52(DB_PATH)) {
32697
33265
  outputError("DB_NOT_FOUND", "Database not found.");
32698
33266
  process.exit(1);
32699
33267
  }
@@ -32737,9 +33305,9 @@ __export(chats_exports, {
32737
33305
  chatsList: () => chatsList,
32738
33306
  chatsRemoveAlias: () => chatsRemoveAlias
32739
33307
  });
32740
- import { existsSync as existsSync52 } from "fs";
33308
+ import { existsSync as existsSync53 } from "fs";
32741
33309
  async function chatsList(_globalOpts) {
32742
- if (!existsSync52(DB_PATH)) {
33310
+ if (!existsSync53(DB_PATH)) {
32743
33311
  outputError("DB_NOT_FOUND", "Database not found.");
32744
33312
  process.exit(1);
32745
33313
  }
@@ -32867,9 +33435,9 @@ var mcps_exports2 = {};
32867
33435
  __export(mcps_exports2, {
32868
33436
  mcpsList: () => mcpsList
32869
33437
  });
32870
- import { existsSync as existsSync53 } from "fs";
33438
+ import { existsSync as existsSync54 } from "fs";
32871
33439
  async function mcpsList(_globalOpts) {
32872
- if (!existsSync53(DB_PATH)) {
33440
+ if (!existsSync54(DB_PATH)) {
32873
33441
  outputError("DB_NOT_FOUND", "Database not found.");
32874
33442
  process.exit(1);
32875
33443
  }
@@ -32906,11 +33474,11 @@ __export(chat_exports2, {
32906
33474
  chatSend: () => chatSend
32907
33475
  });
32908
33476
  import { request as httpRequest2 } from "http";
32909
- import { readFileSync as readFileSync27, existsSync as existsSync54 } from "fs";
33477
+ import { readFileSync as readFileSync27, existsSync as existsSync55 } from "fs";
32910
33478
  function getToken2() {
32911
33479
  if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
32912
33480
  try {
32913
- if (existsSync54(TOKEN_PATH2)) return readFileSync27(TOKEN_PATH2, "utf-8").trim();
33481
+ if (existsSync55(TOKEN_PATH2)) return readFileSync27(TOKEN_PATH2, "utf-8").trim();
32914
33482
  } catch {
32915
33483
  }
32916
33484
  return null;
@@ -33398,7 +33966,7 @@ __export(completion_exports, {
33398
33966
  completionCommand: () => completionCommand
33399
33967
  });
33400
33968
  import { writeFileSync as writeFileSync11, mkdirSync as mkdirSync17 } from "fs";
33401
- import { join as join34 } from "path";
33969
+ import { join as join35 } from "path";
33402
33970
  import { homedir as homedir11 } from "os";
33403
33971
  async function completionCommand(opts) {
33404
33972
  const shell = opts.shell ?? detectShell();
@@ -33414,10 +33982,10 @@ async function completionCommand(opts) {
33414
33982
  process.exit(1);
33415
33983
  }
33416
33984
  if (opts.install) {
33417
- const dir = join34(homedir11(), ".config", "cc-claw", "completions");
33985
+ const dir = join35(homedir11(), ".config", "cc-claw", "completions");
33418
33986
  mkdirSync17(dir, { recursive: true });
33419
33987
  const filename = shell === "zsh" ? "_cc-claw" : shell === "fish" ? "cc-claw.fish" : "cc-claw.bash";
33420
- const filepath = join34(dir, filename);
33988
+ const filepath = join35(dir, filename);
33421
33989
  writeFileSync11(filepath, script, "utf-8");
33422
33990
  console.log(`\u2713 Completion script written to ${filepath}
33423
33991
  `);
@@ -33590,9 +34158,9 @@ __export(evolve_exports2, {
33590
34158
  evolveStatus: () => evolveStatus,
33591
34159
  evolveUndo: () => evolveUndo
33592
34160
  });
33593
- import { existsSync as existsSync55 } from "fs";
34161
+ import { existsSync as existsSync56 } from "fs";
33594
34162
  function ensureDb3() {
33595
- if (!existsSync55(DB_PATH)) {
34163
+ if (!existsSync56(DB_PATH)) {
33596
34164
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
33597
34165
  process.exit(1);
33598
34166
  }
@@ -34066,10 +34634,10 @@ var init_optimize2 = __esm({
34066
34634
 
34067
34635
  // src/setup.ts
34068
34636
  var setup_exports = {};
34069
- import { existsSync as existsSync56, writeFileSync as writeFileSync12, readFileSync as readFileSync28, copyFileSync as copyFileSync4, mkdirSync as mkdirSync18, statSync as statSync12 } from "fs";
34637
+ import { existsSync as existsSync57, writeFileSync as writeFileSync12, readFileSync as readFileSync28, copyFileSync as copyFileSync4, mkdirSync as mkdirSync18, statSync as statSync12 } from "fs";
34070
34638
  import { execFileSync as execFileSync6 } from "child_process";
34071
34639
  import { createInterface as createInterface11 } from "readline";
34072
- import { join as join35 } from "path";
34640
+ import { join as join36 } from "path";
34073
34641
  function divider2() {
34074
34642
  console.log(dim("\u2500".repeat(55)));
34075
34643
  }
@@ -34144,10 +34712,10 @@ async function setup() {
34144
34712
  }
34145
34713
  console.log("");
34146
34714
  for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
34147
- if (!existsSync56(dir)) mkdirSync18(dir, { recursive: true });
34715
+ if (!existsSync57(dir)) mkdirSync18(dir, { recursive: true });
34148
34716
  }
34149
34717
  const env = {};
34150
- const envSource = existsSync56(ENV_PATH) ? ENV_PATH : existsSync56(".env") ? ".env" : null;
34718
+ const envSource = existsSync57(ENV_PATH) ? ENV_PATH : existsSync57(".env") ? ".env" : null;
34151
34719
  if (envSource) {
34152
34720
  console.log(yellow(` Found existing config at ${envSource} \u2014 your values will be preserved`));
34153
34721
  console.log(yellow(" unless you enter new ones. Just press Enter to keep existing values.\n"));
@@ -34157,8 +34725,8 @@ async function setup() {
34157
34725
  if (match) env[match[1].trim()] = match[2].trim();
34158
34726
  }
34159
34727
  }
34160
- const cwdDb = join35(process.cwd(), "cc-claw.db");
34161
- if (existsSync56(cwdDb) && !existsSync56(DB_PATH)) {
34728
+ const cwdDb = join36(process.cwd(), "cc-claw.db");
34729
+ if (existsSync57(cwdDb) && !existsSync57(DB_PATH)) {
34162
34730
  const { size } = statSync12(cwdDb);
34163
34731
  console.log(yellow(` Found existing database at ${cwdDb} (${(size / 1024).toFixed(0)}KB)`));
34164
34732
  const migrate = await confirm("Copy database to ~/.cc-claw/? (preserves memories & history)", true);