specmem-hardwicksoftware 3.7.35 → 3.7.38

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 (71) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +11 -15
  3. package/bin/specmem-autoclaude.cjs +12 -1
  4. package/bin/specmem-cli.cjs +1077 -11
  5. package/bin/specmem-console.cjs +890 -63
  6. package/bootstrap.cjs +10 -2
  7. package/claude-hooks/agent-loading-hook.cjs +16 -16
  8. package/claude-hooks/agent-loading-hook.js +28 -21
  9. package/claude-hooks/agent-type-matcher.js +1 -1
  10. package/claude-hooks/background-completion-silencer.js +1 -1
  11. package/claude-hooks/file-claim-enforcer.cjs +37 -36
  12. package/claude-hooks/output-cleaner.cjs +1 -1
  13. package/claude-hooks/refusal-detector-hook.cjs +53 -0
  14. package/claude-hooks/settings.json +64 -4
  15. package/claude-hooks/smart-search-interceptor.js +1 -1
  16. package/claude-hooks/specmem-search-enforcer.cjs +2 -11
  17. package/claude-hooks/specmem-team-member-inject.js +1 -1
  18. package/claude-hooks/specmem-unified-hook.py +1 -1
  19. package/claude-hooks/subagent-loading-hook.cjs +1 -1
  20. package/claude-hooks/task-progress-hook.cjs +7 -7
  21. package/claude-hooks/task-progress-hook.js +3 -3
  22. package/claude-hooks/team-comms-enforcer.cjs +113 -47
  23. package/claude-hooks/use-code-pointers.cjs +1 -1
  24. package/dist/claude-sessions/sessionParser.js +5 -0
  25. package/dist/cli/deploy-to-claude.js +9 -2
  26. package/dist/codebase/codebaseIndexer.js +48 -17
  27. package/dist/codebase/exclusions.js +3 -4
  28. package/dist/codebase/index.js +4 -0
  29. package/dist/codebase/pdfExtractor.js +298 -0
  30. package/dist/dashboard/api/taskTeamMembers.js +2 -2
  31. package/dist/db/bigBrainMigrations.js +29 -0
  32. package/dist/hooks/hookManager.js +4 -4
  33. package/dist/hooks/teamFramingCli.js +1 -1
  34. package/dist/hooks/teamMemberPrepromptHook.js +5 -5
  35. package/dist/index.js +49 -12
  36. package/dist/init/claudeConfigInjector.js +27 -8
  37. package/dist/installer/autoInstall.js +7 -1
  38. package/dist/mcp/compactionProxy.js +1052 -192
  39. package/dist/mcp/compactionProxyDaemon.js +112 -37
  40. package/dist/mcp/contextVault.js +439 -0
  41. package/dist/mcp/embeddingServerManager.js +151 -17
  42. package/dist/mcp/mcpProtocolHandler.js +6 -1
  43. package/dist/mcp/miniCOTServerManager.js +82 -8
  44. package/dist/mcp/specMemServer.js +45 -10
  45. package/dist/mcp/toolRegistry.js +6 -0
  46. package/dist/startup/startupIndexing.js +14 -0
  47. package/dist/team-members/taskOrchestrator.js +3 -3
  48. package/dist/team-members/taskTeamMemberLogger.js +2 -2
  49. package/dist/tools/goofy/deployTeamMember.js +3 -3
  50. package/dist/tools/goofy/digInTheVault.js +81 -0
  51. package/dist/tools/goofy/findCodePointers.js +17 -0
  52. package/dist/tools/goofy/findWhatISaid.js +19 -0
  53. package/dist/tools/goofy/stashTheGoods.js +56 -0
  54. package/dist/tools/teamMemberDeployer.js +2 -2
  55. package/dist/watcher/changeHandler.js +65 -8
  56. package/dist/watcher/changeQueue.js +20 -1
  57. package/embedding-sandbox/frankenstein-embeddings.py +4 -3
  58. package/embedding-sandbox/mini-cot-service.py +11 -13
  59. package/embedding-sandbox/pdf-text-extract.py +208 -0
  60. package/package.json +1 -1
  61. package/scripts/deploy-hooks.cjs +12 -4
  62. package/scripts/fast-batch-embedder.cjs +2 -2
  63. package/scripts/force-retry.cjs +34 -0
  64. package/scripts/global-postinstall.cjs +97 -4
  65. package/scripts/poetic-abliteration.cjs +379 -0
  66. package/scripts/refusal-enforcer.cjs +88 -0
  67. package/scripts/specmem-init.cjs +222 -41
  68. package/specmem/model-config.json +6 -6
  69. package/specmem/supervisord.conf +1 -1
  70. package/svg-sections/readme-token-compaction.svg +246 -0
  71. package/claude-hooks/agent-chooser-hook.js +0 -179
@@ -0,0 +1,246 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 1020" width="800" height="1020">
2
+ <defs>
3
+ <linearGradient id="bgGrad" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" style="stop-color:#0d1117"/>
5
+ <stop offset="100%" style="stop-color:#161b22"/>
6
+ </linearGradient>
7
+ <linearGradient id="cardGrad" x1="0%" y1="0%" x2="100%" y2="100%">
8
+ <stop offset="0%" style="stop-color:#1c2128;stop-opacity:0.9"/>
9
+ <stop offset="100%" style="stop-color:#21262d;stop-opacity:0.85"/>
10
+ </linearGradient>
11
+ <linearGradient id="titleGrad" x1="0%" y1="0%" x2="100%" y2="0%">
12
+ <stop offset="0%" style="stop-color:#00bfff"/>
13
+ <stop offset="50%" style="stop-color:#a855f7"/>
14
+ <stop offset="100%" style="stop-color:#00bfff"/>
15
+ </linearGradient>
16
+ <linearGradient id="cyanPurple" x1="0%" y1="0%" x2="100%" y2="0%">
17
+ <stop offset="0%" style="stop-color:#00bfff"/>
18
+ <stop offset="100%" style="stop-color:#a855f7"/>
19
+ </linearGradient>
20
+ <linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="0%">
21
+ <stop offset="0%" style="stop-color:#22c55e"/>
22
+ <stop offset="100%" style="stop-color:#4ade80"/>
23
+ </linearGradient>
24
+ <linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="0%">
25
+ <stop offset="0%" style="stop-color:#f59e0b"/>
26
+ <stop offset="100%" style="stop-color:#f78534"/>
27
+ </linearGradient>
28
+ <filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
29
+ <feGaussianBlur in="SourceAlpha" stdDeviation="3" result="blur"/>
30
+ <feFlood flood-color="#00bfff" flood-opacity="0.3"/>
31
+ <feComposite in2="blur" operator="in"/>
32
+ <feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>
33
+ </filter>
34
+ <filter id="glowGreen" x="-20%" y="-20%" width="140%" height="140%">
35
+ <feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur"/>
36
+ <feFlood flood-color="#22c55e" flood-opacity="0.4"/>
37
+ <feComposite in2="blur" operator="in"/>
38
+ <feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>
39
+ </filter>
40
+ </defs>
41
+
42
+ <!-- Background -->
43
+ <rect width="800" height="1020" rx="16" ry="16" fill="url(#bgGrad)"/>
44
+ <rect width="800" height="1020" rx="16" ry="16" fill="none" stroke="url(#cyanPurple)" stroke-width="1" stroke-opacity="0.2"/>
45
+
46
+ <!-- Title Section -->
47
+ <text x="400" y="36" text-anchor="middle" font-family="system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif" font-size="20" font-weight="700" fill="url(#titleGrad)">Token Compaction Pipeline</text>
48
+ <text x="400" y="56" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" fill="#6e7681">5-layer MITM proxy between Claude Code and Anthropic API. Every request gets optimized.</text>
49
+
50
+ <!-- Big stat banner -->
51
+ <rect x="40" y="72" width="720" height="70" rx="12" fill="url(#cardGrad)" stroke="#22c55e" stroke-width="1.5" filter="url(#glowGreen)"/>
52
+ <text x="200" y="103" text-anchor="middle" font-family="system-ui, sans-serif" font-size="36" font-weight="800" fill="#22c55e">~60%</text>
53
+ <text x="200" y="125" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" fill="#6e7681">input token reduction</text>
54
+ <text x="400" y="103" text-anchor="middle" font-family="system-ui, sans-serif" font-size="36" font-weight="800" fill="#00bfff">5</text>
55
+ <text x="400" y="125" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" fill="#6e7681">compression layers</text>
56
+ <text x="600" y="103" text-anchor="middle" font-family="system-ui, sans-serif" font-size="36" font-weight="800" fill="#a855f7">0ms</text>
57
+ <text x="600" y="125" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" fill="#6e7681">added latency (async)</text>
58
+
59
+ <!-- Pipeline flow label -->
60
+ <text x="400" y="170" text-anchor="middle" font-family="system-ui, sans-serif" font-size="14" font-weight="600" fill="#e6edf3">Request Flow: Claude Code &#x2192; Compaction Proxy &#x2192; Anthropic API</text>
61
+
62
+ <!-- ═══════════════════════════════════════════════════════ -->
63
+ <!-- LAYER 1: Context Vault (y=188, h=126) -->
64
+ <!-- ═══════════════════════════════════════════════════════ -->
65
+ <g transform="translate(30, 188)">
66
+ <rect width="740" height="126" rx="12" fill="url(#cardGrad)" stroke="#22c55e" stroke-width="1.5"/>
67
+ <rect width="740" height="3" rx="1.5" fill="url(#greenGrad)"/>
68
+
69
+ <circle cx="36" cy="30" r="14" fill="none" stroke="#22c55e" stroke-width="1.5" stroke-opacity="0.7"/>
70
+ <text x="36" y="35" text-anchor="middle" font-family="system-ui, sans-serif" font-size="13" font-weight="700" fill="#22c55e">1</text>
71
+
72
+ <text x="60" y="34" font-family="system-ui, sans-serif" font-size="14" font-weight="700" fill="#4ade80">&#x1F3E6; Context Vault</text>
73
+ <rect x="210" y="20" width="90" height="20" rx="10" fill="#22c55e" fill-opacity="0.15" stroke="#22c55e" stroke-width="0.8" stroke-opacity="0.4"/>
74
+ <text x="255" y="34" text-anchor="middle" font-family="'SF Mono', 'Fira Code', Consolas, monospace" font-size="10" font-weight="600" fill="#4ade80">99% savings</text>
75
+
76
+ <text font-family="system-ui, sans-serif" font-size="11" fill="#8b949e">
77
+ <tspan x="24" y="58">Tool results larger than 5KB are auto-stashed to PostgreSQL with full-text indexing (tsvector).</tspan>
78
+ <tspan x="24" dy="15">Claude receives a compact receipt (~800 bytes) with preview, vocabulary, and retrieval commands.</tspan>
79
+ <tspan x="24" dy="15">On-demand retrieval via BM25-ranked search. 24h TTL with automatic expiry.</tspan>
80
+ </text>
81
+
82
+ <rect x="24" y="106" width="80" height="14" rx="3" fill="#ef4444" fill-opacity="0.2" stroke="#ef4444" stroke-width="0.8" stroke-opacity="0.4"/>
83
+ <text x="64" y="117" text-anchor="middle" font-family="'SF Mono', monospace" font-size="8" fill="#f87171">315KB result</text>
84
+ <text x="118" y="117" font-family="system-ui, sans-serif" font-size="11" fill="#484f58">&#x2192;</text>
85
+ <rect x="138" y="106" width="68" height="14" rx="3" fill="#22c55e" fill-opacity="0.2" stroke="#22c55e" stroke-width="0.8" stroke-opacity="0.4"/>
86
+ <text x="172" y="117" text-anchor="middle" font-family="'SF Mono', monospace" font-size="8" fill="#4ade80">800B receipt</text>
87
+ <text x="225" y="117" font-family="'SF Mono', monospace" font-size="8" fill="#484f58">stash_the_goods() / dig_in_the_vault()</text>
88
+ </g>
89
+
90
+ <!-- Connector 1→2 -->
91
+ <line x1="66" y1="314" x2="66" y2="330" stroke="#30363d" stroke-width="2" stroke-dasharray="3,3"/>
92
+ <polygon points="62,328 66,336 70,328" fill="#30363d"/>
93
+
94
+ <!-- ═══════════════════════════════════════════════════════ -->
95
+ <!-- LAYER 2: Steno + YCC (y=336, h=180) -->
96
+ <!-- ═══════════════════════════════════════════════════════ -->
97
+ <g transform="translate(30, 336)">
98
+ <rect width="740" height="180" rx="12" fill="url(#cardGrad)" stroke="#00bfff" stroke-width="1.5"/>
99
+ <rect width="740" height="3" rx="1.5" fill="#00bfff"/>
100
+
101
+ <circle cx="36" cy="30" r="14" fill="none" stroke="#00bfff" stroke-width="1.5" stroke-opacity="0.7"/>
102
+ <text x="36" y="35" text-anchor="middle" font-family="system-ui, sans-serif" font-size="13" font-weight="700" fill="#00bfff">2</text>
103
+
104
+ <text x="60" y="34" font-family="system-ui, sans-serif" font-size="14" font-weight="700" fill="#00bfff">&#x1F916; Steno Compression</text>
105
+ <rect x="240" y="20" width="90" height="20" rx="10" fill="#00bfff" fill-opacity="0.12" stroke="#00bfff" stroke-width="0.8" stroke-opacity="0.4"/>
106
+ <text x="285" y="34" text-anchor="middle" font-family="'SF Mono', 'Fira Code', Consolas, monospace" font-size="10" font-weight="600" fill="#00bfff">~40% savings</text>
107
+
108
+ <text font-family="system-ui, sans-serif" font-size="11" fill="#8b949e">
109
+ <tspan x="24" y="58">Strips ~80 filler words (the, a, is, are, was, were, of, to, in, for ...) that carry minimal semantic</tspan>
110
+ <tspan x="24" dy="15">meaning. Applies ~100 programming abbreviations: function&#x2192;fn, configuration&#x2192;cfg, database&#x2192;db,</tspan>
111
+ <tspan x="24" dy="15">implementation&#x2192;impl, authentication&#x2192;auth, environment&#x2192;env, repository&#x2192;repo, and more.</tspan>
112
+ </text>
113
+
114
+ <!-- Before/after example -->
115
+ <rect x="24" y="104" width="340" height="14" rx="3" fill="#1a1f26" stroke="#21262d" stroke-width="0.8"/>
116
+ <text x="30" y="115" font-family="'SF Mono', monospace" font-size="8" fill="#6e7681">"the function configuration is required"</text>
117
+ <text x="372" y="115" font-family="system-ui, sans-serif" font-size="11" fill="#484f58">&#x2192;</text>
118
+ <rect x="390" y="104" width="130" height="14" rx="3" fill="#00bfff" fill-opacity="0.1" stroke="#00bfff" stroke-width="0.8" stroke-opacity="0.3"/>
119
+ <text x="396" y="115" font-family="'SF Mono', monospace" font-size="8" fill="#58a6ff">"fn cfg reqd"</text>
120
+ <text x="540" y="115" font-family="system-ui, sans-serif" font-size="8" fill="#484f58">KEEP_WORDS: not, no, never, must, all</text>
121
+
122
+ <!-- ── YCC Sub-layer ── -->
123
+ <line x1="24" y1="128" x2="716" y2="128" stroke="#30363d" stroke-width="0.8" stroke-dasharray="4,3"/>
124
+
125
+ <!-- YC Logo -->
126
+ <rect x="24" y="137" width="36" height="36" rx="8" fill="#a855f7" fill-opacity="0.2" stroke="#a855f7" stroke-width="1.5" stroke-opacity="0.6"/>
127
+ <text x="42" y="161" text-anchor="middle" font-family="system-ui, sans-serif" font-size="16" font-weight="900" fill="#c084fc">YC</text>
128
+
129
+ <!-- Yung Cracka Compress title + badge -->
130
+ <text x="72" y="152" font-family="system-ui, sans-serif" font-size="13" font-weight="700" fill="#c084fc">&#x1F977;&#x1F3FB; Yung Cracka Compress</text>
131
+ <rect x="260" y="140" width="68" height="18" rx="9" fill="#a855f7" fill-opacity="0.12" stroke="#a855f7" stroke-width="0.8" stroke-opacity="0.4"/>
132
+ <text x="294" y="153" text-anchor="middle" font-family="'SF Mono', monospace" font-size="9" font-weight="600" fill="#c084fc">opt-in</text>
133
+ <text x="340" y="153" font-family="'SF Mono', monospace" font-size="8" fill="#484f58">SLANG_ENABLED: false</text>
134
+
135
+ <!-- YCC description -->
136
+ <text x="72" y="172" font-family="system-ui, sans-serif" font-size="10" fill="#6e7681">DMV x Florida slang layer. 90+ entries LLMs already understand. definitely&#x2192;def, probably&#x2192;prolly, through&#x2192;thru</text>
137
+ </g>
138
+
139
+ <!-- Connector 2→3 -->
140
+ <line x1="66" y1="516" x2="66" y2="532" stroke="#30363d" stroke-width="2" stroke-dasharray="3,3"/>
141
+ <polygon points="62,530 66,538 70,530" fill="#30363d"/>
142
+
143
+ <!-- ═══════════════════════════════════════════════════════ -->
144
+ <!-- LAYER 3: Neural MT (y=538, h=142) -->
145
+ <!-- ═══════════════════════════════════════════════════════ -->
146
+ <g transform="translate(30, 538)">
147
+ <rect width="740" height="142" rx="12" fill="url(#cardGrad)" stroke="#a855f7" stroke-width="1.5"/>
148
+ <rect width="740" height="3" rx="1.5" fill="#a855f7"/>
149
+
150
+ <circle cx="36" cy="30" r="14" fill="none" stroke="#a855f7" stroke-width="1.5" stroke-opacity="0.7"/>
151
+ <text x="36" y="35" text-anchor="middle" font-family="system-ui, sans-serif" font-size="13" font-weight="700" fill="#a855f7">3</text>
152
+
153
+ <text x="60" y="34" font-family="system-ui, sans-serif" font-size="14" font-weight="700" fill="#a855f7">&#x1F9D1;&#x200D;&#x1F4BB; Neural MT (EN&#x2192;ZH)</text>
154
+ <rect x="270" y="20" width="105" height="20" rx="10" fill="#a855f7" fill-opacity="0.12" stroke="#a855f7" stroke-width="0.8" stroke-opacity="0.4"/>
155
+ <text x="322" y="34" text-anchor="middle" font-family="'SF Mono', 'Fira Code', Consolas, monospace" font-size="10" font-weight="600" fill="#a855f7">+30% on top</text>
156
+
157
+ <text font-family="system-ui, sans-serif" font-size="11" fill="#8b949e">
158
+ <tspan x="24" y="58">Steno'd tool_result text is translated EN&#x2192;ZH via local Argos Translate (air-gapped, no API calls).</tspan>
159
+ <tspan x="24" dy="15">Loop-back verification: ZH&#x2192;EN back-translation compared via LCS Dice + Jaccard (threshold 0.65).</tspan>
160
+ <tspan x="24" dy="15">Failed verifications fall back to steno-only. Passthrough vocab for untranslatable tech terms (webpack,</tspan>
161
+ <tspan x="24" dy="15">nginx, docker, etc). Word-level TM cache grows over time &#x2014; repeated words skip the socket entirely.</tspan>
162
+ </text>
163
+
164
+ <!-- Verification flow diagram -->
165
+ <rect x="24" y="120" width="58" height="14" rx="3" fill="#00bfff" fill-opacity="0.1" stroke="#00bfff" stroke-width="0.8" stroke-opacity="0.3"/>
166
+ <text x="53" y="131" text-anchor="middle" font-family="'SF Mono', monospace" font-size="8" fill="#58a6ff">steno'd</text>
167
+ <text x="90" y="131" font-family="system-ui, sans-serif" font-size="9" fill="#484f58">&#x2192;</text>
168
+ <rect x="102" y="120" width="34" height="14" rx="3" fill="#a855f7" fill-opacity="0.15" stroke="#a855f7" stroke-width="0.8" stroke-opacity="0.4"/>
169
+ <text x="119" y="131" text-anchor="middle" font-family="'SF Mono', monospace" font-size="8" fill="#c084fc">ZH</text>
170
+ <text x="144" y="131" font-family="system-ui, sans-serif" font-size="9" fill="#484f58">&#x2192;</text>
171
+ <rect x="156" y="120" width="64" height="14" rx="3" fill="#f59e0b" fill-opacity="0.12" stroke="#f59e0b" stroke-width="0.8" stroke-opacity="0.3"/>
172
+ <text x="188" y="131" text-anchor="middle" font-family="'SF Mono', monospace" font-size="8" fill="#fbbf24">back-EN</text>
173
+ <text x="228" y="131" font-family="system-ui, sans-serif" font-size="9" fill="#484f58">&#x2192;</text>
174
+ <rect x="242" y="120" width="48" height="14" rx="3" fill="#22c55e" fill-opacity="0.15" stroke="#22c55e" stroke-width="0.8" stroke-opacity="0.4"/>
175
+ <text x="266" y="131" text-anchor="middle" font-family="'SF Mono', monospace" font-size="8" fill="#4ade80">&gt;=0.65</text>
176
+ <text x="298" y="131" font-family="system-ui, sans-serif" font-size="9" fill="#4ade80">use ZH</text>
177
+ <text x="350" y="131" font-family="system-ui, sans-serif" font-size="9" fill="#484f58">|</text>
178
+ <rect x="364" y="120" width="48" height="14" rx="3" fill="#ef4444" fill-opacity="0.15" stroke="#ef4444" stroke-width="0.8" stroke-opacity="0.4"/>
179
+ <text x="388" y="131" text-anchor="middle" font-family="'SF Mono', monospace" font-size="8" fill="#f87171">&lt;0.65</text>
180
+ <text x="420" y="131" font-family="system-ui, sans-serif" font-size="9" fill="#f87171">keep steno</text>
181
+ <text x="490" y="131" font-family="system-ui, sans-serif" font-size="8" fill="#484f58">TM cache + synonym learning from failures</text>
182
+ </g>
183
+
184
+ <!-- Connector 3→4 -->
185
+ <line x1="66" y1="680" x2="66" y2="696" stroke="#30363d" stroke-width="2" stroke-dasharray="3,3"/>
186
+ <polygon points="62,694 66,702 70,694" fill="#30363d"/>
187
+
188
+ <!-- ═══════════════════════════════════════════════════════ -->
189
+ <!-- LAYER 4: Old-Message Stripping (y=702, h=114) -->
190
+ <!-- ═══════════════════════════════════════════════════════ -->
191
+ <g transform="translate(30, 702)">
192
+ <rect width="740" height="114" rx="12" fill="url(#cardGrad)" stroke="#f59e0b" stroke-width="1.5"/>
193
+ <rect width="740" height="3" rx="1.5" fill="url(#orangeGrad)"/>
194
+
195
+ <circle cx="36" cy="30" r="14" fill="none" stroke="#f59e0b" stroke-width="1.5" stroke-opacity="0.7"/>
196
+ <text x="36" y="35" text-anchor="middle" font-family="system-ui, sans-serif" font-size="13" font-weight="700" fill="#f59e0b">4</text>
197
+
198
+ <text x="60" y="34" font-family="system-ui, sans-serif" font-size="14" font-weight="700" fill="#fbbf24">&#x1F5D1;&#xFE0F; Old-Message Stripping</text>
199
+ <rect x="272" y="20" width="100" height="20" rx="10" fill="#f59e0b" fill-opacity="0.12" stroke="#f59e0b" stroke-width="0.8" stroke-opacity="0.4"/>
200
+ <text x="322" y="34" text-anchor="middle" font-family="'SF Mono', 'Fira Code', Consolas, monospace" font-size="10" font-weight="600" fill="#fbbf24">85%+ savings</text>
201
+
202
+ <text font-family="system-ui, sans-serif" font-size="11" fill="#8b949e">
203
+ <tspan x="24" y="58">tool_result content from messages outside the recent window (last 3 messages preserved) gets</tspan>
204
+ <tspan x="24" dy="15">replaced with a 200-char preview summary. Stale file reads, grep outputs, and old assistant text</tspan>
205
+ <tspan x="24" dy="15">blocks are trimmed to first-line anchors. SpecMem hook injections (SM-* markers) also stripped.</tspan>
206
+ </text>
207
+
208
+ <text x="24" y="106" font-family="'SF Mono', monospace" font-size="8" fill="#484f58">threshold: 100 chars | preview: 200 chars | preserves: tool_use_id, is_error, cache_control</text>
209
+ </g>
210
+
211
+ <!-- Connector 4→5 -->
212
+ <line x1="66" y1="816" x2="66" y2="832" stroke="#30363d" stroke-width="2" stroke-dasharray="3,3"/>
213
+ <polygon points="62,830 66,838 70,830" fill="#30363d"/>
214
+
215
+ <!-- ═══════════════════════════════════════════════════════ -->
216
+ <!-- LAYER 5: System-Reminder Stripping (y=838, h=114) -->
217
+ <!-- ═══════════════════════════════════════════════════════ -->
218
+ <g transform="translate(30, 838)">
219
+ <rect width="740" height="114" rx="12" fill="url(#cardGrad)" stroke="#ef4444" stroke-width="1.5"/>
220
+ <rect width="740" height="3" rx="1.5" fill="#ef4444"/>
221
+
222
+ <circle cx="36" cy="30" r="14" fill="none" stroke="#ef4444" stroke-width="1.5" stroke-opacity="0.7"/>
223
+ <text x="36" y="35" text-anchor="middle" font-family="system-ui, sans-serif" font-size="13" font-weight="700" fill="#ef4444">5</text>
224
+
225
+ <text x="60" y="34" font-family="system-ui, sans-serif" font-size="14" font-weight="700" fill="#f87171">&#x1F9F9; System-Reminder Stripping</text>
226
+ <rect x="298" y="20" width="100" height="20" rx="10" fill="#ef4444" fill-opacity="0.12" stroke="#ef4444" stroke-width="0.8" stroke-opacity="0.4"/>
227
+ <text x="348" y="34" text-anchor="middle" font-family="'SF Mono', 'Fira Code', Consolas, monospace" font-size="10" font-weight="600" fill="#f87171">variable</text>
228
+
229
+ <text font-family="system-ui, sans-serif" font-size="11" fill="#8b949e">
230
+ <tspan x="24" y="58">Duplicate &lt;system-reminder&gt; tags stripped from messages. First instance is preserved (sets up</tspan>
231
+ <tspan x="24" dy="15">context/style), all subsequent identical reminders removed. System prompt dedup tracks content</tspan>
232
+ <tspan x="24" dy="15">hash &#x2014; same prompt across requests is sent once, then stripped on repeat.</tspan>
233
+ </text>
234
+
235
+ <text x="24" y="106" font-family="'SF Mono', monospace" font-size="8" fill="#484f58">regex: /&lt;system-reminder&gt;[\s\S]*?&lt;\/system-reminder&gt;/g | keeps first, strips rest</text>
236
+ </g>
237
+
238
+ <!-- ═══════════════════════════════════════════════════════ -->
239
+ <!-- Result bar (y=970, h=44) -->
240
+ <!-- ═══════════════════════════════════════════════════════ -->
241
+ <rect x="30" y="968" width="740" height="44" rx="12" fill="#22c55e" fill-opacity="0.08" stroke="#22c55e" stroke-width="1.5" stroke-opacity="0.4"/>
242
+ <circle cx="66" cy="990" r="12" fill="none" stroke="#22c55e" stroke-width="2"/>
243
+ <path d="M60 990 L64 995 L74 984" fill="none" stroke="#4ade80" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
244
+ <text x="88" y="986" font-family="system-ui, sans-serif" font-size="13" font-weight="600" fill="#4ade80">Combined: ~57-60% total input token reduction.</text>
245
+ <text x="88" y="1003" font-family="system-ui, sans-serif" font-size="10" fill="#6e7681">All layers run on every request. Falls back gracefully &#x2014; proxy never breaks the API call. Toggle via /config.</text>
246
+ </svg>
@@ -1,179 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Agent Chooser Hook - Interactive Agent Deployment
4
- * ==================================================
5
- *
6
- * PreToolUse hook that INTERCEPTS Task tool calls and:
7
- * 1. BLOCKS automatic deployment
8
- * 2. Injects instructions for to ask user for preferences
9
- * 3. User chooses agent type, model, settings PER DEPLOYMENT
10
- *
11
- * This gives users FULL CONTROL over every agent that gets deployed!
12
- *
13
- * Hook Event: PreToolUse
14
- * Matcher: Task
15
- *
16
- * To disable interactive mode, set SPECMEM_AGENT_AUTO=1
17
- */
18
-
19
- const fs = require('fs');
20
- const path = require('path');
21
-
22
- // Check if auto mode is enabled (skip interactive)
23
- const AUTO_MODE = process.env.SPECMEM_AGENT_AUTO === '1' || process.env.SPECMEM_AGENT_AUTO === 'true';
24
-
25
- // Check if chooser is disabled
26
- const CHOOSER_DISABLED = process.env.SPECMEM_NO_CHOOSER === '1' || process.env.SPECMEM_NO_CHOOSER === 'true';
27
-
28
- // Marker to prevent re-processing
29
- const CHOOSER_MARKER = '[AGENT_CHOOSER_CONFIRMED]';
30
-
31
- /**
32
- * Load user config
33
- */
34
- function loadConfig() {
35
- const configPaths = [
36
- path.join(process.cwd(), '.specmem', 'agent-config.json'),
37
- path.join(process.env.HOME || '', '.specmem', 'agent-config.json')
38
- ];
39
-
40
- for (const p of configPaths) {
41
- try {
42
- if (fs.existsSync(p)) {
43
- return JSON.parse(fs.readFileSync(p, 'utf8'));
44
- }
45
- } catch (e) {}
46
- }
47
-
48
- return {
49
- defaults: { model: 'sonnet', background: true },
50
- agents: {}
51
- };
52
- }
53
-
54
- /**
55
- * Build the chooser context that instructs to ask the user
56
- */
57
- function buildChooserContext(description, currentType, currentModel, config) {
58
- const agentTypes = [
59
- { name: 'general-purpose', desc: 'Full toolset for complex multi-step tasks' },
60
- { name: 'Explore', desc: 'Fast codebase exploration and search' },
61
- { name: 'feature-dev:code-explorer', desc: 'Deep code analysis with tracing' },
62
- { name: 'feature-dev:code-architect', desc: 'Architecture design blueprints' },
63
- { name: 'feature-dev:code-reviewer', desc: 'Code review with confidence filtering' },
64
- { name: 'Bash', desc: 'Shell/git/CLI command specialist' },
65
- { name: 'Plan', desc: 'Architecture and implementation planning' }
66
- ];
67
-
68
- const models = [
69
- { name: 'haiku', desc: 'Fastest, cheapest - simple tasks' },
70
- { name: 'sonnet', desc: 'Balanced speed and quality (default)' },
71
- { name: 'opus', desc: 'Deepest thinking - complex analysis' }
72
- ];
73
-
74
- return `
75
- [AGENT-CHOOSER]
76
- 🎛️ AGENT DEPLOYMENT PAUSED - Confirm settings
77
-
78
- Task: "${description}"
79
- Agent: ${currentType || 'general-purpose'} | Model: ${currentModel || config.defaults?.model || 'sonnet'}
80
-
81
- Call AskUserQuestion NOW:
82
- {
83
- "questions": [
84
- {
85
- "question": "Agent for: ${description.slice(0, 40)}...?",
86
- "header": "Agent",
87
- "options": [
88
- {"label": "${currentType || 'general-purpose'}", "description": "Current"},
89
- {"label": "Explore", "description": "Fast search"},
90
- {"label": "feature-dev:code-explorer", "description": "Deep analysis"},
91
- {"label": "Plan", "description": "Architecture"}
92
- ],
93
- "multiSelect": false
94
- },
95
- {
96
- "question": "Model?",
97
- "header": "Model",
98
- "options": [
99
- {"label": "sonnet", "description": "Balanced (recommended)"},
100
- {"label": "opus", "description": "Deepest thinking"},
101
- {"label": "haiku", "description": "Fastest"}
102
- ],
103
- "multiSelect": false
104
- }
105
- ]
106
- }
107
-
108
- After response: Re-deploy Task with choices + "${CHOOSER_MARKER}" in prompt.
109
- [/AGENT-CHOOSER]
110
- `;
111
- }
112
-
113
- async function main() {
114
- let input = '';
115
- process.stdin.setEncoding('utf8');
116
- for await (const chunk of process.stdin) {
117
- input += chunk;
118
- }
119
-
120
- try {
121
- const data = JSON.parse(input);
122
- const toolName = data.tool_name || '';
123
- const toolInput = data.tool_input || {};
124
-
125
- // Only intercept Task tool
126
- if (toolName !== 'Task') {
127
- process.exit(0);
128
- }
129
-
130
- // Skip if chooser is disabled
131
- if (CHOOSER_DISABLED) {
132
- process.exit(0); // Let other hooks handle it
133
- }
134
-
135
- // Skip if auto mode is enabled
136
- if (AUTO_MODE) {
137
- process.exit(0); // Let other hooks handle it
138
- }
139
-
140
- const prompt = toolInput.prompt || '';
141
- const description = toolInput.description || 'agent task';
142
- const agentType = toolInput.subagent_type || '';
143
- const model = toolInput.model || '';
144
-
145
- // Skip if already confirmed by user (has our marker)
146
- if (prompt.includes(CHOOSER_MARKER)) {
147
- // User confirmed - let it proceed (other hooks will process)
148
- process.exit(0);
149
- }
150
-
151
- // Load config for defaults
152
- const config = loadConfig();
153
-
154
- // Build the chooser context
155
- const chooserContext = buildChooserContext(description, agentType, model, config);
156
-
157
- // BLOCK the deployment and inject instructions for to ask user
158
- console.log(JSON.stringify({
159
- continue: false, // Block the tool call
160
- stopReason: `Agent deployment requires user confirmation. Use AskUserQuestion to let user choose settings.`,
161
- hookSpecificOutput: {
162
- hookEventName: 'PreToolUse',
163
- permissionDecision: 'deny',
164
- permissionDecisionReason: `🛑 Agent chooser: Awaiting user confirmation for "${description.slice(0, 40)}"`,
165
- additionalContext: chooserContext
166
- }
167
- }));
168
-
169
- } catch (e) {
170
- // LOW-44 FIX: Log errors before exit
171
- console.error('[agent-chooser-hook] Error:', e.message || e);
172
- process.exit(0);
173
- }
174
- }
175
-
176
- main().catch((e) => {
177
- console.error('[agent-chooser-hook] Unhandled error:', e.message || e);
178
- process.exit(0);
179
- });