@cleocode/core 2026.6.6 → 2026.6.8

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 (168) hide show
  1. package/dist/db/index.d.ts +5 -1
  2. package/dist/db/index.d.ts.map +1 -1
  3. package/dist/db/index.js +5 -1
  4. package/dist/db/index.js.map +1 -1
  5. package/dist/dispatch/contracts/output-contracts.d.ts +36 -0
  6. package/dist/dispatch/contracts/output-contracts.d.ts.map +1 -0
  7. package/dist/dispatch/contracts/output-contracts.js +38 -0
  8. package/dist/dispatch/contracts/output-contracts.js.map +1 -0
  9. package/dist/dispatch/describe-operation.d.ts +98 -0
  10. package/dist/dispatch/describe-operation.d.ts.map +1 -0
  11. package/dist/dispatch/describe-operation.js +101 -0
  12. package/dist/dispatch/describe-operation.js.map +1 -0
  13. package/dist/docs/build-provenance-graph.d.ts +12 -0
  14. package/dist/docs/build-provenance-graph.d.ts.map +1 -1
  15. package/dist/docs/build-provenance-graph.js +52 -0
  16. package/dist/docs/build-provenance-graph.js.map +1 -1
  17. package/dist/docs/docs-read-model.d.ts +40 -0
  18. package/dist/docs/docs-read-model.d.ts.map +1 -1
  19. package/dist/docs/docs-read-model.js +29 -0
  20. package/dist/docs/docs-read-model.js.map +1 -1
  21. package/dist/docs/export-document.js +1794 -1235
  22. package/dist/docs/export-document.js.map +3 -3
  23. package/dist/docs/index.d.ts +4 -0
  24. package/dist/docs/index.d.ts.map +1 -1
  25. package/dist/docs/index.js +2 -0
  26. package/dist/docs/index.js.map +1 -1
  27. package/dist/docs/read-doc.d.ts +60 -0
  28. package/dist/docs/read-doc.d.ts.map +1 -0
  29. package/dist/docs/read-doc.js +188 -0
  30. package/dist/docs/read-doc.js.map +1 -0
  31. package/dist/docs/wikilinks.d.ts +119 -0
  32. package/dist/docs/wikilinks.d.ts.map +1 -0
  33. package/dist/docs/wikilinks.js +217 -0
  34. package/dist/docs/wikilinks.js.map +1 -0
  35. package/dist/index.d.ts +2 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +5 -0
  38. package/dist/index.js.map +1 -1
  39. package/dist/internal.d.ts +2 -0
  40. package/dist/internal.d.ts.map +1 -1
  41. package/dist/internal.js +6 -0
  42. package/dist/internal.js.map +1 -1
  43. package/dist/llm/index.d.ts +1 -3
  44. package/dist/llm/index.d.ts.map +1 -1
  45. package/dist/llm/index.js +1 -2
  46. package/dist/llm/index.js.map +1 -1
  47. package/dist/llm/model-metadata.d.ts +14 -0
  48. package/dist/llm/model-metadata.d.ts.map +1 -1
  49. package/dist/llm/model-metadata.js +23 -0
  50. package/dist/llm/model-metadata.js.map +1 -1
  51. package/dist/llm/model-runner.d.ts.map +1 -1
  52. package/dist/llm/model-runner.js +104 -74
  53. package/dist/llm/model-runner.js.map +1 -1
  54. package/dist/llm/plugin-facade.js +1947 -1364
  55. package/dist/llm/plugin-facade.js.map +3 -3
  56. package/dist/llm/provider-registry/builtin/anthropic.d.ts.map +1 -1
  57. package/dist/llm/provider-registry/builtin/anthropic.js +4 -0
  58. package/dist/llm/provider-registry/builtin/anthropic.js.map +1 -1
  59. package/dist/llm/provider-registry/builtin/gemini.d.ts.map +1 -1
  60. package/dist/llm/provider-registry/builtin/gemini.js +4 -0
  61. package/dist/llm/provider-registry/builtin/gemini.js.map +1 -1
  62. package/dist/llm/provider-registry/builtin/ollama.d.ts.map +1 -1
  63. package/dist/llm/provider-registry/builtin/ollama.js +4 -0
  64. package/dist/llm/provider-registry/builtin/ollama.js.map +1 -1
  65. package/dist/llm/provider-registry/builtin/openai.d.ts.map +1 -1
  66. package/dist/llm/provider-registry/builtin/openai.js +6 -0
  67. package/dist/llm/provider-registry/builtin/openai.js.map +1 -1
  68. package/dist/llm/transports/index.d.ts +5 -3
  69. package/dist/llm/transports/index.d.ts.map +1 -1
  70. package/dist/llm/transports/index.js +5 -2
  71. package/dist/llm/transports/index.js.map +1 -1
  72. package/dist/reconciliation/reconciliation-engine.d.ts.map +1 -1
  73. package/dist/reconciliation/reconciliation-engine.js +3 -0
  74. package/dist/reconciliation/reconciliation-engine.js.map +1 -1
  75. package/dist/release/plan.d.ts +27 -0
  76. package/dist/release/plan.d.ts.map +1 -1
  77. package/dist/release/plan.js +36 -2
  78. package/dist/release/plan.js.map +1 -1
  79. package/dist/release/provenance-fk.d.ts +74 -0
  80. package/dist/release/provenance-fk.d.ts.map +1 -0
  81. package/dist/release/provenance-fk.js +122 -0
  82. package/dist/release/provenance-fk.js.map +1 -0
  83. package/dist/release/reconcile.d.ts +2 -53
  84. package/dist/release/reconcile.d.ts.map +1 -1
  85. package/dist/release/reconcile.js +13 -93
  86. package/dist/release/reconcile.js.map +1 -1
  87. package/dist/sticky/convert.d.ts.map +1 -1
  88. package/dist/sticky/convert.js +3 -0
  89. package/dist/sticky/convert.js.map +1 -1
  90. package/dist/store/dual-scope-db.d.ts +83 -0
  91. package/dist/store/dual-scope-db.d.ts.map +1 -1
  92. package/dist/store/dual-scope-db.js +135 -6
  93. package/dist/store/dual-scope-db.js.map +1 -1
  94. package/dist/store/exodus/abort-events.d.ts +116 -0
  95. package/dist/store/exodus/abort-events.d.ts.map +1 -0
  96. package/dist/store/exodus/abort-events.js +130 -0
  97. package/dist/store/exodus/abort-events.js.map +1 -0
  98. package/dist/store/exodus/column-transforms.d.ts +35 -8
  99. package/dist/store/exodus/column-transforms.d.ts.map +1 -1
  100. package/dist/store/exodus/column-transforms.js +47 -13
  101. package/dist/store/exodus/column-transforms.js.map +1 -1
  102. package/dist/store/exodus/count-parity.d.ts +71 -0
  103. package/dist/store/exodus/count-parity.d.ts.map +1 -0
  104. package/dist/store/exodus/count-parity.js +124 -0
  105. package/dist/store/exodus/count-parity.js.map +1 -0
  106. package/dist/store/exodus/health.d.ts +70 -0
  107. package/dist/store/exodus/health.d.ts.map +1 -0
  108. package/dist/store/exodus/health.js +130 -0
  109. package/dist/store/exodus/health.js.map +1 -0
  110. package/dist/store/exodus/index.d.ts +4 -0
  111. package/dist/store/exodus/index.d.ts.map +1 -1
  112. package/dist/store/exodus/index.js +4 -0
  113. package/dist/store/exodus/index.js.map +1 -1
  114. package/dist/store/exodus/migrate.d.ts.map +1 -1
  115. package/dist/store/exodus/migrate.js +98 -31
  116. package/dist/store/exodus/migrate.js.map +1 -1
  117. package/dist/store/exodus/plan.d.ts +48 -4
  118. package/dist/store/exodus/plan.d.ts.map +1 -1
  119. package/dist/store/exodus/plan.js +67 -9
  120. package/dist/store/exodus/plan.js.map +1 -1
  121. package/dist/store/exodus/seal.d.ts +69 -0
  122. package/dist/store/exodus/seal.d.ts.map +1 -0
  123. package/dist/store/exodus/seal.js +73 -0
  124. package/dist/store/exodus/seal.js.map +1 -0
  125. package/dist/store/exodus/types.d.ts +24 -1
  126. package/dist/store/exodus/types.d.ts.map +1 -1
  127. package/dist/store/exodus/types.js.map +1 -1
  128. package/dist/store/exodus/verify-migration.d.ts.map +1 -1
  129. package/dist/store/exodus/verify-migration.js +53 -26
  130. package/dist/store/exodus/verify-migration.js.map +1 -1
  131. package/dist/store/repair-malformed-dbs.d.ts +87 -0
  132. package/dist/store/repair-malformed-dbs.d.ts.map +1 -0
  133. package/dist/store/repair-malformed-dbs.js +188 -0
  134. package/dist/store/repair-malformed-dbs.js.map +1 -0
  135. package/dist/store/schema/attachments.d.ts +133 -0
  136. package/dist/store/schema/attachments.d.ts.map +1 -1
  137. package/dist/store/schema/attachments.js +63 -0
  138. package/dist/store/schema/attachments.js.map +1 -1
  139. package/dist/tasks/add.d.ts +13 -0
  140. package/dist/tasks/add.d.ts.map +1 -1
  141. package/dist/tasks/add.js +50 -18
  142. package/dist/tasks/add.js.map +1 -1
  143. package/dist/tasks/archive.d.ts.map +1 -1
  144. package/dist/tasks/archive.js +12 -7
  145. package/dist/tasks/archive.js.map +1 -1
  146. package/dist/tasks/child-disposition.d.ts +66 -0
  147. package/dist/tasks/child-disposition.d.ts.map +1 -0
  148. package/dist/tasks/child-disposition.js +80 -0
  149. package/dist/tasks/child-disposition.js.map +1 -0
  150. package/dist/tasks/delete-preview.js +1 -1
  151. package/dist/tasks/delete-preview.js.map +1 -1
  152. package/dist/tasks/deletion-strategy.d.ts +21 -3
  153. package/dist/tasks/deletion-strategy.d.ts.map +1 -1
  154. package/dist/tasks/deletion-strategy.js +61 -15
  155. package/dist/tasks/deletion-strategy.js.map +1 -1
  156. package/dist/tasks/engine-wrap.d.ts +8 -0
  157. package/dist/tasks/engine-wrap.d.ts.map +1 -1
  158. package/dist/tasks/engine-wrap.js +22 -9
  159. package/dist/tasks/engine-wrap.js.map +1 -1
  160. package/dist/tasks/update.d.ts.map +1 -1
  161. package/dist/tasks/update.js +12 -0
  162. package/dist/tasks/update.js.map +1 -1
  163. package/migrations/drizzle-tasks/20260605000001_t11826-docs-wikilinks/migration.sql +110 -0
  164. package/package.json +12 -12
  165. package/dist/llm/transports/openai.d.ts +0 -181
  166. package/dist/llm/transports/openai.d.ts.map +0 -1
  167. package/dist/llm/transports/openai.js +0 -645
  168. package/dist/llm/transports/openai.js.map +0 -1
@@ -52,6 +52,10 @@ export type { ListProjectDocsOpts, ResolvedDoc } from './docs-read-model.js';
52
52
  export { createDocsReadModel, DocsReadModel } from './docs-read-model.js';
53
53
  export type { ProvisionResult, PublishPrError, PublishPrOptions, PublishPrResult, PublishPrRunners, PublishPrSuccess, } from './publish-pr.js';
54
54
  export { branchForSlug, buildPublishFrontmatter, defaultPublishPrBody, defaultRun, execMsg, KNOWN_DOC_TYPES, knownDocTypesForProject, parseGhPrUrl, pickRunner, provisionPublishPrWorktree, publishDirForType, publishDocsAsPr, publishPrError, stripExistingFrontmatter, teardownPublishPrWorktree, tempWorktreeDirForSlug, validatePublishSlug, } from './publish-pr.js';
55
+ export type { ReadDocOptions } from './read-doc.js';
56
+ export { DocNotFoundError, readDoc } from './read-doc.js';
55
57
  export type { VersionAuditResult } from './version-ssot.js';
56
58
  export { auditVersionFields, compareCleoVersions, getCanonicalCleoVersion, resolveVersion, VERSION_SSOT_MIGRATION_SQL, versionInRange, } from './version-ssot.js';
59
+ export type { RebuildDocsWikilinksOptions, RebuildDocsWikilinksResult, WikilinkEdge, } from './wikilinks.js';
60
+ export { deriveWikilinkEdges, getDocsWikilinks, rebuildDocsWikilinks } from './wikilinks.js';
57
61
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/docs/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/docs/index.ts"],"names":[],"mappings":"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"}
@@ -38,5 +38,7 @@ export { countAuditEntriesForSlug, DOCS_AUDIT_FILE, readAuditLog, verifyAuditTra
38
38
  export { checkBlobFilesystem, checkDocsConsistency, } from './docs-inconsistency-detector.js';
39
39
  export { createDocsReadModel, DocsReadModel } from './docs-read-model.js';
40
40
  export { branchForSlug, buildPublishFrontmatter, defaultPublishPrBody, defaultRun, execMsg, KNOWN_DOC_TYPES, knownDocTypesForProject, parseGhPrUrl, pickRunner, provisionPublishPrWorktree, publishDirForType, publishDocsAsPr, publishPrError, stripExistingFrontmatter, teardownPublishPrWorktree, tempWorktreeDirForSlug, validatePublishSlug, } from './publish-pr.js';
41
+ export { DocNotFoundError, readDoc } from './read-doc.js';
41
42
  export { auditVersionFields, compareCleoVersions, getCanonicalCleoVersion, resolveVersion, VERSION_SSOT_MIGRATION_SQL, versionInRange, } from './version-ssot.js';
43
+ export { deriveWikilinkEdges, getDocsWikilinks, rebuildDocsWikilinks } from './wikilinks.js';
42
44
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/docs/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AASH,OAAO,EACL,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,uBAAuB,EACvB,8BAA8B,EAC9B,0BAA0B,GAC3B,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAsB1D,OAAO,EACL,cAAc,EACd,0BAA0B,EAC1B,8BAA8B,EAC9B,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,WAAW,EACX,QAAQ,EACR,iBAAiB,EACjB,oBAAoB,EACpB,UAAU,EACV,UAAU,EACV,WAAW,GACZ,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAQtD,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,4BAA4B,EAC5B,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAO/B,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,YAAY,GACb,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,+EAA+E;AAE/E,8EAA8E;AAC9E,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EACjC,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,uCAAuC,CAAC;AAQ/C,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,GACd,MAAM,iCAAiC,CAAC;AAMzC,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,yBAAyB,EACzB,kBAAkB,EAClB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,YAAY,EACZ,cAAc,EACd,uBAAuB,EACvB,iBAAiB,EACjB,OAAO,EACP,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAE1B,+EAA+E;AAE/E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAMpD,OAAO,EACL,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,yBAAyB,CAAC;AASjC,gFAAgF;AAChF,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAQzB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAS1E,OAAO,EACL,aAAa,EACb,uBAAuB,EACvB,oBAAoB,EACpB,UAAU,EACV,OAAO,EACP,eAAe,EACf,uBAAuB,EACvB,YAAY,EACZ,UAAU,EACV,0BAA0B,EAC1B,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,cAAc,EACd,0BAA0B,EAC1B,cAAc,GACf,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/docs/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AASH,OAAO,EACL,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,uBAAuB,EACvB,8BAA8B,EAC9B,0BAA0B,GAC3B,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAsB1D,OAAO,EACL,cAAc,EACd,0BAA0B,EAC1B,8BAA8B,EAC9B,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,WAAW,EACX,QAAQ,EACR,iBAAiB,EACjB,oBAAoB,EACpB,UAAU,EACV,UAAU,EACV,WAAW,GACZ,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAQtD,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,4BAA4B,EAC5B,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAO/B,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,YAAY,GACb,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,+EAA+E;AAE/E,8EAA8E;AAC9E,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EACjC,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,uCAAuC,CAAC;AAQ/C,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,GACd,MAAM,iCAAiC,CAAC;AAMzC,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,yBAAyB,EACzB,kBAAkB,EAClB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,YAAY,EACZ,cAAc,EACd,uBAAuB,EACvB,iBAAiB,EACjB,OAAO,EACP,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAE1B,+EAA+E;AAE/E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAMpD,OAAO,EACL,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,yBAAyB,CAAC;AASjC,gFAAgF;AAChF,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAQzB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAS1E,OAAO,EACL,aAAa,EACb,uBAAuB,EACvB,oBAAoB,EACpB,UAAU,EACV,OAAO,EACP,eAAe,EACf,uBAAuB,EACvB,YAAY,EACZ,UAAU,EACV,0BAA0B,EAC1B,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAG1D,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,cAAc,EACd,0BAA0B,EAC1B,cAAc,GACf,MAAM,mBAAmB,CAAC;AAO3B,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * `docs.read` core-SDK implementation — render a single CLEO doc (body + full
3
+ * provenance frontmatter) directly from `cleo.db`, the SOLE doc authority
4
+ * (saga T11778).
5
+ *
6
+ * This is the live read API the Obsidian plugin (T11827) and `cleo docs view`
7
+ * call. It is NOT a static export: every call reads the current DB state. The
8
+ * body is surfaced as UTF-8 text when decodable, else base64 (T11825 AC2), so
9
+ * binary blobs (images/PDFs) render without a second round-trip.
10
+ *
11
+ * Frontmatter is read straight from the `attachments` provenance columns
12
+ * (`slug`, `doc_version`, `owner_version`, `supersedes`, `superseded_by`,
13
+ * `topics`, `related_tasks`) — the same columns `docs_wikilinks` derives from —
14
+ * so the response is self-describing for an external consumer.
15
+ *
16
+ * @task T11825 (Epic T11781 / Saga T11778)
17
+ * @adr ADR-078 — Docs SSoT as provenance graph
18
+ * @see DocReadResponse — packages/contracts/src/docs/read.ts
19
+ * @see createDocsReadModel — body fetch + base64 plumbing reuse
20
+ */
21
+ import type { DocReadResponse } from '@cleocode/contracts';
22
+ /**
23
+ * Options for {@link readDoc}.
24
+ *
25
+ * @task T11825
26
+ */
27
+ export interface ReadDocOptions {
28
+ /** Project root for DB resolution. Defaults to {@link getProjectRoot}(). */
29
+ readonly projectRoot?: string;
30
+ }
31
+ /**
32
+ * Error raised when {@link readDoc}'s slug cannot be resolved to a doc.
33
+ *
34
+ * @task T11825
35
+ */
36
+ export declare class DocNotFoundError extends Error {
37
+ constructor(slug: string);
38
+ }
39
+ /**
40
+ * Read a single doc by slug, returning its body + full provenance frontmatter.
41
+ *
42
+ * Resolves the supersedes / superseded-by FK targets to their slugs (the
43
+ * external-consumer-friendly addressing) and decodes the body to UTF-8 when
44
+ * possible, falling back to base64 for binary content.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * const doc = await readDoc('adr-078-docs-provenance');
49
+ * console.log(doc.frontmatter.docVersion, doc.frontmatter.topics);
50
+ * if (doc.body.encoding === 'utf-8') console.log(doc.body.text);
51
+ * ```
52
+ *
53
+ * @param slug - The exact doc slug (case-sensitive).
54
+ * @param opts - Optional project-root override.
55
+ * @returns The typed {@link DocReadResponse}.
56
+ * @throws {DocNotFoundError} when no attachment carries the slug.
57
+ * @task T11825
58
+ */
59
+ export declare function readDoc(slug: string, opts?: ReadDocOptions): Promise<DocReadResponse>;
60
+ //# sourceMappingURL=read-doc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-doc.d.ts","sourceRoot":"","sources":["../../src/docs/read-doc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAA2B,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAOpF;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,IAAI,EAAE,MAAM;CAIzB;AAmBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,eAAe,CAAC,CAwD/F"}
@@ -0,0 +1,188 @@
1
+ /**
2
+ * `docs.read` core-SDK implementation — render a single CLEO doc (body + full
3
+ * provenance frontmatter) directly from `cleo.db`, the SOLE doc authority
4
+ * (saga T11778).
5
+ *
6
+ * This is the live read API the Obsidian plugin (T11827) and `cleo docs view`
7
+ * call. It is NOT a static export: every call reads the current DB state. The
8
+ * body is surfaced as UTF-8 text when decodable, else base64 (T11825 AC2), so
9
+ * binary blobs (images/PDFs) render without a second round-trip.
10
+ *
11
+ * Frontmatter is read straight from the `attachments` provenance columns
12
+ * (`slug`, `doc_version`, `owner_version`, `supersedes`, `superseded_by`,
13
+ * `topics`, `related_tasks`) — the same columns `docs_wikilinks` derives from —
14
+ * so the response is self-describing for an external consumer.
15
+ *
16
+ * @task T11825 (Epic T11781 / Saga T11778)
17
+ * @adr ADR-078 — Docs SSoT as provenance graph
18
+ * @see DocReadResponse — packages/contracts/src/docs/read.ts
19
+ * @see createDocsReadModel — body fetch + base64 plumbing reuse
20
+ */
21
+ import { Buffer } from 'node:buffer';
22
+ import { eq } from 'drizzle-orm';
23
+ import { getProjectRoot } from '../paths.js';
24
+ import { createAttachmentStore } from '../store/attachment-store.js';
25
+ import { getDb } from '../store/sqlite.js';
26
+ import { attachments } from '../store/tasks-schema.js';
27
+ /**
28
+ * Error raised when {@link readDoc}'s slug cannot be resolved to a doc.
29
+ *
30
+ * @task T11825
31
+ */
32
+ export class DocNotFoundError extends Error {
33
+ constructor(slug) {
34
+ super(`Doc not found: no attachment carries slug '${slug}'`);
35
+ this.name = 'DocNotFoundError';
36
+ }
37
+ }
38
+ /**
39
+ * Read a single doc by slug, returning its body + full provenance frontmatter.
40
+ *
41
+ * Resolves the supersedes / superseded-by FK targets to their slugs (the
42
+ * external-consumer-friendly addressing) and decodes the body to UTF-8 when
43
+ * possible, falling back to base64 for binary content.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * const doc = await readDoc('adr-078-docs-provenance');
48
+ * console.log(doc.frontmatter.docVersion, doc.frontmatter.topics);
49
+ * if (doc.body.encoding === 'utf-8') console.log(doc.body.text);
50
+ * ```
51
+ *
52
+ * @param slug - The exact doc slug (case-sensitive).
53
+ * @param opts - Optional project-root override.
54
+ * @returns The typed {@link DocReadResponse}.
55
+ * @throws {DocNotFoundError} when no attachment carries the slug.
56
+ * @task T11825
57
+ */
58
+ export async function readDoc(slug, opts = {}) {
59
+ const projectRoot = opts.projectRoot ?? getProjectRoot();
60
+ const db = await getDb(projectRoot);
61
+ const row = await db
62
+ .select({
63
+ id: attachments.id,
64
+ sha256: attachments.sha256,
65
+ slug: attachments.slug,
66
+ type: attachments.type,
67
+ summary: attachments.summary,
68
+ lifecycleStatus: attachments.lifecycleStatus,
69
+ supersedes: attachments.supersedes,
70
+ supersededBy: attachments.supersededBy,
71
+ topics: attachments.topics,
72
+ relatedTasks: attachments.relatedTasks,
73
+ ownerVersion: attachments.ownerVersion,
74
+ docVersion: attachments.docVersion,
75
+ createdAt: attachments.createdAt,
76
+ })
77
+ .from(attachments)
78
+ .where(eq(attachments.slug, slug))
79
+ .get();
80
+ if (!row)
81
+ throw new DocNotFoundError(slug);
82
+ const frontmatterRow = row;
83
+ // Resolve supersession FK ids → slugs for external addressing.
84
+ const [supersedesSlug, supersededBySlug] = await Promise.all([
85
+ resolveSlugById(db, frontmatterRow.supersedes),
86
+ resolveSlugById(db, frontmatterRow.supersededBy),
87
+ ]);
88
+ // Fetch the RAW body bytes (not a decoded string) so binary blobs survive the
89
+ // base64 path with full fidelity — `TextDecoder` would lossily replace
90
+ // invalid sequences with U+FFFD.
91
+ const { bytes, mimeType } = await fetchRawBody(frontmatterRow.sha256, projectRoot);
92
+ const frontmatter = {
93
+ slug: frontmatterRow.slug ?? slug,
94
+ kind: frontmatterRow.type,
95
+ title: frontmatterRow.slug ?? slug,
96
+ summary: frontmatterRow.summary,
97
+ lifecycleStatus: frontmatterRow.lifecycleStatus,
98
+ docVersion: frontmatterRow.docVersion,
99
+ ownerVersion: frontmatterRow.ownerVersion,
100
+ supersedes: supersedesSlug,
101
+ supersededBy: supersededBySlug,
102
+ topics: parseStringArray(frontmatterRow.topics),
103
+ relatedTasks: parseStringArray(frontmatterRow.relatedTasks),
104
+ sha256: frontmatterRow.sha256,
105
+ createdAt: frontmatterRow.createdAt,
106
+ };
107
+ const body = encodeBody(bytes, mimeType);
108
+ return { frontmatter, body };
109
+ }
110
+ /**
111
+ * Read the raw body bytes for a doc by content hash via the content-addressed
112
+ * attachment store. Returns an empty buffer when the metadata row exists but
113
+ * the blob was purged from disk.
114
+ *
115
+ * @internal
116
+ */
117
+ async function fetchRawBody(sha256, projectRoot) {
118
+ const store = createAttachmentStore();
119
+ try {
120
+ const result = await store.get(sha256, projectRoot);
121
+ if (result) {
122
+ const mime = result.metadata.attachment.mime;
123
+ return { bytes: result.bytes, mimeType: typeof mime === 'string' ? mime : null };
124
+ }
125
+ }
126
+ catch {
127
+ // Attachment store unavailable or blob purged — fall through to empty body.
128
+ }
129
+ return { bytes: Buffer.alloc(0), mimeType: null };
130
+ }
131
+ /**
132
+ * Resolve an attachment id to its slug, or null when the id is absent /
133
+ * unresolved / slug-less.
134
+ *
135
+ * @internal
136
+ */
137
+ async function resolveSlugById(db, id) {
138
+ if (!id)
139
+ return null;
140
+ const row = await db
141
+ .select({ slug: attachments.slug })
142
+ .from(attachments)
143
+ .where(eq(attachments.id, id))
144
+ .get();
145
+ return row?.slug ?? null;
146
+ }
147
+ /**
148
+ * Encode a doc body from its raw bytes. UTF-8-decodable content (the common
149
+ * markdown case) is surfaced as `text`; binary content (images/PDFs/files) is
150
+ * surfaced as `base64` so an external consumer can render it (T11825 AC2).
151
+ *
152
+ * UTF-8 validity is detected by a lossless decode→re-encode round-trip: invalid
153
+ * sequences decode to U+FFFD and fail to re-encode to the original bytes.
154
+ *
155
+ * @internal
156
+ */
157
+ function encodeBody(bytes, mimeType) {
158
+ const text = bytes.toString('utf-8');
159
+ const isUtf8 = Buffer.from(text, 'utf-8').equals(bytes);
160
+ if (isUtf8) {
161
+ return { encoding: 'utf-8', text, sizeBytes: bytes.length, mimeType };
162
+ }
163
+ return {
164
+ encoding: 'base64',
165
+ base64: bytes.toString('base64'),
166
+ sizeBytes: bytes.length,
167
+ mimeType,
168
+ };
169
+ }
170
+ /**
171
+ * Parse a JSON-array-of-strings column, tolerating null / malformed values.
172
+ *
173
+ * @internal
174
+ */
175
+ function parseStringArray(raw) {
176
+ if (!raw)
177
+ return [];
178
+ try {
179
+ const parsed = JSON.parse(raw);
180
+ if (!Array.isArray(parsed))
181
+ return [];
182
+ return parsed.filter((v) => typeof v === 'string' && v.length > 0);
183
+ }
184
+ catch {
185
+ return [];
186
+ }
187
+ }
188
+ //# sourceMappingURL=read-doc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-doc.js","sourceRoot":"","sources":["../../src/docs/read-doc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * `docs_wikilinks` — derive + query the slug-addressed docs edge table.
3
+ *
4
+ * Per the ratified Docs-SSoT model (saga T11778) `cleo.db` is the SOLE doc
5
+ * authority and `docs_wikilinks` is a **DERIVED, non-authoritative** edge table
6
+ * reconstructed from three provenance columns already on `attachments`:
7
+ *
8
+ * - `supersedes` / `superseded_by` → doc→doc supersession edges
9
+ * - `related_tasks` → doc→T#### task edges (JSON array)
10
+ * - `topics` → doc↔doc shared-topic edges (JSON array)
11
+ *
12
+ * No markdown body `[[link]]` parsing is performed (T11826 AC4) — the edges
13
+ * derive purely from structured columns. The derivation is **idempotent**: it
14
+ * truncates and rebuilds the whole table, so callers may re-run it after any
15
+ * `docs add` / `supersede` / `docs update` write to keep the graph fresh.
16
+ *
17
+ * This module is the runtime twin of the SQL backfill in
18
+ * `migrations/drizzle-tasks/20260605000001_t11826-docs-wikilinks/migration.sql`
19
+ * — the migration seeds the table on schema upgrade, this function rebuilds it
20
+ * on demand.
21
+ *
22
+ * @task T11826 (Epic T11781 / Saga T11778)
23
+ * @adr ADR-078 — Docs SSoT as provenance graph
24
+ * @see build-provenance-graph.ts — the on-the-fly BFS this table makes O(edges)
25
+ */
26
+ import { type DocsWikilinkRelation } from '../store/schema/attachments.js';
27
+ /**
28
+ * A single slug-addressed wikilink edge.
29
+ *
30
+ * @task T11826
31
+ */
32
+ export interface WikilinkEdge {
33
+ /** Source doc slug (always a doc). */
34
+ readonly fromSlug: string;
35
+ /** Target slug — a doc slug, or a `T####` task id when {@link toIsTask}. */
36
+ readonly toSlug: string;
37
+ /** Which provenance column produced this edge. */
38
+ readonly relation: DocsWikilinkRelation;
39
+ /** True when {@link toSlug} is a task id (`related-task` edges). */
40
+ readonly toIsTask: boolean;
41
+ }
42
+ /**
43
+ * Options shared by {@link rebuildDocsWikilinks}.
44
+ *
45
+ * @task T11826
46
+ */
47
+ export interface RebuildDocsWikilinksOptions {
48
+ /** Project root for DB resolution. Defaults to {@link getProjectRoot}(). */
49
+ readonly projectRoot?: string;
50
+ }
51
+ /**
52
+ * Outcome of a {@link rebuildDocsWikilinks} call.
53
+ *
54
+ * @task T11826
55
+ */
56
+ export interface RebuildDocsWikilinksResult {
57
+ /** Number of edges in the table after the rebuild. */
58
+ readonly edgeCount: number;
59
+ /** Per-relation edge counts. */
60
+ readonly byRelation: Readonly<Record<DocsWikilinkRelation, number>>;
61
+ }
62
+ /** Narrow row shape read from `attachments` during derivation. */
63
+ interface DerivationRow {
64
+ readonly slug: string;
65
+ readonly supersedesSlug: string | null;
66
+ readonly supersededBySlug: string | null;
67
+ readonly relatedTasks: string | null;
68
+ readonly topics: string | null;
69
+ }
70
+ /**
71
+ * Idempotently rebuild the `docs_wikilinks` edge table from the provenance
72
+ * columns on `attachments`.
73
+ *
74
+ * The whole table is truncated and re-derived inside a single transaction, so
75
+ * the function is safe to call after any doc write and always converges to the
76
+ * same edge set for a given `attachments` state.
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * const { edgeCount, byRelation } = await rebuildDocsWikilinks();
81
+ * console.log(`derived ${edgeCount} edges (${byRelation['topic']} topic links)`);
82
+ * ```
83
+ *
84
+ * @param opts - Optional project-root override.
85
+ * @returns Edge totals after the rebuild.
86
+ * @task T11826
87
+ */
88
+ export declare function rebuildDocsWikilinks(opts?: RebuildDocsWikilinksOptions): Promise<RebuildDocsWikilinksResult>;
89
+ /**
90
+ * Pure derivation: turn slugged `attachments` rows into the full edge set.
91
+ *
92
+ * Exposed for unit testing the derivation rules without a database. The output
93
+ * is deduplicated on the (`fromSlug`, `toSlug`, `relation`) composite key.
94
+ *
95
+ * @param rows - Slugged attachment rows with their provenance columns.
96
+ * @returns The derived wikilink edges.
97
+ * @task T11826
98
+ */
99
+ export declare function deriveWikilinkEdges(rows: readonly DerivationRow[]): WikilinkEdge[];
100
+ /**
101
+ * Read all wikilink edges incident to a doc slug — **bidirectional** by default.
102
+ *
103
+ * Returns both outbound edges (`from_slug = slug`) and inbound backlinks
104
+ * (`to_slug = slug`). This is the query the Obsidian plugin (T11827) renders to
105
+ * show a doc's neighborhood, and what `cleo docs graph` hydrates for persisted
106
+ * backlinks.
107
+ *
108
+ * @param slug - The doc slug to fetch edges for.
109
+ * @param opts.direction - `'both'` (default), `'out'`, or `'in'`.
110
+ * @param opts.projectRoot - Project root override.
111
+ * @returns The incident edges.
112
+ * @task T11826
113
+ */
114
+ export declare function getDocsWikilinks(slug: string, opts?: {
115
+ direction?: 'both' | 'out' | 'in';
116
+ projectRoot?: string;
117
+ }): Promise<WikilinkEdge[]>;
118
+ export {};
119
+ //# sourceMappingURL=wikilinks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wikilinks.d.ts","sourceRoot":"","sources":["../../src/docs/wikilinks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,EAAE,KAAK,oBAAoB,EAAiB,MAAM,gCAAgC,CAAC;AAI1F;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,sCAAsC;IACtC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED;;;;GAIG;AACH,MAAM,WAAW,2BAA2B;IAC1C,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;GAIG;AACH,MAAM,WAAW,0BAA0B;IACzC,sDAAsD;IACtD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,gCAAgC;IAChC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC,CAAC;CACrE;AAED,kEAAkE;AAClE,UAAU,aAAa;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,GAAE,2BAAgC,GACrC,OAAO,CAAC,0BAA0B,CAAC,CAoErC;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,aAAa,EAAE,GAAG,YAAY,EAAE,CAoDlF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GACrE,OAAO,CAAC,YAAY,EAAE,CAAC,CAmBzB"}
@@ -0,0 +1,217 @@
1
+ /**
2
+ * `docs_wikilinks` — derive + query the slug-addressed docs edge table.
3
+ *
4
+ * Per the ratified Docs-SSoT model (saga T11778) `cleo.db` is the SOLE doc
5
+ * authority and `docs_wikilinks` is a **DERIVED, non-authoritative** edge table
6
+ * reconstructed from three provenance columns already on `attachments`:
7
+ *
8
+ * - `supersedes` / `superseded_by` → doc→doc supersession edges
9
+ * - `related_tasks` → doc→T#### task edges (JSON array)
10
+ * - `topics` → doc↔doc shared-topic edges (JSON array)
11
+ *
12
+ * No markdown body `[[link]]` parsing is performed (T11826 AC4) — the edges
13
+ * derive purely from structured columns. The derivation is **idempotent**: it
14
+ * truncates and rebuilds the whole table, so callers may re-run it after any
15
+ * `docs add` / `supersede` / `docs update` write to keep the graph fresh.
16
+ *
17
+ * This module is the runtime twin of the SQL backfill in
18
+ * `migrations/drizzle-tasks/20260605000001_t11826-docs-wikilinks/migration.sql`
19
+ * — the migration seeds the table on schema upgrade, this function rebuilds it
20
+ * on demand.
21
+ *
22
+ * @task T11826 (Epic T11781 / Saga T11778)
23
+ * @adr ADR-078 — Docs SSoT as provenance graph
24
+ * @see build-provenance-graph.ts — the on-the-fly BFS this table makes O(edges)
25
+ */
26
+ import { eq, or } from 'drizzle-orm';
27
+ import { getProjectRoot } from '../paths.js';
28
+ import { docsWikilinks } from '../store/schema/attachments.js';
29
+ import { getDb } from '../store/sqlite.js';
30
+ import { attachments } from '../store/tasks-schema.js';
31
+ /**
32
+ * Idempotently rebuild the `docs_wikilinks` edge table from the provenance
33
+ * columns on `attachments`.
34
+ *
35
+ * The whole table is truncated and re-derived inside a single transaction, so
36
+ * the function is safe to call after any doc write and always converges to the
37
+ * same edge set for a given `attachments` state.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * const { edgeCount, byRelation } = await rebuildDocsWikilinks();
42
+ * console.log(`derived ${edgeCount} edges (${byRelation['topic']} topic links)`);
43
+ * ```
44
+ *
45
+ * @param opts - Optional project-root override.
46
+ * @returns Edge totals after the rebuild.
47
+ * @task T11826
48
+ */
49
+ export async function rebuildDocsWikilinks(opts = {}) {
50
+ const projectRoot = opts.projectRoot ?? getProjectRoot();
51
+ const db = await getDb(projectRoot);
52
+ // Read every attachment's id, slug and provenance columns in one pass, then
53
+ // resolve the supersession FK ids → slugs in JS via an id→slug map. (A
54
+ // correlated SQL subquery aliasing the same table is fragile across SQLite
55
+ // versions, so the resolution is done in memory.)
56
+ const allRows = await db
57
+ .select({
58
+ id: attachments.id,
59
+ slug: attachments.slug,
60
+ supersedes: attachments.supersedes,
61
+ supersededBy: attachments.supersededBy,
62
+ relatedTasks: attachments.relatedTasks,
63
+ topics: attachments.topics,
64
+ })
65
+ .from(attachments)
66
+ .all();
67
+ const slugById = new Map();
68
+ for (const r of allRows) {
69
+ if (r.slug)
70
+ slugById.set(r.id, r.slug);
71
+ }
72
+ const slugged = allRows
73
+ .filter((r) => typeof r.slug === 'string')
74
+ .map((r) => ({
75
+ slug: r.slug,
76
+ supersedesSlug: r.supersedes ? (slugById.get(r.supersedes) ?? null) : null,
77
+ supersededBySlug: r.supersededBy ? (slugById.get(r.supersededBy) ?? null) : null,
78
+ relatedTasks: r.relatedTasks,
79
+ topics: r.topics,
80
+ }));
81
+ const edges = deriveWikilinkEdges(slugged);
82
+ const nowIso = new Date().toISOString();
83
+ // The node:sqlite driver is synchronous — drizzle's transaction callback must
84
+ // be sync (an async body throws a DrizzleTypeError at build time).
85
+ db.transaction((tx) => {
86
+ tx.delete(docsWikilinks).run();
87
+ if (edges.length === 0)
88
+ return;
89
+ // Batch insert; INSERT OR IGNORE semantics via onConflictDoNothing keep the
90
+ // composite PK idempotent even if deriveWikilinkEdges emitted a duplicate.
91
+ tx.insert(docsWikilinks)
92
+ .values(edges.map((e) => ({
93
+ fromSlug: e.fromSlug,
94
+ toSlug: e.toSlug,
95
+ relation: e.relation,
96
+ toIsTask: e.toIsTask,
97
+ derivedAt: nowIso,
98
+ })))
99
+ .onConflictDoNothing()
100
+ .run();
101
+ });
102
+ const byRelation = {
103
+ supersedes: 0,
104
+ 'superseded-by': 0,
105
+ 'related-task': 0,
106
+ topic: 0,
107
+ };
108
+ for (const e of edges)
109
+ byRelation[e.relation] += 1;
110
+ return { edgeCount: edges.length, byRelation };
111
+ }
112
+ /**
113
+ * Pure derivation: turn slugged `attachments` rows into the full edge set.
114
+ *
115
+ * Exposed for unit testing the derivation rules without a database. The output
116
+ * is deduplicated on the (`fromSlug`, `toSlug`, `relation`) composite key.
117
+ *
118
+ * @param rows - Slugged attachment rows with their provenance columns.
119
+ * @returns The derived wikilink edges.
120
+ * @task T11826
121
+ */
122
+ export function deriveWikilinkEdges(rows) {
123
+ const seen = new Set();
124
+ const out = [];
125
+ const push = (fromSlug, toSlug, relation, toIsTask) => {
126
+ const key = `${fromSlug}|${toSlug}|${relation}`;
127
+ if (seen.has(key))
128
+ return;
129
+ seen.add(key);
130
+ out.push({ fromSlug, toSlug, relation, toIsTask });
131
+ };
132
+ // Topic membership index: topic slug → set of doc slugs carrying it.
133
+ const topicMembers = new Map();
134
+ for (const row of rows) {
135
+ // supersedes / superseded-by — doc→doc.
136
+ if (row.supersedesSlug)
137
+ push(row.slug, row.supersedesSlug, 'supersedes', false);
138
+ if (row.supersededBySlug)
139
+ push(row.slug, row.supersededBySlug, 'superseded-by', false);
140
+ // related-task — doc→T####.
141
+ for (const taskId of parseStringArray(row.relatedTasks)) {
142
+ if (/^T\d+$/.test(taskId))
143
+ push(row.slug, taskId, 'related-task', true);
144
+ }
145
+ // Accumulate topic membership for the symmetric pass below.
146
+ for (const topic of parseStringArray(row.topics)) {
147
+ let members = topicMembers.get(topic);
148
+ if (!members) {
149
+ members = new Set();
150
+ topicMembers.set(topic, members);
151
+ }
152
+ members.add(row.slug);
153
+ }
154
+ }
155
+ // topic — symmetric doc↔doc edges for co-members of any topic.
156
+ for (const members of topicMembers.values()) {
157
+ const slugs = [...members];
158
+ for (let i = 0; i < slugs.length; i++) {
159
+ for (let j = 0; j < slugs.length; j++) {
160
+ if (i === j)
161
+ continue;
162
+ push(slugs[i], slugs[j], 'topic', false);
163
+ }
164
+ }
165
+ }
166
+ return out;
167
+ }
168
+ /**
169
+ * Read all wikilink edges incident to a doc slug — **bidirectional** by default.
170
+ *
171
+ * Returns both outbound edges (`from_slug = slug`) and inbound backlinks
172
+ * (`to_slug = slug`). This is the query the Obsidian plugin (T11827) renders to
173
+ * show a doc's neighborhood, and what `cleo docs graph` hydrates for persisted
174
+ * backlinks.
175
+ *
176
+ * @param slug - The doc slug to fetch edges for.
177
+ * @param opts.direction - `'both'` (default), `'out'`, or `'in'`.
178
+ * @param opts.projectRoot - Project root override.
179
+ * @returns The incident edges.
180
+ * @task T11826
181
+ */
182
+ export async function getDocsWikilinks(slug, opts = {}) {
183
+ const direction = opts.direction ?? 'both';
184
+ const projectRoot = opts.projectRoot ?? getProjectRoot();
185
+ const db = await getDb(projectRoot);
186
+ const predicate = direction === 'out'
187
+ ? eq(docsWikilinks.fromSlug, slug)
188
+ : direction === 'in'
189
+ ? eq(docsWikilinks.toSlug, slug)
190
+ : or(eq(docsWikilinks.fromSlug, slug), eq(docsWikilinks.toSlug, slug));
191
+ const rows = await db.select().from(docsWikilinks).where(predicate).all();
192
+ return rows.map((r) => ({
193
+ fromSlug: r.fromSlug,
194
+ toSlug: r.toSlug,
195
+ relation: r.relation,
196
+ toIsTask: r.toIsTask,
197
+ }));
198
+ }
199
+ /**
200
+ * Parse a JSON-array-of-strings column, tolerating null / malformed values.
201
+ *
202
+ * @internal
203
+ */
204
+ function parseStringArray(raw) {
205
+ if (!raw)
206
+ return [];
207
+ try {
208
+ const parsed = JSON.parse(raw);
209
+ if (!Array.isArray(parsed))
210
+ return [];
211
+ return parsed.filter((v) => typeof v === 'string' && v.length > 0);
212
+ }
213
+ catch {
214
+ return [];
215
+ }
216
+ }
217
+ //# sourceMappingURL=wikilinks.js.map