@nebulacomponents/citable 0.1.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 (586) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/LICENSE +201 -0
  3. package/README.md +150 -0
  4. package/cli/bin/citable.js +5 -0
  5. package/dist/universal/.agents/skills/citable/SKILL.md +137 -0
  6. package/dist/universal/.agents/skills/citable/VERSION +1 -0
  7. package/dist/universal/.agents/skills/citable/commands/README.md +36 -0
  8. package/dist/universal/.agents/skills/citable/commands/ingest.md +44 -0
  9. package/dist/universal/.agents/skills/citable/commands/measurement.md +49 -0
  10. package/dist/universal/.agents/skills/citable/commands/optimize-page.md +53 -0
  11. package/dist/universal/.agents/skills/citable/commands/page-work.md +48 -0
  12. package/dist/universal/.agents/skills/citable/commands/validate-render.md +24 -0
  13. package/dist/universal/.agents/skills/citable/manifest.json +54 -0
  14. package/dist/universal/.agents/skills/citable/policies/crawler-policy-template.md +47 -0
  15. package/dist/universal/.agents/skills/citable/references/anti-patterns.md +61 -0
  16. package/dist/universal/.agents/skills/citable/rubrics/README.md +30 -0
  17. package/dist/universal/.agents/skills/citable/rubrics/answer-extractability.md +44 -0
  18. package/dist/universal/.agents/skills/citable/rubrics/claim-boundedness.md +36 -0
  19. package/dist/universal/.agents/skills/citable/rubrics/comparison-fairness.md +30 -0
  20. package/dist/universal/.agents/skills/citable/rubrics/conversion-alignment.md +37 -0
  21. package/dist/universal/.agents/skills/citable/rubrics/entity-clarity.md +41 -0
  22. package/dist/universal/.agents/skills/citable/rubrics/evidence-strength.md +53 -0
  23. package/dist/universal/.agents/skills/citable/rubrics/information-gain.md +33 -0
  24. package/dist/universal/.agents/skills/citable/rubrics/intent-alignment.md +41 -0
  25. package/dist/universal/.agents/skills/citable/rubrics/narrative-accuracy.md +46 -0
  26. package/dist/universal/.agents/skills/citable/rubrics/recommendation-eligibility.md +36 -0
  27. package/dist/universal/.agents/skills/citable/rubrics/semantic-clarity.md +42 -0
  28. package/dist/universal/.agents/skills/citable/rubrics/source-authority.md +37 -0
  29. package/dist/universal/.agents/skills/citable/schemas/claim.schema.json +53 -0
  30. package/dist/universal/.agents/skills/citable/schemas/common.defs.json +33 -0
  31. package/dist/universal/.agents/skills/citable/schemas/competitor.schema.json +28 -0
  32. package/dist/universal/.agents/skills/citable/schemas/config.schema.json +41 -0
  33. package/dist/universal/.agents/skills/citable/schemas/crawler.schema.json +33 -0
  34. package/dist/universal/.agents/skills/citable/schemas/detector-quality.schema.json +39 -0
  35. package/dist/universal/.agents/skills/citable/schemas/entity.schema.json +39 -0
  36. package/dist/universal/.agents/skills/citable/schemas/evidence.schema.json +41 -0
  37. package/dist/universal/.agents/skills/citable/schemas/experiment.schema.json +37 -0
  38. package/dist/universal/.agents/skills/citable/schemas/finding.schema.json +101 -0
  39. package/dist/universal/.agents/skills/citable/schemas/page.schema.json +40 -0
  40. package/dist/universal/.agents/skills/citable/schemas/project.schema.json +29 -0
  41. package/dist/universal/.agents/skills/citable/schemas/prompt-result.schema.json +37 -0
  42. package/dist/universal/.agents/skills/citable/schemas/prompt.schema.json +41 -0
  43. package/dist/universal/.agents/skills/citable/schemas/query.schema.json +43 -0
  44. package/dist/universal/.agents/skills/citable/schemas/run.schema.json +36 -0
  45. package/dist/universal/.agents/skills/citable/scripts/README.md +4 -0
  46. package/dist/universal/.agents/skills/citable/templates/finding-review.md +54 -0
  47. package/dist/universal/.claude/skills/citable/SKILL.md +137 -0
  48. package/dist/universal/.claude/skills/citable/VERSION +1 -0
  49. package/dist/universal/.claude/skills/citable/commands/README.md +36 -0
  50. package/dist/universal/.claude/skills/citable/commands/ingest.md +44 -0
  51. package/dist/universal/.claude/skills/citable/commands/measurement.md +49 -0
  52. package/dist/universal/.claude/skills/citable/commands/optimize-page.md +53 -0
  53. package/dist/universal/.claude/skills/citable/commands/page-work.md +48 -0
  54. package/dist/universal/.claude/skills/citable/commands/validate-render.md +24 -0
  55. package/dist/universal/.claude/skills/citable/manifest.json +54 -0
  56. package/dist/universal/.claude/skills/citable/policies/crawler-policy-template.md +47 -0
  57. package/dist/universal/.claude/skills/citable/references/anti-patterns.md +61 -0
  58. package/dist/universal/.claude/skills/citable/rubrics/README.md +30 -0
  59. package/dist/universal/.claude/skills/citable/rubrics/answer-extractability.md +44 -0
  60. package/dist/universal/.claude/skills/citable/rubrics/claim-boundedness.md +36 -0
  61. package/dist/universal/.claude/skills/citable/rubrics/comparison-fairness.md +30 -0
  62. package/dist/universal/.claude/skills/citable/rubrics/conversion-alignment.md +37 -0
  63. package/dist/universal/.claude/skills/citable/rubrics/entity-clarity.md +41 -0
  64. package/dist/universal/.claude/skills/citable/rubrics/evidence-strength.md +53 -0
  65. package/dist/universal/.claude/skills/citable/rubrics/information-gain.md +33 -0
  66. package/dist/universal/.claude/skills/citable/rubrics/intent-alignment.md +41 -0
  67. package/dist/universal/.claude/skills/citable/rubrics/narrative-accuracy.md +46 -0
  68. package/dist/universal/.claude/skills/citable/rubrics/recommendation-eligibility.md +36 -0
  69. package/dist/universal/.claude/skills/citable/rubrics/semantic-clarity.md +42 -0
  70. package/dist/universal/.claude/skills/citable/rubrics/source-authority.md +37 -0
  71. package/dist/universal/.claude/skills/citable/schemas/claim.schema.json +53 -0
  72. package/dist/universal/.claude/skills/citable/schemas/common.defs.json +33 -0
  73. package/dist/universal/.claude/skills/citable/schemas/competitor.schema.json +28 -0
  74. package/dist/universal/.claude/skills/citable/schemas/config.schema.json +41 -0
  75. package/dist/universal/.claude/skills/citable/schemas/crawler.schema.json +33 -0
  76. package/dist/universal/.claude/skills/citable/schemas/detector-quality.schema.json +39 -0
  77. package/dist/universal/.claude/skills/citable/schemas/entity.schema.json +39 -0
  78. package/dist/universal/.claude/skills/citable/schemas/evidence.schema.json +41 -0
  79. package/dist/universal/.claude/skills/citable/schemas/experiment.schema.json +37 -0
  80. package/dist/universal/.claude/skills/citable/schemas/finding.schema.json +101 -0
  81. package/dist/universal/.claude/skills/citable/schemas/page.schema.json +40 -0
  82. package/dist/universal/.claude/skills/citable/schemas/project.schema.json +29 -0
  83. package/dist/universal/.claude/skills/citable/schemas/prompt-result.schema.json +37 -0
  84. package/dist/universal/.claude/skills/citable/schemas/prompt.schema.json +41 -0
  85. package/dist/universal/.claude/skills/citable/schemas/query.schema.json +43 -0
  86. package/dist/universal/.claude/skills/citable/schemas/run.schema.json +36 -0
  87. package/dist/universal/.claude/skills/citable/scripts/README.md +4 -0
  88. package/dist/universal/.claude/skills/citable/templates/finding-review.md +54 -0
  89. package/dist/universal/.cursor/skills/citable/SKILL.md +137 -0
  90. package/dist/universal/.cursor/skills/citable/VERSION +1 -0
  91. package/dist/universal/.cursor/skills/citable/commands/README.md +36 -0
  92. package/dist/universal/.cursor/skills/citable/commands/ingest.md +44 -0
  93. package/dist/universal/.cursor/skills/citable/commands/measurement.md +49 -0
  94. package/dist/universal/.cursor/skills/citable/commands/optimize-page.md +53 -0
  95. package/dist/universal/.cursor/skills/citable/commands/page-work.md +48 -0
  96. package/dist/universal/.cursor/skills/citable/commands/validate-render.md +24 -0
  97. package/dist/universal/.cursor/skills/citable/manifest.json +54 -0
  98. package/dist/universal/.cursor/skills/citable/policies/crawler-policy-template.md +47 -0
  99. package/dist/universal/.cursor/skills/citable/references/anti-patterns.md +61 -0
  100. package/dist/universal/.cursor/skills/citable/rubrics/README.md +30 -0
  101. package/dist/universal/.cursor/skills/citable/rubrics/answer-extractability.md +44 -0
  102. package/dist/universal/.cursor/skills/citable/rubrics/claim-boundedness.md +36 -0
  103. package/dist/universal/.cursor/skills/citable/rubrics/comparison-fairness.md +30 -0
  104. package/dist/universal/.cursor/skills/citable/rubrics/conversion-alignment.md +37 -0
  105. package/dist/universal/.cursor/skills/citable/rubrics/entity-clarity.md +41 -0
  106. package/dist/universal/.cursor/skills/citable/rubrics/evidence-strength.md +53 -0
  107. package/dist/universal/.cursor/skills/citable/rubrics/information-gain.md +33 -0
  108. package/dist/universal/.cursor/skills/citable/rubrics/intent-alignment.md +41 -0
  109. package/dist/universal/.cursor/skills/citable/rubrics/narrative-accuracy.md +46 -0
  110. package/dist/universal/.cursor/skills/citable/rubrics/recommendation-eligibility.md +36 -0
  111. package/dist/universal/.cursor/skills/citable/rubrics/semantic-clarity.md +42 -0
  112. package/dist/universal/.cursor/skills/citable/rubrics/source-authority.md +37 -0
  113. package/dist/universal/.cursor/skills/citable/schemas/claim.schema.json +53 -0
  114. package/dist/universal/.cursor/skills/citable/schemas/common.defs.json +33 -0
  115. package/dist/universal/.cursor/skills/citable/schemas/competitor.schema.json +28 -0
  116. package/dist/universal/.cursor/skills/citable/schemas/config.schema.json +41 -0
  117. package/dist/universal/.cursor/skills/citable/schemas/crawler.schema.json +33 -0
  118. package/dist/universal/.cursor/skills/citable/schemas/detector-quality.schema.json +39 -0
  119. package/dist/universal/.cursor/skills/citable/schemas/entity.schema.json +39 -0
  120. package/dist/universal/.cursor/skills/citable/schemas/evidence.schema.json +41 -0
  121. package/dist/universal/.cursor/skills/citable/schemas/experiment.schema.json +37 -0
  122. package/dist/universal/.cursor/skills/citable/schemas/finding.schema.json +101 -0
  123. package/dist/universal/.cursor/skills/citable/schemas/page.schema.json +40 -0
  124. package/dist/universal/.cursor/skills/citable/schemas/project.schema.json +29 -0
  125. package/dist/universal/.cursor/skills/citable/schemas/prompt-result.schema.json +37 -0
  126. package/dist/universal/.cursor/skills/citable/schemas/prompt.schema.json +41 -0
  127. package/dist/universal/.cursor/skills/citable/schemas/query.schema.json +43 -0
  128. package/dist/universal/.cursor/skills/citable/schemas/run.schema.json +36 -0
  129. package/dist/universal/.cursor/skills/citable/scripts/README.md +4 -0
  130. package/dist/universal/.cursor/skills/citable/templates/finding-review.md +54 -0
  131. package/dist/universal/.gemini/skills/citable/SKILL.md +137 -0
  132. package/dist/universal/.gemini/skills/citable/VERSION +1 -0
  133. package/dist/universal/.gemini/skills/citable/commands/README.md +36 -0
  134. package/dist/universal/.gemini/skills/citable/commands/ingest.md +44 -0
  135. package/dist/universal/.gemini/skills/citable/commands/measurement.md +49 -0
  136. package/dist/universal/.gemini/skills/citable/commands/optimize-page.md +53 -0
  137. package/dist/universal/.gemini/skills/citable/commands/page-work.md +48 -0
  138. package/dist/universal/.gemini/skills/citable/commands/validate-render.md +24 -0
  139. package/dist/universal/.gemini/skills/citable/manifest.json +54 -0
  140. package/dist/universal/.gemini/skills/citable/policies/crawler-policy-template.md +47 -0
  141. package/dist/universal/.gemini/skills/citable/references/anti-patterns.md +61 -0
  142. package/dist/universal/.gemini/skills/citable/rubrics/README.md +30 -0
  143. package/dist/universal/.gemini/skills/citable/rubrics/answer-extractability.md +44 -0
  144. package/dist/universal/.gemini/skills/citable/rubrics/claim-boundedness.md +36 -0
  145. package/dist/universal/.gemini/skills/citable/rubrics/comparison-fairness.md +30 -0
  146. package/dist/universal/.gemini/skills/citable/rubrics/conversion-alignment.md +37 -0
  147. package/dist/universal/.gemini/skills/citable/rubrics/entity-clarity.md +41 -0
  148. package/dist/universal/.gemini/skills/citable/rubrics/evidence-strength.md +53 -0
  149. package/dist/universal/.gemini/skills/citable/rubrics/information-gain.md +33 -0
  150. package/dist/universal/.gemini/skills/citable/rubrics/intent-alignment.md +41 -0
  151. package/dist/universal/.gemini/skills/citable/rubrics/narrative-accuracy.md +46 -0
  152. package/dist/universal/.gemini/skills/citable/rubrics/recommendation-eligibility.md +36 -0
  153. package/dist/universal/.gemini/skills/citable/rubrics/semantic-clarity.md +42 -0
  154. package/dist/universal/.gemini/skills/citable/rubrics/source-authority.md +37 -0
  155. package/dist/universal/.gemini/skills/citable/schemas/claim.schema.json +53 -0
  156. package/dist/universal/.gemini/skills/citable/schemas/common.defs.json +33 -0
  157. package/dist/universal/.gemini/skills/citable/schemas/competitor.schema.json +28 -0
  158. package/dist/universal/.gemini/skills/citable/schemas/config.schema.json +41 -0
  159. package/dist/universal/.gemini/skills/citable/schemas/crawler.schema.json +33 -0
  160. package/dist/universal/.gemini/skills/citable/schemas/detector-quality.schema.json +39 -0
  161. package/dist/universal/.gemini/skills/citable/schemas/entity.schema.json +39 -0
  162. package/dist/universal/.gemini/skills/citable/schemas/evidence.schema.json +41 -0
  163. package/dist/universal/.gemini/skills/citable/schemas/experiment.schema.json +37 -0
  164. package/dist/universal/.gemini/skills/citable/schemas/finding.schema.json +101 -0
  165. package/dist/universal/.gemini/skills/citable/schemas/page.schema.json +40 -0
  166. package/dist/universal/.gemini/skills/citable/schemas/project.schema.json +29 -0
  167. package/dist/universal/.gemini/skills/citable/schemas/prompt-result.schema.json +37 -0
  168. package/dist/universal/.gemini/skills/citable/schemas/prompt.schema.json +41 -0
  169. package/dist/universal/.gemini/skills/citable/schemas/query.schema.json +43 -0
  170. package/dist/universal/.gemini/skills/citable/schemas/run.schema.json +36 -0
  171. package/dist/universal/.gemini/skills/citable/scripts/README.md +4 -0
  172. package/dist/universal/.gemini/skills/citable/templates/finding-review.md +54 -0
  173. package/dist/universal/.github/skills/citable/SKILL.md +137 -0
  174. package/dist/universal/.github/skills/citable/VERSION +1 -0
  175. package/dist/universal/.github/skills/citable/commands/README.md +36 -0
  176. package/dist/universal/.github/skills/citable/commands/ingest.md +44 -0
  177. package/dist/universal/.github/skills/citable/commands/measurement.md +49 -0
  178. package/dist/universal/.github/skills/citable/commands/optimize-page.md +53 -0
  179. package/dist/universal/.github/skills/citable/commands/page-work.md +48 -0
  180. package/dist/universal/.github/skills/citable/commands/validate-render.md +24 -0
  181. package/dist/universal/.github/skills/citable/manifest.json +54 -0
  182. package/dist/universal/.github/skills/citable/policies/crawler-policy-template.md +47 -0
  183. package/dist/universal/.github/skills/citable/references/anti-patterns.md +61 -0
  184. package/dist/universal/.github/skills/citable/rubrics/README.md +30 -0
  185. package/dist/universal/.github/skills/citable/rubrics/answer-extractability.md +44 -0
  186. package/dist/universal/.github/skills/citable/rubrics/claim-boundedness.md +36 -0
  187. package/dist/universal/.github/skills/citable/rubrics/comparison-fairness.md +30 -0
  188. package/dist/universal/.github/skills/citable/rubrics/conversion-alignment.md +37 -0
  189. package/dist/universal/.github/skills/citable/rubrics/entity-clarity.md +41 -0
  190. package/dist/universal/.github/skills/citable/rubrics/evidence-strength.md +53 -0
  191. package/dist/universal/.github/skills/citable/rubrics/information-gain.md +33 -0
  192. package/dist/universal/.github/skills/citable/rubrics/intent-alignment.md +41 -0
  193. package/dist/universal/.github/skills/citable/rubrics/narrative-accuracy.md +46 -0
  194. package/dist/universal/.github/skills/citable/rubrics/recommendation-eligibility.md +36 -0
  195. package/dist/universal/.github/skills/citable/rubrics/semantic-clarity.md +42 -0
  196. package/dist/universal/.github/skills/citable/rubrics/source-authority.md +37 -0
  197. package/dist/universal/.github/skills/citable/schemas/claim.schema.json +53 -0
  198. package/dist/universal/.github/skills/citable/schemas/common.defs.json +33 -0
  199. package/dist/universal/.github/skills/citable/schemas/competitor.schema.json +28 -0
  200. package/dist/universal/.github/skills/citable/schemas/config.schema.json +41 -0
  201. package/dist/universal/.github/skills/citable/schemas/crawler.schema.json +33 -0
  202. package/dist/universal/.github/skills/citable/schemas/detector-quality.schema.json +39 -0
  203. package/dist/universal/.github/skills/citable/schemas/entity.schema.json +39 -0
  204. package/dist/universal/.github/skills/citable/schemas/evidence.schema.json +41 -0
  205. package/dist/universal/.github/skills/citable/schemas/experiment.schema.json +37 -0
  206. package/dist/universal/.github/skills/citable/schemas/finding.schema.json +101 -0
  207. package/dist/universal/.github/skills/citable/schemas/page.schema.json +40 -0
  208. package/dist/universal/.github/skills/citable/schemas/project.schema.json +29 -0
  209. package/dist/universal/.github/skills/citable/schemas/prompt-result.schema.json +37 -0
  210. package/dist/universal/.github/skills/citable/schemas/prompt.schema.json +41 -0
  211. package/dist/universal/.github/skills/citable/schemas/query.schema.json +43 -0
  212. package/dist/universal/.github/skills/citable/schemas/run.schema.json +36 -0
  213. package/dist/universal/.github/skills/citable/scripts/README.md +4 -0
  214. package/dist/universal/.github/skills/citable/templates/finding-review.md +54 -0
  215. package/dist/universal/.kiro/skills/citable/SKILL.md +137 -0
  216. package/dist/universal/.kiro/skills/citable/VERSION +1 -0
  217. package/dist/universal/.kiro/skills/citable/commands/README.md +36 -0
  218. package/dist/universal/.kiro/skills/citable/commands/ingest.md +44 -0
  219. package/dist/universal/.kiro/skills/citable/commands/measurement.md +49 -0
  220. package/dist/universal/.kiro/skills/citable/commands/optimize-page.md +53 -0
  221. package/dist/universal/.kiro/skills/citable/commands/page-work.md +48 -0
  222. package/dist/universal/.kiro/skills/citable/commands/validate-render.md +24 -0
  223. package/dist/universal/.kiro/skills/citable/manifest.json +54 -0
  224. package/dist/universal/.kiro/skills/citable/policies/crawler-policy-template.md +47 -0
  225. package/dist/universal/.kiro/skills/citable/references/anti-patterns.md +61 -0
  226. package/dist/universal/.kiro/skills/citable/rubrics/README.md +30 -0
  227. package/dist/universal/.kiro/skills/citable/rubrics/answer-extractability.md +44 -0
  228. package/dist/universal/.kiro/skills/citable/rubrics/claim-boundedness.md +36 -0
  229. package/dist/universal/.kiro/skills/citable/rubrics/comparison-fairness.md +30 -0
  230. package/dist/universal/.kiro/skills/citable/rubrics/conversion-alignment.md +37 -0
  231. package/dist/universal/.kiro/skills/citable/rubrics/entity-clarity.md +41 -0
  232. package/dist/universal/.kiro/skills/citable/rubrics/evidence-strength.md +53 -0
  233. package/dist/universal/.kiro/skills/citable/rubrics/information-gain.md +33 -0
  234. package/dist/universal/.kiro/skills/citable/rubrics/intent-alignment.md +41 -0
  235. package/dist/universal/.kiro/skills/citable/rubrics/narrative-accuracy.md +46 -0
  236. package/dist/universal/.kiro/skills/citable/rubrics/recommendation-eligibility.md +36 -0
  237. package/dist/universal/.kiro/skills/citable/rubrics/semantic-clarity.md +42 -0
  238. package/dist/universal/.kiro/skills/citable/rubrics/source-authority.md +37 -0
  239. package/dist/universal/.kiro/skills/citable/schemas/claim.schema.json +53 -0
  240. package/dist/universal/.kiro/skills/citable/schemas/common.defs.json +33 -0
  241. package/dist/universal/.kiro/skills/citable/schemas/competitor.schema.json +28 -0
  242. package/dist/universal/.kiro/skills/citable/schemas/config.schema.json +41 -0
  243. package/dist/universal/.kiro/skills/citable/schemas/crawler.schema.json +33 -0
  244. package/dist/universal/.kiro/skills/citable/schemas/detector-quality.schema.json +39 -0
  245. package/dist/universal/.kiro/skills/citable/schemas/entity.schema.json +39 -0
  246. package/dist/universal/.kiro/skills/citable/schemas/evidence.schema.json +41 -0
  247. package/dist/universal/.kiro/skills/citable/schemas/experiment.schema.json +37 -0
  248. package/dist/universal/.kiro/skills/citable/schemas/finding.schema.json +101 -0
  249. package/dist/universal/.kiro/skills/citable/schemas/page.schema.json +40 -0
  250. package/dist/universal/.kiro/skills/citable/schemas/project.schema.json +29 -0
  251. package/dist/universal/.kiro/skills/citable/schemas/prompt-result.schema.json +37 -0
  252. package/dist/universal/.kiro/skills/citable/schemas/prompt.schema.json +41 -0
  253. package/dist/universal/.kiro/skills/citable/schemas/query.schema.json +43 -0
  254. package/dist/universal/.kiro/skills/citable/schemas/run.schema.json +36 -0
  255. package/dist/universal/.kiro/skills/citable/scripts/README.md +4 -0
  256. package/dist/universal/.kiro/skills/citable/templates/finding-review.md +54 -0
  257. package/dist/universal/.opencode/skills/citable/SKILL.md +137 -0
  258. package/dist/universal/.opencode/skills/citable/VERSION +1 -0
  259. package/dist/universal/.opencode/skills/citable/commands/README.md +36 -0
  260. package/dist/universal/.opencode/skills/citable/commands/ingest.md +44 -0
  261. package/dist/universal/.opencode/skills/citable/commands/measurement.md +49 -0
  262. package/dist/universal/.opencode/skills/citable/commands/optimize-page.md +53 -0
  263. package/dist/universal/.opencode/skills/citable/commands/page-work.md +48 -0
  264. package/dist/universal/.opencode/skills/citable/commands/validate-render.md +24 -0
  265. package/dist/universal/.opencode/skills/citable/manifest.json +54 -0
  266. package/dist/universal/.opencode/skills/citable/policies/crawler-policy-template.md +47 -0
  267. package/dist/universal/.opencode/skills/citable/references/anti-patterns.md +61 -0
  268. package/dist/universal/.opencode/skills/citable/rubrics/README.md +30 -0
  269. package/dist/universal/.opencode/skills/citable/rubrics/answer-extractability.md +44 -0
  270. package/dist/universal/.opencode/skills/citable/rubrics/claim-boundedness.md +36 -0
  271. package/dist/universal/.opencode/skills/citable/rubrics/comparison-fairness.md +30 -0
  272. package/dist/universal/.opencode/skills/citable/rubrics/conversion-alignment.md +37 -0
  273. package/dist/universal/.opencode/skills/citable/rubrics/entity-clarity.md +41 -0
  274. package/dist/universal/.opencode/skills/citable/rubrics/evidence-strength.md +53 -0
  275. package/dist/universal/.opencode/skills/citable/rubrics/information-gain.md +33 -0
  276. package/dist/universal/.opencode/skills/citable/rubrics/intent-alignment.md +41 -0
  277. package/dist/universal/.opencode/skills/citable/rubrics/narrative-accuracy.md +46 -0
  278. package/dist/universal/.opencode/skills/citable/rubrics/recommendation-eligibility.md +36 -0
  279. package/dist/universal/.opencode/skills/citable/rubrics/semantic-clarity.md +42 -0
  280. package/dist/universal/.opencode/skills/citable/rubrics/source-authority.md +37 -0
  281. package/dist/universal/.opencode/skills/citable/schemas/claim.schema.json +53 -0
  282. package/dist/universal/.opencode/skills/citable/schemas/common.defs.json +33 -0
  283. package/dist/universal/.opencode/skills/citable/schemas/competitor.schema.json +28 -0
  284. package/dist/universal/.opencode/skills/citable/schemas/config.schema.json +41 -0
  285. package/dist/universal/.opencode/skills/citable/schemas/crawler.schema.json +33 -0
  286. package/dist/universal/.opencode/skills/citable/schemas/detector-quality.schema.json +39 -0
  287. package/dist/universal/.opencode/skills/citable/schemas/entity.schema.json +39 -0
  288. package/dist/universal/.opencode/skills/citable/schemas/evidence.schema.json +41 -0
  289. package/dist/universal/.opencode/skills/citable/schemas/experiment.schema.json +37 -0
  290. package/dist/universal/.opencode/skills/citable/schemas/finding.schema.json +101 -0
  291. package/dist/universal/.opencode/skills/citable/schemas/page.schema.json +40 -0
  292. package/dist/universal/.opencode/skills/citable/schemas/project.schema.json +29 -0
  293. package/dist/universal/.opencode/skills/citable/schemas/prompt-result.schema.json +37 -0
  294. package/dist/universal/.opencode/skills/citable/schemas/prompt.schema.json +41 -0
  295. package/dist/universal/.opencode/skills/citable/schemas/query.schema.json +43 -0
  296. package/dist/universal/.opencode/skills/citable/schemas/run.schema.json +36 -0
  297. package/dist/universal/.opencode/skills/citable/scripts/README.md +4 -0
  298. package/dist/universal/.opencode/skills/citable/templates/finding-review.md +54 -0
  299. package/dist/universal/.pi/agent/skills/citable/SKILL.md +137 -0
  300. package/dist/universal/.pi/agent/skills/citable/VERSION +1 -0
  301. package/dist/universal/.pi/agent/skills/citable/commands/README.md +36 -0
  302. package/dist/universal/.pi/agent/skills/citable/commands/ingest.md +44 -0
  303. package/dist/universal/.pi/agent/skills/citable/commands/measurement.md +49 -0
  304. package/dist/universal/.pi/agent/skills/citable/commands/optimize-page.md +53 -0
  305. package/dist/universal/.pi/agent/skills/citable/commands/page-work.md +48 -0
  306. package/dist/universal/.pi/agent/skills/citable/commands/validate-render.md +24 -0
  307. package/dist/universal/.pi/agent/skills/citable/manifest.json +54 -0
  308. package/dist/universal/.pi/agent/skills/citable/policies/crawler-policy-template.md +47 -0
  309. package/dist/universal/.pi/agent/skills/citable/references/anti-patterns.md +61 -0
  310. package/dist/universal/.pi/agent/skills/citable/rubrics/README.md +30 -0
  311. package/dist/universal/.pi/agent/skills/citable/rubrics/answer-extractability.md +44 -0
  312. package/dist/universal/.pi/agent/skills/citable/rubrics/claim-boundedness.md +36 -0
  313. package/dist/universal/.pi/agent/skills/citable/rubrics/comparison-fairness.md +30 -0
  314. package/dist/universal/.pi/agent/skills/citable/rubrics/conversion-alignment.md +37 -0
  315. package/dist/universal/.pi/agent/skills/citable/rubrics/entity-clarity.md +41 -0
  316. package/dist/universal/.pi/agent/skills/citable/rubrics/evidence-strength.md +53 -0
  317. package/dist/universal/.pi/agent/skills/citable/rubrics/information-gain.md +33 -0
  318. package/dist/universal/.pi/agent/skills/citable/rubrics/intent-alignment.md +41 -0
  319. package/dist/universal/.pi/agent/skills/citable/rubrics/narrative-accuracy.md +46 -0
  320. package/dist/universal/.pi/agent/skills/citable/rubrics/recommendation-eligibility.md +36 -0
  321. package/dist/universal/.pi/agent/skills/citable/rubrics/semantic-clarity.md +42 -0
  322. package/dist/universal/.pi/agent/skills/citable/rubrics/source-authority.md +37 -0
  323. package/dist/universal/.pi/agent/skills/citable/schemas/claim.schema.json +53 -0
  324. package/dist/universal/.pi/agent/skills/citable/schemas/common.defs.json +33 -0
  325. package/dist/universal/.pi/agent/skills/citable/schemas/competitor.schema.json +28 -0
  326. package/dist/universal/.pi/agent/skills/citable/schemas/config.schema.json +41 -0
  327. package/dist/universal/.pi/agent/skills/citable/schemas/crawler.schema.json +33 -0
  328. package/dist/universal/.pi/agent/skills/citable/schemas/detector-quality.schema.json +39 -0
  329. package/dist/universal/.pi/agent/skills/citable/schemas/entity.schema.json +39 -0
  330. package/dist/universal/.pi/agent/skills/citable/schemas/evidence.schema.json +41 -0
  331. package/dist/universal/.pi/agent/skills/citable/schemas/experiment.schema.json +37 -0
  332. package/dist/universal/.pi/agent/skills/citable/schemas/finding.schema.json +101 -0
  333. package/dist/universal/.pi/agent/skills/citable/schemas/page.schema.json +40 -0
  334. package/dist/universal/.pi/agent/skills/citable/schemas/project.schema.json +29 -0
  335. package/dist/universal/.pi/agent/skills/citable/schemas/prompt-result.schema.json +37 -0
  336. package/dist/universal/.pi/agent/skills/citable/schemas/prompt.schema.json +41 -0
  337. package/dist/universal/.pi/agent/skills/citable/schemas/query.schema.json +43 -0
  338. package/dist/universal/.pi/agent/skills/citable/schemas/run.schema.json +36 -0
  339. package/dist/universal/.pi/agent/skills/citable/scripts/README.md +4 -0
  340. package/dist/universal/.pi/agent/skills/citable/templates/finding-review.md +54 -0
  341. package/dist/universal/.qoder/skills/citable/SKILL.md +137 -0
  342. package/dist/universal/.qoder/skills/citable/VERSION +1 -0
  343. package/dist/universal/.qoder/skills/citable/commands/README.md +36 -0
  344. package/dist/universal/.qoder/skills/citable/commands/ingest.md +44 -0
  345. package/dist/universal/.qoder/skills/citable/commands/measurement.md +49 -0
  346. package/dist/universal/.qoder/skills/citable/commands/optimize-page.md +53 -0
  347. package/dist/universal/.qoder/skills/citable/commands/page-work.md +48 -0
  348. package/dist/universal/.qoder/skills/citable/commands/validate-render.md +24 -0
  349. package/dist/universal/.qoder/skills/citable/manifest.json +54 -0
  350. package/dist/universal/.qoder/skills/citable/policies/crawler-policy-template.md +47 -0
  351. package/dist/universal/.qoder/skills/citable/references/anti-patterns.md +61 -0
  352. package/dist/universal/.qoder/skills/citable/rubrics/README.md +30 -0
  353. package/dist/universal/.qoder/skills/citable/rubrics/answer-extractability.md +44 -0
  354. package/dist/universal/.qoder/skills/citable/rubrics/claim-boundedness.md +36 -0
  355. package/dist/universal/.qoder/skills/citable/rubrics/comparison-fairness.md +30 -0
  356. package/dist/universal/.qoder/skills/citable/rubrics/conversion-alignment.md +37 -0
  357. package/dist/universal/.qoder/skills/citable/rubrics/entity-clarity.md +41 -0
  358. package/dist/universal/.qoder/skills/citable/rubrics/evidence-strength.md +53 -0
  359. package/dist/universal/.qoder/skills/citable/rubrics/information-gain.md +33 -0
  360. package/dist/universal/.qoder/skills/citable/rubrics/intent-alignment.md +41 -0
  361. package/dist/universal/.qoder/skills/citable/rubrics/narrative-accuracy.md +46 -0
  362. package/dist/universal/.qoder/skills/citable/rubrics/recommendation-eligibility.md +36 -0
  363. package/dist/universal/.qoder/skills/citable/rubrics/semantic-clarity.md +42 -0
  364. package/dist/universal/.qoder/skills/citable/rubrics/source-authority.md +37 -0
  365. package/dist/universal/.qoder/skills/citable/schemas/claim.schema.json +53 -0
  366. package/dist/universal/.qoder/skills/citable/schemas/common.defs.json +33 -0
  367. package/dist/universal/.qoder/skills/citable/schemas/competitor.schema.json +28 -0
  368. package/dist/universal/.qoder/skills/citable/schemas/config.schema.json +41 -0
  369. package/dist/universal/.qoder/skills/citable/schemas/crawler.schema.json +33 -0
  370. package/dist/universal/.qoder/skills/citable/schemas/detector-quality.schema.json +39 -0
  371. package/dist/universal/.qoder/skills/citable/schemas/entity.schema.json +39 -0
  372. package/dist/universal/.qoder/skills/citable/schemas/evidence.schema.json +41 -0
  373. package/dist/universal/.qoder/skills/citable/schemas/experiment.schema.json +37 -0
  374. package/dist/universal/.qoder/skills/citable/schemas/finding.schema.json +101 -0
  375. package/dist/universal/.qoder/skills/citable/schemas/page.schema.json +40 -0
  376. package/dist/universal/.qoder/skills/citable/schemas/project.schema.json +29 -0
  377. package/dist/universal/.qoder/skills/citable/schemas/prompt-result.schema.json +37 -0
  378. package/dist/universal/.qoder/skills/citable/schemas/prompt.schema.json +41 -0
  379. package/dist/universal/.qoder/skills/citable/schemas/query.schema.json +43 -0
  380. package/dist/universal/.qoder/skills/citable/schemas/run.schema.json +36 -0
  381. package/dist/universal/.qoder/skills/citable/scripts/README.md +4 -0
  382. package/dist/universal/.qoder/skills/citable/templates/finding-review.md +54 -0
  383. package/dist/universal/.rovodev/skills/citable/SKILL.md +137 -0
  384. package/dist/universal/.rovodev/skills/citable/VERSION +1 -0
  385. package/dist/universal/.rovodev/skills/citable/commands/README.md +36 -0
  386. package/dist/universal/.rovodev/skills/citable/commands/ingest.md +44 -0
  387. package/dist/universal/.rovodev/skills/citable/commands/measurement.md +49 -0
  388. package/dist/universal/.rovodev/skills/citable/commands/optimize-page.md +53 -0
  389. package/dist/universal/.rovodev/skills/citable/commands/page-work.md +48 -0
  390. package/dist/universal/.rovodev/skills/citable/commands/validate-render.md +24 -0
  391. package/dist/universal/.rovodev/skills/citable/manifest.json +54 -0
  392. package/dist/universal/.rovodev/skills/citable/policies/crawler-policy-template.md +47 -0
  393. package/dist/universal/.rovodev/skills/citable/references/anti-patterns.md +61 -0
  394. package/dist/universal/.rovodev/skills/citable/rubrics/README.md +30 -0
  395. package/dist/universal/.rovodev/skills/citable/rubrics/answer-extractability.md +44 -0
  396. package/dist/universal/.rovodev/skills/citable/rubrics/claim-boundedness.md +36 -0
  397. package/dist/universal/.rovodev/skills/citable/rubrics/comparison-fairness.md +30 -0
  398. package/dist/universal/.rovodev/skills/citable/rubrics/conversion-alignment.md +37 -0
  399. package/dist/universal/.rovodev/skills/citable/rubrics/entity-clarity.md +41 -0
  400. package/dist/universal/.rovodev/skills/citable/rubrics/evidence-strength.md +53 -0
  401. package/dist/universal/.rovodev/skills/citable/rubrics/information-gain.md +33 -0
  402. package/dist/universal/.rovodev/skills/citable/rubrics/intent-alignment.md +41 -0
  403. package/dist/universal/.rovodev/skills/citable/rubrics/narrative-accuracy.md +46 -0
  404. package/dist/universal/.rovodev/skills/citable/rubrics/recommendation-eligibility.md +36 -0
  405. package/dist/universal/.rovodev/skills/citable/rubrics/semantic-clarity.md +42 -0
  406. package/dist/universal/.rovodev/skills/citable/rubrics/source-authority.md +37 -0
  407. package/dist/universal/.rovodev/skills/citable/schemas/claim.schema.json +53 -0
  408. package/dist/universal/.rovodev/skills/citable/schemas/common.defs.json +33 -0
  409. package/dist/universal/.rovodev/skills/citable/schemas/competitor.schema.json +28 -0
  410. package/dist/universal/.rovodev/skills/citable/schemas/config.schema.json +41 -0
  411. package/dist/universal/.rovodev/skills/citable/schemas/crawler.schema.json +33 -0
  412. package/dist/universal/.rovodev/skills/citable/schemas/detector-quality.schema.json +39 -0
  413. package/dist/universal/.rovodev/skills/citable/schemas/entity.schema.json +39 -0
  414. package/dist/universal/.rovodev/skills/citable/schemas/evidence.schema.json +41 -0
  415. package/dist/universal/.rovodev/skills/citable/schemas/experiment.schema.json +37 -0
  416. package/dist/universal/.rovodev/skills/citable/schemas/finding.schema.json +101 -0
  417. package/dist/universal/.rovodev/skills/citable/schemas/page.schema.json +40 -0
  418. package/dist/universal/.rovodev/skills/citable/schemas/project.schema.json +29 -0
  419. package/dist/universal/.rovodev/skills/citable/schemas/prompt-result.schema.json +37 -0
  420. package/dist/universal/.rovodev/skills/citable/schemas/prompt.schema.json +41 -0
  421. package/dist/universal/.rovodev/skills/citable/schemas/query.schema.json +43 -0
  422. package/dist/universal/.rovodev/skills/citable/schemas/run.schema.json +36 -0
  423. package/dist/universal/.rovodev/skills/citable/scripts/README.md +4 -0
  424. package/dist/universal/.rovodev/skills/citable/templates/finding-review.md +54 -0
  425. package/dist/universal/.trae/skills/citable/SKILL.md +137 -0
  426. package/dist/universal/.trae/skills/citable/VERSION +1 -0
  427. package/dist/universal/.trae/skills/citable/commands/README.md +36 -0
  428. package/dist/universal/.trae/skills/citable/commands/ingest.md +44 -0
  429. package/dist/universal/.trae/skills/citable/commands/measurement.md +49 -0
  430. package/dist/universal/.trae/skills/citable/commands/optimize-page.md +53 -0
  431. package/dist/universal/.trae/skills/citable/commands/page-work.md +48 -0
  432. package/dist/universal/.trae/skills/citable/commands/validate-render.md +24 -0
  433. package/dist/universal/.trae/skills/citable/manifest.json +54 -0
  434. package/dist/universal/.trae/skills/citable/policies/crawler-policy-template.md +47 -0
  435. package/dist/universal/.trae/skills/citable/references/anti-patterns.md +61 -0
  436. package/dist/universal/.trae/skills/citable/rubrics/README.md +30 -0
  437. package/dist/universal/.trae/skills/citable/rubrics/answer-extractability.md +44 -0
  438. package/dist/universal/.trae/skills/citable/rubrics/claim-boundedness.md +36 -0
  439. package/dist/universal/.trae/skills/citable/rubrics/comparison-fairness.md +30 -0
  440. package/dist/universal/.trae/skills/citable/rubrics/conversion-alignment.md +37 -0
  441. package/dist/universal/.trae/skills/citable/rubrics/entity-clarity.md +41 -0
  442. package/dist/universal/.trae/skills/citable/rubrics/evidence-strength.md +53 -0
  443. package/dist/universal/.trae/skills/citable/rubrics/information-gain.md +33 -0
  444. package/dist/universal/.trae/skills/citable/rubrics/intent-alignment.md +41 -0
  445. package/dist/universal/.trae/skills/citable/rubrics/narrative-accuracy.md +46 -0
  446. package/dist/universal/.trae/skills/citable/rubrics/recommendation-eligibility.md +36 -0
  447. package/dist/universal/.trae/skills/citable/rubrics/semantic-clarity.md +42 -0
  448. package/dist/universal/.trae/skills/citable/rubrics/source-authority.md +37 -0
  449. package/dist/universal/.trae/skills/citable/schemas/claim.schema.json +53 -0
  450. package/dist/universal/.trae/skills/citable/schemas/common.defs.json +33 -0
  451. package/dist/universal/.trae/skills/citable/schemas/competitor.schema.json +28 -0
  452. package/dist/universal/.trae/skills/citable/schemas/config.schema.json +41 -0
  453. package/dist/universal/.trae/skills/citable/schemas/crawler.schema.json +33 -0
  454. package/dist/universal/.trae/skills/citable/schemas/detector-quality.schema.json +39 -0
  455. package/dist/universal/.trae/skills/citable/schemas/entity.schema.json +39 -0
  456. package/dist/universal/.trae/skills/citable/schemas/evidence.schema.json +41 -0
  457. package/dist/universal/.trae/skills/citable/schemas/experiment.schema.json +37 -0
  458. package/dist/universal/.trae/skills/citable/schemas/finding.schema.json +101 -0
  459. package/dist/universal/.trae/skills/citable/schemas/page.schema.json +40 -0
  460. package/dist/universal/.trae/skills/citable/schemas/project.schema.json +29 -0
  461. package/dist/universal/.trae/skills/citable/schemas/prompt-result.schema.json +37 -0
  462. package/dist/universal/.trae/skills/citable/schemas/prompt.schema.json +41 -0
  463. package/dist/universal/.trae/skills/citable/schemas/query.schema.json +43 -0
  464. package/dist/universal/.trae/skills/citable/schemas/run.schema.json +36 -0
  465. package/dist/universal/.trae/skills/citable/scripts/README.md +4 -0
  466. package/dist/universal/.trae/skills/citable/templates/finding-review.md +54 -0
  467. package/dist/universal/.trae-cn/skills/citable/SKILL.md +137 -0
  468. package/dist/universal/.trae-cn/skills/citable/VERSION +1 -0
  469. package/dist/universal/.trae-cn/skills/citable/commands/README.md +36 -0
  470. package/dist/universal/.trae-cn/skills/citable/commands/ingest.md +44 -0
  471. package/dist/universal/.trae-cn/skills/citable/commands/measurement.md +49 -0
  472. package/dist/universal/.trae-cn/skills/citable/commands/optimize-page.md +53 -0
  473. package/dist/universal/.trae-cn/skills/citable/commands/page-work.md +48 -0
  474. package/dist/universal/.trae-cn/skills/citable/commands/validate-render.md +24 -0
  475. package/dist/universal/.trae-cn/skills/citable/manifest.json +54 -0
  476. package/dist/universal/.trae-cn/skills/citable/policies/crawler-policy-template.md +47 -0
  477. package/dist/universal/.trae-cn/skills/citable/references/anti-patterns.md +61 -0
  478. package/dist/universal/.trae-cn/skills/citable/rubrics/README.md +30 -0
  479. package/dist/universal/.trae-cn/skills/citable/rubrics/answer-extractability.md +44 -0
  480. package/dist/universal/.trae-cn/skills/citable/rubrics/claim-boundedness.md +36 -0
  481. package/dist/universal/.trae-cn/skills/citable/rubrics/comparison-fairness.md +30 -0
  482. package/dist/universal/.trae-cn/skills/citable/rubrics/conversion-alignment.md +37 -0
  483. package/dist/universal/.trae-cn/skills/citable/rubrics/entity-clarity.md +41 -0
  484. package/dist/universal/.trae-cn/skills/citable/rubrics/evidence-strength.md +53 -0
  485. package/dist/universal/.trae-cn/skills/citable/rubrics/information-gain.md +33 -0
  486. package/dist/universal/.trae-cn/skills/citable/rubrics/intent-alignment.md +41 -0
  487. package/dist/universal/.trae-cn/skills/citable/rubrics/narrative-accuracy.md +46 -0
  488. package/dist/universal/.trae-cn/skills/citable/rubrics/recommendation-eligibility.md +36 -0
  489. package/dist/universal/.trae-cn/skills/citable/rubrics/semantic-clarity.md +42 -0
  490. package/dist/universal/.trae-cn/skills/citable/rubrics/source-authority.md +37 -0
  491. package/dist/universal/.trae-cn/skills/citable/schemas/claim.schema.json +53 -0
  492. package/dist/universal/.trae-cn/skills/citable/schemas/common.defs.json +33 -0
  493. package/dist/universal/.trae-cn/skills/citable/schemas/competitor.schema.json +28 -0
  494. package/dist/universal/.trae-cn/skills/citable/schemas/config.schema.json +41 -0
  495. package/dist/universal/.trae-cn/skills/citable/schemas/crawler.schema.json +33 -0
  496. package/dist/universal/.trae-cn/skills/citable/schemas/detector-quality.schema.json +39 -0
  497. package/dist/universal/.trae-cn/skills/citable/schemas/entity.schema.json +39 -0
  498. package/dist/universal/.trae-cn/skills/citable/schemas/evidence.schema.json +41 -0
  499. package/dist/universal/.trae-cn/skills/citable/schemas/experiment.schema.json +37 -0
  500. package/dist/universal/.trae-cn/skills/citable/schemas/finding.schema.json +101 -0
  501. package/dist/universal/.trae-cn/skills/citable/schemas/page.schema.json +40 -0
  502. package/dist/universal/.trae-cn/skills/citable/schemas/project.schema.json +29 -0
  503. package/dist/universal/.trae-cn/skills/citable/schemas/prompt-result.schema.json +37 -0
  504. package/dist/universal/.trae-cn/skills/citable/schemas/prompt.schema.json +41 -0
  505. package/dist/universal/.trae-cn/skills/citable/schemas/query.schema.json +43 -0
  506. package/dist/universal/.trae-cn/skills/citable/schemas/run.schema.json +36 -0
  507. package/dist/universal/.trae-cn/skills/citable/scripts/README.md +4 -0
  508. package/dist/universal/.trae-cn/skills/citable/templates/finding-review.md +54 -0
  509. package/dist/universal/manifest.json +598 -0
  510. package/package.json +89 -0
  511. package/schemas/claim.schema.json +53 -0
  512. package/schemas/common.defs.json +33 -0
  513. package/schemas/competitor.schema.json +28 -0
  514. package/schemas/config.schema.json +41 -0
  515. package/schemas/crawler.schema.json +33 -0
  516. package/schemas/detector-quality.schema.json +39 -0
  517. package/schemas/entity.schema.json +39 -0
  518. package/schemas/evidence.schema.json +41 -0
  519. package/schemas/experiment.schema.json +37 -0
  520. package/schemas/finding.schema.json +101 -0
  521. package/schemas/page.schema.json +40 -0
  522. package/schemas/project.schema.json +29 -0
  523. package/schemas/prompt-result.schema.json +37 -0
  524. package/schemas/prompt.schema.json +41 -0
  525. package/schemas/query.schema.json +43 -0
  526. package/schemas/run.schema.json +36 -0
  527. package/scripts/build-dist.js +143 -0
  528. package/skill/SKILL.md +137 -0
  529. package/skill/commands/README.md +36 -0
  530. package/skill/commands/ingest.md +44 -0
  531. package/skill/commands/measurement.md +49 -0
  532. package/skill/commands/optimize-page.md +53 -0
  533. package/skill/commands/page-work.md +48 -0
  534. package/skill/commands/validate-render.md +24 -0
  535. package/skill/policies/crawler-policy-template.md +47 -0
  536. package/skill/references/anti-patterns.md +61 -0
  537. package/skill/rubrics/README.md +30 -0
  538. package/skill/rubrics/answer-extractability.md +44 -0
  539. package/skill/rubrics/claim-boundedness.md +36 -0
  540. package/skill/rubrics/comparison-fairness.md +30 -0
  541. package/skill/rubrics/conversion-alignment.md +37 -0
  542. package/skill/rubrics/entity-clarity.md +41 -0
  543. package/skill/rubrics/evidence-strength.md +53 -0
  544. package/skill/rubrics/information-gain.md +33 -0
  545. package/skill/rubrics/intent-alignment.md +41 -0
  546. package/skill/rubrics/narrative-accuracy.md +46 -0
  547. package/skill/rubrics/recommendation-eligibility.md +36 -0
  548. package/skill/rubrics/semantic-clarity.md +42 -0
  549. package/skill/rubrics/source-authority.md +37 -0
  550. package/skill/templates/finding-review.md +54 -0
  551. package/src/cli/index.js +135 -0
  552. package/src/commands/audit.js +111 -0
  553. package/src/commands/compareSnapshots.js +48 -0
  554. package/src/commands/context.js +62 -0
  555. package/src/commands/init.js +111 -0
  556. package/src/commands/inspect.js +61 -0
  557. package/src/commands/mapClaims.js +89 -0
  558. package/src/commands/schemaCmd.js +71 -0
  559. package/src/commands/substantiate.js +121 -0
  560. package/src/commands/validate.js +27 -0
  561. package/src/crawler/fetch.js +31 -0
  562. package/src/crawler/robots.js +67 -0
  563. package/src/crawler/sitemap.js +25 -0
  564. package/src/detectors/ans.js +219 -0
  565. package/src/detectors/arch.js +154 -0
  566. package/src/detectors/claim.js +230 -0
  567. package/src/detectors/crawl.js +143 -0
  568. package/src/detectors/entity.js +202 -0
  569. package/src/detectors/evd.js +164 -0
  570. package/src/detectors/framework.js +137 -0
  571. package/src/detectors/geoReco.js +218 -0
  572. package/src/detectors/index.js +50 -0
  573. package/src/detectors/lifeMeas.js +212 -0
  574. package/src/detectors/link.js +121 -0
  575. package/src/detectors/page.js +202 -0
  576. package/src/detectors/schemaData.js +249 -0
  577. package/src/detectors/tech.js +397 -0
  578. package/src/evidence/run.js +88 -0
  579. package/src/extractor/page.js +125 -0
  580. package/src/extractor/site.js +175 -0
  581. package/src/installer/index.js +1117 -0
  582. package/src/installer/providers.js +164 -0
  583. package/src/registries/index.js +127 -0
  584. package/src/reporting/report.js +82 -0
  585. package/src/shared/io.js +56 -0
  586. package/src/shared/schemaValidator.js +29 -0
@@ -0,0 +1,1117 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import crypto from 'node:crypto';
5
+ import readline from 'node:readline/promises';
6
+ import { fileURLToPath } from 'node:url';
7
+ import {
8
+ PROVIDERS,
9
+ PROVIDER_IDS,
10
+ parseProviderList,
11
+ providerBundleSkillPath,
12
+ providerDestination,
13
+ providerSkillsDir,
14
+ } from './providers.js';
15
+
16
+ export { PROVIDERS, PROVIDER_IDS, parseProviderList, normalizeProviderId } from './providers.js';
17
+
18
+ export const EXIT_CODES = Object.freeze({
19
+ success: 0,
20
+ generalFailure: 1,
21
+ invalidArguments: 2,
22
+ noValidProvider: 3,
23
+ installationCollision: 4,
24
+ integrityFailure: 5,
25
+ unsupportedEnvironment: 6,
26
+ partialFailure: 7,
27
+ updateAvailable: 10,
28
+ interrupt: 130,
29
+ });
30
+
31
+ const INSTALLER_COMMANDS = new Set(['install', 'update', 'check', 'uninstall', 'list', 'doctor']);
32
+ const HELP_COMMANDS = new Set(['help', '--help', '-h']);
33
+ const REQUIRED_SKILL_ENTRIES = [
34
+ 'SKILL.md',
35
+ 'VERSION',
36
+ 'manifest.json',
37
+ 'commands',
38
+ 'references',
39
+ 'rubrics',
40
+ 'policies',
41
+ 'templates',
42
+ 'schemas',
43
+ 'scripts',
44
+ ];
45
+
46
+ export function isInstallerCommand(command) {
47
+ return INSTALLER_COMMANDS.has(command) || HELP_COMMANDS.has(command) || command === '--version' || command === '-v';
48
+ }
49
+
50
+ export class CitableInstallerError extends Error {
51
+ constructor(message, exitCode = EXIT_CODES.generalFailure, details = {}) {
52
+ super(message);
53
+ this.name = 'CitableInstallerError';
54
+ this.exitCode = exitCode;
55
+ this.details = details;
56
+ }
57
+ }
58
+
59
+ export function packageRoot() {
60
+ return path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
61
+ }
62
+
63
+ export function loadPackageInfo(root = packageRoot()) {
64
+ return JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
65
+ }
66
+
67
+ export function getPackageVersion(root = packageRoot()) {
68
+ return loadPackageInfo(root).version;
69
+ }
70
+
71
+ export function bundleRoot(root = packageRoot()) {
72
+ return path.join(root, 'dist', 'universal');
73
+ }
74
+
75
+ function homeDir(env = process.env) {
76
+ return path.resolve(env.CITABLE_HOME || env.HOME || env.USERPROFILE || os.homedir());
77
+ }
78
+
79
+ export function resolveProjectRoot(cwd = process.cwd()) {
80
+ let dir = path.resolve(cwd);
81
+ while (true) {
82
+ if (
83
+ fs.existsSync(path.join(dir, 'package.json')) ||
84
+ fs.existsSync(path.join(dir, '.git')) ||
85
+ PROVIDER_IDS.some((id) => fs.existsSync(path.join(dir, PROVIDERS[id].projectDir)))
86
+ ) {
87
+ return dir;
88
+ }
89
+ const parent = path.dirname(dir);
90
+ if (parent === dir) return path.resolve(cwd);
91
+ dir = parent;
92
+ }
93
+ }
94
+
95
+ export function parseInstallerArgs(argv) {
96
+ const args = {
97
+ providersRaw: null,
98
+ providerSelection: null,
99
+ scope: null,
100
+ yes: false,
101
+ dryRun: false,
102
+ force: false,
103
+ json: false,
104
+ all: false,
105
+ help: false,
106
+ version: false,
107
+ failOnUpdate: false,
108
+ _: [],
109
+ };
110
+
111
+ for (let i = 0; i < argv.length; i++) {
112
+ const arg = argv[i];
113
+ if (arg === '--') {
114
+ args._.push(...argv.slice(i + 1));
115
+ break;
116
+ }
117
+ if (arg === '--yes' || arg === '-y') args.yes = true;
118
+ else if (arg === '--dry-run') args.dryRun = true;
119
+ else if (arg === '--force') args.force = true;
120
+ else if (arg === '--json') args.json = true;
121
+ else if (arg === '--all') args.all = true;
122
+ else if (arg === '--help' || arg === '-h') args.help = true;
123
+ else if (arg === '--version' || arg === '-v') args.version = true;
124
+ else if (arg === '--project' || arg === '--local') args.scope = mergeScope(args.scope, 'project');
125
+ else if (arg === '--global' || arg === '--user') args.scope = mergeScope(args.scope, 'global');
126
+ else if (arg === '--fail-on-update') args.failOnUpdate = true;
127
+ else if (arg === '--providers') {
128
+ if (i + 1 >= argv.length) throw new CitableInstallerError('--providers requires a value', EXIT_CODES.invalidArguments);
129
+ args.providersRaw = argv[++i];
130
+ } else if (arg.startsWith('--providers=')) {
131
+ args.providersRaw = arg.slice('--providers='.length);
132
+ } else if (arg === '--scope') {
133
+ if (i + 1 >= argv.length) throw new CitableInstallerError('--scope requires a value', EXIT_CODES.invalidArguments);
134
+ args.scope = mergeScope(args.scope, normalizeScopeValue(argv[++i]));
135
+ } else if (arg.startsWith('--scope=')) {
136
+ args.scope = mergeScope(args.scope, normalizeScopeValue(arg.slice('--scope='.length)));
137
+ } else if (arg.startsWith('-')) {
138
+ throw new CitableInstallerError(`unknown option: ${arg}`, EXIT_CODES.invalidArguments);
139
+ } else {
140
+ args._.push(arg);
141
+ }
142
+ }
143
+
144
+ if (args.providersRaw !== null) {
145
+ const parsed = parseProviderList(args.providersRaw);
146
+ if (parsed.unknown.length) {
147
+ const supported = PROVIDER_IDS.join(', ');
148
+ throw new CitableInstallerError(
149
+ `unknown provider(s): ${parsed.unknown.join(', ')}. Supported providers: ${supported}`,
150
+ EXIT_CODES.invalidArguments,
151
+ { unknown: parsed.unknown, supported: PROVIDER_IDS },
152
+ );
153
+ }
154
+ args.providerSelection = parsed;
155
+ }
156
+
157
+ return args;
158
+ }
159
+
160
+ function normalizeScopeValue(value) {
161
+ const normalized = String(value ?? '').trim().toLowerCase();
162
+ if (normalized === 'project' || normalized === 'local') return 'project';
163
+ if (normalized === 'global' || normalized === 'user') return 'global';
164
+ throw new CitableInstallerError(`invalid scope: ${value}. Use project or global.`, EXIT_CODES.invalidArguments);
165
+ }
166
+
167
+ function mergeScope(existing, next) {
168
+ if (existing && existing !== next) {
169
+ throw new CitableInstallerError('conflicting scope flags: choose project or global, not both', EXIT_CODES.invalidArguments);
170
+ }
171
+ return next;
172
+ }
173
+
174
+ function rootsFor(options = {}) {
175
+ const cwd = path.resolve(options.cwd || process.cwd());
176
+ const projectRoot = path.resolve(options.projectRoot || resolveProjectRoot(cwd));
177
+ const home = homeDir(options.env || process.env);
178
+ return { cwd, projectRoot, home, packageRoot: path.resolve(options.packageRoot || packageRoot()) };
179
+ }
180
+
181
+ export function detectProviders(options = {}) {
182
+ const roots = rootsFor(options);
183
+ const detected = [];
184
+ for (const id of PROVIDER_IDS) {
185
+ const provider = PROVIDERS[id];
186
+ const projectPath = path.resolve(roots.projectRoot, provider.projectDir);
187
+ const projectSkillsPath = path.resolve(roots.projectRoot, provider.projectSkillsDir);
188
+ const globalHintPaths = provider.globalHints.map((hint) => path.resolve(roots.home, hint));
189
+ const globalSkillsPath = path.resolve(roots.home, provider.globalSkillsDir);
190
+ const projectDetected = exists(projectPath) || exists(projectSkillsPath);
191
+ const globalDetected = globalHintPaths.some((hint) => exists(hint)) || exists(globalSkillsPath);
192
+ detected.push({
193
+ id,
194
+ displayName: provider.displayName,
195
+ projectDetected,
196
+ globalDetected,
197
+ detected: projectDetected || globalDetected,
198
+ projectPath,
199
+ projectSkillsPath,
200
+ globalHintPaths,
201
+ globalSkillsPath,
202
+ });
203
+ }
204
+ return detected;
205
+ }
206
+
207
+ function exists(filePath) {
208
+ try {
209
+ fs.lstatSync(filePath);
210
+ return true;
211
+ } catch {
212
+ return false;
213
+ }
214
+ }
215
+
216
+ function scopesForCommand(command, args) {
217
+ if (args.scope) return [args.scope];
218
+ if (command === 'install') return ['project'];
219
+ return ['project', 'global'];
220
+ }
221
+
222
+ export function selectProviders(command, args, detection) {
223
+ if (args.providerSelection?.kind === 'all') return PROVIDER_IDS;
224
+ if (args.providerSelection?.kind === 'explicit') return args.providerSelection.providers;
225
+ if (args.providerSelection?.kind === 'detected') {
226
+ return detection.filter((entry) => entry.detected).map((entry) => entry.id);
227
+ }
228
+
229
+ if (command === 'install') {
230
+ const projectDetected = detection.filter((entry) => entry.projectDetected).map((entry) => entry.id);
231
+ if (projectDetected.length) return projectDetected;
232
+ const globalDetected = detection.filter((entry) => entry.globalDetected).map((entry) => entry.id);
233
+ if (globalDetected.length) return globalDetected;
234
+ return [];
235
+ }
236
+
237
+ return PROVIDER_IDS;
238
+ }
239
+
240
+ export function resolveTargets(command, args, options = {}) {
241
+ const roots = rootsFor(options);
242
+ const detection = detectProviders({ ...options, ...roots });
243
+ const providers = selectProviders(command, args, detection);
244
+ const scopes = scopesForCommand(command, args);
245
+ const targets = [];
246
+ const seen = new Map();
247
+
248
+ for (const scope of scopes) {
249
+ for (const providerId of providers) {
250
+ const skillsDir = providerSkillsDir(providerId, scope, roots);
251
+ const destination = providerDestination(providerId, scope, roots);
252
+ const realDestination = safeRealDestination(destination);
253
+ const dedupeKey = realDestination || path.resolve(destination);
254
+ const target = {
255
+ providerId,
256
+ provider: PROVIDERS[providerId],
257
+ scope,
258
+ skillsDir,
259
+ destination,
260
+ realDestination,
261
+ dedupeKey,
262
+ bundlePath: path.resolve(roots.packageRoot, providerBundleSkillPath(providerId)),
263
+ scopeRoot: scope === 'project' ? roots.projectRoot : roots.home,
264
+ };
265
+ if (seen.has(dedupeKey)) {
266
+ target.duplicateOf = seen.get(dedupeKey);
267
+ } else {
268
+ seen.set(dedupeKey, `${providerId}:${scope}`);
269
+ }
270
+ targets.push(target);
271
+ }
272
+ }
273
+
274
+ return { roots, detection, providers, scopes, targets };
275
+ }
276
+
277
+ function safeRealDestination(destination) {
278
+ try {
279
+ return fs.realpathSync.native(destination);
280
+ } catch {
281
+ try {
282
+ const parent = fs.realpathSync.native(path.dirname(destination));
283
+ return path.join(parent, path.basename(destination));
284
+ } catch {
285
+ return null;
286
+ }
287
+ }
288
+ }
289
+
290
+ export function assertArchiveEntrySafe(entryName) {
291
+ const text = String(entryName ?? '');
292
+ if (!text || path.isAbsolute(text) || text.includes('\0')) {
293
+ throw new CitableInstallerError(`unsafe archive entry path: ${entryName}`, EXIT_CODES.integrityFailure);
294
+ }
295
+ const normalized = path.posix.normalize(text.replace(/\\/g, '/'));
296
+ if (normalized === '.' || normalized.startsWith('../') || normalized.includes('/../')) {
297
+ throw new CitableInstallerError(`unsafe archive entry path: ${entryName}`, EXIT_CODES.integrityFailure);
298
+ }
299
+ return normalized;
300
+ }
301
+
302
+ export function validateUniversalBundle(options = {}) {
303
+ const roots = rootsFor(options);
304
+ const root = bundleRoot(roots.packageRoot);
305
+ const manifestPath = path.join(root, 'manifest.json');
306
+ const manifest = readJson(manifestPath, 'bundle manifest');
307
+ if (manifest.name !== 'citable-universal-bundle' || manifest.version !== loadPackageInfo(roots.packageRoot).version) {
308
+ throw new CitableInstallerError('universal bundle manifest is missing or version-mismatched', EXIT_CODES.integrityFailure);
309
+ }
310
+ for (const id of PROVIDER_IDS) validateProviderBundle(id, { ...options, packageRoot: roots.packageRoot });
311
+ return manifest;
312
+ }
313
+
314
+ export function validateProviderBundle(providerId, options = {}) {
315
+ const roots = rootsFor(options);
316
+ const source = path.resolve(roots.packageRoot, providerBundleSkillPath(providerId));
317
+ const provider = PROVIDERS[providerId];
318
+ if (!provider) throw new CitableInstallerError(`unknown provider: ${providerId}`, EXIT_CODES.invalidArguments);
319
+ if (!isDirectory(source)) {
320
+ throw new CitableInstallerError(
321
+ `missing bundle for ${provider.displayName}: ${source}. Run npm run build:dist before packing or installing from source.`,
322
+ EXIT_CODES.integrityFailure,
323
+ { provider: providerId, source },
324
+ );
325
+ }
326
+ for (const entry of REQUIRED_SKILL_ENTRIES) {
327
+ if (!exists(path.join(source, entry))) {
328
+ throw new CitableInstallerError(
329
+ `bundle for ${provider.displayName} is missing required entry: ${entry}`,
330
+ EXIT_CODES.integrityFailure,
331
+ { provider: providerId, source, entry },
332
+ );
333
+ }
334
+ }
335
+ const manifest = readJson(path.join(source, 'manifest.json'), `${providerId} bundle manifest`);
336
+ if (manifest.name !== 'citable' || manifest.provider !== providerId || manifest.managedBy !== 'citable-cli') {
337
+ throw new CitableInstallerError(`bundle manifest for ${provider.displayName} is invalid`, EXIT_CODES.integrityFailure);
338
+ }
339
+ const tree = hashTree(source, { exclude: ['manifest.json'] });
340
+ if (manifest.treeHash && manifest.treeHash !== tree.treeHash) {
341
+ throw new CitableInstallerError(`bundle hash mismatch for ${provider.displayName}`, EXIT_CODES.integrityFailure);
342
+ }
343
+ return { source, manifest, tree };
344
+ }
345
+
346
+ function isDirectory(filePath) {
347
+ try {
348
+ return fs.lstatSync(filePath).isDirectory();
349
+ } catch {
350
+ return false;
351
+ }
352
+ }
353
+
354
+ function readJson(filePath, label) {
355
+ try {
356
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
357
+ } catch (err) {
358
+ throw new CitableInstallerError(`could not read ${label}: ${err.message}`, EXIT_CODES.integrityFailure, { filePath });
359
+ }
360
+ }
361
+
362
+ export function hashFile(filePath) {
363
+ return `sha256:${crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex')}`;
364
+ }
365
+
366
+ export function hashTree(root, options = {}) {
367
+ const exclude = new Set(options.exclude || []);
368
+ const files = {};
369
+ for (const rel of walkFiles(root)) {
370
+ if (exclude.has(rel)) continue;
371
+ files[rel] = hashFile(path.join(root, rel));
372
+ }
373
+ const treeHash = `sha256:${crypto.createHash('sha256').update(
374
+ Object.entries(files).map(([rel, hash]) => `${rel}\0${hash}`).join('\n'),
375
+ ).digest('hex')}`;
376
+ return { files, treeHash };
377
+ }
378
+
379
+ function walkFiles(root, base = root, out = []) {
380
+ if (!exists(root)) return out;
381
+ const entries = fs.readdirSync(root, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
382
+ for (const entry of entries) {
383
+ const abs = path.join(root, entry.name);
384
+ const rel = toPosix(path.relative(base, abs));
385
+ assertArchiveEntrySafe(rel);
386
+ if (entry.isSymbolicLink()) {
387
+ throw new CitableInstallerError(`refusing to package symlinked bundle entry: ${rel}`, EXIT_CODES.integrityFailure);
388
+ }
389
+ if (entry.isDirectory()) walkFiles(abs, base, out);
390
+ else if (entry.isFile()) out.push(rel);
391
+ }
392
+ return out;
393
+ }
394
+
395
+ function toPosix(value) {
396
+ return value.split(path.sep).join('/');
397
+ }
398
+
399
+ function makeInstalledManifest(target, packageInfo, tree) {
400
+ return {
401
+ name: 'citable',
402
+ version: packageInfo.version,
403
+ managedBy: 'citable-cli',
404
+ provider: target.providerId,
405
+ providerName: target.provider.displayName,
406
+ installedAt: new Date().toISOString(),
407
+ scope: target.scope,
408
+ sourcePackage: `${packageInfo.name}@${packageInfo.version}`,
409
+ treeHash: tree.treeHash,
410
+ files: tree.files,
411
+ };
412
+ }
413
+
414
+ export function inspectInstallation(target, options = {}) {
415
+ const destination = target.realDestination || target.destination;
416
+ const available = options.availableTree || null;
417
+ if (!exists(destination)) {
418
+ return { state: 'not installed', installed: null, path: destination, problems: [], localModifications: 'none' };
419
+ }
420
+
421
+ let stat;
422
+ try {
423
+ stat = fs.lstatSync(target.destination);
424
+ } catch {
425
+ stat = fs.lstatSync(destination);
426
+ }
427
+ const symlink = stat.isSymbolicLink();
428
+ if (!isDirectory(destination)) {
429
+ return { state: 'unmanaged', installed: null, path: destination, problems: ['destination is not a directory'], symlink, localModifications: 'unknown' };
430
+ }
431
+
432
+ const manifestPath = path.join(destination, 'manifest.json');
433
+ if (!exists(manifestPath)) {
434
+ const hasSkill = exists(path.join(destination, 'SKILL.md'));
435
+ return {
436
+ state: hasSkill ? 'unmanaged' : 'partial',
437
+ installed: null,
438
+ path: destination,
439
+ problems: [hasSkill ? 'manifest.json missing from existing skill directory' : 'empty or partial directory'],
440
+ symlink,
441
+ localModifications: 'unknown',
442
+ };
443
+ }
444
+
445
+ let manifest;
446
+ try {
447
+ manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
448
+ } catch (err) {
449
+ return { state: 'corrupt', installed: null, path: destination, problems: [`manifest parse failed: ${err.message}`], symlink, localModifications: 'unknown' };
450
+ }
451
+ if (manifest.managedBy !== 'citable-cli' || manifest.name !== 'citable') {
452
+ return { state: 'unmanaged', installed: manifest.version ?? null, path: destination, problems: ['manifest is not managed by citable-cli'], manifest, symlink, localModifications: 'unknown' };
453
+ }
454
+
455
+ const expectedFiles = manifest.files && typeof manifest.files === 'object' ? manifest.files : null;
456
+ if (!expectedFiles) {
457
+ return { state: 'corrupt', installed: manifest.version ?? null, path: destination, problems: ['manifest does not contain file hashes'], manifest, symlink, localModifications: 'unknown' };
458
+ }
459
+
460
+ const problems = [];
461
+ const modified = [];
462
+ const missing = [];
463
+ for (const [rel, expectedHash] of Object.entries(expectedFiles).sort(([a], [b]) => a.localeCompare(b))) {
464
+ const filePath = path.join(destination, rel);
465
+ if (!exists(filePath)) {
466
+ missing.push(rel);
467
+ continue;
468
+ }
469
+ if (!fs.lstatSync(filePath).isFile()) {
470
+ modified.push(rel);
471
+ continue;
472
+ }
473
+ const actualHash = hashFile(filePath);
474
+ if (actualHash !== expectedHash) modified.push(rel);
475
+ }
476
+
477
+ const actualFiles = new Set(walkFiles(destination).filter((rel) => rel !== 'manifest.json'));
478
+ const extras = [...actualFiles].filter((rel) => !Object.prototype.hasOwnProperty.call(expectedFiles, rel));
479
+ if (missing.length) problems.push(`missing files: ${missing.join(', ')}`);
480
+ if (modified.length) problems.push(`modified files: ${modified.join(', ')}`);
481
+ if (extras.length) problems.push(`unmanaged extra files: ${extras.join(', ')}`);
482
+
483
+ if (missing.length) {
484
+ return { state: 'partial', installed: manifest.version, path: destination, problems, manifest, symlink, localModifications: modified.length || extras.length ? 'present' : 'none', missing, modified, extras };
485
+ }
486
+ if (modified.length || extras.length) {
487
+ return { state: 'locally modified', installed: manifest.version, path: destination, problems, manifest, symlink, localModifications: 'present', missing, modified, extras };
488
+ }
489
+
490
+ if (!available) {
491
+ return { state: 'unknown', installed: manifest.version, path: destination, problems: [], manifest, symlink, localModifications: 'none' };
492
+ }
493
+
494
+ const currentTreeHash = manifest.treeHash ?? hashTree(destination, { exclude: ['manifest.json'] }).treeHash;
495
+ const availableVersion = options.availableVersion;
496
+ const state = currentTreeHash === available.treeHash && manifest.version === availableVersion ? 'current' : 'update available';
497
+ return { state, installed: manifest.version, path: destination, problems: [], manifest, symlink, localModifications: 'none', currentTreeHash };
498
+ }
499
+
500
+ export function createInstallPlan(command, args, options = {}) {
501
+ const context = resolveTargets(command, args, options);
502
+ const packageInfo = loadPackageInfo(context.roots.packageRoot);
503
+ const selectedTargets = context.targets.filter((target) => !target.duplicateOf);
504
+ const results = [];
505
+
506
+ if (command === 'install' && selectedTargets.length === 0) {
507
+ throw new CitableInstallerError(
508
+ 'no supported coding-agent provider was selected. Use --providers=claude,codex,cursor or create a supported harness directory.',
509
+ EXIT_CODES.noValidProvider,
510
+ );
511
+ }
512
+
513
+ for (const target of selectedTargets) {
514
+ let bundle = null;
515
+ try {
516
+ bundle = validateProviderBundle(target.providerId, { ...options, packageRoot: context.roots.packageRoot });
517
+ } catch (err) {
518
+ results.push(targetResult(target, 'failed', err.message, { exitCode: err.exitCode ?? EXIT_CODES.integrityFailure }));
519
+ continue;
520
+ }
521
+ const status = inspectInstallation(target, { availableTree: bundle.tree, availableVersion: packageInfo.version });
522
+ const action = plannedAction(command, status, args);
523
+ results.push({
524
+ provider: target.providerId,
525
+ providerName: target.provider.displayName,
526
+ scope: target.scope,
527
+ path: target.destination,
528
+ realPath: target.realDestination || target.destination,
529
+ state: status.state,
530
+ installed: status.installed,
531
+ available: packageInfo.version,
532
+ action,
533
+ filesToCreate: action === 'install' || action === 'replace' || action === 'update' ? Object.keys(bundle.tree.files) : [],
534
+ filesToReplace: exists(target.realDestination || target.destination) ? Object.keys(bundle.tree.files) : [],
535
+ filesToRemove: [],
536
+ collisions: collisionProblems(status, args),
537
+ localModifications: status.localModifications,
538
+ validation: ['validate package bundle', 'validate provider target', 'validate staged skill', 'validate final installation'],
539
+ });
540
+ }
541
+
542
+ return { command, dryRun: args.dryRun, package: { name: packageInfo.name, version: packageInfo.version }, ...context, targets: selectedTargets, results };
543
+ }
544
+
545
+ function plannedAction(command, status, args) {
546
+ if (command === 'install') {
547
+ if (status.state === 'current') return 'already current';
548
+ if (status.state === 'not installed') return 'install';
549
+ if (status.state === 'update available') return 'update';
550
+ if ((status.state === 'locally modified' || status.state === 'unmanaged') && !args.force) return 'refuse';
551
+ if (status.state === 'partial' || status.state === 'corrupt') return args.force ? 'repair' : 'refuse';
552
+ return args.force ? 'replace' : 'refuse';
553
+ }
554
+ if (command === 'update') {
555
+ if (status.state === 'current') return 'already current';
556
+ if (status.state === 'update available') return 'update';
557
+ if (status.state === 'not installed') return 'not installed';
558
+ if ((status.state === 'locally modified' || status.state === 'unmanaged') && !args.force) return 'refuse';
559
+ return args.force ? 'replace' : 'refuse';
560
+ }
561
+ if (command === 'uninstall') {
562
+ if (status.state === 'not installed') return 'not installed';
563
+ if (status.manifest?.managedBy === 'citable-cli') return 'remove';
564
+ return 'refuse';
565
+ }
566
+ return 'inspect';
567
+ }
568
+
569
+ function collisionProblems(status, args) {
570
+ if (status.state === 'unmanaged') return status.problems;
571
+ if (status.state === 'locally modified' && !args.force) return status.problems;
572
+ if ((status.state === 'partial' || status.state === 'corrupt') && !args.force) return status.problems;
573
+ return [];
574
+ }
575
+
576
+ function targetResult(target, status, message, extra = {}) {
577
+ return {
578
+ provider: target.providerId,
579
+ providerName: target.provider.displayName,
580
+ scope: target.scope,
581
+ path: target.destination,
582
+ realPath: target.realDestination || target.destination,
583
+ status,
584
+ message,
585
+ ...extra,
586
+ };
587
+ }
588
+
589
+ export async function installCommand(args, options = {}) {
590
+ if (!args.yes && canPrompt(options) && !args.providerSelection) {
591
+ const interactive = await interactiveInstallArgs(args, options);
592
+ args = { ...args, ...interactive };
593
+ }
594
+ const plan = createInstallPlan('install', args, options);
595
+ if (args.dryRun) return { ok: true, command: 'install', dryRun: true, plan: plan.results };
596
+ if (!args.yes && !canPrompt(options)) {
597
+ throw new CitableInstallerError('install needs --yes in non-interactive mode', EXIT_CODES.noValidProvider);
598
+ }
599
+ const results = [];
600
+ for (const target of plan.targets) {
601
+ results.push(installOrUpdateTarget(target, args, options));
602
+ }
603
+ return finalizeMutatingResult('install', results);
604
+ }
605
+
606
+ export async function updateCommand(args, options = {}) {
607
+ const plan = createInstallPlan('update', args, options);
608
+ if (args.dryRun) return { ok: true, command: 'update', dryRun: true, plan: plan.results };
609
+ const results = [];
610
+ for (const target of plan.targets) {
611
+ const status = inspectInstallationWithBundle(target, options);
612
+ if (status.state === 'not installed') continue;
613
+ results.push(installOrUpdateTarget(target, args, options, 'update'));
614
+ }
615
+ return finalizeMutatingResult('update', results);
616
+ }
617
+
618
+ export async function uninstallCommand(args, options = {}) {
619
+ const plan = createInstallPlan('uninstall', args, options);
620
+ if (args.dryRun) {
621
+ for (const item of plan.results) {
622
+ const status = inspectTargetByPlanItem(item);
623
+ item.filesToRemove = status.manifest?.files ? [...Object.keys(status.manifest.files), 'manifest.json'] : [];
624
+ }
625
+ return { ok: true, command: 'uninstall', dryRun: true, plan: plan.results };
626
+ }
627
+ if (!args.yes && !canPrompt(options)) {
628
+ throw new CitableInstallerError('uninstall needs --yes in non-interactive mode', EXIT_CODES.noValidProvider);
629
+ }
630
+ const results = [];
631
+ for (const target of plan.targets) results.push(uninstallTarget(target, args));
632
+ return finalizeMutatingResult('uninstall', results);
633
+ }
634
+
635
+ function inspectTargetByPlanItem(item) {
636
+ return inspectInstallation({
637
+ providerId: item.provider,
638
+ provider: PROVIDERS[item.provider],
639
+ scope: item.scope,
640
+ destination: item.path,
641
+ realDestination: item.realPath,
642
+ });
643
+ }
644
+
645
+ function inspectInstallationWithBundle(target, options = {}) {
646
+ const packageInfo = loadPackageInfo(options.packageRoot || packageRoot());
647
+ const bundle = validateProviderBundle(target.providerId, options);
648
+ return inspectInstallation(target, { availableTree: bundle.tree, availableVersion: packageInfo.version });
649
+ }
650
+
651
+ function installOrUpdateTarget(target, args, options = {}, command = 'install') {
652
+ let stage = null;
653
+ let backup = null;
654
+ try {
655
+ const packageInfo = loadPackageInfo(options.packageRoot || packageRoot());
656
+ const bundle = validateProviderBundle(target.providerId, options);
657
+ const before = inspectInstallation(target, { availableTree: bundle.tree, availableVersion: packageInfo.version });
658
+ const action = plannedAction(command, before, args);
659
+ if (action === 'already current') return targetResult(target, 'already current', 'already current', { before });
660
+ if (action === 'not installed') return targetResult(target, 'skipped', 'not installed', { before });
661
+ if (action === 'refuse') {
662
+ const code = before.state === 'unmanaged' ? EXIT_CODES.installationCollision : EXIT_CODES.integrityFailure;
663
+ return targetResult(target, 'failed', `${before.state}: ${before.problems.join('; ') || 'refusing to overwrite without --force'}`, { before, exitCode: code });
664
+ }
665
+
666
+ const installPath = target.realDestination || target.destination;
667
+ const parent = path.dirname(installPath);
668
+ fs.mkdirSync(parent, { recursive: true });
669
+ ensureTargetInsideScope(parent, target.scopeRoot);
670
+ stage = path.join(parent, `.citable-install-${process.pid}-${Date.now()}-${target.providerId}`);
671
+ backup = path.join(parent, `.citable-backup-${Date.now()}-${target.providerId}`);
672
+ copyDirSafe(bundle.source, stage);
673
+ const stagedTree = hashTree(stage, { exclude: ['manifest.json'] });
674
+ writeJsonFile(path.join(stage, 'manifest.json'), makeInstalledManifest(target, packageInfo, stagedTree));
675
+ validateStagedSkill(stage, target.providerId);
676
+
677
+ if (exists(installPath)) fs.renameSync(installPath, backup);
678
+ fs.renameSync(stage, installPath);
679
+ stage = null;
680
+
681
+ const after = inspectInstallation(target, { availableTree: stagedTree, availableVersion: packageInfo.version });
682
+ if (after.state !== 'current') {
683
+ throw new CitableInstallerError(`final validation failed: ${after.state}`, EXIT_CODES.integrityFailure, { after });
684
+ }
685
+ return targetResult(target, action === 'update' ? 'updated' : 'installed', `${action} complete`, { before, after, backup: exists(backup) ? backup : null });
686
+ } catch (err) {
687
+ if (stage) safeRm(stage);
688
+ const installPath = target.realDestination || target.destination;
689
+ if (backup && exists(backup) && !exists(installPath)) {
690
+ try {
691
+ fs.renameSync(backup, installPath);
692
+ } catch {
693
+ // Keep the original failure. The retained backup path is reported below.
694
+ }
695
+ }
696
+ return targetResult(target, 'failed', err.message, { exitCode: err.exitCode ?? EXIT_CODES.generalFailure, backup: backup && exists(backup) ? backup : null });
697
+ }
698
+ }
699
+
700
+ function ensureTargetInsideScope(targetPath, scopeRoot) {
701
+ const resolvedTarget = path.resolve(targetPath);
702
+ const resolvedScope = path.resolve(scopeRoot);
703
+ const rel = path.relative(resolvedScope, resolvedTarget);
704
+ if (rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel))) return;
705
+ throw new CitableInstallerError(`refusing to write outside selected scope: ${resolvedTarget}`, EXIT_CODES.integrityFailure);
706
+ }
707
+
708
+ function copyDirSafe(source, destination) {
709
+ fs.mkdirSync(destination, { recursive: true });
710
+ for (const entry of fs.readdirSync(source, { withFileTypes: true })) {
711
+ const src = path.join(source, entry.name);
712
+ const dest = path.join(destination, entry.name);
713
+ const rel = path.relative(source, src);
714
+ assertArchiveEntrySafe(toPosix(rel));
715
+ if (entry.isSymbolicLink()) throw new CitableInstallerError(`refusing to copy symlinked bundle entry: ${rel}`, EXIT_CODES.integrityFailure);
716
+ if (entry.isDirectory()) copyDirSafe(src, dest);
717
+ else if (entry.isFile()) fs.copyFileSync(src, dest);
718
+ }
719
+ }
720
+
721
+ function validateStagedSkill(dir, providerId) {
722
+ for (const entry of REQUIRED_SKILL_ENTRIES) {
723
+ if (!exists(path.join(dir, entry))) {
724
+ throw new CitableInstallerError(`staged ${providerId} skill is missing ${entry}`, EXIT_CODES.integrityFailure);
725
+ }
726
+ }
727
+ const manifest = readJson(path.join(dir, 'manifest.json'), 'staged manifest');
728
+ if (manifest.managedBy !== 'citable-cli' || manifest.provider !== providerId) {
729
+ throw new CitableInstallerError(`staged ${providerId} manifest is invalid`, EXIT_CODES.integrityFailure);
730
+ }
731
+ }
732
+
733
+ function writeJsonFile(filePath, data) {
734
+ fs.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
735
+ }
736
+
737
+ function safeRm(filePath) {
738
+ try {
739
+ fs.rmSync(filePath, { recursive: true, force: true });
740
+ } catch {
741
+ // best effort cleanup only
742
+ }
743
+ }
744
+
745
+ function uninstallTarget(target, args) {
746
+ try {
747
+ const status = inspectInstallation(target);
748
+ if (status.state === 'not installed') return targetResult(target, 'skipped', 'not installed', { before: status });
749
+ if (status.manifest?.managedBy !== 'citable-cli') {
750
+ return targetResult(target, 'failed', `${status.state}: refusing to remove unmanaged content`, { before: status, exitCode: EXIT_CODES.installationCollision });
751
+ }
752
+ const destination = target.realDestination || target.destination;
753
+ const relFiles = Object.keys(status.manifest.files || {}).sort((a, b) => b.localeCompare(a));
754
+ for (const rel of relFiles) {
755
+ assertArchiveEntrySafe(rel);
756
+ safeUnlink(path.join(destination, rel));
757
+ }
758
+ safeUnlink(path.join(destination, 'manifest.json'));
759
+ pruneEmptyDirs(destination, destination);
760
+ try {
761
+ if (fs.existsSync(destination) && fs.readdirSync(destination).length === 0) fs.rmdirSync(destination);
762
+ } catch {
763
+ // Directory contains user files; preserve it.
764
+ }
765
+ return targetResult(target, 'removed', 'removed managed files', { before: status, removedFiles: [...relFiles, 'manifest.json'] });
766
+ } catch (err) {
767
+ return targetResult(target, 'failed', err.message, { exitCode: err.exitCode ?? EXIT_CODES.generalFailure });
768
+ }
769
+ }
770
+
771
+ function safeUnlink(filePath) {
772
+ try {
773
+ fs.unlinkSync(filePath);
774
+ } catch (err) {
775
+ if (err.code !== 'ENOENT') throw err;
776
+ }
777
+ }
778
+
779
+ function pruneEmptyDirs(root, dir) {
780
+ if (!exists(dir) || !fs.lstatSync(dir).isDirectory()) return;
781
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
782
+ if (entry.isDirectory()) pruneEmptyDirs(root, path.join(dir, entry.name));
783
+ }
784
+ if (dir !== root) {
785
+ try {
786
+ if (fs.readdirSync(dir).length === 0) fs.rmdirSync(dir);
787
+ } catch {
788
+ // preserve non-empty or inaccessible directories
789
+ }
790
+ }
791
+ }
792
+
793
+ function finalizeMutatingResult(command, results) {
794
+ const failed = results.filter((result) => result.status === 'failed');
795
+ const changed = results.filter((result) => ['installed', 'updated', 'removed'].includes(result.status));
796
+ const ok = failed.length === 0 && (command !== 'install' || changed.length > 0 || results.some((result) => result.status === 'already current'));
797
+ const exitCode = failed.length > 0 && changed.length > 0
798
+ ? EXIT_CODES.partialFailure
799
+ : failed[0]?.exitCode ?? (ok ? EXIT_CODES.success : EXIT_CODES.noValidProvider);
800
+ return { ok, command, results, exitCode };
801
+ }
802
+
803
+ export async function checkCommand(args, options = {}) {
804
+ return inspectCommand('check', args, options);
805
+ }
806
+
807
+ export async function listCommand(args, options = {}) {
808
+ return inspectCommand('list', args, options);
809
+ }
810
+
811
+ async function inspectCommand(command, args, options = {}) {
812
+ const context = resolveTargets(command, args, options);
813
+ const packageInfo = loadPackageInfo(context.roots.packageRoot);
814
+ const rows = [];
815
+ for (const target of context.targets.filter((target) => !target.duplicateOf)) {
816
+ let bundleTree = null;
817
+ try {
818
+ bundleTree = validateProviderBundle(target.providerId, { ...options, packageRoot: context.roots.packageRoot }).tree;
819
+ } catch {
820
+ // Check/list should still report installed state if the package bundle is broken.
821
+ }
822
+ const status = inspectInstallation(target, { availableTree: bundleTree, availableVersion: packageInfo.version });
823
+ rows.push({
824
+ provider: target.providerId,
825
+ providerName: target.provider.displayName,
826
+ scope: target.scope,
827
+ path: target.destination,
828
+ installed: status.installed,
829
+ available: packageInfo.version,
830
+ state: status.state,
831
+ localModifications: status.localModifications,
832
+ problems: status.problems,
833
+ symlink: Boolean(status.symlink),
834
+ });
835
+ }
836
+ const hasUpdate = rows.some((row) => row.state === 'update available');
837
+ return { ok: true, command, providers: rows, exitCode: args.failOnUpdate && hasUpdate ? EXIT_CODES.updateAvailable : EXIT_CODES.success };
838
+ }
839
+
840
+ export async function doctorCommand(args, options = {}) {
841
+ const context = resolveTargets('doctor', args, options);
842
+ const packageInfo = loadPackageInfo(context.roots.packageRoot);
843
+ const checks = [];
844
+ const nodeMajor = Number.parseInt(process.versions.node.split('.')[0], 10);
845
+ checks.push({ level: nodeMajor >= 20 ? 'PASS' : 'FAIL', message: `Node.js ${process.versions.node}` });
846
+ checks.push({ level: 'PASS', message: `citable package ${packageInfo.version}` });
847
+ checks.push({ level: 'PASS', message: `citable command ${process.argv[1] || 'unknown'}` });
848
+
849
+ for (const entry of context.detection) {
850
+ if (entry.projectDetected) checks.push({ level: 'PASS', message: `${entry.displayName} project harness detected at ${entry.projectPath}` });
851
+ else if (entry.globalDetected) checks.push({ level: 'PASS', message: `${entry.displayName} global harness detected at ${entry.globalHintPaths.find((hint) => exists(hint)) || entry.globalSkillsPath}` });
852
+ else checks.push({ level: 'WARN', message: `${entry.displayName} not detected` });
853
+ }
854
+
855
+ const realPaths = new Map();
856
+ for (const target of context.targets.filter((target) => !target.duplicateOf)) {
857
+ const status = inspectInstallationWithOptionalBundle(target, context.roots.packageRoot, packageInfo.version);
858
+ const installedLabel = status.installed ? ` ${status.installed}` : '';
859
+ const level = ['partial', 'corrupt'].includes(status.state) ? 'FAIL'
860
+ : ['locally modified', 'unmanaged'].includes(status.state) ? 'WARN'
861
+ : status.state === 'not installed' ? 'WARN' : 'PASS';
862
+ checks.push({ level, message: `${target.provider.displayName} ${target.scope} Citable${installedLabel}: ${status.state} at ${target.destination}` });
863
+ for (const problem of status.problems) checks.push({ level: level === 'FAIL' ? 'FAIL' : 'WARN', message: `${target.provider.displayName} ${target.scope}: ${problem}` });
864
+ const real = target.realDestination || target.destination;
865
+ if (realPaths.has(real)) checks.push({ level: 'WARN', message: `${target.provider.displayName} ${target.scope} shares real path with ${realPaths.get(real)}: ${real}` });
866
+ else realPaths.set(real, `${target.provider.displayName} ${target.scope}`);
867
+ const staleTemps = staleTempDirs(path.dirname(real));
868
+ for (const temp of staleTemps) checks.push({ level: 'WARN', message: `stale installer directory: ${temp}` });
869
+ try {
870
+ if (exists(path.dirname(real))) fs.accessSync(path.dirname(real), fs.constants.R_OK | fs.constants.W_OK);
871
+ } catch {
872
+ checks.push({ level: 'FAIL', message: `cannot read/write ${path.dirname(real)}` });
873
+ }
874
+ }
875
+
876
+ const hasFail = checks.some((check) => check.level === 'FAIL');
877
+ return { ok: !hasFail, command: 'doctor', checks, exitCode: hasFail ? EXIT_CODES.integrityFailure : EXIT_CODES.success };
878
+ }
879
+
880
+ function inspectInstallationWithOptionalBundle(target, packageRootPath, version) {
881
+ try {
882
+ const bundle = validateProviderBundle(target.providerId, { packageRoot: packageRootPath });
883
+ return inspectInstallation(target, { availableTree: bundle.tree, availableVersion: version });
884
+ } catch {
885
+ return inspectInstallation(target);
886
+ }
887
+ }
888
+
889
+ function staleTempDirs(parent) {
890
+ if (!exists(parent) || !isDirectory(parent)) return [];
891
+ return fs.readdirSync(parent)
892
+ .filter((name) => name.startsWith('.citable-install-'))
893
+ .map((name) => path.join(parent, name));
894
+ }
895
+
896
+ function canPrompt(options = {}) {
897
+ const stdin = options.stdin || process.stdin;
898
+ const stdout = options.stdout || process.stdout;
899
+ return Boolean(stdin.isTTY && stdout.isTTY);
900
+ }
901
+
902
+ async function interactiveInstallArgs(args, options = {}) {
903
+ const stdout = options.stdout || process.stdout;
904
+ const stdin = options.stdin || process.stdin;
905
+ const detection = detectProviders(options);
906
+ const detected = detection.filter((entry) => entry.detected);
907
+ stdout.write('Detected coding agents:\n\n');
908
+ if (detected.length) {
909
+ for (const entry of detected) {
910
+ const where = entry.projectDetected ? entry.projectPath : (entry.globalHintPaths.find((hint) => exists(hint)) || entry.globalSkillsPath);
911
+ stdout.write(` ${entry.displayName.padEnd(16)} ${where}\n`);
912
+ }
913
+ } else {
914
+ stdout.write(' none\n');
915
+ }
916
+ stdout.write('\n');
917
+
918
+ const rl = readline.createInterface({ input: stdin, output: stdout });
919
+ const abort = new AbortController();
920
+ rl.on('SIGINT', () => abort.abort());
921
+ const ask = async (question) => {
922
+ try {
923
+ return await rl.question(question, { signal: abort.signal });
924
+ } catch (err) {
925
+ if (abort.signal.aborted || err?.name === 'AbortError') {
926
+ throw new CitableInstallerError('installation aborted', EXIT_CODES.interrupt);
927
+ }
928
+ throw err;
929
+ }
930
+ };
931
+ try {
932
+ let selected = detected.map((entry) => entry.id);
933
+ if (detected.length) {
934
+ const mode = (await ask('Install to detected agents only? [Y/n/custom] ')).trim().toLowerCase();
935
+ if (mode === 'n' || mode === 'no' || mode === 'custom' || mode === 'c') selected = await promptProviderList(ask);
936
+ } else {
937
+ selected = await promptProviderList(ask);
938
+ }
939
+ if (!selected.length) throw new CitableInstallerError('at least one provider must be selected', EXIT_CODES.noValidProvider);
940
+ const scopeAnswer = (await ask('Install location [project/global] (project): ')).trim().toLowerCase();
941
+ const scope = scopeAnswer ? normalizeScopeValue(scopeAnswer) : 'project';
942
+ const roots = rootsFor(options);
943
+ stdout.write('\nPlanned installation:\n\n');
944
+ for (const providerId of selected) stdout.write(` ${providerDestination(providerId, scope, roots)}\n`);
945
+ stdout.write('\n');
946
+ if (!args.yes) {
947
+ const proceed = (await ask('Proceed? [y/N] ')).trim().toLowerCase();
948
+ if (proceed !== 'y' && proceed !== 'yes') throw new CitableInstallerError('installation aborted', EXIT_CODES.interrupt);
949
+ }
950
+ return { providerSelection: { kind: 'explicit', providers: selected, unknown: [] }, scope, yes: true };
951
+ } finally {
952
+ rl.close();
953
+ }
954
+ }
955
+
956
+ async function promptProviderList(ask) {
957
+ const answer = (await ask(`Select agents (${PROVIDER_IDS.join(', ')} or all): `)).trim();
958
+ const parsed = parseProviderList(answer);
959
+ if (parsed.unknown.length) {
960
+ throw new CitableInstallerError(`unknown provider(s): ${parsed.unknown.join(', ')}`, EXIT_CODES.invalidArguments);
961
+ }
962
+ return parsed.kind === 'all' ? PROVIDER_IDS : parsed.providers;
963
+ }
964
+
965
+ export async function runInstallerCommand(command, argv = [], options = {}) {
966
+ if (HELP_COMMANDS.has(command) || command === undefined) {
967
+ writeHuman(installerHelp(), false);
968
+ return EXIT_CODES.success;
969
+ }
970
+ if (command === '--version' || command === '-v') {
971
+ console.log(getPackageVersion(options.packageRoot || packageRoot()));
972
+ return EXIT_CODES.success;
973
+ }
974
+
975
+ let args;
976
+ try {
977
+ args = parseInstallerArgs(argv);
978
+ if (args.help) {
979
+ writeHuman(installerHelp(), args.json);
980
+ return EXIT_CODES.success;
981
+ }
982
+ if (args.version) {
983
+ outputJsonOrHuman(args, { version: getPackageVersion(options.packageRoot || packageRoot()) }, getPackageVersion(options.packageRoot || packageRoot()));
984
+ return EXIT_CODES.success;
985
+ }
986
+ let result;
987
+ if (command === 'install') result = await installCommand(args, options);
988
+ else if (command === 'update') result = await updateCommand(args, options);
989
+ else if (command === 'uninstall') result = await uninstallCommand(args, options);
990
+ else if (command === 'check') result = await checkCommand(args, options);
991
+ else if (command === 'list') result = await listCommand(args, options);
992
+ else if (command === 'doctor') result = await doctorCommand(args, options);
993
+ else throw new CitableInstallerError(`unknown installer command: ${command}`, EXIT_CODES.invalidArguments);
994
+
995
+ outputJsonOrHuman(args, result, renderHumanResult(result));
996
+ return result.exitCode ?? (result.ok ? EXIT_CODES.success : EXIT_CODES.generalFailure);
997
+ } catch (err) {
998
+ const exitCode = err.exitCode ?? EXIT_CODES.generalFailure;
999
+ if (args?.json) {
1000
+ console.log(JSON.stringify({ ok: false, error: err.message, exitCode, details: err.details ?? {} }, null, 2));
1001
+ } else {
1002
+ console.error(`citable: ${err.message}`);
1003
+ }
1004
+ return exitCode;
1005
+ }
1006
+ }
1007
+
1008
+ function writeHuman(text, jsonMode) {
1009
+ if (jsonMode) console.error(text);
1010
+ else console.log(text);
1011
+ }
1012
+
1013
+ function outputJsonOrHuman(args, data, human) {
1014
+ if (args.json) console.log(JSON.stringify(data, null, 2));
1015
+ else console.log(human);
1016
+ }
1017
+
1018
+ function renderHumanResult(result) {
1019
+ if (result.command === 'check') return renderCheck(result);
1020
+ if (result.command === 'list') return renderList(result);
1021
+ if (result.command === 'doctor') return renderDoctor(result);
1022
+ if (result.dryRun) return renderDryRun(result);
1023
+ return renderMutation(result);
1024
+ }
1025
+
1026
+ function renderMutation(result) {
1027
+ const lines = [`citable ${result.command}: ${result.ok ? 'OK' : 'PROBLEMS'}`];
1028
+ for (const row of result.results) {
1029
+ lines.push(` ${row.providerName} (${row.scope}) ${row.status}: ${row.path}`);
1030
+ if (row.message) lines.push(` ${row.message}`);
1031
+ }
1032
+ return lines.join('\n');
1033
+ }
1034
+
1035
+ function renderDryRun(result) {
1036
+ const lines = [`citable ${result.command} dry run`];
1037
+ for (const row of result.plan) {
1038
+ lines.push(`\n${row.providerName}`);
1039
+ lines.push(` Scope: ${row.scope}`);
1040
+ lines.push(` Path: ${row.path}`);
1041
+ lines.push(` State: ${row.state}`);
1042
+ lines.push(` Planned action: ${row.action}`);
1043
+ lines.push(` Files to create: ${row.filesToCreate.length}`);
1044
+ lines.push(` Files to replace: ${row.filesToReplace.length}`);
1045
+ lines.push(` Files to remove: ${row.filesToRemove.length}`);
1046
+ lines.push(` Collisions: ${row.collisions.length ? row.collisions.join('; ') : 'none'}`);
1047
+ lines.push(` Local modifications: ${row.localModifications}`);
1048
+ lines.push(` Validation: ${row.validation.join('; ')}`);
1049
+ }
1050
+ return lines.join('\n');
1051
+ }
1052
+
1053
+ function renderCheck(result) {
1054
+ const lines = [];
1055
+ for (const row of result.providers) {
1056
+ lines.push(`${row.providerName}`);
1057
+ lines.push(` Scope: ${row.scope}`);
1058
+ lines.push(` Path: ${row.path}`);
1059
+ lines.push(` Installed: ${row.installed ?? 'not installed'}`);
1060
+ lines.push(` Available: ${row.available}`);
1061
+ lines.push(` State: ${row.state}`);
1062
+ lines.push(` Local modifications: ${row.localModifications}`);
1063
+ if (row.problems.length) lines.push(` Problems: ${row.problems.join('; ')}`);
1064
+ lines.push('');
1065
+ }
1066
+ return lines.join('\n').trimEnd();
1067
+ }
1068
+
1069
+ function renderList(result) {
1070
+ const lines = ['Citable installations'];
1071
+ for (const row of result.providers) {
1072
+ lines.push(` ${row.providerName.padEnd(16)} ${row.scope.padEnd(7)} ${row.state.padEnd(18)} ${row.installed ?? '-'} ${row.path}`);
1073
+ }
1074
+ return lines.join('\n');
1075
+ }
1076
+
1077
+ function renderDoctor(result) {
1078
+ return ['Citable doctor', '', ...result.checks.map((check) => `${check.level.padEnd(5)} ${check.message}`)].join('\n');
1079
+ }
1080
+
1081
+ export function installerHelp() {
1082
+ return `citable — SEO / AEO / GEO audit, remediation, validation, and governance
1083
+
1084
+ Usage: citable <command> [options]
1085
+
1086
+ Installer commands
1087
+ install Install the Citable skill into coding-agent harnesses
1088
+ update Update managed Citable skill installations
1089
+ check Report installed and available Citable versions
1090
+ uninstall Remove managed Citable skill files
1091
+ list List detected providers and installation state
1092
+ doctor Diagnose installer, provider, and integrity problems
1093
+ help Show this help
1094
+ --version Print the citable package version
1095
+
1096
+ Install examples
1097
+ npx @nebulacomponents/citable install
1098
+ npx @nebulacomponents/citable install --yes
1099
+ npx @nebulacomponents/citable install --providers=claude,codex,cursor --project --yes
1100
+ npx @nebulacomponents/citable install --providers=all --global --dry-run
1101
+
1102
+ Options
1103
+ --providers=<list> Provider ids, detected, or all
1104
+ --project, --local Install/check project-local skill locations
1105
+ --global, --user Install/check user-global skill locations
1106
+ --scope=<project|global> Scope equivalent
1107
+ --yes, -y Confirm non-interactively
1108
+ --dry-run Preview filesystem changes without mutation
1109
+ --force Replace locally modified or unmanaged citable directory
1110
+ --json Machine-readable output on stdout
1111
+ --fail-on-update check exits 10 when an update is available
1112
+
1113
+ Supported providers: ${PROVIDER_IDS.join(', ')}
1114
+
1115
+ No output of this tool guarantees crawling, indexing, ranking, citation,
1116
+ recommendation, inclusion, sentiment, or conversion outcomes.`;
1117
+ }