cli4ai 0.8.3 → 0.9.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/package.json CHANGED
@@ -1,18 +1,17 @@
1
1
  {
2
2
  "name": "cli4ai",
3
- "version": "0.8.3",
3
+ "version": "0.9.1",
4
4
  "description": "The package manager for AI CLI tools - cli4ai.com",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "cli4ai": "./src/bin.ts"
8
8
  },
9
9
  "scripts": {
10
- "dev": "bun run src/bin.ts",
11
- "build": "bun build src/bin.ts --outdir dist --target node",
10
+ "dev": "npx tsx src/bin.ts",
12
11
  "typecheck": "tsc --noEmit",
13
- "test": "bun test",
14
- "test:watch": "bun test --watch",
15
- "test:coverage": "bun test --coverage"
12
+ "test": "vitest run",
13
+ "test:watch": "vitest",
14
+ "test:coverage": "vitest run --coverage"
16
15
  },
17
16
  "dependencies": {
18
17
  "commander": "^14.0.0",
@@ -20,10 +19,11 @@
20
19
  "semver": "^7.6.0"
21
20
  },
22
21
  "devDependencies": {
23
- "@types/bun": "^1.3.4",
24
22
  "@types/node": "^22.0.0",
25
23
  "@types/semver": "^7.5.0",
26
- "typescript": "^5.7.0"
24
+ "tsx": "^4.0.0",
25
+ "typescript": "^5.7.0",
26
+ "vitest": "^2.0.0"
27
27
  },
28
28
  "author": "cliforai",
29
29
  "license": "MIT",
package/src/cli.ts CHANGED
@@ -57,7 +57,7 @@ export function createProgram(): Command {
57
57
  .command('init [name]')
58
58
  .description('Create a new cli4ai tool project')
59
59
  .option('-t, --template <template>', 'Template to use (basic, api, browser)', 'basic')
60
- .option('-r, --runtime <runtime>', 'Runtime (bun, node)', 'bun')
60
+ .option('-r, --runtime <runtime>', 'Runtime (node, bun)', 'node')
61
61
  .option('-y, --yes', 'Skip prompts, use defaults')
62
62
  .action(withErrorHandling(initCommand));
63
63
 
@@ -237,15 +237,13 @@ async function installNpmDependencies(pkgPath: string, dependencies: Record<stri
237
237
 
238
238
  log(`Installing npm dependencies: ${deps.join(', ')}`);
239
239
 
240
- const proc = Bun.spawn(['bun', 'add', ...deps], {
240
+ const result = spawnSync('npm', ['install', ...deps], {
241
241
  cwd: pkgPath,
242
- stdout: 'inherit',
243
- stderr: 'inherit'
242
+ stdio: 'inherit'
244
243
  });
245
244
 
246
- const exitCode = await proc.exited;
247
- if (exitCode !== 0) {
248
- throw new Error(`Failed to install dependencies (exit code: ${exitCode})`);
245
+ if (result.status !== 0) {
246
+ throw new Error(`Failed to install dependencies (exit code: ${result.status})`);
249
247
  }
250
248
  }
251
249
 
@@ -66,7 +66,7 @@ function outputPackageInfo(manifest: Manifest, path: string, installed: boolean)
66
66
  description: manifest.description,
67
67
  author: manifest.author,
68
68
  license: manifest.license,
69
- runtime: manifest.runtime || 'bun',
69
+ runtime: manifest.runtime || 'node',
70
70
  entry: manifest.entry,
71
71
  path,
72
72
  installed,
@@ -2,7 +2,7 @@
2
2
  * Integration tests for init command
3
3
  */
4
4
 
5
- import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
5
+ import { describe, test, expect, beforeEach, afterEach } from 'vitest';
6
6
  import { mkdtempSync, rmSync, existsSync, readFileSync } from 'fs';
7
7
  import { join } from 'path';
8
8
  import { tmpdir } from 'os';
@@ -88,11 +88,11 @@ describe('init command', () => {
88
88
  expect(manifest.version).toBe('1.0.0');
89
89
  });
90
90
 
91
- test('manifest has default runtime bun', async () => {
91
+ test('manifest has default runtime node', async () => {
92
92
  await initCommand('tool', {});
93
93
 
94
94
  const manifest = JSON.parse(readFileSync(join(tempDir, 'tool', 'cli4ai.json'), 'utf-8'));
95
- expect(manifest.runtime).toBe('bun');
95
+ expect(manifest.runtime).toBe('node');
96
96
  });
97
97
 
98
98
  test('manifest has entry pointing to run.ts', async () => {
@@ -110,7 +110,7 @@ describe('init command', () => {
110
110
  expect(manifest.commands?.hello?.description).toBe('Say hello');
111
111
  });
112
112
 
113
- test('manifest includes dependencies for bun runtime', async () => {
113
+ test('manifest includes dependencies', async () => {
114
114
  await initCommand('tool', {});
115
115
 
116
116
  const manifest = JSON.parse(readFileSync(join(tempDir, 'tool', 'cli4ai.json'), 'utf-8'));
@@ -121,11 +121,11 @@ describe('init command', () => {
121
121
  });
122
122
 
123
123
  describe('entry file content', () => {
124
- test('run.ts starts with shebang', async () => {
124
+ test('run.ts starts with tsx shebang', async () => {
125
125
  await initCommand('tool', {});
126
126
 
127
127
  const content = readFileSync(join(tempDir, 'tool', 'run.ts'), 'utf-8');
128
- expect(content.startsWith('#!/usr/bin/env bun')).toBe(true);
128
+ expect(content.startsWith('#!/usr/bin/env npx tsx')).toBe(true);
129
129
  });
130
130
 
131
131
  test('run.ts has hello command', async () => {
@@ -139,25 +139,73 @@ describe('init command', () => {
139
139
  await initCommand('tool', {});
140
140
 
141
141
  const content = readFileSync(join(tempDir, 'tool', 'run.ts'), 'utf-8');
142
- expect(content).toContain("@cli4ai/lib/cli.ts");
142
+ expect(content).toContain("@cli4ai/lib");
143
143
  expect(content).toContain('output(');
144
144
  });
145
145
  });
146
146
 
147
147
  describe('runtime option', () => {
148
148
  test('respects runtime option', async () => {
149
- await initCommand('tool', { runtime: 'node' });
149
+ await initCommand('tool', { runtime: 'bun' });
150
150
 
151
151
  const manifest = JSON.parse(readFileSync(join(tempDir, 'tool', 'cli4ai.json'), 'utf-8'));
152
- expect(manifest.runtime).toBe('node');
152
+ expect(manifest.runtime).toBe('bun');
153
153
  });
154
154
 
155
- test('node runtime uses run.mjs entry', async () => {
155
+ test('both runtimes use run.ts with tsx', async () => {
156
+ // Node runtime (default)
156
157
  await initCommand('tool', { runtime: 'node' });
158
+ let manifest = JSON.parse(readFileSync(join(tempDir, 'tool', 'cli4ai.json'), 'utf-8'));
159
+ expect(manifest.entry).toBe('run.ts');
160
+ expect(existsSync(join(tempDir, 'tool', 'run.ts'))).toBe(true);
157
161
 
158
- const manifest = JSON.parse(readFileSync(join(tempDir, 'tool', 'cli4ai.json'), 'utf-8'));
159
- expect(manifest.entry).toBe('run.mjs');
160
- expect(existsSync(join(tempDir, 'tool', 'run.mjs'))).toBe(true);
162
+ // Bun runtime
163
+ rmSync(join(tempDir, 'tool'), { recursive: true, force: true });
164
+ await initCommand('tool', { runtime: 'bun' });
165
+ manifest = JSON.parse(readFileSync(join(tempDir, 'tool', 'cli4ai.json'), 'utf-8'));
166
+ expect(manifest.entry).toBe('run.ts');
167
+ expect(existsSync(join(tempDir, 'tool', 'run.ts'))).toBe(true);
168
+ });
169
+ });
170
+
171
+ describe('test scaffolding', () => {
172
+ test('creates vitest.config.ts', async () => {
173
+ await initCommand('tool', {});
174
+ expect(existsSync(join(tempDir, 'tool', 'vitest.config.ts'))).toBe(true);
175
+ });
176
+
177
+ test('creates run.test.ts with vitest imports', async () => {
178
+ await initCommand('tool', {});
179
+ const content = readFileSync(join(tempDir, 'tool', 'run.test.ts'), 'utf-8');
180
+ expect(content).toContain("from 'vitest'");
181
+ expect(content).toContain('describe(');
182
+ });
183
+
184
+ test('package.json has vitest dev dependency', async () => {
185
+ await initCommand('tool', {});
186
+ const pkg = JSON.parse(readFileSync(join(tempDir, 'tool', 'package.json'), 'utf-8'));
187
+ expect(pkg.devDependencies).toBeDefined();
188
+ expect(pkg.devDependencies['vitest']).toBeDefined();
189
+ expect(pkg.devDependencies['tsx']).toBeDefined();
190
+ });
191
+
192
+ test('package.json has test script', async () => {
193
+ await initCommand('tool', {});
194
+ const pkg = JSON.parse(readFileSync(join(tempDir, 'tool', 'package.json'), 'utf-8'));
195
+ expect(pkg.scripts?.test).toBe('vitest run');
196
+ });
197
+ });
198
+
199
+ describe('tsconfig.json', () => {
200
+ test('creates tsconfig.json', async () => {
201
+ await initCommand('tool', {});
202
+ expect(existsSync(join(tempDir, 'tool', 'tsconfig.json'))).toBe(true);
203
+ });
204
+
205
+ test('tsconfig uses NodeNext module resolution', async () => {
206
+ await initCommand('tool', {});
207
+ const tsconfig = JSON.parse(readFileSync(join(tempDir, 'tool', 'tsconfig.json'), 'utf-8'));
208
+ expect(tsconfig.compilerOptions.moduleResolution).toBe('NodeNext');
161
209
  });
162
210
  });
163
211
  });