@nebulacomponents/citable 1.13.1 → 1.15.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 (359) hide show
  1. package/CHANGELOG.md +312 -0
  2. package/README.md +44 -6
  3. package/dist/universal/.agents/skills/citable/SKILL.md +22 -8
  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/README.md +4 -1
  8. package/dist/universal/.agents/skills/citable/commands/action-findings.md +9 -1
  9. package/dist/universal/.agents/skills/citable/commands/observe-and-act.md +62 -8
  10. package/dist/universal/.agents/skills/citable/commands/plan-audit.md +35 -0
  11. package/dist/universal/.agents/skills/citable/commands/reporting.md +57 -0
  12. package/dist/universal/.agents/skills/citable/manifest.json +29 -16
  13. package/dist/universal/.agents/skills/citable/schemas/action-plan.schema.json +13 -0
  14. package/dist/universal/.agents/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  15. package/dist/universal/.agents/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  16. package/dist/universal/.agents/skills/citable/schemas/common.defs.json +12 -1
  17. package/dist/universal/.agents/skills/citable/schemas/corroboration-import.schema.json +48 -0
  18. package/dist/universal/.agents/skills/citable/schemas/entity-graph.schema.json +75 -0
  19. package/dist/universal/.agents/skills/citable/schemas/entity.schema.json +13 -0
  20. package/dist/universal/.agents/skills/citable/schemas/observation.schema.json +1 -1
  21. package/dist/universal/.agents/skills/citable/schemas/page.schema.json +3 -0
  22. package/dist/universal/.agents/skills/citable/schemas/regional-network-import.schema.json +55 -0
  23. package/dist/universal/.agents/skills/citable/schemas/schedule.schema.json +1 -1
  24. package/dist/universal/.agents/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  25. package/dist/universal/.agents/skills/citable/scripts/README.md +3 -1
  26. package/dist/universal/.claude/agents/citable/citable-auditor.md +64 -0
  27. package/dist/universal/.claude/agents/citable/citable-semantic-reviewer.md +66 -0
  28. package/dist/universal/.claude/agents/citable/manifest.json +12 -0
  29. package/dist/universal/.claude/skills/citable/SKILL.md +22 -8
  30. package/dist/universal/.claude/skills/citable/VERSION +1 -1
  31. package/dist/universal/.claude/skills/citable/agents/citable-auditor.md +64 -0
  32. package/dist/universal/.claude/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  33. package/dist/universal/.claude/skills/citable/commands/README.md +4 -1
  34. package/dist/universal/.claude/skills/citable/commands/action-findings.md +9 -1
  35. package/dist/universal/.claude/skills/citable/commands/observe-and-act.md +62 -8
  36. package/dist/universal/.claude/skills/citable/commands/plan-audit.md +35 -0
  37. package/dist/universal/.claude/skills/citable/commands/reporting.md +57 -0
  38. package/dist/universal/.claude/skills/citable/manifest.json +34 -16
  39. package/dist/universal/.claude/skills/citable/schemas/action-plan.schema.json +13 -0
  40. package/dist/universal/.claude/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  41. package/dist/universal/.claude/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  42. package/dist/universal/.claude/skills/citable/schemas/common.defs.json +12 -1
  43. package/dist/universal/.claude/skills/citable/schemas/corroboration-import.schema.json +48 -0
  44. package/dist/universal/.claude/skills/citable/schemas/entity-graph.schema.json +75 -0
  45. package/dist/universal/.claude/skills/citable/schemas/entity.schema.json +13 -0
  46. package/dist/universal/.claude/skills/citable/schemas/observation.schema.json +1 -1
  47. package/dist/universal/.claude/skills/citable/schemas/page.schema.json +3 -0
  48. package/dist/universal/.claude/skills/citable/schemas/regional-network-import.schema.json +55 -0
  49. package/dist/universal/.claude/skills/citable/schemas/schedule.schema.json +1 -1
  50. package/dist/universal/.claude/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  51. package/dist/universal/.claude/skills/citable/scripts/README.md +3 -1
  52. package/dist/universal/.cursor/skills/citable/SKILL.md +22 -8
  53. package/dist/universal/.cursor/skills/citable/VERSION +1 -1
  54. package/dist/universal/.cursor/skills/citable/agents/citable-auditor.md +64 -0
  55. package/dist/universal/.cursor/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  56. package/dist/universal/.cursor/skills/citable/commands/README.md +4 -1
  57. package/dist/universal/.cursor/skills/citable/commands/action-findings.md +9 -1
  58. package/dist/universal/.cursor/skills/citable/commands/observe-and-act.md +62 -8
  59. package/dist/universal/.cursor/skills/citable/commands/plan-audit.md +35 -0
  60. package/dist/universal/.cursor/skills/citable/commands/reporting.md +57 -0
  61. package/dist/universal/.cursor/skills/citable/manifest.json +29 -16
  62. package/dist/universal/.cursor/skills/citable/schemas/action-plan.schema.json +13 -0
  63. package/dist/universal/.cursor/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  64. package/dist/universal/.cursor/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  65. package/dist/universal/.cursor/skills/citable/schemas/common.defs.json +12 -1
  66. package/dist/universal/.cursor/skills/citable/schemas/corroboration-import.schema.json +48 -0
  67. package/dist/universal/.cursor/skills/citable/schemas/entity-graph.schema.json +75 -0
  68. package/dist/universal/.cursor/skills/citable/schemas/entity.schema.json +13 -0
  69. package/dist/universal/.cursor/skills/citable/schemas/observation.schema.json +1 -1
  70. package/dist/universal/.cursor/skills/citable/schemas/page.schema.json +3 -0
  71. package/dist/universal/.cursor/skills/citable/schemas/regional-network-import.schema.json +55 -0
  72. package/dist/universal/.cursor/skills/citable/schemas/schedule.schema.json +1 -1
  73. package/dist/universal/.cursor/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  74. package/dist/universal/.cursor/skills/citable/scripts/README.md +3 -1
  75. package/dist/universal/.gemini/skills/citable/SKILL.md +22 -8
  76. package/dist/universal/.gemini/skills/citable/VERSION +1 -1
  77. package/dist/universal/.gemini/skills/citable/agents/citable-auditor.md +64 -0
  78. package/dist/universal/.gemini/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  79. package/dist/universal/.gemini/skills/citable/commands/README.md +4 -1
  80. package/dist/universal/.gemini/skills/citable/commands/action-findings.md +9 -1
  81. package/dist/universal/.gemini/skills/citable/commands/observe-and-act.md +62 -8
  82. package/dist/universal/.gemini/skills/citable/commands/plan-audit.md +35 -0
  83. package/dist/universal/.gemini/skills/citable/commands/reporting.md +57 -0
  84. package/dist/universal/.gemini/skills/citable/manifest.json +29 -16
  85. package/dist/universal/.gemini/skills/citable/schemas/action-plan.schema.json +13 -0
  86. package/dist/universal/.gemini/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  87. package/dist/universal/.gemini/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  88. package/dist/universal/.gemini/skills/citable/schemas/common.defs.json +12 -1
  89. package/dist/universal/.gemini/skills/citable/schemas/corroboration-import.schema.json +48 -0
  90. package/dist/universal/.gemini/skills/citable/schemas/entity-graph.schema.json +75 -0
  91. package/dist/universal/.gemini/skills/citable/schemas/entity.schema.json +13 -0
  92. package/dist/universal/.gemini/skills/citable/schemas/observation.schema.json +1 -1
  93. package/dist/universal/.gemini/skills/citable/schemas/page.schema.json +3 -0
  94. package/dist/universal/.gemini/skills/citable/schemas/regional-network-import.schema.json +55 -0
  95. package/dist/universal/.gemini/skills/citable/schemas/schedule.schema.json +1 -1
  96. package/dist/universal/.gemini/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  97. package/dist/universal/.gemini/skills/citable/scripts/README.md +3 -1
  98. package/dist/universal/.github/skills/citable/SKILL.md +22 -8
  99. package/dist/universal/.github/skills/citable/VERSION +1 -1
  100. package/dist/universal/.github/skills/citable/agents/citable-auditor.md +64 -0
  101. package/dist/universal/.github/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  102. package/dist/universal/.github/skills/citable/commands/README.md +4 -1
  103. package/dist/universal/.github/skills/citable/commands/action-findings.md +9 -1
  104. package/dist/universal/.github/skills/citable/commands/observe-and-act.md +62 -8
  105. package/dist/universal/.github/skills/citable/commands/plan-audit.md +35 -0
  106. package/dist/universal/.github/skills/citable/commands/reporting.md +57 -0
  107. package/dist/universal/.github/skills/citable/manifest.json +29 -16
  108. package/dist/universal/.github/skills/citable/schemas/action-plan.schema.json +13 -0
  109. package/dist/universal/.github/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  110. package/dist/universal/.github/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  111. package/dist/universal/.github/skills/citable/schemas/common.defs.json +12 -1
  112. package/dist/universal/.github/skills/citable/schemas/corroboration-import.schema.json +48 -0
  113. package/dist/universal/.github/skills/citable/schemas/entity-graph.schema.json +75 -0
  114. package/dist/universal/.github/skills/citable/schemas/entity.schema.json +13 -0
  115. package/dist/universal/.github/skills/citable/schemas/observation.schema.json +1 -1
  116. package/dist/universal/.github/skills/citable/schemas/page.schema.json +3 -0
  117. package/dist/universal/.github/skills/citable/schemas/regional-network-import.schema.json +55 -0
  118. package/dist/universal/.github/skills/citable/schemas/schedule.schema.json +1 -1
  119. package/dist/universal/.github/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  120. package/dist/universal/.github/skills/citable/scripts/README.md +3 -1
  121. package/dist/universal/.kiro/skills/citable/SKILL.md +22 -8
  122. package/dist/universal/.kiro/skills/citable/VERSION +1 -1
  123. package/dist/universal/.kiro/skills/citable/agents/citable-auditor.md +64 -0
  124. package/dist/universal/.kiro/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  125. package/dist/universal/.kiro/skills/citable/commands/README.md +4 -1
  126. package/dist/universal/.kiro/skills/citable/commands/action-findings.md +9 -1
  127. package/dist/universal/.kiro/skills/citable/commands/observe-and-act.md +62 -8
  128. package/dist/universal/.kiro/skills/citable/commands/plan-audit.md +35 -0
  129. package/dist/universal/.kiro/skills/citable/commands/reporting.md +57 -0
  130. package/dist/universal/.kiro/skills/citable/manifest.json +29 -16
  131. package/dist/universal/.kiro/skills/citable/schemas/action-plan.schema.json +13 -0
  132. package/dist/universal/.kiro/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  133. package/dist/universal/.kiro/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  134. package/dist/universal/.kiro/skills/citable/schemas/common.defs.json +12 -1
  135. package/dist/universal/.kiro/skills/citable/schemas/corroboration-import.schema.json +48 -0
  136. package/dist/universal/.kiro/skills/citable/schemas/entity-graph.schema.json +75 -0
  137. package/dist/universal/.kiro/skills/citable/schemas/entity.schema.json +13 -0
  138. package/dist/universal/.kiro/skills/citable/schemas/observation.schema.json +1 -1
  139. package/dist/universal/.kiro/skills/citable/schemas/page.schema.json +3 -0
  140. package/dist/universal/.kiro/skills/citable/schemas/regional-network-import.schema.json +55 -0
  141. package/dist/universal/.kiro/skills/citable/schemas/schedule.schema.json +1 -1
  142. package/dist/universal/.kiro/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  143. package/dist/universal/.kiro/skills/citable/scripts/README.md +3 -1
  144. package/dist/universal/.opencode/skills/citable/SKILL.md +22 -8
  145. package/dist/universal/.opencode/skills/citable/VERSION +1 -1
  146. package/dist/universal/.opencode/skills/citable/agents/citable-auditor.md +64 -0
  147. package/dist/universal/.opencode/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  148. package/dist/universal/.opencode/skills/citable/commands/README.md +4 -1
  149. package/dist/universal/.opencode/skills/citable/commands/action-findings.md +9 -1
  150. package/dist/universal/.opencode/skills/citable/commands/observe-and-act.md +62 -8
  151. package/dist/universal/.opencode/skills/citable/commands/plan-audit.md +35 -0
  152. package/dist/universal/.opencode/skills/citable/commands/reporting.md +57 -0
  153. package/dist/universal/.opencode/skills/citable/manifest.json +29 -16
  154. package/dist/universal/.opencode/skills/citable/schemas/action-plan.schema.json +13 -0
  155. package/dist/universal/.opencode/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  156. package/dist/universal/.opencode/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  157. package/dist/universal/.opencode/skills/citable/schemas/common.defs.json +12 -1
  158. package/dist/universal/.opencode/skills/citable/schemas/corroboration-import.schema.json +48 -0
  159. package/dist/universal/.opencode/skills/citable/schemas/entity-graph.schema.json +75 -0
  160. package/dist/universal/.opencode/skills/citable/schemas/entity.schema.json +13 -0
  161. package/dist/universal/.opencode/skills/citable/schemas/observation.schema.json +1 -1
  162. package/dist/universal/.opencode/skills/citable/schemas/page.schema.json +3 -0
  163. package/dist/universal/.opencode/skills/citable/schemas/regional-network-import.schema.json +55 -0
  164. package/dist/universal/.opencode/skills/citable/schemas/schedule.schema.json +1 -1
  165. package/dist/universal/.opencode/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  166. package/dist/universal/.opencode/skills/citable/scripts/README.md +3 -1
  167. package/dist/universal/.pi/agent/skills/citable/SKILL.md +22 -8
  168. package/dist/universal/.pi/agent/skills/citable/VERSION +1 -1
  169. package/dist/universal/.pi/agent/skills/citable/agents/citable-auditor.md +64 -0
  170. package/dist/universal/.pi/agent/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  171. package/dist/universal/.pi/agent/skills/citable/commands/README.md +4 -1
  172. package/dist/universal/.pi/agent/skills/citable/commands/action-findings.md +9 -1
  173. package/dist/universal/.pi/agent/skills/citable/commands/observe-and-act.md +62 -8
  174. package/dist/universal/.pi/agent/skills/citable/commands/plan-audit.md +35 -0
  175. package/dist/universal/.pi/agent/skills/citable/commands/reporting.md +57 -0
  176. package/dist/universal/.pi/agent/skills/citable/manifest.json +29 -16
  177. package/dist/universal/.pi/agent/skills/citable/schemas/action-plan.schema.json +13 -0
  178. package/dist/universal/.pi/agent/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  179. package/dist/universal/.pi/agent/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  180. package/dist/universal/.pi/agent/skills/citable/schemas/common.defs.json +12 -1
  181. package/dist/universal/.pi/agent/skills/citable/schemas/corroboration-import.schema.json +48 -0
  182. package/dist/universal/.pi/agent/skills/citable/schemas/entity-graph.schema.json +75 -0
  183. package/dist/universal/.pi/agent/skills/citable/schemas/entity.schema.json +13 -0
  184. package/dist/universal/.pi/agent/skills/citable/schemas/observation.schema.json +1 -1
  185. package/dist/universal/.pi/agent/skills/citable/schemas/page.schema.json +3 -0
  186. package/dist/universal/.pi/agent/skills/citable/schemas/regional-network-import.schema.json +55 -0
  187. package/dist/universal/.pi/agent/skills/citable/schemas/schedule.schema.json +1 -1
  188. package/dist/universal/.pi/agent/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  189. package/dist/universal/.pi/agent/skills/citable/scripts/README.md +3 -1
  190. package/dist/universal/.qoder/skills/citable/SKILL.md +22 -8
  191. package/dist/universal/.qoder/skills/citable/VERSION +1 -1
  192. package/dist/universal/.qoder/skills/citable/agents/citable-auditor.md +64 -0
  193. package/dist/universal/.qoder/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  194. package/dist/universal/.qoder/skills/citable/commands/README.md +4 -1
  195. package/dist/universal/.qoder/skills/citable/commands/action-findings.md +9 -1
  196. package/dist/universal/.qoder/skills/citable/commands/observe-and-act.md +62 -8
  197. package/dist/universal/.qoder/skills/citable/commands/plan-audit.md +35 -0
  198. package/dist/universal/.qoder/skills/citable/commands/reporting.md +57 -0
  199. package/dist/universal/.qoder/skills/citable/manifest.json +29 -16
  200. package/dist/universal/.qoder/skills/citable/schemas/action-plan.schema.json +13 -0
  201. package/dist/universal/.qoder/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  202. package/dist/universal/.qoder/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  203. package/dist/universal/.qoder/skills/citable/schemas/common.defs.json +12 -1
  204. package/dist/universal/.qoder/skills/citable/schemas/corroboration-import.schema.json +48 -0
  205. package/dist/universal/.qoder/skills/citable/schemas/entity-graph.schema.json +75 -0
  206. package/dist/universal/.qoder/skills/citable/schemas/entity.schema.json +13 -0
  207. package/dist/universal/.qoder/skills/citable/schemas/observation.schema.json +1 -1
  208. package/dist/universal/.qoder/skills/citable/schemas/page.schema.json +3 -0
  209. package/dist/universal/.qoder/skills/citable/schemas/regional-network-import.schema.json +55 -0
  210. package/dist/universal/.qoder/skills/citable/schemas/schedule.schema.json +1 -1
  211. package/dist/universal/.qoder/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  212. package/dist/universal/.qoder/skills/citable/scripts/README.md +3 -1
  213. package/dist/universal/.rovodev/skills/citable/SKILL.md +22 -8
  214. package/dist/universal/.rovodev/skills/citable/VERSION +1 -1
  215. package/dist/universal/.rovodev/skills/citable/agents/citable-auditor.md +64 -0
  216. package/dist/universal/.rovodev/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  217. package/dist/universal/.rovodev/skills/citable/commands/README.md +4 -1
  218. package/dist/universal/.rovodev/skills/citable/commands/action-findings.md +9 -1
  219. package/dist/universal/.rovodev/skills/citable/commands/observe-and-act.md +62 -8
  220. package/dist/universal/.rovodev/skills/citable/commands/plan-audit.md +35 -0
  221. package/dist/universal/.rovodev/skills/citable/commands/reporting.md +57 -0
  222. package/dist/universal/.rovodev/skills/citable/manifest.json +29 -16
  223. package/dist/universal/.rovodev/skills/citable/schemas/action-plan.schema.json +13 -0
  224. package/dist/universal/.rovodev/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  225. package/dist/universal/.rovodev/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  226. package/dist/universal/.rovodev/skills/citable/schemas/common.defs.json +12 -1
  227. package/dist/universal/.rovodev/skills/citable/schemas/corroboration-import.schema.json +48 -0
  228. package/dist/universal/.rovodev/skills/citable/schemas/entity-graph.schema.json +75 -0
  229. package/dist/universal/.rovodev/skills/citable/schemas/entity.schema.json +13 -0
  230. package/dist/universal/.rovodev/skills/citable/schemas/observation.schema.json +1 -1
  231. package/dist/universal/.rovodev/skills/citable/schemas/page.schema.json +3 -0
  232. package/dist/universal/.rovodev/skills/citable/schemas/regional-network-import.schema.json +55 -0
  233. package/dist/universal/.rovodev/skills/citable/schemas/schedule.schema.json +1 -1
  234. package/dist/universal/.rovodev/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  235. package/dist/universal/.rovodev/skills/citable/scripts/README.md +3 -1
  236. package/dist/universal/.trae/skills/citable/SKILL.md +22 -8
  237. package/dist/universal/.trae/skills/citable/VERSION +1 -1
  238. package/dist/universal/.trae/skills/citable/agents/citable-auditor.md +64 -0
  239. package/dist/universal/.trae/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  240. package/dist/universal/.trae/skills/citable/commands/README.md +4 -1
  241. package/dist/universal/.trae/skills/citable/commands/action-findings.md +9 -1
  242. package/dist/universal/.trae/skills/citable/commands/observe-and-act.md +62 -8
  243. package/dist/universal/.trae/skills/citable/commands/plan-audit.md +35 -0
  244. package/dist/universal/.trae/skills/citable/commands/reporting.md +57 -0
  245. package/dist/universal/.trae/skills/citable/manifest.json +29 -16
  246. package/dist/universal/.trae/skills/citable/schemas/action-plan.schema.json +13 -0
  247. package/dist/universal/.trae/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  248. package/dist/universal/.trae/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  249. package/dist/universal/.trae/skills/citable/schemas/common.defs.json +12 -1
  250. package/dist/universal/.trae/skills/citable/schemas/corroboration-import.schema.json +48 -0
  251. package/dist/universal/.trae/skills/citable/schemas/entity-graph.schema.json +75 -0
  252. package/dist/universal/.trae/skills/citable/schemas/entity.schema.json +13 -0
  253. package/dist/universal/.trae/skills/citable/schemas/observation.schema.json +1 -1
  254. package/dist/universal/.trae/skills/citable/schemas/page.schema.json +3 -0
  255. package/dist/universal/.trae/skills/citable/schemas/regional-network-import.schema.json +55 -0
  256. package/dist/universal/.trae/skills/citable/schemas/schedule.schema.json +1 -1
  257. package/dist/universal/.trae/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  258. package/dist/universal/.trae/skills/citable/scripts/README.md +3 -1
  259. package/dist/universal/.trae-cn/skills/citable/SKILL.md +22 -8
  260. package/dist/universal/.trae-cn/skills/citable/VERSION +1 -1
  261. package/dist/universal/.trae-cn/skills/citable/agents/citable-auditor.md +64 -0
  262. package/dist/universal/.trae-cn/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  263. package/dist/universal/.trae-cn/skills/citable/commands/README.md +4 -1
  264. package/dist/universal/.trae-cn/skills/citable/commands/action-findings.md +9 -1
  265. package/dist/universal/.trae-cn/skills/citable/commands/observe-and-act.md +62 -8
  266. package/dist/universal/.trae-cn/skills/citable/commands/plan-audit.md +35 -0
  267. package/dist/universal/.trae-cn/skills/citable/commands/reporting.md +57 -0
  268. package/dist/universal/.trae-cn/skills/citable/manifest.json +29 -16
  269. package/dist/universal/.trae-cn/skills/citable/schemas/action-plan.schema.json +13 -0
  270. package/dist/universal/.trae-cn/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  271. package/dist/universal/.trae-cn/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  272. package/dist/universal/.trae-cn/skills/citable/schemas/common.defs.json +12 -1
  273. package/dist/universal/.trae-cn/skills/citable/schemas/corroboration-import.schema.json +48 -0
  274. package/dist/universal/.trae-cn/skills/citable/schemas/entity-graph.schema.json +75 -0
  275. package/dist/universal/.trae-cn/skills/citable/schemas/entity.schema.json +13 -0
  276. package/dist/universal/.trae-cn/skills/citable/schemas/observation.schema.json +1 -1
  277. package/dist/universal/.trae-cn/skills/citable/schemas/page.schema.json +3 -0
  278. package/dist/universal/.trae-cn/skills/citable/schemas/regional-network-import.schema.json +55 -0
  279. package/dist/universal/.trae-cn/skills/citable/schemas/schedule.schema.json +1 -1
  280. package/dist/universal/.trae-cn/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  281. package/dist/universal/.trae-cn/skills/citable/scripts/README.md +3 -1
  282. package/dist/universal/manifest.json +306 -195
  283. package/package.json +1 -1
  284. package/schemas/action-plan.schema.json +13 -0
  285. package/schemas/browser-evidence-plan.schema.json +14 -0
  286. package/schemas/canonical-index-import.schema.json +30 -0
  287. package/schemas/common.defs.json +12 -1
  288. package/schemas/corroboration-import.schema.json +48 -0
  289. package/schemas/entity-graph.schema.json +75 -0
  290. package/schemas/entity.schema.json +13 -0
  291. package/schemas/observation.schema.json +1 -1
  292. package/schemas/page.schema.json +3 -0
  293. package/schemas/regional-network-import.schema.json +55 -0
  294. package/schemas/schedule.schema.json +1 -1
  295. package/schemas/source-identity-chain.schema.json +83 -0
  296. package/scripts/build-dist.js +36 -3
  297. package/skill/SKILL.md +22 -8
  298. package/skill/agents/citable-auditor.md +64 -0
  299. package/skill/agents/citable-semantic-reviewer.md +66 -0
  300. package/skill/commands/README.md +4 -1
  301. package/skill/commands/action-findings.md +9 -1
  302. package/skill/commands/observe-and-act.md +62 -8
  303. package/skill/commands/plan-audit.md +35 -0
  304. package/skill/commands/reporting.md +57 -0
  305. package/src/cli/index.js +115 -15
  306. package/src/commands/actionPlan.js +12 -1
  307. package/src/commands/audit.js +4 -0
  308. package/src/commands/connect.js +29 -0
  309. package/src/commands/context.js +15 -0
  310. package/src/commands/delivery.js +86 -2
  311. package/src/commands/demo.js +36 -0
  312. package/src/commands/fixtures/demo/.citable/claims.yaml +27 -0
  313. package/src/commands/fixtures/demo/.citable/competitors.yaml +3 -0
  314. package/src/commands/fixtures/demo/.citable/config.yaml +18 -0
  315. package/src/commands/fixtures/demo/.citable/crawlers.yaml +30 -0
  316. package/src/commands/fixtures/demo/.citable/entities.yaml +28 -0
  317. package/src/commands/fixtures/demo/.citable/evidence.yaml +19 -0
  318. package/src/commands/fixtures/demo/.citable/experiments.yaml +3 -0
  319. package/src/commands/fixtures/demo/.citable/pages.yaml +54 -0
  320. package/src/commands/fixtures/demo/.citable/prompts.yaml +11 -0
  321. package/src/commands/fixtures/demo/.citable/queries.yaml +10 -0
  322. package/src/commands/fixtures/demo/site/index.html +12 -0
  323. package/src/commands/fixtures/demo/site/learn/execution-governance/index.html +14 -0
  324. package/src/commands/fixtures/demo/site/pricing/index.html +10 -0
  325. package/src/commands/fixtures/demo/site/products/gatekeeper/index.html +15 -0
  326. package/src/commands/fixtures/demo/site/robots.txt +7 -0
  327. package/src/commands/fixtures/demo/site/sitemap.xml +7 -0
  328. package/src/commands/init.js +5 -2
  329. package/src/commands/monitor.js +35 -0
  330. package/src/commands/observe.js +315 -15
  331. package/src/commands/planAudit.js +151 -0
  332. package/src/commands/reportDashboard.js +21 -0
  333. package/src/commands/reportShareOfVoice.js +60 -0
  334. package/src/commands/schemaCmd.js +5 -1
  335. package/src/commands/substantiate.js +22 -10
  336. package/src/connectors/http.js +6 -3
  337. package/src/connectors/index.js +3 -1
  338. package/src/connectors/webflow.js +224 -0
  339. package/src/connectors/wordpress.js +226 -0
  340. package/src/crawler/fetch.js +34 -6
  341. package/src/detectors/geoReco.js +101 -0
  342. package/src/extractor/page.js +17 -0
  343. package/src/installer/index.js +282 -28
  344. package/src/installer/providers.js +11 -0
  345. package/src/monitoring/alertDelivery.js +98 -0
  346. package/src/observations/browserJourney.js +15 -7
  347. package/src/observations/claimDiff.js +83 -0
  348. package/src/observations/common.js +25 -0
  349. package/src/observations/entityGraph.js +89 -0
  350. package/src/observations/renderParity.js +49 -0
  351. package/src/observations/sourceIdentity.js +62 -0
  352. package/src/observations/stance.js +261 -0
  353. package/src/registries/seed.js +95 -0
  354. package/src/registries/seeds/saas-pricing/claims.yaml +18 -0
  355. package/src/registries/seeds/saas-pricing/entities.yaml +20 -0
  356. package/src/registries/seeds/saas-pricing/seed.json +6 -0
  357. package/src/reporting/dashboard.js +254 -0
  358. package/src/reporting/shareOfVoice.js +362 -0
  359. package/src/shared/checklist.js +19 -0
@@ -4,9 +4,11 @@ import net from 'node:net';
4
4
  const blockedAddresses = new net.BlockList();
5
5
  for (const [network, prefix] of [
6
6
  ['0.0.0.0', 8], ['10.0.0.0', 8], ['100.64.0.0', 10], ['127.0.0.0', 8],
7
- ['169.254.0.0', 16], ['172.16.0.0', 12], ['192.168.0.0', 16], ['224.0.0.0', 4],
7
+ ['169.254.0.0', 16], ['172.16.0.0', 12], ['192.0.0.0', 24], ['192.0.2.0', 24],
8
+ ['192.168.0.0', 16], ['198.18.0.0', 15], ['198.51.100.0', 24],
9
+ ['203.0.113.0', 24], ['224.0.0.0', 4], ['240.0.0.0', 4],
8
10
  ]) blockedAddresses.addSubnet(network, prefix, 'ipv4');
9
- for (const [network, prefix] of [['::', 128], ['::1', 128], ['fc00::', 7], ['fe80::', 10], ['ff00::', 8]]) {
11
+ for (const [network, prefix] of [['::', 128], ['::1', 128], ['2001:db8::', 32], ['fc00::', 7], ['fe80::', 10], ['ff00::', 8]]) {
10
12
  blockedAddresses.addSubnet(network, prefix, 'ipv6');
11
13
  }
12
14
 
@@ -19,7 +21,7 @@ function isBlockedAddress(address) {
19
21
  return blockedAddresses.check(address, family === 4 ? 'ipv4' : 'ipv6');
20
22
  }
21
23
 
22
- async function validateDestination(url, lookup) {
24
+ export async function validatePublicUrl(url, { lookup = dns.promises.lookup } = {}) {
23
25
  const parsed = new URL(url);
24
26
  if (!['http:', 'https:'].includes(parsed.protocol)) throw new Error(`unsupported URL protocol: ${parsed.protocol}`);
25
27
  if (parsed.username || parsed.password) throw new Error('URL credentials are not permitted');
@@ -34,6 +36,32 @@ async function validateDestination(url, lookup) {
34
36
  return parsed;
35
37
  }
36
38
 
39
+ /** Enforce the public-network policy for each Playwright request. */
40
+ export async function handlePublicBrowserRoute(route, { lookup = dns.promises.lookup } = {}) {
41
+ const url = route.request().url();
42
+ let protocol;
43
+ try {
44
+ protocol = new URL(url).protocol;
45
+ } catch {
46
+ await route.abort('blockedbyclient');
47
+ return;
48
+ }
49
+ if (['data:', 'blob:', 'about:'].includes(protocol)) {
50
+ await route.continue();
51
+ return;
52
+ }
53
+ if (!['http:', 'https:'].includes(protocol)) {
54
+ await route.abort('blockedbyclient');
55
+ return;
56
+ }
57
+ try {
58
+ await validatePublicUrl(url, { lookup });
59
+ await route.continue();
60
+ } catch {
61
+ await route.abort('blockedbyclient');
62
+ }
63
+ }
64
+
37
65
  async function readBodyLimited(res, maxBodyBytes) {
38
66
  const declared = Number(res.headers.get('content-length'));
39
67
  if (Number.isFinite(declared) && declared > maxBodyBytes) {
@@ -66,13 +94,13 @@ export async function fetchUrl(url, {
66
94
  maxRetries = 3, retryDelayMs = 1000, maxBodyBytes = 5 * 1024 * 1024,
67
95
  fetchImpl = globalThis.fetch, lookup = dns.promises.lookup,
68
96
  } = {}) {
69
- const requested = await validateDestination(url, lookup);
97
+ const requested = await validatePublicUrl(url, { lookup });
70
98
  const allowedOrigin = requested.origin;
71
99
  const chain = [];
72
100
  let current = requested.href;
73
101
 
74
102
  for (let redirects = 0; redirects <= maxRedirects; redirects += 1) {
75
- await validateDestination(current, lookup);
103
+ await validatePublicUrl(current, { lookup });
76
104
  let res;
77
105
  let lastError;
78
106
  for (let attempt = 0; attempt < Math.max(1, maxRetries); attempt += 1) {
@@ -108,7 +136,7 @@ export async function fetchUrl(url, {
108
136
  if (redirects === maxRedirects) break;
109
137
  const next = new URL(headers.location, current);
110
138
  if (next.origin !== allowedOrigin) throw new Error(`redirect leaves audited origin: ${next.href}`);
111
- await validateDestination(next.href, lookup);
139
+ await validatePublicUrl(next.href, { lookup });
112
140
  chain.push({ url: current, status: res.status, location: headers.location });
113
141
  current = next.href;
114
142
  await res.body?.cancel();
@@ -1,4 +1,5 @@
1
1
  import { defineDetector, indexTargets, registryPageFor, sitePageFor, pageSubject, entrySubject } from './framework.js';
2
+ import { classifyAnswerStance } from '../observations/stance.js';
2
3
 
3
4
  const D = [];
4
5
 
@@ -223,6 +224,106 @@ D.push(defineDetector({
223
224
  },
224
225
  }));
225
226
 
227
+ D.push(defineDetector({
228
+ id: 'GEO-007', name: 'Unfavorable or distorted entity stance in recorded generative answers', namespace: 'GEO',
229
+ description: 'Recorded generative engine answers express an unfavorable, negative, or critically distorted stance toward a registered entity.',
230
+ discipline: ['geo', 'aeo'], severity: 'high', deterministic: false, requires: ['registries'],
231
+ finding_type: 'evidence_backed_semantic_finding',
232
+ impact: { representation: 'high', reputational: 'high', conversion: 'medium' },
233
+ applicable_requirement: 'GEO §6: accurate synthesis and entity representation; Rubric: narrative accuracy (§4 & §5)',
234
+ false_positive_conditions: ['Legitimate negative comparison where a product does not support a feature by design'],
235
+ remediation: 'Review the recorded answers against the narrative-accuracy rubric, identify whether the negative characterization reflects true product limitations or factual inaccuracies/outdated information, and publish authoritative corroborating evidence or file model corrections.',
236
+ unsafe_shortcuts: ['publishing fabricated positive reviews', 'manipulating third-party review sites', 'crawler prompt injection (GEO-001)'],
237
+ verification: 'Follow-up generative engine observations demonstrate neutral or favorable stance with no narrative-accuracy violations.',
238
+ review_required: true,
239
+ check(ctx) {
240
+ const entities = (ctx.registries?.entities?.entries || []).filter((e) => e.status !== 'retired');
241
+ if (!entities.length) return [];
242
+ const hits = [];
243
+
244
+ // 1. Check promptResults
245
+ for (const pr of ctx.promptResults || []) {
246
+ for (const e of entities) {
247
+ const names = [e.canonical_name, ...(e.aliases || [])].map((n) => n.toLowerCase());
248
+ const text = `${pr.prompt_text || ''} ${pr.answer_text || ''}`.toLowerCase();
249
+ const mentioned = names.some((n) => text.includes(n));
250
+ if (!mentioned) continue;
251
+
252
+ const classification = classifyAnswerStance(pr.answer_text || '', e, { reviewer: pr.evaluator });
253
+ const isNegativeSentiment = pr.sentiment === 'negative' || pr.recommendation_status === 'not_recommended';
254
+ if (classification.stance === 'unfavorable' || isNegativeSentiment) {
255
+ hits.push({
256
+ subject: entrySubject('entities', e.entity_id),
257
+ summary: `Generative engine ${pr.engine || 'unknown'} expresses unfavorable stance toward entity "${e.canonical_name}"`,
258
+ evidence: [
259
+ `engine: ${pr.engine || 'unknown'}`,
260
+ `prompt: ${pr.prompt_text || pr.prompt_id || 'unknown'}`,
261
+ `stance: unfavorable`,
262
+ `markers: ${classification.unfavorable_markers?.join(', ') || 'recorded negative sentiment'}`,
263
+ `excerpt: "${(classification.evidence_passages?.[0] || pr.answer_text || '').slice(0, 160)}"`,
264
+ `reviewer: ${pr.evaluator || 'unreviewed (semantic review required)'}`,
265
+ ],
266
+ confidence: pr.evaluator ? 'confirmed' : 'medium',
267
+ finding_type: 'evidence_backed_semantic_finding',
268
+ });
269
+ }
270
+ }
271
+ }
272
+
273
+ // 2. Check observations (stance or citation)
274
+ for (const obs of ctx.observations || []) {
275
+ if (obs.kind === 'stance' && obs.data?.stance === 'unfavorable') {
276
+ const e = entities.find((ent) => ent.entity_id === obs.data.entity_id);
277
+ if (e) {
278
+ hits.push({
279
+ subject: entrySubject('entities', e.entity_id),
280
+ summary: `Generative engine ${obs.data.engine || 'unknown'} expresses unfavorable stance toward entity "${e.canonical_name}"`,
281
+ evidence: [
282
+ `engine: ${obs.data.engine || 'unknown'}`,
283
+ `prompt: ${obs.data.prompt_text || obs.data.prompt_id || 'unknown'}`,
284
+ `stance: unfavorable`,
285
+ `markers: ${(obs.data.unfavorable_markers || []).join(', ') || 'unfavorable markers'}`,
286
+ `excerpt: "${(obs.data.evidence_passages?.[0] || '').slice(0, 160)}"`,
287
+ `reviewer: ${obs.data.reviewer || 'unreviewed (semantic review required)'}`,
288
+ ],
289
+ confidence: obs.data.reviewer ? 'confirmed' : 'medium',
290
+ finding_type: 'evidence_backed_semantic_finding',
291
+ });
292
+ }
293
+ } else if (obs.kind === 'citation' && obs.data?.answer_text) {
294
+ for (const e of entities) {
295
+ const classification = classifyAnswerStance(obs.data.answer_text, e);
296
+ if (classification.mentioned && classification.stance === 'unfavorable') {
297
+ hits.push({
298
+ subject: entrySubject('entities', e.entity_id),
299
+ summary: `Generative engine ${obs.data.provider || 'unknown'} expresses unfavorable stance toward entity "${e.canonical_name}"`,
300
+ evidence: [
301
+ `engine: ${obs.data.provider || 'unknown'}`,
302
+ `prompt: ${obs.data.prompt_text || obs.data.prompt_id || 'unknown'}`,
303
+ `stance: unfavorable`,
304
+ `markers: ${(classification.unfavorable_markers || []).join(', ')}`,
305
+ `excerpt: "${(classification.evidence_passages?.[0] || obs.data.answer_text).slice(0, 160)}"`,
306
+ `reviewer: unreviewed (semantic review required)`,
307
+ ],
308
+ confidence: 'medium',
309
+ finding_type: 'evidence_backed_semantic_finding',
310
+ });
311
+ }
312
+ }
313
+ }
314
+ }
315
+
316
+ // Deduplicate by entity and summary
317
+ const seen = new Set();
318
+ return hits.filter((h) => {
319
+ const k = `${h.subject.identifier}|${h.summary}`;
320
+ if (seen.has(k)) return false;
321
+ seen.add(k);
322
+ return true;
323
+ });
324
+ },
325
+ }));
326
+
226
327
  /* ---------------- RECO: recommendation eligibility ---------------- */
227
328
 
228
329
  const RECO_CHECKS = [
@@ -57,6 +57,21 @@ export function extractPage({ url, html, status = 200, headers = {}, sourceFile
57
57
  }
58
58
 
59
59
  const body = root.querySelector('body');
60
+ const rawVisibleText = body ? body.text.replace(/\s+/g, ' ').trim() : '';
61
+ const structuralRegions = [];
62
+ const seenRegions = new Set();
63
+ for (const region of root.querySelectorAll('header,nav,aside,footer,[role=banner],[role=navigation],[role=complementary],[role=contentinfo]')) {
64
+ if (seenRegions.has(region)) continue;
65
+ seenRegions.add(region);
66
+ const text = region.text.replace(/\s+/g, ' ').trim();
67
+ if (!text) continue;
68
+ structuralRegions.push({
69
+ region_type: (region.getAttribute('role') || region.tagName || 'unknown').toLowerCase(),
70
+ text,
71
+ html_bytes: Buffer.byteLength(region.outerHTML),
72
+ token_count: text.split(/\s+/).filter(Boolean).length,
73
+ });
74
+ }
60
75
  // Strip script/style/nav/footer noise for main-text estimation
61
76
  const bodyClone = body ? parse(body.outerHTML) : null;
62
77
  if (bodyClone) for (const s of bodyClone.querySelectorAll('script,style,noscript,nav,footer,header[role=banner]')) s.remove();
@@ -115,6 +130,8 @@ export function extractPage({ url, html, status = 200, headers = {}, sourceFile
115
130
  images,
116
131
  jsonLd,
117
132
  text,
133
+ rawVisibleWordCount: rawVisibleText ? rawVisibleText.split(/\s+/).length : 0,
134
+ structuralRegions,
118
135
  paragraphs,
119
136
  wordCount: text ? text.split(/\s+/).length : 0,
120
137
  scriptBytes,
@@ -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() {