@plebeiantech/lat.md 0.12.3-fork.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.
Files changed (144) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +97 -0
  3. package/dist/src/cli/check-frontmatter.d.ts +12 -0
  4. package/dist/src/cli/check-frontmatter.js +44 -0
  5. package/dist/src/cli/check-mode.d.ts +6 -0
  6. package/dist/src/cli/check-mode.js +303 -0
  7. package/dist/src/cli/check-status.d.ts +74 -0
  8. package/dist/src/cli/check-status.js +217 -0
  9. package/dist/src/cli/check.d.ts +45 -0
  10. package/dist/src/cli/check.js +777 -0
  11. package/dist/src/cli/checklist-menu.d.ts +12 -0
  12. package/dist/src/cli/checklist-menu.js +111 -0
  13. package/dist/src/cli/comment-guard.d.ts +16 -0
  14. package/dist/src/cli/comment-guard.js +111 -0
  15. package/dist/src/cli/comment-reminder.d.ts +62 -0
  16. package/dist/src/cli/comment-reminder.js +210 -0
  17. package/dist/src/cli/context.d.ts +11 -0
  18. package/dist/src/cli/context.js +58 -0
  19. package/dist/src/cli/expand.d.ts +8 -0
  20. package/dist/src/cli/expand.js +94 -0
  21. package/dist/src/cli/gen-index.d.ts +45 -0
  22. package/dist/src/cli/gen-index.js +235 -0
  23. package/dist/src/cli/gen.d.ts +6 -0
  24. package/dist/src/cli/gen.js +42 -0
  25. package/dist/src/cli/graph.d.ts +7 -0
  26. package/dist/src/cli/graph.js +47 -0
  27. package/dist/src/cli/hook.d.ts +1 -0
  28. package/dist/src/cli/hook.js +432 -0
  29. package/dist/src/cli/index.d.ts +2 -0
  30. package/dist/src/cli/index.js +307 -0
  31. package/dist/src/cli/init.d.ts +9 -0
  32. package/dist/src/cli/init.js +1031 -0
  33. package/dist/src/cli/link-scheme.d.ts +54 -0
  34. package/dist/src/cli/link-scheme.js +101 -0
  35. package/dist/src/cli/locate.d.ts +2 -0
  36. package/dist/src/cli/locate.js +17 -0
  37. package/dist/src/cli/refs.d.ts +22 -0
  38. package/dist/src/cli/refs.js +229 -0
  39. package/dist/src/cli/reindex.d.ts +13 -0
  40. package/dist/src/cli/reindex.js +136 -0
  41. package/dist/src/cli/search.d.ts +36 -0
  42. package/dist/src/cli/search.js +217 -0
  43. package/dist/src/cli/section.d.ts +43 -0
  44. package/dist/src/cli/section.js +252 -0
  45. package/dist/src/cli/select-menu.d.ts +13 -0
  46. package/dist/src/cli/select-menu.js +100 -0
  47. package/dist/src/cli/templates.d.ts +1 -0
  48. package/dist/src/cli/templates.js +15 -0
  49. package/dist/src/cli/ui.d.ts +11 -0
  50. package/dist/src/cli/ui.js +34 -0
  51. package/dist/src/code-refs.d.ts +17 -0
  52. package/dist/src/code-refs.js +277 -0
  53. package/dist/src/config.d.ts +29 -0
  54. package/dist/src/config.js +83 -0
  55. package/dist/src/context.d.ts +23 -0
  56. package/dist/src/context.js +11 -0
  57. package/dist/src/extensions/wiki-link/from-markdown.d.ts +5 -0
  58. package/dist/src/extensions/wiki-link/from-markdown.js +34 -0
  59. package/dist/src/extensions/wiki-link/index.d.ts +4 -0
  60. package/dist/src/extensions/wiki-link/index.js +3 -0
  61. package/dist/src/extensions/wiki-link/syntax.d.ts +14 -0
  62. package/dist/src/extensions/wiki-link/syntax.js +121 -0
  63. package/dist/src/extensions/wiki-link/to-markdown.d.ts +5 -0
  64. package/dist/src/extensions/wiki-link/to-markdown.js +28 -0
  65. package/dist/src/extensions/wiki-link/types.d.ts +34 -0
  66. package/dist/src/extensions/wiki-link/types.js +1 -0
  67. package/dist/src/format.d.ts +8 -0
  68. package/dist/src/format.js +43 -0
  69. package/dist/src/graph-export.d.ts +60 -0
  70. package/dist/src/graph-export.js +309 -0
  71. package/dist/src/init-version.d.ts +10 -0
  72. package/dist/src/init-version.js +49 -0
  73. package/dist/src/knowledge/bd.d.ts +2 -0
  74. package/dist/src/knowledge/bd.js +107 -0
  75. package/dist/src/knowledge/claude-memory.d.ts +13 -0
  76. package/dist/src/knowledge/claude-memory.js +204 -0
  77. package/dist/src/knowledge/cq.d.ts +2 -0
  78. package/dist/src/knowledge/cq.js +74 -0
  79. package/dist/src/knowledge/index.d.ts +49 -0
  80. package/dist/src/knowledge/index.js +271 -0
  81. package/dist/src/knowledge/ranking.d.ts +17 -0
  82. package/dist/src/knowledge/ranking.js +62 -0
  83. package/dist/src/knowledge/session.d.ts +46 -0
  84. package/dist/src/knowledge/session.js +203 -0
  85. package/dist/src/knowledge/types.d.ts +47 -0
  86. package/dist/src/knowledge/types.js +10 -0
  87. package/dist/src/lattice.d.ts +108 -0
  88. package/dist/src/lattice.js +816 -0
  89. package/dist/src/mcp/server.d.ts +1 -0
  90. package/dist/src/mcp/server.js +58 -0
  91. package/dist/src/parser.d.ts +3 -0
  92. package/dist/src/parser.js +18 -0
  93. package/dist/src/search/db.d.ts +16 -0
  94. package/dist/src/search/db.js +54 -0
  95. package/dist/src/search/embedder.d.ts +25 -0
  96. package/dist/src/search/embedder.js +58 -0
  97. package/dist/src/search/index.d.ts +9 -0
  98. package/dist/src/search/index.js +66 -0
  99. package/dist/src/search/search.d.ts +9 -0
  100. package/dist/src/search/search.js +16 -0
  101. package/dist/src/source-parser.d.ts +27 -0
  102. package/dist/src/source-parser.js +916 -0
  103. package/dist/src/untrusted.d.ts +24 -0
  104. package/dist/src/untrusted.js +97 -0
  105. package/dist/src/version.d.ts +7 -0
  106. package/dist/src/version.js +39 -0
  107. package/dist/src/view/client/assets/index-B0n0u03y.css +1 -0
  108. package/dist/src/view/client/assets/index-xm2_px4q.js +23 -0
  109. package/dist/src/view/client/index.html +14 -0
  110. package/dist/src/view/diagnostics.d.ts +5 -0
  111. package/dist/src/view/diagnostics.js +171 -0
  112. package/dist/src/view/git-diff.d.ts +3 -0
  113. package/dist/src/view/git-diff.js +255 -0
  114. package/dist/src/view/git.d.ts +22 -0
  115. package/dist/src/view/git.js +193 -0
  116. package/dist/src/view/highlight.d.ts +2 -0
  117. package/dist/src/view/highlight.js +3767 -0
  118. package/dist/src/view/markdown.d.ts +22 -0
  119. package/dist/src/view/markdown.js +322 -0
  120. package/dist/src/view/protocol.d.ts +89 -0
  121. package/dist/src/view/protocol.js +1 -0
  122. package/dist/src/view/references.d.ts +62 -0
  123. package/dist/src/view/references.js +345 -0
  124. package/dist/src/view/repository.d.ts +12 -0
  125. package/dist/src/view/repository.js +167 -0
  126. package/dist/src/view/search.d.ts +14 -0
  127. package/dist/src/view/search.js +64 -0
  128. package/dist/src/view/server.d.ts +20 -0
  129. package/dist/src/view/server.js +233 -0
  130. package/dist/src/view/store.d.ts +57 -0
  131. package/dist/src/view/store.js +404 -0
  132. package/dist/src/walk.d.ts +18 -0
  133. package/dist/src/walk.js +27 -0
  134. package/package.json +85 -0
  135. package/templates/AGENTS.md +118 -0
  136. package/templates/README +1 -0
  137. package/templates/cursor-rules.md +77 -0
  138. package/templates/init/lat.md +1 -0
  139. package/templates/logo-dark.svg +182 -0
  140. package/templates/logo-light.svg +182 -0
  141. package/templates/logo.txt +7 -0
  142. package/templates/opencode-plugin.ts +180 -0
  143. package/templates/pi-extension.ts +364 -0
  144. package/templates/skill/SKILL.md +189 -0
@@ -0,0 +1,364 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
3
+ import { getMarkdownTheme, keyHint } from "@mariozechner/pi-coding-agent";
4
+ import type { Theme } from "@mariozechner/pi-tui";
5
+ import { Box, Markdown, Text } from "@mariozechner/pi-tui";
6
+
7
+ const PREVIEW_LINES = 4;
8
+
9
+ function collapsibleResult(
10
+ result: { content: Array<{ type: string; text?: string }> },
11
+ options: { expanded: boolean; isPartial: boolean },
12
+ theme: Theme,
13
+ ) {
14
+ const text = result.content?.[0]?.type === "text" ? (result.content[0] as { type: "text"; text: string }).text : "";
15
+ if (!text) return new Text(theme.fg("dim", "(empty)"), 0, 0);
16
+ if (options.isPartial) return new Text(theme.fg("dim", "…"), 0, 0);
17
+ const mdTheme = getMarkdownTheme();
18
+ if (options.expanded) return new Markdown(text, 0, 0, mdTheme);
19
+
20
+ const lines = text.split("\n");
21
+ if (lines.length <= PREVIEW_LINES) return new Markdown(text, 0, 0, mdTheme);
22
+
23
+ const preview = lines.slice(0, PREVIEW_LINES).join("\n");
24
+ const remaining = lines.length - PREVIEW_LINES;
25
+ const hint = keyHint("expandTools", "to expand");
26
+ return new Text(
27
+ preview + "\n" +
28
+ theme.fg("dim", `… ${remaining} more lines (${hint})`),
29
+ 0, 0,
30
+ );
31
+ }
32
+
33
+ /** Absolute path to the lat binary, injected by `lat init`. */
34
+ const LAT = "__LAT_BIN__";
35
+
36
+ function run(args: string[], cwd?: string): string {
37
+ const { execSync } = require("child_process") as typeof import("child_process");
38
+ return execSync(`${LAT} ${args.join(" ")}`, {
39
+ cwd: cwd ?? process.cwd(),
40
+ encoding: "utf-8",
41
+ timeout: 30_000,
42
+ });
43
+ }
44
+
45
+ function tryRun(args: string[]): string {
46
+ try {
47
+ return run(args);
48
+ } catch {
49
+ return "";
50
+ }
51
+ }
52
+
53
+ export default function (pi: ExtensionAPI) {
54
+ // ── Tools ──────────────────────────────────────────────────────────
55
+
56
+ pi.registerTool({
57
+ name: "lat_search",
58
+ label: "lat search",
59
+ description: "Semantic search across lat.md sections using embeddings",
60
+ promptSnippet: "Search lat.md documentation by meaning",
61
+ promptGuidelines: [
62
+ "Use before starting any task to find relevant design context",
63
+ "Search results include section IDs you can pass to lat_section",
64
+ ],
65
+ parameters: Type.Object({
66
+ query: Type.String({ description: "Search query in natural language" }),
67
+ limit: Type.Optional(
68
+ Type.Number({ description: "Max results (default 5)", default: 5 }),
69
+ ),
70
+ }),
71
+ async execute(_id, params) {
72
+ const args = ["search", JSON.stringify(params.query)];
73
+ if (params.limit) args.push("--limit", String(params.limit));
74
+ const output = tryRun(args);
75
+ return {
76
+ content: [{ type: "text", text: output || "No results found." }],
77
+ };
78
+ },
79
+ renderCall(args, theme) {
80
+ return new Text(
81
+ theme.fg("toolTitle", theme.bold("lat search ")) +
82
+ theme.fg("dim", `"${args.query}"`),
83
+ 0, 0,
84
+ );
85
+ },
86
+ renderResult: collapsibleResult,
87
+ });
88
+
89
+ pi.registerTool({
90
+ name: "lat_section",
91
+ label: "lat section",
92
+ description:
93
+ "Show full content of a lat.md section with outgoing/incoming refs",
94
+ promptSnippet: "Read a specific lat.md section",
95
+ parameters: Type.Object({
96
+ query: Type.String({
97
+ description:
98
+ 'Section ID or name (e.g. "cli#init", "Tests#User login")',
99
+ }),
100
+ }),
101
+ async execute(_id, params) {
102
+ const output = tryRun(["section", JSON.stringify(params.query)]);
103
+ return {
104
+ content: [
105
+ { type: "text", text: output || "Section not found." },
106
+ ],
107
+ };
108
+ },
109
+ renderCall(args, theme) {
110
+ return new Text(
111
+ theme.fg("toolTitle", theme.bold("lat section ")) +
112
+ theme.fg("dim", `"${args.query}"`),
113
+ 0, 0,
114
+ );
115
+ },
116
+ renderResult: collapsibleResult,
117
+ });
118
+
119
+ pi.registerTool({
120
+ name: "lat_locate",
121
+ label: "lat locate",
122
+ description:
123
+ "Find a section by name (exact, subsection tail, or fuzzy match)",
124
+ promptSnippet: "Find a lat.md section by name",
125
+ parameters: Type.Object({
126
+ query: Type.String({ description: "Section name to locate" }),
127
+ }),
128
+ async execute(_id, params) {
129
+ const output = tryRun(["locate", JSON.stringify(params.query)]);
130
+ return {
131
+ content: [
132
+ { type: "text", text: output || "No sections matching query." },
133
+ ],
134
+ };
135
+ },
136
+ renderCall(args, theme) {
137
+ return new Text(
138
+ theme.fg("toolTitle", theme.bold("lat locate ")) +
139
+ theme.fg("dim", `"${args.query}"`),
140
+ 0, 0,
141
+ );
142
+ },
143
+ });
144
+
145
+ pi.registerTool({
146
+ name: "lat_check",
147
+ label: "lat check",
148
+ description:
149
+ "Run full lat.md validation. Returns errors or 'All checks passed'",
150
+ promptSnippet: "Run full lat.md validation",
151
+ parameters: Type.Object({}),
152
+ async execute() {
153
+ try {
154
+ const output = run(["check"]);
155
+ return { content: [{ type: "text", text: output }] };
156
+ } catch (err: unknown) {
157
+ const e = err as { stdout?: string; stderr?: string };
158
+ return {
159
+ content: [{ type: "text", text: e.stdout || e.stderr || "Check failed" }],
160
+ isError: true,
161
+ };
162
+ }
163
+ },
164
+ renderCall(_args, theme) {
165
+ return new Text(
166
+ theme.fg("toolTitle", theme.bold("lat check")),
167
+ 0, 0,
168
+ );
169
+ },
170
+ });
171
+
172
+ pi.registerTool({
173
+ name: "lat_expand",
174
+ label: "lat expand",
175
+ description:
176
+ "Expand [[refs]] in text to resolved file locations and context",
177
+ promptSnippet: "Resolve [[wiki links]] in text",
178
+ parameters: Type.Object({
179
+ text: Type.String({ description: "Text containing [[refs]] to expand" }),
180
+ }),
181
+ async execute(_id, params) {
182
+ const output = tryRun(["expand", JSON.stringify(params.text)]);
183
+ return {
184
+ content: [{ type: "text", text: output || params.text }],
185
+ };
186
+ },
187
+ renderCall(args, theme) {
188
+ const preview = args.text.length > 60 ? args.text.slice(0, 60) + "…" : args.text;
189
+ return new Text(
190
+ theme.fg("toolTitle", theme.bold("lat expand ")) +
191
+ theme.fg("dim", `"${preview}"`),
192
+ 0, 0,
193
+ );
194
+ },
195
+ });
196
+
197
+ pi.registerTool({
198
+ name: "lat_refs",
199
+ label: "lat refs",
200
+ description: "Find what references a given section",
201
+ promptSnippet: "Find incoming references to a lat.md section",
202
+ parameters: Type.Object({
203
+ query: Type.String({
204
+ description: 'Section ID (e.g. "cli#init", "file#Section")',
205
+ }),
206
+ }),
207
+ async execute(_id, params) {
208
+ const output = tryRun(["refs", JSON.stringify(params.query)]);
209
+ return {
210
+ content: [{ type: "text", text: output || "No references found." }],
211
+ };
212
+ },
213
+ renderCall(args, theme) {
214
+ return new Text(
215
+ theme.fg("toolTitle", theme.bold("lat refs ")) +
216
+ theme.fg("dim", `"${args.query}"`),
217
+ 0, 0,
218
+ );
219
+ },
220
+ });
221
+
222
+ // ── Message renderers ────────────────────────────────────────────
223
+
224
+ pi.registerMessageRenderer("lat-reminder", (message, { expanded }, theme) => {
225
+ const box = new Box(1, 1, (t) => theme.bg("customMessageBg", t));
226
+ if (expanded) {
227
+ box.addChild(new Text(theme.fg("accent", "lat.md"), 0, 0));
228
+ box.addChild(new Markdown(message.content, 0, 0, getMarkdownTheme()));
229
+ } else {
230
+ const hint = keyHint("expandTools", "to expand");
231
+ box.addChild(new Text(
232
+ theme.fg("accent", "lat.md") + " " +
233
+ theme.fg("dim", `Search lat.md before starting work. Keep lat.md/ focused. (${hint})`),
234
+ 0, 0,
235
+ ));
236
+ }
237
+ return box;
238
+ });
239
+
240
+ pi.registerMessageRenderer("lat-check", (message, { expanded }, theme) => {
241
+ const box = new Box(1, 1, (t) => theme.bg("customMessageBg", t));
242
+ if (expanded) {
243
+ box.addChild(new Text(theme.fg("warning", "lat check"), 0, 0));
244
+ box.addChild(new Markdown(message.content, 0, 0, getMarkdownTheme()));
245
+ } else {
246
+ const hint = keyHint("expandTools", "to expand");
247
+ const firstLine = message.content.split("\n")[0];
248
+ box.addChild(new Text(
249
+ theme.fg("warning", "lat check") + " " +
250
+ theme.fg("dim", `${firstLine} (${hint})`),
251
+ 0, 0,
252
+ ));
253
+ }
254
+ return box;
255
+ });
256
+
257
+ // ── Lifecycle hooks ────────────────────────────────────────────────
258
+ //
259
+ // Convention, not automated here: a code comment that explains WHY (a
260
+ // reason, trade-off, rejected alternative, or invariant) belongs in a
261
+ // lat.md/ section, with a `// @lat: [[section-id]]` pointer (comment-prefixed
262
+ // per language, naming the section in double brackets) left behind in the
263
+ // code. `lat hook claude PostToolUse` enforces this as a reminder after
264
+ // every Edit/Write in Claude Code and Codex; Pi has no per-edit hook wired
265
+ // up yet.
266
+
267
+ // Guard to prevent agent_end from firing twice per prompt (infinite loop)
268
+ let agentEndFired = false;
269
+
270
+ pi.on("before_agent_start", async () => {
271
+ agentEndFired = false;
272
+
273
+ const reminder = [
274
+ "Before starting work, run `lat_search` with one or more queries describing the user's intent.",
275
+ "ALWAYS do this, even when the task seems straightforward — search results may reveal critical design details, protocols, or constraints.",
276
+ "Use `lat_section` to read the full content of relevant matches.",
277
+ "Do not read files, write code, or run commands until you have searched.",
278
+ "",
279
+ "Remember: `lat.md/` must stay in sync with meaningful codebase state. If you change behavior, architecture, tests, or planned work, update the relevant current-state sections and run `lat_check` before finishing. Do not use `lat.md/` as a journal/changelog or add notes for insignificant details.",
280
+ ].join("\n");
281
+
282
+ return {
283
+ message: {
284
+ customType: "lat-reminder",
285
+ content: reminder,
286
+ display: true,
287
+ },
288
+ };
289
+ });
290
+
291
+ pi.on("agent_end", async () => {
292
+ // Don't fire twice per prompt — prevents infinite loop
293
+ if (agentEndFired) return;
294
+ agentEndFired = true;
295
+
296
+ // Run lat check
297
+ let checkOutput: string;
298
+ let checkFailed = false;
299
+ try {
300
+ checkOutput = run(["check"]);
301
+ } catch (err: unknown) {
302
+ checkFailed = true;
303
+ checkOutput = (err as { stdout?: string }).stdout || "";
304
+ }
305
+
306
+ // Run git diff --numstat to check if lat.md/ is in sync
307
+ let needsSync = false;
308
+ let codeLines = 0;
309
+ try {
310
+ const { execSync } = require("child_process") as typeof import("child_process");
311
+ const numstat = execSync("git diff HEAD --numstat", {
312
+ encoding: "utf-8",
313
+ cwd: process.cwd(),
314
+ });
315
+
316
+ let latMdLines = 0;
317
+ for (const line of numstat.split("\n")) {
318
+ const parts = line.split("\t");
319
+ if (parts.length < 3) continue;
320
+ const added = parseInt(parts[0], 10) || 0;
321
+ const removed = parseInt(parts[1], 10) || 0;
322
+ const file = parts[2];
323
+ const changed = added + removed;
324
+ if (file.startsWith("lat.md/")) {
325
+ latMdLines += changed;
326
+ } else if (/\.(ts|tsx|js|jsx|py|rs|go|c|h)$/.test(file)) {
327
+ codeLines += changed;
328
+ }
329
+ }
330
+
331
+ if (codeLines >= 5) {
332
+ const effectiveLatMd = latMdLines === 0 ? 0 : Math.max(latMdLines, 1);
333
+ needsSync = effectiveLatMd < codeLines * 0.05;
334
+ }
335
+ } catch {
336
+ // git not available or no HEAD — skip diff check
337
+ }
338
+
339
+ if (!checkFailed && !needsSync) return;
340
+
341
+ const parts: string[] = [];
342
+ if (checkFailed && needsSync) {
343
+ parts.push(
344
+ `\`lat check\` found errors AND the codebase has changes (${codeLines} lines) with no updates to \`lat.md/\`. Before finishing:`,
345
+ "",
346
+ "1. Update relevant current-state `lat.md/` sections if the changes affect behavior, architecture, tests, or plans; do not add journal/changelog notes.",
347
+ "2. Run `lat_check` until it passes.",
348
+ );
349
+ } else if (checkFailed) {
350
+ parts.push(
351
+ "`lat check` failed. Run `lat_check`, fix the errors, and repeat until it passes.",
352
+ );
353
+ } else {
354
+ parts.push(
355
+ `The codebase has changes (${codeLines} lines) but \`lat.md/\` was not updated. Review whether current-state \`lat.md/\` sections need updates; do not add journal/changelog notes just to satisfy this reminder. Run \`lat_search\` to find relevant sections and \`lat_check\` at the end.`,
356
+ );
357
+ }
358
+
359
+ pi.sendMessage(
360
+ { customType: "lat-check", content: parts.join("\n"), display: true },
361
+ { deliverAs: "followUp", triggerTurn: true },
362
+ );
363
+ });
364
+ }
@@ -0,0 +1,189 @@
1
+ ---
2
+ name: lat-md
3
+ description: >-
4
+ Writing and maintaining lat.md documentation files — structured markdown that
5
+ describes a project's architecture, design decisions, and test specs. Use when
6
+ creating, editing, or reviewing files in the lat.md/ directory.
7
+ ---
8
+
9
+ # lat.md Authoring Guide
10
+
11
+ This skill covers the syntax, structure rules, and conventions for writing `lat.md/` files. Load it whenever you need to create or edit sections in the `lat.md/` directory.
12
+
13
+ ## What belongs in lat.md
14
+
15
+ `lat.md/` files describe **what** the project does and **why** — domain concepts, key design decisions, business logic, and test specifications. They do NOT duplicate source code. Think of each section as an anchor that source code references back to.
16
+
17
+ Treat `lat.md/` as a focused snapshot of the current state of affairs or planned features. Do not use it as a journal or changelog, and do not grow it just to record insignificant implementation details.
18
+
19
+ Good candidates for sections:
20
+ - Architecture decisions and their rationale
21
+ - Domain concepts and business rules
22
+ - API contracts and protocols
23
+ - Test specifications (what is tested and why)
24
+ - Non-obvious constraints or invariants
25
+
26
+ Bad candidates:
27
+ - Step-by-step code walkthroughs (the code itself is the walkthrough)
28
+ - Auto-generated API docs (use tools for that)
29
+ - Journal/changelog entries for each change
30
+ - Temporary notes or TODOs
31
+ - Insignificant implementation details that do not change the current-state model
32
+
33
+ ## Section structure
34
+
35
+ Every section **must** have a leading paragraph — at least one sentence immediately after the heading, before any child headings or other block content.
36
+
37
+ The first paragraph must be ≤250 characters (excluding `[[wiki link]]` content). This paragraph is the section's identity — it appears in search results, command output, and RAG context.
38
+
39
+ ```markdown
40
+ # Good Section
41
+
42
+ Brief overview of what this section documents and why it matters.
43
+
44
+ More detail can go in subsequent paragraphs, code blocks, or lists.
45
+
46
+ ## Child heading
47
+
48
+ Details about this child topic.
49
+ ```
50
+
51
+ ```markdown
52
+ # Bad Section
53
+
54
+ ## Child heading
55
+
56
+ This is invalid — "Bad Section" has no leading paragraph.
57
+ ```
58
+
59
+ `lat check` enforces this rule.
60
+
61
+ ## Section IDs
62
+
63
+ Sections are addressed by file path and heading chain:
64
+
65
+ - **Full form**: `lat.md/path/to/file#Heading#SubHeading`
66
+ - **Short form**: `file#Heading#SubHeading` (when the file stem is unique)
67
+
68
+ Examples: `lat.md/tests/search#RAG Replay Tests`, `cli#init`, `parser#Wiki Links`.
69
+
70
+ ## Wiki links
71
+
72
+ Cross-reference other sections or source code with `[[target]]` or `[[target|alias]]`.
73
+
74
+ ### Section links
75
+
76
+ ```markdown
77
+ See [[cli#init]] for setup details.
78
+ The parser validates [[parser#Wiki Links|wiki link syntax]].
79
+ ```
80
+
81
+ ### Source code links
82
+
83
+ Reference functions, classes, constants, and methods in source files:
84
+
85
+ ```markdown
86
+ [[src/config.ts#getConfigDir]] — function
87
+ [[src/server.ts#App#listen]] — class method
88
+ [[lib/utils.py#parse_args]] — Python function
89
+ [[src/lib.rs#Greeter#greet]] — Rust impl method
90
+ [[src/app.go#Greeter#Greet]] — Go method
91
+ [[src/app.h#Greeter]] — C struct
92
+ ```
93
+
94
+ `lat check` validates that all targets exist.
95
+
96
+ ## Code refs
97
+
98
+ Tie source code back to `lat.md/` sections with `@lat:` comments:
99
+
100
+ ```typescript
101
+ // @lat: [[cli#init]]
102
+ export function init() { ... }
103
+ ```
104
+
105
+ ```python
106
+ # @lat: [[cli#init]]
107
+ def init():
108
+ ...
109
+ ```
110
+
111
+ Supported comment styles: `//` (JS/TS/Rust/Go/C) and `#` (Python).
112
+
113
+ Place one `@lat:` comment per section, at the relevant code — not at the top of the file.
114
+
115
+ ## Test specs
116
+
117
+ Describe tests as sections in `lat.md/` files. Add frontmatter to require that every leaf section has a matching `@lat:` comment in test code:
118
+
119
+ ```markdown
120
+ ---
121
+ lat:
122
+ require-code-mention: true
123
+ ---
124
+ # Tests
125
+
126
+ Authentication test specifications.
127
+
128
+ ## User login
129
+
130
+ Verify credential validation and error handling.
131
+
132
+ ### Rejects expired tokens
133
+
134
+ Tokens past their expiry timestamp are rejected with 401, even if otherwise valid.
135
+
136
+ ### Handles missing password
137
+
138
+ Login request without a password field returns 400 with a descriptive error.
139
+ ```
140
+
141
+ Each test references its spec:
142
+
143
+ ```python
144
+ # @lat: [[tests#User login#Rejects expired tokens]]
145
+ def test_rejects_expired_tokens():
146
+ ...
147
+ ```
148
+
149
+ Rules:
150
+ - Every leaf section under `require-code-mention: true` must be referenced by exactly one `@lat:` comment
151
+ - Every section MUST have a description — at least one sentence explaining what the test verifies and why
152
+ - `lat check` flags unreferenced specs and dangling code refs
153
+
154
+ ## Frontmatter
155
+
156
+ Optional YAML frontmatter at the top of `lat.md/` files:
157
+
158
+ ```yaml
159
+ ---
160
+ lat:
161
+ require-code-mention: true
162
+ ---
163
+ ```
164
+
165
+ Supported fields: `require-code-mention` for test spec enforcement, and `mode` for Diátaxis mode declaration.
166
+
167
+ ## Diátaxis modes
168
+
169
+ Documents in `tutorials/`, `how-to/`, `reference/`, or `explanation/` under `lat.md/` are checked against their Diátaxis mode. Declare it with nested frontmatter:
170
+
171
+ ```yaml
172
+ ---
173
+ lat:
174
+ mode: reference
175
+ ---
176
+ ```
177
+
178
+ If the document is in one of the four mode directories, the declared mode must match that directory. Each mode enforces one shape rule: tutorials need an ordered list of steps and a stated outcome; how-to guides need an ordered list of steps; reference docs must not contain narrative prose (no second paragraph under a heading); explanations must not give imperative commands. `lat check mode` validates this.
179
+
180
+ ## Validation
181
+
182
+ Always run `lat check` after editing `lat.md/` files. It validates:
183
+ - All wiki links point to existing sections or source code symbols
184
+ - All relative markdown links point to existing files
185
+ - Full and collapsed reference-style links have definitions
186
+ - All `@lat:` code refs point to existing sections
187
+ - Every section has a leading paragraph (≤250 chars)
188
+ - All `require-code-mention` leaf sections are referenced in code
189
+ - Every `lat.md/` directory has an accurate index