@gramatr/mcp 0.10.14 → 0.10.17
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/dist/__mocks__/bun-sqlite.d.ts +9 -10
- package/dist/__mocks__/bun-sqlite.d.ts.map +1 -1
- package/dist/__mocks__/bun-sqlite.js +29 -15
- package/dist/__mocks__/bun-sqlite.js.map +1 -1
- package/dist/bin/gramatr-mcp.d.ts.map +1 -1
- package/dist/bin/gramatr-mcp.js +17 -0
- package/dist/bin/gramatr-mcp.js.map +1 -1
- package/dist/bin/login.js +1 -1
- package/dist/bin/login.js.map +1 -1
- package/dist/bin/setup-config-io.d.ts +5 -0
- package/dist/bin/setup-config-io.d.ts.map +1 -1
- package/dist/bin/setup-config-io.js +53 -0
- package/dist/bin/setup-config-io.js.map +1 -1
- package/dist/bin/setup-legacy.d.ts.map +1 -1
- package/dist/bin/setup-legacy.js +6 -18
- package/dist/bin/setup-legacy.js.map +1 -1
- package/dist/bin/setup.d.ts +6 -6
- package/dist/bin/setup.d.ts.map +1 -1
- package/dist/bin/setup.js +184 -194
- package/dist/bin/setup.js.map +1 -1
- package/dist/hooks/agent-gate.js +1 -1
- package/dist/hooks/agent-gate.js.map +1 -1
- package/dist/hooks/agent-verify.d.ts +1 -1
- package/dist/hooks/agent-verify.js +3 -3
- package/dist/hooks/agent-verify.js.map +1 -1
- package/dist/hooks/generated/schema-constants.d.ts +2 -1
- package/dist/hooks/generated/schema-constants.d.ts.map +1 -1
- package/dist/hooks/generated/schema-constants.js +3 -1
- package/dist/hooks/generated/schema-constants.js.map +1 -1
- package/dist/hooks/lib/formatting-compat.js +1 -1
- package/dist/hooks/lib/formatting-compat.js.map +1 -1
- package/dist/hooks/lib/hook-state.d.ts +2 -0
- package/dist/hooks/lib/hook-state.d.ts.map +1 -1
- package/dist/hooks/lib/hook-state.js +18 -7
- package/dist/hooks/lib/hook-state.js.map +1 -1
- package/dist/hooks/lib/intelligence.js +1 -1
- package/dist/hooks/lib/intelligence.js.map +1 -1
- package/dist/hooks/manifest.d.ts +1 -1
- package/dist/hooks/manifest.js +1 -1
- package/dist/hooks/manifest.js.map +1 -1
- package/dist/hooks/user-prompt-submit.d.ts.map +1 -1
- package/dist/hooks/user-prompt-submit.js +9 -1
- package/dist/hooks/user-prompt-submit.js.map +1 -1
- package/dist/server/hooks-listener.d.ts.map +1 -1
- package/dist/server/hooks-listener.js +91 -15
- package/dist/server/hooks-listener.js.map +1 -1
- package/dist/setup/generated/instruction-blocks.d.ts +11 -11
- package/dist/setup/generated/instruction-blocks.d.ts.map +1 -1
- package/dist/setup/generated/instruction-blocks.js +11 -11
- package/dist/setup/generated/instruction-blocks.js.map +1 -1
- package/dist/setup/instructions.js +3 -3
- package/dist/setup/integrations.d.ts +1 -0
- package/dist/setup/integrations.d.ts.map +1 -1
- package/dist/setup/integrations.js +7 -0
- package/dist/setup/integrations.js.map +1 -1
- package/package.json +2 -5
- package/scripts/postinstall.mjs +8 -3
package/dist/bin/setup.js
CHANGED
|
@@ -15,32 +15,31 @@
|
|
|
15
15
|
* gramatr-mcp setup claude Configure Claude Code
|
|
16
16
|
* gramatr-mcp setup claude --dry Run without writing
|
|
17
17
|
*/
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import { getChatgptDesktopConfigPath, getClaudeDesktopConfigPath, getCursorConfigPath, getGeminiHooksPath, getGeminiManifestPath, getOpenCodeConfigPath, getVscodeConfigPath, getWindsurfConfigPath, } from '../setup/targets.js';
|
|
18
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
19
|
+
import { dirname, join } from "node:path";
|
|
20
|
+
import { CLAUDE_BLOCK_END, CLAUDE_BLOCK_START, CLAUDE_CODE_GUIDANCE, CODEX_BLOCK_END, CODEX_BLOCK_START, } from "../setup/instructions.js";
|
|
21
|
+
import { buildPluginHooksJson } from "../setup/integrations.js";
|
|
22
|
+
import { getChatgptDesktopConfigPath, getClaudeDesktopConfigPath, getCursorConfigPath, getGeminiHooksPath, getGeminiManifestPath, getOpenCodeConfigPath, getVscodeConfigPath, getWindsurfConfigPath, } from "../setup/targets.js";
|
|
24
23
|
// ── Re-export everything from sub-modules so consumers keep importing from setup.js ──
|
|
25
|
-
export { getClaudeConfigPath, getClaudeSettingsPath,
|
|
26
|
-
export { runCleanInstall
|
|
27
|
-
export {
|
|
28
|
-
export {
|
|
24
|
+
export { addPluginRegistration, ensureLocalSettings, escapeRegExp, getClaudeConfigPath, getClaudeMarkdownPath, getClaudeSettingsPath, getCodexAgentsPath, getCodexConfigPath, getCodexHooksPath, getGramatrPluginDir, getGramatrSettingsPath, hasHookCommand, parseJson, readClaudeConfig, readJsonFile, readManagedBlock, removeGramatrHooks, upsertManagedBlock, writeMarketplaceManifest, writePluginFiles, } from "./setup-config-io.js";
|
|
25
|
+
export { runCleanInstall } from "./setup-legacy.js";
|
|
26
|
+
export { emitInstallPromptSuggestion, ensureCodexMcpServerConfig, setupCodex, setupGemini, setupMcpTarget, setupOpenCode, setupWeb, } from "./setup-platforms.js";
|
|
27
|
+
export { deployPlatformBinary, resolveBinaryPath, } from "./setup-shared.js";
|
|
29
28
|
// ── Local imports from sub-modules ──
|
|
30
|
-
import {
|
|
31
|
-
import { runCleanInstall } from
|
|
32
|
-
import { emitInstallPromptSuggestion, setupCodex,
|
|
33
|
-
import { deployPlatformBinary } from
|
|
29
|
+
import { addPluginRegistration, ensureLocalSettings, getClaudeConfigPath, getClaudeMarkdownPath, getClaudeSettingsPath, getCodexAgentsPath, getCodexConfigPath as getCodexConfigPathFn, getCodexHooksPath as getCodexHooksPathFn, getGramatrPluginDir, getGramatrSettingsPath as getGramatrSettingsPathFn, HOME, hasHookCommand, parseJson, readClaudeConfig, readManagedBlock, removeGramatrHooks, upsertManagedBlock, writeMarketplaceManifest, writePluginFiles, } from "./setup-config-io.js";
|
|
30
|
+
import { runCleanInstall } from "./setup-legacy.js";
|
|
31
|
+
import { emitInstallPromptSuggestion, setupCodex, setupGemini, setupMcpTarget, setupOpenCode, } from "./setup-platforms.js";
|
|
32
|
+
import { deployPlatformBinary } from "./setup-shared.js";
|
|
34
33
|
export const AUTO_TARGET_ORDER = [
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
"claude",
|
|
35
|
+
"codex",
|
|
36
|
+
"opencode",
|
|
37
|
+
"gemini",
|
|
38
|
+
"claude-desktop",
|
|
39
|
+
"chatgpt-desktop",
|
|
40
|
+
"cursor",
|
|
41
|
+
"windsurf",
|
|
42
|
+
"vscode",
|
|
44
43
|
];
|
|
45
44
|
// ── setupClaude — the primary Claude Code setup ──
|
|
46
45
|
export function setupClaude(dryRun = false, cleanInstall = false, showPrompts = false) {
|
|
@@ -48,124 +47,98 @@ export function setupClaude(dryRun = false, cleanInstall = false, showPrompts =
|
|
|
48
47
|
runCleanInstall(dryRun);
|
|
49
48
|
}
|
|
50
49
|
deployPlatformBinary(dryRun);
|
|
51
|
-
const configPath = getClaudeConfigPath();
|
|
52
50
|
const settingsPath = getClaudeSettingsPath();
|
|
53
51
|
const markdownPath = getClaudeMarkdownPath();
|
|
54
|
-
const config = readClaudeConfig(configPath);
|
|
55
52
|
const settings = readClaudeConfig(settingsPath);
|
|
56
|
-
const currentMarkdown = existsSync(markdownPath) ? readFileSync(markdownPath,
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
53
|
+
const currentMarkdown = existsSync(markdownPath) ? readFileSync(markdownPath, "utf8") : "";
|
|
54
|
+
const gramatrDir = join(HOME, ".gramatr");
|
|
55
|
+
const pluginDir = getGramatrPluginDir();
|
|
56
|
+
const pluginJson = {
|
|
57
|
+
name: "gramatr",
|
|
58
|
+
version: "0.10.17",
|
|
59
|
+
description: "gramatr — Real-Time Intelligent Context Engineering. Hooks inject a gmtr.intelligence.contract.v2 packet into every prompt, pre-classifying requests and loading memory so Claude skips routing overhead and starts working immediately.",
|
|
60
|
+
author: { name: "gramatr", email: "support@gramatr.com", url: "https://gramatr.com" },
|
|
61
|
+
homepage: "https://gramatr.com",
|
|
62
|
+
repository: "https://github.com/gramatr/gramatr",
|
|
63
|
+
license: "MIT",
|
|
64
|
+
keywords: ["intelligence", "memory", "routing", "context", "mcp"],
|
|
65
|
+
};
|
|
66
|
+
const mcpJson = { gramatr: { command: "npx", args: ["-y", "@gramatr/mcp"] } };
|
|
67
|
+
// Strip gramatr-owned keys and hooks — plugin owns all of these now
|
|
68
|
+
const { statusLine: _statusLine, daidentity: _daidentity, principal: _principal, attribution: _attribution, alwaysThinkingEnabled: _alwaysThinkingEnabled, ...settingsWithoutGramatrKeys } = removeGramatrHooks(settings);
|
|
69
|
+
const mergedSettings = addPluginRegistration({
|
|
70
|
+
...settingsWithoutGramatrKeys,
|
|
70
71
|
env: {
|
|
71
|
-
...
|
|
72
|
-
GRAMATR_DIR:
|
|
73
|
-
GRAMATR_URL: gramatrUrl,
|
|
72
|
+
...settingsWithoutGramatrKeys.env,
|
|
73
|
+
GRAMATR_DIR: gramatrDir,
|
|
74
74
|
PATH: `${HOME}/.gramatr/bin:/usr/local/bin:/usr/bin:/bin`,
|
|
75
75
|
},
|
|
76
|
-
|
|
77
|
-
type: 'command',
|
|
78
|
-
command: `npx tsx ${join(HOME, '.gramatr', 'bin', 'statusline.ts')}`,
|
|
79
|
-
},
|
|
80
|
-
};
|
|
76
|
+
}, gramatrDir);
|
|
81
77
|
const mergedMarkdown = upsertManagedBlock(currentMarkdown, CLAUDE_CODE_GUIDANCE, CLAUDE_BLOCK_START, CLAUDE_BLOCK_END);
|
|
82
|
-
// Check if already configured
|
|
83
|
-
const existing = config.mcpServers?.['gramatr'];
|
|
84
|
-
if (existing) {
|
|
85
|
-
const existingCmd = existing.command;
|
|
86
|
-
const existingArgs = existing.args || [];
|
|
87
|
-
if (existingCmd === serverEntry.command
|
|
88
|
-
&& JSON.stringify(existingArgs) === JSON.stringify(serverEntry.args || [])) {
|
|
89
|
-
process.stderr.write('[gramatr-mcp] Claude Code MCP server already configured. Refreshing hooks and guidance.\n');
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
process.stderr.write('[gramatr-mcp] Updating existing gramatr MCP server config.\n');
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
// Merge into config
|
|
96
|
-
if (!config.mcpServers) {
|
|
97
|
-
config.mcpServers = {};
|
|
98
|
-
}
|
|
99
|
-
config.mcpServers['gramatr'] = serverEntry;
|
|
100
78
|
if (dryRun) {
|
|
101
|
-
process.stderr.write(
|
|
102
|
-
process.stderr.write(
|
|
103
|
-
process.stderr.write(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
79
|
+
process.stderr.write("[gramatr-mcp] Dry run — would write plugin to: " + pluginDir + "\n");
|
|
80
|
+
process.stderr.write("[gramatr-mcp] Dry run — would write Claude settings to: " + settingsPath + "\n");
|
|
81
|
+
process.stderr.write(JSON.stringify({
|
|
82
|
+
extraKnownMarketplaces: mergedSettings.extraKnownMarketplaces,
|
|
83
|
+
enabledPlugins: mergedSettings.enabledPlugins,
|
|
84
|
+
}, null, 2) + "\n");
|
|
85
|
+
process.stderr.write("[gramatr-mcp] Dry run — would write Claude guidance to: " + markdownPath + "\n");
|
|
86
|
+
process.stderr.write(mergedMarkdown + "\n");
|
|
107
87
|
if (showPrompts)
|
|
108
|
-
emitInstallPromptSuggestion(
|
|
88
|
+
emitInstallPromptSuggestion("claude-code");
|
|
109
89
|
return;
|
|
110
90
|
}
|
|
111
|
-
|
|
112
|
-
mkdirSync(join(HOME, '.claude'), { recursive: true });
|
|
91
|
+
mkdirSync(join(HOME, ".claude"), { recursive: true });
|
|
113
92
|
ensureLocalSettings();
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
writeFileSync(
|
|
117
|
-
|
|
118
|
-
process.stderr.write(`[gramatr-mcp]
|
|
93
|
+
writeMarketplaceManifest(gramatrDir);
|
|
94
|
+
writePluginFiles(pluginDir, pluginJson, buildPluginHooksJson(), mcpJson);
|
|
95
|
+
writeFileSync(settingsPath, JSON.stringify(mergedSettings, null, 2) + "\n", "utf8");
|
|
96
|
+
writeFileSync(markdownPath, mergedMarkdown, "utf8");
|
|
97
|
+
process.stderr.write(`[gramatr-mcp] Wrote plugin files to ${pluginDir}\n`);
|
|
98
|
+
process.stderr.write(`[gramatr-mcp] Registered plugin in ${settingsPath}\n`);
|
|
119
99
|
process.stderr.write(`[gramatr-mcp] Configured Claude guidance in ${markdownPath}\n`);
|
|
120
|
-
process.stderr.write(
|
|
121
|
-
// Show what was written
|
|
122
|
-
process.stderr.write('\n mcpServers.gramatr:\n');
|
|
123
|
-
process.stderr.write(` command: ${serverEntry.command}\n`);
|
|
124
|
-
if ((serverEntry.args || []).length > 0) {
|
|
125
|
-
process.stderr.write(` args: ${JSON.stringify(serverEntry.args)}\n`);
|
|
126
|
-
}
|
|
127
|
-
process.stderr.write('\n');
|
|
100
|
+
process.stderr.write("[gramatr-mcp] Restart Claude Code to pick up the change.\n");
|
|
128
101
|
if (showPrompts)
|
|
129
|
-
emitInstallPromptSuggestion(
|
|
102
|
+
emitInstallPromptSuggestion("claude-code");
|
|
130
103
|
}
|
|
131
104
|
// ── Convenience wrappers for MCP-only targets ──
|
|
132
105
|
export function setupClaudeDesktop(dryRun = false, showPrompts = false) {
|
|
133
|
-
setupMcpTarget(
|
|
106
|
+
setupMcpTarget("Claude Desktop", getClaudeDesktopConfigPath(HOME), dryRun);
|
|
134
107
|
if (showPrompts)
|
|
135
|
-
emitInstallPromptSuggestion(
|
|
108
|
+
emitInstallPromptSuggestion("claude-desktop");
|
|
136
109
|
}
|
|
137
110
|
export function setupChatgptDesktop(dryRun = false, showPrompts = false) {
|
|
138
|
-
setupMcpTarget(
|
|
111
|
+
setupMcpTarget("ChatGPT Desktop", getChatgptDesktopConfigPath(HOME), dryRun);
|
|
139
112
|
if (showPrompts)
|
|
140
|
-
emitInstallPromptSuggestion(
|
|
113
|
+
emitInstallPromptSuggestion("chatgpt-desktop");
|
|
141
114
|
}
|
|
142
115
|
export function setupCursor(dryRun = false, showPrompts = false) {
|
|
143
|
-
setupMcpTarget(
|
|
116
|
+
setupMcpTarget("Cursor", getCursorConfigPath(HOME), dryRun);
|
|
144
117
|
if (showPrompts)
|
|
145
|
-
emitInstallPromptSuggestion(
|
|
118
|
+
emitInstallPromptSuggestion("cursor");
|
|
146
119
|
}
|
|
147
120
|
export function setupWindsurf(dryRun = false, showPrompts = false) {
|
|
148
|
-
setupMcpTarget(
|
|
121
|
+
setupMcpTarget("Windsurf", getWindsurfConfigPath(HOME), dryRun);
|
|
149
122
|
if (showPrompts)
|
|
150
|
-
emitInstallPromptSuggestion(
|
|
123
|
+
emitInstallPromptSuggestion("windsurf");
|
|
151
124
|
}
|
|
152
125
|
export function setupVscode(dryRun = false, showPrompts = false) {
|
|
153
|
-
setupMcpTarget(
|
|
126
|
+
setupMcpTarget("VS Code", getVscodeConfigPath(HOME), dryRun);
|
|
154
127
|
if (showPrompts)
|
|
155
|
-
emitInstallPromptSuggestion(
|
|
128
|
+
emitInstallPromptSuggestion("vscode");
|
|
156
129
|
}
|
|
157
130
|
// ── Auto-detect + auto-install ──
|
|
158
131
|
export function getAutoDetectedTargets() {
|
|
159
132
|
const checks = {
|
|
160
|
-
claude: existsSync(join(HOME,
|
|
161
|
-
codex: existsSync(join(HOME,
|
|
162
|
-
opencode: existsSync(join(HOME,
|
|
163
|
-
gemini: existsSync(join(HOME,
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
cursor: existsSync(join(HOME,
|
|
167
|
-
windsurf: existsSync(join(HOME,
|
|
168
|
-
vscode: existsSync(join(HOME,
|
|
133
|
+
claude: existsSync(join(HOME, ".claude")) || existsSync(getClaudeConfigPath()),
|
|
134
|
+
codex: existsSync(join(HOME, ".codex")),
|
|
135
|
+
opencode: existsSync(join(HOME, ".config", "opencode")) || existsSync("opencode.json"),
|
|
136
|
+
gemini: existsSync(join(HOME, ".gemini")),
|
|
137
|
+
"claude-desktop": existsSync(dirname(getClaudeDesktopConfigPath(HOME))),
|
|
138
|
+
"chatgpt-desktop": existsSync(dirname(getChatgptDesktopConfigPath(HOME))),
|
|
139
|
+
cursor: existsSync(join(HOME, ".cursor")),
|
|
140
|
+
windsurf: existsSync(join(HOME, ".windsurf")),
|
|
141
|
+
vscode: existsSync(join(HOME, ".vscode")),
|
|
169
142
|
};
|
|
170
143
|
return AUTO_TARGET_ORDER.filter((target) => checks[target]);
|
|
171
144
|
}
|
|
@@ -179,22 +152,22 @@ export function setupAutoInstall(options = {}) {
|
|
|
179
152
|
? Array.from(new Set(options.selectedTargets))
|
|
180
153
|
: null;
|
|
181
154
|
const selected = requested ?? detected;
|
|
182
|
-
process.stderr.write(
|
|
155
|
+
process.stderr.write("[gramatr-mcp] auto-detect scan complete\n");
|
|
183
156
|
if (detected.length === 0 && !requested) {
|
|
184
|
-
process.stderr.write(
|
|
185
|
-
process.stderr.write(
|
|
157
|
+
process.stderr.write("[gramatr-mcp] No supported local clients detected.\n");
|
|
158
|
+
process.stderr.write("[gramatr-mcp] Install manually with: setup <target>\n");
|
|
186
159
|
return 0;
|
|
187
160
|
}
|
|
188
|
-
process.stderr.write(`[gramatr-mcp] Detected targets: ${detected.join(
|
|
189
|
-
process.stderr.write(`[gramatr-mcp] Selected targets: ${selected.join(
|
|
161
|
+
process.stderr.write(`[gramatr-mcp] Detected targets: ${detected.join(", ")}\n`);
|
|
162
|
+
process.stderr.write(`[gramatr-mcp] Selected targets: ${selected.join(", ")}\n`);
|
|
190
163
|
if (requested) {
|
|
191
164
|
const undetected = requested.filter((target) => !detected.includes(target));
|
|
192
165
|
if (undetected.length > 0) {
|
|
193
|
-
process.stderr.write(`[gramatr-mcp] Requested targets not detected locally (will still configure): ${undetected.join(
|
|
166
|
+
process.stderr.write(`[gramatr-mcp] Requested targets not detected locally (will still configure): ${undetected.join(", ")}\n`);
|
|
194
167
|
}
|
|
195
168
|
}
|
|
196
169
|
if (listOnly) {
|
|
197
|
-
process.stderr.write(
|
|
170
|
+
process.stderr.write("[gramatr-mcp] list-only mode: no setup changes made.\n");
|
|
198
171
|
return selected.length;
|
|
199
172
|
}
|
|
200
173
|
if (cleanInstall) {
|
|
@@ -202,31 +175,31 @@ export function setupAutoInstall(options = {}) {
|
|
|
202
175
|
}
|
|
203
176
|
for (const target of selected) {
|
|
204
177
|
switch (target) {
|
|
205
|
-
case
|
|
178
|
+
case "claude":
|
|
206
179
|
setupClaude(dryRun, false, showPrompts);
|
|
207
180
|
break;
|
|
208
|
-
case
|
|
181
|
+
case "codex":
|
|
209
182
|
setupCodex(dryRun, showPrompts);
|
|
210
183
|
break;
|
|
211
|
-
case
|
|
184
|
+
case "opencode":
|
|
212
185
|
setupOpenCode(dryRun, showPrompts);
|
|
213
186
|
break;
|
|
214
|
-
case
|
|
187
|
+
case "gemini":
|
|
215
188
|
setupGemini(dryRun, showPrompts);
|
|
216
189
|
break;
|
|
217
|
-
case
|
|
190
|
+
case "claude-desktop":
|
|
218
191
|
setupClaudeDesktop(dryRun, showPrompts);
|
|
219
192
|
break;
|
|
220
|
-
case
|
|
193
|
+
case "chatgpt-desktop":
|
|
221
194
|
setupChatgptDesktop(dryRun, showPrompts);
|
|
222
195
|
break;
|
|
223
|
-
case
|
|
196
|
+
case "cursor":
|
|
224
197
|
setupCursor(dryRun, showPrompts);
|
|
225
198
|
break;
|
|
226
|
-
case
|
|
199
|
+
case "windsurf":
|
|
227
200
|
setupWindsurf(dryRun, showPrompts);
|
|
228
201
|
break;
|
|
229
|
-
case
|
|
202
|
+
case "vscode":
|
|
230
203
|
setupVscode(dryRun, showPrompts);
|
|
231
204
|
break;
|
|
232
205
|
default:
|
|
@@ -241,34 +214,52 @@ function addResult(items, severity, label, detail) {
|
|
|
241
214
|
items.push({ severity, label, detail });
|
|
242
215
|
}
|
|
243
216
|
function verifyClaude(items) {
|
|
244
|
-
const configPath = getClaudeConfigPath();
|
|
245
217
|
const settingsPath = getClaudeSettingsPath();
|
|
246
218
|
const markdownPath = getClaudeMarkdownPath();
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
addResult(items, 'ok', 'claude.mcp_server', `${configPath} contains mcpServers.gramatr`);
|
|
219
|
+
const settings = parseJson(settingsPath);
|
|
220
|
+
const enabledPlugins = settings?.enabledPlugins;
|
|
221
|
+
const pluginEnabled = enabledPlugins && enabledPlugins["gramatr@gramatr"] === true;
|
|
222
|
+
if (pluginEnabled) {
|
|
223
|
+
addResult(items, "ok", "claude.plugin", `${settingsPath} has enabledPlugins['gramatr@gramatr']`);
|
|
253
224
|
}
|
|
254
225
|
else {
|
|
255
|
-
addResult(items,
|
|
226
|
+
addResult(items, "error", "claude.plugin", `${settingsPath} missing enabledPlugins['gramatr@gramatr'] — run: npx @gramatr/mcp setup claude`);
|
|
256
227
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const
|
|
228
|
+
// Check that the gramatr marketplace is registered — without this, Claude Code
|
|
229
|
+
// silently fails to install gramatr@gramatr even when enabledPlugins is set.
|
|
230
|
+
const extraMarketplaces = settings?.extraKnownMarketplaces;
|
|
231
|
+
const marketplaceRegistered = extraMarketplaces && extraMarketplaces["gramatr"];
|
|
232
|
+
if (marketplaceRegistered) {
|
|
233
|
+
addResult(items, "ok", "claude.marketplace", `${settingsPath} has extraKnownMarketplaces['gramatr']`);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
addResult(items, "error", "claude.marketplace", `${settingsPath} missing extraKnownMarketplaces['gramatr'] — plugin will silently fail to install. Run: npx @gramatr/mcp setup claude`);
|
|
237
|
+
}
|
|
238
|
+
// Check that plugin files exist on disk
|
|
239
|
+
const pluginJsonPath = join(getGramatrPluginDir(), ".claude-plugin", "plugin.json");
|
|
240
|
+
if (existsSync(pluginJsonPath)) {
|
|
241
|
+
addResult(items, "ok", "claude.plugin_files", `Plugin files present at ${getGramatrPluginDir()}`);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
addResult(items, "error", "claude.plugin_files", `Plugin files missing at ${getGramatrPluginDir()} — run: npx @gramatr/mcp setup claude`);
|
|
245
|
+
}
|
|
246
|
+
const pluginDir = getGramatrPluginDir();
|
|
247
|
+
const pluginHooksPath = join(pluginDir, "hooks", "hooks.json");
|
|
248
|
+
const pluginHooks = parseJson(pluginHooksPath);
|
|
249
|
+
const hasPromptHook = hasHookCommand(pluginHooks, "UserPromptSubmit", "hook user-prompt-submit");
|
|
250
|
+
const hasSessionStartHook = hasHookCommand(pluginHooks, "SessionStart", "hook session-start");
|
|
260
251
|
if (hasPromptHook && hasSessionStartHook) {
|
|
261
|
-
addResult(items,
|
|
252
|
+
addResult(items, "ok", "claude.hooks", `${pluginHooksPath} includes session-start + user-prompt-submit`);
|
|
262
253
|
}
|
|
263
254
|
else {
|
|
264
|
-
addResult(items,
|
|
255
|
+
addResult(items, "error", "claude.hooks", `${pluginHooksPath} missing required hooks (session-start=${hasSessionStartHook}, user-prompt-submit=${hasPromptHook})`);
|
|
265
256
|
}
|
|
266
257
|
const managedBlock = readManagedBlock(markdownPath, CLAUDE_BLOCK_START, CLAUDE_BLOCK_END);
|
|
267
258
|
if (managedBlock) {
|
|
268
|
-
addResult(items,
|
|
259
|
+
addResult(items, "ok", "claude.guidance", `${markdownPath} contains managed gramatr guidance block`);
|
|
269
260
|
}
|
|
270
261
|
else {
|
|
271
|
-
addResult(items,
|
|
262
|
+
addResult(items, "warn", "claude.guidance", `${markdownPath} missing managed guidance block`);
|
|
272
263
|
}
|
|
273
264
|
}
|
|
274
265
|
function verifyCodex(items) {
|
|
@@ -276,145 +267,144 @@ function verifyCodex(items) {
|
|
|
276
267
|
const configPath = getCodexConfigPathFn();
|
|
277
268
|
const agentsPath = getCodexAgentsPath();
|
|
278
269
|
const hooks = parseJson(hooksPath);
|
|
279
|
-
const hasPromptHook = hasHookCommand(hooks,
|
|
280
|
-
const hasSessionStartHook = hasHookCommand(hooks,
|
|
281
|
-
const hasStopHook = hasHookCommand(hooks,
|
|
270
|
+
const hasPromptHook = hasHookCommand(hooks, "UserPromptSubmit", "hook user-prompt-submit");
|
|
271
|
+
const hasSessionStartHook = hasHookCommand(hooks, "SessionStart", "hook session-start");
|
|
272
|
+
const hasStopHook = hasHookCommand(hooks, "Stop", "hook stop");
|
|
282
273
|
if (hasPromptHook && hasSessionStartHook && hasStopHook) {
|
|
283
|
-
addResult(items,
|
|
274
|
+
addResult(items, "ok", "codex.hooks", `${hooksPath} includes session-start + user-prompt-submit + stop`);
|
|
284
275
|
}
|
|
285
276
|
else {
|
|
286
|
-
addResult(items,
|
|
277
|
+
addResult(items, "error", "codex.hooks", `${hooksPath} missing required hooks (session-start=${hasSessionStartHook}, user-prompt-submit=${hasPromptHook}, stop=${hasStopHook})`);
|
|
287
278
|
}
|
|
288
|
-
const configToml = existsSync(configPath) ? readFileSync(configPath,
|
|
279
|
+
const configToml = existsSync(configPath) ? readFileSync(configPath, "utf8") : "";
|
|
289
280
|
const hooksEnabled = /^\s*codex_hooks\s*=\s*true\s*$/m.test(configToml);
|
|
290
281
|
if (hooksEnabled) {
|
|
291
|
-
addResult(items,
|
|
282
|
+
addResult(items, "ok", "codex.feature_flag", `${configPath} enables codex_hooks`);
|
|
292
283
|
}
|
|
293
284
|
else {
|
|
294
|
-
addResult(items,
|
|
285
|
+
addResult(items, "error", "codex.feature_flag", `${configPath} missing codex_hooks = true`);
|
|
295
286
|
}
|
|
296
|
-
const hasMcpServer = /^\[mcp_servers\.gramatr\]\s*$/m.test(configToml)
|
|
297
|
-
&& /^\s*command\s*=\s*.+$/m.test(configToml);
|
|
287
|
+
const hasMcpServer = /^\[mcp_servers\.gramatr\]\s*$/m.test(configToml) && /^\s*command\s*=\s*.+$/m.test(configToml);
|
|
298
288
|
if (hasMcpServer) {
|
|
299
|
-
addResult(items,
|
|
289
|
+
addResult(items, "ok", "codex.mcp_server", `${configPath} contains mcp_servers.gramatr`);
|
|
300
290
|
}
|
|
301
291
|
else {
|
|
302
|
-
addResult(items,
|
|
292
|
+
addResult(items, "error", "codex.mcp_server", `${configPath} missing mcp_servers.gramatr`);
|
|
303
293
|
}
|
|
304
294
|
const managedBlock = readManagedBlock(agentsPath, CODEX_BLOCK_START, CODEX_BLOCK_END);
|
|
305
295
|
if (managedBlock) {
|
|
306
|
-
addResult(items,
|
|
296
|
+
addResult(items, "ok", "codex.guidance", `${agentsPath} contains managed gramatr guidance block`);
|
|
307
297
|
}
|
|
308
298
|
else {
|
|
309
|
-
addResult(items,
|
|
299
|
+
addResult(items, "warn", "codex.guidance", `${agentsPath} missing managed guidance block`);
|
|
310
300
|
}
|
|
311
301
|
}
|
|
312
302
|
function verifyJsonMcpTarget(items, label, configPath) {
|
|
313
303
|
const json = parseJson(configPath);
|
|
314
|
-
const gramatrServer = json?.mcpServers && typeof json.mcpServers ===
|
|
304
|
+
const gramatrServer = json?.mcpServers && typeof json.mcpServers === "object"
|
|
315
305
|
? json.mcpServers.gramatr
|
|
316
306
|
: null;
|
|
317
307
|
if (gramatrServer) {
|
|
318
|
-
addResult(items,
|
|
308
|
+
addResult(items, "ok", `${label}.mcp_server`, `${configPath} contains mcpServers.gramatr`);
|
|
319
309
|
}
|
|
320
310
|
else {
|
|
321
|
-
addResult(items,
|
|
311
|
+
addResult(items, "warn", `${label}.mcp_server`, `${configPath} missing mcpServers.gramatr`);
|
|
322
312
|
}
|
|
323
313
|
}
|
|
324
314
|
function verifyOpenCode(items) {
|
|
325
|
-
verifyJsonMcpTarget(items,
|
|
315
|
+
verifyJsonMcpTarget(items, "opencode", getOpenCodeConfigPath(HOME));
|
|
326
316
|
}
|
|
327
317
|
function verifyGemini(items) {
|
|
328
318
|
const manifestPath = getGeminiManifestPath(HOME);
|
|
329
319
|
const hooksPath = getGeminiHooksPath(HOME);
|
|
330
320
|
const manifest = parseJson(manifestPath);
|
|
331
|
-
const geminiServer = manifest?.mcpServers && typeof manifest.mcpServers ===
|
|
321
|
+
const geminiServer = manifest?.mcpServers && typeof manifest.mcpServers === "object"
|
|
332
322
|
? manifest.mcpServers.gramatr
|
|
333
323
|
: null;
|
|
334
324
|
if (geminiServer) {
|
|
335
|
-
addResult(items,
|
|
325
|
+
addResult(items, "ok", "gemini.manifest", `${manifestPath} contains mcpServers.gramatr`);
|
|
336
326
|
}
|
|
337
327
|
else {
|
|
338
|
-
addResult(items,
|
|
328
|
+
addResult(items, "warn", "gemini.manifest", `${manifestPath} missing mcpServers.gramatr`);
|
|
339
329
|
}
|
|
340
330
|
const hooks = parseJson(hooksPath);
|
|
341
|
-
const hasBeforeAgent = hasHookCommand(hooks,
|
|
342
|
-
const hasSessionStart = hasHookCommand(hooks,
|
|
331
|
+
const hasBeforeAgent = hasHookCommand(hooks, "BeforeAgent", "hook user-prompt-submit");
|
|
332
|
+
const hasSessionStart = hasHookCommand(hooks, "SessionStart", "hook session-start");
|
|
343
333
|
if (hasBeforeAgent && hasSessionStart) {
|
|
344
|
-
addResult(items,
|
|
334
|
+
addResult(items, "ok", "gemini.hooks", `${hooksPath} includes BeforeAgent + SessionStart hooks`);
|
|
345
335
|
}
|
|
346
336
|
else {
|
|
347
|
-
addResult(items,
|
|
337
|
+
addResult(items, "warn", "gemini.hooks", `${hooksPath} missing expected hooks (before-agent=${hasBeforeAgent}, session-start=${hasSessionStart})`);
|
|
348
338
|
}
|
|
349
339
|
}
|
|
350
340
|
function verifyLocalSettings(items) {
|
|
351
341
|
const settingsPath = getGramatrSettingsPathFn();
|
|
352
342
|
const settings = parseJson(settingsPath);
|
|
353
343
|
if (!settings) {
|
|
354
|
-
addResult(items,
|
|
344
|
+
addResult(items, "error", "runtime.settings", `${settingsPath} is missing or invalid JSON`);
|
|
355
345
|
return;
|
|
356
346
|
}
|
|
357
347
|
const hasPrincipal = Boolean(settings.principal?.name);
|
|
358
348
|
const hasIdentity = Boolean(settings.daidentity?.name);
|
|
359
349
|
if (hasPrincipal && hasIdentity) {
|
|
360
|
-
addResult(items,
|
|
350
|
+
addResult(items, "ok", "runtime.settings", `${settingsPath} initialized with principal + daidentity`);
|
|
361
351
|
}
|
|
362
352
|
else {
|
|
363
|
-
addResult(items,
|
|
353
|
+
addResult(items, "warn", "runtime.settings", `${settingsPath} missing expected fields (principal=${hasPrincipal}, daidentity=${hasIdentity})`);
|
|
364
354
|
}
|
|
365
355
|
}
|
|
366
356
|
function printPromptBlocks(target) {
|
|
367
|
-
if (target ===
|
|
357
|
+
if (target === "all" || target === "claude") {
|
|
368
358
|
const block = readManagedBlock(getClaudeMarkdownPath(), CLAUDE_BLOCK_START, CLAUDE_BLOCK_END);
|
|
369
|
-
process.stderr.write(
|
|
370
|
-
process.stdout.write((block ||
|
|
359
|
+
process.stderr.write("\n━━━ Claude Managed Guidance Block ━━━\n\n");
|
|
360
|
+
process.stdout.write((block || "[missing managed block]\n") + "\n");
|
|
371
361
|
}
|
|
372
|
-
if (target ===
|
|
362
|
+
if (target === "all" || target === "codex") {
|
|
373
363
|
const block = readManagedBlock(getCodexAgentsPath(), CODEX_BLOCK_START, CODEX_BLOCK_END);
|
|
374
|
-
process.stderr.write(
|
|
375
|
-
process.stdout.write((block ||
|
|
364
|
+
process.stderr.write("\n━━━ Codex Managed Guidance Block ━━━\n\n");
|
|
365
|
+
process.stdout.write((block || "[missing managed block]\n") + "\n");
|
|
376
366
|
}
|
|
377
367
|
}
|
|
378
|
-
export function verifySetupInstall(target =
|
|
368
|
+
export function verifySetupInstall(target = "all", options = {}) {
|
|
379
369
|
const items = [];
|
|
380
370
|
verifyLocalSettings(items);
|
|
381
|
-
if (target ===
|
|
371
|
+
if (target === "all" || target === "claude")
|
|
382
372
|
verifyClaude(items);
|
|
383
|
-
if (target ===
|
|
373
|
+
if (target === "all" || target === "codex")
|
|
384
374
|
verifyCodex(items);
|
|
385
|
-
if (target ===
|
|
375
|
+
if (target === "all" || target === "opencode")
|
|
386
376
|
verifyOpenCode(items);
|
|
387
|
-
if (target ===
|
|
388
|
-
verifyJsonMcpTarget(items,
|
|
377
|
+
if (target === "all" || target === "claude-desktop") {
|
|
378
|
+
verifyJsonMcpTarget(items, "claude-desktop", getClaudeDesktopConfigPath(HOME));
|
|
389
379
|
}
|
|
390
|
-
if (target ===
|
|
391
|
-
verifyJsonMcpTarget(items,
|
|
380
|
+
if (target === "all" || target === "chatgpt-desktop") {
|
|
381
|
+
verifyJsonMcpTarget(items, "chatgpt-desktop", getChatgptDesktopConfigPath(HOME));
|
|
392
382
|
}
|
|
393
|
-
if (target ===
|
|
394
|
-
verifyJsonMcpTarget(items,
|
|
383
|
+
if (target === "all" || target === "cursor") {
|
|
384
|
+
verifyJsonMcpTarget(items, "cursor", getCursorConfigPath(HOME));
|
|
395
385
|
}
|
|
396
|
-
if (target ===
|
|
397
|
-
verifyJsonMcpTarget(items,
|
|
386
|
+
if (target === "all" || target === "windsurf") {
|
|
387
|
+
verifyJsonMcpTarget(items, "windsurf", getWindsurfConfigPath(HOME));
|
|
398
388
|
}
|
|
399
|
-
if (target ===
|
|
400
|
-
verifyJsonMcpTarget(items,
|
|
389
|
+
if (target === "all" || target === "vscode") {
|
|
390
|
+
verifyJsonMcpTarget(items, "vscode", getVscodeConfigPath(HOME));
|
|
401
391
|
}
|
|
402
|
-
if (target ===
|
|
392
|
+
if (target === "all" || target === "gemini")
|
|
403
393
|
verifyGemini(items);
|
|
404
|
-
const hasError = items.some((item) => item.severity ===
|
|
405
|
-
const hasWarn = items.some((item) => item.severity ===
|
|
394
|
+
const hasError = items.some((item) => item.severity === "error");
|
|
395
|
+
const hasWarn = items.some((item) => item.severity === "warn");
|
|
406
396
|
if (options.json) {
|
|
407
397
|
process.stdout.write(JSON.stringify({
|
|
408
398
|
ok: !hasError,
|
|
409
399
|
warnings: hasWarn,
|
|
410
400
|
target,
|
|
411
401
|
checks: items,
|
|
412
|
-
}, null, 2) +
|
|
402
|
+
}, null, 2) + "\n");
|
|
413
403
|
}
|
|
414
404
|
else {
|
|
415
405
|
process.stderr.write(`\n[gramatr-mcp] Setup verification target=${target}\n`);
|
|
416
406
|
for (const item of items) {
|
|
417
|
-
const marker = item.severity ===
|
|
407
|
+
const marker = item.severity === "ok" ? "OK" : item.severity === "warn" ? "WARN" : "ERROR";
|
|
418
408
|
process.stderr.write(` [${marker}] ${item.label}: ${item.detail}\n`);
|
|
419
409
|
}
|
|
420
410
|
}
|
|
@@ -422,14 +412,14 @@ export function verifySetupInstall(target = 'all', options = {}) {
|
|
|
422
412
|
printPromptBlocks(target);
|
|
423
413
|
}
|
|
424
414
|
if (hasError) {
|
|
425
|
-
process.stderr.write(
|
|
415
|
+
process.stderr.write("[gramatr-mcp] Verification failed. Re-run setup for the failing target(s).\n");
|
|
426
416
|
return 1;
|
|
427
417
|
}
|
|
428
418
|
if (hasWarn) {
|
|
429
|
-
process.stderr.write(
|
|
419
|
+
process.stderr.write("[gramatr-mcp] Verification completed with warnings.\n");
|
|
430
420
|
}
|
|
431
421
|
else {
|
|
432
|
-
process.stderr.write(
|
|
422
|
+
process.stderr.write("[gramatr-mcp] Verification passed.\n");
|
|
433
423
|
}
|
|
434
424
|
return 0;
|
|
435
425
|
}
|