@plebeiantech/lat.md 0.12.3-fork.15 → 0.12.3-fork.17
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.
- package/README.md +1 -1
- package/dist/src/cli/checklist-menu.d.ts +2 -1
- package/dist/src/cli/checklist-menu.js +3 -2
- package/dist/src/cli/index.js +59 -16
- package/dist/src/cli/init-preferences.d.ts +4 -0
- package/dist/src/cli/init-preferences.js +35 -0
- package/dist/src/cli/init.js +10 -5
- package/dist/src/cli/reindex.js +4 -8
- package/dist/src/cli/search.d.ts +3 -2
- package/dist/src/cli/search.js +71 -66
- package/dist/src/cli/ui-build-server.d.ts +12 -0
- package/dist/src/cli/ui-build-server.js +26 -0
- package/dist/src/cli/ui-build.js +1 -1
- package/dist/src/cli/ui.js +1 -1
- package/dist/src/format.d.ts +5 -0
- package/dist/src/format.js +32 -4
- package/dist/src/lattice-model.d.ts +8 -1
- package/dist/src/lattice-model.js +4 -1
- package/dist/src/lattice.d.ts +2 -2
- package/dist/src/lattice.js +17 -34
- package/dist/src/markdown-analysis.d.ts +11 -0
- package/dist/src/markdown-analysis.js +31 -1
- package/dist/src/mcp/server.js +6 -6
- package/dist/src/parser-cache.d.ts +1 -1
- package/dist/src/parser-cache.js +1 -1
- package/dist/src/search/cache.d.ts +3 -0
- package/dist/src/search/cache.js +71 -0
- package/dist/src/search/chunks.d.ts +23 -0
- package/dist/src/search/chunks.js +197 -0
- package/dist/src/search/db.d.ts +27 -16
- package/dist/src/search/db.js +108 -38
- package/dist/src/search/index.d.ts +5 -2
- package/dist/src/search/index.js +173 -69
- package/dist/src/search/lexical.d.ts +6 -0
- package/dist/src/search/lexical.js +36 -0
- package/dist/src/search/lock.d.ts +2 -0
- package/dist/src/search/lock.js +23 -0
- package/dist/src/search/query.d.ts +6 -3
- package/dist/src/search/query.js +45 -26
- package/dist/src/search/search.d.ts +13 -8
- package/dist/src/search/search.js +155 -23
- package/dist/src/search/types.d.ts +31 -0
- package/dist/src/search/types.js +1 -0
- package/dist/src/source-formats.d.ts +1 -1
- package/dist/src/source-formats.js +4 -0
- package/dist/src/source-parser.js +397 -1
- package/dist/src/view/client/assets/GraphView-BWlRdQM4.js +456 -0
- package/dist/src/view/client/assets/{MarkdownEditor-B7quqPb8.js → MarkdownEditor-MW-Ck1O0.js} +8 -8
- package/dist/src/view/client/assets/_commonjsHelpers-CqkleIqs.js +1 -0
- package/dist/src/view/client/assets/abnfDiagram-VCTEODGH-Bn3RshMV.js +1 -0
- package/dist/src/view/client/assets/{arc-BnwR8Bu7.js → arc-En9tdFQF.js} +1 -1
- package/dist/src/view/client/assets/architectureDiagram-5GKGNRK7-B-9J1VAM.js +36 -0
- package/dist/src/view/client/assets/{blockDiagram-I7D4REHJ-DETl8e_H.js → blockDiagram-I7D4REHJ-QlznwalP.js} +2 -2
- package/dist/src/view/client/assets/{c4Diagram-7LVT6UL2-DJRAT1Ve.js → c4Diagram-7LVT6UL2-Bp2y8B4_.js} +2 -2
- package/dist/src/view/client/assets/channel-uy0fFvcW.js +1 -0
- package/dist/src/view/client/assets/{chunk-2Q5K7J3B-C0TmOC1Q.js → chunk-2Q5K7J3B-CnBKL-lm.js} +1 -1
- package/dist/src/view/client/assets/{chunk-5VM5RSS4-dfBOrYE6.js → chunk-5VM5RSS4-LLYMi_n6.js} +1 -1
- package/dist/src/view/client/assets/{chunk-F27PBJKO-DAnks99K.js → chunk-F27PBJKO-VIeO7VF0.js} +1 -1
- package/dist/src/view/client/assets/{chunk-IMKFNOWR-CPjB07if.js → chunk-IMKFNOWR-D5i67DVD.js} +1 -1
- package/dist/src/view/client/assets/{chunk-JWPE2WC7-VvU_gZI6.js → chunk-JWPE2WC7-D4qAtoL3.js} +1 -1
- package/dist/src/view/client/assets/{chunk-POPQ4Y6H-DDUbVF55.js → chunk-POPQ4Y6H-BMzOyiW_.js} +1 -1
- package/dist/src/view/client/assets/{chunk-SVP7TREG-BHROkr6D.js → chunk-SVP7TREG-DWy5nbfp.js} +1 -1
- package/dist/src/view/client/assets/{chunk-TICWLB2K-95PzGIOg.js → chunk-TICWLB2K-CVqCANFB.js} +1 -1
- package/dist/src/view/client/assets/{chunk-XXDRQBXY-MdmuLhIs.js → chunk-XXDRQBXY-Bp23h6Tf.js} +1 -1
- package/dist/src/view/client/assets/classDiagram-ZZMXUADV-DbSEqjA5.js +1 -0
- package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-DbSEqjA5.js +1 -0
- package/dist/src/view/client/assets/cose-bilkent-JH36ORCC-BCw7fCC2.js +1 -0
- package/dist/src/view/client/assets/{cynefin-OW5HDTMX-CeA8IKWo.js → cynefin-OW5HDTMX--lYN4N9m.js} +1 -1
- package/dist/src/view/client/assets/cynefinDiagram-5FMLGOSQ-9sK5EV7v.js +62 -0
- package/dist/src/view/client/assets/{dagre-GXQ25YYZ-DLQwTa3H.js → dagre-GXQ25YYZ-CoMdEFCq.js} +2 -2
- package/dist/src/view/client/assets/{diagram-S7CK7UJ4-kbKdIyXG.js → diagram-S7CK7UJ4-D13dyWdd.js} +2 -2
- package/dist/src/view/client/assets/diagram-UQ7AKVKN-PdwLPPPA.js +41 -0
- package/dist/src/view/client/assets/{diagram-VSXAHHWV-EXJTU8qq.js → diagram-VSXAHHWV-CtWWa_A7.js} +2 -2
- package/dist/src/view/client/assets/diagram-VX7I27RA-BeCYcSU-.js +24 -0
- package/dist/src/view/client/assets/diagram-Z3DM3KII-DiWabOBM.js +24 -0
- package/dist/src/view/client/assets/ebnfDiagram-PWID7BFC-CZQ3MD_K.js +1 -0
- package/dist/src/view/client/assets/{erDiagram-RLTQ6QDP-C925J5A3.js → erDiagram-RLTQ6QDP-B1AhXIrP.js} +2 -2
- package/dist/src/view/client/assets/{flowDiagram-HODETNUW-DzoCa-Nq.js → flowDiagram-HODETNUW-Bhr0JM6P.js} +1 -1
- package/dist/src/view/client/assets/ganttDiagram-EL5Y4UJY-CbP8iAkg.js +292 -0
- package/dist/src/view/client/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/src/view/client/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/src/view/client/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/src/view/client/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/src/view/client/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/dist/src/view/client/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/dist/src/view/client/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/dist/src/view/client/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/dist/src/view/client/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/dist/src/view/client/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/dist/src/view/client/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/src/view/client/assets/gitGraphDiagram-WWUBYQGX-B9uuGdLF.js +106 -0
- package/dist/src/view/client/assets/index-DG0Ez15D.css +1 -0
- package/dist/src/view/client/assets/index-nBmbYfun.js +13 -0
- package/dist/src/view/client/assets/infoDiagram-27XIBGKW-BrfwQ_Cq.js +2 -0
- package/dist/src/view/client/assets/{ishikawaDiagram-5VMMS53U-CW4mto2d.js → ishikawaDiagram-5VMMS53U-CHtJ36re.js} +2 -2
- package/dist/src/view/client/assets/{journeyDiagram-3NMN7TZE-8R4eacFW.js → journeyDiagram-3NMN7TZE-BChcgX1s.js} +2 -2
- package/dist/src/view/client/assets/{kanban-definition-UXKFOSKX-Cy9ioXEJ.js → kanban-definition-UXKFOSKX-BnwFsKiB.js} +6 -6
- package/dist/src/view/client/assets/{layout-C0MFuhth.js → layout-Dnd0pLdb.js} +1 -1
- package/dist/src/view/client/assets/{linear-Dv3N4dQR.js → linear-CFhW2ozn.js} +1 -1
- package/dist/src/view/client/assets/{maplibre-gl-DnqZgfUS.js → maplibre-gl-CDX6Zuow.js} +1 -1
- package/dist/src/view/client/assets/{mermaid.core-bds9A7e2.js → mermaid.core--OP6lXtW.js} +11 -11
- package/dist/src/view/client/assets/{mindmap-definition-YA3MSWOX-DjmGW355.js → mindmap-definition-YA3MSWOX-D51y2zbm.js} +2 -2
- package/dist/src/view/client/assets/pegDiagram-XKGWAZYB--zyQJDvI.js +1 -0
- package/dist/src/view/client/assets/{pieDiagram-E7YTZNPT-CCH2gGR-.js → pieDiagram-E7YTZNPT-Cj8mvpJy.js} +2 -2
- package/dist/src/view/client/assets/{quadrantDiagram-AXDQQJYC-CjocPJFz.js → quadrantDiagram-AXDQQJYC-qvhJrnBf.js} +4 -4
- package/dist/src/view/client/assets/railroadDiagram-O6MQD6OU-CtWvxpSo.js +1 -0
- package/dist/src/view/client/assets/{requirementDiagram-BXWQKSXE-Ct6AoXRr.js → requirementDiagram-BXWQKSXE-CBxen_KG.js} +2 -2
- package/dist/src/view/client/assets/{sankeyDiagram-P5KCCOFB-CIpgGN_w.js → sankeyDiagram-P5KCCOFB-B_fUkmfc.js} +2 -2
- package/dist/src/view/client/assets/{sequenceDiagram-WJ2MYXX4-DrmLFj6w.js → sequenceDiagram-WJ2MYXX4-Dclv4p68.js} +2 -2
- package/dist/src/view/client/assets/sizeCapture-INFHLROL-GfG0sMTZ.js +1 -0
- package/dist/src/view/client/assets/stateDiagram-D77RDMKH-YQWPnzxU.js +1 -0
- package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-gUj6Hzgl.js +1 -0
- package/dist/src/view/client/assets/swimlanes-42K2YHIH-5yiCveVS.js +1 -0
- package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-DR7JhrxL.js +8 -0
- package/dist/src/view/client/assets/{timeline-definition-24CTP7MA-BExPu6X5.js → timeline-definition-24CTP7MA-BvAbB5Zs.js} +2 -2
- package/dist/src/view/client/assets/{vennDiagram-4TSXK5OY-PC1MU3si.js → vennDiagram-4TSXK5OY-guC-r60n.js} +2 -2
- package/dist/src/view/client/assets/{wardleyDiagram-VM6X3IG4-DNWqU3fa.js → wardleyDiagram-VM6X3IG4-C0yXcU2x.js} +2 -2
- package/dist/src/view/client/assets/{xychartDiagram-S5SC5T6Z-E1uA28_D.js → xychartDiagram-S5SC5T6Z-RPmkPNfK.js} +2 -2
- package/dist/src/view/client/index.html +2 -2
- package/dist/src/view/document-route.d.ts +2 -0
- package/dist/src/view/document-route.js +40 -7
- package/dist/src/view/graph.d.ts +2 -2
- package/dist/src/view/graph.js +7 -3
- package/dist/src/view/highlight.js +207 -203
- package/dist/src/view/markdown.js +19 -0
- package/dist/src/view/preindexed-search.d.ts +15 -0
- package/dist/src/view/preindexed-search.js +25 -0
- package/dist/src/view/protocol.d.ts +10 -1
- package/dist/src/view/references.js +6 -7
- package/dist/src/view/repository.js +10 -2
- package/dist/src/view/search-result.d.ts +4 -0
- package/dist/src/view/search-result.js +25 -0
- package/dist/src/view/search.d.ts +7 -2
- package/dist/src/view/search.js +23 -30
- package/dist/src/view/server-build.d.ts +21 -0
- package/dist/src/view/server-build.js +145 -0
- package/dist/src/view/server-deployment.d.ts +32 -0
- package/dist/src/view/server-deployment.js +134 -0
- package/dist/src/view/server-index-worker.d.ts +6 -0
- package/dist/src/view/server-index-worker.js +14 -0
- package/dist/src/view/server.js +25 -7
- package/dist/src/view/source-target.d.ts +2 -0
- package/dist/src/view/source-target.js +24 -0
- package/dist/src/view/static-build.d.ts +16 -1
- package/dist/src/view/static-build.js +187 -89
- package/dist/src/view/static-protocol.d.ts +7 -0
- package/dist/src/view/static-protocol.js +5 -0
- package/dist/src/view/store.js +17 -7
- package/dist/src/view/vercel-build.d.ts +24 -0
- package/dist/src/view/vercel-build.js +140 -0
- package/dist/src/view/vercel-server-build.d.ts +10 -0
- package/dist/src/view/vercel-server-build.js +61 -0
- package/dist/vendor/lat.md-server/index.d.ts +1 -1
- package/dist/vendor/lat.md-server/index.js +1 -1
- package/package.json +20 -5
- package/templates/AGENTS.md +1 -1
- package/templates/cursor-rules.md +1 -1
- package/templates/skill/SKILL.md +1 -1
- package/dist/src/view/client/assets/abnfDiagram-VCTEODGH-V7-T6qsl.js +0 -1
- package/dist/src/view/client/assets/architectureDiagram-5GKGNRK7-u97vDna9.js +0 -36
- package/dist/src/view/client/assets/channel-BwQbnWCN.js +0 -1
- package/dist/src/view/client/assets/classDiagram-ZZMXUADV-HvQKkIS-.js +0 -1
- package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-HvQKkIS-.js +0 -1
- package/dist/src/view/client/assets/cose-bilkent-JH36ORCC-CauUf_rN.js +0 -1
- package/dist/src/view/client/assets/cynefinDiagram-5FMLGOSQ-L1rtcT8l.js +0 -62
- package/dist/src/view/client/assets/diagram-UQ7AKVKN-kmzp92RC.js +0 -41
- package/dist/src/view/client/assets/diagram-VX7I27RA-DAiIyMCG.js +0 -24
- package/dist/src/view/client/assets/diagram-Z3DM3KII-G9wz50ZV.js +0 -24
- package/dist/src/view/client/assets/ebnfDiagram-PWID7BFC-D2tVRCNA.js +0 -1
- package/dist/src/view/client/assets/ganttDiagram-EL5Y4UJY-D2ebbg4p.js +0 -292
- package/dist/src/view/client/assets/gitGraphDiagram-WWUBYQGX-B4QratYy.js +0 -106
- package/dist/src/view/client/assets/index-D0ajr_Ja.css +0 -1
- package/dist/src/view/client/assets/index-KZcnpXJH.js +0 -321
- package/dist/src/view/client/assets/infoDiagram-27XIBGKW-DL4mtHkt.js +0 -2
- package/dist/src/view/client/assets/pegDiagram-XKGWAZYB-BWsZp0ab.js +0 -1
- package/dist/src/view/client/assets/railroadDiagram-O6MQD6OU-BjLeb2KF.js +0 -1
- package/dist/src/view/client/assets/sizeCapture-INFHLROL-BdFMe2Lr.js +0 -1
- package/dist/src/view/client/assets/stateDiagram-D77RDMKH-Zd9F1goW.js +0 -1
- package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-ByIKCftl.js +0 -1
- package/dist/src/view/client/assets/swimlanes-42K2YHIH-a7VFwi00.js +0 -1
- package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-CzxaUHB-.js +0 -8
- /package/dist/src/view/client/assets/{logo-small-RR_KBOAT.svg → logo-RR_KBOAT.svg} +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SearchEvidence, SearchDiagnostics } from './search/types.js';
|
|
1
2
|
import type { ForkFrontmatter } from './fork/frontmatter-fields.js';
|
|
2
3
|
export type Section = {
|
|
3
4
|
id: string;
|
|
@@ -72,6 +73,12 @@ export declare function resolveRef(target: string, sectionIds: Set<string>, file
|
|
|
72
73
|
export type SectionMatch = {
|
|
73
74
|
section: Section;
|
|
74
75
|
reason: string;
|
|
75
|
-
|
|
76
|
+
rankScore?: number;
|
|
77
|
+
semanticSimilarity?: number;
|
|
78
|
+
lexicalScore?: number;
|
|
79
|
+
semanticRank?: number;
|
|
80
|
+
lexicalRank?: number;
|
|
81
|
+
evidence?: SearchEvidence[];
|
|
82
|
+
diagnostics?: SearchDiagnostics;
|
|
76
83
|
};
|
|
77
84
|
export declare function findSections(sections: Section[], query: string): SectionMatch[];
|
|
@@ -94,8 +94,11 @@ export function buildSectionSlugIndex(sections) {
|
|
|
94
94
|
const levels = [
|
|
95
95
|
...stack,
|
|
96
96
|
{
|
|
97
|
+
id: section.id,
|
|
97
98
|
depth: section.depth,
|
|
98
|
-
literal: section.
|
|
99
|
+
literal: section.id
|
|
100
|
+
.slice((stack.at(-1)?.id ?? section.file).length + 1)
|
|
101
|
+
.toLowerCase(),
|
|
99
102
|
slug: headingSlug.toLowerCase(),
|
|
100
103
|
},
|
|
101
104
|
];
|
package/dist/src/lattice.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type LatFrontmatter, type MdLink, type Ref, type Section } from './lattice-model.js';
|
|
2
2
|
import type { Root } from 'mdast';
|
|
3
3
|
import type { Profiler } from './profiler.js';
|
|
4
4
|
export { buildFileIndex, buildSectionSlugIndex, findSections, flattenSections, resolveRef, } from './lattice-model.js';
|
|
@@ -9,6 +9,6 @@ export type { ForkFrontmatter, FrontmatterProblem, } from './fork/frontmatter-fi
|
|
|
9
9
|
export declare function parseFrontmatter(content: string): LatFrontmatter;
|
|
10
10
|
export declare function parseSections(filePath: string, content: string, projectRoot?: string, tree?: Root): Section[];
|
|
11
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): Ref[];
|
|
12
|
+
export declare function extractRefs(filePath: string, content: string, projectRoot?: string, tree?: Root, sections?: Section[]): Ref[];
|
|
13
13
|
/** Extract link destinations and undefined reference syntax. */
|
|
14
14
|
export declare function extractLinks(content: string, tree?: Root): MdLink[];
|
package/dist/src/lattice.js
CHANGED
|
@@ -6,6 +6,7 @@ import { parse } from './parser.js';
|
|
|
6
6
|
import { toPosix } from './path.js';
|
|
7
7
|
import { visit } from 'unist-util-visit';
|
|
8
8
|
import { listLatticeFiles } from './project-discovery.js';
|
|
9
|
+
import { flattenSections, } from './lattice-model.js';
|
|
9
10
|
import { LAT_FIELDS, } from './fork/frontmatter-fields.js';
|
|
10
11
|
export { buildFileIndex, buildSectionSlugIndex, findSections, flattenSections, resolveRef, } from './lattice-model.js';
|
|
11
12
|
export { findLatticeDir, findProjectRoot, listLatticeFiles, } from './project-discovery.js';
|
|
@@ -56,14 +57,8 @@ export function parseFrontmatter(content) {
|
|
|
56
57
|
result.problems = problems;
|
|
57
58
|
return result;
|
|
58
59
|
}
|
|
59
|
-
function headingText(node) {
|
|
60
|
-
return node.children
|
|
61
|
-
.filter((c) => c.type === 'text')
|
|
62
|
-
.map((c) => c.value)
|
|
63
|
-
.join('');
|
|
64
|
-
}
|
|
65
60
|
/** Extract the rendered text GitHub uses as input to its heading slugger. */
|
|
66
|
-
function
|
|
61
|
+
function headingText(node) {
|
|
67
62
|
if (!node || typeof node !== 'object')
|
|
68
63
|
return '';
|
|
69
64
|
const value = node;
|
|
@@ -77,7 +72,7 @@ function githubHeadingText(node) {
|
|
|
77
72
|
return value.data?.alias ?? value.value ?? '';
|
|
78
73
|
}
|
|
79
74
|
if (value.children) {
|
|
80
|
-
return value.children.map(
|
|
75
|
+
return value.children.map(headingText).join('');
|
|
81
76
|
}
|
|
82
77
|
return '';
|
|
83
78
|
}
|
|
@@ -112,6 +107,8 @@ export function parseSections(filePath, content, projectRoot, tree = parse(conte
|
|
|
112
107
|
const stack = [];
|
|
113
108
|
const flat = [];
|
|
114
109
|
const slugger = new GithubSlugger();
|
|
110
|
+
const usedIds = new Set();
|
|
111
|
+
const nextSuffix = new Map();
|
|
115
112
|
visit(tree, 'heading', (node) => {
|
|
116
113
|
const heading = headingText(node);
|
|
117
114
|
const depth = node.depth;
|
|
@@ -121,7 +118,14 @@ export function parseSections(filePath, content, projectRoot, tree = parse(conte
|
|
|
121
118
|
stack.pop();
|
|
122
119
|
}
|
|
123
120
|
const parent = stack.length > 0 ? stack[stack.length - 1] : null;
|
|
124
|
-
const
|
|
121
|
+
const baseId = parent ? `${parent.id}#${heading}` : `${file}#${heading}`;
|
|
122
|
+
let id = baseId;
|
|
123
|
+
const key = baseId.toLowerCase();
|
|
124
|
+
let suffix = nextSuffix.get(key) ?? 1;
|
|
125
|
+
while (usedIds.has(id.toLowerCase()))
|
|
126
|
+
id = `${baseId}-${suffix++}`;
|
|
127
|
+
nextSuffix.set(key, suffix);
|
|
128
|
+
usedIds.add(id.toLowerCase());
|
|
125
129
|
const section = {
|
|
126
130
|
id,
|
|
127
131
|
heading,
|
|
@@ -132,7 +136,7 @@ export function parseSections(filePath, content, projectRoot, tree = parse(conte
|
|
|
132
136
|
startLine,
|
|
133
137
|
endLine: 0,
|
|
134
138
|
firstParagraph: '',
|
|
135
|
-
githubSlug: slugger.slug(
|
|
139
|
+
githubSlug: slugger.slug(headingText(node)),
|
|
136
140
|
};
|
|
137
141
|
if (parent) {
|
|
138
142
|
parent.children.push(section);
|
|
@@ -190,34 +194,13 @@ export async function loadAllSections(latticeDir, projectRoot = dirname(latticeD
|
|
|
190
194
|
}
|
|
191
195
|
return all;
|
|
192
196
|
}
|
|
193
|
-
export function extractRefs(filePath, content, projectRoot, tree = parse(content)) {
|
|
197
|
+
export function extractRefs(filePath, content, projectRoot, tree = parse(content), sections = parseSections(filePath, content, projectRoot, tree)) {
|
|
194
198
|
const file = projectRoot
|
|
195
199
|
? toPosix(relative(projectRoot, filePath)).replace(/\.md$/, '')
|
|
196
200
|
: basename(filePath, '.md');
|
|
197
201
|
const refs = [];
|
|
198
|
-
//
|
|
199
|
-
const flat =
|
|
200
|
-
visit(tree, 'heading', (node) => {
|
|
201
|
-
flat.push({
|
|
202
|
-
id: '', // filled below
|
|
203
|
-
startLine: node.position.start.line,
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
// Re-derive ids using the same logic as parseSections
|
|
207
|
-
const stack = [];
|
|
208
|
-
let idx = 0;
|
|
209
|
-
visit(tree, 'heading', (node) => {
|
|
210
|
-
const heading = headingText(node);
|
|
211
|
-
const depth = node.depth;
|
|
212
|
-
while (stack.length > 0 && stack[stack.length - 1].depth >= depth) {
|
|
213
|
-
stack.pop();
|
|
214
|
-
}
|
|
215
|
-
const parent = stack.length > 0 ? stack[stack.length - 1] : null;
|
|
216
|
-
const id = parent ? `${parent.id}#${heading}` : `${file}#${heading}`;
|
|
217
|
-
flat[idx].id = id;
|
|
218
|
-
stack.push({ id, depth });
|
|
219
|
-
idx++;
|
|
220
|
-
});
|
|
202
|
+
// Reuse canonical identities, including duplicate suffixes and formatted text.
|
|
203
|
+
const flat = flattenSections(sections);
|
|
221
204
|
visit(tree, 'wikiLink', (node) => {
|
|
222
205
|
const line = node.position.start.line;
|
|
223
206
|
// Find enclosing section: last heading with startLine <= link line
|
|
@@ -6,6 +6,16 @@ export type MarkdownParagraph = {
|
|
|
6
6
|
endLine: number;
|
|
7
7
|
text: string;
|
|
8
8
|
};
|
|
9
|
+
/** Source-positioned block facts; never retains the parser AST. */
|
|
10
|
+
export type MarkdownBlock = {
|
|
11
|
+
type: string;
|
|
12
|
+
start: number;
|
|
13
|
+
end: number;
|
|
14
|
+
startLine: number;
|
|
15
|
+
endLine: number;
|
|
16
|
+
language?: string;
|
|
17
|
+
children: MarkdownBlock[];
|
|
18
|
+
};
|
|
9
19
|
export type MarkdownDestinationLink = {
|
|
10
20
|
kind: 'image' | 'link';
|
|
11
21
|
line: number;
|
|
@@ -37,6 +47,7 @@ export type MarkdownFileAnalysis = {
|
|
|
37
47
|
headingTitles: string[];
|
|
38
48
|
wikiRefs: Ref[];
|
|
39
49
|
paragraphs: MarkdownParagraph[];
|
|
50
|
+
blocks: MarkdownBlock[];
|
|
40
51
|
markdownLinks: MarkdownDestinationLink[];
|
|
41
52
|
validationLinks: MdLink[];
|
|
42
53
|
indexEntries: string[];
|
|
@@ -5,6 +5,35 @@ import { extractLinks, extractRefs, parseFrontmatter, parseSections, } from './l
|
|
|
5
5
|
import { parse } from './parser.js';
|
|
6
6
|
import { toPosix } from './path.js';
|
|
7
7
|
import { analyzeLocalMarkdownDiagnostics, } from './markdown-validation.js';
|
|
8
|
+
function extractBlocks(tree) {
|
|
9
|
+
const block = (node) => {
|
|
10
|
+
if (node.position?.start.offset === undefined ||
|
|
11
|
+
node.position?.end.offset === undefined)
|
|
12
|
+
return;
|
|
13
|
+
return {
|
|
14
|
+
type: node.type,
|
|
15
|
+
start: node.position.start.offset,
|
|
16
|
+
end: node.position.end.offset,
|
|
17
|
+
startLine: node.position.start.line,
|
|
18
|
+
endLine: node.position.end.line,
|
|
19
|
+
...('lang' in node && node.lang ? { language: node.lang } : {}),
|
|
20
|
+
children: [
|
|
21
|
+
'list',
|
|
22
|
+
'listItem',
|
|
23
|
+
'blockquote',
|
|
24
|
+
'table',
|
|
25
|
+
'tableRow',
|
|
26
|
+
].includes(node.type)
|
|
27
|
+
? ('children' in node ? node.children : [])
|
|
28
|
+
.map(block)
|
|
29
|
+
.filter((item) => !!item)
|
|
30
|
+
: [],
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
return tree.children
|
|
34
|
+
.map(block)
|
|
35
|
+
.filter((item) => !!item);
|
|
36
|
+
}
|
|
8
37
|
function elapsed(work) {
|
|
9
38
|
const start = performance.now();
|
|
10
39
|
return [work(), performance.now() - start];
|
|
@@ -96,7 +125,7 @@ function extractIndexEntries(content) {
|
|
|
96
125
|
export function analyzeMarkdownFile(absolutePath, content, latDir, projectRoot) {
|
|
97
126
|
const [tree, parseMs] = elapsed(() => parse(content));
|
|
98
127
|
const [sections, sectionsMs] = elapsed(() => parseSections(absolutePath, content, projectRoot, tree));
|
|
99
|
-
const [wikiRefs, refsMs] = elapsed(() => extractRefs(absolutePath, content, projectRoot, tree));
|
|
128
|
+
const [wikiRefs, refsMs] = elapsed(() => extractRefs(absolutePath, content, projectRoot, tree, sections));
|
|
100
129
|
const [validationLinks, linksMs] = elapsed(() => extractLinks(content, tree));
|
|
101
130
|
const [[paragraphs, markdownLinks, headingTitles], paragraphsMs] = elapsed(() => [
|
|
102
131
|
extractParagraphs(content, tree),
|
|
@@ -116,6 +145,7 @@ export function analyzeMarkdownFile(absolutePath, content, latDir, projectRoot)
|
|
|
116
145
|
headingTitles,
|
|
117
146
|
wikiRefs,
|
|
118
147
|
paragraphs,
|
|
148
|
+
blocks: extractBlocks(tree),
|
|
119
149
|
markdownLinks,
|
|
120
150
|
validationLinks,
|
|
121
151
|
indexEntries,
|
package/dist/src/mcp/server.js
CHANGED
|
@@ -7,7 +7,7 @@ import { plainStyler } from '../context.js';
|
|
|
7
7
|
import { locateCommand } from '../cli/locate.js';
|
|
8
8
|
import { sectionCommand } from '../cli/section.js';
|
|
9
9
|
import { searchCommand } from '../cli/search.js';
|
|
10
|
-
import { DEFAULT_SEARCH_LIMIT,
|
|
10
|
+
import { DEFAULT_SEARCH_LIMIT, DEFAULT_MIN_SIMILARITY, } from '../search/search.js';
|
|
11
11
|
import { expandCommand } from '../cli/expand.js';
|
|
12
12
|
import { checkAllCommand } from '../cli/check.js';
|
|
13
13
|
import { refsCommand } from '../cli/refs.js';
|
|
@@ -40,21 +40,21 @@ export async function startMcpServer() {
|
|
|
40
40
|
.string()
|
|
41
41
|
.describe('Local section id or exact handle:path#fragment target'),
|
|
42
42
|
}, async ({ query }) => toMcp(await sectionCommand(requestContext(), query)));
|
|
43
|
-
server.tool('lat_search', '
|
|
43
|
+
server.tool('lat_search', 'Hybrid lexical and semantic search across lat.md sections', {
|
|
44
44
|
query: z.string().describe('Search query in natural language'),
|
|
45
45
|
limit: z
|
|
46
46
|
.number()
|
|
47
47
|
.optional()
|
|
48
48
|
.default(DEFAULT_SEARCH_LIMIT)
|
|
49
49
|
.describe(`Max results (default ${DEFAULT_SEARCH_LIMIT})`),
|
|
50
|
-
|
|
50
|
+
minSimilarity: z
|
|
51
51
|
.number()
|
|
52
52
|
.min(0)
|
|
53
53
|
.max(1)
|
|
54
54
|
.optional()
|
|
55
|
-
.default(
|
|
56
|
-
.describe('Minimum cosine similarity score (default 0.
|
|
57
|
-
}, async ({ query, limit,
|
|
55
|
+
.default(DEFAULT_MIN_SIMILARITY)
|
|
56
|
+
.describe('Minimum cosine similarity score (default 0.20)'),
|
|
57
|
+
}, async ({ query, limit, minSimilarity }) => toMcp(await searchCommand(requestContext(), query, { limit, minSimilarity })));
|
|
58
58
|
server.tool('lat_expand', 'Expand [[refs]] in text to resolved lat.md section paths with context', { text: z.string().describe('Text containing [[refs]] to expand') }, async ({ text: input }) => toMcp(await expandCommand(requestContext(), input)));
|
|
59
59
|
server.tool('lat_check', 'Run full lat.md validation: links, code references, indexes, and section structure', {}, async () => toMcp(await checkAllCommand(requestContext())));
|
|
60
60
|
server.tool('lat_refs', 'Find sections that reference a given section via wiki links or @lat code comments', {
|
package/dist/src/parser-cache.js
CHANGED
|
@@ -3,7 +3,7 @@ import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
|
3
3
|
import { basename, dirname, extname, join, relative } from 'node:path';
|
|
4
4
|
import { toPosix } from './path.js';
|
|
5
5
|
/** Version of persistent parser outputs and their shared on-disk contract. */
|
|
6
|
-
export const PARSER_CACHE_VERSION =
|
|
6
|
+
export const PARSER_CACHE_VERSION = 3;
|
|
7
7
|
/** Hash complete parser input bytes for cache invalidation. */
|
|
8
8
|
export function hashParserContent(content) {
|
|
9
9
|
return createHash('sha1').update(content).digest('hex');
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { SearchDb } from './db.js';
|
|
2
|
+
/** Build a private staging database; readers only open snapshots of search.db. */
|
|
3
|
+
export declare function writeIndex<T>(latDir: string, cacheDir: string | undefined, rebuild: boolean, work: (db: SearchDb, storedModel: string | null) => Promise<T>): Promise<T>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { copyFile, mkdir, rename, rm } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { acquireSearchLock } from './lock.js';
|
|
4
|
+
import { SearchDb, hasIndex, INDEX_FILE, ensureMeta, getStoredModel, } from './db.js';
|
|
5
|
+
/** Retry transient Windows handles without deleting the usable index first. */
|
|
6
|
+
async function withFileRetry(work) {
|
|
7
|
+
for (let attempt = 0;; attempt++) {
|
|
8
|
+
try {
|
|
9
|
+
await work();
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
const code = error.code;
|
|
14
|
+
// Windows may briefly retain a staging handle or a reader's copy handle.
|
|
15
|
+
if (process.platform !== 'win32' ||
|
|
16
|
+
attempt >= 9 ||
|
|
17
|
+
!['EBUSY', 'EPERM', 'EACCES'].includes(code ?? ''))
|
|
18
|
+
throw error;
|
|
19
|
+
await new Promise((resolve) => setTimeout(resolve, 50 * (attempt + 1)));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async function removeStaging(path) {
|
|
24
|
+
for (const suffix of ['', '-wal', '-shm', '-journal'])
|
|
25
|
+
await withFileRetry(() => rm(path + suffix, { force: true }));
|
|
26
|
+
}
|
|
27
|
+
/** Build a private staging database; readers only open snapshots of search.db. */
|
|
28
|
+
export async function writeIndex(latDir, cacheDir, rebuild, work) {
|
|
29
|
+
const dir = cacheDir ?? join(latDir, '.cache');
|
|
30
|
+
await mkdir(dir, { recursive: true });
|
|
31
|
+
const path = join(dir, '_search.db');
|
|
32
|
+
const activePath = join(dir, INDEX_FILE);
|
|
33
|
+
let db;
|
|
34
|
+
const release = await acquireSearchLock(dir);
|
|
35
|
+
try {
|
|
36
|
+
// A crashed writer may have left a partial database and journal behind.
|
|
37
|
+
await removeStaging(path);
|
|
38
|
+
const existing = hasIndex(dir);
|
|
39
|
+
if (existing && !rebuild)
|
|
40
|
+
await copyFile(activePath, path);
|
|
41
|
+
db = new SearchDb(path);
|
|
42
|
+
const model = existing && !rebuild ? await getStoredModel(db) : null;
|
|
43
|
+
await ensureMeta(db);
|
|
44
|
+
const changesBefore = (await db.execute('SELECT total_changes() AS n'))
|
|
45
|
+
.rows[0].n;
|
|
46
|
+
const result = await work(db, model);
|
|
47
|
+
const unchanged = existing &&
|
|
48
|
+
!rebuild &&
|
|
49
|
+
(await db.execute('SELECT total_changes() AS n')).rows[0].n ===
|
|
50
|
+
changesBefore;
|
|
51
|
+
await db.checkpoint();
|
|
52
|
+
await db.close();
|
|
53
|
+
db = undefined;
|
|
54
|
+
if (!unchanged)
|
|
55
|
+
await withFileRetry(() => rename(path, activePath));
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
try {
|
|
60
|
+
await db?.close();
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
try {
|
|
64
|
+
await removeStaging(path);
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
await release();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SourceSpan } from './types.js';
|
|
2
|
+
import type { Embedder } from './embedder.js';
|
|
3
|
+
import type { MarkdownFileAnalysis } from '../markdown-analysis.js';
|
|
4
|
+
import type { Section } from '../lattice-model.js';
|
|
5
|
+
export declare const CHUNK_POLICY = "owned-blocks-v1";
|
|
6
|
+
export type Passage = {
|
|
7
|
+
id: string;
|
|
8
|
+
sectionId: string;
|
|
9
|
+
ordinal: number;
|
|
10
|
+
type: string;
|
|
11
|
+
spans: SourceSpan[];
|
|
12
|
+
text: string;
|
|
13
|
+
input: string;
|
|
14
|
+
inputHash: string;
|
|
15
|
+
heading: string;
|
|
16
|
+
path: string;
|
|
17
|
+
};
|
|
18
|
+
export declare const digest: (text: string) => string;
|
|
19
|
+
export declare function embeddingFingerprint(embedder: Embedder): string;
|
|
20
|
+
/** Find a fitting Unicode-safe prefix without assuming token-count monotonicity. */
|
|
21
|
+
export declare function fittingPrefix(text: string, fits: (text: string) => boolean): number;
|
|
22
|
+
/** Every body block is owned once; headings provide context instead of copied subtrees. */
|
|
23
|
+
export declare function chunkFile(file: MarkdownFileAnalysis, sections: readonly Section[], embedder: Embedder): Passage[];
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
export const CHUNK_POLICY = 'owned-blocks-v1';
|
|
3
|
+
export const digest = (text) => createHash('sha256').update(text).digest('hex');
|
|
4
|
+
export function embeddingFingerprint(embedder) {
|
|
5
|
+
return `${CHUNK_POLICY}:${embedder.name}:${embedder.dimensions}:${embedder.maxInputTokens}:${embedder.tokenizerFingerprint}`;
|
|
6
|
+
}
|
|
7
|
+
/** Find a fitting Unicode-safe prefix without assuming token-count monotonicity. */
|
|
8
|
+
export function fittingPrefix(text, fits) {
|
|
9
|
+
if (fits(text))
|
|
10
|
+
return text.length;
|
|
11
|
+
let size = text.length;
|
|
12
|
+
while (size > 0) {
|
|
13
|
+
size = Math.floor(size / 2);
|
|
14
|
+
if (size && /[\uD800-\uDBFF]/.test(text[size - 1]))
|
|
15
|
+
size--;
|
|
16
|
+
if (size && fits(text.slice(0, size)))
|
|
17
|
+
return size;
|
|
18
|
+
}
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
/** Every body block is owned once; headings provide context instead of copied subtrees. */
|
|
22
|
+
export function chunkFile(file, sections, embedder) {
|
|
23
|
+
const local = embedder.name.startsWith('local:');
|
|
24
|
+
const target = local ? 192 : 512;
|
|
25
|
+
const contextBudget = local ? 48 : 96;
|
|
26
|
+
const source = file.content;
|
|
27
|
+
const lineStarts = [0];
|
|
28
|
+
for (let i = 0; i < source.length; i++)
|
|
29
|
+
if (source[i] === '\n')
|
|
30
|
+
lineStarts.push(i + 1);
|
|
31
|
+
const lineAt = (offset) => {
|
|
32
|
+
let lo = 0, hi = lineStarts.length;
|
|
33
|
+
while (lo + 1 < hi) {
|
|
34
|
+
const mid = (lo + hi) >>> 1;
|
|
35
|
+
if (lineStarts[mid] <= offset)
|
|
36
|
+
lo = mid;
|
|
37
|
+
else
|
|
38
|
+
hi = mid;
|
|
39
|
+
}
|
|
40
|
+
return lo + 1;
|
|
41
|
+
};
|
|
42
|
+
const own = new Map();
|
|
43
|
+
const ordered = [...sections].sort((a, b) => a.startLine - b.startLine || a.depth - b.depth);
|
|
44
|
+
let sectionIndex = 0;
|
|
45
|
+
let ancestors = [];
|
|
46
|
+
for (const block of file.blocks) {
|
|
47
|
+
if (block.type === 'heading' || block.type === 'yaml')
|
|
48
|
+
continue;
|
|
49
|
+
while (sectionIndex < ordered.length &&
|
|
50
|
+
ordered[sectionIndex].startLine <= block.startLine) {
|
|
51
|
+
const next = ordered[sectionIndex++];
|
|
52
|
+
ancestors = ancestors.filter((s) => s.endLine >= next.startLine);
|
|
53
|
+
ancestors.push(next);
|
|
54
|
+
}
|
|
55
|
+
ancestors = ancestors.filter((s) => s.endLine >= block.endLine);
|
|
56
|
+
const owner = ancestors.at(-1);
|
|
57
|
+
if (!owner)
|
|
58
|
+
throw new Error(`No section owns ${file.path}:${block.startLine}`);
|
|
59
|
+
const blocks = own.get(owner.id) ?? [];
|
|
60
|
+
blocks.push(block);
|
|
61
|
+
own.set(owner.id, blocks);
|
|
62
|
+
}
|
|
63
|
+
const result = [];
|
|
64
|
+
for (const section of sections) {
|
|
65
|
+
const headings = section.id.split('#').slice(1);
|
|
66
|
+
const rawContext = `Section: ${section.heading}\nPage: ${file.headingTitles[0] ?? file.path}\nPath: ${headings.slice(0, -1).join(' > ')}`;
|
|
67
|
+
const contextLength = fittingPrefix(rawContext, (t) => embedder.countTokens(t) <= contextBudget);
|
|
68
|
+
const context = rawContext.slice(0, contextLength);
|
|
69
|
+
const path = headings.slice(0, -1).join(' > ');
|
|
70
|
+
let ordinal = 0;
|
|
71
|
+
const inputFor = (text, extra) => {
|
|
72
|
+
const extraSize = fittingPrefix(extra, (t) => embedder.countTokens(t) <= contextBudget / 2);
|
|
73
|
+
const fullContext = [extra.slice(0, extraSize), context]
|
|
74
|
+
.filter(Boolean)
|
|
75
|
+
.join('\n');
|
|
76
|
+
const n = fittingPrefix(fullContext, (c) => embedder.countTokens(c) <= contextBudget &&
|
|
77
|
+
embedder.countTokens(`${c}\n\n${text}`) <= embedder.maxInputTokens);
|
|
78
|
+
return n ? `${fullContext.slice(0, n)}\n\n${text}` : text;
|
|
79
|
+
};
|
|
80
|
+
const fits = (text, extra = '') => embedder.countTokens(text) <= target &&
|
|
81
|
+
embedder.countTokens(inputFor(text, extra)) <= embedder.maxInputTokens;
|
|
82
|
+
const split = (block, extra = '') => {
|
|
83
|
+
const text = source.slice(block.start, block.end);
|
|
84
|
+
if (fits(text, extra))
|
|
85
|
+
return [
|
|
86
|
+
{ start: block.start, end: block.end, type: block.type, extra },
|
|
87
|
+
];
|
|
88
|
+
if (block.children.length) {
|
|
89
|
+
const pieces = [];
|
|
90
|
+
let start = block.start;
|
|
91
|
+
for (let i = 0; i < block.children.length; i++) {
|
|
92
|
+
const child = block.children[i];
|
|
93
|
+
const end = i + 1 < block.children.length
|
|
94
|
+
? block.children[i + 1].start
|
|
95
|
+
: block.end;
|
|
96
|
+
let label = extra;
|
|
97
|
+
if (block.type === 'table')
|
|
98
|
+
label = `Row: ${i}\nTable headers: ${source.slice(block.children[0].start, block.children[0].end)}\n${extra}`;
|
|
99
|
+
if (block.type === 'tableRow')
|
|
100
|
+
label = `Column: ${i + 1}\n${extra}`;
|
|
101
|
+
if (block.type === 'listItem')
|
|
102
|
+
label += `\nList item: ${text.split('\n')[0]}`;
|
|
103
|
+
pieces.push(...split({ ...child, start, end }, label));
|
|
104
|
+
start = end;
|
|
105
|
+
}
|
|
106
|
+
return pieces;
|
|
107
|
+
}
|
|
108
|
+
const pieces = [];
|
|
109
|
+
let start = block.start;
|
|
110
|
+
const label = block.type === 'code'
|
|
111
|
+
? `${extra}\nCode: ${block.language ?? ''}`
|
|
112
|
+
: extra;
|
|
113
|
+
while (start < block.end) {
|
|
114
|
+
const rest = source.slice(start, block.end);
|
|
115
|
+
let n = fittingPrefix(rest, (t) => fits(t, label));
|
|
116
|
+
if (!n)
|
|
117
|
+
throw new Error(`Cannot fit source text at ${file.path}:${lineAt(start)}`);
|
|
118
|
+
if (n < rest.length) {
|
|
119
|
+
const prefix = rest.slice(0, n);
|
|
120
|
+
const boundary = block.type === 'code'
|
|
121
|
+
? prefix.lastIndexOf('\n') + 1
|
|
122
|
+
: [...prefix.matchAll(/[.!?](?:\s+|$)/g)].at(-1)?.index;
|
|
123
|
+
const preferred = typeof boundary === 'number' && boundary > n / 2
|
|
124
|
+
? boundary
|
|
125
|
+
: prefix.search(/\s+\S*$/);
|
|
126
|
+
if (preferred > n / 2 && fits(rest.slice(0, preferred), label))
|
|
127
|
+
n = preferred;
|
|
128
|
+
}
|
|
129
|
+
pieces.push({ start, end: start + n, type: block.type, extra: label });
|
|
130
|
+
start += n;
|
|
131
|
+
}
|
|
132
|
+
return pieces;
|
|
133
|
+
};
|
|
134
|
+
const pieces = (own.get(section.id) ?? []).flatMap((block) => split(block));
|
|
135
|
+
if (!pieces.length) {
|
|
136
|
+
const start = lineStarts[section.startLine - 1] ?? 0;
|
|
137
|
+
const end = source.indexOf('\n', start);
|
|
138
|
+
pieces.push(...split({
|
|
139
|
+
type: 'heading',
|
|
140
|
+
start,
|
|
141
|
+
end: end < 0 ? source.length : end,
|
|
142
|
+
startLine: section.startLine,
|
|
143
|
+
endLine: section.startLine,
|
|
144
|
+
children: [],
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
let pending;
|
|
148
|
+
const emit = (piece) => {
|
|
149
|
+
const text = source.slice(piece.start, piece.end);
|
|
150
|
+
if (!text.trim())
|
|
151
|
+
return;
|
|
152
|
+
const input = inputFor(text, piece.extra);
|
|
153
|
+
if (embedder.countTokens(input) > embedder.maxInputTokens)
|
|
154
|
+
throw new Error('Chunk exceeds embedding limit');
|
|
155
|
+
const inputHash = digest(`${embeddingFingerprint(embedder)}\0${input}`);
|
|
156
|
+
result.push({
|
|
157
|
+
id: `${section.id}:${ordinal}`,
|
|
158
|
+
sectionId: section.id,
|
|
159
|
+
ordinal: ordinal++,
|
|
160
|
+
type: piece.type,
|
|
161
|
+
spans: [
|
|
162
|
+
{
|
|
163
|
+
start: piece.start,
|
|
164
|
+
end: piece.end,
|
|
165
|
+
startLine: lineAt(piece.start),
|
|
166
|
+
endLine: lineAt(Math.max(piece.start, piece.end - 1)),
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
text,
|
|
170
|
+
input,
|
|
171
|
+
inputHash,
|
|
172
|
+
heading: section.heading,
|
|
173
|
+
path,
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
for (const piece of pieces) {
|
|
177
|
+
if (pending &&
|
|
178
|
+
!pending.extra &&
|
|
179
|
+
!piece.extra &&
|
|
180
|
+
!source.slice(pending.end, piece.start).trim() &&
|
|
181
|
+
fits(source.slice(pending.start, piece.end)))
|
|
182
|
+
pending = {
|
|
183
|
+
...pending,
|
|
184
|
+
end: piece.end,
|
|
185
|
+
type: pending.type === piece.type ? piece.type : 'mixed',
|
|
186
|
+
};
|
|
187
|
+
else {
|
|
188
|
+
if (pending)
|
|
189
|
+
emit(pending);
|
|
190
|
+
pending = piece;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (pending)
|
|
194
|
+
emit(pending);
|
|
195
|
+
}
|
|
196
|
+
return result;
|
|
197
|
+
}
|
package/dist/src/search/db.d.ts
CHANGED
|
@@ -1,16 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
export declare
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
export declare const INDEX_FILE = "search.db";
|
|
2
|
+
export declare const CREATE_PASSAGE_FTS = "CREATE INDEX IF NOT EXISTS chunks_fts ON lexical_chunks USING fts(body,heading,path) WITH (tokenizer='whitespace',weights='body=1.0,heading=2.0,path=0.5')";
|
|
3
|
+
export declare function hasIndex(cacheDir: string): boolean;
|
|
4
|
+
/** Small SQL adapter; callers never depend on a libSQL connection. */
|
|
5
|
+
export declare class SearchDb {
|
|
6
|
+
readonly path: string;
|
|
7
|
+
private readonly snapshot;
|
|
8
|
+
private connection;
|
|
9
|
+
constructor(path: string, snapshot?: boolean);
|
|
10
|
+
private snapshotDir;
|
|
11
|
+
private get;
|
|
12
|
+
execute(statement: string | {
|
|
13
|
+
sql: string;
|
|
14
|
+
args?: any[];
|
|
15
|
+
}): Promise<{
|
|
16
|
+
rows: any[];
|
|
17
|
+
}>;
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
checkpoint(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export declare function openDb(latDir: string, requestedCacheDir?: string, readOnly?: boolean): SearchDb;
|
|
22
|
+
export declare function ensureMeta(db: SearchDb): Promise<void>;
|
|
23
|
+
export declare function getStoredModel(db: SearchDb): Promise<string | null>;
|
|
24
|
+
export declare function setStoredModel(db: SearchDb, value: string): Promise<void>;
|
|
25
|
+
export declare function ensureSectionsSchema(db: SearchDb, _dimensions: number): Promise<void>;
|
|
26
|
+
export declare function dropSections(db: SearchDb): Promise<void>;
|
|
27
|
+
export declare function closeDb(db: SearchDb): Promise<void>;
|