@jmtrin/opencode-kevin 1.2.0 → 1.4.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.
Files changed (140) hide show
  1. package/README.md +84 -22
  2. package/dist/plugin/index.d.ts +5 -4
  3. package/dist/plugin/index.js +127 -113
  4. package/dist/plugin/native.d.ts +2 -2
  5. package/dist/plugin/native.js +2 -2
  6. package/package.json +56 -68
  7. package/dist/migrations/001_initial.sql +0 -92
  8. package/dist/migrations/002_indexes.sql +0 -14
  9. package/dist/migrations/003_v02_signal.sql +0 -58
  10. package/dist/migrations/004_v03_knowledge.sql +0 -138
  11. package/dist/migrations/005_v04_signal.sql +0 -57
  12. package/dist/migrations/006_v05_glassbox.sql +0 -118
  13. package/dist/migrations/007_v06_pull.sql +0 -145
  14. package/dist/migrations/008_v07_truth.sql +0 -124
  15. package/dist/migrations/009_v08_team.sql +0 -100
  16. package/dist/migrations/010_v09_native.sql +0 -78
  17. package/dist/migrations/011_v10_proven.sql +0 -69
  18. package/dist/migrations/012_v11_drift.sql +0 -24
  19. package/dist/plugin/Archiver.d.ts +0 -42
  20. package/dist/plugin/Archiver.js +0 -83
  21. package/dist/plugin/ArtifactWriter.d.ts +0 -93
  22. package/dist/plugin/ArtifactWriter.js +0 -291
  23. package/dist/plugin/CausalChain.d.ts +0 -22
  24. package/dist/plugin/CausalChain.js +0 -197
  25. package/dist/plugin/ChatBridge.d.ts +0 -41
  26. package/dist/plugin/ChatBridge.js +0 -103
  27. package/dist/plugin/ConflictDetector.d.ts +0 -35
  28. package/dist/plugin/ConflictDetector.js +0 -260
  29. package/dist/plugin/ContextInjector.d.ts +0 -222
  30. package/dist/plugin/ContextInjector.js +0 -533
  31. package/dist/plugin/ConventionMiner.d.ts +0 -35
  32. package/dist/plugin/ConventionMiner.js +0 -242
  33. package/dist/plugin/Curator.d.ts +0 -116
  34. package/dist/plugin/Curator.js +0 -324
  35. package/dist/plugin/DashboardHtml.d.ts +0 -5
  36. package/dist/plugin/DashboardHtml.js +0 -180
  37. package/dist/plugin/Feedback.d.ts +0 -67
  38. package/dist/plugin/Feedback.js +0 -120
  39. package/dist/plugin/HookLiveness.d.ts +0 -88
  40. package/dist/plugin/HookLiveness.js +0 -322
  41. package/dist/plugin/InjectionLedger.d.ts +0 -98
  42. package/dist/plugin/InjectionLedger.js +0 -295
  43. package/dist/plugin/LessonFixer.d.ts +0 -44
  44. package/dist/plugin/LessonFixer.js +0 -45
  45. package/dist/plugin/Materializer.d.ts +0 -83
  46. package/dist/plugin/Materializer.js +0 -181
  47. package/dist/plugin/MemoryService.d.ts +0 -360
  48. package/dist/plugin/MemoryService.js +0 -1242
  49. package/dist/plugin/Migrate.d.ts +0 -17
  50. package/dist/plugin/Migrate.js +0 -271
  51. package/dist/plugin/PatternMiner.d.ts +0 -49
  52. package/dist/plugin/PatternMiner.js +0 -132
  53. package/dist/plugin/QualityGate.d.ts +0 -124
  54. package/dist/plugin/QualityGate.js +0 -119
  55. package/dist/plugin/Reflector.d.ts +0 -91
  56. package/dist/plugin/Reflector.js +0 -307
  57. package/dist/plugin/RepoIdentity.d.ts +0 -124
  58. package/dist/plugin/RepoIdentity.js +0 -300
  59. package/dist/plugin/RepoTruth.d.ts +0 -80
  60. package/dist/plugin/RepoTruth.js +0 -599
  61. package/dist/plugin/Retrospective.d.ts +0 -17
  62. package/dist/plugin/Retrospective.js +0 -216
  63. package/dist/plugin/SharedLayer.d.ts +0 -159
  64. package/dist/plugin/SharedLayer.js +0 -462
  65. package/dist/plugin/Store.d.ts +0 -29
  66. package/dist/plugin/Store.js +0 -50
  67. package/dist/plugin/ToolCallObserver.d.ts +0 -32
  68. package/dist/plugin/ToolCallObserver.js +0 -201
  69. package/dist/plugin/TuiActions.d.ts +0 -43
  70. package/dist/plugin/TuiActions.js +0 -181
  71. package/dist/plugin/TuiSnapshots.d.ts +0 -24
  72. package/dist/plugin/TuiSnapshots.js +0 -158
  73. package/dist/plugin/columns.d.ts +0 -11
  74. package/dist/plugin/columns.js +0 -54
  75. package/dist/plugin/confidence.d.ts +0 -8
  76. package/dist/plugin/confidence.js +0 -34
  77. package/dist/plugin/contract.d.ts +0 -54
  78. package/dist/plugin/contract.js +0 -386
  79. package/dist/plugin/diff.d.ts +0 -8
  80. package/dist/plugin/diff.js +0 -182
  81. package/dist/plugin/escape.d.ts +0 -39
  82. package/dist/plugin/escape.js +0 -60
  83. package/dist/plugin/fingerprint.d.ts +0 -27
  84. package/dist/plugin/fingerprint.js +0 -73
  85. package/dist/plugin/inferability.d.ts +0 -32
  86. package/dist/plugin/inferability.js +0 -88
  87. package/dist/plugin/kevin_approve.d.ts +0 -34
  88. package/dist/plugin/kevin_approve.js +0 -52
  89. package/dist/plugin/kevin_audit.d.ts +0 -229
  90. package/dist/plugin/kevin_audit.js +0 -631
  91. package/dist/plugin/kevin_bench.d.ts +0 -10
  92. package/dist/plugin/kevin_bench.js +0 -82
  93. package/dist/plugin/kevin_conflicts.d.ts +0 -9
  94. package/dist/plugin/kevin_conflicts.js +0 -50
  95. package/dist/plugin/kevin_contract.d.ts +0 -8
  96. package/dist/plugin/kevin_contract.js +0 -59
  97. package/dist/plugin/kevin_doctor.d.ts +0 -62
  98. package/dist/plugin/kevin_doctor.js +0 -196
  99. package/dist/plugin/kevin_facts.d.ts +0 -42
  100. package/dist/plugin/kevin_facts.js +0 -36
  101. package/dist/plugin/kevin_forget.d.ts +0 -33
  102. package/dist/plugin/kevin_forget.js +0 -260
  103. package/dist/plugin/kevin_native.d.ts +0 -29
  104. package/dist/plugin/kevin_native.js +0 -79
  105. package/dist/plugin/kevin_propose.d.ts +0 -23
  106. package/dist/plugin/kevin_propose.js +0 -14
  107. package/dist/plugin/kevin_publish.d.ts +0 -38
  108. package/dist/plugin/kevin_publish.js +0 -18
  109. package/dist/plugin/kevin_why.d.ts +0 -23
  110. package/dist/plugin/kevin_why.js +0 -111
  111. package/dist/plugin/memory-format.d.ts +0 -20
  112. package/dist/plugin/memory-format.js +0 -59
  113. package/dist/plugin/metrics.d.ts +0 -97
  114. package/dist/plugin/metrics.js +0 -288
  115. package/dist/plugin/okf-export.d.ts +0 -3
  116. package/dist/plugin/okf-export.js +0 -134
  117. package/dist/plugin/okf-import.d.ts +0 -76
  118. package/dist/plugin/okf-import.js +0 -271
  119. package/dist/plugin/okf.d.ts +0 -107
  120. package/dist/plugin/okf.js +0 -303
  121. package/dist/plugin/perf.d.ts +0 -35
  122. package/dist/plugin/perf.js +0 -174
  123. package/dist/plugin/query-tokenizer.d.ts +0 -13
  124. package/dist/plugin/query-tokenizer.js +0 -133
  125. package/dist/plugin/redact.d.ts +0 -2
  126. package/dist/plugin/redact.js +0 -18
  127. package/dist/plugin/replay-types.d.ts +0 -69
  128. package/dist/plugin/replay-types.js +0 -156
  129. package/dist/plugin/replay.d.ts +0 -36
  130. package/dist/plugin/replay.js +0 -202
  131. package/dist/plugin/sqlite-adapter.d.ts +0 -12
  132. package/dist/plugin/sqlite-adapter.js +0 -81
  133. package/dist/plugin/time-ms.d.ts +0 -1
  134. package/dist/plugin/time-ms.js +0 -16
  135. package/dist/plugin/tui-types.d.ts +0 -59
  136. package/dist/plugin/tui-types.js +0 -4
  137. package/dist/plugin/tui.d.ts +0 -18
  138. package/dist/plugin/tui.js +0 -198
  139. package/dist/plugin/uuid.d.ts +0 -1
  140. package/dist/plugin/uuid.js +0 -50
package/README.md CHANGED
@@ -26,7 +26,7 @@ injects exactly what matters back into the model's context, curates the best
26
26
  of it into files you control, and shares it across a team through one
27
27
  git-friendly file — deterministically, locally, with zero network calls.
28
28
 
29
- ![version](https://img.shields.io/badge/version-1.1.0-blue)
29
+ ![version](https://img.shields.io/badge/version-1.4.0-blue)
30
30
  ![node](https://img.shields.io/badge/node-%E2%89%A522.5-green)
31
31
  ![tests](https://img.shields.io/badge/tests-1380%20passing-brightgreen)
32
32
  ![deps](https://img.shields.io/badge/runtime%20deps-1-orange)
@@ -48,10 +48,14 @@ git-friendly file — deterministically, locally, with zero network calls.
48
48
  - [Why Kevin](#-why-kevin)
49
49
  - [The Kevin loop](#-the-kevin-loop)
50
50
  - [Quick start](#-quick-start)
51
- - [What's new in 1.1.0](#-whats-new-in-110)
51
+ - [What's new in 1.4.0 — Bridge](#-whats-new-in-140--bridge)
52
+ - [What's new in 1.3.0 — Bedrock](#-whats-new-in-130--bedrock)
53
+ - [What's new in 1.2.0 — Surface](#-whats-new-in-120--surface)
54
+ - [What's new in 1.1.0 — Drift](#-whats-new-in-110--drift)
52
55
  - [What's new in 1.0.0](#-whats-new-in-100)
53
56
  - [How it works](#-how-it-works)
54
- - [The 26 tools](#-the-26-tools)
57
+ - [The 26 tools (plugin) + 11 MCP tools](#-the-26-tools-plugin--11-mcp-tools)
58
+ - [MCP Bridge — cross-harness memory](#-mcp-bridge--cross-harness-memory)
55
59
  - [The benchmark: proven, not promised](#-the-benchmark-proven-not-promised)
56
60
  - [Curation: from session noise to AGENTS.md](#-curation-from-session-noise-to-agentsmd)
57
61
  - [Team sharing: one file, zero servers](#-team-sharing-one-file-zero-servers)
@@ -141,7 +145,7 @@ npm install @jmtrin/opencode-kevin
141
145
 
142
146
  ### 2. Restart OpenCode
143
147
 
144
- On first boot Kevin migrates its database to schema version `012` and starts
148
+ On first boot Kevin migrates its database to schema version `013` and starts
145
149
  observing. Nothing else is required.
146
150
 
147
151
  ### 3. Talk to it
@@ -176,6 +180,35 @@ kevin_doctor → health report: hooks, deps, perf, verdict
176
180
 
177
181
  ---
178
182
 
183
+ ## 🆕 What's new in 1.4.0 — "Bridge"
184
+
185
+ > 1.4.0 opens Kevin to every MCP harness — same local DB, same ranked recall, no network.
186
+
187
+ - 🌉 **MCP Bridge** — new package `@jmtrin/kevin-mcp` (stdio MCP server, SDK 1.30.0): 7 read tools (`query/get/recall/why/status/trace/feedback` with provenance `{repo_id, identity_source, channel:"mcp"}` + token ceiling `ceil(chars/4)` + `pull_mcp` ledger), 3 gated write tools (`save/approve/share` behind `mcp_write_enabled` / `mcp_approve_enabled` double-gate + `mcp_writes_refused/accepted`), `ping` liveness — single binary `npx @jmtrin/kevin-mcp [--repo <id>]`, stderr `ready` line, SIGINT/SIGTERM flush.
188
+ - 🔐 **Identity & repo scope** — `RepoIdentity` (declared → remote → path hex-16), `mcp_repo_override` setting, `--repo` CLI, scope guard `repo_mismatch` on every tool (read + write), resolves once at init (heals OKF header on rekey).
189
+ - 🗄️ **Migration 013** — `channel TEXT DEFAULT 'plugin' CHECK(plugin|mcp)`, `injected_at_ms` probe, 5 metrics `mcp_requests_total/reads_served/writes_accepted/writes_refused/errors_total`, `schema_version 013`; `busy_timeout=5000` on Store for plugin↔MCP WAL concurrency; pre-013 DBs omit `mcp` block in `kevin_audit` (partial:true).
190
+ - 📊 **Contracts & audit** — `C-04/C-05` since `1.4.0` (3 MCP config keys, 5 MCP metrics), `C-07` schema `013`, golden `v1.json` 35/61, `METRIC_KEY_LABELS`, `kevin_audit` gains `mcp{requests,reads,writes_accepted/refused,errors,channel_split{plugin,mcp}}`.
191
+ - ⏱️ **Perf** — `mcp.read` p95 25/max 100, `mcp.write` p95 50/max 250, flush every 100 requests or SIGINT to `perf_samples`; concurrency stress 500×500 interleaved ops, zero `SQLITE_BUSY`.
192
+ - 📚 **Harnesses & demo** — recipes for Claude Code / Codex / Cursor / Windsurf / Gemini CLI / Opencode (`docs/harnesses/*.md`, valid JSON blocks), 10-min cross-harness demo `docs/demo-cross-harness.md`, release ordering `core→tui→plugin→mcp` in `docs/DISTRIBUTION.md`.
193
+
194
+ **Upgrade:** `npm i @jmtrin/kevin-core@1.4.0 @jmtrin/opencode-kevin@1.4.0 @jmtrin/opencode-kevin-tui@1.4.0 @jmtrin/kevin-mcp@1.4.0` — DB auto-migrates to 013; writes remain gated off until `mcp_write_enabled='1'`.
195
+
196
+ ---
197
+
198
+ ## 🆕 What's new in 1.3.0 — "Bedrock"
199
+
200
+ > 1.3.0 declares the border the code already had. No visible behavior change — everything after this changes *because* of it.
201
+
202
+ - 🏗️ **Monorepo with a hostless core** — root becomes a private `npm workspaces` manager; `packages/core` (`@jmtrin/kevin-core` **zero deps**), `packages/plugin` (`@jmtrin/opencode-kevin` — name frozen by C-06, now depends on core `1.3.0` exact), `packages/tui` (`@jmtrin/opencode-kevin-tui`, isolated). 60+ domain modules `plugin/*.ts` → `packages/core/src/*.ts` via `git mv`, migrations `migrations/` → `packages/core/migrations/` (12 SQL, built to `dist/migrations`).
203
+ - 🔌 **One new type + one new export** (`KevinEnv` + `exportMigrationsDir`): every `process.cwd()`/`homedir()` touchpoint now takes an injected `KevinEnv {projectRoot,dataRoot}` via `resolveEnv()` (defaults `cwd` / `~/.opencode-kevin` only in `env.ts`). Core scans as `0` `process.cwd/homedir/node:os` outside the allowlist (K13-007), verified by `packages/core/tests/core_purity_scan.test.ts`.
204
+ - 🔄 **Replay lives in core** — `replay.ts` + `idle-pipeline.ts` (`IDLE_STEP_ORDER` single source, `composeIdlePipeline`) moved to core; adapter and replay both mount the same pipeline (D13-07). Parity harness `packages/plugin/tests/parity.test.ts` mounts adapter vs core wirings over every `tests/replay/fixtures/*.json` and asserts byte-identical outputs (swapped-step probe proves sensitivity).
205
+ - 📦 **Packaging proof** — both tarballs verified + offline consumer smoke (`npm install <core.tgz> <tui.tgz> <plugin.tgz>` → `Store` + `Migrate` + `exportMigrationsDir` → `schema_version 012`). Core zero-deps and `types`-first exports; plugin pins core exact `1.3.0`, `exports["./tui"]` redirects to the tui package — external specifiers unchanged.
206
+ - 📜 **Contract unchanged** — `describeContract({scanRoots})` now accepts explicit roots (monorepo vs packed), golden values byte-equal (D13-05, 26/32/56 unchanged, no new setting/tool/metric/migration).
207
+
208
+ **Upgrade:** no action required — drop-in reorganization, behavior diff empty (K13-016), DB untouched.
209
+
210
+ ---
211
+
179
212
  ## 🆕 What's new in 1.1.0 — "Drift"
180
213
 
181
214
  > 1.1.0 protects what 1.0.0 proved: a published number without a regression gate is marketing.
@@ -276,7 +309,7 @@ Kevin is an intentionally deterministic pipeline — no LLM in the core loop:
276
309
 
277
310
  ---
278
311
 
279
- ## 🧰 The 26 tools
312
+ ## 🧰 The 26 tools (plugin) + 11 MCP tools
280
313
 
281
314
  <details open>
282
315
  <summary><b>🧠 Core memory</b></summary>
@@ -336,6 +369,22 @@ Kevin is an intentionally deterministic pipeline — no LLM in the core loop:
336
369
 
337
370
  ---
338
371
 
372
+ ## 🌉 MCP Bridge — cross-harness memory
373
+
374
+ Kevin's SQLite file becomes accessible outside OpenCode via the MCP bridge — same ranking, same gates, same file you already trust.
375
+
376
+ | Surface | Install | Transport | Reads | Writes |
377
+ |---|---|---|---|---|
378
+ | **MCP** `@jmtrin/kevin-mcp` | `npx -y @jmtrin/kevin-mcp` ( `--repo <id>` optional) | stdio (SDK 1.30.0), no HTTP/SSE | 7 tools always on (query/get/recall/why/status/trace/feedback + ping) | 3 tools gated (`save` needs `mcp_write_enabled='1'`, `approve/share` need + `mcp_approve_enabled='1'`) |
379
+
380
+ - **One DB, two processes** — plugin and MCP server share `.opencode-kevin/kevin.db` via WAL + `busy_timeout=5000`; every read/write is repo-scoped (`repo_mismatch` on drift).
381
+ - **Provenance** — every MCP response carries `{repo_id, identity_source, channel:"mcp"}` (plus confidence/evidence when applicable).
382
+ - **Harnesses** — `docs/harnesses/{claude-code,codex,cursor,windsurf,gemini-cli,opencode}.md` with exact JSON/TOML snippets, verification, troubleshooting, uninstall; demo `docs/demo-cross-harness.md` (opencode → Claude Code recall).
383
+
384
+ See `packages/mcp/README.md` for full MCP reference.
385
+
386
+ ---
387
+
339
388
  ## 📊 The benchmark: proven, not promised
340
389
 
341
390
  Kevin ships a committed synthetic corpus and a four-arm harness measuring
@@ -458,7 +507,7 @@ removals and silent changes fail loudly; additions must carry `since`.
458
507
 
459
508
  ## ⏱️ Hooks & latency budgets
460
509
 
461
- Six host hooks plus Kevin's own `dispose` checkpoint — eight measured scopes,
510
+ Six host hooks plus Kevin's own `dispose` checkpoint plus two MCP bridges ten measured scopes,
462
511
  each with a declared p95/max budget enforced by `npm run bench:check`:
463
512
 
464
513
  | Scope | p95 budget | max |
@@ -471,6 +520,8 @@ each with a declared p95/max budget enforced by `npm run bench:check`:
471
520
  | `event` | 5 ms | 25 ms |
472
521
  | `session.idle` | 150 ms | 600 ms |
473
522
  | `dispose` | 50 ms | 250 ms |
523
+ | `mcp.read` | 25 ms | 100 ms |
524
+ | `mcp.write` | 50 ms | 250 ms |
474
525
 
475
526
  Measured on the reference laptop: retrieval p50 ≈ 0.2 ms, p95 < 1 ms — orders
476
527
  of magnitude under budget. Samples persist to `perf_samples` at idle; a breach
@@ -518,6 +569,9 @@ client). All values are TEXT — flags compare with `=== "1"`, never truthiness.
518
569
  | `perf_flush_on_idle` | `'1'` | Persist samples at idle |
519
570
  | `contract_report_enabled` | `'1'` | Contract block in `kevin_audit` |
520
571
  | `tui_snapshots_enabled` | `'1'` | Snapshot + dashboard flush at idle (opencode-kevin/tui) |
572
+ | `mcp_write_enabled` | `'0'` | Gate for MCP `save` (refused counter `mcp_writes_refused`) |
573
+ | `mcp_approve_enabled` | `'0'` | Double-gate for MCP `approve`/`share` (needs + `mcp_write_enabled`) |
574
+ | `mcp_repo_override` | `''` | Override `RepoIdentity` for MCP (hex-16 or empty) |
521
575
 
522
576
  ---
523
577
 
@@ -530,8 +584,7 @@ client). All values are TEXT — flags compare with `=== "1"`, never truthiness.
530
584
  | Node 22.5+ | `node:sqlite` behind `--experimental-sqlite` | 🔶 Works, unsupported — exercised in CI, not promised |
531
585
  | Bun ≥ 1.1 | `bun:sqlite` | ✅ **Supported** — smoke-tested in `npm run verify` |
532
586
 
533
- Zero process spawns, zero network calls — asserted by source scan on every
534
- test run.
587
+ Zero network calls — asserted by source scan (forbidden: http/https/net/dgram/fetch/SSE/HttpTransport/child_process/spawn) on every test run; MCP uses stdio only, no spawn, logs on stderr only.
535
588
 
536
589
  ---
537
590
 
@@ -571,26 +624,35 @@ instrumentation. That is what Kevin adds around it.
571
624
  ## 🛠️ Development
572
625
 
573
626
  ```bash
574
- npm install
575
- npm run typecheck # tsc --noEmit (strict)
576
- npm run lint # biome
577
- npm test # vitest — 1374 tests across 190 files
578
- npm run verify # install checks + Bun smoke + verify:pack
579
- npm run gen:corpus # regenerate the seeded corpus (byte-identical)
580
- npm run replay # replay recorded sessions deterministically
627
+ npm install # hoists workspaces (root private, 4 packages)
628
+ npm run build # core → tui → plugin → mcp (tsc + copy-migrations)
629
+ npm run typecheck # -w core -w tui -w plugin -w mcp + root (strict)
630
+ npm run lint # biome
631
+ npm test # vitest root suite (190 files)
632
+ npm test -w @jmtrin/kevin-core # core only also passes with @opencode-ai/plugin absent (K13-013)
633
+ npm run verify # install checks + Bun smoke + verify:pack (×2 + consumer)
634
+ npm run verify:pack # dual-tarball + offline consumer smoke (K13-014)
635
+ npm run gen:corpus # regenerate the seeded corpus (byte-identical)
636
+ npm run replay # replay recorded sessions deterministically (now via core)
581
637
  ```
582
638
 
583
- Project layout:
639
+ Project layout (Bedrock monorepo):
584
640
 
585
641
  ```
586
- plugin/ 53 modules Store, MemoryService, Reflector, Perf, Contract, columns, kevin_forget …
587
- migrations/ 001 012 additive, idempotent, forward-only forever
588
- scripts/ bench · gen-corpus · verify-pack · verify-install ·
589
- tests/ unit · integration · e2e · replay fixtures
590
- bench/ committed corpus + committed results
591
- docs/ CONTRACT.md · per-release plans/tasks · roadmap
642
+ packages/core/ @jmtrin/kevin-core~60 modules (zero deps), src/*.ts, migrations/ 001→013, dist/migrations
643
+ packages/plugin/ @jmtrin/opencode-kevin 4 modules (index, host, native, capabilities), adapter thin, depends on core+tui 1.4.0 exact
644
+ packages/tui/ @jmtrin/opencode-kevin-tui isolated TUI panel (target-exclusive, own package.json/exports)
645
+ packages/mcp/ @jmtrin/kevin-mcp MCP bridge (stdio, 11 tools, pure helper, identity+provenance)
646
+ scripts/ bench · gen-corpus · verify-pack (dual) · verify-install · …
647
+ tests/ unit · integration · e2e · replay fixtures (at root, run via workspaces)
648
+ bench/ committed corpus + committed results
649
+ docs/ CONTRACT.md · per-release plans/tasks · roadmap v2
592
650
  ```
593
651
 
652
+ > **C-10 preview** — the future public surface is the explicit re-export list at `packages/core/src/index.ts` (keep it minimal and deliberate).
653
+
654
+ C-06 frozen: `plugin` package name `@jmtrin/opencode-kevin` and `exports["./tui"]` specifier unchanged; consumers see no break.
655
+
594
656
  ---
595
657
 
596
658
  ## 📄 License
@@ -1,5 +1,6 @@
1
1
  import type { Plugin } from "@opencode-ai/plugin";
2
- import { Store } from "./Store.js";
2
+ import { Store } from "@jmtrin/kevin-core";
3
+ import { KEVIN_VERSION } from "@jmtrin/kevin-core";
3
4
  export interface KevinPluginOptions {
4
5
  dbPath?: string;
5
6
  migrationsDir?: string;
@@ -16,10 +17,10 @@ export interface KevinPluginOptions {
16
17
  * v0.4.0 (K4-021) — settings surfaced by `kevin_config` (plan §8.8).
17
18
  * Unknown keys are rejected on `set` unless `strict: false`.
18
19
  */
19
- export declare const KEVIN_CONFIG_KEYS: readonly ["quality_gate_enabled", "lesson_snippet_injection", "patternminer_enabled", "cross_project_enabled", "llm_reflection_enabled", "tool_calls_dedup_enabled", "deterministic_retrieval", "pre_prompt_budget_tokens", "archive_after_days", "curation_enabled", "agents_md_path", "skill_emission_enabled", "reference_emission_enabled", "injection_confidence_floor", "repo_truth_enabled", "convention_mining_enabled", "conflict_detection_enabled", "error_lesson_mode", "shared_layer_enabled", "okf_path", "share_requires_approval", "author_identity_mode", "shared_confidence_floor", "hook_liveness_enabled", "native_registration_enabled", "host_probe_history_enabled", "dead_hook_report_threshold", "perf_enabled", "perf_ring_capacity", "perf_flush_on_idle", "contract_report_enabled", "tui_snapshots_enabled"];
20
+ export declare const KEVIN_CONFIG_KEYS: readonly ["quality_gate_enabled", "lesson_snippet_injection", "patternminer_enabled", "cross_project_enabled", "llm_reflection_enabled", "tool_calls_dedup_enabled", "deterministic_retrieval", "pre_prompt_budget_tokens", "archive_after_days", "curation_enabled", "agents_md_path", "skill_emission_enabled", "reference_emission_enabled", "injection_confidence_floor", "repo_truth_enabled", "convention_mining_enabled", "conflict_detection_enabled", "error_lesson_mode", "shared_layer_enabled", "okf_path", "share_requires_approval", "author_identity_mode", "shared_confidence_floor", "hook_liveness_enabled", "native_registration_enabled", "host_probe_history_enabled", "dead_hook_report_threshold", "perf_enabled", "perf_ring_capacity", "perf_flush_on_idle", "contract_report_enabled", "tui_snapshots_enabled", "mcp_write_enabled", "mcp_approve_enabled", "mcp_repo_override"];
20
21
  export declare const ERROR_LESSON_MODE_VALUES: readonly ["all", "triage_only"];
21
- /** Plugin release version — stamped into generated files (K8-021/027). */
22
- export declare const KEVIN_VERSION = "1.2.0";
22
+ /** Plugin release version — single source is @jmtrin/kevin-core (B-003 drift fix). */
23
+ export { KEVIN_VERSION };
23
24
  export interface RekeyCounts {
24
25
  memories: number;
25
26
  shared_entries: number;
@@ -1,55 +1,56 @@
1
1
  import { existsSync, mkdirSync } from "node:fs";
2
+ import { createRequire } from "node:module";
2
3
  import { homedir } from "node:os";
3
4
  import { dirname, join } from "node:path";
4
5
  import { fileURLToPath } from "node:url";
5
6
  import { tool } from "@opencode-ai/plugin";
6
- import { Archiver } from "./Archiver.js";
7
- import { ArtifactWriter } from "./ArtifactWriter.js";
8
- import { CausalChain } from "./CausalChain.js";
9
- import { handleBridgeCommand } from "./ChatBridge.js";
10
- import { ConflictDetector } from "./ConflictDetector.js";
11
- import { ContextInjector } from "./ContextInjector.js";
12
- import { ConventionMiner } from "./ConventionMiner.js";
13
- import { Curator } from "./Curator.js";
14
- import { writeDashboard } from "./DashboardHtml.js";
15
- import { Feedback } from "./Feedback.js";
16
- import { HookLiveness } from "./HookLiveness.js";
17
- import { InjectionLedger } from "./InjectionLedger.js";
18
- import { Materializer, SKILL_TOPIC } from "./Materializer.js";
19
- import { MemoryService, hasRepoIdColumn, } from "./MemoryService.js";
20
- import { Migrate } from "./Migrate.js";
21
- import { PatternMiner } from "./PatternMiner.js";
22
- import { ERROR_LINE_RE, Reflector, STRONG_ERROR_RE } from "./Reflector.js";
23
- import * as RepoIdentity from "./RepoIdentity.js";
24
- import { RepoTruth } from "./RepoTruth.js";
25
- import { Retrospective } from "./Retrospective.js";
26
- import { SharedLayer } from "./SharedLayer.js";
27
- import { Store } from "./Store.js";
28
- import { ToolCallObserver } from "./ToolCallObserver.js";
29
- import { deleteMailbox, processActions, readMailbox, writeResults, } from "./TuiActions.js";
30
- import { flushSnapshots } from "./TuiSnapshots.js";
7
+ import { Archiver } from "@jmtrin/kevin-core";
8
+ import { ArtifactWriter } from "@jmtrin/kevin-core";
9
+ import { CausalChain } from "@jmtrin/kevin-core";
10
+ import { handleBridgeCommand } from "@jmtrin/kevin-core";
11
+ import { ConflictDetector } from "@jmtrin/kevin-core";
12
+ import { ContextInjector } from "@jmtrin/kevin-core";
13
+ import { ConventionMiner } from "@jmtrin/kevin-core";
14
+ import { Curator } from "@jmtrin/kevin-core";
15
+ import { writeDashboard } from "@jmtrin/kevin-core";
16
+ import { Feedback } from "@jmtrin/kevin-core";
17
+ import { HookLiveness } from "@jmtrin/kevin-core";
18
+ import { InjectionLedger } from "@jmtrin/kevin-core";
19
+ import { Materializer, SKILL_TOPIC } from "@jmtrin/kevin-core";
20
+ import { MemoryService, hasRepoIdColumn, } from "@jmtrin/kevin-core";
21
+ import { Migrate } from "@jmtrin/kevin-core";
22
+ import { PatternMiner } from "@jmtrin/kevin-core";
23
+ import { ERROR_LINE_RE, Reflector, STRONG_ERROR_RE } from "@jmtrin/kevin-core";
24
+ import * as RepoIdentity from "@jmtrin/kevin-core";
25
+ import { RepoTruth } from "@jmtrin/kevin-core";
26
+ import { Retrospective } from "@jmtrin/kevin-core";
27
+ import { SharedLayer } from "@jmtrin/kevin-core";
28
+ import { Store } from "@jmtrin/kevin-core";
29
+ import { ToolCallObserver } from "@jmtrin/kevin-core";
30
+ import { deleteMailbox, processActions, readMailbox, writeResults, } from "@jmtrin/kevin-core";
31
+ import { flushSnapshots } from "@jmtrin/kevin-core";
31
32
  import { probe } from "./capabilities.js";
32
- import { computeConfidence } from "./confidence.js";
33
- import { contractDigest, describeContract } from "./contract.js";
33
+ import { computeConfidence, exportMigrationsDir, composeIdlePipeline, KEVIN_VERSION } from "@jmtrin/kevin-core";
34
+ import { contractDigest, describeContract } from "@jmtrin/kevin-core";
34
35
  import { probeHost, summarize } from "./host.js";
35
- import { kevinApprove } from "./kevin_approve.js";
36
- import { buildAudit } from "./kevin_audit.js";
37
- import { buildKevinBench } from "./kevin_bench.js";
38
- import { executeKevinConflicts } from "./kevin_conflicts.js";
39
- import { buildKevinContract } from "./kevin_contract.js";
40
- import { buildDoctor } from "./kevin_doctor.js";
41
- import { buildKevinFacts } from "./kevin_facts.js";
42
- import { handleForget } from "./kevin_forget.js";
43
- import { handleNative } from "./kevin_native.js";
44
- import { kevinPropose } from "./kevin_propose.js";
45
- import { kevinPublish } from "./kevin_publish.js";
46
- import { kevinWhy } from "./kevin_why.js";
47
- import { Metrics } from "./metrics.js";
36
+ import { kevinApprove } from "@jmtrin/kevin-core";
37
+ import { buildAudit } from "@jmtrin/kevin-core";
38
+ import { buildKevinBench } from "@jmtrin/kevin-core";
39
+ import { executeKevinConflicts } from "@jmtrin/kevin-core";
40
+ import { buildKevinContract } from "@jmtrin/kevin-core";
41
+ import { buildDoctor } from "@jmtrin/kevin-core";
42
+ import { buildKevinFacts } from "@jmtrin/kevin-core";
43
+ import { handleForget } from "@jmtrin/kevin-core";
44
+ import { handleNative } from "@jmtrin/kevin-core";
45
+ import { kevinPropose } from "@jmtrin/kevin-core";
46
+ import { kevinPublish } from "@jmtrin/kevin-core";
47
+ import { kevinWhy } from "@jmtrin/kevin-core";
48
+ import { Metrics } from "@jmtrin/kevin-core";
48
49
  import { attachNative } from "./native.js";
49
- import { exportMarkdown, exportOkf } from "./okf-export.js";
50
- import { importOkf } from "./okf-import.js";
51
- import { Perf } from "./perf.js";
52
- import { uuidv7 } from "./uuid.js";
50
+ import { exportMarkdown, exportOkf } from "@jmtrin/kevin-core";
51
+ import { importOkf } from "@jmtrin/kevin-core";
52
+ import { Perf } from "@jmtrin/kevin-core";
53
+ import { uuidv7 } from "@jmtrin/kevin-core";
53
54
  // v0.6.0 (K6-019) — process-global set of reference topics already
54
55
  // registered with the host this process. Keeps re-registration idempotent
55
56
  // across sessions within one process: a topic registered once is never
@@ -122,6 +123,11 @@ export const KEVIN_CONFIG_KEYS = [
122
123
  // return { error: "unknown_key" } while `kevin_config list` still
123
124
  // shows it.
124
125
  "tui_snapshots_enabled",
126
+ // v1.4.0 (K14-006 / plan §4) — the three MCP bridge settings seeded by migration 013
127
+ // Omitting makes `kevin_config set` return { error: "unknown_key" } while list shows them.
128
+ "mcp_write_enabled",
129
+ "mcp_approve_enabled",
130
+ "mcp_repo_override",
125
131
  ];
126
132
  // v1.1.0 (K11-007 / D11-08) — no new settings in 1.1.0; thresholds are constants (D11-03)
127
133
  // v0.7.0 (K7-003 / plan §5.6, D7-12) — the explicit VALUE domain for
@@ -131,11 +137,41 @@ export const KEVIN_CONFIG_KEYS = [
131
137
  // at the surface rather than silently changing every installation's
132
138
  // behaviour on the next reflection.
133
139
  export const ERROR_LESSON_MODE_VALUES = ["all", "triage_only"];
134
- /** Plugin release version — stamped into generated files (K8-021/027). */
135
- export const KEVIN_VERSION = "1.2.0";
140
+ /** Plugin release version — single source is @jmtrin/kevin-core (B-003 drift fix). */
141
+ export { KEVIN_VERSION };
136
142
  function resolveMigrationsDir() {
143
+ // K13-008 (D13-04): migrations now owned by @jmtrin/kevin-core.
144
+ // Explicit option is handled at call site (opts.migrationsDir ?? resolve...),
145
+ // so this helper only resolves the core location.
146
+ // 1. Installed layout: walk-up via require.resolve (reuses host.ts strategy)
147
+ try {
148
+ const pkgJson = createRequire(import.meta.url).resolve("@jmtrin/kevin-core/package.json");
149
+ const cand = join(dirname(pkgJson), "dist", "migrations");
150
+ if (existsSync(cand))
151
+ return cand;
152
+ }
153
+ catch { }
154
+ // 2. Workspace sources: delegate to core's own resolver (handles both
155
+ // src via tsx and dist after build).
156
+ try {
157
+ const dir = exportMigrationsDir();
158
+ if (existsSync(dir))
159
+ return dir;
160
+ }
161
+ catch { }
162
+ // 3. Monorepo fallback probes (dev without build)
137
163
  const here = dirname(fileURLToPath(import.meta.url));
138
- return join(here, "..", "migrations");
164
+ const candidates = [
165
+ join(here, "..", "..", "core", "dist", "migrations"),
166
+ join(here, "..", "..", "core", "migrations"),
167
+ join(here, "..", "migrations"),
168
+ join(here, "..", "..", "migrations"),
169
+ ];
170
+ for (const c of candidates) {
171
+ if (existsSync(c))
172
+ return c;
173
+ }
174
+ return join(here, "..", "..", "core", "migrations");
139
175
  }
140
176
  const REKEY_TABLES = ["memories", "shared_entries", "okf_imports"];
141
177
  export function performRekey(store, toRepoId, opts) {
@@ -337,6 +373,8 @@ export const KevinPlugin = async (input, options) => {
337
373
  // v0.7.0 (K7-009 / plan §5.1, D7-13) — the repository truth scanner reads
338
374
  // the JSON project files. Runs once at init, gated by repo_truth_enabled.
339
375
  const projectRoot = opts.projectRoot ?? process.cwd();
376
+ const materializerRoot = opts.materializerRoot ?? join(homedir(), ".opencode-kevin");
377
+ const kevinEnv = { projectRoot, dataRoot: materializerRoot };
340
378
  const repoTruth = new RepoTruth(store, projectId, projectRoot, metrics);
341
379
  if (memoryService.getSetting("repo_truth_enabled", "0") === "1") {
342
380
  try {
@@ -376,7 +414,7 @@ export const KevinPlugin = async (input, options) => {
376
414
  const archiver = new Archiver(store, memoryService, metrics);
377
415
  const retrospective = new Retrospective(store, memoryService, {
378
416
  dir: opts.retrospectivesDir,
379
- }, metrics);
417
+ }, metrics, kevinEnv);
380
418
  const patternMiner = new PatternMiner(store, memoryService, metrics);
381
419
  const conventionMiner = new ConventionMiner(store, memoryService, projectId, metrics);
382
420
  const conflictDetector = new ConflictDetector(store, projectId, metrics, repoTruth, memoryService);
@@ -408,13 +446,12 @@ export const KevinPlugin = async (input, options) => {
408
446
  metrics.incr("shared_entries_imported", report.imported);
409
447
  return report;
410
448
  }
411
- let curator = new Curator(store, memoryService, projectId, metrics, repoId);
449
+ let curator = new Curator(store, memoryService, projectId, metrics, repoId, kevinEnv);
412
450
  // v0.6.0 (K6-017/018 / plan §5.6-5.7, D6-13) — pull-channel bundles and
413
451
  // the v2 domain probe. `probe()` runs ONCE at init and the result is
414
452
  // held; probing per-event is a hot-path cost for a value that cannot
415
453
  // change within a process (K6-016). The Materializer writes next to its
416
454
  // targets, so the bundle directories are created here at init.
417
- const materializerRoot = opts.materializerRoot ?? join(homedir(), ".opencode-kevin");
418
455
  mkdirSync(join(materializerRoot, "skills"), { recursive: true });
419
456
  mkdirSync(join(materializerRoot, "refs"), { recursive: true });
420
457
  const capabilities = probe(input);
@@ -422,7 +459,7 @@ export const KevinPlugin = async (input, options) => {
422
459
  // When the host exposes permission.ask, the presence is noted; absence is silent no-op.
423
460
  // No new setting — bounded to tui_snapshots_enabled host-support check per spec.
424
461
  void capabilities.permissionAsk;
425
- const materializer = new Materializer(store, { root: materializerRoot });
462
+ const materializer = new Materializer(store, { root: materializerRoot }, kevinEnv);
426
463
  // v0.9.0 (K9-016 / plan §5.4, D9-10) — native registration replaces
427
464
  // file emission. When attachNative returns a registration for a
428
465
  // surface, the corresponding *_emission_enabled path is skipped for
@@ -1002,7 +1039,7 @@ export const KevinPlugin = async (input, options) => {
1002
1039
  version: KEVIN_VERSION,
1003
1040
  writer,
1004
1041
  });
1005
- curator = new Curator(store, memoryService, projectId, metrics, live.repoId);
1042
+ curator = new Curator(store, memoryService, projectId, metrics, live.repoId, kevinEnv);
1006
1043
  // v0.8.0 (BUG-003) — heal the OKF file header.
1007
1044
  // Rekey changes the scope the file is written
1008
1045
  // under; a stale `#repo` first line would make
@@ -1077,7 +1114,7 @@ export const KevinPlugin = async (input, options) => {
1077
1114
  // block; on a pre-008 DB the block is omitted, partial:true.
1078
1115
  // v0.8.0 (K8-007 / plan §5.7) — the penalized-memory rollup
1079
1116
  // scopes on repoId once the 009 column exists.
1080
- const report = buildAudit(store, metrics, capabilities, projectId, sessionIdentity.repoId);
1117
+ const report = buildAudit(store, metrics, capabilities, projectId, sessionIdentity.repoId, undefined, kevinEnv);
1081
1118
  const payload = args.verbose
1082
1119
  ? report
1083
1120
  : { ...report, settings: undefined };
@@ -1096,7 +1133,7 @@ export const KevinPlugin = async (input, options) => {
1096
1133
  description: "Doctor de Kevin (v0.9.0): salud del host, hooks, dependencias y registros nativos. Solo lectura, sin LLM ni writes, invocable en cualquier momento. Devuelve host (version, flavour, shell, v2), hooks ordenados dead primero, dependencies (zod_copies), native (enabled, registered/verified por surface), verdict (healthy|degraded|unknown) y reason. Output pensado para pegar en un issue: sin filesystem paths ni session ids.",
1097
1134
  args: {},
1098
1135
  async execute() {
1099
- const report = buildDoctor(store, host, memoryService);
1136
+ const report = buildDoctor(store, host, memoryService, {}, kevinEnv);
1100
1137
  return {
1101
1138
  title: "Doctor de Kevin",
1102
1139
  output: JSON.stringify(report),
@@ -1165,7 +1202,7 @@ export const KevinPlugin = async (input, options) => {
1165
1202
  .describe("status | last"),
1166
1203
  },
1167
1204
  async execute(args) {
1168
- const result = buildKevinBench({ store }, args);
1205
+ const result = buildKevinBench({ store }, args, kevinEnv);
1169
1206
  return {
1170
1207
  title: "Benchmark de Kevin",
1171
1208
  output: JSON.stringify(result),
@@ -1843,62 +1880,39 @@ export const KevinPlugin = async (input, options) => {
1843
1880
  // BUG-011 — the session is done: drop the global query
1844
1881
  // so the next session cannot reuse it.
1845
1882
  lastUserQuery = null;
1846
- // v0.4.0 (K4-024) — plan §5.2: settle the session's
1847
- // unmeasured injections (effective/ineffective +
1848
- // recurrence_count charges) at idle. Best-effort: a
1849
- // legacy DB without migration 005 has no ledger table.
1850
- try {
1851
- ledger.settle(sid);
1852
- }
1853
- catch {
1854
- // best-effort: a legacy DB without the ledger
1855
- // table must not break the idle path
1856
- }
1857
- // v0.5.0 (K5-012 / plan §5.4) — retire stale lessons at
1858
- // idle; pre-006 DBs degrade to a no-op.
1859
- try {
1860
- archiver.run();
1861
- }
1862
- catch {
1863
- // best-effort, same pattern as ledger.settle
1864
- }
1865
- fireAndForget(retrospective.generate(sid));
1866
- memoryService.boostPositiveReflectors(sid);
1867
- const recurred = memoryService.penalizeRecurringReflectors(sid);
1868
- injector.setRecurrences(recurred, sid);
1869
- patternMiner.mine(projectId);
1870
- // v0.7.0 (K7-012 / plan §5.4, D7-10) convention mining is
1871
- // session.idle-only, default-off, and isolated from the rest of
1872
- // the idle chain. Mined rules still enter the ordinary Curator
1873
- // approval path; this step never writes to the repository.
1874
- try {
1875
- if (memoryService.getSetting("convention_mining_enabled", "0") ===
1876
- "1") {
1877
- const conventions = conventionMiner.mine();
1878
- conventionMiner.emit(conventions);
1879
- }
1880
- }
1881
- catch {
1882
- // A throwing miner must not reject or truncate the idle chain.
1883
- }
1884
- // v0.7.0 (K7-016 / plan §5.5, D7-06) — conflict detection is
1885
- // surfacing-only on idle. It may create/open conflict rows, but
1886
- // no idle path can acknowledge or resolve one.
1887
- try {
1888
- if (memoryService.getSetting("conflict_detection_enabled", "0") ===
1889
- "1") {
1890
- conflictDetector.detect();
1891
- }
1892
- }
1893
- catch {
1894
- // Conflict surfacing is best-effort and must not reject idle.
1895
- }
1896
- fireAndForget(Promise.resolve()
1897
- .then(() => causalChain.onSessionIdle(sid))
1898
- // non-blocking — promote is a best-effort pass
1899
- // (legacy DBs pre-005 lack the recurrence_count
1900
- // column)
1901
- .catch(() => { }));
1883
+ // K13-010 (D13-07) — single ORDER via composeIdlePipeline.
1884
+ // Ledger/archiver/reflectors/pattern are the hand-synced core;
1885
+ // adapter and replay both consume IDLE_STEP_ORDER so re-order is visible in both.
1886
+ await composeIdlePipeline({
1887
+ "ledger.settle": () => { ledger.settle(sid); },
1888
+ "archiver.run": () => { archiver.run(); },
1889
+ retrospective: () => {
1890
+ fireAndForget(retrospective.generate(sid));
1891
+ },
1892
+ "reflectors.boost": () => { memoryService.boostPositiveReflectors(sid); },
1893
+ "reflectors.penalize": () => {
1894
+ const recurred = memoryService.penalizeRecurringReflectors(sid);
1895
+ injector.setRecurrences(recurred, sid);
1896
+ },
1897
+ "patternMiner.mine": () => { patternMiner.mine(projectId); },
1898
+ });
1899
+ // v0.7.0 (K7-012/K7-016) + causalChain — folded into the single ORDER via composeIdlePipeline (K13-010)
1900
+ await composeIdlePipeline({
1901
+ "conventionMiner.mine": () => {
1902
+ if (memoryService.getSetting("convention_mining_enabled", "0") === "1") {
1903
+ const conventions = conventionMiner.mine();
1904
+ conventionMiner.emit(conventions);
1905
+ }
1906
+ },
1907
+ "conflictDetector.detect": () => {
1908
+ if (memoryService.getSetting("conflict_detection_enabled", "0") === "1") {
1909
+ conflictDetector.detect();
1910
+ }
1911
+ },
1912
+ "causalChain.onSessionIdle": () => {
1913
+ fireAndForget(Promise.resolve().then(() => causalChain.onSessionIdle(sid)).catch(() => { }));
1914
+ },
1915
+ });
1902
1916
  // v1.2.0 (K12-007/K12-011 / D12-05) — TUI mailbox: actions→curate ordering.
1903
1917
  // Process mailbox BEFORE curator.propose so a fresh proposal created this idle
1904
1918
  // is NOT visible to a stale token (D12-04). Best-effort, never breaks idle.
@@ -2013,7 +2027,7 @@ export const KevinPlugin = async (input, options) => {
2013
2027
  .prepare("SELECT id, kind, target_path, proposed_text, diff, memory_id, created_at FROM curation_proposals WHERE status = 'pending' ORDER BY created_at")
2014
2028
  .all();
2015
2029
  })();
2016
- const { proposalToken } = await import("./TuiActions.js");
2030
+ const { proposalToken } = await import("@jmtrin/kevin-core");
2017
2031
  proposals = rawPending.map((r) => {
2018
2032
  const row = r;
2019
2033
  const id = String(row.id ?? "");
@@ -2060,7 +2074,7 @@ export const KevinPlugin = async (input, options) => {
2060
2074
  // Health
2061
2075
  let health;
2062
2076
  try {
2063
- const doc = buildDoctor(store, host, memoryService);
2077
+ const doc = buildDoctor(store, host, memoryService, {}, kevinEnv);
2064
2078
  let perfRows = [];
2065
2079
  try {
2066
2080
  const stats = perf.stats?.();
@@ -1,5 +1,5 @@
1
- import { type Materializer } from "./Materializer.js";
2
- import type { Store } from "./Store.js";
1
+ import { type Materializer } from "@jmtrin/kevin-core";
2
+ import type { Store } from "@jmtrin/kevin-core";
3
3
  import type { HostSurface } from "./host.js";
4
4
  /** The surface `attachNative()` returned. `verified` is filled by the
5
5
  * read-back checks (K9-014/015) once the host ran `setup()`. */
@@ -1,5 +1,5 @@
1
- import { SKILL_TOPIC } from "./Materializer.js";
2
- import { uuidv7 } from "./uuid.js";
1
+ import { SKILL_TOPIC } from "@jmtrin/kevin-core";
2
+ import { uuidv7 } from "@jmtrin/kevin-core";
3
3
  /** The only place in the repository allowed to name the v2 subpath. */
4
4
  export const V2_SPECIFIER = "@opencode-ai/plugin/v2/promise";
5
5
  /** The skill body Kevin registers through `skill.transform`. */