openhorse 0.2.5 → 0.2.8

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 (150) hide show
  1. package/dist/commands/index.d.ts.map +1 -1
  2. package/dist/commands/index.js +188 -2
  3. package/dist/commands/index.js.map +1 -1
  4. package/dist/commands/types.d.ts +21 -0
  5. package/dist/commands/types.d.ts.map +1 -1
  6. package/dist/commands/types.js.map +1 -1
  7. package/dist/core/checkpoint.d.ts +42 -0
  8. package/dist/core/checkpoint.d.ts.map +1 -0
  9. package/dist/core/checkpoint.js +222 -0
  10. package/dist/core/checkpoint.js.map +1 -0
  11. package/dist/core/tool-artifacts.d.ts +38 -0
  12. package/dist/core/tool-artifacts.d.ts.map +1 -0
  13. package/dist/core/tool-artifacts.js +138 -0
  14. package/dist/core/tool-artifacts.js.map +1 -0
  15. package/dist/core/warn-dedup.d.ts +39 -0
  16. package/dist/core/warn-dedup.d.ts.map +1 -0
  17. package/dist/core/warn-dedup.js +80 -0
  18. package/dist/core/warn-dedup.js.map +1 -0
  19. package/dist/framework/index.d.ts +3 -0
  20. package/dist/framework/index.d.ts.map +1 -1
  21. package/dist/framework/index.js +9 -1
  22. package/dist/framework/index.js.map +1 -1
  23. package/dist/framework/query.d.ts +4 -0
  24. package/dist/framework/query.d.ts.map +1 -1
  25. package/dist/framework/query.js +55 -139
  26. package/dist/framework/query.js.map +1 -1
  27. package/dist/framework/tool-scheduler.d.ts +103 -0
  28. package/dist/framework/tool-scheduler.d.ts.map +1 -0
  29. package/dist/framework/tool-scheduler.js +242 -0
  30. package/dist/framework/tool-scheduler.js.map +1 -0
  31. package/dist/framework/tool-serializer.d.ts +21 -0
  32. package/dist/framework/tool-serializer.d.ts.map +1 -0
  33. package/dist/framework/tool-serializer.js +68 -0
  34. package/dist/framework/tool-serializer.js.map +1 -0
  35. package/dist/framework/tool-state.d.ts +13 -0
  36. package/dist/framework/tool-state.d.ts.map +1 -1
  37. package/dist/framework/tool-state.js +1 -0
  38. package/dist/framework/tool-state.js.map +1 -1
  39. package/dist/framework/tool.d.ts +18 -1
  40. package/dist/framework/tool.d.ts.map +1 -1
  41. package/dist/framework/tool.js.map +1 -1
  42. package/dist/harness/context-harness.d.ts +1 -0
  43. package/dist/harness/context-harness.d.ts.map +1 -1
  44. package/dist/harness/context-harness.js +14 -10
  45. package/dist/harness/context-harness.js.map +1 -1
  46. package/dist/harness/evidence.d.ts +5 -0
  47. package/dist/harness/evidence.d.ts.map +1 -1
  48. package/dist/harness/evidence.js +23 -0
  49. package/dist/harness/evidence.js.map +1 -1
  50. package/dist/harness/index.d.ts +1 -1
  51. package/dist/harness/index.d.ts.map +1 -1
  52. package/dist/harness/index.js +2 -1
  53. package/dist/harness/index.js.map +1 -1
  54. package/dist/harness/intent.d.ts.map +1 -1
  55. package/dist/harness/intent.js +8 -0
  56. package/dist/harness/intent.js.map +1 -1
  57. package/dist/harness/ledger.d.ts +1 -0
  58. package/dist/harness/ledger.d.ts.map +1 -1
  59. package/dist/harness/ledger.js +2 -1
  60. package/dist/harness/ledger.js.map +1 -1
  61. package/dist/harness/types.d.ts +4 -1
  62. package/dist/harness/types.d.ts.map +1 -1
  63. package/dist/ink-ui/screens/ReplScreen.d.ts.map +1 -1
  64. package/dist/ink-ui/screens/ReplScreen.js +29 -0
  65. package/dist/ink-ui/screens/ReplScreen.js.map +1 -1
  66. package/dist/memory/storage.d.ts +2 -0
  67. package/dist/memory/storage.d.ts.map +1 -1
  68. package/dist/memory/storage.js +56 -32
  69. package/dist/memory/storage.js.map +1 -1
  70. package/dist/memory/vector-store.d.ts +23 -3
  71. package/dist/memory/vector-store.d.ts.map +1 -1
  72. package/dist/memory/vector-store.js +107 -37
  73. package/dist/memory/vector-store.js.map +1 -1
  74. package/dist/print-ui/launch.d.ts.map +1 -1
  75. package/dist/print-ui/launch.js +7 -0
  76. package/dist/print-ui/launch.js.map +1 -1
  77. package/dist/runtime/agent-runtime-controller.js +1 -1
  78. package/dist/runtime/agent-runtime-controller.js.map +1 -1
  79. package/dist/runtime/agent-runtime-protocol.d.ts +4 -1
  80. package/dist/runtime/agent-runtime-protocol.d.ts.map +1 -1
  81. package/dist/runtime/agent-runtime-protocol.js +6 -0
  82. package/dist/runtime/agent-runtime-protocol.js.map +1 -1
  83. package/dist/runtime/chat-controller.d.ts +1 -0
  84. package/dist/runtime/chat-controller.d.ts.map +1 -1
  85. package/dist/runtime/chat-controller.js +47 -9
  86. package/dist/runtime/chat-controller.js.map +1 -1
  87. package/dist/runtime/ui-events.d.ts +17 -0
  88. package/dist/runtime/ui-events.d.ts.map +1 -1
  89. package/dist/services/concurrent-sessions.d.ts.map +1 -1
  90. package/dist/services/concurrent-sessions.js +6 -7
  91. package/dist/services/concurrent-sessions.js.map +1 -1
  92. package/dist/services/config-dir.d.ts +29 -6
  93. package/dist/services/config-dir.d.ts.map +1 -1
  94. package/dist/services/config-dir.js +118 -17
  95. package/dist/services/config-dir.js.map +1 -1
  96. package/dist/services/doctor.d.ts.map +1 -1
  97. package/dist/services/doctor.js +137 -0
  98. package/dist/services/doctor.js.map +1 -1
  99. package/dist/services/llm.d.ts +17 -0
  100. package/dist/services/llm.d.ts.map +1 -1
  101. package/dist/services/llm.js +11 -0
  102. package/dist/services/llm.js.map +1 -1
  103. package/dist/services/session-index.d.ts +5 -0
  104. package/dist/services/session-index.d.ts.map +1 -1
  105. package/dist/services/session-index.js +19 -10
  106. package/dist/services/session-index.js.map +1 -1
  107. package/dist/services/session-storage.d.ts +10 -0
  108. package/dist/services/session-storage.d.ts.map +1 -1
  109. package/dist/services/session-storage.js +80 -69
  110. package/dist/services/session-storage.js.map +1 -1
  111. package/dist/services/storage-maintenance.d.ts +53 -0
  112. package/dist/services/storage-maintenance.d.ts.map +1 -0
  113. package/dist/services/storage-maintenance.js +375 -0
  114. package/dist/services/storage-maintenance.js.map +1 -0
  115. package/dist/services/web-search-adapters.d.ts.map +1 -1
  116. package/dist/services/web-search-adapters.js +20 -1
  117. package/dist/services/web-search-adapters.js.map +1 -1
  118. package/dist/services/web-search-mcp.d.ts.map +1 -1
  119. package/dist/services/web-search-mcp.js +20 -1
  120. package/dist/services/web-search-mcp.js.map +1 -1
  121. package/dist/skills/loader.d.ts.map +1 -1
  122. package/dist/skills/loader.js +4 -3
  123. package/dist/skills/loader.js.map +1 -1
  124. package/dist/terminal-ui/launch.d.ts.map +1 -1
  125. package/dist/terminal-ui/launch.js +40 -18
  126. package/dist/terminal-ui/launch.js.map +1 -1
  127. package/dist/tools/index.d.ts +10 -2
  128. package/dist/tools/index.d.ts.map +1 -1
  129. package/dist/tools/index.js +154 -28
  130. package/dist/tools/index.js.map +1 -1
  131. package/dist/tui-ui/layout.js +22 -0
  132. package/dist/tui-ui/layout.js.map +1 -1
  133. package/dist/tui-ui/runner.d.ts.map +1 -1
  134. package/dist/tui-ui/runner.js +21 -0
  135. package/dist/tui-ui/runner.js.map +1 -1
  136. package/dist/tui-ui/state.d.ts +8 -1
  137. package/dist/tui-ui/state.d.ts.map +1 -1
  138. package/dist/tui-ui/state.js +8 -0
  139. package/dist/tui-ui/state.js.map +1 -1
  140. package/dist/ui-v2/components/edit-preview-picker.d.ts +26 -0
  141. package/dist/ui-v2/components/edit-preview-picker.d.ts.map +1 -0
  142. package/dist/ui-v2/components/edit-preview-picker.js +79 -0
  143. package/dist/ui-v2/components/edit-preview-picker.js.map +1 -0
  144. package/dist/ui-v2/index.d.ts +1 -0
  145. package/dist/ui-v2/index.d.ts.map +1 -1
  146. package/dist/ui-v2/index.js +1 -0
  147. package/dist/ui-v2/index.js.map +1 -1
  148. package/dist/ui-v2/types.d.ts +1 -1
  149. package/dist/ui-v2/types.d.ts.map +1 -1
  150. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,aAAa,EAEnB,MAAM,SAAS,CAAC;AAmFjB,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,eAAe,GAAG,SAAS,GAAG,MAAM,CAErF;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CAQrE;AA2qBD,iBAAe,UAAU,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA6SpF;AA08BD,wBAAgB,WAAW,IAAI,YAAY,EAAE,CAE5C;AAED,wBAAgB,kBAAkB,IAAI,YAAY,EAAE,CAEnD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAElE;AAED,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAE1C;AAED,OAAO,EAAE,UAAU,IAAI,WAAW,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,aAAa,EAEnB,MAAM,SAAS,CAAC;AA2FjB,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,eAAe,GAAG,SAAS,GAAG,MAAM,CAErF;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CAQrE;AA2qBD,iBAAe,UAAU,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAuTpF;AAuoCD,wBAAgB,WAAW,IAAI,YAAY,EAAE,CAE5C;AAED,wBAAgB,kBAAkB,IAAI,YAAY,EAAE,CAEnD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAElE;AAED,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAE1C;AAED,OAAO,EAAE,UAAU,IAAI,WAAW,EAAE,CAAC"}
@@ -29,6 +29,7 @@ const framework_1 = require("../framework");
29
29
  const tools_1 = require("../tools");
30
30
  const mcp_1 = require("../tools/mcp");
31
31
  const session_storage_1 = require("../services/session-storage");
32
+ const session_index_1 = require("../services/session-index");
32
33
  const auto_compact_1 = require("../services/compact/auto-compact");
33
34
  const harness_1 = require("../harness");
34
35
  const skills_1 = require("../skills");
@@ -38,6 +39,7 @@ const prompt_context_1 = require("../services/prompt-context");
38
39
  const doctor_1 = require("../services/doctor");
39
40
  const workspace_diff_1 = require("../services/workspace-diff");
40
41
  const commit_plan_1 = require("../services/commit-plan");
42
+ const storage_maintenance_1 = require("../services/storage-maintenance");
41
43
  // ============================================================================
42
44
  // 颜色常量
43
45
  // ============================================================================
@@ -791,7 +793,15 @@ async function handleChat(ctx, input) {
791
793
  : `Tool ${name} is not available.`,
792
794
  });
793
795
  }
794
- const result = await (0, tools_1.executeTool)(name, args, abortSignal);
796
+ const result = await (0, tools_1.executeTool)(name, args, abortSignal, {
797
+ cwd: ctx.cwd,
798
+ config: {
799
+ name: ctx.config.name,
800
+ mode: ctx.config.mode,
801
+ },
802
+ sessionId,
803
+ turnId: ctx.turnId,
804
+ });
795
805
  // 不在这里打印,让 tool_result 事件处理
796
806
  return result;
797
807
  };
@@ -837,6 +847,8 @@ async function handleChat(ctx, input) {
837
847
  name: ctx.config.name,
838
848
  mode: ctx.config.mode,
839
849
  },
850
+ sessionId,
851
+ turnId: ctx.turnId,
840
852
  },
841
853
  abortSignal: ctx.abortSignal,
842
854
  harness,
@@ -1099,6 +1111,45 @@ function handleDoctor(ctx) {
1099
1111
  console.log();
1100
1112
  return { success: !(0, doctor_1.hasDoctorFailures)(report) };
1101
1113
  }
1114
+ function handleStorage(_ctx, args) {
1115
+ const trimmed = args.trim();
1116
+ const [action = 'doctor'] = trimmed.split(/\s+/);
1117
+ if (action === 'cleanup') {
1118
+ const dryRun = /\b--dry-run\b/.test(trimmed);
1119
+ const result = (0, storage_maintenance_1.cleanupStorage)({ dryRun });
1120
+ console.log();
1121
+ console.log((0, storage_maintenance_1.formatStorageCleanupResult)(result));
1122
+ console.log();
1123
+ return { success: true };
1124
+ }
1125
+ if (action === 'repair') {
1126
+ const result = (0, storage_maintenance_1.repairProjectMetadata)();
1127
+ console.log();
1128
+ console.log(HEADER('OpenHorse Storage Repair'));
1129
+ console.log(DIM('─'.repeat(40)));
1130
+ console.log(` Repaired ${ACCENT(String(result.repaired.length))}`);
1131
+ console.log(` Skipped ${DIM(String(result.skipped.length))}`);
1132
+ for (const project of result.repaired.slice(0, 12)) {
1133
+ console.log(` ${SUCCESS('✓')} ${DIM(project)}`);
1134
+ }
1135
+ if (result.repaired.length > 12) {
1136
+ console.log(DIM(` ... ${result.repaired.length - 12} more`));
1137
+ }
1138
+ console.log();
1139
+ return { success: true };
1140
+ }
1141
+ if (action !== 'doctor' && action !== 'status') {
1142
+ return {
1143
+ success: false,
1144
+ error: 'Usage: /storage [doctor|status|repair|cleanup --dry-run]',
1145
+ };
1146
+ }
1147
+ const report = (0, storage_maintenance_1.collectStorageReport)();
1148
+ console.log();
1149
+ console.log((0, storage_maintenance_1.formatStorageReport)(report));
1150
+ console.log();
1151
+ return { success: true };
1152
+ }
1102
1153
  function handleDiff(ctx, args) {
1103
1154
  const maxFilesMatch = args.match(/--max-files(?:=|\s+)(\d+)/);
1104
1155
  const maxFiles = maxFilesMatch ? Number(maxFilesMatch[1]) : 40;
@@ -1325,6 +1376,16 @@ function printSessionRows(sessions, options = {}) {
1325
1376
  console.log(` ${truncateText(sessionTitle(session), 96)}`);
1326
1377
  console.log(` ${DIM(`Started: ${startTime}`)} ${DIM(`Updated: ${updatedTime}`)} ${DIM(`Duration: ${duration}`)}`);
1327
1378
  console.log(` ${DIM(`Messages: ${session.messageCount ?? 0}`)} ${DIM(`Size: ${(0, sessions_1.formatBytes)(session.historySizeBytes ?? 0)}`)} ${DIM(`Tokens: ${session.tokenCount}`)} ${DIM(`Cost: $${session.cost.toFixed(4)}`)}`);
1379
+ if (options.showIndexSummary) {
1380
+ const indexSummary = (0, session_index_1.loadSessionIndex)(session.id, session.projectPath);
1381
+ if (indexSummary) {
1382
+ const toolCount = Object.values(indexSummary.tools).reduce((total, count) => total + count, 0);
1383
+ console.log(` ${DIM(`Index: ${indexSummary.files.length} files, ${toolCount} tool calls, ${indexSummary.topics.length} topics`)}`);
1384
+ }
1385
+ else {
1386
+ console.log(` ${DIM('Index: not built')}`);
1387
+ }
1388
+ }
1328
1389
  if (options.showProject) {
1329
1390
  console.log(` ${DIM(`Project: ${session.projectPath}`)}`);
1330
1391
  }
@@ -1369,6 +1430,40 @@ function printSessionPicker(sessions, options) {
1369
1430
  }
1370
1431
  function handleSessions(ctx, args = '') {
1371
1432
  const scope = parseSessionScopeArgs(args, ctx.cwd);
1433
+ const query = scope.query?.trim();
1434
+ // If there's a search query, use the session index
1435
+ if (query && !query.startsWith('--')) {
1436
+ const allSessions = scope.allProjects
1437
+ ? (0, session_storage_1.listSessions)()
1438
+ : (0, session_storage_1.listProjectSessions)(scope.projectPath);
1439
+ const matchedIds = (0, session_index_1.searchSessions)(query, allSessions.map(session => ({
1440
+ id: session.id,
1441
+ projectPath: session.projectPath,
1442
+ })));
1443
+ if (matchedIds.length === 0) {
1444
+ console.log();
1445
+ console.log(HEADER(`Sessions (search: "${query}")`));
1446
+ console.log(DIM('─'.repeat(40)));
1447
+ console.log(DIM(' No matching sessions found'));
1448
+ console.log();
1449
+ console.log(DIM('Tip: search by file path, tool name, or topic keyword'));
1450
+ console.log();
1451
+ return { success: true };
1452
+ }
1453
+ // Rebuild session list in matched order
1454
+ const sessionMap = new Map(allSessions.map(s => [s.id, s]));
1455
+ const matchedSessions = matchedIds.map(id => sessionMap.get(id)).filter(Boolean);
1456
+ console.log();
1457
+ console.log(HEADER(`Sessions (search: "${query}") — ${matchedSessions.length} matches`));
1458
+ console.log(DIM('─'.repeat(40)));
1459
+ console.log();
1460
+ printSessionRows(matchedSessions, { indexed: true, showProject: scope.allProjects, showIndexSummary: true });
1461
+ console.log();
1462
+ console.log(DIM(`Searched ${allSessions.length} sessions, found ${matchedSessions.length} matches`));
1463
+ console.log(DIM('Use /resume <number|session-id|name> to restore a session'));
1464
+ console.log();
1465
+ return { success: true };
1466
+ }
1372
1467
  console.log();
1373
1468
  console.log(HEADER(scope.allProjects ? 'Sessions (all projects)' : 'Sessions'));
1374
1469
  console.log(DIM('─'.repeat(40)));
@@ -1385,6 +1480,7 @@ function handleSessions(ctx, args = '') {
1385
1480
  console.log(DIM('Use /resume <number|session-id|name> to restore a session'));
1386
1481
  console.log(DIM('Use /session-rename <number|session-id|name> <new name> to rename'));
1387
1482
  console.log(DIM('Use /sessions --all to list sessions from every project'));
1483
+ console.log(DIM('Use /sessions <query> to search by file, tool, or keyword'));
1388
1484
  console.log();
1389
1485
  return { success: true };
1390
1486
  }
@@ -1516,6 +1612,78 @@ function handleSessionRename(ctx, args) {
1516
1612
  console.log();
1517
1613
  return { success: true };
1518
1614
  }
1615
+ async function handleEditPreview(ctx) {
1616
+ const lastEdit = (0, framework_1.getToolState)().lastEditFileArgs;
1617
+ if (!lastEdit) {
1618
+ console.log(ERROR('No previous edit_file call found for preview'));
1619
+ console.log(DIM('Run an edit_file tool call first, then use /edit-preview to inspect the match candidates.'));
1620
+ console.log();
1621
+ return { success: false };
1622
+ }
1623
+ const hasMetadata = Boolean(lastEdit.sessionId || lastEdit.turnId);
1624
+ if (!hasMetadata) {
1625
+ console.log(WARN('Using legacy edit-preview state without session/turn tags. Running preview as best-effort.'));
1626
+ }
1627
+ const staleBySession = Boolean(lastEdit.sessionId && ctx.sessionId && lastEdit.sessionId !== ctx.sessionId);
1628
+ const staleByTurn = Boolean(lastEdit.turnId != null && ctx.turnId != null && String(lastEdit.turnId) !== String(ctx.turnId));
1629
+ if (staleBySession || staleByTurn) {
1630
+ const mismatch = [];
1631
+ if (staleBySession)
1632
+ mismatch.push(`session ${lastEdit.sessionId} vs ${ctx.sessionId}`);
1633
+ if (staleByTurn)
1634
+ mismatch.push(`turn ${String(lastEdit.turnId)} vs ${String(ctx.turnId)}`);
1635
+ console.log(ERROR('Edit preview target does not match current context.'));
1636
+ console.log(DIM(`Stale edit target: ${mismatch.join(', ')}.`));
1637
+ console.log();
1638
+ return { success: false };
1639
+ }
1640
+ if (hasMetadata && !(ctx.sessionId || ctx.turnId)) {
1641
+ console.log(WARN('Edit preview context is available, but current command context is missing session/turn metadata.'));
1642
+ console.log(DIM('Preview is allowed, but stale checks cannot be fully validated.'));
1643
+ }
1644
+ const rawResult = await (0, tools_1.executeTool)('edit_file', {
1645
+ ...lastEdit,
1646
+ preview: true,
1647
+ }, ctx.abortSignal, {
1648
+ cwd: ctx.cwd,
1649
+ config: {
1650
+ name: ctx.config.name,
1651
+ mode: ctx.config.mode,
1652
+ },
1653
+ sessionId: ctx.sessionId,
1654
+ turnId: ctx.turnId,
1655
+ });
1656
+ let parsed;
1657
+ try {
1658
+ parsed = JSON.parse(rawResult);
1659
+ }
1660
+ catch {
1661
+ parsed = { success: false, error: rawResult };
1662
+ }
1663
+ console.log();
1664
+ console.log(HEADER('Edit Preview'));
1665
+ console.log(DIM('─'.repeat(40)));
1666
+ if (parsed.success) {
1667
+ console.log(parsed.output || DIM('No preview output'));
1668
+ }
1669
+ else {
1670
+ console.log(ERROR(parsed.error || 'Preview failed'));
1671
+ }
1672
+ console.log();
1673
+ // Return structured data for v2/TUI/Ink picker rendering
1674
+ if (parsed.success && parsed.metadata?.candidates && Array.isArray(parsed.metadata.candidates)) {
1675
+ return {
1676
+ success: true,
1677
+ editPreview: {
1678
+ path: lastEdit.path,
1679
+ newString: lastEdit.new_string,
1680
+ kind: (lastEdit.fuzzy_match ? 'fuzzy' : 'exact'),
1681
+ candidates: parsed.metadata.candidates,
1682
+ },
1683
+ };
1684
+ }
1685
+ return { success: parsed.success === true };
1686
+ }
1519
1687
  /** Generate a brief summary of conversation history */
1520
1688
  function generateHistorySummary(messages) {
1521
1689
  const userMsgs = messages.filter(m => m.role === 'user' && m.content);
@@ -1622,7 +1790,8 @@ const COMMANDS = [
1622
1790
  },
1623
1791
  {
1624
1792
  name: 'sessions',
1625
- description: 'List recent sessions',
1793
+ description: 'List recent sessions, or search by file/tool/keyword',
1794
+ argumentHint: '[<query>|--all]',
1626
1795
  category: 'session',
1627
1796
  priority: 20,
1628
1797
  type: 'builtin',
@@ -1693,6 +1862,14 @@ const COMMANDS = [
1693
1862
  type: 'builtin',
1694
1863
  execute: (ctx) => handleTools(ctx),
1695
1864
  },
1865
+ {
1866
+ name: 'edit-preview',
1867
+ description: 'Preview the last edit_file match candidates without writing',
1868
+ category: 'tools',
1869
+ priority: 15,
1870
+ type: 'builtin',
1871
+ execute: (ctx) => handleEditPreview(ctx),
1872
+ },
1696
1873
  {
1697
1874
  name: 'mcp',
1698
1875
  description: 'Show connected MCP servers and their status',
@@ -1786,6 +1963,15 @@ const COMMANDS = [
1786
1963
  type: 'builtin',
1787
1964
  execute: (ctx) => handleDoctor(ctx),
1788
1965
  },
1966
+ {
1967
+ name: 'storage',
1968
+ description: 'Inspect, repair, or clean OpenHorse storage layout',
1969
+ argumentHint: '[doctor|repair|cleanup --dry-run]',
1970
+ category: 'diagnostics',
1971
+ priority: 8,
1972
+ type: 'builtin',
1973
+ execute: (ctx, args) => handleStorage(ctx, args),
1974
+ },
1789
1975
  {
1790
1976
  name: 'usage',
1791
1977
  aliases: ['stats'],