@nebulacomponents/citable 1.13.1 → 1.14.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 (305) hide show
  1. package/CHANGELOG.md +246 -0
  2. package/README.md +43 -5
  3. package/dist/universal/.agents/skills/citable/SKILL.md +13 -4
  4. package/dist/universal/.agents/skills/citable/VERSION +1 -1
  5. package/dist/universal/.agents/skills/citable/agents/citable-auditor.md +64 -0
  6. package/dist/universal/.agents/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  7. package/dist/universal/.agents/skills/citable/commands/action-findings.md +9 -1
  8. package/dist/universal/.agents/skills/citable/commands/observe-and-act.md +50 -3
  9. package/dist/universal/.agents/skills/citable/commands/plan-audit.md +35 -0
  10. package/dist/universal/.agents/skills/citable/manifest.json +26 -14
  11. package/dist/universal/.agents/skills/citable/schemas/action-plan.schema.json +13 -0
  12. package/dist/universal/.agents/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  13. package/dist/universal/.agents/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  14. package/dist/universal/.agents/skills/citable/schemas/common.defs.json +12 -1
  15. package/dist/universal/.agents/skills/citable/schemas/corroboration-import.schema.json +48 -0
  16. package/dist/universal/.agents/skills/citable/schemas/entity-graph.schema.json +75 -0
  17. package/dist/universal/.agents/skills/citable/schemas/entity.schema.json +13 -0
  18. package/dist/universal/.agents/skills/citable/schemas/observation.schema.json +1 -1
  19. package/dist/universal/.agents/skills/citable/schemas/page.schema.json +3 -0
  20. package/dist/universal/.agents/skills/citable/schemas/regional-network-import.schema.json +55 -0
  21. package/dist/universal/.agents/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  22. package/dist/universal/.agents/skills/citable/scripts/README.md +3 -1
  23. package/dist/universal/.claude/agents/citable/citable-auditor.md +64 -0
  24. package/dist/universal/.claude/agents/citable/citable-semantic-reviewer.md +66 -0
  25. package/dist/universal/.claude/agents/citable/manifest.json +12 -0
  26. package/dist/universal/.claude/skills/citable/SKILL.md +13 -4
  27. package/dist/universal/.claude/skills/citable/VERSION +1 -1
  28. package/dist/universal/.claude/skills/citable/agents/citable-auditor.md +64 -0
  29. package/dist/universal/.claude/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  30. package/dist/universal/.claude/skills/citable/commands/action-findings.md +9 -1
  31. package/dist/universal/.claude/skills/citable/commands/observe-and-act.md +50 -3
  32. package/dist/universal/.claude/skills/citable/commands/plan-audit.md +35 -0
  33. package/dist/universal/.claude/skills/citable/manifest.json +31 -14
  34. package/dist/universal/.claude/skills/citable/schemas/action-plan.schema.json +13 -0
  35. package/dist/universal/.claude/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  36. package/dist/universal/.claude/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  37. package/dist/universal/.claude/skills/citable/schemas/common.defs.json +12 -1
  38. package/dist/universal/.claude/skills/citable/schemas/corroboration-import.schema.json +48 -0
  39. package/dist/universal/.claude/skills/citable/schemas/entity-graph.schema.json +75 -0
  40. package/dist/universal/.claude/skills/citable/schemas/entity.schema.json +13 -0
  41. package/dist/universal/.claude/skills/citable/schemas/observation.schema.json +1 -1
  42. package/dist/universal/.claude/skills/citable/schemas/page.schema.json +3 -0
  43. package/dist/universal/.claude/skills/citable/schemas/regional-network-import.schema.json +55 -0
  44. package/dist/universal/.claude/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  45. package/dist/universal/.claude/skills/citable/scripts/README.md +3 -1
  46. package/dist/universal/.cursor/skills/citable/SKILL.md +13 -4
  47. package/dist/universal/.cursor/skills/citable/VERSION +1 -1
  48. package/dist/universal/.cursor/skills/citable/agents/citable-auditor.md +64 -0
  49. package/dist/universal/.cursor/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  50. package/dist/universal/.cursor/skills/citable/commands/action-findings.md +9 -1
  51. package/dist/universal/.cursor/skills/citable/commands/observe-and-act.md +50 -3
  52. package/dist/universal/.cursor/skills/citable/commands/plan-audit.md +35 -0
  53. package/dist/universal/.cursor/skills/citable/manifest.json +26 -14
  54. package/dist/universal/.cursor/skills/citable/schemas/action-plan.schema.json +13 -0
  55. package/dist/universal/.cursor/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  56. package/dist/universal/.cursor/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  57. package/dist/universal/.cursor/skills/citable/schemas/common.defs.json +12 -1
  58. package/dist/universal/.cursor/skills/citable/schemas/corroboration-import.schema.json +48 -0
  59. package/dist/universal/.cursor/skills/citable/schemas/entity-graph.schema.json +75 -0
  60. package/dist/universal/.cursor/skills/citable/schemas/entity.schema.json +13 -0
  61. package/dist/universal/.cursor/skills/citable/schemas/observation.schema.json +1 -1
  62. package/dist/universal/.cursor/skills/citable/schemas/page.schema.json +3 -0
  63. package/dist/universal/.cursor/skills/citable/schemas/regional-network-import.schema.json +55 -0
  64. package/dist/universal/.cursor/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  65. package/dist/universal/.cursor/skills/citable/scripts/README.md +3 -1
  66. package/dist/universal/.gemini/skills/citable/SKILL.md +13 -4
  67. package/dist/universal/.gemini/skills/citable/VERSION +1 -1
  68. package/dist/universal/.gemini/skills/citable/agents/citable-auditor.md +64 -0
  69. package/dist/universal/.gemini/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  70. package/dist/universal/.gemini/skills/citable/commands/action-findings.md +9 -1
  71. package/dist/universal/.gemini/skills/citable/commands/observe-and-act.md +50 -3
  72. package/dist/universal/.gemini/skills/citable/commands/plan-audit.md +35 -0
  73. package/dist/universal/.gemini/skills/citable/manifest.json +26 -14
  74. package/dist/universal/.gemini/skills/citable/schemas/action-plan.schema.json +13 -0
  75. package/dist/universal/.gemini/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  76. package/dist/universal/.gemini/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  77. package/dist/universal/.gemini/skills/citable/schemas/common.defs.json +12 -1
  78. package/dist/universal/.gemini/skills/citable/schemas/corroboration-import.schema.json +48 -0
  79. package/dist/universal/.gemini/skills/citable/schemas/entity-graph.schema.json +75 -0
  80. package/dist/universal/.gemini/skills/citable/schemas/entity.schema.json +13 -0
  81. package/dist/universal/.gemini/skills/citable/schemas/observation.schema.json +1 -1
  82. package/dist/universal/.gemini/skills/citable/schemas/page.schema.json +3 -0
  83. package/dist/universal/.gemini/skills/citable/schemas/regional-network-import.schema.json +55 -0
  84. package/dist/universal/.gemini/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  85. package/dist/universal/.gemini/skills/citable/scripts/README.md +3 -1
  86. package/dist/universal/.github/skills/citable/SKILL.md +13 -4
  87. package/dist/universal/.github/skills/citable/VERSION +1 -1
  88. package/dist/universal/.github/skills/citable/agents/citable-auditor.md +64 -0
  89. package/dist/universal/.github/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  90. package/dist/universal/.github/skills/citable/commands/action-findings.md +9 -1
  91. package/dist/universal/.github/skills/citable/commands/observe-and-act.md +50 -3
  92. package/dist/universal/.github/skills/citable/commands/plan-audit.md +35 -0
  93. package/dist/universal/.github/skills/citable/manifest.json +26 -14
  94. package/dist/universal/.github/skills/citable/schemas/action-plan.schema.json +13 -0
  95. package/dist/universal/.github/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  96. package/dist/universal/.github/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  97. package/dist/universal/.github/skills/citable/schemas/common.defs.json +12 -1
  98. package/dist/universal/.github/skills/citable/schemas/corroboration-import.schema.json +48 -0
  99. package/dist/universal/.github/skills/citable/schemas/entity-graph.schema.json +75 -0
  100. package/dist/universal/.github/skills/citable/schemas/entity.schema.json +13 -0
  101. package/dist/universal/.github/skills/citable/schemas/observation.schema.json +1 -1
  102. package/dist/universal/.github/skills/citable/schemas/page.schema.json +3 -0
  103. package/dist/universal/.github/skills/citable/schemas/regional-network-import.schema.json +55 -0
  104. package/dist/universal/.github/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  105. package/dist/universal/.github/skills/citable/scripts/README.md +3 -1
  106. package/dist/universal/.kiro/skills/citable/SKILL.md +13 -4
  107. package/dist/universal/.kiro/skills/citable/VERSION +1 -1
  108. package/dist/universal/.kiro/skills/citable/agents/citable-auditor.md +64 -0
  109. package/dist/universal/.kiro/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  110. package/dist/universal/.kiro/skills/citable/commands/action-findings.md +9 -1
  111. package/dist/universal/.kiro/skills/citable/commands/observe-and-act.md +50 -3
  112. package/dist/universal/.kiro/skills/citable/commands/plan-audit.md +35 -0
  113. package/dist/universal/.kiro/skills/citable/manifest.json +26 -14
  114. package/dist/universal/.kiro/skills/citable/schemas/action-plan.schema.json +13 -0
  115. package/dist/universal/.kiro/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  116. package/dist/universal/.kiro/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  117. package/dist/universal/.kiro/skills/citable/schemas/common.defs.json +12 -1
  118. package/dist/universal/.kiro/skills/citable/schemas/corroboration-import.schema.json +48 -0
  119. package/dist/universal/.kiro/skills/citable/schemas/entity-graph.schema.json +75 -0
  120. package/dist/universal/.kiro/skills/citable/schemas/entity.schema.json +13 -0
  121. package/dist/universal/.kiro/skills/citable/schemas/observation.schema.json +1 -1
  122. package/dist/universal/.kiro/skills/citable/schemas/page.schema.json +3 -0
  123. package/dist/universal/.kiro/skills/citable/schemas/regional-network-import.schema.json +55 -0
  124. package/dist/universal/.kiro/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  125. package/dist/universal/.kiro/skills/citable/scripts/README.md +3 -1
  126. package/dist/universal/.opencode/skills/citable/SKILL.md +13 -4
  127. package/dist/universal/.opencode/skills/citable/VERSION +1 -1
  128. package/dist/universal/.opencode/skills/citable/agents/citable-auditor.md +64 -0
  129. package/dist/universal/.opencode/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  130. package/dist/universal/.opencode/skills/citable/commands/action-findings.md +9 -1
  131. package/dist/universal/.opencode/skills/citable/commands/observe-and-act.md +50 -3
  132. package/dist/universal/.opencode/skills/citable/commands/plan-audit.md +35 -0
  133. package/dist/universal/.opencode/skills/citable/manifest.json +26 -14
  134. package/dist/universal/.opencode/skills/citable/schemas/action-plan.schema.json +13 -0
  135. package/dist/universal/.opencode/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  136. package/dist/universal/.opencode/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  137. package/dist/universal/.opencode/skills/citable/schemas/common.defs.json +12 -1
  138. package/dist/universal/.opencode/skills/citable/schemas/corroboration-import.schema.json +48 -0
  139. package/dist/universal/.opencode/skills/citable/schemas/entity-graph.schema.json +75 -0
  140. package/dist/universal/.opencode/skills/citable/schemas/entity.schema.json +13 -0
  141. package/dist/universal/.opencode/skills/citable/schemas/observation.schema.json +1 -1
  142. package/dist/universal/.opencode/skills/citable/schemas/page.schema.json +3 -0
  143. package/dist/universal/.opencode/skills/citable/schemas/regional-network-import.schema.json +55 -0
  144. package/dist/universal/.opencode/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  145. package/dist/universal/.opencode/skills/citable/scripts/README.md +3 -1
  146. package/dist/universal/.pi/agent/skills/citable/SKILL.md +13 -4
  147. package/dist/universal/.pi/agent/skills/citable/VERSION +1 -1
  148. package/dist/universal/.pi/agent/skills/citable/agents/citable-auditor.md +64 -0
  149. package/dist/universal/.pi/agent/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  150. package/dist/universal/.pi/agent/skills/citable/commands/action-findings.md +9 -1
  151. package/dist/universal/.pi/agent/skills/citable/commands/observe-and-act.md +50 -3
  152. package/dist/universal/.pi/agent/skills/citable/commands/plan-audit.md +35 -0
  153. package/dist/universal/.pi/agent/skills/citable/manifest.json +26 -14
  154. package/dist/universal/.pi/agent/skills/citable/schemas/action-plan.schema.json +13 -0
  155. package/dist/universal/.pi/agent/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  156. package/dist/universal/.pi/agent/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  157. package/dist/universal/.pi/agent/skills/citable/schemas/common.defs.json +12 -1
  158. package/dist/universal/.pi/agent/skills/citable/schemas/corroboration-import.schema.json +48 -0
  159. package/dist/universal/.pi/agent/skills/citable/schemas/entity-graph.schema.json +75 -0
  160. package/dist/universal/.pi/agent/skills/citable/schemas/entity.schema.json +13 -0
  161. package/dist/universal/.pi/agent/skills/citable/schemas/observation.schema.json +1 -1
  162. package/dist/universal/.pi/agent/skills/citable/schemas/page.schema.json +3 -0
  163. package/dist/universal/.pi/agent/skills/citable/schemas/regional-network-import.schema.json +55 -0
  164. package/dist/universal/.pi/agent/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  165. package/dist/universal/.pi/agent/skills/citable/scripts/README.md +3 -1
  166. package/dist/universal/.qoder/skills/citable/SKILL.md +13 -4
  167. package/dist/universal/.qoder/skills/citable/VERSION +1 -1
  168. package/dist/universal/.qoder/skills/citable/agents/citable-auditor.md +64 -0
  169. package/dist/universal/.qoder/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  170. package/dist/universal/.qoder/skills/citable/commands/action-findings.md +9 -1
  171. package/dist/universal/.qoder/skills/citable/commands/observe-and-act.md +50 -3
  172. package/dist/universal/.qoder/skills/citable/commands/plan-audit.md +35 -0
  173. package/dist/universal/.qoder/skills/citable/manifest.json +26 -14
  174. package/dist/universal/.qoder/skills/citable/schemas/action-plan.schema.json +13 -0
  175. package/dist/universal/.qoder/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  176. package/dist/universal/.qoder/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  177. package/dist/universal/.qoder/skills/citable/schemas/common.defs.json +12 -1
  178. package/dist/universal/.qoder/skills/citable/schemas/corroboration-import.schema.json +48 -0
  179. package/dist/universal/.qoder/skills/citable/schemas/entity-graph.schema.json +75 -0
  180. package/dist/universal/.qoder/skills/citable/schemas/entity.schema.json +13 -0
  181. package/dist/universal/.qoder/skills/citable/schemas/observation.schema.json +1 -1
  182. package/dist/universal/.qoder/skills/citable/schemas/page.schema.json +3 -0
  183. package/dist/universal/.qoder/skills/citable/schemas/regional-network-import.schema.json +55 -0
  184. package/dist/universal/.qoder/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  185. package/dist/universal/.qoder/skills/citable/scripts/README.md +3 -1
  186. package/dist/universal/.rovodev/skills/citable/SKILL.md +13 -4
  187. package/dist/universal/.rovodev/skills/citable/VERSION +1 -1
  188. package/dist/universal/.rovodev/skills/citable/agents/citable-auditor.md +64 -0
  189. package/dist/universal/.rovodev/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  190. package/dist/universal/.rovodev/skills/citable/commands/action-findings.md +9 -1
  191. package/dist/universal/.rovodev/skills/citable/commands/observe-and-act.md +50 -3
  192. package/dist/universal/.rovodev/skills/citable/commands/plan-audit.md +35 -0
  193. package/dist/universal/.rovodev/skills/citable/manifest.json +26 -14
  194. package/dist/universal/.rovodev/skills/citable/schemas/action-plan.schema.json +13 -0
  195. package/dist/universal/.rovodev/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  196. package/dist/universal/.rovodev/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  197. package/dist/universal/.rovodev/skills/citable/schemas/common.defs.json +12 -1
  198. package/dist/universal/.rovodev/skills/citable/schemas/corroboration-import.schema.json +48 -0
  199. package/dist/universal/.rovodev/skills/citable/schemas/entity-graph.schema.json +75 -0
  200. package/dist/universal/.rovodev/skills/citable/schemas/entity.schema.json +13 -0
  201. package/dist/universal/.rovodev/skills/citable/schemas/observation.schema.json +1 -1
  202. package/dist/universal/.rovodev/skills/citable/schemas/page.schema.json +3 -0
  203. package/dist/universal/.rovodev/skills/citable/schemas/regional-network-import.schema.json +55 -0
  204. package/dist/universal/.rovodev/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  205. package/dist/universal/.rovodev/skills/citable/scripts/README.md +3 -1
  206. package/dist/universal/.trae/skills/citable/SKILL.md +13 -4
  207. package/dist/universal/.trae/skills/citable/VERSION +1 -1
  208. package/dist/universal/.trae/skills/citable/agents/citable-auditor.md +64 -0
  209. package/dist/universal/.trae/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  210. package/dist/universal/.trae/skills/citable/commands/action-findings.md +9 -1
  211. package/dist/universal/.trae/skills/citable/commands/observe-and-act.md +50 -3
  212. package/dist/universal/.trae/skills/citable/commands/plan-audit.md +35 -0
  213. package/dist/universal/.trae/skills/citable/manifest.json +26 -14
  214. package/dist/universal/.trae/skills/citable/schemas/action-plan.schema.json +13 -0
  215. package/dist/universal/.trae/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  216. package/dist/universal/.trae/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  217. package/dist/universal/.trae/skills/citable/schemas/common.defs.json +12 -1
  218. package/dist/universal/.trae/skills/citable/schemas/corroboration-import.schema.json +48 -0
  219. package/dist/universal/.trae/skills/citable/schemas/entity-graph.schema.json +75 -0
  220. package/dist/universal/.trae/skills/citable/schemas/entity.schema.json +13 -0
  221. package/dist/universal/.trae/skills/citable/schemas/observation.schema.json +1 -1
  222. package/dist/universal/.trae/skills/citable/schemas/page.schema.json +3 -0
  223. package/dist/universal/.trae/skills/citable/schemas/regional-network-import.schema.json +55 -0
  224. package/dist/universal/.trae/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  225. package/dist/universal/.trae/skills/citable/scripts/README.md +3 -1
  226. package/dist/universal/.trae-cn/skills/citable/SKILL.md +13 -4
  227. package/dist/universal/.trae-cn/skills/citable/VERSION +1 -1
  228. package/dist/universal/.trae-cn/skills/citable/agents/citable-auditor.md +64 -0
  229. package/dist/universal/.trae-cn/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  230. package/dist/universal/.trae-cn/skills/citable/commands/action-findings.md +9 -1
  231. package/dist/universal/.trae-cn/skills/citable/commands/observe-and-act.md +50 -3
  232. package/dist/universal/.trae-cn/skills/citable/commands/plan-audit.md +35 -0
  233. package/dist/universal/.trae-cn/skills/citable/manifest.json +26 -14
  234. package/dist/universal/.trae-cn/skills/citable/schemas/action-plan.schema.json +13 -0
  235. package/dist/universal/.trae-cn/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  236. package/dist/universal/.trae-cn/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  237. package/dist/universal/.trae-cn/skills/citable/schemas/common.defs.json +12 -1
  238. package/dist/universal/.trae-cn/skills/citable/schemas/corroboration-import.schema.json +48 -0
  239. package/dist/universal/.trae-cn/skills/citable/schemas/entity-graph.schema.json +75 -0
  240. package/dist/universal/.trae-cn/skills/citable/schemas/entity.schema.json +13 -0
  241. package/dist/universal/.trae-cn/skills/citable/schemas/observation.schema.json +1 -1
  242. package/dist/universal/.trae-cn/skills/citable/schemas/page.schema.json +3 -0
  243. package/dist/universal/.trae-cn/skills/citable/schemas/regional-network-import.schema.json +55 -0
  244. package/dist/universal/.trae-cn/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  245. package/dist/universal/.trae-cn/skills/citable/scripts/README.md +3 -1
  246. package/dist/universal/manifest.json +270 -171
  247. package/package.json +1 -1
  248. package/schemas/action-plan.schema.json +13 -0
  249. package/schemas/browser-evidence-plan.schema.json +14 -0
  250. package/schemas/canonical-index-import.schema.json +30 -0
  251. package/schemas/common.defs.json +12 -1
  252. package/schemas/corroboration-import.schema.json +48 -0
  253. package/schemas/entity-graph.schema.json +75 -0
  254. package/schemas/entity.schema.json +13 -0
  255. package/schemas/observation.schema.json +1 -1
  256. package/schemas/page.schema.json +3 -0
  257. package/schemas/regional-network-import.schema.json +55 -0
  258. package/schemas/source-identity-chain.schema.json +83 -0
  259. package/scripts/build-dist.js +36 -3
  260. package/skill/SKILL.md +13 -4
  261. package/skill/agents/citable-auditor.md +64 -0
  262. package/skill/agents/citable-semantic-reviewer.md +66 -0
  263. package/skill/commands/action-findings.md +9 -1
  264. package/skill/commands/observe-and-act.md +50 -3
  265. package/skill/commands/plan-audit.md +35 -0
  266. package/src/cli/index.js +38 -5
  267. package/src/commands/actionPlan.js +12 -1
  268. package/src/commands/audit.js +4 -0
  269. package/src/commands/demo.js +36 -0
  270. package/src/commands/fixtures/demo/.citable/claims.yaml +27 -0
  271. package/src/commands/fixtures/demo/.citable/competitors.yaml +3 -0
  272. package/src/commands/fixtures/demo/.citable/config.yaml +18 -0
  273. package/src/commands/fixtures/demo/.citable/crawlers.yaml +30 -0
  274. package/src/commands/fixtures/demo/.citable/entities.yaml +28 -0
  275. package/src/commands/fixtures/demo/.citable/evidence.yaml +19 -0
  276. package/src/commands/fixtures/demo/.citable/experiments.yaml +3 -0
  277. package/src/commands/fixtures/demo/.citable/pages.yaml +54 -0
  278. package/src/commands/fixtures/demo/.citable/prompts.yaml +11 -0
  279. package/src/commands/fixtures/demo/.citable/queries.yaml +10 -0
  280. package/src/commands/fixtures/demo/site/index.html +12 -0
  281. package/src/commands/fixtures/demo/site/learn/execution-governance/index.html +14 -0
  282. package/src/commands/fixtures/demo/site/pricing/index.html +10 -0
  283. package/src/commands/fixtures/demo/site/products/gatekeeper/index.html +15 -0
  284. package/src/commands/fixtures/demo/site/robots.txt +7 -0
  285. package/src/commands/fixtures/demo/site/sitemap.xml +7 -0
  286. package/src/commands/init.js +5 -2
  287. package/src/commands/observe.js +313 -15
  288. package/src/commands/planAudit.js +151 -0
  289. package/src/commands/schemaCmd.js +5 -1
  290. package/src/commands/substantiate.js +22 -10
  291. package/src/crawler/fetch.js +34 -6
  292. package/src/extractor/page.js +17 -0
  293. package/src/installer/index.js +282 -28
  294. package/src/installer/providers.js +11 -0
  295. package/src/observations/browserJourney.js +15 -7
  296. package/src/observations/claimDiff.js +83 -0
  297. package/src/observations/common.js +15 -0
  298. package/src/observations/entityGraph.js +89 -0
  299. package/src/observations/renderParity.js +49 -0
  300. package/src/observations/sourceIdentity.js +62 -0
  301. package/src/registries/seed.js +95 -0
  302. package/src/registries/seeds/saas-pricing/claims.yaml +18 -0
  303. package/src/registries/seeds/saas-pricing/entities.yaml +20 -0
  304. package/src/registries/seeds/saas-pricing/seed.json +6 -0
  305. package/src/shared/checklist.js +19 -0
@@ -8,6 +8,7 @@ import {
8
8
  PROVIDERS,
9
9
  PROVIDER_IDS,
10
10
  parseProviderList,
11
+ providerAgentProfilesDestination,
11
12
  providerBundleSkillPath,
12
13
  providerDestination,
13
14
  providerSkillsDir,
@@ -421,7 +422,7 @@ function toPosix(value) {
421
422
 
422
423
  function makeInstalledManifest(target, packageInfo, tree) {
423
424
  return {
424
- name: 'citable',
425
+ name: target.manifestName || 'citable',
425
426
  version: packageInfo.version,
426
427
  managedBy: 'citable-cli',
427
428
  provider: target.providerId,
@@ -471,7 +472,7 @@ export function inspectInstallation(target, options = {}) {
471
472
  } catch (err) {
472
473
  return { state: 'corrupt', installed: null, path: destination, problems: [`manifest parse failed: ${err.message}`], symlink, localModifications: 'unknown' };
473
474
  }
474
- if (manifest.managedBy !== 'citable-cli' || manifest.name !== 'citable') {
475
+ if (manifest.managedBy !== 'citable-cli' || manifest.name !== (target.manifestName || 'citable')) {
475
476
  return { state: 'unmanaged', installed: manifest.version ?? null, path: destination, problems: ['manifest is not managed by citable-cli'], manifest, symlink, localModifications: 'unknown' };
476
477
  }
477
478
 
@@ -543,6 +544,13 @@ export function createInstallPlan(command, args, options = {}) {
543
544
  }
544
545
  const status = inspectInstallation(target, { availableTree: bundle.tree, availableVersion: packageInfo.version });
545
546
  const action = plannedAction(command, status, args);
547
+ let profiles = null;
548
+ try {
549
+ profiles = prepareAgentProfiles(target, bundle, packageInfo, args, command, options);
550
+ } catch (err) {
551
+ results.push(targetResult(target, 'failed', err.message, { exitCode: err.exitCode ?? EXIT_CODES.integrityFailure }));
552
+ continue;
553
+ }
546
554
  results.push({
547
555
  provider: target.providerId,
548
556
  providerName: target.provider.displayName,
@@ -558,6 +566,16 @@ export function createInstallPlan(command, args, options = {}) {
558
566
  filesToRemove: [],
559
567
  collisions: collisionProblems(status, args),
560
568
  localModifications: status.localModifications,
569
+ agentProfiles: profiles ? {
570
+ path: profiles.target.destination,
571
+ state: profiles.before.state,
572
+ action: profiles.action,
573
+ filesToCreate: ['install', 'replace', 'update', 'repair'].includes(profiles.action)
574
+ ? Object.keys(profiles.tree.files)
575
+ : [],
576
+ collisions: collisionProblems(profiles.before, args),
577
+ localModifications: profiles.before.localModifications,
578
+ } : null,
561
579
  validation: ['validate package bundle', 'validate provider target', 'validate staged skill', 'validate final installation'],
562
580
  });
563
581
  }
@@ -674,41 +692,65 @@ function inspectInstallationWithBundle(target, options = {}) {
674
692
  function installOrUpdateTarget(target, args, options = {}, command = 'install') {
675
693
  let stage = null;
676
694
  let backup = null;
695
+ let installedSkill = false;
677
696
  try {
678
697
  const packageInfo = loadPackageInfo(options.packageRoot || packageRoot());
679
698
  const bundle = validateProviderBundle(target.providerId, options);
680
699
  const before = inspectInstallation(target, { availableTree: bundle.tree, availableVersion: packageInfo.version });
681
700
  const action = plannedAction(command, before, args);
682
- if (action === 'already current') return targetResult(target, 'already current', 'already current', { before });
701
+ const profiles = prepareAgentProfiles(target, bundle, packageInfo, args, command, options);
702
+ if (action === 'already current' && (!profiles || profiles.action === 'already current')) {
703
+ return targetResult(target, 'already current', 'already current', { before, agentProfiles: profiles?.before ?? null });
704
+ }
683
705
  if (action === 'not installed') return targetResult(target, 'skipped', 'not installed', { before });
684
706
  if (action === 'refuse') {
685
707
  const code = before.state === 'unmanaged' ? EXIT_CODES.installationCollision : EXIT_CODES.integrityFailure;
686
708
  return targetResult(target, 'failed', `${before.state}: ${before.problems.join('; ') || 'refusing to overwrite without --force'}`, { before, exitCode: code });
687
709
  }
710
+ if (profiles?.action === 'refuse') {
711
+ const code = profiles.before.state === 'unmanaged' ? EXIT_CODES.installationCollision : EXIT_CODES.integrityFailure;
712
+ return targetResult(target, 'failed', `agent profile ${profiles.before.state}: ${profiles.before.problems.join('; ') || 'refusing to overwrite without --force'}`, {
713
+ before,
714
+ agentProfiles: profiles.before,
715
+ exitCode: code,
716
+ });
717
+ }
688
718
 
689
719
  const installPath = target.realDestination || target.destination;
690
- const parent = path.dirname(installPath);
691
- fs.mkdirSync(parent, { recursive: true });
692
- ensureTargetInsideScope(parent, target.scopeRoot);
693
- stage = path.join(parent, `.citable-install-${process.pid}-${Date.now()}-${target.providerId}`);
694
- backup = path.join(parent, `.citable-backup-${Date.now()}-${target.providerId}`);
695
- copyDirSafe(bundle.source, stage);
696
- const stagedTree = hashTree(stage, { exclude: ['manifest.json'] });
697
- writeJsonFile(path.join(stage, 'manifest.json'), makeInstalledManifest(target, packageInfo, stagedTree));
698
- validateStagedSkill(stage, target.providerId);
699
-
700
- if (exists(installPath)) fs.renameSync(installPath, backup);
701
- fs.renameSync(stage, installPath);
702
- stage = null;
703
-
704
- const after = inspectInstallation(target, { availableTree: stagedTree, availableVersion: packageInfo.version });
705
- if (after.state !== 'current') {
706
- throw new CitableInstallerError(`final validation failed: ${after.state}`, EXIT_CODES.integrityFailure, { after });
720
+ let after = before;
721
+ if (action !== 'already current') {
722
+ const parent = path.dirname(installPath);
723
+ fs.mkdirSync(parent, { recursive: true });
724
+ ensureTargetInsideScope(parent, target.scopeRoot);
725
+ stage = path.join(parent, `.citable-install-${process.pid}-${Date.now()}-${target.providerId}`);
726
+ backup = path.join(parent, `.citable-backup-${Date.now()}-${target.providerId}`);
727
+ copyDirSafe(bundle.source, stage);
728
+ const stagedTree = hashTree(stage, { exclude: ['manifest.json'] });
729
+ writeJsonFile(path.join(stage, 'manifest.json'), makeInstalledManifest(target, packageInfo, stagedTree));
730
+ validateStagedSkill(stage, target.providerId);
731
+
732
+ if (exists(installPath)) fs.renameSync(installPath, backup);
733
+ fs.renameSync(stage, installPath);
734
+ stage = null;
735
+ installedSkill = true;
736
+
737
+ after = inspectInstallation(target, { availableTree: stagedTree, availableVersion: packageInfo.version });
738
+ if (after.state !== 'current') {
739
+ throw new CitableInstallerError(`final validation failed: ${after.state}`, EXIT_CODES.integrityFailure, { after });
740
+ }
707
741
  }
708
- return targetResult(target, action === 'update' ? 'updated' : 'installed', `${action} complete`, { before, after, backup: exists(backup) ? backup : null });
742
+ const profileResult = profiles ? installAgentProfiles(profiles, packageInfo) : null;
743
+ const resultStatus = action === 'update' || (action === 'already current' && profileResult?.changed) ? 'updated' : 'installed';
744
+ return targetResult(target, resultStatus, `${action === 'already current' ? 'agent profile install' : action} complete`, {
745
+ before,
746
+ after,
747
+ agentProfiles: profileResult,
748
+ backup: exists(backup) ? backup : null,
749
+ });
709
750
  } catch (err) {
710
751
  if (stage) safeRm(stage);
711
752
  const installPath = target.realDestination || target.destination;
753
+ if (installedSkill && exists(installPath)) safeRm(installPath);
712
754
  if (backup && exists(backup) && !exists(installPath)) {
713
755
  try {
714
756
  fs.renameSync(backup, installPath);
@@ -720,6 +762,80 @@ function installOrUpdateTarget(target, args, options = {}, command = 'install')
720
762
  }
721
763
  }
722
764
 
765
+ function prepareAgentProfiles(target, bundle, packageInfo, args, command, options) {
766
+ const metadata = bundle.manifest.agentProfiles;
767
+ if (!metadata || metadata.status !== 'available') return null;
768
+ const destination = providerAgentProfilesDestination(target.providerId, target.scope, rootsFor(options));
769
+ if (!destination) throw new CitableInstallerError(`agent profiles are not supported for ${target.provider.displayName}`, EXIT_CODES.unsupportedEnvironment);
770
+ const source = path.resolve(bundleRoot(options.packageRoot || packageRoot()), metadata.path);
771
+ const sourceManifest = readJson(path.join(source, 'manifest.json'), `${target.providerId} agent profile manifest`);
772
+ if (
773
+ sourceManifest.name !== 'citable-agent-profiles'
774
+ || sourceManifest.provider !== target.providerId
775
+ || sourceManifest.managedBy !== 'citable-cli'
776
+ ) {
777
+ throw new CitableInstallerError(`agent profile manifest for ${target.provider.displayName} is invalid`, EXIT_CODES.integrityFailure);
778
+ }
779
+ const tree = hashTree(source, { exclude: ['manifest.json'] });
780
+ if (tree.treeHash !== metadata.treeHash || tree.treeHash !== sourceManifest.treeHash) {
781
+ throw new CitableInstallerError(`agent profile bundle hash mismatch for ${target.provider.displayName}`, EXIT_CODES.integrityFailure);
782
+ }
783
+ const profileTarget = {
784
+ ...target,
785
+ destination,
786
+ realDestination: null,
787
+ manifestName: 'citable-agent-profiles',
788
+ };
789
+ const before = inspectInstallation(profileTarget, { availableTree: tree, availableVersion: packageInfo.version });
790
+ return {
791
+ target: profileTarget,
792
+ source,
793
+ tree,
794
+ before,
795
+ action: plannedAction(command, before, args),
796
+ };
797
+ }
798
+
799
+ function installAgentProfiles(profiles, packageInfo) {
800
+ if (profiles.action === 'already current') return { changed: false, before: profiles.before, after: profiles.before };
801
+ const destination = profiles.target.destination;
802
+ const parent = path.dirname(destination);
803
+ let stage = null;
804
+ let backup = null;
805
+ try {
806
+ fs.mkdirSync(parent, { recursive: true });
807
+ ensureTargetInsideScope(parent, profiles.target.scopeRoot);
808
+ stage = path.join(parent, `.citable-agent-install-${process.pid}-${Date.now()}`);
809
+ backup = path.join(parent, `.citable-agent-backup-${Date.now()}`);
810
+ copyDirSafe(profiles.source, stage);
811
+ writeJsonFile(path.join(stage, 'manifest.json'), makeInstalledManifest(profiles.target, packageInfo, profiles.tree));
812
+ validateStagedProfiles(stage, profiles.target.providerId, profiles.tree);
813
+ if (exists(destination)) fs.renameSync(destination, backup);
814
+ fs.renameSync(stage, destination);
815
+ stage = null;
816
+ const after = inspectInstallation(profiles.target, { availableTree: profiles.tree, availableVersion: packageInfo.version });
817
+ if (after.state !== 'current') throw new CitableInstallerError(`final agent profile validation failed: ${after.state}`, EXIT_CODES.integrityFailure);
818
+ return { changed: true, before: profiles.before, after, backup: exists(backup) ? backup : null };
819
+ } catch (err) {
820
+ if (stage) safeRm(stage);
821
+ if (exists(destination)) safeRm(destination);
822
+ if (backup && exists(backup)) fs.renameSync(backup, destination);
823
+ throw err;
824
+ }
825
+ }
826
+
827
+ function validateStagedProfiles(dir, providerId, tree) {
828
+ for (const rel of Object.keys(tree.files)) {
829
+ if (!exists(path.join(dir, rel))) {
830
+ throw new CitableInstallerError(`staged ${providerId} agent profiles are missing ${rel}`, EXIT_CODES.integrityFailure);
831
+ }
832
+ }
833
+ const manifest = readJson(path.join(dir, 'manifest.json'), 'staged agent profile manifest');
834
+ if (manifest.managedBy !== 'citable-cli' || manifest.provider !== providerId || manifest.name !== 'citable-agent-profiles') {
835
+ throw new CitableInstallerError(`staged ${providerId} agent profile manifest is invalid`, EXIT_CODES.integrityFailure);
836
+ }
837
+ }
838
+
723
839
  function ensureTargetInsideScope(targetPath, scopeRoot) {
724
840
  // Use realpathSync when possible to resolve symlinks (e.g. /tmp → /private/tmp on macOS)
725
841
  // so that cross-symlink paths compare correctly.
@@ -777,6 +893,7 @@ function uninstallTarget(target, args) {
777
893
  if (status.manifest?.managedBy !== 'citable-cli') {
778
894
  return targetResult(target, 'failed', `${status.state}: refusing to remove unmanaged content`, { before: status, exitCode: EXIT_CODES.installationCollision });
779
895
  }
896
+ const profileResult = uninstallAgentProfiles(target);
780
897
  const destination = target.realDestination || target.destination;
781
898
  const relFiles = Object.keys(status.manifest.files || {}).sort((a, b) => b.localeCompare(a));
782
899
  for (const rel of relFiles) {
@@ -790,12 +907,39 @@ function uninstallTarget(target, args) {
790
907
  } catch {
791
908
  // Directory contains user files; preserve it.
792
909
  }
793
- return targetResult(target, 'removed', 'removed managed files', { before: status, removedFiles: [...relFiles, 'manifest.json'] });
910
+ return targetResult(target, 'removed', 'removed managed files', {
911
+ before: status,
912
+ agentProfiles: profileResult,
913
+ removedFiles: [...relFiles, 'manifest.json'],
914
+ });
794
915
  } catch (err) {
795
916
  return targetResult(target, 'failed', err.message, { exitCode: err.exitCode ?? EXIT_CODES.generalFailure });
796
917
  }
797
918
  }
798
919
 
920
+ function uninstallAgentProfiles(target) {
921
+ const destination = providerAgentProfilesDestination(target.providerId, target.scope, {
922
+ projectRoot: target.scope === 'project' ? target.scopeRoot : '',
923
+ home: target.scope === 'global' ? target.scopeRoot : '',
924
+ });
925
+ if (!destination) return null;
926
+ const profileTarget = { ...target, destination, realDestination: null, manifestName: 'citable-agent-profiles' };
927
+ const status = inspectInstallation(profileTarget);
928
+ if (status.state === 'not installed') return { status: 'not installed' };
929
+ if (status.manifest?.managedBy !== 'citable-cli' || status.manifest?.name !== 'citable-agent-profiles') {
930
+ return { status: 'preserved', state: status.state, problems: status.problems };
931
+ }
932
+ const relFiles = Object.keys(status.manifest.files || {}).sort((a, b) => b.localeCompare(a));
933
+ for (const rel of relFiles) {
934
+ assertArchiveEntrySafe(rel);
935
+ safeUnlink(path.join(destination, rel));
936
+ }
937
+ safeUnlink(path.join(destination, 'manifest.json'));
938
+ pruneEmptyDirs(destination, destination);
939
+ if (exists(destination) && fs.readdirSync(destination).length === 0) fs.rmdirSync(destination);
940
+ return { status: 'removed', removedFiles: [...relFiles, 'manifest.json'] };
941
+ }
942
+
799
943
  function safeUnlink(filePath) {
800
944
  try {
801
945
  fs.unlinkSync(filePath);
@@ -841,13 +985,31 @@ async function inspectCommand(command, args, options = {}) {
841
985
  const packageInfo = loadPackageInfo(context.roots.packageRoot);
842
986
  const rows = [];
843
987
  for (const target of context.targets.filter((target) => !target.duplicateOf)) {
988
+ let bundle = null;
844
989
  let bundleTree = null;
845
990
  try {
846
- bundleTree = validateProviderBundle(target.providerId, { ...options, packageRoot: context.roots.packageRoot }).tree;
991
+ bundle = validateProviderBundle(target.providerId, { ...options, packageRoot: context.roots.packageRoot });
992
+ bundleTree = bundle.tree;
847
993
  } catch {
848
994
  // Check/list should still report installed state if the package bundle is broken.
849
995
  }
850
996
  const status = inspectInstallation(target, { availableTree: bundleTree, availableVersion: packageInfo.version });
997
+ let agentProfiles = null;
998
+ if (bundle) {
999
+ try {
1000
+ agentProfiles = prepareAgentProfiles(target, bundle, packageInfo, args, 'install', options)?.before ?? null;
1001
+ } catch (err) {
1002
+ agentProfiles = {
1003
+ state: 'corrupt',
1004
+ installed: null,
1005
+ path: providerAgentProfilesDestination(target.providerId, target.scope, context.roots),
1006
+ localModifications: 'unknown',
1007
+ problems: [err.message],
1008
+ };
1009
+ }
1010
+ }
1011
+ const combinedState = combineInstalledState(status.state, agentProfiles?.state);
1012
+ const profileProblems = (agentProfiles?.problems || []).map((problem) => `agent profiles: ${problem}`);
851
1013
  rows.push({
852
1014
  provider: target.providerId,
853
1015
  providerName: target.provider.displayName,
@@ -855,9 +1017,10 @@ async function inspectCommand(command, args, options = {}) {
855
1017
  path: target.destination,
856
1018
  installed: status.installed,
857
1019
  available: packageInfo.version,
858
- state: status.state,
859
- localModifications: status.localModifications,
860
- problems: status.problems,
1020
+ state: combinedState,
1021
+ localModifications: status.localModifications === 'present' || agentProfiles?.localModifications === 'present' ? 'present' : status.localModifications,
1022
+ problems: [...status.problems, ...profileProblems],
1023
+ agentProfiles,
861
1024
  symlink: Boolean(status.symlink),
862
1025
  });
863
1026
  }
@@ -865,9 +1028,18 @@ async function inspectCommand(command, args, options = {}) {
865
1028
  return { ok: true, command, providers: rows, exitCode: args.failOnUpdate && hasUpdate ? EXIT_CODES.updateAvailable : EXIT_CODES.success };
866
1029
  }
867
1030
 
1031
+ function combineInstalledState(skillState, profileState) {
1032
+ if (!profileState || profileState === 'current') return skillState;
1033
+ if (skillState !== 'current') return skillState;
1034
+ if (profileState === 'not installed') return 'partial';
1035
+ if (profileState === 'update available') return 'update available';
1036
+ return profileState;
1037
+ }
1038
+
868
1039
  export async function doctorCommand(args, options = {}) {
869
1040
  const context = resolveTargets('doctor', args, options);
870
1041
  const packageInfo = loadPackageInfo(context.roots.packageRoot);
1042
+ const capabilities = await runtimeCapabilities(options);
871
1043
  const checks = [];
872
1044
  const nodeMajor = Number.parseInt(process.versions.node.split('.')[0], 10);
873
1045
  checks.push({ level: nodeMajor >= 20 ? 'PASS' : 'FAIL', message: `Node.js ${process.versions.node}` });
@@ -902,7 +1074,81 @@ export async function doctorCommand(args, options = {}) {
902
1074
  }
903
1075
 
904
1076
  const hasFail = checks.some((check) => check.level === 'FAIL');
905
- return { ok: !hasFail, command: 'doctor', checks, exitCode: hasFail ? EXIT_CODES.integrityFailure : EXIT_CODES.success };
1077
+ return { ok: !hasFail, command: 'doctor', checks, capabilities, exitCode: hasFail ? EXIT_CODES.integrityFailure : EXIT_CODES.success };
1078
+ }
1079
+
1080
+ async function optionalDependencyAvailable(name, options = {}) {
1081
+ if (options.capabilityResolver) return Boolean(await options.capabilityResolver(name));
1082
+ try {
1083
+ await import(name);
1084
+ return true;
1085
+ } catch {
1086
+ return false;
1087
+ }
1088
+ }
1089
+
1090
+ export async function runtimeCapabilities(options = {}) {
1091
+ const env = options.env || process.env;
1092
+ const dependencyDefinitions = [
1093
+ {
1094
+ id: 'browser_render',
1095
+ requirements: ['playwright'],
1096
+ readyLimitations: ['dependency presence does not establish browser launch readiness'],
1097
+ },
1098
+ {
1099
+ id: 'lighthouse_lab',
1100
+ requirements: ['lighthouse', 'chrome-launcher'],
1101
+ readyLimitations: ['dependency presence does not establish Chrome launch readiness or a successful lab run'],
1102
+ },
1103
+ {
1104
+ id: 'ocr',
1105
+ requirements: ['tesseract.js'],
1106
+ readyLimitations: ['dependency presence does not establish OCR accuracy or source-image availability'],
1107
+ },
1108
+ ];
1109
+ const capabilities = [];
1110
+ for (const definition of dependencyDefinitions) {
1111
+ const availability = await Promise.all(definition.requirements.map(async (name) => ({
1112
+ name,
1113
+ available: await optionalDependencyAvailable(name, options),
1114
+ })));
1115
+ const missing = availability.filter((item) => !item.available).map((item) => item.name);
1116
+ capabilities.push({
1117
+ id: definition.id,
1118
+ state: missing.length ? 'missing_dependency' : 'ready',
1119
+ requirements: definition.requirements,
1120
+ limitations: missing.length
1121
+ ? missing.map((name) => `optional dependency ${name} is not installed`)
1122
+ : definition.readyLimitations,
1123
+ });
1124
+ }
1125
+
1126
+ for (const definition of [
1127
+ {
1128
+ id: 'crux_field',
1129
+ credential: 'CRUX_API_KEY',
1130
+ readyLimitation: 'credential presence does not establish validity, scope, quota, or API availability',
1131
+ },
1132
+ {
1133
+ id: 'gsc_index',
1134
+ credential: 'GSC_ACCESS_TOKEN',
1135
+ readyLimitation: 'credential presence does not establish validity, scope, property access, or API availability',
1136
+ },
1137
+ {
1138
+ id: 'ga4_metrics',
1139
+ credential: 'GA4_ACCESS_TOKEN',
1140
+ readyLimitation: 'credential presence does not establish validity, scope, property access, or API availability',
1141
+ },
1142
+ ]) {
1143
+ const configured = typeof env[definition.credential] === 'string' && env[definition.credential].length > 0;
1144
+ capabilities.push({
1145
+ id: definition.id,
1146
+ state: configured ? 'ready' : 'missing_credential',
1147
+ requirements: [definition.credential],
1148
+ limitations: [configured ? definition.readyLimitation : `${definition.credential} is not configured`],
1149
+ });
1150
+ }
1151
+ return capabilities;
906
1152
  }
907
1153
 
908
1154
  function inspectInstallationWithOptionalBundle(target, packageRootPath, version) {
@@ -1103,7 +1349,15 @@ function renderList(result) {
1103
1349
  }
1104
1350
 
1105
1351
  function renderDoctor(result) {
1106
- return ['Citable doctor', '', ...result.checks.map((check) => `${check.level.padEnd(5)} ${check.message}`)].join('\n');
1352
+ const lines = ['Citable doctor', '', ...result.checks.map((check) => `${check.level.padEnd(5)} ${check.message}`)];
1353
+ if (result.capabilities?.length) {
1354
+ lines.push('', 'Runtime capabilities');
1355
+ for (const capability of result.capabilities) {
1356
+ lines.push(` ${capability.id.padEnd(18)} ${capability.state}`);
1357
+ for (const limitation of capability.limitations) lines.push(` ${limitation}`);
1358
+ }
1359
+ }
1360
+ return lines.join('\n');
1107
1361
  }
1108
1362
 
1109
1363
  export function installerHelp() {
@@ -7,8 +7,10 @@ export const PROVIDERS = Object.freeze({
7
7
  aliases: ['claude-code', 'claudecode'],
8
8
  projectDir: '.claude',
9
9
  projectSkillsDir: '.claude/skills',
10
+ projectAgentProfilesDir: '.claude/agents',
10
11
  globalHints: ['.claude'],
11
12
  globalSkillsDir: '.claude/skills',
13
+ globalAgentProfilesDir: '.claude/agents',
12
14
  },
13
15
  codex: {
14
16
  id: 'codex',
@@ -162,3 +164,12 @@ export function providerBundleSkillPath(providerId) {
162
164
  if (!provider) throw new Error(`unknown provider: ${providerId}`);
163
165
  return path.join('dist', 'universal', provider.projectSkillsDir, 'citable');
164
166
  }
167
+
168
+ export function providerAgentProfilesDestination(providerId, scope, roots) {
169
+ const provider = PROVIDERS[providerId];
170
+ if (!provider) throw new Error(`unknown provider: ${providerId}`);
171
+ const dir = scope === 'project' ? provider.projectAgentProfilesDir : provider.globalAgentProfilesDir;
172
+ if (!dir) return null;
173
+ const root = scope === 'project' ? roots.projectRoot : roots.home;
174
+ return path.join(root, dir, 'citable');
175
+ }
@@ -1,11 +1,15 @@
1
1
  import { envelope, observationRun, readInput } from './common.js';
2
- import { fetchUrl } from '../crawler/fetch.js';
2
+ import { fetchUrl, handlePublicBrowserRoute, validatePublicUrl } from '../crawler/fetch.js';
3
3
  import { sha256 } from '../shared/io.js';
4
4
  import { validateAgainst } from '../shared/schemaValidator.js';
5
+ import { analyzeRenderParity } from './renderParity.js';
5
6
 
6
- async function executeStep(page, step) {
7
+ async function executeStep(page, step, lookup) {
7
8
  const locator = step.locator ? page.locator(step.locator).first() : null;
8
- if (step.action === 'navigate') await page.goto(step.url, { waitUntil: 'networkidle' });
9
+ if (step.action === 'navigate') {
10
+ await validatePublicUrl(step.url, lookup ? { lookup } : {});
11
+ await page.goto(step.url, { waitUntil: 'networkidle' });
12
+ }
9
13
  else if (step.action === 'click') await locator.click();
10
14
  else if (step.action === 'fill') {
11
15
  const value = process.env[step.value_env];
@@ -22,7 +26,7 @@ async function executeStep(page, step) {
22
26
  else if (step.action === 'scroll') await locator.scrollIntoViewIfNeeded();
23
27
  }
24
28
 
25
- async function playwrightCapture(profile, plan, playwright) {
29
+ async function playwrightCapture(profile, plan, playwright, lookup) {
26
30
  if (profile.authentication_state !== 'anonymous') throw new Error(`authentication state ${profile.authentication_state} requires a disclosed custom adapter`);
27
31
  const browserType = playwright[profile.browser.engine];
28
32
  if (!browserType) throw new Error(`Playwright engine ${profile.browser.engine} is unavailable`);
@@ -35,6 +39,7 @@ async function playwrightCapture(profile, plan, playwright) {
35
39
  locale: profile.locale,
36
40
  });
37
41
  try {
42
+ await context.route('**/*', (route) => handlePublicBrowserRoute(route, lookup ? { lookup } : {}));
38
43
  const page = await context.newPage();
39
44
  const consoleErrors = [], networkFailures = [], steps = [], stepScreenshots = {};
40
45
  page.on('console', (message) => { if (message.type() === 'error') consoleErrors.push(message.text()); });
@@ -43,7 +48,7 @@ async function playwrightCapture(profile, plan, playwright) {
43
48
  for (const step of profile.steps) {
44
49
  const record = { step_id: step.step_id, action: step.action, status: 'completed', failure: null, screenshot_ref: null };
45
50
  try {
46
- await executeStep(page, step);
51
+ await executeStep(page, step, lookup);
47
52
  if (step.capture_screenshot) {
48
53
  record.screenshot_ref = `journeys/${profile.profile_id}/steps/${step.step_id}.png`;
49
54
  stepScreenshots[record.screenshot_ref] = await page.screenshot({ fullPage: true });
@@ -79,6 +84,7 @@ export async function observeBrowserPlan(root, options) {
79
84
  const check = validateAgainst('browser-evidence-plan.schema.json', plan);
80
85
  if (!check.valid) throw new Error(`browser evidence plan violates contract: ${check.errors.join('; ')}`);
81
86
  if (options.target && options.target !== plan.target) throw new Error('browser evidence target differs from the plan target');
87
+ await validatePublicUrl(plan.target, options.lookup ? { lookup: options.lookup } : {});
82
88
  const profileIds = plan.profiles.map((item) => item.profile_id);
83
89
  if (new Set(profileIds).size !== profileIds.length) throw new Error('browser evidence plan contains duplicate profile ids');
84
90
  const targetOrigin = new URL(plan.target).origin;
@@ -113,7 +119,7 @@ export async function observeBrowserPlan(root, options) {
113
119
  try {
114
120
  const capture = options.captureJourney
115
121
  ? await options.captureJourney(profile, plan)
116
- : await playwrightCapture(profile, plan, playwright);
122
+ : await playwrightCapture(profile, plan, playwright, options.lookup);
117
123
  if (!capture || typeof capture.dom !== 'string' || typeof capture.text !== 'string' || typeof capture.browser_version !== 'string' || capture.screenshot == null) {
118
124
  throw new Error('browser adapter returned an incomplete capture contract');
119
125
  }
@@ -137,6 +143,7 @@ export async function observeBrowserPlan(root, options) {
137
143
  artifacts[name] = value;
138
144
  }
139
145
  const versionMatches = profile.browser.expected_version == null || profile.browser.expected_version === capture.browser_version;
146
+ const renderParity = analyzeRenderParity(initial.body, capture.dom, plan.semantic_impact_policy || null);
140
147
  const data = {
141
148
  plan_id: plan.plan_id, target: plan.target, profile_id: profile.profile_id,
142
149
  browser: { ...profile.browser, observed_version: capture.browser_version, version_matches_expectation: versionMatches },
@@ -144,9 +151,10 @@ export async function observeBrowserPlan(root, options) {
144
151
  consent_state: profile.consent_state, authentication_state: profile.authentication_state,
145
152
  final_url: capture.final_url, status: capture.status, dom_hash: sha256(capture.dom), text_hash: sha256(capture.text),
146
153
  steps: capture.steps || [], artifact_refs: refs,
154
+ render_parity: renderParity,
147
155
  interpretation_boundary: 'Observed differences do not establish semantic, retrieval, citation, ranking, or business impact.',
148
156
  };
149
- const limitations = [...plan.limitations, ...profile.limitations];
157
+ const limitations = [...plan.limitations, ...profile.limitations, ...(plan.semantic_impact_policy?.limitations || [])];
150
158
  if (!versionMatches) limitations.push('Observed browser version differs from the planned version.');
151
159
  observations.push(envelope('browser_journey', data, { method: 'browser', source: `playwright/${profile.browser.engine}`, raw: capture.dom, limitations }));
152
160
  } catch (error) {
@@ -0,0 +1,83 @@
1
+ import { substantiate } from '../commands/substantiate.js';
2
+ import { loadRegistries } from '../registries/index.js';
3
+
4
+ function keyTerms(text) {
5
+ return (String(text || '').toLowerCase().match(/[a-z][a-z-]{4,}/g) || []).slice(0, 8);
6
+ }
7
+
8
+ function escapeRegExp(s) {
9
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
10
+ }
11
+
12
+ function overlapScore(assertionText, claimText) {
13
+ const terms = keyTerms(assertionText);
14
+ if (terms.length < 3) return 0;
15
+ const target = String(claimText || '').toLowerCase();
16
+ // Whole-word boundaries only — a raw substring test would match "test" inside
17
+ // "greatest" and misassign a substantiation status to the wrong claim.
18
+ const present = terms.filter((t) => new RegExp(`\\b${escapeRegExp(t)}\\b`).test(target)).length;
19
+ return present / terms.length;
20
+ }
21
+
22
+ function surfaceMatches(citationUrl, surface) {
23
+ try {
24
+ const url = new URL(citationUrl);
25
+ // Path-boundary comparison, not raw substring — a query string or fragment
26
+ // containing the surface text must not count as a match.
27
+ return url.pathname === surface || url.pathname.startsWith(surface.endsWith('/') ? surface : `${surface}/`);
28
+ } catch {
29
+ return false;
30
+ }
31
+ }
32
+
33
+ /** Find the best-matching registry claim for one AI answer assertion (token-overlap heuristic, adapted from CLAIM-008). */
34
+ export function matchClaim(assertionText, claims, citationUrl) {
35
+ let best = null;
36
+ let bestScore = 0;
37
+ for (const c of claims.filter((c) => c.status !== 'retired')) {
38
+ let score = overlapScore(assertionText, c.claim);
39
+ if (citationUrl && (c.publication_surfaces || []).some((s) => surfaceMatches(citationUrl, s))) score += 0.1;
40
+ if (score > bestScore) {
41
+ bestScore = score;
42
+ best = c;
43
+ }
44
+ }
45
+ return bestScore >= 0.4 ? best : null;
46
+ }
47
+
48
+ const OUTCOME_TO_STATUS = {
49
+ verified: 'confirmed',
50
+ verified_narrowed: 'confirmed',
51
+ contradicted: 'contradicted',
52
+ expired: 'stale',
53
+ };
54
+
55
+ /**
56
+ * Diff one answer-engine assertion against the caller's own claim/evidence registry.
57
+ * Never asserts the AI is wrong — only confirmed/contradicted/stale/unsupported relative
58
+ * to the caller's own evidence. Never implies a live query happened.
59
+ */
60
+ export function diffAssertion(assertionText, citationUrl, { claims, assessmentByClaimId }) {
61
+ if (!assertionText) return null;
62
+ const matched = matchClaim(assertionText, claims, citationUrl);
63
+ if (!matched) {
64
+ return { status: 'unsupported', matched_claim_id: null, reasons: ['no claim in your registry covers this assertion; it could not be checked'] };
65
+ }
66
+ const assessment = assessmentByClaimId.get(matched.claim_id);
67
+ const status = OUTCOME_TO_STATUS[assessment?.outcome] ?? 'unsupported';
68
+ return { status, matched_claim_id: matched.claim_id, reasons: assessment?.reasons ?? [] };
69
+ }
70
+
71
+ /**
72
+ * Precompute the shared claim/evidence lookups once per `observe citations` run.
73
+ * `problems` surfaces registry schema-validation issues (malformed but parseable
74
+ * YAML) so the caller can report them as warnings rather than silently diffing
75
+ * against data that failed its own contract.
76
+ */
77
+ export function buildClaimDiffContext(root, refDate) {
78
+ const { registries, problems } = loadRegistries(root);
79
+ const claims = Array.isArray(registries.claims?.entries) ? registries.claims.entries : [];
80
+ const { assessments } = substantiate(root, { write: false, refDate });
81
+ const assessmentByClaimId = new Map(assessments.map((a) => [a.claim_id, a]));
82
+ return { claims, assessmentByClaimId, problems };
83
+ }
@@ -42,6 +42,8 @@ export function summarizeObservations(items) {
42
42
  for (const item of items) { byKind[item.kind] = (byKind[item.kind] || 0) + 1; byState[item.state] = (byState[item.state] || 0) + 1; }
43
43
  const citations = items.filter((item) => item.kind === 'citation');
44
44
  const reviews = items.filter((item) => item.kind === 'citation_review');
45
+ const claimDiffCounts = { confirmed: 0, contradicted: 0, stale: 0, unsupported: 0, not_checked: 0 };
46
+ for (const review of reviews) claimDiffCounts[review.data.claim_diff?.status ?? 'not_checked']++;
45
47
  const providers = {};
46
48
  for (const item of citations) {
47
49
  const provider = item.data.provider || 'unknown';
@@ -49,6 +51,17 @@ export function summarizeObservations(items) {
49
51
  providers[provider].runs++;
50
52
  if (item.data.property_cited) providers[provider].property_cited++;
51
53
  }
54
+ const competitiveSourceMap = new Map();
55
+ for (const review of reviews.filter((item) => !item.data.first_party)) {
56
+ let domain;
57
+ try { domain = new URL(review.data.canonical_url).hostname; } catch { continue; }
58
+ const current = competitiveSourceMap.get(domain) || { domain, citation_count: 0, declared_profiles: [] };
59
+ current.citation_count++;
60
+ if (review.data.source_profile && !current.declared_profiles.some((item) => JSON.stringify(item) === JSON.stringify(review.data.source_profile))) {
61
+ current.declared_profiles.push(review.data.source_profile);
62
+ }
63
+ competitiveSourceMap.set(domain, current);
64
+ }
52
65
  return {
53
66
  total: items.length, by_kind: byKind, by_state: byState,
54
67
  citation_metrics: citations.length ? {
@@ -56,8 +69,10 @@ export function summarizeObservations(items) {
56
69
  citation_presence_rate: citations.filter((x) => x.data.property_cited).length / citations.length,
57
70
  supported_citation_rate: reviews.length ? reviews.filter((x) => x.data.support_status === 'supported').length / reviews.length : null,
58
71
  review_required: reviews.filter((x) => x.state === 'review_required').length,
72
+ claim_diff: claimDiffCounts,
59
73
  provider_results: providers,
60
74
  competitive_domains: [...new Set(reviews.filter((x) => !x.data.first_party).map((x) => { try { return new URL(x.data.canonical_url).hostname; } catch { return null; } }).filter(Boolean))].sort(),
75
+ competitive_sources: [...competitiveSourceMap.values()].sort((a, b) => a.domain.localeCompare(b.domain)),
61
76
  } : null,
62
77
  };
63
78
  }