@stan-chen/simple-cli 0.2.3 → 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.
- package/README.md +62 -63
- package/dist/anyllm.py +62 -0
- package/dist/builtins.d.ts +726 -0
- package/dist/builtins.js +481 -0
- package/dist/cli.d.ts +0 -4
- package/dist/cli.js +34 -493
- package/dist/engine.d.ts +33 -0
- package/dist/engine.js +138 -0
- package/dist/learnings.d.ts +15 -0
- package/dist/learnings.js +54 -0
- package/dist/llm.d.ts +18 -0
- package/dist/llm.js +66 -0
- package/dist/mcp.d.ts +132 -0
- package/dist/mcp.js +43 -0
- package/dist/skills.d.ts +5 -16
- package/dist/skills.js +91 -253
- package/dist/tui.d.ts +1 -0
- package/dist/tui.js +10 -0
- package/package.json +10 -6
- package/dist/claw/jit.d.ts +0 -5
- package/dist/claw/jit.js +0 -138
- package/dist/claw/management.d.ts +0 -3
- package/dist/claw/management.js +0 -107
- package/dist/commands/add.d.ts +0 -9
- package/dist/commands/add.js +0 -50
- package/dist/commands/git/commit.d.ts +0 -12
- package/dist/commands/git/commit.js +0 -98
- package/dist/commands/git/status.d.ts +0 -6
- package/dist/commands/git/status.js +0 -42
- package/dist/commands/index.d.ts +0 -16
- package/dist/commands/index.js +0 -377
- package/dist/commands/mcp/status.d.ts +0 -6
- package/dist/commands/mcp/status.js +0 -31
- package/dist/commands/swarm.d.ts +0 -36
- package/dist/commands/swarm.js +0 -236
- package/dist/commands.d.ts +0 -32
- package/dist/commands.js +0 -427
- package/dist/context.d.ts +0 -116
- package/dist/context.js +0 -337
- package/dist/index.d.ts +0 -6
- package/dist/index.js +0 -109
- package/dist/lib/agent.d.ts +0 -99
- package/dist/lib/agent.js +0 -313
- package/dist/lib/editor.d.ts +0 -74
- package/dist/lib/editor.js +0 -441
- package/dist/lib/git.d.ts +0 -164
- package/dist/lib/git.js +0 -356
- package/dist/lib/shim.d.ts +0 -4
- package/dist/lib/shim.js +0 -30
- package/dist/lib/ui.d.ts +0 -159
- package/dist/lib/ui.js +0 -277
- package/dist/mcp/client.d.ts +0 -22
- package/dist/mcp/client.js +0 -81
- package/dist/mcp/manager.d.ts +0 -186
- package/dist/mcp/manager.js +0 -446
- package/dist/prompts/provider.d.ts +0 -22
- package/dist/prompts/provider.js +0 -79
- package/dist/providers/index.d.ts +0 -31
- package/dist/providers/index.js +0 -93
- package/dist/providers/multi.d.ts +0 -12
- package/dist/providers/multi.js +0 -28
- package/dist/registry.d.ts +0 -29
- package/dist/registry.js +0 -443
- package/dist/repoMap.d.ts +0 -5
- package/dist/repoMap.js +0 -79
- package/dist/router.d.ts +0 -41
- package/dist/router.js +0 -118
- package/dist/swarm/coordinator.d.ts +0 -86
- package/dist/swarm/coordinator.js +0 -257
- package/dist/swarm/index.d.ts +0 -28
- package/dist/swarm/index.js +0 -29
- package/dist/swarm/task.d.ts +0 -104
- package/dist/swarm/task.js +0 -221
- package/dist/swarm/types.d.ts +0 -132
- package/dist/swarm/types.js +0 -37
- package/dist/swarm/worker.d.ts +0 -109
- package/dist/swarm/worker.js +0 -369
- package/dist/tools/analyzeFile.d.ts +0 -16
- package/dist/tools/analyzeFile.js +0 -43
- package/dist/tools/analyze_file.d.ts +0 -16
- package/dist/tools/analyze_file.js +0 -43
- package/dist/tools/clawBrain.d.ts +0 -23
- package/dist/tools/clawBrain.js +0 -136
- package/dist/tools/claw_brain.d.ts +0 -23
- package/dist/tools/claw_brain.js +0 -139
- package/dist/tools/deleteFile.d.ts +0 -19
- package/dist/tools/deleteFile.js +0 -36
- package/dist/tools/delete_file.d.ts +0 -19
- package/dist/tools/delete_file.js +0 -36
- package/dist/tools/fileOps.d.ts +0 -22
- package/dist/tools/fileOps.js +0 -43
- package/dist/tools/file_ops.d.ts +0 -22
- package/dist/tools/file_ops.js +0 -43
- package/dist/tools/git.d.ts +0 -40
- package/dist/tools/git.js +0 -236
- package/dist/tools/glob.d.ts +0 -34
- package/dist/tools/glob.js +0 -165
- package/dist/tools/grep.d.ts +0 -53
- package/dist/tools/grep.js +0 -296
- package/dist/tools/linter.d.ts +0 -35
- package/dist/tools/linter.js +0 -407
- package/dist/tools/listDir.d.ts +0 -29
- package/dist/tools/listDir.js +0 -50
- package/dist/tools/list_dir.d.ts +0 -29
- package/dist/tools/list_dir.js +0 -50
- package/dist/tools/memory.d.ts +0 -34
- package/dist/tools/memory.js +0 -215
- package/dist/tools/organizer.d.ts +0 -1
- package/dist/tools/organizer.js +0 -65
- package/dist/tools/readFiles.d.ts +0 -25
- package/dist/tools/readFiles.js +0 -31
- package/dist/tools/read_files.d.ts +0 -25
- package/dist/tools/read_files.js +0 -31
- package/dist/tools/reloadTools.d.ts +0 -11
- package/dist/tools/reloadTools.js +0 -22
- package/dist/tools/reload_tools.d.ts +0 -11
- package/dist/tools/reload_tools.js +0 -22
- package/dist/tools/runCommand.d.ts +0 -32
- package/dist/tools/runCommand.js +0 -79
- package/dist/tools/run_command.d.ts +0 -32
- package/dist/tools/run_command.js +0 -103
- package/dist/tools/scheduler.d.ts +0 -25
- package/dist/tools/scheduler.js +0 -65
- package/dist/tools/scraper.d.ts +0 -31
- package/dist/tools/scraper.js +0 -211
- package/dist/tools/writeFiles.d.ts +0 -63
- package/dist/tools/writeFiles.js +0 -87
- package/dist/tools/write_files.d.ts +0 -84
- package/dist/tools/write_files.js +0 -91
- package/dist/tools/write_to_file.d.ts +0 -15
- package/dist/tools/write_to_file.js +0 -21
- package/dist/ui/server.d.ts +0 -5
- package/dist/ui/server.js +0 -74
- package/dist/watcher.d.ts +0 -35
- package/dist/watcher.js +0 -164
- /package/{docs/assets → assets}/logo.jpeg +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,504 +1,45 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Simple-CLI - Premium TUI agentic coding assistant
|
|
4
|
-
* Powered by @clack/prompts.
|
|
5
|
-
*/
|
|
6
2
|
import 'dotenv/config';
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { getMCPManager } from './mcp/manager.js';
|
|
15
|
-
import { listSkills, setActiveSkill, getActiveSkill } from './skills.js';
|
|
16
|
-
import { readFileSync, existsSync, statSync } from 'fs';
|
|
17
|
-
import fs from 'fs';
|
|
18
|
-
import { writeFile, mkdir } from 'fs/promises';
|
|
19
|
-
import { resolve, join, dirname } from 'path';
|
|
20
|
-
import { fileURLToPath } from 'url';
|
|
21
|
-
import { runSwarm, parseSwarmArgs, printSwarmHelp } from './commands/swarm.js';
|
|
22
|
-
import { runDeterministicOrganizer } from './tools/organizer.js';
|
|
23
|
-
import { jsonrepair } from 'jsonrepair';
|
|
24
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
25
|
-
const __dirname = dirname(__filename);
|
|
26
|
-
// Deterministic organizer moved to src/tools/organizer.ts
|
|
27
|
-
// CLI flags
|
|
28
|
-
const MOE_MODE = process.argv.includes('--moe');
|
|
29
|
-
const SWARM_MODE = process.argv.includes('--swarm');
|
|
30
|
-
const CLAW_MODE = process.argv.includes('--claw') || process.argv.includes('-claw');
|
|
31
|
-
const GHOST_MODE = process.argv.includes('--ghost');
|
|
32
|
-
const YOLO_MODE = process.argv.includes('--yolo') || CLAW_MODE || GHOST_MODE;
|
|
33
|
-
const DEBUG = process.argv.includes('--debug') || process.env.DEBUG === 'true';
|
|
34
|
-
const VERSION = '0.2.2';
|
|
35
|
-
// Non-interactive detection (tests and CI)
|
|
36
|
-
const NON_INTERACTIVE = process.env.VITEST === 'true' || process.env.TEST === 'true' || !process.stdin.isTTY;
|
|
37
|
-
// Handle --version and --help immediately
|
|
38
|
-
if (process.argv.includes('--version') || process.argv.includes('-v')) {
|
|
39
|
-
console.log(`Simple-CLI v${VERSION}`);
|
|
40
|
-
process.exit(0);
|
|
41
|
-
}
|
|
42
|
-
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
43
|
-
console.log(`
|
|
44
|
-
${pc.bgCyan(pc.black(' SIMPLE-CLI '))} ${pc.dim(`v${VERSION}`)}
|
|
45
|
-
|
|
46
|
-
${pc.bold('Usage:')}
|
|
47
|
-
simple [target_dir] [prompt] [options]
|
|
48
|
-
|
|
49
|
-
${pc.bold('Options:')}
|
|
50
|
-
--version, -v Show version
|
|
51
|
-
--help, -h Show help
|
|
52
|
-
--yolo Skip all confirmation prompts
|
|
53
|
-
--moe Enable Mixture of Experts (multi-model)
|
|
54
|
-
--swarm Enable Swarm orchestration mode
|
|
55
|
-
--claw "intent" Enable OpenClaw JIT agent generation
|
|
56
|
-
--debug Enable debug logging
|
|
57
|
-
|
|
58
|
-
${pc.bold('Examples:')}
|
|
59
|
-
simple . "Build a login page"
|
|
60
|
-
simple --claw "Security audit this project"
|
|
61
|
-
simple --moe "Refactor this entire folder"
|
|
62
|
-
`);
|
|
63
|
-
process.exit(0);
|
|
64
|
-
}
|
|
65
|
-
// Claw mode will be handled after directory change in main()
|
|
66
|
-
let clawIntent = null;
|
|
67
|
-
// Handle --swarm mode
|
|
68
|
-
if (SWARM_MODE) {
|
|
69
|
-
if (process.argv.includes('--help')) {
|
|
70
|
-
printSwarmHelp();
|
|
71
|
-
process.exit(0);
|
|
72
|
-
}
|
|
73
|
-
const swarmOptions = parseSwarmArgs(process.argv.slice(2));
|
|
74
|
-
swarmOptions.yolo = swarmOptions.yolo || YOLO_MODE;
|
|
75
|
-
runSwarm(swarmOptions).catch(err => {
|
|
76
|
-
console.error('Swarm error:', err);
|
|
77
|
-
process.exit(1);
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
main().catch(console.error);
|
|
82
|
-
}
|
|
83
|
-
function parseResponse(response) {
|
|
84
|
-
// Try parsing as pure JSON first (for JSON mode)
|
|
85
|
-
try {
|
|
86
|
-
const trimmed = response.trim();
|
|
87
|
-
const parsed = JSON.parse(jsonrepair(trimmed));
|
|
88
|
-
const tool = parsed.tool;
|
|
89
|
-
if (tool) {
|
|
90
|
-
return {
|
|
91
|
-
thought: parsed.thought || '',
|
|
92
|
-
action: {
|
|
93
|
-
tool: tool,
|
|
94
|
-
message: parsed.message || '',
|
|
95
|
-
args: parsed.args || parsed.parameters || parsed.input || parsed
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
catch { /* Fall through to legacy format */ }
|
|
101
|
-
// Legacy format with <thought> tags
|
|
102
|
-
const thought = response.match(/<thought>([\s\S]*?)<\/thought>/)?.[1]?.trim() || '';
|
|
103
|
-
// Clean thought from response for message parsing
|
|
104
|
-
let cleanResponse = response.replace(/<thought>[\s\S]*?<\/thought>/, '').trim();
|
|
105
|
-
const jsonMatch = cleanResponse.match(/\{[\s\S]*"tool"[\s\S]*\}/);
|
|
106
|
-
let action = { tool: 'none', message: '', args: {} };
|
|
107
|
-
if (jsonMatch) {
|
|
108
|
-
try {
|
|
109
|
-
action = JSON.parse(jsonrepair(jsonMatch[0]));
|
|
110
|
-
// normalize tool names to snake_case for consistency with tool registry
|
|
111
|
-
if (action && action.tool && typeof action.tool === 'string') {
|
|
112
|
-
action.tool = String(action.tool).replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase();
|
|
113
|
-
}
|
|
114
|
-
// Remove JSON block for the remaining message
|
|
115
|
-
cleanResponse = cleanResponse.replace(jsonMatch[0], '').trim();
|
|
116
|
-
}
|
|
117
|
-
catch { /* skip */ }
|
|
118
|
-
}
|
|
119
|
-
// If we still have text and no message, use the remaining text
|
|
120
|
-
if (!action.message && cleanResponse) {
|
|
121
|
-
action.message = cleanResponse;
|
|
122
|
-
}
|
|
123
|
-
return { thought, action };
|
|
124
|
-
}
|
|
125
|
-
async function confirm(tool, args, ctx) {
|
|
126
|
-
if (YOLO_MODE)
|
|
127
|
-
return true;
|
|
128
|
-
const t = ctx.getTools().get(tool);
|
|
129
|
-
if (!t || t.permission === 'read')
|
|
130
|
-
return true;
|
|
131
|
-
if (NON_INTERACTIVE)
|
|
132
|
-
return true; // auto-approve in non-interactive/test environments
|
|
133
|
-
const confirmed = await clackConfirm({
|
|
134
|
-
message: `Allow ${pc.cyan(tool)} with args ${pc.dim(JSON.stringify(args))}?`,
|
|
135
|
-
initialValue: true,
|
|
136
|
-
});
|
|
137
|
-
return !isCancel(confirmed) && confirmed;
|
|
138
|
-
}
|
|
139
|
-
async function executeTool(name, args, ctx) {
|
|
140
|
-
const tool = ctx.getTools().get(name);
|
|
141
|
-
if (!tool)
|
|
142
|
-
return `Error: Tool "${name}" not found`;
|
|
143
|
-
try {
|
|
144
|
-
const result = await tool.execute(args);
|
|
145
|
-
return typeof result === 'string' ? result : JSON.stringify(result, null, 2);
|
|
146
|
-
}
|
|
147
|
-
catch (error) {
|
|
148
|
-
return `Error: ${error instanceof Error ? error.message : error}`;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
3
|
+
import { statSync } from 'fs';
|
|
4
|
+
import { Engine, Context, Registry } from './engine.js';
|
|
5
|
+
import { allBuiltins } from './builtins.js';
|
|
6
|
+
import { createLLM } from './llm.js';
|
|
7
|
+
import { MCP } from './mcp.js';
|
|
8
|
+
import { getActiveSkill } from './skills.js';
|
|
9
|
+
import { showBanner } from './tui.js';
|
|
151
10
|
async function main() {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
let
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
process.exit(0);
|
|
161
|
-
}
|
|
162
|
-
if (process.argv.includes('--logs')) {
|
|
163
|
-
const { showGhostLogs } = await import('./claw/management.js');
|
|
164
|
-
const id = process.argv[process.argv.indexOf('--logs') + 1];
|
|
165
|
-
await showGhostLogs(id && !id.startsWith('-') ? id : undefined);
|
|
166
|
-
process.exit(0);
|
|
167
|
-
}
|
|
168
|
-
if (process.argv.includes('--kill')) {
|
|
169
|
-
const { killGhostTask } = await import('./claw/management.js');
|
|
170
|
-
const id = process.argv[process.argv.indexOf('--kill') + 1];
|
|
171
|
-
if (!id || id.startsWith('-')) {
|
|
172
|
-
console.error(pc.red('Error: Task ID required for --kill'));
|
|
173
|
-
process.exit(1);
|
|
174
|
-
}
|
|
175
|
-
await killGhostTask(id);
|
|
176
|
-
process.exit(0);
|
|
177
|
-
}
|
|
178
|
-
if (process.argv.includes('--invoke') || process.argv.includes('--invoke-json')) {
|
|
179
|
-
const isJson = process.argv.includes('--invoke-json');
|
|
180
|
-
const idx = process.argv.indexOf(isJson ? '--invoke-json' : '--invoke');
|
|
181
|
-
const toolName = process.argv[idx + 1];
|
|
182
|
-
const toolArgsStr = process.argv[idx + 2] || '{}';
|
|
183
|
-
if (!toolName) {
|
|
184
|
-
console.error(pc.red('Error: Tool name required for --invoke'));
|
|
185
|
-
process.exit(1);
|
|
186
|
-
}
|
|
187
|
-
try {
|
|
188
|
-
const ctx = getContextManager(process.cwd());
|
|
189
|
-
await ctx.initialize();
|
|
190
|
-
const toolArgs = JSON.parse(toolArgsStr);
|
|
191
|
-
const result = await executeTool(toolName, toolArgs, ctx);
|
|
192
|
-
console.log(result);
|
|
193
|
-
process.exit(0);
|
|
194
|
-
}
|
|
195
|
-
catch (error) {
|
|
196
|
-
console.error(pc.red(`Error invoking tool ${toolName}:`), error);
|
|
197
|
-
process.exit(1);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
if (args.length > 0) {
|
|
201
|
-
try {
|
|
202
|
-
if (statSync(args[0]).isDirectory()) {
|
|
203
|
-
targetDir = resolve(args[0]);
|
|
204
|
-
process.chdir(targetDir);
|
|
205
|
-
// Reload .env from the new directory
|
|
206
|
-
const { config } = await import('dotenv');
|
|
207
|
-
config();
|
|
208
|
-
args.shift();
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
catch { /* ignored */ }
|
|
212
|
-
}
|
|
213
|
-
// Handle --claw mode AFTER directory change
|
|
214
|
-
if (CLAW_MODE) {
|
|
215
|
-
const { execSync } = await import('child_process');
|
|
216
|
-
// args now has directory removed, so join the rest as intent
|
|
217
|
-
clawIntent = args.join(' ') || 'unspecified task';
|
|
218
|
-
console.log(pc.cyan('🧬 Initiating JIT Agent Generation...'));
|
|
219
|
-
console.log(pc.dim(`Intent: "${clawIntent}"`));
|
|
220
|
-
console.log(pc.dim(`Working Directory: ${targetDir}`));
|
|
221
|
-
try {
|
|
222
|
-
const { generateJitAgent } = await import('./claw/jit.js');
|
|
223
|
-
await generateJitAgent(clawIntent, targetDir);
|
|
224
|
-
// If the generated AGENT.md doesn't contain actionable tool instructions,
|
|
225
|
-
// run the deterministic organizer immediately so live demos are deterministic.
|
|
226
|
-
try {
|
|
227
|
-
const agentFile = join(targetDir, '.simple', 'workdir', 'AGENT.md');
|
|
228
|
-
let agentContent = '';
|
|
229
|
-
try {
|
|
230
|
-
agentContent = readFileSync(agentFile, 'utf-8');
|
|
231
|
-
}
|
|
232
|
-
catch {
|
|
233
|
-
agentContent = '';
|
|
234
|
-
}
|
|
235
|
-
const actionable = /list_dir|move_file|move files|move_file|write_to_file|list files|scheduler|schedule|extract total|move\b/i.test(agentContent);
|
|
236
|
-
if (!actionable) {
|
|
237
|
-
console.log(pc.yellow('AGENT.md lacks actionable steps — running deterministic organizer fallback.'));
|
|
238
|
-
runDeterministicOrganizer(targetDir);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
catch (err) {
|
|
242
|
-
console.error('Error checking AGENT.md for actions:', err);
|
|
243
|
-
}
|
|
244
|
-
console.log(pc.green('\n✅ JIT Agent soul ready. Starting autopilot loop...\n'));
|
|
245
|
-
// Inject autonomous environment variables
|
|
246
|
-
process.env.CLAW_WORKSPACE = targetDir;
|
|
247
|
-
process.env.CLAW_SKILL_PATH = join(targetDir, 'skills');
|
|
248
|
-
process.env.CLAW_DATA_DIR = join(targetDir, '.simple/workdir/memory');
|
|
249
|
-
}
|
|
250
|
-
catch (error) {
|
|
251
|
-
console.error(pc.red('❌ Failed to initialize Claw mode:'), error);
|
|
252
|
-
process.exit(1);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
if (!GHOST_MODE) {
|
|
256
|
-
console.log(`\n ${pc.bgCyan(pc.black(' SIMPLE-CLI '))} ${pc.dim(`v${VERSION}`)} ${pc.green('●')} ${pc.cyan(targetDir)}\n`);
|
|
257
|
-
console.log(`${pc.dim('○')} Initializing...`);
|
|
258
|
-
}
|
|
259
|
-
const ctx = getContextManager(targetDir);
|
|
260
|
-
await ctx.initialize();
|
|
261
|
-
if (!GHOST_MODE)
|
|
262
|
-
console.log(`${pc.green('●')} Ready.`);
|
|
263
|
-
const mcpManager = getMCPManager();
|
|
264
|
-
try {
|
|
265
|
-
const configs = await mcpManager.loadConfig();
|
|
266
|
-
if (configs.length > 0)
|
|
267
|
-
await mcpManager.connectAll(configs);
|
|
268
|
-
}
|
|
269
|
-
catch (error) {
|
|
270
|
-
if (DEBUG)
|
|
271
|
-
console.error('MCP init error:', error);
|
|
272
|
-
}
|
|
273
|
-
const tierConfigs = MOE_MODE ? loadTierConfig() : null;
|
|
274
|
-
const multiProvider = tierConfigs ? createMultiProvider(tierConfigs) : null;
|
|
275
|
-
const singleProvider = !MOE_MODE ? createProvider() : null;
|
|
276
|
-
const generate = async (input) => {
|
|
277
|
-
const history = ctx.getHistory();
|
|
278
|
-
const fullPrompt = await ctx.buildSystemPrompt();
|
|
279
|
-
if (MOE_MODE && multiProvider && tierConfigs) {
|
|
280
|
-
const routing = await routeTask(input, async (prompt) => {
|
|
281
|
-
const res = await multiProvider.generateWithTier(1, prompt, [{ role: 'user', content: input }]);
|
|
282
|
-
return res.raw || JSON.stringify(res);
|
|
283
|
-
});
|
|
284
|
-
if (DEBUG)
|
|
285
|
-
console.log(pc.dim(`[Routing] Tier: ${routing.tier}`));
|
|
286
|
-
return multiProvider.generateWithTier(routing.tier, fullPrompt, history.map(m => ({ role: m.role, content: m.content })));
|
|
287
|
-
}
|
|
288
|
-
return singleProvider.generateResponse(fullPrompt, history.map(m => ({ role: m.role, content: m.content })));
|
|
289
|
-
};
|
|
290
|
-
let isFirstPrompt = true;
|
|
291
|
-
const isAutonomousMode = CLAW_MODE && clawIntent;
|
|
292
|
-
let autonomousNudges = 0;
|
|
293
|
-
while (true) {
|
|
294
|
-
const skill = getActiveSkill();
|
|
295
|
-
let input;
|
|
296
|
-
// Support initial prompt from command line or claw intent
|
|
297
|
-
if (isFirstPrompt && (clawIntent || args.length > 0)) {
|
|
298
|
-
input = clawIntent || args.join(' ');
|
|
299
|
-
console.log(`\n${pc.magenta('➤')} ${pc.bold(input)}`);
|
|
300
|
-
}
|
|
301
|
-
else {
|
|
302
|
-
if (NON_INTERACTIVE) {
|
|
303
|
-
// In non-interactive mode return empty string to allow tests to inject inputs
|
|
304
|
-
input = '';
|
|
305
|
-
}
|
|
306
|
-
else {
|
|
307
|
-
input = await text({
|
|
308
|
-
message: pc.dim(`[@${skill.name}]`) + ' Chat with Simple-CLI',
|
|
309
|
-
placeholder: 'Ask anything or use /help',
|
|
310
|
-
validate(value) {
|
|
311
|
-
if (value.trim().length === 0)
|
|
312
|
-
return 'Input required';
|
|
313
|
-
}
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
isFirstPrompt = false;
|
|
318
|
-
if (isCancel(input)) {
|
|
319
|
-
console.log(`\n${pc.dim('—')} Goodbye!`);
|
|
320
|
-
mcpManager.disconnectAll();
|
|
321
|
-
process.exit(0);
|
|
322
|
-
}
|
|
323
|
-
const trimmedInput = input.trim();
|
|
324
|
-
// Slash command
|
|
325
|
-
if (trimmedInput.startsWith('/')) {
|
|
326
|
-
try {
|
|
327
|
-
await executeCommand(trimmedInput, {
|
|
328
|
-
cwd: ctx.getCwd(),
|
|
329
|
-
activeFiles: ctx.getState().activeFiles,
|
|
330
|
-
readOnlyFiles: ctx.getState().readOnlyFiles,
|
|
331
|
-
history: ctx.getHistory(),
|
|
332
|
-
io: {
|
|
333
|
-
output: (m) => console.log(`\n${pc.dim('○')} ${m}`),
|
|
334
|
-
error: (m) => console.log(`\n${pc.red('✖')} ${m}`),
|
|
335
|
-
confirm: async (m) => {
|
|
336
|
-
const c = await clackConfirm({ message: m });
|
|
337
|
-
return !isCancel(c) && c;
|
|
338
|
-
},
|
|
339
|
-
prompt: async (m) => {
|
|
340
|
-
const p = await text({ message: m });
|
|
341
|
-
return isCancel(p) ? '' : p;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
catch (err) {
|
|
347
|
-
console.log(`\n${pc.red('✖')} ${pc.red(String(err))}`);
|
|
348
|
-
}
|
|
11
|
+
const args = process.argv.slice(2);
|
|
12
|
+
// Handle optional directory argument
|
|
13
|
+
let cwd = process.cwd();
|
|
14
|
+
let interactive = true;
|
|
15
|
+
const remainingArgs = [];
|
|
16
|
+
for (const arg of args) {
|
|
17
|
+
if (arg === '--non-interactive') {
|
|
18
|
+
interactive = false;
|
|
349
19
|
continue;
|
|
350
20
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
let selected;
|
|
357
|
-
if (NON_INTERACTIVE) {
|
|
358
|
-
selected = skills.length > 0 ? skills[0].name : undefined;
|
|
359
|
-
}
|
|
360
|
-
else {
|
|
361
|
-
const sel = await select({
|
|
362
|
-
message: 'Select a skill',
|
|
363
|
-
options: skills.map(s => ({ label: `@${s.name} - ${s.description}`, value: s.name }))
|
|
364
|
-
});
|
|
365
|
-
if (!isCancel(sel))
|
|
366
|
-
selected = sel;
|
|
367
|
-
}
|
|
368
|
-
if (selected) {
|
|
369
|
-
const newSkill = setActiveSkill(selected);
|
|
370
|
-
if (newSkill)
|
|
371
|
-
ctx.setSkill(newSkill);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
else {
|
|
375
|
-
const newSkill = setActiveSkill(skillName);
|
|
376
|
-
if (newSkill) {
|
|
377
|
-
ctx.setSkill(newSkill);
|
|
378
|
-
console.log(`\n${pc.cyan('★')} Switched to @${newSkill.name}`);
|
|
379
|
-
}
|
|
380
|
-
else {
|
|
381
|
-
console.log(`\n${pc.red('✖')} Skill @${skillName} not found.`);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
ctx.addMessage('user', trimmedInput);
|
|
387
|
-
let currentInput = trimmedInput;
|
|
388
|
-
if (CLAW_MODE || GHOST_MODE) {
|
|
389
|
-
currentInput = `MISSION START: ${trimmedInput}. Consult your persona in AGENT.md and perform the mission tasks immediately. Use list_dir to see what you are working with.`;
|
|
390
|
-
}
|
|
391
|
-
let steps = 0;
|
|
392
|
-
let ghostLogFile = null;
|
|
393
|
-
if (GHOST_MODE) {
|
|
394
|
-
const logDir = join(targetDir, '.simple/workdir/memory/logs');
|
|
395
|
-
if (!existsSync(logDir))
|
|
396
|
-
await mkdir(logDir, { recursive: true });
|
|
397
|
-
ghostLogFile = join(logDir, `ghost-${Date.now()}.log`);
|
|
398
|
-
await writeFile(ghostLogFile, `[GHOST START] Intent: ${trimmedInput}\n`);
|
|
399
|
-
}
|
|
400
|
-
while (steps < 15) {
|
|
401
|
-
const response = await generate(currentInput);
|
|
402
|
-
const { thought, tool, args, message } = response;
|
|
403
|
-
const action = { tool: tool || 'none', args: args || {}, message: message || '' };
|
|
404
|
-
const logMsg = (msg) => {
|
|
405
|
-
if (GHOST_MODE && ghostLogFile) {
|
|
406
|
-
fs.appendFileSync(ghostLogFile, msg + '\n');
|
|
407
|
-
}
|
|
408
|
-
else {
|
|
409
|
-
console.log(msg);
|
|
410
|
-
}
|
|
411
|
-
};
|
|
412
|
-
if (thought)
|
|
413
|
-
logMsg(`\n${pc.dim('💭')} ${pc.cyan(thought)}`);
|
|
414
|
-
if (action.tool !== 'none') {
|
|
415
|
-
if (await confirm(action.tool, action.args || {}, ctx)) {
|
|
416
|
-
logMsg(`${pc.yellow('⚙')} ${pc.dim(`Executing ${action.tool}...`)}`);
|
|
417
|
-
const result = await executeTool(action.tool, action.args || {}, ctx);
|
|
418
|
-
const resultStr = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
|
|
419
|
-
logMsg(`${pc.green('✔')} ${pc.dim(resultStr.length > 500 ? resultStr.slice(0, 500) + '...' : resultStr)}`);
|
|
420
|
-
const assistantMsg = response.raw || JSON.stringify(response);
|
|
421
|
-
ctx.addMessage('assistant', assistantMsg);
|
|
422
|
-
ctx.addMessage('user', `Tool result: ${resultStr}. Continue the mission.`);
|
|
423
|
-
currentInput = 'Continue the mission.';
|
|
424
|
-
steps++;
|
|
425
|
-
// Autonomous Reflection & Status Check
|
|
426
|
-
if (CLAW_MODE || GHOST_MODE) {
|
|
427
|
-
const brain = ctx.getTools().get('claw_brain');
|
|
428
|
-
if (brain) {
|
|
429
|
-
await brain.execute({
|
|
430
|
-
action: 'log_reflection',
|
|
431
|
-
content: `Executed ${action.tool}. Result: ${resultStr.slice(0, 150)}...`
|
|
432
|
-
});
|
|
433
|
-
// Check if mission is marked completed
|
|
434
|
-
const summary = (await brain.execute({ action: 'get_summary' }));
|
|
435
|
-
if (summary.status === 'completed') {
|
|
436
|
-
logMsg(`\n${pc.green('📌')} Mission status: completed. Ending loop.`);
|
|
437
|
-
break;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
continue;
|
|
442
|
-
}
|
|
443
|
-
else {
|
|
444
|
-
logMsg(`${pc.yellow('⚠')} Skipped.`);
|
|
445
|
-
const assistantMsg = response.raw || JSON.stringify(response);
|
|
446
|
-
ctx.addMessage('assistant', assistantMsg);
|
|
21
|
+
if (!arg.startsWith('-')) {
|
|
22
|
+
try {
|
|
23
|
+
if (statSync(arg).isDirectory()) {
|
|
24
|
+
cwd = arg;
|
|
25
|
+
process.chdir(cwd);
|
|
447
26
|
continue;
|
|
448
27
|
}
|
|
449
28
|
}
|
|
450
|
-
|
|
451
|
-
// Fallback for empty message/tool to avoid "no reply"
|
|
452
|
-
const assistantMessage = action.message || response.raw || '';
|
|
453
|
-
if (assistantMessage) {
|
|
454
|
-
logMsg(`\n${pc.green('🤖')} ${assistantMessage}`);
|
|
455
|
-
ctx.addMessage('assistant', response.raw || assistantMessage);
|
|
456
|
-
}
|
|
457
|
-
else {
|
|
458
|
-
logMsg(`\n${pc.red('✖')} Agent returned an empty response.`);
|
|
459
|
-
}
|
|
460
|
-
break;
|
|
461
|
-
}
|
|
462
|
-
break;
|
|
463
|
-
}
|
|
464
|
-
// In autonomous mode, if we haven't done any steps yet, nudge the agent
|
|
465
|
-
if (isAutonomousMode && steps === 0) {
|
|
466
|
-
autonomousNudges++;
|
|
467
|
-
console.log(pc.yellow(`⚡ Agent replied with text only. Forcing tool usage (nudge ${autonomousNudges}/2)...`));
|
|
468
|
-
if (autonomousNudges > 2) {
|
|
469
|
-
console.log(pc.yellow('⚠️ Agent did not act after several nudges — running deterministic fallback organizer...'));
|
|
470
|
-
try {
|
|
471
|
-
runDeterministicOrganizer(targetDir);
|
|
472
|
-
}
|
|
473
|
-
catch (err) {
|
|
474
|
-
console.error('Fallback organizer failed:', err);
|
|
475
|
-
}
|
|
476
|
-
// Exit autonomous mode after fallback
|
|
477
|
-
console.log(pc.green('✅'));
|
|
478
|
-
mcpManager.disconnectAll();
|
|
479
|
-
process.exit(0);
|
|
480
|
-
}
|
|
481
|
-
ctx.addMessage('user', 'Do not just explain. Use the tools (e.g., list_dir) to execute the plan immediately.');
|
|
482
|
-
continue;
|
|
483
|
-
}
|
|
484
|
-
// In autonomous mode, show summary and exit
|
|
485
|
-
if (isAutonomousMode) {
|
|
486
|
-
console.log(`\n${pc.dim('─'.repeat(60))}`);
|
|
487
|
-
console.log(`${pc.cyan('📊 Execution Summary:')}`);
|
|
488
|
-
console.log(`${pc.dim(' Steps taken:')} ${steps}`);
|
|
489
|
-
console.log(`${pc.dim(' Final status:')} Task completed`);
|
|
490
|
-
// Autonomous Pruning on Exit
|
|
491
|
-
const brain = ctx.getTools().get('claw_brain');
|
|
492
|
-
if (brain) {
|
|
493
|
-
console.log(pc.dim('🧠 Organizing memory...'));
|
|
494
|
-
await brain.execute({ action: 'prune' });
|
|
495
|
-
}
|
|
496
|
-
console.log(`\n${pc.green('✅')} Autonomous task completed.`);
|
|
497
|
-
console.log(`${pc.dim('Exiting autonomous mode...')}`);
|
|
498
|
-
mcpManager.disconnectAll();
|
|
499
|
-
process.exit(0);
|
|
29
|
+
catch { }
|
|
500
30
|
}
|
|
501
|
-
|
|
502
|
-
isFirstPrompt = false;
|
|
31
|
+
remainingArgs.push(arg);
|
|
503
32
|
}
|
|
33
|
+
const prompt = remainingArgs.filter(a => !a.startsWith('-')).join(' ');
|
|
34
|
+
const registry = new Registry();
|
|
35
|
+
allBuiltins.forEach(t => registry.tools.set(t.name, t));
|
|
36
|
+
await registry.loadProjectTools(cwd);
|
|
37
|
+
const mcp = new MCP();
|
|
38
|
+
const provider = createLLM();
|
|
39
|
+
const engine = new Engine(provider, registry, mcp);
|
|
40
|
+
const skill = await getActiveSkill(cwd);
|
|
41
|
+
const ctx = new Context(cwd, skill);
|
|
42
|
+
showBanner();
|
|
43
|
+
await engine.run(ctx, prompt || undefined, { interactive });
|
|
504
44
|
}
|
|
45
|
+
main().catch(console.error);
|
package/dist/engine.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { MCP } from './mcp.js';
|
|
2
|
+
import { Skill } from './skills.js';
|
|
3
|
+
export interface Message {
|
|
4
|
+
role: 'user' | 'assistant' | 'system';
|
|
5
|
+
content: string;
|
|
6
|
+
}
|
|
7
|
+
export interface Tool {
|
|
8
|
+
name: string;
|
|
9
|
+
description: string;
|
|
10
|
+
execute: (args: any) => Promise<any>;
|
|
11
|
+
}
|
|
12
|
+
export declare class Context {
|
|
13
|
+
history: Message[];
|
|
14
|
+
activeFiles: Set<string>;
|
|
15
|
+
cwd: string;
|
|
16
|
+
skill: Skill;
|
|
17
|
+
constructor(cwd: string, skill: Skill);
|
|
18
|
+
buildPrompt(tools: Map<string, Tool>): Promise<string>;
|
|
19
|
+
}
|
|
20
|
+
export declare class Registry {
|
|
21
|
+
tools: Map<string, Tool>;
|
|
22
|
+
loadProjectTools(cwd: string): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
export declare class Engine {
|
|
25
|
+
private llm;
|
|
26
|
+
private registry;
|
|
27
|
+
private mcp;
|
|
28
|
+
private learningManager;
|
|
29
|
+
constructor(llm: any, registry: Registry, mcp: MCP);
|
|
30
|
+
run(ctx: Context, initialPrompt?: string, options?: {
|
|
31
|
+
interactive: boolean;
|
|
32
|
+
}): Promise<void>;
|
|
33
|
+
}
|