@stan-chen/simple-cli 0.2.2 → 0.2.4

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 (100) hide show
  1. package/README.md +58 -271
  2. package/dist/anyllm.py +62 -0
  3. package/dist/builtins.d.ts +726 -0
  4. package/dist/builtins.js +481 -0
  5. package/dist/cli.d.ts +0 -4
  6. package/dist/cli.js +37 -279
  7. package/dist/engine.d.ts +33 -0
  8. package/dist/engine.js +138 -0
  9. package/dist/learnings.d.ts +15 -0
  10. package/dist/learnings.js +54 -0
  11. package/dist/llm.d.ts +18 -0
  12. package/dist/llm.js +66 -0
  13. package/dist/mcp.d.ts +132 -0
  14. package/dist/mcp.js +43 -0
  15. package/dist/skills.d.ts +5 -16
  16. package/dist/skills.js +91 -253
  17. package/dist/tui.d.ts +1 -0
  18. package/dist/tui.js +10 -0
  19. package/package.json +88 -78
  20. package/dist/commands/add.d.ts +0 -9
  21. package/dist/commands/add.js +0 -50
  22. package/dist/commands/git/commit.d.ts +0 -12
  23. package/dist/commands/git/commit.js +0 -97
  24. package/dist/commands/git/status.d.ts +0 -6
  25. package/dist/commands/git/status.js +0 -42
  26. package/dist/commands/index.d.ts +0 -16
  27. package/dist/commands/index.js +0 -376
  28. package/dist/commands/mcp/status.d.ts +0 -6
  29. package/dist/commands/mcp/status.js +0 -31
  30. package/dist/commands/swarm.d.ts +0 -36
  31. package/dist/commands/swarm.js +0 -236
  32. package/dist/commands.d.ts +0 -32
  33. package/dist/commands.js +0 -427
  34. package/dist/context.d.ts +0 -116
  35. package/dist/context.js +0 -327
  36. package/dist/index.d.ts +0 -6
  37. package/dist/index.js +0 -109
  38. package/dist/lib/agent.d.ts +0 -98
  39. package/dist/lib/agent.js +0 -281
  40. package/dist/lib/editor.d.ts +0 -74
  41. package/dist/lib/editor.js +0 -441
  42. package/dist/lib/git.d.ts +0 -164
  43. package/dist/lib/git.js +0 -351
  44. package/dist/lib/ui.d.ts +0 -159
  45. package/dist/lib/ui.js +0 -252
  46. package/dist/mcp/client.d.ts +0 -22
  47. package/dist/mcp/client.js +0 -81
  48. package/dist/mcp/manager.d.ts +0 -186
  49. package/dist/mcp/manager.js +0 -446
  50. package/dist/prompts/provider.d.ts +0 -22
  51. package/dist/prompts/provider.js +0 -78
  52. package/dist/providers/index.d.ts +0 -15
  53. package/dist/providers/index.js +0 -82
  54. package/dist/providers/multi.d.ts +0 -11
  55. package/dist/providers/multi.js +0 -28
  56. package/dist/registry.d.ts +0 -24
  57. package/dist/registry.js +0 -379
  58. package/dist/repoMap.d.ts +0 -5
  59. package/dist/repoMap.js +0 -79
  60. package/dist/router.d.ts +0 -41
  61. package/dist/router.js +0 -108
  62. package/dist/swarm/coordinator.d.ts +0 -86
  63. package/dist/swarm/coordinator.js +0 -257
  64. package/dist/swarm/index.d.ts +0 -28
  65. package/dist/swarm/index.js +0 -29
  66. package/dist/swarm/task.d.ts +0 -104
  67. package/dist/swarm/task.js +0 -221
  68. package/dist/swarm/types.d.ts +0 -132
  69. package/dist/swarm/types.js +0 -37
  70. package/dist/swarm/worker.d.ts +0 -107
  71. package/dist/swarm/worker.js +0 -299
  72. package/dist/tools/analyzeFile.d.ts +0 -16
  73. package/dist/tools/analyzeFile.js +0 -43
  74. package/dist/tools/git.d.ts +0 -40
  75. package/dist/tools/git.js +0 -236
  76. package/dist/tools/glob.d.ts +0 -34
  77. package/dist/tools/glob.js +0 -165
  78. package/dist/tools/grep.d.ts +0 -53
  79. package/dist/tools/grep.js +0 -296
  80. package/dist/tools/linter.d.ts +0 -35
  81. package/dist/tools/linter.js +0 -349
  82. package/dist/tools/listDir.d.ts +0 -29
  83. package/dist/tools/listDir.js +0 -50
  84. package/dist/tools/memory.d.ts +0 -34
  85. package/dist/tools/memory.js +0 -215
  86. package/dist/tools/readFiles.d.ts +0 -25
  87. package/dist/tools/readFiles.js +0 -31
  88. package/dist/tools/reloadTools.d.ts +0 -11
  89. package/dist/tools/reloadTools.js +0 -22
  90. package/dist/tools/runCommand.d.ts +0 -32
  91. package/dist/tools/runCommand.js +0 -79
  92. package/dist/tools/scraper.d.ts +0 -31
  93. package/dist/tools/scraper.js +0 -211
  94. package/dist/tools/writeFiles.d.ts +0 -63
  95. package/dist/tools/writeFiles.js +0 -87
  96. package/dist/ui/server.d.ts +0 -5
  97. package/dist/ui/server.js +0 -74
  98. package/dist/watcher.d.ts +0 -35
  99. package/dist/watcher.js +0 -164
  100. /package/{docs/assets → assets}/logo.jpeg +0 -0
@@ -1,376 +0,0 @@
1
- import { Command, Flags } from '@oclif/core';
2
- import * as ui from '../lib/ui.js';
3
- import { Agent, summarizeHistory } from '../lib/agent.js';
4
- import { getGitManager } from '../lib/git.js';
5
- import { getContextManager } from '../context.js';
6
- import { createProvider } from '../providers/index.js';
7
- import { createMultiProvider } from '../providers/multi.js';
8
- import { routeTask, loadTierConfig } from '../router.js';
9
- import { getMCPManager } from '../mcp/manager.js';
10
- import { getActiveSkill, setActiveSkill, listSkills } from '../skills.js';
11
- import { createFileWatcher } from '../watcher.js';
12
- import 'dotenv/config';
13
- export default class Chat extends Command {
14
- static description = 'Start an interactive coding session';
15
- static flags = {
16
- yolo: Flags.boolean({
17
- description: 'Auto-approve all tool executions',
18
- default: false,
19
- }),
20
- moe: Flags.boolean({
21
- description: 'Enable Mix of Experts routing',
22
- default: false,
23
- }),
24
- watch: Flags.boolean({
25
- description: 'Watch files for AI comments',
26
- default: false,
27
- }),
28
- skill: Flags.string({
29
- description: 'Initial skill/mode (code, architect, test, etc.)',
30
- default: 'code',
31
- }),
32
- 'auto-commit': Flags.boolean({
33
- description: 'Auto-commit after successful changes',
34
- default: false,
35
- }),
36
- 'auto-lint': Flags.boolean({
37
- description: 'Auto-lint after file changes',
38
- default: true,
39
- }),
40
- 'auto-test': Flags.boolean({
41
- description: 'Auto-run tests after changes',
42
- default: false,
43
- }),
44
- 'test-cmd': Flags.string({
45
- description: 'Test command to run',
46
- }),
47
- };
48
- async run() {
49
- const { flags } = await this.parse(Chat);
50
- // Show banner
51
- ui.intro('Simple-CLI v0.2.0');
52
- ui.log(`Mode: ${flags.moe ? 'MoE' : 'Single'} | ${flags.yolo ? 'YOLO' : 'Safe'} | Skill: @${flags.skill}`);
53
- // Initialize context
54
- const ctx = getContextManager();
55
- await ctx.initialize();
56
- ui.success(`Loaded ${ctx.getTools().size} tools`);
57
- // Set initial skill
58
- const skill = setActiveSkill(flags.skill);
59
- if (skill) {
60
- ctx.setSkill(skill);
61
- }
62
- // Initialize git
63
- const git = getGitManager();
64
- const isGitRepo = await git.isRepo();
65
- if (isGitRepo) {
66
- const branch = await git.currentBranch();
67
- ui.log(`Git: ${branch || 'detached HEAD'}`);
68
- }
69
- // Initialize MCP
70
- const mcpManager = getMCPManager();
71
- try {
72
- const configs = await mcpManager.loadConfig();
73
- if (configs.length > 0) {
74
- await ui.spin(`Connecting to ${configs.length} MCP server(s)`, async () => {
75
- await mcpManager.connectAll(configs);
76
- });
77
- }
78
- }
79
- catch {
80
- // MCP not configured
81
- }
82
- // Initialize providers
83
- const tierConfigs = flags.moe ? loadTierConfig() : null;
84
- const multiProvider = tierConfigs ? createMultiProvider(tierConfigs) : null;
85
- const singleProvider = !flags.moe ? createProvider() : null;
86
- // Initialize file watcher
87
- let watcher = null;
88
- if (flags.watch) {
89
- watcher = createFileWatcher({
90
- root: ctx.getCwd(),
91
- onAIComment: (path, comments) => {
92
- ui.note(comments.map(c => `Line ${c.line}: ${c.text}`).join('\n'), `AI Comments in ${path}`);
93
- },
94
- });
95
- ui.success('File watcher active');
96
- }
97
- // Generate function
98
- const generate = async (messages) => {
99
- const fullPrompt = await ctx.buildSystemPrompt();
100
- const llmMessages = messages.map(m => ({ role: m.role, content: m.content }));
101
- if (flags.moe && multiProvider && tierConfigs) {
102
- const userMsg = messages.find(m => m.role === 'user')?.content || '';
103
- const routing = await routeTask(userMsg, async (prompt) => {
104
- return multiProvider.generateWithTier(1, prompt, [{ role: 'user', content: userMsg }]);
105
- });
106
- return multiProvider.generateWithTier(routing.tier, fullPrompt, llmMessages);
107
- }
108
- return singleProvider.generateResponse(fullPrompt, llmMessages);
109
- };
110
- // Tool execution
111
- const executeTool = async (name, args) => {
112
- const tools = ctx.getTools();
113
- const tool = tools.get(name);
114
- if (!tool) {
115
- throw new Error(`Tool not found: ${name}`);
116
- }
117
- // Permission check
118
- if (!flags.yolo && tool.permission !== 'read') {
119
- const confirmed = await ui.confirm({
120
- message: `Execute ${name}?`,
121
- initialValue: false,
122
- });
123
- if (ui.isCancel(confirmed) || !confirmed) {
124
- return 'Cancelled by user';
125
- }
126
- }
127
- return tool.execute(args);
128
- };
129
- // Create agent
130
- const agent = new Agent({
131
- config: {
132
- maxReflections: 3,
133
- autoLint: flags['auto-lint'],
134
- autoTest: flags['auto-test'],
135
- autoCommit: flags['auto-commit'],
136
- testCommand: flags['test-cmd'],
137
- },
138
- git,
139
- generateFn: generate,
140
- executeTool,
141
- lintFn: flags['auto-lint'] ? async (file) => {
142
- const { execute } = await import('../tools/linter.js');
143
- const result = await execute({ path: file, fix: false });
144
- return { passed: result.passed, output: result.output };
145
- } : undefined,
146
- testFn: flags['auto-test'] && flags['test-cmd'] ? async () => {
147
- const { execute } = await import('../tools/runCommand.js');
148
- const result = await execute({ command: flags['test-cmd'] });
149
- return { passed: result.exitCode === 0, output: result.stdout + result.stderr };
150
- } : undefined,
151
- });
152
- // Main loop
153
- while (true) {
154
- const currentSkill = getActiveSkill();
155
- const input = await ui.text({
156
- message: `[${currentSkill.name}]`,
157
- placeholder: 'Type a message or /help...',
158
- });
159
- if (ui.isCancel(input)) {
160
- break;
161
- }
162
- const message = input.trim();
163
- if (!message)
164
- continue;
165
- // Handle slash commands
166
- if (message.startsWith('/')) {
167
- await handleCommand(message, ctx, git, mcpManager);
168
- continue;
169
- }
170
- // Handle skill switching
171
- if (message.startsWith('@')) {
172
- const skillName = message.slice(1).trim();
173
- if (skillName === 'list') {
174
- ui.note(listSkills().map(s => `@${s.name} - ${s.description}`).join('\n'), 'Available Skills');
175
- }
176
- else {
177
- const newSkill = setActiveSkill(skillName);
178
- if (newSkill) {
179
- ctx.setSkill(newSkill);
180
- ui.success(`Switched to @${newSkill.name}`);
181
- }
182
- else {
183
- ui.error(`Unknown skill: @${skillName}`);
184
- }
185
- }
186
- continue;
187
- }
188
- // Add watch context if available
189
- let fullMessage = message;
190
- if (watcher) {
191
- const watchPrompt = watcher.getActionableCommentsPrompt();
192
- if (watchPrompt) {
193
- fullMessage = `${watchPrompt}\n\nUser request: ${message}`;
194
- }
195
- }
196
- // Process with agent
197
- try {
198
- ctx.addMessage('user', message);
199
- const history = ctx.getHistory();
200
- // Summarize if too long
201
- const summarizedHistory = await summarizeHistory(history, generate, 20);
202
- const systemPrompt = await ctx.buildSystemPrompt();
203
- const result = await agent.process(fullMessage, summarizedHistory, systemPrompt);
204
- // Add response to history
205
- if (result.response.action && 'message' in result.response.action) {
206
- ctx.addMessage('assistant', result.response.action.message);
207
- }
208
- }
209
- catch (error) {
210
- ui.error(`Error: ${error instanceof Error ? error.message : error}`);
211
- }
212
- }
213
- // Cleanup
214
- watcher?.stop();
215
- await mcpManager.disconnectAll();
216
- ui.outro('Goodbye!');
217
- }
218
- }
219
- /**
220
- * Handle slash commands
221
- */
222
- async function handleCommand(input, ctx, git, mcpManager) {
223
- const [cmd, ...args] = input.slice(1).split(/\s+/);
224
- const argsStr = args.join(' ');
225
- switch (cmd.toLowerCase()) {
226
- case 'help':
227
- case 'h':
228
- ui.note(`
229
- /add <file> - Add file to context
230
- /drop [file] - Remove file from context
231
- /ls - List files in context
232
- /clear - Clear chat history
233
- /diff - Show git diff
234
- /status - Show git status
235
- /commit [msg] - Commit changes
236
- /undo - Undo last commit
237
- /tokens - Show token estimate
238
- /mcp [cmd] - MCP management
239
- /skill <name> - Switch skill
240
- /exit - Exit
241
- `, 'Commands');
242
- break;
243
- case 'add':
244
- case 'a':
245
- if (argsStr) {
246
- const added = ctx.addFile(argsStr);
247
- if (added) {
248
- ui.success(`Added ${argsStr}`);
249
- }
250
- else {
251
- ui.error(`File not found: ${argsStr}`);
252
- }
253
- }
254
- else {
255
- ui.error('Usage: /add <file>');
256
- }
257
- break;
258
- case 'drop':
259
- case 'd':
260
- if (argsStr) {
261
- ctx.removeFile(argsStr);
262
- ui.success(`Dropped ${argsStr}`);
263
- }
264
- else {
265
- // Drop all
266
- const files = ctx.getFiles();
267
- for (const f of [...files.active, ...files.readOnly]) {
268
- ctx.removeFile(f);
269
- }
270
- ui.success('Dropped all files');
271
- }
272
- break;
273
- case 'ls':
274
- case 'files':
275
- const files = ctx.getFiles();
276
- if (files.active.length === 0 && files.readOnly.length === 0) {
277
- ui.log('No files in context');
278
- }
279
- else {
280
- ui.showFileStatus([
281
- ...files.active.map(f => ({ path: f, status: 'modified' })),
282
- ...files.readOnly.map(f => ({ path: f, status: 'readonly' })),
283
- ]);
284
- }
285
- break;
286
- case 'clear':
287
- ctx.clearHistory();
288
- ui.success('Chat history cleared');
289
- break;
290
- case 'diff':
291
- const diff = await git.diff();
292
- if (diff) {
293
- ui.showDiff(diff);
294
- }
295
- else {
296
- ui.log('No changes');
297
- }
298
- break;
299
- case 'status':
300
- const status = await git.status();
301
- ui.log(`Branch: ${status.current}`);
302
- if (status.modified.length)
303
- ui.log(`Modified: ${status.modified.join(', ')}`);
304
- if (status.created.length)
305
- ui.log(`Created: ${status.created.join(', ')}`);
306
- if (status.deleted.length)
307
- ui.log(`Deleted: ${status.deleted.join(', ')}`);
308
- if (status.not_added.length)
309
- ui.log(`Untracked: ${status.not_added.join(', ')}`);
310
- break;
311
- case 'commit':
312
- const commitDiff = await git.stagedDiff();
313
- if (!commitDiff && !(await git.status()).staged.length) {
314
- await git.addAll();
315
- }
316
- const result = await git.commit({ message: argsStr || 'Update' });
317
- if (result) {
318
- ui.success(`Committed: ${result.hash} ${result.message}`);
319
- }
320
- else {
321
- ui.error('Nothing to commit');
322
- }
323
- break;
324
- case 'undo':
325
- if (await git.undoLastCommit()) {
326
- ui.success('Undid last commit');
327
- }
328
- else {
329
- ui.error('Failed to undo');
330
- }
331
- break;
332
- case 'tokens':
333
- const tokens = await ctx.estimateTokenCount();
334
- ui.showTokens(tokens);
335
- break;
336
- case 'mcp':
337
- const subCmd = args[0];
338
- if (subCmd === 'status') {
339
- const statuses = mcpManager.getAllServerStatuses();
340
- for (const [name, status] of statuses) {
341
- ui.log(`${name}: ${status}`);
342
- }
343
- }
344
- else if (subCmd === 'tools') {
345
- const tools = mcpManager.getAllTools();
346
- for (const tool of tools) {
347
- ui.log(`${tool.name} (${tool.serverName}): ${tool.description}`);
348
- }
349
- }
350
- else {
351
- ui.log('Usage: /mcp [status|tools]');
352
- }
353
- break;
354
- case 'skill':
355
- if (argsStr) {
356
- const newSkill = setActiveSkill(argsStr);
357
- if (newSkill) {
358
- ctx.setSkill(newSkill);
359
- ui.success(`Switched to @${newSkill.name}`);
360
- }
361
- else {
362
- ui.error(`Unknown skill: ${argsStr}`);
363
- }
364
- }
365
- else {
366
- ui.note(listSkills().map(s => `@${s.name} - ${s.description}`).join('\n'), 'Available Skills');
367
- }
368
- break;
369
- case 'exit':
370
- case 'quit':
371
- case 'q':
372
- process.exit(0);
373
- default:
374
- ui.error(`Unknown command: /${cmd}`);
375
- }
376
- }
@@ -1,6 +0,0 @@
1
- import { Command } from '@oclif/core';
2
- export default class MCPStatus extends Command {
3
- static description: string;
4
- static examples: string[];
5
- run(): Promise<void>;
6
- }
@@ -1,31 +0,0 @@
1
- import { Command } from '@oclif/core';
2
- import * as ui from '../../lib/ui.js';
3
- import { getMCPManager, MCPServerStatus } from '../../mcp/manager.js';
4
- export default class MCPStatus extends Command {
5
- static description = 'Show MCP server status';
6
- static examples = ['<%= config.bin %> mcp status'];
7
- async run() {
8
- const manager = getMCPManager();
9
- const statuses = manager.getAllServerStatuses();
10
- if (statuses.size === 0) {
11
- ui.log('No MCP servers configured');
12
- ui.note('Create a mcp.json file to configure MCP servers', 'Hint');
13
- return;
14
- }
15
- ui.log('MCP Servers:\n');
16
- const statusIcon = {
17
- [MCPServerStatus.CONNECTED]: ui.theme.success('ā—'),
18
- [MCPServerStatus.CONNECTING]: ui.theme.warning('ā—‹'),
19
- [MCPServerStatus.DISCONNECTED]: ui.theme.muted('ā—‹'),
20
- [MCPServerStatus.ERROR]: ui.theme.error('ā—'),
21
- };
22
- for (const [name, status] of statuses) {
23
- ui.log(` ${statusIcon[status]} ${name}: ${status}`);
24
- }
25
- // Show tool count
26
- const tools = manager.getAllTools();
27
- const resources = manager.getAllResources();
28
- const prompts = manager.getAllPrompts();
29
- ui.log(`\nDiscovered: ${tools.length} tools, ${resources.length} resources, ${prompts.length} prompts`);
30
- }
31
- }
@@ -1,36 +0,0 @@
1
- /**
2
- * Swarm Command - Run multiple agents in parallel
3
- */
4
- import { type SwarmTask, type CoordinatorOptions } from '../swarm/index.js';
5
- export interface SwarmCliOptions {
6
- tasksFile?: string;
7
- task?: string;
8
- scope?: string;
9
- concurrency?: number;
10
- timeout?: number;
11
- yolo?: boolean;
12
- branch?: string;
13
- }
14
- /**
15
- * Parse swarm options from command line arguments
16
- */
17
- export declare function parseSwarmArgs(args: string[]): SwarmCliOptions;
18
- /**
19
- * Load tasks from a JSON file
20
- */
21
- export declare function loadTasksFromFile(filePath: string): {
22
- tasks: SwarmTask[];
23
- options?: Partial<CoordinatorOptions>;
24
- };
25
- /**
26
- * Create tasks from a glob pattern
27
- */
28
- export declare function createTasksFromScope(task: string, scope: string, type?: SwarmTask['type']): Promise<SwarmTask[]>;
29
- /**
30
- * Run swarm orchestrator
31
- */
32
- export declare function runSwarm(options: SwarmCliOptions): Promise<void>;
33
- /**
34
- * Print swarm help
35
- */
36
- export declare function printSwarmHelp(): void;
@@ -1,236 +0,0 @@
1
- /**
2
- * Swarm Command - Run multiple agents in parallel
3
- */
4
- import { readFileSync, existsSync } from 'fs';
5
- import { SwarmCoordinator } from '../swarm/index.js';
6
- import { execute as globExecute } from '../tools/glob.js';
7
- /**
8
- * Parse swarm options from command line arguments
9
- */
10
- export function parseSwarmArgs(args) {
11
- const options = {};
12
- for (let i = 0; i < args.length; i++) {
13
- const arg = args[i];
14
- if (arg === '--tasks' && args[i + 1]) {
15
- options.tasksFile = args[++i];
16
- }
17
- else if (arg === '--task' && args[i + 1]) {
18
- options.task = args[++i];
19
- }
20
- else if (arg === '--scope' && args[i + 1]) {
21
- options.scope = args[++i];
22
- }
23
- else if (arg === '--concurrency' && args[i + 1]) {
24
- options.concurrency = parseInt(args[++i], 10);
25
- }
26
- else if (arg === '--timeout' && args[i + 1]) {
27
- options.timeout = parseInt(args[++i], 10);
28
- }
29
- else if (arg === '--branch' && args[i + 1]) {
30
- options.branch = args[++i];
31
- }
32
- else if (arg === '--yolo') {
33
- options.yolo = true;
34
- }
35
- else if (!arg.startsWith('--') && !options.tasksFile && existsSync(arg)) {
36
- // Positional argument - tasks file
37
- options.tasksFile = arg;
38
- }
39
- }
40
- return options;
41
- }
42
- /**
43
- * Load tasks from a JSON file
44
- */
45
- export function loadTasksFromFile(filePath) {
46
- if (!existsSync(filePath)) {
47
- throw new Error(`Tasks file not found: ${filePath}`);
48
- }
49
- const content = readFileSync(filePath, 'utf-8');
50
- const data = JSON.parse(content);
51
- // Support both { tasks: [...] } and [...] formats
52
- if (Array.isArray(data)) {
53
- return { tasks: data };
54
- }
55
- const tasks = data.tasks || [];
56
- const options = {};
57
- if (data.session) {
58
- if (data.session.concurrency)
59
- options.concurrency = data.session.concurrency;
60
- if (data.session.timeout)
61
- options.timeout = data.session.timeout;
62
- if (data.session.branch)
63
- options.branch = data.session.branch;
64
- }
65
- return { tasks, options };
66
- }
67
- /**
68
- * Create tasks from a glob pattern
69
- */
70
- export async function createTasksFromScope(task, scope, type = 'implement') {
71
- const result = await globExecute({ pattern: scope, maxResults: 100, includeDirectories: false });
72
- return result.matches.map((file, i) => ({
73
- id: `task-${i}`,
74
- type,
75
- description: `${task} in ${file}`,
76
- scope: { files: [file] },
77
- dependencies: [],
78
- priority: 2,
79
- timeout: 300000,
80
- retries: 2,
81
- }));
82
- }
83
- /**
84
- * Run swarm orchestrator
85
- */
86
- export async function runSwarm(options) {
87
- console.log('\nšŸ Simple-CLI Swarm Mode\n');
88
- let tasks = [];
89
- let coordinatorOptions = {};
90
- // Load tasks from file or create from options
91
- if (options.tasksFile) {
92
- console.log(`šŸ“„ Loading tasks from ${options.tasksFile}...`);
93
- const loaded = loadTasksFromFile(options.tasksFile);
94
- tasks = loaded.tasks;
95
- coordinatorOptions = loaded.options || {};
96
- }
97
- else if (options.task && options.scope) {
98
- console.log(`šŸ” Creating tasks from scope: ${options.scope}...`);
99
- tasks = await createTasksFromScope(options.task, options.scope);
100
- }
101
- else if (options.task) {
102
- // Single task
103
- tasks = [{
104
- id: 'single-task',
105
- type: 'implement',
106
- description: options.task,
107
- scope: {},
108
- dependencies: [],
109
- priority: 1,
110
- timeout: options.timeout || 300000,
111
- retries: 2,
112
- }];
113
- }
114
- if (tasks.length === 0) {
115
- console.error('āŒ No tasks to run. Provide --tasks <file> or --task "description"');
116
- process.exit(1);
117
- }
118
- console.log(`šŸ“‹ ${tasks.length} task(s) to execute\n`);
119
- // Apply CLI options
120
- if (options.concurrency)
121
- coordinatorOptions.concurrency = options.concurrency;
122
- if (options.timeout)
123
- coordinatorOptions.timeout = options.timeout;
124
- if (options.branch)
125
- coordinatorOptions.branch = options.branch;
126
- if (options.yolo !== undefined)
127
- coordinatorOptions.yolo = options.yolo;
128
- // Create coordinator
129
- const coordinator = new SwarmCoordinator({
130
- cwd: process.cwd(),
131
- ...coordinatorOptions,
132
- });
133
- // Add event handlers
134
- coordinator.on('task:start', (task, workerId) => {
135
- console.log(`šŸš€ [${workerId}] Starting: ${task.description.slice(0, 60)}...`);
136
- });
137
- coordinator.on('task:complete', (task, result) => {
138
- const status = result.success ? 'āœ…' : 'āš ļø';
139
- console.log(`${status} [${task.id}] Done in ${result.duration}ms`);
140
- if (result.filesChanged.length > 0) {
141
- console.log(` Files: ${result.filesChanged.join(', ')}`);
142
- }
143
- });
144
- coordinator.on('task:fail', (task, error) => {
145
- console.error(`āŒ [${task.id}] Failed: ${error.message}`);
146
- });
147
- coordinator.on('task:retry', (task, attempt) => {
148
- console.log(`šŸ”„ [${task.id}] Retry attempt ${attempt}`);
149
- });
150
- // Add tasks
151
- coordinator.addTasks(tasks);
152
- // Run swarm
153
- const startTime = Date.now();
154
- console.log(`ā±ļø Starting swarm with concurrency: ${coordinatorOptions.concurrency || 4}\n`);
155
- try {
156
- const result = await coordinator.run();
157
- // Print summary
158
- console.log('\n' + '═'.repeat(50));
159
- console.log('šŸ“Š SWARM COMPLETE');
160
- console.log('═'.repeat(50));
161
- console.log(` Total Tasks: ${result.total}`);
162
- console.log(` Completed: ${result.completed} āœ…`);
163
- console.log(` Failed: ${result.failed} āŒ`);
164
- console.log(` Skipped: ${result.skipped} ā­ļø`);
165
- console.log(` Duration: ${((Date.now() - startTime) / 1000).toFixed(1)}s`);
166
- console.log(` Success Rate: ${(result.successRate * 100).toFixed(1)}%`);
167
- if (result.failedTasks.length > 0) {
168
- console.log('\nāŒ Failed Tasks:');
169
- for (const f of result.failedTasks) {
170
- console.log(` - ${f.task.id}: ${f.error}`);
171
- }
172
- }
173
- console.log('═'.repeat(50) + '\n');
174
- // Exit with error code if any failures
175
- if (result.failed > 0) {
176
- process.exit(1);
177
- }
178
- }
179
- catch (error) {
180
- console.error(`\nāŒ Swarm error: ${error instanceof Error ? error.message : error}`);
181
- process.exit(1);
182
- }
183
- }
184
- /**
185
- * Print swarm help
186
- */
187
- export function printSwarmHelp() {
188
- console.log(`
189
- šŸ Simple-CLI Swarm Mode
190
-
191
- USAGE
192
- simple --swarm [options]
193
-
194
- OPTIONS
195
- --tasks <file> Load tasks from JSON file
196
- --task "desc" Single task description
197
- --scope "pattern" Glob pattern for files (creates task per file)
198
- --concurrency <n> Max parallel workers (default: 4)
199
- --timeout <ms> Task timeout in milliseconds
200
- --branch <name> Git branch for changes
201
- --yolo Auto-approve all actions
202
-
203
- EXAMPLES
204
- # Run tasks from file
205
- simple --swarm --tasks tasks.json
206
-
207
- # Single task
208
- simple --swarm --yolo --task "add tests to all files"
209
-
210
- # Task per file matching pattern
211
- simple --swarm --yolo --task "add JSDoc" --scope "src/**/*.ts"
212
-
213
- # With concurrency limit
214
- simple --swarm --concurrency 2 --tasks tasks.json
215
-
216
- TASKS FILE FORMAT
217
- {
218
- "session": {
219
- "concurrency": 4,
220
- "timeout": 300000,
221
- "branch": "feature/swarm"
222
- },
223
- "tasks": [
224
- {
225
- "id": "task-1",
226
- "type": "implement",
227
- "description": "Add validation",
228
- "scope": { "files": ["src/api.ts"] },
229
- "priority": 1,
230
- "timeout": 60000,
231
- "retries": 2
232
- }
233
- ]
234
- }
235
- `);
236
- }