@rely-ai/caliber 1.25.1 → 1.26.0-dev.1773936625
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 +586 -308
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -230,7 +230,7 @@ __export(resolve_caliber_exports, {
|
|
|
230
230
|
isCaliberCommand: () => isCaliberCommand,
|
|
231
231
|
resolveCaliber: () => resolveCaliber
|
|
232
232
|
});
|
|
233
|
-
import
|
|
233
|
+
import fs19 from "fs";
|
|
234
234
|
import { execSync as execSync6 } from "child_process";
|
|
235
235
|
function resolveCaliber() {
|
|
236
236
|
if (_resolved) return _resolved;
|
|
@@ -252,7 +252,7 @@ function resolveCaliber() {
|
|
|
252
252
|
} catch {
|
|
253
253
|
}
|
|
254
254
|
const binPath = process.argv[1];
|
|
255
|
-
if (binPath &&
|
|
255
|
+
if (binPath && fs19.existsSync(binPath)) {
|
|
256
256
|
_resolved = binPath;
|
|
257
257
|
return _resolved;
|
|
258
258
|
}
|
|
@@ -276,13 +276,13 @@ var init_resolve_caliber = __esm({
|
|
|
276
276
|
|
|
277
277
|
// src/utils/editor.ts
|
|
278
278
|
import { execSync as execSync12, spawn as spawn3 } from "child_process";
|
|
279
|
-
import
|
|
280
|
-
import
|
|
279
|
+
import fs26 from "fs";
|
|
280
|
+
import path22 from "path";
|
|
281
281
|
import os5 from "os";
|
|
282
282
|
function getEmptyFilePath(proposedPath) {
|
|
283
|
-
|
|
284
|
-
const tempPath =
|
|
285
|
-
|
|
283
|
+
fs26.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
|
|
284
|
+
const tempPath = path22.join(DIFF_TEMP_DIR, path22.basename(proposedPath));
|
|
285
|
+
fs26.writeFileSync(tempPath, "");
|
|
286
286
|
return tempPath;
|
|
287
287
|
}
|
|
288
288
|
function commandExists(cmd) {
|
|
@@ -322,7 +322,7 @@ var init_editor = __esm({
|
|
|
322
322
|
"src/utils/editor.ts"() {
|
|
323
323
|
"use strict";
|
|
324
324
|
IS_WINDOWS3 = process.platform === "win32";
|
|
325
|
-
DIFF_TEMP_DIR =
|
|
325
|
+
DIFF_TEMP_DIR = path22.join(os5.tmpdir(), "caliber-diff");
|
|
326
326
|
}
|
|
327
327
|
});
|
|
328
328
|
|
|
@@ -334,7 +334,7 @@ __export(review_exports, {
|
|
|
334
334
|
promptWantsReview: () => promptWantsReview
|
|
335
335
|
});
|
|
336
336
|
import chalk10 from "chalk";
|
|
337
|
-
import
|
|
337
|
+
import fs27 from "fs";
|
|
338
338
|
import select4 from "@inquirer/select";
|
|
339
339
|
import { createTwoFilesPatch } from "diff";
|
|
340
340
|
async function promptWantsReview() {
|
|
@@ -371,8 +371,8 @@ async function openReview(method, stagedFiles) {
|
|
|
371
371
|
return;
|
|
372
372
|
}
|
|
373
373
|
const fileInfos = stagedFiles.map((file) => {
|
|
374
|
-
const proposed =
|
|
375
|
-
const current = file.currentPath ?
|
|
374
|
+
const proposed = fs27.readFileSync(file.proposedPath, "utf-8");
|
|
375
|
+
const current = file.currentPath ? fs27.readFileSync(file.currentPath, "utf-8") : "";
|
|
376
376
|
const patch = createTwoFilesPatch(
|
|
377
377
|
file.isNew ? "/dev/null" : file.relativePath,
|
|
378
378
|
file.relativePath,
|
|
@@ -547,13 +547,13 @@ __export(lock_exports, {
|
|
|
547
547
|
isCaliberRunning: () => isCaliberRunning,
|
|
548
548
|
releaseLock: () => releaseLock
|
|
549
549
|
});
|
|
550
|
-
import
|
|
551
|
-
import
|
|
550
|
+
import fs36 from "fs";
|
|
551
|
+
import path28 from "path";
|
|
552
552
|
import os7 from "os";
|
|
553
553
|
function isCaliberRunning() {
|
|
554
554
|
try {
|
|
555
|
-
if (!
|
|
556
|
-
const raw =
|
|
555
|
+
if (!fs36.existsSync(LOCK_FILE)) return false;
|
|
556
|
+
const raw = fs36.readFileSync(LOCK_FILE, "utf-8").trim();
|
|
557
557
|
const { pid, ts } = JSON.parse(raw);
|
|
558
558
|
if (Date.now() - ts > STALE_MS) return false;
|
|
559
559
|
try {
|
|
@@ -568,13 +568,13 @@ function isCaliberRunning() {
|
|
|
568
568
|
}
|
|
569
569
|
function acquireLock() {
|
|
570
570
|
try {
|
|
571
|
-
|
|
571
|
+
fs36.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, ts: Date.now() }));
|
|
572
572
|
} catch {
|
|
573
573
|
}
|
|
574
574
|
}
|
|
575
575
|
function releaseLock() {
|
|
576
576
|
try {
|
|
577
|
-
if (
|
|
577
|
+
if (fs36.existsSync(LOCK_FILE)) fs36.unlinkSync(LOCK_FILE);
|
|
578
578
|
} catch {
|
|
579
579
|
}
|
|
580
580
|
}
|
|
@@ -582,21 +582,21 @@ var LOCK_FILE, STALE_MS;
|
|
|
582
582
|
var init_lock = __esm({
|
|
583
583
|
"src/lib/lock.ts"() {
|
|
584
584
|
"use strict";
|
|
585
|
-
LOCK_FILE =
|
|
585
|
+
LOCK_FILE = path28.join(os7.tmpdir(), ".caliber.lock");
|
|
586
586
|
STALE_MS = 10 * 60 * 1e3;
|
|
587
587
|
}
|
|
588
588
|
});
|
|
589
589
|
|
|
590
590
|
// src/cli.ts
|
|
591
591
|
import { Command } from "commander";
|
|
592
|
-
import
|
|
593
|
-
import
|
|
592
|
+
import fs46 from "fs";
|
|
593
|
+
import path37 from "path";
|
|
594
594
|
import { fileURLToPath } from "url";
|
|
595
595
|
|
|
596
596
|
// src/commands/init.ts
|
|
597
|
-
import
|
|
597
|
+
import path24 from "path";
|
|
598
598
|
import chalk14 from "chalk";
|
|
599
|
-
import
|
|
599
|
+
import fs31 from "fs";
|
|
600
600
|
|
|
601
601
|
// src/fingerprint/index.ts
|
|
602
602
|
import fs7 from "fs";
|
|
@@ -3424,15 +3424,15 @@ init_config();
|
|
|
3424
3424
|
// src/utils/dependencies.ts
|
|
3425
3425
|
import { readFileSync as readFileSync2 } from "fs";
|
|
3426
3426
|
import { join as join2 } from "path";
|
|
3427
|
-
function readFileOrNull2(
|
|
3427
|
+
function readFileOrNull2(path39) {
|
|
3428
3428
|
try {
|
|
3429
|
-
return readFileSync2(
|
|
3429
|
+
return readFileSync2(path39, "utf-8");
|
|
3430
3430
|
} catch {
|
|
3431
3431
|
return null;
|
|
3432
3432
|
}
|
|
3433
3433
|
}
|
|
3434
|
-
function readJsonOrNull(
|
|
3435
|
-
const content = readFileOrNull2(
|
|
3434
|
+
function readJsonOrNull(path39) {
|
|
3435
|
+
const content = readFileOrNull2(path39);
|
|
3436
3436
|
if (!content) return null;
|
|
3437
3437
|
try {
|
|
3438
3438
|
return JSON.parse(content);
|
|
@@ -4439,7 +4439,11 @@ function cleanupStaging() {
|
|
|
4439
4439
|
}
|
|
4440
4440
|
|
|
4441
4441
|
// src/commands/setup-files.ts
|
|
4442
|
+
import fs18 from "fs";
|
|
4443
|
+
|
|
4444
|
+
// src/lib/builtin-skills.ts
|
|
4442
4445
|
import fs17 from "fs";
|
|
4446
|
+
import path15 from "path";
|
|
4443
4447
|
function buildSkillContent(skill) {
|
|
4444
4448
|
const frontmatter = `---
|
|
4445
4449
|
name: ${skill.name}
|
|
@@ -4449,6 +4453,142 @@ description: ${skill.description}
|
|
|
4449
4453
|
`;
|
|
4450
4454
|
return frontmatter + skill.content;
|
|
4451
4455
|
}
|
|
4456
|
+
var FIND_SKILLS_SKILL = {
|
|
4457
|
+
name: "find-skills",
|
|
4458
|
+
description: "Discovers and installs community skills from the public registry. Use when the user mentions a technology, framework, or task that could benefit from specialized skills not yet installed, asks 'how do I do X', 'find a skill for X', or starts work in a new technology area. Proactively suggest when the user's task involves tools or frameworks without existing skills.",
|
|
4459
|
+
content: `# Find Skills
|
|
4460
|
+
|
|
4461
|
+
Search the public skill registry for community-contributed skills
|
|
4462
|
+
relevant to the user's current task and install them into this project.
|
|
4463
|
+
|
|
4464
|
+
## Instructions
|
|
4465
|
+
|
|
4466
|
+
1. Identify the key technologies, frameworks, or task types from the
|
|
4467
|
+
user's request that might have community skills available
|
|
4468
|
+
2. Ask the user: "Would you like me to search for community skills
|
|
4469
|
+
for [identified technologies]?"
|
|
4470
|
+
3. If the user agrees, run:
|
|
4471
|
+
\`\`\`bash
|
|
4472
|
+
caliber skills --query "<relevant terms>"
|
|
4473
|
+
\`\`\`
|
|
4474
|
+
This outputs the top 5 matching skills with scores and descriptions.
|
|
4475
|
+
4. Present the results to the user and ask which ones to install
|
|
4476
|
+
5. Install the selected skills:
|
|
4477
|
+
\`\`\`bash
|
|
4478
|
+
caliber skills --install <slug1>,<slug2>
|
|
4479
|
+
\`\`\`
|
|
4480
|
+
6. Read the installed SKILL.md files to load them into your current
|
|
4481
|
+
context so you can use them immediately in this session
|
|
4482
|
+
7. Summarize what was installed and continue with the user's task
|
|
4483
|
+
|
|
4484
|
+
## Examples
|
|
4485
|
+
|
|
4486
|
+
User: "let's build a web app using React"
|
|
4487
|
+
-> "I notice you want to work with React. Would you like me to search
|
|
4488
|
+
for community skills that could help with React development?"
|
|
4489
|
+
-> If yes: run \`caliber skills --query "react frontend"\`
|
|
4490
|
+
-> Show the user the results, ask which to install
|
|
4491
|
+
-> Run \`caliber skills --install <selected-slugs>\`
|
|
4492
|
+
-> Read the installed files and continue
|
|
4493
|
+
|
|
4494
|
+
User: "help me set up Docker for this project"
|
|
4495
|
+
-> "Would you like me to search for Docker-related skills?"
|
|
4496
|
+
-> If yes: run \`caliber skills --query "docker deployment"\`
|
|
4497
|
+
|
|
4498
|
+
User: "I need to write tests for this Python ML pipeline"
|
|
4499
|
+
-> "Would you like me to find skills for Python ML testing?"
|
|
4500
|
+
-> If yes: run \`caliber skills --query "python machine-learning testing"\`
|
|
4501
|
+
|
|
4502
|
+
## When NOT to trigger
|
|
4503
|
+
|
|
4504
|
+
- The user is working within an already well-configured area
|
|
4505
|
+
- You already suggested skills for this technology in this session
|
|
4506
|
+
- The user is in the middle of urgent debugging or time-sensitive work
|
|
4507
|
+
- The technology is too generic (e.g. just "code" or "programming")
|
|
4508
|
+
`
|
|
4509
|
+
};
|
|
4510
|
+
var SAVE_LEARNING_SKILL = {
|
|
4511
|
+
name: "save-learning",
|
|
4512
|
+
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.",
|
|
4513
|
+
content: `# Save Learning
|
|
4514
|
+
|
|
4515
|
+
Save a user's instruction or preference as a persistent learning that
|
|
4516
|
+
will be applied in all future sessions on this project.
|
|
4517
|
+
|
|
4518
|
+
## Instructions
|
|
4519
|
+
|
|
4520
|
+
1. Detect when the user gives an instruction to remember, such as:
|
|
4521
|
+
- "remember this", "save this", "always do X", "never do Y"
|
|
4522
|
+
- "from now on", "going forward", "in this project we..."
|
|
4523
|
+
- Any stated convention, preference, or rule
|
|
4524
|
+
2. Refine the instruction into a clean, actionable learning bullet with
|
|
4525
|
+
an appropriate type prefix:
|
|
4526
|
+
- \`**[convention]**\` \u2014 coding style, workflow, git conventions
|
|
4527
|
+
- \`**[pattern]**\` \u2014 reusable code patterns
|
|
4528
|
+
- \`**[anti-pattern]**\` \u2014 things to avoid
|
|
4529
|
+
- \`**[preference]**\` \u2014 personal/team preferences
|
|
4530
|
+
- \`**[context]**\` \u2014 project-specific context
|
|
4531
|
+
3. Show the refined learning to the user and ask for confirmation
|
|
4532
|
+
4. If confirmed, run:
|
|
4533
|
+
\`\`\`bash
|
|
4534
|
+
caliber learn add "<refined learning>"
|
|
4535
|
+
\`\`\`
|
|
4536
|
+
For personal preferences (not project-level), add \`--personal\`:
|
|
4537
|
+
\`\`\`bash
|
|
4538
|
+
caliber learn add --personal "<refined learning>"
|
|
4539
|
+
\`\`\`
|
|
4540
|
+
5. Stage the learnings file for the next commit:
|
|
4541
|
+
\`\`\`bash
|
|
4542
|
+
git add CALIBER_LEARNINGS.md
|
|
4543
|
+
\`\`\`
|
|
4544
|
+
|
|
4545
|
+
## Examples
|
|
4546
|
+
|
|
4547
|
+
User: "when developing features, push to next branch not master, remember it"
|
|
4548
|
+
-> Refine: \`**[convention]** Push feature commits to the \\\`next\\\` branch, not \\\`master\\\`\`
|
|
4549
|
+
-> "I'll save this as a project learning:
|
|
4550
|
+
**[convention]** Push feature commits to the \\\`next\\\` branch, not \\\`master\\\`
|
|
4551
|
+
Save for future sessions?"
|
|
4552
|
+
-> If yes: run \`caliber learn add "**[convention]** Push feature commits to the next branch, not master"\`
|
|
4553
|
+
-> Run \`git add CALIBER_LEARNINGS.md\`
|
|
4554
|
+
|
|
4555
|
+
User: "always use bun instead of npm"
|
|
4556
|
+
-> Refine: \`**[preference]** Use \\\`bun\\\` instead of \\\`npm\\\` for package management\`
|
|
4557
|
+
-> Confirm and save
|
|
4558
|
+
|
|
4559
|
+
User: "never use any in TypeScript, use unknown instead"
|
|
4560
|
+
-> Refine: \`**[convention]** Use \\\`unknown\\\` instead of \\\`any\\\` in TypeScript\`
|
|
4561
|
+
-> Confirm and save
|
|
4562
|
+
|
|
4563
|
+
## When NOT to trigger
|
|
4564
|
+
|
|
4565
|
+
- The user is giving a one-time instruction for the current task only
|
|
4566
|
+
- The instruction is too vague to be actionable
|
|
4567
|
+
- The user explicitly says "just for now" or "only this time"
|
|
4568
|
+
`
|
|
4569
|
+
};
|
|
4570
|
+
var BUILTIN_SKILLS = [FIND_SKILLS_SKILL, SAVE_LEARNING_SKILL];
|
|
4571
|
+
var PLATFORM_CONFIGS = [
|
|
4572
|
+
{ platformDir: ".claude", skillsDir: path15.join(".claude", "skills") },
|
|
4573
|
+
{ platformDir: ".cursor", skillsDir: path15.join(".cursor", "skills") },
|
|
4574
|
+
{ platformDir: ".agents", skillsDir: path15.join(".agents", "skills") }
|
|
4575
|
+
];
|
|
4576
|
+
function ensureBuiltinSkills() {
|
|
4577
|
+
const written = [];
|
|
4578
|
+
for (const { platformDir, skillsDir } of PLATFORM_CONFIGS) {
|
|
4579
|
+
if (!fs17.existsSync(platformDir)) continue;
|
|
4580
|
+
for (const skill of BUILTIN_SKILLS) {
|
|
4581
|
+
const skillPath = path15.join(skillsDir, skill.name, "SKILL.md");
|
|
4582
|
+
if (fs17.existsSync(skillPath)) continue;
|
|
4583
|
+
fs17.mkdirSync(path15.dirname(skillPath), { recursive: true });
|
|
4584
|
+
fs17.writeFileSync(skillPath, buildSkillContent(skill));
|
|
4585
|
+
written.push(skillPath);
|
|
4586
|
+
}
|
|
4587
|
+
}
|
|
4588
|
+
return written;
|
|
4589
|
+
}
|
|
4590
|
+
|
|
4591
|
+
// src/commands/setup-files.ts
|
|
4452
4592
|
function collectSetupFiles(setup, targetAgent) {
|
|
4453
4593
|
const files = [];
|
|
4454
4594
|
const claude = setup.claude;
|
|
@@ -4462,6 +4602,9 @@ function collectSetupFiles(setup, targetAgent) {
|
|
|
4462
4602
|
files.push({ path: `.claude/skills/${skill.name}/SKILL.md`, content: buildSkillContent(skill) });
|
|
4463
4603
|
}
|
|
4464
4604
|
}
|
|
4605
|
+
for (const builtin of BUILTIN_SKILLS) {
|
|
4606
|
+
files.push({ path: `.claude/skills/${builtin.name}/SKILL.md`, content: buildSkillContent(builtin) });
|
|
4607
|
+
}
|
|
4465
4608
|
}
|
|
4466
4609
|
if (codex) {
|
|
4467
4610
|
if (codex.agentsMd) files.push({ path: "AGENTS.md", content: codex.agentsMd });
|
|
@@ -4471,6 +4614,9 @@ function collectSetupFiles(setup, targetAgent) {
|
|
|
4471
4614
|
files.push({ path: `.agents/skills/${skill.name}/SKILL.md`, content: buildSkillContent(skill) });
|
|
4472
4615
|
}
|
|
4473
4616
|
}
|
|
4617
|
+
for (const builtin of BUILTIN_SKILLS) {
|
|
4618
|
+
files.push({ path: `.agents/skills/${builtin.name}/SKILL.md`, content: buildSkillContent(builtin) });
|
|
4619
|
+
}
|
|
4474
4620
|
}
|
|
4475
4621
|
if (cursor) {
|
|
4476
4622
|
if (cursor.cursorrules) files.push({ path: ".cursorrules", content: cursor.cursorrules });
|
|
@@ -4480,6 +4626,9 @@ function collectSetupFiles(setup, targetAgent) {
|
|
|
4480
4626
|
files.push({ path: `.cursor/skills/${skill.name}/SKILL.md`, content: buildSkillContent(skill) });
|
|
4481
4627
|
}
|
|
4482
4628
|
}
|
|
4629
|
+
for (const builtin of BUILTIN_SKILLS) {
|
|
4630
|
+
files.push({ path: `.cursor/skills/${builtin.name}/SKILL.md`, content: buildSkillContent(builtin) });
|
|
4631
|
+
}
|
|
4483
4632
|
const rules = cursor.rules;
|
|
4484
4633
|
if (Array.isArray(rules)) {
|
|
4485
4634
|
for (const rule of rules) {
|
|
@@ -4498,7 +4647,7 @@ function collectSetupFiles(setup, targetAgent) {
|
|
|
4498
4647
|
}
|
|
4499
4648
|
}
|
|
4500
4649
|
const codexTargeted = targetAgent ? targetAgent.includes("codex") : false;
|
|
4501
|
-
if (codexTargeted && !
|
|
4650
|
+
if (codexTargeted && !fs18.existsSync("AGENTS.md") && !(codex && codex.agentsMd)) {
|
|
4502
4651
|
const agentRefs = [];
|
|
4503
4652
|
if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
|
|
4504
4653
|
if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
|
|
@@ -4516,9 +4665,9 @@ ${agentRefs.join(" ")}
|
|
|
4516
4665
|
|
|
4517
4666
|
// src/lib/learning-hooks.ts
|
|
4518
4667
|
init_resolve_caliber();
|
|
4519
|
-
import
|
|
4520
|
-
import
|
|
4521
|
-
var SETTINGS_PATH =
|
|
4668
|
+
import fs20 from "fs";
|
|
4669
|
+
import path16 from "path";
|
|
4670
|
+
var SETTINGS_PATH = path16.join(".claude", "settings.json");
|
|
4522
4671
|
var HOOK_TAILS = [
|
|
4523
4672
|
{ event: "PostToolUse", tail: "learn observe", description: "Caliber: recording tool usage for session learning" },
|
|
4524
4673
|
{ event: "PostToolUseFailure", tail: "learn observe --failure", description: "Caliber: recording tool failure for session learning" },
|
|
@@ -4535,17 +4684,17 @@ function getHookConfigs() {
|
|
|
4535
4684
|
}));
|
|
4536
4685
|
}
|
|
4537
4686
|
function readSettings() {
|
|
4538
|
-
if (!
|
|
4687
|
+
if (!fs20.existsSync(SETTINGS_PATH)) return {};
|
|
4539
4688
|
try {
|
|
4540
|
-
return JSON.parse(
|
|
4689
|
+
return JSON.parse(fs20.readFileSync(SETTINGS_PATH, "utf-8"));
|
|
4541
4690
|
} catch {
|
|
4542
4691
|
return {};
|
|
4543
4692
|
}
|
|
4544
4693
|
}
|
|
4545
4694
|
function writeSettings(settings) {
|
|
4546
|
-
const dir =
|
|
4547
|
-
if (!
|
|
4548
|
-
|
|
4695
|
+
const dir = path16.dirname(SETTINGS_PATH);
|
|
4696
|
+
if (!fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
|
|
4697
|
+
fs20.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
4549
4698
|
}
|
|
4550
4699
|
function hasLearningHook(matchers, tail) {
|
|
4551
4700
|
return matchers.some((entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, tail)));
|
|
@@ -4579,7 +4728,7 @@ function installLearningHooks() {
|
|
|
4579
4728
|
writeSettings(settings);
|
|
4580
4729
|
return { installed: true, alreadyInstalled: false };
|
|
4581
4730
|
}
|
|
4582
|
-
var CURSOR_HOOKS_PATH =
|
|
4731
|
+
var CURSOR_HOOKS_PATH = path16.join(".cursor", "hooks.json");
|
|
4583
4732
|
var CURSOR_HOOK_EVENTS = [
|
|
4584
4733
|
{ event: "postToolUse", tail: "learn observe" },
|
|
4585
4734
|
{ event: "postToolUseFailure", tail: "learn observe --failure" },
|
|
@@ -4587,17 +4736,17 @@ var CURSOR_HOOK_EVENTS = [
|
|
|
4587
4736
|
{ event: "sessionEnd", tail: "learn finalize --auto" }
|
|
4588
4737
|
];
|
|
4589
4738
|
function readCursorHooks() {
|
|
4590
|
-
if (!
|
|
4739
|
+
if (!fs20.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
|
|
4591
4740
|
try {
|
|
4592
|
-
return JSON.parse(
|
|
4741
|
+
return JSON.parse(fs20.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
|
|
4593
4742
|
} catch {
|
|
4594
4743
|
return { version: 1, hooks: {} };
|
|
4595
4744
|
}
|
|
4596
4745
|
}
|
|
4597
4746
|
function writeCursorHooks(config) {
|
|
4598
|
-
const dir =
|
|
4599
|
-
if (!
|
|
4600
|
-
|
|
4747
|
+
const dir = path16.dirname(CURSOR_HOOKS_PATH);
|
|
4748
|
+
if (!fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
|
|
4749
|
+
fs20.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
|
|
4601
4750
|
}
|
|
4602
4751
|
function hasCursorHook(entries, tail) {
|
|
4603
4752
|
return entries.some((e) => isCaliberCommand(e.command, tail));
|
|
@@ -4667,10 +4816,10 @@ function removeLearningHooks() {
|
|
|
4667
4816
|
|
|
4668
4817
|
// src/lib/state.ts
|
|
4669
4818
|
init_constants();
|
|
4670
|
-
import
|
|
4671
|
-
import
|
|
4819
|
+
import fs21 from "fs";
|
|
4820
|
+
import path17 from "path";
|
|
4672
4821
|
import { execSync as execSync7 } from "child_process";
|
|
4673
|
-
var STATE_FILE =
|
|
4822
|
+
var STATE_FILE = path17.join(CALIBER_DIR, ".caliber-state.json");
|
|
4674
4823
|
function normalizeTargetAgent(value) {
|
|
4675
4824
|
if (Array.isArray(value)) return value;
|
|
4676
4825
|
if (typeof value === "string") {
|
|
@@ -4681,8 +4830,8 @@ function normalizeTargetAgent(value) {
|
|
|
4681
4830
|
}
|
|
4682
4831
|
function readState() {
|
|
4683
4832
|
try {
|
|
4684
|
-
if (!
|
|
4685
|
-
const raw = JSON.parse(
|
|
4833
|
+
if (!fs21.existsSync(STATE_FILE)) return null;
|
|
4834
|
+
const raw = JSON.parse(fs21.readFileSync(STATE_FILE, "utf-8"));
|
|
4686
4835
|
if (raw.targetAgent) raw.targetAgent = normalizeTargetAgent(raw.targetAgent);
|
|
4687
4836
|
return raw;
|
|
4688
4837
|
} catch {
|
|
@@ -4690,10 +4839,10 @@ function readState() {
|
|
|
4690
4839
|
}
|
|
4691
4840
|
}
|
|
4692
4841
|
function writeState(state) {
|
|
4693
|
-
if (!
|
|
4694
|
-
|
|
4842
|
+
if (!fs21.existsSync(CALIBER_DIR)) {
|
|
4843
|
+
fs21.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
4695
4844
|
}
|
|
4696
|
-
|
|
4845
|
+
fs21.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
|
|
4697
4846
|
}
|
|
4698
4847
|
function getCurrentHeadSha() {
|
|
4699
4848
|
try {
|
|
@@ -5752,22 +5901,22 @@ function checkSources(dir) {
|
|
|
5752
5901
|
|
|
5753
5902
|
// src/scoring/dismissed.ts
|
|
5754
5903
|
init_constants();
|
|
5755
|
-
import
|
|
5756
|
-
import
|
|
5757
|
-
var DISMISSED_FILE =
|
|
5904
|
+
import fs22 from "fs";
|
|
5905
|
+
import path18 from "path";
|
|
5906
|
+
var DISMISSED_FILE = path18.join(CALIBER_DIR, "dismissed-checks.json");
|
|
5758
5907
|
function readDismissedChecks() {
|
|
5759
5908
|
try {
|
|
5760
|
-
if (!
|
|
5761
|
-
return JSON.parse(
|
|
5909
|
+
if (!fs22.existsSync(DISMISSED_FILE)) return [];
|
|
5910
|
+
return JSON.parse(fs22.readFileSync(DISMISSED_FILE, "utf-8"));
|
|
5762
5911
|
} catch {
|
|
5763
5912
|
return [];
|
|
5764
5913
|
}
|
|
5765
5914
|
}
|
|
5766
5915
|
function writeDismissedChecks(checks) {
|
|
5767
|
-
if (!
|
|
5768
|
-
|
|
5916
|
+
if (!fs22.existsSync(CALIBER_DIR)) {
|
|
5917
|
+
fs22.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
5769
5918
|
}
|
|
5770
|
-
|
|
5919
|
+
fs22.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
|
|
5771
5920
|
}
|
|
5772
5921
|
function getDismissedIds() {
|
|
5773
5922
|
return new Set(readDismissedChecks().map((c) => c.id));
|
|
@@ -6015,13 +6164,13 @@ import { mkdirSync, readFileSync as readFileSync4, readdirSync as readdirSync4,
|
|
|
6015
6164
|
import { join as join10, dirname as dirname2 } from "path";
|
|
6016
6165
|
|
|
6017
6166
|
// src/scanner/index.ts
|
|
6018
|
-
import
|
|
6019
|
-
import
|
|
6167
|
+
import fs23 from "fs";
|
|
6168
|
+
import path19 from "path";
|
|
6020
6169
|
import crypto3 from "crypto";
|
|
6021
6170
|
function scanLocalState(dir) {
|
|
6022
6171
|
const items = [];
|
|
6023
|
-
const claudeMdPath =
|
|
6024
|
-
if (
|
|
6172
|
+
const claudeMdPath = path19.join(dir, "CLAUDE.md");
|
|
6173
|
+
if (fs23.existsSync(claudeMdPath)) {
|
|
6025
6174
|
items.push({
|
|
6026
6175
|
type: "rule",
|
|
6027
6176
|
platform: "claude",
|
|
@@ -6030,10 +6179,10 @@ function scanLocalState(dir) {
|
|
|
6030
6179
|
path: claudeMdPath
|
|
6031
6180
|
});
|
|
6032
6181
|
}
|
|
6033
|
-
const skillsDir =
|
|
6034
|
-
if (
|
|
6035
|
-
for (const file of
|
|
6036
|
-
const filePath =
|
|
6182
|
+
const skillsDir = path19.join(dir, ".claude", "skills");
|
|
6183
|
+
if (fs23.existsSync(skillsDir)) {
|
|
6184
|
+
for (const file of fs23.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
|
|
6185
|
+
const filePath = path19.join(skillsDir, file);
|
|
6037
6186
|
items.push({
|
|
6038
6187
|
type: "skill",
|
|
6039
6188
|
platform: "claude",
|
|
@@ -6043,10 +6192,10 @@ function scanLocalState(dir) {
|
|
|
6043
6192
|
});
|
|
6044
6193
|
}
|
|
6045
6194
|
}
|
|
6046
|
-
const mcpJsonPath =
|
|
6047
|
-
if (
|
|
6195
|
+
const mcpJsonPath = path19.join(dir, ".mcp.json");
|
|
6196
|
+
if (fs23.existsSync(mcpJsonPath)) {
|
|
6048
6197
|
try {
|
|
6049
|
-
const mcpJson = JSON.parse(
|
|
6198
|
+
const mcpJson = JSON.parse(fs23.readFileSync(mcpJsonPath, "utf-8"));
|
|
6050
6199
|
if (mcpJson.mcpServers) {
|
|
6051
6200
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
6052
6201
|
items.push({
|
|
@@ -6061,8 +6210,8 @@ function scanLocalState(dir) {
|
|
|
6061
6210
|
} catch {
|
|
6062
6211
|
}
|
|
6063
6212
|
}
|
|
6064
|
-
const agentsMdPath =
|
|
6065
|
-
if (
|
|
6213
|
+
const agentsMdPath = path19.join(dir, "AGENTS.md");
|
|
6214
|
+
if (fs23.existsSync(agentsMdPath)) {
|
|
6066
6215
|
items.push({
|
|
6067
6216
|
type: "rule",
|
|
6068
6217
|
platform: "codex",
|
|
@@ -6071,12 +6220,12 @@ function scanLocalState(dir) {
|
|
|
6071
6220
|
path: agentsMdPath
|
|
6072
6221
|
});
|
|
6073
6222
|
}
|
|
6074
|
-
const codexSkillsDir =
|
|
6075
|
-
if (
|
|
6223
|
+
const codexSkillsDir = path19.join(dir, ".agents", "skills");
|
|
6224
|
+
if (fs23.existsSync(codexSkillsDir)) {
|
|
6076
6225
|
try {
|
|
6077
|
-
for (const name of
|
|
6078
|
-
const skillFile =
|
|
6079
|
-
if (
|
|
6226
|
+
for (const name of fs23.readdirSync(codexSkillsDir)) {
|
|
6227
|
+
const skillFile = path19.join(codexSkillsDir, name, "SKILL.md");
|
|
6228
|
+
if (fs23.existsSync(skillFile)) {
|
|
6080
6229
|
items.push({
|
|
6081
6230
|
type: "skill",
|
|
6082
6231
|
platform: "codex",
|
|
@@ -6089,8 +6238,8 @@ function scanLocalState(dir) {
|
|
|
6089
6238
|
} catch {
|
|
6090
6239
|
}
|
|
6091
6240
|
}
|
|
6092
|
-
const cursorrulesPath =
|
|
6093
|
-
if (
|
|
6241
|
+
const cursorrulesPath = path19.join(dir, ".cursorrules");
|
|
6242
|
+
if (fs23.existsSync(cursorrulesPath)) {
|
|
6094
6243
|
items.push({
|
|
6095
6244
|
type: "rule",
|
|
6096
6245
|
platform: "cursor",
|
|
@@ -6099,10 +6248,10 @@ function scanLocalState(dir) {
|
|
|
6099
6248
|
path: cursorrulesPath
|
|
6100
6249
|
});
|
|
6101
6250
|
}
|
|
6102
|
-
const cursorRulesDir =
|
|
6103
|
-
if (
|
|
6104
|
-
for (const file of
|
|
6105
|
-
const filePath =
|
|
6251
|
+
const cursorRulesDir = path19.join(dir, ".cursor", "rules");
|
|
6252
|
+
if (fs23.existsSync(cursorRulesDir)) {
|
|
6253
|
+
for (const file of fs23.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
|
|
6254
|
+
const filePath = path19.join(cursorRulesDir, file);
|
|
6106
6255
|
items.push({
|
|
6107
6256
|
type: "rule",
|
|
6108
6257
|
platform: "cursor",
|
|
@@ -6112,12 +6261,12 @@ function scanLocalState(dir) {
|
|
|
6112
6261
|
});
|
|
6113
6262
|
}
|
|
6114
6263
|
}
|
|
6115
|
-
const cursorSkillsDir =
|
|
6116
|
-
if (
|
|
6264
|
+
const cursorSkillsDir = path19.join(dir, ".cursor", "skills");
|
|
6265
|
+
if (fs23.existsSync(cursorSkillsDir)) {
|
|
6117
6266
|
try {
|
|
6118
|
-
for (const name of
|
|
6119
|
-
const skillFile =
|
|
6120
|
-
if (
|
|
6267
|
+
for (const name of fs23.readdirSync(cursorSkillsDir)) {
|
|
6268
|
+
const skillFile = path19.join(cursorSkillsDir, name, "SKILL.md");
|
|
6269
|
+
if (fs23.existsSync(skillFile)) {
|
|
6121
6270
|
items.push({
|
|
6122
6271
|
type: "skill",
|
|
6123
6272
|
platform: "cursor",
|
|
@@ -6130,10 +6279,10 @@ function scanLocalState(dir) {
|
|
|
6130
6279
|
} catch {
|
|
6131
6280
|
}
|
|
6132
6281
|
}
|
|
6133
|
-
const cursorMcpPath =
|
|
6134
|
-
if (
|
|
6282
|
+
const cursorMcpPath = path19.join(dir, ".cursor", "mcp.json");
|
|
6283
|
+
if (fs23.existsSync(cursorMcpPath)) {
|
|
6135
6284
|
try {
|
|
6136
|
-
const mcpJson = JSON.parse(
|
|
6285
|
+
const mcpJson = JSON.parse(fs23.readFileSync(cursorMcpPath, "utf-8"));
|
|
6137
6286
|
if (mcpJson.mcpServers) {
|
|
6138
6287
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
6139
6288
|
items.push({
|
|
@@ -6151,7 +6300,7 @@ function scanLocalState(dir) {
|
|
|
6151
6300
|
return items;
|
|
6152
6301
|
}
|
|
6153
6302
|
function hashFile(filePath) {
|
|
6154
|
-
const text =
|
|
6303
|
+
const text = fs23.readFileSync(filePath, "utf-8");
|
|
6155
6304
|
return crypto3.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
|
|
6156
6305
|
}
|
|
6157
6306
|
function hashJson(obj) {
|
|
@@ -6166,27 +6315,27 @@ import { PostHog } from "posthog-node";
|
|
|
6166
6315
|
import chalk5 from "chalk";
|
|
6167
6316
|
|
|
6168
6317
|
// src/telemetry/config.ts
|
|
6169
|
-
import
|
|
6170
|
-
import
|
|
6318
|
+
import fs24 from "fs";
|
|
6319
|
+
import path20 from "path";
|
|
6171
6320
|
import os4 from "os";
|
|
6172
6321
|
import crypto4 from "crypto";
|
|
6173
6322
|
import { execSync as execSync11 } from "child_process";
|
|
6174
|
-
var CONFIG_DIR2 =
|
|
6175
|
-
var CONFIG_FILE2 =
|
|
6323
|
+
var CONFIG_DIR2 = path20.join(os4.homedir(), ".caliber");
|
|
6324
|
+
var CONFIG_FILE2 = path20.join(CONFIG_DIR2, "config.json");
|
|
6176
6325
|
var runtimeDisabled = false;
|
|
6177
6326
|
function readConfig() {
|
|
6178
6327
|
try {
|
|
6179
|
-
if (!
|
|
6180
|
-
return JSON.parse(
|
|
6328
|
+
if (!fs24.existsSync(CONFIG_FILE2)) return {};
|
|
6329
|
+
return JSON.parse(fs24.readFileSync(CONFIG_FILE2, "utf-8"));
|
|
6181
6330
|
} catch {
|
|
6182
6331
|
return {};
|
|
6183
6332
|
}
|
|
6184
6333
|
}
|
|
6185
6334
|
function writeConfig(config) {
|
|
6186
|
-
if (!
|
|
6187
|
-
|
|
6335
|
+
if (!fs24.existsSync(CONFIG_DIR2)) {
|
|
6336
|
+
fs24.mkdirSync(CONFIG_DIR2, { recursive: true });
|
|
6188
6337
|
}
|
|
6189
|
-
|
|
6338
|
+
fs24.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
|
|
6190
6339
|
}
|
|
6191
6340
|
function getMachineId() {
|
|
6192
6341
|
const config = readConfig();
|
|
@@ -6671,7 +6820,109 @@ async function searchSkills(fingerprint, targetPlatforms, onStatus) {
|
|
|
6671
6820
|
const available = results.filter((r) => contentMap.has(r.slug));
|
|
6672
6821
|
return { results: available, contentMap };
|
|
6673
6822
|
}
|
|
6674
|
-
async function
|
|
6823
|
+
async function querySkills(query) {
|
|
6824
|
+
const terms = query.split(/[\s,]+/).filter(Boolean);
|
|
6825
|
+
if (terms.length === 0) {
|
|
6826
|
+
console.log(chalk6.yellow("Please provide search terms."));
|
|
6827
|
+
throw new Error("__exit__");
|
|
6828
|
+
}
|
|
6829
|
+
const platforms = detectLocalPlatforms();
|
|
6830
|
+
const installedSkills = getInstalledSkills(platforms);
|
|
6831
|
+
const primaryPlatform = platforms.includes("claude") ? "claude" : platforms[0];
|
|
6832
|
+
const searchSpinner = ora("Searching skill registries...").start();
|
|
6833
|
+
const allCandidates = await searchAllProviders(terms, primaryPlatform);
|
|
6834
|
+
if (!allCandidates.length) {
|
|
6835
|
+
searchSpinner.succeed("No skills found matching your query.");
|
|
6836
|
+
return;
|
|
6837
|
+
}
|
|
6838
|
+
const newCandidates = allCandidates.filter((c) => !installedSkills.has(c.slug.toLowerCase()));
|
|
6839
|
+
if (!newCandidates.length) {
|
|
6840
|
+
searchSpinner.succeed("All matching skills are already installed.");
|
|
6841
|
+
return;
|
|
6842
|
+
}
|
|
6843
|
+
searchSpinner.succeed(`Found ${newCandidates.length} candidates`);
|
|
6844
|
+
let results;
|
|
6845
|
+
const config = loadConfig();
|
|
6846
|
+
if (config) {
|
|
6847
|
+
const scoreSpinner = ora("Scoring relevance...").start();
|
|
6848
|
+
try {
|
|
6849
|
+
const queryContext = `User is looking for skills related to: ${query}`;
|
|
6850
|
+
results = await scoreWithLLM(newCandidates, queryContext, terms);
|
|
6851
|
+
if (results.length === 0) {
|
|
6852
|
+
scoreSpinner.succeed("No highly relevant skills found.");
|
|
6853
|
+
return;
|
|
6854
|
+
}
|
|
6855
|
+
scoreSpinner.succeed(`${results.length} relevant`);
|
|
6856
|
+
} catch {
|
|
6857
|
+
results = newCandidates.slice(0, 5);
|
|
6858
|
+
}
|
|
6859
|
+
} else {
|
|
6860
|
+
results = newCandidates.slice(0, 5);
|
|
6861
|
+
}
|
|
6862
|
+
const top = results.slice(0, 5);
|
|
6863
|
+
const fetchSpinner = ora("Verifying availability...").start();
|
|
6864
|
+
const contentMap = /* @__PURE__ */ new Map();
|
|
6865
|
+
await Promise.all(top.map(async (rec) => {
|
|
6866
|
+
const content = await fetchSkillContent(rec);
|
|
6867
|
+
if (content) contentMap.set(rec.slug, content);
|
|
6868
|
+
}));
|
|
6869
|
+
const available = top.filter((r) => contentMap.has(r.slug));
|
|
6870
|
+
fetchSpinner.succeed(`${available.length} available`);
|
|
6871
|
+
if (!available.length) {
|
|
6872
|
+
console.log(chalk6.dim(" No installable skills found.\n"));
|
|
6873
|
+
return;
|
|
6874
|
+
}
|
|
6875
|
+
console.log("");
|
|
6876
|
+
for (let i = 0; i < available.length; i++) {
|
|
6877
|
+
const r = available[i];
|
|
6878
|
+
const scoreStr = r.score > 0 ? ` (score: ${r.score})` : "";
|
|
6879
|
+
console.log(` ${i + 1}. ${r.slug}${scoreStr}`);
|
|
6880
|
+
console.log(` ${r.reason || r.name}`);
|
|
6881
|
+
}
|
|
6882
|
+
console.log("");
|
|
6883
|
+
console.log(chalk6.dim(` Install with: caliber skills --install ${available.map((r) => r.slug).join(",")}`));
|
|
6884
|
+
console.log("");
|
|
6885
|
+
}
|
|
6886
|
+
async function installBySlug(slugStr) {
|
|
6887
|
+
const slugs = slugStr.split(",").map((s) => s.trim()).filter(Boolean);
|
|
6888
|
+
if (slugs.length === 0) {
|
|
6889
|
+
console.log(chalk6.yellow("Please provide skill slugs to install."));
|
|
6890
|
+
throw new Error("__exit__");
|
|
6891
|
+
}
|
|
6892
|
+
const platforms = detectLocalPlatforms();
|
|
6893
|
+
const spinner = ora(`Fetching ${slugs.length} skill${slugs.length > 1 ? "s" : ""}...`).start();
|
|
6894
|
+
const allResults = await searchAllProviders(slugs);
|
|
6895
|
+
const matched = [];
|
|
6896
|
+
for (const slug of slugs) {
|
|
6897
|
+
const match = allResults.find((r) => r.slug.toLowerCase() === slug.toLowerCase());
|
|
6898
|
+
if (match) matched.push(match);
|
|
6899
|
+
}
|
|
6900
|
+
if (!matched.length) {
|
|
6901
|
+
spinner.fail("No matching skills found in the registry.");
|
|
6902
|
+
return;
|
|
6903
|
+
}
|
|
6904
|
+
const contentMap = /* @__PURE__ */ new Map();
|
|
6905
|
+
await Promise.all(matched.map(async (rec) => {
|
|
6906
|
+
const content = await fetchSkillContent(rec);
|
|
6907
|
+
if (content) contentMap.set(rec.slug, content);
|
|
6908
|
+
}));
|
|
6909
|
+
const installable = matched.filter((r) => contentMap.has(r.slug));
|
|
6910
|
+
if (!installable.length) {
|
|
6911
|
+
spinner.fail("Could not fetch skill content.");
|
|
6912
|
+
return;
|
|
6913
|
+
}
|
|
6914
|
+
spinner.succeed(`Fetched ${installable.length} skill${installable.length > 1 ? "s" : ""}`);
|
|
6915
|
+
await installSkills(installable, platforms, contentMap);
|
|
6916
|
+
}
|
|
6917
|
+
async function recommendCommand(options) {
|
|
6918
|
+
if (options.install) {
|
|
6919
|
+
await installBySlug(options.install);
|
|
6920
|
+
return;
|
|
6921
|
+
}
|
|
6922
|
+
if (options.query) {
|
|
6923
|
+
await querySkills(options.query);
|
|
6924
|
+
return;
|
|
6925
|
+
}
|
|
6675
6926
|
const proceed = await select3({
|
|
6676
6927
|
message: "Search public repos for relevant skills to add to this project?",
|
|
6677
6928
|
choices: [
|
|
@@ -7145,11 +7396,11 @@ function countIssuePoints(issues) {
|
|
|
7145
7396
|
}
|
|
7146
7397
|
async function scoreAndRefine(setup, dir, sessionHistory, callbacks) {
|
|
7147
7398
|
const existsCache = /* @__PURE__ */ new Map();
|
|
7148
|
-
const cachedExists = (
|
|
7149
|
-
const cached = existsCache.get(
|
|
7399
|
+
const cachedExists = (path39) => {
|
|
7400
|
+
const cached = existsCache.get(path39);
|
|
7150
7401
|
if (cached !== void 0) return cached;
|
|
7151
|
-
const result = existsSync9(
|
|
7152
|
-
existsCache.set(
|
|
7402
|
+
const result = existsSync9(path39);
|
|
7403
|
+
existsCache.set(path39, result);
|
|
7153
7404
|
return result;
|
|
7154
7405
|
};
|
|
7155
7406
|
const projectStructure = collectProjectStructure(dir);
|
|
@@ -7288,8 +7539,8 @@ async function runScoreRefineWithSpinner(setup, dir, sessionHistory) {
|
|
|
7288
7539
|
}
|
|
7289
7540
|
|
|
7290
7541
|
// src/lib/debug-report.ts
|
|
7291
|
-
import
|
|
7292
|
-
import
|
|
7542
|
+
import fs25 from "fs";
|
|
7543
|
+
import path21 from "path";
|
|
7293
7544
|
var DebugReport = class {
|
|
7294
7545
|
sections = [];
|
|
7295
7546
|
startTime;
|
|
@@ -7358,11 +7609,11 @@ var DebugReport = class {
|
|
|
7358
7609
|
lines.push(`| **Total** | **${formatMs(totalMs)}** |`);
|
|
7359
7610
|
lines.push("");
|
|
7360
7611
|
}
|
|
7361
|
-
const dir =
|
|
7362
|
-
if (!
|
|
7363
|
-
|
|
7612
|
+
const dir = path21.dirname(outputPath);
|
|
7613
|
+
if (!fs25.existsSync(dir)) {
|
|
7614
|
+
fs25.mkdirSync(dir, { recursive: true });
|
|
7364
7615
|
}
|
|
7365
|
-
|
|
7616
|
+
fs25.writeFileSync(outputPath, lines.join("\n"));
|
|
7366
7617
|
}
|
|
7367
7618
|
};
|
|
7368
7619
|
function formatMs(ms) {
|
|
@@ -7763,7 +8014,7 @@ import chalk11 from "chalk";
|
|
|
7763
8014
|
import ora3 from "ora";
|
|
7764
8015
|
import select5 from "@inquirer/select";
|
|
7765
8016
|
import checkbox from "@inquirer/checkbox";
|
|
7766
|
-
import
|
|
8017
|
+
import fs28 from "fs";
|
|
7767
8018
|
|
|
7768
8019
|
// src/ai/refine.ts
|
|
7769
8020
|
async function refineSetup(currentSetup, message, conversationHistory, callbacks) {
|
|
@@ -7904,10 +8155,10 @@ init_config();
|
|
|
7904
8155
|
init_review();
|
|
7905
8156
|
function detectAgents(dir) {
|
|
7906
8157
|
const agents = [];
|
|
7907
|
-
if (
|
|
7908
|
-
if (
|
|
7909
|
-
if (
|
|
7910
|
-
if (
|
|
8158
|
+
if (fs28.existsSync(`${dir}/.claude`)) agents.push("claude");
|
|
8159
|
+
if (fs28.existsSync(`${dir}/.cursor`)) agents.push("cursor");
|
|
8160
|
+
if (fs28.existsSync(`${dir}/.agents`) || fs28.existsSync(`${dir}/AGENTS.md`)) agents.push("codex");
|
|
8161
|
+
if (fs28.existsSync(`${dir}/.github/copilot-instructions.md`)) agents.push("github-copilot");
|
|
7911
8162
|
return agents;
|
|
7912
8163
|
}
|
|
7913
8164
|
async function promptAgent(detected) {
|
|
@@ -8029,18 +8280,18 @@ async function refineLoop(currentSetup, sessionHistory, summarizeSetup2, printSu
|
|
|
8029
8280
|
|
|
8030
8281
|
// src/commands/init-display.ts
|
|
8031
8282
|
import chalk12 from "chalk";
|
|
8032
|
-
import
|
|
8283
|
+
import fs29 from "fs";
|
|
8033
8284
|
function formatWhatChanged(setup) {
|
|
8034
8285
|
const lines = [];
|
|
8035
8286
|
const claude = setup.claude;
|
|
8036
8287
|
const codex = setup.codex;
|
|
8037
8288
|
const cursor = setup.cursor;
|
|
8038
8289
|
if (claude?.claudeMd) {
|
|
8039
|
-
const action =
|
|
8290
|
+
const action = fs29.existsSync("CLAUDE.md") ? "Updated" : "Created";
|
|
8040
8291
|
lines.push(`${action} CLAUDE.md`);
|
|
8041
8292
|
}
|
|
8042
8293
|
if (codex?.agentsMd) {
|
|
8043
|
-
const action =
|
|
8294
|
+
const action = fs29.existsSync("AGENTS.md") ? "Updated" : "Created";
|
|
8044
8295
|
lines.push(`${action} AGENTS.md`);
|
|
8045
8296
|
}
|
|
8046
8297
|
const allSkills = [];
|
|
@@ -8077,7 +8328,7 @@ function printSetupSummary(setup) {
|
|
|
8077
8328
|
};
|
|
8078
8329
|
if (claude) {
|
|
8079
8330
|
if (claude.claudeMd) {
|
|
8080
|
-
const icon =
|
|
8331
|
+
const icon = fs29.existsSync("CLAUDE.md") ? chalk12.yellow("~") : chalk12.green("+");
|
|
8081
8332
|
const desc = getDescription("CLAUDE.md");
|
|
8082
8333
|
console.log(` ${icon} ${chalk12.bold("CLAUDE.md")}`);
|
|
8083
8334
|
if (desc) console.log(chalk12.dim(` ${desc}`));
|
|
@@ -8087,7 +8338,7 @@ function printSetupSummary(setup) {
|
|
|
8087
8338
|
if (Array.isArray(skills) && skills.length > 0) {
|
|
8088
8339
|
for (const skill of skills) {
|
|
8089
8340
|
const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
|
|
8090
|
-
const icon =
|
|
8341
|
+
const icon = fs29.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
|
|
8091
8342
|
const desc = getDescription(skillPath);
|
|
8092
8343
|
console.log(` ${icon} ${chalk12.bold(skillPath)}`);
|
|
8093
8344
|
console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -8098,7 +8349,7 @@ function printSetupSummary(setup) {
|
|
|
8098
8349
|
const codex = setup.codex;
|
|
8099
8350
|
if (codex) {
|
|
8100
8351
|
if (codex.agentsMd) {
|
|
8101
|
-
const icon =
|
|
8352
|
+
const icon = fs29.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
|
|
8102
8353
|
const desc = getDescription("AGENTS.md");
|
|
8103
8354
|
console.log(` ${icon} ${chalk12.bold("AGENTS.md")}`);
|
|
8104
8355
|
if (desc) console.log(chalk12.dim(` ${desc}`));
|
|
@@ -8108,7 +8359,7 @@ function printSetupSummary(setup) {
|
|
|
8108
8359
|
if (Array.isArray(codexSkills) && codexSkills.length > 0) {
|
|
8109
8360
|
for (const skill of codexSkills) {
|
|
8110
8361
|
const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
|
|
8111
|
-
const icon =
|
|
8362
|
+
const icon = fs29.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
|
|
8112
8363
|
const desc = getDescription(skillPath);
|
|
8113
8364
|
console.log(` ${icon} ${chalk12.bold(skillPath)}`);
|
|
8114
8365
|
console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -8118,7 +8369,7 @@ function printSetupSummary(setup) {
|
|
|
8118
8369
|
}
|
|
8119
8370
|
if (cursor) {
|
|
8120
8371
|
if (cursor.cursorrules) {
|
|
8121
|
-
const icon =
|
|
8372
|
+
const icon = fs29.existsSync(".cursorrules") ? chalk12.yellow("~") : chalk12.green("+");
|
|
8122
8373
|
const desc = getDescription(".cursorrules");
|
|
8123
8374
|
console.log(` ${icon} ${chalk12.bold(".cursorrules")}`);
|
|
8124
8375
|
if (desc) console.log(chalk12.dim(` ${desc}`));
|
|
@@ -8128,7 +8379,7 @@ function printSetupSummary(setup) {
|
|
|
8128
8379
|
if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
|
|
8129
8380
|
for (const skill of cursorSkills) {
|
|
8130
8381
|
const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
|
|
8131
|
-
const icon =
|
|
8382
|
+
const icon = fs29.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
|
|
8132
8383
|
const desc = getDescription(skillPath);
|
|
8133
8384
|
console.log(` ${icon} ${chalk12.bold(skillPath)}`);
|
|
8134
8385
|
console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -8139,7 +8390,7 @@ function printSetupSummary(setup) {
|
|
|
8139
8390
|
if (Array.isArray(rulesArr) && rulesArr.length > 0) {
|
|
8140
8391
|
for (const rule of rulesArr) {
|
|
8141
8392
|
const rulePath = `.cursor/rules/${rule.filename}`;
|
|
8142
|
-
const icon =
|
|
8393
|
+
const icon = fs29.existsSync(rulePath) ? chalk12.yellow("~") : chalk12.green("+");
|
|
8143
8394
|
const desc = getDescription(rulePath);
|
|
8144
8395
|
console.log(` ${icon} ${chalk12.bold(rulePath)}`);
|
|
8145
8396
|
if (desc) {
|
|
@@ -8186,12 +8437,12 @@ function displayTokenUsage() {
|
|
|
8186
8437
|
// src/commands/init-helpers.ts
|
|
8187
8438
|
init_config();
|
|
8188
8439
|
import chalk13 from "chalk";
|
|
8189
|
-
import
|
|
8190
|
-
import
|
|
8440
|
+
import fs30 from "fs";
|
|
8441
|
+
import path23 from "path";
|
|
8191
8442
|
function isFirstRun(dir) {
|
|
8192
|
-
const caliberDir =
|
|
8443
|
+
const caliberDir = path23.join(dir, ".caliber");
|
|
8193
8444
|
try {
|
|
8194
|
-
const stat =
|
|
8445
|
+
const stat = fs30.statSync(caliberDir);
|
|
8195
8446
|
return !stat.isDirectory();
|
|
8196
8447
|
} catch {
|
|
8197
8448
|
return true;
|
|
@@ -8244,8 +8495,8 @@ function ensurePermissions(fingerprint) {
|
|
|
8244
8495
|
const settingsPath = ".claude/settings.json";
|
|
8245
8496
|
let settings = {};
|
|
8246
8497
|
try {
|
|
8247
|
-
if (
|
|
8248
|
-
settings = JSON.parse(
|
|
8498
|
+
if (fs30.existsSync(settingsPath)) {
|
|
8499
|
+
settings = JSON.parse(fs30.readFileSync(settingsPath, "utf-8"));
|
|
8249
8500
|
}
|
|
8250
8501
|
} catch {
|
|
8251
8502
|
}
|
|
@@ -8254,12 +8505,12 @@ function ensurePermissions(fingerprint) {
|
|
|
8254
8505
|
if (Array.isArray(allow) && allow.length > 0) return;
|
|
8255
8506
|
permissions.allow = derivePermissions(fingerprint);
|
|
8256
8507
|
settings.permissions = permissions;
|
|
8257
|
-
if (!
|
|
8258
|
-
|
|
8508
|
+
if (!fs30.existsSync(".claude")) fs30.mkdirSync(".claude", { recursive: true });
|
|
8509
|
+
fs30.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
8259
8510
|
}
|
|
8260
8511
|
function writeErrorLog(config, rawOutput, error, stopReason) {
|
|
8261
8512
|
try {
|
|
8262
|
-
const logPath =
|
|
8513
|
+
const logPath = path23.join(process.cwd(), ".caliber", "error-log.md");
|
|
8263
8514
|
const lines = [
|
|
8264
8515
|
`# Generation Error \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
8265
8516
|
"",
|
|
@@ -8272,8 +8523,8 @@ function writeErrorLog(config, rawOutput, error, stopReason) {
|
|
|
8272
8523
|
lines.push("## Error", "```", error, "```", "");
|
|
8273
8524
|
}
|
|
8274
8525
|
lines.push("## Raw LLM Output", "```", rawOutput || "(empty)", "```");
|
|
8275
|
-
|
|
8276
|
-
|
|
8526
|
+
fs30.mkdirSync(path23.join(process.cwd(), ".caliber"), { recursive: true });
|
|
8527
|
+
fs30.writeFileSync(logPath, lines.join("\n"));
|
|
8277
8528
|
console.log(chalk13.dim(`
|
|
8278
8529
|
Error log written to .caliber/error-log.md`));
|
|
8279
8530
|
} catch {
|
|
@@ -8711,7 +8962,7 @@ async function initCommand(options) {
|
|
|
8711
8962
|
const { default: ora9 } = await import("ora");
|
|
8712
8963
|
const writeSpinner = ora9("Writing config files...").start();
|
|
8713
8964
|
try {
|
|
8714
|
-
if (targetAgent.includes("codex") && !
|
|
8965
|
+
if (targetAgent.includes("codex") && !fs31.existsSync("AGENTS.md") && !generatedSetup.codex) {
|
|
8715
8966
|
const claude = generatedSetup.claude;
|
|
8716
8967
|
const cursor = generatedSetup.cursor;
|
|
8717
8968
|
const agentRefs = [];
|
|
@@ -8860,9 +9111,9 @@ ${agentRefs.join(" ")}
|
|
|
8860
9111
|
}
|
|
8861
9112
|
if (report) {
|
|
8862
9113
|
report.markStep("Finished");
|
|
8863
|
-
const reportPath =
|
|
9114
|
+
const reportPath = path24.join(process.cwd(), ".caliber", "debug-report.md");
|
|
8864
9115
|
report.write(reportPath);
|
|
8865
|
-
console.log(chalk14.dim(` Debug report written to ${
|
|
9116
|
+
console.log(chalk14.dim(` Debug report written to ${path24.relative(process.cwd(), reportPath)}
|
|
8866
9117
|
`));
|
|
8867
9118
|
}
|
|
8868
9119
|
}
|
|
@@ -8901,7 +9152,7 @@ function undoCommand() {
|
|
|
8901
9152
|
|
|
8902
9153
|
// src/commands/status.ts
|
|
8903
9154
|
import chalk16 from "chalk";
|
|
8904
|
-
import
|
|
9155
|
+
import fs32 from "fs";
|
|
8905
9156
|
init_config();
|
|
8906
9157
|
async function statusCommand(options) {
|
|
8907
9158
|
const config = loadConfig();
|
|
@@ -8928,7 +9179,7 @@ async function statusCommand(options) {
|
|
|
8928
9179
|
}
|
|
8929
9180
|
console.log(` Files managed: ${chalk16.cyan(manifest.entries.length.toString())}`);
|
|
8930
9181
|
for (const entry of manifest.entries) {
|
|
8931
|
-
const exists =
|
|
9182
|
+
const exists = fs32.existsSync(entry.path);
|
|
8932
9183
|
const icon = exists ? chalk16.green("\u2713") : chalk16.red("\u2717");
|
|
8933
9184
|
console.log(` ${icon} ${entry.path} (${entry.action})`);
|
|
8934
9185
|
}
|
|
@@ -9083,21 +9334,21 @@ async function regenerateCommand(options) {
|
|
|
9083
9334
|
}
|
|
9084
9335
|
|
|
9085
9336
|
// src/commands/score.ts
|
|
9086
|
-
import
|
|
9337
|
+
import fs33 from "fs";
|
|
9087
9338
|
import os6 from "os";
|
|
9088
|
-
import
|
|
9339
|
+
import path25 from "path";
|
|
9089
9340
|
import { execFileSync } from "child_process";
|
|
9090
9341
|
import chalk18 from "chalk";
|
|
9091
9342
|
var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".cursorrules", "CALIBER_LEARNINGS.md"];
|
|
9092
9343
|
var CONFIG_DIRS = [".claude", ".cursor"];
|
|
9093
9344
|
function scoreBaseRef(ref, target) {
|
|
9094
9345
|
if (!/^[\w.\-\/~^@{}]+$/.test(ref)) return null;
|
|
9095
|
-
const tmpDir =
|
|
9346
|
+
const tmpDir = fs33.mkdtempSync(path25.join(os6.tmpdir(), "caliber-compare-"));
|
|
9096
9347
|
try {
|
|
9097
9348
|
for (const file of CONFIG_FILES) {
|
|
9098
9349
|
try {
|
|
9099
9350
|
const content = execFileSync("git", ["show", `${ref}:${file}`], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
9100
|
-
|
|
9351
|
+
fs33.writeFileSync(path25.join(tmpDir, file), content);
|
|
9101
9352
|
} catch {
|
|
9102
9353
|
}
|
|
9103
9354
|
}
|
|
@@ -9105,10 +9356,10 @@ function scoreBaseRef(ref, target) {
|
|
|
9105
9356
|
try {
|
|
9106
9357
|
const files = execFileSync("git", ["ls-tree", "-r", "--name-only", ref, `${dir}/`], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim().split("\n").filter(Boolean);
|
|
9107
9358
|
for (const file of files) {
|
|
9108
|
-
const filePath =
|
|
9109
|
-
|
|
9359
|
+
const filePath = path25.join(tmpDir, file);
|
|
9360
|
+
fs33.mkdirSync(path25.dirname(filePath), { recursive: true });
|
|
9110
9361
|
const content = execFileSync("git", ["show", `${ref}:${file}`], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
9111
|
-
|
|
9362
|
+
fs33.writeFileSync(filePath, content);
|
|
9112
9363
|
}
|
|
9113
9364
|
} catch {
|
|
9114
9365
|
}
|
|
@@ -9118,7 +9369,7 @@ function scoreBaseRef(ref, target) {
|
|
|
9118
9369
|
} catch {
|
|
9119
9370
|
return null;
|
|
9120
9371
|
} finally {
|
|
9121
|
-
|
|
9372
|
+
fs33.rmSync(tmpDir, { recursive: true, force: true });
|
|
9122
9373
|
}
|
|
9123
9374
|
}
|
|
9124
9375
|
async function scoreCommand(options) {
|
|
@@ -9178,8 +9429,8 @@ async function scoreCommand(options) {
|
|
|
9178
9429
|
}
|
|
9179
9430
|
|
|
9180
9431
|
// src/commands/refresh.ts
|
|
9181
|
-
import
|
|
9182
|
-
import
|
|
9432
|
+
import fs37 from "fs";
|
|
9433
|
+
import path29 from "path";
|
|
9183
9434
|
import chalk19 from "chalk";
|
|
9184
9435
|
import ora6 from "ora";
|
|
9185
9436
|
|
|
@@ -9257,48 +9508,48 @@ function collectDiff(lastSha) {
|
|
|
9257
9508
|
}
|
|
9258
9509
|
|
|
9259
9510
|
// src/writers/refresh.ts
|
|
9260
|
-
import
|
|
9261
|
-
import
|
|
9511
|
+
import fs34 from "fs";
|
|
9512
|
+
import path26 from "path";
|
|
9262
9513
|
function writeRefreshDocs(docs) {
|
|
9263
9514
|
const written = [];
|
|
9264
9515
|
if (docs.claudeMd) {
|
|
9265
|
-
|
|
9516
|
+
fs34.writeFileSync("CLAUDE.md", appendLearningsBlock(appendPreCommitBlock(docs.claudeMd)));
|
|
9266
9517
|
written.push("CLAUDE.md");
|
|
9267
9518
|
}
|
|
9268
9519
|
if (docs.readmeMd) {
|
|
9269
|
-
|
|
9520
|
+
fs34.writeFileSync("README.md", docs.readmeMd);
|
|
9270
9521
|
written.push("README.md");
|
|
9271
9522
|
}
|
|
9272
9523
|
if (docs.cursorrules) {
|
|
9273
|
-
|
|
9524
|
+
fs34.writeFileSync(".cursorrules", docs.cursorrules);
|
|
9274
9525
|
written.push(".cursorrules");
|
|
9275
9526
|
}
|
|
9276
9527
|
if (docs.cursorRules) {
|
|
9277
|
-
const rulesDir =
|
|
9278
|
-
if (!
|
|
9528
|
+
const rulesDir = path26.join(".cursor", "rules");
|
|
9529
|
+
if (!fs34.existsSync(rulesDir)) fs34.mkdirSync(rulesDir, { recursive: true });
|
|
9279
9530
|
for (const rule of docs.cursorRules) {
|
|
9280
|
-
|
|
9531
|
+
fs34.writeFileSync(path26.join(rulesDir, rule.filename), rule.content);
|
|
9281
9532
|
written.push(`.cursor/rules/${rule.filename}`);
|
|
9282
9533
|
}
|
|
9283
9534
|
}
|
|
9284
9535
|
if (docs.claudeSkills) {
|
|
9285
|
-
const skillsDir =
|
|
9286
|
-
if (!
|
|
9536
|
+
const skillsDir = path26.join(".claude", "skills");
|
|
9537
|
+
if (!fs34.existsSync(skillsDir)) fs34.mkdirSync(skillsDir, { recursive: true });
|
|
9287
9538
|
for (const skill of docs.claudeSkills) {
|
|
9288
|
-
|
|
9539
|
+
fs34.writeFileSync(path26.join(skillsDir, skill.filename), skill.content);
|
|
9289
9540
|
written.push(`.claude/skills/${skill.filename}`);
|
|
9290
9541
|
}
|
|
9291
9542
|
}
|
|
9292
9543
|
if (docs.copilotInstructions) {
|
|
9293
|
-
|
|
9294
|
-
|
|
9544
|
+
fs34.mkdirSync(".github", { recursive: true });
|
|
9545
|
+
fs34.writeFileSync(path26.join(".github", "copilot-instructions.md"), appendLearningsBlock(appendPreCommitBlock(docs.copilotInstructions)));
|
|
9295
9546
|
written.push(".github/copilot-instructions.md");
|
|
9296
9547
|
}
|
|
9297
9548
|
if (docs.copilotInstructionFiles) {
|
|
9298
|
-
const instructionsDir =
|
|
9299
|
-
|
|
9549
|
+
const instructionsDir = path26.join(".github", "instructions");
|
|
9550
|
+
fs34.mkdirSync(instructionsDir, { recursive: true });
|
|
9300
9551
|
for (const file of docs.copilotInstructionFiles) {
|
|
9301
|
-
|
|
9552
|
+
fs34.writeFileSync(path26.join(instructionsDir, file.filename), file.content);
|
|
9302
9553
|
written.push(`.github/instructions/${file.filename}`);
|
|
9303
9554
|
}
|
|
9304
9555
|
}
|
|
@@ -9378,8 +9629,8 @@ Changed files: ${diff.changedFiles.join(", ")}`);
|
|
|
9378
9629
|
}
|
|
9379
9630
|
|
|
9380
9631
|
// src/learner/writer.ts
|
|
9381
|
-
import
|
|
9382
|
-
import
|
|
9632
|
+
import fs35 from "fs";
|
|
9633
|
+
import path27 from "path";
|
|
9383
9634
|
|
|
9384
9635
|
// src/learner/utils.ts
|
|
9385
9636
|
var TYPE_PREFIX_RE = /^\*\*\[[^\]]+\]\*\*\s*/;
|
|
@@ -9497,20 +9748,20 @@ function deduplicateLearnedItems(existing, incoming) {
|
|
|
9497
9748
|
}
|
|
9498
9749
|
function writeLearnedSectionTo(filePath, header, existing, incoming, mode) {
|
|
9499
9750
|
const { merged, newCount, newItems } = deduplicateLearnedItems(existing, incoming);
|
|
9500
|
-
|
|
9501
|
-
if (mode)
|
|
9751
|
+
fs35.writeFileSync(filePath, header + merged + "\n");
|
|
9752
|
+
if (mode) fs35.chmodSync(filePath, mode);
|
|
9502
9753
|
return { newCount, newItems };
|
|
9503
9754
|
}
|
|
9504
9755
|
function writeLearnedSection(content) {
|
|
9505
9756
|
return writeLearnedSectionTo(LEARNINGS_FILE, LEARNINGS_HEADER, readLearnedSection(), content);
|
|
9506
9757
|
}
|
|
9507
9758
|
function writeLearnedSkill(skill) {
|
|
9508
|
-
const skillDir =
|
|
9509
|
-
if (!
|
|
9510
|
-
const skillPath =
|
|
9511
|
-
if (!skill.isNew &&
|
|
9512
|
-
const existing =
|
|
9513
|
-
|
|
9759
|
+
const skillDir = path27.join(".claude", "skills", skill.name);
|
|
9760
|
+
if (!fs35.existsSync(skillDir)) fs35.mkdirSync(skillDir, { recursive: true });
|
|
9761
|
+
const skillPath = path27.join(skillDir, "SKILL.md");
|
|
9762
|
+
if (!skill.isNew && fs35.existsSync(skillPath)) {
|
|
9763
|
+
const existing = fs35.readFileSync(skillPath, "utf-8");
|
|
9764
|
+
fs35.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
|
|
9514
9765
|
} else {
|
|
9515
9766
|
const frontmatter = [
|
|
9516
9767
|
"---",
|
|
@@ -9519,47 +9770,56 @@ function writeLearnedSkill(skill) {
|
|
|
9519
9770
|
"---",
|
|
9520
9771
|
""
|
|
9521
9772
|
].join("\n");
|
|
9522
|
-
|
|
9773
|
+
fs35.writeFileSync(skillPath, frontmatter + skill.content);
|
|
9523
9774
|
}
|
|
9524
9775
|
return skillPath;
|
|
9525
9776
|
}
|
|
9526
9777
|
function writePersonalLearnedSection(content) {
|
|
9527
|
-
if (!
|
|
9778
|
+
if (!fs35.existsSync(AUTH_DIR)) fs35.mkdirSync(AUTH_DIR, { recursive: true });
|
|
9528
9779
|
return writeLearnedSectionTo(PERSONAL_LEARNINGS_FILE, PERSONAL_LEARNINGS_HEADER, readPersonalLearnings(), content, 384);
|
|
9529
9780
|
}
|
|
9781
|
+
function addLearning(bullet, scope = "project") {
|
|
9782
|
+
const formatted = bullet.startsWith("- ") ? bullet : `- ${bullet}`;
|
|
9783
|
+
if (scope === "personal") {
|
|
9784
|
+
const result2 = writePersonalLearnedSection(formatted);
|
|
9785
|
+
return { file: PERSONAL_LEARNINGS_FILE, added: result2.newCount > 0 };
|
|
9786
|
+
}
|
|
9787
|
+
const result = writeLearnedSection(formatted);
|
|
9788
|
+
return { file: LEARNINGS_FILE, added: result.newCount > 0 };
|
|
9789
|
+
}
|
|
9530
9790
|
function readPersonalLearnings() {
|
|
9531
|
-
if (!
|
|
9532
|
-
const content =
|
|
9791
|
+
if (!fs35.existsSync(PERSONAL_LEARNINGS_FILE)) return null;
|
|
9792
|
+
const content = fs35.readFileSync(PERSONAL_LEARNINGS_FILE, "utf-8");
|
|
9533
9793
|
const bullets = content.split("\n").filter((l) => l.startsWith("- ")).join("\n");
|
|
9534
9794
|
return bullets || null;
|
|
9535
9795
|
}
|
|
9536
9796
|
function readLearnedSection() {
|
|
9537
|
-
if (
|
|
9538
|
-
const content2 =
|
|
9797
|
+
if (fs35.existsSync(LEARNINGS_FILE)) {
|
|
9798
|
+
const content2 = fs35.readFileSync(LEARNINGS_FILE, "utf-8");
|
|
9539
9799
|
const bullets = content2.split("\n").filter((l) => l.startsWith("- ")).join("\n");
|
|
9540
9800
|
return bullets || null;
|
|
9541
9801
|
}
|
|
9542
9802
|
const claudeMdPath = "CLAUDE.md";
|
|
9543
|
-
if (!
|
|
9544
|
-
const content =
|
|
9803
|
+
if (!fs35.existsSync(claudeMdPath)) return null;
|
|
9804
|
+
const content = fs35.readFileSync(claudeMdPath, "utf-8");
|
|
9545
9805
|
const startIdx = content.indexOf(LEARNED_START);
|
|
9546
9806
|
const endIdx = content.indexOf(LEARNED_END);
|
|
9547
9807
|
if (startIdx === -1 || endIdx === -1) return null;
|
|
9548
9808
|
return content.slice(startIdx + LEARNED_START.length, endIdx).trim() || null;
|
|
9549
9809
|
}
|
|
9550
9810
|
function migrateInlineLearnings() {
|
|
9551
|
-
if (
|
|
9811
|
+
if (fs35.existsSync(LEARNINGS_FILE)) return false;
|
|
9552
9812
|
const claudeMdPath = "CLAUDE.md";
|
|
9553
|
-
if (!
|
|
9554
|
-
const content =
|
|
9813
|
+
if (!fs35.existsSync(claudeMdPath)) return false;
|
|
9814
|
+
const content = fs35.readFileSync(claudeMdPath, "utf-8");
|
|
9555
9815
|
const startIdx = content.indexOf(LEARNED_START);
|
|
9556
9816
|
const endIdx = content.indexOf(LEARNED_END);
|
|
9557
9817
|
if (startIdx === -1 || endIdx === -1) return false;
|
|
9558
9818
|
const section = content.slice(startIdx + LEARNED_START.length, endIdx).trim();
|
|
9559
9819
|
if (!section) return false;
|
|
9560
|
-
|
|
9820
|
+
fs35.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
|
|
9561
9821
|
const cleaned = content.slice(0, startIdx) + content.slice(endIdx + LEARNED_END.length);
|
|
9562
|
-
|
|
9822
|
+
fs35.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
|
|
9563
9823
|
return true;
|
|
9564
9824
|
}
|
|
9565
9825
|
|
|
@@ -9571,11 +9831,11 @@ function log2(quiet, ...args) {
|
|
|
9571
9831
|
function discoverGitRepos(parentDir) {
|
|
9572
9832
|
const repos = [];
|
|
9573
9833
|
try {
|
|
9574
|
-
const entries =
|
|
9834
|
+
const entries = fs37.readdirSync(parentDir, { withFileTypes: true });
|
|
9575
9835
|
for (const entry of entries) {
|
|
9576
9836
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
9577
|
-
const childPath =
|
|
9578
|
-
if (
|
|
9837
|
+
const childPath = path29.join(parentDir, entry.name);
|
|
9838
|
+
if (fs37.existsSync(path29.join(childPath, ".git"))) {
|
|
9579
9839
|
repos.push(childPath);
|
|
9580
9840
|
}
|
|
9581
9841
|
}
|
|
@@ -9649,6 +9909,10 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
9649
9909
|
log2(quiet, chalk19.dim(`
|
|
9650
9910
|
${response.changesSummary}`));
|
|
9651
9911
|
}
|
|
9912
|
+
const builtinWritten = ensureBuiltinSkills();
|
|
9913
|
+
for (const file of builtinWritten) {
|
|
9914
|
+
log2(quiet, ` ${chalk19.green("\u2713")} ${file} ${chalk19.dim("(built-in)")}`);
|
|
9915
|
+
}
|
|
9652
9916
|
if (currentSha) {
|
|
9653
9917
|
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
9654
9918
|
}
|
|
@@ -9681,7 +9945,7 @@ async function refreshCommand(options) {
|
|
|
9681
9945
|
`));
|
|
9682
9946
|
const originalDir = process.cwd();
|
|
9683
9947
|
for (const repo of repos) {
|
|
9684
|
-
const repoName =
|
|
9948
|
+
const repoName = path29.basename(repo);
|
|
9685
9949
|
try {
|
|
9686
9950
|
process.chdir(repo);
|
|
9687
9951
|
await refreshSingleRepo(repo, { ...options, label: repoName });
|
|
@@ -9702,31 +9966,31 @@ async function refreshCommand(options) {
|
|
|
9702
9966
|
|
|
9703
9967
|
// src/commands/hooks.ts
|
|
9704
9968
|
import chalk20 from "chalk";
|
|
9705
|
-
import
|
|
9969
|
+
import fs39 from "fs";
|
|
9706
9970
|
|
|
9707
9971
|
// src/lib/hooks.ts
|
|
9708
9972
|
init_resolve_caliber();
|
|
9709
|
-
import
|
|
9710
|
-
import
|
|
9973
|
+
import fs38 from "fs";
|
|
9974
|
+
import path30 from "path";
|
|
9711
9975
|
import { execSync as execSync14 } from "child_process";
|
|
9712
|
-
var SETTINGS_PATH2 =
|
|
9976
|
+
var SETTINGS_PATH2 = path30.join(".claude", "settings.json");
|
|
9713
9977
|
var REFRESH_TAIL = "refresh --quiet";
|
|
9714
9978
|
var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
|
|
9715
9979
|
function getHookCommand() {
|
|
9716
9980
|
return `${resolveCaliber()} ${REFRESH_TAIL}`;
|
|
9717
9981
|
}
|
|
9718
9982
|
function readSettings2() {
|
|
9719
|
-
if (!
|
|
9983
|
+
if (!fs38.existsSync(SETTINGS_PATH2)) return {};
|
|
9720
9984
|
try {
|
|
9721
|
-
return JSON.parse(
|
|
9985
|
+
return JSON.parse(fs38.readFileSync(SETTINGS_PATH2, "utf-8"));
|
|
9722
9986
|
} catch {
|
|
9723
9987
|
return {};
|
|
9724
9988
|
}
|
|
9725
9989
|
}
|
|
9726
9990
|
function writeSettings2(settings) {
|
|
9727
|
-
const dir =
|
|
9728
|
-
if (!
|
|
9729
|
-
|
|
9991
|
+
const dir = path30.dirname(SETTINGS_PATH2);
|
|
9992
|
+
if (!fs38.existsSync(dir)) fs38.mkdirSync(dir, { recursive: true });
|
|
9993
|
+
fs38.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
|
|
9730
9994
|
}
|
|
9731
9995
|
function findHookIndex(sessionEnd) {
|
|
9732
9996
|
return sessionEnd.findIndex(
|
|
@@ -9789,19 +10053,19 @@ ${PRECOMMIT_END}`;
|
|
|
9789
10053
|
function getGitHooksDir() {
|
|
9790
10054
|
try {
|
|
9791
10055
|
const gitDir = execSync14("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
9792
|
-
return
|
|
10056
|
+
return path30.join(gitDir, "hooks");
|
|
9793
10057
|
} catch {
|
|
9794
10058
|
return null;
|
|
9795
10059
|
}
|
|
9796
10060
|
}
|
|
9797
10061
|
function getPreCommitPath() {
|
|
9798
10062
|
const hooksDir = getGitHooksDir();
|
|
9799
|
-
return hooksDir ?
|
|
10063
|
+
return hooksDir ? path30.join(hooksDir, "pre-commit") : null;
|
|
9800
10064
|
}
|
|
9801
10065
|
function isPreCommitHookInstalled() {
|
|
9802
10066
|
const hookPath = getPreCommitPath();
|
|
9803
|
-
if (!hookPath || !
|
|
9804
|
-
const content =
|
|
10067
|
+
if (!hookPath || !fs38.existsSync(hookPath)) return false;
|
|
10068
|
+
const content = fs38.readFileSync(hookPath, "utf-8");
|
|
9805
10069
|
return content.includes(PRECOMMIT_START);
|
|
9806
10070
|
}
|
|
9807
10071
|
function installPreCommitHook() {
|
|
@@ -9810,35 +10074,35 @@ function installPreCommitHook() {
|
|
|
9810
10074
|
}
|
|
9811
10075
|
const hookPath = getPreCommitPath();
|
|
9812
10076
|
if (!hookPath) return { installed: false, alreadyInstalled: false };
|
|
9813
|
-
const hooksDir =
|
|
9814
|
-
if (!
|
|
10077
|
+
const hooksDir = path30.dirname(hookPath);
|
|
10078
|
+
if (!fs38.existsSync(hooksDir)) fs38.mkdirSync(hooksDir, { recursive: true });
|
|
9815
10079
|
let content = "";
|
|
9816
|
-
if (
|
|
9817
|
-
content =
|
|
10080
|
+
if (fs38.existsSync(hookPath)) {
|
|
10081
|
+
content = fs38.readFileSync(hookPath, "utf-8");
|
|
9818
10082
|
if (!content.endsWith("\n")) content += "\n";
|
|
9819
10083
|
content += "\n" + getPrecommitBlock() + "\n";
|
|
9820
10084
|
} else {
|
|
9821
10085
|
content = "#!/bin/sh\n\n" + getPrecommitBlock() + "\n";
|
|
9822
10086
|
}
|
|
9823
|
-
|
|
9824
|
-
|
|
10087
|
+
fs38.writeFileSync(hookPath, content);
|
|
10088
|
+
fs38.chmodSync(hookPath, 493);
|
|
9825
10089
|
return { installed: true, alreadyInstalled: false };
|
|
9826
10090
|
}
|
|
9827
10091
|
function removePreCommitHook() {
|
|
9828
10092
|
const hookPath = getPreCommitPath();
|
|
9829
|
-
if (!hookPath || !
|
|
10093
|
+
if (!hookPath || !fs38.existsSync(hookPath)) {
|
|
9830
10094
|
return { removed: false, notFound: true };
|
|
9831
10095
|
}
|
|
9832
|
-
let content =
|
|
10096
|
+
let content = fs38.readFileSync(hookPath, "utf-8");
|
|
9833
10097
|
if (!content.includes(PRECOMMIT_START)) {
|
|
9834
10098
|
return { removed: false, notFound: true };
|
|
9835
10099
|
}
|
|
9836
10100
|
const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
|
|
9837
10101
|
content = content.replace(regex, "\n");
|
|
9838
10102
|
if (content.trim() === "#!/bin/sh" || content.trim() === "") {
|
|
9839
|
-
|
|
10103
|
+
fs38.unlinkSync(hookPath);
|
|
9840
10104
|
} else {
|
|
9841
|
-
|
|
10105
|
+
fs38.writeFileSync(hookPath, content);
|
|
9842
10106
|
}
|
|
9843
10107
|
return { removed: true, notFound: false };
|
|
9844
10108
|
}
|
|
@@ -9887,11 +10151,11 @@ async function hooksCommand(options) {
|
|
|
9887
10151
|
console.log(chalk20.green(" \u2713") + ` ${hook.label} enabled`);
|
|
9888
10152
|
}
|
|
9889
10153
|
}
|
|
9890
|
-
if (
|
|
10154
|
+
if (fs39.existsSync(".claude")) {
|
|
9891
10155
|
const r = installLearningHooks();
|
|
9892
10156
|
if (r.installed) console.log(chalk20.green(" \u2713") + " Claude Code learning hooks enabled");
|
|
9893
10157
|
}
|
|
9894
|
-
if (
|
|
10158
|
+
if (fs39.existsSync(".cursor")) {
|
|
9895
10159
|
const r = installCursorLearningHooks();
|
|
9896
10160
|
if (r.installed) console.log(chalk20.green(" \u2713") + " Cursor learning hooks enabled");
|
|
9897
10161
|
}
|
|
@@ -10059,8 +10323,8 @@ async function configCommand() {
|
|
|
10059
10323
|
}
|
|
10060
10324
|
|
|
10061
10325
|
// src/commands/learn.ts
|
|
10062
|
-
import
|
|
10063
|
-
import
|
|
10326
|
+
import fs43 from "fs";
|
|
10327
|
+
import path34 from "path";
|
|
10064
10328
|
import chalk23 from "chalk";
|
|
10065
10329
|
|
|
10066
10330
|
// src/learner/stdin.ts
|
|
@@ -10092,8 +10356,8 @@ function readStdin() {
|
|
|
10092
10356
|
|
|
10093
10357
|
// src/learner/storage.ts
|
|
10094
10358
|
init_constants();
|
|
10095
|
-
import
|
|
10096
|
-
import
|
|
10359
|
+
import fs40 from "fs";
|
|
10360
|
+
import path31 from "path";
|
|
10097
10361
|
var MAX_RESPONSE_LENGTH = 2e3;
|
|
10098
10362
|
var DEFAULT_STATE = {
|
|
10099
10363
|
sessionId: null,
|
|
@@ -10102,15 +10366,15 @@ var DEFAULT_STATE = {
|
|
|
10102
10366
|
lastAnalysisEventCount: 0
|
|
10103
10367
|
};
|
|
10104
10368
|
function ensureLearningDir() {
|
|
10105
|
-
if (!
|
|
10106
|
-
|
|
10369
|
+
if (!fs40.existsSync(LEARNING_DIR)) {
|
|
10370
|
+
fs40.mkdirSync(LEARNING_DIR, { recursive: true });
|
|
10107
10371
|
}
|
|
10108
10372
|
}
|
|
10109
10373
|
function sessionFilePath() {
|
|
10110
|
-
return
|
|
10374
|
+
return path31.join(LEARNING_DIR, LEARNING_SESSION_FILE);
|
|
10111
10375
|
}
|
|
10112
10376
|
function stateFilePath() {
|
|
10113
|
-
return
|
|
10377
|
+
return path31.join(LEARNING_DIR, LEARNING_STATE_FILE);
|
|
10114
10378
|
}
|
|
10115
10379
|
function truncateResponse(response) {
|
|
10116
10380
|
const str = JSON.stringify(response);
|
|
@@ -10120,10 +10384,10 @@ function truncateResponse(response) {
|
|
|
10120
10384
|
function trimSessionFileIfNeeded(filePath) {
|
|
10121
10385
|
const state = readState2();
|
|
10122
10386
|
if (state.eventCount + 1 > LEARNING_MAX_EVENTS) {
|
|
10123
|
-
const lines =
|
|
10387
|
+
const lines = fs40.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
10124
10388
|
if (lines.length > LEARNING_MAX_EVENTS) {
|
|
10125
10389
|
const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
|
|
10126
|
-
|
|
10390
|
+
fs40.writeFileSync(filePath, kept.join("\n") + "\n");
|
|
10127
10391
|
}
|
|
10128
10392
|
}
|
|
10129
10393
|
}
|
|
@@ -10131,19 +10395,19 @@ function appendEvent(event) {
|
|
|
10131
10395
|
ensureLearningDir();
|
|
10132
10396
|
const truncated = { ...event, tool_response: truncateResponse(event.tool_response) };
|
|
10133
10397
|
const filePath = sessionFilePath();
|
|
10134
|
-
|
|
10398
|
+
fs40.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
|
|
10135
10399
|
trimSessionFileIfNeeded(filePath);
|
|
10136
10400
|
}
|
|
10137
10401
|
function appendPromptEvent(event) {
|
|
10138
10402
|
ensureLearningDir();
|
|
10139
10403
|
const filePath = sessionFilePath();
|
|
10140
|
-
|
|
10404
|
+
fs40.appendFileSync(filePath, JSON.stringify(event) + "\n");
|
|
10141
10405
|
trimSessionFileIfNeeded(filePath);
|
|
10142
10406
|
}
|
|
10143
10407
|
function readAllEvents() {
|
|
10144
10408
|
const filePath = sessionFilePath();
|
|
10145
|
-
if (!
|
|
10146
|
-
const lines =
|
|
10409
|
+
if (!fs40.existsSync(filePath)) return [];
|
|
10410
|
+
const lines = fs40.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
10147
10411
|
const events = [];
|
|
10148
10412
|
for (const line of lines) {
|
|
10149
10413
|
try {
|
|
@@ -10155,26 +10419,26 @@ function readAllEvents() {
|
|
|
10155
10419
|
}
|
|
10156
10420
|
function getEventCount() {
|
|
10157
10421
|
const filePath = sessionFilePath();
|
|
10158
|
-
if (!
|
|
10159
|
-
const content =
|
|
10422
|
+
if (!fs40.existsSync(filePath)) return 0;
|
|
10423
|
+
const content = fs40.readFileSync(filePath, "utf-8");
|
|
10160
10424
|
return content.split("\n").filter(Boolean).length;
|
|
10161
10425
|
}
|
|
10162
10426
|
function clearSession() {
|
|
10163
10427
|
const filePath = sessionFilePath();
|
|
10164
|
-
if (
|
|
10428
|
+
if (fs40.existsSync(filePath)) fs40.unlinkSync(filePath);
|
|
10165
10429
|
}
|
|
10166
10430
|
function readState2() {
|
|
10167
10431
|
const filePath = stateFilePath();
|
|
10168
|
-
if (!
|
|
10432
|
+
if (!fs40.existsSync(filePath)) return { ...DEFAULT_STATE };
|
|
10169
10433
|
try {
|
|
10170
|
-
return JSON.parse(
|
|
10434
|
+
return JSON.parse(fs40.readFileSync(filePath, "utf-8"));
|
|
10171
10435
|
} catch {
|
|
10172
10436
|
return { ...DEFAULT_STATE };
|
|
10173
10437
|
}
|
|
10174
10438
|
}
|
|
10175
10439
|
function writeState2(state) {
|
|
10176
10440
|
ensureLearningDir();
|
|
10177
|
-
|
|
10441
|
+
fs40.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
|
|
10178
10442
|
}
|
|
10179
10443
|
function resetState() {
|
|
10180
10444
|
writeState2({ ...DEFAULT_STATE });
|
|
@@ -10182,16 +10446,16 @@ function resetState() {
|
|
|
10182
10446
|
var LOCK_FILE2 = "finalize.lock";
|
|
10183
10447
|
var LOCK_STALE_MS = 5 * 60 * 1e3;
|
|
10184
10448
|
function lockFilePath() {
|
|
10185
|
-
return
|
|
10449
|
+
return path31.join(LEARNING_DIR, LOCK_FILE2);
|
|
10186
10450
|
}
|
|
10187
10451
|
function acquireFinalizeLock() {
|
|
10188
10452
|
ensureLearningDir();
|
|
10189
10453
|
const lockPath = lockFilePath();
|
|
10190
|
-
if (
|
|
10454
|
+
if (fs40.existsSync(lockPath)) {
|
|
10191
10455
|
try {
|
|
10192
|
-
const stat =
|
|
10456
|
+
const stat = fs40.statSync(lockPath);
|
|
10193
10457
|
if (Date.now() - stat.mtimeMs < LOCK_STALE_MS) {
|
|
10194
|
-
const pid = parseInt(
|
|
10458
|
+
const pid = parseInt(fs40.readFileSync(lockPath, "utf-8").trim(), 10);
|
|
10195
10459
|
if (!isNaN(pid) && isProcessAlive(pid)) {
|
|
10196
10460
|
return false;
|
|
10197
10461
|
}
|
|
@@ -10199,12 +10463,12 @@ function acquireFinalizeLock() {
|
|
|
10199
10463
|
} catch {
|
|
10200
10464
|
}
|
|
10201
10465
|
try {
|
|
10202
|
-
|
|
10466
|
+
fs40.unlinkSync(lockPath);
|
|
10203
10467
|
} catch {
|
|
10204
10468
|
}
|
|
10205
10469
|
}
|
|
10206
10470
|
try {
|
|
10207
|
-
|
|
10471
|
+
fs40.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
10208
10472
|
return true;
|
|
10209
10473
|
} catch {
|
|
10210
10474
|
return false;
|
|
@@ -10221,7 +10485,7 @@ function isProcessAlive(pid) {
|
|
|
10221
10485
|
function releaseFinalizeLock() {
|
|
10222
10486
|
const lockPath = lockFilePath();
|
|
10223
10487
|
try {
|
|
10224
|
-
if (
|
|
10488
|
+
if (fs40.existsSync(lockPath)) fs40.unlinkSync(lockPath);
|
|
10225
10489
|
} catch {
|
|
10226
10490
|
}
|
|
10227
10491
|
}
|
|
@@ -10267,22 +10531,22 @@ function sanitizeSecrets(text) {
|
|
|
10267
10531
|
|
|
10268
10532
|
// src/lib/notifications.ts
|
|
10269
10533
|
init_constants();
|
|
10270
|
-
import
|
|
10271
|
-
import
|
|
10534
|
+
import fs41 from "fs";
|
|
10535
|
+
import path32 from "path";
|
|
10272
10536
|
import chalk22 from "chalk";
|
|
10273
|
-
var NOTIFICATION_FILE =
|
|
10537
|
+
var NOTIFICATION_FILE = path32.join(LEARNING_DIR, "last-finalize-summary.json");
|
|
10274
10538
|
function writeFinalizeSummary(summary) {
|
|
10275
10539
|
try {
|
|
10276
10540
|
ensureLearningDir();
|
|
10277
|
-
|
|
10541
|
+
fs41.writeFileSync(NOTIFICATION_FILE, JSON.stringify(summary, null, 2));
|
|
10278
10542
|
} catch {
|
|
10279
10543
|
}
|
|
10280
10544
|
}
|
|
10281
10545
|
function checkPendingNotifications() {
|
|
10282
10546
|
try {
|
|
10283
|
-
if (!
|
|
10284
|
-
const raw =
|
|
10285
|
-
|
|
10547
|
+
if (!fs41.existsSync(NOTIFICATION_FILE)) return;
|
|
10548
|
+
const raw = fs41.readFileSync(NOTIFICATION_FILE, "utf-8");
|
|
10549
|
+
fs41.unlinkSync(NOTIFICATION_FILE);
|
|
10286
10550
|
const summary = JSON.parse(raw);
|
|
10287
10551
|
if (!summary.newItemCount || summary.newItemCount === 0) return;
|
|
10288
10552
|
const wasteLabel = summary.wasteTokens > 0 ? ` (~${summary.wasteTokens.toLocaleString()} wasted tokens captured)` : "";
|
|
@@ -10298,7 +10562,7 @@ function checkPendingNotifications() {
|
|
|
10298
10562
|
console.log("");
|
|
10299
10563
|
} catch {
|
|
10300
10564
|
try {
|
|
10301
|
-
|
|
10565
|
+
fs41.unlinkSync(NOTIFICATION_FILE);
|
|
10302
10566
|
} catch {
|
|
10303
10567
|
}
|
|
10304
10568
|
}
|
|
@@ -10436,8 +10700,8 @@ init_config();
|
|
|
10436
10700
|
|
|
10437
10701
|
// src/learner/roi.ts
|
|
10438
10702
|
init_constants();
|
|
10439
|
-
import
|
|
10440
|
-
import
|
|
10703
|
+
import fs42 from "fs";
|
|
10704
|
+
import path33 from "path";
|
|
10441
10705
|
var DEFAULT_TOTALS = {
|
|
10442
10706
|
totalWasteTokens: 0,
|
|
10443
10707
|
totalWasteSeconds: 0,
|
|
@@ -10451,19 +10715,19 @@ var DEFAULT_TOTALS = {
|
|
|
10451
10715
|
lastSessionTimestamp: ""
|
|
10452
10716
|
};
|
|
10453
10717
|
function roiFilePath() {
|
|
10454
|
-
return
|
|
10718
|
+
return path33.join(LEARNING_DIR, LEARNING_ROI_FILE);
|
|
10455
10719
|
}
|
|
10456
10720
|
function readROIStats() {
|
|
10457
10721
|
const filePath = roiFilePath();
|
|
10458
|
-
if (!
|
|
10722
|
+
if (!fs42.existsSync(filePath)) {
|
|
10459
10723
|
return { learnings: [], sessions: [], totals: { ...DEFAULT_TOTALS } };
|
|
10460
10724
|
}
|
|
10461
10725
|
try {
|
|
10462
|
-
return JSON.parse(
|
|
10726
|
+
return JSON.parse(fs42.readFileSync(filePath, "utf-8"));
|
|
10463
10727
|
} catch {
|
|
10464
10728
|
try {
|
|
10465
10729
|
const corruptPath = filePath + ".corrupt";
|
|
10466
|
-
|
|
10730
|
+
fs42.renameSync(filePath, corruptPath);
|
|
10467
10731
|
console.error(`caliber: roi-stats.json was corrupt \u2014 renamed to ${corruptPath}`);
|
|
10468
10732
|
} catch {
|
|
10469
10733
|
}
|
|
@@ -10472,7 +10736,7 @@ function readROIStats() {
|
|
|
10472
10736
|
}
|
|
10473
10737
|
function writeROIStats(stats) {
|
|
10474
10738
|
ensureLearningDir();
|
|
10475
|
-
|
|
10739
|
+
fs42.writeFileSync(roiFilePath(), JSON.stringify(stats, null, 2));
|
|
10476
10740
|
}
|
|
10477
10741
|
function recalculateTotals(stats) {
|
|
10478
10742
|
const totals = stats.totals;
|
|
@@ -10679,9 +10943,9 @@ var AUTO_SETTLE_MS = 200;
|
|
|
10679
10943
|
var INCREMENTAL_INTERVAL = 50;
|
|
10680
10944
|
function writeFinalizeError(message) {
|
|
10681
10945
|
try {
|
|
10682
|
-
const errorPath =
|
|
10683
|
-
if (!
|
|
10684
|
-
|
|
10946
|
+
const errorPath = path34.join(LEARNING_DIR, LEARNING_LAST_ERROR_FILE);
|
|
10947
|
+
if (!fs43.existsSync(LEARNING_DIR)) fs43.mkdirSync(LEARNING_DIR, { recursive: true });
|
|
10948
|
+
fs43.writeFileSync(errorPath, JSON.stringify({
|
|
10685
10949
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10686
10950
|
error: message,
|
|
10687
10951
|
pid: process.pid
|
|
@@ -10691,9 +10955,9 @@ function writeFinalizeError(message) {
|
|
|
10691
10955
|
}
|
|
10692
10956
|
function readFinalizeError() {
|
|
10693
10957
|
try {
|
|
10694
|
-
const errorPath =
|
|
10695
|
-
if (!
|
|
10696
|
-
return JSON.parse(
|
|
10958
|
+
const errorPath = path34.join(LEARNING_DIR, LEARNING_LAST_ERROR_FILE);
|
|
10959
|
+
if (!fs43.existsSync(errorPath)) return null;
|
|
10960
|
+
return JSON.parse(fs43.readFileSync(errorPath, "utf-8"));
|
|
10697
10961
|
} catch {
|
|
10698
10962
|
return null;
|
|
10699
10963
|
}
|
|
@@ -10740,14 +11004,14 @@ async function learnObserveCommand(options) {
|
|
|
10740
11004
|
const { resolveCaliber: resolveCaliber2 } = await Promise.resolve().then(() => (init_resolve_caliber(), resolve_caliber_exports));
|
|
10741
11005
|
const bin = resolveCaliber2();
|
|
10742
11006
|
const { spawn: spawn4 } = await import("child_process");
|
|
10743
|
-
const logPath =
|
|
10744
|
-
if (!
|
|
10745
|
-
const logFd =
|
|
11007
|
+
const logPath = path34.join(LEARNING_DIR, LEARNING_FINALIZE_LOG);
|
|
11008
|
+
if (!fs43.existsSync(LEARNING_DIR)) fs43.mkdirSync(LEARNING_DIR, { recursive: true });
|
|
11009
|
+
const logFd = fs43.openSync(logPath, "a");
|
|
10746
11010
|
spawn4(bin, ["learn", "finalize", "--auto", "--incremental"], {
|
|
10747
11011
|
detached: true,
|
|
10748
11012
|
stdio: ["ignore", logFd, logFd]
|
|
10749
11013
|
}).unref();
|
|
10750
|
-
|
|
11014
|
+
fs43.closeSync(logFd);
|
|
10751
11015
|
} catch {
|
|
10752
11016
|
}
|
|
10753
11017
|
}
|
|
@@ -10954,7 +11218,7 @@ async function learnFinalizeCommand(options) {
|
|
|
10954
11218
|
}
|
|
10955
11219
|
async function learnInstallCommand() {
|
|
10956
11220
|
let anyInstalled = false;
|
|
10957
|
-
if (
|
|
11221
|
+
if (fs43.existsSync(".claude")) {
|
|
10958
11222
|
const r = installLearningHooks();
|
|
10959
11223
|
if (r.installed) {
|
|
10960
11224
|
console.log(chalk23.green("\u2713") + " Claude Code learning hooks installed");
|
|
@@ -10963,7 +11227,7 @@ async function learnInstallCommand() {
|
|
|
10963
11227
|
console.log(chalk23.dim(" Claude Code hooks already installed"));
|
|
10964
11228
|
}
|
|
10965
11229
|
}
|
|
10966
|
-
if (
|
|
11230
|
+
if (fs43.existsSync(".cursor")) {
|
|
10967
11231
|
const r = installCursorLearningHooks();
|
|
10968
11232
|
if (r.installed) {
|
|
10969
11233
|
console.log(chalk23.green("\u2713") + " Cursor learning hooks installed");
|
|
@@ -10972,7 +11236,7 @@ async function learnInstallCommand() {
|
|
|
10972
11236
|
console.log(chalk23.dim(" Cursor hooks already installed"));
|
|
10973
11237
|
}
|
|
10974
11238
|
}
|
|
10975
|
-
if (!
|
|
11239
|
+
if (!fs43.existsSync(".claude") && !fs43.existsSync(".cursor")) {
|
|
10976
11240
|
console.log(chalk23.yellow("No .claude/ or .cursor/ directory found."));
|
|
10977
11241
|
console.log(chalk23.dim(" Run `caliber init` first, or create the directory manually."));
|
|
10978
11242
|
return;
|
|
@@ -11030,8 +11294,8 @@ async function learnStatusCommand() {
|
|
|
11030
11294
|
if (lastError) {
|
|
11031
11295
|
console.log(`Last error: ${chalk23.red(lastError.error)}`);
|
|
11032
11296
|
console.log(chalk23.dim(` at ${lastError.timestamp}`));
|
|
11033
|
-
const logPath =
|
|
11034
|
-
if (
|
|
11297
|
+
const logPath = path34.join(LEARNING_DIR, LEARNING_FINALIZE_LOG);
|
|
11298
|
+
if (fs43.existsSync(logPath)) {
|
|
11035
11299
|
console.log(chalk23.dim(` Full log: ${logPath}`));
|
|
11036
11300
|
}
|
|
11037
11301
|
}
|
|
@@ -11111,11 +11375,11 @@ async function learnDeleteCommand(indexStr) {
|
|
|
11111
11375
|
}
|
|
11112
11376
|
const item = items[targetIdx];
|
|
11113
11377
|
const filePath = item.source === "personal" ? PERSONAL_LEARNINGS_FILE : "CALIBER_LEARNINGS.md";
|
|
11114
|
-
if (!
|
|
11378
|
+
if (!fs43.existsSync(filePath)) {
|
|
11115
11379
|
console.log(chalk23.red("Learnings file not found."));
|
|
11116
11380
|
return;
|
|
11117
11381
|
}
|
|
11118
|
-
const content =
|
|
11382
|
+
const content = fs43.readFileSync(filePath, "utf-8");
|
|
11119
11383
|
const lines = content.split("\n");
|
|
11120
11384
|
const bulletsOfSource = items.filter((i) => i.source === item.source);
|
|
11121
11385
|
const posInFile = bulletsOfSource.indexOf(item);
|
|
@@ -11136,9 +11400,9 @@ async function learnDeleteCommand(indexStr) {
|
|
|
11136
11400
|
}
|
|
11137
11401
|
const bulletToRemove = lines[lineToRemove];
|
|
11138
11402
|
const newLines = lines.filter((_, i) => i !== lineToRemove);
|
|
11139
|
-
|
|
11403
|
+
fs43.writeFileSync(filePath, newLines.join("\n"));
|
|
11140
11404
|
if (item.source === "personal") {
|
|
11141
|
-
|
|
11405
|
+
fs43.chmodSync(filePath, 384);
|
|
11142
11406
|
}
|
|
11143
11407
|
const roiStats = readROIStats();
|
|
11144
11408
|
const cleanText = bulletToRemove.replace(/^- /, "").replace(/^\*\*\[[^\]]+\]\*\*\s*/, "").trim();
|
|
@@ -11149,6 +11413,19 @@ async function learnDeleteCommand(indexStr) {
|
|
|
11149
11413
|
}
|
|
11150
11414
|
console.log(chalk23.green("\u2713") + ` Removed: ${bulletToRemove.replace(/^- /, "").slice(0, 80)}`);
|
|
11151
11415
|
}
|
|
11416
|
+
async function learnAddCommand(content, options) {
|
|
11417
|
+
if (!content.trim()) {
|
|
11418
|
+
console.log(chalk23.yellow("Please provide learning content."));
|
|
11419
|
+
throw new Error("__exit__");
|
|
11420
|
+
}
|
|
11421
|
+
const scope = options.personal ? "personal" : "project";
|
|
11422
|
+
const result = addLearning(content.trim(), scope);
|
|
11423
|
+
if (result.added) {
|
|
11424
|
+
console.log(chalk23.green("\u2713") + ` Learning saved to ${result.file}`);
|
|
11425
|
+
} else {
|
|
11426
|
+
console.log(chalk23.dim(" Similar learning already exists \u2014 skipped."));
|
|
11427
|
+
}
|
|
11428
|
+
}
|
|
11152
11429
|
|
|
11153
11430
|
// src/commands/insights.ts
|
|
11154
11431
|
import chalk24 from "chalk";
|
|
@@ -11285,8 +11562,8 @@ async function insightsCommand(options) {
|
|
|
11285
11562
|
}
|
|
11286
11563
|
|
|
11287
11564
|
// src/commands/sources.ts
|
|
11288
|
-
import
|
|
11289
|
-
import
|
|
11565
|
+
import fs44 from "fs";
|
|
11566
|
+
import path35 from "path";
|
|
11290
11567
|
import chalk25 from "chalk";
|
|
11291
11568
|
async function sourcesListCommand() {
|
|
11292
11569
|
const dir = process.cwd();
|
|
@@ -11302,9 +11579,9 @@ async function sourcesListCommand() {
|
|
|
11302
11579
|
if (configSources.length > 0) {
|
|
11303
11580
|
for (const source of configSources) {
|
|
11304
11581
|
const sourcePath = source.path || source.url || "";
|
|
11305
|
-
const exists = source.path ?
|
|
11582
|
+
const exists = source.path ? fs44.existsSync(path35.resolve(dir, source.path)) : false;
|
|
11306
11583
|
const status = exists ? chalk25.green("reachable") : chalk25.red("not found");
|
|
11307
|
-
const hasSummary = source.path &&
|
|
11584
|
+
const hasSummary = source.path && fs44.existsSync(path35.join(path35.resolve(dir, source.path), ".caliber", "summary.json"));
|
|
11308
11585
|
console.log(` ${chalk25.bold(source.role || source.type)} ${chalk25.dim(sourcePath)}`);
|
|
11309
11586
|
console.log(` Type: ${source.type} Status: ${status}${hasSummary ? " " + chalk25.cyan("has summary.json") : ""}`);
|
|
11310
11587
|
if (source.description) console.log(` ${chalk25.dim(source.description)}`);
|
|
@@ -11314,7 +11591,7 @@ async function sourcesListCommand() {
|
|
|
11314
11591
|
if (workspaces.length > 0) {
|
|
11315
11592
|
console.log(chalk25.dim(" Auto-detected workspaces:"));
|
|
11316
11593
|
for (const ws of workspaces) {
|
|
11317
|
-
const exists =
|
|
11594
|
+
const exists = fs44.existsSync(path35.resolve(dir, ws));
|
|
11318
11595
|
console.log(` ${exists ? chalk25.green("\u25CF") : chalk25.red("\u25CF")} ${ws}`);
|
|
11319
11596
|
}
|
|
11320
11597
|
console.log("");
|
|
@@ -11322,8 +11599,8 @@ async function sourcesListCommand() {
|
|
|
11322
11599
|
}
|
|
11323
11600
|
async function sourcesAddCommand(sourcePath) {
|
|
11324
11601
|
const dir = process.cwd();
|
|
11325
|
-
const absPath =
|
|
11326
|
-
if (!
|
|
11602
|
+
const absPath = path35.resolve(dir, sourcePath);
|
|
11603
|
+
if (!fs44.existsSync(absPath)) {
|
|
11327
11604
|
console.log(chalk25.red(`
|
|
11328
11605
|
Path not found: ${sourcePath}
|
|
11329
11606
|
`));
|
|
@@ -11338,7 +11615,7 @@ async function sourcesAddCommand(sourcePath) {
|
|
|
11338
11615
|
}
|
|
11339
11616
|
const existing = loadSourcesConfig(dir);
|
|
11340
11617
|
const alreadyConfigured = existing.some(
|
|
11341
|
-
(s) => s.path &&
|
|
11618
|
+
(s) => s.path && path35.resolve(dir, s.path) === absPath
|
|
11342
11619
|
);
|
|
11343
11620
|
if (alreadyConfigured) {
|
|
11344
11621
|
console.log(chalk25.yellow(`
|
|
@@ -11386,8 +11663,8 @@ async function sourcesRemoveCommand(name) {
|
|
|
11386
11663
|
}
|
|
11387
11664
|
|
|
11388
11665
|
// src/commands/publish.ts
|
|
11389
|
-
import
|
|
11390
|
-
import
|
|
11666
|
+
import fs45 from "fs";
|
|
11667
|
+
import path36 from "path";
|
|
11391
11668
|
import chalk26 from "chalk";
|
|
11392
11669
|
import ora7 from "ora";
|
|
11393
11670
|
init_config();
|
|
@@ -11401,10 +11678,10 @@ async function publishCommand() {
|
|
|
11401
11678
|
const spinner = ora7("Generating project summary...").start();
|
|
11402
11679
|
try {
|
|
11403
11680
|
const fingerprint = await collectFingerprint(dir);
|
|
11404
|
-
const claudeMd = readFileOrNull(
|
|
11681
|
+
const claudeMd = readFileOrNull(path36.join(dir, "CLAUDE.md"));
|
|
11405
11682
|
const topLevelDirs = fingerprint.fileTree.filter((f) => f.endsWith("/") && !f.includes("/")).map((f) => f.replace(/\/$/, ""));
|
|
11406
11683
|
const summary = {
|
|
11407
|
-
name: fingerprint.packageName ||
|
|
11684
|
+
name: fingerprint.packageName || path36.basename(dir),
|
|
11408
11685
|
version: "1.0.0",
|
|
11409
11686
|
description: fingerprint.description || "",
|
|
11410
11687
|
languages: fingerprint.languages,
|
|
@@ -11416,7 +11693,7 @@ async function publishCommand() {
|
|
|
11416
11693
|
summary.conventions = claudeMd.slice(0, 2e3);
|
|
11417
11694
|
}
|
|
11418
11695
|
try {
|
|
11419
|
-
const pkgContent = readFileOrNull(
|
|
11696
|
+
const pkgContent = readFileOrNull(path36.join(dir, "package.json"));
|
|
11420
11697
|
if (pkgContent) {
|
|
11421
11698
|
const pkg3 = JSON.parse(pkgContent);
|
|
11422
11699
|
if (pkg3.scripts) {
|
|
@@ -11429,14 +11706,14 @@ async function publishCommand() {
|
|
|
11429
11706
|
}
|
|
11430
11707
|
} catch {
|
|
11431
11708
|
}
|
|
11432
|
-
const outputDir =
|
|
11433
|
-
if (!
|
|
11434
|
-
|
|
11709
|
+
const outputDir = path36.join(dir, ".caliber");
|
|
11710
|
+
if (!fs45.existsSync(outputDir)) {
|
|
11711
|
+
fs45.mkdirSync(outputDir, { recursive: true });
|
|
11435
11712
|
}
|
|
11436
|
-
const outputPath =
|
|
11437
|
-
|
|
11713
|
+
const outputPath = path36.join(outputDir, "summary.json");
|
|
11714
|
+
fs45.writeFileSync(outputPath, JSON.stringify(summary, null, 2) + "\n", "utf-8");
|
|
11438
11715
|
spinner.succeed("Project summary published");
|
|
11439
|
-
console.log(` ${chalk26.green("\u2713")} ${
|
|
11716
|
+
console.log(` ${chalk26.green("\u2713")} ${path36.relative(dir, outputPath)}`);
|
|
11440
11717
|
console.log(chalk26.dim("\n Other projects can now reference this repo as a source."));
|
|
11441
11718
|
console.log(chalk26.dim(" When they run `caliber init`, they'll read this summary automatically.\n"));
|
|
11442
11719
|
} catch (err) {
|
|
@@ -11448,9 +11725,9 @@ async function publishCommand() {
|
|
|
11448
11725
|
}
|
|
11449
11726
|
|
|
11450
11727
|
// src/cli.ts
|
|
11451
|
-
var __dirname =
|
|
11728
|
+
var __dirname = path37.dirname(fileURLToPath(import.meta.url));
|
|
11452
11729
|
var pkg = JSON.parse(
|
|
11453
|
-
|
|
11730
|
+
fs46.readFileSync(path37.resolve(__dirname, "..", "package.json"), "utf-8")
|
|
11454
11731
|
);
|
|
11455
11732
|
var program = new Command();
|
|
11456
11733
|
var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
|
|
@@ -11516,7 +11793,7 @@ program.command("undo").description("Revert all config changes made by Caliber")
|
|
|
11516
11793
|
program.command("status").description("Show current Caliber setup status").option("--json", "Output as JSON").action(tracked("status", statusCommand));
|
|
11517
11794
|
program.command("regenerate").alias("regen").alias("re").description("Re-analyze project and regenerate setup").option("--dry-run", "Preview changes without writing files").action(tracked("regenerate", regenerateCommand));
|
|
11518
11795
|
program.command("config").description("Configure LLM provider, API key, and model").action(tracked("config", configCommand));
|
|
11519
|
-
program.command("skills").description("Discover and install community skills for your project").action(tracked("skills", recommendCommand));
|
|
11796
|
+
program.command("skills").description("Discover and install community skills for your project").option("--query <terms>", 'Search for skills by topic (e.g. "react frontend")').option("--install <slugs>", "Install specific skills by slug (comma-separated)").action(tracked("skills", recommendCommand));
|
|
11520
11797
|
program.command("score").description("Score your current agent config setup (deterministic, no network)").option("--json", "Output as JSON").option("--quiet", "One-line output for scripts/hooks").option("--agent <type>", "Target agents (comma-separated): claude, cursor, codex, github-copilot", parseAgentOption).option("--compare <ref>", "Compare score against a git ref (branch, tag, or SHA)").action(tracked("score", scoreCommand));
|
|
11521
11798
|
program.command("refresh").description("Update docs based on recent code changes").option("--quiet", "Suppress output (for use in hooks)").option("--dry-run", "Preview changes without writing files").action(tracked("refresh", refreshCommand));
|
|
11522
11799
|
program.command("hooks").description("Manage auto-refresh hooks (toggle interactively)").option("--install", "Enable all hooks non-interactively").option("--remove", "Disable all hooks non-interactively").action(tracked("hooks", hooksCommand));
|
|
@@ -11534,18 +11811,19 @@ learn.command("remove").description("Remove learning hooks from .claude/settings
|
|
|
11534
11811
|
learn.command("status").description("Show learning system status").action(tracked("learn:status", learnStatusCommand));
|
|
11535
11812
|
learn.command("list").description("List all learnings with their source and activation data").option("--verbose", "Show explanations and activation counts").action(tracked("learn:list", (opts) => learnListCommand(opts)));
|
|
11536
11813
|
learn.command("delete <index>").description("Delete a learning by its index number (from `caliber learn list`)").action(tracked("learn:delete", (index) => learnDeleteCommand(index)));
|
|
11814
|
+
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));
|
|
11537
11815
|
|
|
11538
11816
|
// src/utils/version-check.ts
|
|
11539
|
-
import
|
|
11540
|
-
import
|
|
11817
|
+
import fs47 from "fs";
|
|
11818
|
+
import path38 from "path";
|
|
11541
11819
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
11542
11820
|
import { execSync as execSync15 } from "child_process";
|
|
11543
11821
|
import chalk27 from "chalk";
|
|
11544
11822
|
import ora8 from "ora";
|
|
11545
11823
|
import confirm2 from "@inquirer/confirm";
|
|
11546
|
-
var __dirname_vc =
|
|
11824
|
+
var __dirname_vc = path38.dirname(fileURLToPath2(import.meta.url));
|
|
11547
11825
|
var pkg2 = JSON.parse(
|
|
11548
|
-
|
|
11826
|
+
fs47.readFileSync(path38.resolve(__dirname_vc, "..", "package.json"), "utf-8")
|
|
11549
11827
|
);
|
|
11550
11828
|
function getChannel(version) {
|
|
11551
11829
|
const match = version.match(/-(dev|next)\./);
|
|
@@ -11570,8 +11848,8 @@ function isNewer(registry, current) {
|
|
|
11570
11848
|
function getInstalledVersion() {
|
|
11571
11849
|
try {
|
|
11572
11850
|
const globalRoot = execSync15("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
11573
|
-
const pkgPath =
|
|
11574
|
-
return JSON.parse(
|
|
11851
|
+
const pkgPath = path38.join(globalRoot, "@rely-ai", "caliber", "package.json");
|
|
11852
|
+
return JSON.parse(fs47.readFileSync(pkgPath, "utf-8")).version;
|
|
11575
11853
|
} catch {
|
|
11576
11854
|
return null;
|
|
11577
11855
|
}
|