@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
package/README.md
CHANGED
|
@@ -103,7 +103,7 @@ Alternatively, install `@plebeiantech/lat.md` via npm/mise, run `lat init` in yo
|
|
|
103
103
|
|
|
104
104
|
## How it works
|
|
105
105
|
|
|
106
|
-
Run `lat init` to scaffold a `lat.md/` directory, then write markdown files describing your architecture, business logic, test specs — whatever matters. Link between sections using `[[file#Section#Subsection]]` syntax. Link to source code symbols with `[[src/auth.ts#validateToken]]`. Annotate source code with `// @lat: [[section-id]]` (or `# @lat: [[section-id]]` in Python) comments to tie implementation back to concepts.
|
|
106
|
+
Run `lat init` to scaffold a `lat.md/` directory, then write markdown files describing your architecture, business logic, test specs — whatever matters. Link between sections using `[[file#Section#Subsection]]` syntax. Link to source code symbols with `[[src/auth.ts#validateToken]]`. Annotate source code with `// @lat: [[section-id]]` (or `# @lat: [[section-id]]` in Python and PHP) comments to tie implementation back to concepts.
|
|
107
107
|
|
|
108
108
|
```
|
|
109
109
|
my-project/
|
|
@@ -5,8 +5,9 @@ export interface ChecklistOption {
|
|
|
5
5
|
/**
|
|
6
6
|
* Display an interactive multi-select checklist with arrow-key navigation.
|
|
7
7
|
* Returns an array of checked values.
|
|
8
|
+
* Known initial values start checked; unknown values are ignored.
|
|
8
9
|
*
|
|
9
10
|
* Keys: Up/Down (j/k) to move, Space to toggle, Enter to confirm, Ctrl+C to exit.
|
|
10
11
|
* Non-TTY fallback: returns [].
|
|
11
12
|
*/
|
|
12
|
-
export declare function checklistMenu(options: ChecklistOption[], prompt?: string): Promise<string[]>;
|
|
13
|
+
export declare function checklistMenu(options: ChecklistOption[], prompt?: string, initialValues?: readonly string[]): Promise<string[]>;
|
|
@@ -2,18 +2,19 @@ import { styleText } from 'node:util';
|
|
|
2
2
|
/**
|
|
3
3
|
* Display an interactive multi-select checklist with arrow-key navigation.
|
|
4
4
|
* Returns an array of checked values.
|
|
5
|
+
* Known initial values start checked; unknown values are ignored.
|
|
5
6
|
*
|
|
6
7
|
* Keys: Up/Down (j/k) to move, Space to toggle, Enter to confirm, Ctrl+C to exit.
|
|
7
8
|
* Non-TTY fallback: returns [].
|
|
8
9
|
*/
|
|
9
|
-
export async function checklistMenu(options, prompt) {
|
|
10
|
+
export async function checklistMenu(options, prompt, initialValues = []) {
|
|
10
11
|
if (options.length === 0)
|
|
11
12
|
return [];
|
|
12
13
|
if (!process.stdin.isTTY)
|
|
13
14
|
return [];
|
|
14
15
|
return new Promise((resolve) => {
|
|
15
16
|
let cursor = 0;
|
|
16
|
-
const checked = new Set();
|
|
17
|
+
const checked = new Set(options.flatMap((option, index) => initialValues.includes(option.value) ? [index] : []));
|
|
17
18
|
const stdin = process.stdin;
|
|
18
19
|
const wasRaw = stdin.isRaw;
|
|
19
20
|
stdin.setRawMode(true);
|
package/dist/src/cli/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { dirname, join } from 'node:path';
|
|
|
8
8
|
import { fileURLToPath } from 'node:url';
|
|
9
9
|
import { Command, InvalidArgumentError } from 'commander';
|
|
10
10
|
import { resolveCheckContext, resolveContext } from './context.js';
|
|
11
|
-
import { DEFAULT_SEARCH_LIMIT,
|
|
11
|
+
import { DEFAULT_SEARCH_LIMIT, DEFAULT_MIN_SIMILARITY, } from '../search/search.js';
|
|
12
12
|
function parsePort(value) {
|
|
13
13
|
if (!/^\d+$/.test(value)) {
|
|
14
14
|
throw new InvalidArgumentError('port must be an integer from 1 to 65535');
|
|
@@ -19,10 +19,22 @@ function parsePort(value) {
|
|
|
19
19
|
}
|
|
20
20
|
return port;
|
|
21
21
|
}
|
|
22
|
+
function parseUiServerBuildTarget(value) {
|
|
23
|
+
if (value !== 'node' && value !== 'vercel') {
|
|
24
|
+
throw new InvalidArgumentError('target must be node or vercel');
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
function configureUiRun(command) {
|
|
29
|
+
return command
|
|
30
|
+
.option('--logo-text <text>', 'top-left logo text')
|
|
31
|
+
.option('--no-git', 'disable Git working-tree integration')
|
|
32
|
+
.option('--port <number>', 'server port (default: 4242; explicit ports are strict)', parsePort);
|
|
33
|
+
}
|
|
22
34
|
function parseSimilarityThreshold(value) {
|
|
23
35
|
const threshold = Number(value);
|
|
24
36
|
if (!Number.isFinite(threshold) || threshold < 0 || threshold > 1) {
|
|
25
|
-
throw new InvalidArgumentError('
|
|
37
|
+
throw new InvalidArgumentError('min-similarity must be a number from 0 to 1');
|
|
26
38
|
}
|
|
27
39
|
return threshold;
|
|
28
40
|
}
|
|
@@ -108,27 +120,52 @@ program
|
|
|
108
120
|
const { sectionCommand } = await import('./section.js');
|
|
109
121
|
handleResult(await sectionCommand(ctx, query));
|
|
110
122
|
});
|
|
111
|
-
|
|
112
|
-
.command('ui')
|
|
113
|
-
.description('Open lat.md in a local browser')
|
|
114
|
-
.option('--logo-text <text>', 'top-left logo text')
|
|
115
|
-
.option('--port <number>', 'server port (default: 4242; explicit ports are strict)', parsePort)
|
|
116
|
-
.action(async (opts) => {
|
|
123
|
+
async function runUi(opts) {
|
|
117
124
|
const ctx = resolveContext(program.opts());
|
|
118
125
|
const { uiCommand } = await import('./ui.js');
|
|
119
|
-
handleResult(await uiCommand(ctx, {
|
|
126
|
+
handleResult(await uiCommand(ctx, {
|
|
127
|
+
git: opts.git,
|
|
128
|
+
logoText: opts.logoText,
|
|
129
|
+
port: opts.port,
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
const ui = configureUiRun(program.command('ui').description('Run or build the Lat UI')).action(runUi);
|
|
133
|
+
configureUiRun(ui.command('run').description('Run the local Lat UI server')).action(async (opts) => {
|
|
134
|
+
await runUi(opts);
|
|
120
135
|
});
|
|
121
|
-
ui.command('build')
|
|
122
|
-
|
|
123
|
-
.
|
|
136
|
+
const uiBuild = ui.command('build').description('Build a deployable Lat UI');
|
|
137
|
+
uiBuild
|
|
138
|
+
.command('static')
|
|
139
|
+
.description('Build a fully static, read-only Lat UI')
|
|
140
|
+
.argument('[output]', 'output directory relative to the project (default: .lat-build/static)', '.lat-build/static')
|
|
124
141
|
.option('--base <path>', 'deployment base path', '/')
|
|
142
|
+
.option('--force', 'replace an existing output path')
|
|
125
143
|
.option('--logo-text <text>', 'top-left logo text')
|
|
126
144
|
.action(async (output, opts) => {
|
|
127
145
|
const ctx = resolveContext(program.opts());
|
|
128
146
|
const { uiBuildCommand } = await import('./ui-build.js');
|
|
129
147
|
handleResult(await uiBuildCommand(ctx, output, {
|
|
130
148
|
basePath: opts.base,
|
|
149
|
+
force: opts.force,
|
|
150
|
+
logoText: opts.logoText ?? ui.opts().logoText,
|
|
151
|
+
}));
|
|
152
|
+
});
|
|
153
|
+
uiBuild
|
|
154
|
+
.command('server')
|
|
155
|
+
.description('Build a static Lat UI with a portable search server')
|
|
156
|
+
.argument('[output]', 'output directory (default: .lat-build/server for node, .vercel/output for vercel)')
|
|
157
|
+
.option('--base <path>', 'deployment base path', '/')
|
|
158
|
+
.option('--force', 'replace an existing output path')
|
|
159
|
+
.option('--logo-text <text>', 'top-left logo text')
|
|
160
|
+
.option('--target <target>', 'deployment target: node or vercel', parseUiServerBuildTarget, 'node')
|
|
161
|
+
.action(async (output, opts) => {
|
|
162
|
+
const ctx = resolveContext(program.opts());
|
|
163
|
+
const { uiBuildServerCommand } = await import('./ui-build-server.js');
|
|
164
|
+
handleResult(await uiBuildServerCommand(ctx, output, {
|
|
165
|
+
basePath: opts.base,
|
|
166
|
+
force: opts.force,
|
|
131
167
|
logoText: opts.logoText ?? ui.opts().logoText,
|
|
168
|
+
target: opts.target,
|
|
132
169
|
}));
|
|
133
170
|
});
|
|
134
171
|
program
|
|
@@ -302,11 +339,16 @@ program
|
|
|
302
339
|
});
|
|
303
340
|
program
|
|
304
341
|
.command('search')
|
|
305
|
-
.description('
|
|
342
|
+
.description('Hybrid search across lat.md sections')
|
|
306
343
|
.argument('[query]', 'search query in plain English')
|
|
307
344
|
.option('--limit <n>', `max results (default: ${DEFAULT_SEARCH_LIMIT})`, String(DEFAULT_SEARCH_LIMIT))
|
|
308
|
-
.option('--
|
|
309
|
-
|
|
345
|
+
.option('--preview <variant>', 'preview passage, intro, or both', (value) => {
|
|
346
|
+
if (!['passage', 'intro', 'both'].includes(value))
|
|
347
|
+
throw new InvalidArgumentError('preview must be passage, intro, or both');
|
|
348
|
+
return value;
|
|
349
|
+
}, 'passage')
|
|
350
|
+
.option('--debug', 'show retrieval scores and candidate diagnostics')
|
|
351
|
+
.option('--min-similarity <score>', `minimum cosine similarity score (default: ${DEFAULT_MIN_SIMILARITY})`, parseSimilarityThreshold)
|
|
310
352
|
.action(async (query, opts) => {
|
|
311
353
|
const ctx = resolveContext(program.opts());
|
|
312
354
|
const { searchCommand, cliProgress } = await import('./search.js');
|
|
@@ -314,7 +356,8 @@ program
|
|
|
314
356
|
const result = await searchCommand(ctx, query, {
|
|
315
357
|
limit: parseInt(opts.limit),
|
|
316
358
|
debug: opts.debug,
|
|
317
|
-
|
|
359
|
+
minSimilarity: opts.minSimilarity,
|
|
360
|
+
preview: opts.preview,
|
|
318
361
|
}, progress);
|
|
319
362
|
handleResult(result);
|
|
320
363
|
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Read machine-local checklist defaults without inferring installed agents. */
|
|
2
|
+
export declare function readInitAgents(latDir: string): string[];
|
|
3
|
+
/** Update just the confirmed selection, preserving other YAML and comments. */
|
|
4
|
+
export declare function writeInitAgents(latDir: string, agents: string[]): void;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { isMap, parseDocument } from 'yaml';
|
|
4
|
+
function readPreferences(latDir) {
|
|
5
|
+
const path = join(latDir, 'config.local.yaml');
|
|
6
|
+
const document = parseDocument(existsSync(path) ? readFileSync(path, 'utf8') : '');
|
|
7
|
+
if (document.errors.length > 0) {
|
|
8
|
+
throw new Error(`Cannot parse ${path}: ${document.errors[0].message}`);
|
|
9
|
+
}
|
|
10
|
+
if (document.contents !== null && !isMap(document.contents)) {
|
|
11
|
+
throw new Error(`${path} must be a mapping`);
|
|
12
|
+
}
|
|
13
|
+
if (document.has('init') && !isMap(document.get('init', true))) {
|
|
14
|
+
throw new Error(`${path}: init must be a mapping`);
|
|
15
|
+
}
|
|
16
|
+
return { path, document };
|
|
17
|
+
}
|
|
18
|
+
/** Read machine-local checklist defaults without inferring installed agents. */
|
|
19
|
+
export function readInitAgents(latDir) {
|
|
20
|
+
const { path, document } = readPreferences(latDir);
|
|
21
|
+
const agents = document.toJS()?.init?.agents;
|
|
22
|
+
if (agents === undefined)
|
|
23
|
+
return [];
|
|
24
|
+
if (!Array.isArray(agents) ||
|
|
25
|
+
agents.some((agent) => typeof agent !== 'string')) {
|
|
26
|
+
throw new Error(`${path}: init.agents must be a list of strings`);
|
|
27
|
+
}
|
|
28
|
+
return agents;
|
|
29
|
+
}
|
|
30
|
+
/** Update just the confirmed selection, preserving other YAML and comments. */
|
|
31
|
+
export function writeInitAgents(latDir, agents) {
|
|
32
|
+
const { path, document } = readPreferences(latDir);
|
|
33
|
+
document.setIn(['init', 'agents'], agents);
|
|
34
|
+
writeFileSync(path, document.toString());
|
|
35
|
+
}
|
package/dist/src/cli/init.js
CHANGED
|
@@ -8,7 +8,7 @@ import { findTemplatesDir } from './templates.js';
|
|
|
8
8
|
import { readAgentsTemplate, readCursorRulesTemplate, readPiExtensionTemplate, readOpenCodePluginTemplate, readSkillTemplate, } from './gen.js';
|
|
9
9
|
import { getLlmKey, getRepoEmbedding, setRepoEmbedding } from '../config.js';
|
|
10
10
|
import { makeStyler } from './context.js';
|
|
11
|
-
import { closeDb,
|
|
11
|
+
import { closeDb, getStoredModel, openDb } from '../search/db.js';
|
|
12
12
|
import { modelKey } from '../search/embedder.js';
|
|
13
13
|
import { reindexCommand } from './reindex.js';
|
|
14
14
|
import { INIT_VERSION, writeInitMeta, readInitVersion, readFileHash, contentHash, } from '../init-version.js';
|
|
@@ -18,6 +18,7 @@ import { checklistMenu } from './checklist-menu.js';
|
|
|
18
18
|
import { writeForkInstructions } from './fork-instructions.js';
|
|
19
19
|
import { offerRequireMode, writeForkScaffold } from './fork-scaffold.js';
|
|
20
20
|
import { setupAntigravity } from '../fork/antigravity-init.js';
|
|
21
|
+
import { readInitAgents, writeInitAgents } from './init-preferences.js';
|
|
21
22
|
async function confirm(rl, message) {
|
|
22
23
|
while (true) {
|
|
23
24
|
let answer;
|
|
@@ -667,11 +668,10 @@ async function setupCodex(root, latDir, hashes, ask, style) {
|
|
|
667
668
|
' Run /hooks in Codex to review and trust the project hooks.');
|
|
668
669
|
}
|
|
669
670
|
async function readStoredEmbeddingModel(latDir) {
|
|
670
|
-
if (!existsSync(join(latDir, '.cache', '
|
|
671
|
+
if (!existsSync(join(latDir, '.cache', 'search.db')))
|
|
671
672
|
return null;
|
|
672
|
-
const db = openDb(latDir);
|
|
673
|
+
const db = openDb(latDir, undefined, true);
|
|
673
674
|
try {
|
|
674
|
-
await ensureMeta(db);
|
|
675
675
|
return await getStoredModel(db);
|
|
676
676
|
}
|
|
677
677
|
finally {
|
|
@@ -916,6 +916,7 @@ export async function initCmd(targetDir) {
|
|
|
916
916
|
writeForkScaffold(latDir);
|
|
917
917
|
}
|
|
918
918
|
ensureLatLocalConfigIgnored(latDir);
|
|
919
|
+
ensureGitignored(root, '.lat-build');
|
|
919
920
|
// Step 2: Configure fresh/outdated setups, ask interactive users about an
|
|
920
921
|
// available key, and offer to rebuild an index whose backend differs. This
|
|
921
922
|
// happens before agent selection so "no agents" still completes it.
|
|
@@ -931,7 +932,7 @@ export async function initCmd(targetDir) {
|
|
|
931
932
|
{ label: 'Codex', value: 'codex' },
|
|
932
933
|
{ label: 'Antigravity / Gemini', value: 'antigravity' },
|
|
933
934
|
];
|
|
934
|
-
const selectedAgents = await checklistMenu(allAgents, 'Which coding agents do you use?');
|
|
935
|
+
const selectedAgents = await checklistMenu(allAgents, 'Which coding agents do you use?', readInitAgents(latDir));
|
|
935
936
|
const useClaudeCode = selectedAgents.includes('claude');
|
|
936
937
|
const usePi = selectedAgents.includes('pi');
|
|
937
938
|
const useCursor = selectedAgents.includes('cursor');
|
|
@@ -978,6 +979,8 @@ export async function initCmd(targetDir) {
|
|
|
978
979
|
// agent. Stamp the version so future non-interactive runs do not reapply
|
|
979
980
|
// fresh/outdated defaults and overwrite the chosen backend.
|
|
980
981
|
writeInitMeta(latDir, {});
|
|
982
|
+
if (interactive)
|
|
983
|
+
writeInitAgents(latDir, selectedAgents);
|
|
981
984
|
console.log('');
|
|
982
985
|
console.log(styleText('dim', 'No agents selected. You can re-run') +
|
|
983
986
|
' lat init ' +
|
|
@@ -1049,6 +1052,8 @@ export async function initCmd(targetDir) {
|
|
|
1049
1052
|
await writeForkInstructions(root, latDir, fileHashes, ask);
|
|
1050
1053
|
// Record init version and file hashes so `lat check` can detect stale setups
|
|
1051
1054
|
writeInitMeta(latDir, fileHashes);
|
|
1055
|
+
if (interactive)
|
|
1056
|
+
writeInitAgents(latDir, selectedAgents);
|
|
1052
1057
|
console.log('');
|
|
1053
1058
|
console.log(styleText('green', 'Done!') +
|
|
1054
1059
|
' Run ' +
|
package/dist/src/cli/reindex.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { writeIndex } from '../search/cache.js';
|
|
1
2
|
import readline from 'node:readline/promises';
|
|
2
|
-
import {
|
|
3
|
+
import { ensureMeta, setStoredModel, ensureSectionsSchema, dropSections, } from '../search/db.js';
|
|
3
4
|
import { embedderFromEnv, localEmbedder, modelKey, EmbeddingAuthError, } from '../search/embedder.js';
|
|
4
5
|
import { getLlmKey, getRepoEmbedding, setRepoEmbedding } from '../config.js';
|
|
5
6
|
import { indexSections } from '../search/index.js';
|
|
@@ -83,11 +84,9 @@ export async function reindexCommand(ctx, opts) {
|
|
|
83
84
|
embedder = await localEmbedder();
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
|
-
const db = openDb(ctx.latDir);
|
|
87
87
|
const interactive = ctx.mode === 'cli' && !!process.stderr.isTTY;
|
|
88
|
-
|
|
88
|
+
return writeIndex(ctx.latDir, undefined, true, async (db) => {
|
|
89
89
|
await ensureMeta(db);
|
|
90
|
-
await dropSections(db);
|
|
91
90
|
await ensureSectionsSchema(db, embedder.dimensions);
|
|
92
91
|
const label = `Reindexing with ${embedder.name}`;
|
|
93
92
|
// Interactive terminals get a live progress line driven per embed-chunk
|
|
@@ -130,8 +129,5 @@ export async function reindexCommand(ctx, opts) {
|
|
|
130
129
|
output: s.green(`Reindexed ${stats.added} sections`) +
|
|
131
130
|
` using ${s.cyan(embedder.name)}.`,
|
|
132
131
|
};
|
|
133
|
-
}
|
|
134
|
-
finally {
|
|
135
|
-
await closeDb(db);
|
|
136
|
-
}
|
|
132
|
+
});
|
|
137
133
|
}
|
package/dist/src/cli/search.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export declare function runSearch(latDir: string, query: string, limit: number,
|
|
|
27
27
|
buildIndex?: boolean;
|
|
28
28
|
project?: MarkdownProjectAnalysis;
|
|
29
29
|
sectionById?: ReadonlyMap<string, Section>;
|
|
30
|
-
|
|
30
|
+
minSimilarity?: number;
|
|
31
31
|
cacheDir?: string;
|
|
32
32
|
}): Promise<SearchResult>;
|
|
33
33
|
/**
|
|
@@ -41,5 +41,6 @@ export declare function cliProgress(s: Styler): IndexProgress;
|
|
|
41
41
|
export declare function searchCommand(ctx: CmdContext, query: string | undefined, opts: {
|
|
42
42
|
limit: number;
|
|
43
43
|
debug?: boolean;
|
|
44
|
-
|
|
44
|
+
minSimilarity?: number;
|
|
45
|
+
preview?: 'passage' | 'intro' | 'both';
|
|
45
46
|
}, progress?: IndexProgress): Promise<CmdResult>;
|
package/dist/src/cli/search.js
CHANGED
|
@@ -1,71 +1,55 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { LEXICAL_VERSION } from '../search/lexical.js';
|
|
3
|
+
import { hasIndex } from '../search/db.js';
|
|
4
|
+
import { embeddingFingerprint } from '../search/chunks.js';
|
|
5
|
+
import { writeIndex } from '../search/cache.js';
|
|
2
6
|
import { dirname, join } from 'node:path';
|
|
3
|
-
import { openDb,
|
|
4
|
-
import { getLlmKey } from '../config.js';
|
|
7
|
+
import { openDb, getStoredModel, setStoredModel, ensureSectionsSchema, closeDb, } from '../search/db.js';
|
|
5
8
|
import { embedderForIndex, modelKey, ReindexRequiredError, EmbeddingAuthError, } from '../search/embedder.js';
|
|
6
|
-
import { indexSections } from '../search/index.js';
|
|
9
|
+
import { indexSections, projectFingerprint, } from '../search/index.js';
|
|
7
10
|
import { searchSections } from '../search/search.js';
|
|
8
|
-
import { searchIndexedSections } from '../search/query.js';
|
|
11
|
+
import { resolveSearchMatches, searchIndexedSections, } from '../search/query.js';
|
|
9
12
|
import { analyzeMarkdownProject, commandProjectAnalysis, } from '../project-analysis.js';
|
|
10
13
|
import { formatSectionPreview, formatNavHints } from '../format.js';
|
|
11
14
|
import { provenanceNote, formatProvenanceNote } from './check-status.js';
|
|
12
15
|
async function withDb(latDir, progress, project, cacheDir, fn) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
await ensureMeta(db);
|
|
16
|
-
const stored = await getStoredModel(db);
|
|
17
|
-
// The stored model is authoritative — no silent backend switch. Throws
|
|
18
|
-
// ReindexRequiredError if the environment can't serve the stored index.
|
|
19
|
-
// Rebuilding / switching backends is `lat reindex`, never `lat search`.
|
|
20
|
-
const embedder = await embedderForIndex(stored, latDir);
|
|
21
|
-
await ensureSectionsSchema(db, embedder.dimensions);
|
|
22
|
-
const countResult = await db.execute('SELECT COUNT(*) as n FROM sections');
|
|
23
|
-
let isEmpty = countResult.rows[0].n === 0;
|
|
24
|
-
// Legacy cache: a version before the model was recorded left rows behind
|
|
25
|
-
// with no `meta.embedding_model`. Those vectors may be a different backend
|
|
26
|
-
// (and dimension) than the resolved embedder, and `CREATE TABLE IF NOT
|
|
27
|
-
// EXISTS` won't migrate the column width — so drop and rebuild from scratch
|
|
28
|
-
// under the resolved backend rather than querying a mismatched table.
|
|
29
|
-
if (!stored && !isEmpty) {
|
|
30
|
-
await dropSections(db);
|
|
31
|
-
await ensureSectionsSchema(db, embedder.dimensions);
|
|
32
|
-
isEmpty = true;
|
|
33
|
-
}
|
|
34
|
-
// If the repo is pinned to local but a key is set, say so — otherwise it
|
|
35
|
-
// looks like the key is being silently ignored.
|
|
36
|
-
if (isEmpty && embedder.name.startsWith('local:') && process.stderr.isTTY) {
|
|
37
|
-
let hasKey = false;
|
|
38
|
-
try {
|
|
39
|
-
hasKey = !!getLlmKey();
|
|
40
|
-
}
|
|
41
|
-
catch {
|
|
42
|
-
/* key source misconfigured — irrelevant, we're local */
|
|
43
|
-
}
|
|
44
|
-
if (hasKey) {
|
|
45
|
-
process.stderr.write('This repo is configured for local embeddings; ignoring LAT_LLM_KEY.\n');
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
progress?.beforeIndex?.(isEmpty);
|
|
16
|
+
if (hasIndex(cacheDir ?? join(latDir, '.cache'))) {
|
|
17
|
+
const db = openDb(latDir, cacheDir, true);
|
|
49
18
|
try {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
19
|
+
const stored = await getStoredModel(db);
|
|
20
|
+
const embedder = await embedderForIndex(stored, latDir);
|
|
21
|
+
const metadata = new Map((await db.execute('SELECT key,value FROM meta')).rows.map((row) => [
|
|
22
|
+
row.key,
|
|
23
|
+
row.value,
|
|
24
|
+
]));
|
|
25
|
+
if (metadata.get('fingerprint') !== embeddingFingerprint(embedder))
|
|
26
|
+
throw new ReindexRequiredError('Search chunking or embedding model changed; run lat reindex.');
|
|
27
|
+
if (stored &&
|
|
28
|
+
metadata.get('lexical_version') === LEXICAL_VERSION &&
|
|
29
|
+
metadata.get('project_hash') === projectFingerprint(project)) {
|
|
30
|
+
progress?.afterIndex?.({
|
|
31
|
+
added: 0,
|
|
32
|
+
updated: 0,
|
|
33
|
+
removed: 0,
|
|
34
|
+
unchanged: project.sections.length,
|
|
35
|
+
}, false);
|
|
36
|
+
return await fn(db, embedder, project);
|
|
37
|
+
}
|
|
56
38
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// truly fresh (re-resolves the backend cleanly) rather than stuck.
|
|
60
|
-
if (!stored)
|
|
61
|
-
await dropSections(db);
|
|
62
|
-
throw err;
|
|
39
|
+
finally {
|
|
40
|
+
await closeDb(db);
|
|
63
41
|
}
|
|
64
|
-
return await fn(db, embedder, project);
|
|
65
|
-
}
|
|
66
|
-
finally {
|
|
67
|
-
await closeDb(db);
|
|
68
42
|
}
|
|
43
|
+
return writeIndex(latDir, cacheDir, false, async (db, stored) => {
|
|
44
|
+
const embedder = await embedderForIndex(stored, latDir);
|
|
45
|
+
await ensureSectionsSchema(db, embedder.dimensions);
|
|
46
|
+
const isEmpty = (await db.execute('SELECT COUNT(*) AS n FROM sections')).rows[0].n === 0;
|
|
47
|
+
progress?.beforeIndex?.(isEmpty);
|
|
48
|
+
const stats = await indexSections(latDir, db, embedder, undefined, project);
|
|
49
|
+
await setStoredModel(db, modelKey(embedder));
|
|
50
|
+
progress?.afterIndex?.(stats, isEmpty);
|
|
51
|
+
return fn(db, embedder, project);
|
|
52
|
+
});
|
|
69
53
|
}
|
|
70
54
|
/**
|
|
71
55
|
* Run a semantic search across lat.md sections.
|
|
@@ -86,7 +70,7 @@ export async function runSearch(latDir, query, limit, progress, opts) {
|
|
|
86
70
|
})).sectionById;
|
|
87
71
|
return searchIndexedSections(latDir, query, limit, sectionById, {
|
|
88
72
|
cacheDir: opts.cacheDir,
|
|
89
|
-
|
|
73
|
+
minSimilarity: opts.minSimilarity,
|
|
90
74
|
});
|
|
91
75
|
}
|
|
92
76
|
const project = opts?.project ??
|
|
@@ -94,15 +78,10 @@ export async function runSearch(latDir, query, limit, progress, opts) {
|
|
|
94
78
|
executor: 'auto',
|
|
95
79
|
}));
|
|
96
80
|
return withDb(latDir, progress, project, opts?.cacheDir, async (db, embedder, analyzed) => {
|
|
97
|
-
const results = await searchSections(db, query, embedder, limit, opts?.
|
|
81
|
+
const results = await searchSections(db, query, embedder, limit, opts?.minSimilarity);
|
|
98
82
|
return {
|
|
99
83
|
query,
|
|
100
|
-
matches: results.
|
|
101
|
-
const section = analyzed.sectionById.get(result.id.toLowerCase());
|
|
102
|
-
return section
|
|
103
|
-
? [{ section, reason: 'semantic match', score: result.score }]
|
|
104
|
-
: [];
|
|
105
|
-
}),
|
|
84
|
+
matches: resolveSearchMatches(results, analyzed.sectionById),
|
|
106
85
|
};
|
|
107
86
|
});
|
|
108
87
|
}
|
|
@@ -163,7 +142,32 @@ async function formatSearchMatches(ctx, query, matches, opts) {
|
|
|
163
142
|
lines.push('');
|
|
164
143
|
lines.push(formatSectionPreview(ctx, matches[i].section, {
|
|
165
144
|
reason: matches[i].reason,
|
|
166
|
-
|
|
145
|
+
spans: opts?.preview === 'intro'
|
|
146
|
+
? undefined
|
|
147
|
+
: matches[i].evidence?.[0]?.spans,
|
|
148
|
+
score: opts?.showScores ? matches[i].rankScore : undefined,
|
|
149
|
+
text: opts?.preview === 'intro'
|
|
150
|
+
? undefined
|
|
151
|
+
: matches[i].evidence?.length
|
|
152
|
+
? (opts?.preview === 'both'
|
|
153
|
+
? matches[i].section.firstParagraph + '\n\n'
|
|
154
|
+
: '') + matches[i].evidence[0].text
|
|
155
|
+
: undefined,
|
|
156
|
+
debug: opts?.showScores && matches[i].rankScore !== undefined
|
|
157
|
+
? JSON.stringify({
|
|
158
|
+
lexicalRank: matches[i].lexicalRank,
|
|
159
|
+
semanticRank: matches[i].semanticRank,
|
|
160
|
+
lexicalScore: matches[i].lexicalScore,
|
|
161
|
+
semanticSimilarity: matches[i].semanticSimilarity,
|
|
162
|
+
lexicalContribution: matches[i].lexicalRank
|
|
163
|
+
? 1 / (60 + matches[i].lexicalRank)
|
|
164
|
+
: 0,
|
|
165
|
+
semanticContribution: matches[i].semanticRank
|
|
166
|
+
? 1 / (60 + matches[i].semanticRank)
|
|
167
|
+
: 0,
|
|
168
|
+
...matches[i].diagnostics,
|
|
169
|
+
})
|
|
170
|
+
: undefined,
|
|
167
171
|
}));
|
|
168
172
|
const note = notes.get(matches[i].section.filePath);
|
|
169
173
|
if (note)
|
|
@@ -182,7 +186,7 @@ export async function searchCommand(ctx, query, opts, progress) {
|
|
|
182
186
|
const project = await commandProjectAnalysis(ctx);
|
|
183
187
|
const result = await runSearch(ctx.latDir, query, opts.limit, progress, {
|
|
184
188
|
project,
|
|
185
|
-
|
|
189
|
+
minSimilarity: opts.minSimilarity,
|
|
186
190
|
});
|
|
187
191
|
if (result.matches.length === 0) {
|
|
188
192
|
return { output: 'No results found.' };
|
|
@@ -190,6 +194,7 @@ export async function searchCommand(ctx, query, opts, progress) {
|
|
|
190
194
|
return {
|
|
191
195
|
output: (await formatSearchMatches(ctx, query, result.matches, {
|
|
192
196
|
showScores: opts.debug,
|
|
197
|
+
preview: opts.preview ?? 'passage',
|
|
193
198
|
})) + formatNavHints(ctx),
|
|
194
199
|
};
|
|
195
200
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CmdContext, CmdResult } from '../context.js';
|
|
2
|
+
import { buildServerView, type ServerViewBuildOptions, type ServerViewBuildResult } from '../view/server-build.js';
|
|
3
|
+
export type ServerViewBuildTarget = 'node' | 'vercel';
|
|
4
|
+
export type UiBuildServerOptions = ServerViewBuildOptions & {
|
|
5
|
+
target?: ServerViewBuildTarget;
|
|
6
|
+
};
|
|
7
|
+
export type UiBuildServerDependencies = {
|
|
8
|
+
buildNode: typeof buildServerView;
|
|
9
|
+
buildVercel?: (ctx: CmdContext, output: string, options: ServerViewBuildOptions) => Promise<ServerViewBuildResult>;
|
|
10
|
+
};
|
|
11
|
+
/** Export immutable UI data with a Node or Vercel semantic-search service. */
|
|
12
|
+
export declare function uiBuildServerCommand(ctx: CmdContext, output: string | undefined, options?: UiBuildServerOptions, dependencies?: UiBuildServerDependencies): Promise<CmdResult>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { buildServerView, } from '../view/server-build.js';
|
|
2
|
+
const defaultDependencies = {
|
|
3
|
+
buildNode: buildServerView,
|
|
4
|
+
};
|
|
5
|
+
/** Export immutable UI data with a Node or Vercel semantic-search service. */
|
|
6
|
+
export async function uiBuildServerCommand(ctx, output, options = {}, dependencies = defaultDependencies) {
|
|
7
|
+
try {
|
|
8
|
+
const { target = 'node', ...buildOptions } = options;
|
|
9
|
+
const resolvedOutput = output ?? (target === 'vercel' ? '.vercel/output' : '.lat-build/server');
|
|
10
|
+
let result;
|
|
11
|
+
if (target === 'vercel') {
|
|
12
|
+
const buildVercel = dependencies.buildVercel ??
|
|
13
|
+
(await import('../view/vercel-server-build.js')).buildVercelServerView;
|
|
14
|
+
result = await buildVercel(ctx, resolvedOutput, buildOptions);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
result = await dependencies.buildNode(ctx, resolvedOutput, buildOptions);
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
output: `Built ${result.documents} documents and ${result.sources} source views for ${target} at ${result.outputDir}`,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
return { output: error.message, isError: true };
|
|
25
|
+
}
|
|
26
|
+
}
|
package/dist/src/cli/ui-build.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { buildStaticView, } from '../view/static-build.js';
|
|
2
2
|
/** Export the current vault as a serverless, read-only Lat UI. */
|
|
3
|
-
export async function uiBuildCommand(ctx, output = 'lat-
|
|
3
|
+
export async function uiBuildCommand(ctx, output = '.lat-build/static', options = {}) {
|
|
4
4
|
try {
|
|
5
5
|
const result = await buildStaticView(ctx, output, options);
|
|
6
6
|
return {
|
package/dist/src/cli/ui.js
CHANGED
|
@@ -38,7 +38,7 @@ export async function uiCommand(ctx, options = {}) {
|
|
|
38
38
|
options.onStarted?.(server);
|
|
39
39
|
const lines = [
|
|
40
40
|
`Viewing lat.md at ${server.url}`,
|
|
41
|
-
'Note:
|
|
41
|
+
'Note: use `lat ui build static` or `lat ui build server` to build a deployable UI',
|
|
42
42
|
];
|
|
43
43
|
try {
|
|
44
44
|
await (options.openBrowser ?? openBrowser)(server.url);
|
package/dist/src/format.d.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
import type { SourceSpan } from './search/types.js';
|
|
1
2
|
import type { Section, SectionMatch } from './lattice-model.js';
|
|
2
3
|
import type { CmdContext, Styler } from './context.js';
|
|
3
4
|
export declare function formatSectionId(id: string, s: Styler): string;
|
|
4
5
|
export declare function formatSectionPreview(ctx: CmdContext, section: Section, opts?: {
|
|
5
6
|
reason?: string;
|
|
6
7
|
score?: number;
|
|
8
|
+
text?: string;
|
|
9
|
+
debug?: string;
|
|
10
|
+
spans?: SourceSpan[];
|
|
7
11
|
}): string;
|
|
8
12
|
export declare function formatResultList(ctx: CmdContext, header: string, matches: SectionMatch[], opts?: {
|
|
9
13
|
showScores?: boolean;
|
|
14
|
+
preview?: 'passage' | 'intro' | 'both';
|
|
10
15
|
}): string;
|
|
11
16
|
export declare function formatNavHints(ctx: CmdContext): string;
|
package/dist/src/format.js
CHANGED
|
@@ -12,6 +12,7 @@ export function formatSectionPreview(ctx, section, opts) {
|
|
|
12
12
|
const kind = section.id.includes('#') ? 'Section' : 'File';
|
|
13
13
|
const details = [
|
|
14
14
|
opts?.reason,
|
|
15
|
+
opts?.debug,
|
|
15
16
|
opts?.score === undefined ? undefined : `score: ${opts.score.toFixed(6)}`,
|
|
16
17
|
].filter((detail) => detail !== undefined);
|
|
17
18
|
const detailsSuffix = details.length > 0 ? ' ' + s.dim(`(${details.join(', ')})`) : '';
|
|
@@ -19,9 +20,11 @@ export function formatSectionPreview(ctx, section, opts) {
|
|
|
19
20
|
`${s.dim('*')} ${s.dim(kind + ':')} [[${formatSectionId(section.id, s)}]]${detailsSuffix}`,
|
|
20
21
|
` ${s.dim('Defined in')} ${s.cyan(relPath)}${s.dim(`:${section.startLine}-${section.endLine}`)}`,
|
|
21
22
|
];
|
|
22
|
-
if (
|
|
23
|
-
lines.push(
|
|
24
|
-
|
|
23
|
+
if (opts?.spans?.length)
|
|
24
|
+
lines.push(` ${s.dim('Matched lines:')} ${opts.spans.map((span) => `${span.startLine}-${span.endLine}`).join(', ')}`);
|
|
25
|
+
const text = opts?.text ?? section.firstParagraph;
|
|
26
|
+
if (text)
|
|
27
|
+
lines.push('', ...text.split('\n').map((line) => ` ${s.dim('>')} ${line}`));
|
|
25
28
|
return lines.join('\n');
|
|
26
29
|
}
|
|
27
30
|
export function formatResultList(ctx, header, matches, opts) {
|
|
@@ -31,7 +34,32 @@ export function formatResultList(ctx, header, matches, opts) {
|
|
|
31
34
|
lines.push('');
|
|
32
35
|
lines.push(formatSectionPreview(ctx, matches[i].section, {
|
|
33
36
|
reason: matches[i].reason,
|
|
34
|
-
|
|
37
|
+
spans: opts?.preview === 'intro'
|
|
38
|
+
? undefined
|
|
39
|
+
: matches[i].evidence?.[0]?.spans,
|
|
40
|
+
score: opts?.showScores ? matches[i].rankScore : undefined,
|
|
41
|
+
text: opts?.preview === 'intro'
|
|
42
|
+
? undefined
|
|
43
|
+
: matches[i].evidence?.length
|
|
44
|
+
? (opts?.preview === 'both'
|
|
45
|
+
? matches[i].section.firstParagraph + '\n\n'
|
|
46
|
+
: '') + matches[i].evidence[0].text
|
|
47
|
+
: undefined,
|
|
48
|
+
debug: opts?.showScores && matches[i].rankScore !== undefined
|
|
49
|
+
? JSON.stringify({
|
|
50
|
+
lexicalRank: matches[i].lexicalRank,
|
|
51
|
+
semanticRank: matches[i].semanticRank,
|
|
52
|
+
lexicalScore: matches[i].lexicalScore,
|
|
53
|
+
semanticSimilarity: matches[i].semanticSimilarity,
|
|
54
|
+
lexicalContribution: matches[i].lexicalRank
|
|
55
|
+
? 1 / (60 + matches[i].lexicalRank)
|
|
56
|
+
: 0,
|
|
57
|
+
semanticContribution: matches[i].semanticRank
|
|
58
|
+
? 1 / (60 + matches[i].semanticRank)
|
|
59
|
+
: 0,
|
|
60
|
+
...matches[i].diagnostics,
|
|
61
|
+
})
|
|
62
|
+
: undefined,
|
|
35
63
|
}));
|
|
36
64
|
}
|
|
37
65
|
lines.push('');
|