@christang/keel 5.77.0 → 5.79.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.
@@ -1,4 +1,4 @@
1
- <!-- keel:start version=5.77.0 -->
1
+ <!-- keel:start version=5.79.0 -->
2
2
  ## Keel Bootstrap
3
3
 
4
4
  - Start every session with `keel context`; OpenSpec artifacts and Git are the only durable authority — never native memory, goals, or transcripts.
package/bin/keel.js CHANGED
@@ -1277,9 +1277,34 @@ function skillRootForTarget(target) {
1277
1277
  return path.join(".opencode", "skills");
1278
1278
  }
1279
1279
 
1280
- function openspecSkillRootForTarget(target) {
1280
+ // Which OpenSpec layout a Codex repository carries (#169). OpenSpec 1.6 wrote
1281
+ // Codex's skills under `.codex/skills` and its commands as prompts in
1282
+ // CODEX_HOME; 1.13, which Keel pins, writes skills under `.agents/skills` and
1283
+ // no command files. A repository set up under 1.6 keeps its layout, and every
1284
+ // other one — a fresh repository included — gets the one the pin writes.
1285
+ function codexOpenSpecLayout(repo) {
1286
+ const holdsOpenSpec = (root) => {
1287
+ try {
1288
+ return fs
1289
+ .readdirSync(path.join(repo, root))
1290
+ .some((entry) => entry.startsWith("openspec-"));
1291
+ } catch {
1292
+ return false;
1293
+ }
1294
+ };
1295
+ return holdsOpenSpec(path.join(".codex", "skills"))
1296
+ && !holdsOpenSpec(path.join(".agents", "skills"))
1297
+ ? "legacy"
1298
+ : "agents";
1299
+ }
1300
+
1301
+ function openspecSkillRootForTarget(target, repo) {
1281
1302
  if (target === "claude") return path.join(".claude", "skills");
1282
- if (target === "codex") return path.join(".codex", "skills");
1303
+ if (target === "codex") {
1304
+ return codexOpenSpecLayout(repo) === "legacy"
1305
+ ? path.join(".codex", "skills")
1306
+ : path.join(".agents", "skills");
1307
+ }
1283
1308
  return path.join(".opencode", "skills");
1284
1309
  }
1285
1310
 
@@ -1294,6 +1319,16 @@ function commandSurfaceForTarget(target, repo) {
1294
1319
  remediation: "run keel --init --target claude or openspec update --force",
1295
1320
  };
1296
1321
  }
1322
+ if (target === "codex" && codexOpenSpecLayout(repo) === "agents") {
1323
+ // OpenSpec 1.13 surfaces Codex's workflows as the skills counted on the
1324
+ // line above and writes no command file, so there is nothing to count here.
1325
+ return {
1326
+ location: path.join(".agents", "skills"),
1327
+ paths: [],
1328
+ remediation: "run keel --init --target codex or openspec update --force",
1329
+ skillsOnly: true,
1330
+ };
1331
+ }
1297
1332
  if (target === "codex") {
1298
1333
  const home = codexHome();
1299
1334
  const location = path.join(home, "prompts");
@@ -1320,13 +1355,15 @@ function commandPathForAction(target, repo, action) {
1320
1355
  return path.join(repo, ".claude", "commands", "opsx", `${action}.md`);
1321
1356
  }
1322
1357
  if (target === "codex") {
1323
- return path.join(codexHome(), "prompts", `opsx-${action}.md`);
1358
+ return codexOpenSpecLayout(repo) === "legacy"
1359
+ ? path.join(codexHome(), "prompts", `opsx-${action}.md`)
1360
+ : null;
1324
1361
  }
1325
1362
  return path.join(repo, ".opencode", "commands", `opsx-${action}.md`);
1326
1363
  }
1327
1364
 
1328
1365
  function openspecOverlaySurfacesForTarget(target, repo) {
1329
- const skillRoot = openspecSkillRootForTarget(target);
1366
+ const skillRoot = openspecSkillRootForTarget(target, repo);
1330
1367
  return OPENSPEC_OVERLAY_ACTIONS.flatMap((action) => {
1331
1368
  if (action === "propose" && target === "opencode") {
1332
1369
  return [];
@@ -1346,7 +1383,7 @@ function openspecOverlaySurfacesForTarget(target, repo) {
1346
1383
  action,
1347
1384
  path: commandPathForAction(target, repo, action),
1348
1385
  },
1349
- ];
1386
+ ].filter((surface) => surface.path !== null);
1350
1387
  });
1351
1388
  }
1352
1389
 
@@ -1699,7 +1736,7 @@ function printTargetSurface(repo, target) {
1699
1736
  }
1700
1737
  }
1701
1738
 
1702
- const openspecSkillRoot = openspecSkillRootForTarget(target);
1739
+ const openspecSkillRoot = openspecSkillRootForTarget(target, repo);
1703
1740
  const openspecSkillPaths = OPENSPEC_SKILLS.map((skill) =>
1704
1741
  path.join(repo, openspecSkillRoot, skill, "SKILL.md")
1705
1742
  );
@@ -1722,16 +1759,25 @@ function printTargetSurface(repo, target) {
1722
1759
  );
1723
1760
 
1724
1761
  const commands = commandSurfaceForTarget(target, repo);
1725
- const commandCounts = countExisting(commands.paths);
1726
- const commandDetail =
1727
- surfaceStatus(commandCounts) === "ok"
1728
- ? formatCount(commandCounts, commands.location)
1729
- : `${formatCount(commandCounts, commands.location)}; ${commands.remediation}`;
1730
- printDoctorLine(
1731
- "OpenSpec commands",
1732
- surfaceStatus(commandCounts),
1733
- commandDetail
1734
- );
1762
+ if (commands.skillsOnly) {
1763
+ printDoctorLine(
1764
+ "OpenSpec commands",
1765
+ "ok",
1766
+ `none; OpenSpec 1.13 surfaces Codex's workflows as the skills under `
1767
+ + `${commands.location}`
1768
+ );
1769
+ } else {
1770
+ const commandCounts = countExisting(commands.paths);
1771
+ const commandDetail =
1772
+ surfaceStatus(commandCounts) === "ok"
1773
+ ? formatCount(commandCounts, commands.location)
1774
+ : `${formatCount(commandCounts, commands.location)}; ${commands.remediation}`;
1775
+ printDoctorLine(
1776
+ "OpenSpec commands",
1777
+ surfaceStatus(commandCounts),
1778
+ commandDetail
1779
+ );
1780
+ }
1735
1781
 
1736
1782
  const overlayPaths = openspecOverlaySurfacesForTarget(target, repo).map(
1737
1783
  (surface) => surface.path