@sigx/cli 0.2.7 → 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 (37) hide show
  1. package/README.md +8 -59
  2. package/dist/cli.js +4 -3
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/create.d.ts +1 -1
  5. package/dist/commands/create.js +48 -413
  6. package/dist/commands/create.js.map +1 -1
  7. package/dist/commands/scaffold.js +165 -0
  8. package/dist/commands/scaffold.js.map +1 -0
  9. package/dist/index.d.ts +1 -1
  10. package/dist/plugin.d.ts +54 -0
  11. package/dist/plugin.js.map +1 -1
  12. package/dist/shell/index.d.ts +23 -0
  13. package/dist/shell/index.js +503 -0
  14. package/dist/shell/index.js.map +1 -0
  15. package/dist/templates/basic-daisyui/src/App.tsx +15 -4
  16. package/dist/templates/lynx/src/lynx-env.d.ts +1 -0
  17. package/dist/templates/lynx/src/main.tsx +2 -2
  18. package/dist/templates/lynx-daisyui/src/lynx-env.d.ts +1 -0
  19. package/dist/templates/lynx-daisyui/src/main.tsx +2 -2
  20. package/dist/templates/lynx-tailwind/src/lynx-env.d.ts +1 -0
  21. package/dist/templates/lynx-tailwind/src/main.tsx +2 -2
  22. package/dist/templates/ssg/package.json +4 -3
  23. package/dist/templates/ssg-daisyui/package.json +4 -3
  24. package/dist/templates/ssg-tailwind/package.json +4 -3
  25. package/dist/templates/ssr-daisyui/src/pages/Home.tsx +16 -4
  26. package/package.json +7 -3
  27. package/templates/basic-daisyui/src/App.tsx +15 -4
  28. package/templates/lynx/src/lynx-env.d.ts +1 -0
  29. package/templates/lynx/src/main.tsx +2 -2
  30. package/templates/lynx-daisyui/src/lynx-env.d.ts +1 -0
  31. package/templates/lynx-daisyui/src/main.tsx +2 -2
  32. package/templates/lynx-tailwind/src/lynx-env.d.ts +1 -0
  33. package/templates/lynx-tailwind/src/main.tsx +2 -2
  34. package/templates/ssg/package.json +4 -3
  35. package/templates/ssg-daisyui/package.json +4 -3
  36. package/templates/ssg-tailwind/package.json +4 -3
  37. package/templates/ssr-daisyui/src/pages/Home.tsx +16 -4
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @sigx/cli
2
2
 
3
- The unified `sigx` command-line tool — daily-driver CLI for [SignalX](https://github.com/signalxjs/core) projects (web and Lynx).
3
+ The unified `sigx` command-line tool — daily-driver CLI for [SignalX](https://sigx.dev/core/) projects (web and Lynx).
4
4
 
5
5
  ```bash
6
6
  npm i -D @sigx/cli
@@ -8,70 +8,19 @@ npm i -D @sigx/cli
8
8
  npx sigx <command>
9
9
  ```
10
10
 
11
- After scaffolding with `npm create @sigx@latest`, the generated project depends on `@sigx/cli` directly. The `sigx` binary is the entry point for everything: scaffolding, dev servers, builds, previews, and platform-specific commands provided by plugins.
11
+ After scaffolding with `npm create @sigx@latest`, the generated project depends on `@sigx/cli` directly. The `sigx` binary is the single entry point for everything: scaffolding, dev servers, builds, previews, and platform-specific commands provided by plugins. Any package can extend `sigx` with new commands via a `sigx-cli.plugin` field in its `package.json`.
12
12
 
13
- ## Commands
13
+ ## 📚 Documentation
14
14
 
15
- | Command | Provided by | Description |
16
- |---|---|---|
17
- | `sigx create [name]` | core | Scaffold a new project (interactive TUI; falls back to flag-driven headless when stdout isn't a TTY or when `--yes` / `--type` is passed) |
18
- | `sigx info` | core | Print environment & project info (Node, pnpm, installed `@sigx/*` packages, Lynx toolchain) |
19
- | `sigx dev` | `@sigx/vite` plugin | Start the Vite dev server (web) |
20
- | `sigx build` | `@sigx/vite` plugin | Production build (web) |
21
- | `sigx preview` | `@sigx/ssg` plugin | Serve a built SSG site |
22
- | `sigx prebuild` | `@sigx/lynx-cli` | Generate native iOS/Android project files |
23
- | `sigx run:android` | `@sigx/lynx-cli` | Build & launch on Android |
24
- | `sigx run:ios` | `@sigx/lynx-cli` | Build & launch on iOS |
25
- | `sigx doctor` | `@sigx/lynx-cli` | Verify Lynx toolchain (rspeedy, ADB, Xcode, JDK) |
26
-
27
- Run `sigx --help` for the full live list (varies by what plugins are installed in your project).
28
-
29
- ## Headless `create` (CI / scripts)
15
+ Full command reference, headless `create` flags, the plugin API and live examples → **<https://sigx.dev/cli/>**
30
16
 
31
17
  ```bash
32
- sigx create my-app --type basic # web SPA
33
- sigx create my-app --type ssr --styling tailwind # SSR + Tailwind
34
- sigx create my-app --type ssg --styling daisyui # SSG + Tailwind + daisyUI
35
- sigx create my-app --type lynx --styling tailwind # native mobile (Lynx)
36
- ```
37
-
38
- Valid values:
39
-
40
- - `--type`: `basic` | `ssr` | `ssg` | `lynx`
41
- - `--styling`: `none` | `tailwind` | `daisyui` (daisyUI not available for `lynx`)
42
- - `--yes` / `-y`: skip prompts even when running in a TTY
43
-
44
- Headless mode is also auto-selected when stdin/stdout is not a TTY (e.g. CI runners).
45
-
46
- ## Plugins
47
-
48
- Any package can extend `sigx` with new commands. To author a plugin, ship a `sigx-cli.plugin` field in your `package.json` pointing at a module that default-exports a `SigxPlugin`:
49
-
50
- ```json
51
- {
52
- "name": "my-sigx-plugin",
53
- "sigx-cli": { "plugin": "./dist/plugin.js" }
54
- }
18
+ sigx create my-app # scaffold a project (interactive)
19
+ sigx dev # start the dev server
20
+ sigx build # production build
55
21
  ```
56
22
 
57
- ```ts
58
- // my-sigx-plugin/src/plugin.ts
59
- import type { SigxPlugin } from '@sigx/cli/plugin';
60
-
61
- export default {
62
- name: 'my-plugin',
63
- commands: {
64
- hello: {
65
- description: 'Say hello',
66
- async run({ cwd, args, logger }) {
67
- logger.info(`hello from ${cwd}`);
68
- },
69
- },
70
- },
71
- } satisfies SigxPlugin;
72
- ```
73
-
74
- Plugins are discovered by walking up from `cwd` and inspecting installed dependencies' `package.json` files for the `sigx-cli.plugin` key.
23
+ Run `sigx --help` for the full live list (varies by what plugins are installed in your project).
75
24
 
76
25
  ## License
77
26
 
package/dist/cli.js CHANGED
@@ -182,7 +182,7 @@ function createLogger(prefix = "sigx") {
182
182
  var __dirname = dirname(fileURLToPath(import.meta.url));
183
183
  var pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
184
184
  var logger = createLogger();
185
- function wrapPluginCommand(cmd) {
185
+ function wrapPluginCommand(cmd, plugins) {
186
186
  return defineCommand({
187
187
  meta: { description: cmd.description },
188
188
  args: cmd.args,
@@ -190,7 +190,8 @@ function wrapPluginCommand(cmd) {
190
190
  await cmd.run({
191
191
  cwd: process.cwd(),
192
192
  args,
193
- logger
193
+ logger,
194
+ plugins
194
195
  });
195
196
  }
196
197
  });
@@ -210,7 +211,7 @@ async function main() {
210
211
  });
211
212
  for (const plugin of plugins) for (const [name, cmd] of Object.entries(plugin.commands)) {
212
213
  if (subCommands[name]) logger.warn(`Plugin "${plugin.name}" overrides command "${name}"`);
213
- subCommands[name] = wrapPluginCommand(cmd);
214
+ subCommands[name] = wrapPluginCommand(cmd, plugins);
214
215
  }
215
216
  await runMain(defineCommand({
216
217
  meta: {
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":[],"sources":["../src/discover.ts","../__vite-browser-external","../src/commands/info.ts","../src/utils/logger.ts","../src/cli.ts"],"sourcesContent":["/**\n * Auto-discover sigx CLI plugins from the project's dependencies.\n *\n * Scans the project's package.json for dependencies that declare\n * a `\"sigx-cli\": { \"plugin\": \"./path/to/plugin.js\" }` field.\n * Loads each plugin and calls `detect(cwd)` to check if it applies.\n */\n\nimport { readFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { SigxPlugin } from './plugin.js';\n\ninterface DepPackageJson {\n 'sigx-cli'?: {\n plugin: string;\n };\n}\n\nexport async function discoverPlugins(cwd: string): Promise<SigxPlugin[]> {\n const pkgPath = join(cwd, 'package.json');\n if (!existsSync(pkgPath)) return [];\n\n let pkg: Record<string, unknown>;\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n } catch {\n return [];\n }\n\n const allDeps: Record<string, string> = {\n ...(pkg.dependencies as Record<string, string> | undefined),\n ...(pkg.devDependencies as Record<string, string> | undefined),\n };\n\n const plugins: SigxPlugin[] = [];\n\n for (const depName of Object.keys(allDeps)) {\n try {\n const depPkgPath = join(cwd, 'node_modules', depName, 'package.json');\n if (!existsSync(depPkgPath)) continue;\n\n const depPkg: DepPackageJson = JSON.parse(readFileSync(depPkgPath, 'utf-8'));\n const pluginField = depPkg['sigx-cli'];\n if (!pluginField?.plugin) continue;\n\n const pluginPath = join(cwd, 'node_modules', depName, pluginField.plugin);\n if (!existsSync(pluginPath)) continue;\n\n const mod = await import(pathToFileURL(pluginPath).href);\n const plugin: SigxPlugin = mod.default || mod;\n\n if (typeof plugin.detect === 'function' && plugin.detect(cwd)) {\n plugins.push(plugin);\n }\n } catch {\n // Not a valid plugin or failed to load — skip silently\n }\n }\n\n return plugins;\n}\n","module.exports = {}","import { defineCommand } from 'citty';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { execSync } from 'node:child_process';\n\nfunction getVersion(cmd: string): string | null {\n try {\n return execSync(cmd, { stdio: 'pipe', encoding: 'utf-8' }).trim();\n } catch {\n return null;\n }\n}\n\nexport const infoCommand = defineCommand({\n meta: {\n name: 'info',\n description: 'Print environment and project info',\n },\n run() {\n const cwd = process.cwd();\n\n console.log('\\n \\x1b[1msigx environment info\\x1b[0m\\n');\n console.log(` Platform: ${process.platform} ${process.arch}`);\n console.log(` Node: ${process.version}`);\n\n // Package manager\n const pnpmVer = getVersion('pnpm --version');\n if (pnpmVer) console.log(` pnpm: v${pnpmVer}`);\n else {\n const npmVer = getVersion('npm --version');\n if (npmVer) console.log(` npm: v${npmVer}`);\n }\n\n // Project info\n const pkgPath = join(cwd, 'package.json');\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n console.log(` Project: ${pkg.name || '(unnamed)'} v${pkg.version || '0.0.0'}`);\n\n // Detect sigx packages\n const allDeps: Record<string, string> = {\n ...pkg.dependencies,\n ...pkg.devDependencies,\n };\n const sigxPkgs = Object.entries(allDeps)\n .filter(([name]) => name.startsWith('@sigx/') || name === 'sigx')\n .map(([name, version]) => `${name}@${version}`);\n\n if (sigxPkgs.length > 0) {\n console.log(` Sigx packages:`);\n for (const p of sigxPkgs) {\n console.log(` - ${p}`);\n }\n }\n } catch {\n // ignore parse errors\n }\n } else {\n console.log(' Project: (no package.json found)');\n }\n\n // Config files\n const configs = [\n { file: 'sigx.lynx.config.ts', label: 'Lynx' },\n { file: 'sigx.lynx.config.js', label: 'Lynx' },\n { file: 'lynx.config.ts', label: 'Lynx (rspeedy)' },\n { file: 'lynx.config.js', label: 'Lynx (rspeedy)' },\n { file: 'ssg.config.ts', label: 'SSG' },\n { file: 'vite.config.ts', label: 'Vite' },\n ];\n const detected = configs.filter(c => existsSync(join(cwd, c.file)));\n if (detected.length > 0) {\n console.log(` Config files:`);\n for (const c of detected) {\n console.log(` - ${c.file} (${c.label})`);\n }\n }\n\n // Lynx-specific info\n const isLynx = detected.some(c => c.label.includes('Lynx'));\n if (isLynx) {\n console.log('');\n console.log(' \\x1b[1mLynx Environment\\x1b[0m');\n\n // rspeedy\n const rspeedyVer = getVersion('npx rspeedy --version 2>&1');\n if (rspeedyVer) console.log(` rspeedy: v${rspeedyVer.trim()}`);\n\n // Android SDK\n const androidHome = process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT;\n if (androidHome) {\n console.log(` Android SDK: ${androidHome}`);\n }\n\n // JDK\n const javaVer = getVersion('java -version 2>&1');\n if (javaVer) {\n const match = javaVer.match(/version \"([^\"]+)\"/);\n if (match) console.log(` JDK: ${match[1]}`);\n }\n\n // ADB + devices\n try {\n const adbVer = getVersion('adb version');\n if (adbVer) {\n const verMatch = adbVer.match(/version ([\\d.]+)/);\n console.log(` ADB: ${verMatch ? `v${verMatch[1]}` : 'available'}`);\n\n const devOutput = execSync('adb devices -l', { stdio: 'pipe', encoding: 'utf-8' });\n const devices = devOutput.split('\\n')\n .slice(1)\n .filter(l => l.trim().length > 0)\n .map(l => {\n const modelMatch = l.match(/model:(\\S+)/);\n return modelMatch ? modelMatch[1].replace(/_/g, ' ') : l.split(/\\s+/)[0];\n });\n\n if (devices.length > 0) {\n console.log(` Devices: ${devices.join(', ')}`);\n }\n }\n } catch {\n // ADB not available\n }\n\n // Xcode (macOS only)\n if (process.platform === 'darwin') {\n const xcodeVer = getVersion('xcodebuild -version 2>/dev/null');\n if (xcodeVer) console.log(` Xcode: ${xcodeVer.split('\\n')[0]}`);\n\n const podVer = getVersion('pod --version');\n if (podVer) console.log(` CocoaPods: v${podVer}`);\n }\n }\n\n console.log('');\n },\n});\n","import type { Logger } from '../plugin.js';\n\nexport function createLogger(prefix = 'sigx'): Logger {\n return {\n log: (msg: string) => console.log(`[${prefix}] ${msg}`),\n warn: (msg: string) => console.warn(`[${prefix}] WARN: ${msg}`),\n error: (msg: string) => console.error(`[${prefix}] ERROR: ${msg}`),\n };\n}\n","#!/usr/bin/env node\n\n/**\n * sigx CLI — unified command-line tool for SignalX projects.\n *\n * Core commands (always available):\n * sigx create — scaffold a new project\n * sigx info — print environment info\n *\n * Plugin commands (auto-discovered from installed packages):\n * sigx dev — start dev server\n * sigx build — production build\n * sigx preview — preview build (SSG)\n * sigx prebuild — generate native project files (Lynx)\n * sigx run:android / run:ios — build + launch on device (Lynx)\n */\n\nimport { defineCommand, runMain } from 'citty';\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { discoverPlugins } from './discover.js';\nimport { infoCommand } from './commands/info.js';\nimport { createLogger } from './utils/logger.js';\nimport type { PluginCommand } from './plugin.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));\nconst logger = createLogger();\n\nfunction wrapPluginCommand(cmd: PluginCommand) {\n return defineCommand({\n meta: { description: cmd.description },\n args: cmd.args as any,\n async run({ args }) {\n await cmd.run({ cwd: process.cwd(), args, logger });\n },\n });\n}\n\nasync function main() {\n const cwd = process.cwd();\n const plugins = await discoverPlugins(cwd);\n\n // Build subcommand map: core + plugin commands\n const subCommands: Record<string, ReturnType<typeof defineCommand>> = {\n info: infoCommand,\n };\n\n // Lazy-load create command only when needed (it pulls in @sigx/terminal)\n subCommands.create = defineCommand({\n meta: { name: 'create', description: 'Scaffold a new SignalX project' },\n async run() {\n const { runCreate } = await import('./commands/create.js');\n await runCreate();\n },\n });\n\n // Register plugin commands\n for (const plugin of plugins) {\n for (const [name, cmd] of Object.entries(plugin.commands)) {\n if (subCommands[name]) {\n // Command conflict — last plugin wins, but warn\n logger.warn(`Plugin \"${plugin.name}\" overrides command \"${name}\"`);\n }\n subCommands[name] = wrapPluginCommand(cmd);\n }\n }\n\n const mainCommand = defineCommand({\n meta: {\n name: 'sigx',\n version: pkg.version,\n description: 'SignalX CLI',\n },\n subCommands,\n });\n\n await runMain(mainCommand);\n}\n\nmain().catch((err) => {\n logger.error(err.message || String(err));\n process.exit(1);\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../src/discover.ts","../__vite-browser-external","../src/commands/info.ts","../src/utils/logger.ts","../src/cli.ts"],"sourcesContent":["/**\n * Auto-discover sigx CLI plugins from the project's dependencies.\n *\n * Scans the project's package.json for dependencies that declare\n * a `\"sigx-cli\": { \"plugin\": \"./path/to/plugin.js\" }` field.\n * Loads each plugin and calls `detect(cwd)` to check if it applies.\n */\n\nimport { readFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { SigxPlugin } from './plugin.js';\n\ninterface DepPackageJson {\n 'sigx-cli'?: {\n plugin: string;\n };\n}\n\nexport async function discoverPlugins(cwd: string): Promise<SigxPlugin[]> {\n const pkgPath = join(cwd, 'package.json');\n if (!existsSync(pkgPath)) return [];\n\n let pkg: Record<string, unknown>;\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n } catch {\n return [];\n }\n\n const allDeps: Record<string, string> = {\n ...(pkg.dependencies as Record<string, string> | undefined),\n ...(pkg.devDependencies as Record<string, string> | undefined),\n };\n\n const plugins: SigxPlugin[] = [];\n\n for (const depName of Object.keys(allDeps)) {\n try {\n const depPkgPath = join(cwd, 'node_modules', depName, 'package.json');\n if (!existsSync(depPkgPath)) continue;\n\n const depPkg: DepPackageJson = JSON.parse(readFileSync(depPkgPath, 'utf-8'));\n const pluginField = depPkg['sigx-cli'];\n if (!pluginField?.plugin) continue;\n\n const pluginPath = join(cwd, 'node_modules', depName, pluginField.plugin);\n if (!existsSync(pluginPath)) continue;\n\n const mod = await import(pathToFileURL(pluginPath).href);\n const plugin: SigxPlugin = mod.default || mod;\n\n if (typeof plugin.detect === 'function' && plugin.detect(cwd)) {\n plugins.push(plugin);\n }\n } catch {\n // Not a valid plugin or failed to load — skip silently\n }\n }\n\n return plugins;\n}\n","module.exports = {}","import { defineCommand } from 'citty';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { execSync } from 'node:child_process';\n\nfunction getVersion(cmd: string): string | null {\n try {\n return execSync(cmd, { stdio: 'pipe', encoding: 'utf-8' }).trim();\n } catch {\n return null;\n }\n}\n\nexport const infoCommand = defineCommand({\n meta: {\n name: 'info',\n description: 'Print environment and project info',\n },\n run() {\n const cwd = process.cwd();\n\n console.log('\\n \\x1b[1msigx environment info\\x1b[0m\\n');\n console.log(` Platform: ${process.platform} ${process.arch}`);\n console.log(` Node: ${process.version}`);\n\n // Package manager\n const pnpmVer = getVersion('pnpm --version');\n if (pnpmVer) console.log(` pnpm: v${pnpmVer}`);\n else {\n const npmVer = getVersion('npm --version');\n if (npmVer) console.log(` npm: v${npmVer}`);\n }\n\n // Project info\n const pkgPath = join(cwd, 'package.json');\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n console.log(` Project: ${pkg.name || '(unnamed)'} v${pkg.version || '0.0.0'}`);\n\n // Detect sigx packages\n const allDeps: Record<string, string> = {\n ...pkg.dependencies,\n ...pkg.devDependencies,\n };\n const sigxPkgs = Object.entries(allDeps)\n .filter(([name]) => name.startsWith('@sigx/') || name === 'sigx')\n .map(([name, version]) => `${name}@${version}`);\n\n if (sigxPkgs.length > 0) {\n console.log(` Sigx packages:`);\n for (const p of sigxPkgs) {\n console.log(` - ${p}`);\n }\n }\n } catch {\n // ignore parse errors\n }\n } else {\n console.log(' Project: (no package.json found)');\n }\n\n // Config files\n const configs = [\n { file: 'sigx.lynx.config.ts', label: 'Lynx' },\n { file: 'sigx.lynx.config.js', label: 'Lynx' },\n { file: 'lynx.config.ts', label: 'Lynx (rspeedy)' },\n { file: 'lynx.config.js', label: 'Lynx (rspeedy)' },\n { file: 'ssg.config.ts', label: 'SSG' },\n { file: 'vite.config.ts', label: 'Vite' },\n ];\n const detected = configs.filter(c => existsSync(join(cwd, c.file)));\n if (detected.length > 0) {\n console.log(` Config files:`);\n for (const c of detected) {\n console.log(` - ${c.file} (${c.label})`);\n }\n }\n\n // Lynx-specific info\n const isLynx = detected.some(c => c.label.includes('Lynx'));\n if (isLynx) {\n console.log('');\n console.log(' \\x1b[1mLynx Environment\\x1b[0m');\n\n // rspeedy\n const rspeedyVer = getVersion('npx rspeedy --version 2>&1');\n if (rspeedyVer) console.log(` rspeedy: v${rspeedyVer.trim()}`);\n\n // Android SDK\n const androidHome = process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT;\n if (androidHome) {\n console.log(` Android SDK: ${androidHome}`);\n }\n\n // JDK\n const javaVer = getVersion('java -version 2>&1');\n if (javaVer) {\n const match = javaVer.match(/version \"([^\"]+)\"/);\n if (match) console.log(` JDK: ${match[1]}`);\n }\n\n // ADB + devices\n try {\n const adbVer = getVersion('adb version');\n if (adbVer) {\n const verMatch = adbVer.match(/version ([\\d.]+)/);\n console.log(` ADB: ${verMatch ? `v${verMatch[1]}` : 'available'}`);\n\n const devOutput = execSync('adb devices -l', { stdio: 'pipe', encoding: 'utf-8' });\n const devices = devOutput.split('\\n')\n .slice(1)\n .filter(l => l.trim().length > 0)\n .map(l => {\n const modelMatch = l.match(/model:(\\S+)/);\n return modelMatch ? modelMatch[1].replace(/_/g, ' ') : l.split(/\\s+/)[0];\n });\n\n if (devices.length > 0) {\n console.log(` Devices: ${devices.join(', ')}`);\n }\n }\n } catch {\n // ADB not available\n }\n\n // Xcode (macOS only)\n if (process.platform === 'darwin') {\n const xcodeVer = getVersion('xcodebuild -version 2>/dev/null');\n if (xcodeVer) console.log(` Xcode: ${xcodeVer.split('\\n')[0]}`);\n\n const podVer = getVersion('pod --version');\n if (podVer) console.log(` CocoaPods: v${podVer}`);\n }\n }\n\n console.log('');\n },\n});\n","import type { Logger } from '../plugin.js';\n\nexport function createLogger(prefix = 'sigx'): Logger {\n return {\n log: (msg: string) => console.log(`[${prefix}] ${msg}`),\n warn: (msg: string) => console.warn(`[${prefix}] WARN: ${msg}`),\n error: (msg: string) => console.error(`[${prefix}] ERROR: ${msg}`),\n };\n}\n","#!/usr/bin/env node\n\n/**\n * sigx CLI — unified command-line tool for SignalX projects.\n *\n * Core commands (always available):\n * sigx create — scaffold a new project\n * sigx info — print environment info\n *\n * Plugin commands (auto-discovered from installed packages):\n * sigx dev — start dev server\n * sigx build — production build\n * sigx preview — preview build (SSG)\n * sigx prebuild — generate native project files (Lynx)\n * sigx run:android / run:ios — build + launch on device (Lynx)\n */\n\nimport { defineCommand, runMain } from 'citty';\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { discoverPlugins } from './discover.js';\nimport { infoCommand } from './commands/info.js';\nimport { createLogger } from './utils/logger.js';\nimport type { PluginCommand, SigxPlugin } from './plugin.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));\nconst logger = createLogger();\n\nfunction wrapPluginCommand(cmd: PluginCommand, plugins: SigxPlugin[]) {\n return defineCommand({\n meta: { description: cmd.description },\n args: cmd.args as any,\n async run({ args }) {\n // `plugins` lets a shell-hosting command (e.g. lynx dev) merge\n // peer plugins' TUI contributions via runShell({ plugins }).\n await cmd.run({ cwd: process.cwd(), args, logger, plugins });\n },\n });\n}\n\nasync function main() {\n const cwd = process.cwd();\n const plugins = await discoverPlugins(cwd);\n\n // Build subcommand map: core + plugin commands\n const subCommands: Record<string, ReturnType<typeof defineCommand>> = {\n info: infoCommand,\n };\n\n // Lazy-load create command only when needed (it pulls in @sigx/terminal)\n subCommands.create = defineCommand({\n meta: { name: 'create', description: 'Scaffold a new SignalX project' },\n async run() {\n const { runCreate } = await import('./commands/create.js');\n await runCreate();\n },\n });\n\n // Register plugin commands\n for (const plugin of plugins) {\n for (const [name, cmd] of Object.entries(plugin.commands)) {\n if (subCommands[name]) {\n // Command conflict — last plugin wins, but warn\n logger.warn(`Plugin \"${plugin.name}\" overrides command \"${name}\"`);\n }\n subCommands[name] = wrapPluginCommand(cmd, plugins);\n }\n }\n\n const mainCommand = defineCommand({\n meta: {\n name: 'sigx',\n version: pkg.version,\n description: 'SignalX CLI',\n },\n subCommands,\n });\n\n await runMain(mainCommand);\n}\n\nmain().catch((err) => {\n logger.error(err.message || String(err));\n process.exit(1);\n});\n"],"mappings":"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"}
@@ -1 +1 @@
1
- export declare function runCreate(): void;
1
+ export declare function runCreate(): Promise<void>;
@@ -1,11 +1,11 @@
1
- import { Button, Input, ProgressBar, Select, component, defineApp, signal } from "@sigx/terminal";
2
- import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
3
- import { dirname, join, resolve } from "path";
4
- import { fileURLToPath } from "url";
5
- import { jsx, jsxs } from "@sigx/terminal/jsx-runtime";
6
- //#region src/commands/create.tsx
7
- /** @jsxImportSource @sigx/terminal */
8
- var __dirname = dirname(fileURLToPath(import.meta.url));
1
+ import { lynxStylingOptions, projectTypeOptions, scaffoldProject, webStylingOptions } from "./scaffold.js";
2
+ import { cancel, intro, isCancel, note, outro, select, spinner, text } from "@sigx/terminal";
3
+ //#region src/commands/create.ts
4
+ /**
5
+ * `sigx create` interactive scaffolder on the @sigx/terminal prompt kit,
6
+ * with a flag-driven headless mode for CI (`--type`, `--styling`, `--yes`,
7
+ * or any non-TTY stdio).
8
+ */
9
9
  var rawArgs = process.argv.slice(2);
10
10
  function getFlag(name) {
11
11
  const eq = rawArgs.find((a) => a.startsWith(`--${name}=`));
@@ -21,408 +21,6 @@ var argType = getFlag("type");
21
21
  var argStyling = getFlag("styling");
22
22
  var flagYes = hasFlag("yes", "y");
23
23
  var isNonInteractive = !process.stdout.isTTY || !process.stdin.isTTY || flagYes || Boolean(argType && argProjectName);
24
- var projectTypeOptions = [
25
- {
26
- value: "basic",
27
- label: "Basic SPA",
28
- description: "Simple single-page application (web)"
29
- },
30
- {
31
- value: "ssr",
32
- label: "SSR",
33
- description: "Server-side rendering with Express (web)"
34
- },
35
- {
36
- value: "ssg",
37
- label: "SSG",
38
- description: "Static site with file-based routing & MDX (web)"
39
- },
40
- {
41
- value: "lynx",
42
- label: "Lynx",
43
- description: "Native mobile app with Lynx runtime"
44
- }
45
- ];
46
- var webStylingOptions = [
47
- {
48
- value: "none",
49
- label: "None",
50
- description: "No CSS framework"
51
- },
52
- {
53
- value: "tailwind",
54
- label: "Tailwind CSS",
55
- description: "Utility-first CSS framework"
56
- },
57
- {
58
- value: "daisyui",
59
- label: "Tailwind + Daisy UI",
60
- description: "Tailwind with component library"
61
- }
62
- ];
63
- var lynxStylingOptions = [
64
- {
65
- value: "none",
66
- label: "None",
67
- description: "No CSS framework"
68
- },
69
- {
70
- value: "tailwind",
71
- label: "Tailwind CSS",
72
- description: "Tailwind with Lynx preset"
73
- },
74
- {
75
- value: "daisyui",
76
- label: "Tailwind + Daisy UI",
77
- description: "Lynx + @sigx/lynx-daisyui components"
78
- }
79
- ];
80
- var TEXT_EXTS = new Set([
81
- "ts",
82
- "tsx",
83
- "js",
84
- "jsx",
85
- "mjs",
86
- "cjs",
87
- "mts",
88
- "cts",
89
- "json",
90
- "json5",
91
- "jsonc",
92
- "md",
93
- "mdx",
94
- "txt",
95
- "html",
96
- "htm",
97
- "css",
98
- "scss",
99
- "sass",
100
- "less",
101
- "yml",
102
- "yaml",
103
- "toml",
104
- "xml",
105
- "svg",
106
- "gitignore",
107
- "gitattributes",
108
- "editorconfig",
109
- "npmrc",
110
- "nvmrc",
111
- "env"
112
- ]);
113
- function isTextExtension(filename) {
114
- const ext = filename.startsWith(".") ? filename.slice(1).toLowerCase() : filename.split(".").pop()?.toLowerCase() ?? "";
115
- return TEXT_EXTS.has(ext);
116
- }
117
- function copyDirectory(src, dest, projectName) {
118
- if (!existsSync(dest)) mkdirSync(dest, { recursive: true });
119
- const entries = readdirSync(src);
120
- for (const entry of entries) {
121
- const srcPath = join(src, entry);
122
- const destPath = join(dest, entry === "gitignore" ? ".gitignore" : entry);
123
- if (statSync(srcPath).isDirectory()) copyDirectory(srcPath, destPath, projectName);
124
- else if (isTextExtension(entry)) {
125
- let content = readFileSync(srcPath, "utf-8");
126
- content = content.replace(/\{\{projectName\}\}/g, projectName);
127
- writeFileSync(destPath, content);
128
- } else writeFileSync(destPath, readFileSync(srcPath));
129
- }
130
- }
131
- /**
132
- * Detect if the target directory is inside a pnpm workspace that includes @sigx packages.
133
- * If so, rewrite @sigx/* dependency versions to workspace:* in package.json.
134
- */
135
- function patchWorkspaceDeps(targetDir) {
136
- const pkgPath = join(targetDir, "package.json");
137
- if (!existsSync(pkgPath)) return;
138
- let dir = dirname(targetDir);
139
- let isWorkspace = false;
140
- for (let i = 0; i < 10; i++) {
141
- if (existsSync(join(dir, "pnpm-workspace.yaml"))) {
142
- isWorkspace = true;
143
- break;
144
- }
145
- const parent = dirname(dir);
146
- if (parent === dir) break;
147
- dir = parent;
148
- }
149
- if (!isWorkspace) return;
150
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
151
- for (const section of ["dependencies", "devDependencies"]) {
152
- if (!pkg[section]) continue;
153
- for (const dep of Object.keys(pkg[section])) if (dep.startsWith("@sigx/")) pkg[section][dep] = "workspace:*";
154
- }
155
- writeFileSync(pkgPath, JSON.stringify(pkg, null, 4) + "\n");
156
- }
157
- function scaffoldProject(opts) {
158
- const targetDir = resolve(process.cwd(), opts.projectName);
159
- let templateName;
160
- if (opts.projectType === "lynx") templateName = opts.styling !== "none" ? `lynx-${opts.styling}` : "lynx";
161
- else templateName = opts.styling !== "none" ? `${opts.projectType}-${opts.styling}` : opts.projectType;
162
- const templateDir = resolve(__dirname, "..", "templates", templateName);
163
- if (existsSync(targetDir)) return {
164
- ok: false,
165
- error: `Directory "${opts.projectName}" already exists!`
166
- };
167
- if (!existsSync(templateDir)) return {
168
- ok: false,
169
- error: `Template "${templateName}" not found at ${templateDir}`
170
- };
171
- copyDirectory(templateDir, targetDir, opts.projectName);
172
- patchWorkspaceDeps(targetDir);
173
- return { ok: true };
174
- }
175
- var projectTypeLabel = (t) => {
176
- switch (t) {
177
- case "basic": return "Basic SPA";
178
- case "ssr": return "SSR";
179
- case "ssg": return "SSG";
180
- case "lynx": return "Lynx (Native)";
181
- }
182
- };
183
- var stylingLabel = (s) => {
184
- switch (s) {
185
- case "none": return "None";
186
- case "tailwind": return "Tailwind CSS";
187
- case "daisyui": return "Tailwind + Daisy UI";
188
- }
189
- };
190
- var StepName = component(({ props }) => () => /* @__PURE__ */ jsxs("box", { children: [
191
- /* @__PURE__ */ jsx("text", {
192
- color: "gray",
193
- children: " Step 1 of 3"
194
- }),
195
- /* @__PURE__ */ jsx("br", {}),
196
- /* @__PURE__ */ jsx("br", {}),
197
- /* @__PURE__ */ jsx("text", {
198
- color: "white",
199
- children: " What is your project called?"
200
- }),
201
- /* @__PURE__ */ jsx("br", {}),
202
- /* @__PURE__ */ jsx("br", {}),
203
- /* @__PURE__ */ jsx(Input, {
204
- model: () => props.state.projectName,
205
- label: " Project Name ",
206
- placeholder: "my-sigx-app",
207
- autofocus: true,
208
- onSubmit: props.onSubmit
209
- }),
210
- /* @__PURE__ */ jsx("br", {}),
211
- /* @__PURE__ */ jsx("text", {
212
- color: "gray",
213
- children: " Press Enter to continue"
214
- })
215
- ] }), { name: "StepName" });
216
- var StepType = component(({ props }) => () => /* @__PURE__ */ jsxs("box", { children: [
217
- /* @__PURE__ */ jsx("text", {
218
- color: "gray",
219
- children: " Step 2 of 3"
220
- }),
221
- /* @__PURE__ */ jsx("br", {}),
222
- /* @__PURE__ */ jsx("br", {}),
223
- /* @__PURE__ */ jsx("text", {
224
- color: "white",
225
- children: " What type of project?"
226
- }),
227
- /* @__PURE__ */ jsx("br", {}),
228
- /* @__PURE__ */ jsx("br", {}),
229
- /* @__PURE__ */ jsx(Select, {
230
- model: () => props.state.projectType,
231
- label: " Project Type ",
232
- options: projectTypeOptions,
233
- showDescription: true,
234
- autofocus: true,
235
- onSubmit: props.onSubmit
236
- }),
237
- /* @__PURE__ */ jsx("br", {}),
238
- /* @__PURE__ */ jsx("text", {
239
- color: "gray",
240
- children: " ↑/↓ navigate · Enter select"
241
- })
242
- ] }), { name: "StepType" });
243
- var StepStyling = component(({ props }) => () => /* @__PURE__ */ jsxs("box", { children: [
244
- /* @__PURE__ */ jsx("text", {
245
- color: "gray",
246
- children: " Step 3 of 3"
247
- }),
248
- /* @__PURE__ */ jsx("br", {}),
249
- /* @__PURE__ */ jsx("br", {}),
250
- /* @__PURE__ */ jsx("text", {
251
- color: "white",
252
- children: " Choose a styling approach:"
253
- }),
254
- /* @__PURE__ */ jsx("br", {}),
255
- /* @__PURE__ */ jsx("br", {}),
256
- /* @__PURE__ */ jsx(Select, {
257
- model: () => props.state.styling,
258
- label: " Styling ",
259
- options: props.state.projectType === "lynx" ? lynxStylingOptions : webStylingOptions,
260
- showDescription: true,
261
- autofocus: true,
262
- onSubmit: props.onSubmit
263
- }),
264
- /* @__PURE__ */ jsx("br", {}),
265
- /* @__PURE__ */ jsx("text", {
266
- color: "gray",
267
- children: " ↑/↓ navigate · Enter select"
268
- })
269
- ] }), { name: "StepStyling" });
270
- var StepCreating = component(({ props }) => () => /* @__PURE__ */ jsxs("box", { children: [
271
- /* @__PURE__ */ jsx("br", {}),
272
- /* @__PURE__ */ jsx("text", {
273
- color: "yellow",
274
- children: " Creating project..."
275
- }),
276
- /* @__PURE__ */ jsx("br", {}),
277
- /* @__PURE__ */ jsx(ProgressBar, {
278
- value: props.state.progress,
279
- max: 100,
280
- width: 40,
281
- color: "green"
282
- })
283
- ] }), { name: "StepCreating" });
284
- var StepDone = component(({ props }) => () => /* @__PURE__ */ jsxs("box", { children: [
285
- /* @__PURE__ */ jsx("br", {}),
286
- /* @__PURE__ */ jsxs("text", {
287
- color: "green",
288
- children: [
289
- " ✓ Project \"",
290
- props.state.projectName,
291
- "\" created!"
292
- ]
293
- }),
294
- /* @__PURE__ */ jsx("br", {}),
295
- /* @__PURE__ */ jsx("br", {}),
296
- /* @__PURE__ */ jsxs("text", {
297
- color: "gray",
298
- children: [" Type: ", projectTypeLabel(props.state.projectType)]
299
- }),
300
- /* @__PURE__ */ jsx("br", {}),
301
- /* @__PURE__ */ jsxs("text", {
302
- color: "gray",
303
- children: [" Styling: ", stylingLabel(props.state.styling)]
304
- }),
305
- /* @__PURE__ */ jsx("br", {}),
306
- /* @__PURE__ */ jsx("br", {}),
307
- /* @__PURE__ */ jsx("text", {
308
- color: "white",
309
- children: " Next steps:"
310
- }),
311
- /* @__PURE__ */ jsx("br", {}),
312
- /* @__PURE__ */ jsx("br", {}),
313
- /* @__PURE__ */ jsxs("text", {
314
- color: "cyan",
315
- children: [" cd ", props.state.projectName]
316
- }),
317
- /* @__PURE__ */ jsx("br", {}),
318
- /* @__PURE__ */ jsx("text", {
319
- color: "cyan",
320
- children: " pnpm install"
321
- }),
322
- /* @__PURE__ */ jsx("br", {}),
323
- /* @__PURE__ */ jsxs("text", {
324
- color: "cyan",
325
- children: [" ", props.state.projectType === "lynx" ? "sigx dev" : "pnpm dev"]
326
- }),
327
- /* @__PURE__ */ jsx("br", {}),
328
- /* @__PURE__ */ jsx("br", {}),
329
- /* @__PURE__ */ jsx(Button, {
330
- label: "Exit",
331
- onClick: props.onExit
332
- })
333
- ] }), { name: "StepDone" });
334
- var ErrorScreen = component(({ props }) => () => /* @__PURE__ */ jsxs("box", {
335
- border: "double",
336
- borderColor: "red",
337
- label: " Error ",
338
- children: [
339
- /* @__PURE__ */ jsxs("text", {
340
- color: "red",
341
- children: [" ", props.message]
342
- }),
343
- /* @__PURE__ */ jsx("br", {}),
344
- /* @__PURE__ */ jsx("br", {}),
345
- /* @__PURE__ */ jsx(Button, {
346
- label: "Exit",
347
- onClick: props.onExit
348
- })
349
- ]
350
- }), { name: "ErrorScreen" });
351
- var CreateSigx = component(() => {
352
- const state = signal({
353
- step: "name",
354
- projectName: argProjectName || "my-sigx-app",
355
- projectType: "basic",
356
- styling: "none",
357
- progress: 0,
358
- error: ""
359
- });
360
- const createProject = () => {
361
- if (state.step !== "styling") return;
362
- state.step = "creating";
363
- state.progress = 30;
364
- const result = scaffoldProject({
365
- projectName: state.projectName,
366
- projectType: state.projectType,
367
- styling: state.styling
368
- });
369
- if (!result.ok) {
370
- state.error = result.error;
371
- return;
372
- }
373
- state.progress = 100;
374
- state.step = "done";
375
- };
376
- const handleNameSubmit = () => {
377
- if (state.step !== "name") return;
378
- if (state.projectName.trim()) state.step = "type";
379
- };
380
- const handleTypeSubmit = () => {
381
- if (state.step !== "type") return;
382
- state.step = "styling";
383
- };
384
- const handleStylingSubmit = () => {
385
- if (state.step !== "styling") return;
386
- createProject();
387
- };
388
- const handleExit = () => {
389
- process.exit(0);
390
- };
391
- return () => {
392
- if (state.error) return /* @__PURE__ */ jsx(ErrorScreen, {
393
- message: state.error,
394
- onExit: handleExit
395
- });
396
- return /* @__PURE__ */ jsxs("box", { children: [
397
- /* @__PURE__ */ jsx("text", {
398
- color: "cyan",
399
- children: " ⚡ Create SignalX App"
400
- }),
401
- /* @__PURE__ */ jsx("br", {}),
402
- (() => {
403
- switch (state.step) {
404
- case "name": return /* @__PURE__ */ jsx(StepName, {
405
- state,
406
- onSubmit: handleNameSubmit
407
- });
408
- case "type": return /* @__PURE__ */ jsx(StepType, {
409
- state,
410
- onSubmit: handleTypeSubmit
411
- });
412
- case "styling": return /* @__PURE__ */ jsx(StepStyling, {
413
- state,
414
- onSubmit: handleStylingSubmit
415
- });
416
- case "creating": return /* @__PURE__ */ jsx(StepCreating, { state });
417
- case "done": return /* @__PURE__ */ jsx(StepDone, {
418
- state,
419
- onExit: handleExit
420
- });
421
- }
422
- })()
423
- ] });
424
- };
425
- });
426
24
  function runHeadless() {
427
25
  const validTypes = [
428
26
  "basic",
@@ -465,10 +63,47 @@ function runHeadless() {
465
63
  console.log(` ${projectType === "lynx" ? "sigx dev" : "pnpm dev"}\n`);
466
64
  return 0;
467
65
  }
468
- function runCreate() {
66
+ function bail() {
67
+ cancel("Cancelled — nothing was created.");
68
+ process.exit(130);
69
+ }
70
+ async function runCreate() {
469
71
  if (isNonInteractive) process.exit(runHeadless());
470
- defineApp(/* @__PURE__ */ jsx(CreateSigx, {})).mount({ clearConsole: true });
471
- setInterval(() => {}, 1e4);
72
+ intro("⚡ Create SignalX App");
73
+ const projectName = await text({
74
+ message: "Project name",
75
+ placeholder: "my-sigx-app",
76
+ initialValue: argProjectName || "my-sigx-app",
77
+ validate: (v) => v.trim() ? void 0 : "Project name is required"
78
+ });
79
+ if (isCancel(projectName)) bail();
80
+ const projectType = await select({
81
+ message: "Project type",
82
+ initialValue: argType ?? "basic",
83
+ options: projectTypeOptions
84
+ });
85
+ if (isCancel(projectType)) bail();
86
+ const styling = await select({
87
+ message: "Styling",
88
+ initialValue: argStyling ?? "none",
89
+ options: projectType === "lynx" ? lynxStylingOptions : webStylingOptions
90
+ });
91
+ if (isCancel(styling)) bail();
92
+ const s = spinner();
93
+ s.start(`Scaffolding ${projectName}`);
94
+ const result = scaffoldProject({
95
+ projectName,
96
+ projectType,
97
+ styling
98
+ });
99
+ if (!result.ok) {
100
+ s.stop(result.error, "error");
101
+ process.exit(1);
102
+ }
103
+ s.stop(`Created ${projectName} (${projectType}${styling !== "none" ? ` + ${styling}` : ""})`);
104
+ note(`cd ${projectName}\npnpm install\n${projectType === "lynx" ? "sigx dev" : "pnpm dev"}`, "Next steps");
105
+ outro("Happy hacking!");
106
+ process.exit(0);
472
107
  }
473
108
  //#endregion
474
109
  export { runCreate };