@rely-ai/caliber 1.32.0-dev.1774817730 → 1.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +1070 -1630
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -230,615 +230,21 @@ var init_types = __esm({
|
|
|
230
230
|
}
|
|
231
231
|
});
|
|
232
232
|
|
|
233
|
-
// src/writers/pre-commit-block.ts
|
|
234
|
-
var pre_commit_block_exports = {};
|
|
235
|
-
__export(pre_commit_block_exports, {
|
|
236
|
-
appendLearningsBlock: () => appendLearningsBlock,
|
|
237
|
-
appendManagedBlocks: () => appendManagedBlocks,
|
|
238
|
-
appendPreCommitBlock: () => appendPreCommitBlock,
|
|
239
|
-
appendSyncBlock: () => appendSyncBlock,
|
|
240
|
-
getCursorLearningsRule: () => getCursorLearningsRule,
|
|
241
|
-
getCursorPreCommitRule: () => getCursorPreCommitRule,
|
|
242
|
-
getCursorSetupRule: () => getCursorSetupRule,
|
|
243
|
-
getCursorSyncRule: () => getCursorSyncRule,
|
|
244
|
-
hasLearningsBlock: () => hasLearningsBlock,
|
|
245
|
-
hasPreCommitBlock: () => hasPreCommitBlock,
|
|
246
|
-
hasSyncBlock: () => hasSyncBlock,
|
|
247
|
-
stripManagedBlocks: () => stripManagedBlocks
|
|
248
|
-
});
|
|
249
|
-
function getCaliberNotFoundFallback(platform, bin) {
|
|
250
|
-
switch (platform) {
|
|
251
|
-
case "claude":
|
|
252
|
-
return `If \`${bin}\` is not found, tell the user: "This project uses Caliber for agent config sync. Run /setup-caliber to get set up."`;
|
|
253
|
-
case "codex":
|
|
254
|
-
return `If \`${bin}\` is not found, read \`.agents/skills/setup-caliber/SKILL.md\` and follow its instructions to install Caliber.`;
|
|
255
|
-
case "copilot":
|
|
256
|
-
return `If \`${bin}\` is not found, tell the developer to set up Caliber by running \`/setup-caliber\` in Claude Code or Cursor. Alternatively, they can run these commands in their terminal:
|
|
257
|
-
\`\`\`
|
|
258
|
-
npx @rely-ai/caliber hooks --install
|
|
259
|
-
npx @rely-ai/caliber refresh
|
|
260
|
-
\`\`\``;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
function getPreCommitBlock(platform = "claude") {
|
|
264
|
-
const bin = resolveCaliber();
|
|
265
|
-
return `${BLOCK_START}
|
|
266
|
-
## Before Committing
|
|
267
|
-
|
|
268
|
-
**IMPORTANT:** Before every git commit, you MUST ensure Caliber syncs agent configs with the latest code changes.
|
|
269
|
-
|
|
270
|
-
First, check if the pre-commit hook is already installed:
|
|
271
|
-
\`\`\`bash
|
|
272
|
-
grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "hook-active" || echo "no-hook"
|
|
273
|
-
\`\`\`
|
|
274
|
-
|
|
275
|
-
- If **hook-active**: the hook handles sync automatically \u2014 just commit normally. Tell the user: "Caliber will sync your agent configs automatically via the pre-commit hook."
|
|
276
|
-
- If **no-hook**: run Caliber manually before committing:
|
|
277
|
-
1. Tell the user: "Caliber: Syncing agent configs with your latest changes..."
|
|
278
|
-
2. Run: \`${bin} refresh && git add ${MANAGED_DOC_PATHS} 2>/dev/null\`
|
|
279
|
-
3. After it completes, briefly tell the user what Caliber updated. Then proceed with the commit.
|
|
280
|
-
|
|
281
|
-
${getCaliberNotFoundFallback(platform, bin)}
|
|
282
|
-
${BLOCK_END}`;
|
|
283
|
-
}
|
|
284
|
-
function getCursorRuleContent() {
|
|
285
|
-
const bin = resolveCaliber();
|
|
286
|
-
return `---
|
|
287
|
-
description: Run Caliber sync before git commits to keep agent configs up to date
|
|
288
|
-
alwaysApply: true
|
|
289
|
-
---
|
|
290
|
-
Before every git commit, check if the Caliber pre-commit hook is installed:
|
|
291
|
-
Run: \`grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "hook-active" || echo "no-hook"\`
|
|
292
|
-
|
|
293
|
-
If hook-active: just commit normally \u2014 Caliber syncs automatically via the hook. Tell the user: "Caliber will sync your agent configs automatically."
|
|
294
|
-
If no-hook: run \`${bin} refresh && git add ${MANAGED_DOC_PATHS} 2>/dev/null\` before committing, and tell the user what was updated.
|
|
295
|
-
|
|
296
|
-
If \`${bin}\` is not found, read the setup-caliber skill from .cursor/skills/setup-caliber/SKILL.md and follow its instructions to install Caliber.
|
|
297
|
-
`;
|
|
298
|
-
}
|
|
299
|
-
function hasPreCommitBlock(content) {
|
|
300
|
-
return content.includes(BLOCK_START);
|
|
301
|
-
}
|
|
302
|
-
function appendPreCommitBlock(content, platform = "claude") {
|
|
303
|
-
if (hasPreCommitBlock(content)) return content;
|
|
304
|
-
const trimmed = content.trimEnd();
|
|
305
|
-
return trimmed + "\n\n" + getPreCommitBlock(platform) + "\n";
|
|
306
|
-
}
|
|
307
|
-
function getCursorPreCommitRule() {
|
|
308
|
-
return { filename: CURSOR_RULE_FILENAME, content: getCursorRuleContent() };
|
|
309
|
-
}
|
|
310
|
-
function hasLearningsBlock(content) {
|
|
311
|
-
return content.includes(LEARNINGS_BLOCK_START);
|
|
312
|
-
}
|
|
313
|
-
function appendLearningsBlock(content) {
|
|
314
|
-
if (hasLearningsBlock(content)) return content;
|
|
315
|
-
const trimmed = content.trimEnd();
|
|
316
|
-
return trimmed + "\n\n" + LEARNINGS_BLOCK + "\n";
|
|
317
|
-
}
|
|
318
|
-
function getCursorLearningsRule() {
|
|
319
|
-
return { filename: CURSOR_LEARNINGS_FILENAME, content: CURSOR_LEARNINGS_CONTENT };
|
|
320
|
-
}
|
|
321
|
-
function getSyncSetupInstruction(platform) {
|
|
322
|
-
switch (platform) {
|
|
323
|
-
case "claude":
|
|
324
|
-
return `If the pre-commit hook is not set up, run \`/setup-caliber\` to configure everything automatically.`;
|
|
325
|
-
case "codex":
|
|
326
|
-
return `If the pre-commit hook is not set up, read \`.agents/skills/setup-caliber/SKILL.md\` and follow the setup instructions.`;
|
|
327
|
-
case "copilot":
|
|
328
|
-
return `If the pre-commit hook is not set up, the developer should run \`/setup-caliber\` in Claude Code or Cursor for automated setup. Alternatively, run in terminal:
|
|
329
|
-
\`\`\`bash
|
|
330
|
-
npx @rely-ai/caliber hooks --install
|
|
331
|
-
npx @rely-ai/caliber refresh
|
|
332
|
-
git add ${MANAGED_DOC_PATHS} 2>/dev/null
|
|
333
|
-
\`\`\``;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
function getSyncBlock(platform = "claude") {
|
|
337
|
-
const bin = resolveCaliber();
|
|
338
|
-
return `${SYNC_BLOCK_START}
|
|
339
|
-
## Context Sync
|
|
340
|
-
|
|
341
|
-
This project uses [Caliber](https://github.com/caliber-ai-org/ai-setup) to keep AI agent configs in sync across Claude Code, Cursor, Copilot, and Codex.
|
|
342
|
-
Configs update automatically before each commit via \`${bin} refresh\`.
|
|
343
|
-
${getSyncSetupInstruction(platform)}
|
|
344
|
-
${SYNC_BLOCK_END}`;
|
|
345
|
-
}
|
|
346
|
-
function hasSyncBlock(content) {
|
|
347
|
-
return content.includes(SYNC_BLOCK_START);
|
|
348
|
-
}
|
|
349
|
-
function appendSyncBlock(content, platform = "claude") {
|
|
350
|
-
if (hasSyncBlock(content)) return content;
|
|
351
|
-
const trimmed = content.trimEnd();
|
|
352
|
-
return trimmed + "\n\n" + getSyncBlock(platform) + "\n";
|
|
353
|
-
}
|
|
354
|
-
function appendManagedBlocks(content, platform = "claude") {
|
|
355
|
-
return appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(content, platform)), platform);
|
|
356
|
-
}
|
|
357
|
-
function getCursorSyncContent() {
|
|
358
|
-
const bin = resolveCaliber();
|
|
359
|
-
return `---
|
|
360
|
-
description: This project uses Caliber for automatic AI agent context sync
|
|
361
|
-
alwaysApply: true
|
|
362
|
-
---
|
|
363
|
-
This project uses Caliber to keep AI agent configs (CLAUDE.md, Cursor rules, Copilot instructions)
|
|
364
|
-
in sync with the codebase automatically. Before each commit, run \`${bin} refresh\` to sync configs.
|
|
365
|
-
If \`${bin}\` is not found, read the setup-caliber skill from .cursor/skills/setup-caliber/SKILL.md and follow its instructions to install Caliber.
|
|
366
|
-
`;
|
|
367
|
-
}
|
|
368
|
-
function getCursorSyncRule() {
|
|
369
|
-
return { filename: CURSOR_SYNC_FILENAME, content: getCursorSyncContent() };
|
|
370
|
-
}
|
|
371
|
-
function getCursorSetupContent() {
|
|
372
|
-
return `---
|
|
373
|
-
description: Check if Caliber agent config sync is set up and offer installation on new sessions
|
|
374
|
-
alwaysApply: true
|
|
375
|
-
---
|
|
376
|
-
This project uses Caliber to keep AI agent configs in sync automatically.
|
|
377
|
-
At the start of each session, silently check if sync is active:
|
|
378
|
-
|
|
379
|
-
Run: \`grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "SYNC_ACTIVE" || echo "NO_SYNC"\`
|
|
380
|
-
|
|
381
|
-
If SYNC_ACTIVE: No action needed \u2014 Caliber syncs automatically on every commit.
|
|
382
|
-
If NO_SYNC: Tell the user: "This project uses Caliber for automatic agent config sync, but it's not set up on your machine yet. Want me to set it up? It takes about 30 seconds."
|
|
383
|
-
|
|
384
|
-
If the user agrees, read \`.cursor/skills/setup-caliber/SKILL.md\` and follow its instructions step by step.
|
|
385
|
-
`;
|
|
386
|
-
}
|
|
387
|
-
function getCursorSetupRule() {
|
|
388
|
-
return { filename: CURSOR_SETUP_FILENAME, content: getCursorSetupContent() };
|
|
389
|
-
}
|
|
390
|
-
function stripManagedBlocks(content) {
|
|
391
|
-
let result = content;
|
|
392
|
-
for (const [start, end] of MANAGED_BLOCK_PAIRS) {
|
|
393
|
-
const regex = new RegExp(`\\n?${start.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[\\s\\S]*?${end.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\n?`, "g");
|
|
394
|
-
result = result.replace(regex, "\n");
|
|
395
|
-
}
|
|
396
|
-
return result.replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
397
|
-
}
|
|
398
|
-
var BLOCK_START, BLOCK_END, MANAGED_DOC_PATHS, CURSOR_RULE_FILENAME, LEARNINGS_BLOCK_START, LEARNINGS_BLOCK_END, LEARNINGS_BLOCK, CURSOR_LEARNINGS_FILENAME, CURSOR_LEARNINGS_CONTENT, SYNC_BLOCK_START, SYNC_BLOCK_END, CURSOR_SYNC_FILENAME, CURSOR_SETUP_FILENAME, MANAGED_BLOCK_PAIRS;
|
|
399
|
-
var init_pre_commit_block = __esm({
|
|
400
|
-
"src/writers/pre-commit-block.ts"() {
|
|
401
|
-
"use strict";
|
|
402
|
-
init_resolve_caliber();
|
|
403
|
-
BLOCK_START = "<!-- caliber:managed:pre-commit -->";
|
|
404
|
-
BLOCK_END = "<!-- /caliber:managed:pre-commit -->";
|
|
405
|
-
MANAGED_DOC_PATHS = "CLAUDE.md .claude/ .cursor/ .cursorrules .github/copilot-instructions.md .github/instructions/ AGENTS.md CALIBER_LEARNINGS.md";
|
|
406
|
-
CURSOR_RULE_FILENAME = "caliber-pre-commit.mdc";
|
|
407
|
-
LEARNINGS_BLOCK_START = "<!-- caliber:managed:learnings -->";
|
|
408
|
-
LEARNINGS_BLOCK_END = "<!-- /caliber:managed:learnings -->";
|
|
409
|
-
LEARNINGS_BLOCK = `${LEARNINGS_BLOCK_START}
|
|
410
|
-
## Session Learnings
|
|
411
|
-
|
|
412
|
-
Read \`CALIBER_LEARNINGS.md\` for patterns and anti-patterns learned from previous sessions.
|
|
413
|
-
These are auto-extracted from real tool usage \u2014 treat them as project-specific rules.
|
|
414
|
-
${LEARNINGS_BLOCK_END}`;
|
|
415
|
-
CURSOR_LEARNINGS_FILENAME = "caliber-learnings.mdc";
|
|
416
|
-
CURSOR_LEARNINGS_CONTENT = `---
|
|
417
|
-
description: Reference session-learned patterns from CALIBER_LEARNINGS.md
|
|
418
|
-
alwaysApply: true
|
|
419
|
-
---
|
|
420
|
-
Read \`CALIBER_LEARNINGS.md\` for patterns and anti-patterns learned from previous sessions.
|
|
421
|
-
These are auto-extracted from real tool usage \u2014 treat them as project-specific rules.
|
|
422
|
-
`;
|
|
423
|
-
SYNC_BLOCK_START = "<!-- caliber:managed:sync -->";
|
|
424
|
-
SYNC_BLOCK_END = "<!-- /caliber:managed:sync -->";
|
|
425
|
-
CURSOR_SYNC_FILENAME = "caliber-sync.mdc";
|
|
426
|
-
CURSOR_SETUP_FILENAME = "caliber-setup.mdc";
|
|
427
|
-
MANAGED_BLOCK_PAIRS = [
|
|
428
|
-
[BLOCK_START, BLOCK_END],
|
|
429
|
-
[LEARNINGS_BLOCK_START, LEARNINGS_BLOCK_END],
|
|
430
|
-
[SYNC_BLOCK_START, SYNC_BLOCK_END]
|
|
431
|
-
];
|
|
432
|
-
}
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
// src/lib/builtin-skills.ts
|
|
436
|
-
var builtin_skills_exports = {};
|
|
437
|
-
__export(builtin_skills_exports, {
|
|
438
|
-
BUILTIN_SKILLS: () => BUILTIN_SKILLS,
|
|
439
|
-
BUILTIN_SKILL_NAMES: () => BUILTIN_SKILL_NAMES,
|
|
440
|
-
FIND_SKILLS_SKILL: () => FIND_SKILLS_SKILL,
|
|
441
|
-
PLATFORM_CONFIGS: () => PLATFORM_CONFIGS,
|
|
442
|
-
SAVE_LEARNING_SKILL: () => SAVE_LEARNING_SKILL,
|
|
443
|
-
SETUP_CALIBER_SKILL: () => SETUP_CALIBER_SKILL,
|
|
444
|
-
buildSkillContent: () => buildSkillContent,
|
|
445
|
-
ensureBuiltinSkills: () => ensureBuiltinSkills
|
|
446
|
-
});
|
|
447
|
-
import fs17 from "fs";
|
|
448
|
-
import path17 from "path";
|
|
449
|
-
function buildSkillContent(skill) {
|
|
450
|
-
const frontmatter = `---
|
|
451
|
-
name: ${skill.name}
|
|
452
|
-
description: ${skill.description}
|
|
453
|
-
---
|
|
454
|
-
|
|
455
|
-
`;
|
|
456
|
-
return frontmatter + skill.content;
|
|
457
|
-
}
|
|
458
|
-
function getFindSkillsContent() {
|
|
459
|
-
const bin = resolveCaliber();
|
|
460
|
-
return `# Find Skills
|
|
461
|
-
|
|
462
|
-
Search the public skill registry for community-contributed skills
|
|
463
|
-
relevant to the user's current task and install them into this project.
|
|
464
|
-
|
|
465
|
-
## Instructions
|
|
466
|
-
|
|
467
|
-
1. Identify the key technologies, frameworks, or task types from the
|
|
468
|
-
user's request that might have community skills available
|
|
469
|
-
2. Ask the user: "Would you like me to search for community skills
|
|
470
|
-
for [identified technologies]?"
|
|
471
|
-
3. If the user agrees, run:
|
|
472
|
-
\`\`\`bash
|
|
473
|
-
${bin} skills --query "<relevant terms>"
|
|
474
|
-
\`\`\`
|
|
475
|
-
This outputs the top 5 matching skills with scores and descriptions.
|
|
476
|
-
4. Present the results to the user and ask which ones to install
|
|
477
|
-
5. Install the selected skills:
|
|
478
|
-
\`\`\`bash
|
|
479
|
-
${bin} skills --install <slug1>,<slug2>
|
|
480
|
-
\`\`\`
|
|
481
|
-
6. Read the installed SKILL.md files to load them into your current
|
|
482
|
-
context so you can use them immediately in this session
|
|
483
|
-
7. Summarize what was installed and continue with the user's task
|
|
484
|
-
|
|
485
|
-
## Examples
|
|
486
|
-
|
|
487
|
-
User: "let's build a web app using React"
|
|
488
|
-
-> "I notice you want to work with React. Would you like me to search
|
|
489
|
-
for community skills that could help with React development?"
|
|
490
|
-
-> If yes: run \`${bin} skills --query "react frontend"\`
|
|
491
|
-
-> Show the user the results, ask which to install
|
|
492
|
-
-> Run \`${bin} skills --install <selected-slugs>\`
|
|
493
|
-
-> Read the installed files and continue
|
|
494
|
-
|
|
495
|
-
User: "help me set up Docker for this project"
|
|
496
|
-
-> "Would you like me to search for Docker-related skills?"
|
|
497
|
-
-> If yes: run \`${bin} skills --query "docker deployment"\`
|
|
498
|
-
|
|
499
|
-
User: "I need to write tests for this Python ML pipeline"
|
|
500
|
-
-> "Would you like me to find skills for Python ML testing?"
|
|
501
|
-
-> If yes: run \`${bin} skills --query "python machine-learning testing"\`
|
|
502
|
-
|
|
503
|
-
## When NOT to trigger
|
|
504
|
-
|
|
505
|
-
- The user is working within an already well-configured area
|
|
506
|
-
- You already suggested skills for this technology in this session
|
|
507
|
-
- The user is in the middle of urgent debugging or time-sensitive work
|
|
508
|
-
- The technology is too generic (e.g. just "code" or "programming")
|
|
509
|
-
`;
|
|
510
|
-
}
|
|
511
|
-
function getSaveLearningContent() {
|
|
512
|
-
const bin = resolveCaliber();
|
|
513
|
-
return `# Save Learning
|
|
514
|
-
|
|
515
|
-
Save a user's instruction or preference as a persistent learning that
|
|
516
|
-
will be applied in all future sessions on this project.
|
|
517
|
-
|
|
518
|
-
## Instructions
|
|
519
|
-
|
|
520
|
-
1. Detect when the user gives an instruction to remember, such as:
|
|
521
|
-
- "remember this", "save this", "always do X", "never do Y"
|
|
522
|
-
- "from now on", "going forward", "in this project we..."
|
|
523
|
-
- Any stated convention, preference, or rule
|
|
524
|
-
2. Refine the instruction into a clean, actionable learning bullet with
|
|
525
|
-
an appropriate type prefix:
|
|
526
|
-
- \`**[convention]**\` \u2014 coding style, workflow, git conventions
|
|
527
|
-
- \`**[pattern]**\` \u2014 reusable code patterns
|
|
528
|
-
- \`**[anti-pattern]**\` \u2014 things to avoid
|
|
529
|
-
- \`**[preference]**\` \u2014 personal/team preferences
|
|
530
|
-
- \`**[context]**\` \u2014 project-specific context
|
|
531
|
-
3. Show the refined learning to the user and ask for confirmation
|
|
532
|
-
4. If confirmed, run:
|
|
533
|
-
\`\`\`bash
|
|
534
|
-
${bin} learn add "<refined learning>"
|
|
535
|
-
\`\`\`
|
|
536
|
-
For personal preferences (not project-level), add \`--personal\`:
|
|
537
|
-
\`\`\`bash
|
|
538
|
-
${bin} learn add --personal "<refined learning>"
|
|
539
|
-
\`\`\`
|
|
540
|
-
5. Stage the learnings file for the next commit:
|
|
541
|
-
\`\`\`bash
|
|
542
|
-
git add CALIBER_LEARNINGS.md
|
|
543
|
-
\`\`\`
|
|
544
|
-
|
|
545
|
-
## Examples
|
|
546
|
-
|
|
547
|
-
User: "when developing features, push to next branch not master, remember it"
|
|
548
|
-
-> Refine: \`**[convention]** Push feature commits to the \\\`next\\\` branch, not \\\`master\\\`\`
|
|
549
|
-
-> "I'll save this as a project learning:
|
|
550
|
-
**[convention]** Push feature commits to the \\\`next\\\` branch, not \\\`master\\\`
|
|
551
|
-
Save for future sessions?"
|
|
552
|
-
-> If yes: run \`${bin} learn add "**[convention]** Push feature commits to the next branch, not master"\`
|
|
553
|
-
-> Run \`git add CALIBER_LEARNINGS.md\`
|
|
554
|
-
|
|
555
|
-
User: "always use bun instead of npm"
|
|
556
|
-
-> Refine: \`**[preference]** Use \\\`bun\\\` instead of \\\`npm\\\` for package management\`
|
|
557
|
-
-> Confirm and save
|
|
558
|
-
|
|
559
|
-
User: "never use any in TypeScript, use unknown instead"
|
|
560
|
-
-> Refine: \`**[convention]** Use \\\`unknown\\\` instead of \\\`any\\\` in TypeScript\`
|
|
561
|
-
-> Confirm and save
|
|
562
|
-
|
|
563
|
-
## When NOT to trigger
|
|
564
|
-
|
|
565
|
-
- The user is giving a one-time instruction for the current task only
|
|
566
|
-
- The instruction is too vague to be actionable
|
|
567
|
-
- The user explicitly says "just for now" or "only this time"
|
|
568
|
-
`;
|
|
569
|
-
}
|
|
570
|
-
function getSetupCaliberContent() {
|
|
571
|
-
return `# Setup Caliber
|
|
572
|
-
|
|
573
|
-
Dynamic onboarding for Caliber \u2014 automatic AI agent context sync.
|
|
574
|
-
Run all diagnostic steps below on every invocation to determine what's already
|
|
575
|
-
set up and what still needs to be done.
|
|
576
|
-
|
|
577
|
-
## Instructions
|
|
578
|
-
|
|
579
|
-
Run these checks in order. For each step, check the current state first,
|
|
580
|
-
then only act if something is missing.
|
|
581
|
-
|
|
582
|
-
### Step 1: Check if Caliber is installed
|
|
583
|
-
|
|
584
|
-
\`\`\`bash
|
|
585
|
-
command -v caliber >/dev/null 2>&1 && caliber --version || echo "NOT_INSTALLED"
|
|
586
|
-
\`\`\`
|
|
587
|
-
|
|
588
|
-
- If a version prints \u2192 Caliber is installed globally. Set \`CALIBER="caliber"\` and move to Step 2.
|
|
589
|
-
- If NOT_INSTALLED \u2192 Install it globally (faster for daily use since the pre-commit hook runs on every commit):
|
|
590
|
-
\`\`\`bash
|
|
591
|
-
npm install -g @rely-ai/caliber
|
|
592
|
-
\`\`\`
|
|
593
|
-
Set \`CALIBER="caliber"\`.
|
|
594
|
-
|
|
595
|
-
If npm fails (permissions, no sudo, etc.), fall back to npx:
|
|
596
|
-
\`\`\`bash
|
|
597
|
-
npx @rely-ai/caliber --version 2>/dev/null || echo "NO_NODE"
|
|
598
|
-
\`\`\`
|
|
599
|
-
- If npx works \u2192 Set \`CALIBER="npx @rely-ai/caliber"\`. This works but adds ~500ms per invocation.
|
|
600
|
-
- If NO_NODE \u2192 Tell the user: "Caliber requires Node.js >= 20. Install Node first, then run /setup-caliber again." Stop here.
|
|
601
|
-
|
|
602
|
-
### Step 2: Check if pre-commit hook is installed
|
|
603
|
-
|
|
604
|
-
\`\`\`bash
|
|
605
|
-
grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "HOOK_ACTIVE" || echo "NO_HOOK"
|
|
606
|
-
\`\`\`
|
|
607
|
-
|
|
608
|
-
- If HOOK_ACTIVE \u2192 Tell the user: "Pre-commit hook is active \u2014 configs sync on every commit." Move to Step 3.
|
|
609
|
-
- If NO_HOOK \u2192 Tell the user: "I'll install the pre-commit hook so your agent configs sync automatically on every commit."
|
|
610
|
-
\`\`\`bash
|
|
611
|
-
$CALIBER hooks --install
|
|
612
|
-
\`\`\`
|
|
613
|
-
|
|
614
|
-
### Step 3: Detect agents and check if configs exist
|
|
615
|
-
|
|
616
|
-
First, detect which coding agents are configured in this project:
|
|
617
|
-
\`\`\`bash
|
|
618
|
-
AGENTS=""
|
|
619
|
-
[ -d .claude ] && AGENTS="claude"
|
|
620
|
-
[ -d .cursor ] && AGENTS="\${AGENTS:+$AGENTS,}cursor"
|
|
621
|
-
[ -d .agents ] || [ -f AGENTS.md ] && AGENTS="\${AGENTS:+$AGENTS,}codex"
|
|
622
|
-
[ -f .github/copilot-instructions.md ] && AGENTS="\${AGENTS:+$AGENTS,}github-copilot"
|
|
623
|
-
echo "DETECTED_AGENTS=\${AGENTS:-none}"
|
|
624
|
-
\`\`\`
|
|
625
|
-
|
|
626
|
-
If no agents are detected, ask the user which coding agents they use (Claude Code, Cursor, Codex, GitHub Copilot).
|
|
627
|
-
Build the agent list from their answer as a comma-separated string (e.g. "claude,cursor").
|
|
628
|
-
|
|
629
|
-
Then check if agent configs exist:
|
|
630
|
-
\`\`\`bash
|
|
631
|
-
echo "CLAUDE_MD=$([ -f CLAUDE.md ] && echo exists || echo missing)"
|
|
632
|
-
echo "CURSOR_RULES=$([ -d .cursor/rules ] && ls .cursor/rules/*.mdc 2>/dev/null | wc -l | tr -d ' ' || echo 0)"
|
|
633
|
-
echo "AGENTS_MD=$([ -f AGENTS.md ] && echo exists || echo missing)"
|
|
634
|
-
echo "COPILOT=$([ -f .github/copilot-instructions.md ] && echo exists || echo missing)"
|
|
635
|
-
\`\`\`
|
|
636
|
-
|
|
637
|
-
- If configs exist for the detected agents \u2192 Tell the user which configs are present. Move to Step 4.
|
|
638
|
-
- If configs are missing \u2192 Tell the user: "No agent configs found. I'll generate them now."
|
|
639
|
-
Use the detected or user-selected agent list:
|
|
640
|
-
\`\`\`bash
|
|
641
|
-
$CALIBER init --auto-approve --agent <comma-separated-agents>
|
|
642
|
-
\`\`\`
|
|
643
|
-
For example: \`$CALIBER init --auto-approve --agent claude,cursor\`
|
|
644
|
-
This generates CLAUDE.md, Cursor rules, AGENTS.md, skills, and sync infrastructure for the specified agents.
|
|
645
|
-
|
|
646
|
-
### Step 4: Check if configs are fresh
|
|
647
|
-
|
|
648
|
-
\`\`\`bash
|
|
649
|
-
$CALIBER score --json --quiet 2>/dev/null | head -1
|
|
650
|
-
\`\`\`
|
|
651
|
-
|
|
652
|
-
- If score is 80+ \u2192 Tell the user: "Your configs are in good shape (score: X/100)."
|
|
653
|
-
- If score is below 80 \u2192 Tell the user: "Your configs could be improved (score: X/100). Want me to run a refresh?"
|
|
654
|
-
If yes:
|
|
655
|
-
\`\`\`bash
|
|
656
|
-
$CALIBER refresh
|
|
657
|
-
\`\`\`
|
|
658
|
-
|
|
659
|
-
### Step 5: Ask about team setup
|
|
660
|
-
|
|
661
|
-
Ask the user: "Are you setting up for yourself only, or for your team too?"
|
|
662
|
-
|
|
663
|
-
- If **solo** \u2192 Continue with solo setup:
|
|
664
|
-
|
|
665
|
-
Check if session learning is enabled:
|
|
666
|
-
\`\`\`bash
|
|
667
|
-
$CALIBER learn status 2>/dev/null | head -3
|
|
668
|
-
\`\`\`
|
|
669
|
-
- If learning is already enabled \u2192 note it in the summary.
|
|
670
|
-
- If not enabled \u2192 ask the user: "Caliber can learn from your coding sessions \u2014 when you correct a mistake or fix a pattern, it remembers for next time. Enable session learning?"
|
|
671
|
-
If yes:
|
|
672
|
-
\`\`\`bash
|
|
673
|
-
$CALIBER learn install
|
|
674
|
-
\`\`\`
|
|
675
|
-
|
|
676
|
-
Then tell the user:
|
|
677
|
-
"You're all set! Here's what happens next:
|
|
678
|
-
- Every time you commit, Caliber syncs your agent configs automatically
|
|
679
|
-
- Your CLAUDE.md, Cursor rules, and AGENTS.md stay current with your code
|
|
680
|
-
- Run \`$CALIBER skills\` anytime to discover community skills for your stack"
|
|
681
|
-
|
|
682
|
-
Then show the summary (see below) and stop.
|
|
683
|
-
|
|
684
|
-
- If **team** \u2192 Check if the GitHub Action already exists:
|
|
685
|
-
\`\`\`bash
|
|
686
|
-
[ -f .github/workflows/caliber-sync.yml ] && echo "ACTION_EXISTS" || echo "NO_ACTION"
|
|
687
|
-
\`\`\`
|
|
688
|
-
- If ACTION_EXISTS \u2192 Tell the user: "GitHub Action is already configured."
|
|
689
|
-
- If NO_ACTION \u2192 Tell the user: "I'll create a GitHub Action that syncs configs nightly and on every PR."
|
|
690
|
-
Write this file to \`.github/workflows/caliber-sync.yml\`:
|
|
691
|
-
\`\`\`yaml
|
|
692
|
-
name: Caliber Sync
|
|
693
|
-
on:
|
|
694
|
-
schedule:
|
|
695
|
-
- cron: '0 3 * * 1-5'
|
|
696
|
-
pull_request:
|
|
697
|
-
types: [opened, synchronize]
|
|
698
|
-
workflow_dispatch:
|
|
699
|
-
jobs:
|
|
700
|
-
sync:
|
|
701
|
-
runs-on: ubuntu-latest
|
|
702
|
-
steps:
|
|
703
|
-
- uses: actions/checkout@v4
|
|
704
|
-
- uses: caliber-ai-org/ai-setup@v1
|
|
705
|
-
with:
|
|
706
|
-
mode: sync
|
|
707
|
-
auto-refresh: true
|
|
708
|
-
comment: true
|
|
709
|
-
github-token: \${{ secrets.GITHUB_TOKEN }}
|
|
710
|
-
env:
|
|
711
|
-
ANTHROPIC_API_KEY: \${{ secrets.ANTHROPIC_API_KEY }}
|
|
712
|
-
\`\`\`
|
|
713
|
-
Now determine which LLM provider the team uses. Check the local Caliber config:
|
|
714
|
-
\`\`\`bash
|
|
715
|
-
$CALIBER config --show 2>/dev/null || echo "NO_CONFIG"
|
|
716
|
-
\`\`\`
|
|
717
|
-
|
|
718
|
-
Based on the provider, the GitHub Action needs the corresponding secret:
|
|
719
|
-
- **anthropic** \u2192 \`ANTHROPIC_API_KEY\`
|
|
720
|
-
- **openai** \u2192 \`OPENAI_API_KEY\`
|
|
721
|
-
- **vertex** \u2192 \`VERTEX_PROJECT_ID\` and \`GOOGLE_APPLICATION_CREDENTIALS\` (service account JSON)
|
|
722
|
-
|
|
723
|
-
Update the workflow env block to match the provider. For example, if using OpenAI:
|
|
724
|
-
\`\`\`yaml
|
|
725
|
-
env:
|
|
726
|
-
OPENAI_API_KEY: \${{ secrets.OPENAI_API_KEY }}
|
|
727
|
-
\`\`\`
|
|
728
|
-
|
|
729
|
-
Then check if the \`gh\` CLI is available to set the secret:
|
|
730
|
-
\`\`\`bash
|
|
731
|
-
command -v gh >/dev/null 2>&1 && echo "GH_AVAILABLE" || echo "NO_GH"
|
|
732
|
-
\`\`\`
|
|
733
|
-
|
|
734
|
-
- If GH_AVAILABLE \u2192 Ask the user for their API key and set it:
|
|
735
|
-
\`\`\`bash
|
|
736
|
-
gh secret set ANTHROPIC_API_KEY
|
|
737
|
-
\`\`\`
|
|
738
|
-
(This prompts for the value securely via stdin)
|
|
739
|
-
- If NO_GH \u2192 Tell the user exactly what to do:
|
|
740
|
-
"Go to your repo on GitHub \u2192 Settings \u2192 Secrets and variables \u2192 Actions \u2192 New repository secret.
|
|
741
|
-
Name: ANTHROPIC_API_KEY (or OPENAI_API_KEY depending on provider)
|
|
742
|
-
Value: your API key"
|
|
743
|
-
|
|
744
|
-
Finally, offer to commit and push the workflow file:
|
|
745
|
-
\`\`\`bash
|
|
746
|
-
git add .github/workflows/caliber-sync.yml
|
|
747
|
-
git commit -m "feat: add Caliber sync GitHub Action"
|
|
748
|
-
git push
|
|
749
|
-
\`\`\`
|
|
750
|
-
|
|
751
|
-
### Summary
|
|
752
|
-
|
|
753
|
-
After completing all steps, show the user what's configured:
|
|
754
|
-
|
|
755
|
-
\`\`\`
|
|
756
|
-
Caliber Setup Complete:
|
|
757
|
-
\u2713 Caliber installed (vX.X.X)
|
|
758
|
-
\u2713 Pre-commit hook \u2014 configs sync on every commit
|
|
759
|
-
\u2713 Agent configs \u2014 CLAUDE.md, Cursor rules, AGENTS.md
|
|
760
|
-
\u2713 Config score: X/100
|
|
761
|
-
\u2713 GitHub Action \u2014 nightly sync + PR checks (team only)
|
|
762
|
-
|
|
763
|
-
From now on, every commit keeps all your agent configs in sync automatically.
|
|
764
|
-
\`\`\`
|
|
765
|
-
|
|
766
|
-
## When to trigger
|
|
767
|
-
|
|
768
|
-
- When a developer first opens this project
|
|
769
|
-
- When the user asks about keeping agent configs up to date
|
|
770
|
-
- When the user asks "how do I set up Caliber" or "what is Caliber"
|
|
771
|
-
- When the user sees a message saying "Run /setup-caliber to get set up"
|
|
772
|
-
|
|
773
|
-
## When NOT to trigger
|
|
774
|
-
|
|
775
|
-
- The user is in the middle of time-sensitive work
|
|
776
|
-
`;
|
|
777
|
-
}
|
|
778
|
-
function ensureBuiltinSkills() {
|
|
779
|
-
const written = [];
|
|
780
|
-
for (const { platformDir, skillsDir } of PLATFORM_CONFIGS) {
|
|
781
|
-
if (!fs17.existsSync(platformDir)) continue;
|
|
782
|
-
for (const skill of BUILTIN_SKILLS) {
|
|
783
|
-
const skillPath = path17.join(skillsDir, skill.name, "SKILL.md");
|
|
784
|
-
fs17.mkdirSync(path17.dirname(skillPath), { recursive: true });
|
|
785
|
-
fs17.writeFileSync(skillPath, buildSkillContent(skill));
|
|
786
|
-
written.push(skillPath);
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
return written;
|
|
790
|
-
}
|
|
791
|
-
var FIND_SKILLS_SKILL, SAVE_LEARNING_SKILL, SETUP_CALIBER_SKILL, BUILTIN_SKILLS, PLATFORM_CONFIGS, BUILTIN_SKILL_NAMES;
|
|
792
|
-
var init_builtin_skills = __esm({
|
|
793
|
-
"src/lib/builtin-skills.ts"() {
|
|
794
|
-
"use strict";
|
|
795
|
-
init_resolve_caliber();
|
|
796
|
-
FIND_SKILLS_SKILL = {
|
|
797
|
-
name: "find-skills",
|
|
798
|
-
description: "Discovers and installs community skills from the public registry. Use when the user mentions a technology, framework, or task that could benefit from specialized skills not yet installed, asks 'how do I do X', 'find a skill for X', or starts work in a new technology area. Proactively suggest when the user's task involves tools or frameworks without existing skills.",
|
|
799
|
-
get content() {
|
|
800
|
-
return getFindSkillsContent();
|
|
801
|
-
}
|
|
802
|
-
};
|
|
803
|
-
SAVE_LEARNING_SKILL = {
|
|
804
|
-
name: "save-learning",
|
|
805
|
-
description: "Saves user instructions as persistent learnings for future sessions. Use when the user says 'remember this', 'always do X', 'from now on', 'never do Y', or gives any instruction they want persisted across sessions. Proactively suggest when the user states a preference, convention, or rule they clearly want followed in the future.",
|
|
806
|
-
get content() {
|
|
807
|
-
return getSaveLearningContent();
|
|
808
|
-
}
|
|
809
|
-
};
|
|
810
|
-
SETUP_CALIBER_SKILL = {
|
|
811
|
-
name: "setup-caliber",
|
|
812
|
-
description: "Sets up Caliber for automatic AI agent context sync. Installs pre-commit hooks so CLAUDE.md, Cursor rules, and Copilot instructions update automatically on every commit. Use when Caliber hooks are not yet installed or when the user asks about keeping agent configs in sync.",
|
|
813
|
-
get content() {
|
|
814
|
-
return getSetupCaliberContent();
|
|
815
|
-
}
|
|
816
|
-
};
|
|
817
|
-
BUILTIN_SKILLS = [FIND_SKILLS_SKILL, SAVE_LEARNING_SKILL, SETUP_CALIBER_SKILL];
|
|
818
|
-
PLATFORM_CONFIGS = [
|
|
819
|
-
{ platformDir: ".claude", skillsDir: path17.join(".claude", "skills") },
|
|
820
|
-
{ platformDir: ".cursor", skillsDir: path17.join(".cursor", "skills") },
|
|
821
|
-
{ platformDir: ".agents", skillsDir: path17.join(".agents", "skills") }
|
|
822
|
-
];
|
|
823
|
-
BUILTIN_SKILL_NAMES = new Set(BUILTIN_SKILLS.map((s) => s.name));
|
|
824
|
-
}
|
|
825
|
-
});
|
|
826
|
-
|
|
827
233
|
// src/utils/editor.ts
|
|
828
|
-
import { execSync as
|
|
829
|
-
import
|
|
830
|
-
import
|
|
234
|
+
import { execSync as execSync13, spawn as spawn3 } from "child_process";
|
|
235
|
+
import fs26 from "fs";
|
|
236
|
+
import path23 from "path";
|
|
831
237
|
import os6 from "os";
|
|
832
238
|
function getEmptyFilePath(proposedPath) {
|
|
833
|
-
|
|
834
|
-
const tempPath =
|
|
835
|
-
|
|
239
|
+
fs26.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
|
|
240
|
+
const tempPath = path23.join(DIFF_TEMP_DIR, path23.basename(proposedPath));
|
|
241
|
+
fs26.writeFileSync(tempPath, "");
|
|
836
242
|
return tempPath;
|
|
837
243
|
}
|
|
838
244
|
function commandExists(cmd) {
|
|
839
245
|
try {
|
|
840
246
|
const check = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
|
|
841
|
-
|
|
247
|
+
execSync13(check, { stdio: "ignore" });
|
|
842
248
|
return true;
|
|
843
249
|
} catch {
|
|
844
250
|
return false;
|
|
@@ -872,7 +278,7 @@ var init_editor = __esm({
|
|
|
872
278
|
"src/utils/editor.ts"() {
|
|
873
279
|
"use strict";
|
|
874
280
|
IS_WINDOWS3 = process.platform === "win32";
|
|
875
|
-
DIFF_TEMP_DIR =
|
|
281
|
+
DIFF_TEMP_DIR = path23.join(os6.tmpdir(), "caliber-diff");
|
|
876
282
|
}
|
|
877
283
|
});
|
|
878
284
|
|
|
@@ -884,7 +290,7 @@ __export(review_exports, {
|
|
|
884
290
|
promptWantsReview: () => promptWantsReview
|
|
885
291
|
});
|
|
886
292
|
import chalk10 from "chalk";
|
|
887
|
-
import
|
|
293
|
+
import fs27 from "fs";
|
|
888
294
|
import select4 from "@inquirer/select";
|
|
889
295
|
import { createTwoFilesPatch } from "diff";
|
|
890
296
|
async function promptWantsReview() {
|
|
@@ -921,8 +327,8 @@ async function openReview(method, stagedFiles) {
|
|
|
921
327
|
return;
|
|
922
328
|
}
|
|
923
329
|
const fileInfos = stagedFiles.map((file) => {
|
|
924
|
-
const proposed =
|
|
925
|
-
const current = file.currentPath ?
|
|
330
|
+
const proposed = fs27.readFileSync(file.proposedPath, "utf-8");
|
|
331
|
+
const current = file.currentPath ? fs27.readFileSync(file.currentPath, "utf-8") : "";
|
|
926
332
|
const patch = createTwoFilesPatch(
|
|
927
333
|
file.isNew ? "/dev/null" : file.relativePath,
|
|
928
334
|
file.relativePath,
|
|
@@ -1097,13 +503,13 @@ __export(lock_exports, {
|
|
|
1097
503
|
isCaliberRunning: () => isCaliberRunning,
|
|
1098
504
|
releaseLock: () => releaseLock
|
|
1099
505
|
});
|
|
1100
|
-
import
|
|
1101
|
-
import
|
|
506
|
+
import fs37 from "fs";
|
|
507
|
+
import path30 from "path";
|
|
1102
508
|
import os8 from "os";
|
|
1103
509
|
function isCaliberRunning() {
|
|
1104
510
|
try {
|
|
1105
|
-
if (!
|
|
1106
|
-
const raw =
|
|
511
|
+
if (!fs37.existsSync(LOCK_FILE)) return false;
|
|
512
|
+
const raw = fs37.readFileSync(LOCK_FILE, "utf-8").trim();
|
|
1107
513
|
const { pid, ts } = JSON.parse(raw);
|
|
1108
514
|
if (Date.now() - ts > STALE_MS) return false;
|
|
1109
515
|
try {
|
|
@@ -1118,13 +524,13 @@ function isCaliberRunning() {
|
|
|
1118
524
|
}
|
|
1119
525
|
function acquireLock() {
|
|
1120
526
|
try {
|
|
1121
|
-
|
|
527
|
+
fs37.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, ts: Date.now() }));
|
|
1122
528
|
} catch {
|
|
1123
529
|
}
|
|
1124
530
|
}
|
|
1125
531
|
function releaseLock() {
|
|
1126
532
|
try {
|
|
1127
|
-
if (
|
|
533
|
+
if (fs37.existsSync(LOCK_FILE)) fs37.unlinkSync(LOCK_FILE);
|
|
1128
534
|
} catch {
|
|
1129
535
|
}
|
|
1130
536
|
}
|
|
@@ -1132,21 +538,21 @@ var LOCK_FILE, STALE_MS;
|
|
|
1132
538
|
var init_lock = __esm({
|
|
1133
539
|
"src/lib/lock.ts"() {
|
|
1134
540
|
"use strict";
|
|
1135
|
-
LOCK_FILE =
|
|
541
|
+
LOCK_FILE = path30.join(os8.tmpdir(), ".caliber.lock");
|
|
1136
542
|
STALE_MS = 10 * 60 * 1e3;
|
|
1137
543
|
}
|
|
1138
544
|
});
|
|
1139
545
|
|
|
1140
546
|
// src/cli.ts
|
|
1141
547
|
import { Command } from "commander";
|
|
1142
|
-
import
|
|
1143
|
-
import
|
|
548
|
+
import fs47 from "fs";
|
|
549
|
+
import path39 from "path";
|
|
1144
550
|
import { fileURLToPath } from "url";
|
|
1145
551
|
|
|
1146
552
|
// src/commands/init.ts
|
|
1147
|
-
import
|
|
553
|
+
import path26 from "path";
|
|
1148
554
|
import chalk14 from "chalk";
|
|
1149
|
-
import
|
|
555
|
+
import fs32 from "fs";
|
|
1150
556
|
|
|
1151
557
|
// src/fingerprint/index.ts
|
|
1152
558
|
import fs8 from "fs";
|
|
@@ -3821,151 +3227,9 @@ function getCursorConfigDir() {
|
|
|
3821
3227
|
return path9.join(home, ".config", "Cursor");
|
|
3822
3228
|
}
|
|
3823
3229
|
|
|
3824
|
-
// src/
|
|
3825
|
-
init_resolve_caliber();
|
|
3230
|
+
// src/fingerprint/sources.ts
|
|
3826
3231
|
import fs10 from "fs";
|
|
3827
3232
|
import path10 from "path";
|
|
3828
|
-
import { execSync as execSync8 } from "child_process";
|
|
3829
|
-
var SETTINGS_PATH = path10.join(".claude", "settings.json");
|
|
3830
|
-
var REFRESH_TAIL = "refresh --quiet";
|
|
3831
|
-
var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
|
|
3832
|
-
function getHookCommand() {
|
|
3833
|
-
return `${resolveCaliber()} ${REFRESH_TAIL}`;
|
|
3834
|
-
}
|
|
3835
|
-
function readSettings() {
|
|
3836
|
-
if (!fs10.existsSync(SETTINGS_PATH)) return {};
|
|
3837
|
-
try {
|
|
3838
|
-
return JSON.parse(fs10.readFileSync(SETTINGS_PATH, "utf-8"));
|
|
3839
|
-
} catch {
|
|
3840
|
-
return {};
|
|
3841
|
-
}
|
|
3842
|
-
}
|
|
3843
|
-
function writeSettings(settings) {
|
|
3844
|
-
const dir = path10.dirname(SETTINGS_PATH);
|
|
3845
|
-
if (!fs10.existsSync(dir)) fs10.mkdirSync(dir, { recursive: true });
|
|
3846
|
-
fs10.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
3847
|
-
}
|
|
3848
|
-
function findHookIndex(sessionEnd) {
|
|
3849
|
-
return sessionEnd.findIndex(
|
|
3850
|
-
(entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, REFRESH_TAIL))
|
|
3851
|
-
);
|
|
3852
|
-
}
|
|
3853
|
-
function isHookInstalled() {
|
|
3854
|
-
const settings = readSettings();
|
|
3855
|
-
const sessionEnd = settings.hooks?.SessionEnd;
|
|
3856
|
-
if (!Array.isArray(sessionEnd)) return false;
|
|
3857
|
-
return findHookIndex(sessionEnd) !== -1;
|
|
3858
|
-
}
|
|
3859
|
-
function installHook() {
|
|
3860
|
-
const settings = readSettings();
|
|
3861
|
-
if (!settings.hooks) settings.hooks = {};
|
|
3862
|
-
if (!Array.isArray(settings.hooks.SessionEnd)) settings.hooks.SessionEnd = [];
|
|
3863
|
-
if (findHookIndex(settings.hooks.SessionEnd) !== -1) {
|
|
3864
|
-
return { installed: false, alreadyInstalled: true };
|
|
3865
|
-
}
|
|
3866
|
-
settings.hooks.SessionEnd.push({
|
|
3867
|
-
matcher: "",
|
|
3868
|
-
hooks: [{ type: "command", command: getHookCommand(), description: HOOK_DESCRIPTION }]
|
|
3869
|
-
});
|
|
3870
|
-
writeSettings(settings);
|
|
3871
|
-
return { installed: true, alreadyInstalled: false };
|
|
3872
|
-
}
|
|
3873
|
-
function removeHook() {
|
|
3874
|
-
const settings = readSettings();
|
|
3875
|
-
const sessionEnd = settings.hooks?.SessionEnd;
|
|
3876
|
-
if (!Array.isArray(sessionEnd)) {
|
|
3877
|
-
return { removed: false, notFound: true };
|
|
3878
|
-
}
|
|
3879
|
-
const idx = findHookIndex(sessionEnd);
|
|
3880
|
-
if (idx === -1) {
|
|
3881
|
-
return { removed: false, notFound: true };
|
|
3882
|
-
}
|
|
3883
|
-
sessionEnd.splice(idx, 1);
|
|
3884
|
-
if (sessionEnd.length === 0) {
|
|
3885
|
-
delete settings.hooks.SessionEnd;
|
|
3886
|
-
}
|
|
3887
|
-
if (settings.hooks && Object.keys(settings.hooks).length === 0) {
|
|
3888
|
-
delete settings.hooks;
|
|
3889
|
-
}
|
|
3890
|
-
writeSettings(settings);
|
|
3891
|
-
return { removed: true, notFound: false };
|
|
3892
|
-
}
|
|
3893
|
-
var PRECOMMIT_START = "# caliber:pre-commit:start";
|
|
3894
|
-
var PRECOMMIT_END = "# caliber:pre-commit:end";
|
|
3895
|
-
function getPrecommitBlock() {
|
|
3896
|
-
const bin = resolveCaliber();
|
|
3897
|
-
const npx = isNpxResolution();
|
|
3898
|
-
const guard = npx ? "command -v npx >/dev/null 2>&1" : `[ -x "${bin}" ] || command -v "${bin}" >/dev/null 2>&1`;
|
|
3899
|
-
const invoke = npx ? bin : `"${bin}"`;
|
|
3900
|
-
return `${PRECOMMIT_START}
|
|
3901
|
-
if ${guard}; then
|
|
3902
|
-
echo "\\033[2mcaliber: refreshing docs...\\033[0m"
|
|
3903
|
-
${invoke} refresh 2>/dev/null || true
|
|
3904
|
-
${invoke} learn finalize 2>/dev/null || true
|
|
3905
|
-
git diff --name-only -- CLAUDE.md .claude/ .cursor/ AGENTS.md CALIBER_LEARNINGS.md 2>/dev/null | xargs git add 2>/dev/null || true
|
|
3906
|
-
fi
|
|
3907
|
-
${PRECOMMIT_END}`;
|
|
3908
|
-
}
|
|
3909
|
-
function getGitHooksDir() {
|
|
3910
|
-
try {
|
|
3911
|
-
const gitDir = execSync8("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
3912
|
-
return path10.join(gitDir, "hooks");
|
|
3913
|
-
} catch {
|
|
3914
|
-
return null;
|
|
3915
|
-
}
|
|
3916
|
-
}
|
|
3917
|
-
function getPreCommitPath() {
|
|
3918
|
-
const hooksDir = getGitHooksDir();
|
|
3919
|
-
return hooksDir ? path10.join(hooksDir, "pre-commit") : null;
|
|
3920
|
-
}
|
|
3921
|
-
function isPreCommitHookInstalled() {
|
|
3922
|
-
const hookPath = getPreCommitPath();
|
|
3923
|
-
if (!hookPath || !fs10.existsSync(hookPath)) return false;
|
|
3924
|
-
const content = fs10.readFileSync(hookPath, "utf-8");
|
|
3925
|
-
return content.includes(PRECOMMIT_START);
|
|
3926
|
-
}
|
|
3927
|
-
function installPreCommitHook() {
|
|
3928
|
-
if (isPreCommitHookInstalled()) {
|
|
3929
|
-
return { installed: false, alreadyInstalled: true };
|
|
3930
|
-
}
|
|
3931
|
-
const hookPath = getPreCommitPath();
|
|
3932
|
-
if (!hookPath) return { installed: false, alreadyInstalled: false };
|
|
3933
|
-
const hooksDir = path10.dirname(hookPath);
|
|
3934
|
-
if (!fs10.existsSync(hooksDir)) fs10.mkdirSync(hooksDir, { recursive: true });
|
|
3935
|
-
let content = "";
|
|
3936
|
-
if (fs10.existsSync(hookPath)) {
|
|
3937
|
-
content = fs10.readFileSync(hookPath, "utf-8");
|
|
3938
|
-
if (!content.endsWith("\n")) content += "\n";
|
|
3939
|
-
content += "\n" + getPrecommitBlock() + "\n";
|
|
3940
|
-
} else {
|
|
3941
|
-
content = "#!/bin/sh\n\n" + getPrecommitBlock() + "\n";
|
|
3942
|
-
}
|
|
3943
|
-
fs10.writeFileSync(hookPath, content);
|
|
3944
|
-
fs10.chmodSync(hookPath, 493);
|
|
3945
|
-
return { installed: true, alreadyInstalled: false };
|
|
3946
|
-
}
|
|
3947
|
-
function removePreCommitHook() {
|
|
3948
|
-
const hookPath = getPreCommitPath();
|
|
3949
|
-
if (!hookPath || !fs10.existsSync(hookPath)) {
|
|
3950
|
-
return { removed: false, notFound: true };
|
|
3951
|
-
}
|
|
3952
|
-
let content = fs10.readFileSync(hookPath, "utf-8");
|
|
3953
|
-
if (!content.includes(PRECOMMIT_START)) {
|
|
3954
|
-
return { removed: false, notFound: true };
|
|
3955
|
-
}
|
|
3956
|
-
const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
|
|
3957
|
-
content = content.replace(regex, "\n");
|
|
3958
|
-
if (content.trim() === "#!/bin/sh" || content.trim() === "") {
|
|
3959
|
-
fs10.unlinkSync(hookPath);
|
|
3960
|
-
} else {
|
|
3961
|
-
fs10.writeFileSync(hookPath, content);
|
|
3962
|
-
}
|
|
3963
|
-
return { removed: true, notFound: false };
|
|
3964
|
-
}
|
|
3965
|
-
|
|
3966
|
-
// src/fingerprint/sources.ts
|
|
3967
|
-
import fs11 from "fs";
|
|
3968
|
-
import path11 from "path";
|
|
3969
3233
|
|
|
3970
3234
|
// src/scoring/utils.ts
|
|
3971
3235
|
import { existsSync as existsSync2, readFileSync, readdirSync } from "fs";
|
|
@@ -4294,7 +3558,7 @@ var SOURCE_CONTENT_LIMIT = 2e3;
|
|
|
4294
3558
|
var README_CONTENT_LIMIT = 1e3;
|
|
4295
3559
|
var ORIGIN_PRIORITY = { cli: 0, config: 1, workspace: 2 };
|
|
4296
3560
|
function loadSourcesConfig(dir) {
|
|
4297
|
-
const configPath =
|
|
3561
|
+
const configPath = path10.join(dir, ".caliber", "sources.json");
|
|
4298
3562
|
const content = readFileOrNull(configPath);
|
|
4299
3563
|
if (!content) return [];
|
|
4300
3564
|
try {
|
|
@@ -4312,29 +3576,29 @@ function loadSourcesConfig(dir) {
|
|
|
4312
3576
|
}
|
|
4313
3577
|
}
|
|
4314
3578
|
function writeSourcesConfig(dir, sources2) {
|
|
4315
|
-
const configDir =
|
|
4316
|
-
if (!
|
|
4317
|
-
|
|
3579
|
+
const configDir = path10.join(dir, ".caliber");
|
|
3580
|
+
if (!fs10.existsSync(configDir)) {
|
|
3581
|
+
fs10.mkdirSync(configDir, { recursive: true });
|
|
4318
3582
|
}
|
|
4319
|
-
const configPath =
|
|
4320
|
-
|
|
3583
|
+
const configPath = path10.join(configDir, "sources.json");
|
|
3584
|
+
fs10.writeFileSync(configPath, JSON.stringify({ sources: sources2 }, null, 2) + "\n", "utf-8");
|
|
4321
3585
|
}
|
|
4322
3586
|
function detectSourceType(absPath) {
|
|
4323
3587
|
try {
|
|
4324
|
-
return
|
|
3588
|
+
return fs10.statSync(absPath).isDirectory() ? "repo" : "file";
|
|
4325
3589
|
} catch {
|
|
4326
3590
|
return "file";
|
|
4327
3591
|
}
|
|
4328
3592
|
}
|
|
4329
3593
|
function isInsideDir(childPath, parentDir) {
|
|
4330
|
-
const relative2 =
|
|
4331
|
-
return !relative2.startsWith("..") && !
|
|
3594
|
+
const relative2 = path10.relative(parentDir, childPath);
|
|
3595
|
+
return !relative2.startsWith("..") && !path10.isAbsolute(relative2);
|
|
4332
3596
|
}
|
|
4333
3597
|
function resolveAllSources(dir, cliSources, workspaces) {
|
|
4334
3598
|
const seen = /* @__PURE__ */ new Map();
|
|
4335
|
-
const projectRoot =
|
|
3599
|
+
const projectRoot = path10.resolve(dir);
|
|
4336
3600
|
for (const src of cliSources) {
|
|
4337
|
-
const absPath =
|
|
3601
|
+
const absPath = path10.resolve(dir, src);
|
|
4338
3602
|
if (seen.has(absPath)) continue;
|
|
4339
3603
|
const type = detectSourceType(absPath);
|
|
4340
3604
|
seen.set(absPath, {
|
|
@@ -4347,12 +3611,12 @@ function resolveAllSources(dir, cliSources, workspaces) {
|
|
|
4347
3611
|
for (const cfg of configSources) {
|
|
4348
3612
|
if (cfg.type === "url") continue;
|
|
4349
3613
|
if (!cfg.path) continue;
|
|
4350
|
-
const absPath =
|
|
3614
|
+
const absPath = path10.resolve(dir, cfg.path);
|
|
4351
3615
|
if (seen.has(absPath)) continue;
|
|
4352
3616
|
seen.set(absPath, { absPath, config: cfg, origin: "config" });
|
|
4353
3617
|
}
|
|
4354
3618
|
for (const ws of workspaces) {
|
|
4355
|
-
const absPath =
|
|
3619
|
+
const absPath = path10.resolve(dir, ws);
|
|
4356
3620
|
if (seen.has(absPath)) continue;
|
|
4357
3621
|
if (!isInsideDir(absPath, projectRoot)) continue;
|
|
4358
3622
|
seen.set(absPath, {
|
|
@@ -4365,7 +3629,7 @@ function resolveAllSources(dir, cliSources, workspaces) {
|
|
|
4365
3629
|
for (const [absPath, resolved] of seen) {
|
|
4366
3630
|
let stat;
|
|
4367
3631
|
try {
|
|
4368
|
-
stat =
|
|
3632
|
+
stat = fs10.statSync(absPath);
|
|
4369
3633
|
} catch {
|
|
4370
3634
|
console.warn(`Source ${resolved.config.path || absPath} not found, skipping`);
|
|
4371
3635
|
continue;
|
|
@@ -4394,13 +3658,13 @@ function collectSourceSummary(resolved, projectDir) {
|
|
|
4394
3658
|
if (config.type === "file") {
|
|
4395
3659
|
return collectFileSummary(resolved, projectDir);
|
|
4396
3660
|
}
|
|
4397
|
-
const summaryPath =
|
|
3661
|
+
const summaryPath = path10.join(absPath, ".caliber", "summary.json");
|
|
4398
3662
|
const summaryContent = readFileOrNull(summaryPath);
|
|
4399
3663
|
if (summaryContent) {
|
|
4400
3664
|
try {
|
|
4401
3665
|
const published = JSON.parse(summaryContent);
|
|
4402
3666
|
return {
|
|
4403
|
-
name: published.name ||
|
|
3667
|
+
name: published.name || path10.basename(absPath),
|
|
4404
3668
|
type: "repo",
|
|
4405
3669
|
role: config.role || published.role || "related-repo",
|
|
4406
3670
|
description: config.description || published.description || "",
|
|
@@ -4420,18 +3684,18 @@ function collectRepoSummary(resolved, _projectDir) {
|
|
|
4420
3684
|
let topLevelDirs;
|
|
4421
3685
|
let keyFiles;
|
|
4422
3686
|
try {
|
|
4423
|
-
const entries =
|
|
3687
|
+
const entries = fs10.readdirSync(absPath, { withFileTypes: true });
|
|
4424
3688
|
topLevelDirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name).slice(0, 20);
|
|
4425
3689
|
keyFiles = entries.filter((e) => e.isFile() && !e.name.startsWith(".")).map((e) => e.name).slice(0, 15);
|
|
4426
3690
|
} catch {
|
|
4427
3691
|
}
|
|
4428
|
-
const claudeMdContent = readFileOrNull(
|
|
3692
|
+
const claudeMdContent = readFileOrNull(path10.join(absPath, "CLAUDE.md"));
|
|
4429
3693
|
const existingClaudeMd = claudeMdContent ? claudeMdContent.slice(0, SOURCE_CONTENT_LIMIT) : void 0;
|
|
4430
|
-
const readmeContent = readFileOrNull(
|
|
3694
|
+
const readmeContent = readFileOrNull(path10.join(absPath, "README.md"));
|
|
4431
3695
|
const readmeExcerpt = readmeContent ? readmeContent.slice(0, README_CONTENT_LIMIT) : void 0;
|
|
4432
3696
|
const gitRemoteUrl = getGitRemoteUrl(absPath);
|
|
4433
3697
|
return {
|
|
4434
|
-
name: packageName ||
|
|
3698
|
+
name: packageName || path10.basename(absPath),
|
|
4435
3699
|
type: "repo",
|
|
4436
3700
|
role: config.role || "related-repo",
|
|
4437
3701
|
description: config.description || "",
|
|
@@ -4448,7 +3712,7 @@ function collectFileSummary(resolved, _projectDir) {
|
|
|
4448
3712
|
const { config, origin, absPath } = resolved;
|
|
4449
3713
|
const content = readFileOrNull(absPath);
|
|
4450
3714
|
return {
|
|
4451
|
-
name:
|
|
3715
|
+
name: path10.basename(absPath),
|
|
4452
3716
|
type: "file",
|
|
4453
3717
|
role: config.role || "reference-doc",
|
|
4454
3718
|
description: config.description || content?.slice(0, 100).split("\n")[0] || "",
|
|
@@ -4492,15 +3756,15 @@ init_config();
|
|
|
4492
3756
|
// src/utils/dependencies.ts
|
|
4493
3757
|
import { readFileSync as readFileSync2 } from "fs";
|
|
4494
3758
|
import { join as join2 } from "path";
|
|
4495
|
-
function readFileOrNull2(
|
|
3759
|
+
function readFileOrNull2(path41) {
|
|
4496
3760
|
try {
|
|
4497
|
-
return readFileSync2(
|
|
3761
|
+
return readFileSync2(path41, "utf-8");
|
|
4498
3762
|
} catch {
|
|
4499
3763
|
return null;
|
|
4500
3764
|
}
|
|
4501
3765
|
}
|
|
4502
|
-
function readJsonOrNull(
|
|
4503
|
-
const content = readFileOrNull2(
|
|
3766
|
+
function readJsonOrNull(path41) {
|
|
3767
|
+
const content = readFileOrNull2(path41);
|
|
4504
3768
|
if (!content) return null;
|
|
4505
3769
|
try {
|
|
4506
3770
|
return JSON.parse(content);
|
|
@@ -5083,29 +4347,149 @@ ${f.content}
|
|
|
5083
4347
|
header = `
|
|
5084
4348
|
--- Project Files (${ca.files.length} files, ~${ca.includedTokens.toLocaleString()} tokens) ---`;
|
|
5085
4349
|
}
|
|
5086
|
-
parts.push(header);
|
|
5087
|
-
parts.push(codeLines.join("\n"));
|
|
4350
|
+
parts.push(header);
|
|
4351
|
+
parts.push(codeLines.join("\n"));
|
|
4352
|
+
}
|
|
4353
|
+
if (fingerprint.sources?.length) {
|
|
4354
|
+
parts.push(formatSourcesForPrompt(fingerprint.sources));
|
|
4355
|
+
}
|
|
4356
|
+
return parts.join("\n");
|
|
4357
|
+
}
|
|
4358
|
+
|
|
4359
|
+
// src/writers/index.ts
|
|
4360
|
+
import fs18 from "fs";
|
|
4361
|
+
|
|
4362
|
+
// src/writers/claude/index.ts
|
|
4363
|
+
import fs11 from "fs";
|
|
4364
|
+
import path11 from "path";
|
|
4365
|
+
|
|
4366
|
+
// src/writers/pre-commit-block.ts
|
|
4367
|
+
init_resolve_caliber();
|
|
4368
|
+
var BLOCK_START = "<!-- caliber:managed:pre-commit -->";
|
|
4369
|
+
var BLOCK_END = "<!-- /caliber:managed:pre-commit -->";
|
|
4370
|
+
var MANAGED_DOC_PATHS = "CLAUDE.md .claude/ .cursor/ .github/copilot-instructions.md AGENTS.md CALIBER_LEARNINGS.md";
|
|
4371
|
+
function getPreCommitBlock() {
|
|
4372
|
+
const bin = resolveCaliber();
|
|
4373
|
+
return `${BLOCK_START}
|
|
4374
|
+
## Before Committing
|
|
4375
|
+
|
|
4376
|
+
Run \`${bin} refresh\` before creating git commits to keep docs in sync with code changes.
|
|
4377
|
+
After it completes, stage any modified doc files before committing:
|
|
4378
|
+
|
|
4379
|
+
\`\`\`bash
|
|
4380
|
+
${bin} refresh && git add ${MANAGED_DOC_PATHS} 2>/dev/null
|
|
4381
|
+
\`\`\`
|
|
4382
|
+
${BLOCK_END}`;
|
|
4383
|
+
}
|
|
4384
|
+
var CURSOR_RULE_FILENAME = "caliber-pre-commit.mdc";
|
|
4385
|
+
function getCursorRuleContent() {
|
|
4386
|
+
const bin = resolveCaliber();
|
|
4387
|
+
return `---
|
|
4388
|
+
description: Run caliber refresh before git commits to keep docs in sync
|
|
4389
|
+
alwaysApply: true
|
|
4390
|
+
---
|
|
4391
|
+
Before creating git commits, run \`${bin} refresh\` to update docs based on code changes,
|
|
4392
|
+
then stage modified doc files (${MANAGED_DOC_PATHS}).
|
|
4393
|
+
`;
|
|
4394
|
+
}
|
|
4395
|
+
function hasPreCommitBlock(content) {
|
|
4396
|
+
return content.includes(BLOCK_START);
|
|
4397
|
+
}
|
|
4398
|
+
function appendPreCommitBlock(content) {
|
|
4399
|
+
if (hasPreCommitBlock(content)) return content;
|
|
4400
|
+
const trimmed = content.trimEnd();
|
|
4401
|
+
return trimmed + "\n\n" + getPreCommitBlock() + "\n";
|
|
4402
|
+
}
|
|
4403
|
+
function getCursorPreCommitRule() {
|
|
4404
|
+
return { filename: CURSOR_RULE_FILENAME, content: getCursorRuleContent() };
|
|
4405
|
+
}
|
|
4406
|
+
var LEARNINGS_BLOCK_START = "<!-- caliber:managed:learnings -->";
|
|
4407
|
+
var LEARNINGS_BLOCK_END = "<!-- /caliber:managed:learnings -->";
|
|
4408
|
+
var LEARNINGS_BLOCK = `${LEARNINGS_BLOCK_START}
|
|
4409
|
+
## Session Learnings
|
|
4410
|
+
|
|
4411
|
+
Read \`CALIBER_LEARNINGS.md\` for patterns and anti-patterns learned from previous sessions.
|
|
4412
|
+
These are auto-extracted from real tool usage \u2014 treat them as project-specific rules.
|
|
4413
|
+
${LEARNINGS_BLOCK_END}`;
|
|
4414
|
+
var CURSOR_LEARNINGS_FILENAME = "caliber-learnings.mdc";
|
|
4415
|
+
var CURSOR_LEARNINGS_CONTENT = `---
|
|
4416
|
+
description: Reference session-learned patterns from CALIBER_LEARNINGS.md
|
|
4417
|
+
alwaysApply: true
|
|
4418
|
+
---
|
|
4419
|
+
Read \`CALIBER_LEARNINGS.md\` for patterns and anti-patterns learned from previous sessions.
|
|
4420
|
+
These are auto-extracted from real tool usage \u2014 treat them as project-specific rules.
|
|
4421
|
+
`;
|
|
4422
|
+
function hasLearningsBlock(content) {
|
|
4423
|
+
return content.includes(LEARNINGS_BLOCK_START);
|
|
4424
|
+
}
|
|
4425
|
+
function appendLearningsBlock(content) {
|
|
4426
|
+
if (hasLearningsBlock(content)) return content;
|
|
4427
|
+
const trimmed = content.trimEnd();
|
|
4428
|
+
return trimmed + "\n\n" + LEARNINGS_BLOCK + "\n";
|
|
4429
|
+
}
|
|
4430
|
+
function getCursorLearningsRule() {
|
|
4431
|
+
return { filename: CURSOR_LEARNINGS_FILENAME, content: CURSOR_LEARNINGS_CONTENT };
|
|
4432
|
+
}
|
|
4433
|
+
|
|
4434
|
+
// src/writers/claude/index.ts
|
|
4435
|
+
function writeClaudeConfig(config) {
|
|
4436
|
+
const written = [];
|
|
4437
|
+
fs11.writeFileSync("CLAUDE.md", appendLearningsBlock(appendPreCommitBlock(config.claudeMd)));
|
|
4438
|
+
written.push("CLAUDE.md");
|
|
4439
|
+
if (config.skills?.length) {
|
|
4440
|
+
for (const skill of config.skills) {
|
|
4441
|
+
const skillDir = path11.join(".claude", "skills", skill.name);
|
|
4442
|
+
if (!fs11.existsSync(skillDir)) fs11.mkdirSync(skillDir, { recursive: true });
|
|
4443
|
+
const skillPath = path11.join(skillDir, "SKILL.md");
|
|
4444
|
+
const frontmatter = [
|
|
4445
|
+
"---",
|
|
4446
|
+
`name: ${skill.name}`,
|
|
4447
|
+
`description: ${skill.description}`,
|
|
4448
|
+
"---",
|
|
4449
|
+
""
|
|
4450
|
+
].join("\n");
|
|
4451
|
+
fs11.writeFileSync(skillPath, frontmatter + skill.content);
|
|
4452
|
+
written.push(skillPath);
|
|
4453
|
+
}
|
|
5088
4454
|
}
|
|
5089
|
-
if (
|
|
5090
|
-
|
|
4455
|
+
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
4456
|
+
let existingServers = {};
|
|
4457
|
+
try {
|
|
4458
|
+
if (fs11.existsSync(".mcp.json")) {
|
|
4459
|
+
const existing = JSON.parse(fs11.readFileSync(".mcp.json", "utf-8"));
|
|
4460
|
+
if (existing.mcpServers) existingServers = existing.mcpServers;
|
|
4461
|
+
}
|
|
4462
|
+
} catch {
|
|
4463
|
+
}
|
|
4464
|
+
const mergedServers = { ...existingServers, ...config.mcpServers };
|
|
4465
|
+
fs11.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
|
|
4466
|
+
written.push(".mcp.json");
|
|
5091
4467
|
}
|
|
5092
|
-
return
|
|
4468
|
+
return written;
|
|
5093
4469
|
}
|
|
5094
4470
|
|
|
5095
|
-
// src/writers/index.ts
|
|
5096
|
-
import fs19 from "fs";
|
|
5097
|
-
|
|
5098
|
-
// src/writers/claude/index.ts
|
|
5099
|
-
init_pre_commit_block();
|
|
4471
|
+
// src/writers/cursor/index.ts
|
|
5100
4472
|
import fs12 from "fs";
|
|
5101
4473
|
import path12 from "path";
|
|
5102
|
-
function
|
|
4474
|
+
function writeCursorConfig(config) {
|
|
5103
4475
|
const written = [];
|
|
5104
|
-
|
|
5105
|
-
|
|
4476
|
+
if (config.cursorrules) {
|
|
4477
|
+
fs12.writeFileSync(".cursorrules", config.cursorrules);
|
|
4478
|
+
written.push(".cursorrules");
|
|
4479
|
+
}
|
|
4480
|
+
const preCommitRule = getCursorPreCommitRule();
|
|
4481
|
+
const learningsRule = getCursorLearningsRule();
|
|
4482
|
+
const allRules = [...config.rules || [], preCommitRule, learningsRule];
|
|
4483
|
+
const rulesDir = path12.join(".cursor", "rules");
|
|
4484
|
+
if (!fs12.existsSync(rulesDir)) fs12.mkdirSync(rulesDir, { recursive: true });
|
|
4485
|
+
for (const rule of allRules) {
|
|
4486
|
+
const rulePath = path12.join(rulesDir, rule.filename);
|
|
4487
|
+
fs12.writeFileSync(rulePath, rule.content);
|
|
4488
|
+
written.push(rulePath);
|
|
4489
|
+
}
|
|
5106
4490
|
if (config.skills?.length) {
|
|
5107
4491
|
for (const skill of config.skills) {
|
|
5108
|
-
const skillDir = path12.join(".
|
|
4492
|
+
const skillDir = path12.join(".cursor", "skills", skill.name);
|
|
5109
4493
|
if (!fs12.existsSync(skillDir)) fs12.mkdirSync(skillDir, { recursive: true });
|
|
5110
4494
|
const skillPath = path12.join(skillDir, "SKILL.md");
|
|
5111
4495
|
const frontmatter = [
|
|
@@ -5120,183 +4504,286 @@ function writeClaudeConfig(config) {
|
|
|
5120
4504
|
}
|
|
5121
4505
|
}
|
|
5122
4506
|
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
4507
|
+
const cursorDir = ".cursor";
|
|
4508
|
+
if (!fs12.existsSync(cursorDir)) fs12.mkdirSync(cursorDir, { recursive: true });
|
|
4509
|
+
const mcpPath = path12.join(cursorDir, "mcp.json");
|
|
5123
4510
|
let existingServers = {};
|
|
5124
4511
|
try {
|
|
5125
|
-
if (fs12.existsSync(
|
|
5126
|
-
const existing = JSON.parse(fs12.readFileSync(
|
|
4512
|
+
if (fs12.existsSync(mcpPath)) {
|
|
4513
|
+
const existing = JSON.parse(fs12.readFileSync(mcpPath, "utf-8"));
|
|
5127
4514
|
if (existing.mcpServers) existingServers = existing.mcpServers;
|
|
5128
4515
|
}
|
|
5129
4516
|
} catch {
|
|
5130
4517
|
}
|
|
5131
4518
|
const mergedServers = { ...existingServers, ...config.mcpServers };
|
|
5132
|
-
fs12.writeFileSync(
|
|
5133
|
-
written.push(
|
|
4519
|
+
fs12.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
|
|
4520
|
+
written.push(mcpPath);
|
|
4521
|
+
}
|
|
4522
|
+
return written;
|
|
4523
|
+
}
|
|
4524
|
+
|
|
4525
|
+
// src/writers/codex/index.ts
|
|
4526
|
+
import fs13 from "fs";
|
|
4527
|
+
import path13 from "path";
|
|
4528
|
+
function writeCodexConfig(config) {
|
|
4529
|
+
const written = [];
|
|
4530
|
+
fs13.writeFileSync("AGENTS.md", appendLearningsBlock(appendPreCommitBlock(config.agentsMd)));
|
|
4531
|
+
written.push("AGENTS.md");
|
|
4532
|
+
if (config.skills?.length) {
|
|
4533
|
+
for (const skill of config.skills) {
|
|
4534
|
+
const skillDir = path13.join(".agents", "skills", skill.name);
|
|
4535
|
+
if (!fs13.existsSync(skillDir)) fs13.mkdirSync(skillDir, { recursive: true });
|
|
4536
|
+
const skillPath = path13.join(skillDir, "SKILL.md");
|
|
4537
|
+
const frontmatter = [
|
|
4538
|
+
"---",
|
|
4539
|
+
`name: ${skill.name}`,
|
|
4540
|
+
`description: ${skill.description}`,
|
|
4541
|
+
"---",
|
|
4542
|
+
""
|
|
4543
|
+
].join("\n");
|
|
4544
|
+
fs13.writeFileSync(skillPath, frontmatter + skill.content);
|
|
4545
|
+
written.push(skillPath);
|
|
4546
|
+
}
|
|
4547
|
+
}
|
|
4548
|
+
return written;
|
|
4549
|
+
}
|
|
4550
|
+
|
|
4551
|
+
// src/writers/github-copilot/index.ts
|
|
4552
|
+
import fs14 from "fs";
|
|
4553
|
+
import path14 from "path";
|
|
4554
|
+
function writeGithubCopilotConfig(config) {
|
|
4555
|
+
const written = [];
|
|
4556
|
+
if (config.instructions) {
|
|
4557
|
+
fs14.mkdirSync(".github", { recursive: true });
|
|
4558
|
+
fs14.writeFileSync(path14.join(".github", "copilot-instructions.md"), appendLearningsBlock(appendPreCommitBlock(config.instructions)));
|
|
4559
|
+
written.push(".github/copilot-instructions.md");
|
|
4560
|
+
}
|
|
4561
|
+
if (config.instructionFiles?.length) {
|
|
4562
|
+
const instructionsDir = path14.join(".github", "instructions");
|
|
4563
|
+
fs14.mkdirSync(instructionsDir, { recursive: true });
|
|
4564
|
+
for (const file of config.instructionFiles) {
|
|
4565
|
+
fs14.writeFileSync(path14.join(instructionsDir, file.filename), file.content);
|
|
4566
|
+
written.push(`.github/instructions/${file.filename}`);
|
|
4567
|
+
}
|
|
5134
4568
|
}
|
|
5135
4569
|
return written;
|
|
5136
4570
|
}
|
|
5137
|
-
|
|
5138
|
-
// src/writers/
|
|
5139
|
-
|
|
5140
|
-
import
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
const
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
4571
|
+
|
|
4572
|
+
// src/writers/backup.ts
|
|
4573
|
+
import fs15 from "fs";
|
|
4574
|
+
import path15 from "path";
|
|
4575
|
+
function createBackup(files) {
|
|
4576
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4577
|
+
const backupDir = path15.join(BACKUPS_DIR, timestamp);
|
|
4578
|
+
for (const file of files) {
|
|
4579
|
+
if (!fs15.existsSync(file)) continue;
|
|
4580
|
+
const dest = path15.join(backupDir, file);
|
|
4581
|
+
const destDir = path15.dirname(dest);
|
|
4582
|
+
if (!fs15.existsSync(destDir)) {
|
|
4583
|
+
fs15.mkdirSync(destDir, { recursive: true });
|
|
4584
|
+
}
|
|
4585
|
+
fs15.copyFileSync(file, dest);
|
|
4586
|
+
}
|
|
4587
|
+
return backupDir;
|
|
4588
|
+
}
|
|
4589
|
+
function restoreBackup(backupDir, file) {
|
|
4590
|
+
const backupFile = path15.join(backupDir, file);
|
|
4591
|
+
if (!fs15.existsSync(backupFile)) return false;
|
|
4592
|
+
const destDir = path15.dirname(file);
|
|
4593
|
+
if (!fs15.existsSync(destDir)) {
|
|
4594
|
+
fs15.mkdirSync(destDir, { recursive: true });
|
|
4595
|
+
}
|
|
4596
|
+
fs15.copyFileSync(backupFile, file);
|
|
4597
|
+
return true;
|
|
4598
|
+
}
|
|
4599
|
+
|
|
4600
|
+
// src/lib/builtin-skills.ts
|
|
4601
|
+
init_resolve_caliber();
|
|
4602
|
+
import fs16 from "fs";
|
|
4603
|
+
import path16 from "path";
|
|
4604
|
+
function buildSkillContent(skill) {
|
|
4605
|
+
const frontmatter = `---
|
|
4606
|
+
name: ${skill.name}
|
|
4607
|
+
description: ${skill.description}
|
|
4608
|
+
---
|
|
4609
|
+
|
|
4610
|
+
`;
|
|
4611
|
+
return frontmatter + skill.content;
|
|
4612
|
+
}
|
|
4613
|
+
function getFindSkillsContent() {
|
|
4614
|
+
const bin = resolveCaliber();
|
|
4615
|
+
return `# Find Skills
|
|
4616
|
+
|
|
4617
|
+
Search the public skill registry for community-contributed skills
|
|
4618
|
+
relevant to the user's current task and install them into this project.
|
|
4619
|
+
|
|
4620
|
+
## Instructions
|
|
4621
|
+
|
|
4622
|
+
1. Identify the key technologies, frameworks, or task types from the
|
|
4623
|
+
user's request that might have community skills available
|
|
4624
|
+
2. Ask the user: "Would you like me to search for community skills
|
|
4625
|
+
for [identified technologies]?"
|
|
4626
|
+
3. If the user agrees, run:
|
|
4627
|
+
\`\`\`bash
|
|
4628
|
+
${bin} skills --query "<relevant terms>"
|
|
4629
|
+
\`\`\`
|
|
4630
|
+
This outputs the top 5 matching skills with scores and descriptions.
|
|
4631
|
+
4. Present the results to the user and ask which ones to install
|
|
4632
|
+
5. Install the selected skills:
|
|
4633
|
+
\`\`\`bash
|
|
4634
|
+
${bin} skills --install <slug1>,<slug2>
|
|
4635
|
+
\`\`\`
|
|
4636
|
+
6. Read the installed SKILL.md files to load them into your current
|
|
4637
|
+
context so you can use them immediately in this session
|
|
4638
|
+
7. Summarize what was installed and continue with the user's task
|
|
4639
|
+
|
|
4640
|
+
## Examples
|
|
4641
|
+
|
|
4642
|
+
User: "let's build a web app using React"
|
|
4643
|
+
-> "I notice you want to work with React. Would you like me to search
|
|
4644
|
+
for community skills that could help with React development?"
|
|
4645
|
+
-> If yes: run \`${bin} skills --query "react frontend"\`
|
|
4646
|
+
-> Show the user the results, ask which to install
|
|
4647
|
+
-> Run \`${bin} skills --install <selected-slugs>\`
|
|
4648
|
+
-> Read the installed files and continue
|
|
4649
|
+
|
|
4650
|
+
User: "help me set up Docker for this project"
|
|
4651
|
+
-> "Would you like me to search for Docker-related skills?"
|
|
4652
|
+
-> If yes: run \`${bin} skills --query "docker deployment"\`
|
|
4653
|
+
|
|
4654
|
+
User: "I need to write tests for this Python ML pipeline"
|
|
4655
|
+
-> "Would you like me to find skills for Python ML testing?"
|
|
4656
|
+
-> If yes: run \`${bin} skills --query "python machine-learning testing"\`
|
|
4657
|
+
|
|
4658
|
+
## When NOT to trigger
|
|
4659
|
+
|
|
4660
|
+
- The user is working within an already well-configured area
|
|
4661
|
+
- You already suggested skills for this technology in this session
|
|
4662
|
+
- The user is in the middle of urgent debugging or time-sensitive work
|
|
4663
|
+
- The technology is too generic (e.g. just "code" or "programming")
|
|
4664
|
+
`;
|
|
4665
|
+
}
|
|
4666
|
+
function getSaveLearningContent() {
|
|
4667
|
+
const bin = resolveCaliber();
|
|
4668
|
+
return `# Save Learning
|
|
4669
|
+
|
|
4670
|
+
Save a user's instruction or preference as a persistent learning that
|
|
4671
|
+
will be applied in all future sessions on this project.
|
|
4672
|
+
|
|
4673
|
+
## Instructions
|
|
4674
|
+
|
|
4675
|
+
1. Detect when the user gives an instruction to remember, such as:
|
|
4676
|
+
- "remember this", "save this", "always do X", "never do Y"
|
|
4677
|
+
- "from now on", "going forward", "in this project we..."
|
|
4678
|
+
- Any stated convention, preference, or rule
|
|
4679
|
+
2. Refine the instruction into a clean, actionable learning bullet with
|
|
4680
|
+
an appropriate type prefix:
|
|
4681
|
+
- \`**[convention]**\` \u2014 coding style, workflow, git conventions
|
|
4682
|
+
- \`**[pattern]**\` \u2014 reusable code patterns
|
|
4683
|
+
- \`**[anti-pattern]**\` \u2014 things to avoid
|
|
4684
|
+
- \`**[preference]**\` \u2014 personal/team preferences
|
|
4685
|
+
- \`**[context]**\` \u2014 project-specific context
|
|
4686
|
+
3. Show the refined learning to the user and ask for confirmation
|
|
4687
|
+
4. If confirmed, run:
|
|
4688
|
+
\`\`\`bash
|
|
4689
|
+
${bin} learn add "<refined learning>"
|
|
4690
|
+
\`\`\`
|
|
4691
|
+
For personal preferences (not project-level), add \`--personal\`:
|
|
4692
|
+
\`\`\`bash
|
|
4693
|
+
${bin} learn add --personal "<refined learning>"
|
|
4694
|
+
\`\`\`
|
|
4695
|
+
5. Stage the learnings file for the next commit:
|
|
4696
|
+
\`\`\`bash
|
|
4697
|
+
git add CALIBER_LEARNINGS.md
|
|
4698
|
+
\`\`\`
|
|
4699
|
+
|
|
4700
|
+
## Examples
|
|
4701
|
+
|
|
4702
|
+
User: "when developing features, push to next branch not master, remember it"
|
|
4703
|
+
-> Refine: \`**[convention]** Push feature commits to the \\\`next\\\` branch, not \\\`master\\\`\`
|
|
4704
|
+
-> "I'll save this as a project learning:
|
|
4705
|
+
**[convention]** Push feature commits to the \\\`next\\\` branch, not \\\`master\\\`
|
|
4706
|
+
Save for future sessions?"
|
|
4707
|
+
-> If yes: run \`${bin} learn add "**[convention]** Push feature commits to the next branch, not master"\`
|
|
4708
|
+
-> Run \`git add CALIBER_LEARNINGS.md\`
|
|
4709
|
+
|
|
4710
|
+
User: "always use bun instead of npm"
|
|
4711
|
+
-> Refine: \`**[preference]** Use \\\`bun\\\` instead of \\\`npm\\\` for package management\`
|
|
4712
|
+
-> Confirm and save
|
|
4713
|
+
|
|
4714
|
+
User: "never use any in TypeScript, use unknown instead"
|
|
4715
|
+
-> Refine: \`**[convention]** Use \\\`unknown\\\` instead of \\\`any\\\` in TypeScript\`
|
|
4716
|
+
-> Confirm and save
|
|
4717
|
+
|
|
4718
|
+
## When NOT to trigger
|
|
4719
|
+
|
|
4720
|
+
- The user is giving a one-time instruction for the current task only
|
|
4721
|
+
- The instruction is too vague to be actionable
|
|
4722
|
+
- The user explicitly says "just for now" or "only this time"
|
|
4723
|
+
`;
|
|
4724
|
+
}
|
|
4725
|
+
var FIND_SKILLS_SKILL = {
|
|
4726
|
+
name: "find-skills",
|
|
4727
|
+
description: "Discovers and installs community skills from the public registry. Use when the user mentions a technology, framework, or task that could benefit from specialized skills not yet installed, asks 'how do I do X', 'find a skill for X', or starts work in a new technology area. Proactively suggest when the user's task involves tools or frameworks without existing skills.",
|
|
4728
|
+
get content() {
|
|
4729
|
+
return getFindSkillsContent();
|
|
5157
4730
|
}
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
"---",
|
|
5165
|
-
`name: ${skill.name}`,
|
|
5166
|
-
`description: ${skill.description}`,
|
|
5167
|
-
"---",
|
|
5168
|
-
""
|
|
5169
|
-
].join("\n");
|
|
5170
|
-
fs13.writeFileSync(skillPath, frontmatter + skill.content);
|
|
5171
|
-
written.push(skillPath);
|
|
5172
|
-
}
|
|
5173
|
-
}
|
|
5174
|
-
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
5175
|
-
const cursorDir = ".cursor";
|
|
5176
|
-
if (!fs13.existsSync(cursorDir)) fs13.mkdirSync(cursorDir, { recursive: true });
|
|
5177
|
-
const mcpPath = path13.join(cursorDir, "mcp.json");
|
|
5178
|
-
let existingServers = {};
|
|
5179
|
-
try {
|
|
5180
|
-
if (fs13.existsSync(mcpPath)) {
|
|
5181
|
-
const existing = JSON.parse(fs13.readFileSync(mcpPath, "utf-8"));
|
|
5182
|
-
if (existing.mcpServers) existingServers = existing.mcpServers;
|
|
5183
|
-
}
|
|
5184
|
-
} catch {
|
|
5185
|
-
}
|
|
5186
|
-
const mergedServers = { ...existingServers, ...config.mcpServers };
|
|
5187
|
-
fs13.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
|
|
5188
|
-
written.push(mcpPath);
|
|
4731
|
+
};
|
|
4732
|
+
var SAVE_LEARNING_SKILL = {
|
|
4733
|
+
name: "save-learning",
|
|
4734
|
+
description: "Saves user instructions as persistent learnings for future sessions. Use when the user says 'remember this', 'always do X', 'from now on', 'never do Y', or gives any instruction they want persisted across sessions. Proactively suggest when the user states a preference, convention, or rule they clearly want followed in the future.",
|
|
4735
|
+
get content() {
|
|
4736
|
+
return getSaveLearningContent();
|
|
5189
4737
|
}
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
function
|
|
4738
|
+
};
|
|
4739
|
+
var BUILTIN_SKILLS = [FIND_SKILLS_SKILL, SAVE_LEARNING_SKILL];
|
|
4740
|
+
var PLATFORM_CONFIGS = [
|
|
4741
|
+
{ platformDir: ".claude", skillsDir: path16.join(".claude", "skills") },
|
|
4742
|
+
{ platformDir: ".cursor", skillsDir: path16.join(".cursor", "skills") },
|
|
4743
|
+
{ platformDir: ".agents", skillsDir: path16.join(".agents", "skills") }
|
|
4744
|
+
];
|
|
4745
|
+
function ensureBuiltinSkills() {
|
|
5198
4746
|
const written = [];
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
const frontmatter = [
|
|
5207
|
-
"---",
|
|
5208
|
-
`name: ${skill.name}`,
|
|
5209
|
-
`description: ${skill.description}`,
|
|
5210
|
-
"---",
|
|
5211
|
-
""
|
|
5212
|
-
].join("\n");
|
|
5213
|
-
fs14.writeFileSync(skillPath, frontmatter + skill.content);
|
|
4747
|
+
for (const { platformDir, skillsDir } of PLATFORM_CONFIGS) {
|
|
4748
|
+
if (!fs16.existsSync(platformDir)) continue;
|
|
4749
|
+
for (const skill of BUILTIN_SKILLS) {
|
|
4750
|
+
const skillPath = path16.join(skillsDir, skill.name, "SKILL.md");
|
|
4751
|
+
if (fs16.existsSync(skillPath)) continue;
|
|
4752
|
+
fs16.mkdirSync(path16.dirname(skillPath), { recursive: true });
|
|
4753
|
+
fs16.writeFileSync(skillPath, buildSkillContent(skill));
|
|
5214
4754
|
written.push(skillPath);
|
|
5215
4755
|
}
|
|
5216
4756
|
}
|
|
5217
4757
|
return written;
|
|
5218
4758
|
}
|
|
5219
4759
|
|
|
5220
|
-
// src/writers/github-copilot/index.ts
|
|
5221
|
-
init_pre_commit_block();
|
|
5222
|
-
import fs15 from "fs";
|
|
5223
|
-
import path15 from "path";
|
|
5224
|
-
function writeGithubCopilotConfig(config) {
|
|
5225
|
-
const written = [];
|
|
5226
|
-
if (config.instructions) {
|
|
5227
|
-
fs15.mkdirSync(".github", { recursive: true });
|
|
5228
|
-
fs15.writeFileSync(path15.join(".github", "copilot-instructions.md"), appendLearningsBlock(appendPreCommitBlock(config.instructions)));
|
|
5229
|
-
written.push(".github/copilot-instructions.md");
|
|
5230
|
-
}
|
|
5231
|
-
if (config.instructionFiles?.length) {
|
|
5232
|
-
const instructionsDir = path15.join(".github", "instructions");
|
|
5233
|
-
fs15.mkdirSync(instructionsDir, { recursive: true });
|
|
5234
|
-
for (const file of config.instructionFiles) {
|
|
5235
|
-
fs15.writeFileSync(path15.join(instructionsDir, file.filename), file.content);
|
|
5236
|
-
written.push(`.github/instructions/${file.filename}`);
|
|
5237
|
-
}
|
|
5238
|
-
}
|
|
5239
|
-
return written;
|
|
5240
|
-
}
|
|
5241
|
-
|
|
5242
|
-
// src/writers/backup.ts
|
|
5243
|
-
import fs16 from "fs";
|
|
5244
|
-
import path16 from "path";
|
|
5245
|
-
function createBackup(files) {
|
|
5246
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
5247
|
-
const backupDir = path16.join(BACKUPS_DIR, timestamp);
|
|
5248
|
-
for (const file of files) {
|
|
5249
|
-
if (!fs16.existsSync(file)) continue;
|
|
5250
|
-
const dest = path16.join(backupDir, file);
|
|
5251
|
-
const destDir = path16.dirname(dest);
|
|
5252
|
-
if (!fs16.existsSync(destDir)) {
|
|
5253
|
-
fs16.mkdirSync(destDir, { recursive: true });
|
|
5254
|
-
}
|
|
5255
|
-
fs16.copyFileSync(file, dest);
|
|
5256
|
-
}
|
|
5257
|
-
return backupDir;
|
|
5258
|
-
}
|
|
5259
|
-
function restoreBackup(backupDir, file) {
|
|
5260
|
-
const backupFile = path16.join(backupDir, file);
|
|
5261
|
-
if (!fs16.existsSync(backupFile)) return false;
|
|
5262
|
-
const destDir = path16.dirname(file);
|
|
5263
|
-
if (!fs16.existsSync(destDir)) {
|
|
5264
|
-
fs16.mkdirSync(destDir, { recursive: true });
|
|
5265
|
-
}
|
|
5266
|
-
fs16.copyFileSync(backupFile, file);
|
|
5267
|
-
return true;
|
|
5268
|
-
}
|
|
5269
|
-
|
|
5270
|
-
// src/writers/index.ts
|
|
5271
|
-
init_builtin_skills();
|
|
5272
|
-
|
|
5273
4760
|
// src/writers/manifest.ts
|
|
5274
|
-
import
|
|
4761
|
+
import fs17 from "fs";
|
|
5275
4762
|
import crypto3 from "crypto";
|
|
5276
4763
|
function readManifest() {
|
|
5277
4764
|
try {
|
|
5278
|
-
if (!
|
|
5279
|
-
return JSON.parse(
|
|
4765
|
+
if (!fs17.existsSync(MANIFEST_FILE)) return null;
|
|
4766
|
+
return JSON.parse(fs17.readFileSync(MANIFEST_FILE, "utf-8"));
|
|
5280
4767
|
} catch {
|
|
5281
4768
|
return null;
|
|
5282
4769
|
}
|
|
5283
4770
|
}
|
|
5284
4771
|
function writeManifest(manifest) {
|
|
5285
|
-
if (!
|
|
5286
|
-
|
|
4772
|
+
if (!fs17.existsSync(CALIBER_DIR)) {
|
|
4773
|
+
fs17.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
5287
4774
|
}
|
|
5288
|
-
|
|
4775
|
+
fs17.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
|
|
5289
4776
|
}
|
|
5290
4777
|
function fileChecksum(filePath) {
|
|
5291
|
-
const content =
|
|
4778
|
+
const content = fs17.readFileSync(filePath);
|
|
5292
4779
|
return crypto3.createHash("sha256").update(content).digest("hex");
|
|
5293
4780
|
}
|
|
5294
4781
|
|
|
5295
4782
|
// src/writers/index.ts
|
|
5296
4783
|
function writeSetup(setup) {
|
|
5297
4784
|
const filesToWrite = getFilesToWrite(setup);
|
|
5298
|
-
const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) =>
|
|
5299
|
-
const existingFiles = [...filesToWrite.filter((f) =>
|
|
4785
|
+
const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs18.existsSync(f));
|
|
4786
|
+
const existingFiles = [...filesToWrite.filter((f) => fs18.existsSync(f)), ...filesToDelete];
|
|
5300
4787
|
const backupDir = existingFiles.length > 0 ? createBackup(existingFiles) : void 0;
|
|
5301
4788
|
const written = [];
|
|
5302
4789
|
if (setup.targetAgent.includes("claude") && setup.claude) {
|
|
@@ -5313,7 +4800,7 @@ function writeSetup(setup) {
|
|
|
5313
4800
|
}
|
|
5314
4801
|
const deleted = [];
|
|
5315
4802
|
for (const filePath of filesToDelete) {
|
|
5316
|
-
|
|
4803
|
+
fs18.unlinkSync(filePath);
|
|
5317
4804
|
deleted.push(filePath);
|
|
5318
4805
|
}
|
|
5319
4806
|
written.push(...ensureBuiltinSkills());
|
|
@@ -5344,8 +4831,8 @@ function undoSetup() {
|
|
|
5344
4831
|
const removed = [];
|
|
5345
4832
|
for (const entry of manifest.entries) {
|
|
5346
4833
|
if (entry.action === "created") {
|
|
5347
|
-
if (
|
|
5348
|
-
|
|
4834
|
+
if (fs18.existsSync(entry.path)) {
|
|
4835
|
+
fs18.unlinkSync(entry.path);
|
|
5349
4836
|
removed.push(entry.path);
|
|
5350
4837
|
}
|
|
5351
4838
|
} else if ((entry.action === "modified" || entry.action === "deleted") && manifest.backupDir) {
|
|
@@ -5354,8 +4841,8 @@ function undoSetup() {
|
|
|
5354
4841
|
}
|
|
5355
4842
|
}
|
|
5356
4843
|
}
|
|
5357
|
-
if (
|
|
5358
|
-
|
|
4844
|
+
if (fs18.existsSync(MANIFEST_FILE)) {
|
|
4845
|
+
fs18.unlinkSync(MANIFEST_FILE);
|
|
5359
4846
|
}
|
|
5360
4847
|
return { restored, removed };
|
|
5361
4848
|
}
|
|
@@ -5397,22 +4884,22 @@ function getFilesToWrite(setup) {
|
|
|
5397
4884
|
}
|
|
5398
4885
|
function ensureGitignore() {
|
|
5399
4886
|
const gitignorePath = ".gitignore";
|
|
5400
|
-
if (
|
|
5401
|
-
const content =
|
|
4887
|
+
if (fs18.existsSync(gitignorePath)) {
|
|
4888
|
+
const content = fs18.readFileSync(gitignorePath, "utf-8");
|
|
5402
4889
|
if (!content.includes(".caliber/")) {
|
|
5403
|
-
|
|
4890
|
+
fs18.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
|
|
5404
4891
|
}
|
|
5405
4892
|
} else {
|
|
5406
|
-
|
|
4893
|
+
fs18.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
|
|
5407
4894
|
}
|
|
5408
4895
|
}
|
|
5409
4896
|
|
|
5410
4897
|
// src/writers/staging.ts
|
|
5411
|
-
import
|
|
5412
|
-
import
|
|
5413
|
-
var STAGED_DIR =
|
|
5414
|
-
var PROPOSED_DIR =
|
|
5415
|
-
var CURRENT_DIR =
|
|
4898
|
+
import fs19 from "fs";
|
|
4899
|
+
import path17 from "path";
|
|
4900
|
+
var STAGED_DIR = path17.join(CALIBER_DIR, "staged");
|
|
4901
|
+
var PROPOSED_DIR = path17.join(STAGED_DIR, "proposed");
|
|
4902
|
+
var CURRENT_DIR = path17.join(STAGED_DIR, "current");
|
|
5416
4903
|
function normalizeContent(content) {
|
|
5417
4904
|
return content.split("\n").map((line) => line.trimEnd()).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
5418
4905
|
}
|
|
@@ -5423,20 +4910,20 @@ function stageFiles(files, projectDir) {
|
|
|
5423
4910
|
const stagedFiles = [];
|
|
5424
4911
|
for (const file of files) {
|
|
5425
4912
|
assertPathWithinDir(file.path, projectDir);
|
|
5426
|
-
const originalPath =
|
|
5427
|
-
if (
|
|
5428
|
-
const existing =
|
|
4913
|
+
const originalPath = path17.join(projectDir, file.path);
|
|
4914
|
+
if (fs19.existsSync(originalPath)) {
|
|
4915
|
+
const existing = fs19.readFileSync(originalPath, "utf-8");
|
|
5429
4916
|
if (normalizeContent(existing) === normalizeContent(file.content)) {
|
|
5430
4917
|
continue;
|
|
5431
4918
|
}
|
|
5432
4919
|
}
|
|
5433
|
-
const proposedPath =
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
if (
|
|
5437
|
-
const currentPath =
|
|
5438
|
-
|
|
5439
|
-
|
|
4920
|
+
const proposedPath = path17.join(PROPOSED_DIR, file.path);
|
|
4921
|
+
fs19.mkdirSync(path17.dirname(proposedPath), { recursive: true });
|
|
4922
|
+
fs19.writeFileSync(proposedPath, file.content);
|
|
4923
|
+
if (fs19.existsSync(originalPath)) {
|
|
4924
|
+
const currentPath = path17.join(CURRENT_DIR, file.path);
|
|
4925
|
+
fs19.mkdirSync(path17.dirname(currentPath), { recursive: true });
|
|
4926
|
+
fs19.copyFileSync(originalPath, currentPath);
|
|
5440
4927
|
modifiedFiles++;
|
|
5441
4928
|
stagedFiles.push({
|
|
5442
4929
|
relativePath: file.path,
|
|
@@ -5453,14 +4940,13 @@ function stageFiles(files, projectDir) {
|
|
|
5453
4940
|
return { newFiles, modifiedFiles, stagedFiles };
|
|
5454
4941
|
}
|
|
5455
4942
|
function cleanupStaging() {
|
|
5456
|
-
if (
|
|
5457
|
-
|
|
4943
|
+
if (fs19.existsSync(STAGED_DIR)) {
|
|
4944
|
+
fs19.rmSync(STAGED_DIR, { recursive: true, force: true });
|
|
5458
4945
|
}
|
|
5459
4946
|
}
|
|
5460
4947
|
|
|
5461
4948
|
// src/commands/setup-files.ts
|
|
5462
|
-
|
|
5463
|
-
import fs21 from "fs";
|
|
4949
|
+
import fs20 from "fs";
|
|
5464
4950
|
function collectSetupFiles(setup, targetAgent) {
|
|
5465
4951
|
const files = [];
|
|
5466
4952
|
const claude = setup.claude;
|
|
@@ -5545,7 +5031,7 @@ function collectSetupFiles(setup, targetAgent) {
|
|
|
5545
5031
|
}
|
|
5546
5032
|
}
|
|
5547
5033
|
const codexTargeted = targetAgent ? targetAgent.includes("codex") : false;
|
|
5548
|
-
if (codexTargeted && !
|
|
5034
|
+
if (codexTargeted && !fs20.existsSync("AGENTS.md") && !(codex && codex.agentsMd)) {
|
|
5549
5035
|
const agentRefs = [];
|
|
5550
5036
|
if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
|
|
5551
5037
|
if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
|
|
@@ -5564,9 +5050,9 @@ ${agentRefs.join(" ")}
|
|
|
5564
5050
|
|
|
5565
5051
|
// src/lib/learning-hooks.ts
|
|
5566
5052
|
init_resolve_caliber();
|
|
5567
|
-
import
|
|
5568
|
-
import
|
|
5569
|
-
var
|
|
5053
|
+
import fs21 from "fs";
|
|
5054
|
+
import path18 from "path";
|
|
5055
|
+
var SETTINGS_PATH = path18.join(".claude", "settings.json");
|
|
5570
5056
|
var HOOK_TAILS = [
|
|
5571
5057
|
{ event: "PostToolUse", tail: "learn observe", description: "Caliber: recording tool usage for session learning" },
|
|
5572
5058
|
{ event: "PostToolUseFailure", tail: "learn observe --failure", description: "Caliber: recording tool failure for session learning" },
|
|
@@ -5582,24 +5068,24 @@ function getHookConfigs() {
|
|
|
5582
5068
|
description
|
|
5583
5069
|
}));
|
|
5584
5070
|
}
|
|
5585
|
-
function
|
|
5586
|
-
if (!
|
|
5071
|
+
function readSettings() {
|
|
5072
|
+
if (!fs21.existsSync(SETTINGS_PATH)) return {};
|
|
5587
5073
|
try {
|
|
5588
|
-
return JSON.parse(
|
|
5074
|
+
return JSON.parse(fs21.readFileSync(SETTINGS_PATH, "utf-8"));
|
|
5589
5075
|
} catch {
|
|
5590
5076
|
return {};
|
|
5591
5077
|
}
|
|
5592
5078
|
}
|
|
5593
|
-
function
|
|
5594
|
-
const dir =
|
|
5595
|
-
if (!
|
|
5596
|
-
|
|
5079
|
+
function writeSettings(settings) {
|
|
5080
|
+
const dir = path18.dirname(SETTINGS_PATH);
|
|
5081
|
+
if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
|
|
5082
|
+
fs21.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
5597
5083
|
}
|
|
5598
5084
|
function hasLearningHook(matchers, tail) {
|
|
5599
5085
|
return matchers.some((entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, tail)));
|
|
5600
5086
|
}
|
|
5601
5087
|
function areLearningHooksInstalled() {
|
|
5602
|
-
const settings =
|
|
5088
|
+
const settings = readSettings();
|
|
5603
5089
|
if (!settings.hooks) return false;
|
|
5604
5090
|
return HOOK_TAILS.every((cfg) => {
|
|
5605
5091
|
const matchers = settings.hooks[cfg.event];
|
|
@@ -5610,7 +5096,7 @@ function installLearningHooks() {
|
|
|
5610
5096
|
if (areLearningHooksInstalled()) {
|
|
5611
5097
|
return { installed: false, alreadyInstalled: true };
|
|
5612
5098
|
}
|
|
5613
|
-
const settings =
|
|
5099
|
+
const settings = readSettings();
|
|
5614
5100
|
if (!settings.hooks) settings.hooks = {};
|
|
5615
5101
|
const configs = getHookConfigs();
|
|
5616
5102
|
for (const cfg of configs) {
|
|
@@ -5624,10 +5110,10 @@ function installLearningHooks() {
|
|
|
5624
5110
|
});
|
|
5625
5111
|
}
|
|
5626
5112
|
}
|
|
5627
|
-
|
|
5113
|
+
writeSettings(settings);
|
|
5628
5114
|
return { installed: true, alreadyInstalled: false };
|
|
5629
5115
|
}
|
|
5630
|
-
var CURSOR_HOOKS_PATH =
|
|
5116
|
+
var CURSOR_HOOKS_PATH = path18.join(".cursor", "hooks.json");
|
|
5631
5117
|
var CURSOR_HOOK_EVENTS = [
|
|
5632
5118
|
{ event: "postToolUse", tail: "learn observe" },
|
|
5633
5119
|
{ event: "postToolUseFailure", tail: "learn observe --failure" },
|
|
@@ -5635,17 +5121,17 @@ var CURSOR_HOOK_EVENTS = [
|
|
|
5635
5121
|
{ event: "sessionEnd", tail: "learn finalize --auto" }
|
|
5636
5122
|
];
|
|
5637
5123
|
function readCursorHooks() {
|
|
5638
|
-
if (!
|
|
5124
|
+
if (!fs21.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
|
|
5639
5125
|
try {
|
|
5640
|
-
return JSON.parse(
|
|
5126
|
+
return JSON.parse(fs21.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
|
|
5641
5127
|
} catch {
|
|
5642
5128
|
return { version: 1, hooks: {} };
|
|
5643
5129
|
}
|
|
5644
5130
|
}
|
|
5645
5131
|
function writeCursorHooks(config) {
|
|
5646
|
-
const dir =
|
|
5647
|
-
if (!
|
|
5648
|
-
|
|
5132
|
+
const dir = path18.dirname(CURSOR_HOOKS_PATH);
|
|
5133
|
+
if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
|
|
5134
|
+
fs21.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
|
|
5649
5135
|
}
|
|
5650
5136
|
function hasCursorHook(entries, tail) {
|
|
5651
5137
|
return entries.some((e) => isCaliberCommand(e.command, tail));
|
|
@@ -5692,7 +5178,7 @@ function removeCursorLearningHooks() {
|
|
|
5692
5178
|
return { removed: true, notFound: false };
|
|
5693
5179
|
}
|
|
5694
5180
|
function removeLearningHooks() {
|
|
5695
|
-
const settings =
|
|
5181
|
+
const settings = readSettings();
|
|
5696
5182
|
if (!settings.hooks) return { removed: false, notFound: true };
|
|
5697
5183
|
let removedAny = false;
|
|
5698
5184
|
for (const cfg of HOOK_TAILS) {
|
|
@@ -5709,7 +5195,7 @@ function removeLearningHooks() {
|
|
|
5709
5195
|
delete settings.hooks;
|
|
5710
5196
|
}
|
|
5711
5197
|
if (!removedAny) return { removed: false, notFound: true };
|
|
5712
|
-
|
|
5198
|
+
writeSettings(settings);
|
|
5713
5199
|
return { removed: true, notFound: false };
|
|
5714
5200
|
}
|
|
5715
5201
|
|
|
@@ -5717,10 +5203,10 @@ function removeLearningHooks() {
|
|
|
5717
5203
|
init_resolve_caliber();
|
|
5718
5204
|
|
|
5719
5205
|
// src/lib/state.ts
|
|
5720
|
-
import
|
|
5721
|
-
import
|
|
5722
|
-
import { execSync as
|
|
5723
|
-
var STATE_FILE =
|
|
5206
|
+
import fs22 from "fs";
|
|
5207
|
+
import path19 from "path";
|
|
5208
|
+
import { execSync as execSync8 } from "child_process";
|
|
5209
|
+
var STATE_FILE = path19.join(CALIBER_DIR, ".caliber-state.json");
|
|
5724
5210
|
function normalizeTargetAgent(value) {
|
|
5725
5211
|
if (Array.isArray(value)) return value;
|
|
5726
5212
|
if (typeof value === "string") {
|
|
@@ -5731,8 +5217,8 @@ function normalizeTargetAgent(value) {
|
|
|
5731
5217
|
}
|
|
5732
5218
|
function readState() {
|
|
5733
5219
|
try {
|
|
5734
|
-
if (!
|
|
5735
|
-
const raw = JSON.parse(
|
|
5220
|
+
if (!fs22.existsSync(STATE_FILE)) return null;
|
|
5221
|
+
const raw = JSON.parse(fs22.readFileSync(STATE_FILE, "utf-8"));
|
|
5736
5222
|
if (raw.targetAgent) raw.targetAgent = normalizeTargetAgent(raw.targetAgent);
|
|
5737
5223
|
return raw;
|
|
5738
5224
|
} catch {
|
|
@@ -5740,14 +5226,14 @@ function readState() {
|
|
|
5740
5226
|
}
|
|
5741
5227
|
}
|
|
5742
5228
|
function writeState(state) {
|
|
5743
|
-
if (!
|
|
5744
|
-
|
|
5229
|
+
if (!fs22.existsSync(CALIBER_DIR)) {
|
|
5230
|
+
fs22.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
5745
5231
|
}
|
|
5746
|
-
|
|
5232
|
+
fs22.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
|
|
5747
5233
|
}
|
|
5748
5234
|
function getCurrentHeadSha() {
|
|
5749
5235
|
try {
|
|
5750
|
-
return
|
|
5236
|
+
return execSync8("git rev-parse HEAD", {
|
|
5751
5237
|
encoding: "utf-8",
|
|
5752
5238
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5753
5239
|
}).trim();
|
|
@@ -5857,9 +5343,6 @@ async function runInteractiveProviderSetup(options) {
|
|
|
5857
5343
|
return config;
|
|
5858
5344
|
}
|
|
5859
5345
|
|
|
5860
|
-
// src/commands/init.ts
|
|
5861
|
-
import confirm2 from "@inquirer/confirm";
|
|
5862
|
-
|
|
5863
5346
|
// src/scoring/index.ts
|
|
5864
5347
|
import { existsSync as existsSync7 } from "fs";
|
|
5865
5348
|
import { join as join9 } from "path";
|
|
@@ -6350,7 +5833,7 @@ function checkGrounding(dir) {
|
|
|
6350
5833
|
|
|
6351
5834
|
// src/scoring/checks/accuracy.ts
|
|
6352
5835
|
import { existsSync as existsSync4, statSync } from "fs";
|
|
6353
|
-
import { execSync as
|
|
5836
|
+
import { execSync as execSync9 } from "child_process";
|
|
6354
5837
|
import { join as join5 } from "path";
|
|
6355
5838
|
init_resolve_caliber();
|
|
6356
5839
|
function validateReferences(dir) {
|
|
@@ -6360,13 +5843,13 @@ function validateReferences(dir) {
|
|
|
6360
5843
|
}
|
|
6361
5844
|
function detectGitDrift(dir) {
|
|
6362
5845
|
try {
|
|
6363
|
-
|
|
5846
|
+
execSync9("git rev-parse --git-dir", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] });
|
|
6364
5847
|
} catch {
|
|
6365
5848
|
return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: false };
|
|
6366
5849
|
}
|
|
6367
5850
|
const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules", ".cursor/rules"];
|
|
6368
5851
|
try {
|
|
6369
|
-
const headTimestamp =
|
|
5852
|
+
const headTimestamp = execSync9(
|
|
6370
5853
|
"git log -1 --format=%ct HEAD",
|
|
6371
5854
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
6372
5855
|
).trim();
|
|
@@ -6387,7 +5870,7 @@ function detectGitDrift(dir) {
|
|
|
6387
5870
|
let latestConfigCommitHash = null;
|
|
6388
5871
|
for (const file of configFiles) {
|
|
6389
5872
|
try {
|
|
6390
|
-
const hash =
|
|
5873
|
+
const hash = execSync9(
|
|
6391
5874
|
`git log -1 --format=%H -- "${file}"`,
|
|
6392
5875
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
6393
5876
|
).trim();
|
|
@@ -6396,7 +5879,7 @@ function detectGitDrift(dir) {
|
|
|
6396
5879
|
latestConfigCommitHash = hash;
|
|
6397
5880
|
} else {
|
|
6398
5881
|
try {
|
|
6399
|
-
|
|
5882
|
+
execSync9(
|
|
6400
5883
|
`git merge-base --is-ancestor ${latestConfigCommitHash} ${hash}`,
|
|
6401
5884
|
{ cwd: dir, stdio: ["pipe", "pipe", "pipe"] }
|
|
6402
5885
|
);
|
|
@@ -6411,12 +5894,12 @@ function detectGitDrift(dir) {
|
|
|
6411
5894
|
return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: true };
|
|
6412
5895
|
}
|
|
6413
5896
|
try {
|
|
6414
|
-
const countStr =
|
|
5897
|
+
const countStr = execSync9(
|
|
6415
5898
|
`git rev-list --count ${latestConfigCommitHash}..HEAD`,
|
|
6416
5899
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
6417
5900
|
).trim();
|
|
6418
5901
|
const commitsSince = parseInt(countStr, 10) || 0;
|
|
6419
|
-
const lastDate =
|
|
5902
|
+
const lastDate = execSync9(
|
|
6420
5903
|
`git log -1 --format=%ci ${latestConfigCommitHash}`,
|
|
6421
5904
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
6422
5905
|
).trim();
|
|
@@ -6489,12 +5972,12 @@ function checkAccuracy(dir) {
|
|
|
6489
5972
|
// src/scoring/checks/freshness.ts
|
|
6490
5973
|
init_resolve_caliber();
|
|
6491
5974
|
import { existsSync as existsSync5, statSync as statSync2 } from "fs";
|
|
6492
|
-
import { execSync as
|
|
5975
|
+
import { execSync as execSync10 } from "child_process";
|
|
6493
5976
|
import { join as join6 } from "path";
|
|
6494
5977
|
function getCommitsSinceConfigUpdate(dir) {
|
|
6495
5978
|
const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules"];
|
|
6496
5979
|
try {
|
|
6497
|
-
const headTimestamp =
|
|
5980
|
+
const headTimestamp = execSync10(
|
|
6498
5981
|
"git log -1 --format=%ct HEAD",
|
|
6499
5982
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
6500
5983
|
).trim();
|
|
@@ -6514,12 +5997,12 @@ function getCommitsSinceConfigUpdate(dir) {
|
|
|
6514
5997
|
}
|
|
6515
5998
|
for (const file of configFiles) {
|
|
6516
5999
|
try {
|
|
6517
|
-
const hash =
|
|
6000
|
+
const hash = execSync10(
|
|
6518
6001
|
`git log -1 --format=%H -- "${file}"`,
|
|
6519
6002
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
6520
6003
|
).trim();
|
|
6521
6004
|
if (hash) {
|
|
6522
|
-
const countStr =
|
|
6005
|
+
const countStr = execSync10(
|
|
6523
6006
|
`git rev-list --count ${hash}..HEAD`,
|
|
6524
6007
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
6525
6008
|
).trim();
|
|
@@ -6637,13 +6120,12 @@ function checkFreshness(dir) {
|
|
|
6637
6120
|
|
|
6638
6121
|
// src/scoring/checks/bonus.ts
|
|
6639
6122
|
import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
|
|
6640
|
-
import { execSync as
|
|
6123
|
+
import { execSync as execSync11 } from "child_process";
|
|
6641
6124
|
import { join as join7 } from "path";
|
|
6642
6125
|
init_resolve_caliber();
|
|
6643
|
-
init_pre_commit_block();
|
|
6644
6126
|
function hasPreCommitHook(dir) {
|
|
6645
6127
|
try {
|
|
6646
|
-
const gitDir =
|
|
6128
|
+
const gitDir = execSync11("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
6647
6129
|
const hookPath = join7(gitDir, "hooks", "pre-commit");
|
|
6648
6130
|
const content = readFileOrNull(hookPath);
|
|
6649
6131
|
return content ? content.includes("caliber") : false;
|
|
@@ -6800,22 +6282,22 @@ function checkSources(dir) {
|
|
|
6800
6282
|
}
|
|
6801
6283
|
|
|
6802
6284
|
// src/scoring/dismissed.ts
|
|
6803
|
-
import
|
|
6804
|
-
import
|
|
6805
|
-
var DISMISSED_FILE =
|
|
6285
|
+
import fs23 from "fs";
|
|
6286
|
+
import path20 from "path";
|
|
6287
|
+
var DISMISSED_FILE = path20.join(CALIBER_DIR, "dismissed-checks.json");
|
|
6806
6288
|
function readDismissedChecks() {
|
|
6807
6289
|
try {
|
|
6808
|
-
if (!
|
|
6809
|
-
return JSON.parse(
|
|
6290
|
+
if (!fs23.existsSync(DISMISSED_FILE)) return [];
|
|
6291
|
+
return JSON.parse(fs23.readFileSync(DISMISSED_FILE, "utf-8"));
|
|
6810
6292
|
} catch {
|
|
6811
6293
|
return [];
|
|
6812
6294
|
}
|
|
6813
6295
|
}
|
|
6814
6296
|
function writeDismissedChecks(checks) {
|
|
6815
|
-
if (!
|
|
6816
|
-
|
|
6297
|
+
if (!fs23.existsSync(CALIBER_DIR)) {
|
|
6298
|
+
fs23.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
6817
6299
|
}
|
|
6818
|
-
|
|
6300
|
+
fs23.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
|
|
6819
6301
|
}
|
|
6820
6302
|
function getDismissedIds() {
|
|
6821
6303
|
return new Set(readDismissedChecks().map((c) => c.id));
|
|
@@ -6845,8 +6327,8 @@ function detectTargetAgent(dir) {
|
|
|
6845
6327
|
const agents = [];
|
|
6846
6328
|
if (existsSync7(join9(dir, "CLAUDE.md")) || existsSync7(join9(dir, ".claude", "skills"))) agents.push("claude");
|
|
6847
6329
|
if (existsSync7(join9(dir, ".cursorrules")) || existsSync7(join9(dir, ".cursor", "rules"))) agents.push("cursor");
|
|
6848
|
-
if (existsSync7(join9(dir, ".codex")) || existsSync7(join9(dir, ".agents", "skills"))
|
|
6849
|
-
if (existsSync7(join9(dir, ".github", "copilot-instructions.md"))
|
|
6330
|
+
if (existsSync7(join9(dir, ".codex")) || existsSync7(join9(dir, ".agents", "skills"))) agents.push("codex");
|
|
6331
|
+
if (existsSync7(join9(dir, ".github", "copilot-instructions.md"))) agents.push("github-copilot");
|
|
6850
6332
|
return agents.length > 0 ? agents : ["claude"];
|
|
6851
6333
|
}
|
|
6852
6334
|
function computeLocalScore(dir, targetAgent) {
|
|
@@ -7070,27 +6552,27 @@ import { PostHog } from "posthog-node";
|
|
|
7070
6552
|
import chalk5 from "chalk";
|
|
7071
6553
|
|
|
7072
6554
|
// src/telemetry/config.ts
|
|
7073
|
-
import
|
|
7074
|
-
import
|
|
6555
|
+
import fs24 from "fs";
|
|
6556
|
+
import path21 from "path";
|
|
7075
6557
|
import os5 from "os";
|
|
7076
6558
|
import crypto4 from "crypto";
|
|
7077
|
-
import { execSync as
|
|
7078
|
-
var CONFIG_DIR2 =
|
|
7079
|
-
var CONFIG_FILE2 =
|
|
6559
|
+
import { execSync as execSync12 } from "child_process";
|
|
6560
|
+
var CONFIG_DIR2 = path21.join(os5.homedir(), ".caliber");
|
|
6561
|
+
var CONFIG_FILE2 = path21.join(CONFIG_DIR2, "config.json");
|
|
7080
6562
|
var runtimeDisabled = false;
|
|
7081
6563
|
function readConfig() {
|
|
7082
6564
|
try {
|
|
7083
|
-
if (!
|
|
7084
|
-
return JSON.parse(
|
|
6565
|
+
if (!fs24.existsSync(CONFIG_FILE2)) return {};
|
|
6566
|
+
return JSON.parse(fs24.readFileSync(CONFIG_FILE2, "utf-8"));
|
|
7085
6567
|
} catch {
|
|
7086
6568
|
return {};
|
|
7087
6569
|
}
|
|
7088
6570
|
}
|
|
7089
6571
|
function writeConfig(config) {
|
|
7090
|
-
if (!
|
|
7091
|
-
|
|
6572
|
+
if (!fs24.existsSync(CONFIG_DIR2)) {
|
|
6573
|
+
fs24.mkdirSync(CONFIG_DIR2, { recursive: true });
|
|
7092
6574
|
}
|
|
7093
|
-
|
|
6575
|
+
fs24.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
|
|
7094
6576
|
}
|
|
7095
6577
|
function getMachineId() {
|
|
7096
6578
|
const config = readConfig();
|
|
@@ -7100,57 +6582,11 @@ function getMachineId() {
|
|
|
7100
6582
|
return machineId;
|
|
7101
6583
|
}
|
|
7102
6584
|
var EMAIL_HASH_KEY = "caliber-telemetry-v1";
|
|
7103
|
-
|
|
7104
|
-
"gmail.com",
|
|
7105
|
-
"googlemail.com",
|
|
7106
|
-
"outlook.com",
|
|
7107
|
-
"hotmail.com",
|
|
7108
|
-
"live.com",
|
|
7109
|
-
"yahoo.com",
|
|
7110
|
-
"yahoo.co.uk",
|
|
7111
|
-
"icloud.com",
|
|
7112
|
-
"me.com",
|
|
7113
|
-
"mac.com",
|
|
7114
|
-
"aol.com",
|
|
7115
|
-
"protonmail.com",
|
|
7116
|
-
"proton.me",
|
|
7117
|
-
"pm.me",
|
|
7118
|
-
"mail.com",
|
|
7119
|
-
"zoho.com",
|
|
7120
|
-
"yandex.com",
|
|
7121
|
-
"gmx.com",
|
|
7122
|
-
"gmx.net",
|
|
7123
|
-
"fastmail.com",
|
|
7124
|
-
"tutanota.com",
|
|
7125
|
-
"tuta.io"
|
|
7126
|
-
]);
|
|
7127
|
-
function getGitEmail() {
|
|
7128
|
-
try {
|
|
7129
|
-
const email = execSync13("git config user.email", { encoding: "utf-8" }).trim();
|
|
7130
|
-
return email || void 0;
|
|
7131
|
-
} catch {
|
|
7132
|
-
return void 0;
|
|
7133
|
-
}
|
|
7134
|
-
}
|
|
7135
|
-
function getGitEmailInfo() {
|
|
7136
|
-
const email = getGitEmail();
|
|
7137
|
-
if (!email) return {};
|
|
7138
|
-
const hash = crypto4.createHmac("sha256", EMAIL_HASH_KEY).update(email).digest("hex");
|
|
7139
|
-
let domain;
|
|
7140
|
-
if (email.includes("@")) {
|
|
7141
|
-
const d = email.split("@")[1].toLowerCase();
|
|
7142
|
-
if (!PERSONAL_DOMAINS.has(d)) domain = d;
|
|
7143
|
-
}
|
|
7144
|
-
return { hash, domain };
|
|
7145
|
-
}
|
|
7146
|
-
function getRepoHash() {
|
|
6585
|
+
function getGitEmailHash() {
|
|
7147
6586
|
try {
|
|
7148
|
-
const
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
}).trim();
|
|
7152
|
-
if (!remote) return void 0;
|
|
7153
|
-
return crypto4.createHmac("sha256", EMAIL_HASH_KEY).update(remote).digest("hex").slice(0, 16);
|
|
6587
|
+
const email = execSync12("git config user.email", { encoding: "utf-8" }).trim();
|
|
6588
|
+
if (!email) return void 0;
|
|
6589
|
+
return crypto4.createHmac("sha256", EMAIL_HASH_KEY).update(email).digest("hex");
|
|
7154
6590
|
} catch {
|
|
7155
6591
|
return void 0;
|
|
7156
6592
|
}
|
|
@@ -7175,7 +6611,6 @@ function markNoticeShown() {
|
|
|
7175
6611
|
var POSTHOG_KEY = "phc_XXrV0pSX4s2QVxVoOaeuyXDvtlRwPAjovt1ttMGVMPp";
|
|
7176
6612
|
var client = null;
|
|
7177
6613
|
var distinctId = null;
|
|
7178
|
-
var superProperties = {};
|
|
7179
6614
|
function initTelemetry() {
|
|
7180
6615
|
if (isTelemetryDisabled()) return;
|
|
7181
6616
|
const machineId = getMachineId();
|
|
@@ -7191,17 +6626,11 @@ function initTelemetry() {
|
|
|
7191
6626
|
);
|
|
7192
6627
|
markNoticeShown();
|
|
7193
6628
|
}
|
|
7194
|
-
const
|
|
7195
|
-
const repoHash = getRepoHash();
|
|
7196
|
-
superProperties = {
|
|
7197
|
-
...repoHash ? { repo_hash: repoHash } : {},
|
|
7198
|
-
...emailDomain ? { email_domain: emailDomain } : {}
|
|
7199
|
-
};
|
|
6629
|
+
const gitEmailHash = getGitEmailHash();
|
|
7200
6630
|
client.identify({
|
|
7201
6631
|
distinctId: machineId,
|
|
7202
6632
|
properties: {
|
|
7203
|
-
...gitEmailHash ? { git_email_hash: gitEmailHash } : {}
|
|
7204
|
-
...emailDomain ? { email_domain: emailDomain } : {}
|
|
6633
|
+
...gitEmailHash ? { git_email_hash: gitEmailHash } : {}
|
|
7205
6634
|
}
|
|
7206
6635
|
});
|
|
7207
6636
|
}
|
|
@@ -7210,7 +6639,7 @@ function trackEvent(name, properties) {
|
|
|
7210
6639
|
client.capture({
|
|
7211
6640
|
distinctId,
|
|
7212
6641
|
event: name,
|
|
7213
|
-
properties:
|
|
6642
|
+
properties: properties ?? {}
|
|
7214
6643
|
});
|
|
7215
6644
|
}
|
|
7216
6645
|
async function flushTelemetry() {
|
|
@@ -7259,14 +6688,11 @@ function trackInitSkillsSearch(searched, installedCount) {
|
|
|
7259
6688
|
function trackInitScoreRegression(oldScore, newScore) {
|
|
7260
6689
|
trackEvent("init_score_regression", { old_score: oldScore, new_score: newScore });
|
|
7261
6690
|
}
|
|
7262
|
-
function trackInitCompleted(path42, score) {
|
|
7263
|
-
trackEvent("init_completed", { path: path42, score });
|
|
7264
|
-
}
|
|
7265
6691
|
function trackRegenerateCompleted(action, durationMs) {
|
|
7266
6692
|
trackEvent("regenerate_completed", { action, duration_ms: durationMs });
|
|
7267
6693
|
}
|
|
7268
|
-
function trackRefreshCompleted(changesCount, durationMs
|
|
7269
|
-
trackEvent("refresh_completed", { changes_count: changesCount, duration_ms: durationMs
|
|
6694
|
+
function trackRefreshCompleted(changesCount, durationMs) {
|
|
6695
|
+
trackEvent("refresh_completed", { changes_count: changesCount, duration_ms: durationMs });
|
|
7270
6696
|
}
|
|
7271
6697
|
function trackScoreComputed(score, agent) {
|
|
7272
6698
|
trackEvent("score_computed", { score, agent });
|
|
@@ -7280,9 +6706,6 @@ function trackSkillsInstalled(count) {
|
|
|
7280
6706
|
function trackUndoExecuted() {
|
|
7281
6707
|
trackEvent("undo_executed");
|
|
7282
6708
|
}
|
|
7283
|
-
function trackUninstallExecuted() {
|
|
7284
|
-
trackEvent("uninstall_executed");
|
|
7285
|
-
}
|
|
7286
6709
|
function trackInitLearnEnabled(enabled) {
|
|
7287
6710
|
trackEvent("init_learn_enabled", { enabled });
|
|
7288
6711
|
}
|
|
@@ -8254,11 +7677,11 @@ function countIssuePoints(issues) {
|
|
|
8254
7677
|
}
|
|
8255
7678
|
async function scoreAndRefine(setup, dir, sessionHistory, callbacks) {
|
|
8256
7679
|
const existsCache = /* @__PURE__ */ new Map();
|
|
8257
|
-
const cachedExists = (
|
|
8258
|
-
const cached = existsCache.get(
|
|
7680
|
+
const cachedExists = (path41) => {
|
|
7681
|
+
const cached = existsCache.get(path41);
|
|
8259
7682
|
if (cached !== void 0) return cached;
|
|
8260
|
-
const result = existsSync9(
|
|
8261
|
-
existsCache.set(
|
|
7683
|
+
const result = existsSync9(path41);
|
|
7684
|
+
existsCache.set(path41, result);
|
|
8262
7685
|
return result;
|
|
8263
7686
|
};
|
|
8264
7687
|
const projectStructure = collectProjectStructure(dir);
|
|
@@ -8397,8 +7820,8 @@ async function runScoreRefineWithSpinner(setup, dir, sessionHistory) {
|
|
|
8397
7820
|
}
|
|
8398
7821
|
|
|
8399
7822
|
// src/lib/debug-report.ts
|
|
8400
|
-
import
|
|
8401
|
-
import
|
|
7823
|
+
import fs25 from "fs";
|
|
7824
|
+
import path22 from "path";
|
|
8402
7825
|
var DebugReport = class {
|
|
8403
7826
|
sections = [];
|
|
8404
7827
|
startTime;
|
|
@@ -8467,11 +7890,11 @@ var DebugReport = class {
|
|
|
8467
7890
|
lines.push(`| **Total** | **${formatMs(totalMs)}** |`);
|
|
8468
7891
|
lines.push("");
|
|
8469
7892
|
}
|
|
8470
|
-
const dir =
|
|
8471
|
-
if (!
|
|
8472
|
-
|
|
7893
|
+
const dir = path22.dirname(outputPath);
|
|
7894
|
+
if (!fs25.existsSync(dir)) {
|
|
7895
|
+
fs25.mkdirSync(dir, { recursive: true });
|
|
8473
7896
|
}
|
|
8474
|
-
|
|
7897
|
+
fs25.writeFileSync(outputPath, lines.join("\n"));
|
|
8475
7898
|
}
|
|
8476
7899
|
};
|
|
8477
7900
|
function formatMs(ms) {
|
|
@@ -8872,7 +8295,7 @@ import chalk11 from "chalk";
|
|
|
8872
8295
|
import ora3 from "ora";
|
|
8873
8296
|
import select5 from "@inquirer/select";
|
|
8874
8297
|
import checkbox from "@inquirer/checkbox";
|
|
8875
|
-
import
|
|
8298
|
+
import fs28 from "fs";
|
|
8876
8299
|
|
|
8877
8300
|
// src/ai/refine.ts
|
|
8878
8301
|
async function refineSetup(currentSetup, message, conversationHistory, callbacks) {
|
|
@@ -9013,10 +8436,10 @@ init_config();
|
|
|
9013
8436
|
init_review();
|
|
9014
8437
|
function detectAgents(dir) {
|
|
9015
8438
|
const agents = [];
|
|
9016
|
-
if (
|
|
9017
|
-
if (
|
|
9018
|
-
if (
|
|
9019
|
-
if (
|
|
8439
|
+
if (fs28.existsSync(`${dir}/.claude`)) agents.push("claude");
|
|
8440
|
+
if (fs28.existsSync(`${dir}/.cursor`)) agents.push("cursor");
|
|
8441
|
+
if (fs28.existsSync(`${dir}/.agents`) || fs28.existsSync(`${dir}/AGENTS.md`)) agents.push("codex");
|
|
8442
|
+
if (fs28.existsSync(`${dir}/.github/copilot-instructions.md`)) agents.push("github-copilot");
|
|
9020
8443
|
return agents;
|
|
9021
8444
|
}
|
|
9022
8445
|
async function promptAgent(detected) {
|
|
@@ -9038,6 +8461,25 @@ async function promptAgent(detected) {
|
|
|
9038
8461
|
});
|
|
9039
8462
|
return selected;
|
|
9040
8463
|
}
|
|
8464
|
+
async function promptLearnInstall(targetAgent) {
|
|
8465
|
+
const hasClaude = targetAgent.includes("claude");
|
|
8466
|
+
const hasCursor = targetAgent.includes("cursor");
|
|
8467
|
+
const agentName = hasClaude && hasCursor ? "Claude and Cursor" : hasClaude ? "Claude" : "Cursor";
|
|
8468
|
+
console.log(chalk11.bold(`
|
|
8469
|
+
Session Learning
|
|
8470
|
+
`));
|
|
8471
|
+
console.log(chalk11.dim(` Caliber can learn from your ${agentName} sessions \u2014 when a tool fails`));
|
|
8472
|
+
console.log(chalk11.dim(` or you correct a mistake, it captures the lesson so it won't`));
|
|
8473
|
+
console.log(chalk11.dim(` happen again. Runs once at session end using the fast model.
|
|
8474
|
+
`));
|
|
8475
|
+
return select5({
|
|
8476
|
+
message: "Enable session learning?",
|
|
8477
|
+
choices: [
|
|
8478
|
+
{ name: "Enable session learning (recommended)", value: true },
|
|
8479
|
+
{ name: "Skip for now", value: false }
|
|
8480
|
+
]
|
|
8481
|
+
});
|
|
8482
|
+
}
|
|
9041
8483
|
async function promptReviewAction(hasSkillResults, hasChanges, staged) {
|
|
9042
8484
|
if (!hasChanges && !hasSkillResults) return "accept";
|
|
9043
8485
|
const choices = [];
|
|
@@ -9119,18 +8561,18 @@ async function refineLoop(currentSetup, sessionHistory, summarizeSetup2, printSu
|
|
|
9119
8561
|
|
|
9120
8562
|
// src/commands/init-display.ts
|
|
9121
8563
|
import chalk12 from "chalk";
|
|
9122
|
-
import
|
|
8564
|
+
import fs29 from "fs";
|
|
9123
8565
|
function formatWhatChanged(setup) {
|
|
9124
8566
|
const lines = [];
|
|
9125
8567
|
const claude = setup.claude;
|
|
9126
8568
|
const codex = setup.codex;
|
|
9127
8569
|
const cursor = setup.cursor;
|
|
9128
8570
|
if (claude?.claudeMd) {
|
|
9129
|
-
const action =
|
|
8571
|
+
const action = fs29.existsSync("CLAUDE.md") ? "Updated" : "Created";
|
|
9130
8572
|
lines.push(`${action} CLAUDE.md`);
|
|
9131
8573
|
}
|
|
9132
8574
|
if (codex?.agentsMd) {
|
|
9133
|
-
const action =
|
|
8575
|
+
const action = fs29.existsSync("AGENTS.md") ? "Updated" : "Created";
|
|
9134
8576
|
lines.push(`${action} AGENTS.md`);
|
|
9135
8577
|
}
|
|
9136
8578
|
const allSkills = [];
|
|
@@ -9173,7 +8615,7 @@ function printSetupSummary(setup) {
|
|
|
9173
8615
|
};
|
|
9174
8616
|
if (claude) {
|
|
9175
8617
|
if (claude.claudeMd) {
|
|
9176
|
-
const icon =
|
|
8618
|
+
const icon = fs29.existsSync("CLAUDE.md") ? chalk12.yellow("~") : chalk12.green("+");
|
|
9177
8619
|
const desc = getDescription("CLAUDE.md");
|
|
9178
8620
|
console.log(` ${icon} ${chalk12.bold("CLAUDE.md")}`);
|
|
9179
8621
|
if (desc) console.log(chalk12.dim(` ${desc}`));
|
|
@@ -9183,7 +8625,7 @@ function printSetupSummary(setup) {
|
|
|
9183
8625
|
if (Array.isArray(skills) && skills.length > 0) {
|
|
9184
8626
|
for (const skill of skills) {
|
|
9185
8627
|
const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
|
|
9186
|
-
const icon =
|
|
8628
|
+
const icon = fs29.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
|
|
9187
8629
|
const desc = getDescription(skillPath);
|
|
9188
8630
|
console.log(` ${icon} ${chalk12.bold(skillPath)}`);
|
|
9189
8631
|
console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -9194,7 +8636,7 @@ function printSetupSummary(setup) {
|
|
|
9194
8636
|
const codex = setup.codex;
|
|
9195
8637
|
if (codex) {
|
|
9196
8638
|
if (codex.agentsMd) {
|
|
9197
|
-
const icon =
|
|
8639
|
+
const icon = fs29.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
|
|
9198
8640
|
const desc = getDescription("AGENTS.md");
|
|
9199
8641
|
console.log(` ${icon} ${chalk12.bold("AGENTS.md")}`);
|
|
9200
8642
|
if (desc) console.log(chalk12.dim(` ${desc}`));
|
|
@@ -9204,7 +8646,7 @@ function printSetupSummary(setup) {
|
|
|
9204
8646
|
if (Array.isArray(codexSkills) && codexSkills.length > 0) {
|
|
9205
8647
|
for (const skill of codexSkills) {
|
|
9206
8648
|
const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
|
|
9207
|
-
const icon =
|
|
8649
|
+
const icon = fs29.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
|
|
9208
8650
|
const desc = getDescription(skillPath);
|
|
9209
8651
|
console.log(` ${icon} ${chalk12.bold(skillPath)}`);
|
|
9210
8652
|
console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -9214,7 +8656,7 @@ function printSetupSummary(setup) {
|
|
|
9214
8656
|
}
|
|
9215
8657
|
if (cursor) {
|
|
9216
8658
|
if (cursor.cursorrules) {
|
|
9217
|
-
const icon =
|
|
8659
|
+
const icon = fs29.existsSync(".cursorrules") ? chalk12.yellow("~") : chalk12.green("+");
|
|
9218
8660
|
const desc = getDescription(".cursorrules");
|
|
9219
8661
|
console.log(` ${icon} ${chalk12.bold(".cursorrules")}`);
|
|
9220
8662
|
if (desc) console.log(chalk12.dim(` ${desc}`));
|
|
@@ -9224,7 +8666,7 @@ function printSetupSummary(setup) {
|
|
|
9224
8666
|
if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
|
|
9225
8667
|
for (const skill of cursorSkills) {
|
|
9226
8668
|
const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
|
|
9227
|
-
const icon =
|
|
8669
|
+
const icon = fs29.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
|
|
9228
8670
|
const desc = getDescription(skillPath);
|
|
9229
8671
|
console.log(` ${icon} ${chalk12.bold(skillPath)}`);
|
|
9230
8672
|
console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -9235,7 +8677,7 @@ function printSetupSummary(setup) {
|
|
|
9235
8677
|
if (Array.isArray(rulesArr) && rulesArr.length > 0) {
|
|
9236
8678
|
for (const rule of rulesArr) {
|
|
9237
8679
|
const rulePath = `.cursor/rules/${rule.filename}`;
|
|
9238
|
-
const icon =
|
|
8680
|
+
const icon = fs29.existsSync(rulePath) ? chalk12.yellow("~") : chalk12.green("+");
|
|
9239
8681
|
const desc = getDescription(rulePath);
|
|
9240
8682
|
console.log(` ${icon} ${chalk12.bold(rulePath)}`);
|
|
9241
8683
|
if (desc) {
|
|
@@ -9290,12 +8732,12 @@ function displayTokenUsage() {
|
|
|
9290
8732
|
// src/commands/init-helpers.ts
|
|
9291
8733
|
init_config();
|
|
9292
8734
|
import chalk13 from "chalk";
|
|
9293
|
-
import
|
|
9294
|
-
import
|
|
8735
|
+
import fs30 from "fs";
|
|
8736
|
+
import path24 from "path";
|
|
9295
8737
|
function isFirstRun(dir) {
|
|
9296
|
-
const caliberDir =
|
|
8738
|
+
const caliberDir = path24.join(dir, ".caliber");
|
|
9297
8739
|
try {
|
|
9298
|
-
const stat =
|
|
8740
|
+
const stat = fs30.statSync(caliberDir);
|
|
9299
8741
|
return !stat.isDirectory();
|
|
9300
8742
|
} catch {
|
|
9301
8743
|
return true;
|
|
@@ -9348,8 +8790,8 @@ function ensurePermissions(fingerprint) {
|
|
|
9348
8790
|
const settingsPath = ".claude/settings.json";
|
|
9349
8791
|
let settings = {};
|
|
9350
8792
|
try {
|
|
9351
|
-
if (
|
|
9352
|
-
settings = JSON.parse(
|
|
8793
|
+
if (fs30.existsSync(settingsPath)) {
|
|
8794
|
+
settings = JSON.parse(fs30.readFileSync(settingsPath, "utf-8"));
|
|
9353
8795
|
}
|
|
9354
8796
|
} catch {
|
|
9355
8797
|
}
|
|
@@ -9358,12 +8800,12 @@ function ensurePermissions(fingerprint) {
|
|
|
9358
8800
|
if (Array.isArray(allow) && allow.length > 0) return;
|
|
9359
8801
|
permissions.allow = derivePermissions(fingerprint);
|
|
9360
8802
|
settings.permissions = permissions;
|
|
9361
|
-
if (!
|
|
9362
|
-
|
|
8803
|
+
if (!fs30.existsSync(".claude")) fs30.mkdirSync(".claude", { recursive: true });
|
|
8804
|
+
fs30.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
9363
8805
|
}
|
|
9364
8806
|
function writeErrorLog(config, rawOutput, error, stopReason) {
|
|
9365
8807
|
try {
|
|
9366
|
-
const logPath =
|
|
8808
|
+
const logPath = path24.join(process.cwd(), ".caliber", "error-log.md");
|
|
9367
8809
|
const lines = [
|
|
9368
8810
|
`# Generation Error \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
9369
8811
|
"",
|
|
@@ -9376,8 +8818,8 @@ function writeErrorLog(config, rawOutput, error, stopReason) {
|
|
|
9376
8818
|
lines.push("## Error", "```", error, "```", "");
|
|
9377
8819
|
}
|
|
9378
8820
|
lines.push("## Raw LLM Output", "```", rawOutput || "(empty)", "```");
|
|
9379
|
-
|
|
9380
|
-
|
|
8821
|
+
fs30.mkdirSync(path24.join(process.cwd(), ".caliber"), { recursive: true });
|
|
8822
|
+
fs30.writeFileSync(logPath, lines.join("\n"));
|
|
9381
8823
|
console.log(chalk13.dim(`
|
|
9382
8824
|
Error log written to .caliber/error-log.md`));
|
|
9383
8825
|
} catch {
|
|
@@ -9428,13 +8870,13 @@ ${JSON.stringify(checkList, null, 2)}`,
|
|
|
9428
8870
|
}
|
|
9429
8871
|
|
|
9430
8872
|
// src/scoring/history.ts
|
|
9431
|
-
import
|
|
9432
|
-
import
|
|
8873
|
+
import fs31 from "fs";
|
|
8874
|
+
import path25 from "path";
|
|
9433
8875
|
var HISTORY_FILE = "score-history.jsonl";
|
|
9434
8876
|
var MAX_ENTRIES = 500;
|
|
9435
8877
|
var TRIM_THRESHOLD = MAX_ENTRIES + 50;
|
|
9436
8878
|
function historyFilePath() {
|
|
9437
|
-
return
|
|
8879
|
+
return path25.join(CALIBER_DIR, HISTORY_FILE);
|
|
9438
8880
|
}
|
|
9439
8881
|
function recordScore(result, trigger) {
|
|
9440
8882
|
const entry = {
|
|
@@ -9445,14 +8887,14 @@ function recordScore(result, trigger) {
|
|
|
9445
8887
|
trigger
|
|
9446
8888
|
};
|
|
9447
8889
|
try {
|
|
9448
|
-
|
|
8890
|
+
fs31.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
9449
8891
|
const filePath = historyFilePath();
|
|
9450
|
-
|
|
9451
|
-
const stat =
|
|
8892
|
+
fs31.appendFileSync(filePath, JSON.stringify(entry) + "\n");
|
|
8893
|
+
const stat = fs31.statSync(filePath);
|
|
9452
8894
|
if (stat.size > TRIM_THRESHOLD * 120) {
|
|
9453
|
-
const lines =
|
|
8895
|
+
const lines = fs31.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
9454
8896
|
if (lines.length > MAX_ENTRIES) {
|
|
9455
|
-
|
|
8897
|
+
fs31.writeFileSync(filePath, lines.slice(-MAX_ENTRIES).join("\n") + "\n");
|
|
9456
8898
|
}
|
|
9457
8899
|
}
|
|
9458
8900
|
} catch {
|
|
@@ -9461,7 +8903,7 @@ function recordScore(result, trigger) {
|
|
|
9461
8903
|
function readScoreHistory() {
|
|
9462
8904
|
const filePath = historyFilePath();
|
|
9463
8905
|
try {
|
|
9464
|
-
const content =
|
|
8906
|
+
const content = fs31.readFileSync(filePath, "utf-8");
|
|
9465
8907
|
const entries = [];
|
|
9466
8908
|
for (const line of content.split("\n")) {
|
|
9467
8909
|
if (!line) continue;
|
|
@@ -9499,73 +8941,49 @@ async function initCommand(options) {
|
|
|
9499
8941
|
const bin = resolveCaliber();
|
|
9500
8942
|
const firstRun = isFirstRun(process.cwd());
|
|
9501
8943
|
if (firstRun) {
|
|
9502
|
-
console.log(
|
|
8944
|
+
console.log(
|
|
8945
|
+
brand.bold(`
|
|
9503
8946
|
\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
|
|
9504
8947
|
\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
|
|
9505
8948
|
\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
|
|
9506
8949
|
\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
|
|
9507
8950
|
\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
|
|
9508
8951
|
\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
|
|
9509
|
-
`)
|
|
9510
|
-
|
|
9511
|
-
console.log(chalk14.dim("
|
|
8952
|
+
`)
|
|
8953
|
+
);
|
|
8954
|
+
console.log(chalk14.dim(" Scan your project and generate tailored config files for"));
|
|
8955
|
+
console.log(chalk14.dim(" Claude Code, Cursor, Codex, and GitHub Copilot.\n"));
|
|
9512
8956
|
console.log(title.bold(" How it works:\n"));
|
|
9513
|
-
console.log(chalk14.dim(" 1. Connect
|
|
9514
|
-
console.log(chalk14.dim(" 2.
|
|
9515
|
-
console.log(chalk14.dim(" 3.
|
|
8957
|
+
console.log(chalk14.dim(" 1. Connect Link your LLM provider and select your agents"));
|
|
8958
|
+
console.log(chalk14.dim(" 2. Engine Detect stack, generate configs & skills in parallel"));
|
|
8959
|
+
console.log(chalk14.dim(" 3. Review See all changes \u2014 accept, refine, or decline"));
|
|
8960
|
+
console.log(chalk14.dim(" 4. Finalize Score check and auto-sync hooks\n"));
|
|
9516
8961
|
} else {
|
|
9517
|
-
console.log(brand.bold("\n CALIBER") + chalk14.dim(" \u2014
|
|
8962
|
+
console.log(brand.bold("\n CALIBER") + chalk14.dim(" \u2014 regenerating config\n"));
|
|
9518
8963
|
}
|
|
9519
8964
|
const platforms = detectPlatforms();
|
|
9520
8965
|
if (!platforms.claude && !platforms.cursor && !platforms.codex) {
|
|
9521
8966
|
console.log(chalk14.yellow(" \u26A0 No supported AI platforms detected (Claude, Cursor, Codex)."));
|
|
9522
|
-
console.log(
|
|
8967
|
+
console.log(
|
|
8968
|
+
chalk14.yellow(
|
|
8969
|
+
" Caliber will still generate config files, but they won't be auto-installed.\n"
|
|
8970
|
+
)
|
|
8971
|
+
);
|
|
9523
8972
|
}
|
|
9524
8973
|
const report = options.debugReport ? new DebugReport() : null;
|
|
9525
|
-
console.log(title.bold(" Step 1/
|
|
8974
|
+
console.log(title.bold(" Step 1/4 \u2014 Connect\n"));
|
|
9526
8975
|
let config = loadConfig();
|
|
9527
|
-
if (!config && !options.autoApprove) {
|
|
9528
|
-
if (isClaudeCliAvailable()) {
|
|
9529
|
-
console.log(chalk14.dim(" Detected: Claude Code CLI (uses your Pro/Max/Team subscription)\n"));
|
|
9530
|
-
const useIt = await confirm2({ message: "Use Claude Code as your LLM provider?" });
|
|
9531
|
-
if (useIt) {
|
|
9532
|
-
const autoConfig = { provider: "claude-cli", model: "default" };
|
|
9533
|
-
writeConfigFile(autoConfig);
|
|
9534
|
-
config = autoConfig;
|
|
9535
|
-
}
|
|
9536
|
-
} else if (isCursorAgentAvailable() && isCursorLoggedIn()) {
|
|
9537
|
-
console.log(chalk14.dim(" Detected: Cursor (uses your existing subscription)\n"));
|
|
9538
|
-
const useIt = await confirm2({ message: "Use Cursor as your LLM provider?" });
|
|
9539
|
-
if (useIt) {
|
|
9540
|
-
const autoConfig = { provider: "cursor", model: "sonnet-4.6" };
|
|
9541
|
-
writeConfigFile(autoConfig);
|
|
9542
|
-
config = autoConfig;
|
|
9543
|
-
}
|
|
9544
|
-
}
|
|
9545
|
-
}
|
|
9546
8976
|
if (!config) {
|
|
9547
|
-
|
|
9548
|
-
|
|
9549
|
-
|
|
9550
|
-
|
|
9551
|
-
|
|
9552
|
-
} else if (isCursorAgentAvailable() && isCursorLoggedIn()) {
|
|
9553
|
-
const autoConfig = { provider: "cursor", model: "sonnet-4.6" };
|
|
9554
|
-
writeConfigFile(autoConfig);
|
|
9555
|
-
config = autoConfig;
|
|
9556
|
-
}
|
|
9557
|
-
}
|
|
8977
|
+
console.log(chalk14.dim(" No LLM provider configured yet.\n"));
|
|
8978
|
+
await runInteractiveProviderSetup({
|
|
8979
|
+
selectMessage: "How do you want to use Caliber? (choose LLM provider)"
|
|
8980
|
+
});
|
|
8981
|
+
config = loadConfig();
|
|
9558
8982
|
if (!config) {
|
|
9559
|
-
console.log(chalk14.
|
|
9560
|
-
|
|
9561
|
-
selectMessage: "How do you want to use Caliber? (choose LLM provider)"
|
|
9562
|
-
});
|
|
9563
|
-
config = loadConfig();
|
|
9564
|
-
if (!config) {
|
|
9565
|
-
console.log(chalk14.red(" Configuration cancelled or failed.\n"));
|
|
9566
|
-
throw new Error("__exit__");
|
|
9567
|
-
}
|
|
8983
|
+
console.log(chalk14.red(" Configuration cancelled or failed.\n"));
|
|
8984
|
+
throw new Error("__exit__");
|
|
9568
8985
|
}
|
|
8986
|
+
console.log(chalk14.green(" \u2713 Provider saved\n"));
|
|
9569
8987
|
}
|
|
9570
8988
|
trackInitProviderSelected(config.provider, config.model, firstRun);
|
|
9571
8989
|
const displayModel = getDisplayModel(config);
|
|
@@ -9574,67 +8992,51 @@ async function initCommand(options) {
|
|
|
9574
8992
|
console.log(chalk14.dim(modelLine + "\n"));
|
|
9575
8993
|
if (report) {
|
|
9576
8994
|
report.markStep("Provider connection");
|
|
9577
|
-
report.addSection(
|
|
8995
|
+
report.addSection(
|
|
8996
|
+
"LLM Provider",
|
|
8997
|
+
`- **Provider**: ${config.provider}
|
|
9578
8998
|
- **Model**: ${displayModel}
|
|
9579
|
-
- **Fast model**: ${fastModel || "none"}`
|
|
8999
|
+
- **Fast model**: ${fastModel || "none"}`
|
|
9000
|
+
);
|
|
9580
9001
|
}
|
|
9581
9002
|
await validateModel({ fast: true });
|
|
9582
9003
|
let targetAgent;
|
|
9583
9004
|
const agentAutoDetected = !options.agent;
|
|
9584
9005
|
if (options.agent) {
|
|
9585
9006
|
targetAgent = options.agent;
|
|
9007
|
+
} else if (options.autoApprove) {
|
|
9008
|
+
targetAgent = ["claude"];
|
|
9009
|
+
log(options.verbose, "Auto-approve: defaulting to claude agent");
|
|
9586
9010
|
} else {
|
|
9587
9011
|
const detected = detectAgents(process.cwd());
|
|
9588
|
-
|
|
9589
|
-
targetAgent = detected;
|
|
9590
|
-
console.log(chalk14.dim(` Coding agents in this repo: ${detected.join(", ")}
|
|
9591
|
-
`));
|
|
9592
|
-
} else if (detected.length > 0) {
|
|
9593
|
-
console.log(chalk14.dim(` Coding agents in this repo: ${detected.join(", ")}
|
|
9594
|
-
`));
|
|
9595
|
-
const useDetected = await confirm2({ message: "Generate configs for these agents?" });
|
|
9596
|
-
targetAgent = useDetected ? detected : await promptAgent();
|
|
9597
|
-
} else {
|
|
9598
|
-
targetAgent = options.autoApprove ? ["claude"] : await promptAgent();
|
|
9599
|
-
}
|
|
9012
|
+
targetAgent = await promptAgent(detected.length > 0 ? detected : void 0);
|
|
9600
9013
|
}
|
|
9601
9014
|
console.log(chalk14.dim(` Target: ${targetAgent.join(", ")}
|
|
9602
9015
|
`));
|
|
9603
9016
|
trackInitAgentSelected(targetAgent, agentAutoDetected);
|
|
9604
|
-
console.log(title.bold(" Step 2/3 \u2014 Build\n"));
|
|
9605
|
-
const hookResult = installPreCommitHook();
|
|
9606
|
-
if (hookResult.installed) {
|
|
9607
|
-
console.log(` ${chalk14.green("\u2713")} Pre-commit hook installed`);
|
|
9608
|
-
} else if (hookResult.alreadyInstalled) {
|
|
9609
|
-
console.log(` ${chalk14.green("\u2713")} Pre-commit hook \u2014 active`);
|
|
9610
|
-
}
|
|
9611
|
-
const { ensureBuiltinSkills: ensureBuiltinSkills2 } = await Promise.resolve().then(() => (init_builtin_skills(), builtin_skills_exports));
|
|
9612
|
-
for (const agent of targetAgent) {
|
|
9613
|
-
if (agent === "claude" && !fs33.existsSync(".claude")) fs33.mkdirSync(".claude", { recursive: true });
|
|
9614
|
-
if (agent === "cursor" && !fs33.existsSync(".cursor")) fs33.mkdirSync(".cursor", { recursive: true });
|
|
9615
|
-
if (agent === "codex" && !fs33.existsSync(".agents")) fs33.mkdirSync(".agents", { recursive: true });
|
|
9616
|
-
}
|
|
9617
|
-
const skillsWritten = ensureBuiltinSkills2();
|
|
9618
|
-
if (skillsWritten.length > 0) {
|
|
9619
|
-
console.log(` ${chalk14.green("\u2713")} Agent skills installed`);
|
|
9620
|
-
}
|
|
9621
|
-
const hasLearnableAgent = targetAgent.includes("claude") || targetAgent.includes("cursor");
|
|
9622
|
-
if (hasLearnableAgent) {
|
|
9623
|
-
if (targetAgent.includes("claude")) installLearningHooks();
|
|
9624
|
-
if (targetAgent.includes("cursor")) installCursorLearningHooks();
|
|
9625
|
-
console.log(` ${chalk14.green("\u2713")} Session learning enabled`);
|
|
9626
|
-
trackInitLearnEnabled(true);
|
|
9627
|
-
}
|
|
9628
|
-
console.log("");
|
|
9629
9017
|
const baselineScore = computeLocalScore(process.cwd(), targetAgent);
|
|
9630
|
-
log(
|
|
9018
|
+
console.log(chalk14.dim("\n Current config score:"));
|
|
9019
|
+
displayScoreSummary(baselineScore);
|
|
9020
|
+
if (options.verbose) {
|
|
9021
|
+
for (const c of baselineScore.checks) {
|
|
9022
|
+
log(
|
|
9023
|
+
options.verbose,
|
|
9024
|
+
` ${c.passed ? "\u2713" : "\u2717"} ${c.name}: ${c.earnedPoints}/${c.maxPoints}${c.suggestion ? ` \u2014 ${c.suggestion}` : ""}`
|
|
9025
|
+
);
|
|
9026
|
+
}
|
|
9027
|
+
}
|
|
9631
9028
|
if (report) {
|
|
9632
9029
|
report.markStep("Baseline scoring");
|
|
9633
|
-
report.addSection(
|
|
9030
|
+
report.addSection(
|
|
9031
|
+
"Scoring: Baseline",
|
|
9032
|
+
`**Score**: ${baselineScore.score}/100
|
|
9634
9033
|
|
|
9635
9034
|
| Check | Passed | Points | Max |
|
|
9636
9035
|
|-------|--------|--------|-----|
|
|
9637
|
-
` + baselineScore.checks.map(
|
|
9036
|
+
` + baselineScore.checks.map(
|
|
9037
|
+
(c) => `| ${c.name} | ${c.passed ? "Yes" : "No"} | ${c.earnedPoints} | ${c.maxPoints} |`
|
|
9038
|
+
).join("\n")
|
|
9039
|
+
);
|
|
9638
9040
|
report.addSection("Generation: Target Agents", targetAgent.join(", "));
|
|
9639
9041
|
}
|
|
9640
9042
|
const hasExistingConfig = !!(baselineScore.checks.some((c) => c.id === "claude_md_exists" && c.passed) || baselineScore.checks.some((c) => c.id === "cursorrules_exists" && c.passed));
|
|
@@ -9648,77 +9050,33 @@ async function initCommand(options) {
|
|
|
9648
9050
|
]);
|
|
9649
9051
|
const passingCount = baselineScore.checks.filter((c) => c.passed).length;
|
|
9650
9052
|
const failingCount = baselineScore.checks.filter((c) => !c.passed).length;
|
|
9053
|
+
if (hasExistingConfig && baselineScore.score === 100) {
|
|
9054
|
+
trackInitScoreComputed(baselineScore.score, passingCount, failingCount, true);
|
|
9055
|
+
console.log(chalk14.bold.green(" Your config is already optimal \u2014 nothing to change.\n"));
|
|
9056
|
+
console.log(
|
|
9057
|
+
chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} init --force`) + chalk14.dim(" to regenerate anyway.\n")
|
|
9058
|
+
);
|
|
9059
|
+
if (!options.force) return;
|
|
9060
|
+
}
|
|
9061
|
+
const allFailingChecks = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0);
|
|
9062
|
+
const llmFixableChecks = allFailingChecks.filter((c) => !NON_LLM_CHECKS.has(c.id));
|
|
9651
9063
|
trackInitScoreComputed(baselineScore.score, passingCount, failingCount, false);
|
|
9652
|
-
|
|
9653
|
-
|
|
9654
|
-
|
|
9655
|
-
|
|
9656
|
-
|
|
9657
|
-
|
|
9658
|
-
|
|
9659
|
-
skipGeneration = !improveAnswer;
|
|
9660
|
-
}
|
|
9661
|
-
if (skipGeneration) {
|
|
9662
|
-
const {
|
|
9663
|
-
appendManagedBlocks: appendManagedBlocks2,
|
|
9664
|
-
getCursorPreCommitRule: getCursorPreCommitRule2,
|
|
9665
|
-
getCursorLearningsRule: getCursorLearningsRule2,
|
|
9666
|
-
getCursorSyncRule: getCursorSyncRule2,
|
|
9667
|
-
getCursorSetupRule: getCursorSetupRule2
|
|
9668
|
-
} = await Promise.resolve().then(() => (init_pre_commit_block(), pre_commit_block_exports));
|
|
9669
|
-
const claudeMdPath = "CLAUDE.md";
|
|
9670
|
-
let claudeContent = "";
|
|
9671
|
-
try {
|
|
9672
|
-
claudeContent = fs33.readFileSync(claudeMdPath, "utf-8");
|
|
9673
|
-
} catch {
|
|
9674
|
-
}
|
|
9675
|
-
if (!claudeContent) {
|
|
9676
|
-
claudeContent = `# ${path27.basename(process.cwd())}
|
|
9677
|
-
`;
|
|
9678
|
-
}
|
|
9679
|
-
const updatedClaude = appendManagedBlocks2(claudeContent, "claude");
|
|
9680
|
-
if (updatedClaude !== claudeContent || !fs33.existsSync(claudeMdPath)) {
|
|
9681
|
-
fs33.writeFileSync(claudeMdPath, updatedClaude);
|
|
9682
|
-
console.log(` ${chalk14.green("\u2713")} CLAUDE.md \u2014 added Caliber sync instructions`);
|
|
9683
|
-
}
|
|
9684
|
-
if (targetAgent.includes("cursor")) {
|
|
9685
|
-
const rulesDir = path27.join(".cursor", "rules");
|
|
9686
|
-
if (!fs33.existsSync(rulesDir)) fs33.mkdirSync(rulesDir, { recursive: true });
|
|
9687
|
-
for (const rule of [getCursorPreCommitRule2(), getCursorLearningsRule2(), getCursorSyncRule2(), getCursorSetupRule2()]) {
|
|
9688
|
-
fs33.writeFileSync(path27.join(rulesDir, rule.filename), rule.content);
|
|
9689
|
-
}
|
|
9690
|
-
console.log(` ${chalk14.green("\u2713")} Cursor rules \u2014 added Caliber sync rules`);
|
|
9691
|
-
}
|
|
9692
|
-
if (targetAgent.includes("github-copilot")) {
|
|
9693
|
-
const copilotPath = path27.join(".github", "copilot-instructions.md");
|
|
9694
|
-
let copilotContent = "";
|
|
9695
|
-
try {
|
|
9696
|
-
copilotContent = fs33.readFileSync(copilotPath, "utf-8");
|
|
9697
|
-
} catch {
|
|
9698
|
-
}
|
|
9699
|
-
if (!copilotContent) {
|
|
9700
|
-
fs33.mkdirSync(".github", { recursive: true });
|
|
9701
|
-
copilotContent = `# ${path27.basename(process.cwd())}
|
|
9702
|
-
`;
|
|
9703
|
-
}
|
|
9704
|
-
const updatedCopilot = appendManagedBlocks2(copilotContent, "copilot");
|
|
9705
|
-
if (updatedCopilot !== copilotContent) {
|
|
9706
|
-
fs33.writeFileSync(copilotPath, updatedCopilot);
|
|
9707
|
-
console.log(` ${chalk14.green("\u2713")} Copilot instructions \u2014 added Caliber sync instructions`);
|
|
9064
|
+
if (hasExistingConfig && llmFixableChecks.length === 0 && allFailingChecks.length > 0 && !options.force) {
|
|
9065
|
+
console.log(chalk14.bold.green("\n Your config is fully optimized for LLM generation.\n"));
|
|
9066
|
+
console.log(chalk14.dim(" Remaining items need CLI actions:\n"));
|
|
9067
|
+
for (const check of allFailingChecks) {
|
|
9068
|
+
console.log(chalk14.dim(` \u2022 ${check.name}`));
|
|
9069
|
+
if (check.suggestion) {
|
|
9070
|
+
console.log(` ${chalk14.hex("#83D1EB")(check.suggestion)}`);
|
|
9708
9071
|
}
|
|
9709
9072
|
}
|
|
9710
|
-
|
|
9711
|
-
|
|
9712
|
-
|
|
9713
|
-
|
|
9714
|
-
targetAgent
|
|
9715
|
-
});
|
|
9716
|
-
trackInitCompleted("sync-only", baselineScore.score);
|
|
9717
|
-
console.log(chalk14.bold.green("\n Caliber sync is set up!\n"));
|
|
9718
|
-
console.log(chalk14.dim(" Your agent configs will sync automatically on every commit."));
|
|
9719
|
-
console.log(chalk14.dim(" Run ") + title(`${bin} init --force`) + chalk14.dim(" anytime to generate or improve configs.\n"));
|
|
9073
|
+
console.log("");
|
|
9074
|
+
console.log(
|
|
9075
|
+
chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} init --force`) + chalk14.dim(" to regenerate anyway.\n")
|
|
9076
|
+
);
|
|
9720
9077
|
return;
|
|
9721
9078
|
}
|
|
9079
|
+
console.log(title.bold(" Step 2/4 \u2014 Engine\n"));
|
|
9722
9080
|
const genModelInfo = fastModel ? ` Using ${displayModel} for docs, ${fastModel} for skills` : ` Using ${displayModel}`;
|
|
9723
9081
|
console.log(chalk14.dim(genModelInfo + "\n"));
|
|
9724
9082
|
if (report) report.markStep("Generation");
|
|
@@ -9734,8 +9092,14 @@ async function initCommand(options) {
|
|
|
9734
9092
|
const TASK_STACK = display.add("Detecting project stack", { pipelineLabel: "Scan" });
|
|
9735
9093
|
const TASK_CONFIG = display.add("Generating configs", { depth: 1, pipelineLabel: "Generate" });
|
|
9736
9094
|
const TASK_SKILLS_GEN = display.add("Generating skills", { depth: 2, pipelineLabel: "Skills" });
|
|
9737
|
-
const TASK_SKILLS_SEARCH = display.add("Searching community skills", {
|
|
9738
|
-
|
|
9095
|
+
const TASK_SKILLS_SEARCH = display.add("Searching community skills", {
|
|
9096
|
+
depth: 1,
|
|
9097
|
+
pipelineLabel: "Search",
|
|
9098
|
+
pipelineRow: 1
|
|
9099
|
+
});
|
|
9100
|
+
const TASK_SCORE_REFINE = display.add("Validating & refining config", {
|
|
9101
|
+
pipelineLabel: "Validate"
|
|
9102
|
+
});
|
|
9739
9103
|
display.start();
|
|
9740
9104
|
display.enableWaitingContent();
|
|
9741
9105
|
try {
|
|
@@ -9745,21 +9109,39 @@ async function initCommand(options) {
|
|
|
9745
9109
|
const stackSummary = stackParts.join(", ") || "no languages";
|
|
9746
9110
|
const largeRepoNote = fingerprint.fileTree.length > 5e3 ? ` (${fingerprint.fileTree.length.toLocaleString()} files, smart sampling active)` : "";
|
|
9747
9111
|
display.update(TASK_STACK, "done", stackSummary + largeRepoNote);
|
|
9748
|
-
trackInitProjectDiscovered(
|
|
9749
|
-
|
|
9112
|
+
trackInitProjectDiscovered(
|
|
9113
|
+
fingerprint.languages.length,
|
|
9114
|
+
fingerprint.frameworks.length,
|
|
9115
|
+
fingerprint.fileTree.length
|
|
9116
|
+
);
|
|
9117
|
+
log(
|
|
9118
|
+
options.verbose,
|
|
9119
|
+
`Fingerprint: ${fingerprint.languages.length} languages, ${fingerprint.frameworks.length} frameworks, ${fingerprint.fileTree.length} files`
|
|
9120
|
+
);
|
|
9750
9121
|
const cliSources = options.source || [];
|
|
9751
9122
|
const workspaces = getDetectedWorkspaces(process.cwd());
|
|
9752
9123
|
const sources2 = resolveAllSources(process.cwd(), cliSources, workspaces);
|
|
9753
9124
|
if (sources2.length > 0) {
|
|
9754
9125
|
fingerprint.sources = sources2;
|
|
9755
|
-
log(
|
|
9126
|
+
log(
|
|
9127
|
+
options.verbose,
|
|
9128
|
+
`Sources: ${sources2.length} resolved (${sources2.map((s) => s.name).join(", ")})`
|
|
9129
|
+
);
|
|
9756
9130
|
}
|
|
9757
9131
|
if (report) {
|
|
9758
|
-
report.addJson("Fingerprint: Git", {
|
|
9132
|
+
report.addJson("Fingerprint: Git", {
|
|
9133
|
+
remote: fingerprint.gitRemoteUrl,
|
|
9134
|
+
packageName: fingerprint.packageName
|
|
9135
|
+
});
|
|
9759
9136
|
report.addCodeBlock("Fingerprint: File Tree", fingerprint.fileTree.join("\n"));
|
|
9760
|
-
report.addJson("Fingerprint: Detected Stack", {
|
|
9137
|
+
report.addJson("Fingerprint: Detected Stack", {
|
|
9138
|
+
languages: fingerprint.languages,
|
|
9139
|
+
frameworks: fingerprint.frameworks,
|
|
9140
|
+
tools: fingerprint.tools
|
|
9141
|
+
});
|
|
9761
9142
|
report.addJson("Fingerprint: Existing Configs", fingerprint.existingConfigs);
|
|
9762
|
-
if (fingerprint.codeAnalysis)
|
|
9143
|
+
if (fingerprint.codeAnalysis)
|
|
9144
|
+
report.addJson("Fingerprint: Code Analysis", fingerprint.codeAnalysis);
|
|
9763
9145
|
}
|
|
9764
9146
|
const isEmpty = fingerprint.fileTree.length < 3;
|
|
9765
9147
|
if (isEmpty) {
|
|
@@ -9790,13 +9172,26 @@ async function initCommand(options) {
|
|
|
9790
9172
|
let passingChecks;
|
|
9791
9173
|
let currentScore;
|
|
9792
9174
|
if (hasExistingConfig && localBaseline.score >= 95 && !options.force) {
|
|
9793
|
-
const currentLlmFixable = localBaseline.checks.filter(
|
|
9794
|
-
|
|
9175
|
+
const currentLlmFixable = localBaseline.checks.filter(
|
|
9176
|
+
(c) => !c.passed && c.maxPoints > 0 && !NON_LLM_CHECKS.has(c.id)
|
|
9177
|
+
);
|
|
9178
|
+
failingChecks = currentLlmFixable.map((c) => ({
|
|
9179
|
+
name: c.name,
|
|
9180
|
+
suggestion: c.suggestion,
|
|
9181
|
+
fix: c.fix
|
|
9182
|
+
}));
|
|
9795
9183
|
passingChecks = localBaseline.checks.filter((c) => c.passed).map((c) => ({ name: c.name }));
|
|
9796
9184
|
currentScore = localBaseline.score;
|
|
9797
9185
|
}
|
|
9798
9186
|
if (report) {
|
|
9799
|
-
const fullPrompt = buildGeneratePrompt(
|
|
9187
|
+
const fullPrompt = buildGeneratePrompt(
|
|
9188
|
+
fingerprint,
|
|
9189
|
+
targetAgent,
|
|
9190
|
+
fingerprint.description,
|
|
9191
|
+
failingChecks,
|
|
9192
|
+
currentScore,
|
|
9193
|
+
passingChecks
|
|
9194
|
+
);
|
|
9800
9195
|
report.addCodeBlock("Generation: Full LLM Prompt", fullPrompt);
|
|
9801
9196
|
}
|
|
9802
9197
|
const result = await generateSetup(
|
|
@@ -9916,8 +9311,11 @@ async function initCommand(options) {
|
|
|
9916
9311
|
if (rawOutput) report.addCodeBlock("Generation: Raw LLM Response", rawOutput);
|
|
9917
9312
|
report.addJson("Generation: Parsed Config", generatedSetup);
|
|
9918
9313
|
}
|
|
9919
|
-
log(
|
|
9920
|
-
|
|
9314
|
+
log(
|
|
9315
|
+
options.verbose,
|
|
9316
|
+
`Generation completed: ${elapsedMs}ms, stopReason: ${genStopReason || "end_turn"}`
|
|
9317
|
+
);
|
|
9318
|
+
console.log(title.bold(" Step 3/4 \u2014 Review\n"));
|
|
9921
9319
|
const setupFiles = collectSetupFiles(generatedSetup, targetAgent);
|
|
9922
9320
|
const staged = stageFiles(setupFiles, process.cwd());
|
|
9923
9321
|
const totalChanges = staged.newFiles + staged.modifiedFiles;
|
|
@@ -9928,10 +9326,18 @@ async function initCommand(options) {
|
|
|
9928
9326
|
}
|
|
9929
9327
|
console.log("");
|
|
9930
9328
|
}
|
|
9931
|
-
console.log(
|
|
9329
|
+
console.log(
|
|
9330
|
+
chalk14.dim(
|
|
9331
|
+
` ${chalk14.green(`${staged.newFiles} new`)} / ${chalk14.yellow(`${staged.modifiedFiles} modified`)} file${totalChanges !== 1 ? "s" : ""}`
|
|
9332
|
+
)
|
|
9333
|
+
);
|
|
9932
9334
|
if (skillSearchResult.results.length > 0) {
|
|
9933
|
-
console.log(
|
|
9934
|
-
|
|
9335
|
+
console.log(
|
|
9336
|
+
chalk14.dim(
|
|
9337
|
+
` ${chalk14.cyan(`${skillSearchResult.results.length}`)} community skills available to install
|
|
9338
|
+
`
|
|
9339
|
+
)
|
|
9340
|
+
);
|
|
9935
9341
|
} else {
|
|
9936
9342
|
console.log("");
|
|
9937
9343
|
}
|
|
@@ -9967,8 +9373,12 @@ async function initCommand(options) {
|
|
|
9967
9373
|
}
|
|
9968
9374
|
const updatedFiles = collectSetupFiles(generatedSetup, targetAgent);
|
|
9969
9375
|
const restaged = stageFiles(updatedFiles, process.cwd());
|
|
9970
|
-
console.log(
|
|
9971
|
-
|
|
9376
|
+
console.log(
|
|
9377
|
+
chalk14.dim(
|
|
9378
|
+
` ${chalk14.green(`${restaged.newFiles} new`)} / ${chalk14.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
|
|
9379
|
+
`
|
|
9380
|
+
)
|
|
9381
|
+
);
|
|
9972
9382
|
printSetupSummary(generatedSetup);
|
|
9973
9383
|
const { openReview: openRev } = await Promise.resolve().then(() => (init_review(), review_exports));
|
|
9974
9384
|
await openRev("terminal", restaged.stagedFiles);
|
|
@@ -9980,6 +9390,7 @@ async function initCommand(options) {
|
|
|
9980
9390
|
console.log(chalk14.dim("Declined. No files were modified."));
|
|
9981
9391
|
return;
|
|
9982
9392
|
}
|
|
9393
|
+
console.log(title.bold("\n Step 4/4 \u2014 Finalize\n"));
|
|
9983
9394
|
if (options.dryRun) {
|
|
9984
9395
|
console.log(chalk14.yellow("\n[Dry run] Would write the following files:"));
|
|
9985
9396
|
console.log(JSON.stringify(generatedSetup, null, 2));
|
|
@@ -9988,13 +9399,14 @@ async function initCommand(options) {
|
|
|
9988
9399
|
const { default: ora9 } = await import("ora");
|
|
9989
9400
|
const writeSpinner = ora9("Writing config files...").start();
|
|
9990
9401
|
try {
|
|
9991
|
-
if (targetAgent.includes("codex") && !
|
|
9402
|
+
if (targetAgent.includes("codex") && !fs32.existsSync("AGENTS.md") && !generatedSetup.codex) {
|
|
9992
9403
|
const claude = generatedSetup.claude;
|
|
9993
9404
|
const cursor = generatedSetup.cursor;
|
|
9994
9405
|
const agentRefs = [];
|
|
9995
9406
|
if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
|
|
9996
9407
|
if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
|
|
9997
|
-
if (agentRefs.length === 0)
|
|
9408
|
+
if (agentRefs.length === 0)
|
|
9409
|
+
agentRefs.push("See CLAUDE.md and .cursor/rules/ for agent configurations.");
|
|
9998
9410
|
const stubContent = `# AGENTS.md
|
|
9999
9411
|
|
|
10000
9412
|
This project uses AI coding agents configured by [Caliber](https://github.com/caliber-ai-org/ai-setup).
|
|
@@ -10041,32 +9453,49 @@ ${agentRefs.join(" ")}
|
|
|
10041
9453
|
if (afterScore.score < baselineScore.score) {
|
|
10042
9454
|
trackInitScoreRegression(baselineScore.score, afterScore.score);
|
|
10043
9455
|
console.log("");
|
|
10044
|
-
console.log(
|
|
9456
|
+
console.log(
|
|
9457
|
+
chalk14.yellow(
|
|
9458
|
+
` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`
|
|
9459
|
+
)
|
|
9460
|
+
);
|
|
10045
9461
|
try {
|
|
10046
9462
|
const { restored, removed } = undoSetup();
|
|
10047
9463
|
if (restored.length > 0 || removed.length > 0) {
|
|
10048
|
-
console.log(
|
|
9464
|
+
console.log(
|
|
9465
|
+
chalk14.dim(
|
|
9466
|
+
` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`
|
|
9467
|
+
)
|
|
9468
|
+
);
|
|
10049
9469
|
}
|
|
10050
9470
|
} catch {
|
|
10051
9471
|
}
|
|
10052
|
-
console.log(
|
|
9472
|
+
console.log(
|
|
9473
|
+
chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} init --force`) + chalk14.dim(" to override.\n")
|
|
9474
|
+
);
|
|
10053
9475
|
return;
|
|
10054
9476
|
}
|
|
10055
9477
|
if (report) {
|
|
10056
9478
|
report.markStep("Post-write scoring");
|
|
10057
|
-
report.addSection(
|
|
9479
|
+
report.addSection(
|
|
9480
|
+
"Scoring: Post-Write",
|
|
9481
|
+
`**Score**: ${afterScore.score}/100 (delta: ${afterScore.score - baselineScore.score >= 0 ? "+" : ""}${afterScore.score - baselineScore.score})
|
|
10058
9482
|
|
|
10059
9483
|
| Check | Passed | Points | Max |
|
|
10060
9484
|
|-------|--------|--------|-----|
|
|
10061
|
-
` + afterScore.checks.map(
|
|
9485
|
+
` + afterScore.checks.map(
|
|
9486
|
+
(c) => `| ${c.name} | ${c.passed ? "Yes" : "No"} | ${c.earnedPoints} | ${c.maxPoints} |`
|
|
9487
|
+
).join("\n")
|
|
9488
|
+
);
|
|
10062
9489
|
}
|
|
10063
9490
|
recordScore(afterScore, "init");
|
|
10064
|
-
trackInitCompleted("full-generation", afterScore.score);
|
|
10065
9491
|
displayScoreDelta(baselineScore, afterScore);
|
|
10066
9492
|
if (options.verbose) {
|
|
10067
9493
|
log(options.verbose, `Final score: ${afterScore.score}/100`);
|
|
10068
9494
|
for (const c of afterScore.checks.filter((ch) => !ch.passed)) {
|
|
10069
|
-
log(
|
|
9495
|
+
log(
|
|
9496
|
+
options.verbose,
|
|
9497
|
+
` Still failing: ${c.name} (${c.earnedPoints}/${c.maxPoints})${c.suggestion ? ` \u2014 ${c.suggestion}` : ""}`
|
|
9498
|
+
);
|
|
10070
9499
|
}
|
|
10071
9500
|
}
|
|
10072
9501
|
let communitySkillsInstalled = 0;
|
|
@@ -10079,36 +9508,99 @@ ${agentRefs.join(" ")}
|
|
|
10079
9508
|
communitySkillsInstalled = selected.length;
|
|
10080
9509
|
}
|
|
10081
9510
|
}
|
|
9511
|
+
console.log("");
|
|
9512
|
+
console.log(
|
|
9513
|
+
` ${chalk14.green("\u2713")} Docs auto-refresh ${chalk14.dim(`agents run ${resolveCaliber()} refresh before commits`)}`
|
|
9514
|
+
);
|
|
10082
9515
|
trackInitHookSelected("config-instructions");
|
|
9516
|
+
const hasLearnableAgent = targetAgent.includes("claude") || targetAgent.includes("cursor");
|
|
9517
|
+
let enableLearn = false;
|
|
9518
|
+
if (hasLearnableAgent) {
|
|
9519
|
+
if (!options.autoApprove) {
|
|
9520
|
+
enableLearn = await promptLearnInstall(targetAgent);
|
|
9521
|
+
trackInitLearnEnabled(enableLearn);
|
|
9522
|
+
if (enableLearn) {
|
|
9523
|
+
if (targetAgent.includes("claude")) {
|
|
9524
|
+
const r = installLearningHooks();
|
|
9525
|
+
if (r.installed)
|
|
9526
|
+
console.log(` ${chalk14.green("\u2713")} Learning hooks installed for Claude Code`);
|
|
9527
|
+
else if (r.alreadyInstalled)
|
|
9528
|
+
console.log(chalk14.dim(" Claude Code learning hooks already installed"));
|
|
9529
|
+
}
|
|
9530
|
+
if (targetAgent.includes("cursor")) {
|
|
9531
|
+
const r = installCursorLearningHooks();
|
|
9532
|
+
if (r.installed) console.log(` ${chalk14.green("\u2713")} Learning hooks installed for Cursor`);
|
|
9533
|
+
else if (r.alreadyInstalled)
|
|
9534
|
+
console.log(chalk14.dim(" Cursor learning hooks already installed"));
|
|
9535
|
+
}
|
|
9536
|
+
console.log(
|
|
9537
|
+
chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} learn status`) + chalk14.dim(" to see insights")
|
|
9538
|
+
);
|
|
9539
|
+
} else {
|
|
9540
|
+
console.log(
|
|
9541
|
+
chalk14.dim(" Skipped. Run ") + chalk14.hex("#83D1EB")(`${bin} learn install`) + chalk14.dim(" later to enable.")
|
|
9542
|
+
);
|
|
9543
|
+
}
|
|
9544
|
+
} else {
|
|
9545
|
+
enableLearn = true;
|
|
9546
|
+
if (targetAgent.includes("claude")) installLearningHooks();
|
|
9547
|
+
if (targetAgent.includes("cursor")) installCursorLearningHooks();
|
|
9548
|
+
}
|
|
9549
|
+
}
|
|
9550
|
+
console.log(chalk14.bold.green("\n Configuration complete!"));
|
|
9551
|
+
console.log(
|
|
9552
|
+
chalk14.dim(" Your AI agents now understand your project's architecture, build commands,")
|
|
9553
|
+
);
|
|
9554
|
+
console.log(
|
|
9555
|
+
chalk14.dim(" testing patterns, and conventions. All changes are backed up automatically.\n")
|
|
9556
|
+
);
|
|
10083
9557
|
const done = chalk14.green("\u2713");
|
|
10084
|
-
|
|
10085
|
-
console.log(chalk14.bold(" What
|
|
10086
|
-
console.log(
|
|
10087
|
-
|
|
10088
|
-
|
|
9558
|
+
const skip = chalk14.dim("\u2013");
|
|
9559
|
+
console.log(chalk14.bold(" What was configured:\n"));
|
|
9560
|
+
console.log(
|
|
9561
|
+
` ${done} Config generated ${title(`${bin} score`)} ${chalk14.dim("for full breakdown")}`
|
|
9562
|
+
);
|
|
9563
|
+
console.log(
|
|
9564
|
+
` ${done} Docs auto-refresh ${chalk14.dim(`agents run ${bin} refresh before commits`)}`
|
|
9565
|
+
);
|
|
10089
9566
|
if (hasLearnableAgent) {
|
|
10090
|
-
|
|
9567
|
+
if (enableLearn) {
|
|
9568
|
+
console.log(
|
|
9569
|
+
` ${done} Session learning ${chalk14.dim("agent learns from your feedback")}`
|
|
9570
|
+
);
|
|
9571
|
+
} else {
|
|
9572
|
+
console.log(
|
|
9573
|
+
` ${skip} Session learning ${title(`${bin} learn install`)} to enable later`
|
|
9574
|
+
);
|
|
9575
|
+
}
|
|
10091
9576
|
}
|
|
10092
9577
|
if (communitySkillsInstalled > 0) {
|
|
10093
|
-
console.log(
|
|
10094
|
-
}
|
|
10095
|
-
|
|
10096
|
-
|
|
10097
|
-
|
|
10098
|
-
|
|
10099
|
-
console.log(
|
|
10100
|
-
console.log(
|
|
10101
|
-
|
|
9578
|
+
console.log(
|
|
9579
|
+
` ${done} Community skills ${chalk14.dim(`${communitySkillsInstalled} skill${communitySkillsInstalled > 1 ? "s" : ""} installed for your stack`)}`
|
|
9580
|
+
);
|
|
9581
|
+
} else if (skillSearchResult.results.length > 0) {
|
|
9582
|
+
console.log(` ${skip} Community skills ${chalk14.dim("available but skipped")}`);
|
|
9583
|
+
}
|
|
9584
|
+
console.log(chalk14.bold("\n Explore next:\n"));
|
|
9585
|
+
console.log(
|
|
9586
|
+
` ${title(`${bin} skills`)} Find more community skills as your codebase evolves`
|
|
9587
|
+
);
|
|
9588
|
+
console.log(
|
|
9589
|
+
` ${title(`${bin} score`)} See the full scoring breakdown with improvement tips`
|
|
9590
|
+
);
|
|
9591
|
+
console.log(` ${title(`${bin} undo`)} Revert all changes from this run`);
|
|
10102
9592
|
console.log("");
|
|
10103
9593
|
if (options.showTokens) {
|
|
10104
9594
|
displayTokenUsage();
|
|
10105
9595
|
}
|
|
10106
9596
|
if (report) {
|
|
10107
9597
|
report.markStep("Finished");
|
|
10108
|
-
const reportPath =
|
|
9598
|
+
const reportPath = path26.join(process.cwd(), ".caliber", "debug-report.md");
|
|
10109
9599
|
report.write(reportPath);
|
|
10110
|
-
console.log(
|
|
10111
|
-
`))
|
|
9600
|
+
console.log(
|
|
9601
|
+
chalk14.dim(` Debug report written to ${path26.relative(process.cwd(), reportPath)}
|
|
9602
|
+
`)
|
|
9603
|
+
);
|
|
10112
9604
|
}
|
|
10113
9605
|
}
|
|
10114
9606
|
|
|
@@ -10146,7 +9638,7 @@ function undoCommand() {
|
|
|
10146
9638
|
|
|
10147
9639
|
// src/commands/status.ts
|
|
10148
9640
|
import chalk16 from "chalk";
|
|
10149
|
-
import
|
|
9641
|
+
import fs33 from "fs";
|
|
10150
9642
|
init_config();
|
|
10151
9643
|
init_resolve_caliber();
|
|
10152
9644
|
async function statusCommand(options) {
|
|
@@ -10175,7 +9667,7 @@ async function statusCommand(options) {
|
|
|
10175
9667
|
}
|
|
10176
9668
|
console.log(` Files managed: ${chalk16.cyan(manifest.entries.length.toString())}`);
|
|
10177
9669
|
for (const entry of manifest.entries) {
|
|
10178
|
-
const exists =
|
|
9670
|
+
const exists = fs33.existsSync(entry.path);
|
|
10179
9671
|
const icon = exists ? chalk16.green("\u2713") : chalk16.red("\u2717");
|
|
10180
9672
|
console.log(` ${icon} ${entry.path} (${entry.action})`);
|
|
10181
9673
|
}
|
|
@@ -10332,9 +9824,9 @@ async function regenerateCommand(options) {
|
|
|
10332
9824
|
}
|
|
10333
9825
|
|
|
10334
9826
|
// src/commands/score.ts
|
|
10335
|
-
import
|
|
9827
|
+
import fs34 from "fs";
|
|
10336
9828
|
import os7 from "os";
|
|
10337
|
-
import
|
|
9829
|
+
import path27 from "path";
|
|
10338
9830
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
10339
9831
|
import chalk18 from "chalk";
|
|
10340
9832
|
init_resolve_caliber();
|
|
@@ -10342,7 +9834,7 @@ var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".cursorrules", "CALIBER_LEARNINGS
|
|
|
10342
9834
|
var CONFIG_DIRS = [".claude", ".cursor"];
|
|
10343
9835
|
function scoreBaseRef(ref, target) {
|
|
10344
9836
|
if (!/^[\w.\-/~^@{}]+$/.test(ref)) return null;
|
|
10345
|
-
const tmpDir =
|
|
9837
|
+
const tmpDir = fs34.mkdtempSync(path27.join(os7.tmpdir(), "caliber-compare-"));
|
|
10346
9838
|
try {
|
|
10347
9839
|
for (const file of CONFIG_FILES) {
|
|
10348
9840
|
try {
|
|
@@ -10350,7 +9842,7 @@ function scoreBaseRef(ref, target) {
|
|
|
10350
9842
|
encoding: "utf-8",
|
|
10351
9843
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10352
9844
|
});
|
|
10353
|
-
|
|
9845
|
+
fs34.writeFileSync(path27.join(tmpDir, file), content);
|
|
10354
9846
|
} catch {
|
|
10355
9847
|
}
|
|
10356
9848
|
}
|
|
@@ -10361,13 +9853,13 @@ function scoreBaseRef(ref, target) {
|
|
|
10361
9853
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10362
9854
|
}).trim().split("\n").filter(Boolean);
|
|
10363
9855
|
for (const file of files) {
|
|
10364
|
-
const filePath =
|
|
10365
|
-
|
|
9856
|
+
const filePath = path27.join(tmpDir, file);
|
|
9857
|
+
fs34.mkdirSync(path27.dirname(filePath), { recursive: true });
|
|
10366
9858
|
const content = execFileSync2("git", ["show", `${ref}:${file}`], {
|
|
10367
9859
|
encoding: "utf-8",
|
|
10368
9860
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10369
9861
|
});
|
|
10370
|
-
|
|
9862
|
+
fs34.writeFileSync(filePath, content);
|
|
10371
9863
|
}
|
|
10372
9864
|
} catch {
|
|
10373
9865
|
}
|
|
@@ -10377,7 +9869,7 @@ function scoreBaseRef(ref, target) {
|
|
|
10377
9869
|
} catch {
|
|
10378
9870
|
return null;
|
|
10379
9871
|
} finally {
|
|
10380
|
-
|
|
9872
|
+
fs34.rmSync(tmpDir, { recursive: true, force: true });
|
|
10381
9873
|
}
|
|
10382
9874
|
}
|
|
10383
9875
|
async function scoreCommand(options) {
|
|
@@ -10461,13 +9953,13 @@ async function scoreCommand(options) {
|
|
|
10461
9953
|
}
|
|
10462
9954
|
|
|
10463
9955
|
// src/commands/refresh.ts
|
|
10464
|
-
import
|
|
10465
|
-
import
|
|
9956
|
+
import fs38 from "fs";
|
|
9957
|
+
import path31 from "path";
|
|
10466
9958
|
import chalk19 from "chalk";
|
|
10467
9959
|
import ora6 from "ora";
|
|
10468
9960
|
|
|
10469
9961
|
// src/lib/git-diff.ts
|
|
10470
|
-
import { execSync as
|
|
9962
|
+
import { execSync as execSync14 } from "child_process";
|
|
10471
9963
|
var MAX_DIFF_BYTES = 1e5;
|
|
10472
9964
|
var DOC_PATTERNS = [
|
|
10473
9965
|
"CLAUDE.md",
|
|
@@ -10482,7 +9974,7 @@ function excludeArgs() {
|
|
|
10482
9974
|
}
|
|
10483
9975
|
function safeExec(cmd) {
|
|
10484
9976
|
try {
|
|
10485
|
-
return
|
|
9977
|
+
return execSync14(cmd, {
|
|
10486
9978
|
encoding: "utf-8",
|
|
10487
9979
|
stdio: ["pipe", "pipe", "pipe"],
|
|
10488
9980
|
maxBuffer: 10 * 1024 * 1024
|
|
@@ -10540,49 +10032,48 @@ function collectDiff(lastSha) {
|
|
|
10540
10032
|
}
|
|
10541
10033
|
|
|
10542
10034
|
// src/writers/refresh.ts
|
|
10543
|
-
|
|
10544
|
-
import
|
|
10545
|
-
import path29 from "path";
|
|
10035
|
+
import fs35 from "fs";
|
|
10036
|
+
import path28 from "path";
|
|
10546
10037
|
function writeRefreshDocs(docs) {
|
|
10547
10038
|
const written = [];
|
|
10548
10039
|
if (docs.claudeMd) {
|
|
10549
|
-
|
|
10040
|
+
fs35.writeFileSync("CLAUDE.md", appendLearningsBlock(appendPreCommitBlock(docs.claudeMd)));
|
|
10550
10041
|
written.push("CLAUDE.md");
|
|
10551
10042
|
}
|
|
10552
10043
|
if (docs.readmeMd) {
|
|
10553
|
-
|
|
10044
|
+
fs35.writeFileSync("README.md", docs.readmeMd);
|
|
10554
10045
|
written.push("README.md");
|
|
10555
10046
|
}
|
|
10556
10047
|
if (docs.cursorrules) {
|
|
10557
|
-
|
|
10048
|
+
fs35.writeFileSync(".cursorrules", docs.cursorrules);
|
|
10558
10049
|
written.push(".cursorrules");
|
|
10559
10050
|
}
|
|
10560
10051
|
if (docs.cursorRules) {
|
|
10561
|
-
const rulesDir =
|
|
10562
|
-
if (!
|
|
10052
|
+
const rulesDir = path28.join(".cursor", "rules");
|
|
10053
|
+
if (!fs35.existsSync(rulesDir)) fs35.mkdirSync(rulesDir, { recursive: true });
|
|
10563
10054
|
for (const rule of docs.cursorRules) {
|
|
10564
|
-
|
|
10055
|
+
fs35.writeFileSync(path28.join(rulesDir, rule.filename), rule.content);
|
|
10565
10056
|
written.push(`.cursor/rules/${rule.filename}`);
|
|
10566
10057
|
}
|
|
10567
10058
|
}
|
|
10568
10059
|
if (docs.claudeSkills) {
|
|
10569
|
-
const skillsDir =
|
|
10570
|
-
if (!
|
|
10060
|
+
const skillsDir = path28.join(".claude", "skills");
|
|
10061
|
+
if (!fs35.existsSync(skillsDir)) fs35.mkdirSync(skillsDir, { recursive: true });
|
|
10571
10062
|
for (const skill of docs.claudeSkills) {
|
|
10572
|
-
|
|
10063
|
+
fs35.writeFileSync(path28.join(skillsDir, skill.filename), skill.content);
|
|
10573
10064
|
written.push(`.claude/skills/${skill.filename}`);
|
|
10574
10065
|
}
|
|
10575
10066
|
}
|
|
10576
10067
|
if (docs.copilotInstructions) {
|
|
10577
|
-
|
|
10578
|
-
|
|
10068
|
+
fs35.mkdirSync(".github", { recursive: true });
|
|
10069
|
+
fs35.writeFileSync(path28.join(".github", "copilot-instructions.md"), appendLearningsBlock(appendPreCommitBlock(docs.copilotInstructions)));
|
|
10579
10070
|
written.push(".github/copilot-instructions.md");
|
|
10580
10071
|
}
|
|
10581
10072
|
if (docs.copilotInstructionFiles) {
|
|
10582
|
-
const instructionsDir =
|
|
10583
|
-
|
|
10073
|
+
const instructionsDir = path28.join(".github", "instructions");
|
|
10074
|
+
fs35.mkdirSync(instructionsDir, { recursive: true });
|
|
10584
10075
|
for (const file of docs.copilotInstructionFiles) {
|
|
10585
|
-
|
|
10076
|
+
fs35.writeFileSync(path28.join(instructionsDir, file.filename), file.content);
|
|
10586
10077
|
written.push(`.github/instructions/${file.filename}`);
|
|
10587
10078
|
}
|
|
10588
10079
|
}
|
|
@@ -10668,8 +10159,8 @@ Changed files: ${diff.changedFiles.join(", ")}`);
|
|
|
10668
10159
|
}
|
|
10669
10160
|
|
|
10670
10161
|
// src/learner/writer.ts
|
|
10671
|
-
import
|
|
10672
|
-
import
|
|
10162
|
+
import fs36 from "fs";
|
|
10163
|
+
import path29 from "path";
|
|
10673
10164
|
|
|
10674
10165
|
// src/learner/utils.ts
|
|
10675
10166
|
var TYPE_PREFIX_RE = /^\*\*\[[^\]]+\]\*\*\s*/;
|
|
@@ -10786,20 +10277,20 @@ function deduplicateLearnedItems(existing, incoming) {
|
|
|
10786
10277
|
}
|
|
10787
10278
|
function writeLearnedSectionTo(filePath, header, existing, incoming, mode) {
|
|
10788
10279
|
const { merged, newCount, newItems } = deduplicateLearnedItems(existing, incoming);
|
|
10789
|
-
|
|
10790
|
-
if (mode)
|
|
10280
|
+
fs36.writeFileSync(filePath, header + merged + "\n");
|
|
10281
|
+
if (mode) fs36.chmodSync(filePath, mode);
|
|
10791
10282
|
return { newCount, newItems };
|
|
10792
10283
|
}
|
|
10793
10284
|
function writeLearnedSection(content) {
|
|
10794
10285
|
return writeLearnedSectionTo(LEARNINGS_FILE, LEARNINGS_HEADER, readLearnedSection(), content);
|
|
10795
10286
|
}
|
|
10796
10287
|
function writeLearnedSkill(skill) {
|
|
10797
|
-
const skillDir =
|
|
10798
|
-
if (!
|
|
10799
|
-
const skillPath =
|
|
10800
|
-
if (!skill.isNew &&
|
|
10801
|
-
const existing =
|
|
10802
|
-
|
|
10288
|
+
const skillDir = path29.join(".claude", "skills", skill.name);
|
|
10289
|
+
if (!fs36.existsSync(skillDir)) fs36.mkdirSync(skillDir, { recursive: true });
|
|
10290
|
+
const skillPath = path29.join(skillDir, "SKILL.md");
|
|
10291
|
+
if (!skill.isNew && fs36.existsSync(skillPath)) {
|
|
10292
|
+
const existing = fs36.readFileSync(skillPath, "utf-8");
|
|
10293
|
+
fs36.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
|
|
10803
10294
|
} else {
|
|
10804
10295
|
const frontmatter = [
|
|
10805
10296
|
"---",
|
|
@@ -10808,12 +10299,12 @@ function writeLearnedSkill(skill) {
|
|
|
10808
10299
|
"---",
|
|
10809
10300
|
""
|
|
10810
10301
|
].join("\n");
|
|
10811
|
-
|
|
10302
|
+
fs36.writeFileSync(skillPath, frontmatter + skill.content);
|
|
10812
10303
|
}
|
|
10813
10304
|
return skillPath;
|
|
10814
10305
|
}
|
|
10815
10306
|
function writePersonalLearnedSection(content) {
|
|
10816
|
-
if (!
|
|
10307
|
+
if (!fs36.existsSync(AUTH_DIR)) fs36.mkdirSync(AUTH_DIR, { recursive: true });
|
|
10817
10308
|
return writeLearnedSectionTo(PERSONAL_LEARNINGS_FILE, PERSONAL_LEARNINGS_HEADER, readPersonalLearnings(), content, 384);
|
|
10818
10309
|
}
|
|
10819
10310
|
function addLearning(bullet, scope = "project") {
|
|
@@ -10826,56 +10317,55 @@ function addLearning(bullet, scope = "project") {
|
|
|
10826
10317
|
return { file: LEARNINGS_FILE, added: result.newCount > 0 };
|
|
10827
10318
|
}
|
|
10828
10319
|
function readPersonalLearnings() {
|
|
10829
|
-
if (!
|
|
10830
|
-
const content =
|
|
10320
|
+
if (!fs36.existsSync(PERSONAL_LEARNINGS_FILE)) return null;
|
|
10321
|
+
const content = fs36.readFileSync(PERSONAL_LEARNINGS_FILE, "utf-8");
|
|
10831
10322
|
const bullets = content.split("\n").filter((l) => l.startsWith("- ")).join("\n");
|
|
10832
10323
|
return bullets || null;
|
|
10833
10324
|
}
|
|
10834
10325
|
function readLearnedSection() {
|
|
10835
|
-
if (
|
|
10836
|
-
const content2 =
|
|
10326
|
+
if (fs36.existsSync(LEARNINGS_FILE)) {
|
|
10327
|
+
const content2 = fs36.readFileSync(LEARNINGS_FILE, "utf-8");
|
|
10837
10328
|
const bullets = content2.split("\n").filter((l) => l.startsWith("- ")).join("\n");
|
|
10838
10329
|
return bullets || null;
|
|
10839
10330
|
}
|
|
10840
10331
|
const claudeMdPath = "CLAUDE.md";
|
|
10841
|
-
if (!
|
|
10842
|
-
const content =
|
|
10332
|
+
if (!fs36.existsSync(claudeMdPath)) return null;
|
|
10333
|
+
const content = fs36.readFileSync(claudeMdPath, "utf-8");
|
|
10843
10334
|
const startIdx = content.indexOf(LEARNED_START);
|
|
10844
10335
|
const endIdx = content.indexOf(LEARNED_END);
|
|
10845
10336
|
if (startIdx === -1 || endIdx === -1) return null;
|
|
10846
10337
|
return content.slice(startIdx + LEARNED_START.length, endIdx).trim() || null;
|
|
10847
10338
|
}
|
|
10848
10339
|
function migrateInlineLearnings() {
|
|
10849
|
-
if (
|
|
10340
|
+
if (fs36.existsSync(LEARNINGS_FILE)) return false;
|
|
10850
10341
|
const claudeMdPath = "CLAUDE.md";
|
|
10851
|
-
if (!
|
|
10852
|
-
const content =
|
|
10342
|
+
if (!fs36.existsSync(claudeMdPath)) return false;
|
|
10343
|
+
const content = fs36.readFileSync(claudeMdPath, "utf-8");
|
|
10853
10344
|
const startIdx = content.indexOf(LEARNED_START);
|
|
10854
10345
|
const endIdx = content.indexOf(LEARNED_END);
|
|
10855
10346
|
if (startIdx === -1 || endIdx === -1) return false;
|
|
10856
10347
|
const section = content.slice(startIdx + LEARNED_START.length, endIdx).trim();
|
|
10857
10348
|
if (!section) return false;
|
|
10858
|
-
|
|
10349
|
+
fs36.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
|
|
10859
10350
|
const cleaned = content.slice(0, startIdx) + content.slice(endIdx + LEARNED_END.length);
|
|
10860
|
-
|
|
10351
|
+
fs36.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
|
|
10861
10352
|
return true;
|
|
10862
10353
|
}
|
|
10863
10354
|
|
|
10864
10355
|
// src/commands/refresh.ts
|
|
10865
10356
|
init_config();
|
|
10866
10357
|
init_resolve_caliber();
|
|
10867
|
-
init_builtin_skills();
|
|
10868
10358
|
function log2(quiet, ...args) {
|
|
10869
10359
|
if (!quiet) console.log(...args);
|
|
10870
10360
|
}
|
|
10871
10361
|
function discoverGitRepos(parentDir) {
|
|
10872
10362
|
const repos = [];
|
|
10873
10363
|
try {
|
|
10874
|
-
const entries =
|
|
10364
|
+
const entries = fs38.readdirSync(parentDir, { withFileTypes: true });
|
|
10875
10365
|
for (const entry of entries) {
|
|
10876
10366
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
10877
|
-
const childPath =
|
|
10878
|
-
if (
|
|
10367
|
+
const childPath = path31.join(parentDir, entry.name);
|
|
10368
|
+
if (fs38.existsSync(path31.join(childPath, ".git"))) {
|
|
10879
10369
|
repos.push(childPath);
|
|
10880
10370
|
}
|
|
10881
10371
|
}
|
|
@@ -10961,27 +10451,26 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
10961
10451
|
const filesToWrite = response.docsUpdated || [];
|
|
10962
10452
|
const preRefreshContents = /* @__PURE__ */ new Map();
|
|
10963
10453
|
for (const filePath of filesToWrite) {
|
|
10964
|
-
const fullPath =
|
|
10454
|
+
const fullPath = path31.resolve(repoDir, filePath);
|
|
10965
10455
|
try {
|
|
10966
|
-
preRefreshContents.set(filePath,
|
|
10456
|
+
preRefreshContents.set(filePath, fs38.readFileSync(fullPath, "utf-8"));
|
|
10967
10457
|
} catch {
|
|
10968
10458
|
preRefreshContents.set(filePath, null);
|
|
10969
10459
|
}
|
|
10970
10460
|
}
|
|
10971
10461
|
const written = writeRefreshDocs(response.updatedDocs);
|
|
10972
|
-
|
|
10973
|
-
trackRefreshCompleted(written.length, Date.now(), trigger);
|
|
10462
|
+
trackRefreshCompleted(written.length, Date.now());
|
|
10974
10463
|
const postScore = computeLocalScore(repoDir, targetAgent);
|
|
10975
10464
|
if (postScore.score < preScore.score) {
|
|
10976
10465
|
for (const [filePath, content] of preRefreshContents) {
|
|
10977
|
-
const fullPath =
|
|
10466
|
+
const fullPath = path31.resolve(repoDir, filePath);
|
|
10978
10467
|
if (content === null) {
|
|
10979
10468
|
try {
|
|
10980
|
-
|
|
10469
|
+
fs38.unlinkSync(fullPath);
|
|
10981
10470
|
} catch {
|
|
10982
10471
|
}
|
|
10983
10472
|
} else {
|
|
10984
|
-
|
|
10473
|
+
fs38.writeFileSync(fullPath, content);
|
|
10985
10474
|
}
|
|
10986
10475
|
}
|
|
10987
10476
|
spinner?.warn(`${prefix}Refresh reverted \u2014 score would drop from ${preScore.score} to ${postScore.score}`);
|
|
@@ -11036,7 +10525,7 @@ async function refreshCommand(options) {
|
|
|
11036
10525
|
`));
|
|
11037
10526
|
const originalDir = process.cwd();
|
|
11038
10527
|
for (const repo of repos) {
|
|
11039
|
-
const repoName =
|
|
10528
|
+
const repoName = path31.basename(repo);
|
|
11040
10529
|
try {
|
|
11041
10530
|
process.chdir(repo);
|
|
11042
10531
|
await refreshSingleRepo(repo, { ...options, label: repoName });
|
|
@@ -11058,6 +10547,150 @@ async function refreshCommand(options) {
|
|
|
11058
10547
|
// src/commands/hooks.ts
|
|
11059
10548
|
import chalk20 from "chalk";
|
|
11060
10549
|
import fs40 from "fs";
|
|
10550
|
+
|
|
10551
|
+
// src/lib/hooks.ts
|
|
10552
|
+
init_resolve_caliber();
|
|
10553
|
+
import fs39 from "fs";
|
|
10554
|
+
import path32 from "path";
|
|
10555
|
+
import { execSync as execSync15 } from "child_process";
|
|
10556
|
+
var SETTINGS_PATH2 = path32.join(".claude", "settings.json");
|
|
10557
|
+
var REFRESH_TAIL = "refresh --quiet";
|
|
10558
|
+
var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
|
|
10559
|
+
function getHookCommand() {
|
|
10560
|
+
return `${resolveCaliber()} ${REFRESH_TAIL}`;
|
|
10561
|
+
}
|
|
10562
|
+
function readSettings2() {
|
|
10563
|
+
if (!fs39.existsSync(SETTINGS_PATH2)) return {};
|
|
10564
|
+
try {
|
|
10565
|
+
return JSON.parse(fs39.readFileSync(SETTINGS_PATH2, "utf-8"));
|
|
10566
|
+
} catch {
|
|
10567
|
+
return {};
|
|
10568
|
+
}
|
|
10569
|
+
}
|
|
10570
|
+
function writeSettings2(settings) {
|
|
10571
|
+
const dir = path32.dirname(SETTINGS_PATH2);
|
|
10572
|
+
if (!fs39.existsSync(dir)) fs39.mkdirSync(dir, { recursive: true });
|
|
10573
|
+
fs39.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
|
|
10574
|
+
}
|
|
10575
|
+
function findHookIndex(sessionEnd) {
|
|
10576
|
+
return sessionEnd.findIndex(
|
|
10577
|
+
(entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, REFRESH_TAIL))
|
|
10578
|
+
);
|
|
10579
|
+
}
|
|
10580
|
+
function isHookInstalled() {
|
|
10581
|
+
const settings = readSettings2();
|
|
10582
|
+
const sessionEnd = settings.hooks?.SessionEnd;
|
|
10583
|
+
if (!Array.isArray(sessionEnd)) return false;
|
|
10584
|
+
return findHookIndex(sessionEnd) !== -1;
|
|
10585
|
+
}
|
|
10586
|
+
function installHook() {
|
|
10587
|
+
const settings = readSettings2();
|
|
10588
|
+
if (!settings.hooks) settings.hooks = {};
|
|
10589
|
+
if (!Array.isArray(settings.hooks.SessionEnd)) settings.hooks.SessionEnd = [];
|
|
10590
|
+
if (findHookIndex(settings.hooks.SessionEnd) !== -1) {
|
|
10591
|
+
return { installed: false, alreadyInstalled: true };
|
|
10592
|
+
}
|
|
10593
|
+
settings.hooks.SessionEnd.push({
|
|
10594
|
+
matcher: "",
|
|
10595
|
+
hooks: [{ type: "command", command: getHookCommand(), description: HOOK_DESCRIPTION }]
|
|
10596
|
+
});
|
|
10597
|
+
writeSettings2(settings);
|
|
10598
|
+
return { installed: true, alreadyInstalled: false };
|
|
10599
|
+
}
|
|
10600
|
+
function removeHook() {
|
|
10601
|
+
const settings = readSettings2();
|
|
10602
|
+
const sessionEnd = settings.hooks?.SessionEnd;
|
|
10603
|
+
if (!Array.isArray(sessionEnd)) {
|
|
10604
|
+
return { removed: false, notFound: true };
|
|
10605
|
+
}
|
|
10606
|
+
const idx = findHookIndex(sessionEnd);
|
|
10607
|
+
if (idx === -1) {
|
|
10608
|
+
return { removed: false, notFound: true };
|
|
10609
|
+
}
|
|
10610
|
+
sessionEnd.splice(idx, 1);
|
|
10611
|
+
if (sessionEnd.length === 0) {
|
|
10612
|
+
delete settings.hooks.SessionEnd;
|
|
10613
|
+
}
|
|
10614
|
+
if (settings.hooks && Object.keys(settings.hooks).length === 0) {
|
|
10615
|
+
delete settings.hooks;
|
|
10616
|
+
}
|
|
10617
|
+
writeSettings2(settings);
|
|
10618
|
+
return { removed: true, notFound: false };
|
|
10619
|
+
}
|
|
10620
|
+
var PRECOMMIT_START = "# caliber:pre-commit:start";
|
|
10621
|
+
var PRECOMMIT_END = "# caliber:pre-commit:end";
|
|
10622
|
+
function getPrecommitBlock() {
|
|
10623
|
+
const bin = resolveCaliber();
|
|
10624
|
+
const npx = isNpxResolution();
|
|
10625
|
+
const guard = npx ? "command -v npx >/dev/null 2>&1" : `[ -x "${bin}" ] || command -v "${bin}" >/dev/null 2>&1`;
|
|
10626
|
+
const invoke = npx ? bin : `"${bin}"`;
|
|
10627
|
+
return `${PRECOMMIT_START}
|
|
10628
|
+
if ${guard}; then
|
|
10629
|
+
echo "\\033[2mcaliber: refreshing docs...\\033[0m"
|
|
10630
|
+
${invoke} refresh 2>/dev/null || true
|
|
10631
|
+
${invoke} learn finalize 2>/dev/null || true
|
|
10632
|
+
git diff --name-only -- CLAUDE.md .claude/ .cursor/ AGENTS.md CALIBER_LEARNINGS.md 2>/dev/null | xargs git add 2>/dev/null || true
|
|
10633
|
+
fi
|
|
10634
|
+
${PRECOMMIT_END}`;
|
|
10635
|
+
}
|
|
10636
|
+
function getGitHooksDir() {
|
|
10637
|
+
try {
|
|
10638
|
+
const gitDir = execSync15("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
10639
|
+
return path32.join(gitDir, "hooks");
|
|
10640
|
+
} catch {
|
|
10641
|
+
return null;
|
|
10642
|
+
}
|
|
10643
|
+
}
|
|
10644
|
+
function getPreCommitPath() {
|
|
10645
|
+
const hooksDir = getGitHooksDir();
|
|
10646
|
+
return hooksDir ? path32.join(hooksDir, "pre-commit") : null;
|
|
10647
|
+
}
|
|
10648
|
+
function isPreCommitHookInstalled() {
|
|
10649
|
+
const hookPath = getPreCommitPath();
|
|
10650
|
+
if (!hookPath || !fs39.existsSync(hookPath)) return false;
|
|
10651
|
+
const content = fs39.readFileSync(hookPath, "utf-8");
|
|
10652
|
+
return content.includes(PRECOMMIT_START);
|
|
10653
|
+
}
|
|
10654
|
+
function installPreCommitHook() {
|
|
10655
|
+
if (isPreCommitHookInstalled()) {
|
|
10656
|
+
return { installed: false, alreadyInstalled: true };
|
|
10657
|
+
}
|
|
10658
|
+
const hookPath = getPreCommitPath();
|
|
10659
|
+
if (!hookPath) return { installed: false, alreadyInstalled: false };
|
|
10660
|
+
const hooksDir = path32.dirname(hookPath);
|
|
10661
|
+
if (!fs39.existsSync(hooksDir)) fs39.mkdirSync(hooksDir, { recursive: true });
|
|
10662
|
+
let content = "";
|
|
10663
|
+
if (fs39.existsSync(hookPath)) {
|
|
10664
|
+
content = fs39.readFileSync(hookPath, "utf-8");
|
|
10665
|
+
if (!content.endsWith("\n")) content += "\n";
|
|
10666
|
+
content += "\n" + getPrecommitBlock() + "\n";
|
|
10667
|
+
} else {
|
|
10668
|
+
content = "#!/bin/sh\n\n" + getPrecommitBlock() + "\n";
|
|
10669
|
+
}
|
|
10670
|
+
fs39.writeFileSync(hookPath, content);
|
|
10671
|
+
fs39.chmodSync(hookPath, 493);
|
|
10672
|
+
return { installed: true, alreadyInstalled: false };
|
|
10673
|
+
}
|
|
10674
|
+
function removePreCommitHook() {
|
|
10675
|
+
const hookPath = getPreCommitPath();
|
|
10676
|
+
if (!hookPath || !fs39.existsSync(hookPath)) {
|
|
10677
|
+
return { removed: false, notFound: true };
|
|
10678
|
+
}
|
|
10679
|
+
let content = fs39.readFileSync(hookPath, "utf-8");
|
|
10680
|
+
if (!content.includes(PRECOMMIT_START)) {
|
|
10681
|
+
return { removed: false, notFound: true };
|
|
10682
|
+
}
|
|
10683
|
+
const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
|
|
10684
|
+
content = content.replace(regex, "\n");
|
|
10685
|
+
if (content.trim() === "#!/bin/sh" || content.trim() === "") {
|
|
10686
|
+
fs39.unlinkSync(hookPath);
|
|
10687
|
+
} else {
|
|
10688
|
+
fs39.writeFileSync(hookPath, content);
|
|
10689
|
+
}
|
|
10690
|
+
return { removed: true, notFound: false };
|
|
10691
|
+
}
|
|
10692
|
+
|
|
10693
|
+
// src/commands/hooks.ts
|
|
11061
10694
|
var HOOKS = [
|
|
11062
10695
|
{
|
|
11063
10696
|
id: "session-end",
|
|
@@ -12666,199 +12299,10 @@ async function publishCommand() {
|
|
|
12666
12299
|
}
|
|
12667
12300
|
}
|
|
12668
12301
|
|
|
12669
|
-
// src/commands/bootstrap.ts
|
|
12670
|
-
init_builtin_skills();
|
|
12671
|
-
import fs47 from "fs";
|
|
12672
|
-
import chalk27 from "chalk";
|
|
12673
|
-
var PLATFORM_SKILL_DIRS = {
|
|
12674
|
-
claude: ".claude/skills",
|
|
12675
|
-
cursor: ".cursor/skills",
|
|
12676
|
-
codex: ".agents/skills"
|
|
12677
|
-
};
|
|
12678
|
-
async function bootstrapCommand() {
|
|
12679
|
-
const platforms = detectPlatforms();
|
|
12680
|
-
const detected = [];
|
|
12681
|
-
if (platforms.claude) detected.push("claude");
|
|
12682
|
-
if (platforms.cursor) detected.push("cursor");
|
|
12683
|
-
if (platforms.codex) detected.push("codex");
|
|
12684
|
-
if (detected.length === 0) detected.push("claude");
|
|
12685
|
-
const written = [];
|
|
12686
|
-
for (const platform of detected) {
|
|
12687
|
-
const skillsDir = PLATFORM_SKILL_DIRS[platform];
|
|
12688
|
-
if (!skillsDir) continue;
|
|
12689
|
-
for (const skill of BUILTIN_SKILLS) {
|
|
12690
|
-
const skillDir = `${skillsDir}/${skill.name}`;
|
|
12691
|
-
const skillPath = `${skillDir}/SKILL.md`;
|
|
12692
|
-
fs47.mkdirSync(skillDir, { recursive: true });
|
|
12693
|
-
fs47.writeFileSync(skillPath, buildSkillContent(skill));
|
|
12694
|
-
written.push(skillPath);
|
|
12695
|
-
}
|
|
12696
|
-
}
|
|
12697
|
-
if (written.length === 0) {
|
|
12698
|
-
console.log(chalk27.yellow("No skills were written."));
|
|
12699
|
-
return;
|
|
12700
|
-
}
|
|
12701
|
-
console.log(chalk27.bold.green("\n Caliber skills installed!\n"));
|
|
12702
|
-
for (const file of written) {
|
|
12703
|
-
console.log(` ${chalk27.green("\u2713")} ${file}`);
|
|
12704
|
-
}
|
|
12705
|
-
console.log(chalk27.dim("\n Your agent can now run /setup-caliber to complete the setup."));
|
|
12706
|
-
console.log(chalk27.dim(' Just tell your agent: "Run /setup-caliber"\n'));
|
|
12707
|
-
}
|
|
12708
|
-
|
|
12709
|
-
// src/commands/uninstall.ts
|
|
12710
|
-
import fs48 from "fs";
|
|
12711
|
-
import path39 from "path";
|
|
12712
|
-
import chalk28 from "chalk";
|
|
12713
|
-
import confirm3 from "@inquirer/confirm";
|
|
12714
|
-
init_pre_commit_block();
|
|
12715
|
-
init_builtin_skills();
|
|
12716
|
-
init_config();
|
|
12717
|
-
var MANAGED_DOC_FILES = [
|
|
12718
|
-
"CLAUDE.md",
|
|
12719
|
-
path39.join(".github", "copilot-instructions.md"),
|
|
12720
|
-
"AGENTS.md"
|
|
12721
|
-
];
|
|
12722
|
-
var SKILL_DIRS = PLATFORM_CONFIGS.map((c) => c.skillsDir);
|
|
12723
|
-
var CURSOR_RULES_DIR = path39.join(".cursor", "rules");
|
|
12724
|
-
function removeCaliberCursorRules() {
|
|
12725
|
-
const removed = [];
|
|
12726
|
-
if (!fs48.existsSync(CURSOR_RULES_DIR)) return removed;
|
|
12727
|
-
for (const file of fs48.readdirSync(CURSOR_RULES_DIR)) {
|
|
12728
|
-
if (file.startsWith("caliber-") && file.endsWith(".mdc")) {
|
|
12729
|
-
const fullPath = path39.join(CURSOR_RULES_DIR, file);
|
|
12730
|
-
fs48.unlinkSync(fullPath);
|
|
12731
|
-
removed.push(fullPath);
|
|
12732
|
-
}
|
|
12733
|
-
}
|
|
12734
|
-
return removed;
|
|
12735
|
-
}
|
|
12736
|
-
function removeBuiltinSkills() {
|
|
12737
|
-
const removed = [];
|
|
12738
|
-
for (const skillsDir of SKILL_DIRS) {
|
|
12739
|
-
if (!fs48.existsSync(skillsDir)) continue;
|
|
12740
|
-
for (const name of BUILTIN_SKILL_NAMES) {
|
|
12741
|
-
const skillDir = path39.join(skillsDir, name);
|
|
12742
|
-
if (fs48.existsSync(skillDir)) {
|
|
12743
|
-
fs48.rmSync(skillDir, { recursive: true });
|
|
12744
|
-
removed.push(skillDir);
|
|
12745
|
-
}
|
|
12746
|
-
}
|
|
12747
|
-
}
|
|
12748
|
-
return removed;
|
|
12749
|
-
}
|
|
12750
|
-
function stripManagedBlocksFromFiles() {
|
|
12751
|
-
const modified = [];
|
|
12752
|
-
for (const filePath of MANAGED_DOC_FILES) {
|
|
12753
|
-
if (!fs48.existsSync(filePath)) continue;
|
|
12754
|
-
const original = fs48.readFileSync(filePath, "utf-8");
|
|
12755
|
-
const stripped = stripManagedBlocks(original);
|
|
12756
|
-
if (stripped !== original) {
|
|
12757
|
-
const trimmed = stripped.trim();
|
|
12758
|
-
if (!trimmed || /^#\s*\S*$/.test(trimmed)) {
|
|
12759
|
-
fs48.unlinkSync(filePath);
|
|
12760
|
-
} else {
|
|
12761
|
-
fs48.writeFileSync(filePath, stripped);
|
|
12762
|
-
}
|
|
12763
|
-
modified.push(filePath);
|
|
12764
|
-
}
|
|
12765
|
-
}
|
|
12766
|
-
return modified;
|
|
12767
|
-
}
|
|
12768
|
-
function removeDirectory(dir) {
|
|
12769
|
-
if (!fs48.existsSync(dir)) return false;
|
|
12770
|
-
fs48.rmSync(dir, { recursive: true });
|
|
12771
|
-
return true;
|
|
12772
|
-
}
|
|
12773
|
-
async function uninstallCommand(options) {
|
|
12774
|
-
console.log(chalk28.bold("\n Caliber Uninstall\n"));
|
|
12775
|
-
console.log(chalk28.dim(" This will remove all Caliber resources from this project:\n"));
|
|
12776
|
-
console.log(chalk28.dim(" \u2022 Pre-commit hook"));
|
|
12777
|
-
console.log(chalk28.dim(" \u2022 Session learning hooks"));
|
|
12778
|
-
console.log(chalk28.dim(" \u2022 Managed blocks in CLAUDE.md, AGENTS.md, copilot-instructions.md"));
|
|
12779
|
-
console.log(chalk28.dim(" \u2022 Cursor rules (caliber-*.mdc)"));
|
|
12780
|
-
console.log(chalk28.dim(" \u2022 Built-in skills (setup-caliber, find-skills, save-learning)"));
|
|
12781
|
-
console.log(chalk28.dim(" \u2022 CALIBER_LEARNINGS.md"));
|
|
12782
|
-
console.log(chalk28.dim(" \u2022 .caliber/ directory (backups, cache, state)\n"));
|
|
12783
|
-
if (!options.force) {
|
|
12784
|
-
const proceed = await confirm3({ message: "Continue with uninstall?" });
|
|
12785
|
-
if (!proceed) {
|
|
12786
|
-
console.log(chalk28.dim("\n Cancelled.\n"));
|
|
12787
|
-
return;
|
|
12788
|
-
}
|
|
12789
|
-
}
|
|
12790
|
-
console.log("");
|
|
12791
|
-
const actions = [];
|
|
12792
|
-
const hookResult = removePreCommitHook();
|
|
12793
|
-
if (hookResult.removed) {
|
|
12794
|
-
console.log(` ${chalk28.red("\u2717")} Pre-commit hook removed`);
|
|
12795
|
-
actions.push("pre-commit hook");
|
|
12796
|
-
}
|
|
12797
|
-
const learnResult = removeLearningHooks();
|
|
12798
|
-
if (learnResult.removed) {
|
|
12799
|
-
console.log(` ${chalk28.red("\u2717")} Claude Code learning hooks removed`);
|
|
12800
|
-
actions.push("claude learning hooks");
|
|
12801
|
-
}
|
|
12802
|
-
const cursorLearnResult = removeCursorLearningHooks();
|
|
12803
|
-
if (cursorLearnResult.removed) {
|
|
12804
|
-
console.log(` ${chalk28.red("\u2717")} Cursor learning hooks removed`);
|
|
12805
|
-
actions.push("cursor learning hooks");
|
|
12806
|
-
}
|
|
12807
|
-
const strippedFiles = stripManagedBlocksFromFiles();
|
|
12808
|
-
for (const file of strippedFiles) {
|
|
12809
|
-
console.log(` ${chalk28.yellow("~")} ${file} \u2014 managed blocks removed`);
|
|
12810
|
-
actions.push(file);
|
|
12811
|
-
}
|
|
12812
|
-
const removedRules = removeCaliberCursorRules();
|
|
12813
|
-
for (const rule of removedRules) {
|
|
12814
|
-
console.log(` ${chalk28.red("\u2717")} ${rule}`);
|
|
12815
|
-
}
|
|
12816
|
-
if (removedRules.length > 0) actions.push("cursor rules");
|
|
12817
|
-
const removedSkills = removeBuiltinSkills();
|
|
12818
|
-
for (const skill of removedSkills) {
|
|
12819
|
-
console.log(` ${chalk28.red("\u2717")} ${skill}/`);
|
|
12820
|
-
}
|
|
12821
|
-
if (removedSkills.length > 0) actions.push("builtin skills");
|
|
12822
|
-
if (fs48.existsSync("CALIBER_LEARNINGS.md")) {
|
|
12823
|
-
fs48.unlinkSync("CALIBER_LEARNINGS.md");
|
|
12824
|
-
console.log(` ${chalk28.red("\u2717")} CALIBER_LEARNINGS.md`);
|
|
12825
|
-
actions.push("learnings file");
|
|
12826
|
-
}
|
|
12827
|
-
if (removeDirectory(CALIBER_DIR)) {
|
|
12828
|
-
console.log(` ${chalk28.red("\u2717")} .caliber/ directory`);
|
|
12829
|
-
actions.push(".caliber directory");
|
|
12830
|
-
}
|
|
12831
|
-
if (actions.length === 0) {
|
|
12832
|
-
console.log(chalk28.dim(" Nothing to remove \u2014 Caliber is not installed in this project.\n"));
|
|
12833
|
-
return;
|
|
12834
|
-
}
|
|
12835
|
-
trackUninstallExecuted();
|
|
12836
|
-
const configPath = getConfigFilePath();
|
|
12837
|
-
if (fs48.existsSync(configPath)) {
|
|
12838
|
-
console.log("");
|
|
12839
|
-
const removeConfig = options.force || await confirm3({
|
|
12840
|
-
message: `Remove global config (~/.caliber/config.json)? This affects all projects.`
|
|
12841
|
-
});
|
|
12842
|
-
if (removeConfig) {
|
|
12843
|
-
fs48.unlinkSync(configPath);
|
|
12844
|
-
console.log(` ${chalk28.red("\u2717")} ${configPath}`);
|
|
12845
|
-
const configDir = path39.dirname(configPath);
|
|
12846
|
-
try {
|
|
12847
|
-
const remaining = fs48.readdirSync(configDir);
|
|
12848
|
-
if (remaining.length === 0) fs48.rmdirSync(configDir);
|
|
12849
|
-
} catch {
|
|
12850
|
-
}
|
|
12851
|
-
}
|
|
12852
|
-
}
|
|
12853
|
-
console.log(chalk28.bold.green(`
|
|
12854
|
-
Caliber has been removed from this project.`));
|
|
12855
|
-
console.log(chalk28.dim(" Your code is untouched \u2014 only Caliber config files were removed.\n"));
|
|
12856
|
-
}
|
|
12857
|
-
|
|
12858
12302
|
// src/cli.ts
|
|
12859
|
-
var __dirname =
|
|
12303
|
+
var __dirname = path39.dirname(fileURLToPath(import.meta.url));
|
|
12860
12304
|
var pkg = JSON.parse(
|
|
12861
|
-
|
|
12305
|
+
fs47.readFileSync(path39.resolve(__dirname, "..", "package.json"), "utf-8")
|
|
12862
12306
|
);
|
|
12863
12307
|
var program = new Command();
|
|
12864
12308
|
var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
|
|
@@ -12866,11 +12310,9 @@ program.name(process.env.CALIBER_LOCAL ? "caloc" : "caliber").description("AI co
|
|
|
12866
12310
|
function tracked(commandName, handler) {
|
|
12867
12311
|
const wrapper = async (...args) => {
|
|
12868
12312
|
const start = Date.now();
|
|
12869
|
-
const isCI = !!(process.env.CI || process.env.GITHUB_ACTIONS);
|
|
12870
12313
|
trackEvent("command_started", {
|
|
12871
12314
|
command: commandName,
|
|
12872
|
-
cli_version: pkg.version
|
|
12873
|
-
is_ci: isCI
|
|
12315
|
+
cli_version: pkg.version
|
|
12874
12316
|
});
|
|
12875
12317
|
try {
|
|
12876
12318
|
await handler(...args);
|
|
@@ -12922,9 +12364,7 @@ function parseAgentOption(value) {
|
|
|
12922
12364
|
return agents;
|
|
12923
12365
|
}
|
|
12924
12366
|
program.command("init").description("Initialize your project for AI-assisted development").option("--agent <type>", "Target agents (comma-separated): claude, cursor, codex, github-copilot", parseAgentOption).option("--source <paths...>", "Related source paths to include as context").option("--dry-run", "Preview changes without writing files").option("--force", "Overwrite existing config without prompting").option("--debug-report", void 0, false).option("--show-tokens", "Show token usage summary at the end").option("--auto-approve", "Run without interactive prompts (auto-accept all)").option("--verbose", "Show detailed logs of each step").action(tracked("init", initCommand));
|
|
12925
|
-
program.command("bootstrap").description("Install agent skills (/setup-caliber, /find-skills, /save-learning) without running init").action(tracked("bootstrap", bootstrapCommand));
|
|
12926
12367
|
program.command("undo").description("Revert all config changes made by Caliber").action(tracked("undo", undoCommand));
|
|
12927
|
-
program.command("uninstall").description("Remove all Caliber resources from this project").option("--force", "Skip confirmation prompt").action(tracked("uninstall", (options) => uninstallCommand(options)));
|
|
12928
12368
|
program.command("status").description("Show current Caliber config status").option("--json", "Output as JSON").action(tracked("status", statusCommand));
|
|
12929
12369
|
program.command("regenerate").alias("regen").alias("re").description("Re-analyze project and regenerate config").option("--dry-run", "Preview changes without writing files").action(tracked("regenerate", regenerateCommand));
|
|
12930
12370
|
program.command("config").description("Configure LLM provider, API key, and model").action(tracked("config", configCommand));
|
|
@@ -12949,15 +12389,15 @@ learn.command("delete <index>").description("Delete a learning by its index numb
|
|
|
12949
12389
|
learn.command("add <content>").description("Add a learning directly (used by agent skills)").option("--personal", "Save as a personal learning instead of project-level").action(tracked("learn:add", learnAddCommand));
|
|
12950
12390
|
|
|
12951
12391
|
// src/utils/version-check.ts
|
|
12952
|
-
import
|
|
12953
|
-
import
|
|
12392
|
+
import fs48 from "fs";
|
|
12393
|
+
import path40 from "path";
|
|
12954
12394
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
12955
12395
|
import { execSync as execSync16, execFileSync as execFileSync3 } from "child_process";
|
|
12956
|
-
import
|
|
12396
|
+
import chalk27 from "chalk";
|
|
12957
12397
|
import ora8 from "ora";
|
|
12958
|
-
import
|
|
12959
|
-
var __dirname_vc =
|
|
12960
|
-
var pkg2 = JSON.parse(
|
|
12398
|
+
import confirm2 from "@inquirer/confirm";
|
|
12399
|
+
var __dirname_vc = path40.dirname(fileURLToPath2(import.meta.url));
|
|
12400
|
+
var pkg2 = JSON.parse(fs48.readFileSync(path40.resolve(__dirname_vc, "..", "package.json"), "utf-8"));
|
|
12961
12401
|
function getChannel(version) {
|
|
12962
12402
|
const match = version.match(/-(dev|next)\./);
|
|
12963
12403
|
return match ? match[1] : "latest";
|
|
@@ -12984,8 +12424,8 @@ function getInstalledVersion() {
|
|
|
12984
12424
|
encoding: "utf-8",
|
|
12985
12425
|
stdio: ["pipe", "pipe", "pipe"]
|
|
12986
12426
|
}).trim();
|
|
12987
|
-
const pkgPath =
|
|
12988
|
-
return JSON.parse(
|
|
12427
|
+
const pkgPath = path40.join(globalRoot, "@rely-ai", "caliber", "package.json");
|
|
12428
|
+
return JSON.parse(fs48.readFileSync(pkgPath, "utf-8")).version;
|
|
12989
12429
|
} catch {
|
|
12990
12430
|
return null;
|
|
12991
12431
|
}
|
|
@@ -13010,18 +12450,18 @@ async function checkForUpdates() {
|
|
|
13010
12450
|
if (!isInteractive) {
|
|
13011
12451
|
const installTag = channel === "latest" ? "" : `@${channel}`;
|
|
13012
12452
|
console.log(
|
|
13013
|
-
|
|
12453
|
+
chalk27.yellow(
|
|
13014
12454
|
`
|
|
13015
12455
|
Update available: ${current} -> ${latest}
|
|
13016
|
-
Run ${
|
|
12456
|
+
Run ${chalk27.bold(`npm install -g @rely-ai/caliber${installTag}`)} to upgrade.
|
|
13017
12457
|
`
|
|
13018
12458
|
)
|
|
13019
12459
|
);
|
|
13020
12460
|
return;
|
|
13021
12461
|
}
|
|
13022
|
-
console.log(
|
|
12462
|
+
console.log(chalk27.yellow(`
|
|
13023
12463
|
Update available: ${current} -> ${latest}`));
|
|
13024
|
-
const shouldUpdate = await
|
|
12464
|
+
const shouldUpdate = await confirm2({
|
|
13025
12465
|
message: "Would you like to update now? (Y/n)",
|
|
13026
12466
|
default: true
|
|
13027
12467
|
});
|
|
@@ -13042,14 +12482,14 @@ Update available: ${current} -> ${latest}`));
|
|
|
13042
12482
|
if (installed !== latest) {
|
|
13043
12483
|
spinner.fail(`Update incomplete \u2014 got ${installed ?? "unknown"}, expected ${latest}`);
|
|
13044
12484
|
console.log(
|
|
13045
|
-
|
|
12485
|
+
chalk27.yellow(`Run ${chalk27.bold(`npm install -g @rely-ai/caliber@${tag}`)} manually.
|
|
13046
12486
|
`)
|
|
13047
12487
|
);
|
|
13048
12488
|
return;
|
|
13049
12489
|
}
|
|
13050
|
-
spinner.succeed(
|
|
12490
|
+
spinner.succeed(chalk27.green(`Updated to ${latest}`));
|
|
13051
12491
|
const args = process.argv.slice(2);
|
|
13052
|
-
console.log(
|
|
12492
|
+
console.log(chalk27.dim(`
|
|
13053
12493
|
Restarting: caliber ${args.join(" ")}
|
|
13054
12494
|
`));
|
|
13055
12495
|
execFileSync3("caliber", args, {
|
|
@@ -13062,11 +12502,11 @@ Restarting: caliber ${args.join(" ")}
|
|
|
13062
12502
|
if (err instanceof Error) {
|
|
13063
12503
|
const stderr = err.stderr;
|
|
13064
12504
|
const errMsg = stderr ? String(stderr).trim().split("\n").pop() : err.message.split("\n")[0];
|
|
13065
|
-
if (errMsg && !errMsg.includes("SIGTERM")) console.log(
|
|
12505
|
+
if (errMsg && !errMsg.includes("SIGTERM")) console.log(chalk27.dim(` ${errMsg}`));
|
|
13066
12506
|
}
|
|
13067
12507
|
console.log(
|
|
13068
|
-
|
|
13069
|
-
`Run ${
|
|
12508
|
+
chalk27.yellow(
|
|
12509
|
+
`Run ${chalk27.bold(`npm install -g @rely-ai/caliber@${tag}`)} manually to upgrade.
|
|
13070
12510
|
`
|
|
13071
12511
|
)
|
|
13072
12512
|
);
|