@rpamis/comet 0.4.0-beta.4 → 0.4.0-beta.5

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 (183) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +5 -1
  3. package/assets/manifest.json +1 -1
  4. package/assets/skills/comet/SKILL.md +30 -28
  5. package/assets/skills/comet/reference/auto-transition.md +1 -1
  6. package/assets/skills/comet/reference/comet-yaml-fields.md +6 -4
  7. package/assets/skills/comet/reference/context-recovery.md +2 -2
  8. package/assets/skills/comet/reference/decision-point.md +11 -0
  9. package/assets/skills/comet/reference/subagent-dispatch.md +6 -5
  10. package/assets/skills/comet/rules/comet-phase-guard.en.md +23 -28
  11. package/assets/skills/comet/rules/comet-phase-guard.md +22 -28
  12. package/assets/skills/comet/scripts/comet-runtime.mjs +185 -56
  13. package/assets/skills/comet-any/SKILL.md +1 -1
  14. package/assets/skills/comet-any/reference/authored-zone-example.md +10 -5
  15. package/assets/skills/comet-any/reference/subagents/pause-points-author.md +14 -11
  16. package/assets/skills/comet-any/reference/subagents/skill-reviewer.md +2 -0
  17. package/assets/skills/comet-any/reference/subagents/workflow-entry-author.md +5 -4
  18. package/assets/skills/comet-archive/SKILL.md +34 -13
  19. package/assets/skills/comet-build/SKILL.md +23 -21
  20. package/assets/skills/comet-design/SKILL.md +23 -22
  21. package/assets/skills/comet-hotfix/SKILL.md +35 -34
  22. package/assets/skills/comet-open/SKILL.md +97 -58
  23. package/assets/skills/comet-tweak/SKILL.md +21 -18
  24. package/assets/skills/comet-verify/SKILL.md +35 -59
  25. package/assets/skills-zh/comet/SKILL.md +30 -28
  26. package/assets/skills-zh/comet/reference/auto-transition.md +1 -1
  27. package/assets/skills-zh/comet/reference/comet-yaml-fields.md +6 -4
  28. package/assets/skills-zh/comet/reference/context-recovery.md +2 -2
  29. package/assets/skills-zh/comet/reference/decision-point.md +11 -0
  30. package/assets/skills-zh/comet/reference/subagent-dispatch.md +6 -5
  31. package/assets/skills-zh/comet-any/SKILL.md +1 -1
  32. package/assets/skills-zh/comet-any/reference/authored-zone-example.md +10 -5
  33. package/assets/skills-zh/comet-any/reference/subagents/pause-points-author.md +14 -12
  34. package/assets/skills-zh/comet-any/reference/subagents/skill-reviewer.md +2 -0
  35. package/assets/skills-zh/comet-any/reference/subagents/workflow-entry-author.md +5 -4
  36. package/assets/skills-zh/comet-archive/SKILL.md +34 -13
  37. package/assets/skills-zh/comet-build/SKILL.md +23 -21
  38. package/assets/skills-zh/comet-design/SKILL.md +23 -22
  39. package/assets/skills-zh/comet-hotfix/SKILL.md +34 -33
  40. package/assets/skills-zh/comet-open/SKILL.md +96 -57
  41. package/assets/skills-zh/comet-tweak/SKILL.md +20 -17
  42. package/assets/skills-zh/comet-verify/SKILL.md +35 -59
  43. package/dist/app/commands/doctor.d.ts.map +1 -1
  44. package/dist/app/commands/doctor.js +62 -15
  45. package/dist/app/commands/doctor.js.map +1 -1
  46. package/dist/app/commands/init.d.ts +1 -1
  47. package/dist/app/commands/init.d.ts.map +1 -1
  48. package/dist/app/commands/init.js +58 -14
  49. package/dist/app/commands/init.js.map +1 -1
  50. package/dist/app/commands/uninstall.d.ts +3 -0
  51. package/dist/app/commands/uninstall.d.ts.map +1 -1
  52. package/dist/app/commands/uninstall.js +70 -36
  53. package/dist/app/commands/uninstall.js.map +1 -1
  54. package/dist/app/commands/update.d.ts.map +1 -1
  55. package/dist/app/commands/update.js +213 -34
  56. package/dist/app/commands/update.js.map +1 -1
  57. package/dist/domains/bundle/bundle-platform.d.ts +1 -0
  58. package/dist/domains/bundle/bundle-platform.d.ts.map +1 -1
  59. package/dist/domains/bundle/bundle-platform.js +6 -2
  60. package/dist/domains/bundle/bundle-platform.js.map +1 -1
  61. package/dist/domains/comet-classic/classic-guard.d.ts.map +1 -1
  62. package/dist/domains/comet-classic/classic-guard.js +15 -11
  63. package/dist/domains/comet-classic/classic-guard.js.map +1 -1
  64. package/dist/domains/comet-classic/classic-hook-guard.d.ts.map +1 -1
  65. package/dist/domains/comet-classic/classic-hook-guard.js +113 -19
  66. package/dist/domains/comet-classic/classic-hook-guard.js.map +1 -1
  67. package/dist/domains/comet-classic/classic-resolver.js +1 -1
  68. package/dist/domains/comet-classic/classic-resolver.js.map +1 -1
  69. package/dist/domains/comet-classic/classic-state-command.d.ts.map +1 -1
  70. package/dist/domains/comet-classic/classic-state-command.js +22 -13
  71. package/dist/domains/comet-classic/classic-state-command.js.map +1 -1
  72. package/dist/domains/comet-classic/classic-state.d.ts +3 -2
  73. package/dist/domains/comet-classic/classic-state.d.ts.map +1 -1
  74. package/dist/domains/comet-classic/classic-state.js +13 -1
  75. package/dist/domains/comet-classic/classic-state.js.map +1 -1
  76. package/dist/domains/comet-classic/classic-transitions.d.ts.map +1 -1
  77. package/dist/domains/comet-classic/classic-transitions.js +16 -2
  78. package/dist/domains/comet-classic/classic-transitions.js.map +1 -1
  79. package/dist/domains/comet-classic/classic-validate-command.d.ts.map +1 -1
  80. package/dist/domains/comet-classic/classic-validate-command.js +7 -1
  81. package/dist/domains/comet-classic/classic-validate-command.js.map +1 -1
  82. package/dist/domains/dashboard/web/assets/_commonjsHelpers-CqkleIqs.js +1 -0
  83. package/dist/domains/dashboard/web/assets/arc-TGVUG2WN.js +1 -0
  84. package/dist/domains/dashboard/web/assets/architectureDiagram-3BPJPVTR-ASHmiHoP.js +36 -0
  85. package/dist/domains/dashboard/web/assets/blockDiagram-GPEHLZMM-CwASxBKe.js +132 -0
  86. package/dist/domains/dashboard/web/assets/c4Diagram-AAUBKEIU-BU8z_WNk.js +10 -0
  87. package/dist/domains/dashboard/web/assets/channel-kp6Ng2JJ.js +1 -0
  88. package/dist/domains/dashboard/web/assets/chunk-2J33WTMH-CBGlUx4_.js +1 -0
  89. package/dist/domains/dashboard/web/assets/chunk-4BX2VUAB-CS-iGcFz.js +1 -0
  90. package/dist/domains/dashboard/web/assets/chunk-55IACEB6-BUtCVJat.js +1 -0
  91. package/dist/domains/dashboard/web/assets/chunk-727SXJPM-C94tj_pf.js +206 -0
  92. package/dist/domains/dashboard/web/assets/chunk-AQP2D5EJ-BFOKcpdG.js +231 -0
  93. package/dist/domains/dashboard/web/assets/chunk-FMBD7UC4-Dmx8B-st.js +15 -0
  94. package/dist/domains/dashboard/web/assets/chunk-ND2GUHAM-DYV00CNn.js +1 -0
  95. package/dist/domains/dashboard/web/assets/chunk-QZHKN3VN-DMJJyQnw.js +1 -0
  96. package/dist/domains/dashboard/web/assets/classDiagram-4FO5ZUOK-yBDjgMY_.js +1 -0
  97. package/dist/domains/dashboard/web/assets/classDiagram-v2-Q7XG4LA2-yBDjgMY_.js +1 -0
  98. package/dist/domains/dashboard/web/assets/cose-bilkent-S5V4N54A-P4nE2rea.js +1 -0
  99. package/dist/domains/dashboard/web/assets/cynefin-VYW2F7L2-C-R1HXDv.js +166 -0
  100. package/dist/domains/dashboard/web/assets/cytoscape.esm-D3_iZ_3b.js +321 -0
  101. package/dist/domains/dashboard/web/assets/dagre-BM42HDAG-BH8d-C9z.js +4 -0
  102. package/dist/domains/dashboard/web/assets/defaultLocale-DX6XiGOO.js +1 -0
  103. package/dist/domains/dashboard/web/assets/diagram-2AECGRRQ-DpXTgQIH.js +43 -0
  104. package/dist/domains/dashboard/web/assets/diagram-5GNKFQAL-DSiFJzSw.js +10 -0
  105. package/dist/domains/dashboard/web/assets/diagram-KO2AKTUF-DULMkben.js +3 -0
  106. package/dist/domains/dashboard/web/assets/diagram-LMA3HP47-Pi8m_NO9.js +24 -0
  107. package/dist/domains/dashboard/web/assets/diagram-OG6HWLK6-BSe6bCWm.js +24 -0
  108. package/dist/domains/dashboard/web/assets/erDiagram-TEJ5UH35-zb-FEcbq.js +85 -0
  109. package/dist/domains/dashboard/web/assets/flowDiagram-I6XJVG4X-Dr-Gk0z2.js +162 -0
  110. package/dist/domains/dashboard/web/assets/ganttDiagram-6RSMTGT7-qs6Zb2A6.js +292 -0
  111. package/dist/domains/dashboard/web/assets/gitGraphDiagram-PVQCEYII-BQ314orT.js +106 -0
  112. package/dist/domains/dashboard/web/assets/graph--OzhPTMs.js +1 -0
  113. package/dist/domains/dashboard/web/assets/index-BLAfkNTu.css +1 -0
  114. package/dist/domains/dashboard/web/assets/index-DlSYK56b.js +139 -0
  115. package/dist/domains/dashboard/web/assets/index-DtLUOcAW.js +24 -0
  116. package/dist/domains/dashboard/web/assets/index-QO0z6F4w.js +10 -0
  117. package/dist/domains/dashboard/web/assets/infoDiagram-5YYISTIA-O0bQp98A.js +2 -0
  118. package/dist/domains/dashboard/web/assets/init-Gi6I4Gst.js +1 -0
  119. package/dist/domains/dashboard/web/assets/ishikawaDiagram-YF4QCWOH-BPC7wbKh.js +70 -0
  120. package/dist/domains/dashboard/web/assets/journeyDiagram-JHISSGLW-ohViOd4b.js +139 -0
  121. package/dist/domains/dashboard/web/assets/kanban-definition-UN3LZRKU-CHleNdbG.js +89 -0
  122. package/dist/domains/dashboard/web/assets/katex-HP8lGamR.js +257 -0
  123. package/dist/domains/dashboard/web/assets/layout-SsrduOYp.js +1 -0
  124. package/dist/domains/dashboard/web/assets/linear-CyqiW--Q.js +1 -0
  125. package/dist/domains/dashboard/web/assets/marked.esm-DECB3bxB.js +64 -0
  126. package/dist/domains/dashboard/web/assets/mermaid.core-qS9XvtaK.js +301 -0
  127. package/dist/domains/dashboard/web/assets/mindmap-definition-RKZ34NQL-oaGkqO9l.js +96 -0
  128. package/dist/domains/dashboard/web/assets/ordinal-Cboi1Yqb.js +1 -0
  129. package/dist/domains/dashboard/web/assets/pieDiagram-4H26LBE5-CPcB-AFh.js +30 -0
  130. package/dist/domains/dashboard/web/assets/purify.es-VaSPOPhr.js +3 -0
  131. package/dist/domains/dashboard/web/assets/quadrantDiagram-W4KKPZXB-LFy52Qtj.js +7 -0
  132. package/dist/domains/dashboard/web/assets/requirementDiagram-4Y6WPE33-BdjeaYP_.js +84 -0
  133. package/dist/domains/dashboard/web/assets/sankeyDiagram-5OEKKPKP-DDJ4styL.js +40 -0
  134. package/dist/domains/dashboard/web/assets/sequenceDiagram-3UESZ5HK-BL5bFNoz.js +162 -0
  135. package/dist/domains/dashboard/web/assets/stateDiagram-AJRCARHV-BNUfikui.js +1 -0
  136. package/dist/domains/dashboard/web/assets/stateDiagram-v2-BHNVJYJU-DGrsmWts.js +1 -0
  137. package/dist/domains/dashboard/web/assets/timeline-definition-PNZ67QCA--ps2uu6L.js +120 -0
  138. package/dist/domains/dashboard/web/assets/vennDiagram-CIIHVFJN-C2q8q26c.js +34 -0
  139. package/dist/domains/dashboard/web/assets/wardleyDiagram-YWT4CUSO-BUH9gXqd.js +78 -0
  140. package/dist/domains/dashboard/web/assets/xychartDiagram-2RQKCTM6-B-DielEy.js +7 -0
  141. package/dist/domains/dashboard/web/index.html +16 -16
  142. package/dist/domains/factory/package.d.ts.map +1 -1
  143. package/dist/domains/factory/package.js +20 -6
  144. package/dist/domains/factory/package.js.map +1 -1
  145. package/dist/domains/integrations/openspec.d.ts +4 -1
  146. package/dist/domains/integrations/openspec.d.ts.map +1 -1
  147. package/dist/domains/integrations/openspec.js +56 -4
  148. package/dist/domains/integrations/openspec.js.map +1 -1
  149. package/dist/domains/skill/json-object.d.ts +13 -0
  150. package/dist/domains/skill/json-object.d.ts.map +1 -0
  151. package/dist/domains/skill/json-object.js +24 -0
  152. package/dist/domains/skill/json-object.js.map +1 -0
  153. package/dist/domains/skill/platform-inspect.d.ts +9 -0
  154. package/dist/domains/skill/platform-inspect.d.ts.map +1 -0
  155. package/dist/domains/skill/platform-inspect.js +149 -0
  156. package/dist/domains/skill/platform-inspect.js.map +1 -0
  157. package/dist/domains/skill/platform-install.d.ts +16 -6
  158. package/dist/domains/skill/platform-install.d.ts.map +1 -1
  159. package/dist/domains/skill/platform-install.js +277 -144
  160. package/dist/domains/skill/platform-install.js.map +1 -1
  161. package/dist/domains/skill/uninstall.d.ts.map +1 -1
  162. package/dist/domains/skill/uninstall.js +204 -141
  163. package/dist/domains/skill/uninstall.js.map +1 -1
  164. package/dist/platform/fs/file-system.d.ts +3 -3
  165. package/dist/platform/fs/file-system.d.ts.map +1 -1
  166. package/dist/platform/fs/file-system.js +21 -11
  167. package/dist/platform/fs/file-system.js.map +1 -1
  168. package/dist/platform/install/platforms.d.ts +4 -0
  169. package/dist/platform/install/platforms.d.ts.map +1 -1
  170. package/dist/platform/install/platforms.js +2 -0
  171. package/dist/platform/install/platforms.js.map +1 -1
  172. package/dist/platform/install/project-registry.d.ts +1 -0
  173. package/dist/platform/install/project-registry.d.ts.map +1 -1
  174. package/dist/platform/install/project-registry.js +9 -1
  175. package/dist/platform/install/project-registry.js.map +1 -1
  176. package/dist/platform/install/skill-root-owner.d.ts +15 -0
  177. package/dist/platform/install/skill-root-owner.d.ts.map +1 -0
  178. package/dist/platform/install/skill-root-owner.js +68 -0
  179. package/dist/platform/install/skill-root-owner.js.map +1 -0
  180. package/package.json +6 -1
  181. package/dist/domains/dashboard/web/assets/index-CIvN-PL8.js +0 -22
  182. package/dist/domains/dashboard/web/assets/index-DcyRPBnT.css +0 -1
  183. package/eval/.env +0 -28
@@ -7,6 +7,7 @@ import { fileExists, readJson, copyFile, ensureDir } from '../../platform/fs/fil
7
7
  import { getPlatformConfigDir, getPlatformSkillsDir, } from '../../platform/install/platforms.js';
8
8
  import { formatSupportedArtifactLanguages, resolveArtifactLanguage } from './languages.js';
9
9
  import { installCometProjectInstructions } from './project-instructions.js';
10
+ import { readJsonObjectFile } from './json-object.js';
10
11
  const __filename = fileURLToPath(import.meta.url);
11
12
  const __dirname = path.dirname(__filename);
12
13
  function planSkillDirectoryCopy(files, destinationRoot) {
@@ -190,11 +191,11 @@ async function installSkillsAsSymlink(baseDir, platform, overwrite, languageSkil
190
191
  const sourceDir = isScript ? 'skills' : languageSkillsDir;
191
192
  const src = path.join(assetsDir, sourceDir, skillRelPath);
192
193
  const centralDest = path.join(centralDir, 'skills', skillRelPath);
193
- if (!overwrite && (await fileExists(centralDest))) {
194
- skippedCount++;
195
- continue;
196
- }
197
194
  try {
195
+ if (!overwrite && (await fileExists(centralDest))) {
196
+ skippedCount++;
197
+ continue;
198
+ }
198
199
  await copyFile(src, centralDest);
199
200
  copied++;
200
201
  }
@@ -218,12 +219,14 @@ async function installSkillsAsSymlink(baseDir, platform, overwrite, languageSkil
218
219
  const result = await createOpenCodeCommands(baseDir, platform, manifest.skills, overwrite, scope, languageSkillsDir);
219
220
  copied += result.copied;
220
221
  skippedCount += result.skipped;
222
+ failedCount += result.failed;
221
223
  }
222
224
  // Handle Pi platform command extension
223
225
  if (platform.id === 'pi') {
224
226
  const result = await createPiCommandExtension(baseDir, platform, manifest.skills, overwrite, scope);
225
227
  copied += result.copied;
226
228
  skippedCount += result.skipped;
229
+ failedCount += result.failed;
227
230
  }
228
231
  return { copied, skipped: skippedCount, failed: failedCount };
229
232
  }
@@ -248,11 +251,11 @@ async function copyCometSkillsForPlatform(baseDir, platform, overwrite, language
248
251
  const sourceDir = isScript ? 'skills' : languageSkillsDir;
249
252
  const src = path.join(assetsDir, sourceDir, skillRelPath);
250
253
  const dest = path.join(baseDir, getPlatformSkillsDir(platform, scope), 'skills', skillRelPath);
251
- if (!overwrite && (await fileExists(dest))) {
252
- skippedCount++;
253
- continue;
254
- }
255
254
  try {
255
+ if (!overwrite && (await fileExists(dest))) {
256
+ skippedCount++;
257
+ continue;
258
+ }
256
259
  await copyFile(src, dest);
257
260
  copied++;
258
261
  }
@@ -269,11 +272,13 @@ async function copyCometSkillsForPlatform(baseDir, platform, overwrite, language
269
272
  const result = await createOpenCodeCommands(baseDir, platform, manifest.skills, overwrite, scope, languageSkillsDir);
270
273
  copied += result.copied;
271
274
  skippedCount += result.skipped;
275
+ failedCount += result.failed;
272
276
  }
273
277
  if (platform.id === 'pi') {
274
278
  const result = await createPiCommandExtension(baseDir, platform, manifest.skills, overwrite, scope);
275
279
  copied += result.copied;
276
280
  skippedCount += result.skipped;
281
+ failedCount += result.failed;
277
282
  }
278
283
  return { copied, skipped: skippedCount, failed: failedCount };
279
284
  }
@@ -304,37 +309,46 @@ async function createPiCommandExtension(baseDir, platform, skillPaths, overwrite
304
309
  const platformBase = path.join(baseDir, getPlatformSkillsDir(platform, scope));
305
310
  const settingsPath = path.join(platformBase, 'settings.json');
306
311
  const extensionPath = path.join(platformBase, 'extensions', PI_COMMAND_EXTENSION_FILE);
307
- let settings = {};
308
- if (await fileExists(settingsPath)) {
309
- try {
312
+ let copied = 0;
313
+ let skipped = 0;
314
+ let failed = 0;
315
+ try {
316
+ let settings = {};
317
+ if (await fileExists(settingsPath)) {
310
318
  const parsed = JSON.parse(await readFile(settingsPath, 'utf-8'));
311
319
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
312
320
  throw new Error('expected a JSON object');
313
321
  }
314
322
  settings = parsed;
315
323
  }
316
- catch (err) {
317
- throw new Error(`Invalid Pi settings at ${settingsPath}: ${err.message}`, {
318
- cause: err,
319
- });
324
+ if (settings.enableSkillCommands !== true) {
325
+ settings.enableSkillCommands = true;
326
+ await ensureDir(path.dirname(settingsPath));
327
+ await writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
328
+ copied++;
320
329
  }
321
330
  }
322
- let copied = 0;
323
- let skipped = 0;
324
- if (settings.enableSkillCommands !== true) {
325
- settings.enableSkillCommands = true;
326
- await ensureDir(path.dirname(settingsPath));
327
- await writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
328
- copied++;
331
+ catch (err) {
332
+ failed++;
333
+ console.error(` Failed to update Pi settings at ${settingsPath}: ${err.message}`);
329
334
  }
330
- if (!overwrite && (await fileExists(extensionPath))) {
331
- skipped++;
332
- return { copied, skipped };
335
+ if (failed > 0)
336
+ return { copied, skipped, failed };
337
+ try {
338
+ if (!overwrite && (await fileExists(extensionPath))) {
339
+ skipped++;
340
+ }
341
+ else {
342
+ await ensureDir(path.dirname(extensionPath));
343
+ await writeFile(extensionPath, renderPiCommandExtension(getTopLevelSkillNames(skillPaths)), 'utf-8');
344
+ copied++;
345
+ }
333
346
  }
334
- await ensureDir(path.dirname(extensionPath));
335
- await writeFile(extensionPath, renderPiCommandExtension(getTopLevelSkillNames(skillPaths)), 'utf-8');
336
- copied++;
337
- return { copied, skipped };
347
+ catch (err) {
348
+ failed++;
349
+ console.error(` Failed to write Pi command extension at ${extensionPath}: ${err.message}`);
350
+ }
351
+ return { copied, skipped, failed };
338
352
  }
339
353
  function stripFrontmatter(content) {
340
354
  if (!content.startsWith('---\n') && !content.startsWith('---\r\n')) {
@@ -349,6 +363,7 @@ function stripFrontmatter(content) {
349
363
  async function createOpenCodeCommands(baseDir, platform, skillPaths, overwrite, scope, languageSkillsDir) {
350
364
  let copied = 0;
351
365
  let skipped = 0;
366
+ let failed = 0;
352
367
  const assetsDir = getAssetsDir();
353
368
  const commandsDir = path.join(baseDir, getPlatformSkillsDir(platform, scope), 'commands');
354
369
  for (const skillPath of skillPaths) {
@@ -357,17 +372,18 @@ async function createOpenCodeCommands(baseDir, platform, skillPaths, overwrite,
357
372
  continue;
358
373
  const skillName = parts[0];
359
374
  const dest = path.join(commandsDir, `${skillName}.md`);
360
- if (!overwrite && (await fileExists(dest))) {
361
- skipped++;
362
- continue;
363
- }
364
- await ensureDir(path.dirname(dest));
365
- let skillSourcePath = path.join(assetsDir, languageSkillsDir, skillPath);
366
- if (!(await fileExists(skillSourcePath))) {
367
- skillSourcePath = path.join(assetsDir, 'skills', skillPath);
368
- }
369
- const skillBody = stripFrontmatter(await readFile(skillSourcePath, 'utf-8'));
370
- const content = `${OPENCODE_COMMAND_HEADER.replace('{skillName}', skillName)}
375
+ try {
376
+ if (!overwrite && (await fileExists(dest))) {
377
+ skipped++;
378
+ continue;
379
+ }
380
+ await ensureDir(path.dirname(dest));
381
+ let skillSourcePath = path.join(assetsDir, languageSkillsDir, skillPath);
382
+ if (!(await fileExists(skillSourcePath))) {
383
+ skillSourcePath = path.join(assetsDir, 'skills', skillPath);
384
+ }
385
+ const skillBody = stripFrontmatter(await readFile(skillSourcePath, 'utf-8'));
386
+ const content = `${OPENCODE_COMMAND_HEADER.replace('{skillName}', skillName)}
371
387
  Equivalent Comet skill: \`${skillName}\`
372
388
  Command name: \`/${skillName}\`
373
389
 
@@ -379,10 +395,15 @@ $ARGUMENTS
379
395
 
380
396
  ${skillBody}
381
397
  `;
382
- await writeFile(dest, content, 'utf-8');
383
- copied++;
398
+ await writeFile(dest, content, 'utf-8');
399
+ copied++;
400
+ }
401
+ catch (err) {
402
+ failed++;
403
+ console.error(` Failed to create OpenCode command ${dest}: ${err.message}`);
404
+ }
384
405
  }
385
- return { copied, skipped };
406
+ return { copied, skipped, failed };
386
407
  }
387
408
  async function readManifest() {
388
409
  const assetsDir = getAssetsDir();
@@ -429,12 +450,12 @@ function selectRulePathsForLanguage(rulePaths, languageId) {
429
450
  }
430
451
  async function copyCometRulesForPlatform(baseDir, platform, overwrite, languageId, scope = 'project') {
431
452
  if (!platform.rulesDir || !platform.rulesFormat) {
432
- return { copied: 0, skipped: 0 };
453
+ return { copied: 0, skipped: 0, failed: 0 };
433
454
  }
434
455
  const manifest = await readManifest();
435
456
  const rulePaths = selectRulePathsForLanguage(manifest.rules ?? [], languageId);
436
457
  if (!rulePaths || rulePaths.length === 0) {
437
- return { copied: 0, skipped: 0 };
458
+ return { copied: 0, skipped: 0, failed: 0 };
438
459
  }
439
460
  const assetsDir = getAssetsDir();
440
461
  // Support platforms whose rules live outside the skills config dir
@@ -446,22 +467,24 @@ async function copyCometRulesForPlatform(baseDir, platform, overwrite, languageI
446
467
  : path.join(baseDir, getPlatformSkillsDir(platform, scope));
447
468
  let copied = 0;
448
469
  let skippedCount = 0;
470
+ let failed = 0;
449
471
  for (const ruleRelPath of rulePaths) {
450
472
  const src = path.join(assetsDir, 'skills', ruleRelPath);
451
- if (!(await fileExists(src))) {
452
- console.error(` Rule source not found: ${ruleRelPath}`);
453
- continue;
454
- }
455
- // Normalize the `.en` infix away so the installed file name is the same
456
- // regardless of which language variant was selected.
457
- const ruleFileName = toRuleBaseName(path.basename(ruleRelPath));
458
- const rulesDestDir = path.join(rulesBase, platform.rulesDir);
459
- const dest = computeRuleDestPath(rulesDestDir, ruleFileName, platform.rulesFormat);
460
- if (!overwrite && (await fileExists(dest))) {
461
- skippedCount++;
462
- continue;
463
- }
464
473
  try {
474
+ if (!(await fileExists(src))) {
475
+ console.error(` Rule source not found: ${ruleRelPath}`);
476
+ failed++;
477
+ continue;
478
+ }
479
+ // Normalize the `.en` infix away so the installed file name is the same
480
+ // regardless of which language variant was selected.
481
+ const ruleFileName = toRuleBaseName(path.basename(ruleRelPath));
482
+ const rulesDestDir = path.join(rulesBase, platform.rulesDir);
483
+ const dest = computeRuleDestPath(rulesDestDir, ruleFileName, platform.rulesFormat);
484
+ if (!overwrite && (await fileExists(dest))) {
485
+ skippedCount++;
486
+ continue;
487
+ }
465
488
  const content = await readFile(src, 'utf-8');
466
489
  await ensureDir(path.dirname(dest));
467
490
  const formatted = formatRuleContent(content, ruleFileName, platform.rulesFormat);
@@ -470,9 +493,10 @@ async function copyCometRulesForPlatform(baseDir, platform, overwrite, languageI
470
493
  }
471
494
  catch (err) {
472
495
  console.error(` Failed to copy rule ${ruleRelPath}: ${err.message}`);
496
+ failed++;
473
497
  }
474
498
  }
475
- return { copied, skipped: skippedCount };
499
+ return { copied, skipped: skippedCount, failed };
476
500
  }
477
501
  function computeRuleDestPath(rulesDestDir, ruleFileName, rulesFormat) {
478
502
  if (rulesFormat === 'mdc') {
@@ -509,7 +533,8 @@ ${content}`;
509
533
  /**
510
534
  * Install Comet hooks for platforms that support them.
511
535
  * Supports multiple hook formats:
512
- * 'claude-code' — settings.local.json with PreToolUse array (Claude Code, Codex, Amazon Q)
536
+ * 'claude-code' — Claude-shaped JSON with PreToolUse array; defaults to settings.local.json,
537
+ * with platform metadata able to override the filename
513
538
  * 'qwen' — settings.json with PreToolUse/hooks array (Qwen Code)
514
539
  * 'qoder' — settings.json with PreToolUse/hooks array (Qoder)
515
540
  * 'codebuddy' — settings.json with PreToolUse/hooks array (CodeBuddy Code)
@@ -519,39 +544,57 @@ ${content}`;
519
544
  * 'kiro' — hooks/*.kiro.hook JSON files
520
545
  */
521
546
  async function installCometHooksForPlatform(baseDir, platform, scope = 'project') {
522
- if (!platform.supportsHooks || !platform.hookFormat) {
523
- return { installed: false, reason: 'platform does not support hooks' };
547
+ if (!platform.supportsHooks) {
548
+ return { status: 'skipped', reason: 'platform does not support hooks' };
524
549
  }
525
- const manifest = await readManifest();
526
- const hooksConfig = manifest.hooks;
527
- if (!hooksConfig || Object.keys(hooksConfig).length === 0) {
528
- return { installed: false, reason: 'no hooks defined in manifest' };
550
+ if (!platform.hookFormat) {
551
+ return {
552
+ status: 'failed',
553
+ reason: 'hook-capable platform does not declare a hook format',
554
+ };
529
555
  }
530
- const hookFormat = platform.hookFormat;
531
- const skillsDir = getPlatformSkillsDir(platform, scope);
532
- const platformBase = path.join(baseDir, getPlatformConfigDir(platform, scope));
533
556
  try {
557
+ const manifest = await readManifest();
558
+ const hooksConfig = manifest.hooks;
559
+ if (!hooksConfig || Object.keys(hooksConfig).length === 0) {
560
+ return { status: 'skipped', reason: 'no hooks defined in manifest' };
561
+ }
562
+ const hookFormat = platform.hookFormat;
563
+ const skillsDir = getPlatformSkillsDir(platform, scope);
564
+ const platformBase = path.join(baseDir, getPlatformConfigDir(platform, scope));
534
565
  switch (hookFormat) {
535
- case 'claude-code':
536
- return await installClaudeCodeHooks(baseDir, platformBase, skillsDir, hooksConfig);
566
+ case 'claude-code': {
567
+ const result = await installClaudeCodeHooks(baseDir, platformBase, skillsDir, hooksConfig, platform.hookConfigFile ?? 'settings.local.json', platform.name);
568
+ if (result.status === 'installed') {
569
+ for (const legacyFile of platform.legacyHookConfigFiles ?? []) {
570
+ try {
571
+ await removeManagedHooksFromJsonFile(path.join(platformBase, legacyFile), Object.keys(hooksConfig));
572
+ }
573
+ catch {
574
+ // Historical Hook cleanup is best-effort after canonical install succeeds.
575
+ }
576
+ }
577
+ }
578
+ return result;
579
+ }
537
580
  case 'qwen':
538
581
  case 'qoder':
539
582
  case 'codebuddy':
540
- return await installQwenStyleHooks(baseDir, platformBase, skillsDir, hooksConfig, hookFormat);
583
+ return await installQwenStyleHooks(baseDir, platformBase, skillsDir, hooksConfig, platform.name);
541
584
  case 'gemini':
542
- return await installGeminiHooks(baseDir, platformBase, skillsDir, hooksConfig);
585
+ return await installGeminiHooks(baseDir, platformBase, skillsDir, hooksConfig, platform.name);
543
586
  case 'windsurf':
544
- return await installWindsurfHooks(baseDir, platformBase, skillsDir, hooksConfig);
587
+ return await installWindsurfHooks(baseDir, platformBase, skillsDir, hooksConfig, platform.name);
545
588
  case 'copilot':
546
589
  return await installCopilotHooks(baseDir, platformBase, skillsDir, hooksConfig);
547
590
  case 'kiro':
548
591
  return await installKiroHooks(baseDir, platformBase, skillsDir, hooksConfig);
549
592
  default:
550
- return { installed: false, reason: `unsupported hook format: ${hookFormat}` };
593
+ return { status: 'failed', reason: `unsupported hook format: ${hookFormat}` };
551
594
  }
552
595
  }
553
596
  catch (err) {
554
- return { installed: false, reason: err.message };
597
+ return { status: 'failed', reason: err.message };
555
598
  }
556
599
  }
557
600
  function quoteCommandArg(value) {
@@ -563,34 +606,96 @@ function buildHookCommand(baseDir, skillsDir, scriptRelPath) {
563
606
  const scriptPath = path.join(projectRoot, skillsDir, 'skills', ...scriptRelPath.split('/'));
564
607
  return `node ${quoteCommandArg(scriptPath)} --project-root ${quoteCommandArg(projectRoot)}`;
565
608
  }
609
+ function parseCommandTokens(command) {
610
+ const tokens = [];
611
+ let current = '';
612
+ let quote;
613
+ let tokenStarted = false;
614
+ let quoteClosed = false;
615
+ for (let index = 0; index < command.length; index++) {
616
+ const character = command[index];
617
+ if (quote) {
618
+ if (character === quote) {
619
+ quote = undefined;
620
+ quoteClosed = true;
621
+ }
622
+ else if (character === '\\' && command[index + 1] === quote) {
623
+ current += quote;
624
+ index++;
625
+ }
626
+ else {
627
+ current += character;
628
+ }
629
+ continue;
630
+ }
631
+ if (character === '\r' || character === '\n' || ';|&<>`'.includes(character)) {
632
+ return undefined;
633
+ }
634
+ if (/\s/u.test(character)) {
635
+ if (tokenStarted) {
636
+ tokens.push(current);
637
+ current = '';
638
+ tokenStarted = false;
639
+ quoteClosed = false;
640
+ }
641
+ continue;
642
+ }
643
+ if (quoteClosed)
644
+ return undefined;
645
+ if (character === '"' || character === "'") {
646
+ if (tokenStarted)
647
+ return undefined;
648
+ quote = character;
649
+ tokenStarted = true;
650
+ continue;
651
+ }
652
+ current += character;
653
+ tokenStarted = true;
654
+ }
655
+ if (quote)
656
+ return undefined;
657
+ if (tokenStarted)
658
+ tokens.push(current);
659
+ return tokens;
660
+ }
566
661
  function isManagedHookCommand(command, scriptRelPaths) {
567
662
  if (typeof command !== 'string')
568
663
  return false;
569
664
  // Match both the current `node .../comet-hook-guard.mjs` form and the legacy
570
665
  // `bash .../comet-hook-guard.sh` form so uninstall also cleans up hooks
571
666
  // written by older Comet releases. Compare basenames without extension.
572
- const commandPath = command
573
- .trim()
574
- .match(/^(?:node|bash|sh)\s+["']?([^"'\s]+)["']?(?:\s|$)/)?.[1]
575
- ?.replace(/\\/g, '/');
576
- if (!commandPath)
667
+ const tokens = parseCommandTokens(command.trim());
668
+ if (!tokens || tokens.length < 2 || !['node', 'bash', 'sh'].includes(tokens[0]))
577
669
  return false;
670
+ const commandPath = tokens[1].replace(/\\/g, '/');
578
671
  const normalize = (value) => value.replace(/\.(?:sh|mjs)$/u, '');
579
672
  return scriptRelPaths.some((scriptRelPath) => normalize(commandPath).endsWith(`/skills/${normalize(scriptRelPath.replace(/\\/g, '/'))}`));
580
673
  }
581
674
  function mergeHookGroups(existingGroups, newGroups, scriptRelPaths) {
582
- const mergedGroups = existingGroups.flatMap((group) => {
583
- if (!Array.isArray(group.hooks))
584
- return [group];
585
- const hooks = group.hooks.filter((hook) => !isManagedHookCommand(hook.command, scriptRelPaths));
586
- if (hooks.length === 0 && group.hooks.length > 0)
587
- return [];
588
- return [{ ...group, hooks }];
675
+ const mergedGroups = existingGroups.map((group) => {
676
+ if (!group || typeof group !== 'object' || Array.isArray(group))
677
+ return group;
678
+ const record = group;
679
+ if (!Array.isArray(record.hooks))
680
+ return record;
681
+ const hooks = record.hooks.filter((hook) => {
682
+ const command = hook && typeof hook === 'object' ? hook.command : undefined;
683
+ return !isManagedHookCommand(command, scriptRelPaths);
684
+ });
685
+ return { ...record, hooks };
589
686
  });
590
687
  for (const newGroup of newGroups) {
591
- const existingGroup = mergedGroups.find((group) => group.matcher === newGroup.matcher && Array.isArray(group.hooks));
592
- if (existingGroup) {
593
- existingGroup.hooks = [...existingGroup.hooks, ...newGroup.hooks];
688
+ const existingGroupIndex = mergedGroups.findIndex((group) => Boolean(group) &&
689
+ typeof group === 'object' &&
690
+ !Array.isArray(group) &&
691
+ group.matcher === newGroup.matcher &&
692
+ Array.isArray(group.hooks));
693
+ if (existingGroupIndex >= 0) {
694
+ const existingGroup = mergedGroups[existingGroupIndex];
695
+ mergedGroups[existingGroupIndex] = {
696
+ ...existingGroup,
697
+ hooks: [...existingGroup.hooks, ...newGroup.hooks],
698
+ };
594
699
  }
595
700
  else {
596
701
  mergedGroups.push(newGroup);
@@ -606,28 +711,77 @@ function mergeHookGroups(existingGroups, newGroups, scriptRelPaths) {
606
711
  function asHookGroup(value) {
607
712
  return Array.isArray(value) ? value : [];
608
713
  }
609
- async function readSettingsJsonObject(settingsPath, hookFormat) {
714
+ async function removeManagedHooksFromJsonFile(settingsPath, scriptRelPaths) {
610
715
  if (!(await fileExists(settingsPath)))
611
- return {};
716
+ return { removed: 0, failed: 0 };
717
+ let source;
612
718
  try {
613
- const parsed = JSON.parse(await readFile(settingsPath, 'utf-8'));
614
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
615
- throw new Error('expected a JSON object');
616
- }
617
- return parsed;
719
+ source = await readFile(settingsPath, 'utf-8');
720
+ }
721
+ catch {
722
+ return { removed: 0, failed: 1 };
618
723
  }
619
- catch (error) {
620
- throw new Error(`Invalid ${hookFormat} settings at ${settingsPath}: ${error.message}`, {
621
- cause: error,
724
+ let settings;
725
+ try {
726
+ const parsed = JSON.parse(source);
727
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
728
+ return { removed: 0, failed: 1 };
729
+ }
730
+ settings = parsed;
731
+ }
732
+ catch {
733
+ return { removed: 0, failed: 1 };
734
+ }
735
+ const existingHooks = settings.hooks;
736
+ const existingPreToolUse = existingHooks?.PreToolUse;
737
+ if (!existingHooks || !Array.isArray(existingPreToolUse)) {
738
+ return { removed: 0, failed: 0 };
739
+ }
740
+ let removed = 0;
741
+ const filtered = existingPreToolUse.map((group) => {
742
+ if (!group || typeof group !== 'object')
743
+ return group;
744
+ const record = group;
745
+ if (!Array.isArray(record.hooks))
746
+ return record;
747
+ const handlers = record.hooks.filter((handler) => {
748
+ const command = handler && typeof handler === 'object'
749
+ ? handler.command
750
+ : undefined;
751
+ const managed = isManagedHookCommand(command, scriptRelPaths);
752
+ if (managed)
753
+ removed++;
754
+ return !managed;
622
755
  });
756
+ return { ...record, hooks: handlers };
757
+ });
758
+ if (removed === 0)
759
+ return { removed: 0, failed: 0 };
760
+ existingHooks.PreToolUse = filtered;
761
+ try {
762
+ await writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
623
763
  }
764
+ catch {
765
+ return { removed: 0, failed: 1 };
766
+ }
767
+ return { removed, failed: 0 };
768
+ }
769
+ async function readSettingsJsonObject(settingsPath, platformName) {
770
+ const result = await readJsonObjectFile(settingsPath);
771
+ if (result.status === 'missing')
772
+ return {};
773
+ if (result.status === 'present')
774
+ return result.value;
775
+ throw new Error(`Invalid ${platformName} settings at ${settingsPath}: ${result.error.message}`, {
776
+ cause: result.error,
777
+ });
624
778
  }
625
779
  /**
626
- * Claude Code, Codex, Amazon Q format:
627
- * Writes to settings.local.json with { hooks: { PreToolUse: [...] } }
780
+ * Claude-shaped JSON format used by Claude Code, Codex, and Amazon Q.
781
+ * Defaults to settings.local.json; platform metadata may override the filename.
628
782
  */
629
- async function installClaudeCodeHooks(baseDir, platformBase, skillsDir, hooksConfig) {
630
- const settingsPath = path.join(platformBase, 'settings.local.json');
783
+ async function installClaudeCodeHooks(baseDir, platformBase, skillsDir, hooksConfig, configFile, platformName) {
784
+ const settingsPath = path.join(platformBase, configFile);
631
785
  // Group by matcher so hooks sharing the same matcher are merged
632
786
  const matcherGroups = {};
633
787
  for (const [scriptRelPath, config] of Object.entries(hooksConfig)) {
@@ -638,28 +792,20 @@ async function installClaudeCodeHooks(baseDir, platformBase, skillsDir, hooksCon
638
792
  matcherGroups[config.matcher].push({ type: 'command', command });
639
793
  }
640
794
  const newEntries = Object.entries(matcherGroups).map(([matcher, hooks]) => ({ matcher, hooks }));
641
- let settings = {};
642
- if (await fileExists(settingsPath)) {
643
- try {
644
- settings = JSON.parse(await readFile(settingsPath, 'utf-8'));
645
- }
646
- catch {
647
- settings = {};
648
- }
649
- }
795
+ const settings = await readSettingsJsonObject(settingsPath, platformName);
650
796
  const existingHooks = settings.hooks ?? {};
651
797
  const existingPreToolUse = asHookGroup(existingHooks.PreToolUse);
652
798
  const merged = mergeHookGroups(existingPreToolUse, newEntries, Object.keys(hooksConfig));
653
799
  settings.hooks = { ...existingHooks, PreToolUse: merged };
654
800
  await ensureDir(path.dirname(settingsPath));
655
801
  await writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
656
- return { installed: true };
802
+ return { status: 'installed' };
657
803
  }
658
804
  /**
659
805
  * Qwen Code / Qoder / CodeBuddy format:
660
806
  * Writes to settings.json with { hooks: { PreToolUse: [{ matcher, hooks: [{ type, command }] }] } }
661
807
  */
662
- async function installQwenStyleHooks(baseDir, platformBase, skillsDir, hooksConfig, hookFormat) {
808
+ async function installQwenStyleHooks(baseDir, platformBase, skillsDir, hooksConfig, platformName) {
663
809
  const settingsPath = path.join(platformBase, 'settings.json');
664
810
  // Group by matcher
665
811
  const matcherGroups = {};
@@ -677,20 +823,20 @@ async function installQwenStyleHooks(baseDir, platformBase, skillsDir, hooksConf
677
823
  matcher,
678
824
  hooks,
679
825
  }));
680
- const settings = await readSettingsJsonObject(settingsPath, hookFormat);
826
+ const settings = await readSettingsJsonObject(settingsPath, platformName);
681
827
  const existingHooks = settings.hooks ?? {};
682
828
  const existingPreToolUse = asHookGroup(existingHooks.PreToolUse);
683
829
  const merged = mergeHookGroups(existingPreToolUse, preToolUseEntries, Object.keys(hooksConfig));
684
830
  settings.hooks = { ...existingHooks, PreToolUse: merged };
685
831
  await ensureDir(path.dirname(settingsPath));
686
832
  await writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
687
- return { installed: true };
833
+ return { status: 'installed' };
688
834
  }
689
835
  /**
690
836
  * Gemini CLI format:
691
837
  * Writes to .gemini/settings.json with { hooks: { BeforeTool: [{ matcher, hooks: [{ type, command }] }] } }
692
838
  */
693
- async function installGeminiHooks(baseDir, platformBase, skillsDir, hooksConfig) {
839
+ async function installGeminiHooks(baseDir, platformBase, skillsDir, hooksConfig, platformName) {
694
840
  const settingsPath = path.join(platformBase, 'settings.json');
695
841
  const entries = [];
696
842
  for (const [scriptRelPath, config] of Object.entries(hooksConfig)) {
@@ -705,28 +851,20 @@ async function installGeminiHooks(baseDir, platformBase, skillsDir, hooksConfig)
705
851
  ],
706
852
  });
707
853
  }
708
- let settings = {};
709
- if (await fileExists(settingsPath)) {
710
- try {
711
- settings = JSON.parse(await readFile(settingsPath, 'utf-8'));
712
- }
713
- catch {
714
- settings = {};
715
- }
716
- }
854
+ const settings = await readSettingsJsonObject(settingsPath, platformName);
717
855
  const existingHooks = settings.hooks ?? {};
718
856
  const existingBeforeTool = asHookGroup(existingHooks.BeforeTool);
719
857
  const merged = mergeHookGroups(existingBeforeTool, entries, Object.keys(hooksConfig));
720
858
  settings.hooks = { ...existingHooks, BeforeTool: merged };
721
859
  await ensureDir(path.dirname(settingsPath));
722
860
  await writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
723
- return { installed: true };
861
+ return { status: 'installed' };
724
862
  }
725
863
  /**
726
864
  * Windsurf format:
727
865
  * Writes to .windsurf/hooks.json with { hooks: { pre_write_code: [{ command }] } }
728
866
  */
729
- async function installWindsurfHooks(baseDir, platformBase, skillsDir, hooksConfig) {
867
+ async function installWindsurfHooks(baseDir, platformBase, skillsDir, hooksConfig, platformName) {
730
868
  const hooksPath = path.join(platformBase, 'hooks.json');
731
869
  const entries = [];
732
870
  for (const [scriptRelPath] of Object.entries(hooksConfig)) {
@@ -735,23 +873,18 @@ async function installWindsurfHooks(baseDir, platformBase, skillsDir, hooksConfi
735
873
  show_output: true,
736
874
  });
737
875
  }
738
- let hooksFile = {};
739
- if (await fileExists(hooksPath)) {
740
- try {
741
- hooksFile = JSON.parse(await readFile(hooksPath, 'utf-8'));
742
- }
743
- catch {
744
- hooksFile = {};
745
- }
746
- }
876
+ const hooksFile = await readSettingsJsonObject(hooksPath, platformName);
747
877
  const existingHooks = hooksFile.hooks ?? {};
748
878
  const existingPreWrite = asHookGroup(existingHooks.pre_write_code);
749
- const merged = existingPreWrite.filter((entry) => !isManagedHookCommand(entry.command, Object.keys(hooksConfig)));
879
+ const merged = existingPreWrite.filter((entry) => {
880
+ const command = entry && typeof entry === 'object' ? entry.command : undefined;
881
+ return !isManagedHookCommand(command, Object.keys(hooksConfig));
882
+ });
750
883
  merged.push(...entries);
751
884
  hooksFile.hooks = { ...existingHooks, pre_write_code: merged };
752
885
  await ensureDir(path.dirname(hooksPath));
753
886
  await writeFile(hooksPath, JSON.stringify(hooksFile, null, 2) + '\n', 'utf-8');
754
- return { installed: true };
887
+ return { status: 'installed' };
755
888
  }
756
889
  /**
757
890
  * GitHub Copilot format:
@@ -774,7 +907,7 @@ async function installCopilotHooks(baseDir, platformBase, skillsDir, hooksConfig
774
907
  };
775
908
  await ensureDir(hooksDir);
776
909
  await writeFile(hookFilePath, JSON.stringify(hookConfig, null, 2) + '\n', 'utf-8');
777
- return { installed: true };
910
+ return { status: 'installed' };
778
911
  }
779
912
  /**
780
913
  * Kiro format:
@@ -804,7 +937,7 @@ async function installKiroHooks(baseDir, platformBase, skillsDir, hooksConfig) {
804
937
  await ensureDir(hooksDir);
805
938
  await writeFile(hookFilePath, JSON.stringify(hookConfig, null, 2) + '\n', 'utf-8');
806
939
  }
807
- return { installed: true };
940
+ return { status: 'installed' };
808
941
  }
809
942
  function managedConfigFields(language = 'en') {
810
943
  const artifactLanguage = resolveArtifactLanguage(language);
@@ -885,5 +1018,5 @@ async function createWorkingDirs(projectPath, language = 'en') {
885
1018
  await mergeProjectConfig(projectPath, language);
886
1019
  await installCometProjectInstructions(projectPath, language === 'zh-CN' ? 'zh' : 'en');
887
1020
  }
888
- export { copyCometSkillsForPlatform, copyCometRulesForPlatform, installCometHooksForPlatform, readManifest, getManagedSkillPaths, getManifestSkills, getUserFacingSkillNames, createWorkingDirs, getAssetsDir, computeRuleDestPath, formatRuleContent, isManagedHookCommand, planSkillDirectoryCopy, mergeProjectConfig, parseProjectConfigOverrides, renderProjectConfig, getCentralSkillsDir, installSkillsAsSymlink, };
1021
+ export { copyCometSkillsForPlatform, copyCometRulesForPlatform, installCometHooksForPlatform, readManifest, getManagedSkillPaths, getManifestSkills, getUserFacingSkillNames, createWorkingDirs, getAssetsDir, computeRuleDestPath, formatRuleContent, isManagedHookCommand, buildHookCommand, removeManagedHooksFromJsonFile, planSkillDirectoryCopy, mergeProjectConfig, parseProjectConfigOverrides, renderProjectConfig, getCentralSkillsDir, installSkillsAsSymlink, };
889
1022
  //# sourceMappingURL=platform-install.js.map