@vobs/cli 0.3.0 → 1.2.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 (143) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +116 -0
  3. package/bin/vobs.js +12 -0
  4. package/dist/banner.cjs +37 -0
  5. package/dist/banner.cjs.map +1 -0
  6. package/dist/banner.d.cts +18 -0
  7. package/dist/banner.d.ts +18 -0
  8. package/dist/banner.js +33 -0
  9. package/dist/banner.js.map +1 -0
  10. package/dist/cli.cjs +416 -0
  11. package/dist/cli.cjs.map +1 -0
  12. package/dist/cli.d.cts +5 -0
  13. package/dist/cli.d.ts +5 -0
  14. package/dist/cli.js +389 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/commands/add.cjs +113 -0
  17. package/dist/commands/add.cjs.map +1 -0
  18. package/dist/commands/add.d.cts +5 -0
  19. package/dist/commands/add.d.ts +5 -0
  20. package/dist/commands/add.js +88 -0
  21. package/dist/commands/add.js.map +1 -0
  22. package/dist/commands/build.cjs +55 -0
  23. package/dist/commands/build.cjs.map +1 -0
  24. package/dist/commands/build.d.cts +5 -0
  25. package/dist/commands/build.d.ts +5 -0
  26. package/dist/commands/build.js +49 -0
  27. package/dist/commands/build.js.map +1 -0
  28. package/dist/commands/dev.cjs +57 -0
  29. package/dist/commands/dev.cjs.map +1 -0
  30. package/dist/commands/dev.d.cts +5 -0
  31. package/dist/commands/dev.d.ts +5 -0
  32. package/dist/commands/dev.js +51 -0
  33. package/dist/commands/dev.js.map +1 -0
  34. package/dist/commands/generate.cjs +153 -0
  35. package/dist/commands/generate.cjs.map +1 -0
  36. package/dist/commands/generate.d.cts +5 -0
  37. package/dist/commands/generate.d.ts +5 -0
  38. package/dist/commands/generate.js +126 -0
  39. package/dist/commands/generate.js.map +1 -0
  40. package/dist/commands/index.cjs +331 -0
  41. package/dist/commands/index.cjs.map +1 -0
  42. package/dist/commands/index.d.cts +6 -0
  43. package/dist/commands/index.d.ts +6 -0
  44. package/dist/commands/index.js +300 -0
  45. package/dist/commands/index.js.map +1 -0
  46. package/dist/commands/init.cjs +187 -0
  47. package/dist/commands/init.cjs.map +1 -0
  48. package/dist/commands/init.d.cts +5 -0
  49. package/dist/commands/init.d.ts +5 -0
  50. package/dist/commands/init.js +160 -0
  51. package/dist/commands/init.js.map +1 -0
  52. package/dist/index.cjs +561 -0
  53. package/dist/index.cjs.map +1 -0
  54. package/dist/index.d.cts +14 -0
  55. package/dist/index.d.ts +14 -18
  56. package/dist/index.js +489 -486
  57. package/dist/index.js.map +1 -0
  58. package/dist/repl.cjs +436 -0
  59. package/dist/repl.cjs.map +1 -0
  60. package/dist/repl.d.cts +8 -0
  61. package/dist/repl.d.ts +8 -0
  62. package/dist/repl.js +409 -0
  63. package/dist/repl.js.map +1 -0
  64. package/dist/start.cjs +543 -0
  65. package/dist/start.cjs.map +1 -0
  66. package/dist/start.d.cts +4 -0
  67. package/dist/start.d.ts +4 -0
  68. package/dist/start.js +516 -0
  69. package/dist/start.js.map +1 -0
  70. package/dist/templates/basic/.gitignore.hbs +4 -0
  71. package/dist/templates/basic/index.html.hbs +12 -0
  72. package/dist/templates/basic/package.json.hbs +25 -0
  73. package/dist/templates/basic/src/App.tsx.hbs +8 -0
  74. package/dist/templates/basic/src/app.css.hbs +32 -0
  75. package/dist/templates/basic/src/main.ts.hbs +20 -0
  76. package/dist/templates/basic/tsconfig.json.hbs +19 -0
  77. package/dist/templates/basic/vite.config.ts.hbs +10 -0
  78. package/dist/templates/component.hbs +7 -0
  79. package/dist/templates/page.hbs +7 -0
  80. package/dist/types.cjs +4 -0
  81. package/dist/types.cjs.map +1 -0
  82. package/dist/types.d.cts +71 -0
  83. package/dist/types.d.ts +71 -0
  84. package/dist/types.js +3 -0
  85. package/dist/types.js.map +1 -0
  86. package/dist/utils/file.cjs +79 -0
  87. package/dist/utils/file.cjs.map +1 -0
  88. package/dist/utils/file.d.cts +14 -0
  89. package/dist/utils/file.d.ts +14 -0
  90. package/dist/utils/file.js +67 -0
  91. package/dist/utils/file.js.map +1 -0
  92. package/dist/utils/logger.cjs +45 -0
  93. package/dist/utils/logger.cjs.map +1 -0
  94. package/dist/utils/logger.d.cts +14 -0
  95. package/dist/utils/logger.d.ts +14 -0
  96. package/dist/utils/logger.js +34 -0
  97. package/dist/utils/logger.js.map +1 -0
  98. package/dist/utils/pm.cjs +38 -0
  99. package/dist/utils/pm.cjs.map +1 -0
  100. package/dist/utils/pm.d.cts +9 -0
  101. package/dist/utils/pm.d.ts +9 -0
  102. package/dist/utils/pm.js +34 -0
  103. package/dist/utils/pm.js.map +1 -0
  104. package/package.json +30 -40
  105. package/src/banner.ts +41 -0
  106. package/src/cli.ts +108 -0
  107. package/src/commands/add.ts +37 -0
  108. package/src/commands/build.ts +17 -0
  109. package/src/commands/dev.ts +19 -0
  110. package/src/commands/generate.ts +87 -0
  111. package/src/commands/index.ts +5 -0
  112. package/src/commands/init.ts +84 -0
  113. package/src/index.ts +13 -0
  114. package/src/repl.ts +140 -0
  115. package/src/start.ts +20 -0
  116. package/src/templates/basic/.gitignore.hbs +4 -0
  117. package/src/templates/basic/index.html.hbs +12 -0
  118. package/src/templates/basic/package.json.hbs +25 -0
  119. package/src/templates/basic/src/App.tsx.hbs +8 -0
  120. package/src/templates/basic/src/app.css.hbs +32 -0
  121. package/src/templates/basic/src/main.ts.hbs +20 -0
  122. package/src/templates/basic/tsconfig.json.hbs +19 -0
  123. package/src/templates/basic/vite.config.ts.hbs +10 -0
  124. package/src/templates/component.hbs +7 -0
  125. package/src/templates/page.hbs +7 -0
  126. package/src/types.ts +78 -0
  127. package/src/utils/file.ts +77 -0
  128. package/src/utils/logger.ts +27 -0
  129. package/src/utils/pm.ts +42 -0
  130. package/dist/ai-server.d.ts +0 -11
  131. package/dist/ai-server.js +0 -212
  132. package/dist/context.d.ts +0 -12
  133. package/dist/context.js +0 -301
  134. package/dist/diagnose.d.ts +0 -12
  135. package/dist/diagnose.js +0 -122
  136. package/dist/enterprise-template.d.ts +0 -83
  137. package/dist/enterprise-template.js +0 -638
  138. package/dist/inspect.d.ts +0 -11
  139. package/dist/inspect.js +0 -576
  140. package/dist/logo/cli-logo.d.ts +0 -5
  141. package/dist/logo/cli-logo.js +0 -16
  142. package/dist/scaffold.d.ts +0 -23
  143. package/dist/scaffold.js +0 -448
package/src/repl.ts ADDED
@@ -0,0 +1,140 @@
1
+ import { createInterface } from 'node:readline/promises'
2
+ import { stdin, stdout } from 'node:process'
3
+ import pc from 'picocolors'
4
+ import { showLogo, showBanner } from './banner.js'
5
+ import { initCommand } from './commands/init.js'
6
+ import { generateCommand } from './commands/generate.js'
7
+ import { addCommand } from './commands/add.js'
8
+
9
+ /**
10
+ * Interactive REPL shell. Runs `vobs` with no arguments and stays in CLI
11
+ * mode accepting repeated commands until the user types `exit` / `quit`
12
+ * or presses Ctrl+C / Ctrl+D.
13
+ */
14
+ export async function startRepl(): Promise<void> {
15
+ showLogo()
16
+
17
+ const rl = createInterface({
18
+ input: stdin,
19
+ output: stdout,
20
+ prompt: ` ${pc.magenta('❯')} `,
21
+ terminal: true
22
+ })
23
+
24
+ rl.prompt()
25
+
26
+ for await (const rawLine of rl) {
27
+ const line = rawLine.trim()
28
+
29
+ if (!line) {
30
+ rl.prompt()
31
+ continue
32
+ }
33
+
34
+ // Split into args, honoring simple double quotes
35
+ const args = splitArgs(line)
36
+
37
+ try {
38
+ await dispatch(args)
39
+ } catch (err) {
40
+ console.error(` ${pc.red('✖')} ${err instanceof Error ? err.message : String(err)}`)
41
+ }
42
+
43
+ rl.prompt()
44
+ }
45
+ }
46
+
47
+ type Command = (args: string[]) => Promise<void>
48
+
49
+ /** Internal command table for the REPL */
50
+ const TABLE: Record<string, { cmd: Command; help: string }> = {
51
+ help: {
52
+ cmd: async () => {
53
+ console.log(` ${pc.bold('Available commands:')}`)
54
+ console.log(` ${pc.dim('exit|quit')} Leave the interactive CLI`)
55
+ console.log(` ${pc.dim('clear')} Clear the screen`)
56
+ console.log(` ${pc.dim('logo')} Re-print the banner`)
57
+ console.log(` ${pc.dim('version')} Show version`)
58
+ console.log(` ${pc.dim('init <name>')} Create a new vobs project`)
59
+ console.log(` ${pc.dim('generate <name>')} Generate a component/page`)
60
+ console.log(` ${pc.dim('add <package>')} Add a package`)
61
+ console.log(` ${pc.dim('dev|build')} Use \`vobs dev\` / \`vobs build\` in a terminal`)
62
+ },
63
+ help: ''
64
+ },
65
+ logo: { cmd: async () => showLogo(), help: '' },
66
+ version: { cmd: async () => showBanner(), help: '' },
67
+ clear: {
68
+ cmd: async () => {
69
+ process.stdout.write('\x1Bc')
70
+ },
71
+ help: ''
72
+ },
73
+ exit: { cmd: async () => process.exit(0), help: '' },
74
+ quit: { cmd: async () => process.exit(0), help: '' },
75
+ init: {
76
+ cmd: async (args) => {
77
+ await initCommand({
78
+ name: args[0],
79
+ targetDir: undefined,
80
+ packageManager: undefined,
81
+ typescript: true,
82
+ git: true
83
+ })
84
+ },
85
+ help: ''
86
+ },
87
+ generate: {
88
+ cmd: async (args) => {
89
+ await generateCommand({
90
+ name: args[0],
91
+ type: undefined,
92
+ targetDir: undefined
93
+ })
94
+ },
95
+ help: ''
96
+ },
97
+ g: {
98
+ cmd: async (args) => {
99
+ await generateCommand({
100
+ name: args[0],
101
+ type: undefined,
102
+ targetDir: undefined
103
+ })
104
+ },
105
+ help: ''
106
+ },
107
+ add: {
108
+ cmd: async (args) => {
109
+ await addCommand({ package: args[0], dev: args.includes('-D') || args.includes('--dev') })
110
+ },
111
+ help: ''
112
+ }
113
+ }
114
+
115
+ /** Dispatch a parsed command line to the internal table */
116
+ async function dispatch(args: string[]): Promise<void> {
117
+ const name = args[0]
118
+ const rest = args.slice(1)
119
+
120
+ if (!name) return
121
+
122
+ const entry = TABLE[name]
123
+ if (entry) {
124
+ await entry.cmd(rest)
125
+ return
126
+ }
127
+
128
+ console.log(` ${pc.yellow('⚠')} Unknown command: ${pc.bold(String(name))}. Type ${pc.cyan('help')} to see options.`)
129
+ }
130
+
131
+ /** Minimal tokenizer supporting double-quoted arguments */
132
+ function splitArgs(line: string): string[] {
133
+ const out: string[] = []
134
+ const re = /[^\s"']+|"([^"]*)"|'([^']*)'/g
135
+ let m: RegExpExecArray | null
136
+ while ((m = re.exec(line)) !== null) {
137
+ out.push(m[1] ?? m[2] ?? m[0])
138
+ }
139
+ return out
140
+ }
package/src/start.ts ADDED
@@ -0,0 +1,20 @@
1
+ import { createCLI } from './cli.js'
2
+ import { showBanner } from './banner.js'
3
+ import { startRepl } from './repl.js'
4
+
5
+ /** Entry point: decides between interactive REPL and single-shot commands */
6
+ export async function start(args: string[]): Promise<void> {
7
+ // No args (or explicit `cli`) → launch the interactive REPL
8
+ if (args.length === 0 || args[0] === 'cli') {
9
+ await startRepl()
10
+ return
11
+ }
12
+
13
+ if (args.includes('--version') || args.includes('-V')) {
14
+ showBanner()
15
+ return
16
+ }
17
+
18
+ // Let cac read the full process.argv so single-shot commands dispatch correctly
19
+ createCLI().parse()
20
+ }
@@ -0,0 +1,4 @@
1
+ node_modules
2
+ dist
3
+ *.local
4
+ .DS_Store
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>{{name}}</title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ <script type="module" src="/src/main.ts"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "{{packageName}}",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "private": true,
6
+ "scripts": {
7
+ "dev": "vobs dev",
8
+ "build": "vobs build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "@vobs/dom": "^{{vobsVersion}}",
13
+ "@vobs/ui": "^{{vobsVersion}}",
14
+ "@vobs/router": "^{{vobsVersion}}",
15
+ "@vobs/i18n": "^{{vobsVersion}}",
16
+ "@vobs/notification": "^{{vobsVersion}}"
17
+ },
18
+ "devDependencies": {
19
+ "@vobs/cli": "^{{vobsVersion}}",
20
+ "@vobs/vite-plugin": "^{{vobsVersion}}",
21
+ "@vobs/tailwind": "^{{vobsVersion}}",
22
+ "vite": "^7.0.0",
23
+ "typescript": "^5.6.0"
24
+ }
25
+ }
@@ -0,0 +1,8 @@
1
+ export default function App() {
2
+ return (
3
+ <div>
4
+ <h1>Welcome to {{name}}</h1>
5
+ <p>Built with Vobs Framework</p>
6
+ </div>
7
+ )
8
+ }
@@ -0,0 +1,32 @@
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ body {
8
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
9
+ line-height: 1.6;
10
+ color: #1e2233;
11
+ background: #f8f9fb;
12
+ }
13
+
14
+ #app {
15
+ min-height: 100vh;
16
+ display: flex;
17
+ flex-direction: column;
18
+ align-items: center;
19
+ justify-content: center;
20
+ text-align: center;
21
+ }
22
+
23
+ h1 {
24
+ font-size: 2.5rem;
25
+ margin-bottom: 0.5rem;
26
+ color: #6366f1;
27
+ }
28
+
29
+ p {
30
+ font-size: 1.125rem;
31
+ color: #6b7280;
32
+ }
@@ -0,0 +1,20 @@
1
+ import { createVobs } from '@vobs/dom'
2
+ import { i18nPlugin } from '@vobs/i18n'
3
+ import { notificationPlugin } from '@vobs/notification'
4
+ import { routerPlugin } from '@vobs/router'
5
+ import { createBrowserHistory } from '@vobs/router/history'
6
+ import App from './App.js'
7
+ import './app.css'
8
+
9
+ const app = createVobs({
10
+ render: App,
11
+ plugins: [
12
+ i18nPlugin(),
13
+ notificationPlugin(),
14
+ routerPlugin({
15
+ history: createBrowserHistory('/'),
16
+ }),
17
+ ],
18
+ })
19
+
20
+ app.mount('#app')
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "jsx": "preserve",
7
+ "jsxImportSource": "@vobs/dom",
8
+ "strict": true,
9
+ "noUnusedLocals": true,
10
+ "noUnusedParameters": true,
11
+ "noFallthroughCasesInSwitch": true,
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "sourceMap": true,
15
+ "outDir": "dist",
16
+ "skipLibCheck": true
17
+ },
18
+ "include": ["src"]
19
+ }
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'vite'
2
+ import { vobsPlugin } from '@vobs/vite-plugin'
3
+ import { vobsTailwind } from '@vobs/tailwind'
4
+
5
+ export default defineConfig({
6
+ plugins: [
7
+ vobsPlugin(),
8
+ vobsTailwind()
9
+ ]
10
+ })
@@ -0,0 +1,7 @@
1
+ export default function {{name}}() {
2
+ return (
3
+ <div class="{{kebabName}}">
4
+ <h2>{{name}} Component</h2>
5
+ </div>
6
+ )
7
+ }
@@ -0,0 +1,7 @@
1
+ export default function {{name}}() {
2
+ return (
3
+ <div>
4
+ <h1>{{name}}</h1>
5
+ </div>
6
+ )
7
+ }
package/src/types.ts ADDED
@@ -0,0 +1,78 @@
1
+ export interface InitOptions {
2
+ /** Project name */
3
+ name: string
4
+ /** Target directory */
5
+ targetDir: string
6
+ /** Package manager to use */
7
+ packageManager?: 'pnpm' | 'npm' | 'yarn'
8
+ /** Use TypeScript */
9
+ typescript?: boolean
10
+ /** Initialize git repo */
11
+ git?: boolean
12
+ }
13
+
14
+ export interface GenerateOptions {
15
+ /** Generator type: component | page */
16
+ type: 'component' | 'page'
17
+ /** Name of the generated artifact */
18
+ name: string
19
+ /** Target directory (defaults to src/components/ or src/pages/) */
20
+ targetDir?: string
21
+ }
22
+
23
+ export interface DevOptions {
24
+ /** Vite config file path */
25
+ config?: string
26
+ /** Port to listen on */
27
+ port?: number
28
+ /** Open browser on start */
29
+ open?: boolean
30
+ }
31
+
32
+ export interface BuildOptions {
33
+ /** Vite config file path */
34
+ config?: string
35
+ /** Output directory */
36
+ outDir?: string
37
+ /** Mode (production | development) */
38
+ mode?: string
39
+ }
40
+
41
+ export interface AddOptions {
42
+ /** Package name to add */
43
+ package: string
44
+ /** Save as dev dependency */
45
+ dev?: boolean
46
+ }
47
+
48
+ export interface TemplateContext {
49
+ /** Project name */
50
+ name: string
51
+ /** Package name (kebab-case) */
52
+ packageName: string
53
+ /** Use TypeScript */
54
+ typescript: boolean
55
+ /** @vobs/* version to use */
56
+ vobsVersion: string
57
+ }
58
+
59
+ export interface ComponentTemplateContext {
60
+ /** Component name (PascalCase) */
61
+ name: string
62
+ /** Component name (kebab-case) */
63
+ kebabName: string
64
+ }
65
+
66
+ export interface PageTemplateContext {
67
+ /** Page name (PascalCase) */
68
+ name: string
69
+ /** Route path */
70
+ path: string
71
+ }
72
+
73
+ export type LoggerLevel = 'info' | 'success' | 'warn' | 'error'
74
+
75
+ export interface CLIOptions {
76
+ /** Enable debug output */
77
+ debug?: boolean
78
+ }
@@ -0,0 +1,77 @@
1
+ import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs'
2
+ import { resolve, dirname } from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+ import Handlebars from 'handlebars'
5
+
6
+ const __dirname = dirname(fileURLToPath(import.meta.url))
7
+ const TEMPLATES_ROOT = resolve(__dirname, '..', 'templates')
8
+
9
+ /** Get the absolute path to the templates directory */
10
+ export function getTemplatesDir(): string {
11
+ return TEMPLATES_ROOT
12
+ }
13
+
14
+ /** Read a template file and compile it with Handlebars */
15
+ export function renderTemplate(
16
+ templatePath: string,
17
+ context: Record<string, unknown>
18
+ ): string {
19
+ const source = readFileSync(templatePath, 'utf-8')
20
+ const template = Handlebars.compile(source)
21
+ return template(context)
22
+ }
23
+
24
+ /** Write a file, creating parent directories if needed */
25
+ export function writeFile(dir: string, filename: string, content: string): void {
26
+ const filePath = resolve(dir, filename)
27
+ mkdirSync(dirname(filePath), { recursive: true })
28
+ writeFileSync(filePath, content, 'utf-8')
29
+ }
30
+
31
+ /** Ensure a directory exists */
32
+ export function ensureDir(dir: string): void {
33
+ if (!existsSync(dir)) {
34
+ mkdirSync(dir, { recursive: true })
35
+ }
36
+ }
37
+
38
+ /** Check if a path exists */
39
+ export function pathExists(path: string): boolean {
40
+ return existsSync(path)
41
+ }
42
+
43
+ /** Copy a template directory recursively, rendering each .hbs file */
44
+ async function copyDirRecursive(srcDir: string, destDir: string, context: Record<string, unknown>): Promise<void> {
45
+ const { readdirSync } = await import('node:fs')
46
+ const { join } = await import('node:path')
47
+
48
+ const entries = readdirSync(srcDir, { withFileTypes: true })
49
+
50
+ for (const entry of entries) {
51
+ const srcPath = join(srcDir, entry.name)
52
+ const isHbs = entry.name.endsWith('.hbs')
53
+ const destName = isHbs ? entry.name.slice(0, -4) : entry.name
54
+ const destPath = join(destDir, destName)
55
+
56
+ if (entry.isDirectory()) {
57
+ ensureDir(destPath)
58
+ await copyDirRecursive(srcPath, destPath, context)
59
+ } else if (isHbs) {
60
+ const content = renderTemplate(srcPath, context)
61
+ writeFile(destDir, destName, content)
62
+ } else {
63
+ const content = readFileSync(srcPath, 'utf-8')
64
+ writeFile(destDir, destName, content)
65
+ }
66
+ }
67
+ }
68
+
69
+ /** Scaffold a project from the basic template */
70
+ export async function scaffoldProject(
71
+ targetDir: string,
72
+ context: Record<string, unknown>
73
+ ): Promise<void> {
74
+ const templateDir = resolve(TEMPLATES_ROOT, 'basic')
75
+ ensureDir(targetDir)
76
+ await copyDirRecursive(templateDir, targetDir, context)
77
+ }
@@ -0,0 +1,27 @@
1
+ import pc from 'picocolors'
2
+
3
+ function timestamp(): string {
4
+ return pc.dim(`[${new Date().toLocaleTimeString()}]`)
5
+ }
6
+
7
+ export function info(msg: string): void {
8
+ console.log(`${timestamp()} ${pc.cyan('ℹ')} ${msg}`)
9
+ }
10
+
11
+ export function success(msg: string): void {
12
+ console.log(`${timestamp()} ${pc.green('✔')} ${msg}`)
13
+ }
14
+
15
+ export function warn(msg: string): void {
16
+ console.log(`${timestamp()} ${pc.yellow('⚠')} ${msg}`)
17
+ }
18
+
19
+ export function error(msg: string): void {
20
+ console.error(`${timestamp()} ${pc.red('✖')} ${msg}`)
21
+ }
22
+
23
+ export function step(msg: string): void {
24
+ console.log(`\n ${pc.bold(pc.cyan(msg))}`)
25
+ }
26
+
27
+ export const logger = { info, success, warn, error, step }
@@ -0,0 +1,42 @@
1
+ import { existsSync } from 'node:fs'
2
+ import { resolve } from 'node:path'
3
+ import { execSync } from 'node:child_process'
4
+
5
+ export type PackageManager = 'pnpm' | 'npm' | 'yarn'
6
+
7
+ /** Detect the package manager to use */
8
+ export function detectPackageManager(targetDir?: string): PackageManager {
9
+ // Check if a specific package manager is locked
10
+ if (targetDir) {
11
+ if (existsSync(resolve(targetDir, 'pnpm-lock.yaml'))) return 'pnpm'
12
+ if (existsSync(resolve(targetDir, 'yarn.lock'))) return 'yarn'
13
+ if (existsSync(resolve(targetDir, 'package-lock.json'))) return 'npm'
14
+ }
15
+
16
+ // Check for global preference
17
+ const userAgent = process.env.npm_config_user_agent || ''
18
+ if (userAgent.startsWith('pnpm')) return 'pnpm'
19
+ if (userAgent.startsWith('yarn')) return 'yarn'
20
+ if (userAgent.startsWith('npm')) return 'npm'
21
+
22
+ // Default to pnpm
23
+ return 'pnpm'
24
+ }
25
+
26
+ /** Install dependencies in a directory */
27
+ export function installDependencies(targetDir: string, pm: PackageManager): void {
28
+ const cmd = pm === 'yarn' ? 'yarn' : `${pm} install`
29
+ execSync(cmd, { cwd: targetDir, stdio: 'inherit' })
30
+ }
31
+
32
+ /** Add a package as a dependency */
33
+ export function addPackage(targetDir: string, pm: PackageManager, pkg: string, dev: boolean): void {
34
+ const installFlag = dev ? '-D' : ''
35
+ const cmd = pm === 'npm'
36
+ ? `npm install ${installFlag} ${pkg}`
37
+ : pm === 'yarn'
38
+ ? `yarn add ${installFlag} ${pkg}`
39
+ : `pnpm add ${installFlag} ${pkg}`
40
+
41
+ execSync(cmd, { cwd: targetDir, stdio: 'inherit' })
42
+ }
@@ -1,11 +0,0 @@
1
- /** @license MIT
2
- * Copyright (c) 2026 vobsjs
3
- * @vobs/cli
4
- */
5
- export interface VobsAiServerStreams {
6
- readonly input: AsyncIterable<string>;
7
- readonly stdout: Pick<NodeJS.WriteStream, 'write'>;
8
- readonly stderr?: Pick<NodeJS.WriteStream, 'write'>;
9
- }
10
- /** Serve the read-only vobs AI tools over line-delimited JSON-RPC on stdio. */
11
- export declare function runVobsAiServer(streams: VobsAiServerStreams): Promise<void>;