cli4ai 1.1.5 → 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/README.md +39 -0
- package/dist/bin.d.ts +6 -0
- package/dist/bin.js +105 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +335 -0
- package/dist/commands/add.d.ts +11 -0
- package/dist/commands/add.js +459 -0
- package/dist/commands/browse.d.ts +4 -0
- package/dist/commands/browse.js +379 -0
- package/dist/commands/config.d.ts +10 -0
- package/dist/commands/config.js +121 -0
- package/dist/commands/info.d.ts +9 -0
- package/dist/commands/info.js +122 -0
- package/dist/commands/init.d.ts +10 -0
- package/dist/commands/init.js +458 -0
- package/dist/commands/list.d.ts +10 -0
- package/dist/commands/list.js +76 -0
- package/dist/commands/mcp-config.d.ts +10 -0
- package/dist/commands/mcp-config.js +49 -0
- package/dist/commands/remotes.d.ts +22 -0
- package/dist/commands/remotes.js +196 -0
- package/dist/commands/remove.d.ts +8 -0
- package/dist/commands/remove.js +61 -0
- package/dist/commands/routines.d.ts +29 -0
- package/dist/commands/routines.js +363 -0
- package/dist/commands/run.d.ts +12 -0
- package/dist/commands/run.js +104 -0
- package/dist/commands/scheduler.d.ts +27 -0
- package/dist/commands/scheduler.js +350 -0
- package/dist/commands/search.d.ts +9 -0
- package/dist/commands/search.js +159 -0
- package/dist/commands/secrets.d.ts +28 -0
- package/dist/commands/secrets.js +236 -0
- package/dist/commands/serve.d.ts +13 -0
- package/dist/commands/serve.js +49 -0
- package/dist/commands/start.d.ts +8 -0
- package/dist/commands/start.js +27 -0
- package/dist/commands/update.d.ts +17 -0
- package/dist/commands/update.js +210 -0
- package/dist/core/config.d.ts +91 -0
- package/dist/core/config.js +738 -0
- package/dist/core/execute.d.ts +51 -0
- package/dist/core/execute.js +475 -0
- package/dist/core/link.d.ts +39 -0
- package/dist/core/link.js +214 -0
- package/dist/core/lockfile.d.ts +63 -0
- package/dist/core/lockfile.js +140 -0
- package/dist/core/manifest.d.ts +96 -0
- package/dist/core/manifest.js +224 -0
- package/dist/core/registry.d.ts +74 -0
- package/dist/core/registry.js +116 -0
- package/dist/core/remote-client.d.ts +98 -0
- package/dist/core/remote-client.js +252 -0
- package/dist/core/remotes.d.ts +88 -0
- package/dist/core/remotes.js +206 -0
- package/dist/core/routine-engine.d.ts +124 -0
- package/dist/core/routine-engine.js +699 -0
- package/dist/core/routines.d.ts +36 -0
- package/dist/core/routines.js +132 -0
- package/dist/core/scheduler-daemon.d.ts +10 -0
- package/dist/core/scheduler-daemon.js +77 -0
- package/dist/core/scheduler.d.ts +131 -0
- package/dist/core/scheduler.js +492 -0
- package/dist/core/secrets.d.ts +48 -0
- package/dist/core/secrets.js +384 -0
- package/dist/lib/cli.d.ts +84 -0
- package/dist/lib/cli.js +216 -0
- package/dist/mcp/adapter.d.ts +35 -0
- package/dist/mcp/adapter.js +94 -0
- package/dist/mcp/config-gen.d.ts +31 -0
- package/dist/mcp/config-gen.js +75 -0
- package/dist/mcp/server.d.ts +41 -0
- package/dist/mcp/server.js +296 -0
- package/dist/server/service.d.ts +85 -0
- package/dist/server/service.js +304 -0
- package/package.json +6 -3
- package/src/bin.ts +0 -118
- package/src/cli.ts +0 -409
- package/src/commands/add.ts +0 -562
- package/src/commands/browse.ts +0 -449
- package/src/commands/config.ts +0 -154
- package/src/commands/info.ts +0 -102
- package/src/commands/init.ts +0 -514
- package/src/commands/list.ts +0 -72
- package/src/commands/mcp-config.ts +0 -69
- package/src/commands/remotes.ts +0 -253
- package/src/commands/remove.ts +0 -78
- package/src/commands/routines.ts +0 -427
- package/src/commands/run.ts +0 -127
- package/src/commands/scheduler.ts +0 -438
- package/src/commands/search.ts +0 -148
- package/src/commands/secrets.ts +0 -292
- package/src/commands/serve.ts +0 -66
- package/src/commands/start.ts +0 -40
- package/src/commands/update.ts +0 -252
- package/src/core/config.ts +0 -845
- package/src/core/execute.ts +0 -569
- package/src/core/link.ts +0 -246
- package/src/core/lockfile.ts +0 -187
- package/src/core/manifest.ts +0 -327
- package/src/core/registry.ts +0 -165
- package/src/core/remote-client.ts +0 -419
- package/src/core/remotes.ts +0 -268
- package/src/core/routine-engine.ts +0 -895
- package/src/core/routines.ts +0 -171
- package/src/core/scheduler-daemon.ts +0 -94
- package/src/core/scheduler.ts +0 -606
- package/src/core/secrets.ts +0 -430
- package/src/lib/cli.ts +0 -261
- package/src/mcp/adapter.ts +0 -131
- package/src/mcp/config-gen.ts +0 -106
- package/src/mcp/server.ts +0 -365
- package/src/server/service.ts +0 -434
package/src/commands/init.ts
DELETED
|
@@ -1,514 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* cli4ai init - Create a new tool project
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
6
|
-
import { resolve, basename } from 'path';
|
|
7
|
-
import { output, outputError, log } from '../lib/cli.js';
|
|
8
|
-
import { createManifest, MANIFEST_FILENAME, type Manifest } from '../core/manifest.js';
|
|
9
|
-
|
|
10
|
-
interface InitOptions {
|
|
11
|
-
template?: string;
|
|
12
|
-
runtime?: 'node' | 'bun';
|
|
13
|
-
yes?: boolean;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export async function initCommand(name: string | undefined, options: InitOptions): Promise<void> {
|
|
17
|
-
const projectName = name || basename(process.cwd());
|
|
18
|
-
const targetDir = name ? resolve(process.cwd(), name) : process.cwd();
|
|
19
|
-
const manifestPath = resolve(targetDir, MANIFEST_FILENAME);
|
|
20
|
-
const templateName = options.template || 'basic';
|
|
21
|
-
const runtime = options.runtime || 'node';
|
|
22
|
-
|
|
23
|
-
// Check if cli4ai.json already exists
|
|
24
|
-
if (existsSync(manifestPath)) {
|
|
25
|
-
outputError('INVALID_INPUT', `${MANIFEST_FILENAME} already exists`, {
|
|
26
|
-
path: manifestPath,
|
|
27
|
-
hint: 'Remove it first or use a different directory'
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Create directory if needed
|
|
32
|
-
if (name && !existsSync(targetDir)) {
|
|
33
|
-
mkdirSync(targetDir, { recursive: true });
|
|
34
|
-
log(`Created directory: ${targetDir}`);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const manifest = createManifest(projectName, {
|
|
38
|
-
...getBaseManifest(templateName, runtime),
|
|
39
|
-
...getTemplateManifest(templateName, runtime),
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
// Write manifest
|
|
43
|
-
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
44
|
-
log(`Created ${MANIFEST_FILENAME}`);
|
|
45
|
-
|
|
46
|
-
// Create entry file if it doesn't exist
|
|
47
|
-
const entryPath = resolve(targetDir, manifest.entry);
|
|
48
|
-
if (!existsSync(entryPath)) {
|
|
49
|
-
const template = getTemplate(templateName, manifest);
|
|
50
|
-
writeFileSync(entryPath, template);
|
|
51
|
-
log(`Created ${manifest.entry}`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Create helpful extras (non-destructive)
|
|
55
|
-
const readmePath = resolve(targetDir, 'README.md');
|
|
56
|
-
if (!existsSync(readmePath)) {
|
|
57
|
-
writeFileSync(readmePath, getReadmeTemplate(templateName, manifest));
|
|
58
|
-
log('Created README.md');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const gitignorePath = resolve(targetDir, '.gitignore');
|
|
62
|
-
if (!existsSync(gitignorePath)) {
|
|
63
|
-
writeFileSync(gitignorePath, getGitignoreTemplate());
|
|
64
|
-
log('Created .gitignore');
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const pkgJsonPath = resolve(targetDir, 'package.json');
|
|
68
|
-
if (!existsSync(pkgJsonPath)) {
|
|
69
|
-
const pkgJson = getDevPackageJson(templateName, manifest);
|
|
70
|
-
writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2) + '\n');
|
|
71
|
-
log('Created package.json');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Create test file
|
|
75
|
-
const testPath = resolve(targetDir, manifest.entry.replace(/\.ts$/, '.test.ts'));
|
|
76
|
-
if (!existsSync(testPath)) {
|
|
77
|
-
writeFileSync(testPath, getTestTemplate(templateName, manifest));
|
|
78
|
-
log(`Created ${basename(testPath)}`);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Create tsconfig.json for TypeScript support
|
|
82
|
-
const tsconfigPath = resolve(targetDir, 'tsconfig.json');
|
|
83
|
-
if (!existsSync(tsconfigPath)) {
|
|
84
|
-
writeFileSync(tsconfigPath, getTsconfigTemplate());
|
|
85
|
-
log('Created tsconfig.json');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Create vitest.config.ts
|
|
89
|
-
const vitestConfigPath = resolve(targetDir, 'vitest.config.ts');
|
|
90
|
-
if (!existsSync(vitestConfigPath)) {
|
|
91
|
-
writeFileSync(vitestConfigPath, getVitestConfigTemplate());
|
|
92
|
-
log('Created vitest.config.ts');
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Template-specific extras
|
|
96
|
-
if (templateName === 'api') {
|
|
97
|
-
const envExamplePath = resolve(targetDir, '.env.example');
|
|
98
|
-
if (!existsSync(envExamplePath)) {
|
|
99
|
-
writeFileSync(envExamplePath, 'API_KEY=\n');
|
|
100
|
-
log('Created .env.example');
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const nextSteps = getNextSteps(name, targetDir, templateName, manifest);
|
|
105
|
-
|
|
106
|
-
output({
|
|
107
|
-
created: {
|
|
108
|
-
manifest: manifestPath,
|
|
109
|
-
entry: entryPath,
|
|
110
|
-
test: testPath,
|
|
111
|
-
readme: readmePath,
|
|
112
|
-
gitignore: gitignorePath,
|
|
113
|
-
tsconfig: tsconfigPath,
|
|
114
|
-
vitestConfig: vitestConfigPath,
|
|
115
|
-
packageJson: existsSync(resolve(targetDir, 'package.json')) ? resolve(targetDir, 'package.json') : undefined,
|
|
116
|
-
},
|
|
117
|
-
name: manifest.name,
|
|
118
|
-
version: manifest.version,
|
|
119
|
-
runtime: manifest.runtime,
|
|
120
|
-
hint: nextSteps[0]?.command,
|
|
121
|
-
nextSteps
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function guessAuthor(): string | undefined {
|
|
126
|
-
const candidates = [
|
|
127
|
-
process.env.GIT_AUTHOR_NAME,
|
|
128
|
-
process.env.GIT_COMMITTER_NAME,
|
|
129
|
-
process.env.USER,
|
|
130
|
-
process.env.USERNAME,
|
|
131
|
-
].filter((v): v is string => Boolean(v));
|
|
132
|
-
|
|
133
|
-
return candidates[0];
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function getBaseManifest(templateName: string, runtime: 'node' | 'bun'): Partial<Manifest> {
|
|
137
|
-
const author = guessAuthor();
|
|
138
|
-
const keywords = Array.from(new Set([
|
|
139
|
-
'cli4ai',
|
|
140
|
-
'cli4ai',
|
|
141
|
-
templateName,
|
|
142
|
-
]));
|
|
143
|
-
|
|
144
|
-
return {
|
|
145
|
-
entry: 'run.ts',
|
|
146
|
-
runtime,
|
|
147
|
-
description: `${templateName === 'basic' ? 'CLI' : templateName} tool`,
|
|
148
|
-
author,
|
|
149
|
-
license: 'BUSL-1.1',
|
|
150
|
-
keywords,
|
|
151
|
-
mcp: { enabled: true, transport: 'stdio' },
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function getTemplateManifest(templateName: string, runtime: 'node' | 'bun'): Partial<Manifest> {
|
|
156
|
-
switch (templateName) {
|
|
157
|
-
case 'api':
|
|
158
|
-
return {
|
|
159
|
-
commands: {
|
|
160
|
-
fetch: {
|
|
161
|
-
description: 'Fetch JSON from an API endpoint',
|
|
162
|
-
args: [{ name: 'endpoint', required: true }],
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
env: {
|
|
166
|
-
API_KEY: {
|
|
167
|
-
required: true,
|
|
168
|
-
description: 'API key for the service you are calling (stored via `cli4ai secrets`)',
|
|
169
|
-
}
|
|
170
|
-
},
|
|
171
|
-
dependencies: getDefaultDependencies(templateName),
|
|
172
|
-
};
|
|
173
|
-
case 'browser':
|
|
174
|
-
return {
|
|
175
|
-
commands: {
|
|
176
|
-
screenshot: {
|
|
177
|
-
description: 'Take a screenshot of a webpage',
|
|
178
|
-
args: [
|
|
179
|
-
{ name: 'url', required: true },
|
|
180
|
-
{ name: 'output', required: false },
|
|
181
|
-
],
|
|
182
|
-
options: [
|
|
183
|
-
{ name: 'full-page', type: 'boolean', description: 'Capture full page' },
|
|
184
|
-
]
|
|
185
|
-
}
|
|
186
|
-
},
|
|
187
|
-
dependencies: getDefaultDependencies(templateName),
|
|
188
|
-
};
|
|
189
|
-
default:
|
|
190
|
-
return {
|
|
191
|
-
commands: {
|
|
192
|
-
hello: {
|
|
193
|
-
description: 'Say hello',
|
|
194
|
-
args: [{ name: 'name', required: false }]
|
|
195
|
-
}
|
|
196
|
-
},
|
|
197
|
-
dependencies: getDefaultDependencies(templateName),
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function getDefaultDependencies(templateName: string): Record<string, string> | undefined {
|
|
203
|
-
const deps: Record<string, string> = {
|
|
204
|
-
'@cli4ai/lib': '^1.0.0',
|
|
205
|
-
'commander': '^14.0.0',
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
if (templateName === 'browser') {
|
|
209
|
-
deps['puppeteer'] = '^24.0.0';
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return deps;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function getTemplate(templateName: string, manifest: Manifest): string {
|
|
216
|
-
switch (templateName) {
|
|
217
|
-
case 'api':
|
|
218
|
-
return getApiTemplate(manifest);
|
|
219
|
-
case 'browser':
|
|
220
|
-
return getBrowserTemplate(manifest);
|
|
221
|
-
default:
|
|
222
|
-
return getBasicTemplate(manifest);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
function getBasicTemplate(manifest: Manifest): string {
|
|
227
|
-
return `#!/usr/bin/env npx tsx
|
|
228
|
-
/**
|
|
229
|
-
* ${manifest.name} - ${manifest.description || 'A cli4ai tool'}
|
|
230
|
-
*/
|
|
231
|
-
|
|
232
|
-
import { cli, output } from '@cli4ai/lib';
|
|
233
|
-
|
|
234
|
-
const program = cli('${manifest.name}', '${manifest.version}', '${manifest.description || manifest.name}');
|
|
235
|
-
|
|
236
|
-
program
|
|
237
|
-
.command('hello [name]')
|
|
238
|
-
.description('Say hello')
|
|
239
|
-
.action((name?: string) => {
|
|
240
|
-
const who = name?.trim() || 'world';
|
|
241
|
-
output({ message: \`Hello, \${who}!\` });
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
program.parse();
|
|
245
|
-
`;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function getApiTemplate(manifest: Manifest): string {
|
|
249
|
-
return `#!/usr/bin/env npx tsx
|
|
250
|
-
/**
|
|
251
|
-
* ${manifest.name} - ${manifest.description || 'API wrapper tool'}
|
|
252
|
-
*/
|
|
253
|
-
|
|
254
|
-
import { cli, env, output, outputError, withErrorHandling } from '@cli4ai/lib';
|
|
255
|
-
|
|
256
|
-
const program = cli('${manifest.name}', '${manifest.version}', '${manifest.description || manifest.name}');
|
|
257
|
-
|
|
258
|
-
program
|
|
259
|
-
.command('fetch <endpoint>')
|
|
260
|
-
.description('Fetch data from API')
|
|
261
|
-
.action(withErrorHandling(async (endpoint: string) => {
|
|
262
|
-
// Use \`cli4ai secrets set API_KEY\` to store this securely (cli4ai injects it at runtime).
|
|
263
|
-
const apiKey = env('API_KEY');
|
|
264
|
-
|
|
265
|
-
const res = await fetch(\`https://api.example.com/\${endpoint}\`, {
|
|
266
|
-
headers: { 'Authorization': \`Bearer \${apiKey}\` }
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
if (!res.ok) {
|
|
270
|
-
outputError('API_ERROR', \`HTTP \${res.status}: \${res.statusText}\`);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
output(await res.json());
|
|
274
|
-
}));
|
|
275
|
-
|
|
276
|
-
program.parse();
|
|
277
|
-
`;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function getBrowserTemplate(manifest: Manifest): string {
|
|
281
|
-
return `#!/usr/bin/env npx tsx
|
|
282
|
-
/**
|
|
283
|
-
* ${manifest.name} - ${manifest.description || 'Browser automation tool'}
|
|
284
|
-
*/
|
|
285
|
-
|
|
286
|
-
import puppeteer from 'puppeteer';
|
|
287
|
-
import { cli, output, outputError, withErrorHandling } from '@cli4ai/lib';
|
|
288
|
-
|
|
289
|
-
const program = cli('${manifest.name}', '${manifest.version}', '${manifest.description || manifest.name}');
|
|
290
|
-
|
|
291
|
-
program
|
|
292
|
-
.command('screenshot <url>')
|
|
293
|
-
.description('Take a screenshot of a webpage')
|
|
294
|
-
.option('-o, --output <file>', 'Output file', 'screenshot.png')
|
|
295
|
-
.option('--full-page', 'Capture full page')
|
|
296
|
-
.action(withErrorHandling(async (url: string, options: { output: string; fullPage?: boolean }) => {
|
|
297
|
-
const browser = await puppeteer.launch({ headless: true });
|
|
298
|
-
|
|
299
|
-
try {
|
|
300
|
-
const page = await browser.newPage();
|
|
301
|
-
await page.goto(url, { waitUntil: 'networkidle2' });
|
|
302
|
-
await page.screenshot({ path: options.output, fullPage: options.fullPage });
|
|
303
|
-
|
|
304
|
-
output({
|
|
305
|
-
url,
|
|
306
|
-
screenshot: options.output,
|
|
307
|
-
timestamp: new Date().toISOString()
|
|
308
|
-
});
|
|
309
|
-
} finally {
|
|
310
|
-
await browser.close();
|
|
311
|
-
}
|
|
312
|
-
}));
|
|
313
|
-
|
|
314
|
-
program.parse();
|
|
315
|
-
`;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
function getReadmeTemplate(templateName: string, manifest: Manifest): string {
|
|
319
|
-
const exampleCmd =
|
|
320
|
-
templateName === 'api' ? 'fetch users' :
|
|
321
|
-
templateName === 'browser' ? 'screenshot https://example.com' :
|
|
322
|
-
'hello world';
|
|
323
|
-
|
|
324
|
-
return `# ${manifest.name}
|
|
325
|
-
|
|
326
|
-
${manifest.description || ''}
|
|
327
|
-
|
|
328
|
-
## Setup
|
|
329
|
-
|
|
330
|
-
\`\`\`bash
|
|
331
|
-
npm install
|
|
332
|
-
\`\`\`
|
|
333
|
-
|
|
334
|
-
## Run directly
|
|
335
|
-
|
|
336
|
-
\`\`\`bash
|
|
337
|
-
npx tsx run.ts ${exampleCmd}
|
|
338
|
-
\`\`\`
|
|
339
|
-
|
|
340
|
-
## Install with cli4ai (project-scoped)
|
|
341
|
-
|
|
342
|
-
\`\`\`bash
|
|
343
|
-
# From the project you want to use this tool in:
|
|
344
|
-
cli4ai add --local /path/to/${manifest.name} -y
|
|
345
|
-
cli4ai run ${manifest.name} ${exampleCmd}
|
|
346
|
-
\`\`\`
|
|
347
|
-
|
|
348
|
-
## Testing
|
|
349
|
-
|
|
350
|
-
\`\`\`bash
|
|
351
|
-
npm test
|
|
352
|
-
\`\`\`
|
|
353
|
-
|
|
354
|
-
## MCP
|
|
355
|
-
|
|
356
|
-
\`\`\`bash
|
|
357
|
-
cli4ai start ${manifest.name}
|
|
358
|
-
\`\`\`
|
|
359
|
-
`;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
function getGitignoreTemplate(): string {
|
|
363
|
-
return `node_modules
|
|
364
|
-
.cli4ai
|
|
365
|
-
.env
|
|
366
|
-
.env.*
|
|
367
|
-
dist
|
|
368
|
-
.DS_Store
|
|
369
|
-
*.log
|
|
370
|
-
coverage
|
|
371
|
-
`;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
function getTsconfigTemplate(): string {
|
|
375
|
-
return `{
|
|
376
|
-
"compilerOptions": {
|
|
377
|
-
"target": "ES2022",
|
|
378
|
-
"module": "NodeNext",
|
|
379
|
-
"moduleResolution": "NodeNext",
|
|
380
|
-
"strict": true,
|
|
381
|
-
"esModuleInterop": true,
|
|
382
|
-
"skipLibCheck": true,
|
|
383
|
-
"forceConsistentCasingInFileNames": true,
|
|
384
|
-
"resolveJsonModule": true,
|
|
385
|
-
"declaration": false,
|
|
386
|
-
"noEmit": true
|
|
387
|
-
},
|
|
388
|
-
"include": ["*.ts", "lib/**/*.ts"],
|
|
389
|
-
"exclude": ["node_modules"]
|
|
390
|
-
}
|
|
391
|
-
`;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
function getVitestConfigTemplate(): string {
|
|
395
|
-
return `import { defineConfig } from 'vitest/config';
|
|
396
|
-
|
|
397
|
-
export default defineConfig({
|
|
398
|
-
test: {
|
|
399
|
-
globals: true,
|
|
400
|
-
},
|
|
401
|
-
});
|
|
402
|
-
`;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
function getTestTemplate(templateName: string, manifest: Manifest): string {
|
|
406
|
-
const testCmd = templateName === 'api' ? 'fetch' : templateName === 'browser' ? 'screenshot' : 'hello';
|
|
407
|
-
const envSetup = templateName === 'api' ? `
|
|
408
|
-
// Set required env vars before importing
|
|
409
|
-
process.env.API_KEY = 'test-api-key';
|
|
410
|
-
` : '';
|
|
411
|
-
|
|
412
|
-
return `import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
413
|
-
${envSetup}
|
|
414
|
-
describe('${manifest.name}', () => {
|
|
415
|
-
let consoleSpy: ReturnType<typeof vi.spyOn>;
|
|
416
|
-
let logs: string[];
|
|
417
|
-
|
|
418
|
-
beforeEach(() => {
|
|
419
|
-
logs = [];
|
|
420
|
-
consoleSpy = vi.spyOn(console, 'log').mockImplementation((msg: string) => {
|
|
421
|
-
logs.push(msg);
|
|
422
|
-
});
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
afterEach(() => {
|
|
426
|
-
consoleSpy.mockRestore();
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
describe('${testCmd} command', () => {
|
|
430
|
-
test('should output valid JSON', async () => {
|
|
431
|
-
// TODO: Import and test your command handler
|
|
432
|
-
// Example:
|
|
433
|
-
// import { ${testCmd}Command } from './run';
|
|
434
|
-
// await ${testCmd}Command('test-arg');
|
|
435
|
-
// expect(logs.length).toBeGreaterThan(0);
|
|
436
|
-
// const result = JSON.parse(logs[0]);
|
|
437
|
-
// expect(result).toBeDefined();
|
|
438
|
-
|
|
439
|
-
expect(true).toBe(true); // Placeholder - replace with real test
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
test('should handle invalid input', async () => {
|
|
443
|
-
// TODO: Test error handling
|
|
444
|
-
// Example:
|
|
445
|
-
// expect(() => ${testCmd}Command('')).toThrow();
|
|
446
|
-
|
|
447
|
-
expect(true).toBe(true); // Placeholder - replace with real test
|
|
448
|
-
});
|
|
449
|
-
});
|
|
450
|
-
});
|
|
451
|
-
`;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
function getDevPackageJson(
|
|
455
|
-
templateName: string,
|
|
456
|
-
manifest: Manifest
|
|
457
|
-
): Record<string, unknown> {
|
|
458
|
-
const deps = manifest.dependencies || {};
|
|
459
|
-
|
|
460
|
-
return {
|
|
461
|
-
name: manifest.name,
|
|
462
|
-
version: manifest.version,
|
|
463
|
-
private: true,
|
|
464
|
-
type: 'module',
|
|
465
|
-
bin: {
|
|
466
|
-
[manifest.name]: `./run.ts`,
|
|
467
|
-
},
|
|
468
|
-
dependencies: deps,
|
|
469
|
-
devDependencies: {
|
|
470
|
-
'typescript': '^5.0.0',
|
|
471
|
-
'tsx': '^4.0.0',
|
|
472
|
-
'vitest': '^2.0.0',
|
|
473
|
-
'@types/node': '^22.0.0',
|
|
474
|
-
},
|
|
475
|
-
scripts: {
|
|
476
|
-
dev: 'tsx run.ts',
|
|
477
|
-
test: 'vitest run',
|
|
478
|
-
'test:watch': 'vitest',
|
|
479
|
-
},
|
|
480
|
-
};
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
function getNextSteps(
|
|
484
|
-
nameArg: string | undefined,
|
|
485
|
-
targetDir: string,
|
|
486
|
-
templateName: string,
|
|
487
|
-
manifest: Manifest
|
|
488
|
-
): Array<{ description: string; command: string }> {
|
|
489
|
-
const exampleArgs =
|
|
490
|
-
templateName === 'api' ? 'fetch users' :
|
|
491
|
-
templateName === 'browser' ? 'screenshot https://example.com' :
|
|
492
|
-
'hello world';
|
|
493
|
-
|
|
494
|
-
const installPath = nameArg ? `./${basename(targetDir)}` : '.';
|
|
495
|
-
|
|
496
|
-
return [
|
|
497
|
-
{
|
|
498
|
-
description: 'Install dependencies',
|
|
499
|
-
command: 'npm install',
|
|
500
|
-
},
|
|
501
|
-
{
|
|
502
|
-
description: 'Run the tool directly',
|
|
503
|
-
command: `npx tsx run.ts ${exampleArgs}`,
|
|
504
|
-
},
|
|
505
|
-
{
|
|
506
|
-
description: 'Run tests',
|
|
507
|
-
command: 'npm test',
|
|
508
|
-
},
|
|
509
|
-
{
|
|
510
|
-
description: 'Install into a project and run via cli4ai',
|
|
511
|
-
command: `cli4ai add --local ${installPath} -y && cli4ai run ${manifest.name} ${exampleArgs}`,
|
|
512
|
-
}
|
|
513
|
-
];
|
|
514
|
-
}
|
package/src/commands/list.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* cli4ai list - Show installed packages
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { output } from '../lib/cli.js';
|
|
6
|
-
import {
|
|
7
|
-
getGlobalPackages,
|
|
8
|
-
getLocalPackages,
|
|
9
|
-
getNpmGlobalPackages,
|
|
10
|
-
type InstalledPackage
|
|
11
|
-
} from '../core/config.js';
|
|
12
|
-
|
|
13
|
-
interface ListOptions {
|
|
14
|
-
global?: boolean;
|
|
15
|
-
json?: boolean;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
type PackageWithScope = InstalledPackage & { scope: 'local' | 'cli4ai' | 'npm' };
|
|
19
|
-
|
|
20
|
-
export async function listCommand(options: ListOptions): Promise<void> {
|
|
21
|
-
let packages: PackageWithScope[];
|
|
22
|
-
|
|
23
|
-
// Get all package sources
|
|
24
|
-
const local = getLocalPackages(process.cwd());
|
|
25
|
-
const cli4aiGlobal = getGlobalPackages();
|
|
26
|
-
const npmGlobal = getNpmGlobalPackages();
|
|
27
|
-
|
|
28
|
-
if (options.global) {
|
|
29
|
-
// Only global packages (cli4ai + npm)
|
|
30
|
-
const cli4aiNames = new Set(cli4aiGlobal.map(p => p.name));
|
|
31
|
-
packages = [
|
|
32
|
-
...cli4aiGlobal.map(p => ({ ...p, scope: 'cli4ai' as const })),
|
|
33
|
-
...npmGlobal.filter(p => !cli4aiNames.has(p.name)).map(p => ({ ...p, scope: 'npm' as const }))
|
|
34
|
-
];
|
|
35
|
-
} else {
|
|
36
|
-
// All packages: local > cli4ai global > npm global
|
|
37
|
-
const seenNames = new Set<string>();
|
|
38
|
-
|
|
39
|
-
packages = [];
|
|
40
|
-
|
|
41
|
-
for (const pkg of local) {
|
|
42
|
-
packages.push({ ...pkg, scope: 'local' as const });
|
|
43
|
-
seenNames.add(pkg.name);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
for (const pkg of cli4aiGlobal) {
|
|
47
|
-
if (!seenNames.has(pkg.name)) {
|
|
48
|
-
packages.push({ ...pkg, scope: 'cli4ai' as const });
|
|
49
|
-
seenNames.add(pkg.name);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
for (const pkg of npmGlobal) {
|
|
54
|
-
if (!seenNames.has(pkg.name)) {
|
|
55
|
-
packages.push({ ...pkg, scope: 'npm' as const });
|
|
56
|
-
seenNames.add(pkg.name);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
output({
|
|
62
|
-
packages: packages.map(p => ({
|
|
63
|
-
name: p.name,
|
|
64
|
-
version: p.version,
|
|
65
|
-
path: p.path,
|
|
66
|
-
source: p.source,
|
|
67
|
-
scope: p.scope
|
|
68
|
-
})),
|
|
69
|
-
count: packages.length,
|
|
70
|
-
location: options.global ? 'global' : 'all'
|
|
71
|
-
});
|
|
72
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* cli4ai mcp-config - Generate MCP configuration for Claude Code
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { output, outputError } from '../lib/cli.js';
|
|
6
|
-
import {
|
|
7
|
-
generateClaudeCodeConfig,
|
|
8
|
-
formatClaudeCodeConfig,
|
|
9
|
-
generateConfigSnippet,
|
|
10
|
-
generateServerConfig
|
|
11
|
-
} from '../mcp/config-gen.js';
|
|
12
|
-
import { findPackage } from '../core/config.js';
|
|
13
|
-
import { tryLoadManifest } from '../core/manifest.js';
|
|
14
|
-
|
|
15
|
-
interface McpConfigOptions {
|
|
16
|
-
global?: boolean;
|
|
17
|
-
package?: string;
|
|
18
|
-
snippet?: boolean;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function mcpConfigCommand(options: McpConfigOptions): Promise<void> {
|
|
22
|
-
const cwd = process.cwd();
|
|
23
|
-
|
|
24
|
-
// Single package snippet mode
|
|
25
|
-
if (options.snippet && options.package) {
|
|
26
|
-
const pkg = findPackage(options.package, cwd);
|
|
27
|
-
if (!pkg) {
|
|
28
|
-
outputError('NOT_FOUND', `Package not found: ${options.package}`);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const manifest = tryLoadManifest(pkg!.path);
|
|
32
|
-
if (!manifest) {
|
|
33
|
-
outputError('MANIFEST_ERROR', `Could not load manifest for ${options.package}`);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (!manifest!.mcp?.enabled) {
|
|
37
|
-
outputError('INVALID_INPUT', `Package ${options.package} does not have MCP enabled`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const serverName = `cli4ai-${manifest!.name}`;
|
|
41
|
-
const serverConfig = generateServerConfig(manifest!, pkg!.path);
|
|
42
|
-
const snippet = generateConfigSnippet(manifest!, pkg!.path);
|
|
43
|
-
output({
|
|
44
|
-
serverName,
|
|
45
|
-
serverConfig,
|
|
46
|
-
snippet
|
|
47
|
-
});
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Generate full config
|
|
52
|
-
const packages = options.package ? [options.package] : undefined;
|
|
53
|
-
const config = generateClaudeCodeConfig(cwd, {
|
|
54
|
-
global: options.global,
|
|
55
|
-
packages
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
if (Object.keys(config.mcpServers).length === 0) {
|
|
59
|
-
outputError('NOT_FOUND', 'No MCP-enabled packages found', {
|
|
60
|
-
hint: 'Install packages with "cli4ai add <package>" first'
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Output formatted config
|
|
65
|
-
output({
|
|
66
|
-
config,
|
|
67
|
-
formatted: formatClaudeCodeConfig(config)
|
|
68
|
-
});
|
|
69
|
-
}
|