@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,32 +0,0 @@
1
- /**
2
- * Slash Commands System
3
- * Based on Aider's commands.py
4
- */
5
- export interface CommandContext {
6
- cwd: string;
7
- activeFiles: Set<string>;
8
- readOnlyFiles: Set<string>;
9
- history: Array<{
10
- role: string;
11
- content: string;
12
- }>;
13
- io: {
14
- output: (message: string) => void;
15
- error: (message: string) => void;
16
- confirm: (message: string) => Promise<boolean>;
17
- prompt: (message: string) => Promise<string>;
18
- };
19
- }
20
- export interface Command {
21
- name: string;
22
- aliases: string[];
23
- description: string;
24
- execute: (args: string, context: CommandContext) => Promise<string | void>;
25
- }
26
- export declare function parseCommand(input: string): {
27
- command: string;
28
- args: string;
29
- } | null;
30
- export declare const commands: Command[];
31
- export declare function findCommand(name: string): Command | undefined;
32
- export declare function executeCommand(input: string, context: CommandContext): Promise<string | void>;
package/dist/commands.js DELETED
@@ -1,427 +0,0 @@
1
- /**
2
- * Slash Commands System
3
- * Based on Aider's commands.py
4
- */
5
- import { readFile, writeFile } from 'fs/promises';
6
- import { existsSync } from 'fs';
7
- import { relative, resolve } from 'path';
8
- import { getMCPManager } from './mcp/manager.js';
9
- import { getStagedDiff } from './tools/git.js';
10
- import { execute as grepExecute } from './tools/grep.js';
11
- import { execute as globExecute } from './tools/glob.js';
12
- // Parse command line into command and args
13
- export function parseCommand(input) {
14
- if (!input.startsWith('/')) {
15
- return null;
16
- }
17
- const trimmed = input.slice(1).trim();
18
- const spaceIndex = trimmed.indexOf(' ');
19
- if (spaceIndex === -1) {
20
- return { command: trimmed.toLowerCase(), args: '' };
21
- }
22
- return {
23
- command: trimmed.slice(0, spaceIndex).toLowerCase(),
24
- args: trimmed.slice(spaceIndex + 1).trim(),
25
- };
26
- }
27
- // Built-in commands
28
- export const commands = [
29
- // File Management
30
- {
31
- name: 'add',
32
- aliases: ['a'],
33
- description: 'Add files to the chat context',
34
- execute: async (args, ctx) => {
35
- if (!args) {
36
- ctx.io.output('Usage: /add <file_pattern>');
37
- return;
38
- }
39
- const patterns = args.split(/\s+/);
40
- let added = 0;
41
- for (const pattern of patterns) {
42
- // Check if it's a glob pattern
43
- if (pattern.includes('*')) {
44
- const result = await globExecute({ pattern, cwd: ctx.cwd, ignore: [], maxResults: 1000, includeDirectories: false });
45
- for (const file of result.matches) {
46
- const fullPath = resolve(ctx.cwd, file);
47
- if (!ctx.activeFiles.has(fullPath)) {
48
- ctx.activeFiles.add(fullPath);
49
- ctx.io.output(`Added ${file}`);
50
- added++;
51
- }
52
- }
53
- }
54
- else {
55
- const fullPath = resolve(ctx.cwd, pattern);
56
- if (existsSync(fullPath)) {
57
- if (!ctx.activeFiles.has(fullPath)) {
58
- ctx.activeFiles.add(fullPath);
59
- ctx.io.output(`Added ${pattern}`);
60
- added++;
61
- }
62
- }
63
- else {
64
- // File doesn't exist - offer to create it
65
- const create = await ctx.io.confirm(`File ${pattern} doesn't exist. Create it?`);
66
- if (create) {
67
- await writeFile(fullPath, '');
68
- ctx.activeFiles.add(fullPath);
69
- ctx.io.output(`Created and added ${pattern}`);
70
- added++;
71
- }
72
- }
73
- }
74
- }
75
- ctx.io.output(`Added ${added} file(s) to the chat`);
76
- },
77
- },
78
- {
79
- name: 'drop',
80
- aliases: ['d', 'remove'],
81
- description: 'Remove files from the chat context',
82
- execute: async (args, ctx) => {
83
- if (!args) {
84
- // Drop all files
85
- const count = ctx.activeFiles.size;
86
- ctx.activeFiles.clear();
87
- ctx.io.output(`Dropped all ${count} file(s)`);
88
- return;
89
- }
90
- const patterns = args.split(/\s+/);
91
- let dropped = 0;
92
- for (const pattern of patterns) {
93
- const fullPath = resolve(ctx.cwd, pattern);
94
- if (ctx.activeFiles.has(fullPath)) {
95
- ctx.activeFiles.delete(fullPath);
96
- ctx.io.output(`Dropped ${pattern}`);
97
- dropped++;
98
- }
99
- }
100
- ctx.io.output(`Dropped ${dropped} file(s)`);
101
- },
102
- },
103
- {
104
- name: 'ls',
105
- aliases: ['files', 'list'],
106
- description: 'List files in the chat context',
107
- execute: async (args, ctx) => {
108
- if (ctx.activeFiles.size === 0 && ctx.readOnlyFiles.size === 0) {
109
- ctx.io.output('No files in chat context');
110
- return;
111
- }
112
- if (ctx.activeFiles.size > 0) {
113
- ctx.io.output('\nEditable files:');
114
- for (const file of ctx.activeFiles) {
115
- ctx.io.output(` ${relative(ctx.cwd, file)}`);
116
- }
117
- }
118
- if (ctx.readOnlyFiles.size > 0) {
119
- ctx.io.output('\nRead-only files:');
120
- for (const file of ctx.readOnlyFiles) {
121
- ctx.io.output(` ${relative(ctx.cwd, file)}`);
122
- }
123
- }
124
- },
125
- },
126
- {
127
- name: 'read-only',
128
- aliases: ['ro'],
129
- description: 'Add files as read-only context',
130
- execute: async (args, ctx) => {
131
- if (!args) {
132
- // Convert all active files to read-only
133
- for (const file of ctx.activeFiles) {
134
- ctx.readOnlyFiles.add(file);
135
- }
136
- ctx.activeFiles.clear();
137
- ctx.io.output('Converted all files to read-only');
138
- return;
139
- }
140
- const patterns = args.split(/\s+/);
141
- for (const pattern of patterns) {
142
- if (pattern.includes('*')) {
143
- const result = await globExecute({ pattern, cwd: ctx.cwd, maxResults: 1000, includeDirectories: false });
144
- for (const file of result.matches) {
145
- const fullPath = resolve(ctx.cwd, file);
146
- ctx.readOnlyFiles.add(fullPath);
147
- ctx.io.output(`Added ${file} as read-only`);
148
- }
149
- }
150
- else {
151
- const fullPath = resolve(ctx.cwd, pattern);
152
- if (existsSync(fullPath)) {
153
- ctx.readOnlyFiles.add(fullPath);
154
- ctx.io.output(`Added ${pattern} as read-only`);
155
- }
156
- }
157
- }
158
- },
159
- },
160
- // Git Integration
161
- {
162
- name: 'git',
163
- aliases: [],
164
- description: 'Run a git command',
165
- execute: async (args, ctx) => {
166
- const { spawnSync } = await import('child_process');
167
- const result = spawnSync('git', args.split(/\s+/), {
168
- cwd: ctx.cwd,
169
- encoding: 'utf-8',
170
- });
171
- if (result.stdout)
172
- ctx.io.output(result.stdout);
173
- if (result.stderr)
174
- ctx.io.error(result.stderr);
175
- },
176
- },
177
- {
178
- name: 'diff',
179
- aliases: [],
180
- description: 'Show git diff of changes',
181
- execute: async (args, ctx) => {
182
- const { spawnSync } = await import('child_process');
183
- const gitArgs = args ? args.split(/\s+/) : [];
184
- const result = spawnSync('git', ['diff', '--no-color', ...gitArgs], {
185
- cwd: ctx.cwd,
186
- encoding: 'utf-8',
187
- });
188
- if (result.stdout) {
189
- ctx.io.output(result.stdout);
190
- }
191
- else {
192
- ctx.io.output('No changes');
193
- }
194
- },
195
- },
196
- {
197
- name: 'commit',
198
- aliases: [],
199
- description: 'Commit staged changes with AI-generated message',
200
- execute: async (args, ctx) => {
201
- const diff = getStagedDiff(ctx.cwd);
202
- if (!diff) {
203
- ctx.io.output('No staged changes to commit');
204
- return;
205
- }
206
- const message = args || 'Update files';
207
- const { spawnSync } = await import('child_process');
208
- const result = spawnSync('git', ['commit', '-m', message], {
209
- cwd: ctx.cwd,
210
- encoding: 'utf-8',
211
- });
212
- if (result.stdout)
213
- ctx.io.output(result.stdout);
214
- if (result.stderr)
215
- ctx.io.error(result.stderr);
216
- },
217
- },
218
- // Search
219
- {
220
- name: 'search',
221
- aliases: ['grep', 'find'],
222
- description: 'Search for pattern in files',
223
- execute: async (args, ctx) => {
224
- if (!args) {
225
- ctx.io.output('Usage: /search <pattern> [path]');
226
- return;
227
- }
228
- const parts = args.split(/\s+/);
229
- const pattern = parts[0];
230
- const path = parts[1] || ctx.cwd;
231
- const result = await grepExecute({
232
- pattern,
233
- path,
234
- maxResults: 50,
235
- ignoreCase: true,
236
- contextLines: 2,
237
- filesOnly: false,
238
- includeHidden: false,
239
- });
240
- if (result.matches.length === 0) {
241
- ctx.io.output('No matches found');
242
- return;
243
- }
244
- ctx.io.output(`Found ${result.count} matches in ${result.files.length} file(s):`);
245
- for (const match of result.matches.slice(0, 20)) {
246
- ctx.io.output(` ${match.file}:${match.line}: ${match.text.trim()}`);
247
- }
248
- if (result.truncated) {
249
- ctx.io.output(` ... and more (truncated)`);
250
- }
251
- },
252
- },
253
- // Chat Management
254
- {
255
- name: 'clear',
256
- aliases: ['reset'],
257
- description: 'Clear chat history',
258
- execute: async (args, ctx) => {
259
- ctx.history.length = 0;
260
- ctx.io.output('Chat history cleared');
261
- },
262
- },
263
- {
264
- name: 'undo',
265
- aliases: [],
266
- description: 'Undo the last git commit made by the AI',
267
- execute: async (args, ctx) => {
268
- const { spawnSync } = await import('child_process');
269
- const result = spawnSync('git', ['reset', '--soft', 'HEAD~1'], {
270
- cwd: ctx.cwd,
271
- encoding: 'utf-8',
272
- });
273
- if (result.status === 0) {
274
- ctx.io.output('Undid last commit');
275
- }
276
- else {
277
- ctx.io.error('Failed to undo commit');
278
- }
279
- },
280
- },
281
- // Context
282
- {
283
- name: 'tokens',
284
- aliases: [],
285
- description: 'Show approximate token count',
286
- execute: async (args, ctx) => {
287
- let totalChars = 0;
288
- for (const file of ctx.activeFiles) {
289
- try {
290
- const content = await readFile(file, 'utf-8');
291
- totalChars += content.length;
292
- }
293
- catch { }
294
- }
295
- for (const file of ctx.readOnlyFiles) {
296
- try {
297
- const content = await readFile(file, 'utf-8');
298
- totalChars += content.length;
299
- }
300
- catch { }
301
- }
302
- for (const msg of ctx.history) {
303
- totalChars += msg.content.length;
304
- }
305
- // Rough estimate: ~4 chars per token
306
- const estimatedTokens = Math.ceil(totalChars / 4);
307
- ctx.io.output(`Approximate tokens: ${estimatedTokens.toLocaleString()}`);
308
- ctx.io.output(` Files: ${ctx.activeFiles.size} editable, ${ctx.readOnlyFiles.size} read-only`);
309
- ctx.io.output(` History: ${ctx.history.length} messages`);
310
- },
311
- },
312
- // MCP
313
- {
314
- name: 'mcp',
315
- aliases: [],
316
- description: 'MCP server management',
317
- execute: async (args, ctx) => {
318
- const manager = getMCPManager();
319
- const parts = args.split(/\s+/);
320
- const subcommand = parts[0] || 'status';
321
- switch (subcommand) {
322
- case 'status': {
323
- const statuses = manager.getAllServerStatuses();
324
- if (statuses.size === 0) {
325
- ctx.io.output('No MCP servers configured');
326
- return;
327
- }
328
- ctx.io.output('MCP Servers:');
329
- for (const [name, status] of statuses) {
330
- ctx.io.output(` ${name}: ${status}`);
331
- }
332
- break;
333
- }
334
- case 'tools': {
335
- const tools = manager.getAllTools();
336
- if (tools.length === 0) {
337
- ctx.io.output('No MCP tools available');
338
- return;
339
- }
340
- ctx.io.output(`MCP Tools (${tools.length}):`);
341
- for (const tool of tools) {
342
- ctx.io.output(` ${tool.name} (${tool.serverName}): ${tool.description}`);
343
- }
344
- break;
345
- }
346
- case 'connect': {
347
- await manager.connectAll();
348
- ctx.io.output('Connected to MCP servers');
349
- break;
350
- }
351
- case 'disconnect': {
352
- await manager.disconnectAll();
353
- ctx.io.output('Disconnected from MCP servers');
354
- break;
355
- }
356
- default:
357
- ctx.io.output('Usage: /mcp [status|tools|connect|disconnect]');
358
- }
359
- },
360
- },
361
- // Web
362
- {
363
- name: 'web',
364
- aliases: ['url', 'fetch'],
365
- description: 'Fetch a URL and add to context',
366
- execute: async (args, ctx) => {
367
- if (!args) {
368
- ctx.io.output('Usage: /web <url>');
369
- return;
370
- }
371
- const { execute: scrapeExecute } = await import('./tools/scraper.js');
372
- const result = await scrapeExecute({ url: args, convertToMarkdown: true, verifySSL: true, timeout: 10000 });
373
- if (result.error) {
374
- ctx.io.error(`Failed to fetch: ${result.error}`);
375
- return;
376
- }
377
- ctx.io.output(`Fetched ${args} (${result.content.length} chars)`);
378
- // Add to history as context
379
- ctx.history.push({
380
- role: 'user',
381
- content: `Content from ${args}:\n\n${result.content}`,
382
- });
383
- },
384
- },
385
- // Help
386
- {
387
- name: 'help',
388
- aliases: ['h', '?'],
389
- description: 'Show available commands',
390
- execute: async (args, ctx) => {
391
- ctx.io.output('\nAvailable commands:\n');
392
- for (const cmd of commands) {
393
- const aliases = cmd.aliases.length > 0 ? ` (${cmd.aliases.join(', ')})` : '';
394
- ctx.io.output(` /${cmd.name}${aliases}`);
395
- ctx.io.output(` ${cmd.description}\n`);
396
- }
397
- },
398
- },
399
- // Exit
400
- {
401
- name: 'exit',
402
- aliases: ['quit', 'q'],
403
- description: 'Exit the CLI',
404
- execute: async (args, ctx) => {
405
- ctx.io.output('Goodbye!');
406
- process.exit(0);
407
- },
408
- },
409
- ];
410
- // Find command by name or alias
411
- export function findCommand(name) {
412
- const lower = name.toLowerCase();
413
- return commands.find(cmd => cmd.name === lower || cmd.aliases.includes(lower));
414
- }
415
- // Execute a command
416
- export async function executeCommand(input, context) {
417
- const parsed = parseCommand(input);
418
- if (!parsed) {
419
- return undefined; // Not a command
420
- }
421
- const command = findCommand(parsed.command);
422
- if (!command) {
423
- context.io.error(`Unknown command: /${parsed.command}. Type /help for available commands.`);
424
- return;
425
- }
426
- return command.execute(parsed.args, context);
427
- }
package/dist/context.d.ts DELETED
@@ -1,116 +0,0 @@
1
- /**
2
- * Context Manager - Manages conversation context and file state
3
- * Based on Aider's coder.py and GeminiCLI's context management
4
- * Uses gpt-tokenizer for accurate token counting (with fallback)
5
- */
6
- import { type Skill } from './skills.js';
7
- import { type Tool } from './registry.js';
8
- export interface Message {
9
- role: 'user' | 'assistant' | 'system';
10
- content: string;
11
- timestamp?: number;
12
- }
13
- export interface ContextState {
14
- cwd: string;
15
- activeFiles: Set<string>;
16
- readOnlyFiles: Set<string>;
17
- history: Message[];
18
- skill: Skill;
19
- tokenEstimate: number;
20
- }
21
- /**
22
- * Synchronous token count (uses cached encoder or estimation)
23
- */
24
- declare function countTokens(text: string): number;
25
- /**
26
- * ContextManager class
27
- * Manages the conversation context, file state, and system prompts
28
- */
29
- export declare class ContextManager {
30
- private cwd;
31
- private activeFiles;
32
- private readOnlyFiles;
33
- private history;
34
- private skill;
35
- private tools;
36
- private repoMapCache;
37
- private repoMapTimestamp;
38
- constructor(cwd?: string);
39
- /**
40
- * Initialize the context manager
41
- */
42
- initialize(): Promise<void>;
43
- /**
44
- * Add a file to active context
45
- */
46
- addFile(path: string, readOnly?: boolean): boolean;
47
- /**
48
- * Remove a file from context
49
- */
50
- removeFile(path: string): boolean;
51
- /**
52
- * Add a message to history
53
- */
54
- addMessage(role: Message['role'], content: string): void;
55
- /**
56
- * Clear conversation history
57
- */
58
- clearHistory(): void;
59
- /**
60
- * Get all files in context
61
- */
62
- getFiles(): {
63
- active: string[];
64
- readOnly: string[];
65
- };
66
- /**
67
- * Read all files in context
68
- */
69
- getFileContents(): Promise<Map<string, string>>;
70
- /**
71
- * Set the active skill
72
- */
73
- setSkill(skill: Skill): void;
74
- /**
75
- * Get the current skill
76
- */
77
- getSkill(): Skill;
78
- /**
79
- * Refresh the repository map
80
- */
81
- refreshRepoMap(): Promise<string>;
82
- /**
83
- * Build the system prompt
84
- */
85
- buildSystemPrompt(): Promise<string>;
86
- /**
87
- * Build messages for LLM
88
- */
89
- buildMessages(userMessage: string): Promise<Message[]>;
90
- /**
91
- * Estimate token count using accurate tokenizer
92
- */
93
- estimateTokenCount(): Promise<number>;
94
- /**
95
- * Get current state
96
- */
97
- getState(): ContextState;
98
- /**
99
- * Restore state
100
- */
101
- restoreState(state: Partial<ContextState>): void;
102
- /**
103
- * Get conversation history
104
- */
105
- getHistory(): Message[];
106
- /**
107
- * Get tools
108
- */
109
- getTools(): Map<string, Tool>;
110
- /**
111
- * Get working directory
112
- */
113
- getCwd(): string;
114
- }
115
- export declare function getContextManager(cwd?: string): ContextManager;
116
- export { countTokens };