shakers-cli 0.0.0-stage → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/LICENSE +59 -0
  2. package/README.md +1233 -2
  3. package/bin/add-agent.js +88 -0
  4. package/bin/add-project.js +88 -0
  5. package/bin/add-role.js +61 -0
  6. package/bin/add-skill.js +88 -0
  7. package/bin/ai-usage.js +99 -0
  8. package/bin/alma.js +99 -0
  9. package/bin/applications.js +69 -0
  10. package/bin/availability.js +70 -0
  11. package/bin/certifications.js +69 -0
  12. package/bin/certify.js +73 -0
  13. package/bin/change-role.js +61 -0
  14. package/bin/config.js +124 -0
  15. package/bin/experiences.js +69 -0
  16. package/bin/find-projects.js +89 -0
  17. package/bin/invitations.js +69 -0
  18. package/bin/lang.js +70 -0
  19. package/bin/login.js +216 -0
  20. package/bin/mcp.js +124 -0
  21. package/bin/mcpb.js +5 -0
  22. package/bin/me.js +69 -0
  23. package/bin/onboarding.js +134 -0
  24. package/bin/portfolios.js +69 -0
  25. package/bin/rate.js +70 -0
  26. package/bin/register.js +337 -0
  27. package/bin/report-html.js +134 -0
  28. package/bin/save.js +77 -0
  29. package/bin/shakers.js +363 -0
  30. package/bin/share.js +72 -0
  31. package/bin/show-project.js +72 -0
  32. package/bin/socials.js +70 -0
  33. package/bin/superadmin.js +351 -0
  34. package/bin/update-check-worker.js +70 -0
  35. package/bin/update.js +111 -0
  36. package/package.json +40 -4
  37. package/src/agent-category.js +13 -0
  38. package/src/agent-evaluation.js +177 -0
  39. package/src/agent-org-chart.js +355 -0
  40. package/src/agent-synthesis.js +132 -0
  41. package/src/agent-usage.js +118 -0
  42. package/src/agents-client.js +165 -0
  43. package/src/ai-fluency-cell.js +72 -0
  44. package/src/ai-profile-client.js +45 -0
  45. package/src/ai-profile-preview.js +154 -0
  46. package/src/ai.js +66 -0
  47. package/src/alma-client.js +123 -0
  48. package/src/alma-flow.js +32 -0
  49. package/src/alma-repo-context.js +47 -0
  50. package/src/alma-shell.js +161 -0
  51. package/src/ansi.js +62 -0
  52. package/src/applications-client.js +79 -0
  53. package/src/applications-flow.js +58 -0
  54. package/src/auth-client.js +144 -0
  55. package/src/auth-device-client.js +118 -0
  56. package/src/auth-method-picker.js +44 -0
  57. package/src/auth-methods.js +35 -0
  58. package/src/auth-session-store.js +215 -0
  59. package/src/authorship-mismatch.js +62 -0
  60. package/src/authorship.js +204 -0
  61. package/src/automations-detector.js +170 -0
  62. package/src/availability-client.js +93 -0
  63. package/src/availability-flow.js +124 -0
  64. package/src/backend-request.js +191 -0
  65. package/src/blind-approval-extract.js +48 -0
  66. package/src/brand-ansi.js +14 -0
  67. package/src/brand-svg.js +9 -0
  68. package/src/browser-tools-detector.js +21 -0
  69. package/src/build-next-level.js +56 -0
  70. package/src/certifications-client.js +89 -0
  71. package/src/certifications-flow.js +75 -0
  72. package/src/certify-dimension-client.js +163 -0
  73. package/src/certify-dimension-flow.js +135 -0
  74. package/src/certify-disclaimer.js +51 -0
  75. package/src/certify-remediation-prompt.js +23 -0
  76. package/src/cli-args.js +83 -0
  77. package/src/command-audit.js +105 -0
  78. package/src/config-dir.js +120 -0
  79. package/src/config.js +877 -0
  80. package/src/consent-flow.js +115 -0
  81. package/src/consent-skip.js +27 -0
  82. package/src/cv-file.js +62 -0
  83. package/src/cv-text.js +92 -0
  84. package/src/decisions-extract.js +43 -0
  85. package/src/detect-repos.js +145 -0
  86. package/src/detected-tools.js +15 -0
  87. package/src/detection-evidence.js +105 -0
  88. package/src/detection-noise.js +132 -0
  89. package/src/detectors.js +221 -0
  90. package/src/device-auth-store.js +100 -0
  91. package/src/device-login.js +120 -0
  92. package/src/email-verification.js +152 -0
  93. package/src/env-paths.js +30 -0
  94. package/src/find-projects-cache.js +89 -0
  95. package/src/find-projects-client.js +148 -0
  96. package/src/find-projects-flow.js +164 -0
  97. package/src/git-activity.js +124 -0
  98. package/src/git-log-prefetch.js +53 -0
  99. package/src/graph-certs.js +58 -0
  100. package/src/graph-generator.js +230 -0
  101. package/src/graph-scan.js +239 -0
  102. package/src/i18n.js +3341 -0
  103. package/src/inter-fonts.js +6 -0
  104. package/src/interactive-select.js +183 -0
  105. package/src/interview-client.js +226 -0
  106. package/src/interview-transcript.js +56 -0
  107. package/src/inventory-client.js +90 -0
  108. package/src/invitations-client.js +66 -0
  109. package/src/invitations-flow.js +50 -0
  110. package/src/lang-client.js +147 -0
  111. package/src/lang-codes.js +14 -0
  112. package/src/lang-flow.js +85 -0
  113. package/src/legal-notice.js +53 -0
  114. package/src/locale.js +72 -0
  115. package/src/markdown-ansi.js +50 -0
  116. package/src/maturity.js +111 -0
  117. package/src/mcp-ai-usage-result-tool.js +111 -0
  118. package/src/mcp-ai-usage-tool.js +401 -0
  119. package/src/mcp-alma-tools.js +95 -0
  120. package/src/mcp-applications-tools.js +44 -0
  121. package/src/mcp-auth-form.js +480 -0
  122. package/src/mcp-availability-tools.js +45 -0
  123. package/src/mcp-certifications-tools.js +44 -0
  124. package/src/mcp-certify-dimension-tools.js +66 -0
  125. package/src/mcp-choice.js +95 -0
  126. package/src/mcp-detector.js +223 -0
  127. package/src/mcp-device-login-tools.js +177 -0
  128. package/src/mcp-find-projects-tools.js +69 -0
  129. package/src/mcp-install.js +275 -0
  130. package/src/mcp-instructions.js +27 -0
  131. package/src/mcp-invitations-tools.js +44 -0
  132. package/src/mcp-lang-tools.js +41 -0
  133. package/src/mcp-onboarding-tools.js +379 -0
  134. package/src/mcp-portfolios-tools.js +50 -0
  135. package/src/mcp-profile-tools.js +48 -0
  136. package/src/mcp-prompts.js +73 -0
  137. package/src/mcp-rate-tools.js +44 -0
  138. package/src/mcp-register-tools.js +441 -0
  139. package/src/mcp-report-tools.js +105 -0
  140. package/src/mcp-role-tools.js +372 -0
  141. package/src/mcp-saved-positions-tools.js +59 -0
  142. package/src/mcp-server.js +290 -0
  143. package/src/mcp-show-project-tools.js +52 -0
  144. package/src/mcp-signup-client.js +118 -0
  145. package/src/mcp-signup-tools.js +1171 -0
  146. package/src/mcp-socials-tools.js +41 -0
  147. package/src/memory-structure-detector.js +108 -0
  148. package/src/model-call-record.js +198 -0
  149. package/src/onboarding-availability.js +26 -0
  150. package/src/onboarding-client.js +374 -0
  151. package/src/onboarding-flow.js +1162 -0
  152. package/src/onboarding-funnel.js +272 -0
  153. package/src/onboarding-livekit-client.js +102 -0
  154. package/src/onboarding-status-client.js +57 -0
  155. package/src/open-file.js +32 -0
  156. package/src/osc-link.js +16 -0
  157. package/src/portfolio-client.js +164 -0
  158. package/src/portfolio-git-info.js +61 -0
  159. package/src/portfolios-client.js +85 -0
  160. package/src/portfolios-flow.js +79 -0
  161. package/src/profile-client.js +133 -0
  162. package/src/profile-flow.js +115 -0
  163. package/src/profile-switch.js +46 -0
  164. package/src/prompt-select.js +66 -0
  165. package/src/rate-client.js +81 -0
  166. package/src/rate-flow.js +137 -0
  167. package/src/register-context.js +145 -0
  168. package/src/render-ai-fluency.js +266 -0
  169. package/src/render-certification.js +177 -0
  170. package/src/render-html.js +140 -0
  171. package/src/render-report-text.js +27 -0
  172. package/src/render-sheet.js +261 -0
  173. package/src/render-terminal.js +109 -0
  174. package/src/render-usage-report-text.js +155 -0
  175. package/src/repo-context.js +215 -0
  176. package/src/report-gating.js +49 -0
  177. package/src/report-store.js +226 -0
  178. package/src/report-theme.js +177 -0
  179. package/src/report-view-client.js +50 -0
  180. package/src/reveal-output.js +29 -0
  181. package/src/roadmap-content.js +453 -0
  182. package/src/roadmap-personalization.js +132 -0
  183. package/src/roadmap-prompt.js +154 -0
  184. package/src/roles-client.js +112 -0
  185. package/src/roles-flow.js +192 -0
  186. package/src/sanitize-network-text.js +36 -0
  187. package/src/saved-positions-client.js +71 -0
  188. package/src/saved-positions-flow.js +50 -0
  189. package/src/scan-exclusions.js +50 -0
  190. package/src/scanner.js +624 -0
  191. package/src/scope-signals.js +148 -0
  192. package/src/secret-prompt.js +72 -0
  193. package/src/session-gate.js +30 -0
  194. package/src/session-normalizers.js +174 -0
  195. package/src/session-refresh.js +51 -0
  196. package/src/session-scan.js +475 -0
  197. package/src/session-signals.js +36 -0
  198. package/src/shakers-wordmark.js +17 -0
  199. package/src/share-card.js +362 -0
  200. package/src/share-certification.js +111 -0
  201. package/src/share-consent-state.js +157 -0
  202. package/src/share-payload.js +300 -0
  203. package/src/share.js +185 -0
  204. package/src/show-project-client.js +58 -0
  205. package/src/show-project-flow.js +119 -0
  206. package/src/signup-client.js +103 -0
  207. package/src/signup-copy.js +450 -0
  208. package/src/signup-language.js +21 -0
  209. package/src/skills-client.js +102 -0
  210. package/src/socials-client.js +91 -0
  211. package/src/socials-flow.js +83 -0
  212. package/src/specificity-extract.js +41 -0
  213. package/src/start-add-agents.js +306 -0
  214. package/src/start-add-portfolio.js +388 -0
  215. package/src/start-add-skills.js +260 -0
  216. package/src/stdin-ask.js +129 -0
  217. package/src/steering-extract.js +43 -0
  218. package/src/strip-internal-comments.js +45 -0
  219. package/src/tech-detector.js +295 -0
  220. package/src/templates/report-sheet.html +572 -0
  221. package/src/terminal-agents.js +207 -0
  222. package/src/terminal-format.js +85 -0
  223. package/src/terminal-progress.js +106 -0
  224. package/src/terminal-sections.js +219 -0
  225. package/src/tier-analysis.js +126 -0
  226. package/src/tier-engine.js +114 -0
  227. package/src/tier-projection.js +117 -0
  228. package/src/transcript-turns.js +49 -0
  229. package/src/translations-client.js +84 -0
  230. package/src/typewriter.js +154 -0
  231. package/src/update-flow.js +223 -0
  232. package/src/update-notifier.js +113 -0
  233. package/src/usage-model-calls.js +119 -0
  234. package/src/usage-oneshot-commands.js +180 -0
  235. package/src/usage-repo-scope.js +162 -0
  236. package/src/usage-run-steps.js +354 -0
  237. package/src/work-streams.js +79 -0
@@ -0,0 +1,475 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { getHomeDir } = require('./env-paths');
6
+ const { scrubSecrets } = require('./agent-synthesis');
7
+ const {
8
+ normalizeCodexRecord,
9
+ normalizeGeminiRecord,
10
+ normalizeContinueRecord,
11
+ normalizeClineRecord,
12
+ parseAiderHistory,
13
+ } = require('./session-normalizers');
14
+
15
+ // GLOBAL, machine-wide inventory of AI coding-tool SESSIONS (hybrid evaluation, Slice 1).
16
+
17
+ const MAX_FILES_PER_TOOL = 2000;
18
+ const MAX_BYTES_PER_FILE = 8 * 1024 * 1024;
19
+ const MAX_COMMAND_SAMPLE = 20;
20
+ const MAX_COMMAND_LEN = 200;
21
+ const IDLE_GAP_CAP_MS = 25 * 60 * 1000;
22
+ // Recency window for sessions90d / activeDaysPerWeek only; all-time metrics untouched.
23
+ const TRACTION_WINDOW_DAYS = 90;
24
+ const TRACTION_WINDOW_MS = TRACTION_WINDOW_DAYS * 24 * 60 * 60 * 1000;
25
+ const DAY_MS = 24 * 60 * 60 * 1000;
26
+
27
+ function activeMsForObjects(objects) {
28
+ const stamps = [];
29
+ for (const o of objects) {
30
+ if (o && typeof o.timestamp === 'string') {
31
+ const t = Date.parse(o.timestamp);
32
+ if (Number.isFinite(t)) stamps.push(t);
33
+ }
34
+ }
35
+ stamps.sort((a, b) => a - b);
36
+ let ms = 0;
37
+ for (let i = 1; i < stamps.length; i++) {
38
+ ms += Math.min(stamps[i] - stamps[i - 1], IDLE_GAP_CAP_MS);
39
+ }
40
+ return ms;
41
+ }
42
+
43
+ // Most-recent parseable timestamp = when the session was last active; null if none.
44
+ function latestTimestampOf(objects) {
45
+ let latest = null;
46
+ for (const o of objects) {
47
+ if (o && typeof o.timestamp === 'string') {
48
+ const t = Date.parse(o.timestamp);
49
+ if (Number.isFinite(t) && (latest === null || t > latest)) latest = t;
50
+ }
51
+ }
52
+ return latest;
53
+ }
54
+
55
+ // distinct active days ÷ weeks of the observed active span (clamped ≥1 week), capped at 7.
56
+ function activeDaysPerWeekFrom(activeDayStrings) {
57
+ const dayEpochs = [...activeDayStrings].map((d) => Date.parse(`${d}T00:00:00Z`));
58
+ if (dayEpochs.length === 0) return 0;
59
+ const spanDays =
60
+ Math.floor((Math.max(...dayEpochs) - Math.min(...dayEpochs)) / DAY_MS) + 1;
61
+ const weeks = Math.max(1, spanDays / 7);
62
+ return Math.min(7, Math.round((dayEpochs.length / weeks) * 10) / 10);
63
+ }
64
+
65
+ function isTranscriptFile(name) {
66
+ if (name.endsWith('.meta.json')) return false;
67
+ if (name === 'sessions-index.json') return false;
68
+ return name.endsWith('.jsonl') || name.endsWith('.json');
69
+ }
70
+
71
+ function isSubagentFile(filePath) {
72
+ return filePath.split(path.sep).includes('subagents');
73
+ }
74
+ const FILE_TOOL_NAMES = new Set([
75
+ 'Read',
76
+ 'Edit',
77
+ 'MultiEdit',
78
+ 'Write',
79
+ 'NotebookEdit',
80
+ ]);
81
+ const PLANNING_TOOL_NAMES = new Set(['ExitPlanMode', 'TodoWrite']);
82
+
83
+ // Only logs.json + session-*.jsonl under ~/.gemini/tmp are transcripts (skip checkpoints/config).
84
+ function isGeminiTranscriptFile(name) {
85
+ return name === 'logs.json' || (name.startsWith('session-') && name.endsWith('.jsonl'));
86
+ }
87
+
88
+ // Cline / Roo Code conversation file (both forks use this filename under tasks/<id>/).
89
+ function isClineHistoryFile(name) {
90
+ return name === 'api_conversation_history.json';
91
+ }
92
+
93
+ // VS Code globalStorage root per OS (Cline/Roo live under <ext-id>/tasks/<id>/ here).
94
+ function vscodeGlobalStorageDir(home) {
95
+ if (process.platform === 'darwin') return path.join(home, 'Library', 'Application Support', 'Code', 'User', 'globalStorage');
96
+ if (process.platform === 'win32') return path.join(home, 'AppData', 'Roaming', 'Code', 'User', 'globalStorage');
97
+ return path.join(home, '.config', 'Code', 'User', 'globalStorage');
98
+ }
99
+
100
+ function toolSources(env = process.env) {
101
+ const home = getHomeDir(env);
102
+ return [
103
+ { tool: 'claude', dir: path.join(home, '.claude', 'projects') },
104
+ { tool: 'codex', dir: path.join(home, '.codex', 'sessions'), normalize: normalizeCodexRecord },
105
+ { tool: 'gemini', dir: path.join(home, '.gemini', 'tmp'), fileFilter: isGeminiTranscriptFile, normalize: normalizeGeminiRecord },
106
+ // Kept by decision: ~/.cursor/chats is OBSOLETE (real Cursor chat is SQLite in App Support); awaiting a future SQLite extractor, no tool removed.
107
+ { tool: 'cursor', dir: path.join(home, '.cursor', 'chats') },
108
+ { tool: 'continue', dir: path.join(home, '.continue', 'sessions'), normalize: normalizeContinueRecord },
109
+ { tool: 'cline', dir: vscodeGlobalStorageDir(home), fileFilter: isClineHistoryFile, normalize: normalizeClineRecord },
110
+ // Repo-level: .aider.chat.history.md lives INSIDE each git repo, so cwd = the repo toplevel.
111
+ { tool: 'aider', repoLevel: true, file: '.aider.chat.history.md', parseText: parseAiderHistory },
112
+ ];
113
+ }
114
+
115
+ function listSessionFiles(root, fileFilter = isTranscriptFile) {
116
+ const out = [];
117
+ const stack = [root];
118
+ while (stack.length > 0) {
119
+ const dir = stack.pop();
120
+ let entries;
121
+ try {
122
+ entries = fs.readdirSync(dir, { withFileTypes: true });
123
+ } catch {
124
+ continue;
125
+ }
126
+ for (const entry of entries) {
127
+ const full = path.join(dir, entry.name);
128
+ if (entry.isDirectory()) {
129
+ stack.push(full);
130
+ } else if (entry.isFile() && fileFilter(entry.name)) {
131
+ out.push(full);
132
+ if (out.length >= MAX_FILES_PER_TOOL) return out;
133
+ }
134
+ }
135
+ }
136
+ return out;
137
+ }
138
+
139
+ // Whole-file JSON reader (bounded) for `.json` transcripts (e.g. Gemini logs.json = array).
140
+ function readJsonArrayFile(file) {
141
+ let fd;
142
+ try {
143
+ fd = fs.openSync(file, 'r');
144
+ } catch {
145
+ return [];
146
+ }
147
+ try {
148
+ const { size } = fs.fstatSync(fd);
149
+ const readLen = Math.min(size, MAX_BYTES_PER_FILE);
150
+ const buf = Buffer.allocUnsafe(readLen);
151
+ fs.readSync(fd, buf, 0, readLen, 0);
152
+ const parsed = JSON.parse(buf.toString('utf8'));
153
+ if (Array.isArray(parsed)) return parsed;
154
+ return parsed && typeof parsed === 'object' ? [parsed] : [];
155
+ } catch {
156
+ return [];
157
+ } finally {
158
+ try { fs.closeSync(fd); } catch { /* already closed */ }
159
+ }
160
+ }
161
+
162
+ // Parse a file to raw records by extension (.jsonl = per line, .json = whole array).
163
+ function parseRawRecords(file) {
164
+ return file.endsWith('.jsonl') ? readSessionObjects(file) : readJsonArrayFile(file);
165
+ }
166
+
167
+ // Read a source's file and normalize its records into the Claude-shaped internal model.
168
+ // A normalizer may return an object, an array (expanded), or null/[] (fail-closed skip).
169
+ function readSourceObjects(source, file) {
170
+ const raw = parseRawRecords(file);
171
+ if (typeof source.normalize !== 'function') return raw;
172
+ const out = [];
173
+ for (const r of raw) {
174
+ const o = source.normalize(r);
175
+ if (Array.isArray(o)) out.push(...o);
176
+ else if (o) out.push(o);
177
+ }
178
+ return out;
179
+ }
180
+
181
+ // Bounded whole-file text read (for repo-level markdown sources, e.g. Aider).
182
+ function readTextFile(file) {
183
+ let fd;
184
+ try {
185
+ fd = fs.openSync(file, 'r');
186
+ } catch {
187
+ return null;
188
+ }
189
+ try {
190
+ const { size } = fs.fstatSync(fd);
191
+ const readLen = Math.min(size, MAX_BYTES_PER_FILE);
192
+ const buf = Buffer.allocUnsafe(readLen);
193
+ fs.readSync(fd, buf, 0, readLen, 0);
194
+ return buf.toString('utf8');
195
+ } catch {
196
+ return null;
197
+ } finally {
198
+ try { fs.closeSync(fd); } catch { /* already closed */ }
199
+ }
200
+ }
201
+
202
+ // Repo-level source: read <toplevel>/<source.file> and parse it with cwd = the toplevel.
203
+ function readRepoLevelObjects(source, toplevel) {
204
+ const text = readTextFile(path.join(toplevel, source.file));
205
+ if (text === null) return [];
206
+ const objects = source.parseText(text, toplevel);
207
+ return Array.isArray(objects) ? objects : [];
208
+ }
209
+
210
+ function pushCommand(acc, command) {
211
+ if (typeof command !== 'string') return;
212
+ acc.bashCommandsCount += 1;
213
+ if (acc.commandSample.length >= MAX_COMMAND_SAMPLE) return;
214
+ const scrubbed = scrubSecrets(command).trim().slice(0, MAX_COMMAND_LEN);
215
+ if (scrubbed && !acc.commandSample.includes(scrubbed)) {
216
+ acc.commandSample.push(scrubbed);
217
+ }
218
+ }
219
+
220
+ function harvestToolUse(node, acc) {
221
+ if (!node || typeof node !== 'object') return;
222
+ const name = node.name;
223
+ const input = node.input && typeof node.input === 'object' ? node.input : null;
224
+ if (name === 'Bash' && input) {
225
+ pushCommand(acc, input.command);
226
+ } else if (name === 'Shell' && input) {
227
+ pushCommand(acc, input.command);
228
+ } else if (FILE_TOOL_NAMES.has(name) && input) {
229
+ const p = input.file_path || input.notebook_path;
230
+ if (typeof p === 'string' && p) acc.filesTouched.add(p);
231
+ } else if (PLANNING_TOOL_NAMES.has(name)) {
232
+ acc.planningSignalCount = (acc.planningSignalCount || 0) + 1;
233
+ }
234
+ }
235
+
236
+ function harvestObject(o, acc) {
237
+ const msg = o && o.message ? o.message : o;
238
+ const content = msg && Array.isArray(msg.content) ? msg.content : null;
239
+ if (content) {
240
+ for (const c of content) {
241
+ if (c && c.type === 'tool_use') harvestToolUse(c, acc);
242
+ }
243
+ }
244
+ if (o && typeof o.cwd === 'string' && o.cwd) acc.projectKeys.add(o.cwd);
245
+ }
246
+
247
+ function readSessionObjects(file) {
248
+ const out = [];
249
+ let fd;
250
+ try {
251
+ fd = fs.openSync(file, 'r');
252
+ } catch {
253
+ return out;
254
+ }
255
+ try {
256
+ const { size } = fs.fstatSync(fd);
257
+ const readLen = Math.min(size, MAX_BYTES_PER_FILE);
258
+ const buf = Buffer.allocUnsafe(readLen);
259
+ fs.readSync(fd, buf, 0, readLen, 0);
260
+ const lines = buf.toString('utf8').split('\n');
261
+ const complete = size > MAX_BYTES_PER_FILE ? lines.slice(0, -1) : lines;
262
+ for (const ln of complete) {
263
+ if (!ln) continue;
264
+ let o;
265
+ try {
266
+ o = JSON.parse(ln);
267
+ } catch {
268
+ continue;
269
+ }
270
+ out.push(o);
271
+ }
272
+ } catch {
273
+ /* unreadable mid-stream — return what parsed */
274
+ } finally {
275
+ try {
276
+ fs.closeSync(fd);
277
+ } catch {
278
+ /* already closed */
279
+ }
280
+ }
281
+ return out;
282
+ }
283
+
284
+ function scanFile(file, acc) {
285
+ for (const o of readSessionObjects(file)) harvestObject(o, acc);
286
+ }
287
+
288
+ function resolveFileCwd(objects) {
289
+ for (const o of objects) {
290
+ if (o && typeof o.cwd === 'string' && o.cwd) return o.cwd;
291
+ }
292
+ return null;
293
+ }
294
+
295
+ function fileInScope(cwd, selectedCwds) {
296
+ if (selectedCwds === null) return true;
297
+ return cwd !== null && selectedCwds.has(cwd);
298
+ }
299
+
300
+ // Yields every parsed session object across every known AI-tool session directory on this machine, tagged with the tool it came from.
301
+ function* iterateSessionObjects(env = process.env) {
302
+ const sources = toolSources(env);
303
+ // Phase 1: home-dir sources; collect the repo toplevels (cwds) they reveal.
304
+ const seenCwds = new Set();
305
+ for (const source of sources) {
306
+ if (source.repoLevel) continue;
307
+ const files = listSessionFiles(source.dir, source.fileFilter || isTranscriptFile);
308
+ for (const file of files) {
309
+ const objects = readSourceObjects(source, file);
310
+ const cwd = resolveFileCwd(objects);
311
+ if (cwd) seenCwds.add(cwd);
312
+ for (const obj of objects) {
313
+ yield { tool: source.tool, obj, cwd, filePath: file };
314
+ }
315
+ }
316
+ }
317
+ // Phase 2: repo-level sources (e.g. Aider) over the repos just discovered — cwd is known.
318
+ for (const source of sources) {
319
+ if (!source.repoLevel) continue;
320
+ for (const top of seenCwds) {
321
+ const objects = readRepoLevelObjects(source, top);
322
+ for (const obj of objects) {
323
+ yield { tool: source.tool, obj, cwd: top, filePath: path.join(top, source.file) };
324
+ }
325
+ }
326
+ }
327
+ }
328
+
329
+ // Scan every known AI-tool session directory on this machine.
330
+ function makeSessionsAccumulator() {
331
+ const toolsSeen = [];
332
+ let sessionCount = 0;
333
+ let subagentRunCount = 0;
334
+ let activeMs = 0;
335
+ const filesTouched = new Set();
336
+ let bashCommandsCount = 0;
337
+ let planningSignalCount = 0;
338
+ const commandSample = [];
339
+ const projectKeysByTool = new Map();
340
+ // Recency-windowed session signals, from the same objects already read.
341
+ const windowCutoff = Date.now() - TRACTION_WINDOW_MS;
342
+ let sessions90d = 0;
343
+ const activeDaysInWindow = new Set();
344
+ let cur = null;
345
+
346
+ return {
347
+ startTool(tool) {
348
+ const projectKeys = new Set();
349
+ cur = {
350
+ tool,
351
+ projectKeys,
352
+ rootSessions: 0,
353
+ subagentRuns: 0,
354
+ acc: {
355
+ filesTouched,
356
+ commandSample,
357
+ projectKeys,
358
+ bashCommandsCount: 0,
359
+ planningSignalCount: 0,
360
+ },
361
+ };
362
+ },
363
+ addFile(file, objects) {
364
+ for (const o of objects) harvestObject(o, cur.acc);
365
+ if (isSubagentFile(file)) {
366
+ cur.subagentRuns += 1;
367
+ } else {
368
+ cur.rootSessions += 1;
369
+ activeMs += activeMsForObjects(objects);
370
+ // Root session in the 90d window → count it + record its (UTC) active day.
371
+ const lastTs = latestTimestampOf(objects);
372
+ if (lastTs !== null && lastTs >= windowCutoff) {
373
+ sessions90d += 1;
374
+ activeDaysInWindow.add(new Date(lastTs).toISOString().slice(0, 10));
375
+ }
376
+ }
377
+ },
378
+ endTool() {
379
+ if (cur.rootSessions === 0 && cur.subagentRuns === 0) {
380
+ cur = null;
381
+ return;
382
+ }
383
+ toolsSeen.push(cur.tool);
384
+ sessionCount += cur.rootSessions;
385
+ subagentRunCount += cur.subagentRuns;
386
+ bashCommandsCount += cur.acc.bashCommandsCount;
387
+ planningSignalCount += cur.acc.planningSignalCount;
388
+ projectKeysByTool.set(cur.tool, cur.projectKeys);
389
+ cur = null;
390
+ },
391
+ result() {
392
+ if (toolsSeen.length === 0) return null;
393
+ const keyToolCount = new Map();
394
+ for (const keys of projectKeysByTool.values()) {
395
+ for (const key of keys) {
396
+ keyToolCount.set(key, (keyToolCount.get(key) || 0) + 1);
397
+ }
398
+ }
399
+ let crossToolLinks = 0;
400
+ for (const count of keyToolCount.values()) {
401
+ if (count > 1) crossToolLinks += 1;
402
+ }
403
+ return {
404
+ toolsSeen,
405
+ sessionCount,
406
+ subagentRunCount,
407
+ activeHours: Math.round((activeMs / 3600000) * 10) / 10,
408
+ crossToolLinks,
409
+ filesTouchedCount: filesTouched.size,
410
+ bashCommandsCount,
411
+ planningSignalCount,
412
+ sessions90d,
413
+ activeDaysPerWeek: activeDaysPerWeekFrom(activeDaysInWindow),
414
+ redactedCommandSample: commandSample,
415
+ };
416
+ },
417
+ };
418
+ }
419
+
420
+ function walkSessionSignals(env = process.env, selectedCwds = null, onHumanObject = null) {
421
+ const sessions = makeSessionsAccumulator();
422
+ const sources = toolSources(env);
423
+ const seenCwds = new Set();
424
+ // Phase 1: home-dir sources; remember the repo toplevels (cwds) they reveal.
425
+ for (const source of sources) {
426
+ if (source.repoLevel) continue;
427
+ const files = listSessionFiles(source.dir, source.fileFilter || isTranscriptFile);
428
+ if (files.length === 0) continue;
429
+ sessions.startTool(source.tool);
430
+ for (const f of files) {
431
+ const objects = readSourceObjects(source, f);
432
+ const cwd = resolveFileCwd(objects);
433
+ if (cwd) seenCwds.add(cwd);
434
+ if (!fileInScope(cwd, selectedCwds)) continue;
435
+ sessions.addFile(f, objects);
436
+ if (onHumanObject) {
437
+ for (const o of objects) onHumanObject(o);
438
+ }
439
+ }
440
+ sessions.endTool();
441
+ }
442
+ // Phase 2: repo-level sources (e.g. Aider) — candidate repos = the selected scope, else
443
+ // the repos seen in phase 1. cwd is the repo toplevel, so attribution is exact.
444
+ const toplevels = selectedCwds instanceof Set ? selectedCwds : seenCwds;
445
+ for (const source of sources) {
446
+ if (!source.repoLevel) continue;
447
+ sessions.startTool(source.tool);
448
+ for (const top of toplevels) {
449
+ if (!fileInScope(top, selectedCwds)) continue;
450
+ const objects = readRepoLevelObjects(source, top);
451
+ if (objects.length === 0) continue;
452
+ sessions.addFile(path.join(top, source.file), objects);
453
+ if (onHumanObject) {
454
+ for (const o of objects) onHumanObject(o);
455
+ }
456
+ }
457
+ sessions.endTool();
458
+ }
459
+ return sessions.result();
460
+ }
461
+
462
+ function collectSessions(env = process.env, selectedCwds = null) {
463
+ return walkSessionSignals(env, selectedCwds, null);
464
+ }
465
+
466
+ module.exports = {
467
+ collectSessions,
468
+ makeSessionsAccumulator,
469
+ walkSessionSignals,
470
+ toolSources,
471
+ listSessionFiles,
472
+ readSessionObjects,
473
+ resolveFileCwd,
474
+ iterateSessionObjects,
475
+ };
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ const { walkSessionSignals } = require('./session-scan');
4
+ const { humanTextsFromObject } = require('./transcript-turns');
5
+ const { makeSteeringAccumulator } = require('./steering-extract');
6
+ const { makeDecisionsAccumulator } = require('./decisions-extract');
7
+ const { makeBlindApprovalAccumulator } = require('./blind-approval-extract');
8
+ const { makeSpecificityAccumulator } = require('./specificity-extract');
9
+
10
+ function collectSessionSignals(env = process.env, selectedCwds = null) {
11
+ const steering = makeSteeringAccumulator();
12
+ const decisions = makeDecisionsAccumulator();
13
+ const blindApproval = makeBlindApprovalAccumulator();
14
+ const specificity = makeSpecificityAccumulator();
15
+ const sessions = walkSessionSignals(env, selectedCwds, (obj) => {
16
+ for (const text of humanTextsFromObject(obj)) {
17
+ steering.add(text);
18
+ decisions.add(text);
19
+ blindApproval.add(text);
20
+ specificity.add(text);
21
+ }
22
+ });
23
+ if (sessions) {
24
+ const approval = blindApproval.result();
25
+ const specific = specificity.result();
26
+ if (approval) Object.assign(sessions, approval);
27
+ if (specific) Object.assign(sessions, specific);
28
+ }
29
+ return {
30
+ sessions,
31
+ steering: steering.result(),
32
+ decisions: decisions.result(),
33
+ };
34
+ }
35
+
36
+ module.exports = { collectSessionSignals };
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ // Shakers hand-drawn "shakers" WORDMARK, inlined VERBATIM from the design asset shakers-hub-frontend/apps/hub/public/images/shakers-text-logo.svg (viewBox 0 0 87 18, 7 glyph <path>s).
4
+
5
+ const WORDMARK_VIEWBOX = { w: 87, h: 18 };
6
+
7
+ const WORDMARK_PATHS = [
8
+ 'M19.2845 8.74573C19.2845 8.74573 16.556 10.0876 15.7698 10.5041C15.7236 11.4295 15.6311 12.355 15.5848 13.2804C15.5386 14.3447 15.4924 14.9462 15.4461 16.0105C15.4461 16.2419 15.4461 16.4732 15.3999 16.6583C15.2611 17.445 14.8449 17.9077 14.2437 17.9077C13.55 17.9077 13.0876 17.3987 13.0876 16.5658C13.0876 15.1776 13.1801 14.2059 13.2263 12.8177C13.2263 12.4475 13.2263 11.8923 13.2263 11.4758C13.2263 11.4758 12.8101 11.6609 12.5326 11.7072C12.0701 11.6609 11.7002 11.4295 11.5614 10.9668C11.4227 10.5041 11.5614 10.0876 11.9314 9.76372C12.0239 9.71745 13.5963 8.74573 13.5963 8.74573C13.6425 6.80228 13.735 4.07221 13.8737 2.17503C13.92 1.80485 13.9662 1.43467 14.105 1.11077C14.2437 0.694314 14.6137 0.462951 15.0761 0.509223C15.5386 0.509223 15.8623 0.786858 15.9548 1.20331C16.0473 1.57349 16.0935 1.98994 16.0935 2.4064C16.0473 3.93339 16.0011 4.95138 15.9548 6.47837C15.9548 6.84856 15.9548 7.21873 15.9548 7.774C17.7122 6.9411 17.8971 6.70974 19.562 5.92311C19.6545 4.53493 19.7007 3.65575 19.7932 2.26758C19.8395 1.80485 19.8857 1.29586 19.9782 0.833131C20.117 0.324133 20.4869 0.000226092 21.0419 0.0464986C21.5968 0.0927711 21.9205 0.416678 22.013 0.925676C22.0593 1.34213 22.0593 1.71231 22.0593 2.12876C22.0593 3.05421 21.9668 3.7483 21.9668 4.81257C22.2905 4.72002 22.7067 4.30357 23.0304 4.2573C23.6779 4.07221 24.2791 4.34984 24.5103 4.90511C24.7416 5.41411 24.5566 5.96938 24.0016 6.33956C23.5854 6.52465 21.7356 7.68146 21.7356 7.68146C21.6893 10.6892 21.5968 12.8177 21.5043 15.8717C21.5043 16.1493 21.5043 16.4732 21.4581 16.7509C21.3193 17.445 20.8569 17.8614 20.2557 17.8151C19.6545 17.7689 19.2383 17.3524 19.1458 16.6583C19.0995 16.3807 19.192 12.4013 19.2845 9.76372C19.2845 9.39354 19.2845 9.20845 19.2845 8.74573Z',
9
+ 'M64.097 4.67388C63.542 4.72016 63.1258 4.76643 62.7558 4.76643C61.9697 4.76643 61.5072 4.48879 61.4147 3.88725C61.3222 3.28571 61.6459 2.82299 62.3859 2.59162C62.9408 2.40653 63.542 2.31399 64.097 2.22144C65.9931 1.99008 67.8429 1.89754 69.6927 2.45281C70.3402 2.6379 70.9414 2.91553 71.4963 3.28571C72.8837 4.21116 73.1612 5.73815 72.2825 7.1726C71.7738 8.0055 70.9876 8.60705 70.1552 9.06977C69.6002 9.39368 69.0453 9.67131 68.3978 9.99522C68.8603 10.4117 73.1149 13.5582 73.4849 14.0209C73.8549 14.4837 73.8086 15.1315 73.4386 15.5479C73.0687 15.9644 72.56 16.0569 72.005 15.8256C71.6813 15.6405 71.4038 15.4091 71.0801 15.1777C69.8315 14.206 67.6579 12.6328 66.3168 11.5685C66.2705 12.0312 65.9931 15.0389 65.9468 16.1032C65.9468 16.3808 65.9468 16.7047 65.8543 16.9824C65.7156 17.5376 65.2069 17.8615 64.6982 17.8153C64.1432 17.769 63.7733 17.4914 63.6345 16.9824C63.542 16.5659 63.542 16.1032 63.542 15.6867C63.727 12.6328 64.097 5.18288 64.097 4.67388ZM66.4555 8.14432C67.7504 7.63532 68.9528 7.1726 69.8777 6.24715C70.2477 5.87697 70.1552 5.27543 69.6927 5.04406C68.6753 4.58134 67.6117 4.53507 66.548 4.44252L66.4555 8.14432Z',
10
+ 'M32.2335 12.8178C31.9097 13.1879 31.6785 13.4656 31.4473 13.6969C30.6149 14.4836 29.8287 15.3627 28.95 16.0568C28.4413 16.4733 27.7939 16.7047 27.1464 16.8897C25.7128 17.2599 24.4641 16.427 24.2792 14.8538C24.1867 14.0208 24.2329 13.0954 24.4641 12.2625C25.3428 9.02342 27.1002 6.29334 29.6437 4.1648C30.2449 3.65581 30.9386 3.28563 31.6323 3.00799C33.5746 2.22136 35.1007 3.8409 35.1007 5.55298C35.0082 6.5247 34.7308 12.4939 34.6845 14.4373C34.6845 14.7612 34.6845 15.1314 34.592 15.4553C34.4533 16.0568 33.9446 16.4733 33.3896 16.427C32.8809 16.3807 32.4185 16.0106 32.326 15.409C32.2335 14.8538 32.2797 14.2985 32.2797 13.6969C32.2797 13.5118 32.2797 13.2342 32.2335 12.8178ZM26.9614 14.3448C29.6899 13.2805 32.6497 8.51442 32.3722 5.64552C32.3722 5.46043 32.1872 5.36789 32.0485 5.46043C29.6899 6.5247 26.684 11.337 26.684 14.1134C26.684 14.2522 26.8227 14.391 26.9614 14.3448Z',
11
+ 'M40.4189 7.91296C41.9451 6.84869 43.2399 5.92324 44.5348 5.04406C44.8586 4.8127 45.1823 4.58134 45.5522 4.39625C46.2459 4.02607 46.8934 4.16489 47.2171 4.67388C47.5408 5.18288 47.4483 5.73815 46.8934 6.24715C46.5234 6.57106 46.1534 6.84869 45.7372 7.12633C44.3498 8.09805 42.9625 9.06977 41.4363 10.134C41.8063 10.3191 41.9913 10.5042 42.2688 10.5968C43.8874 11.3834 45.4598 12.1238 47.0784 12.8641C47.4483 13.0492 47.8183 13.2343 48.0958 13.5119C48.512 13.8821 48.5582 14.3448 48.327 14.8538C48.0958 15.3166 47.6333 15.5479 47.1246 15.4554C46.7546 15.4091 46.4309 15.224 46.1072 15.0852C44.5811 14.3448 43.055 13.6045 41.4826 12.9104C41.2051 12.7716 40.8814 12.6328 40.4652 12.4939C40.4189 12.8178 40.3727 13.0492 40.3727 13.2806C40.3727 14.3448 40.3264 14.9464 40.3264 16.0569C40.3264 16.3808 40.3264 16.7047 40.2802 17.0286C40.1415 17.6765 39.7252 18.0466 39.1703 18.0466C38.6153 18.0466 38.1529 17.6765 38.0141 17.0286C37.9679 16.7047 37.9679 16.3808 37.9679 16.0569C38.0141 10.643 38.1066 7.58905 38.1529 2.17517C38.1529 1.75872 38.1991 1.34227 38.2454 0.972085C38.3379 0.463088 38.7078 0.139181 39.2628 0.092908C39.7715 0.000363052 40.234 0.370543 40.4189 0.925813C40.5114 1.29599 40.5114 2.03635 40.5114 2.03635C40.5114 4.16489 40.4189 4.48879 40.3727 6.61733C40.3727 6.94124 40.3727 7.31142 40.4189 7.91296Z',
12
+ 'M51.5643 9.20847C50.7781 8.88456 50.5468 8.09793 51.0093 7.35757C51.148 7.12621 51.3793 6.89485 51.6105 6.70976C53.8766 4.48868 55.4027 3.19305 57.6687 1.01824C57.9462 0.740604 58.2237 0.509241 58.5012 0.324151C59.0099 0.000243835 59.5648 0.0465163 59.9348 0.416696C60.3048 0.786876 60.351 1.34215 60.0273 1.85114C59.796 2.17505 59.5186 2.49896 59.2411 2.77659C57.6687 4.34986 56.8825 4.9514 55.3102 6.52467C55.079 6.75603 54.8015 7.03366 54.4315 7.45012C55.5877 7.68148 56.5126 7.91284 57.4837 8.1442C57.9462 8.23675 58.4087 8.32929 58.8711 8.46811C59.3798 8.60693 59.796 8.93084 59.8885 9.53238C59.981 10.0876 59.7036 10.5504 59.1024 10.828C57.9462 11.337 56.7901 11.7535 55.5877 12.2162C54.894 12.4938 54.154 12.7715 53.2754 13.1416C53.6453 13.373 56.1889 14.7149 57.345 15.3627C57.6225 15.5478 57.9462 15.6866 58.1774 15.918C58.6399 16.3344 58.7786 16.936 58.5012 17.3987C58.2237 17.8614 57.6687 18.0928 57.0675 17.9077C56.8363 17.8152 56.6051 17.7226 56.3738 17.5838C54.339 16.427 52.4429 15.5478 50.4081 14.3447C50.2694 14.2522 50.0844 14.1596 49.9456 14.0671C49.067 13.373 49.1132 12.355 50.1306 11.846C50.9168 11.4296 55.2177 9.9951 55.2177 9.9951C55.2177 9.9951 51.9342 9.39356 51.5643 9.20847Z',
13
+ 'M83.0116 9.9949C82.0866 9.90236 81.4392 9.85608 80.7455 9.80981C79.7281 9.71727 78.7107 9.62472 77.6933 9.53218C77.4158 9.4859 77.1383 9.43963 76.8608 9.30081C75.566 8.79182 75.1497 7.49619 76.0284 6.2931C76.4446 5.69156 76.9533 5.18256 77.5083 4.71984C79.4044 3.1003 81.5779 2.12858 83.7977 1.20313C83.9827 1.11058 84.214 1.01804 84.4452 1.06431C84.7227 1.11058 85.0464 1.2494 85.1389 1.48076C85.2314 1.7584 85.2314 2.22112 85.0926 2.45248C84.8152 2.82266 84.4452 3.05403 84.0752 3.28539C83.0116 3.88693 81.9016 4.4422 80.8842 5.04374C79.9593 5.59901 79.0806 6.15428 78.2482 7.21855C78.8494 7.26482 79.3119 7.35737 79.7743 7.40364C81.4392 7.54246 83.1041 7.635 84.7689 7.77382C85.1389 7.82009 85.5088 7.86637 85.8326 8.00518C86.9425 8.37536 87.2199 9.43963 86.4338 10.3651C86.0175 10.8741 85.4626 11.3368 84.9076 11.707C83.7052 12.5399 82.4104 13.3265 81.208 14.2057C80.2368 14.8998 79.2656 15.5939 78.2945 16.288C77.9245 16.5193 77.5545 16.797 77.1383 16.9358C76.6296 17.1209 76.2134 16.8895 75.9359 16.4268C75.6584 15.9641 75.7047 15.5013 76.0284 15.0849C76.2596 14.8072 76.5834 14.5759 76.8608 14.3445C78.7107 13.0489 80.5605 11.7995 82.3641 10.5502C82.5029 10.4576 82.6416 10.3188 83.0116 9.9949Z',
14
+ 'M3.56097 8.32919C4.53213 8.51428 5.22582 8.65309 5.96576 8.74564C6.79819 8.88446 7.63061 8.93073 8.50929 9.02327C9.20298 9.11582 9.80418 9.39345 10.0354 10.1338C10.2666 10.9204 9.89667 11.4757 9.34172 11.9384C9.06424 12.1698 8.74052 12.4012 8.46304 12.6325C6.38197 14.1132 4.3009 15.594 2.21983 17.0747C2.08109 17.1672 1.94236 17.306 1.75737 17.3986C1.24867 17.6762 0.693714 17.5837 0.323746 17.1672C-0.0462222 16.7508 -0.0924682 16.1955 0.231254 15.6865C0.462484 15.3163 0.786206 15.0387 1.10993 14.8073C2.63605 13.6968 4.16216 12.6325 5.87327 11.3832C5.36456 11.2444 5.04084 11.1981 4.71712 11.1055C3.83844 10.9204 2.95977 10.7816 2.12734 10.504C0.601222 9.94872 0.231254 8.60682 1.29491 7.31119C1.9886 6.47829 2.82103 5.73793 3.65346 5.04384C4.80961 4.16466 6.05825 3.37803 7.30689 2.5914C8.27806 1.94358 8.97175 1.98985 9.38796 2.63767C9.80418 3.28548 9.5267 3.9333 8.60178 4.62739C7.44563 5.46029 6.24323 6.24692 5.08708 7.07983C4.62462 7.40374 4.20841 7.77392 3.56097 8.32919Z',
15
+ ];
16
+
17
+ module.exports = { WORDMARK_VIEWBOX, WORDMARK_PATHS };