@jmtrin/opencode-kevin 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -45,8 +45,13 @@ Migrations run automatically on startup.
45
45
 
46
46
  ### Requirements
47
47
 
48
- - Node.js >= 20
49
- - OpenCode with plugin support (`@opencode-ai/plugin` >= 1.17)
48
+ - **Node.js >= 22.5** (uses `node:sqlite`, the built-in SQLite module — no native binaries to compile).
49
+ - OpenCode with plugin support (`@opencode-ai/plugin` >= 1.17).
50
+
51
+ > **Runtimes**:
52
+ > - **Bun**: uses `bun:sqlite` (built-in).
53
+ > - **Node 24+**: uses `node:sqlite` directly, no flags needed (emits an experimental warning, harmless).
54
+ > - **Node 22/23 without `--experimental-sqlite` flag** or **Node 20**: falls back to `better-sqlite3`, declared as `optionalDependencies`. If you need it, install it manually in your opencode config directory (`~/.config/opencode/`): `npm install better-sqlite3`.
50
55
 
51
56
  ### Verification
52
57
 
@@ -226,7 +231,7 @@ npm publish --access public
226
231
  ```
227
232
  plugin/
228
233
  index.ts # Entry point: KevinPlugin
229
- Store.ts # Wrapper better-sqlite3 (WAL, FK, transactions)
234
+ Store.ts # Wrapper SQLite (node:sqlite / bun:sqlite / better-sqlite3 fallback)
230
235
  Migrate.ts # Idempotent migrations
231
236
  MemoryService.ts # save/query/getRelevant (FTS5 + bm25)
232
237
  ToolCallObserver.ts # onBefore/onAfter + redact + inferErrorType
@@ -67,16 +67,16 @@ export class MemoryService {
67
67
  expiresAt = sqliteUtcNowPlusHours(SESSION_DEFAULT_TTL_HOURS);
68
68
  }
69
69
  this.store
70
- .prepare(`INSERT INTO memories
71
- (id, type, content, scope, relevance_score, source_tool, source_session, metadata, expires_at)
70
+ .prepare(`INSERT INTO memories
71
+ (id, type, content, scope, relevance_score, source_tool, source_session, metadata, expires_at)
72
72
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
73
73
  .run(id, input.type, input.content, scope, relevanceScore, input.sourceTool ?? null, input.sourceSession ?? null, metadata, expiresAt);
74
74
  return id;
75
75
  }
76
76
  getById(id) {
77
77
  const row = this.store
78
- .prepare(`SELECT id, type, content, scope, relevance_score, source_tool, source_session,
79
- metadata, created_at, updated_at, expires_at
78
+ .prepare(`SELECT id, type, content, scope, relevance_score, source_tool, source_session,
79
+ metadata, created_at, updated_at, expires_at
80
80
  FROM memories WHERE id = ?`)
81
81
  .get(id);
82
82
  return row ? mapRow(row) : null;
@@ -125,14 +125,14 @@ export class MemoryService {
125
125
  return [];
126
126
  const scope = input.scope ?? "all";
127
127
  const limit = input.limit ?? 10;
128
- let sql = `
129
- SELECT m.id, m.type, m.content, m.scope, m.relevance_score,
130
- m.source_tool, m.source_session, m.metadata,
131
- m.created_at, m.updated_at, m.expires_at,
132
- bm25(memories_fts) AS score
133
- FROM memories_fts
134
- JOIN memories m ON m.rowid = memories_fts.rowid
135
- WHERE memories_fts MATCH ?
128
+ let sql = `
129
+ SELECT m.id, m.type, m.content, m.scope, m.relevance_score,
130
+ m.source_tool, m.source_session, m.metadata,
131
+ m.created_at, m.updated_at, m.expires_at,
132
+ bm25(memories_fts) AS score
133
+ FROM memories_fts
134
+ JOIN memories m ON m.rowid = memories_fts.rowid
135
+ WHERE memories_fts MATCH ?
136
136
  AND (m.expires_at IS NULL OR m.expires_at > datetime('now'))`;
137
137
  const params = [match];
138
138
  if (input.type) {
@@ -151,10 +151,10 @@ export class MemoryService {
151
151
  .filter((m) => !isNotSearchable(m));
152
152
  }
153
153
  loadAll(scope) {
154
- let sql = `
155
- SELECT id, type, content, scope, relevance_score, source_tool, source_session,
156
- metadata, created_at, updated_at, expires_at
157
- FROM memories
154
+ let sql = `
155
+ SELECT id, type, content, scope, relevance_score, source_tool, source_session,
156
+ metadata, created_at, updated_at, expires_at
157
+ FROM memories
158
158
  WHERE (expires_at IS NULL OR expires_at > datetime('now'))`;
159
159
  const params = [];
160
160
  if (scope !== "all") {
@@ -172,14 +172,14 @@ export class MemoryService {
172
172
  if (tokens.length === 0)
173
173
  return [];
174
174
  const match = tokens.join(" OR ");
175
- let sql = `
176
- SELECT m.id, m.type, m.content, m.scope, m.relevance_score,
177
- m.source_tool, m.source_session, m.metadata,
178
- m.created_at, m.updated_at, m.expires_at,
179
- bm25(memories_fts) AS score
180
- FROM memories_fts
181
- JOIN memories m ON m.rowid = memories_fts.rowid
182
- WHERE memories_fts MATCH ?
175
+ let sql = `
176
+ SELECT m.id, m.type, m.content, m.scope, m.relevance_score,
177
+ m.source_tool, m.source_session, m.metadata,
178
+ m.created_at, m.updated_at, m.expires_at,
179
+ bm25(memories_fts) AS score
180
+ FROM memories_fts
181
+ JOIN memories m ON m.rowid = memories_fts.rowid
182
+ WHERE memories_fts MATCH ?
183
183
  AND (m.expires_at IS NULL OR m.expires_at > datetime('now'))`;
184
184
  const params = [match];
185
185
  if (scope !== "all") {
@@ -9,7 +9,8 @@ export class Retrospective {
9
9
  constructor(store, memoryService, options) {
10
10
  this.store = store;
11
11
  this.memoryService = memoryService;
12
- this.retrospectivesDir = options?.dir ?? join(homedir(), ".opencode-kevin", "retrospectives");
12
+ this.retrospectivesDir =
13
+ options?.dir ?? join(homedir(), ".opencode-kevin", "retrospectives");
13
14
  }
14
15
  async generate(sessionId) {
15
16
  const existing = this.store
@@ -18,10 +19,10 @@ export class Retrospective {
18
19
  if (existing?.file_path)
19
20
  return existing.file_path;
20
21
  const counts = this.store
21
- .prepare(`SELECT
22
- SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) AS success_count,
23
- SUM(CASE WHEN success = 0 THEN 1 ELSE 0 END) AS failure_count,
24
- COUNT(*) AS total
22
+ .prepare(`SELECT
23
+ SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) AS success_count,
24
+ SUM(CASE WHEN success = 0 THEN 1 ELSE 0 END) AS failure_count,
25
+ COUNT(*) AS total
25
26
  FROM tool_calls WHERE session_id = ?`)
26
27
  .get(sessionId);
27
28
  const successCount = counts?.success_count ?? 0;
@@ -30,14 +31,14 @@ export class Retrospective {
30
31
  if (failureCount === 0)
31
32
  return null;
32
33
  const failedTools = this.store
33
- .prepare(`SELECT tool, args_summary, error_type
34
- FROM tool_calls
35
- WHERE session_id = ? AND success = 0
34
+ .prepare(`SELECT tool, args_summary, error_type
35
+ FROM tool_calls
36
+ WHERE session_id = ? AND success = 0
36
37
  ORDER BY ts ASC`)
37
38
  .all(sessionId);
38
39
  const lessons = this.store
39
- .prepare(`SELECT content FROM memories
40
- WHERE type = 'error' AND source_session = ?
40
+ .prepare(`SELECT content FROM memories
41
+ WHERE type = 'error' AND source_session = ?
41
42
  ORDER BY created_at ASC`)
42
43
  .all(sessionId);
43
44
  const md = this.buildMarkdown(sessionId, total, successCount, failureCount, failedTools, lessons);
@@ -46,8 +47,8 @@ export class Retrospective {
46
47
  writeFileSync(filePath, md, "utf8");
47
48
  const id = uuidv7();
48
49
  this.store
49
- .prepare(`INSERT OR IGNORE INTO retrospectives
50
- (id, session_id, ts, failure_count, success_count, lessons_count, file_path, metadata)
50
+ .prepare(`INSERT OR IGNORE INTO retrospectives
51
+ (id, session_id, ts, failure_count, success_count, lessons_count, file_path, metadata)
51
52
  VALUES (?, ?, datetime('now'), ?, ?, ?, ?, ?)`)
52
53
  .run(id, sessionId, failureCount, successCount, lessons.length, filePath, null);
53
54
  return filePath;
@@ -1 +1 @@
1
- {"version":3,"file":"Retrospective.js","sourceRoot":"","sources":["../../plugin/Retrospective.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Retrospective.js","sourceRoot":"","sources":["../../plugin/Retrospective.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"Store.js","sourceRoot":"","sources":["../../plugin/Store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAsB,MAAM,qBAAqB,CAAC;AAMzE,MAAM,OAAO,KAAK;IACT,EAAE,CAAgB;IAClB,MAAM,GAAG,KAAK,CAAC;IAEvB,YAAY,OAAqB;QAChC,IAAI,CAAC,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QAC1C,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,CAAC,GAAW;QAClB,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACpD,OAAO,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED,WAAW,CAAI,EAAW;QACzB,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACpD,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QACnC,OAAO,EAAE,EAAE,CAAC;IACb,CAAC;IAED,IAAI,CAAC,GAAW;QACf,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACpD,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IAED,KAAK;QACJ,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IACjB,CAAC;IAED,IAAI,GAAG;QACN,OAAO,IAAI,CAAC,EAAE,CAAC;IAChB,CAAC;CACD"}
1
+ {"version":3,"file":"Store.js","sourceRoot":"","sources":["../../plugin/Store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAMzE,MAAM,OAAO,KAAK;IACT,EAAE,CAAgB;IAClB,MAAM,GAAG,KAAK,CAAC;IAEvB,YAAY,OAAqB;QAChC,IAAI,CAAC,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QAC1C,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,CAAC,GAAW;QAClB,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACpD,OAAO,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED,WAAW,CAAI,EAAW;QACzB,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACpD,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QACnC,OAAO,EAAE,EAAE,CAAC;IACb,CAAC;IAED,IAAI,CAAC,GAAW;QACf,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACpD,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IAED,KAAK;QACJ,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IACjB,CAAC;IAED,IAAI,GAAG;QACN,OAAO,IAAI,CAAC,EAAE,CAAC;IAChB,CAAC;CACD"}
@@ -30,8 +30,8 @@ export class ToolCallObserver {
30
30
  : null;
31
31
  const metadata = JSON.stringify(this.redactArgs(input.args ?? {}));
32
32
  this.store
33
- .prepare(`INSERT INTO tool_calls
34
- (id, session_id, ts, tool, args_summary, success, duration_ms, agent, error_type, metadata)
33
+ .prepare(`INSERT INTO tool_calls
34
+ (id, session_id, ts, tool, args_summary, success, duration_ms, agent, error_type, metadata)
35
35
  VALUES (?, ?, datetime('now'), ?, ?, ?, ?, ?, ?, ?)`)
36
36
  .run(uuidv7(), sessionId, input.tool, argsSummary, success, durationMs, agent, errorType, metadata);
37
37
  }
@@ -13,11 +13,66 @@ function loadBunDatabase(path) {
13
13
  const mod = localRequire("bun:sqlite");
14
14
  return new mod.Database(path);
15
15
  }
16
- function loadNodeDatabase(path) {
16
+ /**
17
+ * Wrapper sobre `node:sqlite` (DatabaseSync, estable en Node 24+).
18
+ * API equivalente a better-sqlite3 excepto `transaction`, que implementamos
19
+ * manualmente con BEGIN/COMMIT/ROLLBACK.
20
+ */
21
+ class NodeSqliteAdapter {
22
+ db;
23
+ constructor(db) {
24
+ this.db = db;
25
+ }
26
+ prepare(sql) {
27
+ return this.db.prepare(sql);
28
+ }
29
+ exec(sql) {
30
+ this.db.exec(sql);
31
+ }
32
+ close() {
33
+ this.db.close();
34
+ }
35
+ transaction(fn) {
36
+ return () => {
37
+ this.db.exec("BEGIN");
38
+ try {
39
+ const result = fn();
40
+ this.db.exec("COMMIT");
41
+ return result;
42
+ }
43
+ catch (err) {
44
+ try {
45
+ this.db.exec("ROLLBACK");
46
+ }
47
+ catch {
48
+ // ignore rollback errors
49
+ }
50
+ throw err;
51
+ }
52
+ };
53
+ }
54
+ }
55
+ function loadNodeSqliteDatabase(path) {
56
+ const mod = localRequire("node:sqlite");
57
+ return new NodeSqliteAdapter(new mod.DatabaseSync(path));
58
+ }
59
+ function loadBetterSqliteDatabase(path) {
17
60
  const D = localRequire("better-sqlite3");
18
61
  const Ctor = "default" in D ? D.default : D;
19
62
  return new Ctor(path);
20
63
  }
64
+ function loadNodeDatabase(path) {
65
+ // 1) Prefer node:sqlite (built-in, sin binarios nativos que descargar).
66
+ // Estable en Node 24+ sin flag; en Node 22/23 requiere --experimental-sqlite.
67
+ // Si require falla (modulo ausente o flag deshabilitado), cae al fallback.
68
+ try {
69
+ return loadNodeSqliteDatabase(path);
70
+ }
71
+ catch {
72
+ // 2) Fallback a better-sqlite3 (Node 20 o Node 22/23 sin flag).
73
+ return loadBetterSqliteDatabase(path);
74
+ }
75
+ }
21
76
  export function createDatabase(path) {
22
77
  if (detectRuntime() === "bun") {
23
78
  return loadBunDatabase(path);
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite-adapter.js","sourceRoot":"","sources":["../../plugin/sqlite-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAezC,MAAM,YAAY,GAAG,aAAa,CACjC,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,QAAQ;IAClC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG;IACjB,CAAC,CAAC,aAAa,CAAC,UAAU,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAC5C,CAAC;AAEF,SAAS,aAAa;IACrB,IAAI,OAAQ,UAAgC,CAAC,GAAG,KAAK,WAAW,EAAE,CAAC;QAClE,OAAO,KAAK,CAAC;IACd,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC;AAED,SAAS,eAAe,CAAC,IAAY;IACpC,MAAM,GAAG,GAAG,YAAY,CAAC,YAAY,CAEpC,CAAC;IACF,OAAO,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY;IACrC,MAAM,CAAC,GAAG,YAAY,CAAC,gBAAgB,CAES,CAAC;IACjD,MAAM,IAAI,GAAG,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IAC1C,IAAI,aAAa,EAAE,KAAK,KAAK,EAAE,CAAC;QAC/B,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC"}
1
+ {"version":3,"file":"sqlite-adapter.js","sourceRoot":"","sources":["../../plugin/sqlite-adapter.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,50 +1,48 @@
1
- {
2
- "name": "@jmtrin/opencode-kevin",
3
- "version": "0.1.1",
4
- "description": "Kevin — Observa y aprende: capa de aprendizaje para OpenCode",
5
- "type": "module",
6
- "main": "dist/plugin/index.js",
7
- "exports": {
8
- ".": {
9
- "import": "./dist/plugin/index.js",
10
- "types": "./dist/plugin/index.d.ts"
11
- }
12
- },
13
- "files": [
14
- "dist/plugin",
15
- "dist/migrations",
16
- "migrations"
17
- ],
18
- "scripts": {
19
- "build": "tsc --outDir dist && node scripts/copy-migrations.mjs",
20
- "prepublishOnly": "npm run build",
21
- "test": "vitest run",
22
- "test:unit": "vitest run tests/unit",
23
- "test:integration": "vitest run tests/integration",
24
- "test:e2e": "vitest run tests/e2e",
25
- "typecheck": "tsc --noEmit",
26
- "lint": "biome check .",
27
- "format": "biome format --write .",
28
- "verify": "node --import tsx scripts/verify-install.ts"
29
- },
30
- "dependencies": {
31
- "@opencode-ai/plugin": "^1.17.6",
32
- "better-sqlite3": "^12.11.0",
33
- "zod": "^3.23.8"
34
- },
35
- "devDependencies": {
36
- "@biomejs/biome": "^1.9.4",
37
- "@types/better-sqlite3": "^7.6.12",
38
- "@types/node": "^22.10.0",
39
- "tsx": "^4.19.0",
40
- "typescript": "^5.7.2",
41
- "vitest": "^2.1.8"
42
- },
43
- "engines": {
44
- "node": ">=20.0.0"
45
- },
46
- "publishConfig": {
47
- "access": "public"
48
- },
49
- "license": "MIT"
50
- }
1
+ {
2
+ "name": "@jmtrin/opencode-kevin",
3
+ "version": "0.1.2",
4
+ "description": "Kevin — Observa y aprende: capa de aprendizaje para OpenCode",
5
+ "type": "module",
6
+ "main": "dist/plugin/index.js",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/plugin/index.js",
10
+ "types": "./dist/plugin/index.d.ts"
11
+ }
12
+ },
13
+ "files": ["dist/plugin", "dist/migrations", "migrations"],
14
+ "scripts": {
15
+ "build": "tsc --outDir dist && node scripts/copy-migrations.mjs",
16
+ "prepublishOnly": "npm run build",
17
+ "test": "vitest run",
18
+ "test:unit": "vitest run tests/unit",
19
+ "test:integration": "vitest run tests/integration",
20
+ "test:e2e": "vitest run tests/e2e",
21
+ "typecheck": "tsc --noEmit",
22
+ "lint": "biome check .",
23
+ "format": "biome format --write .",
24
+ "verify": "node --import tsx scripts/verify-install.ts"
25
+ },
26
+ "dependencies": {
27
+ "@opencode-ai/plugin": "^1.17.6",
28
+ "zod": "^3.23.8"
29
+ },
30
+ "optionalDependencies": {
31
+ "better-sqlite3": "^12.11.0"
32
+ },
33
+ "devDependencies": {
34
+ "@biomejs/biome": "^1.9.4",
35
+ "@types/better-sqlite3": "^7.6.12",
36
+ "@types/node": "^22.10.0",
37
+ "tsx": "^4.19.0",
38
+ "typescript": "^5.7.2",
39
+ "vitest": "^2.1.8"
40
+ },
41
+ "engines": {
42
+ "node": ">=22.5.0"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "license": "MIT"
48
+ }