Skip to content

Commit 8c21546

Browse files
author
Zehee
committed
release: v0.2.0
1 parent 450d1c0 commit 8c21546

24 files changed

Lines changed: 2425 additions & 1129 deletions

‎CHANGELOG.md‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
10+
## [0.2.0] - 2026-06-25
11+
12+
### Added
13+
- MCP tools: `delete_theme` and `delete_search_view` (with optional `deleteRefinedTurns` cascade).
14+
- Visual dashboard: delete actions for themes and search views, read-only default for Markdown editors, Edit/Delete buttons in composer headers, scrollable Markdown preview, dynamic page title using workspace folder name, lightweight Markdown rendering.
15+
- Frontend refactor: dashboard `app.js` split into ES modules (`state.js`, `api.js`, `utils/helpers.js`, `utils/markdown.js`).
16+
17+
### Changed
18+
- Markdown documents in the dashboard now render in read-only mode by default; editing requires clicking the Edit button.
19+
- Breadcrumbs root label now shows the actual workspace folder name.
20+
821
## [0.1.2] - 2026-06-25
922

1023
### Added
@@ -48,7 +61,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4861
- Improved `scripts/session-search-verify.mjs` with CLI/env args, safer parsing, and cleanup.
4962
- Updated READMEs to reflect npm-published status and setup command.
5063

51-
[Unreleased]: https://github.com/Zehee/kimi-code-memory-mcp-server/compare/v0.1.2...HEAD
64+
[Unreleased]: https://github.com/Zehee/kimi-code-memory-mcp-server/compare/v0.2.0...HEAD
65+
[0.2.0]: https://github.com/Zehee/kimi-code-memory-mcp-server/releases/tag/v0.2.0
5266
[0.1.2]: https://github.com/Zehee/kimi-code-memory-mcp-server/releases/tag/v0.1.2
5367
[0.1.1]: https://github.com/Zehee/kimi-code-memory-mcp-server/releases/tag/v0.1.1
5468
[0.1.0]: https://github.com/Zehee/kimi-code-memory-mcp-server/releases/tag/v0.1.0

‎README.en.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ A local stdio MCP server that gives [Kimi Code CLI](https://github.com/MoonshotA
1919
- **Theme tracing** — associate conversation turns and memories with themes, then trace their evolution.
2020
- **Refined turn summaries** — reusable turn-level atomic summaries shared across themes.
2121
- **Rebuilding index** — `index.json` is a cache; `.md` files are the source of truth.
22+
- **Theme & search-view deletion** — remove low-quality themes or saved search views, optionally purging their refined turns.
23+
- **Dashboard enhancements** — dynamic workspace title, read-only Markdown viewer with lightweight rendering, delete actions.
2224

2325
## Theme Tracing
2426

@@ -256,6 +258,9 @@ You can override the storage root with the `MEMORY_STORE_ROOT` environment varia
256258
| `tag_theme` | Associate a turn or memory with a theme |
257259
| `trace_theme` | Trace a theme's evolution |
258260
| `list_themes` | List themes |
261+
| `delete_theme` | Delete a theme association file |
262+
| `list_search_views` | List saved search views |
263+
| `delete_search_view` | Delete a saved search view (optionally purge referenced refined turns) |
259264
| `open_memory_dashboard` | Open the memory dashboard in the browser |
260265
| `refine_session_turns` | Generate refined turn summaries |
261266

@@ -269,6 +274,8 @@ npx kimi-memory-vis
269274

270275
It starts on `http://127.0.0.1:58628` and opens the browser automatically.
271276

277+
The dashboard now renders Markdown documents in read-only mode by default; click **Edit** to modify. Other improvements include a scrollable Markdown preview, dynamic page title using the workspace folder name, and delete actions for themes and search views.
278+
272279
To auto-start the dashboard when the MCP server starts:
273280

274281
```bash

‎README.md‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
- **主题追溯** —— 将对话轮次和记忆关联到主题,并追踪其演化。
2121
- **精炼轮次摘要** —— 可在多个主题间共享的轮次级原子摘要。
2222
- **可重建索引** —— `index.json` 只是缓存,`.md` 文件才是真相来源。
23+
- **主题与搜索视图删除** —— 支持删除低质量主题或搜索视图,并可选择级联清理其关联的精炼轮次。
24+
- **可视化仪表盘增强** —— 工作区标题动态显示文件夹名、Markdown 默认只读、轻量级渲染预览、主题/搜索视图删除。
2325

2426
## 主题追溯
2527

@@ -257,6 +259,10 @@ Agent:[调用 mcp__kimi-memory__tag_theme] theme=cache-design
257259
| `tag_theme` | 将轮次或记忆关联到主题 |
258260
| `trace_theme` | 追溯主题演化 |
259261
| `list_themes` | 列出主题 |
262+
| `delete_theme` | 删除主题关联文件 |
263+
| `list_search_views` | 列出保存的搜索视图 |
264+
| `delete_search_view` | 删除搜索视图(可选级联删除关联精炼轮次) |
265+
| `open_memory_dashboard` | 在浏览器中打开记忆仪表盘 |
260266
| `refine_session_turns` | 生成精炼轮次摘要 |
261267

262268
## Prompts
@@ -303,10 +309,13 @@ export KIMI_MEMORY_AUTO_VIS=1
303309

304310
仪表盘支持:
305311

306-
- 工作区概览与 `essence.md` 行内编辑
307-
- 主题时间线浏览
312+
- 工作区概览与 `essence.md` 浏览/编辑(默认只读,点击“编辑”后修改)
313+
- Markdown 文档轻量级渲染,超长内容自带滚动条
314+
- 主题时间线浏览与主题删除
315+
- 搜索视图列表与删除(支持仅删视图或级联清理精炼轮次)
308316
- 近期决策列表
309317
- 显式记忆目录浏览
318+
- 控制面板标题与面包屑根目录显示为用户工作区文件夹名
310319

311320
## 开发
312321

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "kimi-code-memory-mcp-server",
3-
"version": "0.1.2",
3+
"version": "0.2.0",
44
"description": "Local stdio MCP server providing cross-session memory for Kimi Code CLI",
55
"main": "dist/server.js",
66
"types": "dist/server.d.ts",

‎restart-kimi-web.bat‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@echo off
2+
kimi server kill
3+
timeout /t 3 /nobreak >nul
4+
cd /d "D:\workspace\kimi-code-memory-mcp-server"
5+
start "" cmd /c "kimi web"

‎src/refine/store.ts‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,20 @@ export class RefinedStore {
208208
return rows.map((r) => rowToTurn(r));
209209
}
210210

211+
deleteRefinedTurns(refs: Array<{ sessionId: string; turnId: number }>): number {
212+
if (refs.length === 0) return 0;
213+
const deleteStmt = this.db.prepare('DELETE FROM refined_turns WHERE session_id = ? AND turn_id = ?');
214+
const transaction = this.db.transaction(() => {
215+
let count = 0;
216+
for (const ref of refs) {
217+
const info = deleteStmt.run(ref.sessionId, ref.turnId);
218+
count += info.changes;
219+
}
220+
return count;
221+
});
222+
return transaction();
223+
}
224+
211225
close(): void {
212226
this.db.close();
213227
}

‎src/refined-manager.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ export class RefinedManager {
6969
return this.store.listRecentTurns(limit);
7070
}
7171

72+
async deleteRefinedTurns(refs: Array<{ sessionId: string; turnId: number }>): Promise<number> {
73+
return this.mutex.runExclusive(() => {
74+
return this.store.deleteRefinedTurns(refs);
75+
});
76+
}
77+
7278
close(): void {
7379
this.store.close();
7480
}

‎src/theme-manager.ts‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ export class ThemeManager {
6969
fs.renameSync(tmpPath, filePath);
7070
}
7171

72+
deleteTheme(theme: string): boolean {
73+
const filePath = this.themeFilePath(theme);
74+
if (!fs.existsSync(filePath)) return false;
75+
fs.unlinkSync(filePath);
76+
return true;
77+
}
78+
7279
listThemes(): string[] {
7380
if (!fs.existsSync(this.themesRoot)) return [];
7481
return fs

‎src/tools/context-tools.ts‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44

55
import type {
6+
DeleteSearchViewArgs,
67
ListSearchViewsArgs,
78
LoadMoreContextArgs,
89
LoadTurnContextArgs,
@@ -572,6 +573,50 @@ export function createContextTools(ctx: Ctx): ToolDefinition[] {
572573
});
573574
}
574575

576+
async function handleDeleteSearchView(args: DeleteSearchViewArgs) {
577+
const key = typeof args.key === 'string' ? args.key.trim() : '';
578+
if (!key) {
579+
return toolResult({ success: false, error: 'Missing or invalid "key"' }, true);
580+
}
581+
const deleteRefinedTurns = args.deleteRefinedTurns === true;
582+
const filePath = path.join(storeRoot, 'searches', `${key}.json`);
583+
if (!fs.existsSync(filePath)) {
584+
return toolResult({ success: false, error: 'Search view not found' }, true);
585+
}
586+
587+
const refinedTurnsToDelete: Array<{ sessionId: string; turnId: number }> = [];
588+
if (deleteRefinedTurns) {
589+
try {
590+
const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
591+
const clusters = Array.isArray(data.clusters) ? data.clusters : [];
592+
const seen = new Set<string>();
593+
for (const cluster of clusters) {
594+
const members = Array.isArray(cluster.members) ? cluster.members : [];
595+
for (const m of members) {
596+
if (m && typeof m.sessionId === 'string' && typeof m.turnId === 'number') {
597+
const id = `${m.sessionId}:${m.turnId}`;
598+
if (!seen.has(id)) {
599+
seen.add(id);
600+
refinedTurnsToDelete.push({ sessionId: m.sessionId, turnId: m.turnId });
601+
}
602+
}
603+
}
604+
}
605+
} catch {
606+
// Ignore parse errors and proceed with deleting the view file.
607+
}
608+
}
609+
610+
fs.unlinkSync(filePath);
611+
612+
let deletedRefinedTurns = 0;
613+
if (deleteRefinedTurns && refinedTurnsToDelete.length > 0) {
614+
deletedRefinedTurns = await refinedManager.deleteRefinedTurns(refinedTurnsToDelete);
615+
}
616+
617+
return toolResult({ success: true, deletedRefinedTurns });
618+
}
619+
575620
const tools: ToolDefinition[] = [
576621
{
577622
name: 'load_workspace_context',
@@ -661,6 +706,23 @@ export function createContextTools(ctx: Ctx): ToolDefinition[] {
661706
},
662707
handler: adaptHandler(handleListSearchViews),
663708
},
709+
{
710+
name: 'delete_search_view',
711+
description:
712+
'Delete a saved search view. Set deleteRefinedTurns to true to also remove the refined turns referenced by this view (useful for purging low-quality refined data).',
713+
inputSchema: {
714+
type: 'object',
715+
properties: {
716+
key: { type: 'string', description: 'Search view file key (e.g. search-abc123.json without extension)' },
717+
deleteRefinedTurns: {
718+
type: 'boolean',
719+
description: 'If true, also delete all refined turns referenced by this view.',
720+
},
721+
},
722+
required: ['key'],
723+
},
724+
handler: adaptHandler(handleDeleteSearchView),
725+
},
664726
{
665727
name: 'load_turn_context',
666728
description:

‎src/tools/system-tools.ts‎

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ const ESSENCE = {
3131
'不强制模板:Agent 自行决定最终结构和章节。',
3232
'大小提示:工具校验并提示是否超过 15KB,但不阻止保存。',
3333
'工具不碰 memory/ 源文件:Agent 整理后如需清理或重写源记忆,自行调用 delete / remember。',
34+
'剔除过时内容:整理时识别并移除已被后续决策覆盖、已失效或仅具临时价值的记忆。',
35+
'生成精要后,你可以重新组织 memory/ 区的文件内容和目录结构(重命名、移动、合并、拆分),并自行调用 remember / move / delete 等工具执行。',
3436
].join('\n'),
3537
};
3638

@@ -164,16 +166,43 @@ export function createSystemTools(ctx: Ctx): ToolDefinition[] {
164166
async function handleBootstrapWorkspace(args: {
165167
detailed_rounds?: number;
166168
summary_rounds?: number;
169+
force?: boolean;
167170
}) {
168171
await indexDao.reconcileIndex();
169172

170173
const contextData = await buildWorkspaceContext(ctx, args);
171174
const existingEssence = loadEssenceFile();
172175
const config = loadMcpConfig();
173176

177+
// Defensive: if the host already loaded this session's turns (e.g. `kimi web`
178+
// or `kimi -c`), skip returning them again to avoid context duplication.
179+
let recentContext:
180+
| (typeof contextData.recentContext & {
181+
skipped?: boolean;
182+
reason?: string;
183+
})
184+
| null = contextData.recentContext;
185+
if (
186+
recentContext &&
187+
args.force !== true &&
188+
(recentContext.totalTurns > 0 ||
189+
recentContext.detailedRounds.length > 0 ||
190+
recentContext.summaryRounds.length > 0)
191+
) {
192+
recentContext = {
193+
...recentContext,
194+
detailedRounds: [],
195+
summaryRounds: [],
196+
compactionSummaries: [],
197+
skipped: true,
198+
reason:
199+
'Session already has turns; skipping detailed/summary rounds to avoid duplicating host-loaded context.',
200+
};
201+
}
202+
174203
return toolResult({
175204
workspace: contextData.workspace,
176-
recentContext: contextData.recentContext,
205+
recentContext,
177206
essence: existingEssence
178207
? {
179208
found: true,
@@ -273,6 +302,11 @@ export function createSystemTools(ctx: Ctx): ToolDefinition[] {
273302
type: 'number',
274303
description: 'Number of preceding rounds to return as summaries.',
275304
},
305+
force: {
306+
type: 'boolean',
307+
description:
308+
'If true, return recent context even when the current session already has turns.',
309+
},
276310
},
277311
},
278312
handler: adaptHandler(handleBootstrapWorkspace),

0 commit comments

Comments
 (0)