@leanspec/cli 0.3.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.
Files changed (68) hide show
  1. package/README.md +267 -0
  2. package/bin/lean-spec-rust.js +201 -0
  3. package/bin/lean-spec.js +9 -0
  4. package/bin/leanspec-rust.js +193 -0
  5. package/bin/leanspec.js +9 -0
  6. package/binaries/darwin-arm64/leanspec +0 -0
  7. package/binaries/darwin-arm64/leanspec-http +0 -0
  8. package/binaries/darwin-arm64/leanspec-mcp +0 -0
  9. package/binaries/darwin-arm64/package.json +24 -0
  10. package/binaries/darwin-arm64/postinstall.js +17 -0
  11. package/binaries/darwin-x64/leanspec +0 -0
  12. package/binaries/darwin-x64/leanspec-http +0 -0
  13. package/binaries/darwin-x64/leanspec-mcp +0 -0
  14. package/binaries/darwin-x64/package.json +24 -0
  15. package/binaries/darwin-x64/postinstall.js +17 -0
  16. package/binaries/linux-x64/leanspec +0 -0
  17. package/binaries/linux-x64/leanspec-http +0 -0
  18. package/binaries/linux-x64/leanspec-mcp +0 -0
  19. package/binaries/linux-x64/package.json +24 -0
  20. package/binaries/linux-x64/postinstall.js +17 -0
  21. package/binaries/windows-x64/leanspec-http.exe +0 -0
  22. package/binaries/windows-x64/leanspec-mcp.exe +0 -0
  23. package/binaries/windows-x64/leanspec.exe +0 -0
  24. package/binaries/windows-x64/package.json +24 -0
  25. package/binaries/windows-x64/postinstall.js +6 -0
  26. package/package.json +48 -0
  27. package/templates/detailed/AGENTS-minimal.md +9 -0
  28. package/templates/detailed/AGENTS.md +114 -0
  29. package/templates/detailed/README.md +28 -0
  30. package/templates/detailed/config.json +20 -0
  31. package/templates/detailed/files/DESIGN.md +43 -0
  32. package/templates/detailed/files/PLAN.md +59 -0
  33. package/templates/detailed/files/README.md +30 -0
  34. package/templates/detailed/files/TEST.md +71 -0
  35. package/templates/examples/api-refactor/README.md +81 -0
  36. package/templates/examples/api-refactor/package.json +16 -0
  37. package/templates/examples/api-refactor/src/app.js +40 -0
  38. package/templates/examples/api-refactor/src/services/currencyService.js +43 -0
  39. package/templates/examples/api-refactor/src/services/timezoneService.js +41 -0
  40. package/templates/examples/api-refactor/src/services/weatherService.js +42 -0
  41. package/templates/examples/dark-theme/README.md +66 -0
  42. package/templates/examples/dark-theme/package.json +16 -0
  43. package/templates/examples/dark-theme/src/public/app.js +277 -0
  44. package/templates/examples/dark-theme/src/public/index.html +225 -0
  45. package/templates/examples/dark-theme/src/public/style.css +625 -0
  46. package/templates/examples/dark-theme/src/server.js +18 -0
  47. package/templates/examples/dashboard-widgets/README.md +70 -0
  48. package/templates/examples/dashboard-widgets/index.html +12 -0
  49. package/templates/examples/dashboard-widgets/package.json +22 -0
  50. package/templates/examples/dashboard-widgets/src/App.css +20 -0
  51. package/templates/examples/dashboard-widgets/src/App.jsx +16 -0
  52. package/templates/examples/dashboard-widgets/src/components/Dashboard.css +17 -0
  53. package/templates/examples/dashboard-widgets/src/components/Dashboard.jsx +15 -0
  54. package/templates/examples/dashboard-widgets/src/components/WidgetWrapper.css +23 -0
  55. package/templates/examples/dashboard-widgets/src/components/WidgetWrapper.jsx +16 -0
  56. package/templates/examples/dashboard-widgets/src/components/widgets/ChartWidget.css +33 -0
  57. package/templates/examples/dashboard-widgets/src/components/widgets/ChartWidget.jsx +28 -0
  58. package/templates/examples/dashboard-widgets/src/components/widgets/StatsWidget.css +24 -0
  59. package/templates/examples/dashboard-widgets/src/components/widgets/StatsWidget.jsx +22 -0
  60. package/templates/examples/dashboard-widgets/src/index.css +13 -0
  61. package/templates/examples/dashboard-widgets/src/main.jsx +10 -0
  62. package/templates/examples/dashboard-widgets/src/utils/mockData.js +30 -0
  63. package/templates/examples/dashboard-widgets/vite.config.js +6 -0
  64. package/templates/standard/AGENTS-minimal.md +10 -0
  65. package/templates/standard/AGENTS.md +114 -0
  66. package/templates/standard/README.md +25 -0
  67. package/templates/standard/config.json +18 -0
  68. package/templates/standard/files/README.md +42 -0
package/README.md ADDED
@@ -0,0 +1,267 @@
1
+ # LeanSpec
2
+
3
+ <p align="center">
4
+ <img src="https://github.com/codervisor/lean-spec-docs/blob/main/static/img/logo-with-bg.svg" alt="LeanSpec Logo" width="120" height="120">
5
+ </p>
6
+
7
+ <p align="center">
8
+ <a href="https://github.com/codervisor/lean-spec/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/codervisor/lean-spec/ci.yml?branch=main" alt="CI Status"></a>
9
+ <a href="https://www.npmjs.com/package/lean-spec"><img src="https://img.shields.io/npm/v/lean-spec.svg" alt="npm version"></a>
10
+ <a href="https://www.npmjs.com/package/lean-spec"><img src="https://img.shields.io/npm/dm/lean-spec.svg" alt="npm downloads"></a>
11
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
12
+ </p>
13
+
14
+ <p align="center">
15
+ <a href="https://www.lean-spec.dev"><strong>Documentation</strong></a>
16
+
17
+ <a href="https://www.lean-spec.dev/zh-Hans/docs/guide/"><strong>中文文档</strong></a>
18
+
19
+ <a href="https://web.lean-spec.dev"><strong>Live Examples</strong></a>
20
+
21
+ <a href="https://www.lean-spec.dev/docs/tutorials/first-spec-with-ai"><strong>Tutorials</strong></a>
22
+ </p>
23
+
24
+ ---
25
+
26
+ **The tool-agnostic spec framework. Use any spec backend — your workflow, your rules.**
27
+
28
+ LeanSpec is a spec coding framework that works with whatever spec workflow you already use. GitHub Issues for personal projects, ADO Work Items for enterprise, Jira, Linear, or plain markdown — LeanSpec provides the unified interface, AI integration, and intelligence layer on top.
29
+
30
+ ---
31
+
32
+ ## Quick Start
33
+
34
+ ```bash
35
+ # Markdown specs (default — works out of the box)
36
+ npm install -g @leanspec/cli && leanspec init
37
+
38
+ # Or try with a tutorial project
39
+ npx -p @leanspec/cli leanspec init --example dark-theme
40
+ cd dark-theme && npm install && npm start
41
+ ```
42
+
43
+ **Configure your spec backend:**
44
+
45
+ ```yaml
46
+ # leanspec.provider.yaml
47
+
48
+ # Option 1: Markdown files (default, zero config)
49
+ provider: markdown
50
+ directory: specs
51
+
52
+ # Option 2: GitHub Issues as specs
53
+ # provider: github
54
+ # owner: myuser
55
+ # repo: myproject
56
+
57
+ # Option 3: Azure DevOps Work Items as specs
58
+ # provider: ado
59
+ # organization: mycompany
60
+ # project: myproject
61
+ ```
62
+
63
+ **Visualize your project (works with any backend):**
64
+
65
+ ```bash
66
+ leanspec board # Kanban view
67
+ leanspec stats # Project metrics
68
+ leanspec ui # Web UI at localhost:3000
69
+ ```
70
+
71
+ **Next:** [Your First Spec with AI](https://www.lean-spec.dev/docs/tutorials/first-spec-with-ai) (10 min tutorial)
72
+
73
+ ---
74
+
75
+ ## Why LeanSpec?
76
+
77
+ **Your workflow, not ours.** Other SDD frameworks force you to adopt their spec format and tooling. LeanSpec adapts to whatever you already use:
78
+
79
+ - **Tool-agnostic** - GitHub Issues, ADO, Jira, Linear, Notion, or plain markdown
80
+ - **One interface** - Same CLI, MCP, and UI regardless of backend
81
+ - **AI-native** - Structured spec data for any AI coding assistant
82
+ - **Fast iteration** - Living documents that grow with your code
83
+ - **Context economy** - Small specs (<2K tokens) = better AI output
84
+
85
+ 📖 [Compare with Spec Kit, OpenSpec, Kiro →](https://www.lean-spec.dev/docs/guide/why-leanspec)
86
+
87
+ ---
88
+
89
+ ## AI Integration
90
+
91
+ Works with any AI coding assistant via MCP or CLI:
92
+
93
+ ```json
94
+ {
95
+ "mcpServers": {
96
+ "leanspec": { "command": "npx", "args": ["@leanspec/mcp"] }
97
+ }
98
+ }
99
+ ```
100
+
101
+ **Compatible with:** VS Code Copilot, Claude Code, Gemini CLI, Cursor, Windsurf, Kiro CLI, Kimi CLI, Qodo CLI, Amp, Trae Agent, Qwen Code, Droid, and more.
102
+
103
+ 📖 [Full AI integration guide →](https://www.lean-spec.dev/docs/guide/usage/ai-coding-workflow)
104
+
105
+ ---
106
+
107
+ ## Spec Providers
108
+
109
+ LeanSpec connects to your existing spec workflow through a provider architecture:
110
+
111
+ | Provider | Backend | Status |
112
+ |----------|---------|--------|
113
+ | `markdown` | Local `specs/` directory (default) | **Available** |
114
+ | `github` | GitHub Issues + Projects | Planned |
115
+ | `ado` | Azure DevOps Work Items | Planned |
116
+ | `jira` | Jira tickets | Future |
117
+ | `linear` | Linear issues | Future |
118
+
119
+ Core LeanSpec concepts map naturally to each backend:
120
+
121
+ | Concept | GitHub Issues | ADO Work Items | Markdown |
122
+ |---------|--------------|----------------|----------|
123
+ | Spec ID | Issue number | Work Item ID | Directory name |
124
+ | Status | Labels | State field | Frontmatter |
125
+ | Priority | Labels | Priority field | Frontmatter |
126
+ | Tags | Labels | Tags | Frontmatter |
127
+ | Assignee | Assignees | Assigned To | Frontmatter |
128
+ | Content | Issue body | Description | Markdown body |
129
+
130
+ 📖 [Provider architecture →](https://www.lean-spec.dev/docs/guide/providers)
131
+
132
+ ---
133
+
134
+ ## Agent Skills
135
+
136
+ Teach your AI assistant the Spec-Driven Development methodology:
137
+
138
+ ```bash
139
+ # Install the leanspec skill
140
+ npx skills add codervisor/lean-spec@leanspec
141
+ ```
142
+
143
+ This installs the **leanspec** skill which teaches AI agents:
144
+ - When to create specs vs. implement directly
145
+ - How to discover existing specs before creating new ones
146
+ - Best practices for context economy and progressive disclosure
147
+ - Complete SDD workflow (Discover → Design → Implement → Validate)
148
+
149
+ **Compatible with:** Claude Code, Cursor, Windsurf, GitHub Copilot, and other [Agent Skills](https://skills.sh/) compatible tools.
150
+
151
+ 📖 [Skill source →](skills/leanspec/SKILL.md)
152
+
153
+ ---
154
+
155
+ ## Features
156
+
157
+ | Feature | Description |
158
+ | ------------------- | ------------------------------------------------------------------------------------------------- |
159
+ | **📊 Kanban Board** | `leanspec board` - visual project tracking |
160
+ | **🔍 Smart Search** | `leanspec search` - find specs by content or metadata |
161
+ | **🔗 Dependencies** | Track spec relationships with `depends_on` and `related` |
162
+ | **🎨 Web UI** | `leanspec ui` - browser-based dashboard |
163
+ | **📈 Project Stats** | `leanspec stats` - health metrics and bottleneck detection |
164
+ | **🤖 AI-Native** | MCP server + CLI for AI assistants |
165
+ | **🖥️ Desktop App** | Desktop app repo: [codervisor/lean-spec-desktop](https://github.com/codervisor/lean-spec-desktop) |
166
+
167
+ <p align="center">
168
+ <img src="https://github.com/codervisor/lean-spec-docs/blob/main/static/img/ui/ui-board-view.png" alt="Kanban Board View" width="800">
169
+ </p>
170
+
171
+ ---
172
+
173
+ ## Requirements
174
+
175
+ ### Runtime
176
+ - **Node.js**: `>= 20.0.0`
177
+ - **pnpm**: `>= 10.0.0` (preferred package manager)
178
+
179
+ ### Development
180
+ - **Node.js**: `>= 20.0.0`
181
+ - **Rust**: `>= 1.70` (for building CLI/MCP/HTTP binaries)
182
+ - **pnpm**: `>= 10.0.0`
183
+
184
+ **Quick Check:**
185
+ ```bash
186
+ node --version # Should be v20.0.0 or higher
187
+ pnpm --version # Should be 10.0.0 or higher
188
+ rustc --version # Should be 1.70 or higher (dev only)
189
+ ```
190
+
191
+ ---
192
+
193
+ ## Desktop App
194
+
195
+ The desktop application has moved to a dedicated repository:
196
+
197
+ - https://github.com/codervisor/lean-spec-desktop
198
+
199
+ Use that repository for desktop development, CI, and release workflows.
200
+
201
+ ---
202
+
203
+ ## Developer Workflow
204
+
205
+ Common development tasks using `pnpm`:
206
+
207
+ ```bash
208
+ # Development
209
+ pnpm install # Install dependencies
210
+ pnpm build # Build all packages
211
+ pnpm dev # Start dev mode (UI + Core)
212
+ pnpm dev:web # UI only
213
+ pnpm dev:cli # CLI only
214
+
215
+ # Testing
216
+ pnpm test # Run all tests
217
+ pnpm test:ui # Tests with UI
218
+ pnpm test:coverage # Coverage report
219
+ pnpm typecheck # Type check all packages
220
+
221
+ # Rust
222
+ pnpm rust:build # Build Rust packages (release)
223
+ pnpm rust:build:dev # Build Rust (dev, faster)
224
+ pnpm rust:test # Run Rust tests
225
+ pnpm rust:check # Quick Rust check
226
+ pnpm rust:clippy # Rust linting
227
+ pnpm rust:fmt # Format Rust code
228
+
229
+ # CLI (run locally)
230
+ pnpm cli board # Show spec board
231
+ pnpm cli list # List specs
232
+ pnpm cli create my-feat # Create new spec
233
+ pnpm cli validate # Validate specs
234
+
235
+ # Documentation
236
+ pnpm docs:dev # Start docs site
237
+ pnpm docs:build # Build docs
238
+
239
+ # Release
240
+ pnpm pre-release # Run all pre-release checks
241
+ pnpm prepare-publish # Prepare for npm publish
242
+ pnpm restore-packages # Restore after publish
243
+ ```
244
+
245
+ See [package.json](package.json) for all available scripts.
246
+
247
+ ---
248
+
249
+ ## Documentation
250
+
251
+ 📖 [Full Documentation](https://www.lean-spec.dev) · [CLI Reference](https://www.lean-spec.dev/docs/reference/cli) · [First Principles](https://www.lean-spec.dev/docs/advanced/first-principles) · [FAQ](https://www.lean-spec.dev/docs/faq) · [中文文档](https://www.lean-spec.dev/zh-Hans/)
252
+
253
+ ## Community
254
+
255
+ 💬 [Discussions](https://github.com/codervisor/lean-spec/discussions) · 🐛 [Issues](https://github.com/codervisor/lean-spec/issues) · 🤝 [Contributing](CONTRIBUTING.md) · 📋 [Changelog](CHANGELOG.md) · 📄 [LICENSE](LICENSE)
256
+
257
+ ---
258
+
259
+ ### Contact Me | 联系我
260
+
261
+ If you find LeanSpec helpful, feel free to add me on WeChat (note "LeanSpec") to join the discussion group.
262
+
263
+ 如果您觉得 LeanSpec 对您有帮助,欢迎添加微信(备注 "LeanSpec")加入交流群。
264
+
265
+ <p align="center">
266
+ <img src="https://github.com/codervisor/lean-spec-docs/blob/main/static/img/qr-code.png" alt="WeChat Contact | 微信联系" height="280">
267
+ </p>
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * LeanSpec CLI Binary Wrapper
4
+ *
5
+ * This script detects the current platform and architecture,
6
+ * then spawns the appropriate Rust binary with the provided arguments.
7
+ *
8
+ * The wrapper looks for binaries in the following locations:
9
+ * 1. Rust target/debug binary (for local development)
10
+ * 2. Rust target/release binary (for local development)
11
+ * 3. Platform-specific npm package (lean-spec-darwin-x64, etc.)
12
+ * 4. Local binaries directory (fallback)
13
+ */
14
+
15
+ import { spawn } from 'child_process';
16
+ import { createRequire } from 'module';
17
+ import { fileURLToPath } from 'url';
18
+ import { dirname, join } from 'path';
19
+ import { accessSync, openSync, readSync, closeSync } from 'fs';
20
+
21
+ const require = createRequire(import.meta.url);
22
+ const __filename = fileURLToPath(import.meta.url);
23
+ const __dirname = dirname(__filename);
24
+
25
+ // Debug mode - enable with LEANSPEC_DEBUG=1
26
+ const DEBUG = process.env.LEANSPEC_DEBUG === '1';
27
+ const debug = (...args) => DEBUG && console.error('[lean-spec debug]', ...args);
28
+
29
+ // Platform detection mapping
30
+ const PLATFORM_MAP = {
31
+ darwin: { x64: 'darwin-x64', arm64: 'darwin-arm64' },
32
+ linux: { x64: 'linux-x64' },
33
+ win32: { x64: 'windows-x64' }
34
+ };
35
+
36
+ const MACHO_MAGICS = new Set([
37
+ 0xfeedface,
38
+ 0xfeedfacf,
39
+ 0xcefaedfe,
40
+ 0xcffaedfe,
41
+ 0xcafebabe,
42
+ 0xbebafeca,
43
+ ]);
44
+
45
+ function readHeaderBytes(filePath) {
46
+ const fd = openSync(filePath, 'r');
47
+ try {
48
+ const buffer = Buffer.alloc(4);
49
+ const bytesRead = readSync(fd, buffer, 0, 4, 0);
50
+ return bytesRead === 4 ? buffer : null;
51
+ } finally {
52
+ closeSync(fd);
53
+ }
54
+ }
55
+
56
+ function isValidBinaryHeader(filePath, platform) {
57
+ try {
58
+ const header = readHeaderBytes(filePath);
59
+ if (!header) return false;
60
+
61
+ if (platform === 'linux') {
62
+ return header[0] === 0x7f && header[1] === 0x45 && header[2] === 0x4c && header[3] === 0x46;
63
+ }
64
+
65
+ if (platform === 'win32') {
66
+ return header[0] === 0x4d && header[1] === 0x5a;
67
+ }
68
+
69
+ if (platform === 'darwin') {
70
+ const magicBE = header.readUInt32BE(0);
71
+ const magicLE = header.readUInt32LE(0);
72
+ return MACHO_MAGICS.has(magicBE) || MACHO_MAGICS.has(magicLE);
73
+ }
74
+
75
+ return false;
76
+ } catch (error) {
77
+ debug('Failed to read binary header:', error.message);
78
+ return false;
79
+ }
80
+ }
81
+
82
+ function isExecutableBinary(filePath, platform) {
83
+ if (!isValidBinaryHeader(filePath, platform)) {
84
+ debug('Invalid binary header:', filePath);
85
+ return false;
86
+ }
87
+ return true;
88
+ }
89
+
90
+ function getBinaryPath() {
91
+ const platform = process.platform;
92
+ const arch = process.arch;
93
+
94
+ debug('Platform detection:', { platform, arch });
95
+
96
+ const platformKey = PLATFORM_MAP[platform]?.[arch];
97
+ if (!platformKey) {
98
+ console.error(`Unsupported platform: ${platform}-${arch}`);
99
+ console.error('Supported: macOS (x64/arm64), Linux (x64), Windows (x64)');
100
+ process.exit(1);
101
+ }
102
+
103
+ const isWindows = platform === 'win32';
104
+ const binaryName = isWindows ? 'lean-spec.exe' : 'lean-spec';
105
+ const packageName = `@leanspec/cli-${platformKey}`;
106
+
107
+ debug('Binary info:', { platformKey, binaryName, packageName });
108
+
109
+ // Try rust/target/debug directory first (for local development with `pnpm build:rust`)
110
+ try {
111
+ const rustDebugPath = join(__dirname, '..', '..', '..', 'rust', 'target', 'debug', binaryName);
112
+ debug('Trying rust debug binary:', rustDebugPath);
113
+ accessSync(rustDebugPath);
114
+ if (isExecutableBinary(rustDebugPath, platform)) {
115
+ debug('Found rust debug binary:', rustDebugPath);
116
+ return rustDebugPath;
117
+ }
118
+ debug('Rust debug binary is invalid:', rustDebugPath);
119
+ } catch (e) {
120
+ debug('Rust debug binary not found:', e.message);
121
+ }
122
+
123
+ // Try rust/target/release directory (for local development with `pnpm build:rust:release`)
124
+ try {
125
+ const rustTargetPath = join(__dirname, '..', '..', '..', 'rust', 'target', 'release', binaryName);
126
+ debug('Trying rust release binary:', rustTargetPath);
127
+ accessSync(rustTargetPath);
128
+ if (isExecutableBinary(rustTargetPath, platform)) {
129
+ debug('Found rust release binary:', rustTargetPath);
130
+ return rustTargetPath;
131
+ }
132
+ debug('Rust release binary is invalid:', rustTargetPath);
133
+ } catch (e) {
134
+ debug('Rust release binary not found:', e.message);
135
+ }
136
+
137
+ // Try to resolve platform package
138
+ try {
139
+ const resolvedPath = require.resolve(`${packageName}/${binaryName}`);
140
+ if (isExecutableBinary(resolvedPath, platform)) {
141
+ debug('Found platform package binary:', resolvedPath);
142
+ return resolvedPath;
143
+ }
144
+ debug('Platform package binary is invalid:', resolvedPath);
145
+ } catch (e) {
146
+ debug('Platform package not found:', packageName, '-', e.message);
147
+ }
148
+
149
+ // Try local binaries directory (fallback)
150
+ try {
151
+ const localPath = join(__dirname, '..', 'binaries', platformKey, binaryName);
152
+ debug('Trying local binary:', localPath);
153
+ accessSync(localPath);
154
+ if (isExecutableBinary(localPath, platform)) {
155
+ debug('Found local binary:', localPath);
156
+ return localPath;
157
+ }
158
+ debug('Local binary is invalid:', localPath);
159
+ } catch (e) {
160
+ debug('Local binary not found:', e.message);
161
+ }
162
+
163
+ console.error(`Binary not found for ${platform}-${arch}`);
164
+ console.error(`Expected package: ${packageName}`);
165
+ console.error('');
166
+ console.error('Detected missing or corrupted binary.');
167
+ console.error('If you installed globally, reinstall to restore the binary:');
168
+ console.error(' npm uninstall -g lean-spec && npm install -g lean-spec');
169
+ console.error('');
170
+ console.error('If your npm config omits optional dependencies, enable them and reinstall.');
171
+ console.error('');
172
+ console.error('To install:');
173
+ console.error(' npm install -g lean-spec');
174
+ console.error('');
175
+ console.error('If you installed globally, try:');
176
+ console.error(' npm uninstall -g lean-spec && npm install -g lean-spec');
177
+ process.exit(1);
178
+ }
179
+
180
+ // Execute binary
181
+ const binaryPath = getBinaryPath();
182
+ const args = process.argv.slice(2);
183
+
184
+ debug('Spawning binary:', binaryPath);
185
+ debug('Arguments:', args);
186
+
187
+ const child = spawn(binaryPath, args, {
188
+ stdio: 'inherit',
189
+ windowsHide: true,
190
+ });
191
+
192
+ child.on('exit', (code) => {
193
+ debug('Binary exited with code:', code);
194
+ process.exit(code ?? 1);
195
+ });
196
+
197
+ child.on('error', (err) => {
198
+ console.error('Failed to start lean-spec:', err.message);
199
+ debug('Spawn error:', err);
200
+ process.exit(1);
201
+ });
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * LeanSpec CLI (deprecated entry point)
4
+ *
5
+ * The `lean-spec` command has been renamed to `leanspec`.
6
+ * This wrapper shows a deprecation notice and delegates to the new entry point.
7
+ */
8
+ console.error('\x1b[33m⚠ "lean-spec" is deprecated. Use "leanspec" instead.\x1b[0m');
9
+ import './leanspec-rust.js';
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * LeanSpec CLI Binary Wrapper
4
+ *
5
+ * This script detects the current platform and architecture,
6
+ * then spawns the appropriate Rust binary with the provided arguments.
7
+ *
8
+ * The wrapper looks for binaries in the following locations:
9
+ * 1. Rust target/debug binary (for local development)
10
+ * 2. Rust target/release binary (for local development)
11
+ * 3. Platform-specific npm package (leanspec-darwin-x64, etc.)
12
+ * 4. Local binaries directory (fallback)
13
+ */
14
+
15
+ import { spawn } from 'child_process';
16
+ import { createRequire } from 'module';
17
+ import { fileURLToPath } from 'url';
18
+ import { dirname, join } from 'path';
19
+ import { accessSync, openSync, readSync, closeSync } from 'fs';
20
+
21
+ const require = createRequire(import.meta.url);
22
+ const __filename = fileURLToPath(import.meta.url);
23
+ const __dirname = dirname(__filename);
24
+
25
+ // Debug mode - enable with LEANSPEC_DEBUG=1
26
+ const DEBUG = process.env.LEANSPEC_DEBUG === '1';
27
+ const debug = (...args) => DEBUG && console.error('[leanspec debug]', ...args);
28
+
29
+ // Platform detection mapping
30
+ const PLATFORM_MAP = {
31
+ darwin: { x64: 'darwin-x64', arm64: 'darwin-arm64' },
32
+ linux: { x64: 'linux-x64' },
33
+ win32: { x64: 'windows-x64' }
34
+ };
35
+
36
+ const MACHO_MAGICS = new Set([
37
+ 0xfeedface,
38
+ 0xfeedfacf,
39
+ 0xcefaedfe,
40
+ 0xcffaedfe,
41
+ 0xcafebabe,
42
+ 0xbebafeca,
43
+ ]);
44
+
45
+ function readHeaderBytes(filePath) {
46
+ const fd = openSync(filePath, 'r');
47
+ try {
48
+ const buffer = Buffer.alloc(4);
49
+ const bytesRead = readSync(fd, buffer, 0, 4, 0);
50
+ return bytesRead === 4 ? buffer : null;
51
+ } finally {
52
+ closeSync(fd);
53
+ }
54
+ }
55
+
56
+ function isValidBinaryHeader(filePath, platform) {
57
+ try {
58
+ const header = readHeaderBytes(filePath);
59
+ if (!header) return false;
60
+
61
+ if (platform === 'linux') {
62
+ return header[0] === 0x7f && header[1] === 0x45 && header[2] === 0x4c && header[3] === 0x46;
63
+ }
64
+
65
+ if (platform === 'win32') {
66
+ return header[0] === 0x4d && header[1] === 0x5a;
67
+ }
68
+
69
+ if (platform === 'darwin') {
70
+ const magicBE = header.readUInt32BE(0);
71
+ const magicLE = header.readUInt32LE(0);
72
+ return MACHO_MAGICS.has(magicBE) || MACHO_MAGICS.has(magicLE);
73
+ }
74
+
75
+ return false;
76
+ } catch (error) {
77
+ debug('Failed to read binary header:', error.message);
78
+ return false;
79
+ }
80
+ }
81
+
82
+ function isExecutableBinary(filePath, platform) {
83
+ if (!isValidBinaryHeader(filePath, platform)) {
84
+ debug('Invalid binary header:', filePath);
85
+ return false;
86
+ }
87
+ return true;
88
+ }
89
+
90
+ function getBinaryPath() {
91
+ const platform = process.platform;
92
+ const arch = process.arch;
93
+
94
+ debug('Platform detection:', { platform, arch });
95
+
96
+ const platformKey = PLATFORM_MAP[platform]?.[arch];
97
+ if (!platformKey) {
98
+ console.error(`Unsupported platform: ${platform}-${arch}`);
99
+ console.error('Supported: macOS (x64/arm64), Linux (x64), Windows (x64)');
100
+ process.exit(1);
101
+ }
102
+
103
+ const isWindows = platform === 'win32';
104
+ const binaryName = isWindows ? 'leanspec.exe' : 'leanspec';
105
+ const legacyBinaryName = isWindows ? 'lean-spec.exe' : 'lean-spec';
106
+ const packageName = `@leanspec/cli-${platformKey}`;
107
+
108
+ debug('Binary info:', { platformKey, binaryName, packageName });
109
+
110
+ // Try rust/target/debug directory first (for local development with `pnpm build:rust`)
111
+ for (const targetDir of ['debug', 'release']) {
112
+ for (const name of [binaryName, legacyBinaryName]) {
113
+ try {
114
+ const rustPath = join(__dirname, '..', '..', '..', 'rust', 'target', targetDir, name);
115
+ debug(`Trying rust ${targetDir} binary:`, rustPath);
116
+ accessSync(rustPath);
117
+ if (isExecutableBinary(rustPath, platform)) {
118
+ debug(`Found rust ${targetDir} binary:`, rustPath);
119
+ return rustPath;
120
+ }
121
+ debug(`Rust ${targetDir} binary is invalid:`, rustPath);
122
+ } catch (e) {
123
+ debug(`Rust ${targetDir} binary not found:`, e.message);
124
+ }
125
+ }
126
+ }
127
+
128
+ // Try to resolve platform package (try new name first, then legacy)
129
+ for (const name of [binaryName, legacyBinaryName]) {
130
+ try {
131
+ const resolvedPath = require.resolve(`${packageName}/${name}`);
132
+ if (isExecutableBinary(resolvedPath, platform)) {
133
+ debug('Found platform package binary:', resolvedPath);
134
+ return resolvedPath;
135
+ }
136
+ debug('Platform package binary is invalid:', resolvedPath);
137
+ } catch (e) {
138
+ debug('Platform package not found:', packageName, name, '-', e.message);
139
+ }
140
+ }
141
+
142
+ // Try local binaries directory (fallback, try new name first then legacy)
143
+ for (const name of [binaryName, legacyBinaryName]) {
144
+ try {
145
+ const localPath = join(__dirname, '..', 'binaries', platformKey, name);
146
+ debug('Trying local binary:', localPath);
147
+ accessSync(localPath);
148
+ if (isExecutableBinary(localPath, platform)) {
149
+ debug('Found local binary:', localPath);
150
+ return localPath;
151
+ }
152
+ debug('Local binary is invalid:', localPath);
153
+ } catch (e) {
154
+ debug('Local binary not found:', e.message);
155
+ }
156
+ }
157
+
158
+ console.error(`Binary not found for ${platform}-${arch}`);
159
+ console.error(`Expected package: ${packageName}`);
160
+ console.error('');
161
+ console.error('Detected missing or corrupted binary.');
162
+ console.error('If you installed globally, reinstall to restore the binary:');
163
+ console.error(' npm uninstall -g leanspec && npm install -g leanspec');
164
+ console.error('');
165
+ console.error('If your npm config omits optional dependencies, enable them and reinstall.');
166
+ console.error('');
167
+ console.error('To install:');
168
+ console.error(' npm install -g leanspec');
169
+ process.exit(1);
170
+ }
171
+
172
+ // Execute binary
173
+ const binaryPath = getBinaryPath();
174
+ const args = process.argv.slice(2);
175
+
176
+ debug('Spawning binary:', binaryPath);
177
+ debug('Arguments:', args);
178
+
179
+ const child = spawn(binaryPath, args, {
180
+ stdio: 'inherit',
181
+ windowsHide: true,
182
+ });
183
+
184
+ child.on('exit', (code) => {
185
+ debug('Binary exited with code:', code);
186
+ process.exit(code ?? 1);
187
+ });
188
+
189
+ child.on('error', (err) => {
190
+ console.error('Failed to start leanspec:', err.message);
191
+ debug('Spawn error:', err);
192
+ process.exit(1);
193
+ });
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * LeanSpec CLI Entry Point
4
+ *
5
+ * Thin wrapper that delegates to the Rust binary.
6
+ *
7
+ * @see spec 181-typescript-deprecation-rust-migration
8
+ */
9
+ import './leanspec-rust.js';
Binary file