@blockrun/runcode 2.2.0 → 2.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.
@@ -271,6 +271,29 @@ export async function handleSlashCommand(input, ctx) {
271
271
  return { handled: false, rewritten: rewrite(arg) };
272
272
  }
273
273
  }
274
- // Not a recognized command
274
+ // Not a recognized command — suggest closest match
275
+ const allCommands = [
276
+ ...Object.keys(DIRECT_COMMANDS),
277
+ ...Object.keys(REWRITE_COMMANDS),
278
+ ...ARG_COMMANDS.map(c => c.prefix.trim()),
279
+ '/branch', '/resume', '/model', '/wallet', '/cost', '/help', '/clear', '/retry', '/exit',
280
+ ];
281
+ const cmd = input.split(/\s/)[0];
282
+ const close = allCommands.filter(c => {
283
+ // Simple distance: share >= 50% of characters
284
+ const shorter = Math.min(cmd.length, c.length);
285
+ let matches = 0;
286
+ for (let i = 0; i < shorter; i++) {
287
+ if (cmd[i] === c[i])
288
+ matches++;
289
+ }
290
+ return matches >= shorter * 0.5 && matches >= 3;
291
+ });
292
+ if (close.length > 0) {
293
+ ctx.onEvent({ kind: 'text_delta', text: `Unknown command: ${cmd}. Did you mean: ${close.slice(0, 3).join(', ')}?\n` });
294
+ emitDone(ctx);
295
+ return { handled: true };
296
+ }
297
+ // Truly unknown — pass through as regular input
275
298
  return { handled: false };
276
299
  }
package/dist/ui/app.js CHANGED
@@ -13,8 +13,8 @@ import { estimateCost } from '../pricing.js';
13
13
  function InputBox({ input, setInput, onSubmit, model, balance, focused }) {
14
14
  const { stdout } = useStdout();
15
15
  const cols = stdout?.columns ?? 80;
16
- const innerWidth = Math.max(40, cols - 4);
17
- return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, children: '╭' + '─'.repeat(cols - 2) + '╮' }), _jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: "\u2502 " }), _jsx(Box, { width: innerWidth, children: _jsx(TextInput, { value: input, onChange: setInput, onSubmit: onSubmit, placeholder: "Ask anything... (/model to switch, /help for commands)", focus: focused !== false }) }), _jsxs(Text, { dimColor: true, children: [' '.repeat(Math.max(0, cols - innerWidth - 4)), "\u2502"] })] }), _jsx(Text, { dimColor: true, children: '╰' + '─'.repeat(cols - 2) + '╯' }), _jsx(Box, { marginLeft: 1, children: _jsxs(Text, { dimColor: true, children: [model, " \u00B7 ", balance, " \u00B7 esc to abort/quit"] }) })] }));
16
+ const innerWidth = Math.min(Math.max(30, cols - 4), cols - 2);
17
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, children: '╭' + '─'.repeat(cols - 2) + '╮' }), _jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: "\u2502 " }), _jsx(Box, { width: innerWidth, children: _jsx(TextInput, { value: input, onChange: setInput, onSubmit: onSubmit, placeholder: "Ask anything... (/help for commands)", focus: focused !== false }) }), _jsxs(Text, { dimColor: true, children: [' '.repeat(Math.max(0, cols - innerWidth - 4)), "\u2502"] })] }), _jsx(Text, { dimColor: true, children: '╰' + '─'.repeat(cols - 2) + '╯' }), _jsx(Box, { marginLeft: 1, children: _jsxs(Text, { dimColor: true, children: [model, " \u00B7 ", balance, " \u00B7 esc to abort/quit"] }) })] }));
18
18
  }
19
19
  // ─── Model picker data ─────────────────────────────────────────────────────
20
20
  const PICKER_MODELS = [
@@ -167,45 +167,6 @@ function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain
167
167
  setShowHelp(true);
168
168
  setShowWallet(false);
169
169
  return;
170
- case '/commit':
171
- case '/push':
172
- case '/pr':
173
- case '/undo':
174
- case '/review':
175
- case '/test':
176
- case '/fix':
177
- case '/debug':
178
- case '/init':
179
- case '/todo':
180
- case '/deps':
181
- case '/tasks':
182
- case '/status':
183
- case '/diff':
184
- case '/log':
185
- case '/stash':
186
- case '/unstash':
187
- case '/security':
188
- case '/lint':
189
- case '/optimize':
190
- case '/migrate':
191
- case '/clean':
192
- case '/context':
193
- case '/doctor':
194
- case '/bug':
195
- case '/version':
196
- case '/plan':
197
- case '/execute':
198
- onSubmit(trimmed);
199
- setStreamText('');
200
- setWaiting(true);
201
- setReady(false);
202
- return;
203
- case '/sessions':
204
- setStreamText('');
205
- setWaiting(true);
206
- setReady(false);
207
- onSubmit('/sessions');
208
- return;
209
170
  case '/clear':
210
171
  setStreamText('');
211
172
  setTools(new Map());
@@ -228,29 +189,15 @@ function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain
228
189
  setTurnTokens({ input: 0, output: 0 });
229
190
  onSubmit(lastPrompt);
230
191
  return;
231
- case '/compact':
192
+ default:
193
+ // All other slash commands pass through to the agent loop's command registry
232
194
  setStreamText('');
233
195
  setThinking(false);
234
196
  setThinkingText('');
235
197
  setTools(new Map());
236
198
  setWaiting(true);
237
199
  setReady(false);
238
- onSubmit('/compact');
239
- return;
240
- default:
241
- // Commands with arguments that pass through to the loop
242
- if (trimmed.startsWith('/resume ') || trimmed.startsWith('/branch ')
243
- || trimmed.startsWith('/explain ') || trimmed.startsWith('/search ')
244
- || trimmed.startsWith('/find ') || trimmed.startsWith('/refactor ')
245
- || trimmed.startsWith('/scaffold ') || trimmed.startsWith('/doc ')) {
246
- setStreamText('');
247
- setWaiting(true);
248
- setReady(false);
249
- onSubmit(trimmed);
250
- return;
251
- }
252
- setStatusMsg(`Unknown command: ${cmd}. Try /help`);
253
- setTimeout(() => setStatusMsg(''), 3000);
200
+ onSubmit(trimmed);
254
201
  return;
255
202
  }
256
203
  }
@@ -270,7 +217,7 @@ function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain
270
217
  setShowWallet(false);
271
218
  setTurnTokens({ input: 0, output: 0 });
272
219
  onSubmit(trimmed);
273
- }, [currentModel, totalCost, onSubmit, onModelChange, onExit, exit]);
220
+ }, [currentModel, totalCost, onSubmit, onModelChange, onAbort, onExit, exit, lastPrompt, inputHistory]);
274
221
  // Expose event handler + balance updater
275
222
  useEffect(() => {
276
223
  globalThis.__runcode_ui = {
@@ -348,7 +295,7 @@ function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain
348
295
  // ── Normal Mode ──
349
296
  return (_jsxs(Box, { flexDirection: "column", children: [statusMsg && (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "green", children: statusMsg }) })), showHelp && (_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginTop: 1, marginBottom: 1, children: [_jsx(Text, { bold: true, children: "Commands" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/model" }), " [name] Switch model (picker if no name)"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/wallet" }), " Show wallet address & balance"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/cost" }), " Session cost & savings"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/retry" }), " Retry the last prompt"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/compact" }), " Compress conversation history"] }), _jsx(Text, { dimColor: true, children: " \u2500\u2500 Coding \u2500\u2500" }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/test" }), " Run tests"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/fix" }), " Fix last error"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/review" }), " Code review"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/explain" }), " file Explain code"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/search" }), " query Search codebase"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/refactor" }), " desc Refactor code"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/scaffold" }), " desc Generate boilerplate"] }), _jsx(Text, { dimColor: true, children: " \u2500\u2500 Git \u2500\u2500" }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/commit" }), " Commit changes"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/push" }), " Push to remote"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/pr" }), " Create pull request"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/status" }), " Git status"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/diff" }), " Git diff"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/log" }), " Git log"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/branch" }), " [name] Branches"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/stash" }), " Stash changes"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/undo" }), " Undo last commit"] }), _jsx(Text, { dimColor: true, children: " \u2500\u2500 Analysis \u2500\u2500" }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/security" }), " Security audit"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/lint" }), " Quality check"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/optimize" }), " Performance check"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/todo" }), " Find TODOs"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/deps" }), " Dependencies"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/clean" }), " Dead code removal"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/context" }), " Session info (model, tokens, mode)"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/plan" }), " Enter plan mode (read-only tools)"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/execute" }), " Exit plan mode (enable all tools)"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/sessions" }), " List saved sessions"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/resume" }), " id Resume a saved session"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/clear" }), " Clear conversation display"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/doctor" }), " Diagnose setup issues"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/help" }), " This help"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/exit" }), " Quit"] }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: " Shortcuts: sonnet, opus, gpt, gemini, deepseek, flash, free, r1, o4, nano, mini, haiku" })] })), showWallet && (_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginTop: 1, marginBottom: 1, children: [_jsx(Text, { bold: true, children: "Wallet" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [" Chain: ", _jsx(Text, { color: "magenta", children: chain })] }), _jsxs(Text, { children: [" Address: ", _jsx(Text, { color: "cyan", children: walletAddress })] }), _jsxs(Text, { children: [" Balance: ", _jsx(Text, { color: "green", children: balance })] })] })), Array.from(tools.values()).map((tool, i) => (_jsx(Box, { marginLeft: 1, children: tool.done ? (tool.error
350
297
  ? _jsxs(Text, { color: "red", children: [" \u2717 ", tool.name, " ", _jsxs(Text, { dimColor: true, children: [tool.elapsed, "ms"] })] })
351
- : _jsxs(Text, { color: "green", children: [" \u2713 ", tool.name, " ", _jsxs(Text, { dimColor: true, children: [tool.elapsed, "ms \u2014 ", tool.preview.slice(0, 200), tool.preview.length > 200 ? '...' : ''] })] })) : (_jsxs(Text, { color: "cyan", children: [" ", _jsx(Spinner, { type: "dots" }), " ", tool.name, "... ", _jsxs(Text, { dimColor: true, children: [Math.round((Date.now() - tool.startTime) / 1000), "s"] })] })) }, i))), thinking && (_jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [_jsxs(Text, { color: "magenta", children: [" ", _jsx(Spinner, { type: "dots" }), " thinking..."] }), thinkingText && (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: [" ", thinkingText.split('\n').pop()?.slice(0, 80)] }))] })), waiting && !thinking && tools.size === 0 && (_jsx(Box, { marginLeft: 1, children: _jsxs(Text, { color: "yellow", children: [" ", _jsx(Spinner, { type: "dots" }), " ", _jsx(Text, { dimColor: true, children: currentModel })] }) })), streamText && (_jsx(Box, { marginTop: 0, marginBottom: 0, children: _jsx(Text, { children: streamText }) })), ready && (turnTokens.input > 0 || turnTokens.output > 0) && streamText && (_jsx(Box, { marginLeft: 1, marginTop: 0, children: _jsxs(Text, { dimColor: true, children: [turnTokens.input.toLocaleString(), " in / ", turnTokens.output.toLocaleString(), " out", totalCost > 0 ? ` · $${totalCost.toFixed(4)} session` : ''] }) })), ready && (_jsx(InputBox, { input: input, setInput: setInput, onSubmit: handleSubmit, model: currentModel, balance: balance, focused: mode === 'input' }))] }));
298
+ : _jsxs(Text, { color: "green", children: [" \u2713 ", tool.name, " ", _jsxs(Text, { dimColor: true, children: [tool.elapsed, "ms \u2014 ", tool.preview.slice(0, 200), tool.preview.length > 200 ? '...' : ''] })] })) : (_jsxs(Text, { color: "cyan", children: [" ", _jsx(Spinner, { type: "dots" }), " ", tool.name, "... ", _jsx(Text, { dimColor: true, children: (() => { const s = Math.round((Date.now() - tool.startTime) / 1000); return s > 0 ? `${s}s` : ''; })() })] })) }, i))), thinking && (_jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [_jsxs(Text, { color: "magenta", children: [" ", _jsx(Spinner, { type: "dots" }), " thinking..."] }), thinkingText && (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: [" ", thinkingText.split('\n').pop()?.slice(0, 80)] }))] })), waiting && !thinking && tools.size === 0 && (_jsx(Box, { marginLeft: 1, children: _jsxs(Text, { color: "yellow", children: [" ", _jsx(Spinner, { type: "dots" }), " ", _jsx(Text, { dimColor: true, children: currentModel })] }) })), streamText && (_jsx(Box, { marginTop: 0, marginBottom: 0, children: _jsx(Text, { children: streamText }) })), ready && (turnTokens.input > 0 || turnTokens.output > 0) && streamText && (_jsx(Box, { marginLeft: 1, marginTop: 0, children: _jsxs(Text, { dimColor: true, children: [turnTokens.input.toLocaleString(), " in / ", turnTokens.output.toLocaleString(), " out", totalCost > 0 ? ` · $${totalCost.toFixed(4)} session` : ''] }) })), ready && (_jsx(InputBox, { input: input, setInput: setInput, onSubmit: handleSubmit, model: currentModel, balance: balance, focused: mode === 'input' }))] }));
352
299
  }
353
300
  export function launchInkUI(opts) {
354
301
  let resolveInput = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/runcode",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "RunCode — AI coding agent powered by 41+ models. Pay per use with USDC.",
5
5
  "type": "module",
6
6
  "bin": {