@staticpayload/zai-code 1.2.15 → 1.4.0

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.
Files changed (67) hide show
  1. package/README.md +85 -154
  2. package/dist/apply.d.ts.map +1 -1
  3. package/dist/apply.js +43 -16
  4. package/dist/apply.js.map +1 -1
  5. package/dist/auth.d.ts.map +1 -1
  6. package/dist/auth.js +5 -1
  7. package/dist/auth.js.map +1 -1
  8. package/dist/commands.d.ts.map +1 -1
  9. package/dist/commands.js +1021 -47
  10. package/dist/commands.js.map +1 -1
  11. package/dist/context/context_builder.d.ts.map +1 -1
  12. package/dist/context/context_builder.js +18 -3
  13. package/dist/context/context_builder.js.map +1 -1
  14. package/dist/context/project_memory.d.ts +2 -2
  15. package/dist/context/project_memory.d.ts.map +1 -1
  16. package/dist/context/project_memory.js +14 -4
  17. package/dist/context/project_memory.js.map +1 -1
  18. package/dist/doctor.d.ts.map +1 -1
  19. package/dist/doctor.js +51 -7
  20. package/dist/doctor.js.map +1 -1
  21. package/dist/history.d.ts.map +1 -1
  22. package/dist/history.js +13 -8
  23. package/dist/history.js.map +1 -1
  24. package/dist/mode_prompts.d.ts.map +1 -1
  25. package/dist/mode_prompts.js +25 -22
  26. package/dist/mode_prompts.js.map +1 -1
  27. package/dist/orchestrator.d.ts.map +1 -1
  28. package/dist/orchestrator.js +69 -11
  29. package/dist/orchestrator.js.map +1 -1
  30. package/dist/planner.js +18 -18
  31. package/dist/planner.js.map +1 -1
  32. package/dist/rollback.d.ts +1 -0
  33. package/dist/rollback.d.ts.map +1 -1
  34. package/dist/rollback.js +28 -4
  35. package/dist/rollback.js.map +1 -1
  36. package/dist/runtime.d.ts.map +1 -1
  37. package/dist/runtime.js +22 -9
  38. package/dist/runtime.js.map +1 -1
  39. package/dist/session.d.ts +1 -0
  40. package/dist/session.d.ts.map +1 -1
  41. package/dist/session.js +8 -1
  42. package/dist/session.js.map +1 -1
  43. package/dist/settings.d.ts +1 -0
  44. package/dist/settings.d.ts.map +1 -1
  45. package/dist/settings.js +8 -3
  46. package/dist/settings.js.map +1 -1
  47. package/dist/shell.d.ts.map +1 -1
  48. package/dist/shell.js +85 -14
  49. package/dist/shell.js.map +1 -1
  50. package/dist/task_runner.d.ts.map +1 -1
  51. package/dist/task_runner.js +20 -2
  52. package/dist/task_runner.js.map +1 -1
  53. package/dist/tui.d.ts.map +1 -1
  54. package/dist/tui.js +566 -88
  55. package/dist/tui.js.map +1 -1
  56. package/dist/ui.d.ts +7 -0
  57. package/dist/ui.d.ts.map +1 -1
  58. package/dist/ui.js +113 -11
  59. package/dist/ui.js.map +1 -1
  60. package/dist/workspace.d.ts.map +1 -1
  61. package/dist/workspace.js +6 -0
  62. package/dist/workspace.js.map +1 -1
  63. package/dist/workspace_model.d.ts +1 -1
  64. package/dist/workspace_model.d.ts.map +1 -1
  65. package/dist/workspace_model.js +8 -1
  66. package/dist/workspace_model.js.map +1 -1
  67. package/package.json +4 -2
package/dist/commands.js CHANGED
@@ -44,12 +44,17 @@ const history_1 = require("./history");
44
44
  const settings_menu_1 = require("./settings_menu");
45
45
  const shell_1 = require("./shell");
46
46
  const workspace_model_1 = require("./workspace_model");
47
+ const auth_1 = require("./auth");
48
+ const runtime_1 = require("./runtime");
49
+ const workspace_1 = require("./workspace");
47
50
  const apply_1 = require("./apply");
48
51
  const ui_1 = require("./ui");
49
52
  const planner_1 = require("./planner");
50
53
  const task_runner_1 = require("./task_runner");
51
54
  const rollback_1 = require("./rollback");
55
+ const mode_prompts_1 = require("./mode_prompts");
52
56
  const path = __importStar(require("path"));
57
+ const fs = __importStar(require("fs"));
53
58
  // Parse input to detect slash commands
54
59
  function parseInput(input) {
55
60
  const trimmed = input.trim();
@@ -68,19 +73,541 @@ function parseInput(input) {
68
73
  }
69
74
  // Stub handlers for all commands
70
75
  const handlers = {
71
- help: () => {
72
- console.log('Navigation:');
73
- console.log(' /help /context /files /open /workspace');
74
- console.log('Execution:');
75
- console.log(' /plan /generate /diff /apply /undo');
76
- console.log('Modes:');
77
- console.log(' /ask /mode /model /dry-run /profile');
78
- console.log('Tasks:');
79
- console.log(' /decompose /step /next /skip /progress');
80
- console.log('System:');
81
- console.log(' /settings /git /exec /history /doctor');
82
- console.log('Session:');
83
- console.log(' /reset /exit');
76
+ help: (ctx) => {
77
+ const topic = ctx.args[0]?.toLowerCase();
78
+ if (topic === 'modes') {
79
+ console.log('');
80
+ console.log((0, ui_1.info)('Available Modes:'));
81
+ console.log('');
82
+ console.log(' auto ⚡ YOLO mode: execute tasks directly without confirmation');
83
+ console.log(' edit ✏️ Default: plan → generate → review → apply workflow');
84
+ console.log(' ask ❓ Read-only: answer questions, no file changes');
85
+ console.log(' explain 📖 Read-only: explain code concepts');
86
+ console.log(' review 👁 Read-only: code review and analysis');
87
+ console.log(' debug 🔧 Investigate and fix issues');
88
+ console.log('');
89
+ console.log((0, ui_1.hint)('/mode <name> to switch'));
90
+ return;
91
+ }
92
+ if (topic === 'workflow') {
93
+ console.log('');
94
+ console.log((0, ui_1.info)('Standard Workflow:'));
95
+ console.log('');
96
+ console.log(' 1. Type a task (e.g., "add error handling to auth.ts")');
97
+ console.log(' 2. /plan - Generate execution plan');
98
+ console.log(' 3. /generate - Create file changes');
99
+ console.log(' 4. /diff - Review changes');
100
+ console.log(' 5. /apply - Apply changes');
101
+ console.log(' 6. /undo - Rollback if needed');
102
+ console.log('');
103
+ console.log((0, ui_1.info)('Quick Workflow:'));
104
+ console.log('');
105
+ console.log(' /do <task> - Plan + generate in one step');
106
+ console.log(' /run <task> - Plan + generate + apply (YOLO)');
107
+ console.log('');
108
+ return;
109
+ }
110
+ if (topic === 'shortcuts') {
111
+ console.log('');
112
+ console.log((0, ui_1.info)('Keyboard Shortcuts:'));
113
+ console.log('');
114
+ console.log(' Ctrl+D /do <task> - Quick execute');
115
+ console.log(' Ctrl+R /run <task> - Auto mode run');
116
+ console.log(' Ctrl+P /plan - Generate plan');
117
+ console.log(' Ctrl+G /generate - Create changes');
118
+ console.log(' Ctrl+Z /undo - Rollback');
119
+ console.log(' Ctrl+A /ask <q> - Quick question');
120
+ console.log(' Ctrl+F /fix <desc> - Quick debug');
121
+ console.log(' Ctrl+C Exit');
122
+ console.log('');
123
+ console.log((0, ui_1.info)('Command Aliases:'));
124
+ console.log('');
125
+ console.log(' /h = /help /p = /plan /g = /generate');
126
+ console.log(' /d = /diff /a = /apply /u = /undo');
127
+ console.log(' /s = /status /c = /context /f = /files');
128
+ console.log('');
129
+ return;
130
+ }
131
+ if (topic === 'quick') {
132
+ console.log('');
133
+ console.log((0, ui_1.info)('Quick Commands:'));
134
+ console.log('');
135
+ console.log(' /do <task> Plan + generate in one step');
136
+ console.log(' Example: /do add input validation to login form');
137
+ console.log('');
138
+ console.log(' /run <task> Plan + generate + apply automatically');
139
+ console.log(' Example: /run fix the typo in README.md');
140
+ console.log('');
141
+ console.log(' /ask <q> Quick question without changing mode');
142
+ console.log(' Example: /ask what does this function do?');
143
+ console.log('');
144
+ console.log(' /fix <desc> Quick debug mode task');
145
+ console.log(' Example: /fix login button not working');
146
+ console.log('');
147
+ console.log(' /commit Generate AI commit message');
148
+ console.log(' Example: /commit (auto) or /commit feat: add login');
149
+ console.log('');
150
+ return;
151
+ }
152
+ // Default help
153
+ console.log('');
154
+ console.log((0, ui_1.info)('zai·code Commands'));
155
+ console.log('');
156
+ console.log((0, ui_1.dim)('Quick Actions:'));
157
+ console.log(' /do <task> Plan + generate in one step');
158
+ console.log(' /run <task> Plan + generate + apply (YOLO)');
159
+ console.log(' /ask <q> Quick question');
160
+ console.log(' /fix <desc> Quick debug task');
161
+ console.log('');
162
+ console.log((0, ui_1.dim)('Workflow:'));
163
+ console.log(' /plan Generate execution plan');
164
+ console.log(' /generate Create file changes');
165
+ console.log(' /diff [full] Review pending changes');
166
+ console.log(' /apply [-f] Apply changes');
167
+ console.log(' /undo [n] Rollback operations');
168
+ console.log(' /retry Retry last failed operation');
169
+ console.log(' /clear Clear current task');
170
+ console.log('');
171
+ console.log((0, ui_1.dim)('Files:'));
172
+ console.log(' /open <path> Add file to context');
173
+ console.log(' /close <path> Remove from context');
174
+ console.log(' /files List open files');
175
+ console.log(' /search <q> Search files');
176
+ console.log(' /read <path> View file contents');
177
+ console.log(' /tree [depth] Show file tree');
178
+ console.log('');
179
+ console.log((0, ui_1.dim)('Modes & Settings:'));
180
+ console.log(' /mode [name] Set mode (edit/ask/auto/debug/review/explain)');
181
+ console.log(' /model [id] Select AI model');
182
+ console.log(' /settings Open settings menu');
183
+ console.log(' /dry-run Toggle dry-run mode');
184
+ console.log('');
185
+ console.log((0, ui_1.dim)('Git:'));
186
+ console.log(' /git [cmd] Git operations (status/log/diff/stash/pop)');
187
+ console.log(' /commit [msg] AI-powered commit');
188
+ console.log('');
189
+ console.log((0, ui_1.dim)('System:'));
190
+ console.log(' /status Show session status');
191
+ console.log(' /doctor System health check');
192
+ console.log(' /version Show version');
193
+ console.log(' /reset Reset session');
194
+ console.log(' /exit Exit zcode');
195
+ console.log('');
196
+ console.log((0, ui_1.hint)('/help <topic> for details: modes, workflow, shortcuts, quick'));
197
+ },
198
+ // Quick execute - plan + generate in one step
199
+ do: async (ctx) => {
200
+ const task = ctx.args.join(' ');
201
+ if (!task) {
202
+ console.log('Usage: /do <task>');
203
+ console.log('Example: /do add input validation to login form');
204
+ return;
205
+ }
206
+ // Set intent
207
+ (0, session_1.setIntent)(task);
208
+ console.log((0, ui_1.info)(`Task: ${task}`));
209
+ // Plan
210
+ console.log((0, ui_1.dim)('Planning...'));
211
+ const planResult = await (0, planner_1.runPlannerLoop)();
212
+ if (!planResult.success) {
213
+ console.log((0, ui_1.error)(planResult.message));
214
+ return;
215
+ }
216
+ console.log((0, ui_1.success)(`Plan: ${planResult.plan?.length || 0} steps`));
217
+ // Generate
218
+ console.log((0, ui_1.dim)('Generating...'));
219
+ const genResult = await (0, planner_1.runGenerateLoop)();
220
+ if (!genResult.success) {
221
+ console.log((0, ui_1.error)(genResult.message));
222
+ return;
223
+ }
224
+ const fileCount = (genResult.changes?.files?.length || 0) + (genResult.changes?.diffs?.length || 0);
225
+ console.log((0, ui_1.success)(`Generated ${fileCount} file change(s)`));
226
+ console.log((0, ui_1.hint)('/diff to review, /apply to execute'));
227
+ },
228
+ // Full auto run - plan + generate + apply
229
+ run: async (ctx) => {
230
+ const task = ctx.args.join(' ');
231
+ if (!task) {
232
+ console.log('Usage: /run <task>');
233
+ console.log('Warning: This will apply changes automatically!');
234
+ return;
235
+ }
236
+ const session = (0, session_1.getSession)();
237
+ if (session.dryRun) {
238
+ console.log((0, ui_1.error)('Dry-run mode enabled. Use /dry-run off first.'));
239
+ return;
240
+ }
241
+ // Set intent
242
+ (0, session_1.setIntent)(task);
243
+ console.log((0, ui_1.info)(`Task: ${task}`));
244
+ // Plan
245
+ console.log((0, ui_1.dim)('Planning...'));
246
+ const planResult = await (0, planner_1.runPlannerLoop)();
247
+ if (!planResult.success) {
248
+ console.log((0, ui_1.error)(planResult.message));
249
+ return;
250
+ }
251
+ console.log((0, ui_1.success)(`Plan: ${planResult.plan?.length || 0} steps`));
252
+ // Generate
253
+ console.log((0, ui_1.dim)('Generating...'));
254
+ const genResult = await (0, planner_1.runGenerateLoop)();
255
+ if (!genResult.success) {
256
+ console.log((0, ui_1.error)(genResult.message));
257
+ return;
258
+ }
259
+ // Apply
260
+ console.log((0, ui_1.dim)('Applying...'));
261
+ const actions = session.pendingActions;
262
+ if (!actions || ((!actions.files || actions.files.length === 0) && (!actions.diffs || actions.diffs.length === 0))) {
263
+ console.log('No changes to apply.');
264
+ return;
265
+ }
266
+ const result = (0, apply_1.applyResponse)(actions, {
267
+ basePath: session.workingDirectory,
268
+ dryRun: false,
269
+ });
270
+ if (!result.success) {
271
+ for (const failed of result.failed) {
272
+ console.log((0, ui_1.error)(`Failed: ${failed.path}: ${failed.error}`));
273
+ }
274
+ return;
275
+ }
276
+ (0, session_1.setPendingActions)(null);
277
+ console.log((0, ui_1.success)(`Applied ${result.applied.length} change(s)`));
278
+ console.log((0, ui_1.hint)('/undo to rollback'));
279
+ },
280
+ // Retry last failed operation
281
+ retry: async () => {
282
+ const session = (0, session_1.getSession)();
283
+ if (session.lastPlan && session.lastPlan.length > 0 && !session.lastDiff) {
284
+ // Have plan but no diff - retry generate
285
+ console.log((0, ui_1.dim)('Retrying generation...'));
286
+ const result = await (0, planner_1.runGenerateLoop)();
287
+ if (result.success) {
288
+ console.log((0, ui_1.success)('Generation succeeded on retry'));
289
+ console.log((0, ui_1.hint)('/diff to review'));
290
+ }
291
+ else {
292
+ console.log((0, ui_1.error)(result.message));
293
+ }
294
+ return;
295
+ }
296
+ if ((0, session_1.getIntent)() && (!session.lastPlan || session.lastPlan.length === 0)) {
297
+ // Have intent but no plan - retry plan
298
+ console.log((0, ui_1.dim)('Retrying planning...'));
299
+ const result = await (0, planner_1.runPlannerLoop)();
300
+ if (result.success) {
301
+ console.log((0, ui_1.success)('Planning succeeded on retry'));
302
+ console.log((0, ui_1.hint)('/generate to create changes'));
303
+ }
304
+ else {
305
+ console.log((0, ui_1.error)(result.message));
306
+ }
307
+ return;
308
+ }
309
+ console.log('Nothing to retry.');
310
+ },
311
+ // Clear current task
312
+ clear: () => {
313
+ (0, session_1.clearIntent)();
314
+ (0, session_1.setLastPlan)(null);
315
+ (0, session_1.setLastDiff)(null);
316
+ (0, session_1.setPendingActions)(null);
317
+ console.log('Task cleared.');
318
+ },
319
+ // Close/remove file from context
320
+ close: (ctx) => {
321
+ const filePath = ctx.args[0];
322
+ if (!filePath) {
323
+ console.log('Usage: /close <path> or /close all');
324
+ return;
325
+ }
326
+ if (filePath === 'all') {
327
+ (0, session_1.clearOpenFiles)();
328
+ console.log('All files removed from context.');
329
+ return;
330
+ }
331
+ (0, session_1.removeOpenFile)(filePath);
332
+ console.log(`Removed: ${filePath}`);
333
+ },
334
+ // Search files in workspace
335
+ search: (ctx) => {
336
+ const query = ctx.args.join(' ');
337
+ if (!query) {
338
+ console.log('Usage: /search <pattern>');
339
+ return;
340
+ }
341
+ const session = (0, session_1.getSession)();
342
+ const ws = (0, workspace_model_1.getWorkspace)(session.workingDirectory);
343
+ ws.indexFileTree();
344
+ const files = ws.getFileIndex();
345
+ const pattern = query.toLowerCase();
346
+ const matches = files.filter(f => f.path.toLowerCase().includes(pattern));
347
+ if (matches.length === 0) {
348
+ console.log(`No files matching "${query}"`);
349
+ return;
350
+ }
351
+ console.log(`Found ${matches.length} file(s):`);
352
+ for (const m of matches.slice(0, 20)) {
353
+ console.log(` ${m.path}`);
354
+ }
355
+ if (matches.length > 20) {
356
+ console.log((0, ui_1.dim)(` ... and ${matches.length - 20} more`));
357
+ }
358
+ },
359
+ // Quick ask without changing mode
360
+ 'ask-quick': async (ctx) => {
361
+ const question = ctx.args.join(' ');
362
+ if (!question) {
363
+ console.log('Usage: /ask <question>');
364
+ return;
365
+ }
366
+ let apiKey;
367
+ try {
368
+ apiKey = await (0, auth_1.ensureAuthenticated)();
369
+ if (!apiKey) {
370
+ console.log((0, ui_1.error)('No API key configured.'));
371
+ return;
372
+ }
373
+ }
374
+ catch (e) {
375
+ console.log((0, ui_1.error)(`Auth failed: ${e?.message}`));
376
+ return;
377
+ }
378
+ const session = (0, session_1.getSession)();
379
+ const modePrompt = (0, mode_prompts_1.buildSystemPrompt)('ask', session.workingDirectory);
380
+ console.log((0, ui_1.dim)('Thinking...'));
381
+ const result = await (0, runtime_1.execute)({
382
+ instruction: `${modePrompt}\n\nQuestion: ${question}\n\nAnswer concisely.`,
383
+ enforceSchema: false,
384
+ }, apiKey);
385
+ if (result.success && result.output) {
386
+ const text = typeof result.output === 'string' ? result.output : JSON.stringify(result.output, null, 2);
387
+ console.log(text);
388
+ }
389
+ else {
390
+ console.log((0, ui_1.error)(result.error || 'Failed'));
391
+ }
392
+ },
393
+ // Quick fix/debug
394
+ fix: async (ctx) => {
395
+ const description = ctx.args.join(' ');
396
+ if (!description) {
397
+ console.log('Usage: /fix <problem description>');
398
+ return;
399
+ }
400
+ (0, session_1.setMode)('debug');
401
+ (0, session_1.setIntent)(`Fix: ${description}`);
402
+ console.log((0, ui_1.info)(`Debug task: ${description}`));
403
+ console.log((0, ui_1.hint)('/plan to start debugging'));
404
+ },
405
+ // Save session to disk
406
+ save: () => {
407
+ const session = (0, session_1.getSession)();
408
+ const ws = (0, workspace_model_1.getWorkspace)(session.workingDirectory);
409
+ const saved = ws.saveState();
410
+ if (saved) {
411
+ console.log((0, ui_1.success)('Session saved to .zai/workspace.json'));
412
+ }
413
+ else {
414
+ console.log((0, ui_1.error)('Failed to save session'));
415
+ }
416
+ },
417
+ // Load session from disk
418
+ load: () => {
419
+ const session = (0, session_1.getSession)();
420
+ const ws = (0, workspace_model_1.getWorkspace)(session.workingDirectory);
421
+ const restored = ws.restoreState();
422
+ if (restored) {
423
+ console.log((0, ui_1.success)('Session restored'));
424
+ console.log(`Mode: ${session.mode}`);
425
+ console.log(`Intent: ${session.currentIntent || 'none'}`);
426
+ console.log(`Files: ${session.openFiles.length}`);
427
+ }
428
+ else {
429
+ console.log('No saved session found.');
430
+ }
431
+ },
432
+ // Git commit helper
433
+ commit: async (ctx) => {
434
+ const message = ctx.args.join(' ');
435
+ const session = (0, session_1.getSession)();
436
+ const gitInfo = (0, git_1.getGitInfo)(session.workingDirectory);
437
+ if (!gitInfo.isRepo) {
438
+ console.log((0, ui_1.error)('Not a git repository.'));
439
+ return;
440
+ }
441
+ if (!gitInfo.isDirty) {
442
+ console.log('Nothing to commit (working tree clean).');
443
+ return;
444
+ }
445
+ if (!message) {
446
+ // Generate commit message
447
+ let apiKey;
448
+ try {
449
+ apiKey = await (0, auth_1.ensureAuthenticated)();
450
+ if (!apiKey) {
451
+ console.log('Usage: /commit <message>');
452
+ return;
453
+ }
454
+ }
455
+ catch {
456
+ console.log('Usage: /commit <message>');
457
+ return;
458
+ }
459
+ console.log((0, ui_1.dim)('Generating commit message...'));
460
+ // Get git diff
461
+ const { execSync } = require('child_process');
462
+ let diff = '';
463
+ try {
464
+ diff = execSync('git diff --staged', { cwd: session.workingDirectory, encoding: 'utf-8', maxBuffer: 50000 });
465
+ if (!diff) {
466
+ diff = execSync('git diff', { cwd: session.workingDirectory, encoding: 'utf-8', maxBuffer: 50000 });
467
+ }
468
+ }
469
+ catch {
470
+ console.log((0, ui_1.error)('Failed to get git diff'));
471
+ return;
472
+ }
473
+ if (!diff) {
474
+ console.log('No changes to commit.');
475
+ return;
476
+ }
477
+ const result = await (0, runtime_1.execute)({
478
+ instruction: `Generate a concise git commit message for these changes. Use conventional commit format (feat/fix/docs/refactor/etc). Output ONLY the commit message, nothing else.\n\nDiff:\n${diff.substring(0, 5000)}`,
479
+ enforceSchema: false,
480
+ }, apiKey);
481
+ if (result.success && result.output) {
482
+ const msg = typeof result.output === 'string' ? result.output.trim() : String(result.output);
483
+ console.log(`Suggested: ${msg}`);
484
+ console.log((0, ui_1.hint)(`/commit ${msg}`));
485
+ }
486
+ else {
487
+ console.log('Usage: /commit <message>');
488
+ }
489
+ return;
490
+ }
491
+ // Execute commit
492
+ const { execSync } = require('child_process');
493
+ try {
494
+ execSync('git add -A', { cwd: session.workingDirectory, stdio: 'pipe' });
495
+ execSync(`git commit -m "${message.replace(/"/g, '\\"')}"`, { cwd: session.workingDirectory, stdio: 'pipe' });
496
+ console.log((0, ui_1.success)(`Committed: ${message}`));
497
+ }
498
+ catch (e) {
499
+ console.log((0, ui_1.error)(`Commit failed: ${e?.message || e}`));
500
+ }
501
+ },
502
+ // Version info
503
+ version: () => {
504
+ try {
505
+ const pkg = require('../package.json');
506
+ console.log(`zai-code v${pkg.version}`);
507
+ console.log(`Node.js ${process.version}`);
508
+ console.log(`Platform: ${process.platform} ${process.arch}`);
509
+ }
510
+ catch {
511
+ console.log('zai-code');
512
+ }
513
+ },
514
+ // Status - comprehensive overview
515
+ status: () => {
516
+ const session = (0, session_1.getSession)();
517
+ const gitInfo = (0, git_1.getGitInfo)(session.workingDirectory);
518
+ const undoCount = (0, rollback_1.getUndoCount)();
519
+ console.log('');
520
+ console.log((0, ui_1.info)('Session Status'));
521
+ console.log('');
522
+ // Mode with icon
523
+ const modeIcons = {
524
+ 'auto': '⚡', 'edit': '✏️', 'ask': '❓', 'debug': '🔧', 'review': '👁', 'explain': '📖'
525
+ };
526
+ const modeIcon = modeIcons[session.mode] || '';
527
+ console.log(` Mode: ${modeIcon} ${session.mode}${session.dryRun ? ' (dry-run)' : ''}`);
528
+ console.log(` Model: ${(0, settings_1.getModel)()}`);
529
+ console.log(` Directory: ${session.workingDirectory}`);
530
+ console.log('');
531
+ // Git status
532
+ if (gitInfo.isRepo) {
533
+ const status = gitInfo.isDirty ? `${gitInfo.uncommittedFiles} uncommitted` : 'clean';
534
+ console.log(` Git: ${gitInfo.branch}${gitInfo.isDirty ? '*' : ''} (${status})`);
535
+ }
536
+ else {
537
+ console.log(' Git: not a repository');
538
+ }
539
+ console.log('');
540
+ // Session state
541
+ console.log(` Files: ${session.openFiles.length} in context`);
542
+ // Current task
543
+ if (session.currentIntent) {
544
+ const truncated = session.currentIntent.length > 50
545
+ ? session.currentIntent.substring(0, 50) + '...'
546
+ : session.currentIntent;
547
+ console.log(` Task: ${truncated}`);
548
+ }
549
+ else {
550
+ console.log(` Task: none`);
551
+ }
552
+ // Plan status
553
+ if (session.lastPlan && session.lastPlan.length > 0) {
554
+ console.log(` Plan: ${session.lastPlan.length} step(s)`);
555
+ }
556
+ else {
557
+ console.log(` Plan: none`);
558
+ }
559
+ // Pending changes
560
+ if (session.pendingActions) {
561
+ const fileCount = (session.pendingActions.files?.length || 0);
562
+ const diffCount = (session.pendingActions.diffs?.length || 0);
563
+ console.log(` Pending: ${fileCount + diffCount} change(s)`);
564
+ }
565
+ else {
566
+ console.log(` Pending: none`);
567
+ }
568
+ console.log(` Undo: ${undoCount} operation(s) in stack`);
569
+ console.log('');
570
+ // Suggestions based on state
571
+ if (session.pendingActions || session.lastDiff) {
572
+ console.log((0, ui_1.hint)('/diff to review, /apply to execute'));
573
+ }
574
+ else if (session.lastPlan && session.lastPlan.length > 0) {
575
+ console.log((0, ui_1.hint)('/generate to create changes'));
576
+ }
577
+ else if (session.currentIntent) {
578
+ console.log((0, ui_1.hint)('/plan to create execution plan'));
579
+ }
580
+ else {
581
+ console.log((0, ui_1.hint)('Type a task to get started'));
582
+ }
583
+ },
584
+ // YOLO mode shortcut
585
+ yolo: () => {
586
+ (0, session_1.setMode)('auto');
587
+ console.log((0, ui_1.success)('⚡ YOLO mode activated!'));
588
+ console.log((0, ui_1.dim)('Tasks will execute directly without confirmation.'));
589
+ console.log((0, ui_1.hint)('Type a task or use /run <task>'));
590
+ },
591
+ // Quick mode switches
592
+ edit: () => {
593
+ (0, session_1.setMode)('edit');
594
+ console.log((0, ui_1.success)('Edit mode activated'));
595
+ console.log((0, ui_1.hint)('Type a task, then /plan → /generate → /diff → /apply'));
596
+ },
597
+ debug: () => {
598
+ (0, session_1.setMode)('debug');
599
+ console.log((0, ui_1.success)('🔧 Debug mode activated'));
600
+ console.log((0, ui_1.hint)('Describe the bug or use /fix <description>'));
601
+ },
602
+ review: () => {
603
+ (0, session_1.setMode)('review');
604
+ console.log((0, ui_1.success)('👁 Review mode activated'));
605
+ console.log((0, ui_1.hint)('Ask for code review or analysis'));
606
+ },
607
+ explain: () => {
608
+ (0, session_1.setMode)('explain');
609
+ console.log((0, ui_1.success)('📖 Explain mode activated'));
610
+ console.log((0, ui_1.hint)('Ask about code concepts'));
84
611
  },
85
612
  reset: () => {
86
613
  (0, session_1.resetSession)();
@@ -142,13 +669,68 @@ const handlers = {
142
669
  }
143
670
  },
144
671
  open: (ctx) => {
145
- const path = ctx.args[0];
146
- if (!path) {
672
+ const filePath = ctx.args[0];
673
+ if (!filePath) {
147
674
  console.log('Usage: /open <path>');
148
675
  return;
149
676
  }
150
- (0, session_1.addOpenFile)(path);
151
- console.log(`Added: ${path}`);
677
+ const session = (0, session_1.getSession)();
678
+ const fullPath = path.isAbsolute(filePath) ? filePath : path.join(session.workingDirectory, filePath);
679
+ if (!fs.existsSync(fullPath)) {
680
+ console.log((0, ui_1.error)(`File not found: ${filePath}`));
681
+ return;
682
+ }
683
+ (0, session_1.addOpenFile)(filePath);
684
+ console.log((0, ui_1.success)(`Added to context: ${filePath}`));
685
+ },
686
+ // Read/view file contents
687
+ read: (ctx) => {
688
+ const filePath = ctx.args[0];
689
+ if (!filePath) {
690
+ console.log('Usage: /read <path> [lines]');
691
+ return;
692
+ }
693
+ const session = (0, session_1.getSession)();
694
+ const fullPath = path.isAbsolute(filePath) ? filePath : path.join(session.workingDirectory, filePath);
695
+ if (!fs.existsSync(fullPath)) {
696
+ console.log((0, ui_1.error)(`File not found: ${filePath}`));
697
+ return;
698
+ }
699
+ const maxLines = ctx.args[1] ? parseInt(ctx.args[1], 10) : 50;
700
+ const content = (0, workspace_1.getFileContent)(fullPath, 100000);
701
+ if (!content) {
702
+ console.log((0, ui_1.error)('File too large or unreadable'));
703
+ return;
704
+ }
705
+ const lines = content.split('\n');
706
+ const displayLines = lines.slice(0, maxLines);
707
+ console.log((0, ui_1.dim)(`--- ${filePath} (${lines.length} lines) ---`));
708
+ displayLines.forEach((line, i) => {
709
+ console.log(`${String(i + 1).padStart(4)} | ${line}`);
710
+ });
711
+ if (lines.length > maxLines) {
712
+ console.log((0, ui_1.dim)(`... ${lines.length - maxLines} more lines`));
713
+ }
714
+ },
715
+ // Cat file (alias for read, full content)
716
+ cat: (ctx) => {
717
+ const filePath = ctx.args[0];
718
+ if (!filePath) {
719
+ console.log('Usage: /cat <path>');
720
+ return;
721
+ }
722
+ const session = (0, session_1.getSession)();
723
+ const fullPath = path.isAbsolute(filePath) ? filePath : path.join(session.workingDirectory, filePath);
724
+ if (!fs.existsSync(fullPath)) {
725
+ console.log((0, ui_1.error)(`File not found: ${filePath}`));
726
+ return;
727
+ }
728
+ const content = (0, workspace_1.getFileContent)(fullPath, 500000);
729
+ if (!content) {
730
+ console.log((0, ui_1.error)('File too large or unreadable'));
731
+ return;
732
+ }
733
+ console.log(content);
152
734
  },
153
735
  mode: (ctx) => {
154
736
  const newMode = ctx.args[0]?.toLowerCase();
@@ -272,7 +854,7 @@ const handlers = {
272
854
  console.log((0, ui_1.error)(`Generation failed: ${e?.message || e}`));
273
855
  }
274
856
  },
275
- diff: () => {
857
+ diff: (ctx) => {
276
858
  const session = (0, session_1.getSession)();
277
859
  // Check if there's a diff to display
278
860
  if (!session.lastDiff) {
@@ -280,12 +862,25 @@ const handlers = {
280
862
  return;
281
863
  }
282
864
  const response = session.lastDiff;
865
+ const showFull = ctx.args[0] === 'full';
283
866
  // Display file operations if present
284
867
  if (response.files && response.files.length > 0) {
285
868
  for (const file of response.files) {
286
- console.log(`--- ${file.operation}: ${file.path} ---`);
869
+ const opColor = file.operation === 'create' ? '\x1b[32m' :
870
+ file.operation === 'delete' ? '\x1b[31m' : '\x1b[33m';
871
+ const reset = '\x1b[0m';
872
+ console.log(`${opColor}--- ${file.operation.toUpperCase()}: ${file.path} ---${reset}`);
287
873
  if (file.content && file.operation !== 'delete') {
288
- console.log(file.content);
874
+ const lines = file.content.split('\n');
875
+ const maxLines = showFull ? lines.length : 50;
876
+ for (let i = 0; i < Math.min(lines.length, maxLines); i++) {
877
+ const lineNum = String(i + 1).padStart(4);
878
+ const prefix = file.operation === 'create' ? '\x1b[32m+' : ' ';
879
+ console.log(`${prefix}${lineNum} | ${lines[i]}${reset}`);
880
+ }
881
+ if (lines.length > maxLines) {
882
+ console.log((0, ui_1.dim)(`... ${lines.length - maxLines} more lines (use /diff full to see all)`));
883
+ }
289
884
  }
290
885
  console.log('');
291
886
  }
@@ -293,33 +888,49 @@ const handlers = {
293
888
  // Display diffs if present
294
889
  if (response.diffs && response.diffs.length > 0) {
295
890
  for (const diff of response.diffs) {
296
- console.log(`--- ${diff.file} ---`);
891
+ console.log(`\x1b[33m--- MODIFY: ${diff.file} ---\x1b[0m`);
297
892
  for (const hunk of diff.hunks) {
298
- console.log(`@@ -${hunk.start},${hunk.end - hunk.start + 1} @@`);
299
- console.log(hunk.content);
893
+ console.log(`\x1b[36m@@ lines ${hunk.start}-${hunk.end} @@\x1b[0m`);
894
+ const lines = hunk.content.split('\n');
895
+ for (const line of lines) {
896
+ if (line.startsWith('+')) {
897
+ console.log(`\x1b[32m${line}\x1b[0m`);
898
+ }
899
+ else if (line.startsWith('-')) {
900
+ console.log(`\x1b[31m${line}\x1b[0m`);
901
+ }
902
+ else {
903
+ console.log(line);
904
+ }
905
+ }
300
906
  }
301
907
  console.log('');
302
908
  }
303
909
  }
304
- // If neither files nor diffs present
305
- if ((!response.files || response.files.length === 0) &&
306
- (!response.diffs || response.diffs.length === 0)) {
910
+ // Summary
911
+ const fileCount = (response.files?.length || 0);
912
+ const diffCount = (response.diffs?.length || 0);
913
+ if (fileCount === 0 && diffCount === 0) {
307
914
  console.log('No file changes in last response.');
308
915
  return;
309
916
  }
917
+ console.log((0, ui_1.dim)(`Total: ${fileCount} file operation(s), ${diffCount} diff(s)`));
918
+ console.log((0, ui_1.hint)('/apply to execute changes'));
310
919
  },
311
- apply: () => {
920
+ apply: (ctx) => {
312
921
  const session = (0, session_1.getSession)();
922
+ const force = ctx.args[0] === '--force' || ctx.args[0] === '-f';
313
923
  // Check dry run mode
314
- if (session.dryRun) {
924
+ if (session.dryRun && !force) {
315
925
  console.log((0, ui_1.error)('Dry-run mode. Apply blocked.'));
316
- console.log((0, ui_1.hint)('/dry-run off'));
926
+ console.log((0, ui_1.hint)('/dry-run off or /apply --force'));
317
927
  return;
318
928
  }
319
929
  // Warn on dirty git
320
930
  const gitInfo = (0, git_1.getGitInfo)(session.workingDirectory);
321
- if (gitInfo.isRepo && gitInfo.isDirty) {
322
- console.log((0, ui_1.info)('Warning: uncommitted changes exist.'));
931
+ if (gitInfo.isRepo && gitInfo.isDirty && !force) {
932
+ console.log((0, ui_1.info)('Warning: uncommitted git changes exist.'));
933
+ console.log((0, ui_1.hint)('Consider committing first, or use /apply --force'));
323
934
  }
324
935
  // Check if there are pending actions
325
936
  if (!session.pendingActions) {
@@ -333,26 +944,49 @@ const handlers = {
333
944
  }
334
945
  const actions = session.pendingActions;
335
946
  // Check if there are actual file operations
336
- if ((!actions.files || actions.files.length === 0) &&
337
- (!actions.diffs || actions.diffs.length === 0)) {
947
+ const fileCount = (actions.files?.length || 0);
948
+ const diffCount = (actions.diffs?.length || 0);
949
+ if (fileCount === 0 && diffCount === 0) {
338
950
  console.log('No file changes to apply.');
339
951
  return;
340
952
  }
953
+ console.log((0, ui_1.info)(`Applying ${fileCount + diffCount} change(s)...`));
341
954
  // Apply using the apply engine
342
955
  const result = (0, apply_1.applyResponse)(actions, {
343
956
  basePath: session.workingDirectory,
344
957
  dryRun: false,
345
958
  });
959
+ // Show results
960
+ for (const applied of result.applied) {
961
+ console.log((0, ui_1.success)(` ${applied}`));
962
+ }
963
+ for (const failed of result.failed) {
964
+ console.log((0, ui_1.error)(` Failed: ${failed.path}: ${failed.error}`));
965
+ }
346
966
  if (!result.success) {
347
- for (const failed of result.failed) {
348
- console.log((0, ui_1.error)(`Failed: ${failed.path}: ${failed.error}`));
349
- }
967
+ console.log((0, ui_1.error)(`\nSome operations failed. Use /undo to rollback.`));
350
968
  return;
351
969
  }
352
970
  // Clear pending actions after success
353
971
  (0, session_1.setPendingActions)(null);
354
- console.log('Applied.');
355
- console.log('Session clean.');
972
+ (0, session_1.setLastDiff)(null);
973
+ (0, session_1.clearIntent)();
974
+ (0, session_1.setLastPlan)(null);
975
+ console.log('');
976
+ console.log((0, ui_1.success)(`Applied ${result.applied.length} change(s) successfully.`));
977
+ console.log((0, ui_1.hint)('/undo to rollback'));
978
+ // Log to history
979
+ if (session.currentIntent) {
980
+ (0, history_1.logTask)({
981
+ timestamp: new Date().toISOString(),
982
+ intent: session.currentIntent,
983
+ intentType: session.intentType || 'COMMAND',
984
+ mode: session.mode,
985
+ model: (0, settings_1.getModel)(),
986
+ filesCount: result.applied.length,
987
+ outcome: 'success',
988
+ });
989
+ }
356
990
  },
357
991
  workspace: () => {
358
992
  const ws = (0, workspace_model_1.getWorkspace)();
@@ -360,6 +994,54 @@ const handlers = {
360
994
  console.log(ws.printTreeSummary());
361
995
  console.log(`Root: ${ws.getRoot()}`);
362
996
  },
997
+ // Tree view of workspace
998
+ tree: (ctx) => {
999
+ const maxDepth = ctx.args[0] ? parseInt(ctx.args[0], 10) : 3;
1000
+ const session = (0, session_1.getSession)();
1001
+ const ws = (0, workspace_model_1.getWorkspace)(session.workingDirectory);
1002
+ const tree = ws.indexFileTree(maxDepth);
1003
+ function printTree(node, prefix = '', isLast = true) {
1004
+ const connector = isLast ? '└── ' : '├── ';
1005
+ const extension = isLast ? ' ' : '│ ';
1006
+ if (node.path !== '.') {
1007
+ const icon = node.type === 'directory' ? '📁' : '📄';
1008
+ console.log(`${prefix}${connector}${icon} ${node.name}`);
1009
+ }
1010
+ if (node.children) {
1011
+ const children = node.children;
1012
+ children.forEach((child, index) => {
1013
+ const childIsLast = index === children.length - 1;
1014
+ const newPrefix = node.path === '.' ? '' : prefix + extension;
1015
+ printTree(child, newPrefix, childIsLast);
1016
+ });
1017
+ }
1018
+ }
1019
+ console.log((0, ui_1.dim)(`${session.workingDirectory}`));
1020
+ printTree(tree);
1021
+ },
1022
+ // List files matching pattern
1023
+ ls: (ctx) => {
1024
+ const pattern = ctx.args[0] || '.';
1025
+ const session = (0, session_1.getSession)();
1026
+ const targetPath = path.isAbsolute(pattern) ? pattern : path.join(session.workingDirectory, pattern);
1027
+ try {
1028
+ const stats = fs.statSync(targetPath);
1029
+ if (stats.isDirectory()) {
1030
+ const entries = fs.readdirSync(targetPath, { withFileTypes: true });
1031
+ for (const entry of entries) {
1032
+ const icon = entry.isDirectory() ? '📁' : '📄';
1033
+ const size = entry.isFile() ? ` (${fs.statSync(path.join(targetPath, entry.name)).size} bytes)` : '';
1034
+ console.log(`${icon} ${entry.name}${size}`);
1035
+ }
1036
+ }
1037
+ else {
1038
+ console.log(`${pattern}: ${stats.size} bytes`);
1039
+ }
1040
+ }
1041
+ catch (e) {
1042
+ console.log((0, ui_1.error)(`Cannot access: ${pattern}`));
1043
+ }
1044
+ },
363
1045
  doctor: async () => {
364
1046
  const { runDiagnostics, formatDiagnostics } = await Promise.resolve().then(() => __importStar(require('./doctor')));
365
1047
  const results = await runDiagnostics();
@@ -573,29 +1255,321 @@ const handlers = {
573
1255
  console.log('Usage: /dry-run [on | off]');
574
1256
  }
575
1257
  },
576
- git: () => {
1258
+ git: (ctx) => {
577
1259
  const session = (0, session_1.getSession)();
578
- const info = (0, git_1.getGitInfo)(session.workingDirectory);
579
- if (!info.isRepo) {
1260
+ const gitInfo = (0, git_1.getGitInfo)(session.workingDirectory);
1261
+ const subcommand = ctx.args[0];
1262
+ if (!gitInfo.isRepo) {
580
1263
  console.log('Not a git repository.');
581
1264
  return;
582
1265
  }
583
- console.log(`Repository: ${info.repoName}`);
584
- console.log(`Branch: ${info.branch}`);
585
- console.log(`Status: ${info.isDirty ? 'dirty' : 'clean'}`);
586
- if (info.uncommittedFiles > 0) {
587
- console.log(`Uncommitted files: ${info.uncommittedFiles}`);
1266
+ // /git status (default)
1267
+ if (!subcommand || subcommand === 'status') {
1268
+ console.log(`Repository: ${gitInfo.repoName}`);
1269
+ console.log(`Branch: ${gitInfo.branch}`);
1270
+ console.log(`Status: ${gitInfo.isDirty ? 'dirty' : 'clean'}`);
1271
+ if (gitInfo.uncommittedFiles > 0) {
1272
+ console.log(`Uncommitted files: ${gitInfo.uncommittedFiles}`);
1273
+ // Show changed files
1274
+ try {
1275
+ const { execSync } = require('child_process');
1276
+ const status = execSync('git status --porcelain', {
1277
+ cwd: session.workingDirectory,
1278
+ encoding: 'utf-8',
1279
+ stdio: ['pipe', 'pipe', 'pipe']
1280
+ });
1281
+ const lines = status.trim().split('\n').filter((l) => l);
1282
+ for (const line of lines.slice(0, 10)) {
1283
+ const status = line.substring(0, 2);
1284
+ const file = line.substring(3);
1285
+ const statusColor = status.includes('M') ? '\x1b[33m' :
1286
+ status.includes('A') ? '\x1b[32m' :
1287
+ status.includes('D') ? '\x1b[31m' : '\x1b[36m';
1288
+ console.log(` ${statusColor}${status}\x1b[0m ${file}`);
1289
+ }
1290
+ if (lines.length > 10) {
1291
+ console.log((0, ui_1.dim)(` ... and ${lines.length - 10} more`));
1292
+ }
1293
+ }
1294
+ catch {
1295
+ // Ignore
1296
+ }
1297
+ }
1298
+ return;
1299
+ }
1300
+ // /git log
1301
+ if (subcommand === 'log') {
1302
+ try {
1303
+ const { execSync } = require('child_process');
1304
+ const log = execSync('git log --oneline -10', {
1305
+ cwd: session.workingDirectory,
1306
+ encoding: 'utf-8'
1307
+ });
1308
+ console.log('Recent commits:');
1309
+ console.log(log);
1310
+ }
1311
+ catch (e) {
1312
+ console.log((0, ui_1.error)('Failed to get git log'));
1313
+ }
1314
+ return;
1315
+ }
1316
+ // /git diff
1317
+ if (subcommand === 'diff') {
1318
+ try {
1319
+ const { execSync } = require('child_process');
1320
+ const diff = execSync('git diff --stat', {
1321
+ cwd: session.workingDirectory,
1322
+ encoding: 'utf-8'
1323
+ });
1324
+ if (diff.trim()) {
1325
+ console.log(diff);
1326
+ }
1327
+ else {
1328
+ console.log('No unstaged changes.');
1329
+ }
1330
+ }
1331
+ catch (e) {
1332
+ console.log((0, ui_1.error)('Failed to get git diff'));
1333
+ }
1334
+ return;
1335
+ }
1336
+ // /git stash
1337
+ if (subcommand === 'stash') {
1338
+ try {
1339
+ const { execSync } = require('child_process');
1340
+ execSync('git stash', { cwd: session.workingDirectory, stdio: 'pipe' });
1341
+ console.log((0, ui_1.success)('Changes stashed'));
1342
+ }
1343
+ catch (e) {
1344
+ console.log((0, ui_1.error)('Failed to stash'));
1345
+ }
1346
+ return;
1347
+ }
1348
+ // /git pop
1349
+ if (subcommand === 'pop') {
1350
+ try {
1351
+ const { execSync } = require('child_process');
1352
+ execSync('git stash pop', { cwd: session.workingDirectory, stdio: 'pipe' });
1353
+ console.log((0, ui_1.success)('Stash popped'));
1354
+ }
1355
+ catch (e) {
1356
+ console.log((0, ui_1.error)('Failed to pop stash'));
1357
+ }
1358
+ return;
1359
+ }
1360
+ console.log('Usage: /git [status|log|diff|stash|pop]');
1361
+ },
1362
+ // Create new file
1363
+ touch: (ctx) => {
1364
+ const filePath = ctx.args[0];
1365
+ if (!filePath) {
1366
+ console.log('Usage: /touch <path>');
1367
+ return;
1368
+ }
1369
+ const session = (0, session_1.getSession)();
1370
+ const fullPath = path.isAbsolute(filePath) ? filePath : path.join(session.workingDirectory, filePath);
1371
+ if (fs.existsSync(fullPath)) {
1372
+ console.log((0, ui_1.error)(`File already exists: ${filePath}`));
1373
+ return;
1374
+ }
1375
+ try {
1376
+ const dir = path.dirname(fullPath);
1377
+ if (!fs.existsSync(dir)) {
1378
+ fs.mkdirSync(dir, { recursive: true });
1379
+ }
1380
+ fs.writeFileSync(fullPath, '', 'utf-8');
1381
+ console.log((0, ui_1.success)(`Created: ${filePath}`));
1382
+ (0, session_1.addOpenFile)(filePath);
1383
+ }
1384
+ catch (e) {
1385
+ console.log((0, ui_1.error)(`Failed to create: ${e?.message}`));
1386
+ }
1387
+ },
1388
+ // Make directory
1389
+ mkdir: (ctx) => {
1390
+ const dirPath = ctx.args[0];
1391
+ if (!dirPath) {
1392
+ console.log('Usage: /mkdir <path>');
1393
+ return;
1394
+ }
1395
+ const session = (0, session_1.getSession)();
1396
+ const fullPath = path.isAbsolute(dirPath) ? dirPath : path.join(session.workingDirectory, dirPath);
1397
+ if (fs.existsSync(fullPath)) {
1398
+ console.log((0, ui_1.error)(`Already exists: ${dirPath}`));
1399
+ return;
1400
+ }
1401
+ try {
1402
+ fs.mkdirSync(fullPath, { recursive: true });
1403
+ console.log((0, ui_1.success)(`Created directory: ${dirPath}`));
1404
+ }
1405
+ catch (e) {
1406
+ console.log((0, ui_1.error)(`Failed to create: ${e?.message}`));
1407
+ }
1408
+ },
1409
+ // What now - contextual suggestions
1410
+ whatnow: () => {
1411
+ const session = (0, session_1.getSession)();
1412
+ const gitInfo = (0, git_1.getGitInfo)(session.workingDirectory);
1413
+ console.log('');
1414
+ console.log((0, ui_1.info)('What to do next:'));
1415
+ console.log('');
1416
+ // Based on current state
1417
+ if (session.pendingActions || session.lastDiff) {
1418
+ const count = (session.pendingActions?.files?.length || 0) + (session.pendingActions?.diffs?.length || 0);
1419
+ console.log(` You have ${count} pending change(s).`);
1420
+ console.log('');
1421
+ console.log(' /diff Review the changes');
1422
+ console.log(' /diff full See full file contents');
1423
+ console.log(' /apply Apply the changes');
1424
+ console.log(' /apply -f Force apply (skip warnings)');
1425
+ console.log(' /clear Discard and start over');
1426
+ console.log('');
1427
+ }
1428
+ else if (session.lastPlan && session.lastPlan.length > 0) {
1429
+ console.log(` You have a plan with ${session.lastPlan.length} step(s).`);
1430
+ console.log('');
1431
+ console.log(' /generate Create file changes from plan');
1432
+ console.log(' /plan Regenerate the plan');
1433
+ console.log(' /clear Discard and start over');
1434
+ console.log('');
1435
+ }
1436
+ else if (session.currentIntent) {
1437
+ console.log(` Task: "${session.currentIntent.substring(0, 50)}..."`);
1438
+ console.log('');
1439
+ console.log(' /plan Create execution plan');
1440
+ console.log(' /do Plan + generate in one step');
1441
+ console.log(' /run Plan + generate + apply (YOLO)');
1442
+ console.log(' /clear Clear task and start over');
1443
+ console.log('');
588
1444
  }
1445
+ else {
1446
+ console.log(' No active task. Here are some ideas:');
1447
+ console.log('');
1448
+ console.log(' Type a task naturally:');
1449
+ console.log(' "add error handling to auth.ts"');
1450
+ console.log(' "create a new React component for user profile"');
1451
+ console.log(' "fix the bug in the login function"');
1452
+ console.log('');
1453
+ console.log(' Or use quick commands:');
1454
+ console.log(' /do <task> Plan + generate');
1455
+ console.log(' /run <task> Full auto execution');
1456
+ console.log(' /ask <q> Ask a question');
1457
+ console.log(' /fix <desc> Debug an issue');
1458
+ console.log('');
1459
+ }
1460
+ // Git suggestions
1461
+ if (gitInfo.isRepo && gitInfo.isDirty) {
1462
+ console.log((0, ui_1.dim)(` Git: ${gitInfo.uncommittedFiles} uncommitted file(s)`));
1463
+ console.log(' /commit Generate AI commit message');
1464
+ console.log(' /git stash Stash changes');
1465
+ console.log('');
1466
+ }
1467
+ // Mode suggestion
1468
+ if (session.mode === 'edit') {
1469
+ console.log((0, ui_1.dim)(' Tip: Use /yolo for autonomous execution'));
1470
+ }
1471
+ },
1472
+ // Alias for whatnow
1473
+ whatsnext: () => {
1474
+ handlers.whatnow({ args: [], rawInput: '/whatnow' });
589
1475
  },
1476
+ // Quick examples
1477
+ examples: () => {
1478
+ console.log('');
1479
+ console.log((0, ui_1.info)('Example Tasks:'));
1480
+ console.log('');
1481
+ console.log((0, ui_1.dim)('Code Generation:'));
1482
+ console.log(' "create a REST API endpoint for user registration"');
1483
+ console.log(' "add a new React component for displaying user cards"');
1484
+ console.log(' "implement a binary search function in TypeScript"');
1485
+ console.log('');
1486
+ console.log((0, ui_1.dim)('Bug Fixes:'));
1487
+ console.log(' "fix the null pointer exception in auth.ts line 42"');
1488
+ console.log(' "the login button doesn\'t work, fix it"');
1489
+ console.log(' "users can\'t logout, investigate and fix"');
1490
+ console.log('');
1491
+ console.log((0, ui_1.dim)('Refactoring:'));
1492
+ console.log(' "refactor the database module to use async/await"');
1493
+ console.log(' "split the UserService into smaller modules"');
1494
+ console.log(' "add TypeScript types to the utils folder"');
1495
+ console.log('');
1496
+ console.log((0, ui_1.dim)('Documentation:'));
1497
+ console.log(' "add JSDoc comments to all exported functions"');
1498
+ console.log(' "create a README for the project"');
1499
+ console.log(' "document the API endpoints"');
1500
+ console.log('');
1501
+ console.log((0, ui_1.hint)('Just type naturally - zai·code understands context'));
1502
+ },
1503
+ };
1504
+ // Command aliases for convenience
1505
+ const COMMAND_ALIASES = {
1506
+ // Single letter shortcuts
1507
+ 'h': 'help',
1508
+ '?': 'help',
1509
+ 'q': 'exit',
1510
+ 'quit': 'exit',
1511
+ 's': 'status',
1512
+ 'p': 'plan',
1513
+ 'g': 'generate',
1514
+ 'd': 'diff',
1515
+ 'a': 'apply',
1516
+ 'u': 'undo',
1517
+ 'c': 'context',
1518
+ 'f': 'files',
1519
+ 'm': 'mode',
1520
+ 'r': 'run',
1521
+ 'x': 'exec',
1522
+ 'o': 'open',
1523
+ 't': 'tree',
1524
+ 'v': 'version',
1525
+ // Word aliases
1526
+ 'gen': 'generate',
1527
+ 'show': 'diff',
1528
+ 'view': 'read',
1529
+ 'cat': 'read',
1530
+ 'ls': 'tree',
1531
+ 'list': 'files',
1532
+ 'add': 'open',
1533
+ 'rm': 'close',
1534
+ 'remove': 'close',
1535
+ 'find': 'search',
1536
+ 'grep': 'search',
1537
+ 'auto': 'yolo',
1538
+ 'quick': 'do',
1539
+ 'execute': 'run',
1540
+ 'rollback': 'undo',
1541
+ 'revert': 'undo',
1542
+ 'info': 'status',
1543
+ 'state': 'status',
1544
+ 'check': 'doctor',
1545
+ 'health': 'doctor',
1546
+ 'cfg': 'settings',
1547
+ 'config': 'settings',
1548
+ 'prefs': 'settings',
1549
+ 'preferences': 'settings',
590
1550
  };
591
1551
  // Execute a parsed slash command
592
1552
  async function executeCommand(parsed) {
593
1553
  if (!parsed.isSlashCommand || !parsed.command) {
594
1554
  return false;
595
1555
  }
596
- const handler = handlers[parsed.command];
1556
+ // Resolve alias
1557
+ const command = COMMAND_ALIASES[parsed.command] || parsed.command;
1558
+ const handler = handlers[command];
597
1559
  if (!handler) {
1560
+ // Check for partial match
1561
+ const matches = Object.keys(handlers).filter(h => h.startsWith(parsed.command));
1562
+ if (matches.length === 1) {
1563
+ await handlers[matches[0]]({ args: parsed.args || [], rawInput: parsed.rawInput });
1564
+ return true;
1565
+ }
1566
+ else if (matches.length > 1) {
1567
+ console.log(`Ambiguous command: /${parsed.command}`);
1568
+ console.log(`Did you mean: ${matches.map(m => '/' + m).join(', ')}?`);
1569
+ return true;
1570
+ }
598
1571
  console.log(`Unknown command: /${parsed.command}`);
1572
+ console.log((0, ui_1.dim)('Run /help for available commands'));
599
1573
  return true;
600
1574
  }
601
1575
  await handler({ args: parsed.args || [], rawInput: parsed.rawInput });