@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.
- package/README.md +68 -19
- package/assets/hooks-handlers/before-submit-prompt.sh +44 -0
- package/assets/hooks-handlers/cursor-session-start.sh +42 -0
- package/assets/hooks-handlers/lib/capture.js +291 -40
- package/assets/scripts/package-desktop-skill.sh +36 -0
- package/assets/skills/meko-mcp-tools/references/tools-overview.md +1 -1
- package/assets/skills/meko-mcp-tools-desktop/SKILL.md +251 -0
- package/assets/skills/meko-mcp-tools-desktop/desktop/README.md +150 -0
- package/assets/skills/meko-mcp-tools-desktop/references/_sections.md +24 -0
- package/assets/skills/meko-mcp-tools-desktop/references/tools-agent-id-conventions.md +99 -0
- package/assets/skills/meko-mcp-tools-desktop/references/tools-cookbook.md +406 -0
- package/assets/skills/meko-mcp-tools-desktop/references/tools-known-limitations.md +69 -0
- package/assets/skills/meko-mcp-tools-desktop/references/tools-memory-vs-conversation.md +217 -0
- package/assets/skills/meko-mcp-tools-desktop/references/tools-overview.md +112 -0
- package/assets/skills/meko-mcp-tools-desktop/references/tools-rag-workflow.md +134 -0
- package/assets/skills/meko-mcp-tools-desktop/references/tools-scope-permissions.md +75 -0
- package/assets/skills/meko-mcp-tools-desktop/references/tools-troubleshooting.md +155 -0
- package/bin/create-meko-setup.mjs +4 -4
- package/lib/clients/claude-desktop.mjs +11 -8
- package/lib/clients/codex.mjs +236 -4
- package/lib/clients/common.mjs +81 -0
- package/lib/clients/cursor.mjs +47 -10
- package/lib/config.mjs +26 -0
- package/lib/installer.mjs +2 -2
- package/package.json +1 -1
- package/scripts/bundle-assets.mjs +8 -0
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
const fs = require("fs");
|
|
24
|
+
const os = require("os");
|
|
24
25
|
const path = require("path");
|
|
25
26
|
const http = require("http");
|
|
26
27
|
const https = require("https");
|
|
@@ -71,20 +72,46 @@ function deriveAgentId(opts) {
|
|
|
71
72
|
* Resolution order:
|
|
72
73
|
* 1. MEKO_AGENT_ID env override (verbatim).
|
|
73
74
|
* 2. cwd from the transcript header (peekTranscriptMetadata).
|
|
74
|
-
* 3.
|
|
75
|
+
* 3. explicit workspace/project roots from the hook input.
|
|
76
|
+
* 4. cwd from the hook input.
|
|
77
|
+
* 5. process.cwd() — last resort if no transcript metadata yet.
|
|
75
78
|
*/
|
|
76
|
-
function resolveSessionAgentId(transcriptPath) {
|
|
79
|
+
function resolveSessionAgentId(transcriptPath, hookInput = null) {
|
|
77
80
|
const envOverride = process.env.MEKO_AGENT_ID;
|
|
78
81
|
if (envOverride && envOverride.trim()) return envOverride.trim();
|
|
79
82
|
const meta = transcriptPath ? peekTranscriptMetadata(transcriptPath) : null;
|
|
80
|
-
const
|
|
81
|
-
|
|
83
|
+
const nested = hookInput && hookInput.hookSpecificInput && typeof hookInput.hookSpecificInput === "object"
|
|
84
|
+
? hookInput.hookSpecificInput
|
|
85
|
+
: {};
|
|
86
|
+
const roots = hookInput && Array.isArray(hookInput.workspace_roots)
|
|
87
|
+
? hookInput.workspace_roots
|
|
88
|
+
: [];
|
|
89
|
+
const nestedRoots = Array.isArray(nested.workspace_roots) ? nested.workspace_roots : [];
|
|
90
|
+
const workspaceRoot = [...roots, ...nestedRoots]
|
|
91
|
+
.find((item) => typeof item === "string" && item.trim());
|
|
92
|
+
const cwd =
|
|
93
|
+
(meta && meta.cwd) ||
|
|
94
|
+
hookInput?.workspace_path ||
|
|
95
|
+
hookInput?.workspacePath ||
|
|
96
|
+
hookInput?.project_root ||
|
|
97
|
+
nested.workspace_path ||
|
|
98
|
+
nested.workspacePath ||
|
|
99
|
+
nested.project_root ||
|
|
100
|
+
workspaceRoot ||
|
|
101
|
+
hookInput?.cwd ||
|
|
102
|
+
nested.cwd ||
|
|
103
|
+
process.cwd();
|
|
104
|
+
const client = process.env.MEKO_HOOK_CLIENT === "cursor" ? "cursor" : "claude_code";
|
|
105
|
+
return deriveAgentId({ client, cwd, envOverride });
|
|
82
106
|
}
|
|
83
107
|
const MEKO_API_KEY = process.env.MEKO_API_KEY || "";
|
|
84
108
|
const MEKO_API_TIMEOUT = parseInt(process.env.MEKO_API_TIMEOUT || "10", 10) * 1000;
|
|
85
109
|
const WATERMARK_DIR =
|
|
86
110
|
process.env.MEKO_WATERMARK_DIR ||
|
|
87
|
-
path.join(
|
|
111
|
+
path.join(os.homedir(), ".claude", "meko-capture");
|
|
112
|
+
const SESSION_CACHE_DIR =
|
|
113
|
+
process.env.MEKO_SESSION_CACHE_DIR ||
|
|
114
|
+
path.join(os.homedir(), ".cursor", "meko-session-cache");
|
|
88
115
|
|
|
89
116
|
let mcpRequestId = 0;
|
|
90
117
|
let mcpSessionId = null; // MCP Streamable HTTP session ID
|
|
@@ -367,7 +394,7 @@ function mcpPost(jsonBody) {
|
|
|
367
394
|
return;
|
|
368
395
|
}
|
|
369
396
|
try {
|
|
370
|
-
const parsed =
|
|
397
|
+
const parsed = parseMcpResponseBody(data);
|
|
371
398
|
if (parsed.error) {
|
|
372
399
|
reject(new Error(JSON.stringify(parsed.error)));
|
|
373
400
|
} else {
|
|
@@ -395,6 +422,37 @@ function mcpPost(jsonBody) {
|
|
|
395
422
|
});
|
|
396
423
|
}
|
|
397
424
|
|
|
425
|
+
function parseMcpResponseBody(data) {
|
|
426
|
+
const trimmed = data.trim();
|
|
427
|
+
if (!trimmed) return null;
|
|
428
|
+
|
|
429
|
+
if (!trimmed.startsWith("event:") && !trimmed.startsWith("data:")) {
|
|
430
|
+
return JSON.parse(trimmed);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const payloads = [];
|
|
434
|
+
let current = [];
|
|
435
|
+
for (const line of data.split(/\r?\n/)) {
|
|
436
|
+
if (line === "") {
|
|
437
|
+
if (current.length) {
|
|
438
|
+
payloads.push(current.join("\n"));
|
|
439
|
+
current = [];
|
|
440
|
+
}
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
if (line.startsWith("data:")) {
|
|
444
|
+
current.push(line.startsWith("data: ") ? line.slice(6) : line.slice(5));
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
if (current.length) payloads.push(current.join("\n"));
|
|
448
|
+
|
|
449
|
+
const payload = payloads.find((item) => item.trim()) || "";
|
|
450
|
+
if (!payload) {
|
|
451
|
+
throw new Error("SSE response did not include a data payload");
|
|
452
|
+
}
|
|
453
|
+
return JSON.parse(payload);
|
|
454
|
+
}
|
|
455
|
+
|
|
398
456
|
/**
|
|
399
457
|
* Abort all pending MCP requests. Used by the preload timeout path so the
|
|
400
458
|
* SessionStart hook can return promptly instead of waiting for a slow server
|
|
@@ -580,12 +638,112 @@ function hookEventName(hookType) {
|
|
|
580
638
|
}
|
|
581
639
|
|
|
582
640
|
function hookOutput(additionalContext) {
|
|
583
|
-
|
|
641
|
+
// Emit the native coding-agent harness field while preserving the nested
|
|
642
|
+
// Claude-compatible shape for clients that still consume it.
|
|
643
|
+
const output = {
|
|
584
644
|
hookSpecificOutput: {
|
|
585
645
|
hookEventName: hookEventName(process.argv[2]),
|
|
586
646
|
additionalContext: additionalContext || "",
|
|
587
647
|
},
|
|
588
|
-
}
|
|
648
|
+
};
|
|
649
|
+
if (process.argv[2] === "session-start") {
|
|
650
|
+
output.additional_context = additionalContext || "";
|
|
651
|
+
}
|
|
652
|
+
return JSON.stringify(output);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function nativeOutput(additionalContext) {
|
|
656
|
+
return JSON.stringify({ additional_context: additionalContext || "" });
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
function beforeSubmitOutput(shouldContinue, userMessage = "") {
|
|
660
|
+
const output = { continue: Boolean(shouldContinue) };
|
|
661
|
+
if (userMessage) output.user_message = userMessage;
|
|
662
|
+
return JSON.stringify(output);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function extractSessionId(hookInput, transcriptPath) {
|
|
666
|
+
if (transcriptPath) return path.basename(transcriptPath, ".jsonl");
|
|
667
|
+
const nested = hookInput && hookInput.hookSpecificInput && typeof hookInput.hookSpecificInput === "object"
|
|
668
|
+
? hookInput.hookSpecificInput
|
|
669
|
+
: {};
|
|
670
|
+
for (const value of [
|
|
671
|
+
hookInput && hookInput.session_id,
|
|
672
|
+
hookInput && hookInput.sessionId,
|
|
673
|
+
nested.session_id,
|
|
674
|
+
nested.sessionId,
|
|
675
|
+
]) {
|
|
676
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
677
|
+
}
|
|
678
|
+
return null;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
function sessionCachePath(sessionId) {
|
|
682
|
+
if (!sessionId) return null;
|
|
683
|
+
const safe = sessionId.replace(/[^A-Za-z0-9_.-]/g, "_");
|
|
684
|
+
return path.join(SESSION_CACHE_DIR, `${safe}.json`);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function writeSessionCache(sessionId, payload) {
|
|
688
|
+
const filePath = sessionCachePath(sessionId);
|
|
689
|
+
if (!filePath) return;
|
|
690
|
+
const workspaceKey = payload.workspace_key || "";
|
|
691
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
692
|
+
const record = {
|
|
693
|
+
session_id: sessionId,
|
|
694
|
+
updated_at: new Date().toISOString(),
|
|
695
|
+
...payload,
|
|
696
|
+
};
|
|
697
|
+
fs.writeFileSync(filePath, JSON.stringify(record));
|
|
698
|
+
if (workspaceKey) {
|
|
699
|
+
const safe = workspaceKey.replace(/[^A-Za-z0-9_.-]/g, "_");
|
|
700
|
+
fs.writeFileSync(path.join(SESSION_CACHE_DIR, `workspace-${safe}.json`), JSON.stringify(record));
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
function readSessionCache(sessionId) {
|
|
705
|
+
const filePath = sessionCachePath(sessionId);
|
|
706
|
+
if (!filePath) return null;
|
|
707
|
+
try {
|
|
708
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
709
|
+
} catch {
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function workspaceKeyFromHookInput(hookInput, transcriptPath) {
|
|
715
|
+
const roots = hookInput && Array.isArray(hookInput.workspace_roots)
|
|
716
|
+
? hookInput.workspace_roots
|
|
717
|
+
: [];
|
|
718
|
+
const firstRoot = roots.find((item) => typeof item === "string" && item.trim());
|
|
719
|
+
if (firstRoot) return firstRoot.trim();
|
|
720
|
+
if (transcriptPath) return path.dirname(transcriptPath);
|
|
721
|
+
const cwd = hookInput && typeof hookInput.cwd === "string" && hookInput.cwd.trim()
|
|
722
|
+
? hookInput.cwd.trim()
|
|
723
|
+
: "";
|
|
724
|
+
return cwd || process.cwd();
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function readWorkspaceSessionCache(workspaceKey) {
|
|
728
|
+
if (!workspaceKey) return null;
|
|
729
|
+
const safe = workspaceKey.replace(/[^A-Za-z0-9_.-]/g, "_");
|
|
730
|
+
try {
|
|
731
|
+
return JSON.parse(fs.readFileSync(path.join(SESSION_CACHE_DIR, `workspace-${safe}.json`), "utf-8"));
|
|
732
|
+
} catch {
|
|
733
|
+
return null;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
function selectSessionCache(sessionId, workspaceKey) {
|
|
738
|
+
const exactCache = sessionId ? readSessionCache(sessionId) : null;
|
|
739
|
+
const workspaceCache = readWorkspaceSessionCache(workspaceKey);
|
|
740
|
+
const cache =
|
|
741
|
+
exactCache && exactCache.status === "ready"
|
|
742
|
+
? exactCache
|
|
743
|
+
: workspaceCache && workspaceCache.status === "ready"
|
|
744
|
+
? workspaceCache
|
|
745
|
+
: exactCache || workspaceCache;
|
|
746
|
+
return cache;
|
|
589
747
|
}
|
|
590
748
|
|
|
591
749
|
// --- Main ---
|
|
@@ -688,6 +846,21 @@ The SessionStart hook could not reach the Meko MCP server to create a conversati
|
|
|
688
846
|
When Meko MCP tools are available, ALWAYS use them instead of local file-based memory. If a Meko tool call fails, fall back to local storage and tell the user.`;
|
|
689
847
|
}
|
|
690
848
|
|
|
849
|
+
function buildBootstrapPendingContext(agentId) {
|
|
850
|
+
const resolvedAgentId = agentId || COMMON_BUCKET_AGENT_ID;
|
|
851
|
+
return `## Meko Bootstrap Pending
|
|
852
|
+
|
|
853
|
+
The Cursor SessionStart hook has started Meko bootstrap work in the background, but the local Meko session cache is not ready yet. Do not guess a Meko conversation_id.
|
|
854
|
+
|
|
855
|
+
### What you should do
|
|
856
|
+
|
|
857
|
+
- **agent_id**: use "${resolvedAgentId}" for project-scoped reads/writes if a Meko MCP call is necessary before bootstrap completes
|
|
858
|
+
- **conversation_id**: use nil UUID "00000000-0000-0000-0000-000000000000" only as a placeholder until the cache provides the real conversation_id
|
|
859
|
+
- **scope**: Only "read", "write", or "admin"
|
|
860
|
+
|
|
861
|
+
If the user explicitly needs Meko-backed continuity before proceeding, ask them to retry the prompt after bootstrap finishes.`;
|
|
862
|
+
}
|
|
863
|
+
|
|
691
864
|
/**
|
|
692
865
|
* Race a promise against a timeout; on timeout resolve to the fallback value.
|
|
693
866
|
* Memory preload is best-effort — if it stalls, the SessionStart hook must
|
|
@@ -727,17 +900,51 @@ function withTimeout(promise, ms, fallback, { onTimeout } = {}) {
|
|
|
727
900
|
});
|
|
728
901
|
}
|
|
729
902
|
|
|
903
|
+
function handleSessionPending(hookInput) {
|
|
904
|
+
const transcriptPath =
|
|
905
|
+
hookInput.transcript_path ||
|
|
906
|
+
(hookInput.hookSpecificInput || {}).transcript_path;
|
|
907
|
+
const sessionId = extractSessionId(hookInput, transcriptPath);
|
|
908
|
+
const agentId = resolveSessionAgentId(transcriptPath, hookInput);
|
|
909
|
+
const workspaceKey = workspaceKeyFromHookInput(hookInput, transcriptPath);
|
|
910
|
+
if (sessionId) {
|
|
911
|
+
writeSessionCache(sessionId, {
|
|
912
|
+
status: "pending",
|
|
913
|
+
agent_id: agentId,
|
|
914
|
+
workspace_key: workspaceKey,
|
|
915
|
+
additional_context: buildBootstrapPendingContext(agentId),
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
process.stdout.write(nativeOutput(""));
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
function handleBeforeSubmitPrompt(hookInput) {
|
|
922
|
+
const transcriptPath =
|
|
923
|
+
hookInput.transcript_path ||
|
|
924
|
+
(hookInput.hookSpecificInput || {}).transcript_path;
|
|
925
|
+
const sessionId = extractSessionId(hookInput, transcriptPath);
|
|
926
|
+
const workspaceKey = workspaceKeyFromHookInput(hookInput, transcriptPath);
|
|
927
|
+
const cache = selectSessionCache(sessionId, workspaceKey);
|
|
928
|
+
if (cache && cache.status === "pending") {
|
|
929
|
+
process.stdout.write(beforeSubmitOutput(
|
|
930
|
+
false,
|
|
931
|
+
"Meko is still creating this session's conversation. Please retry in a moment so the agent starts with Meko context.",
|
|
932
|
+
));
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
process.stdout.write(beforeSubmitOutput(true));
|
|
936
|
+
}
|
|
937
|
+
|
|
730
938
|
async function handleSessionStart(hookInput) {
|
|
731
939
|
const transcriptPath =
|
|
732
940
|
hookInput.transcript_path ||
|
|
733
941
|
(hookInput.hookSpecificInput || {}).transcript_path;
|
|
734
942
|
|
|
735
|
-
const sessionId = transcriptPath
|
|
736
|
-
? path.basename(transcriptPath, ".jsonl")
|
|
737
|
-
: null;
|
|
943
|
+
const sessionId = extractSessionId(hookInput, transcriptPath);
|
|
738
944
|
|
|
739
945
|
const source = hookInput.source || "";
|
|
740
|
-
const derivedAgentId = resolveSessionAgentId(transcriptPath);
|
|
946
|
+
const derivedAgentId = resolveSessionAgentId(transcriptPath, hookInput);
|
|
947
|
+
const workspaceKey = workspaceKeyFromHookInput(hookInput, transcriptPath);
|
|
741
948
|
|
|
742
949
|
// Check if watermark already exists (same session ID re-fires SessionStart, e.g. /clear, /compact)
|
|
743
950
|
if (sessionId) {
|
|
@@ -753,16 +960,20 @@ async function handleSessionStart(hookInput) {
|
|
|
753
960
|
const cached = (existing.agent_id || "").trim();
|
|
754
961
|
const resumedAgentId = cached && cached !== "agent" ? cached : derivedAgentId;
|
|
755
962
|
// Skip preload on resume (we don't know what's already in context).
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
resumedAgentId,
|
|
762
|
-
null,
|
|
763
|
-
),
|
|
764
|
-
),
|
|
963
|
+
const context = buildSessionStartContext(
|
|
964
|
+
existing.conversation_id,
|
|
965
|
+
sessionId,
|
|
966
|
+
resumedAgentId,
|
|
967
|
+
null,
|
|
765
968
|
);
|
|
969
|
+
writeSessionCache(sessionId, {
|
|
970
|
+
status: "ready",
|
|
971
|
+
conversation_id: existing.conversation_id,
|
|
972
|
+
agent_id: resumedAgentId,
|
|
973
|
+
workspace_key: workspaceKey,
|
|
974
|
+
additional_context: context,
|
|
975
|
+
});
|
|
976
|
+
process.stdout.write(hookOutput(context));
|
|
766
977
|
return;
|
|
767
978
|
}
|
|
768
979
|
}
|
|
@@ -788,17 +999,21 @@ async function handleSessionStart(hookInput) {
|
|
|
788
999
|
priorAgentId && priorAgentId !== "agent" ? priorAgentId : agentId;
|
|
789
1000
|
writeWatermark(wmPath, prior.conversation_id, currentLineCount, resumedAgentId);
|
|
790
1001
|
process.stderr.write(`[meko-capture] Session ${sessionId}: resuming conversation ${prior.conversation_id} from ${prior.sessionId}, skipping ${currentLineCount} copied lines\n`);
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
null,
|
|
798
|
-
{ resumed: true },
|
|
799
|
-
),
|
|
800
|
-
),
|
|
1002
|
+
const context = buildSessionStartContext(
|
|
1003
|
+
prior.conversation_id,
|
|
1004
|
+
sessionId,
|
|
1005
|
+
resumedAgentId,
|
|
1006
|
+
null,
|
|
1007
|
+
{ resumed: true },
|
|
801
1008
|
);
|
|
1009
|
+
writeSessionCache(sessionId, {
|
|
1010
|
+
status: "ready",
|
|
1011
|
+
conversation_id: prior.conversation_id,
|
|
1012
|
+
agent_id: resumedAgentId,
|
|
1013
|
+
workspace_key: workspaceKey,
|
|
1014
|
+
additional_context: context,
|
|
1015
|
+
});
|
|
1016
|
+
process.stdout.write(hookOutput(context));
|
|
802
1017
|
return;
|
|
803
1018
|
}
|
|
804
1019
|
process.stderr.write(`[meko-capture] Session ${sessionId}: source=resume but no prior project watermark found; creating new conversation\n`);
|
|
@@ -813,13 +1028,33 @@ async function handleSessionStart(hookInput) {
|
|
|
813
1028
|
convId = await createConversation(sessionId || "unknown", agentId, transcriptMetadata);
|
|
814
1029
|
} catch (err) {
|
|
815
1030
|
process.stderr.write(`[meko-capture] SessionStart: MCP unavailable (${err.message}). Falling back to agent-driven setup.\n`);
|
|
816
|
-
|
|
1031
|
+
const context = buildSessionStartFallbackContext(agentId);
|
|
1032
|
+
if (sessionId) {
|
|
1033
|
+
writeSessionCache(sessionId, {
|
|
1034
|
+
status: "error",
|
|
1035
|
+
agent_id: agentId,
|
|
1036
|
+
workspace_key: workspaceKey,
|
|
1037
|
+
error: err && err.message ? String(err.message).slice(0, 240) : "MCP unavailable",
|
|
1038
|
+
additional_context: context,
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
process.stdout.write(hookOutput(context));
|
|
817
1042
|
return;
|
|
818
1043
|
}
|
|
819
1044
|
|
|
820
1045
|
if (!convId) {
|
|
821
1046
|
process.stderr.write("[meko-capture] SessionStart: conversation_create returned no ID.\n");
|
|
822
|
-
|
|
1047
|
+
const context = buildSessionStartFallbackContext(agentId);
|
|
1048
|
+
if (sessionId) {
|
|
1049
|
+
writeSessionCache(sessionId, {
|
|
1050
|
+
status: "error",
|
|
1051
|
+
agent_id: agentId,
|
|
1052
|
+
workspace_key: workspaceKey,
|
|
1053
|
+
error: "conversation_create returned no ID",
|
|
1054
|
+
additional_context: context,
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
process.stdout.write(hookOutput(context));
|
|
823
1058
|
return;
|
|
824
1059
|
}
|
|
825
1060
|
|
|
@@ -845,19 +1080,25 @@ async function handleSessionStart(hookInput) {
|
|
|
845
1080
|
);
|
|
846
1081
|
}
|
|
847
1082
|
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
1083
|
+
const context = buildSessionStartContext(convId, sessionId, agentId, memories);
|
|
1084
|
+
if (sessionId) {
|
|
1085
|
+
writeSessionCache(sessionId, {
|
|
1086
|
+
status: "ready",
|
|
1087
|
+
conversation_id: convId,
|
|
1088
|
+
agent_id: agentId,
|
|
1089
|
+
workspace_key: workspaceKey,
|
|
1090
|
+
additional_context: context,
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
process.stdout.write(hookOutput(context));
|
|
853
1094
|
}
|
|
854
1095
|
|
|
855
1096
|
// --- Main ---
|
|
856
1097
|
|
|
857
1098
|
async function main() {
|
|
858
1099
|
const hookType = process.argv[2];
|
|
859
|
-
if (!hookType || !["session-start", "pre-compact", "session-end", "checkpoint"].includes(hookType)) {
|
|
860
|
-
process.stderr.write("Usage: capture.js <session-start|pre-compact|session-end|checkpoint>\n");
|
|
1100
|
+
if (!hookType || !["session-start", "session-pending", "before-submit-prompt", "pre-compact", "session-end", "checkpoint"].includes(hookType)) {
|
|
1101
|
+
process.stderr.write("Usage: capture.js <session-start|session-pending|before-submit-prompt|pre-compact|session-end|checkpoint>\n");
|
|
861
1102
|
process.exit(1);
|
|
862
1103
|
}
|
|
863
1104
|
|
|
@@ -878,6 +1119,16 @@ async function main() {
|
|
|
878
1119
|
return;
|
|
879
1120
|
}
|
|
880
1121
|
|
|
1122
|
+
if (hookType === "session-pending") {
|
|
1123
|
+
handleSessionPending(hookInput);
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
if (hookType === "before-submit-prompt") {
|
|
1128
|
+
handleBeforeSubmitPrompt(hookInput);
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
881
1132
|
// PreCompact / SessionEnd: capture transcript exchanges
|
|
882
1133
|
const transcriptPath =
|
|
883
1134
|
hookInput.transcript_path ||
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Package meko-mcp-tools-desktop as a .skill file for Claude Desktop distribution.
|
|
3
|
+
# Usage: bash scripts/package-desktop-skill.sh
|
|
4
|
+
# Output: meko-mcp-tools-desktop.skill (ZIP archive)
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
SKILLS_ROOT="$SCRIPT_DIR/../skills"
|
|
10
|
+
SKILL_NAME="meko-mcp-tools-desktop"
|
|
11
|
+
OUTPUT="$SCRIPT_DIR/../$SKILL_NAME.skill"
|
|
12
|
+
|
|
13
|
+
if [ ! -d "$SKILLS_ROOT/$SKILL_NAME" ]; then
|
|
14
|
+
echo "Error: Skill directory not found at $SKILLS_ROOT/$SKILL_NAME" >&2
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
if [ ! -f "$SKILLS_ROOT/$SKILL_NAME/SKILL.md" ]; then
|
|
19
|
+
echo "Error: SKILL.md not found in $SKILLS_ROOT/$SKILL_NAME/" >&2
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# Remove previous build if it exists
|
|
24
|
+
rm -f "$OUTPUT"
|
|
25
|
+
|
|
26
|
+
# Package skill + references (exclude OS artifacts and desktop README)
|
|
27
|
+
cd "$SKILLS_ROOT"
|
|
28
|
+
zip -r "$OUTPUT" \
|
|
29
|
+
"$SKILL_NAME/SKILL.md" \
|
|
30
|
+
"$SKILL_NAME/references/" \
|
|
31
|
+
-x "*.DS_Store" "*__pycache__*"
|
|
32
|
+
|
|
33
|
+
echo "Created $OUTPUT ($(du -h "$OUTPUT" | cut -f1))"
|
|
34
|
+
echo ""
|
|
35
|
+
echo "Contents:"
|
|
36
|
+
unzip -l "$OUTPUT" | grep -E "^\s+[0-9]" | grep -v "files$"
|
|
@@ -15,7 +15,7 @@ Before starting a session, verify that the Meko MCP tools are working:
|
|
|
15
15
|
|
|
16
16
|
1. `memory_search(scope="read", query="test", agent_id="<your-session-agent-id>", conversation_id="<session-conversation-id>")` — Confirms memory subsystem is up. Use the `agent_id` and `conversation_id` from the SessionStart `additionalContext`. May fail with "connection already closed" — see `tools-troubleshooting.md`.
|
|
17
17
|
2. `datapack_list(scope="read")` — Confirms Meko API connectivity
|
|
18
|
-
3. `db_summarize_database(scope="read")` — *Optional, paid-tier only.* On free-tier datapacks this returns `{"error": "not_available"}`. The old "use this for `agent_id` discovery" protocol is retired — `agent_id`
|
|
18
|
+
3. `db_summarize_database(scope="read")` — *Optional, paid-tier only.* On free-tier datapacks this returns `{"error": "not_available"}`. The old "use this for `agent_id` discovery" protocol is retired — use the session-injected `agent_id` for project-scoped calls, or `meko_agent` for the common bucket.
|
|
19
19
|
|
|
20
20
|
## Decision tree: which tool do I need?
|
|
21
21
|
|