@rely-ai/caliber 1.19.7 → 1.20.0-dev.1773685636
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 +720 -410
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -225,7 +225,7 @@ import { fileURLToPath } from "url";
|
|
|
225
225
|
|
|
226
226
|
// src/commands/init.ts
|
|
227
227
|
import path19 from "path";
|
|
228
|
-
import
|
|
228
|
+
import chalk10 from "chalk";
|
|
229
229
|
import ora2 from "ora";
|
|
230
230
|
import select5 from "@inquirer/select";
|
|
231
231
|
import checkbox from "@inquirer/checkbox";
|
|
@@ -2062,7 +2062,7 @@ Return a JSON object with this exact shape:
|
|
|
2062
2062
|
Respond with ONLY the JSON object, no markdown fences or extra text.`;
|
|
2063
2063
|
var LEARN_SYSTEM_PROMPT = `You are an expert developer experience engineer. You analyze raw tool call events from AI coding sessions to extract reusable operational lessons that will help future LLM sessions work more effectively in this project.
|
|
2064
2064
|
|
|
2065
|
-
You receive a chronological sequence of
|
|
2065
|
+
You receive a chronological sequence of events from a Claude Code session. Most events are tool calls (with tool name, input, response, and success/failure status). Some events are USER_PROMPT events that capture what the user typed \u2014 these are critical for detecting corrections and redirections.
|
|
2066
2066
|
|
|
2067
2067
|
Your job is to find OPERATIONAL patterns \u2014 things that went wrong and how they were fixed, commands that required specific flags or configuration, APIs that needed a particular approach to work. Focus on the WORKFLOW, not the code logic.
|
|
2068
2068
|
|
|
@@ -2074,6 +2074,7 @@ Look for:
|
|
|
2074
2074
|
4. **Project-specific commands**: The correct way to build, test, lint, deploy \u2014 especially if it differs from defaults.
|
|
2075
2075
|
5. **File/path traps**: Paths that are misleading, files that shouldn't be edited, directories with unexpected structure.
|
|
2076
2076
|
6. **Configuration quirks**: Settings, flags, or arguments that are required but non-obvious.
|
|
2077
|
+
7. **User corrections**: The user explicitly told the AI what's wrong, what to use instead, or what to avoid. Look for phrases like "no, use X instead of Y", "don't touch/edit/modify X", "that's wrong, you need to...", "always/never do X in this project", "stop, that file is...". These are the HIGHEST VALUE signals \u2014 they represent direct human feedback about project-specific requirements. If a user correction contradicts a pattern you'd otherwise extract, the correction wins.
|
|
2077
2078
|
|
|
2078
2079
|
DO NOT extract:
|
|
2079
2080
|
- Descriptions of what the code does or how features work (e.g. "compression removes comments" or "skeleton extraction creates outlines")
|
|
@@ -2084,21 +2085,30 @@ DO NOT extract:
|
|
|
2084
2085
|
From these observations, produce:
|
|
2085
2086
|
|
|
2086
2087
|
### claudeMdLearnedSection
|
|
2087
|
-
A markdown section with concise, actionable bullet points. Your output will be written to CALIBER_LEARNINGS.md \u2014 a standalone file that all AI coding agents (Claude Code, Cursor, Codex) reference for project-specific operational patterns.
|
|
2088
|
+
A markdown section with concise, actionable bullet points. Your output will be written to CALIBER_LEARNINGS.md \u2014 a standalone file that all AI coding agents (Claude Code, Cursor, Codex) reference for project-specific operational patterns.
|
|
2089
|
+
|
|
2090
|
+
Each bullet MUST be prefixed with an observation type in bold brackets. Valid types:
|
|
2091
|
+
- **[correction]** \u2014 user explicitly told the AI what's wrong or what to do differently (HIGHEST PRIORITY \u2014 always include these)
|
|
2092
|
+
- **[gotcha]** \u2014 a trap or edge case that wastes time if you don't know about it
|
|
2093
|
+
- **[fix]** \u2014 a specific failure-to-recovery sequence
|
|
2094
|
+
- **[pattern]** \u2014 a reusable approach that works in this project
|
|
2095
|
+
- **[env]** \u2014 an environment or configuration requirement
|
|
2096
|
+
- **[convention]** \u2014 a project-specific rule or naming convention
|
|
2088
2097
|
|
|
2089
2098
|
Good examples:
|
|
2090
|
-
- "
|
|
2091
|
-
- "
|
|
2092
|
-
- "
|
|
2093
|
-
- "
|
|
2094
|
-
- "
|
|
2095
|
-
- "
|
|
2096
|
-
- "
|
|
2099
|
+
- "**[correction]** Files in \`src/generated/\` are auto-generated \u2014 never edit them directly"
|
|
2100
|
+
- "**[correction]** Use \`pnpm\` not \`npm\` \u2014 the lockfile is pnpm-lock.yaml and npm creates conflicts"
|
|
2101
|
+
- "**[gotcha]** When \`tsup\` build fails with a type error, run \`npx tsc --noEmit\` first to get the real error \u2014 tsup swallows the details"
|
|
2102
|
+
- "**[fix]** If \`npm install\` fails with ERESOLVE, use \`--legacy-peer-deps\`"
|
|
2103
|
+
- "**[env]** The test database requires \`DATABASE_URL\` to be set \u2014 use \`source .env.test\` first"
|
|
2104
|
+
- "**[pattern]** Do NOT run \`jest\` directly \u2014 always use \`npm run test\` which sets the correct NODE_ENV"
|
|
2105
|
+
- "**[convention]** API calls to \`/v2/users\` require the \`X-Api-Version\` header \u2014 without it you get a 404 that looks like the endpoint doesn't exist"
|
|
2097
2106
|
|
|
2098
2107
|
Bad examples (do NOT produce these):
|
|
2099
2108
|
- "The codebase uses TypeScript with strict mode" (describes code, not actionable)
|
|
2100
2109
|
- "Components follow a pattern of X" (describes architecture, not operational)
|
|
2101
2110
|
- "The project has a scoring module" (summarizes code structure)
|
|
2111
|
+
- Any bullet without a **[type]** prefix
|
|
2102
2112
|
|
|
2103
2113
|
Rules for the learned section:
|
|
2104
2114
|
- Be additive: keep all existing learned items, add new ones, remove duplicates
|
|
@@ -2676,6 +2686,37 @@ async function generateMonolithic(fingerprint, targetAgent, prompt, callbacks, f
|
|
|
2676
2686
|
};
|
|
2677
2687
|
return attemptGeneration();
|
|
2678
2688
|
}
|
|
2689
|
+
async function generateSkillsForSetup(setup, fingerprint, targetAgent, onStatus) {
|
|
2690
|
+
const skillTopics = collectSkillTopics(setup, targetAgent, fingerprint);
|
|
2691
|
+
if (skillTopics.length === 0) return 0;
|
|
2692
|
+
onStatus?.(`Generating ${skillTopics.length} skills...`);
|
|
2693
|
+
const allDeps = extractAllDeps(process.cwd());
|
|
2694
|
+
const skillContext = buildSkillContext(fingerprint, setup, allDeps);
|
|
2695
|
+
const fastModel = getFastModel();
|
|
2696
|
+
const skillResults = await Promise.allSettled(
|
|
2697
|
+
skillTopics.map(
|
|
2698
|
+
({ platform, topic }) => generateSkill(skillContext, topic, fastModel).then((skill) => ({ platform, skill }))
|
|
2699
|
+
)
|
|
2700
|
+
);
|
|
2701
|
+
for (const result of skillResults) {
|
|
2702
|
+
if (result.status === "fulfilled") {
|
|
2703
|
+
const { platform, skill } = result.value;
|
|
2704
|
+
const platformObj = setup[platform] ?? {};
|
|
2705
|
+
const skills = platformObj.skills ?? [];
|
|
2706
|
+
skills.push(skill);
|
|
2707
|
+
platformObj.skills = skills;
|
|
2708
|
+
setup[platform] = platformObj;
|
|
2709
|
+
const skillPath = platform === "codex" ? `.agents/skills/${skill.name}/SKILL.md` : `.${platform}/skills/${skill.name}/SKILL.md`;
|
|
2710
|
+
const descriptions = setup.fileDescriptions ?? {};
|
|
2711
|
+
descriptions[skillPath] = skill.description.slice(0, 80);
|
|
2712
|
+
setup.fileDescriptions = descriptions;
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
const succeeded = skillResults.filter((r) => r.status === "fulfilled").length;
|
|
2716
|
+
const failed = skillResults.filter((r) => r.status === "rejected").length;
|
|
2717
|
+
if (failed > 0) onStatus?.(`${succeeded} generated, ${failed} failed`);
|
|
2718
|
+
return succeeded;
|
|
2719
|
+
}
|
|
2679
2720
|
var LIMITS = {
|
|
2680
2721
|
FILE_TREE_ENTRIES: 500,
|
|
2681
2722
|
EXISTING_CONFIG_CHARS: 8e3,
|
|
@@ -3669,8 +3710,8 @@ function removePreCommitHook() {
|
|
|
3669
3710
|
if (!content.includes(PRECOMMIT_START)) {
|
|
3670
3711
|
return { removed: false, notFound: true };
|
|
3671
3712
|
}
|
|
3672
|
-
const
|
|
3673
|
-
content = content.replace(
|
|
3713
|
+
const regex2 = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
|
|
3714
|
+
content = content.replace(regex2, "\n");
|
|
3674
3715
|
if (content.trim() === "#!/bin/sh" || content.trim() === "") {
|
|
3675
3716
|
fs17.unlinkSync(hookPath);
|
|
3676
3717
|
} else {
|
|
@@ -3686,6 +3727,7 @@ var SETTINGS_PATH2 = path13.join(".claude", "settings.json");
|
|
|
3686
3727
|
var HOOK_TAILS = [
|
|
3687
3728
|
{ event: "PostToolUse", tail: "learn observe", description: "Caliber: recording tool usage for session learning" },
|
|
3688
3729
|
{ event: "PostToolUseFailure", tail: "learn observe --failure", description: "Caliber: recording tool failure for session learning" },
|
|
3730
|
+
{ event: "UserPromptSubmit", tail: "learn observe --prompt", description: "Caliber: recording user prompt for correction detection" },
|
|
3689
3731
|
{ event: "SessionEnd", tail: "learn finalize", description: "Caliber: finalizing session learnings" }
|
|
3690
3732
|
];
|
|
3691
3733
|
function getHookConfigs() {
|
|
@@ -3746,6 +3788,7 @@ var CURSOR_HOOKS_PATH = path13.join(".cursor", "hooks.json");
|
|
|
3746
3788
|
var CURSOR_HOOK_EVENTS = [
|
|
3747
3789
|
{ event: "postToolUse", tail: "learn observe" },
|
|
3748
3790
|
{ event: "postToolUseFailure", tail: "learn observe --failure" },
|
|
3791
|
+
{ event: "userPromptSubmit", tail: "learn observe --prompt" },
|
|
3749
3792
|
{ event: "sessionEnd", tail: "learn finalize" }
|
|
3750
3793
|
];
|
|
3751
3794
|
function readCursorHooks() {
|
|
@@ -5995,6 +6038,51 @@ function extractTopDeps() {
|
|
|
5995
6038
|
return [];
|
|
5996
6039
|
}
|
|
5997
6040
|
}
|
|
6041
|
+
async function searchSkills(fingerprint, targetPlatforms, onStatus) {
|
|
6042
|
+
const installedSkills = getInstalledSkills(targetPlatforms);
|
|
6043
|
+
const technologies = [...new Set([
|
|
6044
|
+
...fingerprint.languages,
|
|
6045
|
+
...fingerprint.frameworks,
|
|
6046
|
+
...extractTopDeps()
|
|
6047
|
+
].filter(Boolean))];
|
|
6048
|
+
if (technologies.length === 0) {
|
|
6049
|
+
return { results: [], contentMap: /* @__PURE__ */ new Map() };
|
|
6050
|
+
}
|
|
6051
|
+
const primaryPlatform = targetPlatforms.includes("claude") ? "claude" : targetPlatforms[0];
|
|
6052
|
+
onStatus?.("Searching skill registries...");
|
|
6053
|
+
const allCandidates = await searchAllProviders(technologies, primaryPlatform);
|
|
6054
|
+
if (!allCandidates.length) {
|
|
6055
|
+
return { results: [], contentMap: /* @__PURE__ */ new Map() };
|
|
6056
|
+
}
|
|
6057
|
+
const newCandidates = allCandidates.filter((c) => !installedSkills.has(c.slug.toLowerCase()));
|
|
6058
|
+
if (!newCandidates.length) {
|
|
6059
|
+
return { results: [], contentMap: /* @__PURE__ */ new Map() };
|
|
6060
|
+
}
|
|
6061
|
+
onStatus?.(`Scoring ${newCandidates.length} candidates...`);
|
|
6062
|
+
let results;
|
|
6063
|
+
const config = loadConfig();
|
|
6064
|
+
if (config) {
|
|
6065
|
+
try {
|
|
6066
|
+
const projectContext = buildProjectContext(fingerprint, targetPlatforms);
|
|
6067
|
+
results = await scoreWithLLM(newCandidates, projectContext, technologies);
|
|
6068
|
+
} catch {
|
|
6069
|
+
results = newCandidates.slice(0, 20);
|
|
6070
|
+
}
|
|
6071
|
+
} else {
|
|
6072
|
+
results = newCandidates.slice(0, 20);
|
|
6073
|
+
}
|
|
6074
|
+
if (results.length === 0) {
|
|
6075
|
+
return { results: [], contentMap: /* @__PURE__ */ new Map() };
|
|
6076
|
+
}
|
|
6077
|
+
onStatus?.("Fetching skill content...");
|
|
6078
|
+
const contentMap = /* @__PURE__ */ new Map();
|
|
6079
|
+
await Promise.all(results.map(async (rec) => {
|
|
6080
|
+
const content = await fetchSkillContent(rec);
|
|
6081
|
+
if (content) contentMap.set(rec.slug, content);
|
|
6082
|
+
}));
|
|
6083
|
+
const available = results.filter((r) => contentMap.has(r.slug));
|
|
6084
|
+
return { results: available, contentMap };
|
|
6085
|
+
}
|
|
5998
6086
|
async function recommendCommand() {
|
|
5999
6087
|
const proceed = await select4({
|
|
6000
6088
|
message: "Search public repos for relevant skills to add to this project?",
|
|
@@ -6359,13 +6447,144 @@ function formatMs(ms) {
|
|
|
6359
6447
|
return `${secs}s`;
|
|
6360
6448
|
}
|
|
6361
6449
|
|
|
6450
|
+
// src/utils/parallel-tasks.ts
|
|
6451
|
+
import chalk9 from "chalk";
|
|
6452
|
+
|
|
6453
|
+
// node_modules/ansi-regex/index.js
|
|
6454
|
+
function ansiRegex({ onlyFirst = false } = {}) {
|
|
6455
|
+
const ST = "(?:\\u0007|\\u001B\\u005C|\\u009C)";
|
|
6456
|
+
const osc = `(?:\\u001B\\][\\s\\S]*?${ST})`;
|
|
6457
|
+
const csi = "[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]";
|
|
6458
|
+
const pattern = `${osc}|${csi}`;
|
|
6459
|
+
return new RegExp(pattern, onlyFirst ? void 0 : "g");
|
|
6460
|
+
}
|
|
6461
|
+
|
|
6462
|
+
// node_modules/strip-ansi/index.js
|
|
6463
|
+
var regex = ansiRegex();
|
|
6464
|
+
function stripAnsi(string) {
|
|
6465
|
+
if (typeof string !== "string") {
|
|
6466
|
+
throw new TypeError(`Expected a \`string\`, got \`${typeof string}\``);
|
|
6467
|
+
}
|
|
6468
|
+
if (!string.includes("\x1B") && !string.includes("\x9B")) {
|
|
6469
|
+
return string;
|
|
6470
|
+
}
|
|
6471
|
+
return string.replace(regex, "");
|
|
6472
|
+
}
|
|
6473
|
+
|
|
6474
|
+
// src/utils/parallel-tasks.ts
|
|
6475
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
6476
|
+
var SPINNER_INTERVAL_MS = 80;
|
|
6477
|
+
var ParallelTaskDisplay = class {
|
|
6478
|
+
tasks = [];
|
|
6479
|
+
lineCount = 0;
|
|
6480
|
+
spinnerFrame = 0;
|
|
6481
|
+
timer = null;
|
|
6482
|
+
startTime = 0;
|
|
6483
|
+
rendered = false;
|
|
6484
|
+
add(name) {
|
|
6485
|
+
const index = this.tasks.length;
|
|
6486
|
+
this.tasks.push({ name, status: "pending", message: "" });
|
|
6487
|
+
return index;
|
|
6488
|
+
}
|
|
6489
|
+
start() {
|
|
6490
|
+
this.startTime = Date.now();
|
|
6491
|
+
this.draw(true);
|
|
6492
|
+
this.timer = setInterval(() => {
|
|
6493
|
+
this.spinnerFrame = (this.spinnerFrame + 1) % SPINNER_FRAMES.length;
|
|
6494
|
+
this.draw(false);
|
|
6495
|
+
}, SPINNER_INTERVAL_MS);
|
|
6496
|
+
}
|
|
6497
|
+
update(index, status, message) {
|
|
6498
|
+
const task = this.tasks[index];
|
|
6499
|
+
if (!task) return;
|
|
6500
|
+
if (status === "running" && task.status === "pending") {
|
|
6501
|
+
task.startTime = Date.now();
|
|
6502
|
+
}
|
|
6503
|
+
if ((status === "done" || status === "failed") && !task.endTime) {
|
|
6504
|
+
task.endTime = Date.now();
|
|
6505
|
+
}
|
|
6506
|
+
task.status = status;
|
|
6507
|
+
if (message !== void 0) task.message = message;
|
|
6508
|
+
}
|
|
6509
|
+
stop() {
|
|
6510
|
+
if (this.timer) {
|
|
6511
|
+
clearInterval(this.timer);
|
|
6512
|
+
this.timer = null;
|
|
6513
|
+
}
|
|
6514
|
+
this.draw(false);
|
|
6515
|
+
}
|
|
6516
|
+
formatTime(ms) {
|
|
6517
|
+
const secs = Math.floor(ms / 1e3);
|
|
6518
|
+
if (secs < 60) return `${secs}s`;
|
|
6519
|
+
return `${Math.floor(secs / 60)}m ${secs % 60}s`;
|
|
6520
|
+
}
|
|
6521
|
+
truncate(text, maxVisible) {
|
|
6522
|
+
const plain = stripAnsi(text);
|
|
6523
|
+
if (plain.length <= maxVisible) return text;
|
|
6524
|
+
let visible = 0;
|
|
6525
|
+
let i = 0;
|
|
6526
|
+
while (i < text.length && visible < maxVisible - 3) {
|
|
6527
|
+
if (text[i] === "\x1B") {
|
|
6528
|
+
const end = text.indexOf("m", i);
|
|
6529
|
+
if (end !== -1) {
|
|
6530
|
+
i = end + 1;
|
|
6531
|
+
continue;
|
|
6532
|
+
}
|
|
6533
|
+
}
|
|
6534
|
+
visible++;
|
|
6535
|
+
i++;
|
|
6536
|
+
}
|
|
6537
|
+
return text.slice(0, i) + "...";
|
|
6538
|
+
}
|
|
6539
|
+
renderLine(task) {
|
|
6540
|
+
const maxWidth = process.stdout.columns || 80;
|
|
6541
|
+
const elapsed = task.startTime ? this.formatTime((task.endTime ?? Date.now()) - task.startTime) : "";
|
|
6542
|
+
let line;
|
|
6543
|
+
switch (task.status) {
|
|
6544
|
+
case "pending":
|
|
6545
|
+
line = ` ${chalk9.dim("\u25CB")} ${chalk9.dim(task.name)}${task.message ? chalk9.dim(` \u2014 ${task.message}`) : ""}`;
|
|
6546
|
+
break;
|
|
6547
|
+
case "running": {
|
|
6548
|
+
const spinner = chalk9.cyan(SPINNER_FRAMES[this.spinnerFrame]);
|
|
6549
|
+
const time = elapsed ? chalk9.dim(` (${elapsed})`) : "";
|
|
6550
|
+
line = ` ${spinner} ${task.name}${task.message ? chalk9.dim(` \u2014 ${task.message}`) : ""}${time}`;
|
|
6551
|
+
break;
|
|
6552
|
+
}
|
|
6553
|
+
case "done": {
|
|
6554
|
+
const time = elapsed ? chalk9.dim(` (${elapsed})`) : "";
|
|
6555
|
+
line = ` ${chalk9.green("\u2713")} ${task.name}${task.message ? chalk9.dim(` \u2014 ${task.message}`) : ""}${time}`;
|
|
6556
|
+
break;
|
|
6557
|
+
}
|
|
6558
|
+
case "failed":
|
|
6559
|
+
line = ` ${chalk9.red("\u2717")} ${task.name}${task.message ? chalk9.red(` \u2014 ${task.message}`) : ""}`;
|
|
6560
|
+
break;
|
|
6561
|
+
}
|
|
6562
|
+
return this.truncate(line, maxWidth - 1);
|
|
6563
|
+
}
|
|
6564
|
+
draw(initial) {
|
|
6565
|
+
const { stdout } = process;
|
|
6566
|
+
if (!initial && this.rendered && this.lineCount > 0) {
|
|
6567
|
+
stdout.write(`\x1B[${this.lineCount}A`);
|
|
6568
|
+
}
|
|
6569
|
+
stdout.write("\x1B[0J");
|
|
6570
|
+
const lines = this.tasks.map((t) => this.renderLine(t));
|
|
6571
|
+
const totalElapsed = this.formatTime(Date.now() - this.startTime);
|
|
6572
|
+
lines.push(chalk9.dim(`
|
|
6573
|
+
Total: ${totalElapsed}`));
|
|
6574
|
+
const output = lines.join("\n");
|
|
6575
|
+
stdout.write(output + "\n");
|
|
6576
|
+
this.lineCount = output.split("\n").length;
|
|
6577
|
+
this.rendered = true;
|
|
6578
|
+
}
|
|
6579
|
+
};
|
|
6580
|
+
|
|
6362
6581
|
// src/commands/init.ts
|
|
6363
6582
|
function log(verbose, ...args) {
|
|
6364
|
-
if (verbose) console.log(
|
|
6583
|
+
if (verbose) console.log(chalk10.dim(` [verbose] ${args.map(String).join(" ")}`));
|
|
6365
6584
|
}
|
|
6366
6585
|
async function initCommand(options) {
|
|
6367
|
-
const brand =
|
|
6368
|
-
const title =
|
|
6586
|
+
const brand = chalk10.hex("#EB9D83");
|
|
6587
|
+
const title = chalk10.hex("#83D1EB");
|
|
6369
6588
|
console.log(brand.bold(`
|
|
6370
6589
|
\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
6371
6590
|
\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
|
|
@@ -6374,34 +6593,33 @@ async function initCommand(options) {
|
|
|
6374
6593
|
\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551
|
|
6375
6594
|
\u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
|
|
6376
6595
|
`));
|
|
6377
|
-
console.log(
|
|
6378
|
-
console.log(
|
|
6596
|
+
console.log(chalk10.dim(" Scan your project and generate tailored config files for"));
|
|
6597
|
+
console.log(chalk10.dim(" Claude Code, Cursor, and Codex.\n"));
|
|
6379
6598
|
const report = options.debugReport ? new DebugReport() : null;
|
|
6380
6599
|
console.log(title.bold(" How it works:\n"));
|
|
6381
|
-
console.log(
|
|
6382
|
-
console.log(
|
|
6383
|
-
console.log(
|
|
6384
|
-
console.log(
|
|
6385
|
-
console.log(
|
|
6386
|
-
console.log(title.bold(" Step 1/5 \u2014 Connect\n"));
|
|
6600
|
+
console.log(chalk10.dim(" 1. Setup Connect your LLM provider and select your agents"));
|
|
6601
|
+
console.log(chalk10.dim(" 2. Engine Detect stack, generate configs & skills in parallel"));
|
|
6602
|
+
console.log(chalk10.dim(" 3. Review See all changes \u2014 accept, refine, or decline"));
|
|
6603
|
+
console.log(chalk10.dim(" 4. Finalize Score check and auto-sync hooks\n"));
|
|
6604
|
+
console.log(title.bold(" Step 1/4 \u2014 Setup\n"));
|
|
6387
6605
|
let config = loadConfig();
|
|
6388
6606
|
if (!config) {
|
|
6389
|
-
console.log(
|
|
6607
|
+
console.log(chalk10.dim(" No LLM provider configured yet.\n"));
|
|
6390
6608
|
await runInteractiveProviderSetup({
|
|
6391
6609
|
selectMessage: "How do you want to use Caliber? (choose LLM provider)"
|
|
6392
6610
|
});
|
|
6393
6611
|
config = loadConfig();
|
|
6394
6612
|
if (!config) {
|
|
6395
|
-
console.log(
|
|
6613
|
+
console.log(chalk10.red(" Setup was cancelled or failed.\n"));
|
|
6396
6614
|
throw new Error("__exit__");
|
|
6397
6615
|
}
|
|
6398
|
-
console.log(
|
|
6616
|
+
console.log(chalk10.green(" \u2713 Provider saved\n"));
|
|
6399
6617
|
}
|
|
6400
6618
|
trackInitProviderSelected(config.provider, config.model);
|
|
6401
6619
|
const displayModel = getDisplayModel(config);
|
|
6402
6620
|
const fastModel = getFastModel();
|
|
6403
6621
|
const modelLine = fastModel ? ` Provider: ${config.provider} | Model: ${displayModel} | Scan: ${fastModel}` : ` Provider: ${config.provider} | Model: ${displayModel}`;
|
|
6404
|
-
console.log(
|
|
6622
|
+
console.log(chalk10.dim(modelLine + "\n"));
|
|
6405
6623
|
if (report) {
|
|
6406
6624
|
report.markStep("Provider setup");
|
|
6407
6625
|
report.addSection("LLM Provider", `- **Provider**: ${config.provider}
|
|
@@ -6409,30 +6627,6 @@ async function initCommand(options) {
|
|
|
6409
6627
|
- **Fast model**: ${fastModel || "none"}`);
|
|
6410
6628
|
}
|
|
6411
6629
|
await validateModel({ fast: true });
|
|
6412
|
-
console.log(title.bold(" Step 2/5 \u2014 Discover\n"));
|
|
6413
|
-
const spinner = ora2("Scanning code, dependencies, and existing configs...").start();
|
|
6414
|
-
const fingerprint = await collectFingerprint(process.cwd());
|
|
6415
|
-
spinner.succeed("Project scanned");
|
|
6416
|
-
log(options.verbose, `Fingerprint: ${fingerprint.languages.length} languages, ${fingerprint.frameworks.length} frameworks, ${fingerprint.fileTree.length} files`);
|
|
6417
|
-
if (options.verbose && fingerprint.codeAnalysis) {
|
|
6418
|
-
log(options.verbose, `Code analysis: ${fingerprint.codeAnalysis.filesIncluded}/${fingerprint.codeAnalysis.filesAnalyzed} files, ~${fingerprint.codeAnalysis.includedTokens.toLocaleString()} tokens, ${fingerprint.codeAnalysis.duplicateGroups} dedup groups`);
|
|
6419
|
-
}
|
|
6420
|
-
trackInitProjectDiscovered(fingerprint.languages.length, fingerprint.frameworks.length, fingerprint.fileTree.length);
|
|
6421
|
-
const langDisplay = fingerprint.languages.join(", ") || "none detected";
|
|
6422
|
-
const frameworkDisplay = fingerprint.frameworks.length > 0 ? ` (${fingerprint.frameworks.join(", ")})` : "";
|
|
6423
|
-
console.log(chalk9.dim(` Languages: ${langDisplay}${frameworkDisplay}`));
|
|
6424
|
-
console.log(chalk9.dim(` Files: ${fingerprint.fileTree.length} found
|
|
6425
|
-
`));
|
|
6426
|
-
if (report) {
|
|
6427
|
-
report.markStep("Fingerprint");
|
|
6428
|
-
report.addJson("Fingerprint: Git", { remote: fingerprint.gitRemoteUrl, packageName: fingerprint.packageName });
|
|
6429
|
-
report.addCodeBlock("Fingerprint: File Tree", fingerprint.fileTree.join("\n"));
|
|
6430
|
-
report.addJson("Fingerprint: Detected Stack", { languages: fingerprint.languages, frameworks: fingerprint.frameworks, tools: fingerprint.tools });
|
|
6431
|
-
report.addJson("Fingerprint: Existing Configs", fingerprint.existingConfigs);
|
|
6432
|
-
if (fingerprint.codeAnalysis) {
|
|
6433
|
-
report.addJson("Fingerprint: Code Analysis", fingerprint.codeAnalysis);
|
|
6434
|
-
}
|
|
6435
|
-
}
|
|
6436
6630
|
let targetAgent;
|
|
6437
6631
|
if (options.agent) {
|
|
6438
6632
|
targetAgent = options.agent;
|
|
@@ -6442,30 +6636,27 @@ async function initCommand(options) {
|
|
|
6442
6636
|
} else {
|
|
6443
6637
|
targetAgent = await promptAgent();
|
|
6444
6638
|
}
|
|
6445
|
-
console.log(
|
|
6639
|
+
console.log(chalk10.dim(` Target: ${targetAgent.join(", ")}
|
|
6446
6640
|
`));
|
|
6447
6641
|
trackInitAgentSelected(targetAgent);
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
}
|
|
6642
|
+
let wantsSkills = false;
|
|
6643
|
+
if (!options.autoApprove) {
|
|
6644
|
+
wantsSkills = await select5({
|
|
6645
|
+
message: "Discover community-maintained skills that match your stack?",
|
|
6646
|
+
choices: [
|
|
6647
|
+
{ name: "Yes, find skills for my project", value: true },
|
|
6648
|
+
{ name: "Skip for now", value: false }
|
|
6649
|
+
]
|
|
6650
|
+
});
|
|
6458
6651
|
}
|
|
6459
|
-
|
|
6460
|
-
console.log(
|
|
6652
|
+
let baselineScore = computeLocalScore(process.cwd(), targetAgent);
|
|
6653
|
+
console.log(chalk10.dim("\n Current setup score:"));
|
|
6461
6654
|
displayScoreSummary(baselineScore);
|
|
6462
6655
|
if (options.verbose) {
|
|
6463
6656
|
for (const c of baselineScore.checks) {
|
|
6464
6657
|
log(options.verbose, ` ${c.passed ? "\u2713" : "\u2717"} ${c.name}: ${c.earnedPoints}/${c.maxPoints}${c.suggestion ? ` \u2014 ${c.suggestion}` : ""}`);
|
|
6465
6658
|
}
|
|
6466
6659
|
}
|
|
6467
|
-
const passingCount = baselineScore.checks.filter((c) => c.passed).length;
|
|
6468
|
-
const failingCount = baselineScore.checks.filter((c) => !c.passed).length;
|
|
6469
6660
|
if (report) {
|
|
6470
6661
|
report.markStep("Baseline scoring");
|
|
6471
6662
|
report.addSection("Scoring: Baseline", `**Score**: ${baselineScore.score}/100
|
|
@@ -6475,7 +6666,7 @@ async function initCommand(options) {
|
|
|
6475
6666
|
` + baselineScore.checks.map((c) => `| ${c.name} | ${c.passed ? "Yes" : "No"} | ${c.earnedPoints} | ${c.maxPoints} |`).join("\n"));
|
|
6476
6667
|
report.addSection("Generation: Target Agents", targetAgent.join(", "));
|
|
6477
6668
|
}
|
|
6478
|
-
const hasExistingConfig = !!(
|
|
6669
|
+
const hasExistingConfig = !!(baselineScore.checks.some((c) => c.id === "claude_md_exists" && c.passed) || baselineScore.checks.some((c) => c.id === "cursorrules_exists" && c.passed));
|
|
6479
6670
|
const NON_LLM_CHECKS = /* @__PURE__ */ new Set([
|
|
6480
6671
|
"hooks_configured",
|
|
6481
6672
|
"agents_md_exists",
|
|
@@ -6484,111 +6675,175 @@ async function initCommand(options) {
|
|
|
6484
6675
|
"service_coverage",
|
|
6485
6676
|
"mcp_completeness"
|
|
6486
6677
|
]);
|
|
6678
|
+
const passingCount = baselineScore.checks.filter((c) => c.passed).length;
|
|
6679
|
+
const failingCount = baselineScore.checks.filter((c) => !c.passed).length;
|
|
6487
6680
|
if (hasExistingConfig && baselineScore.score === 100) {
|
|
6488
6681
|
trackInitScoreComputed(baselineScore.score, passingCount, failingCount, true);
|
|
6489
|
-
console.log(
|
|
6490
|
-
console.log(
|
|
6682
|
+
console.log(chalk10.bold.green(" Your setup is already optimal \u2014 nothing to change.\n"));
|
|
6683
|
+
console.log(chalk10.dim(" Run ") + chalk10.hex("#83D1EB")("caliber init --force") + chalk10.dim(" to regenerate anyway.\n"));
|
|
6491
6684
|
if (!options.force) return;
|
|
6492
6685
|
}
|
|
6493
6686
|
const allFailingChecks = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0);
|
|
6494
6687
|
const llmFixableChecks = allFailingChecks.filter((c) => !NON_LLM_CHECKS.has(c.id));
|
|
6495
6688
|
trackInitScoreComputed(baselineScore.score, passingCount, failingCount, false);
|
|
6496
6689
|
if (hasExistingConfig && llmFixableChecks.length === 0 && allFailingChecks.length > 0 && !options.force) {
|
|
6497
|
-
console.log(
|
|
6498
|
-
console.log(
|
|
6690
|
+
console.log(chalk10.bold.green("\n Your config is fully optimized for LLM generation.\n"));
|
|
6691
|
+
console.log(chalk10.dim(" Remaining items need CLI actions:\n"));
|
|
6499
6692
|
for (const check of allFailingChecks) {
|
|
6500
|
-
console.log(
|
|
6693
|
+
console.log(chalk10.dim(` \u2022 ${check.name}`));
|
|
6501
6694
|
if (check.suggestion) {
|
|
6502
|
-
console.log(` ${
|
|
6695
|
+
console.log(` ${chalk10.hex("#83D1EB")(check.suggestion)}`);
|
|
6503
6696
|
}
|
|
6504
6697
|
}
|
|
6505
6698
|
console.log("");
|
|
6506
|
-
console.log(
|
|
6699
|
+
console.log(chalk10.dim(" Run ") + chalk10.hex("#83D1EB")("caliber init --force") + chalk10.dim(" to regenerate anyway.\n"));
|
|
6507
6700
|
return;
|
|
6508
6701
|
}
|
|
6702
|
+
console.log(title.bold(" Step 2/4 \u2014 Engine\n"));
|
|
6703
|
+
const genModelInfo = fastModel ? ` Using ${displayModel} for docs, ${fastModel} for skills` : ` Using ${displayModel}`;
|
|
6704
|
+
console.log(chalk10.dim(genModelInfo + "\n"));
|
|
6705
|
+
if (report) report.markStep("Generation");
|
|
6706
|
+
trackInitGenerationStarted(false);
|
|
6707
|
+
const genStartTime = Date.now();
|
|
6708
|
+
let generatedSetup = null;
|
|
6709
|
+
let rawOutput;
|
|
6710
|
+
let genStopReason;
|
|
6711
|
+
let skillSearchResult = { results: [], contentMap: /* @__PURE__ */ new Map() };
|
|
6712
|
+
let fingerprint;
|
|
6713
|
+
const fpSpinner = ora2("Scanning project...").start();
|
|
6714
|
+
try {
|
|
6715
|
+
fingerprint = await collectFingerprint(process.cwd());
|
|
6716
|
+
fpSpinner.succeed(`Stack detected \u2014 ${fingerprint.languages.join(", ") || "no languages"}${fingerprint.frameworks.length > 0 ? `, ${fingerprint.frameworks.join(", ")}` : ""}`);
|
|
6717
|
+
} catch (err) {
|
|
6718
|
+
fpSpinner.fail("Failed to scan project");
|
|
6719
|
+
throw new Error("__exit__");
|
|
6720
|
+
}
|
|
6721
|
+
trackInitProjectDiscovered(fingerprint.languages.length, fingerprint.frameworks.length, fingerprint.fileTree.length);
|
|
6722
|
+
log(options.verbose, `Fingerprint: ${fingerprint.languages.length} languages, ${fingerprint.frameworks.length} frameworks, ${fingerprint.fileTree.length} files`);
|
|
6723
|
+
if (report) {
|
|
6724
|
+
report.addJson("Fingerprint: Git", { remote: fingerprint.gitRemoteUrl, packageName: fingerprint.packageName });
|
|
6725
|
+
report.addCodeBlock("Fingerprint: File Tree", fingerprint.fileTree.join("\n"));
|
|
6726
|
+
report.addJson("Fingerprint: Detected Stack", { languages: fingerprint.languages, frameworks: fingerprint.frameworks, tools: fingerprint.tools });
|
|
6727
|
+
report.addJson("Fingerprint: Existing Configs", fingerprint.existingConfigs);
|
|
6728
|
+
if (fingerprint.codeAnalysis) report.addJson("Fingerprint: Code Analysis", fingerprint.codeAnalysis);
|
|
6729
|
+
}
|
|
6509
6730
|
const isEmpty = fingerprint.fileTree.length < 3;
|
|
6510
6731
|
if (isEmpty) {
|
|
6511
6732
|
fingerprint.description = await promptInput("What will you build in this project?");
|
|
6512
6733
|
}
|
|
6734
|
+
const failingForDismissal = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0);
|
|
6735
|
+
if (failingForDismissal.length > 0) {
|
|
6736
|
+
const newDismissals = await evaluateDismissals(failingForDismissal, fingerprint);
|
|
6737
|
+
if (newDismissals.length > 0) {
|
|
6738
|
+
const existing = readDismissedChecks();
|
|
6739
|
+
const existingIds = new Set(existing.map((d) => d.id));
|
|
6740
|
+
const merged = [...existing, ...newDismissals.filter((d) => !existingIds.has(d.id))];
|
|
6741
|
+
writeDismissedChecks(merged);
|
|
6742
|
+
baselineScore = computeLocalScore(process.cwd(), targetAgent);
|
|
6743
|
+
}
|
|
6744
|
+
}
|
|
6513
6745
|
let failingChecks;
|
|
6514
6746
|
let passingChecks;
|
|
6515
6747
|
let currentScore;
|
|
6516
6748
|
if (hasExistingConfig && baselineScore.score >= 95 && !options.force) {
|
|
6517
|
-
|
|
6749
|
+
const currentLlmFixable = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0 && !NON_LLM_CHECKS.has(c.id));
|
|
6750
|
+
failingChecks = currentLlmFixable.map((c) => ({ name: c.name, suggestion: c.suggestion, fix: c.fix }));
|
|
6518
6751
|
passingChecks = baselineScore.checks.filter((c) => c.passed).map((c) => ({ name: c.name }));
|
|
6519
6752
|
currentScore = baselineScore.score;
|
|
6520
|
-
if (failingChecks.length > 0) {
|
|
6521
|
-
console.log(title.bold(" Step 3/5 \u2014 Generate\n"));
|
|
6522
|
-
console.log(chalk9.dim(` Score is ${baselineScore.score}/100 \u2014 fine-tuning ${failingChecks.length} remaining issue${failingChecks.length === 1 ? "" : "s"}:
|
|
6523
|
-
`));
|
|
6524
|
-
for (const check of failingChecks) {
|
|
6525
|
-
console.log(chalk9.dim(` \u2022 ${check.name}`));
|
|
6526
|
-
}
|
|
6527
|
-
console.log("");
|
|
6528
|
-
}
|
|
6529
|
-
} else if (hasExistingConfig) {
|
|
6530
|
-
console.log(title.bold(" Step 3/5 \u2014 Generate\n"));
|
|
6531
|
-
console.log(chalk9.dim(" Auditing your existing configs against your codebase and improving them.\n"));
|
|
6532
|
-
} else {
|
|
6533
|
-
console.log(title.bold(" Step 3/5 \u2014 Generate\n"));
|
|
6534
|
-
console.log(chalk9.dim(" Building CLAUDE.md, rules, and skills tailored to your project.\n"));
|
|
6535
6753
|
}
|
|
6536
|
-
const genModelInfo = fastModel ? ` Using ${displayModel} for docs, ${fastModel} for skills` : ` Using ${displayModel}`;
|
|
6537
|
-
console.log(chalk9.dim(genModelInfo));
|
|
6538
|
-
console.log(chalk9.dim(" This can take a couple of minutes depending on your model and provider.\n"));
|
|
6539
6754
|
if (report) {
|
|
6540
|
-
report.markStep("Generation");
|
|
6541
6755
|
const fullPrompt = buildGeneratePrompt(fingerprint, targetAgent, fingerprint.description, failingChecks, currentScore, passingChecks);
|
|
6542
6756
|
report.addCodeBlock("Generation: Full LLM Prompt", fullPrompt);
|
|
6543
6757
|
}
|
|
6544
|
-
|
|
6545
|
-
const
|
|
6546
|
-
const
|
|
6547
|
-
const
|
|
6548
|
-
|
|
6549
|
-
let generatedSetup = null;
|
|
6550
|
-
let rawOutput;
|
|
6551
|
-
let genStopReason;
|
|
6758
|
+
const display = new ParallelTaskDisplay();
|
|
6759
|
+
const TASK_CONFIG = display.add("Generating configs");
|
|
6760
|
+
const TASK_SKILLS_GEN = display.add("Generating skills");
|
|
6761
|
+
const TASK_SKILLS_SEARCH = wantsSkills ? display.add("Searching community skills") : -1;
|
|
6762
|
+
display.start();
|
|
6552
6763
|
try {
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6764
|
+
display.update(TASK_CONFIG, "running");
|
|
6765
|
+
const generatePromise = (async () => {
|
|
6766
|
+
const result = await generateSetup(
|
|
6767
|
+
fingerprint,
|
|
6768
|
+
targetAgent,
|
|
6769
|
+
fingerprint.description,
|
|
6770
|
+
{
|
|
6771
|
+
onStatus: (status) => display.update(TASK_CONFIG, "running", status),
|
|
6772
|
+
onComplete: (setup) => {
|
|
6773
|
+
generatedSetup = setup;
|
|
6774
|
+
},
|
|
6775
|
+
onError: (error) => display.update(TASK_CONFIG, "failed", error)
|
|
6563
6776
|
},
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
}
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
currentScore,
|
|
6571
|
-
passingChecks
|
|
6572
|
-
);
|
|
6573
|
-
if (!generatedSetup) {
|
|
6574
|
-
generatedSetup = result.setup;
|
|
6777
|
+
failingChecks,
|
|
6778
|
+
currentScore,
|
|
6779
|
+
passingChecks,
|
|
6780
|
+
{ skipSkills: true }
|
|
6781
|
+
);
|
|
6782
|
+
if (!generatedSetup) generatedSetup = result.setup;
|
|
6575
6783
|
rawOutput = result.raw;
|
|
6576
|
-
|
|
6577
|
-
|
|
6784
|
+
genStopReason = result.stopReason;
|
|
6785
|
+
if (!generatedSetup) {
|
|
6786
|
+
display.update(TASK_CONFIG, "failed", "Could not parse LLM response");
|
|
6787
|
+
display.update(TASK_SKILLS_GEN, "failed", "Skipped");
|
|
6788
|
+
return;
|
|
6789
|
+
}
|
|
6790
|
+
display.update(TASK_CONFIG, "done");
|
|
6791
|
+
display.update(TASK_SKILLS_GEN, "running");
|
|
6792
|
+
const skillCount = await generateSkillsForSetup(
|
|
6793
|
+
generatedSetup,
|
|
6794
|
+
fingerprint,
|
|
6795
|
+
targetAgent,
|
|
6796
|
+
(status) => display.update(TASK_SKILLS_GEN, "running", status)
|
|
6797
|
+
);
|
|
6798
|
+
display.update(TASK_SKILLS_GEN, "done", `${skillCount} skills`);
|
|
6799
|
+
})();
|
|
6800
|
+
const SEARCH_TIMEOUT_MS = 6e4;
|
|
6801
|
+
const searchPromise = wantsSkills ? (async () => {
|
|
6802
|
+
display.update(TASK_SKILLS_SEARCH, "running");
|
|
6803
|
+
try {
|
|
6804
|
+
const searchWithTimeout = Promise.race([
|
|
6805
|
+
searchSkills(
|
|
6806
|
+
fingerprint,
|
|
6807
|
+
targetAgent,
|
|
6808
|
+
(status) => display.update(TASK_SKILLS_SEARCH, "running", status)
|
|
6809
|
+
),
|
|
6810
|
+
new Promise(
|
|
6811
|
+
(_, reject) => setTimeout(() => reject(new Error("timeout")), SEARCH_TIMEOUT_MS)
|
|
6812
|
+
)
|
|
6813
|
+
]);
|
|
6814
|
+
skillSearchResult = await searchWithTimeout;
|
|
6815
|
+
const count = skillSearchResult.results.length;
|
|
6816
|
+
display.update(TASK_SKILLS_SEARCH, "done", count > 0 ? `${count} skills found` : "No matches");
|
|
6817
|
+
} catch (err) {
|
|
6818
|
+
const reason = err instanceof Error && err.message === "timeout" ? "Timed out" : "Search failed";
|
|
6819
|
+
display.update(TASK_SKILLS_SEARCH, "failed", reason);
|
|
6820
|
+
}
|
|
6821
|
+
})() : Promise.resolve();
|
|
6822
|
+
await Promise.all([generatePromise, searchPromise]);
|
|
6578
6823
|
} catch (err) {
|
|
6579
|
-
|
|
6824
|
+
display.stop();
|
|
6580
6825
|
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
6581
|
-
|
|
6826
|
+
console.log(chalk10.red(`
|
|
6827
|
+
Engine failed: ${msg}
|
|
6828
|
+
`));
|
|
6582
6829
|
writeErrorLog(config, void 0, msg, "exception");
|
|
6583
6830
|
throw new Error("__exit__");
|
|
6584
6831
|
}
|
|
6585
|
-
|
|
6832
|
+
display.stop();
|
|
6833
|
+
const elapsedMs = Date.now() - genStartTime;
|
|
6834
|
+
trackInitGenerationCompleted(elapsedMs, 0);
|
|
6835
|
+
const mins = Math.floor(elapsedMs / 6e4);
|
|
6836
|
+
const secs = Math.floor(elapsedMs % 6e4 / 1e3);
|
|
6837
|
+
const timeStr = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
|
|
6838
|
+
console.log(chalk10.dim(`
|
|
6839
|
+
Completed in ${timeStr}
|
|
6840
|
+
`));
|
|
6586
6841
|
if (!generatedSetup) {
|
|
6587
|
-
|
|
6842
|
+
console.log(chalk10.red(" Failed to generate setup."));
|
|
6588
6843
|
writeErrorLog(config, rawOutput, void 0, genStopReason);
|
|
6589
6844
|
if (rawOutput) {
|
|
6590
|
-
console.log(
|
|
6591
|
-
console.log(
|
|
6845
|
+
console.log(chalk10.dim("\nRaw LLM output (JSON parse failed):"));
|
|
6846
|
+
console.log(chalk10.dim(rawOutput.slice(0, 500)));
|
|
6592
6847
|
}
|
|
6593
6848
|
throw new Error("__exit__");
|
|
6594
6849
|
}
|
|
@@ -6596,12 +6851,6 @@ async function initCommand(options) {
|
|
|
6596
6851
|
if (rawOutput) report.addCodeBlock("Generation: Raw LLM Response", rawOutput);
|
|
6597
6852
|
report.addJson("Generation: Parsed Setup", generatedSetup);
|
|
6598
6853
|
}
|
|
6599
|
-
const elapsedMs = Date.now() - genStartTime;
|
|
6600
|
-
trackInitGenerationCompleted(elapsedMs, 0);
|
|
6601
|
-
const mins = Math.floor(elapsedMs / 6e4);
|
|
6602
|
-
const secs = Math.floor(elapsedMs % 6e4 / 1e3);
|
|
6603
|
-
const timeStr = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
|
|
6604
|
-
genSpinner.succeed(`Setup generated ${chalk9.dim(`in ${timeStr}`)}`);
|
|
6605
6854
|
log(options.verbose, `Generation completed: ${elapsedMs}ms, stopReason: ${genStopReason || "end_turn"}`);
|
|
6606
6855
|
printSetupSummary(generatedSetup);
|
|
6607
6856
|
const sessionHistory = [];
|
|
@@ -6609,15 +6858,20 @@ async function initCommand(options) {
|
|
|
6609
6858
|
role: "assistant",
|
|
6610
6859
|
content: summarizeSetup("Initial generation", generatedSetup)
|
|
6611
6860
|
});
|
|
6612
|
-
console.log(title.bold(" Step 4
|
|
6861
|
+
console.log(title.bold(" Step 3/4 \u2014 Review\n"));
|
|
6613
6862
|
const setupFiles = collectSetupFiles(generatedSetup, targetAgent);
|
|
6614
6863
|
const staged = stageFiles(setupFiles, process.cwd());
|
|
6615
6864
|
const totalChanges = staged.newFiles + staged.modifiedFiles;
|
|
6616
|
-
console.log(
|
|
6865
|
+
console.log(chalk10.dim(` ${chalk10.green(`${staged.newFiles} new`)} / ${chalk10.yellow(`${staged.modifiedFiles} modified`)} file${totalChanges !== 1 ? "s" : ""}`));
|
|
6866
|
+
if (skillSearchResult.results.length > 0) {
|
|
6867
|
+
console.log(chalk10.dim(` ${chalk10.cyan(`${skillSearchResult.results.length}`)} community skills available to install
|
|
6617
6868
|
`));
|
|
6869
|
+
} else {
|
|
6870
|
+
console.log("");
|
|
6871
|
+
}
|
|
6618
6872
|
let action;
|
|
6619
|
-
if (totalChanges === 0) {
|
|
6620
|
-
console.log(
|
|
6873
|
+
if (totalChanges === 0 && skillSearchResult.results.length === 0) {
|
|
6874
|
+
console.log(chalk10.dim(" No changes needed \u2014 your configs are already up to date.\n"));
|
|
6621
6875
|
cleanupStaging();
|
|
6622
6876
|
action = "accept";
|
|
6623
6877
|
} else if (options.autoApprove) {
|
|
@@ -6625,13 +6879,15 @@ async function initCommand(options) {
|
|
|
6625
6879
|
action = "accept";
|
|
6626
6880
|
trackInitReviewAction(action, "auto-approved");
|
|
6627
6881
|
} else {
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6882
|
+
if (totalChanges > 0) {
|
|
6883
|
+
const wantsReview = await promptWantsReview();
|
|
6884
|
+
if (wantsReview) {
|
|
6885
|
+
const reviewMethod = await promptReviewMethod();
|
|
6886
|
+
await openReview(reviewMethod, staged.stagedFiles);
|
|
6887
|
+
}
|
|
6632
6888
|
}
|
|
6633
6889
|
action = await promptReviewAction();
|
|
6634
|
-
trackInitReviewAction(action,
|
|
6890
|
+
trackInitReviewAction(action, totalChanges > 0 ? "reviewed" : "skipped");
|
|
6635
6891
|
}
|
|
6636
6892
|
let refinementRound = 0;
|
|
6637
6893
|
while (action === "refine") {
|
|
@@ -6640,12 +6896,12 @@ async function initCommand(options) {
|
|
|
6640
6896
|
trackInitRefinementRound(refinementRound, !!generatedSetup);
|
|
6641
6897
|
if (!generatedSetup) {
|
|
6642
6898
|
cleanupStaging();
|
|
6643
|
-
console.log(
|
|
6899
|
+
console.log(chalk10.dim("Refinement cancelled. No files were modified."));
|
|
6644
6900
|
return;
|
|
6645
6901
|
}
|
|
6646
6902
|
const updatedFiles = collectSetupFiles(generatedSetup, targetAgent);
|
|
6647
6903
|
const restaged = stageFiles(updatedFiles, process.cwd());
|
|
6648
|
-
console.log(
|
|
6904
|
+
console.log(chalk10.dim(` ${chalk10.green(`${restaged.newFiles} new`)} / ${chalk10.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
|
|
6649
6905
|
`));
|
|
6650
6906
|
printSetupSummary(generatedSetup);
|
|
6651
6907
|
await openReview("terminal", restaged.stagedFiles);
|
|
@@ -6654,11 +6910,12 @@ async function initCommand(options) {
|
|
|
6654
6910
|
}
|
|
6655
6911
|
cleanupStaging();
|
|
6656
6912
|
if (action === "decline") {
|
|
6657
|
-
console.log(
|
|
6913
|
+
console.log(chalk10.dim("Setup declined. No files were modified."));
|
|
6658
6914
|
return;
|
|
6659
6915
|
}
|
|
6916
|
+
console.log(title.bold("\n Step 4/4 \u2014 Finalize\n"));
|
|
6660
6917
|
if (options.dryRun) {
|
|
6661
|
-
console.log(
|
|
6918
|
+
console.log(chalk10.yellow("\n[Dry run] Would write the following files:"));
|
|
6662
6919
|
console.log(JSON.stringify(generatedSetup, null, 2));
|
|
6663
6920
|
return;
|
|
6664
6921
|
}
|
|
@@ -6687,85 +6944,45 @@ ${agentRefs.join(" ")}
|
|
|
6687
6944
|
0,
|
|
6688
6945
|
result.deleted.length
|
|
6689
6946
|
);
|
|
6690
|
-
console.log(
|
|
6947
|
+
console.log(chalk10.bold("\nFiles created/updated:"));
|
|
6691
6948
|
for (const file of result.written) {
|
|
6692
|
-
console.log(` ${
|
|
6949
|
+
console.log(` ${chalk10.green("\u2713")} ${file}`);
|
|
6693
6950
|
}
|
|
6694
6951
|
if (result.deleted.length > 0) {
|
|
6695
|
-
console.log(
|
|
6952
|
+
console.log(chalk10.bold("\nFiles removed:"));
|
|
6696
6953
|
for (const file of result.deleted) {
|
|
6697
|
-
console.log(` ${
|
|
6954
|
+
console.log(` ${chalk10.red("\u2717")} ${file}`);
|
|
6698
6955
|
}
|
|
6699
6956
|
}
|
|
6700
6957
|
if (result.backupDir) {
|
|
6701
|
-
console.log(
|
|
6958
|
+
console.log(chalk10.dim(`
|
|
6702
6959
|
Backups saved to ${result.backupDir}`));
|
|
6703
6960
|
}
|
|
6704
6961
|
} catch (err) {
|
|
6705
6962
|
writeSpinner.fail("Failed to write files");
|
|
6706
|
-
console.error(
|
|
6963
|
+
console.error(chalk10.red(err instanceof Error ? err.message : "Unknown error"));
|
|
6707
6964
|
throw new Error("__exit__");
|
|
6708
6965
|
}
|
|
6709
|
-
ensurePermissions(fingerprint);
|
|
6966
|
+
if (fingerprint) ensurePermissions(fingerprint);
|
|
6710
6967
|
const sha = getCurrentHeadSha();
|
|
6711
6968
|
writeState({
|
|
6712
6969
|
lastRefreshSha: sha ?? "",
|
|
6713
6970
|
lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6714
6971
|
targetAgent
|
|
6715
6972
|
});
|
|
6716
|
-
console.log("");
|
|
6717
|
-
console.log(chalk9.dim(" Auto-refresh: keep your configs in sync as your code changes.\n"));
|
|
6718
|
-
let hookChoice;
|
|
6719
|
-
if (options.autoApprove) {
|
|
6720
|
-
hookChoice = "skip";
|
|
6721
|
-
log(options.verbose, "Auto-approve: skipping hook installation");
|
|
6722
|
-
} else {
|
|
6723
|
-
hookChoice = await promptHookType(targetAgent);
|
|
6724
|
-
}
|
|
6725
|
-
trackInitHookSelected(hookChoice);
|
|
6726
|
-
if (hookChoice === "claude" || hookChoice === "both") {
|
|
6727
|
-
const hookResult = installHook();
|
|
6728
|
-
if (hookResult.installed) {
|
|
6729
|
-
console.log(` ${chalk9.green("\u2713")} Claude Code hook installed \u2014 docs update on session end`);
|
|
6730
|
-
console.log(chalk9.dim(" Run ") + chalk9.hex("#83D1EB")("caliber hooks --remove") + chalk9.dim(" to disable"));
|
|
6731
|
-
} else if (hookResult.alreadyInstalled) {
|
|
6732
|
-
console.log(chalk9.dim(" Claude Code hook already installed"));
|
|
6733
|
-
}
|
|
6734
|
-
const learnResult = installLearningHooks();
|
|
6735
|
-
if (learnResult.installed) {
|
|
6736
|
-
console.log(` ${chalk9.green("\u2713")} Learning hooks installed \u2014 session insights captured automatically`);
|
|
6737
|
-
console.log(chalk9.dim(" Run ") + chalk9.hex("#83D1EB")("caliber learn remove") + chalk9.dim(" to disable"));
|
|
6738
|
-
} else if (learnResult.alreadyInstalled) {
|
|
6739
|
-
console.log(chalk9.dim(" Learning hooks already installed"));
|
|
6740
|
-
}
|
|
6741
|
-
}
|
|
6742
|
-
if (hookChoice === "precommit" || hookChoice === "both") {
|
|
6743
|
-
const precommitResult = installPreCommitHook();
|
|
6744
|
-
if (precommitResult.installed) {
|
|
6745
|
-
console.log(` ${chalk9.green("\u2713")} Pre-commit hook installed \u2014 docs refresh before each commit`);
|
|
6746
|
-
console.log(chalk9.dim(" Run ") + chalk9.hex("#83D1EB")("caliber hooks --remove") + chalk9.dim(" to disable"));
|
|
6747
|
-
} else if (precommitResult.alreadyInstalled) {
|
|
6748
|
-
console.log(chalk9.dim(" Pre-commit hook already installed"));
|
|
6749
|
-
} else {
|
|
6750
|
-
console.log(chalk9.yellow(" Could not install pre-commit hook (not a git repository?)"));
|
|
6751
|
-
}
|
|
6752
|
-
}
|
|
6753
|
-
if (hookChoice === "skip") {
|
|
6754
|
-
console.log(chalk9.dim(" Skipped auto-refresh hooks. Run ") + chalk9.hex("#83D1EB")("caliber hooks --install") + chalk9.dim(" later to enable."));
|
|
6755
|
-
}
|
|
6756
6973
|
const afterScore = computeLocalScore(process.cwd(), targetAgent);
|
|
6757
6974
|
if (afterScore.score < baselineScore.score) {
|
|
6758
6975
|
trackInitScoreRegression(baselineScore.score, afterScore.score);
|
|
6759
6976
|
console.log("");
|
|
6760
|
-
console.log(
|
|
6977
|
+
console.log(chalk10.yellow(` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`));
|
|
6761
6978
|
try {
|
|
6762
6979
|
const { restored, removed } = undoSetup();
|
|
6763
6980
|
if (restored.length > 0 || removed.length > 0) {
|
|
6764
|
-
console.log(
|
|
6981
|
+
console.log(chalk10.dim(` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`));
|
|
6765
6982
|
}
|
|
6766
6983
|
} catch {
|
|
6767
6984
|
}
|
|
6768
|
-
console.log(
|
|
6985
|
+
console.log(chalk10.dim(" Run ") + chalk10.hex("#83D1EB")("caliber init --force") + chalk10.dim(" to override.\n"));
|
|
6769
6986
|
return;
|
|
6770
6987
|
}
|
|
6771
6988
|
if (report) {
|
|
@@ -6783,39 +7000,57 @@ ${agentRefs.join(" ")}
|
|
|
6783
7000
|
log(options.verbose, ` Still failing: ${c.name} (${c.earnedPoints}/${c.maxPoints})${c.suggestion ? ` \u2014 ${c.suggestion}` : ""}`);
|
|
6784
7001
|
}
|
|
6785
7002
|
}
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
|
|
7003
|
+
if (skillSearchResult.results.length > 0 && !options.autoApprove) {
|
|
7004
|
+
console.log(chalk10.dim(" Community skills matched to your project:\n"));
|
|
7005
|
+
const selected = await interactiveSelect(skillSearchResult.results);
|
|
7006
|
+
if (selected?.length) {
|
|
7007
|
+
await installSkills(selected, targetAgent, skillSearchResult.contentMap);
|
|
7008
|
+
trackInitSkillsSearch(true, selected.length);
|
|
7009
|
+
}
|
|
7010
|
+
}
|
|
7011
|
+
console.log("");
|
|
7012
|
+
let hookChoice;
|
|
6789
7013
|
if (options.autoApprove) {
|
|
6790
|
-
|
|
6791
|
-
log(options.verbose, "Auto-approve: skipping
|
|
7014
|
+
hookChoice = "skip";
|
|
7015
|
+
log(options.verbose, "Auto-approve: skipping hook installation");
|
|
6792
7016
|
} else {
|
|
6793
|
-
|
|
6794
|
-
message: "Search public repos for relevant skills to add to this project?",
|
|
6795
|
-
choices: [
|
|
6796
|
-
{ name: "Yes, find skills for my project", value: true },
|
|
6797
|
-
{ name: "Skip for now", value: false }
|
|
6798
|
-
]
|
|
6799
|
-
});
|
|
7017
|
+
hookChoice = await promptHookType(targetAgent);
|
|
6800
7018
|
}
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
7019
|
+
trackInitHookSelected(hookChoice);
|
|
7020
|
+
if (hookChoice === "claude" || hookChoice === "both") {
|
|
7021
|
+
const hookResult = installHook();
|
|
7022
|
+
if (hookResult.installed) {
|
|
7023
|
+
console.log(` ${chalk10.green("\u2713")} Claude Code hook installed \u2014 docs update on session end`);
|
|
7024
|
+
console.log(chalk10.dim(" Run ") + chalk10.hex("#83D1EB")("caliber hooks --remove") + chalk10.dim(" to disable"));
|
|
7025
|
+
} else if (hookResult.alreadyInstalled) {
|
|
7026
|
+
console.log(chalk10.dim(" Claude Code hook already installed"));
|
|
7027
|
+
}
|
|
7028
|
+
const learnResult = installLearningHooks();
|
|
7029
|
+
if (learnResult.installed) {
|
|
7030
|
+
console.log(` ${chalk10.green("\u2713")} Learning hooks installed \u2014 session insights captured automatically`);
|
|
7031
|
+
console.log(chalk10.dim(" Run ") + chalk10.hex("#83D1EB")("caliber learn remove") + chalk10.dim(" to disable"));
|
|
7032
|
+
} else if (learnResult.alreadyInstalled) {
|
|
7033
|
+
console.log(chalk10.dim(" Learning hooks already installed"));
|
|
6810
7034
|
}
|
|
6811
|
-
} else {
|
|
6812
|
-
trackInitSkillsSearch(false, 0);
|
|
6813
|
-
console.log(chalk9.dim(" Skipped. Run ") + chalk9.hex("#83D1EB")("caliber skills") + chalk9.dim(" later to browse.\n"));
|
|
6814
7035
|
}
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
7036
|
+
if (hookChoice === "precommit" || hookChoice === "both") {
|
|
7037
|
+
const precommitResult = installPreCommitHook();
|
|
7038
|
+
if (precommitResult.installed) {
|
|
7039
|
+
console.log(` ${chalk10.green("\u2713")} Pre-commit hook installed \u2014 docs refresh before each commit`);
|
|
7040
|
+
console.log(chalk10.dim(" Run ") + chalk10.hex("#83D1EB")("caliber hooks --remove") + chalk10.dim(" to disable"));
|
|
7041
|
+
} else if (precommitResult.alreadyInstalled) {
|
|
7042
|
+
console.log(chalk10.dim(" Pre-commit hook already installed"));
|
|
7043
|
+
} else {
|
|
7044
|
+
console.log(chalk10.yellow(" Could not install pre-commit hook (not a git repository?)"));
|
|
7045
|
+
}
|
|
7046
|
+
}
|
|
7047
|
+
if (hookChoice === "skip") {
|
|
7048
|
+
console.log(chalk10.dim(" Skipped auto-sync hooks. Run ") + chalk10.hex("#83D1EB")("caliber hooks --install") + chalk10.dim(" later to enable."));
|
|
7049
|
+
}
|
|
7050
|
+
console.log(chalk10.bold.green("\n Setup complete!"));
|
|
7051
|
+
console.log(chalk10.dim(" Your AI agents now understand your project's architecture, build commands,"));
|
|
7052
|
+
console.log(chalk10.dim(" testing patterns, and conventions. All changes are backed up automatically.\n"));
|
|
7053
|
+
console.log(chalk10.bold(" Next steps:\n"));
|
|
6819
7054
|
console.log(` ${title("caliber score")} See your full config breakdown`);
|
|
6820
7055
|
console.log(` ${title("caliber refresh")} Update docs after code changes`);
|
|
6821
7056
|
console.log(` ${title("caliber hooks")} Toggle auto-refresh hooks`);
|
|
@@ -6829,7 +7064,7 @@ ${agentRefs.join(" ")}
|
|
|
6829
7064
|
report.markStep("Finished");
|
|
6830
7065
|
const reportPath = path19.join(process.cwd(), ".caliber", "debug-report.md");
|
|
6831
7066
|
report.write(reportPath);
|
|
6832
|
-
console.log(
|
|
7067
|
+
console.log(chalk10.dim(` Debug report written to ${path19.relative(process.cwd(), reportPath)}
|
|
6833
7068
|
`));
|
|
6834
7069
|
}
|
|
6835
7070
|
}
|
|
@@ -6844,9 +7079,9 @@ async function refineLoop(currentSetup, _targetAgent, sessionHistory) {
|
|
|
6844
7079
|
}
|
|
6845
7080
|
const isValid = await classifyRefineIntent(message);
|
|
6846
7081
|
if (!isValid) {
|
|
6847
|
-
console.log(
|
|
6848
|
-
console.log(
|
|
6849
|
-
console.log(
|
|
7082
|
+
console.log(chalk10.dim(" This doesn't look like a config change request."));
|
|
7083
|
+
console.log(chalk10.dim(" Describe what to add, remove, or modify in your configs."));
|
|
7084
|
+
console.log(chalk10.dim(' Type "done" to accept the current setup.\n'));
|
|
6850
7085
|
continue;
|
|
6851
7086
|
}
|
|
6852
7087
|
const refineSpinner = ora2("Refining setup...").start();
|
|
@@ -6867,10 +7102,10 @@ async function refineLoop(currentSetup, _targetAgent, sessionHistory) {
|
|
|
6867
7102
|
});
|
|
6868
7103
|
refineSpinner.succeed("Setup updated");
|
|
6869
7104
|
printSetupSummary(refined);
|
|
6870
|
-
console.log(
|
|
7105
|
+
console.log(chalk10.dim('Type "done" to accept, or describe more changes.'));
|
|
6871
7106
|
} else {
|
|
6872
7107
|
refineSpinner.fail("Refinement failed \u2014 could not parse AI response.");
|
|
6873
|
-
console.log(
|
|
7108
|
+
console.log(chalk10.dim('Try rephrasing your request, or type "done" to keep the current setup.'));
|
|
6874
7109
|
}
|
|
6875
7110
|
}
|
|
6876
7111
|
}
|
|
@@ -6898,6 +7133,7 @@ Return {"valid": true} or {"valid": false}. Nothing else.`,
|
|
|
6898
7133
|
}
|
|
6899
7134
|
}
|
|
6900
7135
|
async function evaluateDismissals(failingChecks, fingerprint) {
|
|
7136
|
+
if (failingChecks.length === 0) return [];
|
|
6901
7137
|
const fastModel = getFastModel();
|
|
6902
7138
|
const checkList = failingChecks.map((c) => ({
|
|
6903
7139
|
id: c.id,
|
|
@@ -6965,7 +7201,7 @@ async function promptHookType(targetAgent) {
|
|
|
6965
7201
|
}
|
|
6966
7202
|
choices.push({ name: "Skip for now", value: "skip" });
|
|
6967
7203
|
return select5({
|
|
6968
|
-
message: "
|
|
7204
|
+
message: "Keep your AI docs & skills in sync as your code evolves?",
|
|
6969
7205
|
choices
|
|
6970
7206
|
});
|
|
6971
7207
|
}
|
|
@@ -6985,26 +7221,26 @@ function printSetupSummary(setup) {
|
|
|
6985
7221
|
const fileDescriptions = setup.fileDescriptions;
|
|
6986
7222
|
const deletions = setup.deletions;
|
|
6987
7223
|
console.log("");
|
|
6988
|
-
console.log(
|
|
7224
|
+
console.log(chalk10.bold(" Proposed changes:\n"));
|
|
6989
7225
|
const getDescription = (filePath) => {
|
|
6990
7226
|
return fileDescriptions?.[filePath];
|
|
6991
7227
|
};
|
|
6992
7228
|
if (claude) {
|
|
6993
7229
|
if (claude.claudeMd) {
|
|
6994
|
-
const icon = fs24.existsSync("CLAUDE.md") ?
|
|
7230
|
+
const icon = fs24.existsSync("CLAUDE.md") ? chalk10.yellow("~") : chalk10.green("+");
|
|
6995
7231
|
const desc = getDescription("CLAUDE.md");
|
|
6996
|
-
console.log(` ${icon} ${
|
|
6997
|
-
if (desc) console.log(
|
|
7232
|
+
console.log(` ${icon} ${chalk10.bold("CLAUDE.md")}`);
|
|
7233
|
+
if (desc) console.log(chalk10.dim(` ${desc}`));
|
|
6998
7234
|
console.log("");
|
|
6999
7235
|
}
|
|
7000
7236
|
const skills = claude.skills;
|
|
7001
7237
|
if (Array.isArray(skills) && skills.length > 0) {
|
|
7002
7238
|
for (const skill of skills) {
|
|
7003
7239
|
const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
|
|
7004
|
-
const icon = fs24.existsSync(skillPath) ?
|
|
7240
|
+
const icon = fs24.existsSync(skillPath) ? chalk10.yellow("~") : chalk10.green("+");
|
|
7005
7241
|
const desc = getDescription(skillPath);
|
|
7006
|
-
console.log(` ${icon} ${
|
|
7007
|
-
console.log(
|
|
7242
|
+
console.log(` ${icon} ${chalk10.bold(skillPath)}`);
|
|
7243
|
+
console.log(chalk10.dim(` ${desc || skill.description || skill.name}`));
|
|
7008
7244
|
console.log("");
|
|
7009
7245
|
}
|
|
7010
7246
|
}
|
|
@@ -7012,40 +7248,40 @@ function printSetupSummary(setup) {
|
|
|
7012
7248
|
const codex = setup.codex;
|
|
7013
7249
|
if (codex) {
|
|
7014
7250
|
if (codex.agentsMd) {
|
|
7015
|
-
const icon = fs24.existsSync("AGENTS.md") ?
|
|
7251
|
+
const icon = fs24.existsSync("AGENTS.md") ? chalk10.yellow("~") : chalk10.green("+");
|
|
7016
7252
|
const desc = getDescription("AGENTS.md");
|
|
7017
|
-
console.log(` ${icon} ${
|
|
7018
|
-
if (desc) console.log(
|
|
7253
|
+
console.log(` ${icon} ${chalk10.bold("AGENTS.md")}`);
|
|
7254
|
+
if (desc) console.log(chalk10.dim(` ${desc}`));
|
|
7019
7255
|
console.log("");
|
|
7020
7256
|
}
|
|
7021
7257
|
const codexSkills = codex.skills;
|
|
7022
7258
|
if (Array.isArray(codexSkills) && codexSkills.length > 0) {
|
|
7023
7259
|
for (const skill of codexSkills) {
|
|
7024
7260
|
const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
|
|
7025
|
-
const icon = fs24.existsSync(skillPath) ?
|
|
7261
|
+
const icon = fs24.existsSync(skillPath) ? chalk10.yellow("~") : chalk10.green("+");
|
|
7026
7262
|
const desc = getDescription(skillPath);
|
|
7027
|
-
console.log(` ${icon} ${
|
|
7028
|
-
console.log(
|
|
7263
|
+
console.log(` ${icon} ${chalk10.bold(skillPath)}`);
|
|
7264
|
+
console.log(chalk10.dim(` ${desc || skill.description || skill.name}`));
|
|
7029
7265
|
console.log("");
|
|
7030
7266
|
}
|
|
7031
7267
|
}
|
|
7032
7268
|
}
|
|
7033
7269
|
if (cursor) {
|
|
7034
7270
|
if (cursor.cursorrules) {
|
|
7035
|
-
const icon = fs24.existsSync(".cursorrules") ?
|
|
7271
|
+
const icon = fs24.existsSync(".cursorrules") ? chalk10.yellow("~") : chalk10.green("+");
|
|
7036
7272
|
const desc = getDescription(".cursorrules");
|
|
7037
|
-
console.log(` ${icon} ${
|
|
7038
|
-
if (desc) console.log(
|
|
7273
|
+
console.log(` ${icon} ${chalk10.bold(".cursorrules")}`);
|
|
7274
|
+
if (desc) console.log(chalk10.dim(` ${desc}`));
|
|
7039
7275
|
console.log("");
|
|
7040
7276
|
}
|
|
7041
7277
|
const cursorSkills = cursor.skills;
|
|
7042
7278
|
if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
|
|
7043
7279
|
for (const skill of cursorSkills) {
|
|
7044
7280
|
const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
|
|
7045
|
-
const icon = fs24.existsSync(skillPath) ?
|
|
7281
|
+
const icon = fs24.existsSync(skillPath) ? chalk10.yellow("~") : chalk10.green("+");
|
|
7046
7282
|
const desc = getDescription(skillPath);
|
|
7047
|
-
console.log(` ${icon} ${
|
|
7048
|
-
console.log(
|
|
7283
|
+
console.log(` ${icon} ${chalk10.bold(skillPath)}`);
|
|
7284
|
+
console.log(chalk10.dim(` ${desc || skill.description || skill.name}`));
|
|
7049
7285
|
console.log("");
|
|
7050
7286
|
}
|
|
7051
7287
|
}
|
|
@@ -7053,14 +7289,14 @@ function printSetupSummary(setup) {
|
|
|
7053
7289
|
if (Array.isArray(rules) && rules.length > 0) {
|
|
7054
7290
|
for (const rule of rules) {
|
|
7055
7291
|
const rulePath = `.cursor/rules/${rule.filename}`;
|
|
7056
|
-
const icon = fs24.existsSync(rulePath) ?
|
|
7292
|
+
const icon = fs24.existsSync(rulePath) ? chalk10.yellow("~") : chalk10.green("+");
|
|
7057
7293
|
const desc = getDescription(rulePath);
|
|
7058
|
-
console.log(` ${icon} ${
|
|
7294
|
+
console.log(` ${icon} ${chalk10.bold(rulePath)}`);
|
|
7059
7295
|
if (desc) {
|
|
7060
|
-
console.log(
|
|
7296
|
+
console.log(chalk10.dim(` ${desc}`));
|
|
7061
7297
|
} else {
|
|
7062
7298
|
const firstLine = rule.content.split("\n").filter((l) => l.trim() && !l.trim().startsWith("#"))[0];
|
|
7063
|
-
if (firstLine) console.log(
|
|
7299
|
+
if (firstLine) console.log(chalk10.dim(` ${firstLine.trim().slice(0, 80)}`));
|
|
7064
7300
|
}
|
|
7065
7301
|
console.log("");
|
|
7066
7302
|
}
|
|
@@ -7068,12 +7304,12 @@ function printSetupSummary(setup) {
|
|
|
7068
7304
|
}
|
|
7069
7305
|
if (Array.isArray(deletions) && deletions.length > 0) {
|
|
7070
7306
|
for (const del of deletions) {
|
|
7071
|
-
console.log(` ${
|
|
7072
|
-
console.log(
|
|
7307
|
+
console.log(` ${chalk10.red("-")} ${chalk10.bold(del.filePath)}`);
|
|
7308
|
+
console.log(chalk10.dim(` ${del.reason}`));
|
|
7073
7309
|
console.log("");
|
|
7074
7310
|
}
|
|
7075
7311
|
}
|
|
7076
|
-
console.log(` ${
|
|
7312
|
+
console.log(` ${chalk10.green("+")} ${chalk10.dim("new")} ${chalk10.yellow("~")} ${chalk10.dim("modified")} ${chalk10.red("-")} ${chalk10.dim("removed")}`);
|
|
7077
7313
|
console.log("");
|
|
7078
7314
|
}
|
|
7079
7315
|
function derivePermissions(fingerprint) {
|
|
@@ -7133,20 +7369,20 @@ function ensurePermissions(fingerprint) {
|
|
|
7133
7369
|
function displayTokenUsage() {
|
|
7134
7370
|
const summary = getUsageSummary();
|
|
7135
7371
|
if (summary.length === 0) {
|
|
7136
|
-
console.log(
|
|
7372
|
+
console.log(chalk10.dim(" Token tracking not available for this provider.\n"));
|
|
7137
7373
|
return;
|
|
7138
7374
|
}
|
|
7139
|
-
console.log(
|
|
7375
|
+
console.log(chalk10.bold(" Token usage:\n"));
|
|
7140
7376
|
let totalIn = 0;
|
|
7141
7377
|
let totalOut = 0;
|
|
7142
7378
|
for (const m of summary) {
|
|
7143
7379
|
totalIn += m.inputTokens;
|
|
7144
7380
|
totalOut += m.outputTokens;
|
|
7145
|
-
const cacheInfo = m.cacheReadTokens > 0 || m.cacheWriteTokens > 0 ?
|
|
7146
|
-
console.log(` ${
|
|
7381
|
+
const cacheInfo = m.cacheReadTokens > 0 || m.cacheWriteTokens > 0 ? chalk10.dim(` (cache: ${m.cacheReadTokens.toLocaleString()} read, ${m.cacheWriteTokens.toLocaleString()} write)`) : "";
|
|
7382
|
+
console.log(` ${chalk10.dim(m.model)}: ${m.inputTokens.toLocaleString()} in / ${m.outputTokens.toLocaleString()} out (${m.calls} call${m.calls === 1 ? "" : "s"})${cacheInfo}`);
|
|
7147
7383
|
}
|
|
7148
7384
|
if (summary.length > 1) {
|
|
7149
|
-
console.log(` ${
|
|
7385
|
+
console.log(` ${chalk10.dim("Total")}: ${totalIn.toLocaleString()} in / ${totalOut.toLocaleString()} out`);
|
|
7150
7386
|
}
|
|
7151
7387
|
console.log("");
|
|
7152
7388
|
}
|
|
@@ -7167,14 +7403,14 @@ function writeErrorLog(config, rawOutput, error, stopReason) {
|
|
|
7167
7403
|
lines.push("## Raw LLM Output", "```", rawOutput || "(empty)", "```");
|
|
7168
7404
|
fs24.mkdirSync(path19.join(process.cwd(), ".caliber"), { recursive: true });
|
|
7169
7405
|
fs24.writeFileSync(logPath, lines.join("\n"));
|
|
7170
|
-
console.log(
|
|
7406
|
+
console.log(chalk10.dim(`
|
|
7171
7407
|
Error log written to .caliber/error-log.md`));
|
|
7172
7408
|
} catch {
|
|
7173
7409
|
}
|
|
7174
7410
|
}
|
|
7175
7411
|
|
|
7176
7412
|
// src/commands/undo.ts
|
|
7177
|
-
import
|
|
7413
|
+
import chalk11 from "chalk";
|
|
7178
7414
|
import ora3 from "ora";
|
|
7179
7415
|
function undoCommand() {
|
|
7180
7416
|
const spinner = ora3("Reverting setup...").start();
|
|
@@ -7187,26 +7423,26 @@ function undoCommand() {
|
|
|
7187
7423
|
trackUndoExecuted();
|
|
7188
7424
|
spinner.succeed("Setup reverted successfully.\n");
|
|
7189
7425
|
if (restored.length > 0) {
|
|
7190
|
-
console.log(
|
|
7426
|
+
console.log(chalk11.cyan(" Restored from backup:"));
|
|
7191
7427
|
for (const file of restored) {
|
|
7192
|
-
console.log(` ${
|
|
7428
|
+
console.log(` ${chalk11.green("\u21A9")} ${file}`);
|
|
7193
7429
|
}
|
|
7194
7430
|
}
|
|
7195
7431
|
if (removed.length > 0) {
|
|
7196
|
-
console.log(
|
|
7432
|
+
console.log(chalk11.cyan(" Removed:"));
|
|
7197
7433
|
for (const file of removed) {
|
|
7198
|
-
console.log(` ${
|
|
7434
|
+
console.log(` ${chalk11.red("\u2717")} ${file}`);
|
|
7199
7435
|
}
|
|
7200
7436
|
}
|
|
7201
7437
|
console.log("");
|
|
7202
7438
|
} catch (err) {
|
|
7203
|
-
spinner.fail(
|
|
7439
|
+
spinner.fail(chalk11.red(err instanceof Error ? err.message : "Undo failed"));
|
|
7204
7440
|
throw new Error("__exit__");
|
|
7205
7441
|
}
|
|
7206
7442
|
}
|
|
7207
7443
|
|
|
7208
7444
|
// src/commands/status.ts
|
|
7209
|
-
import
|
|
7445
|
+
import chalk12 from "chalk";
|
|
7210
7446
|
import fs25 from "fs";
|
|
7211
7447
|
init_config();
|
|
7212
7448
|
async function statusCommand(options) {
|
|
@@ -7221,40 +7457,40 @@ async function statusCommand(options) {
|
|
|
7221
7457
|
}, null, 2));
|
|
7222
7458
|
return;
|
|
7223
7459
|
}
|
|
7224
|
-
console.log(
|
|
7460
|
+
console.log(chalk12.bold("\nCaliber Status\n"));
|
|
7225
7461
|
if (config) {
|
|
7226
|
-
console.log(` LLM: ${
|
|
7462
|
+
console.log(` LLM: ${chalk12.green(config.provider)} (${config.model})`);
|
|
7227
7463
|
} else {
|
|
7228
|
-
console.log(` LLM: ${
|
|
7464
|
+
console.log(` LLM: ${chalk12.yellow("Not configured")} \u2014 run ${chalk12.hex("#83D1EB")("caliber config")}`);
|
|
7229
7465
|
}
|
|
7230
7466
|
if (!manifest) {
|
|
7231
|
-
console.log(` Setup: ${
|
|
7232
|
-
console.log(
|
|
7467
|
+
console.log(` Setup: ${chalk12.dim("No setup applied")}`);
|
|
7468
|
+
console.log(chalk12.dim("\n Run ") + chalk12.hex("#83D1EB")("caliber init") + chalk12.dim(" to get started.\n"));
|
|
7233
7469
|
return;
|
|
7234
7470
|
}
|
|
7235
|
-
console.log(` Files managed: ${
|
|
7471
|
+
console.log(` Files managed: ${chalk12.cyan(manifest.entries.length.toString())}`);
|
|
7236
7472
|
for (const entry of manifest.entries) {
|
|
7237
7473
|
const exists = fs25.existsSync(entry.path);
|
|
7238
|
-
const icon = exists ?
|
|
7474
|
+
const icon = exists ? chalk12.green("\u2713") : chalk12.red("\u2717");
|
|
7239
7475
|
console.log(` ${icon} ${entry.path} (${entry.action})`);
|
|
7240
7476
|
}
|
|
7241
7477
|
console.log("");
|
|
7242
7478
|
}
|
|
7243
7479
|
|
|
7244
7480
|
// src/commands/regenerate.ts
|
|
7245
|
-
import
|
|
7481
|
+
import chalk13 from "chalk";
|
|
7246
7482
|
import ora4 from "ora";
|
|
7247
7483
|
import select6 from "@inquirer/select";
|
|
7248
7484
|
init_config();
|
|
7249
7485
|
async function regenerateCommand(options) {
|
|
7250
7486
|
const config = loadConfig();
|
|
7251
7487
|
if (!config) {
|
|
7252
|
-
console.log(
|
|
7488
|
+
console.log(chalk13.red("No LLM provider configured. Run ") + chalk13.hex("#83D1EB")("caliber config") + chalk13.red(" first."));
|
|
7253
7489
|
throw new Error("__exit__");
|
|
7254
7490
|
}
|
|
7255
7491
|
const manifest = readManifest();
|
|
7256
7492
|
if (!manifest) {
|
|
7257
|
-
console.log(
|
|
7493
|
+
console.log(chalk13.yellow("No existing setup found. Run ") + chalk13.hex("#83D1EB")("caliber init") + chalk13.yellow(" first."));
|
|
7258
7494
|
throw new Error("__exit__");
|
|
7259
7495
|
}
|
|
7260
7496
|
const targetAgent = readState()?.targetAgent ?? ["claude", "cursor"];
|
|
@@ -7265,7 +7501,7 @@ async function regenerateCommand(options) {
|
|
|
7265
7501
|
const baselineScore = computeLocalScore(process.cwd(), targetAgent);
|
|
7266
7502
|
displayScoreSummary(baselineScore);
|
|
7267
7503
|
if (baselineScore.score === 100) {
|
|
7268
|
-
console.log(
|
|
7504
|
+
console.log(chalk13.green(" Your setup is already at 100/100 \u2014 nothing to regenerate.\n"));
|
|
7269
7505
|
return;
|
|
7270
7506
|
}
|
|
7271
7507
|
const genSpinner = ora4("Regenerating setup...").start();
|
|
@@ -7306,18 +7542,18 @@ async function regenerateCommand(options) {
|
|
|
7306
7542
|
const setupFiles = collectSetupFiles(generatedSetup, targetAgent);
|
|
7307
7543
|
const staged = stageFiles(setupFiles, process.cwd());
|
|
7308
7544
|
const totalChanges = staged.newFiles + staged.modifiedFiles;
|
|
7309
|
-
console.log(
|
|
7310
|
-
${
|
|
7545
|
+
console.log(chalk13.dim(`
|
|
7546
|
+
${chalk13.green(`${staged.newFiles} new`)} / ${chalk13.yellow(`${staged.modifiedFiles} modified`)} file${totalChanges !== 1 ? "s" : ""}
|
|
7311
7547
|
`));
|
|
7312
7548
|
if (totalChanges === 0) {
|
|
7313
|
-
console.log(
|
|
7549
|
+
console.log(chalk13.dim(" No changes needed \u2014 your configs are already up to date.\n"));
|
|
7314
7550
|
cleanupStaging();
|
|
7315
7551
|
return;
|
|
7316
7552
|
}
|
|
7317
7553
|
if (options.dryRun) {
|
|
7318
|
-
console.log(
|
|
7554
|
+
console.log(chalk13.yellow("[Dry run] Would write:"));
|
|
7319
7555
|
for (const f of staged.stagedFiles) {
|
|
7320
|
-
console.log(` ${f.isNew ?
|
|
7556
|
+
console.log(` ${f.isNew ? chalk13.green("+") : chalk13.yellow("~")} ${f.relativePath}`);
|
|
7321
7557
|
}
|
|
7322
7558
|
cleanupStaging();
|
|
7323
7559
|
return;
|
|
@@ -7336,7 +7572,7 @@ async function regenerateCommand(options) {
|
|
|
7336
7572
|
});
|
|
7337
7573
|
cleanupStaging();
|
|
7338
7574
|
if (action === "decline") {
|
|
7339
|
-
console.log(
|
|
7575
|
+
console.log(chalk13.dim("Regeneration cancelled. No files were modified."));
|
|
7340
7576
|
return;
|
|
7341
7577
|
}
|
|
7342
7578
|
const writeSpinner = ora4("Writing config files...").start();
|
|
@@ -7344,20 +7580,20 @@ async function regenerateCommand(options) {
|
|
|
7344
7580
|
const result = writeSetup(generatedSetup);
|
|
7345
7581
|
writeSpinner.succeed("Config files written");
|
|
7346
7582
|
for (const file of result.written) {
|
|
7347
|
-
console.log(` ${
|
|
7583
|
+
console.log(` ${chalk13.green("\u2713")} ${file}`);
|
|
7348
7584
|
}
|
|
7349
7585
|
if (result.deleted.length > 0) {
|
|
7350
7586
|
for (const file of result.deleted) {
|
|
7351
|
-
console.log(` ${
|
|
7587
|
+
console.log(` ${chalk13.red("\u2717")} ${file}`);
|
|
7352
7588
|
}
|
|
7353
7589
|
}
|
|
7354
7590
|
if (result.backupDir) {
|
|
7355
|
-
console.log(
|
|
7591
|
+
console.log(chalk13.dim(`
|
|
7356
7592
|
Backups saved to ${result.backupDir}`));
|
|
7357
7593
|
}
|
|
7358
7594
|
} catch (err) {
|
|
7359
7595
|
writeSpinner.fail("Failed to write files");
|
|
7360
|
-
console.error(
|
|
7596
|
+
console.error(chalk13.red(err instanceof Error ? err.message : "Unknown error"));
|
|
7361
7597
|
throw new Error("__exit__");
|
|
7362
7598
|
}
|
|
7363
7599
|
const sha = getCurrentHeadSha();
|
|
@@ -7369,25 +7605,25 @@ async function regenerateCommand(options) {
|
|
|
7369
7605
|
const afterScore = computeLocalScore(process.cwd(), targetAgent);
|
|
7370
7606
|
if (afterScore.score < baselineScore.score) {
|
|
7371
7607
|
console.log("");
|
|
7372
|
-
console.log(
|
|
7608
|
+
console.log(chalk13.yellow(` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`));
|
|
7373
7609
|
try {
|
|
7374
7610
|
const { restored, removed } = undoSetup();
|
|
7375
7611
|
if (restored.length > 0 || removed.length > 0) {
|
|
7376
|
-
console.log(
|
|
7612
|
+
console.log(chalk13.dim(` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`));
|
|
7377
7613
|
}
|
|
7378
7614
|
} catch {
|
|
7379
7615
|
}
|
|
7380
|
-
console.log(
|
|
7616
|
+
console.log(chalk13.dim(" Run ") + chalk13.hex("#83D1EB")("caliber init --force") + chalk13.dim(" to override.\n"));
|
|
7381
7617
|
return;
|
|
7382
7618
|
}
|
|
7383
7619
|
displayScoreDelta(baselineScore, afterScore);
|
|
7384
7620
|
trackRegenerateCompleted(action, Date.now());
|
|
7385
|
-
console.log(
|
|
7386
|
-
console.log(
|
|
7621
|
+
console.log(chalk13.bold.green(" Regeneration complete!"));
|
|
7622
|
+
console.log(chalk13.dim(" Run ") + chalk13.hex("#83D1EB")("caliber undo") + chalk13.dim(" to revert changes.\n"));
|
|
7387
7623
|
}
|
|
7388
7624
|
|
|
7389
7625
|
// src/commands/score.ts
|
|
7390
|
-
import
|
|
7626
|
+
import chalk14 from "chalk";
|
|
7391
7627
|
async function scoreCommand(options) {
|
|
7392
7628
|
const dir = process.cwd();
|
|
7393
7629
|
const target = options.agent ?? readState()?.targetAgent;
|
|
@@ -7402,14 +7638,14 @@ async function scoreCommand(options) {
|
|
|
7402
7638
|
return;
|
|
7403
7639
|
}
|
|
7404
7640
|
displayScore(result);
|
|
7405
|
-
const separator =
|
|
7641
|
+
const separator = chalk14.gray(" " + "\u2500".repeat(53));
|
|
7406
7642
|
console.log(separator);
|
|
7407
7643
|
if (result.score < 40) {
|
|
7408
|
-
console.log(
|
|
7644
|
+
console.log(chalk14.gray(" Run ") + chalk14.hex("#83D1EB")("caliber init") + chalk14.gray(" to generate a complete, optimized setup."));
|
|
7409
7645
|
} else if (result.score < 70) {
|
|
7410
|
-
console.log(
|
|
7646
|
+
console.log(chalk14.gray(" Run ") + chalk14.hex("#83D1EB")("caliber init") + chalk14.gray(" to improve your setup."));
|
|
7411
7647
|
} else {
|
|
7412
|
-
console.log(
|
|
7648
|
+
console.log(chalk14.green(" Looking good!") + chalk14.gray(" Run ") + chalk14.hex("#83D1EB")("caliber regenerate") + chalk14.gray(" to rebuild from scratch."));
|
|
7413
7649
|
}
|
|
7414
7650
|
console.log("");
|
|
7415
7651
|
}
|
|
@@ -7417,7 +7653,7 @@ async function scoreCommand(options) {
|
|
|
7417
7653
|
// src/commands/refresh.ts
|
|
7418
7654
|
import fs29 from "fs";
|
|
7419
7655
|
import path23 from "path";
|
|
7420
|
-
import
|
|
7656
|
+
import chalk15 from "chalk";
|
|
7421
7657
|
import ora5 from "ora";
|
|
7422
7658
|
|
|
7423
7659
|
// src/lib/git-diff.ts
|
|
@@ -7649,8 +7885,12 @@ function parseBullets(content) {
|
|
|
7649
7885
|
if (current) bullets.push(current);
|
|
7650
7886
|
return bullets;
|
|
7651
7887
|
}
|
|
7888
|
+
var TYPE_PREFIX_RE = /^\*\*\[[^\]]+\]\*\*\s*/;
|
|
7652
7889
|
function normalizeBullet(bullet) {
|
|
7653
|
-
return bullet.replace(/^- /, "").replace(/`[^`]*`/g, "").replace(/\s+/g, " ").toLowerCase().trim();
|
|
7890
|
+
return bullet.replace(/^- /, "").replace(TYPE_PREFIX_RE, "").replace(/`[^`]*`/g, "").replace(/\s+/g, " ").toLowerCase().trim();
|
|
7891
|
+
}
|
|
7892
|
+
function hasTypePrefix(bullet) {
|
|
7893
|
+
return TYPE_PREFIX_RE.test(bullet.replace(/^- /, ""));
|
|
7654
7894
|
}
|
|
7655
7895
|
function deduplicateLearnedItems(existing, incoming) {
|
|
7656
7896
|
const existingBullets = existing ? parseBullets(existing) : [];
|
|
@@ -7660,14 +7900,18 @@ function deduplicateLearnedItems(existing, incoming) {
|
|
|
7660
7900
|
for (const bullet of incomingBullets) {
|
|
7661
7901
|
const norm = normalizeBullet(bullet);
|
|
7662
7902
|
if (!norm) continue;
|
|
7663
|
-
const
|
|
7903
|
+
const dupIdx = merged.findIndex((e) => {
|
|
7664
7904
|
const eNorm = normalizeBullet(e);
|
|
7665
7905
|
const shorter = Math.min(norm.length, eNorm.length);
|
|
7666
7906
|
const longer = Math.max(norm.length, eNorm.length);
|
|
7667
7907
|
if (!(eNorm.includes(norm) || norm.includes(eNorm))) return false;
|
|
7668
7908
|
return shorter / longer > 0.7;
|
|
7669
7909
|
});
|
|
7670
|
-
if (
|
|
7910
|
+
if (dupIdx !== -1) {
|
|
7911
|
+
if (hasTypePrefix(bullet) && !hasTypePrefix(merged[dupIdx])) {
|
|
7912
|
+
merged[dupIdx] = bullet;
|
|
7913
|
+
}
|
|
7914
|
+
} else {
|
|
7671
7915
|
merged.push(bullet);
|
|
7672
7916
|
newItems.push(bullet);
|
|
7673
7917
|
}
|
|
@@ -7752,7 +7996,7 @@ function discoverGitRepos(parentDir) {
|
|
|
7752
7996
|
}
|
|
7753
7997
|
async function refreshSingleRepo(repoDir, options) {
|
|
7754
7998
|
const quiet = !!options.quiet;
|
|
7755
|
-
const prefix = options.label ? `${
|
|
7999
|
+
const prefix = options.label ? `${chalk15.bold(options.label)} ` : "";
|
|
7756
8000
|
const state = readState();
|
|
7757
8001
|
const lastSha = state?.lastRefreshSha ?? null;
|
|
7758
8002
|
const diff = collectDiff(lastSha);
|
|
@@ -7761,7 +8005,7 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
7761
8005
|
if (currentSha) {
|
|
7762
8006
|
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
7763
8007
|
}
|
|
7764
|
-
log2(quiet,
|
|
8008
|
+
log2(quiet, chalk15.dim(`${prefix}No changes since last refresh.`));
|
|
7765
8009
|
return;
|
|
7766
8010
|
}
|
|
7767
8011
|
const spinner = quiet ? null : ora5(`${prefix}Analyzing changes...`).start();
|
|
@@ -7795,10 +8039,10 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
7795
8039
|
if (options.dryRun) {
|
|
7796
8040
|
spinner?.info(`${prefix}Dry run \u2014 would update:`);
|
|
7797
8041
|
for (const doc of response.docsUpdated) {
|
|
7798
|
-
console.log(` ${
|
|
8042
|
+
console.log(` ${chalk15.yellow("~")} ${doc}`);
|
|
7799
8043
|
}
|
|
7800
8044
|
if (response.changesSummary) {
|
|
7801
|
-
console.log(
|
|
8045
|
+
console.log(chalk15.dim(`
|
|
7802
8046
|
${response.changesSummary}`));
|
|
7803
8047
|
}
|
|
7804
8048
|
return;
|
|
@@ -7807,10 +8051,10 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
7807
8051
|
trackRefreshCompleted(written.length, Date.now());
|
|
7808
8052
|
spinner?.succeed(`${prefix}Updated ${written.length} doc${written.length === 1 ? "" : "s"}`);
|
|
7809
8053
|
for (const file of written) {
|
|
7810
|
-
log2(quiet, ` ${
|
|
8054
|
+
log2(quiet, ` ${chalk15.green("\u2713")} ${file}`);
|
|
7811
8055
|
}
|
|
7812
8056
|
if (response.changesSummary) {
|
|
7813
|
-
log2(quiet,
|
|
8057
|
+
log2(quiet, chalk15.dim(`
|
|
7814
8058
|
${response.changesSummary}`));
|
|
7815
8059
|
}
|
|
7816
8060
|
if (currentSha) {
|
|
@@ -7827,7 +8071,7 @@ async function refreshCommand(options) {
|
|
|
7827
8071
|
const config = loadConfig();
|
|
7828
8072
|
if (!config) {
|
|
7829
8073
|
if (quiet) return;
|
|
7830
|
-
console.log(
|
|
8074
|
+
console.log(chalk15.red("No LLM provider configured. Run ") + chalk15.hex("#83D1EB")("caliber config") + chalk15.red(" (e.g. choose Cursor) or set an API key."));
|
|
7831
8075
|
throw new Error("__exit__");
|
|
7832
8076
|
}
|
|
7833
8077
|
await validateModel({ fast: true });
|
|
@@ -7838,10 +8082,10 @@ async function refreshCommand(options) {
|
|
|
7838
8082
|
const repos = discoverGitRepos(process.cwd());
|
|
7839
8083
|
if (repos.length === 0) {
|
|
7840
8084
|
if (quiet) return;
|
|
7841
|
-
console.log(
|
|
8085
|
+
console.log(chalk15.red("Not inside a git repository and no git repos found in child directories."));
|
|
7842
8086
|
throw new Error("__exit__");
|
|
7843
8087
|
}
|
|
7844
|
-
log2(quiet,
|
|
8088
|
+
log2(quiet, chalk15.dim(`Found ${repos.length} git repo${repos.length === 1 ? "" : "s"}
|
|
7845
8089
|
`));
|
|
7846
8090
|
const originalDir = process.cwd();
|
|
7847
8091
|
for (const repo of repos) {
|
|
@@ -7851,7 +8095,7 @@ async function refreshCommand(options) {
|
|
|
7851
8095
|
await refreshSingleRepo(repo, { ...options, label: repoName });
|
|
7852
8096
|
} catch (err) {
|
|
7853
8097
|
if (err instanceof Error && err.message === "__exit__") continue;
|
|
7854
|
-
log2(quiet,
|
|
8098
|
+
log2(quiet, chalk15.yellow(`${repoName}: refresh failed \u2014 ${err instanceof Error ? err.message : "unknown error"}`));
|
|
7855
8099
|
}
|
|
7856
8100
|
}
|
|
7857
8101
|
process.chdir(originalDir);
|
|
@@ -7859,13 +8103,13 @@ async function refreshCommand(options) {
|
|
|
7859
8103
|
if (err instanceof Error && err.message === "__exit__") throw err;
|
|
7860
8104
|
if (quiet) return;
|
|
7861
8105
|
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
7862
|
-
console.log(
|
|
8106
|
+
console.log(chalk15.red(`Refresh failed: ${msg}`));
|
|
7863
8107
|
throw new Error("__exit__");
|
|
7864
8108
|
}
|
|
7865
8109
|
}
|
|
7866
8110
|
|
|
7867
8111
|
// src/commands/hooks.ts
|
|
7868
|
-
import
|
|
8112
|
+
import chalk16 from "chalk";
|
|
7869
8113
|
var HOOKS = [
|
|
7870
8114
|
{
|
|
7871
8115
|
id: "session-end",
|
|
@@ -7885,13 +8129,13 @@ var HOOKS = [
|
|
|
7885
8129
|
}
|
|
7886
8130
|
];
|
|
7887
8131
|
function printStatus() {
|
|
7888
|
-
console.log(
|
|
8132
|
+
console.log(chalk16.bold("\n Hooks\n"));
|
|
7889
8133
|
for (const hook of HOOKS) {
|
|
7890
8134
|
const installed = hook.isInstalled();
|
|
7891
|
-
const icon = installed ?
|
|
7892
|
-
const state = installed ?
|
|
8135
|
+
const icon = installed ? chalk16.green("\u2713") : chalk16.dim("\u2717");
|
|
8136
|
+
const state = installed ? chalk16.green("enabled") : chalk16.dim("disabled");
|
|
7893
8137
|
console.log(` ${icon} ${hook.label.padEnd(26)} ${state}`);
|
|
7894
|
-
console.log(
|
|
8138
|
+
console.log(chalk16.dim(` ${hook.description}`));
|
|
7895
8139
|
}
|
|
7896
8140
|
console.log("");
|
|
7897
8141
|
}
|
|
@@ -7900,9 +8144,9 @@ async function hooksCommand(options) {
|
|
|
7900
8144
|
for (const hook of HOOKS) {
|
|
7901
8145
|
const result = hook.install();
|
|
7902
8146
|
if (result.alreadyInstalled) {
|
|
7903
|
-
console.log(
|
|
8147
|
+
console.log(chalk16.dim(` ${hook.label} already enabled.`));
|
|
7904
8148
|
} else {
|
|
7905
|
-
console.log(
|
|
8149
|
+
console.log(chalk16.green(" \u2713") + ` ${hook.label} enabled`);
|
|
7906
8150
|
}
|
|
7907
8151
|
}
|
|
7908
8152
|
return;
|
|
@@ -7911,9 +8155,9 @@ async function hooksCommand(options) {
|
|
|
7911
8155
|
for (const hook of HOOKS) {
|
|
7912
8156
|
const result = hook.remove();
|
|
7913
8157
|
if (result.notFound) {
|
|
7914
|
-
console.log(
|
|
8158
|
+
console.log(chalk16.dim(` ${hook.label} already disabled.`));
|
|
7915
8159
|
} else {
|
|
7916
|
-
console.log(
|
|
8160
|
+
console.log(chalk16.green(" \u2713") + ` ${hook.label} removed`);
|
|
7917
8161
|
}
|
|
7918
8162
|
}
|
|
7919
8163
|
return;
|
|
@@ -7928,18 +8172,18 @@ async function hooksCommand(options) {
|
|
|
7928
8172
|
const states = HOOKS.map((h) => h.isInstalled());
|
|
7929
8173
|
function render() {
|
|
7930
8174
|
const lines = [];
|
|
7931
|
-
lines.push(
|
|
8175
|
+
lines.push(chalk16.bold(" Hooks"));
|
|
7932
8176
|
lines.push("");
|
|
7933
8177
|
for (let i = 0; i < HOOKS.length; i++) {
|
|
7934
8178
|
const hook = HOOKS[i];
|
|
7935
8179
|
const enabled = states[i];
|
|
7936
|
-
const toggle = enabled ?
|
|
7937
|
-
const ptr = i === cursor ?
|
|
8180
|
+
const toggle = enabled ? chalk16.green("[on] ") : chalk16.dim("[off]");
|
|
8181
|
+
const ptr = i === cursor ? chalk16.cyan(">") : " ";
|
|
7938
8182
|
lines.push(` ${ptr} ${toggle} ${hook.label}`);
|
|
7939
|
-
lines.push(
|
|
8183
|
+
lines.push(chalk16.dim(` ${hook.description}`));
|
|
7940
8184
|
}
|
|
7941
8185
|
lines.push("");
|
|
7942
|
-
lines.push(
|
|
8186
|
+
lines.push(chalk16.dim(" \u2191\u2193 navigate \u23B5 toggle a all on n all off \u23CE apply q cancel"));
|
|
7943
8187
|
return lines.join("\n");
|
|
7944
8188
|
}
|
|
7945
8189
|
function draw(initial) {
|
|
@@ -7970,16 +8214,16 @@ async function hooksCommand(options) {
|
|
|
7970
8214
|
const wantEnabled = states[i];
|
|
7971
8215
|
if (wantEnabled && !wasInstalled) {
|
|
7972
8216
|
hook.install();
|
|
7973
|
-
console.log(
|
|
8217
|
+
console.log(chalk16.green(" \u2713") + ` ${hook.label} enabled`);
|
|
7974
8218
|
changed++;
|
|
7975
8219
|
} else if (!wantEnabled && wasInstalled) {
|
|
7976
8220
|
hook.remove();
|
|
7977
|
-
console.log(
|
|
8221
|
+
console.log(chalk16.green(" \u2713") + ` ${hook.label} disabled`);
|
|
7978
8222
|
changed++;
|
|
7979
8223
|
}
|
|
7980
8224
|
}
|
|
7981
8225
|
if (changed === 0) {
|
|
7982
|
-
console.log(
|
|
8226
|
+
console.log(chalk16.dim(" No changes."));
|
|
7983
8227
|
}
|
|
7984
8228
|
console.log("");
|
|
7985
8229
|
}
|
|
@@ -8015,7 +8259,7 @@ async function hooksCommand(options) {
|
|
|
8015
8259
|
case "\x1B":
|
|
8016
8260
|
case "":
|
|
8017
8261
|
cleanup();
|
|
8018
|
-
console.log(
|
|
8262
|
+
console.log(chalk16.dim("\n Cancelled.\n"));
|
|
8019
8263
|
resolve2();
|
|
8020
8264
|
break;
|
|
8021
8265
|
}
|
|
@@ -8026,51 +8270,51 @@ async function hooksCommand(options) {
|
|
|
8026
8270
|
|
|
8027
8271
|
// src/commands/config.ts
|
|
8028
8272
|
init_config();
|
|
8029
|
-
import
|
|
8273
|
+
import chalk17 from "chalk";
|
|
8030
8274
|
async function configCommand() {
|
|
8031
8275
|
const existing = loadConfig();
|
|
8032
8276
|
if (existing) {
|
|
8033
8277
|
const displayModel = getDisplayModel(existing);
|
|
8034
8278
|
const fastModel = getFastModel();
|
|
8035
|
-
console.log(
|
|
8036
|
-
console.log(` Provider: ${
|
|
8037
|
-
console.log(` Model: ${
|
|
8279
|
+
console.log(chalk17.bold("\nCurrent Configuration\n"));
|
|
8280
|
+
console.log(` Provider: ${chalk17.cyan(existing.provider)}`);
|
|
8281
|
+
console.log(` Model: ${chalk17.cyan(displayModel)}`);
|
|
8038
8282
|
if (fastModel) {
|
|
8039
|
-
console.log(` Scan: ${
|
|
8283
|
+
console.log(` Scan: ${chalk17.cyan(fastModel)}`);
|
|
8040
8284
|
}
|
|
8041
8285
|
if (existing.apiKey) {
|
|
8042
8286
|
const masked = existing.apiKey.slice(0, 8) + "..." + existing.apiKey.slice(-4);
|
|
8043
|
-
console.log(` API Key: ${
|
|
8287
|
+
console.log(` API Key: ${chalk17.dim(masked)}`);
|
|
8044
8288
|
}
|
|
8045
8289
|
if (existing.provider === "cursor") {
|
|
8046
|
-
console.log(` Seat: ${
|
|
8290
|
+
console.log(` Seat: ${chalk17.dim("Cursor (agent acp)")}`);
|
|
8047
8291
|
}
|
|
8048
8292
|
if (existing.provider === "claude-cli") {
|
|
8049
|
-
console.log(` Seat: ${
|
|
8293
|
+
console.log(` Seat: ${chalk17.dim("Claude Code (claude -p)")}`);
|
|
8050
8294
|
}
|
|
8051
8295
|
if (existing.baseUrl) {
|
|
8052
|
-
console.log(` Base URL: ${
|
|
8296
|
+
console.log(` Base URL: ${chalk17.dim(existing.baseUrl)}`);
|
|
8053
8297
|
}
|
|
8054
8298
|
if (existing.vertexProjectId) {
|
|
8055
|
-
console.log(` Vertex Project: ${
|
|
8056
|
-
console.log(` Vertex Region: ${
|
|
8299
|
+
console.log(` Vertex Project: ${chalk17.dim(existing.vertexProjectId)}`);
|
|
8300
|
+
console.log(` Vertex Region: ${chalk17.dim(existing.vertexRegion || "us-east5")}`);
|
|
8057
8301
|
}
|
|
8058
|
-
console.log(` Source: ${
|
|
8302
|
+
console.log(` Source: ${chalk17.dim(process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY || process.env.VERTEX_PROJECT_ID || process.env.CALIBER_USE_CURSOR_SEAT || process.env.CALIBER_USE_CLAUDE_CLI ? "environment variables" : getConfigFilePath())}`);
|
|
8059
8303
|
console.log("");
|
|
8060
8304
|
}
|
|
8061
8305
|
await runInteractiveProviderSetup();
|
|
8062
8306
|
const updated = loadConfig();
|
|
8063
8307
|
if (updated) trackConfigProviderSet(updated.provider);
|
|
8064
|
-
console.log(
|
|
8065
|
-
console.log(
|
|
8308
|
+
console.log(chalk17.green("\n\u2713 Configuration saved"));
|
|
8309
|
+
console.log(chalk17.dim(` ${getConfigFilePath()}
|
|
8066
8310
|
`));
|
|
8067
|
-
console.log(
|
|
8068
|
-
console.log(
|
|
8311
|
+
console.log(chalk17.dim(" You can also set environment variables instead:"));
|
|
8312
|
+
console.log(chalk17.dim(" ANTHROPIC_API_KEY, OPENAI_API_KEY, VERTEX_PROJECT_ID, CALIBER_USE_CURSOR_SEAT=1, or CALIBER_USE_CLAUDE_CLI=1\n"));
|
|
8069
8313
|
}
|
|
8070
8314
|
|
|
8071
8315
|
// src/commands/learn.ts
|
|
8072
8316
|
import fs31 from "fs";
|
|
8073
|
-
import
|
|
8317
|
+
import chalk18 from "chalk";
|
|
8074
8318
|
|
|
8075
8319
|
// src/learner/stdin.ts
|
|
8076
8320
|
var STDIN_TIMEOUT_MS = 5e3;
|
|
@@ -8137,6 +8381,17 @@ function appendEvent(event) {
|
|
|
8137
8381
|
fs30.writeFileSync(filePath, kept.join("\n") + "\n");
|
|
8138
8382
|
}
|
|
8139
8383
|
}
|
|
8384
|
+
function appendPromptEvent(event) {
|
|
8385
|
+
ensureLearningDir();
|
|
8386
|
+
const filePath = sessionFilePath();
|
|
8387
|
+
fs30.appendFileSync(filePath, JSON.stringify(event) + "\n");
|
|
8388
|
+
const count = getEventCount();
|
|
8389
|
+
if (count > LEARNING_MAX_EVENTS) {
|
|
8390
|
+
const lines = fs30.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
8391
|
+
const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
|
|
8392
|
+
fs30.writeFileSync(filePath, kept.join("\n") + "\n");
|
|
8393
|
+
}
|
|
8394
|
+
}
|
|
8140
8395
|
function readAllEvents() {
|
|
8141
8396
|
const filePath = sessionFilePath();
|
|
8142
8397
|
if (!fs30.existsSync(filePath)) return [];
|
|
@@ -8197,14 +8452,6 @@ function acquireFinalizeLock() {
|
|
|
8197
8452
|
fs30.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
8198
8453
|
return true;
|
|
8199
8454
|
} catch {
|
|
8200
|
-
try {
|
|
8201
|
-
const stat = fs30.statSync(lockPath);
|
|
8202
|
-
if (Date.now() - stat.mtimeMs >= LOCK_STALE_MS) {
|
|
8203
|
-
fs30.writeFileSync(lockPath, String(process.pid));
|
|
8204
|
-
return true;
|
|
8205
|
-
}
|
|
8206
|
-
} catch {
|
|
8207
|
-
}
|
|
8208
8455
|
return false;
|
|
8209
8456
|
}
|
|
8210
8457
|
}
|
|
@@ -8216,17 +8463,64 @@ function releaseFinalizeLock() {
|
|
|
8216
8463
|
}
|
|
8217
8464
|
}
|
|
8218
8465
|
|
|
8466
|
+
// src/lib/sanitize.ts
|
|
8467
|
+
var KNOWN_PREFIX_PATTERNS = [
|
|
8468
|
+
// Anthropic (before generic sk- pattern)
|
|
8469
|
+
[/sk-ant-[A-Za-z0-9_-]{20,}/g, "[REDACTED]"],
|
|
8470
|
+
// AWS access key IDs
|
|
8471
|
+
[/AKIA[0-9A-Z]{16}/g, "[REDACTED]"],
|
|
8472
|
+
// AWS secret keys in assignments
|
|
8473
|
+
[/(?:aws)?_?secret_?(?:access)?_?key\s*[:=]\s*['"]?[A-Za-z0-9/+=]{40}['"]?/gi, "[REDACTED]"],
|
|
8474
|
+
// GitHub tokens (PAT, OAuth, server, app install, fine-grained)
|
|
8475
|
+
[/gh[pousr]_[A-Za-z0-9_]{36,}/g, "[REDACTED]"],
|
|
8476
|
+
[/github_pat_[A-Za-z0-9_]{22,}/g, "[REDACTED]"],
|
|
8477
|
+
// Stripe keys
|
|
8478
|
+
[/[sr]k_(live|test)_[A-Za-z0-9]{20,}/g, "[REDACTED]"],
|
|
8479
|
+
// Slack tokens
|
|
8480
|
+
[/xox[bpsar]-[A-Za-z0-9-]{10,}/g, "[REDACTED]"],
|
|
8481
|
+
// JWTs (3-segment base64url)
|
|
8482
|
+
[/eyJ[A-Za-z0-9_-]{20,}\.eyJ[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}/g, "[REDACTED]"],
|
|
8483
|
+
// OpenAI keys (after sk-ant- to avoid false match)
|
|
8484
|
+
[/sk-[A-Za-z0-9-]{20,}/g, "[REDACTED]"],
|
|
8485
|
+
// Google API keys
|
|
8486
|
+
[/AIza[A-Za-z0-9_-]{35}/g, "[REDACTED]"],
|
|
8487
|
+
// Bearer tokens
|
|
8488
|
+
[/[Bb]earer\s+[A-Za-z0-9_\-.]{20,}/g, "[REDACTED]"],
|
|
8489
|
+
// PEM private keys
|
|
8490
|
+
[/-----BEGIN[A-Z ]+KEY-----[\s\S]+?-----END[A-Z ]+KEY-----/g, "[REDACTED]"]
|
|
8491
|
+
];
|
|
8492
|
+
var SENSITIVE_ASSIGNMENT = /(?:api[_-]?key|secret[_-]?key|password|token|credential|auth[_-]?token|private[_-]?key)\s*[:=]\s*['"]?([^\s'"]{8,500})['"]?/gi;
|
|
8493
|
+
function sanitizeSecrets(text) {
|
|
8494
|
+
let result = text;
|
|
8495
|
+
for (const [pattern, replacement] of KNOWN_PREFIX_PATTERNS) {
|
|
8496
|
+
result = result.replace(pattern, replacement);
|
|
8497
|
+
}
|
|
8498
|
+
result = result.replace(
|
|
8499
|
+
SENSITIVE_ASSIGNMENT,
|
|
8500
|
+
(match, value) => match.replace(value, "[REDACTED]")
|
|
8501
|
+
);
|
|
8502
|
+
return result;
|
|
8503
|
+
}
|
|
8504
|
+
|
|
8219
8505
|
// src/ai/learn.ts
|
|
8220
8506
|
init_config();
|
|
8221
8507
|
var MAX_PROMPT_TOKENS = 1e5;
|
|
8222
8508
|
function formatEventsForPrompt(events) {
|
|
8223
8509
|
return events.map((e, i) => {
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8510
|
+
if (e.hook_event_name === "UserPromptSubmit") {
|
|
8511
|
+
const pe = e;
|
|
8512
|
+
return `--- Event ${i + 1} [USER_PROMPT] ---
|
|
8513
|
+
Time: ${pe.timestamp}
|
|
8514
|
+
User said:
|
|
8515
|
+
${pe.prompt_content}`;
|
|
8516
|
+
}
|
|
8517
|
+
const te = e;
|
|
8518
|
+
const status = te.hook_event_name === "PostToolUseFailure" ? "FAILURE" : "SUCCESS";
|
|
8519
|
+
const inputStr = JSON.stringify(te.tool_input, null, 2);
|
|
8520
|
+
const responseStr = typeof te.tool_response === "object" && "_truncated" in te.tool_response ? String(te.tool_response._truncated) : JSON.stringify(te.tool_response, null, 2);
|
|
8227
8521
|
return `--- Event ${i + 1} [${status}] ---
|
|
8228
|
-
Tool: ${
|
|
8229
|
-
Time: ${
|
|
8522
|
+
Tool: ${te.tool_name}
|
|
8523
|
+
Time: ${te.timestamp}
|
|
8230
8524
|
Input:
|
|
8231
8525
|
${inputStr}
|
|
8232
8526
|
Response:
|
|
@@ -8298,9 +8592,25 @@ async function learnObserveCommand(options) {
|
|
|
8298
8592
|
const raw = await readStdin();
|
|
8299
8593
|
if (!raw.trim()) return;
|
|
8300
8594
|
const hookData = JSON.parse(raw);
|
|
8595
|
+
const sessionId = hookData.session_id || hookData.conversation_id || "unknown";
|
|
8596
|
+
if (options.prompt) {
|
|
8597
|
+
const event2 = {
|
|
8598
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8599
|
+
session_id: sessionId,
|
|
8600
|
+
hook_event_name: "UserPromptSubmit",
|
|
8601
|
+
prompt_content: sanitizeSecrets(String(hookData.prompt_content || hookData.content || hookData.prompt || "")),
|
|
8602
|
+
cwd: hookData.cwd || process.cwd()
|
|
8603
|
+
};
|
|
8604
|
+
appendPromptEvent(event2);
|
|
8605
|
+
const state2 = readState2();
|
|
8606
|
+
state2.eventCount++;
|
|
8607
|
+
if (!state2.sessionId) state2.sessionId = sessionId;
|
|
8608
|
+
writeState2(state2);
|
|
8609
|
+
return;
|
|
8610
|
+
}
|
|
8301
8611
|
const event = {
|
|
8302
8612
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8303
|
-
session_id:
|
|
8613
|
+
session_id: sessionId,
|
|
8304
8614
|
hook_event_name: options.failure ? "PostToolUseFailure" : "PostToolUse",
|
|
8305
8615
|
tool_name: hookData.tool_name || "unknown",
|
|
8306
8616
|
tool_input: hookData.tool_input || {},
|
|
@@ -8311,7 +8621,7 @@ async function learnObserveCommand(options) {
|
|
|
8311
8621
|
appendEvent(event);
|
|
8312
8622
|
const state = readState2();
|
|
8313
8623
|
state.eventCount++;
|
|
8314
|
-
if (!state.sessionId) state.sessionId =
|
|
8624
|
+
if (!state.sessionId) state.sessionId = sessionId;
|
|
8315
8625
|
writeState2(state);
|
|
8316
8626
|
} catch {
|
|
8317
8627
|
}
|
|
@@ -8320,26 +8630,26 @@ async function learnFinalizeCommand(options) {
|
|
|
8320
8630
|
if (!options?.force) {
|
|
8321
8631
|
const { isCaliberRunning: isCaliberRunning2 } = await Promise.resolve().then(() => (init_lock(), lock_exports));
|
|
8322
8632
|
if (isCaliberRunning2()) {
|
|
8323
|
-
console.log(
|
|
8633
|
+
console.log(chalk18.dim("caliber: skipping finalize \u2014 another caliber process is running"));
|
|
8324
8634
|
return;
|
|
8325
8635
|
}
|
|
8326
8636
|
}
|
|
8327
8637
|
if (!acquireFinalizeLock()) {
|
|
8328
|
-
console.log(
|
|
8638
|
+
console.log(chalk18.dim("caliber: skipping finalize \u2014 another finalize is in progress"));
|
|
8329
8639
|
return;
|
|
8330
8640
|
}
|
|
8331
8641
|
let analyzed = false;
|
|
8332
8642
|
try {
|
|
8333
8643
|
const config = loadConfig();
|
|
8334
8644
|
if (!config) {
|
|
8335
|
-
console.log(
|
|
8645
|
+
console.log(chalk18.yellow("caliber: no LLM provider configured \u2014 run `caliber config` first"));
|
|
8336
8646
|
clearSession();
|
|
8337
8647
|
resetState();
|
|
8338
8648
|
return;
|
|
8339
8649
|
}
|
|
8340
8650
|
const events = readAllEvents();
|
|
8341
8651
|
if (events.length < MIN_EVENTS_FOR_ANALYSIS) {
|
|
8342
|
-
console.log(
|
|
8652
|
+
console.log(chalk18.dim(`caliber: ${events.length}/${MIN_EVENTS_FOR_ANALYSIS} events recorded \u2014 need more before analysis`));
|
|
8343
8653
|
return;
|
|
8344
8654
|
}
|
|
8345
8655
|
await validateModel({ fast: true });
|
|
@@ -8360,15 +8670,15 @@ async function learnFinalizeCommand(options) {
|
|
|
8360
8670
|
skills: response.skills
|
|
8361
8671
|
});
|
|
8362
8672
|
if (result.newItemCount > 0) {
|
|
8363
|
-
console.log(
|
|
8673
|
+
console.log(chalk18.dim(`caliber: learned ${result.newItemCount} new pattern${result.newItemCount === 1 ? "" : "s"}`));
|
|
8364
8674
|
for (const item of result.newItems) {
|
|
8365
|
-
console.log(
|
|
8675
|
+
console.log(chalk18.dim(` + ${item.replace(/^- /, "").slice(0, 80)}`));
|
|
8366
8676
|
}
|
|
8367
8677
|
}
|
|
8368
8678
|
}
|
|
8369
8679
|
} catch (err) {
|
|
8370
8680
|
if (options?.force) {
|
|
8371
|
-
console.error(
|
|
8681
|
+
console.error(chalk18.red("caliber: finalize failed \u2014"), err instanceof Error ? err.message : err);
|
|
8372
8682
|
}
|
|
8373
8683
|
} finally {
|
|
8374
8684
|
if (analyzed) {
|
|
@@ -8383,45 +8693,45 @@ async function learnInstallCommand() {
|
|
|
8383
8693
|
if (fs31.existsSync(".claude")) {
|
|
8384
8694
|
const r = installLearningHooks();
|
|
8385
8695
|
if (r.installed) {
|
|
8386
|
-
console.log(
|
|
8696
|
+
console.log(chalk18.green("\u2713") + " Claude Code learning hooks installed");
|
|
8387
8697
|
anyInstalled = true;
|
|
8388
8698
|
} else if (r.alreadyInstalled) {
|
|
8389
|
-
console.log(
|
|
8699
|
+
console.log(chalk18.dim(" Claude Code hooks already installed"));
|
|
8390
8700
|
}
|
|
8391
8701
|
}
|
|
8392
8702
|
if (fs31.existsSync(".cursor")) {
|
|
8393
8703
|
const r = installCursorLearningHooks();
|
|
8394
8704
|
if (r.installed) {
|
|
8395
|
-
console.log(
|
|
8705
|
+
console.log(chalk18.green("\u2713") + " Cursor learning hooks installed");
|
|
8396
8706
|
anyInstalled = true;
|
|
8397
8707
|
} else if (r.alreadyInstalled) {
|
|
8398
|
-
console.log(
|
|
8708
|
+
console.log(chalk18.dim(" Cursor hooks already installed"));
|
|
8399
8709
|
}
|
|
8400
8710
|
}
|
|
8401
8711
|
if (!fs31.existsSync(".claude") && !fs31.existsSync(".cursor")) {
|
|
8402
|
-
console.log(
|
|
8403
|
-
console.log(
|
|
8712
|
+
console.log(chalk18.yellow("No .claude/ or .cursor/ directory found."));
|
|
8713
|
+
console.log(chalk18.dim(" Run `caliber init` first, or create the directory manually."));
|
|
8404
8714
|
return;
|
|
8405
8715
|
}
|
|
8406
8716
|
if (anyInstalled) {
|
|
8407
|
-
console.log(
|
|
8408
|
-
console.log(
|
|
8717
|
+
console.log(chalk18.dim(` Tool usage will be recorded and learnings extracted after \u2265${MIN_EVENTS_FOR_ANALYSIS} events.`));
|
|
8718
|
+
console.log(chalk18.dim(" Learnings written to CALIBER_LEARNINGS.md."));
|
|
8409
8719
|
}
|
|
8410
8720
|
}
|
|
8411
8721
|
async function learnRemoveCommand() {
|
|
8412
8722
|
let anyRemoved = false;
|
|
8413
8723
|
const r1 = removeLearningHooks();
|
|
8414
8724
|
if (r1.removed) {
|
|
8415
|
-
console.log(
|
|
8725
|
+
console.log(chalk18.green("\u2713") + " Claude Code learning hooks removed");
|
|
8416
8726
|
anyRemoved = true;
|
|
8417
8727
|
}
|
|
8418
8728
|
const r2 = removeCursorLearningHooks();
|
|
8419
8729
|
if (r2.removed) {
|
|
8420
|
-
console.log(
|
|
8730
|
+
console.log(chalk18.green("\u2713") + " Cursor learning hooks removed");
|
|
8421
8731
|
anyRemoved = true;
|
|
8422
8732
|
}
|
|
8423
8733
|
if (!anyRemoved) {
|
|
8424
|
-
console.log(
|
|
8734
|
+
console.log(chalk18.dim("No learning hooks found."));
|
|
8425
8735
|
}
|
|
8426
8736
|
}
|
|
8427
8737
|
async function learnStatusCommand() {
|
|
@@ -8429,34 +8739,34 @@ async function learnStatusCommand() {
|
|
|
8429
8739
|
const cursorInstalled = areCursorLearningHooksInstalled();
|
|
8430
8740
|
const state = readState2();
|
|
8431
8741
|
const eventCount = getEventCount();
|
|
8432
|
-
console.log(
|
|
8742
|
+
console.log(chalk18.bold("Session Learning Status"));
|
|
8433
8743
|
console.log();
|
|
8434
8744
|
if (claudeInstalled) {
|
|
8435
|
-
console.log(
|
|
8745
|
+
console.log(chalk18.green("\u2713") + " Claude Code hooks " + chalk18.green("installed"));
|
|
8436
8746
|
} else {
|
|
8437
|
-
console.log(
|
|
8747
|
+
console.log(chalk18.dim("\u2717") + " Claude Code hooks " + chalk18.dim("not installed"));
|
|
8438
8748
|
}
|
|
8439
8749
|
if (cursorInstalled) {
|
|
8440
|
-
console.log(
|
|
8750
|
+
console.log(chalk18.green("\u2713") + " Cursor hooks " + chalk18.green("installed"));
|
|
8441
8751
|
} else {
|
|
8442
|
-
console.log(
|
|
8752
|
+
console.log(chalk18.dim("\u2717") + " Cursor hooks " + chalk18.dim("not installed"));
|
|
8443
8753
|
}
|
|
8444
8754
|
if (!claudeInstalled && !cursorInstalled) {
|
|
8445
|
-
console.log(
|
|
8755
|
+
console.log(chalk18.dim(" Run `caliber learn install` to enable session learning."));
|
|
8446
8756
|
}
|
|
8447
8757
|
console.log();
|
|
8448
|
-
console.log(`Events recorded: ${
|
|
8449
|
-
console.log(`Threshold for analysis: ${
|
|
8758
|
+
console.log(`Events recorded: ${chalk18.cyan(String(eventCount))}`);
|
|
8759
|
+
console.log(`Threshold for analysis: ${chalk18.cyan(String(MIN_EVENTS_FOR_ANALYSIS))}`);
|
|
8450
8760
|
if (state.lastAnalysisTimestamp) {
|
|
8451
|
-
console.log(`Last analysis: ${
|
|
8761
|
+
console.log(`Last analysis: ${chalk18.cyan(state.lastAnalysisTimestamp)}`);
|
|
8452
8762
|
} else {
|
|
8453
|
-
console.log(`Last analysis: ${
|
|
8763
|
+
console.log(`Last analysis: ${chalk18.dim("none")}`);
|
|
8454
8764
|
}
|
|
8455
8765
|
const learnedSection = readLearnedSection();
|
|
8456
8766
|
if (learnedSection) {
|
|
8457
8767
|
const lineCount = learnedSection.split("\n").filter(Boolean).length;
|
|
8458
8768
|
console.log(`
|
|
8459
|
-
Learned items in CALIBER_LEARNINGS.md: ${
|
|
8769
|
+
Learned items in CALIBER_LEARNINGS.md: ${chalk18.cyan(String(lineCount))}`);
|
|
8460
8770
|
}
|
|
8461
8771
|
}
|
|
8462
8772
|
|
|
@@ -8530,7 +8840,7 @@ program.command("score").description("Score your current agent config setup (det
|
|
|
8530
8840
|
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));
|
|
8531
8841
|
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));
|
|
8532
8842
|
var learn = program.command("learn", { hidden: true }).description("[dev] Session learning \u2014 observe tool usage and extract reusable instructions");
|
|
8533
|
-
learn.command("observe").description("Record a tool event from stdin (called by hooks)").option("--failure", "Mark event as a tool failure").action(tracked("learn:observe", learnObserveCommand));
|
|
8843
|
+
learn.command("observe").description("Record a tool event from stdin (called by hooks)").option("--failure", "Mark event as a tool failure").option("--prompt", "Record a user prompt event").action(tracked("learn:observe", learnObserveCommand));
|
|
8534
8844
|
learn.command("finalize").description("Analyze session events and update CALIBER_LEARNINGS.md (called on SessionEnd)").option("--force", "Skip the running-process check (for manual invocation)").action(tracked("learn:finalize", (opts) => learnFinalizeCommand(opts)));
|
|
8535
8845
|
learn.command("install").description("Install learning hooks into .claude/settings.json").action(tracked("learn:install", learnInstallCommand));
|
|
8536
8846
|
learn.command("remove").description("Remove learning hooks from .claude/settings.json").action(tracked("learn:remove", learnRemoveCommand));
|
|
@@ -8541,7 +8851,7 @@ import fs33 from "fs";
|
|
|
8541
8851
|
import path26 from "path";
|
|
8542
8852
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8543
8853
|
import { execSync as execSync14 } from "child_process";
|
|
8544
|
-
import
|
|
8854
|
+
import chalk19 from "chalk";
|
|
8545
8855
|
import ora6 from "ora";
|
|
8546
8856
|
import confirm2 from "@inquirer/confirm";
|
|
8547
8857
|
var __dirname_vc = path26.dirname(fileURLToPath2(import.meta.url));
|
|
@@ -8575,17 +8885,17 @@ async function checkForUpdates() {
|
|
|
8575
8885
|
const isInteractive = process.stdin.isTTY === true;
|
|
8576
8886
|
if (!isInteractive) {
|
|
8577
8887
|
console.log(
|
|
8578
|
-
|
|
8888
|
+
chalk19.yellow(
|
|
8579
8889
|
`
|
|
8580
8890
|
Update available: ${current} -> ${latest}
|
|
8581
|
-
Run ${
|
|
8891
|
+
Run ${chalk19.bold("npm install -g @rely-ai/caliber")} to upgrade.
|
|
8582
8892
|
`
|
|
8583
8893
|
)
|
|
8584
8894
|
);
|
|
8585
8895
|
return;
|
|
8586
8896
|
}
|
|
8587
8897
|
console.log(
|
|
8588
|
-
|
|
8898
|
+
chalk19.yellow(`
|
|
8589
8899
|
Update available: ${current} -> ${latest}`)
|
|
8590
8900
|
);
|
|
8591
8901
|
const shouldUpdate = await confirm2({ message: "Would you like to update now? (Y/n)", default: true });
|
|
@@ -8603,13 +8913,13 @@ Update available: ${current} -> ${latest}`)
|
|
|
8603
8913
|
const installed = getInstalledVersion();
|
|
8604
8914
|
if (installed !== latest) {
|
|
8605
8915
|
spinner.fail(`Update incomplete \u2014 got ${installed ?? "unknown"}, expected ${latest}`);
|
|
8606
|
-
console.log(
|
|
8916
|
+
console.log(chalk19.yellow(`Run ${chalk19.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually.
|
|
8607
8917
|
`));
|
|
8608
8918
|
return;
|
|
8609
8919
|
}
|
|
8610
|
-
spinner.succeed(
|
|
8920
|
+
spinner.succeed(chalk19.green(`Updated to ${latest}`));
|
|
8611
8921
|
const args = process.argv.slice(2);
|
|
8612
|
-
console.log(
|
|
8922
|
+
console.log(chalk19.dim(`
|
|
8613
8923
|
Restarting: caliber ${args.join(" ")}
|
|
8614
8924
|
`));
|
|
8615
8925
|
execSync14(`caliber ${args.map((a) => JSON.stringify(a)).join(" ")}`, {
|
|
@@ -8622,11 +8932,11 @@ Restarting: caliber ${args.join(" ")}
|
|
|
8622
8932
|
if (err instanceof Error) {
|
|
8623
8933
|
const stderr = err.stderr;
|
|
8624
8934
|
const errMsg = stderr ? String(stderr).trim().split("\n").pop() : err.message.split("\n")[0];
|
|
8625
|
-
if (errMsg && !errMsg.includes("SIGTERM")) console.log(
|
|
8935
|
+
if (errMsg && !errMsg.includes("SIGTERM")) console.log(chalk19.dim(` ${errMsg}`));
|
|
8626
8936
|
}
|
|
8627
8937
|
console.log(
|
|
8628
|
-
|
|
8629
|
-
`Run ${
|
|
8938
|
+
chalk19.yellow(
|
|
8939
|
+
`Run ${chalk19.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually to upgrade.
|
|
8630
8940
|
`
|
|
8631
8941
|
)
|
|
8632
8942
|
);
|