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.
- package/dist/cli/repl.js +1065 -8
- package/dist/cli/slash/parser.d.ts +182 -0
- package/dist/cli/slash/parser.js +209 -1
- package/dist/tui/app.js +25 -13
- package/package.json +1 -1
|
@@ -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[];
|
package/dist/cli/slash/parser.js
CHANGED
|
@@ -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', '
|
|
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 = (() => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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 ·
|
|
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' ? '
|
|
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'), "
|
|
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