@yugabytedb/meko-mcp 1.1.0 → 1.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 (26) hide show
  1. package/README.md +68 -19
  2. package/assets/hooks-handlers/before-submit-prompt.sh +44 -0
  3. package/assets/hooks-handlers/cursor-session-start.sh +42 -0
  4. package/assets/hooks-handlers/lib/capture.js +291 -40
  5. package/assets/scripts/package-desktop-skill.sh +36 -0
  6. package/assets/skills/meko-mcp-tools/references/tools-overview.md +1 -1
  7. package/assets/skills/meko-mcp-tools-desktop/SKILL.md +251 -0
  8. package/assets/skills/meko-mcp-tools-desktop/desktop/README.md +150 -0
  9. package/assets/skills/meko-mcp-tools-desktop/references/_sections.md +24 -0
  10. package/assets/skills/meko-mcp-tools-desktop/references/tools-agent-id-conventions.md +99 -0
  11. package/assets/skills/meko-mcp-tools-desktop/references/tools-cookbook.md +406 -0
  12. package/assets/skills/meko-mcp-tools-desktop/references/tools-known-limitations.md +69 -0
  13. package/assets/skills/meko-mcp-tools-desktop/references/tools-memory-vs-conversation.md +217 -0
  14. package/assets/skills/meko-mcp-tools-desktop/references/tools-overview.md +112 -0
  15. package/assets/skills/meko-mcp-tools-desktop/references/tools-rag-workflow.md +134 -0
  16. package/assets/skills/meko-mcp-tools-desktop/references/tools-scope-permissions.md +75 -0
  17. package/assets/skills/meko-mcp-tools-desktop/references/tools-troubleshooting.md +155 -0
  18. package/bin/create-meko-setup.mjs +4 -4
  19. package/lib/clients/claude-desktop.mjs +11 -8
  20. package/lib/clients/codex.mjs +236 -4
  21. package/lib/clients/common.mjs +81 -0
  22. package/lib/clients/cursor.mjs +47 -10
  23. package/lib/config.mjs +26 -0
  24. package/lib/installer.mjs +2 -2
  25. package/package.json +1 -1
  26. package/scripts/bundle-assets.mjs +8 -0
@@ -17,6 +17,7 @@ import {
17
17
  } from "../config.mjs";
18
18
  import {
19
19
  getInstallerVersion,
20
+ isDanglingSymlink,
20
21
  markerIsStale,
21
22
  readInstallerMarker,
22
23
  resolveHooksRoot,
@@ -29,7 +30,8 @@ import {
29
30
  const SKILLS = [{ name: "meko-mcp-tools" }];
30
31
 
31
32
  const HOOKS = [
32
- { event: "sessionStart", script: "session-start.sh", timeout: 15000 },
33
+ { event: "sessionStart", script: "cursor-session-start.sh", timeout: 15000 },
34
+ { event: "beforeSubmitPrompt", script: "before-submit-prompt.sh", matcher: "UserPromptSubmit", timeout: 2000 },
33
35
  { event: "preCompact", script: "pre-compact.sh" },
34
36
  { event: "sessionEnd", script: "session-end.sh", timeout: 30000 },
35
37
  ];
@@ -101,21 +103,21 @@ export function createCursorAdapter(customPaths = {}) {
101
103
  writeJsonFile(mcpPath, cfg);
102
104
  },
103
105
  setHookEnvironment({ url, apiKey, dryRun, log, scope, projectRoot, cursorHookMode }) {
104
- const mode = cursorHookMode || "hybrid";
106
+ const mode = cursorHookMode || "native";
105
107
  if (dryRun) {
106
- if (mode === "native" || mode === "hybrid") {
108
+ if (mode === "native") {
107
109
  log.dim(` Would configure Cursor hooks at ${paths.getHooksPath(scope, projectRoot)}`);
108
110
  }
109
- if (mode === "claude-compat" || mode === "hybrid") {
111
+ if (mode === "claude-compat") {
110
112
  log.dim(` Would configure Claude-compatible hooks at ${paths.getClaudeCompatSettingsPath(scope, projectRoot)}`);
111
113
  }
112
114
  return;
113
115
  }
114
116
 
115
- if (mode === "native" || mode === "hybrid") {
117
+ if (mode === "native") {
116
118
  installNativeCursorHooks({ scope, projectRoot, url, apiKey, paths });
117
119
  }
118
- if (mode === "claude-compat" || mode === "hybrid") {
120
+ if (mode === "claude-compat") {
119
121
  installClaudeCompatHooks({ scope, projectRoot, url, apiKey, paths });
120
122
  }
121
123
  },
@@ -159,6 +161,15 @@ export function createCursorAdapter(customPaths = {}) {
159
161
  // Same version already deployed — nothing to do.
160
162
  continue;
161
163
  }
164
+ } else if (isDanglingSymlink(destination)) {
165
+ // Stale symlink left behind by an earlier install whose target
166
+ // (e.g. a deleted worktree) no longer exists. existsSync is false
167
+ // for these, so the branch above misses them. Clear it so the
168
+ // fresh-install path below can replace it cleanly.
169
+ log.dim?.(
170
+ ` Replacing dangling symlink at ${destination} (target missing).`,
171
+ );
172
+ removePath(destination);
162
173
  }
163
174
 
164
175
  const strategy = symlinkOrCopy(source, destination);
@@ -186,7 +197,7 @@ export function createCursorAdapter(customPaths = {}) {
186
197
  uninstallCursorHooks({
187
198
  scope,
188
199
  projectRoot,
189
- mode: cursorHookMode || "hybrid",
200
+ mode: cursorHookMode || "native",
190
201
  paths,
191
202
  });
192
203
  results.push({ key: "hooks", ok: true, message: "Cursor hook configuration cleaned up." });
@@ -243,10 +254,12 @@ function installNativeCursorHooks({ scope, projectRoot, url, apiKey, paths }) {
243
254
  }
244
255
 
245
256
  const handlersRoot = resolveHooksRoot();
257
+ pruneNativeCursorMekoHooks(cfg.hooks);
246
258
  for (const hook of HOOKS) {
247
259
  const scriptPath = join(handlersRoot, hook.script);
248
260
  const entry = { command: buildScriptCommand({ scriptPath }) };
249
261
  if (hook.timeout) entry.timeout = hook.timeout;
262
+ if (hook.matcher) entry.matcher = hook.matcher;
250
263
  upsertHookEntry(cfg.hooks, hook.event, entry);
251
264
  }
252
265
 
@@ -305,6 +318,30 @@ function upsertHookEntry(hooksObj, event, entry) {
305
318
  if (!exists) hooksObj[event].push(entry);
306
319
  }
307
320
 
321
+ function pruneNativeCursorMekoHooks(hooksObj) {
322
+ if (!hooksObj || typeof hooksObj !== "object") return;
323
+ for (const event of Object.keys(hooksObj)) {
324
+ if (!Array.isArray(hooksObj[event])) continue;
325
+ hooksObj[event] = hooksObj[event].filter((item) => {
326
+ if (!item?.command) return true;
327
+ return !isMekoHookCommand(item.command);
328
+ });
329
+ }
330
+ }
331
+
332
+ function isMekoHookCommand(command) {
333
+ if (typeof command !== "string") return false;
334
+ if (!command.includes("hooks-handlers/")) return false;
335
+ return [
336
+ "session-start.sh",
337
+ "cursor-session-start.sh",
338
+ "before-submit-prompt.sh",
339
+ "pre-compact.sh",
340
+ "session-end.sh",
341
+ "post-tool-context.sh",
342
+ ].some((script) => command.includes(script));
343
+ }
344
+
308
345
  function upsertClaudeCompatHook(hooksObj, event, entry) {
309
346
  if (!Array.isArray(hooksObj[event])) hooksObj[event] = [];
310
347
  let bucket = hooksObj[event].find((item) => Array.isArray(item.hooks));
@@ -318,10 +355,10 @@ function upsertClaudeCompatHook(hooksObj, event, entry) {
318
355
 
319
356
  function uninstallCursorHooks({ scope, projectRoot, mode, paths }) {
320
357
  const handlersRoot = resolveHooksRoot();
321
- const scripts = ["session-start.sh", "pre-compact.sh", "session-end.sh"]
358
+ const scripts = ["session-start.sh", "cursor-session-start.sh", "before-submit-prompt.sh", "pre-compact.sh", "session-end.sh"]
322
359
  .map((name) => join(handlersRoot, name));
323
360
 
324
- if (mode === "native" || mode === "hybrid") {
361
+ if (mode === "native") {
325
362
  const hooksPath = paths.getHooksPath(scope, projectRoot);
326
363
  const cfg = readJsonFile(hooksPath);
327
364
  if (cfg?.hooks && typeof cfg.hooks === "object") {
@@ -336,7 +373,7 @@ function uninstallCursorHooks({ scope, projectRoot, mode, paths }) {
336
373
  }
337
374
  }
338
375
 
339
- if (mode === "claude-compat" || mode === "hybrid") {
376
+ if (mode === "claude-compat") {
340
377
  // NOTE: we deliberately do NOT strip env.MEKO_MCP_URL / env.MEKO_API_KEY here.
341
378
  // On scope=user, this file is the same ~/.claude/settings.json the Claude Code
342
379
  // adapter owns; the Claude Code uninstall (claude-code.mjs removeKeys call) is
package/lib/config.mjs CHANGED
@@ -117,6 +117,32 @@ export function getCodexAgentsMdPath() {
117
117
  return join(codexHome, "AGENTS.md");
118
118
  }
119
119
 
120
+ /**
121
+ * Return path to Codex's user-level hooks config.
122
+ * @returns {string}
123
+ */
124
+ export function getCodexHooksPath() {
125
+ const codexHome = process.env.CODEX_HOME || join(homedir(), ".codex");
126
+ return join(codexHome, "hooks.json");
127
+ }
128
+
129
+ /**
130
+ * Return path to the Meko env file the Codex hooks source on launch.
131
+ * Lives next to hooks.json under $CODEX_HOME (default ~/.codex/) and is
132
+ * written at mode 0600. Used because Codex's hooks.json schema has no
133
+ * `env` block, and inlining the API key into each hook's `command`
134
+ * field would expose it via `ps -ef` while the hook runs.
135
+ *
136
+ * The hook command becomes `. ~/.codex/meko-env.sh && bash /path/to/hook.sh`,
137
+ * so the secret never appears in process arguments.
138
+ *
139
+ * @returns {string}
140
+ */
141
+ export function getCodexMekoEnvFilePath() {
142
+ const codexHome = process.env.CODEX_HOME || join(homedir(), ".codex");
143
+ return join(codexHome, "meko-env.sh");
144
+ }
145
+
120
146
  /**
121
147
  * Return path to the shared agent-skills directory used by Codex
122
148
  * (and other agent runtimes that follow the convention).
package/lib/installer.mjs CHANGED
@@ -37,7 +37,7 @@ export async function install(options) {
37
37
  verbose = false,
38
38
  yes = false,
39
39
  projectRoot = process.cwd(),
40
- cursorHookMode = "hybrid",
40
+ cursorHookMode = "native",
41
41
  } = options;
42
42
 
43
43
  const adapters = getClientAdapters();
@@ -225,7 +225,7 @@ export async function uninstall(options = {}) {
225
225
  scope = "user",
226
226
  verbose = false,
227
227
  projectRoot = process.cwd(),
228
- cursorHookMode = "hybrid",
228
+ cursorHookMode = "native",
229
229
  } = options;
230
230
 
231
231
  const adapters = getClientAdapters();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yugabytedb/meko-mcp",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Single-line installer for Meko MCP Server across Claude and Cursor clients",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,10 +18,18 @@ const PAYLOAD = [
18
18
  from: join(repoRoot, "skills", "skills", "meko-mcp-tools"),
19
19
  to: join(assetsDir, "skills", "meko-mcp-tools"),
20
20
  },
21
+ {
22
+ from: join(repoRoot, "skills", "skills", "meko-mcp-tools-desktop"),
23
+ to: join(assetsDir, "skills", "meko-mcp-tools-desktop"),
24
+ },
21
25
  {
22
26
  from: join(repoRoot, "skills", "hooks-handlers"),
23
27
  to: join(assetsDir, "hooks-handlers"),
24
28
  },
29
+ {
30
+ from: join(repoRoot, "skills", "scripts", "package-desktop-skill.sh"),
31
+ to: join(assetsDir, "scripts", "package-desktop-skill.sh"),
32
+ },
25
33
  ];
26
34
 
27
35
  const cleaning = process.argv.includes("--clean");