lat.md 0.7.0 → 0.7.1

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 CHANGED
@@ -33,7 +33,7 @@ After installing, run `lat init` in the repo you want to use lat in.
33
33
 
34
34
  ## How it works
35
35
 
36
- 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. Annotate source code with `// @lat: [[section-id]]` (or `# @lat: [[section-id]]` in Python) comments to tie implementation back to concepts.
36
+ 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.
37
37
 
38
38
  ```
39
39
  my-project/
@@ -53,9 +53,10 @@ my-project/
53
53
  npx lat.md init # scaffold a lat.md/ directory
54
54
  npx lat.md check # validate all wiki links and code refs
55
55
  npx lat.md locate "OAuth Flow" # find sections by name (exact, fuzzy)
56
+ npx lat.md section "auth#OAuth Flow" # show a section with its links and refs
56
57
  npx lat.md refs "auth#OAuth Flow" # find what references a section
57
58
  npx lat.md search "how do we auth?" # semantic search via embeddings
58
- npx lat.md prompt "fix [[OAuth Flow]]" # expand [[refs]] in a prompt for agents
59
+ npx lat.md expand "fix [[OAuth Flow]]" # expand [[refs]] in a prompt for agents
59
60
  ```
60
61
 
61
62
  ## Configuration
@@ -97,7 +97,7 @@ export async function refsCommand(ctx, query, scope) {
97
97
  parts.push(formatResultList(ctx, `References to "${target.id}":`, mdRefs));
98
98
  }
99
99
  if (codeRefs.length > 0) {
100
- parts.push(s.bold('Code references:') +
100
+ parts.push('## Code references:' +
101
101
  '\n\n' +
102
102
  codeRefs.map((l) => `${s.dim('*')} ${l}`).join('\n'));
103
103
  }
@@ -3,7 +3,7 @@ import { detectProvider } from '../search/provider.js';
3
3
  import { indexSections } from '../search/index.js';
4
4
  import { searchSections } from '../search/search.js';
5
5
  import { loadAllSections, flattenSections, } from '../lattice.js';
6
- import { formatResultList } from '../format.js';
6
+ import { formatResultList, formatNavHints } from '../format.js';
7
7
  async function withDb(latDir, key, progress, fn) {
8
8
  const provider = detectProvider(key);
9
9
  const db = openDb(latDir);
@@ -94,14 +94,8 @@ export async function searchCommand(ctx, query, opts, progress) {
94
94
  if (result.matches.length === 0) {
95
95
  return { output: 'No results found.' };
96
96
  }
97
- const navHints = ctx.mode === 'cli'
98
- ? '- `lat section "section#id"` \u2014 show full content with outgoing/incoming refs\n' +
99
- '- `lat search "new query"` \u2014 search for something else'
100
- : '- `lat_section` \u2014 show full content with outgoing/incoming refs\n' +
101
- '- `lat_search` \u2014 search for something else';
102
97
  return {
103
98
  output: formatResultList(ctx, `Search results for "${query}":`, result.matches) +
104
- '\nTo navigate further:\n' +
105
- navHints,
99
+ formatNavHints(ctx),
106
100
  };
107
101
  }
@@ -1,7 +1,7 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { join, relative } from 'node:path';
3
3
  import { loadAllSections, findSections, flattenSections, extractRefs, buildFileIndex, resolveRef, listLatticeFiles, } from '../lattice.js';
4
- import { formatSectionId } from '../format.js';
4
+ import { formatSectionId, formatNavHints } from '../format.js';
5
5
  /**
6
6
  * Look up a section by id, return its content, outgoing wiki link targets,
7
7
  * and incoming references from other sections.
@@ -84,13 +84,17 @@ export function formatSectionOutput(ctx, result) {
84
84
  const { section, content, outgoingRefs, incomingRefs } = result;
85
85
  const relPath = relative(process.cwd(), join(ctx.projectRoot, section.filePath));
86
86
  const loc = `${s.cyan(relPath)}${s.dim(`:${section.startLine}-${section.endLine}`)}`;
87
+ const quoted = content
88
+ .split('\n')
89
+ .map((line) => (line ? `> ${line}` : '>'))
90
+ .join('\n');
87
91
  const parts = [
88
92
  `${s.bold('[[' + formatSectionId(section.id, s) + ']]')} (${loc})`,
89
93
  '',
90
- content,
94
+ quoted,
91
95
  ];
92
96
  if (outgoingRefs.length > 0) {
93
- parts.push('', s.bold('This section references:'), '');
97
+ parts.push('', '## This section references:', '');
94
98
  for (const ref of outgoingRefs) {
95
99
  const body = ref.resolved.firstParagraph
96
100
  ? ` ${s.dim('—')} ${truncate(ref.resolved.firstParagraph, 120)}`
@@ -99,7 +103,7 @@ export function formatSectionOutput(ctx, result) {
99
103
  }
100
104
  }
101
105
  if (incomingRefs.length > 0) {
102
- parts.push('', s.bold('Referenced by:'), '');
106
+ parts.push('', '## Referenced by:', '');
103
107
  for (const ref of incomingRefs) {
104
108
  const body = ref.section.firstParagraph
105
109
  ? ` ${s.dim('—')} ${truncate(ref.section.firstParagraph, 120)}`
@@ -107,6 +111,7 @@ export function formatSectionOutput(ctx, result) {
107
111
  parts.push(`${s.dim('*')} [[${formatSectionId(ref.section.id, s)}]]${body}`);
108
112
  }
109
113
  }
114
+ parts.push(formatNavHints(ctx));
110
115
  return parts.join('\n');
111
116
  }
112
117
  export async function sectionCommand(ctx, query) {
@@ -5,3 +5,4 @@ export declare function formatSectionPreview(ctx: CmdContext, section: Section,
5
5
  reason?: string;
6
6
  }): string;
7
7
  export declare function formatResultList(ctx: CmdContext, header: string, matches: SectionMatch[]): string;
8
+ export declare function formatNavHints(ctx: CmdContext): string;
@@ -21,7 +21,7 @@ export function formatSectionPreview(ctx, section, opts) {
21
21
  return lines.join('\n');
22
22
  }
23
23
  export function formatResultList(ctx, header, matches) {
24
- const lines = ['', ctx.styler.bold(header), ''];
24
+ const lines = ['', `## ${header}`, ''];
25
25
  for (let i = 0; i < matches.length; i++) {
26
26
  if (i > 0)
27
27
  lines.push('');
@@ -32,3 +32,12 @@ export function formatResultList(ctx, header, matches) {
32
32
  lines.push('');
33
33
  return lines.join('\n');
34
34
  }
35
+ export function formatNavHints(ctx) {
36
+ const s = ctx.styler;
37
+ const hints = ctx.mode === 'cli'
38
+ ? `${s.dim('*')} \`lat section "section#id"\` \u2014 show full content with outgoing/incoming refs\n` +
39
+ `${s.dim('*')} \`lat search "new query"\` \u2014 search for something else`
40
+ : `${s.dim('*')} \`lat_section\` \u2014 show full content with outgoing/incoming refs\n` +
41
+ `${s.dim('*')} \`lat_search\` \u2014 search for something else`;
42
+ return `\n## To navigate further:\n\n${hints}`;
43
+ }
@@ -19,7 +19,6 @@ export type LatFrontmatter = {
19
19
  requireCodeMention?: boolean;
20
20
  };
21
21
  export declare function parseFrontmatter(content: string): LatFrontmatter;
22
- export declare function stripFrontmatter(content: string): string;
23
22
  export declare function findLatticeDir(from?: string): string | null;
24
23
  export declare function findProjectRoot(from?: string): string | null;
25
24
  export declare function listLatticeFiles(latticeDir: string): Promise<string[]>;
@@ -15,9 +15,6 @@ export function parseFrontmatter(content) {
15
15
  }
16
16
  return result;
17
17
  }
18
- export function stripFrontmatter(content) {
19
- return content.replace(/^---\n[\s\S]*?\n---\n*/, '');
20
- }
21
18
  export function findLatticeDir(from) {
22
19
  let dir = resolve(from ?? process.cwd());
23
20
  while (true) {
@@ -69,7 +66,7 @@ function lastLine(content) {
69
66
  return lines[lines.length - 1] === '' ? lines.length - 1 : lines.length;
70
67
  }
71
68
  export function parseSections(filePath, content, projectRoot) {
72
- const tree = parse(stripFrontmatter(content));
69
+ const tree = parse(content);
73
70
  const file = projectRoot
74
71
  ? relative(projectRoot, filePath).replace(/\.md$/, '')
75
72
  : basename(filePath, '.md');
@@ -522,7 +519,7 @@ export function findSections(sections, query) {
522
519
  ];
523
520
  }
524
521
  export function extractRefs(filePath, content, projectRoot) {
525
- const tree = parse(stripFrontmatter(content));
522
+ const tree = parse(content);
526
523
  const file = projectRoot
527
524
  ? relative(projectRoot, filePath).replace(/\.md$/, '')
528
525
  : basename(filePath, '.md');
@@ -1,9 +1,11 @@
1
1
  import { unified } from 'unified';
2
2
  import remarkParse from 'remark-parse';
3
3
  import remarkStringify from 'remark-stringify';
4
+ import remarkFrontmatter from 'remark-frontmatter';
4
5
  import { wikiLinkSyntax, wikiLinkFromMarkdown, wikiLinkToMarkdown, } from './extensions/wiki-link/index.js';
5
6
  const processor = unified()
6
7
  .use(remarkParse)
8
+ .use(remarkFrontmatter)
7
9
  .use(remarkStringify)
8
10
  .data('micromarkExtensions', [wikiLinkSyntax()])
9
11
  .data('fromMarkdownExtensions', [wikiLinkFromMarkdown()])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lat.md",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "A knowledge graph for your codebase, written in markdown",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.30.2",
@@ -55,6 +55,7 @@
55
55
  "commander": "^14.0.3",
56
56
  "ignore-walk": "^8.0.0",
57
57
  "mdast-util-to-markdown": "^2.1.0",
58
+ "remark-frontmatter": "^5.0.0",
58
59
  "remark-parse": "^11.0.0",
59
60
  "remark-stringify": "^11.0.0",
60
61
  "unified": "^11.0.0",