opencodekit 0.20.2 → 0.20.4
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.
- package/dist/index.js +1 -1
- package/dist/template/.opencode/agent/build.md +4 -0
- package/dist/template/.opencode/agent/explore.md +4 -0
- package/dist/template/.opencode/agent/general.md +4 -0
- package/dist/template/.opencode/agent/plan.md +4 -0
- package/dist/template/.opencode/agent/review.md +4 -0
- package/dist/template/.opencode/agent/scout.md +4 -0
- package/dist/template/.opencode/command/create.md +119 -25
- package/dist/template/.opencode/command/design.md +1 -2
- package/dist/template/.opencode/command/health.md +234 -0
- package/dist/template/.opencode/command/init-user.md +15 -0
- package/dist/template/.opencode/command/plan.md +3 -4
- package/dist/template/.opencode/command/pr.md +13 -0
- package/dist/template/.opencode/command/research.md +15 -3
- package/dist/template/.opencode/command/review-codebase.md +11 -1
- package/dist/template/.opencode/command/ship.md +72 -8
- package/dist/template/.opencode/command/status.md +1 -1
- package/dist/template/.opencode/command/ui-review.md +0 -1
- package/dist/template/.opencode/command/ui-slop-check.md +1 -1
- package/dist/template/.opencode/command/verify.md +11 -1
- package/dist/template/.opencode/memory.db +0 -0
- package/dist/template/.opencode/memory.db-shm +0 -0
- package/dist/template/.opencode/memory.db-wal +0 -0
- package/dist/template/.opencode/opencode.json +1678 -1677
- package/dist/template/.opencode/plugin/README.md +1 -1
- package/dist/template/.opencode/plugin/lib/compact.ts +194 -0
- package/dist/template/.opencode/plugin/lib/compile.ts +253 -0
- package/dist/template/.opencode/plugin/lib/db/graph.ts +253 -0
- package/dist/template/.opencode/plugin/lib/db/observations.ts +8 -3
- package/dist/template/.opencode/plugin/lib/db/schema.ts +96 -5
- package/dist/template/.opencode/plugin/lib/db/types.ts +73 -0
- package/dist/template/.opencode/plugin/lib/index-generator.ts +170 -0
- package/dist/template/.opencode/plugin/lib/lint.ts +359 -0
- package/dist/template/.opencode/plugin/lib/memory-admin-tools.ts +78 -4
- package/dist/template/.opencode/plugin/lib/memory-db.ts +19 -1
- package/dist/template/.opencode/plugin/lib/memory-helpers.ts +30 -0
- package/dist/template/.opencode/plugin/lib/memory-hooks.ts +10 -0
- package/dist/template/.opencode/plugin/lib/memory-tools.ts +167 -2
- package/dist/template/.opencode/plugin/lib/operation-log.ts +109 -0
- package/dist/template/.opencode/plugin/lib/validate.ts +243 -0
- package/dist/template/.opencode/plugin/memory.ts +2 -1
- package/dist/template/.opencode/skill/design-taste-frontend/SKILL.md +13 -1
- package/dist/template/.opencode/skill/figma-go/SKILL.md +1 -1
- package/dist/template/.opencode/skill/full-output-enforcement/SKILL.md +13 -0
- package/dist/template/.opencode/skill/high-end-visual-design/SKILL.md +13 -0
- package/dist/template/.opencode/skill/industrial-brutalist-ui/SKILL.md +13 -0
- package/dist/template/.opencode/skill/memory-system/SKILL.md +65 -1
- package/dist/template/.opencode/skill/minimalist-ui/SKILL.md +13 -0
- package/dist/template/.opencode/skill/redesign-existing-projects/SKILL.md +13 -0
- package/dist/template/.opencode/skill/requesting-code-review/SKILL.md +48 -2
- package/dist/template/.opencode/skill/requesting-code-review/references/specialist-profiles.md +108 -0
- package/dist/template/.opencode/skill/skill-creator/SKILL.md +25 -0
- package/dist/template/.opencode/skill/stitch-design-taste/SKILL.md +13 -0
- package/dist/template/.opencode/skill/verification-before-completion/SKILL.md +46 -0
- package/package.json +1 -1
- package/dist/template/.opencode/agent/runner.md +0 -79
- package/dist/template/.opencode/command/start.md +0 -156
|
@@ -28,10 +28,11 @@ export function storeObservation(input: ObservationInput): number {
|
|
|
28
28
|
.query(
|
|
29
29
|
`
|
|
30
30
|
INSERT INTO observations (
|
|
31
|
-
type, title, subtitle, facts, narrative, concepts,
|
|
31
|
+
type, title, subtitle, facts, narrative, raw_source, concepts,
|
|
32
32
|
files_read, files_modified, confidence, bead_id,
|
|
33
|
-
supersedes, markdown_file, source,
|
|
34
|
-
|
|
33
|
+
supersedes, markdown_file, source, wing, hall, room,
|
|
34
|
+
created_at, created_at_epoch
|
|
35
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
35
36
|
`,
|
|
36
37
|
)
|
|
37
38
|
.run(
|
|
@@ -40,6 +41,7 @@ export function storeObservation(input: ObservationInput): number {
|
|
|
40
41
|
input.subtitle ?? null,
|
|
41
42
|
input.facts ? JSON.stringify(input.facts) : null,
|
|
42
43
|
input.narrative ?? null,
|
|
44
|
+
input.raw_source ?? null,
|
|
43
45
|
input.concepts ? JSON.stringify(input.concepts) : null,
|
|
44
46
|
input.files_read ? JSON.stringify(input.files_read) : null,
|
|
45
47
|
input.files_modified ? JSON.stringify(input.files_modified) : null,
|
|
@@ -48,6 +50,9 @@ export function storeObservation(input: ObservationInput): number {
|
|
|
48
50
|
input.supersedes ?? null,
|
|
49
51
|
input.markdown_file ?? null,
|
|
50
52
|
input.source ?? "manual",
|
|
53
|
+
input.wing ?? null,
|
|
54
|
+
input.hall ?? null,
|
|
55
|
+
input.room ?? null,
|
|
51
56
|
now.toISOString(),
|
|
52
57
|
now.getTime(),
|
|
53
58
|
);
|
|
@@ -28,10 +28,10 @@ function logRecovery(message: string): void {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
// ============================================================================
|
|
31
|
-
// Schema v2
|
|
31
|
+
// Schema v3 (v2 + navigation, entity graph, raw source, chunk type)
|
|
32
32
|
// ============================================================================
|
|
33
33
|
|
|
34
|
-
const SCHEMA_VERSION =
|
|
34
|
+
const SCHEMA_VERSION = 3;
|
|
35
35
|
|
|
36
36
|
const SCHEMA_SQL = `
|
|
37
37
|
-- Schema versioning for migrations
|
|
@@ -41,7 +41,7 @@ CREATE TABLE IF NOT EXISTS schema_versions (
|
|
|
41
41
|
applied_at TEXT NOT NULL
|
|
42
42
|
);
|
|
43
43
|
|
|
44
|
-
-- Observations table (
|
|
44
|
+
-- Observations table (v3: added raw_source, wing, hall, room)
|
|
45
45
|
CREATE TABLE IF NOT EXISTS observations (
|
|
46
46
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
47
47
|
type TEXT NOT NULL CHECK(type IN ('decision','bugfix','feature','pattern','discovery','learning','warning')),
|
|
@@ -49,6 +49,7 @@ CREATE TABLE IF NOT EXISTS observations (
|
|
|
49
49
|
subtitle TEXT,
|
|
50
50
|
facts TEXT,
|
|
51
51
|
narrative TEXT,
|
|
52
|
+
raw_source TEXT,
|
|
52
53
|
concepts TEXT,
|
|
53
54
|
files_read TEXT,
|
|
54
55
|
files_modified TEXT,
|
|
@@ -59,6 +60,9 @@ CREATE TABLE IF NOT EXISTS observations (
|
|
|
59
60
|
valid_until TEXT,
|
|
60
61
|
markdown_file TEXT,
|
|
61
62
|
source TEXT CHECK(source IN ('manual','curator','imported')) DEFAULT 'manual',
|
|
63
|
+
wing TEXT,
|
|
64
|
+
hall TEXT CHECK(hall IS NULL OR hall IN ('facts','events','discoveries','preferences','advice')),
|
|
65
|
+
room TEXT,
|
|
62
66
|
created_at TEXT NOT NULL,
|
|
63
67
|
created_at_epoch INTEGER NOT NULL,
|
|
64
68
|
updated_at TEXT,
|
|
@@ -84,6 +88,10 @@ CREATE INDEX IF NOT EXISTS idx_observations_created ON observations(created_at_e
|
|
|
84
88
|
CREATE INDEX IF NOT EXISTS idx_observations_bead_id ON observations(bead_id);
|
|
85
89
|
CREATE INDEX IF NOT EXISTS idx_observations_superseded ON observations(superseded_by) WHERE superseded_by IS NOT NULL;
|
|
86
90
|
CREATE INDEX IF NOT EXISTS idx_observations_source ON observations(source);
|
|
91
|
+
CREATE INDEX IF NOT EXISTS idx_observations_wing ON observations(wing) WHERE wing IS NOT NULL;
|
|
92
|
+
CREATE INDEX IF NOT EXISTS idx_observations_hall ON observations(hall) WHERE hall IS NOT NULL;
|
|
93
|
+
CREATE INDEX IF NOT EXISTS idx_observations_room ON observations(room) WHERE room IS NOT NULL;
|
|
94
|
+
CREATE INDEX IF NOT EXISTS idx_observations_navigation ON observations(wing, hall, room) WHERE wing IS NOT NULL;
|
|
87
95
|
|
|
88
96
|
-- Memory files table
|
|
89
97
|
CREATE TABLE IF NOT EXISTS memory_files (
|
|
@@ -102,7 +110,7 @@ CREATE INDEX IF NOT EXISTS idx_memory_files_path ON memory_files(file_path);
|
|
|
102
110
|
|
|
103
111
|
|
|
104
112
|
|
|
105
|
-
-- Temporal messages table (
|
|
113
|
+
-- Temporal messages table (v3: added chunk_type)
|
|
106
114
|
CREATE TABLE IF NOT EXISTS temporal_messages (
|
|
107
115
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
108
116
|
session_id TEXT NOT NULL,
|
|
@@ -112,6 +120,7 @@ CREATE TABLE IF NOT EXISTS temporal_messages (
|
|
|
112
120
|
token_estimate INTEGER NOT NULL DEFAULT 0,
|
|
113
121
|
time_created INTEGER NOT NULL,
|
|
114
122
|
distillation_id INTEGER,
|
|
123
|
+
chunk_type TEXT,
|
|
115
124
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
116
125
|
FOREIGN KEY(distillation_id) REFERENCES distillations(id) ON DELETE SET NULL
|
|
117
126
|
);
|
|
@@ -139,6 +148,27 @@ CREATE TABLE IF NOT EXISTS distillations (
|
|
|
139
148
|
CREATE INDEX IF NOT EXISTS idx_distillations_session ON distillations(session_id, time_created DESC);
|
|
140
149
|
CREATE INDEX IF NOT EXISTS idx_distillations_time ON distillations(time_created DESC);
|
|
141
150
|
|
|
151
|
+
-- Entity triples table (v3: temporal knowledge graph)
|
|
152
|
+
CREATE TABLE IF NOT EXISTS entity_triples (
|
|
153
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
154
|
+
subject TEXT NOT NULL,
|
|
155
|
+
predicate TEXT NOT NULL,
|
|
156
|
+
object TEXT NOT NULL,
|
|
157
|
+
valid_from TEXT NOT NULL,
|
|
158
|
+
valid_to TEXT,
|
|
159
|
+
confidence REAL NOT NULL DEFAULT 1.0,
|
|
160
|
+
source_observation_id INTEGER,
|
|
161
|
+
created_at TEXT NOT NULL,
|
|
162
|
+
created_at_epoch INTEGER NOT NULL,
|
|
163
|
+
FOREIGN KEY(source_observation_id) REFERENCES observations(id) ON DELETE SET NULL
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
CREATE INDEX IF NOT EXISTS idx_entity_subject ON entity_triples(subject);
|
|
167
|
+
CREATE INDEX IF NOT EXISTS idx_entity_object ON entity_triples(object);
|
|
168
|
+
CREATE INDEX IF NOT EXISTS idx_entity_predicate ON entity_triples(predicate);
|
|
169
|
+
CREATE INDEX IF NOT EXISTS idx_entity_valid ON entity_triples(valid_from, valid_to);
|
|
170
|
+
CREATE INDEX IF NOT EXISTS idx_entity_active ON entity_triples(subject, valid_to) WHERE valid_to IS NULL;
|
|
171
|
+
|
|
142
172
|
-- FTS5 for distillations (v2)
|
|
143
173
|
CREATE VIRTUAL TABLE IF NOT EXISTS distillations_fts USING fts5(
|
|
144
174
|
content,
|
|
@@ -243,6 +273,47 @@ CREATE INDEX IF NOT EXISTS idx_temporal_undistilled ON temporal_messages(session
|
|
|
243
273
|
CREATE INDEX IF NOT EXISTS idx_temporal_time ON temporal_messages(time_created DESC);
|
|
244
274
|
`;
|
|
245
275
|
|
|
276
|
+
// Migration from v2 to v3
|
|
277
|
+
const MIGRATION_V2_TO_V3 = `
|
|
278
|
+
-- Add raw_source column to observations
|
|
279
|
+
ALTER TABLE observations ADD COLUMN raw_source TEXT;
|
|
280
|
+
|
|
281
|
+
-- Add navigation columns to observations
|
|
282
|
+
ALTER TABLE observations ADD COLUMN wing TEXT;
|
|
283
|
+
ALTER TABLE observations ADD COLUMN hall TEXT CHECK(hall IS NULL OR hall IN ('facts','events','discoveries','preferences','advice'));
|
|
284
|
+
ALTER TABLE observations ADD COLUMN room TEXT;
|
|
285
|
+
|
|
286
|
+
-- Navigation indexes
|
|
287
|
+
CREATE INDEX IF NOT EXISTS idx_observations_wing ON observations(wing) WHERE wing IS NOT NULL;
|
|
288
|
+
CREATE INDEX IF NOT EXISTS idx_observations_hall ON observations(hall) WHERE hall IS NOT NULL;
|
|
289
|
+
CREATE INDEX IF NOT EXISTS idx_observations_room ON observations(room) WHERE room IS NOT NULL;
|
|
290
|
+
CREATE INDEX IF NOT EXISTS idx_observations_navigation ON observations(wing, hall, room) WHERE wing IS NOT NULL;
|
|
291
|
+
|
|
292
|
+
-- Add chunk_type to temporal_messages
|
|
293
|
+
ALTER TABLE temporal_messages ADD COLUMN chunk_type TEXT;
|
|
294
|
+
|
|
295
|
+
-- Entity triples table (temporal knowledge graph)
|
|
296
|
+
CREATE TABLE IF NOT EXISTS entity_triples (
|
|
297
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
298
|
+
subject TEXT NOT NULL,
|
|
299
|
+
predicate TEXT NOT NULL,
|
|
300
|
+
object TEXT NOT NULL,
|
|
301
|
+
valid_from TEXT NOT NULL,
|
|
302
|
+
valid_to TEXT,
|
|
303
|
+
confidence REAL NOT NULL DEFAULT 1.0,
|
|
304
|
+
source_observation_id INTEGER,
|
|
305
|
+
created_at TEXT NOT NULL,
|
|
306
|
+
created_at_epoch INTEGER NOT NULL,
|
|
307
|
+
FOREIGN KEY(source_observation_id) REFERENCES observations(id) ON DELETE SET NULL
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
CREATE INDEX IF NOT EXISTS idx_entity_subject ON entity_triples(subject);
|
|
311
|
+
CREATE INDEX IF NOT EXISTS idx_entity_object ON entity_triples(object);
|
|
312
|
+
CREATE INDEX IF NOT EXISTS idx_entity_predicate ON entity_triples(predicate);
|
|
313
|
+
CREATE INDEX IF NOT EXISTS idx_entity_valid ON entity_triples(valid_from, valid_to);
|
|
314
|
+
CREATE INDEX IF NOT EXISTS idx_entity_active ON entity_triples(subject, valid_to) WHERE valid_to IS NULL;
|
|
315
|
+
`;
|
|
316
|
+
|
|
246
317
|
// ============================================================================
|
|
247
318
|
// Database Manager
|
|
248
319
|
// ============================================================================
|
|
@@ -429,7 +500,7 @@ function initializeSchema(db: Database): void {
|
|
|
429
500
|
}
|
|
430
501
|
|
|
431
502
|
if (currentVersion === 0) {
|
|
432
|
-
// Fresh install — run full
|
|
503
|
+
// Fresh install — run full v3 schema
|
|
433
504
|
db.exec(SCHEMA_SQL);
|
|
434
505
|
|
|
435
506
|
// Run FTS triggers
|
|
@@ -443,6 +514,9 @@ function initializeSchema(db: Database): void {
|
|
|
443
514
|
if (currentVersion < 2) {
|
|
444
515
|
migrateV1ToV2(db);
|
|
445
516
|
}
|
|
517
|
+
if (currentVersion < 3) {
|
|
518
|
+
migrateV2ToV3(db);
|
|
519
|
+
}
|
|
446
520
|
}
|
|
447
521
|
|
|
448
522
|
// Record schema version
|
|
@@ -501,3 +575,20 @@ function migrateV1ToV2(db: Database): void {
|
|
|
501
575
|
// Triggers may already exist
|
|
502
576
|
}
|
|
503
577
|
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Migrate from schema v2 to v3.
|
|
581
|
+
* Adds: raw_source, wing/hall/room navigation, chunk_type, entity_triples table.
|
|
582
|
+
*/
|
|
583
|
+
function migrateV2ToV3(db: Database): void {
|
|
584
|
+
for (const stmt of MIGRATION_V2_TO_V3.split(";")) {
|
|
585
|
+
const trimmed = stmt.trim();
|
|
586
|
+
if (trimmed) {
|
|
587
|
+
try {
|
|
588
|
+
db.run(trimmed);
|
|
589
|
+
} catch {
|
|
590
|
+
// Statement may fail if already applied (e.g. column exists)
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
@@ -46,6 +46,69 @@ export const MEMORY_CONFIG = {
|
|
|
46
46
|
},
|
|
47
47
|
} as const;
|
|
48
48
|
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// Navigation Types (v3: structured navigation)
|
|
51
|
+
// ============================================================================
|
|
52
|
+
|
|
53
|
+
export type HallType = "facts" | "events" | "discoveries" | "preferences" | "advice";
|
|
54
|
+
|
|
55
|
+
export const VALID_HALLS: HallType[] = ["facts", "events", "discoveries", "preferences", "advice"];
|
|
56
|
+
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// Entity Graph Types (v3: temporal knowledge graph)
|
|
59
|
+
// ============================================================================
|
|
60
|
+
|
|
61
|
+
export interface EntityTripleInput {
|
|
62
|
+
subject: string;
|
|
63
|
+
predicate: string;
|
|
64
|
+
object: string;
|
|
65
|
+
valid_from?: string; // ISO date or epoch
|
|
66
|
+
valid_to?: string | null;
|
|
67
|
+
confidence?: number; // 0.0–1.0
|
|
68
|
+
source_observation_id?: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface EntityTripleRow {
|
|
72
|
+
id: number;
|
|
73
|
+
subject: string;
|
|
74
|
+
predicate: string;
|
|
75
|
+
object: string;
|
|
76
|
+
valid_from: string;
|
|
77
|
+
valid_to: string | null;
|
|
78
|
+
confidence: number;
|
|
79
|
+
source_observation_id: number | null;
|
|
80
|
+
created_at: string;
|
|
81
|
+
created_at_epoch: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface EntityQueryResult {
|
|
85
|
+
id: number;
|
|
86
|
+
subject: string;
|
|
87
|
+
predicate: string;
|
|
88
|
+
object: string;
|
|
89
|
+
valid_from: string;
|
|
90
|
+
valid_to: string | null;
|
|
91
|
+
confidence: number;
|
|
92
|
+
is_active: boolean;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ============================================================================
|
|
96
|
+
// Compact Format Types (v3: AAAK-inspired compression)
|
|
97
|
+
// ============================================================================
|
|
98
|
+
|
|
99
|
+
export interface CompactEntry {
|
|
100
|
+
code: string; // 3-letter entity code
|
|
101
|
+
full: string; // Full name
|
|
102
|
+
role?: string; // Role or type
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface CompactResult {
|
|
106
|
+
compressed: string;
|
|
107
|
+
token_estimate: number;
|
|
108
|
+
original_tokens: number;
|
|
109
|
+
compression_ratio: number;
|
|
110
|
+
}
|
|
111
|
+
|
|
49
112
|
// ============================================================================
|
|
50
113
|
// Observation Types
|
|
51
114
|
// ============================================================================
|
|
@@ -68,6 +131,7 @@ export interface ObservationRow {
|
|
|
68
131
|
subtitle: string | null;
|
|
69
132
|
facts: string | null; // JSON array
|
|
70
133
|
narrative: string | null;
|
|
134
|
+
raw_source: string | null; // v3: verbatim source text
|
|
71
135
|
concepts: string | null; // JSON array
|
|
72
136
|
files_read: string | null; // JSON array
|
|
73
137
|
files_modified: string | null; // JSON array
|
|
@@ -78,6 +142,9 @@ export interface ObservationRow {
|
|
|
78
142
|
valid_until: string | null;
|
|
79
143
|
markdown_file: string | null;
|
|
80
144
|
source: ObservationSource;
|
|
145
|
+
wing: string | null; // v3: navigation wing (project/person)
|
|
146
|
+
hall: HallType | null; // v3: navigation hall (facts/events/...)
|
|
147
|
+
room: string | null; // v3: navigation room (topic)
|
|
81
148
|
created_at: string;
|
|
82
149
|
created_at_epoch: number;
|
|
83
150
|
updated_at: string | null;
|
|
@@ -89,6 +156,7 @@ export interface ObservationInput {
|
|
|
89
156
|
subtitle?: string;
|
|
90
157
|
facts?: string[];
|
|
91
158
|
narrative?: string;
|
|
159
|
+
raw_source?: string; // v3: verbatim source text
|
|
92
160
|
concepts?: string[];
|
|
93
161
|
files_read?: string[];
|
|
94
162
|
files_modified?: string[];
|
|
@@ -97,6 +165,9 @@ export interface ObservationInput {
|
|
|
97
165
|
supersedes?: number;
|
|
98
166
|
markdown_file?: string;
|
|
99
167
|
source?: ObservationSource;
|
|
168
|
+
wing?: string; // v3: navigation wing
|
|
169
|
+
hall?: HallType; // v3: navigation hall
|
|
170
|
+
room?: string; // v3: navigation room
|
|
100
171
|
}
|
|
101
172
|
|
|
102
173
|
export interface SearchIndexResult {
|
|
@@ -136,6 +207,7 @@ export interface TemporalMessageRow {
|
|
|
136
207
|
token_estimate: number;
|
|
137
208
|
time_created: number;
|
|
138
209
|
distillation_id: number | null;
|
|
210
|
+
chunk_type: string | null; // v3: exchange-pair | paragraph | sliding-window
|
|
139
211
|
created_at: string;
|
|
140
212
|
}
|
|
141
213
|
|
|
@@ -146,6 +218,7 @@ export interface TemporalMessageInput {
|
|
|
146
218
|
content: string;
|
|
147
219
|
token_estimate: number;
|
|
148
220
|
time_created: number;
|
|
221
|
+
chunk_type?: string; // v3: exchange-pair | paragraph | sliding-window
|
|
149
222
|
}
|
|
150
223
|
|
|
151
224
|
// ============================================================================
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Index Generator — Auto-Generated Knowledge Catalog
|
|
3
|
+
*
|
|
4
|
+
* Inspired by Karpathy's LLM Wiki index.md:
|
|
5
|
+
* generates a structured catalog of all observations, grouped by type,
|
|
6
|
+
* with cross-references and concept clusters.
|
|
7
|
+
*
|
|
8
|
+
* Stored in memory_files as "index" for injection or on-demand reading.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { upsertMemoryFile } from "./db/maintenance.js";
|
|
12
|
+
import type { ObservationRow } from "./db/types.js";
|
|
13
|
+
import { getMemoryDB } from "./memory-db.js";
|
|
14
|
+
import { TYPE_ICONS, parseConcepts } from "./memory-helpers.js";
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Types
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
export interface IndexEntry {
|
|
21
|
+
id: number;
|
|
22
|
+
type: string;
|
|
23
|
+
title: string;
|
|
24
|
+
concepts: string[];
|
|
25
|
+
created_at: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface IndexResult {
|
|
29
|
+
entryCount: number;
|
|
30
|
+
conceptCount: number;
|
|
31
|
+
content: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// Index Generation
|
|
36
|
+
// ============================================================================
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Generate a comprehensive index of all active observations.
|
|
40
|
+
* Groups by type, lists concept clusters, and writes to memory_files.
|
|
41
|
+
*/
|
|
42
|
+
export function generateMemoryIndex(): IndexResult {
|
|
43
|
+
const db = getMemoryDB();
|
|
44
|
+
|
|
45
|
+
const observations = db
|
|
46
|
+
.query(
|
|
47
|
+
`SELECT id, type, title, concepts, created_at FROM observations
|
|
48
|
+
WHERE superseded_by IS NULL
|
|
49
|
+
ORDER BY type, created_at_epoch DESC`,
|
|
50
|
+
)
|
|
51
|
+
.all() as Pick<
|
|
52
|
+
ObservationRow,
|
|
53
|
+
"id" | "type" | "title" | "concepts" | "created_at"
|
|
54
|
+
>[];
|
|
55
|
+
|
|
56
|
+
// Group by type
|
|
57
|
+
const byType = new Map<string, IndexEntry[]>();
|
|
58
|
+
const allConcepts = new Map<string, number[]>();
|
|
59
|
+
|
|
60
|
+
for (const obs of observations) {
|
|
61
|
+
const entry: IndexEntry = {
|
|
62
|
+
id: obs.id,
|
|
63
|
+
type: obs.type,
|
|
64
|
+
title: obs.title,
|
|
65
|
+
concepts: parseConcepts(obs.concepts),
|
|
66
|
+
created_at: obs.created_at,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const group = byType.get(obs.type) ?? [];
|
|
70
|
+
group.push(entry);
|
|
71
|
+
byType.set(obs.type, group);
|
|
72
|
+
|
|
73
|
+
for (const concept of entry.concepts) {
|
|
74
|
+
const ids = allConcepts.get(concept) ?? [];
|
|
75
|
+
ids.push(obs.id);
|
|
76
|
+
allConcepts.set(concept, ids);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Build markdown
|
|
81
|
+
const lines: string[] = [];
|
|
82
|
+
lines.push("# Memory Index");
|
|
83
|
+
lines.push("");
|
|
84
|
+
lines.push(
|
|
85
|
+
`> Auto-generated catalog of ${observations.length} active observations.`,
|
|
86
|
+
);
|
|
87
|
+
lines.push(`> Last updated: ${new Date().toISOString().slice(0, 19)}`);
|
|
88
|
+
lines.push("");
|
|
89
|
+
|
|
90
|
+
// Summary table
|
|
91
|
+
lines.push("## Summary");
|
|
92
|
+
lines.push("");
|
|
93
|
+
lines.push("| Type | Count |");
|
|
94
|
+
lines.push("|------|-------|");
|
|
95
|
+
for (const [type, entries] of byType) {
|
|
96
|
+
const icon = TYPE_ICONS[type] ?? "📌";
|
|
97
|
+
lines.push(`| ${icon} ${type} | ${entries.length} |`);
|
|
98
|
+
}
|
|
99
|
+
lines.push(`| **Total** | **${observations.length}** |`);
|
|
100
|
+
lines.push("");
|
|
101
|
+
|
|
102
|
+
// By type
|
|
103
|
+
for (const [type, entries] of byType) {
|
|
104
|
+
const icon = TYPE_ICONS[type] ?? "📌";
|
|
105
|
+
lines.push(
|
|
106
|
+
`## ${icon} ${type.charAt(0).toUpperCase() + type.slice(1)} (${entries.length})`,
|
|
107
|
+
);
|
|
108
|
+
lines.push("");
|
|
109
|
+
for (const entry of entries) {
|
|
110
|
+
const concepts =
|
|
111
|
+
entry.concepts.length > 0 ? ` [${entry.concepts.join(", ")}]` : "";
|
|
112
|
+
lines.push(
|
|
113
|
+
`- **#${entry.id}** ${entry.title}${concepts} _(${entry.created_at.slice(0, 10)})_`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
lines.push("");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Concept clusters
|
|
120
|
+
const significantConcepts = [...allConcepts.entries()]
|
|
121
|
+
.filter(([, ids]) => ids.length >= 2)
|
|
122
|
+
.sort((a, b) => b[1].length - a[1].length);
|
|
123
|
+
|
|
124
|
+
if (significantConcepts.length > 0) {
|
|
125
|
+
lines.push("## Concept Clusters");
|
|
126
|
+
lines.push("");
|
|
127
|
+
lines.push("Concepts appearing in 2+ observations:");
|
|
128
|
+
lines.push("");
|
|
129
|
+
for (const [concept, ids] of significantConcepts.slice(0, 30)) {
|
|
130
|
+
lines.push(
|
|
131
|
+
`- **${concept}** (${ids.length}): ${ids.map((id) => `#${id}`).join(", ")}`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
lines.push("");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Orphan concepts
|
|
138
|
+
const orphanConcepts = [...allConcepts.entries()].filter(
|
|
139
|
+
([, ids]) => ids.length === 1,
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
if (orphanConcepts.length > 0) {
|
|
143
|
+
lines.push("## Orphan Concepts");
|
|
144
|
+
lines.push("");
|
|
145
|
+
lines.push(
|
|
146
|
+
`${orphanConcepts.length} concepts appear in only 1 observation:`,
|
|
147
|
+
);
|
|
148
|
+
lines.push("");
|
|
149
|
+
const orphanList = orphanConcepts
|
|
150
|
+
.slice(0, 20)
|
|
151
|
+
.map(([concept, ids]) => `${concept} (#${ids[0]})`);
|
|
152
|
+
lines.push(orphanList.join(", "));
|
|
153
|
+
if (orphanConcepts.length > 20) {
|
|
154
|
+
lines.push(`... and ${orphanConcepts.length - 20} more`);
|
|
155
|
+
}
|
|
156
|
+
lines.push("");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const content = lines.join("\n");
|
|
160
|
+
|
|
161
|
+
// Store in memory_files
|
|
162
|
+
upsertMemoryFile("index", content, "replace");
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
entryCount: observations.length,
|
|
166
|
+
conceptCount: allConcepts.size,
|
|
167
|
+
content,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|