@vobs/cli 0.3.0 → 1.2.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/LICENSE +1 -1
- package/README.md +116 -0
- package/bin/vobs.js +12 -0
- package/dist/banner.cjs +37 -0
- package/dist/banner.cjs.map +1 -0
- package/dist/banner.d.cts +18 -0
- package/dist/banner.d.ts +18 -0
- package/dist/banner.js +33 -0
- package/dist/banner.js.map +1 -0
- package/dist/cli.cjs +416 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +5 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +389 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/add.cjs +113 -0
- package/dist/commands/add.cjs.map +1 -0
- package/dist/commands/add.d.cts +5 -0
- package/dist/commands/add.d.ts +5 -0
- package/dist/commands/add.js +88 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/build.cjs +55 -0
- package/dist/commands/build.cjs.map +1 -0
- package/dist/commands/build.d.cts +5 -0
- package/dist/commands/build.d.ts +5 -0
- package/dist/commands/build.js +49 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/dev.cjs +57 -0
- package/dist/commands/dev.cjs.map +1 -0
- package/dist/commands/dev.d.cts +5 -0
- package/dist/commands/dev.d.ts +5 -0
- package/dist/commands/dev.js +51 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/generate.cjs +153 -0
- package/dist/commands/generate.cjs.map +1 -0
- package/dist/commands/generate.d.cts +5 -0
- package/dist/commands/generate.d.ts +5 -0
- package/dist/commands/generate.js +126 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/index.cjs +331 -0
- package/dist/commands/index.cjs.map +1 -0
- package/dist/commands/index.d.cts +6 -0
- package/dist/commands/index.d.ts +6 -0
- package/dist/commands/index.js +300 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.cjs +187 -0
- package/dist/commands/init.cjs.map +1 -0
- package/dist/commands/init.d.cts +5 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.js +160 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/index.cjs +561 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +14 -0
- package/dist/index.d.ts +14 -18
- package/dist/index.js +489 -486
- package/dist/index.js.map +1 -0
- package/dist/repl.cjs +436 -0
- package/dist/repl.cjs.map +1 -0
- package/dist/repl.d.cts +8 -0
- package/dist/repl.d.ts +8 -0
- package/dist/repl.js +409 -0
- package/dist/repl.js.map +1 -0
- package/dist/start.cjs +543 -0
- package/dist/start.cjs.map +1 -0
- package/dist/start.d.cts +4 -0
- package/dist/start.d.ts +4 -0
- package/dist/start.js +516 -0
- package/dist/start.js.map +1 -0
- package/dist/templates/basic/.gitignore.hbs +4 -0
- package/dist/templates/basic/index.html.hbs +12 -0
- package/dist/templates/basic/package.json.hbs +25 -0
- package/dist/templates/basic/src/App.tsx.hbs +8 -0
- package/dist/templates/basic/src/app.css.hbs +32 -0
- package/dist/templates/basic/src/main.ts.hbs +20 -0
- package/dist/templates/basic/tsconfig.json.hbs +19 -0
- package/dist/templates/basic/vite.config.ts.hbs +10 -0
- package/dist/templates/component.hbs +7 -0
- package/dist/templates/page.hbs +7 -0
- package/dist/types.cjs +4 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +71 -0
- package/dist/types.d.ts +71 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/file.cjs +79 -0
- package/dist/utils/file.cjs.map +1 -0
- package/dist/utils/file.d.cts +14 -0
- package/dist/utils/file.d.ts +14 -0
- package/dist/utils/file.js +67 -0
- package/dist/utils/file.js.map +1 -0
- package/dist/utils/logger.cjs +45 -0
- package/dist/utils/logger.cjs.map +1 -0
- package/dist/utils/logger.d.cts +14 -0
- package/dist/utils/logger.d.ts +14 -0
- package/dist/utils/logger.js +34 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/pm.cjs +38 -0
- package/dist/utils/pm.cjs.map +1 -0
- package/dist/utils/pm.d.cts +9 -0
- package/dist/utils/pm.d.ts +9 -0
- package/dist/utils/pm.js +34 -0
- package/dist/utils/pm.js.map +1 -0
- package/package.json +34 -40
- package/src/banner.ts +41 -0
- package/src/cli.ts +108 -0
- package/src/commands/add.ts +37 -0
- package/src/commands/build.ts +17 -0
- package/src/commands/dev.ts +19 -0
- package/src/commands/generate.ts +87 -0
- package/src/commands/index.ts +5 -0
- package/src/commands/init.ts +84 -0
- package/src/index.ts +13 -0
- package/src/repl.ts +140 -0
- package/src/start.ts +20 -0
- package/src/templates/basic/.gitignore.hbs +4 -0
- package/src/templates/basic/index.html.hbs +12 -0
- package/src/templates/basic/package.json.hbs +25 -0
- package/src/templates/basic/src/App.tsx.hbs +8 -0
- package/src/templates/basic/src/app.css.hbs +32 -0
- package/src/templates/basic/src/main.ts.hbs +20 -0
- package/src/templates/basic/tsconfig.json.hbs +19 -0
- package/src/templates/basic/vite.config.ts.hbs +10 -0
- package/src/templates/component.hbs +7 -0
- package/src/templates/page.hbs +7 -0
- package/src/types.ts +78 -0
- package/src/utils/file.ts +77 -0
- package/src/utils/logger.ts +27 -0
- package/src/utils/pm.ts +42 -0
- package/dist/ai-server.d.ts +0 -11
- package/dist/ai-server.js +0 -212
- package/dist/context.d.ts +0 -12
- package/dist/context.js +0 -301
- package/dist/diagnose.d.ts +0 -12
- package/dist/diagnose.js +0 -122
- package/dist/enterprise-template.d.ts +0 -83
- package/dist/enterprise-template.js +0 -638
- package/dist/inspect.d.ts +0 -11
- package/dist/inspect.js +0 -576
- package/dist/logo/cli-logo.d.ts +0 -5
- package/dist/logo/cli-logo.js +0 -16
- package/dist/scaffold.d.ts +0 -23
- 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,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,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
|
+
}
|
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 }
|
package/src/utils/pm.ts
ADDED
|
@@ -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
|
+
}
|
package/dist/ai-server.d.ts
DELETED
|
@@ -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>;
|