agents-inc 0.153.0 → 0.155.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 (218) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/dist/chunk-2H7UWE2G.js +246 -0
  3. package/dist/chunk-2H7UWE2G.js.map +1 -0
  4. package/dist/{chunk-ZNDLUWWD.js → chunk-4A4L3YIW.js} +5 -5
  5. package/dist/chunk-4G2YHZIM.js +132 -0
  6. package/dist/chunk-4G2YHZIM.js.map +1 -0
  7. package/dist/{chunk-WDAO3RUY.js → chunk-5F2YR5XV.js} +3 -3
  8. package/dist/{chunk-JFAUQDZM.js → chunk-5OKLCVM4.js} +3 -3
  9. package/dist/{chunk-REKOWTIB.js → chunk-5QZAK6J7.js} +44 -10
  10. package/dist/chunk-5QZAK6J7.js.map +1 -0
  11. package/dist/{chunk-VECFAKM5.js → chunk-6RLV45HN.js} +10 -10
  12. package/dist/{chunk-VECFAKM5.js.map → chunk-6RLV45HN.js.map} +1 -1
  13. package/dist/{chunk-K7PJDWER.js → chunk-CLFHC2DK.js} +15 -106
  14. package/dist/chunk-CLFHC2DK.js.map +1 -0
  15. package/dist/chunk-D2CAV7MG.js +61 -0
  16. package/dist/chunk-D2CAV7MG.js.map +1 -0
  17. package/dist/{chunk-NXGMM5YN.js → chunk-F2AMBBIA.js} +6 -6
  18. package/dist/{chunk-NXGMM5YN.js.map → chunk-F2AMBBIA.js.map} +1 -1
  19. package/dist/{chunk-HEBPKK5L.js → chunk-F5T6TV4Z.js} +213 -92
  20. package/dist/chunk-F5T6TV4Z.js.map +1 -0
  21. package/dist/{chunk-K3O3YQYG.js → chunk-G47TDUK3.js} +2 -2
  22. package/dist/{chunk-U2DNAHFE.js → chunk-GS2NZ3GD.js} +8 -8
  23. package/dist/chunk-GS2NZ3GD.js.map +1 -0
  24. package/dist/{chunk-ORJ5Y222.js → chunk-H6UTKTLK.js} +176 -256
  25. package/dist/chunk-H6UTKTLK.js.map +1 -0
  26. package/dist/{chunk-M5HSTNYU.js → chunk-HG4JPXJT.js} +11 -29
  27. package/dist/chunk-HG4JPXJT.js.map +1 -0
  28. package/dist/{chunk-VGEBYCQZ.js → chunk-HTYH7CKN.js} +4 -15
  29. package/dist/chunk-HTYH7CKN.js.map +1 -0
  30. package/dist/{chunk-JC7DBPRD.js → chunk-HXKOIWLU.js} +2 -2
  31. package/dist/chunk-HXKOIWLU.js.map +1 -0
  32. package/dist/{chunk-NSJ2S7R5.js → chunk-ILUVL7WM.js} +2 -2
  33. package/dist/{chunk-R54KIGN2.js → chunk-IO5YBKHV.js} +13 -2
  34. package/dist/chunk-IO5YBKHV.js.map +1 -0
  35. package/dist/chunk-IXRZAHZF.js +53 -0
  36. package/dist/chunk-IXRZAHZF.js.map +1 -0
  37. package/dist/chunk-JCPANDNR.js +35 -0
  38. package/dist/chunk-JCPANDNR.js.map +1 -0
  39. package/dist/{chunk-R4SEU3DF.js → chunk-LJCNUP6M.js} +2 -2
  40. package/dist/{chunk-FRWKCE6R.js → chunk-MHRC3KRX.js} +5 -5
  41. package/dist/chunk-MHRC3KRX.js.map +1 -0
  42. package/dist/chunk-MO5FGLVS.js +142 -0
  43. package/dist/chunk-MO5FGLVS.js.map +1 -0
  44. package/dist/{chunk-Q5FLW4JG.js → chunk-OKH64JZE.js} +2 -2
  45. package/dist/{chunk-WG6V6VJV.js → chunk-PCC4LBKB.js} +2 -2
  46. package/dist/chunk-PMUABBPG.js +258 -0
  47. package/dist/chunk-PMUABBPG.js.map +1 -0
  48. package/dist/{chunk-EFU6P3B7.js → chunk-PNMM5HNX.js} +5 -5
  49. package/dist/{chunk-4VYALFQZ.js → chunk-QWVXOCXO.js} +6 -6
  50. package/dist/{chunk-35VOK7XE.js → chunk-RWOYPOMR.js} +4365 -4355
  51. package/dist/chunk-RWOYPOMR.js.map +1 -0
  52. package/dist/{chunk-IO4VEW5U.js → chunk-SY4X2LND.js} +4 -4
  53. package/dist/{chunk-XGMOXMS4.js → chunk-TBOVYLQJ.js} +2 -2
  54. package/dist/{chunk-BMGBA6HJ.js → chunk-TNGMF4UX.js} +20 -20
  55. package/dist/{chunk-BMGBA6HJ.js.map → chunk-TNGMF4UX.js.map} +1 -1
  56. package/dist/{chunk-TY6CAOL7.js → chunk-UMH2YZKG.js} +14 -5
  57. package/dist/chunk-UMH2YZKG.js.map +1 -0
  58. package/dist/{chunk-6CNRMCFM.js → chunk-VPASBA7P.js} +2 -2
  59. package/dist/{chunk-NXQAY5U7.js → chunk-WCAUUBHO.js} +5860 -5126
  60. package/dist/chunk-WCAUUBHO.js.map +1 -0
  61. package/dist/{chunk-V3ENMW7G.js → chunk-WFRTHH3A.js} +3 -3
  62. package/dist/{chunk-2HC2SNRP.js → chunk-WTJ3AY33.js} +1 -5
  63. package/dist/chunk-WTJ3AY33.js.map +1 -0
  64. package/dist/{chunk-F7Z5I6GU.js → chunk-WUQ3MTPF.js} +11 -5
  65. package/dist/{chunk-F7Z5I6GU.js.map → chunk-WUQ3MTPF.js.map} +1 -1
  66. package/dist/{chunk-U2YSGBHA.js → chunk-XAXJWXI4.js} +93 -43
  67. package/dist/chunk-XAXJWXI4.js.map +1 -0
  68. package/dist/{chunk-ACKC577F.js → chunk-Y5Y7XHVU.js} +4 -4
  69. package/dist/{chunk-X5XJ6WKO.js → chunk-YO2LTXB4.js} +107 -16
  70. package/dist/chunk-YO2LTXB4.js.map +1 -0
  71. package/dist/{chunk-FIHTAFST.js → chunk-ZUAZHDKP.js} +64 -65
  72. package/dist/chunk-ZUAZHDKP.js.map +1 -0
  73. package/dist/commands/build/marketplace.js +98 -120
  74. package/dist/commands/build/marketplace.js.map +1 -1
  75. package/dist/commands/build/plugins.js +8 -8
  76. package/dist/commands/compile.js +57 -24
  77. package/dist/commands/compile.js.map +1 -1
  78. package/dist/commands/doctor.js +220 -79
  79. package/dist/commands/doctor.js.map +1 -1
  80. package/dist/commands/edit.js +608 -100
  81. package/dist/commands/edit.js.map +1 -1
  82. package/dist/commands/eject.js +26 -21
  83. package/dist/commands/eject.js.map +1 -1
  84. package/dist/commands/init.js +41 -39
  85. package/dist/commands/list.js +11 -11
  86. package/dist/commands/list.js.map +1 -1
  87. package/dist/commands/new/marketplace.js +243 -0
  88. package/dist/commands/new/marketplace.js.map +1 -0
  89. package/dist/commands/search.js +34 -24
  90. package/dist/commands/search.js.map +1 -1
  91. package/dist/commands/share.js +67 -0
  92. package/dist/commands/share.js.map +1 -0
  93. package/dist/commands/uninstall.js +234 -196
  94. package/dist/commands/uninstall.js.map +1 -1
  95. package/dist/commands/update.js +9 -9
  96. package/dist/commands/update.js.map +1 -1
  97. package/dist/components/common/prompt-confirm.js +1 -1
  98. package/dist/components/common/removal-plan-confirm.js +10 -0
  99. package/dist/components/common/select-list.js +2 -2
  100. package/dist/components/wizard/category-grid.js +5 -5
  101. package/dist/components/wizard/checkbox-grid.js +5 -5
  102. package/dist/components/wizard/domain-selection.js +11 -11
  103. package/dist/components/wizard/run-wizard-session.js +29 -28
  104. package/dist/components/wizard/scroll-affordance.js +2 -2
  105. package/dist/components/wizard/skill-agent-summary.js +6 -6
  106. package/dist/components/wizard/source-grid.js +6 -6
  107. package/dist/components/wizard/stack-selection.js +8 -8
  108. package/dist/components/wizard/step-agents.js +10 -11
  109. package/dist/components/wizard/step-build.js +9 -9
  110. package/dist/components/wizard/step-confirm.js +10 -10
  111. package/dist/components/wizard/step-sources.js +10 -10
  112. package/dist/components/wizard/step-stack.js +9 -9
  113. package/dist/components/wizard/summary-panel.js +9 -9
  114. package/dist/components/wizard/toast.js +2 -2
  115. package/dist/components/wizard/wizard-layout.js +14 -14
  116. package/dist/components/wizard/wizard-tabs.js +2 -2
  117. package/dist/components/wizard/wizard.js +27 -27
  118. package/dist/copy-local-skills-NSUTBVLJ.js +14 -0
  119. package/dist/hooks/init.js +42 -62
  120. package/dist/hooks/init.js.map +1 -1
  121. package/dist/load-agent-defs-X4E57I2U.js +15 -0
  122. package/dist/{loader-4U7UCE2Y.js → loader-G6BFG2VA.js} +3 -3
  123. package/dist/recompile-project-agents-JE4MM72I.js +20 -0
  124. package/dist/{source-loader-ZNSYJJCI.js → source-loader-3DL3NYUX.js} +7 -5
  125. package/dist/source-loader-3DL3NYUX.js.map +1 -0
  126. package/dist/src/agents/developer/ai-developer/critical-reminders.md +1 -1
  127. package/dist/src/agents/developer/ai-developer/critical-requirements.md +1 -1
  128. package/dist/src/agents/developer/ai-developer/identity.md +1 -1
  129. package/dist/src/agents/developer/ai-developer/playbook.md +1 -1
  130. package/dist/src/agents/developer/api-developer/identity.md +1 -1
  131. package/dist/src/agents/developer/cli-developer/identity.md +1 -1
  132. package/dist/src/agents/developer/web-developer/identity.md +1 -1
  133. package/dist/src/agents/meta/codex-keeper/identity.md +1 -1
  134. package/dist/src/agents/meta/convention-keeper/identity.md +1 -1
  135. package/dist/src/agents/meta/skill-summoner/identity.md +1 -1
  136. package/dist/src/agents/researcher/ai-researcher/identity.md +1 -1
  137. package/dist/src/agents/researcher/api-researcher/identity.md +1 -1
  138. package/dist/src/agents/researcher/cli-researcher/identity.md +1 -1
  139. package/dist/src/agents/researcher/web-researcher/identity.md +1 -1
  140. package/dist/src/agents/reviewer/reviewer/playbook.md +1 -1
  141. package/dist/src/agents/tester/ai-tester/identity.md +1 -1
  142. package/dist/src/agents/tester/api-tester/critical-reminders.md +1 -1
  143. package/dist/src/agents/tester/api-tester/critical-requirements.md +1 -1
  144. package/dist/src/agents/tester/api-tester/identity.md +2 -2
  145. package/dist/src/agents/tester/cli-tester/identity.md +1 -1
  146. package/dist/src/agents/tester/web-tester/identity.md +3 -3
  147. package/dist/stores/wizard-store.js +7 -5
  148. package/package.json +10 -7
  149. package/src/agents/developer/ai-developer/critical-reminders.md +1 -1
  150. package/src/agents/developer/ai-developer/critical-requirements.md +1 -1
  151. package/src/agents/developer/ai-developer/identity.md +1 -1
  152. package/src/agents/developer/ai-developer/playbook.md +1 -1
  153. package/src/agents/developer/api-developer/identity.md +1 -1
  154. package/src/agents/developer/cli-developer/identity.md +1 -1
  155. package/src/agents/developer/web-developer/identity.md +1 -1
  156. package/src/agents/meta/codex-keeper/identity.md +1 -1
  157. package/src/agents/meta/convention-keeper/identity.md +1 -1
  158. package/src/agents/meta/skill-summoner/identity.md +1 -1
  159. package/src/agents/researcher/ai-researcher/identity.md +1 -1
  160. package/src/agents/researcher/api-researcher/identity.md +1 -1
  161. package/src/agents/researcher/cli-researcher/identity.md +1 -1
  162. package/src/agents/researcher/web-researcher/identity.md +1 -1
  163. package/src/agents/reviewer/reviewer/playbook.md +1 -1
  164. package/src/agents/tester/ai-tester/identity.md +1 -1
  165. package/src/agents/tester/api-tester/critical-reminders.md +1 -1
  166. package/src/agents/tester/api-tester/critical-requirements.md +1 -1
  167. package/src/agents/tester/api-tester/identity.md +2 -2
  168. package/src/agents/tester/cli-tester/identity.md +1 -1
  169. package/src/agents/tester/web-tester/identity.md +3 -3
  170. package/src/schemas/custom-metadata.schema.json +3 -0
  171. package/src/schemas/metadata.schema.json +3 -0
  172. package/src/schemas/plugin.schema.json +3 -0
  173. package/src/schemas/project-config.schema.json +4 -8
  174. package/src/schemas/project-source-config.schema.json +2 -2
  175. package/dist/chunk-2HC2SNRP.js.map +0 -1
  176. package/dist/chunk-35VOK7XE.js.map +0 -1
  177. package/dist/chunk-FIHTAFST.js.map +0 -1
  178. package/dist/chunk-FRWKCE6R.js.map +0 -1
  179. package/dist/chunk-HEBPKK5L.js.map +0 -1
  180. package/dist/chunk-JC7DBPRD.js.map +0 -1
  181. package/dist/chunk-K7PJDWER.js.map +0 -1
  182. package/dist/chunk-KET3EVGX.js +0 -41
  183. package/dist/chunk-KET3EVGX.js.map +0 -1
  184. package/dist/chunk-M5HSTNYU.js.map +0 -1
  185. package/dist/chunk-NXQAY5U7.js.map +0 -1
  186. package/dist/chunk-OJJLYHM3.js +0 -34
  187. package/dist/chunk-OJJLYHM3.js.map +0 -1
  188. package/dist/chunk-ORJ5Y222.js.map +0 -1
  189. package/dist/chunk-R54KIGN2.js.map +0 -1
  190. package/dist/chunk-REKOWTIB.js.map +0 -1
  191. package/dist/chunk-TY6CAOL7.js.map +0 -1
  192. package/dist/chunk-U2DNAHFE.js.map +0 -1
  193. package/dist/chunk-U2YSGBHA.js.map +0 -1
  194. package/dist/chunk-VGEBYCQZ.js.map +0 -1
  195. package/dist/chunk-X5XJ6WKO.js.map +0 -1
  196. package/dist/copy-local-skills-KF2RAVD3.js +0 -14
  197. package/dist/load-agent-defs-DGXGBU3Q.js +0 -15
  198. package/dist/recompile-project-agents-CJVBAWDJ.js +0 -20
  199. /package/dist/{chunk-ZNDLUWWD.js.map → chunk-4A4L3YIW.js.map} +0 -0
  200. /package/dist/{chunk-WDAO3RUY.js.map → chunk-5F2YR5XV.js.map} +0 -0
  201. /package/dist/{chunk-JFAUQDZM.js.map → chunk-5OKLCVM4.js.map} +0 -0
  202. /package/dist/{chunk-K3O3YQYG.js.map → chunk-G47TDUK3.js.map} +0 -0
  203. /package/dist/{chunk-NSJ2S7R5.js.map → chunk-ILUVL7WM.js.map} +0 -0
  204. /package/dist/{chunk-R4SEU3DF.js.map → chunk-LJCNUP6M.js.map} +0 -0
  205. /package/dist/{chunk-Q5FLW4JG.js.map → chunk-OKH64JZE.js.map} +0 -0
  206. /package/dist/{chunk-WG6V6VJV.js.map → chunk-PCC4LBKB.js.map} +0 -0
  207. /package/dist/{chunk-EFU6P3B7.js.map → chunk-PNMM5HNX.js.map} +0 -0
  208. /package/dist/{chunk-4VYALFQZ.js.map → chunk-QWVXOCXO.js.map} +0 -0
  209. /package/dist/{chunk-IO4VEW5U.js.map → chunk-SY4X2LND.js.map} +0 -0
  210. /package/dist/{chunk-XGMOXMS4.js.map → chunk-TBOVYLQJ.js.map} +0 -0
  211. /package/dist/{chunk-6CNRMCFM.js.map → chunk-VPASBA7P.js.map} +0 -0
  212. /package/dist/{chunk-V3ENMW7G.js.map → chunk-WFRTHH3A.js.map} +0 -0
  213. /package/dist/{chunk-ACKC577F.js.map → chunk-Y5Y7XHVU.js.map} +0 -0
  214. /package/dist/{copy-local-skills-KF2RAVD3.js.map → components/common/removal-plan-confirm.js.map} +0 -0
  215. /package/dist/{load-agent-defs-DGXGBU3Q.js.map → copy-local-skills-NSUTBVLJ.js.map} +0 -0
  216. /package/dist/{loader-4U7UCE2Y.js.map → load-agent-defs-X4E57I2U.js.map} +0 -0
  217. /package/dist/{recompile-project-agents-CJVBAWDJ.js.map → loader-G6BFG2VA.js.map} +0 -0
  218. /package/dist/{source-loader-ZNSYJJCI.js.map → recompile-project-agents-JE4MM72I.js.map} +0 -0
@@ -2,21 +2,23 @@
2
2
  import {
3
3
  detectProject,
4
4
  loadSource
5
- } from "../chunk-F7Z5I6GU.js";
6
- import "../chunk-REKOWTIB.js";
7
- import "../chunk-NXGMM5YN.js";
8
- import "../chunk-FRWKCE6R.js";
5
+ } from "../chunk-WUQ3MTPF.js";
6
+ import "../chunk-5QZAK6J7.js";
7
+ import "../chunk-MHRC3KRX.js";
8
+ import "../chunk-F2AMBBIA.js";
9
9
  import {
10
10
  filterExcludedEntries,
11
- listAgentMdFiles
12
- } from "../chunk-FIHTAFST.js";
11
+ listAgentMdFiles,
12
+ splitAgentsByProvenance
13
+ } from "../chunk-ZUAZHDKP.js";
13
14
  import {
14
15
  BaseCommand,
15
16
  EXIT_CODES
16
- } from "../chunk-X5XJ6WKO.js";
17
- import "../chunk-TY6CAOL7.js";
18
- import "../chunk-K3O3YQYG.js";
17
+ } from "../chunk-YO2LTXB4.js";
18
+ import "../chunk-UMH2YZKG.js";
19
+ import "../chunk-G47TDUK3.js";
19
20
  import {
21
+ ConfigDefaultExportError,
20
22
  checkMatrixHealth,
21
23
  configDirsInPlay,
22
24
  declaresNoContent,
@@ -32,13 +34,13 @@ import {
32
34
  installBaseDir,
33
35
  isHomeDirectory,
34
36
  isLocalSource,
35
- isRecord,
36
37
  listRegisteredPluginInstalls,
37
38
  loadConfig,
38
39
  loadProjectConfigFromDir,
39
40
  loadProjectSourceConfig,
40
41
  loadSkillsMatrixFromSource,
41
42
  parseMarketplacePluginRef,
43
+ readForkedFromMetadata,
42
44
  resolveInstallPaths,
43
45
  resolvePrimarySourceEntry,
44
46
  validateAgentFrontmatter,
@@ -46,7 +48,7 @@ import {
46
48
  validatePlugin,
47
49
  validateProjectConfig,
48
50
  validateSkillFrontmatter
49
- } from "../chunk-NXQAY5U7.js";
51
+ } from "../chunk-WCAUUBHO.js";
50
52
  import {
51
53
  agentYamlGenerationSchema,
52
54
  directoryExists,
@@ -54,6 +56,7 @@ import {
54
56
  formatZodErrors,
55
57
  getErrorMessage,
56
58
  glob,
59
+ isRecord,
57
60
  listDirectories,
58
61
  metadataValidationSchema,
59
62
  parseFrontmatter,
@@ -66,10 +69,10 @@ import {
66
69
  stackConfigValidationSchema,
67
70
  stacksConfigSchema,
68
71
  validateSkillMetadata
69
- } from "../chunk-U2YSGBHA.js";
72
+ } from "../chunk-XAXJWXI4.js";
70
73
  import {
71
74
  matrix
72
- } from "../chunk-35VOK7XE.js";
75
+ } from "../chunk-RWOYPOMR.js";
73
76
  import "../chunk-VASLW636.js";
74
77
  import {
75
78
  CLAUDE_SRC_DIR,
@@ -85,7 +88,7 @@ import {
85
88
  STACKS_FILE_PATH,
86
89
  STANDARD_FILES,
87
90
  UI_SYMBOLS
88
- } from "../chunk-R54KIGN2.js";
91
+ } from "../chunk-IO5YBKHV.js";
89
92
  import "../chunk-VZRO7RYT.js";
90
93
 
91
94
  // src/cli/commands/doctor.ts
@@ -160,14 +163,14 @@ function validateSkillFilePairs(skillMdDirs, metadataDirs, skillsDir) {
160
163
  }));
161
164
  return [...missingMetadata, ...missingSkillMd];
162
165
  }
163
- async function validateSource(sourcePath) {
166
+ async function validateSource(sourcePath, reader = "author") {
164
167
  const issues = [];
165
168
  const resolvedPath = path.isAbsolute(sourcePath) ? sourcePath : path.resolve(sourcePath);
166
169
  if (!await directoryExists(resolvedPath)) {
167
170
  issues.push({
168
171
  severity: "error",
169
172
  file: resolvedPath,
170
- message: "Source directory does not exist"
173
+ message: "Marketplace directory does not exist"
171
174
  });
172
175
  return buildResult(issues, 0);
173
176
  }
@@ -191,7 +194,7 @@ async function validateSource(sourcePath) {
191
194
  for (const metadataFile of validMetadataFiles) {
192
195
  issues.push(...await validateOneSkill(skillsDir, skillsDirRelPath, metadataFile));
193
196
  }
194
- issues.push(...await checkCrossReferences(resolvedPath));
197
+ issues.push(...await checkCrossReferences(resolvedPath, reader));
195
198
  const extraIssues = await Promise.all([
196
199
  validateStacks(resolvedPath),
197
200
  validateAgents(resolvedPath),
@@ -200,19 +203,16 @@ async function validateSource(sourcePath) {
200
203
  issues.push(...extraIssues.flat());
201
204
  return buildResult(issues, validMetadataFiles.length);
202
205
  }
206
+ function yamlParseFailure(error) {
207
+ return `Failed to parse YAML: ${getErrorMessage(error)}`;
208
+ }
203
209
  async function validateOneSkill(skillsDir, skillsDirRelPath, metadataFile) {
204
210
  const relPath = path.join(skillsDirRelPath, metadataFile);
205
211
  let rawMetadata;
206
212
  try {
207
213
  rawMetadata = parseYaml(await readFile(path.join(skillsDir, metadataFile)));
208
214
  } catch (error) {
209
- return [
210
- {
211
- severity: "error",
212
- file: relPath,
213
- message: `Failed to parse YAML: ${getErrorMessage(error)}`
214
- }
215
- ];
215
+ return [{ severity: "error", file: relPath, message: yamlParseFailure(error) }];
216
216
  }
217
217
  return [
218
218
  ...checkSnakeCaseKeys(rawMetadata, relPath),
@@ -230,14 +230,12 @@ function checkMetadataSchema(rawMetadata, relPath) {
230
230
  ...warnings.map((message) => ({ severity: "warning", file: relPath, message }))
231
231
  ];
232
232
  }
233
- async function checkCrossReferences(resolvedPath) {
233
+ async function checkCrossReferences(resolvedPath, reader) {
234
234
  try {
235
235
  await loadSkillsMatrixFromSource({ sourceFlag: resolvedPath, skipExtraSources: true });
236
- return checkMatrixHealth(matrix).map((healthIssue) => ({
237
- severity: healthIssue.severity,
238
- file: SKILL_CATEGORIES_PATH,
239
- message: healthIssue.details
240
- }));
236
+ return checkMatrixHealth(matrix).map(
237
+ (healthIssue) => toSourceIssue(healthIssue, resolvedPath, reader)
238
+ );
241
239
  } catch (error) {
242
240
  return [
243
241
  {
@@ -248,6 +246,42 @@ async function checkCrossReferences(resolvedPath) {
248
246
  ];
249
247
  }
250
248
  }
249
+ var SKILL_AUDIT_PATH = "src/cli/lib/configuration/skill-audit.ts";
250
+ function fileHoldingDefect(finding) {
251
+ switch (finding) {
252
+ case "category-missing-domain":
253
+ case "skill-unknown-category":
254
+ return SKILL_CATEGORIES_PATH;
255
+ case "skill-unresolved-relation-ref":
256
+ case "rule-unresolved-slug":
257
+ return SKILL_RULES_PATH;
258
+ case "audit-verdict-contradiction":
259
+ case "skill-unaudited":
260
+ return SKILL_AUDIT_PATH;
261
+ default: {
262
+ const exhaustive = finding;
263
+ return exhaustive;
264
+ }
265
+ }
266
+ }
267
+ function toSourceIssue(healthIssue, marketplacePath, reader) {
268
+ const file = fileHoldingDefect(healthIssue.finding);
269
+ if (reader === "consumer" && healthIssue.finding === "rule-unresolved-slug") {
270
+ return {
271
+ severity: "warning",
272
+ file,
273
+ message: consumedMarketplaceMessage(marketplacePath, healthIssue.details)
274
+ };
275
+ }
276
+ return {
277
+ severity: healthIssue.severity,
278
+ file,
279
+ message: healthIssue.details
280
+ };
281
+ }
282
+ function consumedMarketplaceMessage(marketplacePath, details) {
283
+ return `Marketplace '${marketplacePath}': ${details}. Nothing to fix here \u2014 the rule is that marketplace's, not this project's`;
284
+ }
251
285
  async function validateStacks(resolvedPath) {
252
286
  const stacksDir = path.join(resolvedPath, DIRS.stacks);
253
287
  if (!await directoryExists(stacksDir)) return [];
@@ -294,8 +328,8 @@ async function validateYamlFiles(opts) {
294
328
  let parsed;
295
329
  try {
296
330
  parsed = parseYaml(await readFile(absPath));
297
- } catch {
298
- issues.push({ severity: "error", file: displayPath, message: "Failed to parse YAML" });
331
+ } catch (error) {
332
+ issues.push({ severity: "error", file: displayPath, message: yamlParseFailure(error) });
299
333
  continue;
300
334
  }
301
335
  const result = opts.schema.safeParse(parsed);
@@ -316,10 +350,13 @@ async function validateTsConfig(resolvedPath, relConfigPath, schema) {
316
350
  try {
317
351
  const loaded = await loadConfig(absPath, schema);
318
352
  if (loaded === null) {
319
- return [{ severity: "error", file: relConfigPath, message: "Config has no default export" }];
353
+ return [{ severity: "error", file: relConfigPath, message: "Config is empty" }];
320
354
  }
321
355
  return [];
322
356
  } catch (error) {
357
+ if (error instanceof ConfigDefaultExportError) {
358
+ return [{ severity: "error", file: relConfigPath, message: "Config has no default export" }];
359
+ }
323
360
  return [
324
361
  { severity: "error", file: relConfigPath, message: formatLoadError(relConfigPath, error) }
325
362
  ];
@@ -339,7 +376,9 @@ function buildResult(issues, skillCount) {
339
376
  // src/cli/lib/content-validator.ts
340
377
  import os from "os";
341
378
  import path2 from "path";
379
+ import { partition } from "remeda";
342
380
  var CWD_SOURCE_NAME = "current directory";
381
+ var AGENT_FILE_EXTENSION = ".md";
343
382
  var NOTHING_VALIDATED = { count: 0, issues: [], notes: [] };
344
383
  function mergeValidations(parts) {
345
384
  return parts.reduce(
@@ -391,28 +430,34 @@ async function validateRegisteredSources(projectDir) {
391
430
  const registered = [primary];
392
431
  const remote = registered.filter((source) => !isLocalSource(source.url));
393
432
  const local = await localSourcesToValidate(projectDir, registered);
394
- const outcomes = await Promise.all(local.map(validateOneSource));
433
+ const outcomes = await Promise.all(
434
+ local.map((source) => validateOneSource(source, readerFor(projectDir, source)))
435
+ );
395
436
  return {
396
437
  count: local.length,
397
438
  issues: outcomes.flatMap((outcome) => outcome.issues),
398
439
  notes: [
399
440
  ...outcomes.map((outcome) => outcome.note),
400
- ...remote.map((source) => `${source.name} (${source.url}) \u2014 skipped (remote source)`)
441
+ ...remote.map((source) => `${source.name} (${source.url}) \u2014 skipped (remote)`)
401
442
  ]
402
443
  };
403
444
  }
404
445
  async function localSourcesToValidate(projectDir, registered) {
405
446
  const local = registered.filter((source) => isLocalSource(source.url));
406
- const alreadyCovered = local.some(
407
- (source) => path2.resolve(projectDir, source.url) === projectDir
408
- );
447
+ const alreadyCovered = local.some((source) => isCwdMarketplace(projectDir, source));
409
448
  if (alreadyCovered || !await isSourceRepo(projectDir)) return local;
410
449
  return [...local, { name: CWD_SOURCE_NAME, url: projectDir }];
411
450
  }
412
- async function validateOneSource(source) {
451
+ function isCwdMarketplace(projectDir, source) {
452
+ return path2.resolve(projectDir, source.url) === projectDir;
453
+ }
454
+ function readerFor(projectDir, source) {
455
+ return isCwdMarketplace(projectDir, source) ? "author" : "consumer";
456
+ }
457
+ async function validateOneSource(source, reader) {
413
458
  const label = `${source.name} (${source.url})`;
414
459
  try {
415
- const result = await validateSource(source.url);
460
+ const result = await validateSource(source.url, reader);
416
461
  return { note: `${label} \u2014 ${result.skillCount} skills`, issues: result.issues };
417
462
  } catch (error) {
418
463
  return {
@@ -479,10 +524,23 @@ async function findPluginDirectories(pluginsDir) {
479
524
  return checks.filter((check) => check.isPlugin).map((check) => check.name);
480
525
  }
481
526
  async function validateInstalledSkills(projectDir) {
527
+ const configuredIds = await configuredSkillIds(projectDir);
482
528
  return mergeValidations(
483
- await Promise.all(installedDirs(projectDir, "skillsDir").map(validateSkillsDirectory))
529
+ await Promise.all(
530
+ installedDirs(projectDir, "skillsDir").map(
531
+ (dir) => validateSkillsDirectory(dir, configuredIds)
532
+ )
533
+ )
484
534
  );
485
535
  }
536
+ async function configuredSkillIds(projectDir) {
537
+ const loaded = await Promise.all(configDirsInPlay(projectDir).map(loadProjectConfigFromDir));
538
+ return new Set(loaded.flatMap(configuredIdsOf));
539
+ }
540
+ function configuredIdsOf(loaded) {
541
+ if (!loaded) return [];
542
+ return loaded.config.skills.map((skill) => skill.id);
543
+ }
486
544
  async function validateInstalledAgents(projectDir) {
487
545
  return mergeValidations(
488
546
  await Promise.all(installedDirs(projectDir, "agentsDir").map(validateAgentsDirectory))
@@ -495,11 +553,22 @@ function installedDirs(projectDir, key) {
495
553
  }
496
554
  async function listInstalledArtifacts(projectDir) {
497
555
  const [skills, agents] = await Promise.all([
498
- listAcrossScopes(installedDirs(projectDir, "skillsDir"), listDirectories),
499
- listAcrossScopes(installedDirs(projectDir, "agentsDir"), listAgentMdFiles)
556
+ listAcrossScopes(installedDirs(projectDir, "skillsDir"), listSkillDirsWithOurProvenance),
557
+ listAcrossScopes(installedDirs(projectDir, "agentsDir"), listAgentFilesWithOurProvenance)
500
558
  ]);
501
559
  return { skills, agents };
502
560
  }
561
+ async function listAgentFilesWithOurProvenance(agentsDir) {
562
+ const { marked } = await splitAgentsByProvenance(agentsDir);
563
+ return marked.map((name) => `${name}${AGENT_FILE_EXTENSION}`);
564
+ }
565
+ async function listSkillDirsWithOurProvenance(skillsDir) {
566
+ const names = await listDirectories(skillsDir);
567
+ const judged = await Promise.all(
568
+ names.map(async (name) => ({ name, ours: await carriesOurProvenance(skillsDir, name) }))
569
+ );
570
+ return judged.filter((entry) => entry.ours).map((entry) => entry.name);
571
+ }
503
572
  async function listAcrossScopes(dirs, listEntries) {
504
573
  const perDir = await Promise.all(dirs.map((dir) => listDisplayPaths(dir, listEntries)));
505
574
  return perDir.flat();
@@ -509,17 +578,38 @@ async function listDisplayPaths(dir, listEntries) {
509
578
  const entries = await listEntries(dir);
510
579
  return entries.map((entry) => displayDir(path2.join(dir, entry)));
511
580
  }
512
- async function validateSkillsDirectory(skillsDir) {
581
+ async function validateSkillsDirectory(skillsDir, configuredIds) {
513
582
  if (!await directoryExists(skillsDir)) return NOTHING_VALIDATED;
514
583
  const skillDirs = await listDirectories(skillsDir);
515
584
  if (skillDirs.length === 0) return NOTHING_VALIDATED;
585
+ const judged = await Promise.all(
586
+ skillDirs.map(async (name) => ({
587
+ name,
588
+ ours: await isOurSkillDirectory(skillsDir, name, configuredIds)
589
+ }))
590
+ );
591
+ const [ours, foreign] = partition(judged, (entry) => entry.ours);
516
592
  const results = await Promise.all(
517
- skillDirs.map(async (name) => {
593
+ ours.map(async ({ name }) => {
518
594
  const skillDir = path2.join(skillsDir, name);
519
595
  return toIssues(displayDir(skillDir), await validateInstalledSkill(skillDir));
520
596
  })
521
597
  );
522
- return { count: skillDirs.length, issues: results.flat(), notes: [] };
598
+ return {
599
+ count: ours.length,
600
+ issues: results.flat(),
601
+ notes: foreign.map(({ name }) => foreignSkillNote(skillsDir, name))
602
+ };
603
+ }
604
+ async function isOurSkillDirectory(skillsDir, name, configuredIds) {
605
+ if (configuredIds.has(name)) return true;
606
+ return carriesOurProvenance(skillsDir, name);
607
+ }
608
+ async function carriesOurProvenance(skillsDir, name) {
609
+ return await readForkedFromMetadata(path2.join(skillsDir, name)) !== null;
610
+ }
611
+ function foreignSkillNote(skillsDir, name) {
612
+ return `${displayDir(path2.join(skillsDir, name))} \u2014 not installed by this CLI and named by no configuration here: not validated`;
523
613
  }
524
614
  async function validateAgentsDirectory(agentsDir) {
525
615
  if (!await directoryExists(agentsDir)) return NOTHING_VALIDATED;
@@ -776,7 +866,7 @@ function countedArtifacts(skills, agents) {
776
866
  }
777
867
  async function checkSkillsInstalled(config, projectDir) {
778
868
  const skills = config.skills;
779
- const ejectSkills = skills.filter((s) => s.source === EJECT_SOURCE);
869
+ const ejectSkills = skills.filter((s) => s.origin === EJECT_SOURCE);
780
870
  if (ejectSkills.length === 0) {
781
871
  return {
782
872
  kind: "installed",
@@ -807,7 +897,7 @@ async function checkSkillsInstalled(config, projectDir) {
807
897
  };
808
898
  }
809
899
  async function checkPluginSkillsInstalled(config, projectDir) {
810
- const pluginSkills = config.skills.filter((s) => s.source !== EJECT_SOURCE);
900
+ const pluginSkills = config.skills.filter((s) => s.origin !== EJECT_SOURCE);
811
901
  if (pluginSkills.length === 0) {
812
902
  return {
813
903
  kind: "plugins",
@@ -859,7 +949,7 @@ async function checkSourceReachable(projectDir) {
859
949
  return {
860
950
  kind: "source",
861
951
  status: "fail",
862
- message: "Failed to load source",
952
+ message: "Failed to load marketplace",
863
953
  details: [message]
864
954
  };
865
955
  }
@@ -873,10 +963,14 @@ var CONFIG_CHECK = {
873
963
  var CONTENT_CHECKS = [
874
964
  {
875
965
  kind: "content-sources",
876
- name: "Sources",
877
- noun: "source",
878
- // The registered sources are the ones config.ts names.
966
+ name: "Marketplaces",
967
+ noun: "marketplace",
968
+ // The registered marketplaces are the ones config.ts names.
879
969
  readsConfig: true,
970
+ // A marketplace whose content is broken is missing skills from the matrix every configured
971
+ // id is resolved against, so the skills row would call ids "not found" that this row has
972
+ // already accounted for.
973
+ blocks: ["skills"],
880
974
  run: validateRegisteredSources
881
975
  },
882
976
  {
@@ -884,13 +978,23 @@ var CONTENT_CHECKS = [
884
978
  name: "Plugins",
885
979
  noun: "plugin",
886
980
  readsConfig: false,
981
+ // `resolvePluginInstallPaths` swallows a registry it cannot parse and returns none, so every
982
+ // plugin-mode skill would read "no enabled plugin found" — the registry's finding, once per
983
+ // configured skill, wearing the row's words.
984
+ blocks: ["plugins"],
887
985
  run: validateInstalledPlugins
888
986
  },
889
987
  {
890
988
  kind: "content-skills",
891
989
  name: "Skills",
892
990
  noun: "skill",
893
- readsConfig: false,
991
+ // `~/.claude/skills/` is shared with everything else that installs a skill there, so which of
992
+ // its directories are this installation's is a question only the config can answer for the
993
+ // ones carrying no provenance marker. A config nobody can read leaves that unanswerable.
994
+ readsConfig: true,
995
+ // `extractLocalSkill` DROPS a skill whose metadata.yaml is missing or unusable, so a "not
996
+ // found" from the skills row would be this finding re-worded.
997
+ blocks: ["skills"],
894
998
  run: validateInstalledSkills
895
999
  },
896
1000
  {
@@ -898,6 +1002,9 @@ var CONTENT_CHECKS = [
898
1002
  name: "Agents",
899
1003
  noun: "agent",
900
1004
  readsConfig: false,
1005
+ // Nothing downstream opens an agent .md. `Agents Compiled` asks whether the file is there and
1006
+ // `No Orphans` reads the names of the files that are — broken frontmatter changes neither.
1007
+ blocks: [],
901
1008
  run: validateInstalledAgents
902
1009
  }
903
1010
  ];
@@ -929,14 +1036,17 @@ function toContentResult(check, validation) {
929
1036
  ]
930
1037
  };
931
1038
  }
932
- var CHECK_WIDTH = 20;
1039
+ var CHECK_WIDTH = 24;
933
1040
  var SECTION_INDENT = " ";
934
1041
  var ROW_INDENT = " ";
935
1042
  var SECTION_CONTENT = "Content checks";
936
1043
  var SECTION_OPERATIONAL = "Operational checks";
937
- var SKIP_AFTER_CONTENT_ERRORS = "Skipped \u2014 fix the content errors above first";
938
- var SKIP_NO_INSTALLATION = "Skipped \u2014 no installation here (skills source repository)";
1044
+ var SKIP_NO_INSTALLATION = "Skipped \u2014 no installation here (marketplace repository)";
939
1045
  var SKIP_CONFIG_UNREADABLE = "Skipped \u2014 the configuration that names them cannot be read";
1046
+ var SKIP_AFTER_CONFIG_ERROR = "Skipped \u2014 fix the content errors above first";
1047
+ function skipRestatingContent(nouns) {
1048
+ return `Skipped \u2014 this row would only restate the ${nouns.join(" and ")} errors above`;
1049
+ }
940
1050
  function plural(count, word) {
941
1051
  return `${count} ${word}${count === 1 ? "" : "s"}`;
942
1052
  }
@@ -1005,11 +1115,12 @@ var TIPS = [
1005
1115
  {
1006
1116
  // Printed beside the config tip above, which says how to get a configuration back and
1007
1117
  // nothing about the files that outlived the old one. Both halves are named with what they
1008
- // actually do: `uninstall` matches skill directories by their own `forked-from` metadata,
1009
- // so it clears them without a config — and identifies compiled agents only through one.
1118
+ // actually do: `uninstall` matches skill directories by their own `forked-from` metadata and
1119
+ // compiled agents by the marker each one carries, so neither needs the configuration that is
1120
+ // gone — which is why the row can name both and this tip can promise both.
1010
1121
  kind: "orphans-unowned",
1011
1122
  status: "fail",
1012
- tip: ` Tip: Nothing declares the files above \u2014 '${CLI_INVOKE_COMMAND} init' writes a configuration that can own them again, or '${CLI_INVOKE_COMMAND} uninstall' removes the installed skills (the compiled agents outlive it: identifying them needs the configuration that is gone)`
1123
+ tip: ` Tip: Nothing declares the files above \u2014 '${CLI_INVOKE_COMMAND} init' writes a configuration that can own them again, or '${CLI_INVOKE_COMMAND} uninstall' removes them, the compiled agents included: each file listed carries this CLI's own provenance, which is what that command reads when there is no configuration left`
1013
1124
  },
1014
1125
  {
1015
1126
  kind: "skills",
@@ -1020,9 +1131,9 @@ var TIPS = [
1020
1131
  kind: "installed",
1021
1132
  status: "warn",
1022
1133
  // No command is named on purpose: 'eject skills --force' re-copies every skill in the
1023
- // source and always targets project scope, so it cannot repair a global-scoped skill
1024
- // and it litters a plugin-mode project with local skill directories.
1025
- tip: " Tip: Re-eject the missing skills from the source to restore their files"
1134
+ // marketplace and always targets project scope, so it cannot repair a global-scoped
1135
+ // skill and it litters a plugin-mode project with local skill directories.
1136
+ tip: " Tip: Re-eject the missing skills from the marketplace to restore their files"
1026
1137
  }
1027
1138
  ];
1028
1139
  function formatTips(results) {
@@ -1034,11 +1145,19 @@ function skippedResult(kind) {
1034
1145
  return { kind, status: "skip", message: "Skipped (config invalid)" };
1035
1146
  }
1036
1147
  function sourceUnreachableSkillsResult() {
1037
- return { kind: "skills", status: "skip", message: "Skipped (source unreachable)" };
1148
+ return { kind: "skills", status: "skip", message: "Skipped (marketplace unreachable)" };
1038
1149
  }
1039
1150
  function skippedContentResult(kind) {
1040
1151
  return { kind, status: "skip", message: SKIP_CONFIG_UNREADABLE };
1041
1152
  }
1153
+ function failedContentKinds(contentResults) {
1154
+ return new Set(contentResults.filter((r) => r.status === "fail").map((r) => r.kind));
1155
+ }
1156
+ function contentBlockedResult(row, failedContent) {
1157
+ const blockingNouns = CONTENT_CHECKS.filter((check) => failedContent.has(check.kind)).filter((check) => check.blocks.includes(row)).map((check) => check.noun);
1158
+ if (blockingNouns.length === 0) return null;
1159
+ return { kind: row, status: "skip", message: skipRestatingContent(blockingNouns) };
1160
+ }
1042
1161
  function runContentCheck(check, projectDir) {
1043
1162
  return safeCheck(check.kind, async () => toContentResult(check, await check.run(projectDir)));
1044
1163
  }
@@ -1051,7 +1170,7 @@ async function safeCheck(kind, fn) {
1051
1170
  }
1052
1171
  var Doctor = class _Doctor extends BaseCommand {
1053
1172
  static summary = "Diagnose common configuration issues";
1054
- static description = `Run diagnostic checks on your ${DEFAULT_BRANDING.NAME} configuration to identify issues with config validity, skill resolution, agent compilation, and source connectivity.`;
1173
+ static description = `Run diagnostic checks on your ${DEFAULT_BRANDING.NAME} configuration to identify issues with config validity, skill resolution, agent compilation, and marketplace connectivity.`;
1055
1174
  static examples = ["<%= config.bin %> <%= command.id %>"];
1056
1175
  static flags = {};
1057
1176
  async run() {
@@ -1097,16 +1216,18 @@ var Doctor = class _Doctor extends BaseCommand {
1097
1216
  return allRows.map((row) => row.result);
1098
1217
  }
1099
1218
  /**
1100
- * The operational layer runs only when it can say something the reader can act on:
1101
- * not after content errors (its findings would be their cascades), and not in a
1102
- * source repository with nothing installed (a marketplace author has no install
1103
- * for it to describe).
1219
+ * The whole layer stands down for the two findings that leave every row with nothing to say: a
1220
+ * config nobody can read — every row is read out of it, so all of them would be cascades of that
1221
+ * one file — and a marketplace repository with nothing installed, where a marketplace author has
1222
+ * no install for the layer to describe. Every other content error is scoped to the rows that
1223
+ * read what it is about; {@link GatedContentCheck} says which those are, per pass.
1104
1224
  */
1105
1225
  async runOperationalChecks(projectDir, contentResults) {
1106
1226
  this.log("");
1107
1227
  this.log(`${SECTION_INDENT}${SECTION_OPERATIONAL}`);
1108
- if (contentResults.some((r) => r.status === "fail")) {
1109
- this.log(`${ROW_INDENT}${SKIP_AFTER_CONTENT_ERRORS}`);
1228
+ const failedContent = failedContentKinds(contentResults);
1229
+ if (failedContent.has(CONFIG_CHECK.kind)) {
1230
+ this.log(`${ROW_INDENT}${SKIP_AFTER_CONFIG_ERROR}`);
1110
1231
  return [];
1111
1232
  }
1112
1233
  const detected = await detectProject(projectDir);
@@ -1114,7 +1235,8 @@ var Doctor = class _Doctor extends BaseCommand {
1114
1235
  this.log(`${ROW_INDENT}${SKIP_NO_INSTALLATION}`);
1115
1236
  return [];
1116
1237
  }
1117
- return this.runAllChecks(projectDir, await resolveConfigState(detected, projectDir));
1238
+ const configState = await resolveConfigState(detected, projectDir);
1239
+ return this.runAllChecks(projectDir, configState, failedContent);
1118
1240
  }
1119
1241
  /**
1120
1242
  * A config file that exists but failed to load also detects as "no project", and
@@ -1125,12 +1247,17 @@ var Doctor = class _Doctor extends BaseCommand {
1125
1247
  if (await fileExists(getProjectConfigPath(projectDir))) return false;
1126
1248
  return isSourceRepo(projectDir);
1127
1249
  }
1128
- async runAllChecks(projectDir, configState) {
1250
+ async runAllChecks(projectDir, configState, failedContent) {
1129
1251
  const { result: configResult, config } = checkConfigValid(configState);
1130
1252
  this.logCheck("Config Valid", configResult);
1131
1253
  const sourceResult = await safeCheck("source", () => checkSourceReachable(projectDir));
1132
1254
  const filteredConfig = config ? filterExcludedEntries(config) : null;
1133
- const skillsResult = await this.resolveSkillsCheck(config, sourceResult, projectDir);
1255
+ const skillsResult = await this.resolveSkillsCheck(
1256
+ config,
1257
+ sourceResult,
1258
+ projectDir,
1259
+ failedContent
1260
+ );
1134
1261
  this.logCheck("Skills Resolved", skillsResult);
1135
1262
  const agentsResult = filteredConfig ? await safeCheck("agents", () => checkAgentsCompiled(filteredConfig, projectDir)) : skippedResult("agents");
1136
1263
  this.logCheck("Agents Compiled", agentsResult);
@@ -1138,9 +1265,9 @@ var Doctor = class _Doctor extends BaseCommand {
1138
1265
  this.logCheck("No Orphans", orphansResult);
1139
1266
  const installedResult = filteredConfig ? await safeCheck("installed", () => checkSkillsInstalled(filteredConfig, projectDir)) : skippedResult("installed");
1140
1267
  this.logCheck("Skills Installed", installedResult);
1141
- const pluginsResult = filteredConfig ? await safeCheck("plugins", () => checkPluginSkillsInstalled(filteredConfig, projectDir)) : skippedResult("plugins");
1268
+ const pluginsResult = await this.resolvePluginsCheck(filteredConfig, projectDir, failedContent);
1142
1269
  this.logCheck("Plugins Installed", pluginsResult);
1143
- this.logCheck("Source Reachable", sourceResult);
1270
+ this.logCheck("Marketplace Reachable", sourceResult);
1144
1271
  return [
1145
1272
  configResult,
1146
1273
  skillsResult,
@@ -1166,15 +1293,29 @@ var Doctor = class _Doctor extends BaseCommand {
1166
1293
  return skippedResult("orphans");
1167
1294
  }
1168
1295
  /**
1169
- * The skills row, and the two states in which it cannot be computed: no config to read the
1170
- * skills out of, and a source that never loaded — so there is no populated matrix to resolve
1171
- * them against, and every configured skill would be reported "not found".
1296
+ * The skills row, and the three states in which it cannot be computed: no config to read the
1297
+ * skills out of, content it resolves against that the layer above has already reported broken,
1298
+ * and a marketplace that never loaded — so there is no populated matrix to resolve them
1299
+ * against, and every configured skill would be reported "not found".
1172
1300
  */
1173
- async resolveSkillsCheck(config, sourceResult, projectDir) {
1301
+ async resolveSkillsCheck(config, sourceResult, projectDir, failedContent) {
1174
1302
  if (!config) return skippedResult("skills");
1303
+ const blocked = contentBlockedResult("skills", failedContent);
1304
+ if (blocked) return blocked;
1175
1305
  if (sourceResult.status === "fail") return sourceUnreachableSkillsResult();
1176
1306
  return safeCheck("skills", () => checkSkillsResolved(config, matrix, projectDir));
1177
1307
  }
1308
+ /**
1309
+ * The plugin row, and the two states in which it cannot be computed: no config to read the
1310
+ * plugin-mode skills out of, and a plugin registry the layer above could not parse — which
1311
+ * resolves as no installs at all, so every configured skill would be reported missing.
1312
+ */
1313
+ async resolvePluginsCheck(config, projectDir, failedContent) {
1314
+ if (!config) return skippedResult("plugins");
1315
+ const blocked = contentBlockedResult("plugins", failedContent);
1316
+ if (blocked) return blocked;
1317
+ return safeCheck("plugins", () => checkPluginSkillsInstalled(config, projectDir));
1318
+ }
1178
1319
  logCheck(name, result) {
1179
1320
  for (const line of formatCheckLine(name, result)) {
1180
1321
  this.log(line);