cli4ai 1.2.0 → 1.2.2

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