kimi-code-memory-mcp-server 0.1.0 → 0.1.2

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 (129) hide show
  1. package/CHANGELOG.md +29 -1
  2. package/README.en.md +342 -0
  3. package/README.md +267 -125
  4. package/assets/contextFlow.svg +144 -0
  5. package/assets/user-agents.md +111 -0
  6. package/dist/config.d.ts +13 -0
  7. package/dist/config.js +13 -0
  8. package/dist/config.js.map +1 -1
  9. package/dist/context/wire-context.d.ts +3 -0
  10. package/dist/context/wire-context.js +20 -50
  11. package/dist/context/wire-context.js.map +1 -1
  12. package/dist/dao/constants.d.ts +33 -0
  13. package/dist/dao/constants.js +17 -0
  14. package/dist/dao/constants.js.map +1 -0
  15. package/dist/dao/index-catalog.d.ts +19 -0
  16. package/dist/dao/index-catalog.js +94 -0
  17. package/dist/dao/index-catalog.js.map +1 -0
  18. package/dist/dao/index-reconciler.d.ts +13 -0
  19. package/dist/dao/index-reconciler.js +162 -0
  20. package/dist/dao/index-reconciler.js.map +1 -0
  21. package/dist/dao/index-store.d.ts +31 -0
  22. package/dist/dao/index-store.js +128 -0
  23. package/dist/dao/index-store.js.map +1 -0
  24. package/dist/dao/index.d.ts +12 -31
  25. package/dist/dao/index.js +50 -404
  26. package/dist/dao/index.js.map +1 -1
  27. package/dist/dao/memory-store.js +2 -10
  28. package/dist/dao/memory-store.js.map +1 -1
  29. package/dist/dao/memory-tree-renderer.d.ts +22 -0
  30. package/dist/dao/memory-tree-renderer.js +75 -0
  31. package/dist/dao/memory-tree-renderer.js.map +1 -0
  32. package/dist/prompts/index.d.ts +26 -0
  33. package/dist/prompts/index.js +103 -0
  34. package/dist/prompts/index.js.map +1 -0
  35. package/dist/refine/adapter.d.ts +6 -0
  36. package/dist/refine/adapter.js +28 -0
  37. package/dist/refine/adapter.js.map +1 -0
  38. package/dist/refine/constants.d.ts +35 -0
  39. package/dist/refine/constants.js +107 -0
  40. package/dist/refine/constants.js.map +1 -0
  41. package/dist/refine/extractor.d.ts +12 -0
  42. package/dist/refine/extractor.js +122 -0
  43. package/dist/refine/extractor.js.map +1 -0
  44. package/dist/refine/store.d.ts +19 -0
  45. package/dist/refine/store.js +139 -0
  46. package/dist/refine/store.js.map +1 -0
  47. package/dist/refine/types.d.ts +56 -0
  48. package/dist/refine/types.js +5 -0
  49. package/dist/refine/types.js.map +1 -0
  50. package/dist/refined-manager.d.ts +10 -56
  51. package/dist/refined-manager.js +22 -341
  52. package/dist/refined-manager.js.map +1 -1
  53. package/dist/resources/index.d.ts +15 -0
  54. package/dist/resources/index.js +134 -0
  55. package/dist/resources/index.js.map +1 -0
  56. package/dist/server.js +46 -2
  57. package/dist/server.js.map +1 -1
  58. package/dist/setup-cli.d.ts +11 -0
  59. package/dist/setup-cli.js +84 -0
  60. package/dist/setup-cli.js.map +1 -0
  61. package/dist/setup.d.ts +33 -0
  62. package/dist/setup.js +206 -0
  63. package/dist/setup.js.map +1 -0
  64. package/dist/tools/context-tools.d.ts +16 -51
  65. package/dist/tools/context-tools.js +247 -55
  66. package/dist/tools/context-tools.js.map +1 -1
  67. package/dist/tools/index.d.ts +5 -827
  68. package/dist/tools/index.js +23 -354
  69. package/dist/tools/index.js.map +1 -1
  70. package/dist/tools/memory-tools.d.ts +4 -60
  71. package/dist/tools/memory-tools.js +129 -79
  72. package/dist/tools/memory-tools.js.map +1 -1
  73. package/dist/tools/system-tools.d.ts +3 -34
  74. package/dist/tools/system-tools.js +86 -32
  75. package/dist/tools/system-tools.js.map +1 -1
  76. package/dist/tools/theme-tools.d.ts +3 -31
  77. package/dist/tools/theme-tools.js +78 -22
  78. package/dist/tools/theme-tools.js.map +1 -1
  79. package/dist/tools/types.d.ts +21 -0
  80. package/dist/tools/types.js +13 -0
  81. package/dist/tools/types.js.map +1 -0
  82. package/dist/types.d.ts +4 -2
  83. package/dist/utils/action-entities.d.ts +16 -0
  84. package/dist/utils/action-entities.js +35 -0
  85. package/dist/utils/action-entities.js.map +1 -0
  86. package/dist/utils/date.d.ts +11 -0
  87. package/dist/utils/date.js +13 -0
  88. package/dist/utils/date.js.map +1 -0
  89. package/dist/utils/file-helpers.d.ts +10 -0
  90. package/dist/utils/file-helpers.js +28 -0
  91. package/dist/utils/file-helpers.js.map +1 -0
  92. package/dist/utils/headings.d.ts +5 -0
  93. package/dist/utils/headings.js +21 -0
  94. package/dist/utils/headings.js.map +1 -0
  95. package/dist/utils/search.d.ts +17 -0
  96. package/dist/utils/search.js +60 -0
  97. package/dist/utils/search.js.map +1 -0
  98. package/dist/utils/tools.d.ts +5 -0
  99. package/dist/utils/tools.js +10 -0
  100. package/dist/utils/tools.js.map +1 -0
  101. package/dist/version.d.ts +1 -1
  102. package/dist/version.js +1 -1
  103. package/dist/vis/api.d.ts +82 -0
  104. package/dist/vis/api.js +212 -0
  105. package/dist/vis/api.js.map +1 -0
  106. package/dist/vis/auto-start.d.ts +12 -0
  107. package/dist/vis/auto-start.js +87 -0
  108. package/dist/vis/auto-start.js.map +1 -0
  109. package/dist/vis/server.d.ts +14 -0
  110. package/dist/vis/server.js +103 -0
  111. package/dist/vis/server.js.map +1 -0
  112. package/dist/vis/static/app.js +395 -0
  113. package/dist/vis/static/index.html +95 -0
  114. package/dist/vis/static/style.css +707 -0
  115. package/dist/vis-cli.d.ts +7 -0
  116. package/dist/vis-cli.js +140 -0
  117. package/dist/vis-cli.js.map +1 -0
  118. package/docs/0.agent-memory-market-research.md +354 -0
  119. package/docs/1.memory-system-proposal-for-kimi-code.md +688 -0
  120. package/docs/2.memory-architecture-overview.md +417 -0
  121. package/docs/3.memory-skill-prompt-design.md +430 -0
  122. package/docs/4.kimi-code-native-evolution-roadmap.md +559 -0
  123. package/docs/5.decision-guard-design-notes.md +500 -0
  124. package/docs/ARCHITECTURE.md +2 -2
  125. package/docs/design-story.md +350 -0
  126. package/docs/three-layer-memory-model.md +153 -0
  127. package/docs/three-layer-memory-model.zh-CN.md +153 -0
  128. package/package.json +14 -6
  129. package/README.zh-CN.md +0 -227
@@ -0,0 +1,395 @@
1
+ (() => {
2
+ const state = {
3
+ workspace: null,
4
+ themes: [],
5
+ decisions: [],
6
+ memories: null,
7
+ activeMemoryPath: null,
8
+ };
9
+
10
+ const $ = (sel) => document.querySelector(sel);
11
+ const $$ = (sel) => Array.from(document.querySelectorAll(sel));
12
+
13
+ function formatDate(iso) {
14
+ if (!iso) return '–';
15
+ const d = new Date(iso);
16
+ return isNaN(d.getTime()) ? iso : d.toLocaleString();
17
+ }
18
+
19
+ function escapeHtml(str) {
20
+ return String(str)
21
+ .replace(/&/g, '&')
22
+ .replace(/</g, '&lt;')
23
+ .replace(/>/g, '&gt;')
24
+ .replace(/"/g, '&quot;');
25
+ }
26
+
27
+ async function api(path, options = {}) {
28
+ const res = await fetch(path, {
29
+ headers: { 'Content-Type': 'application/json' },
30
+ ...options,
31
+ });
32
+ if (!res.ok) {
33
+ const text = await res.text().catch(() => '');
34
+ throw new Error(`${res.status}: ${text}`);
35
+ }
36
+ return res.json();
37
+ }
38
+
39
+ function setStatus(el, message, type = 'success') {
40
+ el.textContent = message;
41
+ el.className = `status ${type}`;
42
+ if (message) {
43
+ setTimeout(() => {
44
+ el.textContent = '';
45
+ el.className = 'status';
46
+ }, 3000);
47
+ }
48
+ }
49
+
50
+ function showView(name) {
51
+ $$('.view').forEach((v) => v.classList.remove('view-active'));
52
+ $(`#view-${name}`).classList.add('view-active');
53
+ $$('.nav-item').forEach((n) => n.classList.toggle('active', n.dataset.view === name));
54
+ $('#sidebar')?.classList.remove('open');
55
+ }
56
+
57
+ function renderStats(stats) {
58
+ const grid = $('#stats-grid');
59
+ grid.innerHTML = Object.entries(stats)
60
+ .map(
61
+ ([key, value]) => `
62
+ <div class="stat-card">
63
+ <div class="stat-value">${escapeHtml(String(value))}</div>
64
+ <div class="stat-label">${escapeHtml(key.replace(/([A-Z])/g, ' $1').toLowerCase())}</div>
65
+ </div>
66
+ `,
67
+ )
68
+ .join('');
69
+ }
70
+
71
+ async function loadWorkspace() {
72
+ const data = await api('/api/workspace');
73
+ state.workspace = data;
74
+ $('#workspace-id').textContent = data.id;
75
+ renderStats(data.stats);
76
+ $('#essence-editor').value = data.essence;
77
+ }
78
+
79
+ async function saveEssence() {
80
+ const content = $('#essence-editor').value;
81
+ const status = $('#essence-status');
82
+ try {
83
+ await api('/api/essence', {
84
+ method: 'POST',
85
+ body: JSON.stringify({ content }),
86
+ });
87
+ setStatus(status, 'Essence saved.', 'success');
88
+ } catch (err) {
89
+ setStatus(status, `Save failed: ${err.message}`, 'error');
90
+ }
91
+ }
92
+
93
+ async function syncIndex() {
94
+ const btn = $('#sync-btn');
95
+ const original = btn.textContent;
96
+ btn.textContent = 'Syncing…';
97
+ try {
98
+ await api('/api/sync', { method: 'POST' });
99
+ await loadWorkspace();
100
+ btn.textContent = 'Synced';
101
+ } catch (err) {
102
+ btn.textContent = `Failed: ${err.message}`;
103
+ }
104
+ setTimeout(() => (btn.textContent = original), 1500);
105
+ }
106
+
107
+ function renderThemes() {
108
+ const grid = $('#themes-grid');
109
+ if (state.themes.length === 0) {
110
+ grid.innerHTML = '<div class="empty-state">No themes yet.</div>';
111
+ return;
112
+ }
113
+ grid.innerHTML = state.themes
114
+ .map(
115
+ (theme) => `
116
+ <div class="theme-card" data-theme="${escapeHtml(theme.name)}">
117
+ <div class="theme-name">${escapeHtml(theme.displayName || theme.name)}</div>
118
+ <div class="theme-meta">
119
+ <span>${theme.turnCount} turns</span>
120
+ <span>${theme.memoryCount} memories</span>
121
+ </div>
122
+ </div>
123
+ `,
124
+ )
125
+ .join('');
126
+
127
+ grid.querySelectorAll('.theme-card').forEach((card) => {
128
+ card.addEventListener('click', () => openThemeModal(card.dataset.theme));
129
+ });
130
+ }
131
+
132
+ async function loadThemes() {
133
+ state.themes = await api('/api/themes');
134
+ renderThemes();
135
+ }
136
+
137
+ async function openThemeModal(themeName) {
138
+ const data = await api(`/api/themes/${encodeURIComponent(themeName)}`);
139
+ $('#modal-title').textContent = data.displayName || data.theme;
140
+ const body = $('#modal-body');
141
+
142
+ body.innerHTML = `
143
+ <div class="inline-edit">
144
+ <input id="theme-display-name" type="text" value="${escapeHtml(
145
+ data.displayName || data.theme,
146
+ )}" placeholder="Display name" />
147
+ <button id="theme-save-name" class="btn btn-primary btn-sm" type="button">Rename</button>
148
+ </div>
149
+ <div class="timeline" id="theme-timeline"></div>
150
+ `;
151
+
152
+ const timeline = $('#theme-timeline');
153
+ if (data.items.length === 0) {
154
+ timeline.innerHTML = '<div class="empty-state">No turns or memories linked to this theme.</div>';
155
+ } else {
156
+ timeline.innerHTML = data.items
157
+ .map((item) => {
158
+ if (item.type === 'turn') {
159
+ const turn = item.data;
160
+ const bullets =
161
+ Array.isArray(turn.facts) && turn.facts.length
162
+ ? `<ul class="decision-bullets">${turn.facts
163
+ .map((f) => `<li>${escapeHtml(f)}</li>`)
164
+ .join('')}</ul>`
165
+ : '';
166
+ const tags = [
167
+ ...(turn.entities?.files || []),
168
+ ...(Object.values(turn.categories || {}).flat()),
169
+ ];
170
+ return `
171
+ <div class="timeline-item">
172
+ <div class="timeline-dot"></div>
173
+ <div class="timeline-time">${formatDate(turn.timestamp)} · Turn ${turn.turnId}</div>
174
+ <div class="timeline-card">
175
+ <h4>${escapeHtml(turn.summary || 'Untitled turn')}</h4>
176
+ ${bullets}
177
+ <div class="tag-list">
178
+ ${tags
179
+ .map((t) => `<span class="tag${t.includes('.') ? ' file' : ''}">${escapeHtml(t)}</span>`)
180
+ .join('')}
181
+ </div>
182
+ </div>
183
+ </div>
184
+ `;
185
+ }
186
+ const memory = item.data;
187
+ return `
188
+ <div class="timeline-item">
189
+ <div class="timeline-dot memory"></div>
190
+ <div class="timeline-time">${formatDate(memory.timestamp)} · Memory</div>
191
+ <div class="timeline-card">
192
+ <h4>${escapeHtml(memory.title || memory.key)}</h4>
193
+ <p>${escapeHtml(memory.content.slice(0, 240))}${memory.content.length > 240 ? '…' : ''}</p>
194
+ <div class="timeline-meta">${escapeHtml(memory.folder)}/${escapeHtml(memory.key)}</div>
195
+ </div>
196
+ </div>
197
+ `;
198
+ })
199
+ .join('');
200
+ }
201
+
202
+ $('#theme-save-name').addEventListener('click', async () => {
203
+ const displayName = $('#theme-display-name').value;
204
+ try {
205
+ await api(`/api/themes/${encodeURIComponent(themeName)}`, {
206
+ method: 'POST',
207
+ body: JSON.stringify({ displayName }),
208
+ });
209
+ await loadThemes();
210
+ $('#modal-title').textContent = displayName;
211
+ } catch (err) {
212
+ alert(`Rename failed: ${err.message}`);
213
+ }
214
+ });
215
+
216
+ openModal();
217
+ }
218
+
219
+ function renderDecisions() {
220
+ const list = $('#decisions-list');
221
+ if (state.decisions.length === 0) {
222
+ list.innerHTML = '<div class="empty-state">No recent decisions.</div>';
223
+ return;
224
+ }
225
+ list.innerHTML = state.decisions
226
+ .map(
227
+ (d) => `
228
+ <div class="decision-card">
229
+ <div class="decision-header">
230
+ <h3 class="decision-title">Turn ${d.turnId} · ${escapeHtml(d.sessionId)}</h3>
231
+ <span class="decision-time">${formatDate(d.timestamp)}</span>
232
+ </div>
233
+ <div class="decision-summary">${escapeHtml(d.summary)}</div>
234
+ <ul class="decision-bullets">
235
+ ${d.decisions.map((dec) => `<li>${escapeHtml(dec)}</li>`).join('')}
236
+ </ul>
237
+ <div class="tag-list">
238
+ ${d.files.map((f) => `<span class="tag file">${escapeHtml(f)}</span>`).join('')}
239
+ ${d.tags.map((t) => `<span class="tag">${escapeHtml(t)}</span>`).join('')}
240
+ </div>
241
+ </div>
242
+ `,
243
+ )
244
+ .join('');
245
+ }
246
+
247
+ async function loadDecisions() {
248
+ state.decisions = await api('/api/decisions?limit=50');
249
+ renderDecisions();
250
+ }
251
+
252
+ function renderMemoryNode(node, basePath = '') {
253
+ const currentPath = basePath ? `${basePath}/${node.name}` : node.name;
254
+ const filesHtml =
255
+ node.files && node.files.length
256
+ ? node.files
257
+ .map(
258
+ (file) => `
259
+ <div class="tree-file" data-path="${escapeHtml(currentPath)}/${escapeHtml(file.key)}" data-key="${escapeHtml(
260
+ file.key,
261
+ )}">
262
+ <span>📄</span>
263
+ <span>${escapeHtml(file.title || file.key)}</span>
264
+ </div>
265
+ `,
266
+ )
267
+ .join('')
268
+ : '';
269
+
270
+ const childrenHtml =
271
+ node.children && node.children.length
272
+ ? node.children.map((child) => renderMemoryNode(child, currentPath)).join('')
273
+ : '';
274
+
275
+ const commentHtml = node.comment
276
+ ? `<div class="tree-comment">${escapeHtml(node.comment)}</div>`
277
+ : '';
278
+
279
+ if (!node.children?.length && !node.files?.length) {
280
+ return '';
281
+ }
282
+
283
+ return `
284
+ <div class="tree-node">
285
+ <div class="tree-folder">
286
+ <span class="tree-folder-icon">▸</span>
287
+ <span>${escapeHtml(node.name)}</span>
288
+ </div>
289
+ ${commentHtml}
290
+ ${filesHtml}
291
+ ${childrenHtml}
292
+ </div>
293
+ `;
294
+ }
295
+
296
+ async function loadMemoryContent(filePath, key) {
297
+ const folder = filePath.replace(new RegExp(`/${key}$`), '');
298
+ const encodedFolder = encodeURIComponent(folder);
299
+ const encodedKey = encodeURIComponent(key);
300
+ const preview = $('#memory-preview');
301
+
302
+ try {
303
+ const memory = await api(`/api/memory/${encodedFolder}/${encodedKey}`);
304
+ preview.innerHTML = `
305
+ <div class="preview-header">
306
+ <div>
307
+ <h2 class="preview-title">${escapeHtml(memory.title || key)}</h2>
308
+ <div class="preview-path">${escapeHtml(filePath)}</div>
309
+ <div class="tag-list" style="margin-top:8px">
310
+ ${memory.tags.map((t) => `<span class="tag">${escapeHtml(t)}</span>`).join('')}
311
+ </div>
312
+ </div>
313
+ </div>
314
+ <div class="preview-content">${escapeHtml(memory.content)}</div>
315
+ `;
316
+ } catch (err) {
317
+ preview.innerHTML = `
318
+ <div class="preview-header">
319
+ <h2 class="preview-title">${escapeHtml(key)}</h2>
320
+ </div>
321
+ <div class="preview-content error">Failed to load memory: ${escapeHtml(err.message)}</div>
322
+ `;
323
+ }
324
+ }
325
+
326
+ function renderMemories() {
327
+ const tree = $('#memory-tree');
328
+ if (!state.memories) {
329
+ tree.innerHTML = '<div class="empty-state">Loading memories…</div>';
330
+ return;
331
+ }
332
+ tree.innerHTML = renderMemoryNode(state.memories);
333
+
334
+ tree.querySelectorAll('.tree-file').forEach((fileEl) => {
335
+ fileEl.addEventListener('click', () => {
336
+ tree.querySelectorAll('.tree-file').forEach((f) => f.classList.remove('active'));
337
+ fileEl.classList.add('active');
338
+ state.activeMemoryPath = fileEl.dataset.path;
339
+ loadMemoryContent(fileEl.dataset.path, fileEl.dataset.key);
340
+ });
341
+ });
342
+ }
343
+
344
+ async function loadMemories() {
345
+ state.memories = await api('/api/memories');
346
+ renderMemories();
347
+ }
348
+
349
+ function openModal() {
350
+ $('#modal').classList.add('open');
351
+ $('#modal').setAttribute('aria-hidden', 'false');
352
+ }
353
+
354
+ function closeModal() {
355
+ $('#modal').classList.remove('open');
356
+ $('#modal').setAttribute('aria-hidden', 'true');
357
+ }
358
+
359
+ function init() {
360
+ $$('.nav-item').forEach((link) => {
361
+ link.addEventListener('click', (e) => {
362
+ e.preventDefault();
363
+ const view = link.dataset.view;
364
+ showView(view);
365
+ history.pushState(null, '', `#${view}`);
366
+ if (view === 'themes' && state.themes.length === 0) loadThemes();
367
+ if (view === 'decisions' && state.decisions.length === 0) loadDecisions();
368
+ if (view === 'memories' && !state.memories) loadMemories();
369
+ });
370
+ });
371
+
372
+ $('#menu-toggle').addEventListener('click', () => {
373
+ $('.sidebar').classList.toggle('open');
374
+ });
375
+
376
+ $('#save-essence').addEventListener('click', saveEssence);
377
+ $('#sync-btn').addEventListener('click', syncIndex);
378
+ $('#modal-close').addEventListener('click', closeModal);
379
+ $('.modal-backdrop').addEventListener('click', closeModal);
380
+
381
+ document.addEventListener('keydown', (e) => {
382
+ if (e.key === 'Escape') closeModal();
383
+ });
384
+
385
+ const initialView = location.hash.replace('#', '') || 'workspace';
386
+ showView(initialView);
387
+
388
+ loadWorkspace();
389
+ if (initialView === 'themes') loadThemes();
390
+ if (initialView === 'decisions') loadDecisions();
391
+ if (initialView === 'memories') loadMemories();
392
+ }
393
+
394
+ init();
395
+ })();
@@ -0,0 +1,95 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Kimi Memory Vis</title>
7
+ <link rel="stylesheet" href="/style.css" />
8
+ </head>
9
+ <body>
10
+ <div class="app">
11
+ <aside class="sidebar">
12
+ <div class="brand">
13
+ <div class="brand-icon">K</div>
14
+ <div class="brand-text">Memory Vis</div>
15
+ </div>
16
+ <nav class="nav">
17
+ <a class="nav-item active" href="#workspace" data-view="workspace">
18
+ <span class="nav-icon">◈</span>
19
+ <span>Workspace</span>
20
+ </a>
21
+ <a class="nav-item" href="#themes" data-view="themes">
22
+ <span class="nav-icon">◉</span>
23
+ <span>Themes</span>
24
+ </a>
25
+ <a class="nav-item" href="#decisions" data-view="decisions">
26
+ <span class="nav-icon">◆</span>
27
+ <span>Decisions</span>
28
+ </a>
29
+ <a class="nav-item" href="#memories" data-view="memories">
30
+ <span class="nav-icon">▣</span>
31
+ <span>Memories</span>
32
+ </a>
33
+ </nav>
34
+ <div class="sidebar-footer">
35
+ <button id="sync-btn" class="btn btn-ghost" type="button">Sync index</button>
36
+ </div>
37
+ </aside>
38
+
39
+ <main class="main">
40
+ <header class="topbar">
41
+ <button id="menu-toggle" class="menu-toggle" type="button" aria-label="Toggle menu">☰</button>
42
+ <div class="workspace-id" id="workspace-id">–</div>
43
+ </header>
44
+
45
+ <div class="content">
46
+ <section id="view-workspace" class="view view-active">
47
+ <h1 class="page-title">Workspace</h1>
48
+ <div class="stats-grid" id="stats-grid"></div>
49
+ <div class="card">
50
+ <div class="card-header">
51
+ <h2 class="card-title">Essence</h2>
52
+ <button id="save-essence" class="btn btn-primary" type="button">Save</button>
53
+ </div>
54
+ <textarea id="essence-editor" class="essence-editor" placeholder="Workspace essence is empty. Write a short constitution here..."></textarea>
55
+ <div id="essence-status" class="status"></div>
56
+ </div>
57
+ </section>
58
+
59
+ <section id="view-themes" class="view">
60
+ <h1 class="page-title">Themes</h1>
61
+ <div id="themes-grid" class="themes-grid"></div>
62
+ </section>
63
+
64
+ <section id="view-decisions" class="view">
65
+ <h1 class="page-title">Recent Decisions</h1>
66
+ <div id="decisions-list" class="decisions-list"></div>
67
+ </section>
68
+
69
+ <section id="view-memories" class="view">
70
+ <h1 class="page-title">Memories</h1>
71
+ <div class="memories-layout">
72
+ <div id="memory-tree" class="memory-tree"></div>
73
+ <div id="memory-preview" class="memory-preview">
74
+ <div class="empty-state">Select a memory to view its content.</div>
75
+ </div>
76
+ </div>
77
+ </section>
78
+ </div>
79
+ </main>
80
+ </div>
81
+
82
+ <div id="modal" class="modal" aria-hidden="true">
83
+ <div class="modal-backdrop"></div>
84
+ <div class="modal-content">
85
+ <div class="modal-header">
86
+ <h2 id="modal-title" class="modal-title">Theme</h2>
87
+ <button id="modal-close" class="modal-close" type="button" aria-label="Close">×</button>
88
+ </div>
89
+ <div id="modal-body" class="modal-body"></div>
90
+ </div>
91
+ </div>
92
+
93
+ <script src="/app.js"></script>
94
+ </body>
95
+ </html>