lean-spec 0.2.2 → 0.2.4

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 (37) hide show
  1. package/dist/{chunk-7MCDTSVE.js → chunk-UHMYFCXJ.js} +4083 -2886
  2. package/dist/chunk-UHMYFCXJ.js.map +1 -0
  3. package/dist/cli.js +67 -238
  4. package/dist/cli.js.map +1 -1
  5. package/dist/mcp-server.d.ts +1 -1
  6. package/dist/mcp-server.js +1 -1
  7. package/package.json +4 -4
  8. package/templates/_shared/agents-components/{core-rules-base.md → core-rules-base-additions.md} +0 -1
  9. package/templates/_shared/agents-components/{core-rules-enterprise.md → core-rules-enterprise-additions.md} +0 -1
  10. package/templates/_shared/agents-components/core-rules-shared.md +1 -0
  11. package/templates/_shared/agents-components/discovery-commands-enterprise-additions.md +6 -0
  12. package/templates/_shared/agents-components/discovery-commands-minimal-additions.md +0 -0
  13. package/templates/_shared/agents-components/discovery-commands-standard-additions.md +3 -0
  14. package/templates/_shared/agents-components/essential-commands-enterprise-additions.md +29 -0
  15. package/templates/_shared/agents-components/essential-commands-minimal-additions.md +1 -0
  16. package/templates/_shared/agents-components/essential-commands-shared.md +15 -0
  17. package/templates/_shared/agents-components/essential-commands-standard-additions.md +18 -0
  18. package/templates/_shared/agents-components/{quality-standards-enterprise.md → quality-standards-enterprise-additions.md} +0 -2
  19. package/templates/_shared/agents-components/{quality-standards-base.md → quality-standards-minimal-additions.md} +0 -2
  20. package/templates/_shared/agents-components/quality-standards-shared.md +6 -0
  21. package/templates/_shared/agents-components/status-update-triggers.md +14 -0
  22. package/templates/_shared/agents-components/workflow-enterprise.md +7 -4
  23. package/templates/_shared/agents-components/workflow-standard-detailed.md +7 -4
  24. package/templates/_shared/agents-components/workflow-standard.md +7 -4
  25. package/templates/_shared/agents-template.hbs +4 -0
  26. package/templates/enterprise/agents-config.json +5 -4
  27. package/templates/enterprise/files/AGENTS.md +79 -9
  28. package/templates/minimal/agents-config.json +5 -4
  29. package/templates/minimal/files/AGENTS.md +44 -5
  30. package/templates/standard/agents-config.json +5 -4
  31. package/templates/standard/files/AGENTS.md +66 -9
  32. package/dist/chunk-7MCDTSVE.js.map +0 -1
  33. package/dist/commands-GRG5UUOF.js +0 -4
  34. package/dist/commands-GRG5UUOF.js.map +0 -1
  35. package/templates/_shared/agents-components/discovery-commands-enterprise.md +0 -10
  36. package/templates/_shared/agents-components/discovery-commands-standard.md +0 -9
  37. /package/templates/_shared/agents-components/{discovery-commands-minimal.md → discovery-commands-shared.md} +0 -0
package/dist/cli.js CHANGED
@@ -1,24 +1,39 @@
1
- import { archiveSpec, backfillTimestamps, boardCommand, checkSpecs, validateCommand, createSpec, depsCommand, filesCommand, ganttCommand, initProject, listSpecs, openCommand, searchCommand, statsCommand, listTemplates, showTemplate, addTemplate, removeTemplate, copyTemplate, timelineCommand, updateSpec, viewCommand, migrateCommand, mcpCommand } from './chunk-7MCDTSVE.js';
1
+ import { analyzeCommand, archiveCommand, backfillCommand, boardCommand, checkCommand, compactCommand, createCommand, depsCommand, filesCommand, ganttCommand, initCommand, linkCommand, listCommand, mcpCommand, migrateCommand, openCommand, searchCommand, splitCommand, statsCommand, templatesCommand, timelineCommand, tokensCommand, uiCommand, unlinkCommand, updateCommand, validateCommand, viewCommand } from './chunk-UHMYFCXJ.js';
2
2
  import './chunk-LVD7ZAVZ.js';
3
3
  import { Command } from 'commander';
4
4
  import { readFileSync } from 'fs';
5
5
  import { fileURLToPath } from 'url';
6
6
  import { dirname, join } from 'path';
7
7
 
8
- // src/utils/cli-helpers.ts
9
- function parseCustomFieldOptions(fieldOptions) {
10
- const customFields = {};
11
- if (!fieldOptions) {
12
- return customFields;
13
- }
14
- for (const field of fieldOptions) {
15
- const [key, ...valueParts] = field.split("=");
16
- if (key && valueParts.length > 0) {
17
- const value = valueParts.join("=");
18
- customFields[key.trim()] = value.trim();
19
- }
20
- }
21
- return customFields;
8
+ // src/commands/registry.ts
9
+ function registerCommands(program2) {
10
+ program2.addCommand(analyzeCommand());
11
+ program2.addCommand(archiveCommand());
12
+ program2.addCommand(backfillCommand());
13
+ program2.addCommand(boardCommand());
14
+ program2.addCommand(checkCommand());
15
+ program2.addCommand(compactCommand());
16
+ program2.addCommand(createCommand());
17
+ program2.addCommand(depsCommand());
18
+ program2.addCommand(filesCommand());
19
+ program2.addCommand(ganttCommand());
20
+ program2.addCommand(initCommand());
21
+ program2.addCommand(linkCommand());
22
+ program2.addCommand(listCommand());
23
+ program2.addCommand(mcpCommand());
24
+ program2.addCommand(migrateCommand());
25
+ program2.addCommand(openCommand());
26
+ program2.addCommand(searchCommand());
27
+ program2.addCommand(splitCommand());
28
+ program2.addCommand(statsCommand());
29
+ program2.addCommand(templatesCommand());
30
+ program2.addCommand(timelineCommand());
31
+ program2.addCommand(tokensCommand());
32
+ program2.addCommand(uiCommand());
33
+ program2.addCommand(unlinkCommand());
34
+ program2.addCommand(updateCommand());
35
+ program2.addCommand(validateCommand());
36
+ program2.addCommand(viewCommand());
22
37
  }
23
38
 
24
39
  // src/cli.ts
@@ -31,257 +46,71 @@ var program = new Command();
31
46
  program.name("lean-spec").description("Manage LeanSpec documents").version(packageJson.version);
32
47
  program.addHelpText("after", `
33
48
  Command Groups:
34
-
35
- Core Commands:
36
- init Initialize LeanSpec in current directory
37
- create <name> Create new spec in folder structure
38
- list List all specs
39
- update <spec> Update spec metadata
49
+
50
+ Core Workflow:
40
51
  archive <spec> Move spec to archived/
41
52
  backfill [specs...] Backfill timestamps from git history
53
+ create <name> Create new spec
54
+ init Initialize LeanSpec in current directory
55
+ link <spec> Add relationships between specs
42
56
  migrate <input-path> Migrate specs from other SDD tools
57
+ unlink <spec> Remove relationships between specs
58
+ update <spec> Update spec metadata
43
59
 
44
- Viewing & Navigation:
45
- view <spec> View spec content
60
+ Discovery & Search:
61
+ files <spec> List files in a spec
62
+ list List all specs
46
63
  open <spec> Open spec in editor
47
64
  search <query> Full-text search with metadata filters
48
- files <spec> List files in a spec
65
+ view <spec> View spec content
49
66
 
50
- Project & Analytics:
67
+ Project Analytics:
51
68
  board Show Kanban-style board view
69
+ deps <spec> Show dependency graph for a spec
70
+ gantt Show timeline with dependencies
52
71
  stats Show aggregate statistics
53
72
  timeline Show creation/completion over time
54
- gantt Show timeline with dependencies
55
- deps <spec> Show dependency graph for a spec
56
73
 
57
- Maintenance:
74
+ Quality & Optimization:
75
+ analyze <spec> Analyze spec complexity and structure
58
76
  check Check for sequence conflicts
77
+ tokens [spec] Count tokens for LLM context management
59
78
  validate [specs...] Validate specs for quality issues
79
+
80
+ Advanced Editing:
81
+ compact <spec> Remove specified line ranges from spec
82
+ split <spec> Split spec into multiple files
83
+
84
+ Configuration:
60
85
  templates Manage spec templates
61
-
62
- Server:
86
+
87
+ Integration:
63
88
  mcp Start MCP server for AI assistants
89
+ ui Start local web UI for spec management
64
90
 
65
91
  Examples:
66
92
  $ lean-spec init
67
93
  $ lean-spec create my-feature --priority high
68
94
  $ lean-spec list --status in-progress
69
95
  $ lean-spec view 042
96
+ $ lean-spec link 085 --depends-on 042,035
97
+ $ lean-spec link 085 --related 082
98
+ $ lean-spec unlink 085 --depends-on 042
99
+ $ lean-spec deps 085
70
100
  $ lean-spec backfill --dry-run
71
101
  $ lean-spec migrate ./docs/adr
72
102
  $ lean-spec migrate ./docs/rfcs --with copilot
73
103
  $ lean-spec board --tag backend
74
104
  $ lean-spec search "authentication"
75
105
  $ lean-spec validate
76
- $ lean-spec validate --verbose
77
- $ lean-spec validate --quiet --rule max-lines
78
- $ lean-spec validate 018 --max-lines 500
106
+ $ lean-spec tokens 059
107
+ $ lean-spec analyze 045 --json
108
+ $ lean-spec split 045 --output README.md:1-150 --output DESIGN.md:151-end
109
+ $ lean-spec ui
110
+ $ lean-spec ui --port 3001 --no-open
111
+ $ lean-spec ui --specs ./docs/specs --dry-run
79
112
  `);
80
- program.command("archive <spec>").description("Move spec to archived/").action(async (specPath) => {
81
- await archiveSpec(specPath);
82
- });
83
- program.command("backfill [specs...]").description("Backfill timestamps from git history").option("--dry-run", "Show what would be updated without making changes").option("--force", "Overwrite existing timestamp values").option("--assignee", "Include assignee from first commit author").option("--transitions", "Include full status transition history").option("--all", "Include all optional fields (assignee + transitions)").action(async (specs, options) => {
84
- await backfillTimestamps({
85
- dryRun: options.dryRun,
86
- force: options.force,
87
- includeAssignee: options.assignee || options.all,
88
- includeTransitions: options.transitions || options.all,
89
- specs: specs && specs.length > 0 ? specs : void 0
90
- });
91
- });
92
- program.command("board").description("Show Kanban-style board view with project completion summary").option("--complete", "Include complete specs (default: hidden)").option("--simple", "Hide completion summary (kanban only)").option("--completion-only", "Show only completion summary (no kanban)").option("--tag <tag>", "Filter by tag").option("--assignee <name>", "Filter by assignee").action(async (options) => {
93
- await boardCommand(options);
94
- });
95
- program.command("check").description("Check for sequence conflicts").option("-q, --quiet", "Brief output").action(async (options) => {
96
- const hasNoConflicts = await checkSpecs(options);
97
- process.exit(hasNoConflicts ? 0 : 1);
98
- });
99
- program.command("validate [specs...]").description("Validate specs for quality issues").option("--max-lines <number>", "Custom line limit (default: 400)", parseInt).option("--verbose", "Show passing specs").option("--quiet", "Suppress warnings, only show errors").option("--format <format>", "Output format: default, json, compact", "default").option("--rule <rule>", "Filter by specific rule name (e.g., max-lines, frontmatter)").action(async (specs, options) => {
100
- const passed = await validateCommand({
101
- maxLines: options.maxLines,
102
- specs: specs && specs.length > 0 ? specs : void 0,
103
- verbose: options.verbose,
104
- quiet: options.quiet,
105
- format: options.format,
106
- rule: options.rule
107
- });
108
- process.exit(passed ? 0 : 1);
109
- });
110
- program.command("create <name>").description("Create new spec in folder structure").option("--title <title>", "Set custom title").option("--description <desc>", "Set initial description").option("--tags <tags>", "Set tags (comma-separated)").option("--priority <priority>", "Set priority (low, medium, high, critical)").option("--assignee <name>", "Set assignee").option("--template <template>", "Use a specific template").option("--field <name=value...>", "Set custom field (can specify multiple)").option("--no-prefix", "Skip date prefix even if configured").action(async (name, options) => {
111
- const customFields = parseCustomFieldOptions(options.field);
112
- const createOptions = {
113
- title: options.title,
114
- description: options.description,
115
- tags: options.tags ? options.tags.split(",").map((t) => t.trim()) : void 0,
116
- priority: options.priority,
117
- assignee: options.assignee,
118
- template: options.template,
119
- customFields: Object.keys(customFields).length > 0 ? customFields : void 0,
120
- noPrefix: options.prefix === false
121
- };
122
- await createSpec(name, createOptions);
123
- });
124
- program.command("deps <spec>").description("Show dependency graph for a spec. Related specs (\u27F7) are shown bidirectionally, depends_on (\u2192) are directional.").option("--depth <n>", "Show N levels deep (default: 3)", parseInt).option("--graph", "ASCII graph visualization").option("--json", "Output as JSON").action(async (specPath, options) => {
125
- await depsCommand(specPath, options);
126
- });
127
- program.command("files <spec>").description("List files in a spec").option("--type <type>", "Filter by type: docs, assets").option("--tree", "Show tree structure").action(async (specPath, options) => {
128
- await filesCommand(specPath, options);
129
- });
130
- program.command("gantt").description("Show timeline with dependencies").option("--weeks <n>", "Show N weeks (default: 4)", parseInt).option("--show-complete", "Include completed specs").option("--critical-path", "Highlight critical path").action(async (options) => {
131
- await ganttCommand(options);
132
- });
133
- program.command("init").description("Initialize LeanSpec in current directory").action(async () => {
134
- await initProject();
135
- });
136
- program.command("list").description("List all specs").option("--archived", "Include archived specs").option("--status <status>", "Filter by status (planned, in-progress, complete, archived)").option("--tag <tag...>", "Filter by tag (can specify multiple)").option("--priority <priority>", "Filter by priority (low, medium, high, critical)").option("--assignee <name>", "Filter by assignee").option("--field <name=value...>", "Filter by custom field (can specify multiple)").option("--sort <field>", "Sort by field (id, created, name, status, priority)", "id").option("--order <order>", "Sort order (asc, desc)", "desc").action(async (options) => {
137
- const customFields = parseCustomFieldOptions(options.field);
138
- const listOptions = {
139
- showArchived: options.archived,
140
- status: options.status,
141
- tags: options.tag,
142
- priority: options.priority,
143
- assignee: options.assignee,
144
- customFields: Object.keys(customFields).length > 0 ? customFields : void 0,
145
- sortBy: options.sort || "id",
146
- sortOrder: options.order || "desc"
147
- };
148
- await listSpecs(listOptions);
149
- });
150
- program.command("open <spec>").description("Open spec in editor").option("--editor <editor>", "Specify editor command").action(async (specPath, options) => {
151
- try {
152
- await openCommand(specPath, {
153
- editor: options.editor
154
- });
155
- } catch (error) {
156
- console.error("\x1B[31mError:\x1B[0m", error instanceof Error ? error.message : String(error));
157
- process.exit(1);
158
- }
159
- });
160
- program.command("search <query>").description("Full-text search with metadata filters").option("--status <status>", "Filter by status").option("--tag <tag>", "Filter by tag").option("--priority <priority>", "Filter by priority").option("--assignee <name>", "Filter by assignee").option("--field <name=value...>", "Filter by custom field (can specify multiple)").action(async (query, options) => {
161
- const customFields = parseCustomFieldOptions(options.field);
162
- await searchCommand(query, {
163
- status: options.status,
164
- tag: options.tag,
165
- priority: options.priority,
166
- assignee: options.assignee,
167
- customFields: Object.keys(customFields).length > 0 ? customFields : void 0
168
- });
169
- });
170
- program.command("stats").description("Show aggregate statistics (default: simplified view)").option("--tag <tag>", "Filter by tag").option("--assignee <name>", "Filter by assignee").option("--full", "Show full detailed analytics (all sections)").option("--timeline", "Show only timeline section").option("--velocity", "Show only velocity section").option("--json", "Output as JSON").action(async (options) => {
171
- await statsCommand(options);
172
- });
173
- program.command("analyze <spec>").description("Analyze spec complexity and structure (spec 059)").option("--json", "Output as JSON for AI agents").option("--verbose", "Include detailed section breakdown").action(async (specPath, options) => {
174
- const { analyzeCommand } = await import('./commands-GRG5UUOF.js');
175
- await analyzeCommand(specPath, options);
176
- });
177
- program.command("split <spec>").description("Split spec into multiple files by line ranges (spec 059)").option("--output <file:lines>", "Output file with line range (e.g., README.md:1-150)", collectOutputs, []).option("--update-refs", "Update cross-references in README.md").option("--dry-run", "Show what would be created without making changes").option("--force", "Overwrite existing files").action(async (specPath, options) => {
178
- const { splitCommand } = await import('./commands-GRG5UUOF.js');
179
- const outputs = options.output.map((opt) => {
180
- const [file, lines] = opt.split(":");
181
- if (!file || !lines) {
182
- throw new Error(`Invalid --output format: ${opt}. Expected: file.md:1-150`);
183
- }
184
- return { file, lines };
185
- });
186
- await splitCommand(specPath, {
187
- outputs,
188
- updateRefs: options.updateRefs,
189
- dryRun: options.dryRun,
190
- force: options.force
191
- });
192
- });
193
- function collectOutputs(value, previous) {
194
- return previous.concat([value]);
195
- }
196
- function collectRemoves(value, previous) {
197
- return previous.concat([value]);
198
- }
199
- program.command("compact <spec>").description("Remove specified line ranges from spec (spec 059)").option("--remove <lines>", "Line range to remove (e.g., 145-153)", collectRemoves, []).option("--dry-run", "Show what would be removed without making changes").option("--force", "Skip confirmation").action(async (specPath, options) => {
200
- const { compactCommand } = await import('./commands-GRG5UUOF.js');
201
- await compactCommand(specPath, {
202
- removes: options.remove,
203
- dryRun: options.dryRun,
204
- force: options.force
205
- });
206
- });
207
- program.command("tokens [spec]").description("Count tokens in spec(s) for LLM context management").option("--detailed", "Show content type breakdown (code, prose, tables)").option("--include-sub-specs", "Count all sub-spec files (DESIGN.md, etc.)").option("--all", "Show all specs (when [spec] is omitted)").option("--sort-by <field>", "Sort by: tokens, lines, name (default: tokens)").option("--json", "Output as JSON").action(async (specPath, options) => {
208
- const { tokensCommand, tokensAllCommand } = await import('./commands-GRG5UUOF.js');
209
- if (specPath) {
210
- await tokensCommand(specPath, options);
211
- } else {
212
- await tokensAllCommand(options);
213
- }
214
- });
215
- var templatesCmd = program.command("templates").description("Manage spec templates");
216
- templatesCmd.command("list").description("List available templates").action(async () => {
217
- await listTemplates();
218
- });
219
- templatesCmd.command("show <name>").description("Show template content").action(async (name) => {
220
- await showTemplate(name);
221
- });
222
- templatesCmd.command("add <name> <file>").description("Register a template").action(async (name, file) => {
223
- await addTemplate(name, file);
224
- });
225
- templatesCmd.command("remove <name>").description("Unregister a template").action(async (name) => {
226
- await removeTemplate(name);
227
- });
228
- templatesCmd.command("copy <source> <target>").description("Copy a template to create a new one").action(async (source, target) => {
229
- await copyTemplate(source, target);
230
- });
231
- templatesCmd.action(async () => {
232
- await listTemplates();
233
- });
234
- program.command("timeline").description("Show creation/completion over time").option("--days <n>", "Show last N days (default: 30)", parseInt).option("--by-tag", "Group by tag").option("--by-assignee", "Group by assignee").action(async (options) => {
235
- await timelineCommand(options);
236
- });
237
- program.command("update <spec>").description("Update spec metadata").option("--status <status>", "Set status (planned, in-progress, complete, archived)").option("--priority <priority>", "Set priority (low, medium, high, critical)").option("--tags <tags>", "Set tags (comma-separated)").option("--assignee <name>", "Set assignee").option("--field <name=value...>", "Set custom field (can specify multiple)").action(async (specPath, options) => {
238
- const customFields = parseCustomFieldOptions(options.field);
239
- const updates = {
240
- status: options.status,
241
- priority: options.priority,
242
- tags: options.tags ? options.tags.split(",").map((t) => t.trim()) : void 0,
243
- assignee: options.assignee,
244
- customFields: Object.keys(customFields).length > 0 ? customFields : void 0
245
- };
246
- Object.keys(updates).forEach((key) => {
247
- if (updates[key] === void 0) {
248
- delete updates[key];
249
- }
250
- });
251
- if (Object.keys(updates).length === 0) {
252
- console.error("Error: At least one update option required (--status, --priority, --tags, --assignee, --field)");
253
- process.exit(1);
254
- }
255
- await updateSpec(specPath, updates);
256
- });
257
- program.command("view <spec>").description('View spec content (supports sub-specs like "045/DESIGN.md")').option("--raw", "Output raw markdown (for piping/scripting)").option("--json", "Output as JSON").option("--no-color", "Disable colors").action(async (specPath, options) => {
258
- try {
259
- await viewCommand(specPath, {
260
- raw: options.raw,
261
- json: options.json,
262
- noColor: options.color === false
263
- });
264
- } catch (error) {
265
- console.error("\x1B[31mError:\x1B[0m", error instanceof Error ? error.message : String(error));
266
- process.exit(1);
267
- }
268
- });
269
- program.command("migrate <input-path>").description("Migrate specs from other SDD tools (ADR, RFC, OpenSpec, spec-kit, etc.)").option("--with <provider>", "AI-assisted migration (copilot, claude, gemini)").option("--dry-run", "Preview without making changes").option("--batch-size <n>", "Process N docs at a time", parseInt).option("--skip-validation", "Don't validate after migration").option("--backfill", "Auto-run backfill after migration").action(async (inputPath, options) => {
270
- if (options.with && !["copilot", "claude", "gemini"].includes(options.with)) {
271
- console.error("\x1B[31m\u274C Error:\x1B[0m Invalid AI provider. Use: copilot, claude, or gemini");
272
- process.exit(1);
273
- }
274
- await migrateCommand(inputPath, {
275
- aiProvider: options.with,
276
- dryRun: options.dryRun,
277
- batchSize: options.batchSize,
278
- skipValidation: options.skipValidation,
279
- backfill: options.backfill
280
- });
281
- });
282
- program.command("mcp").description("Start MCP server for AI assistants (Claude Desktop, Cline, etc.)").action(async () => {
283
- await mcpCommand();
284
- });
113
+ registerCommands(program);
285
114
  program.parse();
286
115
  //# sourceMappingURL=cli.js.map
287
116
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/cli-helpers.ts","../src/cli.ts"],"names":[],"mappings":"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","file":"cli.js","sourcesContent":["/**\n * Parse custom fields from CLI --field options\n * @param fieldOptions Array of \"name=value\" strings\n * @returns Record of parsed field names and values\n */\nexport function parseCustomFieldOptions(fieldOptions?: string[]): Record<string, unknown> {\n const customFields: Record<string, unknown> = {};\n \n if (!fieldOptions) {\n return customFields;\n }\n \n for (const field of fieldOptions) {\n const [key, ...valueParts] = field.split('=');\n if (key && valueParts.length > 0) {\n const value = valueParts.join('='); // Handle values with '=' in them\n customFields[key.trim()] = value.trim();\n }\n }\n \n return customFields;\n}\n","import { Command } from 'commander';\nimport { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\nimport {\n createSpec,\n archiveSpec,\n listSpecs,\n updateSpec,\n checkSpecs,\n backfillTimestamps,\n listTemplates,\n showTemplate,\n addTemplate,\n removeTemplate,\n copyTemplate,\n initProject,\n statsCommand,\n boardCommand,\n timelineCommand,\n depsCommand,\n searchCommand,\n ganttCommand,\n filesCommand,\n viewCommand,\n openCommand,\n validateCommand,\n mcpCommand,\n migrateCommand,\n} from './commands/index.js';\nimport { parseCustomFieldOptions } from './utils/cli-helpers.js';\nimport type { SpecStatus, SpecPriority } from './frontmatter.js';\n\n// Get version from package.json\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst packageJson = JSON.parse(\n readFileSync(join(__dirname, '../package.json'), 'utf-8')\n);\n\nconst program = new Command();\n\nprogram\n .name('lean-spec')\n .description('Manage LeanSpec documents')\n .version(packageJson.version);\n\n// Add custom help text with grouped commands\nprogram.addHelpText('after', `\nCommand Groups:\n \n Core Commands:\n init Initialize LeanSpec in current directory\n create <name> Create new spec in folder structure\n list List all specs\n update <spec> Update spec metadata\n archive <spec> Move spec to archived/\n backfill [specs...] Backfill timestamps from git history\n migrate <input-path> Migrate specs from other SDD tools\n \n Viewing & Navigation:\n view <spec> View spec content\n open <spec> Open spec in editor\n search <query> Full-text search with metadata filters\n files <spec> List files in a spec\n \n Project & Analytics:\n board Show Kanban-style board view\n stats Show aggregate statistics\n timeline Show creation/completion over time\n gantt Show timeline with dependencies\n deps <spec> Show dependency graph for a spec\n \n Maintenance:\n check Check for sequence conflicts\n validate [specs...] Validate specs for quality issues\n templates Manage spec templates\n \n Server:\n mcp Start MCP server for AI assistants\n\nExamples:\n $ lean-spec init\n $ lean-spec create my-feature --priority high\n $ lean-spec list --status in-progress\n $ lean-spec view 042\n $ lean-spec backfill --dry-run\n $ lean-spec migrate ./docs/adr\n $ lean-spec migrate ./docs/rfcs --with copilot\n $ lean-spec board --tag backend\n $ lean-spec search \"authentication\"\n $ lean-spec validate\n $ lean-spec validate --verbose\n $ lean-spec validate --quiet --rule max-lines\n $ lean-spec validate 018 --max-lines 500\n`);\n\n// archive command\nprogram\n .command('archive <spec>')\n .description('Move spec to archived/')\n .action(async (specPath: string) => {\n await archiveSpec(specPath);\n });\n\n// backfill command\nprogram\n .command('backfill [specs...]')\n .description('Backfill timestamps from git history')\n .option('--dry-run', 'Show what would be updated without making changes')\n .option('--force', 'Overwrite existing timestamp values')\n .option('--assignee', 'Include assignee from first commit author')\n .option('--transitions', 'Include full status transition history')\n .option('--all', 'Include all optional fields (assignee + transitions)')\n .action(async (specs: string[] | undefined, options: {\n dryRun?: boolean;\n force?: boolean;\n assignee?: boolean;\n transitions?: boolean;\n all?: boolean;\n }) => {\n await backfillTimestamps({\n dryRun: options.dryRun,\n force: options.force,\n includeAssignee: options.assignee || options.all,\n includeTransitions: options.transitions || options.all,\n specs: specs && specs.length > 0 ? specs : undefined,\n });\n });\n\n// board command\nprogram\n .command('board')\n .description('Show Kanban-style board view with project completion summary')\n .option('--complete', 'Include complete specs (default: hidden)')\n .option('--simple', 'Hide completion summary (kanban only)')\n .option('--completion-only', 'Show only completion summary (no kanban)')\n .option('--tag <tag>', 'Filter by tag')\n .option('--assignee <name>', 'Filter by assignee')\n .action(async (options: {\n showComplete?: boolean;\n simple?: boolean;\n completionOnly?: boolean;\n tag?: string;\n assignee?: string;\n }) => {\n await boardCommand(options);\n });\n\n// check command\nprogram\n .command('check')\n .description('Check for sequence conflicts')\n .option('-q, --quiet', 'Brief output')\n .action(async (options: {\n quiet?: boolean;\n }) => {\n const hasNoConflicts = await checkSpecs(options);\n // Exit with 0 (success) if no conflicts, 1 (error) if conflicts found\n process.exit(hasNoConflicts ? 0 : 1);\n });\n\n// validate command\nprogram\n .command('validate [specs...]')\n .description('Validate specs for quality issues')\n .option('--max-lines <number>', 'Custom line limit (default: 400)', parseInt)\n .option('--verbose', 'Show passing specs')\n .option('--quiet', 'Suppress warnings, only show errors')\n .option('--format <format>', 'Output format: default, json, compact', 'default')\n .option('--rule <rule>', 'Filter by specific rule name (e.g., max-lines, frontmatter)')\n .action(async (specs: string[] | undefined, options: {\n maxLines?: number;\n verbose?: boolean;\n quiet?: boolean;\n format?: 'default' | 'json' | 'compact';\n rule?: string;\n }) => {\n const passed = await validateCommand({\n maxLines: options.maxLines,\n specs: specs && specs.length > 0 ? specs : undefined,\n verbose: options.verbose,\n quiet: options.quiet,\n format: options.format,\n rule: options.rule,\n });\n // Exit with 0 (success) if all passed, 1 (error) if any failed\n process.exit(passed ? 0 : 1);\n });\n\n// create command\nprogram\n .command('create <name>')\n .description('Create new spec in folder structure')\n .option('--title <title>', 'Set custom title')\n .option('--description <desc>', 'Set initial description')\n .option('--tags <tags>', 'Set tags (comma-separated)')\n .option('--priority <priority>', 'Set priority (low, medium, high, critical)')\n .option('--assignee <name>', 'Set assignee')\n .option('--template <template>', 'Use a specific template')\n .option('--field <name=value...>', 'Set custom field (can specify multiple)')\n .option('--no-prefix', 'Skip date prefix even if configured')\n .action(async (name: string, options: {\n title?: string;\n description?: string;\n tags?: string;\n priority?: SpecPriority;\n assignee?: string;\n template?: string;\n field?: string[];\n prefix?: boolean;\n }) => {\n // Parse custom fields from --field options\n const customFields = parseCustomFieldOptions(options.field);\n \n const createOptions: {\n title?: string;\n description?: string;\n tags?: string[];\n priority?: SpecPriority;\n assignee?: string;\n template?: string;\n customFields?: Record<string, unknown>;\n noPrefix?: boolean;\n } = {\n title: options.title,\n description: options.description,\n tags: options.tags ? options.tags.split(',').map(t => t.trim()) : undefined,\n priority: options.priority,\n assignee: options.assignee,\n template: options.template,\n customFields: Object.keys(customFields).length > 0 ? customFields : undefined,\n noPrefix: options.prefix === false,\n };\n await createSpec(name, createOptions);\n });\n\n// deps command\nprogram\n .command('deps <spec>')\n .description('Show dependency graph for a spec. Related specs (⟷) are shown bidirectionally, depends_on (→) are directional.')\n .option('--depth <n>', 'Show N levels deep (default: 3)', parseInt)\n .option('--graph', 'ASCII graph visualization')\n .option('--json', 'Output as JSON')\n .action(async (specPath: string, options: {\n depth?: number;\n graph?: boolean;\n json?: boolean;\n }) => {\n await depsCommand(specPath, options);\n });\n\n// files command\nprogram\n .command('files <spec>')\n .description('List files in a spec')\n .option('--type <type>', 'Filter by type: docs, assets')\n .option('--tree', 'Show tree structure')\n .action(async (specPath: string, options: {\n type?: 'docs' | 'assets';\n tree?: boolean;\n }) => {\n await filesCommand(specPath, options);\n });\n\n// gantt command\nprogram\n .command('gantt')\n .description('Show timeline with dependencies')\n .option('--weeks <n>', 'Show N weeks (default: 4)', parseInt)\n .option('--show-complete', 'Include completed specs')\n .option('--critical-path', 'Highlight critical path')\n .action(async (options: {\n weeks?: number;\n showComplete?: boolean;\n criticalPath?: boolean;\n }) => {\n await ganttCommand(options);\n });\n\n// init command\nprogram\n .command('init')\n .description('Initialize LeanSpec in current directory')\n .action(async () => {\n await initProject();\n });\n\n// list command\nprogram\n .command('list')\n .description('List all specs')\n .option('--archived', 'Include archived specs')\n .option('--status <status>', 'Filter by status (planned, in-progress, complete, archived)')\n .option('--tag <tag...>', 'Filter by tag (can specify multiple)')\n .option('--priority <priority>', 'Filter by priority (low, medium, high, critical)')\n .option('--assignee <name>', 'Filter by assignee')\n .option('--field <name=value...>', 'Filter by custom field (can specify multiple)')\n .option('--sort <field>', 'Sort by field (id, created, name, status, priority)', 'id')\n .option('--order <order>', 'Sort order (asc, desc)', 'desc')\n .action(async (options: {\n archived?: boolean;\n status?: SpecStatus;\n tag?: string[];\n priority?: SpecPriority;\n assignee?: string;\n field?: string[];\n sort?: string;\n order?: string;\n }) => {\n // Parse custom field filters from --field options\n const customFields = parseCustomFieldOptions(options.field);\n \n const listOptions: {\n showArchived?: boolean;\n status?: SpecStatus;\n tags?: string[];\n priority?: SpecPriority;\n assignee?: string;\n customFields?: Record<string, unknown>;\n sortBy?: string;\n sortOrder?: 'asc' | 'desc';\n } = {\n showArchived: options.archived,\n status: options.status,\n tags: options.tag,\n priority: options.priority,\n assignee: options.assignee,\n customFields: Object.keys(customFields).length > 0 ? customFields : undefined,\n sortBy: options.sort || 'id',\n sortOrder: (options.order as 'asc' | 'desc') || 'desc',\n };\n await listSpecs(listOptions);\n });\n\n// open command\nprogram\n .command('open <spec>')\n .description('Open spec in editor')\n .option('--editor <editor>', 'Specify editor command')\n .action(async (specPath: string, options: {\n editor?: string;\n }) => {\n try {\n await openCommand(specPath, {\n editor: options.editor,\n });\n } catch (error) {\n console.error('\\x1b[31mError:\\x1b[0m', error instanceof Error ? error.message : String(error));\n process.exit(1);\n }\n });\n\n// search command\nprogram\n .command('search <query>')\n .description('Full-text search with metadata filters')\n .option('--status <status>', 'Filter by status')\n .option('--tag <tag>', 'Filter by tag')\n .option('--priority <priority>', 'Filter by priority')\n .option('--assignee <name>', 'Filter by assignee')\n .option('--field <name=value...>', 'Filter by custom field (can specify multiple)')\n .action(async (query: string, options: {\n status?: SpecStatus;\n tag?: string;\n priority?: SpecPriority;\n assignee?: string;\n field?: string[];\n }) => {\n // Parse custom field filters from --field options\n const customFields = parseCustomFieldOptions(options.field);\n \n await searchCommand(query, {\n status: options.status,\n tag: options.tag,\n priority: options.priority,\n assignee: options.assignee,\n customFields: Object.keys(customFields).length > 0 ? customFields : undefined,\n });\n });\n\n// stats command\nprogram\n .command('stats')\n .description('Show aggregate statistics (default: simplified view)')\n .option('--tag <tag>', 'Filter by tag')\n .option('--assignee <name>', 'Filter by assignee')\n .option('--full', 'Show full detailed analytics (all sections)')\n .option('--timeline', 'Show only timeline section')\n .option('--velocity', 'Show only velocity section')\n .option('--json', 'Output as JSON')\n .action(async (options: {\n tag?: string;\n assignee?: string;\n full?: boolean;\n timeline?: boolean;\n velocity?: boolean;\n json?: boolean;\n }) => {\n await statsCommand(options);\n });\n\n// analyze command - for spec 059\nprogram\n .command('analyze <spec>')\n .description('Analyze spec complexity and structure (spec 059)')\n .option('--json', 'Output as JSON for AI agents')\n .option('--verbose', 'Include detailed section breakdown')\n .action(async (specPath: string, options: { json?: boolean; verbose?: boolean }) => {\n const { analyzeCommand } = await import('./commands/index.js');\n await analyzeCommand(specPath, options);\n });\n\n// split command - for spec 059\nprogram\n .command('split <spec>')\n .description('Split spec into multiple files by line ranges (spec 059)')\n .option('--output <file:lines>', 'Output file with line range (e.g., README.md:1-150)', collectOutputs, [])\n .option('--update-refs', 'Update cross-references in README.md')\n .option('--dry-run', 'Show what would be created without making changes')\n .option('--force', 'Overwrite existing files')\n .action(async (specPath: string, options: { \n output: Array<string>;\n updateRefs?: boolean;\n dryRun?: boolean;\n force?: boolean;\n }) => {\n const { splitCommand } = await import('./commands/index.js');\n \n // Parse outputs into structured format\n const outputs = options.output.map((opt: string) => {\n const [file, lines] = opt.split(':');\n if (!file || !lines) {\n throw new Error(`Invalid --output format: ${opt}. Expected: file.md:1-150`);\n }\n return { file, lines };\n });\n \n await splitCommand(specPath, {\n outputs,\n updateRefs: options.updateRefs,\n dryRun: options.dryRun,\n force: options.force,\n });\n });\n\n// Helper function to collect multiple --output options\nfunction collectOutputs(value: string, previous: string[]): string[] {\n return previous.concat([value]);\n}\n\n// Helper function to collect multiple --remove options\nfunction collectRemoves(value: string, previous: string[]): string[] {\n return previous.concat([value]);\n}\n\n// compact command - for spec 059\nprogram\n .command('compact <spec>')\n .description('Remove specified line ranges from spec (spec 059)')\n .option('--remove <lines>', 'Line range to remove (e.g., 145-153)', collectRemoves, [])\n .option('--dry-run', 'Show what would be removed without making changes')\n .option('--force', 'Skip confirmation')\n .action(async (specPath: string, options: { \n remove: string[];\n dryRun?: boolean;\n force?: boolean;\n }) => {\n const { compactCommand } = await import('./commands/index.js');\n \n await compactCommand(specPath, {\n removes: options.remove,\n dryRun: options.dryRun,\n force: options.force,\n });\n });\n\n// tokens command\nprogram\n .command('tokens [spec]')\n .description('Count tokens in spec(s) for LLM context management')\n .option('--detailed', 'Show content type breakdown (code, prose, tables)')\n .option('--include-sub-specs', 'Count all sub-spec files (DESIGN.md, etc.)')\n .option('--all', 'Show all specs (when [spec] is omitted)')\n .option('--sort-by <field>', 'Sort by: tokens, lines, name (default: tokens)')\n .option('--json', 'Output as JSON')\n .action(async (specPath: string | undefined, options: {\n detailed?: boolean;\n includeSubSpecs?: boolean;\n all?: boolean;\n sortBy?: 'tokens' | 'lines' | 'name';\n json?: boolean;\n }) => {\n const { tokensCommand, tokensAllCommand } = await import('./commands/index.js');\n \n if (specPath) {\n // Count specific spec\n await tokensCommand(specPath, options);\n } else {\n // Count all specs\n await tokensAllCommand(options);\n }\n });\n\n// templates command and subcommands\nconst templatesCmd = program\n .command('templates')\n .description('Manage spec templates');\n\ntemplatesCmd\n .command('list')\n .description('List available templates')\n .action(async () => {\n await listTemplates();\n });\n\ntemplatesCmd\n .command('show <name>')\n .description('Show template content')\n .action(async (name: string) => {\n await showTemplate(name);\n });\n\ntemplatesCmd\n .command('add <name> <file>')\n .description('Register a template')\n .action(async (name: string, file: string) => {\n await addTemplate(name, file);\n });\n\ntemplatesCmd\n .command('remove <name>')\n .description('Unregister a template')\n .action(async (name: string) => {\n await removeTemplate(name);\n });\n\ntemplatesCmd\n .command('copy <source> <target>')\n .description('Copy a template to create a new one')\n .action(async (source: string, target: string) => {\n await copyTemplate(source, target);\n });\n\n// Default action for templates (list)\ntemplatesCmd\n .action(async () => {\n await listTemplates();\n });\n\n// timeline command\nprogram\n .command('timeline')\n .description('Show creation/completion over time')\n .option('--days <n>', 'Show last N days (default: 30)', parseInt)\n .option('--by-tag', 'Group by tag')\n .option('--by-assignee', 'Group by assignee')\n .action(async (options: {\n days?: number;\n byTag?: boolean;\n byAssignee?: boolean;\n }) => {\n await timelineCommand(options);\n });\n\n// update command\nprogram\n .command('update <spec>')\n .description('Update spec metadata')\n .option('--status <status>', 'Set status (planned, in-progress, complete, archived)')\n .option('--priority <priority>', 'Set priority (low, medium, high, critical)')\n .option('--tags <tags>', 'Set tags (comma-separated)')\n .option('--assignee <name>', 'Set assignee')\n .option('--field <name=value...>', 'Set custom field (can specify multiple)')\n .action(async (specPath: string, options: {\n status?: SpecStatus;\n priority?: SpecPriority;\n tags?: string;\n assignee?: string;\n field?: string[];\n }) => {\n // Parse custom fields from --field options\n const customFields = parseCustomFieldOptions(options.field);\n \n const updates: {\n status?: SpecStatus;\n priority?: SpecPriority;\n tags?: string[];\n assignee?: string;\n customFields?: Record<string, unknown>;\n } = {\n status: options.status,\n priority: options.priority,\n tags: options.tags ? options.tags.split(',').map(t => t.trim()) : undefined,\n assignee: options.assignee,\n customFields: Object.keys(customFields).length > 0 ? customFields : undefined,\n };\n \n // Filter out undefined values\n Object.keys(updates).forEach(key => {\n if (updates[key as keyof typeof updates] === undefined) {\n delete updates[key as keyof typeof updates];\n }\n });\n \n if (Object.keys(updates).length === 0) {\n console.error('Error: At least one update option required (--status, --priority, --tags, --assignee, --field)');\n process.exit(1);\n }\n \n await updateSpec(specPath, updates);\n });\n\n// view command (primary viewer)\nprogram\n .command('view <spec>')\n .description('View spec content (supports sub-specs like \"045/DESIGN.md\")')\n .option('--raw', 'Output raw markdown (for piping/scripting)')\n .option('--json', 'Output as JSON')\n .option('--no-color', 'Disable colors')\n .action(async (specPath: string, options: {\n raw?: boolean;\n json?: boolean;\n color?: boolean;\n }) => {\n try {\n await viewCommand(specPath, {\n raw: options.raw,\n json: options.json,\n noColor: options.color === false,\n });\n } catch (error) {\n console.error('\\x1b[31mError:\\x1b[0m', error instanceof Error ? error.message : String(error));\n process.exit(1);\n }\n });\n\n// migrate command\nprogram\n .command('migrate <input-path>')\n .description('Migrate specs from other SDD tools (ADR, RFC, OpenSpec, spec-kit, etc.)')\n .option('--with <provider>', 'AI-assisted migration (copilot, claude, gemini)')\n .option('--dry-run', 'Preview without making changes')\n .option('--batch-size <n>', 'Process N docs at a time', parseInt)\n .option('--skip-validation', \"Don't validate after migration\")\n .option('--backfill', 'Auto-run backfill after migration')\n .action(async (inputPath: string, options: {\n with?: string;\n dryRun?: boolean;\n batchSize?: number;\n skipValidation?: boolean;\n backfill?: boolean;\n }) => {\n // Validate AI provider if specified\n if (options.with && !['copilot', 'claude', 'gemini'].includes(options.with)) {\n console.error('\\x1b[31m❌ Error:\\x1b[0m Invalid AI provider. Use: copilot, claude, or gemini');\n process.exit(1);\n }\n \n await migrateCommand(inputPath, {\n aiProvider: options.with as 'copilot' | 'claude' | 'gemini' | undefined,\n dryRun: options.dryRun,\n batchSize: options.batchSize,\n skipValidation: options.skipValidation,\n backfill: options.backfill,\n });\n });\n\n// mcp command\nprogram\n .command('mcp')\n .description('Start MCP server for AI assistants (Claude Desktop, Cline, etc.)')\n .action(async () => {\n await mcpCommand();\n });\n\n// Parse and execute\nprogram.parse();\n"]}
1
+ {"version":3,"sources":["../src/commands/registry.ts","../src/cli.ts"],"names":["program"],"mappings":"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","file":"cli.js","sourcesContent":["import { Command } from 'commander';\nimport {\n analyzeCommand,\n archiveCommand,\n backfillCommand,\n boardCommand,\n checkCommand,\n compactCommand,\n createCommand,\n depsCommand,\n filesCommand,\n ganttCommand,\n initCommand,\n linkCommand,\n listCommand,\n mcpCommand,\n migrateCommand,\n openCommand,\n searchCommand,\n splitCommand,\n statsCommand,\n templatesCommand,\n timelineCommand,\n tokensCommand,\n uiCommand,\n unlinkCommand,\n updateCommand,\n validateCommand,\n viewCommand,\n} from './index.js';\n\n/**\n * Register all commands in alphabetical order\n */\nexport function registerCommands(program: Command): void {\n // Alphabetically sorted command registration\n program.addCommand(analyzeCommand());\n program.addCommand(archiveCommand());\n program.addCommand(backfillCommand());\n program.addCommand(boardCommand());\n program.addCommand(checkCommand());\n program.addCommand(compactCommand());\n program.addCommand(createCommand());\n program.addCommand(depsCommand());\n program.addCommand(filesCommand());\n program.addCommand(ganttCommand());\n program.addCommand(initCommand());\n program.addCommand(linkCommand());\n program.addCommand(listCommand());\n program.addCommand(mcpCommand());\n program.addCommand(migrateCommand());\n program.addCommand(openCommand());\n program.addCommand(searchCommand());\n program.addCommand(splitCommand());\n program.addCommand(statsCommand());\n program.addCommand(templatesCommand());\n program.addCommand(timelineCommand());\n program.addCommand(tokensCommand());\n program.addCommand(uiCommand());\n program.addCommand(unlinkCommand());\n program.addCommand(updateCommand());\n program.addCommand(validateCommand());\n program.addCommand(viewCommand());\n}\n","import { Command } from 'commander';\nimport { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\nimport { registerCommands } from './commands/registry.js';\n\n// Get version from package.json\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst packageJson = JSON.parse(\n readFileSync(join(__dirname, '../package.json'), 'utf-8')\n);\n\nconst program = new Command();\n\nprogram\n .name('lean-spec')\n .description('Manage LeanSpec documents')\n .version(packageJson.version);\n\n// Add custom help text with grouped commands\nprogram.addHelpText('after', `\nCommand Groups:\n\n Core Workflow:\n archive <spec> Move spec to archived/\n backfill [specs...] Backfill timestamps from git history\n create <name> Create new spec\n init Initialize LeanSpec in current directory\n link <spec> Add relationships between specs\n migrate <input-path> Migrate specs from other SDD tools\n unlink <spec> Remove relationships between specs\n update <spec> Update spec metadata\n \n Discovery & Search:\n files <spec> List files in a spec\n list List all specs\n open <spec> Open spec in editor\n search <query> Full-text search with metadata filters\n view <spec> View spec content\n \n Project Analytics:\n board Show Kanban-style board view\n deps <spec> Show dependency graph for a spec\n gantt Show timeline with dependencies\n stats Show aggregate statistics\n timeline Show creation/completion over time\n \n Quality & Optimization:\n analyze <spec> Analyze spec complexity and structure\n check Check for sequence conflicts\n tokens [spec] Count tokens for LLM context management\n validate [specs...] Validate specs for quality issues\n \n Advanced Editing:\n compact <spec> Remove specified line ranges from spec\n split <spec> Split spec into multiple files\n \n Configuration:\n templates Manage spec templates\n \n Integration:\n mcp Start MCP server for AI assistants\n ui Start local web UI for spec management\n\nExamples:\n $ lean-spec init\n $ lean-spec create my-feature --priority high\n $ lean-spec list --status in-progress\n $ lean-spec view 042\n $ lean-spec link 085 --depends-on 042,035\n $ lean-spec link 085 --related 082\n $ lean-spec unlink 085 --depends-on 042\n $ lean-spec deps 085\n $ lean-spec backfill --dry-run\n $ lean-spec migrate ./docs/adr\n $ lean-spec migrate ./docs/rfcs --with copilot\n $ lean-spec board --tag backend\n $ lean-spec search \"authentication\"\n $ lean-spec validate\n $ lean-spec tokens 059\n $ lean-spec analyze 045 --json\n $ lean-spec split 045 --output README.md:1-150 --output DESIGN.md:151-end\n $ lean-spec ui\n $ lean-spec ui --port 3001 --no-open\n $ lean-spec ui --specs ./docs/specs --dry-run\n`);\n\n// Register all commands (alphabetically ordered)\nregisterCommands(program);\n\n// Parse and execute\nprogram.parse();\n"]}
@@ -9,7 +9,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
9
9
  */
10
10
 
11
11
  /**
12
- * Create the MCP server
12
+ * Create the MCP server with all tools, resources, and prompts
13
13
  */
14
14
  declare function createMcpServer(): Promise<McpServer>;
15
15
 
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- export { createMcpServer } from './chunk-7MCDTSVE.js';
2
+ export { createMcpServer } from './chunk-UHMYFCXJ.js';
3
3
  import './chunk-LVD7ZAVZ.js';
4
4
  //# sourceMappingURL=mcp-server.js.map
5
5
  //# sourceMappingURL=mcp-server.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lean-spec",
3
- "version": "0.2.2",
4
- "description": "Lightweight spec methodology for AI-powered development",
3
+ "version": "0.2.4",
4
+ "description": "Specification-driven development made simple",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "lean-spec": "./bin/lean-spec.js"
@@ -59,6 +59,7 @@
59
59
  "js-yaml": "^4.1.0",
60
60
  "marked": "^15.0.12",
61
61
  "marked-terminal": "^7.3.0",
62
+ "open": "^11.0.0",
62
63
  "ora": "^9.0.0",
63
64
  "react": "^19.2.0",
64
65
  "strip-ansi": "^7.1.2",
@@ -68,7 +69,6 @@
68
69
  },
69
70
  "devDependencies": {
70
71
  "@leanspec/core": "workspace:*",
71
- "@types/handlebars": "^4.1.0",
72
72
  "@types/js-yaml": "^4.0.9",
73
73
  "@types/marked-terminal": "^6.1.1",
74
74
  "@types/node": "^20.10.0",
@@ -78,6 +78,6 @@
78
78
  "vitest": "^4.0.6"
79
79
  },
80
80
  "engines": {
81
- "node": ">=18"
81
+ "node": ">=20"
82
82
  }
83
83
  }
@@ -2,4 +2,3 @@
2
2
  2. **Check specs/** - Review existing specs before starting
3
3
  3. **Follow LeanSpec principles** - Clarity over documentation
4
4
  4. **Keep it minimal** - If it doesn't add clarity, cut it
5
- 5. **Never use nested code blocks** - Markdown doesn't support code blocks within code blocks. Use indentation or describe the structure instead
@@ -2,4 +2,3 @@
2
2
  2. **Read specs/** - Check for compliance requirements
3
3
  3. **Follow LeanSpec** - Lean doesn't mean skipping governance
4
4
  4. **Document decisions** - Especially security and compliance choices
5
- 5. **Never use nested code blocks** - Markdown doesn't support code blocks within code blocks. Use indentation or describe the structure instead
@@ -0,0 +1 @@
1
+ 5. **Never use nested code blocks** - Markdown doesn't support code blocks within code blocks. Use indentation or describe the structure instead
@@ -0,0 +1,6 @@
1
+ **Additional commands:**
2
+ - `lean-spec list --tag=<tag>` - Find specs by tag (e.g., `--tag=security`)
3
+ - `lean-spec deps <spec>` - Check dependencies before starting work
4
+ - `lean-spec gantt` - View project timeline and milestones
5
+
6
+ **Note:** For enterprise, also consider dependencies and project timelines when planning work.
@@ -0,0 +1,3 @@
1
+ **Additional commands:**
2
+ - `lean-spec list --tag=<tag>` - Find specs by tag (e.g., `--tag=api`)
3
+ - `lean-spec deps <spec>` - Check dependencies before starting work
@@ -0,0 +1,29 @@
1
+ **Viewing specs (additional):**
2
+ - `lean-spec view <spec>/DESIGN.md` - View sub-spec file (DESIGN.md, TESTING.md, etc.)
3
+ - `lean-spec view <spec> --raw` - Get raw markdown (for parsing)
4
+ - `lean-spec view <spec> --json` - Get structured JSON
5
+ - `lean-spec files <spec>` - List all files in a spec (including sub-specs)
6
+
7
+ **Project Overview:**
8
+ - `lean-spec board` - Kanban view with project health summary
9
+ - `lean-spec stats` - Quick project metrics and insights
10
+ - `lean-spec stats --full` - Detailed analytics (all sections)
11
+
12
+ **Working with specs (additional):**
13
+ - `lean-spec update <spec> --status <status>` - Update spec status (REQUIRED - never edit frontmatter manually)
14
+ - `lean-spec update <spec> --priority <priority>` - Update spec priority (REQUIRED - never edit frontmatter manually)
15
+ - `lean-spec update <spec> --tags <tag1,tag2>` - Update spec tags (REQUIRED - never edit frontmatter manually)
16
+ - `lean-spec update <spec> --assignee <name>` - Update spec assignee (REQUIRED - never edit frontmatter manually)
17
+ - `lean-spec deps <spec>` - Show dependencies and relationships
18
+
19
+ **Token Management:**
20
+ - `lean-spec tokens <spec>` - Count tokens in a spec for LLM context management
21
+ - `lean-spec tokens` - Show token counts for all specs (sorted by token count)
22
+ - `lean-spec tokens <spec> --detailed` - Show content breakdown (prose vs code vs tables)
23
+
24
+ **Advanced Editing (AI Agent Orchestration):**
25
+ - `lean-spec analyze <spec>` - Analyze spec complexity and structure (returns JSON with --json flag)
26
+ - `lean-spec split <spec> --output FILE:LINES` - Split spec into multiple files by line ranges (spec 059)
27
+ - `lean-spec compact <spec> --remove LINES` - Remove specified line ranges from spec (spec 059)
28
+
29
+ **When in doubt (extended):** Run `lean-spec <command> --help` to discover available commands and options.
@@ -0,0 +1,15 @@
1
+ ## Essential Commands
2
+
3
+ **Discovery:**
4
+ - `lean-spec list` - See all specs
5
+ - `lean-spec search "<query>"` - Find relevant specs
6
+
7
+ **Viewing specs:**
8
+ - `lean-spec view <spec>` - View a spec (formatted)
9
+ - `lean-spec open <spec>` - Open spec in editor
10
+
11
+ **Working with specs:**
12
+ - `lean-spec create <name>` - Create a new spec
13
+ - `lean-spec update <spec> --status <status>` - Update spec status
14
+
15
+ **When in doubt:** Run `lean-spec --help` to see all available commands.
@@ -0,0 +1,18 @@
1
+ **Viewing specs (additional):**
2
+ - `lean-spec view <spec> --raw` - Get raw markdown
3
+ - `lean-spec files <spec>` - List all files in a spec
4
+
5
+ **Project Overview:**
6
+ - `lean-spec board` - Kanban view with project health summary
7
+ - `lean-spec stats` - Quick project metrics
8
+
9
+ **Working with specs (additional):**
10
+ - `lean-spec update <spec> --priority <priority>` - Update spec priority
11
+ - `lean-spec update <spec> --tags <tag1,tag2>` - Update spec tags
12
+ - `lean-spec deps <spec>` - Show dependencies and relationships
13
+
14
+ **Token Management:**
15
+ - `lean-spec tokens <spec>` - Count tokens in a spec
16
+ - `lean-spec tokens` - Show token counts for all specs
17
+
18
+ **When in doubt (extended):** Run `lean-spec <command> --help` to discover available commands.
@@ -2,5 +2,3 @@
2
2
  - Tests include security scenarios
3
3
  - Documentation complete
4
4
  - Compliance checklist completed
5
- - Code is clear and maintainable
6
- - Specs stay in sync with implementation
@@ -1,5 +1,3 @@
1
- - Code is clear and maintainable
2
1
  - Tests cover critical paths
3
2
  - No unnecessary complexity
4
3
  - Documentation where needed (not everywhere)
5
- - Specs stay in sync with implementation