@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
@@ -0,0 +1,261 @@
1
+ import { envelope, observationRun, readInput } from './common.js';
2
+ import { loadRegistries } from '../registries/index.js';
3
+ import { sha256 } from '../shared/io.js';
4
+
5
+ const FAVORABLE_MARKERS = [
6
+ /\b(?:highly\s+)?recommended\b/i,
7
+ /\bindustry\s+standard\b/i,
8
+ /\bmarket\s+leader\b/i,
9
+ /\bbest(?:-in-class)?\b/i,
10
+ /\btop(?:\s+choice|\s+tier|\s+rated)?\b/i,
11
+ /\bpreferred\s+(?:solution|option|tool|platform)\b/i,
12
+ /\breliable\b/i,
13
+ /\brobust\b/i,
14
+ /\bsecure\b/i,
15
+ /\bperformant\b/i,
16
+ /\befficient\b/i,
17
+ /\bcomprehensive\b/i,
18
+ /\btrusted\b/i,
19
+ /\bintuitive\b/i,
20
+ /\bexcellent\b/i,
21
+ ];
22
+
23
+ const UNFAVORABLE_MARKERS = [
24
+ /\bcriticized\s+for\b/i,
25
+ /\bexpensive\b/i,
26
+ /\boverpriced\b/i,
27
+ /\bunreliable\b/i,
28
+ /\boutdated\b/i,
29
+ /\blegacy\b/i,
30
+ /\bslow\b/i,
31
+ /\bbuggy\b/i,
32
+ /\bunstable\b/i,
33
+ /\bvulnerabilit(?:y|ies)\b/i,
34
+ /\bsecurity\s+flaw[s]?\b/i,
35
+ /\bsecurity\s+issue[s]?\b/i,
36
+ /\bbreach(?:es)?\b/i,
37
+ /\blacks?\s+(?:critical|essential|basic|key)\b/i,
38
+ /\bmissing\s+(?:critical|essential|key)\b/i,
39
+ /\bpoor\s+(?:performance|support|usability|documentation)\b/i,
40
+ /\bavoid\b/i,
41
+ /\bnot\s+recommended\b/i,
42
+ /\binferior\b/i,
43
+ /\bworst\b/i,
44
+ /\bsteep\s+learning\s+curve\b/i,
45
+ /\bfrustrating\b/i,
46
+ /\bfrequent\s+(?:downtime|crashes|errors|failures)\b/i,
47
+ ];
48
+
49
+ function escapeRegex(text) {
50
+ return String(text).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
51
+ }
52
+
53
+ /**
54
+ * Extract sentences from text.
55
+ */
56
+ function splitSentences(text) {
57
+ if (!text) return [];
58
+ return String(text)
59
+ .replace(/([.?!])\s+/g, '$1|§|')
60
+ .split('|§|')
61
+ .map((s) => s.trim())
62
+ .filter(Boolean);
63
+ }
64
+
65
+ /**
66
+ * Classify tone / stance towards a specific entity within an answer text.
67
+ * Stance categories: 'favorable', 'neutral', 'unfavorable', 'mixed', 'ambiguous'.
68
+ */
69
+ export function classifyAnswerStance(answerText, entity, { reviewer = null, manualStance = null } = {}) {
70
+ const entityId = entity?.entity_id || 'unknown';
71
+ const canonicalName = entity?.canonical_name || entityId;
72
+ const names = [canonicalName, ...(entity?.aliases || [])].filter(Boolean);
73
+
74
+ if (!names.length || !answerText) {
75
+ return {
76
+ entity_id: entityId,
77
+ canonical_name: canonicalName,
78
+ mentioned: false,
79
+ stance: 'not_mentioned',
80
+ confidence: 'confirmed',
81
+ evidence_passages: [],
82
+ favorable_markers: [],
83
+ unfavorable_markers: [],
84
+ tone_summary: 'Entity not mentioned in text',
85
+ reviewer,
86
+ review_required: false,
87
+ };
88
+ }
89
+
90
+ // Check if any alias / canonical name is mentioned
91
+ const namePatterns = names.map((n) => new RegExp(`\\b${escapeRegex(n)}\\b`, 'i'));
92
+ const isMentioned = namePatterns.some((pattern) => pattern.test(answerText));
93
+
94
+ if (!isMentioned) {
95
+ return {
96
+ entity_id: entityId,
97
+ canonical_name: canonicalName,
98
+ mentioned: false,
99
+ stance: 'not_mentioned',
100
+ confidence: 'confirmed',
101
+ evidence_passages: [],
102
+ favorable_markers: [],
103
+ unfavorable_markers: [],
104
+ tone_summary: 'Entity not mentioned in text',
105
+ reviewer,
106
+ review_required: false,
107
+ };
108
+ }
109
+
110
+ // If a manual stance from a human reviewer is already recorded, honor it directly
111
+ if (manualStance && ['favorable', 'neutral', 'unfavorable', 'mixed', 'ambiguous'].includes(manualStance)) {
112
+ return {
113
+ entity_id: entityId,
114
+ canonical_name: canonicalName,
115
+ mentioned: true,
116
+ stance: manualStance,
117
+ confidence: reviewer ? 'confirmed' : 'high',
118
+ evidence_passages: [answerText.slice(0, 300)],
119
+ favorable_markers: [],
120
+ unfavorable_markers: [],
121
+ tone_summary: `Human-adjudicated stance: ${manualStance}`,
122
+ reviewer,
123
+ review_required: !reviewer,
124
+ };
125
+ }
126
+
127
+ // Find sentences mentioning the entity
128
+ const sentences = splitSentences(answerText);
129
+ const matchedIndices = new Set();
130
+ sentences.forEach((sentence, idx) => {
131
+ if (namePatterns.some((p) => p.test(sentence))) {
132
+ matchedIndices.add(idx);
133
+ // include adjacent sentence context if available
134
+ if (idx > 0) matchedIndices.add(idx - 1);
135
+ if (idx < sentences.length - 1) matchedIndices.add(idx + 1);
136
+ }
137
+ });
138
+
139
+ const contextSentences = [...matchedIndices].sort((a, b) => a - b).map((idx) => sentences[idx]);
140
+ const contextText = contextSentences.join(' ');
141
+
142
+ const favorableHits = [];
143
+ for (const rx of FAVORABLE_MARKERS) {
144
+ const m = contextText.match(rx);
145
+ if (m) favorableHits.push(m[0].toLowerCase());
146
+ }
147
+
148
+ const unfavorableHits = [];
149
+ for (const rx of UNFAVORABLE_MARKERS) {
150
+ const m = contextText.match(rx);
151
+ if (m) unfavorableHits.push(m[0].toLowerCase());
152
+ }
153
+
154
+ let stance = 'neutral';
155
+ if (unfavorableHits.length > 0 && favorableHits.length > 0) {
156
+ stance = 'mixed';
157
+ } else if (unfavorableHits.length > 0) {
158
+ stance = 'unfavorable';
159
+ } else if (favorableHits.length > 0) {
160
+ stance = 'favorable';
161
+ }
162
+
163
+ const toneSummary = stance === 'unfavorable'
164
+ ? `Unfavorable tone detected with markers: ${unfavorableHits.join(', ')}`
165
+ : stance === 'favorable'
166
+ ? `Favorable tone detected with markers: ${favorableHits.join(', ')}`
167
+ : stance === 'mixed'
168
+ ? `Mixed tone detected (favorable: ${favorableHits.join(', ')}; unfavorable: ${unfavorableHits.join(', ')})`
169
+ : 'Neutral / descriptive tone without evaluative polarity';
170
+
171
+ return {
172
+ entity_id: entityId,
173
+ canonical_name: canonicalName,
174
+ mentioned: true,
175
+ stance,
176
+ confidence: reviewer ? 'confirmed' : 'medium',
177
+ evidence_passages: contextSentences.slice(0, 3),
178
+ favorable_markers: [...new Set(favorableHits)],
179
+ unfavorable_markers: [...new Set(unfavorableHits)],
180
+ tone_summary: toneSummary,
181
+ reviewer,
182
+ review_required: !reviewer,
183
+ };
184
+ }
185
+
186
+ /**
187
+ * Execute `citable observe stance`.
188
+ */
189
+ export async function observeStance(root, options = {}) {
190
+ const input = readInput(options.input);
191
+ const { registries, problems } = loadRegistries(root);
192
+ if (problems.length) throw new Error(`registry validation failed: ${problems.join('; ')}`);
193
+
194
+ const entities = (registries.entities?.entries || []).filter((e) => e.status !== 'retired');
195
+ if (!entities.length) throw new Error('no active entities found in entity registry');
196
+
197
+ const targetEntities = options.entity
198
+ ? entities.filter((e) => e.entity_id === options.entity || e.canonical_name.toLowerCase() === options.entity.toLowerCase())
199
+ : entities;
200
+
201
+ if (options.entity && !targetEntities.length) {
202
+ throw new Error(`entity not found: ${options.entity}`);
203
+ }
204
+
205
+ // Parse candidate items from input
206
+ const doc = input.value;
207
+ const items = Array.isArray(doc)
208
+ ? doc
209
+ : doc.observations || doc.items || doc.answers || doc.prompts || [doc];
210
+
211
+ const observations = [];
212
+ for (const item of items) {
213
+ const answerText = item.answer_text || item.answer || item.text || item.data?.answer_text || '';
214
+ if (!answerText) continue;
215
+
216
+ const promptId = item.prompt_id || item.data?.prompt_id || 'unknown';
217
+ const promptText = item.prompt_text || item.data?.prompt_text || null;
218
+ const engine = item.provider || item.engine || item.data?.provider || item.data?.engine || 'unknown';
219
+ const reviewer = options.reviewer || item.reviewer || item.evaluator || null;
220
+
221
+ for (const entity of targetEntities) {
222
+ const classification = classifyAnswerStance(answerText, entity, {
223
+ reviewer,
224
+ manualStance: item.stance || item.sentiment,
225
+ });
226
+
227
+ if (!classification.mentioned) continue;
228
+
229
+ const data = {
230
+ prompt_id: promptId,
231
+ prompt_text: promptText,
232
+ engine,
233
+ entity_id: entity.entity_id,
234
+ canonical_name: entity.canonical_name,
235
+ stance: classification.stance,
236
+ evidence_passages: classification.evidence_passages,
237
+ favorable_markers: classification.favorable_markers,
238
+ unfavorable_markers: classification.unfavorable_markers,
239
+ tone_summary: classification.tone_summary,
240
+ reviewer: classification.reviewer,
241
+ review_required: classification.review_required,
242
+ };
243
+
244
+ const raw = JSON.stringify({ item, classification });
245
+ observations.push(envelope('stance', data, {
246
+ method: reviewer ? 'human_review' : 'static_analysis',
247
+ source: input.file,
248
+ state: classification.review_required ? 'review_required' : 'observed',
249
+ confidence: classification.confidence,
250
+ raw,
251
+ limitations: classification.review_required
252
+ ? ['Automated stance classification is an evidence-backed semantic finding; authoritative determination requires a named human reviewer per the narrative-accuracy rubric.']
253
+ : [],
254
+ }));
255
+ }
256
+ }
257
+
258
+ return observationRun(root, 'observe stance', input.file, observations, {
259
+ rawInputs: { stance_input: input.raw },
260
+ });
261
+ }
@@ -0,0 +1,95 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { readYaml, nowIso, sha256 } from '../shared/io.js';
5
+ import { loadRegistries, saveRegistry, REGISTRY_SPECS } from './index.js';
6
+
7
+ const SEEDS_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), 'seeds');
8
+
9
+ export function listSeeds(seedsDir = SEEDS_DIR) {
10
+ if (!fs.existsSync(seedsDir)) return [];
11
+ return fs.readdirSync(seedsDir).filter((name) => fs.existsSync(path.join(seedsDir, name, 'seed.json')));
12
+ }
13
+
14
+ function loadSeedManifest(name, seedsDir) {
15
+ const dir = path.join(seedsDir, name);
16
+ const manifestFile = path.join(dir, 'seed.json');
17
+ if (!fs.existsSync(manifestFile)) {
18
+ throw new Error(`unknown seed: ${name}; available seeds: ${listSeeds(seedsDir).join(', ') || 'none bundled'}`);
19
+ }
20
+ return { dir, manifest: JSON.parse(fs.readFileSync(manifestFile, 'utf8')) };
21
+ }
22
+
23
+ /** Deterministic, key-order-independent serialization for content fingerprinting. */
24
+ function stableStringify(value) {
25
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`;
26
+ if (value && typeof value === 'object') {
27
+ return `{${Object.keys(value).sort().map((k) => `${JSON.stringify(k)}:${stableStringify(value[k])}`).join(',')}}`;
28
+ }
29
+ return JSON.stringify(value);
30
+ }
31
+
32
+ /** Fingerprint an entry's content, excluding provenance (which carries the fingerprint itself). */
33
+ function contentFingerprint(entry) {
34
+ const { provenance, ...rest } = entry;
35
+ return sha256(stableStringify(rest));
36
+ }
37
+
38
+ /**
39
+ * Overlay a bundled starter registry seed onto the caller's `.citable/` registries.
40
+ *
41
+ * Fail-closed guarantees, enforced in code (never trusted from the seed file itself):
42
+ * - every written entry is stamped `status: unverified` unconditionally, regardless of
43
+ * what status the seed bundle declares (candidate, active, verified — all forced);
44
+ * - an existing entry is only ever refreshed (even with `force`) if its current content
45
+ * still matches the fingerprint recorded at seed time — `provenance.seededFrom` being
46
+ * present is not by itself proof the entry is unedited, since a user can edit a field
47
+ * while leaving provenance untouched; a fingerprint mismatch means the owner changed
48
+ * it, and a seed can never clobber that edit.
49
+ */
50
+ export function applySeed(root, name, { force = false, seedsDir = SEEDS_DIR } = {}) {
51
+ const { dir, manifest } = loadSeedManifest(name, seedsDir);
52
+ const { registries } = loadRegistries(root);
53
+ const appliedAt = nowIso();
54
+ const result = { seedName: name, seedVersion: manifest.version, appliedAt, registriesTouched: {}, warnings: [] };
55
+
56
+ for (const kind of manifest.registries) {
57
+ const spec = REGISTRY_SPECS.find((s) => s.kind === kind);
58
+ if (!spec) { result.warnings.push(`seed "${name}" references unknown registry kind "${kind}"; skipped`); continue; }
59
+ const seedFile = path.join(dir, spec.file);
60
+ if (!fs.existsSync(seedFile)) continue;
61
+ const seedRegistry = readYaml(seedFile);
62
+ const existing = registries[kind];
63
+ const existingById = new Map(existing.entries.map((e) => [e[spec.idField], e]));
64
+ let added = 0;
65
+ let skipped = 0;
66
+
67
+ for (const entry of seedRegistry.entries) {
68
+ const id = entry[spec.idField];
69
+ const already = existingById.get(id);
70
+ if (already) {
71
+ const seededFrom = already.provenance?.seededFrom;
72
+ const unedited = seededFrom && seededFrom.contentFingerprint === contentFingerprint(already);
73
+ if (!force || !unedited) { skipped++; continue; }
74
+ }
75
+
76
+ if (entry.status !== 'unverified') {
77
+ result.warnings.push(`${kind}/${id}: seed attempted status "${entry.status}"; forced to "unverified"`);
78
+ entry.status = 'unverified';
79
+ }
80
+ entry.provenance = {
81
+ ...(entry.provenance || {}),
82
+ seededFrom: { seedName: name, seedVersion: manifest.version, importedAt: appliedAt, contentFingerprint: contentFingerprint(entry) },
83
+ };
84
+
85
+ if (already) Object.assign(already, entry);
86
+ else existing.entries.push(entry);
87
+ added++;
88
+ }
89
+
90
+ saveRegistry(root, kind, existing);
91
+ result.registriesTouched[kind] = { added, skipped };
92
+ }
93
+
94
+ return result;
95
+ }
@@ -0,0 +1,18 @@
1
+ version: 1
2
+ kind: claims
3
+ entries:
4
+ - claim_id: CLAIM-SEED-PRICING-TRANSPARENCY
5
+ claim: >
6
+ Placeholder: replace with a specific, evidenced pricing claim for your
7
+ product (e.g. published starting price, billing unit, plan boundaries).
8
+ claim_type: commercial
9
+ entity: ENT-SEED-PRODUCT
10
+ publication_surfaces: ["/pricing/"]
11
+ status: unverified
12
+ - claim_id: CLAIM-SEED-FEATURE-SCOPE
13
+ claim: >
14
+ Placeholder: replace with a specific, evidenced capability claim and its
15
+ scope/exclusions (what the product does and explicitly does not do).
16
+ claim_type: capability
17
+ entity: ENT-SEED-PRODUCT
18
+ status: unverified
@@ -0,0 +1,20 @@
1
+ version: 1
2
+ kind: entities
3
+ entries:
4
+ - entity_id: ENT-SEED-VENDOR
5
+ canonical_name: (your company name)
6
+ entity_type: organization
7
+ category: SaaS vendor
8
+ definition: >
9
+ Replace with your organization's own definition. Seeded as a starting
10
+ shape only — not a fact about your company.
11
+ status: unverified
12
+ - entity_id: ENT-SEED-PRODUCT
13
+ canonical_name: (your product name)
14
+ entity_type: product
15
+ owner_entity: ENT-SEED-VENDOR
16
+ category: SaaS product
17
+ definition: >
18
+ Replace with your product's own definition. Seeded as a starting shape
19
+ only — not a fact about your product.
20
+ status: unverified
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "saas-pricing",
3
+ "version": "1.0.0",
4
+ "description": "Starter claims/entities for a SaaS pricing page vertical.",
5
+ "registries": ["entities", "claims"]
6
+ }
@@ -0,0 +1,254 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { readJson } from '../shared/io.js';
4
+
5
+ const POSTURE_CLASS = {
6
+ pass: 'posture-pass',
7
+ partial: 'posture-partial',
8
+ fail: 'posture-fail',
9
+ not_established: 'posture-unknown',
10
+ observed: 'posture-pass',
11
+ not_evidenced: 'posture-unknown',
12
+ };
13
+
14
+ const NO_GUARANTEE = 'This dashboard renders values already recorded by each audit run. It derives no new findings, combines nothing into an AI visibility score, and guarantees no crawling, indexing, ranking, citation, recommendation, or conversion outcome.';
15
+
16
+ function isAuditSummary(summary) {
17
+ return Boolean(summary) && typeof summary === 'object' && Boolean(summary.by_severity) && Boolean(summary.posture);
18
+ }
19
+
20
+ /**
21
+ * Load every audit run that recorded a summary.json, oldest first. Run IDs are
22
+ * `<ISO-timestamp>-<command>-<rand>`, so a lexicographic sort is chronological.
23
+ * A run whose evidence cannot be read is reported in `skipped`, never dropped
24
+ * silently and never thrown.
25
+ */
26
+ export function loadRunHistory(root, { since, last } = {}) {
27
+ const runsDir = path.join(root, '.citable', 'runs');
28
+ const included = [];
29
+ const skipped = [];
30
+ if (!fs.existsSync(runsDir)) return { included, skipped };
31
+ let runIds = fs.readdirSync(runsDir)
32
+ .filter((runId) => fs.existsSync(path.join(runsDir, runId, 'summary.json')))
33
+ .sort();
34
+ if (since) runIds = runIds.filter((runId) => runId >= since);
35
+ for (const runId of runIds) {
36
+ const dir = path.join(runsDir, runId);
37
+ try {
38
+ const summary = readJson(path.join(dir, 'summary.json'));
39
+ if (!isAuditSummary(summary)) {
40
+ skipped.push({ run_id: runId, reason: 'summary.json records no by_severity or posture; observation runs are not part of the audit trend' });
41
+ continue;
42
+ }
43
+ included.push({ run_id: runId, manifest: readJson(path.join(dir, 'manifest.json')), summary });
44
+ } catch (err) {
45
+ skipped.push({ run_id: runId, reason: err.message });
46
+ }
47
+ }
48
+ // `last` counts audit runs that carry usable evidence, so an unreadable or
49
+ // observation-only run cannot silently consume the requested window. Skipped
50
+ // runs are reported unless `last` moved the window past them; `since` has
51
+ // already bounded them above.
52
+ const windowed = Number.isInteger(last) && last > 0 ? included.slice(-last) : included;
53
+ const truncated = windowed.length < included.length && windowed.length > 0;
54
+ return {
55
+ included: windowed,
56
+ skipped: truncated ? skipped.filter((item) => item.run_id >= windowed[0].run_id) : skipped,
57
+ };
58
+ }
59
+
60
+ /** Project the loaded runs into parallel, run-aligned series. */
61
+ export function buildTimeSeries(included) {
62
+ return {
63
+ run_ids: included.map((run) => run.run_id),
64
+ timestamps: included.map((run) => run.manifest?.timestamp ?? null),
65
+ by_severity_series: included.map((run) => run.summary.by_severity ?? {}),
66
+ retrieval_eligibility_series: included.map((run) => run.summary.posture?.retrieval_eligibility?.result ?? null),
67
+ source_extraction_series: included.map((run) => run.summary.posture?.source_extraction_and_support?.result ?? null),
68
+ citation_presence_rate_series: included.map((run) => run.summary.posture?.observed_citation_behavior?.citation_presence_rate ?? null),
69
+ };
70
+ }
71
+
72
+ function severityCount(bySeverity, severity) {
73
+ return bySeverity?.[severity] ?? 0;
74
+ }
75
+
76
+ function formatRate(rate) {
77
+ return rate == null ? 'not evidenced' : `${(rate * 100).toFixed(1)}%`;
78
+ }
79
+
80
+ function formatPosture(result) {
81
+ return result ?? 'not recorded';
82
+ }
83
+
84
+ function formatTimestamp(timestamp) {
85
+ return timestamp ?? 'not recorded';
86
+ }
87
+
88
+ export function renderDashboardMarkdown(series, skipped = []) {
89
+ const lines = [];
90
+ const runCount = series.run_ids.length;
91
+ lines.push(`# Citable evidence dashboard`);
92
+ lines.push('');
93
+ lines.push(`- Audit runs included: ${runCount}`);
94
+ lines.push(`- Runs skipped: ${skipped.length}`);
95
+ if (runCount) lines.push(`- Window: \`${series.run_ids[0]}\` → \`${series.run_ids[runCount - 1]}\``);
96
+ lines.push('');
97
+ lines.push(`> ${NO_GUARANTEE}`);
98
+ lines.push('');
99
+ if (runCount < 2) {
100
+ lines.push('## Insufficient history');
101
+ lines.push('');
102
+ lines.push(runCount === 0
103
+ ? 'No audit run in `.citable/runs/` recorded a summary. No trend is established. Run `citable audit` to record comparable evidence.'
104
+ : 'Only one audit run is available. A single point establishes no trend, no direction, and no rate of change. Record at least two comparable runs before reading movement into these values.');
105
+ lines.push('');
106
+ }
107
+ if (runCount) {
108
+ lines.push('## Run history');
109
+ lines.push('');
110
+ lines.push('| Run | Timestamp | Critical | High | Medium | Retrieval eligibility | Source extraction | Citation presence rate |');
111
+ lines.push('| --- | --- | --- | --- | --- | --- | --- | --- |');
112
+ for (let i = 0; i < runCount; i++) {
113
+ const sev = series.by_severity_series[i];
114
+ lines.push(`| \`${series.run_ids[i]}\` | ${formatTimestamp(series.timestamps[i])} | ${severityCount(sev, 'critical')} | ${severityCount(sev, 'high')} | ${severityCount(sev, 'medium')} | ${formatPosture(series.retrieval_eligibility_series[i])} | ${formatPosture(series.source_extraction_series[i])} | ${formatRate(series.citation_presence_rate_series[i])} |`);
115
+ }
116
+ lines.push('');
117
+ lines.push('Counts come from each run\'s own recorded summary. A lower count is an observed difference between runs, not proof that a condition was fixed — see `citable compare-snapshots` for run comparability.');
118
+ lines.push('');
119
+ }
120
+ if (skipped.length) {
121
+ lines.push('## Skipped runs');
122
+ lines.push('');
123
+ for (const item of skipped) lines.push(`- \`${item.run_id}\`: ${item.reason}`);
124
+ lines.push('');
125
+ }
126
+ return lines.join('\n');
127
+ }
128
+
129
+ function escapeHtml(value) {
130
+ return String(value)
131
+ .replace(/&/g, '&amp;')
132
+ .replace(/</g, '&lt;')
133
+ .replace(/>/g, '&gt;')
134
+ .replace(/"/g, '&quot;')
135
+ .replace(/'/g, '&#39;');
136
+ }
137
+
138
+ /**
139
+ * Map run-aligned values to `<polyline points>` coordinates. Non-numeric entries
140
+ * (a run with no evidenced value) are omitted rather than interpolated, and
141
+ * fewer than two numeric points yields no line at all.
142
+ */
143
+ function sparklinePath(values, { width = 480, height = 80 } = {}) {
144
+ const pad = 3;
145
+ const points = values.map((value, index) => ({ value, index })).filter((point) => Number.isFinite(point.value));
146
+ if (points.length < 2) return '';
147
+ const min = Math.min(...points.map((point) => point.value));
148
+ const max = Math.max(...points.map((point) => point.value));
149
+ const span = max - min;
150
+ const steps = Math.max(values.length - 1, 1);
151
+ return points.map((point) => {
152
+ const x = pad + (point.index / steps) * (width - pad * 2);
153
+ const y = span === 0 ? height / 2 : (height - pad) - ((point.value - min) / span) * (height - pad * 2);
154
+ return `${x.toFixed(1)},${y.toFixed(1)}`;
155
+ }).join(' ');
156
+ }
157
+
158
+ function renderSparkline(title, values, format) {
159
+ const points = sparklinePath(values);
160
+ const numeric = values.filter((value) => Number.isFinite(value));
161
+ const html = [];
162
+ html.push('<section class="chart">');
163
+ html.push(`<h3>${escapeHtml(title)}</h3>`);
164
+ if (!points) {
165
+ html.push(`<p class="note">Fewer than two runs recorded a numeric value for this series (${numeric.length} of ${values.length}). No trend line is drawn.</p>`);
166
+ } else {
167
+ html.push(`<svg viewBox="0 0 480 80" width="480" height="80" role="img" aria-label="${escapeHtml(title)}">`);
168
+ html.push(`<polyline points="${points}" fill="none" stroke="#2f6f4f" stroke-width="2" />`);
169
+ html.push('</svg>');
170
+ html.push(`<p class="note">Range across ${numeric.length} run(s) with a recorded value: ${escapeHtml(format(Math.min(...numeric)))} to ${escapeHtml(format(Math.max(...numeric)))}. The vertical axis is scaled to this range only; runs without a recorded value are omitted, not interpolated.</p>`);
171
+ }
172
+ html.push('</section>');
173
+ return html.join('\n');
174
+ }
175
+
176
+ function renderPostureRow(label, results, runIds) {
177
+ const cells = results.map((result, index) => `<span class="posture ${POSTURE_CLASS[result] ?? 'posture-unknown'}" title="${escapeHtml(runIds[index])}">${escapeHtml(formatPosture(result))}</span>`).join('');
178
+ return `<section class="chart"><h3>${escapeHtml(label)}</h3><div class="posture-row">${cells}</div><p class="note">Categorical states in run order. These are not plotted as numbers because they have no defined numeric distance.</p></section>`;
179
+ }
180
+
181
+ export function renderDashboardHtml(series, skipped = []) {
182
+ const runCount = series.run_ids.length;
183
+ const criticalHigh = series.by_severity_series.map((sev) => severityCount(sev, 'critical') + severityCount(sev, 'high'));
184
+ const html = [];
185
+ html.push('<!doctype html>');
186
+ html.push('<html lang="en">');
187
+ html.push('<head>');
188
+ html.push('<meta charset="utf-8" />');
189
+ html.push('<meta name="viewport" content="width=device-width, initial-scale=1" />');
190
+ html.push('<title>Citable evidence dashboard</title>');
191
+ html.push('<style>');
192
+ html.push('body { font: 16px/1.5 system-ui, sans-serif; margin: 2rem auto; max-width: 60rem; padding: 0 1rem; color: #1c1c1c; }');
193
+ html.push('table { border-collapse: collapse; width: 100%; font-size: 0.875rem; }');
194
+ html.push('th, td { border: 1px solid #d4d4d4; padding: 0.375rem 0.5rem; text-align: left; }');
195
+ html.push('th { background: #f3f3f3; }');
196
+ html.push('code { font-size: 0.8125rem; }');
197
+ html.push('blockquote { border-left: 4px solid #999; margin: 0 0 1.5rem; padding: 0.25rem 1rem; color: #333; }');
198
+ html.push('.note { color: #555; font-size: 0.8125rem; }');
199
+ html.push('.chart { margin: 1.5rem 0; }');
200
+ html.push('.chart svg { background: #fafafa; border: 1px solid #e4e4e4; }');
201
+ html.push('.posture-row { display: flex; flex-wrap: wrap; gap: 0.25rem; }');
202
+ html.push('.posture { border-radius: 3px; font-size: 0.75rem; padding: 0.125rem 0.375rem; }');
203
+ html.push('.posture-pass { background: #e2f2e6; color: #1d5c33; }');
204
+ html.push('.posture-partial { background: #fbf1d8; color: #7a5510; }');
205
+ html.push('.posture-fail { background: #fbe0e0; color: #8c1f1f; }');
206
+ html.push('.posture-unknown { background: #ececec; color: #4a4a4a; }');
207
+ html.push('.insufficient { background: #fbf1d8; border: 1px solid #e2c98a; padding: 0.75rem 1rem; }');
208
+ html.push('</style>');
209
+ html.push('</head>');
210
+ html.push('<body>');
211
+ html.push('<h1>Citable evidence dashboard</h1>');
212
+ html.push('<ul>');
213
+ html.push(`<li>Audit runs included: ${runCount}</li>`);
214
+ html.push(`<li>Runs skipped: ${skipped.length}</li>`);
215
+ if (runCount) html.push(`<li>Window: <code>${escapeHtml(series.run_ids[0])}</code> to <code>${escapeHtml(series.run_ids[runCount - 1])}</code></li>`);
216
+ html.push('</ul>');
217
+ html.push(`<blockquote>${escapeHtml(NO_GUARANTEE)}</blockquote>`);
218
+ if (runCount < 2) {
219
+ html.push('<section class="insufficient">');
220
+ html.push('<h2>Insufficient history</h2>');
221
+ html.push(`<p>${runCount === 0
222
+ ? 'No audit run in <code>.citable/runs/</code> recorded a summary. No trend is established. Run <code>citable audit</code> to record comparable evidence.'
223
+ : 'Only one audit run is available. A single point establishes no trend, no direction, and no rate of change. Record at least two comparable runs before reading movement into these values.'}</p>`);
224
+ html.push('</section>');
225
+ }
226
+ if (runCount) {
227
+ html.push('<h2>Trends</h2>');
228
+ html.push(renderSparkline('Critical and high findings per run', criticalHigh, (value) => String(value)));
229
+ html.push(renderSparkline('Observed citation presence rate per run', series.citation_presence_rate_series, formatRate));
230
+ html.push(renderPostureRow('Retrieval eligibility per run', series.retrieval_eligibility_series, series.run_ids));
231
+ html.push(renderPostureRow('Source extraction and support per run', series.source_extraction_series, series.run_ids));
232
+ html.push('<h2>Run history</h2>');
233
+ html.push('<table>');
234
+ html.push('<thead><tr><th>Run</th><th>Timestamp</th><th>Critical</th><th>High</th><th>Medium</th><th>Retrieval eligibility</th><th>Source extraction</th><th>Citation presence rate</th></tr></thead>');
235
+ html.push('<tbody>');
236
+ for (let i = 0; i < runCount; i++) {
237
+ const sev = series.by_severity_series[i];
238
+ html.push(`<tr><td><code>${escapeHtml(series.run_ids[i])}</code></td><td>${escapeHtml(formatTimestamp(series.timestamps[i]))}</td><td>${severityCount(sev, 'critical')}</td><td>${severityCount(sev, 'high')}</td><td>${severityCount(sev, 'medium')}</td><td>${escapeHtml(formatPosture(series.retrieval_eligibility_series[i]))}</td><td>${escapeHtml(formatPosture(series.source_extraction_series[i]))}</td><td>${escapeHtml(formatRate(series.citation_presence_rate_series[i]))}</td></tr>`);
239
+ }
240
+ html.push('</tbody>');
241
+ html.push('</table>');
242
+ html.push('<p class="note">Counts come from each run&#39;s own recorded summary. A lower count is an observed difference between runs, not proof that a condition was fixed.</p>');
243
+ }
244
+ if (skipped.length) {
245
+ html.push('<h2>Skipped runs</h2>');
246
+ html.push('<ul>');
247
+ for (const item of skipped) html.push(`<li><code>${escapeHtml(item.run_id)}</code>: ${escapeHtml(item.reason)}</li>`);
248
+ html.push('</ul>');
249
+ }
250
+ html.push('</body>');
251
+ html.push('</html>');
252
+ html.push('');
253
+ return html.join('\n');
254
+ }