lat.md 0.8.2 → 0.9.0
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/dist/src/cli/gen.d.ts +1 -0
- package/dist/src/cli/gen.js +7 -1
- package/dist/src/cli/init.d.ts +1 -0
- package/dist/src/cli/init.js +36 -4
- package/dist/src/cli/select-menu.js +1 -1
- package/package.json +1 -1
- package/templates/logo.txt +7 -0
- package/templates/skill/SKILL.md +169 -0
package/dist/src/cli/gen.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare function readAgentsTemplate(): string;
|
|
2
2
|
export declare function readCursorRulesTemplate(): string;
|
|
3
3
|
export declare function readPiExtensionTemplate(): string;
|
|
4
|
+
export declare function readSkillTemplate(): string;
|
|
4
5
|
export declare function genCmd(target: string): Promise<void>;
|
package/dist/src/cli/gen.js
CHANGED
|
@@ -10,6 +10,9 @@ export function readCursorRulesTemplate() {
|
|
|
10
10
|
export function readPiExtensionTemplate() {
|
|
11
11
|
return readFileSync(join(findTemplatesDir(), 'pi-extension.ts'), 'utf-8');
|
|
12
12
|
}
|
|
13
|
+
export function readSkillTemplate() {
|
|
14
|
+
return readFileSync(join(findTemplatesDir(), 'skill', 'SKILL.md'), 'utf-8');
|
|
15
|
+
}
|
|
13
16
|
export async function genCmd(target) {
|
|
14
17
|
const normalized = target.toLowerCase();
|
|
15
18
|
switch (normalized) {
|
|
@@ -23,8 +26,11 @@ export async function genCmd(target) {
|
|
|
23
26
|
case 'pi-extension.ts':
|
|
24
27
|
process.stdout.write(readPiExtensionTemplate());
|
|
25
28
|
break;
|
|
29
|
+
case 'skill.md':
|
|
30
|
+
process.stdout.write(readSkillTemplate());
|
|
31
|
+
break;
|
|
26
32
|
default:
|
|
27
|
-
console.error(`Unknown target: ${target}. Supported: agents.md, claude.md, cursor-rules.md, pi-extension.ts`);
|
|
33
|
+
console.error(`Unknown target: ${target}. Supported: agents.md, claude.md, cursor-rules.md, pi-extension.ts, skill.md`);
|
|
28
34
|
process.exit(1);
|
|
29
35
|
}
|
|
30
36
|
}
|
package/dist/src/cli/init.d.ts
CHANGED
package/dist/src/cli/init.js
CHANGED
|
@@ -4,7 +4,7 @@ import { execSync } from 'node:child_process';
|
|
|
4
4
|
import { createInterface } from 'node:readline/promises';
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import { findTemplatesDir } from './templates.js';
|
|
7
|
-
import { readAgentsTemplate, readCursorRulesTemplate, readPiExtensionTemplate, } from './gen.js';
|
|
7
|
+
import { readAgentsTemplate, readCursorRulesTemplate, readPiExtensionTemplate, readSkillTemplate, } from './gen.js';
|
|
8
8
|
import { getLlmKey, getConfigPath, readConfig, writeConfig, } from '../config.js';
|
|
9
9
|
import { writeInitMeta, readFileHash, contentHash } from '../init-version.js';
|
|
10
10
|
import { getLocalVersion, fetchLatestVersion } from '../version.js';
|
|
@@ -314,6 +314,15 @@ async function writeTemplateFile(root, latDir, relPath, template, genTarget, lab
|
|
|
314
314
|
' to see the latest template.');
|
|
315
315
|
return null;
|
|
316
316
|
}
|
|
317
|
+
// ── Shared skill setup ───────────────────────────────────────────────
|
|
318
|
+
async function writeAgentsSkill(root, latDir, hashes, ask) {
|
|
319
|
+
console.log('');
|
|
320
|
+
console.log(chalk.dim(' The lat-md skill teaches the agent how to write and maintain lat.md/ files.'));
|
|
321
|
+
const skillTemplate = readSkillTemplate();
|
|
322
|
+
const skillHash = await writeTemplateFile(root, latDir, '.agents/skills/lat-md/SKILL.md', skillTemplate, 'skill.md', 'Skill (.agents/skills/lat-md/SKILL.md)', ' ', ask);
|
|
323
|
+
if (skillHash)
|
|
324
|
+
hashes['.agents/skills/lat-md/SKILL.md'] = skillHash;
|
|
325
|
+
}
|
|
317
326
|
// ── Per-agent setup ──────────────────────────────────────────────────
|
|
318
327
|
async function setupAgentsMd(root, latDir, template, hashes, ask) {
|
|
319
328
|
const hash = await writeTemplateFile(root, latDir, 'AGENTS.md', template, 'agents.md', 'AGENTS.md', '', ask);
|
|
@@ -334,6 +343,13 @@ async function setupClaudeCode(root, latDir, template, hashes, ask, style) {
|
|
|
334
343
|
mkdirSync(claudeDir, { recursive: true });
|
|
335
344
|
syncLatHooks(settingsPath, style);
|
|
336
345
|
console.log(chalk.green(' Hooks') + ' synced (UserPromptSubmit + Stop)');
|
|
346
|
+
// .claude/skills/lat-md/SKILL.md — skill for authoring lat.md files
|
|
347
|
+
console.log('');
|
|
348
|
+
console.log(chalk.dim(' The lat-md skill teaches the agent how to write and maintain lat.md/ files.'));
|
|
349
|
+
const skillTemplate = readSkillTemplate();
|
|
350
|
+
const skillHash = await writeTemplateFile(root, latDir, '.claude/skills/lat-md/SKILL.md', skillTemplate, 'skill.md', 'Skill (.claude/skills/lat-md/SKILL.md)', ' ', ask);
|
|
351
|
+
if (skillHash)
|
|
352
|
+
hashes['.claude/skills/lat-md/SKILL.md'] = skillHash;
|
|
337
353
|
// Ensure .claude is gitignored (settings contain local absolute paths)
|
|
338
354
|
ensureGitignored(root, '.claude');
|
|
339
355
|
// MCP server → .mcp.json at project root
|
|
@@ -370,6 +386,8 @@ async function setupCursor(root, latDir, hashes, ask, style) {
|
|
|
370
386
|
}
|
|
371
387
|
// Ensure .cursor/mcp.json is gitignored (it contains local absolute paths)
|
|
372
388
|
ensureGitignored(root, '.cursor/mcp.json');
|
|
389
|
+
// .agents/skills/lat-md/SKILL.md — skill for authoring lat.md files
|
|
390
|
+
await writeAgentsSkill(root, latDir, hashes, ask);
|
|
373
391
|
console.log('');
|
|
374
392
|
console.log(chalk.yellow(' Note:') +
|
|
375
393
|
' Enable MCP in Cursor: Settings → Features → MCP → check "Enable MCP"');
|
|
@@ -391,6 +409,8 @@ async function setupCopilot(root, latDir, hashes, ask, style) {
|
|
|
391
409
|
addMcpServer(mcpPath, 'servers', style);
|
|
392
410
|
console.log(chalk.green(' MCP server') + ' registered in .vscode/mcp.json');
|
|
393
411
|
}
|
|
412
|
+
// .agents/skills/lat-md/SKILL.md — skill for authoring lat.md files
|
|
413
|
+
await writeAgentsSkill(root, latDir, hashes, ask);
|
|
394
414
|
}
|
|
395
415
|
async function setupPi(root, latDir, hashes, ask, style) {
|
|
396
416
|
// AGENTS.md — Pi reads this natively
|
|
@@ -403,6 +423,13 @@ async function setupPi(root, latDir, hashes, ask, style) {
|
|
|
403
423
|
const hash = await writeTemplateFile(root, latDir, '.pi/extensions/lat.ts', template, 'pi-extension.ts', 'Extension (.pi/extensions/lat.ts)', ' ', ask);
|
|
404
424
|
if (hash)
|
|
405
425
|
hashes['.pi/extensions/lat.ts'] = hash;
|
|
426
|
+
// .pi/skills/lat-md/SKILL.md — skill for authoring lat.md files
|
|
427
|
+
console.log('');
|
|
428
|
+
console.log(chalk.dim(' The lat-md skill teaches the agent how to write and maintain lat.md/ files.'));
|
|
429
|
+
const skillTemplate = readSkillTemplate();
|
|
430
|
+
const skillHash = await writeTemplateFile(root, latDir, '.pi/skills/lat-md/SKILL.md', skillTemplate, 'skill.md', 'Skill (.pi/skills/lat-md/SKILL.md)', ' ', ask);
|
|
431
|
+
if (skillHash)
|
|
432
|
+
hashes['.pi/skills/lat-md/SKILL.md'] = skillHash;
|
|
406
433
|
// Ensure .pi is gitignored (extension contains local absolute paths)
|
|
407
434
|
ensureGitignored(root, '.pi');
|
|
408
435
|
}
|
|
@@ -477,7 +504,11 @@ async function setupLlmKey(rl) {
|
|
|
477
504
|
console.log(chalk.green(' Key saved') + ' to ' + chalk.dim(getConfigPath()));
|
|
478
505
|
}
|
|
479
506
|
// ── Main init flow ───────────────────────────────────────────────────
|
|
507
|
+
export function readLogo() {
|
|
508
|
+
return readFileSync(join(findTemplatesDir(), 'logo.txt'), 'utf-8');
|
|
509
|
+
}
|
|
480
510
|
export async function initCmd(targetDir) {
|
|
511
|
+
console.log(chalk.cyan(readLogo()));
|
|
481
512
|
// Upfront version check — let the user upgrade before proceeding
|
|
482
513
|
process.stdout.write(chalk.dim('Checking latest version...'));
|
|
483
514
|
const latest = await fetchLatestVersion();
|
|
@@ -554,7 +585,7 @@ export async function initCmd(targetDir) {
|
|
|
554
585
|
{
|
|
555
586
|
label: selectedAgents.length === 0
|
|
556
587
|
? "I don't use any of these"
|
|
557
|
-
: 'This is it:
|
|
588
|
+
: 'This is it: continue',
|
|
558
589
|
value: '__done__',
|
|
559
590
|
accent: true,
|
|
560
591
|
},
|
|
@@ -638,8 +669,9 @@ export async function initCmd(targetDir) {
|
|
|
638
669
|
}
|
|
639
670
|
if (useCodex) {
|
|
640
671
|
console.log('');
|
|
641
|
-
console.log(chalk.bold('Codex / OpenCode')
|
|
642
|
-
|
|
672
|
+
console.log(chalk.bold('Setting up Codex / OpenCode...'));
|
|
673
|
+
console.log(chalk.dim(' Uses AGENTS.md (already created).'));
|
|
674
|
+
await writeAgentsSkill(root, latDir, fileHashes, ask);
|
|
643
675
|
}
|
|
644
676
|
// Step 5: LLM key setup
|
|
645
677
|
await setupLlmKey(rl);
|
|
@@ -32,7 +32,7 @@ export async function selectMenu(options, prompt, defaultIndex) {
|
|
|
32
32
|
const pointer = selected ? '❯' : ' ';
|
|
33
33
|
if (selected) {
|
|
34
34
|
if (opt.accent) {
|
|
35
|
-
lines.push(` ${pointer} ${chalk.
|
|
35
|
+
lines.push(` ${pointer} ${chalk.bgGreen.black.bold(` ${opt.label} `)}`);
|
|
36
36
|
}
|
|
37
37
|
else {
|
|
38
38
|
lines.push(` ${pointer} ${chalk.bgCyan.black.bold(` ${opt.label} `)}`);
|
package/package.json
CHANGED
|
@@ -0,0 +1,169 @@
|
|
|
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
|
+
Good candidates for sections:
|
|
18
|
+
- Architecture decisions and their rationale
|
|
19
|
+
- Domain concepts and business rules
|
|
20
|
+
- API contracts and protocols
|
|
21
|
+
- Test specifications (what is tested and why)
|
|
22
|
+
- Non-obvious constraints or invariants
|
|
23
|
+
|
|
24
|
+
Bad candidates:
|
|
25
|
+
- Step-by-step code walkthroughs (the code itself is the walkthrough)
|
|
26
|
+
- Auto-generated API docs (use tools for that)
|
|
27
|
+
- Temporary notes or TODOs
|
|
28
|
+
|
|
29
|
+
## Section structure
|
|
30
|
+
|
|
31
|
+
Every section **must** have a leading paragraph — at least one sentence immediately after the heading, before any child headings or other block content.
|
|
32
|
+
|
|
33
|
+
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.
|
|
34
|
+
|
|
35
|
+
```markdown
|
|
36
|
+
# Good Section
|
|
37
|
+
|
|
38
|
+
Brief overview of what this section documents and why it matters.
|
|
39
|
+
|
|
40
|
+
More detail can go in subsequent paragraphs, code blocks, or lists.
|
|
41
|
+
|
|
42
|
+
## Child heading
|
|
43
|
+
|
|
44
|
+
Details about this child topic.
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```markdown
|
|
48
|
+
# Bad Section
|
|
49
|
+
|
|
50
|
+
## Child heading
|
|
51
|
+
|
|
52
|
+
This is invalid — "Bad Section" has no leading paragraph.
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`lat check` enforces this rule.
|
|
56
|
+
|
|
57
|
+
## Section IDs
|
|
58
|
+
|
|
59
|
+
Sections are addressed by file path and heading chain:
|
|
60
|
+
|
|
61
|
+
- **Full form**: `lat.md/path/to/file#Heading#SubHeading`
|
|
62
|
+
- **Short form**: `file#Heading#SubHeading` (when the file stem is unique)
|
|
63
|
+
|
|
64
|
+
Examples: `lat.md/tests/search#RAG Replay Tests`, `cli#init`, `parser#Wiki Links`.
|
|
65
|
+
|
|
66
|
+
## Wiki links
|
|
67
|
+
|
|
68
|
+
Cross-reference other sections or source code with `[[target]]` or `[[target|alias]]`.
|
|
69
|
+
|
|
70
|
+
### Section links
|
|
71
|
+
|
|
72
|
+
```markdown
|
|
73
|
+
See [[cli#init]] for setup details.
|
|
74
|
+
The parser validates [[parser#Wiki Links|wiki link syntax]].
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Source code links
|
|
78
|
+
|
|
79
|
+
Reference functions, classes, constants, and methods in source files:
|
|
80
|
+
|
|
81
|
+
```markdown
|
|
82
|
+
[[src/config.ts#getConfigDir]] — function
|
|
83
|
+
[[src/server.ts#App#listen]] — class method
|
|
84
|
+
[[lib/utils.py#parse_args]] — Python function
|
|
85
|
+
[[src/lib.rs#Greeter#greet]] — Rust impl method
|
|
86
|
+
[[src/app.go#Greeter#Greet]] — Go method
|
|
87
|
+
[[src/app.h#Greeter]] — C struct
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`lat check` validates that all targets exist.
|
|
91
|
+
|
|
92
|
+
## Code refs
|
|
93
|
+
|
|
94
|
+
Tie source code back to `lat.md/` sections with `@lat:` comments:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// @lat: [[cli#init]]
|
|
98
|
+
export function init() { ... }
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
# @lat: [[cli#init]]
|
|
103
|
+
def init():
|
|
104
|
+
...
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Supported comment styles: `//` (JS/TS/Rust/Go/C) and `#` (Python).
|
|
108
|
+
|
|
109
|
+
Place one `@lat:` comment per section, at the relevant code — not at the top of the file.
|
|
110
|
+
|
|
111
|
+
## Test specs
|
|
112
|
+
|
|
113
|
+
Describe tests as sections in `lat.md/` files. Add frontmatter to require that every leaf section has a matching `@lat:` comment in test code:
|
|
114
|
+
|
|
115
|
+
```markdown
|
|
116
|
+
---
|
|
117
|
+
lat:
|
|
118
|
+
require-code-mention: true
|
|
119
|
+
---
|
|
120
|
+
# Tests
|
|
121
|
+
|
|
122
|
+
Authentication test specifications.
|
|
123
|
+
|
|
124
|
+
## User login
|
|
125
|
+
|
|
126
|
+
Verify credential validation and error handling.
|
|
127
|
+
|
|
128
|
+
### Rejects expired tokens
|
|
129
|
+
|
|
130
|
+
Tokens past their expiry timestamp are rejected with 401, even if otherwise valid.
|
|
131
|
+
|
|
132
|
+
### Handles missing password
|
|
133
|
+
|
|
134
|
+
Login request without a password field returns 400 with a descriptive error.
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Each test references its spec:
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
# @lat: [[tests#User login#Rejects expired tokens]]
|
|
141
|
+
def test_rejects_expired_tokens():
|
|
142
|
+
...
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Rules:
|
|
146
|
+
- Every leaf section under `require-code-mention: true` must be referenced by exactly one `@lat:` comment
|
|
147
|
+
- Every section MUST have a description — at least one sentence explaining what the test verifies and why
|
|
148
|
+
- `lat check` flags unreferenced specs and dangling code refs
|
|
149
|
+
|
|
150
|
+
## Frontmatter
|
|
151
|
+
|
|
152
|
+
Optional YAML frontmatter at the top of `lat.md/` files:
|
|
153
|
+
|
|
154
|
+
```yaml
|
|
155
|
+
---
|
|
156
|
+
lat:
|
|
157
|
+
require-code-mention: true
|
|
158
|
+
---
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Currently the only supported field is `require-code-mention` for test spec enforcement.
|
|
162
|
+
|
|
163
|
+
## Validation
|
|
164
|
+
|
|
165
|
+
Always run `lat check` after editing `lat.md/` files. It validates:
|
|
166
|
+
- All wiki links point to existing sections or source code symbols
|
|
167
|
+
- All `@lat:` code refs point to existing sections
|
|
168
|
+
- Every section has a leading paragraph (≤250 chars)
|
|
169
|
+
- All `require-code-mention` leaf sections are referenced in code
|