@yugabytedb/meko-mcp 2.2.0 → 2.5.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 (67) hide show
  1. package/README.md +168 -61
  2. package/assets/hooks-handlers/lib/capture-cleanup-cli.js +2134 -0
  3. package/assets/hooks-handlers/lib/capture-notices.js +224 -14
  4. package/assets/hooks-handlers/lib/capture-state.js +206 -1
  5. package/assets/hooks-handlers/lib/capture-status-cli.js +6 -2
  6. package/assets/hooks-handlers/lib/capture.js +1364 -139
  7. package/assets/hooks-handlers/lib/checkpoint-timer.js +445 -18
  8. package/assets/hooks-handlers/lib/journal-bridge.js +218 -0
  9. package/assets/hooks-handlers/lib/kiro-handler.js +26 -4
  10. package/assets/hooks-handlers/lib/opencode-handler.js +643 -0
  11. package/assets/hooks-handlers/lib/opencode-normalizer.js +145 -0
  12. package/assets/hooks-handlers/lib/terminal-handoff.js +10 -1
  13. package/assets/opencode-plugin/meko.js +213 -0
  14. package/assets/scripts/meko-statusline.sh +102 -19
  15. package/assets/skills/meko-mcp-tools/SKILL.md +59 -16
  16. package/assets/skills/meko-mcp-tools/references/_sections.md +2 -2
  17. package/assets/skills/meko-mcp-tools/references/tools-agent-id-conventions.md +15 -7
  18. package/assets/skills/meko-mcp-tools/references/tools-cookbook.md +275 -19
  19. package/assets/skills/meko-mcp-tools/references/tools-datapack-workflow.md +7 -4
  20. package/assets/skills/meko-mcp-tools/references/tools-known-limitations.md +39 -11
  21. package/assets/skills/meko-mcp-tools/references/tools-memory-vs-conversation.md +10 -6
  22. package/assets/skills/meko-mcp-tools/references/tools-overview.md +44 -29
  23. package/assets/skills/meko-mcp-tools/references/tools-rag-workflow.md +79 -14
  24. package/assets/skills/meko-mcp-tools/references/tools-troubleshooting.md +47 -1
  25. package/assets/skills/meko-mcp-tools/references/tools-user-id-conventions.md +6 -39
  26. package/assets/skills/meko-mcp-tools-desktop/SKILL.md +57 -21
  27. package/assets/skills/meko-mcp-tools-desktop/desktop/README.md +4 -4
  28. package/assets/skills/meko-mcp-tools-desktop/references/_sections.md +1 -1
  29. package/assets/skills/meko-mcp-tools-desktop/references/tools-agent-id-conventions.md +8 -6
  30. package/assets/skills/meko-mcp-tools-desktop/references/tools-cookbook.md +284 -38
  31. package/assets/skills/meko-mcp-tools-desktop/references/tools-known-limitations.md +32 -10
  32. package/assets/skills/meko-mcp-tools-desktop/references/tools-memory-vs-conversation.md +16 -12
  33. package/assets/skills/meko-mcp-tools-desktop/references/tools-overview.md +46 -31
  34. package/assets/skills/meko-mcp-tools-desktop/references/tools-rag-workflow.md +79 -14
  35. package/assets/skills/meko-mcp-tools-desktop/references/tools-troubleshooting.md +69 -1
  36. package/assets/skills/meko-select-datapack-desktop/SKILL.md +93 -14
  37. package/assets/skills/meko-select-datapack-desktop/references/cookbook.md +44 -12
  38. package/assets/skills/meko-select-datapack-desktop/references/role-display-future.md +14 -10
  39. package/assets/skills/meko-select-datapack-desktop/references/troubleshooting.md +17 -5
  40. package/bin/create-meko-setup.mjs +186 -30
  41. package/lib/autofix.mjs +100 -0
  42. package/lib/browser-login.mjs +495 -0
  43. package/lib/capture-cleanup.mjs +63 -0
  44. package/lib/clients/claude-code.mjs +1 -0
  45. package/lib/clients/codex.mjs +80 -35
  46. package/lib/clients/common.mjs +7 -0
  47. package/lib/clients/index.mjs +2 -0
  48. package/lib/clients/kiro.mjs +5 -5
  49. package/lib/clients/opencode.mjs +537 -0
  50. package/lib/config.mjs +68 -1
  51. package/lib/installer.mjs +18 -2
  52. package/lib/migrate/adapters/claude-code.mjs +11 -2
  53. package/lib/migrate/adapters/codex.mjs +9 -2
  54. package/lib/migrate/adapters/memory-md.mjs +27 -8
  55. package/lib/migrate/adapters/opencode.mjs +148 -0
  56. package/lib/migrate/claude-parser.mjs +153 -9
  57. package/lib/migrate/datapack.mjs +254 -16
  58. package/lib/migrate/id.mjs +195 -13
  59. package/lib/migrate/mcp-client.mjs +59 -1
  60. package/lib/migrate/opencode-normalizer.mjs +132 -0
  61. package/lib/migrate/orchestrator.mjs +294 -78
  62. package/lib/migrate/progress.mjs +239 -5
  63. package/lib/migrate/registry.mjs +72 -0
  64. package/lib/migrate.mjs +97 -9
  65. package/lib/validate.mjs +111 -22
  66. package/package.json +5 -5
  67. package/scripts/bundle-assets.mjs +4 -0
package/lib/validate.mjs CHANGED
@@ -38,8 +38,13 @@ const CORE_TOOLS = [
38
38
  /** Nil UUID used as the conversation_id placeholder for the canary call. */
39
39
  const NIL_UUID = "00000000-0000-0000-0000-000000000000";
40
40
 
41
- /** Tool the canary invokes. */
42
- const CANARY_TOOL = "datapack_list";
41
+ /**
42
+ * Tool the canary invokes. `datapack_search(limit=1)` reads one page whatever
43
+ * the account size; a server that predates it gets `datapack_list`, which
44
+ * reads the whole account.
45
+ */
46
+ const CANARY_TOOL = "datapack_search";
47
+ const LEGACY_CANARY_TOOL = "datapack_list";
43
48
 
44
49
  // ---------------------------------------------------------------------------
45
50
  // 1. Config validation
@@ -139,9 +144,10 @@ export async function validateTools(url, apiKey) {
139
144
  * The canary's job is to prove the part of the stack that connectivity +
140
145
  * tools/list don't cover: auth was accepted as a real Meko identity, the
141
146
  * tenant resolver mapped it to an account, and the datapack routing wired
142
- * up the user's actual workspace. We invoke `datapack_list` for this — it
143
- * is a single read-only call, runs a short SQL query against the system
144
- * schema, and returns deterministically (every authenticated user has at
147
+ * up the user's actual workspace. We invoke `datapack_search(limit=1)` for
148
+ * this, or `datapack_list` on a server whose tools/list has no
149
+ * `datapack_search`. Either is a single read-only call against the system
150
+ * schema and returns deterministically (every authenticated user has at
145
151
  * least one datapack: their default).
146
152
  *
147
153
  * Why not memory_add? The previous canary did a write/read/delete round-trip
@@ -166,11 +172,13 @@ export async function validateTools(url, apiKey) {
166
172
  * initialize handshake is performed.
167
173
  * @param {number} [options.timeoutMs] - Override the canary call's transport timeout.
168
174
  * Defaults to DEFAULT_REQUEST_TIMEOUT_MS.
175
+ * @param {string[]} [options.toolNames] - Tool names from an earlier tools/list.
176
+ * If omitted, the canary lists tools itself.
169
177
  * @returns {Promise<{
170
178
  * ok: boolean,
171
179
  * message: string,
172
180
  * datapack_count: number|null,
173
- * step?: "initialize"|"datapack_list",
181
+ * step?: "initialize"|"datapack_search"|"datapack_list",
174
182
  * }>}
175
183
  */
176
184
  export async function validateCanary(url, apiKey, options = {}) {
@@ -190,11 +198,77 @@ export async function validateCanary(url, apiKey, options = {}) {
190
198
  sessionId = init.sessionId;
191
199
  }
192
200
 
201
+ let toolNames = options.toolNames;
202
+ if (!Array.isArray(toolNames)) {
203
+ // A failed listing just means we cannot tell; datapack_list works on
204
+ // every server.
205
+ const listed = await sendToolsList(url, apiKey, sessionId);
206
+ toolNames = listed.tools;
207
+ }
208
+ if (toolNames.includes(CANARY_TOOL)) {
209
+ return searchCanary(url, apiKey, sessionId, timeoutMs);
210
+ }
211
+ return listCanary(url, apiKey, sessionId, timeoutMs);
212
+ }
213
+
214
+ async function searchCanary(url, apiKey, sessionId, timeoutMs) {
193
215
  const callRes = await mcpCallTool(
194
216
  url,
195
217
  apiKey,
196
218
  sessionId,
197
219
  CANARY_TOOL,
220
+ { conversation_id: NIL_UUID, limit: 1 },
221
+ { timeoutMs },
222
+ );
223
+ if (!callRes.ok) {
224
+ return {
225
+ ok: false,
226
+ datapack_count: null,
227
+ step: CANARY_TOOL,
228
+ message: `Canary: ${callRes.message}`,
229
+ };
230
+ }
231
+
232
+ const datapacks = parseDatapackSearchPage(callRes.text);
233
+ if (!datapacks) {
234
+ return {
235
+ ok: false,
236
+ datapack_count: null,
237
+ step: CANARY_TOOL,
238
+ message:
239
+ `Canary: ${CANARY_TOOL} returned an unexpected response shape ` +
240
+ `(expected an object with a datapacks array). Check the server logs.`,
241
+ };
242
+ }
243
+ if (datapacks.length === 0) {
244
+ return zeroDatapacks(CANARY_TOOL);
245
+ }
246
+ return {
247
+ ok: true,
248
+ datapack_count: datapacks.length,
249
+ message: `Canary OK — ${CANARY_TOOL} found a datapack.`,
250
+ };
251
+ }
252
+
253
+ function zeroDatapacks(tool) {
254
+ return {
255
+ ok: false,
256
+ datapack_count: 0,
257
+ step: tool,
258
+ message:
259
+ `Canary: ${tool} returned zero datapacks. Every authenticated ` +
260
+ `Meko user should have at least their default datapack — this likely ` +
261
+ `means the API token authenticated but resolved to the wrong tenant. ` +
262
+ `Verify the API key matches the tenant the URL points at.`,
263
+ };
264
+ }
265
+
266
+ async function listCanary(url, apiKey, sessionId, timeoutMs) {
267
+ const callRes = await mcpCallTool(
268
+ url,
269
+ apiKey,
270
+ sessionId,
271
+ LEGACY_CANARY_TOOL,
198
272
  {
199
273
  scope: "read",
200
274
  conversation_id: NIL_UUID,
@@ -210,7 +284,7 @@ export async function validateCanary(url, apiKey, options = {}) {
210
284
  return {
211
285
  ok: false,
212
286
  datapack_count: null,
213
- step: CANARY_TOOL,
287
+ step: LEGACY_CANARY_TOOL,
214
288
  message: `Canary: ${callRes.message}`,
215
289
  };
216
290
  }
@@ -224,30 +298,21 @@ export async function validateCanary(url, apiKey, options = {}) {
224
298
  return {
225
299
  ok: false,
226
300
  datapack_count: null,
227
- step: CANARY_TOOL,
301
+ step: LEGACY_CANARY_TOOL,
228
302
  message:
229
- `Canary: ${CANARY_TOOL} returned an unexpected response shape ` +
303
+ `Canary: ${LEGACY_CANARY_TOOL} returned an unexpected response shape ` +
230
304
  `(expected a JSON array of datapack objects). Check the server logs.`,
231
305
  };
232
306
  }
233
307
 
234
308
  if (datapacks.length === 0) {
235
- return {
236
- ok: false,
237
- datapack_count: 0,
238
- step: CANARY_TOOL,
239
- message:
240
- `Canary: ${CANARY_TOOL} returned zero datapacks. Every authenticated ` +
241
- `Meko user should have at least their default datapack — this likely ` +
242
- `means the API token authenticated but resolved to the wrong tenant. ` +
243
- `Verify the API key matches the tenant the URL points at.`,
244
- };
309
+ return zeroDatapacks(LEGACY_CANARY_TOOL);
245
310
  }
246
311
 
247
312
  return {
248
313
  ok: true,
249
314
  datapack_count: datapacks.length,
250
- message: `Canary OK — ${CANARY_TOOL} returned ${datapacks.length} datapack(s).`,
315
+ message: `Canary OK — ${LEGACY_CANARY_TOOL} returned ${datapacks.length} datapack(s).`,
251
316
  };
252
317
  }
253
318
 
@@ -263,8 +328,8 @@ export async function validateCanary(url, apiKey, options = {}) {
263
328
  * canary reuses that same session too.
264
329
  *
265
330
  * The canary is skipped (not failed) when tools/list didn't pass — the
266
- * canary depends on `datapack_list` being present, and a tools failure
267
- * already surfaces that.
331
+ * canary depends on `datapack_list` (a core tool) or `datapack_search`
332
+ * being present, and a tools failure already surfaces that.
268
333
  *
269
334
  * @param {string} url - Full MCP endpoint URL.
270
335
  * @param {string|null} apiKey - Optional API key.
@@ -292,6 +357,7 @@ export async function runValidation(url, apiKey, serverName = "meko", options =
292
357
  if (tools.ok && connectivity.ok) {
293
358
  canary = await validateCanary(url, apiKey, {
294
359
  sessionId: connectivity.sessionId,
360
+ toolNames: tools.tools,
295
361
  });
296
362
  } else {
297
363
  canary = {
@@ -508,6 +574,29 @@ function extractFirstText(result) {
508
574
  return null;
509
575
  }
510
576
 
577
+ /**
578
+ * Parse a `datapack_search` response body and return its `datapacks` rows,
579
+ * or `null` if the body is not a recognizable page.
580
+ *
581
+ * @param {string|null} text
582
+ * @returns {Array|null}
583
+ */
584
+ function parseDatapackSearchPage(text) {
585
+ if (!text) return null;
586
+ let body;
587
+ try {
588
+ body = JSON.parse(text);
589
+ } catch {
590
+ return null;
591
+ }
592
+ if (!body || typeof body !== "object" || !Array.isArray(body.datapacks)) return null;
593
+ const rows = body.datapacks;
594
+ const allLookLikeDatapacks = rows.every(
595
+ (entry) => entry && typeof entry === "object" && typeof entry.datapack_id === "string",
596
+ );
597
+ return allLookLikeDatapacks ? rows : null;
598
+ }
599
+
511
600
  /**
512
601
  * Parse a `datapack_list` response body and return the array of datapacks,
513
602
  * or `null` if the body is not a recognizable datapack list.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yugabytedb/meko-mcp",
3
- "version": "2.2.0",
3
+ "version": "2.5.0",
4
4
  "description": "Installer for Meko across Claude, Cursor, Codex, and Kiro clients",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,11 +18,11 @@
18
18
  "prepack": "node scripts/bundle-assets.mjs",
19
19
  "postpack": "node scripts/bundle-assets.mjs --clean",
20
20
  "bundle-assets": "node scripts/bundle-assets.mjs",
21
- "test": "node --test \"test/*.mjs\" && node --test \"test/serial/*.mjs\"",
21
+ "test": "node --import ./test/helpers/hermetic-env.mjs --test \"test/*.mjs\" && node --import ./test/helpers/hermetic-env.mjs --test \"test/serial/*.mjs\"",
22
22
  "smoke": "./scripts/prepublish-smoke.sh",
23
- "test:unit": "node --test test/config.test.mjs test/validate.test.mjs test/clients.test.mjs",
24
- "test:integration": "RUN_INSTALLER_INTEGRATION=1 node --test test/installer.test.mjs",
25
- "test:integration:cloud": "RUN_INSTALLER_INTEGRATION=1 node --test test/installer.test.mjs"
23
+ "test:unit": "node --import ./test/helpers/hermetic-env.mjs --test test/config.test.mjs test/validate.test.mjs test/clients.test.mjs",
24
+ "test:integration": "RUN_INSTALLER_INTEGRATION=1 node --import ./test/helpers/hermetic-env.mjs --test test/installer.test.mjs",
25
+ "test:integration:cloud": "RUN_INSTALLER_INTEGRATION=1 node --import ./test/helpers/hermetic-env.mjs --test test/installer.test.mjs"
26
26
  },
27
27
  "engines": {
28
28
  "node": ">=20.18.1"
@@ -30,6 +30,10 @@ const PAYLOAD = [
30
30
  from: join(repoRoot, "skills", "hooks-handlers"),
31
31
  to: join(assetsDir, "hooks-handlers"),
32
32
  },
33
+ {
34
+ from: join(repoRoot, "skills", "opencode-plugin"),
35
+ to: join(assetsDir, "opencode-plugin"),
36
+ },
33
37
  {
34
38
  from: join(repoRoot, "skills", "scripts", "package-desktop-skill.sh"),
35
39
  to: join(assetsDir, "scripts", "package-desktop-skill.sh"),