codehere 0.3.0-rc.8 → 0.3.0-rc.9
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/dist/presentation/cli/repl.js +162 -34
- package/package.json +1 -1
|
@@ -26,16 +26,42 @@ import readline from 'readline';
|
|
|
26
26
|
import { colors, newline } from '../../ui.js';
|
|
27
27
|
import { getVersion } from '../../utils/version.js';
|
|
28
28
|
import { SessionManager, availableAgents } from '../../infrastructure/orchestration/session-manager.js';
|
|
29
|
+
const SLASH_COMMANDS = [
|
|
30
|
+
// Meta · don't spawn agents · run locally
|
|
31
|
+
{ name: 'agents', aliases: ['a'], description: 'list installed specialists + routing rules', action: 'meta' },
|
|
32
|
+
{ name: 'status', aliases: ['s'], description: 'last 24h activity from the audit log', action: 'meta' },
|
|
33
|
+
{ name: 'history', aliases: ['hist'], description: 'recent prompts in this session', action: 'meta' },
|
|
34
|
+
{ name: 'web', description: 'launch the web UI at :3773 (in another terminal)', action: 'meta' },
|
|
35
|
+
{ name: 'clear', description: 'clear the screen', action: 'meta' },
|
|
36
|
+
{ name: 'help', aliases: ['?'], description: 'show this help', action: 'meta' },
|
|
37
|
+
{ name: 'quit', aliases: ['exit', 'q'], description: 'exit (Ctrl+C / Ctrl+D also work)', action: 'meta' },
|
|
38
|
+
// Direct-route · skip the Conductor and send the prompt straight to a specific specialist
|
|
39
|
+
{ name: 'codex', args: '<prompt>', description: 'send to Codex specifically (skip Conductor)', action: 'route', targetAgent: 'codex' },
|
|
40
|
+
{ name: 'claude', args: '<prompt>', aliases: ['cc'], description: 'send to Claude Code (skip Conductor)', action: 'route', targetAgent: 'claude-code' },
|
|
41
|
+
{ name: 'aider', args: '<prompt>', description: 'send to Aider (skip Conductor)', action: 'route', targetAgent: 'aider' },
|
|
42
|
+
{ name: 'opencode', args: '<prompt>', aliases: ['oc'], description: 'send to OpenCode (skip Conductor)', action: 'route', targetAgent: 'opencode' },
|
|
43
|
+
];
|
|
44
|
+
function findSlashCommand(input) {
|
|
45
|
+
const parts = input.trim().split(/\s+/);
|
|
46
|
+
const head = parts[0].toLowerCase();
|
|
47
|
+
for (const cmd of SLASH_COMMANDS) {
|
|
48
|
+
if (cmd.name === head || (cmd.aliases ?? []).includes(head)) {
|
|
49
|
+
return { command: cmd, args: parts.slice(1).join(' ').trim() };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
29
54
|
export async function runInteractiveRepl(options = {}) {
|
|
30
55
|
const cwd = options.cwd ?? process.cwd();
|
|
56
|
+
const history = [];
|
|
31
57
|
newline();
|
|
32
58
|
console.log(colors.bold('codehere ') + colors.dim('v' + getVersion()));
|
|
33
|
-
console.log(colors.dim('Talk to your AI coding agents. Type a task
|
|
59
|
+
console.log(colors.dim('Talk to your AI coding agents. Type a task to delegate; or use a /command.'));
|
|
34
60
|
newline();
|
|
35
|
-
console.log(colors.dim(' /
|
|
36
|
-
console.log(colors.dim(' /
|
|
37
|
-
console.log(colors.dim(' /
|
|
38
|
-
console.log(colors.dim('
|
|
61
|
+
console.log(colors.dim(' /help show all slash commands'));
|
|
62
|
+
console.log(colors.dim(' /agents installed specialists + routing rules'));
|
|
63
|
+
console.log(colors.dim(' /codex <…> skip the Conductor — send straight to a specific specialist'));
|
|
64
|
+
console.log(colors.dim(' ↑/↓ history · Ctrl+L to clear · Ctrl+C / Ctrl+D to exit'));
|
|
39
65
|
newline();
|
|
40
66
|
const installed = availableAgents().filter((a) => a.installed);
|
|
41
67
|
if (installed.length === 0) {
|
|
@@ -57,9 +83,10 @@ export async function runInteractiveRepl(options = {}) {
|
|
|
57
83
|
output: process.stdout,
|
|
58
84
|
prompt: colors.cyan('› '),
|
|
59
85
|
terminal: true,
|
|
86
|
+
historySize: 200,
|
|
60
87
|
});
|
|
61
88
|
const manager = SessionManager.getInstance();
|
|
62
|
-
const
|
|
89
|
+
const promptNext = () => {
|
|
63
90
|
rl.setPrompt(colors.cyan('› '));
|
|
64
91
|
rl.prompt();
|
|
65
92
|
};
|
|
@@ -69,46 +96,82 @@ export async function runInteractiveRepl(options = {}) {
|
|
|
69
96
|
process.exit(0);
|
|
70
97
|
});
|
|
71
98
|
rl.on('SIGINT', () => {
|
|
72
|
-
// Ctrl+C: if we're idle at the prompt, exit; if a task is mid-flight the
|
|
73
|
-
// task's own kill hook handles it via session.kill() (TODO: wire up).
|
|
74
99
|
rl.close();
|
|
75
100
|
});
|
|
76
|
-
|
|
101
|
+
promptNext();
|
|
77
102
|
for await (const line of rl) {
|
|
78
103
|
const trimmed = line.trim();
|
|
79
104
|
if (!trimmed) {
|
|
80
|
-
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
if (trimmed === '/quit' || trimmed === '/exit') {
|
|
84
|
-
rl.close();
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
if (trimmed === '/agents') {
|
|
88
|
-
printAgentsAndRoutingRules();
|
|
89
|
-
prompt();
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
if (trimmed === '/status') {
|
|
93
|
-
await printStatusSummary();
|
|
94
|
-
prompt();
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
if (trimmed === '/web' || trimmed === '/start') {
|
|
98
|
-
console.log(colors.dim(' Tip: run ') + colors.cyan('codehere start') + colors.dim(' in another terminal to launch the web UI.'));
|
|
99
|
-
prompt();
|
|
105
|
+
promptNext();
|
|
100
106
|
continue;
|
|
101
107
|
}
|
|
108
|
+
history.push(trimmed);
|
|
109
|
+
// Slash commands first
|
|
102
110
|
if (trimmed.startsWith('/')) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
111
|
+
const found = findSlashCommand(trimmed.slice(1));
|
|
112
|
+
if (!found) {
|
|
113
|
+
console.log(colors.yellow(` Unknown command: ${trimmed.split(/\s+/)[0]}`));
|
|
114
|
+
console.log(colors.dim(' Run /help to see all commands.'));
|
|
115
|
+
promptNext();
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const { command, args } = found;
|
|
119
|
+
switch (command.name) {
|
|
120
|
+
case 'quit':
|
|
121
|
+
rl.close();
|
|
122
|
+
return;
|
|
123
|
+
case 'help':
|
|
124
|
+
printSlashCommandHelp();
|
|
125
|
+
break;
|
|
126
|
+
case 'clear':
|
|
127
|
+
console.clear();
|
|
128
|
+
newline();
|
|
129
|
+
console.log(colors.bold('codehere ') + colors.dim('v' + getVersion()));
|
|
130
|
+
newline();
|
|
131
|
+
break;
|
|
132
|
+
case 'agents':
|
|
133
|
+
printAgentsAndRoutingRules();
|
|
134
|
+
break;
|
|
135
|
+
case 'status':
|
|
136
|
+
await printStatusSummary();
|
|
137
|
+
break;
|
|
138
|
+
case 'history':
|
|
139
|
+
printHistory(history);
|
|
140
|
+
break;
|
|
141
|
+
case 'web':
|
|
142
|
+
console.log(colors.dim(' Tip: run ') +
|
|
143
|
+
colors.cyan('codehere start') +
|
|
144
|
+
colors.dim(' in another terminal to launch the web UI at http://127.0.0.1:3773'));
|
|
145
|
+
break;
|
|
146
|
+
default: {
|
|
147
|
+
// Direct-route slash command (/codex, /claude, /aider, /opencode)
|
|
148
|
+
if (command.action !== 'route' || !command.targetAgent)
|
|
149
|
+
break;
|
|
150
|
+
if (!args) {
|
|
151
|
+
console.log(colors.yellow(` Usage: /${command.name} ${command.args ?? '<prompt>'}`));
|
|
152
|
+
console.log(colors.dim(` Example: /${command.name} review src/auth.ts`));
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
if (!installed.some((a) => a.id === command.targetAgent)) {
|
|
156
|
+
console.log(colors.yellow(` ${command.targetAgent} is not installed. Install it or use a different /command.`));
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
await runTaskWithAgent(args, cwd, manager, command.targetAgent);
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
console.log(colors.red(' ✗ ') + (err instanceof Error ? err.message : String(err)));
|
|
164
|
+
}
|
|
165
|
+
newline();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
promptNext();
|
|
106
169
|
continue;
|
|
107
170
|
}
|
|
108
171
|
// Plain text → delegate via Conductor
|
|
109
172
|
if (installed.length === 0) {
|
|
110
173
|
console.log(colors.yellow(' No specialist installed — install one and try again.'));
|
|
111
|
-
|
|
174
|
+
promptNext();
|
|
112
175
|
continue;
|
|
113
176
|
}
|
|
114
177
|
try {
|
|
@@ -118,8 +181,73 @@ export async function runInteractiveRepl(options = {}) {
|
|
|
118
181
|
console.log(colors.red(' ✗ ') + (err instanceof Error ? err.message : String(err)));
|
|
119
182
|
}
|
|
120
183
|
newline();
|
|
121
|
-
|
|
184
|
+
promptNext();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Run a task against a specific specialist (skipping the Conductor).
|
|
189
|
+
* Used by /codex, /claude, /aider, /opencode slash commands.
|
|
190
|
+
*/
|
|
191
|
+
async function runTaskWithAgent(prompt, cwd, manager, agentId) {
|
|
192
|
+
const session = manager.spawn(prompt, agentId, { cwd });
|
|
193
|
+
const started = colors.dim(' ┌─ ') +
|
|
194
|
+
colors.bold(session.sessionId.slice(0, 8)) +
|
|
195
|
+
colors.dim(' · ' + agentId + ' · ' + new Date(session.startedAt).toLocaleTimeString());
|
|
196
|
+
console.log(started);
|
|
197
|
+
await new Promise((resolve) => {
|
|
198
|
+
const unsubscribe = session.subscribe((event) => {
|
|
199
|
+
renderEvent(event);
|
|
200
|
+
if (event.type === 'codehere:complete' || event.type === 'codehere:error' || event.type === 'exit') {
|
|
201
|
+
unsubscribe();
|
|
202
|
+
resolve();
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
const finalStatus = session.status;
|
|
207
|
+
const verdict = finalStatus === 'completed'
|
|
208
|
+
? colors.green(' └─ done')
|
|
209
|
+
: finalStatus === 'killed'
|
|
210
|
+
? colors.yellow(' └─ killed')
|
|
211
|
+
: colors.red(' └─ ' + finalStatus);
|
|
212
|
+
console.log(verdict);
|
|
213
|
+
}
|
|
214
|
+
function printSlashCommandHelp() {
|
|
215
|
+
newline();
|
|
216
|
+
console.log(colors.bold('Slash commands'));
|
|
217
|
+
console.log(colors.dim('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
218
|
+
newline();
|
|
219
|
+
console.log(colors.bold(' Meta'));
|
|
220
|
+
for (const cmd of SLASH_COMMANDS.filter((c) => c.action === 'meta')) {
|
|
221
|
+
const aliases = cmd.aliases?.length ? colors.dim(' (' + cmd.aliases.map((a) => '/' + a).join(', ') + ')') : '';
|
|
222
|
+
const cmdLabel = '/' + cmd.name + (cmd.args ? ' ' + cmd.args : '');
|
|
223
|
+
console.log(' ' + colors.cyan(cmdLabel.padEnd(28)) + colors.dim(cmd.description) + aliases);
|
|
224
|
+
}
|
|
225
|
+
newline();
|
|
226
|
+
console.log(colors.bold(' Direct route') + colors.dim(' — skip the Conductor, send straight to a specialist'));
|
|
227
|
+
for (const cmd of SLASH_COMMANDS.filter((c) => c.action === 'route')) {
|
|
228
|
+
const aliases = cmd.aliases?.length ? colors.dim(' (' + cmd.aliases.map((a) => '/' + a).join(', ') + ')') : '';
|
|
229
|
+
const cmdLabel = '/' + cmd.name + (cmd.args ? ' ' + cmd.args : '');
|
|
230
|
+
console.log(' ' + colors.cyan(cmdLabel.padEnd(28)) + colors.dim(cmd.description) + aliases);
|
|
231
|
+
}
|
|
232
|
+
newline();
|
|
233
|
+
console.log(colors.bold(' Or just type'));
|
|
234
|
+
console.log(colors.dim(' Plain text in any other shape → Conductor routes to the right specialist'));
|
|
235
|
+
console.log(colors.dim(' Examples: "review src/auth.ts" → claude-code · "fix the race in payments" → aider'));
|
|
236
|
+
newline();
|
|
237
|
+
}
|
|
238
|
+
function printHistory(history) {
|
|
239
|
+
newline();
|
|
240
|
+
if (history.length === 0) {
|
|
241
|
+
console.log(colors.dim(' No prompts yet in this session.'));
|
|
242
|
+
newline();
|
|
243
|
+
return;
|
|
122
244
|
}
|
|
245
|
+
console.log(colors.bold(' Recent prompts:'));
|
|
246
|
+
history.slice(-10).forEach((h, i) => {
|
|
247
|
+
const preview = h.length > 70 ? h.slice(0, 67) + '…' : h;
|
|
248
|
+
console.log(colors.dim(` ${(i + 1).toString().padStart(2, ' ')}. `) + preview);
|
|
249
|
+
});
|
|
250
|
+
newline();
|
|
123
251
|
}
|
|
124
252
|
/**
|
|
125
253
|
* Run a single task and exit — bypass the REPL prompt loop.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codehere",
|
|
3
|
-
"version": "0.3.0-rc.
|
|
3
|
+
"version": "0.3.0-rc.9",
|
|
4
4
|
"description": "A PM for your AI coding agents. Delegate, orchestrate, and audit Claude Code, Codex, Aider, and OpenCode from one local web UI — every AI action traced, every file scored.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|