@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.
- package/README.md +168 -61
- package/assets/hooks-handlers/lib/capture-cleanup-cli.js +2134 -0
- package/assets/hooks-handlers/lib/capture-notices.js +224 -14
- package/assets/hooks-handlers/lib/capture-state.js +206 -1
- package/assets/hooks-handlers/lib/capture-status-cli.js +6 -2
- package/assets/hooks-handlers/lib/capture.js +1364 -139
- package/assets/hooks-handlers/lib/checkpoint-timer.js +445 -18
- package/assets/hooks-handlers/lib/journal-bridge.js +218 -0
- package/assets/hooks-handlers/lib/kiro-handler.js +26 -4
- package/assets/hooks-handlers/lib/opencode-handler.js +643 -0
- package/assets/hooks-handlers/lib/opencode-normalizer.js +145 -0
- package/assets/hooks-handlers/lib/terminal-handoff.js +10 -1
- package/assets/opencode-plugin/meko.js +213 -0
- package/assets/scripts/meko-statusline.sh +102 -19
- package/assets/skills/meko-mcp-tools/SKILL.md +59 -16
- package/assets/skills/meko-mcp-tools/references/_sections.md +2 -2
- package/assets/skills/meko-mcp-tools/references/tools-agent-id-conventions.md +15 -7
- package/assets/skills/meko-mcp-tools/references/tools-cookbook.md +275 -19
- package/assets/skills/meko-mcp-tools/references/tools-datapack-workflow.md +7 -4
- package/assets/skills/meko-mcp-tools/references/tools-known-limitations.md +39 -11
- package/assets/skills/meko-mcp-tools/references/tools-memory-vs-conversation.md +10 -6
- package/assets/skills/meko-mcp-tools/references/tools-overview.md +44 -29
- package/assets/skills/meko-mcp-tools/references/tools-rag-workflow.md +79 -14
- package/assets/skills/meko-mcp-tools/references/tools-troubleshooting.md +47 -1
- package/assets/skills/meko-mcp-tools/references/tools-user-id-conventions.md +6 -39
- package/assets/skills/meko-mcp-tools-desktop/SKILL.md +57 -21
- package/assets/skills/meko-mcp-tools-desktop/desktop/README.md +4 -4
- package/assets/skills/meko-mcp-tools-desktop/references/_sections.md +1 -1
- package/assets/skills/meko-mcp-tools-desktop/references/tools-agent-id-conventions.md +8 -6
- package/assets/skills/meko-mcp-tools-desktop/references/tools-cookbook.md +284 -38
- package/assets/skills/meko-mcp-tools-desktop/references/tools-known-limitations.md +32 -10
- package/assets/skills/meko-mcp-tools-desktop/references/tools-memory-vs-conversation.md +16 -12
- package/assets/skills/meko-mcp-tools-desktop/references/tools-overview.md +46 -31
- package/assets/skills/meko-mcp-tools-desktop/references/tools-rag-workflow.md +79 -14
- package/assets/skills/meko-mcp-tools-desktop/references/tools-troubleshooting.md +69 -1
- package/assets/skills/meko-select-datapack-desktop/SKILL.md +93 -14
- package/assets/skills/meko-select-datapack-desktop/references/cookbook.md +44 -12
- package/assets/skills/meko-select-datapack-desktop/references/role-display-future.md +14 -10
- package/assets/skills/meko-select-datapack-desktop/references/troubleshooting.md +17 -5
- package/bin/create-meko-setup.mjs +186 -30
- package/lib/autofix.mjs +100 -0
- package/lib/browser-login.mjs +495 -0
- package/lib/capture-cleanup.mjs +63 -0
- package/lib/clients/claude-code.mjs +1 -0
- package/lib/clients/codex.mjs +80 -35
- package/lib/clients/common.mjs +7 -0
- package/lib/clients/index.mjs +2 -0
- package/lib/clients/kiro.mjs +5 -5
- package/lib/clients/opencode.mjs +537 -0
- package/lib/config.mjs +68 -1
- package/lib/installer.mjs +18 -2
- package/lib/migrate/adapters/claude-code.mjs +11 -2
- package/lib/migrate/adapters/codex.mjs +9 -2
- package/lib/migrate/adapters/memory-md.mjs +27 -8
- package/lib/migrate/adapters/opencode.mjs +148 -0
- package/lib/migrate/claude-parser.mjs +153 -9
- package/lib/migrate/datapack.mjs +254 -16
- package/lib/migrate/id.mjs +195 -13
- package/lib/migrate/mcp-client.mjs +59 -1
- package/lib/migrate/opencode-normalizer.mjs +132 -0
- package/lib/migrate/orchestrator.mjs +294 -78
- package/lib/migrate/progress.mjs +239 -5
- package/lib/migrate/registry.mjs +72 -0
- package/lib/migrate.mjs +97 -9
- package/lib/validate.mjs +111 -22
- package/package.json +5 -5
- package/scripts/bundle-assets.mjs +4 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Cookbook — meko-select-datapack-desktop
|
|
2
2
|
|
|
3
|
-
Concrete transcripts of the
|
|
3
|
+
Concrete transcripts of the flows the skill supports on Claude Desktop.
|
|
4
4
|
|
|
5
5
|
## Flow 1 — First pin (no existing pin memory)
|
|
6
6
|
|
|
@@ -8,11 +8,11 @@ Concrete transcripts of the four flows the skill supports on Claude Desktop.
|
|
|
8
8
|
|
|
9
9
|
**Skill:**
|
|
10
10
|
```
|
|
11
|
-
> memory_search(query="meko_active_datapack", agent_id="claude_desktop", limit=1)
|
|
11
|
+
> memory_search(query="meko_active_datapack", agent_id="claude_desktop", conversation_id="<conv-id>", limit=1)
|
|
12
12
|
[empty]
|
|
13
13
|
|
|
14
|
-
>
|
|
15
|
-
[
|
|
14
|
+
> datapack_search(conversation_id="<conv-id>", query="prod-research", limit=10, include_total=true)
|
|
15
|
+
[1 row, has_more=false, next_cursor="", total=1]
|
|
16
16
|
|
|
17
17
|
One match: `prod-research`. Pinning.
|
|
18
18
|
|
|
@@ -23,7 +23,7 @@ One match: `prod-research`. Pinning.
|
|
|
23
23
|
metadata='{"type":"active-datapack-pin"}')
|
|
24
24
|
[returns id=mem_abc123]
|
|
25
25
|
|
|
26
|
-
> memory_search(query="meko_active_datapack", agent_id="claude_desktop", limit=1)
|
|
26
|
+
> memory_search(query="meko_active_datapack", agent_id="claude_desktop", conversation_id="<conv-id>", limit=1)
|
|
27
27
|
[verifies the row exists]
|
|
28
28
|
|
|
29
29
|
Pinned datapack `prod-research` (`8b1c…f4a2`) for Claude Desktop.
|
|
@@ -41,11 +41,11 @@ datapack_id flows through automatically.
|
|
|
41
41
|
|
|
42
42
|
**Skill:**
|
|
43
43
|
```
|
|
44
|
-
> memory_search(query="meko_active_datapack", agent_id="claude_desktop", limit=1)
|
|
44
|
+
> memory_search(query="meko_active_datapack", agent_id="claude_desktop", conversation_id="<conv-id>", limit=1)
|
|
45
45
|
[returns id=mem_abc123, text="meko_active_datapack=8b1c…f4a2 name=prod-research …"]
|
|
46
46
|
|
|
47
|
-
>
|
|
48
|
-
[
|
|
47
|
+
> datapack_search(conversation_id="<conv-id>", query="q2-roadmap-deck", limit=10, include_total=true)
|
|
48
|
+
[1 row, has_more=false, next_cursor="", total=1]
|
|
49
49
|
|
|
50
50
|
One match: `q2-roadmap-deck`. Switching pin.
|
|
51
51
|
|
|
@@ -66,11 +66,11 @@ The `memory_update` is critical here — using `memory_add` again would create a
|
|
|
66
66
|
|
|
67
67
|
**Skill:**
|
|
68
68
|
```
|
|
69
|
-
> memory_search(query="meko_active_datapack", agent_id="claude_desktop", limit=1)
|
|
69
|
+
> memory_search(query="meko_active_datapack", agent_id="claude_desktop", conversation_id="<conv-id>", limit=1)
|
|
70
70
|
[empty]
|
|
71
71
|
|
|
72
|
-
>
|
|
73
|
-
[1
|
|
72
|
+
> datapack_search(conversation_id="<conv-id>", limit=10, include_total=true)
|
|
73
|
+
[1 row, has_more=false, next_cursor="", total=1]
|
|
74
74
|
|
|
75
75
|
Only one datapack: `meko-local-setup`. Auto-selecting.
|
|
76
76
|
|
|
@@ -90,7 +90,7 @@ Pinned datapack `meko-local-setup` (`a17d…cc91`). Run this skill again with
|
|
|
90
90
|
|
|
91
91
|
**Skill:**
|
|
92
92
|
```
|
|
93
|
-
> memory_search(query="meko_active_datapack", agent_id="claude_desktop", limit=1)
|
|
93
|
+
> memory_search(query="meko_active_datapack", agent_id="claude_desktop", conversation_id="<conv-id>", limit=1)
|
|
94
94
|
[returns id=mem_abc123]
|
|
95
95
|
|
|
96
96
|
> memory_delete_by_id(
|
|
@@ -119,3 +119,35 @@ This isn't a user-triggered flow — it's the per-turn lookup the skill teaches
|
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
Cache the UUID for the rest of the turn — don't call `memory_search` once per Meko tool call.
|
|
122
|
+
|
|
123
|
+
## Flow 6: Browse and page
|
|
124
|
+
|
|
125
|
+
**User:** `list my datapacks`
|
|
126
|
+
|
|
127
|
+
**Skill:**
|
|
128
|
+
```
|
|
129
|
+
> datapack_search(conversation_id="<conv-id>", limit=10, include_total=true)
|
|
130
|
+
[10 rows, has_more=true, next_cursor="eyJ2Ijox…", total=14]
|
|
131
|
+
|
|
132
|
+
Your datapacks (showing 1-10 of 14):
|
|
133
|
+
…
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**User:** `next`
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
> datapack_search(conversation_id="<conv-id>", limit=10, cursor="eyJ2Ijox…")
|
|
140
|
+
[4 rows, has_more=false, next_cursor=""]
|
|
141
|
+
|
|
142
|
+
Your datapacks (showing 11-14 of 14):
|
|
143
|
+
…
|
|
144
|
+
|
|
145
|
+
Reply with: a number (11-14) to pin · a substring to search · `prev` to page
|
|
146
|
+
· `clear` to unset · `cancel` to leave the current pin alone
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The skill remembers each cursor it used. On `prev` it drops the last one and repeats the call for page 1, which had no cursor. The total (14) is reused from page 1. A substring reply calls `datapack_search(query="<text>", limit=10, include_total=true)` and starts a new listing.
|
|
150
|
+
|
|
151
|
+
## Flow 7: Older server without `datapack_search`
|
|
152
|
+
|
|
153
|
+
The tool list has `datapack_list` but no `datapack_search`. The skill calls `datapack_list(conversation_id="<conv-id>")` once, then sorts, pages and filters that array in the conversation. The table and replies look the same.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Today: render `grant` verbatim
|
|
4
4
|
|
|
5
|
-
The deployed Meko `GET /datapacks` response includes a `grant` field on every datapack. Possible values (per maintainer guidance, 2026-05-26):
|
|
5
|
+
The deployed Meko `GET /datapacks` response includes a `grant` field on every datapack, and so does every row that `datapack_search` and `datapack_list` return. Possible values (per maintainer guidance, 2026-05-26):
|
|
6
6
|
|
|
7
7
|
- `"owner"` — datapacks the caller created.
|
|
8
8
|
- `"maintainer"` — write access to a shared datapack including memories AND collective learnings/knowledge.
|
|
@@ -20,21 +20,25 @@ Use the field directly in the **Role** column:
|
|
|
20
20
|
|
|
21
21
|
Do not invent values, do not normalize the casing, do not display `"Owner"` if the field says `"owner"`. The deployed schema is the source of truth.
|
|
22
22
|
|
|
23
|
-
Note that the
|
|
23
|
+
Note that the server's `Datapack` model doesn't declare `grant`: the deployed server enriches the response beyond what the public Go code lists. Don't rely on the public struct for the wire schema; trust the actual response.
|
|
24
24
|
|
|
25
|
-
##
|
|
25
|
+
## Counts are not on the list response
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
Neither `datapack_search` nor `datapack_list` computes counts; their rows carry no count fields. If the user asks for counts, call `datapack_describe(datapack_id=<id>, conversation_id="<conversation id>")` and read them from that response:
|
|
28
28
|
|
|
29
|
-
- `memory_count
|
|
30
|
-
- `
|
|
31
|
-
- `learnings_count
|
|
29
|
+
- `memory_count`: all memories stored in the datapack.
|
|
30
|
+
- `collective_memory_count`: memories promoted to shared knowledge.
|
|
31
|
+
- `learnings_count`: memories awaiting a promotion decision.
|
|
32
|
+
- `knowledge_base_file_count`: uploaded KB documents (`null` if the status endpoint is unreachable).
|
|
33
|
+
- `knowledge_chunk_count`: KB chunks across all documents. `knowledge_count` is a deprecated alias.
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
Don't add counts to the table silently, and never invent the numbers.
|
|
34
36
|
|
|
35
|
-
## Sharing
|
|
37
|
+
## Sharing a datapack
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
An owner or maintainer shares a datapack from the Cloud console's **Share** page. Each datapack has one share link. The owner or maintainer can turn it on or off, reset it, and choose whether people who open it join as viewer or contributor. Anyone signed in who opens an active link joins straight away. There are no MCP tools to manage sharing.
|
|
40
|
+
|
|
41
|
+
A viewer is read-only, and the server enforces it: over MCP, conversation and memory writes return `read_only_access`. If the user picks a datapack where they're a viewer, tell them that Meko won't record conversations or save memories there.
|
|
38
42
|
|
|
39
43
|
## Why the skill ships without Mine / Shared / All filter tabs
|
|
40
44
|
|
|
@@ -18,12 +18,12 @@ You (or an earlier run) created duplicates by using `memory_add` instead of `mem
|
|
|
18
18
|
|
|
19
19
|
The cookbook's Flow 2 demonstrates the correct switch path.
|
|
20
20
|
|
|
21
|
-
## Symptom: A pinned datapack returns
|
|
21
|
+
## Symptom: A pinned datapack returns `datapack_access_denied` when used
|
|
22
22
|
|
|
23
|
-
The pin became stale
|
|
23
|
+
The pin became stale: the datapack was deleted, or its share to you was revoked, in another client. The server checks ownership and shares on every call that passes a `datapack_id`, so the call fails with `datapack_access_denied` (older servers returned a not-found error). The check also fails closed on a transient server database error, so retry the call once before treating the pin as stale. Tell the user:
|
|
24
24
|
|
|
25
25
|
```
|
|
26
|
-
The pinned datapack `<name>` (`<id>`) no longer exists
|
|
26
|
+
The pinned datapack `<name>` (`<id>`) no longer exists or is no longer shared with you.
|
|
27
27
|
Run `meko-select-datapack-desktop` to pick a fresh one, or `clear` to unpin.
|
|
28
28
|
```
|
|
29
29
|
|
|
@@ -33,20 +33,32 @@ Then either re-run the skill or delete the pin memory. Do not silently fall back
|
|
|
33
33
|
|
|
34
34
|
Never auto-pick when there's ambiguity. Re-render just the matching rows numbered from 1, and ask the user to pick a number from the filtered subset. Same as the Claude Code variant.
|
|
35
35
|
|
|
36
|
-
## Symptom: `datapack_list` returns an empty array
|
|
36
|
+
## Symptom: The account has no datapacks (`total` is 0, or `datapack_list` returns an empty array)
|
|
37
37
|
|
|
38
38
|
The user has zero datapacks. Print:
|
|
39
39
|
|
|
40
40
|
```
|
|
41
41
|
You don't have any datapacks yet. Create one with:
|
|
42
42
|
|
|
43
|
-
datapack_create(name="<your-datapack-name>")
|
|
43
|
+
datapack_create(name="<your-datapack-name>", conversation_id="<conversation id>")
|
|
44
44
|
|
|
45
45
|
Or visit the Meko Cloud console (Datapacks → New datapack).
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
Stop. Don't `memory_add` an empty pin.
|
|
49
49
|
|
|
50
|
+
## Symptom: `next` or `prev` returns an error about the cursor
|
|
51
|
+
|
|
52
|
+
The cursor is tied to the search it came from. It stops working if you change `query`, `sort` or `grant` while paging, or if you edit it. Always send the cursor back unchanged, with the same filters as the call that returned it. Tell the user the page could not be loaded and start again from page 1 of the same search (no cursor).
|
|
53
|
+
|
|
54
|
+
## Symptom: `datapack_list` returns `too_many_datapacks`
|
|
55
|
+
|
|
56
|
+
The account can see more datapacks than `datapack_list` returns in one array (1000 by default). The server that sends this error also has `datapack_search`. Use the main flow with `datapack_search`; if it is missing from your tool list, the list is stale, so reload the Meko tools.
|
|
57
|
+
|
|
58
|
+
## Symptom: The tool list has no `datapack_search`
|
|
59
|
+
|
|
60
|
+
The Meko server is older than that tool. Use the skill's fallback flow with `datapack_list`: one call, then sort, page and filter the array in the conversation. On an older server, this call can fail for an account with a very large number of datapacks; that account needs a server that has `datapack_search`.
|
|
61
|
+
|
|
50
62
|
## Symptom: User typed `cancel` after the table rendered
|
|
51
63
|
|
|
52
64
|
Leave the existing pin memory alone. Print:
|
|
@@ -7,7 +7,8 @@ import { dirname, join } from 'node:path';
|
|
|
7
7
|
|
|
8
8
|
import { install, uninstall } from '../lib/installer.mjs';
|
|
9
9
|
import { askChoice, askMultiChoice, askText, askSecret, askYesNo, close as closePrompts } from '../lib/prompt.mjs';
|
|
10
|
-
import { banner, error, dim, info, warn } from '../lib/logger.mjs';
|
|
10
|
+
import { banner, error, dim, info, success, warn } from '../lib/logger.mjs';
|
|
11
|
+
import { BrowserLoginError, consoleUrlFor, loginAndCreateApiKey } from '../lib/browser-login.mjs';
|
|
11
12
|
import {
|
|
12
13
|
isPackagedInstall,
|
|
13
14
|
MEKO_PRODUCTION_CONSOLE_URL,
|
|
@@ -19,6 +20,8 @@ import { parseClientList } from '../lib/clients/aliases.mjs';
|
|
|
19
20
|
import { startMigration, runChildMigration, readStatus, stopMigration } from '../lib/migrate.mjs';
|
|
20
21
|
import { checkLatest } from '../lib/version-check.mjs';
|
|
21
22
|
import { runCaptureStatus } from '../lib/capture-status.mjs';
|
|
23
|
+
import { captureCleanupArgs, runCaptureCleanup } from '../lib/capture-cleanup.mjs';
|
|
24
|
+
import { applyAutofixSetting, describeAutofix } from '../lib/autofix.mjs';
|
|
22
25
|
|
|
23
26
|
// ---------------------------------------------------------------------------
|
|
24
27
|
// Flag definitions
|
|
@@ -27,6 +30,7 @@ import { runCaptureStatus } from '../lib/capture-status.mjs';
|
|
|
27
30
|
const options = {
|
|
28
31
|
url: { type: 'string' },
|
|
29
32
|
'api-key': { type: 'string' },
|
|
33
|
+
'no-browser': { type: 'boolean', default: false },
|
|
30
34
|
local: { type: 'boolean', default: false },
|
|
31
35
|
scope: { type: 'string', default: 'user' },
|
|
32
36
|
name: { type: 'string', default: 'meko' },
|
|
@@ -36,6 +40,8 @@ const options = {
|
|
|
36
40
|
'skip-validation': { type: 'boolean', default: false },
|
|
37
41
|
statusline: { type: 'boolean', default: false },
|
|
38
42
|
'no-statusline': { type: 'boolean', default: false },
|
|
43
|
+
autofix: { type: 'boolean', default: false },
|
|
44
|
+
'no-autofix': { type: 'boolean', default: false },
|
|
39
45
|
'no-canary': { type: 'boolean', default: false },
|
|
40
46
|
'dry-run': { type: 'boolean', default: false },
|
|
41
47
|
'check-updates': { type: 'boolean', default: false },
|
|
@@ -48,6 +54,11 @@ const options = {
|
|
|
48
54
|
// --- Capture health (Capture V2 Workstream D) ---
|
|
49
55
|
'capture-status': { type: 'boolean', default: false },
|
|
50
56
|
json: { type: 'boolean', default: false },
|
|
57
|
+
'capture-cleanup': { type: 'string' },
|
|
58
|
+
session: { type: 'string', multiple: true },
|
|
59
|
+
all: { type: 'boolean', default: false },
|
|
60
|
+
'max-exchanges': { type: 'string' },
|
|
61
|
+
'pace-ms': { type: 'string' },
|
|
51
62
|
|
|
52
63
|
// --- Migrate subcommand ---
|
|
53
64
|
migrate: { type: 'boolean', default: false },
|
|
@@ -75,11 +86,11 @@ function readVersion() {
|
|
|
75
86
|
return pkg.version;
|
|
76
87
|
}
|
|
77
88
|
|
|
78
|
-
const ALL_CLIENTS = ['claude-code', 'claude-desktop', 'cursor', 'codex', 'kiro'];
|
|
89
|
+
const ALL_CLIENTS = ['claude-code', 'claude-desktop', 'cursor', 'codex', 'kiro', 'opencode'];
|
|
79
90
|
|
|
80
91
|
function printHelp() {
|
|
81
92
|
const text = `
|
|
82
|
-
Meko Setup for Claude Code, Claude Desktop, Cursor, Codex, and
|
|
93
|
+
Meko Setup for Claude Code, Claude Desktop, Cursor, Codex, Kiro, and OpenCode
|
|
83
94
|
|
|
84
95
|
Install and configure Meko MCP Server in one command.
|
|
85
96
|
|
|
@@ -90,7 +101,10 @@ Examples:
|
|
|
90
101
|
# Interactive setup (recommended for first-time users):
|
|
91
102
|
create-meko-setup
|
|
92
103
|
|
|
93
|
-
# Cloud Meko for Claude Code (
|
|
104
|
+
# Cloud Meko for Claude Code (signs in through your browser):
|
|
105
|
+
create-meko-setup --url https://mcp.mekodata.ai/mcp --client claude-code
|
|
106
|
+
|
|
107
|
+
# Cloud Meko with an existing API key (unattended, e.g. CI):
|
|
94
108
|
create-meko-setup --url https://mcp.mekodata.ai/mcp --api-key "$MEKO_API_KEY" --yes
|
|
95
109
|
|
|
96
110
|
# Local Meko with Cursor config:
|
|
@@ -108,11 +122,13 @@ Examples:
|
|
|
108
122
|
# Install for Kiro (IDE and kiro-cli share the same config):
|
|
109
123
|
create-meko-setup --client kiro --yes
|
|
110
124
|
|
|
125
|
+
create-meko-setup --client opencode --scope user --yes
|
|
126
|
+
|
|
111
127
|
# Local Meko (docker-compose stack, Claude Code):
|
|
112
128
|
create-meko-setup --local --yes
|
|
113
129
|
|
|
114
130
|
Options:
|
|
115
|
-
--client <list> Target clients: one or more of claude-code, claude-desktop, cursor, codex, kiro.
|
|
131
|
+
--client <list> Target clients: one or more of claude-code, claude-desktop, cursor, codex, kiro, opencode.
|
|
116
132
|
"claude" is accepted as an alias for claude-code;
|
|
117
133
|
"kiro-cli" and "kiro-ide" for kiro.
|
|
118
134
|
Comma-separated, or "all" to target every supported client (default: claude-code)
|
|
@@ -123,7 +139,10 @@ Options:
|
|
|
123
139
|
untrusted IDE workspaces and non-managed CLI agents fall
|
|
124
140
|
back to agent-driven persistence.
|
|
125
141
|
--url <url> MCP server URL (from cloud.mekodata.ai > Datapacks > Connect)
|
|
126
|
-
--api-key <key>
|
|
142
|
+
--api-key <key> Use an existing Cloud Meko API key instead of signing in
|
|
143
|
+
through the browser. Also read from $MEKO_API_KEY.
|
|
144
|
+
Required when no terminal is attached (CI, scripts).
|
|
145
|
+
--no-browser Print the sign-in URL instead of opening a browser
|
|
127
146
|
--local Use local Meko at http://localhost:8000/mcp
|
|
128
147
|
--scope <scope> Config scope: user or project (default: user)
|
|
129
148
|
codex always installs at user scope (no project-level
|
|
@@ -136,8 +155,13 @@ Options:
|
|
|
136
155
|
Default: ON under --yes, prompted otherwise.
|
|
137
156
|
Claude Code only.
|
|
138
157
|
--no-statusline Skip statusline wiring even under --yes.
|
|
158
|
+
--no-autofix Turn off automatic capture repair: the capture timer
|
|
159
|
+
otherwise runs --capture-cleanup fix every 30 minutes
|
|
160
|
+
(MEKO_AUTOFIX_INTERVAL) when a session is stuck.
|
|
161
|
+
--autofix Turn automatic capture repair back on.
|
|
139
162
|
--skip-validation Skip post-install validation
|
|
140
|
-
--no-canary Skip the canary
|
|
163
|
+
--no-canary Skip the canary datapack_search (or, on older
|
|
164
|
+
servers, datapack_list) call. The
|
|
141
165
|
connectivity + tools/list checks still run.
|
|
142
166
|
--dry-run Show what would be done without making changes
|
|
143
167
|
--check-updates Compare installed version against npm registry and exit
|
|
@@ -153,7 +177,31 @@ Capture health (durable outbox / recovery):
|
|
|
153
177
|
Exit 0 = healthy|catching_up, 2 = degraded_retrying,
|
|
154
178
|
3 = blocked_action_required.
|
|
155
179
|
--json With --capture-status, print machine-readable JSON
|
|
156
|
-
matching the capture-health cache shape.
|
|
180
|
+
matching the capture-health cache shape. Also
|
|
181
|
+
accepted by --capture-cleanup.
|
|
182
|
+
--capture-cleanup <action>
|
|
183
|
+
Clean up stuck capture on this machine:
|
|
184
|
+
status list sessions with unsent work and the
|
|
185
|
+
background capture processes (read-only)
|
|
186
|
+
fix diagnose each stuck session and fix it:
|
|
187
|
+
repair, drain, or move unsent turns to a
|
|
188
|
+
new conversation; reports what only you
|
|
189
|
+
can fix (start here)
|
|
190
|
+
stop stop SessionEnd workers and in-flight
|
|
191
|
+
drains, and park all unsent work so no
|
|
192
|
+
hook retries it (nothing is deleted)
|
|
193
|
+
resume unpark and hand back to the hooks
|
|
194
|
+
drain send now, one session at a time; stops
|
|
195
|
+
and re-parks at the first failure
|
|
196
|
+
discard skip a session's unsent exchanges for
|
|
197
|
+
good (needs --yes)
|
|
198
|
+
repair clear stale failure fields and leftover
|
|
199
|
+
PID, lease, and job files
|
|
200
|
+
Options: --session <id> or a bare session ID
|
|
201
|
+
(repeatable, prefix ok),
|
|
202
|
+
--all, --dry-run, --json, --yes,
|
|
203
|
+
--max-exchanges N (drain, default 200),
|
|
204
|
+
--pace-ms N (drain, default 1000).
|
|
157
205
|
|
|
158
206
|
Migrate existing conversations and memories into Meko:
|
|
159
207
|
--migrate Start a detached migration of existing Claude Code
|
|
@@ -267,12 +315,53 @@ function parseRate(raw) {
|
|
|
267
315
|
return n;
|
|
268
316
|
}
|
|
269
317
|
|
|
318
|
+
// Placeholder shown in --dry-run output in place of a key that browser
|
|
319
|
+
// sign-in would create. Never sent anywhere: dry runs make no changes.
|
|
320
|
+
const DRY_RUN_SIGN_IN_KEY = 'mko_tkn_<created-by-browser-sign-in>';
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Sign in through the browser and create an API key for this install.
|
|
324
|
+
* Returns the key, or null after printing why sign-in failed.
|
|
325
|
+
*/
|
|
326
|
+
async function signInWithBrowser({ url, noBrowser, dryRun }) {
|
|
327
|
+
if (dryRun) {
|
|
328
|
+
dim('Would sign in with your browser and create an API key (skipped: --dry-run).');
|
|
329
|
+
return DRY_RUN_SIGN_IN_KEY;
|
|
330
|
+
}
|
|
331
|
+
info('Signing in to Meko with your browser to create an API key...');
|
|
332
|
+
try {
|
|
333
|
+
const key = await loginAndCreateApiKey({ url, noBrowser, notify: (msg) => dim(msg) });
|
|
334
|
+
const expires = key.expiresAt ? ` (expires ${String(key.expiresAt).slice(0, 10)})` : '';
|
|
335
|
+
success(`Signed in. Created API key "${key.name}"${expires}.`);
|
|
336
|
+
dim(`Manage or revoke it at ${consoleUrlFor(url)} > Settings > API Keys.`);
|
|
337
|
+
return key.apiKey;
|
|
338
|
+
} catch (err) {
|
|
339
|
+
error(err instanceof BrowserLoginError ? err.message : `Browser sign-in failed: ${err?.message ?? err}`);
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
270
344
|
// ---------------------------------------------------------------------------
|
|
271
345
|
// Main
|
|
272
346
|
// ---------------------------------------------------------------------------
|
|
273
347
|
|
|
274
348
|
async function main() {
|
|
275
|
-
const { values: flags } = parseArgs({
|
|
349
|
+
const { values: flags, positionals } = parseArgs({
|
|
350
|
+
options,
|
|
351
|
+
strict: true,
|
|
352
|
+
allowPositionals: true,
|
|
353
|
+
});
|
|
354
|
+
// Only --capture-cleanup takes bare arguments: session IDs, so
|
|
355
|
+
// `--capture-cleanup drain c0acdeb2` works like `--session c0acdeb2`.
|
|
356
|
+
if (positionals.length > 0) {
|
|
357
|
+
if (flags['capture-cleanup'] === undefined) {
|
|
358
|
+
error(
|
|
359
|
+
`Unexpected argument '${positionals[0]}'. This command does not take positional arguments`,
|
|
360
|
+
);
|
|
361
|
+
process.exit(1);
|
|
362
|
+
}
|
|
363
|
+
flags.session = [...(flags.session || []), ...positionals];
|
|
364
|
+
}
|
|
276
365
|
const projectRoot = process.cwd();
|
|
277
366
|
|
|
278
367
|
// --help
|
|
@@ -297,8 +386,17 @@ async function main() {
|
|
|
297
386
|
if (out.stderr) process.stderr.write(out.stderr);
|
|
298
387
|
process.exit(out.code);
|
|
299
388
|
}
|
|
389
|
+
// --capture-cleanup <action>: inspect, fix, stop, park, drain, or discard
|
|
390
|
+
// stuck capture sessions. Exit codes: 0 done, 1 error, 2 partly done.
|
|
391
|
+
if (flags['capture-cleanup'] !== undefined) {
|
|
392
|
+
const out = runCaptureCleanup(
|
|
393
|
+
captureCleanupArgs(flags['capture-cleanup'], flags),
|
|
394
|
+
);
|
|
395
|
+
if (out.error) error(out.error);
|
|
396
|
+
process.exit(out.code);
|
|
397
|
+
}
|
|
300
398
|
if (flags.json) {
|
|
301
|
-
error('--json requires --capture-status');
|
|
399
|
+
error('--json requires --capture-status or --capture-cleanup');
|
|
302
400
|
process.exit(1);
|
|
303
401
|
}
|
|
304
402
|
|
|
@@ -456,6 +554,10 @@ async function main() {
|
|
|
456
554
|
// submitting an empty value at the full-flow prompt would re-ask the
|
|
457
555
|
// same question.
|
|
458
556
|
let promptedForApiKey = false;
|
|
557
|
+
// Set when the user picked "Sign in with browser" at the interactive auth
|
|
558
|
+
// prompt. Someone answered that prompt, so sign-in runs even when stdin
|
|
559
|
+
// is not a TTY (e.g. piped answers in the smoke harness).
|
|
560
|
+
let choseBrowserSignIn = false;
|
|
459
561
|
|
|
460
562
|
// --local shortcut
|
|
461
563
|
if (flags.local) {
|
|
@@ -478,6 +580,7 @@ async function main() {
|
|
|
478
580
|
cursor: 'Cursor',
|
|
479
581
|
codex: 'OpenAI Codex (CLI + App)',
|
|
480
582
|
kiro: 'Kiro (IDE + kiro-cli)',
|
|
583
|
+
opencode: 'OpenCode CLI/TUI',
|
|
481
584
|
}[id];
|
|
482
585
|
return {
|
|
483
586
|
label: id,
|
|
@@ -538,7 +641,7 @@ async function main() {
|
|
|
538
641
|
// When only Cloud is available, skip the prompt entirely — asking to
|
|
539
642
|
// pick between one option is pointless UX.
|
|
540
643
|
const connectionOptions = [
|
|
541
|
-
{ label: 'Cloud Meko', description: 'Hosted at mekodata.ai (
|
|
644
|
+
{ label: 'Cloud Meko', description: 'Hosted at mekodata.ai (sign in with your browser)' },
|
|
542
645
|
];
|
|
543
646
|
if (!isPackagedInstall()) {
|
|
544
647
|
connectionOptions.push({
|
|
@@ -567,11 +670,24 @@ async function main() {
|
|
|
567
670
|
suggestedUrl?.url ?? undefined,
|
|
568
671
|
);
|
|
569
672
|
|
|
570
|
-
// Step 3 —
|
|
571
|
-
//
|
|
572
|
-
//
|
|
573
|
-
apiKey
|
|
574
|
-
|
|
673
|
+
// Step 3 — authentication. Browser sign-in is the default and runs
|
|
674
|
+
// in the Cloud-URL check below; pasting a key is the advanced path.
|
|
675
|
+
// An existing --api-key or $MEKO_API_KEY skips the question.
|
|
676
|
+
if (!apiKey && !normalizeApiKey(process.env.MEKO_API_KEY)) {
|
|
677
|
+
const authChoice = await askChoice('How do you want to authenticate?', [
|
|
678
|
+
{ label: 'Sign in with browser', description: 'Recommended. Creates an API key for you' },
|
|
679
|
+
{ label: 'Paste an API key', description: `From ${consoleUrlFor(url)} > Settings > API Keys` },
|
|
680
|
+
]);
|
|
681
|
+
if (authChoice === 'Paste an API key') {
|
|
682
|
+
// Normalize: terminal paste can drag in trailing whitespace/CR or
|
|
683
|
+
// accidental surrounding quotes, all of which would sail straight
|
|
684
|
+
// into `Authorization: Bearer ...` and 401.
|
|
685
|
+
apiKey = normalizeApiKey(await askSecret('API key:'));
|
|
686
|
+
promptedForApiKey = true;
|
|
687
|
+
} else {
|
|
688
|
+
choseBrowserSignIn = true;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
575
691
|
} else {
|
|
576
692
|
url = 'http://localhost:8000/mcp';
|
|
577
693
|
apiKey = null;
|
|
@@ -610,25 +726,38 @@ async function main() {
|
|
|
610
726
|
|
|
611
727
|
// Validation — Cloud URL requires an API key.
|
|
612
728
|
// Resolution order: --api-key (already in `apiKey`) → $MEKO_API_KEY →
|
|
613
|
-
//
|
|
614
|
-
//
|
|
615
|
-
//
|
|
616
|
-
//
|
|
617
|
-
//
|
|
618
|
-
//
|
|
729
|
+
// browser sign-in, which creates a key through the server's
|
|
730
|
+
// POST /api-keys. Sign-in also covers the cloud one-liner from the UI,
|
|
731
|
+
// which only shows --url and --client. It is skipped when the full
|
|
732
|
+
// interactive flow already took a pasted key (an empty paste is an
|
|
733
|
+
// error, not a silent switch to the browser), and whenever stdin is not
|
|
734
|
+
// a terminal, with or without --yes: a script or CI step may have nobody
|
|
735
|
+
// at the browser, and a 5-minute callback wait would stall it. The one
|
|
736
|
+
// exception is an explicit "Sign in with browser" answer at the prompt.
|
|
619
737
|
if (isCloudUrl(url) && !apiKey) {
|
|
620
738
|
apiKey = normalizeApiKey(process.env.MEKO_API_KEY);
|
|
739
|
+
let signInFailed = false;
|
|
621
740
|
if (apiKey) {
|
|
622
741
|
dim('Using API key from MEKO_API_KEY environment variable.');
|
|
623
|
-
} else if (!
|
|
624
|
-
|
|
625
|
-
|
|
742
|
+
} else if (!promptedForApiKey && (process.stdin.isTTY || choseBrowserSignIn)) {
|
|
743
|
+
apiKey = await signInWithBrowser({
|
|
744
|
+
url,
|
|
745
|
+
noBrowser: flags['no-browser'],
|
|
746
|
+
dryRun: flags['dry-run'],
|
|
747
|
+
});
|
|
748
|
+
signInFailed = !apiKey;
|
|
626
749
|
}
|
|
627
750
|
if (!apiKey) {
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
)
|
|
751
|
+
let message;
|
|
752
|
+
if (signInFailed) {
|
|
753
|
+
message = `Browser sign-in did not complete. To use an existing key instead, create one at ${consoleUrlFor(url)} > Settings > API Keys and rerun with --api-key, or set MEKO_API_KEY.`;
|
|
754
|
+
} else if (promptedForApiKey) {
|
|
755
|
+
message = 'No API key entered. Rerun and choose Sign in with browser, or pass --api-key.';
|
|
756
|
+
} else {
|
|
757
|
+
message = 'API key is required for Cloud Meko. ' +
|
|
758
|
+
'Provide --api-key, set MEKO_API_KEY env var, or run in a terminal to sign in with your browser.';
|
|
759
|
+
}
|
|
760
|
+
error(message);
|
|
632
761
|
process.exit(1);
|
|
633
762
|
}
|
|
634
763
|
}
|
|
@@ -663,6 +792,23 @@ async function main() {
|
|
|
663
792
|
}
|
|
664
793
|
}
|
|
665
794
|
|
|
795
|
+
// Automatic capture repair runs inside the capture timer the hooks start,
|
|
796
|
+
// so installing the hooks deploys it. Save --autofix / --no-autofix once,
|
|
797
|
+
// for every client (they share one capture directory).
|
|
798
|
+
if (errors.length < clients.length) {
|
|
799
|
+
if (flags.autofix && flags['no-autofix']) {
|
|
800
|
+
warn('Both --autofix and --no-autofix were given; keeping the current setting.');
|
|
801
|
+
}
|
|
802
|
+
const enable =
|
|
803
|
+
flags['no-autofix'] && !flags.autofix ? false : flags.autofix && !flags['no-autofix'] ? true : undefined;
|
|
804
|
+
try {
|
|
805
|
+
const res = applyAutofixSetting({ enable, dryRun: flags['dry-run'], projectRoot });
|
|
806
|
+
info(describeAutofix(res.enabled));
|
|
807
|
+
} catch (err) {
|
|
808
|
+
warn(`Could not save the automatic repair setting: ${err.message}`);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
666
812
|
if (errors.length === clients.length) {
|
|
667
813
|
process.exit(1);
|
|
668
814
|
} else if (errors.length > 0) {
|
|
@@ -760,15 +906,23 @@ async function maybeOfferMigration({ url, apiKey, scope, projectRoot, datapack }
|
|
|
760
906
|
dryRun: true,
|
|
761
907
|
datapack: datapack ?? null,
|
|
762
908
|
});
|
|
909
|
+
if (dry && dry.ok === false) {
|
|
910
|
+
dim(`Skipped migration offer: ${dry.message || "dry run failed"}`);
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
763
913
|
const s = dry?.message?.match(/Claude Code sessions: *(\d+)/);
|
|
764
914
|
const c = dry?.message?.match(/Cursor sessions: *(\d+)/);
|
|
915
|
+
const x = dry?.message?.match(/Codex sessions: *(\d+)/);
|
|
916
|
+
const o = dry?.message?.match(/OpenCode sessions: *(\d+)/);
|
|
765
917
|
const m = dry?.message?.match(/Memory items \(MD\): *(\d+)/);
|
|
766
918
|
const counts = {
|
|
767
919
|
claude: s ? Number(s[1]) : 0,
|
|
768
920
|
cursor: c ? Number(c[1]) : 0,
|
|
921
|
+
codex: x ? Number(x[1]) : 0,
|
|
922
|
+
opencode: o ? Number(o[1]) : 0,
|
|
769
923
|
memory: m ? Number(m[1]) : 0,
|
|
770
924
|
};
|
|
771
|
-
const total = counts.claude + counts.cursor + counts.memory;
|
|
925
|
+
const total = counts.claude + counts.cursor + counts.codex + counts.opencode + counts.memory;
|
|
772
926
|
if (total === 0) return;
|
|
773
927
|
|
|
774
928
|
console.log('');
|
|
@@ -776,6 +930,8 @@ async function maybeOfferMigration({ url, apiKey, scope, projectRoot, datapack }
|
|
|
776
930
|
`Found existing data to migrate: ` +
|
|
777
931
|
`${counts.claude} Claude Code sessions, ` +
|
|
778
932
|
`${counts.cursor} Cursor sessions, ` +
|
|
933
|
+
`${counts.codex} Codex sessions, ` +
|
|
934
|
+
`${counts.opencode} OpenCode sessions, ` +
|
|
779
935
|
`${counts.memory} memory items.`,
|
|
780
936
|
);
|
|
781
937
|
const proceed = await askYesNo(
|