lean-spec 0.1.1 → 0.1.2
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/CHANGELOG.md +40 -0
- package/README.md +25 -24
- package/dist/{chunk-GLXYUS7F.js → chunk-OXTU3PN4.js} +148 -87
- package/dist/chunk-OXTU3PN4.js.map +1 -0
- package/dist/cli.js +13 -13
- package/dist/cli.js.map +1 -1
- package/dist/mcp-server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-GLXYUS7F.js.map +0 -1
- /package/bin/{lspec.js → lean-spec.js} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.2] - 2025-11-10
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
**BREAKING: Command and directory naming migration**
|
|
15
|
+
- **Command name**: `lspec` → `lean-spec` (full name for clarity and consistency)
|
|
16
|
+
- **Config directory**: `.lspec/` → `.lean-spec/` (matches package and command name)
|
|
17
|
+
- **Binary**: Only `lean-spec` command available (removed `lspec` alias)
|
|
18
|
+
|
|
19
|
+
**Benefits:**
|
|
20
|
+
- ✅ Consistency: Package name, command, and config directory all use `lean-spec`
|
|
21
|
+
- ✅ Clarity: `npx lean-spec` works immediately (matches npm package name)
|
|
22
|
+
- ✅ Simplicity: Single command to remember, no abbreviations
|
|
23
|
+
|
|
24
|
+
**Migration Guide for Existing Users:**
|
|
25
|
+
|
|
26
|
+
1. **Uninstall old version:**
|
|
27
|
+
```bash
|
|
28
|
+
npm uninstall -g lean-spec
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
2. **Install new version:**
|
|
32
|
+
```bash
|
|
33
|
+
npm install -g lean-spec
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
3. **Update existing projects:**
|
|
37
|
+
```bash
|
|
38
|
+
# Rename config directory
|
|
39
|
+
mv .lspec .lean-spec
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
4. **Update commands:**
|
|
43
|
+
- Old: `lspec init` → New: `lean-spec init`
|
|
44
|
+
- Old: `lspec board` → New: `lean-spec board`
|
|
45
|
+
- Old: `npx lspec` → New: `npx lean-spec`
|
|
46
|
+
|
|
47
|
+
**All documentation, examples, and specs updated to reflect new naming.**
|
|
48
|
+
|
|
10
49
|
## [0.1.1] - 2025-11-07
|
|
11
50
|
|
|
12
51
|
### Changed
|
|
@@ -88,5 +127,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
88
127
|
- Gray-matter for frontmatter parsing
|
|
89
128
|
- Dayjs for date handling
|
|
90
129
|
|
|
130
|
+
[0.1.2]: https://github.com/codervisor/lean-spec/releases/tag/v0.1.2
|
|
91
131
|
[0.1.1]: https://github.com/codervisor/lean-spec/releases/tag/v0.1.1
|
|
92
132
|
[0.1.0]: https://github.com/codervisor/lean-spec/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ Specs under 300 lines. Intent-focused. Machine-readable. Adapts to your workflow
|
|
|
31
31
|
<p align="center">
|
|
32
32
|
<a href="#quick-start-5-minutes"><strong>Quick Start (5 Minutes) →</strong></a> •
|
|
33
33
|
<a href="https://www.lean-spec.dev"><strong>Documentation</strong></a> •
|
|
34
|
-
<a href="https://www.lean-spec.dev/docs/
|
|
34
|
+
<a href="https://www.lean-spec.dev/docs/guide/ai-executable-patterns"><strong>Examples</strong></a>
|
|
35
35
|
</p>
|
|
36
36
|
|
|
37
37
|
---
|
|
@@ -98,7 +98,7 @@ priority: high
|
|
|
98
98
|
# Unified Dashboard
|
|
99
99
|
|
|
100
100
|
## Overview
|
|
101
|
-
Combine `
|
|
101
|
+
Combine `lean-spec board` and `lean-spec stats` into a single, comprehensive
|
|
102
102
|
project health view. Give users instant insight into project status,
|
|
103
103
|
bottlenecks, and team velocity.
|
|
104
104
|
|
|
@@ -136,9 +136,10 @@ LeanSpec isn't arbitrary rules—it's derived from fundamental constraints of wo
|
|
|
136
136
|
### 🧠 Context Economy
|
|
137
137
|
**Specs <300 lines → Fit in working memory**
|
|
138
138
|
|
|
139
|
-
- **Physics**: AI
|
|
140
|
-
- **Biology**: Human working memory is limited (7±2 items)
|
|
139
|
+
- **Physics**: AI performance degrades with longer context (quality drops beyond 50K tokens despite 200K limits)
|
|
140
|
+
- **Biology**: Human working memory is limited (7±2 items, 5-10 min attention)
|
|
141
141
|
- **Economics**: Large contexts cost more time and money
|
|
142
|
+
- **Reality**: Attention is the scarce resource, not storage
|
|
142
143
|
- **Result**: Keep specs under 300 lines, split complex features
|
|
143
144
|
|
|
144
145
|
### ✂️ Signal-to-Noise Maximization
|
|
@@ -177,7 +178,7 @@ LeanSpec isn't arbitrary rules—it's derived from fundamental constraints of wo
|
|
|
177
178
|
|
|
178
179
|
**These aren't preferences—they're constraints.** Physics (context windows), biology (working memory), and economics (token costs) dictate what works.
|
|
179
180
|
|
|
180
|
-
📖 [Deep dive: First Principles Guide →](https://www.lean-spec.dev/docs/guide/first-principles)
|
|
181
|
+
📖 [Deep dive: First Principles Guide →](https://www.lean-spec.dev/docs/guide/understanding#the-five-first-principles)
|
|
181
182
|
|
|
182
183
|
---
|
|
183
184
|
|
|
@@ -199,7 +200,7 @@ MCP-native specs. Works with any tool that supports Model Context Protocol.
|
|
|
199
200
|
Track progress without leaving the terminal:
|
|
200
201
|
|
|
201
202
|
```bash
|
|
202
|
-
$
|
|
203
|
+
$ lean-spec board
|
|
203
204
|
|
|
204
205
|
📋 Spec Kanban Board
|
|
205
206
|
|
|
@@ -219,7 +220,7 @@ $ lspec board
|
|
|
219
220
|
```
|
|
220
221
|
|
|
221
222
|
```bash
|
|
222
|
-
$
|
|
223
|
+
$ lean-spec stats
|
|
223
224
|
|
|
224
225
|
📊 Project Stats
|
|
225
226
|
|
|
@@ -271,7 +272,7 @@ Custom fields fully supported. Adapts to your workflow as you grow.
|
|
|
271
272
|
```bash
|
|
272
273
|
npm install -g lean-spec
|
|
273
274
|
cd your-project
|
|
274
|
-
|
|
275
|
+
lean-spec init
|
|
275
276
|
```
|
|
276
277
|
|
|
277
278
|
### 2. Work with Your AI Tool
|
|
@@ -281,7 +282,7 @@ lspec init
|
|
|
281
282
|
```
|
|
282
283
|
👤 You: "Create a spec for user authentication with OAuth2."
|
|
283
284
|
|
|
284
|
-
🤖 AI: [runs
|
|
285
|
+
🤖 AI: [runs lean-spec create user-authentication]
|
|
285
286
|
"I've created specs/001-user-authentication/README.md.
|
|
286
287
|
Here's the spec..."
|
|
287
288
|
|
|
@@ -295,19 +296,19 @@ lspec init
|
|
|
295
296
|
|
|
296
297
|
```bash
|
|
297
298
|
# Check project status
|
|
298
|
-
|
|
299
|
+
lean-spec board
|
|
299
300
|
|
|
300
301
|
# View spec with AI-friendly output
|
|
301
|
-
|
|
302
|
+
lean-spec view user-authentication --json
|
|
302
303
|
|
|
303
304
|
# Update status as you progress
|
|
304
|
-
|
|
305
|
+
lean-spec update user-authentication --status in-progress
|
|
305
306
|
```
|
|
306
307
|
|
|
307
308
|
**The workflow:**
|
|
308
|
-
1. ✅ Ask AI to create spec (it uses `
|
|
309
|
+
1. ✅ Ask AI to create spec (it uses `lean-spec create`)
|
|
309
310
|
2. ✅ AI reads spec and implements (spec fits in context)
|
|
310
|
-
3. ✅ Track with `
|
|
311
|
+
3. ✅ Track with `lean-spec board` / `lean-spec stats`
|
|
311
312
|
4. ✅ Update status as work progresses
|
|
312
313
|
|
|
313
314
|
**Why this works:**
|
|
@@ -317,9 +318,9 @@ lspec update user-authentication --status in-progress
|
|
|
317
318
|
- You drive, AI executes
|
|
318
319
|
|
|
319
320
|
**Next steps:**
|
|
320
|
-
- 📘 [Full CLI Reference](https://www.lean-spec.dev/docs/cli
|
|
321
|
-
- 🎨 [Choose a Template](https://www.lean-spec.dev/docs/templates) - Minimal, standard, or enterprise
|
|
322
|
-
- 🤖 [AI Agent Setup](AGENTS.md) - Configure
|
|
321
|
+
- 📘 [Full CLI Reference](https://www.lean-spec.dev/docs/reference/cli) - All commands
|
|
322
|
+
- 🎨 [Choose a Template](https://www.lean-spec.dev/docs/guide/templates) - Minimal, standard, or enterprise
|
|
323
|
+
- 🤖 [AI Agent Setup](AGENTS.md) - Configure AI coding tools
|
|
323
324
|
|
|
324
325
|
---
|
|
325
326
|
|
|
@@ -387,24 +388,24 @@ We dogfood our own methodology. Specs that fit in AI context enable the velocity
|
|
|
387
388
|
## Learn More
|
|
388
389
|
|
|
389
390
|
### 📚 Documentation
|
|
390
|
-
- [Getting Started Guide](https://www.lean-spec.dev/docs/getting-started) - Complete setup walkthrough
|
|
391
|
-
- [First Principles](https://www.lean-spec.dev/docs/guide/first-principles) - The philosophy behind LeanSpec
|
|
392
|
-
- [CLI Reference](https://www.lean-spec.dev/docs/cli
|
|
391
|
+
- [Getting Started Guide](https://www.lean-spec.dev/docs/guide/getting-started) - Complete setup walkthrough
|
|
392
|
+
- [First Principles](https://www.lean-spec.dev/docs/guide/understanding#the-five-first-principles) - The philosophy behind LeanSpec
|
|
393
|
+
- [CLI Reference](https://www.lean-spec.dev/docs/reference/cli) - All commands with examples
|
|
393
394
|
|
|
394
395
|
### 🛠️ Integrations
|
|
395
396
|
- [AI Agent Configuration](AGENTS.md) - Cursor, Copilot, Aider setup
|
|
396
397
|
- [MCP Server](docs/MCP-SERVER.md) - Claude Desktop integration
|
|
397
|
-
- [VS Code Extension](https://www.lean-spec.dev/docs/
|
|
398
|
+
- [VS Code Extension](https://www.lean-spec.dev/docs/roadmap#vs-code-extension) - Enhanced editor support (planned)
|
|
398
399
|
|
|
399
400
|
### 🎓 Guides
|
|
400
401
|
- [Custom Fields](https://www.lean-spec.dev/docs/guide/custom-fields) - Adapt to your workflow
|
|
401
|
-
- [
|
|
402
|
-
- [
|
|
402
|
+
- [Templates](https://www.lean-spec.dev/docs/guide/templates) - Choose the right structure
|
|
403
|
+
- [Frontmatter](https://www.lean-spec.dev/docs/guide/frontmatter) - Metadata and organization
|
|
403
404
|
|
|
404
405
|
### 🤝 Community
|
|
405
406
|
- [GitHub Issues](https://github.com/codervisor/lean-spec/issues) - Report bugs or request features
|
|
406
407
|
- [Contributing Guide](CONTRIBUTING.md) - Join the project
|
|
407
|
-
- [
|
|
408
|
+
- [AI-Executable Patterns](https://www.lean-spec.dev/docs/guide/ai-executable-patterns) - Real-world usage patterns
|
|
408
409
|
|
|
409
410
|
---
|
|
410
411
|
|
|
@@ -39,7 +39,7 @@ var DEFAULT_CONFIG = {
|
|
|
39
39
|
}
|
|
40
40
|
};
|
|
41
41
|
async function loadConfig(cwd = process.cwd()) {
|
|
42
|
-
const configPath = path.join(cwd, ".
|
|
42
|
+
const configPath = path.join(cwd, ".lean-spec", "config.json");
|
|
43
43
|
try {
|
|
44
44
|
const content = await fs.readFile(configPath, "utf-8");
|
|
45
45
|
const userConfig = JSON.parse(content);
|
|
@@ -51,7 +51,7 @@ async function loadConfig(cwd = process.cwd()) {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
async function saveConfig(config, cwd = process.cwd()) {
|
|
54
|
-
const configDir = path.join(cwd, ".
|
|
54
|
+
const configDir = path.join(cwd, ".lean-spec");
|
|
55
55
|
const configPath = path.join(configDir, "config.json");
|
|
56
56
|
await fs.mkdir(configDir, { recursive: true });
|
|
57
57
|
await fs.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
@@ -616,14 +616,14 @@ async function checkSpecs(options = {}) {
|
|
|
616
616
|
console.log("");
|
|
617
617
|
}
|
|
618
618
|
console.log(chalk3.cyan("Tip: Use date prefix to prevent conflicts:"));
|
|
619
|
-
console.log(chalk3.gray(' Edit .
|
|
619
|
+
console.log(chalk3.gray(' Edit .lean-spec/config.json \u2192 structure.prefix: "{YYYYMMDD}-"'));
|
|
620
620
|
console.log("");
|
|
621
621
|
console.log(chalk3.cyan("Or rename folders manually to resolve."));
|
|
622
622
|
console.log("");
|
|
623
623
|
} else {
|
|
624
624
|
console.log("");
|
|
625
625
|
console.log(chalk3.yellow(`\u26A0\uFE0F Conflict warning: ${conflicts.length} sequence conflict(s) detected`));
|
|
626
|
-
console.log(chalk3.gray("Run:
|
|
626
|
+
console.log(chalk3.gray("Run: lean-spec check"));
|
|
627
627
|
console.log("");
|
|
628
628
|
}
|
|
629
629
|
}
|
|
@@ -677,7 +677,7 @@ async function createSpec(name, options = {}) {
|
|
|
677
677
|
}
|
|
678
678
|
}
|
|
679
679
|
await fs5.mkdir(specDir, { recursive: true });
|
|
680
|
-
const templatesDir = path6.join(cwd, ".
|
|
680
|
+
const templatesDir = path6.join(cwd, ".lean-spec", "templates");
|
|
681
681
|
let templateName;
|
|
682
682
|
if (options.template) {
|
|
683
683
|
if (config.templates?.[options.template]) {
|
|
@@ -1351,7 +1351,7 @@ import * as path11 from "path";
|
|
|
1351
1351
|
import chalk9 from "chalk";
|
|
1352
1352
|
async function listTemplates(cwd = process.cwd()) {
|
|
1353
1353
|
const config = await loadConfig(cwd);
|
|
1354
|
-
const templatesDir = path11.join(cwd, ".
|
|
1354
|
+
const templatesDir = path11.join(cwd, ".lean-spec", "templates");
|
|
1355
1355
|
console.log("");
|
|
1356
1356
|
console.log(chalk9.green("=== Project Templates ==="));
|
|
1357
1357
|
console.log("");
|
|
@@ -1397,7 +1397,7 @@ async function showTemplate(templateName, cwd = process.cwd()) {
|
|
|
1397
1397
|
console.error(chalk9.gray(`Available: ${Object.keys(config.templates || {}).join(", ")}`));
|
|
1398
1398
|
process.exit(1);
|
|
1399
1399
|
}
|
|
1400
|
-
const templatesDir = path11.join(cwd, ".
|
|
1400
|
+
const templatesDir = path11.join(cwd, ".lean-spec", "templates");
|
|
1401
1401
|
const templateFile = config.templates[templateName];
|
|
1402
1402
|
const templatePath = path11.join(templatesDir, templateFile);
|
|
1403
1403
|
try {
|
|
@@ -1415,7 +1415,7 @@ async function showTemplate(templateName, cwd = process.cwd()) {
|
|
|
1415
1415
|
}
|
|
1416
1416
|
async function addTemplate(name, file, cwd = process.cwd()) {
|
|
1417
1417
|
const config = await loadConfig(cwd);
|
|
1418
|
-
const templatesDir = path11.join(cwd, ".
|
|
1418
|
+
const templatesDir = path11.join(cwd, ".lean-spec", "templates");
|
|
1419
1419
|
const templatePath = path11.join(templatesDir, file);
|
|
1420
1420
|
try {
|
|
1421
1421
|
await fs8.access(templatePath);
|
|
@@ -1453,11 +1453,11 @@ async function removeTemplate(name, cwd = process.cwd()) {
|
|
|
1453
1453
|
delete config.templates[name];
|
|
1454
1454
|
await saveConfig(config, cwd);
|
|
1455
1455
|
console.log(chalk9.green(`\u2713 Removed template: ${name}`));
|
|
1456
|
-
console.log(chalk9.gray(` Note: Template file ${file} still exists in .
|
|
1456
|
+
console.log(chalk9.gray(` Note: Template file ${file} still exists in .lean-spec/templates/`));
|
|
1457
1457
|
}
|
|
1458
1458
|
async function copyTemplate(source, target, cwd = process.cwd()) {
|
|
1459
1459
|
const config = await loadConfig(cwd);
|
|
1460
|
-
const templatesDir = path11.join(cwd, ".
|
|
1460
|
+
const templatesDir = path11.join(cwd, ".lean-spec", "templates");
|
|
1461
1461
|
let sourceFile;
|
|
1462
1462
|
if (config.templates?.[source]) {
|
|
1463
1463
|
sourceFile = config.templates[source];
|
|
@@ -1523,7 +1523,56 @@ async function handleExistingFiles(action, existingFiles, templateDir, cwd, vari
|
|
|
1523
1523
|
} catch {
|
|
1524
1524
|
continue;
|
|
1525
1525
|
}
|
|
1526
|
-
if (action === "merge" && file === "AGENTS.md") {
|
|
1526
|
+
if (action === "merge-ai" && file === "AGENTS.md") {
|
|
1527
|
+
const existing = await fs9.readFile(filePath, "utf-8");
|
|
1528
|
+
let template = await fs9.readFile(templateFilePath, "utf-8");
|
|
1529
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
1530
|
+
template = template.replace(new RegExp(`\\{${key}\\}`, "g"), value);
|
|
1531
|
+
}
|
|
1532
|
+
const promptPath = path12.join(cwd, ".lean-spec", "MERGE-AGENTS-PROMPT.md");
|
|
1533
|
+
const aiPrompt = `# AI Prompt: Consolidate AGENTS.md
|
|
1534
|
+
|
|
1535
|
+
## Task
|
|
1536
|
+
Consolidate the existing AGENTS.md with LeanSpec instructions into a single, coherent document.
|
|
1537
|
+
|
|
1538
|
+
## Instructions
|
|
1539
|
+
1. Read both documents below
|
|
1540
|
+
2. Merge them intelligently:
|
|
1541
|
+
- Preserve ALL existing project-specific information (workflows, SOPs, architecture, conventions)
|
|
1542
|
+
- Integrate LeanSpec sections where they fit naturally
|
|
1543
|
+
- Remove redundancy and ensure coherent flow
|
|
1544
|
+
- Keep the tone and style consistent
|
|
1545
|
+
3. Replace the existing AGENTS.md with the consolidated version
|
|
1546
|
+
|
|
1547
|
+
## Existing AGENTS.md
|
|
1548
|
+
\`\`\`markdown
|
|
1549
|
+
${existing}
|
|
1550
|
+
\`\`\`
|
|
1551
|
+
|
|
1552
|
+
## LeanSpec Instructions to Integrate
|
|
1553
|
+
\`\`\`markdown
|
|
1554
|
+
${template}
|
|
1555
|
+
\`\`\`
|
|
1556
|
+
|
|
1557
|
+
## Output
|
|
1558
|
+
Create a single consolidated AGENTS.md that:
|
|
1559
|
+
- Keeps all existing project context and workflows
|
|
1560
|
+
- Adds LeanSpec commands and principles where appropriate
|
|
1561
|
+
- Maintains clear structure and readability
|
|
1562
|
+
- Removes any duplicate or conflicting guidance
|
|
1563
|
+
`;
|
|
1564
|
+
await fs9.mkdir(path12.dirname(promptPath), { recursive: true });
|
|
1565
|
+
await fs9.writeFile(promptPath, aiPrompt, "utf-8");
|
|
1566
|
+
console.log(chalk10.green(`\u2713 Created AI consolidation prompt`));
|
|
1567
|
+
console.log(chalk10.cyan(` \u2192 ${promptPath}`));
|
|
1568
|
+
console.log("");
|
|
1569
|
+
console.log(chalk10.yellow("\u{1F4DD} Next steps:"));
|
|
1570
|
+
console.log(chalk10.gray(" 1. Open .lean-spec/MERGE-AGENTS-PROMPT.md"));
|
|
1571
|
+
console.log(chalk10.gray(" 2. Send it to your AI coding assistant (GitHub Copilot, Cursor, etc.)"));
|
|
1572
|
+
console.log(chalk10.gray(" 3. Let AI create the consolidated AGENTS.md"));
|
|
1573
|
+
console.log(chalk10.gray(" 4. Review and commit the result"));
|
|
1574
|
+
console.log("");
|
|
1575
|
+
} else if (action === "merge-append" && file === "AGENTS.md") {
|
|
1527
1576
|
const existing = await fs9.readFile(filePath, "utf-8");
|
|
1528
1577
|
let template = await fs9.readFile(templateFilePath, "utf-8");
|
|
1529
1578
|
for (const [key, value] of Object.entries(variables)) {
|
|
@@ -1537,8 +1586,9 @@ async function handleExistingFiles(action, existingFiles, templateDir, cwd, vari
|
|
|
1537
1586
|
|
|
1538
1587
|
${template.split("\n").slice(1).join("\n")}`;
|
|
1539
1588
|
await fs9.writeFile(filePath, merged, "utf-8");
|
|
1540
|
-
console.log(chalk10.green(`\u2713
|
|
1541
|
-
|
|
1589
|
+
console.log(chalk10.green(`\u2713 Appended LeanSpec section to ${file}`));
|
|
1590
|
+
console.log(chalk10.yellow(" \u26A0 Note: May be verbose. Consider consolidating later."));
|
|
1591
|
+
} else if (action === "overwrite") {
|
|
1542
1592
|
const backupPath = `${filePath}.backup`;
|
|
1543
1593
|
await fs9.rename(filePath, backupPath);
|
|
1544
1594
|
console.log(chalk10.yellow(`\u2713 Backed up ${file} \u2192 ${file}.backup`));
|
|
@@ -1548,6 +1598,7 @@ ${template.split("\n").slice(1).join("\n")}`;
|
|
|
1548
1598
|
}
|
|
1549
1599
|
await fs9.writeFile(filePath, content, "utf-8");
|
|
1550
1600
|
console.log(chalk10.green(`\u2713 Created new ${file}`));
|
|
1601
|
+
console.log(chalk10.gray(` \u{1F4A1} Your original content is preserved in ${file}.backup`));
|
|
1551
1602
|
}
|
|
1552
1603
|
}
|
|
1553
1604
|
}
|
|
@@ -1594,9 +1645,9 @@ var TEMPLATES_DIR = path13.join(__dirname2, "..", "templates");
|
|
|
1594
1645
|
async function initProject() {
|
|
1595
1646
|
const cwd = process.cwd();
|
|
1596
1647
|
try {
|
|
1597
|
-
await fs10.access(path13.join(cwd, ".
|
|
1598
|
-
console.log(chalk11.yellow("LeanSpec already initialized in this directory."));
|
|
1599
|
-
console.log(chalk11.gray("To reinitialize, delete .
|
|
1648
|
+
await fs10.access(path13.join(cwd, ".lean-spec", "config.json"));
|
|
1649
|
+
console.log(chalk11.yellow("\u26A0 LeanSpec already initialized in this directory."));
|
|
1650
|
+
console.log(chalk11.gray("To reinitialize, delete .lean-spec/ directory first."));
|
|
1600
1651
|
return;
|
|
1601
1652
|
} catch {
|
|
1602
1653
|
}
|
|
@@ -1615,12 +1666,13 @@ async function initProject() {
|
|
|
1615
1666
|
name: "Choose template",
|
|
1616
1667
|
value: "template",
|
|
1617
1668
|
description: "Pick from: minimal, standard, enterprise"
|
|
1618
|
-
},
|
|
1619
|
-
{
|
|
1620
|
-
name: "Customize everything",
|
|
1621
|
-
value: "custom",
|
|
1622
|
-
description: "Full control over structure and settings"
|
|
1623
1669
|
}
|
|
1670
|
+
// TODO: Re-enable when custom setup mode is implemented
|
|
1671
|
+
// {
|
|
1672
|
+
// name: 'Customize everything',
|
|
1673
|
+
// value: 'custom',
|
|
1674
|
+
// description: 'Full control over structure and settings',
|
|
1675
|
+
// },
|
|
1624
1676
|
]
|
|
1625
1677
|
});
|
|
1626
1678
|
let templateName = "standard";
|
|
@@ -1637,8 +1689,6 @@ async function initProject() {
|
|
|
1637
1689
|
}
|
|
1638
1690
|
]
|
|
1639
1691
|
});
|
|
1640
|
-
} else if (setupMode === "custom") {
|
|
1641
|
-
console.log(chalk11.yellow("Full customization coming soon. Using standard for now."));
|
|
1642
1692
|
}
|
|
1643
1693
|
const templateDir = path13.join(TEMPLATES_DIR, templateName);
|
|
1644
1694
|
const templateConfigPath = path13.join(templateDir, "config.json");
|
|
@@ -1650,31 +1700,34 @@ async function initProject() {
|
|
|
1650
1700
|
console.error(chalk11.red(`Error: Template not found: ${templateName}`));
|
|
1651
1701
|
process.exit(1);
|
|
1652
1702
|
}
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1703
|
+
let patternChoice = "simple";
|
|
1704
|
+
if (setupMode !== "quick") {
|
|
1705
|
+
patternChoice = await select({
|
|
1706
|
+
message: "Select folder pattern:",
|
|
1707
|
+
choices: [
|
|
1708
|
+
{
|
|
1709
|
+
name: "Simple: 001-my-spec/",
|
|
1710
|
+
value: "simple",
|
|
1711
|
+
description: "Global sequential numbering (recommended)"
|
|
1712
|
+
},
|
|
1713
|
+
{
|
|
1714
|
+
name: "Date-grouped: 20251105/001-my-spec/",
|
|
1715
|
+
value: "date-grouped",
|
|
1716
|
+
description: "Group specs by creation date (good for teams)"
|
|
1717
|
+
},
|
|
1718
|
+
{
|
|
1719
|
+
name: "Flat with date: 20251105-001-my-spec/",
|
|
1720
|
+
value: "date-prefix",
|
|
1721
|
+
description: "Date prefix with global numbering"
|
|
1722
|
+
},
|
|
1723
|
+
{
|
|
1724
|
+
name: "Custom pattern",
|
|
1725
|
+
value: "custom",
|
|
1726
|
+
description: "Enter your own pattern"
|
|
1727
|
+
}
|
|
1728
|
+
]
|
|
1729
|
+
});
|
|
1730
|
+
}
|
|
1678
1731
|
if (patternChoice === "simple") {
|
|
1679
1732
|
templateConfig.structure.pattern = "flat";
|
|
1680
1733
|
templateConfig.structure.prefix = "";
|
|
@@ -1688,13 +1741,13 @@ async function initProject() {
|
|
|
1688
1741
|
} else if (patternChoice === "custom") {
|
|
1689
1742
|
console.log("");
|
|
1690
1743
|
console.log(chalk11.yellow("\u26A0 Custom pattern input is not yet implemented."));
|
|
1691
|
-
console.log(chalk11.gray(" You can manually edit .
|
|
1744
|
+
console.log(chalk11.gray(" You can manually edit .lean-spec/config.json after initialization."));
|
|
1692
1745
|
console.log(chalk11.gray(" Using simple pattern for now."));
|
|
1693
1746
|
console.log("");
|
|
1694
1747
|
templateConfig.structure.pattern = "flat";
|
|
1695
1748
|
templateConfig.structure.prefix = "";
|
|
1696
1749
|
}
|
|
1697
|
-
const templatesDir = path13.join(cwd, ".
|
|
1750
|
+
const templatesDir = path13.join(cwd, ".lean-spec", "templates");
|
|
1698
1751
|
try {
|
|
1699
1752
|
await fs10.mkdir(templatesDir, { recursive: true });
|
|
1700
1753
|
} catch (error) {
|
|
@@ -1705,7 +1758,7 @@ async function initProject() {
|
|
|
1705
1758
|
const targetSpecPath = path13.join(templatesDir, "spec-template.md");
|
|
1706
1759
|
try {
|
|
1707
1760
|
await fs10.copyFile(templateSpecPath, targetSpecPath);
|
|
1708
|
-
console.log(chalk11.green("\u2713 Created .
|
|
1761
|
+
console.log(chalk11.green("\u2713 Created .lean-spec/templates/spec-template.md"));
|
|
1709
1762
|
} catch (error) {
|
|
1710
1763
|
console.error(chalk11.red("Error copying template:"), error);
|
|
1711
1764
|
process.exit(1);
|
|
@@ -1715,29 +1768,34 @@ async function initProject() {
|
|
|
1715
1768
|
default: "spec-template.md"
|
|
1716
1769
|
};
|
|
1717
1770
|
await saveConfig(templateConfig, cwd);
|
|
1718
|
-
console.log(chalk11.green("\u2713 Created .
|
|
1771
|
+
console.log(chalk11.green("\u2713 Created .lean-spec/config.json"));
|
|
1719
1772
|
const existingFiles = await detectExistingSystemPrompts(cwd);
|
|
1720
1773
|
let skipFiles = [];
|
|
1721
1774
|
if (existingFiles.length > 0) {
|
|
1722
1775
|
console.log("");
|
|
1723
1776
|
console.log(chalk11.yellow(`Found existing: ${existingFiles.join(", ")}`));
|
|
1724
1777
|
const action = await select({
|
|
1725
|
-
message: "How would you like to
|
|
1778
|
+
message: "How would you like to handle existing AGENTS.md?",
|
|
1726
1779
|
choices: [
|
|
1727
1780
|
{
|
|
1728
|
-
name: "
|
|
1729
|
-
value: "merge",
|
|
1730
|
-
description: "
|
|
1781
|
+
name: "AI-Assisted Merge (recommended)",
|
|
1782
|
+
value: "merge-ai",
|
|
1783
|
+
description: "Creates prompt for AI to intelligently consolidate both files"
|
|
1731
1784
|
},
|
|
1732
1785
|
{
|
|
1733
|
-
name: "
|
|
1734
|
-
value: "
|
|
1735
|
-
description: "
|
|
1786
|
+
name: "Simple Append",
|
|
1787
|
+
value: "merge-append",
|
|
1788
|
+
description: "Quickly appends LeanSpec section (may be verbose)"
|
|
1736
1789
|
},
|
|
1737
1790
|
{
|
|
1738
|
-
name: "
|
|
1791
|
+
name: "Replace with LeanSpec",
|
|
1792
|
+
value: "overwrite",
|
|
1793
|
+
description: "Backs up existing, creates fresh AGENTS.md from template"
|
|
1794
|
+
},
|
|
1795
|
+
{
|
|
1796
|
+
name: "Keep Existing Only",
|
|
1739
1797
|
value: "skip",
|
|
1740
|
-
description: "
|
|
1798
|
+
description: "Skips AGENTS.md, only adds .lean-spec config and specs/"
|
|
1741
1799
|
}
|
|
1742
1800
|
]
|
|
1743
1801
|
});
|
|
@@ -3035,7 +3093,7 @@ function renderColumn(title, emoji, specs, expanded, colorFn) {
|
|
|
3035
3093
|
}
|
|
3036
3094
|
console.log("");
|
|
3037
3095
|
} else if (!expanded && specs.length > 0) {
|
|
3038
|
-
console.log(` ${chalk15.dim("(collapsed, use --
|
|
3096
|
+
console.log(` ${chalk15.dim("(collapsed, use --complete to expand)")}`);
|
|
3039
3097
|
console.log("");
|
|
3040
3098
|
} else {
|
|
3041
3099
|
console.log(` ${chalk15.dim("(empty)")}`);
|
|
@@ -3701,26 +3759,28 @@ async function depsCommand(specPath, options) {
|
|
|
3701
3759
|
console.log("");
|
|
3702
3760
|
console.log(chalk18.green(`\u{1F4E6} Dependencies for ${chalk18.cyan(sanitizeUserInput(spec.path))}`));
|
|
3703
3761
|
console.log("");
|
|
3704
|
-
|
|
3762
|
+
const hasAnyRelationships = dependsOn.length > 0 || blocks.length > 0 || relatedSpecs.length > 0;
|
|
3763
|
+
if (!hasAnyRelationships) {
|
|
3764
|
+
console.log(chalk18.gray(" No dependencies or relationships"));
|
|
3765
|
+
console.log("");
|
|
3766
|
+
return;
|
|
3767
|
+
}
|
|
3705
3768
|
if (dependsOn.length > 0) {
|
|
3769
|
+
console.log(chalk18.bold("Depends On:"));
|
|
3706
3770
|
for (const dep of dependsOn) {
|
|
3707
3771
|
const status = getStatusIndicator(dep.frontmatter.status);
|
|
3708
3772
|
console.log(` \u2192 ${sanitizeUserInput(dep.path)} ${status}`);
|
|
3709
3773
|
}
|
|
3710
|
-
|
|
3711
|
-
console.log(chalk18.gray(" (none)"));
|
|
3774
|
+
console.log("");
|
|
3712
3775
|
}
|
|
3713
|
-
console.log("");
|
|
3714
|
-
console.log(chalk18.bold("Blocks:"));
|
|
3715
3776
|
if (blocks.length > 0) {
|
|
3777
|
+
console.log(chalk18.bold("Required By:"));
|
|
3716
3778
|
for (const blocked of blocks) {
|
|
3717
3779
|
const status = getStatusIndicator(blocked.frontmatter.status);
|
|
3718
3780
|
console.log(` \u2190 ${sanitizeUserInput(blocked.path)} ${status}`);
|
|
3719
3781
|
}
|
|
3720
|
-
|
|
3721
|
-
console.log(chalk18.gray(" (none)"));
|
|
3782
|
+
console.log("");
|
|
3722
3783
|
}
|
|
3723
|
-
console.log("");
|
|
3724
3784
|
if (relatedSpecs.length > 0) {
|
|
3725
3785
|
console.log(chalk18.bold("Related Specs:"));
|
|
3726
3786
|
for (const rel of relatedSpecs) {
|
|
@@ -3744,8 +3804,8 @@ function findDependencies(spec, specMap) {
|
|
|
3744
3804
|
if (dep) {
|
|
3745
3805
|
deps.push(dep);
|
|
3746
3806
|
} else {
|
|
3747
|
-
for (const [
|
|
3748
|
-
if (
|
|
3807
|
+
for (const [path19, s] of specMap.entries()) {
|
|
3808
|
+
if (path19.includes(depPath)) {
|
|
3749
3809
|
deps.push(s);
|
|
3750
3810
|
break;
|
|
3751
3811
|
}
|
|
@@ -3777,8 +3837,8 @@ function findRelated(spec, specMap) {
|
|
|
3777
3837
|
if (rel) {
|
|
3778
3838
|
related.push(rel);
|
|
3779
3839
|
} else {
|
|
3780
|
-
for (const [
|
|
3781
|
-
if (
|
|
3840
|
+
for (const [path19, s] of specMap.entries()) {
|
|
3841
|
+
if (path19.includes(relPath)) {
|
|
3782
3842
|
related.push(s);
|
|
3783
3843
|
break;
|
|
3784
3844
|
}
|
|
@@ -4432,7 +4492,6 @@ async function mcpCommand() {
|
|
|
4432
4492
|
}
|
|
4433
4493
|
|
|
4434
4494
|
// src/mcp-server.ts
|
|
4435
|
-
import * as path19 from "path";
|
|
4436
4495
|
function formatErrorMessage(prefix, error) {
|
|
4437
4496
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4438
4497
|
return `${prefix}: ${errorMsg}`;
|
|
@@ -4496,20 +4555,22 @@ async function searchSpecsData(query, options) {
|
|
|
4496
4555
|
return results;
|
|
4497
4556
|
}
|
|
4498
4557
|
async function readSpecData(specPath) {
|
|
4499
|
-
const config = await loadConfig();
|
|
4500
4558
|
const cwd = process.cwd();
|
|
4501
|
-
const
|
|
4502
|
-
|
|
4503
|
-
if (!resolvedPath) {
|
|
4504
|
-
throw new Error(`Spec not found: ${specPath}`);
|
|
4505
|
-
}
|
|
4506
|
-
const specInfo = await getSpec(resolvedPath);
|
|
4507
|
-
if (!specInfo) {
|
|
4559
|
+
const specContent = await readSpecContent(specPath, cwd);
|
|
4560
|
+
if (!specContent) {
|
|
4508
4561
|
throw new Error(`Spec not found: ${specPath}`);
|
|
4509
4562
|
}
|
|
4510
4563
|
return {
|
|
4511
|
-
spec:
|
|
4512
|
-
|
|
4564
|
+
spec: {
|
|
4565
|
+
name: specContent.name,
|
|
4566
|
+
path: specContent.path,
|
|
4567
|
+
status: specContent.frontmatter.status,
|
|
4568
|
+
created: String(specContent.frontmatter.created),
|
|
4569
|
+
priority: specContent.frontmatter.priority,
|
|
4570
|
+
tags: specContent.frontmatter.tags,
|
|
4571
|
+
assignee: specContent.frontmatter.assignee
|
|
4572
|
+
},
|
|
4573
|
+
content: specContent.content
|
|
4513
4574
|
};
|
|
4514
4575
|
}
|
|
4515
4576
|
async function getStatsData() {
|
|
@@ -4665,7 +4726,7 @@ async function createMcpServer() {
|
|
|
4665
4726
|
title: "View Spec",
|
|
4666
4727
|
description: "Read the complete content of a specification. Use this to understand spec details, review design decisions, or check implementation status. Returns metadata and full content.",
|
|
4667
4728
|
inputSchema: {
|
|
4668
|
-
specPath: z.string().describe('The spec to view. Can be: spec name (e.g., "unified-dashboard"), sequence number (e.g., "045" or "45"),
|
|
4729
|
+
specPath: z.string().describe('The spec to view. Can be: spec name (e.g., "unified-dashboard"), sequence number (e.g., "045" or "45"), full folder name (e.g., "045-unified-dashboard"), or sub-spec file (e.g., "045/DESIGN.md" or "unified-dashboard/TESTING.md").'),
|
|
4669
4730
|
raw: z.boolean().optional().describe("Output raw markdown instead of formatted"),
|
|
4670
4731
|
json: z.boolean().optional().describe("Output as JSON instead of formatted")
|
|
4671
4732
|
},
|
|
@@ -5365,4 +5426,4 @@ export {
|
|
|
5365
5426
|
createMcpServer,
|
|
5366
5427
|
mcpCommand
|
|
5367
5428
|
};
|
|
5368
|
-
//# sourceMappingURL=chunk-
|
|
5429
|
+
//# sourceMappingURL=chunk-OXTU3PN4.js.map
|