shakers-cli-beta 0.8.0-beta.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 (218) hide show
  1. package/LICENSE +59 -0
  2. package/README.md +1175 -0
  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 +81 -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 +120 -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 +283 -0
  20. package/bin/mcp.js +98 -0
  21. package/bin/me.js +69 -0
  22. package/bin/onboarding.js +134 -0
  23. package/bin/portfolios.js +69 -0
  24. package/bin/rate.js +70 -0
  25. package/bin/register.js +325 -0
  26. package/bin/report-html.js +134 -0
  27. package/bin/save.js +77 -0
  28. package/bin/shakers.js +344 -0
  29. package/bin/share.js +72 -0
  30. package/bin/show-project.js +72 -0
  31. package/bin/socials.js +70 -0
  32. package/bin/superadmin.js +351 -0
  33. package/package.json +40 -0
  34. package/src/agent-category.js +13 -0
  35. package/src/agent-evaluation.js +177 -0
  36. package/src/agent-org-chart.js +355 -0
  37. package/src/agent-synthesis.js +132 -0
  38. package/src/agent-usage.js +118 -0
  39. package/src/agents-client.js +165 -0
  40. package/src/ai-fluency-cell.js +72 -0
  41. package/src/ai-profile-client.js +45 -0
  42. package/src/ai-profile-preview.js +154 -0
  43. package/src/ai.js +66 -0
  44. package/src/alma-client.js +123 -0
  45. package/src/alma-flow.js +32 -0
  46. package/src/alma-repo-context.js +47 -0
  47. package/src/alma-shell.js +161 -0
  48. package/src/ansi.js +62 -0
  49. package/src/applications-client.js +79 -0
  50. package/src/applications-flow.js +58 -0
  51. package/src/auth-client.js +144 -0
  52. package/src/auth-device-client.js +118 -0
  53. package/src/auth-method-picker.js +44 -0
  54. package/src/auth-methods.js +35 -0
  55. package/src/auth-session-store.js +214 -0
  56. package/src/authorship.js +203 -0
  57. package/src/automations-detector.js +170 -0
  58. package/src/availability-client.js +92 -0
  59. package/src/availability-flow.js +105 -0
  60. package/src/backend-request.js +170 -0
  61. package/src/blind-approval-extract.js +48 -0
  62. package/src/brand-ansi.js +14 -0
  63. package/src/brand-svg.js +9 -0
  64. package/src/browser-tools-detector.js +21 -0
  65. package/src/build-next-level.js +56 -0
  66. package/src/certifications-client.js +89 -0
  67. package/src/certifications-flow.js +75 -0
  68. package/src/certify-dimension-client.js +217 -0
  69. package/src/certify-dimension-flow.js +193 -0
  70. package/src/certify-disclaimer.js +51 -0
  71. package/src/certify-remediation-prompt.js +23 -0
  72. package/src/cli-args.js +83 -0
  73. package/src/config-dir.js +120 -0
  74. package/src/config.js +778 -0
  75. package/src/consent-flow.js +115 -0
  76. package/src/consent-skip.js +27 -0
  77. package/src/decisions-extract.js +43 -0
  78. package/src/detect-repos.js +145 -0
  79. package/src/detected-tools.js +15 -0
  80. package/src/detection-evidence.js +105 -0
  81. package/src/detection-noise.js +132 -0
  82. package/src/detectors.js +221 -0
  83. package/src/device-auth-store.js +100 -0
  84. package/src/device-login.js +118 -0
  85. package/src/email-verification.js +152 -0
  86. package/src/env-paths.js +16 -0
  87. package/src/find-projects-cache.js +89 -0
  88. package/src/find-projects-client.js +138 -0
  89. package/src/find-projects-flow.js +164 -0
  90. package/src/git-activity.js +123 -0
  91. package/src/graph-certs.js +58 -0
  92. package/src/graph-generator.js +230 -0
  93. package/src/graph-scan.js +239 -0
  94. package/src/i18n.js +3264 -0
  95. package/src/inter-fonts.js +6 -0
  96. package/src/interactive-select.js +183 -0
  97. package/src/interview-client.js +226 -0
  98. package/src/interview-transcript.js +56 -0
  99. package/src/inventory-client.js +90 -0
  100. package/src/invitations-client.js +66 -0
  101. package/src/invitations-flow.js +50 -0
  102. package/src/lang-client.js +147 -0
  103. package/src/lang-codes.js +14 -0
  104. package/src/lang-flow.js +85 -0
  105. package/src/legal-notice.js +53 -0
  106. package/src/locale.js +51 -0
  107. package/src/markdown-ansi.js +50 -0
  108. package/src/maturity.js +111 -0
  109. package/src/mcp-ai-usage-result-tool.js +109 -0
  110. package/src/mcp-ai-usage-tool.js +333 -0
  111. package/src/mcp-alma-tools.js +91 -0
  112. package/src/mcp-applications-tools.js +42 -0
  113. package/src/mcp-ask-tools.js +50 -0
  114. package/src/mcp-auth-form.js +282 -0
  115. package/src/mcp-availability-tools.js +43 -0
  116. package/src/mcp-certifications-tools.js +42 -0
  117. package/src/mcp-certify-dimension-tools.js +58 -0
  118. package/src/mcp-detector.js +223 -0
  119. package/src/mcp-device-login-tools.js +173 -0
  120. package/src/mcp-find-projects-tools.js +67 -0
  121. package/src/mcp-install.js +245 -0
  122. package/src/mcp-invitations-tools.js +42 -0
  123. package/src/mcp-lang-tools.js +39 -0
  124. package/src/mcp-onboarding-tools.js +388 -0
  125. package/src/mcp-portfolios-tools.js +46 -0
  126. package/src/mcp-profile-tools.js +46 -0
  127. package/src/mcp-rate-tools.js +42 -0
  128. package/src/mcp-register-tools.js +517 -0
  129. package/src/mcp-report-tools.js +101 -0
  130. package/src/mcp-role-tools.js +112 -0
  131. package/src/mcp-saved-positions-tools.js +55 -0
  132. package/src/mcp-server.js +170 -0
  133. package/src/mcp-show-project-tools.js +50 -0
  134. package/src/mcp-socials-tools.js +39 -0
  135. package/src/memory-structure-detector.js +108 -0
  136. package/src/model-call-record.js +198 -0
  137. package/src/onboarding-availability.js +26 -0
  138. package/src/onboarding-client.js +364 -0
  139. package/src/onboarding-flow.js +1078 -0
  140. package/src/onboarding-livekit-client.js +102 -0
  141. package/src/onboarding-status-client.js +42 -0
  142. package/src/open-file.js +32 -0
  143. package/src/osc-link.js +16 -0
  144. package/src/portfolio-client.js +164 -0
  145. package/src/portfolio-git-info.js +61 -0
  146. package/src/portfolios-client.js +85 -0
  147. package/src/portfolios-flow.js +79 -0
  148. package/src/profile-client.js +125 -0
  149. package/src/profile-flow.js +115 -0
  150. package/src/profile-switch.js +46 -0
  151. package/src/prompt-select.js +29 -0
  152. package/src/rate-client.js +82 -0
  153. package/src/rate-flow.js +137 -0
  154. package/src/render-ai-fluency.js +266 -0
  155. package/src/render-certification.js +177 -0
  156. package/src/render-html.js +140 -0
  157. package/src/render-report-text.js +27 -0
  158. package/src/render-sheet.js +261 -0
  159. package/src/render-terminal.js +109 -0
  160. package/src/render-usage-report-text.js +155 -0
  161. package/src/repo-context.js +215 -0
  162. package/src/report-gating.js +49 -0
  163. package/src/report-store.js +226 -0
  164. package/src/report-theme.js +177 -0
  165. package/src/report-view-client.js +50 -0
  166. package/src/reveal-output.js +29 -0
  167. package/src/roadmap-content.js +453 -0
  168. package/src/roadmap-personalization.js +132 -0
  169. package/src/roadmap-prompt.js +154 -0
  170. package/src/roles-client.js +114 -0
  171. package/src/roles-flow.js +192 -0
  172. package/src/sanitize-network-text.js +36 -0
  173. package/src/saved-positions-client.js +71 -0
  174. package/src/saved-positions-flow.js +50 -0
  175. package/src/scan-exclusions.js +50 -0
  176. package/src/scanner.js +612 -0
  177. package/src/scope-signals.js +148 -0
  178. package/src/secret-prompt.js +72 -0
  179. package/src/session-gate.js +30 -0
  180. package/src/session-refresh.js +51 -0
  181. package/src/session-scan.js +336 -0
  182. package/src/session-signals.js +36 -0
  183. package/src/shakers-wordmark.js +17 -0
  184. package/src/share-card.js +362 -0
  185. package/src/share-certification.js +111 -0
  186. package/src/share-consent-state.js +157 -0
  187. package/src/share-payload.js +300 -0
  188. package/src/share.js +184 -0
  189. package/src/show-project-client.js +58 -0
  190. package/src/show-project-flow.js +119 -0
  191. package/src/signup-client.js +91 -0
  192. package/src/skills-client.js +102 -0
  193. package/src/socials-client.js +91 -0
  194. package/src/socials-flow.js +83 -0
  195. package/src/specificity-extract.js +41 -0
  196. package/src/start-add-agents.js +306 -0
  197. package/src/start-add-portfolio.js +388 -0
  198. package/src/start-add-skills.js +260 -0
  199. package/src/stdin-ask.js +129 -0
  200. package/src/steering-extract.js +43 -0
  201. package/src/strip-internal-comments.js +45 -0
  202. package/src/tech-detector.js +295 -0
  203. package/src/templates/report-sheet.html +572 -0
  204. package/src/terminal-agents.js +199 -0
  205. package/src/terminal-format.js +85 -0
  206. package/src/terminal-progress.js +106 -0
  207. package/src/terminal-sections.js +219 -0
  208. package/src/tier-analysis.js +126 -0
  209. package/src/tier-engine.js +114 -0
  210. package/src/tier-projection.js +117 -0
  211. package/src/transcript-turns.js +49 -0
  212. package/src/translations-client.js +84 -0
  213. package/src/typewriter.js +154 -0
  214. package/src/usage-model-calls.js +119 -0
  215. package/src/usage-oneshot-commands.js +180 -0
  216. package/src/usage-repo-scope.js +162 -0
  217. package/src/usage-run-steps.js +338 -0
  218. package/src/work-streams.js +74 -0
package/README.md ADDED
@@ -0,0 +1,1175 @@
1
+ # Shakers `shakers`
2
+
3
+ A **local-first** Shakers CLI (`shakers`) — a plain argument dispatcher:
4
+ `shakers <command> [flags]` runs one command and exits; run `shakers` with no
5
+ command to print the usage help. There is no interactive shell (the former
6
+ REPL was retired — ADR-060). Its commands:
7
+
8
+ - **`ai-usage`** — builds, **locally**, a deterministic profile of your AI
9
+ tool setup: which copilots/agents you have configured, how deep that
10
+ configuration goes, and what "level-up" **tier** (T0-T7) you're at. Its
11
+ terminal output leads with **why** you're at that tier, then the score, your
12
+ tools, technologies and a **one-line-per-agent** summary (each agent scored
13
+ for how well it's built + how often you've actually used it). The tier
14
+ roadmap / next-steps is available behind `--roadmap`.
15
+ - **`certify`** — certifies Skills from your Shakers catalog by analyzing your
16
+ local project's code (resolve which Skills apply, then sample and send
17
+ secret-scrubbed code for a per-Skill assessment), and certifies how deeply
18
+ you command your own agents (`certify agents`) through an interview. Both
19
+ require you to be [logged in](#login-login--logout) first.
20
+ - **`report`** — builds and **opens** the full, shareable HTML report for the
21
+ current project (usage + certified Skills, with the complete per-agent
22
+ detail). `ai-usage`/`certify` persist their result silently; `report` is how
23
+ you produce and open the HTML to share with your team.
24
+ - **`share`** — *(temporarily disabled)* turns the project's latest `ai-usage`
25
+ result (tier + score) into a branded card you can post on LinkedIn (built
26
+ offline; the PNG is exported in your browser). Invoking it prints a disabled
27
+ notice and runs nothing.
28
+ - **`login` / `logout`** — identify yourself as a registered Talent (email
29
+ or Google). See [Login](#login-login--logout).
30
+ - **`add-role` / `change-role`** — manage your roles: add a growth role from the
31
+ catalog, or switch your MAIN role to one you already hold (arrow-key pickers).
32
+ The onboarding interview also ends with the same MAIN-role step. See
33
+ [Command families](#command-families) for the `role`/`profile`/`add` groupings.
34
+ - **`superadmin`** — a non-production, password-gated test tool for the team
35
+ running this CLI against the service; not something a Talent uses. See
36
+ [Superadmin](#superadmin-non-production-only).
37
+
38
+ **Session gating (ADR-061).** `ai-usage` is the only command a non-registered
39
+ (session-less) caller can run — the anonymous AI-usage evaluation, with its
40
+ consent → email → persistence flow. Every other command requires an active
41
+ session: exempt are `ai-usage` and `login`; `logout` without a session is a
42
+ silent no-op; `certify`/`report`/`share` require `shakers login` first
43
+ (`superadmin` is its own non-prod escape hatch, exempt too). A session-less
44
+ call to a gated command prints a clear error naming what is missing and both
45
+ `shakers login` and `shakers register`; in non-TTY/agent mode it exits with a
46
+ non-zero code. `ai-usage` is public: when no session exists it only reminds the
47
+ Talent they can sign in (a Yes/No prompt on a TTY), never blocks. The former
48
+ standalone `ai-footprint`/`ai-certify` binaries were retired earlier; the
49
+ interactive shell that replaced them is now retired too (see
50
+ [`docs/decisions.md`](docs/decisions.md): ADR-060 REPL→argv, ADR-061 gating).
51
+
52
+ The AI usage mechanism is inspired by `darnoux/claude-code-level-up` (scan
53
+ local signals and classify by level), extended to cover the main AI tools on
54
+ the market (not just Claude) and to a full 8-tier ladder with per-tier roadmap
55
+ content.
56
+
57
+ ## What it does
58
+
59
+ 1. **Scans** the current project (and relevant parts of your home
60
+ directory) for known AI-tool configuration.
61
+ 2. **Classifies** the setup into a tier, `T0` to `T7` ("empty bench" to
62
+ "orchestrated workshop"), computed deterministically — no LLM involved
63
+ in the level itself.
64
+ 3. **Leads with a "why this tier" analysis**: exactly which criteria you meet,
65
+ with the signal behind each one, and the single next criterion blocking
66
+ progression to the next tier — shown FIRST, before the score.
67
+ 4. **Evaluates your agents**: one line per agent (nested subagents drawn with
68
+ stacked `↓`), each with a **definition-quality score** (0-100, how well the
69
+ agent is built) and a **usage signal** (how often you've actually invoked it,
70
+ read from your local Claude Code history — usage only, never session
71
+ content). Full per-agent detail (rationale included) lives in the `report`
72
+ HTML.
73
+ 5. **Roadmap behind `--roadmap`**: current tier → next tier, with what it
74
+ unlocks, steps, a copyable code snippet, community tips and common mistakes,
75
+ plus a ready-to-paste implementation prompt — curated, authored content, not
76
+ generated per run. Hidden from the default output to keep the terminal clean;
77
+ pass `--roadmap` (alias `--next`) to see it.
78
+
79
+ Everything above runs **always, locally, unconditionally** — nothing is
80
+ gated behind a decision. See [Privacy & consent](#privacy--consent-model)
81
+ for what happens if you choose to save your report to Shakers.
82
+
83
+ ### Agent evaluation (the LLM feature)
84
+
85
+ The definition-quality score is the one AI-model-backed part of `ai-usage`. As
86
+ with the org-chart synthesis and roadmap personalization, the CLI never calls a
87
+ model directly: it POSTs the scrubbed agent definitions to the certifications
88
+ service's `agent-evaluation` endpoint (a sibling of your configured ingest
89
+ endpoint) that runs the model server-side (**gemini-2.5-flash**) and returns a
90
+ score + short rationale per agent. It degrades gracefully: no endpoint, a network error, or a
91
+ timeout simply means no scores are shown — the rest of the report is unaffected.
92
+ The **usage** signal is entirely local (no network, no model). See
93
+ [Privacy & consent](#privacy--consent-model).
94
+
95
+ ## Installation
96
+
97
+ One line:
98
+
99
+ ```bash
100
+ curl -fsSL https://raw.githubusercontent.com/devShakers/ai-usage-evaluator/main/install.sh | bash
101
+ ```
102
+
103
+ The installer:
104
+
105
+ - Requires **Node 18+** (checks and fails clearly if missing/too old).
106
+ - Discovers the CLI's own `src/*.js` modules dynamically (via a local copy
107
+ or the GitHub API) rather than hardcoding a file list.
108
+ - Places the tool in `~/.shakers/` and drops the single `shakers`
109
+ command in `~/.local/bin` (the standalone `ai-footprint`/`ai-certify`
110
+ launchers are no longer created, and any legacy ones are removed on
111
+ upgrade). The only third-party package is **`@livekit/rtc-node`** (the
112
+ LiveKit interview engine), fetched post-install for every flavor;
113
+ `SHAKERS_SKIP_NATIVE_INSTALL=1` skips that fetch (offline installs / CI).
114
+ - All installer output is in English, unconditionally (it runs before
115
+ Node/the CLI's own i18n layer is available).
116
+
117
+ Alternative, cloning the repo first (same result, lets you review the code
118
+ before installing):
119
+
120
+ ```bash
121
+ git clone https://github.com/devShakers/ai-usage-evaluator
122
+ cd ai-usage-evaluator
123
+ ./install.sh
124
+ ```
125
+
126
+ Uninstall: `./install.sh --uninstall`.
127
+
128
+ ## Publishing (maintainers)
129
+
130
+ One codebase ships in three flavors; the baked backend target is derived from
131
+ the package **name** (`src/config.js#PACKAGE_NAME_ENV`), overridable per run
132
+ with `SHAKERS_CLI_ENV=staging|dev|local`. Per-endpoint `SHAKERS_CLI_*_BASE`
133
+ env vars and `config.json` always win over the baked default.
134
+
135
+ | Flavor | Package name | Registry | Baked target | Publish |
136
+ |---|---|---|---|---|
137
+ | Private (current) | `@shakers/shakers-cli` | GitLab proj 201 (`publishConfig`) | dev (new-works) | `export SHAKERS_GITLAB_TOKEN=<PAT, scope write_registry on proj 201>` then `npm publish` |
138
+ | Public beta | `shakers-cli-beta` | npmjs (public) | staging | `node scripts/prepare-beta.js` → `npm publish --tag beta` → `git checkout -- package.json` |
139
+ | Local | (checkout, `@shakers/shakers-cli`) | — | dev, or `SHAKERS_CLI_ENV=local` for localhost | run from the clone; no publish |
140
+
141
+ Both publishes bump `version` first (the registry rejects re-publishing an
142
+ existing version). The private one publishes from a checked-out `main` with the
143
+ committed `@shakers/shakers-cli` manifest, unchanged. The beta one uses
144
+ `scripts/prepare-beta.js` to stamp `name`/`version`/`publishConfig` (version
145
+ `0.8.0-beta.0`, dist-tag `beta`), publishes, then restores the manifest with
146
+ git. The npmjs org/account for `shakers-cli-beta` must exist and you must be
147
+ `npm login`ed to it (or set `NODE_AUTH_TOKEN`) before publishing — nothing here
148
+ publishes automatically.
149
+
150
+ `@livekit/rtc-node` is a real `dependencies` entry, so `npm i` / `npm i -g` of
151
+ either package pulls it automatically (GitLab's packument keeps `dependencies`
152
+ but strips `optionalDependencies`, which is why it must not be optional).
153
+
154
+ ## Usage
155
+
156
+ `shakers <command> [flags]` runs one command and exits. With no command it
157
+ prints the usage help:
158
+
159
+ ```bash
160
+ shakers # print the usage help and exit
161
+ shakers --lang es|en # force the CLI copy language instead of OS-locale detect
162
+ ```
163
+
164
+ The commands (only `ai-usage` and `login` run without a session — see
165
+ [Session gating](#shakers-shakers)):
166
+
167
+ ```bash
168
+ shakers ai-usage # scan this project + machine, print the report (no HTML link) — anonymous
169
+ shakers certify # certify a DIMENSION of your role over a LiveKit interview (needs login)
170
+ shakers report # build + open the full shareable HTML report (needs login)
171
+ shakers share # branded LinkedIn card (temporarily disabled)
172
+ shakers login # identify yourself as a registered Talent (email or Google)
173
+ shakers logout # forget the stored session (no-op if none)
174
+ shakers superadmin # non-production test tool — not for Talents
175
+
176
+ # Marketplace & profile (all need login):
177
+ shakers alma # interactive mini-shell to chat with Alma, your AI assistant
178
+ shakers find-projects # list the Projects/Positions available to you (paginated, filterable)
179
+ shakers show-project # detail of one project/position (by number or id)
180
+ shakers save-project # bookmark a project/position (by number or id)
181
+ shakers unsave-project # remove a bookmark (by number or id)
182
+ shakers applications # projects you applied to + status
183
+ shakers invitations # your unread project invitations
184
+ shakers availability # view (or --set) your availability
185
+ shakers rate # view (or --set) your per-project rate
186
+ shakers certifications# your certified / uncertified / expired role dimensions
187
+ shakers add-role # add a role to your profile (a growth role) — arrow-key picker
188
+ shakers change-role # switch your MAIN role to one you already have — arrow-key picker
189
+ shakers me # your profile summary (alias: shakers profile)
190
+ ```
191
+
192
+ #### Command families
193
+
194
+ The flat commands above are also grouped into **families** — a friendlier way to
195
+ discover them for both humans and agents. `shakers <family> <subcommand>` runs the
196
+ exact same handler; **every flat command keeps working as an alias**, so nothing
197
+ breaks. Run `shakers <family>` with no subcommand to list its subcommands, and
198
+ `shakers` (no args) prints the full family map.
199
+
200
+ | Family | Subcommands | Flat aliases |
201
+ |--------|-------------|--------------|
202
+ | `add` | `skill`, `agent`, `project`, `role` | `add-skill`, `add-agent`, `add-project`, `add-role` |
203
+ | `role` | `add`, `change` (`main`) | `add-role`, `change-role` |
204
+ | `profile` | `show`, `rate`, `languages`, `socials`, `experiences`, `portfolios`, `availability`, `certifications` | `me`, `rate`, `lang`, `socials`, `experiences`, `portfolios`, `availability`, `certifications` |
205
+ | `projects` | `find`, `show`, `save`, `unsave`, `invitations`, `applications` | `find-projects`, `show-project`, `save-project`, `unsave-project`, `invitations`, `applications` |
206
+ | `usage` | `run`, `report`, `share` | `ai-usage`, `report`, `share` |
207
+ | `certs` | `list`, `certify` | `certifications`, `certify` |
208
+ | `account` | `login`, `logout`, `register`, `config` | `login`, `logout`, `register`, `config` |
209
+
210
+ ```bash
211
+ shakers role add # == shakers add-role
212
+ shakers role change # == shakers change-role
213
+ shakers profile rate # == shakers rate
214
+ shakers projects find # == shakers find-projects
215
+ shakers usage run # == shakers ai-usage
216
+ shakers account login # == shakers login
217
+ shakers profile # no subcommand → your profile summary (== shakers me)
218
+ shakers projects # no subcommand → lists the "projects" subcommands
219
+ ```
220
+
221
+ Each command keeps all its flags:
222
+
223
+ ```bash
224
+ shakers ai-usage --json # report as JSON on stdout
225
+ shakers ai-usage --root ../other # scan another directory instead of the current one
226
+ shakers ai-usage --roadmap # also show the tier roadmap / next-steps (hidden by default; alias --next)
227
+ shakers ai-usage --no-save # persist nothing (report only, on screen)
228
+ shakers ai-usage --build-next-level # secondary path: write deterministic starter file(s) for the next tier
229
+ shakers ai-usage --force # with --build-next-level, overwrite an existing file
230
+ shakers ai-usage --lang es|en # force the report language instead of OS-locale detect
231
+ shakers ai-usage --consent-status # view your save decision / email / last send
232
+ shakers ai-usage --consent-revoke # revoke consent to save (-> denied), no more sends
233
+ shakers ai-usage --consent-reset # clear the decision (-> undecided), ask again next run
234
+ shakers ai-usage --consent-email E # change the email on file, without touching the decision
235
+ shakers ai-usage --set-endpoint URL # persist the ingest endpoint (see "Configuration" below)
236
+ shakers ai-usage --show-endpoint # show the effective endpoint and where it comes from
237
+ ```
238
+
239
+ `ai-usage` no longer prints a report link — its output is the clean terminal
240
+ view. Use `report` to produce and open the full HTML (needs login):
241
+
242
+ ```bash
243
+ shakers report # materialize + open this project's HTML report in the browser
244
+ shakers report --root ../other # report for another project
245
+ shakers report --no-open # just print the file:// link, don't launch the browser
246
+ shakers report --lang es|en # force the surrounding CLI copy + report language
247
+ ```
248
+
249
+ The `share` command reuses the last ai-usage stored for the project (needs login):
250
+
251
+ ```bash
252
+ shakers share # build the LinkedIn card for this project's ai-usage
253
+ shakers share --root ../other # card for another project's ai-usage
254
+ shakers share --lang es|en # force the surrounding CLI copy language (the card is English)
255
+ ```
256
+
257
+ ### Marketplace & profile
258
+
259
+ These talk to the Shakers platform on your behalf (they use your login session,
260
+ never a project scan). All need `shakers login` first.
261
+
262
+ **Chat with Alma.** Open an interactive mini-shell with your AI assistant from the
263
+ terminal. The conversation keeps its context across turns, and Alma can act on your
264
+ profile (with an arrow-key confirmation before anything irreversible):
265
+
266
+ ```bash
267
+ shakers alma # opens the conversation; type "exit" to quit
268
+ ```
269
+
270
+ Before the first message, `alma` asks for **consent to share a code-free map of the
271
+ current project** (its name, services, agents, technologies — never your source
272
+ code) so Alma can answer with context. If you decline — or there is no terminal to
273
+ ask — every message is sent **without** that context. The consent is CLI-side only.
274
+
275
+ **Find and open projects.** `find-projects` lists the positions available to you,
276
+ ordered by match, paginated and filterable server-side:
277
+
278
+ ```bash
279
+ shakers find-projects # first page (grouped All / Saved)
280
+ shakers find-projects --page 2 --limit 20 # paginate
281
+ shakers find-projects --attendance remote --country ES # filter by work mode / country
282
+ shakers find-projects --tab saved # only your saved positions
283
+ shakers find-projects --json # machine output (includes ids)
284
+ ```
285
+
286
+ **Reference a project by its number.** `find-projects` and `applications` hide the
287
+ raw ids from the human listing and print a `N)` number instead. `show-project`,
288
+ `save-project` and `unsave-project` accept **that number** (from the last listing)
289
+ or a full id:
290
+
291
+ ```bash
292
+ shakers show-project 3 # detail of item 3 from the last listing
293
+ shakers save-project 3 # bookmark item 3
294
+ shakers unsave-project 3 # remove the bookmark
295
+ shakers show-project <uuid> # a raw id also works (for scripts / --json)
296
+ ```
297
+
298
+ **Your applications and invitations:**
299
+
300
+ ```bash
301
+ shakers applications # positions you applied to + status (also refreshes the number cache)
302
+ shakers invitations # your unread project invitations
303
+ ```
304
+
305
+ **Your profile.** View by default; the two setters (`--set`) are interactive with
306
+ an explicit confirmation before writing (they change what clients see):
307
+
308
+ ```bash
309
+ shakers me # summary: headline, role, % complete, rate, availability, languages, skills/agents count
310
+ shakers certifications # your role dimensions by state (certified / uncertified / expired) + band
311
+ shakers availability # view your availability (open-to-work, hours/month, location)
312
+ shakers availability --set # change it — arrow-key pickers, then confirm
313
+ shakers rate # view your per-project rate
314
+ shakers rate --set # change it — pick modality + currency, type the amount, then confirm
315
+ ```
316
+
317
+ **Your roles.** A talent has one **MAIN role** plus optional **growth roles**. Both
318
+ commands use the same arrow-key picker as the rest of the CLI:
319
+
320
+ ```bash
321
+ shakers add-role # pick a role from the catalog and add it as a growth role
322
+ shakers change-role # switch your MAIN role to one you already have
323
+ ```
324
+
325
+ `add-role` (alias `shakers role add`) reads the role catalog (the roles you do not
326
+ already hold) and adds the one you pick. `change-role` (alias `shakers role change`)
327
+ lists the roles you already hold and sets the one you pick as your MAIN role.
328
+
329
+ Setting your **MAIN role** is a step of **registration** (`shakers register`): after
330
+ you take or skip the interview during the alta, it offers your resolved/recommended
331
+ roles and lets you set your MAIN role. Running the interview on its own
332
+ (`shakers onboarding`) does **not** ask for the main role — to change it later use
333
+ `shakers change-role`.
334
+
335
+ **Repeat the onboarding interview.** `shakers onboarding --repeat` (alias
336
+ `shakers onboarding repeat`) re-runs the **interview only**, **overwriting** the
337
+ previous one (it does not touch your main role). Because the prior evaluation is
338
+ discarded and it cannot be undone, it first shows a clear warning and requires an
339
+ **explicit Yes/No confirmation** (arrow-key selector); on a non-TTY, pass `--yes` to
340
+ confirm. Only after you confirm does it reset the interview and run it again.
341
+ Declining aborts with no change. If you have no onboarding yet it tells you to run
342
+ `shakers onboarding` first.
343
+
344
+ ```bash
345
+ shakers onboarding # the interview (+ final main-role step)
346
+ shakers onboarding --repeat # warn + confirm, then overwrite and re-run
347
+ shakers onboarding repeat --yes # same, pre-confirmed (non-interactive / agents)
348
+ ```
349
+
350
+ Every listing/detail is also available as `--json` for scripting, and every
351
+ command takes `--lang es|en`.
352
+
353
+ Without installing, from a copy of the repo, `shakers <cmd>` is equivalent to
354
+ `node bin/shakers.js <cmd>`.
355
+
356
+ ### Human vs. agent mode (dual TTY contract)
357
+
358
+ Every command keeps the same dual contract it had before the shell was
359
+ retired: on a real terminal it is **interactive** (prompts, masked secret
360
+ input, pickers); on a non-TTY (piped stdin, CI, an agent) it runs **one-shot**
361
+ and never blocks on a prompt it cannot answer. Each interactive question has a
362
+ flag equivalent (`--email`, `--root`, `--skill`, `--accept-disclaimer`,
363
+ `--consent-email`, …), read-heavy commands print machine-readable output with
364
+ `--json` (`ai-usage --json`), and **exit codes are meaningful** — a gated
365
+ command with no session, or a command that failed, exits non-zero, so an agent
366
+ can branch on the result.
367
+
368
+ Results are saved in `~/.config/shakers/` (a per-project cumulative HTML
369
+ report plus its `report-state.json`). **Nothing is ever written to the
370
+ scanned project** — the report can't slip into a commit by accident.
371
+
372
+ ### MCP server (`shakers mcp`)
373
+
374
+ `shakers mcp` speaks JSON-RPC over stdio so a desktop AI app (Claude / ChatGPT
375
+ Desktop) can drive the same features as tools. It exposes, among others:
376
+
377
+ - **AI usage**: `ai_usage`, `ai_usage_result`.
378
+ - **Auth**: `login`, `logout`, `social_signin_start`, `social_signin_poll`.
379
+ - **Register / onboarding**: `register`, `register_status`,
380
+ `onboarding_interview_start`, `onboarding_interview_turn`,
381
+ `onboarding_interview_complete`, `repeat_onboarding_interview`
382
+ (OVERWRITE-repeat — the agent must warn the talent it is irreversible before
383
+ calling), `onboarding_finish`.
384
+ - **Roles**: `list_available_roles`, `add_role`, `list_my_roles`, `set_main_role`
385
+ — `set_main_role` is the onboarding **final step** (after
386
+ `onboarding_interview_complete`, or if the interview was skipped, offer
387
+ `list_my_roles` and set the picked one).
388
+ - **Profile / skills / agents**: `list_addable`, `add_skill`, `add_agent`,
389
+ `add_project`, `get_profile`, `get_rate`, `get_languages`, `list_socials`,
390
+ `list_experiences`, `list_portfolios`, `get_availability`.
391
+ - **Marketplace**: `find_projects`, `show_project`, `save_project`,
392
+ `unsave_project`, `list_invitations`, `list_applications`.
393
+ - **Certifications**: `list_certifications`, `list_certifiable_dimensions`.
394
+ - **Alma**: `ask`.
395
+
396
+ MCP tool names are **stable** (renaming would break existing client configs); the
397
+ role tools above were added, not renamed.
398
+
399
+ ## Try it
400
+
401
+ Today the CLI runs from a repo checkout with `node bin/shakers.js` (the npm
402
+ package is `shakers`, run with `npx shakers <command>` — e.g. `npx shakers ai-usage` — which is later work). Verified commands:
403
+
404
+ ```bash
405
+ node bin/shakers.js # no command → prints the usage help (exit 0)
406
+ node bin/shakers.js ai-usage # anonymous AI-usage evaluation (no session needed)
407
+ node bin/shakers.js ai-usage --help # the command's own flags/help
408
+ node bin/shakers.js login # sign in (needs an endpoint configured)
409
+ node bin/shakers.js logout # sign out (silent no-op if no session)
410
+ node bin/shakers.js certify # gated: with no session → clear error, exits 1
411
+ ```
412
+
413
+ Gating behaviour to expect, session-less: `ai-usage` and `login` run; `logout`
414
+ is a silent no-op (exit 0); `certify`/`report`/`share` print "requires an
415
+ active session" naming both `shakers login` and `shakers register`, and **exit
416
+ non-zero** (so an agent can branch on it). An unknown command (e.g. the removed `start`) also exits
417
+ non-zero.
418
+
419
+ ## Skill certification (`certify`)
420
+
421
+ `certify` (run as `shakers certify`, alias `certify skills`) certifies
422
+ Skills from your Shakers catalog by analyzing your local project. **It
423
+ requires you to be [logged in](#login-login--logout) first** (ADR-050) — see
424
+ the login gate below; there is no email-only path any more. It runs in two
425
+ phases, back to back:
426
+
427
+ 1. **Resolve** — detects your project technologies, asks the certifications
428
+ service which map to a Skill you can certify, and shows certifiable vs
429
+ non-certifiable. Only the detected **technology names** (plus an email
430
+ audit hint, when one is on file) leave the machine here — the call is
431
+ authenticated by your login session's bearer token, not by the email.
432
+ 2. **Certify** — you pick ONE certifiable Skill to certify (interactive
433
+ single-select, or `--skill <name>` / `--skill <position>` non-interactively
434
+ — one Skill per run, so the mandatory interview below always has exactly
435
+ one Skill to interview about); the CLI takes a deterministic **code
436
+ sample** for it, runs a secret-scrub pass, sends it for a Skill assessment,
437
+ and shows the result. The assessment report is **always shown**; the
438
+ analyzed result is persisted to Shakers only if you granted consent up
439
+ front. Skill scores are **indicative and not reproducible** — not an
440
+ official qualification. This is the single most invasive payload in this
441
+ repo — your **actual sampled code** — so if you granted consent, it's also
442
+ the clearest case of what [Privacy & consent model](#privacy--consent-model)
443
+ describes as Datadog capture: a third party retaining that code for up to
444
+ three months, with no selective deletion afterwards. Right after the
445
+ code-only result, a short **interview about that same Skill** starts
446
+ automatically (see below) — it is no longer optional.
447
+
448
+ ```bash
449
+ shakers certify # resolve + certify a Skill for the current project
450
+ shakers certify --root ../other # analyze another directory
451
+ shakers certify --skill React # certify the Skill by name (no interactive select)
452
+ shakers certify --skill 1 # certify the Skill at this position in the resolve report
453
+ shakers certify --email you@shakers.com
454
+ shakers certify --lang es|en
455
+ shakers certify --accept-disclaimer # accept the legal disclaimer non-interactively
456
+ ```
457
+
458
+ Unlike `ai-usage` (which always produces a local report), `certify` is
459
+ inherently server-side: it requires an endpoint — `SHAKERS_CLI_CERTIFY_ENDPOINT`,
460
+ or (its usual case) the resolved `SHAKERS_CLI_INGEST_ENDPOINT` from which the
461
+ certify route is derived as a sibling (see
462
+ [Configuration](#configuration-environment-variables)) — there is no
463
+ local-only certification and, since ADR-042, only ONE backend to configure. The
464
+ flow is deliberately front-loaded, and checked in this order, entirely before
465
+ any Skill is chosen: (1) the endpoint must be configured, (2) your project's
466
+ technologies are detected locally (no egress yet), (3) the **login gate**
467
+ refuses — with no egress at all — a run with no active session (a superadmin
468
+ session bypasses it, see [Superadmin](#superadmin-non-production-only)), (4) a
469
+ legal disclaimer is shown that you must explicitly accept (the code-egress
470
+ gate), and (5) — the first time, and skipped entirely for a logged-in Talent
471
+ whose session already carries an email — the save-to-Shakers consent and an
472
+ email OTP verification are handled, still before you choose a Skill. The
473
+ disclaimer assumes the project is your own and attributes responsibility to
474
+ you, so **never run it on a third party's code** (e.g. a client under NDA).
475
+ For local end-to-end testing, run the real certifications service and point
476
+ the endpoint at loopback (see "Local end-to-end testing" below); the in-repo
477
+ stub that used to serve this route is retired. The real implementation lives
478
+ in `sh-web-works-certifications` — the CLI's only backend.
479
+
480
+ ### Skill interview — starts on its own, consent still required
481
+
482
+ Once `certify skills` shows the code-only assessment for the Skill you
483
+ certified, it goes straight into a short **interview about that same Skill**:
484
+ a few questions about the sampled code (an antifraud check) and about your
485
+ general command of the tool, over the same session/turn/verdict machine
486
+ `certify agents` uses. There is no "do you want to try it?" prompt any more —
487
+ it always reaches the interview when stdin is an interactive terminal and an
488
+ interview endpoint is configured. What it does NOT skip is consent: a legal
489
+ notice (what is asked, and what happens to your answers — including the
490
+ third-party Datadog retention if you gave content consent) is shown and you
491
+ must explicitly accept it before anything about the interview leaves the
492
+ machine; declining (or answering nothing) stops there, visibly, and your
493
+ code-only Skill certification is unaffected either way. Running from a
494
+ pipe/CI (no TTY) cannot host an interview or a consent answer, so it is
495
+ skipped with a visible message. The server combines the interview with the
496
+ code score into an attested combined level (Middle/Senior/Expert), which can
497
+ raise or lower what the code alone gave you.
498
+
499
+ ### Agent certification (`certify agents`) — an interview, not a form
500
+
501
+ `certify agents` certifies how deeply you command ONE of your own agents
502
+ (`.claude/agents/*.md`). It is a real **interview**: the service opens a
503
+ session, asks **one question per turn**, decides which of the five assessment
504
+ areas each question probes, and decides when to stop. The CLI is the courier —
505
+ it re-sends the agent **definition** (never persisted server-side, only hashed)
506
+ and your answer, and it deliberately cannot choose the question or the area.
507
+
508
+ ```bash
509
+ shakers certify agents # interview + verdict for one agent
510
+ shakers certify agents --root ../other
511
+ shakers certify agents --email you@shakers.com
512
+ shakers certify agents --lang es|en
513
+ ```
514
+
515
+ What is worth knowing before you start:
516
+
517
+ - **Either the whole flow, or nothing is asked of you.** Certifying now needs
518
+ an **active [login](#login-login--logout) session** (ADR-050) — a session
519
+ proves control of the account, which is a higher bar than the
520
+ email+OTP check this used to be. That is checked **locally, before you are
521
+ asked for anything** — no address, no agent picker, no egress — and if
522
+ you're not logged in it stops right there with a distinct message for
523
+ "never logged in" vs "your session expired," and tells you to run `login`.
524
+ A non-production superadmin session bypasses this (see
525
+ [Superadmin](#superadmin-non-production-only)). `--email` (or the address
526
+ already on file) still travels as an optional audit hint the server matches
527
+ against your session — it is no longer what authenticates you. The
528
+ disclosure still comes first, because the login check itself sends nothing.
529
+ - **You can stop and come back.** Progress lives in
530
+ `~/.config/shakers/interview-session.json` (permissions `600`), written
531
+ atomically after every turn. If the process dies — Ctrl-C, a dropped
532
+ connection, a closed laptop — re-run `certify agents` and it offers to
533
+ continue where you left off, re-sending the answer that was in flight so you
534
+ never retype it. The file is deleted when the verdict arrives (or when the
535
+ session expires), and it holds a **bearer token** for your interview plus the
536
+ answer currently in flight, which is why it is `600` and why nothing prints
537
+ it.
538
+ - **There is a limit of 5 interviews per 24 hours**, and an abandoned session
539
+ still counts. That is why declining a resume tells you the attempt is already
540
+ spent, and why a transient network failure never silently opens a second
541
+ session.
542
+ - **Answers are capped at 4000 characters, counted after the secret-scrub
543
+ pass.** Scrubbing can make text *longer* (a path or a key becomes
544
+ `[REDACTED]`), so an answer that fits raw can be refused. You are told before
545
+ the first question, and an over-long answer is refused **locally**, with the
546
+ exact overshoot — never as a server error after you wrote three paragraphs.
547
+ - **You see the turn number and the areas you have been ASKED ABOUT, each with
548
+ how many turns it has had — not how many turns are left.** "Asked about" is not
549
+ "covered": whether an area is substantiated is the grader's judgement, and one
550
+ question is never it. The per-area count is there so a lopsided interview (five
551
+ turns on one area, one on another) is visible instead of invisible. The service
552
+ owns the stop condition; showing you its budget would turn "when does this end"
553
+ into something to play against.
554
+ - **Before the first question you are told what is being judged**: the reasoning
555
+ behind each decision, the alternatives you discarded and why, and concrete
556
+ cases where it showed. It is not a request for longer answers — a claim with no
557
+ reasoning behind it does not evidence command however long it is.
558
+ - **Two different lifetimes for what you write**, both stated in the disclosure
559
+ you accept before anything leaves: (1) inside Shakers your free text is
560
+ **deleted when the verdict is emitted**, in the same operation — only the
561
+ question, the quote that was verified and each area's tag survive; (2) if you
562
+ granted content consent, the copy at **Datadog LLM Observability** (a third
563
+ party) runs its full retention and **cannot be deleted** — the first deletion
564
+ does not reach it. And the asymmetry that matters: consent is read fresh on
565
+ every call, but if it is on for the **verdict** request, the **whole**
566
+ interview is captured, even if every earlier turn was sent without it.
567
+ - **The verdict can take minutes.** Grading runs three samples over the full
568
+ transcript, and it can outlast the CLI's own wait. When that happens the CLI
569
+ polls and collects the verdict rather than reporting a failure; if you give up
570
+ first, the session stays on disk and the next run picks the verdict up.
571
+
572
+ Like `certify skills`, this needs an endpoint: the four interview routes are
573
+ derived from `SHAKERS_CLI_INGEST_ENDPOINT`, the CLI's one backend (see
574
+ [Configuration](#configuration-environment-variables)).
575
+
576
+ ## Login (`login` / `logout`)
577
+
578
+ A registered Talent identifies themselves with `login` (issue 122 / ADR-044).
579
+ Since ADR-061 a session is required by **every** command except `ai-usage` and
580
+ `login` itself — `certify` (both `skills` and `agents`) also proves control of
581
+ the **account**, not just of an email address (ADR-050). `login` and `logout`
582
+ run without a session (that is how you obtain/drop one); `logout` with no
583
+ session is a silent no-op.
584
+
585
+ ```bash
586
+ shakers login # no method flag: interactive picker (Email / Google) on a real TTY
587
+ shakers login --email you@shakers.com # skip the picker, prompt only for the password
588
+ shakers login --google # skip the picker, go straight to the Google flow
589
+ shakers logout # forget the stored session (no-op if none)
590
+ ```
591
+
592
+ - **No `--password` flag, ever.** `argv` is visible to every other process on
593
+ the machine (`ps`) and lands in shell history, so the password is only ever
594
+ collected via a masked interactive prompt or a stdin pipe — never a flag.
595
+ - **Email/password** sends `{ email, password }` to the certifications
596
+ service's `POST /api/v1/auth/login/email`, which proxies the credentials to
597
+ the Shakers Hub **server-side** and runs its token-exchange, returning a
598
+ certifications-service JWT the CLI stores locally. The password transits
599
+ CLI → certifications service → Hub, over TLS, and is never persisted by the
600
+ certifications service.
601
+ - **Google** is an **OAuth 2.0 Device Authorization Grant** (RFC 8628): the CLI
602
+ asks the Hub for a short code, shows you a URL and a `XXXX-XXXX` code to enter
603
+ in your browser, and polls until you authorize. Nothing about Google lives on
604
+ your machine — no client id, no loopback server, no secret; the Hub owns all
605
+ Google config and does the token exchange server-side. After you authorize, the
606
+ Hub session is exchanged for the same API JWT the email flow stores.
607
+ - **LinkedIn is parked** (ADR-041/046) — not implemented.
608
+ - The session (`{ accessToken, expiresAt, email }`) is stored **atomically**,
609
+ permissions `600`, in `~/.config/shakers/auth-session.json`. **Logged in**
610
+ means "an unexpired token is present" — an unparseable or missing
611
+ `expiresAt` is treated as expired, never as active. `login` while a session
612
+ is already active tells you so instead of silently overwriting it; run
613
+ `logout` first to switch accounts.
614
+
615
+ ## Superadmin (non-production only)
616
+
617
+ `superadmin` is a password-gated, **non-production** test tool for the team
618
+ running this CLI against a service instance — not something a Talent uses.
619
+ The server 404s every superadmin route in production regardless of the
620
+ password.
621
+
622
+ ```bash
623
+ shakers superadmin # open a session (prompts password + your own email)
624
+ shakers superadmin --email you@shakers.com # same, prompts only the password
625
+ shakers superadmin --inspect --email talent@example.com # read-only certification receipt (ADR-025)
626
+ shakers superadmin --logout # forget the locally stored session
627
+ ```
628
+
629
+ - **Also no `--password` flag**, for the same reason as `login` — the
630
+ password is only ever read from an interactive prompt or a stdin pipe.
631
+ - Opening a session validates the password server-side and persists the
632
+ returned token locally (`config.json`'s `superadminSession`, ADR-027).
633
+ `certify` then accepts that session **instead of** a `login` session,
634
+ bypassing the login/identity gates on **any** email/repo, and its results
635
+ are stamped `test_origin: true`.
636
+ - **A known, documented exception (ADR-050):** unlike `certify agents`
637
+ (hardened to reject an unregistered email), the skill-code certification's
638
+ superadmin branch still tolerates `userId: null` and lets a superadmin
639
+ certify code against an unregistered email. This asymmetry is deliberate
640
+ and left as-is — it's non-production, encrypted, and yields no durable
641
+ credential — not a bug to fix to parity.
642
+ - `--inspect` is read-only: it prints the stored authorship + rubric evidence
643
+ for an email's certifications (an attribution trail, not cryptographic
644
+ proof).
645
+
646
+ ## The report
647
+
648
+ - **Terminal output** (from `ai-usage`/`certify`) and a **self-contained HTML
649
+ dashboard** (opened by `report`; Shakers branding, responsive, zero network
650
+ calls — no CDN, no external script, no fetch). The terminal is the concise
651
+ view; the HTML carries the full detail.
652
+ - **`ai-usage`/`certify` persist their result silently** into
653
+ `report-state.json` and print **no** link (ADR-016). The HTML is produced and
654
+ opened only when you run **`report`** — which regenerates the per-project HTML
655
+ from state, opens it in your browser (use `--no-open` to just print the link),
656
+ and prints the clickable `file://` link.
657
+ - **Localized to your OS locale**: a locale starting with `es` shows
658
+ Spanish; anything else shows English (`src/locale.js` / `src/i18n.js`).
659
+ `--lang` overrides this for a single run, including the copyable
660
+ implementation prompt.
661
+ - **Terminal sections (ADR-016), in order**: the "why this tier" analysis
662
+ FIRST, then the score meter, detected tools, technologies, and a
663
+ one-line-per-agent summary (name + model + definition-quality score + local
664
+ usage, with `↓` nesting). No environment section; the roadmap / next-steps is
665
+ behind `--roadmap`.
666
+ - **HTML sections**: everything above at full length, plus the environment,
667
+ MCP servers, the agent org chart with **per-agent detail** (score, rationale,
668
+ usage), and the current→next roadmap with its implementation prompt.
669
+ - **The HTML report is cumulative and scoped per project.** Each scanned
670
+ project has its OWN report file (`report-<hash>.html` in
671
+ `~/.config/shakers/`), keyed by the project's absolute path, and is
672
+ regenerated whole from `report-state.json` each time you run `report`. It
673
+ fills in over time: the usage section appears once you've run `ai-usage`
674
+ in that project, and the certification section once you've run `certify`
675
+ there; both appear together when both have run for the **same** project. This is
676
+ intentional (skill-code-certification, reporting redesign) — the report is a
677
+ persistent per-project artifact, not a per-invocation transcript. So running
678
+ `certify` in a project where you previously ran `ai-usage` correctly
679
+ still shows the usage section: that usage was produced for this
680
+ project and is part of its cumulative record, not stale or leaked data.
681
+ Different projects never mix into one document.
682
+ - **Score scope (ADR-010, reverts ADR-009): the 0-100 score is computed over
683
+ project ∪ home** — your whole developer AI setup (the project's signals plus
684
+ your global `~/.claude` and machine-level config), not this one project in
685
+ isolation. It measures the maturity of _you as a developer_, so projects that
686
+ share a home tend to read similar scores. ADR-009 had briefly scoped the score
687
+ to the project directory only, but that drove notes too low (project-level AI
688
+ config is sparse), so ADR-010 reverted it to the earlier project ∪ home
689
+ behaviour. The **tier (T0-T7)** uses the same project ∪ home scope.
690
+
691
+ ## What it detects
692
+
693
+ All detection is **deterministic** (no LLM) and reads only known AI-tool
694
+ configuration — never your project's business logic or source code:
695
+
696
+ - **Tools**: Claude Code, Cursor, GitHub Copilot, Windsurf, Aider,
697
+ Continue, Cline, Gemini CLI, Codex CLI, Amazon Q Developer, Cody, Zed,
698
+ Tabnine — existence of config files/directories, binaries on `PATH`,
699
+ installed editor extensions.
700
+ - **Depth per tool**: project instructions/rules, MCP servers configured,
701
+ own skills/commands, hooks.
702
+ - **MCP servers**: names and a heuristic category (data/comms/dev/browser/
703
+ other) from known config locations (`.mcp.json`, `.cursor/mcp.json`,
704
+ Windsurf/Gemini config, etc.) — only the top-level server-name keys are
705
+ read, never the values (which can carry commands, URLs or env vars).
706
+ - **Memory structure**: `@file` import count and nesting depth, section
707
+ count and byte size of context files (`CLAUDE.md`, `AGENTS.md`,
708
+ `GEMINI.md`) — structure only, never the file's actual text is stored.
709
+ - **Automations**: npm/shell scripts that invoke a known AI CLI, JSON-piping
710
+ patterns, and scheduled tasks (cron/launchd/pm2/systemd) — counts and
711
+ booleans only.
712
+ - **Browser tools**: Playwright/Puppeteer as a project dependency, or a
713
+ browser-category MCP server.
714
+ - **Agents**: your `.claude/agents/*.md` org chart (name, wired tools,
715
+ model, hierarchy) — deterministic; an optional server-side LLM step can
716
+ synthesize a short description per agent for nicer cards (see below).
717
+ - **Technologies**: frameworks/libraries actually used in the project
718
+ (React, Next.js, Express, Django...), parsed from dependency manifests
719
+ (`package.json`, `requirements.txt`, `go.mod`, etc.) and filtered through
720
+ a curated name map — never a raw dependency dump.
721
+ - **Hooks**: hook-based automation configured for a tool.
722
+
723
+ **Never** read, stored or sent: file _contents_ beyond what's needed to
724
+ compute a count, absolute paths, environment variables, or credentials.
725
+
726
+ ## Tier ladder (T0-T7)
727
+
728
+ The tier is "the highest tier whose criteria you ALL meet, checked strictly
729
+ bottom-up". The 0-4 band used elsewhere (e.g. Shakers direction views) is
730
+ derived from the tier — the tier is the single source of truth.
731
+
732
+ | Tier | Name | Criterion |
733
+ | ---- | --------------------- | -------------------------------------------- |
734
+ | T0 | Empty bench | no tool detected |
735
+ | T1 | First tool | at least one tool detected |
736
+ | T2 | Bench with notes | T1 + project instructions/rules/config exist |
737
+ | T3 | Connected bench | T2 + at least one MCP server configured |
738
+ | T4 | Own tooling | T3 + own skills/commands/rules |
739
+ | T5 | Agentic operator | agentic CLI + MCP + own tooling together |
740
+ | T6 | Multi-agent | T5 + 2 or more agents defined |
741
+ | T7 | Orchestrated workshop | T6 + at least one hook configured |
742
+
743
+ File recency (`mtime`) is informative only — it is never a gating signal
744
+ for any tier.
745
+
746
+ ## Optional server-side LLM layers
747
+
748
+ Three capabilities are ephemeral, optional, server-side LLM calls — all run
749
+ independently of the save/consent decision (they never touch the
750
+ persistence payload) and all degrade gracefully (deterministic fallback, or
751
+ simply no scores shown) if the endpoint is unset or the call fails for any
752
+ other reason. Since ADR-042 there is a single backend to answer any of
753
+ them — no second hop to retry against.
754
+
755
+ "Ephemeral" here is about the SHAKERS-side persistence payload only — it does
756
+ **not** mean the call's content is never stored anywhere. See
757
+ [Privacy & consent model](#privacy--consent-model) for what a `granted`
758
+ consent additionally authorizes a third-party observability provider to
759
+ capture.
760
+
761
+ - **Agent-card synthesis** (`SHAKERS_CLI_SYNTHESIS_ENDPOINT`): sends your
762
+ agents' description text to synthesize a short symbolic name + "what it
763
+ does" per agent, for nicer cards. Falls back to the deterministic org
764
+ chart if unavailable — and, per agent, to your own raw `.claude/agents/`
765
+ description (cleaned up and excerpted for card display) or a minimal
766
+ name-derived line if that's missing too, so a card's description is
767
+ never blank.
768
+ - **Roadmap personalization** (`SHAKERS_CLI_ROADMAP_ENDPOINT`): asks the
769
+ certifications service to rewrite the current tier jump's prose (what it unlocks, steps,
770
+ tips, mistakes) adapted to your detected stack. The tier, the band, and
771
+ the "when to upgrade" criterion are **never** touched by this call — only
772
+ the curated roadmap's fallback prose can ever change. Only derived
773
+ signals are sent (frameworks, tool/MCP categories, tier), never raw file
774
+ content or agent descriptions.
775
+ - **Agent definition-quality evaluation** (`agent-evaluation`, derived as a
776
+ sibling of your ingest endpoint; overridable with
777
+ `SHAKERS_CLI_AGENT_EVAL_ENDPOINT`): sends the scrubbed agent definitions and
778
+ gets back a 0-100 score + short rationale per agent (**gemini-2.5-flash**
779
+ server-side, prompt version `agent-eval-v1`). The definition is treated as
780
+ DATA server-side (prompt-injection safe). Degradation is by omission — the
781
+ server drops any agent it couldn't score, and agents with no returned score
782
+ render without a number. No endpoint / failure → no scores shown.
783
+
784
+ All three send only derived signals or scrubbed agent definitions — never raw
785
+ file content — and a scrub pass runs before anything leaves the machine.
786
+
787
+ Separately, `ai-usage` reads your **local Claude Code history**
788
+ (`~/.claude/projects/**`) to count how often each detected agent was actually
789
+ invoked on this machine. This is **usage only**: it extracts a single signal per
790
+ line — the `subagent_type` of an `Agent` tool call — and never reads, stores, or
791
+ transmits prompt text, tool inputs, or any session content. The counts stay
792
+ strictly local (not in the persistence payload). It degrades gracefully if the
793
+ history directory is absent or unreadable.
794
+
795
+ ## Privacy & consent model
796
+
797
+ - **The CLI talks to ONE Shakers backend.** Every call this tool makes — the
798
+ usage send, the optional LLM layers above, `login`, and everything under
799
+ `certify` (resolve, certify, email verification, agent certification,
800
+ skill interview) — goes to a single configured endpoint,
801
+ `sh-web-works-certifications`. Until 2026-08-04 (ADR-021) there was a
802
+ second hop that fell back to `shakers-hub-backend`; ADR-042 retired it once
803
+ it was confirmed that hop never had the routes mounted (it only ever
804
+ answered 404, which had already masked at least one real timeout in a
805
+ diagnosis). There is no fallback any more: an unset or unreachable endpoint
806
+ is a real, named error (timeout / connection refused / 5xx), not a silent
807
+ hop to somewhere else. Nothing is deployed at a real address yet — see
808
+ [Configuration](#configuration-environment-variables) — and `install.sh`
809
+ writes a loopback placeholder by default, so a fresh, unconfigured install
810
+ talks to nothing on the Talent's own machine.
811
+ - **The report is always generated and shown locally, unconditionally.**
812
+ There is no gate, no wall, no preview step before you see your own data.
813
+ - **Saving (persisting) it to Shakers is opt-in, asked once.** The first
814
+ time you run the tool, right **after** the report is already on screen,
815
+ you're asked a short yes/no question: do you want this report saved in
816
+ Shakers? Accepting asks for an email; declining persists that choice and
817
+ you're never asked again. Manage the decision any time with
818
+ `--consent-status` / `--consent-revoke` / `--consent-email`, without
819
+ re-running the scan.
820
+ - **This one yes/no now governs TWO different things (talents-ai-score,
821
+ ADR-028), kept explicit rather than blurred into one vague permission —
822
+ there is no separate flag for each yet, it's a single consent:**
823
+ 1. **Persisting in Shakers.** Only derived signals are ever sent for
824
+ saving — booleans, counts, categories, tier/band, the detected
825
+ technology names, and (if it ran) the synthesized agent summaries.
826
+ **Never** file contents, absolute paths, environment variables, or
827
+ credentials. The email you typed travels outside this whitelisted
828
+ payload, in the request body.
829
+ 2. **Capturing content in a third-party observability provider.**
830
+ `ai-usage`/`certify`'s optional server-side AI calls — agent-card
831
+ synthesis, roadmap personalization, agent definition-quality
832
+ evaluation, CERTIFY's skill-code
833
+ assessment, and every call of the agent-certification **interview**
834
+ (each turn, plus the verdict) — send a
835
+ `traceContentConsent` flag reflecting your **current**, per-request
836
+ decision. When (and only when) it's `true`, **Datadog LLM
837
+ Observability**, a company **other than Shakers**, may capture and
838
+ retain the prompt/output of that call — which, for CERTIFY and agent
839
+ certification, includes your **actual sampled source code** and your
840
+ **free-text answers**, not just derived signals. Retention is up to
841
+ **three months**, configured on Datadog's side. **There is no promise
842
+ of later, per-item deletion of that content** — unlike the structured
843
+ payload above (governed by `--consent-revoke`/purge), Datadog's
844
+ retention is the only expiry that exists for it. If you decline (or
845
+ never decide), nothing is sent there — this is the safe default; the
846
+ flag is only ever omitted or `true`, never inferred.
847
+ - **The optional LLM layers above still run REGARDLESS of your save
848
+ decision** — that's what makes the diagram, the roadmap and the agent
849
+ scores "always show". What changes with consent is ONLY whether Datadog
850
+ may capture what they send, per point 2 above; the call itself, and
851
+ whether it's silently dropped or degraded on failure, is unaffected by
852
+ consent either way. The local **usage** read (Claude Code history) is not
853
+ even a network call — it never leaves the machine, consent or not.
854
+ - The consent decision lives in `~/.config/shakers/consent.json`
855
+ (permissions `600`).
856
+ - An **unfinished interview** (agent certification, or the skill-code
857
+ interview under `certify skills`) lives in
858
+ `~/.config/shakers/interview-session.json` (permissions `600`, written
859
+ atomically — the two kinds share one file, discriminated by a `kind`
860
+ field). It holds a bearer token for that interview and the answer
861
+ currently in flight — nothing else of what you typed — and it is deleted as
862
+ soon as the verdict arrives or the session expires. Delete it by hand at any
863
+ time: the only consequence is losing the ability to resume that interview
864
+ (the attempt it spent is already counted server-side either way).
865
+ - A **login session** lives in `~/.config/shakers/auth-session.json`
866
+ (permissions `600`, written atomically): the bearer token that identifies
867
+ you to `certify`, plus its expiry and (when the server sent one) your
868
+ email. See [Login](#login-login--logout).
869
+ - **Legal**: sending data about how a person works involves personal-data
870
+ processing (GDPR). This model has legal/labor sign-off reported for the
871
+ current design (see `active-work/talents-ai-score/decisions.md`,
872
+ ADR-011/013). **Activating sending against real Shakers talents in
873
+ production is a separate deployment decision that still requires its own
874
+ legal/labor go-ahead** for that rollout — this repo doesn't ship a
875
+ production endpoint by default (see below).
876
+ - **Legal copy across FOUR separate disclosure texts** (reviewed — the
877
+ `[PENDING LEGAL/LABOR REVIEW — NOT FINAL]` markers were removed), each shown
878
+ before a different kind of egress: the save-to-Shakers consent intro
879
+ (`ai-usage` / `certify skills`), the `certify skills` code-egress disclaimer,
880
+ the `certify agents` interview disclaimer, and the `certify skills` interview
881
+ disclaimer. All four state that **you are solely responsible for owning/being
882
+ authorized to analyze the code you submit, that Shakers assumes no liability
883
+ for it, and that misuse may lead to penalties on your Shakers account (up to
884
+ suspension)**. The skill-interview disclosure additionally names, per
885
+ ADR-049, that the interview asks questions **about the code you just
886
+ sampled**. And per ADR-051, that disclosure's own yes/no is a **real consent
887
+ gate**, not decorative text: declining it skips the interview outright (your
888
+ code-only Skill certification stands either way) — only the earlier "do you
889
+ want to try the interview?" opt-in was removed, not this consent.
890
+
891
+ ## Configuration (environment variables)
892
+
893
+ The backend endpoints ARE baked in, per flavor, in `ENV_PROFILES`
894
+ (`src/config.js`): `staging`, `dev` and `local`. The default flavor is derived
895
+ from the package `name` (`shakers-cli-beta` → staging, `@shakers/shakers-cli` →
896
+ dev), forceable with `SHAKERS_CLI_ENV=staging|dev|local` — see
897
+ [Publishing](#publishing-maintainers) above. No secret is ever compiled in
898
+ (ADR-002/007). The baked profile is only a DEFAULT: the per-endpoint
899
+ `SHAKERS_CLI_*_BASE` env vars and `config.json` always win over it (env >
900
+ `config.json` > baked profile), and the profile bases reach the getters by
901
+ seeding a fresh `config.json` on first run. Every code path that depends on an
902
+ endpoint either degrades gracefully (no send / deterministic fallback) or — for
903
+ `certify`, which has no local-only product — surfaces an actionable error;
904
+ neither ever breaks the local `ai-usage` report.
905
+
906
+ **Since ADR-042 the CLI talks to ONE backend**, `sh-web-works-certifications`.
907
+ The two-hop chain this section used to describe (a `shakers-hub-backend`
908
+ fallback) is retired: it never had the routes mounted on `develop`, so every
909
+ "fallback" was an instant 404 that once masked a real timeout in a
910
+ diagnosis. Only ONE variable needs setting; everything else this CLI calls is
911
+ **derived from it** as a sibling route (`deriveFromIngest` in `src/config.js`)
912
+ — the base path's last segment (e.g. `.../usage/reports`) is swapped for the
913
+ sibling's (e.g. `.../usage/skill-certification/sessions`).
914
+
915
+ | Variable | Purpose |
916
+ | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
917
+ | `SHAKERS_CLI_INGEST_ENDPOINT` | **The one endpoint to configure.** Where a saved `usage` report is sent (if consent is granted). Every other endpoint below derives from it unless it has its own override. |
918
+ | `SHAKERS_CLI_CERTIFY_ENDPOINT` | Override for the `certify` skill-certification route. Unset derives from ingest. Unlike `usage`'s endpoints, a missing one here is an **actionable error**, never a silent no-op — `certify` has no local-only product. |
919
+ | `SHAKERS_CLI_LOGIN_ENDPOINT` | Override for `login`'s email/password route. Unset derives from ingest (climbing out of the `usage` module: `.../api/v1/auth/login/email`). |
920
+ | `SHAKERS_CLI_DEVICE_AUTHORIZE_ENDPOINT`| Override for Google device-flow authorize (`POST .../auth/device/code`). Unset derives from the hub base. |
921
+ | `SHAKERS_CLI_DEVICE_TOKEN_ENDPOINT` | Override for Google device-flow token polling (`POST .../works/auth/device/token`). Unset derives from the hub base. |
922
+ | `SHAKERS_CLI_SYNTHESIS_ENDPOINT` | Override for agent-card synthesis (optional feature). Unset derives from ingest. |
923
+ | `SHAKERS_CLI_ROADMAP_ENDPOINT` | Override for roadmap personalization (optional feature). Unset derives from ingest. |
924
+ | `SHAKERS_CLI_AGENT_EVAL_ENDPOINT` | Override for agent definition-quality evaluation (optional feature). Unset derives from ingest. |
925
+ | `SHAKERS_CLI_CONFIG_DIR` | Override `~/.config/shakers/` (mainly for tests). |
926
+
927
+ > **Env prefix (ADR-045):** the CLI's variables use the `SHAKERS_CLI_*` prefix
928
+ > (distinct from the certifications backend's own `SHAKERS_*`). The pre-rename
929
+ > `AI_FOOTPRINT_*` names are still honoured as a fallback — if the new name is
930
+ > unset and the old one is set, the old one is used — so nothing breaks for
931
+ > anyone who exported the old variables.
932
+
933
+ Every other endpoint this CLI calls (email verification request/verify,
934
+ agent-certification interview sessions, skill-certification interview
935
+ sessions, and the two superadmin routes) has **no** env-var override at all —
936
+ they are always derived from ingest; see `src/config.js` for the full list of
937
+ `get*Endpoint` getters. The agent-certification and skill-interview session
938
+ bases are derived exactly like the others, but their **per-session** URLs
939
+ (`/:id`, `/:id/turns`, `/:id/verdict`) are built by string concatenation in
940
+ the client, never re-derived (`deriveFromIngest` replaces the base's last
941
+ path segment, and that base already ends in `sessions`).
942
+
943
+ ### Config file (persistent ingest endpoint)
944
+
945
+ So you don't have to `export SHAKERS_CLI_INGEST_ENDPOINT` every session, it
946
+ can also live in a persistent file next to consent/reports:
947
+
948
+ ```
949
+ ~/.config/shakers/config.json
950
+ {
951
+ "ingestEndpoint": "https://your-certifications-service/api/v1/usage/reports"
952
+ }
953
+ ```
954
+
955
+ Set it without editing JSON by hand:
956
+
957
+ ```bash
958
+ shakers ai-usage --set-endpoint https://your-certifications-service/api/v1/usage/reports
959
+ shakers ai-usage --show-endpoint # prints the effective endpoint and its source
960
+ ```
961
+
962
+ **Resolution precedence:** `SHAKERS_CLI_INGEST_ENDPOINT` (env var, the raw
963
+ developer override, legacy `AI_FOOTPRINT_INGEST_ENDPOINT` honoured too) **>**
964
+ `config.json` **>** none. There is still no compiled-in production default in
965
+ the code — only `install.sh` bakes a loopback placeholder into a FRESH config
966
+ file (see the prominent comment there; **this must change before real
967
+ distribution**). Every sibling-derived endpoint in the table above keeps
968
+ deriving from whichever value this resolves to.
969
+
970
+ **A config file written before ADR-042 may still carry the retired
971
+ `ingestEndpointFallback` key.** `install.sh` never overwrites an existing
972
+ `config.json`, so upgrading doesn't clean it up. The policy is deliberate:
973
+ the key is **ignored** (nothing reads it any more), **never migrated** (there
974
+ is nowhere to migrate a hub URL to — the one endpoint is already configured
975
+ separately) and **never erased** (this CLI never rewrites a Talent's config
976
+ file except behind an explicit `--set-*`, and a rewrite is the one operation
977
+ that could lose it). `--show-endpoint` is the one place that says the key is
978
+ present and unused, so nobody debugging their setup mistakes it for a live
979
+ hop.
980
+
981
+ **Endpoint safety (bounded):** because the endpoint decides where the sampled
982
+ code (`certify`) and derived signals (`ai-usage`) are sent, `--set-endpoint`
983
+ refuses to persist an `http://` URL to any host other than
984
+ `localhost`/`127.0.0.1`/`::1` — a non-local host must be `https`. There is no
985
+ domain allowlist yet (`SHAKERS_CLI_DOMAINS` in `src/config.js` is deliberately
986
+ empty — see that file's docblock for why an unverified entry would be worse
987
+ than none); a non-loopback, non-allowlisted host still requires you to type
988
+ the hostname back before `--set-endpoint` persists it. The env var is not
989
+ validated (it's the explicit developer escape hatch); a hand-edited config
990
+ file carrying an insecure remote endpoint is ignored at read time.
991
+
992
+ ## Local end-to-end testing
993
+
994
+ **Run the real backend and point the CLI at loopback.** There used to be a
995
+ `reference-server/` stub in this repo that you could run instead; it is
996
+ **retired** (talents-ai-score, issue 044) and it is not coming back. It still
997
+ answered the `agent-certification/followups` and `/verdict` routes after those
998
+ were retired from the service, and it never had a single **interview** route, so
999
+ anyone who ran it was testing against a contract that no longer exists. That is
1000
+ worse than having no stub: it fails silently and in your favour.
1001
+
1002
+ It was retired rather than repaired on purpose. Serving the agent-certification
1003
+ interview would mean reimplementing session state, the `seq` cursor, per-turn
1004
+ idempotency, the stop condition and evidence anchoring — a second implementation
1005
+ of the most delicate semantics in the system, which would lie to your face the
1006
+ moment it drifted from the real one. Running the real service is what the team
1007
+ does, and it is the only way to get answers you can trust.
1008
+
1009
+ This also settles a standing caveat: the old stub kept its reports in an
1010
+ in-memory map, so every mention of it had to warn that production demands a real
1011
+ database. That warning is **obsolete by retirement, not by oversight** — there is
1012
+ no in-repo server left that anyone could mistake for the real one. Persistence is
1013
+ the certifications service's business (Prisma/Postgres), and always was.
1014
+
1015
+ The backend is the Shakers certifications service
1016
+ (NestJS + Prisma + Postgres) — since ADR-042, the CLI's **only** backend.
1017
+ Its `usage` module mounts every route this CLI calls (plus `auth/login/*`)
1018
+ under the service's `api/v1` global prefix. Start it in its own checkout (it
1019
+ needs its own database and secrets — see that repo's README), then:
1020
+
1021
+ ```bash
1022
+ # the service listens on PORT, default 3000, with global prefix /api/v1
1023
+ export SHAKERS_CLI_INGEST_ENDPOINT=http://127.0.0.1:3000/api/v1/usage/reports
1024
+ shakers ai-usage
1025
+ ```
1026
+
1027
+ `http://` is accepted **only** for loopback (`localhost`, `127.0.0.1`, `::1`);
1028
+ every other host must be `https` (`src/config.js`). If you prefer not to export
1029
+ a variable each session, persist it instead — the env var wins over the file:
1030
+
1031
+ ```bash
1032
+ shakers ai-usage --set-endpoint http://127.0.0.1:3000/api/v1/usage/reports
1033
+ ```
1034
+
1035
+ Every other URL the CLI needs (`skill-certification`, `login`, the Google
1036
+ config/login pair, `email-verification/request` and `/verify`,
1037
+ `agent-synthesis`, `agent-evaluation`, `graph-inference`, and the
1038
+ `agent-certification/sessions…` / `skill-certification/sessions…` interview
1039
+ routes) is derived as a sibling of whatever `ingestEndpoint` resolves to, so
1040
+ that one variable is enough — `login` first, since `certify` refuses to run
1041
+ without a session (ADR-050):
1042
+
1043
+ ```bash
1044
+ shakers login --email you@shakers.com
1045
+ ```
1046
+
1047
+ Note the real OTP (for the non-login email-verification path, still used by
1048
+ `ai-usage`'s save-to-Shakers consent) is emailed via HubSpot with a Redis
1049
+ TTL/single-use code — there is no fixed local code any more, so read the one
1050
+ you were sent. There is no fallback hop to exercise any more (ADR-042): an
1051
+ unset or unreachable endpoint fails with the real cause (timeout / connection
1052
+ refused / 5xx), it does not retry anywhere else.
1053
+
1054
+ The CLI's own test suite does not need any of this: it wires an in-process fake
1055
+ (`test-fixtures/ingest-service-fake.js`) into a throwaway server on an ephemeral
1056
+ port. That fake is a **test double, not a runnable server** — it binds no port,
1057
+ has no launcher, is never installed, and only implements what the assertions in
1058
+ `test/` require.
1059
+
1060
+ ## How to add a new tool
1061
+
1062
+ Edit `src/detectors.js` and add an entry with its signals:
1063
+
1064
+ ```js
1065
+ {
1066
+ id: 'my-tool',
1067
+ name: 'My Tool',
1068
+ vendor: 'Vendor',
1069
+ category: CATEGORIES.AGENTIC_CLI,
1070
+ signals: [
1071
+ { type: 'projectPath', path: '.mytool' },
1072
+ { type: 'bin', name: 'mytool' },
1073
+ ],
1074
+ }
1075
+ ```
1076
+
1077
+ If you want to measure depth, add a probe in `src/scanner.js` inside
1078
+ `probes` that returns **only numbers**.
1079
+
1080
+ ## Structure
1081
+
1082
+ ```
1083
+ bin/shakers.js Argv command dispatcher + session gate — the single entrypoint (ADR-060/061)
1084
+ bin/ai-usage.js `ai-usage` command logic (run(args,{ask}))
1085
+ bin/certify.js `certify` command logic — skills/agents subcommands + picker (run(args,{ask}))
1086
+ bin/report-html.js `report` command — materialize + open the HTML (run(args,{ask}))
1087
+ bin/share.js `share` command logic — currently disabled (run(args,{ask}))
1088
+ bin/login.js `login`/`logout` command logic (run/runLogout(args,{ask}))
1089
+ bin/superadmin.js `superadmin` non-production test-tool command (run(args,{ask}))
1090
+
1091
+ ── ai-usage: detection & classification ──
1092
+ src/detectors.js Catalog of tools and signals
1093
+ src/scanner.js Scan engine -> report object
1094
+ src/scan-exclusions.js Paths/dirs excluded from the scan
1095
+ src/tier-engine.js T0-T7 ladder computation (single source of truth)
1096
+ src/tier-analysis.js "Why this tier" deterministic breakdown
1097
+ src/maturity.js 0-4 band + 0-100 score (derived from the tier)
1098
+ src/mcp-detector.js MCP server name/category detector
1099
+ src/memory-structure-detector.js Context-file import/structure detector
1100
+ src/automations-detector.js Scripts/scheduler automation detector
1101
+ src/browser-tools-detector.js Browser-automation tooling detector
1102
+ src/tech-detector.js Project technologies (frameworks) detector
1103
+ src/tech-extensions.js Technology name map / extensions
1104
+ src/agent-org-chart.js Deterministic agent org chart parser (+ hierarchy)
1105
+ src/agent-synthesis.js Optional LLM agent-card synthesis client
1106
+ src/agent-evaluation.js Optional LLM agent definition-quality client (gemini-2.5-flash, server-side)
1107
+ src/agent-usage.js Local Claude Code history reader (usage signal only, never content)
1108
+ src/roadmap-content.js Curated per-tier roadmap content (es/en)
1109
+ src/roadmap-prompt.js Ready-to-paste implementation prompt
1110
+ src/roadmap-personalization.js Optional LLM roadmap-prose personalization client
1111
+ src/build-next-level.js Secondary: writes deterministic starter files
1112
+
1113
+ ── certify: Skill + agent certification ──
1114
+ src/certify-args.js `certify` flag parsing (--skill, --accept-disclaimer, --fast…)
1115
+ src/certify-client.js Resolve/certify HTTP client (discriminated results)
1116
+ src/certify-disclaimer.js Legal disclaimer + explicit acceptance (egress gate, `certify skills`)
1117
+ src/certify-preconditions.js LOGIN precondition for `certify agents` (ADR-050)
1118
+ src/certify-render.js Resolve report renderer
1119
+ src/certify-remediation-prompt.js Copyable remediation prompt per Skill
1120
+ src/certify-agents.js `certify agents` interview flow
1121
+ src/certify-skill-interview.js `certify skills`' mandatory-start skill interview (ADR-049/051)
1122
+ src/interview-engine.js Shared turn-loop/resume/verdict engine (agents + skill interviews)
1123
+ src/agent-certification-client.js HTTP client for both interview kinds' session/turn/verdict routes
1124
+ src/skill-sampler.js Deterministic per-Skill code sampling (+ scrub)
1125
+ src/skill-selection.js --skill selection parsing (by name or position)
1126
+ src/authorship.js Git-authorship filtering/attribution of sampled code (ADR-017)
1127
+ src/interactive-select.js Interactive single-/multi-select picker
1128
+ src/interview-session-store.js Atomic 0600 session-in-flight store (shared by both interview kinds)
1129
+ src/legal-notice.js Shared framing for the four disclosure texts
1130
+ src/render-certification.js Certification report (terminal + HTML sections)
1131
+ src/render-skill-interview.js Skill-interview combined-level verdict renderer
1132
+
1133
+ ── login / superadmin ──
1134
+ src/auth-session-store.js Atomic 0600 login-session store (`login`)
1135
+ src/auth-client.js Email/password login HTTP client
1136
+ src/auth-device-client.js Google device-flow HTTP client (RFC 8628: authorize/token/api-token)
1137
+ src/device-login.js Google device-flow orchestration (blocking CLI loop + shared start/poll)
1138
+ src/device-auth-store.js Atomic 0600 pending-device-grant store (bridges the MCP start/poll tools)
1139
+
1140
+ ── share: LinkedIn card ──
1141
+ src/share-card.js Builds the self-contained branded SVG/HTML card
1142
+ src/shakers-wordmark.js Inlined "shakers" logotype (auto-generated from SVG)
1143
+
1144
+ ── consent, identity, persistence, reporting ──
1145
+ src/consent-flow.js Short, one-time "save to Shakers?" prompt
1146
+ src/consent-skip.js Explicit reasons the consent prompt is/isn't shown
1147
+ src/email-verification.js Email OTP verification client (gates persistence)
1148
+ src/share.js Consent state + derived-payload whitelist + sending
1149
+ src/report-store.js Per-project cumulative report state + HTML (persistence)
1150
+ src/report-theme.js Shared Shakers HTML theme (tokens/base/copy script)
1151
+ src/store.js Legacy latest/history store — UNUSED (kept on disk)
1152
+ src/render-terminal.js Terminal output (why-first, one-line-per-agent; ADR-016)
1153
+ src/render-html.js Self-contained HTML dashboard (full detail, per-agent scores)
1154
+ src/osc-link.js OSC 8 clickable terminal links
1155
+ src/open-file.js Best-effort cross-platform "open in browser" (report/login-browser)
1156
+
1157
+ ── graph-inference plumbing (retained; the `map` command was removed) ──
1158
+ src/repo-context.js Content-free structural repo summary (entrypoints, AI call-sites…)
1159
+ src/graph-infer-client.js Optional LLM graph-inference client (gemini-2.5-pro, server-side)
1160
+ src/graph-scan.js Deterministic agent-subgraph + footprint drawer (feeds the sheet)
1161
+ src/graph-generator.js Deterministic graph generation
1162
+
1163
+ ── shared plumbing ──
1164
+ src/cli-args.js `ai-usage` flag parsing
1165
+ src/config.js Endpoint configuration from env vars, never hardcoded — one backend (ADR-042)
1166
+ src/config-dir.js Single source for the config dir + legacy `~/.config/ai-footprint` migration (ADR-045)
1167
+ src/env-paths.js Home-directory + `SHAKERS_CLI_*`/`AI_FOOTPRINT_*` env resolution (test-overridable)
1168
+ src/stdin-ask.js Injectable stdin question/answer helper
1169
+ src/terminal-progress.js Status line + spinner for slow calls
1170
+ src/locale.js OS locale detection
1171
+ src/i18n.js Text catalogs (es/en) — report, CLI, certify, login, superadmin
1172
+
1173
+ test-fixtures/ingest-service-fake.js In-process backend FAKE for tests only — not shipped, binds no port
1174
+ install.sh Installer (curl | bash, or local)
1175
+ ```