kiro-memory 1.0.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.
@@ -0,0 +1,1259 @@
1
+ #!/usr/bin/env node
2
+ #!/usr/bin/env node
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
6
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
7
+ }) : x)(function(x) {
8
+ if (typeof require !== "undefined") return require.apply(this, arguments);
9
+ throw Error('Dynamic require of "' + x + '" is not supported');
10
+ });
11
+ var __esm = (fn, res) => function __init() {
12
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
+ };
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+
19
+ // src/services/sqlite/Sessions.ts
20
+ var Sessions_exports = {};
21
+ __export(Sessions_exports, {
22
+ completeSession: () => completeSession,
23
+ createSession: () => createSession,
24
+ failSession: () => failSession,
25
+ getActiveSessions: () => getActiveSessions,
26
+ getSessionByContentId: () => getSessionByContentId,
27
+ getSessionById: () => getSessionById,
28
+ getSessionsByProject: () => getSessionsByProject,
29
+ updateSessionMemoryId: () => updateSessionMemoryId
30
+ });
31
+ function createSession(db, contentSessionId, project, userPrompt) {
32
+ const now = /* @__PURE__ */ new Date();
33
+ const result = db.run(
34
+ `INSERT INTO sessions (content_session_id, project, user_prompt, status, started_at, started_at_epoch)
35
+ VALUES (?, ?, ?, 'active', ?, ?)`,
36
+ [contentSessionId, project, userPrompt, now.toISOString(), now.getTime()]
37
+ );
38
+ return Number(result.lastInsertRowid);
39
+ }
40
+ function getSessionByContentId(db, contentSessionId) {
41
+ const query = db.query("SELECT * FROM sessions WHERE content_session_id = ?");
42
+ return query.get(contentSessionId);
43
+ }
44
+ function getSessionById(db, id) {
45
+ const query = db.query("SELECT * FROM sessions WHERE id = ?");
46
+ return query.get(id);
47
+ }
48
+ function updateSessionMemoryId(db, id, memorySessionId) {
49
+ db.run(
50
+ "UPDATE sessions SET memory_session_id = ? WHERE id = ?",
51
+ [memorySessionId, id]
52
+ );
53
+ }
54
+ function completeSession(db, id) {
55
+ const now = /* @__PURE__ */ new Date();
56
+ db.run(
57
+ `UPDATE sessions
58
+ SET status = 'completed', completed_at = ?, completed_at_epoch = ?
59
+ WHERE id = ?`,
60
+ [now.toISOString(), now.getTime(), id]
61
+ );
62
+ }
63
+ function failSession(db, id) {
64
+ const now = /* @__PURE__ */ new Date();
65
+ db.run(
66
+ `UPDATE sessions
67
+ SET status = 'failed', completed_at = ?, completed_at_epoch = ?
68
+ WHERE id = ?`,
69
+ [now.toISOString(), now.getTime(), id]
70
+ );
71
+ }
72
+ function getActiveSessions(db) {
73
+ const query = db.query("SELECT * FROM sessions WHERE status = 'active' ORDER BY started_at_epoch DESC");
74
+ return query.all();
75
+ }
76
+ function getSessionsByProject(db, project, limit = 100) {
77
+ const query = db.query("SELECT * FROM sessions WHERE project = ? ORDER BY started_at_epoch DESC LIMIT ?");
78
+ return query.all(project, limit);
79
+ }
80
+ var init_Sessions = __esm({
81
+ "src/services/sqlite/Sessions.ts"() {
82
+ "use strict";
83
+ }
84
+ });
85
+
86
+ // src/services/sqlite/Observations.ts
87
+ var Observations_exports = {};
88
+ __export(Observations_exports, {
89
+ createObservation: () => createObservation,
90
+ deleteObservation: () => deleteObservation,
91
+ getObservationsByProject: () => getObservationsByProject,
92
+ getObservationsBySession: () => getObservationsBySession,
93
+ searchObservations: () => searchObservations
94
+ });
95
+ function createObservation(db, memorySessionId, project, type, title, subtitle, text, narrative, facts, concepts, filesRead, filesModified, promptNumber) {
96
+ const now = /* @__PURE__ */ new Date();
97
+ const result = db.run(
98
+ `INSERT INTO observations
99
+ (memory_session_id, project, type, title, subtitle, text, narrative, facts, concepts, files_read, files_modified, prompt_number, created_at, created_at_epoch)
100
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
101
+ [memorySessionId, project, type, title, subtitle, text, narrative, facts, concepts, filesRead, filesModified, promptNumber, now.toISOString(), now.getTime()]
102
+ );
103
+ return Number(result.lastInsertRowid);
104
+ }
105
+ function getObservationsBySession(db, memorySessionId) {
106
+ const query = db.query(
107
+ "SELECT * FROM observations WHERE memory_session_id = ? ORDER BY prompt_number ASC"
108
+ );
109
+ return query.all(memorySessionId);
110
+ }
111
+ function getObservationsByProject(db, project, limit = 100) {
112
+ const query = db.query(
113
+ "SELECT * FROM observations WHERE project = ? ORDER BY created_at_epoch DESC LIMIT ?"
114
+ );
115
+ return query.all(project, limit);
116
+ }
117
+ function searchObservations(db, searchTerm, project) {
118
+ const sql = project ? `SELECT * FROM observations
119
+ WHERE project = ? AND (title LIKE ? OR text LIKE ? OR narrative LIKE ?)
120
+ ORDER BY created_at_epoch DESC` : `SELECT * FROM observations
121
+ WHERE title LIKE ? OR text LIKE ? OR narrative LIKE ?
122
+ ORDER BY created_at_epoch DESC`;
123
+ const pattern = `%${searchTerm}%`;
124
+ const query = db.query(sql);
125
+ if (project) {
126
+ return query.all(project, pattern, pattern, pattern);
127
+ }
128
+ return query.all(pattern, pattern, pattern);
129
+ }
130
+ function deleteObservation(db, id) {
131
+ db.run("DELETE FROM observations WHERE id = ?", [id]);
132
+ }
133
+ var init_Observations = __esm({
134
+ "src/services/sqlite/Observations.ts"() {
135
+ "use strict";
136
+ }
137
+ });
138
+
139
+ // src/services/sqlite/Summaries.ts
140
+ var Summaries_exports = {};
141
+ __export(Summaries_exports, {
142
+ createSummary: () => createSummary,
143
+ deleteSummary: () => deleteSummary,
144
+ getSummariesByProject: () => getSummariesByProject,
145
+ getSummaryBySession: () => getSummaryBySession,
146
+ searchSummaries: () => searchSummaries
147
+ });
148
+ function createSummary(db, sessionId, project, request, investigated, learned, completed, nextSteps, notes) {
149
+ const now = /* @__PURE__ */ new Date();
150
+ const result = db.run(
151
+ `INSERT INTO summaries
152
+ (session_id, project, request, investigated, learned, completed, next_steps, notes, created_at, created_at_epoch)
153
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
154
+ [sessionId, project, request, investigated, learned, completed, nextSteps, notes, now.toISOString(), now.getTime()]
155
+ );
156
+ return Number(result.lastInsertRowid);
157
+ }
158
+ function getSummaryBySession(db, sessionId) {
159
+ const query = db.query("SELECT * FROM summaries WHERE session_id = ? ORDER BY created_at_epoch DESC LIMIT 1");
160
+ return query.get(sessionId);
161
+ }
162
+ function getSummariesByProject(db, project, limit = 50) {
163
+ const query = db.query(
164
+ "SELECT * FROM summaries WHERE project = ? ORDER BY created_at_epoch DESC LIMIT ?"
165
+ );
166
+ return query.all(project, limit);
167
+ }
168
+ function searchSummaries(db, searchTerm, project) {
169
+ const sql = project ? `SELECT * FROM summaries
170
+ WHERE project = ? AND (request LIKE ? OR learned LIKE ? OR completed LIKE ? OR notes LIKE ?)
171
+ ORDER BY created_at_epoch DESC` : `SELECT * FROM summaries
172
+ WHERE request LIKE ? OR learned LIKE ? OR completed LIKE ? OR notes LIKE ?
173
+ ORDER BY created_at_epoch DESC`;
174
+ const pattern = `%${searchTerm}%`;
175
+ const query = db.query(sql);
176
+ if (project) {
177
+ return query.all(project, pattern, pattern, pattern, pattern);
178
+ }
179
+ return query.all(pattern, pattern, pattern, pattern);
180
+ }
181
+ function deleteSummary(db, id) {
182
+ db.run("DELETE FROM summaries WHERE id = ?", [id]);
183
+ }
184
+ var init_Summaries = __esm({
185
+ "src/services/sqlite/Summaries.ts"() {
186
+ "use strict";
187
+ }
188
+ });
189
+
190
+ // src/services/sqlite/Prompts.ts
191
+ var Prompts_exports = {};
192
+ __export(Prompts_exports, {
193
+ createPrompt: () => createPrompt,
194
+ deletePrompt: () => deletePrompt,
195
+ getLatestPrompt: () => getLatestPrompt,
196
+ getPromptsByProject: () => getPromptsByProject,
197
+ getPromptsBySession: () => getPromptsBySession
198
+ });
199
+ function createPrompt(db, contentSessionId, project, promptNumber, promptText) {
200
+ const now = /* @__PURE__ */ new Date();
201
+ const result = db.run(
202
+ `INSERT INTO prompts
203
+ (content_session_id, project, prompt_number, prompt_text, created_at, created_at_epoch)
204
+ VALUES (?, ?, ?, ?, ?, ?)`,
205
+ [contentSessionId, project, promptNumber, promptText, now.toISOString(), now.getTime()]
206
+ );
207
+ return Number(result.lastInsertRowid);
208
+ }
209
+ function getPromptsBySession(db, contentSessionId) {
210
+ const query = db.query(
211
+ "SELECT * FROM prompts WHERE content_session_id = ? ORDER BY prompt_number ASC"
212
+ );
213
+ return query.all(contentSessionId);
214
+ }
215
+ function getPromptsByProject(db, project, limit = 100) {
216
+ const query = db.query(
217
+ "SELECT * FROM prompts WHERE project = ? ORDER BY created_at_epoch DESC LIMIT ?"
218
+ );
219
+ return query.all(project, limit);
220
+ }
221
+ function getLatestPrompt(db, contentSessionId) {
222
+ const query = db.query(
223
+ "SELECT * FROM prompts WHERE content_session_id = ? ORDER BY prompt_number DESC LIMIT 1"
224
+ );
225
+ return query.get(contentSessionId);
226
+ }
227
+ function deletePrompt(db, id) {
228
+ db.run("DELETE FROM prompts WHERE id = ?", [id]);
229
+ }
230
+ var init_Prompts = __esm({
231
+ "src/services/sqlite/Prompts.ts"() {
232
+ "use strict";
233
+ }
234
+ });
235
+
236
+ // src/services/sqlite/Search.ts
237
+ var Search_exports = {};
238
+ __export(Search_exports, {
239
+ getObservationsByIds: () => getObservationsByIds,
240
+ getProjectStats: () => getProjectStats,
241
+ getTimeline: () => getTimeline,
242
+ searchObservationsFTS: () => searchObservationsFTS,
243
+ searchObservationsLIKE: () => searchObservationsLIKE,
244
+ searchSummariesFiltered: () => searchSummariesFiltered
245
+ });
246
+ function searchObservationsFTS(db, query, filters = {}) {
247
+ const limit = filters.limit || 50;
248
+ try {
249
+ let sql = `
250
+ SELECT o.* FROM observations o
251
+ JOIN observations_fts fts ON o.id = fts.rowid
252
+ WHERE observations_fts MATCH ?
253
+ `;
254
+ const params = [query];
255
+ if (filters.project) {
256
+ sql += " AND o.project = ?";
257
+ params.push(filters.project);
258
+ }
259
+ if (filters.type) {
260
+ sql += " AND o.type = ?";
261
+ params.push(filters.type);
262
+ }
263
+ if (filters.dateStart) {
264
+ sql += " AND o.created_at_epoch >= ?";
265
+ params.push(filters.dateStart);
266
+ }
267
+ if (filters.dateEnd) {
268
+ sql += " AND o.created_at_epoch <= ?";
269
+ params.push(filters.dateEnd);
270
+ }
271
+ sql += " ORDER BY rank LIMIT ?";
272
+ params.push(limit);
273
+ const stmt = db.query(sql);
274
+ return stmt.all(...params);
275
+ } catch {
276
+ return searchObservationsLIKE(db, query, filters);
277
+ }
278
+ }
279
+ function searchObservationsLIKE(db, query, filters = {}) {
280
+ const limit = filters.limit || 50;
281
+ const pattern = `%${query}%`;
282
+ let sql = `
283
+ SELECT * FROM observations
284
+ WHERE (title LIKE ? OR text LIKE ? OR narrative LIKE ? OR concepts LIKE ?)
285
+ `;
286
+ const params = [pattern, pattern, pattern, pattern];
287
+ if (filters.project) {
288
+ sql += " AND project = ?";
289
+ params.push(filters.project);
290
+ }
291
+ if (filters.type) {
292
+ sql += " AND type = ?";
293
+ params.push(filters.type);
294
+ }
295
+ if (filters.dateStart) {
296
+ sql += " AND created_at_epoch >= ?";
297
+ params.push(filters.dateStart);
298
+ }
299
+ if (filters.dateEnd) {
300
+ sql += " AND created_at_epoch <= ?";
301
+ params.push(filters.dateEnd);
302
+ }
303
+ sql += " ORDER BY created_at_epoch DESC LIMIT ?";
304
+ params.push(limit);
305
+ const stmt = db.query(sql);
306
+ return stmt.all(...params);
307
+ }
308
+ function searchSummariesFiltered(db, query, filters = {}) {
309
+ const limit = filters.limit || 20;
310
+ const pattern = `%${query}%`;
311
+ let sql = `
312
+ SELECT * FROM summaries
313
+ WHERE (request LIKE ? OR learned LIKE ? OR completed LIKE ? OR notes LIKE ? OR next_steps LIKE ?)
314
+ `;
315
+ const params = [pattern, pattern, pattern, pattern, pattern];
316
+ if (filters.project) {
317
+ sql += " AND project = ?";
318
+ params.push(filters.project);
319
+ }
320
+ if (filters.dateStart) {
321
+ sql += " AND created_at_epoch >= ?";
322
+ params.push(filters.dateStart);
323
+ }
324
+ if (filters.dateEnd) {
325
+ sql += " AND created_at_epoch <= ?";
326
+ params.push(filters.dateEnd);
327
+ }
328
+ sql += " ORDER BY created_at_epoch DESC LIMIT ?";
329
+ params.push(limit);
330
+ const stmt = db.query(sql);
331
+ return stmt.all(...params);
332
+ }
333
+ function getObservationsByIds(db, ids) {
334
+ if (ids.length === 0) return [];
335
+ const placeholders = ids.map(() => "?").join(",");
336
+ const sql = `SELECT * FROM observations WHERE id IN (${placeholders}) ORDER BY created_at_epoch DESC`;
337
+ const stmt = db.query(sql);
338
+ return stmt.all(...ids);
339
+ }
340
+ function getTimeline(db, anchorId, depthBefore = 5, depthAfter = 5) {
341
+ const anchorStmt = db.query("SELECT created_at_epoch FROM observations WHERE id = ?");
342
+ const anchor = anchorStmt.get(anchorId);
343
+ if (!anchor) return [];
344
+ const anchorEpoch = anchor.created_at_epoch;
345
+ const beforeStmt = db.query(`
346
+ SELECT id, 'observation' as type, title, text as content, project, created_at, created_at_epoch
347
+ FROM observations
348
+ WHERE created_at_epoch < ?
349
+ ORDER BY created_at_epoch DESC
350
+ LIMIT ?
351
+ `);
352
+ const before = beforeStmt.all(anchorEpoch, depthBefore).reverse();
353
+ const selfStmt = db.query(`
354
+ SELECT id, 'observation' as type, title, text as content, project, created_at, created_at_epoch
355
+ FROM observations WHERE id = ?
356
+ `);
357
+ const self = selfStmt.all(anchorId);
358
+ const afterStmt = db.query(`
359
+ SELECT id, 'observation' as type, title, text as content, project, created_at, created_at_epoch
360
+ FROM observations
361
+ WHERE created_at_epoch > ?
362
+ ORDER BY created_at_epoch ASC
363
+ LIMIT ?
364
+ `);
365
+ const after = afterStmt.all(anchorEpoch, depthAfter);
366
+ return [...before, ...self, ...after];
367
+ }
368
+ function getProjectStats(db, project) {
369
+ const obsStmt = db.query("SELECT COUNT(*) as count FROM observations WHERE project = ?");
370
+ const sumStmt = db.query("SELECT COUNT(*) as count FROM summaries WHERE project = ?");
371
+ const sesStmt = db.query("SELECT COUNT(*) as count FROM sessions WHERE project = ?");
372
+ const prmStmt = db.query("SELECT COUNT(*) as count FROM prompts WHERE project = ?");
373
+ return {
374
+ observations: obsStmt.get(project)?.count || 0,
375
+ summaries: sumStmt.get(project)?.count || 0,
376
+ sessions: sesStmt.get(project)?.count || 0,
377
+ prompts: prmStmt.get(project)?.count || 0
378
+ };
379
+ }
380
+ var init_Search = __esm({
381
+ "src/services/sqlite/Search.ts"() {
382
+ "use strict";
383
+ }
384
+ });
385
+
386
+ // src/shims/bun-sqlite.ts
387
+ import BetterSqlite3 from "better-sqlite3";
388
+ var Database = class {
389
+ _db;
390
+ constructor(path, options) {
391
+ this._db = new BetterSqlite3(path, {
392
+ // better-sqlite3 crea il file di default (non serve 'create')
393
+ readonly: options?.readwrite === false ? true : false
394
+ });
395
+ }
396
+ /**
397
+ * Esegui una query SQL senza risultati
398
+ */
399
+ run(sql, params) {
400
+ const stmt = this._db.prepare(sql);
401
+ const result = params ? stmt.run(...params) : stmt.run();
402
+ return result;
403
+ }
404
+ /**
405
+ * Prepara una query con interfaccia compatibile bun:sqlite
406
+ */
407
+ query(sql) {
408
+ return new BunQueryCompat(this._db, sql);
409
+ }
410
+ /**
411
+ * Crea una transazione
412
+ */
413
+ transaction(fn) {
414
+ return this._db.transaction(fn);
415
+ }
416
+ /**
417
+ * Chiudi la connessione
418
+ */
419
+ close() {
420
+ this._db.close();
421
+ }
422
+ };
423
+ var BunQueryCompat = class {
424
+ _db;
425
+ _sql;
426
+ constructor(db, sql) {
427
+ this._db = db;
428
+ this._sql = sql;
429
+ }
430
+ /**
431
+ * Restituisce tutte le righe
432
+ */
433
+ all(...params) {
434
+ const stmt = this._db.prepare(this._sql);
435
+ return params.length > 0 ? stmt.all(...params) : stmt.all();
436
+ }
437
+ /**
438
+ * Restituisce la prima riga o null
439
+ */
440
+ get(...params) {
441
+ const stmt = this._db.prepare(this._sql);
442
+ return params.length > 0 ? stmt.get(...params) : stmt.get();
443
+ }
444
+ /**
445
+ * Esegui senza risultati
446
+ */
447
+ run(...params) {
448
+ const stmt = this._db.prepare(this._sql);
449
+ return params.length > 0 ? stmt.run(...params) : stmt.run();
450
+ }
451
+ };
452
+
453
+ // src/shared/paths.ts
454
+ import { join as join2, dirname, basename } from "path";
455
+ import { homedir as homedir2 } from "os";
456
+ import { mkdirSync as mkdirSync2 } from "fs";
457
+ import { fileURLToPath } from "url";
458
+
459
+ // src/utils/logger.ts
460
+ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "fs";
461
+ import { join } from "path";
462
+ import { homedir } from "os";
463
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
464
+ LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
465
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
466
+ LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
467
+ LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
468
+ LogLevel2[LogLevel2["SILENT"] = 4] = "SILENT";
469
+ return LogLevel2;
470
+ })(LogLevel || {});
471
+ var DEFAULT_DATA_DIR = join(homedir(), ".contextkit");
472
+ var Logger = class {
473
+ level = null;
474
+ useColor;
475
+ logFilePath = null;
476
+ logFileInitialized = false;
477
+ constructor() {
478
+ this.useColor = process.stdout.isTTY ?? false;
479
+ }
480
+ /**
481
+ * Initialize log file path and ensure directory exists (lazy initialization)
482
+ */
483
+ ensureLogFileInitialized() {
484
+ if (this.logFileInitialized) return;
485
+ this.logFileInitialized = true;
486
+ try {
487
+ const logsDir = join(DEFAULT_DATA_DIR, "logs");
488
+ if (!existsSync(logsDir)) {
489
+ mkdirSync(logsDir, { recursive: true });
490
+ }
491
+ const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
492
+ this.logFilePath = join(logsDir, `contextkit-${date}.log`);
493
+ } catch (error) {
494
+ console.error("[LOGGER] Failed to initialize log file:", error);
495
+ this.logFilePath = null;
496
+ }
497
+ }
498
+ /**
499
+ * Lazy-load log level from settings file
500
+ */
501
+ getLevel() {
502
+ if (this.level === null) {
503
+ try {
504
+ const settingsPath = join(DEFAULT_DATA_DIR, "settings.json");
505
+ if (existsSync(settingsPath)) {
506
+ const settingsData = readFileSync(settingsPath, "utf-8");
507
+ const settings = JSON.parse(settingsData);
508
+ const envLevel = (settings.CONTEXTKIT_LOG_LEVEL || "INFO").toUpperCase();
509
+ this.level = LogLevel[envLevel] ?? 1 /* INFO */;
510
+ } else {
511
+ this.level = 1 /* INFO */;
512
+ }
513
+ } catch (error) {
514
+ this.level = 1 /* INFO */;
515
+ }
516
+ }
517
+ return this.level;
518
+ }
519
+ /**
520
+ * Create correlation ID for tracking an observation through the pipeline
521
+ */
522
+ correlationId(sessionId, observationNum) {
523
+ return `obs-${sessionId}-${observationNum}`;
524
+ }
525
+ /**
526
+ * Create session correlation ID
527
+ */
528
+ sessionId(sessionId) {
529
+ return `session-${sessionId}`;
530
+ }
531
+ /**
532
+ * Format data for logging - create compact summaries instead of full dumps
533
+ */
534
+ formatData(data) {
535
+ if (data === null || data === void 0) return "";
536
+ if (typeof data === "string") return data;
537
+ if (typeof data === "number") return data.toString();
538
+ if (typeof data === "boolean") return data.toString();
539
+ if (typeof data === "object") {
540
+ if (data instanceof Error) {
541
+ return this.getLevel() === 0 /* DEBUG */ ? `${data.message}
542
+ ${data.stack}` : data.message;
543
+ }
544
+ if (Array.isArray(data)) {
545
+ return `[${data.length} items]`;
546
+ }
547
+ const keys = Object.keys(data);
548
+ if (keys.length === 0) return "{}";
549
+ if (keys.length <= 3) {
550
+ return JSON.stringify(data);
551
+ }
552
+ return `{${keys.length} keys: ${keys.slice(0, 3).join(", ")}...}`;
553
+ }
554
+ return String(data);
555
+ }
556
+ /**
557
+ * Format timestamp in local timezone (YYYY-MM-DD HH:MM:SS.mmm)
558
+ */
559
+ formatTimestamp(date) {
560
+ const year = date.getFullYear();
561
+ const month = String(date.getMonth() + 1).padStart(2, "0");
562
+ const day = String(date.getDate()).padStart(2, "0");
563
+ const hours = String(date.getHours()).padStart(2, "0");
564
+ const minutes = String(date.getMinutes()).padStart(2, "0");
565
+ const seconds = String(date.getSeconds()).padStart(2, "0");
566
+ const ms = String(date.getMilliseconds()).padStart(3, "0");
567
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${ms}`;
568
+ }
569
+ /**
570
+ * Core logging method
571
+ */
572
+ log(level, component, message, context, data) {
573
+ if (level < this.getLevel()) return;
574
+ this.ensureLogFileInitialized();
575
+ const timestamp = this.formatTimestamp(/* @__PURE__ */ new Date());
576
+ const levelStr = LogLevel[level].padEnd(5);
577
+ const componentStr = component.padEnd(6);
578
+ let correlationStr = "";
579
+ if (context?.correlationId) {
580
+ correlationStr = `[${context.correlationId}] `;
581
+ } else if (context?.sessionId) {
582
+ correlationStr = `[session-${context.sessionId}] `;
583
+ }
584
+ let dataStr = "";
585
+ if (data !== void 0 && data !== null) {
586
+ if (data instanceof Error) {
587
+ dataStr = this.getLevel() === 0 /* DEBUG */ ? `
588
+ ${data.message}
589
+ ${data.stack}` : ` ${data.message}`;
590
+ } else if (this.getLevel() === 0 /* DEBUG */ && typeof data === "object") {
591
+ dataStr = "\n" + JSON.stringify(data, null, 2);
592
+ } else {
593
+ dataStr = " " + this.formatData(data);
594
+ }
595
+ }
596
+ let contextStr = "";
597
+ if (context) {
598
+ const { sessionId, memorySessionId, correlationId, ...rest } = context;
599
+ if (Object.keys(rest).length > 0) {
600
+ const pairs = Object.entries(rest).map(([k, v]) => `${k}=${v}`);
601
+ contextStr = ` {${pairs.join(", ")}}`;
602
+ }
603
+ }
604
+ const logLine = `[${timestamp}] [${levelStr}] [${componentStr}] ${correlationStr}${message}${contextStr}${dataStr}`;
605
+ if (this.logFilePath) {
606
+ try {
607
+ appendFileSync(this.logFilePath, logLine + "\n", "utf8");
608
+ } catch (error) {
609
+ process.stderr.write(`[LOGGER] Failed to write to log file: ${error}
610
+ `);
611
+ }
612
+ } else {
613
+ process.stderr.write(logLine + "\n");
614
+ }
615
+ }
616
+ // Public logging methods
617
+ debug(component, message, context, data) {
618
+ this.log(0 /* DEBUG */, component, message, context, data);
619
+ }
620
+ info(component, message, context, data) {
621
+ this.log(1 /* INFO */, component, message, context, data);
622
+ }
623
+ warn(component, message, context, data) {
624
+ this.log(2 /* WARN */, component, message, context, data);
625
+ }
626
+ error(component, message, context, data) {
627
+ this.log(3 /* ERROR */, component, message, context, data);
628
+ }
629
+ /**
630
+ * Log data flow: input → processing
631
+ */
632
+ dataIn(component, message, context, data) {
633
+ this.info(component, `\u2192 ${message}`, context, data);
634
+ }
635
+ /**
636
+ * Log data flow: processing → output
637
+ */
638
+ dataOut(component, message, context, data) {
639
+ this.info(component, `\u2190 ${message}`, context, data);
640
+ }
641
+ /**
642
+ * Log successful completion
643
+ */
644
+ success(component, message, context, data) {
645
+ this.info(component, `\u2713 ${message}`, context, data);
646
+ }
647
+ /**
648
+ * Log failure
649
+ */
650
+ failure(component, message, context, data) {
651
+ this.error(component, `\u2717 ${message}`, context, data);
652
+ }
653
+ /**
654
+ * Log timing information
655
+ */
656
+ timing(component, message, durationMs, context) {
657
+ this.info(component, `\u23F1 ${message}`, context, { duration: `${durationMs}ms` });
658
+ }
659
+ /**
660
+ * Happy Path Error - logs when the expected "happy path" fails but we have a fallback
661
+ */
662
+ happyPathError(component, message, context, data, fallback = "") {
663
+ const stack = new Error().stack || "";
664
+ const stackLines = stack.split("\n");
665
+ const callerLine = stackLines[2] || "";
666
+ const callerMatch = callerLine.match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/);
667
+ const location = callerMatch ? `${callerMatch[1].split("/").pop()}:${callerMatch[2]}` : "unknown";
668
+ const enhancedContext = {
669
+ ...context,
670
+ location
671
+ };
672
+ this.warn(component, `[HAPPY-PATH] ${message}`, enhancedContext, data);
673
+ return fallback;
674
+ }
675
+ };
676
+ var logger = new Logger();
677
+
678
+ // src/shared/paths.ts
679
+ function getDirname() {
680
+ if (typeof __dirname !== "undefined") {
681
+ return __dirname;
682
+ }
683
+ return dirname(fileURLToPath(import.meta.url));
684
+ }
685
+ var _dirname = getDirname();
686
+ var DATA_DIR = process.env.CONTEXTKIT_DATA_DIR || join2(homedir2(), ".contextkit");
687
+ var KIRO_CONFIG_DIR = process.env.KIRO_CONFIG_DIR || join2(homedir2(), ".kiro");
688
+ var PLUGIN_ROOT = join2(KIRO_CONFIG_DIR, "plugins", "contextkit");
689
+ var ARCHIVES_DIR = join2(DATA_DIR, "archives");
690
+ var LOGS_DIR = join2(DATA_DIR, "logs");
691
+ var TRASH_DIR = join2(DATA_DIR, "trash");
692
+ var BACKUPS_DIR = join2(DATA_DIR, "backups");
693
+ var MODES_DIR = join2(DATA_DIR, "modes");
694
+ var USER_SETTINGS_PATH = join2(DATA_DIR, "settings.json");
695
+ var DB_PATH = join2(DATA_DIR, "contextkit.db");
696
+ var VECTOR_DB_DIR = join2(DATA_DIR, "vector-db");
697
+ var OBSERVER_SESSIONS_DIR = join2(DATA_DIR, "observer-sessions");
698
+ var KIRO_SETTINGS_PATH = join2(KIRO_CONFIG_DIR, "settings.json");
699
+ var KIRO_CONTEXT_PATH = join2(KIRO_CONFIG_DIR, "context.md");
700
+ function ensureDir(dirPath) {
701
+ mkdirSync2(dirPath, { recursive: true });
702
+ }
703
+
704
+ // src/services/sqlite/Database.ts
705
+ var SQLITE_MMAP_SIZE_BYTES = 256 * 1024 * 1024;
706
+ var SQLITE_CACHE_SIZE_PAGES = 1e4;
707
+ var ContextKitDatabase = class {
708
+ db;
709
+ constructor(dbPath = DB_PATH) {
710
+ if (dbPath !== ":memory:") {
711
+ ensureDir(DATA_DIR);
712
+ }
713
+ this.db = new Database(dbPath, { create: true, readwrite: true });
714
+ this.db.run("PRAGMA journal_mode = WAL");
715
+ this.db.run("PRAGMA synchronous = NORMAL");
716
+ this.db.run("PRAGMA foreign_keys = ON");
717
+ this.db.run("PRAGMA temp_store = memory");
718
+ this.db.run(`PRAGMA mmap_size = ${SQLITE_MMAP_SIZE_BYTES}`);
719
+ this.db.run(`PRAGMA cache_size = ${SQLITE_CACHE_SIZE_PAGES}`);
720
+ const migrationRunner = new MigrationRunner(this.db);
721
+ migrationRunner.runAllMigrations();
722
+ }
723
+ /**
724
+ * Close the database connection
725
+ */
726
+ close() {
727
+ this.db.close();
728
+ }
729
+ };
730
+ var MigrationRunner = class {
731
+ db;
732
+ constructor(db) {
733
+ this.db = db;
734
+ }
735
+ runAllMigrations() {
736
+ this.db.run(`
737
+ CREATE TABLE IF NOT EXISTS schema_versions (
738
+ id INTEGER PRIMARY KEY,
739
+ version INTEGER UNIQUE NOT NULL,
740
+ applied_at TEXT NOT NULL
741
+ )
742
+ `);
743
+ const versionQuery = this.db.query("SELECT MAX(version) as version FROM schema_versions");
744
+ const result = versionQuery.get();
745
+ const currentVersion = result?.version || 0;
746
+ const migrations = this.getMigrations();
747
+ for (const migration of migrations) {
748
+ if (migration.version > currentVersion) {
749
+ logger.info("DB", `Applying migration ${migration.version}`);
750
+ const transaction = this.db.transaction(() => {
751
+ migration.up(this.db);
752
+ const insert = this.db.query("INSERT INTO schema_versions (version, applied_at) VALUES (?, ?)");
753
+ insert.run(migration.version, (/* @__PURE__ */ new Date()).toISOString());
754
+ });
755
+ transaction();
756
+ logger.info("DB", `Migration ${migration.version} applied successfully`);
757
+ }
758
+ }
759
+ }
760
+ getMigrations() {
761
+ return [
762
+ {
763
+ version: 1,
764
+ up: (db) => {
765
+ db.run(`
766
+ CREATE TABLE IF NOT EXISTS sessions (
767
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
768
+ content_session_id TEXT NOT NULL UNIQUE,
769
+ project TEXT NOT NULL,
770
+ user_prompt TEXT NOT NULL,
771
+ memory_session_id TEXT,
772
+ status TEXT DEFAULT 'active',
773
+ started_at TEXT NOT NULL,
774
+ started_at_epoch INTEGER NOT NULL,
775
+ completed_at TEXT,
776
+ completed_at_epoch INTEGER
777
+ )
778
+ `);
779
+ db.run(`
780
+ CREATE TABLE IF NOT EXISTS observations (
781
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
782
+ memory_session_id TEXT NOT NULL,
783
+ project TEXT NOT NULL,
784
+ type TEXT NOT NULL,
785
+ title TEXT NOT NULL,
786
+ subtitle TEXT,
787
+ text TEXT,
788
+ narrative TEXT,
789
+ facts TEXT,
790
+ concepts TEXT,
791
+ files_read TEXT,
792
+ files_modified TEXT,
793
+ prompt_number INTEGER NOT NULL,
794
+ created_at TEXT NOT NULL,
795
+ created_at_epoch INTEGER NOT NULL
796
+ )
797
+ `);
798
+ db.run(`
799
+ CREATE TABLE IF NOT EXISTS summaries (
800
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
801
+ session_id TEXT NOT NULL,
802
+ project TEXT NOT NULL,
803
+ request TEXT,
804
+ investigated TEXT,
805
+ learned TEXT,
806
+ completed TEXT,
807
+ next_steps TEXT,
808
+ notes TEXT,
809
+ created_at TEXT NOT NULL,
810
+ created_at_epoch INTEGER NOT NULL
811
+ )
812
+ `);
813
+ db.run(`
814
+ CREATE TABLE IF NOT EXISTS prompts (
815
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
816
+ content_session_id TEXT NOT NULL,
817
+ project TEXT NOT NULL,
818
+ prompt_number INTEGER NOT NULL,
819
+ prompt_text TEXT NOT NULL,
820
+ created_at TEXT NOT NULL,
821
+ created_at_epoch INTEGER NOT NULL
822
+ )
823
+ `);
824
+ db.run(`
825
+ CREATE TABLE IF NOT EXISTS pending_messages (
826
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
827
+ content_session_id TEXT NOT NULL,
828
+ type TEXT NOT NULL,
829
+ data TEXT NOT NULL,
830
+ created_at TEXT NOT NULL,
831
+ created_at_epoch INTEGER NOT NULL
832
+ )
833
+ `);
834
+ db.run("CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project)");
835
+ db.run("CREATE INDEX IF NOT EXISTS idx_observations_project ON observations(project)");
836
+ db.run("CREATE INDEX IF NOT EXISTS idx_observations_session ON observations(memory_session_id)");
837
+ db.run("CREATE INDEX IF NOT EXISTS idx_summaries_session ON summaries(session_id)");
838
+ db.run("CREATE INDEX IF NOT EXISTS idx_prompts_session ON prompts(content_session_id)");
839
+ }
840
+ },
841
+ {
842
+ version: 2,
843
+ up: (db) => {
844
+ db.run(`
845
+ CREATE VIRTUAL TABLE IF NOT EXISTS observations_fts USING fts5(
846
+ title, text, narrative, concepts,
847
+ content='observations',
848
+ content_rowid='id'
849
+ )
850
+ `);
851
+ db.run(`
852
+ CREATE TRIGGER IF NOT EXISTS observations_ai AFTER INSERT ON observations BEGIN
853
+ INSERT INTO observations_fts(rowid, title, text, narrative, concepts)
854
+ VALUES (new.id, new.title, new.text, new.narrative, new.concepts);
855
+ END
856
+ `);
857
+ db.run(`
858
+ CREATE TRIGGER IF NOT EXISTS observations_ad AFTER DELETE ON observations BEGIN
859
+ INSERT INTO observations_fts(observations_fts, rowid, title, text, narrative, concepts)
860
+ VALUES ('delete', old.id, old.title, old.text, old.narrative, old.concepts);
861
+ END
862
+ `);
863
+ db.run(`
864
+ CREATE TRIGGER IF NOT EXISTS observations_au AFTER UPDATE ON observations BEGIN
865
+ INSERT INTO observations_fts(observations_fts, rowid, title, text, narrative, concepts)
866
+ VALUES ('delete', old.id, old.title, old.text, old.narrative, old.concepts);
867
+ INSERT INTO observations_fts(rowid, title, text, narrative, concepts)
868
+ VALUES (new.id, new.title, new.text, new.narrative, new.concepts);
869
+ END
870
+ `);
871
+ db.run(`
872
+ INSERT INTO observations_fts(rowid, title, text, narrative, concepts)
873
+ SELECT id, title, text, narrative, concepts FROM observations
874
+ `);
875
+ db.run("CREATE INDEX IF NOT EXISTS idx_observations_type ON observations(type)");
876
+ db.run("CREATE INDEX IF NOT EXISTS idx_observations_epoch ON observations(created_at_epoch)");
877
+ db.run("CREATE INDEX IF NOT EXISTS idx_summaries_project ON summaries(project)");
878
+ db.run("CREATE INDEX IF NOT EXISTS idx_summaries_epoch ON summaries(created_at_epoch)");
879
+ db.run("CREATE INDEX IF NOT EXISTS idx_prompts_project ON prompts(project)");
880
+ }
881
+ }
882
+ ];
883
+ }
884
+ };
885
+
886
+ // src/services/sqlite/index.ts
887
+ init_Sessions();
888
+ init_Observations();
889
+ init_Summaries();
890
+ init_Prompts();
891
+ init_Search();
892
+
893
+ // src/sdk/index.ts
894
+ var ContextKitSDK = class {
895
+ db;
896
+ project;
897
+ constructor(config = {}) {
898
+ this.db = new ContextKitDatabase(config.dataDir);
899
+ this.project = config.project || this.detectProject();
900
+ }
901
+ detectProject() {
902
+ try {
903
+ const { execSync } = __require("child_process");
904
+ const gitRoot = execSync("git rev-parse --show-toplevel", {
905
+ cwd: process.cwd(),
906
+ encoding: "utf8",
907
+ stdio: ["pipe", "pipe", "ignore"]
908
+ }).trim();
909
+ return gitRoot.split("/").pop() || "default";
910
+ } catch {
911
+ return "default";
912
+ }
913
+ }
914
+ /**
915
+ * Get context for the current project
916
+ */
917
+ async getContext() {
918
+ const { getObservationsByProject: getObservationsByProject2 } = await Promise.resolve().then(() => (init_Observations(), Observations_exports));
919
+ const { getSummariesByProject: getSummariesByProject2 } = await Promise.resolve().then(() => (init_Summaries(), Summaries_exports));
920
+ const { getPromptsByProject: getPromptsByProject2 } = await Promise.resolve().then(() => (init_Prompts(), Prompts_exports));
921
+ return {
922
+ project: this.project,
923
+ relevantObservations: getObservationsByProject2(this.db.db, this.project, 20),
924
+ relevantSummaries: getSummariesByProject2(this.db.db, this.project, 5),
925
+ recentPrompts: getPromptsByProject2(this.db.db, this.project, 10)
926
+ };
927
+ }
928
+ /**
929
+ * Store a new observation
930
+ */
931
+ async storeObservation(data) {
932
+ const { createObservation: createObservation2 } = await Promise.resolve().then(() => (init_Observations(), Observations_exports));
933
+ return createObservation2(
934
+ this.db.db,
935
+ "sdk-" + Date.now(),
936
+ this.project,
937
+ data.type,
938
+ data.title,
939
+ null,
940
+ // subtitle
941
+ data.content,
942
+ null,
943
+ // narrative
944
+ null,
945
+ // facts
946
+ data.concepts?.join(", ") || null,
947
+ data.files?.join(", ") || null,
948
+ // files_read
949
+ data.files?.join(", ") || null,
950
+ // files_modified
951
+ 0
952
+ // prompt_number
953
+ );
954
+ }
955
+ /**
956
+ * Store a session summary
957
+ */
958
+ async storeSummary(data) {
959
+ const { createSummary: createSummary2 } = await Promise.resolve().then(() => (init_Summaries(), Summaries_exports));
960
+ return createSummary2(
961
+ this.db.db,
962
+ "sdk-" + Date.now(),
963
+ this.project,
964
+ data.request || null,
965
+ null,
966
+ data.learned || null,
967
+ data.completed || null,
968
+ data.nextSteps || null,
969
+ null
970
+ );
971
+ }
972
+ /**
973
+ * Search across all stored context
974
+ */
975
+ async search(query) {
976
+ const { searchObservations: searchObservations2 } = await Promise.resolve().then(() => (init_Observations(), Observations_exports));
977
+ const { searchSummaries: searchSummaries2 } = await Promise.resolve().then(() => (init_Summaries(), Summaries_exports));
978
+ return {
979
+ observations: searchObservations2(this.db.db, query, this.project),
980
+ summaries: searchSummaries2(this.db.db, query, this.project)
981
+ };
982
+ }
983
+ /**
984
+ * Get recent observations
985
+ */
986
+ async getRecentObservations(limit = 10) {
987
+ const { getObservationsByProject: getObservationsByProject2 } = await Promise.resolve().then(() => (init_Observations(), Observations_exports));
988
+ return getObservationsByProject2(this.db.db, this.project, limit);
989
+ }
990
+ /**
991
+ * Get recent summaries
992
+ */
993
+ async getRecentSummaries(limit = 5) {
994
+ const { getSummariesByProject: getSummariesByProject2 } = await Promise.resolve().then(() => (init_Summaries(), Summaries_exports));
995
+ return getSummariesByProject2(this.db.db, this.project, limit);
996
+ }
997
+ /**
998
+ * Ricerca avanzata con FTS5 e filtri
999
+ */
1000
+ async searchAdvanced(query, filters = {}) {
1001
+ const { searchObservationsFTS: searchObservationsFTS2 } = await Promise.resolve().then(() => (init_Search(), Search_exports));
1002
+ const { searchSummariesFiltered: searchSummariesFiltered2 } = await Promise.resolve().then(() => (init_Search(), Search_exports));
1003
+ const projectFilters = { ...filters, project: filters.project || this.project };
1004
+ return {
1005
+ observations: searchObservationsFTS2(this.db.db, query, projectFilters),
1006
+ summaries: searchSummariesFiltered2(this.db.db, query, projectFilters)
1007
+ };
1008
+ }
1009
+ /**
1010
+ * Recupera osservazioni per ID (batch)
1011
+ */
1012
+ async getObservationsByIds(ids) {
1013
+ const { getObservationsByIds: getObservationsByIds2 } = await Promise.resolve().then(() => (init_Search(), Search_exports));
1014
+ return getObservationsByIds2(this.db.db, ids);
1015
+ }
1016
+ /**
1017
+ * Timeline: contesto cronologico attorno a un'osservazione
1018
+ */
1019
+ async getTimeline(anchorId, depthBefore = 5, depthAfter = 5) {
1020
+ const { getTimeline: getTimeline2 } = await Promise.resolve().then(() => (init_Search(), Search_exports));
1021
+ return getTimeline2(this.db.db, anchorId, depthBefore, depthAfter);
1022
+ }
1023
+ /**
1024
+ * Crea o recupera una sessione per il progetto corrente
1025
+ */
1026
+ async getOrCreateSession(contentSessionId) {
1027
+ const { getSessionByContentId: getSessionByContentId2, createSession: createSession2 } = await Promise.resolve().then(() => (init_Sessions(), Sessions_exports));
1028
+ let session = getSessionByContentId2(this.db.db, contentSessionId);
1029
+ if (!session) {
1030
+ const id = createSession2(this.db.db, contentSessionId, this.project, "");
1031
+ session = {
1032
+ id,
1033
+ content_session_id: contentSessionId,
1034
+ project: this.project,
1035
+ user_prompt: "",
1036
+ memory_session_id: null,
1037
+ status: "active",
1038
+ started_at: (/* @__PURE__ */ new Date()).toISOString(),
1039
+ started_at_epoch: Date.now(),
1040
+ completed_at: null,
1041
+ completed_at_epoch: null
1042
+ };
1043
+ }
1044
+ return session;
1045
+ }
1046
+ /**
1047
+ * Salva un prompt utente
1048
+ */
1049
+ async storePrompt(contentSessionId, promptNumber, text) {
1050
+ const { createPrompt: createPrompt2 } = await Promise.resolve().then(() => (init_Prompts(), Prompts_exports));
1051
+ return createPrompt2(this.db.db, contentSessionId, this.project, promptNumber, text);
1052
+ }
1053
+ /**
1054
+ * Completa una sessione
1055
+ */
1056
+ async completeSession(sessionId) {
1057
+ const { completeSession: completeSession2 } = await Promise.resolve().then(() => (init_Sessions(), Sessions_exports));
1058
+ completeSession2(this.db.db, sessionId);
1059
+ }
1060
+ /**
1061
+ * Getter per il nome progetto corrente
1062
+ */
1063
+ getProject() {
1064
+ return this.project;
1065
+ }
1066
+ /**
1067
+ * Getter per accesso diretto al database (per route API)
1068
+ */
1069
+ getDb() {
1070
+ return this.db.db;
1071
+ }
1072
+ /**
1073
+ * Close database connection
1074
+ */
1075
+ close() {
1076
+ this.db.close();
1077
+ }
1078
+ };
1079
+ function createContextKit(config) {
1080
+ return new ContextKitSDK(config);
1081
+ }
1082
+
1083
+ // src/cli/contextkit.ts
1084
+ var args = process.argv.slice(2);
1085
+ var command = args[0];
1086
+ async function main() {
1087
+ const contextkit = createContextKit();
1088
+ try {
1089
+ switch (command) {
1090
+ case "context":
1091
+ case "ctx":
1092
+ await showContext(contextkit);
1093
+ break;
1094
+ case "search":
1095
+ await searchContext(contextkit, args[1]);
1096
+ break;
1097
+ case "observations":
1098
+ case "obs":
1099
+ await showObservations(contextkit, parseInt(args[1]) || 10);
1100
+ break;
1101
+ case "summaries":
1102
+ case "sum":
1103
+ await showSummaries(contextkit, parseInt(args[1]) || 5);
1104
+ break;
1105
+ case "add-observation":
1106
+ case "add-obs":
1107
+ await addObservation(contextkit, args[1], args.slice(2).join(" "));
1108
+ break;
1109
+ case "add-summary":
1110
+ case "add-sum":
1111
+ await addSummary(contextkit, args.slice(1).join(" "));
1112
+ break;
1113
+ case "help":
1114
+ case "--help":
1115
+ case "-h":
1116
+ showHelp();
1117
+ break;
1118
+ default:
1119
+ console.log("ContextKit CLI\n");
1120
+ showHelp();
1121
+ process.exit(1);
1122
+ }
1123
+ } finally {
1124
+ contextkit.close();
1125
+ }
1126
+ }
1127
+ async function showContext(contextkit) {
1128
+ const context = await contextkit.getContext();
1129
+ console.log(`
1130
+ \u{1F4C1} Project: ${context.project}
1131
+ `);
1132
+ console.log("\u{1F4DD} Recent Observations:");
1133
+ context.relevantObservations.slice(0, 5).forEach((obs, i) => {
1134
+ console.log(` ${i + 1}. ${obs.title} (${new Date(obs.created_at).toLocaleDateString()})`);
1135
+ if (obs.text) {
1136
+ console.log(` ${obs.text.substring(0, 100)}${obs.text.length > 100 ? "..." : ""}`);
1137
+ }
1138
+ });
1139
+ console.log("\n\u{1F4CA} Recent Summaries:");
1140
+ context.relevantSummaries.slice(0, 3).forEach((sum, i) => {
1141
+ console.log(` ${i + 1}. ${sum.request || "No request"} (${new Date(sum.created_at).toLocaleDateString()})`);
1142
+ if (sum.learned) {
1143
+ console.log(` Learned: ${sum.learned.substring(0, 100)}${sum.learned.length > 100 ? "..." : ""}`);
1144
+ }
1145
+ });
1146
+ console.log("");
1147
+ }
1148
+ async function searchContext(contextkit, query) {
1149
+ if (!query) {
1150
+ console.error("Error: Please provide a search query");
1151
+ process.exit(1);
1152
+ }
1153
+ const results = await contextkit.search(query);
1154
+ console.log(`
1155
+ \u{1F50D} Search results for: "${query}"
1156
+ `);
1157
+ if (results.observations.length > 0) {
1158
+ console.log(`\u{1F4CB} Observations (${results.observations.length}):`);
1159
+ results.observations.forEach((obs, i) => {
1160
+ console.log(` ${i + 1}. ${obs.title}`);
1161
+ if (obs.text) {
1162
+ console.log(` ${obs.text.substring(0, 150)}${obs.text.length > 150 ? "..." : ""}`);
1163
+ }
1164
+ });
1165
+ }
1166
+ if (results.summaries.length > 0) {
1167
+ console.log(`
1168
+ \u{1F4CA} Summaries (${results.summaries.length}):`);
1169
+ results.summaries.forEach((sum, i) => {
1170
+ console.log(` ${i + 1}. ${sum.request || "No request"}`);
1171
+ if (sum.learned) {
1172
+ console.log(` ${sum.learned.substring(0, 150)}${sum.learned.length > 150 ? "..." : ""}`);
1173
+ }
1174
+ });
1175
+ }
1176
+ if (results.observations.length === 0 && results.summaries.length === 0) {
1177
+ console.log("No results found.\n");
1178
+ } else {
1179
+ console.log("");
1180
+ }
1181
+ }
1182
+ async function showObservations(contextkit, limit) {
1183
+ const observations = await contextkit.getRecentObservations(limit);
1184
+ console.log(`
1185
+ \u{1F4CB} Last ${limit} Observations:
1186
+ `);
1187
+ observations.forEach((obs, i) => {
1188
+ console.log(`${i + 1}. ${obs.title} [${obs.type}]`);
1189
+ console.log(` Date: ${new Date(obs.created_at).toLocaleString()}`);
1190
+ if (obs.text) {
1191
+ console.log(` Content: ${obs.text.substring(0, 200)}${obs.text.length > 200 ? "..." : ""}`);
1192
+ }
1193
+ console.log("");
1194
+ });
1195
+ }
1196
+ async function showSummaries(contextkit, limit) {
1197
+ const summaries = await contextkit.getRecentSummaries(limit);
1198
+ console.log(`
1199
+ \u{1F4CA} Last ${limit} Summaries:
1200
+ `);
1201
+ summaries.forEach((sum, i) => {
1202
+ console.log(`${i + 1}. ${sum.request || "No request"}`);
1203
+ console.log(` Date: ${new Date(sum.created_at).toLocaleString()}`);
1204
+ if (sum.learned) {
1205
+ console.log(` Learned: ${sum.learned}`);
1206
+ }
1207
+ if (sum.completed) {
1208
+ console.log(` Completed: ${sum.completed}`);
1209
+ }
1210
+ if (sum.next_steps) {
1211
+ console.log(` Next Steps: ${sum.next_steps}`);
1212
+ }
1213
+ console.log("");
1214
+ });
1215
+ }
1216
+ async function addObservation(contextkit, title, content) {
1217
+ if (!title || !content) {
1218
+ console.error("Error: Please provide both title and content");
1219
+ process.exit(1);
1220
+ }
1221
+ const id = await contextkit.storeObservation({
1222
+ type: "manual",
1223
+ title,
1224
+ content
1225
+ });
1226
+ console.log(`\u2705 Observation stored with ID: ${id}
1227
+ `);
1228
+ }
1229
+ async function addSummary(contextkit, content) {
1230
+ if (!content) {
1231
+ console.error("Error: Please provide summary content");
1232
+ process.exit(1);
1233
+ }
1234
+ const id = await contextkit.storeSummary({
1235
+ learned: content
1236
+ });
1237
+ console.log(`\u2705 Summary stored with ID: ${id}
1238
+ `);
1239
+ }
1240
+ function showHelp() {
1241
+ console.log(`Usage: contextkit <command> [options]
1242
+
1243
+ Commands:
1244
+ context, ctx Show current project context
1245
+ search <query> Search across all context
1246
+ observations [limit] Show recent observations (default: 10)
1247
+ summaries [limit] Show recent summaries (default: 5)
1248
+ add-observation <title> <content> Add a new observation
1249
+ add-summary <content> Add a new summary
1250
+ help Show this help message
1251
+
1252
+ Examples:
1253
+ contextkit context
1254
+ contextkit search "authentication"
1255
+ contextkit observations 20
1256
+ contextkit add-observation "Bug Fix" "Fixed the login issue"
1257
+ `);
1258
+ }
1259
+ main().catch(console.error);