@gramatr/mcp 0.10.13 → 0.10.16
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/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 +166 -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 +12 -0
- 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 +1 -1
- 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.15",
|
|
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,34 @@ 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']`);
|
|
256
227
|
}
|
|
257
|
-
const
|
|
258
|
-
const
|
|
259
|
-
const
|
|
228
|
+
const pluginDir = getGramatrPluginDir();
|
|
229
|
+
const pluginHooksPath = join(pluginDir, "hooks", "hooks.json");
|
|
230
|
+
const pluginHooks = parseJson(pluginHooksPath);
|
|
231
|
+
const hasPromptHook = hasHookCommand(pluginHooks, "UserPromptSubmit", "hook user-prompt-submit");
|
|
232
|
+
const hasSessionStartHook = hasHookCommand(pluginHooks, "SessionStart", "hook session-start");
|
|
260
233
|
if (hasPromptHook && hasSessionStartHook) {
|
|
261
|
-
addResult(items,
|
|
234
|
+
addResult(items, "ok", "claude.hooks", `${pluginHooksPath} includes session-start + user-prompt-submit`);
|
|
262
235
|
}
|
|
263
236
|
else {
|
|
264
|
-
addResult(items,
|
|
237
|
+
addResult(items, "error", "claude.hooks", `${pluginHooksPath} missing required hooks (session-start=${hasSessionStartHook}, user-prompt-submit=${hasPromptHook})`);
|
|
265
238
|
}
|
|
266
239
|
const managedBlock = readManagedBlock(markdownPath, CLAUDE_BLOCK_START, CLAUDE_BLOCK_END);
|
|
267
240
|
if (managedBlock) {
|
|
268
|
-
addResult(items,
|
|
241
|
+
addResult(items, "ok", "claude.guidance", `${markdownPath} contains managed gramatr guidance block`);
|
|
269
242
|
}
|
|
270
243
|
else {
|
|
271
|
-
addResult(items,
|
|
244
|
+
addResult(items, "warn", "claude.guidance", `${markdownPath} missing managed guidance block`);
|
|
272
245
|
}
|
|
273
246
|
}
|
|
274
247
|
function verifyCodex(items) {
|
|
@@ -276,145 +249,144 @@ function verifyCodex(items) {
|
|
|
276
249
|
const configPath = getCodexConfigPathFn();
|
|
277
250
|
const agentsPath = getCodexAgentsPath();
|
|
278
251
|
const hooks = parseJson(hooksPath);
|
|
279
|
-
const hasPromptHook = hasHookCommand(hooks,
|
|
280
|
-
const hasSessionStartHook = hasHookCommand(hooks,
|
|
281
|
-
const hasStopHook = hasHookCommand(hooks,
|
|
252
|
+
const hasPromptHook = hasHookCommand(hooks, "UserPromptSubmit", "hook user-prompt-submit");
|
|
253
|
+
const hasSessionStartHook = hasHookCommand(hooks, "SessionStart", "hook session-start");
|
|
254
|
+
const hasStopHook = hasHookCommand(hooks, "Stop", "hook stop");
|
|
282
255
|
if (hasPromptHook && hasSessionStartHook && hasStopHook) {
|
|
283
|
-
addResult(items,
|
|
256
|
+
addResult(items, "ok", "codex.hooks", `${hooksPath} includes session-start + user-prompt-submit + stop`);
|
|
284
257
|
}
|
|
285
258
|
else {
|
|
286
|
-
addResult(items,
|
|
259
|
+
addResult(items, "error", "codex.hooks", `${hooksPath} missing required hooks (session-start=${hasSessionStartHook}, user-prompt-submit=${hasPromptHook}, stop=${hasStopHook})`);
|
|
287
260
|
}
|
|
288
|
-
const configToml = existsSync(configPath) ? readFileSync(configPath,
|
|
261
|
+
const configToml = existsSync(configPath) ? readFileSync(configPath, "utf8") : "";
|
|
289
262
|
const hooksEnabled = /^\s*codex_hooks\s*=\s*true\s*$/m.test(configToml);
|
|
290
263
|
if (hooksEnabled) {
|
|
291
|
-
addResult(items,
|
|
264
|
+
addResult(items, "ok", "codex.feature_flag", `${configPath} enables codex_hooks`);
|
|
292
265
|
}
|
|
293
266
|
else {
|
|
294
|
-
addResult(items,
|
|
267
|
+
addResult(items, "error", "codex.feature_flag", `${configPath} missing codex_hooks = true`);
|
|
295
268
|
}
|
|
296
|
-
const hasMcpServer = /^\[mcp_servers\.gramatr\]\s*$/m.test(configToml)
|
|
297
|
-
&& /^\s*command\s*=\s*.+$/m.test(configToml);
|
|
269
|
+
const hasMcpServer = /^\[mcp_servers\.gramatr\]\s*$/m.test(configToml) && /^\s*command\s*=\s*.+$/m.test(configToml);
|
|
298
270
|
if (hasMcpServer) {
|
|
299
|
-
addResult(items,
|
|
271
|
+
addResult(items, "ok", "codex.mcp_server", `${configPath} contains mcp_servers.gramatr`);
|
|
300
272
|
}
|
|
301
273
|
else {
|
|
302
|
-
addResult(items,
|
|
274
|
+
addResult(items, "error", "codex.mcp_server", `${configPath} missing mcp_servers.gramatr`);
|
|
303
275
|
}
|
|
304
276
|
const managedBlock = readManagedBlock(agentsPath, CODEX_BLOCK_START, CODEX_BLOCK_END);
|
|
305
277
|
if (managedBlock) {
|
|
306
|
-
addResult(items,
|
|
278
|
+
addResult(items, "ok", "codex.guidance", `${agentsPath} contains managed gramatr guidance block`);
|
|
307
279
|
}
|
|
308
280
|
else {
|
|
309
|
-
addResult(items,
|
|
281
|
+
addResult(items, "warn", "codex.guidance", `${agentsPath} missing managed guidance block`);
|
|
310
282
|
}
|
|
311
283
|
}
|
|
312
284
|
function verifyJsonMcpTarget(items, label, configPath) {
|
|
313
285
|
const json = parseJson(configPath);
|
|
314
|
-
const gramatrServer = json?.mcpServers && typeof json.mcpServers ===
|
|
286
|
+
const gramatrServer = json?.mcpServers && typeof json.mcpServers === "object"
|
|
315
287
|
? json.mcpServers.gramatr
|
|
316
288
|
: null;
|
|
317
289
|
if (gramatrServer) {
|
|
318
|
-
addResult(items,
|
|
290
|
+
addResult(items, "ok", `${label}.mcp_server`, `${configPath} contains mcpServers.gramatr`);
|
|
319
291
|
}
|
|
320
292
|
else {
|
|
321
|
-
addResult(items,
|
|
293
|
+
addResult(items, "warn", `${label}.mcp_server`, `${configPath} missing mcpServers.gramatr`);
|
|
322
294
|
}
|
|
323
295
|
}
|
|
324
296
|
function verifyOpenCode(items) {
|
|
325
|
-
verifyJsonMcpTarget(items,
|
|
297
|
+
verifyJsonMcpTarget(items, "opencode", getOpenCodeConfigPath(HOME));
|
|
326
298
|
}
|
|
327
299
|
function verifyGemini(items) {
|
|
328
300
|
const manifestPath = getGeminiManifestPath(HOME);
|
|
329
301
|
const hooksPath = getGeminiHooksPath(HOME);
|
|
330
302
|
const manifest = parseJson(manifestPath);
|
|
331
|
-
const geminiServer = manifest?.mcpServers && typeof manifest.mcpServers ===
|
|
303
|
+
const geminiServer = manifest?.mcpServers && typeof manifest.mcpServers === "object"
|
|
332
304
|
? manifest.mcpServers.gramatr
|
|
333
305
|
: null;
|
|
334
306
|
if (geminiServer) {
|
|
335
|
-
addResult(items,
|
|
307
|
+
addResult(items, "ok", "gemini.manifest", `${manifestPath} contains mcpServers.gramatr`);
|
|
336
308
|
}
|
|
337
309
|
else {
|
|
338
|
-
addResult(items,
|
|
310
|
+
addResult(items, "warn", "gemini.manifest", `${manifestPath} missing mcpServers.gramatr`);
|
|
339
311
|
}
|
|
340
312
|
const hooks = parseJson(hooksPath);
|
|
341
|
-
const hasBeforeAgent = hasHookCommand(hooks,
|
|
342
|
-
const hasSessionStart = hasHookCommand(hooks,
|
|
313
|
+
const hasBeforeAgent = hasHookCommand(hooks, "BeforeAgent", "hook user-prompt-submit");
|
|
314
|
+
const hasSessionStart = hasHookCommand(hooks, "SessionStart", "hook session-start");
|
|
343
315
|
if (hasBeforeAgent && hasSessionStart) {
|
|
344
|
-
addResult(items,
|
|
316
|
+
addResult(items, "ok", "gemini.hooks", `${hooksPath} includes BeforeAgent + SessionStart hooks`);
|
|
345
317
|
}
|
|
346
318
|
else {
|
|
347
|
-
addResult(items,
|
|
319
|
+
addResult(items, "warn", "gemini.hooks", `${hooksPath} missing expected hooks (before-agent=${hasBeforeAgent}, session-start=${hasSessionStart})`);
|
|
348
320
|
}
|
|
349
321
|
}
|
|
350
322
|
function verifyLocalSettings(items) {
|
|
351
323
|
const settingsPath = getGramatrSettingsPathFn();
|
|
352
324
|
const settings = parseJson(settingsPath);
|
|
353
325
|
if (!settings) {
|
|
354
|
-
addResult(items,
|
|
326
|
+
addResult(items, "error", "runtime.settings", `${settingsPath} is missing or invalid JSON`);
|
|
355
327
|
return;
|
|
356
328
|
}
|
|
357
329
|
const hasPrincipal = Boolean(settings.principal?.name);
|
|
358
330
|
const hasIdentity = Boolean(settings.daidentity?.name);
|
|
359
331
|
if (hasPrincipal && hasIdentity) {
|
|
360
|
-
addResult(items,
|
|
332
|
+
addResult(items, "ok", "runtime.settings", `${settingsPath} initialized with principal + daidentity`);
|
|
361
333
|
}
|
|
362
334
|
else {
|
|
363
|
-
addResult(items,
|
|
335
|
+
addResult(items, "warn", "runtime.settings", `${settingsPath} missing expected fields (principal=${hasPrincipal}, daidentity=${hasIdentity})`);
|
|
364
336
|
}
|
|
365
337
|
}
|
|
366
338
|
function printPromptBlocks(target) {
|
|
367
|
-
if (target ===
|
|
339
|
+
if (target === "all" || target === "claude") {
|
|
368
340
|
const block = readManagedBlock(getClaudeMarkdownPath(), CLAUDE_BLOCK_START, CLAUDE_BLOCK_END);
|
|
369
|
-
process.stderr.write(
|
|
370
|
-
process.stdout.write((block ||
|
|
341
|
+
process.stderr.write("\n━━━ Claude Managed Guidance Block ━━━\n\n");
|
|
342
|
+
process.stdout.write((block || "[missing managed block]\n") + "\n");
|
|
371
343
|
}
|
|
372
|
-
if (target ===
|
|
344
|
+
if (target === "all" || target === "codex") {
|
|
373
345
|
const block = readManagedBlock(getCodexAgentsPath(), CODEX_BLOCK_START, CODEX_BLOCK_END);
|
|
374
|
-
process.stderr.write(
|
|
375
|
-
process.stdout.write((block ||
|
|
346
|
+
process.stderr.write("\n━━━ Codex Managed Guidance Block ━━━\n\n");
|
|
347
|
+
process.stdout.write((block || "[missing managed block]\n") + "\n");
|
|
376
348
|
}
|
|
377
349
|
}
|
|
378
|
-
export function verifySetupInstall(target =
|
|
350
|
+
export function verifySetupInstall(target = "all", options = {}) {
|
|
379
351
|
const items = [];
|
|
380
352
|
verifyLocalSettings(items);
|
|
381
|
-
if (target ===
|
|
353
|
+
if (target === "all" || target === "claude")
|
|
382
354
|
verifyClaude(items);
|
|
383
|
-
if (target ===
|
|
355
|
+
if (target === "all" || target === "codex")
|
|
384
356
|
verifyCodex(items);
|
|
385
|
-
if (target ===
|
|
357
|
+
if (target === "all" || target === "opencode")
|
|
386
358
|
verifyOpenCode(items);
|
|
387
|
-
if (target ===
|
|
388
|
-
verifyJsonMcpTarget(items,
|
|
359
|
+
if (target === "all" || target === "claude-desktop") {
|
|
360
|
+
verifyJsonMcpTarget(items, "claude-desktop", getClaudeDesktopConfigPath(HOME));
|
|
389
361
|
}
|
|
390
|
-
if (target ===
|
|
391
|
-
verifyJsonMcpTarget(items,
|
|
362
|
+
if (target === "all" || target === "chatgpt-desktop") {
|
|
363
|
+
verifyJsonMcpTarget(items, "chatgpt-desktop", getChatgptDesktopConfigPath(HOME));
|
|
392
364
|
}
|
|
393
|
-
if (target ===
|
|
394
|
-
verifyJsonMcpTarget(items,
|
|
365
|
+
if (target === "all" || target === "cursor") {
|
|
366
|
+
verifyJsonMcpTarget(items, "cursor", getCursorConfigPath(HOME));
|
|
395
367
|
}
|
|
396
|
-
if (target ===
|
|
397
|
-
verifyJsonMcpTarget(items,
|
|
368
|
+
if (target === "all" || target === "windsurf") {
|
|
369
|
+
verifyJsonMcpTarget(items, "windsurf", getWindsurfConfigPath(HOME));
|
|
398
370
|
}
|
|
399
|
-
if (target ===
|
|
400
|
-
verifyJsonMcpTarget(items,
|
|
371
|
+
if (target === "all" || target === "vscode") {
|
|
372
|
+
verifyJsonMcpTarget(items, "vscode", getVscodeConfigPath(HOME));
|
|
401
373
|
}
|
|
402
|
-
if (target ===
|
|
374
|
+
if (target === "all" || target === "gemini")
|
|
403
375
|
verifyGemini(items);
|
|
404
|
-
const hasError = items.some((item) => item.severity ===
|
|
405
|
-
const hasWarn = items.some((item) => item.severity ===
|
|
376
|
+
const hasError = items.some((item) => item.severity === "error");
|
|
377
|
+
const hasWarn = items.some((item) => item.severity === "warn");
|
|
406
378
|
if (options.json) {
|
|
407
379
|
process.stdout.write(JSON.stringify({
|
|
408
380
|
ok: !hasError,
|
|
409
381
|
warnings: hasWarn,
|
|
410
382
|
target,
|
|
411
383
|
checks: items,
|
|
412
|
-
}, null, 2) +
|
|
384
|
+
}, null, 2) + "\n");
|
|
413
385
|
}
|
|
414
386
|
else {
|
|
415
387
|
process.stderr.write(`\n[gramatr-mcp] Setup verification target=${target}\n`);
|
|
416
388
|
for (const item of items) {
|
|
417
|
-
const marker = item.severity ===
|
|
389
|
+
const marker = item.severity === "ok" ? "OK" : item.severity === "warn" ? "WARN" : "ERROR";
|
|
418
390
|
process.stderr.write(` [${marker}] ${item.label}: ${item.detail}\n`);
|
|
419
391
|
}
|
|
420
392
|
}
|
|
@@ -422,14 +394,14 @@ export function verifySetupInstall(target = 'all', options = {}) {
|
|
|
422
394
|
printPromptBlocks(target);
|
|
423
395
|
}
|
|
424
396
|
if (hasError) {
|
|
425
|
-
process.stderr.write(
|
|
397
|
+
process.stderr.write("[gramatr-mcp] Verification failed. Re-run setup for the failing target(s).\n");
|
|
426
398
|
return 1;
|
|
427
399
|
}
|
|
428
400
|
if (hasWarn) {
|
|
429
|
-
process.stderr.write(
|
|
401
|
+
process.stderr.write("[gramatr-mcp] Verification completed with warnings.\n");
|
|
430
402
|
}
|
|
431
403
|
else {
|
|
432
|
-
process.stderr.write(
|
|
404
|
+
process.stderr.write("[gramatr-mcp] Verification passed.\n");
|
|
433
405
|
}
|
|
434
406
|
return 0;
|
|
435
407
|
}
|