klyro 0.1.44 → 0.1.45

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.
@@ -127,6 +127,180 @@ export type SlashCommand = {
127
127
  } | {
128
128
  kind: 'copy';
129
129
  n?: string;
130
+ } | {
131
+ kind: 'review';
132
+ target?: string;
133
+ } | {
134
+ kind: 'code-review';
135
+ options?: string;
136
+ } | {
137
+ kind: 'security-review';
138
+ } | {
139
+ kind: 'simplify';
140
+ target?: string;
141
+ } | {
142
+ kind: 'test';
143
+ target?: string;
144
+ } | {
145
+ kind: 'lint';
146
+ } | {
147
+ kind: 'build';
148
+ } | {
149
+ kind: 'run';
150
+ command: string;
151
+ } | {
152
+ kind: 'fix';
153
+ target?: string;
154
+ } | {
155
+ kind: 'explain';
156
+ target?: string;
157
+ } | {
158
+ kind: 'format';
159
+ } | {
160
+ kind: 'ask';
161
+ question: string;
162
+ } | {
163
+ kind: 'redo';
164
+ } | {
165
+ kind: 'checkpoint';
166
+ } | {
167
+ kind: 'accept';
168
+ } | {
169
+ kind: 'reject';
170
+ } | {
171
+ kind: 'details';
172
+ } | {
173
+ kind: 'verbose';
174
+ } | {
175
+ kind: 'raw';
176
+ } | {
177
+ kind: 'activity';
178
+ } | {
179
+ kind: 'tasks';
180
+ } | {
181
+ kind: 'ps';
182
+ } | {
183
+ kind: 'stop';
184
+ id?: string;
185
+ } | {
186
+ kind: 'queue';
187
+ } | {
188
+ kind: 'retry';
189
+ } | {
190
+ kind: 'kill';
191
+ id?: string;
192
+ } | {
193
+ kind: 'mcp';
194
+ sub?: string;
195
+ } | {
196
+ kind: 'agents';
197
+ } | {
198
+ kind: 'agent';
199
+ name: string;
200
+ } | {
201
+ kind: 'subagents';
202
+ } | {
203
+ kind: 'subtask';
204
+ task: string;
205
+ } | {
206
+ kind: 'background';
207
+ task?: string;
208
+ } | {
209
+ kind: 'add-dir';
210
+ path: string;
211
+ } | {
212
+ kind: 'cd';
213
+ path: string;
214
+ } | {
215
+ kind: 'attach';
216
+ file: string;
217
+ } | {
218
+ kind: 'drop';
219
+ file?: string;
220
+ } | {
221
+ kind: 'image';
222
+ path?: string;
223
+ } | {
224
+ kind: 'paste';
225
+ } | {
226
+ kind: 'files';
227
+ } | {
228
+ kind: 'ls';
229
+ path?: string;
230
+ } | {
231
+ kind: 'tree';
232
+ path?: string;
233
+ } | {
234
+ kind: 'search';
235
+ query: string;
236
+ } | {
237
+ kind: 'web';
238
+ url: string;
239
+ } | {
240
+ kind: 'read';
241
+ path: string;
242
+ } | {
243
+ kind: 'map';
244
+ } | {
245
+ kind: 'tokens';
246
+ } | {
247
+ kind: 'commit';
248
+ message?: string;
249
+ } | {
250
+ kind: 'push';
251
+ } | {
252
+ kind: 'pull';
253
+ } | {
254
+ kind: 'pr';
255
+ args?: string;
256
+ } | {
257
+ kind: 'issue';
258
+ id?: string;
259
+ } | {
260
+ kind: 'editor';
261
+ file?: string;
262
+ } | {
263
+ kind: 'keymap';
264
+ name: string;
265
+ } | {
266
+ kind: 'vim';
267
+ state: string;
268
+ } | {
269
+ kind: 'theme';
270
+ name: string;
271
+ } | {
272
+ kind: 'statusline';
273
+ format: string;
274
+ } | {
275
+ kind: 'output-style';
276
+ style: string;
277
+ } | {
278
+ kind: 'debug';
279
+ } | {
280
+ kind: 'whoami';
281
+ } | {
282
+ kind: 'reload';
283
+ } | {
284
+ kind: 'reset';
285
+ } | {
286
+ kind: 'bug';
287
+ } | {
288
+ kind: 'changelog';
289
+ } | {
290
+ kind: 'promptcmd';
291
+ args: string;
292
+ } | {
293
+ kind: 'alias';
294
+ args: string;
295
+ } | {
296
+ kind: 'commands';
297
+ } | {
298
+ kind: 'env';
299
+ args?: string;
300
+ } | {
301
+ kind: 'deps';
302
+ } | {
303
+ kind: 'install';
130
304
  } | {
131
305
  kind: 'prompt';
132
306
  text: string;
@@ -136,3 +310,11 @@ export type SlashCommand = {
136
310
  };
137
311
  export declare function parse(input: string): SlashCommand;
138
312
  export declare function listCommands(): string[];
313
+ export interface CommandDef {
314
+ name: string;
315
+ hint: string;
316
+ }
317
+ /** Curated canonical commands with one-line hints — drives /help, /commands and TUI autocomplete. */
318
+ export declare const COMMAND_DEFS: CommandDef[];
319
+ /** Prefix-match command names for TUI autocomplete — top `limit` (default 6). */
320
+ export declare function suggestCommands(prefix: string, limit?: number): CommandDef[];
@@ -14,7 +14,7 @@
14
14
  * Anything not starting with "/" is a regular prompt and yields
15
15
  * { kind: 'prompt', text }.
16
16
  */
17
- const KNOWN = ['help', 'clear', 'new', 'exit', 'quit', 'q', 'compact', 'resume', 'sessions', 'rename', 'fork', 'branch', 'export', 'copy', 'model', 'm', 'models', 'provider', 'p', 'effort', 'e', 'fast', 'init', 'status', 'context', 'diff', 'plan', 'todos', 'memory', 'permissions', 'mode', 'sandbox', 'approve', 'deny', 'login', 'logout', 'auth', 'version', 'update', 'cancel', 'shell', 'mention', 'tools', 'config', 'settings', 'doctor', 'version', 'cost', 'thinking', 'jobs', 'verify', 'project', 'undo', 'rewind'];
17
+ const KNOWN = ['help', 'clear', 'new', 'exit', 'quit', 'q', 'compact', 'resume', 'sessions', 'rename', 'fork', 'branch', 'export', 'copy', 'model', 'm', 'models', 'provider', 'p', 'effort', 'e', 'fast', 'init', 'status', 'context', 'diff', 'plan', 'todos', 'memory', 'permissions', 'mode', 'sandbox', 'approve', 'deny', 'login', 'logout', 'auth', 'version', 'update', 'cancel', 'shell', 'mention', 'tools', 'config', 'settings', 'doctor', 'cost', 'thinking', 'jobs', 'verify', 'project', 'undo', 'rewind', 'review', 'code-review', 'security-review', 'simplify', 'test', 'lint', 'build', 'run', 'fix', 'explain', 'format', 'ask', 'redo', 'checkpoint', 'accept', 'reject', 'details', 'verbose', 'raw', 'activity', 'tasks', 'ps', 'stop', 'queue', 'retry', 'kill', 'mcp', 'agents', 'agent', 'subagents', 'subtask', 'background', 'add-dir', 'cd', 'attach', 'drop', 'image', 'paste', 'files', 'ls', 'tree', 'search', 'web', 'read', 'map', 'tokens', 'commit', 'push', 'pull', 'pr', 'issue', 'editor', 'keymap', 'vim', 'theme', 'statusline', 'output-style', 'debug', 'whoami', 'reload', 'reset', 'bug', 'changelog', 'prompt', 'alias', 'commands', 'env', 'deps', 'install'];
18
18
  export function parse(input) {
19
19
  const trimmed = input.trim();
20
20
  // `!cmd` alias for /shell, `@path` alias for /mention (commands.md P1)
@@ -94,9 +94,217 @@ export function parse(input) {
94
94
  case 'm': {
95
95
  return { kind: 'model', model: rest };
96
96
  }
97
+ // Priority 2 — developer workflow
98
+ case 'review': return { kind: 'review', target: rest || undefined };
99
+ case 'code-review': return { kind: 'code-review', options: rest || undefined };
100
+ case 'security-review': return { kind: 'security-review' };
101
+ case 'simplify': return { kind: 'simplify', target: rest || undefined };
102
+ case 'test': return { kind: 'test', target: rest || undefined };
103
+ case 'lint': return { kind: 'lint' };
104
+ case 'build': return { kind: 'build' };
105
+ case 'run': return { kind: 'run', command: rest };
106
+ case 'fix': return { kind: 'fix', target: rest || undefined };
107
+ case 'explain': return { kind: 'explain', target: rest || undefined };
108
+ case 'format': return { kind: 'format' };
109
+ case 'ask': return { kind: 'ask', question: rest };
110
+ // Priority 2 — checkpoints / edit loop
111
+ case 'redo': return { kind: 'redo' };
112
+ case 'checkpoint':
113
+ case 'snapshot': return { kind: 'checkpoint' };
114
+ case 'accept': return { kind: 'accept' };
115
+ case 'reject': return { kind: 'reject' };
116
+ // Priority 2 — activity controls
117
+ case 'details': return { kind: 'details' };
118
+ case 'verbose': return { kind: 'verbose' };
119
+ case 'raw': return { kind: 'raw' };
120
+ case 'activity': return { kind: 'activity' };
121
+ case 'tasks': return { kind: 'tasks' };
122
+ case 'ps': return { kind: 'ps' };
123
+ case 'stop': return { kind: 'stop', id: rest || undefined };
124
+ case 'queue': return { kind: 'queue' };
125
+ case 'retry': return { kind: 'retry' };
126
+ case 'kill': return { kind: 'kill', id: rest || undefined };
127
+ // Priority 2 — MCP / agents
128
+ case 'mcp': return { kind: 'mcp', sub: rest || undefined };
129
+ case 'agents': return { kind: 'agents' };
130
+ case 'agent': return { kind: 'agent', name: rest };
131
+ case 'subagents': return { kind: 'subagents' };
132
+ case 'subtask': return { kind: 'subtask', task: rest };
133
+ case 'background':
134
+ case 'bg': return { kind: 'background', task: rest || undefined };
135
+ // Priority 2 — files / context
136
+ case 'add-dir': return { kind: 'add-dir', path: rest };
137
+ case 'cd': return { kind: 'cd', path: rest };
138
+ case 'attach': return { kind: 'attach', file: rest };
139
+ case 'drop': return { kind: 'drop', file: rest || undefined };
140
+ case 'image': return { kind: 'image', path: rest || undefined };
141
+ case 'paste': return { kind: 'paste' };
142
+ case 'files': return { kind: 'files' };
143
+ case 'ls': return { kind: 'ls', path: rest || undefined };
144
+ case 'tree': return { kind: 'tree', path: rest || undefined };
145
+ case 'search': return { kind: 'search', query: rest };
146
+ case 'web': return { kind: 'web', url: rest };
147
+ case 'read': return { kind: 'read', path: rest };
148
+ case 'map': return { kind: 'map' };
149
+ case 'tokens': return { kind: 'tokens' };
150
+ // Priority 2 — git workflow
151
+ case 'commit': return { kind: 'commit', message: rest || undefined };
152
+ case 'push': return { kind: 'push' };
153
+ case 'pull': return { kind: 'pull' };
154
+ case 'pr': return { kind: 'pr', args: rest || undefined };
155
+ case 'issue': return { kind: 'issue', id: rest || undefined };
156
+ // Priority 2 — config / UI
157
+ case 'editor': return { kind: 'editor', file: rest || undefined };
158
+ case 'keymap': return { kind: 'keymap', name: rest };
159
+ case 'vim': return { kind: 'vim', state: rest };
160
+ case 'theme': return { kind: 'theme', name: rest };
161
+ case 'statusline': return { kind: 'statusline', format: rest };
162
+ case 'output-style': return { kind: 'output-style', style: rest };
163
+ // Priority 2 — diagnostics / misc
164
+ case 'debug': return { kind: 'debug' };
165
+ case 'whoami': return { kind: 'whoami' };
166
+ case 'reload': return { kind: 'reload' };
167
+ case 'reset': return { kind: 'reset' };
168
+ case 'bug': return { kind: 'bug' };
169
+ case 'changelog': return { kind: 'changelog' };
170
+ case 'prompt': return { kind: 'promptcmd', args: rest };
171
+ case 'alias': return { kind: 'alias', args: rest };
172
+ case 'commands': return { kind: 'commands' };
173
+ case 'env': return { kind: 'env', args: rest || undefined };
174
+ case 'deps': return { kind: 'deps' };
175
+ case 'install': return { kind: 'install' };
97
176
  default: return { kind: 'unknown', raw: trimmed };
98
177
  }
99
178
  }
100
179
  export function listCommands() {
101
180
  return [...KNOWN];
102
181
  }
182
+ /** Curated canonical commands with one-line hints — drives /help, /commands and TUI autocomplete. */
183
+ export const COMMAND_DEFS = [
184
+ { name: 'help', hint: 'show all commands' },
185
+ { name: 'clear', hint: 'clear transcript' },
186
+ { name: 'new', hint: 'start a new session' },
187
+ { name: 'exit', hint: 'exit Klyro' },
188
+ { name: 'compact', hint: 'compact context [focus]' },
189
+ { name: 'resume', hint: 'resume previous session' },
190
+ { name: 'sessions', hint: 'list sessions' },
191
+ { name: 'rename', hint: 'rename current session' },
192
+ { name: 'fork', hint: 'fork conversation' },
193
+ { name: 'branch', hint: 'create/switch branch' },
194
+ { name: 'export', hint: 'export conversation' },
195
+ { name: 'copy', hint: 'copy last response' },
196
+ { name: 'model', hint: 'show/switch model' },
197
+ { name: 'models', hint: 'list available models' },
198
+ { name: 'provider', hint: 'show/switch provider' },
199
+ { name: 'effort', hint: 'set reasoning effort' },
200
+ { name: 'fast', hint: 'toggle fast mode' },
201
+ { name: 'init', hint: 'create KLYRO.md' },
202
+ { name: 'status', hint: 'session/model status' },
203
+ { name: 'context', hint: 'context usage' },
204
+ { name: 'diff', hint: 'show git diff' },
205
+ { name: 'plan', hint: 'show/enter plan' },
206
+ { name: 'todos', hint: 'show task state' },
207
+ { name: 'memory', hint: 'view memory' },
208
+ { name: 'permissions', hint: 'permission rules' },
209
+ { name: 'mode', hint: 'permission mode' },
210
+ { name: 'sandbox', hint: 'sandbox policy' },
211
+ { name: 'approve', hint: 'approve blocked action' },
212
+ { name: 'deny', hint: 'deny blocked action' },
213
+ { name: 'login', hint: 'authenticate' },
214
+ { name: 'logout', hint: 'sign out' },
215
+ { name: 'auth', hint: 'auth status' },
216
+ { name: 'version', hint: 'show version' },
217
+ { name: 'update', hint: 'update CLI' },
218
+ { name: 'cancel', hint: 'interrupt operation' },
219
+ { name: 'shell', hint: 'run shell (!cmd)' },
220
+ { name: 'mention', hint: 'attach file (@path)' },
221
+ { name: 'tools', hint: 'list tools' },
222
+ { name: 'config', hint: 'open configuration' },
223
+ { name: 'doctor', hint: 'health check' },
224
+ { name: 'review', hint: 'review changes' },
225
+ { name: 'code-review', hint: 'deep code review' },
226
+ { name: 'security-review', hint: 'security review' },
227
+ { name: 'simplify', hint: 'simplify changes' },
228
+ { name: 'test', hint: 'run tests' },
229
+ { name: 'lint', hint: 'run linting' },
230
+ { name: 'build', hint: 'run build' },
231
+ { name: 'run', hint: 'run a command' },
232
+ { name: 'fix', hint: 'fix errors/tests' },
233
+ { name: 'explain', hint: 'explain code' },
234
+ { name: 'format', hint: 'format code' },
235
+ { name: 'ask', hint: 'read-only Q&A' },
236
+ { name: 'undo', hint: 'undo change' },
237
+ { name: 'redo', hint: 'redo change' },
238
+ { name: 'rewind', hint: 'rewind code' },
239
+ { name: 'checkpoint', hint: 'create checkpoint' },
240
+ { name: 'accept', hint: 'accept edits' },
241
+ { name: 'reject', hint: 'reject edits' },
242
+ { name: 'details', hint: 'toggle details' },
243
+ { name: 'verbose', hint: 'toggle verbose' },
244
+ { name: 'raw', hint: 'toggle raw output' },
245
+ { name: 'activity', hint: 'agent activity' },
246
+ { name: 'tasks', hint: 'background tasks' },
247
+ { name: 'ps', hint: 'background processes' },
248
+ { name: 'stop', hint: 'stop process' },
249
+ { name: 'queue', hint: 'queued prompts' },
250
+ { name: 'retry', hint: 'retry last action' },
251
+ { name: 'kill', hint: 'kill process' },
252
+ { name: 'mcp', hint: 'MCP servers' },
253
+ { name: 'agents', hint: 'manage agents' },
254
+ { name: 'agent', hint: 'switch agent' },
255
+ { name: 'subagents', hint: 'list subagents' },
256
+ { name: 'subtask', hint: 'spawn subagent' },
257
+ { name: 'background', hint: 'run in background' },
258
+ { name: 'add-dir', hint: 'add directory' },
259
+ { name: 'cd', hint: 'change directory' },
260
+ { name: 'attach', hint: 'attach file' },
261
+ { name: 'drop', hint: 'drop file' },
262
+ { name: 'image', hint: 'attach image' },
263
+ { name: 'paste', hint: 'paste clipboard' },
264
+ { name: 'files', hint: 'files in context' },
265
+ { name: 'ls', hint: 'list files' },
266
+ { name: 'tree', hint: 'file tree' },
267
+ { name: 'search', hint: 'search codebase' },
268
+ { name: 'web', hint: 'fetch URL' },
269
+ { name: 'read', hint: 'read file' },
270
+ { name: 'map', hint: 'repository map' },
271
+ { name: 'tokens', hint: 'token usage' },
272
+ { name: 'commit', hint: 'git commit' },
273
+ { name: 'push', hint: 'git push' },
274
+ { name: 'pull', hint: 'git pull' },
275
+ { name: 'pr', hint: 'pull request' },
276
+ { name: 'issue', hint: 'github issue' },
277
+ { name: 'editor', hint: 'external editor' },
278
+ { name: 'keymap', hint: 'keyboard shortcuts' },
279
+ { name: 'vim', hint: 'vim input mode' },
280
+ { name: 'theme', hint: 'configure theme' },
281
+ { name: 'statusline', hint: 'status bar format' },
282
+ { name: 'output-style', hint: 'output style' },
283
+ { name: 'debug', hint: 'debug info' },
284
+ { name: 'whoami', hint: 'identity' },
285
+ { name: 'reload', hint: 'reload config' },
286
+ { name: 'reset', hint: 'reset settings' },
287
+ { name: 'bug', hint: 'report a bug' },
288
+ { name: 'changelog', hint: 'release notes' },
289
+ { name: 'prompt', hint: 'saved prompts' },
290
+ { name: 'alias', hint: 'command aliases' },
291
+ { name: 'commands', hint: 'custom commands' },
292
+ { name: 'env', hint: 'environment vars' },
293
+ { name: 'deps', hint: 'dependencies' },
294
+ { name: 'install', hint: 'install deps' },
295
+ ];
296
+ /** Prefix-match command names for TUI autocomplete — top `limit` (default 6). */
297
+ export function suggestCommands(prefix, limit = 6) {
298
+ const p = prefix.toLowerCase().replace(/^\//, '');
299
+ if (!p)
300
+ return COMMAND_DEFS.slice(0, limit);
301
+ const starts = [];
302
+ const contains = [];
303
+ for (const d of COMMAND_DEFS) {
304
+ if (d.name.startsWith(p))
305
+ starts.push(d);
306
+ else if (d.name.includes(p))
307
+ contains.push(d);
308
+ }
309
+ return [...starts, ...contains].slice(0, limit);
310
+ }
package/dist/tui/app.js CHANGED
@@ -5,19 +5,21 @@ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
5
5
  */
6
6
  import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
7
7
  import { Box, Text, useInput, useStdout } from 'ink';
8
+ import { execFileSync } from 'node:child_process';
8
9
  import { TuiApprovalBridge } from './approval.js';
9
- import { parse as parseSlash } from '../cli/slash/parser.js';
10
+ import { parse as parseSlash, suggestCommands } from '../cli/slash/parser.js';
10
11
  import { tokens, g } from './tokens.js';
11
12
  let _id = 0;
12
13
  function nextId(p) { _id++; return `${p}-${_id}`; }
13
14
  function Header({ cwd, model, version, width }) {
14
- const branch = (() => { try {
15
- const { execSync } = require('node:child_process');
16
- return execSync('git rev-parse --abbrev-ref HEAD', { cwd, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
17
- }
18
- catch {
19
- return '';
20
- } })();
15
+ const branch = useMemo(() => {
16
+ try {
17
+ return execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
18
+ }
19
+ catch {
20
+ return '';
21
+ }
22
+ }, [cwd]);
21
23
  const showLinks = width >= 120;
22
24
  return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { bold: true, color: tokens.colors.accent, children: ["KLYRO v", version] }), showLinks ? _jsx(Text, { color: tokens.colors.dim, children: "\u2502 /help /config /clear /exit" }) : null] }), _jsxs(Text, { color: tokens.colors.dim, children: [model, "[200k] \u00B7 API Usage Billing"] }), _jsxs(Text, { color: tokens.colors.dim, children: [cwd, branch ? ` · ${branch}` : ''] })] }));
23
25
  }
@@ -241,11 +243,21 @@ export function App(props) {
241
243
  n.delete(id);
242
244
  else
243
245
  n.add(id); return n; });
246
+ // `/` autocomplete — top 6 prefix matches while completing a command name (no space yet)
247
+ const slashSuggest = input.startsWith('/') && !input.slice(1).includes(' ') && !awaitingApproval
248
+ ? suggestCommands(input, 6)
249
+ : [];
244
250
  useInput((inputStr, key) => {
245
251
  if (key.escape && queuedInputs.length > 0) {
246
252
  setQueuedInputs((prev) => prev.slice(1));
247
253
  return;
248
254
  }
255
+ // Tab completes the top slash suggestion (e.g. `/c` → `/clear `)
256
+ if ((key.tab || inputStr === '\t') && slashSuggest.length > 0) {
257
+ const top = slashSuggest[0];
258
+ setInput(`/${top.name} `);
259
+ return;
260
+ }
249
261
  // Scroll keys (work in any mode, including while running).
250
262
  if (isFullscreen && maxOffset > 0) {
251
263
  if (key.home) {
@@ -331,7 +343,7 @@ export function App(props) {
331
343
  const cost = (status.usageInput / 1000 * 0.003 + status.usageOutput / 1000 * 0.015);
332
344
  const totalTokens = status.usageInput + status.usageOutput;
333
345
  const ctxPct = totalTokens > 0 ? Math.round((totalTokens / 120_000) * 100) : 0;
334
- const baseHints = status.status === 'running' ? 'ctrl+c to stop · enter to queue · ctrl+o expand' : transcript.length === 0 ? 'shift+tab to cycle · †‘†“ for history · / for commands' : 'enter to send · shift+enter newline · @ to attach';
346
+ const baseHints = status.status === 'running' ? 'ctrl+c to stop · enter to queue · ctrl+o expand' : transcript.length === 0 ? 'shift+tab to cycle · ↑/↓ for history · / for commands' : 'enter to send · shift+enter newline · @ to attach';
335
347
  const hints = maxOffset > 0 && isFullscreen ? `${baseHints} · PgUp/Dn scroll` : baseHints;
336
348
  return (_jsxs(Box, { flexDirection: "column", width: width, height: isFullscreen ? height - 1 : undefined, children: [_jsx(Header, { cwd: props.cwd, model: status.model, version: ver, width: width }), _jsxs(Box, { flexDirection: "row", flexGrow: isFullscreen ? 1 : 0, overflow: isFullscreen ? 'hidden' : undefined, children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, overflow: isFullscreen ? 'hidden' : undefined, paddingX: 0, children: [grouped.length === 0 ? (_jsx(Text, { color: tokens.colors.dim, children: "Message Klyro..." })) : visibleGrouped.map((item) => {
337
349
  if (item.verb) {
@@ -366,8 +378,8 @@ export function App(props) {
366
378
  return `Edited ${gr.items.length} files`;
367
379
  return `${gr.verb} ${gr.items.length} items`;
368
380
  })();
369
- const right = gr.status === 'running' ? `${(elapsed / 1000).toFixed(1)}s` : gr.status === 'error' ? 'œ—' : `${gr.totalMs}ms`;
370
- const marker = isExpanded ? '–¼' : 'œ“';
381
+ const right = gr.status === 'running' ? `${(elapsed / 1000).toFixed(1)}s` : gr.status === 'error' ? g('failure') : `${gr.totalMs}ms`;
382
+ const marker = isExpanded ? g('expanded') : g('collapsed');
371
383
  const markerColor = gr.status === 'error' ? tokens.colors.err : gr.status === 'running' ? tokens.colors.warn : tokens.colors.ok;
372
384
  return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: tokens.colors.guide, children: [" ", g('guide'), " "] }), _jsxs(Text, { color: markerColor, children: [marker, " ", verbLine] }), _jsxs(Text, { color: tokens.colors.dim, children: [" ", right] })] }), isExpanded ? gr.items.map((it) => {
373
385
  let friendly = '';
@@ -399,7 +411,7 @@ export function App(props) {
399
411
  return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: tokens.colors.guide, children: [" ", g('guide'), " "] }), _jsxs(Text, { color: tokens.colors.accent, children: [g('agentBullet'), " Klyro"] })] }), _jsx(Box, { paddingLeft: 2, flexDirection: "column", children: _jsxs(Box, { children: [_jsxs(Text, { color: tokens.colors.dim, children: [" ", g('guide'), " "] }), _jsx(Box, { flexGrow: 1, children: _jsx(MarkdownText, { text: it.text }) })] }) })] }, it.id));
400
412
  }
401
413
  if (it.kind === 'error')
402
- return _jsx(Box, { paddingLeft: 2, marginBottom: 1, children: _jsxs(Text, { color: tokens.colors.err, children: [" ", g('guide'), " \u0153\u2014 ", it.message] }) }, it.id);
414
+ return _jsx(Box, { paddingLeft: 2, marginBottom: 1, children: _jsxs(Text, { color: tokens.colors.err, children: [" ", g('guide'), " ", g('failure'), " ", it.message] }) }, it.id);
403
415
  if (it.kind === 'policy')
404
416
  return null;
405
417
  if (it.kind === 'file_changed')
@@ -407,5 +419,5 @@ export function App(props) {
407
419
  if (it.kind === 'diff')
408
420
  return (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [_jsx(Text, { bold: true, color: tokens.colors.soft, children: it.summary ?? 'Diff' }), it.hunks.map((h, i) => (_jsxs(Box, { flexDirection: "column", marginTop: 0, children: [_jsx(Text, { color: tokens.colors.soft, children: h.path }), h.lines.map((l, j) => (_jsxs(Text, { wrap: "wrap", color: l.kind === 'add' ? tokens.colors.ok : l.kind === 'remove' ? tokens.colors.err : tokens.colors.dim, children: [l.kind === 'add' ? '+ ' : l.kind === 'remove' ? '- ' : ' ', l.text] }, j)))] }, i)))] }, it.id));
409
421
  return null;
410
- }), status.status === 'running' && !streamingIdRef.current ? (_jsxs(Box, { paddingLeft: 2, marginBottom: 1, children: [_jsxs(Text, { color: tokens.colors.guide, children: [" ", g('guide'), " "] }), _jsx(Text, { color: tokens.colors.dim, children: "Thinking..." }), _jsxs(Text, { color: tokens.colors.dim, children: [" ", (elapsed / 1000).toFixed(1), "s"] })] })) : null, plan.length > 0 ? (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, marginTop: 0, marginBottom: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: tokens.colors.guide, children: [" ", g('guide'), " "] }), _jsxs(Text, { bold: true, children: [g('todoPlan'), " Plan ", plan.filter((p) => p.status === 'done').length, "/", plan.length] })] }), plan.slice(0, 8).map((p) => (_jsxs(Box, { children: [_jsxs(Text, { color: tokens.colors.guide, children: [" ", g('guide'), " "] }), _jsxs(Text, { color: p.status === 'done' ? tokens.colors.ok : p.status === 'in_progress' ? tokens.colors.accent : tokens.colors.dim, children: [p.status === 'done' ? g('todoDone') : p.status === 'in_progress' ? g('todoActive') : g('todoPending'), " ", p.title] })] }, p.id)))] })) : null, queuedInputs.length > 0 ? (_jsx(Box, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: queuedInputs.map((q, i) => (_jsxs(Text, { color: tokens.colors.dim, children: ["queued: ", q.slice(0, 60), i === 0 ? ' esc to drop' : ''] }, i))) })) : null] }), isFullscreen ? (_jsx(Box, { flexDirection: "column", width: 1, marginLeft: 1, children: Array.from({ length: trackH }).map((_, i) => (_jsx(Text, { color: i === thumbPos ? tokens.colors.accent : tokens.colors.guide, children: i === thumbPos ? '●' : '│' }, i))) })) : null] }), pinned && pendingNew > 0 ? (_jsx(Box, { justifyContent: "flex-end", paddingX: 1, marginTop: -1, children: _jsxs(Text, { backgroundColor: tokens.colors.accentSoft, color: tokens.colors.accent, bold: true, children: [' ↓ ', pendingNew, ' new ', pendingNew === 1 ? 'message' : 'messages', ' '] }) })) : null, _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: tokens.colors.guide, children: g('rule').repeat(Math.max(10, width - 2)) }), _jsxs(Box, { children: [_jsxs(Text, { color: tokens.colors.accent, bold: true, children: [g('prompt'), " "] }), _jsxs(Text, { wrap: "wrap", children: [input || _jsx(Text, { color: tokens.colors.dim, children: "Message Klyro..." }), "|"] })] }), _jsx(Text, { color: tokens.colors.guide, children: g('rule').repeat(Math.max(10, width - 2)) })] }), _jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { color: tokens.colors.dim, children: [baseHints, maxOffset > 0 && isFullscreen ? ' · PgUp/Dn scroll' : ''] }), _jsxs(Text, { color: tokens.colors.dim, children: [cost > 0 ? `$${cost.toFixed(2)} · ` : '', ctxPct > 0 ? `${ctxPct}% ctx · ` : '', status.status === 'running' ? 'auto mode on ●' : ''] })] })] }));
422
+ }), status.status === 'running' && !streamingIdRef.current ? (_jsxs(Box, { paddingLeft: 2, marginBottom: 1, children: [_jsxs(Text, { color: tokens.colors.guide, children: [" ", g('guide'), " "] }), _jsx(Text, { color: tokens.colors.dim, children: "Thinking..." }), _jsxs(Text, { color: tokens.colors.dim, children: [" ", (elapsed / 1000).toFixed(1), "s"] })] })) : null, plan.length > 0 ? (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, marginTop: 0, marginBottom: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: tokens.colors.guide, children: [" ", g('guide'), " "] }), _jsxs(Text, { bold: true, children: [g('todoPlan'), " Plan ", plan.filter((p) => p.status === 'done').length, "/", plan.length] })] }), plan.slice(0, 8).map((p) => (_jsxs(Box, { children: [_jsxs(Text, { color: tokens.colors.guide, children: [" ", g('guide'), " "] }), _jsxs(Text, { color: p.status === 'done' ? tokens.colors.ok : p.status === 'in_progress' ? tokens.colors.accent : tokens.colors.dim, children: [p.status === 'done' ? g('todoDone') : p.status === 'in_progress' ? g('todoActive') : g('todoPending'), " ", p.title] })] }, p.id)))] })) : null, queuedInputs.length > 0 ? (_jsx(Box, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: queuedInputs.map((q, i) => (_jsxs(Text, { color: tokens.colors.dim, children: ["queued: ", q.slice(0, 60), i === 0 ? ' esc to drop' : ''] }, i))) })) : null] }), isFullscreen ? (_jsx(Box, { flexDirection: "column", width: 1, marginLeft: 1, children: Array.from({ length: trackH }).map((_, i) => (_jsx(Text, { color: i === thumbPos ? tokens.colors.accent : tokens.colors.guide, children: i === thumbPos ? '●' : '│' }, i))) })) : null] }), pinned && pendingNew > 0 ? (_jsx(Box, { justifyContent: "flex-end", paddingX: 1, marginTop: -1, children: _jsxs(Text, { backgroundColor: tokens.colors.accentSoft, color: tokens.colors.accent, bold: true, children: [' ↓ ', pendingNew, ' new ', pendingNew === 1 ? 'message' : 'messages', ' '] }) })) : null, slashSuggest.length > 0 ? (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, children: [slashSuggest.map((s, i) => (_jsxs(Text, { color: i === 0 ? tokens.colors.accent : tokens.colors.dim, children: [i === 0 ? '▸' : ' ', " /", s.name, " \u2014 ", s.hint] }, s.name))), _jsx(Text, { color: tokens.colors.dim, children: " tab to complete" })] })) : null, _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: tokens.colors.guide, children: g('rule').repeat(Math.max(10, width - 2)) }), _jsxs(Box, { children: [_jsxs(Text, { color: tokens.colors.accent, bold: true, children: [g('prompt'), " "] }), _jsxs(Text, { wrap: "wrap", children: [input || _jsx(Text, { color: tokens.colors.dim, children: "Message Klyro..." }), "|"] })] }), _jsx(Text, { color: tokens.colors.guide, children: g('rule').repeat(Math.max(10, width - 2)) })] }), _jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { color: tokens.colors.dim, children: [baseHints, maxOffset > 0 && isFullscreen ? ' · PgUp/Dn scroll' : ''] }), _jsxs(Text, { color: tokens.colors.dim, children: [cost > 0 ? `$${cost.toFixed(2)} · ` : '', ctxPct > 0 ? `${ctxPct}% ctx · ` : '', status.status === 'running' ? 'auto mode on ●' : ''] })] })] }));
411
423
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "klyro",
3
- "version": "0.1.44",
3
+ "version": "0.1.45",
4
4
  "description": "Klyro \u2014 autonomous coding harness CLI that streams from any OpenAI-compatible or Anthropic LLM endpoint.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",