@rely-ai/caliber 1.38.0 → 1.40.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 +333 -96
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -1309,6 +1309,19 @@ var REFRESH_LAST_ERROR_FILE = path2.join(CALIBER_DIR, "last-refresh-error.json")
|
|
|
1309
1309
|
var MIN_SESSIONS_FOR_COMPARISON = 3;
|
|
1310
1310
|
|
|
1311
1311
|
// src/fingerprint/existing-config.ts
|
|
1312
|
+
var CALIBER_MANAGED_PREFIX = "caliber-";
|
|
1313
|
+
var INCLUDABLE_DOC_PATTERNS = [
|
|
1314
|
+
"ARCHITECTURE.md",
|
|
1315
|
+
"CONTRIBUTING.md",
|
|
1316
|
+
"DEVELOPMENT.md",
|
|
1317
|
+
"SETUP.md",
|
|
1318
|
+
"docs/ARCHITECTURE.md",
|
|
1319
|
+
"docs/CONTRIBUTING.md",
|
|
1320
|
+
"docs/DEVELOPMENT.md",
|
|
1321
|
+
"docs/API.md",
|
|
1322
|
+
"docs/GUIDE.md",
|
|
1323
|
+
"docs/SETUP.md"
|
|
1324
|
+
];
|
|
1312
1325
|
function readSkillsFromDir(skillsDir) {
|
|
1313
1326
|
if (!fs2.existsSync(skillsDir)) return void 0;
|
|
1314
1327
|
try {
|
|
@@ -1371,6 +1384,18 @@ function readExistingConfigs(dir) {
|
|
|
1371
1384
|
} catch {
|
|
1372
1385
|
}
|
|
1373
1386
|
}
|
|
1387
|
+
const claudeRulesDir = path3.join(dir, ".claude", "rules");
|
|
1388
|
+
if (fs2.existsSync(claudeRulesDir)) {
|
|
1389
|
+
try {
|
|
1390
|
+
const files = fs2.readdirSync(claudeRulesDir).filter((f) => f.endsWith(".md"));
|
|
1391
|
+
const rules = files.map((f) => ({
|
|
1392
|
+
filename: f,
|
|
1393
|
+
content: fs2.readFileSync(path3.join(claudeRulesDir, f), "utf-8")
|
|
1394
|
+
}));
|
|
1395
|
+
if (rules.length > 0) configs.claudeRules = rules;
|
|
1396
|
+
} catch {
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1374
1399
|
const cursorrulesPath = path3.join(dir, ".cursorrules");
|
|
1375
1400
|
if (fs2.existsSync(cursorrulesPath)) {
|
|
1376
1401
|
configs.cursorrules = fs2.readFileSync(cursorrulesPath, "utf-8");
|
|
@@ -1434,6 +1459,8 @@ function readExistingConfigs(dir) {
|
|
|
1434
1459
|
} catch {
|
|
1435
1460
|
}
|
|
1436
1461
|
}
|
|
1462
|
+
const found = INCLUDABLE_DOC_PATTERNS.filter((p) => fs2.existsSync(path3.join(dir, p)));
|
|
1463
|
+
if (found.length > 0) configs.includableDocs = found;
|
|
1437
1464
|
return configs;
|
|
1438
1465
|
}
|
|
1439
1466
|
|
|
@@ -2708,15 +2735,16 @@ var CLAUDE_CLI_BIN = "claude";
|
|
|
2708
2735
|
var DEFAULT_TIMEOUT_MS2 = 10 * 60 * 1e3;
|
|
2709
2736
|
var IS_WINDOWS2 = process.platform === "win32";
|
|
2710
2737
|
function spawnClaude(args) {
|
|
2738
|
+
const env = { ...process.env, CLAUDE_CODE_SIMPLE: "1" };
|
|
2711
2739
|
return IS_WINDOWS2 ? spawn2([CLAUDE_CLI_BIN, ...args].join(" "), {
|
|
2712
2740
|
cwd: process.cwd(),
|
|
2713
2741
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2714
|
-
env
|
|
2742
|
+
env,
|
|
2715
2743
|
shell: true
|
|
2716
2744
|
}) : spawn2(CLAUDE_CLI_BIN, args, {
|
|
2717
2745
|
cwd: process.cwd(),
|
|
2718
2746
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2719
|
-
env
|
|
2747
|
+
env
|
|
2720
2748
|
});
|
|
2721
2749
|
}
|
|
2722
2750
|
var ClaudeCliProvider = class {
|
|
@@ -3113,6 +3141,7 @@ var CONFIG_FILE_TYPES = `You understand these config files:
|
|
|
3113
3141
|
- .agents/skills/{name}/SKILL.md: Same OpenSkills format for Codex skills (Codex scans .agents/skills/ for skills).
|
|
3114
3142
|
- .opencode/skills/{name}/SKILL.md: Same OpenSkills format for OpenCode skills (OpenCode scans .opencode/skills/ for skills).
|
|
3115
3143
|
- .cursor/skills/{name}/SKILL.md: Same OpenSkills format for Cursor skills.
|
|
3144
|
+
- .claude/rules/*.md: Path-scoped rules for Claude Code with YAML frontmatter. Each rule file contains a \`paths:\` field with glob patterns \u2014 Claude Code only loads the rule when the user works on matching files. Use for domain-specific conventions (e.g., API patterns, test conventions, database rules). Always-apply rules omit the \`paths:\` field.
|
|
3116
3145
|
- .cursorrules: Coding rules for Cursor (deprecated legacy format \u2014 do NOT generate this).
|
|
3117
3146
|
- .cursor/rules/*.mdc: Modern Cursor rules with frontmatter (description, globs, alwaysApply).
|
|
3118
3147
|
- .github/copilot-instructions.md: Always-on repository-wide instructions for GitHub Copilot \u2014 same purpose as CLAUDE.md but for Copilot. Plain markdown, no frontmatter.
|
|
@@ -3152,6 +3181,7 @@ Skill field requirements:
|
|
|
3152
3181
|
- "name" MUST NOT be any of these reserved names (they are managed by Caliber automatically): "setup-caliber", "find-skills", "save-learning". Do NOT generate skills with these names.
|
|
3153
3182
|
- "description": MUST include WHAT it does + WHEN to use it with specific trigger phrases. Example: "Manages database migrations. Use when user says 'run migration', 'create migration', 'db schema change', or modifies files in db/migrations/."
|
|
3154
3183
|
- "content": markdown body only \u2014 do NOT include YAML frontmatter, it is generated from name+description.
|
|
3184
|
+
- "paths" (optional): array of glob patterns. When provided, the skill is only loaded when the user works on matching files. Use for skills tied to specific file types or directories. Example: ["src/api/**", "src/routes/**"] for an API skill, or ["Dockerfile*", "docker-compose*"] for a Docker skill. Omit for general-purpose skills.
|
|
3155
3185
|
|
|
3156
3186
|
Skill content structure \u2014 follow this template:
|
|
3157
3187
|
1. A heading with the skill name
|
|
@@ -3160,6 +3190,31 @@ Skill content structure \u2014 follow this template:
|
|
|
3160
3190
|
4. "## Troubleshooting" (optional) \u2014 common errors and how to fix them
|
|
3161
3191
|
|
|
3162
3192
|
Keep skill content under 200 lines. Focus on actionable instructions, not documentation prose.`;
|
|
3193
|
+
var CLAUDE_RULES_FORMAT = `CLAUDE RULES FORMAT \u2014 .claude/rules/*.md files:
|
|
3194
|
+
Claude Code loads .claude/rules/*.md files as path-scoped instructions. Generate 2-4 rules that extract domain-specific conventions from CLAUDE.md into focused, contextually-loaded files.
|
|
3195
|
+
|
|
3196
|
+
Each rule file uses YAML frontmatter with an optional \`paths:\` field:
|
|
3197
|
+
\`\`\`markdown
|
|
3198
|
+
---
|
|
3199
|
+
paths:
|
|
3200
|
+
- src/api/**
|
|
3201
|
+
- src/routes/**
|
|
3202
|
+
---
|
|
3203
|
+
|
|
3204
|
+
# API Conventions
|
|
3205
|
+
|
|
3206
|
+
- All endpoints return \`{ data, error }\` envelope
|
|
3207
|
+
- Use \`validateRequest(schema)\` middleware before handlers
|
|
3208
|
+
\`\`\`
|
|
3209
|
+
|
|
3210
|
+
Rules without \`paths:\` apply globally (use sparingly \u2014 prefer path-scoped rules).
|
|
3211
|
+
|
|
3212
|
+
Guidelines:
|
|
3213
|
+
- Extract patterns from the codebase that apply to specific file types or directories
|
|
3214
|
+
- Keep each rule under 50 lines \u2014 these are loaded into context, conciseness matters
|
|
3215
|
+
- Good candidates: testing patterns, API conventions, database query patterns, component structure
|
|
3216
|
+
- Do NOT duplicate content from CLAUDE.md \u2014 rules supplement it with path-specific detail
|
|
3217
|
+
- filename must be kebab-case ending in .md (e.g. \`testing-patterns.md\`, \`api-conventions.md\`)`;
|
|
3163
3218
|
var SCORING_CRITERIA = `SCORING CRITERIA \u2014 your output is scored deterministically against the actual filesystem. Optimize for 100/100:
|
|
3164
3219
|
|
|
3165
3220
|
Existence (25 pts):
|
|
@@ -3202,7 +3257,13 @@ For command sections, use code blocks with one command per line.
|
|
|
3202
3257
|
|
|
3203
3258
|
- Each skill content: max 150 lines. Focus on patterns and examples, not exhaustive docs.
|
|
3204
3259
|
- Cursor rules: max 5 .mdc files.
|
|
3205
|
-
- If the project is large, prioritize depth on the 3-4 most critical tools over breadth across everything
|
|
3260
|
+
- If the project is large, prioritize depth on the 3-4 most critical tools over breadth across everything.
|
|
3261
|
+
|
|
3262
|
+
@include directives (Claude Code only):
|
|
3263
|
+
- If the project has existing documentation files (ARCHITECTURE.md, CONTRIBUTING.md, API docs, etc.), reference them in CLAUDE.md using \`@./path\` instead of summarizing their content. Claude Code will inline them automatically.
|
|
3264
|
+
- Example: Instead of writing an architecture summary, add \`@./ARCHITECTURE.md\` in the relevant section.
|
|
3265
|
+
- This keeps CLAUDE.md compact while giving the agent access to detailed docs.
|
|
3266
|
+
- Only use @include for files that actually exist in the provided file tree.`;
|
|
3206
3267
|
var GENERATION_SYSTEM_PROMPT = `${ROLE_AND_CONTEXT}
|
|
3207
3268
|
|
|
3208
3269
|
${CONFIG_FILE_TYPES}
|
|
@@ -3224,18 +3285,19 @@ AgentSetup schema:
|
|
|
3224
3285
|
],
|
|
3225
3286
|
"claude": {
|
|
3226
3287
|
"claudeMd": "string (markdown content for CLAUDE.md)",
|
|
3227
|
-
"
|
|
3288
|
+
"rules": [{ "filename": "string.md (kebab-case, e.g. api-conventions.md)", "content": "string (markdown with YAML frontmatter containing paths: glob)" }],
|
|
3289
|
+
"skills": [{ "name": "string (kebab-case, matches directory name)", "description": "string (what this skill does and when to use it)", "content": "string (markdown body \u2014 NO frontmatter, it will be generated from name+description)", "paths": ["optional array of glob patterns \u2014 omit for general-purpose skills"] }]
|
|
3228
3290
|
},
|
|
3229
3291
|
"codex": {
|
|
3230
3292
|
"agentsMd": "string (markdown content for AGENTS.md \u2014 the primary Codex instructions file, same quality/structure as CLAUDE.md)",
|
|
3231
|
-
"skills": [{ "name": "string (kebab-case, matches directory name)", "description": "string (what this skill does and when to use it)", "content": "string (markdown body \u2014 NO frontmatter, it will be generated from name+description)" }]
|
|
3293
|
+
"skills": [{ "name": "string (kebab-case, matches directory name)", "description": "string (what this skill does and when to use it)", "content": "string (markdown body \u2014 NO frontmatter, it will be generated from name+description)", "paths": ["optional array of glob patterns \u2014 omit for general-purpose skills"] }]
|
|
3232
3294
|
},
|
|
3233
3295
|
"opencode": {
|
|
3234
3296
|
"agentsMd": "string (markdown content for AGENTS.md \u2014 reuse codex.agentsMd if codex is also targeted, otherwise generate fresh)",
|
|
3235
|
-
"skills": [{ "name": "string (kebab-case, matches directory name)", "description": "string (what this skill does and when to use it)", "content": "string (markdown body \u2014 NO frontmatter, it will be generated from name+description)" }]
|
|
3297
|
+
"skills": [{ "name": "string (kebab-case, matches directory name)", "description": "string (what this skill does and when to use it)", "content": "string (markdown body \u2014 NO frontmatter, it will be generated from name+description)", "paths": ["optional array of glob patterns \u2014 omit for general-purpose skills"] }]
|
|
3236
3298
|
},
|
|
3237
3299
|
"cursor": {
|
|
3238
|
-
"skills": [{ "name": "string (kebab-case, matches directory name)", "description": "string (what this skill does and when to use it)", "content": "string (markdown body \u2014 NO frontmatter, it will be generated from name+description)" }],
|
|
3300
|
+
"skills": [{ "name": "string (kebab-case, matches directory name)", "description": "string (what this skill does and when to use it)", "content": "string (markdown body \u2014 NO frontmatter, it will be generated from name+description)", "paths": ["optional array of glob patterns \u2014 omit for general-purpose skills"] }],
|
|
3239
3301
|
"rules": [{ "filename": "string.mdc", "content": "string (with frontmatter)" }]
|
|
3240
3302
|
},
|
|
3241
3303
|
"copilot": {
|
|
@@ -3248,12 +3310,15 @@ NOTE: If both "codex" and "opencode" are targeted, set opencode.agentsMd to the
|
|
|
3248
3310
|
|
|
3249
3311
|
${SKILL_FORMAT_RULES}
|
|
3250
3312
|
|
|
3313
|
+
${CLAUDE_RULES_FORMAT}
|
|
3314
|
+
|
|
3251
3315
|
${FILE_DESCRIPTIONS_RULES}
|
|
3252
3316
|
|
|
3253
3317
|
${SCORING_CRITERIA}
|
|
3254
3318
|
|
|
3255
3319
|
${OUTPUT_SIZE_CONSTRAINTS}
|
|
3256
|
-
- Skills: generate 3-6 skills per target platform based on project complexity. Each skill should cover a distinct tool, workflow, or domain \u2014 don't pad with generic skills
|
|
3320
|
+
- Skills: generate 3-6 skills per target platform based on project complexity. Each skill should cover a distinct tool, workflow, or domain \u2014 don't pad with generic skills.
|
|
3321
|
+
- Claude rules: generate 2-4 .claude/rules/*.md files when claude is targeted. Each rule should extract domain-specific patterns from the codebase.`;
|
|
3257
3322
|
var CORE_GENERATION_PROMPT = `${ROLE_AND_CONTEXT}
|
|
3258
3323
|
|
|
3259
3324
|
${CONFIG_FILE_TYPES}
|
|
@@ -3275,6 +3340,7 @@ CoreSetup schema:
|
|
|
3275
3340
|
],
|
|
3276
3341
|
"claude": {
|
|
3277
3342
|
"claudeMd": "string (markdown content for CLAUDE.md)",
|
|
3343
|
+
"rules": [{ "filename": "string.md (kebab-case, e.g. api-conventions.md)", "content": "string (markdown with YAML frontmatter containing paths: glob)" }],
|
|
3278
3344
|
"skillTopics": [{ "name": "string (kebab-case)", "description": "string (what this skill does and WHEN to use it \u2014 include trigger phrases)" }]
|
|
3279
3345
|
},
|
|
3280
3346
|
"codex": {
|
|
@@ -3308,12 +3374,15 @@ Skill topic description MUST follow this formula: [What it does] + [When to use
|
|
|
3308
3374
|
Include specific trigger phrases users would actually say. Also include negative triggers to prevent over-triggering.
|
|
3309
3375
|
Example: "Creates a new API endpoint following the project's route pattern. Handles request validation, error responses, and DB queries. Use when user says 'add endpoint', 'new route', 'create API', or adds files to src/routes/. Do NOT use for modifying existing routes."
|
|
3310
3376
|
|
|
3377
|
+
${CLAUDE_RULES_FORMAT}
|
|
3378
|
+
|
|
3311
3379
|
${FILE_DESCRIPTIONS_RULES}
|
|
3312
3380
|
|
|
3313
3381
|
${SCORING_CRITERIA}
|
|
3314
3382
|
|
|
3315
3383
|
${OUTPUT_SIZE_CONSTRAINTS}
|
|
3316
|
-
- Skill topics: 3-6 per platform based on project complexity (name + description only, no content)
|
|
3384
|
+
- Skill topics: 3-6 per platform based on project complexity (name + description only, no content).
|
|
3385
|
+
- Claude rules: generate 2-4 .claude/rules/*.md files when claude is targeted.`;
|
|
3317
3386
|
var SKILL_GENERATION_PROMPT = `You generate a single skill file for a coding agent (Claude Code, Cursor, Codex, or OpenCode).
|
|
3318
3387
|
|
|
3319
3388
|
Given project context and a skill topic, produce a focused SKILL.md body.
|
|
@@ -3343,7 +3412,7 @@ Rules:
|
|
|
3343
3412
|
Description field formula: [What it does] + [When to use it with trigger phrases] + [Key capabilities]. Include negative triggers ("Do NOT use for X") to prevent over-triggering.
|
|
3344
3413
|
|
|
3345
3414
|
Return ONLY a JSON object:
|
|
3346
|
-
{"name": "string (kebab-case)", "description": "string (what + when + capabilities + negative triggers)", "content": "string (markdown body)"}`;
|
|
3415
|
+
{"name": "string (kebab-case)", "description": "string (what + when + capabilities + negative triggers)", "content": "string (markdown body)", "paths": ["optional glob patterns \u2014 include when the skill applies to specific file types or directories, omit for general-purpose skills"]}`;
|
|
3347
3416
|
var REFINE_SYSTEM_PROMPT = `You are an expert at modifying coding agent configurations (Claude Code, Cursor, Codex, OpenCode, and GitHub Copilot).
|
|
3348
3417
|
|
|
3349
3418
|
You will receive the current AgentSetup JSON and a user request describing what to change.
|
|
@@ -3400,7 +3469,7 @@ var REFRESH_SYSTEM_PROMPT = `You are an expert at maintaining coding project doc
|
|
|
3400
3469
|
|
|
3401
3470
|
You will receive:
|
|
3402
3471
|
1. Git diffs showing what code changed
|
|
3403
|
-
2. Current contents of documentation files (CLAUDE.md, README.md, skills, cursor rules, copilot instructions)
|
|
3472
|
+
2. Current contents of documentation files (CLAUDE.md, .claude/rules/*.md, README.md, skills, cursor rules, copilot instructions)
|
|
3404
3473
|
3. Project context (languages, frameworks, file tree)
|
|
3405
3474
|
|
|
3406
3475
|
CONSERVATIVE UPDATE means:
|
|
@@ -3419,6 +3488,12 @@ Quality constraints (the output is scored deterministically):
|
|
|
3419
3488
|
- ONLY reference file paths that exist in the provided file tree \u2014 do NOT invent paths
|
|
3420
3489
|
- Preserve the existing structure (headings, bullet style, formatting)
|
|
3421
3490
|
|
|
3491
|
+
Claude rules (.claude/rules/*.md):
|
|
3492
|
+
- If the diff affects code in a domain covered by an existing rule, update that rule
|
|
3493
|
+
- If the diff introduces a new domain pattern (e.g., new API conventions, new test patterns), create a new rule file
|
|
3494
|
+
- Rules with paths: frontmatter should only contain conventions relevant to those paths
|
|
3495
|
+
- Keep rules under 50 lines \u2014 they load into context alongside other instructions
|
|
3496
|
+
|
|
3422
3497
|
Cross-agent sync:
|
|
3423
3498
|
- When a code change affects documentation, update ALL provided platform configs together.
|
|
3424
3499
|
- A renamed command, moved file, or changed convention must be reflected in every config (CLAUDE.md, AGENTS.md, copilot instructions, skills across all platforms).
|
|
@@ -3435,6 +3510,7 @@ Return a JSON object with this exact shape:
|
|
|
3435
3510
|
"updatedDocs": {
|
|
3436
3511
|
"agentsMd": "<updated content or null>",
|
|
3437
3512
|
"claudeMd": "<updated content or null>",
|
|
3513
|
+
"claudeRules": [{"filename": "name.md", "content": "full content with frontmatter"}] or null,
|
|
3438
3514
|
"readmeMd": "<updated content or null>",
|
|
3439
3515
|
"cursorrules": "<updated content or null>",
|
|
3440
3516
|
"cursorRules": [{"filename": "name.mdc", "content": "..."}] or null,
|
|
@@ -4028,6 +4104,55 @@ function removeHook() {
|
|
|
4028
4104
|
writeSettings(settings);
|
|
4029
4105
|
return { removed: true, notFound: false };
|
|
4030
4106
|
}
|
|
4107
|
+
function createScriptHook(config) {
|
|
4108
|
+
const { eventName, scriptPath, scriptContent, description } = config;
|
|
4109
|
+
const hasHook = (matchers) => matchers.some((entry) => entry.hooks?.some((h) => h.description === description));
|
|
4110
|
+
function isInstalled() {
|
|
4111
|
+
const settings = readSettings();
|
|
4112
|
+
const matchers = settings.hooks?.[eventName];
|
|
4113
|
+
return Array.isArray(matchers) && hasHook(matchers);
|
|
4114
|
+
}
|
|
4115
|
+
function install() {
|
|
4116
|
+
const settings = readSettings();
|
|
4117
|
+
if (!settings.hooks) settings.hooks = {};
|
|
4118
|
+
const matchers = settings.hooks[eventName];
|
|
4119
|
+
if (Array.isArray(matchers) && hasHook(matchers)) {
|
|
4120
|
+
return { installed: false, alreadyInstalled: true };
|
|
4121
|
+
}
|
|
4122
|
+
const scriptDir = path10.dirname(scriptPath);
|
|
4123
|
+
if (!fs10.existsSync(scriptDir)) fs10.mkdirSync(scriptDir, { recursive: true });
|
|
4124
|
+
fs10.writeFileSync(scriptPath, scriptContent);
|
|
4125
|
+
fs10.chmodSync(scriptPath, 493);
|
|
4126
|
+
if (!Array.isArray(settings.hooks[eventName])) {
|
|
4127
|
+
settings.hooks[eventName] = [];
|
|
4128
|
+
}
|
|
4129
|
+
settings.hooks[eventName].push({
|
|
4130
|
+
matcher: "",
|
|
4131
|
+
hooks: [{ type: "command", command: scriptPath, description }]
|
|
4132
|
+
});
|
|
4133
|
+
writeSettings(settings);
|
|
4134
|
+
return { installed: true, alreadyInstalled: false };
|
|
4135
|
+
}
|
|
4136
|
+
function remove() {
|
|
4137
|
+
const settings = readSettings();
|
|
4138
|
+
const matchers = settings.hooks?.[eventName];
|
|
4139
|
+
if (!Array.isArray(matchers)) return { removed: false, notFound: true };
|
|
4140
|
+
const idx = matchers.findIndex(
|
|
4141
|
+
(entry) => entry.hooks?.some((h) => h.description === description)
|
|
4142
|
+
);
|
|
4143
|
+
if (idx === -1) return { removed: false, notFound: true };
|
|
4144
|
+
matchers.splice(idx, 1);
|
|
4145
|
+
if (matchers.length === 0) delete settings.hooks[eventName];
|
|
4146
|
+
if (settings.hooks && Object.keys(settings.hooks).length === 0) delete settings.hooks;
|
|
4147
|
+
writeSettings(settings);
|
|
4148
|
+
try {
|
|
4149
|
+
fs10.unlinkSync(scriptPath);
|
|
4150
|
+
} catch {
|
|
4151
|
+
}
|
|
4152
|
+
return { removed: true, notFound: false };
|
|
4153
|
+
}
|
|
4154
|
+
return { isInstalled, install, remove };
|
|
4155
|
+
}
|
|
4031
4156
|
var STOP_HOOK_SCRIPT_CONTENT = `#!/bin/sh
|
|
4032
4157
|
if grep -q "caliber" .git/hooks/pre-commit 2>/dev/null; then
|
|
4033
4158
|
exit 0
|
|
@@ -4040,58 +4165,44 @@ fi
|
|
|
4040
4165
|
touch "$FLAG"
|
|
4041
4166
|
printf '{"decision":"block","reason":"Caliber agent config sync is not set up on this machine. Tell the user: This project uses Caliber to keep agent configs in sync \u2014 it is not set up yet. Want me to run /setup-caliber? (~30 seconds)"}'
|
|
4042
4167
|
`;
|
|
4043
|
-
var
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
const idx = stop.findIndex(
|
|
4082
|
-
(entry) => entry.hooks?.some((h) => h.description === STOP_HOOK_DESCRIPTION)
|
|
4083
|
-
);
|
|
4084
|
-
if (idx === -1) return { removed: false, notFound: true };
|
|
4085
|
-
stop.splice(idx, 1);
|
|
4086
|
-
if (stop.length === 0) delete settings.hooks.Stop;
|
|
4087
|
-
if (settings.hooks && Object.keys(settings.hooks).length === 0) delete settings.hooks;
|
|
4088
|
-
writeSettings(settings);
|
|
4089
|
-
try {
|
|
4090
|
-
fs10.unlinkSync(STOP_HOOK_SCRIPT_PATH);
|
|
4091
|
-
} catch {
|
|
4092
|
-
}
|
|
4093
|
-
return { removed: true, notFound: false };
|
|
4094
|
-
}
|
|
4168
|
+
var stopHook = createScriptHook({
|
|
4169
|
+
eventName: "Stop",
|
|
4170
|
+
scriptPath: path10.join(".claude", "hooks", "caliber-check-sync.sh"),
|
|
4171
|
+
scriptContent: STOP_HOOK_SCRIPT_CONTENT,
|
|
4172
|
+
description: "Caliber: offer setup if not configured"
|
|
4173
|
+
});
|
|
4174
|
+
var installStopHook = stopHook.install;
|
|
4175
|
+
var removeStopHook = stopHook.remove;
|
|
4176
|
+
var FRESHNESS_SCRIPT = `#!/bin/sh
|
|
4177
|
+
STATE_FILE=".caliber/.caliber-state.json"
|
|
4178
|
+
[ ! -f "$STATE_FILE" ] && exit 0
|
|
4179
|
+
LAST_SHA=$(grep -o '"lastRefreshSha":"[^"]*"' "$STATE_FILE" 2>/dev/null | cut -d'"' -f4)
|
|
4180
|
+
[ -z "$LAST_SHA" ] && exit 0
|
|
4181
|
+
CURRENT_SHA=$(git rev-parse HEAD 2>/dev/null)
|
|
4182
|
+
[ "$LAST_SHA" = "$CURRENT_SHA" ] && exit 0
|
|
4183
|
+
COMMITS_BEHIND=$(git rev-list --count "$LAST_SHA".."$CURRENT_SHA" 2>/dev/null || echo 0)
|
|
4184
|
+
if [ "$COMMITS_BEHIND" -gt 15 ]; then
|
|
4185
|
+
printf '{"systemMessage":"Caliber: agent configs are %s commits behind. Run caliber refresh to sync."}' "$COMMITS_BEHIND"
|
|
4186
|
+
fi
|
|
4187
|
+
`;
|
|
4188
|
+
var sessionStartHook = createScriptHook({
|
|
4189
|
+
eventName: "SessionStart",
|
|
4190
|
+
scriptPath: path10.join(".claude", "hooks", "caliber-session-freshness.sh"),
|
|
4191
|
+
scriptContent: FRESHNESS_SCRIPT,
|
|
4192
|
+
description: "Caliber: check config freshness on session start"
|
|
4193
|
+
});
|
|
4194
|
+
var isSessionStartHookInstalled = sessionStartHook.isInstalled;
|
|
4195
|
+
var installSessionStartHook = sessionStartHook.install;
|
|
4196
|
+
var removeSessionStartHook = sessionStartHook.remove;
|
|
4197
|
+
var notificationHook = createScriptHook({
|
|
4198
|
+
eventName: "Notification",
|
|
4199
|
+
scriptPath: path10.join(".claude", "hooks", "caliber-freshness-notify.sh"),
|
|
4200
|
+
scriptContent: FRESHNESS_SCRIPT,
|
|
4201
|
+
description: "Caliber: warn when agent configs are stale"
|
|
4202
|
+
});
|
|
4203
|
+
var isNotificationHookInstalled = notificationHook.isInstalled;
|
|
4204
|
+
var installNotificationHook = notificationHook.install;
|
|
4205
|
+
var removeNotificationHook = notificationHook.remove;
|
|
4095
4206
|
var PRECOMMIT_START = "# caliber:pre-commit:start";
|
|
4096
4207
|
var PRECOMMIT_END = "# caliber:pre-commit:end";
|
|
4097
4208
|
function getPrecommitBlock() {
|
|
@@ -4972,7 +5083,8 @@ Generate the skill content following the instructions in the system prompt.`;
|
|
|
4972
5083
|
return {
|
|
4973
5084
|
name: result.name || topic.name,
|
|
4974
5085
|
description: result.description || topic.description,
|
|
4975
|
-
content
|
|
5086
|
+
content,
|
|
5087
|
+
...result.paths?.length ? { paths: result.paths } : {}
|
|
4976
5088
|
};
|
|
4977
5089
|
}
|
|
4978
5090
|
async function streamGeneration(config) {
|
|
@@ -5188,7 +5300,7 @@ function sampleFileTree(fileTree, codeAnalysisPaths, limit) {
|
|
|
5188
5300
|
function buildGeneratePrompt(fingerprint, targetAgent, prompt, failingChecks, currentScore, passingChecks) {
|
|
5189
5301
|
const parts = [];
|
|
5190
5302
|
const existing = fingerprint.existingConfigs;
|
|
5191
|
-
const hasExistingConfigs = !!(existing.claudeMd || existing.claudeSettings || existing.claudeSkills?.length || existing.readmeMd || existing.agentsMd || existing.cursorrules || existing.cursorRules?.length);
|
|
5303
|
+
const hasExistingConfigs = !!(existing.claudeMd || existing.claudeSettings || existing.claudeSkills?.length || existing.claudeRules?.length || existing.readmeMd || existing.agentsMd || existing.cursorrules || existing.cursorRules?.length);
|
|
5192
5304
|
const isTargetedFix = failingChecks && failingChecks.length > 0 && currentScore !== void 0 && currentScore >= 95;
|
|
5193
5305
|
if (isTargetedFix) {
|
|
5194
5306
|
parts.push(`TARGETED FIX MODE \u2014 current score: ${currentScore}/100, target: ${targetAgent}`);
|
|
@@ -5280,6 +5392,16 @@ ${truncate(skill.content, LIMITS.SKILL_CHARS)}`
|
|
|
5280
5392
|
(${existing.claudeSkills.length - LIMITS.SKILLS_MAX} more skills omitted)`);
|
|
5281
5393
|
}
|
|
5282
5394
|
}
|
|
5395
|
+
if (existing.claudeRules?.length) {
|
|
5396
|
+
parts.push("\n--- Existing Claude Rules ---");
|
|
5397
|
+
for (const rule of existing.claudeRules.slice(0, LIMITS.RULES_MAX)) {
|
|
5398
|
+
parts.push(
|
|
5399
|
+
`
|
|
5400
|
+
[.claude/rules/${rule.filename}]
|
|
5401
|
+
${truncate(rule.content, LIMITS.SKILL_CHARS)}`
|
|
5402
|
+
);
|
|
5403
|
+
}
|
|
5404
|
+
}
|
|
5283
5405
|
if (existing.cursorrules)
|
|
5284
5406
|
parts.push(
|
|
5285
5407
|
`
|
|
@@ -5327,6 +5449,13 @@ ${existing.personalLearnings}`
|
|
|
5327
5449
|
Project dependencies (${allDeps.length}):`);
|
|
5328
5450
|
parts.push(allDeps.join(", "));
|
|
5329
5451
|
}
|
|
5452
|
+
if (existing.includableDocs?.length) {
|
|
5453
|
+
parts.push("\n--- Existing Documentation Files (use @include) ---");
|
|
5454
|
+
parts.push("These files exist and can be referenced in CLAUDE.md using @./path:");
|
|
5455
|
+
for (const doc of existing.includableDocs) {
|
|
5456
|
+
parts.push(`- ${doc}`);
|
|
5457
|
+
}
|
|
5458
|
+
}
|
|
5330
5459
|
if (prompt) parts.push(`
|
|
5331
5460
|
User instructions: ${prompt}`);
|
|
5332
5461
|
if (fingerprint.codeAnalysis) {
|
|
@@ -5390,18 +5519,29 @@ function writeClaudeConfig(config) {
|
|
|
5390
5519
|
appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(config.claudeMd)))
|
|
5391
5520
|
);
|
|
5392
5521
|
written.push("CLAUDE.md");
|
|
5522
|
+
if (config.rules?.length) {
|
|
5523
|
+
const rulesDir = path12.join(".claude", "rules");
|
|
5524
|
+
if (!fs12.existsSync(rulesDir)) fs12.mkdirSync(rulesDir, { recursive: true });
|
|
5525
|
+
for (const rule of config.rules) {
|
|
5526
|
+
const rulePath = path12.join(rulesDir, rule.filename);
|
|
5527
|
+
fs12.writeFileSync(rulePath, rule.content);
|
|
5528
|
+
written.push(rulePath);
|
|
5529
|
+
}
|
|
5530
|
+
}
|
|
5393
5531
|
if (config.skills?.length) {
|
|
5394
5532
|
for (const skill of config.skills) {
|
|
5395
5533
|
const skillDir = path12.join(".claude", "skills", skill.name);
|
|
5396
5534
|
if (!fs12.existsSync(skillDir)) fs12.mkdirSync(skillDir, { recursive: true });
|
|
5397
5535
|
const skillPath = path12.join(skillDir, "SKILL.md");
|
|
5398
|
-
const
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5536
|
+
const frontmatterLines = ["---", `name: ${skill.name}`, `description: ${skill.description}`];
|
|
5537
|
+
if (skill.paths?.length) {
|
|
5538
|
+
frontmatterLines.push("paths:");
|
|
5539
|
+
for (const p of skill.paths) {
|
|
5540
|
+
frontmatterLines.push(` - ${p}`);
|
|
5541
|
+
}
|
|
5542
|
+
}
|
|
5543
|
+
frontmatterLines.push("---", "");
|
|
5544
|
+
const frontmatter = frontmatterLines.join("\n");
|
|
5405
5545
|
fs12.writeFileSync(skillPath, frontmatter + skill.content);
|
|
5406
5546
|
written.push(skillPath);
|
|
5407
5547
|
}
|
|
@@ -5688,6 +5828,9 @@ function getFilesToWrite(setup) {
|
|
|
5688
5828
|
if (setup.targetAgent.includes("claude") && setup.claude) {
|
|
5689
5829
|
files.push("CLAUDE.md");
|
|
5690
5830
|
if (setup.claude.mcpServers) files.push(".mcp.json");
|
|
5831
|
+
if (setup.claude.rules) {
|
|
5832
|
+
for (const r of setup.claude.rules) files.push(`.claude/rules/${r.filename}`);
|
|
5833
|
+
}
|
|
5691
5834
|
if (setup.claude.skills) {
|
|
5692
5835
|
for (const s of setup.claude.skills) {
|
|
5693
5836
|
files.push(`.claude/skills/${s.name.replace(/[^a-z0-9-]/gi, "-").toLowerCase()}/SKILL.md`);
|
|
@@ -6230,6 +6373,7 @@ var POINTS_SKILLS_BONUS_PER_EXTRA = 1;
|
|
|
6230
6373
|
var POINTS_SKILLS_BONUS_CAP = 2;
|
|
6231
6374
|
var POINTS_CURSOR_MDC_RULES = 3;
|
|
6232
6375
|
var POINTS_MCP_SERVERS = 3;
|
|
6376
|
+
var POINTS_CLAUDE_RULES = 3;
|
|
6233
6377
|
var POINTS_CROSS_PLATFORM_PARITY = 2;
|
|
6234
6378
|
var POINTS_EXECUTABLE_CONTENT = 8;
|
|
6235
6379
|
var POINTS_CONCISE_CONFIG = 6;
|
|
@@ -6299,7 +6443,11 @@ var SECRET_PLACEHOLDER_PATTERNS = [
|
|
|
6299
6443
|
/<[^>]+>/
|
|
6300
6444
|
];
|
|
6301
6445
|
var CURSOR_ONLY_CHECKS = /* @__PURE__ */ new Set(["cursor_rules_exist", "cursor_mdc_rules"]);
|
|
6302
|
-
var CLAUDE_ONLY_CHECKS = /* @__PURE__ */ new Set([
|
|
6446
|
+
var CLAUDE_ONLY_CHECKS = /* @__PURE__ */ new Set([
|
|
6447
|
+
"claude_md_exists",
|
|
6448
|
+
"claude_md_freshness",
|
|
6449
|
+
"claude_rules_exist"
|
|
6450
|
+
]);
|
|
6303
6451
|
var BOTH_ONLY_CHECKS = /* @__PURE__ */ new Set(["cross_platform_parity", "no_duplicate_content"]);
|
|
6304
6452
|
var CODEX_ONLY_CHECKS = /* @__PURE__ */ new Set(["codex_agents_md_exists"]);
|
|
6305
6453
|
var COPILOT_ONLY_CHECKS = /* @__PURE__ */ new Set(["copilot_instructions_exists"]);
|
|
@@ -6368,6 +6516,32 @@ function checkExistence(dir) {
|
|
|
6368
6516
|
instruction: "Create CLAUDE.md with project context, commands, architecture, and conventions."
|
|
6369
6517
|
}
|
|
6370
6518
|
});
|
|
6519
|
+
const claudeRulesDir = join3(dir, ".claude", "rules");
|
|
6520
|
+
let claudeRuleFiles = [];
|
|
6521
|
+
if (existsSync3(claudeRulesDir)) {
|
|
6522
|
+
try {
|
|
6523
|
+
claudeRuleFiles = readdirSync2(claudeRulesDir).filter(
|
|
6524
|
+
(f) => f.endsWith(".md") && !f.startsWith(CALIBER_MANAGED_PREFIX)
|
|
6525
|
+
);
|
|
6526
|
+
} catch {
|
|
6527
|
+
}
|
|
6528
|
+
}
|
|
6529
|
+
const hasClaudeRules = claudeRuleFiles.length > 0;
|
|
6530
|
+
checks.push({
|
|
6531
|
+
id: "claude_rules_exist",
|
|
6532
|
+
name: "Claude rules exist (.claude/rules/)",
|
|
6533
|
+
category: "existence",
|
|
6534
|
+
maxPoints: POINTS_CLAUDE_RULES,
|
|
6535
|
+
earnedPoints: hasClaudeRules ? POINTS_CLAUDE_RULES : 0,
|
|
6536
|
+
passed: hasClaudeRules,
|
|
6537
|
+
detail: hasClaudeRules ? `${claudeRuleFiles.length} rule${claudeRuleFiles.length === 1 ? "" : "s"} found` : "No .claude/rules/*.md files",
|
|
6538
|
+
suggestion: hasClaudeRules ? void 0 : "Add .claude/rules/*.md with path-scoped conventions for better context efficiency",
|
|
6539
|
+
fix: hasClaudeRules ? void 0 : {
|
|
6540
|
+
action: "create_file",
|
|
6541
|
+
data: { file: ".claude/rules/" },
|
|
6542
|
+
instruction: "Create .claude/rules/ with path-scoped markdown rules (e.g., testing-patterns.md, api-conventions.md)."
|
|
6543
|
+
}
|
|
6544
|
+
});
|
|
6371
6545
|
const hasCursorrules = existsSync3(join3(dir, ".cursorrules"));
|
|
6372
6546
|
const cursorRulesDir = existsSync3(join3(dir, ".cursor", "rules"));
|
|
6373
6547
|
const cursorRulesExist = hasCursorrules || cursorRulesDir;
|
|
@@ -10056,6 +10230,8 @@ async function initCommand(options) {
|
|
|
10056
10230
|
}
|
|
10057
10231
|
installStopHook();
|
|
10058
10232
|
console.log(` ${chalk14.green("\u2713")} Onboarding hook \u2014 nudges new team members to set up`);
|
|
10233
|
+
installSessionStartHook();
|
|
10234
|
+
console.log(` ${chalk14.green("\u2713")} Freshness hook \u2014 warns when configs are stale`);
|
|
10059
10235
|
const { ensureBuiltinSkills: ensureBuiltinSkills2 } = await Promise.resolve().then(() => (init_builtin_skills(), builtin_skills_exports));
|
|
10060
10236
|
for (const agent of targetAgent) {
|
|
10061
10237
|
if (agent === "claude" && !fs34.existsSync(".claude"))
|
|
@@ -11169,6 +11345,14 @@ function scopeDiffToDir(diff, dir, allConfigDirs) {
|
|
|
11169
11345
|
init_pre_commit_block();
|
|
11170
11346
|
import fs37 from "fs";
|
|
11171
11347
|
import path30 from "path";
|
|
11348
|
+
function writeFileGroup(groupDir, files) {
|
|
11349
|
+
fs37.mkdirSync(groupDir, { recursive: true });
|
|
11350
|
+
return files.map((file) => {
|
|
11351
|
+
const filePath = path30.join(groupDir, file.filename);
|
|
11352
|
+
fs37.writeFileSync(filePath, file.content);
|
|
11353
|
+
return filePath.replace(/\\/g, "/");
|
|
11354
|
+
});
|
|
11355
|
+
}
|
|
11172
11356
|
function writeRefreshDocs(docs, dir = ".") {
|
|
11173
11357
|
const written = [];
|
|
11174
11358
|
const p = (relPath) => (dir === "." ? relPath : path30.join(dir, relPath)).replace(/\\/g, "/");
|
|
@@ -11188,6 +11372,9 @@ function writeRefreshDocs(docs, dir = ".") {
|
|
|
11188
11372
|
fs37.writeFileSync(filePath, appendManagedBlocks(docs.claudeMd));
|
|
11189
11373
|
written.push(filePath);
|
|
11190
11374
|
}
|
|
11375
|
+
if (docs.claudeRules) {
|
|
11376
|
+
written.push(...writeFileGroup(p(path30.join(".claude", "rules")), docs.claudeRules));
|
|
11377
|
+
}
|
|
11191
11378
|
if (docs.readmeMd) {
|
|
11192
11379
|
const filePath = p("README.md");
|
|
11193
11380
|
ensureParent(filePath);
|
|
@@ -11201,12 +11388,7 @@ function writeRefreshDocs(docs, dir = ".") {
|
|
|
11201
11388
|
written.push(filePath);
|
|
11202
11389
|
}
|
|
11203
11390
|
if (docs.cursorRules) {
|
|
11204
|
-
|
|
11205
|
-
if (!fs37.existsSync(rulesDir)) fs37.mkdirSync(rulesDir, { recursive: true });
|
|
11206
|
-
for (const rule of docs.cursorRules) {
|
|
11207
|
-
fs37.writeFileSync(path30.join(rulesDir, rule.filename), rule.content);
|
|
11208
|
-
written.push(p(path30.join(".cursor", "rules", rule.filename)));
|
|
11209
|
-
}
|
|
11391
|
+
written.push(...writeFileGroup(p(path30.join(".cursor", "rules")), docs.cursorRules));
|
|
11210
11392
|
}
|
|
11211
11393
|
if (docs.copilotInstructions) {
|
|
11212
11394
|
const filePath = p(path30.join(".github", "copilot-instructions.md"));
|
|
@@ -11215,12 +11397,9 @@ function writeRefreshDocs(docs, dir = ".") {
|
|
|
11215
11397
|
written.push(filePath);
|
|
11216
11398
|
}
|
|
11217
11399
|
if (docs.copilotInstructionFiles) {
|
|
11218
|
-
|
|
11219
|
-
|
|
11220
|
-
|
|
11221
|
-
fs37.writeFileSync(path30.join(instructionsDir, file.filename), file.content);
|
|
11222
|
-
written.push(p(path30.join(".github", "instructions", file.filename)));
|
|
11223
|
-
}
|
|
11400
|
+
written.push(
|
|
11401
|
+
...writeFileGroup(p(path30.join(".github", "instructions")), docs.copilotInstructionFiles)
|
|
11402
|
+
);
|
|
11224
11403
|
}
|
|
11225
11404
|
return written;
|
|
11226
11405
|
}
|
|
@@ -11307,9 +11486,17 @@ Changed files: ${diff.changedFiles.join(", ")}`);
|
|
|
11307
11486
|
parts.push(skill.content);
|
|
11308
11487
|
}
|
|
11309
11488
|
}
|
|
11489
|
+
if (existingDocs.claudeRules?.length) {
|
|
11490
|
+
for (const rule of existingDocs.claudeRules) {
|
|
11491
|
+
if (rule.filename.startsWith(CALIBER_MANAGED_PREFIX)) continue;
|
|
11492
|
+
parts.push(`
|
|
11493
|
+
[.claude/rules/${rule.filename}]`);
|
|
11494
|
+
parts.push(rule.content);
|
|
11495
|
+
}
|
|
11496
|
+
}
|
|
11310
11497
|
if (existingDocs.cursorRules?.length) {
|
|
11311
11498
|
for (const rule of existingDocs.cursorRules) {
|
|
11312
|
-
if (rule.filename.startsWith(
|
|
11499
|
+
if (rule.filename.startsWith(CALIBER_MANAGED_PREFIX)) continue;
|
|
11313
11500
|
parts.push(`
|
|
11314
11501
|
[.cursor/rules/${rule.filename}]`);
|
|
11315
11502
|
parts.push(rule.content);
|
|
@@ -11326,6 +11513,16 @@ Changed files: ${diff.changedFiles.join(", ")}`);
|
|
|
11326
11513
|
parts.push(file.content);
|
|
11327
11514
|
}
|
|
11328
11515
|
}
|
|
11516
|
+
if (existingDocs.includableDocs?.length) {
|
|
11517
|
+
parts.push(`
|
|
11518
|
+
--- Existing Documentation Files (use @include) ---`);
|
|
11519
|
+
parts.push(
|
|
11520
|
+
"These files exist in the project and can be referenced in CLAUDE.md using @./path:"
|
|
11521
|
+
);
|
|
11522
|
+
for (const doc of existingDocs.includableDocs) {
|
|
11523
|
+
parts.push(`- ${doc}`);
|
|
11524
|
+
}
|
|
11525
|
+
}
|
|
11329
11526
|
if (learnedSection) {
|
|
11330
11527
|
parts.push("\n--- Learned Patterns (from session learning) ---");
|
|
11331
11528
|
parts.push("Consider these accumulated learnings when deciding what to update:");
|
|
@@ -11669,6 +11866,10 @@ function collectFilesToWrite(updatedDocs, dir = ".") {
|
|
|
11669
11866
|
const p = (relPath) => (dir === "." ? relPath : path34.join(dir, relPath)).replace(/\\/g, "/");
|
|
11670
11867
|
if (updatedDocs.agentsMd) files.push(p("AGENTS.md"));
|
|
11671
11868
|
if (updatedDocs.claudeMd) files.push(p("CLAUDE.md"));
|
|
11869
|
+
if (Array.isArray(updatedDocs.claudeRules)) {
|
|
11870
|
+
for (const r of updatedDocs.claudeRules)
|
|
11871
|
+
files.push(p(`.claude/rules/${r.filename}`));
|
|
11872
|
+
}
|
|
11672
11873
|
if (updatedDocs.readmeMd) files.push(p("README.md"));
|
|
11673
11874
|
if (updatedDocs.cursorrules) files.push(p(".cursorrules"));
|
|
11674
11875
|
if (Array.isArray(updatedDocs.cursorRules)) {
|
|
@@ -11964,6 +12165,22 @@ var HOOKS = [
|
|
|
11964
12165
|
isInstalled: isPreCommitHookInstalled,
|
|
11965
12166
|
install: installPreCommitHook,
|
|
11966
12167
|
remove: removePreCommitHook
|
|
12168
|
+
},
|
|
12169
|
+
{
|
|
12170
|
+
id: "session-start",
|
|
12171
|
+
label: "Claude Code SessionStart",
|
|
12172
|
+
description: "Check config freshness when a session starts",
|
|
12173
|
+
isInstalled: isSessionStartHookInstalled,
|
|
12174
|
+
install: installSessionStartHook,
|
|
12175
|
+
remove: removeSessionStartHook
|
|
12176
|
+
},
|
|
12177
|
+
{
|
|
12178
|
+
id: "notification",
|
|
12179
|
+
label: "Claude Code Notification",
|
|
12180
|
+
description: "Warn when agent configs are stale (>15 commits behind)",
|
|
12181
|
+
isInstalled: isNotificationHookInstalled,
|
|
12182
|
+
install: installNotificationHook,
|
|
12183
|
+
remove: removeNotificationHook
|
|
11967
12184
|
}
|
|
11968
12185
|
];
|
|
11969
12186
|
function printStatus() {
|
|
@@ -11979,8 +12196,12 @@ function printStatus() {
|
|
|
11979
12196
|
}
|
|
11980
12197
|
async function hooksCommand(options) {
|
|
11981
12198
|
if (!options.install && !options.remove) {
|
|
11982
|
-
console.log(
|
|
11983
|
-
|
|
12199
|
+
console.log(
|
|
12200
|
+
chalk20.dim("\n Note: caliber now adds refresh instructions directly to config files.")
|
|
12201
|
+
);
|
|
12202
|
+
console.log(
|
|
12203
|
+
chalk20.dim(" These hooks are available for non-agent workflows (manual commits).\n")
|
|
12204
|
+
);
|
|
11984
12205
|
}
|
|
11985
12206
|
if (options.install) {
|
|
11986
12207
|
for (const hook of HOOKS) {
|
|
@@ -13611,12 +13832,13 @@ var MANAGED_DOC_FILES = [
|
|
|
13611
13832
|
];
|
|
13612
13833
|
var SKILL_DIRS = PLATFORM_CONFIGS.map((c) => c.skillsDir);
|
|
13613
13834
|
var CURSOR_RULES_DIR = path41.join(".cursor", "rules");
|
|
13614
|
-
|
|
13835
|
+
var CLAUDE_RULES_DIR = path41.join(".claude", "rules");
|
|
13836
|
+
function removeCaliberManagedFiles(dir, extension) {
|
|
13615
13837
|
const removed = [];
|
|
13616
|
-
if (!fs50.existsSync(
|
|
13617
|
-
for (const file of fs50.readdirSync(
|
|
13618
|
-
if (file.startsWith(
|
|
13619
|
-
const fullPath = path41.join(
|
|
13838
|
+
if (!fs50.existsSync(dir)) return removed;
|
|
13839
|
+
for (const file of fs50.readdirSync(dir)) {
|
|
13840
|
+
if (file.startsWith(CALIBER_MANAGED_PREFIX) && file.endsWith(extension)) {
|
|
13841
|
+
const fullPath = path41.join(dir, file);
|
|
13620
13842
|
fs50.unlinkSync(fullPath);
|
|
13621
13843
|
removed.push(fullPath);
|
|
13622
13844
|
}
|
|
@@ -13689,6 +13911,16 @@ async function uninstallCommand(options) {
|
|
|
13689
13911
|
console.log(` ${chalk28.red("\u2717")} Onboarding hook removed`);
|
|
13690
13912
|
actions.push("onboarding hook");
|
|
13691
13913
|
}
|
|
13914
|
+
const notificationHookResult = removeNotificationHook();
|
|
13915
|
+
if (notificationHookResult.removed) {
|
|
13916
|
+
console.log(` ${chalk28.red("\u2717")} Notification hook removed`);
|
|
13917
|
+
actions.push("notification hook");
|
|
13918
|
+
}
|
|
13919
|
+
const sessionStartResult = removeSessionStartHook();
|
|
13920
|
+
if (sessionStartResult.removed) {
|
|
13921
|
+
console.log(` ${chalk28.red("\u2717")} SessionStart hook removed`);
|
|
13922
|
+
actions.push("session-start hook");
|
|
13923
|
+
}
|
|
13692
13924
|
const learnResult = removeLearningHooks();
|
|
13693
13925
|
if (learnResult.removed) {
|
|
13694
13926
|
console.log(` ${chalk28.red("\u2717")} Claude Code learning hooks removed`);
|
|
@@ -13704,11 +13936,16 @@ async function uninstallCommand(options) {
|
|
|
13704
13936
|
console.log(` ${chalk28.yellow("~")} ${file} \u2014 managed blocks removed`);
|
|
13705
13937
|
actions.push(file);
|
|
13706
13938
|
}
|
|
13707
|
-
const
|
|
13708
|
-
for (const rule of
|
|
13939
|
+
const removedCursorRules = removeCaliberManagedFiles(CURSOR_RULES_DIR, ".mdc");
|
|
13940
|
+
for (const rule of removedCursorRules) {
|
|
13941
|
+
console.log(` ${chalk28.red("\u2717")} ${rule}`);
|
|
13942
|
+
}
|
|
13943
|
+
if (removedCursorRules.length > 0) actions.push("cursor rules");
|
|
13944
|
+
const removedClaudeRules = removeCaliberManagedFiles(CLAUDE_RULES_DIR, ".md");
|
|
13945
|
+
for (const rule of removedClaudeRules) {
|
|
13709
13946
|
console.log(` ${chalk28.red("\u2717")} ${rule}`);
|
|
13710
13947
|
}
|
|
13711
|
-
if (
|
|
13948
|
+
if (removedClaudeRules.length > 0) actions.push("claude rules");
|
|
13712
13949
|
const removedSkills = removeBuiltinSkills();
|
|
13713
13950
|
for (const skill of removedSkills) {
|
|
13714
13951
|
console.log(` ${chalk28.red("\u2717")} ${skill}/`);
|
package/package.json
CHANGED