@rely-ai/caliber 1.31.0-dev.1774741028 → 1.31.0-dev.1774815271
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/bin.js +1170 -792
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -9,155 +9,6 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
// src/llm/config.ts
|
|
13
|
-
var config_exports = {};
|
|
14
|
-
__export(config_exports, {
|
|
15
|
-
DEFAULT_FAST_MODELS: () => DEFAULT_FAST_MODELS,
|
|
16
|
-
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
17
|
-
MODEL_CONTEXT_WINDOWS: () => MODEL_CONTEXT_WINDOWS,
|
|
18
|
-
getConfigFilePath: () => getConfigFilePath,
|
|
19
|
-
getDisplayModel: () => getDisplayModel,
|
|
20
|
-
getFastModel: () => getFastModel,
|
|
21
|
-
getMaxPromptTokens: () => getMaxPromptTokens,
|
|
22
|
-
loadConfig: () => loadConfig,
|
|
23
|
-
readConfigFile: () => readConfigFile,
|
|
24
|
-
resolveFromEnv: () => resolveFromEnv,
|
|
25
|
-
writeConfigFile: () => writeConfigFile
|
|
26
|
-
});
|
|
27
|
-
import fs4 from "fs";
|
|
28
|
-
import path5 from "path";
|
|
29
|
-
import os2 from "os";
|
|
30
|
-
function getMaxPromptTokens() {
|
|
31
|
-
const config = loadConfig();
|
|
32
|
-
const model = process.env.CALIBER_MODEL || config?.model;
|
|
33
|
-
const contextWindow = model ? MODEL_CONTEXT_WINDOWS[model] ?? DEFAULT_CONTEXT_WINDOW : DEFAULT_CONTEXT_WINDOW;
|
|
34
|
-
const budget = Math.floor(contextWindow * INPUT_BUDGET_FRACTION);
|
|
35
|
-
return Math.max(MIN_PROMPT_TOKENS, Math.min(budget, MAX_PROMPT_TOKENS_CAP));
|
|
36
|
-
}
|
|
37
|
-
function loadConfig() {
|
|
38
|
-
const envConfig = resolveFromEnv();
|
|
39
|
-
if (envConfig) return envConfig;
|
|
40
|
-
return readConfigFile();
|
|
41
|
-
}
|
|
42
|
-
function resolveFromEnv() {
|
|
43
|
-
if (process.env.ANTHROPIC_API_KEY) {
|
|
44
|
-
return {
|
|
45
|
-
provider: "anthropic",
|
|
46
|
-
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
47
|
-
model: process.env.CALIBER_MODEL || DEFAULT_MODELS.anthropic
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
if (process.env.VERTEX_PROJECT_ID || process.env.GCP_PROJECT_ID) {
|
|
51
|
-
return {
|
|
52
|
-
provider: "vertex",
|
|
53
|
-
model: process.env.CALIBER_MODEL || DEFAULT_MODELS.vertex,
|
|
54
|
-
vertexProjectId: process.env.VERTEX_PROJECT_ID || process.env.GCP_PROJECT_ID,
|
|
55
|
-
vertexRegion: process.env.VERTEX_REGION || process.env.GCP_REGION || "us-east5",
|
|
56
|
-
vertexCredentials: process.env.VERTEX_SA_CREDENTIALS || process.env.GOOGLE_APPLICATION_CREDENTIALS
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
if (process.env.OPENAI_API_KEY) {
|
|
60
|
-
return {
|
|
61
|
-
provider: "openai",
|
|
62
|
-
apiKey: process.env.OPENAI_API_KEY,
|
|
63
|
-
model: process.env.CALIBER_MODEL || DEFAULT_MODELS.openai,
|
|
64
|
-
baseUrl: process.env.OPENAI_BASE_URL
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
if (process.env.CALIBER_USE_CURSOR_SEAT === "1" || process.env.CALIBER_USE_CURSOR_SEAT === "true") {
|
|
68
|
-
return {
|
|
69
|
-
provider: "cursor",
|
|
70
|
-
model: process.env.CALIBER_MODEL || DEFAULT_MODELS.cursor
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
if (process.env.CALIBER_USE_CLAUDE_CLI === "1" || process.env.CALIBER_USE_CLAUDE_CLI === "true") {
|
|
74
|
-
return {
|
|
75
|
-
provider: "claude-cli",
|
|
76
|
-
model: process.env.CALIBER_MODEL || DEFAULT_MODELS["claude-cli"]
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
function readConfigFile() {
|
|
82
|
-
try {
|
|
83
|
-
if (!fs4.existsSync(CONFIG_FILE)) return null;
|
|
84
|
-
const raw = fs4.readFileSync(CONFIG_FILE, "utf-8");
|
|
85
|
-
const parsed = JSON.parse(raw);
|
|
86
|
-
if (!parsed.provider || !["anthropic", "vertex", "openai", "cursor", "claude-cli"].includes(parsed.provider)) {
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
return parsed;
|
|
90
|
-
} catch {
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
function writeConfigFile(config) {
|
|
95
|
-
if (!fs4.existsSync(CONFIG_DIR)) {
|
|
96
|
-
fs4.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
97
|
-
}
|
|
98
|
-
const sanitized = { ...config };
|
|
99
|
-
if (sanitized.apiKey) {
|
|
100
|
-
sanitized.apiKey = sanitized.apiKey.trim();
|
|
101
|
-
}
|
|
102
|
-
fs4.writeFileSync(CONFIG_FILE, JSON.stringify(sanitized, null, 2) + "\n", { mode: 384 });
|
|
103
|
-
}
|
|
104
|
-
function getConfigFilePath() {
|
|
105
|
-
return CONFIG_FILE;
|
|
106
|
-
}
|
|
107
|
-
function getDisplayModel(config) {
|
|
108
|
-
if (config.model === "default" && config.provider === "claude-cli") {
|
|
109
|
-
return process.env.ANTHROPIC_MODEL || "default (inherited from Claude Code)";
|
|
110
|
-
}
|
|
111
|
-
return config.model;
|
|
112
|
-
}
|
|
113
|
-
function getFastModel() {
|
|
114
|
-
if (process.env.CALIBER_FAST_MODEL) return process.env.CALIBER_FAST_MODEL;
|
|
115
|
-
const config = loadConfig();
|
|
116
|
-
const provider = config?.provider;
|
|
117
|
-
if (process.env.ANTHROPIC_SMALL_FAST_MODEL && (!provider || provider === "anthropic" || provider === "vertex" || provider === "claude-cli")) {
|
|
118
|
-
return process.env.ANTHROPIC_SMALL_FAST_MODEL;
|
|
119
|
-
}
|
|
120
|
-
if (config?.fastModel) return config.fastModel;
|
|
121
|
-
if (provider) return DEFAULT_FAST_MODELS[provider];
|
|
122
|
-
return void 0;
|
|
123
|
-
}
|
|
124
|
-
var CONFIG_DIR, CONFIG_FILE, DEFAULT_MODELS, MODEL_CONTEXT_WINDOWS, DEFAULT_CONTEXT_WINDOW, INPUT_BUDGET_FRACTION, MAX_PROMPT_TOKENS_CAP, MIN_PROMPT_TOKENS, DEFAULT_FAST_MODELS;
|
|
125
|
-
var init_config = __esm({
|
|
126
|
-
"src/llm/config.ts"() {
|
|
127
|
-
"use strict";
|
|
128
|
-
CONFIG_DIR = path5.join(os2.homedir(), ".caliber");
|
|
129
|
-
CONFIG_FILE = path5.join(CONFIG_DIR, "config.json");
|
|
130
|
-
DEFAULT_MODELS = {
|
|
131
|
-
anthropic: "claude-sonnet-4-6",
|
|
132
|
-
vertex: "claude-sonnet-4-6",
|
|
133
|
-
openai: "gpt-4.1",
|
|
134
|
-
cursor: "sonnet-4.6",
|
|
135
|
-
"claude-cli": "default"
|
|
136
|
-
};
|
|
137
|
-
MODEL_CONTEXT_WINDOWS = {
|
|
138
|
-
"claude-sonnet-4-6": 2e5,
|
|
139
|
-
"claude-opus-4-6": 2e5,
|
|
140
|
-
"claude-haiku-4-5-20251001": 2e5,
|
|
141
|
-
"claude-sonnet-4-5-20250514": 2e5,
|
|
142
|
-
"gpt-4.1": 1e6,
|
|
143
|
-
"gpt-4.1-mini": 1e6,
|
|
144
|
-
"gpt-4o": 128e3,
|
|
145
|
-
"gpt-4o-mini": 128e3,
|
|
146
|
-
"sonnet-4.6": 2e5
|
|
147
|
-
};
|
|
148
|
-
DEFAULT_CONTEXT_WINDOW = 2e5;
|
|
149
|
-
INPUT_BUDGET_FRACTION = 0.6;
|
|
150
|
-
MAX_PROMPT_TOKENS_CAP = 3e5;
|
|
151
|
-
MIN_PROMPT_TOKENS = 3e4;
|
|
152
|
-
DEFAULT_FAST_MODELS = {
|
|
153
|
-
anthropic: "claude-haiku-4-5-20251001",
|
|
154
|
-
vertex: "claude-haiku-4-5-20251001",
|
|
155
|
-
openai: "gpt-4.1-mini",
|
|
156
|
-
cursor: "gpt-5.3-codex-fast"
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
|
|
161
12
|
// src/lib/resolve-caliber.ts
|
|
162
13
|
var resolve_caliber_exports = {};
|
|
163
14
|
__export(resolve_caliber_exports, {
|
|
@@ -166,8 +17,8 @@ __export(resolve_caliber_exports, {
|
|
|
166
17
|
resetResolvedCaliber: () => resetResolvedCaliber,
|
|
167
18
|
resolveCaliber: () => resolveCaliber
|
|
168
19
|
});
|
|
169
|
-
import
|
|
170
|
-
import { execSync as
|
|
20
|
+
import fs2 from "fs";
|
|
21
|
+
import { execSync as execSync3 } from "child_process";
|
|
171
22
|
function resolveCaliber() {
|
|
172
23
|
if (_resolved) return _resolved;
|
|
173
24
|
const isNpx = process.argv[1]?.includes("_npx") || process.env.npm_execpath?.includes("npx");
|
|
@@ -177,7 +28,7 @@ function resolveCaliber() {
|
|
|
177
28
|
}
|
|
178
29
|
try {
|
|
179
30
|
const whichCmd = process.platform === "win32" ? "where caliber" : "which caliber";
|
|
180
|
-
|
|
31
|
+
execSync3(whichCmd, {
|
|
181
32
|
encoding: "utf-8",
|
|
182
33
|
stdio: ["pipe", "pipe", "pipe"]
|
|
183
34
|
});
|
|
@@ -186,7 +37,7 @@ function resolveCaliber() {
|
|
|
186
37
|
} catch {
|
|
187
38
|
}
|
|
188
39
|
const binPath = process.argv[1];
|
|
189
|
-
if (binPath && /caliber/.test(binPath) &&
|
|
40
|
+
if (binPath && /caliber/.test(binPath) && fs2.existsSync(binPath)) {
|
|
190
41
|
_resolved = binPath;
|
|
191
42
|
return _resolved;
|
|
192
43
|
}
|
|
@@ -214,214 +65,65 @@ var init_resolve_caliber = __esm({
|
|
|
214
65
|
}
|
|
215
66
|
});
|
|
216
67
|
|
|
217
|
-
// src/
|
|
218
|
-
var
|
|
219
|
-
__export(
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
"use strict";
|
|
229
|
-
SEAT_BASED_PROVIDERS = /* @__PURE__ */ new Set(["cursor", "claude-cli"]);
|
|
230
|
-
}
|
|
68
|
+
// src/lib/builtin-skills.ts
|
|
69
|
+
var builtin_skills_exports = {};
|
|
70
|
+
__export(builtin_skills_exports, {
|
|
71
|
+
BUILTIN_SKILLS: () => BUILTIN_SKILLS,
|
|
72
|
+
BUILTIN_SKILL_NAMES: () => BUILTIN_SKILL_NAMES,
|
|
73
|
+
FIND_SKILLS_SKILL: () => FIND_SKILLS_SKILL,
|
|
74
|
+
PLATFORM_CONFIGS: () => PLATFORM_CONFIGS,
|
|
75
|
+
SAVE_LEARNING_SKILL: () => SAVE_LEARNING_SKILL,
|
|
76
|
+
SETUP_CALIBER_SKILL: () => SETUP_CALIBER_SKILL,
|
|
77
|
+
buildSkillContent: () => buildSkillContent,
|
|
78
|
+
ensureBuiltinSkills: () => ensureBuiltinSkills
|
|
231
79
|
});
|
|
80
|
+
import fs3 from "fs";
|
|
81
|
+
import path3 from "path";
|
|
82
|
+
function buildSkillContent(skill) {
|
|
83
|
+
const frontmatter = `---
|
|
84
|
+
name: ${skill.name}
|
|
85
|
+
description: ${skill.description}
|
|
86
|
+
---
|
|
232
87
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
appendPreCommitBlock: () => appendPreCommitBlock,
|
|
238
|
-
appendSyncBlock: () => appendSyncBlock,
|
|
239
|
-
getCursorLearningsRule: () => getCursorLearningsRule,
|
|
240
|
-
getCursorPreCommitRule: () => getCursorPreCommitRule,
|
|
241
|
-
getCursorSyncRule: () => getCursorSyncRule,
|
|
242
|
-
hasLearningsBlock: () => hasLearningsBlock,
|
|
243
|
-
hasPreCommitBlock: () => hasPreCommitBlock,
|
|
244
|
-
hasSyncBlock: () => hasSyncBlock
|
|
245
|
-
});
|
|
246
|
-
function getPreCommitBlock() {
|
|
88
|
+
`;
|
|
89
|
+
return frontmatter + skill.content;
|
|
90
|
+
}
|
|
91
|
+
function getFindSkillsContent() {
|
|
247
92
|
const bin = resolveCaliber();
|
|
248
|
-
return
|
|
249
|
-
## Before Committing
|
|
93
|
+
return `# Find Skills
|
|
250
94
|
|
|
251
|
-
|
|
95
|
+
Search the public skill registry for community-contributed skills
|
|
96
|
+
relevant to the user's current task and install them into this project.
|
|
252
97
|
|
|
253
|
-
|
|
254
|
-
\`\`\`bash
|
|
255
|
-
grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "hook-active" || echo "no-hook"
|
|
256
|
-
\`\`\`
|
|
98
|
+
## Instructions
|
|
257
99
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
100
|
+
1. Identify the key technologies, frameworks, or task types from the
|
|
101
|
+
user's request that might have community skills available
|
|
102
|
+
2. Ask the user: "Would you like me to search for community skills
|
|
103
|
+
for [identified technologies]?"
|
|
104
|
+
3. If the user agrees, run:
|
|
105
|
+
\`\`\`bash
|
|
106
|
+
${bin} skills --query "<relevant terms>"
|
|
107
|
+
\`\`\`
|
|
108
|
+
This outputs the top 5 matching skills with scores and descriptions.
|
|
109
|
+
4. Present the results to the user and ask which ones to install
|
|
110
|
+
5. Install the selected skills:
|
|
111
|
+
\`\`\`bash
|
|
112
|
+
${bin} skills --install <slug1>,<slug2>
|
|
113
|
+
\`\`\`
|
|
114
|
+
6. Read the installed SKILL.md files to load them into your current
|
|
115
|
+
context so you can use them immediately in this session
|
|
116
|
+
7. Summarize what was installed and continue with the user's task
|
|
263
117
|
|
|
264
|
-
|
|
265
|
-
${BLOCK_END}`;
|
|
266
|
-
}
|
|
267
|
-
function getCursorRuleContent() {
|
|
268
|
-
const bin = resolveCaliber();
|
|
269
|
-
return `---
|
|
270
|
-
description: Run Caliber sync before git commits to keep agent configs up to date
|
|
271
|
-
alwaysApply: true
|
|
272
|
-
---
|
|
273
|
-
Before every git commit, check if the Caliber pre-commit hook is installed:
|
|
274
|
-
Run: \`grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "hook-active" || echo "no-hook"\`
|
|
118
|
+
## Examples
|
|
275
119
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
If \`${bin}
|
|
280
|
-
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return content.includes(BLOCK_START);
|
|
284
|
-
}
|
|
285
|
-
function appendPreCommitBlock(content) {
|
|
286
|
-
if (hasPreCommitBlock(content)) return content;
|
|
287
|
-
const trimmed = content.trimEnd();
|
|
288
|
-
return trimmed + "\n\n" + getPreCommitBlock() + "\n";
|
|
289
|
-
}
|
|
290
|
-
function getCursorPreCommitRule() {
|
|
291
|
-
return { filename: CURSOR_RULE_FILENAME, content: getCursorRuleContent() };
|
|
292
|
-
}
|
|
293
|
-
function hasLearningsBlock(content) {
|
|
294
|
-
return content.includes(LEARNINGS_BLOCK_START);
|
|
295
|
-
}
|
|
296
|
-
function appendLearningsBlock(content) {
|
|
297
|
-
if (hasLearningsBlock(content)) return content;
|
|
298
|
-
const trimmed = content.trimEnd();
|
|
299
|
-
return trimmed + "\n\n" + LEARNINGS_BLOCK + "\n";
|
|
300
|
-
}
|
|
301
|
-
function getCursorLearningsRule() {
|
|
302
|
-
return { filename: CURSOR_LEARNINGS_FILENAME, content: CURSOR_LEARNINGS_CONTENT };
|
|
303
|
-
}
|
|
304
|
-
function getSyncBlock() {
|
|
305
|
-
const bin = resolveCaliber();
|
|
306
|
-
return `${SYNC_BLOCK_START}
|
|
307
|
-
## Context Sync
|
|
308
|
-
|
|
309
|
-
This project uses [Caliber](https://github.com/caliber-ai-org/ai-setup) to keep AI agent configs in sync across Claude Code, Cursor, Copilot, and Codex.
|
|
310
|
-
Configs update automatically before each commit via \`${bin} refresh\`.
|
|
311
|
-
If the pre-commit hook is not set up, run \`${bin} hooks --install\` to enable automatic sync.
|
|
312
|
-
${SYNC_BLOCK_END}`;
|
|
313
|
-
}
|
|
314
|
-
function hasSyncBlock(content) {
|
|
315
|
-
return content.includes(SYNC_BLOCK_START);
|
|
316
|
-
}
|
|
317
|
-
function appendSyncBlock(content) {
|
|
318
|
-
if (hasSyncBlock(content)) return content;
|
|
319
|
-
const trimmed = content.trimEnd();
|
|
320
|
-
return trimmed + "\n\n" + getSyncBlock() + "\n";
|
|
321
|
-
}
|
|
322
|
-
function getCursorSyncContent() {
|
|
323
|
-
const bin = resolveCaliber();
|
|
324
|
-
return `---
|
|
325
|
-
description: This project uses Caliber for automatic AI agent context sync
|
|
326
|
-
alwaysApply: true
|
|
327
|
-
---
|
|
328
|
-
This project uses Caliber to keep AI agent configs (CLAUDE.md, Cursor rules, Copilot instructions)
|
|
329
|
-
in sync with the codebase automatically. Before each commit, run \`${bin} refresh\` to sync configs.
|
|
330
|
-
If \`${bin}\` is not found, read the setup-caliber skill from .cursor/skills/setup-caliber/SKILL.md and follow its instructions to install Caliber.
|
|
331
|
-
`;
|
|
332
|
-
}
|
|
333
|
-
function getCursorSyncRule() {
|
|
334
|
-
return { filename: CURSOR_SYNC_FILENAME, content: getCursorSyncContent() };
|
|
335
|
-
}
|
|
336
|
-
var BLOCK_START, BLOCK_END, MANAGED_DOC_PATHS, CURSOR_RULE_FILENAME, LEARNINGS_BLOCK_START, LEARNINGS_BLOCK_END, LEARNINGS_BLOCK, CURSOR_LEARNINGS_FILENAME, CURSOR_LEARNINGS_CONTENT, SYNC_BLOCK_START, SYNC_BLOCK_END, CURSOR_SYNC_FILENAME;
|
|
337
|
-
var init_pre_commit_block = __esm({
|
|
338
|
-
"src/writers/pre-commit-block.ts"() {
|
|
339
|
-
"use strict";
|
|
340
|
-
init_resolve_caliber();
|
|
341
|
-
BLOCK_START = "<!-- caliber:managed:pre-commit -->";
|
|
342
|
-
BLOCK_END = "<!-- /caliber:managed:pre-commit -->";
|
|
343
|
-
MANAGED_DOC_PATHS = "CLAUDE.md .claude/ .cursor/ .cursorrules .github/copilot-instructions.md .github/instructions/ AGENTS.md CALIBER_LEARNINGS.md";
|
|
344
|
-
CURSOR_RULE_FILENAME = "caliber-pre-commit.mdc";
|
|
345
|
-
LEARNINGS_BLOCK_START = "<!-- caliber:managed:learnings -->";
|
|
346
|
-
LEARNINGS_BLOCK_END = "<!-- /caliber:managed:learnings -->";
|
|
347
|
-
LEARNINGS_BLOCK = `${LEARNINGS_BLOCK_START}
|
|
348
|
-
## Session Learnings
|
|
349
|
-
|
|
350
|
-
Read \`CALIBER_LEARNINGS.md\` for patterns and anti-patterns learned from previous sessions.
|
|
351
|
-
These are auto-extracted from real tool usage \u2014 treat them as project-specific rules.
|
|
352
|
-
${LEARNINGS_BLOCK_END}`;
|
|
353
|
-
CURSOR_LEARNINGS_FILENAME = "caliber-learnings.mdc";
|
|
354
|
-
CURSOR_LEARNINGS_CONTENT = `---
|
|
355
|
-
description: Reference session-learned patterns from CALIBER_LEARNINGS.md
|
|
356
|
-
alwaysApply: true
|
|
357
|
-
---
|
|
358
|
-
Read \`CALIBER_LEARNINGS.md\` for patterns and anti-patterns learned from previous sessions.
|
|
359
|
-
These are auto-extracted from real tool usage \u2014 treat them as project-specific rules.
|
|
360
|
-
`;
|
|
361
|
-
SYNC_BLOCK_START = "<!-- caliber:managed:sync -->";
|
|
362
|
-
SYNC_BLOCK_END = "<!-- /caliber:managed:sync -->";
|
|
363
|
-
CURSOR_SYNC_FILENAME = "caliber-sync.mdc";
|
|
364
|
-
}
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
// src/lib/builtin-skills.ts
|
|
368
|
-
var builtin_skills_exports = {};
|
|
369
|
-
__export(builtin_skills_exports, {
|
|
370
|
-
BUILTIN_SKILLS: () => BUILTIN_SKILLS,
|
|
371
|
-
BUILTIN_SKILL_NAMES: () => BUILTIN_SKILL_NAMES,
|
|
372
|
-
FIND_SKILLS_SKILL: () => FIND_SKILLS_SKILL,
|
|
373
|
-
SAVE_LEARNING_SKILL: () => SAVE_LEARNING_SKILL,
|
|
374
|
-
SETUP_CALIBER_SKILL: () => SETUP_CALIBER_SKILL,
|
|
375
|
-
buildSkillContent: () => buildSkillContent,
|
|
376
|
-
ensureBuiltinSkills: () => ensureBuiltinSkills
|
|
377
|
-
});
|
|
378
|
-
import fs17 from "fs";
|
|
379
|
-
import path16 from "path";
|
|
380
|
-
function buildSkillContent(skill) {
|
|
381
|
-
const frontmatter = `---
|
|
382
|
-
name: ${skill.name}
|
|
383
|
-
description: ${skill.description}
|
|
384
|
-
---
|
|
385
|
-
|
|
386
|
-
`;
|
|
387
|
-
return frontmatter + skill.content;
|
|
388
|
-
}
|
|
389
|
-
function getFindSkillsContent() {
|
|
390
|
-
const bin = resolveCaliber();
|
|
391
|
-
return `# Find Skills
|
|
392
|
-
|
|
393
|
-
Search the public skill registry for community-contributed skills
|
|
394
|
-
relevant to the user's current task and install them into this project.
|
|
395
|
-
|
|
396
|
-
## Instructions
|
|
397
|
-
|
|
398
|
-
1. Identify the key technologies, frameworks, or task types from the
|
|
399
|
-
user's request that might have community skills available
|
|
400
|
-
2. Ask the user: "Would you like me to search for community skills
|
|
401
|
-
for [identified technologies]?"
|
|
402
|
-
3. If the user agrees, run:
|
|
403
|
-
\`\`\`bash
|
|
404
|
-
${bin} skills --query "<relevant terms>"
|
|
405
|
-
\`\`\`
|
|
406
|
-
This outputs the top 5 matching skills with scores and descriptions.
|
|
407
|
-
4. Present the results to the user and ask which ones to install
|
|
408
|
-
5. Install the selected skills:
|
|
409
|
-
\`\`\`bash
|
|
410
|
-
${bin} skills --install <slug1>,<slug2>
|
|
411
|
-
\`\`\`
|
|
412
|
-
6. Read the installed SKILL.md files to load them into your current
|
|
413
|
-
context so you can use them immediately in this session
|
|
414
|
-
7. Summarize what was installed and continue with the user's task
|
|
415
|
-
|
|
416
|
-
## Examples
|
|
417
|
-
|
|
418
|
-
User: "let's build a web app using React"
|
|
419
|
-
-> "I notice you want to work with React. Would you like me to search
|
|
420
|
-
for community skills that could help with React development?"
|
|
421
|
-
-> If yes: run \`${bin} skills --query "react frontend"\`
|
|
422
|
-
-> Show the user the results, ask which to install
|
|
423
|
-
-> Run \`${bin} skills --install <selected-slugs>\`
|
|
424
|
-
-> Read the installed files and continue
|
|
120
|
+
User: "let's build a web app using React"
|
|
121
|
+
-> "I notice you want to work with React. Would you like me to search
|
|
122
|
+
for community skills that could help with React development?"
|
|
123
|
+
-> If yes: run \`${bin} skills --query "react frontend"\`
|
|
124
|
+
-> Show the user the results, ask which to install
|
|
125
|
+
-> Run \`${bin} skills --install <selected-slugs>\`
|
|
126
|
+
-> Read the installed files and continue
|
|
425
127
|
|
|
426
128
|
User: "help me set up Docker for this project"
|
|
427
129
|
-> "Would you like me to search for Docker-related skills?"
|
|
@@ -516,18 +218,19 @@ then only act if something is missing.
|
|
|
516
218
|
command -v caliber >/dev/null 2>&1 && caliber --version || echo "NOT_INSTALLED"
|
|
517
219
|
\`\`\`
|
|
518
220
|
|
|
519
|
-
- If a version prints \u2192 Caliber is installed.
|
|
520
|
-
- If NOT_INSTALLED \u2192
|
|
521
|
-
Then run:
|
|
221
|
+
- If a version prints \u2192 Caliber is installed globally. Set \`CALIBER="caliber"\` and move to Step 2.
|
|
222
|
+
- If NOT_INSTALLED \u2192 Install it globally (faster for daily use since the pre-commit hook runs on every commit):
|
|
522
223
|
\`\`\`bash
|
|
523
224
|
npm install -g @rely-ai/caliber
|
|
524
225
|
\`\`\`
|
|
525
|
-
|
|
226
|
+
Set \`CALIBER="caliber"\`.
|
|
227
|
+
|
|
228
|
+
If npm fails (permissions, no sudo, etc.), fall back to npx:
|
|
526
229
|
\`\`\`bash
|
|
527
|
-
npx @rely-ai/caliber --version
|
|
230
|
+
npx @rely-ai/caliber --version 2>/dev/null || echo "NO_NODE"
|
|
528
231
|
\`\`\`
|
|
529
|
-
If npx works
|
|
530
|
-
If
|
|
232
|
+
- If npx works \u2192 Set \`CALIBER="npx @rely-ai/caliber"\`. This works but adds ~500ms per invocation.
|
|
233
|
+
- If NO_NODE \u2192 Tell the user: "Caliber requires Node.js >= 20. Install Node first, then run /setup-caliber again." Stop here.
|
|
531
234
|
|
|
532
235
|
### Step 2: Check if pre-commit hook is installed
|
|
533
236
|
|
|
@@ -538,35 +241,52 @@ grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "HOOK_ACTIVE" || ech
|
|
|
538
241
|
- If HOOK_ACTIVE \u2192 Tell the user: "Pre-commit hook is active \u2014 configs sync on every commit." Move to Step 3.
|
|
539
242
|
- If NO_HOOK \u2192 Tell the user: "I'll install the pre-commit hook so your agent configs sync automatically on every commit."
|
|
540
243
|
\`\`\`bash
|
|
541
|
-
|
|
244
|
+
$CALIBER hooks --install
|
|
542
245
|
\`\`\`
|
|
543
246
|
|
|
544
|
-
### Step 3:
|
|
247
|
+
### Step 3: Detect agents and check if configs exist
|
|
545
248
|
|
|
249
|
+
First, detect which coding agents are configured in this project:
|
|
250
|
+
\`\`\`bash
|
|
251
|
+
AGENTS=""
|
|
252
|
+
[ -d .claude ] && AGENTS="claude"
|
|
253
|
+
[ -d .cursor ] && AGENTS="\${AGENTS:+$AGENTS,}cursor"
|
|
254
|
+
[ -d .agents ] || [ -f AGENTS.md ] && AGENTS="\${AGENTS:+$AGENTS,}codex"
|
|
255
|
+
[ -f .github/copilot-instructions.md ] && AGENTS="\${AGENTS:+$AGENTS,}github-copilot"
|
|
256
|
+
echo "DETECTED_AGENTS=\${AGENTS:-none}"
|
|
257
|
+
\`\`\`
|
|
258
|
+
|
|
259
|
+
If no agents are detected, ask the user which coding agents they use (Claude Code, Cursor, Codex, GitHub Copilot).
|
|
260
|
+
Build the agent list from their answer as a comma-separated string (e.g. "claude,cursor").
|
|
261
|
+
|
|
262
|
+
Then check if agent configs exist:
|
|
546
263
|
\`\`\`bash
|
|
547
264
|
echo "CLAUDE_MD=$([ -f CLAUDE.md ] && echo exists || echo missing)"
|
|
548
265
|
echo "CURSOR_RULES=$([ -d .cursor/rules ] && ls .cursor/rules/*.mdc 2>/dev/null | wc -l | tr -d ' ' || echo 0)"
|
|
549
266
|
echo "AGENTS_MD=$([ -f AGENTS.md ] && echo exists || echo missing)"
|
|
267
|
+
echo "COPILOT=$([ -f .github/copilot-instructions.md ] && echo exists || echo missing)"
|
|
550
268
|
\`\`\`
|
|
551
269
|
|
|
552
|
-
- If configs exist \u2192 Tell the user which configs are present. Move to Step 4.
|
|
270
|
+
- If configs exist for the detected agents \u2192 Tell the user which configs are present. Move to Step 4.
|
|
553
271
|
- If configs are missing \u2192 Tell the user: "No agent configs found. I'll generate them now."
|
|
272
|
+
Use the detected or user-selected agent list:
|
|
554
273
|
\`\`\`bash
|
|
555
|
-
|
|
274
|
+
$CALIBER init --auto-approve --agent <comma-separated-agents>
|
|
556
275
|
\`\`\`
|
|
557
|
-
|
|
276
|
+
For example: \`$CALIBER init --auto-approve --agent claude,cursor\`
|
|
277
|
+
This generates CLAUDE.md, Cursor rules, AGENTS.md, skills, and sync infrastructure for the specified agents.
|
|
558
278
|
|
|
559
279
|
### Step 4: Check if configs are fresh
|
|
560
280
|
|
|
561
281
|
\`\`\`bash
|
|
562
|
-
|
|
282
|
+
$CALIBER score --json --quiet 2>/dev/null | head -1
|
|
563
283
|
\`\`\`
|
|
564
284
|
|
|
565
285
|
- If score is 80+ \u2192 Tell the user: "Your configs are in good shape (score: X/100)."
|
|
566
286
|
- If score is below 80 \u2192 Tell the user: "Your configs could be improved (score: X/100). Want me to run a refresh?"
|
|
567
287
|
If yes:
|
|
568
288
|
\`\`\`bash
|
|
569
|
-
|
|
289
|
+
$CALIBER refresh
|
|
570
290
|
\`\`\`
|
|
571
291
|
|
|
572
292
|
### Step 5: Ask about team setup
|
|
@@ -577,20 +297,20 @@ Ask the user: "Are you setting up for yourself only, or for your team too?"
|
|
|
577
297
|
|
|
578
298
|
Check if session learning is enabled:
|
|
579
299
|
\`\`\`bash
|
|
580
|
-
|
|
300
|
+
$CALIBER learn status 2>/dev/null | head -3
|
|
581
301
|
\`\`\`
|
|
582
302
|
- If learning is already enabled \u2192 note it in the summary.
|
|
583
303
|
- If not enabled \u2192 ask the user: "Caliber can learn from your coding sessions \u2014 when you correct a mistake or fix a pattern, it remembers for next time. Enable session learning?"
|
|
584
304
|
If yes:
|
|
585
305
|
\`\`\`bash
|
|
586
|
-
|
|
306
|
+
$CALIBER learn install
|
|
587
307
|
\`\`\`
|
|
588
308
|
|
|
589
309
|
Then tell the user:
|
|
590
310
|
"You're all set! Here's what happens next:
|
|
591
311
|
- Every time you commit, Caliber syncs your agent configs automatically
|
|
592
312
|
- Your CLAUDE.md, Cursor rules, and AGENTS.md stay current with your code
|
|
593
|
-
- Run
|
|
313
|
+
- Run \`$CALIBER skills\` anytime to discover community skills for your stack"
|
|
594
314
|
|
|
595
315
|
Then show the summary (see below) and stop.
|
|
596
316
|
|
|
@@ -625,7 +345,7 @@ Ask the user: "Are you setting up for yourself only, or for your team too?"
|
|
|
625
345
|
\`\`\`
|
|
626
346
|
Now determine which LLM provider the team uses. Check the local Caliber config:
|
|
627
347
|
\`\`\`bash
|
|
628
|
-
|
|
348
|
+
$CALIBER config --show 2>/dev/null || echo "NO_CONFIG"
|
|
629
349
|
\`\`\`
|
|
630
350
|
|
|
631
351
|
Based on the provider, the GitHub Action needs the corresponding secret:
|
|
@@ -691,11 +411,11 @@ From now on, every commit keeps all your agent configs in sync automatically.
|
|
|
691
411
|
function ensureBuiltinSkills() {
|
|
692
412
|
const written = [];
|
|
693
413
|
for (const { platformDir, skillsDir } of PLATFORM_CONFIGS) {
|
|
694
|
-
if (!
|
|
414
|
+
if (!fs3.existsSync(platformDir)) continue;
|
|
695
415
|
for (const skill of BUILTIN_SKILLS) {
|
|
696
|
-
const skillPath =
|
|
697
|
-
|
|
698
|
-
|
|
416
|
+
const skillPath = path3.join(skillsDir, skill.name, "SKILL.md");
|
|
417
|
+
fs3.mkdirSync(path3.dirname(skillPath), { recursive: true });
|
|
418
|
+
fs3.writeFileSync(skillPath, buildSkillContent(skill));
|
|
699
419
|
written.push(skillPath);
|
|
700
420
|
}
|
|
701
421
|
}
|
|
@@ -713,27 +433,394 @@ var init_builtin_skills = __esm({
|
|
|
713
433
|
return getFindSkillsContent();
|
|
714
434
|
}
|
|
715
435
|
};
|
|
716
|
-
SAVE_LEARNING_SKILL = {
|
|
717
|
-
name: "save-learning",
|
|
718
|
-
description: "Saves user instructions as persistent learnings for future sessions. Use when the user says 'remember this', 'always do X', 'from now on', 'never do Y', or gives any instruction they want persisted across sessions. Proactively suggest when the user states a preference, convention, or rule they clearly want followed in the future.",
|
|
719
|
-
get content() {
|
|
720
|
-
return getSaveLearningContent();
|
|
721
|
-
}
|
|
436
|
+
SAVE_LEARNING_SKILL = {
|
|
437
|
+
name: "save-learning",
|
|
438
|
+
description: "Saves user instructions as persistent learnings for future sessions. Use when the user says 'remember this', 'always do X', 'from now on', 'never do Y', or gives any instruction they want persisted across sessions. Proactively suggest when the user states a preference, convention, or rule they clearly want followed in the future.",
|
|
439
|
+
get content() {
|
|
440
|
+
return getSaveLearningContent();
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
SETUP_CALIBER_SKILL = {
|
|
444
|
+
name: "setup-caliber",
|
|
445
|
+
description: "Sets up Caliber for automatic AI agent context sync. Installs pre-commit hooks so CLAUDE.md, Cursor rules, and Copilot instructions update automatically on every commit. Use when Caliber hooks are not yet installed or when the user asks about keeping agent configs in sync.",
|
|
446
|
+
get content() {
|
|
447
|
+
return getSetupCaliberContent();
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
BUILTIN_SKILLS = [FIND_SKILLS_SKILL, SAVE_LEARNING_SKILL, SETUP_CALIBER_SKILL];
|
|
451
|
+
PLATFORM_CONFIGS = [
|
|
452
|
+
{ platformDir: ".claude", skillsDir: path3.join(".claude", "skills") },
|
|
453
|
+
{ platformDir: ".cursor", skillsDir: path3.join(".cursor", "skills") },
|
|
454
|
+
{ platformDir: ".agents", skillsDir: path3.join(".agents", "skills") }
|
|
455
|
+
];
|
|
456
|
+
BUILTIN_SKILL_NAMES = new Set(BUILTIN_SKILLS.map((s) => s.name));
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// src/writers/pre-commit-block.ts
|
|
461
|
+
var pre_commit_block_exports = {};
|
|
462
|
+
__export(pre_commit_block_exports, {
|
|
463
|
+
appendLearningsBlock: () => appendLearningsBlock,
|
|
464
|
+
appendManagedBlocks: () => appendManagedBlocks,
|
|
465
|
+
appendPreCommitBlock: () => appendPreCommitBlock,
|
|
466
|
+
appendSyncBlock: () => appendSyncBlock,
|
|
467
|
+
getCursorLearningsRule: () => getCursorLearningsRule,
|
|
468
|
+
getCursorPreCommitRule: () => getCursorPreCommitRule,
|
|
469
|
+
getCursorSetupRule: () => getCursorSetupRule,
|
|
470
|
+
getCursorSyncRule: () => getCursorSyncRule,
|
|
471
|
+
hasLearningsBlock: () => hasLearningsBlock,
|
|
472
|
+
hasPreCommitBlock: () => hasPreCommitBlock,
|
|
473
|
+
hasSyncBlock: () => hasSyncBlock,
|
|
474
|
+
stripManagedBlocks: () => stripManagedBlocks
|
|
475
|
+
});
|
|
476
|
+
function getCaliberNotFoundFallback(platform, bin) {
|
|
477
|
+
switch (platform) {
|
|
478
|
+
case "claude":
|
|
479
|
+
return `If \`${bin}\` is not found, tell the user: "This project uses Caliber for agent config sync. Run /setup-caliber to get set up."`;
|
|
480
|
+
case "codex":
|
|
481
|
+
return `If \`${bin}\` is not found, read \`.agents/skills/setup-caliber/SKILL.md\` and follow its instructions to install Caliber.`;
|
|
482
|
+
case "copilot":
|
|
483
|
+
return `If \`${bin}\` is not found, tell the developer to set up Caliber by running \`/setup-caliber\` in Claude Code or Cursor. Alternatively, they can run these commands in their terminal:
|
|
484
|
+
\`\`\`
|
|
485
|
+
npx @rely-ai/caliber hooks --install
|
|
486
|
+
npx @rely-ai/caliber refresh
|
|
487
|
+
\`\`\``;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
function getPreCommitBlock(platform = "claude") {
|
|
491
|
+
const bin = resolveCaliber();
|
|
492
|
+
return `${BLOCK_START}
|
|
493
|
+
## Before Committing
|
|
494
|
+
|
|
495
|
+
**IMPORTANT:** Before every git commit, you MUST ensure Caliber syncs agent configs with the latest code changes.
|
|
496
|
+
|
|
497
|
+
First, check if the pre-commit hook is already installed:
|
|
498
|
+
\`\`\`bash
|
|
499
|
+
grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "hook-active" || echo "no-hook"
|
|
500
|
+
\`\`\`
|
|
501
|
+
|
|
502
|
+
- If **hook-active**: the hook handles sync automatically \u2014 just commit normally. Tell the user: "Caliber will sync your agent configs automatically via the pre-commit hook."
|
|
503
|
+
- If **no-hook**: run Caliber manually before committing:
|
|
504
|
+
1. Tell the user: "Caliber: Syncing agent configs with your latest changes..."
|
|
505
|
+
2. Run: \`${bin} refresh && git add ${MANAGED_DOC_PATHS} 2>/dev/null\`
|
|
506
|
+
3. After it completes, briefly tell the user what Caliber updated. Then proceed with the commit.
|
|
507
|
+
|
|
508
|
+
${getCaliberNotFoundFallback(platform, bin)}
|
|
509
|
+
${BLOCK_END}`;
|
|
510
|
+
}
|
|
511
|
+
function getCursorRuleContent() {
|
|
512
|
+
const bin = resolveCaliber();
|
|
513
|
+
return `---
|
|
514
|
+
description: Run Caliber sync before git commits to keep agent configs up to date
|
|
515
|
+
alwaysApply: true
|
|
516
|
+
---
|
|
517
|
+
Before every git commit, check if the Caliber pre-commit hook is installed:
|
|
518
|
+
Run: \`grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "hook-active" || echo "no-hook"\`
|
|
519
|
+
|
|
520
|
+
If hook-active: just commit normally \u2014 Caliber syncs automatically via the hook. Tell the user: "Caliber will sync your agent configs automatically."
|
|
521
|
+
If no-hook: run \`${bin} refresh && git add ${MANAGED_DOC_PATHS} 2>/dev/null\` before committing, and tell the user what was updated.
|
|
522
|
+
|
|
523
|
+
If \`${bin}\` is not found, read the setup-caliber skill from .cursor/skills/setup-caliber/SKILL.md and follow its instructions to install Caliber.
|
|
524
|
+
`;
|
|
525
|
+
}
|
|
526
|
+
function hasPreCommitBlock(content) {
|
|
527
|
+
return content.includes(BLOCK_START);
|
|
528
|
+
}
|
|
529
|
+
function appendPreCommitBlock(content, platform = "claude") {
|
|
530
|
+
if (hasPreCommitBlock(content)) return content;
|
|
531
|
+
const trimmed = content.trimEnd();
|
|
532
|
+
return trimmed + "\n\n" + getPreCommitBlock(platform) + "\n";
|
|
533
|
+
}
|
|
534
|
+
function getCursorPreCommitRule() {
|
|
535
|
+
return { filename: CURSOR_RULE_FILENAME, content: getCursorRuleContent() };
|
|
536
|
+
}
|
|
537
|
+
function hasLearningsBlock(content) {
|
|
538
|
+
return content.includes(LEARNINGS_BLOCK_START);
|
|
539
|
+
}
|
|
540
|
+
function appendLearningsBlock(content) {
|
|
541
|
+
if (hasLearningsBlock(content)) return content;
|
|
542
|
+
const trimmed = content.trimEnd();
|
|
543
|
+
return trimmed + "\n\n" + LEARNINGS_BLOCK + "\n";
|
|
544
|
+
}
|
|
545
|
+
function getCursorLearningsRule() {
|
|
546
|
+
return { filename: CURSOR_LEARNINGS_FILENAME, content: CURSOR_LEARNINGS_CONTENT };
|
|
547
|
+
}
|
|
548
|
+
function getSyncSetupInstruction(platform, bin) {
|
|
549
|
+
switch (platform) {
|
|
550
|
+
case "claude":
|
|
551
|
+
return `If the pre-commit hook is not set up, run \`/setup-caliber\` to configure everything automatically.`;
|
|
552
|
+
case "codex":
|
|
553
|
+
return `If the pre-commit hook is not set up, read \`.agents/skills/setup-caliber/SKILL.md\` and follow the setup instructions.`;
|
|
554
|
+
case "copilot":
|
|
555
|
+
return `If the pre-commit hook is not set up, the developer should run \`/setup-caliber\` in Claude Code or Cursor for automated setup. Alternatively, run in terminal:
|
|
556
|
+
\`\`\`bash
|
|
557
|
+
npx @rely-ai/caliber hooks --install
|
|
558
|
+
npx @rely-ai/caliber refresh
|
|
559
|
+
git add ${MANAGED_DOC_PATHS} 2>/dev/null
|
|
560
|
+
\`\`\``;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
function getSyncBlock(platform = "claude") {
|
|
564
|
+
const bin = resolveCaliber();
|
|
565
|
+
return `${SYNC_BLOCK_START}
|
|
566
|
+
## Context Sync
|
|
567
|
+
|
|
568
|
+
This project uses [Caliber](https://github.com/caliber-ai-org/ai-setup) to keep AI agent configs in sync across Claude Code, Cursor, Copilot, and Codex.
|
|
569
|
+
Configs update automatically before each commit via \`${bin} refresh\`.
|
|
570
|
+
${getSyncSetupInstruction(platform, bin)}
|
|
571
|
+
${SYNC_BLOCK_END}`;
|
|
572
|
+
}
|
|
573
|
+
function hasSyncBlock(content) {
|
|
574
|
+
return content.includes(SYNC_BLOCK_START);
|
|
575
|
+
}
|
|
576
|
+
function appendSyncBlock(content, platform = "claude") {
|
|
577
|
+
if (hasSyncBlock(content)) return content;
|
|
578
|
+
const trimmed = content.trimEnd();
|
|
579
|
+
return trimmed + "\n\n" + getSyncBlock(platform) + "\n";
|
|
580
|
+
}
|
|
581
|
+
function appendManagedBlocks(content, platform = "claude") {
|
|
582
|
+
return appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(content, platform)), platform);
|
|
583
|
+
}
|
|
584
|
+
function getCursorSyncContent() {
|
|
585
|
+
const bin = resolveCaliber();
|
|
586
|
+
return `---
|
|
587
|
+
description: This project uses Caliber for automatic AI agent context sync
|
|
588
|
+
alwaysApply: true
|
|
589
|
+
---
|
|
590
|
+
This project uses Caliber to keep AI agent configs (CLAUDE.md, Cursor rules, Copilot instructions)
|
|
591
|
+
in sync with the codebase automatically. Before each commit, run \`${bin} refresh\` to sync configs.
|
|
592
|
+
If \`${bin}\` is not found, read the setup-caliber skill from .cursor/skills/setup-caliber/SKILL.md and follow its instructions to install Caliber.
|
|
593
|
+
`;
|
|
594
|
+
}
|
|
595
|
+
function getCursorSyncRule() {
|
|
596
|
+
return { filename: CURSOR_SYNC_FILENAME, content: getCursorSyncContent() };
|
|
597
|
+
}
|
|
598
|
+
function getCursorSetupContent() {
|
|
599
|
+
return `---
|
|
600
|
+
description: Check if Caliber agent config sync is set up and offer installation on new sessions
|
|
601
|
+
alwaysApply: true
|
|
602
|
+
---
|
|
603
|
+
This project uses Caliber to keep AI agent configs in sync automatically.
|
|
604
|
+
At the start of each session, silently check if sync is active:
|
|
605
|
+
|
|
606
|
+
Run: \`grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "SYNC_ACTIVE" || echo "NO_SYNC"\`
|
|
607
|
+
|
|
608
|
+
If SYNC_ACTIVE: No action needed \u2014 Caliber syncs automatically on every commit.
|
|
609
|
+
If NO_SYNC: Tell the user: "This project uses Caliber for automatic agent config sync, but it's not set up on your machine yet. Want me to set it up? It takes about 30 seconds."
|
|
610
|
+
|
|
611
|
+
If the user agrees, read \`.cursor/skills/setup-caliber/SKILL.md\` and follow its instructions step by step.
|
|
612
|
+
`;
|
|
613
|
+
}
|
|
614
|
+
function getCursorSetupRule() {
|
|
615
|
+
return { filename: CURSOR_SETUP_FILENAME, content: getCursorSetupContent() };
|
|
616
|
+
}
|
|
617
|
+
function stripManagedBlocks(content) {
|
|
618
|
+
let result = content;
|
|
619
|
+
for (const [start, end] of MANAGED_BLOCK_PAIRS) {
|
|
620
|
+
const regex = new RegExp(`\\n?${start.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[\\s\\S]*?${end.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\n?`, "g");
|
|
621
|
+
result = result.replace(regex, "\n");
|
|
622
|
+
}
|
|
623
|
+
return result.replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
624
|
+
}
|
|
625
|
+
var BLOCK_START, BLOCK_END, MANAGED_DOC_PATHS, CURSOR_RULE_FILENAME, LEARNINGS_BLOCK_START, LEARNINGS_BLOCK_END, LEARNINGS_BLOCK, CURSOR_LEARNINGS_FILENAME, CURSOR_LEARNINGS_CONTENT, SYNC_BLOCK_START, SYNC_BLOCK_END, CURSOR_SYNC_FILENAME, CURSOR_SETUP_FILENAME, MANAGED_BLOCK_PAIRS;
|
|
626
|
+
var init_pre_commit_block = __esm({
|
|
627
|
+
"src/writers/pre-commit-block.ts"() {
|
|
628
|
+
"use strict";
|
|
629
|
+
init_resolve_caliber();
|
|
630
|
+
BLOCK_START = "<!-- caliber:managed:pre-commit -->";
|
|
631
|
+
BLOCK_END = "<!-- /caliber:managed:pre-commit -->";
|
|
632
|
+
MANAGED_DOC_PATHS = "CLAUDE.md .claude/ .cursor/ .cursorrules .github/copilot-instructions.md .github/instructions/ AGENTS.md CALIBER_LEARNINGS.md";
|
|
633
|
+
CURSOR_RULE_FILENAME = "caliber-pre-commit.mdc";
|
|
634
|
+
LEARNINGS_BLOCK_START = "<!-- caliber:managed:learnings -->";
|
|
635
|
+
LEARNINGS_BLOCK_END = "<!-- /caliber:managed:learnings -->";
|
|
636
|
+
LEARNINGS_BLOCK = `${LEARNINGS_BLOCK_START}
|
|
637
|
+
## Session Learnings
|
|
638
|
+
|
|
639
|
+
Read \`CALIBER_LEARNINGS.md\` for patterns and anti-patterns learned from previous sessions.
|
|
640
|
+
These are auto-extracted from real tool usage \u2014 treat them as project-specific rules.
|
|
641
|
+
${LEARNINGS_BLOCK_END}`;
|
|
642
|
+
CURSOR_LEARNINGS_FILENAME = "caliber-learnings.mdc";
|
|
643
|
+
CURSOR_LEARNINGS_CONTENT = `---
|
|
644
|
+
description: Reference session-learned patterns from CALIBER_LEARNINGS.md
|
|
645
|
+
alwaysApply: true
|
|
646
|
+
---
|
|
647
|
+
Read \`CALIBER_LEARNINGS.md\` for patterns and anti-patterns learned from previous sessions.
|
|
648
|
+
These are auto-extracted from real tool usage \u2014 treat them as project-specific rules.
|
|
649
|
+
`;
|
|
650
|
+
SYNC_BLOCK_START = "<!-- caliber:managed:sync -->";
|
|
651
|
+
SYNC_BLOCK_END = "<!-- /caliber:managed:sync -->";
|
|
652
|
+
CURSOR_SYNC_FILENAME = "caliber-sync.mdc";
|
|
653
|
+
CURSOR_SETUP_FILENAME = "caliber-setup.mdc";
|
|
654
|
+
MANAGED_BLOCK_PAIRS = [
|
|
655
|
+
[BLOCK_START, BLOCK_END],
|
|
656
|
+
[LEARNINGS_BLOCK_START, LEARNINGS_BLOCK_END],
|
|
657
|
+
[SYNC_BLOCK_START, SYNC_BLOCK_END]
|
|
658
|
+
];
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
// src/llm/config.ts
|
|
663
|
+
var config_exports = {};
|
|
664
|
+
__export(config_exports, {
|
|
665
|
+
DEFAULT_FAST_MODELS: () => DEFAULT_FAST_MODELS,
|
|
666
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
667
|
+
MODEL_CONTEXT_WINDOWS: () => MODEL_CONTEXT_WINDOWS,
|
|
668
|
+
getConfigFilePath: () => getConfigFilePath,
|
|
669
|
+
getDisplayModel: () => getDisplayModel,
|
|
670
|
+
getFastModel: () => getFastModel,
|
|
671
|
+
getMaxPromptTokens: () => getMaxPromptTokens,
|
|
672
|
+
loadConfig: () => loadConfig,
|
|
673
|
+
readConfigFile: () => readConfigFile,
|
|
674
|
+
resolveFromEnv: () => resolveFromEnv,
|
|
675
|
+
writeConfigFile: () => writeConfigFile
|
|
676
|
+
});
|
|
677
|
+
import fs6 from "fs";
|
|
678
|
+
import path6 from "path";
|
|
679
|
+
import os2 from "os";
|
|
680
|
+
function getMaxPromptTokens() {
|
|
681
|
+
const config = loadConfig();
|
|
682
|
+
const model = process.env.CALIBER_MODEL || config?.model;
|
|
683
|
+
const contextWindow = model ? MODEL_CONTEXT_WINDOWS[model] ?? DEFAULT_CONTEXT_WINDOW : DEFAULT_CONTEXT_WINDOW;
|
|
684
|
+
const budget = Math.floor(contextWindow * INPUT_BUDGET_FRACTION);
|
|
685
|
+
return Math.max(MIN_PROMPT_TOKENS, Math.min(budget, MAX_PROMPT_TOKENS_CAP));
|
|
686
|
+
}
|
|
687
|
+
function loadConfig() {
|
|
688
|
+
const envConfig = resolveFromEnv();
|
|
689
|
+
if (envConfig) return envConfig;
|
|
690
|
+
return readConfigFile();
|
|
691
|
+
}
|
|
692
|
+
function resolveFromEnv() {
|
|
693
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
694
|
+
return {
|
|
695
|
+
provider: "anthropic",
|
|
696
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
697
|
+
model: process.env.CALIBER_MODEL || DEFAULT_MODELS.anthropic
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
if (process.env.VERTEX_PROJECT_ID || process.env.GCP_PROJECT_ID) {
|
|
701
|
+
return {
|
|
702
|
+
provider: "vertex",
|
|
703
|
+
model: process.env.CALIBER_MODEL || DEFAULT_MODELS.vertex,
|
|
704
|
+
vertexProjectId: process.env.VERTEX_PROJECT_ID || process.env.GCP_PROJECT_ID,
|
|
705
|
+
vertexRegion: process.env.VERTEX_REGION || process.env.GCP_REGION || "us-east5",
|
|
706
|
+
vertexCredentials: process.env.VERTEX_SA_CREDENTIALS || process.env.GOOGLE_APPLICATION_CREDENTIALS
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
if (process.env.OPENAI_API_KEY) {
|
|
710
|
+
return {
|
|
711
|
+
provider: "openai",
|
|
712
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
713
|
+
model: process.env.CALIBER_MODEL || DEFAULT_MODELS.openai,
|
|
714
|
+
baseUrl: process.env.OPENAI_BASE_URL
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
if (process.env.CALIBER_USE_CURSOR_SEAT === "1" || process.env.CALIBER_USE_CURSOR_SEAT === "true") {
|
|
718
|
+
return {
|
|
719
|
+
provider: "cursor",
|
|
720
|
+
model: process.env.CALIBER_MODEL || DEFAULT_MODELS.cursor
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
if (process.env.CALIBER_USE_CLAUDE_CLI === "1" || process.env.CALIBER_USE_CLAUDE_CLI === "true") {
|
|
724
|
+
return {
|
|
725
|
+
provider: "claude-cli",
|
|
726
|
+
model: process.env.CALIBER_MODEL || DEFAULT_MODELS["claude-cli"]
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
return null;
|
|
730
|
+
}
|
|
731
|
+
function readConfigFile() {
|
|
732
|
+
try {
|
|
733
|
+
if (!fs6.existsSync(CONFIG_FILE)) return null;
|
|
734
|
+
const raw = fs6.readFileSync(CONFIG_FILE, "utf-8");
|
|
735
|
+
const parsed = JSON.parse(raw);
|
|
736
|
+
if (!parsed.provider || !["anthropic", "vertex", "openai", "cursor", "claude-cli"].includes(parsed.provider)) {
|
|
737
|
+
return null;
|
|
738
|
+
}
|
|
739
|
+
return parsed;
|
|
740
|
+
} catch {
|
|
741
|
+
return null;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
function writeConfigFile(config) {
|
|
745
|
+
if (!fs6.existsSync(CONFIG_DIR)) {
|
|
746
|
+
fs6.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
747
|
+
}
|
|
748
|
+
const sanitized = { ...config };
|
|
749
|
+
if (sanitized.apiKey) {
|
|
750
|
+
sanitized.apiKey = sanitized.apiKey.trim();
|
|
751
|
+
}
|
|
752
|
+
fs6.writeFileSync(CONFIG_FILE, JSON.stringify(sanitized, null, 2) + "\n", { mode: 384 });
|
|
753
|
+
}
|
|
754
|
+
function getConfigFilePath() {
|
|
755
|
+
return CONFIG_FILE;
|
|
756
|
+
}
|
|
757
|
+
function getDisplayModel(config) {
|
|
758
|
+
if (config.model === "default" && config.provider === "claude-cli") {
|
|
759
|
+
return process.env.ANTHROPIC_MODEL || "default (inherited from Claude Code)";
|
|
760
|
+
}
|
|
761
|
+
return config.model;
|
|
762
|
+
}
|
|
763
|
+
function getFastModel() {
|
|
764
|
+
if (process.env.CALIBER_FAST_MODEL) return process.env.CALIBER_FAST_MODEL;
|
|
765
|
+
const config = loadConfig();
|
|
766
|
+
const provider = config?.provider;
|
|
767
|
+
if (process.env.ANTHROPIC_SMALL_FAST_MODEL && (!provider || provider === "anthropic" || provider === "vertex" || provider === "claude-cli")) {
|
|
768
|
+
return process.env.ANTHROPIC_SMALL_FAST_MODEL;
|
|
769
|
+
}
|
|
770
|
+
if (config?.fastModel) return config.fastModel;
|
|
771
|
+
if (provider) return DEFAULT_FAST_MODELS[provider];
|
|
772
|
+
return void 0;
|
|
773
|
+
}
|
|
774
|
+
var CONFIG_DIR, CONFIG_FILE, DEFAULT_MODELS, MODEL_CONTEXT_WINDOWS, DEFAULT_CONTEXT_WINDOW, INPUT_BUDGET_FRACTION, MAX_PROMPT_TOKENS_CAP, MIN_PROMPT_TOKENS, DEFAULT_FAST_MODELS;
|
|
775
|
+
var init_config = __esm({
|
|
776
|
+
"src/llm/config.ts"() {
|
|
777
|
+
"use strict";
|
|
778
|
+
CONFIG_DIR = path6.join(os2.homedir(), ".caliber");
|
|
779
|
+
CONFIG_FILE = path6.join(CONFIG_DIR, "config.json");
|
|
780
|
+
DEFAULT_MODELS = {
|
|
781
|
+
anthropic: "claude-sonnet-4-6",
|
|
782
|
+
vertex: "claude-sonnet-4-6",
|
|
783
|
+
openai: "gpt-4.1",
|
|
784
|
+
cursor: "sonnet-4.6",
|
|
785
|
+
"claude-cli": "default"
|
|
786
|
+
};
|
|
787
|
+
MODEL_CONTEXT_WINDOWS = {
|
|
788
|
+
"claude-sonnet-4-6": 2e5,
|
|
789
|
+
"claude-opus-4-6": 2e5,
|
|
790
|
+
"claude-haiku-4-5-20251001": 2e5,
|
|
791
|
+
"claude-sonnet-4-5-20250514": 2e5,
|
|
792
|
+
"gpt-4.1": 1e6,
|
|
793
|
+
"gpt-4.1-mini": 1e6,
|
|
794
|
+
"gpt-4o": 128e3,
|
|
795
|
+
"gpt-4o-mini": 128e3,
|
|
796
|
+
"sonnet-4.6": 2e5
|
|
722
797
|
};
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
798
|
+
DEFAULT_CONTEXT_WINDOW = 2e5;
|
|
799
|
+
INPUT_BUDGET_FRACTION = 0.6;
|
|
800
|
+
MAX_PROMPT_TOKENS_CAP = 3e5;
|
|
801
|
+
MIN_PROMPT_TOKENS = 3e4;
|
|
802
|
+
DEFAULT_FAST_MODELS = {
|
|
803
|
+
anthropic: "claude-haiku-4-5-20251001",
|
|
804
|
+
vertex: "claude-haiku-4-5-20251001",
|
|
805
|
+
openai: "gpt-4.1-mini",
|
|
806
|
+
cursor: "gpt-5.3-codex-fast"
|
|
729
807
|
};
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
// src/llm/types.ts
|
|
812
|
+
var types_exports = {};
|
|
813
|
+
__export(types_exports, {
|
|
814
|
+
isSeatBased: () => isSeatBased
|
|
815
|
+
});
|
|
816
|
+
function isSeatBased(provider) {
|
|
817
|
+
return SEAT_BASED_PROVIDERS.has(provider);
|
|
818
|
+
}
|
|
819
|
+
var SEAT_BASED_PROVIDERS;
|
|
820
|
+
var init_types = __esm({
|
|
821
|
+
"src/llm/types.ts"() {
|
|
822
|
+
"use strict";
|
|
823
|
+
SEAT_BASED_PROVIDERS = /* @__PURE__ */ new Set(["cursor", "claude-cli"]);
|
|
737
824
|
}
|
|
738
825
|
});
|
|
739
826
|
|
|
@@ -1052,8 +1139,8 @@ var init_lock = __esm({
|
|
|
1052
1139
|
|
|
1053
1140
|
// src/cli.ts
|
|
1054
1141
|
import { Command } from "commander";
|
|
1055
|
-
import
|
|
1056
|
-
import
|
|
1142
|
+
import fs49 from "fs";
|
|
1143
|
+
import path39 from "path";
|
|
1057
1144
|
import { fileURLToPath } from "url";
|
|
1058
1145
|
|
|
1059
1146
|
// src/commands/init.ts
|
|
@@ -1062,8 +1149,8 @@ import chalk14 from "chalk";
|
|
|
1062
1149
|
import fs33 from "fs";
|
|
1063
1150
|
|
|
1064
1151
|
// src/fingerprint/index.ts
|
|
1065
|
-
import
|
|
1066
|
-
import
|
|
1152
|
+
import fs9 from "fs";
|
|
1153
|
+
import path8 from "path";
|
|
1067
1154
|
|
|
1068
1155
|
// src/fingerprint/git.ts
|
|
1069
1156
|
import { execSync } from "child_process";
|
|
@@ -1171,8 +1258,8 @@ function scan(base, rel, depth, maxDepth, result) {
|
|
|
1171
1258
|
}
|
|
1172
1259
|
|
|
1173
1260
|
// src/fingerprint/existing-config.ts
|
|
1174
|
-
import
|
|
1175
|
-
import
|
|
1261
|
+
import fs4 from "fs";
|
|
1262
|
+
import path4 from "path";
|
|
1176
1263
|
|
|
1177
1264
|
// src/constants.ts
|
|
1178
1265
|
import path2 from "path";
|
|
@@ -1207,97 +1294,121 @@ var LEARNING_LAST_ERROR_FILE = "last-error.json";
|
|
|
1207
1294
|
var MIN_SESSIONS_FOR_COMPARISON = 3;
|
|
1208
1295
|
|
|
1209
1296
|
// src/fingerprint/existing-config.ts
|
|
1297
|
+
init_builtin_skills();
|
|
1298
|
+
init_pre_commit_block();
|
|
1299
|
+
var MANAGED_CURSOR_RULES = /* @__PURE__ */ new Set([
|
|
1300
|
+
getCursorPreCommitRule().filename,
|
|
1301
|
+
getCursorLearningsRule().filename,
|
|
1302
|
+
getCursorSyncRule().filename,
|
|
1303
|
+
getCursorSetupRule().filename
|
|
1304
|
+
]);
|
|
1210
1305
|
function readExistingConfigs(dir) {
|
|
1211
1306
|
const configs = {};
|
|
1212
|
-
const readmeMdPath =
|
|
1213
|
-
if (
|
|
1214
|
-
configs.readmeMd =
|
|
1307
|
+
const readmeMdPath = path4.join(dir, "README.md");
|
|
1308
|
+
if (fs4.existsSync(readmeMdPath)) {
|
|
1309
|
+
configs.readmeMd = fs4.readFileSync(readmeMdPath, "utf-8");
|
|
1215
1310
|
}
|
|
1216
|
-
const agentsMdPath =
|
|
1217
|
-
if (
|
|
1218
|
-
configs.agentsMd =
|
|
1311
|
+
const agentsMdPath = path4.join(dir, "AGENTS.md");
|
|
1312
|
+
if (fs4.existsSync(agentsMdPath)) {
|
|
1313
|
+
configs.agentsMd = fs4.readFileSync(agentsMdPath, "utf-8");
|
|
1219
1314
|
}
|
|
1220
|
-
const claudeMdPath =
|
|
1221
|
-
if (
|
|
1222
|
-
configs.claudeMd =
|
|
1315
|
+
const claudeMdPath = path4.join(dir, "CLAUDE.md");
|
|
1316
|
+
if (fs4.existsSync(claudeMdPath)) {
|
|
1317
|
+
configs.claudeMd = fs4.readFileSync(claudeMdPath, "utf-8");
|
|
1223
1318
|
}
|
|
1224
|
-
const claudeSettingsPath =
|
|
1225
|
-
if (
|
|
1319
|
+
const claudeSettingsPath = path4.join(dir, ".claude", "settings.json");
|
|
1320
|
+
if (fs4.existsSync(claudeSettingsPath)) {
|
|
1226
1321
|
try {
|
|
1227
|
-
configs.claudeSettings = JSON.parse(
|
|
1322
|
+
configs.claudeSettings = JSON.parse(fs4.readFileSync(claudeSettingsPath, "utf-8"));
|
|
1228
1323
|
} catch {
|
|
1229
1324
|
}
|
|
1230
1325
|
}
|
|
1231
|
-
const skillsDir =
|
|
1232
|
-
if (
|
|
1326
|
+
const skillsDir = path4.join(dir, ".claude", "skills");
|
|
1327
|
+
if (fs4.existsSync(skillsDir)) {
|
|
1233
1328
|
try {
|
|
1234
|
-
const entries =
|
|
1329
|
+
const entries = fs4.readdirSync(skillsDir);
|
|
1235
1330
|
const skills = [];
|
|
1236
1331
|
for (const entry of entries) {
|
|
1237
|
-
|
|
1238
|
-
const
|
|
1239
|
-
|
|
1240
|
-
|
|
1332
|
+
if (BUILTIN_SKILL_NAMES.has(entry)) continue;
|
|
1333
|
+
const entryPath = path4.join(skillsDir, entry);
|
|
1334
|
+
const skillMdPath = path4.join(entryPath, "SKILL.md");
|
|
1335
|
+
if (fs4.statSync(entryPath).isDirectory() && fs4.existsSync(skillMdPath)) {
|
|
1336
|
+
skills.push({ filename: `${entry}/SKILL.md`, content: fs4.readFileSync(skillMdPath, "utf-8") });
|
|
1241
1337
|
} else if (entry.endsWith(".md")) {
|
|
1242
|
-
skills.push({ filename: entry, content:
|
|
1338
|
+
skills.push({ filename: entry, content: fs4.readFileSync(entryPath, "utf-8") });
|
|
1243
1339
|
}
|
|
1244
1340
|
}
|
|
1245
1341
|
if (skills.length > 0) configs.claudeSkills = skills;
|
|
1246
1342
|
} catch {
|
|
1247
1343
|
}
|
|
1248
1344
|
}
|
|
1249
|
-
const cursorrulesPath =
|
|
1250
|
-
if (
|
|
1251
|
-
configs.cursorrules =
|
|
1345
|
+
const cursorrulesPath = path4.join(dir, ".cursorrules");
|
|
1346
|
+
if (fs4.existsSync(cursorrulesPath)) {
|
|
1347
|
+
configs.cursorrules = fs4.readFileSync(cursorrulesPath, "utf-8");
|
|
1252
1348
|
}
|
|
1253
|
-
const cursorRulesDir =
|
|
1254
|
-
if (
|
|
1349
|
+
const cursorRulesDir = path4.join(dir, ".cursor", "rules");
|
|
1350
|
+
if (fs4.existsSync(cursorRulesDir)) {
|
|
1255
1351
|
try {
|
|
1256
|
-
const files =
|
|
1352
|
+
const files = fs4.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc") && !MANAGED_CURSOR_RULES.has(f));
|
|
1257
1353
|
configs.cursorRules = files.map((f) => ({
|
|
1258
1354
|
filename: f,
|
|
1259
|
-
content:
|
|
1355
|
+
content: fs4.readFileSync(path4.join(cursorRulesDir, f), "utf-8")
|
|
1260
1356
|
}));
|
|
1261
1357
|
} catch {
|
|
1262
1358
|
}
|
|
1263
1359
|
}
|
|
1264
|
-
const cursorSkillsDir =
|
|
1265
|
-
if (
|
|
1360
|
+
const cursorSkillsDir = path4.join(dir, ".cursor", "skills");
|
|
1361
|
+
if (fs4.existsSync(cursorSkillsDir)) {
|
|
1266
1362
|
try {
|
|
1267
|
-
const slugs =
|
|
1268
|
-
return
|
|
1363
|
+
const slugs = fs4.readdirSync(cursorSkillsDir).filter((f) => {
|
|
1364
|
+
return fs4.statSync(path4.join(cursorSkillsDir, f)).isDirectory() && !BUILTIN_SKILL_NAMES.has(f);
|
|
1269
1365
|
});
|
|
1270
|
-
configs.cursorSkills = slugs.filter((slug) =>
|
|
1366
|
+
configs.cursorSkills = slugs.filter((slug) => fs4.existsSync(path4.join(cursorSkillsDir, slug, "SKILL.md"))).map((name) => ({
|
|
1271
1367
|
name,
|
|
1272
1368
|
filename: "SKILL.md",
|
|
1273
|
-
content:
|
|
1369
|
+
content: fs4.readFileSync(path4.join(cursorSkillsDir, name, "SKILL.md"), "utf-8")
|
|
1274
1370
|
}));
|
|
1275
1371
|
} catch {
|
|
1276
1372
|
}
|
|
1277
1373
|
}
|
|
1278
|
-
const mcpJsonPath =
|
|
1279
|
-
if (
|
|
1374
|
+
const mcpJsonPath = path4.join(dir, ".mcp.json");
|
|
1375
|
+
if (fs4.existsSync(mcpJsonPath)) {
|
|
1280
1376
|
try {
|
|
1281
|
-
const mcpJson = JSON.parse(
|
|
1377
|
+
const mcpJson = JSON.parse(fs4.readFileSync(mcpJsonPath, "utf-8"));
|
|
1282
1378
|
if (mcpJson.mcpServers) {
|
|
1283
1379
|
configs.claudeMcpServers = mcpJson.mcpServers;
|
|
1284
1380
|
}
|
|
1285
1381
|
} catch {
|
|
1286
1382
|
}
|
|
1287
1383
|
}
|
|
1288
|
-
const cursorMcpPath =
|
|
1289
|
-
if (
|
|
1384
|
+
const cursorMcpPath = path4.join(dir, ".cursor", "mcp.json");
|
|
1385
|
+
if (fs4.existsSync(cursorMcpPath)) {
|
|
1290
1386
|
try {
|
|
1291
|
-
const cursorMcpJson = JSON.parse(
|
|
1387
|
+
const cursorMcpJson = JSON.parse(fs4.readFileSync(cursorMcpPath, "utf-8"));
|
|
1292
1388
|
if (cursorMcpJson.mcpServers) {
|
|
1293
1389
|
configs.cursorMcpServers = cursorMcpJson.mcpServers;
|
|
1294
1390
|
}
|
|
1295
1391
|
} catch {
|
|
1296
1392
|
}
|
|
1297
1393
|
}
|
|
1298
|
-
|
|
1394
|
+
const copilotPath = path4.join(dir, ".github", "copilot-instructions.md");
|
|
1395
|
+
if (fs4.existsSync(copilotPath)) {
|
|
1396
|
+
configs.copilotInstructions = fs4.readFileSync(copilotPath, "utf-8");
|
|
1397
|
+
}
|
|
1398
|
+
const instructionsDir = path4.join(dir, ".github", "instructions");
|
|
1399
|
+
if (fs4.existsSync(instructionsDir)) {
|
|
1400
|
+
try {
|
|
1401
|
+
const files = fs4.readdirSync(instructionsDir).filter((f) => f.endsWith(".md"));
|
|
1402
|
+
configs.copilotInstructionFiles = files.map((f) => ({
|
|
1403
|
+
filename: f,
|
|
1404
|
+
content: fs4.readFileSync(path4.join(instructionsDir, f), "utf-8")
|
|
1405
|
+
}));
|
|
1406
|
+
} catch {
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
if (fs4.existsSync(PERSONAL_LEARNINGS_FILE)) {
|
|
1299
1410
|
try {
|
|
1300
|
-
const content =
|
|
1411
|
+
const content = fs4.readFileSync(PERSONAL_LEARNINGS_FILE, "utf-8");
|
|
1301
1412
|
const bullets = content.split("\n").filter((l) => l.startsWith("- ")).join("\n");
|
|
1302
1413
|
if (bullets) configs.personalLearnings = bullets;
|
|
1303
1414
|
} catch {
|
|
@@ -1307,9 +1418,9 @@ function readExistingConfigs(dir) {
|
|
|
1307
1418
|
}
|
|
1308
1419
|
|
|
1309
1420
|
// src/fingerprint/code-analysis.ts
|
|
1310
|
-
import
|
|
1311
|
-
import
|
|
1312
|
-
import { execSync as
|
|
1421
|
+
import fs5 from "fs";
|
|
1422
|
+
import path5 from "path";
|
|
1423
|
+
import { execSync as execSync4 } from "child_process";
|
|
1313
1424
|
|
|
1314
1425
|
// src/lib/sanitize.ts
|
|
1315
1426
|
var KNOWN_PREFIX_PATTERNS = [
|
|
@@ -1653,18 +1764,18 @@ function extractSkeletonIndentBased(lines, ext) {
|
|
|
1653
1764
|
}
|
|
1654
1765
|
function extractImports(content, filePath) {
|
|
1655
1766
|
const imports = [];
|
|
1656
|
-
const dir =
|
|
1767
|
+
const dir = path5.dirname(filePath);
|
|
1657
1768
|
for (const line of content.split("\n")) {
|
|
1658
1769
|
const trimmed = line.trim();
|
|
1659
1770
|
const jsMatch = trimmed.match(/(?:from|require\()\s*['"]([^'"]+)['"]/);
|
|
1660
1771
|
if (jsMatch && jsMatch[1].startsWith(".")) {
|
|
1661
|
-
imports.push(
|
|
1772
|
+
imports.push(path5.normalize(path5.join(dir, jsMatch[1])));
|
|
1662
1773
|
continue;
|
|
1663
1774
|
}
|
|
1664
1775
|
const pyMatch = trimmed.match(/^from\s+(\.[.\w]*)\s+import/);
|
|
1665
1776
|
if (pyMatch) {
|
|
1666
1777
|
const modulePath = pyMatch[1].replace(/\./g, "/");
|
|
1667
|
-
imports.push(
|
|
1778
|
+
imports.push(path5.normalize(path5.join(dir, modulePath)));
|
|
1668
1779
|
continue;
|
|
1669
1780
|
}
|
|
1670
1781
|
const goMatch = trimmed.match(/^\s*"([^"]+)"/);
|
|
@@ -1677,7 +1788,7 @@ function extractImports(content, filePath) {
|
|
|
1677
1788
|
function buildImportCounts(files) {
|
|
1678
1789
|
const counts = /* @__PURE__ */ new Map();
|
|
1679
1790
|
for (const [filePath, content] of files) {
|
|
1680
|
-
const ext =
|
|
1791
|
+
const ext = path5.extname(filePath).toLowerCase();
|
|
1681
1792
|
if (!SOURCE_EXTENSIONS.has(ext)) continue;
|
|
1682
1793
|
const imports = extractImports(content, filePath);
|
|
1683
1794
|
for (const imp of imports) {
|
|
@@ -1696,7 +1807,7 @@ function buildImportCounts(files) {
|
|
|
1696
1807
|
function getGitFrequency(dir) {
|
|
1697
1808
|
const freq = /* @__PURE__ */ new Map();
|
|
1698
1809
|
try {
|
|
1699
|
-
const output =
|
|
1810
|
+
const output = execSync4(
|
|
1700
1811
|
'git log --since="6 months ago" --format="" --name-only --diff-filter=ACMR 2>/dev/null | head -10000',
|
|
1701
1812
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 5e3 }
|
|
1702
1813
|
);
|
|
@@ -1711,7 +1822,7 @@ function getGitFrequency(dir) {
|
|
|
1711
1822
|
function groupByDirectory(files) {
|
|
1712
1823
|
const groups = /* @__PURE__ */ new Map();
|
|
1713
1824
|
for (const f of files) {
|
|
1714
|
-
const dir =
|
|
1825
|
+
const dir = path5.dirname(f.path);
|
|
1715
1826
|
const group = groups.get(dir) || [];
|
|
1716
1827
|
group.push(f);
|
|
1717
1828
|
groups.set(dir, group);
|
|
@@ -1732,14 +1843,14 @@ function analyzeCode(dir) {
|
|
|
1732
1843
|
let totalChars = 0;
|
|
1733
1844
|
for (const relPath of allPaths) {
|
|
1734
1845
|
try {
|
|
1735
|
-
totalChars +=
|
|
1846
|
+
totalChars += fs5.statSync(path5.join(dir, relPath)).size;
|
|
1736
1847
|
} catch {
|
|
1737
1848
|
}
|
|
1738
1849
|
}
|
|
1739
1850
|
const fileContents = /* @__PURE__ */ new Map();
|
|
1740
1851
|
for (const relPath of allPaths) {
|
|
1741
1852
|
try {
|
|
1742
|
-
const content =
|
|
1853
|
+
const content = fs5.readFileSync(path5.join(dir, relPath), "utf-8");
|
|
1743
1854
|
if (content.split("\n").length <= 500) fileContents.set(relPath, content);
|
|
1744
1855
|
} catch {
|
|
1745
1856
|
}
|
|
@@ -1749,7 +1860,7 @@ function analyzeCode(dir) {
|
|
|
1749
1860
|
const scored = [];
|
|
1750
1861
|
let compressedChars = 0;
|
|
1751
1862
|
for (const [relPath, rawContent] of fileContents) {
|
|
1752
|
-
const ext =
|
|
1863
|
+
const ext = path5.extname(relPath).toLowerCase();
|
|
1753
1864
|
const compressed = compressContent(rawContent, ext);
|
|
1754
1865
|
const skeleton = extractSkeleton(compressed, ext);
|
|
1755
1866
|
compressedChars += compressed.length;
|
|
@@ -1778,7 +1889,7 @@ function analyzeCode(dir) {
|
|
|
1778
1889
|
}
|
|
1779
1890
|
if (similar.length > 0) {
|
|
1780
1891
|
dupGroups++;
|
|
1781
|
-
const names = similar.map((f) =>
|
|
1892
|
+
const names = similar.map((f) => path5.basename(f.path));
|
|
1782
1893
|
const summary = `(${similar.length} similar file${similar.length === 1 ? "" : "s"} in ${dirPath}/: ${names.join(", ")})`;
|
|
1783
1894
|
const summarySize = summary.length + 30;
|
|
1784
1895
|
if (includedChars + summarySize <= CHAR_BUDGET) {
|
|
@@ -1815,10 +1926,10 @@ function analyzeCode(dir) {
|
|
|
1815
1926
|
}
|
|
1816
1927
|
function walkDir(base, rel, depth, maxDepth, files) {
|
|
1817
1928
|
if (depth > maxDepth) return;
|
|
1818
|
-
const fullPath =
|
|
1929
|
+
const fullPath = path5.join(base, rel);
|
|
1819
1930
|
let entries;
|
|
1820
1931
|
try {
|
|
1821
|
-
entries =
|
|
1932
|
+
entries = fs5.readdirSync(fullPath, { withFileTypes: true });
|
|
1822
1933
|
} catch {
|
|
1823
1934
|
return;
|
|
1824
1935
|
}
|
|
@@ -1831,7 +1942,7 @@ function walkDir(base, rel, depth, maxDepth, files) {
|
|
|
1831
1942
|
} else if (entry.isFile()) {
|
|
1832
1943
|
if (SKIP_FILES.has(entry.name)) continue;
|
|
1833
1944
|
if (SKIP_PATTERNS.some((p) => p.test(entry.name))) continue;
|
|
1834
|
-
const ext =
|
|
1945
|
+
const ext = path5.extname(entry.name).toLowerCase();
|
|
1835
1946
|
if (TEXT_EXTENSIONS.has(ext) || depth === 0 && !ext && !entry.name.startsWith(".")) {
|
|
1836
1947
|
files.push(relPath);
|
|
1837
1948
|
}
|
|
@@ -1839,7 +1950,7 @@ function walkDir(base, rel, depth, maxDepth, files) {
|
|
|
1839
1950
|
}
|
|
1840
1951
|
}
|
|
1841
1952
|
function filePriority(filePath) {
|
|
1842
|
-
const base =
|
|
1953
|
+
const base = path5.basename(filePath);
|
|
1843
1954
|
const entryPoints = /* @__PURE__ */ new Set([
|
|
1844
1955
|
"index.ts",
|
|
1845
1956
|
"index.js",
|
|
@@ -1971,7 +2082,7 @@ var AnthropicProvider = class {
|
|
|
1971
2082
|
};
|
|
1972
2083
|
|
|
1973
2084
|
// src/llm/vertex.ts
|
|
1974
|
-
import
|
|
2085
|
+
import fs7 from "fs";
|
|
1975
2086
|
import { AnthropicVertex } from "@anthropic-ai/vertex-sdk";
|
|
1976
2087
|
import { GoogleAuth } from "google-auth-library";
|
|
1977
2088
|
var VertexProvider = class {
|
|
@@ -1994,7 +2105,7 @@ var VertexProvider = class {
|
|
|
1994
2105
|
}
|
|
1995
2106
|
} else {
|
|
1996
2107
|
try {
|
|
1997
|
-
creds = JSON.parse(
|
|
2108
|
+
creds = JSON.parse(fs7.readFileSync(raw, "utf-8"));
|
|
1998
2109
|
} catch {
|
|
1999
2110
|
throw new Error(`Cannot read credentials file: ${raw}`);
|
|
2000
2111
|
}
|
|
@@ -3163,6 +3274,11 @@ CONSERVATIVE UPDATE means:
|
|
|
3163
3274
|
- NEVER remove code blocks, backtick references, or architecture paths unless the diff deleted them
|
|
3164
3275
|
- NEVER replace specific paths/commands with generic prose
|
|
3165
3276
|
|
|
3277
|
+
Cross-agent sync:
|
|
3278
|
+
- When a change affects CLAUDE.md, apply the same semantic update to AGENTS.md and copilot instructions if they exist
|
|
3279
|
+
- Each file uses its own format and conventions \u2014 do NOT copy content verbatim between them
|
|
3280
|
+
- The goal is consistency: all agent config files should reflect the same project state after refresh
|
|
3281
|
+
|
|
3166
3282
|
Quality constraints (the output is scored deterministically):
|
|
3167
3283
|
- CLAUDE.md / AGENTS.md: MUST stay under 400 lines. If the diff adds content, trim the least important lines elsewhere.
|
|
3168
3284
|
- Keep 3+ code blocks with executable commands \u2014 do not remove code blocks
|
|
@@ -3171,16 +3287,17 @@ Quality constraints (the output is scored deterministically):
|
|
|
3171
3287
|
- Preserve the existing structure (headings, bullet style, formatting)
|
|
3172
3288
|
|
|
3173
3289
|
Managed content:
|
|
3174
|
-
-
|
|
3175
|
-
-
|
|
3290
|
+
- All blocks between <!-- caliber:managed:* --> and <!-- /caliber:managed:* --> are managed by Caliber \u2014 copy them verbatim, do NOT modify their contents
|
|
3291
|
+
- This includes: pre-commit, sync, and learnings blocks
|
|
3176
3292
|
- Do NOT modify CALIBER_LEARNINGS.md \u2014 it is managed separately
|
|
3177
|
-
- Preserve any references to CALIBER_LEARNINGS.md
|
|
3293
|
+
- Preserve any references to CALIBER_LEARNINGS.md
|
|
3178
3294
|
|
|
3179
3295
|
Return a JSON object with this exact shape:
|
|
3180
3296
|
{
|
|
3181
3297
|
"updatedDocs": {
|
|
3182
3298
|
"claudeMd": "<updated content or null>",
|
|
3183
3299
|
"readmeMd": "<updated content or null>",
|
|
3300
|
+
"agentsMd": "<updated content or null>",
|
|
3184
3301
|
"cursorrules": "<updated content or null>",
|
|
3185
3302
|
"cursorRules": [{"filename": "name.mdc", "content": "..."}] or null,
|
|
3186
3303
|
"claudeSkills": [{"filename": "name.md", "content": "..."}] or null,
|
|
@@ -3342,15 +3459,15 @@ async function detectProjectStack(fileTree, suffixCounts) {
|
|
|
3342
3459
|
init_config();
|
|
3343
3460
|
|
|
3344
3461
|
// src/fingerprint/cache.ts
|
|
3345
|
-
import
|
|
3346
|
-
import
|
|
3462
|
+
import fs8 from "fs";
|
|
3463
|
+
import path7 from "path";
|
|
3347
3464
|
import crypto from "crypto";
|
|
3348
3465
|
import { execSync as execSync7 } from "child_process";
|
|
3349
3466
|
var CACHE_VERSION = 1;
|
|
3350
3467
|
var CACHE_DIR = ".caliber/cache";
|
|
3351
3468
|
var CACHE_FILE = "fingerprint.json";
|
|
3352
3469
|
function getCachePath(dir) {
|
|
3353
|
-
return
|
|
3470
|
+
return path7.join(dir, CACHE_DIR, CACHE_FILE);
|
|
3354
3471
|
}
|
|
3355
3472
|
function getGitHead(dir) {
|
|
3356
3473
|
try {
|
|
@@ -3387,8 +3504,8 @@ function computeTreeSignature(fileTree, dir) {
|
|
|
3387
3504
|
function loadFingerprintCache(dir, fileTree) {
|
|
3388
3505
|
const cachePath = getCachePath(dir);
|
|
3389
3506
|
try {
|
|
3390
|
-
if (!
|
|
3391
|
-
const raw =
|
|
3507
|
+
if (!fs8.existsSync(cachePath)) return null;
|
|
3508
|
+
const raw = fs8.readFileSync(cachePath, "utf-8");
|
|
3392
3509
|
const cache = JSON.parse(raw);
|
|
3393
3510
|
if (cache.version !== CACHE_VERSION) return null;
|
|
3394
3511
|
const currentHead = getGitHead(dir);
|
|
@@ -3409,9 +3526,9 @@ function loadFingerprintCache(dir, fileTree) {
|
|
|
3409
3526
|
function saveFingerprintCache(dir, fileTree, codeAnalysis, languages, frameworks, tools, workspaces) {
|
|
3410
3527
|
const cachePath = getCachePath(dir);
|
|
3411
3528
|
try {
|
|
3412
|
-
const cacheDir =
|
|
3413
|
-
if (!
|
|
3414
|
-
|
|
3529
|
+
const cacheDir = path7.dirname(cachePath);
|
|
3530
|
+
if (!fs8.existsSync(cacheDir)) {
|
|
3531
|
+
fs8.mkdirSync(cacheDir, { recursive: true });
|
|
3415
3532
|
}
|
|
3416
3533
|
const cache = {
|
|
3417
3534
|
version: CACHE_VERSION,
|
|
@@ -3423,15 +3540,15 @@ function saveFingerprintCache(dir, fileTree, codeAnalysis, languages, frameworks
|
|
|
3423
3540
|
tools,
|
|
3424
3541
|
workspaces
|
|
3425
3542
|
};
|
|
3426
|
-
|
|
3543
|
+
fs8.writeFileSync(cachePath, JSON.stringify(cache), "utf-8");
|
|
3427
3544
|
} catch {
|
|
3428
3545
|
}
|
|
3429
3546
|
}
|
|
3430
3547
|
function getDetectedWorkspaces(dir) {
|
|
3431
3548
|
const cachePath = getCachePath(dir);
|
|
3432
3549
|
try {
|
|
3433
|
-
if (!
|
|
3434
|
-
const raw =
|
|
3550
|
+
if (!fs8.existsSync(cachePath)) return [];
|
|
3551
|
+
const raw = fs8.readFileSync(cachePath, "utf-8");
|
|
3435
3552
|
const cache = JSON.parse(raw);
|
|
3436
3553
|
return cache.workspaces ?? [];
|
|
3437
3554
|
} catch {
|
|
@@ -3483,9 +3600,9 @@ async function collectFingerprint(dir) {
|
|
|
3483
3600
|
}
|
|
3484
3601
|
function readPackageName(dir) {
|
|
3485
3602
|
try {
|
|
3486
|
-
const pkgPath =
|
|
3487
|
-
if (!
|
|
3488
|
-
const pkg3 = JSON.parse(
|
|
3603
|
+
const pkgPath = path8.join(dir, "package.json");
|
|
3604
|
+
if (!fs9.existsSync(pkgPath)) return void 0;
|
|
3605
|
+
const pkg3 = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
|
|
3489
3606
|
return pkg3.name;
|
|
3490
3607
|
} catch {
|
|
3491
3608
|
return void 0;
|
|
@@ -3499,7 +3616,7 @@ async function enrichWithLLM(fingerprint) {
|
|
|
3499
3616
|
const suffixCounts = {};
|
|
3500
3617
|
for (const entry of fingerprint.fileTree) {
|
|
3501
3618
|
if (entry.endsWith("/")) continue;
|
|
3502
|
-
const ext =
|
|
3619
|
+
const ext = path8.extname(entry).toLowerCase();
|
|
3503
3620
|
if (ext) {
|
|
3504
3621
|
suffixCounts[ext] = (suffixCounts[ext] || 0) + 1;
|
|
3505
3622
|
}
|
|
@@ -3515,22 +3632,22 @@ async function enrichWithLLM(fingerprint) {
|
|
|
3515
3632
|
}
|
|
3516
3633
|
|
|
3517
3634
|
// src/scanner/index.ts
|
|
3518
|
-
import
|
|
3519
|
-
import
|
|
3635
|
+
import fs10 from "fs";
|
|
3636
|
+
import path9 from "path";
|
|
3520
3637
|
import crypto2 from "crypto";
|
|
3521
3638
|
import os4 from "os";
|
|
3522
3639
|
function detectPlatforms() {
|
|
3523
3640
|
const home = os4.homedir();
|
|
3524
3641
|
return {
|
|
3525
|
-
claude:
|
|
3526
|
-
cursor:
|
|
3527
|
-
codex:
|
|
3642
|
+
claude: fs10.existsSync(path9.join(home, ".claude")),
|
|
3643
|
+
cursor: fs10.existsSync(getCursorConfigDir()),
|
|
3644
|
+
codex: fs10.existsSync(path9.join(home, ".codex"))
|
|
3528
3645
|
};
|
|
3529
3646
|
}
|
|
3530
3647
|
function scanLocalState(dir) {
|
|
3531
3648
|
const items = [];
|
|
3532
|
-
const claudeMdPath =
|
|
3533
|
-
if (
|
|
3649
|
+
const claudeMdPath = path9.join(dir, "CLAUDE.md");
|
|
3650
|
+
if (fs10.existsSync(claudeMdPath)) {
|
|
3534
3651
|
items.push({
|
|
3535
3652
|
type: "rule",
|
|
3536
3653
|
platform: "claude",
|
|
@@ -3539,10 +3656,10 @@ function scanLocalState(dir) {
|
|
|
3539
3656
|
path: claudeMdPath
|
|
3540
3657
|
});
|
|
3541
3658
|
}
|
|
3542
|
-
const skillsDir =
|
|
3543
|
-
if (
|
|
3544
|
-
for (const file of
|
|
3545
|
-
const filePath =
|
|
3659
|
+
const skillsDir = path9.join(dir, ".claude", "skills");
|
|
3660
|
+
if (fs10.existsSync(skillsDir)) {
|
|
3661
|
+
for (const file of fs10.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
|
|
3662
|
+
const filePath = path9.join(skillsDir, file);
|
|
3546
3663
|
items.push({
|
|
3547
3664
|
type: "skill",
|
|
3548
3665
|
platform: "claude",
|
|
@@ -3552,10 +3669,10 @@ function scanLocalState(dir) {
|
|
|
3552
3669
|
});
|
|
3553
3670
|
}
|
|
3554
3671
|
}
|
|
3555
|
-
const mcpJsonPath =
|
|
3556
|
-
if (
|
|
3672
|
+
const mcpJsonPath = path9.join(dir, ".mcp.json");
|
|
3673
|
+
if (fs10.existsSync(mcpJsonPath)) {
|
|
3557
3674
|
try {
|
|
3558
|
-
const mcpJson = JSON.parse(
|
|
3675
|
+
const mcpJson = JSON.parse(fs10.readFileSync(mcpJsonPath, "utf-8"));
|
|
3559
3676
|
if (mcpJson.mcpServers) {
|
|
3560
3677
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
3561
3678
|
items.push({
|
|
@@ -3571,8 +3688,8 @@ function scanLocalState(dir) {
|
|
|
3571
3688
|
warnScanSkip(".mcp.json", error);
|
|
3572
3689
|
}
|
|
3573
3690
|
}
|
|
3574
|
-
const agentsMdPath =
|
|
3575
|
-
if (
|
|
3691
|
+
const agentsMdPath = path9.join(dir, "AGENTS.md");
|
|
3692
|
+
if (fs10.existsSync(agentsMdPath)) {
|
|
3576
3693
|
items.push({
|
|
3577
3694
|
type: "rule",
|
|
3578
3695
|
platform: "codex",
|
|
@@ -3581,12 +3698,12 @@ function scanLocalState(dir) {
|
|
|
3581
3698
|
path: agentsMdPath
|
|
3582
3699
|
});
|
|
3583
3700
|
}
|
|
3584
|
-
const codexSkillsDir =
|
|
3585
|
-
if (
|
|
3701
|
+
const codexSkillsDir = path9.join(dir, ".agents", "skills");
|
|
3702
|
+
if (fs10.existsSync(codexSkillsDir)) {
|
|
3586
3703
|
try {
|
|
3587
|
-
for (const name of
|
|
3588
|
-
const skillFile =
|
|
3589
|
-
if (
|
|
3704
|
+
for (const name of fs10.readdirSync(codexSkillsDir)) {
|
|
3705
|
+
const skillFile = path9.join(codexSkillsDir, name, "SKILL.md");
|
|
3706
|
+
if (fs10.existsSync(skillFile)) {
|
|
3590
3707
|
items.push({
|
|
3591
3708
|
type: "skill",
|
|
3592
3709
|
platform: "codex",
|
|
@@ -3600,8 +3717,8 @@ function scanLocalState(dir) {
|
|
|
3600
3717
|
warnScanSkip(".agents/skills", error);
|
|
3601
3718
|
}
|
|
3602
3719
|
}
|
|
3603
|
-
const cursorrulesPath =
|
|
3604
|
-
if (
|
|
3720
|
+
const cursorrulesPath = path9.join(dir, ".cursorrules");
|
|
3721
|
+
if (fs10.existsSync(cursorrulesPath)) {
|
|
3605
3722
|
items.push({
|
|
3606
3723
|
type: "rule",
|
|
3607
3724
|
platform: "cursor",
|
|
@@ -3610,10 +3727,10 @@ function scanLocalState(dir) {
|
|
|
3610
3727
|
path: cursorrulesPath
|
|
3611
3728
|
});
|
|
3612
3729
|
}
|
|
3613
|
-
const cursorRulesDir =
|
|
3614
|
-
if (
|
|
3615
|
-
for (const file of
|
|
3616
|
-
const filePath =
|
|
3730
|
+
const cursorRulesDir = path9.join(dir, ".cursor", "rules");
|
|
3731
|
+
if (fs10.existsSync(cursorRulesDir)) {
|
|
3732
|
+
for (const file of fs10.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
|
|
3733
|
+
const filePath = path9.join(cursorRulesDir, file);
|
|
3617
3734
|
items.push({
|
|
3618
3735
|
type: "rule",
|
|
3619
3736
|
platform: "cursor",
|
|
@@ -3623,12 +3740,12 @@ function scanLocalState(dir) {
|
|
|
3623
3740
|
});
|
|
3624
3741
|
}
|
|
3625
3742
|
}
|
|
3626
|
-
const cursorSkillsDir =
|
|
3627
|
-
if (
|
|
3743
|
+
const cursorSkillsDir = path9.join(dir, ".cursor", "skills");
|
|
3744
|
+
if (fs10.existsSync(cursorSkillsDir)) {
|
|
3628
3745
|
try {
|
|
3629
|
-
for (const name of
|
|
3630
|
-
const skillFile =
|
|
3631
|
-
if (
|
|
3746
|
+
for (const name of fs10.readdirSync(cursorSkillsDir)) {
|
|
3747
|
+
const skillFile = path9.join(cursorSkillsDir, name, "SKILL.md");
|
|
3748
|
+
if (fs10.existsSync(skillFile)) {
|
|
3632
3749
|
items.push({
|
|
3633
3750
|
type: "skill",
|
|
3634
3751
|
platform: "cursor",
|
|
@@ -3642,10 +3759,10 @@ function scanLocalState(dir) {
|
|
|
3642
3759
|
warnScanSkip(".cursor/skills", error);
|
|
3643
3760
|
}
|
|
3644
3761
|
}
|
|
3645
|
-
const cursorMcpPath =
|
|
3646
|
-
if (
|
|
3762
|
+
const cursorMcpPath = path9.join(dir, ".cursor", "mcp.json");
|
|
3763
|
+
if (fs10.existsSync(cursorMcpPath)) {
|
|
3647
3764
|
try {
|
|
3648
|
-
const mcpJson = JSON.parse(
|
|
3765
|
+
const mcpJson = JSON.parse(fs10.readFileSync(cursorMcpPath, "utf-8"));
|
|
3649
3766
|
if (mcpJson.mcpServers) {
|
|
3650
3767
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
3651
3768
|
items.push({
|
|
@@ -3664,7 +3781,7 @@ function scanLocalState(dir) {
|
|
|
3664
3781
|
return items;
|
|
3665
3782
|
}
|
|
3666
3783
|
function hashFile(filePath) {
|
|
3667
|
-
const text =
|
|
3784
|
+
const text = fs10.readFileSync(filePath, "utf-8");
|
|
3668
3785
|
return crypto2.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
|
|
3669
3786
|
}
|
|
3670
3787
|
function hashJson(obj) {
|
|
@@ -3677,37 +3794,37 @@ function warnScanSkip(target, error) {
|
|
|
3677
3794
|
function getCursorConfigDir() {
|
|
3678
3795
|
const home = os4.homedir();
|
|
3679
3796
|
if (process.platform === "darwin") {
|
|
3680
|
-
return
|
|
3797
|
+
return path9.join(home, "Library", "Application Support", "Cursor");
|
|
3681
3798
|
}
|
|
3682
3799
|
if (process.platform === "win32") {
|
|
3683
|
-
return
|
|
3800
|
+
return path9.join(home, "AppData", "Roaming", "Cursor");
|
|
3684
3801
|
}
|
|
3685
|
-
return
|
|
3802
|
+
return path9.join(home, ".config", "Cursor");
|
|
3686
3803
|
}
|
|
3687
3804
|
|
|
3688
3805
|
// src/lib/hooks.ts
|
|
3689
3806
|
init_resolve_caliber();
|
|
3690
|
-
import
|
|
3691
|
-
import
|
|
3807
|
+
import fs11 from "fs";
|
|
3808
|
+
import path10 from "path";
|
|
3692
3809
|
import { execSync as execSync8 } from "child_process";
|
|
3693
|
-
var SETTINGS_PATH =
|
|
3810
|
+
var SETTINGS_PATH = path10.join(".claude", "settings.json");
|
|
3694
3811
|
var REFRESH_TAIL = "refresh --quiet";
|
|
3695
3812
|
var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
|
|
3696
3813
|
function getHookCommand() {
|
|
3697
3814
|
return `${resolveCaliber()} ${REFRESH_TAIL}`;
|
|
3698
3815
|
}
|
|
3699
3816
|
function readSettings() {
|
|
3700
|
-
if (!
|
|
3817
|
+
if (!fs11.existsSync(SETTINGS_PATH)) return {};
|
|
3701
3818
|
try {
|
|
3702
|
-
return JSON.parse(
|
|
3819
|
+
return JSON.parse(fs11.readFileSync(SETTINGS_PATH, "utf-8"));
|
|
3703
3820
|
} catch {
|
|
3704
3821
|
return {};
|
|
3705
3822
|
}
|
|
3706
3823
|
}
|
|
3707
3824
|
function writeSettings(settings) {
|
|
3708
|
-
const dir =
|
|
3709
|
-
if (!
|
|
3710
|
-
|
|
3825
|
+
const dir = path10.dirname(SETTINGS_PATH);
|
|
3826
|
+
if (!fs11.existsSync(dir)) fs11.mkdirSync(dir, { recursive: true });
|
|
3827
|
+
fs11.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
3711
3828
|
}
|
|
3712
3829
|
function findHookIndex(sessionEnd) {
|
|
3713
3830
|
return sessionEnd.findIndex(
|
|
@@ -3773,19 +3890,19 @@ ${PRECOMMIT_END}`;
|
|
|
3773
3890
|
function getGitHooksDir() {
|
|
3774
3891
|
try {
|
|
3775
3892
|
const gitDir = execSync8("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
3776
|
-
return
|
|
3893
|
+
return path10.join(gitDir, "hooks");
|
|
3777
3894
|
} catch {
|
|
3778
3895
|
return null;
|
|
3779
3896
|
}
|
|
3780
3897
|
}
|
|
3781
3898
|
function getPreCommitPath() {
|
|
3782
3899
|
const hooksDir = getGitHooksDir();
|
|
3783
|
-
return hooksDir ?
|
|
3900
|
+
return hooksDir ? path10.join(hooksDir, "pre-commit") : null;
|
|
3784
3901
|
}
|
|
3785
3902
|
function isPreCommitHookInstalled() {
|
|
3786
3903
|
const hookPath = getPreCommitPath();
|
|
3787
|
-
if (!hookPath || !
|
|
3788
|
-
const content =
|
|
3904
|
+
if (!hookPath || !fs11.existsSync(hookPath)) return false;
|
|
3905
|
+
const content = fs11.readFileSync(hookPath, "utf-8");
|
|
3789
3906
|
return content.includes(PRECOMMIT_START);
|
|
3790
3907
|
}
|
|
3791
3908
|
function installPreCommitHook() {
|
|
@@ -3794,42 +3911,42 @@ function installPreCommitHook() {
|
|
|
3794
3911
|
}
|
|
3795
3912
|
const hookPath = getPreCommitPath();
|
|
3796
3913
|
if (!hookPath) return { installed: false, alreadyInstalled: false };
|
|
3797
|
-
const hooksDir =
|
|
3798
|
-
if (!
|
|
3914
|
+
const hooksDir = path10.dirname(hookPath);
|
|
3915
|
+
if (!fs11.existsSync(hooksDir)) fs11.mkdirSync(hooksDir, { recursive: true });
|
|
3799
3916
|
let content = "";
|
|
3800
|
-
if (
|
|
3801
|
-
content =
|
|
3917
|
+
if (fs11.existsSync(hookPath)) {
|
|
3918
|
+
content = fs11.readFileSync(hookPath, "utf-8");
|
|
3802
3919
|
if (!content.endsWith("\n")) content += "\n";
|
|
3803
3920
|
content += "\n" + getPrecommitBlock() + "\n";
|
|
3804
3921
|
} else {
|
|
3805
3922
|
content = "#!/bin/sh\n\n" + getPrecommitBlock() + "\n";
|
|
3806
3923
|
}
|
|
3807
|
-
|
|
3808
|
-
|
|
3924
|
+
fs11.writeFileSync(hookPath, content);
|
|
3925
|
+
fs11.chmodSync(hookPath, 493);
|
|
3809
3926
|
return { installed: true, alreadyInstalled: false };
|
|
3810
3927
|
}
|
|
3811
3928
|
function removePreCommitHook() {
|
|
3812
3929
|
const hookPath = getPreCommitPath();
|
|
3813
|
-
if (!hookPath || !
|
|
3930
|
+
if (!hookPath || !fs11.existsSync(hookPath)) {
|
|
3814
3931
|
return { removed: false, notFound: true };
|
|
3815
3932
|
}
|
|
3816
|
-
let content =
|
|
3933
|
+
let content = fs11.readFileSync(hookPath, "utf-8");
|
|
3817
3934
|
if (!content.includes(PRECOMMIT_START)) {
|
|
3818
3935
|
return { removed: false, notFound: true };
|
|
3819
3936
|
}
|
|
3820
3937
|
const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
|
|
3821
3938
|
content = content.replace(regex, "\n");
|
|
3822
3939
|
if (content.trim() === "#!/bin/sh" || content.trim() === "") {
|
|
3823
|
-
|
|
3940
|
+
fs11.unlinkSync(hookPath);
|
|
3824
3941
|
} else {
|
|
3825
|
-
|
|
3942
|
+
fs11.writeFileSync(hookPath, content);
|
|
3826
3943
|
}
|
|
3827
3944
|
return { removed: true, notFound: false };
|
|
3828
3945
|
}
|
|
3829
3946
|
|
|
3830
3947
|
// src/fingerprint/sources.ts
|
|
3831
|
-
import
|
|
3832
|
-
import
|
|
3948
|
+
import fs12 from "fs";
|
|
3949
|
+
import path11 from "path";
|
|
3833
3950
|
|
|
3834
3951
|
// src/scoring/utils.ts
|
|
3835
3952
|
import { existsSync as existsSync2, readFileSync, readdirSync } from "fs";
|
|
@@ -4148,7 +4265,7 @@ var SOURCE_CONTENT_LIMIT = 2e3;
|
|
|
4148
4265
|
var README_CONTENT_LIMIT = 1e3;
|
|
4149
4266
|
var ORIGIN_PRIORITY = { cli: 0, config: 1, workspace: 2 };
|
|
4150
4267
|
function loadSourcesConfig(dir) {
|
|
4151
|
-
const configPath =
|
|
4268
|
+
const configPath = path11.join(dir, ".caliber", "sources.json");
|
|
4152
4269
|
const content = readFileOrNull(configPath);
|
|
4153
4270
|
if (!content) return [];
|
|
4154
4271
|
try {
|
|
@@ -4166,29 +4283,29 @@ function loadSourcesConfig(dir) {
|
|
|
4166
4283
|
}
|
|
4167
4284
|
}
|
|
4168
4285
|
function writeSourcesConfig(dir, sources2) {
|
|
4169
|
-
const configDir =
|
|
4170
|
-
if (!
|
|
4171
|
-
|
|
4286
|
+
const configDir = path11.join(dir, ".caliber");
|
|
4287
|
+
if (!fs12.existsSync(configDir)) {
|
|
4288
|
+
fs12.mkdirSync(configDir, { recursive: true });
|
|
4172
4289
|
}
|
|
4173
|
-
const configPath =
|
|
4174
|
-
|
|
4290
|
+
const configPath = path11.join(configDir, "sources.json");
|
|
4291
|
+
fs12.writeFileSync(configPath, JSON.stringify({ sources: sources2 }, null, 2) + "\n", "utf-8");
|
|
4175
4292
|
}
|
|
4176
4293
|
function detectSourceType(absPath) {
|
|
4177
4294
|
try {
|
|
4178
|
-
return
|
|
4295
|
+
return fs12.statSync(absPath).isDirectory() ? "repo" : "file";
|
|
4179
4296
|
} catch {
|
|
4180
4297
|
return "file";
|
|
4181
4298
|
}
|
|
4182
4299
|
}
|
|
4183
4300
|
function isInsideDir(childPath, parentDir) {
|
|
4184
|
-
const relative2 =
|
|
4185
|
-
return !relative2.startsWith("..") && !
|
|
4301
|
+
const relative2 = path11.relative(parentDir, childPath);
|
|
4302
|
+
return !relative2.startsWith("..") && !path11.isAbsolute(relative2);
|
|
4186
4303
|
}
|
|
4187
4304
|
function resolveAllSources(dir, cliSources, workspaces) {
|
|
4188
4305
|
const seen = /* @__PURE__ */ new Map();
|
|
4189
|
-
const projectRoot =
|
|
4306
|
+
const projectRoot = path11.resolve(dir);
|
|
4190
4307
|
for (const src of cliSources) {
|
|
4191
|
-
const absPath =
|
|
4308
|
+
const absPath = path11.resolve(dir, src);
|
|
4192
4309
|
if (seen.has(absPath)) continue;
|
|
4193
4310
|
const type = detectSourceType(absPath);
|
|
4194
4311
|
seen.set(absPath, {
|
|
@@ -4201,12 +4318,12 @@ function resolveAllSources(dir, cliSources, workspaces) {
|
|
|
4201
4318
|
for (const cfg of configSources) {
|
|
4202
4319
|
if (cfg.type === "url") continue;
|
|
4203
4320
|
if (!cfg.path) continue;
|
|
4204
|
-
const absPath =
|
|
4321
|
+
const absPath = path11.resolve(dir, cfg.path);
|
|
4205
4322
|
if (seen.has(absPath)) continue;
|
|
4206
4323
|
seen.set(absPath, { absPath, config: cfg, origin: "config" });
|
|
4207
4324
|
}
|
|
4208
4325
|
for (const ws of workspaces) {
|
|
4209
|
-
const absPath =
|
|
4326
|
+
const absPath = path11.resolve(dir, ws);
|
|
4210
4327
|
if (seen.has(absPath)) continue;
|
|
4211
4328
|
if (!isInsideDir(absPath, projectRoot)) continue;
|
|
4212
4329
|
seen.set(absPath, {
|
|
@@ -4219,7 +4336,7 @@ function resolveAllSources(dir, cliSources, workspaces) {
|
|
|
4219
4336
|
for (const [absPath, resolved] of seen) {
|
|
4220
4337
|
let stat;
|
|
4221
4338
|
try {
|
|
4222
|
-
stat =
|
|
4339
|
+
stat = fs12.statSync(absPath);
|
|
4223
4340
|
} catch {
|
|
4224
4341
|
console.warn(`Source ${resolved.config.path || absPath} not found, skipping`);
|
|
4225
4342
|
continue;
|
|
@@ -4248,13 +4365,13 @@ function collectSourceSummary(resolved, projectDir) {
|
|
|
4248
4365
|
if (config.type === "file") {
|
|
4249
4366
|
return collectFileSummary(resolved, projectDir);
|
|
4250
4367
|
}
|
|
4251
|
-
const summaryPath =
|
|
4368
|
+
const summaryPath = path11.join(absPath, ".caliber", "summary.json");
|
|
4252
4369
|
const summaryContent = readFileOrNull(summaryPath);
|
|
4253
4370
|
if (summaryContent) {
|
|
4254
4371
|
try {
|
|
4255
4372
|
const published = JSON.parse(summaryContent);
|
|
4256
4373
|
return {
|
|
4257
|
-
name: published.name ||
|
|
4374
|
+
name: published.name || path11.basename(absPath),
|
|
4258
4375
|
type: "repo",
|
|
4259
4376
|
role: config.role || published.role || "related-repo",
|
|
4260
4377
|
description: config.description || published.description || "",
|
|
@@ -4274,18 +4391,18 @@ function collectRepoSummary(resolved, projectDir) {
|
|
|
4274
4391
|
let topLevelDirs;
|
|
4275
4392
|
let keyFiles;
|
|
4276
4393
|
try {
|
|
4277
|
-
const entries =
|
|
4394
|
+
const entries = fs12.readdirSync(absPath, { withFileTypes: true });
|
|
4278
4395
|
topLevelDirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name).slice(0, 20);
|
|
4279
4396
|
keyFiles = entries.filter((e) => e.isFile() && !e.name.startsWith(".")).map((e) => e.name).slice(0, 15);
|
|
4280
4397
|
} catch {
|
|
4281
4398
|
}
|
|
4282
|
-
const claudeMdContent = readFileOrNull(
|
|
4399
|
+
const claudeMdContent = readFileOrNull(path11.join(absPath, "CLAUDE.md"));
|
|
4283
4400
|
const existingClaudeMd = claudeMdContent ? claudeMdContent.slice(0, SOURCE_CONTENT_LIMIT) : void 0;
|
|
4284
|
-
const readmeContent = readFileOrNull(
|
|
4401
|
+
const readmeContent = readFileOrNull(path11.join(absPath, "README.md"));
|
|
4285
4402
|
const readmeExcerpt = readmeContent ? readmeContent.slice(0, README_CONTENT_LIMIT) : void 0;
|
|
4286
4403
|
const gitRemoteUrl = getGitRemoteUrl(absPath);
|
|
4287
4404
|
return {
|
|
4288
|
-
name: packageName ||
|
|
4405
|
+
name: packageName || path11.basename(absPath),
|
|
4289
4406
|
type: "repo",
|
|
4290
4407
|
role: config.role || "related-repo",
|
|
4291
4408
|
description: config.description || "",
|
|
@@ -4302,7 +4419,7 @@ function collectFileSummary(resolved, projectDir) {
|
|
|
4302
4419
|
const { config, origin, absPath } = resolved;
|
|
4303
4420
|
const content = readFileOrNull(absPath);
|
|
4304
4421
|
return {
|
|
4305
|
-
name:
|
|
4422
|
+
name: path11.basename(absPath),
|
|
4306
4423
|
type: "file",
|
|
4307
4424
|
role: config.role || "reference-doc",
|
|
4308
4425
|
description: config.description || content?.slice(0, 100).split("\n")[0] || "",
|
|
@@ -4346,15 +4463,15 @@ init_config();
|
|
|
4346
4463
|
// src/utils/dependencies.ts
|
|
4347
4464
|
import { readFileSync as readFileSync2 } from "fs";
|
|
4348
4465
|
import { join as join2 } from "path";
|
|
4349
|
-
function readFileOrNull2(
|
|
4466
|
+
function readFileOrNull2(path41) {
|
|
4350
4467
|
try {
|
|
4351
|
-
return readFileSync2(
|
|
4468
|
+
return readFileSync2(path41, "utf-8");
|
|
4352
4469
|
} catch {
|
|
4353
4470
|
return null;
|
|
4354
4471
|
}
|
|
4355
4472
|
}
|
|
4356
|
-
function readJsonOrNull(
|
|
4357
|
-
const content = readFileOrNull2(
|
|
4473
|
+
function readJsonOrNull(path41) {
|
|
4474
|
+
const content = readFileOrNull2(path41);
|
|
4358
4475
|
if (!content) return null;
|
|
4359
4476
|
try {
|
|
4360
4477
|
return JSON.parse(content);
|
|
@@ -4945,17 +5062,17 @@ import fs19 from "fs";
|
|
|
4945
5062
|
|
|
4946
5063
|
// src/writers/claude/index.ts
|
|
4947
5064
|
init_pre_commit_block();
|
|
4948
|
-
import
|
|
4949
|
-
import
|
|
5065
|
+
import fs13 from "fs";
|
|
5066
|
+
import path12 from "path";
|
|
4950
5067
|
function writeClaudeConfig(config) {
|
|
4951
5068
|
const written = [];
|
|
4952
|
-
|
|
5069
|
+
fs13.writeFileSync("CLAUDE.md", appendManagedBlocks(config.claudeMd, "claude"));
|
|
4953
5070
|
written.push("CLAUDE.md");
|
|
4954
5071
|
if (config.skills?.length) {
|
|
4955
5072
|
for (const skill of config.skills) {
|
|
4956
|
-
const skillDir =
|
|
4957
|
-
if (!
|
|
4958
|
-
const skillPath =
|
|
5073
|
+
const skillDir = path12.join(".claude", "skills", skill.name);
|
|
5074
|
+
if (!fs13.existsSync(skillDir)) fs13.mkdirSync(skillDir, { recursive: true });
|
|
5075
|
+
const skillPath = path12.join(skillDir, "SKILL.md");
|
|
4959
5076
|
const frontmatter = [
|
|
4960
5077
|
"---",
|
|
4961
5078
|
`name: ${skill.name}`,
|
|
@@ -4963,21 +5080,21 @@ function writeClaudeConfig(config) {
|
|
|
4963
5080
|
"---",
|
|
4964
5081
|
""
|
|
4965
5082
|
].join("\n");
|
|
4966
|
-
|
|
5083
|
+
fs13.writeFileSync(skillPath, frontmatter + skill.content);
|
|
4967
5084
|
written.push(skillPath);
|
|
4968
5085
|
}
|
|
4969
5086
|
}
|
|
4970
5087
|
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
4971
5088
|
let existingServers = {};
|
|
4972
5089
|
try {
|
|
4973
|
-
if (
|
|
4974
|
-
const existing = JSON.parse(
|
|
5090
|
+
if (fs13.existsSync(".mcp.json")) {
|
|
5091
|
+
const existing = JSON.parse(fs13.readFileSync(".mcp.json", "utf-8"));
|
|
4975
5092
|
if (existing.mcpServers) existingServers = existing.mcpServers;
|
|
4976
5093
|
}
|
|
4977
5094
|
} catch {
|
|
4978
5095
|
}
|
|
4979
5096
|
const mergedServers = { ...existingServers, ...config.mcpServers };
|
|
4980
|
-
|
|
5097
|
+
fs13.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
|
|
4981
5098
|
written.push(".mcp.json");
|
|
4982
5099
|
}
|
|
4983
5100
|
return written;
|
|
@@ -4985,30 +5102,31 @@ function writeClaudeConfig(config) {
|
|
|
4985
5102
|
|
|
4986
5103
|
// src/writers/cursor/index.ts
|
|
4987
5104
|
init_pre_commit_block();
|
|
4988
|
-
import
|
|
4989
|
-
import
|
|
5105
|
+
import fs14 from "fs";
|
|
5106
|
+
import path13 from "path";
|
|
4990
5107
|
function writeCursorConfig(config) {
|
|
4991
5108
|
const written = [];
|
|
4992
5109
|
if (config.cursorrules) {
|
|
4993
|
-
|
|
5110
|
+
fs14.writeFileSync(".cursorrules", config.cursorrules);
|
|
4994
5111
|
written.push(".cursorrules");
|
|
4995
5112
|
}
|
|
4996
5113
|
const preCommitRule = getCursorPreCommitRule();
|
|
4997
5114
|
const learningsRule = getCursorLearningsRule();
|
|
4998
5115
|
const syncRule = getCursorSyncRule();
|
|
4999
|
-
const
|
|
5000
|
-
const
|
|
5001
|
-
|
|
5116
|
+
const setupRule = getCursorSetupRule();
|
|
5117
|
+
const allRules = [...config.rules || [], preCommitRule, learningsRule, syncRule, setupRule];
|
|
5118
|
+
const rulesDir = path13.join(".cursor", "rules");
|
|
5119
|
+
if (!fs14.existsSync(rulesDir)) fs14.mkdirSync(rulesDir, { recursive: true });
|
|
5002
5120
|
for (const rule of allRules) {
|
|
5003
|
-
const rulePath =
|
|
5004
|
-
|
|
5121
|
+
const rulePath = path13.join(rulesDir, rule.filename);
|
|
5122
|
+
fs14.writeFileSync(rulePath, rule.content);
|
|
5005
5123
|
written.push(rulePath);
|
|
5006
5124
|
}
|
|
5007
5125
|
if (config.skills?.length) {
|
|
5008
5126
|
for (const skill of config.skills) {
|
|
5009
|
-
const skillDir =
|
|
5010
|
-
if (!
|
|
5011
|
-
const skillPath =
|
|
5127
|
+
const skillDir = path13.join(".cursor", "skills", skill.name);
|
|
5128
|
+
if (!fs14.existsSync(skillDir)) fs14.mkdirSync(skillDir, { recursive: true });
|
|
5129
|
+
const skillPath = path13.join(skillDir, "SKILL.md");
|
|
5012
5130
|
const frontmatter = [
|
|
5013
5131
|
"---",
|
|
5014
5132
|
`name: ${skill.name}`,
|
|
@@ -5016,24 +5134,24 @@ function writeCursorConfig(config) {
|
|
|
5016
5134
|
"---",
|
|
5017
5135
|
""
|
|
5018
5136
|
].join("\n");
|
|
5019
|
-
|
|
5137
|
+
fs14.writeFileSync(skillPath, frontmatter + skill.content);
|
|
5020
5138
|
written.push(skillPath);
|
|
5021
5139
|
}
|
|
5022
5140
|
}
|
|
5023
5141
|
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
5024
5142
|
const cursorDir = ".cursor";
|
|
5025
|
-
if (!
|
|
5026
|
-
const mcpPath =
|
|
5143
|
+
if (!fs14.existsSync(cursorDir)) fs14.mkdirSync(cursorDir, { recursive: true });
|
|
5144
|
+
const mcpPath = path13.join(cursorDir, "mcp.json");
|
|
5027
5145
|
let existingServers = {};
|
|
5028
5146
|
try {
|
|
5029
|
-
if (
|
|
5030
|
-
const existing = JSON.parse(
|
|
5147
|
+
if (fs14.existsSync(mcpPath)) {
|
|
5148
|
+
const existing = JSON.parse(fs14.readFileSync(mcpPath, "utf-8"));
|
|
5031
5149
|
if (existing.mcpServers) existingServers = existing.mcpServers;
|
|
5032
5150
|
}
|
|
5033
5151
|
} catch {
|
|
5034
5152
|
}
|
|
5035
5153
|
const mergedServers = { ...existingServers, ...config.mcpServers };
|
|
5036
|
-
|
|
5154
|
+
fs14.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
|
|
5037
5155
|
written.push(mcpPath);
|
|
5038
5156
|
}
|
|
5039
5157
|
return written;
|
|
@@ -5041,17 +5159,17 @@ function writeCursorConfig(config) {
|
|
|
5041
5159
|
|
|
5042
5160
|
// src/writers/codex/index.ts
|
|
5043
5161
|
init_pre_commit_block();
|
|
5044
|
-
import
|
|
5045
|
-
import
|
|
5162
|
+
import fs15 from "fs";
|
|
5163
|
+
import path14 from "path";
|
|
5046
5164
|
function writeCodexConfig(config) {
|
|
5047
5165
|
const written = [];
|
|
5048
|
-
|
|
5166
|
+
fs15.writeFileSync("AGENTS.md", appendManagedBlocks(config.agentsMd, "codex"));
|
|
5049
5167
|
written.push("AGENTS.md");
|
|
5050
5168
|
if (config.skills?.length) {
|
|
5051
5169
|
for (const skill of config.skills) {
|
|
5052
|
-
const skillDir =
|
|
5053
|
-
if (!
|
|
5054
|
-
const skillPath =
|
|
5170
|
+
const skillDir = path14.join(".agents", "skills", skill.name);
|
|
5171
|
+
if (!fs15.existsSync(skillDir)) fs15.mkdirSync(skillDir, { recursive: true });
|
|
5172
|
+
const skillPath = path14.join(skillDir, "SKILL.md");
|
|
5055
5173
|
const frontmatter = [
|
|
5056
5174
|
"---",
|
|
5057
5175
|
`name: ${skill.name}`,
|
|
@@ -5059,7 +5177,7 @@ function writeCodexConfig(config) {
|
|
|
5059
5177
|
"---",
|
|
5060
5178
|
""
|
|
5061
5179
|
].join("\n");
|
|
5062
|
-
|
|
5180
|
+
fs15.writeFileSync(skillPath, frontmatter + skill.content);
|
|
5063
5181
|
written.push(skillPath);
|
|
5064
5182
|
}
|
|
5065
5183
|
}
|
|
@@ -5068,20 +5186,20 @@ function writeCodexConfig(config) {
|
|
|
5068
5186
|
|
|
5069
5187
|
// src/writers/github-copilot/index.ts
|
|
5070
5188
|
init_pre_commit_block();
|
|
5071
|
-
import
|
|
5072
|
-
import
|
|
5189
|
+
import fs16 from "fs";
|
|
5190
|
+
import path15 from "path";
|
|
5073
5191
|
function writeGithubCopilotConfig(config) {
|
|
5074
5192
|
const written = [];
|
|
5075
5193
|
if (config.instructions) {
|
|
5076
|
-
|
|
5077
|
-
|
|
5194
|
+
fs16.mkdirSync(".github", { recursive: true });
|
|
5195
|
+
fs16.writeFileSync(path15.join(".github", "copilot-instructions.md"), appendManagedBlocks(config.instructions, "copilot"));
|
|
5078
5196
|
written.push(".github/copilot-instructions.md");
|
|
5079
5197
|
}
|
|
5080
5198
|
if (config.instructionFiles?.length) {
|
|
5081
|
-
const instructionsDir =
|
|
5082
|
-
|
|
5199
|
+
const instructionsDir = path15.join(".github", "instructions");
|
|
5200
|
+
fs16.mkdirSync(instructionsDir, { recursive: true });
|
|
5083
5201
|
for (const file of config.instructionFiles) {
|
|
5084
|
-
|
|
5202
|
+
fs16.writeFileSync(path15.join(instructionsDir, file.filename), file.content);
|
|
5085
5203
|
written.push(`.github/instructions/${file.filename}`);
|
|
5086
5204
|
}
|
|
5087
5205
|
}
|
|
@@ -5089,30 +5207,30 @@ function writeGithubCopilotConfig(config) {
|
|
|
5089
5207
|
}
|
|
5090
5208
|
|
|
5091
5209
|
// src/writers/backup.ts
|
|
5092
|
-
import
|
|
5093
|
-
import
|
|
5210
|
+
import fs17 from "fs";
|
|
5211
|
+
import path16 from "path";
|
|
5094
5212
|
function createBackup(files) {
|
|
5095
5213
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
5096
|
-
const backupDir =
|
|
5214
|
+
const backupDir = path16.join(BACKUPS_DIR, timestamp);
|
|
5097
5215
|
for (const file of files) {
|
|
5098
|
-
if (!
|
|
5099
|
-
const dest =
|
|
5100
|
-
const destDir =
|
|
5101
|
-
if (!
|
|
5102
|
-
|
|
5216
|
+
if (!fs17.existsSync(file)) continue;
|
|
5217
|
+
const dest = path16.join(backupDir, file);
|
|
5218
|
+
const destDir = path16.dirname(dest);
|
|
5219
|
+
if (!fs17.existsSync(destDir)) {
|
|
5220
|
+
fs17.mkdirSync(destDir, { recursive: true });
|
|
5103
5221
|
}
|
|
5104
|
-
|
|
5222
|
+
fs17.copyFileSync(file, dest);
|
|
5105
5223
|
}
|
|
5106
5224
|
return backupDir;
|
|
5107
5225
|
}
|
|
5108
5226
|
function restoreBackup(backupDir, file) {
|
|
5109
|
-
const backupFile =
|
|
5110
|
-
if (!
|
|
5111
|
-
const destDir =
|
|
5112
|
-
if (!
|
|
5113
|
-
|
|
5227
|
+
const backupFile = path16.join(backupDir, file);
|
|
5228
|
+
if (!fs17.existsSync(backupFile)) return false;
|
|
5229
|
+
const destDir = path16.dirname(file);
|
|
5230
|
+
if (!fs17.existsSync(destDir)) {
|
|
5231
|
+
fs17.mkdirSync(destDir, { recursive: true });
|
|
5114
5232
|
}
|
|
5115
|
-
|
|
5233
|
+
fs17.copyFileSync(backupFile, file);
|
|
5116
5234
|
return true;
|
|
5117
5235
|
}
|
|
5118
5236
|
|
|
@@ -5674,6 +5792,9 @@ async function runInteractiveProviderSetup(options) {
|
|
|
5674
5792
|
return config;
|
|
5675
5793
|
}
|
|
5676
5794
|
|
|
5795
|
+
// src/commands/init.ts
|
|
5796
|
+
import confirm2 from "@inquirer/confirm";
|
|
5797
|
+
|
|
5677
5798
|
// src/scoring/index.ts
|
|
5678
5799
|
import { existsSync as existsSync7 } from "fs";
|
|
5679
5800
|
import { join as join9 } from "path";
|
|
@@ -6672,8 +6793,8 @@ function detectTargetAgent(dir) {
|
|
|
6672
6793
|
const agents = [];
|
|
6673
6794
|
if (existsSync7(join9(dir, "CLAUDE.md")) || existsSync7(join9(dir, ".claude", "skills"))) agents.push("claude");
|
|
6674
6795
|
if (existsSync7(join9(dir, ".cursorrules")) || existsSync7(join9(dir, ".cursor", "rules"))) agents.push("cursor");
|
|
6675
|
-
if (existsSync7(join9(dir, ".codex")) || existsSync7(join9(dir, ".agents", "skills"))) agents.push("codex");
|
|
6676
|
-
if (existsSync7(join9(dir, ".github", "copilot-instructions.md"))) agents.push("github-copilot");
|
|
6796
|
+
if (existsSync7(join9(dir, ".codex")) || existsSync7(join9(dir, ".agents", "skills")) || existsSync7(join9(dir, "AGENTS.md"))) agents.push("codex");
|
|
6797
|
+
if (existsSync7(join9(dir, ".github", "copilot-instructions.md")) || existsSync7(join9(dir, ".github", "instructions"))) agents.push("github-copilot");
|
|
6677
6798
|
return agents.length > 0 ? agents : ["claude"];
|
|
6678
6799
|
}
|
|
6679
6800
|
function computeLocalScore(dir, targetAgent) {
|
|
@@ -6927,11 +7048,57 @@ function getMachineId() {
|
|
|
6927
7048
|
return machineId;
|
|
6928
7049
|
}
|
|
6929
7050
|
var EMAIL_HASH_KEY = "caliber-telemetry-v1";
|
|
6930
|
-
|
|
7051
|
+
var PERSONAL_DOMAINS = /* @__PURE__ */ new Set([
|
|
7052
|
+
"gmail.com",
|
|
7053
|
+
"googlemail.com",
|
|
7054
|
+
"outlook.com",
|
|
7055
|
+
"hotmail.com",
|
|
7056
|
+
"live.com",
|
|
7057
|
+
"yahoo.com",
|
|
7058
|
+
"yahoo.co.uk",
|
|
7059
|
+
"icloud.com",
|
|
7060
|
+
"me.com",
|
|
7061
|
+
"mac.com",
|
|
7062
|
+
"aol.com",
|
|
7063
|
+
"protonmail.com",
|
|
7064
|
+
"proton.me",
|
|
7065
|
+
"pm.me",
|
|
7066
|
+
"mail.com",
|
|
7067
|
+
"zoho.com",
|
|
7068
|
+
"yandex.com",
|
|
7069
|
+
"gmx.com",
|
|
7070
|
+
"gmx.net",
|
|
7071
|
+
"fastmail.com",
|
|
7072
|
+
"tutanota.com",
|
|
7073
|
+
"tuta.io"
|
|
7074
|
+
]);
|
|
7075
|
+
function getGitEmail() {
|
|
6931
7076
|
try {
|
|
6932
7077
|
const email = execSync13("git config user.email", { encoding: "utf-8" }).trim();
|
|
6933
|
-
|
|
6934
|
-
|
|
7078
|
+
return email || void 0;
|
|
7079
|
+
} catch {
|
|
7080
|
+
return void 0;
|
|
7081
|
+
}
|
|
7082
|
+
}
|
|
7083
|
+
function getGitEmailInfo() {
|
|
7084
|
+
const email = getGitEmail();
|
|
7085
|
+
if (!email) return {};
|
|
7086
|
+
const hash = crypto4.createHmac("sha256", EMAIL_HASH_KEY).update(email).digest("hex");
|
|
7087
|
+
let domain;
|
|
7088
|
+
if (email.includes("@")) {
|
|
7089
|
+
const d = email.split("@")[1].toLowerCase();
|
|
7090
|
+
if (!PERSONAL_DOMAINS.has(d)) domain = d;
|
|
7091
|
+
}
|
|
7092
|
+
return { hash, domain };
|
|
7093
|
+
}
|
|
7094
|
+
function getRepoHash() {
|
|
7095
|
+
try {
|
|
7096
|
+
const remote = execSync13("git remote get-url origin || git rev-parse --show-toplevel", {
|
|
7097
|
+
encoding: "utf-8",
|
|
7098
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
7099
|
+
}).trim();
|
|
7100
|
+
if (!remote) return void 0;
|
|
7101
|
+
return crypto4.createHmac("sha256", EMAIL_HASH_KEY).update(remote).digest("hex").slice(0, 16);
|
|
6935
7102
|
} catch {
|
|
6936
7103
|
return void 0;
|
|
6937
7104
|
}
|
|
@@ -6956,6 +7123,7 @@ function markNoticeShown() {
|
|
|
6956
7123
|
var POSTHOG_KEY = "phc_XXrV0pSX4s2QVxVoOaeuyXDvtlRwPAjovt1ttMGVMPp";
|
|
6957
7124
|
var client = null;
|
|
6958
7125
|
var distinctId = null;
|
|
7126
|
+
var superProperties = {};
|
|
6959
7127
|
function initTelemetry() {
|
|
6960
7128
|
if (isTelemetryDisabled()) return;
|
|
6961
7129
|
const machineId = getMachineId();
|
|
@@ -6971,11 +7139,17 @@ function initTelemetry() {
|
|
|
6971
7139
|
);
|
|
6972
7140
|
markNoticeShown();
|
|
6973
7141
|
}
|
|
6974
|
-
const gitEmailHash =
|
|
7142
|
+
const { hash: gitEmailHash, domain: emailDomain } = getGitEmailInfo();
|
|
7143
|
+
const repoHash = getRepoHash();
|
|
7144
|
+
superProperties = {
|
|
7145
|
+
...repoHash ? { repo_hash: repoHash } : {},
|
|
7146
|
+
...emailDomain ? { email_domain: emailDomain } : {}
|
|
7147
|
+
};
|
|
6975
7148
|
client.identify({
|
|
6976
7149
|
distinctId: machineId,
|
|
6977
7150
|
properties: {
|
|
6978
|
-
...gitEmailHash ? { git_email_hash: gitEmailHash } : {}
|
|
7151
|
+
...gitEmailHash ? { git_email_hash: gitEmailHash } : {},
|
|
7152
|
+
...emailDomain ? { email_domain: emailDomain } : {}
|
|
6979
7153
|
}
|
|
6980
7154
|
});
|
|
6981
7155
|
}
|
|
@@ -6984,7 +7158,7 @@ function trackEvent(name, properties) {
|
|
|
6984
7158
|
client.capture({
|
|
6985
7159
|
distinctId,
|
|
6986
7160
|
event: name,
|
|
6987
|
-
properties: properties
|
|
7161
|
+
properties: { ...superProperties, ...properties }
|
|
6988
7162
|
});
|
|
6989
7163
|
}
|
|
6990
7164
|
async function flushTelemetry() {
|
|
@@ -7033,11 +7207,14 @@ function trackInitSkillsSearch(searched, installedCount) {
|
|
|
7033
7207
|
function trackInitScoreRegression(oldScore, newScore) {
|
|
7034
7208
|
trackEvent("init_score_regression", { old_score: oldScore, new_score: newScore });
|
|
7035
7209
|
}
|
|
7210
|
+
function trackInitCompleted(path41, score) {
|
|
7211
|
+
trackEvent("init_completed", { path: path41, score });
|
|
7212
|
+
}
|
|
7036
7213
|
function trackRegenerateCompleted(action, durationMs) {
|
|
7037
7214
|
trackEvent("regenerate_completed", { action, duration_ms: durationMs });
|
|
7038
7215
|
}
|
|
7039
|
-
function trackRefreshCompleted(changesCount, durationMs) {
|
|
7040
|
-
trackEvent("refresh_completed", { changes_count: changesCount, duration_ms: durationMs });
|
|
7216
|
+
function trackRefreshCompleted(changesCount, durationMs, trigger) {
|
|
7217
|
+
trackEvent("refresh_completed", { changes_count: changesCount, duration_ms: durationMs, trigger: trigger ?? "manual" });
|
|
7041
7218
|
}
|
|
7042
7219
|
function trackScoreComputed(score, agent) {
|
|
7043
7220
|
trackEvent("score_computed", { score, agent });
|
|
@@ -7051,6 +7228,9 @@ function trackSkillsInstalled(count) {
|
|
|
7051
7228
|
function trackUndoExecuted() {
|
|
7052
7229
|
trackEvent("undo_executed");
|
|
7053
7230
|
}
|
|
7231
|
+
function trackUninstallExecuted() {
|
|
7232
|
+
trackEvent("uninstall_executed");
|
|
7233
|
+
}
|
|
7054
7234
|
function trackInitLearnEnabled(enabled) {
|
|
7055
7235
|
trackEvent("init_learn_enabled", { enabled });
|
|
7056
7236
|
}
|
|
@@ -7985,11 +8165,11 @@ function countIssuePoints(issues) {
|
|
|
7985
8165
|
}
|
|
7986
8166
|
async function scoreAndRefine(setup, dir, sessionHistory, callbacks) {
|
|
7987
8167
|
const existsCache = /* @__PURE__ */ new Map();
|
|
7988
|
-
const cachedExists = (
|
|
7989
|
-
const cached = existsCache.get(
|
|
8168
|
+
const cachedExists = (path41) => {
|
|
8169
|
+
const cached = existsCache.get(path41);
|
|
7990
8170
|
if (cached !== void 0) return cached;
|
|
7991
|
-
const result = existsSync9(
|
|
7992
|
-
existsCache.set(
|
|
8171
|
+
const result = existsSync9(path41);
|
|
8172
|
+
existsCache.set(path41, result);
|
|
7993
8173
|
return result;
|
|
7994
8174
|
};
|
|
7995
8175
|
const projectStructure = collectProjectStructure(dir);
|
|
@@ -8769,25 +8949,6 @@ async function promptAgent(detected) {
|
|
|
8769
8949
|
});
|
|
8770
8950
|
return selected;
|
|
8771
8951
|
}
|
|
8772
|
-
async function promptLearnInstall(targetAgent) {
|
|
8773
|
-
const hasClaude = targetAgent.includes("claude");
|
|
8774
|
-
const hasCursor = targetAgent.includes("cursor");
|
|
8775
|
-
const agentName = hasClaude && hasCursor ? "Claude and Cursor" : hasClaude ? "Claude" : "Cursor";
|
|
8776
|
-
console.log(chalk11.bold(`
|
|
8777
|
-
Session Learning
|
|
8778
|
-
`));
|
|
8779
|
-
console.log(chalk11.dim(` Caliber can learn from your ${agentName} sessions \u2014 when a tool fails`));
|
|
8780
|
-
console.log(chalk11.dim(` or you correct a mistake, it captures the lesson so it won't`));
|
|
8781
|
-
console.log(chalk11.dim(` happen again. Runs once at session end using the fast model.
|
|
8782
|
-
`));
|
|
8783
|
-
return select5({
|
|
8784
|
-
message: "Enable session learning?",
|
|
8785
|
-
choices: [
|
|
8786
|
-
{ name: "Enable session learning (recommended)", value: true },
|
|
8787
|
-
{ name: "Skip for now", value: false }
|
|
8788
|
-
]
|
|
8789
|
-
});
|
|
8790
|
-
}
|
|
8791
8952
|
async function promptReviewAction(hasSkillResults, hasChanges, staged) {
|
|
8792
8953
|
if (!hasChanges && !hasSkillResults) return "accept";
|
|
8793
8954
|
const choices = [];
|
|
@@ -9246,10 +9407,9 @@ async function initCommand(options) {
|
|
|
9246
9407
|
console.log(chalk14.dim(" Keep your AI agent configs in sync \u2014 automatically."));
|
|
9247
9408
|
console.log(chalk14.dim(" Works across Claude Code, Cursor, Codex, and GitHub Copilot.\n"));
|
|
9248
9409
|
console.log(title.bold(" How it works:\n"));
|
|
9249
|
-
console.log(chalk14.dim(" 1. Connect
|
|
9250
|
-
console.log(chalk14.dim(" 2.
|
|
9251
|
-
console.log(chalk14.dim(" 3.
|
|
9252
|
-
console.log(chalk14.dim(" 4. Finalize Review changes and score your setup\n"));
|
|
9410
|
+
console.log(chalk14.dim(" 1. Connect Auto-detect your LLM provider and agents"));
|
|
9411
|
+
console.log(chalk14.dim(" 2. Build Install sync, scan your project, generate configs"));
|
|
9412
|
+
console.log(chalk14.dim(" 3. Done Review score and start syncing\n"));
|
|
9253
9413
|
} else {
|
|
9254
9414
|
console.log(brand.bold("\n CALIBER") + chalk14.dim(" \u2014 setting up continuous sync\n"));
|
|
9255
9415
|
}
|
|
@@ -9259,19 +9419,50 @@ async function initCommand(options) {
|
|
|
9259
9419
|
console.log(chalk14.yellow(" Caliber will still generate config files, but they won't be auto-installed.\n"));
|
|
9260
9420
|
}
|
|
9261
9421
|
const report = options.debugReport ? new DebugReport() : null;
|
|
9262
|
-
console.log(title.bold(" Step 1/
|
|
9422
|
+
console.log(title.bold(" Step 1/3 \u2014 Connect\n"));
|
|
9263
9423
|
let config = loadConfig();
|
|
9424
|
+
if (!config && !options.autoApprove) {
|
|
9425
|
+
if (isClaudeCliAvailable()) {
|
|
9426
|
+
console.log(chalk14.dim(" Detected: Claude Code CLI (uses your Pro/Max/Team subscription)\n"));
|
|
9427
|
+
const useIt = await confirm2({ message: "Use Claude Code as your LLM provider?" });
|
|
9428
|
+
if (useIt) {
|
|
9429
|
+
const autoConfig = { provider: "claude-cli", model: "default" };
|
|
9430
|
+
writeConfigFile(autoConfig);
|
|
9431
|
+
config = autoConfig;
|
|
9432
|
+
}
|
|
9433
|
+
} else if (isCursorAgentAvailable() && isCursorLoggedIn()) {
|
|
9434
|
+
console.log(chalk14.dim(" Detected: Cursor (uses your existing subscription)\n"));
|
|
9435
|
+
const useIt = await confirm2({ message: "Use Cursor as your LLM provider?" });
|
|
9436
|
+
if (useIt) {
|
|
9437
|
+
const autoConfig = { provider: "cursor", model: "sonnet-4.6" };
|
|
9438
|
+
writeConfigFile(autoConfig);
|
|
9439
|
+
config = autoConfig;
|
|
9440
|
+
}
|
|
9441
|
+
}
|
|
9442
|
+
}
|
|
9264
9443
|
if (!config) {
|
|
9265
|
-
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
|
|
9269
|
-
|
|
9444
|
+
if (options.autoApprove) {
|
|
9445
|
+
if (isClaudeCliAvailable()) {
|
|
9446
|
+
const autoConfig = { provider: "claude-cli", model: "default" };
|
|
9447
|
+
writeConfigFile(autoConfig);
|
|
9448
|
+
config = autoConfig;
|
|
9449
|
+
} else if (isCursorAgentAvailable() && isCursorLoggedIn()) {
|
|
9450
|
+
const autoConfig = { provider: "cursor", model: "sonnet-4.6" };
|
|
9451
|
+
writeConfigFile(autoConfig);
|
|
9452
|
+
config = autoConfig;
|
|
9453
|
+
}
|
|
9454
|
+
}
|
|
9270
9455
|
if (!config) {
|
|
9271
|
-
console.log(chalk14.
|
|
9272
|
-
|
|
9456
|
+
console.log(chalk14.dim(" No LLM provider detected.\n"));
|
|
9457
|
+
await runInteractiveProviderSetup({
|
|
9458
|
+
selectMessage: "How do you want to use Caliber? (choose LLM provider)"
|
|
9459
|
+
});
|
|
9460
|
+
config = loadConfig();
|
|
9461
|
+
if (!config) {
|
|
9462
|
+
console.log(chalk14.red(" Configuration cancelled or failed.\n"));
|
|
9463
|
+
throw new Error("__exit__");
|
|
9464
|
+
}
|
|
9273
9465
|
}
|
|
9274
|
-
console.log(chalk14.green(" \u2713 Provider saved\n"));
|
|
9275
9466
|
}
|
|
9276
9467
|
trackInitProviderSelected(config.provider, config.model, firstRun);
|
|
9277
9468
|
const displayModel = getDisplayModel(config);
|
|
@@ -9289,21 +9480,28 @@ async function initCommand(options) {
|
|
|
9289
9480
|
const agentAutoDetected = !options.agent;
|
|
9290
9481
|
if (options.agent) {
|
|
9291
9482
|
targetAgent = options.agent;
|
|
9292
|
-
} else if (options.autoApprove) {
|
|
9293
|
-
targetAgent = ["claude"];
|
|
9294
|
-
log(options.verbose, "Auto-approve: defaulting to claude agent");
|
|
9295
9483
|
} else {
|
|
9296
9484
|
const detected = detectAgents(process.cwd());
|
|
9297
|
-
|
|
9485
|
+
if (detected.length > 0 && (options.autoApprove || firstRun)) {
|
|
9486
|
+
targetAgent = detected;
|
|
9487
|
+
console.log(chalk14.dim(` Detected agents: ${detected.join(", ")}
|
|
9488
|
+
`));
|
|
9489
|
+
} else if (detected.length > 0) {
|
|
9490
|
+
console.log(chalk14.dim(` Detected agents: ${detected.join(", ")}
|
|
9491
|
+
`));
|
|
9492
|
+
const useDetected = await confirm2({ message: "Use detected agents?" });
|
|
9493
|
+
targetAgent = useDetected ? detected : await promptAgent();
|
|
9494
|
+
} else {
|
|
9495
|
+
targetAgent = options.autoApprove ? ["claude"] : await promptAgent();
|
|
9496
|
+
}
|
|
9298
9497
|
}
|
|
9299
9498
|
console.log(chalk14.dim(` Target: ${targetAgent.join(", ")}
|
|
9300
9499
|
`));
|
|
9301
9500
|
trackInitAgentSelected(targetAgent, agentAutoDetected);
|
|
9302
|
-
console.log(title.bold(" Step 2/
|
|
9303
|
-
console.log(chalk14.dim(" Installing sync infrastructure...\n"));
|
|
9501
|
+
console.log(title.bold(" Step 2/3 \u2014 Build\n"));
|
|
9304
9502
|
const hookResult = installPreCommitHook();
|
|
9305
9503
|
if (hookResult.installed) {
|
|
9306
|
-
console.log(` ${chalk14.green("\u2713")} Pre-commit hook installed
|
|
9504
|
+
console.log(` ${chalk14.green("\u2713")} Pre-commit hook installed`);
|
|
9307
9505
|
} else if (hookResult.alreadyInstalled) {
|
|
9308
9506
|
console.log(` ${chalk14.green("\u2713")} Pre-commit hook \u2014 active`);
|
|
9309
9507
|
}
|
|
@@ -9315,21 +9513,18 @@ async function initCommand(options) {
|
|
|
9315
9513
|
}
|
|
9316
9514
|
const skillsWritten = ensureBuiltinSkills2();
|
|
9317
9515
|
if (skillsWritten.length > 0) {
|
|
9318
|
-
console.log(` ${chalk14.green("\u2713")} Agent skills installed
|
|
9319
|
-
}
|
|
9320
|
-
|
|
9516
|
+
console.log(` ${chalk14.green("\u2713")} Agent skills installed`);
|
|
9517
|
+
}
|
|
9518
|
+
const hasLearnableAgent = targetAgent.includes("claude") || targetAgent.includes("cursor");
|
|
9519
|
+
if (hasLearnableAgent) {
|
|
9520
|
+
if (targetAgent.includes("claude")) installLearningHooks();
|
|
9521
|
+
if (targetAgent.includes("cursor")) installCursorLearningHooks();
|
|
9522
|
+
console.log(` ${chalk14.green("\u2713")} Session learning enabled`);
|
|
9523
|
+
trackInitLearnEnabled(true);
|
|
9321
9524
|
}
|
|
9322
9525
|
console.log("");
|
|
9323
|
-
console.log(chalk14.dim(" New team members can run /setup-caliber inside their coding agent"));
|
|
9324
|
-
console.log(chalk14.dim(" (Claude Code or Cursor) to get set up automatically.\n"));
|
|
9325
9526
|
let baselineScore = computeLocalScore(process.cwd(), targetAgent);
|
|
9326
|
-
|
|
9327
|
-
displayScoreSummary(baselineScore);
|
|
9328
|
-
if (options.verbose) {
|
|
9329
|
-
for (const c of baselineScore.checks) {
|
|
9330
|
-
log(options.verbose, ` ${c.passed ? "\u2713" : "\u2717"} ${c.name}: ${c.earnedPoints}/${c.maxPoints}${c.suggestion ? ` \u2014 ${c.suggestion}` : ""}`);
|
|
9331
|
-
}
|
|
9332
|
-
}
|
|
9527
|
+
log(options.verbose, `Baseline score: ${baselineScore.score}/100`);
|
|
9333
9528
|
if (report) {
|
|
9334
9529
|
report.markStep("Baseline scoring");
|
|
9335
9530
|
report.addSection("Scoring: Baseline", `**Score**: ${baselineScore.score}/100
|
|
@@ -9350,34 +9545,23 @@ async function initCommand(options) {
|
|
|
9350
9545
|
]);
|
|
9351
9546
|
const passingCount = baselineScore.checks.filter((c) => c.passed).length;
|
|
9352
9547
|
const failingCount = baselineScore.checks.filter((c) => !c.passed).length;
|
|
9548
|
+
trackInitScoreComputed(baselineScore.score, passingCount, failingCount, false);
|
|
9353
9549
|
let skipGeneration = false;
|
|
9354
|
-
if (hasExistingConfig && baselineScore.score === 100) {
|
|
9355
|
-
|
|
9356
|
-
console.log(chalk14.bold.green("\n Your config is already optimal.\n"));
|
|
9357
|
-
skipGeneration = !options.force;
|
|
9550
|
+
if (hasExistingConfig && baselineScore.score === 100 && !options.force) {
|
|
9551
|
+
skipGeneration = true;
|
|
9358
9552
|
} else if (hasExistingConfig && !options.force && !options.autoApprove) {
|
|
9359
|
-
|
|
9360
|
-
|
|
9361
|
-
|
|
9362
|
-
|
|
9363
|
-
skipGeneration = auditAnswer.toLowerCase() === "n";
|
|
9364
|
-
} else if (!hasExistingConfig && !options.force && !options.autoApprove) {
|
|
9365
|
-
trackInitScoreComputed(baselineScore.score, passingCount, failingCount, false);
|
|
9366
|
-
console.log(chalk14.dim("\n Sync infrastructure is ready. Caliber can also generate tailored"));
|
|
9367
|
-
console.log(chalk14.dim(" CLAUDE.md, Cursor rules, and Codex configs for your project.\n"));
|
|
9368
|
-
const generateAnswer = await promptInput(" Generate agent configs? (Y/n) ");
|
|
9369
|
-
skipGeneration = generateAnswer.toLowerCase() === "n";
|
|
9370
|
-
} else {
|
|
9371
|
-
trackInitScoreComputed(baselineScore.score, passingCount, failingCount, false);
|
|
9553
|
+
console.log(chalk14.dim(` Config score: ${baselineScore.score}/100 \u2014 Caliber can improve this.
|
|
9554
|
+
`));
|
|
9555
|
+
const improveAnswer = await confirm2({ message: "Improve your existing configs?" });
|
|
9556
|
+
skipGeneration = !improveAnswer;
|
|
9372
9557
|
}
|
|
9373
9558
|
if (skipGeneration) {
|
|
9374
9559
|
const {
|
|
9375
|
-
|
|
9376
|
-
appendLearningsBlock: appendLearningsBlock2,
|
|
9377
|
-
appendSyncBlock: appendSyncBlock2,
|
|
9560
|
+
appendManagedBlocks: appendManagedBlocks2,
|
|
9378
9561
|
getCursorPreCommitRule: getCursorPreCommitRule2,
|
|
9379
9562
|
getCursorLearningsRule: getCursorLearningsRule2,
|
|
9380
|
-
getCursorSyncRule: getCursorSyncRule2
|
|
9563
|
+
getCursorSyncRule: getCursorSyncRule2,
|
|
9564
|
+
getCursorSetupRule: getCursorSetupRule2
|
|
9381
9565
|
} = await Promise.resolve().then(() => (init_pre_commit_block(), pre_commit_block_exports));
|
|
9382
9566
|
const claudeMdPath = "CLAUDE.md";
|
|
9383
9567
|
let claudeContent = "";
|
|
@@ -9389,7 +9573,7 @@ async function initCommand(options) {
|
|
|
9389
9573
|
claudeContent = `# ${path26.basename(process.cwd())}
|
|
9390
9574
|
`;
|
|
9391
9575
|
}
|
|
9392
|
-
const updatedClaude =
|
|
9576
|
+
const updatedClaude = appendManagedBlocks2(claudeContent, "claude");
|
|
9393
9577
|
if (updatedClaude !== claudeContent || !fs33.existsSync(claudeMdPath)) {
|
|
9394
9578
|
fs33.writeFileSync(claudeMdPath, updatedClaude);
|
|
9395
9579
|
console.log(` ${chalk14.green("\u2713")} CLAUDE.md \u2014 added Caliber sync instructions`);
|
|
@@ -9397,7 +9581,7 @@ async function initCommand(options) {
|
|
|
9397
9581
|
if (targetAgent.includes("cursor")) {
|
|
9398
9582
|
const rulesDir = path26.join(".cursor", "rules");
|
|
9399
9583
|
if (!fs33.existsSync(rulesDir)) fs33.mkdirSync(rulesDir, { recursive: true });
|
|
9400
|
-
for (const rule of [getCursorPreCommitRule2(), getCursorLearningsRule2(), getCursorSyncRule2()]) {
|
|
9584
|
+
for (const rule of [getCursorPreCommitRule2(), getCursorLearningsRule2(), getCursorSyncRule2(), getCursorSetupRule2()]) {
|
|
9401
9585
|
fs33.writeFileSync(path26.join(rulesDir, rule.filename), rule.content);
|
|
9402
9586
|
}
|
|
9403
9587
|
console.log(` ${chalk14.green("\u2713")} Cursor rules \u2014 added Caliber sync rules`);
|
|
@@ -9414,7 +9598,7 @@ async function initCommand(options) {
|
|
|
9414
9598
|
copilotContent = `# ${path26.basename(process.cwd())}
|
|
9415
9599
|
`;
|
|
9416
9600
|
}
|
|
9417
|
-
const updatedCopilot =
|
|
9601
|
+
const updatedCopilot = appendManagedBlocks2(copilotContent, "copilot");
|
|
9418
9602
|
if (updatedCopilot !== copilotContent) {
|
|
9419
9603
|
fs33.writeFileSync(copilotPath, updatedCopilot);
|
|
9420
9604
|
console.log(` ${chalk14.green("\u2713")} Copilot instructions \u2014 added Caliber sync instructions`);
|
|
@@ -9426,6 +9610,7 @@ async function initCommand(options) {
|
|
|
9426
9610
|
lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9427
9611
|
targetAgent
|
|
9428
9612
|
});
|
|
9613
|
+
trackInitCompleted("sync-only", baselineScore.score);
|
|
9429
9614
|
console.log(chalk14.bold.green("\n Caliber sync is set up!\n"));
|
|
9430
9615
|
console.log(chalk14.dim(" Your agent configs will sync automatically on every commit."));
|
|
9431
9616
|
console.log(chalk14.dim(" Run ") + title(`${bin} init --force`) + chalk14.dim(" anytime to generate or improve configs.\n"));
|
|
@@ -9433,7 +9618,6 @@ async function initCommand(options) {
|
|
|
9433
9618
|
}
|
|
9434
9619
|
const allFailingChecks = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0);
|
|
9435
9620
|
const llmFixableChecks = allFailingChecks.filter((c) => !NON_LLM_CHECKS.has(c.id));
|
|
9436
|
-
console.log(title.bold("\n Step 3/4 \u2014 Generate\n"));
|
|
9437
9621
|
const genModelInfo = fastModel ? ` Using ${displayModel} for docs, ${fastModel} for skills` : ` Using ${displayModel}`;
|
|
9438
9622
|
console.log(chalk14.dim(genModelInfo + "\n"));
|
|
9439
9623
|
if (report) report.markStep("Generation");
|
|
@@ -9632,7 +9816,7 @@ async function initCommand(options) {
|
|
|
9632
9816
|
report.addJson("Generation: Parsed Config", generatedSetup);
|
|
9633
9817
|
}
|
|
9634
9818
|
log(options.verbose, `Generation completed: ${elapsedMs}ms, stopReason: ${genStopReason || "end_turn"}`);
|
|
9635
|
-
console.log(title.bold(" Step
|
|
9819
|
+
console.log(title.bold(" Step 3/3 \u2014 Done\n"));
|
|
9636
9820
|
const setupFiles = collectSetupFiles(generatedSetup, targetAgent);
|
|
9637
9821
|
const staged = stageFiles(setupFiles, process.cwd());
|
|
9638
9822
|
const totalChanges = staged.newFiles + staged.modifiedFiles;
|
|
@@ -9776,6 +9960,7 @@ ${agentRefs.join(" ")}
|
|
|
9776
9960
|
` + afterScore.checks.map((c) => `| ${c.name} | ${c.passed ? "Yes" : "No"} | ${c.earnedPoints} | ${c.maxPoints} |`).join("\n"));
|
|
9777
9961
|
}
|
|
9778
9962
|
recordScore(afterScore, "init");
|
|
9963
|
+
trackInitCompleted("full-generation", afterScore.score);
|
|
9779
9964
|
displayScoreDelta(baselineScore, afterScore);
|
|
9780
9965
|
if (options.verbose) {
|
|
9781
9966
|
log(options.verbose, `Final score: ${afterScore.score}/100`);
|
|
@@ -9793,64 +9978,26 @@ ${agentRefs.join(" ")}
|
|
|
9793
9978
|
communitySkillsInstalled = selected.length;
|
|
9794
9979
|
}
|
|
9795
9980
|
}
|
|
9796
|
-
console.log("");
|
|
9797
|
-
console.log(` ${chalk14.green("\u2713")} Docs auto-refresh ${chalk14.dim(`agents run ${resolveCaliber()} refresh before commits`)}`);
|
|
9798
9981
|
trackInitHookSelected("config-instructions");
|
|
9799
|
-
const hasLearnableAgent = targetAgent.includes("claude") || targetAgent.includes("cursor");
|
|
9800
|
-
let enableLearn = false;
|
|
9801
|
-
if (hasLearnableAgent) {
|
|
9802
|
-
if (!options.autoApprove) {
|
|
9803
|
-
enableLearn = await promptLearnInstall(targetAgent);
|
|
9804
|
-
trackInitLearnEnabled(enableLearn);
|
|
9805
|
-
if (enableLearn) {
|
|
9806
|
-
if (targetAgent.includes("claude")) {
|
|
9807
|
-
const r = installLearningHooks();
|
|
9808
|
-
if (r.installed) console.log(` ${chalk14.green("\u2713")} Learning hooks installed for Claude Code`);
|
|
9809
|
-
else if (r.alreadyInstalled) console.log(chalk14.dim(" Claude Code learning hooks already installed"));
|
|
9810
|
-
}
|
|
9811
|
-
if (targetAgent.includes("cursor")) {
|
|
9812
|
-
const r = installCursorLearningHooks();
|
|
9813
|
-
if (r.installed) console.log(` ${chalk14.green("\u2713")} Learning hooks installed for Cursor`);
|
|
9814
|
-
else if (r.alreadyInstalled) console.log(chalk14.dim(" Cursor learning hooks already installed"));
|
|
9815
|
-
}
|
|
9816
|
-
console.log(chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} learn status`) + chalk14.dim(" to see insights"));
|
|
9817
|
-
} else {
|
|
9818
|
-
console.log(chalk14.dim(" Skipped. Run ") + chalk14.hex("#83D1EB")(`${bin} learn install`) + chalk14.dim(" later to enable."));
|
|
9819
|
-
}
|
|
9820
|
-
} else {
|
|
9821
|
-
enableLearn = true;
|
|
9822
|
-
if (targetAgent.includes("claude")) installLearningHooks();
|
|
9823
|
-
if (targetAgent.includes("cursor")) installCursorLearningHooks();
|
|
9824
|
-
}
|
|
9825
|
-
}
|
|
9826
|
-
console.log(chalk14.bold.green("\n Caliber is set up!"));
|
|
9827
|
-
console.log(chalk14.dim(" Your AI agent configs will now stay in sync with your codebase automatically."));
|
|
9828
|
-
console.log(chalk14.dim(" Every commit refreshes configs for all your agents. All changes are backed up.\n"));
|
|
9829
9982
|
const done = chalk14.green("\u2713");
|
|
9830
|
-
|
|
9831
|
-
console.log(chalk14.bold(" What
|
|
9832
|
-
console.log(` ${done} Continuous sync
|
|
9833
|
-
console.log(` ${done} Config generated
|
|
9834
|
-
console.log(` ${done} Agent skills
|
|
9983
|
+
console.log(chalk14.bold.green("\n Caliber is set up!\n"));
|
|
9984
|
+
console.log(chalk14.bold(" What's configured:\n"));
|
|
9985
|
+
console.log(` ${done} Continuous sync ${chalk14.dim("pre-commit hook keeps all agent configs in sync")}`);
|
|
9986
|
+
console.log(` ${done} Config generated ${chalk14.dim(`score: ${afterScore.score}/100`)}`);
|
|
9987
|
+
console.log(` ${done} Agent skills ${chalk14.dim("/setup-caliber for new team members")}`);
|
|
9835
9988
|
if (hasLearnableAgent) {
|
|
9836
|
-
|
|
9837
|
-
console.log(` ${done} Session learning ${chalk14.dim("agent learns from your feedback")}`);
|
|
9838
|
-
} else {
|
|
9839
|
-
console.log(` ${skip} Session learning ${title(`${bin} learn install`)} ${chalk14.dim("to enable later")}`);
|
|
9840
|
-
}
|
|
9989
|
+
console.log(` ${done} Session learning ${chalk14.dim("learns from your corrections")}`);
|
|
9841
9990
|
}
|
|
9842
9991
|
if (communitySkillsInstalled > 0) {
|
|
9843
|
-
console.log(` ${done} Community skills
|
|
9844
|
-
} else if (skillSearchResult.results.length > 0) {
|
|
9845
|
-
console.log(` ${skip} Community skills ${chalk14.dim("available but skipped")}`);
|
|
9992
|
+
console.log(` ${done} Community skills ${chalk14.dim(`${communitySkillsInstalled} installed for your stack`)}`);
|
|
9846
9993
|
}
|
|
9847
9994
|
console.log(chalk14.bold("\n What happens next:\n"));
|
|
9848
|
-
console.log(chalk14.dim(" Every commit
|
|
9849
|
-
console.log(chalk14.dim(" New team members
|
|
9850
|
-
console.log(
|
|
9851
|
-
console.log(` ${title(`${bin}
|
|
9852
|
-
console.log(` ${title(`${bin}
|
|
9853
|
-
console.log(` ${title(`${bin}
|
|
9995
|
+
console.log(chalk14.dim(" Every commit syncs your agent configs automatically."));
|
|
9996
|
+
console.log(chalk14.dim(" New team members run /setup-caliber to get set up instantly.\n"));
|
|
9997
|
+
console.log(` ${title(`${bin} score`)} Full scoring breakdown`);
|
|
9998
|
+
console.log(` ${title(`${bin} skills`)} Find community skills`);
|
|
9999
|
+
console.log(` ${title(`${bin} undo`)} Revert changes`);
|
|
10000
|
+
console.log(` ${title(`${bin} uninstall`)} Remove Caliber completely`);
|
|
9854
10001
|
console.log("");
|
|
9855
10002
|
if (options.showTokens) {
|
|
9856
10003
|
displayTokenUsage();
|
|
@@ -10193,14 +10340,24 @@ var MAX_DIFF_BYTES = 1e5;
|
|
|
10193
10340
|
var DOC_PATTERNS = [
|
|
10194
10341
|
"CLAUDE.md",
|
|
10195
10342
|
"README.md",
|
|
10343
|
+
"AGENTS.md",
|
|
10196
10344
|
".cursorrules",
|
|
10197
10345
|
".cursor/rules/",
|
|
10346
|
+
".cursor/skills/",
|
|
10198
10347
|
".claude/skills/",
|
|
10348
|
+
".agents/skills/",
|
|
10349
|
+
".github/copilot-instructions.md",
|
|
10350
|
+
".github/instructions/",
|
|
10199
10351
|
"CALIBER_LEARNINGS.md"
|
|
10200
10352
|
];
|
|
10201
10353
|
function excludeArgs() {
|
|
10202
10354
|
return DOC_PATTERNS.flatMap((p) => ["--", `:!${p}`]);
|
|
10203
10355
|
}
|
|
10356
|
+
function truncateAtLine(text, maxBytes) {
|
|
10357
|
+
if (text.length <= maxBytes) return text;
|
|
10358
|
+
const cut = text.lastIndexOf("\n", maxBytes);
|
|
10359
|
+
return cut > 0 ? text.slice(0, cut) : text.slice(0, maxBytes);
|
|
10360
|
+
}
|
|
10204
10361
|
function safeExec(cmd) {
|
|
10205
10362
|
try {
|
|
10206
10363
|
return execSync15(cmd, {
|
|
@@ -10246,9 +10403,9 @@ function collectDiff(lastSha) {
|
|
|
10246
10403
|
const totalSize = committedDiff.length + stagedDiff.length + unstagedDiff.length;
|
|
10247
10404
|
if (totalSize > MAX_DIFF_BYTES) {
|
|
10248
10405
|
const ratio = MAX_DIFF_BYTES / totalSize;
|
|
10249
|
-
committedDiff = committedDiff
|
|
10250
|
-
stagedDiff = stagedDiff
|
|
10251
|
-
unstagedDiff = unstagedDiff
|
|
10406
|
+
committedDiff = truncateAtLine(committedDiff, Math.floor(committedDiff.length * ratio));
|
|
10407
|
+
stagedDiff = truncateAtLine(stagedDiff, Math.floor(stagedDiff.length * ratio));
|
|
10408
|
+
unstagedDiff = truncateAtLine(unstagedDiff, Math.floor(unstagedDiff.length * ratio));
|
|
10252
10409
|
}
|
|
10253
10410
|
const hasChanges = !!(committedDiff || stagedDiff || unstagedDiff || changedFiles.length);
|
|
10254
10411
|
const parts = [];
|
|
@@ -10267,13 +10424,17 @@ import path28 from "path";
|
|
|
10267
10424
|
function writeRefreshDocs(docs) {
|
|
10268
10425
|
const written = [];
|
|
10269
10426
|
if (docs.claudeMd) {
|
|
10270
|
-
fs36.writeFileSync("CLAUDE.md",
|
|
10427
|
+
fs36.writeFileSync("CLAUDE.md", appendManagedBlocks(docs.claudeMd, "claude"));
|
|
10271
10428
|
written.push("CLAUDE.md");
|
|
10272
10429
|
}
|
|
10273
10430
|
if (docs.readmeMd) {
|
|
10274
10431
|
fs36.writeFileSync("README.md", docs.readmeMd);
|
|
10275
10432
|
written.push("README.md");
|
|
10276
10433
|
}
|
|
10434
|
+
if (docs.agentsMd) {
|
|
10435
|
+
fs36.writeFileSync("AGENTS.md", appendManagedBlocks(docs.agentsMd, "codex"));
|
|
10436
|
+
written.push("AGENTS.md");
|
|
10437
|
+
}
|
|
10277
10438
|
if (docs.cursorrules) {
|
|
10278
10439
|
fs36.writeFileSync(".cursorrules", docs.cursorrules);
|
|
10279
10440
|
written.push(".cursorrules");
|
|
@@ -10296,7 +10457,7 @@ function writeRefreshDocs(docs) {
|
|
|
10296
10457
|
}
|
|
10297
10458
|
if (docs.copilotInstructions) {
|
|
10298
10459
|
fs36.mkdirSync(".github", { recursive: true });
|
|
10299
|
-
fs36.writeFileSync(path28.join(".github", "copilot-instructions.md"),
|
|
10460
|
+
fs36.writeFileSync(path28.join(".github", "copilot-instructions.md"), appendManagedBlocks(docs.copilotInstructions, "copilot"));
|
|
10300
10461
|
written.push(".github/copilot-instructions.md");
|
|
10301
10462
|
}
|
|
10302
10463
|
if (docs.copilotInstructionFiles) {
|
|
@@ -10315,10 +10476,18 @@ init_config();
|
|
|
10315
10476
|
async function refreshDocs(diff, existingDocs, projectContext, learnedSection, sources2) {
|
|
10316
10477
|
const prompt = buildRefreshPrompt(diff, existingDocs, projectContext, learnedSection, sources2);
|
|
10317
10478
|
const fastModel = getFastModel();
|
|
10479
|
+
const docCount = [
|
|
10480
|
+
existingDocs.claudeMd,
|
|
10481
|
+
existingDocs.readmeMd,
|
|
10482
|
+
existingDocs.agentsMd,
|
|
10483
|
+
existingDocs.cursorrules,
|
|
10484
|
+
existingDocs.copilotInstructions
|
|
10485
|
+
].filter(Boolean).length + (existingDocs.cursorRules?.length ?? 0) + (existingDocs.claudeSkills?.length ?? 0);
|
|
10486
|
+
const maxTokens = Math.min(32768, Math.max(8192, docCount * 4096));
|
|
10318
10487
|
const raw = await llmCall({
|
|
10319
10488
|
system: REFRESH_SYSTEM_PROMPT,
|
|
10320
10489
|
prompt,
|
|
10321
|
-
maxTokens
|
|
10490
|
+
maxTokens,
|
|
10322
10491
|
...fastModel ? { model: fastModel } : {}
|
|
10323
10492
|
});
|
|
10324
10493
|
return parseJsonResponse(raw);
|
|
@@ -10377,6 +10546,21 @@ Changed files: ${diff.changedFiles.join(", ")}`);
|
|
|
10377
10546
|
parts.push(rule.content);
|
|
10378
10547
|
}
|
|
10379
10548
|
}
|
|
10549
|
+
if (existingDocs.agentsMd) {
|
|
10550
|
+
parts.push("\n[AGENTS.md]");
|
|
10551
|
+
parts.push(existingDocs.agentsMd);
|
|
10552
|
+
}
|
|
10553
|
+
if (existingDocs.copilotInstructions) {
|
|
10554
|
+
parts.push("\n[.github/copilot-instructions.md]");
|
|
10555
|
+
parts.push(existingDocs.copilotInstructions);
|
|
10556
|
+
}
|
|
10557
|
+
if (existingDocs.copilotInstructionFiles?.length) {
|
|
10558
|
+
for (const file of existingDocs.copilotInstructionFiles) {
|
|
10559
|
+
parts.push(`
|
|
10560
|
+
[.github/instructions/${file.filename}]`);
|
|
10561
|
+
parts.push(file.content);
|
|
10562
|
+
}
|
|
10563
|
+
}
|
|
10380
10564
|
if (learnedSection) {
|
|
10381
10565
|
parts.push("\n--- Learned Patterns (from session learning) ---");
|
|
10382
10566
|
parts.push("Consider these accumulated learnings when deciding what to update:");
|
|
@@ -10699,7 +10883,8 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
10699
10883
|
}
|
|
10700
10884
|
}
|
|
10701
10885
|
const written = writeRefreshDocs(response.updatedDocs);
|
|
10702
|
-
|
|
10886
|
+
const trigger = quiet ? "hook" : "manual";
|
|
10887
|
+
trackRefreshCompleted(written.length, Date.now(), trigger);
|
|
10703
10888
|
const postScore = computeLocalScore(repoDir, targetAgent);
|
|
10704
10889
|
if (postScore.score < preScore.score) {
|
|
10705
10890
|
for (const [filePath, content] of preRefreshContents) {
|
|
@@ -12403,10 +12588,199 @@ async function publishCommand() {
|
|
|
12403
12588
|
}
|
|
12404
12589
|
}
|
|
12405
12590
|
|
|
12591
|
+
// src/commands/bootstrap.ts
|
|
12592
|
+
init_builtin_skills();
|
|
12593
|
+
import fs47 from "fs";
|
|
12594
|
+
import chalk27 from "chalk";
|
|
12595
|
+
var PLATFORM_SKILL_DIRS = {
|
|
12596
|
+
claude: ".claude/skills",
|
|
12597
|
+
cursor: ".cursor/skills",
|
|
12598
|
+
codex: ".agents/skills"
|
|
12599
|
+
};
|
|
12600
|
+
async function bootstrapCommand() {
|
|
12601
|
+
const platforms = detectPlatforms();
|
|
12602
|
+
const detected = [];
|
|
12603
|
+
if (platforms.claude) detected.push("claude");
|
|
12604
|
+
if (platforms.cursor) detected.push("cursor");
|
|
12605
|
+
if (platforms.codex) detected.push("codex");
|
|
12606
|
+
if (detected.length === 0) detected.push("claude");
|
|
12607
|
+
const written = [];
|
|
12608
|
+
for (const platform of detected) {
|
|
12609
|
+
const skillsDir = PLATFORM_SKILL_DIRS[platform];
|
|
12610
|
+
if (!skillsDir) continue;
|
|
12611
|
+
for (const skill of BUILTIN_SKILLS) {
|
|
12612
|
+
const skillDir = `${skillsDir}/${skill.name}`;
|
|
12613
|
+
const skillPath = `${skillDir}/SKILL.md`;
|
|
12614
|
+
fs47.mkdirSync(skillDir, { recursive: true });
|
|
12615
|
+
fs47.writeFileSync(skillPath, buildSkillContent(skill));
|
|
12616
|
+
written.push(skillPath);
|
|
12617
|
+
}
|
|
12618
|
+
}
|
|
12619
|
+
if (written.length === 0) {
|
|
12620
|
+
console.log(chalk27.yellow("No skills were written."));
|
|
12621
|
+
return;
|
|
12622
|
+
}
|
|
12623
|
+
console.log(chalk27.bold.green("\n Caliber skills installed!\n"));
|
|
12624
|
+
for (const file of written) {
|
|
12625
|
+
console.log(` ${chalk27.green("\u2713")} ${file}`);
|
|
12626
|
+
}
|
|
12627
|
+
console.log(chalk27.dim("\n Your agent can now run /setup-caliber to complete the setup."));
|
|
12628
|
+
console.log(chalk27.dim(' Just tell your agent: "Run /setup-caliber"\n'));
|
|
12629
|
+
}
|
|
12630
|
+
|
|
12631
|
+
// src/commands/uninstall.ts
|
|
12632
|
+
import fs48 from "fs";
|
|
12633
|
+
import path38 from "path";
|
|
12634
|
+
import chalk28 from "chalk";
|
|
12635
|
+
import confirm3 from "@inquirer/confirm";
|
|
12636
|
+
init_pre_commit_block();
|
|
12637
|
+
init_builtin_skills();
|
|
12638
|
+
init_config();
|
|
12639
|
+
var MANAGED_DOC_FILES = [
|
|
12640
|
+
"CLAUDE.md",
|
|
12641
|
+
path38.join(".github", "copilot-instructions.md"),
|
|
12642
|
+
"AGENTS.md"
|
|
12643
|
+
];
|
|
12644
|
+
var SKILL_DIRS = PLATFORM_CONFIGS.map((c) => c.skillsDir);
|
|
12645
|
+
var CURSOR_RULES_DIR = path38.join(".cursor", "rules");
|
|
12646
|
+
function removeCaliberCursorRules() {
|
|
12647
|
+
const removed = [];
|
|
12648
|
+
if (!fs48.existsSync(CURSOR_RULES_DIR)) return removed;
|
|
12649
|
+
for (const file of fs48.readdirSync(CURSOR_RULES_DIR)) {
|
|
12650
|
+
if (file.startsWith("caliber-") && file.endsWith(".mdc")) {
|
|
12651
|
+
const fullPath = path38.join(CURSOR_RULES_DIR, file);
|
|
12652
|
+
fs48.unlinkSync(fullPath);
|
|
12653
|
+
removed.push(fullPath);
|
|
12654
|
+
}
|
|
12655
|
+
}
|
|
12656
|
+
return removed;
|
|
12657
|
+
}
|
|
12658
|
+
function removeBuiltinSkills() {
|
|
12659
|
+
const removed = [];
|
|
12660
|
+
for (const skillsDir of SKILL_DIRS) {
|
|
12661
|
+
if (!fs48.existsSync(skillsDir)) continue;
|
|
12662
|
+
for (const name of BUILTIN_SKILL_NAMES) {
|
|
12663
|
+
const skillDir = path38.join(skillsDir, name);
|
|
12664
|
+
if (fs48.existsSync(skillDir)) {
|
|
12665
|
+
fs48.rmSync(skillDir, { recursive: true });
|
|
12666
|
+
removed.push(skillDir);
|
|
12667
|
+
}
|
|
12668
|
+
}
|
|
12669
|
+
}
|
|
12670
|
+
return removed;
|
|
12671
|
+
}
|
|
12672
|
+
function stripManagedBlocksFromFiles() {
|
|
12673
|
+
const modified = [];
|
|
12674
|
+
for (const filePath of MANAGED_DOC_FILES) {
|
|
12675
|
+
if (!fs48.existsSync(filePath)) continue;
|
|
12676
|
+
const original = fs48.readFileSync(filePath, "utf-8");
|
|
12677
|
+
const stripped = stripManagedBlocks(original);
|
|
12678
|
+
if (stripped !== original) {
|
|
12679
|
+
const trimmed = stripped.trim();
|
|
12680
|
+
if (!trimmed || /^#\s*\S*$/.test(trimmed)) {
|
|
12681
|
+
fs48.unlinkSync(filePath);
|
|
12682
|
+
} else {
|
|
12683
|
+
fs48.writeFileSync(filePath, stripped);
|
|
12684
|
+
}
|
|
12685
|
+
modified.push(filePath);
|
|
12686
|
+
}
|
|
12687
|
+
}
|
|
12688
|
+
return modified;
|
|
12689
|
+
}
|
|
12690
|
+
function removeDirectory(dir) {
|
|
12691
|
+
if (!fs48.existsSync(dir)) return false;
|
|
12692
|
+
fs48.rmSync(dir, { recursive: true });
|
|
12693
|
+
return true;
|
|
12694
|
+
}
|
|
12695
|
+
async function uninstallCommand(options) {
|
|
12696
|
+
console.log(chalk28.bold("\n Caliber Uninstall\n"));
|
|
12697
|
+
console.log(chalk28.dim(" This will remove all Caliber resources from this project:\n"));
|
|
12698
|
+
console.log(chalk28.dim(" \u2022 Pre-commit hook"));
|
|
12699
|
+
console.log(chalk28.dim(" \u2022 Session learning hooks"));
|
|
12700
|
+
console.log(chalk28.dim(" \u2022 Managed blocks in CLAUDE.md, AGENTS.md, copilot-instructions.md"));
|
|
12701
|
+
console.log(chalk28.dim(" \u2022 Cursor rules (caliber-*.mdc)"));
|
|
12702
|
+
console.log(chalk28.dim(" \u2022 Built-in skills (setup-caliber, find-skills, save-learning)"));
|
|
12703
|
+
console.log(chalk28.dim(" \u2022 CALIBER_LEARNINGS.md"));
|
|
12704
|
+
console.log(chalk28.dim(" \u2022 .caliber/ directory (backups, cache, state)\n"));
|
|
12705
|
+
if (!options.force) {
|
|
12706
|
+
const proceed = await confirm3({ message: "Continue with uninstall?" });
|
|
12707
|
+
if (!proceed) {
|
|
12708
|
+
console.log(chalk28.dim("\n Cancelled.\n"));
|
|
12709
|
+
return;
|
|
12710
|
+
}
|
|
12711
|
+
}
|
|
12712
|
+
console.log("");
|
|
12713
|
+
const actions = [];
|
|
12714
|
+
const hookResult = removePreCommitHook();
|
|
12715
|
+
if (hookResult.removed) {
|
|
12716
|
+
console.log(` ${chalk28.red("\u2717")} Pre-commit hook removed`);
|
|
12717
|
+
actions.push("pre-commit hook");
|
|
12718
|
+
}
|
|
12719
|
+
const learnResult = removeLearningHooks();
|
|
12720
|
+
if (learnResult.removed) {
|
|
12721
|
+
console.log(` ${chalk28.red("\u2717")} Claude Code learning hooks removed`);
|
|
12722
|
+
actions.push("claude learning hooks");
|
|
12723
|
+
}
|
|
12724
|
+
const cursorLearnResult = removeCursorLearningHooks();
|
|
12725
|
+
if (cursorLearnResult.removed) {
|
|
12726
|
+
console.log(` ${chalk28.red("\u2717")} Cursor learning hooks removed`);
|
|
12727
|
+
actions.push("cursor learning hooks");
|
|
12728
|
+
}
|
|
12729
|
+
const strippedFiles = stripManagedBlocksFromFiles();
|
|
12730
|
+
for (const file of strippedFiles) {
|
|
12731
|
+
console.log(` ${chalk28.yellow("~")} ${file} \u2014 managed blocks removed`);
|
|
12732
|
+
actions.push(file);
|
|
12733
|
+
}
|
|
12734
|
+
const removedRules = removeCaliberCursorRules();
|
|
12735
|
+
for (const rule of removedRules) {
|
|
12736
|
+
console.log(` ${chalk28.red("\u2717")} ${rule}`);
|
|
12737
|
+
}
|
|
12738
|
+
if (removedRules.length > 0) actions.push("cursor rules");
|
|
12739
|
+
const removedSkills = removeBuiltinSkills();
|
|
12740
|
+
for (const skill of removedSkills) {
|
|
12741
|
+
console.log(` ${chalk28.red("\u2717")} ${skill}/`);
|
|
12742
|
+
}
|
|
12743
|
+
if (removedSkills.length > 0) actions.push("builtin skills");
|
|
12744
|
+
if (fs48.existsSync("CALIBER_LEARNINGS.md")) {
|
|
12745
|
+
fs48.unlinkSync("CALIBER_LEARNINGS.md");
|
|
12746
|
+
console.log(` ${chalk28.red("\u2717")} CALIBER_LEARNINGS.md`);
|
|
12747
|
+
actions.push("learnings file");
|
|
12748
|
+
}
|
|
12749
|
+
if (removeDirectory(CALIBER_DIR)) {
|
|
12750
|
+
console.log(` ${chalk28.red("\u2717")} .caliber/ directory`);
|
|
12751
|
+
actions.push(".caliber directory");
|
|
12752
|
+
}
|
|
12753
|
+
if (actions.length === 0) {
|
|
12754
|
+
console.log(chalk28.dim(" Nothing to remove \u2014 Caliber is not installed in this project.\n"));
|
|
12755
|
+
return;
|
|
12756
|
+
}
|
|
12757
|
+
trackUninstallExecuted();
|
|
12758
|
+
const configPath = getConfigFilePath();
|
|
12759
|
+
if (fs48.existsSync(configPath)) {
|
|
12760
|
+
console.log("");
|
|
12761
|
+
const removeConfig = options.force || await confirm3({
|
|
12762
|
+
message: `Remove global config (~/.caliber/config.json)? This affects all projects.`
|
|
12763
|
+
});
|
|
12764
|
+
if (removeConfig) {
|
|
12765
|
+
fs48.unlinkSync(configPath);
|
|
12766
|
+
console.log(` ${chalk28.red("\u2717")} ${configPath}`);
|
|
12767
|
+
const configDir = path38.dirname(configPath);
|
|
12768
|
+
try {
|
|
12769
|
+
const remaining = fs48.readdirSync(configDir);
|
|
12770
|
+
if (remaining.length === 0) fs48.rmdirSync(configDir);
|
|
12771
|
+
} catch {
|
|
12772
|
+
}
|
|
12773
|
+
}
|
|
12774
|
+
}
|
|
12775
|
+
console.log(chalk28.bold.green(`
|
|
12776
|
+
Caliber has been removed from this project.`));
|
|
12777
|
+
console.log(chalk28.dim(" Your code is untouched \u2014 only Caliber config files were removed.\n"));
|
|
12778
|
+
}
|
|
12779
|
+
|
|
12406
12780
|
// src/cli.ts
|
|
12407
|
-
var __dirname =
|
|
12781
|
+
var __dirname = path39.dirname(fileURLToPath(import.meta.url));
|
|
12408
12782
|
var pkg = JSON.parse(
|
|
12409
|
-
|
|
12783
|
+
fs49.readFileSync(path39.resolve(__dirname, "..", "package.json"), "utf-8")
|
|
12410
12784
|
);
|
|
12411
12785
|
var program = new Command();
|
|
12412
12786
|
var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
|
|
@@ -12414,9 +12788,11 @@ program.name(process.env.CALIBER_LOCAL ? "caloc" : "caliber").description("AI co
|
|
|
12414
12788
|
function tracked(commandName, handler) {
|
|
12415
12789
|
const wrapper = async (...args) => {
|
|
12416
12790
|
const start = Date.now();
|
|
12791
|
+
const isCI = !!(process.env.CI || process.env.GITHUB_ACTIONS);
|
|
12417
12792
|
trackEvent("command_started", {
|
|
12418
12793
|
command: commandName,
|
|
12419
|
-
cli_version: pkg.version
|
|
12794
|
+
cli_version: pkg.version,
|
|
12795
|
+
is_ci: isCI
|
|
12420
12796
|
});
|
|
12421
12797
|
try {
|
|
12422
12798
|
await handler(...args);
|
|
@@ -12468,7 +12844,9 @@ function parseAgentOption(value) {
|
|
|
12468
12844
|
return agents;
|
|
12469
12845
|
}
|
|
12470
12846
|
program.command("init").description("Initialize your project for AI-assisted development").option("--agent <type>", "Target agents (comma-separated): claude, cursor, codex, github-copilot", parseAgentOption).option("--source <paths...>", "Related source paths to include as context").option("--dry-run", "Preview changes without writing files").option("--force", "Overwrite existing config without prompting").option("--debug-report", void 0, false).option("--show-tokens", "Show token usage summary at the end").option("--auto-approve", "Run without interactive prompts (auto-accept all)").option("--verbose", "Show detailed logs of each step").action(tracked("init", initCommand));
|
|
12847
|
+
program.command("bootstrap").description("Install agent skills (/setup-caliber, /find-skills, /save-learning) without running init").action(tracked("bootstrap", bootstrapCommand));
|
|
12471
12848
|
program.command("undo").description("Revert all config changes made by Caliber").action(tracked("undo", undoCommand));
|
|
12849
|
+
program.command("uninstall").description("Remove all Caliber resources from this project").option("--force", "Skip confirmation prompt").action(tracked("uninstall", (options) => uninstallCommand(options)));
|
|
12472
12850
|
program.command("status").description("Show current Caliber config status").option("--json", "Output as JSON").action(tracked("status", statusCommand));
|
|
12473
12851
|
program.command("regenerate").alias("regen").alias("re").description("Re-analyze project and regenerate config").option("--dry-run", "Preview changes without writing files").action(tracked("regenerate", regenerateCommand));
|
|
12474
12852
|
program.command("config").description("Configure LLM provider, API key, and model").action(tracked("config", configCommand));
|
|
@@ -12493,16 +12871,16 @@ learn.command("delete <index>").description("Delete a learning by its index numb
|
|
|
12493
12871
|
learn.command("add <content>").description("Add a learning directly (used by agent skills)").option("--personal", "Save as a personal learning instead of project-level").action(tracked("learn:add", learnAddCommand));
|
|
12494
12872
|
|
|
12495
12873
|
// src/utils/version-check.ts
|
|
12496
|
-
import
|
|
12497
|
-
import
|
|
12874
|
+
import fs50 from "fs";
|
|
12875
|
+
import path40 from "path";
|
|
12498
12876
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
12499
12877
|
import { execSync as execSync16, execFileSync as execFileSync3 } from "child_process";
|
|
12500
|
-
import
|
|
12878
|
+
import chalk29 from "chalk";
|
|
12501
12879
|
import ora8 from "ora";
|
|
12502
|
-
import
|
|
12503
|
-
var __dirname_vc =
|
|
12880
|
+
import confirm4 from "@inquirer/confirm";
|
|
12881
|
+
var __dirname_vc = path40.dirname(fileURLToPath2(import.meta.url));
|
|
12504
12882
|
var pkg2 = JSON.parse(
|
|
12505
|
-
|
|
12883
|
+
fs50.readFileSync(path40.resolve(__dirname_vc, "..", "package.json"), "utf-8")
|
|
12506
12884
|
);
|
|
12507
12885
|
function getChannel(version) {
|
|
12508
12886
|
const match = version.match(/-(dev|next)\./);
|
|
@@ -12527,8 +12905,8 @@ function isNewer(registry, current) {
|
|
|
12527
12905
|
function getInstalledVersion() {
|
|
12528
12906
|
try {
|
|
12529
12907
|
const globalRoot = execSync16("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
12530
|
-
const pkgPath =
|
|
12531
|
-
return JSON.parse(
|
|
12908
|
+
const pkgPath = path40.join(globalRoot, "@rely-ai", "caliber", "package.json");
|
|
12909
|
+
return JSON.parse(fs50.readFileSync(pkgPath, "utf-8")).version;
|
|
12532
12910
|
} catch {
|
|
12533
12911
|
return null;
|
|
12534
12912
|
}
|
|
@@ -12553,20 +12931,20 @@ async function checkForUpdates() {
|
|
|
12553
12931
|
if (!isInteractive) {
|
|
12554
12932
|
const installTag = channel === "latest" ? "" : `@${channel}`;
|
|
12555
12933
|
console.log(
|
|
12556
|
-
|
|
12934
|
+
chalk29.yellow(
|
|
12557
12935
|
`
|
|
12558
12936
|
Update available: ${current} -> ${latest}
|
|
12559
|
-
Run ${
|
|
12937
|
+
Run ${chalk29.bold(`npm install -g @rely-ai/caliber${installTag}`)} to upgrade.
|
|
12560
12938
|
`
|
|
12561
12939
|
)
|
|
12562
12940
|
);
|
|
12563
12941
|
return;
|
|
12564
12942
|
}
|
|
12565
12943
|
console.log(
|
|
12566
|
-
|
|
12944
|
+
chalk29.yellow(`
|
|
12567
12945
|
Update available: ${current} -> ${latest}`)
|
|
12568
12946
|
);
|
|
12569
|
-
const shouldUpdate = await
|
|
12947
|
+
const shouldUpdate = await confirm4({ message: "Would you like to update now? (Y/n)", default: true });
|
|
12570
12948
|
if (!shouldUpdate) {
|
|
12571
12949
|
console.log();
|
|
12572
12950
|
return;
|
|
@@ -12583,13 +12961,13 @@ Update available: ${current} -> ${latest}`)
|
|
|
12583
12961
|
const installed = getInstalledVersion();
|
|
12584
12962
|
if (installed !== latest) {
|
|
12585
12963
|
spinner.fail(`Update incomplete \u2014 got ${installed ?? "unknown"}, expected ${latest}`);
|
|
12586
|
-
console.log(
|
|
12964
|
+
console.log(chalk29.yellow(`Run ${chalk29.bold(`npm install -g @rely-ai/caliber@${tag}`)} manually.
|
|
12587
12965
|
`));
|
|
12588
12966
|
return;
|
|
12589
12967
|
}
|
|
12590
|
-
spinner.succeed(
|
|
12968
|
+
spinner.succeed(chalk29.green(`Updated to ${latest}`));
|
|
12591
12969
|
const args = process.argv.slice(2);
|
|
12592
|
-
console.log(
|
|
12970
|
+
console.log(chalk29.dim(`
|
|
12593
12971
|
Restarting: caliber ${args.join(" ")}
|
|
12594
12972
|
`));
|
|
12595
12973
|
execFileSync3("caliber", args, {
|
|
@@ -12602,11 +12980,11 @@ Restarting: caliber ${args.join(" ")}
|
|
|
12602
12980
|
if (err instanceof Error) {
|
|
12603
12981
|
const stderr = err.stderr;
|
|
12604
12982
|
const errMsg = stderr ? String(stderr).trim().split("\n").pop() : err.message.split("\n")[0];
|
|
12605
|
-
if (errMsg && !errMsg.includes("SIGTERM")) console.log(
|
|
12983
|
+
if (errMsg && !errMsg.includes("SIGTERM")) console.log(chalk29.dim(` ${errMsg}`));
|
|
12606
12984
|
}
|
|
12607
12985
|
console.log(
|
|
12608
|
-
|
|
12609
|
-
`Run ${
|
|
12986
|
+
chalk29.yellow(
|
|
12987
|
+
`Run ${chalk29.bold(`npm install -g @rely-ai/caliber@${tag}`)} manually to upgrade.
|
|
12610
12988
|
`
|
|
12611
12989
|
)
|
|
12612
12990
|
);
|