@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.
- package/README.md +287 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +259 -0
- package/dist/commands/add.d.ts +9 -0
- package/dist/commands/add.js +50 -0
- package/dist/commands/git/commit.d.ts +12 -0
- package/dist/commands/git/commit.js +97 -0
- package/dist/commands/git/status.d.ts +6 -0
- package/dist/commands/git/status.js +42 -0
- package/dist/commands/index.d.ts +16 -0
- package/dist/commands/index.js +376 -0
- package/dist/commands/mcp/status.d.ts +6 -0
- package/dist/commands/mcp/status.js +31 -0
- package/dist/commands/swarm.d.ts +36 -0
- package/dist/commands/swarm.js +236 -0
- package/dist/commands.d.ts +32 -0
- package/dist/commands.js +427 -0
- package/dist/context.d.ts +116 -0
- package/dist/context.js +327 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +109 -0
- package/dist/lib/agent.d.ts +98 -0
- package/dist/lib/agent.js +281 -0
- package/dist/lib/editor.d.ts +74 -0
- package/dist/lib/editor.js +441 -0
- package/dist/lib/git.d.ts +164 -0
- package/dist/lib/git.js +351 -0
- package/dist/lib/ui.d.ts +159 -0
- package/dist/lib/ui.js +252 -0
- package/dist/mcp/client.d.ts +22 -0
- package/dist/mcp/client.js +81 -0
- package/dist/mcp/manager.d.ts +186 -0
- package/dist/mcp/manager.js +442 -0
- package/dist/prompts/provider.d.ts +22 -0
- package/dist/prompts/provider.js +78 -0
- package/dist/providers/index.d.ts +15 -0
- package/dist/providers/index.js +82 -0
- package/dist/providers/multi.d.ts +11 -0
- package/dist/providers/multi.js +28 -0
- package/dist/registry.d.ts +24 -0
- package/dist/registry.js +379 -0
- package/dist/repoMap.d.ts +5 -0
- package/dist/repoMap.js +79 -0
- package/dist/router.d.ts +41 -0
- package/dist/router.js +108 -0
- package/dist/skills.d.ts +25 -0
- package/dist/skills.js +288 -0
- package/dist/swarm/coordinator.d.ts +86 -0
- package/dist/swarm/coordinator.js +257 -0
- package/dist/swarm/index.d.ts +28 -0
- package/dist/swarm/index.js +29 -0
- package/dist/swarm/task.d.ts +104 -0
- package/dist/swarm/task.js +221 -0
- package/dist/swarm/types.d.ts +132 -0
- package/dist/swarm/types.js +37 -0
- package/dist/swarm/worker.d.ts +107 -0
- package/dist/swarm/worker.js +299 -0
- package/dist/tools/analyzeFile.d.ts +16 -0
- package/dist/tools/analyzeFile.js +43 -0
- package/dist/tools/git.d.ts +40 -0
- package/dist/tools/git.js +236 -0
- package/dist/tools/glob.d.ts +34 -0
- package/dist/tools/glob.js +165 -0
- package/dist/tools/grep.d.ts +53 -0
- package/dist/tools/grep.js +296 -0
- package/dist/tools/linter.d.ts +35 -0
- package/dist/tools/linter.js +349 -0
- package/dist/tools/listDir.d.ts +29 -0
- package/dist/tools/listDir.js +50 -0
- package/dist/tools/memory.d.ts +34 -0
- package/dist/tools/memory.js +215 -0
- package/dist/tools/readFiles.d.ts +25 -0
- package/dist/tools/readFiles.js +31 -0
- package/dist/tools/reloadTools.d.ts +11 -0
- package/dist/tools/reloadTools.js +22 -0
- package/dist/tools/runCommand.d.ts +32 -0
- package/dist/tools/runCommand.js +79 -0
- package/dist/tools/scraper.d.ts +31 -0
- package/dist/tools/scraper.js +211 -0
- package/dist/tools/writeFiles.d.ts +63 -0
- package/dist/tools/writeFiles.js +87 -0
- package/dist/ui/server.d.ts +5 -0
- package/dist/ui/server.js +74 -0
- package/dist/watcher.d.ts +35 -0
- package/dist/watcher.js +164 -0
- package/docs/assets/logo.jpeg +0 -0
- 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
|
+
[](https://www.npmjs.com/package/@stan-chen/simple-cli)
|
|
6
|
+
[](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
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
|
+
}
|