agentvigil 1.0.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/LICENSE +21 -0
- package/README.md +76 -0
- package/dist/commands/autostart.d.ts +3 -0
- package/dist/commands/autostart.d.ts.map +1 -0
- package/dist/commands/autostart.js +57 -0
- package/dist/commands/autostart.js.map +1 -0
- package/dist/commands/daemon.d.ts +31 -0
- package/dist/commands/daemon.d.ts.map +1 -0
- package/dist/commands/daemon.js +131 -0
- package/dist/commands/daemon.js.map +1 -0
- package/dist/commands/setup.d.ts +5 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +158 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/start.d.ts +2 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +89 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/uninstall.d.ts +7 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/uninstall.js +13 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/crypto/encryption.d.ts +11 -0
- package/dist/crypto/encryption.d.ts.map +1 -0
- package/dist/crypto/encryption.js +57 -0
- package/dist/crypto/encryption.js.map +1 -0
- package/dist/hooks/hook-handler.d.ts +18 -0
- package/dist/hooks/hook-handler.d.ts.map +1 -0
- package/dist/hooks/hook-handler.js +256 -0
- package/dist/hooks/hook-handler.js.map +1 -0
- package/dist/hooks/hook-manager.d.ts +22 -0
- package/dist/hooks/hook-manager.d.ts.map +1 -0
- package/dist/hooks/hook-manager.js +133 -0
- package/dist/hooks/hook-manager.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +88 -0
- package/dist/index.js.map +1 -0
- package/dist/notifications/fcm-client.d.ts +15 -0
- package/dist/notifications/fcm-client.d.ts.map +1 -0
- package/dist/notifications/fcm-client.js +72 -0
- package/dist/notifications/fcm-client.js.map +1 -0
- package/dist/notifications/ntfy-client.d.ts +6 -0
- package/dist/notifications/ntfy-client.d.ts.map +1 -0
- package/dist/notifications/ntfy-client.js +51 -0
- package/dist/notifications/ntfy-client.js.map +1 -0
- package/dist/relay/relay-handler.d.ts +51 -0
- package/dist/relay/relay-handler.d.ts.map +1 -0
- package/dist/relay/relay-handler.js +325 -0
- package/dist/relay/relay-handler.js.map +1 -0
- package/dist/sessions/keystroke-injector.d.ts +4 -0
- package/dist/sessions/keystroke-injector.d.ts.map +1 -0
- package/dist/sessions/keystroke-injector.js +216 -0
- package/dist/sessions/keystroke-injector.js.map +1 -0
- package/dist/sessions/process-detector.d.ts +27 -0
- package/dist/sessions/process-detector.d.ts.map +1 -0
- package/dist/sessions/process-detector.js +180 -0
- package/dist/sessions/process-detector.js.map +1 -0
- package/dist/sessions/session-manager.d.ts +37 -0
- package/dist/sessions/session-manager.d.ts.map +1 -0
- package/dist/sessions/session-manager.js +90 -0
- package/dist/sessions/session-manager.js.map +1 -0
- package/dist/sessions/session-poller.d.ts +18 -0
- package/dist/sessions/session-poller.d.ts.map +1 -0
- package/dist/sessions/session-poller.js +73 -0
- package/dist/sessions/session-poller.js.map +1 -0
- package/dist/sessions/session-watcher.d.ts +20 -0
- package/dist/sessions/session-watcher.d.ts.map +1 -0
- package/dist/sessions/session-watcher.js +71 -0
- package/dist/sessions/session-watcher.js.map +1 -0
- package/dist/sessions/tmux-bridge.d.ts +11 -0
- package/dist/sessions/tmux-bridge.d.ts.map +1 -0
- package/dist/sessions/tmux-bridge.js +67 -0
- package/dist/sessions/tmux-bridge.js.map +1 -0
- package/dist/tunnel/tunnel-manager.d.ts +8 -0
- package/dist/tunnel/tunnel-manager.d.ts.map +1 -0
- package/dist/tunnel/tunnel-manager.js +57 -0
- package/dist/tunnel/tunnel-manager.js.map +1 -0
- package/dist/tunnel/websocket-server.d.ts +48 -0
- package/dist/tunnel/websocket-server.d.ts.map +1 -0
- package/dist/tunnel/websocket-server.js +173 -0
- package/dist/tunnel/websocket-server.js.map +1 -0
- package/dist/types.d.ts +37 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/config.d.ts +23 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +54 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +11 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/qr.d.ts +19 -0
- package/dist/utils/qr.d.ts.map +1 -0
- package/dist/utils/qr.js +17 -0
- package/dist/utils/qr.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { logger } from '../utils/logger.js';
|
|
2
|
+
import { unregisterHooks } from '../hooks/hook-manager.js';
|
|
3
|
+
/**
|
|
4
|
+
* Removes all AgentVigil hook entries from ~/.claude/settings.json.
|
|
5
|
+
* Only removes commands registered by AgentVigil — all other hooks
|
|
6
|
+
* (including other tools' hooks) are left completely untouched.
|
|
7
|
+
*/
|
|
8
|
+
export async function runUninstall() {
|
|
9
|
+
logger.info('Removing AgentVigil hooks from ~/.claude/settings.json...');
|
|
10
|
+
await unregisterHooks();
|
|
11
|
+
logger.info('Verify with: cat ~/.claude/settings.json | grep -i agentvigil');
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=uninstall.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uninstall.js","sourceRoot":"","sources":["../../src/commands/uninstall.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE3D;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAC;IACzE,MAAM,eAAe,EAAE,CAAC;IACxB,MAAM,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;AAC/E,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface KeyPair {
|
|
2
|
+
publicKey: string;
|
|
3
|
+
secretKey: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function generateKeyPair(): KeyPair;
|
|
6
|
+
/** Loads the persisted X25519 keypair from ~/.agentvigil/keys.json, generating and saving one on first run. */
|
|
7
|
+
export declare function loadOrCreateKeyPair(): Promise<KeyPair>;
|
|
8
|
+
export declare function deriveSharedSecret(ourSecretKey: string, theirPublicKey: string): string;
|
|
9
|
+
export declare function encrypt(plaintext: string, sharedSecret: string): string;
|
|
10
|
+
export declare function decrypt(ciphertext: string, sharedSecret: string): string;
|
|
11
|
+
//# sourceMappingURL=encryption.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../../src/crypto/encryption.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,OAAO;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAOD,wBAAgB,eAAe,IAAI,OAAO,CAMzC;AAED,+GAA+G;AAC/G,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAe5D;AAED,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAMvF;AAED,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAavE;AAED,wBAAgB,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAWxE"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import nacl from 'tweetnacl';
|
|
5
|
+
import { logger } from '../utils/logger.js';
|
|
6
|
+
// Use Node's built-in Buffer for base64 — avoids tweetnacl-util ESM compat issues on Node 26.
|
|
7
|
+
const encodeBase64 = (bytes) => Buffer.from(bytes).toString('base64');
|
|
8
|
+
const decodeBase64 = (str) => new Uint8Array(Buffer.from(str, 'base64'));
|
|
9
|
+
const KEYS_PATH = path.join(os.homedir(), '.agentvigil', 'keys.json');
|
|
10
|
+
export function generateKeyPair() {
|
|
11
|
+
const keyPair = nacl.box.keyPair();
|
|
12
|
+
return {
|
|
13
|
+
publicKey: encodeBase64(keyPair.publicKey),
|
|
14
|
+
secretKey: encodeBase64(keyPair.secretKey),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** Loads the persisted X25519 keypair from ~/.agentvigil/keys.json, generating and saving one on first run. */
|
|
18
|
+
export async function loadOrCreateKeyPair() {
|
|
19
|
+
try {
|
|
20
|
+
const raw = await fs.readFile(KEYS_PATH, 'utf8');
|
|
21
|
+
const stored = JSON.parse(raw);
|
|
22
|
+
logger.info('Loaded existing keypair from keys.json');
|
|
23
|
+
return { publicKey: stored.public_key, secretKey: stored.secret_key };
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
const keyPair = generateKeyPair();
|
|
27
|
+
const store = { public_key: keyPair.publicKey, secret_key: keyPair.secretKey };
|
|
28
|
+
await fs.mkdir(path.dirname(KEYS_PATH), { recursive: true });
|
|
29
|
+
await fs.writeFile(KEYS_PATH, JSON.stringify(store, null, 2), { mode: 0o600 });
|
|
30
|
+
await fs.chmod(KEYS_PATH, 0o600);
|
|
31
|
+
logger.info('Generated new keypair (first run)');
|
|
32
|
+
return keyPair;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function deriveSharedSecret(ourSecretKey, theirPublicKey) {
|
|
36
|
+
const sharedSecret = nacl.box.before(decodeBase64(theirPublicKey), decodeBase64(ourSecretKey));
|
|
37
|
+
return encodeBase64(sharedSecret);
|
|
38
|
+
}
|
|
39
|
+
export function encrypt(plaintext, sharedSecret) {
|
|
40
|
+
const nonce = nacl.randomBytes(nacl.box.nonceLength);
|
|
41
|
+
const messageUint8 = new TextEncoder().encode(plaintext);
|
|
42
|
+
const encrypted = nacl.box.after(messageUint8, nonce, decodeBase64(sharedSecret));
|
|
43
|
+
const combined = new Uint8Array(nonce.length + encrypted.length);
|
|
44
|
+
combined.set(nonce);
|
|
45
|
+
combined.set(encrypted, nonce.length);
|
|
46
|
+
return encodeBase64(combined);
|
|
47
|
+
}
|
|
48
|
+
export function decrypt(ciphertext, sharedSecret) {
|
|
49
|
+
const combined = decodeBase64(ciphertext);
|
|
50
|
+
const nonce = combined.slice(0, nacl.box.nonceLength);
|
|
51
|
+
const encrypted = combined.slice(nacl.box.nonceLength);
|
|
52
|
+
const decrypted = nacl.box.open.after(encrypted, nonce, decodeBase64(sharedSecret));
|
|
53
|
+
if (!decrypted)
|
|
54
|
+
throw new Error('Decryption failed — wrong key or tampered message');
|
|
55
|
+
return new TextDecoder().decode(decrypted);
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=encryption.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryption.js","sourceRoot":"","sources":["../../src/crypto/encryption.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AgentEvent, HookPayload } from '../types.js';
|
|
2
|
+
export declare function handleHook(eventType: string): Promise<void>;
|
|
3
|
+
export declare function parseHookPayload(raw: string): HookPayload;
|
|
4
|
+
export declare function buildAgentEvent(eventType: string, payload: HookPayload): AgentEvent;
|
|
5
|
+
export interface PermissionDetails {
|
|
6
|
+
toolName: string;
|
|
7
|
+
toolInput: string;
|
|
8
|
+
fullText: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Reads the JSONL transcript and returns the tool name/input behind the most
|
|
12
|
+
* recent permission prompt, formatted as the human-readable command text
|
|
13
|
+
* shown on the phone (e.g. "$ rm -rf node_modules" or "Write file: foo.ts").
|
|
14
|
+
* Falls back to FALLBACK_PERMISSION_DETAILS if the transcript can't be read
|
|
15
|
+
* or contains no tool_use entry.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getPermissionDetails(transcriptPath: string): Promise<PermissionDetails>;
|
|
18
|
+
//# sourceMappingURL=hook-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-handler.d.ts","sourceRoot":"","sources":["../../src/hooks/hook-handler.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAkB,WAAW,EAAE,MAAM,aAAa,CAAC;AA4B3E,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0DjE;AA2CD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAUzD;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,UAAU,CAoBnF;AAMD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAQD;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA2B7F"}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { WebSocket } from 'ws';
|
|
5
|
+
import { logger } from '../utils/logger.js';
|
|
6
|
+
import { getConfig } from '../utils/config.js';
|
|
7
|
+
import { loadOrCreateKeyPair } from '../crypto/encryption.js';
|
|
8
|
+
import { RelayHandler } from '../relay/relay-handler.js';
|
|
9
|
+
import { isBlocklisted } from '../sessions/session-watcher.js';
|
|
10
|
+
const LOG_FILE = path.join(os.homedir(), '.agentvigil', 'hooks.log');
|
|
11
|
+
async function logToFile(type, payload) {
|
|
12
|
+
const line = `${new Date().toISOString()} [${type}] ${JSON.stringify(payload)}\n`;
|
|
13
|
+
await fs.appendFile(LOG_FILE, line).catch(() => { });
|
|
14
|
+
}
|
|
15
|
+
const DAEMON_FORWARD_TIMEOUT_MS = 1500;
|
|
16
|
+
const HOOK_TYPES = ['permission_prompt', 'idle_prompt', 'stop', 'subagent_stop'];
|
|
17
|
+
const DEFAULT_MESSAGE = {
|
|
18
|
+
permission_prompt: 'Permission required',
|
|
19
|
+
idle_prompt: 'Session idle, waiting for input',
|
|
20
|
+
stop: 'Session closed',
|
|
21
|
+
subagent_stop: 'Sub-agent completed',
|
|
22
|
+
};
|
|
23
|
+
const AGENT_EVENT_TYPE = {
|
|
24
|
+
permission_prompt: 'permission_prompt',
|
|
25
|
+
idle_prompt: 'idle_waiting',
|
|
26
|
+
stop: 'session_ended',
|
|
27
|
+
subagent_stop: 'task_complete',
|
|
28
|
+
};
|
|
29
|
+
export async function handleHook(eventType) {
|
|
30
|
+
// Log to file FIRST — before any other logic — so we can confirm hooks
|
|
31
|
+
// are actually firing even if downstream code fails.
|
|
32
|
+
const raw = await readStdin();
|
|
33
|
+
await logToFile(eventType, raw);
|
|
34
|
+
try {
|
|
35
|
+
const payload = parseHookPayload(raw);
|
|
36
|
+
if (isBlocklisted(payload.cwd)) {
|
|
37
|
+
logger.dim(`[${path.basename(payload.cwd)}] Ignoring hook from blocklisted project`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// Sub-agent completions are an internal implementation detail — only the
|
|
41
|
+
// top-level Stop hook represents the session actually finishing, so
|
|
42
|
+
// SubagentStop must never reach the session store, the daemon, or ntfy.
|
|
43
|
+
if (payload.hook_event_name === 'SubagentStop') {
|
|
44
|
+
logger.dim('Ignoring SubagentStop — sub-agent completions never notify the phone');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const event = buildAgentEvent(eventType, payload);
|
|
48
|
+
if (eventType === 'permission_prompt') {
|
|
49
|
+
const details = await getPermissionDetails(payload.transcript_path);
|
|
50
|
+
if (details !== FALLBACK_PERMISSION_DETAILS) {
|
|
51
|
+
event.message = details.fullText;
|
|
52
|
+
event.permission_command = details.fullText;
|
|
53
|
+
event.tool_name = details.toolName;
|
|
54
|
+
event.tool_input = details.toolInput;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
logger.info(`[${event.project_name}] ${event.type}: ${event.message}`);
|
|
58
|
+
const config = await getConfig();
|
|
59
|
+
const keyPair = await loadOrCreateKeyPair();
|
|
60
|
+
// Forward the event in-process to the long-running daemon (`agentvigil
|
|
61
|
+
// start`), which holds the live phone WebSocket connection and can relay
|
|
62
|
+
// it immediately.
|
|
63
|
+
const forwarded = await forwardToDaemon(event, config.ws_port, keyPair.secretKey);
|
|
64
|
+
if (!forwarded) {
|
|
65
|
+
// Daemon isn't running — this short-lived CLI process is the only
|
|
66
|
+
// chance this event gets to reach ntfy, so push it directly. When the
|
|
67
|
+
// daemon IS reachable it relays the forwarded event itself, so doing
|
|
68
|
+
// it here too would double-send every notification.
|
|
69
|
+
const relay = new RelayHandler({ sendEvent: () => { }, isPhoneConnected: false, getSharedSecret: () => undefined }, config.ntfy_topic);
|
|
70
|
+
await relay.handleAgentEvent(event);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
logger.error('Failed to handle hook event', err);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Sends the event to the daemon's local WebSocket server, authenticated with
|
|
79
|
+
* the Mac's own secret key (never sent to the phone). Resolves `true` only if
|
|
80
|
+
* the message was actually handed to the daemon; `false` if the daemon isn't
|
|
81
|
+
* running (or didn't respond in time), so the caller can fall back to its own
|
|
82
|
+
* ntfy push.
|
|
83
|
+
*/
|
|
84
|
+
function forwardToDaemon(event, wsPort, localToken) {
|
|
85
|
+
return new Promise((resolve) => {
|
|
86
|
+
let settled = false;
|
|
87
|
+
const finish = (delivered) => {
|
|
88
|
+
if (settled)
|
|
89
|
+
return;
|
|
90
|
+
settled = true;
|
|
91
|
+
clearTimeout(timer);
|
|
92
|
+
resolve(delivered);
|
|
93
|
+
};
|
|
94
|
+
let socket;
|
|
95
|
+
try {
|
|
96
|
+
socket = new WebSocket(`ws://127.0.0.1:${wsPort}/hook`);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
resolve(false);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const timer = setTimeout(() => {
|
|
103
|
+
socket.terminate();
|
|
104
|
+
finish(false);
|
|
105
|
+
}, DAEMON_FORWARD_TIMEOUT_MS);
|
|
106
|
+
let delivered = false;
|
|
107
|
+
socket.on('open', () => {
|
|
108
|
+
socket.send(JSON.stringify({ type: 'hook_event', token: localToken, event }));
|
|
109
|
+
delivered = true;
|
|
110
|
+
socket.close();
|
|
111
|
+
});
|
|
112
|
+
socket.on('close', () => finish(delivered));
|
|
113
|
+
socket.on('error', () => finish(false));
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
export function parseHookPayload(raw) {
|
|
117
|
+
const parsed = JSON.parse(raw || '{}');
|
|
118
|
+
return {
|
|
119
|
+
session_id: parsed.session_id ?? 'unknown',
|
|
120
|
+
transcript_path: parsed.transcript_path ?? '',
|
|
121
|
+
cwd: parsed.cwd ?? process.cwd(),
|
|
122
|
+
hook_event_name: parsed.hook_event_name ?? '',
|
|
123
|
+
notification_type: parsed.notification_type,
|
|
124
|
+
message: parsed.message,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
export function buildAgentEvent(eventType, payload) {
|
|
128
|
+
if (!isHookType(eventType)) {
|
|
129
|
+
throw new Error(`Unknown hook type: ${eventType}`);
|
|
130
|
+
}
|
|
131
|
+
const event = {
|
|
132
|
+
type: AGENT_EVENT_TYPE[eventType],
|
|
133
|
+
session_id: payload.session_id,
|
|
134
|
+
project_name: path.basename(payload.cwd),
|
|
135
|
+
cwd: payload.cwd,
|
|
136
|
+
agent: 'claude-code',
|
|
137
|
+
message: payload.message ?? DEFAULT_MESSAGE[eventType],
|
|
138
|
+
timestamp: new Date().toISOString(),
|
|
139
|
+
};
|
|
140
|
+
if (eventType === 'permission_prompt') {
|
|
141
|
+
event.permission_command = payload.message;
|
|
142
|
+
}
|
|
143
|
+
return event;
|
|
144
|
+
}
|
|
145
|
+
function isHookType(value) {
|
|
146
|
+
return HOOK_TYPES.includes(value);
|
|
147
|
+
}
|
|
148
|
+
const FALLBACK_PERMISSION_DETAILS = {
|
|
149
|
+
toolName: 'Unknown',
|
|
150
|
+
toolInput: '',
|
|
151
|
+
fullText: 'Claude needs your permission',
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* Reads the JSONL transcript and returns the tool name/input behind the most
|
|
155
|
+
* recent permission prompt, formatted as the human-readable command text
|
|
156
|
+
* shown on the phone (e.g. "$ rm -rf node_modules" or "Write file: foo.ts").
|
|
157
|
+
* Falls back to FALLBACK_PERMISSION_DETAILS if the transcript can't be read
|
|
158
|
+
* or contains no tool_use entry.
|
|
159
|
+
*/
|
|
160
|
+
export async function getPermissionDetails(transcriptPath) {
|
|
161
|
+
if (!transcriptPath)
|
|
162
|
+
return FALLBACK_PERMISSION_DETAILS;
|
|
163
|
+
try {
|
|
164
|
+
const content = await fs.readFile(transcriptPath, 'utf-8');
|
|
165
|
+
const lines = content.trim().split('\n').filter((l) => l.trim());
|
|
166
|
+
for (const line of [...lines].reverse()) {
|
|
167
|
+
try {
|
|
168
|
+
const entry = JSON.parse(line);
|
|
169
|
+
const toolUse = extractToolUse(entry);
|
|
170
|
+
if (toolUse) {
|
|
171
|
+
return {
|
|
172
|
+
toolName: toolUse.name,
|
|
173
|
+
toolInput: JSON.stringify(toolUse.input),
|
|
174
|
+
fullText: formatPermissionText(toolUse.name, toolUse.input),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// Transcript missing or unreadable — fall through to the default.
|
|
185
|
+
}
|
|
186
|
+
return FALLBACK_PERMISSION_DETAILS;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Finds the most recent tool_use block in a transcript line, supporting both
|
|
190
|
+
* the raw `{ tool_name, tool_input }` shape and Claude Code's nested
|
|
191
|
+
* `{ message: { content: [{ type: 'tool_use', name, input }] } }` shape.
|
|
192
|
+
*/
|
|
193
|
+
function extractToolUse(entry) {
|
|
194
|
+
if (entry?.type === 'tool_use' || entry?.tool_name) {
|
|
195
|
+
return {
|
|
196
|
+
name: entry.tool_name ?? entry.name ?? 'Unknown',
|
|
197
|
+
input: entry.tool_input ?? entry.input ?? {},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
const blocks = entry?.message?.content;
|
|
201
|
+
if (Array.isArray(blocks)) {
|
|
202
|
+
for (let i = blocks.length - 1; i >= 0; i--) {
|
|
203
|
+
const block = blocks[i];
|
|
204
|
+
if (block?.type === 'tool_use') {
|
|
205
|
+
return { name: block.name ?? 'Unknown', input: block.input ?? {} };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return undefined;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Formats the tool_use block as the question the terminal shows the user,
|
|
213
|
+
* e.g. "Do you want to create test.txt?" or "$ rm -rf node_modules".
|
|
214
|
+
* Claude Code does not persist this question text anywhere in the
|
|
215
|
+
* transcript — it's rendered client-side from the tool_use block — so it's
|
|
216
|
+
* reconstructed here from the tool name + input.
|
|
217
|
+
*/
|
|
218
|
+
function formatPermissionText(toolName, input) {
|
|
219
|
+
switch (toolName) {
|
|
220
|
+
case 'Bash':
|
|
221
|
+
return `$ ${input.command ?? ''}`;
|
|
222
|
+
case 'Write':
|
|
223
|
+
return `Do you want to create ${fileNameOf(input)}?`;
|
|
224
|
+
case 'Edit':
|
|
225
|
+
case 'MultiEdit':
|
|
226
|
+
return `Do you want to edit ${fileNameOf(input)}?`;
|
|
227
|
+
case 'Read':
|
|
228
|
+
return `Read ${fileNameOf(input)}`;
|
|
229
|
+
case 'WebFetch':
|
|
230
|
+
return `Fetch URL: ${input.url ?? ''}`;
|
|
231
|
+
case 'TodoWrite':
|
|
232
|
+
return 'Update todo list';
|
|
233
|
+
default:
|
|
234
|
+
return `${toolName}: ${keyDetailOf(input)}`;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/** Extracts just the filename (e.g. "test.txt") from a tool_use input's path. */
|
|
238
|
+
function fileNameOf(input) {
|
|
239
|
+
const filePath = (input.file_path ?? input.path);
|
|
240
|
+
return filePath ? path.basename(filePath) : '';
|
|
241
|
+
}
|
|
242
|
+
/** Picks the single most relevant field from a tool_use input for the default case. */
|
|
243
|
+
function keyDetailOf(input) {
|
|
244
|
+
const value = (input.file_path ?? input.path ?? input.pattern ?? input.command ?? input.url ?? input.query ?? input.description);
|
|
245
|
+
return value ?? JSON.stringify(input).substring(0, 100);
|
|
246
|
+
}
|
|
247
|
+
async function readStdin() {
|
|
248
|
+
return new Promise((resolve) => {
|
|
249
|
+
let data = '';
|
|
250
|
+
process.stdin.setEncoding('utf8');
|
|
251
|
+
process.stdin.on('data', (chunk) => (data += chunk));
|
|
252
|
+
process.stdin.on('end', () => resolve(data));
|
|
253
|
+
setTimeout(() => resolve(data || '{}'), 2000);
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=hook-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-handler.js","sourceRoot":"","sources":["../../src/hooks/hook-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Builds the shell command for a given hook type with full absolute paths. */
|
|
2
|
+
export declare const buildHookCommand: (type: string) => string;
|
|
3
|
+
interface HookCommand {
|
|
4
|
+
type: 'command';
|
|
5
|
+
command: string;
|
|
6
|
+
}
|
|
7
|
+
interface HookEntry {
|
|
8
|
+
matcher?: string;
|
|
9
|
+
hooks: HookCommand[];
|
|
10
|
+
}
|
|
11
|
+
type HookConfig = Record<string, HookEntry[]>;
|
|
12
|
+
export declare function buildHookConfig(): HookConfig;
|
|
13
|
+
/**
|
|
14
|
+
* Merge our hook config into the existing one without dropping or duplicating
|
|
15
|
+
* anything. An entry is considered "ours already" when an existing entry with
|
|
16
|
+
* the same matcher already runs a command that looks like one of ours.
|
|
17
|
+
*/
|
|
18
|
+
export declare function mergeHooks(existing: HookConfig, ours: HookConfig): HookConfig;
|
|
19
|
+
export declare function registerHooks(): Promise<void>;
|
|
20
|
+
export declare function unregisterHooks(): Promise<void>;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=hook-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-manager.d.ts","sourceRoot":"","sources":["../../src/hooks/hook-manager.ts"],"names":[],"mappings":"AAkBA,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,KAAG,MACA,CAAC;AAcjD,UAAU,WAAW;IACnB,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,SAAS;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;AAS9C,wBAAgB,eAAe,IAAI,UAAU,CAmB5C;AAID;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,GAAG,UAAU,CA+B7E;AAmCD,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAKnD;AAED,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAqBrD"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { fileURLToPath } from 'url';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { logger } from '../utils/logger.js';
|
|
6
|
+
// ── Absolute-path hook command ─────────────────────────────────────────────
|
|
7
|
+
// Claude Code fires hooks in a restricted shell where the npm global bin
|
|
8
|
+
// directory is NOT in PATH, so bare `agentvigil hook <type>` silently fails.
|
|
9
|
+
// Using process.execPath + the absolute path to dist/index.js works on any
|
|
10
|
+
// machine regardless of how Node or the package were installed.
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
// dist/hooks/hook-manager.js → go up one level to reach dist/
|
|
13
|
+
const distDir = path.dirname(path.dirname(__filename));
|
|
14
|
+
const entryPoint = path.join(distDir, 'index.js');
|
|
15
|
+
const nodeExecutable = process.execPath;
|
|
16
|
+
/** Builds the shell command for a given hook type with full absolute paths. */
|
|
17
|
+
export const buildHookCommand = (type) => `${nodeExecutable} ${entryPoint} hook ${type}`;
|
|
18
|
+
// ── Settings path ──────────────────────────────────────────────────────────
|
|
19
|
+
const SETTINGS_PATH = path.join(os.homedir(), '.claude', 'settings.json');
|
|
20
|
+
/** Marker present in every OLD-style bare command (pre-absolute-path fix). */
|
|
21
|
+
const LEGACY_MARKER = 'agentvigil hook';
|
|
22
|
+
/** Returns true when a command was registered by AgentVigil (old or new). */
|
|
23
|
+
const isOurCommand = (cmd) => cmd.includes(entryPoint) || cmd.includes(LEGACY_MARKER);
|
|
24
|
+
// ── Hook config builder ────────────────────────────────────────────────────
|
|
25
|
+
export function buildHookConfig() {
|
|
26
|
+
return {
|
|
27
|
+
Notification: [
|
|
28
|
+
{
|
|
29
|
+
matcher: 'permission_prompt',
|
|
30
|
+
hooks: [{ type: 'command', command: buildHookCommand('permission_prompt') }],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
matcher: 'idle_prompt',
|
|
34
|
+
hooks: [{ type: 'command', command: buildHookCommand('idle_prompt') }],
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
Stop: [
|
|
38
|
+
{ hooks: [{ type: 'command', command: buildHookCommand('stop') }] },
|
|
39
|
+
],
|
|
40
|
+
SubagentStop: [
|
|
41
|
+
{ hooks: [{ type: 'command', command: buildHookCommand('subagent_stop') }] },
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// ── Merge ──────────────────────────────────────────────────────────────────
|
|
46
|
+
/**
|
|
47
|
+
* Merge our hook config into the existing one without dropping or duplicating
|
|
48
|
+
* anything. An entry is considered "ours already" when an existing entry with
|
|
49
|
+
* the same matcher already runs a command that looks like one of ours.
|
|
50
|
+
*/
|
|
51
|
+
export function mergeHooks(existing, ours) {
|
|
52
|
+
const merged = {};
|
|
53
|
+
for (const [event, entries] of Object.entries(existing)) {
|
|
54
|
+
merged[event] = [...entries];
|
|
55
|
+
}
|
|
56
|
+
for (const [event, ourEntries] of Object.entries(ours)) {
|
|
57
|
+
const entries = merged[event] ?? (merged[event] = []);
|
|
58
|
+
for (const ourEntry of ourEntries) {
|
|
59
|
+
// Strip any old/new variant of our command before adding the fresh one,
|
|
60
|
+
// so re-running setup upgrades bare commands to absolute-path commands.
|
|
61
|
+
for (const existingEntry of entries) {
|
|
62
|
+
if (existingEntry.matcher === ourEntry.matcher) {
|
|
63
|
+
existingEntry.hooks = existingEntry.hooks.filter((h) => !isOurCommand(h.command));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Remove entries that are now empty after stripping our old commands.
|
|
67
|
+
const emptyIdx = entries.findIndex((e) => e.matcher === ourEntry.matcher && e.hooks.length === 0);
|
|
68
|
+
if (emptyIdx !== -1)
|
|
69
|
+
entries.splice(emptyIdx, 1);
|
|
70
|
+
// Add the fresh entry (always — we removed any pre-existing one above).
|
|
71
|
+
entries.push(ourEntry);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return merged;
|
|
75
|
+
}
|
|
76
|
+
// ── File helpers ───────────────────────────────────────────────────────────
|
|
77
|
+
async function fileExists(p) {
|
|
78
|
+
try {
|
|
79
|
+
await fs.access(p);
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async function readSettings() {
|
|
87
|
+
try {
|
|
88
|
+
const raw = await fs.readFile(SETTINGS_PATH, 'utf8');
|
|
89
|
+
return JSON.parse(raw);
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
if (err.code === 'ENOENT')
|
|
93
|
+
return {};
|
|
94
|
+
logger.warn('~/.claude/settings.json is malformed — backing it up and starting fresh');
|
|
95
|
+
await fs.copyFile(SETTINGS_PATH, `${SETTINGS_PATH}.bak`).catch(() => { });
|
|
96
|
+
return {};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function writeSettings(settings) {
|
|
100
|
+
await fs.mkdir(path.dirname(SETTINGS_PATH), { recursive: true });
|
|
101
|
+
if (await fileExists(SETTINGS_PATH)) {
|
|
102
|
+
await fs.copyFile(SETTINGS_PATH, `${SETTINGS_PATH}.bak`);
|
|
103
|
+
}
|
|
104
|
+
await fs.writeFile(SETTINGS_PATH, JSON.stringify(settings, null, 2), 'utf8');
|
|
105
|
+
}
|
|
106
|
+
// ── Public API ─────────────────────────────────────────────────────────────
|
|
107
|
+
export async function registerHooks() {
|
|
108
|
+
const existing = await readSettings();
|
|
109
|
+
existing.hooks = mergeHooks(existing.hooks ?? {}, buildHookConfig());
|
|
110
|
+
await writeSettings(existing);
|
|
111
|
+
logger.success('Hooks registered in ~/.claude/settings.json');
|
|
112
|
+
}
|
|
113
|
+
export async function unregisterHooks() {
|
|
114
|
+
const existing = await readSettings();
|
|
115
|
+
if (!existing.hooks)
|
|
116
|
+
return;
|
|
117
|
+
const cleaned = {};
|
|
118
|
+
for (const [event, entries] of Object.entries(existing.hooks)) {
|
|
119
|
+
const keptEntries = entries
|
|
120
|
+
.map((entry) => ({
|
|
121
|
+
...entry,
|
|
122
|
+
hooks: entry.hooks.filter((h) => !isOurCommand(h.command)),
|
|
123
|
+
}))
|
|
124
|
+
.filter((entry) => entry.hooks.length > 0);
|
|
125
|
+
if (keptEntries.length > 0) {
|
|
126
|
+
cleaned[event] = keptEntries;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
existing.hooks = cleaned;
|
|
130
|
+
await writeSettings(existing);
|
|
131
|
+
logger.success('Hooks removed from ~/.claude/settings.json');
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=hook-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-manager.js","sourceRoot":"","sources":["../../src/hooks/hook-manager.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|