Skip to content

Commit df2d05e

Browse files
CSCSoftwareclaude
andcommitted
feat: add summary layer for tasks and note history (v1.15.0)
Tasks and archived notes now support a one-sentence `summary` field that serves as a table-of-contents entry. Task lists show summaries inline, note history displays summaries instead of truncated text. Auto-migration via ALTER TABLE for existing databases. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e956cec commit df2d05e

12 files changed

Lines changed: 102 additions & 26 deletions

File tree

.claude/CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
MCP Server für persistentes Code-Indexing. Ermöglicht Claude Code schnelle, präzise Suchen statt Grep/Glob.
44

5-
**Version:** 1.11.0 | **Sprachen:** 11 | **Repo:** https://github.com/CSCSoftware/AiDex
5+
**Version:** 1.15.0 | **Sprachen:** 11 | **Repo:** https://github.com/CSCSoftware/AiDex
66

77
## Build & Run
88

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ All notable changes to AiDex will be documented in this file.
44

55
## [Unreleased]
66

7+
## [1.15.0] - 2026-03-17
8+
9+
### Added
10+
- **Task summaries**: Tasks now support a `summary` field — a one-sentence table-of-contents entry (~150 chars) that the AI writes on create/update. `aidex_tasks` shows summaries inline so you can scan the backlog without reading full details.
11+
- **Note history summaries**: Archived notes now get an optional `summary` field. When a note is overwritten or cleared, a summary can be provided for the archive. `aidex_note` with `history: true` shows summaries (with fallback to truncated preview for older notes). Search also matches summaries.
12+
- **Auto-migration**: Existing databases are automatically upgraded with `ALTER TABLE ADD COLUMN summary` — no manual migration needed.
13+
- **Viewer integration**: Task summaries shown in italic between title and description in the browser viewer.
14+
715
## [1.14.0] - 2026-03-10
816

917
### Added

MCP-API-REFERENCE.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,7 @@ Read or write session notes. Persists in the database between sessions.
578578
| `history` | boolean | - | Show archived note history, newest first (default: false) |
579579
| `search` | string | - | Search term to find in note history (case-insensitive) |
580580
| `limit` | number | - | Max history/search entries to return (default: 20) |
581+
| `summary` | string | - | One-sentence summary for the archived note (~150 chars). Provide when writing (old note gets archived with this summary) or clearing. |
581582

582583
**Operations:**
583584

@@ -595,8 +596,8 @@ Read or write session notes. Persists in the database between sessions.
595596
// Read note
596597
{ "path": "." }
597598

598-
// Write note (old note auto-archived)
599-
{ "path": ".", "note": "Test glob fix after restart" }
599+
// Write note with summary for archive (old note auto-archived with summary)
600+
{ "path": ".", "note": "Test glob fix after restart", "summary": "Previous: finished parser refactoring and tests" }
600601

601602
// Append to note
602603
{ "path": ".", "note": "Also check edge cases", "append": true }
@@ -669,6 +670,7 @@ Manage a single task in the project backlog. Tasks persist in the AiDex database
669670
| `id` | number | for read/update/delete/log | Task ID |
670671
| `title` | string | for create | Task title |
671672
| `description` | string | - | Task description (optional details) |
673+
| `summary` | string | - | One-sentence summary (~150 chars). Shown in task list as table-of-contents. Write on create, update on changes. |
672674
| `priority` | number | - | `1` = high, `2` = medium (default), `3` = low |
673675
| `status` | string | - | `backlog` (default), `active`, `done`, `cancelled` |
674676
| `tags` | string | - | Comma-separated tags (e.g., `"bug, viewer"`) |
@@ -690,10 +692,11 @@ Manage a single task in the project backlog. Tasks persist in the AiDex database
690692

691693
**Examples:**
692694
```json
693-
// Create a high-priority bug task
695+
// Create a high-priority bug task with summary
694696
{
695697
"path": ".", "action": "create",
696698
"title": "Fix memory leak in parser",
699+
"summary": "Parser allocates unbounded buffers for nested generics",
697700
"priority": 1, "tags": "bug, parser"
698701
}
699702

@@ -728,6 +731,7 @@ List and filter tasks in the project backlog. Returns tasks grouped by status an
728731
**Returns:**
729732
- Tasks grouped by status (Active → Backlog → Done → Cancelled)
730733
- Priority icons: 🔴 high, 🟡 medium, ⚪ low
734+
- Task summaries shown inline (one-sentence table-of-contents)
731735
- Tags displayed inline
732736

733737
**Examples:**
@@ -1045,7 +1049,7 @@ SQLite database at `.aidex/index.db`:
10451049
| `dependencies` | Linked projects |
10461050
| `project_files` | All files with type classification |
10471051
| `metadata` | Key-value store (session times, notes, etc.) |
1048-
| `tasks` | Project backlog tasks (priority, status, tags, timestamps) |
1052+
| `tasks` | Project backlog tasks (priority, status, tags, summary, timestamps) |
10491053
| `task_log` | Task history log (auto-logged status changes + manual notes) |
10501054

10511055
---

README.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -458,11 +458,17 @@ aidex_note({ path: ".", clear: true }) # Clear
458458
**Note History** (v1.10): Old notes are automatically archived when overwritten or cleared. Browse and search past notes:
459459

460460
```
461-
aidex_note({ path: ".", history: true }) # Browse archived notes
462-
aidex_note({ path: ".", search: "parser" }) # Search note history
461+
aidex_note({ path: ".", history: true }) # Browse archived notes (shows summaries)
462+
aidex_note({ path: ".", search: "parser" }) # Search note history (searches summaries too)
463463
aidex_note({ path: ".", history: true, limit: 5 }) # Last 5 archived notes
464464
```
465465

466+
**Note Summaries** (v1.15): Provide a `summary` when writing/clearing a note — the archived note gets this one-sentence description. History then shows summaries instead of truncated text:
467+
468+
```
469+
aidex_note({ path: ".", note: "New focus", summary: "Previous session: finished parser refactoring" })
470+
```
471+
466472
**Use cases:**
467473
- Before ending a session: *"Remember to test X next time"*
468474
- AI auto-reminder: Save what to verify after a restart
@@ -476,13 +482,14 @@ Notes are stored in the SQLite database (`.aidex/index.db`) and persist indefini
476482
Keep your project tasks right next to your code index - no Jira, no Trello, no context switching:
477483

478484
```
479-
aidex_task({ path: ".", action: "create", title: "Fix parser bug", priority: 1, tags: "bug" })
485+
aidex_task({ path: ".", action: "create", title: "Fix parser bug", priority: 1, tags: "bug", summary: "Parser crashes on nested generics in C#" })
480486
aidex_task({ path: ".", action: "update", id: 1, status: "done" })
481487
aidex_task({ path: ".", action: "log", id: 1, note: "Root cause: unbounded buffer" })
482488
aidex_tasks({ path: ".", status: "active" })
483489
```
484490

485491
**Features:**
492+
- **Summaries**: One-sentence table-of-contents per task — scan the backlog without reading full details
486493
- **Priorities**: 🔴 high, 🟡 medium, ⚪ low
487494
- **Statuses**: `backlog → active → done | cancelled`
488495
- **Tags**: Categorize tasks (`bug`, `feature`, `docs`, etc.)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "aidex-mcp",
3-
"version": "1.14.1",
3+
"version": "1.15.0",
44
"mcpName": "io.github.CSCSoftware/aidex",
55
"description": "MCP Server for persistent code indexing. Gives AI assistants (Claude, Gemini, Copilot, Cursor) instant access to your codebase. 50x less context than grep.",
66
"main": "build/index.js",

src/commands/note.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,13 @@ export interface NoteParams {
2626
history?: boolean; // If true, shows archived note history
2727
search?: string; // If provided, searches note history for this term
2828
limit?: number; // Max history entries to return (default 20)
29+
summary?: string; // One-sentence summary for the archived note (~150 chars)
2930
}
3031

3132
export interface NoteHistoryEntry {
3233
id: number;
3334
note: string;
35+
summary: string | null;
3436
created_at: number;
3537
}
3638

@@ -80,10 +82,18 @@ export function note(params: NoteParams): NoteResult {
8082
CREATE TABLE IF NOT EXISTS note_history (
8183
id INTEGER PRIMARY KEY AUTOINCREMENT,
8284
note TEXT NOT NULL,
85+
summary TEXT,
8386
created_at INTEGER NOT NULL
8487
);
8588
CREATE INDEX IF NOT EXISTS idx_note_history_created ON note_history(created_at);
8689
`);
90+
// Add summary column if missing (for existing DBs before v1.15)
91+
const hasSummary = db.getDb().prepare(
92+
"SELECT COUNT(*) as cnt FROM pragma_table_info('note_history') WHERE name = 'summary'"
93+
).get() as { cnt: number };
94+
if (hasSummary.cnt === 0) {
95+
db.getDb().exec('ALTER TABLE note_history ADD COLUMN summary TEXT');
96+
}
8797
}
8898

8999
// --- Search history ---
@@ -117,7 +127,7 @@ export function note(params: NoteParams): NoteResult {
117127
// Archive current note before clearing
118128
const existing = db.getMetadata(NOTE_KEY);
119129
if (existing) {
120-
db.archiveNote(existing);
130+
db.archiveNote(existing, params.summary);
121131
}
122132
db.deleteMetadata(NOTE_KEY);
123133
return {
@@ -140,7 +150,7 @@ export function note(params: NoteParams): NoteResult {
140150
// Overwrite: archive the old note first
141151
const existing = db.getMetadata(NOTE_KEY);
142152
if (existing) {
143-
db.archiveNote(existing);
153+
db.archiveNote(existing, params.summary);
144154
}
145155
}
146156

src/commands/task.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export interface TaskParams {
2323
id?: number;
2424
title?: string;
2525
description?: string;
26+
summary?: string;
2627
priority?: 1 | 2 | 3;
2728
status?: 'backlog' | 'active' | 'done' | 'cancelled';
2829
tags?: string;
@@ -62,6 +63,7 @@ CREATE TABLE IF NOT EXISTS tasks (
6263
id INTEGER PRIMARY KEY AUTOINCREMENT,
6364
title TEXT NOT NULL,
6465
description TEXT,
66+
summary TEXT,
6567
priority INTEGER NOT NULL DEFAULT 2 CHECK(priority IN (1, 2, 3)),
6668
status TEXT NOT NULL DEFAULT 'backlog' CHECK(status IN ('backlog', 'active', 'done', 'cancelled')),
6769
tags TEXT,
@@ -90,6 +92,7 @@ CREATE TABLE IF NOT EXISTS tasks_new (
9092
id INTEGER PRIMARY KEY AUTOINCREMENT,
9193
title TEXT NOT NULL,
9294
description TEXT,
95+
summary TEXT,
9396
priority INTEGER NOT NULL DEFAULT 2 CHECK(priority IN (1, 2, 3)),
9497
status TEXT NOT NULL DEFAULT 'backlog' CHECK(status IN ('backlog', 'active', 'done', 'cancelled')),
9598
tags TEXT,
@@ -99,7 +102,8 @@ CREATE TABLE IF NOT EXISTS tasks_new (
99102
updated_at INTEGER NOT NULL,
100103
completed_at INTEGER
101104
);
102-
INSERT INTO tasks_new SELECT * FROM tasks;
105+
INSERT INTO tasks_new (id, title, description, priority, status, tags, source, sort_order, created_at, updated_at, completed_at)
106+
SELECT id, title, description, priority, status, tags, source, sort_order, created_at, updated_at, completed_at FROM tasks;
103107
DROP TABLE tasks;
104108
ALTER TABLE tasks_new RENAME TO tasks;
105109
CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
@@ -115,6 +119,14 @@ function ensureTaskTables(db: AiDexDatabase): void {
115119
if (tableInfo?.sql && !tableInfo.sql.includes('cancelled')) {
116120
sqlite.exec(TASKS_MIGRATE_CANCELLED);
117121
}
122+
123+
// Add summary column if missing (for existing DBs before v1.15)
124+
const hasSummary = sqlite.prepare(
125+
"SELECT COUNT(*) as cnt FROM pragma_table_info('tasks') WHERE name = 'summary'"
126+
).get() as { cnt: number };
127+
if (hasSummary.cnt === 0) {
128+
sqlite.exec('ALTER TABLE tasks ADD COLUMN summary TEXT');
129+
}
118130
}
119131

120132
// ============================================================
@@ -146,6 +158,7 @@ export function task(params: TaskParams): TaskResult {
146158
const id = queries.insertTask(
147159
params.title,
148160
params.description ?? null,
161+
params.summary ?? null,
149162
params.priority ?? 2,
150163
params.status ?? 'backlog',
151164
params.tags ?? null,
@@ -177,6 +190,7 @@ export function task(params: TaskParams): TaskResult {
177190
const fields: Record<string, unknown> = {};
178191
if (params.title !== undefined) fields.title = params.title;
179192
if (params.description !== undefined) fields.description = params.description;
193+
if (params.summary !== undefined) fields.summary = params.summary;
180194
if (params.priority !== undefined) fields.priority = params.priority;
181195
if (params.status !== undefined) fields.status = params.status;
182196
if (params.tags !== undefined) fields.tags = params.tags;

src/db/database.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,28 +79,28 @@ export class AiDexDatabase {
7979
/**
8080
* Archive a note to the history table (called before overwriting)
8181
*/
82-
archiveNote(note: string): void {
82+
archiveNote(note: string, summary?: string | null): void {
8383
this.db.prepare(
84-
'INSERT INTO note_history (note, created_at) VALUES (?, ?)'
85-
).run(note, Date.now());
84+
'INSERT INTO note_history (note, summary, created_at) VALUES (?, ?, ?)'
85+
).run(note, summary ?? null, Date.now());
8686
}
8787

8888
/**
8989
* Get note history entries, newest first
9090
*/
91-
getNoteHistory(limit = 50): Array<{ id: number; note: string; created_at: number }> {
91+
getNoteHistory(limit = 50): Array<{ id: number; note: string; summary: string | null; created_at: number }> {
9292
return this.db.prepare(
93-
'SELECT id, note, created_at FROM note_history ORDER BY created_at DESC LIMIT ?'
94-
).all(limit) as Array<{ id: number; note: string; created_at: number }>;
93+
'SELECT id, note, summary, created_at FROM note_history ORDER BY created_at DESC LIMIT ?'
94+
).all(limit) as Array<{ id: number; note: string; summary: string | null; created_at: number }>;
9595
}
9696

9797
/**
9898
* Search note history by text (case-insensitive LIKE)
9999
*/
100-
searchNoteHistory(query: string, limit = 20): Array<{ id: number; note: string; created_at: number }> {
100+
searchNoteHistory(query: string, limit = 20): Array<{ id: number; note: string; summary: string | null; created_at: number }> {
101101
return this.db.prepare(
102-
'SELECT id, note, created_at FROM note_history WHERE note LIKE ? ORDER BY created_at DESC LIMIT ?'
103-
).all(`%${query}%`, limit) as Array<{ id: number; note: string; created_at: number }>;
102+
'SELECT id, note, summary, created_at FROM note_history WHERE note LIKE ? OR summary LIKE ? ORDER BY created_at DESC LIMIT ?'
103+
).all(`%${query}%`, `%${query}%`, limit) as Array<{ id: number; note: string; summary: string | null; created_at: number }>;
104104
}
105105

106106
/**

src/db/queries.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export interface TaskRow {
8484
id: number;
8585
title: string;
8686
description: string | null;
87+
summary: string | null;
8788
priority: 1 | 2 | 3;
8889
status: 'backlog' | 'active' | 'done' | 'cancelled';
8990
tags: string | null;
@@ -546,22 +547,23 @@ export class Queries {
546547
insertTask(
547548
title: string,
548549
description: string | null,
550+
summary: string | null,
549551
priority: 1 | 2 | 3,
550552
status: 'backlog' | 'active' | 'done' | 'cancelled',
551553
tags: string | null,
552554
source: string | null,
553555
sortOrder: number
554556
): number {
555557
this._insertTask ??= this.db.prepare(
556-
'INSERT INTO tasks (title, description, priority, status, tags, source, sort_order, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)'
558+
'INSERT INTO tasks (title, description, summary, priority, status, tags, source, sort_order, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
557559
);
558560
const now = Date.now();
559-
const result = this._insertTask.run(title, description, priority, status, tags, source, sortOrder, now, now);
561+
const result = this._insertTask.run(title, description, summary, priority, status, tags, source, sortOrder, now, now);
560562
return result.lastInsertRowid as number;
561563
}
562564

563-
updateTask(id: number, fields: Partial<Pick<TaskRow, 'title' | 'description' | 'priority' | 'status' | 'tags' | 'source' | 'sort_order'>>): boolean {
564-
const ALLOWED_FIELDS = new Set(['title', 'description', 'status', 'priority', 'tags', 'source', 'sort_order', 'completed_at']);
565+
updateTask(id: number, fields: Partial<Pick<TaskRow, 'title' | 'description' | 'summary' | 'priority' | 'status' | 'tags' | 'source' | 'sort_order'>>): boolean {
566+
const ALLOWED_FIELDS = new Set(['title', 'description', 'summary', 'status', 'priority', 'tags', 'source', 'sort_order', 'completed_at']);
565567
const sets: string[] = [];
566568
const values: unknown[] = [];
567569
for (const [key, value] of Object.entries(fields)) {

src/db/schema.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ CREATE TABLE IF NOT EXISTS tasks (
134134
id INTEGER PRIMARY KEY AUTOINCREMENT,
135135
title TEXT NOT NULL,
136136
description TEXT,
137+
summary TEXT,
137138
priority INTEGER NOT NULL DEFAULT 2 CHECK(priority IN (1, 2, 3)),
138139
status TEXT NOT NULL DEFAULT 'backlog' CHECK(status IN ('backlog', 'active', 'done', 'cancelled')),
139140
tags TEXT,
@@ -166,6 +167,7 @@ CREATE INDEX IF NOT EXISTS idx_task_log_task ON task_log(task_id);
166167
CREATE TABLE IF NOT EXISTS note_history (
167168
id INTEGER PRIMARY KEY AUTOINCREMENT,
168169
note TEXT NOT NULL,
170+
summary TEXT,
169171
created_at INTEGER NOT NULL
170172
);
171173

0 commit comments

Comments
 (0)