claude-launchpad 1.15.0 → 1.17.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 (44) hide show
  1. package/dist/{chunk-IPGNXDJF.js → chunk-IIOY56ZE.js} +2 -2
  2. package/dist/{chunk-DLDVXHI3.js → chunk-JKDFOKHD.js} +2 -2
  3. package/dist/{chunk-U2TFIUGE.js → chunk-KYGWNN5F.js} +38 -7
  4. package/dist/chunk-KYGWNN5F.js.map +1 -0
  5. package/dist/{chunk-IDH3M4EI.js → chunk-MRW63LDL.js} +6 -1
  6. package/dist/chunk-MRW63LDL.js.map +1 -0
  7. package/dist/{chunk-LCKV24Q3.js → chunk-ROQI7RKM.js} +4 -4
  8. package/dist/{chunk-MX3T2VEX.js → chunk-ULECXQFX.js} +3 -3
  9. package/dist/{chunk-7KB5YGQZ.js → chunk-YLIV5WBW.js} +2 -2
  10. package/dist/cli.js +69 -29
  11. package/dist/cli.js.map +1 -1
  12. package/dist/commands/memory/server.js +5 -5
  13. package/dist/{context-3GHG7KW6.js → context-K5B43NEI.js} +7 -7
  14. package/dist/{doctor-EKGJ2YAO.js → doctor-ESIQYNE4.js} +5 -5
  15. package/dist/{install-543ZKMXX.js → install-VREFJNLF.js} +9 -7
  16. package/dist/install-VREFJNLF.js.map +1 -0
  17. package/dist/{pull-ULSZEVCR.js → pull-H6ARY6DG.js} +9 -9
  18. package/dist/{push-JXCWX4NY.js → push-JDHT6AMD.js} +9 -9
  19. package/dist/{require-deps-AEDO5YRM.js → require-deps-GVBWF5PB.js} +3 -3
  20. package/dist/{stats-63L7C24W.js → stats-TWIVFA4I.js} +8 -8
  21. package/dist/{sync-QTJ6BXQ4.js → sync-WG7OOWOX.js} +5 -5
  22. package/dist/{sync-clean-OJEK4XH4.js → sync-clean-GQUFTYG2.js} +3 -3
  23. package/dist/{sync-status-EAX527SC.js → sync-status-B3DKLET4.js} +9 -9
  24. package/dist/{tui-3KITZCNS.js → tui-ZINEA7TI.js} +240 -74
  25. package/dist/tui-ZINEA7TI.js.map +1 -0
  26. package/package.json +2 -1
  27. package/dist/chunk-IDH3M4EI.js.map +0 -1
  28. package/dist/chunk-U2TFIUGE.js.map +0 -1
  29. package/dist/install-543ZKMXX.js.map +0 -1
  30. package/dist/tui-3KITZCNS.js.map +0 -1
  31. /package/dist/{chunk-IPGNXDJF.js.map → chunk-IIOY56ZE.js.map} +0 -0
  32. /package/dist/{chunk-DLDVXHI3.js.map → chunk-JKDFOKHD.js.map} +0 -0
  33. /package/dist/{chunk-LCKV24Q3.js.map → chunk-ROQI7RKM.js.map} +0 -0
  34. /package/dist/{chunk-MX3T2VEX.js.map → chunk-ULECXQFX.js.map} +0 -0
  35. /package/dist/{chunk-7KB5YGQZ.js.map → chunk-YLIV5WBW.js.map} +0 -0
  36. /package/dist/{context-3GHG7KW6.js.map → context-K5B43NEI.js.map} +0 -0
  37. /package/dist/{doctor-EKGJ2YAO.js.map → doctor-ESIQYNE4.js.map} +0 -0
  38. /package/dist/{pull-ULSZEVCR.js.map → pull-H6ARY6DG.js.map} +0 -0
  39. /package/dist/{push-JXCWX4NY.js.map → push-JDHT6AMD.js.map} +0 -0
  40. /package/dist/{require-deps-AEDO5YRM.js.map → require-deps-GVBWF5PB.js.map} +0 -0
  41. /package/dist/{stats-63L7C24W.js.map → stats-TWIVFA4I.js.map} +0 -0
  42. /package/dist/{sync-QTJ6BXQ4.js.map → sync-WG7OOWOX.js.map} +0 -0
  43. /package/dist/{sync-clean-OJEK4XH4.js.map → sync-clean-GQUFTYG2.js.map} +0 -0
  44. /package/dist/{sync-status-EAX527SC.js.map → sync-status-B3DKLET4.js.map} +0 -0
@@ -5,19 +5,19 @@ import {
5
5
  } from "./chunk-HSGSTSVN.js";
6
6
  import {
7
7
  migrate
8
- } from "./chunk-MX3T2VEX.js";
8
+ } from "./chunk-ULECXQFX.js";
9
9
  import {
10
10
  MemoryRepo
11
- } from "./chunk-IDH3M4EI.js";
11
+ } from "./chunk-MRW63LDL.js";
12
12
  import {
13
13
  DEFAULT_DECAY_PARAMS,
14
14
  closeDatabase,
15
15
  createDatabase,
16
16
  loadConfig,
17
17
  resolveDataDir
18
- } from "./chunk-DLDVXHI3.js";
19
- import "./chunk-7KB5YGQZ.js";
20
- import "./chunk-U2TFIUGE.js";
18
+ } from "./chunk-JKDFOKHD.js";
19
+ import "./chunk-YLIV5WBW.js";
20
+ import "./chunk-KYGWNN5F.js";
21
21
 
22
22
  // src/commands/memory/dashboard/tui.tsx
23
23
  import { render } from "ink";
@@ -31,6 +31,7 @@ var DashboardDataSource = class {
31
31
  #searchRepo;
32
32
  #dbPath;
33
33
  #cachedMemories = [];
34
+ #relationsByMemory = /* @__PURE__ */ new Map();
34
35
  constructor(memoryRepo, relationRepo, searchRepo, dataDir) {
35
36
  this.#memoryRepo = memoryRepo;
36
37
  this.#relationRepo = relationRepo;
@@ -40,6 +41,15 @@ var DashboardDataSource = class {
40
41
  /** Re-query all memories from DB and cache them. Excludes soft-deleted (importance=0). */
41
42
  refresh() {
42
43
  this.#cachedMemories = this.#memoryRepo.getAll().filter((m) => m.importance > 0);
44
+ const byMemory = /* @__PURE__ */ new Map();
45
+ for (const r of this.#relationRepo.getAll()) {
46
+ for (const id of /* @__PURE__ */ new Set([r.sourceId, r.targetId])) {
47
+ const list = byMemory.get(id) ?? [];
48
+ list.push(r);
49
+ byMemory.set(id, list);
50
+ }
51
+ }
52
+ this.#relationsByMemory = byMemory;
43
53
  }
44
54
  /** Return cached memories, optionally filtered by type, project, or FTS query. */
45
55
  getMemories(filter) {
@@ -63,9 +73,13 @@ var DashboardDataSource = class {
63
73
  }
64
74
  return results;
65
75
  }
66
- /** Get all relations for a specific memory. */
76
+ /** Resolve a memory id to its title for relation display (cache-only, no I/O). */
77
+ getMemoryTitle(id) {
78
+ const m = this.#cachedMemories.find((mem) => mem.id === id);
79
+ return m?.title ?? void 0;
80
+ }
67
81
  getRelationsForMemory(id) {
68
- return this.#relationRepo.getByMemory(id);
82
+ return this.#relationsByMemory.get(id) ?? [];
69
83
  }
70
84
  /** Compute aggregate stats from cached data + DB queries. */
71
85
  getStats() {
@@ -98,9 +112,23 @@ var DashboardDataSource = class {
98
112
  onChange();
99
113
  });
100
114
  }
101
- /** Hard-delete a memory by ID. Returns true if deleted. */
115
+ /** Soft-delete a memory; returns a snapshot for undo (null if not found). */
102
116
  deleteMemory(id) {
103
- return this.#memoryRepo.hardDelete(id);
117
+ const snapshot = this.#memoryRepo.getById(id);
118
+ if (!snapshot) return null;
119
+ this.#memoryRepo.softDelete(id);
120
+ return snapshot;
121
+ }
122
+ /** Undo a soft delete using the snapshot taken at delete time. */
123
+ restoreMemory(snapshot) {
124
+ return this.#memoryRepo.restoreImportance(snapshot.id, snapshot.importance, snapshot.baseImportance);
125
+ }
126
+ /** Curation edits from the TUI: explicit re-rate re-anchors the decay base. */
127
+ updateMemory(id, updates) {
128
+ return this.#memoryRepo.updateContent(id, {
129
+ ...updates.importance !== void 0 ? { importance: updates.importance } : {},
130
+ ...updates.tags !== void 0 ? { tags: updates.tags } : {}
131
+ });
104
132
  }
105
133
  /** Count memories for a project (unfiltered). */
106
134
  countByProject(project) {
@@ -133,7 +161,7 @@ var DashboardDataSource = class {
133
161
  };
134
162
 
135
163
  // src/commands/memory/dashboard/app.tsx
136
- import { Box as Box13, useApp } from "ink";
164
+ import { Box as Box14, Text as Text14, useApp } from "ink";
137
165
 
138
166
  // src/commands/memory/dashboard/hooks/use-dashboard-state.ts
139
167
  import { useState, useMemo, useEffect, useCallback } from "react";
@@ -230,12 +258,15 @@ function useDashboardState(dataSource) {
230
258
  const [currentProject, setCurrentProject] = useState();
231
259
  const [sortMode, setSortMode] = useState("importance");
232
260
  const [selectedIndex, setSelectedIndex] = useState(0);
233
- const [focusedPane, setFocusedPane] = useState("list");
234
261
  const [showHelp, setShowHelp] = useState(false);
235
262
  const [showProjectPicker, setShowProjectPicker] = useState(false);
236
263
  const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
237
264
  const [showPurgeConfirm, setShowPurgeConfirm] = useState(false);
238
265
  const [showExpand, setShowExpand] = useState(false);
266
+ const [showTagEditor, setShowTagEditor] = useState(false);
267
+ const [tagDraft, setTagDraft] = useState("");
268
+ const [lastDeleted, setLastDeleted] = useState(null);
269
+ const [notice, setNotice] = useState(null);
239
270
  useEffect(() => {
240
271
  dataSource.refresh();
241
272
  setRevision((r) => r + 1);
@@ -251,14 +282,12 @@ function useDashboardState(dataSource) {
251
282
  }, [dataSource, revision]);
252
283
  const filteredMemories = useMemo(() => {
253
284
  void revision;
254
- const raw = dataSource.getMemories({ type: typeFilter, project: currentProject });
285
+ const raw = dataSource.getMemories({ type: typeFilter, project: currentProject, query: searchQuery || void 0 });
255
286
  const withLife = lifespanFilter ? raw.filter((m) => computeLifespan(m).status === lifespanFilter) : raw;
256
- const withSearch = searchQuery ? withLife.filter(
257
- (m) => (m.title?.toLowerCase().includes(searchQuery.toLowerCase()) ?? false) || m.content.toLowerCase().includes(searchQuery.toLowerCase()) || m.tags.some((t) => t.toLowerCase().includes(searchQuery.toLowerCase()))
258
- ) : withLife;
259
- return sortMemories(withSearch, sortMode);
287
+ return sortMemories(withLife, sortMode);
260
288
  }, [dataSource, revision, typeFilter, lifespanFilter, searchQuery, currentProject, sortMode]);
261
- const selectedMemory = filteredMemories[selectedIndex];
289
+ const clampedIndex = Math.max(0, Math.min(selectedIndex, Math.max(0, filteredMemories.length - 1)));
290
+ const selectedMemory = filteredMemories[clampedIndex];
262
291
  const relations = useMemo(
263
292
  () => selectedMemory ? dataSource.getRelationsForMemory(selectedMemory.id) : [],
264
293
  [dataSource, selectedMemory, revision]
@@ -268,10 +297,12 @@ function useDashboardState(dataSource) {
268
297
  return dataSource.getStats();
269
298
  }, [dataSource, revision]);
270
299
  const navigateUp = useCallback(() => {
271
- setSelectedIndex((i) => Math.max(0, i - 1));
272
- }, []);
300
+ const last = filteredMemories.length - 1;
301
+ setSelectedIndex((i) => Math.max(0, Math.min(i, last) - 1));
302
+ }, [filteredMemories.length]);
273
303
  const navigateDown = useCallback(() => {
274
- setSelectedIndex((i) => Math.min(filteredMemories.length - 1, i + 1));
304
+ const last = filteredMemories.length - 1;
305
+ setSelectedIndex((i) => Math.max(0, Math.min(last, Math.min(i, last) + 1)));
275
306
  }, [filteredMemories.length]);
276
307
  const cycleSort = useCallback(() => {
277
308
  setSortMode((m) => SORT_MODES[(SORT_MODES.indexOf(m) + 1) % SORT_MODES.length]);
@@ -296,14 +327,15 @@ function useDashboardState(dataSource) {
296
327
  });
297
328
  setSelectedIndex(0);
298
329
  }, [projects]);
299
- const focusNext = useCallback(() => {
300
- setFocusedPane((p) => p === "list" ? "projects" : p === "projects" ? "detail" : "list");
301
- }, []);
302
330
  const filterByType = useCallback((type) => {
303
331
  setTypeFilter(type);
304
332
  setSelectedIndex(0);
305
333
  }, []);
306
334
  const openSearch = useCallback(() => setSearchActive(true), []);
335
+ const submitSearch = useCallback(() => {
336
+ setSearchActive(false);
337
+ setSelectedIndex(0);
338
+ }, []);
307
339
  const closeSearch = useCallback(() => {
308
340
  setSearchActive(false);
309
341
  setSearchQuery("");
@@ -314,12 +346,48 @@ function useDashboardState(dataSource) {
314
346
  }, [selectedMemory]);
315
347
  const confirmDelete = useCallback(() => {
316
348
  if (!selectedMemory) return;
317
- dataSource.deleteMemory(selectedMemory.id);
349
+ const snapshot = dataSource.deleteMemory(selectedMemory.id);
350
+ if (snapshot) {
351
+ setLastDeleted(snapshot);
352
+ setNotice(`Deleted "${snapshot.title ?? snapshot.id.slice(0, 8)}" \u2014 press u to undo`);
353
+ }
318
354
  setShowDeleteConfirm(false);
319
355
  setSelectedIndex((i) => Math.max(0, i - 1));
320
356
  dataSource.refresh();
321
357
  setRevision((r) => r + 1);
322
358
  }, [dataSource, selectedMemory]);
359
+ const undoDelete = useCallback(() => {
360
+ if (!lastDeleted) return;
361
+ const ok = dataSource.restoreMemory(lastDeleted);
362
+ setNotice(ok ? `Restored "${lastDeleted.title ?? lastDeleted.id.slice(0, 8)}"` : `Could not restore "${lastDeleted.title ?? lastDeleted.id.slice(0, 8)}" \u2014 it was purged or removed by sync`);
363
+ setLastDeleted(null);
364
+ dataSource.refresh();
365
+ setRevision((r) => r + 1);
366
+ }, [dataSource, lastDeleted]);
367
+ const adjustImportance = useCallback((delta) => {
368
+ if (!selectedMemory) return;
369
+ const next = Math.min(1, Math.max(0.05, Math.round((selectedMemory.importance + delta) * 100) / 100));
370
+ dataSource.updateMemory(selectedMemory.id, { importance: next });
371
+ setNotice(`Importance ${next.toFixed(2)} (re-anchored)`);
372
+ dataSource.refresh();
373
+ setRevision((r) => r + 1);
374
+ }, [dataSource, selectedMemory]);
375
+ const openTagEditor = useCallback(() => {
376
+ if (!selectedMemory) return;
377
+ setTagDraft(selectedMemory.tags.join(", "));
378
+ setShowTagEditor(true);
379
+ }, [selectedMemory]);
380
+ const submitTags = useCallback(() => {
381
+ if (selectedMemory) {
382
+ const tags = tagDraft.split(",").map((t) => t.trim()).filter(Boolean);
383
+ dataSource.updateMemory(selectedMemory.id, { tags });
384
+ setNotice(`Tags updated (${tags.length})`);
385
+ dataSource.refresh();
386
+ setRevision((r) => r + 1);
387
+ }
388
+ setShowTagEditor(false);
389
+ }, [dataSource, selectedMemory, tagDraft]);
390
+ const cancelTagEditor = useCallback(() => setShowTagEditor(false), []);
323
391
  const cancelDelete = useCallback(() => setShowDeleteConfirm(false), []);
324
392
  const promptPurge = useCallback(() => {
325
393
  if (currentProject) setShowPurgeConfirm(true);
@@ -344,9 +412,12 @@ function useDashboardState(dataSource) {
344
412
  searchQuery,
345
413
  searchActive,
346
414
  currentProject,
415
+ showTagEditor,
416
+ tagDraft,
417
+ setTagDraft,
418
+ notice,
419
+ lastDeleted,
347
420
  sortMode,
348
- selectedIndex,
349
- focusedPane,
350
421
  showHelp,
351
422
  showProjectPicker,
352
423
  showDeleteConfirm,
@@ -357,6 +428,7 @@ function useDashboardState(dataSource) {
357
428
  relations,
358
429
  projects,
359
430
  stats,
431
+ selectedIndex: clampedIndex,
360
432
  setSearchQuery,
361
433
  setCurrentProject,
362
434
  setSelectedIndex,
@@ -368,9 +440,9 @@ function useDashboardState(dataSource) {
368
440
  cycleLifespan,
369
441
  cycleProjectNext,
370
442
  cycleProjectPrev,
371
- focusNext,
372
443
  filterByType,
373
444
  openSearch,
445
+ submitSearch,
374
446
  closeSearch,
375
447
  promptDelete,
376
448
  confirmDelete,
@@ -379,7 +451,12 @@ function useDashboardState(dataSource) {
379
451
  confirmPurge,
380
452
  cancelPurge,
381
453
  expandMemory,
382
- closeExpand
454
+ closeExpand,
455
+ undoDelete,
456
+ adjustImportance,
457
+ openTagEditor,
458
+ submitTags,
459
+ cancelTagEditor
383
460
  };
384
461
  }
385
462
  function sortMemories(memories, mode) {
@@ -405,6 +482,11 @@ var TYPE_KEYS = {
405
482
  };
406
483
  function useKeybindings(actions, opts) {
407
484
  useInput((input, key) => {
485
+ if (opts.modalOpen) return;
486
+ if (opts.tagEditorOpen) {
487
+ if (key.escape) actions.cancelTagEditor();
488
+ return;
489
+ }
408
490
  if (opts.expandOpen) return;
409
491
  if (opts.searchActive) {
410
492
  if (key.escape) actions.closeSearch();
@@ -425,9 +507,12 @@ function useKeybindings(actions, opts) {
425
507
  if (input === "p") actions.openProjectPicker();
426
508
  if (input === "]" || key.rightArrow) actions.cycleProjectNext();
427
509
  if (input === "[" || key.leftArrow) actions.cycleProjectPrev();
428
- if (key.tab) actions.focusNext();
429
- if (input === "r") actions.removeMemory();
430
- if (input === "d") actions.purgeProject();
510
+ if (input === "r" || input === "d") actions.removeMemory();
511
+ if (input === "X") actions.purgeProject();
512
+ if (input === "u") actions.undoDelete();
513
+ if (input === "+" || input === "=") actions.adjustImportanceUp();
514
+ if (input === "-") actions.adjustImportanceDown();
515
+ if (input === "t") actions.openTagEditor();
431
516
  if (input === "?") actions.showHelp();
432
517
  if (key.return) actions.expandMemory();
433
518
  if (input === "q") actions.quit();
@@ -466,14 +551,16 @@ var HINTS = [
466
551
  ["1-5", "type"],
467
552
  ["l", "life"],
468
553
  ["s", "sort"],
469
- ["r", "remove"],
470
- ["d", "delete project"],
471
- ["Tab", "focus"],
554
+ ["d", "delete"],
555
+ ["u", "undo"],
556
+ ["t", "tags"],
557
+ ["+/-", "rate"],
558
+ ["X", "purge"],
472
559
  ["?", "help"],
473
560
  ["q", "quit"]
474
561
  ];
475
562
  function KeybindingBar() {
476
- return /* @__PURE__ */ jsx(Box, { children: HINTS.map(([key, label], i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
563
+ return /* @__PURE__ */ jsx(Box, { overflow: "hidden", children: HINTS.map(([key, label], i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
477
564
  i > 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
478
565
  /* @__PURE__ */ jsx(Text, { color: "cyan", children: key }),
479
566
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
@@ -519,11 +606,11 @@ function Header({ project, typeFilter, lifespanFilter, sortMode, searchQuery, la
519
606
  import { Box as Box3, Text as Text3 } from "ink";
520
607
  import TextInput from "ink-text-input";
521
608
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
522
- function SearchBar({ query, onChange, onClose }) {
609
+ function SearchBar({ query, onChange, onSubmit }) {
523
610
  return /* @__PURE__ */ jsxs3(Box3, { children: [
524
611
  /* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "Search: " }),
525
- /* @__PURE__ */ jsx3(TextInput, { value: query, onChange, onSubmit: onClose }),
526
- /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " (Esc to clear, Enter to keep)" })
612
+ /* @__PURE__ */ jsx3(TextInput, { value: query, onChange, onSubmit }),
613
+ /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " (Enter apply \xB7 Esc cancel)" })
527
614
  ] });
528
615
  }
529
616
 
@@ -646,7 +733,7 @@ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
646
733
  var METADATA_ROWS = 18;
647
734
  var BORDER_ROWS = 2;
648
735
  var MIN_CONTENT_ROWS = 2;
649
- function MemoryDetail({ memory, relations, isFocused, height, width }) {
736
+ function MemoryDetail({ memory, relations, isFocused, height, width, resolveTitle }) {
650
737
  return /* @__PURE__ */ jsxs5(
651
738
  Box5,
652
739
  {
@@ -657,12 +744,12 @@ function MemoryDetail({ memory, relations, isFocused, height, width }) {
657
744
  height,
658
745
  children: [
659
746
  /* @__PURE__ */ jsx5(Text5, { bold: true, color: isFocused ? "blue" : "gray", children: " Detail " }),
660
- !memory ? /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Select a memory to view details" }) : /* @__PURE__ */ jsx5(DetailContent, { memory, relations, height, width })
747
+ !memory ? /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Select a memory to view details" }) : /* @__PURE__ */ jsx5(DetailContent, { resolveTitle, memory, relations, height, width })
661
748
  ]
662
749
  }
663
750
  );
664
751
  }
665
- function DetailContent({ memory, relations, height, width }) {
752
+ function DetailContent({ memory, relations, height, width, resolveTitle }) {
666
753
  const life = computeLifespan(memory);
667
754
  const typeColor = TYPE_COLORS[memory.type] ?? "white";
668
755
  const relationRows = relations.length > 0 ? relations.length + 2 : 0;
@@ -734,6 +821,10 @@ function DetailContent({ memory, relations, height, width }) {
734
821
  /* @__PURE__ */ jsx5(Text5, { bold: true, children: "Content" }),
735
822
  /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\u2500".repeat(Math.min(contentWidth, 40)) }),
736
823
  preview.lines.map((line, i) => /* @__PURE__ */ jsx5(Text5, { children: line === "" ? " " : line }, `line-${i}`)),
824
+ memory.context && /* @__PURE__ */ jsxs5(Text5, { dimColor: true, wrap: "truncate", children: [
825
+ "ctx: ",
826
+ memory.context.slice(0, 120)
827
+ ] }),
737
828
  preview.truncated && /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
738
829
  "\u2026 +",
739
830
  preview.remainingLines,
@@ -741,7 +832,7 @@ function DetailContent({ memory, relations, height, width }) {
741
832
  preview.remainingLines === 1 ? "" : "s",
742
833
  " \xB7 press Enter to view full"
743
834
  ] }),
744
- relations.length > 0 && /* @__PURE__ */ jsx5(RelationsList, { relations, memoryId: memory.id })
835
+ relations.length > 0 && /* @__PURE__ */ jsx5(RelationsList, { resolveTitle, relations, memoryId: memory.id })
745
836
  ] });
746
837
  }
747
838
  function truncateContent(content, maxRows, wrapWidth) {
@@ -756,7 +847,7 @@ function truncateContent(content, maxRows, wrapWidth) {
756
847
  remainingLines: allLines.length - visible
757
848
  };
758
849
  }
759
- function RelationsList({ relations, memoryId }) {
850
+ function RelationsList({ relations, memoryId, resolveTitle }) {
760
851
  return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
761
852
  /* @__PURE__ */ jsx5(Text5, { bold: true, children: "Relations" }),
762
853
  /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\u2500".repeat(40) }),
@@ -764,13 +855,14 @@ function RelationsList({ relations, memoryId }) {
764
855
  const relColor = RELATION_COLORS[r.relationType] ?? "white";
765
856
  const direction = r.sourceId === memoryId ? "\u2192" : "\u2190";
766
857
  const otherId = r.sourceId === memoryId ? r.targetId : r.sourceId;
858
+ const label = resolveTitle?.(otherId) ?? otherId.slice(0, 8);
767
859
  return /* @__PURE__ */ jsxs5(Text5, { children: [
768
860
  " ",
769
861
  direction,
770
862
  " ",
771
863
  /* @__PURE__ */ jsx5(Text5, { color: relColor, children: r.relationType }),
772
864
  " ",
773
- otherId
865
+ label
774
866
  ] }, i);
775
867
  })
776
868
  ] });
@@ -916,9 +1008,11 @@ var BINDINGS = [
916
1008
  ["s", "Cycle sort mode"],
917
1009
  ["p", "Open project picker"],
918
1010
  ["[ / ]", "Previous / next project"],
919
- ["r", "Remove selected memory"],
920
- ["d", "Delete all memories for current project"],
921
- ["Tab", "Focus next pane"],
1011
+ ["d / r", "Delete selected memory (soft \u2014 u restores)"],
1012
+ ["u", "Undo last delete"],
1013
+ ["t", "Edit tags of selected memory"],
1014
+ ["+ / -", "Raise / lower importance (re-anchors decay)"],
1015
+ ["X", "Purge all memories for current project"],
922
1016
  ["?", "Show this help"],
923
1017
  ["q", "Quit"]
924
1018
  ];
@@ -1050,7 +1144,7 @@ function PurgeConfirm({ project, memoryCount, onConfirm, onCancel }) {
1050
1144
  import { useState as useState3, useEffect as useEffect3 } from "react";
1051
1145
  import { Box as Box12, Text as Text12, useInput as useInput6 } from "ink";
1052
1146
  import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1053
- function ExpandMemory({ memory, relations, onClose }) {
1147
+ function ExpandMemory({ memory, relations, onClose, resolveTitle }) {
1054
1148
  const { columns, rows } = useTerminalSize();
1055
1149
  const [scroll, setScroll] = useState3(0);
1056
1150
  const contentWidth = Math.max(20, columns - 6);
@@ -1108,13 +1202,14 @@ function ExpandMemory({ memory, relations, onClose }) {
1108
1202
  const relColor = RELATION_COLORS[r.relationType] ?? "white";
1109
1203
  const direction = r.sourceId === memory.id ? "\u2192" : "\u2190";
1110
1204
  const otherId = r.sourceId === memory.id ? r.targetId : r.sourceId;
1205
+ const label = resolveTitle?.(otherId) ?? otherId.slice(0, 8);
1111
1206
  return /* @__PURE__ */ jsxs12(Text12, { children: [
1112
1207
  " ",
1113
1208
  direction,
1114
1209
  " ",
1115
1210
  /* @__PURE__ */ jsx12(Text12, { color: relColor, children: r.relationType }),
1116
1211
  " ",
1117
- otherId
1212
+ label
1118
1213
  ] }, i);
1119
1214
  })
1120
1215
  ] }),
@@ -1126,8 +1221,20 @@ function ExpandMemory({ memory, relations, onClose }) {
1126
1221
  ] });
1127
1222
  }
1128
1223
 
1129
- // src/commands/memory/dashboard/app.tsx
1224
+ // src/commands/memory/dashboard/components/tag-editor.tsx
1225
+ import { Box as Box13, Text as Text13 } from "ink";
1226
+ import TextInput2 from "ink-text-input";
1130
1227
  import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1228
+ function TagEditor({ value, onChange, onSubmit }) {
1229
+ return /* @__PURE__ */ jsxs13(Box13, { children: [
1230
+ /* @__PURE__ */ jsx13(Text13, { color: "cyan", children: "Tags (comma-separated): " }),
1231
+ /* @__PURE__ */ jsx13(TextInput2, { value, onChange, onSubmit }),
1232
+ /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: " (Enter save \xB7 Esc cancel)" })
1233
+ ] });
1234
+ }
1235
+
1236
+ // src/commands/memory/dashboard/app.tsx
1237
+ import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
1131
1238
  function App({ dataSource }) {
1132
1239
  const { exit } = useApp();
1133
1240
  const { columns, rows, layout } = useTerminalSize();
@@ -1142,33 +1249,40 @@ function App({ dataSource }) {
1142
1249
  cycleProjectNext: state.cycleProjectNext,
1143
1250
  cycleProjectPrev: state.cycleProjectPrev,
1144
1251
  cycleSort: state.cycleSort,
1145
- focusNext: state.focusNext,
1146
1252
  removeMemory: state.promptDelete,
1147
1253
  purgeProject: state.promptPurge,
1148
1254
  openProjectPicker: () => state.setShowProjectPicker((v) => !v),
1149
1255
  showHelp: () => state.setShowHelp((v) => !v),
1150
1256
  expandMemory: state.expandMemory,
1257
+ undoDelete: state.undoDelete,
1258
+ adjustImportanceUp: () => state.adjustImportance(0.1),
1259
+ adjustImportanceDown: () => state.adjustImportance(-0.1),
1260
+ openTagEditor: state.openTagEditor,
1261
+ cancelTagEditor: state.cancelTagEditor,
1151
1262
  quit: exit
1152
1263
  }, {
1153
1264
  searchActive: state.searchActive,
1154
1265
  pickerOpen: state.showProjectPicker,
1155
- expandOpen: state.showExpand
1266
+ expandOpen: state.showExpand,
1267
+ modalOpen: state.showDeleteConfirm || state.showPurgeConfirm || state.showHelp,
1268
+ tagEditorOpen: state.showTagEditor
1156
1269
  });
1157
1270
  if (state.showHelp) {
1158
- return /* @__PURE__ */ jsx13(HelpOverlay, { onClose: () => state.setShowHelp(false) });
1271
+ return /* @__PURE__ */ jsx14(HelpOverlay, { onClose: () => state.setShowHelp(false) });
1159
1272
  }
1160
1273
  if (state.showExpand && state.selectedMemory) {
1161
- return /* @__PURE__ */ jsx13(
1274
+ return /* @__PURE__ */ jsx14(
1162
1275
  ExpandMemory,
1163
1276
  {
1164
1277
  memory: state.selectedMemory,
1165
1278
  relations: state.relations,
1279
+ resolveTitle: (id) => dataSource.getMemoryTitle(id),
1166
1280
  onClose: state.closeExpand
1167
1281
  }
1168
1282
  );
1169
1283
  }
1170
1284
  if (state.showProjectPicker) {
1171
- return /* @__PURE__ */ jsx13(
1285
+ return /* @__PURE__ */ jsx14(
1172
1286
  ProjectPicker,
1173
1287
  {
1174
1288
  projects: [...state.projects],
@@ -1182,7 +1296,7 @@ function App({ dataSource }) {
1182
1296
  );
1183
1297
  }
1184
1298
  if (state.showDeleteConfirm && state.selectedMemory) {
1185
- return /* @__PURE__ */ jsx13(
1299
+ return /* @__PURE__ */ jsx14(
1186
1300
  DeleteConfirm,
1187
1301
  {
1188
1302
  memory: state.selectedMemory,
@@ -1192,7 +1306,7 @@ function App({ dataSource }) {
1192
1306
  );
1193
1307
  }
1194
1308
  if (state.showPurgeConfirm && state.currentProject) {
1195
- return /* @__PURE__ */ jsx13(
1309
+ return /* @__PURE__ */ jsx14(
1196
1310
  PurgeConfirm,
1197
1311
  {
1198
1312
  project: state.currentProject,
@@ -1210,9 +1324,9 @@ function App({ dataSource }) {
1210
1324
  const projectListHeight = isNarrow ? 0 : Math.min(Math.max(4, Math.floor(contentHeight / 3)), projectsCount + 3, 12);
1211
1325
  const listHeight = isNarrow ? Math.max(6, Math.floor(contentHeight * 0.6)) : contentHeight;
1212
1326
  const detailHeight = isNarrow ? Math.max(6, contentHeight - listHeight) : Math.max(6, contentHeight - projectListHeight);
1213
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
1214
- /* @__PURE__ */ jsx13(KeybindingBar, {}),
1215
- /* @__PURE__ */ jsx13(
1327
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
1328
+ /* @__PURE__ */ jsx14(KeybindingBar, {}),
1329
+ /* @__PURE__ */ jsx14(
1216
1330
  Header,
1217
1331
  {
1218
1332
  project: state.currentProject,
@@ -1223,52 +1337,104 @@ function App({ dataSource }) {
1223
1337
  layout
1224
1338
  }
1225
1339
  ),
1226
- state.searchActive && /* @__PURE__ */ jsx13(
1340
+ state.showTagEditor && /* @__PURE__ */ jsx14(
1341
+ TagEditor,
1342
+ {
1343
+ value: state.tagDraft,
1344
+ onChange: state.setTagDraft,
1345
+ onSubmit: state.submitTags
1346
+ }
1347
+ ),
1348
+ state.notice && !state.searchActive && !state.showTagEditor && /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
1349
+ " ",
1350
+ state.notice
1351
+ ] }),
1352
+ state.searchActive && /* @__PURE__ */ jsx14(
1227
1353
  SearchBar,
1228
1354
  {
1229
1355
  query: state.searchQuery,
1230
1356
  onChange: state.setSearchQuery,
1231
- onClose: () => state.setSearchQuery(state.searchQuery)
1357
+ onSubmit: state.submitSearch
1232
1358
  }
1233
1359
  ),
1234
- /* @__PURE__ */ jsxs13(Box13, { flexDirection: isNarrow ? "column" : "row", height: isNarrow ? void 0 : contentHeight, children: [
1235
- /* @__PURE__ */ jsx13(Box13, { width: isNarrow ? "100%" : "60%", children: /* @__PURE__ */ jsx13(
1360
+ /* @__PURE__ */ jsxs14(Box14, { flexDirection: isNarrow ? "column" : "row", height: isNarrow ? void 0 : contentHeight, children: [
1361
+ /* @__PURE__ */ jsx14(Box14, { width: isNarrow ? "100%" : "60%", children: /* @__PURE__ */ jsx14(
1236
1362
  MemoryList,
1237
1363
  {
1238
1364
  memories: state.filteredMemories,
1239
1365
  selectedIndex: state.selectedIndex,
1240
- isFocused: state.focusedPane === "list",
1366
+ isFocused: true,
1241
1367
  height: listHeight
1242
1368
  }
1243
1369
  ) }),
1244
- /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", width: isNarrow ? "100%" : "40%", children: [
1245
- !isNarrow && /* @__PURE__ */ jsx13(
1370
+ /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", width: isNarrow ? "100%" : "40%", children: [
1371
+ !isNarrow && /* @__PURE__ */ jsx14(
1246
1372
  ProjectList,
1247
1373
  {
1248
1374
  memories: state.filteredMemories,
1249
1375
  activeProject: state.currentProject,
1250
- isFocused: state.focusedPane === "projects",
1376
+ isFocused: false,
1251
1377
  height: projectListHeight
1252
1378
  }
1253
1379
  ),
1254
- /* @__PURE__ */ jsx13(
1380
+ /* @__PURE__ */ jsx14(
1255
1381
  MemoryDetail,
1256
1382
  {
1257
1383
  memory: state.selectedMemory,
1258
1384
  relations: state.relations,
1259
- isFocused: state.focusedPane === "detail",
1385
+ resolveTitle: (id) => dataSource.getMemoryTitle(id),
1386
+ isFocused: false,
1260
1387
  height: detailHeight,
1261
1388
  width: rightWidth
1262
1389
  }
1263
1390
  )
1264
1391
  ] })
1265
1392
  ] }),
1266
- /* @__PURE__ */ jsx13(StatsBar, { stats: state.stats, visible: state.filteredMemories })
1393
+ /* @__PURE__ */ jsx14(StatsBar, { stats: state.stats, visible: state.filteredMemories })
1267
1394
  ] });
1268
1395
  }
1269
1396
 
1397
+ // src/commands/memory/dashboard/components/error-boundary.tsx
1398
+ import React6 from "react";
1399
+ import { Box as Box15, Text as Text15, useApp as useApp2, useInput as useInput7 } from "ink";
1400
+ import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
1401
+ function CrashScreen({
1402
+ message
1403
+ }) {
1404
+ const { exit } = useApp2();
1405
+ useInput7((input) => {
1406
+ if (input === "q") exit();
1407
+ });
1408
+ return /* @__PURE__ */ jsxs15(
1409
+ Box15,
1410
+ {
1411
+ flexDirection: "column",
1412
+ borderStyle: "round",
1413
+ borderColor: "red",
1414
+ padding: 1,
1415
+ children: [
1416
+ /* @__PURE__ */ jsx15(Text15, { bold: true, color: "red", children: "Dashboard crashed" }),
1417
+ /* @__PURE__ */ jsx15(Text15, { children: message }),
1418
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "Your data is untouched. Press q to quit." })
1419
+ ]
1420
+ }
1421
+ );
1422
+ }
1423
+ var DashboardErrorBoundary = class extends React6.Component {
1424
+ state = { error: null };
1425
+ static getDerivedStateFromError(error) {
1426
+ return { error };
1427
+ }
1428
+ render() {
1429
+ if (this.state.error) {
1430
+ return /* @__PURE__ */ jsx15(CrashScreen, { message: this.state.error.message });
1431
+ }
1432
+ return this.props.children;
1433
+ }
1434
+ };
1435
+
1270
1436
  // src/commands/memory/dashboard/tui.tsx
1271
- import { jsx as jsx14 } from "react/jsx-runtime";
1437
+ import { jsx as jsx16 } from "react/jsx-runtime";
1272
1438
  async function startTui(options) {
1273
1439
  const config = loadConfig(
1274
1440
  options?.dbPath ? { dataDir: options.dbPath } : void 0
@@ -1287,7 +1453,7 @@ async function startTui(options) {
1287
1453
  );
1288
1454
  let shuttingDown = false;
1289
1455
  const { waitUntilExit, unmount } = render(
1290
- /* @__PURE__ */ jsx14(App, { dataSource })
1456
+ /* @__PURE__ */ jsx16(DashboardErrorBoundary, { children: /* @__PURE__ */ jsx16(App, { dataSource }) })
1291
1457
  );
1292
1458
  function shutdown() {
1293
1459
  if (shuttingDown) return;
@@ -1310,4 +1476,4 @@ async function startTui(options) {
1310
1476
  export {
1311
1477
  startTui
1312
1478
  };
1313
- //# sourceMappingURL=tui-3KITZCNS.js.map
1479
+ //# sourceMappingURL=tui-ZINEA7TI.js.map