@theihtisham/agent-shadow-brain 5.1.1 → 6.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.
Files changed (183) hide show
  1. package/README.md +355 -1095
  2. package/dist/adapters/copilot.d.ts +14 -0
  3. package/dist/adapters/copilot.d.ts.map +1 -0
  4. package/dist/adapters/copilot.js +93 -0
  5. package/dist/adapters/copilot.js.map +1 -0
  6. package/dist/adapters/cursor.d.ts +13 -0
  7. package/dist/adapters/cursor.d.ts.map +1 -0
  8. package/dist/adapters/cursor.js +96 -0
  9. package/dist/adapters/cursor.js.map +1 -0
  10. package/dist/adapters/index.d.ts +4 -1
  11. package/dist/adapters/index.d.ts.map +1 -1
  12. package/dist/adapters/index.js +7 -3
  13. package/dist/adapters/index.js.map +1 -1
  14. package/dist/adapters/windsurf.d.ts +14 -0
  15. package/dist/adapters/windsurf.d.ts.map +1 -0
  16. package/dist/adapters/windsurf.js +107 -0
  17. package/dist/adapters/windsurf.js.map +1 -0
  18. package/dist/brain/agent-firewall.d.ts +7 -0
  19. package/dist/brain/agent-firewall.d.ts.map +1 -0
  20. package/dist/brain/agent-firewall.js +152 -0
  21. package/dist/brain/agent-firewall.js.map +1 -0
  22. package/dist/brain/agent-handoff.d.ts +19 -0
  23. package/dist/brain/agent-handoff.d.ts.map +1 -0
  24. package/dist/brain/agent-handoff.js +127 -0
  25. package/dist/brain/agent-handoff.js.map +1 -0
  26. package/dist/brain/air-gap.d.ts +23 -0
  27. package/dist/brain/air-gap.d.ts.map +1 -0
  28. package/dist/brain/air-gap.js +137 -0
  29. package/dist/brain/air-gap.js.map +1 -0
  30. package/dist/brain/attention-heatmap.d.ts +24 -0
  31. package/dist/brain/attention-heatmap.d.ts.map +1 -0
  32. package/dist/brain/attention-heatmap.js +108 -0
  33. package/dist/brain/attention-heatmap.js.map +1 -0
  34. package/dist/brain/auto-setup.js +19 -14
  35. package/dist/brain/auto-setup.js.map +1 -1
  36. package/dist/brain/auto-update.js +1 -1
  37. package/dist/brain/brain-chat.d.ts +42 -0
  38. package/dist/brain/brain-chat.d.ts.map +1 -0
  39. package/dist/brain/brain-chat.js +162 -0
  40. package/dist/brain/brain-chat.js.map +1 -0
  41. package/dist/brain/brain-encryption.d.ts +14 -0
  42. package/dist/brain/brain-encryption.d.ts.map +1 -0
  43. package/dist/brain/brain-encryption.js +68 -0
  44. package/dist/brain/brain-encryption.js.map +1 -0
  45. package/dist/brain/brain-exchange.d.ts +30 -0
  46. package/dist/brain/brain-exchange.d.ts.map +1 -0
  47. package/dist/brain/brain-exchange.js +106 -0
  48. package/dist/brain/brain-exchange.js.map +1 -0
  49. package/dist/brain/brain-garden.d.ts +18 -0
  50. package/dist/brain/brain-garden.d.ts.map +1 -0
  51. package/dist/brain/brain-garden.js +81 -0
  52. package/dist/brain/brain-garden.js.map +1 -0
  53. package/dist/brain/brain-portability.js +1 -1
  54. package/dist/brain/branch-brain.d.ts +23 -0
  55. package/dist/brain/branch-brain.d.ts.map +1 -0
  56. package/dist/brain/branch-brain.js +159 -0
  57. package/dist/brain/branch-brain.js.map +1 -0
  58. package/dist/brain/calibration-monitor.d.ts +20 -0
  59. package/dist/brain/calibration-monitor.d.ts.map +1 -0
  60. package/dist/brain/calibration-monitor.js +144 -0
  61. package/dist/brain/calibration-monitor.js.map +1 -0
  62. package/dist/brain/causal-chains.d.ts +37 -0
  63. package/dist/brain/causal-chains.d.ts.map +1 -0
  64. package/dist/brain/causal-chains.js +250 -0
  65. package/dist/brain/causal-chains.js.map +1 -0
  66. package/dist/brain/collision-detective.d.ts +32 -0
  67. package/dist/brain/collision-detective.d.ts.map +1 -0
  68. package/dist/brain/collision-detective.js +233 -0
  69. package/dist/brain/collision-detective.js.map +1 -0
  70. package/dist/brain/cost-aware-spawner.d.ts +7 -0
  71. package/dist/brain/cost-aware-spawner.d.ts.map +1 -0
  72. package/dist/brain/cost-aware-spawner.js +77 -0
  73. package/dist/brain/cost-aware-spawner.js.map +1 -0
  74. package/dist/brain/dream-engine.d.ts +35 -0
  75. package/dist/brain/dream-engine.d.ts.map +1 -0
  76. package/dist/brain/dream-engine.js +273 -0
  77. package/dist/brain/dream-engine.js.map +1 -0
  78. package/dist/brain/embeddings.d.ts +27 -0
  79. package/dist/brain/embeddings.d.ts.map +1 -0
  80. package/dist/brain/embeddings.js +175 -0
  81. package/dist/brain/embeddings.js.map +1 -0
  82. package/dist/brain/forgetting-curve.d.ts +21 -0
  83. package/dist/brain/forgetting-curve.d.ts.map +1 -0
  84. package/dist/brain/forgetting-curve.js +174 -0
  85. package/dist/brain/forgetting-curve.js.map +1 -0
  86. package/dist/brain/formal-verification-bridge.d.ts +26 -0
  87. package/dist/brain/formal-verification-bridge.d.ts.map +1 -0
  88. package/dist/brain/formal-verification-bridge.js +219 -0
  89. package/dist/brain/formal-verification-bridge.js.map +1 -0
  90. package/dist/brain/global-brain.d.ts +63 -0
  91. package/dist/brain/global-brain.d.ts.map +1 -0
  92. package/dist/brain/global-brain.js +348 -0
  93. package/dist/brain/global-brain.js.map +1 -0
  94. package/dist/brain/hallucination-quarantine.d.ts +26 -0
  95. package/dist/brain/hallucination-quarantine.d.ts.map +1 -0
  96. package/dist/brain/hallucination-quarantine.js +135 -0
  97. package/dist/brain/hallucination-quarantine.js.map +1 -0
  98. package/dist/brain/hive-accelerator.d.ts +39 -0
  99. package/dist/brain/hive-accelerator.d.ts.map +1 -0
  100. package/dist/brain/hive-accelerator.js +152 -0
  101. package/dist/brain/hive-accelerator.js.map +1 -0
  102. package/dist/brain/json-output.d.ts +26 -0
  103. package/dist/brain/json-output.d.ts.map +1 -0
  104. package/dist/brain/json-output.js +112 -0
  105. package/dist/brain/json-output.js.map +1 -0
  106. package/dist/brain/l0-cache.d.ts +40 -0
  107. package/dist/brain/l0-cache.d.ts.map +1 -0
  108. package/dist/brain/l0-cache.js +176 -0
  109. package/dist/brain/l0-cache.js.map +1 -0
  110. package/dist/brain/local-llm.d.ts +20 -0
  111. package/dist/brain/local-llm.d.ts.map +1 -0
  112. package/dist/brain/local-llm.js +132 -0
  113. package/dist/brain/local-llm.js.map +1 -0
  114. package/dist/brain/mcp-server.d.ts +44 -0
  115. package/dist/brain/mcp-server.d.ts.map +1 -1
  116. package/dist/brain/mcp-server.js +486 -12
  117. package/dist/brain/mcp-server.js.map +1 -1
  118. package/dist/brain/model-registry.d.ts +96 -0
  119. package/dist/brain/model-registry.d.ts.map +1 -0
  120. package/dist/brain/model-registry.js +585 -0
  121. package/dist/brain/model-registry.js.map +1 -0
  122. package/dist/brain/orchestrator.js +1 -1
  123. package/dist/brain/pr-auto-review.d.ts +17 -0
  124. package/dist/brain/pr-auto-review.d.ts.map +1 -0
  125. package/dist/brain/pr-auto-review.js +73 -0
  126. package/dist/brain/pr-auto-review.js.map +1 -0
  127. package/dist/brain/pre-mortem.d.ts +21 -0
  128. package/dist/brain/pre-mortem.d.ts.map +1 -0
  129. package/dist/brain/pre-mortem.js +210 -0
  130. package/dist/brain/pre-mortem.js.map +1 -0
  131. package/dist/brain/prompt-cache.d.ts +54 -0
  132. package/dist/brain/prompt-cache.d.ts.map +1 -0
  133. package/dist/brain/prompt-cache.js +105 -0
  134. package/dist/brain/prompt-cache.js.map +1 -0
  135. package/dist/brain/reputation-ledger.d.ts +42 -0
  136. package/dist/brain/reputation-ledger.d.ts.map +1 -0
  137. package/dist/brain/reputation-ledger.js +326 -0
  138. package/dist/brain/reputation-ledger.js.map +1 -0
  139. package/dist/brain/session-hooks.d.ts +47 -0
  140. package/dist/brain/session-hooks.d.ts.map +1 -0
  141. package/dist/brain/session-hooks.js +706 -0
  142. package/dist/brain/session-hooks.js.map +1 -0
  143. package/dist/brain/subagent-bridge.d.ts +46 -0
  144. package/dist/brain/subagent-bridge.d.ts.map +1 -0
  145. package/dist/brain/subagent-bridge.js +364 -0
  146. package/dist/brain/subagent-bridge.js.map +1 -0
  147. package/dist/brain/subconscious.d.ts +34 -0
  148. package/dist/brain/subconscious.d.ts.map +1 -0
  149. package/dist/brain/subconscious.js +275 -0
  150. package/dist/brain/subconscious.js.map +1 -0
  151. package/dist/brain/swarm-debate.d.ts +22 -0
  152. package/dist/brain/swarm-debate.d.ts.map +1 -0
  153. package/dist/brain/swarm-debate.js +134 -0
  154. package/dist/brain/swarm-debate.js.map +1 -0
  155. package/dist/brain/team-brain-sync.d.ts +22 -0
  156. package/dist/brain/team-brain-sync.d.ts.map +1 -0
  157. package/dist/brain/team-brain-sync.js +132 -0
  158. package/dist/brain/team-brain-sync.js.map +1 -0
  159. package/dist/brain/token-economy.d.ts +25 -0
  160. package/dist/brain/token-economy.d.ts.map +1 -0
  161. package/dist/brain/token-economy.js +216 -0
  162. package/dist/brain/token-economy.js.map +1 -0
  163. package/dist/brain/voice-mode.d.ts +15 -0
  164. package/dist/brain/voice-mode.d.ts.map +1 -0
  165. package/dist/brain/voice-mode.js +97 -0
  166. package/dist/brain/voice-mode.js.map +1 -0
  167. package/dist/cli.js +990 -6
  168. package/dist/cli.js.map +1 -1
  169. package/dist/dashboard/server.d.ts +6 -0
  170. package/dist/dashboard/server.d.ts.map +1 -1
  171. package/dist/dashboard/server.js +1118 -368
  172. package/dist/dashboard/server.js.map +1 -1
  173. package/dist/dashboard/v6-dashboard-html.d.ts +2 -0
  174. package/dist/dashboard/v6-dashboard-html.d.ts.map +1 -0
  175. package/dist/dashboard/v6-dashboard-html.js +2474 -0
  176. package/dist/dashboard/v6-dashboard-html.js.map +1 -0
  177. package/dist/index.d.ts +27 -0
  178. package/dist/index.d.ts.map +1 -1
  179. package/dist/index.js +29 -1
  180. package/dist/index.js.map +1 -1
  181. package/dist/types.d.ts +644 -1
  182. package/dist/types.d.ts.map +1 -1
  183. package/package.json +77 -12
@@ -0,0 +1,706 @@
1
+ // src/brain/session-hooks.ts — Universal Session Hook Installer (v5.2.0)
2
+ //
3
+ // Installs SessionStart-equivalent hooks for every supported AI coding agent so
4
+ // that Shadow Brain is the FIRST thing each agent calls when a new session begins.
5
+ //
6
+ // Each agent has a different hook mechanism — this module abstracts them:
7
+ //
8
+ // Claude Code → ~/.claude/settings.json hooks.SessionStart
9
+ // Cursor → .cursor/rules + global ~/.cursor/mcp.json
10
+ // Cline → ~/.vscode/User/cline_mcp_settings.json
11
+ // Windsurf → .windsurfrules + ~/.windsurf/mcp.json
12
+ // Codex → ~/.codex/config.json
13
+ // Kilo Code → ~/.kilocode/settings.json
14
+ // OpenCode → ~/.opencode/settings.json
15
+ // Roo Code → ~/.roocode/mcp.json
16
+ // Aider → ~/.aider.conf.yml load_md
17
+ // Copilot → .github/copilot-instructions.md (workspace) + user settings.json
18
+ //
19
+ // Falls back gracefully when a specific agent's hook system isn't available.
20
+ import * as fs from 'fs';
21
+ import * as path from 'path';
22
+ import * as os from 'os';
23
+ const SHADOW_BRAIN_HOOK_CMD = 'shadow-brain subconscious inject';
24
+ const SHADOW_BRAIN_MCP_NAME = 'shadow-brain';
25
+ const SHADOW_BRAIN_MCP_CONFIG = {
26
+ command: 'npx',
27
+ args: ['-y', '@theihtisham/agent-shadow-brain', 'mcp', '--stdio'],
28
+ };
29
+ export class SessionHookInstaller {
30
+ /** Detect installed agents on this machine + project */
31
+ async detectInstalled(projectDir) {
32
+ const detected = [];
33
+ const home = os.homedir();
34
+ const checks = [
35
+ { agent: 'claude-code', paths: [path.join(home, '.claude'), path.join(projectDir, '.claude')] },
36
+ { agent: 'cursor', paths: [path.join(home, '.cursor'), path.join(projectDir, '.cursor'), path.join(projectDir, '.cursorrules')] },
37
+ { agent: 'cline', paths: [path.join(home, 'AppData', 'Roaming', 'Code', 'User'), path.join(home, '.vscode')] },
38
+ { agent: 'windsurf', paths: [path.join(home, '.windsurf'), path.join(projectDir, '.windsurfrules')] },
39
+ { agent: 'codex', paths: [path.join(home, '.codex'), path.join(projectDir, '.codex')] },
40
+ { agent: 'kilo-code', paths: [path.join(home, '.kilocode'), path.join(projectDir, '.kilocode')] },
41
+ { agent: 'opencode', paths: [path.join(home, '.opencode'), path.join(projectDir, '.opencode')] },
42
+ { agent: 'roo-code', paths: [path.join(home, '.roocode'), path.join(home, '.rovodev')] },
43
+ { agent: 'aider', paths: [path.join(home, '.aider.conf.yml'), path.join(projectDir, '.aider.conf.yml')] },
44
+ { agent: 'copilot', paths: [path.join(projectDir, '.github', 'copilot-instructions.md'), path.join(home, '.config', 'copilot')] },
45
+ ];
46
+ for (const { agent, paths } of checks) {
47
+ if (paths.some(p => fs.existsSync(p))) {
48
+ detected.push(agent);
49
+ }
50
+ }
51
+ return detected;
52
+ }
53
+ /** Install hooks for ALL detected agents in one shot */
54
+ async attachAll(projectDir) {
55
+ const start = Date.now();
56
+ const detected = await this.detectInstalled(projectDir);
57
+ const attached = [];
58
+ const failed = [];
59
+ const hooks = [];
60
+ for (const agent of detected) {
61
+ try {
62
+ const installed = await this.attach(agent, projectDir);
63
+ if (installed.length) {
64
+ attached.push(agent);
65
+ hooks.push(...installed);
66
+ }
67
+ else {
68
+ failed.push({ agent, reason: 'no hook installed (no supported mechanism)' });
69
+ }
70
+ }
71
+ catch (err) {
72
+ failed.push({ agent, reason: err instanceof Error ? err.message : String(err) });
73
+ }
74
+ }
75
+ return {
76
+ detected,
77
+ attached,
78
+ failed,
79
+ hooks,
80
+ totalAgents: detected.length,
81
+ durationMs: Date.now() - start,
82
+ };
83
+ }
84
+ /** Best-effort non-mutating audit of Shadow Brain hook artifacts. */
85
+ async audit(projectDir) {
86
+ const detected = [];
87
+ const agents = await this.detectInstalled(projectDir);
88
+ for (const agent of agents) {
89
+ detected.push(...this.findInstalledHooks(agent, projectDir));
90
+ }
91
+ return detected;
92
+ }
93
+ /** Detach Shadow Brain from every detected agent without creating new hooks. */
94
+ async detachAll(projectDir) {
95
+ const start = Date.now();
96
+ const detected = await this.detectInstalled(projectDir);
97
+ const detached = [];
98
+ const failed = [];
99
+ const hooks = [];
100
+ for (const agent of detected) {
101
+ try {
102
+ const before = this.findInstalledHooks(agent, projectDir);
103
+ const ok = await this.detach(agent, projectDir);
104
+ if (ok) {
105
+ detached.push(agent);
106
+ hooks.push(...before);
107
+ }
108
+ else {
109
+ failed.push({ agent, reason: 'no Shadow Brain hook found' });
110
+ }
111
+ }
112
+ catch (err) {
113
+ failed.push({ agent, reason: err instanceof Error ? err.message : String(err) });
114
+ }
115
+ }
116
+ return {
117
+ detected,
118
+ attached: detached,
119
+ failed,
120
+ hooks,
121
+ totalAgents: detected.length,
122
+ durationMs: Date.now() - start,
123
+ };
124
+ }
125
+ /** Install hooks for a single agent. Returns list of installed hooks. */
126
+ async attach(agent, projectDir) {
127
+ switch (agent) {
128
+ case 'claude-code': return this.attachClaudeCode(projectDir);
129
+ case 'cursor': return this.attachCursor(projectDir);
130
+ case 'cline': return this.attachCline(projectDir);
131
+ case 'windsurf': return this.attachWindsurf(projectDir);
132
+ case 'codex': return this.attachCodex(projectDir);
133
+ case 'kilo-code': return this.attachKiloCode(projectDir);
134
+ case 'opencode': return this.attachOpenCode(projectDir);
135
+ case 'roo-code': return this.attachRooCode(projectDir);
136
+ case 'aider': return this.attachAider(projectDir);
137
+ case 'copilot': return this.attachCopilot(projectDir);
138
+ default: return [];
139
+ }
140
+ }
141
+ /** Detach Shadow Brain from a specific agent */
142
+ async detach(agent, projectDir) {
143
+ const hooks = this.findInstalledHooks(agent, projectDir);
144
+ if (!hooks.length)
145
+ return false;
146
+ for (const hook of hooks) {
147
+ try {
148
+ if (!fs.existsSync(hook.installPath))
149
+ continue;
150
+ if (hook.hookType === 'workspace-rule') {
151
+ if (path.basename(hook.installPath).includes('shadow-brain')) {
152
+ fs.unlinkSync(hook.installPath);
153
+ }
154
+ else {
155
+ this.removeShadowBrainBlock(hook.installPath);
156
+ }
157
+ }
158
+ else {
159
+ this.removeFromJsonConfig(hook.installPath);
160
+ }
161
+ }
162
+ catch { /* best effort */ }
163
+ }
164
+ return true;
165
+ }
166
+ // ─── Per-agent installers ───────────────────────────────────────────────────
167
+ attachClaudeCode(projectDir) {
168
+ const home = os.homedir();
169
+ const settingsPath = path.join(home, '.claude', 'settings.json');
170
+ const hooks = [];
171
+ try {
172
+ let settings = {};
173
+ if (fs.existsSync(settingsPath)) {
174
+ try {
175
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8')) ?? {};
176
+ }
177
+ catch {
178
+ settings = {};
179
+ }
180
+ }
181
+ // FIRST — repair any malformed entries from older Shadow Brain versions.
182
+ // Claude Code schema: hooks.SessionStart is an array of { matcher, hooks[] } blocks.
183
+ settings.hooks ?? (settings.hooks = {});
184
+ if (!Array.isArray(settings.hooks.SessionStart))
185
+ settings.hooks.SessionStart = [];
186
+ settings.hooks.SessionStart = this.repairClaudeHookBlocks(settings.hooks.SessionStart);
187
+ // Check if our hook already exists anywhere in a well-formed block.
188
+ let present = false;
189
+ for (const block of settings.hooks.SessionStart) {
190
+ if (!block || !Array.isArray(block.hooks))
191
+ continue;
192
+ if (block.hooks.some((h) => h && typeof h.command === 'string' && h.command.includes('shadow-brain'))) {
193
+ present = true;
194
+ break;
195
+ }
196
+ }
197
+ if (!present) {
198
+ // Install into a matcher-less block so the hook runs for every session.
199
+ // Prefer merging into an existing empty-matcher block if one exists.
200
+ let target = settings.hooks.SessionStart.find((b) => b && (b.matcher === '' || b.matcher == null));
201
+ if (!target) {
202
+ target = { matcher: '', hooks: [] };
203
+ settings.hooks.SessionStart.push(target);
204
+ }
205
+ if (!Array.isArray(target.hooks))
206
+ target.hooks = [];
207
+ target.hooks.push({ type: 'command', command: SHADOW_BRAIN_HOOK_CMD, timeout: 5000 });
208
+ fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
209
+ // Atomic write — prevents half-written settings breaking Claude Code
210
+ const tmp = settingsPath + '.tmp';
211
+ fs.writeFileSync(tmp, JSON.stringify(settings, null, 2));
212
+ fs.renameSync(tmp, settingsPath);
213
+ }
214
+ else {
215
+ // Still ensure the (previously malformed) file is now valid.
216
+ const tmp = settingsPath + '.tmp';
217
+ fs.writeFileSync(tmp, JSON.stringify(settings, null, 2));
218
+ fs.renameSync(tmp, settingsPath);
219
+ }
220
+ hooks.push({
221
+ agent: 'claude-code',
222
+ event: 'session-start',
223
+ hookType: 'settings-json',
224
+ installPath: settingsPath,
225
+ command: SHADOW_BRAIN_HOOK_CMD,
226
+ active: true,
227
+ installedAt: new Date(),
228
+ });
229
+ }
230
+ catch (err) {
231
+ throw new Error(`claude-code attach failed: ${err}`);
232
+ }
233
+ return hooks;
234
+ }
235
+ attachCursor(projectDir) {
236
+ const hooks = [];
237
+ const cursorDir = path.join(projectDir, '.cursor');
238
+ const rulesDir = path.join(cursorDir, 'rules');
239
+ const mcpPath = path.join(cursorDir, 'mcp.json');
240
+ fs.mkdirSync(rulesDir, { recursive: true });
241
+ // 1. Workspace rule that tells Cursor to call shadow-brain on session
242
+ const ruleFile = path.join(rulesDir, 'shadow-brain.md');
243
+ if (!fs.existsSync(ruleFile)) {
244
+ fs.writeFileSync(ruleFile, this.cursorRuleTemplate(), 'utf-8');
245
+ }
246
+ hooks.push({
247
+ agent: 'cursor',
248
+ event: 'session-start',
249
+ hookType: 'workspace-rule',
250
+ installPath: ruleFile,
251
+ command: SHADOW_BRAIN_HOOK_CMD,
252
+ active: true,
253
+ installedAt: new Date(),
254
+ });
255
+ // 2. MCP server registration
256
+ this.upsertMcpJson(mcpPath);
257
+ hooks.push({
258
+ agent: 'cursor',
259
+ event: 'session-start',
260
+ hookType: 'extension-config',
261
+ installPath: mcpPath,
262
+ command: SHADOW_BRAIN_MCP_CONFIG.command,
263
+ active: true,
264
+ installedAt: new Date(),
265
+ });
266
+ return hooks;
267
+ }
268
+ attachCline(projectDir) {
269
+ const home = os.homedir();
270
+ const candidates = [
271
+ path.join(home, 'AppData', 'Roaming', 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'),
272
+ path.join(home, 'Library', 'Application Support', 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'),
273
+ path.join(home, '.config', 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'),
274
+ ];
275
+ const hooks = [];
276
+ for (const settingsPath of candidates) {
277
+ const dir = path.dirname(settingsPath);
278
+ if (!fs.existsSync(path.dirname(dir)))
279
+ continue;
280
+ try {
281
+ fs.mkdirSync(dir, { recursive: true });
282
+ this.upsertMcpJson(settingsPath, 'mcpServers');
283
+ hooks.push({
284
+ agent: 'cline',
285
+ event: 'session-start',
286
+ hookType: 'extension-config',
287
+ installPath: settingsPath,
288
+ command: SHADOW_BRAIN_MCP_CONFIG.command,
289
+ active: true,
290
+ installedAt: new Date(),
291
+ });
292
+ break; // Only one location per OS
293
+ }
294
+ catch { /* try next */ }
295
+ }
296
+ // Workspace fallback rule
297
+ const projectRule = path.join(projectDir, '.clinerules');
298
+ if (!fs.existsSync(projectRule)) {
299
+ fs.writeFileSync(projectRule, this.genericRuleTemplate('Cline'), 'utf-8');
300
+ }
301
+ hooks.push({
302
+ agent: 'cline',
303
+ event: 'session-start',
304
+ hookType: 'workspace-rule',
305
+ installPath: projectRule,
306
+ command: SHADOW_BRAIN_HOOK_CMD,
307
+ active: true,
308
+ installedAt: new Date(),
309
+ });
310
+ return hooks;
311
+ }
312
+ attachWindsurf(projectDir) {
313
+ const home = os.homedir();
314
+ const hooks = [];
315
+ const ruleFile = path.join(projectDir, '.windsurfrules');
316
+ if (!fs.existsSync(ruleFile)) {
317
+ fs.writeFileSync(ruleFile, this.genericRuleTemplate('Windsurf'), 'utf-8');
318
+ }
319
+ hooks.push({
320
+ agent: 'windsurf',
321
+ event: 'session-start',
322
+ hookType: 'workspace-rule',
323
+ installPath: ruleFile,
324
+ command: SHADOW_BRAIN_HOOK_CMD,
325
+ active: true,
326
+ installedAt: new Date(),
327
+ });
328
+ const mcpPath = path.join(home, '.windsurf', 'mcp.json');
329
+ fs.mkdirSync(path.dirname(mcpPath), { recursive: true });
330
+ this.upsertMcpJson(mcpPath);
331
+ hooks.push({
332
+ agent: 'windsurf',
333
+ event: 'session-start',
334
+ hookType: 'extension-config',
335
+ installPath: mcpPath,
336
+ command: SHADOW_BRAIN_MCP_CONFIG.command,
337
+ active: true,
338
+ installedAt: new Date(),
339
+ });
340
+ return hooks;
341
+ }
342
+ attachCodex(projectDir) {
343
+ const home = os.homedir();
344
+ const cfgPath = path.join(home, '.codex', 'config.json');
345
+ fs.mkdirSync(path.dirname(cfgPath), { recursive: true });
346
+ let cfg = {};
347
+ if (fs.existsSync(cfgPath)) {
348
+ try {
349
+ cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf-8'));
350
+ }
351
+ catch {
352
+ cfg = {};
353
+ }
354
+ }
355
+ cfg.session_start_command = SHADOW_BRAIN_HOOK_CMD;
356
+ cfg.mcp_servers ?? (cfg.mcp_servers = {});
357
+ cfg.mcp_servers[SHADOW_BRAIN_MCP_NAME] = SHADOW_BRAIN_MCP_CONFIG;
358
+ fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
359
+ return [{
360
+ agent: 'codex',
361
+ event: 'session-start',
362
+ hookType: 'config-file',
363
+ installPath: cfgPath,
364
+ command: SHADOW_BRAIN_HOOK_CMD,
365
+ active: true,
366
+ installedAt: new Date(),
367
+ }];
368
+ }
369
+ attachKiloCode(projectDir) {
370
+ const home = os.homedir();
371
+ const settingsPath = path.join(home, '.kilocode', 'settings.json');
372
+ fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
373
+ let cfg = {};
374
+ if (fs.existsSync(settingsPath)) {
375
+ try {
376
+ cfg = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
377
+ }
378
+ catch {
379
+ cfg = {};
380
+ }
381
+ }
382
+ cfg.mcpServers ?? (cfg.mcpServers = {});
383
+ cfg.mcpServers[SHADOW_BRAIN_MCP_NAME] = SHADOW_BRAIN_MCP_CONFIG;
384
+ cfg.sessionStartHook = SHADOW_BRAIN_HOOK_CMD;
385
+ fs.writeFileSync(settingsPath, JSON.stringify(cfg, null, 2));
386
+ return [{
387
+ agent: 'kilo-code',
388
+ event: 'session-start',
389
+ hookType: 'settings-json',
390
+ installPath: settingsPath,
391
+ command: SHADOW_BRAIN_HOOK_CMD,
392
+ active: true,
393
+ installedAt: new Date(),
394
+ }];
395
+ }
396
+ attachOpenCode(projectDir) {
397
+ const home = os.homedir();
398
+ const settingsPath = path.join(home, '.opencode', 'settings.json');
399
+ fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
400
+ let cfg = {};
401
+ if (fs.existsSync(settingsPath)) {
402
+ try {
403
+ cfg = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
404
+ }
405
+ catch {
406
+ cfg = {};
407
+ }
408
+ }
409
+ cfg.mcpServers ?? (cfg.mcpServers = {});
410
+ cfg.mcpServers[SHADOW_BRAIN_MCP_NAME] = SHADOW_BRAIN_MCP_CONFIG;
411
+ fs.writeFileSync(settingsPath, JSON.stringify(cfg, null, 2));
412
+ return [{
413
+ agent: 'opencode',
414
+ event: 'session-start',
415
+ hookType: 'settings-json',
416
+ installPath: settingsPath,
417
+ command: SHADOW_BRAIN_MCP_CONFIG.command,
418
+ active: true,
419
+ installedAt: new Date(),
420
+ }];
421
+ }
422
+ attachRooCode(projectDir) {
423
+ const home = os.homedir();
424
+ const candidates = [
425
+ path.join(home, '.roocode', 'mcp.json'),
426
+ path.join(home, '.rovodev', 'mcp.json'),
427
+ ];
428
+ const hooks = [];
429
+ for (const mcpPath of candidates) {
430
+ try {
431
+ fs.mkdirSync(path.dirname(mcpPath), { recursive: true });
432
+ this.upsertMcpJson(mcpPath);
433
+ hooks.push({
434
+ agent: 'roo-code',
435
+ event: 'session-start',
436
+ hookType: 'extension-config',
437
+ installPath: mcpPath,
438
+ command: SHADOW_BRAIN_MCP_CONFIG.command,
439
+ active: true,
440
+ installedAt: new Date(),
441
+ });
442
+ break;
443
+ }
444
+ catch { /* try next */ }
445
+ }
446
+ return hooks;
447
+ }
448
+ attachAider(projectDir) {
449
+ const home = os.homedir();
450
+ const cfgPath = path.join(home, '.aider.conf.yml');
451
+ const briefingPath = path.join(home, '.aider.shadow-brain.md');
452
+ // Write the briefing wrapper file Aider will load via load_md
453
+ fs.writeFileSync(briefingPath, '# Shadow Brain\n\nThis file is rewritten on each Shadow Brain session-start hook.\n');
454
+ let cfg = '';
455
+ if (fs.existsSync(cfgPath)) {
456
+ cfg = fs.readFileSync(cfgPath, 'utf-8');
457
+ }
458
+ if (!cfg.includes('.aider.shadow-brain.md')) {
459
+ cfg += `\n# Shadow Brain auto-injected\nread:\n - ${briefingPath}\n`;
460
+ fs.writeFileSync(cfgPath, cfg);
461
+ }
462
+ return [{
463
+ agent: 'aider',
464
+ event: 'session-start',
465
+ hookType: 'config-file',
466
+ installPath: cfgPath,
467
+ command: SHADOW_BRAIN_HOOK_CMD,
468
+ active: true,
469
+ installedAt: new Date(),
470
+ }];
471
+ }
472
+ attachCopilot(projectDir) {
473
+ // GitHub Copilot reads .github/copilot-instructions.md as workspace context.
474
+ const githubDir = path.join(projectDir, '.github');
475
+ fs.mkdirSync(githubDir, { recursive: true });
476
+ const instrPath = path.join(githubDir, 'copilot-instructions.md');
477
+ if (!fs.existsSync(instrPath)) {
478
+ fs.writeFileSync(instrPath, this.copilotInstructionsTemplate(), 'utf-8');
479
+ }
480
+ else {
481
+ // Append marker if not present
482
+ const existing = fs.readFileSync(instrPath, 'utf-8');
483
+ if (!existing.includes('Shadow Brain')) {
484
+ fs.appendFileSync(instrPath, `\n\n---\n\n${this.copilotInstructionsTemplate()}`);
485
+ }
486
+ }
487
+ return [{
488
+ agent: 'copilot',
489
+ event: 'session-start',
490
+ hookType: 'workspace-rule',
491
+ installPath: instrPath,
492
+ command: SHADOW_BRAIN_HOOK_CMD,
493
+ active: true,
494
+ installedAt: new Date(),
495
+ }];
496
+ }
497
+ // ─── Helpers ────────────────────────────────────────────────────────────────
498
+ /** Add Shadow Brain to an MCP-server-style JSON config without clobbering existing servers */
499
+ upsertMcpJson(jsonPath, key = 'mcpServers') {
500
+ let cfg = {};
501
+ if (fs.existsSync(jsonPath)) {
502
+ try {
503
+ cfg = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'));
504
+ }
505
+ catch {
506
+ cfg = {};
507
+ }
508
+ }
509
+ cfg[key] ?? (cfg[key] = {});
510
+ cfg[key][SHADOW_BRAIN_MCP_NAME] = SHADOW_BRAIN_MCP_CONFIG;
511
+ fs.mkdirSync(path.dirname(jsonPath), { recursive: true });
512
+ fs.writeFileSync(jsonPath, JSON.stringify(cfg, null, 2));
513
+ }
514
+ findInstalledHooks(agent, projectDir) {
515
+ const home = os.homedir();
516
+ const candidates = [];
517
+ switch (agent) {
518
+ case 'claude-code':
519
+ candidates.push({ path: path.join(home, '.claude', 'settings.json'), hookType: 'settings-json' });
520
+ break;
521
+ case 'cursor':
522
+ candidates.push({ path: path.join(projectDir, '.cursor', 'rules', 'shadow-brain.md'), hookType: 'workspace-rule' }, { path: path.join(projectDir, '.cursor', 'mcp.json'), hookType: 'extension-config' });
523
+ break;
524
+ case 'cline':
525
+ candidates.push({ path: path.join(home, 'AppData', 'Roaming', 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'), hookType: 'extension-config' }, { path: path.join(home, 'Library', 'Application Support', 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'), hookType: 'extension-config' }, { path: path.join(home, '.config', 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'), hookType: 'extension-config' }, { path: path.join(projectDir, '.clinerules'), hookType: 'workspace-rule' });
526
+ break;
527
+ case 'windsurf':
528
+ candidates.push({ path: path.join(projectDir, '.windsurfrules'), hookType: 'workspace-rule' }, { path: path.join(home, '.windsurf', 'mcp.json'), hookType: 'extension-config' });
529
+ break;
530
+ case 'codex':
531
+ candidates.push({ path: path.join(home, '.codex', 'config.json'), hookType: 'config-file' });
532
+ break;
533
+ case 'kilo-code':
534
+ candidates.push({ path: path.join(home, '.kilocode', 'settings.json'), hookType: 'settings-json' });
535
+ break;
536
+ case 'opencode':
537
+ candidates.push({ path: path.join(home, '.opencode', 'settings.json'), hookType: 'settings-json' });
538
+ break;
539
+ case 'roo-code':
540
+ candidates.push({ path: path.join(home, '.roocode', 'mcp.json'), hookType: 'extension-config' }, { path: path.join(home, '.rovodev', 'mcp.json'), hookType: 'extension-config' });
541
+ break;
542
+ case 'aider':
543
+ candidates.push({ path: path.join(home, '.aider.conf.yml'), hookType: 'config-file' }, { path: path.join(home, '.aider.shadow-brain.md'), hookType: 'workspace-rule' });
544
+ break;
545
+ case 'copilot':
546
+ candidates.push({ path: path.join(projectDir, '.github', 'copilot-instructions.md'), hookType: 'workspace-rule' });
547
+ break;
548
+ }
549
+ return candidates
550
+ .filter(c => this.containsShadowBrain(c.path))
551
+ .map(c => ({
552
+ agent,
553
+ event: 'session-start',
554
+ hookType: c.hookType,
555
+ installPath: c.path,
556
+ command: c.hookType === 'workspace-rule' ? SHADOW_BRAIN_HOOK_CMD : SHADOW_BRAIN_MCP_CONFIG.command,
557
+ active: true,
558
+ installedAt: new Date(),
559
+ }));
560
+ }
561
+ containsShadowBrain(filePath) {
562
+ try {
563
+ return fs.existsSync(filePath) && fs.readFileSync(filePath, 'utf-8').includes('shadow-brain');
564
+ }
565
+ catch {
566
+ return false;
567
+ }
568
+ }
569
+ removeShadowBrainBlock(filePath) {
570
+ const content = fs.readFileSync(filePath, 'utf-8');
571
+ const cleaned = content
572
+ .replace(/\n?# Shadow Brain auto-injected\nread:\n\s+- .+?\.aider\.shadow-brain\.md\n?/gs, '\n')
573
+ .replace(/\n?---\n\n## Shadow Brain Context[\s\S]*?(?=\n---|\n## |\s*$)/g, '\n')
574
+ .replace(/\n?# Shadow Brain Integration \([^)]+\)[\s\S]*?Run `shadow-brain status` for state\.\n?/g, '\n')
575
+ .replace(/\n?# === Shadow Brain Critical Alerts ===[\s\S]*?(?=\n# |\s*$)/g, '\n')
576
+ .trimEnd() + '\n';
577
+ if (cleaned.trim())
578
+ fs.writeFileSync(filePath, cleaned, 'utf-8');
579
+ else
580
+ fs.unlinkSync(filePath);
581
+ }
582
+ removeFromJsonConfig(jsonPath) {
583
+ let cfg = {};
584
+ try {
585
+ cfg = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'));
586
+ }
587
+ catch {
588
+ return;
589
+ }
590
+ if (cfg.mcpServers)
591
+ delete cfg.mcpServers[SHADOW_BRAIN_MCP_NAME];
592
+ if (cfg.mcp_servers)
593
+ delete cfg.mcp_servers[SHADOW_BRAIN_MCP_NAME];
594
+ if (cfg.servers)
595
+ delete cfg.servers[SHADOW_BRAIN_MCP_NAME];
596
+ if (cfg.sessionStartHook === SHADOW_BRAIN_HOOK_CMD)
597
+ delete cfg.sessionStartHook;
598
+ if (cfg.session_start_command === SHADOW_BRAIN_HOOK_CMD)
599
+ delete cfg.session_start_command;
600
+ // Remove ANY Shadow Brain entry from hooks.SessionStart — handle every shape.
601
+ if (cfg.hooks?.SessionStart && Array.isArray(cfg.hooks.SessionStart)) {
602
+ cfg.hooks.SessionStart = cfg.hooks.SessionStart
603
+ .map((block) => {
604
+ if (!block || typeof block !== 'object')
605
+ return null;
606
+ // Well-formed { matcher, hooks[] } — strip our entries from the inner array
607
+ if (Array.isArray(block.hooks)) {
608
+ const filtered = block.hooks.filter((h) => !(h && typeof h.command === 'string' && h.command.includes('shadow-brain')));
609
+ if (!filtered.length)
610
+ return null;
611
+ return { ...block, hooks: filtered };
612
+ }
613
+ // Legacy flat entry — drop if it references shadow-brain
614
+ if (typeof block.command === 'string' && block.command.includes('shadow-brain'))
615
+ return null;
616
+ return block;
617
+ })
618
+ .filter((b) => b !== null);
619
+ // After stripping, run the repair once more so the file is always valid Claude schema
620
+ cfg.hooks.SessionStart = this.repairClaudeHookBlocks(cfg.hooks.SessionStart);
621
+ }
622
+ // Atomic write — prevents leaving a broken settings file
623
+ const tmp = jsonPath + '.tmp';
624
+ fs.writeFileSync(tmp, JSON.stringify(cfg, null, 2));
625
+ fs.renameSync(tmp, jsonPath);
626
+ }
627
+ /**
628
+ * Claude Code schema requires each SessionStart entry to have shape:
629
+ * { matcher: string, hooks: [{ type: 'command', command: '...' }] }
630
+ * Older Shadow Brain versions mistakenly wrote flat { name, command, timeout }
631
+ * entries directly, which broke Claude Code's settings loader. This function:
632
+ * - drops null/undefined/non-object entries
633
+ * - rewraps flat "legacy" entries into a valid { matcher, hooks } block
634
+ * - ensures every block.hooks is a valid array of { type:'command', command }
635
+ * - strips any nested malformed entries
636
+ */
637
+ repairClaudeHookBlocks(blocks) {
638
+ if (!Array.isArray(blocks))
639
+ return [];
640
+ const out = [];
641
+ for (const block of blocks) {
642
+ if (block == null || typeof block !== 'object')
643
+ continue;
644
+ if (Array.isArray(block.hooks)) {
645
+ const cleanHooks = block.hooks
646
+ .filter((h) => h && typeof h === 'object')
647
+ .map((h) => ({
648
+ type: typeof h.type === 'string' ? h.type : 'command',
649
+ command: typeof h.command === 'string' ? h.command : '',
650
+ ...(typeof h.timeout === 'number' ? { timeout: h.timeout } : {}),
651
+ }))
652
+ .filter((h) => h.command);
653
+ if (cleanHooks.length) {
654
+ out.push({ matcher: typeof block.matcher === 'string' ? block.matcher : '', hooks: cleanHooks });
655
+ }
656
+ }
657
+ else if (typeof block.command === 'string') {
658
+ out.push({
659
+ matcher: '',
660
+ hooks: [{ type: 'command', command: block.command, ...(typeof block.timeout === 'number' ? { timeout: block.timeout } : {}) }],
661
+ });
662
+ }
663
+ }
664
+ return out;
665
+ }
666
+ cursorRuleTemplate() {
667
+ return `# Shadow Brain — Always-On Context Layer
668
+
669
+ This project is augmented by Shadow Brain (https://github.com/theihtisham/agent-shadow-brain).
670
+
671
+ At the start of every session, Shadow Brain auto-injects a briefing of:
672
+ - Recent project decisions
673
+ - Active tasks
674
+ - Cross-agent insights from Claude/Cline/Codex
675
+ - Known warnings and patterns
676
+
677
+ To check what Shadow Brain knows about this project: \`shadow-brain recall\`
678
+ To view the global brain stats: \`shadow-brain global stats\`
679
+ `;
680
+ }
681
+ genericRuleTemplate(agentName) {
682
+ return `# Shadow Brain Integration (${agentName})
683
+
684
+ Shadow Brain is active for this project. It maintains a singleton global brain
685
+ across all your AI coding agents — what one agent learns, all agents know.
686
+
687
+ Auto-injects context on session start. Run \`shadow-brain status\` for state.
688
+ `;
689
+ }
690
+ copilotInstructionsTemplate() {
691
+ return `## Shadow Brain Context
692
+
693
+ This workspace is monitored by Shadow Brain — a cross-agent intelligence layer.
694
+ When making suggestions, prefer patterns documented in the Shadow Brain memory at \`.shadow-brain/\` over generic patterns.
695
+
696
+ Project-specific decisions, conventions, and warnings are recorded in \`.shadow-brain/SHADOW_BRAIN.md\`.
697
+ `;
698
+ }
699
+ }
700
+ let defaultInstaller = null;
701
+ export function getHookInstaller() {
702
+ if (!defaultInstaller)
703
+ defaultInstaller = new SessionHookInstaller();
704
+ return defaultInstaller;
705
+ }
706
+ //# sourceMappingURL=session-hooks.js.map