codehere 1.0.1 → 1.0.3

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 (163) hide show
  1. package/README.md +35 -193
  2. package/dist/application/use-cases/check-claimed-tests.js +81 -0
  3. package/dist/application/use-cases/detect-test-weakening.js +237 -0
  4. package/dist/application/use-cases/rerun-tests.js +110 -7
  5. package/dist/index.js +38 -1212
  6. package/dist/infrastructure/orchestration/artifacts-extractor.js +2 -323
  7. package/dist/presentation/cli/commands/check-command.js +178 -23
  8. package/dist/presentation/cli/commands/help-command.js +21 -285
  9. package/dist/ui.js +5 -236
  10. package/dist/utils/version.js +0 -6
  11. package/package.json +9 -38
  12. package/dist/application/services/verdict-service.js +0 -154
  13. package/dist/application/use-cases/assess-cross-vendor.js +0 -126
  14. package/dist/application/use-cases/build-cross-vendor-verdict.js +0 -118
  15. package/dist/application/use-cases/domain-trust.js +0 -110
  16. package/dist/application/use-cases/export-aat.js +0 -107
  17. package/dist/application/use-cases/generate-acceptance-criteria.js +0 -223
  18. package/dist/application/use-cases/generate-final-report.js +0 -259
  19. package/dist/application/use-cases/generate-narration.js +0 -130
  20. package/dist/application/use-cases/generate-thread-title.js +0 -181
  21. package/dist/application/use-cases/narrator-prompt-template.js +0 -158
  22. package/dist/application/use-cases/plan-auto-review.js +0 -57
  23. package/dist/application/use-cases/reconcile-audit.js +0 -100
  24. package/dist/application/use-cases/search-sessions.js +0 -70
  25. package/dist/application/use-cases/should-auto-review.js +0 -71
  26. package/dist/application/use-cases/thread-export.js +0 -245
  27. package/dist/application/use-cases/verify-claim.js +0 -456
  28. package/dist/application/use-cases/write-pass.js +0 -277
  29. package/dist/domain/audit.js +0 -11
  30. package/dist/domain/services/codebase-detector.js +0 -85
  31. package/dist/domain/task.js +0 -91
  32. package/dist/error-handler.js +0 -139
  33. package/dist/infrastructure/completion/bash-completion.js +0 -29
  34. package/dist/infrastructure/completion/fish-completion.js +0 -17
  35. package/dist/infrastructure/completion/zsh-completion.js +0 -25
  36. package/dist/infrastructure/config/config-manager.js +0 -174
  37. package/dist/infrastructure/config/product-context.js +0 -49
  38. package/dist/infrastructure/context/adapters/drawio-source.js +0 -157
  39. package/dist/infrastructure/context/adapters/figma-source.js +0 -198
  40. package/dist/infrastructure/context/adapters/github-issue-source.js +0 -160
  41. package/dist/infrastructure/context/adapters/linear-source.js +0 -190
  42. package/dist/infrastructure/context/context-resolver.js +0 -144
  43. package/dist/infrastructure/context/context-source.js +0 -114
  44. package/dist/infrastructure/context/github-api.js +0 -110
  45. package/dist/infrastructure/context/github-device-flow.js +0 -65
  46. package/dist/infrastructure/context/oauth-flow.js +0 -207
  47. package/dist/infrastructure/context/oauth-store.js +0 -122
  48. package/dist/infrastructure/context/registry-bootstrap.js +0 -95
  49. package/dist/infrastructure/context/write-pass-resolver.js +0 -106
  50. package/dist/infrastructure/cost/budget-config.js +0 -114
  51. package/dist/infrastructure/cost/cost-router.js +0 -97
  52. package/dist/infrastructure/cost/spend-aggregator.js +0 -222
  53. package/dist/infrastructure/cost/team-ledger.js +0 -355
  54. package/dist/infrastructure/discovery/discovery-log.js +0 -85
  55. package/dist/infrastructure/hooks/audit-tip-signature.js +0 -336
  56. package/dist/infrastructure/hooks/claude-sdk-hook.js +0 -1018
  57. package/dist/infrastructure/hooks/hook-mode.js +0 -87
  58. package/dist/infrastructure/hooks/mirror-tool-call-to-audit.js +0 -49
  59. package/dist/infrastructure/hooks/pre-prompt-scan.js +0 -122
  60. package/dist/infrastructure/hooks/session-analyzer.js +0 -177
  61. package/dist/infrastructure/observability/execution-tracer.js +0 -227
  62. package/dist/infrastructure/orchestration/agent-adapter.js +0 -13
  63. package/dist/infrastructure/orchestration/agent-installer.js +0 -101
  64. package/dist/infrastructure/orchestration/agents-config-seed.js +0 -68
  65. package/dist/infrastructure/orchestration/aider-adapter.js +0 -611
  66. package/dist/infrastructure/orchestration/cassette-recorder.js +0 -120
  67. package/dist/infrastructure/orchestration/claude-code-adapter.js +0 -592
  68. package/dist/infrastructure/orchestration/codex-adapter.js +0 -574
  69. package/dist/infrastructure/orchestration/conductor-adapter.js +0 -416
  70. package/dist/infrastructure/orchestration/context-handoff.js +0 -162
  71. package/dist/infrastructure/orchestration/control/codex-control-surface.js +0 -99
  72. package/dist/infrastructure/orchestration/control/control-surface.js +0 -15
  73. package/dist/infrastructure/orchestration/detect-ollama.js +0 -75
  74. package/dist/infrastructure/orchestration/failover-classifier.js +0 -151
  75. package/dist/infrastructure/orchestration/failover-config.js +0 -67
  76. package/dist/infrastructure/orchestration/failover-policy.js +0 -106
  77. package/dist/infrastructure/orchestration/gemini-adapter.js +0 -663
  78. package/dist/infrastructure/orchestration/generic-cli-adapter.js +0 -689
  79. package/dist/infrastructure/orchestration/graphify-context.js +0 -209
  80. package/dist/infrastructure/orchestration/memory-injector.js +0 -380
  81. package/dist/infrastructure/orchestration/mocks/mock-agent-repl.js +0 -388
  82. package/dist/infrastructure/orchestration/mocks/mock-codex-app-server.js +0 -73
  83. package/dist/infrastructure/orchestration/mocks/mock-synthesis.js +0 -83
  84. package/dist/infrastructure/orchestration/mocks/resolve-agent-spawn.js +0 -37
  85. package/dist/infrastructure/orchestration/opencode-adapter.js +0 -580
  86. package/dist/infrastructure/orchestration/progress-aggregator.js +0 -906
  87. package/dist/infrastructure/orchestration/readiness.js +0 -103
  88. package/dist/infrastructure/orchestration/session-manager.js +0 -1590
  89. package/dist/infrastructure/orchestration/session-persistence.js +0 -394
  90. package/dist/infrastructure/orchestration/thread-key-store.js +0 -127
  91. package/dist/infrastructure/orchestration/write-pass-store.js +0 -38
  92. package/dist/infrastructure/protocols/cloud-sync.js +0 -207
  93. package/dist/infrastructure/protocols/mcp-helpers.js +0 -25
  94. package/dist/infrastructure/protocols/mcp-server-impl.js +0 -3935
  95. package/dist/infrastructure/protocols/mcp-tools.js +0 -401
  96. package/dist/infrastructure/protocols/probe-port.js +0 -36
  97. package/dist/infrastructure/protocols/thread-phase.js +0 -105
  98. package/dist/infrastructure/protocols/ui/app-script-artifacts.js +0 -121
  99. package/dist/infrastructure/protocols/ui/app-script-audit-footer.js +0 -44
  100. package/dist/infrastructure/protocols/ui/app-script-audit-verify.js +0 -56
  101. package/dist/infrastructure/protocols/ui/app-script-composer-cost.js +0 -588
  102. package/dist/infrastructure/protocols/ui/app-script-context-sources.js +0 -168
  103. package/dist/infrastructure/protocols/ui/app-script-conversation.js +0 -67
  104. package/dist/infrastructure/protocols/ui/app-script-drawer.js +0 -115
  105. package/dist/infrastructure/protocols/ui/app-script-failover.js +0 -133
  106. package/dist/infrastructure/protocols/ui/app-script-github.js +0 -113
  107. package/dist/infrastructure/protocols/ui/app-script-observer.js +0 -104
  108. package/dist/infrastructure/protocols/ui/app-script-onboarding.js +0 -80
  109. package/dist/infrastructure/protocols/ui/app-script-overlay.js +0 -203
  110. package/dist/infrastructure/protocols/ui/app-script-recovery.js +0 -136
  111. package/dist/infrastructure/protocols/ui/app-script-render.js +0 -166
  112. package/dist/infrastructure/protocols/ui/app-script-share.js +0 -78
  113. package/dist/infrastructure/protocols/ui/app-script-sidebar.js +0 -194
  114. package/dist/infrastructure/protocols/ui/app-script-tour.js +0 -78
  115. package/dist/infrastructure/protocols/ui/app-script-wpass.js +0 -312
  116. package/dist/infrastructure/protocols/ui/app-script.js +0 -2998
  117. package/dist/infrastructure/protocols/ui/font-faces.js +0 -7
  118. package/dist/infrastructure/protocols/ui/styles.js +0 -2147
  119. package/dist/infrastructure/protocols/ui/tokens.js +0 -96
  120. package/dist/infrastructure/protocols/ui-page-v2.js +0 -320
  121. package/dist/infrastructure/protocols/verify-page.js +0 -1677
  122. package/dist/infrastructure/security/file-collection.js +0 -118
  123. package/dist/infrastructure/security/fix-code.js +0 -144
  124. package/dist/infrastructure/security/fix-secret.js +0 -89
  125. package/dist/infrastructure/security/permissions-config.js +0 -235
  126. package/dist/infrastructure/security/scan-types.js +0 -10
  127. package/dist/infrastructure/security/ship-scan.js +0 -195
  128. package/dist/infrastructure/security/trust-scorer.js +0 -724
  129. package/dist/infrastructure/security/vibe-checks.js +0 -99
  130. package/dist/infrastructure/storage/atomic-write.js +0 -86
  131. package/dist/infrastructure/storage/cross-tool-agents.js +0 -268
  132. package/dist/infrastructure/storage/project-thread-migration.js +0 -237
  133. package/dist/infrastructure/storage/project-thread-mirror.js +0 -671
  134. package/dist/infrastructure/storage/project-thread-reader.js +0 -409
  135. package/dist/infrastructure/storage/project-thread-title-backfill.js +0 -214
  136. package/dist/monitoring.js +0 -212
  137. package/dist/presentation/cli/commands/audit-command.js +0 -201
  138. package/dist/presentation/cli/commands/budget-command.js +0 -79
  139. package/dist/presentation/cli/commands/config-command.js +0 -125
  140. package/dist/presentation/cli/commands/context-source-command.js +0 -217
  141. package/dist/presentation/cli/commands/hook-command.js +0 -265
  142. package/dist/presentation/cli/commands/init-command.js +0 -590
  143. package/dist/presentation/cli/commands/memory-command.js +0 -1031
  144. package/dist/presentation/cli/commands/narrator-command.js +0 -101
  145. package/dist/presentation/cli/commands/permission-command.js +0 -105
  146. package/dist/presentation/cli/commands/protocols-builder.js +0 -254
  147. package/dist/presentation/cli/commands/protocols-command.js +0 -84
  148. package/dist/presentation/cli/commands/repo-map-builder.js +0 -247
  149. package/dist/presentation/cli/commands/report-command.js +0 -403
  150. package/dist/presentation/cli/commands/scan-command.js +0 -52
  151. package/dist/presentation/cli/commands/scan-tool-command.js +0 -110
  152. package/dist/presentation/cli/commands/search-command.js +0 -51
  153. package/dist/presentation/cli/commands/setup-command.js +0 -330
  154. package/dist/presentation/cli/commands/status-command.js +0 -614
  155. package/dist/presentation/cli/commands/team-command.js +0 -256
  156. package/dist/presentation/cli/commands/thread-command.js +0 -370
  157. package/dist/presentation/cli/commands/trust-command.js +0 -328
  158. package/dist/presentation/cli/commands/verify-command.js +0 -298
  159. package/dist/presentation/cli/error-display.js +0 -328
  160. package/dist/presentation/cli/repl.js +0 -805
  161. package/dist/presentation/cli/task-deliverable.js +0 -153
  162. package/dist/testing/cassette.js +0 -125
  163. package/dist/testing/replay-adapter.js +0 -147
@@ -1,217 +0,0 @@
1
- /**
2
- * CLI: `codehere context-source` · v0.8 S2.5
3
- *
4
- * Subcommands:
5
- * list · show every registered provider + connection status
6
- * connect <provider> · open the browser to begin OAuth · prints URL
7
- * disconnect <provider> · clear the stored OAuth token
8
- *
9
- * Talks to the running daemon at 127.0.0.1:3773. If the daemon isn't running,
10
- * we print a clear message rather than starting one silently.
11
- */
12
- import { colors, symbols } from '../../../ui.js';
13
- import { spawn } from 'child_process';
14
- const DAEMON_BASE = process.env.CODEHERE_DAEMON_URL || 'http://127.0.0.1:3773';
15
- export async function executeContextSourceCommand(sub, arg, options = {}) {
16
- switch (sub) {
17
- case undefined:
18
- case 'list':
19
- return list(options.json ?? false);
20
- case 'connect':
21
- return connect(arg, options.json ?? false, options.open !== false);
22
- case 'disconnect':
23
- return disconnect(arg, options.json ?? false);
24
- default:
25
- process.stderr.write(colors.error(`${symbols.error} Unknown subcommand: ${sub}\n`) +
26
- colors.dim('Try: codehere context-source list | connect <provider> | disconnect <provider>\n'));
27
- return 2;
28
- }
29
- }
30
- async function fetchJson(path, init = {}) {
31
- try {
32
- const r = await fetch(DAEMON_BASE + path, init);
33
- if (!r.ok) {
34
- const text = await r.text();
35
- return { ok: false, status: r.status, error: text };
36
- }
37
- return { ok: true, status: r.status, data: (await r.json()) };
38
- }
39
- catch (err) {
40
- return { ok: false, status: 0, error: err instanceof Error ? err.message : String(err) };
41
- }
42
- }
43
- function daemonUnreachableNote(err) {
44
- return (colors.error(`${symbols.error} Daemon not reachable at ${DAEMON_BASE}.\n`) +
45
- colors.dim(` Start it with: `) +
46
- colors.bold('codehere start') +
47
- '\n' +
48
- colors.dim(` Detail: ${err}\n`));
49
- }
50
- /* ──────────────────────────────────────────────────────────────────────── */
51
- /* list */
52
- /* ──────────────────────────────────────────────────────────────────────── */
53
- async function list(json) {
54
- const r = await fetchJson('/api/context-sources');
55
- if (!r.ok) {
56
- if (json) {
57
- process.stdout.write(JSON.stringify({ error: 'daemon_unreachable', detail: r.error }, null, 2) + '\n');
58
- }
59
- else {
60
- process.stderr.write(daemonUnreachableNote(r.error ?? ''));
61
- }
62
- return 2;
63
- }
64
- const items = r.data?.providers ?? [];
65
- if (json) {
66
- process.stdout.write(JSON.stringify({ providers: items }, null, 2) + '\n');
67
- return 0;
68
- }
69
- process.stdout.write('\n' + colors.bold('Context sources') + '\n\n');
70
- if (!items.length) {
71
- process.stdout.write(colors.dim(' (no providers registered)\n\n'));
72
- return 0;
73
- }
74
- for (const p of items) {
75
- const tag = statusTag(p.status);
76
- const who = p.userLabel ? colors.gray('· ' + p.userLabel) : '';
77
- process.stdout.write(` ${tag} ${colors.bold(p.label.padEnd(10))} ${colors.dim(p.scheme + '://')} ${who}\n`);
78
- if (!p.clientIdConfigured) {
79
- process.stdout.write(' ' + colors.warning(`set CODEHERE_${p.id.toUpperCase()}_CLIENT_ID before connect()`) + '\n');
80
- }
81
- }
82
- process.stdout.write('\n' + colors.dim('Connect: ') + colors.bold('codehere context-source connect <provider>') + '\n\n');
83
- return 0;
84
- }
85
- function statusTag(s) {
86
- switch (s) {
87
- case 'connected':
88
- return colors.green('● connected ');
89
- case 'expired':
90
- return colors.warning('● expired ');
91
- case 'manual':
92
- return colors.gray('● manual ');
93
- case 'disconnected':
94
- return colors.gray('○ disconnected');
95
- default:
96
- // A status this CLI doesn't know yet must never print "undefined"
97
- // (2026-06-07 audit, Lane C: draw.io/GitHub rows did exactly that).
98
- return colors.gray(('○ ' + String(s ?? 'unknown')).padEnd(14).slice(0, 14));
99
- }
100
- }
101
- /* ──────────────────────────────────────────────────────────────────────── */
102
- /* connect */
103
- /* ──────────────────────────────────────────────────────────────────────── */
104
- async function connect(providerArg, json, openBrowser) {
105
- if (!providerArg) {
106
- process.stderr.write(colors.error(`${symbols.error} Need a provider id: codehere context-source connect <id>\n`));
107
- return 2;
108
- }
109
- const r = await fetchJson(`/api/context-sources/${encodeURIComponent(providerArg)}/connect`, { method: 'POST' });
110
- if (!r.ok) {
111
- let detail = null;
112
- try {
113
- detail = JSON.parse(r.error ?? '{}');
114
- }
115
- catch {
116
- /* ignore */
117
- }
118
- if (detail?.error === 'provider_not_registered') {
119
- const envVar = detail.envVar ?? `CODEHERE_${providerArg.toUpperCase()}_CLIENT_ID`;
120
- if (json) {
121
- process.stdout.write(JSON.stringify({ error: 'provider_not_registered', envVar, message: detail.message }, null, 2) + '\n');
122
- }
123
- else {
124
- process.stderr.write(colors.error(`${symbols.error} ${providerArg} OAuth app not registered.\n`) +
125
- colors.dim(` ${detail.message ?? ''}\n`) +
126
- colors.dim(` Set the env var: `) +
127
- colors.bold(envVar) +
128
- '\n');
129
- }
130
- return 2;
131
- }
132
- if (json) {
133
- process.stdout.write(JSON.stringify({ error: 'connect_failed', detail: r.error }, null, 2) + '\n');
134
- }
135
- else if (r.status === 0) {
136
- // Only a NETWORK failure means the daemon is unreachable.
137
- process.stderr.write(daemonUnreachableNote(r.error ?? ''));
138
- }
139
- else {
140
- // The daemon answered — show ITS message, don't blame the connection
141
- // (2026-06-07 audit, Lane C: a 404 from a live daemon printed
142
- // "Daemon not reachable").
143
- let msg = r.error ?? `request failed (${r.status})`;
144
- try {
145
- const j = JSON.parse(msg);
146
- msg = j.error || j.message || msg;
147
- }
148
- catch {
149
- /* raw text is fine */
150
- }
151
- process.stderr.write(colors.error(`${symbols.error} ${providerArg}: ${msg}\n`));
152
- }
153
- return 2;
154
- }
155
- const url = r.data?.authorizeUrl;
156
- if (!url) {
157
- process.stderr.write(colors.error(`${symbols.error} Daemon did not return an authorize URL.\n`));
158
- return 1;
159
- }
160
- if (json) {
161
- process.stdout.write(JSON.stringify({ authorizeUrl: url }, null, 2) + '\n');
162
- return 0;
163
- }
164
- process.stdout.write('\n' +
165
- colors.bold(`Opening ${providerArg} OAuth flow`) +
166
- '\n\n' +
167
- colors.dim(' URL: ') +
168
- url +
169
- '\n\n' +
170
- colors.dim(' Codehere will store the token at ~/.codehere/context-sources.json.\n') +
171
- colors.dim(' You can close the browser tab when it says "connected".\n\n'));
172
- if (openBrowser) {
173
- openInBrowser(url);
174
- }
175
- return 0;
176
- }
177
- function openInBrowser(url) {
178
- const cmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
179
- try {
180
- const child = spawn(cmd, [url], { detached: true, stdio: 'ignore' });
181
- child.unref();
182
- }
183
- catch {
184
- // best-effort · the URL is already printed
185
- }
186
- }
187
- /* ──────────────────────────────────────────────────────────────────────── */
188
- /* disconnect */
189
- /* ──────────────────────────────────────────────────────────────────────── */
190
- async function disconnect(providerArg, json) {
191
- if (!providerArg) {
192
- process.stderr.write(colors.error(`${symbols.error} Need a provider id: codehere context-source disconnect <id>\n`));
193
- return 2;
194
- }
195
- const r = await fetchJson(`/api/context-sources/${encodeURIComponent(providerArg)}/disconnect`, { method: 'POST' });
196
- if (!r.ok) {
197
- if (json) {
198
- process.stdout.write(JSON.stringify({ error: 'disconnect_failed', detail: r.error }, null, 2) + '\n');
199
- }
200
- else {
201
- process.stderr.write(daemonUnreachableNote(r.error ?? ''));
202
- }
203
- return 2;
204
- }
205
- if (json) {
206
- process.stdout.write(JSON.stringify(r.data) + '\n');
207
- return 0;
208
- }
209
- if (r.data?.ok) {
210
- process.stdout.write(colors.green(`${symbols.success} disconnected ${providerArg}\n`));
211
- }
212
- else {
213
- process.stdout.write(colors.gray(`${symbols.dot} ${providerArg} was not connected\n`));
214
- }
215
- return 0;
216
- }
217
- //# sourceMappingURL=context-source-command.js.map
@@ -1,265 +0,0 @@
1
- /**
2
- * CLI Command: hook
3
- *
4
- * Manages Claude Code SDK hook integration.
5
- * Intercepts every Claude Code tool call and runs Layer 0 security scan.
6
- *
7
- * Usage:
8
- * codehere hook install # Add hooks to ~/.claude/settings.json
9
- * codehere hook uninstall # Remove hooks
10
- * codehere hook status # Show current hook config and recent events
11
- * codehere hook log # Show recent audit log
12
- * codehere hook log --tail 50 # Last 50 events
13
- */
14
- import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, copyFileSync, unlinkSync, rmdirSync, } from 'fs';
15
- import { join, dirname } from 'path';
16
- import { homedir } from 'os';
17
- import { fileURLToPath } from 'url';
18
- import { generateHookConfig, AUDIT_FILE } from '../../../infrastructure/hooks/claude-sdk-hook.js';
19
- import { colors, success, error, warning, info, newline, sectionHeader } from '../../../ui.js';
20
- // ── Skills management ─────────────────────────────────────────────────────────
21
- const SKILLS_TARGET_DIR = join(homedir(), '.claude', 'commands', 'codehere');
22
- function getSkillsSourceDir() {
23
- // When running from dist/, the .claude/commands dir is at package root
24
- const __filename = fileURLToPath(import.meta.url);
25
- const __dirname = dirname(__filename);
26
- // dist/presentation/cli/commands → ../../../../.claude/commands
27
- return join(__dirname, '..', '..', '..', '..', '.claude', 'commands');
28
- }
29
- function installSkills() {
30
- const sourceDir = getSkillsSourceDir();
31
- if (!existsSync(sourceDir))
32
- return false;
33
- if (!existsSync(SKILLS_TARGET_DIR)) {
34
- mkdirSync(SKILLS_TARGET_DIR, { recursive: true });
35
- }
36
- const files = readdirSync(sourceDir).filter((f) => f.endsWith('.md'));
37
- for (const file of files) {
38
- copyFileSync(join(sourceDir, file), join(SKILLS_TARGET_DIR, file));
39
- }
40
- return files.length > 0;
41
- }
42
- function uninstallSkills() {
43
- if (!existsSync(SKILLS_TARGET_DIR))
44
- return;
45
- try {
46
- for (const file of readdirSync(SKILLS_TARGET_DIR)) {
47
- unlinkSync(join(SKILLS_TARGET_DIR, file));
48
- }
49
- rmdirSync(SKILLS_TARGET_DIR);
50
- }
51
- catch {
52
- /* ignore */
53
- }
54
- }
55
- function skillsInstalled() {
56
- return existsSync(SKILLS_TARGET_DIR) && readdirSync(SKILLS_TARGET_DIR).some((f) => f.endsWith('.md'));
57
- }
58
- const SETTINGS_PATH = join(homedir(), '.claude', 'settings.json');
59
- // ── helpers ──────────────────────────────────────────────────────────────────
60
- function readSettings() {
61
- if (!existsSync(SETTINGS_PATH))
62
- return {};
63
- try {
64
- return JSON.parse(readFileSync(SETTINGS_PATH, 'utf-8'));
65
- }
66
- catch {
67
- return {};
68
- }
69
- }
70
- function writeSettings(settings) {
71
- const dir = join(homedir(), '.claude');
72
- if (!existsSync(dir))
73
- mkdirSync(dir, { recursive: true });
74
- writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + '\n');
75
- }
76
- function isInstalled(settings) {
77
- const hooks = (settings.hooks ?? {});
78
- const preToolUse = (hooks.PreToolUse ?? []);
79
- return preToolUse.some((entry) => entry.hooks?.some((h) => h.command?.includes('codehere scan-tool')));
80
- }
81
- // ── command ───────────────────────────────────────────────────────────────────
82
- export async function executeHookCommand(subcommand, options = {}) {
83
- const sub = subcommand ?? 'status';
84
- switch (sub) {
85
- case 'install': {
86
- const settings = readSettings();
87
- if (isInstalled(settings)) {
88
- console.log(warning('codehere hooks already installed'));
89
- console.log(colors.dim(` Config: ${SETTINGS_PATH}`));
90
- console.log(colors.dim(' Run `codehere hook status` to see details'));
91
- newline();
92
- return;
93
- }
94
- const hookConfig = generateHookConfig({
95
- logOnly: options.logOnly,
96
- blockMode: options.blockMode,
97
- });
98
- // Merge with existing hooks config
99
- const existingHooks = settings.hooks ?? {};
100
- const mergedHooks = { ...existingHooks };
101
- for (const [eventName, handlers] of Object.entries(hookConfig)) {
102
- const existing = mergedHooks[eventName] ?? [];
103
- mergedHooks[eventName] = [...existing, ...handlers];
104
- }
105
- settings.hooks = mergedHooks;
106
- writeSettings(settings);
107
- const skillsOk = installSkills();
108
- console.log(success('codehere hooks installed'));
109
- newline();
110
- console.log(colors.bold('What happens now:'));
111
- console.log(' Every Bash, Edit, Write, and MultiEdit call your AI makes is');
112
- console.log(' scanned by codehere before it executes.');
113
- console.log(' Zero API calls. < 5ms per scan. Runs entirely on your machine.');
114
- newline();
115
- console.log(colors.bold('Scan covers:'));
116
- console.log(' • Prompt injection & jailbreak attempts');
117
- console.log(' • Credential theft (API keys, SSH keys, env vars)');
118
- console.log(' • Destructive operations (rm -rf, DB drops, force push)');
119
- console.log(' • Code injection (curl|bash, reverse shells)');
120
- console.log(' • Supply chain attacks (typosquatting packages)');
121
- console.log(' • OWASP Top 10 patterns (SQLi, XSS, hardcoded secrets)');
122
- newline();
123
- console.log(colors.bold('Audit log:'));
124
- console.log(colors.dim(` ${AUDIT_FILE}`));
125
- newline();
126
- if (options.logOnly) {
127
- console.log(warning('Mode: log-only (no blocking). Run without --log-only to enable blocking.'));
128
- newline();
129
- }
130
- console.log(colors.dim(' Config written to: ' + SETTINGS_PATH));
131
- if (skillsOk) {
132
- console.log(colors.dim(` Skills installed to: ${SKILLS_TARGET_DIR}`));
133
- newline();
134
- console.log(colors.bold('Slash commands now available in your AI assistant:'));
135
- console.log(colors.dim(' /codehere:trust · scan current project'));
136
- console.log(colors.dim(' /codehere:report · generate shareable audit report'));
137
- console.log(colors.dim(' /codehere:audit · pre-merge security check'));
138
- console.log(colors.dim(' /codehere:hook-status · check hook health'));
139
- }
140
- newline();
141
- console.log(colors.dim(' Restart your AI assistant for changes to take effect.'));
142
- newline();
143
- break;
144
- }
145
- case 'uninstall': {
146
- const settings = readSettings();
147
- if (!isInstalled(settings)) {
148
- console.log(info('codehere hooks are not installed'));
149
- return;
150
- }
151
- const hooks = settings.hooks;
152
- for (const [eventName, handlers] of Object.entries(hooks)) {
153
- if (Array.isArray(handlers)) {
154
- hooks[eventName] = handlers.filter((entry) => !entry.hooks?.some((h) => h.command?.includes('codehere scan-tool')));
155
- }
156
- }
157
- writeSettings(settings);
158
- uninstallSkills();
159
- console.log(success('codehere hooks and skills removed'));
160
- console.log(colors.dim('Restart your AI assistant for changes to take effect.'));
161
- newline();
162
- break;
163
- }
164
- case 'status': {
165
- const settings = readSettings();
166
- const installed = isInstalled(settings);
167
- sectionHeader('codehere Hook Status');
168
- newline();
169
- if (installed) {
170
- console.log(success('Running') + ' Intercepting every tool call before it executes.');
171
- }
172
- else {
173
- console.log(warning('Not installed') + ' Run `codehere hook install` to activate.');
174
- }
175
- newline();
176
- const skills = skillsInstalled();
177
- if (skills) {
178
- console.log(info('Skills') + ' /codehere:trust /codehere:report /codehere:audit /codehere:hook-status');
179
- }
180
- else {
181
- console.log(colors.dim('Skills: not installed (run `codehere hook install` to add)'));
182
- }
183
- newline();
184
- console.log(colors.dim('Config: ') + SETTINGS_PATH);
185
- console.log(colors.dim('Audit log:') + ' ' + AUDIT_FILE);
186
- newline();
187
- // Autonomous mode callout
188
- console.log(colors.bold('Autonomous mode (shift+tab)'));
189
- console.log(colors.dim(' When Claude Code runs without approval prompts, this hook is your'));
190
- console.log(colors.dim(' last line of defense. Every tool call is scanned before it executes.'));
191
- console.log(colors.dim(' After a session: `codehere status --sessions` to review what happened.'));
192
- newline();
193
- // Show recent audit stats
194
- if (existsSync(AUDIT_FILE)) {
195
- const lines = readFileSync(AUDIT_FILE, 'utf-8').split('\n').filter(Boolean).slice(-1000); // last 1000 events
196
- if (lines.length > 0) {
197
- const entries = lines
198
- .map((l) => {
199
- try {
200
- return JSON.parse(l);
201
- }
202
- catch {
203
- return null;
204
- }
205
- })
206
- .filter(Boolean);
207
- const total = entries.length;
208
- const blocked = entries.filter((e) => e.blocked).length;
209
- const critical = entries.filter((e) => (e.criticalFindings ?? 0) > 0).length;
210
- console.log(colors.bold("What it's seen") + colors.dim(' (last 1000 events):'));
211
- console.log(` ${colors.cyan(String(total))} tool calls scanned`);
212
- console.log(` ${blocked > 0 ? colors.red(String(blocked)) : colors.green('none')} blocked`);
213
- console.log(` ${critical > 0 ? colors.red(String(critical)) : colors.green('none')} with critical findings`);
214
- newline();
215
- if (blocked > 0) {
216
- console.log(colors.dim(' Run `codehere status --sessions` to see which sessions had blocks.'));
217
- newline();
218
- }
219
- }
220
- }
221
- else {
222
- console.log(colors.dim(" No events logged yet · run Claude Code and it'll start tracking."));
223
- newline();
224
- }
225
- break;
226
- }
227
- case 'log': {
228
- const tail = options.tail ?? 20;
229
- if (!existsSync(AUDIT_FILE)) {
230
- console.log(info('No audit log yet'));
231
- console.log(colors.dim(' Run `codehere hook install` first'));
232
- return;
233
- }
234
- const lines = readFileSync(AUDIT_FILE, 'utf-8').split('\n').filter(Boolean).slice(-tail);
235
- if (lines.length === 0) {
236
- console.log(info('Audit log is empty'));
237
- return;
238
- }
239
- sectionHeader(`Hook Audit Log (last ${lines.length})`);
240
- newline();
241
- for (const line of lines) {
242
- try {
243
- const entry = JSON.parse(line);
244
- const ts = new Date(entry.timestamp).toLocaleTimeString();
245
- const status = entry.blocked ? colors.red('[BLOCKED]') : colors.green('[ALLOW] ');
246
- const risk = entry.riskScore > 0 ? colors.yellow(` risk:${entry.riskScore}`) : '';
247
- const tool = colors.cyan(entry.toolName.padEnd(10));
248
- const preview = entry.toolInput.substring(0, 60).replace(/\n/g, ' ');
249
- console.log(` ${ts} ${status} ${tool}${risk} ${colors.dim(preview)}`);
250
- }
251
- catch {
252
- // skip malformed line
253
- }
254
- }
255
- newline();
256
- break;
257
- }
258
- default: {
259
- console.log(error(`Unknown subcommand: ${sub}`));
260
- console.log(colors.dim('Usage: codehere hook install|uninstall|status|log'));
261
- process.exit(1);
262
- }
263
- }
264
- }
265
- //# sourceMappingURL=hook-command.js.map