@rely-ai/caliber 1.34.0-dev.1774825768 → 1.35.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.
Files changed (2) hide show
  1. package/dist/bin.js +532 -398
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -443,8 +443,8 @@ __export(builtin_skills_exports, {
443
443
  buildSkillContent: () => buildSkillContent,
444
444
  ensureBuiltinSkills: () => ensureBuiltinSkills
445
445
  });
446
- import fs17 from "fs";
447
- import path17 from "path";
446
+ import fs16 from "fs";
447
+ import path16 from "path";
448
448
  function buildSkillContent(skill) {
449
449
  const frontmatter = `---
450
450
  name: ${skill.name}
@@ -777,11 +777,11 @@ From now on, every commit keeps all your agent configs in sync automatically.
777
777
  function ensureBuiltinSkills() {
778
778
  const written = [];
779
779
  for (const { platformDir, skillsDir } of PLATFORM_CONFIGS) {
780
- if (!fs17.existsSync(platformDir)) continue;
780
+ if (!fs16.existsSync(platformDir)) continue;
781
781
  for (const skill of BUILTIN_SKILLS) {
782
- const skillPath = path17.join(skillsDir, skill.name, "SKILL.md");
783
- fs17.mkdirSync(path17.dirname(skillPath), { recursive: true });
784
- fs17.writeFileSync(skillPath, buildSkillContent(skill));
782
+ const skillPath = path16.join(skillsDir, skill.name, "SKILL.md");
783
+ fs16.mkdirSync(path16.dirname(skillPath), { recursive: true });
784
+ fs16.writeFileSync(skillPath, buildSkillContent(skill));
785
785
  written.push(skillPath);
786
786
  }
787
787
  }
@@ -815,9 +815,10 @@ var init_builtin_skills = __esm({
815
815
  };
816
816
  BUILTIN_SKILLS = [FIND_SKILLS_SKILL, SAVE_LEARNING_SKILL, SETUP_CALIBER_SKILL];
817
817
  PLATFORM_CONFIGS = [
818
- { platformDir: ".claude", skillsDir: path17.join(".claude", "skills") },
819
- { platformDir: ".cursor", skillsDir: path17.join(".cursor", "skills") },
820
- { platformDir: ".agents", skillsDir: path17.join(".agents", "skills") }
818
+ { platformDir: ".claude", skillsDir: path16.join(".claude", "skills") },
819
+ { platformDir: ".cursor", skillsDir: path16.join(".cursor", "skills") },
820
+ { platformDir: ".agents", skillsDir: path16.join(".agents", "skills") },
821
+ { platformDir: ".opencode", skillsDir: path16.join(".opencode", "skills") }
821
822
  ];
822
823
  BUILTIN_SKILL_NAMES = new Set(BUILTIN_SKILLS.map((s) => s.name));
823
824
  }
@@ -825,13 +826,13 @@ var init_builtin_skills = __esm({
825
826
 
826
827
  // src/utils/editor.ts
827
828
  import { execSync as execSync14, spawn as spawn3 } from "child_process";
828
- import fs27 from "fs";
829
- import path24 from "path";
829
+ import fs28 from "fs";
830
+ import path25 from "path";
830
831
  import os6 from "os";
831
832
  function getEmptyFilePath(proposedPath) {
832
- fs27.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
833
- const tempPath = path24.join(DIFF_TEMP_DIR, path24.basename(proposedPath));
834
- fs27.writeFileSync(tempPath, "");
833
+ fs28.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
834
+ const tempPath = path25.join(DIFF_TEMP_DIR, path25.basename(proposedPath));
835
+ fs28.writeFileSync(tempPath, "");
835
836
  return tempPath;
836
837
  }
837
838
  function commandExists(cmd) {
@@ -871,7 +872,7 @@ var init_editor = __esm({
871
872
  "src/utils/editor.ts"() {
872
873
  "use strict";
873
874
  IS_WINDOWS3 = process.platform === "win32";
874
- DIFF_TEMP_DIR = path24.join(os6.tmpdir(), "caliber-diff");
875
+ DIFF_TEMP_DIR = path25.join(os6.tmpdir(), "caliber-diff");
875
876
  }
876
877
  });
877
878
 
@@ -883,7 +884,7 @@ __export(review_exports, {
883
884
  promptWantsReview: () => promptWantsReview
884
885
  });
885
886
  import chalk10 from "chalk";
886
- import fs28 from "fs";
887
+ import fs29 from "fs";
887
888
  import select4 from "@inquirer/select";
888
889
  import { createTwoFilesPatch } from "diff";
889
890
  async function promptWantsReview() {
@@ -920,8 +921,8 @@ async function openReview(method, stagedFiles) {
920
921
  return;
921
922
  }
922
923
  const fileInfos = stagedFiles.map((file) => {
923
- const proposed = fs28.readFileSync(file.proposedPath, "utf-8");
924
- const current = file.currentPath ? fs28.readFileSync(file.currentPath, "utf-8") : "";
924
+ const proposed = fs29.readFileSync(file.proposedPath, "utf-8");
925
+ const current = file.currentPath ? fs29.readFileSync(file.currentPath, "utf-8") : "";
925
926
  const patch = createTwoFilesPatch(
926
927
  file.isNew ? "/dev/null" : file.relativePath,
927
928
  file.relativePath,
@@ -1096,13 +1097,13 @@ __export(lock_exports, {
1096
1097
  isCaliberRunning: () => isCaliberRunning,
1097
1098
  releaseLock: () => releaseLock
1098
1099
  });
1099
- import fs38 from "fs";
1100
- import path31 from "path";
1100
+ import fs39 from "fs";
1101
+ import path32 from "path";
1101
1102
  import os8 from "os";
1102
1103
  function isCaliberRunning() {
1103
1104
  try {
1104
- if (!fs38.existsSync(LOCK_FILE)) return false;
1105
- const raw = fs38.readFileSync(LOCK_FILE, "utf-8").trim();
1105
+ if (!fs39.existsSync(LOCK_FILE)) return false;
1106
+ const raw = fs39.readFileSync(LOCK_FILE, "utf-8").trim();
1106
1107
  const { pid, ts } = JSON.parse(raw);
1107
1108
  if (Date.now() - ts > STALE_MS) return false;
1108
1109
  try {
@@ -1117,13 +1118,13 @@ function isCaliberRunning() {
1117
1118
  }
1118
1119
  function acquireLock() {
1119
1120
  try {
1120
- fs38.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, ts: Date.now() }));
1121
+ fs39.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, ts: Date.now() }));
1121
1122
  } catch {
1122
1123
  }
1123
1124
  }
1124
1125
  function releaseLock() {
1125
1126
  try {
1126
- if (fs38.existsSync(LOCK_FILE)) fs38.unlinkSync(LOCK_FILE);
1127
+ if (fs39.existsSync(LOCK_FILE)) fs39.unlinkSync(LOCK_FILE);
1127
1128
  } catch {
1128
1129
  }
1129
1130
  }
@@ -1131,21 +1132,21 @@ var LOCK_FILE, STALE_MS;
1131
1132
  var init_lock = __esm({
1132
1133
  "src/lib/lock.ts"() {
1133
1134
  "use strict";
1134
- LOCK_FILE = path31.join(os8.tmpdir(), ".caliber.lock");
1135
+ LOCK_FILE = path32.join(os8.tmpdir(), ".caliber.lock");
1135
1136
  STALE_MS = 10 * 60 * 1e3;
1136
1137
  }
1137
1138
  });
1138
1139
 
1139
1140
  // src/cli.ts
1140
1141
  import { Command } from "commander";
1141
- import fs49 from "fs";
1142
- import path40 from "path";
1142
+ import fs50 from "fs";
1143
+ import path41 from "path";
1143
1144
  import { fileURLToPath } from "url";
1144
1145
 
1145
1146
  // src/commands/init.ts
1146
- import path27 from "path";
1147
+ import path28 from "path";
1147
1148
  import chalk14 from "chalk";
1148
- import fs33 from "fs";
1149
+ import fs34 from "fs";
1149
1150
 
1150
1151
  // src/fingerprint/index.ts
1151
1152
  import fs8 from "fs";
@@ -3007,7 +3008,7 @@ async function validateModel(options) {
3007
3008
  init_config();
3008
3009
 
3009
3010
  // src/ai/prompts.ts
3010
- var ROLE_AND_CONTEXT = `You are an expert auditor for coding agent configurations (Claude Code, Cursor, Codex, and GitHub Copilot).
3011
+ var ROLE_AND_CONTEXT = `You are an expert auditor for coding agent configurations (Claude Code, Cursor, Codex, OpenCode, and GitHub Copilot).
3011
3012
 
3012
3013
  Your job depends on context:
3013
3014
  - If no existing configs exist \u2192 generate an initial configuration from scratch.
@@ -3017,6 +3018,7 @@ var CONFIG_FILE_TYPES = `You understand these config files:
3017
3018
  - AGENTS.md: Primary instructions file for OpenAI Codex \u2014 same purpose as CLAUDE.md but for the Codex agent. Also serves as a cross-agent coordination file.
3018
3019
  - .claude/skills/{name}/SKILL.md: Skill files following the OpenSkills standard (agentskills.io). Each skill is a directory named after the skill, containing a SKILL.md with YAML frontmatter.
3019
3020
  - .agents/skills/{name}/SKILL.md: Same OpenSkills format for Codex skills (Codex scans .agents/skills/ for skills).
3021
+ - .opencode/skills/{name}/SKILL.md: Same OpenSkills format for OpenCode skills (OpenCode scans .opencode/skills/ for skills).
3020
3022
  - .cursor/skills/{name}/SKILL.md: Same OpenSkills format for Cursor skills.
3021
3023
  - .cursorrules: Coding rules for Cursor (deprecated legacy format \u2014 do NOT generate this).
3022
3024
  - .cursor/rules/*.mdc: Modern Cursor rules with frontmatter (description, globs, alwaysApply).
@@ -3044,7 +3046,7 @@ EXPLAIN:
3044
3046
  Omit empty categories. Keep each reason punchy and specific. End with a blank line.
3045
3047
 
3046
3048
  3. The JSON object starting with {.`;
3047
- var FILE_DESCRIPTIONS_RULES = `The "fileDescriptions" object MUST include a one-liner for every file that will be created or modified. Use actual file paths as keys (e.g. "CLAUDE.md", "AGENTS.md", ".claude/skills/my-skill/SKILL.md", ".agents/skills/my-skill/SKILL.md", ".cursor/skills/my-skill/SKILL.md", ".cursor/rules/my-rule.mdc"). Each description should explain why the change is needed, be concise and lowercase.
3049
+ var FILE_DESCRIPTIONS_RULES = `The "fileDescriptions" object MUST include a one-liner for every file that will be created or modified. Use actual file paths as keys (e.g. "CLAUDE.md", "AGENTS.md", ".claude/skills/my-skill/SKILL.md", ".agents/skills/my-skill/SKILL.md", ".opencode/skills/my-skill/SKILL.md", ".cursor/skills/my-skill/SKILL.md", ".cursor/rules/my-rule.mdc"). Each description should explain why the change is needed, be concise and lowercase.
3048
3050
 
3049
3051
  The "deletions" array should list files that should be removed (e.g. duplicate skills, stale configs). Include a reason for each. Omit the array or leave empty if nothing should be deleted.`;
3050
3052
  var SKILL_FORMAT_RULES = `All skills follow the OpenSkills standard (agentskills.io). Anthropic's official skill guide defines three levels of progressive disclosure:
@@ -3068,7 +3070,7 @@ var SCORING_CRITERIA = `SCORING CRITERIA \u2014 your output is scored determinis
3068
3070
 
3069
3071
  Existence (25 pts):
3070
3072
  - CLAUDE.md exists (6 pts) \u2014 always generate for claude targets
3071
- - AGENTS.md exists (6 pts) \u2014 always generate for codex target
3073
+ - AGENTS.md exists (6 pts) \u2014 always generate for codex or opencode targets
3072
3074
  - copilot-instructions.md exists (6 pts) \u2014 always generate for github-copilot target
3073
3075
  - Skills configured (8 pts) \u2014 generate 3+ skills for full points
3074
3076
  - MCP servers referenced (3 pts) \u2014 mention detected MCP integrations in your config text
@@ -3119,7 +3121,7 @@ ${OUTPUT_FORMAT}
3119
3121
 
3120
3122
  AgentSetup schema:
3121
3123
  {
3122
- "targetAgent": ["claude", "cursor", "codex", "github-copilot"] (array of selected agents),
3124
+ "targetAgent": ["claude", "cursor", "codex", "opencode", "github-copilot"] (array of selected agents),
3123
3125
  "fileDescriptions": {
3124
3126
  "<file-path>": "reason for this change (max 80 chars)"
3125
3127
  },
@@ -3134,6 +3136,10 @@ AgentSetup schema:
3134
3136
  "agentsMd": "string (markdown content for AGENTS.md \u2014 the primary Codex instructions file, same quality/structure as CLAUDE.md)",
3135
3137
  "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)" }]
3136
3138
  },
3139
+ "opencode": {
3140
+ "agentsMd": "string (markdown content for AGENTS.md \u2014 reuse codex.agentsMd if codex is also targeted, otherwise generate fresh)",
3141
+ "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)" }]
3142
+ },
3137
3143
  "cursor": {
3138
3144
  "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)" }],
3139
3145
  "rules": [{ "filename": "string.mdc", "content": "string (with frontmatter)" }]
@@ -3144,6 +3150,8 @@ AgentSetup schema:
3144
3150
  }
3145
3151
  }
3146
3152
 
3153
+ NOTE: If both "codex" and "opencode" are targeted, set opencode.agentsMd to the SAME content as codex.agentsMd \u2014 both agents read the same AGENTS.md file.
3154
+
3147
3155
  ${SKILL_FORMAT_RULES}
3148
3156
 
3149
3157
  ${FILE_DESCRIPTIONS_RULES}
@@ -3164,7 +3172,7 @@ ${OUTPUT_FORMAT}
3164
3172
 
3165
3173
  CoreSetup schema:
3166
3174
  {
3167
- "targetAgent": ["claude", "cursor", "codex", "github-copilot"] (array of selected agents),
3175
+ "targetAgent": ["claude", "cursor", "codex", "opencode", "github-copilot"] (array of selected agents),
3168
3176
  "fileDescriptions": {
3169
3177
  "<file-path>": "reason for this change (max 80 chars)"
3170
3178
  },
@@ -3179,6 +3187,10 @@ CoreSetup schema:
3179
3187
  "agentsMd": "string (markdown content for AGENTS.md)",
3180
3188
  "skillTopics": [{ "name": "string (kebab-case)", "description": "string (what this skill does and WHEN to use it \u2014 include trigger phrases)" }]
3181
3189
  },
3190
+ "opencode": {
3191
+ "agentsMd": "string (reuse codex.agentsMd if codex also targeted)",
3192
+ "skillTopics": [{ "name": "string (kebab-case)", "description": "string (what this skill does and WHEN to use it \u2014 include trigger phrases)" }]
3193
+ },
3182
3194
  "cursor": {
3183
3195
  "skillTopics": [{ "name": "string (kebab-case)", "description": "string (what this skill does and WHEN to use it \u2014 include trigger phrases)" }],
3184
3196
  "rules": [{ "filename": "string.mdc", "content": "string (with frontmatter)" }]
@@ -3208,7 +3220,7 @@ ${SCORING_CRITERIA}
3208
3220
 
3209
3221
  ${OUTPUT_SIZE_CONSTRAINTS}
3210
3222
  - Skill topics: 3-6 per platform based on project complexity (name + description only, no content).`;
3211
- var SKILL_GENERATION_PROMPT = `You generate a single skill file for a coding agent (Claude Code, Cursor, or Codex).
3223
+ var SKILL_GENERATION_PROMPT = `You generate a single skill file for a coding agent (Claude Code, Cursor, Codex, or OpenCode).
3212
3224
 
3213
3225
  Given project context and a skill topic, produce a focused SKILL.md body.
3214
3226
 
@@ -3238,7 +3250,7 @@ Description field formula: [What it does] + [When to use it with trigger phrases
3238
3250
 
3239
3251
  Return ONLY a JSON object:
3240
3252
  {"name": "string (kebab-case)", "description": "string (what + when + capabilities + negative triggers)", "content": "string (markdown body)"}`;
3241
- var REFINE_SYSTEM_PROMPT = `You are an expert at modifying coding agent configurations (Claude Code, Cursor, Codex, and GitHub Copilot).
3253
+ var REFINE_SYSTEM_PROMPT = `You are an expert at modifying coding agent configurations (Claude Code, Cursor, Codex, OpenCode, and GitHub Copilot).
3242
3254
 
3243
3255
  You will receive the current AgentSetup JSON and a user request describing what to change.
3244
3256
 
@@ -3246,7 +3258,7 @@ Apply the requested changes to the setup and return the complete updated AgentSe
3246
3258
 
3247
3259
  AgentSetup schema:
3248
3260
  {
3249
- "targetAgent": ["claude", "cursor", "codex", "github-copilot"] (array of selected agents),
3261
+ "targetAgent": ["claude", "cursor", "codex", "opencode", "github-copilot"] (array of selected agents),
3250
3262
  "fileDescriptions": {
3251
3263
  "<file-path>": "reason for this change (max 80 chars)"
3252
3264
  },
@@ -3261,6 +3273,10 @@ AgentSetup schema:
3261
3273
  "agentsMd": "string (markdown content for AGENTS.md)",
3262
3274
  "skills": [{ "name": "string (kebab-case)", "description": "string", "content": "string (markdown body, no frontmatter)" }]
3263
3275
  },
3276
+ "opencode": {
3277
+ "agentsMd": "string (reuse codex.agentsMd if codex also targeted)",
3278
+ "skills": [{ "name": "string (kebab-case)", "description": "string", "content": "string (markdown body, no frontmatter)" }]
3279
+ },
3264
3280
  "cursor": {
3265
3281
  "skills": [{ "name": "string (kebab-case)", "description": "string", "content": "string (markdown body, no frontmatter)" }],
3266
3282
  "rules": [{ "filename": "string.mdc", "content": "string (with frontmatter)" }]
@@ -3317,6 +3333,7 @@ Managed content:
3317
3333
  Return a JSON object with this exact shape:
3318
3334
  {
3319
3335
  "updatedDocs": {
3336
+ "agentsMd": "<updated content or null>",
3320
3337
  "claudeMd": "<updated content or null>",
3321
3338
  "readmeMd": "<updated content or null>",
3322
3339
  "cursorrules": "<updated content or null>",
@@ -3659,7 +3676,8 @@ function detectPlatforms() {
3659
3676
  return {
3660
3677
  claude: fs9.existsSync(path9.join(home, ".claude")),
3661
3678
  cursor: fs9.existsSync(getCursorConfigDir()),
3662
- codex: fs9.existsSync(path9.join(home, ".codex"))
3679
+ codex: fs9.existsSync(path9.join(home, ".codex")),
3680
+ opencode: fs9.existsSync(path9.join(home, ".config", "opencode"))
3663
3681
  };
3664
3682
  }
3665
3683
  function scanLocalState(dir) {
@@ -3735,6 +3753,25 @@ function scanLocalState(dir) {
3735
3753
  warnScanSkip(".agents/skills", error);
3736
3754
  }
3737
3755
  }
3756
+ const opencodeSkillsDir = path9.join(dir, ".opencode", "skills");
3757
+ if (fs9.existsSync(opencodeSkillsDir)) {
3758
+ try {
3759
+ for (const name of fs9.readdirSync(opencodeSkillsDir)) {
3760
+ const skillFile = path9.join(opencodeSkillsDir, name, "SKILL.md");
3761
+ if (fs9.existsSync(skillFile)) {
3762
+ items.push({
3763
+ type: "skill",
3764
+ platform: "opencode",
3765
+ name: `${name}/SKILL.md`,
3766
+ contentHash: hashFile(skillFile),
3767
+ path: skillFile
3768
+ });
3769
+ }
3770
+ }
3771
+ } catch (error) {
3772
+ warnScanSkip(".opencode/skills", error);
3773
+ }
3774
+ }
3738
3775
  const cursorrulesPath = path9.join(dir, ".cursorrules");
3739
3776
  if (fs9.existsSync(cursorrulesPath)) {
3740
3777
  items.push({
@@ -4553,15 +4590,15 @@ init_config();
4553
4590
  // src/utils/dependencies.ts
4554
4591
  import { readFileSync as readFileSync2 } from "fs";
4555
4592
  import { join as join2 } from "path";
4556
- function readFileOrNull2(path42) {
4593
+ function readFileOrNull2(path43) {
4557
4594
  try {
4558
- return readFileSync2(path42, "utf-8");
4595
+ return readFileSync2(path43, "utf-8");
4559
4596
  } catch {
4560
4597
  return null;
4561
4598
  }
4562
4599
  }
4563
- function readJsonOrNull(path42) {
4564
- const content = readFileOrNull2(path42);
4600
+ function readJsonOrNull(path43) {
4601
+ const content = readFileOrNull2(path43);
4565
4602
  if (!content) return null;
4566
4603
  try {
4567
4604
  return JSON.parse(content);
@@ -4700,7 +4737,7 @@ function mergeSkillResults(results, setup) {
4700
4737
  skills.push(skill);
4701
4738
  platformObj.skills = skills;
4702
4739
  setup[platform] = platformObj;
4703
- const skillPath = platform === "codex" ? `.agents/skills/${skill.name}/SKILL.md` : `.${platform}/skills/${skill.name}/SKILL.md`;
4740
+ const skillPath = platform === "codex" ? `.agents/skills/${skill.name}/SKILL.md` : platform === "opencode" ? `.opencode/skills/${skill.name}/SKILL.md` : `.${platform}/skills/${skill.name}/SKILL.md`;
4704
4741
  const descriptions = setup.fileDescriptions ?? {};
4705
4742
  descriptions[skillPath] = skill.description.slice(0, 80);
4706
4743
  setup.fileDescriptions = descriptions;
@@ -4713,7 +4750,7 @@ function mergeSkillResults(results, setup) {
4713
4750
  }
4714
4751
  function collectSkillTopics(setup, targetAgent, fingerprint) {
4715
4752
  const topics = [];
4716
- for (const platform of ["claude", "codex", "cursor"]) {
4753
+ for (const platform of ["claude", "codex", "opencode", "cursor"]) {
4717
4754
  if (!targetAgent.includes(platform)) continue;
4718
4755
  const platformObj = setup[platform];
4719
4756
  const skillTopics = platformObj?.skillTopics;
@@ -5154,7 +5191,7 @@ ${f.content}
5154
5191
  }
5155
5192
 
5156
5193
  // src/writers/index.ts
5157
- import fs19 from "fs";
5194
+ import fs20 from "fs";
5158
5195
 
5159
5196
  // src/writers/claude/index.ts
5160
5197
  init_pre_commit_block();
@@ -5300,31 +5337,54 @@ function writeGithubCopilotConfig(config) {
5300
5337
  return written;
5301
5338
  }
5302
5339
 
5340
+ // src/writers/opencode/index.ts
5341
+ init_pre_commit_block();
5342
+ init_builtin_skills();
5343
+ import fs17 from "fs";
5344
+ import path17 from "path";
5345
+ function writeOpencodeConfig(config, agentsMdAlreadyWritten = false) {
5346
+ const written = [];
5347
+ if (!agentsMdAlreadyWritten) {
5348
+ fs17.writeFileSync("AGENTS.md", appendLearningsBlock(appendPreCommitBlock(config.agentsMd)));
5349
+ written.push("AGENTS.md");
5350
+ }
5351
+ if (config.skills?.length) {
5352
+ for (const skill of config.skills) {
5353
+ const skillDir = path17.join(".opencode", "skills", skill.name);
5354
+ if (!fs17.existsSync(skillDir)) fs17.mkdirSync(skillDir, { recursive: true });
5355
+ const skillPath = path17.join(skillDir, "SKILL.md");
5356
+ fs17.writeFileSync(skillPath, buildSkillContent(skill));
5357
+ written.push(skillPath);
5358
+ }
5359
+ }
5360
+ return written;
5361
+ }
5362
+
5303
5363
  // src/writers/backup.ts
5304
- import fs16 from "fs";
5305
- import path16 from "path";
5364
+ import fs18 from "fs";
5365
+ import path18 from "path";
5306
5366
  function createBackup(files) {
5307
5367
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
5308
- const backupDir = path16.join(BACKUPS_DIR, timestamp);
5368
+ const backupDir = path18.join(BACKUPS_DIR, timestamp);
5309
5369
  for (const file of files) {
5310
- if (!fs16.existsSync(file)) continue;
5311
- const dest = path16.join(backupDir, file);
5312
- const destDir = path16.dirname(dest);
5313
- if (!fs16.existsSync(destDir)) {
5314
- fs16.mkdirSync(destDir, { recursive: true });
5370
+ if (!fs18.existsSync(file)) continue;
5371
+ const dest = path18.join(backupDir, file);
5372
+ const destDir = path18.dirname(dest);
5373
+ if (!fs18.existsSync(destDir)) {
5374
+ fs18.mkdirSync(destDir, { recursive: true });
5315
5375
  }
5316
- fs16.copyFileSync(file, dest);
5376
+ fs18.copyFileSync(file, dest);
5317
5377
  }
5318
5378
  return backupDir;
5319
5379
  }
5320
5380
  function restoreBackup(backupDir, file) {
5321
- const backupFile = path16.join(backupDir, file);
5322
- if (!fs16.existsSync(backupFile)) return false;
5323
- const destDir = path16.dirname(file);
5324
- if (!fs16.existsSync(destDir)) {
5325
- fs16.mkdirSync(destDir, { recursive: true });
5381
+ const backupFile = path18.join(backupDir, file);
5382
+ if (!fs18.existsSync(backupFile)) return false;
5383
+ const destDir = path18.dirname(file);
5384
+ if (!fs18.existsSync(destDir)) {
5385
+ fs18.mkdirSync(destDir, { recursive: true });
5326
5386
  }
5327
- fs16.copyFileSync(backupFile, file);
5387
+ fs18.copyFileSync(backupFile, file);
5328
5388
  return true;
5329
5389
  }
5330
5390
 
@@ -5332,32 +5392,32 @@ function restoreBackup(backupDir, file) {
5332
5392
  init_builtin_skills();
5333
5393
 
5334
5394
  // src/writers/manifest.ts
5335
- import fs18 from "fs";
5395
+ import fs19 from "fs";
5336
5396
  import crypto3 from "crypto";
5337
5397
  function readManifest() {
5338
5398
  try {
5339
- if (!fs18.existsSync(MANIFEST_FILE)) return null;
5340
- return JSON.parse(fs18.readFileSync(MANIFEST_FILE, "utf-8"));
5399
+ if (!fs19.existsSync(MANIFEST_FILE)) return null;
5400
+ return JSON.parse(fs19.readFileSync(MANIFEST_FILE, "utf-8"));
5341
5401
  } catch {
5342
5402
  return null;
5343
5403
  }
5344
5404
  }
5345
5405
  function writeManifest(manifest) {
5346
- if (!fs18.existsSync(CALIBER_DIR)) {
5347
- fs18.mkdirSync(CALIBER_DIR, { recursive: true });
5406
+ if (!fs19.existsSync(CALIBER_DIR)) {
5407
+ fs19.mkdirSync(CALIBER_DIR, { recursive: true });
5348
5408
  }
5349
- fs18.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
5409
+ fs19.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
5350
5410
  }
5351
5411
  function fileChecksum(filePath) {
5352
- const content = fs18.readFileSync(filePath);
5412
+ const content = fs19.readFileSync(filePath);
5353
5413
  return crypto3.createHash("sha256").update(content).digest("hex");
5354
5414
  }
5355
5415
 
5356
5416
  // src/writers/index.ts
5357
5417
  function writeSetup(setup) {
5358
5418
  const filesToWrite = getFilesToWrite(setup);
5359
- const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs19.existsSync(f));
5360
- const existingFiles = [...filesToWrite.filter((f) => fs19.existsSync(f)), ...filesToDelete];
5419
+ const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs20.existsSync(f));
5420
+ const existingFiles = [...filesToWrite.filter((f) => fs20.existsSync(f)), ...filesToDelete];
5361
5421
  const backupDir = existingFiles.length > 0 ? createBackup(existingFiles) : void 0;
5362
5422
  const written = [];
5363
5423
  if (setup.targetAgent.includes("claude") && setup.claude) {
@@ -5369,12 +5429,16 @@ function writeSetup(setup) {
5369
5429
  if (setup.targetAgent.includes("codex") && setup.codex) {
5370
5430
  written.push(...writeCodexConfig(setup.codex));
5371
5431
  }
5432
+ if (setup.targetAgent.includes("opencode") && setup.opencode) {
5433
+ const agentsMdAlreadyWritten = written.includes("AGENTS.md");
5434
+ written.push(...writeOpencodeConfig(setup.opencode, agentsMdAlreadyWritten));
5435
+ }
5372
5436
  if (setup.targetAgent.includes("github-copilot") && setup.copilot) {
5373
5437
  written.push(...writeGithubCopilotConfig(setup.copilot));
5374
5438
  }
5375
5439
  const deleted = [];
5376
5440
  for (const filePath of filesToDelete) {
5377
- fs19.unlinkSync(filePath);
5441
+ fs20.unlinkSync(filePath);
5378
5442
  deleted.push(filePath);
5379
5443
  }
5380
5444
  written.push(...ensureBuiltinSkills());
@@ -5405,8 +5469,8 @@ function undoSetup() {
5405
5469
  const removed = [];
5406
5470
  for (const entry of manifest.entries) {
5407
5471
  if (entry.action === "created") {
5408
- if (fs19.existsSync(entry.path)) {
5409
- fs19.unlinkSync(entry.path);
5472
+ if (fs20.existsSync(entry.path)) {
5473
+ fs20.unlinkSync(entry.path);
5410
5474
  removed.push(entry.path);
5411
5475
  }
5412
5476
  } else if ((entry.action === "modified" || entry.action === "deleted") && manifest.backupDir) {
@@ -5415,8 +5479,8 @@ function undoSetup() {
5415
5479
  }
5416
5480
  }
5417
5481
  }
5418
- if (fs19.existsSync(MANIFEST_FILE)) {
5419
- fs19.unlinkSync(MANIFEST_FILE);
5482
+ if (fs20.existsSync(MANIFEST_FILE)) {
5483
+ fs20.unlinkSync(MANIFEST_FILE);
5420
5484
  }
5421
5485
  return { restored, removed };
5422
5486
  }
@@ -5447,6 +5511,14 @@ function getFilesToWrite(setup) {
5447
5511
  for (const s of setup.codex.skills) files.push(`.agents/skills/${s.name}/SKILL.md`);
5448
5512
  }
5449
5513
  }
5514
+ if (setup.targetAgent.includes("opencode") && setup.opencode) {
5515
+ if (!setup.targetAgent.includes("codex")) {
5516
+ files.push("AGENTS.md");
5517
+ }
5518
+ if (setup.opencode.skills) {
5519
+ for (const s of setup.opencode.skills) files.push(`.opencode/skills/${s.name}/SKILL.md`);
5520
+ }
5521
+ }
5450
5522
  if (setup.targetAgent.includes("github-copilot") && setup.copilot) {
5451
5523
  if (setup.copilot.instructions) files.push(".github/copilot-instructions.md");
5452
5524
  if (setup.copilot.instructionFiles) {
@@ -5458,22 +5530,22 @@ function getFilesToWrite(setup) {
5458
5530
  }
5459
5531
  function ensureGitignore() {
5460
5532
  const gitignorePath = ".gitignore";
5461
- if (fs19.existsSync(gitignorePath)) {
5462
- const content = fs19.readFileSync(gitignorePath, "utf-8");
5533
+ if (fs20.existsSync(gitignorePath)) {
5534
+ const content = fs20.readFileSync(gitignorePath, "utf-8");
5463
5535
  if (!content.includes(".caliber/")) {
5464
- fs19.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
5536
+ fs20.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
5465
5537
  }
5466
5538
  } else {
5467
- fs19.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
5539
+ fs20.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
5468
5540
  }
5469
5541
  }
5470
5542
 
5471
5543
  // src/writers/staging.ts
5472
- import fs20 from "fs";
5473
- import path18 from "path";
5474
- var STAGED_DIR = path18.join(CALIBER_DIR, "staged");
5475
- var PROPOSED_DIR = path18.join(STAGED_DIR, "proposed");
5476
- var CURRENT_DIR = path18.join(STAGED_DIR, "current");
5544
+ import fs21 from "fs";
5545
+ import path19 from "path";
5546
+ var STAGED_DIR = path19.join(CALIBER_DIR, "staged");
5547
+ var PROPOSED_DIR = path19.join(STAGED_DIR, "proposed");
5548
+ var CURRENT_DIR = path19.join(STAGED_DIR, "current");
5477
5549
  function normalizeContent(content) {
5478
5550
  return content.split("\n").map((line) => line.trimEnd()).join("\n").replace(/\n{3,}/g, "\n\n").trim();
5479
5551
  }
@@ -5484,20 +5556,20 @@ function stageFiles(files, projectDir) {
5484
5556
  const stagedFiles = [];
5485
5557
  for (const file of files) {
5486
5558
  assertPathWithinDir(file.path, projectDir);
5487
- const originalPath = path18.join(projectDir, file.path);
5488
- if (fs20.existsSync(originalPath)) {
5489
- const existing = fs20.readFileSync(originalPath, "utf-8");
5559
+ const originalPath = path19.join(projectDir, file.path);
5560
+ if (fs21.existsSync(originalPath)) {
5561
+ const existing = fs21.readFileSync(originalPath, "utf-8");
5490
5562
  if (normalizeContent(existing) === normalizeContent(file.content)) {
5491
5563
  continue;
5492
5564
  }
5493
5565
  }
5494
- const proposedPath = path18.join(PROPOSED_DIR, file.path);
5495
- fs20.mkdirSync(path18.dirname(proposedPath), { recursive: true });
5496
- fs20.writeFileSync(proposedPath, file.content);
5497
- if (fs20.existsSync(originalPath)) {
5498
- const currentPath = path18.join(CURRENT_DIR, file.path);
5499
- fs20.mkdirSync(path18.dirname(currentPath), { recursive: true });
5500
- fs20.copyFileSync(originalPath, currentPath);
5566
+ const proposedPath = path19.join(PROPOSED_DIR, file.path);
5567
+ fs21.mkdirSync(path19.dirname(proposedPath), { recursive: true });
5568
+ fs21.writeFileSync(proposedPath, file.content);
5569
+ if (fs21.existsSync(originalPath)) {
5570
+ const currentPath = path19.join(CURRENT_DIR, file.path);
5571
+ fs21.mkdirSync(path19.dirname(currentPath), { recursive: true });
5572
+ fs21.copyFileSync(originalPath, currentPath);
5501
5573
  modifiedFiles++;
5502
5574
  stagedFiles.push({
5503
5575
  relativePath: file.path,
@@ -5514,14 +5586,14 @@ function stageFiles(files, projectDir) {
5514
5586
  return { newFiles, modifiedFiles, stagedFiles };
5515
5587
  }
5516
5588
  function cleanupStaging() {
5517
- if (fs20.existsSync(STAGED_DIR)) {
5518
- fs20.rmSync(STAGED_DIR, { recursive: true, force: true });
5589
+ if (fs21.existsSync(STAGED_DIR)) {
5590
+ fs21.rmSync(STAGED_DIR, { recursive: true, force: true });
5519
5591
  }
5520
5592
  }
5521
5593
 
5522
5594
  // src/commands/setup-files.ts
5523
5595
  init_builtin_skills();
5524
- import fs21 from "fs";
5596
+ import fs22 from "fs";
5525
5597
  function collectSetupFiles(setup, targetAgent) {
5526
5598
  const files = [];
5527
5599
  const claude = setup.claude;
@@ -5588,6 +5660,27 @@ function collectSetupFiles(setup, targetAgent) {
5588
5660
  }
5589
5661
  }
5590
5662
  }
5663
+ const opencode = setup.opencode;
5664
+ if (opencode) {
5665
+ if (opencode.agentsMd && !files.some((f) => f.path === "AGENTS.md")) {
5666
+ files.push({ path: "AGENTS.md", content: opencode.agentsMd });
5667
+ }
5668
+ const opencodeSkills = opencode.skills;
5669
+ if (Array.isArray(opencodeSkills)) {
5670
+ for (const skill of opencodeSkills) {
5671
+ files.push({
5672
+ path: `.opencode/skills/${sanitizePath(skill.name)}/SKILL.md`,
5673
+ content: buildSkillContent(skill)
5674
+ });
5675
+ }
5676
+ }
5677
+ for (const builtin of BUILTIN_SKILLS) {
5678
+ files.push({
5679
+ path: `.opencode/skills/${builtin.name}/SKILL.md`,
5680
+ content: buildSkillContent(builtin)
5681
+ });
5682
+ }
5683
+ }
5591
5684
  const copilot = setup.copilot;
5592
5685
  if (copilot) {
5593
5686
  if (copilot.instructions)
@@ -5606,7 +5699,8 @@ function collectSetupFiles(setup, targetAgent) {
5606
5699
  }
5607
5700
  }
5608
5701
  const codexTargeted = targetAgent ? targetAgent.includes("codex") : false;
5609
- if (codexTargeted && !fs21.existsSync("AGENTS.md") && !(codex && codex.agentsMd)) {
5702
+ const opencodeTargeted = targetAgent ? targetAgent.includes("opencode") : false;
5703
+ if ((codexTargeted || opencodeTargeted) && !fs22.existsSync("AGENTS.md") && !(codex && codex.agentsMd) && !(opencode && opencode.agentsMd)) {
5610
5704
  const agentRefs = [];
5611
5705
  if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
5612
5706
  if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
@@ -5625,9 +5719,9 @@ ${agentRefs.join(" ")}
5625
5719
 
5626
5720
  // src/lib/learning-hooks.ts
5627
5721
  init_resolve_caliber();
5628
- import fs22 from "fs";
5629
- import path19 from "path";
5630
- var SETTINGS_PATH2 = path19.join(".claude", "settings.json");
5722
+ import fs23 from "fs";
5723
+ import path20 from "path";
5724
+ var SETTINGS_PATH2 = path20.join(".claude", "settings.json");
5631
5725
  var HOOK_TAILS = [
5632
5726
  { event: "PostToolUse", tail: "learn observe", description: "Caliber: recording tool usage for session learning" },
5633
5727
  { event: "PostToolUseFailure", tail: "learn observe --failure", description: "Caliber: recording tool failure for session learning" },
@@ -5644,17 +5738,17 @@ function getHookConfigs() {
5644
5738
  }));
5645
5739
  }
5646
5740
  function readSettings2() {
5647
- if (!fs22.existsSync(SETTINGS_PATH2)) return {};
5741
+ if (!fs23.existsSync(SETTINGS_PATH2)) return {};
5648
5742
  try {
5649
- return JSON.parse(fs22.readFileSync(SETTINGS_PATH2, "utf-8"));
5743
+ return JSON.parse(fs23.readFileSync(SETTINGS_PATH2, "utf-8"));
5650
5744
  } catch {
5651
5745
  return {};
5652
5746
  }
5653
5747
  }
5654
5748
  function writeSettings2(settings) {
5655
- const dir = path19.dirname(SETTINGS_PATH2);
5656
- if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
5657
- fs22.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
5749
+ const dir = path20.dirname(SETTINGS_PATH2);
5750
+ if (!fs23.existsSync(dir)) fs23.mkdirSync(dir, { recursive: true });
5751
+ fs23.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
5658
5752
  }
5659
5753
  function hasLearningHook(matchers, tail) {
5660
5754
  return matchers.some((entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, tail)));
@@ -5688,7 +5782,7 @@ function installLearningHooks() {
5688
5782
  writeSettings2(settings);
5689
5783
  return { installed: true, alreadyInstalled: false };
5690
5784
  }
5691
- var CURSOR_HOOKS_PATH = path19.join(".cursor", "hooks.json");
5785
+ var CURSOR_HOOKS_PATH = path20.join(".cursor", "hooks.json");
5692
5786
  var CURSOR_HOOK_EVENTS = [
5693
5787
  { event: "postToolUse", tail: "learn observe" },
5694
5788
  { event: "postToolUseFailure", tail: "learn observe --failure" },
@@ -5696,17 +5790,17 @@ var CURSOR_HOOK_EVENTS = [
5696
5790
  { event: "sessionEnd", tail: "learn finalize --auto" }
5697
5791
  ];
5698
5792
  function readCursorHooks() {
5699
- if (!fs22.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
5793
+ if (!fs23.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
5700
5794
  try {
5701
- return JSON.parse(fs22.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
5795
+ return JSON.parse(fs23.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
5702
5796
  } catch {
5703
5797
  return { version: 1, hooks: {} };
5704
5798
  }
5705
5799
  }
5706
5800
  function writeCursorHooks(config) {
5707
- const dir = path19.dirname(CURSOR_HOOKS_PATH);
5708
- if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
5709
- fs22.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
5801
+ const dir = path20.dirname(CURSOR_HOOKS_PATH);
5802
+ if (!fs23.existsSync(dir)) fs23.mkdirSync(dir, { recursive: true });
5803
+ fs23.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
5710
5804
  }
5711
5805
  function hasCursorHook(entries, tail) {
5712
5806
  return entries.some((e) => isCaliberCommand(e.command, tail));
@@ -5778,22 +5872,22 @@ function removeLearningHooks() {
5778
5872
  init_resolve_caliber();
5779
5873
 
5780
5874
  // src/lib/state.ts
5781
- import fs23 from "fs";
5782
- import path20 from "path";
5875
+ import fs24 from "fs";
5876
+ import path21 from "path";
5783
5877
  import { execSync as execSync9 } from "child_process";
5784
- var STATE_FILE = path20.join(CALIBER_DIR, ".caliber-state.json");
5878
+ var STATE_FILE = path21.join(CALIBER_DIR, ".caliber-state.json");
5785
5879
  function normalizeTargetAgent(value) {
5786
5880
  if (Array.isArray(value)) return value;
5787
5881
  if (typeof value === "string") {
5788
5882
  if (value === "both") return ["claude", "cursor"];
5789
- if (["claude", "cursor", "codex", "github-copilot"].includes(value)) return [value];
5883
+ if (["claude", "cursor", "codex", "opencode", "github-copilot"].includes(value)) return [value];
5790
5884
  }
5791
5885
  return void 0;
5792
5886
  }
5793
5887
  function readState() {
5794
5888
  try {
5795
- if (!fs23.existsSync(STATE_FILE)) return null;
5796
- const raw = JSON.parse(fs23.readFileSync(STATE_FILE, "utf-8"));
5889
+ if (!fs24.existsSync(STATE_FILE)) return null;
5890
+ const raw = JSON.parse(fs24.readFileSync(STATE_FILE, "utf-8"));
5797
5891
  if (raw.targetAgent) raw.targetAgent = normalizeTargetAgent(raw.targetAgent);
5798
5892
  return raw;
5799
5893
  } catch {
@@ -5801,10 +5895,10 @@ function readState() {
5801
5895
  }
5802
5896
  }
5803
5897
  function writeState(state) {
5804
- if (!fs23.existsSync(CALIBER_DIR)) {
5805
- fs23.mkdirSync(CALIBER_DIR, { recursive: true });
5898
+ if (!fs24.existsSync(CALIBER_DIR)) {
5899
+ fs24.mkdirSync(CALIBER_DIR, { recursive: true });
5806
5900
  }
5807
- fs23.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
5901
+ fs24.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
5808
5902
  }
5809
5903
  function getCurrentHeadSha() {
5810
5904
  try {
@@ -6127,7 +6221,8 @@ function checkExistence(dir) {
6127
6221
  });
6128
6222
  const claudeSkills = countFiles(join3(dir, ".claude", "skills"), /\.(md|SKILL\.md)$/);
6129
6223
  const codexSkills = countFiles(join3(dir, ".agents", "skills"), /SKILL\.md$/);
6130
- const skillCount = claudeSkills.length + codexSkills.length;
6224
+ const opencodeSkills = countFiles(join3(dir, ".opencode", "skills"), /SKILL\.md$/);
6225
+ const skillCount = claudeSkills.length + codexSkills.length + opencodeSkills.length;
6131
6226
  const skillBase = skillCount >= 1 ? POINTS_SKILLS_EXIST : 0;
6132
6227
  const skillBonus = Math.min((skillCount - 1) * POINTS_SKILLS_BONUS_PER_EXTRA, POINTS_SKILLS_BONUS_CAP);
6133
6228
  const skillPoints = skillCount >= 1 ? skillBase + Math.max(0, skillBonus) : 0;
@@ -6861,25 +6956,29 @@ function checkSources(dir) {
6861
6956
  }
6862
6957
 
6863
6958
  // src/scoring/dismissed.ts
6864
- import fs24 from "fs";
6865
- import path21 from "path";
6866
- var DISMISSED_FILE = path21.join(CALIBER_DIR, "dismissed-checks.json");
6867
- function readDismissedChecks() {
6959
+ import fs25 from "fs";
6960
+ import path22 from "path";
6961
+ var DISMISSED_FILE = path22.join(CALIBER_DIR, "dismissed-checks.json");
6962
+ function dismissedFilePath(dir) {
6963
+ return dir ? path22.join(dir, CALIBER_DIR, "dismissed-checks.json") : DISMISSED_FILE;
6964
+ }
6965
+ function readDismissedChecks(dir) {
6868
6966
  try {
6869
- if (!fs24.existsSync(DISMISSED_FILE)) return [];
6870
- return JSON.parse(fs24.readFileSync(DISMISSED_FILE, "utf-8"));
6967
+ const filePath = dismissedFilePath(dir);
6968
+ if (!fs25.existsSync(filePath)) return [];
6969
+ return JSON.parse(fs25.readFileSync(filePath, "utf-8"));
6871
6970
  } catch {
6872
6971
  return [];
6873
6972
  }
6874
6973
  }
6875
6974
  function writeDismissedChecks(checks) {
6876
- if (!fs24.existsSync(CALIBER_DIR)) {
6877
- fs24.mkdirSync(CALIBER_DIR, { recursive: true });
6975
+ if (!fs25.existsSync(CALIBER_DIR)) {
6976
+ fs25.mkdirSync(CALIBER_DIR, { recursive: true });
6878
6977
  }
6879
- fs24.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
6978
+ fs25.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
6880
6979
  }
6881
- function getDismissedIds() {
6882
- return new Set(readDismissedChecks().map((c) => c.id));
6980
+ function getDismissedIds(dir) {
6981
+ return new Set(readDismissedChecks(dir).map((c) => c.id));
6883
6982
  }
6884
6983
 
6885
6984
  // src/scoring/index.ts
@@ -6894,11 +6993,11 @@ function filterChecksForTarget(checks, target) {
6894
6993
  return checks.filter((c) => {
6895
6994
  if (CLAUDE_ONLY_CHECKS.has(c.id)) return target.includes("claude");
6896
6995
  if (CURSOR_ONLY_CHECKS.has(c.id)) return target.includes("cursor");
6897
- if (CODEX_ONLY_CHECKS.has(c.id)) return target.includes("codex");
6996
+ if (CODEX_ONLY_CHECKS.has(c.id)) return target.includes("codex") || target.includes("opencode");
6898
6997
  if (COPILOT_ONLY_CHECKS.has(c.id)) return target.includes("github-copilot");
6899
6998
  if (BOTH_ONLY_CHECKS.has(c.id)) return target.includes("claude") && target.includes("cursor");
6900
- if (NON_CODEX_CHECKS.has(c.id)) return !target.includes("codex");
6901
- if (CLAUDE_OR_CODEX_CHECKS.has(c.id)) return target.includes("claude") || target.includes("codex");
6999
+ if (NON_CODEX_CHECKS.has(c.id)) return !target.includes("codex") && !target.includes("opencode");
7000
+ if (CLAUDE_OR_CODEX_CHECKS.has(c.id)) return target.includes("claude") || target.includes("codex") || target.includes("opencode");
6902
7001
  return true;
6903
7002
  });
6904
7003
  }
@@ -6907,6 +7006,7 @@ function detectTargetAgent(dir) {
6907
7006
  if (existsSync7(join9(dir, "CLAUDE.md")) || existsSync7(join9(dir, ".claude", "skills"))) agents.push("claude");
6908
7007
  if (existsSync7(join9(dir, ".cursorrules")) || existsSync7(join9(dir, ".cursor", "rules"))) agents.push("cursor");
6909
7008
  if (existsSync7(join9(dir, ".codex")) || existsSync7(join9(dir, ".agents", "skills")) || existsSync7(join9(dir, "AGENTS.md"))) agents.push("codex");
7009
+ if (existsSync7(join9(dir, ".opencode"))) agents.push("opencode");
6910
7010
  if (existsSync7(join9(dir, ".github", "copilot-instructions.md")) || existsSync7(join9(dir, ".github", "instructions"))) agents.push("github-copilot");
6911
7011
  return agents.length > 0 ? agents : ["claude"];
6912
7012
  }
@@ -6921,7 +7021,7 @@ function computeLocalScore(dir, targetAgent) {
6921
7021
  ...checkBonus(dir),
6922
7022
  ...checkSources(dir)
6923
7023
  ];
6924
- const dismissed = getDismissedIds();
7024
+ const dismissed = getDismissedIds(dir);
6925
7025
  const checks = filterChecksForTarget(allChecks, target).filter((c) => !dismissed.has(c.id));
6926
7026
  const maxPossible = checks.reduce((s, c) => s + c.maxPoints, 0);
6927
7027
  const earned = checks.reduce((s, c) => s + c.earnedPoints, 0);
@@ -7131,27 +7231,27 @@ import { PostHog } from "posthog-node";
7131
7231
  import chalk5 from "chalk";
7132
7232
 
7133
7233
  // src/telemetry/config.ts
7134
- import fs25 from "fs";
7135
- import path22 from "path";
7234
+ import fs26 from "fs";
7235
+ import path23 from "path";
7136
7236
  import os5 from "os";
7137
7237
  import crypto4 from "crypto";
7138
7238
  import { execSync as execSync13 } from "child_process";
7139
- var CONFIG_DIR2 = path22.join(os5.homedir(), ".caliber");
7140
- var CONFIG_FILE2 = path22.join(CONFIG_DIR2, "config.json");
7239
+ var CONFIG_DIR2 = path23.join(os5.homedir(), ".caliber");
7240
+ var CONFIG_FILE2 = path23.join(CONFIG_DIR2, "config.json");
7141
7241
  var runtimeDisabled = false;
7142
7242
  function readConfig() {
7143
7243
  try {
7144
- if (!fs25.existsSync(CONFIG_FILE2)) return {};
7145
- return JSON.parse(fs25.readFileSync(CONFIG_FILE2, "utf-8"));
7244
+ if (!fs26.existsSync(CONFIG_FILE2)) return {};
7245
+ return JSON.parse(fs26.readFileSync(CONFIG_FILE2, "utf-8"));
7146
7246
  } catch {
7147
7247
  return {};
7148
7248
  }
7149
7249
  }
7150
7250
  function writeConfig(config) {
7151
- if (!fs25.existsSync(CONFIG_DIR2)) {
7152
- fs25.mkdirSync(CONFIG_DIR2, { recursive: true });
7251
+ if (!fs26.existsSync(CONFIG_DIR2)) {
7252
+ fs26.mkdirSync(CONFIG_DIR2, { recursive: true });
7153
7253
  }
7154
- fs25.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
7254
+ fs26.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
7155
7255
  }
7156
7256
  function getMachineId() {
7157
7257
  const config = readConfig();
@@ -7320,8 +7420,8 @@ function trackInitSkillsSearch(searched, installedCount) {
7320
7420
  function trackInitScoreRegression(oldScore, newScore) {
7321
7421
  trackEvent("init_score_regression", { old_score: oldScore, new_score: newScore });
7322
7422
  }
7323
- function trackInitCompleted(path42, score) {
7324
- trackEvent("init_completed", { path: path42, score });
7423
+ function trackInitCompleted(path43, score) {
7424
+ trackEvent("init_completed", { path: path43, score });
7325
7425
  }
7326
7426
  function trackRegenerateCompleted(action, durationMs) {
7327
7427
  trackEvent("regenerate_completed", { action, duration_ms: durationMs });
@@ -7400,7 +7500,7 @@ function sanitizeSlug(slug) {
7400
7500
  function getSkillPath(platform, slug) {
7401
7501
  const safe = sanitizeSlug(slug);
7402
7502
  if (!safe) throw new Error(`Invalid skill slug: "${slug}"`);
7403
- const baseDir = platform === "cursor" ? join10(".cursor", "skills") : platform === "codex" ? join10(".agents", "skills") : join10(".claude", "skills");
7503
+ const baseDir = platform === "cursor" ? join10(".cursor", "skills") : platform === "codex" ? join10(".agents", "skills") : platform === "opencode" ? join10(".opencode", "skills") : join10(".claude", "skills");
7404
7504
  const cwd = process.cwd();
7405
7505
  const fullPath = resolve2(cwd, baseDir, safe, "SKILL.md");
7406
7506
  if (!fullPath.startsWith(resolve2(cwd, baseDir) + "/")) {
@@ -7411,6 +7511,7 @@ function getSkillPath(platform, slug) {
7411
7511
  function getSkillDir(platform) {
7412
7512
  if (platform === "cursor") return join10(process.cwd(), ".cursor", "skills");
7413
7513
  if (platform === "codex") return join10(process.cwd(), ".agents", "skills");
7514
+ if (platform === "opencode") return join10(process.cwd(), ".opencode", "skills");
7414
7515
  return join10(process.cwd(), ".claude", "skills");
7415
7516
  }
7416
7517
  function getInstalledSkills(platforms) {
@@ -8315,11 +8416,11 @@ function countIssuePoints(issues) {
8315
8416
  }
8316
8417
  async function scoreAndRefine(setup, dir, sessionHistory, callbacks) {
8317
8418
  const existsCache = /* @__PURE__ */ new Map();
8318
- const cachedExists = (path42) => {
8319
- const cached = existsCache.get(path42);
8419
+ const cachedExists = (path43) => {
8420
+ const cached = existsCache.get(path43);
8320
8421
  if (cached !== void 0) return cached;
8321
- const result = existsSync9(path42);
8322
- existsCache.set(path42, result);
8422
+ const result = existsSync9(path43);
8423
+ existsCache.set(path43, result);
8323
8424
  return result;
8324
8425
  };
8325
8426
  const projectStructure = collectProjectStructure(dir);
@@ -8458,8 +8559,8 @@ async function runScoreRefineWithSpinner(setup, dir, sessionHistory) {
8458
8559
  }
8459
8560
 
8460
8561
  // src/lib/debug-report.ts
8461
- import fs26 from "fs";
8462
- import path23 from "path";
8562
+ import fs27 from "fs";
8563
+ import path24 from "path";
8463
8564
  var DebugReport = class {
8464
8565
  sections = [];
8465
8566
  startTime;
@@ -8528,11 +8629,11 @@ var DebugReport = class {
8528
8629
  lines.push(`| **Total** | **${formatMs(totalMs)}** |`);
8529
8630
  lines.push("");
8530
8631
  }
8531
- const dir = path23.dirname(outputPath);
8532
- if (!fs26.existsSync(dir)) {
8533
- fs26.mkdirSync(dir, { recursive: true });
8632
+ const dir = path24.dirname(outputPath);
8633
+ if (!fs27.existsSync(dir)) {
8634
+ fs27.mkdirSync(dir, { recursive: true });
8534
8635
  }
8535
- fs26.writeFileSync(outputPath, lines.join("\n"));
8636
+ fs27.writeFileSync(outputPath, lines.join("\n"));
8536
8637
  }
8537
8638
  };
8538
8639
  function formatMs(ms) {
@@ -8933,7 +9034,7 @@ import chalk11 from "chalk";
8933
9034
  import ora3 from "ora";
8934
9035
  import select5 from "@inquirer/select";
8935
9036
  import checkbox from "@inquirer/checkbox";
8936
- import fs29 from "fs";
9037
+ import fs30 from "fs";
8937
9038
 
8938
9039
  // src/ai/refine.ts
8939
9040
  async function refineSetup(currentSetup, message, conversationHistory, callbacks) {
@@ -9074,10 +9175,11 @@ init_config();
9074
9175
  init_review();
9075
9176
  function detectAgents(dir) {
9076
9177
  const agents = [];
9077
- if (fs29.existsSync(`${dir}/.claude`)) agents.push("claude");
9078
- if (fs29.existsSync(`${dir}/.cursor`)) agents.push("cursor");
9079
- if (fs29.existsSync(`${dir}/.agents`) || fs29.existsSync(`${dir}/AGENTS.md`)) agents.push("codex");
9080
- if (fs29.existsSync(`${dir}/.github/copilot-instructions.md`)) agents.push("github-copilot");
9178
+ if (fs30.existsSync(`${dir}/.claude`)) agents.push("claude");
9179
+ if (fs30.existsSync(`${dir}/.cursor`)) agents.push("cursor");
9180
+ if (fs30.existsSync(`${dir}/.agents`) || fs30.existsSync(`${dir}/AGENTS.md`)) agents.push("codex");
9181
+ if (fs30.existsSync(`${dir}/.opencode`)) agents.push("opencode");
9182
+ if (fs30.existsSync(`${dir}/.github/copilot-instructions.md`)) agents.push("github-copilot");
9081
9183
  return agents;
9082
9184
  }
9083
9185
  async function promptAgent(detected) {
@@ -9085,6 +9187,7 @@ async function promptAgent(detected) {
9085
9187
  { name: "Claude Code", value: "claude", checked: detected?.includes("claude") ?? false },
9086
9188
  { name: "Cursor", value: "cursor", checked: detected?.includes("cursor") ?? false },
9087
9189
  { name: "Codex (OpenAI)", value: "codex", checked: detected?.includes("codex") ?? false },
9190
+ { name: "OpenCode", value: "opencode", checked: detected?.includes("opencode") ?? false },
9088
9191
  { name: "GitHub Copilot", value: "github-copilot", checked: detected?.includes("github-copilot") ?? false }
9089
9192
  ];
9090
9193
  const hasDefaults = detected && detected.length > 0;
@@ -9180,24 +9283,26 @@ async function refineLoop(currentSetup, sessionHistory, summarizeSetup2, printSu
9180
9283
 
9181
9284
  // src/commands/init-display.ts
9182
9285
  import chalk12 from "chalk";
9183
- import fs30 from "fs";
9286
+ import fs31 from "fs";
9184
9287
  function formatWhatChanged(setup) {
9185
9288
  const lines = [];
9186
9289
  const claude = setup.claude;
9187
9290
  const codex = setup.codex;
9188
9291
  const cursor = setup.cursor;
9189
9292
  if (claude?.claudeMd) {
9190
- const action = fs30.existsSync("CLAUDE.md") ? "Updated" : "Created";
9293
+ const action = fs31.existsSync("CLAUDE.md") ? "Updated" : "Created";
9191
9294
  lines.push(`${action} CLAUDE.md`);
9192
9295
  }
9193
- if (codex?.agentsMd) {
9194
- const action = fs30.existsSync("AGENTS.md") ? "Updated" : "Created";
9296
+ const opencode = setup.opencode;
9297
+ if (codex?.agentsMd || opencode?.agentsMd) {
9298
+ const action = fs31.existsSync("AGENTS.md") ? "Updated" : "Created";
9195
9299
  lines.push(`${action} AGENTS.md`);
9196
9300
  }
9197
9301
  const allSkills = [];
9198
9302
  for (const [_platform, obj] of [
9199
9303
  ["claude", claude],
9200
9304
  ["codex", codex],
9305
+ ["opencode", opencode],
9201
9306
  ["cursor", cursor]
9202
9307
  ]) {
9203
9308
  const skills = obj?.skills;
@@ -9234,7 +9339,7 @@ function printSetupSummary(setup) {
9234
9339
  };
9235
9340
  if (claude) {
9236
9341
  if (claude.claudeMd) {
9237
- const icon = fs30.existsSync("CLAUDE.md") ? chalk12.yellow("~") : chalk12.green("+");
9342
+ const icon = fs31.existsSync("CLAUDE.md") ? chalk12.yellow("~") : chalk12.green("+");
9238
9343
  const desc = getDescription("CLAUDE.md");
9239
9344
  console.log(` ${icon} ${chalk12.bold("CLAUDE.md")}`);
9240
9345
  if (desc) console.log(chalk12.dim(` ${desc}`));
@@ -9244,7 +9349,7 @@ function printSetupSummary(setup) {
9244
9349
  if (Array.isArray(skills) && skills.length > 0) {
9245
9350
  for (const skill of skills) {
9246
9351
  const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
9247
- const icon = fs30.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
9352
+ const icon = fs31.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
9248
9353
  const desc = getDescription(skillPath);
9249
9354
  console.log(` ${icon} ${chalk12.bold(skillPath)}`);
9250
9355
  console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
@@ -9255,7 +9360,7 @@ function printSetupSummary(setup) {
9255
9360
  const codex = setup.codex;
9256
9361
  if (codex) {
9257
9362
  if (codex.agentsMd) {
9258
- const icon = fs30.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
9363
+ const icon = fs31.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
9259
9364
  const desc = getDescription("AGENTS.md");
9260
9365
  console.log(` ${icon} ${chalk12.bold("AGENTS.md")}`);
9261
9366
  if (desc) console.log(chalk12.dim(` ${desc}`));
@@ -9265,7 +9370,28 @@ function printSetupSummary(setup) {
9265
9370
  if (Array.isArray(codexSkills) && codexSkills.length > 0) {
9266
9371
  for (const skill of codexSkills) {
9267
9372
  const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
9268
- const icon = fs30.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
9373
+ const icon = fs31.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
9374
+ const desc = getDescription(skillPath);
9375
+ console.log(` ${icon} ${chalk12.bold(skillPath)}`);
9376
+ console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
9377
+ console.log("");
9378
+ }
9379
+ }
9380
+ }
9381
+ const opencode = setup.opencode;
9382
+ if (opencode) {
9383
+ if (opencode.agentsMd && !codex?.agentsMd) {
9384
+ const icon = fs31.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
9385
+ const desc = getDescription("AGENTS.md");
9386
+ console.log(` ${icon} ${chalk12.bold("AGENTS.md")} ${chalk12.dim("(OpenCode)")}`);
9387
+ if (desc) console.log(chalk12.dim(` ${desc}`));
9388
+ console.log("");
9389
+ }
9390
+ const opencodeSkills = opencode.skills;
9391
+ if (Array.isArray(opencodeSkills) && opencodeSkills.length > 0) {
9392
+ for (const skill of opencodeSkills) {
9393
+ const skillPath = `.opencode/skills/${skill.name}/SKILL.md`;
9394
+ const icon = fs31.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
9269
9395
  const desc = getDescription(skillPath);
9270
9396
  console.log(` ${icon} ${chalk12.bold(skillPath)}`);
9271
9397
  console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
@@ -9275,7 +9401,7 @@ function printSetupSummary(setup) {
9275
9401
  }
9276
9402
  if (cursor) {
9277
9403
  if (cursor.cursorrules) {
9278
- const icon = fs30.existsSync(".cursorrules") ? chalk12.yellow("~") : chalk12.green("+");
9404
+ const icon = fs31.existsSync(".cursorrules") ? chalk12.yellow("~") : chalk12.green("+");
9279
9405
  const desc = getDescription(".cursorrules");
9280
9406
  console.log(` ${icon} ${chalk12.bold(".cursorrules")}`);
9281
9407
  if (desc) console.log(chalk12.dim(` ${desc}`));
@@ -9285,7 +9411,7 @@ function printSetupSummary(setup) {
9285
9411
  if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
9286
9412
  for (const skill of cursorSkills) {
9287
9413
  const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
9288
- const icon = fs30.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
9414
+ const icon = fs31.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
9289
9415
  const desc = getDescription(skillPath);
9290
9416
  console.log(` ${icon} ${chalk12.bold(skillPath)}`);
9291
9417
  console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
@@ -9296,7 +9422,7 @@ function printSetupSummary(setup) {
9296
9422
  if (Array.isArray(rulesArr) && rulesArr.length > 0) {
9297
9423
  for (const rule of rulesArr) {
9298
9424
  const rulePath = `.cursor/rules/${rule.filename}`;
9299
- const icon = fs30.existsSync(rulePath) ? chalk12.yellow("~") : chalk12.green("+");
9425
+ const icon = fs31.existsSync(rulePath) ? chalk12.yellow("~") : chalk12.green("+");
9300
9426
  const desc = getDescription(rulePath);
9301
9427
  console.log(` ${icon} ${chalk12.bold(rulePath)}`);
9302
9428
  if (desc) {
@@ -9351,12 +9477,12 @@ function displayTokenUsage() {
9351
9477
  // src/commands/init-helpers.ts
9352
9478
  init_config();
9353
9479
  import chalk13 from "chalk";
9354
- import fs31 from "fs";
9355
- import path25 from "path";
9480
+ import fs32 from "fs";
9481
+ import path26 from "path";
9356
9482
  function isFirstRun(dir) {
9357
- const caliberDir = path25.join(dir, ".caliber");
9483
+ const caliberDir = path26.join(dir, ".caliber");
9358
9484
  try {
9359
- const stat = fs31.statSync(caliberDir);
9485
+ const stat = fs32.statSync(caliberDir);
9360
9486
  return !stat.isDirectory();
9361
9487
  } catch {
9362
9488
  return true;
@@ -9409,8 +9535,8 @@ function ensurePermissions(fingerprint) {
9409
9535
  const settingsPath = ".claude/settings.json";
9410
9536
  let settings = {};
9411
9537
  try {
9412
- if (fs31.existsSync(settingsPath)) {
9413
- settings = JSON.parse(fs31.readFileSync(settingsPath, "utf-8"));
9538
+ if (fs32.existsSync(settingsPath)) {
9539
+ settings = JSON.parse(fs32.readFileSync(settingsPath, "utf-8"));
9414
9540
  }
9415
9541
  } catch {
9416
9542
  }
@@ -9419,12 +9545,12 @@ function ensurePermissions(fingerprint) {
9419
9545
  if (Array.isArray(allow) && allow.length > 0) return;
9420
9546
  permissions.allow = derivePermissions(fingerprint);
9421
9547
  settings.permissions = permissions;
9422
- if (!fs31.existsSync(".claude")) fs31.mkdirSync(".claude", { recursive: true });
9423
- fs31.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
9548
+ if (!fs32.existsSync(".claude")) fs32.mkdirSync(".claude", { recursive: true });
9549
+ fs32.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
9424
9550
  }
9425
9551
  function writeErrorLog(config, rawOutput, error, stopReason) {
9426
9552
  try {
9427
- const logPath = path25.join(process.cwd(), ".caliber", "error-log.md");
9553
+ const logPath = path26.join(process.cwd(), ".caliber", "error-log.md");
9428
9554
  const lines = [
9429
9555
  `# Generation Error \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`,
9430
9556
  "",
@@ -9437,8 +9563,8 @@ function writeErrorLog(config, rawOutput, error, stopReason) {
9437
9563
  lines.push("## Error", "```", error, "```", "");
9438
9564
  }
9439
9565
  lines.push("## Raw LLM Output", "```", rawOutput || "(empty)", "```");
9440
- fs31.mkdirSync(path25.join(process.cwd(), ".caliber"), { recursive: true });
9441
- fs31.writeFileSync(logPath, lines.join("\n"));
9566
+ fs32.mkdirSync(path26.join(process.cwd(), ".caliber"), { recursive: true });
9567
+ fs32.writeFileSync(logPath, lines.join("\n"));
9442
9568
  console.log(chalk13.dim(`
9443
9569
  Error log written to .caliber/error-log.md`));
9444
9570
  } catch {
@@ -9489,13 +9615,13 @@ ${JSON.stringify(checkList, null, 2)}`,
9489
9615
  }
9490
9616
 
9491
9617
  // src/scoring/history.ts
9492
- import fs32 from "fs";
9493
- import path26 from "path";
9618
+ import fs33 from "fs";
9619
+ import path27 from "path";
9494
9620
  var HISTORY_FILE = "score-history.jsonl";
9495
9621
  var MAX_ENTRIES = 500;
9496
9622
  var TRIM_THRESHOLD = MAX_ENTRIES + 50;
9497
9623
  function historyFilePath() {
9498
- return path26.join(CALIBER_DIR, HISTORY_FILE);
9624
+ return path27.join(CALIBER_DIR, HISTORY_FILE);
9499
9625
  }
9500
9626
  function recordScore(result, trigger) {
9501
9627
  const entry = {
@@ -9506,14 +9632,14 @@ function recordScore(result, trigger) {
9506
9632
  trigger
9507
9633
  };
9508
9634
  try {
9509
- fs32.mkdirSync(CALIBER_DIR, { recursive: true });
9635
+ fs33.mkdirSync(CALIBER_DIR, { recursive: true });
9510
9636
  const filePath = historyFilePath();
9511
- fs32.appendFileSync(filePath, JSON.stringify(entry) + "\n");
9512
- const stat = fs32.statSync(filePath);
9637
+ fs33.appendFileSync(filePath, JSON.stringify(entry) + "\n");
9638
+ const stat = fs33.statSync(filePath);
9513
9639
  if (stat.size > TRIM_THRESHOLD * 120) {
9514
- const lines = fs32.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
9640
+ const lines = fs33.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
9515
9641
  if (lines.length > MAX_ENTRIES) {
9516
- fs32.writeFileSync(filePath, lines.slice(-MAX_ENTRIES).join("\n") + "\n");
9642
+ fs33.writeFileSync(filePath, lines.slice(-MAX_ENTRIES).join("\n") + "\n");
9517
9643
  }
9518
9644
  }
9519
9645
  } catch {
@@ -9522,7 +9648,7 @@ function recordScore(result, trigger) {
9522
9648
  function readScoreHistory() {
9523
9649
  const filePath = historyFilePath();
9524
9650
  try {
9525
- const content = fs32.readFileSync(filePath, "utf-8");
9651
+ const content = fs33.readFileSync(filePath, "utf-8");
9526
9652
  const entries = [];
9527
9653
  for (const line of content.split("\n")) {
9528
9654
  if (!line) continue;
@@ -9578,8 +9704,8 @@ async function initCommand(options) {
9578
9704
  console.log(brand.bold("\n CALIBER") + chalk14.dim(" \u2014 setting up continuous sync\n"));
9579
9705
  }
9580
9706
  const platforms = detectPlatforms();
9581
- if (!platforms.claude && !platforms.cursor && !platforms.codex) {
9582
- console.log(chalk14.yellow(" \u26A0 No supported AI platforms detected (Claude, Cursor, Codex)."));
9707
+ if (!platforms.claude && !platforms.cursor && !platforms.codex && !platforms.opencode) {
9708
+ console.log(chalk14.yellow(" \u26A0 No supported AI platforms detected (Claude, Cursor, Codex, OpenCode)."));
9583
9709
  console.log(chalk14.yellow(" Caliber will still generate config files, but they won't be auto-installed.\n"));
9584
9710
  }
9585
9711
  const report = options.debugReport ? new DebugReport() : null;
@@ -9673,9 +9799,9 @@ async function initCommand(options) {
9673
9799
  console.log(` ${chalk14.green("\u2713")} Onboarding hook \u2014 nudges new team members to set up`);
9674
9800
  const { ensureBuiltinSkills: ensureBuiltinSkills2 } = await Promise.resolve().then(() => (init_builtin_skills(), builtin_skills_exports));
9675
9801
  for (const agent of targetAgent) {
9676
- if (agent === "claude" && !fs33.existsSync(".claude")) fs33.mkdirSync(".claude", { recursive: true });
9677
- if (agent === "cursor" && !fs33.existsSync(".cursor")) fs33.mkdirSync(".cursor", { recursive: true });
9678
- if (agent === "codex" && !fs33.existsSync(".agents")) fs33.mkdirSync(".agents", { recursive: true });
9802
+ if (agent === "claude" && !fs34.existsSync(".claude")) fs34.mkdirSync(".claude", { recursive: true });
9803
+ if (agent === "cursor" && !fs34.existsSync(".cursor")) fs34.mkdirSync(".cursor", { recursive: true });
9804
+ if (agent === "codex" && !fs34.existsSync(".agents")) fs34.mkdirSync(".agents", { recursive: true });
9679
9805
  }
9680
9806
  const skillsWritten = ensureBuiltinSkills2();
9681
9807
  if (skillsWritten.length > 0) {
@@ -9732,41 +9858,41 @@ async function initCommand(options) {
9732
9858
  const claudeMdPath = "CLAUDE.md";
9733
9859
  let claudeContent = "";
9734
9860
  try {
9735
- claudeContent = fs33.readFileSync(claudeMdPath, "utf-8");
9861
+ claudeContent = fs34.readFileSync(claudeMdPath, "utf-8");
9736
9862
  } catch {
9737
9863
  }
9738
9864
  if (!claudeContent) {
9739
- claudeContent = `# ${path27.basename(process.cwd())}
9865
+ claudeContent = `# ${path28.basename(process.cwd())}
9740
9866
  `;
9741
9867
  }
9742
9868
  const updatedClaude = appendManagedBlocks2(claudeContent, "claude");
9743
- if (updatedClaude !== claudeContent || !fs33.existsSync(claudeMdPath)) {
9744
- fs33.writeFileSync(claudeMdPath, updatedClaude);
9869
+ if (updatedClaude !== claudeContent || !fs34.existsSync(claudeMdPath)) {
9870
+ fs34.writeFileSync(claudeMdPath, updatedClaude);
9745
9871
  console.log(` ${chalk14.green("\u2713")} CLAUDE.md \u2014 added Caliber sync instructions`);
9746
9872
  }
9747
9873
  if (targetAgent.includes("cursor")) {
9748
- const rulesDir = path27.join(".cursor", "rules");
9749
- if (!fs33.existsSync(rulesDir)) fs33.mkdirSync(rulesDir, { recursive: true });
9874
+ const rulesDir = path28.join(".cursor", "rules");
9875
+ if (!fs34.existsSync(rulesDir)) fs34.mkdirSync(rulesDir, { recursive: true });
9750
9876
  for (const rule of [getCursorPreCommitRule2(), getCursorLearningsRule2(), getCursorSyncRule2(), getCursorSetupRule2()]) {
9751
- fs33.writeFileSync(path27.join(rulesDir, rule.filename), rule.content);
9877
+ fs34.writeFileSync(path28.join(rulesDir, rule.filename), rule.content);
9752
9878
  }
9753
9879
  console.log(` ${chalk14.green("\u2713")} Cursor rules \u2014 added Caliber sync rules`);
9754
9880
  }
9755
9881
  if (targetAgent.includes("github-copilot")) {
9756
- const copilotPath = path27.join(".github", "copilot-instructions.md");
9882
+ const copilotPath = path28.join(".github", "copilot-instructions.md");
9757
9883
  let copilotContent = "";
9758
9884
  try {
9759
- copilotContent = fs33.readFileSync(copilotPath, "utf-8");
9885
+ copilotContent = fs34.readFileSync(copilotPath, "utf-8");
9760
9886
  } catch {
9761
9887
  }
9762
9888
  if (!copilotContent) {
9763
- fs33.mkdirSync(".github", { recursive: true });
9764
- copilotContent = `# ${path27.basename(process.cwd())}
9889
+ fs34.mkdirSync(".github", { recursive: true });
9890
+ copilotContent = `# ${path28.basename(process.cwd())}
9765
9891
  `;
9766
9892
  }
9767
9893
  const updatedCopilot = appendManagedBlocks2(copilotContent, "copilot");
9768
9894
  if (updatedCopilot !== copilotContent) {
9769
- fs33.writeFileSync(copilotPath, updatedCopilot);
9895
+ fs34.writeFileSync(copilotPath, updatedCopilot);
9770
9896
  console.log(` ${chalk14.green("\u2713")} Copilot instructions \u2014 added Caliber sync instructions`);
9771
9897
  }
9772
9898
  }
@@ -10051,7 +10177,7 @@ async function initCommand(options) {
10051
10177
  const { default: ora9 } = await import("ora");
10052
10178
  const writeSpinner = ora9("Writing config files...").start();
10053
10179
  try {
10054
- if (targetAgent.includes("codex") && !fs33.existsSync("AGENTS.md") && !generatedSetup.codex) {
10180
+ if (targetAgent.includes("codex") && !fs34.existsSync("AGENTS.md") && !generatedSetup.codex) {
10055
10181
  const claude = generatedSetup.claude;
10056
10182
  const cursor = generatedSetup.cursor;
10057
10183
  const agentRefs = [];
@@ -10168,9 +10294,9 @@ ${agentRefs.join(" ")}
10168
10294
  }
10169
10295
  if (report) {
10170
10296
  report.markStep("Finished");
10171
- const reportPath = path27.join(process.cwd(), ".caliber", "debug-report.md");
10297
+ const reportPath = path28.join(process.cwd(), ".caliber", "debug-report.md");
10172
10298
  report.write(reportPath);
10173
- console.log(chalk14.dim(` Debug report written to ${path27.relative(process.cwd(), reportPath)}
10299
+ console.log(chalk14.dim(` Debug report written to ${path28.relative(process.cwd(), reportPath)}
10174
10300
  `));
10175
10301
  }
10176
10302
  }
@@ -10209,7 +10335,7 @@ function undoCommand() {
10209
10335
 
10210
10336
  // src/commands/status.ts
10211
10337
  import chalk16 from "chalk";
10212
- import fs34 from "fs";
10338
+ import fs35 from "fs";
10213
10339
  init_config();
10214
10340
  init_resolve_caliber();
10215
10341
  async function statusCommand(options) {
@@ -10238,7 +10364,7 @@ async function statusCommand(options) {
10238
10364
  }
10239
10365
  console.log(` Files managed: ${chalk16.cyan(manifest.entries.length.toString())}`);
10240
10366
  for (const entry of manifest.entries) {
10241
- const exists = fs34.existsSync(entry.path);
10367
+ const exists = fs35.existsSync(entry.path);
10242
10368
  const icon = exists ? chalk16.green("\u2713") : chalk16.red("\u2717");
10243
10369
  console.log(` ${icon} ${entry.path} (${entry.action})`);
10244
10370
  }
@@ -10395,9 +10521,9 @@ async function regenerateCommand(options) {
10395
10521
  }
10396
10522
 
10397
10523
  // src/commands/score.ts
10398
- import fs35 from "fs";
10524
+ import fs36 from "fs";
10399
10525
  import os7 from "os";
10400
- import path28 from "path";
10526
+ import path29 from "path";
10401
10527
  import { execFileSync as execFileSync2 } from "child_process";
10402
10528
  import chalk18 from "chalk";
10403
10529
  init_resolve_caliber();
@@ -10405,7 +10531,7 @@ var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".cursorrules", "CALIBER_LEARNINGS
10405
10531
  var CONFIG_DIRS = [".claude", ".cursor"];
10406
10532
  function scoreBaseRef(ref, target) {
10407
10533
  if (!/^[\w.\-/~^@{}]+$/.test(ref)) return null;
10408
- const tmpDir = fs35.mkdtempSync(path28.join(os7.tmpdir(), "caliber-compare-"));
10534
+ const tmpDir = fs36.mkdtempSync(path29.join(os7.tmpdir(), "caliber-compare-"));
10409
10535
  try {
10410
10536
  for (const file of CONFIG_FILES) {
10411
10537
  try {
@@ -10413,7 +10539,7 @@ function scoreBaseRef(ref, target) {
10413
10539
  encoding: "utf-8",
10414
10540
  stdio: ["pipe", "pipe", "pipe"]
10415
10541
  });
10416
- fs35.writeFileSync(path28.join(tmpDir, file), content);
10542
+ fs36.writeFileSync(path29.join(tmpDir, file), content);
10417
10543
  } catch {
10418
10544
  }
10419
10545
  }
@@ -10424,13 +10550,13 @@ function scoreBaseRef(ref, target) {
10424
10550
  stdio: ["pipe", "pipe", "pipe"]
10425
10551
  }).trim().split("\n").filter(Boolean);
10426
10552
  for (const file of files) {
10427
- const filePath = path28.join(tmpDir, file);
10428
- fs35.mkdirSync(path28.dirname(filePath), { recursive: true });
10553
+ const filePath = path29.join(tmpDir, file);
10554
+ fs36.mkdirSync(path29.dirname(filePath), { recursive: true });
10429
10555
  const content = execFileSync2("git", ["show", `${ref}:${file}`], {
10430
10556
  encoding: "utf-8",
10431
10557
  stdio: ["pipe", "pipe", "pipe"]
10432
10558
  });
10433
- fs35.writeFileSync(filePath, content);
10559
+ fs36.writeFileSync(filePath, content);
10434
10560
  }
10435
10561
  } catch {
10436
10562
  }
@@ -10440,7 +10566,7 @@ function scoreBaseRef(ref, target) {
10440
10566
  } catch {
10441
10567
  return null;
10442
10568
  } finally {
10443
- fs35.rmSync(tmpDir, { recursive: true, force: true });
10569
+ fs36.rmSync(tmpDir, { recursive: true, force: true });
10444
10570
  }
10445
10571
  }
10446
10572
  async function scoreCommand(options) {
@@ -10524,8 +10650,8 @@ async function scoreCommand(options) {
10524
10650
  }
10525
10651
 
10526
10652
  // src/commands/refresh.ts
10527
- import fs39 from "fs";
10528
- import path32 from "path";
10653
+ import fs40 from "fs";
10654
+ import path33 from "path";
10529
10655
  import chalk19 from "chalk";
10530
10656
  import ora6 from "ora";
10531
10657
 
@@ -10604,48 +10730,52 @@ function collectDiff(lastSha) {
10604
10730
 
10605
10731
  // src/writers/refresh.ts
10606
10732
  init_pre_commit_block();
10607
- import fs36 from "fs";
10608
- import path29 from "path";
10733
+ import fs37 from "fs";
10734
+ import path30 from "path";
10609
10735
  function writeRefreshDocs(docs) {
10610
10736
  const written = [];
10737
+ if (docs.agentsMd) {
10738
+ fs37.writeFileSync("AGENTS.md", appendLearningsBlock(appendPreCommitBlock(docs.agentsMd)));
10739
+ written.push("AGENTS.md");
10740
+ }
10611
10741
  if (docs.claudeMd) {
10612
- fs36.writeFileSync("CLAUDE.md", appendLearningsBlock(appendPreCommitBlock(docs.claudeMd)));
10742
+ fs37.writeFileSync("CLAUDE.md", appendLearningsBlock(appendPreCommitBlock(docs.claudeMd)));
10613
10743
  written.push("CLAUDE.md");
10614
10744
  }
10615
10745
  if (docs.readmeMd) {
10616
- fs36.writeFileSync("README.md", docs.readmeMd);
10746
+ fs37.writeFileSync("README.md", docs.readmeMd);
10617
10747
  written.push("README.md");
10618
10748
  }
10619
10749
  if (docs.cursorrules) {
10620
- fs36.writeFileSync(".cursorrules", docs.cursorrules);
10750
+ fs37.writeFileSync(".cursorrules", docs.cursorrules);
10621
10751
  written.push(".cursorrules");
10622
10752
  }
10623
10753
  if (docs.cursorRules) {
10624
- const rulesDir = path29.join(".cursor", "rules");
10625
- if (!fs36.existsSync(rulesDir)) fs36.mkdirSync(rulesDir, { recursive: true });
10754
+ const rulesDir = path30.join(".cursor", "rules");
10755
+ if (!fs37.existsSync(rulesDir)) fs37.mkdirSync(rulesDir, { recursive: true });
10626
10756
  for (const rule of docs.cursorRules) {
10627
- fs36.writeFileSync(path29.join(rulesDir, rule.filename), rule.content);
10757
+ fs37.writeFileSync(path30.join(rulesDir, rule.filename), rule.content);
10628
10758
  written.push(`.cursor/rules/${rule.filename}`);
10629
10759
  }
10630
10760
  }
10631
10761
  if (docs.claudeSkills) {
10632
- const skillsDir = path29.join(".claude", "skills");
10633
- if (!fs36.existsSync(skillsDir)) fs36.mkdirSync(skillsDir, { recursive: true });
10762
+ const skillsDir = path30.join(".claude", "skills");
10763
+ if (!fs37.existsSync(skillsDir)) fs37.mkdirSync(skillsDir, { recursive: true });
10634
10764
  for (const skill of docs.claudeSkills) {
10635
- fs36.writeFileSync(path29.join(skillsDir, skill.filename), skill.content);
10765
+ fs37.writeFileSync(path30.join(skillsDir, skill.filename), skill.content);
10636
10766
  written.push(`.claude/skills/${skill.filename}`);
10637
10767
  }
10638
10768
  }
10639
10769
  if (docs.copilotInstructions) {
10640
- fs36.mkdirSync(".github", { recursive: true });
10641
- fs36.writeFileSync(path29.join(".github", "copilot-instructions.md"), appendLearningsBlock(appendPreCommitBlock(docs.copilotInstructions)));
10770
+ fs37.mkdirSync(".github", { recursive: true });
10771
+ fs37.writeFileSync(path30.join(".github", "copilot-instructions.md"), appendLearningsBlock(appendPreCommitBlock(docs.copilotInstructions)));
10642
10772
  written.push(".github/copilot-instructions.md");
10643
10773
  }
10644
10774
  if (docs.copilotInstructionFiles) {
10645
- const instructionsDir = path29.join(".github", "instructions");
10646
- fs36.mkdirSync(instructionsDir, { recursive: true });
10775
+ const instructionsDir = path30.join(".github", "instructions");
10776
+ fs37.mkdirSync(instructionsDir, { recursive: true });
10647
10777
  for (const file of docs.copilotInstructionFiles) {
10648
- fs36.writeFileSync(path29.join(instructionsDir, file.filename), file.content);
10778
+ fs37.writeFileSync(path30.join(instructionsDir, file.filename), file.content);
10649
10779
  written.push(`.github/instructions/${file.filename}`);
10650
10780
  }
10651
10781
  }
@@ -10693,6 +10823,10 @@ Changed files: ${diff.changedFiles.join(", ")}`);
10693
10823
  parts.push(diff.unstaged);
10694
10824
  }
10695
10825
  parts.push("\n--- Current Documentation ---");
10826
+ if (existingDocs.agentsMd) {
10827
+ parts.push("\n[AGENTS.md]");
10828
+ parts.push(existingDocs.agentsMd);
10829
+ }
10696
10830
  if (existingDocs.claudeMd) {
10697
10831
  parts.push("\n[CLAUDE.md]");
10698
10832
  parts.push(existingDocs.claudeMd);
@@ -10731,8 +10865,8 @@ Changed files: ${diff.changedFiles.join(", ")}`);
10731
10865
  }
10732
10866
 
10733
10867
  // src/learner/writer.ts
10734
- import fs37 from "fs";
10735
- import path30 from "path";
10868
+ import fs38 from "fs";
10869
+ import path31 from "path";
10736
10870
 
10737
10871
  // src/learner/utils.ts
10738
10872
  var TYPE_PREFIX_RE = /^\*\*\[[^\]]+\]\*\*\s*/;
@@ -10849,20 +10983,20 @@ function deduplicateLearnedItems(existing, incoming) {
10849
10983
  }
10850
10984
  function writeLearnedSectionTo(filePath, header, existing, incoming, mode) {
10851
10985
  const { merged, newCount, newItems } = deduplicateLearnedItems(existing, incoming);
10852
- fs37.writeFileSync(filePath, header + merged + "\n");
10853
- if (mode) fs37.chmodSync(filePath, mode);
10986
+ fs38.writeFileSync(filePath, header + merged + "\n");
10987
+ if (mode) fs38.chmodSync(filePath, mode);
10854
10988
  return { newCount, newItems };
10855
10989
  }
10856
10990
  function writeLearnedSection(content) {
10857
10991
  return writeLearnedSectionTo(LEARNINGS_FILE, LEARNINGS_HEADER, readLearnedSection(), content);
10858
10992
  }
10859
10993
  function writeLearnedSkill(skill) {
10860
- const skillDir = path30.join(".claude", "skills", skill.name);
10861
- if (!fs37.existsSync(skillDir)) fs37.mkdirSync(skillDir, { recursive: true });
10862
- const skillPath = path30.join(skillDir, "SKILL.md");
10863
- if (!skill.isNew && fs37.existsSync(skillPath)) {
10864
- const existing = fs37.readFileSync(skillPath, "utf-8");
10865
- fs37.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
10994
+ const skillDir = path31.join(".claude", "skills", skill.name);
10995
+ if (!fs38.existsSync(skillDir)) fs38.mkdirSync(skillDir, { recursive: true });
10996
+ const skillPath = path31.join(skillDir, "SKILL.md");
10997
+ if (!skill.isNew && fs38.existsSync(skillPath)) {
10998
+ const existing = fs38.readFileSync(skillPath, "utf-8");
10999
+ fs38.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
10866
11000
  } else {
10867
11001
  const frontmatter = [
10868
11002
  "---",
@@ -10871,12 +11005,12 @@ function writeLearnedSkill(skill) {
10871
11005
  "---",
10872
11006
  ""
10873
11007
  ].join("\n");
10874
- fs37.writeFileSync(skillPath, frontmatter + skill.content);
11008
+ fs38.writeFileSync(skillPath, frontmatter + skill.content);
10875
11009
  }
10876
11010
  return skillPath;
10877
11011
  }
10878
11012
  function writePersonalLearnedSection(content) {
10879
- if (!fs37.existsSync(AUTH_DIR)) fs37.mkdirSync(AUTH_DIR, { recursive: true });
11013
+ if (!fs38.existsSync(AUTH_DIR)) fs38.mkdirSync(AUTH_DIR, { recursive: true });
10880
11014
  return writeLearnedSectionTo(PERSONAL_LEARNINGS_FILE, PERSONAL_LEARNINGS_HEADER, readPersonalLearnings(), content, 384);
10881
11015
  }
10882
11016
  function addLearning(bullet, scope = "project") {
@@ -10889,38 +11023,38 @@ function addLearning(bullet, scope = "project") {
10889
11023
  return { file: LEARNINGS_FILE, added: result.newCount > 0 };
10890
11024
  }
10891
11025
  function readPersonalLearnings() {
10892
- if (!fs37.existsSync(PERSONAL_LEARNINGS_FILE)) return null;
10893
- const content = fs37.readFileSync(PERSONAL_LEARNINGS_FILE, "utf-8");
11026
+ if (!fs38.existsSync(PERSONAL_LEARNINGS_FILE)) return null;
11027
+ const content = fs38.readFileSync(PERSONAL_LEARNINGS_FILE, "utf-8");
10894
11028
  const bullets = content.split("\n").filter((l) => l.startsWith("- ")).join("\n");
10895
11029
  return bullets || null;
10896
11030
  }
10897
11031
  function readLearnedSection() {
10898
- if (fs37.existsSync(LEARNINGS_FILE)) {
10899
- const content2 = fs37.readFileSync(LEARNINGS_FILE, "utf-8");
11032
+ if (fs38.existsSync(LEARNINGS_FILE)) {
11033
+ const content2 = fs38.readFileSync(LEARNINGS_FILE, "utf-8");
10900
11034
  const bullets = content2.split("\n").filter((l) => l.startsWith("- ")).join("\n");
10901
11035
  return bullets || null;
10902
11036
  }
10903
11037
  const claudeMdPath = "CLAUDE.md";
10904
- if (!fs37.existsSync(claudeMdPath)) return null;
10905
- const content = fs37.readFileSync(claudeMdPath, "utf-8");
11038
+ if (!fs38.existsSync(claudeMdPath)) return null;
11039
+ const content = fs38.readFileSync(claudeMdPath, "utf-8");
10906
11040
  const startIdx = content.indexOf(LEARNED_START);
10907
11041
  const endIdx = content.indexOf(LEARNED_END);
10908
11042
  if (startIdx === -1 || endIdx === -1) return null;
10909
11043
  return content.slice(startIdx + LEARNED_START.length, endIdx).trim() || null;
10910
11044
  }
10911
11045
  function migrateInlineLearnings() {
10912
- if (fs37.existsSync(LEARNINGS_FILE)) return false;
11046
+ if (fs38.existsSync(LEARNINGS_FILE)) return false;
10913
11047
  const claudeMdPath = "CLAUDE.md";
10914
- if (!fs37.existsSync(claudeMdPath)) return false;
10915
- const content = fs37.readFileSync(claudeMdPath, "utf-8");
11048
+ if (!fs38.existsSync(claudeMdPath)) return false;
11049
+ const content = fs38.readFileSync(claudeMdPath, "utf-8");
10916
11050
  const startIdx = content.indexOf(LEARNED_START);
10917
11051
  const endIdx = content.indexOf(LEARNED_END);
10918
11052
  if (startIdx === -1 || endIdx === -1) return false;
10919
11053
  const section = content.slice(startIdx + LEARNED_START.length, endIdx).trim();
10920
11054
  if (!section) return false;
10921
- fs37.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
11055
+ fs38.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
10922
11056
  const cleaned = content.slice(0, startIdx) + content.slice(endIdx + LEARNED_END.length);
10923
- fs37.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
11057
+ fs38.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
10924
11058
  return true;
10925
11059
  }
10926
11060
 
@@ -10934,11 +11068,11 @@ function log2(quiet, ...args) {
10934
11068
  function discoverGitRepos(parentDir) {
10935
11069
  const repos = [];
10936
11070
  try {
10937
- const entries = fs39.readdirSync(parentDir, { withFileTypes: true });
11071
+ const entries = fs40.readdirSync(parentDir, { withFileTypes: true });
10938
11072
  for (const entry of entries) {
10939
11073
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
10940
- const childPath = path32.join(parentDir, entry.name);
10941
- if (fs39.existsSync(path32.join(childPath, ".git"))) {
11074
+ const childPath = path33.join(parentDir, entry.name);
11075
+ if (fs40.existsSync(path33.join(childPath, ".git"))) {
10942
11076
  repos.push(childPath);
10943
11077
  }
10944
11078
  }
@@ -11024,9 +11158,9 @@ async function refreshSingleRepo(repoDir, options) {
11024
11158
  const filesToWrite = response.docsUpdated || [];
11025
11159
  const preRefreshContents = /* @__PURE__ */ new Map();
11026
11160
  for (const filePath of filesToWrite) {
11027
- const fullPath = path32.resolve(repoDir, filePath);
11161
+ const fullPath = path33.resolve(repoDir, filePath);
11028
11162
  try {
11029
- preRefreshContents.set(filePath, fs39.readFileSync(fullPath, "utf-8"));
11163
+ preRefreshContents.set(filePath, fs40.readFileSync(fullPath, "utf-8"));
11030
11164
  } catch {
11031
11165
  preRefreshContents.set(filePath, null);
11032
11166
  }
@@ -11037,14 +11171,14 @@ async function refreshSingleRepo(repoDir, options) {
11037
11171
  const postScore = computeLocalScore(repoDir, targetAgent);
11038
11172
  if (postScore.score < preScore.score) {
11039
11173
  for (const [filePath, content] of preRefreshContents) {
11040
- const fullPath = path32.resolve(repoDir, filePath);
11174
+ const fullPath = path33.resolve(repoDir, filePath);
11041
11175
  if (content === null) {
11042
11176
  try {
11043
- fs39.unlinkSync(fullPath);
11177
+ fs40.unlinkSync(fullPath);
11044
11178
  } catch {
11045
11179
  }
11046
11180
  } else {
11047
- fs39.writeFileSync(fullPath, content);
11181
+ fs40.writeFileSync(fullPath, content);
11048
11182
  }
11049
11183
  }
11050
11184
  spinner?.warn(`${prefix}Refresh reverted \u2014 score would drop from ${preScore.score} to ${postScore.score}`);
@@ -11099,7 +11233,7 @@ async function refreshCommand(options) {
11099
11233
  `));
11100
11234
  const originalDir = process.cwd();
11101
11235
  for (const repo of repos) {
11102
- const repoName = path32.basename(repo);
11236
+ const repoName = path33.basename(repo);
11103
11237
  try {
11104
11238
  process.chdir(repo);
11105
11239
  await refreshSingleRepo(repo, { ...options, label: repoName });
@@ -11120,7 +11254,7 @@ async function refreshCommand(options) {
11120
11254
 
11121
11255
  // src/commands/hooks.ts
11122
11256
  import chalk20 from "chalk";
11123
- import fs40 from "fs";
11257
+ import fs41 from "fs";
11124
11258
  var HOOKS = [
11125
11259
  {
11126
11260
  id: "session-end",
@@ -11164,11 +11298,11 @@ async function hooksCommand(options) {
11164
11298
  console.log(chalk20.green(" \u2713") + ` ${hook.label} enabled`);
11165
11299
  }
11166
11300
  }
11167
- if (fs40.existsSync(".claude")) {
11301
+ if (fs41.existsSync(".claude")) {
11168
11302
  const r = installLearningHooks();
11169
11303
  if (r.installed) console.log(chalk20.green(" \u2713") + " Claude Code learning hooks enabled");
11170
11304
  }
11171
- if (fs40.existsSync(".cursor")) {
11305
+ if (fs41.existsSync(".cursor")) {
11172
11306
  const r = installCursorLearningHooks();
11173
11307
  if (r.installed) console.log(chalk20.green(" \u2713") + " Cursor learning hooks enabled");
11174
11308
  }
@@ -11336,8 +11470,8 @@ async function configCommand() {
11336
11470
  }
11337
11471
 
11338
11472
  // src/commands/learn.ts
11339
- import fs44 from "fs";
11340
- import path36 from "path";
11473
+ import fs45 from "fs";
11474
+ import path37 from "path";
11341
11475
  import chalk23 from "chalk";
11342
11476
 
11343
11477
  // src/learner/stdin.ts
@@ -11368,8 +11502,8 @@ function readStdin() {
11368
11502
  }
11369
11503
 
11370
11504
  // src/learner/storage.ts
11371
- import fs41 from "fs";
11372
- import path33 from "path";
11505
+ import fs42 from "fs";
11506
+ import path34 from "path";
11373
11507
  var MAX_RESPONSE_LENGTH = 2e3;
11374
11508
  var DEFAULT_STATE = {
11375
11509
  sessionId: null,
@@ -11378,15 +11512,15 @@ var DEFAULT_STATE = {
11378
11512
  lastAnalysisEventCount: 0
11379
11513
  };
11380
11514
  function ensureLearningDir() {
11381
- if (!fs41.existsSync(getLearningDir())) {
11382
- fs41.mkdirSync(getLearningDir(), { recursive: true });
11515
+ if (!fs42.existsSync(getLearningDir())) {
11516
+ fs42.mkdirSync(getLearningDir(), { recursive: true });
11383
11517
  }
11384
11518
  }
11385
11519
  function sessionFilePath() {
11386
- return path33.join(getLearningDir(), LEARNING_SESSION_FILE);
11520
+ return path34.join(getLearningDir(), LEARNING_SESSION_FILE);
11387
11521
  }
11388
11522
  function stateFilePath() {
11389
- return path33.join(getLearningDir(), LEARNING_STATE_FILE);
11523
+ return path34.join(getLearningDir(), LEARNING_STATE_FILE);
11390
11524
  }
11391
11525
  function truncateResponse(response) {
11392
11526
  const str = JSON.stringify(response);
@@ -11396,10 +11530,10 @@ function truncateResponse(response) {
11396
11530
  function trimSessionFileIfNeeded(filePath) {
11397
11531
  const state = readState2();
11398
11532
  if (state.eventCount + 1 > LEARNING_MAX_EVENTS) {
11399
- const lines = fs41.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
11533
+ const lines = fs42.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
11400
11534
  if (lines.length > LEARNING_MAX_EVENTS) {
11401
11535
  const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
11402
- fs41.writeFileSync(filePath, kept.join("\n") + "\n");
11536
+ fs42.writeFileSync(filePath, kept.join("\n") + "\n");
11403
11537
  }
11404
11538
  }
11405
11539
  }
@@ -11407,19 +11541,19 @@ function appendEvent(event) {
11407
11541
  ensureLearningDir();
11408
11542
  const truncated = { ...event, tool_response: truncateResponse(event.tool_response) };
11409
11543
  const filePath = sessionFilePath();
11410
- fs41.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
11544
+ fs42.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
11411
11545
  trimSessionFileIfNeeded(filePath);
11412
11546
  }
11413
11547
  function appendPromptEvent(event) {
11414
11548
  ensureLearningDir();
11415
11549
  const filePath = sessionFilePath();
11416
- fs41.appendFileSync(filePath, JSON.stringify(event) + "\n");
11550
+ fs42.appendFileSync(filePath, JSON.stringify(event) + "\n");
11417
11551
  trimSessionFileIfNeeded(filePath);
11418
11552
  }
11419
11553
  function readAllEvents() {
11420
11554
  const filePath = sessionFilePath();
11421
- if (!fs41.existsSync(filePath)) return [];
11422
- const lines = fs41.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
11555
+ if (!fs42.existsSync(filePath)) return [];
11556
+ const lines = fs42.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
11423
11557
  const events = [];
11424
11558
  for (const line of lines) {
11425
11559
  try {
@@ -11431,26 +11565,26 @@ function readAllEvents() {
11431
11565
  }
11432
11566
  function getEventCount() {
11433
11567
  const filePath = sessionFilePath();
11434
- if (!fs41.existsSync(filePath)) return 0;
11435
- const content = fs41.readFileSync(filePath, "utf-8");
11568
+ if (!fs42.existsSync(filePath)) return 0;
11569
+ const content = fs42.readFileSync(filePath, "utf-8");
11436
11570
  return content.split("\n").filter(Boolean).length;
11437
11571
  }
11438
11572
  function clearSession() {
11439
11573
  const filePath = sessionFilePath();
11440
- if (fs41.existsSync(filePath)) fs41.unlinkSync(filePath);
11574
+ if (fs42.existsSync(filePath)) fs42.unlinkSync(filePath);
11441
11575
  }
11442
11576
  function readState2() {
11443
11577
  const filePath = stateFilePath();
11444
- if (!fs41.existsSync(filePath)) return { ...DEFAULT_STATE };
11578
+ if (!fs42.existsSync(filePath)) return { ...DEFAULT_STATE };
11445
11579
  try {
11446
- return JSON.parse(fs41.readFileSync(filePath, "utf-8"));
11580
+ return JSON.parse(fs42.readFileSync(filePath, "utf-8"));
11447
11581
  } catch {
11448
11582
  return { ...DEFAULT_STATE };
11449
11583
  }
11450
11584
  }
11451
11585
  function writeState2(state) {
11452
11586
  ensureLearningDir();
11453
- fs41.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
11587
+ fs42.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
11454
11588
  }
11455
11589
  function resetState() {
11456
11590
  writeState2({ ...DEFAULT_STATE });
@@ -11458,16 +11592,16 @@ function resetState() {
11458
11592
  var LOCK_FILE2 = "finalize.lock";
11459
11593
  var LOCK_STALE_MS = 5 * 60 * 1e3;
11460
11594
  function lockFilePath() {
11461
- return path33.join(getLearningDir(), LOCK_FILE2);
11595
+ return path34.join(getLearningDir(), LOCK_FILE2);
11462
11596
  }
11463
11597
  function acquireFinalizeLock() {
11464
11598
  ensureLearningDir();
11465
11599
  const lockPath = lockFilePath();
11466
- if (fs41.existsSync(lockPath)) {
11600
+ if (fs42.existsSync(lockPath)) {
11467
11601
  try {
11468
- const stat = fs41.statSync(lockPath);
11602
+ const stat = fs42.statSync(lockPath);
11469
11603
  if (Date.now() - stat.mtimeMs < LOCK_STALE_MS) {
11470
- const pid = parseInt(fs41.readFileSync(lockPath, "utf-8").trim(), 10);
11604
+ const pid = parseInt(fs42.readFileSync(lockPath, "utf-8").trim(), 10);
11471
11605
  if (!isNaN(pid) && isProcessAlive(pid)) {
11472
11606
  return false;
11473
11607
  }
@@ -11475,12 +11609,12 @@ function acquireFinalizeLock() {
11475
11609
  } catch {
11476
11610
  }
11477
11611
  try {
11478
- fs41.unlinkSync(lockPath);
11612
+ fs42.unlinkSync(lockPath);
11479
11613
  } catch {
11480
11614
  }
11481
11615
  }
11482
11616
  try {
11483
- fs41.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
11617
+ fs42.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
11484
11618
  return true;
11485
11619
  } catch {
11486
11620
  return false;
@@ -11497,30 +11631,30 @@ function isProcessAlive(pid) {
11497
11631
  function releaseFinalizeLock() {
11498
11632
  const lockPath = lockFilePath();
11499
11633
  try {
11500
- if (fs41.existsSync(lockPath)) fs41.unlinkSync(lockPath);
11634
+ if (fs42.existsSync(lockPath)) fs42.unlinkSync(lockPath);
11501
11635
  } catch {
11502
11636
  }
11503
11637
  }
11504
11638
 
11505
11639
  // src/lib/notifications.ts
11506
- import fs42 from "fs";
11507
- import path34 from "path";
11640
+ import fs43 from "fs";
11641
+ import path35 from "path";
11508
11642
  import chalk22 from "chalk";
11509
11643
  function notificationFilePath() {
11510
- return path34.join(getLearningDir(), "last-finalize-summary.json");
11644
+ return path35.join(getLearningDir(), "last-finalize-summary.json");
11511
11645
  }
11512
11646
  function writeFinalizeSummary(summary) {
11513
11647
  try {
11514
11648
  ensureLearningDir();
11515
- fs42.writeFileSync(notificationFilePath(), JSON.stringify(summary, null, 2));
11649
+ fs43.writeFileSync(notificationFilePath(), JSON.stringify(summary, null, 2));
11516
11650
  } catch {
11517
11651
  }
11518
11652
  }
11519
11653
  function checkPendingNotifications() {
11520
11654
  try {
11521
- if (!fs42.existsSync(notificationFilePath())) return;
11522
- const raw = fs42.readFileSync(notificationFilePath(), "utf-8");
11523
- fs42.unlinkSync(notificationFilePath());
11655
+ if (!fs43.existsSync(notificationFilePath())) return;
11656
+ const raw = fs43.readFileSync(notificationFilePath(), "utf-8");
11657
+ fs43.unlinkSync(notificationFilePath());
11524
11658
  const summary = JSON.parse(raw);
11525
11659
  if (!summary.newItemCount || summary.newItemCount === 0) return;
11526
11660
  const wasteLabel = summary.wasteTokens > 0 ? ` (~${summary.wasteTokens.toLocaleString()} wasted tokens captured)` : "";
@@ -11536,7 +11670,7 @@ function checkPendingNotifications() {
11536
11670
  console.log("");
11537
11671
  } catch {
11538
11672
  try {
11539
- fs42.unlinkSync(notificationFilePath());
11673
+ fs43.unlinkSync(notificationFilePath());
11540
11674
  } catch {
11541
11675
  }
11542
11676
  }
@@ -11688,8 +11822,8 @@ function calculateSessionWaste(events) {
11688
11822
  init_config();
11689
11823
 
11690
11824
  // src/learner/roi.ts
11691
- import fs43 from "fs";
11692
- import path35 from "path";
11825
+ import fs44 from "fs";
11826
+ import path36 from "path";
11693
11827
  var DEFAULT_TOTALS = {
11694
11828
  totalWasteTokens: 0,
11695
11829
  totalWasteSeconds: 0,
@@ -11703,19 +11837,19 @@ var DEFAULT_TOTALS = {
11703
11837
  lastSessionTimestamp: ""
11704
11838
  };
11705
11839
  function roiFilePath() {
11706
- return path35.join(getLearningDir(), LEARNING_ROI_FILE);
11840
+ return path36.join(getLearningDir(), LEARNING_ROI_FILE);
11707
11841
  }
11708
11842
  function readROIStats() {
11709
11843
  const filePath = roiFilePath();
11710
- if (!fs43.existsSync(filePath)) {
11844
+ if (!fs44.existsSync(filePath)) {
11711
11845
  return { learnings: [], sessions: [], totals: { ...DEFAULT_TOTALS } };
11712
11846
  }
11713
11847
  try {
11714
- return JSON.parse(fs43.readFileSync(filePath, "utf-8"));
11848
+ return JSON.parse(fs44.readFileSync(filePath, "utf-8"));
11715
11849
  } catch {
11716
11850
  try {
11717
11851
  const corruptPath = filePath + ".corrupt";
11718
- fs43.renameSync(filePath, corruptPath);
11852
+ fs44.renameSync(filePath, corruptPath);
11719
11853
  console.error(`caliber: roi-stats.json was corrupt \u2014 renamed to ${corruptPath}`);
11720
11854
  } catch {
11721
11855
  }
@@ -11724,7 +11858,7 @@ function readROIStats() {
11724
11858
  }
11725
11859
  function writeROIStats(stats) {
11726
11860
  ensureLearningDir();
11727
- fs43.writeFileSync(roiFilePath(), JSON.stringify(stats, null, 2));
11861
+ fs44.writeFileSync(roiFilePath(), JSON.stringify(stats, null, 2));
11728
11862
  }
11729
11863
  function recalculateTotals(stats) {
11730
11864
  const totals = stats.totals;
@@ -11933,9 +12067,9 @@ var AUTO_SETTLE_MS = 200;
11933
12067
  var INCREMENTAL_INTERVAL = 50;
11934
12068
  function writeFinalizeError(message) {
11935
12069
  try {
11936
- const errorPath = path36.join(getLearningDir(), LEARNING_LAST_ERROR_FILE);
11937
- if (!fs44.existsSync(getLearningDir())) fs44.mkdirSync(getLearningDir(), { recursive: true });
11938
- fs44.writeFileSync(errorPath, JSON.stringify({
12070
+ const errorPath = path37.join(getLearningDir(), LEARNING_LAST_ERROR_FILE);
12071
+ if (!fs45.existsSync(getLearningDir())) fs45.mkdirSync(getLearningDir(), { recursive: true });
12072
+ fs45.writeFileSync(errorPath, JSON.stringify({
11939
12073
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
11940
12074
  error: message,
11941
12075
  pid: process.pid
@@ -11945,9 +12079,9 @@ function writeFinalizeError(message) {
11945
12079
  }
11946
12080
  function readFinalizeError() {
11947
12081
  try {
11948
- const errorPath = path36.join(getLearningDir(), LEARNING_LAST_ERROR_FILE);
11949
- if (!fs44.existsSync(errorPath)) return null;
11950
- return JSON.parse(fs44.readFileSync(errorPath, "utf-8"));
12082
+ const errorPath = path37.join(getLearningDir(), LEARNING_LAST_ERROR_FILE);
12083
+ if (!fs45.existsSync(errorPath)) return null;
12084
+ return JSON.parse(fs45.readFileSync(errorPath, "utf-8"));
11951
12085
  } catch {
11952
12086
  return null;
11953
12087
  }
@@ -11994,14 +12128,14 @@ async function learnObserveCommand(options) {
11994
12128
  const { resolveCaliber: resolveCaliber2 } = await Promise.resolve().then(() => (init_resolve_caliber(), resolve_caliber_exports));
11995
12129
  const bin = resolveCaliber2();
11996
12130
  const { spawn: spawn4 } = await import("child_process");
11997
- const logPath = path36.join(getLearningDir(), LEARNING_FINALIZE_LOG);
11998
- if (!fs44.existsSync(getLearningDir())) fs44.mkdirSync(getLearningDir(), { recursive: true });
11999
- const logFd = fs44.openSync(logPath, "a");
12131
+ const logPath = path37.join(getLearningDir(), LEARNING_FINALIZE_LOG);
12132
+ if (!fs45.existsSync(getLearningDir())) fs45.mkdirSync(getLearningDir(), { recursive: true });
12133
+ const logFd = fs45.openSync(logPath, "a");
12000
12134
  spawn4(bin, ["learn", "finalize", "--auto", "--incremental"], {
12001
12135
  detached: true,
12002
12136
  stdio: ["ignore", logFd, logFd]
12003
12137
  }).unref();
12004
- fs44.closeSync(logFd);
12138
+ fs45.closeSync(logFd);
12005
12139
  } catch {
12006
12140
  }
12007
12141
  }
@@ -12208,7 +12342,7 @@ async function learnFinalizeCommand(options) {
12208
12342
  }
12209
12343
  async function learnInstallCommand() {
12210
12344
  let anyInstalled = false;
12211
- if (fs44.existsSync(".claude")) {
12345
+ if (fs45.existsSync(".claude")) {
12212
12346
  const r = installLearningHooks();
12213
12347
  if (r.installed) {
12214
12348
  console.log(chalk23.green("\u2713") + " Claude Code learning hooks installed");
@@ -12217,7 +12351,7 @@ async function learnInstallCommand() {
12217
12351
  console.log(chalk23.dim(" Claude Code hooks already installed"));
12218
12352
  }
12219
12353
  }
12220
- if (fs44.existsSync(".cursor")) {
12354
+ if (fs45.existsSync(".cursor")) {
12221
12355
  const r = installCursorLearningHooks();
12222
12356
  if (r.installed) {
12223
12357
  console.log(chalk23.green("\u2713") + " Cursor learning hooks installed");
@@ -12226,7 +12360,7 @@ async function learnInstallCommand() {
12226
12360
  console.log(chalk23.dim(" Cursor hooks already installed"));
12227
12361
  }
12228
12362
  }
12229
- if (!fs44.existsSync(".claude") && !fs44.existsSync(".cursor")) {
12363
+ if (!fs45.existsSync(".claude") && !fs45.existsSync(".cursor")) {
12230
12364
  console.log(chalk23.yellow("No .claude/ or .cursor/ directory found."));
12231
12365
  console.log(chalk23.dim(` Run \`${resolveCaliber()} init\` first, or create the directory manually.`));
12232
12366
  return;
@@ -12284,8 +12418,8 @@ async function learnStatusCommand() {
12284
12418
  if (lastError) {
12285
12419
  console.log(`Last error: ${chalk23.red(lastError.error)}`);
12286
12420
  console.log(chalk23.dim(` at ${lastError.timestamp}`));
12287
- const logPath = path36.join(getLearningDir(), LEARNING_FINALIZE_LOG);
12288
- if (fs44.existsSync(logPath)) {
12421
+ const logPath = path37.join(getLearningDir(), LEARNING_FINALIZE_LOG);
12422
+ if (fs45.existsSync(logPath)) {
12289
12423
  console.log(chalk23.dim(` Full log: ${logPath}`));
12290
12424
  }
12291
12425
  }
@@ -12365,11 +12499,11 @@ async function learnDeleteCommand(indexStr) {
12365
12499
  }
12366
12500
  const item = items[targetIdx];
12367
12501
  const filePath = item.source === "personal" ? PERSONAL_LEARNINGS_FILE : "CALIBER_LEARNINGS.md";
12368
- if (!fs44.existsSync(filePath)) {
12502
+ if (!fs45.existsSync(filePath)) {
12369
12503
  console.log(chalk23.red("Learnings file not found."));
12370
12504
  return;
12371
12505
  }
12372
- const content = fs44.readFileSync(filePath, "utf-8");
12506
+ const content = fs45.readFileSync(filePath, "utf-8");
12373
12507
  const lines = content.split("\n");
12374
12508
  const bulletsOfSource = items.filter((i) => i.source === item.source);
12375
12509
  const posInFile = bulletsOfSource.indexOf(item);
@@ -12390,9 +12524,9 @@ async function learnDeleteCommand(indexStr) {
12390
12524
  }
12391
12525
  const bulletToRemove = lines[lineToRemove];
12392
12526
  const newLines = lines.filter((_, i) => i !== lineToRemove);
12393
- fs44.writeFileSync(filePath, newLines.join("\n"));
12527
+ fs45.writeFileSync(filePath, newLines.join("\n"));
12394
12528
  if (item.source === "personal") {
12395
- fs44.chmodSync(filePath, 384);
12529
+ fs45.chmodSync(filePath, 384);
12396
12530
  }
12397
12531
  const roiStats = readROIStats();
12398
12532
  const cleanText = bulletToRemove.replace(/^- /, "").replace(/^\*\*\[[^\]]+\]\*\*\s*/, "").trim();
@@ -12565,8 +12699,8 @@ async function insightsCommand(options) {
12565
12699
  }
12566
12700
 
12567
12701
  // src/commands/sources.ts
12568
- import fs45 from "fs";
12569
- import path37 from "path";
12702
+ import fs46 from "fs";
12703
+ import path38 from "path";
12570
12704
  import chalk25 from "chalk";
12571
12705
  init_resolve_caliber();
12572
12706
  async function sourcesListCommand() {
@@ -12583,9 +12717,9 @@ async function sourcesListCommand() {
12583
12717
  if (configSources.length > 0) {
12584
12718
  for (const source of configSources) {
12585
12719
  const sourcePath = source.path || source.url || "";
12586
- const exists = source.path ? fs45.existsSync(path37.resolve(dir, source.path)) : false;
12720
+ const exists = source.path ? fs46.existsSync(path38.resolve(dir, source.path)) : false;
12587
12721
  const status = exists ? chalk25.green("reachable") : chalk25.red("not found");
12588
- const hasSummary = source.path && fs45.existsSync(path37.join(path37.resolve(dir, source.path), ".caliber", "summary.json"));
12722
+ const hasSummary = source.path && fs46.existsSync(path38.join(path38.resolve(dir, source.path), ".caliber", "summary.json"));
12589
12723
  console.log(` ${chalk25.bold(source.role || source.type)} ${chalk25.dim(sourcePath)}`);
12590
12724
  console.log(` Type: ${source.type} Status: ${status}${hasSummary ? " " + chalk25.cyan("has summary.json") : ""}`);
12591
12725
  if (source.description) console.log(` ${chalk25.dim(source.description)}`);
@@ -12595,7 +12729,7 @@ async function sourcesListCommand() {
12595
12729
  if (workspaces.length > 0) {
12596
12730
  console.log(chalk25.dim(" Auto-detected workspaces:"));
12597
12731
  for (const ws of workspaces) {
12598
- const exists = fs45.existsSync(path37.resolve(dir, ws));
12732
+ const exists = fs46.existsSync(path38.resolve(dir, ws));
12599
12733
  console.log(` ${exists ? chalk25.green("\u25CF") : chalk25.red("\u25CF")} ${ws}`);
12600
12734
  }
12601
12735
  console.log("");
@@ -12603,8 +12737,8 @@ async function sourcesListCommand() {
12603
12737
  }
12604
12738
  async function sourcesAddCommand(sourcePath) {
12605
12739
  const dir = process.cwd();
12606
- const absPath = path37.resolve(dir, sourcePath);
12607
- if (!fs45.existsSync(absPath)) {
12740
+ const absPath = path38.resolve(dir, sourcePath);
12741
+ if (!fs46.existsSync(absPath)) {
12608
12742
  console.log(chalk25.red(`
12609
12743
  Path not found: ${sourcePath}
12610
12744
  `));
@@ -12619,7 +12753,7 @@ async function sourcesAddCommand(sourcePath) {
12619
12753
  }
12620
12754
  const existing = loadSourcesConfig(dir);
12621
12755
  const alreadyConfigured = existing.some(
12622
- (s) => s.path && path37.resolve(dir, s.path) === absPath
12756
+ (s) => s.path && path38.resolve(dir, s.path) === absPath
12623
12757
  );
12624
12758
  if (alreadyConfigured) {
12625
12759
  console.log(chalk25.yellow(`
@@ -12667,8 +12801,8 @@ async function sourcesRemoveCommand(name) {
12667
12801
  }
12668
12802
 
12669
12803
  // src/commands/publish.ts
12670
- import fs46 from "fs";
12671
- import path38 from "path";
12804
+ import fs47 from "fs";
12805
+ import path39 from "path";
12672
12806
  import chalk26 from "chalk";
12673
12807
  import ora7 from "ora";
12674
12808
  init_config();
@@ -12683,10 +12817,10 @@ async function publishCommand() {
12683
12817
  const spinner = ora7("Generating project summary...").start();
12684
12818
  try {
12685
12819
  const fingerprint = await collectFingerprint(dir);
12686
- const claudeMd = readFileOrNull(path38.join(dir, "CLAUDE.md"));
12820
+ const claudeMd = readFileOrNull(path39.join(dir, "CLAUDE.md"));
12687
12821
  const topLevelDirs = fingerprint.fileTree.filter((f) => f.endsWith("/") && !f.includes("/")).map((f) => f.replace(/\/$/, ""));
12688
12822
  const summary = {
12689
- name: fingerprint.packageName || path38.basename(dir),
12823
+ name: fingerprint.packageName || path39.basename(dir),
12690
12824
  version: "1.0.0",
12691
12825
  description: fingerprint.description || "",
12692
12826
  languages: fingerprint.languages,
@@ -12698,7 +12832,7 @@ async function publishCommand() {
12698
12832
  summary.conventions = claudeMd.slice(0, 2e3);
12699
12833
  }
12700
12834
  try {
12701
- const pkgContent = readFileOrNull(path38.join(dir, "package.json"));
12835
+ const pkgContent = readFileOrNull(path39.join(dir, "package.json"));
12702
12836
  if (pkgContent) {
12703
12837
  const pkg3 = JSON.parse(pkgContent);
12704
12838
  if (pkg3.scripts) {
@@ -12711,14 +12845,14 @@ async function publishCommand() {
12711
12845
  }
12712
12846
  } catch {
12713
12847
  }
12714
- const outputDir = path38.join(dir, ".caliber");
12715
- if (!fs46.existsSync(outputDir)) {
12716
- fs46.mkdirSync(outputDir, { recursive: true });
12848
+ const outputDir = path39.join(dir, ".caliber");
12849
+ if (!fs47.existsSync(outputDir)) {
12850
+ fs47.mkdirSync(outputDir, { recursive: true });
12717
12851
  }
12718
- const outputPath = path38.join(outputDir, "summary.json");
12719
- fs46.writeFileSync(outputPath, JSON.stringify(summary, null, 2) + "\n", "utf-8");
12852
+ const outputPath = path39.join(outputDir, "summary.json");
12853
+ fs47.writeFileSync(outputPath, JSON.stringify(summary, null, 2) + "\n", "utf-8");
12720
12854
  spinner.succeed("Project summary published");
12721
- console.log(` ${chalk26.green("\u2713")} ${path38.relative(dir, outputPath)}`);
12855
+ console.log(` ${chalk26.green("\u2713")} ${path39.relative(dir, outputPath)}`);
12722
12856
  console.log(chalk26.dim("\n Other projects can now reference this repo as a source."));
12723
12857
  console.log(chalk26.dim(" When they run `caliber init`, they'll read this summary automatically.\n"));
12724
12858
  } catch (err) {
@@ -12731,7 +12865,7 @@ async function publishCommand() {
12731
12865
 
12732
12866
  // src/commands/bootstrap.ts
12733
12867
  init_builtin_skills();
12734
- import fs47 from "fs";
12868
+ import fs48 from "fs";
12735
12869
  import chalk27 from "chalk";
12736
12870
  var PLATFORM_SKILL_DIRS = {
12737
12871
  claude: ".claude/skills",
@@ -12752,8 +12886,8 @@ async function bootstrapCommand() {
12752
12886
  for (const skill of BUILTIN_SKILLS) {
12753
12887
  const skillDir = `${skillsDir}/${skill.name}`;
12754
12888
  const skillPath = `${skillDir}/SKILL.md`;
12755
- fs47.mkdirSync(skillDir, { recursive: true });
12756
- fs47.writeFileSync(skillPath, buildSkillContent(skill));
12889
+ fs48.mkdirSync(skillDir, { recursive: true });
12890
+ fs48.writeFileSync(skillPath, buildSkillContent(skill));
12757
12891
  written.push(skillPath);
12758
12892
  }
12759
12893
  }
@@ -12770,8 +12904,8 @@ async function bootstrapCommand() {
12770
12904
  }
12771
12905
 
12772
12906
  // src/commands/uninstall.ts
12773
- import fs48 from "fs";
12774
- import path39 from "path";
12907
+ import fs49 from "fs";
12908
+ import path40 from "path";
12775
12909
  import chalk28 from "chalk";
12776
12910
  import confirm3 from "@inquirer/confirm";
12777
12911
  init_pre_commit_block();
@@ -12779,18 +12913,18 @@ init_builtin_skills();
12779
12913
  init_config();
12780
12914
  var MANAGED_DOC_FILES = [
12781
12915
  "CLAUDE.md",
12782
- path39.join(".github", "copilot-instructions.md"),
12916
+ path40.join(".github", "copilot-instructions.md"),
12783
12917
  "AGENTS.md"
12784
12918
  ];
12785
12919
  var SKILL_DIRS = PLATFORM_CONFIGS.map((c) => c.skillsDir);
12786
- var CURSOR_RULES_DIR = path39.join(".cursor", "rules");
12920
+ var CURSOR_RULES_DIR = path40.join(".cursor", "rules");
12787
12921
  function removeCaliberCursorRules() {
12788
12922
  const removed = [];
12789
- if (!fs48.existsSync(CURSOR_RULES_DIR)) return removed;
12790
- for (const file of fs48.readdirSync(CURSOR_RULES_DIR)) {
12923
+ if (!fs49.existsSync(CURSOR_RULES_DIR)) return removed;
12924
+ for (const file of fs49.readdirSync(CURSOR_RULES_DIR)) {
12791
12925
  if (file.startsWith("caliber-") && file.endsWith(".mdc")) {
12792
- const fullPath = path39.join(CURSOR_RULES_DIR, file);
12793
- fs48.unlinkSync(fullPath);
12926
+ const fullPath = path40.join(CURSOR_RULES_DIR, file);
12927
+ fs49.unlinkSync(fullPath);
12794
12928
  removed.push(fullPath);
12795
12929
  }
12796
12930
  }
@@ -12799,11 +12933,11 @@ function removeCaliberCursorRules() {
12799
12933
  function removeBuiltinSkills() {
12800
12934
  const removed = [];
12801
12935
  for (const skillsDir of SKILL_DIRS) {
12802
- if (!fs48.existsSync(skillsDir)) continue;
12936
+ if (!fs49.existsSync(skillsDir)) continue;
12803
12937
  for (const name of BUILTIN_SKILL_NAMES) {
12804
- const skillDir = path39.join(skillsDir, name);
12805
- if (fs48.existsSync(skillDir)) {
12806
- fs48.rmSync(skillDir, { recursive: true });
12938
+ const skillDir = path40.join(skillsDir, name);
12939
+ if (fs49.existsSync(skillDir)) {
12940
+ fs49.rmSync(skillDir, { recursive: true });
12807
12941
  removed.push(skillDir);
12808
12942
  }
12809
12943
  }
@@ -12813,15 +12947,15 @@ function removeBuiltinSkills() {
12813
12947
  function stripManagedBlocksFromFiles() {
12814
12948
  const modified = [];
12815
12949
  for (const filePath of MANAGED_DOC_FILES) {
12816
- if (!fs48.existsSync(filePath)) continue;
12817
- const original = fs48.readFileSync(filePath, "utf-8");
12950
+ if (!fs49.existsSync(filePath)) continue;
12951
+ const original = fs49.readFileSync(filePath, "utf-8");
12818
12952
  const stripped = stripManagedBlocks(original);
12819
12953
  if (stripped !== original) {
12820
12954
  const trimmed = stripped.trim();
12821
12955
  if (!trimmed || /^#\s*\S*$/.test(trimmed)) {
12822
- fs48.unlinkSync(filePath);
12956
+ fs49.unlinkSync(filePath);
12823
12957
  } else {
12824
- fs48.writeFileSync(filePath, stripped);
12958
+ fs49.writeFileSync(filePath, stripped);
12825
12959
  }
12826
12960
  modified.push(filePath);
12827
12961
  }
@@ -12829,8 +12963,8 @@ function stripManagedBlocksFromFiles() {
12829
12963
  return modified;
12830
12964
  }
12831
12965
  function removeDirectory(dir) {
12832
- if (!fs48.existsSync(dir)) return false;
12833
- fs48.rmSync(dir, { recursive: true });
12966
+ if (!fs49.existsSync(dir)) return false;
12967
+ fs49.rmSync(dir, { recursive: true });
12834
12968
  return true;
12835
12969
  }
12836
12970
  async function uninstallCommand(options) {
@@ -12887,8 +13021,8 @@ async function uninstallCommand(options) {
12887
13021
  console.log(` ${chalk28.red("\u2717")} ${skill}/`);
12888
13022
  }
12889
13023
  if (removedSkills.length > 0) actions.push("builtin skills");
12890
- if (fs48.existsSync("CALIBER_LEARNINGS.md")) {
12891
- fs48.unlinkSync("CALIBER_LEARNINGS.md");
13024
+ if (fs49.existsSync("CALIBER_LEARNINGS.md")) {
13025
+ fs49.unlinkSync("CALIBER_LEARNINGS.md");
12892
13026
  console.log(` ${chalk28.red("\u2717")} CALIBER_LEARNINGS.md`);
12893
13027
  actions.push("learnings file");
12894
13028
  }
@@ -12902,18 +13036,18 @@ async function uninstallCommand(options) {
12902
13036
  }
12903
13037
  trackUninstallExecuted();
12904
13038
  const configPath = getConfigFilePath();
12905
- if (fs48.existsSync(configPath)) {
13039
+ if (fs49.existsSync(configPath)) {
12906
13040
  console.log("");
12907
13041
  const removeConfig = options.force || await confirm3({
12908
13042
  message: `Remove global config (~/.caliber/config.json)? This affects all projects.`
12909
13043
  });
12910
13044
  if (removeConfig) {
12911
- fs48.unlinkSync(configPath);
13045
+ fs49.unlinkSync(configPath);
12912
13046
  console.log(` ${chalk28.red("\u2717")} ${configPath}`);
12913
- const configDir = path39.dirname(configPath);
13047
+ const configDir = path40.dirname(configPath);
12914
13048
  try {
12915
- const remaining = fs48.readdirSync(configDir);
12916
- if (remaining.length === 0) fs48.rmdirSync(configDir);
13049
+ const remaining = fs49.readdirSync(configDir);
13050
+ if (remaining.length === 0) fs49.rmdirSync(configDir);
12917
13051
  } catch {
12918
13052
  }
12919
13053
  }
@@ -12924,9 +13058,9 @@ async function uninstallCommand(options) {
12924
13058
  }
12925
13059
 
12926
13060
  // src/cli.ts
12927
- var __dirname = path40.dirname(fileURLToPath(import.meta.url));
13061
+ var __dirname = path41.dirname(fileURLToPath(import.meta.url));
12928
13062
  var pkg = JSON.parse(
12929
- fs49.readFileSync(path40.resolve(__dirname, "..", "package.json"), "utf-8")
13063
+ fs50.readFileSync(path41.resolve(__dirname, "..", "package.json"), "utf-8")
12930
13064
  );
12931
13065
  var program = new Command();
12932
13066
  var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
@@ -12980,16 +13114,16 @@ program.hook("preAction", (thisCommand) => {
12980
13114
  });
12981
13115
  function parseAgentOption(value) {
12982
13116
  if (value === "both") return ["claude", "cursor"];
12983
- if (value === "all") return ["claude", "cursor", "codex", "github-copilot"];
12984
- const valid = ["claude", "cursor", "codex", "github-copilot"];
13117
+ if (value === "all") return ["claude", "cursor", "codex", "opencode", "github-copilot"];
13118
+ const valid = ["claude", "cursor", "codex", "opencode", "github-copilot"];
12985
13119
  const agents = [...new Set(value.split(",").map((s) => s.trim().toLowerCase()).filter((a) => valid.includes(a)))];
12986
13120
  if (agents.length === 0) {
12987
- console.error(`Invalid agent "${value}". Choose from: claude, cursor, codex, github-copilot (comma-separated for multiple)`);
13121
+ console.error(`Invalid agent "${value}". Choose from: claude, cursor, codex, opencode, github-copilot (comma-separated for multiple)`);
12988
13122
  process.exit(1);
12989
13123
  }
12990
13124
  return agents;
12991
13125
  }
12992
- 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));
13126
+ program.command("init").description("Initialize your project for AI-assisted development").option("--agent <type>", "Target agents (comma-separated): claude, cursor, codex, opencode, 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));
12993
13127
  program.command("bootstrap").description("Install agent skills (/setup-caliber, /find-skills, /save-learning) without running init").action(tracked("bootstrap", bootstrapCommand));
12994
13128
  program.command("undo").description("Revert all config changes made by Caliber").action(tracked("undo", undoCommand));
12995
13129
  program.command("uninstall").description("Remove all Caliber resources from this project").option("--force", "Skip confirmation prompt").action(tracked("uninstall", (options) => uninstallCommand(options)));
@@ -12997,7 +13131,7 @@ program.command("status").description("Show current Caliber config status").opti
12997
13131
  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));
12998
13132
  program.command("config").description("Configure LLM provider, API key, and model").action(tracked("config", configCommand));
12999
13133
  program.command("skills").description("Discover and install community skills for your project").option("--query <terms>", 'Search for skills by topic (e.g. "react frontend")').option("--install <slugs>", "Install specific skills by slug (comma-separated)").action(tracked("skills", recommendCommand));
13000
- program.command("score").description("Score your AI context configuration (deterministic, no network)").option("--json", "Output as JSON").option("--quiet", "One-line output for scripts/hooks").option("--agent <type>", "Target agents (comma-separated): claude, cursor, codex, github-copilot", parseAgentOption).option("--compare <ref>", "Compare score against a git ref (branch, tag, or SHA)").action(tracked("score", scoreCommand));
13134
+ program.command("score").description("Score your AI context configuration (deterministic, no network)").option("--json", "Output as JSON").option("--quiet", "One-line output for scripts/hooks").option("--agent <type>", "Target agents (comma-separated): claude, cursor, codex, opencode, github-copilot", parseAgentOption).option("--compare <ref>", "Compare score against a git ref (branch, tag, or SHA)").action(tracked("score", scoreCommand));
13001
13135
  program.command("refresh").description("Update docs based on recent code changes").option("--quiet", "Suppress output (for use in hooks)").option("--dry-run", "Preview changes without writing files").action(tracked("refresh", refreshCommand));
13002
13136
  program.command("hooks").description("Manage auto-refresh hooks (toggle interactively)").option("--install", "Enable all hooks non-interactively").option("--remove", "Disable all hooks non-interactively").action(tracked("hooks", hooksCommand));
13003
13137
  program.command("insights").description("Show agent performance insights and learning impact").option("--json", "Output as JSON").action(tracked("insights", insightsCommand));
@@ -13017,15 +13151,15 @@ learn.command("delete <index>").description("Delete a learning by its index numb
13017
13151
  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));
13018
13152
 
13019
13153
  // src/utils/version-check.ts
13020
- import fs50 from "fs";
13021
- import path41 from "path";
13154
+ import fs51 from "fs";
13155
+ import path42 from "path";
13022
13156
  import { fileURLToPath as fileURLToPath2 } from "url";
13023
13157
  import { execSync as execSync16, execFileSync as execFileSync3 } from "child_process";
13024
13158
  import chalk29 from "chalk";
13025
13159
  import ora8 from "ora";
13026
13160
  import confirm4 from "@inquirer/confirm";
13027
- var __dirname_vc = path41.dirname(fileURLToPath2(import.meta.url));
13028
- var pkg2 = JSON.parse(fs50.readFileSync(path41.resolve(__dirname_vc, "..", "package.json"), "utf-8"));
13161
+ var __dirname_vc = path42.dirname(fileURLToPath2(import.meta.url));
13162
+ var pkg2 = JSON.parse(fs51.readFileSync(path42.resolve(__dirname_vc, "..", "package.json"), "utf-8"));
13029
13163
  function getChannel(version) {
13030
13164
  const match = version.match(/-(dev|next)\./);
13031
13165
  return match ? match[1] : "latest";
@@ -13052,8 +13186,8 @@ function getInstalledVersion() {
13052
13186
  encoding: "utf-8",
13053
13187
  stdio: ["pipe", "pipe", "pipe"]
13054
13188
  }).trim();
13055
- const pkgPath = path41.join(globalRoot, "@rely-ai", "caliber", "package.json");
13056
- return JSON.parse(fs50.readFileSync(pkgPath, "utf-8")).version;
13189
+ const pkgPath = path42.join(globalRoot, "@rely-ai", "caliber", "package.json");
13190
+ return JSON.parse(fs51.readFileSync(pkgPath, "utf-8")).version;
13057
13191
  } catch {
13058
13192
  return null;
13059
13193
  }