cc-claw 0.4.10 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +551 -209
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -23,7 +23,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
23
23
|
// src/paths.ts
|
|
24
24
|
import { homedir } from "os";
|
|
25
25
|
import { join } from "path";
|
|
26
|
-
var CC_CLAW_HOME, ENV_PATH, DATA_PATH, DB_PATH, LOGS_PATH, LOG_PATH, ERROR_LOG_PATH, WORKSPACE_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH;
|
|
26
|
+
var CC_CLAW_HOME, ENV_PATH, DATA_PATH, DB_PATH, LOGS_PATH, LOG_PATH, ERROR_LOG_PATH, IDENTITY_PATH, WORKSPACE_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH;
|
|
27
27
|
var init_paths = __esm({
|
|
28
28
|
"src/paths.ts"() {
|
|
29
29
|
"use strict";
|
|
@@ -34,6 +34,7 @@ var init_paths = __esm({
|
|
|
34
34
|
LOGS_PATH = join(CC_CLAW_HOME, "logs");
|
|
35
35
|
LOG_PATH = join(LOGS_PATH, "cc-claw.log");
|
|
36
36
|
ERROR_LOG_PATH = join(LOGS_PATH, "cc-claw.error.log");
|
|
37
|
+
IDENTITY_PATH = join(CC_CLAW_HOME, "identity");
|
|
37
38
|
WORKSPACE_PATH = join(CC_CLAW_HOME, "workspace");
|
|
38
39
|
SKILLS_PATH = join(WORKSPACE_PATH, "skills");
|
|
39
40
|
RUNNERS_PATH = join(CC_CLAW_HOME, "runners");
|
|
@@ -48,7 +49,7 @@ var VERSION;
|
|
|
48
49
|
var init_version = __esm({
|
|
49
50
|
"src/version.ts"() {
|
|
50
51
|
"use strict";
|
|
51
|
-
VERSION = true ? "0.
|
|
52
|
+
VERSION = true ? "0.5.0" : (() => {
|
|
52
53
|
try {
|
|
53
54
|
return JSON.parse(readFileSync(join2(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
54
55
|
} catch {
|
|
@@ -68,14 +69,14 @@ function ts() {
|
|
|
68
69
|
const absM = Math.abs(offset) % 60;
|
|
69
70
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}${sign}${pad(absH)}${pad(absM)}`;
|
|
70
71
|
}
|
|
71
|
-
function log(...
|
|
72
|
-
console.log(`[${ts()}]`, ...
|
|
72
|
+
function log(...args2) {
|
|
73
|
+
console.log(`[${ts()}]`, ...args2);
|
|
73
74
|
}
|
|
74
|
-
function warn(...
|
|
75
|
-
console.warn(`[${ts()}]`, ...
|
|
75
|
+
function warn(...args2) {
|
|
76
|
+
console.warn(`[${ts()}]`, ...args2);
|
|
76
77
|
}
|
|
77
|
-
function error(...
|
|
78
|
-
console.error(`[${ts()}]`, ...
|
|
78
|
+
function error(...args2) {
|
|
79
|
+
console.error(`[${ts()}]`, ...args2);
|
|
79
80
|
}
|
|
80
81
|
function errorMessage(err) {
|
|
81
82
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -964,6 +965,7 @@ __export(store_exports3, {
|
|
|
964
965
|
getMemoriesWithoutEmbeddings: () => getMemoriesWithoutEmbeddings,
|
|
965
966
|
getMode: () => getMode,
|
|
966
967
|
getModel: () => getModel,
|
|
968
|
+
getModelSignature: () => getModelSignature,
|
|
967
969
|
getPendingEscalation: () => getPendingEscalation,
|
|
968
970
|
getRecentBookmarks: () => getRecentBookmarks,
|
|
969
971
|
getRecentMemories: () => getRecentMemories,
|
|
@@ -1010,6 +1012,7 @@ __export(store_exports3, {
|
|
|
1010
1012
|
setHeartbeatConfig: () => setHeartbeatConfig,
|
|
1011
1013
|
setMode: () => setMode,
|
|
1012
1014
|
setModel: () => setModel,
|
|
1015
|
+
setModelSignature: () => setModelSignature,
|
|
1013
1016
|
setResponseStyle: () => setResponseStyle,
|
|
1014
1017
|
setSessionId: () => setSessionId,
|
|
1015
1018
|
setSessionStartedAt: () => setSessionStartedAt,
|
|
@@ -1486,6 +1489,12 @@ function initDatabase() {
|
|
|
1486
1489
|
style TEXT NOT NULL DEFAULT 'normal'
|
|
1487
1490
|
);
|
|
1488
1491
|
`);
|
|
1492
|
+
db.exec(`
|
|
1493
|
+
CREATE TABLE IF NOT EXISTS chat_model_signature (
|
|
1494
|
+
chat_id TEXT PRIMARY KEY,
|
|
1495
|
+
value TEXT NOT NULL DEFAULT 'off'
|
|
1496
|
+
);
|
|
1497
|
+
`);
|
|
1489
1498
|
}
|
|
1490
1499
|
function getDb() {
|
|
1491
1500
|
return db;
|
|
@@ -1629,6 +1638,17 @@ function setResponseStyle(chatId, style) {
|
|
|
1629
1638
|
ON CONFLICT(chat_id) DO UPDATE SET style = excluded.style
|
|
1630
1639
|
`).run(chatId, style);
|
|
1631
1640
|
}
|
|
1641
|
+
function getModelSignature(chatId) {
|
|
1642
|
+
const row = db.prepare("SELECT value FROM chat_model_signature WHERE chat_id = ?").get(chatId);
|
|
1643
|
+
return row?.value ?? "off";
|
|
1644
|
+
}
|
|
1645
|
+
function setModelSignature(chatId, value) {
|
|
1646
|
+
db.prepare(`
|
|
1647
|
+
INSERT INTO chat_model_signature (chat_id, value)
|
|
1648
|
+
VALUES (?, ?)
|
|
1649
|
+
ON CONFLICT(chat_id) DO UPDATE SET value = excluded.value
|
|
1650
|
+
`).run(chatId, value);
|
|
1651
|
+
}
|
|
1632
1652
|
function getSessionId(chatId) {
|
|
1633
1653
|
const row = db.prepare(
|
|
1634
1654
|
"SELECT session_id FROM sessions WHERE chat_id = ?"
|
|
@@ -2533,11 +2553,13 @@ var init_env = __esm({
|
|
|
2533
2553
|
// src/backends/claude.ts
|
|
2534
2554
|
import { existsSync } from "fs";
|
|
2535
2555
|
import { execSync } from "child_process";
|
|
2556
|
+
import { join as join3 } from "path";
|
|
2536
2557
|
var ADAPTIVE_MODELS, ClaudeAdapter;
|
|
2537
2558
|
var init_claude = __esm({
|
|
2538
2559
|
"src/backends/claude.ts"() {
|
|
2539
2560
|
"use strict";
|
|
2540
2561
|
init_env();
|
|
2562
|
+
init_paths();
|
|
2541
2563
|
ADAPTIVE_MODELS = /* @__PURE__ */ new Set(["claude-opus-4-6", "claude-sonnet-4-6"]);
|
|
2542
2564
|
ClaudeAdapter = class {
|
|
2543
2565
|
id = "claude";
|
|
@@ -2617,37 +2639,41 @@ var init_claude = __esm({
|
|
|
2617
2639
|
return { envOverrides: { MAX_THINKING_TOKENS: budgets[level] ?? "16384" } };
|
|
2618
2640
|
}
|
|
2619
2641
|
buildSpawnConfig(opts) {
|
|
2620
|
-
const
|
|
2642
|
+
const args2 = [
|
|
2621
2643
|
"-p",
|
|
2622
2644
|
opts.prompt,
|
|
2623
2645
|
"--output-format",
|
|
2624
2646
|
"stream-json",
|
|
2625
2647
|
"--verbose",
|
|
2626
2648
|
"--max-turns",
|
|
2627
|
-
|
|
2649
|
+
String(opts.maxTurns ?? 25)
|
|
2628
2650
|
];
|
|
2629
|
-
if (opts.sessionId)
|
|
2630
|
-
if (opts.model)
|
|
2651
|
+
if (opts.sessionId) args2.push("--resume", opts.sessionId);
|
|
2652
|
+
if (opts.model) args2.push("--model", opts.model);
|
|
2631
2653
|
switch (opts.permMode) {
|
|
2632
2654
|
case "plan":
|
|
2633
|
-
|
|
2634
|
-
|
|
2655
|
+
args2.push("--permission-mode", "plan");
|
|
2656
|
+
args2.push("--strict-mcp-config");
|
|
2635
2657
|
break;
|
|
2636
2658
|
case "safe":
|
|
2637
|
-
|
|
2659
|
+
args2.push("--permission-mode", "bypassPermissions");
|
|
2638
2660
|
if (opts.allowedTools.length > 0) {
|
|
2639
|
-
|
|
2661
|
+
args2.push("--tools", opts.allowedTools.join(","));
|
|
2640
2662
|
} else {
|
|
2641
|
-
|
|
2663
|
+
args2.push("--tools", "Read,Glob,Grep");
|
|
2642
2664
|
}
|
|
2643
2665
|
break;
|
|
2644
2666
|
case "yolo":
|
|
2645
2667
|
default:
|
|
2646
|
-
|
|
2647
|
-
|
|
2668
|
+
args2.push("--permission-mode", "bypassPermissions");
|
|
2669
|
+
args2.push("--allow-dangerously-skip-permissions");
|
|
2648
2670
|
break;
|
|
2649
2671
|
}
|
|
2650
|
-
|
|
2672
|
+
const identityFile = join3(IDENTITY_PATH, "CC-CLAW.md");
|
|
2673
|
+
if (existsSync(identityFile)) {
|
|
2674
|
+
args2.push("--append-system-prompt-file", identityFile);
|
|
2675
|
+
}
|
|
2676
|
+
return { executable: this.getExecutablePath(), args: args2, cwd: opts.cwd };
|
|
2651
2677
|
}
|
|
2652
2678
|
parseLine(raw) {
|
|
2653
2679
|
if (typeof raw !== "object" || raw === null) return [];
|
|
@@ -2723,11 +2749,13 @@ var init_claude = __esm({
|
|
|
2723
2749
|
// src/backends/gemini.ts
|
|
2724
2750
|
import { existsSync as existsSync2 } from "fs";
|
|
2725
2751
|
import { execSync as execSync2 } from "child_process";
|
|
2752
|
+
import { join as join4 } from "path";
|
|
2726
2753
|
var GeminiAdapter;
|
|
2727
2754
|
var init_gemini = __esm({
|
|
2728
2755
|
"src/backends/gemini.ts"() {
|
|
2729
2756
|
"use strict";
|
|
2730
2757
|
init_env();
|
|
2758
|
+
init_paths();
|
|
2731
2759
|
GeminiAdapter = class {
|
|
2732
2760
|
id = "gemini";
|
|
2733
2761
|
displayName = "Gemini";
|
|
@@ -2774,7 +2802,12 @@ var init_gemini = __esm({
|
|
|
2774
2802
|
return this._resolvedPath;
|
|
2775
2803
|
}
|
|
2776
2804
|
getEnv(thinkingOverrides) {
|
|
2777
|
-
|
|
2805
|
+
const env = buildBaseEnv(thinkingOverrides);
|
|
2806
|
+
const identityFile = join4(IDENTITY_PATH, "CC-CLAW.md");
|
|
2807
|
+
if (existsSync2(identityFile)) {
|
|
2808
|
+
env.GEMINI_SYSTEM_MD = identityFile;
|
|
2809
|
+
}
|
|
2810
|
+
return env;
|
|
2778
2811
|
}
|
|
2779
2812
|
applyThinkingConfig(level, _model) {
|
|
2780
2813
|
if (level === "auto" || level === "high") return {};
|
|
@@ -2785,7 +2818,7 @@ var init_gemini = __esm({
|
|
|
2785
2818
|
return {};
|
|
2786
2819
|
}
|
|
2787
2820
|
buildSpawnConfig(opts) {
|
|
2788
|
-
const
|
|
2821
|
+
const args2 = [
|
|
2789
2822
|
"-p",
|
|
2790
2823
|
opts.prompt,
|
|
2791
2824
|
"-o",
|
|
@@ -2793,21 +2826,21 @@ var init_gemini = __esm({
|
|
|
2793
2826
|
"--allowed-mcp-server-names=__none__"
|
|
2794
2827
|
// nonexistent name = skip all user MCP servers (avoids crash/hang from broken servers)
|
|
2795
2828
|
];
|
|
2796
|
-
if (opts.sessionId)
|
|
2797
|
-
if (opts.model)
|
|
2829
|
+
if (opts.sessionId) args2.push("--resume", opts.sessionId);
|
|
2830
|
+
if (opts.model) args2.push("-m", opts.model);
|
|
2798
2831
|
switch (opts.permMode) {
|
|
2799
2832
|
case "plan":
|
|
2800
|
-
|
|
2833
|
+
args2.push("--approval-mode", "plan");
|
|
2801
2834
|
break;
|
|
2802
2835
|
case "safe":
|
|
2803
|
-
|
|
2836
|
+
args2.push("--approval-mode", "plan");
|
|
2804
2837
|
break;
|
|
2805
2838
|
case "yolo":
|
|
2806
2839
|
default:
|
|
2807
|
-
|
|
2840
|
+
args2.push("--yolo");
|
|
2808
2841
|
break;
|
|
2809
2842
|
}
|
|
2810
|
-
return { executable: this.getExecutablePath(), args, cwd: opts.cwd };
|
|
2843
|
+
return { executable: this.getExecutablePath(), args: args2, cwd: opts.cwd };
|
|
2811
2844
|
}
|
|
2812
2845
|
parseLine(raw) {
|
|
2813
2846
|
if (typeof raw !== "object" || raw === null) return [];
|
|
@@ -2942,31 +2975,31 @@ var init_codex = __esm({
|
|
|
2942
2975
|
}
|
|
2943
2976
|
buildSpawnConfig(opts) {
|
|
2944
2977
|
const isResume = !!opts.sessionId;
|
|
2945
|
-
const
|
|
2978
|
+
const args2 = [];
|
|
2946
2979
|
if (isResume) {
|
|
2947
|
-
|
|
2980
|
+
args2.push("exec", "resume", opts.sessionId, opts.prompt, "--json");
|
|
2948
2981
|
} else {
|
|
2949
|
-
|
|
2982
|
+
args2.push("exec", opts.prompt, "--json");
|
|
2950
2983
|
}
|
|
2951
|
-
if (opts.model)
|
|
2984
|
+
if (opts.model) args2.push("-m", opts.model);
|
|
2952
2985
|
switch (opts.permMode) {
|
|
2953
2986
|
case "plan":
|
|
2954
|
-
|
|
2987
|
+
args2.push("--sandbox", "read-only");
|
|
2955
2988
|
break;
|
|
2956
2989
|
case "safe":
|
|
2957
|
-
|
|
2990
|
+
args2.push("--sandbox", "read-only");
|
|
2958
2991
|
break;
|
|
2959
2992
|
case "yolo":
|
|
2960
2993
|
default:
|
|
2961
|
-
|
|
2994
|
+
args2.push("--dangerously-bypass-approvals-and-sandbox");
|
|
2962
2995
|
break;
|
|
2963
2996
|
}
|
|
2964
2997
|
if (!isResume && opts.cwd) {
|
|
2965
|
-
|
|
2998
|
+
args2.push("-C", opts.cwd);
|
|
2966
2999
|
}
|
|
2967
3000
|
return {
|
|
2968
3001
|
executable: this.getExecutablePath(),
|
|
2969
|
-
args,
|
|
3002
|
+
args: args2,
|
|
2970
3003
|
// For resume, pass cwd via spawn option since -C isn't supported
|
|
2971
3004
|
cwd: isResume ? opts.cwd : void 0
|
|
2972
3005
|
};
|
|
@@ -3261,7 +3294,7 @@ You are a proactive personal AI assistant running inside a Telegram bot powered
|
|
|
3261
3294
|
## Identity
|
|
3262
3295
|
- You serve one user through Telegram. Be helpful, concise, and proactive.
|
|
3263
3296
|
- You can switch between multiple AI backends (Claude, Gemini, Codex) \u2014 the user controls which one is active.
|
|
3264
|
-
- Your name, personality, and behavioral rules are defined in this file. The user can customize everything by editing ~/.cc-claw/
|
|
3297
|
+
- Your name, personality, and behavioral rules are defined in this file. The user can customize everything by editing ~/.cc-claw/identity/SOUL.md.
|
|
3265
3298
|
|
|
3266
3299
|
## Capabilities
|
|
3267
3300
|
- **Coding**: Read, write, and edit files. Run shell commands. Full agentic coding.
|
|
@@ -3278,6 +3311,15 @@ You are a proactive personal AI assistant running inside a Telegram bot powered
|
|
|
3278
3311
|
- When you learn something about the user's preferences, include \`[UPDATE_USER:key=value]\` in your response to suggest saving it.
|
|
3279
3312
|
- For the heartbeat: if nothing needs attention, respond with exactly \`HEARTBEAT_OK\` and nothing else.
|
|
3280
3313
|
- Always use the user's timezone when discussing times and dates.
|
|
3314
|
+
|
|
3315
|
+
## Identity File Maintenance
|
|
3316
|
+
- Keep SOUL.md under 100 lines. Every line costs tokens on every message.
|
|
3317
|
+
- Before adding content to SOUL.md or USER.md, consider if it belongs in:
|
|
3318
|
+
- A context file (context/*.md) for topic-specific knowledge
|
|
3319
|
+
- A skill (skills/) for reusable instructions
|
|
3320
|
+
- A memory (via /remember) for facts
|
|
3321
|
+
- Never duplicate information already in memories or context files.
|
|
3322
|
+
- When updating identity files, remove outdated content first.
|
|
3281
3323
|
`;
|
|
3282
3324
|
DEFAULT_USER = `# User Profile
|
|
3283
3325
|
|
|
@@ -3298,7 +3340,7 @@ You are an expert user and operator of the CC-Claw architecture. Because you are
|
|
|
3298
3340
|
- **Database**: All state is stored locally in SQLite (\`~/.cc-claw/data/cc-claw.db\`), ensuring total privacy and fast local access for memory, jobs, and orchestration.
|
|
3299
3341
|
|
|
3300
3342
|
## 2. Identity & Context (The "Brain")
|
|
3301
|
-
- **SOUL & USER**: Your personality is defined in \`~/.cc-claw/
|
|
3343
|
+
- **SOUL & USER**: Your personality is defined in \`~/.cc-claw/identity/SOUL.md\`, and the user's profile is in \`~/.cc-claw/identity/USER.md\`. These are the single source of truth for your behavior.
|
|
3302
3344
|
- **On-Demand Context**: The \`context/\` directory holds files like this one. They are injected into your prompt only when triggered by relevant semantic keywords, keeping your context window lean.
|
|
3303
3345
|
- **Proactive Memory**: You can autonomously save user facts and preferences to the database by writing \`[UPDATE_USER:key=value]\` in your replies.
|
|
3304
3346
|
|
|
@@ -3326,9 +3368,49 @@ If the user asks *how* to do something with CC-Claw, use this expertise to sugge
|
|
|
3326
3368
|
});
|
|
3327
3369
|
|
|
3328
3370
|
// src/bootstrap/init.ts
|
|
3329
|
-
import {
|
|
3330
|
-
|
|
3371
|
+
import {
|
|
3372
|
+
existsSync as existsSync4,
|
|
3373
|
+
writeFileSync,
|
|
3374
|
+
mkdirSync,
|
|
3375
|
+
readFileSync as readFileSync2,
|
|
3376
|
+
statSync,
|
|
3377
|
+
copyFileSync,
|
|
3378
|
+
unlinkSync
|
|
3379
|
+
} from "fs";
|
|
3380
|
+
import { join as join5 } from "path";
|
|
3381
|
+
function migrateFile(legacyPath, newPath, label2) {
|
|
3382
|
+
if (existsSync4(newPath)) return false;
|
|
3383
|
+
if (existsSync4(legacyPath)) {
|
|
3384
|
+
copyFileSync(legacyPath, newPath);
|
|
3385
|
+
const copied = readFileSync2(newPath, "utf-8");
|
|
3386
|
+
if (copied.length > 0) {
|
|
3387
|
+
unlinkSync(legacyPath);
|
|
3388
|
+
log(`[bootstrap] Migrated ${label2} from workspace/ to identity/`);
|
|
3389
|
+
return true;
|
|
3390
|
+
}
|
|
3391
|
+
log(`[bootstrap] WARNING: Migration verification failed for ${label2}, keeping original`);
|
|
3392
|
+
return false;
|
|
3393
|
+
}
|
|
3394
|
+
return false;
|
|
3395
|
+
}
|
|
3331
3396
|
function bootstrapWorkspaceFiles() {
|
|
3397
|
+
if (!existsSync4(IDENTITY_PATH)) {
|
|
3398
|
+
mkdirSync(IDENTITY_PATH, { recursive: true });
|
|
3399
|
+
log("[bootstrap] Created identity/ directory");
|
|
3400
|
+
}
|
|
3401
|
+
if (!existsSync4(WORKSPACE_PATH)) {
|
|
3402
|
+
mkdirSync(WORKSPACE_PATH, { recursive: true });
|
|
3403
|
+
}
|
|
3404
|
+
migrateFile(LEGACY_SOUL_PATH, SOUL_PATH, "SOUL.md");
|
|
3405
|
+
migrateFile(LEGACY_USER_PATH, USER_PATH, "USER.md");
|
|
3406
|
+
if (existsSync4(LEGACY_CLAUDE_MD)) {
|
|
3407
|
+
unlinkSync(LEGACY_CLAUDE_MD);
|
|
3408
|
+
log("[bootstrap] Removed legacy workspace/CLAUDE.md (replaced by identity/CC-CLAW.md)");
|
|
3409
|
+
}
|
|
3410
|
+
if (existsSync4(LEGACY_GEMINI_MD)) {
|
|
3411
|
+
unlinkSync(LEGACY_GEMINI_MD);
|
|
3412
|
+
log("[bootstrap] Removed legacy workspace/GEMINI.md (replaced by identity/CC-CLAW.md)");
|
|
3413
|
+
}
|
|
3332
3414
|
if (!existsSync4(SOUL_PATH)) {
|
|
3333
3415
|
writeFileSync(SOUL_PATH, DEFAULT_SOUL, "utf-8");
|
|
3334
3416
|
log("[bootstrap] Created default SOUL.md");
|
|
@@ -3347,11 +3429,32 @@ function bootstrapWorkspaceFiles() {
|
|
|
3347
3429
|
mkdirSync(CONTEXT_DIR, { recursive: true });
|
|
3348
3430
|
log("[bootstrap] Created context/ directory");
|
|
3349
3431
|
}
|
|
3350
|
-
const expertisePath =
|
|
3432
|
+
const expertisePath = join5(CONTEXT_DIR, "cc-claw-expertise.md");
|
|
3351
3433
|
if (!existsSync4(expertisePath)) {
|
|
3352
3434
|
writeFileSync(expertisePath, DEFAULT_EXPERTISE, "utf-8");
|
|
3353
3435
|
log("[bootstrap] Created default context/cc-claw-expertise.md");
|
|
3354
3436
|
}
|
|
3437
|
+
const IGNORE_CONTENT = [
|
|
3438
|
+
"# CC-Claw workspace: only essential files are scanned by coding CLIs.",
|
|
3439
|
+
"# Agents can still access any file via absolute path when asked.",
|
|
3440
|
+
"*",
|
|
3441
|
+
"!.gitignore",
|
|
3442
|
+
"!.geminiignore",
|
|
3443
|
+
"!AGENTS.md",
|
|
3444
|
+
"!context/",
|
|
3445
|
+
"!context/**",
|
|
3446
|
+
""
|
|
3447
|
+
].join("\n");
|
|
3448
|
+
const gitignorePath = join5(WORKSPACE_PATH, ".gitignore");
|
|
3449
|
+
if (!existsSync4(gitignorePath)) {
|
|
3450
|
+
writeFileSync(gitignorePath, IGNORE_CONTENT, "utf-8");
|
|
3451
|
+
log("[bootstrap] Created .gitignore (workspace allowlist)");
|
|
3452
|
+
}
|
|
3453
|
+
const geminiignorePath = join5(WORKSPACE_PATH, ".geminiignore");
|
|
3454
|
+
if (!existsSync4(geminiignorePath)) {
|
|
3455
|
+
writeFileSync(geminiignorePath, IGNORE_CONTENT, "utf-8");
|
|
3456
|
+
log("[bootstrap] Created .geminiignore (workspace allowlist)");
|
|
3457
|
+
}
|
|
3355
3458
|
syncNativeCliFiles();
|
|
3356
3459
|
}
|
|
3357
3460
|
function syncNativeCliFiles() {
|
|
@@ -3360,15 +3463,23 @@ function syncNativeCliFiles() {
|
|
|
3360
3463
|
try {
|
|
3361
3464
|
soul = readFileSync2(SOUL_PATH, "utf-8").trim();
|
|
3362
3465
|
} catch {
|
|
3466
|
+
try {
|
|
3467
|
+
soul = readFileSync2(LEGACY_SOUL_PATH, "utf-8").trim();
|
|
3468
|
+
} catch {
|
|
3469
|
+
}
|
|
3363
3470
|
}
|
|
3364
3471
|
try {
|
|
3365
3472
|
user = readFileSync2(USER_PATH, "utf-8").trim();
|
|
3366
3473
|
} catch {
|
|
3474
|
+
try {
|
|
3475
|
+
user = readFileSync2(LEGACY_USER_PATH, "utf-8").trim();
|
|
3476
|
+
} catch {
|
|
3477
|
+
}
|
|
3367
3478
|
}
|
|
3368
3479
|
try {
|
|
3369
3480
|
const soulMtime = existsSync4(SOUL_PATH) ? statSync(SOUL_PATH).mtimeMs : 0;
|
|
3370
3481
|
const userMtime = existsSync4(USER_PATH) ? statSync(USER_PATH).mtimeMs : 0;
|
|
3371
|
-
if (soulMtime === lastSoulMtime && userMtime === lastUserMtime) {
|
|
3482
|
+
if (soulMtime > 0 && soulMtime === lastSoulMtime && userMtime === lastUserMtime) {
|
|
3372
3483
|
return;
|
|
3373
3484
|
}
|
|
3374
3485
|
lastSoulMtime = soulMtime;
|
|
@@ -3402,27 +3513,26 @@ function syncNativeCliFiles() {
|
|
|
3402
3513
|
"- Skills are in ~/.cc-claw/workspace/skills/",
|
|
3403
3514
|
""
|
|
3404
3515
|
].join("\n");
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
}
|
|
3411
|
-
writeFileSync(filePath, content, "utf-8");
|
|
3412
|
-
}
|
|
3413
|
-
log(`[bootstrap] Synced SOUL.md + USER.md \u2192 ${NATIVE_CLI_FILES.join(", ")}`);
|
|
3516
|
+
const ccClawPath = join5(IDENTITY_PATH, "CC-CLAW.md");
|
|
3517
|
+
writeFileSync(ccClawPath, nativeContent, "utf-8");
|
|
3518
|
+
const agentsPath = join5(WORKSPACE_PATH, "AGENTS.md");
|
|
3519
|
+
writeFileSync(agentsPath, nativeContent, "utf-8");
|
|
3520
|
+
log("[bootstrap] Synced SOUL.md + USER.md \u2192 identity/CC-CLAW.md, workspace/AGENTS.md");
|
|
3414
3521
|
}
|
|
3415
|
-
var SOUL_PATH, USER_PATH, CONTEXT_DIR,
|
|
3522
|
+
var SOUL_PATH, USER_PATH, CONTEXT_DIR, LEGACY_SOUL_PATH, LEGACY_USER_PATH, LEGACY_CLAUDE_MD, LEGACY_GEMINI_MD, lastSoulMtime, lastUserMtime;
|
|
3416
3523
|
var init_init = __esm({
|
|
3417
3524
|
"src/bootstrap/init.ts"() {
|
|
3418
3525
|
"use strict";
|
|
3419
3526
|
init_paths();
|
|
3420
3527
|
init_defaults();
|
|
3421
3528
|
init_log();
|
|
3422
|
-
SOUL_PATH =
|
|
3423
|
-
USER_PATH =
|
|
3424
|
-
CONTEXT_DIR =
|
|
3425
|
-
|
|
3529
|
+
SOUL_PATH = join5(IDENTITY_PATH, "SOUL.md");
|
|
3530
|
+
USER_PATH = join5(IDENTITY_PATH, "USER.md");
|
|
3531
|
+
CONTEXT_DIR = join5(WORKSPACE_PATH, "context");
|
|
3532
|
+
LEGACY_SOUL_PATH = join5(WORKSPACE_PATH, "SOUL.md");
|
|
3533
|
+
LEGACY_USER_PATH = join5(WORKSPACE_PATH, "USER.md");
|
|
3534
|
+
LEGACY_CLAUDE_MD = join5(WORKSPACE_PATH, "CLAUDE.md");
|
|
3535
|
+
LEGACY_GEMINI_MD = join5(WORKSPACE_PATH, "GEMINI.md");
|
|
3426
3536
|
lastSoulMtime = 0;
|
|
3427
3537
|
lastUserMtime = 0;
|
|
3428
3538
|
}
|
|
@@ -3430,7 +3540,7 @@ var init_init = __esm({
|
|
|
3430
3540
|
|
|
3431
3541
|
// src/bootstrap/loader.ts
|
|
3432
3542
|
import { readFileSync as readFileSync3, existsSync as existsSync5, readdirSync } from "fs";
|
|
3433
|
-
import { join as
|
|
3543
|
+
import { join as join6 } from "path";
|
|
3434
3544
|
function searchContext(userMessage) {
|
|
3435
3545
|
if (!existsSync5(CONTEXT_DIR2)) return null;
|
|
3436
3546
|
const msgWords = new Set(
|
|
@@ -3441,7 +3551,7 @@ function searchContext(userMessage) {
|
|
|
3441
3551
|
try {
|
|
3442
3552
|
const files = readdirSync(CONTEXT_DIR2).filter((f) => f.endsWith(".md"));
|
|
3443
3553
|
for (const file of files) {
|
|
3444
|
-
const filePath =
|
|
3554
|
+
const filePath = join6(CONTEXT_DIR2, file);
|
|
3445
3555
|
try {
|
|
3446
3556
|
const content = readFileSync3(filePath, "utf-8").trim();
|
|
3447
3557
|
if (!content) continue;
|
|
@@ -3471,7 +3581,10 @@ function searchContext(userMessage) {
|
|
|
3471
3581
|
}
|
|
3472
3582
|
async function assembleBootstrapPrompt(userMessage, tier = "full", chatId, permMode, responseStyle) {
|
|
3473
3583
|
const sections = [];
|
|
3474
|
-
|
|
3584
|
+
if (Date.now() - lastSyncMs >= 5e3) {
|
|
3585
|
+
syncNativeCliFiles();
|
|
3586
|
+
lastSyncMs = Date.now();
|
|
3587
|
+
}
|
|
3475
3588
|
if (permMode && permMode !== "yolo") {
|
|
3476
3589
|
sections.push(buildPermissionNotice(permMode));
|
|
3477
3590
|
}
|
|
@@ -3489,7 +3602,7 @@ async function assembleBootstrapPrompt(userMessage, tier = "full", chatId, permM
|
|
|
3489
3602
|
${ctx}`);
|
|
3490
3603
|
}
|
|
3491
3604
|
}
|
|
3492
|
-
if (chatId && tier !== "slim") {
|
|
3605
|
+
if (chatId && tier !== "slim" && tier !== "chat") {
|
|
3493
3606
|
const bridge = consumeContextBridge(chatId);
|
|
3494
3607
|
if (bridge) {
|
|
3495
3608
|
sections.push(bridge);
|
|
@@ -3562,7 +3675,7 @@ ${boardText}`);
|
|
|
3562
3675
|
return null;
|
|
3563
3676
|
}
|
|
3564
3677
|
}
|
|
3565
|
-
var CONTEXT_DIR2, MAX_CONTEXT_CHARS, ACTIVITY_TOKEN_BUDGET, INBOX_TOKEN_BUDGET, WHITEBOARD_TOKEN_BUDGET;
|
|
3678
|
+
var lastSyncMs, CONTEXT_DIR2, MAX_CONTEXT_CHARS, ACTIVITY_TOKEN_BUDGET, INBOX_TOKEN_BUDGET, WHITEBOARD_TOKEN_BUDGET;
|
|
3566
3679
|
var init_loader = __esm({
|
|
3567
3680
|
"src/bootstrap/loader.ts"() {
|
|
3568
3681
|
"use strict";
|
|
@@ -3573,7 +3686,8 @@ var init_loader = __esm({
|
|
|
3573
3686
|
init_store3();
|
|
3574
3687
|
init_store4();
|
|
3575
3688
|
init_store();
|
|
3576
|
-
|
|
3689
|
+
lastSyncMs = 0;
|
|
3690
|
+
CONTEXT_DIR2 = join6(WORKSPACE_PATH, "context");
|
|
3577
3691
|
MAX_CONTEXT_CHARS = 4e3;
|
|
3578
3692
|
ACTIVITY_TOKEN_BUDGET = 1500;
|
|
3579
3693
|
INBOX_TOKEN_BUDGET = 2e3;
|
|
@@ -3615,6 +3729,16 @@ function clearLog(chatId) {
|
|
|
3615
3729
|
markMessageLogSummarized(chatId);
|
|
3616
3730
|
cache.delete(chatId);
|
|
3617
3731
|
}
|
|
3732
|
+
function getLastMessageTimestamp(chatId) {
|
|
3733
|
+
const cached = cache.get(chatId);
|
|
3734
|
+
if (cached && cached.length > 0) {
|
|
3735
|
+
return cached[cached.length - 1].timestamp;
|
|
3736
|
+
}
|
|
3737
|
+
const rows = getUnsummarizedLog(chatId);
|
|
3738
|
+
if (rows.length === 0) return null;
|
|
3739
|
+
const last = rows[rows.length - 1];
|
|
3740
|
+
return (/* @__PURE__ */ new Date(last.created_at + (last.created_at.includes("Z") ? "" : "Z"))).getTime();
|
|
3741
|
+
}
|
|
3618
3742
|
function getLoggedChatIds() {
|
|
3619
3743
|
return getUnsummarizedChatIds();
|
|
3620
3744
|
}
|
|
@@ -3807,7 +3931,15 @@ async function summarizeSession(chatId) {
|
|
|
3807
3931
|
return summarizeWithFallbackChain(chatId);
|
|
3808
3932
|
}
|
|
3809
3933
|
async function summarizeAllPending() {
|
|
3810
|
-
const
|
|
3934
|
+
const allIds = getLoggedChatIds();
|
|
3935
|
+
const chatIds = allIds.filter((id) => !id.includes("-test-"));
|
|
3936
|
+
if (allIds.length > chatIds.length) {
|
|
3937
|
+
const skipped = allIds.length - chatIds.length;
|
|
3938
|
+
log(`[summarize] Skipping ${skipped} test session(s)`);
|
|
3939
|
+
for (const testId of allIds.filter((id) => id.includes("-test-"))) {
|
|
3940
|
+
clearLog(testId);
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3811
3943
|
if (chatIds.length === 0) return;
|
|
3812
3944
|
log(`[summarize] Summarizing ${chatIds.length} pending session(s)...`);
|
|
3813
3945
|
for (const chatId of chatIds) {
|
|
@@ -3998,8 +4130,8 @@ function spawnAgentProcess(runner, opts, callbacks) {
|
|
|
3998
4130
|
permMode: opts.permMode,
|
|
3999
4131
|
allowedTools: opts.allowedTools
|
|
4000
4132
|
});
|
|
4001
|
-
const
|
|
4002
|
-
const child = spawn2(runner.getExecutablePath(),
|
|
4133
|
+
const args2 = opts.extraArgs?.length ? [...baseArgs, ...opts.extraArgs] : baseArgs;
|
|
4134
|
+
const child = spawn2(runner.getExecutablePath(), args2, {
|
|
4003
4135
|
env: buildSpawnEnv(runner, opts.isSubAgent),
|
|
4004
4136
|
cwd: opts.cwd,
|
|
4005
4137
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -4120,8 +4252,8 @@ async function discoverExistingMcps(runner) {
|
|
|
4120
4252
|
try {
|
|
4121
4253
|
const listCmd = runner.getMcpListCommand();
|
|
4122
4254
|
const exe = runner.getExecutablePath();
|
|
4123
|
-
const
|
|
4124
|
-
const result = await execFileAsync(exe,
|
|
4255
|
+
const args2 = listCmd.slice(1);
|
|
4256
|
+
const result = await execFileAsync(exe, args2, {
|
|
4125
4257
|
encoding: "utf-8",
|
|
4126
4258
|
env: runner.getEnv(),
|
|
4127
4259
|
cwd: homedir3(),
|
|
@@ -4169,8 +4301,8 @@ async function injectMcps(runner, mcpNames, db3, scope) {
|
|
|
4169
4301
|
try {
|
|
4170
4302
|
const config2 = defToConfig(def);
|
|
4171
4303
|
const addCmd = runner.getMcpAddCommand(config2);
|
|
4172
|
-
const
|
|
4173
|
-
await execFileAsync(exe,
|
|
4304
|
+
const args2 = addCmd.slice(1);
|
|
4305
|
+
await execFileAsync(exe, args2, { encoding: "utf-8" });
|
|
4174
4306
|
addPropagation(db3, name, runnerId, scope);
|
|
4175
4307
|
added.push(name);
|
|
4176
4308
|
} catch {
|
|
@@ -4184,8 +4316,8 @@ async function cleanupMcps(runner, mcps2, db3, scope) {
|
|
|
4184
4316
|
for (const name of mcps2) {
|
|
4185
4317
|
try {
|
|
4186
4318
|
const removeCmd = runner.getMcpRemoveCommand(name);
|
|
4187
|
-
const
|
|
4188
|
-
await execFileAsync(exe,
|
|
4319
|
+
const args2 = removeCmd.slice(1);
|
|
4320
|
+
await execFileAsync(exe, args2, { encoding: "utf-8" });
|
|
4189
4321
|
} catch {
|
|
4190
4322
|
}
|
|
4191
4323
|
removePropagation(db3, name, runnerId, scope);
|
|
@@ -4202,13 +4334,13 @@ var init_propagate = __esm({
|
|
|
4202
4334
|
});
|
|
4203
4335
|
|
|
4204
4336
|
// src/agents/mcp-config.ts
|
|
4205
|
-
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, existsSync as existsSync6, readdirSync as readdirSync2, unlinkSync } from "fs";
|
|
4206
|
-
import { join as
|
|
4337
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, existsSync as existsSync6, readdirSync as readdirSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
4338
|
+
import { join as join7, dirname } from "path";
|
|
4207
4339
|
import { fileURLToPath } from "url";
|
|
4208
4340
|
function generateOrchestratorMcpConfig(opts) {
|
|
4209
4341
|
const port = opts.port ?? process.env.DASHBOARD_PORT ?? "3141";
|
|
4210
|
-
const distPath =
|
|
4211
|
-
const tsxPath =
|
|
4342
|
+
const distPath = join7(__dirname, "agents", "mcp-server.js");
|
|
4343
|
+
const tsxPath = join7(__dirname, "..", "src", "agents", "mcp-server.ts");
|
|
4212
4344
|
const useTs = !existsSync6(distPath);
|
|
4213
4345
|
return {
|
|
4214
4346
|
name: opts.agentId === "main" ? "cc-claw" : `cc-claw-${opts.agentId.slice(0, 8)}`,
|
|
@@ -4235,15 +4367,15 @@ function writeMcpConfigFile(config2) {
|
|
|
4235
4367
|
}
|
|
4236
4368
|
};
|
|
4237
4369
|
const safeName = config2.name.replace(/[^a-zA-Z0-9-]/g, "_");
|
|
4238
|
-
const configPath =
|
|
4370
|
+
const configPath = join7(MCP_CONFIG_DIR, `mcp-${safeName}.json`);
|
|
4239
4371
|
writeFileSync2(configPath, JSON.stringify(jsonConfig, null, 2), { mode: 384 });
|
|
4240
4372
|
return configPath;
|
|
4241
4373
|
}
|
|
4242
4374
|
function deleteMcpConfigFile(mcpName) {
|
|
4243
4375
|
const safeName = mcpName.replace(/[^a-zA-Z0-9-]/g, "_");
|
|
4244
|
-
const configPath =
|
|
4376
|
+
const configPath = join7(MCP_CONFIG_DIR, `mcp-${safeName}.json`);
|
|
4245
4377
|
try {
|
|
4246
|
-
|
|
4378
|
+
unlinkSync2(configPath);
|
|
4247
4379
|
} catch {
|
|
4248
4380
|
}
|
|
4249
4381
|
}
|
|
@@ -4254,7 +4386,7 @@ function cleanupOrphanedMcpConfigs() {
|
|
|
4254
4386
|
for (const file of files) {
|
|
4255
4387
|
if (file.startsWith("mcp-cc-claw-") && file.endsWith(".json")) {
|
|
4256
4388
|
try {
|
|
4257
|
-
|
|
4389
|
+
unlinkSync2(join7(MCP_CONFIG_DIR, file));
|
|
4258
4390
|
} catch {
|
|
4259
4391
|
}
|
|
4260
4392
|
}
|
|
@@ -4269,7 +4401,7 @@ var init_mcp_config = __esm({
|
|
|
4269
4401
|
init_paths();
|
|
4270
4402
|
__filename = fileURLToPath(import.meta.url);
|
|
4271
4403
|
__dirname = dirname(__filename);
|
|
4272
|
-
MCP_CONFIG_DIR =
|
|
4404
|
+
MCP_CONFIG_DIR = join7(CC_CLAW_HOME, "mcp-configs");
|
|
4273
4405
|
}
|
|
4274
4406
|
});
|
|
4275
4407
|
|
|
@@ -4280,7 +4412,7 @@ __export(loader_exports, {
|
|
|
4280
4412
|
listTemplates: () => listTemplates
|
|
4281
4413
|
});
|
|
4282
4414
|
import { readdirSync as readdirSync3, readFileSync as readFileSync5 } from "fs";
|
|
4283
|
-
import { join as
|
|
4415
|
+
import { join as join8 } from "path";
|
|
4284
4416
|
function parseFrontmatter(content) {
|
|
4285
4417
|
const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/m);
|
|
4286
4418
|
if (!match || match.index !== 0) return { meta: {}, body: content.trim() };
|
|
@@ -4318,7 +4450,7 @@ function scanTemplates() {
|
|
|
4318
4450
|
return;
|
|
4319
4451
|
}
|
|
4320
4452
|
for (const file of files) {
|
|
4321
|
-
const filePath =
|
|
4453
|
+
const filePath = join8(AGENTS_PATH, file);
|
|
4322
4454
|
try {
|
|
4323
4455
|
const raw = readFileSync5(filePath, "utf-8");
|
|
4324
4456
|
const { meta, body } = parseFrontmatter(raw);
|
|
@@ -4541,8 +4673,8 @@ async function startAgent(agentId, chatId, opts) {
|
|
|
4541
4673
|
const addCmd = runner.getMcpAddCommand(mcpConfig);
|
|
4542
4674
|
const { execFileSync: execFileSync5 } = await import("child_process");
|
|
4543
4675
|
const exe = addCmd[0];
|
|
4544
|
-
const
|
|
4545
|
-
execFileSync5(exe,
|
|
4676
|
+
const args2 = addCmd.slice(1);
|
|
4677
|
+
execFileSync5(exe, args2, { encoding: "utf-8", timeout: 15e3, env: runner.getEnv() });
|
|
4546
4678
|
});
|
|
4547
4679
|
mcpsAdded = [...mcpsAdded, orchestratorMcpName];
|
|
4548
4680
|
updateAgentMcpsAdded(db3, agentId, mcpsAdded);
|
|
@@ -5045,13 +5177,13 @@ __export(health_exports, {
|
|
|
5045
5177
|
stopHealthMonitor: () => stopHealthMonitor
|
|
5046
5178
|
});
|
|
5047
5179
|
import { spawn as spawn3 } from "child_process";
|
|
5048
|
-
async function checkStdioHealth(command,
|
|
5180
|
+
async function checkStdioHealth(command, args2) {
|
|
5049
5181
|
return new Promise((resolve) => {
|
|
5050
5182
|
const timer = setTimeout(() => {
|
|
5051
5183
|
child.kill("SIGKILL");
|
|
5052
5184
|
resolve("timeout");
|
|
5053
5185
|
}, CHECK_TIMEOUT_MS);
|
|
5054
|
-
const child = spawn3(command,
|
|
5186
|
+
const child = spawn3(command, args2, {
|
|
5055
5187
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5056
5188
|
});
|
|
5057
5189
|
let responded = false;
|
|
@@ -5106,8 +5238,8 @@ async function runHealthChecks(db3) {
|
|
|
5106
5238
|
let status = "unhealthy";
|
|
5107
5239
|
try {
|
|
5108
5240
|
if (mcp.transport === "stdio" && mcp.command) {
|
|
5109
|
-
const
|
|
5110
|
-
status = await checkStdioHealth(mcp.command,
|
|
5241
|
+
const args2 = mcp.args ? JSON.parse(mcp.args) : [];
|
|
5242
|
+
status = await checkStdioHealth(mcp.command, args2);
|
|
5111
5243
|
} else if ((mcp.transport === "sse" || mcp.transport === "streamable-http") && mcp.url) {
|
|
5112
5244
|
status = await checkHttpHealth(mcp.url);
|
|
5113
5245
|
} else {
|
|
@@ -6092,7 +6224,7 @@ function stopAgent(chatId) {
|
|
|
6092
6224
|
}
|
|
6093
6225
|
return true;
|
|
6094
6226
|
}
|
|
6095
|
-
function spawnQuery(adapter, config2, model2, cancelState, onStream, onToolAction, thinkingLevel, timeoutMs) {
|
|
6227
|
+
function spawnQuery(adapter, config2, model2, cancelState, onStream, onToolAction, thinkingLevel, timeoutMs, maxTurns) {
|
|
6096
6228
|
const effectiveTimeout = timeoutMs ?? SPAWN_TIMEOUT_MS;
|
|
6097
6229
|
return new Promise((resolve, reject) => {
|
|
6098
6230
|
const thinkingConfig = thinkingLevel && thinkingLevel !== "auto" ? adapter.applyThinkingConfig(thinkingLevel, model2) : void 0;
|
|
@@ -6123,6 +6255,7 @@ function spawnQuery(adapter, config2, model2, cancelState, onStream, onToolActio
|
|
|
6123
6255
|
let cacheRead = 0;
|
|
6124
6256
|
let sawToolEvents = false;
|
|
6125
6257
|
let sawResultEvent = false;
|
|
6258
|
+
let toolTurnCount = 0;
|
|
6126
6259
|
const t0 = Date.now();
|
|
6127
6260
|
const elapsed = () => `${((Date.now() - t0) / 1e3).toFixed(1)}s`;
|
|
6128
6261
|
const pendingTools = /* @__PURE__ */ new Map();
|
|
@@ -6164,6 +6297,13 @@ function spawnQuery(adapter, config2, model2, cancelState, onStream, onToolActio
|
|
|
6164
6297
|
error("[agent] tool action error:", err);
|
|
6165
6298
|
});
|
|
6166
6299
|
}
|
|
6300
|
+
if (maxTurns && adapter.id !== "claude") {
|
|
6301
|
+
toolTurnCount++;
|
|
6302
|
+
if (toolTurnCount >= maxTurns) {
|
|
6303
|
+
log(`[agent] Turn limit ${maxTurns} reached for ${adapter.id} -- stopping`);
|
|
6304
|
+
killProcessGroup(proc, "SIGTERM");
|
|
6305
|
+
}
|
|
6306
|
+
}
|
|
6167
6307
|
break;
|
|
6168
6308
|
case "tool_end":
|
|
6169
6309
|
if (onToolAction) {
|
|
@@ -6246,7 +6386,7 @@ function askAgent(chatId, userMessage, opts) {
|
|
|
6246
6386
|
return withChatLock(chatId, () => askAgentImpl(chatId, userMessage, opts));
|
|
6247
6387
|
}
|
|
6248
6388
|
async function askAgentImpl(chatId, userMessage, opts) {
|
|
6249
|
-
const { cwd, onStream, model: model2, backend: backend2, permMode, onToolAction, bootstrapTier, timeoutMs } = opts ?? {};
|
|
6389
|
+
const { cwd, onStream, model: model2, backend: backend2, permMode, onToolAction, bootstrapTier, timeoutMs, maxTurns } = opts ?? {};
|
|
6250
6390
|
const adapter = backend2 ? getAdapter(backend2) : getAdapterForChat(chatId);
|
|
6251
6391
|
const mode = permMode ?? getMode(chatId);
|
|
6252
6392
|
const responseStyle = getResponseStyle(chatId);
|
|
@@ -6263,7 +6403,8 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
6263
6403
|
permMode: mode,
|
|
6264
6404
|
allowedTools,
|
|
6265
6405
|
cwd: resolvedCwd,
|
|
6266
|
-
thinkingLevel
|
|
6406
|
+
thinkingLevel,
|
|
6407
|
+
maxTurns
|
|
6267
6408
|
});
|
|
6268
6409
|
if (mcpConfigPath) {
|
|
6269
6410
|
baseConfig.args = injectMcpConfig(adapter.id, baseConfig.args, mcpConfigPath);
|
|
@@ -6276,7 +6417,8 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
6276
6417
|
permMode: mode,
|
|
6277
6418
|
allowedTools,
|
|
6278
6419
|
cwd: resolvedCwd,
|
|
6279
|
-
thinkingLevel
|
|
6420
|
+
thinkingLevel,
|
|
6421
|
+
maxTurns
|
|
6280
6422
|
});
|
|
6281
6423
|
if (mcpConfigPath) {
|
|
6282
6424
|
cfg.args = injectMcpConfig(adapter.id, cfg.args, mcpConfigPath);
|
|
@@ -6288,13 +6430,13 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
6288
6430
|
const resolvedModel = model2 ?? adapter.defaultModel;
|
|
6289
6431
|
let result = { resultText: "", sessionId: void 0, input: 0, output: 0, cacheRead: 0, sawToolEvents: false, sawResultEvent: false };
|
|
6290
6432
|
try {
|
|
6291
|
-
result = await spawnQuery(adapter, configWithSession, resolvedModel, cancelState, onStream, onToolAction, thinkingLevel, timeoutMs);
|
|
6433
|
+
result = await spawnQuery(adapter, configWithSession, resolvedModel, cancelState, onStream, onToolAction, thinkingLevel, timeoutMs, maxTurns);
|
|
6292
6434
|
const wasEmptyResponse = !result.resultText && !result.sawToolEvents && !result.sawResultEvent;
|
|
6293
6435
|
if (wasEmptyResponse && !cancelState.cancelled && existingSessionId) {
|
|
6294
6436
|
warn(`[agent] No result with session ${existingSessionId} for chat ${chatId} \u2014 retrying fresh`);
|
|
6295
6437
|
await summarizeSession(chatId);
|
|
6296
6438
|
clearSession(chatId);
|
|
6297
|
-
result = await spawnQuery(adapter, baseConfig, resolvedModel, cancelState, onStream, onToolAction, thinkingLevel, timeoutMs);
|
|
6439
|
+
result = await spawnQuery(adapter, baseConfig, resolvedModel, cancelState, onStream, onToolAction, thinkingLevel, timeoutMs, maxTurns);
|
|
6298
6440
|
}
|
|
6299
6441
|
} finally {
|
|
6300
6442
|
activeChats.delete(chatId);
|
|
@@ -6316,7 +6458,7 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
6316
6458
|
if (result.resultText) {
|
|
6317
6459
|
appendToLog(chatId, userMessage, result.resultText, adapter.id, model2 ?? null, result.sessionId ?? null);
|
|
6318
6460
|
const AUTO_SUMMARIZE_THRESHOLD = 30;
|
|
6319
|
-
const pairCount = getMessagePairCount(chatId);
|
|
6461
|
+
const pairCount = tier !== "chat" ? getMessagePairCount(chatId) : 0;
|
|
6320
6462
|
if (pairCount >= AUTO_SUMMARIZE_THRESHOLD) {
|
|
6321
6463
|
log(`[agent] Auto-summarizing chat ${chatId} after ${pairCount} turns`);
|
|
6322
6464
|
summarizeWithFallbackChain(chatId).then((saved) => {
|
|
@@ -6338,10 +6480,10 @@ function getMcpConfigPath(chatId) {
|
|
|
6338
6480
|
const config2 = generateOrchestratorMcpConfig({ chatId, agentId: "main", token });
|
|
6339
6481
|
return writeMcpConfigFile(config2);
|
|
6340
6482
|
}
|
|
6341
|
-
function injectMcpConfig(adapterId,
|
|
6483
|
+
function injectMcpConfig(adapterId, args2, mcpConfigPath) {
|
|
6342
6484
|
const flag = MCP_CONFIG_FLAG[adapterId];
|
|
6343
|
-
if (!flag) return
|
|
6344
|
-
return [...
|
|
6485
|
+
if (!flag) return args2;
|
|
6486
|
+
return [...args2, ...flag, mcpConfigPath];
|
|
6345
6487
|
}
|
|
6346
6488
|
var activeChats, chatLocks, SPAWN_TIMEOUT_MS, MCP_CONFIG_FLAG;
|
|
6347
6489
|
var init_agent = __esm({
|
|
@@ -6969,7 +7111,7 @@ var init_cron = __esm({
|
|
|
6969
7111
|
});
|
|
6970
7112
|
|
|
6971
7113
|
// src/agents/runners/wrap-backend.ts
|
|
6972
|
-
import { join as
|
|
7114
|
+
import { join as join9 } from "path";
|
|
6973
7115
|
function buildMcpCommands(backendId) {
|
|
6974
7116
|
const exe = backendId;
|
|
6975
7117
|
return {
|
|
@@ -7045,22 +7187,22 @@ function wrapBackendAdapter(adapter) {
|
|
|
7045
7187
|
if (method !== "config-file") return [];
|
|
7046
7188
|
if (adapter.id === "codex") {
|
|
7047
7189
|
const safeName = server.name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
7048
|
-
const
|
|
7049
|
-
|
|
7190
|
+
const args2 = [];
|
|
7191
|
+
args2.push("-c", `mcp_servers.${safeName}.command="${server.command}"`);
|
|
7050
7192
|
if (server.args?.length) {
|
|
7051
|
-
|
|
7193
|
+
args2.push("-c", `mcp_servers.${safeName}.args=${JSON.stringify(server.args)}`);
|
|
7052
7194
|
}
|
|
7053
7195
|
if (server.env) {
|
|
7054
7196
|
for (const [k, v] of Object.entries(server.env)) {
|
|
7055
|
-
|
|
7197
|
+
args2.push("-c", `mcp_servers.${safeName}.env.${k}="${v}"`);
|
|
7056
7198
|
}
|
|
7057
7199
|
}
|
|
7058
|
-
return
|
|
7200
|
+
return args2;
|
|
7059
7201
|
}
|
|
7060
7202
|
const configPath = writeMcpConfigFile(server);
|
|
7061
7203
|
return ["--mcp-config", configPath];
|
|
7062
7204
|
},
|
|
7063
|
-
getSkillPath: () =>
|
|
7205
|
+
getSkillPath: () => join9(SKILLS_PATH, `agent-${adapter.id}.md`)
|
|
7064
7206
|
};
|
|
7065
7207
|
}
|
|
7066
7208
|
var BACKEND_CAPABILITIES;
|
|
@@ -7103,7 +7245,7 @@ var init_wrap_backend = __esm({
|
|
|
7103
7245
|
|
|
7104
7246
|
// src/agents/runners/config-loader.ts
|
|
7105
7247
|
import { readFileSync as readFileSync6, readdirSync as readdirSync4, existsSync as existsSync9, mkdirSync as mkdirSync4, watchFile, unwatchFile } from "fs";
|
|
7106
|
-
import { join as
|
|
7248
|
+
import { join as join10 } from "path";
|
|
7107
7249
|
import { execFileSync } from "child_process";
|
|
7108
7250
|
function resolveExecutable(config2) {
|
|
7109
7251
|
if (existsSync9(config2.executable)) return config2.executable;
|
|
@@ -7174,8 +7316,8 @@ function buildGenericParser(config2) {
|
|
|
7174
7316
|
return events;
|
|
7175
7317
|
};
|
|
7176
7318
|
}
|
|
7177
|
-
function templateReplace(
|
|
7178
|
-
return
|
|
7319
|
+
function templateReplace(args2, vars) {
|
|
7320
|
+
return args2.map((a) => {
|
|
7179
7321
|
let result = a;
|
|
7180
7322
|
for (const [key, val] of Object.entries(vars)) {
|
|
7181
7323
|
result = result.replace(`{${key}}`, val);
|
|
@@ -7201,23 +7343,23 @@ function configToRunner(config2) {
|
|
|
7201
7343
|
getExecutablePath: () => exePath,
|
|
7202
7344
|
getEnv: () => buildBaseEnv(config2.env),
|
|
7203
7345
|
buildSpawnArgs(opts) {
|
|
7204
|
-
const
|
|
7205
|
-
if (config2.spawnPattern.extraArgs)
|
|
7206
|
-
if (config2.spawnPattern.outputFormatFlag)
|
|
7207
|
-
|
|
7346
|
+
const args2 = [];
|
|
7347
|
+
if (config2.spawnPattern.extraArgs) args2.push(...config2.spawnPattern.extraArgs);
|
|
7348
|
+
if (config2.spawnPattern.outputFormatFlag) args2.push(...config2.spawnPattern.outputFormatFlag);
|
|
7349
|
+
args2.push(...config2.spawnPattern.promptFlag, opts.prompt);
|
|
7208
7350
|
if (opts.model && config2.spawnPattern.modelFlag) {
|
|
7209
|
-
|
|
7351
|
+
args2.push(...config2.spawnPattern.modelFlag, opts.model);
|
|
7210
7352
|
}
|
|
7211
7353
|
if (opts.cwd && config2.spawnPattern.cwdFlag) {
|
|
7212
|
-
|
|
7354
|
+
args2.push(...config2.spawnPattern.cwdFlag, opts.cwd);
|
|
7213
7355
|
}
|
|
7214
7356
|
if (opts.sessionId && config2.spawnPattern.sessionResumeFlag) {
|
|
7215
|
-
|
|
7357
|
+
args2.push(...config2.spawnPattern.sessionResumeFlag, opts.sessionId);
|
|
7216
7358
|
}
|
|
7217
7359
|
if (opts.permMode && config2.spawnPattern.permModes?.[opts.permMode]) {
|
|
7218
|
-
|
|
7360
|
+
args2.push(...config2.spawnPattern.permModes[opts.permMode]);
|
|
7219
7361
|
}
|
|
7220
|
-
return
|
|
7362
|
+
return args2;
|
|
7221
7363
|
},
|
|
7222
7364
|
parseLine,
|
|
7223
7365
|
shouldKillOnResult: () => config2.shouldKillOnResult,
|
|
@@ -7239,7 +7381,7 @@ function configToRunner(config2) {
|
|
|
7239
7381
|
prepareMcpInjection() {
|
|
7240
7382
|
return [];
|
|
7241
7383
|
},
|
|
7242
|
-
getSkillPath: () =>
|
|
7384
|
+
getSkillPath: () => join10(SKILLS_PATH, `agent-${config2.id}.md`)
|
|
7243
7385
|
};
|
|
7244
7386
|
}
|
|
7245
7387
|
function loadRunnerConfig(filePath) {
|
|
@@ -7259,7 +7401,7 @@ function loadAllRunnerConfigs() {
|
|
|
7259
7401
|
const files = readdirSync4(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
|
|
7260
7402
|
const configs = [];
|
|
7261
7403
|
for (const file of files) {
|
|
7262
|
-
const config2 = loadRunnerConfig(
|
|
7404
|
+
const config2 = loadRunnerConfig(join10(RUNNERS_PATH, file));
|
|
7263
7405
|
if (config2) configs.push(config2);
|
|
7264
7406
|
}
|
|
7265
7407
|
return configs;
|
|
@@ -7289,7 +7431,7 @@ function watchRunnerConfigs(onChange) {
|
|
|
7289
7431
|
}
|
|
7290
7432
|
const files = readdirSync4(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
|
|
7291
7433
|
for (const file of files) {
|
|
7292
|
-
const fullPath =
|
|
7434
|
+
const fullPath = join10(RUNNERS_PATH, file);
|
|
7293
7435
|
if (watchedFiles.has(fullPath)) continue;
|
|
7294
7436
|
watchedFiles.add(fullPath);
|
|
7295
7437
|
watchFile(fullPath, { interval: 5e3 }, () => {
|
|
@@ -7600,9 +7742,11 @@ var init_telegram2 = __esm({
|
|
|
7600
7742
|
{ command: "voice", description: "Toggle voice responses" },
|
|
7601
7743
|
{ command: "voice_config", description: "Configure voice provider and voice" },
|
|
7602
7744
|
{ command: "response_style", description: "Set the AI response style (concise/normal/detailed)" },
|
|
7745
|
+
{ command: "model_signature", description: "Toggle model+thinking signature on responses" },
|
|
7603
7746
|
{ command: "imagine", description: "Generate an image from a prompt" },
|
|
7604
7747
|
{ command: "heartbeat", description: "Configure proactive heartbeat" },
|
|
7605
|
-
{ command: "chats", description: "Manage multi-chat aliases" }
|
|
7748
|
+
{ command: "chats", description: "Manage multi-chat aliases" },
|
|
7749
|
+
{ command: "intent", description: "Test intent classifier on a message" }
|
|
7606
7750
|
]);
|
|
7607
7751
|
this.bot.on("message", (ctx) => {
|
|
7608
7752
|
const chatId = ctx.chat.id.toString();
|
|
@@ -7876,22 +8020,44 @@ var init_telegram2 = __esm({
|
|
|
7876
8020
|
var discover_exports = {};
|
|
7877
8021
|
__export(discover_exports, {
|
|
7878
8022
|
discoverAllSkills: () => discoverAllSkills,
|
|
8023
|
+
invalidateSkillCache: () => invalidateSkillCache,
|
|
7879
8024
|
stripFrontmatter: () => stripFrontmatter2
|
|
7880
8025
|
});
|
|
7881
8026
|
import { readdir, readFile as readFile2 } from "fs/promises";
|
|
7882
8027
|
import { createHash } from "crypto";
|
|
7883
8028
|
import { homedir as homedir4 } from "os";
|
|
7884
|
-
import { join as
|
|
8029
|
+
import { join as join11 } from "path";
|
|
8030
|
+
function invalidateSkillCache() {
|
|
8031
|
+
cachedSkills = null;
|
|
8032
|
+
cacheTimestamp = 0;
|
|
8033
|
+
}
|
|
7885
8034
|
async function discoverAllSkills() {
|
|
7886
|
-
const
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
const dirs = BACKEND_SKILL_DIRS[backendId] ?? [join9(homedir4(), `.${backendId}`, "skills")];
|
|
7890
|
-
for (const dir of dirs) {
|
|
7891
|
-
rawSkills.push(...await scanSkillDir(dir, backendId));
|
|
7892
|
-
}
|
|
8035
|
+
const now = Date.now();
|
|
8036
|
+
if (cachedSkills !== null && now - cacheTimestamp < CACHE_TTL_MS2) {
|
|
8037
|
+
return cachedSkills;
|
|
7893
8038
|
}
|
|
7894
|
-
|
|
8039
|
+
if (pendingScan !== null) {
|
|
8040
|
+
return pendingScan;
|
|
8041
|
+
}
|
|
8042
|
+
pendingScan = (async () => {
|
|
8043
|
+
try {
|
|
8044
|
+
const rawSkills = [];
|
|
8045
|
+
rawSkills.push(...await scanSkillDir(SKILLS_PATH, "cc-claw"));
|
|
8046
|
+
for (const backendId of getAllBackendIds()) {
|
|
8047
|
+
const dirs = BACKEND_SKILL_DIRS[backendId] ?? [join11(homedir4(), `.${backendId}`, "skills")];
|
|
8048
|
+
for (const dir of dirs) {
|
|
8049
|
+
rawSkills.push(...await scanSkillDir(dir, backendId));
|
|
8050
|
+
}
|
|
8051
|
+
}
|
|
8052
|
+
const result = mergeAndDeduplicate(rawSkills);
|
|
8053
|
+
cachedSkills = result;
|
|
8054
|
+
cacheTimestamp = Date.now();
|
|
8055
|
+
return result;
|
|
8056
|
+
} finally {
|
|
8057
|
+
pendingScan = null;
|
|
8058
|
+
}
|
|
8059
|
+
})();
|
|
8060
|
+
return pendingScan;
|
|
7895
8061
|
}
|
|
7896
8062
|
async function scanSkillDir(skillsDir, source) {
|
|
7897
8063
|
const results = [];
|
|
@@ -7906,7 +8072,7 @@ async function scanSkillDir(skillsDir, source) {
|
|
|
7906
8072
|
let content;
|
|
7907
8073
|
let resolvedPath;
|
|
7908
8074
|
for (const candidate of SKILL_FILE_CANDIDATES) {
|
|
7909
|
-
const p =
|
|
8075
|
+
const p = join11(skillsDir, entry.name, candidate);
|
|
7910
8076
|
try {
|
|
7911
8077
|
content = await readFile2(p, "utf-8");
|
|
7912
8078
|
resolvedPath = p;
|
|
@@ -8001,7 +8167,7 @@ function parseFrontmatter2(content, fallbackName) {
|
|
|
8001
8167
|
function stripFrontmatter2(content) {
|
|
8002
8168
|
return content.replace(/^---\s*\n[\s\S]*?\n---\s*\n?/, "").trim();
|
|
8003
8169
|
}
|
|
8004
|
-
var SKILL_FILE_CANDIDATES, BACKEND_SKILL_DIRS;
|
|
8170
|
+
var SKILL_FILE_CANDIDATES, BACKEND_SKILL_DIRS, CACHE_TTL_MS2, cachedSkills, cacheTimestamp, pendingScan;
|
|
8005
8171
|
var init_discover = __esm({
|
|
8006
8172
|
"src/skills/discover.ts"() {
|
|
8007
8173
|
"use strict";
|
|
@@ -8009,13 +8175,17 @@ var init_discover = __esm({
|
|
|
8009
8175
|
init_backends();
|
|
8010
8176
|
SKILL_FILE_CANDIDATES = ["SKILL.md", "skill.md"];
|
|
8011
8177
|
BACKEND_SKILL_DIRS = {
|
|
8012
|
-
claude: [
|
|
8013
|
-
gemini: [
|
|
8178
|
+
claude: [join11(homedir4(), ".claude", "skills")],
|
|
8179
|
+
gemini: [join11(homedir4(), ".gemini", "skills")],
|
|
8014
8180
|
codex: [
|
|
8015
|
-
|
|
8016
|
-
|
|
8181
|
+
join11(homedir4(), ".agents", "skills"),
|
|
8182
|
+
join11(homedir4(), ".codex", "skills")
|
|
8017
8183
|
]
|
|
8018
8184
|
};
|
|
8185
|
+
CACHE_TTL_MS2 = 3e5;
|
|
8186
|
+
cachedSkills = null;
|
|
8187
|
+
cacheTimestamp = 0;
|
|
8188
|
+
pendingScan = null;
|
|
8019
8189
|
}
|
|
8020
8190
|
});
|
|
8021
8191
|
|
|
@@ -8026,7 +8196,7 @@ __export(install_exports, {
|
|
|
8026
8196
|
});
|
|
8027
8197
|
import { mkdir, readdir as readdir2, readFile as readFile3, cp } from "fs/promises";
|
|
8028
8198
|
import { existsSync as existsSync10 } from "fs";
|
|
8029
|
-
import { join as
|
|
8199
|
+
import { join as join12, basename } from "path";
|
|
8030
8200
|
import { execSync as execSync4 } from "child_process";
|
|
8031
8201
|
async function installSkillFromGitHub(urlOrShorthand) {
|
|
8032
8202
|
let repoUrl;
|
|
@@ -8037,23 +8207,23 @@ async function installSkillFromGitHub(urlOrShorthand) {
|
|
|
8037
8207
|
}
|
|
8038
8208
|
repoUrl = parsed.cloneUrl;
|
|
8039
8209
|
subPath = parsed.subPath;
|
|
8040
|
-
const tmpDir =
|
|
8210
|
+
const tmpDir = join12("/tmp", `cc-claw-skill-${Date.now()}`);
|
|
8041
8211
|
try {
|
|
8042
8212
|
log(`[skill-install] Cloning ${repoUrl} to ${tmpDir}`);
|
|
8043
8213
|
execSync4(`git clone --depth 1 ${repoUrl} ${tmpDir}`, {
|
|
8044
8214
|
stdio: "pipe",
|
|
8045
8215
|
timeout: 3e4
|
|
8046
8216
|
});
|
|
8047
|
-
if (!existsSync10(
|
|
8217
|
+
if (!existsSync10(join12(tmpDir, ".git"))) {
|
|
8048
8218
|
return { success: false, error: "Git clone failed: no .git directory produced" };
|
|
8049
8219
|
}
|
|
8050
|
-
const searchRoot = subPath ?
|
|
8220
|
+
const searchRoot = subPath ? join12(tmpDir, subPath) : tmpDir;
|
|
8051
8221
|
const skillDir = await findSkillDir(searchRoot);
|
|
8052
8222
|
if (!skillDir) {
|
|
8053
8223
|
return { success: false, error: "No SKILL.md found in the repository." };
|
|
8054
8224
|
}
|
|
8055
8225
|
const skillFolderName = basename(skillDir);
|
|
8056
|
-
const destDir =
|
|
8226
|
+
const destDir = join12(SKILLS_PATH, skillFolderName);
|
|
8057
8227
|
if (existsSync10(destDir)) {
|
|
8058
8228
|
log(`[skill-install] Overwriting existing skill at ${destDir}`);
|
|
8059
8229
|
}
|
|
@@ -8061,12 +8231,12 @@ async function installSkillFromGitHub(urlOrShorthand) {
|
|
|
8061
8231
|
await cp(skillDir, destDir, { recursive: true });
|
|
8062
8232
|
let skillName = skillFolderName;
|
|
8063
8233
|
try {
|
|
8064
|
-
const content = await readFile3(
|
|
8234
|
+
const content = await readFile3(join12(destDir, "SKILL.md"), "utf-8");
|
|
8065
8235
|
const nameMatch = content.match(/^name:\s*(.+)$/m);
|
|
8066
8236
|
if (nameMatch) skillName = nameMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
8067
8237
|
} catch {
|
|
8068
8238
|
try {
|
|
8069
|
-
const content = await readFile3(
|
|
8239
|
+
const content = await readFile3(join12(destDir, "skill.md"), "utf-8");
|
|
8070
8240
|
const nameMatch = content.match(/^name:\s*(.+)$/m);
|
|
8071
8241
|
if (nameMatch) skillName = nameMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
8072
8242
|
} catch {
|
|
@@ -8101,15 +8271,15 @@ function parseGitHubUrl(input) {
|
|
|
8101
8271
|
async function findSkillDir(root) {
|
|
8102
8272
|
const candidates = ["SKILL.md", "skill.md"];
|
|
8103
8273
|
for (const c of candidates) {
|
|
8104
|
-
if (existsSync10(
|
|
8274
|
+
if (existsSync10(join12(root, c))) return root;
|
|
8105
8275
|
}
|
|
8106
8276
|
try {
|
|
8107
8277
|
const entries = await readdir2(root, { withFileTypes: true });
|
|
8108
8278
|
for (const entry of entries) {
|
|
8109
8279
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
8110
8280
|
for (const c of candidates) {
|
|
8111
|
-
if (existsSync10(
|
|
8112
|
-
return
|
|
8281
|
+
if (existsSync10(join12(root, entry.name, c))) {
|
|
8282
|
+
return join12(root, entry.name);
|
|
8113
8283
|
}
|
|
8114
8284
|
}
|
|
8115
8285
|
}
|
|
@@ -8121,15 +8291,15 @@ async function findSkillDir(root) {
|
|
|
8121
8291
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
8122
8292
|
let subEntries;
|
|
8123
8293
|
try {
|
|
8124
|
-
subEntries = await readdir2(
|
|
8294
|
+
subEntries = await readdir2(join12(root, entry.name), { withFileTypes: true });
|
|
8125
8295
|
} catch {
|
|
8126
8296
|
continue;
|
|
8127
8297
|
}
|
|
8128
8298
|
for (const sub of subEntries) {
|
|
8129
8299
|
if (!sub.isDirectory() || sub.name.startsWith(".")) continue;
|
|
8130
8300
|
for (const c of candidates) {
|
|
8131
|
-
if (existsSync10(
|
|
8132
|
-
return
|
|
8301
|
+
if (existsSync10(join12(root, entry.name, sub.name, c))) {
|
|
8302
|
+
return join12(root, entry.name, sub.name);
|
|
8133
8303
|
}
|
|
8134
8304
|
}
|
|
8135
8305
|
}
|
|
@@ -8148,7 +8318,7 @@ var init_install = __esm({
|
|
|
8148
8318
|
|
|
8149
8319
|
// src/bootstrap/profile.ts
|
|
8150
8320
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync11 } from "fs";
|
|
8151
|
-
import { join as
|
|
8321
|
+
import { join as join13 } from "path";
|
|
8152
8322
|
function hasActiveProfile(chatId) {
|
|
8153
8323
|
return activeProfiles.has(chatId);
|
|
8154
8324
|
}
|
|
@@ -8263,7 +8433,7 @@ Timezone: ${state.timezone}
|
|
|
8263
8433
|
Style: ${state.style}
|
|
8264
8434
|
Use: ${state.useCase}
|
|
8265
8435
|
|
|
8266
|
-
You can edit ~/.cc-claw/
|
|
8436
|
+
You can edit ~/.cc-claw/identity/USER.md anytime or run /setup-profile again.`,
|
|
8267
8437
|
"plain"
|
|
8268
8438
|
);
|
|
8269
8439
|
}
|
|
@@ -8293,14 +8463,14 @@ var init_profile = __esm({
|
|
|
8293
8463
|
"use strict";
|
|
8294
8464
|
init_paths();
|
|
8295
8465
|
init_log();
|
|
8296
|
-
USER_PATH2 =
|
|
8466
|
+
USER_PATH2 = join13(IDENTITY_PATH, "USER.md");
|
|
8297
8467
|
activeProfiles = /* @__PURE__ */ new Map();
|
|
8298
8468
|
}
|
|
8299
8469
|
});
|
|
8300
8470
|
|
|
8301
8471
|
// src/bootstrap/heartbeat.ts
|
|
8302
8472
|
import { readFileSync as readFileSync8, existsSync as existsSync12 } from "fs";
|
|
8303
|
-
import { join as
|
|
8473
|
+
import { join as join14 } from "path";
|
|
8304
8474
|
function initHeartbeat(channelReg) {
|
|
8305
8475
|
registry2 = channelReg;
|
|
8306
8476
|
}
|
|
@@ -8472,8 +8642,8 @@ function formatHeartbeatStatus(chatId) {
|
|
|
8472
8642
|
}
|
|
8473
8643
|
return lines.join("\n");
|
|
8474
8644
|
}
|
|
8475
|
-
function parseHeartbeatCommand(chatId,
|
|
8476
|
-
const parts =
|
|
8645
|
+
function parseHeartbeatCommand(chatId, args2) {
|
|
8646
|
+
const parts = args2.trim().toLowerCase().split(/\s+/);
|
|
8477
8647
|
return { action: parts[0] ?? "status", value: parts.slice(1).join(" ") };
|
|
8478
8648
|
}
|
|
8479
8649
|
var HEARTBEAT_MD_PATH, HEARTBEAT_OK, registry2, activeTimers2;
|
|
@@ -8486,7 +8656,7 @@ var init_heartbeat = __esm({
|
|
|
8486
8656
|
init_backends();
|
|
8487
8657
|
init_health2();
|
|
8488
8658
|
init_log();
|
|
8489
|
-
HEARTBEAT_MD_PATH =
|
|
8659
|
+
HEARTBEAT_MD_PATH = join14(WORKSPACE_PATH, "HEARTBEAT.md");
|
|
8490
8660
|
HEARTBEAT_OK = "HEARTBEAT_OK";
|
|
8491
8661
|
registry2 = null;
|
|
8492
8662
|
activeTimers2 = /* @__PURE__ */ new Map();
|
|
@@ -8531,10 +8701,127 @@ var init_format_time = __esm({
|
|
|
8531
8701
|
}
|
|
8532
8702
|
});
|
|
8533
8703
|
|
|
8704
|
+
// src/intent/classify.ts
|
|
8705
|
+
function classifyIntent(text, chatId) {
|
|
8706
|
+
const trimmed = text.trim();
|
|
8707
|
+
if (trimmed.startsWith(">>")) return "agentic";
|
|
8708
|
+
if (trimmed.startsWith("/")) return "agentic";
|
|
8709
|
+
const lower = trimmed.toLowerCase();
|
|
8710
|
+
for (const pattern of AGENTIC_PATTERNS) {
|
|
8711
|
+
if (pattern.test(trimmed)) {
|
|
8712
|
+
log(`[intent] "${trimmed.slice(0, 30)}..." -> agentic (pattern: ${pattern})`);
|
|
8713
|
+
return "agentic";
|
|
8714
|
+
}
|
|
8715
|
+
}
|
|
8716
|
+
const sessionId = getSessionId(chatId);
|
|
8717
|
+
if (sessionId) {
|
|
8718
|
+
const lastTs = getLastMessageTimestamp(chatId);
|
|
8719
|
+
if (lastTs) {
|
|
8720
|
+
const elapsed = Date.now() - lastTs;
|
|
8721
|
+
if (elapsed < 12e4) {
|
|
8722
|
+
if (trimmed.length < 30) {
|
|
8723
|
+
log(`[intent] "${trimmed.slice(0, 30)}" -> agentic (active session, ${(elapsed / 1e3).toFixed(0)}s ago)`);
|
|
8724
|
+
return "agentic";
|
|
8725
|
+
}
|
|
8726
|
+
}
|
|
8727
|
+
}
|
|
8728
|
+
}
|
|
8729
|
+
if (CHAT_EXACT.has(lower)) {
|
|
8730
|
+
log(`[intent] "${lower}" -> chat (exact match)`);
|
|
8731
|
+
return "chat";
|
|
8732
|
+
}
|
|
8733
|
+
if (trimmed.length <= 4 && /^[\p{Emoji}\s]+$/u.test(trimmed)) {
|
|
8734
|
+
log(`[intent] "${trimmed}" -> chat (emoji-only)`);
|
|
8735
|
+
return "chat";
|
|
8736
|
+
}
|
|
8737
|
+
log(`[intent] "${trimmed.slice(0, 30)}..." -> agentic (default)`);
|
|
8738
|
+
return "agentic";
|
|
8739
|
+
}
|
|
8740
|
+
var CHAT_EXACT, AGENTIC_PATTERNS;
|
|
8741
|
+
var init_classify = __esm({
|
|
8742
|
+
"src/intent/classify.ts"() {
|
|
8743
|
+
"use strict";
|
|
8744
|
+
init_store4();
|
|
8745
|
+
init_session_log();
|
|
8746
|
+
init_log();
|
|
8747
|
+
CHAT_EXACT = /* @__PURE__ */ new Set([
|
|
8748
|
+
"hey",
|
|
8749
|
+
"hi",
|
|
8750
|
+
"hello",
|
|
8751
|
+
"yo",
|
|
8752
|
+
"sup",
|
|
8753
|
+
"howdy",
|
|
8754
|
+
"hiya",
|
|
8755
|
+
"thanks",
|
|
8756
|
+
"thank you",
|
|
8757
|
+
"thx",
|
|
8758
|
+
"ty",
|
|
8759
|
+
"thank u",
|
|
8760
|
+
"ok",
|
|
8761
|
+
"okay",
|
|
8762
|
+
"k",
|
|
8763
|
+
"kk",
|
|
8764
|
+
"cool",
|
|
8765
|
+
"nice",
|
|
8766
|
+
"great",
|
|
8767
|
+
"awesome",
|
|
8768
|
+
"perfect",
|
|
8769
|
+
"good morning",
|
|
8770
|
+
"good night",
|
|
8771
|
+
"good evening",
|
|
8772
|
+
"gm",
|
|
8773
|
+
"gn",
|
|
8774
|
+
"bye",
|
|
8775
|
+
"goodbye",
|
|
8776
|
+
"later",
|
|
8777
|
+
"see ya",
|
|
8778
|
+
"cya",
|
|
8779
|
+
"lol",
|
|
8780
|
+
"lmao",
|
|
8781
|
+
"haha",
|
|
8782
|
+
"heh",
|
|
8783
|
+
"np",
|
|
8784
|
+
"no problem",
|
|
8785
|
+
"no worries",
|
|
8786
|
+
"nw",
|
|
8787
|
+
"got it",
|
|
8788
|
+
"understood",
|
|
8789
|
+
"roger",
|
|
8790
|
+
"copy",
|
|
8791
|
+
"good",
|
|
8792
|
+
"fine",
|
|
8793
|
+
"alright",
|
|
8794
|
+
"sure"
|
|
8795
|
+
]);
|
|
8796
|
+
AGENTIC_PATTERNS = [
|
|
8797
|
+
/\.\w{1,5}$/,
|
|
8798
|
+
// file extensions (.ts, .py, .md)
|
|
8799
|
+
/[\/\\][\w.-]+/,
|
|
8800
|
+
// file paths
|
|
8801
|
+
/`[^`]+`/,
|
|
8802
|
+
// inline code
|
|
8803
|
+
/```/,
|
|
8804
|
+
// code blocks
|
|
8805
|
+
/\b(fix|create|build|deploy|run|execute|install|delete|remove|update|edit|write|read|check|test|debug|refactor|implement|add|change|modify|move|copy|rename|push|pull|commit|merge|rebase)\b/i,
|
|
8806
|
+
// imperative verbs
|
|
8807
|
+
/\b(error|bug|crash|fail|broken|issue|problem|exception|stack ?trace)\b/i,
|
|
8808
|
+
// error keywords
|
|
8809
|
+
/\b(function|class|const|let|var|import|export|return|async|await)\b/i,
|
|
8810
|
+
// code keywords
|
|
8811
|
+
/[!]{1,2}\s/,
|
|
8812
|
+
// shell prefix
|
|
8813
|
+
/^\/\//,
|
|
8814
|
+
// backend command prefix
|
|
8815
|
+
/\b(file|folder|directory|script|server|database|api|endpoint|config|package|module|component)\b/i
|
|
8816
|
+
// tech nouns
|
|
8817
|
+
];
|
|
8818
|
+
}
|
|
8819
|
+
});
|
|
8820
|
+
|
|
8534
8821
|
// src/media/image-gen.ts
|
|
8535
8822
|
import { mkdirSync as mkdirSync5, existsSync as existsSync13 } from "fs";
|
|
8536
8823
|
import { writeFile as writeFile2 } from "fs/promises";
|
|
8537
|
-
import { join as
|
|
8824
|
+
import { join as join15 } from "path";
|
|
8538
8825
|
async function generateImage(prompt) {
|
|
8539
8826
|
const apiKey = process.env.GEMINI_API_KEY;
|
|
8540
8827
|
if (!apiKey) {
|
|
@@ -8586,7 +8873,7 @@ async function generateImage(prompt) {
|
|
|
8586
8873
|
}
|
|
8587
8874
|
const ext = mimeType.includes("jpeg") || mimeType.includes("jpg") ? "jpg" : "png";
|
|
8588
8875
|
const filename = `img_${Date.now()}.${ext}`;
|
|
8589
|
-
const filePath =
|
|
8876
|
+
const filePath = join15(IMAGE_OUTPUT_DIR, filename);
|
|
8590
8877
|
const buffer = Buffer.from(imageData, "base64");
|
|
8591
8878
|
await writeFile2(filePath, buffer);
|
|
8592
8879
|
log(`[image-gen] Saved ${buffer.length} bytes to ${filePath}`);
|
|
@@ -8601,8 +8888,8 @@ var init_image_gen = __esm({
|
|
|
8601
8888
|
"use strict";
|
|
8602
8889
|
init_log();
|
|
8603
8890
|
IMAGE_MODEL = "gemini-3.1-flash-image-preview";
|
|
8604
|
-
IMAGE_OUTPUT_DIR =
|
|
8605
|
-
process.env.CC_CLAW_HOME ??
|
|
8891
|
+
IMAGE_OUTPUT_DIR = join15(
|
|
8892
|
+
process.env.CC_CLAW_HOME ?? join15(process.env.HOME ?? "/tmp", ".cc-claw"),
|
|
8606
8893
|
"data",
|
|
8607
8894
|
"images"
|
|
8608
8895
|
);
|
|
@@ -9689,6 +9976,25 @@ function shortModelName(modelId) {
|
|
|
9689
9976
|
}
|
|
9690
9977
|
return modelId;
|
|
9691
9978
|
}
|
|
9979
|
+
function formatModelShort(modelId) {
|
|
9980
|
+
const MAP = {
|
|
9981
|
+
"claude-opus-4-6": "Opus 4.6",
|
|
9982
|
+
"claude-sonnet-4-6": "Sonnet 4.6",
|
|
9983
|
+
"claude-haiku-4-5": "Haiku 4.5",
|
|
9984
|
+
"gemini-3.1-pro-preview": "Gemini Pro 3.1",
|
|
9985
|
+
"gemini-3-flash-preview": "Gemini 3 Flash",
|
|
9986
|
+
"gpt-5.4": "GPT-5.4",
|
|
9987
|
+
"gpt-5.3-codex": "GPT-5.3 Codex",
|
|
9988
|
+
"gpt-5.2-codex": "GPT-5.2 Codex",
|
|
9989
|
+
"gpt-5.1-codex-max": "GPT-5.1 Codex Max",
|
|
9990
|
+
"gpt-5.4-mini": "GPT-5.4-mini"
|
|
9991
|
+
};
|
|
9992
|
+
return MAP[modelId] ?? modelId;
|
|
9993
|
+
}
|
|
9994
|
+
function capitalize(s) {
|
|
9995
|
+
if (s === "extra_high") return "xHigh";
|
|
9996
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
9997
|
+
}
|
|
9692
9998
|
function resolveModel(chatId) {
|
|
9693
9999
|
const explicit = getModel(chatId);
|
|
9694
10000
|
if (explicit) return explicit;
|
|
@@ -9767,7 +10073,7 @@ async function handleCommand(msg, channel) {
|
|
|
9767
10073
|
case "help":
|
|
9768
10074
|
await channel.sendText(
|
|
9769
10075
|
chatId,
|
|
9770
|
-
"Hey! I'm CC-Claw \u2014 your personal AI assistant on Telegram.\n\nI use AI coding CLIs (Claude, Gemini, Codex) as my brain. Just send me a message to get started.\n\nCommands:\n/backend [name] - Switch AI backend (or /claude /gemini /codex)\n/model - Switch model for active backend\n/summarizer - Configure session summarization model\n/status - Show session, model, backend, and usage\n/cost - Show estimated API cost (use /cost all for all-time)\n/usage - Show usage per backend with limits\n/limits - Configure usage limits per backend\n/newchat - Start a fresh conversation\n/summarize - Save session to memory (without resetting)\n/summarize all - Summarize all pending sessions (pre-restart)\n/cwd <path> - Set working directory\n/cwd - Show current working directory\n/memory - List stored memories\n/remember <text> - Save a memory\n/forget <keyword> - Remove a memory\n/voice - Toggle voice responses\n/voice_config - Configure voice provider and voice\n/imagine <prompt> - Generate an image (or /image)\n/cron <description> - Schedule a task (or /schedule)\n/cron - List scheduled jobs (or /jobs)\n/cron cancel <id> - Cancel a job\n/cron pause <id> - Pause a job\n/cron resume <id> - Resume a job\n/cron run <id> - Trigger a job now\n/cron runs [id] - View run history\n/cron edit <id> - Edit a job\n/cron health - Scheduler health\n/skills - List skills from all backends\n/skill-install <url> - Install a skill from GitHub\n/setup-profile - Set up your user profile\n/chats - List authorized chats and aliases\n/heartbeat - Proactive awareness (on/off/interval/hours)\n/history - List recent session summaries\n/stop - Cancel the current running task\n/tools - Configure which tools the agent can use\n/permissions - Switch permission mode (yolo/safe/plan)\n/verbose - Tool visibility (off/normal/verbose)\n/agents - List active sub-agents\n/tasks - Show task board for current orchestration\n/stopagent <id> - Cancel a specific sub-agent\n/stopall - Cancel all sub-agents in this chat\n/runners - List registered CLI runners\n/mcps - List registered MCP servers\n/help - Show this message",
|
|
10076
|
+
"Hey! I'm CC-Claw \u2014 your personal AI assistant on Telegram.\n\nI use AI coding CLIs (Claude, Gemini, Codex) as my brain. Just send me a message to get started.\n\nCommands:\n/backend [name] - Switch AI backend (or /claude /gemini /codex)\n/model - Switch model for active backend\n/summarizer - Configure session summarization model\n/status - Show session, model, backend, and usage\n/cost - Show estimated API cost (use /cost all for all-time)\n/usage - Show usage per backend with limits\n/limits - Configure usage limits per backend\n/newchat - Start a fresh conversation\n/summarize - Save session to memory (without resetting)\n/summarize all - Summarize all pending sessions (pre-restart)\n/cwd <path> - Set working directory\n/cwd - Show current working directory\n/memory - List stored memories\n/remember <text> - Save a memory\n/forget <keyword> - Remove a memory\n/voice - Toggle voice responses\n/voice_config - Configure voice provider and voice\n/imagine <prompt> - Generate an image (or /image)\n/cron <description> - Schedule a task (or /schedule)\n/cron - List scheduled jobs (or /jobs)\n/cron cancel <id> - Cancel a job\n/cron pause <id> - Pause a job\n/cron resume <id> - Resume a job\n/cron run <id> - Trigger a job now\n/cron runs [id] - View run history\n/cron edit <id> - Edit a job\n/cron health - Scheduler health\n/skills - List skills from all backends\n/skill-install <url> - Install a skill from GitHub\n/setup-profile - Set up your user profile\n/chats - List authorized chats and aliases\n/heartbeat - Proactive awareness (on/off/interval/hours)\n/history - List recent session summaries\n/stop - Cancel the current running task\n/tools - Configure which tools the agent can use\n/permissions - Switch permission mode (yolo/safe/plan)\n/verbose - Tool visibility (off/normal/verbose)\n/model_signature - Toggle model+thinking signature on responses\n/intent <msg> - Test intent classifier (chat vs agentic)\n/agents - List active sub-agents\n/tasks - Show task board for current orchestration\n/stopagent <id> - Cancel a specific sub-agent\n/stopall - Cancel all sub-agents in this chat\n/runners - List registered CLI runners\n/mcps - List registered MCP servers\n/help - Show this message",
|
|
9771
10077
|
"plain"
|
|
9772
10078
|
);
|
|
9773
10079
|
break;
|
|
@@ -10294,6 +10600,17 @@ ${lines.join("\n")}`, "plain");
|
|
|
10294
10600
|
}
|
|
10295
10601
|
break;
|
|
10296
10602
|
}
|
|
10603
|
+
case "model_signature": {
|
|
10604
|
+
const currentSig = getModelSignature(chatId);
|
|
10605
|
+
const newSig = currentSig === "on" ? "off" : "on";
|
|
10606
|
+
setModelSignature(chatId, newSig);
|
|
10607
|
+
await channel.sendText(
|
|
10608
|
+
chatId,
|
|
10609
|
+
newSig === "on" ? "\u{1F9E0} Model signature enabled. Each response will show the active model and thinking level." : "Model signature disabled.",
|
|
10610
|
+
"plain"
|
|
10611
|
+
);
|
|
10612
|
+
break;
|
|
10613
|
+
}
|
|
10297
10614
|
case "imagine":
|
|
10298
10615
|
case "image": {
|
|
10299
10616
|
if (!commandArgs) {
|
|
@@ -10933,6 +11250,13 @@ Use /skills to see it.`, "plain");
|
|
|
10933
11250
|
}
|
|
10934
11251
|
break;
|
|
10935
11252
|
}
|
|
11253
|
+
case "intent": {
|
|
11254
|
+
const testMsg = args.join(" ") || "hey";
|
|
11255
|
+
const result = classifyIntent(testMsg, chatId);
|
|
11256
|
+
await channel.sendText(chatId, `Intent: ${result}
|
|
11257
|
+
Message: "${testMsg}"`, "plain");
|
|
11258
|
+
break;
|
|
11259
|
+
}
|
|
10936
11260
|
default:
|
|
10937
11261
|
await channel.sendText(chatId, `Unknown command: /${command}. Type /help for available commands.`, "plain");
|
|
10938
11262
|
}
|
|
@@ -11113,6 +11437,10 @@ async function handleText(msg, channel) {
|
|
|
11113
11437
|
await channel.sendText(chatId, limitMsg, "plain");
|
|
11114
11438
|
return;
|
|
11115
11439
|
}
|
|
11440
|
+
const intent = classifyIntent(text, chatId);
|
|
11441
|
+
const cleanText = text.startsWith(">>") ? text.slice(2).trim() : text;
|
|
11442
|
+
const bootstrapTier = intent === "chat" ? "chat" : void 0;
|
|
11443
|
+
const maxTurns = intent === "chat" ? 1 : void 0;
|
|
11116
11444
|
if (isChatBusy(chatId) && !bypassBusyCheck.delete(chatId)) {
|
|
11117
11445
|
if (typeof channel.sendKeyboard === "function") {
|
|
11118
11446
|
pendingInterrupts.set(chatId, { msg, channel });
|
|
@@ -11141,20 +11469,33 @@ async function handleText(msg, channel) {
|
|
|
11141
11469
|
const tMode = getMode(chatId);
|
|
11142
11470
|
const tVerbose = getVerboseLevel(chatId);
|
|
11143
11471
|
const tToolCb = tVerbose !== "off" ? makeToolActionCallback(chatId, channel, tVerbose) : void 0;
|
|
11144
|
-
const response = await askAgent(chatId, text, {
|
|
11472
|
+
const response = await askAgent(chatId, cleanText || text, {
|
|
11145
11473
|
cwd: getCwd(chatId),
|
|
11146
11474
|
model: model2,
|
|
11147
11475
|
permMode: tMode,
|
|
11148
11476
|
onToolAction: tToolCb,
|
|
11477
|
+
bootstrapTier,
|
|
11478
|
+
maxTurns,
|
|
11149
11479
|
onCompaction: (cid) => {
|
|
11150
11480
|
channel.sendText(cid, "\u{1F4BE} Context saved to memory.").catch(() => {
|
|
11151
11481
|
});
|
|
11152
11482
|
}
|
|
11153
11483
|
});
|
|
11154
11484
|
if (response.usage) addUsage(chatId, response.usage.input, response.usage.output, response.usage.cacheRead, model2);
|
|
11155
|
-
|
|
11156
|
-
|
|
11157
|
-
|
|
11485
|
+
let responseText = response.text;
|
|
11486
|
+
const sigEnabled = getModelSignature(chatId);
|
|
11487
|
+
if (sigEnabled === "on" && responseText && !responseText.startsWith("(No response")) {
|
|
11488
|
+
const adapter = getAdapterForChat(chatId);
|
|
11489
|
+
const modelId = model2 ?? adapter.defaultModel;
|
|
11490
|
+
const thinking2 = getThinkingLevel(chatId) || "auto";
|
|
11491
|
+
const shortModel = formatModelShort(modelId);
|
|
11492
|
+
responseText += `
|
|
11493
|
+
|
|
11494
|
+
\u{1F9E0} [${shortModel} | ${capitalize(thinking2)}]`;
|
|
11495
|
+
}
|
|
11496
|
+
if (responseText.includes("[NEED_PERMISSION]") && tMode !== "yolo") {
|
|
11497
|
+
const cleanText2 = responseText.replace(/\[NEED_PERMISSION\]/g, "").trim();
|
|
11498
|
+
await sendResponse(chatId, channel, cleanText2, msg.messageId);
|
|
11158
11499
|
const targetMode = determineEscalationTarget(chatId, tMode);
|
|
11159
11500
|
storePendingEscalation(chatId, text);
|
|
11160
11501
|
if (typeof channel.sendKeyboard === "function") {
|
|
@@ -11164,7 +11505,7 @@ async function handleText(msg, channel) {
|
|
|
11164
11505
|
}
|
|
11165
11506
|
return;
|
|
11166
11507
|
}
|
|
11167
|
-
await sendResponse(chatId, channel,
|
|
11508
|
+
await sendResponse(chatId, channel, responseText, msg.messageId);
|
|
11168
11509
|
} catch (err) {
|
|
11169
11510
|
error("[router] Error:", err);
|
|
11170
11511
|
const errMsg = errorMessage(err);
|
|
@@ -11999,6 +12340,7 @@ var init_router = __esm({
|
|
|
11999
12340
|
init_format_time();
|
|
12000
12341
|
init_agent();
|
|
12001
12342
|
init_retry();
|
|
12343
|
+
init_classify();
|
|
12002
12344
|
init_image_gen();
|
|
12003
12345
|
init_stt();
|
|
12004
12346
|
init_store4();
|
|
@@ -12128,7 +12470,7 @@ var init_router = __esm({
|
|
|
12128
12470
|
// src/skills/bootstrap.ts
|
|
12129
12471
|
import { existsSync as existsSync14 } from "fs";
|
|
12130
12472
|
import { readdir as readdir3, readFile as readFile6, writeFile as writeFile4, copyFile } from "fs/promises";
|
|
12131
|
-
import { join as
|
|
12473
|
+
import { join as join16, dirname as dirname2 } from "path";
|
|
12132
12474
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
12133
12475
|
async function copyAgentManifestSkills() {
|
|
12134
12476
|
if (!existsSync14(PKG_SKILLS)) return;
|
|
@@ -12136,8 +12478,8 @@ async function copyAgentManifestSkills() {
|
|
|
12136
12478
|
const entries = await readdir3(PKG_SKILLS, { withFileTypes: true });
|
|
12137
12479
|
for (const entry of entries) {
|
|
12138
12480
|
if (!entry.isFile() || !entry.name.startsWith("agent-") || !entry.name.endsWith(".md")) continue;
|
|
12139
|
-
const src =
|
|
12140
|
-
const dest =
|
|
12481
|
+
const src = join16(PKG_SKILLS, entry.name);
|
|
12482
|
+
const dest = join16(SKILLS_PATH, entry.name);
|
|
12141
12483
|
if (existsSync14(dest)) continue;
|
|
12142
12484
|
await copyFile(src, dest);
|
|
12143
12485
|
log(`[skills] Bootstrapped ${entry.name} to ${SKILLS_PATH}`);
|
|
@@ -12148,7 +12490,7 @@ async function copyAgentManifestSkills() {
|
|
|
12148
12490
|
}
|
|
12149
12491
|
async function bootstrapSkills() {
|
|
12150
12492
|
await copyAgentManifestSkills();
|
|
12151
|
-
const usmDir =
|
|
12493
|
+
const usmDir = join16(SKILLS_PATH, USM_DIR_NAME);
|
|
12152
12494
|
if (existsSync14(usmDir)) return;
|
|
12153
12495
|
try {
|
|
12154
12496
|
const entries = await readdir3(SKILLS_PATH);
|
|
@@ -12171,7 +12513,7 @@ async function bootstrapSkills() {
|
|
|
12171
12513
|
}
|
|
12172
12514
|
}
|
|
12173
12515
|
async function patchUsmForCcClaw(usmDir) {
|
|
12174
|
-
const skillPath =
|
|
12516
|
+
const skillPath = join16(usmDir, "SKILL.md");
|
|
12175
12517
|
if (!existsSync14(skillPath)) return;
|
|
12176
12518
|
try {
|
|
12177
12519
|
let content = await readFile6(skillPath, "utf-8");
|
|
@@ -12217,8 +12559,8 @@ var init_bootstrap = __esm({
|
|
|
12217
12559
|
USM_REPO = "jacob-bd/universal-skills-manager";
|
|
12218
12560
|
USM_DIR_NAME = "universal-skills-manager";
|
|
12219
12561
|
CC_CLAW_ECOSYSTEM_PATCH = `| **CC-Claw** | \`~/.cc-claw/workspace/skills/\` | N/A (daemon, no project scope) |`;
|
|
12220
|
-
PKG_ROOT =
|
|
12221
|
-
PKG_SKILLS =
|
|
12562
|
+
PKG_ROOT = join16(dirname2(fileURLToPath2(import.meta.url)), "..", "..");
|
|
12563
|
+
PKG_SKILLS = join16(PKG_ROOT, "skills");
|
|
12222
12564
|
}
|
|
12223
12565
|
});
|
|
12224
12566
|
|
|
@@ -12433,7 +12775,7 @@ __export(ai_skill_exports, {
|
|
|
12433
12775
|
installAiSkill: () => installAiSkill
|
|
12434
12776
|
});
|
|
12435
12777
|
import { existsSync as existsSync15, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6 } from "fs";
|
|
12436
|
-
import { join as
|
|
12778
|
+
import { join as join17 } from "path";
|
|
12437
12779
|
import { homedir as homedir5 } from "os";
|
|
12438
12780
|
function generateAiSkill() {
|
|
12439
12781
|
const version = VERSION;
|
|
@@ -12734,8 +13076,8 @@ function installAiSkill() {
|
|
|
12734
13076
|
const failed = [];
|
|
12735
13077
|
for (const [backend2, dirs] of Object.entries(BACKEND_SKILL_DIRS2)) {
|
|
12736
13078
|
for (const dir of dirs) {
|
|
12737
|
-
const skillDir =
|
|
12738
|
-
const skillPath =
|
|
13079
|
+
const skillDir = join17(dir, "cc-claw-cli");
|
|
13080
|
+
const skillPath = join17(skillDir, "SKILL.md");
|
|
12739
13081
|
try {
|
|
12740
13082
|
mkdirSync6(skillDir, { recursive: true });
|
|
12741
13083
|
writeFileSync5(skillPath, skill, "utf-8");
|
|
@@ -12754,10 +13096,10 @@ var init_ai_skill = __esm({
|
|
|
12754
13096
|
init_paths();
|
|
12755
13097
|
init_version();
|
|
12756
13098
|
BACKEND_SKILL_DIRS2 = {
|
|
12757
|
-
"cc-claw": [
|
|
12758
|
-
claude: [
|
|
12759
|
-
gemini: [
|
|
12760
|
-
codex: [
|
|
13099
|
+
"cc-claw": [join17(homedir5(), ".cc-claw", "workspace", "skills")],
|
|
13100
|
+
claude: [join17(homedir5(), ".claude", "skills")],
|
|
13101
|
+
gemini: [join17(homedir5(), ".gemini", "skills")],
|
|
13102
|
+
codex: [join17(homedir5(), ".agents", "skills")]
|
|
12761
13103
|
};
|
|
12762
13104
|
}
|
|
12763
13105
|
});
|
|
@@ -12768,17 +13110,17 @@ __export(index_exports, {
|
|
|
12768
13110
|
main: () => main
|
|
12769
13111
|
});
|
|
12770
13112
|
import { mkdirSync as mkdirSync7, existsSync as existsSync16, renameSync, statSync as statSync2, readFileSync as readFileSync10 } from "fs";
|
|
12771
|
-
import { join as
|
|
13113
|
+
import { join as join18 } from "path";
|
|
12772
13114
|
import dotenv from "dotenv";
|
|
12773
13115
|
function migrateLayout() {
|
|
12774
13116
|
const moves = [
|
|
12775
|
-
[
|
|
12776
|
-
[
|
|
12777
|
-
[
|
|
12778
|
-
[
|
|
12779
|
-
[
|
|
12780
|
-
[
|
|
12781
|
-
[
|
|
13117
|
+
[join18(CC_CLAW_HOME, "cc-claw.db"), join18(DATA_PATH, "cc-claw.db")],
|
|
13118
|
+
[join18(CC_CLAW_HOME, "cc-claw.db-shm"), join18(DATA_PATH, "cc-claw.db-shm")],
|
|
13119
|
+
[join18(CC_CLAW_HOME, "cc-claw.db-wal"), join18(DATA_PATH, "cc-claw.db-wal")],
|
|
13120
|
+
[join18(CC_CLAW_HOME, "cc-claw.log"), join18(LOGS_PATH, "cc-claw.log")],
|
|
13121
|
+
[join18(CC_CLAW_HOME, "cc-claw.log.1"), join18(LOGS_PATH, "cc-claw.log.1")],
|
|
13122
|
+
[join18(CC_CLAW_HOME, "cc-claw.error.log"), join18(LOGS_PATH, "cc-claw.error.log")],
|
|
13123
|
+
[join18(CC_CLAW_HOME, "cc-claw.error.log.1"), join18(LOGS_PATH, "cc-claw.error.log.1")]
|
|
12782
13124
|
];
|
|
12783
13125
|
for (const [from, to] of moves) {
|
|
12784
13126
|
if (existsSync16(from) && !existsSync16(to)) {
|
|
@@ -12884,10 +13226,10 @@ async function main() {
|
|
|
12884
13226
|
try {
|
|
12885
13227
|
const { generateAiSkill: generateAiSkill2 } = await Promise.resolve().then(() => (init_ai_skill(), ai_skill_exports));
|
|
12886
13228
|
const { writeFileSync: writeFileSync8, mkdirSync: mkdirSync11 } = await import("fs");
|
|
12887
|
-
const { join:
|
|
12888
|
-
const skillDir =
|
|
13229
|
+
const { join: join21 } = await import("path");
|
|
13230
|
+
const skillDir = join21(SKILLS_PATH, "cc-claw-cli");
|
|
12889
13231
|
mkdirSync11(skillDir, { recursive: true });
|
|
12890
|
-
writeFileSync8(
|
|
13232
|
+
writeFileSync8(join21(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
|
|
12891
13233
|
log("[cc-claw] AI skill updated");
|
|
12892
13234
|
} catch {
|
|
12893
13235
|
}
|
|
@@ -12977,10 +13319,10 @@ __export(service_exports, {
|
|
|
12977
13319
|
serviceStatus: () => serviceStatus,
|
|
12978
13320
|
uninstallService: () => uninstallService
|
|
12979
13321
|
});
|
|
12980
|
-
import { existsSync as existsSync17, mkdirSync as mkdirSync8, writeFileSync as writeFileSync6, unlinkSync as
|
|
13322
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync8, writeFileSync as writeFileSync6, unlinkSync as unlinkSync3 } from "fs";
|
|
12981
13323
|
import { execFileSync as execFileSync2, execSync as execSync5 } from "child_process";
|
|
12982
13324
|
import { homedir as homedir6, platform } from "os";
|
|
12983
|
-
import { join as
|
|
13325
|
+
import { join as join19, dirname as dirname3 } from "path";
|
|
12984
13326
|
function resolveExecutable2(name) {
|
|
12985
13327
|
try {
|
|
12986
13328
|
return execFileSync2("which", [name], { encoding: "utf-8" }).trim();
|
|
@@ -12993,14 +13335,14 @@ function getPathDirs() {
|
|
|
12993
13335
|
const home = homedir6();
|
|
12994
13336
|
const dirs = /* @__PURE__ */ new Set([
|
|
12995
13337
|
nodeBin,
|
|
12996
|
-
|
|
13338
|
+
join19(home, ".local", "bin"),
|
|
12997
13339
|
"/usr/local/bin",
|
|
12998
13340
|
"/usr/bin",
|
|
12999
13341
|
"/bin"
|
|
13000
13342
|
]);
|
|
13001
13343
|
try {
|
|
13002
13344
|
const prefix = execSync5("npm config get prefix", { encoding: "utf-8" }).trim();
|
|
13003
|
-
if (prefix) dirs.add(
|
|
13345
|
+
if (prefix) dirs.add(join19(prefix, "bin"));
|
|
13004
13346
|
} catch {
|
|
13005
13347
|
}
|
|
13006
13348
|
return [...dirs].join(":");
|
|
@@ -13077,7 +13419,7 @@ function uninstallMacOS() {
|
|
|
13077
13419
|
execFileSync2("launchctl", ["unload", PLIST_PATH]);
|
|
13078
13420
|
} catch {
|
|
13079
13421
|
}
|
|
13080
|
-
|
|
13422
|
+
unlinkSync3(PLIST_PATH);
|
|
13081
13423
|
console.log(" Service uninstalled.");
|
|
13082
13424
|
}
|
|
13083
13425
|
function statusMacOS() {
|
|
@@ -13142,7 +13484,7 @@ function uninstallLinux() {
|
|
|
13142
13484
|
execFileSync2("systemctl", ["--user", "disable", "cc-claw"]);
|
|
13143
13485
|
} catch {
|
|
13144
13486
|
}
|
|
13145
|
-
|
|
13487
|
+
unlinkSync3(UNIT_PATH);
|
|
13146
13488
|
execFileSync2("systemctl", ["--user", "daemon-reload"]);
|
|
13147
13489
|
console.log(" Service uninstalled.");
|
|
13148
13490
|
}
|
|
@@ -13155,7 +13497,7 @@ function statusLinux() {
|
|
|
13155
13497
|
}
|
|
13156
13498
|
}
|
|
13157
13499
|
function installService() {
|
|
13158
|
-
if (!existsSync17(
|
|
13500
|
+
if (!existsSync17(join19(CC_CLAW_HOME, ".env"))) {
|
|
13159
13501
|
console.error(` Config not found at ${CC_CLAW_HOME}/.env`);
|
|
13160
13502
|
console.error(" Run 'cc-claw setup' before installing the service.");
|
|
13161
13503
|
process.exitCode = 1;
|
|
@@ -13184,9 +13526,9 @@ var init_service = __esm({
|
|
|
13184
13526
|
"use strict";
|
|
13185
13527
|
init_paths();
|
|
13186
13528
|
PLIST_LABEL = "com.cc-claw";
|
|
13187
|
-
PLIST_PATH =
|
|
13188
|
-
SYSTEMD_DIR =
|
|
13189
|
-
UNIT_PATH =
|
|
13529
|
+
PLIST_PATH = join19(homedir6(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
13530
|
+
SYSTEMD_DIR = join19(homedir6(), ".config", "systemd", "user");
|
|
13531
|
+
UNIT_PATH = join19(SYSTEMD_DIR, "cc-claw.service");
|
|
13190
13532
|
}
|
|
13191
13533
|
});
|
|
13192
13534
|
|
|
@@ -14588,7 +14930,7 @@ __export(db_exports, {
|
|
|
14588
14930
|
dbPath: () => dbPath,
|
|
14589
14931
|
dbStats: () => dbStats
|
|
14590
14932
|
});
|
|
14591
|
-
import { existsSync as existsSync27, statSync as statSync5, copyFileSync, mkdirSync as mkdirSync9 } from "fs";
|
|
14933
|
+
import { existsSync as existsSync27, statSync as statSync5, copyFileSync as copyFileSync2, mkdirSync as mkdirSync9 } from "fs";
|
|
14592
14934
|
import { dirname as dirname4 } from "path";
|
|
14593
14935
|
async function dbStats(globalOpts) {
|
|
14594
14936
|
if (!existsSync27(DB_PATH)) {
|
|
@@ -14640,9 +14982,9 @@ async function dbBackup(globalOpts, destPath) {
|
|
|
14640
14982
|
const dest = destPath ?? `${DB_PATH}.backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
14641
14983
|
try {
|
|
14642
14984
|
mkdirSync9(dirname4(dest), { recursive: true });
|
|
14643
|
-
|
|
14985
|
+
copyFileSync2(DB_PATH, dest);
|
|
14644
14986
|
const walPath = DB_PATH + "-wal";
|
|
14645
|
-
if (existsSync27(walPath))
|
|
14987
|
+
if (existsSync27(walPath)) copyFileSync2(walPath, dest + "-wal");
|
|
14646
14988
|
output({ path: dest, sizeBytes: statSync5(dest).size }, (d) => {
|
|
14647
14989
|
const b = d;
|
|
14648
14990
|
return `
|
|
@@ -16037,10 +16379,10 @@ var init_completion = __esm({
|
|
|
16037
16379
|
|
|
16038
16380
|
// src/setup.ts
|
|
16039
16381
|
var setup_exports = {};
|
|
16040
|
-
import { existsSync as existsSync38, writeFileSync as writeFileSync7, readFileSync as readFileSync17, copyFileSync as
|
|
16382
|
+
import { existsSync as existsSync38, writeFileSync as writeFileSync7, readFileSync as readFileSync17, copyFileSync as copyFileSync3, mkdirSync as mkdirSync10, statSync as statSync6 } from "fs";
|
|
16041
16383
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
16042
16384
|
import { createInterface as createInterface5 } from "readline";
|
|
16043
|
-
import { join as
|
|
16385
|
+
import { join as join20 } from "path";
|
|
16044
16386
|
function divider2() {
|
|
16045
16387
|
console.log(dim("\u2500".repeat(55)));
|
|
16046
16388
|
}
|
|
@@ -16127,13 +16469,13 @@ async function setup() {
|
|
|
16127
16469
|
if (match) env[match[1].trim()] = match[2].trim();
|
|
16128
16470
|
}
|
|
16129
16471
|
}
|
|
16130
|
-
const cwdDb =
|
|
16472
|
+
const cwdDb = join20(process.cwd(), "cc-claw.db");
|
|
16131
16473
|
if (existsSync38(cwdDb) && !existsSync38(DB_PATH)) {
|
|
16132
16474
|
const { size } = statSync6(cwdDb);
|
|
16133
16475
|
console.log(yellow(` Found existing database at ${cwdDb} (${(size / 1024).toFixed(0)}KB)`));
|
|
16134
16476
|
const migrate = await confirm("Copy database to ~/.cc-claw/? (preserves memories & history)", true);
|
|
16135
16477
|
if (migrate) {
|
|
16136
|
-
|
|
16478
|
+
copyFileSync3(cwdDb, DB_PATH);
|
|
16137
16479
|
console.log(green(` Database copied to ${DB_PATH}`));
|
|
16138
16480
|
}
|
|
16139
16481
|
console.log("");
|