@stan-chen/simple-cli 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +287 -0
  2. package/dist/cli.d.ts +6 -0
  3. package/dist/cli.js +259 -0
  4. package/dist/commands/add.d.ts +9 -0
  5. package/dist/commands/add.js +50 -0
  6. package/dist/commands/git/commit.d.ts +12 -0
  7. package/dist/commands/git/commit.js +97 -0
  8. package/dist/commands/git/status.d.ts +6 -0
  9. package/dist/commands/git/status.js +42 -0
  10. package/dist/commands/index.d.ts +16 -0
  11. package/dist/commands/index.js +376 -0
  12. package/dist/commands/mcp/status.d.ts +6 -0
  13. package/dist/commands/mcp/status.js +31 -0
  14. package/dist/commands/swarm.d.ts +36 -0
  15. package/dist/commands/swarm.js +236 -0
  16. package/dist/commands.d.ts +32 -0
  17. package/dist/commands.js +427 -0
  18. package/dist/context.d.ts +116 -0
  19. package/dist/context.js +327 -0
  20. package/dist/index.d.ts +6 -0
  21. package/dist/index.js +109 -0
  22. package/dist/lib/agent.d.ts +98 -0
  23. package/dist/lib/agent.js +281 -0
  24. package/dist/lib/editor.d.ts +74 -0
  25. package/dist/lib/editor.js +441 -0
  26. package/dist/lib/git.d.ts +164 -0
  27. package/dist/lib/git.js +351 -0
  28. package/dist/lib/ui.d.ts +159 -0
  29. package/dist/lib/ui.js +252 -0
  30. package/dist/mcp/client.d.ts +22 -0
  31. package/dist/mcp/client.js +81 -0
  32. package/dist/mcp/manager.d.ts +186 -0
  33. package/dist/mcp/manager.js +442 -0
  34. package/dist/prompts/provider.d.ts +22 -0
  35. package/dist/prompts/provider.js +78 -0
  36. package/dist/providers/index.d.ts +15 -0
  37. package/dist/providers/index.js +82 -0
  38. package/dist/providers/multi.d.ts +11 -0
  39. package/dist/providers/multi.js +28 -0
  40. package/dist/registry.d.ts +24 -0
  41. package/dist/registry.js +379 -0
  42. package/dist/repoMap.d.ts +5 -0
  43. package/dist/repoMap.js +79 -0
  44. package/dist/router.d.ts +41 -0
  45. package/dist/router.js +108 -0
  46. package/dist/skills.d.ts +25 -0
  47. package/dist/skills.js +288 -0
  48. package/dist/swarm/coordinator.d.ts +86 -0
  49. package/dist/swarm/coordinator.js +257 -0
  50. package/dist/swarm/index.d.ts +28 -0
  51. package/dist/swarm/index.js +29 -0
  52. package/dist/swarm/task.d.ts +104 -0
  53. package/dist/swarm/task.js +221 -0
  54. package/dist/swarm/types.d.ts +132 -0
  55. package/dist/swarm/types.js +37 -0
  56. package/dist/swarm/worker.d.ts +107 -0
  57. package/dist/swarm/worker.js +299 -0
  58. package/dist/tools/analyzeFile.d.ts +16 -0
  59. package/dist/tools/analyzeFile.js +43 -0
  60. package/dist/tools/git.d.ts +40 -0
  61. package/dist/tools/git.js +236 -0
  62. package/dist/tools/glob.d.ts +34 -0
  63. package/dist/tools/glob.js +165 -0
  64. package/dist/tools/grep.d.ts +53 -0
  65. package/dist/tools/grep.js +296 -0
  66. package/dist/tools/linter.d.ts +35 -0
  67. package/dist/tools/linter.js +349 -0
  68. package/dist/tools/listDir.d.ts +29 -0
  69. package/dist/tools/listDir.js +50 -0
  70. package/dist/tools/memory.d.ts +34 -0
  71. package/dist/tools/memory.js +215 -0
  72. package/dist/tools/readFiles.d.ts +25 -0
  73. package/dist/tools/readFiles.js +31 -0
  74. package/dist/tools/reloadTools.d.ts +11 -0
  75. package/dist/tools/reloadTools.js +22 -0
  76. package/dist/tools/runCommand.d.ts +32 -0
  77. package/dist/tools/runCommand.js +79 -0
  78. package/dist/tools/scraper.d.ts +31 -0
  79. package/dist/tools/scraper.js +211 -0
  80. package/dist/tools/writeFiles.d.ts +63 -0
  81. package/dist/tools/writeFiles.js +87 -0
  82. package/dist/ui/server.d.ts +5 -0
  83. package/dist/ui/server.js +74 -0
  84. package/dist/watcher.d.ts +35 -0
  85. package/dist/watcher.js +164 -0
  86. package/docs/assets/logo.jpeg +0 -0
  87. package/package.json +78 -0
package/README.md ADDED
@@ -0,0 +1,287 @@
1
+ # Simple-CLI ⚡
2
+
3
+ > **The terminal-native AI coding assistant that shapes itself to your task.**
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@stan-chen/simple-cli)](https://www.npmjs.com/package/@stan-chen/simple-cli)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
+
8
+ Simple-CLI is a **lean, autonomous AI agent** that lives in your terminal. Unlike bloated IDEs, it's built for speed, horizontal scalability, and intelligent task execution.
9
+
10
+ ```bash
11
+ # Interactive mode (chat loop)
12
+ simple
13
+
14
+ # One-shot execution
15
+ simple "Add TypeScript to this project"
16
+
17
+ # OpenClaw agent mode (auto-schedules background tasks)
18
+ simple --claw "Delete trash emails every hour"
19
+ ```
20
+
21
+ ---
22
+
23
+ ## Why Simple-CLI?
24
+
25
+ ⚡ **Terminal-First** - No Electron, no overhead, pure speed
26
+ � **Autonomous Execution** - Multi-step reasoning with tool usage
27
+ 🌊 **Swarm Mode** - Horizontally scale with distributed orchestration
28
+ 🔌 **Multi-Provider** - OpenAI, Anthropic, LiteLLM - switch instantly
29
+
30
+ **Advanced (OpenClaw Integration):**
31
+ 🧬 JIT Agent Generation - Task-specific personas via LLM
32
+ 🧠 Autonomous Memory - Persistent context across sessions
33
+ 👻 Ghost Mode - Background task scheduling
34
+
35
+ ---
36
+
37
+ ## Instant Setup
38
+
39
+ ```bash
40
+ # Install
41
+ npm install -g @stan-chen/simple-cli
42
+
43
+ # Configure
44
+ export OPENAI_API_KEY="sk-..."
45
+
46
+ # Start coding
47
+ simple
48
+ ```
49
+
50
+ **That's it.** It launches an interactive terminal session where you can:
51
+ - Ask questions about your codebase
52
+ - Request code changes
53
+ - Run commands and see results
54
+ - Let the agent iterate autonomously
55
+
56
+ **Optional:** Use `simple --claw "intent"` for OpenClaw JIT mode.
57
+
58
+ ---
59
+
60
+ ## Three Ways to Use
61
+
62
+ ### 1. Interactive Mode
63
+ ```bash
64
+ simple
65
+ ```
66
+ Launch a chat session where you can ask questions, request changes, and see the agent iterate through multi-step tasks autonomously.
67
+
68
+ ### 2. One-Shot Execution
69
+ ```bash
70
+ simple "Convert this Express app to Fastify"
71
+ ```
72
+ Execute a single task and exit. Perfect for scripting or quick one-off commands.
73
+
74
+ ### 3. OpenClaw Agent Mode
75
+ ```bash
76
+ simple --claw "Delete trash emails every hour"
77
+ ```
78
+ Runs in OpenClaw-compatible environments with full access to skills, memory, and scheduling. The agent can:
79
+ - Generate specialized personas (JIT)
80
+ - Use OpenClaw skills from `skills/` directory
81
+ - **Automatically schedule recurring tasks** (e.g., "every hour" → creates ghost task)
82
+ - Persist memory across sessions
83
+
84
+ When you use `--claw`, the agent intelligently determines if your task should run:
85
+ - **Once** (immediate execution)
86
+ - **Recurring** (auto-creates scheduled background task)
87
+
88
+ ---
89
+
90
+ ## Advanced: OpenClaw Integration
91
+
92
+ Want specialized agents? Enable OpenClaw features with `--claw`.
93
+
94
+ ### 🎯 Task-Optimized Agents
95
+
96
+ ```bash
97
+ simple --claw "Migrate Express to Fastify"
98
+ ```
99
+
100
+ This doesn't just "chat" - it **generates a specialized AI persona** via LLM:
101
+ - Expert migration strategist
102
+ - Framework-specific constraints
103
+ - Best practices for the exact task
104
+
105
+ Then you work with *that* agent, not a generic assistant.
106
+
107
+ ### 🧠 Persistent Memory
108
+
109
+ Your agent builds knowledge over time:
110
+ ```
111
+ .simple/workdir/memory/
112
+ ├── notes/ # Session summaries
113
+ ├── reflections/ # What it learned
114
+ ├── logs/ # Full execution history
115
+ └── graph/ # Knowledge connections
116
+ ```
117
+
118
+ When you return, it **remembers**. Logs auto-prune and archive.
119
+
120
+ ### 👻 Background Execution
121
+
122
+ ```bash
123
+ # Schedule a recurring security check
124
+ npx tsx tools/claw.ts run clawGhost \
125
+ action=schedule \
126
+ intent="Scan for CVEs" \
127
+ cron="0 9 * * 1" # Every Monday 9am
128
+ ```
129
+
130
+ Uses **real OS schedulers** (Windows Task Scheduler / crontab) - not polling loops.
131
+
132
+ ### 🌊 Swarm for Scale
133
+
134
+ ```bash
135
+ simple --swarm tasks.json --concurrency 5
136
+ ```
137
+
138
+ Distribute tasks across isolated Git worktrees:
139
+ - File-level locking
140
+ - Conflict-free merges
141
+ - Observable task queue
142
+ - Works on local machines or CI/CD
143
+
144
+ ---
145
+
146
+ ## Core Features
147
+
148
+ | Feature | Description |
149
+ |---------|-------------|
150
+ | **Multi-Provider** | OpenAI, Anthropic, LiteLLM - switch with `--moe` |
151
+ | **MCP Integration** | Model Context Protocol for external data sources |
152
+ | **Skills System** | Extensible via `SKILL.md` manifests |
153
+ | **Git Worktree Isolation** | Swarm agents work in separate branches |
154
+ | **Auto-Pruning Memory** | Keeps last 50 logs, archives the rest |
155
+ | **YOLO Mode** | `--yolo` for unattended execution |
156
+
157
+ ---
158
+
159
+ ## Real-World Examples
160
+
161
+ ### Native Usage (Modes 1 & 2)
162
+
163
+ ```bash
164
+ # Interactive exploration
165
+ simple
166
+ → "What database does this app use?"
167
+ → "Add input validation to the user registration endpoint"
168
+
169
+ # One-shot tasks
170
+ simple "Add TypeScript strict mode and fix all errors"
171
+ simple "Generate OpenAPI docs from my Express routes"
172
+ simple "Refactor to use async/await instead of callbacks"
173
+ ```
174
+
175
+ ### OpenClaw Mode (Mode 3)
176
+
177
+ ```bash
178
+ # Immediate execution with specialized agent
179
+ simple --claw "Audit this React app for security vulnerabilities"
180
+ simple --claw "Migrate from Vue 2 to Vue 3"
181
+
182
+ # Auto-scheduled recurring tasks
183
+ simple --claw "Check for npm vulnerabilities every day at 9am"
184
+ simple --claw "Delete old log files every week"
185
+ simple --claw "Run integration tests hourly"
186
+ ```
187
+
188
+ When you provide time-based language ("every hour", "daily", etc.), the `--claw` mode **automatically:**
189
+ 1. Generates a specialized agent persona for the task
190
+ 2. Creates a scheduled background task (Ghost Mode)
191
+ 3. Registers it with your OS scheduler (crontab / Task Scheduler)
192
+
193
+ ---
194
+
195
+ ## Architecture
196
+
197
+ **Zero Core Disruption** - Everything is modular:
198
+
199
+ ```
200
+ simple-cli/
201
+ ├── src/ # Core agent logic
202
+ ├── tools/ # Discoverable tool primitives
203
+ ├── skills/ # OpenClaw-compatible skill packs
204
+ │ ├── claw-jit/ # JIT persona generation
205
+ │ ├── claw-brain/ # Memory management
206
+ │ └── claw-ghost/ # Task scheduling
207
+ └── .simple/ # Your workspace state
208
+ ├── AGENT.md # Generated persona
209
+ └── workdir/ # Memory & artifacts
210
+ ```
211
+
212
+ Built with the **Adapter Pattern** - add features without touching core.
213
+
214
+ ---
215
+
216
+ ## Advanced
217
+
218
+ ### Environment Variables
219
+ ```bash
220
+ OPENAI_API_KEY=sk-... # Primary LLM
221
+ CLAW_MODEL=gpt-4 # Model selection
222
+ LITELLM_BASE_URL=... # Proxy support
223
+ DEBUG=true # Verbose logging
224
+ ```
225
+
226
+ ### MOE (Mixture of Experts)
227
+ ```bash
228
+ simple --moe # Routes tasks to tier-appropriate models
229
+ ```
230
+
231
+ ### Swarm Configuration
232
+ ```json
233
+ {
234
+ "tasks": [
235
+ {"file": "src/auth.ts", "instruction": "Add 2FA"},
236
+ {"file": "src/api.ts", "instruction": "Add rate limiting"}
237
+ ],
238
+ "concurrency": 3
239
+ }
240
+ ```
241
+
242
+ ---
243
+
244
+ ## Documentation
245
+
246
+ - 📖 [Full Docs](docs/index.md) - Architecture, workflows, customization
247
+ - 🧬 [OpenClaw Integration](docs/claw-integration.md) - JIT, Memory, Ghost Mode
248
+ - 🌊 [Swarm Guide](docs/swarm.md) - Distributed task orchestration
249
+ - 🛠️ [Custom Skills](docs/custom-skills.md) - Build your own
250
+
251
+ ---
252
+
253
+ ## Contributing
254
+
255
+ PRs welcome! See [CONTRIBUTING.md](CONTRIBUTING.md).
256
+
257
+ **Core Principles:**
258
+ - **No bloat** - Every feature must justify its existence
259
+ - **Agent-first** - Tools serve the agent, not the UI
260
+ - **Horizontal scale** - Features should work in swarm mode
261
+ - **Zero lock-in** - Portable configs, standard formats
262
+
263
+ ---
264
+
265
+ ## License
266
+
267
+ MIT © [Stan Chen](https://github.com/stancsz)
268
+
269
+ ---
270
+
271
+ ## Acknowledgments
272
+
273
+ Built with inspiration from:
274
+ - **Gemini CLI** - Multi-provider architecture
275
+ - **OpenClaw** - Skill system design
276
+ - **Cursor/Aider** - Agentic coding patterns
277
+
278
+ Powered by:
279
+ - [@clack/prompts](https://github.com/natemoo-re/clack) - Beautiful TUI
280
+ - [LiteLLM](https://github.com/BerriAI/litellm) - Universal LLM proxy
281
+
282
+ ---
283
+
284
+ <p align="center">
285
+ <strong>Stop configuring. Start building.</strong><br>
286
+ <code>simple "Your next big idea"</code>
287
+ </p>
package/dist/cli.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Simple-CLI - Premium TUI agentic coding assistant
4
+ * Powered by @clack/prompts.
5
+ */
6
+ import 'dotenv/config';
package/dist/cli.js ADDED
@@ -0,0 +1,259 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Simple-CLI - Premium TUI agentic coding assistant
4
+ * Powered by @clack/prompts.
5
+ */
6
+ import 'dotenv/config';
7
+ import { text, confirm as clackConfirm, isCancel, select } from '@clack/prompts';
8
+ import pc from 'picocolors';
9
+ import { getContextManager } from './context.js';
10
+ import { createProvider } from './providers/index.js';
11
+ import { createMultiProvider } from './providers/multi.js';
12
+ import { routeTask, loadTierConfig } from './router.js';
13
+ import { executeCommand } from './commands.js';
14
+ import { getMCPManager } from './mcp/manager.js';
15
+ import { listSkills, setActiveSkill, getActiveSkill } from './skills.js';
16
+ import { statSync } from 'fs';
17
+ import { resolve } from 'path';
18
+ import { runSwarm, parseSwarmArgs, printSwarmHelp } from './commands/swarm.js';
19
+ import { jsonrepair } from 'jsonrepair';
20
+ // CLI flags
21
+ const YOLO_MODE = process.argv.includes('--yolo');
22
+ const MOE_MODE = process.argv.includes('--moe');
23
+ const SWARM_MODE = process.argv.includes('--swarm');
24
+ const CLAW_MODE = process.argv.includes('--claw') || process.argv.includes('-claw');
25
+ const DEBUG = process.argv.includes('--debug') || process.env.DEBUG === 'true';
26
+ const VERSION = '0.2.0';
27
+ // Handle --claw mode (JIT Agent Generation)
28
+ if (CLAW_MODE) {
29
+ const { execSync } = await import('child_process');
30
+ const args = process.argv.slice(2).filter(a => !a.startsWith('-'));
31
+ const intent = args.join(' ') || 'unspecified task';
32
+ console.log(pc.cyan('🧬 Initiating JIT Agent Generation...'));
33
+ console.log(pc.dim(`Intent: "${intent}"`));
34
+ try {
35
+ const output = execSync(`npx tsx tools/claw.ts run clawJit intent="${intent}"`, {
36
+ cwd: process.cwd(),
37
+ encoding: 'utf-8',
38
+ stdio: 'inherit'
39
+ });
40
+ console.log(pc.green('\n✅ JIT Agent ready. Run `simple` to begin.'));
41
+ process.exit(0);
42
+ }
43
+ catch (error) {
44
+ console.error(pc.red('❌ Failed to initialize Claw mode:'), error);
45
+ process.exit(1);
46
+ }
47
+ }
48
+ // Handle --swarm mode
49
+ if (SWARM_MODE) {
50
+ if (process.argv.includes('--help')) {
51
+ printSwarmHelp();
52
+ process.exit(0);
53
+ }
54
+ const swarmOptions = parseSwarmArgs(process.argv.slice(2));
55
+ swarmOptions.yolo = swarmOptions.yolo || YOLO_MODE;
56
+ runSwarm(swarmOptions).catch(err => {
57
+ console.error('Swarm error:', err);
58
+ process.exit(1);
59
+ });
60
+ }
61
+ else {
62
+ main().catch(console.error);
63
+ }
64
+ function parseResponse(response) {
65
+ const thought = response.match(/<thought>([\s\S]*?)<\/thought>/)?.[1]?.trim() || '';
66
+ const jsonMatch = response.match(/\{[\s\S]*"tool"[\s\S]*\}/);
67
+ let action = { tool: 'none', message: '', args: {} };
68
+ if (jsonMatch) {
69
+ try {
70
+ action = JSON.parse(jsonrepair(jsonMatch[0]));
71
+ }
72
+ catch { /* skip */ }
73
+ }
74
+ return { thought, action };
75
+ }
76
+ async function confirm(tool, args, ctx) {
77
+ if (YOLO_MODE)
78
+ return true;
79
+ const t = ctx.getTools().get(tool);
80
+ if (!t || t.permission === 'read')
81
+ return true;
82
+ const confirmed = await clackConfirm({
83
+ message: `Allow ${pc.cyan(tool)} with args ${pc.dim(JSON.stringify(args))}?`,
84
+ initialValue: true,
85
+ });
86
+ return !isCancel(confirmed) && confirmed;
87
+ }
88
+ async function executeTool(name, args, ctx) {
89
+ const tool = ctx.getTools().get(name);
90
+ if (!tool)
91
+ return `Error: Tool "${name}" not found`;
92
+ try {
93
+ const result = await tool.execute(args);
94
+ return typeof result === 'string' ? result : JSON.stringify(result, null, 2);
95
+ }
96
+ catch (error) {
97
+ return `Error: ${error instanceof Error ? error.message : error}`;
98
+ }
99
+ }
100
+ async function main() {
101
+ console.clear();
102
+ const args = process.argv.slice(2).filter(arg => !arg.startsWith('-'));
103
+ let targetDir = process.cwd();
104
+ if (args.length > 0) {
105
+ try {
106
+ if (statSync(args[0]).isDirectory()) {
107
+ targetDir = resolve(args[0]);
108
+ process.chdir(targetDir);
109
+ // Reload .env from the new directory
110
+ const { config } = await import('dotenv');
111
+ config();
112
+ args.shift();
113
+ }
114
+ }
115
+ catch { /* ignored */ }
116
+ }
117
+ console.log(`\n ${pc.bgCyan(pc.black(' SIMPLE-CLI '))} ${pc.dim(`v${VERSION}`)} ${pc.green('●')} ${pc.cyan(targetDir)}\n`);
118
+ console.log(`${pc.dim('○')} Initializing...`);
119
+ const ctx = getContextManager(targetDir);
120
+ await ctx.initialize();
121
+ console.log(`${pc.green('●')} Ready.`);
122
+ const mcpManager = getMCPManager();
123
+ try {
124
+ const configs = await mcpManager.loadConfig();
125
+ if (configs.length > 0)
126
+ await mcpManager.connectAll(configs);
127
+ }
128
+ catch (error) {
129
+ if (DEBUG)
130
+ console.error('MCP init error:', error);
131
+ }
132
+ const tierConfigs = MOE_MODE ? loadTierConfig() : null;
133
+ const multiProvider = tierConfigs ? createMultiProvider(tierConfigs) : null;
134
+ const singleProvider = !MOE_MODE ? createProvider() : null;
135
+ const generate = async (input) => {
136
+ const history = ctx.getHistory();
137
+ const fullPrompt = await ctx.buildSystemPrompt();
138
+ if (MOE_MODE && multiProvider && tierConfigs) {
139
+ const routing = await routeTask(input, async (prompt) => {
140
+ return multiProvider.generateWithTier(1, prompt, [{ role: 'user', content: input }]);
141
+ });
142
+ if (DEBUG)
143
+ console.log(pc.dim(`[Routing] Tier: ${routing.tier}`));
144
+ return multiProvider.generateWithTier(routing.tier, fullPrompt, history.map(m => ({ role: m.role, content: m.content })));
145
+ }
146
+ return singleProvider.generateResponse(fullPrompt, history.map(m => ({ role: m.role, content: m.content })));
147
+ };
148
+ let isFirstPrompt = true;
149
+ while (true) {
150
+ const skill = getActiveSkill();
151
+ let input;
152
+ // Support initial prompt from command line
153
+ if (isFirstPrompt && args.length > 0) {
154
+ input = args.join(' ');
155
+ console.log(`\n${pc.magenta('➤')} ${pc.bold(input)}`);
156
+ }
157
+ else {
158
+ input = await text({
159
+ message: pc.dim(`[@${skill.name}]`) + ' Chat with Simple-CLI',
160
+ placeholder: 'Ask anything or use /help',
161
+ validate(value) {
162
+ if (value.trim().length === 0)
163
+ return 'Input required';
164
+ }
165
+ });
166
+ }
167
+ isFirstPrompt = false;
168
+ if (isCancel(input)) {
169
+ console.log(`\n${pc.dim('—')} Goodbye!`);
170
+ mcpManager.disconnectAll();
171
+ process.exit(0);
172
+ }
173
+ const trimmedInput = input.trim();
174
+ // Slash command
175
+ if (trimmedInput.startsWith('/')) {
176
+ try {
177
+ await executeCommand(trimmedInput, {
178
+ cwd: ctx.getCwd(),
179
+ activeFiles: ctx.getState().activeFiles,
180
+ readOnlyFiles: ctx.getState().readOnlyFiles,
181
+ history: ctx.getHistory(),
182
+ io: {
183
+ output: (m) => console.log(`\n${pc.dim('○')} ${m}`),
184
+ error: (m) => console.log(`\n${pc.red('✖')} ${m}`),
185
+ confirm: async (m) => {
186
+ const c = await clackConfirm({ message: m });
187
+ return !isCancel(c) && c;
188
+ },
189
+ prompt: async (m) => {
190
+ const p = await text({ message: m });
191
+ return isCancel(p) ? '' : p;
192
+ }
193
+ }
194
+ });
195
+ }
196
+ catch (err) {
197
+ console.log(`\n${pc.red('✖')} ${pc.red(String(err))}`);
198
+ }
199
+ continue;
200
+ }
201
+ // Skill switching
202
+ if (trimmedInput.startsWith('@')) {
203
+ const skillName = trimmedInput.slice(1).trim();
204
+ if (skillName === 'list') {
205
+ const skills = listSkills();
206
+ const selected = await select({
207
+ message: 'Select a skill',
208
+ options: skills.map(s => ({ label: `@${s.name} - ${s.description}`, value: s.name }))
209
+ });
210
+ if (!isCancel(selected)) {
211
+ const newSkill = setActiveSkill(selected);
212
+ if (newSkill)
213
+ ctx.setSkill(newSkill);
214
+ }
215
+ }
216
+ else {
217
+ const newSkill = setActiveSkill(skillName);
218
+ if (newSkill) {
219
+ ctx.setSkill(newSkill);
220
+ console.log(`\n${pc.cyan('★')} Switched to @${newSkill.name}`);
221
+ }
222
+ else {
223
+ console.log(`\n${pc.red('✖')} Skill @${skillName} not found.`);
224
+ }
225
+ }
226
+ continue;
227
+ }
228
+ ctx.addMessage('user', trimmedInput);
229
+ let steps = 0;
230
+ while (steps < 15) {
231
+ const response = await generate(trimmedInput);
232
+ const { thought, action } = parseResponse(response);
233
+ if (thought)
234
+ console.log(`\n${pc.dim('💭')} ${pc.cyan(thought)}`);
235
+ if (action.tool !== 'none') {
236
+ if (await confirm(action.tool, action.args || {}, ctx)) {
237
+ console.log(`${pc.yellow('⚙')} ${pc.dim(`Executing ${action.tool}...`)}`);
238
+ const result = await executeTool(action.tool, action.args || {}, ctx);
239
+ console.log(`${pc.green('✔')} ${pc.dim(result.length > 500 ? result.slice(0, 500) + '...' : result)}`);
240
+ ctx.addMessage('assistant', response);
241
+ ctx.addMessage('user', `Tool result: ${result}`);
242
+ steps++;
243
+ }
244
+ else {
245
+ console.log(`${pc.yellow('⚠')} Skipped.`);
246
+ ctx.addMessage('assistant', response);
247
+ break;
248
+ }
249
+ }
250
+ else {
251
+ const msg = action.message || response.replace(/<thought>[\s\S]*?<\/thought>/, '').trim();
252
+ if (msg)
253
+ console.log(`\n${pc.magenta('✦')} ${msg}`);
254
+ ctx.addMessage('assistant', response);
255
+ break;
256
+ }
257
+ }
258
+ }
259
+ }
@@ -0,0 +1,9 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Add extends Command {
3
+ static description: string;
4
+ static args: {
5
+ files: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
6
+ };
7
+ static examples: string[];
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,50 @@
1
+ import { Command, Args } from '@oclif/core';
2
+ import * as ui from '../lib/ui.js';
3
+ import { getContextManager } from '../context.js';
4
+ import { resolve } from 'path';
5
+ import { existsSync } from 'fs';
6
+ export default class Add extends Command {
7
+ static description = 'Add files to the chat context';
8
+ static args = {
9
+ files: Args.string({
10
+ description: 'Files to add (supports glob patterns)',
11
+ required: true,
12
+ }),
13
+ };
14
+ static examples = [
15
+ '<%= config.bin %> add src/index.ts',
16
+ '<%= config.bin %> add "src/**/*.ts"',
17
+ '<%= config.bin %> add package.json tsconfig.json',
18
+ ];
19
+ async run() {
20
+ const { args, argv } = await this.parse(Add);
21
+ const ctx = getContextManager();
22
+ // Handle multiple files from argv
23
+ const files = argv;
24
+ let added = 0;
25
+ for (const file of files) {
26
+ const fullPath = resolve(process.cwd(), file);
27
+ if (file.includes('*')) {
28
+ // Handle glob pattern
29
+ const { execute } = await import('../tools/glob.js');
30
+ const result = await execute({ pattern: file, cwd: process.cwd(), maxResults: 1000, includeDirectories: false });
31
+ for (const match of result.matches) {
32
+ if (ctx.addFile(match)) {
33
+ ui.success(`Added ${match}`);
34
+ added++;
35
+ }
36
+ }
37
+ }
38
+ else if (existsSync(fullPath)) {
39
+ if (ctx.addFile(file)) {
40
+ ui.success(`Added ${file}`);
41
+ added++;
42
+ }
43
+ }
44
+ else {
45
+ ui.error(`File not found: ${file}`);
46
+ }
47
+ }
48
+ ui.log(`Added ${added} file(s) to context`);
49
+ }
50
+ }
@@ -0,0 +1,12 @@
1
+ import { Command } from '@oclif/core';
2
+ import 'dotenv/config';
3
+ export default class GitCommit extends Command {
4
+ static description: string;
5
+ static flags: {
6
+ message: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ ai: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ };
10
+ static examples: string[];
11
+ run(): Promise<void>;
12
+ }