@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.
Files changed (50) hide show
  1. package/dist/bin/login.js +1 -1
  2. package/dist/bin/login.js.map +1 -1
  3. package/dist/bin/setup-config-io.d.ts +5 -0
  4. package/dist/bin/setup-config-io.d.ts.map +1 -1
  5. package/dist/bin/setup-config-io.js +53 -0
  6. package/dist/bin/setup-config-io.js.map +1 -1
  7. package/dist/bin/setup-legacy.d.ts.map +1 -1
  8. package/dist/bin/setup-legacy.js +6 -18
  9. package/dist/bin/setup-legacy.js.map +1 -1
  10. package/dist/bin/setup.d.ts +6 -6
  11. package/dist/bin/setup.d.ts.map +1 -1
  12. package/dist/bin/setup.js +166 -194
  13. package/dist/bin/setup.js.map +1 -1
  14. package/dist/hooks/agent-gate.js +1 -1
  15. package/dist/hooks/agent-gate.js.map +1 -1
  16. package/dist/hooks/agent-verify.d.ts +1 -1
  17. package/dist/hooks/agent-verify.js +3 -3
  18. package/dist/hooks/agent-verify.js.map +1 -1
  19. package/dist/hooks/generated/schema-constants.d.ts +2 -1
  20. package/dist/hooks/generated/schema-constants.d.ts.map +1 -1
  21. package/dist/hooks/generated/schema-constants.js +3 -1
  22. package/dist/hooks/generated/schema-constants.js.map +1 -1
  23. package/dist/hooks/lib/formatting-compat.js +1 -1
  24. package/dist/hooks/lib/formatting-compat.js.map +1 -1
  25. package/dist/hooks/lib/hook-state.d.ts +2 -0
  26. package/dist/hooks/lib/hook-state.d.ts.map +1 -1
  27. package/dist/hooks/lib/hook-state.js +12 -0
  28. package/dist/hooks/lib/hook-state.js.map +1 -1
  29. package/dist/hooks/lib/intelligence.js +1 -1
  30. package/dist/hooks/lib/intelligence.js.map +1 -1
  31. package/dist/hooks/manifest.d.ts +1 -1
  32. package/dist/hooks/manifest.js +1 -1
  33. package/dist/hooks/manifest.js.map +1 -1
  34. package/dist/hooks/user-prompt-submit.d.ts.map +1 -1
  35. package/dist/hooks/user-prompt-submit.js +9 -1
  36. package/dist/hooks/user-prompt-submit.js.map +1 -1
  37. package/dist/server/hooks-listener.d.ts.map +1 -1
  38. package/dist/server/hooks-listener.js +91 -15
  39. package/dist/server/hooks-listener.js.map +1 -1
  40. package/dist/setup/generated/instruction-blocks.d.ts +11 -11
  41. package/dist/setup/generated/instruction-blocks.d.ts.map +1 -1
  42. package/dist/setup/generated/instruction-blocks.js +11 -11
  43. package/dist/setup/generated/instruction-blocks.js.map +1 -1
  44. package/dist/setup/instructions.js +3 -3
  45. package/dist/setup/integrations.d.ts +1 -0
  46. package/dist/setup/integrations.d.ts.map +1 -1
  47. package/dist/setup/integrations.js +7 -0
  48. package/dist/setup/integrations.js.map +1 -1
  49. package/package.json +1 -1
  50. 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 { readFileSync, writeFileSync, existsSync, mkdirSync, } from 'node:fs';
19
- import { join, dirname } from 'node:path';
20
- import { getGramatrUrlFromEnv } from '../config-runtime.js';
21
- import { buildClaudeHooksFile, buildClaudeMcpServerEntry, mergeManagedHooks, } from '../setup/integrations.js';
22
- import { CLAUDE_BLOCK_END, CLAUDE_BLOCK_START, CLAUDE_CODE_GUIDANCE, CODEX_BLOCK_END, CODEX_BLOCK_START, } from '../setup/instructions.js';
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, getCodexHooksPath, getCodexConfigPath, getClaudeMarkdownPath, getCodexAgentsPath, getGramatrSettingsPath, readJsonFile, readClaudeConfig, escapeRegExp, upsertManagedBlock, ensureLocalSettings, parseJson, readManagedBlock, hasHookCommand, } from './setup-config-io.js';
26
- export { runCleanInstall, } from './setup-legacy.js';
27
- export { ensureCodexMcpServerConfig, emitInstallPromptSuggestion, setupCodex, setupMcpTarget, setupGemini, setupOpenCode, setupWeb, } from './setup-platforms.js';
28
- export { resolveBinaryPath, deployPlatformBinary, } from './setup-shared.js';
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 { readClaudeConfig, upsertManagedBlock, ensureLocalSettings, getClaudeConfigPath, getClaudeSettingsPath, getClaudeMarkdownPath, getCodexHooksPath as getCodexHooksPathFn, getCodexConfigPath as getCodexConfigPathFn, getCodexAgentsPath, getGramatrSettingsPath as getGramatrSettingsPathFn, parseJson, readManagedBlock, hasHookCommand, HOME, } from './setup-config-io.js';
31
- import { runCleanInstall } from './setup-legacy.js';
32
- import { emitInstallPromptSuggestion, setupCodex, setupMcpTarget, setupGemini, setupOpenCode, } from './setup-platforms.js';
33
- import { deployPlatformBinary } from './setup-shared.js';
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
- 'claude',
36
- 'codex',
37
- 'opencode',
38
- 'gemini',
39
- 'claude-desktop',
40
- 'chatgpt-desktop',
41
- 'cursor',
42
- 'windsurf',
43
- 'vscode',
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, 'utf8') : '';
57
- const localEntry = buildClaudeMcpServerEntry();
58
- const resolvedCommand = localEntry.command.startsWith('~/')
59
- ? localEntry.command.replace(/^~\//, `${HOME}/`)
60
- : localEntry.command;
61
- const resolvedArgs = (localEntry.args || []).map(a => a.startsWith('~/') ? a.replace(/^~\//, `${HOME}/`) : a);
62
- const serverEntry = { command: resolvedCommand };
63
- if (resolvedArgs.length > 0)
64
- serverEntry.args = resolvedArgs;
65
- const managedHooks = buildClaudeHooksFile(join(HOME, '.gramatr'));
66
- const gramatrUrl = getGramatrUrlFromEnv() || 'https://api.gramatr.com/mcp';
67
- const mergedSettings = {
68
- ...settings,
69
- ...mergeManagedHooks(settings, managedHooks),
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
- ...settings.env,
72
- GRAMATR_DIR: join(HOME, '.gramatr'),
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
- statusLine: {
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('[gramatr-mcp] Dry run — would write to: ' + configPath + '\n');
102
- process.stderr.write(JSON.stringify(config.mcpServers['gramatr'], null, 2) + '\n');
103
- process.stderr.write('[gramatr-mcp] Dry run — would write Claude hooks to: ' + settingsPath + '\n');
104
- process.stderr.write(JSON.stringify(mergedSettings.hooks, null, 2) + '\n');
105
- process.stderr.write('[gramatr-mcp] Dry run — would write Claude guidance to: ' + markdownPath + '\n');
106
- process.stderr.write(mergedMarkdown + '\n');
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('claude-code');
88
+ emitInstallPromptSuggestion("claude-code");
109
89
  return;
110
90
  }
111
- // Write back
112
- mkdirSync(join(HOME, '.claude'), { recursive: true });
91
+ mkdirSync(join(HOME, ".claude"), { recursive: true });
113
92
  ensureLocalSettings();
114
- writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
115
- writeFileSync(settingsPath, JSON.stringify(mergedSettings, null, 2) + '\n', 'utf8');
116
- writeFileSync(markdownPath, mergedMarkdown, 'utf8');
117
- process.stderr.write(`[gramatr-mcp] Configured Claude Code MCP server in ${configPath}\n`);
118
- process.stderr.write(`[gramatr-mcp] Configured Claude hooks in ${settingsPath}\n`);
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('[gramatr-mcp] Restart Claude Code to pick up the change.\n');
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('claude-code');
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('Claude Desktop', getClaudeDesktopConfigPath(HOME), dryRun);
106
+ setupMcpTarget("Claude Desktop", getClaudeDesktopConfigPath(HOME), dryRun);
134
107
  if (showPrompts)
135
- emitInstallPromptSuggestion('claude-desktop');
108
+ emitInstallPromptSuggestion("claude-desktop");
136
109
  }
137
110
  export function setupChatgptDesktop(dryRun = false, showPrompts = false) {
138
- setupMcpTarget('ChatGPT Desktop', getChatgptDesktopConfigPath(HOME), dryRun);
111
+ setupMcpTarget("ChatGPT Desktop", getChatgptDesktopConfigPath(HOME), dryRun);
139
112
  if (showPrompts)
140
- emitInstallPromptSuggestion('chatgpt-desktop');
113
+ emitInstallPromptSuggestion("chatgpt-desktop");
141
114
  }
142
115
  export function setupCursor(dryRun = false, showPrompts = false) {
143
- setupMcpTarget('Cursor', getCursorConfigPath(HOME), dryRun);
116
+ setupMcpTarget("Cursor", getCursorConfigPath(HOME), dryRun);
144
117
  if (showPrompts)
145
- emitInstallPromptSuggestion('cursor');
118
+ emitInstallPromptSuggestion("cursor");
146
119
  }
147
120
  export function setupWindsurf(dryRun = false, showPrompts = false) {
148
- setupMcpTarget('Windsurf', getWindsurfConfigPath(HOME), dryRun);
121
+ setupMcpTarget("Windsurf", getWindsurfConfigPath(HOME), dryRun);
149
122
  if (showPrompts)
150
- emitInstallPromptSuggestion('windsurf');
123
+ emitInstallPromptSuggestion("windsurf");
151
124
  }
152
125
  export function setupVscode(dryRun = false, showPrompts = false) {
153
- setupMcpTarget('VS Code', getVscodeConfigPath(HOME), dryRun);
126
+ setupMcpTarget("VS Code", getVscodeConfigPath(HOME), dryRun);
154
127
  if (showPrompts)
155
- emitInstallPromptSuggestion('vscode');
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, '.claude')) || existsSync(getClaudeConfigPath()),
161
- codex: existsSync(join(HOME, '.codex')),
162
- opencode: existsSync(join(HOME, '.config', 'opencode')) || existsSync('opencode.json'),
163
- gemini: existsSync(join(HOME, '.gemini')),
164
- 'claude-desktop': existsSync(dirname(getClaudeDesktopConfigPath(HOME))),
165
- 'chatgpt-desktop': existsSync(dirname(getChatgptDesktopConfigPath(HOME))),
166
- cursor: existsSync(join(HOME, '.cursor')),
167
- windsurf: existsSync(join(HOME, '.windsurf')),
168
- vscode: existsSync(join(HOME, '.vscode')),
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('[gramatr-mcp] auto-detect scan complete\n');
155
+ process.stderr.write("[gramatr-mcp] auto-detect scan complete\n");
183
156
  if (detected.length === 0 && !requested) {
184
- process.stderr.write('[gramatr-mcp] No supported local clients detected.\n');
185
- process.stderr.write('[gramatr-mcp] Install manually with: setup <target>\n');
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(', ')}\n`);
189
- process.stderr.write(`[gramatr-mcp] Selected targets: ${selected.join(', ')}\n`);
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(', ')}\n`);
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('[gramatr-mcp] list-only mode: no setup changes made.\n');
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 'claude':
178
+ case "claude":
206
179
  setupClaude(dryRun, false, showPrompts);
207
180
  break;
208
- case 'codex':
181
+ case "codex":
209
182
  setupCodex(dryRun, showPrompts);
210
183
  break;
211
- case 'opencode':
184
+ case "opencode":
212
185
  setupOpenCode(dryRun, showPrompts);
213
186
  break;
214
- case 'gemini':
187
+ case "gemini":
215
188
  setupGemini(dryRun, showPrompts);
216
189
  break;
217
- case 'claude-desktop':
190
+ case "claude-desktop":
218
191
  setupClaudeDesktop(dryRun, showPrompts);
219
192
  break;
220
- case 'chatgpt-desktop':
193
+ case "chatgpt-desktop":
221
194
  setupChatgptDesktop(dryRun, showPrompts);
222
195
  break;
223
- case 'cursor':
196
+ case "cursor":
224
197
  setupCursor(dryRun, showPrompts);
225
198
  break;
226
- case 'windsurf':
199
+ case "windsurf":
227
200
  setupWindsurf(dryRun, showPrompts);
228
201
  break;
229
- case 'vscode':
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 config = parseJson(configPath);
248
- const gramatrServer = config?.mcpServers && typeof config.mcpServers === 'object'
249
- ? config.mcpServers.gramatr
250
- : null;
251
- if (gramatrServer) {
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, 'error', 'claude.mcp_server', `${configPath} missing mcpServers.gramatr`);
226
+ addResult(items, "error", "claude.plugin", `${settingsPath} missing enabledPlugins['gramatr@gramatr']`);
256
227
  }
257
- const settings = parseJson(settingsPath);
258
- const hasPromptHook = hasHookCommand(settings, 'UserPromptSubmit', 'hook user-prompt-submit');
259
- const hasSessionStartHook = hasHookCommand(settings, 'SessionStart', 'hook session-start');
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, 'ok', 'claude.hooks', `${settingsPath} includes session-start + user-prompt-submit`);
234
+ addResult(items, "ok", "claude.hooks", `${pluginHooksPath} includes session-start + user-prompt-submit`);
262
235
  }
263
236
  else {
264
- addResult(items, 'error', 'claude.hooks', `${settingsPath} missing required hooks (session-start=${hasSessionStartHook}, user-prompt-submit=${hasPromptHook})`);
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, 'ok', 'claude.guidance', `${markdownPath} contains managed gramatr guidance block`);
241
+ addResult(items, "ok", "claude.guidance", `${markdownPath} contains managed gramatr guidance block`);
269
242
  }
270
243
  else {
271
- addResult(items, 'warn', 'claude.guidance', `${markdownPath} missing managed guidance block`);
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, 'UserPromptSubmit', 'hook user-prompt-submit');
280
- const hasSessionStartHook = hasHookCommand(hooks, 'SessionStart', 'hook session-start');
281
- const hasStopHook = hasHookCommand(hooks, 'Stop', 'hook stop');
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, 'ok', 'codex.hooks', `${hooksPath} includes session-start + user-prompt-submit + stop`);
256
+ addResult(items, "ok", "codex.hooks", `${hooksPath} includes session-start + user-prompt-submit + stop`);
284
257
  }
285
258
  else {
286
- addResult(items, 'error', 'codex.hooks', `${hooksPath} missing required hooks (session-start=${hasSessionStartHook}, user-prompt-submit=${hasPromptHook}, stop=${hasStopHook})`);
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, 'utf8') : '';
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, 'ok', 'codex.feature_flag', `${configPath} enables codex_hooks`);
264
+ addResult(items, "ok", "codex.feature_flag", `${configPath} enables codex_hooks`);
292
265
  }
293
266
  else {
294
- addResult(items, 'error', 'codex.feature_flag', `${configPath} missing codex_hooks = true`);
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, 'ok', 'codex.mcp_server', `${configPath} contains mcp_servers.gramatr`);
271
+ addResult(items, "ok", "codex.mcp_server", `${configPath} contains mcp_servers.gramatr`);
300
272
  }
301
273
  else {
302
- addResult(items, 'error', 'codex.mcp_server', `${configPath} missing mcp_servers.gramatr`);
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, 'ok', 'codex.guidance', `${agentsPath} contains managed gramatr guidance block`);
278
+ addResult(items, "ok", "codex.guidance", `${agentsPath} contains managed gramatr guidance block`);
307
279
  }
308
280
  else {
309
- addResult(items, 'warn', 'codex.guidance', `${agentsPath} missing managed guidance block`);
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 === 'object'
286
+ const gramatrServer = json?.mcpServers && typeof json.mcpServers === "object"
315
287
  ? json.mcpServers.gramatr
316
288
  : null;
317
289
  if (gramatrServer) {
318
- addResult(items, 'ok', `${label}.mcp_server`, `${configPath} contains mcpServers.gramatr`);
290
+ addResult(items, "ok", `${label}.mcp_server`, `${configPath} contains mcpServers.gramatr`);
319
291
  }
320
292
  else {
321
- addResult(items, 'warn', `${label}.mcp_server`, `${configPath} missing mcpServers.gramatr`);
293
+ addResult(items, "warn", `${label}.mcp_server`, `${configPath} missing mcpServers.gramatr`);
322
294
  }
323
295
  }
324
296
  function verifyOpenCode(items) {
325
- verifyJsonMcpTarget(items, 'opencode', getOpenCodeConfigPath(HOME));
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 === 'object'
303
+ const geminiServer = manifest?.mcpServers && typeof manifest.mcpServers === "object"
332
304
  ? manifest.mcpServers.gramatr
333
305
  : null;
334
306
  if (geminiServer) {
335
- addResult(items, 'ok', 'gemini.manifest', `${manifestPath} contains mcpServers.gramatr`);
307
+ addResult(items, "ok", "gemini.manifest", `${manifestPath} contains mcpServers.gramatr`);
336
308
  }
337
309
  else {
338
- addResult(items, 'warn', 'gemini.manifest', `${manifestPath} missing mcpServers.gramatr`);
310
+ addResult(items, "warn", "gemini.manifest", `${manifestPath} missing mcpServers.gramatr`);
339
311
  }
340
312
  const hooks = parseJson(hooksPath);
341
- const hasBeforeAgent = hasHookCommand(hooks, 'BeforeAgent', 'hook user-prompt-submit');
342
- const hasSessionStart = hasHookCommand(hooks, 'SessionStart', 'hook session-start');
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, 'ok', 'gemini.hooks', `${hooksPath} includes BeforeAgent + SessionStart hooks`);
316
+ addResult(items, "ok", "gemini.hooks", `${hooksPath} includes BeforeAgent + SessionStart hooks`);
345
317
  }
346
318
  else {
347
- addResult(items, 'warn', 'gemini.hooks', `${hooksPath} missing expected hooks (before-agent=${hasBeforeAgent}, session-start=${hasSessionStart})`);
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, 'error', 'runtime.settings', `${settingsPath} is missing or invalid JSON`);
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, 'ok', 'runtime.settings', `${settingsPath} initialized with principal + daidentity`);
332
+ addResult(items, "ok", "runtime.settings", `${settingsPath} initialized with principal + daidentity`);
361
333
  }
362
334
  else {
363
- addResult(items, 'warn', 'runtime.settings', `${settingsPath} missing expected fields (principal=${hasPrincipal}, daidentity=${hasIdentity})`);
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 === 'all' || target === 'claude') {
339
+ if (target === "all" || target === "claude") {
368
340
  const block = readManagedBlock(getClaudeMarkdownPath(), CLAUDE_BLOCK_START, CLAUDE_BLOCK_END);
369
- process.stderr.write('\n━━━ Claude Managed Guidance Block ━━━\n\n');
370
- process.stdout.write((block || '[missing managed block]\n') + '\n');
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 === 'all' || target === 'codex') {
344
+ if (target === "all" || target === "codex") {
373
345
  const block = readManagedBlock(getCodexAgentsPath(), CODEX_BLOCK_START, CODEX_BLOCK_END);
374
- process.stderr.write('\n━━━ Codex Managed Guidance Block ━━━\n\n');
375
- process.stdout.write((block || '[missing managed block]\n') + '\n');
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 = 'all', options = {}) {
350
+ export function verifySetupInstall(target = "all", options = {}) {
379
351
  const items = [];
380
352
  verifyLocalSettings(items);
381
- if (target === 'all' || target === 'claude')
353
+ if (target === "all" || target === "claude")
382
354
  verifyClaude(items);
383
- if (target === 'all' || target === 'codex')
355
+ if (target === "all" || target === "codex")
384
356
  verifyCodex(items);
385
- if (target === 'all' || target === 'opencode')
357
+ if (target === "all" || target === "opencode")
386
358
  verifyOpenCode(items);
387
- if (target === 'all' || target === 'claude-desktop') {
388
- verifyJsonMcpTarget(items, 'claude-desktop', getClaudeDesktopConfigPath(HOME));
359
+ if (target === "all" || target === "claude-desktop") {
360
+ verifyJsonMcpTarget(items, "claude-desktop", getClaudeDesktopConfigPath(HOME));
389
361
  }
390
- if (target === 'all' || target === 'chatgpt-desktop') {
391
- verifyJsonMcpTarget(items, 'chatgpt-desktop', getChatgptDesktopConfigPath(HOME));
362
+ if (target === "all" || target === "chatgpt-desktop") {
363
+ verifyJsonMcpTarget(items, "chatgpt-desktop", getChatgptDesktopConfigPath(HOME));
392
364
  }
393
- if (target === 'all' || target === 'cursor') {
394
- verifyJsonMcpTarget(items, 'cursor', getCursorConfigPath(HOME));
365
+ if (target === "all" || target === "cursor") {
366
+ verifyJsonMcpTarget(items, "cursor", getCursorConfigPath(HOME));
395
367
  }
396
- if (target === 'all' || target === 'windsurf') {
397
- verifyJsonMcpTarget(items, 'windsurf', getWindsurfConfigPath(HOME));
368
+ if (target === "all" || target === "windsurf") {
369
+ verifyJsonMcpTarget(items, "windsurf", getWindsurfConfigPath(HOME));
398
370
  }
399
- if (target === 'all' || target === 'vscode') {
400
- verifyJsonMcpTarget(items, 'vscode', getVscodeConfigPath(HOME));
371
+ if (target === "all" || target === "vscode") {
372
+ verifyJsonMcpTarget(items, "vscode", getVscodeConfigPath(HOME));
401
373
  }
402
- if (target === 'all' || target === 'gemini')
374
+ if (target === "all" || target === "gemini")
403
375
  verifyGemini(items);
404
- const hasError = items.some((item) => item.severity === 'error');
405
- const hasWarn = items.some((item) => item.severity === 'warn');
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) + '\n');
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 === 'ok' ? 'OK' : item.severity === 'warn' ? 'WARN' : 'ERROR';
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('[gramatr-mcp] Verification failed. Re-run setup for the failing target(s).\n');
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('[gramatr-mcp] Verification completed with warnings.\n');
401
+ process.stderr.write("[gramatr-mcp] Verification completed with warnings.\n");
430
402
  }
431
403
  else {
432
- process.stderr.write('[gramatr-mcp] Verification passed.\n');
404
+ process.stderr.write("[gramatr-mcp] Verification passed.\n");
433
405
  }
434
406
  return 0;
435
407
  }