syntaur 0.7.0 → 0.8.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 (105) hide show
  1. package/.claude-plugin/plugin.json +23 -0
  2. package/README.md +64 -24
  3. package/dashboard/dist/assets/{_basePickBy-DTYUlCEg.js → _basePickBy-CIZortGD.js} +1 -1
  4. package/dashboard/dist/assets/{_baseUniq-C0Y4HRd5.js → _baseUniq-BHP5NBQ7.js} +1 -1
  5. package/dashboard/dist/assets/{arc-BFx2eqN9.js → arc-BLtQy7Cl.js} +1 -1
  6. package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-Erol1JD6.js → architectureDiagram-2XIMDMQ5-0mhIBjWy.js} +1 -1
  7. package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-kSkh6VkS.js → blockDiagram-WCTKOSBZ-CM7K5Tf5.js} +1 -1
  8. package/dashboard/dist/assets/{c4Diagram-IC4MRINW-C04oKzvX.js → c4Diagram-IC4MRINW-gsbeygvS.js} +1 -1
  9. package/dashboard/dist/assets/channel-FWLgstBg.js +1 -0
  10. package/dashboard/dist/assets/{chunk-4BX2VUAB-C3t0tXt-.js → chunk-4BX2VUAB-CneRz9Nd.js} +1 -1
  11. package/dashboard/dist/assets/{chunk-55IACEB6-2cnyEL0b.js → chunk-55IACEB6-uetANWHd.js} +1 -1
  12. package/dashboard/dist/assets/{chunk-FMBD7UC4-DIY9MTNi.js → chunk-FMBD7UC4-3-tNszoF.js} +1 -1
  13. package/dashboard/dist/assets/{chunk-JSJVCQXG-Cw8fpqpE.js → chunk-JSJVCQXG-DOO88rZD.js} +1 -1
  14. package/dashboard/dist/assets/{chunk-KX2RTZJC-BAhd66XV.js → chunk-KX2RTZJC-Bnc1CFy2.js} +1 -1
  15. package/dashboard/dist/assets/{chunk-NQ4KR5QH-RzXwoxk3.js → chunk-NQ4KR5QH-BfViC1Ms.js} +1 -1
  16. package/dashboard/dist/assets/{chunk-QZHKN3VN-Dgri4sGz.js → chunk-QZHKN3VN-CgjyAt2l.js} +1 -1
  17. package/dashboard/dist/assets/{chunk-WL4C6EOR-DYLj9JRa.js → chunk-WL4C6EOR-BYFermSR.js} +1 -1
  18. package/dashboard/dist/assets/classDiagram-VBA2DB6C-BAvQTSn_.js +1 -0
  19. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-BAvQTSn_.js +1 -0
  20. package/dashboard/dist/assets/clone-rSEllQ1P.js +1 -0
  21. package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-DfY_Fnfu.js → cose-bilkent-S5V4N54A-CE-k5SLv.js} +1 -1
  22. package/dashboard/dist/assets/{dagre-KLK3FWXG-CyTKIVSK.js → dagre-KLK3FWXG-CX7Br8LQ.js} +1 -1
  23. package/dashboard/dist/assets/{diagram-E7M64L7V-Krub7Xxo.js → diagram-E7M64L7V-t-xhWjCU.js} +1 -1
  24. package/dashboard/dist/assets/{diagram-IFDJBPK2-giUl9uHz.js → diagram-IFDJBPK2-BVhcTXsJ.js} +1 -1
  25. package/dashboard/dist/assets/{diagram-P4PSJMXO-oAtnO3C9.js → diagram-P4PSJMXO-fxt8ZSca.js} +1 -1
  26. package/dashboard/dist/assets/{erDiagram-INFDFZHY-eYaVjXqo.js → erDiagram-INFDFZHY-C20k2bxX.js} +1 -1
  27. package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-or5S0_Sb.js → flowDiagram-PKNHOUZH-CdeygexM.js} +1 -1
  28. package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-C9R1lsme.js → ganttDiagram-A5KZAMGK-CPsoOAV-.js} +1 -1
  29. package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-BQwsDzvp.js → gitGraphDiagram-K3NZZRJ6-t0tCTIZy.js} +1 -1
  30. package/dashboard/dist/assets/{graph-EQOX1wg8.js → graph-Bcqd31wL.js} +1 -1
  31. package/dashboard/dist/assets/{index-u80fISp0.css → index-bLWoSCLL.css} +1 -1
  32. package/dashboard/dist/assets/{index-Cy7yjuqO.js → index-eS0wtDO0.js} +87 -87
  33. package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-BjLlQWxk.js → infoDiagram-LFFYTUFH-DgoKvH3N.js} +1 -1
  34. package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-BNjydh4j.js → ishikawaDiagram-PHBUUO56-CGr4P8do.js} +1 -1
  35. package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-DNzE7TgQ.js → journeyDiagram-4ABVD52K-CHYRi6wF.js} +1 -1
  36. package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-FbNvGx6i.js → kanban-definition-K7BYSVSG-kwzqrFcW.js} +1 -1
  37. package/dashboard/dist/assets/{layout-B2yZvlWs.js → layout-wud47qGs.js} +1 -1
  38. package/dashboard/dist/assets/{linear-p68yY_14.js → linear-DG5js90X.js} +1 -1
  39. package/dashboard/dist/assets/{mermaid.core-D558akcW.js → mermaid.core-BDw69uC2.js} +4 -4
  40. package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-CZPgesSK.js → mindmap-definition-YRQLILUH-Dw_uyg1-.js} +1 -1
  41. package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-CdXMWspp.js → pieDiagram-SKSYHLDU-C7NBi6tK.js} +1 -1
  42. package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-D7tq22ZY.js → quadrantDiagram-337W2JSQ-_N_BSBd4.js} +1 -1
  43. package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-ByZxUSmd.js → requirementDiagram-Z7DCOOCP-BqUiqfP1.js} +1 -1
  44. package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-CZon9rRY.js → sankeyDiagram-WA2Y5GQK-C2Qq5ULW.js} +1 -1
  45. package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-nbELB6rb.js → sequenceDiagram-2WXFIKYE-BOCF2z8f.js} +1 -1
  46. package/dashboard/dist/assets/{stateDiagram-RAJIS63D-D_OPKr5B.js → stateDiagram-RAJIS63D-BvDyTxC5.js} +1 -1
  47. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-TaLLhLWD.js +1 -0
  48. package/dashboard/dist/assets/{timeline-definition-YZTLITO2-Cxuvk1D2.js → timeline-definition-YZTLITO2-DNNZxSrG.js} +1 -1
  49. package/dashboard/dist/assets/{treemap-KZPCXAKY-C0CRpL92.js → treemap-KZPCXAKY-C6hOdiGH.js} +1 -1
  50. package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-DijCj6M3.js → vennDiagram-LZ73GAT5-FmXl6puP.js} +1 -1
  51. package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-CdpE0oRi.js → xychartDiagram-JWTSCODW-Bjav12WG.js} +1 -1
  52. package/dashboard/dist/index.html +2 -2
  53. package/dist/dashboard/server.js +12 -14
  54. package/dist/dashboard/server.js.map +1 -1
  55. package/dist/index.js +644 -268
  56. package/dist/index.js.map +1 -1
  57. package/package.json +6 -8
  58. package/platforms/claude-code/.claude-plugin/plugin.json +15 -2
  59. package/{vendor/syntaur-skills → platforms/claude-code}/skills/complete-assignment/SKILL.md +0 -2
  60. package/{vendor/syntaur-skills → platforms/claude-code}/skills/grab-assignment/SKILL.md +2 -7
  61. package/{vendor/syntaur-skills → platforms/claude-code}/skills/plan-assignment/SKILL.md +1 -3
  62. package/{vendor/syntaur-skills → platforms/claude-code}/skills/syntaur-protocol/SKILL.md +4 -23
  63. package/{vendor/syntaur-skills → platforms/claude-code}/skills/syntaur-protocol/references/file-ownership.md +1 -2
  64. package/{vendor/syntaur-skills → platforms/claude-code}/skills/syntaur-protocol/references/protocol-summary.md +1 -6
  65. package/platforms/claude-code/skills/track-server/SKILL.md +49 -0
  66. package/platforms/codex/.codex-plugin/plugin.json +2 -2
  67. package/platforms/codex/skills/clear-assignment/SKILL.md +111 -0
  68. package/platforms/codex/skills/complete-assignment/SKILL.md +146 -0
  69. package/platforms/codex/skills/create-assignment/SKILL.md +73 -0
  70. package/platforms/codex/skills/create-project/SKILL.md +56 -0
  71. package/platforms/codex/skills/grab-assignment/SKILL.md +158 -0
  72. package/platforms/codex/skills/manage-statuses/SKILL.md +72 -0
  73. package/platforms/codex/skills/plan-assignment/SKILL.md +137 -0
  74. package/platforms/codex/skills/save-session-summary/SKILL.md +113 -0
  75. package/platforms/codex/skills/syntaur-protocol/SKILL.md +119 -0
  76. package/platforms/codex/skills/syntaur-protocol/references/file-ownership.md +67 -0
  77. package/platforms/codex/skills/syntaur-protocol/references/protocol-summary.md +82 -0
  78. package/platforms/codex/skills/track-server/SKILL.md +49 -0
  79. package/platforms/codex/skills/track-session/SKILL.md +63 -26
  80. package/skills/clear-assignment/SKILL.md +111 -0
  81. package/skills/complete-assignment/SKILL.md +146 -0
  82. package/skills/create-assignment/SKILL.md +73 -0
  83. package/skills/create-project/SKILL.md +56 -0
  84. package/skills/grab-assignment/SKILL.md +158 -0
  85. package/skills/manage-statuses/SKILL.md +72 -0
  86. package/skills/plan-assignment/SKILL.md +137 -0
  87. package/skills/save-session-summary/SKILL.md +113 -0
  88. package/skills/syntaur-protocol/SKILL.md +119 -0
  89. package/skills/syntaur-protocol/references/file-ownership.md +67 -0
  90. package/skills/syntaur-protocol/references/protocol-summary.md +82 -0
  91. package/skills/track-server/SKILL.md +49 -0
  92. package/skills/track-session/SKILL.md +86 -0
  93. package/dashboard/dist/assets/channel-C82tBKZ7.js +0 -1
  94. package/dashboard/dist/assets/classDiagram-VBA2DB6C-STOZ51tg.js +0 -1
  95. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-STOZ51tg.js +0 -1
  96. package/dashboard/dist/assets/clone-TzhWk-Bj.js +0 -1
  97. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-4pLM5B3m.js +0 -1
  98. package/scripts/postinstall-submodules.mjs +0 -40
  99. package/vendor/syntaur-skills/LICENSE +0 -21
  100. package/vendor/syntaur-skills/README.md +0 -57
  101. /package/{vendor/syntaur-skills → platforms/claude-code}/skills/clear-assignment/SKILL.md +0 -0
  102. /package/{vendor/syntaur-skills → platforms/claude-code}/skills/create-assignment/SKILL.md +0 -0
  103. /package/{vendor/syntaur-skills → platforms/claude-code}/skills/create-project/SKILL.md +0 -0
  104. /package/{vendor/syntaur-skills → platforms/claude-code}/skills/manage-statuses/SKILL.md +0 -0
  105. /package/{vendor/syntaur-skills → platforms/claude-code}/skills/save-session-summary/SKILL.md +0 -0
package/dist/index.js CHANGED
@@ -148,7 +148,7 @@ function extractFrontmatter(fileContent) {
148
148
  }
149
149
  function parseSimpleValue(raw) {
150
150
  const trimmed = raw.trim();
151
- if (trimmed === "null" || trimmed === "") return null;
151
+ if (trimmed === "null" || trimmed === "~" || trimmed === "") return null;
152
152
  if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
153
153
  return trimmed.slice(1, -1);
154
154
  }
@@ -199,6 +199,7 @@ function parseProject(fileContent) {
199
199
  updated: getField(fm, "updated") ?? "",
200
200
  tags: parseListField(fm, "tags"),
201
201
  workspace: getField(fm, "workspace"),
202
+ externalIds: parseExternalIds(fm),
202
203
  body
203
204
  };
204
205
  }
@@ -243,16 +244,14 @@ function parseExternalIds(frontmatter) {
243
244
  const colonIdx = line.indexOf(":");
244
245
  if (colonIdx < 0) continue;
245
246
  const key = line.slice(0, colonIdx).trim().replace(/^-\s+/, "");
246
- const value = line.slice(colonIdx + 1).trim();
247
- if (key && value) {
248
- entry[key] = value;
249
- }
247
+ if (!key) continue;
248
+ entry[key] = parseSimpleValue(line.slice(colonIdx + 1));
250
249
  }
251
- if (entry["system"] && entry["id"] && entry["url"]) {
250
+ if (entry["system"] && entry["id"]) {
252
251
  results.push({
253
252
  system: entry["system"],
254
253
  id: entry["id"],
255
- url: entry["url"]
254
+ url: entry["url"] || null
256
255
  });
257
256
  }
258
257
  }
@@ -3021,7 +3020,7 @@ function extractFrontmatter2(fileContent) {
3021
3020
  }
3022
3021
  function parseSimpleValue2(raw) {
3023
3022
  const trimmed = raw.trim();
3024
- if (trimmed === "null") return null;
3023
+ if (trimmed === "null" || trimmed === "~" || trimmed === "") return null;
3025
3024
  if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
3026
3025
  return trimmed.slice(1, -1);
3027
3026
  }
@@ -3069,16 +3068,14 @@ function parseExternalIds2(frontmatter) {
3069
3068
  const colonIdx = line.indexOf(":");
3070
3069
  if (colonIdx < 0) continue;
3071
3070
  const key = line.slice(0, colonIdx).trim().replace(/^-\s+/, "");
3072
- const value = line.slice(colonIdx + 1).trim();
3073
- if (key && value) {
3074
- entry[key] = value;
3075
- }
3071
+ if (!key) continue;
3072
+ entry[key] = parseSimpleValue2(line.slice(colonIdx + 1));
3076
3073
  }
3077
- if (entry["system"] && entry["id"] && entry["url"]) {
3074
+ if (entry["system"] && entry["id"]) {
3078
3075
  results.push({
3079
3076
  system: entry["system"],
3080
3077
  id: entry["id"],
3081
- url: entry["url"]
3078
+ url: entry["url"] || null
3082
3079
  });
3083
3080
  }
3084
3081
  }
@@ -4822,6 +4819,7 @@ async function getProjectDetail(projectsDir2, slug) {
4822
4819
  created: project.created,
4823
4820
  updated,
4824
4821
  tags: project.tags,
4822
+ externalIds: project.externalIds,
4825
4823
  body: project.body,
4826
4824
  progress: rollup.progress,
4827
4825
  needsAttention: rollup.needsAttention,
@@ -6708,7 +6706,7 @@ __export(launch_exports, {
6708
6706
  });
6709
6707
  import { spawn as spawn2 } from "child_process";
6710
6708
  import { mkdir as mkdir6, writeFile as writeFile9 } from "fs/promises";
6711
- import { isAbsolute as isAbsolute3, resolve as resolve32 } from "path";
6709
+ import { isAbsolute as isAbsolute3, resolve as resolve33 } from "path";
6712
6710
  function formatFallbackCwdWarning(opts) {
6713
6711
  const missing = [];
6714
6712
  if (!opts.worktreePath) missing.push("worktreePath");
@@ -6752,8 +6750,8 @@ async function launchAgent(options) {
6752
6750
  console.error(`Assignment not found: ${projectSlug}/${assignmentSlug}`);
6753
6751
  process.exit(1);
6754
6752
  }
6755
- const projectDir = resolve32(projectsDir2, projectSlug);
6756
- const assignmentDir = resolve32(projectDir, "assignments", assignmentSlug);
6753
+ const projectDir = resolve33(projectsDir2, projectSlug);
6754
+ const assignmentDir = resolve33(projectDir, "assignments", assignmentSlug);
6757
6755
  const resolvedFromWorkspace = cwdOverride ?? detail.workspace.worktreePath ?? (detail.workspace.repository?.startsWith("/") ? detail.workspace.repository : null);
6758
6756
  const workspaceDir = resolvedFromWorkspace ?? process.cwd();
6759
6757
  if (!cwdOverride) {
@@ -6765,7 +6763,7 @@ async function launchAgent(options) {
6765
6763
  });
6766
6764
  if (warning) console.warn(warning);
6767
6765
  }
6768
- const contextDir = resolve32(workspaceDir, ".syntaur");
6766
+ const contextDir = resolve33(workspaceDir, ".syntaur");
6769
6767
  await mkdir6(contextDir, { recursive: true });
6770
6768
  const context = {
6771
6769
  projectSlug,
@@ -6778,7 +6776,7 @@ async function launchAgent(options) {
6778
6776
  grabbedAt: (/* @__PURE__ */ new Date()).toISOString()
6779
6777
  };
6780
6778
  await writeFile9(
6781
- resolve32(contextDir, "context.json"),
6779
+ resolve33(contextDir, "context.json"),
6782
6780
  JSON.stringify(context, null, 2) + "\n"
6783
6781
  );
6784
6782
  const { argv, shellFallbackWarning } = buildAgentArgv(
@@ -6837,7 +6835,7 @@ __export(git_worktree_exports, {
6837
6835
  removeWorktree: () => removeWorktree
6838
6836
  });
6839
6837
  import { spawn as spawn3 } from "child_process";
6840
- import { readFile as readFile20 } from "fs/promises";
6838
+ import { readFile as readFile21 } from "fs/promises";
6841
6839
  function run(command, args, cwd) {
6842
6840
  return new Promise((resolvePromise) => {
6843
6841
  const child = spawn3(command, args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
@@ -6881,7 +6879,7 @@ async function createWorktreeAndRecord(opts) {
6881
6879
  const { assignmentPath, repository, branch, worktreePath, parentBranch } = opts;
6882
6880
  await createWorktree({ repository, branch, worktreePath, parentBranch });
6883
6881
  try {
6884
- const content = await readFile20(assignmentPath, "utf-8");
6882
+ const content = await readFile21(assignmentPath, "utf-8");
6885
6883
  const updated = updateAssignmentWorkspace(content, {
6886
6884
  repository,
6887
6885
  worktreePath,
@@ -7265,8 +7263,8 @@ async function migrateFromMarkdown(projectsDir2) {
7265
7263
  return allSessions.length;
7266
7264
  }
7267
7265
  async function parseMarkdownSessionsIndex(filePath, projectSlug) {
7268
- const { readFile: readFile32 } = await import("fs/promises");
7269
- const raw = await readFile32(filePath, "utf-8");
7266
+ const { readFile: readFile35 } = await import("fs/promises");
7267
+ const raw = await readFile35(filePath, "utf-8");
7270
7268
  const sessions = [];
7271
7269
  const lines = raw.split("\n");
7272
7270
  let inTable = false;
@@ -9738,8 +9736,8 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
9738
9736
  router.post("/:workspace/archive", async (req, res) => {
9739
9737
  try {
9740
9738
  const { archivePath: archivePath2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports));
9741
- const { resolve: resolve47 } = await import("path");
9742
- const { readFile: readFile32 } = await import("fs/promises");
9739
+ const { resolve: resolve50 } = await import("path");
9740
+ const { readFile: readFile35 } = await import("fs/promises");
9743
9741
  const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
9744
9742
  const workspace = getWorkspaceParam(req.params.workspace);
9745
9743
  const checklist = await readChecklist(todosDir2, workspace);
@@ -9755,10 +9753,10 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
9755
9753
  (e) => e.itemIds.every((id) => completedIds.has(id))
9756
9754
  );
9757
9755
  const archFile = archivePath2(todosDir2, workspace, checklist.archiveInterval);
9758
- await ensureDir(resolve47(todosDir2, "archive"));
9756
+ await ensureDir(resolve50(todosDir2, "archive"));
9759
9757
  let archContent = "";
9760
9758
  if (await fileExists(archFile)) {
9761
- archContent = await readFile32(archFile, "utf-8");
9759
+ archContent = await readFile35(archFile, "utf-8");
9762
9760
  archContent = archContent.trimEnd() + "\n\n";
9763
9761
  } else {
9764
9762
  archContent = `---
@@ -10017,7 +10015,7 @@ workspace: ${workspace}
10017
10015
  const { readConfig: readConfig3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
10018
10016
  const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
10019
10017
  const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
10020
- const { readFile: readFile32 } = await import("fs/promises");
10018
+ const { readFile: readFile35 } = await import("fs/promises");
10021
10019
  const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
10022
10020
  const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
10023
10021
  let assignmentRef;
@@ -10055,7 +10053,7 @@ workspace: ${workspace}
10055
10053
  if (!await fileExists2(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
10056
10054
  }
10057
10055
  const assignmentMdPath = resolvePath2(assignmentDir, "assignment.md");
10058
- let content = await readFile32(assignmentMdPath, "utf-8");
10056
+ let content = await readFile35(assignmentMdPath, "utf-8");
10059
10057
  content = appendTodosToAssignmentBody2(
10060
10058
  content,
10061
10059
  items.map((it) => ({
@@ -12449,6 +12447,7 @@ import {
12449
12447
  lstat,
12450
12448
  readFile as readFile16,
12451
12449
  readlink,
12450
+ rename as rename6,
12452
12451
  rm as rm3,
12453
12452
  unlink as unlink5,
12454
12453
  writeFile as writeFile6
@@ -12655,8 +12654,29 @@ async function readClaudeMarketplaceFile(manifestPath) {
12655
12654
  }
12656
12655
  async function writeClaudeMarketplaceFile(manifestPath, marketplace) {
12657
12656
  await ensureDir(dirname8(manifestPath));
12658
- await writeFile6(manifestPath, `${JSON.stringify(marketplace, null, 2)}
12657
+ if (Array.isArray(marketplace.plugins)) {
12658
+ marketplace.plugins = [...marketplace.plugins].sort((a, b) => {
12659
+ const an = a?.name ?? "";
12660
+ const bn = b?.name ?? "";
12661
+ return an.localeCompare(bn);
12662
+ });
12663
+ }
12664
+ if (await fileExists(manifestPath)) {
12665
+ try {
12666
+ const prev = await readFile16(manifestPath, "utf-8");
12667
+ JSON.parse(prev);
12668
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
12669
+ await writeFile6(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
12670
+ } catch {
12671
+ throw new Error(
12672
+ `Refusing to overwrite ${manifestPath}: existing file is not valid JSON. Inspect and remove or repair it manually before re-running.`
12673
+ );
12674
+ }
12675
+ }
12676
+ const tmpPath = `${manifestPath}.tmp`;
12677
+ await writeFile6(tmpPath, `${JSON.stringify(marketplace, null, 2)}
12659
12678
  `, "utf-8");
12679
+ await rename6(tmpPath, manifestPath);
12660
12680
  }
12661
12681
  function buildClaudeMarketplaceSourcePath(pluginTargetDir, marketplaceRootDir) {
12662
12682
  const relPath = relative2(marketplaceRootDir, pluginTargetDir).replaceAll("\\", "/");
@@ -12789,11 +12809,25 @@ async function getPreferredClaudeMarketplace() {
12789
12809
  }
12790
12810
  async function registerKnownClaudeMarketplace(name, rootDir) {
12791
12811
  const manifestPath = getClaudeKnownMarketplacesPath();
12792
- const existing = await readJsonFileIfExists(
12793
- manifestPath
12794
- ) ?? {};
12795
- if (existing[name]?.installLocation === rootDir) {
12796
- return;
12812
+ let existing = {};
12813
+ if (await fileExists(manifestPath)) {
12814
+ const raw = await readFile16(manifestPath, "utf-8");
12815
+ try {
12816
+ const parsed = JSON.parse(raw);
12817
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
12818
+ existing = parsed;
12819
+ } else {
12820
+ throw new Error("not a JSON object");
12821
+ }
12822
+ } catch (err2) {
12823
+ throw new Error(
12824
+ `Refusing to update ${manifestPath}: existing file is not a valid JSON object (${err2 instanceof Error ? err2.message : String(err2)}). Inspect and repair (or delete) it before re-running install-plugin.`
12825
+ );
12826
+ }
12827
+ }
12828
+ const had = Object.prototype.hasOwnProperty.call(existing, name);
12829
+ if (had && existing[name]?.installLocation === rootDir) {
12830
+ return { added: false, updated: false };
12797
12831
  }
12798
12832
  existing[name] = {
12799
12833
  ...existing[name] ?? {},
@@ -12803,8 +12837,52 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
12803
12837
  existing[name].lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
12804
12838
  existing[name].autoUpdate = true;
12805
12839
  await ensureDir(dirname8(manifestPath));
12806
- await writeFile6(manifestPath, `${JSON.stringify(existing, null, 2)}
12840
+ if (await fileExists(manifestPath)) {
12841
+ const prev = await readFile16(manifestPath, "utf-8");
12842
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
12843
+ await writeFile6(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
12844
+ }
12845
+ const tmpPath = `${manifestPath}.tmp`;
12846
+ await writeFile6(tmpPath, `${JSON.stringify(existing, null, 2)}
12807
12847
  `, "utf-8");
12848
+ await rename6(tmpPath, manifestPath);
12849
+ return { added: !had, updated: had };
12850
+ }
12851
+ async function ensureKnownClaudeMarketplaceForRoot(options) {
12852
+ return registerKnownClaudeMarketplace(options.name, options.rootDir);
12853
+ }
12854
+ async function setSyntaurPluginEnabled(options) {
12855
+ const settingsPath = resolve25(homedir2(), ".claude", "settings.json");
12856
+ const key = `syntaur@${options.marketplaceName}`;
12857
+ let parsed = {};
12858
+ if (await fileExists(settingsPath)) {
12859
+ const raw = await readFile16(settingsPath, "utf-8");
12860
+ try {
12861
+ parsed = JSON.parse(raw);
12862
+ } catch {
12863
+ throw new Error(
12864
+ `Cannot toggle plugin: ${settingsPath} is not valid JSON. Inspect and repair it before retrying.`
12865
+ );
12866
+ }
12867
+ }
12868
+ const enabledPlugins = parsed.enabledPlugins ?? {};
12869
+ const previous = typeof enabledPlugins[key] === "boolean" ? enabledPlugins[key] : void 0;
12870
+ if (previous === options.enabled) {
12871
+ return { key, previous, current: options.enabled, changed: false };
12872
+ }
12873
+ enabledPlugins[key] = options.enabled;
12874
+ parsed.enabledPlugins = enabledPlugins;
12875
+ await ensureDir(dirname8(settingsPath));
12876
+ if (await fileExists(settingsPath)) {
12877
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
12878
+ const prev = await readFile16(settingsPath, "utf-8");
12879
+ await writeFile6(`${settingsPath}.bak-${stamp}`, prev, "utf-8");
12880
+ }
12881
+ const tmpPath = `${settingsPath}.tmp`;
12882
+ await writeFile6(tmpPath, `${JSON.stringify(parsed, null, 2)}
12883
+ `, "utf-8");
12884
+ await rename6(tmpPath, settingsPath);
12885
+ return { key, previous, current: options.enabled, changed: true };
12808
12886
  }
12809
12887
  async function ensureClaudeUserMarketplace() {
12810
12888
  const existing = await getPreferredClaudeMarketplace();
@@ -13271,31 +13349,63 @@ async function textPrompt(question, defaultValue) {
13271
13349
 
13272
13350
  // src/utils/install-skills.ts
13273
13351
  init_fs();
13274
- import { readFile as readFile17, readdir as readdir11, mkdir as mkdir4, copyFile, rm as rm4 } from "fs/promises";
13275
- import { dirname as dirname9, resolve as resolve26, relative as relative3, join as join3 } from "path";
13276
- import { fileURLToPath as fileURLToPath4 } from "url";
13352
+ import { readFile as readFile18, readdir as readdir11, mkdir as mkdir4, copyFile, rm as rm4, lstat as lstat2 } from "fs/promises";
13353
+ import { resolve as resolve27, relative as relative3, join as join3 } from "path";
13354
+ import { homedir as homedir4 } from "os";
13355
+
13356
+ // src/utils/plugin-state.ts
13357
+ init_fs();
13358
+ import { readFile as readFile17 } from "fs/promises";
13359
+ import { resolve as resolve26 } from "path";
13277
13360
  import { homedir as homedir3 } from "os";
13278
- var REQUIRED_SKILLS = [
13361
+ function settingsPathFor(agent) {
13362
+ if (agent === "claude") return resolve26(homedir3(), ".claude", "settings.json");
13363
+ return null;
13364
+ }
13365
+ async function readJsonOrNull(path) {
13366
+ if (!path) return null;
13367
+ if (!await fileExists(path)) return null;
13368
+ try {
13369
+ const raw = await readFile17(path, "utf-8");
13370
+ return JSON.parse(raw);
13371
+ } catch {
13372
+ return null;
13373
+ }
13374
+ }
13375
+ async function isSyntaurPluginEnabledFor(agent) {
13376
+ const settings = await readJsonOrNull(settingsPathFor(agent));
13377
+ const enabled = settings?.enabledPlugins ?? {};
13378
+ for (const [key, value] of Object.entries(enabled)) {
13379
+ if (value !== true) continue;
13380
+ const atIndex = key.lastIndexOf("@");
13381
+ const pluginName = atIndex > 0 ? key.slice(0, atIndex) : key;
13382
+ if (pluginName === "syntaur") return true;
13383
+ }
13384
+ return false;
13385
+ }
13386
+
13387
+ // src/utils/install-skills.ts
13388
+ var KNOWN_SKILL_NAMES = [
13279
13389
  "syntaur-protocol",
13280
13390
  "grab-assignment",
13281
13391
  "plan-assignment",
13282
13392
  "complete-assignment",
13283
13393
  "create-assignment",
13284
13394
  "create-project",
13285
- "save-session-summary"
13395
+ "manage-statuses",
13396
+ "clear-assignment",
13397
+ "save-session-summary",
13398
+ "track-session",
13399
+ "track-server"
13286
13400
  ];
13287
- function getVendoredSkillsDir() {
13288
- const here = dirname9(fileURLToPath4(import.meta.url));
13289
- return resolve26(here, "..", "vendor", "syntaur-skills", "skills");
13290
- }
13291
- function getPlatformSkillsDir(target) {
13292
- const here = dirname9(fileURLToPath4(import.meta.url));
13293
- const kind = target === "claude" ? "claude-code" : "codex";
13294
- return resolve26(here, "..", "platforms", kind, "skills");
13401
+ var KNOWN_SKILLS = KNOWN_SKILL_NAMES;
13402
+ async function getSkillsDir() {
13403
+ const packageRoot = await findPackageRoot("skills");
13404
+ return resolve27(packageRoot, "skills");
13295
13405
  }
13296
13406
  function defaultSkillTargetDir(target) {
13297
- if (target === "claude") return resolve26(homedir3(), ".claude", "skills");
13298
- return resolve26(homedir3(), ".codex", "skills");
13407
+ if (target === "claude") return resolve27(homedir4(), ".claude", "skills");
13408
+ return resolve27(homedir4(), ".codex", "skills");
13299
13409
  }
13300
13410
  async function walkFiles(root) {
13301
13411
  const out = [];
@@ -13315,7 +13425,7 @@ async function walkFiles(root) {
13315
13425
  }
13316
13426
  async function filesEqual(a, b) {
13317
13427
  try {
13318
- const [ba, bb] = await Promise.all([readFile17(a), readFile17(b)]);
13428
+ const [ba, bb] = await Promise.all([readFile18(a), readFile18(b)]);
13319
13429
  if (ba.length !== bb.length) return false;
13320
13430
  return ba.equals(bb);
13321
13431
  } catch {
@@ -13347,70 +13457,97 @@ async function skillMatches(srcDir, destDir) {
13347
13457
  if (destFiles.length !== srcFiles.length) return false;
13348
13458
  return true;
13349
13459
  }
13350
- async function installSkillDir(srcDir, destDir, skillName, source, force) {
13460
+ async function isSymlink(path) {
13461
+ try {
13462
+ const stat6 = await lstat2(path);
13463
+ return stat6.isSymbolicLink();
13464
+ } catch {
13465
+ return false;
13466
+ }
13467
+ }
13468
+ async function installSkillDir(srcDir, destDir, skillName, force) {
13469
+ if (await isSymlink(destDir)) {
13470
+ return {
13471
+ skill: skillName,
13472
+ status: "skipped-symlink",
13473
+ targetPath: destDir
13474
+ };
13475
+ }
13351
13476
  if (!await fileExists(destDir)) {
13352
13477
  await copyDir(srcDir, destDir);
13353
- return { skill: skillName, status: "installed", targetPath: destDir, source };
13478
+ return { skill: skillName, status: "installed", targetPath: destDir };
13354
13479
  }
13355
13480
  if (await skillMatches(srcDir, destDir)) {
13356
- return { skill: skillName, status: "already-current", targetPath: destDir, source };
13481
+ return { skill: skillName, status: "already-current", targetPath: destDir };
13357
13482
  }
13358
13483
  if (force) {
13359
13484
  await rm4(destDir, { recursive: true, force: true });
13360
13485
  await copyDir(srcDir, destDir);
13361
- return { skill: skillName, status: "overwritten", targetPath: destDir, source };
13486
+ return { skill: skillName, status: "overwritten", targetPath: destDir };
13487
+ }
13488
+ return { skill: skillName, status: "differs-preserved", targetPath: destDir };
13489
+ }
13490
+ async function discoverSkillNames(sourceDir) {
13491
+ const entries = await readdir11(sourceDir, { withFileTypes: true });
13492
+ const names = [];
13493
+ for (const entry of entries) {
13494
+ if (!entry.isDirectory()) continue;
13495
+ if (entry.name.startsWith(".")) continue;
13496
+ if (await fileExists(join3(sourceDir, entry.name, "SKILL.md"))) {
13497
+ names.push(entry.name);
13498
+ }
13362
13499
  }
13363
- return { skill: skillName, status: "differs-preserved", targetPath: destDir, source };
13500
+ const pinnedSet = new Set(KNOWN_SKILL_NAMES);
13501
+ const pinned = KNOWN_SKILL_NAMES.filter((name) => names.includes(name));
13502
+ const extras = names.filter((name) => !pinnedSet.has(name)).sort();
13503
+ return [...pinned, ...extras];
13364
13504
  }
13365
- async function installSkills(options) {
13366
- const source = options.sourceDir ?? getVendoredSkillsDir();
13367
- const platformSource = options.platformSkillsDir ?? getPlatformSkillsDir(options.target);
13505
+ async function installSkillsWithReport(options) {
13506
+ if (!options.ignorePluginActive && options.targetDir === void 0) {
13507
+ if (await isSyntaurPluginEnabledFor(options.target)) {
13508
+ return { results: [], skippedReason: "plugin-active" };
13509
+ }
13510
+ }
13511
+ const source = options.sourceDir ?? await getSkillsDir();
13368
13512
  const targetRoot = options.targetDir ?? defaultSkillTargetDir(options.target);
13369
13513
  const force = options.force ?? false;
13370
13514
  if (!await fileExists(source)) {
13371
13515
  throw new Error(
13372
- `Vendored skills not found at ${source}. Reinstall syntaur: npm install -g syntaur@latest`
13516
+ `Syntaur skills not found at ${source}. Reinstall syntaur: npm install -g syntaur@latest`
13373
13517
  );
13374
13518
  }
13519
+ const skillNames = await discoverSkillNames(source);
13375
13520
  const results = [];
13376
13521
  await mkdir4(targetRoot, { recursive: true });
13377
- for (const skill of REQUIRED_SKILLS) {
13522
+ for (const skill of skillNames) {
13378
13523
  const srcDir = join3(source, skill);
13379
- if (!await fileExists(srcDir)) continue;
13380
13524
  const destDir = join3(targetRoot, skill);
13381
- results.push(await installSkillDir(srcDir, destDir, skill, "shared", force));
13382
- }
13383
- if (options.target === "claude" && await fileExists(platformSource)) {
13384
- const entries = await readdir11(platformSource, { withFileTypes: true });
13385
- for (const entry of entries) {
13386
- if (!entry.isDirectory()) continue;
13387
- const skill = entry.name;
13388
- if (REQUIRED_SKILLS.includes(skill)) continue;
13389
- const srcDir = join3(platformSource, skill);
13390
- const destDir = join3(targetRoot, skill);
13391
- results.push(await installSkillDir(srcDir, destDir, skill, "platform", force));
13392
- }
13525
+ results.push(await installSkillDir(srcDir, destDir, skill, force));
13393
13526
  }
13394
- return results;
13527
+ return { results };
13395
13528
  }
13396
13529
  async function uninstallSkills(options) {
13397
13530
  const targetRoot = options.targetDir ?? defaultSkillTargetDir(options.target);
13398
13531
  if (!await fileExists(targetRoot)) return [];
13399
- const known = new Set(REQUIRED_SKILLS);
13400
- const platformSource = options.platformSkillsDir ?? getPlatformSkillsDir(options.target);
13401
- if (options.target === "claude" && await fileExists(platformSource)) {
13402
- const entries = await readdir11(platformSource, { withFileTypes: true });
13403
- for (const entry of entries) {
13404
- if (entry.isDirectory()) known.add(entry.name);
13532
+ const sourceDir = options.sourceDir ?? await getSkillsDir();
13533
+ const known = /* @__PURE__ */ new Set();
13534
+ if (await fileExists(sourceDir)) {
13535
+ for (const name of await discoverSkillNames(sourceDir)) {
13536
+ known.add(name);
13537
+ }
13538
+ } else {
13539
+ for (const name of KNOWN_SKILL_NAMES) {
13540
+ known.add(name);
13405
13541
  }
13406
13542
  }
13407
13543
  const removed = [];
13408
13544
  for (const skill of known) {
13409
13545
  const destDir = join3(targetRoot, skill);
13410
13546
  if (!await fileExists(destDir)) continue;
13547
+ if (await isSymlink(destDir)) continue;
13411
13548
  const skillMd = join3(destDir, "SKILL.md");
13412
13549
  if (!await fileExists(skillMd)) continue;
13413
- const content = await readFile17(skillMd, "utf-8").catch(() => "");
13550
+ const content = await readFile18(skillMd, "utf-8").catch(() => "");
13414
13551
  const match = content.match(/^name:\s*(\S+)\s*$/m);
13415
13552
  if (!match || match[1] !== skill) continue;
13416
13553
  await rm4(destDir, { recursive: true, force: true });
@@ -13418,11 +13555,22 @@ async function uninstallSkills(options) {
13418
13555
  }
13419
13556
  return removed;
13420
13557
  }
13421
- function formatInstallReport(results, target) {
13558
+ function formatInstallReport(resultsOrReport, target) {
13559
+ const report = Array.isArray(resultsOrReport) ? { results: resultsOrReport } : resultsOrReport;
13560
+ const { results, skippedReason } = report;
13422
13561
  const lines = [];
13423
13562
  lines.push(`Skill install (${target}):`);
13563
+ if (skippedReason === "plugin-active") {
13564
+ lines.push(
13565
+ " Skipped \u2014 syntaur plugin is enabled for this agent and already loads skills from its manifest."
13566
+ );
13567
+ lines.push(
13568
+ " Run with --force-skills to install globally anyway (creates duplicates with the plugin)."
13569
+ );
13570
+ return lines.join("\n");
13571
+ }
13424
13572
  for (const r of results) {
13425
- const marker = r.status === "installed" ? "+" : r.status === "overwritten" ? "!" : r.status === "differs-preserved" ? "?" : "=";
13573
+ const marker = r.status === "installed" ? "+" : r.status === "overwritten" ? "!" : r.status === "differs-preserved" ? "?" : r.status === "skipped-symlink" ? "~" : "=";
13426
13574
  lines.push(` ${marker} ${r.skill} (${r.status})`);
13427
13575
  }
13428
13576
  const diffs = results.filter((r) => r.status === "differs-preserved");
@@ -13432,7 +13580,14 @@ function formatInstallReport(results, target) {
13432
13580
  ` Note: ${diffs.length} skill(s) already exist with different content and were preserved.`
13433
13581
  );
13434
13582
  lines.push(
13435
- " Run with --force-skills to overwrite with the vendored version."
13583
+ " Run with --force-skills to overwrite with the syntaur version."
13584
+ );
13585
+ }
13586
+ const symlinked = results.filter((r) => r.status === "skipped-symlink");
13587
+ if (symlinked.length > 0) {
13588
+ lines.push("");
13589
+ lines.push(
13590
+ ` Note: ${symlinked.length} skill(s) were skipped because the target is a symlink (likely managed by skills.sh).`
13436
13591
  );
13437
13592
  }
13438
13593
  return lines.join("\n");
@@ -13450,11 +13605,12 @@ async function promptForInstallPath(question, recommendedPath) {
13450
13605
  }
13451
13606
  }
13452
13607
  async function installPluginCommand(options) {
13608
+ const envOverride = process.env.SYNTAUR_PLUGIN_TARGET?.trim();
13453
13609
  const shouldPromptForTarget = Boolean(
13454
- options.promptForTarget !== false && isInteractiveTerminal() && !options.targetDir
13610
+ options.promptForTarget !== false && isInteractiveTerminal() && !options.targetDir && !envOverride
13455
13611
  );
13456
13612
  const recommendedTargetDir = await recommendPluginTargetDir("claude");
13457
- const targetDir = options.targetDir ? normalizeAbsoluteInstallPath(options.targetDir, "Claude plugin target") : shouldPromptForTarget ? await promptForInstallPath("Claude plugin directory", recommendedTargetDir) : recommendedTargetDir;
13613
+ const targetDir = options.targetDir ? normalizeAbsoluteInstallPath(options.targetDir, "Claude plugin target") : envOverride ? normalizeAbsoluteInstallPath(envOverride, "SYNTAUR_PLUGIN_TARGET") : shouldPromptForTarget ? await promptForInstallPath("Claude plugin directory", recommendedTargetDir) : recommendedTargetDir;
13458
13614
  const previousTargetDir = await getConfiguredOrLegacyManagedPluginDir("claude");
13459
13615
  const migrating = Boolean(previousTargetDir && previousTargetDir !== targetDir);
13460
13616
  let previousInstall = previousTargetDir ? await inspectInstallPath("claude", previousTargetDir) : null;
@@ -13487,6 +13643,7 @@ async function installPluginCommand(options) {
13487
13643
  targetDir
13488
13644
  });
13489
13645
  const currentMarketplace = await detectClaudeMarketplaceForTarget(result.targetDir);
13646
+ let knownMarketplaceState = null;
13490
13647
  if (currentMarketplace) {
13491
13648
  await ensureClaudeMarketplaceEntry({
13492
13649
  marketplaceRootDir: currentMarketplace.rootDir,
@@ -13494,6 +13651,10 @@ async function installPluginCommand(options) {
13494
13651
  pluginTargetDir: result.targetDir,
13495
13652
  expectedExistingPluginTargetDir: previousMarketplace && previousMarketplace.manifestPath === currentMarketplace.manifestPath ? previousTargetDir : null
13496
13653
  });
13654
+ knownMarketplaceState = await ensureKnownClaudeMarketplaceForRoot({
13655
+ name: currentMarketplace.name,
13656
+ rootDir: currentMarketplace.rootDir
13657
+ });
13497
13658
  } else {
13498
13659
  console.warn(
13499
13660
  `Warning: ${result.targetDir} is not inside a Claude Code marketplace (expected parent path of the form ~/.claude/plugins/marketplaces/<name>/plugins/syntaur). The plugin files were copied, but Claude Code will not discover them until you place them inside a marketplace.`
@@ -13517,21 +13678,49 @@ async function installPluginCommand(options) {
13517
13678
  }
13518
13679
  previousInstall = null;
13519
13680
  }
13681
+ let enableResult = null;
13682
+ if (options.enable && currentMarketplace) {
13683
+ try {
13684
+ enableResult = await setSyntaurPluginEnabled({
13685
+ marketplaceName: currentMarketplace.name,
13686
+ enabled: true
13687
+ });
13688
+ } catch (error) {
13689
+ console.warn(
13690
+ `Warning: could not enable plugin \u2014 ${error instanceof Error ? error.message : String(error)}`
13691
+ );
13692
+ }
13693
+ }
13520
13694
  console.log("Installed Syntaur plugin:");
13521
13695
  console.log(` target: ${result.targetDir}`);
13522
13696
  console.log(` source: ${result.sourceDir}`);
13523
13697
  console.log(` mode: ${result.mode}`);
13524
13698
  if (currentMarketplace) {
13525
13699
  console.log(` marketplace: ${currentMarketplace.manifestPath}`);
13700
+ if (knownMarketplaceState) {
13701
+ const tag = knownMarketplaceState.added ? "registered (added)" : knownMarketplaceState.updated ? "registered (updated)" : "already registered";
13702
+ console.log(` known_marketplaces.json: ${tag}`);
13703
+ }
13704
+ const enabledKey = `syntaur@${currentMarketplace.name}`;
13705
+ if (enableResult) {
13706
+ console.log(
13707
+ ` enabledPlugins: ${enabledKey} = ${enableResult.current}` + (enableResult.changed ? "" : " (already)")
13708
+ );
13709
+ } else {
13710
+ console.log(
13711
+ ` enabledPlugins: ${enabledKey} not modified \u2014 run /plugin to enable, or pass --enable next time`
13712
+ );
13713
+ }
13526
13714
  }
13527
13715
  if (!options.skipSkills) {
13528
13716
  try {
13529
- const skillResults = await installSkills({
13717
+ const skillReport = await installSkillsWithReport({
13530
13718
  target: "claude",
13531
- force: options.forceSkills
13719
+ force: options.forceSkills,
13720
+ ignorePluginActive: options.forceSkills
13532
13721
  });
13533
13722
  console.log("");
13534
- console.log(formatInstallReport(skillResults, "claude"));
13723
+ console.log(formatInstallReport(skillReport, "claude"));
13535
13724
  } catch (error) {
13536
13725
  console.warn(
13537
13726
  `Warning: skill install failed \u2014 ${error instanceof Error ? error.message : String(error)}`
@@ -13539,25 +13728,24 @@ async function installPluginCommand(options) {
13539
13728
  }
13540
13729
  }
13541
13730
  console.log("\nThe plugin is now available in Claude Code.");
13542
- console.log(" Slash commands: /grab-assignment, /plan-assignment, /complete-assignment, /create-assignment, /create-project, /track-session, /save-session-summary");
13731
+ console.log(" Slash commands: /grab-assignment, /plan-assignment, /complete-assignment, /create-assignment, /create-project, /track-session, /clear-assignment, /manage-statuses");
13543
13732
  console.log(" Background: syntaur-protocol skill (auto-invoked)");
13544
- console.log(" Claude-specific skill: track-session (agent session registration)");
13545
- console.log(" Hook: write boundary enforcement (PreToolUse) + SessionStart/End + PreCompact (prompts /save-session-summary)");
13733
+ console.log(" Hook: write boundary enforcement (PreToolUse) + SessionStart/End");
13546
13734
  }
13547
13735
 
13548
13736
  // src/commands/install-statusline.ts
13549
13737
  init_paths();
13550
13738
  init_fs();
13551
- import { readFile as readFile19, writeFile as writeFile8, copyFile as copyFile2, rm as rm5, stat as stat3, symlink as symlink2, unlink as unlink6, lstat as lstat2 } from "fs/promises";
13552
- import { resolve as resolve28, dirname as dirname11 } from "path";
13553
- import { homedir as homedir4 } from "os";
13554
- import { fileURLToPath as fileURLToPath5 } from "url";
13739
+ import { readFile as readFile20, writeFile as writeFile8, copyFile as copyFile2, rm as rm5, stat as stat2, symlink as symlink2, unlink as unlink6, lstat as lstat3 } from "fs/promises";
13740
+ import { resolve as resolve29, dirname as dirname10 } from "path";
13741
+ import { homedir as homedir5 } from "os";
13742
+ import { fileURLToPath as fileURLToPath4 } from "url";
13555
13743
 
13556
13744
  // src/commands/configure-statusline.ts
13557
13745
  init_paths();
13558
13746
  init_fs();
13559
- import { readFile as readFile18, writeFile as writeFile7 } from "fs/promises";
13560
- import { resolve as resolve27, dirname as dirname10 } from "path";
13747
+ import { readFile as readFile19, writeFile as writeFile7 } from "fs/promises";
13748
+ import { resolve as resolve28, dirname as dirname9 } from "path";
13561
13749
  import { spawnSync } from "child_process";
13562
13750
  import { checkbox, input as input2, confirm } from "@inquirer/prompts";
13563
13751
  var AVAILABLE_SEGMENTS = [
@@ -13578,12 +13766,12 @@ var PRESETS = {
13578
13766
  tracker: { segments: ["git", "assignment", "external", "session"], separator: " \xB7 " }
13579
13767
  };
13580
13768
  function getConfigPath(installRoot) {
13581
- return resolve27(installRoot, "statusline.config.json");
13769
+ return resolve28(installRoot, "statusline.config.json");
13582
13770
  }
13583
13771
  async function readConfig2(path) {
13584
13772
  if (!await fileExists(path)) return null;
13585
13773
  try {
13586
- const raw = await readFile18(path, "utf-8");
13774
+ const raw = await readFile19(path, "utf-8");
13587
13775
  const parsed = JSON.parse(raw);
13588
13776
  if (!parsed || typeof parsed !== "object") return null;
13589
13777
  const segments = Array.isArray(parsed.segments) ? parsed.segments.filter(isSegmentName) : [];
@@ -13678,7 +13866,7 @@ function renderPreview(config, statuslineScript, cwd) {
13678
13866
  env: {
13679
13867
  ...process.env,
13680
13868
  // Force the child to pick up the freshly-written config from install root.
13681
- HOME: dirname10(dirname10(statuslineScript))
13869
+ HOME: dirname9(dirname9(statuslineScript))
13682
13870
  }
13683
13871
  });
13684
13872
  if (res.status !== 0) return null;
@@ -13729,14 +13917,14 @@ async function configureStatuslineCommand(options = {}) {
13729
13917
  console.log("(preview mode \u2014 config NOT written)");
13730
13918
  return;
13731
13919
  }
13732
- await ensureDir(dirname10(configPath));
13920
+ await ensureDir(dirname9(configPath));
13733
13921
  await writeFile7(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
13734
13922
  console.log("Wrote statusline config:");
13735
13923
  console.log(` path: ${configPath}`);
13736
13924
  console.log(` segments: ${config.segments.join(", ")}`);
13737
13925
  console.log(` separator: ${JSON.stringify(config.separator)}`);
13738
13926
  if (config.wrap) console.log(` wrap: ${config.wrap}`);
13739
- const script = options.statuslineScript ?? resolve27(installRoot, "statusline.sh");
13927
+ const script = options.statuslineScript ?? resolve28(installRoot, "statusline.sh");
13740
13928
  if (await fileExists(script)) {
13741
13929
  console.log("");
13742
13930
  console.log("Live preview:");
@@ -13756,7 +13944,7 @@ async function configureStatuslineCommand(options = {}) {
13756
13944
  async function writeDefaultConfigIfMissing(installRoot) {
13757
13945
  const path = getConfigPath(installRoot);
13758
13946
  if (await fileExists(path)) return;
13759
- await ensureDir(dirname10(path));
13947
+ await ensureDir(dirname9(path));
13760
13948
  const defaultConfig = {
13761
13949
  segments: ["git", "assignment", "session"],
13762
13950
  separator: " \xB7 "
@@ -13766,12 +13954,12 @@ async function writeDefaultConfigIfMissing(installRoot) {
13766
13954
 
13767
13955
  // src/commands/install-statusline.ts
13768
13956
  function getPackageStatuslineSource() {
13769
- const here = dirname11(fileURLToPath5(import.meta.url));
13770
- return resolve28(here, "..", "statusline", "statusline.sh");
13957
+ const here = dirname10(fileURLToPath4(import.meta.url));
13958
+ return resolve29(here, "..", "statusline", "statusline.sh");
13771
13959
  }
13772
13960
  async function readSettingsJson(settingsPath) {
13773
13961
  if (!await fileExists(settingsPath)) return {};
13774
- const raw = await readFile19(settingsPath, "utf-8");
13962
+ const raw = await readFile20(settingsPath, "utf-8");
13775
13963
  if (raw.trim() === "") return {};
13776
13964
  try {
13777
13965
  const parsed = JSON.parse(raw);
@@ -13783,7 +13971,7 @@ async function readSettingsJson(settingsPath) {
13783
13971
  }
13784
13972
  }
13785
13973
  async function writeSettingsJson(settingsPath, data) {
13786
- await ensureDir(dirname11(settingsPath));
13974
+ await ensureDir(dirname10(settingsPath));
13787
13975
  await writeFile8(settingsPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
13788
13976
  }
13789
13977
  async function resolveMode(mode, existingCommand, ourCommand) {
@@ -13818,7 +14006,7 @@ function extractExistingCommand(settings) {
13818
14006
  };
13819
14007
  }
13820
14008
  async function backupSettings(settingsSnapshot, backupPath) {
13821
- await ensureDir(dirname11(backupPath));
14009
+ await ensureDir(dirname10(backupPath));
13822
14010
  await writeFile8(
13823
14011
  backupPath,
13824
14012
  JSON.stringify(
@@ -13835,9 +14023,9 @@ async function backupSettings(settingsSnapshot, backupPath) {
13835
14023
  );
13836
14024
  }
13837
14025
  async function installScript(sourceScript, destScript, link) {
13838
- await ensureDir(dirname11(destScript));
14026
+ await ensureDir(dirname10(destScript));
13839
14027
  try {
13840
- const s = await lstat2(destScript);
14028
+ const s = await lstat3(destScript);
13841
14029
  if (s.isSymbolicLink() || s.isFile()) {
13842
14030
  await unlink6(destScript);
13843
14031
  }
@@ -13851,12 +14039,12 @@ async function installScript(sourceScript, destScript, link) {
13851
14039
  }
13852
14040
  async function installStatuslineCommand(options = {}) {
13853
14041
  const mode = options.mode ?? "ask";
13854
- const settingsPath = options.settingsPath ?? resolve28(homedir4(), ".claude", "settings.json");
14042
+ const settingsPath = options.settingsPath ?? resolve29(homedir5(), ".claude", "settings.json");
13855
14043
  const installRoot = options.installRoot ?? syntaurRoot();
13856
14044
  const sourceScript = options.sourceScript ?? getPackageStatuslineSource();
13857
- const destScript = resolve28(installRoot, "statusline.sh");
13858
- const confPath = resolve28(installRoot, "statusline.conf");
13859
- const backupPath = resolve28(installRoot, "statusline.backup.json");
14045
+ const destScript = resolve29(installRoot, "statusline.sh");
14046
+ const confPath = resolve29(installRoot, "statusline.conf");
14047
+ const backupPath = resolve29(installRoot, "statusline.backup.json");
13860
14048
  if (!await fileExists(sourceScript)) {
13861
14049
  throw new Error(
13862
14050
  `Statusline source script not found at ${sourceScript}. Try re-installing syntaur (npm install -g syntaur) or pass --source-script explicitly.`
@@ -13892,7 +14080,7 @@ async function installStatuslineCommand(options = {}) {
13892
14080
  if (parsed) {
13893
14081
  wrapTarget = parsed;
13894
14082
  } else {
13895
- const wrapperPath = resolve28(installRoot, "statusline-wrapped.sh");
14083
+ const wrapperPath = resolve29(installRoot, "statusline-wrapped.sh");
13896
14084
  const wrapperBody = `#!/usr/bin/env bash
13897
14085
  # Auto-generated by syntaur install-statusline.
13898
14086
  # Executes the previously configured statusLine command.
@@ -13903,7 +14091,7 @@ exec ${existingCommand}
13903
14091
  wrapTarget = wrapperPath;
13904
14092
  }
13905
14093
  }
13906
- await ensureDir(dirname11(confPath));
14094
+ await ensureDir(dirname10(confPath));
13907
14095
  await writeFile8(
13908
14096
  confPath,
13909
14097
  wrapTarget ? `# Wrap target \u2014 the command below is invoked with the same stdin; its
@@ -13941,25 +14129,25 @@ function parseWrapCommand(command) {
13941
14129
  async function chmodExec(path) {
13942
14130
  const fs = await import("fs/promises");
13943
14131
  try {
13944
- const s = await stat3(path);
14132
+ const s = await stat2(path);
13945
14133
  await fs.chmod(path, s.mode | 73);
13946
14134
  } catch {
13947
14135
  }
13948
14136
  }
13949
14137
  async function uninstallStatuslineCommand(options = {}) {
13950
- const settingsPath = options.settingsPath ?? resolve28(homedir4(), ".claude", "settings.json");
14138
+ const settingsPath = options.settingsPath ?? resolve29(homedir5(), ".claude", "settings.json");
13951
14139
  const installRoot = options.installRoot ?? syntaurRoot();
13952
- const destScript = resolve28(installRoot, "statusline.sh");
13953
- const confPath = resolve28(installRoot, "statusline.conf");
13954
- const backupPath = resolve28(installRoot, "statusline.backup.json");
13955
- const wrapperPath = resolve28(installRoot, "statusline-wrapped.sh");
14140
+ const destScript = resolve29(installRoot, "statusline.sh");
14141
+ const confPath = resolve29(installRoot, "statusline.conf");
14142
+ const backupPath = resolve29(installRoot, "statusline.backup.json");
14143
+ const wrapperPath = resolve29(installRoot, "statusline-wrapped.sh");
13956
14144
  const settings = await readSettingsJson(settingsPath);
13957
14145
  const existing = extractExistingCommand(settings);
13958
14146
  const ourCommand = `bash ${destScript}`;
13959
14147
  let restored = null;
13960
14148
  if (await fileExists(backupPath)) {
13961
14149
  try {
13962
- const raw = await readFile19(backupPath, "utf-8");
14150
+ const raw = await readFile20(backupPath, "utf-8");
13963
14151
  const parsed = JSON.parse(raw);
13964
14152
  const prev = parsed?.previousStatusLine;
13965
14153
  if (prev && typeof prev === "object" && typeof prev.command === "string") {
@@ -13980,7 +14168,7 @@ async function uninstallStatuslineCommand(options = {}) {
13980
14168
  await writeSettingsJson(settingsPath, settings);
13981
14169
  }
13982
14170
  if (!options.keepScript) {
13983
- const configPath = resolve28(installRoot, "statusline.config.json");
14171
+ const configPath = resolve29(installRoot, "statusline.config.json");
13984
14172
  for (const path of [destScript, confPath, backupPath, wrapperPath, configPath]) {
13985
14173
  try {
13986
14174
  await rm5(path, { force: true });
@@ -14078,12 +14266,13 @@ async function installCodexPluginCommand(options) {
14078
14266
  console.log(` marketplace: ${marketplace.marketplacePath}`);
14079
14267
  if (!options.skipSkills) {
14080
14268
  try {
14081
- const skillResults = await installSkills({
14269
+ const skillReport = await installSkillsWithReport({
14082
14270
  target: "codex",
14083
- force: options.forceSkills
14271
+ force: options.forceSkills,
14272
+ ignorePluginActive: options.forceSkills
14084
14273
  });
14085
14274
  console.log("");
14086
- console.log(formatInstallReport(skillResults, "codex"));
14275
+ console.log(formatInstallReport(skillReport, "codex"));
14087
14276
  } catch (error) {
14088
14277
  console.warn(
14089
14278
  `Warning: skill install failed \u2014 ${error instanceof Error ? error.message : String(error)}`
@@ -14236,7 +14425,7 @@ async function setupCommand(options) {
14236
14425
  }
14237
14426
 
14238
14427
  // src/commands/uninstall.ts
14239
- import { resolve as resolve29 } from "path";
14428
+ import { resolve as resolve30 } from "path";
14240
14429
  init_paths();
14241
14430
  function expandTargets(options) {
14242
14431
  if (options.all) {
@@ -14316,7 +14505,7 @@ async function uninstallCommand(options) {
14316
14505
  const configuredProjectDir = await getConfiguredProjectDir();
14317
14506
  await removeSyntaurData();
14318
14507
  console.log(`Removed ${syntaurRoot()}`);
14319
- if (configuredProjectDir && resolve29(configuredProjectDir) !== resolve29(syntaurRoot(), "projects")) {
14508
+ if (configuredProjectDir && resolve30(configuredProjectDir) !== resolve30(syntaurRoot(), "projects")) {
14320
14509
  console.warn(
14321
14510
  `Warning: config.md pointed to an external project directory (${configuredProjectDir}). That directory was not removed automatically.`
14322
14511
  );
@@ -14335,7 +14524,7 @@ init_slug();
14335
14524
  init_cursor_rules();
14336
14525
  init_codex_agents();
14337
14526
  init_opencode_config();
14338
- import { resolve as resolve30 } from "path";
14527
+ import { resolve as resolve31 } from "path";
14339
14528
  var SUPPORTED_FRAMEWORKS = ["cursor", "codex", "opencode"];
14340
14529
  async function setupAdapterCommand(framework, options) {
14341
14530
  if (!SUPPORTED_FRAMEWORKS.includes(framework)) {
@@ -14361,19 +14550,19 @@ async function setupAdapterCommand(framework, options) {
14361
14550
  }
14362
14551
  const config = await readConfig();
14363
14552
  const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
14364
- const projectDir = resolve30(baseDir, options.project);
14365
- const assignmentDir = resolve30(
14553
+ const projectDir = resolve31(baseDir, options.project);
14554
+ const assignmentDir = resolve31(
14366
14555
  projectDir,
14367
14556
  "assignments",
14368
14557
  options.assignment
14369
14558
  );
14370
- const projectMdPath = resolve30(projectDir, "project.md");
14559
+ const projectMdPath = resolve31(projectDir, "project.md");
14371
14560
  if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
14372
14561
  throw new Error(
14373
14562
  `Project "${options.project}" not found at ${projectDir}.`
14374
14563
  );
14375
14564
  }
14376
- const assignmentMdPath = resolve30(assignmentDir, "assignment.md");
14565
+ const assignmentMdPath = resolve31(assignmentDir, "assignment.md");
14377
14566
  if (!await fileExists(assignmentDir) || !await fileExists(assignmentMdPath)) {
14378
14567
  throw new Error(
14379
14568
  `Assignment "${options.assignment}" not found at ${assignmentDir}.`
@@ -14401,15 +14590,15 @@ async function setupAdapterCommand(framework, options) {
14401
14590
  }
14402
14591
  }
14403
14592
  if (framework === "cursor") {
14404
- const protocolPath = resolve30(cwd, ".cursor", "rules", "syntaur-protocol.mdc");
14405
- const assignmentPath = resolve30(cwd, ".cursor", "rules", "syntaur-assignment.mdc");
14593
+ const protocolPath = resolve31(cwd, ".cursor", "rules", "syntaur-protocol.mdc");
14594
+ const assignmentPath = resolve31(cwd, ".cursor", "rules", "syntaur-assignment.mdc");
14406
14595
  await writeAdapterFile(protocolPath, renderCursorProtocol());
14407
14596
  await writeAdapterFile(assignmentPath, renderCursorAssignment(rendererParams));
14408
14597
  } else if (framework === "codex" || framework === "opencode") {
14409
- const agentsPath = resolve30(cwd, "AGENTS.md");
14598
+ const agentsPath = resolve31(cwd, "AGENTS.md");
14410
14599
  await writeAdapterFile(agentsPath, renderCodexAgents(rendererParams));
14411
14600
  if (framework === "opencode") {
14412
- const configPath = resolve30(cwd, "opencode.json");
14601
+ const configPath = resolve31(cwd, "opencode.json");
14413
14602
  await writeAdapterFile(configPath, renderOpenCodeConfig({ projectDir }));
14414
14603
  }
14415
14604
  }
@@ -14434,7 +14623,7 @@ async function setupAdapterCommand(framework, options) {
14434
14623
  init_paths();
14435
14624
  init_fs();
14436
14625
  init_config2();
14437
- import { resolve as resolve31 } from "path";
14626
+ import { resolve as resolve32 } from "path";
14438
14627
  async function trackSessionCommand(options) {
14439
14628
  if (!options.agent) {
14440
14629
  throw new Error("--agent <name> is required.");
@@ -14447,7 +14636,7 @@ async function trackSessionCommand(options) {
14447
14636
  if (options.project) {
14448
14637
  const config = await readConfig();
14449
14638
  const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
14450
- const projectDir = resolve31(baseDir, options.project);
14639
+ const projectDir = resolve32(baseDir, options.project);
14451
14640
  if (!await fileExists(projectDir)) {
14452
14641
  throw new Error(
14453
14642
  `Project "${options.project}" not found at ${projectDir}.`
@@ -14481,8 +14670,8 @@ init_config2();
14481
14670
  init_paths();
14482
14671
  init_fs();
14483
14672
  import { spawnSync as spawnSync2 } from "child_process";
14484
- import { resolve as resolve33, isAbsolute as isAbsolute4 } from "path";
14485
- import { readFile as readFile21 } from "fs/promises";
14673
+ import { resolve as resolve34, isAbsolute as isAbsolute4 } from "path";
14674
+ import { readFile as readFile22 } from "fs/promises";
14486
14675
  import { select, confirm as confirm2, input as input3 } from "@inquirer/prompts";
14487
14676
  async function browseCommand(options) {
14488
14677
  const config = await readConfig();
@@ -14551,7 +14740,7 @@ async function pickAgent(agents) {
14551
14740
  return picked;
14552
14741
  }
14553
14742
  async function ensureWorktree(opts) {
14554
- const assignmentPath = resolve33(
14743
+ const assignmentPath = resolve34(
14555
14744
  opts.projectsDir,
14556
14745
  opts.projectSlug,
14557
14746
  "assignments",
@@ -14561,7 +14750,7 @@ async function ensureWorktree(opts) {
14561
14750
  if (!await fileExists(assignmentPath)) {
14562
14751
  return void 0;
14563
14752
  }
14564
- const content = await readFile21(assignmentPath, "utf-8");
14753
+ const content = await readFile22(assignmentPath, "utf-8");
14565
14754
  const { parseAssignmentFrontmatter: parseAssignmentFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
14566
14755
  const fm = parseAssignmentFrontmatter2(content);
14567
14756
  const { workspace } = fm;
@@ -14632,7 +14821,7 @@ function computeWorktreeDefaults(opts) {
14632
14821
  const repository = opts.existing.repository ?? detectCurrentGitRoot();
14633
14822
  const branch = opts.projectSlug ? `syntaur/${opts.projectSlug}/${opts.assignmentSlug}` : `syntaur/${opts.assignmentSlug}`;
14634
14823
  const parentBranch = opts.existing.parentBranch ?? detectCurrentBranch() ?? "main";
14635
- const worktreeBase = resolve33(
14824
+ const worktreeBase = resolve34(
14636
14825
  syntaurRoot(),
14637
14826
  "worktrees",
14638
14827
  opts.projectSlug || "standalone",
@@ -14665,7 +14854,7 @@ function detectCurrentBranch() {
14665
14854
  async function runCreate(opts) {
14666
14855
  const { createWorktreeAndRecord: createWorktreeAndRecord2, GitWorktreeError: GitWorktreeError2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
14667
14856
  const expandedWorktree = expandHome(opts.worktreePath);
14668
- const absWorktree = isAbsolute4(expandedWorktree) ? expandedWorktree : resolve33(expandedWorktree);
14857
+ const absWorktree = isAbsolute4(expandedWorktree) ? expandedWorktree : resolve34(expandedWorktree);
14669
14858
  try {
14670
14859
  await createWorktreeAndRecord2({
14671
14860
  assignmentPath: opts.assignmentPath,
@@ -14694,7 +14883,7 @@ init_paths();
14694
14883
  init_fs();
14695
14884
  init_playbook();
14696
14885
  init_playbooks();
14697
- import { resolve as resolve34 } from "path";
14886
+ import { resolve as resolve35 } from "path";
14698
14887
  async function createPlaybookCommand(name, options) {
14699
14888
  if (!name.trim()) {
14700
14889
  throw new Error("Playbook name cannot be empty.");
@@ -14707,7 +14896,7 @@ async function createPlaybookCommand(name, options) {
14707
14896
  }
14708
14897
  const dir = playbooksDir();
14709
14898
  await ensureDir(dir);
14710
- const filePath = resolve34(dir, `${slug}.md`);
14899
+ const filePath = resolve35(dir, `${slug}.md`);
14711
14900
  if (await fileExists(filePath)) {
14712
14901
  throw new Error(
14713
14902
  `Playbook "${slug}" already exists at ${filePath}
@@ -14729,8 +14918,8 @@ init_paths();
14729
14918
  init_fs();
14730
14919
  init_parser();
14731
14920
  init_config2();
14732
- import { readdir as readdir12, readFile as readFile22 } from "fs/promises";
14733
- import { resolve as resolve35 } from "path";
14921
+ import { readdir as readdir12, readFile as readFile23 } from "fs/promises";
14922
+ import { resolve as resolve36 } from "path";
14734
14923
  async function listPlaybooksCommand(options = {}) {
14735
14924
  const dir = playbooksDir();
14736
14925
  if (!await fileExists(dir)) {
@@ -14745,8 +14934,8 @@ async function listPlaybooksCommand(options = {}) {
14745
14934
  );
14746
14935
  const rows = [];
14747
14936
  for (const entry of mdFiles) {
14748
- const filePath = resolve35(dir, entry.name);
14749
- const raw = await readFile22(filePath, "utf-8");
14937
+ const filePath = resolve36(dir, entry.name);
14938
+ const raw = await readFile23(filePath, "utf-8");
14750
14939
  const parsed = parsePlaybook(raw);
14751
14940
  const slug = parsed.slug || entry.name.replace(/\.md$/, "");
14752
14941
  const disabled = disabledSet.has(slug);
@@ -14844,8 +15033,8 @@ init_fs();
14844
15033
  init_config2();
14845
15034
  init_slug();
14846
15035
  import { Command } from "commander";
14847
- import { readFile as readFile23 } from "fs/promises";
14848
- import { resolve as resolve36 } from "path";
15036
+ import { readFile as readFile24 } from "fs/promises";
15037
+ import { resolve as resolve37 } from "path";
14849
15038
  var WORKSPACE_REGEX3 = /^[a-z0-9_][a-z0-9-]*$/;
14850
15039
  async function resolveScope(options) {
14851
15040
  const flagCount = [Boolean(options.project), Boolean(options.workspace), Boolean(options.global)].filter(Boolean).length;
@@ -14857,7 +15046,7 @@ async function resolveScope(options) {
14857
15046
  throw new Error(`Invalid project slug: "${options.project}".`);
14858
15047
  }
14859
15048
  const config = await readConfig();
14860
- const projectMd = resolve36(config.defaultProjectDir, options.project, "project.md");
15049
+ const projectMd = resolve37(config.defaultProjectDir, options.project, "project.md");
14861
15050
  if (!await fileExists(projectMd)) {
14862
15051
  throw new Error(`Project "${options.project}" not found.`);
14863
15052
  }
@@ -15176,10 +15365,10 @@ todoCommand.command("archive").description("Archive completed todos and their lo
15176
15365
  (e) => e.itemIds.every((id) => completedIds.has(id))
15177
15366
  );
15178
15367
  const archFile = archivePath(todosPath, workspace, checklist.archiveInterval);
15179
- await ensureDir(resolve36(todosPath, "archive"));
15368
+ await ensureDir(resolve37(todosPath, "archive"));
15180
15369
  let archContent = "";
15181
15370
  if (await fileExists(archFile)) {
15182
- archContent = await readFile23(archFile, "utf-8");
15371
+ archContent = await readFile24(archFile, "utf-8");
15183
15372
  archContent = archContent.trimEnd() + "\n\n";
15184
15373
  } else {
15185
15374
  archContent = `---
@@ -15352,12 +15541,12 @@ function describeScope(scope) {
15352
15541
  }
15353
15542
  async function injectPromotedTodos(assignmentDir, todos, scopeLabel) {
15354
15543
  const { resolve: resolvePath2 } = await import("path");
15355
- const { readFile: readFile32 } = await import("fs/promises");
15544
+ const { readFile: readFile35 } = await import("fs/promises");
15356
15545
  const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
15357
15546
  const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
15358
15547
  const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
15359
15548
  const assignmentMdPath = resolvePath2(assignmentDir, "assignment.md");
15360
- let content = await readFile32(assignmentMdPath, "utf-8");
15549
+ let content = await readFile35(assignmentMdPath, "utf-8");
15361
15550
  content = appendTodosToAssignmentBody2(
15362
15551
  content,
15363
15552
  todos.map((t) => ({
@@ -15402,13 +15591,13 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
15402
15591
  }
15403
15592
  const planDir = todoPlanDir(todosPath, workspace, id);
15404
15593
  await ensureDir(planDir);
15405
- const { readdir: readdir18 } = await import("fs/promises");
15406
- const existingFiles = (await readdir18(planDir).catch(() => [])).filter(
15594
+ const { readdir: readdir19 } = await import("fs/promises");
15595
+ const existingFiles = (await readdir19(planDir).catch(() => [])).filter(
15407
15596
  (f) => /^plan(?:-v\d+)?\.md$/.test(f)
15408
15597
  );
15409
15598
  let target;
15410
15599
  if (existingFiles.length === 0) {
15411
- target = resolve36(planDir, "plan.md");
15600
+ target = resolve37(planDir, "plan.md");
15412
15601
  } else {
15413
15602
  const versions = /* @__PURE__ */ new Set();
15414
15603
  for (const f of existingFiles) {
@@ -15418,7 +15607,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
15418
15607
  }
15419
15608
  let n = 2;
15420
15609
  while (versions.has(n)) n++;
15421
- target = resolve36(planDir, `plan-v${n}.md`);
15610
+ target = resolve37(planDir, `plan-v${n}.md`);
15422
15611
  }
15423
15612
  if (!await fileExists(target)) {
15424
15613
  const stub = `---
@@ -15488,10 +15677,10 @@ async function moveTodo(id, options) {
15488
15677
  if (await fileExists(newPlanDir)) {
15489
15678
  throw new Error(`Plan directory already exists at target: ${newPlanDir}; refusing to move.`);
15490
15679
  }
15491
- const { rename: rename6, mkdir: mkdir7 } = await import("fs/promises");
15680
+ const { rename: rename7, mkdir: mkdir7 } = await import("fs/promises");
15492
15681
  const { dirname: dirname16 } = await import("path");
15493
15682
  await mkdir7(dirname16(newPlanDir), { recursive: true });
15494
- await rename6(item.planDir, newPlanDir);
15683
+ await rename7(item.planDir, newPlanDir);
15495
15684
  item.planDir = newPlanDir;
15496
15685
  }
15497
15686
  sourceChecklist.items.splice(idx, 1);
@@ -15609,20 +15798,20 @@ backupCommand.command("config").description("Show or update backup configuration
15609
15798
  import { Command as Command3 } from "commander";
15610
15799
 
15611
15800
  // src/utils/doctor/index.ts
15612
- import { fileURLToPath as fileURLToPath7 } from "url";
15613
- import { readFile as readFile27 } from "fs/promises";
15614
- import { dirname as dirname13, join as join5 } from "path";
15801
+ import { fileURLToPath as fileURLToPath6 } from "url";
15802
+ import { readFile as readFile30 } from "fs/promises";
15803
+ import { dirname as dirname13, join as join6 } from "path";
15615
15804
 
15616
15805
  // src/utils/doctor/context.ts
15617
15806
  init_config2();
15618
15807
  init_paths();
15619
15808
  init_fs();
15620
15809
  import Database2 from "better-sqlite3";
15621
- import { resolve as resolve37 } from "path";
15810
+ import { resolve as resolve38 } from "path";
15622
15811
  async function buildCheckContext(cwd = process.cwd()) {
15623
15812
  const config = await readConfig();
15624
15813
  const root = syntaurRoot();
15625
- const dbPath = resolve37(root, "syntaur.db");
15814
+ const dbPath = resolve38(root, "syntaur.db");
15626
15815
  let db2 = null;
15627
15816
  let dbError = null;
15628
15817
  if (await fileExists(dbPath)) {
@@ -15656,10 +15845,10 @@ function closeCheckContext(ctx) {
15656
15845
  // src/utils/doctor/checks/env.ts
15657
15846
  init_fs();
15658
15847
  init_paths();
15659
- import { resolve as resolve38, isAbsolute as isAbsolute5 } from "path";
15660
- import { readFile as readFile24, stat as stat4 } from "fs/promises";
15661
- import { fileURLToPath as fileURLToPath6 } from "url";
15662
- import { dirname as dirname12, join as join4 } from "path";
15848
+ import { resolve as resolve39, isAbsolute as isAbsolute5 } from "path";
15849
+ import { readFile as readFile25, stat as stat3 } from "fs/promises";
15850
+ import { fileURLToPath as fileURLToPath5 } from "url";
15851
+ import { dirname as dirname11, join as join4 } from "path";
15663
15852
  var CATEGORY = "env";
15664
15853
  var syntaurRootExists = {
15665
15854
  id: "env.syntaur-root-exists",
@@ -15667,7 +15856,7 @@ var syntaurRootExists = {
15667
15856
  title: "~/.syntaur/ directory exists",
15668
15857
  async run(ctx) {
15669
15858
  try {
15670
- const s = await stat4(ctx.syntaurRoot);
15859
+ const s = await stat3(ctx.syntaurRoot);
15671
15860
  if (!s.isDirectory()) {
15672
15861
  return err(this, `${ctx.syntaurRoot} exists but is not a directory`, [
15673
15862
  ctx.syntaurRoot
@@ -15697,7 +15886,7 @@ var configValid = {
15697
15886
  category: CATEGORY,
15698
15887
  title: "~/.syntaur/config.md is valid",
15699
15888
  async run(ctx) {
15700
- const configPath = resolve38(ctx.syntaurRoot, "config.md");
15889
+ const configPath = resolve39(ctx.syntaurRoot, "config.md");
15701
15890
  if (!await fileExists(configPath)) {
15702
15891
  return {
15703
15892
  id: this.id,
@@ -15714,7 +15903,7 @@ var configValid = {
15714
15903
  autoFixable: false
15715
15904
  };
15716
15905
  }
15717
- const content = await readFile24(configPath, "utf-8");
15906
+ const content = await readFile25(configPath, "utf-8");
15718
15907
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
15719
15908
  if (!fmMatch || fmMatch[1].trim() === "") {
15720
15909
  return {
@@ -15989,15 +16178,15 @@ async function readLocalVersion() {
15989
16178
  }
15990
16179
  async function readLocalPkg() {
15991
16180
  try {
15992
- const here = fileURLToPath6(import.meta.url);
15993
- let dir = dirname12(here);
16181
+ const here = fileURLToPath5(import.meta.url);
16182
+ let dir = dirname11(here);
15994
16183
  for (let i = 0; i < 6; i++) {
15995
16184
  const candidate = join4(dir, "package.json");
15996
16185
  try {
15997
- const text = await readFile24(candidate, "utf-8");
16186
+ const text = await readFile25(candidate, "utf-8");
15998
16187
  return JSON.parse(text);
15999
16188
  } catch {
16000
- dir = dirname12(dir);
16189
+ dir = dirname11(dir);
16001
16190
  }
16002
16191
  }
16003
16192
  return null;
@@ -16046,8 +16235,8 @@ function versionGte(a, b) {
16046
16235
 
16047
16236
  // src/utils/doctor/checks/structure.ts
16048
16237
  init_fs();
16049
- import { resolve as resolve39 } from "path";
16050
- import { readdir as readdir13, stat as stat5 } from "fs/promises";
16238
+ import { resolve as resolve40 } from "path";
16239
+ import { readdir as readdir13, stat as stat4 } from "fs/promises";
16051
16240
  var CATEGORY2 = "structure";
16052
16241
  var KNOWN_TOP_LEVEL = /* @__PURE__ */ new Set([
16053
16242
  "projects",
@@ -16066,7 +16255,7 @@ var projectsDir = {
16066
16255
  category: CATEGORY2,
16067
16256
  title: "projects/ directory exists",
16068
16257
  async run(ctx) {
16069
- const p = resolve39(ctx.syntaurRoot, "projects");
16258
+ const p = resolve40(ctx.syntaurRoot, "projects");
16070
16259
  if (!await fileExists(p)) {
16071
16260
  return {
16072
16261
  id: this.id,
@@ -16091,7 +16280,7 @@ var playbooksDir2 = {
16091
16280
  category: CATEGORY2,
16092
16281
  title: "playbooks/ directory exists",
16093
16282
  async run(ctx) {
16094
- const p = resolve39(ctx.syntaurRoot, "playbooks");
16283
+ const p = resolve40(ctx.syntaurRoot, "playbooks");
16095
16284
  if (!await fileExists(p)) {
16096
16285
  return {
16097
16286
  id: this.id,
@@ -16116,7 +16305,7 @@ var todosDirValid = {
16116
16305
  category: CATEGORY2,
16117
16306
  title: "todos/ directory is readable (if present)",
16118
16307
  async run(ctx) {
16119
- const p = resolve39(ctx.syntaurRoot, "todos");
16308
+ const p = resolve40(ctx.syntaurRoot, "todos");
16120
16309
  if (!await fileExists(p)) {
16121
16310
  return {
16122
16311
  id: this.id,
@@ -16127,7 +16316,7 @@ var todosDirValid = {
16127
16316
  autoFixable: false
16128
16317
  };
16129
16318
  }
16130
- const s = await stat5(p);
16319
+ const s = await stat4(p);
16131
16320
  if (!s.isDirectory()) {
16132
16321
  return {
16133
16322
  id: this.id,
@@ -16147,7 +16336,7 @@ var serversDirValid = {
16147
16336
  category: CATEGORY2,
16148
16337
  title: "servers/ directory is readable (if present)",
16149
16338
  async run(ctx) {
16150
- const p = resolve39(ctx.syntaurRoot, "servers");
16339
+ const p = resolve40(ctx.syntaurRoot, "servers");
16151
16340
  if (!await fileExists(p)) {
16152
16341
  return {
16153
16342
  id: this.id,
@@ -16158,7 +16347,7 @@ var serversDirValid = {
16158
16347
  autoFixable: false
16159
16348
  };
16160
16349
  }
16161
- const s = await stat5(p);
16350
+ const s = await stat4(p);
16162
16351
  if (!s.isDirectory()) {
16163
16352
  return {
16164
16353
  id: this.id,
@@ -16192,7 +16381,7 @@ var knownFilesRecognized = {
16192
16381
  title: this.title,
16193
16382
  status: "warn",
16194
16383
  detail: `unexpected top-level entries: ${unexpected.join(", ")}`,
16195
- affected: unexpected.map((n) => resolve39(ctx.syntaurRoot, n)),
16384
+ affected: unexpected.map((n) => resolve40(ctx.syntaurRoot, n)),
16196
16385
  remediation: {
16197
16386
  kind: "manual",
16198
16387
  suggestion: "Review these entries \u2014 they may be leftover state from older versions",
@@ -16221,8 +16410,8 @@ function pass2(check) {
16221
16410
 
16222
16411
  // src/utils/doctor/checks/project.ts
16223
16412
  init_fs();
16224
- import { resolve as resolve40 } from "path";
16225
- import { readdir as readdir14, stat as stat6 } from "fs/promises";
16413
+ import { resolve as resolve41 } from "path";
16414
+ import { readdir as readdir14, stat as stat5 } from "fs/promises";
16226
16415
  var CATEGORY3 = "project";
16227
16416
  var REQUIRED_PROJECT_FILES = [
16228
16417
  "project.md",
@@ -16251,10 +16440,10 @@ async function listProjects2(ctx) {
16251
16440
  for (const e of entries) {
16252
16441
  if (!e.isDirectory()) continue;
16253
16442
  if (e.name.startsWith(".") || e.name.startsWith("_")) continue;
16254
- const projectDir = resolve40(dir, e.name);
16443
+ const projectDir = resolve41(dir, e.name);
16255
16444
  let looksLikeProject = false;
16256
16445
  for (const marker of PROJECT_MARKERS) {
16257
- if (await fileExists(resolve40(projectDir, marker))) {
16446
+ if (await fileExists(resolve41(projectDir, marker))) {
16258
16447
  looksLikeProject = true;
16259
16448
  break;
16260
16449
  }
@@ -16273,7 +16462,7 @@ var requiredFiles = {
16273
16462
  for (const projectDir of projects) {
16274
16463
  const missing = [];
16275
16464
  for (const rel of REQUIRED_PROJECT_FILES) {
16276
- const p = resolve40(projectDir, rel);
16465
+ const p = resolve41(projectDir, rel);
16277
16466
  if (!await fileExists(p)) missing.push(rel);
16278
16467
  }
16279
16468
  if (missing.length === 0) continue;
@@ -16283,7 +16472,7 @@ var requiredFiles = {
16283
16472
  title: this.title,
16284
16473
  status: "error",
16285
16474
  detail: `project at ${projectDir} is missing: ${missing.join(", ")}`,
16286
- affected: missing.map((m) => resolve40(projectDir, m)),
16475
+ affected: missing.map((m) => resolve41(projectDir, m)),
16287
16476
  remediation: {
16288
16477
  kind: "manual",
16289
16478
  suggestion: "Recreate the missing scaffold files from templates",
@@ -16306,9 +16495,9 @@ var manifestStale = {
16306
16495
  const projects = await listProjects2(ctx);
16307
16496
  const results = [];
16308
16497
  for (const projectDir of projects) {
16309
- const manifestPath = resolve40(projectDir, "manifest.md");
16498
+ const manifestPath = resolve41(projectDir, "manifest.md");
16310
16499
  if (!await fileExists(manifestPath)) continue;
16311
- const manifestMtime = (await stat6(manifestPath)).mtimeMs;
16500
+ const manifestMtime = (await stat5(manifestPath)).mtimeMs;
16312
16501
  const newestAssignment = await newestAssignmentMtime(projectDir);
16313
16502
  if (newestAssignment === 0) continue;
16314
16503
  if (newestAssignment > manifestMtime) {
@@ -16355,7 +16544,7 @@ var orphanFiles = {
16355
16544
  title: this.title,
16356
16545
  status: "warn",
16357
16546
  detail: `project at ${projectDir} has unexpected entries: ${orphans.join(", ")}`,
16358
- affected: orphans.map((o) => resolve40(projectDir, o)),
16547
+ affected: orphans.map((o) => resolve41(projectDir, o)),
16359
16548
  autoFixable: false
16360
16549
  });
16361
16550
  }
@@ -16365,7 +16554,7 @@ var orphanFiles = {
16365
16554
  };
16366
16555
  var projectChecks = [requiredFiles, manifestStale, orphanFiles];
16367
16556
  async function newestAssignmentMtime(projectDir) {
16368
- const assignmentsRoot = resolve40(projectDir, "assignments");
16557
+ const assignmentsRoot = resolve41(projectDir, "assignments");
16369
16558
  if (!await fileExists(assignmentsRoot)) return 0;
16370
16559
  let newest = 0;
16371
16560
  let entries;
@@ -16376,9 +16565,9 @@ async function newestAssignmentMtime(projectDir) {
16376
16565
  }
16377
16566
  for (const e of entries) {
16378
16567
  if (!e.isDirectory()) continue;
16379
- const assignmentMd = resolve40(assignmentsRoot, e.name, "assignment.md");
16568
+ const assignmentMd = resolve41(assignmentsRoot, e.name, "assignment.md");
16380
16569
  try {
16381
- const s = await stat6(assignmentMd);
16570
+ const s = await stat5(assignmentMd);
16382
16571
  if (s.mtimeMs > newest) newest = s.mtimeMs;
16383
16572
  } catch {
16384
16573
  }
@@ -16400,8 +16589,8 @@ init_fs();
16400
16589
  init_parser();
16401
16590
  init_types();
16402
16591
  init_paths();
16403
- import { resolve as resolve41 } from "path";
16404
- import { readdir as readdir15, readFile as readFile25 } from "fs/promises";
16592
+ import { resolve as resolve42 } from "path";
16593
+ import { readdir as readdir15, readFile as readFile26 } from "fs/promises";
16405
16594
  var CATEGORY4 = "assignment";
16406
16595
  var STATUSES_REQUIRING_HANDOFF = /* @__PURE__ */ new Set(["review", "completed"]);
16407
16596
  async function listAssignments(ctx) {
@@ -16412,16 +16601,16 @@ async function listAssignments(ctx) {
16412
16601
  for (const m of projects) {
16413
16602
  if (!m.isDirectory()) continue;
16414
16603
  if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
16415
- const assignmentsDir2 = resolve41(projectsDir2, m.name, "assignments");
16604
+ const assignmentsDir2 = resolve42(projectsDir2, m.name, "assignments");
16416
16605
  if (!await fileExists(assignmentsDir2)) continue;
16417
16606
  const entries = await readdir15(assignmentsDir2, { withFileTypes: true });
16418
16607
  for (const a of entries) {
16419
16608
  if (!a.isDirectory()) continue;
16420
16609
  if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
16421
- const assignmentDir = resolve41(assignmentsDir2, a.name);
16422
- const assignmentMd = resolve41(assignmentDir, "assignment.md");
16610
+ const assignmentDir = resolve42(assignmentsDir2, a.name);
16611
+ const assignmentMd = resolve42(assignmentDir, "assignment.md");
16423
16612
  const entry = {
16424
- projectDir: resolve41(projectsDir2, m.name),
16613
+ projectDir: resolve42(projectsDir2, m.name),
16425
16614
  projectSlug: m.name,
16426
16615
  assignmentDir,
16427
16616
  assignmentSlug: a.name,
@@ -16441,8 +16630,8 @@ async function listAssignments(ctx) {
16441
16630
  for (const a of entries) {
16442
16631
  if (!a.isDirectory()) continue;
16443
16632
  if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
16444
- const assignmentDir = resolve41(standaloneRoot, a.name);
16445
- const assignmentMd = resolve41(assignmentDir, "assignment.md");
16633
+ const assignmentDir = resolve42(standaloneRoot, a.name);
16634
+ const assignmentMd = resolve42(assignmentDir, "assignment.md");
16446
16635
  const entry = {
16447
16636
  projectDir: standaloneRoot,
16448
16637
  projectSlug: null,
@@ -16520,7 +16709,7 @@ var invalidStatus = {
16520
16709
  const allowed = configuredStatuses(ctx);
16521
16710
  const results = [];
16522
16711
  for (const a of withAssignmentMd) {
16523
- const path = resolve41(a.assignmentDir, "assignment.md");
16712
+ const path = resolve42(a.assignmentDir, "assignment.md");
16524
16713
  const parsed = await parseSafe(path);
16525
16714
  if (!parsed) continue;
16526
16715
  if (!allowed.has(parsed.status)) {
@@ -16553,7 +16742,7 @@ var workspaceMissing = {
16553
16742
  const terminal = terminalStatuses(ctx);
16554
16743
  const results = [];
16555
16744
  for (const a of withAssignmentMd) {
16556
- const path = resolve41(a.assignmentDir, "assignment.md");
16745
+ const path = resolve42(a.assignmentDir, "assignment.md");
16557
16746
  const parsed = await parseSafe(path);
16558
16747
  if (!parsed) continue;
16559
16748
  if (terminal.has(parsed.status)) continue;
@@ -16600,12 +16789,12 @@ var requiredFilesByStatus = {
16600
16789
  const { withAssignmentMd } = await listAssignments(ctx);
16601
16790
  const results = [];
16602
16791
  for (const a of withAssignmentMd) {
16603
- const assignmentPath = resolve41(a.assignmentDir, "assignment.md");
16792
+ const assignmentPath = resolve42(a.assignmentDir, "assignment.md");
16604
16793
  const parsed = await parseSafe(assignmentPath);
16605
16794
  if (!parsed) continue;
16606
16795
  const missing = [];
16607
16796
  if (STATUSES_REQUIRING_HANDOFF.has(parsed.status)) {
16608
- const handoffPath = resolve41(a.assignmentDir, "handoff.md");
16797
+ const handoffPath = resolve42(a.assignmentDir, "handoff.md");
16609
16798
  if (!await fileExists(handoffPath)) missing.push("handoff.md");
16610
16799
  }
16611
16800
  if (missing.length === 0) continue;
@@ -16615,7 +16804,7 @@ var requiredFilesByStatus = {
16615
16804
  title: this.title,
16616
16805
  status: "warn",
16617
16806
  detail: `${a.projectSlug}/${a.assignmentSlug} (status: ${parsed.status}) is missing ${missing.join(", ")}`,
16618
- affected: missing.map((m) => resolve41(a.assignmentDir, m)),
16807
+ affected: missing.map((m) => resolve42(a.assignmentDir, m)),
16619
16808
  remediation: {
16620
16809
  kind: "manual",
16621
16810
  suggestion: `Create the missing ${missing.join(" and ")} files for this assignment`,
@@ -16638,7 +16827,7 @@ var companionFilesScaffolded = {
16638
16827
  for (const a of withAssignmentMd) {
16639
16828
  const missing = [];
16640
16829
  for (const filename of ["progress.md", "comments.md"]) {
16641
- if (!await fileExists(resolve41(a.assignmentDir, filename))) {
16830
+ if (!await fileExists(resolve42(a.assignmentDir, filename))) {
16642
16831
  missing.push(filename);
16643
16832
  }
16644
16833
  }
@@ -16650,7 +16839,7 @@ var companionFilesScaffolded = {
16650
16839
  title: this.title,
16651
16840
  status: "warn",
16652
16841
  detail: `${label} is missing ${missing.join(" and ")} (pre-v2.0 assignment \u2014 not required, but scaffolding them keeps the dashboard and CLIs consistent)`,
16653
- affected: missing.map((m) => resolve41(a.assignmentDir, m)),
16842
+ affected: missing.map((m) => resolve42(a.assignmentDir, m)),
16654
16843
  remediation: {
16655
16844
  kind: "manual",
16656
16845
  suggestion: `Create ${missing.join(" and ")} with the renderProgress/renderComments templates, or re-scaffold via the CLI`,
@@ -16683,7 +16872,7 @@ var typeDefinition = {
16683
16872
  const { withAssignmentMd } = await listAssignments(ctx);
16684
16873
  const results = [];
16685
16874
  for (const a of withAssignmentMd) {
16686
- const path = resolve41(a.assignmentDir, "assignment.md");
16875
+ const path = resolve42(a.assignmentDir, "assignment.md");
16687
16876
  const parsed = await parseSafe(path);
16688
16877
  if (!parsed) continue;
16689
16878
  if (!parsed.type) continue;
@@ -16717,7 +16906,7 @@ var projectFrontmatterMatchesContainer = {
16717
16906
  const { withAssignmentMd } = await listAssignments(ctx);
16718
16907
  const results = [];
16719
16908
  for (const a of withAssignmentMd) {
16720
- const path = resolve41(a.assignmentDir, "assignment.md");
16909
+ const path = resolve42(a.assignmentDir, "assignment.md");
16721
16910
  const parsed = await parseSafe(path);
16722
16911
  if (!parsed) continue;
16723
16912
  if (a.standalone) {
@@ -16772,7 +16961,7 @@ var assignmentChecks = [
16772
16961
  ];
16773
16962
  async function parseSafe(path) {
16774
16963
  try {
16775
- const content = await readFile25(path, "utf-8");
16964
+ const content = await readFile26(path, "utf-8");
16776
16965
  return parseAssignmentFull(content);
16777
16966
  } catch {
16778
16967
  return null;
@@ -16791,7 +16980,7 @@ function pass4(check, detail) {
16791
16980
 
16792
16981
  // src/utils/doctor/checks/dashboard.ts
16793
16982
  init_fs();
16794
- import { resolve as resolve42 } from "path";
16983
+ import { resolve as resolve43 } from "path";
16795
16984
  var CATEGORY5 = "dashboard";
16796
16985
  var dbReachable = {
16797
16986
  id: "dashboard.db-reachable",
@@ -16805,7 +16994,7 @@ var dbReachable = {
16805
16994
  title: this.title,
16806
16995
  status: "error",
16807
16996
  detail: `could not open syntaur.db: ${ctx.dbError ?? "unknown error"}`,
16808
- affected: [resolve42(ctx.syntaurRoot, "syntaur.db")],
16997
+ affected: [resolve43(ctx.syntaurRoot, "syntaur.db")],
16809
16998
  remediation: {
16810
16999
  kind: "manual",
16811
17000
  suggestion: "Start the dashboard once (`syntaur dashboard`) to initialize the DB, or restore it from backup",
@@ -16823,7 +17012,7 @@ var dbReachable = {
16823
17012
  title: this.title,
16824
17013
  status: "error",
16825
17014
  detail: 'syntaur.db is missing the expected "sessions" table',
16826
- affected: [resolve42(ctx.syntaurRoot, "syntaur.db")],
17015
+ affected: [resolve43(ctx.syntaurRoot, "syntaur.db")],
16827
17016
  autoFixable: false
16828
17017
  };
16829
17018
  }
@@ -16835,7 +17024,7 @@ var dbReachable = {
16835
17024
  title: this.title,
16836
17025
  status: "error",
16837
17026
  detail: `syntaur.db query failed: ${err2 instanceof Error ? err2.message : String(err2)}`,
16838
- affected: [resolve42(ctx.syntaurRoot, "syntaur.db")],
17027
+ affected: [resolve43(ctx.syntaurRoot, "syntaur.db")],
16839
17028
  autoFixable: false
16840
17029
  };
16841
17030
  }
@@ -16861,7 +17050,7 @@ var ghostSessions = {
16861
17050
  const results = [];
16862
17051
  for (const row of rows) {
16863
17052
  if (!row.project_slug) continue;
16864
- const projectPath = resolve42(projectsDir2, row.project_slug, "project.md");
17053
+ const projectPath = resolve43(projectsDir2, row.project_slug, "project.md");
16865
17054
  if (!await fileExists(projectPath)) {
16866
17055
  results.push({
16867
17056
  id: this.id,
@@ -16880,7 +17069,7 @@ var ghostSessions = {
16880
17069
  continue;
16881
17070
  }
16882
17071
  if (row.assignment_slug) {
16883
- const assignmentPath = resolve42(
17072
+ const assignmentPath = resolve43(
16884
17073
  projectsDir2,
16885
17074
  row.project_slug,
16886
17075
  "assignments",
@@ -16932,7 +17121,9 @@ function skipped(check, reason) {
16932
17121
 
16933
17122
  // src/utils/doctor/checks/integrations.ts
16934
17123
  init_fs();
16935
- import { readdir as readdir16 } from "fs/promises";
17124
+ import { resolve as resolve44, dirname as dirname12, basename as basename2 } from "path";
17125
+ import { readdir as readdir16, readFile as readFile27 } from "fs/promises";
17126
+ import { homedir as homedir6 } from "os";
16936
17127
  var CATEGORY6 = "integrations";
16937
17128
  var claudePluginLinked = {
16938
17129
  id: "integrations.claude-plugin-linked",
@@ -17012,7 +17203,111 @@ var backupConfigured = {
17012
17203
  };
17013
17204
  }
17014
17205
  };
17015
- var integrationChecks = [claudePluginLinked, codexPluginLinked, backupConfigured];
17206
+ async function readKnownMarketplaces() {
17207
+ const path = resolve44(homedir6(), ".claude", "plugins", "known_marketplaces.json");
17208
+ if (!await fileExists(path)) return {};
17209
+ try {
17210
+ const raw = await readFile27(path, "utf-8");
17211
+ return JSON.parse(raw);
17212
+ } catch {
17213
+ return {};
17214
+ }
17215
+ }
17216
+ var claudeMarketplaceRegistered = {
17217
+ id: "integrations.claude-marketplace-registered",
17218
+ category: CATEGORY6,
17219
+ title: "Claude marketplace containing syntaur is registered in known_marketplaces.json",
17220
+ async run(ctx) {
17221
+ const dir = ctx.config.integrations.claudePluginDir;
17222
+ if (!dir) return skipped2(this, "claudePluginDir not configured");
17223
+ if (!await fileExists(dir)) {
17224
+ return skipped2(this, "claudePluginDir does not exist (run install-plugin)");
17225
+ }
17226
+ const pluginsParent = dirname12(dir);
17227
+ if (basename2(pluginsParent) !== "plugins") {
17228
+ return {
17229
+ id: this.id,
17230
+ category: this.category,
17231
+ title: this.title,
17232
+ status: "error",
17233
+ detail: `claudePluginDir is ${dir}, which is not under a Claude marketplace (expected path of the form <marketplace-root>/plugins/syntaur). Claude Code will not show this plugin until it lives inside a marketplace.`,
17234
+ affected: [dir],
17235
+ remediation: {
17236
+ kind: "manual",
17237
+ suggestion: "Re-run install-plugin to relocate into a marketplace, or pass --target-dir <marketplace>/plugins/syntaur.",
17238
+ command: "syntaur install-plugin"
17239
+ },
17240
+ autoFixable: false
17241
+ };
17242
+ }
17243
+ const marketplaceRoot = dirname12(pluginsParent);
17244
+ const marketplaceManifest = resolve44(marketplaceRoot, ".claude-plugin", "marketplace.json");
17245
+ if (!await fileExists(marketplaceManifest)) {
17246
+ return {
17247
+ id: this.id,
17248
+ category: this.category,
17249
+ title: this.title,
17250
+ status: "error",
17251
+ detail: `${marketplaceManifest} does not exist \u2014 Claude won't see this plugin.`,
17252
+ affected: [marketplaceManifest],
17253
+ remediation: {
17254
+ kind: "manual",
17255
+ suggestion: "Re-run install-plugin to repair the marketplace files.",
17256
+ command: "syntaur install-plugin"
17257
+ },
17258
+ autoFixable: false
17259
+ };
17260
+ }
17261
+ let parsed = {};
17262
+ try {
17263
+ parsed = JSON.parse(await readFile27(marketplaceManifest, "utf-8"));
17264
+ } catch {
17265
+ return {
17266
+ id: this.id,
17267
+ category: this.category,
17268
+ title: this.title,
17269
+ status: "error",
17270
+ detail: `${marketplaceManifest} is not valid JSON.`,
17271
+ affected: [marketplaceManifest],
17272
+ autoFixable: false
17273
+ };
17274
+ }
17275
+ const marketplaceName = parsed.name ?? basename2(marketplaceRoot);
17276
+ const hasSyntaurEntry = (parsed.plugins ?? []).some((p) => p?.name === "syntaur");
17277
+ const known = await readKnownMarketplaces();
17278
+ const registered = known[marketplaceName]?.installLocation === marketplaceRoot || Object.values(known).some((v) => v.installLocation === marketplaceRoot);
17279
+ const issues = [];
17280
+ if (!hasSyntaurEntry) {
17281
+ issues.push(`marketplace.json at ${marketplaceManifest} does not list a "syntaur" plugin`);
17282
+ }
17283
+ if (!registered) {
17284
+ issues.push(
17285
+ `known_marketplaces.json does not register ${marketplaceName} \u2192 ${marketplaceRoot} (Claude will not show this plugin)`
17286
+ );
17287
+ }
17288
+ if (issues.length === 0) return pass6(this);
17289
+ return {
17290
+ id: this.id,
17291
+ category: this.category,
17292
+ title: this.title,
17293
+ status: "error",
17294
+ detail: issues.join("; "),
17295
+ affected: [marketplaceManifest, resolve44(homedir6(), ".claude", "plugins", "known_marketplaces.json")],
17296
+ remediation: {
17297
+ kind: "manual",
17298
+ suggestion: "Re-run install-plugin to ensure both files are in sync.",
17299
+ command: "syntaur install-plugin"
17300
+ },
17301
+ autoFixable: false
17302
+ };
17303
+ }
17304
+ };
17305
+ var integrationChecks = [
17306
+ claudePluginLinked,
17307
+ claudeMarketplaceRegistered,
17308
+ codexPluginLinked,
17309
+ backupConfigured
17310
+ ];
17016
17311
  function pass6(check) {
17017
17312
  return {
17018
17313
  id: check.id,
@@ -17037,8 +17332,8 @@ function skipped2(check, reason) {
17037
17332
  init_fs();
17038
17333
  init_parser();
17039
17334
  init_types();
17040
- import { resolve as resolve43 } from "path";
17041
- import { readFile as readFile26 } from "fs/promises";
17335
+ import { resolve as resolve45 } from "path";
17336
+ import { readFile as readFile28 } from "fs/promises";
17042
17337
  var CATEGORY7 = "workspace";
17043
17338
  var ASSIGNMENT_FIELDS = ["projectSlug", "assignmentSlug", "projectDir", "assignmentDir"];
17044
17339
  function hasAnyAssignmentField(ctx) {
@@ -17050,12 +17345,12 @@ function isStandaloneSession(ctx) {
17050
17345
  return !hasAnyAssignmentField(ctx) && typeof ctx.sessionId === "string" && ctx.sessionId.length > 0;
17051
17346
  }
17052
17347
  async function loadContext(ctx) {
17053
- const path = resolve43(ctx.cwd, ".syntaur", "context.json");
17348
+ const path = resolve45(ctx.cwd, ".syntaur", "context.json");
17054
17349
  if (!await fileExists(path)) {
17055
17350
  return { data: null, path, exists: false, parseError: null };
17056
17351
  }
17057
17352
  try {
17058
- const raw = await readFile26(path, "utf-8");
17353
+ const raw = await readFile28(path, "utf-8");
17059
17354
  return { data: JSON.parse(raw), path, exists: true, parseError: null };
17060
17355
  } catch (err2) {
17061
17356
  return {
@@ -17130,7 +17425,7 @@ var contextAssignmentResolves = {
17130
17425
  if (!exists) return skipped3(this, "no context to resolve");
17131
17426
  if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to resolve");
17132
17427
  if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
17133
- const assignmentMd = resolve43(data.assignmentDir, "assignment.md");
17428
+ const assignmentMd = resolve45(data.assignmentDir, "assignment.md");
17134
17429
  if (!await fileExists(assignmentMd)) {
17135
17430
  return {
17136
17431
  id: this.id,
@@ -17159,10 +17454,10 @@ var contextTerminal = {
17159
17454
  if (!exists) return skipped3(this, "no context to check");
17160
17455
  if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to check");
17161
17456
  if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
17162
- const assignmentMd = resolve43(data.assignmentDir, "assignment.md");
17457
+ const assignmentMd = resolve45(data.assignmentDir, "assignment.md");
17163
17458
  if (!await fileExists(assignmentMd)) return skipped3(this, "assignment file missing");
17164
17459
  try {
17165
- const content = await readFile26(assignmentMd, "utf-8");
17460
+ const content = await readFile28(assignmentMd, "utf-8");
17166
17461
  const parsed = parseAssignmentFull(content);
17167
17462
  const terminal = terminalStatuses2(ctx);
17168
17463
  if (terminal.has(parsed.status)) {
@@ -17305,6 +17600,86 @@ async function checkAgent(agent) {
17305
17600
  }
17306
17601
  var agentChecks = [agentsResolvable];
17307
17602
 
17603
+ // src/utils/doctor/checks/skills.ts
17604
+ init_fs();
17605
+ import { resolve as resolve46, join as join5 } from "path";
17606
+ import { readdir as readdir17, readFile as readFile29, lstat as lstat4 } from "fs/promises";
17607
+ import { homedir as homedir7 } from "os";
17608
+ var CATEGORY9 = "skills";
17609
+ var skillTargets = [
17610
+ { agent: "claude", dir: resolve46(homedir7(), ".claude", "skills"), label: "~/.claude/skills" },
17611
+ { agent: "codex", dir: resolve46(homedir7(), ".codex", "skills"), label: "~/.codex/skills" }
17612
+ ];
17613
+ var skillsDedupCheck = {
17614
+ id: "skills.dedup",
17615
+ category: CATEGORY9,
17616
+ title: "Syntaur skills are not duplicated across install paths",
17617
+ async run() {
17618
+ const findings = [];
17619
+ const affected = [];
17620
+ for (const { agent, dir, label } of skillTargets) {
17621
+ if (!await fileExists(dir)) continue;
17622
+ const pluginEnabled = await isSyntaurPluginEnabledFor(agent);
17623
+ const present = [];
17624
+ let entries;
17625
+ try {
17626
+ entries = await readdir17(dir, { withFileTypes: true });
17627
+ } catch {
17628
+ continue;
17629
+ }
17630
+ for (const entry of entries) {
17631
+ if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
17632
+ if (!KNOWN_SKILLS.includes(entry.name)) continue;
17633
+ const skillMd = join5(dir, entry.name, "SKILL.md");
17634
+ if (!await fileExists(skillMd)) continue;
17635
+ const content = await readFile29(skillMd, "utf-8").catch(() => "");
17636
+ const match = content.match(/^name:\s*(\S+)\s*$/m);
17637
+ if (!match || match[1] !== entry.name) continue;
17638
+ let isSymlink2 = false;
17639
+ try {
17640
+ isSymlink2 = (await lstat4(join5(dir, entry.name))).isSymbolicLink();
17641
+ } catch {
17642
+ }
17643
+ present.push({ name: entry.name, isSymlink: isSymlink2 });
17644
+ }
17645
+ if (present.length === 0) continue;
17646
+ if (pluginEnabled) {
17647
+ const nonSymlink = present.filter((p) => !p.isSymlink);
17648
+ if (nonSymlink.length > 0) {
17649
+ findings.push(
17650
+ `${label}: ${nonSymlink.length} syntaur skill(s) installed globally while the syntaur plugin is enabled (${agent}) \u2014 duplicate registrations`
17651
+ );
17652
+ for (const p of nonSymlink) affected.push(join5(dir, p.name));
17653
+ }
17654
+ }
17655
+ }
17656
+ if (findings.length === 0) {
17657
+ return {
17658
+ id: this.id,
17659
+ category: this.category,
17660
+ title: this.title,
17661
+ status: "pass",
17662
+ autoFixable: false
17663
+ };
17664
+ }
17665
+ return {
17666
+ id: this.id,
17667
+ category: this.category,
17668
+ title: this.title,
17669
+ status: "warn",
17670
+ detail: findings.join("; "),
17671
+ affected,
17672
+ remediation: {
17673
+ kind: "manual",
17674
+ suggestion: "Either disable the plugin or remove the global skill copies. Recommended: keep the plugin path and remove the global copies via `syntaur uninstall --skills` (preserves user-authored skills with non-syntaur frontmatter).",
17675
+ command: null
17676
+ },
17677
+ autoFixable: false
17678
+ };
17679
+ }
17680
+ };
17681
+ var skillsChecks = [skillsDedupCheck];
17682
+
17308
17683
  // src/utils/doctor/registry.ts
17309
17684
  function allChecks() {
17310
17685
  return [
@@ -17315,7 +17690,8 @@ function allChecks() {
17315
17690
  ...dashboardChecks,
17316
17691
  ...integrationChecks,
17317
17692
  ...workspaceChecks,
17318
- ...agentChecks
17693
+ ...agentChecks,
17694
+ ...skillsChecks
17319
17695
  ];
17320
17696
  }
17321
17697
 
@@ -17397,11 +17773,11 @@ async function finalize(checks) {
17397
17773
  }
17398
17774
  async function readVersion() {
17399
17775
  try {
17400
- const here = fileURLToPath7(import.meta.url);
17776
+ const here = fileURLToPath6(import.meta.url);
17401
17777
  let dir = dirname13(here);
17402
17778
  for (let i = 0; i < 6; i++) {
17403
17779
  try {
17404
- const raw = await readFile27(join5(dir, "package.json"), "utf-8");
17780
+ const raw = await readFile30(join6(dir, "package.json"), "utf-8");
17405
17781
  const parsed = JSON.parse(raw);
17406
17782
  return typeof parsed.version === "string" ? parsed.version : null;
17407
17783
  } catch {
@@ -17749,8 +18125,8 @@ init_uuid();
17749
18125
  init_timestamp();
17750
18126
  init_assignment_resolver();
17751
18127
  init_templates();
17752
- import { resolve as resolve44 } from "path";
17753
- import { readFile as readFile28 } from "fs/promises";
18128
+ import { resolve as resolve47 } from "path";
18129
+ import { readFile as readFile31 } from "fs/promises";
17754
18130
  function shortId() {
17755
18131
  return generateId().split("-")[0];
17756
18132
  }
@@ -17780,7 +18156,7 @@ async function commentCommand(target, text, options = {}) {
17780
18156
  if (!isValidSlug(target)) {
17781
18157
  throw new Error(`Invalid assignment slug "${target}".`);
17782
18158
  }
17783
- assignmentDir = resolve44(baseDir, options.project, "assignments", target);
18159
+ assignmentDir = resolve47(baseDir, options.project, "assignments", target);
17784
18160
  assignmentRef = target;
17785
18161
  } else {
17786
18162
  const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
@@ -17790,13 +18166,13 @@ async function commentCommand(target, text, options = {}) {
17790
18166
  assignmentDir = resolved.assignmentDir;
17791
18167
  assignmentRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
17792
18168
  }
17793
- const commentsPath = resolve44(assignmentDir, "comments.md");
18169
+ const commentsPath = resolve47(assignmentDir, "comments.md");
17794
18170
  const timestamp = nowTimestamp();
17795
18171
  const author = options.author ?? process.env.USER ?? "unknown";
17796
18172
  let currentContent;
17797
18173
  let currentCount = 0;
17798
18174
  if (await fileExists(commentsPath)) {
17799
- currentContent = await readFile28(commentsPath, "utf-8");
18175
+ currentContent = await readFile31(commentsPath, "utf-8");
17800
18176
  const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
17801
18177
  if (countMatch) currentCount = parseInt(countMatch[1], 10);
17802
18178
  } else {
@@ -17837,8 +18213,8 @@ init_slug();
17837
18213
  init_timestamp();
17838
18214
  init_assignment_resolver();
17839
18215
  init_assignment_todos();
17840
- import { resolve as resolve45 } from "path";
17841
- import { readFile as readFile29 } from "fs/promises";
18216
+ import { resolve as resolve48 } from "path";
18217
+ import { readFile as readFile32 } from "fs/promises";
17842
18218
  async function requestCommand(target, text, options = {}) {
17843
18219
  if (!text || !text.trim()) {
17844
18220
  throw new Error("Request text cannot be empty.");
@@ -17854,7 +18230,7 @@ async function requestCommand(target, text, options = {}) {
17854
18230
  if (!isValidSlug(target)) {
17855
18231
  throw new Error(`Invalid assignment slug "${target}".`);
17856
18232
  }
17857
- assignmentDir = resolve45(baseDir, options.project, "assignments", target);
18233
+ assignmentDir = resolve48(baseDir, options.project, "assignments", target);
17858
18234
  targetRef = target;
17859
18235
  } else {
17860
18236
  const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
@@ -17864,12 +18240,12 @@ async function requestCommand(target, text, options = {}) {
17864
18240
  assignmentDir = resolved.assignmentDir;
17865
18241
  targetRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
17866
18242
  }
17867
- const assignmentMdPath = resolve45(assignmentDir, "assignment.md");
18243
+ const assignmentMdPath = resolve48(assignmentDir, "assignment.md");
17868
18244
  if (!await fileExists(assignmentMdPath)) {
17869
18245
  throw new Error(`assignment.md not found at ${assignmentMdPath}`);
17870
18246
  }
17871
18247
  const source = options.from ?? process.env.SYNTAUR_ASSIGNMENT ?? "unknown";
17872
- let content = await readFile29(assignmentMdPath, "utf-8");
18248
+ let content = await readFile32(assignmentMdPath, "utf-8");
17873
18249
  content = appendTodosToAssignmentBody(content, [
17874
18250
  { description: `${text.trim()} (from: ${source})` }
17875
18251
  ]);
@@ -17880,10 +18256,10 @@ async function requestCommand(target, text, options = {}) {
17880
18256
 
17881
18257
  // src/cli-default-command.ts
17882
18258
  init_config2();
17883
- import { readdir as readdir17 } from "fs/promises";
18259
+ import { readdir as readdir18 } from "fs/promises";
17884
18260
  async function hasAnyProjectContent(projectsDir2) {
17885
18261
  try {
17886
- const entries = await readdir17(projectsDir2, { withFileTypes: true });
18262
+ const entries = await readdir18(projectsDir2, { withFileTypes: true });
17887
18263
  return entries.some((entry) => entry.isDirectory());
17888
18264
  } catch {
17889
18265
  return false;
@@ -17919,21 +18295,21 @@ async function getDefaultCommandName() {
17919
18295
  // src/utils/npx-prompt.ts
17920
18296
  init_paths();
17921
18297
  init_fs();
17922
- import { fileURLToPath as fileURLToPath9 } from "url";
17923
- import { readFile as readFile31 } from "fs/promises";
17924
- import { dirname as dirname15, join as join7, resolve as resolve46 } from "path";
18298
+ import { fileURLToPath as fileURLToPath8 } from "url";
18299
+ import { readFile as readFile34 } from "fs/promises";
18300
+ import { dirname as dirname15, join as join8, resolve as resolve49 } from "path";
17925
18301
  import { spawn as spawn4 } from "child_process";
17926
18302
  import { createInterface as createInterface2 } from "readline/promises";
17927
18303
 
17928
18304
  // src/utils/version.ts
17929
- import { fileURLToPath as fileURLToPath8 } from "url";
17930
- import { readFile as readFile30 } from "fs/promises";
17931
- import { dirname as dirname14, join as join6 } from "path";
18305
+ import { fileURLToPath as fileURLToPath7 } from "url";
18306
+ import { readFile as readFile33 } from "fs/promises";
18307
+ import { dirname as dirname14, join as join7 } from "path";
17932
18308
  async function readPackageVersion(scriptUrl) {
17933
18309
  try {
17934
- const scriptPath = fileURLToPath8(scriptUrl);
18310
+ const scriptPath = fileURLToPath7(scriptUrl);
17935
18311
  const pkgRoot = dirname14(dirname14(scriptPath));
17936
- const raw = await readFile30(join6(pkgRoot, "package.json"), "utf-8");
18312
+ const raw = await readFile33(join7(pkgRoot, "package.json"), "utf-8");
17937
18313
  const parsed = JSON.parse(raw);
17938
18314
  return typeof parsed.version === "string" ? parsed.version : null;
17939
18315
  } catch {
@@ -17942,13 +18318,13 @@ async function readPackageVersion(scriptUrl) {
17942
18318
  }
17943
18319
 
17944
18320
  // src/utils/npx-prompt.ts
17945
- var STATE_FILE = resolve46(syntaurRoot(), "npx-install.json");
18321
+ var STATE_FILE = resolve49(syntaurRoot(), "npx-install.json");
17946
18322
  var META_ARGS = /* @__PURE__ */ new Set(["-h", "--help", "-V", "--version", "help"]);
17947
18323
  var GLOBAL_VERSION_TIMEOUT_MS = 2e3;
17948
18324
  function isRunningViaNpx(scriptUrl) {
17949
18325
  let scriptPath;
17950
18326
  try {
17951
- scriptPath = fileURLToPath9(scriptUrl);
18327
+ scriptPath = fileURLToPath8(scriptUrl);
17952
18328
  } catch {
17953
18329
  return false;
17954
18330
  }
@@ -17963,7 +18339,7 @@ function isRunningViaNpx(scriptUrl) {
17963
18339
  async function readState() {
17964
18340
  if (!await fileExists(STATE_FILE)) return null;
17965
18341
  try {
17966
- const raw = await readFile31(STATE_FILE, "utf-8");
18342
+ const raw = await readFile34(STATE_FILE, "utf-8");
17967
18343
  return JSON.parse(raw);
17968
18344
  } catch {
17969
18345
  return null;
@@ -17977,7 +18353,7 @@ async function resolveNpmBin() {
17977
18353
  const nodeDir = dirname15(process.execPath);
17978
18354
  const isWin = process.platform === "win32";
17979
18355
  const npmName = isWin ? "npm.cmd" : "npm";
17980
- const nearNode = join7(nodeDir, npmName);
18356
+ const nearNode = join8(nodeDir, npmName);
17981
18357
  if (await fileExists(nearNode)) {
17982
18358
  return { cmd: nearNode, shell: false };
17983
18359
  }
@@ -18020,9 +18396,9 @@ async function readGlobalVersion() {
18020
18396
  });
18021
18397
  if (!rootPath) return null;
18022
18398
  try {
18023
- const manifestPath = join7(rootPath, "syntaur", "package.json");
18399
+ const manifestPath = join8(rootPath, "syntaur", "package.json");
18024
18400
  if (!await fileExists(manifestPath)) return null;
18025
- const raw = await readFile31(manifestPath, "utf-8");
18401
+ const raw = await readFile34(manifestPath, "utf-8");
18026
18402
  const parsed = JSON.parse(raw);
18027
18403
  return typeof parsed.version === "string" ? parsed.version : null;
18028
18404
  } catch {
@@ -18317,7 +18693,7 @@ program.command("setup").description("Initialize Syntaur and optionally install
18317
18693
  process.exit(1);
18318
18694
  }
18319
18695
  });
18320
- program.command("install-plugin").description("Install the Syntaur Claude Code plugin").option("--force", "Overwrite an existing Syntaur-managed install").option("--target-dir <path>", "Install the plugin at a specific directory").option("--link", "Use a symlink instead of copying files (repo-local dev only)").option("--force-skills", "Overwrite user-edited skills in ~/.claude/skills").option("--skip-skills", "Do not install protocol skills into ~/.claude/skills").action(async (options) => {
18696
+ program.command("install-plugin").description("Install the Syntaur Claude Code plugin").option("--force", "Overwrite an existing Syntaur-managed install").option("--target-dir <path>", "Install the plugin at a specific directory").option("--link", "Use a symlink instead of copying files (repo-local dev only)").option("--force-skills", "Overwrite user-edited skills in ~/.claude/skills").option("--skip-skills", "Do not install protocol skills into ~/.claude/skills").option("--enable", "Enable the plugin in ~/.claude/settings.json after install").action(async (options) => {
18321
18697
  try {
18322
18698
  await installPluginCommand({ ...options, promptForTarget: true });
18323
18699
  } catch (error) {