@plebeiantech/lat.md 0.12.3-fork.17 → 0.12.3-fork.20

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 (244) hide show
  1. package/README.md +1 -1
  2. package/dist/src/cli/agent-invocation.d.ts +6 -0
  3. package/dist/src/cli/agent-invocation.js +23 -0
  4. package/dist/src/cli/fork-instructions.js +1 -1
  5. package/dist/src/cli/fork-scaffold.js +2 -2
  6. package/dist/src/cli/graph.d.ts +1 -1
  7. package/dist/src/cli/hook.js +11 -11
  8. package/dist/src/cli/index.js +41 -59
  9. package/dist/src/cli/init-preferences.js +5 -4
  10. package/dist/src/cli/init.d.ts +1 -1
  11. package/dist/src/cli/init.js +54 -45
  12. package/dist/src/cli/reindex.d.ts +1 -1
  13. package/dist/src/cli/reindex.js +2 -2
  14. package/dist/src/cli/search.d.ts +4 -4
  15. package/dist/src/cli/search.js +41 -28
  16. package/dist/src/cli/ui-build-server.d.ts +1 -1
  17. package/dist/src/cli/ui-build.d.ts +1 -1
  18. package/dist/src/cli/ui.d.ts +1 -1
  19. package/dist/src/fork/antigravity-hook.js +6 -6
  20. package/dist/src/fork/antigravity-init.d.ts +2 -2
  21. package/dist/src/fork/antigravity-init.js +2 -2
  22. package/dist/src/graph-export.js +7 -7
  23. package/dist/src/knowledge/index.js +2 -2
  24. package/dist/src/mcp/server.js +8 -8
  25. package/dist/src/search/cache.d.ts +1 -1
  26. package/dist/src/search/cache.js +40 -7
  27. package/dist/src/search/chunks.d.ts +2 -2
  28. package/dist/src/search/db.d.ts +5 -4
  29. package/dist/src/search/db.js +36 -40
  30. package/dist/src/search/embedder.js +1 -1
  31. package/dist/src/search/index.d.ts +1 -1
  32. package/dist/src/search/index.js +2 -2
  33. package/dist/src/search/lock.d.ts +4 -1
  34. package/dist/src/search/lock.js +30 -8
  35. package/dist/src/search/query.d.ts +5 -2
  36. package/dist/src/search/query.js +55 -38
  37. package/dist/src/search/search.d.ts +3 -1
  38. package/dist/src/search/search.js +21 -12
  39. package/dist/src/search/types.d.ts +2 -18
  40. package/dist/src/view/client/assets/{GraphView-BWlRdQM4.js → GraphView-Dij8mAA_.js} +1 -1
  41. package/dist/src/view/client/assets/{MarkdownEditor-MW-Ck1O0.js → MarkdownEditor-88Wbt0M1.js} +1 -1
  42. package/dist/src/view/client/assets/{abnfDiagram-VCTEODGH-Bn3RshMV.js → abnfDiagram-VCTEODGH-y0DlYa8E.js} +1 -1
  43. package/dist/src/view/client/assets/{arc-En9tdFQF.js → arc-FlMu8dnO.js} +1 -1
  44. package/dist/src/view/client/assets/{architectureDiagram-5GKGNRK7-B-9J1VAM.js → architectureDiagram-5GKGNRK7-DA_uWZW1.js} +1 -1
  45. package/dist/src/view/client/assets/{blockDiagram-I7D4REHJ-QlznwalP.js → blockDiagram-I7D4REHJ-BHQ5LU85.js} +1 -1
  46. package/dist/src/view/client/assets/{c4Diagram-7LVT6UL2-Bp2y8B4_.js → c4Diagram-7LVT6UL2-BRucKCt2.js} +1 -1
  47. package/dist/src/view/client/assets/channel-BgDNA_Ay.js +1 -0
  48. package/dist/src/view/client/assets/{chunk-2Q5K7J3B-CnBKL-lm.js → chunk-2Q5K7J3B-BJPcFivt.js} +1 -1
  49. package/dist/src/view/client/assets/{chunk-5VM5RSS4-LLYMi_n6.js → chunk-5VM5RSS4-v2q99bGz.js} +1 -1
  50. package/dist/src/view/client/assets/{chunk-F27PBJKO-VIeO7VF0.js → chunk-F27PBJKO-CcHOuJCs.js} +1 -1
  51. package/dist/src/view/client/assets/{chunk-IMKFNOWR-D5i67DVD.js → chunk-IMKFNOWR-DNxYuJNq.js} +1 -1
  52. package/dist/src/view/client/assets/{chunk-JWPE2WC7-D4qAtoL3.js → chunk-JWPE2WC7-C0eHKvLW.js} +1 -1
  53. package/dist/src/view/client/assets/{chunk-POPQ4Y6H-BMzOyiW_.js → chunk-POPQ4Y6H-57CWp2Ve.js} +1 -1
  54. package/dist/src/view/client/assets/{chunk-SVP7TREG-DWy5nbfp.js → chunk-SVP7TREG-BqcoxIJw.js} +1 -1
  55. package/dist/src/view/client/assets/{chunk-TICWLB2K-CVqCANFB.js → chunk-TICWLB2K-DRUJR5zH.js} +1 -1
  56. package/dist/src/view/client/assets/{chunk-XXDRQBXY-Bp23h6Tf.js → chunk-XXDRQBXY-Dez0oRoM.js} +1 -1
  57. package/dist/src/view/client/assets/classDiagram-ZZMXUADV-CFZ_Sgh5.js +1 -0
  58. package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-CFZ_Sgh5.js +1 -0
  59. package/dist/src/view/client/assets/{cose-bilkent-JH36ORCC-BCw7fCC2.js → cose-bilkent-JH36ORCC-BHtud6Hg.js} +1 -1
  60. package/dist/src/view/client/assets/{cynefin-OW5HDTMX--lYN4N9m.js → cynefin-OW5HDTMX-BjBW8VGk.js} +1 -1
  61. package/dist/src/view/client/assets/{cynefinDiagram-5FMLGOSQ-9sK5EV7v.js → cynefinDiagram-5FMLGOSQ-DwGuD7oF.js} +1 -1
  62. package/dist/src/view/client/assets/{dagre-GXQ25YYZ-CoMdEFCq.js → dagre-GXQ25YYZ-C5zSlGK9.js} +1 -1
  63. package/dist/src/view/client/assets/{diagram-S7CK7UJ4-D13dyWdd.js → diagram-S7CK7UJ4-C_NhA3bj.js} +1 -1
  64. package/dist/src/view/client/assets/{diagram-UQ7AKVKN-PdwLPPPA.js → diagram-UQ7AKVKN-az7QPYNb.js} +1 -1
  65. package/dist/src/view/client/assets/{diagram-VSXAHHWV-CtWWa_A7.js → diagram-VSXAHHWV-BRvDuM8_.js} +1 -1
  66. package/dist/src/view/client/assets/{diagram-VX7I27RA-BeCYcSU-.js → diagram-VX7I27RA-BWLgF6S-.js} +1 -1
  67. package/dist/src/view/client/assets/{diagram-Z3DM3KII-DiWabOBM.js → diagram-Z3DM3KII-DV3d6mL5.js} +1 -1
  68. package/dist/src/view/client/assets/{ebnfDiagram-PWID7BFC-CZQ3MD_K.js → ebnfDiagram-PWID7BFC-yUXrEAvK.js} +1 -1
  69. package/dist/src/view/client/assets/{erDiagram-RLTQ6QDP-B1AhXIrP.js → erDiagram-RLTQ6QDP-D35FN1Pt.js} +1 -1
  70. package/dist/src/view/client/assets/{flowDiagram-HODETNUW-Bhr0JM6P.js → flowDiagram-HODETNUW-BciZqsjT.js} +1 -1
  71. package/dist/src/view/client/assets/{ganttDiagram-EL5Y4UJY-CbP8iAkg.js → ganttDiagram-EL5Y4UJY-BysPGgXS.js} +1 -1
  72. package/dist/src/view/client/assets/{gitGraphDiagram-WWUBYQGX-B9uuGdLF.js → gitGraphDiagram-WWUBYQGX-C3DdJ5xG.js} +1 -1
  73. package/dist/src/view/client/assets/index-CJokoRSp.js +13 -0
  74. package/dist/src/view/client/assets/{infoDiagram-27XIBGKW-BrfwQ_Cq.js → infoDiagram-27XIBGKW-D3HZrDRb.js} +1 -1
  75. package/dist/src/view/client/assets/{ishikawaDiagram-5VMMS53U-CHtJ36re.js → ishikawaDiagram-5VMMS53U-BXyVIXTU.js} +1 -1
  76. package/dist/src/view/client/assets/{journeyDiagram-3NMN7TZE-BChcgX1s.js → journeyDiagram-3NMN7TZE-D4-1NxVU.js} +1 -1
  77. package/dist/src/view/client/assets/{kanban-definition-UXKFOSKX-BnwFsKiB.js → kanban-definition-UXKFOSKX-4Gu_5yBA.js} +1 -1
  78. package/dist/src/view/client/assets/{layout-Dnd0pLdb.js → layout-Jf1K3TMM.js} +1 -1
  79. package/dist/src/view/client/assets/{linear-CFhW2ozn.js → linear-DWfwzUT_.js} +1 -1
  80. package/dist/src/view/client/assets/{mermaid.core--OP6lXtW.js → mermaid.core-DMx_DAl8.js} +5 -5
  81. package/dist/src/view/client/assets/{mindmap-definition-YA3MSWOX-D51y2zbm.js → mindmap-definition-YA3MSWOX-8pxY_RWi.js} +1 -1
  82. package/dist/src/view/client/assets/{pegDiagram-XKGWAZYB--zyQJDvI.js → pegDiagram-XKGWAZYB-DNAw_O5t.js} +1 -1
  83. package/dist/src/view/client/assets/{pieDiagram-E7YTZNPT-Cj8mvpJy.js → pieDiagram-E7YTZNPT-uuvRCDkC.js} +1 -1
  84. package/dist/src/view/client/assets/{quadrantDiagram-AXDQQJYC-qvhJrnBf.js → quadrantDiagram-AXDQQJYC-BER2O4Aq.js} +1 -1
  85. package/dist/src/view/client/assets/{railroadDiagram-O6MQD6OU-CtWvxpSo.js → railroadDiagram-O6MQD6OU-CxEsJ_0n.js} +1 -1
  86. package/dist/src/view/client/assets/{requirementDiagram-BXWQKSXE-CBxen_KG.js → requirementDiagram-BXWQKSXE-yS9rspca.js} +1 -1
  87. package/dist/src/view/client/assets/{sankeyDiagram-P5KCCOFB-B_fUkmfc.js → sankeyDiagram-P5KCCOFB-Bqt70a2j.js} +1 -1
  88. package/dist/src/view/client/assets/{sequenceDiagram-WJ2MYXX4-Dclv4p68.js → sequenceDiagram-WJ2MYXX4-BvOOEJNr.js} +1 -1
  89. package/dist/src/view/client/assets/{sizeCapture-INFHLROL-GfG0sMTZ.js → sizeCapture-INFHLROL-CvO_Pjjo.js} +1 -1
  90. package/dist/src/view/client/assets/{stateDiagram-D77RDMKH-YQWPnzxU.js → stateDiagram-D77RDMKH-KLmVOZkq.js} +1 -1
  91. package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-KCS8Mdzu.js +1 -0
  92. package/dist/src/view/client/assets/{swimlanes-42K2YHIH-5yiCveVS.js → swimlanes-42K2YHIH-DC1r4_sq.js} +1 -1
  93. package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-B2TcAwis.js +8 -0
  94. package/dist/src/view/client/assets/{timeline-definition-24CTP7MA-BvAbB5Zs.js → timeline-definition-24CTP7MA-ChXLOuBL.js} +1 -1
  95. package/dist/src/view/client/assets/{vennDiagram-4TSXK5OY-guC-r60n.js → vennDiagram-4TSXK5OY-D9_h25-Y.js} +1 -1
  96. package/dist/src/view/client/assets/{wardleyDiagram-VM6X3IG4-C0yXcU2x.js → wardleyDiagram-VM6X3IG4-CsQJGGW0.js} +1 -1
  97. package/dist/src/view/client/assets/{xychartDiagram-S5SC5T6Z-RPmkPNfK.js → xychartDiagram-S5SC5T6Z-DGMuPMlm.js} +1 -1
  98. package/dist/src/view/client/index.html +1 -1
  99. package/dist/src/view/diagnostics.d.ts +2 -2
  100. package/dist/src/view/diagnostics.js +5 -5
  101. package/dist/src/view/external-document-tree.js +1 -1
  102. package/dist/src/view/git-diff.js +1 -1
  103. package/dist/src/view/git.js +1 -1
  104. package/dist/src/view/graph.d.ts +2 -2
  105. package/dist/src/view/graph.js +2 -2
  106. package/dist/src/view/markdown.js +1 -1
  107. package/dist/src/view/preindexed-search.d.ts +1 -1
  108. package/dist/src/view/protocol.d.ts +2 -18
  109. package/dist/src/view/publication.d.ts +2 -0
  110. package/dist/src/view/publication.js +78 -0
  111. package/dist/src/view/references.d.ts +3 -3
  112. package/dist/src/view/references.js +3 -3
  113. package/dist/src/view/repository.d.ts +6 -6
  114. package/dist/src/view/repository.js +28 -14
  115. package/dist/src/view/request-security.d.ts +5 -0
  116. package/dist/src/view/request-security.js +46 -0
  117. package/dist/src/view/search-result.d.ts +1 -1
  118. package/dist/src/view/search-result.js +1 -1
  119. package/dist/src/view/search.d.ts +1 -1
  120. package/dist/src/view/server-build.d.ts +2 -2
  121. package/dist/src/view/server-build.js +2 -2
  122. package/dist/src/view/server-deployment.d.ts +1 -2
  123. package/dist/src/view/server-deployment.js +6 -46
  124. package/dist/src/view/server-index-worker.d.ts +1 -1
  125. package/dist/src/view/server.d.ts +1 -1
  126. package/dist/src/view/server.js +13 -3
  127. package/dist/src/view/source-target.js +2 -2
  128. package/dist/src/view/static-build.d.ts +1 -1
  129. package/dist/src/view/static-build.js +49 -17
  130. package/dist/src/view/store.d.ts +4 -2
  131. package/dist/src/view/store.js +31 -19
  132. package/dist/src/view/table-of-contents.d.ts +1 -1
  133. package/dist/src/view/table-of-contents.js +1 -1
  134. package/dist/src/view/vercel-server-build.d.ts +1 -1
  135. package/package.json +19 -29
  136. package/templates/AGENTS.md +2 -2
  137. package/templates/cursor-rules.md +2 -2
  138. package/templates/opencode-plugin.ts +9 -9
  139. package/templates/pi-extension.ts +11 -11
  140. package/templates/skill/SKILL.md +2 -1
  141. package/dist/src/cli/check-context.d.ts +0 -56
  142. package/dist/src/cli/check-context.js +0 -215
  143. package/dist/src/cli/check-coverage.d.ts +0 -2
  144. package/dist/src/cli/check-coverage.js +0 -75
  145. package/dist/src/cli/check-frontmatter.d.ts +0 -12
  146. package/dist/src/cli/check-frontmatter.js +0 -44
  147. package/dist/src/cli/check-mode.d.ts +0 -21
  148. package/dist/src/cli/check-mode.js +0 -394
  149. package/dist/src/cli/check-status.d.ts +0 -74
  150. package/dist/src/cli/check-status.js +0 -217
  151. package/dist/src/cli/check.d.ts +0 -52
  152. package/dist/src/cli/check.js +0 -834
  153. package/dist/src/cli/context.d.ts +0 -11
  154. package/dist/src/cli/context.js +0 -58
  155. package/dist/src/cli/expand.d.ts +0 -7
  156. package/dist/src/cli/expand.js +0 -141
  157. package/dist/src/cli/external.d.ts +0 -11
  158. package/dist/src/cli/external.js +0 -185
  159. package/dist/src/cli/gen-index.d.ts +0 -45
  160. package/dist/src/cli/gen-index.js +0 -235
  161. package/dist/src/cli/link-scheme.d.ts +0 -54
  162. package/dist/src/cli/link-scheme.js +0 -101
  163. package/dist/src/cli/locate.d.ts +0 -2
  164. package/dist/src/cli/locate.js +0 -18
  165. package/dist/src/cli/refs.d.ts +0 -23
  166. package/dist/src/cli/refs.js +0 -274
  167. package/dist/src/cli/section.d.ts +0 -45
  168. package/dist/src/cli/section.js +0 -328
  169. package/dist/src/cli/select-menu.d.ts +0 -13
  170. package/dist/src/cli/select-menu.js +0 -100
  171. package/dist/src/code-refs.d.ts +0 -29
  172. package/dist/src/code-refs.js +0 -479
  173. package/dist/src/config.d.ts +0 -29
  174. package/dist/src/config.js +0 -83
  175. package/dist/src/context.d.ts +0 -26
  176. package/dist/src/context.js +0 -11
  177. package/dist/src/document-formats.d.ts +0 -7
  178. package/dist/src/document-formats.js +0 -39
  179. package/dist/src/extensions/alert-marker.d.ts +0 -25
  180. package/dist/src/extensions/alert-marker.js +0 -55
  181. package/dist/src/extensions/wiki-link/from-markdown.d.ts +0 -5
  182. package/dist/src/extensions/wiki-link/from-markdown.js +0 -34
  183. package/dist/src/extensions/wiki-link/index.d.ts +0 -4
  184. package/dist/src/extensions/wiki-link/index.js +0 -3
  185. package/dist/src/extensions/wiki-link/syntax.d.ts +0 -14
  186. package/dist/src/extensions/wiki-link/syntax.js +0 -121
  187. package/dist/src/extensions/wiki-link/to-markdown.d.ts +0 -5
  188. package/dist/src/extensions/wiki-link/to-markdown.js +0 -28
  189. package/dist/src/extensions/wiki-link/types.d.ts +0 -34
  190. package/dist/src/extensions/wiki-link/types.js +0 -1
  191. package/dist/src/external-documents.d.ts +0 -56
  192. package/dist/src/external-documents.js +0 -404
  193. package/dist/src/external-sources.d.ts +0 -136
  194. package/dist/src/external-sources.js +0 -1098
  195. package/dist/src/fork/frontmatter-fields.d.ts +0 -36
  196. package/dist/src/fork/frontmatter-fields.js +0 -23
  197. package/dist/src/format.d.ts +0 -16
  198. package/dist/src/format.js +0 -76
  199. package/dist/src/init-version.d.ts +0 -10
  200. package/dist/src/init-version.js +0 -49
  201. package/dist/src/lattice-model.d.ts +0 -84
  202. package/dist/src/lattice-model.js +0 -467
  203. package/dist/src/lattice.d.ts +0 -14
  204. package/dist/src/lattice.js +0 -353
  205. package/dist/src/markdown-analysis-cache.d.ts +0 -20
  206. package/dist/src/markdown-analysis-cache.js +0 -139
  207. package/dist/src/markdown-analysis-worker.d.ts +0 -16
  208. package/dist/src/markdown-analysis-worker.js +0 -33
  209. package/dist/src/markdown-analysis.d.ts +0 -61
  210. package/dist/src/markdown-analysis.js +0 -169
  211. package/dist/src/markdown-analyzer-loader.d.ts +0 -4
  212. package/dist/src/markdown-analyzer-loader.js +0 -13
  213. package/dist/src/markdown-validation.d.ts +0 -25
  214. package/dist/src/markdown-validation.js +0 -104
  215. package/dist/src/parser-cache.d.ts +0 -17
  216. package/dist/src/parser-cache.js +0 -65
  217. package/dist/src/parser-import.d.ts +0 -8
  218. package/dist/src/parser-import.js +0 -1
  219. package/dist/src/parser.d.ts +0 -3
  220. package/dist/src/parser.js +0 -93
  221. package/dist/src/path.d.ts +0 -2
  222. package/dist/src/path.js +0 -4
  223. package/dist/src/profiler.d.ts +0 -16
  224. package/dist/src/profiler.js +0 -81
  225. package/dist/src/project-analysis.d.ts +0 -59
  226. package/dist/src/project-analysis.js +0 -233
  227. package/dist/src/project-discovery.d.ts +0 -6
  228. package/dist/src/project-discovery.js +0 -30
  229. package/dist/src/repository-path.d.ts +0 -11
  230. package/dist/src/repository-path.js +0 -52
  231. package/dist/src/source-formats.d.ts +0 -7
  232. package/dist/src/source-formats.js +0 -30
  233. package/dist/src/source-parser.d.ts +0 -67
  234. package/dist/src/source-parser.js +0 -1749
  235. package/dist/src/untrusted.d.ts +0 -18
  236. package/dist/src/untrusted.js +0 -93
  237. package/dist/src/view/client/assets/channel-uy0fFvcW.js +0 -1
  238. package/dist/src/view/client/assets/classDiagram-ZZMXUADV-DbSEqjA5.js +0 -1
  239. package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-DbSEqjA5.js +0 -1
  240. package/dist/src/view/client/assets/index-nBmbYfun.js +0 -13
  241. package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-gUj6Hzgl.js +0 -1
  242. package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-DR7JhrxL.js +0 -8
  243. package/dist/src/walk.d.ts +0 -20
  244. package/dist/src/walk.js +0 -121
@@ -1,467 +0,0 @@
1
- import GithubSlugger from 'github-slugger';
2
- import { toPosix } from './path.js';
3
- export function flattenSections(sections) {
4
- const result = [];
5
- for (const s of sections) {
6
- result.push(s);
7
- result.push(...flattenSections(s.children));
8
- }
9
- return result;
10
- }
11
- function levenshtein(a, b) {
12
- const m = a.length;
13
- const n = b.length;
14
- const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
15
- for (let i = 0; i <= m; i++)
16
- dp[i][0] = i;
17
- for (let j = 0; j <= n; j++)
18
- dp[0][j] = j;
19
- for (let i = 1; i <= m; i++) {
20
- for (let j = 1; j <= n; j++) {
21
- dp[i][j] =
22
- a[i - 1] === b[j - 1]
23
- ? dp[i - 1][j - 1]
24
- : 1 + Math.min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]);
25
- }
26
- }
27
- return dp[m][n];
28
- }
29
- /**
30
- * Returns the trailing segment(s) of a section id.
31
- * e.g. "markdown#Frontmatter#require-code-mention" → ["Frontmatter#require-code-mention", "require-code-mention"]
32
- * The full id itself is not included (handled by exact match).
33
- */
34
- function tailSegments(id) {
35
- const parts = id.split('#');
36
- const tails = [];
37
- for (let i = 1; i < parts.length; i++) {
38
- tails.push(parts.slice(i).join('#'));
39
- }
40
- return tails;
41
- }
42
- /**
43
- * Build an index mapping path suffixes to their full vault-relative paths.
44
- * Used by resolveRef to allow short references when a suffix is unambiguous.
45
- *
46
- * For a file like `lat.md/guides/setup`, indexes both `guides/setup` and `setup`.
47
- * This ensures backward-compatible short refs after the vault root moved to the
48
- * project root (so section IDs now include the `lat.md/` prefix).
49
- */
50
- export function buildFileIndex(sections) {
51
- const flat = flattenSections(sections);
52
- const index = new Map();
53
- for (const s of flat) {
54
- const parts = s.file.split('/');
55
- // Index all trailing path suffixes (excluding the full path itself,
56
- // which is handled by exact match). Keys are lowercase for
57
- // case-insensitive lookup.
58
- for (let i = 1; i < parts.length; i++) {
59
- const suffix = parts.slice(i).join('/').toLowerCase();
60
- if (!index.has(suffix))
61
- index.set(suffix, new Set());
62
- index.get(suffix).add(s.file);
63
- }
64
- }
65
- const result = new Map();
66
- for (const [stem, paths] of index) {
67
- result.set(stem, [...paths]);
68
- }
69
- return result;
70
- }
71
- /**
72
- * Map GitHub-slugged heading paths back to canonical, literal-heading ids.
73
- * Every path segment accepts either representation so an implicit literal h1
74
- * can still prefix slugged child headings during short-ref resolution.
75
- */
76
- export function buildSectionSlugIndex(sections) {
77
- const aliases = new Map();
78
- const stacks = new Map();
79
- const sluggers = new Map();
80
- for (const section of flattenSections(sections)) {
81
- let slugger = sluggers.get(section.file);
82
- if (!slugger) {
83
- slugger = new GithubSlugger();
84
- sluggers.set(section.file, slugger);
85
- }
86
- // Always advance the fallback slugger so manually constructed Section
87
- // objects without githubSlug still get correct duplicate suffixes.
88
- const fallbackSlug = slugger.slug(section.heading);
89
- const headingSlug = section.githubSlug ?? fallbackSlug;
90
- const stack = stacks.get(section.file) ?? [];
91
- while (stack.length > 0 && stack[stack.length - 1].depth >= section.depth) {
92
- stack.pop();
93
- }
94
- const levels = [
95
- ...stack,
96
- {
97
- id: section.id,
98
- depth: section.depth,
99
- literal: section.id
100
- .slice((stack.at(-1)?.id ?? section.file).length + 1)
101
- .toLowerCase(),
102
- slug: headingSlug.toLowerCase(),
103
- },
104
- ];
105
- let paths = [''];
106
- for (const level of levels) {
107
- const options = [...new Set([level.literal, level.slug])];
108
- paths = paths.flatMap((path) => options.map((part) => (path ? `${path}#${part}` : part)));
109
- }
110
- for (const path of paths) {
111
- const alias = `${section.file.toLowerCase()}#${path}`;
112
- if (!aliases.has(alias))
113
- aliases.set(alias, section.id);
114
- }
115
- stack.push(levels[levels.length - 1]);
116
- stacks.set(section.file, stack);
117
- }
118
- return aliases;
119
- }
120
- /**
121
- * Normalize separators only in the file portion of a section reference.
122
- * Headings are authored text and may legitimately contain backslashes.
123
- */
124
- function normalizeRefFilePath(target) {
125
- const hashIdx = target.indexOf('#');
126
- const file = toPosix(hashIdx === -1 ? target : target.slice(0, hashIdx));
127
- const normalizedFile = file.replace(/\.md$/i, '');
128
- return normalizedFile + (hashIdx === -1 ? '' : target.slice(hashIdx));
129
- }
130
- /**
131
- * Resolve a potentially short reference to its canonical full-path form.
132
- * If the file segment of the ref is a bare stem that uniquely maps to one
133
- * full path, expands it. Otherwise returns the ref unchanged.
134
- *
135
- * When ambiguous (multiple files share the stem), returns all candidates.
136
- * If exactly one candidate actually contains the referenced section,
137
- * `suggested` is set to that candidate so the caller can propose a fix.
138
- */
139
- export function resolveRef(target, sectionIds, fileIndex, slugIndex) {
140
- target = normalizeRefFilePath(target);
141
- const resolveKnown = (candidate) => {
142
- // Preserve existing wiki-link meaning when a literal heading happens to
143
- // collide with a different heading's GitHub slug.
144
- if (sectionIds.has(candidate.toLowerCase()))
145
- return candidate;
146
- return slugIndex?.get(candidate.toLowerCase()) ?? null;
147
- };
148
- const resolveInFile = (file, rest) => {
149
- const direct = resolveKnown(file + rest);
150
- if (direct)
151
- return direct;
152
- // Try inserting root headings between file and rest. This also works for
153
- // slugged child segments because the slug index contains mixed paths.
154
- const rootHeadings = findRootHeadings(file, sectionIds);
155
- for (const h1 of rootHeadings) {
156
- const withRoot = rest ? `${file}#${h1}${rest}` : `${file}#${h1}`;
157
- const resolved = resolveKnown(withRoot);
158
- if (resolved)
159
- return resolved;
160
- }
161
- return null;
162
- };
163
- // Already matches a known section — no resolution needed
164
- const direct = resolveKnown(target);
165
- if (direct) {
166
- return { resolved: direct, ambiguous: null, suggested: null };
167
- }
168
- // Extract the file segment (before first #) and try resolving it
169
- const hashIdx = target.indexOf('#');
170
- const filePart = hashIdx === -1 ? target : target.slice(0, hashIdx);
171
- const rest = hashIdx === -1 ? '' : target.slice(hashIdx);
172
- // Try resolving the file part: either it's a full path or a bare stem
173
- // File index keys are lowercase for case-insensitive lookup.
174
- const lcFilePart = filePart.toLowerCase();
175
- const filePaths = fileIndex.has(lcFilePart)
176
- ? fileIndex.get(lcFilePart)
177
- : [filePart];
178
- if (filePaths.length === 1) {
179
- const fp = filePaths[0];
180
- const resolved = resolveInFile(fp, rest);
181
- if (resolved) {
182
- return { resolved, ambiguous: null, suggested: null };
183
- }
184
- }
185
- else if (filePaths.length > 1) {
186
- // Multiple files share this stem — ambiguous at the filename level
187
- const all = filePaths.map((c) => c + rest);
188
- const valid = filePaths.filter((c) => resolveInFile(c, rest) !== null);
189
- return {
190
- resolved: target,
191
- ambiguous: all,
192
- suggested: valid.length === 1 ? valid[0] + rest : null,
193
- };
194
- }
195
- return { resolved: target, ambiguous: null, suggested: null };
196
- }
197
- /**
198
- * Find root (h1) headings for a file by scanning sectionIds for entries
199
- * that have exactly the pattern `file#heading` (no further # segments).
200
- */
201
- function findRootHeadings(file, sectionIds) {
202
- const prefix = file.toLowerCase() + '#';
203
- const headings = [];
204
- for (const id of sectionIds) {
205
- if (id.startsWith(prefix) && !id.includes('#', prefix.length)) {
206
- headings.push(id.slice(prefix.length));
207
- }
208
- }
209
- return headings;
210
- }
211
- const MAX_DISTANCE_RATIO = 0.4;
212
- export function findSections(sections, query) {
213
- const flat = flattenSections(sections);
214
- // Leading # means "search for a heading", strip it
215
- const normalized = normalizeRefFilePath(query.startsWith('#') ? query.slice(1) : query);
216
- const q = normalized.toLowerCase();
217
- const isFullPath = normalized.includes('#');
218
- const byId = new Map(flat.map((s) => [s.id.toLowerCase(), s]));
219
- const slugIndex = buildSectionSlugIndex(sections);
220
- const sectionFor = (candidate) => {
221
- const key = candidate.toLowerCase();
222
- return byId.get(key) ?? byId.get(slugIndex.get(key)?.toLowerCase() ?? '');
223
- };
224
- // Tier 1: exact full-id match
225
- const literalExact = flat.filter((s) => s.id.toLowerCase() === q);
226
- const slugExact = literalExact.length === 0 && isFullPath
227
- ? sectionFor(normalized)
228
- : undefined;
229
- const exact = slugExact ? [slugExact] : literalExact;
230
- const exactMatches = exact.map((s) => ({
231
- section: s,
232
- reason: 'exact match',
233
- }));
234
- if (exactMatches.length > 0 && isFullPath)
235
- return exactMatches;
236
- // Build file index early — used by both tier 1a and 1b
237
- const fileIndex = buildFileIndex(sections);
238
- // Tier 1a: bare name matches file — return root sections of that file
239
- // Also checks via file index (e.g. "dev-process" → "lat.md/dev-process")
240
- if (!isFullPath && exactMatches.length === 0) {
241
- const matchFiles = new Set();
242
- // Direct match
243
- for (const s of flat) {
244
- if (s.file.toLowerCase() === q &&
245
- !s.id.includes('#', s.file.length + 1)) {
246
- matchFiles.add(s.file);
247
- }
248
- }
249
- // File index expansion (keys are lowercase)
250
- const indexPaths = fileIndex.get(q) ?? [];
251
- for (const p of indexPaths) {
252
- matchFiles.add(p);
253
- }
254
- if (matchFiles.size > 0) {
255
- const fileRoots = flat.filter((s) => matchFiles.has(s.file) && !s.id.includes('#', s.file.length + 1));
256
- if (fileRoots.length > 0) {
257
- return fileRoots.map((s) => ({
258
- section: s,
259
- reason: 'exact match',
260
- }));
261
- }
262
- }
263
- }
264
- // Tier 1b: file stem expansion
265
- // For bare names: "locate" → matches root section of "tests/locate.md"
266
- // For paths with #: "setup#Install" → expands to "guides/setup#Install"
267
- const stemMatches = [];
268
- if (isFullPath) {
269
- // Expand file stem in the file part of the query
270
- const hashIdx = normalized.indexOf('#');
271
- const filePart = normalized.slice(0, hashIdx);
272
- const rest = normalized.slice(hashIdx);
273
- const stemPaths = fileIndex.get(filePart.toLowerCase()) ?? [];
274
- // Also try filePart as a direct file path (for root-level files not in index)
275
- const allPaths = stemPaths.length > 0 ? stemPaths : filePart ? [filePart] : [];
276
- for (const p of allPaths) {
277
- const s = sectionFor(p + rest);
278
- if (s) {
279
- if (exact.includes(s))
280
- continue;
281
- stemMatches.push({
282
- section: s,
283
- reason: stemPaths.length > 0
284
- ? `file stem expanded: ${filePart} → ${p}`
285
- : 'exact match',
286
- });
287
- continue;
288
- }
289
- // Try inserting root headings: file#rest → file#h1#rest
290
- const rootsOfFile = flat.filter((s) => s.file.toLowerCase() === p.toLowerCase() &&
291
- !s.id.includes('#', s.file.length + 1));
292
- for (const root of rootsOfFile) {
293
- const match = sectionFor(root.id + rest);
294
- if (match) {
295
- if (exact.includes(match))
296
- continue;
297
- stemMatches.push({
298
- section: match,
299
- reason: stemPaths.length > 0
300
- ? `file stem expanded: ${filePart} → ${p}`
301
- : 'exact match',
302
- });
303
- }
304
- }
305
- }
306
- if (stemMatches.length > 0)
307
- return [...exactMatches, ...stemMatches];
308
- }
309
- else {
310
- // Bare name: match root sections of files via stem index (keys lowercase)
311
- const paths = fileIndex.get(q) ?? [];
312
- for (const p of paths) {
313
- for (const s of flat) {
314
- if (exact.includes(s))
315
- continue;
316
- // Root sections have id = "file#heading" (exactly 2 segments)
317
- if (s.file.toLowerCase() === p.toLowerCase() &&
318
- !s.id.includes('#', s.file.length + 1)) {
319
- stemMatches.push({ section: s, reason: 'file stem match' });
320
- }
321
- }
322
- }
323
- }
324
- // Tier 2: exact match on trailing segments (subsection name match)
325
- const seen = new Set([
326
- ...exact.map((s) => s.id),
327
- ...stemMatches.map((m) => m.section.id),
328
- ]);
329
- const slugTailMatches = new Set();
330
- if (!isFullPath) {
331
- for (const [alias, canonical] of slugIndex) {
332
- if (alias.slice(alias.lastIndexOf('#') + 1) === q) {
333
- slugTailMatches.add(canonical.toLowerCase());
334
- }
335
- }
336
- }
337
- const literalTailMatches = new Set(isFullPath
338
- ? []
339
- : flat
340
- .filter((s) => tailSegments(s.id).some((tail) => tail.toLowerCase() === q))
341
- .map((s) => s.id.toLowerCase()));
342
- const subsection = isFullPath
343
- ? []
344
- : flat
345
- .filter((s) => {
346
- if (seen.has(s.id))
347
- return false;
348
- const id = s.id.toLowerCase();
349
- return literalTailMatches.size > 0
350
- ? literalTailMatches.has(id)
351
- : slugTailMatches.has(id);
352
- })
353
- .map((s) => ({ section: s, reason: 'section name match' }));
354
- // Tier 2b: subsequence match — query segments are a subsequence of section id segments
355
- // e.g. "Markdown#Resolution Rules" matches "markdown#Wiki Links#Resolution Rules"
356
- // Also tries expanding the file part via the file index for short refs.
357
- const seenSub = new Set([...seen, ...subsection.map((m) => m.section.id)]);
358
- const qParts = q.split('#');
359
- // Build query variants: original + file-index-expanded forms
360
- const qVariants = [qParts];
361
- if (qParts.length >= 2) {
362
- const expanded = fileIndex.get(qParts[0]);
363
- if (expanded) {
364
- for (const exp of expanded) {
365
- qVariants.push([exp.toLowerCase(), ...qParts.slice(1)]);
366
- }
367
- }
368
- }
369
- const subsequence = qParts.length >= 2
370
- ? flat
371
- .filter((s) => {
372
- if (seenSub.has(s.id))
373
- return false;
374
- const sParts = s.id.toLowerCase().split('#');
375
- return qVariants.some((variant) => {
376
- if (sParts.length <= variant.length)
377
- return false;
378
- let qi = 0;
379
- for (const sp of sParts) {
380
- if (sp === variant[qi])
381
- qi++;
382
- if (qi === variant.length)
383
- return true;
384
- }
385
- return false;
386
- });
387
- })
388
- .map((s) => {
389
- const skipped = s.id.split('#').length - qParts.length;
390
- return {
391
- section: s,
392
- reason: `path match, ${skipped} intermediate ${skipped === 1 ? 'section' : 'sections'} skipped`,
393
- };
394
- })
395
- : [];
396
- // Tier 3: fuzzy match by edit distance on each segment tail and full id
397
- const seenAll = new Set([
398
- ...seenSub,
399
- ...subsequence.map((m) => m.section.id),
400
- ]);
401
- const fuzzy = [];
402
- // For full-path queries, extract the file and heading parts so we can
403
- // fuzzy-match only the heading portion when the file part matches exactly.
404
- // This prevents the shared file prefix from inflating similarity scores
405
- // (e.g. "cli#locat" would otherwise fuzzy-match "cli#prompt").
406
- const qHashIdx = normalized.indexOf('#');
407
- const qFile = qHashIdx === -1 ? null : normalized.slice(0, qHashIdx).toLowerCase();
408
- const qHeading = qHashIdx === -1 ? null : normalized.slice(qHashIdx + 1).toLowerCase();
409
- for (const s of flat) {
410
- if (seenAll.has(s.id))
411
- continue;
412
- const candidates = [s.id, ...tailSegments(s.id)];
413
- let best = Infinity;
414
- let bestCandidate = '';
415
- for (const c of candidates) {
416
- let d;
417
- let maxLen;
418
- const cl = c.toLowerCase();
419
- const cHashIdx = cl.indexOf('#');
420
- // When both query and candidate have # and their file parts match,
421
- // compare only the heading portions to avoid file-prefix inflation
422
- if (qFile && qHeading && cHashIdx !== -1) {
423
- const cFile = cl.slice(0, cHashIdx);
424
- const cHeading = cl.slice(cHashIdx + 1);
425
- if (cFile === qFile) {
426
- d = levenshtein(cHeading, qHeading);
427
- maxLen = Math.max(cHeading.length, qHeading.length);
428
- }
429
- else {
430
- d = levenshtein(cl, q);
431
- maxLen = Math.max(c.length, q.length);
432
- }
433
- }
434
- else {
435
- d = levenshtein(cl, q);
436
- maxLen = Math.max(c.length, q.length);
437
- }
438
- if (maxLen > 0 && d / maxLen <= MAX_DISTANCE_RATIO && d < best) {
439
- best = d;
440
- bestCandidate = c;
441
- }
442
- }
443
- if (best < Infinity) {
444
- fuzzy.push({ section: s, distance: best, matched: bestCandidate });
445
- }
446
- }
447
- fuzzy.sort((a, b) => a.distance - b.distance);
448
- const fuzzyMatches = fuzzy.map((f) => ({
449
- section: f.section,
450
- reason: f.matched.toLowerCase() === f.section.id.toLowerCase()
451
- ? `fuzzy match, distance ${f.distance}`
452
- : `fuzzy match on "${f.matched}", distance ${f.distance}`,
453
- }));
454
- // Sort results: shallower depth first, then fewer path segments
455
- const sortKey = (s) => {
456
- const pathDepth = (s.file.match(/\//g) || []).length;
457
- return s.depth * 100 + pathDepth;
458
- };
459
- const sortedStems = [...stemMatches].sort((a, b) => sortKey(a.section) - sortKey(b.section));
460
- return [
461
- ...exactMatches,
462
- ...sortedStems,
463
- ...subsection,
464
- ...subsequence,
465
- ...fuzzyMatches,
466
- ];
467
- }
@@ -1,14 +0,0 @@
1
- import { type LatFrontmatter, type MdLink, type Ref, type Section } from './lattice-model.js';
2
- import type { Root } from 'mdast';
3
- import type { Profiler } from './profiler.js';
4
- export { buildFileIndex, buildSectionSlugIndex, findSections, flattenSections, resolveRef, } from './lattice-model.js';
5
- export type { LatFrontmatter, MdLink, Ref, ResolveResult, Section, SectionMatch, SectionSlugIndex, } from './lattice-model.js';
6
- export { findLatticeDir, findProjectRoot, listLatticeFiles, } from './project-discovery.js';
7
- export { LAT_FIELDS } from './fork/frontmatter-fields.js';
8
- export type { ForkFrontmatter, FrontmatterProblem, } from './fork/frontmatter-fields.js';
9
- export declare function parseFrontmatter(content: string): LatFrontmatter;
10
- export declare function parseSections(filePath: string, content: string, projectRoot?: string, tree?: Root): Section[];
11
- export declare function loadAllSections(latticeDir: string, projectRoot?: string, profile?: Profiler): Promise<Section[]>;
12
- export declare function extractRefs(filePath: string, content: string, projectRoot?: string, tree?: Root, sections?: Section[]): Ref[];
13
- /** Extract link destinations and undefined reference syntax. */
14
- export declare function extractLinks(content: string, tree?: Root): MdLink[];