@stan-chen/simple-cli 0.2.3 → 0.2.5

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 (136) hide show
  1. package/README.md +62 -63
  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 +34 -493
  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 +73 -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 +15 -0
  19. package/package.json +10 -6
  20. package/dist/claw/jit.d.ts +0 -5
  21. package/dist/claw/jit.js +0 -138
  22. package/dist/claw/management.d.ts +0 -3
  23. package/dist/claw/management.js +0 -107
  24. package/dist/commands/add.d.ts +0 -9
  25. package/dist/commands/add.js +0 -50
  26. package/dist/commands/git/commit.d.ts +0 -12
  27. package/dist/commands/git/commit.js +0 -98
  28. package/dist/commands/git/status.d.ts +0 -6
  29. package/dist/commands/git/status.js +0 -42
  30. package/dist/commands/index.d.ts +0 -16
  31. package/dist/commands/index.js +0 -377
  32. package/dist/commands/mcp/status.d.ts +0 -6
  33. package/dist/commands/mcp/status.js +0 -31
  34. package/dist/commands/swarm.d.ts +0 -36
  35. package/dist/commands/swarm.js +0 -236
  36. package/dist/commands.d.ts +0 -32
  37. package/dist/commands.js +0 -427
  38. package/dist/context.d.ts +0 -116
  39. package/dist/context.js +0 -337
  40. package/dist/index.d.ts +0 -6
  41. package/dist/index.js +0 -109
  42. package/dist/lib/agent.d.ts +0 -99
  43. package/dist/lib/agent.js +0 -313
  44. package/dist/lib/editor.d.ts +0 -74
  45. package/dist/lib/editor.js +0 -441
  46. package/dist/lib/git.d.ts +0 -164
  47. package/dist/lib/git.js +0 -356
  48. package/dist/lib/shim.d.ts +0 -4
  49. package/dist/lib/shim.js +0 -30
  50. package/dist/lib/ui.d.ts +0 -159
  51. package/dist/lib/ui.js +0 -277
  52. package/dist/mcp/client.d.ts +0 -22
  53. package/dist/mcp/client.js +0 -81
  54. package/dist/mcp/manager.d.ts +0 -186
  55. package/dist/mcp/manager.js +0 -446
  56. package/dist/prompts/provider.d.ts +0 -22
  57. package/dist/prompts/provider.js +0 -79
  58. package/dist/providers/index.d.ts +0 -31
  59. package/dist/providers/index.js +0 -93
  60. package/dist/providers/multi.d.ts +0 -12
  61. package/dist/providers/multi.js +0 -28
  62. package/dist/registry.d.ts +0 -29
  63. package/dist/registry.js +0 -443
  64. package/dist/repoMap.d.ts +0 -5
  65. package/dist/repoMap.js +0 -79
  66. package/dist/router.d.ts +0 -41
  67. package/dist/router.js +0 -118
  68. package/dist/swarm/coordinator.d.ts +0 -86
  69. package/dist/swarm/coordinator.js +0 -257
  70. package/dist/swarm/index.d.ts +0 -28
  71. package/dist/swarm/index.js +0 -29
  72. package/dist/swarm/task.d.ts +0 -104
  73. package/dist/swarm/task.js +0 -221
  74. package/dist/swarm/types.d.ts +0 -132
  75. package/dist/swarm/types.js +0 -37
  76. package/dist/swarm/worker.d.ts +0 -109
  77. package/dist/swarm/worker.js +0 -369
  78. package/dist/tools/analyzeFile.d.ts +0 -16
  79. package/dist/tools/analyzeFile.js +0 -43
  80. package/dist/tools/analyze_file.d.ts +0 -16
  81. package/dist/tools/analyze_file.js +0 -43
  82. package/dist/tools/clawBrain.d.ts +0 -23
  83. package/dist/tools/clawBrain.js +0 -136
  84. package/dist/tools/claw_brain.d.ts +0 -23
  85. package/dist/tools/claw_brain.js +0 -139
  86. package/dist/tools/deleteFile.d.ts +0 -19
  87. package/dist/tools/deleteFile.js +0 -36
  88. package/dist/tools/delete_file.d.ts +0 -19
  89. package/dist/tools/delete_file.js +0 -36
  90. package/dist/tools/fileOps.d.ts +0 -22
  91. package/dist/tools/fileOps.js +0 -43
  92. package/dist/tools/file_ops.d.ts +0 -22
  93. package/dist/tools/file_ops.js +0 -43
  94. package/dist/tools/git.d.ts +0 -40
  95. package/dist/tools/git.js +0 -236
  96. package/dist/tools/glob.d.ts +0 -34
  97. package/dist/tools/glob.js +0 -165
  98. package/dist/tools/grep.d.ts +0 -53
  99. package/dist/tools/grep.js +0 -296
  100. package/dist/tools/linter.d.ts +0 -35
  101. package/dist/tools/linter.js +0 -407
  102. package/dist/tools/listDir.d.ts +0 -29
  103. package/dist/tools/listDir.js +0 -50
  104. package/dist/tools/list_dir.d.ts +0 -29
  105. package/dist/tools/list_dir.js +0 -50
  106. package/dist/tools/memory.d.ts +0 -34
  107. package/dist/tools/memory.js +0 -215
  108. package/dist/tools/organizer.d.ts +0 -1
  109. package/dist/tools/organizer.js +0 -65
  110. package/dist/tools/readFiles.d.ts +0 -25
  111. package/dist/tools/readFiles.js +0 -31
  112. package/dist/tools/read_files.d.ts +0 -25
  113. package/dist/tools/read_files.js +0 -31
  114. package/dist/tools/reloadTools.d.ts +0 -11
  115. package/dist/tools/reloadTools.js +0 -22
  116. package/dist/tools/reload_tools.d.ts +0 -11
  117. package/dist/tools/reload_tools.js +0 -22
  118. package/dist/tools/runCommand.d.ts +0 -32
  119. package/dist/tools/runCommand.js +0 -79
  120. package/dist/tools/run_command.d.ts +0 -32
  121. package/dist/tools/run_command.js +0 -103
  122. package/dist/tools/scheduler.d.ts +0 -25
  123. package/dist/tools/scheduler.js +0 -65
  124. package/dist/tools/scraper.d.ts +0 -31
  125. package/dist/tools/scraper.js +0 -211
  126. package/dist/tools/writeFiles.d.ts +0 -63
  127. package/dist/tools/writeFiles.js +0 -87
  128. package/dist/tools/write_files.d.ts +0 -84
  129. package/dist/tools/write_files.js +0 -91
  130. package/dist/tools/write_to_file.d.ts +0 -15
  131. package/dist/tools/write_to_file.js +0 -21
  132. package/dist/ui/server.d.ts +0 -5
  133. package/dist/ui/server.js +0 -74
  134. package/dist/watcher.d.ts +0 -35
  135. package/dist/watcher.js +0 -164
  136. /package/{docs/assets → assets}/logo.jpeg +0 -0
@@ -1,377 +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
- const res = await multiProvider.generateWithTier(1, prompt, [{ role: 'user', content: userMsg }]);
105
- return res.message || res.thought || res.raw || '';
106
- });
107
- return multiProvider.generateWithTier(routing.tier, fullPrompt, llmMessages);
108
- }
109
- return singleProvider.generateResponse(fullPrompt, llmMessages);
110
- };
111
- // Tool execution
112
- const executeTool = async (name, args) => {
113
- const tools = ctx.getTools();
114
- const tool = tools.get(name);
115
- if (!tool) {
116
- throw new Error(`Tool not found: ${name}`);
117
- }
118
- // Permission check
119
- if (!flags.yolo && tool.permission !== 'read') {
120
- const confirmed = await ui.confirm({
121
- message: `Execute ${name}?`,
122
- initialValue: false,
123
- });
124
- if (ui.isCancel(confirmed) || !confirmed) {
125
- return 'Cancelled by user';
126
- }
127
- }
128
- return tool.execute(args);
129
- };
130
- // Create agent
131
- const agent = new Agent({
132
- config: {
133
- maxReflections: 3,
134
- autoLint: flags['auto-lint'],
135
- autoTest: flags['auto-test'],
136
- autoCommit: flags['auto-commit'],
137
- testCommand: flags['test-cmd'],
138
- },
139
- git,
140
- generateFn: generate,
141
- executeTool,
142
- lintFn: flags['auto-lint'] ? async (file) => {
143
- const { execute } = await import('../tools/linter.js');
144
- const result = await execute({ path: file, fix: false });
145
- return { passed: result.passed, output: result.output };
146
- } : undefined,
147
- testFn: flags['auto-test'] && flags['test-cmd'] ? async () => {
148
- const { execute } = await import('../tools/run_command.js');
149
- const result = await execute({ command: flags['test-cmd'] });
150
- return { passed: result.exitCode === 0, output: result.stdout + result.stderr };
151
- } : undefined,
152
- });
153
- // Main loop
154
- while (true) {
155
- const currentSkill = getActiveSkill();
156
- const input = await ui.text({
157
- message: `[${currentSkill.name}]`,
158
- placeholder: 'Type a message or /help...',
159
- });
160
- if (ui.isCancel(input)) {
161
- break;
162
- }
163
- const message = input.trim();
164
- if (!message)
165
- continue;
166
- // Handle slash commands
167
- if (message.startsWith('/')) {
168
- await handleCommand(message, ctx, git, mcpManager);
169
- continue;
170
- }
171
- // Handle skill switching
172
- if (message.startsWith('@')) {
173
- const skillName = message.slice(1).trim();
174
- if (skillName === 'list') {
175
- ui.note(listSkills().map(s => `@${s.name} - ${s.description}`).join('\n'), 'Available Skills');
176
- }
177
- else {
178
- const newSkill = setActiveSkill(skillName);
179
- if (newSkill) {
180
- ctx.setSkill(newSkill);
181
- ui.success(`Switched to @${newSkill.name}`);
182
- }
183
- else {
184
- ui.error(`Unknown skill: @${skillName}`);
185
- }
186
- }
187
- continue;
188
- }
189
- // Add watch context if available
190
- let fullMessage = message;
191
- if (watcher) {
192
- const watchPrompt = watcher.getActionableCommentsPrompt();
193
- if (watchPrompt) {
194
- fullMessage = `${watchPrompt}\n\nUser request: ${message}`;
195
- }
196
- }
197
- // Process with agent
198
- try {
199
- ctx.addMessage('user', message);
200
- const history = ctx.getHistory();
201
- // Summarize if too long
202
- const summarizedHistory = await summarizeHistory(history, generate, 20);
203
- const systemPrompt = await ctx.buildSystemPrompt();
204
- const result = await agent.process(fullMessage, summarizedHistory, systemPrompt);
205
- // Add response to history
206
- if (result.response.action && 'message' in result.response.action) {
207
- ctx.addMessage('assistant', result.response.action.message);
208
- }
209
- }
210
- catch (error) {
211
- ui.error(`Error: ${error instanceof Error ? error.message : error}`);
212
- }
213
- }
214
- // Cleanup
215
- watcher?.stop();
216
- await mcpManager.disconnectAll();
217
- ui.outro('Goodbye!');
218
- }
219
- }
220
- /**
221
- * Handle slash commands
222
- */
223
- async function handleCommand(input, ctx, git, mcpManager) {
224
- const [cmd, ...args] = input.slice(1).split(/\s+/);
225
- const argsStr = args.join(' ');
226
- switch (cmd.toLowerCase()) {
227
- case 'help':
228
- case 'h':
229
- ui.note(`
230
- /add <file> - Add file to context
231
- /drop [file] - Remove file from context
232
- /ls - List files in context
233
- /clear - Clear chat history
234
- /diff - Show git diff
235
- /status - Show git status
236
- /commit [msg] - Commit changes
237
- /undo - Undo last commit
238
- /tokens - Show token estimate
239
- /mcp [cmd] - MCP management
240
- /skill <name> - Switch skill
241
- /exit - Exit
242
- `, 'Commands');
243
- break;
244
- case 'add':
245
- case 'a':
246
- if (argsStr) {
247
- const added = ctx.addFile(argsStr);
248
- if (added) {
249
- ui.success(`Added ${argsStr}`);
250
- }
251
- else {
252
- ui.error(`File not found: ${argsStr}`);
253
- }
254
- }
255
- else {
256
- ui.error('Usage: /add <file>');
257
- }
258
- break;
259
- case 'drop':
260
- case 'd':
261
- if (argsStr) {
262
- ctx.removeFile(argsStr);
263
- ui.success(`Dropped ${argsStr}`);
264
- }
265
- else {
266
- // Drop all
267
- const files = ctx.getFiles();
268
- for (const f of [...files.active, ...files.readOnly]) {
269
- ctx.removeFile(f);
270
- }
271
- ui.success('Dropped all files');
272
- }
273
- break;
274
- case 'ls':
275
- case 'files':
276
- const files = ctx.getFiles();
277
- if (files.active.length === 0 && files.readOnly.length === 0) {
278
- ui.log('No files in context');
279
- }
280
- else {
281
- ui.showFileStatus([
282
- ...files.active.map(f => ({ path: f, status: 'modified' })),
283
- ...files.readOnly.map(f => ({ path: f, status: 'readonly' })),
284
- ]);
285
- }
286
- break;
287
- case 'clear':
288
- ctx.clearHistory();
289
- ui.success('Chat history cleared');
290
- break;
291
- case 'diff':
292
- const diff = await git.diff();
293
- if (diff) {
294
- ui.showDiff(diff);
295
- }
296
- else {
297
- ui.log('No changes');
298
- }
299
- break;
300
- case 'status':
301
- const status = await git.status();
302
- ui.log(`Branch: ${status.current}`);
303
- if (status.modified.length)
304
- ui.log(`Modified: ${status.modified.join(', ')}`);
305
- if (status.created.length)
306
- ui.log(`Created: ${status.created.join(', ')}`);
307
- if (status.deleted.length)
308
- ui.log(`Deleted: ${status.deleted.join(', ')}`);
309
- if (status.not_added.length)
310
- ui.log(`Untracked: ${status.not_added.join(', ')}`);
311
- break;
312
- case 'commit':
313
- const commitDiff = await git.stagedDiff();
314
- if (!commitDiff && !(await git.status()).staged.length) {
315
- await git.addAll();
316
- }
317
- const result = await git.commit({ message: argsStr || 'Update' });
318
- if (result) {
319
- ui.success(`Committed: ${result.hash} ${result.message}`);
320
- }
321
- else {
322
- ui.error('Nothing to commit');
323
- }
324
- break;
325
- case 'undo':
326
- if (await git.undoLastCommit()) {
327
- ui.success('Undid last commit');
328
- }
329
- else {
330
- ui.error('Failed to undo');
331
- }
332
- break;
333
- case 'tokens':
334
- const tokens = await ctx.estimateTokenCount();
335
- ui.showTokens(tokens);
336
- break;
337
- case 'mcp':
338
- const subCmd = args[0];
339
- if (subCmd === 'status') {
340
- const statuses = mcpManager.getAllServerStatuses();
341
- for (const [name, status] of statuses) {
342
- ui.log(`${name}: ${status}`);
343
- }
344
- }
345
- else if (subCmd === 'tools') {
346
- const tools = mcpManager.getAllTools();
347
- for (const tool of tools) {
348
- ui.log(`${tool.name} (${tool.serverName}): ${tool.description}`);
349
- }
350
- }
351
- else {
352
- ui.log('Usage: /mcp [status|tools]');
353
- }
354
- break;
355
- case 'skill':
356
- if (argsStr) {
357
- const newSkill = setActiveSkill(argsStr);
358
- if (newSkill) {
359
- ctx.setSkill(newSkill);
360
- ui.success(`Switched to @${newSkill.name}`);
361
- }
362
- else {
363
- ui.error(`Unknown skill: ${argsStr}`);
364
- }
365
- }
366
- else {
367
- ui.note(listSkills().map(s => `@${s.name} - ${s.description}`).join('\n'), 'Available Skills');
368
- }
369
- break;
370
- case 'exit':
371
- case 'quit':
372
- case 'q':
373
- process.exit(0);
374
- default:
375
- ui.error(`Unknown command: /${cmd}`);
376
- }
377
- }
@@ -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
- }