@cybedefend/vibedefend 1.1.1

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 (103) hide show
  1. package/LICENSE +77 -0
  2. package/README.md +120 -0
  3. package/bin/vibedefend.js +19 -0
  4. package/dist/auth/auth-store.js +170 -0
  5. package/dist/auth/auth-store.js.map +1 -0
  6. package/dist/auth/auth.js +125 -0
  7. package/dist/auth/auth.js.map +1 -0
  8. package/dist/auth/callback-server.js +216 -0
  9. package/dist/auth/callback-server.js.map +1 -0
  10. package/dist/auth/pkce.js +31 -0
  11. package/dist/auth/pkce.js.map +1 -0
  12. package/dist/auth/token-exchange.js +83 -0
  13. package/dist/auth/token-exchange.js.map +1 -0
  14. package/dist/clients/claude-code.js +170 -0
  15. package/dist/clients/claude-code.js.map +1 -0
  16. package/dist/clients/codex.js +378 -0
  17. package/dist/clients/codex.js.map +1 -0
  18. package/dist/clients/cursor-guards-rules.js +94 -0
  19. package/dist/clients/cursor-guards-rules.js.map +1 -0
  20. package/dist/clients/cursor.js +172 -0
  21. package/dist/clients/cursor.js.map +1 -0
  22. package/dist/clients/detect.js +86 -0
  23. package/dist/clients/detect.js.map +1 -0
  24. package/dist/clients/registry.js +41 -0
  25. package/dist/clients/registry.js.map +1 -0
  26. package/dist/clients/types.js +2 -0
  27. package/dist/clients/types.js.map +1 -0
  28. package/dist/clients/vscode.js +187 -0
  29. package/dist/clients/vscode.js.map +1 -0
  30. package/dist/clients/windsurf.js +151 -0
  31. package/dist/clients/windsurf.js.map +1 -0
  32. package/dist/config.js +32 -0
  33. package/dist/config.js.map +1 -0
  34. package/dist/custom-regions.js +112 -0
  35. package/dist/custom-regions.js.map +1 -0
  36. package/dist/diagnostics.js +122 -0
  37. package/dist/diagnostics.js.map +1 -0
  38. package/dist/doctor.js +125 -0
  39. package/dist/doctor.js.map +1 -0
  40. package/dist/guards-evaluator/bucketing.js +83 -0
  41. package/dist/guards-evaluator/bucketing.js.map +1 -0
  42. package/dist/guards-evaluator/evaluate.js +272 -0
  43. package/dist/guards-evaluator/evaluate.js.map +1 -0
  44. package/dist/guards-evaluator/glob.js +148 -0
  45. package/dist/guards-evaluator/glob.js.map +1 -0
  46. package/dist/guards-evaluator/index.js +9 -0
  47. package/dist/guards-evaluator/index.js.map +1 -0
  48. package/dist/guards-evaluator/preprocess.js +174 -0
  49. package/dist/guards-evaluator/preprocess.js.map +1 -0
  50. package/dist/guards-evaluator/redact.js +111 -0
  51. package/dist/guards-evaluator/redact.js.map +1 -0
  52. package/dist/guards-evaluator/regex.js +125 -0
  53. package/dist/guards-evaluator/regex.js.map +1 -0
  54. package/dist/guards-evaluator/types.js +2 -0
  55. package/dist/guards-evaluator/types.js.map +1 -0
  56. package/dist/guards-evaluator/validation.js +115 -0
  57. package/dist/guards-evaluator/validation.js.map +1 -0
  58. package/dist/hook-runner.js +6680 -0
  59. package/dist/hooks/install.js +169 -0
  60. package/dist/hooks/install.js.map +1 -0
  61. package/dist/hooks/runtime/api.js +167 -0
  62. package/dist/hooks/runtime/api.js.map +1 -0
  63. package/dist/hooks/runtime/config.js +60 -0
  64. package/dist/hooks/runtime/config.js.map +1 -0
  65. package/dist/hooks/runtime/emit.js +45 -0
  66. package/dist/hooks/runtime/emit.js.map +1 -0
  67. package/dist/hooks/runtime/fetch-rules.js +154 -0
  68. package/dist/hooks/runtime/fetch-rules.js.map +1 -0
  69. package/dist/hooks/runtime/guard-rules-cache.js +217 -0
  70. package/dist/hooks/runtime/guard-rules-cache.js.map +1 -0
  71. package/dist/hooks/runtime/guard-violations-buffer.js +105 -0
  72. package/dist/hooks/runtime/guard-violations-buffer.js.map +1 -0
  73. package/dist/hooks/runtime/pre-compact.js +41 -0
  74. package/dist/hooks/runtime/pre-compact.js.map +1 -0
  75. package/dist/hooks/runtime/resolve.js +206 -0
  76. package/dist/hooks/runtime/resolve.js.map +1 -0
  77. package/dist/hooks/runtime/session-review.js +198 -0
  78. package/dist/hooks/runtime/session-review.js.map +1 -0
  79. package/dist/hooks/runtime/session-start.js +101 -0
  80. package/dist/hooks/runtime/session-start.js.map +1 -0
  81. package/dist/hooks/runtime/sniff.js +112 -0
  82. package/dist/hooks/runtime/sniff.js.map +1 -0
  83. package/dist/hooks/runtime/types.js +22 -0
  84. package/dist/hooks/runtime/types.js.map +1 -0
  85. package/dist/hooks/runtime/user-prompt-submit.js +154 -0
  86. package/dist/hooks/runtime/user-prompt-submit.js.map +1 -0
  87. package/dist/index.js +129 -0
  88. package/dist/index.js.map +1 -0
  89. package/dist/install.js +183 -0
  90. package/dist/install.js.map +1 -0
  91. package/dist/login.js +335 -0
  92. package/dist/login.js.map +1 -0
  93. package/dist/prompts.js +134 -0
  94. package/dist/prompts.js.map +1 -0
  95. package/dist/self-update.js +177 -0
  96. package/dist/self-update.js.map +1 -0
  97. package/dist/status.js +58 -0
  98. package/dist/status.js.map +1 -0
  99. package/dist/utils.js +84 -0
  100. package/dist/utils.js.map +1 -0
  101. package/dist/version.js +23 -0
  102. package/dist/version.js.map +1 -0
  103. package/package.json +73 -0
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Local HTTP callback server for the OAuth 2.0 Authorization Code flow.
3
+ *
4
+ * Binds to 127.0.0.1 (localhost only, never 0.0.0.0) on a random unprivileged
5
+ * port (port 0 → OS assigns a free port). After a successful callback the
6
+ * server is closed immediately.
7
+ *
8
+ * Security:
9
+ * - Binds to 127.0.0.1 only (not reachable from the network).
10
+ * - Validates the `state` parameter on every callback to prevent CSRF.
11
+ * - Closes as soon as the callback is received (no lingering listener).
12
+ */
13
+ import { createServer } from 'node:http';
14
+ /**
15
+ * Start a one-shot local HTTP server on a random port. Returns the port,
16
+ * a promise that resolves with the OAuth code, and a stop function.
17
+ *
18
+ * @param expectedState The CSRF state value the caller generated. Every
19
+ * incoming callback is validated against this; mismatches are rejected.
20
+ */
21
+ /**
22
+ * Default port for production. Matches the loopback redirect URIs registered
23
+ * on the Logto "CybeDefend CLI" Native application
24
+ * (http://localhost:9877/callback and http://127.0.0.1:9877/callback).
25
+ * Tests override this with port 0 to avoid sequential-test collisions.
26
+ */
27
+ export const DEFAULT_CALLBACK_PORT = 9877;
28
+ export async function startCallbackServer(expectedState, options = {}) {
29
+ let resolveCode;
30
+ let rejectCode;
31
+ const codePromise = new Promise((res, rej) => {
32
+ resolveCode = res;
33
+ rejectCode = rej;
34
+ });
35
+ const server = createServer((req, res) => {
36
+ const url = new URL(req.url ?? '/', 'http://localhost');
37
+ if (url.pathname !== '/callback') {
38
+ res.writeHead(404).end();
39
+ return;
40
+ }
41
+ const code = url.searchParams.get('code');
42
+ const state = url.searchParams.get('state');
43
+ const error = url.searchParams.get('error');
44
+ const errorDescription = url.searchParams.get('error_description') ?? '';
45
+ if (error) {
46
+ res.writeHead(400, { 'Content-Type': 'text/html' });
47
+ res.end(prettyErrorPage(error, errorDescription));
48
+ rejectCode(new Error(`OAuth error: ${error}${errorDescription ? ` — ${errorDescription}` : ''}`));
49
+ return;
50
+ }
51
+ if (!code || state !== expectedState) {
52
+ const reason = !code ? 'missing authorization code' : 'state mismatch';
53
+ res.writeHead(400, { 'Content-Type': 'text/html' });
54
+ res.end(prettyErrorPage('invalid_request', reason));
55
+ rejectCode(new Error(`Invalid OAuth callback: ${reason}.`));
56
+ return;
57
+ }
58
+ res.writeHead(200, { 'Content-Type': 'text/html' });
59
+ res.end(prettySuccessPage());
60
+ resolveCode(code);
61
+ });
62
+ // Bind to localhost on the chosen port. Production uses DEFAULT_CALLBACK_PORT
63
+ // (9877) because Logto enforces strict redirect_uri matching against the URIs
64
+ // registered for the "CybeDefend CLI" Native application
65
+ // (http://localhost:9877/callback and http://127.0.0.1:9877/callback). RFC
66
+ // 8252 recommends random loopback ports, but Logto does not honor wildcards.
67
+ // Tests pass port=0 to let the OS assign a free port — required to avoid
68
+ // EADDRINUSE collisions across sequentially-run test cases.
69
+ const requestedPort = options.port ?? DEFAULT_CALLBACK_PORT;
70
+ await new Promise((resolve, reject) => {
71
+ server.once('error', (err) => {
72
+ if (err.code === 'EADDRINUSE') {
73
+ reject(new Error(`Port ${requestedPort} is already in use. Another vibedefend login flow may be running, or another process is bound to this port. ` +
74
+ `Kill the other process (lsof -i :${requestedPort}) and try again.`));
75
+ }
76
+ else {
77
+ reject(err);
78
+ }
79
+ });
80
+ server.listen(requestedPort, '127.0.0.1', resolve);
81
+ });
82
+ const addr = server.address();
83
+ if (!addr)
84
+ throw new Error('Failed to bind callback server.');
85
+ // When the caller passed port=0 the OS assigned a free port; reflect the
86
+ // ACTUAL bound port back to the caller so they can build the redirect_uri.
87
+ const port = addr.port;
88
+ const stopServer = () => new Promise((resolve) => {
89
+ // server.close() alone only refuses NEW connections — existing keep-alive
90
+ // connections (the browser's callback request) remain in the event loop
91
+ // until they idle out, which hangs `vibedefend login` for ~5 s after
92
+ // success. closeAllConnections() forcefully closes those so the process
93
+ // can exit immediately. Available since Node 18.2; we require >=18.17.
94
+ server.closeAllConnections?.();
95
+ server.close(() => resolve());
96
+ });
97
+ return { port, codePromise, stopServer };
98
+ }
99
+ // ─── HTML pages ──────────────────────────────────────────────────────────────
100
+ /**
101
+ * Shared HTML shell for the OAuth callback success / error pages.
102
+ *
103
+ * Mirrors the branded layout used by the Go `cybedefend-cli` (pkg/auth/oauth.go:
104
+ * `callbackPageBase`) so users see the same authentication-complete experience
105
+ * regardless of which CLI initiated the flow. The single shared template keeps
106
+ * the look-and-feel — radial-gradient background, white card, CybeDefend logo,
107
+ * "CybeDefend CLI" tag — consistent across both CLIs.
108
+ *
109
+ * Caller injects `body` as the inner content (icon + title + optional detail).
110
+ * The caller is responsible for HTML-escaping any user-controlled string before
111
+ * embedding it into `body`.
112
+ */
113
+ function renderCallbackPage(body) {
114
+ return `<!DOCTYPE html>
115
+ <html lang="en">
116
+ <head>
117
+ <meta charset="UTF-8" />
118
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
119
+ <title>CybeDefend — Authentication</title>
120
+ <style>
121
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
122
+ body {
123
+ min-height: 100vh;
124
+ display: flex;
125
+ align-items: center;
126
+ justify-content: center;
127
+ background: radial-gradient(ellipse at 60% 10%, #2d1045 0%, #14082a 50%, #0a0414 100%);
128
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
129
+ color: #e8d8ff;
130
+ }
131
+ .card {
132
+ background: #ffffff;
133
+ border: none;
134
+ border-radius: 20px;
135
+ padding: 48px 52px;
136
+ max-width: 480px;
137
+ width: 100%;
138
+ text-align: center;
139
+ box-shadow: 0 8px 48px rgba(0,0,0,0.5);
140
+ }
141
+ h1 { color: #1a0a2e; font-size: 22px; font-weight: 600; margin-bottom: 10px; }
142
+ p { color: rgba(40,20,60,0.6); font-size: 14px; line-height: 1.6; }
143
+ .logo { height: 44px; margin-bottom: 36px; }
144
+ .icon {
145
+ width: 72px; height: 72px;
146
+ border-radius: 50%;
147
+ display: flex; align-items: center; justify-content: center;
148
+ margin: 0 auto 24px;
149
+ font-size: 32px;
150
+ }
151
+ .icon.success { background: #e6f9ee; border: 1px solid #4caf7d; }
152
+ .icon.error { background: rgba(255,80,80,0.08); border: 1px solid rgba(255,80,80,0.25); }
153
+ .detail {
154
+ margin-top: 16px;
155
+ padding: 12px 16px;
156
+ background: rgba(255,80,80,0.06);
157
+ border: 1px solid rgba(255,80,80,0.18);
158
+ border-radius: 10px;
159
+ font-size: 13px;
160
+ color: rgba(200,40,40,0.85);
161
+ word-break: break-word;
162
+ }
163
+ .tag {
164
+ display: inline-block;
165
+ margin-top: 28px;
166
+ padding: 5px 14px;
167
+ border-radius: 99px;
168
+ font-size: 11px;
169
+ letter-spacing: 0.05em;
170
+ text-transform: uppercase;
171
+ border: 1px solid rgba(100,50,180,0.25);
172
+ color: rgba(100,50,180,0.5);
173
+ }
174
+ </style>
175
+ </head>
176
+ <body>
177
+ <div class="card">
178
+ <img class="logo" src="https://eu.cybedefend.com/logo.webp" alt="CybeDefend" />
179
+ ${body}
180
+ <span class="tag">VibeDefend</span>
181
+ </div>
182
+ </body>
183
+ </html>`;
184
+ }
185
+ function prettySuccessPage() {
186
+ const body = `
187
+ <div class="icon success">
188
+ <svg width="34" height="34" viewBox="0 0 24 24" fill="none" stroke="#1a7a40" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
189
+ </div>
190
+ <h1>Authentication successful</h1>
191
+ <p>You're now logged in. You can close this tab and return to your terminal.</p>
192
+ <script>
193
+ // Best-effort auto-close — works in most browsers unless blocked by policy.
194
+ setTimeout(() => { try { window.close(); } catch (e) {} }, 1500);
195
+ </script>`;
196
+ return renderCallbackPage(body);
197
+ }
198
+ function prettyErrorPage(error, description) {
199
+ const safeError = htmlEscape(error || 'Authentication error');
200
+ const safeDesc = htmlEscape(description || '');
201
+ const body = `
202
+ <div class="icon error">✕</div>
203
+ <h1>${safeError}</h1>
204
+ <p>Authentication could not be completed.</p>
205
+ ${safeDesc ? `<div class="detail">${safeDesc}</div>` : ''}`;
206
+ return renderCallbackPage(body);
207
+ }
208
+ function htmlEscape(s) {
209
+ return s
210
+ .replace(/&/g, '&amp;')
211
+ .replace(/</g, '&lt;')
212
+ .replace(/>/g, '&gt;')
213
+ .replace(/"/g, '&quot;')
214
+ .replace(/'/g, '&#39;');
215
+ }
216
+ //# sourceMappingURL=callback-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callback-server.js","sourceRoot":"","sources":["../../src/auth/callback-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * PKCE (Proof Key for Code Exchange) helpers for the OAuth 2.0 Authorization
3
+ * Code flow. Used by `vibedefend login` to authenticate against Logto without
4
+ * a client_secret (Public/Native client model).
5
+ *
6
+ * RFC 7636 — PKCE spec.
7
+ */
8
+ import { createHash, randomBytes } from 'node:crypto';
9
+ /**
10
+ * Generate a cryptographically random, URL-safe base64 string of the given
11
+ * byte length. Used both for the PKCE code_verifier and the state CSRF token.
12
+ *
13
+ * PKCE spec requires 43-128 characters for code_verifier. A 64-byte random
14
+ * value produces an 86-character base64url string (well within the range).
15
+ */
16
+ export function randomBase64url(byteLength) {
17
+ return randomBytes(byteLength).toString('base64url');
18
+ }
19
+ /**
20
+ * Compute the PKCE code_challenge from a code_verifier using the S256 method:
21
+ * BASE64URL(SHA256(ASCII(code_verifier)))
22
+ *
23
+ * The resulting challenge is sent in the authorization request; the verifier
24
+ * is sent in the token exchange. The server re-derives the challenge and
25
+ * verifies they match, proving the entity that started the flow is the same
26
+ * one completing it.
27
+ */
28
+ export function pkceChallenge(verifier) {
29
+ return createHash('sha256').update(verifier).digest('base64url');
30
+ }
31
+ //# sourceMappingURL=pkce.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pkce.js","sourceRoot":"","sources":["../../src/auth/pkce.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtD;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,UAAkB;IAChD,OAAO,WAAW,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACvD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AACnE,CAAC"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * OAuth 2.0 token exchange and refresh helpers.
3
+ *
4
+ * Handles two grant types against Logto's /oidc/token endpoint:
5
+ * - authorization_code (initial login, includes PKCE code_verifier)
6
+ * - refresh_token (transparent refresh before API calls)
7
+ *
8
+ * Both functions accept an optional `fetchImpl` so they can be injected
9
+ * in unit tests without touching globalThis.fetch.
10
+ */
11
+ // ─── Errors ───────────────────────────────────────────────────────────────────
12
+ /**
13
+ * Thrown when the /oidc/token endpoint rejects the refresh_token (e.g. the
14
+ * token was revoked, expired beyond the inactivity window, or the user
15
+ * changed their password). The caller should clear local credentials and
16
+ * ask the user to `vibedefend login` again.
17
+ */
18
+ export class RefreshTokenInvalidError extends Error {
19
+ constructor(msg) {
20
+ super(msg);
21
+ this.name = 'RefreshTokenInvalidError';
22
+ }
23
+ }
24
+ /**
25
+ * Exchange an authorization code + PKCE code_verifier for a full token bundle.
26
+ *
27
+ * Called once at the end of the `vibedefend login` browser flow. The resulting
28
+ * tokens (access, refresh, id) are persisted to the OS keychain / file store.
29
+ */
30
+ export async function exchangeCodeForTokens(params) {
31
+ const f = params.fetchImpl ?? globalThis.fetch;
32
+ const body = new URLSearchParams({
33
+ grant_type: 'authorization_code',
34
+ code: params.code,
35
+ code_verifier: params.codeVerifier,
36
+ client_id: params.clientId,
37
+ redirect_uri: params.redirectUri,
38
+ resource: params.resource,
39
+ });
40
+ const res = await f(`${params.logtoEndpoint}/oidc/token`, {
41
+ method: 'POST',
42
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
43
+ body,
44
+ signal: AbortSignal.timeout(10_000),
45
+ });
46
+ if (!res.ok) {
47
+ const text = await res.text().catch(() => '');
48
+ throw new Error(`Token exchange failed: HTTP ${res.status}${text ? ` — ${text}` : ''}`);
49
+ }
50
+ return res.json();
51
+ }
52
+ /**
53
+ * Exchange a refresh_token for a new access_token (and a rotated
54
+ * refresh_token, if Logto is configured for rolling refresh).
55
+ *
56
+ * Throws `RefreshTokenInvalidError` on 400/401 so the caller can purge
57
+ * local credentials and prompt for re-login.
58
+ */
59
+ export async function refreshAccessToken(params) {
60
+ const f = params.fetchImpl ?? globalThis.fetch;
61
+ const body = new URLSearchParams({
62
+ grant_type: 'refresh_token',
63
+ refresh_token: params.refreshToken,
64
+ client_id: params.clientId,
65
+ resource: params.resource,
66
+ });
67
+ const res = await f(`${params.logtoEndpoint}/oidc/token`, {
68
+ method: 'POST',
69
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
70
+ body,
71
+ signal: AbortSignal.timeout(10_000),
72
+ });
73
+ if (!res.ok) {
74
+ const text = await res.text().catch(() => '');
75
+ // 400 (invalid_grant) and 401 both indicate a dead refresh token.
76
+ if (res.status === 400 || res.status === 401) {
77
+ throw new RefreshTokenInvalidError(`Refresh token rejected: HTTP ${res.status}${text ? ` — ${text}` : ''}`);
78
+ }
79
+ throw new Error(`Token refresh failed: HTTP ${res.status}${text ? ` — ${text}` : ''}`);
80
+ }
81
+ return res.json();
82
+ }
83
+ //# sourceMappingURL=token-exchange.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-exchange.js","sourceRoot":"","sources":["../../src/auth/token-exchange.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Claude Code adapter.
3
+ *
4
+ * Wires the Node-based hook runner into `~/.claude/settings.json` and
5
+ * registers the MCP server via `claude mcp add`.
6
+ *
7
+ * Hook entries point at `node <path>/hook-runner.js <subcommand>` — the
8
+ * universal bundled runner installed at `~/.cybedefend/hook-runner.js`.
9
+ * No bash, no jq — runs the same way on Linux / macOS / Windows.
10
+ */
11
+ import { execFileSync } from 'node:child_process';
12
+ import { existsSync, readFileSync } from 'node:fs';
13
+ import { home, log, readJson, run, which, writeJson } from '../utils.js';
14
+ import { hookCommand, VIBEDEFEND_PATH_MARKER, isVibedefendOwnedHookCommand, } from '../hooks/install.js';
15
+ import { probeBinaryVersion } from './detect.js';
16
+ const CLAUDE_SETTINGS = home('.claude', 'settings.json');
17
+ function detect() {
18
+ if (which('claude') === null) {
19
+ return {
20
+ installed: false,
21
+ supportsHooks: false,
22
+ reason: '`claude` CLI not on PATH. Install Claude Code from https://claude.com/claude-code',
23
+ };
24
+ }
25
+ const version = probeBinaryVersion('claude') ?? undefined;
26
+ return { installed: true, version, supportsHooks: true };
27
+ }
28
+ function supports(event) {
29
+ return [
30
+ 'fetch-rules',
31
+ 'session-start',
32
+ 'stop',
33
+ 'pre-compact',
34
+ // Claude Code is the ONLY adapter that opts into UserPromptSubmit
35
+ // today — see user-prompt-submit.ts header for the rationale (the
36
+ // brainstorming skill hijack we need to backstop). Codex follows the
37
+ // doctrine directly via MCP Server.instructions; the other clients
38
+ // either don't have an equivalent event or haven't shown the same
39
+ // hijack behaviour.
40
+ 'user-prompt-submit',
41
+ ].includes(event);
42
+ }
43
+ /**
44
+ * Drop any prior entry that belongs to vibedefend (V0 manual template
45
+ * `tools/cybedefend-claude-hooks/hook-*.sh`, V1 bash hooks under
46
+ * `~/.cybedefend/hooks/*.sh`, and current V2 entries
47
+ * `node ~/.cybedefend/hook-runner.js ...`). Preserves unrelated user
48
+ * hooks. See `isVibedefendOwnedHookCommand` for the exact match set.
49
+ */
50
+ function dropOurs(entries) {
51
+ return entries.filter((e) => !(e.hooks ?? []).some((h) => isVibedefendOwnedHookCommand(h.command)));
52
+ }
53
+ function writeSettings(opts) {
54
+ const settings = readJson(CLAUDE_SETTINGS, {});
55
+ settings.hooks ??= {};
56
+ settings.hooks.PreToolUse ??= [];
57
+ settings.hooks.Stop ??= [];
58
+ settings.hooks.SessionStart ??= [];
59
+ settings.hooks.PreCompact ??= [];
60
+ settings.hooks.UserPromptSubmit ??= [];
61
+ settings.hooks.PreToolUse = dropOurs(settings.hooks.PreToolUse);
62
+ settings.hooks.SessionStart = dropOurs(settings.hooks.SessionStart);
63
+ settings.hooks.Stop = dropOurs(settings.hooks.Stop);
64
+ settings.hooks.PreCompact = dropOurs(settings.hooks.PreCompact);
65
+ settings.hooks.UserPromptSubmit = dropOurs(settings.hooks.UserPromptSubmit);
66
+ settings.hooks.PreToolUse.push({
67
+ matcher: 'Edit|Write|MultiEdit',
68
+ hooks: [{ type: 'command', command: hookCommand('fetch-rules') }],
69
+ });
70
+ // Action Guards: hard-block hook. Runs on every tool call (no matcher so
71
+ // it fires for Read/Write/Edit/Bash/WebFetch etc.). Exit code 2 → Claude
72
+ // Code blocks the tool call and surfaces the message to the model.
73
+ settings.hooks.PreToolUse.push({
74
+ hooks: [{ type: 'command', command: hookCommand('guard-check') }],
75
+ });
76
+ settings.hooks.SessionStart.push({
77
+ hooks: [{ type: 'command', command: hookCommand('session-start') }],
78
+ });
79
+ // UserPromptSubmit fires before the agent processes each user prompt.
80
+ // We use it to backstop the CybeDefend doctrine when a skill (notably
81
+ // superpowers:brainstorming) auto-activates and would otherwise
82
+ // hijack the flow before step 0 / step 1 fire. See
83
+ // src/hooks/runtime/user-prompt-submit.ts for the full rationale.
84
+ settings.hooks.UserPromptSubmit.push({
85
+ hooks: [{ type: 'command', command: hookCommand('user-prompt-submit') }],
86
+ });
87
+ if (opts.enableSessionReview) {
88
+ settings.hooks.Stop.push({
89
+ matcher: 'Stop',
90
+ hooks: [{ type: 'command', command: hookCommand('session-review') }],
91
+ });
92
+ settings.hooks.PreCompact.push({
93
+ hooks: [{ type: 'command', command: hookCommand('pre-compact') }],
94
+ });
95
+ }
96
+ writeJson(CLAUDE_SETTINGS, settings);
97
+ log.ok(`Claude Code hooks wired into ${CLAUDE_SETTINGS}`);
98
+ }
99
+ function registerMcp(opts) {
100
+ const { region } = opts;
101
+ log.step(`Registering MCP "${region.mcpName}" in Claude Code`);
102
+ log.hint(`URL: ${region.mcpUrl}`);
103
+ const code = run('claude', [
104
+ 'mcp',
105
+ 'add',
106
+ region.mcpName,
107
+ '--transport',
108
+ 'http',
109
+ region.mcpUrl,
110
+ ]);
111
+ if (code === 0) {
112
+ log.ok(`MCP "${region.mcpName}" registered.`);
113
+ return true;
114
+ }
115
+ log.warn(`\`claude mcp add\` returned exit ${code}. It may already be registered — run \`claude mcp list\` to verify.`);
116
+ return false;
117
+ }
118
+ /**
119
+ * Pure introspection — never throws. Reports whether our hooks are wired
120
+ * into `~/.claude/settings.json` and whether the MCP server is registered.
121
+ *
122
+ * `mcpRegistered` shells out to `claude mcp list` (the MCP registry lives
123
+ * in Claude Code's internal store, not a file we can read directly). If
124
+ * the `claude` binary is absent or the command fails, we degrade to
125
+ * `false` rather than throwing.
126
+ */
127
+ function inspect(region) {
128
+ let hooksWired = false;
129
+ try {
130
+ if (existsSync(CLAUDE_SETTINGS)) {
131
+ hooksWired = readFileSync(CLAUDE_SETTINGS, 'utf8').includes(VIBEDEFEND_PATH_MARKER);
132
+ }
133
+ }
134
+ catch {
135
+ /* missing/unreadable → false */
136
+ }
137
+ let mcpRegistered = false;
138
+ try {
139
+ const stdout = execFileSync('claude', ['mcp', 'list'], {
140
+ encoding: 'utf8',
141
+ stdio: ['ignore', 'pipe', 'ignore'],
142
+ });
143
+ mcpRegistered = stdout.includes(region.mcpName);
144
+ }
145
+ catch {
146
+ /* `claude` binary absent or command failed → false */
147
+ }
148
+ return { hooksWired, mcpRegistered };
149
+ }
150
+ export const claudeCodeAdapter = {
151
+ id: 'claude-code',
152
+ label: 'Claude Code',
153
+ detect,
154
+ supports,
155
+ writeSettings,
156
+ registerMcp,
157
+ inspect,
158
+ };
159
+ /**
160
+ * Cross-platform sanity gate (used by the installer banner).
161
+ *
162
+ * With the Node-based hook runtime (V2), Windows is now SUPPORTED — no
163
+ * Git Bash / WSL needed. Linux and macOS were already fine. We keep
164
+ * this function as the central place to gate hooks support in case a
165
+ * future platform (e.g. some embedded shell) needs special handling.
166
+ */
167
+ export function checkPlatformSupport() {
168
+ return { hooksSupported: true };
169
+ }
170
+ //# sourceMappingURL=claude-code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/clients/claude-code.ts"],"names":[],"mappings":"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"}