vault-go 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -76,6 +76,9 @@ Clientes suportados:
76
76
 
77
77
  - Codex e ChatGPT Desktop local;
78
78
  - Claude Code e Claude Desktop;
79
+ - Grok (config.toml nativo e hooks em `~/.grok/hooks`);
80
+ - Agy / Antigravity CLI e IDE;
81
+ - Gemini CLI;
79
82
  - Cursor e Windsurf;
80
83
  - VS Code com GitHub Copilot;
81
84
  - GitHub Copilot CLI;
@@ -170,7 +173,7 @@ limitados.
170
173
 
171
174
  O seletor de motor segue o mesmo modelo do claude-mem: Claude (CLI da assinatura), OpenRouter (chave), Gemini (chave) ou o Vault AI Resume da conta. Grave chaves com `vault-go engine key openrouter|gemini` ou no painel local; as ferramentas MCP não aceitam segredos.
172
175
 
173
- Na instalação, o Vault Go registra hooks no Claude Code e no Codex. Eles injetam contexto no início da sessão, capturam prompts e ferramentas, e o serviço local (`http://localhost:38850`) gera memórias com o motor escolhido — o mesmo ciclo do claude-mem, persistindo no Vault.
176
+ Na instalação, o Vault Go registra hooks no Claude Code, no Codex, no Grok e no Agy. Eles injetam contexto no início da sessão, capturam prompts e ferramentas, e o serviço local (`http://localhost:38850`) gera memórias com o motor escolhido — o mesmo ciclo do claude-mem, persistindo no Vault.
174
177
 
175
178
  O recurso `vault-go://status` fornece o resumo sanitizado em JSON.
176
179
 
package/dist/cli.js CHANGED
@@ -324,8 +324,8 @@ Uso:
324
324
  Escolhe o motor e o modelo de contexto. No Claude: haiku, sonnet ou opus.
325
325
 
326
326
  vault-go hook <adaptador> <evento>
327
- Usado pelos hooks do Claude/Codex: injeta contexto, captura atividade
328
- e enfileira geração com o motor escolhido.
327
+ Usado pelos hooks do Claude, Codex, Grok e Agy: injeta contexto,
328
+ captura atividade e enfileira geração com o motor escolhido.
329
329
 
330
330
  --lang pt|en|es
331
331
  Idioma do assistente (padrão: idioma do sistema).
@@ -0,0 +1,14 @@
1
+ export declare const HOOK_OWNER = "Vault Go: captura e contexto";
2
+ export declare const HOOK_EVENTS: {
3
+ readonly SessionStart: "context";
4
+ readonly UserPromptSubmit: "session-init";
5
+ readonly PreToolUse: "file-context";
6
+ readonly PostToolUse: "observation";
7
+ readonly Stop: "summarize";
8
+ };
9
+ export declare function quoteShell(value: string): string;
10
+ export declare function hookEntryPath(configHome?: string): string;
11
+ export declare function hookCommand(adapter: string, event: string, configHome?: string): string;
12
+ export type HookClient = "claude" | "codex" | "grok" | "agy";
13
+ export declare function installClientHooks(client: HookClient, userHome?: string, configHome?: string): void;
14
+ export declare function repairClientHooks(userHome?: string, configHome?: string): void;
@@ -0,0 +1,148 @@
1
+ import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { vaultHome } from "./config.js";
6
+ function readJsonObject(path) {
7
+ if (!existsSync(path))
8
+ return {};
9
+ let parsed;
10
+ try {
11
+ parsed = JSON.parse(readFileSync(path, "utf8"));
12
+ }
13
+ catch (error) {
14
+ throw new Error(`JSON inválido em ${path}; o arquivo não foi alterado.`, {
15
+ cause: error,
16
+ });
17
+ }
18
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
19
+ throw new Error(`Configuração inválida em ${path}; o arquivo não foi alterado.`);
20
+ }
21
+ return parsed;
22
+ }
23
+ function atomicWriteJson(path, value) {
24
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
25
+ const temporary = join(dirname(path), `.${process.pid}.${Date.now()}.tmp`);
26
+ writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, {
27
+ mode: 0o600,
28
+ });
29
+ renameSync(temporary, path);
30
+ chmodSync(path, 0o600);
31
+ }
32
+ export const HOOK_OWNER = "Vault Go: captura e contexto";
33
+ export const HOOK_EVENTS = {
34
+ SessionStart: "context",
35
+ UserPromptSubmit: "session-init",
36
+ PreToolUse: "file-context",
37
+ PostToolUse: "observation",
38
+ Stop: "summarize",
39
+ };
40
+ export function quoteShell(value) {
41
+ return "'" + value.replaceAll("'", "'\\''") + "'";
42
+ }
43
+ export function hookEntryPath(configHome = vaultHome()) {
44
+ const staged = join(configHome, "local-runtime", "dist", "hook-runner.js");
45
+ if (existsSync(staged))
46
+ return staged;
47
+ return join(dirname(fileURLToPath(import.meta.url)), "hook-runner.js");
48
+ }
49
+ export function hookCommand(adapter, event, configHome = vaultHome()) {
50
+ return `${quoteShell(process.execPath)} ${quoteShell(hookEntryPath(configHome))} ${adapter} ${event}`;
51
+ }
52
+ function hookManifest(adapter, configHome = vaultHome()) {
53
+ return {
54
+ hooks: Object.fromEntries(Object.entries(HOOK_EVENTS).map(([name, event]) => [
55
+ name,
56
+ [
57
+ {
58
+ hooks: [
59
+ {
60
+ type: "command",
61
+ command: hookCommand(adapter, event, configHome),
62
+ timeout: event === "context" ? 60 : 20,
63
+ statusMessage: HOOK_OWNER,
64
+ ...(event === "observation" ||
65
+ event === "summarize" ||
66
+ event === "file-context"
67
+ ? { async: true }
68
+ : {}),
69
+ },
70
+ ],
71
+ },
72
+ ],
73
+ ])),
74
+ };
75
+ }
76
+ function agyHandler(event, configHome = vaultHome()) {
77
+ return {
78
+ type: "command",
79
+ command: hookCommand("agy", event, configHome),
80
+ timeout: event === "context" ? 60 : 20,
81
+ };
82
+ }
83
+ function agyHookManifest(configHome = vaultHome()) {
84
+ return {
85
+ PreInvocation: [agyHandler("context", configHome)],
86
+ PreToolUse: [{ matcher: "*", hooks: [agyHandler("file-context", configHome)] }],
87
+ PostToolUse: [
88
+ { matcher: "*", hooks: [agyHandler("observation", configHome)] },
89
+ ],
90
+ Stop: [agyHandler("summarize", configHome)],
91
+ };
92
+ }
93
+ export function installClientHooks(client, userHome = homedir(), configHome = vaultHome()) {
94
+ if (client === "grok") {
95
+ atomicWriteJson(join(userHome, ".grok", "hooks", "vault-go.json"), hookManifest("grok", configHome));
96
+ return;
97
+ }
98
+ if (client === "agy") {
99
+ const path = join(userHome, ".gemini", "config", "hooks.json");
100
+ const config = readJsonObject(path);
101
+ atomicWriteJson(path, { ...config, "vault-go": agyHookManifest(configHome) });
102
+ return;
103
+ }
104
+ const path = join(userHome, client === "claude" ? ".claude/settings.json" : ".codex/hooks.json");
105
+ const config = readJsonObject(path);
106
+ const hooks = {
107
+ ...(config.hooks &&
108
+ typeof config.hooks === "object" &&
109
+ !Array.isArray(config.hooks)
110
+ ? config.hooks
111
+ : {}),
112
+ };
113
+ const desired = hookManifest(client, configHome).hooks;
114
+ for (const event of Object.keys(HOOK_EVENTS)) {
115
+ const current = hooks[event];
116
+ const retained = Array.isArray(current)
117
+ ? current
118
+ .map((group) => {
119
+ if (!group ||
120
+ typeof group !== "object" ||
121
+ !Array.isArray(group.hooks))
122
+ return group;
123
+ const filtered = group.hooks.filter((hook) => !hook ||
124
+ typeof hook !== "object" ||
125
+ hook.statusMessage !== HOOK_OWNER);
126
+ return { ...group, hooks: filtered };
127
+ })
128
+ .filter((group) => !group ||
129
+ typeof group !== "object" ||
130
+ !Array.isArray(group.hooks) ||
131
+ group.hooks.length > 0)
132
+ : [];
133
+ hooks[event] = [...retained, ...(desired[event] || [])];
134
+ }
135
+ atomicWriteJson(path, { ...config, hooks });
136
+ }
137
+ export function repairClientHooks(userHome = homedir(), configHome = vaultHome()) {
138
+ installClientHooks("claude", userHome, configHome);
139
+ installClientHooks("codex", userHome, configHome);
140
+ if (existsSync(join(userHome, ".grok"))) {
141
+ installClientHooks("grok", userHome, configHome);
142
+ }
143
+ if (existsSync(join(userHome, ".gemini", "config")) ||
144
+ existsSync(join(userHome, ".gemini", "antigravity")) ||
145
+ existsSync(join(userHome, ".gemini", "antigravity-cli"))) {
146
+ installClientHooks("agy", userHome, configHome);
147
+ }
148
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import { HOOK_EVENTS, runHook } from "./hooks.js";
3
+ const adapter = process.argv[2] ?? "auto";
4
+ const event = process.argv[3];
5
+ if (!event || !HOOK_EVENTS.includes(event)) {
6
+ process.stderr.write("Uso: hook-runner <adaptador> context|session-init|observation|file-context|summarize\n");
7
+ process.exit(1);
8
+ }
9
+ await runHook(adapter, event);
package/dist/hooks.d.ts CHANGED
@@ -2,7 +2,8 @@ import { type VaultMemoryApi } from "./cloud.js";
2
2
  export declare const HOOK_EVENTS: readonly ["context", "session-init", "observation", "file-context", "summarize"];
3
3
  export type HookEvent = (typeof HOOK_EVENTS)[number];
4
4
  type RecordValue = Record<string, unknown>;
5
- export declare function hookOutput(event: HookEvent, context?: string): RecordValue;
5
+ export declare function normalizeHookInput(adapter: string, value: unknown): RecordValue;
6
+ export declare function hookOutput(event: HookEvent, context?: string, adapter?: string): RecordValue;
6
7
  export declare function executeHook(adapter: string, event: HookEvent, value: unknown, cloud?: VaultMemoryApi, home?: string): Promise<RecordValue>;
7
8
  export declare function readHookInput(stream?: NodeJS.ReadableStream): Promise<unknown>;
8
9
  export declare function runHook(adapter: string, event: string): Promise<void>;
package/dist/hooks.js CHANGED
@@ -45,7 +45,34 @@ function redact(value) {
45
45
  }
46
46
  return value;
47
47
  }
48
- export function hookOutput(event, context = "") {
48
+ export function normalizeHookInput(adapter, value) {
49
+ const input = record(value);
50
+ const toolCall = record(input.toolCall);
51
+ const workspaces = Array.isArray(input.workspacePaths)
52
+ ? input.workspacePaths
53
+ : [];
54
+ const workspace = workspaces.find((item) => typeof item === "string" && Boolean(item.trim()));
55
+ return {
56
+ ...input,
57
+ session_id: string(input.session_id, input.sessionId, input.conversation_id, input.conversationId, input.thread_id),
58
+ cwd: string(input.cwd, input.workspace, input.workspaceRoot, workspace),
59
+ tool_name: string(input.tool_name, input.toolName, toolCall.name),
60
+ tool_input: record(input.tool_input ?? input.toolInput ?? toolCall.args),
61
+ last_assistant_message: string(input.last_assistant_message, input.lastAssistantMessage, input.response, input.summary),
62
+ prompt: string(input.prompt, input.user_prompt, input.userPrompt),
63
+ };
64
+ }
65
+ export function hookOutput(event, context = "", adapter = "claude") {
66
+ if (adapter === "agy") {
67
+ if (event === "context" || event === "session-init") {
68
+ return context
69
+ ? { injectSteps: [{ ephemeralMessage: context }] }
70
+ : {};
71
+ }
72
+ if (event === "file-context")
73
+ return { decision: "allow" };
74
+ return {};
75
+ }
49
76
  const result = { continue: true, suppressOutput: true };
50
77
  const name = event === "context"
51
78
  ? "SessionStart"
@@ -79,29 +106,29 @@ async function preferences(cloud) {
79
106
  };
80
107
  }
81
108
  export async function executeHook(adapter, event, value, cloud = new VaultCloudClient(), home = vaultHome()) {
82
- const input = record(value);
83
- const toolName = string(input.tool_name, input.toolName);
109
+ const input = normalizeHookInput(adapter, value);
110
+ const toolName = string(input.tool_name);
84
111
  if (input.private === true ||
85
112
  input.is_private === true ||
86
113
  /vault[_-]go|vault[_-]mem/.test(toolName)) {
87
- return hookOutput(event);
114
+ return hookOutput(event, "", adapter);
88
115
  }
89
116
  const prefs = await preferences(cloud);
90
117
  const reading = event === "context" || event === "file-context";
91
118
  if (!reading && prefs.automaticCapture !== true)
92
- return hookOutput(event);
93
- const tool = record(input.tool_input ?? input.toolInput);
94
- const file = string(tool.file_path, tool.path, tool.notebook_path);
119
+ return hookOutput(event, "", adapter);
120
+ const tool = record(input.tool_input);
121
+ const file = string(tool.file_path, tool.path, tool.notebook_path, tool.AbsolutePath, tool.Path, tool.filePath);
95
122
  const modifies = /write|edit|patch|create|delete|move|rename/i.test(toolName);
96
123
  if (!reading && prefs.captureMode === "focused" && event !== "summarize")
97
- return hookOutput(event);
124
+ return hookOutput(event, "", adapter);
98
125
  if (event === "observation" && prefs.captureMode === "balanced" && !modifies)
99
- return hookOutput(event);
126
+ return hookOutput(event, "", adapter);
100
127
  if (event === "file-context" && !file)
101
- return hookOutput(event);
102
- const sessionId = string(input.session_id, input.sessionId, input.thread_id, input.conversation_id);
128
+ return hookOutput(event, "", adapter);
129
+ const sessionId = string(input.session_id);
103
130
  if (!reading && !sessionId)
104
- return hookOutput(event);
131
+ return hookOutput(event, "", adapter);
105
132
  const cwd = root(string(input.cwd, input.workspace) || process.cwd());
106
133
  const projects = await cloud.projects();
107
134
  if (!Array.isArray(projects))
@@ -110,7 +137,7 @@ export async function executeHook(adapter, event, value, cloud = new VaultCloudC
110
137
  typeof item === "object" &&
111
138
  resolve(String(item.rootPath || "")) === cwd);
112
139
  if (!project && reading)
113
- return hookOutput(event);
140
+ return hookOutput(event, "", adapter);
114
141
  if (!project) {
115
142
  project = record(await cloud.createProject({
116
143
  name: (basename(cwd) || "workspace").padEnd(3, "_").slice(0, 80),
@@ -131,7 +158,7 @@ export async function executeHook(adapter, event, value, cloud = new VaultCloudC
131
158
  paths: [...new Set([file, resolve(cwd, file)])],
132
159
  })
133
160
  : await cloud.context(limits));
134
- return hookOutput(event, string(result.context).slice(0, 24_000));
161
+ return hookOutput(event, string(result.context).slice(0, 24_000), adapter);
135
162
  }
136
163
  const session = record(await cloud.startSession({
137
164
  projectId: project.id,
@@ -140,8 +167,8 @@ export async function executeHook(adapter, event, value, cloud = new VaultCloudC
140
167
  }));
141
168
  if (typeof session.id !== "string")
142
169
  throw new Error("Sessão inválida");
143
- const prompt = string(input.prompt, input.user_prompt);
144
- const response = string(input.last_assistant_message, input.response, input.summary);
170
+ const prompt = string(input.prompt);
171
+ const response = string(input.last_assistant_message);
145
172
  const rawContent = event === "session-init"
146
173
  ? prompt
147
174
  : event === "summarize"
@@ -195,7 +222,7 @@ export async function executeHook(adapter, event, value, cloud = new VaultCloudC
195
222
  }
196
223
  if (event === "summarize")
197
224
  await cloud.endSession(session.id);
198
- return hookOutput(event);
225
+ return hookOutput(event, "", adapter);
199
226
  }
200
227
  export async function readHookInput(stream = process.stdin) {
201
228
  const chunks = [];
@@ -1,10 +1,7 @@
1
1
  export declare const MCP_SERVER_NAME = "vault-go";
2
2
  export declare const MCP_SERVER_COMMAND: readonly ["bunx", "--bun", "vault-go@latest", "serve"];
3
- export declare const MCP_CLIENTS: readonly ["codex", "claude", "claude-desktop", "cursor", "vscode", "copilot", "windsurf", "roo", "opencode"];
3
+ export declare const MCP_CLIENTS: readonly ["codex", "claude", "claude-desktop", "cursor", "vscode", "copilot", "windsurf", "roo", "opencode", "grok", "agy", "gemini"];
4
4
  export type McpClient = (typeof MCP_CLIENTS)[number];
5
- export declare function quoteShell(value: string): string;
6
- export declare function hookEntryPath(configHome?: string): string;
7
- export declare function hookCommand(adapter: string, event: string, configHome?: string): string;
8
5
  export interface DetectedClient {
9
6
  client: McpClient;
10
7
  detected: boolean;
package/dist/installer.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { chmodSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFileSync, } from 'node:fs';
2
2
  import { homedir, platform } from 'node:os';
3
3
  import { dirname, join, resolve } from 'node:path';
4
- import { fileURLToPath } from 'node:url';
5
4
  import { spawnSync } from 'node:child_process';
6
5
  import { vaultHome } from './config.js';
7
6
  import { stageLocalRuntime } from './local-install.js';
7
+ import { installClientHooks } from './hook-install.js';
8
8
  export const MCP_SERVER_NAME = 'vault-go';
9
9
  export const MCP_SERVER_COMMAND = ['bunx', '--bun', 'vault-go@latest', 'serve'];
10
10
  export const MCP_CLIENTS = [
@@ -17,76 +17,14 @@ export const MCP_CLIENTS = [
17
17
  'windsurf',
18
18
  'roo',
19
19
  'opencode',
20
+ 'grok',
21
+ 'agy',
22
+ 'gemini',
20
23
  ];
21
- const HOOK_OWNER = 'Vault Go: captura e contexto';
22
- const HOOK_EVENTS = {
23
- SessionStart: 'context',
24
- UserPromptSubmit: 'session-init',
25
- PreToolUse: 'file-context',
26
- PostToolUse: 'observation',
27
- Stop: 'summarize',
24
+ const CLIENT_ALIASES = {
25
+ antigravity: 'agy',
26
+ 'gemini-cli': 'gemini',
28
27
  };
29
- export function quoteShell(value) {
30
- return "'" + value.replaceAll("'", "'\\''") + "'";
31
- }
32
- export function hookEntryPath(configHome = vaultHome()) {
33
- const staged = join(configHome, 'local-runtime', 'dist', 'index.js');
34
- if (existsSync(staged))
35
- return staged;
36
- return join(dirname(fileURLToPath(import.meta.url)), 'index.js');
37
- }
38
- export function hookCommand(adapter, event, configHome = vaultHome()) {
39
- return `${quoteShell(process.execPath)} ${quoteShell(hookEntryPath(configHome))} hook ${adapter} ${event}`;
40
- }
41
- function hookManifest(adapter, configHome = vaultHome()) {
42
- return {
43
- hooks: Object.fromEntries(Object.entries(HOOK_EVENTS).map(([name, event]) => [
44
- name,
45
- [
46
- {
47
- hooks: [
48
- {
49
- type: 'command',
50
- command: hookCommand(adapter, event, configHome),
51
- timeout: event === 'context' ? 60 : 20,
52
- statusMessage: HOOK_OWNER,
53
- ...(event === 'observation' || event === 'summarize' || event === 'file-context'
54
- ? { async: true }
55
- : {}),
56
- },
57
- ],
58
- },
59
- ],
60
- ])),
61
- };
62
- }
63
- function installClientHooks(client, userHome, configHome = vaultHome()) {
64
- try {
65
- stageLocalRuntime(configHome);
66
- }
67
- catch {
68
- /* tests and incomplete installs still write the hook command */
69
- }
70
- const path = join(userHome, client === 'claude' ? '.claude/settings.json' : '.codex/hooks.json');
71
- const config = readJsonObject(path);
72
- const hooks = { ...(config.hooks && typeof config.hooks === 'object' && !Array.isArray(config.hooks) ? config.hooks : {}) };
73
- const desired = hookManifest(client, configHome).hooks;
74
- for (const event of Object.keys(HOOK_EVENTS)) {
75
- const current = hooks[event];
76
- const retained = Array.isArray(current)
77
- ? current
78
- .map((group) => {
79
- if (!group || typeof group !== 'object' || !Array.isArray(group.hooks))
80
- return group;
81
- const filtered = group.hooks.filter((hook) => !hook || typeof hook !== 'object' || hook.statusMessage !== HOOK_OWNER);
82
- return { ...group, hooks: filtered };
83
- })
84
- .filter((group) => !group || typeof group !== 'object' || !Array.isArray(group.hooks) || group.hooks.length > 0)
85
- : [];
86
- hooks[event] = [...retained, ...(desired[event] || [])];
87
- }
88
- atomicWriteJson(path, { ...config, hooks });
89
- }
90
28
  const runCommand = (command, args) => spawnSync(command, [...args], { stdio: 'ignore' });
91
29
  function readJsonObject(path) {
92
30
  if (!existsSync(path))
@@ -104,12 +42,37 @@ function readJsonObject(path) {
104
42
  return parsed;
105
43
  }
106
44
  function atomicWriteJson(path, value) {
45
+ atomicWriteText(path, `${JSON.stringify(value, null, 2)}\n`);
46
+ }
47
+ function atomicWriteText(path, text) {
107
48
  mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
108
49
  const temporary = join(dirname(path), `.${process.pid}.${Date.now()}.tmp`);
109
- writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
50
+ writeFileSync(temporary, text.endsWith('\n') ? text : `${text}\n`, { mode: 0o600 });
110
51
  renameSync(temporary, path);
111
52
  chmodSync(path, 0o600);
112
53
  }
54
+ function formatTomlValue(value) {
55
+ if (typeof value === 'boolean')
56
+ return value ? 'true' : 'false';
57
+ if (typeof value === 'string')
58
+ return JSON.stringify(value);
59
+ return `[${value.map((item) => JSON.stringify(item)).join(', ')}]`;
60
+ }
61
+ function upsertTomlTable(path, table, assignments) {
62
+ const previous = existsSync(path) ? readFileSync(path, 'utf8') : '';
63
+ const body = Object.entries(assignments)
64
+ .map(([key, value]) => `${key} = ${formatTomlValue(value)}`)
65
+ .join('\n');
66
+ const block = `[${table}]\n${body}\n`;
67
+ const escaped = table.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
68
+ const pattern = new RegExp(`(^|\\r?\\n)\\[${escaped}\\](?:\\r?\\n(?!\\[).*)*`);
69
+ const next = pattern.test(previous)
70
+ ? previous.replace(pattern, (match) => `${/^\r?\n/.exec(match)?.[0] ?? ''}${block}`)
71
+ : previous.trimEnd()
72
+ ? `${previous.trimEnd()}\n\n${block}`
73
+ : block;
74
+ atomicWriteText(path, next);
75
+ }
113
76
  function standardServer() {
114
77
  return {
115
78
  command: MCP_SERVER_COMMAND[0],
@@ -156,6 +119,10 @@ export function installMcpClient(client, options = {}) {
156
119
  const runner = options.runner ?? runCommand;
157
120
  if (client === 'codex') {
158
121
  const result = installViaCli(client, 'codex', ['mcp', 'get', MCP_SERVER_NAME], ['mcp', 'add', MCP_SERVER_NAME, '--', ...MCP_SERVER_COMMAND], runner);
122
+ try {
123
+ stageLocalRuntime(vaultHome());
124
+ }
125
+ catch { /* incomplete installs still write hooks */ }
159
126
  installClientHooks('codex', home);
160
127
  return result;
161
128
  }
@@ -171,6 +138,10 @@ export function installMcpClient(client, options = {}) {
171
138
  '--',
172
139
  ...MCP_SERVER_COMMAND,
173
140
  ], runner);
141
+ try {
142
+ stageLocalRuntime(vaultHome());
143
+ }
144
+ catch { /* incomplete installs still write hooks */ }
174
145
  installClientHooks('claude', home);
175
146
  return result;
176
147
  }
@@ -186,6 +157,36 @@ export function installMcpClient(client, options = {}) {
186
157
  }
187
158
  return { client, status: 'installed', destination: 'VS Code user profile' };
188
159
  }
160
+ if (client === 'grok') {
161
+ const destination = join(home, '.grok', 'config.toml');
162
+ upsertTomlTable(destination, 'mcp_servers.vault-go', {
163
+ command: MCP_SERVER_COMMAND[0],
164
+ args: MCP_SERVER_COMMAND.slice(1),
165
+ enabled: true,
166
+ });
167
+ try {
168
+ stageLocalRuntime(vaultHome());
169
+ }
170
+ catch { /* incomplete installs still write hooks */ }
171
+ installClientHooks('grok', home);
172
+ return { client, status: 'installed', destination };
173
+ }
174
+ if (client === 'agy') {
175
+ const cliPath = join(home, '.gemini', 'config', 'mcp_config.json');
176
+ mergeServer(cliPath);
177
+ mergeServer(join(home, '.gemini', 'antigravity', 'mcp_config.json'));
178
+ try {
179
+ stageLocalRuntime(vaultHome());
180
+ }
181
+ catch { /* incomplete installs still write hooks */ }
182
+ installClientHooks('agy', home);
183
+ return { client, status: 'installed', destination: cliPath };
184
+ }
185
+ if (client === 'gemini') {
186
+ const path = join(home, '.gemini', 'settings.json');
187
+ mergeServer(path);
188
+ return { client, status: 'installed', destination: path };
189
+ }
189
190
  let path;
190
191
  if (client === 'claude-desktop')
191
192
  path = claudeDesktopPath(home);
@@ -230,6 +231,9 @@ export function detectMcpClients(home = homedir()) {
230
231
  ['windsurf', ['windsurf'], [join(home, '.codeium', 'windsurf')]],
231
232
  ['roo', [], []],
232
233
  ['opencode', ['opencode'], [join(home, '.config', 'opencode')]],
234
+ ['grok', ['grok'], [join(home, '.grok')]],
235
+ ['agy', ['agy'], [join(home, '.gemini', 'antigravity-cli'), join(home, '.gemini', 'config'), join(home, '.gemini', 'antigravity')]],
236
+ ['gemini', ['gemini'], [join(home, '.gemini', 'settings.json')]],
233
237
  ];
234
238
  return definitions.map(([client, commands, paths]) => {
235
239
  const command = commands.find((candidate) => Bun.which(candidate));
@@ -256,7 +260,10 @@ export function parseClientSelection(value) {
256
260
  const normalized = value.trim().toLowerCase();
257
261
  if (normalized === 'all' || normalized === 'todos')
258
262
  return [...MCP_CLIENTS];
259
- const selected = [...new Set(normalized.split(',').map((item) => item.trim()).filter(Boolean))];
263
+ const selected = [...new Set(normalized.split(',').map((item) => {
264
+ const trimmed = item.trim();
265
+ return CLIENT_ALIASES[trimmed] ?? trimmed;
266
+ }).filter(Boolean))];
260
267
  const invalid = selected.filter((item) => !MCP_CLIENTS.includes(item));
261
268
  if (invalid.length > 0)
262
269
  throw new Error(`Clientes MCP desconhecidos: ${invalid.join(', ')}`);
@@ -51,13 +51,10 @@ export function stageLocalRuntime(home = vaultHome(), source = dirname(fileURLTo
51
51
  mkdirSync(target, { recursive: true, mode: 0o700 });
52
52
  chmodSync(root, 0o700);
53
53
  chmodSync(target, 0o700);
54
- const excluded = new Set(["server.js", "installer.js", "cli.js"]);
55
54
  if (!existsSync(join(source, "local-worker.js")))
56
55
  throw new Error("Local worker is missing. Build or reinstall vault-go first.");
57
56
  for (const file of readdirSync(source)) {
58
- if (!file.endsWith(".js") ||
59
- file.endsWith(".test.js") ||
60
- excluded.has(file))
57
+ if (!file.endsWith(".js") || file.endsWith(".test.js"))
61
58
  continue;
62
59
  const destination = join(target, file);
63
60
  copyFileSync(join(source, file), `${destination}.tmp`);
@@ -76,6 +73,8 @@ export function stageLocalRuntime(home = vaultHome(), source = dirname(fileURLTo
76
73
  version: manifest.version,
77
74
  type: "module",
78
75
  }), { mode: 0o600 });
76
+ if (!existsSync(join(target, "hook-runner.js")))
77
+ throw new Error("Hook runner is missing. Build or reinstall vault-go first.");
79
78
  return join(target, "local-worker.js");
80
79
  }
81
80
  export async function startLocal(home = vaultHome()) {
@@ -218,6 +217,13 @@ export async function replaceLaunchAgent(domain, path, prepare, operations) {
218
217
  }
219
218
  export async function installLocal(home = vaultHome()) {
220
219
  const worker = stageLocalRuntime(home);
220
+ try {
221
+ const { repairClientHooks } = await import("./hook-install.js");
222
+ repairClientHooks(homedir(), home);
223
+ }
224
+ catch {
225
+ /* settings files may not exist yet */
226
+ }
221
227
  if (process.platform !== "darwin") {
222
228
  await startLocal(home);
223
229
  return { startup: false };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vault-go",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Servidor MCP universal com autenticação e instalação multi-cliente para a plataforma Vault.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,8 +34,12 @@
34
34
  "dist/config.d.ts",
35
35
  "dist/context-engines.js",
36
36
  "dist/context-engines.d.ts",
37
+ "dist/hook-install.js",
38
+ "dist/hook-install.d.ts",
37
39
  "dist/hook-queue.js",
38
40
  "dist/hook-queue.d.ts",
41
+ "dist/hook-runner.js",
42
+ "dist/hook-runner.d.ts",
39
43
  "dist/hooks.js",
40
44
  "dist/hooks.d.ts",
41
45
  "dist/index.js",
@@ -99,7 +103,10 @@
99
103
  "codex",
100
104
  "claude",
101
105
  "copilot",
102
- "cursor"
106
+ "cursor",
107
+ "grok",
108
+ "opencode",
109
+ "antigravity"
103
110
  ],
104
111
  "author": "Gutierrez Henrique",
105
112
  "license": "MIT",