@sigx/cli 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -11,10 +11,57 @@ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).expor
11
11
  * Auto-discover sigx CLI plugins from the project's dependencies.
12
12
  *
13
13
  * Scans the project's package.json for dependencies that declare
14
- * a `"sigx-cli": { "plugin": "./path/to/plugin.js" }` field.
14
+ * a `"sigx-cli"` manifest field:
15
+ *
16
+ * "sigx-cli": {
17
+ * "plugin": "./dist/plugin.js",
18
+ * "requires": ">=0.3.0" // optional: CLI version range
19
+ * }
20
+ *
15
21
  * Loads each plugin and calls `detect(cwd)` to check if it applies.
22
+ * When `requires` is declared and the RUNNING CLI binary doesn't satisfy
23
+ * it, the plugin still loads (best effort) but a prominent warning tells
24
+ * the user exactly how to update — peerDependencies can't cover this,
25
+ * because the binary that dispatches commands isn't necessarily the
26
+ * install the peer range was resolved against.
27
+ */
28
+ function parseVersion(v) {
29
+ const m = v.trim().match(/^(\d+)\.(\d+)\.(\d+)/);
30
+ if (!m) return null;
31
+ return {
32
+ major: Number(m[1]),
33
+ minor: Number(m[2]),
34
+ patch: Number(m[3])
35
+ };
36
+ }
37
+ function compare(a, b) {
38
+ return a.major - b.major || a.minor - b.minor || a.patch - b.patch;
39
+ }
40
+ /**
41
+ * Minimal semver-range check covering the forms plugins actually use:
42
+ * `^x.y.z` (0.x caret = same minor, like npm), `>=x.y.z`, and exact.
43
+ * Unknown/garbled ranges return true — never block on a malformed field.
16
44
  */
17
- async function discoverPlugins(cwd) {
45
+ function satisfiesCliRange(version, range) {
46
+ const v = parseVersion(version);
47
+ if (!v) return true;
48
+ const r = range.trim();
49
+ if (r.startsWith("^")) {
50
+ const want = parseVersion(r.slice(1));
51
+ if (!want) return true;
52
+ if (compare(v, want) < 0) return false;
53
+ if (v.major !== want.major) return false;
54
+ if (want.major === 0 && v.minor !== want.minor) return false;
55
+ return true;
56
+ }
57
+ if (r.startsWith(">=")) {
58
+ const want = parseVersion(r.slice(2));
59
+ return !want || compare(v, want) >= 0;
60
+ }
61
+ const want = parseVersion(r);
62
+ return !want || compare(v, want) === 0;
63
+ }
64
+ async function discoverPlugins(cwd, opts = {}) {
18
65
  const pkgPath = join(cwd, "package.json");
19
66
  if (!existsSync(pkgPath)) return [];
20
67
  let pkg;
@@ -35,6 +82,7 @@ async function discoverPlugins(cwd) {
35
82
  if (!pluginField?.plugin) continue;
36
83
  const pluginPath = join(cwd, "node_modules", depName, pluginField.plugin);
37
84
  if (!existsSync(pluginPath)) continue;
85
+ if (pluginField.requires && opts.cliVersion && !satisfiesCliRange(opts.cliVersion, pluginField.requires)) opts.logger?.warn(`Plugin "${depName}" requires sigx CLI ${pluginField.requires} but this project runs ${opts.cliVersion} — some commands may misbehave. Update with: pnpm up @sigx/cli --latest`);
38
86
  const mod = await import(pathToFileURL(pluginPath).href);
39
87
  const plugin = mod.default || mod;
40
88
  if (typeof plugin.detect === "function" && plugin.detect(cwd)) plugins.push(plugin);
@@ -191,13 +239,17 @@ function wrapPluginCommand(cmd, plugins) {
191
239
  cwd: process.cwd(),
192
240
  args,
193
241
  logger,
194
- plugins
242
+ plugins,
243
+ cliVersion: pkg.version
195
244
  });
196
245
  }
197
246
  });
198
247
  }
199
248
  async function main() {
200
- const plugins = await discoverPlugins(process.cwd());
249
+ const plugins = await discoverPlugins(process.cwd(), {
250
+ cliVersion: pkg.version,
251
+ logger
252
+ });
201
253
  const subCommands = { info: infoCommand };
202
254
  subCommands.create = defineCommand({
203
255
  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, 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
+ {"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\"` manifest field:\n *\n * \"sigx-cli\": {\n * \"plugin\": \"./dist/plugin.js\",\n * \"requires\": \">=0.3.0\" // optional: CLI version range\n * }\n *\n * Loads each plugin and calls `detect(cwd)` to check if it applies.\n * When `requires` is declared and the RUNNING CLI binary doesn't satisfy\n * it, the plugin still loads (best effort) but a prominent warning tells\n * the user exactly how to update — peerDependencies can't cover this,\n * because the binary that dispatches commands isn't necessarily the\n * install the peer range was resolved against.\n */\n\nimport { readFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { SigxPlugin, Logger } from './plugin.js';\n\ninterface DepPackageJson {\n 'sigx-cli'?: {\n plugin: string;\n /** Semver range of @sigx/cli this plugin needs: '^x.y.z', '>=x.y.z', or exact. */\n requires?: string;\n };\n}\n\ninterface ParsedVersion { major: number; minor: number; patch: number }\n\nfunction parseVersion(v: string): ParsedVersion | null {\n const m = v.trim().match(/^(\\d+)\\.(\\d+)\\.(\\d+)/);\n if (!m) return null;\n return { major: Number(m[1]), minor: Number(m[2]), patch: Number(m[3]) };\n}\n\nfunction compare(a: ParsedVersion, b: ParsedVersion): number {\n return a.major - b.major || a.minor - b.minor || a.patch - b.patch;\n}\n\n/**\n * Minimal semver-range check covering the forms plugins actually use:\n * `^x.y.z` (0.x caret = same minor, like npm), `>=x.y.z`, and exact.\n * Unknown/garbled ranges return true — never block on a malformed field.\n */\nexport function satisfiesCliRange(version: string, range: string): boolean {\n const v = parseVersion(version);\n if (!v) return true;\n const r = range.trim();\n if (r.startsWith('^')) {\n const want = parseVersion(r.slice(1));\n if (!want) return true;\n if (compare(v, want) < 0) return false;\n // Caret: same major; on 0.x the minor acts as the major.\n if (v.major !== want.major) return false;\n if (want.major === 0 && v.minor !== want.minor) return false;\n return true;\n }\n if (r.startsWith('>=')) {\n const want = parseVersion(r.slice(2));\n return !want || compare(v, want) >= 0;\n }\n const want = parseVersion(r);\n return !want || compare(v, want) === 0;\n}\n\nexport interface DiscoverOptions {\n /** The running CLI's version — enables `requires` compatibility checks. */\n cliVersion?: string;\n /** Where compatibility warnings go. */\n logger?: Logger;\n}\n\nexport async function discoverPlugins(cwd: string, opts: DiscoverOptions = {}): 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 if (\n pluginField.requires &&\n opts.cliVersion &&\n !satisfiesCliRange(opts.cliVersion, pluginField.requires)\n ) {\n opts.logger?.warn(\n `Plugin \"${depName}\" requires sigx CLI ${pluginField.requires} ` +\n `but this project runs ${opts.cliVersion} — some commands may misbehave. ` +\n `Update with: pnpm up @sigx/cli --latest`,\n );\n }\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, cliVersion: pkg.version });\n },\n });\n}\n\nasync function main() {\n const cwd = process.cwd();\n const plugins = await discoverPlugins(cwd, { cliVersion: pkg.version, logger });\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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"}
package/dist/plugin.d.ts CHANGED
@@ -9,6 +9,8 @@ export interface CommandContext {
9
9
  logger: Logger;
10
10
  /** All plugins discovered for this project (for runShell({ plugins })). */
11
11
  plugins?: SigxPlugin[];
12
+ /** The running CLI binary's version — for plugin feature detection. */
13
+ cliVersion?: string;
12
14
  }
13
15
  export interface Logger {
14
16
  log: (msg: string) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":[],"sources":["../src/plugin.ts"],"sourcesContent":["/**\n * Plugin interface for the sigx CLI.\n *\n * Packages that want to extend the CLI declare a `\"sigx-cli\"` field\n * in their package.json pointing to a module that default-exports\n * a SigxPlugin created with `definePlugin()`.\n *\n * This module is deliberately dependency-free: plugins import it via\n * `@sigx/cli/plugin` without pulling the TUI stack. The TUI types below\n * (`ShellTab`, `ShellHandle`, …) are structural contracts consumed by\n * `runShell` from `@sigx/cli/shell`.\n */\n\nexport interface ArgDef {\n type: 'string' | 'boolean';\n description?: string;\n default?: string | boolean;\n}\n\nexport interface CommandContext {\n cwd: string;\n args: Record<string, unknown>;\n logger: Logger;\n /**\n * All plugins discovered for this project — lets a shell-hosting command\n * (e.g. lynx `dev`) merge peer plugins' TUI contributions via\n * `runShell({ plugins })` from `@sigx/cli/shell`.\n */\n plugins?: SigxPlugin[];\n}\n\nexport interface Logger {\n log: (msg: string) => void;\n warn: (msg: string) => void;\n error: (msg: string) => void;\n}\n\nexport interface PluginCommand {\n description: string;\n args?: Record<string, ArgDef>;\n run: (ctx: CommandContext) => Promise<void>;\n}\n\n/**\n * Opaque renderable returned by a tab's `render()`. Author it with JSX\n * (`@jsxImportSource @sigx/terminal`); typed as `unknown` so this module\n * stays dependency-free — the shell passes it straight to the renderer.\n */\nexport type ShellNode = unknown;\n\n/** One entry in the shell's status line. `tone` is a theme token. */\nexport interface StatusItem {\n label: string;\n value: string;\n /** e.g. 'success' | 'warn' | 'danger' | 'dim' | 'accent' */\n tone?: string;\n}\n\n/** A tab in the shell's tab strip. */\nexport interface ShellTab {\n id: string;\n label: string;\n render: () => ShellNode;\n}\n\n/** A `/command` offered in the shell input's intellisense. */\nexport interface SlashCommand {\n /** Includes the leading slash, e.g. '/reload'. */\n name: string;\n description: string;\n run: (shell: ShellHandle) => void | Promise<void>;\n}\n\n/** A single-key shortcut, active only while the command input is empty. */\nexport interface Shortcut {\n key: string;\n label: string;\n run: (shell: ShellHandle) => void | Promise<void>;\n}\n\n/** Structural subset of @sigx/terminal's LogStore — keeps this module dep-free. */\nexport interface ShellLogStore {\n push: (chunk: string) => void;\n}\n\n/**\n * Handle to a running shell, passed to slash commands, shortcuts, and the\n * host's onReady. In non-TTY environments (`isInteractive: false`) the shell\n * never mounts: `say` writes plain lines, the store streams through, and the\n * navigation methods are no-ops — callers keep a single code path.\n */\nexport interface ShellHandle {\n isInteractive: boolean;\n /** Print a permanent transcript line above the live region. */\n say: (text?: string) => void;\n /** The main streaming log store (feeds the host's Logs tab). */\n store: ShellLogStore;\n setStatus: (items: StatusItem[]) => void;\n switchTab: (id: string) => void;\n pushView: (id: string) => void;\n popView: () => void;\n /**\n * Register teardown to run BEFORE the process exits — on `exit()`,\n * Ctrl+C/q, and external SIGTERM/SIGHUP/SIGINT. Subscribers run after\n * the host's onExit, most-recently-registered first. Returns an\n * unsubscribe. Use this for servers, watchers, locks.\n */\n onExit: (cb: () => void | Promise<void>) => () => void;\n exit: (code?: number) => void;\n}\n\n/**\n * TUI contributions a plugin offers to whichever plugin hosts the shell:\n * tabs, slash commands, shortcuts, and status-line items.\n */\nexport interface TuiContribution {\n tabs?: ShellTab[];\n commands?: SlashCommand[];\n shortcuts?: Shortcut[];\n status?: () => StatusItem[];\n /**\n * Lifecycle: called once when the hosting shell comes up (interactive or\n * plain). Start servers/watchers here; an optionally returned function is\n * registered as teardown (equivalent to `shell.onExit(fn)`).\n */\n setup?: (shell: ShellHandle) => void | (() => void | Promise<void>) | Promise<void | (() => void | Promise<void>)>;\n}\n\nexport interface SigxPlugin {\n /** Unique plugin name (e.g. 'ssg', 'lynx') */\n name: string;\n /** Return true if this plugin handles the current project */\n detect: (cwd: string) => boolean;\n /** Commands this plugin provides */\n commands: Record<string, PluginCommand>;\n /** Optional TUI contributions merged into any plugin-hosted shell. */\n tui?: TuiContribution;\n}\n\n/**\n * Define a sigx CLI plugin. Identity function for type safety.\n */\nexport function definePlugin(plugin: SigxPlugin): SigxPlugin {\n return plugin;\n}\n"],"mappings":";;;;AA8IA,SAAgB,aAAa,QAAgC;CACzD,OAAO"}
1
+ {"version":3,"file":"plugin.js","names":[],"sources":["../src/plugin.ts"],"sourcesContent":["/**\n * Plugin interface for the sigx CLI.\n *\n * Packages that want to extend the CLI declare a `\"sigx-cli\"` field\n * in their package.json pointing to a module that default-exports\n * a SigxPlugin created with `definePlugin()`.\n *\n * This module is deliberately dependency-free: plugins import it via\n * `@sigx/cli/plugin` without pulling the TUI stack. The TUI types below\n * (`ShellTab`, `ShellHandle`, …) are structural contracts consumed by\n * `runShell` from `@sigx/cli/shell`.\n */\n\nexport interface ArgDef {\n type: 'string' | 'boolean';\n description?: string;\n default?: string | boolean;\n}\n\nexport interface CommandContext {\n cwd: string;\n args: Record<string, unknown>;\n logger: Logger;\n /**\n * All plugins discovered for this project — lets a shell-hosting command\n * (e.g. lynx `dev`) merge peer plugins' TUI contributions via\n * `runShell({ plugins })` from `@sigx/cli/shell`.\n */\n plugins?: SigxPlugin[];\n /** The running CLI binary's version — for plugin feature detection. */\n cliVersion?: string;\n}\n\nexport interface Logger {\n log: (msg: string) => void;\n warn: (msg: string) => void;\n error: (msg: string) => void;\n}\n\nexport interface PluginCommand {\n description: string;\n args?: Record<string, ArgDef>;\n run: (ctx: CommandContext) => Promise<void>;\n}\n\n/**\n * Opaque renderable returned by a tab's `render()`. Author it with JSX\n * (`@jsxImportSource @sigx/terminal`); typed as `unknown` so this module\n * stays dependency-free — the shell passes it straight to the renderer.\n */\nexport type ShellNode = unknown;\n\n/** One entry in the shell's status line. `tone` is a theme token. */\nexport interface StatusItem {\n label: string;\n value: string;\n /** e.g. 'success' | 'warn' | 'danger' | 'dim' | 'accent' */\n tone?: string;\n}\n\n/** A tab in the shell's tab strip. */\nexport interface ShellTab {\n id: string;\n label: string;\n render: () => ShellNode;\n}\n\n/** A `/command` offered in the shell input's intellisense. */\nexport interface SlashCommand {\n /** Includes the leading slash, e.g. '/reload'. */\n name: string;\n description: string;\n run: (shell: ShellHandle) => void | Promise<void>;\n}\n\n/** A single-key shortcut, active only while the command input is empty. */\nexport interface Shortcut {\n key: string;\n label: string;\n run: (shell: ShellHandle) => void | Promise<void>;\n}\n\n/** Structural subset of @sigx/terminal's LogStore — keeps this module dep-free. */\nexport interface ShellLogStore {\n push: (chunk: string) => void;\n}\n\n/**\n * Handle to a running shell, passed to slash commands, shortcuts, and the\n * host's onReady. In non-TTY environments (`isInteractive: false`) the shell\n * never mounts: `say` writes plain lines, the store streams through, and the\n * navigation methods are no-ops — callers keep a single code path.\n */\nexport interface ShellHandle {\n isInteractive: boolean;\n /** Print a permanent transcript line above the live region. */\n say: (text?: string) => void;\n /** The main streaming log store (feeds the host's Logs tab). */\n store: ShellLogStore;\n setStatus: (items: StatusItem[]) => void;\n switchTab: (id: string) => void;\n pushView: (id: string) => void;\n popView: () => void;\n /**\n * Register teardown to run BEFORE the process exits — on `exit()`,\n * Ctrl+C/q, and external SIGTERM/SIGHUP/SIGINT. Subscribers run after\n * the host's onExit, most-recently-registered first. Returns an\n * unsubscribe. Use this for servers, watchers, locks.\n */\n onExit: (cb: () => void | Promise<void>) => () => void;\n exit: (code?: number) => void;\n}\n\n/**\n * TUI contributions a plugin offers to whichever plugin hosts the shell:\n * tabs, slash commands, shortcuts, and status-line items.\n */\nexport interface TuiContribution {\n tabs?: ShellTab[];\n commands?: SlashCommand[];\n shortcuts?: Shortcut[];\n status?: () => StatusItem[];\n /**\n * Lifecycle: called once when the hosting shell comes up (interactive or\n * plain). Start servers/watchers here; an optionally returned function is\n * registered as teardown (equivalent to `shell.onExit(fn)`).\n */\n setup?: (shell: ShellHandle) => void | (() => void | Promise<void>) | Promise<void | (() => void | Promise<void>)>;\n}\n\nexport interface SigxPlugin {\n /** Unique plugin name (e.g. 'ssg', 'lynx') */\n name: string;\n /** Return true if this plugin handles the current project */\n detect: (cwd: string) => boolean;\n /** Commands this plugin provides */\n commands: Record<string, PluginCommand>;\n /** Optional TUI contributions merged into any plugin-hosted shell. */\n tui?: TuiContribution;\n}\n\n/**\n * Define a sigx CLI plugin. Identity function for type safety.\n */\nexport function definePlugin(plugin: SigxPlugin): SigxPlugin {\n return plugin;\n}\n"],"mappings":";;;;AAgJA,SAAgB,aAAa,QAAgC;CACzD,OAAO"}
@@ -24,7 +24,7 @@
24
24
  "@sigx/lynx-websocket": "latest"
25
25
  },
26
26
  "devDependencies": {
27
- "@sigx/cli": "latest",
27
+ "@sigx/cli": "^0.3.0",
28
28
  "@sigx/lynx-cli": "latest",
29
29
  "@sigx/lynx-plugin": "latest",
30
30
  "@sigx/lynx-dev-client": "latest",
@@ -25,7 +25,7 @@
25
25
  "@sigx/lynx-daisyui": "latest"
26
26
  },
27
27
  "devDependencies": {
28
- "@sigx/cli": "latest",
28
+ "@sigx/cli": "^0.3.0",
29
29
  "@sigx/lynx-cli": "latest",
30
30
  "@sigx/lynx-plugin": "latest",
31
31
  "@sigx/lynx-dev-client": "latest",
@@ -24,7 +24,7 @@
24
24
  "@sigx/lynx-websocket": "latest"
25
25
  },
26
26
  "devDependencies": {
27
- "@sigx/cli": "latest",
27
+ "@sigx/cli": "^0.3.0",
28
28
  "@sigx/lynx-cli": "latest",
29
29
  "@sigx/lynx-plugin": "latest",
30
30
  "@sigx/lynx-dev-client": "latest",
@@ -15,7 +15,7 @@
15
15
  "@sigx/server-renderer": "latest"
16
16
  },
17
17
  "devDependencies": {
18
- "@sigx/cli": "latest",
18
+ "@sigx/cli": "^0.3.0",
19
19
  "@sigx/vite": "latest",
20
20
  "vite": "^8.0.3",
21
21
  "typescript": "^5.9.3"
@@ -16,7 +16,7 @@
16
16
  "@sigx/daisyui": "latest"
17
17
  },
18
18
  "devDependencies": {
19
- "@sigx/cli": "latest",
19
+ "@sigx/cli": "^0.3.0",
20
20
  "@sigx/vite": "latest",
21
21
  "vite": "^8.0.3",
22
22
  "typescript": "^5.9.3",
@@ -15,7 +15,7 @@
15
15
  "@sigx/server-renderer": "latest"
16
16
  },
17
17
  "devDependencies": {
18
- "@sigx/cli": "latest",
18
+ "@sigx/cli": "^0.3.0",
19
19
  "@sigx/vite": "latest",
20
20
  "vite": "^8.0.3",
21
21
  "typescript": "^5.9.3",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sigx/cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Unified CLI for SignalX — create, dev, build, and run projects",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -24,7 +24,7 @@
24
24
  "@sigx/lynx-websocket": "latest"
25
25
  },
26
26
  "devDependencies": {
27
- "@sigx/cli": "latest",
27
+ "@sigx/cli": "^0.3.0",
28
28
  "@sigx/lynx-cli": "latest",
29
29
  "@sigx/lynx-plugin": "latest",
30
30
  "@sigx/lynx-dev-client": "latest",
@@ -25,7 +25,7 @@
25
25
  "@sigx/lynx-daisyui": "latest"
26
26
  },
27
27
  "devDependencies": {
28
- "@sigx/cli": "latest",
28
+ "@sigx/cli": "^0.3.0",
29
29
  "@sigx/lynx-cli": "latest",
30
30
  "@sigx/lynx-plugin": "latest",
31
31
  "@sigx/lynx-dev-client": "latest",
@@ -24,7 +24,7 @@
24
24
  "@sigx/lynx-websocket": "latest"
25
25
  },
26
26
  "devDependencies": {
27
- "@sigx/cli": "latest",
27
+ "@sigx/cli": "^0.3.0",
28
28
  "@sigx/lynx-cli": "latest",
29
29
  "@sigx/lynx-plugin": "latest",
30
30
  "@sigx/lynx-dev-client": "latest",
@@ -15,7 +15,7 @@
15
15
  "@sigx/server-renderer": "latest"
16
16
  },
17
17
  "devDependencies": {
18
- "@sigx/cli": "latest",
18
+ "@sigx/cli": "^0.3.0",
19
19
  "@sigx/vite": "latest",
20
20
  "vite": "^8.0.3",
21
21
  "typescript": "^5.9.3"
@@ -16,7 +16,7 @@
16
16
  "@sigx/daisyui": "latest"
17
17
  },
18
18
  "devDependencies": {
19
- "@sigx/cli": "latest",
19
+ "@sigx/cli": "^0.3.0",
20
20
  "@sigx/vite": "latest",
21
21
  "vite": "^8.0.3",
22
22
  "typescript": "^5.9.3",
@@ -15,7 +15,7 @@
15
15
  "@sigx/server-renderer": "latest"
16
16
  },
17
17
  "devDependencies": {
18
- "@sigx/cli": "latest",
18
+ "@sigx/cli": "^0.3.0",
19
19
  "@sigx/vite": "latest",
20
20
  "vite": "^8.0.3",
21
21
  "typescript": "^5.9.3",