@yugabytedb/meko-mcp 2.0.0 → 2.2.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 (65) hide show
  1. package/README.md +183 -33
  2. package/assets/hooks-handlers/codex-session-end.sh +13 -0
  3. package/assets/hooks-handlers/cursor-session-start.sh +19 -1
  4. package/assets/hooks-handlers/lib/capture-notices.js +321 -0
  5. package/assets/hooks-handlers/lib/capture-state.js +2079 -0
  6. package/assets/hooks-handlers/lib/capture-status-cli.js +82 -0
  7. package/assets/hooks-handlers/lib/capture.js +1653 -225
  8. package/assets/hooks-handlers/lib/checkpoint-timer.js +455 -50
  9. package/assets/hooks-handlers/lib/kiro-handler.js +738 -0
  10. package/assets/hooks-handlers/lib/terminal-handoff.js +418 -0
  11. package/assets/hooks-handlers/session-end.sh +3 -24
  12. package/assets/hooks-handlers/session-start.sh +8 -1
  13. package/assets/hooks-handlers/test_checkpoint_timer.sh +162 -0
  14. package/assets/scripts/meko-statusline.sh +41 -1
  15. package/assets/scripts/package-desktop-skill.sh +32 -1
  16. package/assets/skills/meko-mcp-tools/SKILL.md +90 -137
  17. package/assets/skills/meko-mcp-tools/references/_sections.md +3 -5
  18. package/assets/skills/meko-mcp-tools/references/tools-agent-id-conventions.md +31 -28
  19. package/assets/skills/meko-mcp-tools/references/tools-conversation-dedup.md +5 -7
  20. package/assets/skills/meko-mcp-tools/references/tools-conversation-reasoning.md +50 -0
  21. package/assets/skills/meko-mcp-tools/references/tools-cookbook.md +69 -49
  22. package/assets/skills/meko-mcp-tools/references/tools-datapack-workflow.md +6 -6
  23. package/assets/skills/meko-mcp-tools/references/tools-known-limitations.md +29 -5
  24. package/assets/skills/meko-mcp-tools/references/tools-memory-vs-conversation.md +29 -92
  25. package/assets/skills/meko-mcp-tools/references/tools-overview.md +42 -40
  26. package/assets/skills/meko-mcp-tools/references/tools-rag-workflow.md +1 -2
  27. package/assets/skills/meko-mcp-tools/references/tools-troubleshooting.md +18 -11
  28. package/assets/skills/meko-mcp-tools/references/tools-user-id-conventions.md +10 -9
  29. package/assets/skills/meko-mcp-tools-desktop/SKILL.md +51 -44
  30. package/assets/skills/meko-mcp-tools-desktop/desktop/README.md +6 -6
  31. package/assets/skills/meko-mcp-tools-desktop/references/_sections.md +2 -2
  32. package/assets/skills/meko-mcp-tools-desktop/references/tools-agent-id-conventions.md +34 -25
  33. package/assets/skills/meko-mcp-tools-desktop/references/tools-conversation-reasoning.md +50 -0
  34. package/assets/skills/meko-mcp-tools-desktop/references/tools-cookbook.md +63 -37
  35. package/assets/skills/meko-mcp-tools-desktop/references/tools-known-limitations.md +4 -4
  36. package/assets/skills/meko-mcp-tools-desktop/references/tools-memory-vs-conversation.md +19 -22
  37. package/assets/skills/meko-mcp-tools-desktop/references/tools-overview.md +42 -40
  38. package/assets/skills/meko-mcp-tools-desktop/references/tools-rag-workflow.md +1 -2
  39. package/assets/skills/meko-mcp-tools-desktop/references/tools-troubleshooting.md +3 -17
  40. package/assets/skills/meko-select-datapack-desktop/SKILL.md +31 -21
  41. package/assets/skills/meko-select-datapack-desktop/references/cookbook.md +8 -8
  42. package/assets/skills/meko-select-datapack-desktop/references/role-display-future.md +41 -0
  43. package/assets/skills/meko-select-datapack-desktop/references/troubleshooting.md +1 -1
  44. package/bin/create-meko-setup.mjs +93 -16
  45. package/lib/capture-status.mjs +67 -0
  46. package/lib/clients/aliases.mjs +5 -0
  47. package/lib/clients/claude-code.mjs +8 -4
  48. package/lib/clients/claude-desktop.mjs +133 -4
  49. package/lib/clients/codex.mjs +29 -11
  50. package/lib/clients/common.mjs +2 -0
  51. package/lib/clients/cursor.mjs +7 -2
  52. package/lib/clients/index.mjs +2 -0
  53. package/lib/clients/kiro.mjs +705 -0
  54. package/lib/config.mjs +90 -0
  55. package/lib/detect.mjs +7 -2
  56. package/lib/installer.mjs +1 -0
  57. package/lib/migrate/adapters/codex.mjs +490 -0
  58. package/lib/migrate/id.mjs +28 -3
  59. package/lib/migrate/orchestrator.mjs +34 -7
  60. package/lib/migrate.mjs +4 -2
  61. package/lib/prompt.mjs +17 -2
  62. package/lib/url-selection.mjs +55 -0
  63. package/package.json +4 -4
  64. package/assets/skills/meko-mcp-tools/references/tools-scope-permissions.md +0 -71
  65. package/assets/skills/meko-mcp-tools-desktop/references/tools-scope-permissions.md +0 -71
@@ -17,6 +17,7 @@ import { seedFor } from "./id.mjs";
17
17
  import { resolveDestinationDatapack } from "./datapack.mjs";
18
18
  import * as claudeCodeAdapter from "./adapters/claude-code.mjs";
19
19
  import * as cursorAdapter from "./adapters/cursor.mjs";
20
+ import * as codexAdapter from "./adapters/codex.mjs";
20
21
  import * as memoryMdAdapter from "./adapters/memory-md.mjs";
21
22
 
22
23
  /**
@@ -46,7 +47,9 @@ import * as memoryMdAdapter from "./adapters/memory-md.mjs";
46
47
  * @returns {Promise<{ok: boolean, summary: object, counts: object}>}
47
48
  */
48
49
  export async function runMigration(opts) {
49
- fs.mkdirSync(opts.runDir, { recursive: true });
50
+ // A planning pass is intentionally read-only. In particular, don't create
51
+ // a run directory that `--migrate-status` can mistake for an active run.
52
+ if (!opts.dryRun) fs.mkdirSync(opts.runDir, { recursive: true });
50
53
 
51
54
  const log = opts.dryRun
52
55
  ? nullLogger()
@@ -55,6 +58,7 @@ export async function runMigration(opts) {
55
58
  const prog = progress.initial(opts.runId, opts.scope);
56
59
  const progressPath = path.join(opts.runDir, "progress.json");
57
60
  const writeProgress = () => {
61
+ if (opts.dryRun) return;
58
62
  try { progress.write(progressPath, prog); } catch { /* best effort */ }
59
63
  };
60
64
 
@@ -91,19 +95,26 @@ export async function runMigration(opts) {
91
95
  projectCwd: opts.projectCwd,
92
96
  home: opts.home,
93
97
  });
98
+ const codexSessions = codexAdapter.listSessions({
99
+ scope: opts.scope,
100
+ projectCwd: opts.projectCwd,
101
+ home: opts.home,
102
+ });
94
103
  const memoryItems = await memoryMdAdapter.enumerateMemoryItems({
95
104
  scope: opts.scope,
96
105
  projectCwd: opts.projectCwd,
97
106
  home: opts.home,
98
107
  });
99
108
 
100
- prog.counts.sessionsPlanned = claudeSessions.length + cursorSessions.length;
109
+ prog.counts.sessionsPlanned =
110
+ claudeSessions.length + cursorSessions.length + codexSessions.length;
101
111
  writeProgress();
102
112
 
103
113
  log.info("plan", {
104
114
  scope: opts.scope,
105
115
  claudeSessions: claudeSessions.length,
106
116
  cursorSessions: cursorSessions.length,
117
+ codexSessions: codexSessions.length,
107
118
  memoryItems: memoryItems.length,
108
119
  dryRun: !!opts.dryRun,
109
120
  noExtract: !!opts.noExtract,
@@ -122,7 +133,12 @@ export async function runMigration(opts) {
122
133
  const parsed = cursorAdapter.parseSession(ref.path);
123
134
  pairedTurns += parsed.exchanges.length;
124
135
  }
125
- const plannedConversationCreates = claudeSessions.length + cursorSessions.length
136
+ for (const ref of codexSessions) {
137
+ const parsed = codexAdapter.parseSession(ref.path);
138
+ pairedTurns += parsed.exchanges.length;
139
+ }
140
+ const plannedConversationCreates =
141
+ claudeSessions.length + cursorSessions.length + codexSessions.length
126
142
  + (memoryItems.length > 0 ? countDistinctMemoryAgents(memoryItems) : 0);
127
143
  const plannedConversationAddMessage = pairedTurns;
128
144
  const plannedMemoryAddExtract = opts.noExtract ? 0 : pairedTurns;
@@ -132,6 +148,7 @@ export async function runMigration(opts) {
132
148
  scope: opts.scope,
133
149
  claudeSessions: claudeSessions.length,
134
150
  cursorSessions: cursorSessions.length,
151
+ codexSessions: codexSessions.length,
135
152
  memoryItems: memoryItems.length,
136
153
  pairedTurns,
137
154
  plannedCalls: {
@@ -218,7 +235,15 @@ export async function runMigration(opts) {
218
235
  const withDp = (args) => (datapackId ? { ...args, datapack_id: datapackId } : args);
219
236
 
220
237
  // -- Conversations --
221
- for (const ref of [...claudeSessions, ...cursorSessions]) {
238
+ // Tag each ref with its source so the parse dispatch below is explicit.
239
+ // Claude Code and Codex both store rollouts as `.jsonl`, so an
240
+ // extension check alone can't tell them apart.
241
+ const taggedSessions = [
242
+ ...claudeSessions.map((ref) => ({ ref, source: "claude-code" })),
243
+ ...cursorSessions.map((ref) => ({ ref, source: "cursor" })),
244
+ ...codexSessions.map((ref) => ({ ref, source: "codex" })),
245
+ ];
246
+ for (const { ref, source } of taggedSessions) {
222
247
  let existing = state.getSession(st, ref.agentId, ref.sessionUuid);
223
248
  if (existing?.status === "done" && !opts.force) {
224
249
  log.info("session_skip_done", { agentId: ref.agentId, sessionUuid: ref.sessionUuid });
@@ -243,9 +268,11 @@ export async function runMigration(opts) {
243
268
  prog.currentSession = ref.sessionUuid;
244
269
  writeProgress();
245
270
 
246
- const parsed = (ref.path && ref.path.endsWith(".jsonl"))
271
+ const parsed = source === "claude-code"
247
272
  ? await claudeCodeAdapter.parseSession(ref.path)
248
- : cursorAdapter.parseSession(ref.path);
273
+ : source === "codex"
274
+ ? codexAdapter.parseSession(ref.path)
275
+ : cursorAdapter.parseSession(ref.path);
249
276
 
250
277
  if (parsed.partial) {
251
278
  log.warn("session_partial", { path: ref.path });
@@ -260,7 +287,7 @@ export async function runMigration(opts) {
260
287
  session_id: ref.sessionUuid,
261
288
  title: (parsed.firstUserText || ref.sessionUuid).slice(0, 120),
262
289
  metadata: JSON.stringify({
263
- source: ref.path.endsWith(".jsonl") ? "claude-code" : "cursor",
290
+ source,
264
291
  cwd: ref.cwd || null,
265
292
  gitBranch: ref.slug ? (parsed.gitBranch || null) : null,
266
293
  startedAt: parsed.startedAt || null,
package/lib/migrate.mjs CHANGED
@@ -212,10 +212,10 @@ export async function startMigration(opts) {
212
212
  // don't move when the state key changes.
213
213
  const urlDatapackKey = datapackKeyFromUrl(opts.url);
214
214
  let P = paths({ datapackKey: urlDatapackKey });
215
- fs.mkdirSync(P.root, { recursive: true });
216
215
 
217
216
  // Dry-run is a local-only planning pass: no HTTP, no lock, no fork.
218
- // Don't gate it on URL/apiKey.
217
+ // Don't gate it on URL/apiKey, and don't create the migration root: a
218
+ // planning pass must not appear as a phantom active run in --migrate-status.
219
219
  if (opts.dryRun) {
220
220
  const runId = generateRunId();
221
221
  const runDir = P.runDir(runId);
@@ -260,6 +260,8 @@ export async function startMigration(opts) {
260
260
  };
261
261
  }
262
262
 
263
+ fs.mkdirSync(P.root, { recursive: true });
264
+
263
265
  // Real run — now URL/apiKey are required (dry-run branch already returned).
264
266
  if (!opts.url) {
265
267
  return { ok: false, message: "--url (or MEKO_MCP_URL) is required for --migrate." };
package/lib/prompt.mjs CHANGED
@@ -37,6 +37,21 @@ function question(query) {
37
37
  });
38
38
  }
39
39
 
40
+ /**
41
+ * Apply terminal erase bytes that survive readline's muted-output path.
42
+ * Some PTYs leave DEL/BS in the answer when readline is attached to a
43
+ * non-TTY Writable, even with terminal mode enabled. Treat them exactly as
44
+ * interactive backspace so a corrected secret never persists control bytes.
45
+ */
46
+ function applyEraseCharacters(value) {
47
+ const chars = [];
48
+ for (const char of value) {
49
+ if (char === "\x7f" || char === "\b") chars.pop();
50
+ else chars.push(char);
51
+ }
52
+ return chars.join("");
53
+ }
54
+
40
55
  // ---------------------------------------------------------------------------
41
56
  // askChoice
42
57
  // ---------------------------------------------------------------------------
@@ -50,7 +65,7 @@ function question(query) {
50
65
  *
51
66
  * @example
52
67
  * const choice = await askChoice('Connection type:', [
53
- * { label: 'Cloud Meko', description: 'Hosted at mekodev.com' },
68
+ * { label: 'Cloud Meko', description: 'Hosted at mekodata.ai' },
54
69
  * { label: 'Local Meko', description: 'docker-compose stack' },
55
70
  * ]);
56
71
  */
@@ -218,7 +233,7 @@ export async function askSecret(questionText) {
218
233
 
219
234
  secretRL.question("", (answer) => {
220
235
  finish();
221
- resolve(answer);
236
+ resolve(applyEraseCharacters(answer));
222
237
  });
223
238
  });
224
239
  }
@@ -0,0 +1,55 @@
1
+ import { MEKO_PRODUCTION_MCP_URL } from "./clients/common.mjs";
2
+
3
+ function validHttpUrl(value) {
4
+ if (typeof value !== "string" || !value.trim()) return null;
5
+ const candidate = value.trim();
6
+ try {
7
+ const parsed = new URL(candidate);
8
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
9
+ parsed.pathname = parsed.pathname.replace(/\/+$/, "") || "/";
10
+ return { value: candidate, comparisonKey: parsed.toString() };
11
+ } catch {
12
+ return null;
13
+ }
14
+ }
15
+
16
+ /**
17
+ * Select the URL offered by interactive/non-interactive setup.
18
+ * Explicit CLI URLs are resolved before this helper is called.
19
+ */
20
+ export function resolveSuggestedMcpUrl({
21
+ adapters,
22
+ clients,
23
+ name = "meko",
24
+ scope = "user",
25
+ projectRoot = process.cwd(),
26
+ }) {
27
+ const existingUrlsByKey = new Map();
28
+ for (const client of clients) {
29
+ const adapter = adapters[client];
30
+ if (!adapter?.detectExisting) continue;
31
+ try {
32
+ const existing = adapter.detectExisting(name, { scope, projectRoot });
33
+ const url = existing?.found ? validHttpUrl(existing.url) : null;
34
+ if (url && !existingUrlsByKey.has(url.comparisonKey)) {
35
+ existingUrlsByKey.set(url.comparisonKey, url.value);
36
+ }
37
+ } catch {
38
+ // Existing-config discovery is advisory. Installation performs its own
39
+ // prerequisite and config checks later with actionable errors.
40
+ }
41
+ }
42
+
43
+ const uniqueUrls = [...existingUrlsByKey.values()];
44
+ if (uniqueUrls.length === 1) {
45
+ return { url: uniqueUrls[0], source: "existing", existingUrls: uniqueUrls };
46
+ }
47
+ if (uniqueUrls.length > 1) {
48
+ return { url: null, source: "conflict", existingUrls: uniqueUrls };
49
+ }
50
+ return {
51
+ url: MEKO_PRODUCTION_MCP_URL,
52
+ source: "production-default",
53
+ existingUrls: [],
54
+ };
55
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@yugabytedb/meko-mcp",
3
- "version": "2.0.0",
4
- "description": "Single-line installer for Meko MCP Server across Claude and Cursor clients",
3
+ "version": "2.2.0",
4
+ "description": "Installer for Meko across Claude, Cursor, Codex, and Kiro clients",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "create-meko-setup": "bin/create-meko-setup.mjs",
@@ -18,14 +18,14 @@
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\"",
21
+ "test": "node --test \"test/*.mjs\" && node --test \"test/serial/*.mjs\"",
22
22
  "smoke": "./scripts/prepublish-smoke.sh",
23
23
  "test:unit": "node --test test/config.test.mjs test/validate.test.mjs test/clients.test.mjs",
24
24
  "test:integration": "RUN_INSTALLER_INTEGRATION=1 node --test test/installer.test.mjs",
25
25
  "test:integration:cloud": "RUN_INSTALLER_INTEGRATION=1 node --test test/installer.test.mjs"
26
26
  },
27
27
  "engines": {
28
- "node": ">=18.0.0"
28
+ "node": ">=20.18.1"
29
29
  },
30
30
  "keywords": [
31
31
  "meko",
@@ -1,71 +0,0 @@
1
- # Scope Permission Hierarchy and Per-Tool Requirements
2
-
3
- **WRONG:** `memory_add(scope="read", ...)` → PermissionError: requires 'write' or higher.
4
- **WRONG:** `memory_delete_all(scope="write", ...)` → PermissionError: requires 'admin'.
5
-
6
- ## Scope Hierarchy
7
-
8
- ```
9
- admin (level 2) -- can do everything
10
- ↓
11
- write (level 1) -- can read + write, not admin-only
12
- ↓
13
- read (level 0) -- can only read
14
- ```
15
-
16
- Higher scopes include lower scopes. An unrecognized scope string always fails.
17
-
18
- ## Per-Tool Requirements
19
-
20
- ### read scope (9 tools)
21
-
22
- | Tool | Group |
23
- |------|-------|
24
- | `knowledgebase_search` | RAG |
25
- | `memory_search` | Memory |
26
- | `memory_get_by_id` | Memory |
27
- | `memory_get_all` | Memory |
28
- | `flush_pending_memory_candidates` | Memory |
29
- | `conversation_get` | Conversation |
30
- | `conversation_list` | Conversation |
31
- | `datapack_list` | Datapack |
32
- | `datapack_describe` | Datapack |
33
-
34
- ### write scope (9 tools)
35
-
36
- | Tool | Group |
37
- |------|-------|
38
- | `memory_add` | Memory |
39
- | `memory_update` | Memory |
40
- | `memory_delete_by_id` | Memory |
41
- | `conversation_create` | Conversation |
42
- | `conversation_add_message` | Conversation |
43
- | `conversation_update` | Conversation |
44
- | `datapack_create` | Datapack |
45
- | `datapack_update` | Datapack |
46
- | `track_token_usage` | Observability |
47
-
48
- ### admin scope (3 tools — destructive, irreversible)
49
-
50
- | Tool | Group |
51
- |------|-------|
52
- | `memory_delete_all` | Memory |
53
- | `conversation_delete` | Conversation |
54
- | `datapack_delete` | Datapack |
55
-
56
- Agent and knowledge-base lifecycle tools are not MCP-exposed — see "Platform capabilities NOT exposed via MCP" in `tools-overview.md`.
57
-
58
- ## Best Practices
59
-
60
- 1. **Use the minimum scope needed.** `scope="read"` for reads documents intent and prevents accidental writes.
61
- 2. **The scope is a parameter, not an auth token.** When OAuth is enabled, the actual permission check happens against the OAuth token's claims. Both `scope` parameter and OAuth permissions must be satisfied.
62
-
63
- ## Common Mistakes
64
-
65
- **WRONG:** `scope="all"` — there is no "all" scope. Valid values are exactly: `"read"`, `"write"`, `"admin"`. Nothing else.
66
-
67
- Other invalid values that will fail: `"readwrite"`, `"rw"`, `"full"`, `"any"`.
68
-
69
- The error `Insufficient scope: 'all'. This tool requires 'read' or higher.` means the scope string was not recognized as a valid value. Always use one of the three valid strings.
70
-
71
- **Default choice:** Start with `scope="read"` unless you specifically need to write or delete.
@@ -1,71 +0,0 @@
1
- # Scope Permission Hierarchy and Per-Tool Requirements
2
-
3
- **WRONG:** `memory_add(scope="read", ...)` → PermissionError: requires 'write' or higher.
4
- **WRONG:** `memory_delete_all(scope="write", ...)` → PermissionError: requires 'admin'.
5
-
6
- ## Scope Hierarchy
7
-
8
- ```
9
- admin (level 2) -- can do everything
10
- ↓
11
- write (level 1) -- can read + write, not admin-only
12
- ↓
13
- read (level 0) -- can only read
14
- ```
15
-
16
- Higher scopes include lower scopes. An unrecognized scope string always fails.
17
-
18
- ## Per-Tool Requirements
19
-
20
- ### read scope (9 tools)
21
-
22
- | Tool | Group |
23
- |------|-------|
24
- | `knowledgebase_search` | RAG |
25
- | `memory_search` | Memory |
26
- | `memory_get_by_id` | Memory |
27
- | `memory_get_all` | Memory |
28
- | `flush_pending_memory_candidates` | Memory |
29
- | `conversation_get` | Conversation |
30
- | `conversation_list` | Conversation |
31
- | `datapack_list` | Datapack |
32
- | `datapack_describe` | Datapack |
33
-
34
- ### write scope (9 tools)
35
-
36
- | Tool | Group |
37
- |------|-------|
38
- | `memory_add` | Memory |
39
- | `memory_update` | Memory |
40
- | `memory_delete_by_id` | Memory |
41
- | `conversation_create` | Conversation |
42
- | `conversation_add_message` | Conversation |
43
- | `conversation_update` | Conversation |
44
- | `datapack_create` | Datapack |
45
- | `datapack_update` | Datapack |
46
- | `track_token_usage` | Observability |
47
-
48
- ### admin scope (3 tools — destructive, irreversible)
49
-
50
- | Tool | Group |
51
- |------|-------|
52
- | `memory_delete_all` | Memory |
53
- | `conversation_delete` | Conversation |
54
- | `datapack_delete` | Datapack |
55
-
56
- Agent and knowledge-base lifecycle tools are not MCP-exposed — see "Platform capabilities NOT exposed via MCP" in `tools-overview.md`.
57
-
58
- ## Best Practices
59
-
60
- 1. **Use the minimum scope needed.** `scope="read"` for reads documents intent and prevents accidental writes.
61
- 2. **The scope is a parameter, not an auth token.** When OAuth is enabled, the actual permission check happens against the OAuth token's claims. Both `scope` parameter and OAuth permissions must be satisfied.
62
-
63
- ## Common Mistakes
64
-
65
- **WRONG:** `scope="all"` — there is no "all" scope. Valid values are exactly: `"read"`, `"write"`, `"admin"`. Nothing else.
66
-
67
- Other invalid values that will fail: `"readwrite"`, `"rw"`, `"full"`, `"any"`.
68
-
69
- The error `Insufficient scope: 'all'. This tool requires 'read' or higher.` means the scope string was not recognized as a valid value. Always use one of the three valid strings.
70
-
71
- **Default choice:** Start with `scope="read"` unless you specifically need to write or delete.