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
@@ -0,0 +1,92 @@
1
+ 'use strict';
2
+
3
+ // Availability client. VIEW = GET `works/me/availability`; SET = PUT
4
+ // `works/talents/me/work-details/availability` (the same endpoint register uses).
5
+
6
+ const { postJsonWithTimeout } = require('./backend-request');
7
+ const { reasonForError } = require('./onboarding-client');
8
+
9
+ const DEFAULT_TIMEOUT_MS = 20000;
10
+
11
+ function parseJson(raw) {
12
+ try {
13
+ return raw ? JSON.parse(raw) : null;
14
+ } catch {
15
+ return null;
16
+ }
17
+ }
18
+
19
+ function bearerHeaders(accessToken) {
20
+ const headers = {};
21
+ if (typeof accessToken === 'string' && accessToken) headers.Authorization = `Bearer ${accessToken}`;
22
+ return headers;
23
+ }
24
+
25
+ function bool(v) {
26
+ return v === true ? true : v === false ? false : null;
27
+ }
28
+
29
+ function str(v) {
30
+ return typeof v === 'string' && v ? v : null;
31
+ }
32
+
33
+ function normalizeAvailability(data) {
34
+ const it = data && typeof data === 'object' ? data : {};
35
+ return {
36
+ available: bool(it.available),
37
+ // A numeric hours value (incl. 0) must survive, not be dropped as "absent".
38
+ monthlyHours: typeof it.monthlyHours === 'number' ? String(it.monthlyHours) : str(it.monthlyHours),
39
+ onlyRemote: bool(it.onlyRemote),
40
+ country: str(it.country),
41
+ subdivision: str(it.subdivision),
42
+ timezone: str(it.timezone),
43
+ city: typeof it.city === 'number' ? it.city : null,
44
+ longFullTimeProjects: bool(it.longFullTimeProjects),
45
+ availabilityExpiringDate: it.availabilityExpiringDate ? String(it.availabilityExpiringDate) : null,
46
+ };
47
+ }
48
+
49
+ async function requestAvailability({ hubAccessToken } = {}, { endpoint, timeoutMs = DEFAULT_TIMEOUT_MS } = {}) {
50
+ if (!endpoint) return { ok: false, reason: 'no-endpoint' };
51
+ if (!hubAccessToken) return { ok: false, reason: 'no-hub-token' };
52
+ let res;
53
+ try {
54
+ res = await postJsonWithTimeout(endpoint, null, timeoutMs, 'GET', null, bearerHeaders(hubAccessToken));
55
+ } catch (e) {
56
+ return { ok: false, reason: (e && e.kind) || 'network-error' };
57
+ }
58
+ if (res.status === 404) return { ok: false, reason: 'not-found' };
59
+ if (res.status < 200 || res.status >= 300) return { ok: false, reason: reasonForError(res.status, res.raw) };
60
+ const env = parseJson(res.raw);
61
+ const data = env && env.status === 'OK' ? env.data : env;
62
+ if (!data || typeof data !== 'object') return { ok: false, reason: 'bad-response' };
63
+ return { ok: true, availability: normalizeAvailability(data) };
64
+ }
65
+
66
+ async function fetchAvailability(deps = {}, { hubAccessToken, timeoutMs = DEFAULT_TIMEOUT_MS } = {}) {
67
+ const {
68
+ getMeAvailabilityEndpoint = require('./config').getMeAvailabilityEndpoint,
69
+ requestAvailability: request = requestAvailability,
70
+ } = deps;
71
+ const endpoint = getMeAvailabilityEndpoint();
72
+ if (!endpoint) return { ok: false, reason: 'no-endpoint' };
73
+ return request({ hubAccessToken }, { endpoint, timeoutMs });
74
+ }
75
+
76
+ // Partial upsert: only the changed fields ride the PUT (all fields optional).
77
+ async function saveAvailability(deps = {}, { hubAccessToken, availability, timeoutMs = DEFAULT_TIMEOUT_MS } = {}) {
78
+ const {
79
+ getSetAvailabilityEndpoint = require('./config').getSetAvailabilityEndpoint,
80
+ requestSetAvailability = require('./onboarding-client').requestSetAvailability,
81
+ } = deps;
82
+ const endpoint = getSetAvailabilityEndpoint();
83
+ if (!endpoint) return { ok: false, reason: 'no-endpoint' };
84
+ return requestSetAvailability({ availability, hubAccessToken }, { endpoint, timeoutMs });
85
+ }
86
+
87
+ module.exports = {
88
+ normalizeAvailability,
89
+ requestAvailability,
90
+ fetchAvailability,
91
+ saveAvailability,
92
+ };
@@ -0,0 +1,105 @@
1
+ 'use strict';
2
+
3
+ // `availability` flow: view (hours/month, open-to-work, location), and `--set`
4
+ // (interactive, explicit confirmation — it writes your profile). Talent Bearer.
5
+
6
+ const { styleLabelPrefix } = require('./ansi');
7
+
8
+ function makeAvailabilityDeps(overrides = {}) {
9
+ const client = require('./availability-client');
10
+ return {
11
+ fetchAvailability: (opts) => client.fetchAvailability({}, opts),
12
+ saveAvailability: (opts) => client.saveAvailability({}, opts),
13
+ promptSelect: (opts) => require('./prompt-select').promptSelect(opts),
14
+ ...overrides,
15
+ };
16
+ }
17
+
18
+ function locationLine(a) {
19
+ return [a.country, a.subdivision, a.timezone].filter(Boolean).join(' · ') || null;
20
+ }
21
+
22
+ function renderView(io, fp, a) {
23
+ io.section(fp.title);
24
+ io.notify(styleLabelPrefix(fp.openToWork(a.available === true ? fp.yes : a.available === false ? fp.no : fp.unknown)));
25
+ io.notify(styleLabelPrefix(fp.monthlyHours(a.monthlyHours != null ? a.monthlyHours : fp.unknown)));
26
+ io.notify(styleLabelPrefix(fp.onlyRemote(a.onlyRemote === true ? fp.yes : a.onlyRemote === false ? fp.no : fp.unknown)));
27
+ const loc = locationLine(a);
28
+ if (loc) io.notify(styleLabelPrefix(fp.location(loc)));
29
+ if (a.availabilityExpiringDate) io.notify(styleLabelPrefix(fp.expires(a.availabilityExpiringDate)));
30
+ io.success(fp.done);
31
+ }
32
+
33
+ async function runSet({ io, ask, session, catalog, opts, deps }) {
34
+ const fp = catalog.availability;
35
+ const hubAccessToken = session ? session.hubAccessToken : null;
36
+ if (!opts.stdinIsTTY) {
37
+ io.error(fp.setNeedsInteractive);
38
+ return { ok: false, reason: 'non-interactive' };
39
+ }
40
+ const selOut = (s) => io.notify(s);
41
+ const yesNo = [{ label: fp.yes, value: true }, { label: fp.no, value: false }];
42
+ const pickYesNo = (header) => deps.promptSelect({ ask, stdinIsTTY: opts.stdinIsTTY, out: selOut, header, items: yesNo, labelFor: (x) => x.label, input: opts.input, output: opts.output });
43
+
44
+ const read = await io.withProgress(fp.loading, () => deps.fetchAvailability({ hubAccessToken }));
45
+ const current = read.ok ? read.availability : null;
46
+ if (!read.ok && read.reason !== 'not-found') {
47
+ io.error(fp.fetchFailed(read.reason));
48
+ return { ok: false, reason: read.reason };
49
+ }
50
+
51
+ io.section(fp.setTitle);
52
+ if (current) renderView({ section: () => {}, notify: io.notify, success: () => {} }, fp, current);
53
+
54
+ // Arrow-key yes/no pickers for the discrete fields.
55
+ const openPick = await pickYesNo(fp.askOpenToWork);
56
+ if (!openPick) { io.notify(fp.setCancelled); return { ok: true, cancelled: true }; }
57
+ const available = openPick.value;
58
+ const hoursRaw = (await io.ask(fp.askMonthlyHours)).trim();
59
+ if (hoursRaw && !/^\d{1,3}$/.test(hoursRaw)) { io.error(fp.monthlyHoursInvalid); return { ok: false, reason: 'bad-hours' }; }
60
+ const remotePick = await pickYesNo(fp.askOnlyRemote);
61
+ if (!remotePick) { io.notify(fp.setCancelled); return { ok: true, cancelled: true }; }
62
+ const onlyRemote = remotePick.value;
63
+
64
+ // Only the asked fields ride the partial upsert.
65
+ const body = { available, onlyRemote };
66
+ if (hoursRaw) body.monthlyHours = hoursRaw;
67
+
68
+ io.notify(fp.setDiff(
69
+ `${available ? fp.yes : fp.no}`,
70
+ hoursRaw || (current && current.monthlyHours) || fp.unknown,
71
+ `${onlyRemote ? fp.yes : fp.no}`,
72
+ ));
73
+ const confirmed = await io.confirm(fp.setConfirm);
74
+ if (!confirmed) { io.notify(fp.setCancelled); return { ok: true, cancelled: true }; }
75
+
76
+ const res = await io.withProgress(fp.setSaving, () => deps.saveAvailability({ hubAccessToken, availability: body }));
77
+ if (!res.ok) { io.error(fp.setFailed(res.reason)); return { ok: false, reason: res.reason }; }
78
+ io.success(fp.setDone);
79
+ return { ok: true, set: body };
80
+ }
81
+
82
+ async function runAvailability({ io, rawOut, ask, session, catalog, opts = {}, deps = makeAvailabilityDeps() }) {
83
+ const fp = catalog.availability;
84
+ if (opts.set) return runSet({ io, ask, session, catalog, opts, deps });
85
+
86
+ const hubAccessToken = session ? session.hubAccessToken : null;
87
+ const doFetch = () => deps.fetchAvailability({ hubAccessToken });
88
+ const res = opts.json ? await doFetch() : await io.withProgress(fp.loading, doFetch);
89
+ if (!res.ok) {
90
+ io.error(res.reason === 'not-found' ? fp.notFound : fp.fetchFailed(res.reason));
91
+ return { ok: false, reason: res.reason };
92
+ }
93
+ if (opts.json) {
94
+ (rawOut || ((s) => process.stdout.write(s)))(`${JSON.stringify({ ok: true, availability: res.availability }, null, 2)}\n`);
95
+ return { ok: true, availability: res.availability };
96
+ }
97
+ renderView(io, fp, res.availability);
98
+ return { ok: true, availability: res.availability };
99
+ }
100
+
101
+ module.exports = {
102
+ makeAvailabilityDeps,
103
+ runAvailability,
104
+ locationLine,
105
+ };
@@ -0,0 +1,170 @@
1
+ 'use strict';
2
+
3
+ const http = require('http');
4
+ const https = require('https');
5
+ const crypto = require('crypto');
6
+
7
+ // ONE BACKEND: the certifications service (talents-ai-score, ADR-042).
8
+
9
+ const MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
10
+
11
+ // Extra request headers, sanitized (talents-ai-score, issue 038 — the agent certification interview needs `Authorization: Bearer <sessionToken>`).
12
+ const UNSAFE_HEADER_VALUE_RE = /[\r\n\0]/;
13
+
14
+ function sanitizeExtraHeaders(extraHeaders) {
15
+ if (!extraHeaders || typeof extraHeaders !== 'object') return {};
16
+ const out = {};
17
+ for (const [name, value] of Object.entries(extraHeaders)) {
18
+ if (typeof name !== 'string' || !name || UNSAFE_HEADER_VALUE_RE.test(name)) continue;
19
+ if (typeof value !== 'string' || !value || UNSAFE_HEADER_VALUE_RE.test(value)) continue;
20
+ out[name] = value;
21
+ }
22
+ return out;
23
+ }
24
+
25
+ // Canonical, zero-dependency JSON request with a timeout — consolidates the ~8 near-identical copies that used to live one per client module.
26
+ function postJsonWithTimeout(url, body, timeoutMs, method = 'POST', idempotencyKey = null, extraHeaders = null) {
27
+ return new Promise((resolve, reject) => {
28
+ let u;
29
+ try {
30
+ u = new URL(url);
31
+ } catch (e) {
32
+ return reject(Object.assign(e, { kind: 'invalid-url' }));
33
+ }
34
+ const lib = u.protocol === 'https:' ? https : http;
35
+ const data = body === null || body === undefined ? null : Buffer.from(JSON.stringify(body));
36
+ const req = lib.request(
37
+ u,
38
+ {
39
+ method,
40
+ headers: {
41
+ Accept: 'application/json',
42
+ // Only declared when there IS a body — see the bodyless note above.
43
+ ...(data ? { 'Content-Type': 'application/json', 'Content-Length': data.length } : {}),
44
+ ...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {}),
45
+ ...sanitizeExtraHeaders(extraHeaders),
46
+ },
47
+ timeout: timeoutMs,
48
+ },
49
+ (res) => {
50
+ let raw = '';
51
+ res.setEncoding('utf8');
52
+ res.on('data', (chunk) => {
53
+ raw += chunk;
54
+ if (raw.length > MAX_RESPONSE_BYTES) req.destroy();
55
+ });
56
+ // `headers` is exposed so the better-auth email flow can read `Set-Cookie` (the session cookie it captures and replays); every other caller reads only `{status, raw}` and ignores it.
57
+ res.on('end', () => resolve({ status: res.statusCode, raw, headers: res.headers }));
58
+ },
59
+ );
60
+ req.on('timeout', () => req.destroy(Object.assign(new Error('request timed out'), { kind: 'timeout' })));
61
+ req.on('error', (e) => reject(Object.assign(e, { kind: e.kind || 'network-error' })));
62
+ if (data) req.write(data);
63
+ req.end();
64
+ });
65
+ }
66
+
67
+ // Pulls one `name=value` cookie pair out of a response's `Set-Cookie` header (Node exposes it as an array, or a single string).
68
+ // Accepts the base cookie name with or without the RFC6265 `__Secure-`/`__Host-`
69
+ // prefixes that better-auth applies over HTTPS, and returns the EXACT server-set
70
+ // name (prefix included) so it can be forwarded back verbatim in a `Cookie:` header.
71
+ function extractSetCookie(headers, name) {
72
+ if (!headers) return null;
73
+ const raw = headers['set-cookie'];
74
+ const list = Array.isArray(raw) ? raw : (typeof raw === 'string' && raw ? [raw] : []);
75
+ const candidates = [name, `__Secure-${name}`, `__Host-${name}`];
76
+ for (const candidate of candidates) {
77
+ const prefix = `${candidate}=`;
78
+ for (const entry of list) {
79
+ if (typeof entry === 'string' && entry.startsWith(prefix)) {
80
+ const value = entry.slice(prefix.length).split(';')[0];
81
+ if (value) return `${candidate}=${value}`;
82
+ }
83
+ }
84
+ }
85
+ return null;
86
+ }
87
+
88
+ function postMultipartWithTimeout(url, fields, timeoutMs, method = 'POST', extraHeaders = null) {
89
+ return new Promise((resolve, reject) => {
90
+ let u;
91
+ try {
92
+ u = new URL(url);
93
+ } catch (e) {
94
+ return reject(Object.assign(e, { kind: 'invalid-url' }));
95
+ }
96
+ const lib = u.protocol === 'https:' ? https : http;
97
+ const boundary = `----shakers${crypto.randomBytes(16).toString('hex')}`;
98
+ const chunks = [];
99
+ for (const [name, value] of Object.entries(fields || {})) {
100
+ if (value === undefined || value === null) continue;
101
+ const clean = String(value).replace(UNSAFE_HEADER_VALUE_RE, '');
102
+ chunks.push(Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="${name}"\r\n\r\n${clean}\r\n`));
103
+ }
104
+ chunks.push(Buffer.from(`--${boundary}--\r\n`));
105
+ const data = Buffer.concat(chunks);
106
+ const req = lib.request(
107
+ u,
108
+ {
109
+ method,
110
+ headers: {
111
+ Accept: 'application/json',
112
+ 'Content-Type': `multipart/form-data; boundary=${boundary}`,
113
+ 'Content-Length': data.length,
114
+ ...sanitizeExtraHeaders(extraHeaders),
115
+ },
116
+ timeout: timeoutMs,
117
+ },
118
+ (res) => {
119
+ let raw = '';
120
+ res.setEncoding('utf8');
121
+ res.on('data', (chunk) => {
122
+ raw += chunk;
123
+ if (raw.length > MAX_RESPONSE_BYTES) req.destroy();
124
+ });
125
+ res.on('end', () => resolve({ status: res.statusCode, raw }));
126
+ },
127
+ );
128
+ req.on('timeout', () => req.destroy(Object.assign(new Error('request timed out'), { kind: 'timeout' })));
129
+ req.on('error', (e) => reject(Object.assign(e, { kind: e.kind || 'network-error' })));
130
+ req.write(data);
131
+ req.end();
132
+ });
133
+ }
134
+
135
+ // One request to the one backend.
136
+ async function requestBackend({
137
+ endpoint,
138
+ body,
139
+ timeoutMs,
140
+ method = 'POST',
141
+ idempotencyKey = null,
142
+ extraHeaders = null,
143
+ trace = null,
144
+ } = {}) {
145
+ if (!endpoint) {
146
+ throw Object.assign(new Error('no endpoint configured'), { kind: 'no-endpoint' });
147
+ }
148
+
149
+ // Optional trace (talents-ai-score, issue 109).
150
+ const note = (entry) => {
151
+ if (Array.isArray(trace)) trace.push(entry);
152
+ };
153
+
154
+ try {
155
+ const res = await postJsonWithTimeout(endpoint, body, timeoutMs, method, idempotencyKey, extraHeaders);
156
+ note({ backend: 'primary', status: res.status, kind: null });
157
+ return { backend: 'primary', status: res.status, raw: res.raw };
158
+ } catch (e) {
159
+ note({ backend: 'primary', status: null, kind: (e && e.kind) || 'network-error' });
160
+ throw e;
161
+ }
162
+ }
163
+
164
+ module.exports = {
165
+ MAX_RESPONSE_BYTES,
166
+ postJsonWithTimeout,
167
+ postMultipartWithTimeout,
168
+ extractSetCookie,
169
+ requestBackend,
170
+ };
@@ -0,0 +1,48 @@
1
+ 'use strict';
2
+
3
+ const { eachHumanText } = require('./transcript-turns');
4
+
5
+ const MAX_AFFIRMATION_LEN = 24;
6
+
7
+ const AFFIRMATION_RE =
8
+ /^(s[ií]|ok|okay|oka|vale|dale|perfecto|perfect|genial|correcto|exacto|eso|as[ií]|adelante|hazlo|listo|ya|yes|yep|yeah|yup|sure|lgtm|great|thanks|thx|gracias|go ahead|do it|sounds good|looks good|makes sense|ok perfecto|ok gracias)$/i;
9
+
10
+ const EMOJI_AFFIRMATION_RE = /^[\s👍👌✅🙌💯🚀]+$/u;
11
+
12
+ function normalizeTurn(text) {
13
+ return text
14
+ .replace(/^[\s(*_~"'`\-]+/u, '')
15
+ .replace(/[\s.!¡¿?…,:;)*_~"'`\-]+$/u, '')
16
+ .trim();
17
+ }
18
+
19
+ function makeBlindApprovalAccumulator() {
20
+ let blindApprovalCount = 0;
21
+ let humanTurnCount = 0;
22
+ return {
23
+ add(text) {
24
+ const trimmed = typeof text === 'string' ? text.trim() : '';
25
+ if (!trimmed) return;
26
+ humanTurnCount += 1;
27
+ if (trimmed.length > MAX_AFFIRMATION_LEN) return;
28
+ if (EMOJI_AFFIRMATION_RE.test(trimmed)) {
29
+ blindApprovalCount += 1;
30
+ return;
31
+ }
32
+ const norm = normalizeTurn(trimmed).toLowerCase();
33
+ if (norm && AFFIRMATION_RE.test(norm)) blindApprovalCount += 1;
34
+ },
35
+ result() {
36
+ if (humanTurnCount === 0) return null;
37
+ return { blindApprovalCount, humanTurnCount };
38
+ },
39
+ };
40
+ }
41
+
42
+ function collectBlindApproval(env = process.env, selectedCwds = null) {
43
+ const acc = makeBlindApprovalAccumulator();
44
+ for (const text of eachHumanText(env, { selectedCwds })) acc.add(text);
45
+ return acc.result();
46
+ }
47
+
48
+ module.exports = { collectBlindApproval, makeBlindApprovalAccumulator };
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ // BRAND_ANSI — the Shakers brand palette, reincarnated as ANSI 256-colour SGR codes (talents-ai-score, terminal design-consistency pass).
4
+
5
+ const BRAND_ANSI = {
6
+ primary: '\x1b[38;5;73m',
7
+ accent: '\x1b[38;5;185m',
8
+ success: '\x1b[38;5;29m',
9
+ warning: '\x1b[38;5;214m',
10
+ danger: '\x1b[38;5;161m',
11
+ muted: '\x1b[38;5;243m',
12
+ };
13
+
14
+ module.exports = { BRAND_ANSI };
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ const SHAKERS_ICON_SVG = "<svg class=\"ic\" viewBox=\"0 0 12 12\" fill=\"none\" title=\"Shakers\" xmlns=\"http://www.w3.org/2000/svg\"><rect rx=\"2\" fill=\"#F0FF3D\"/><mask id=\"path-2-inside-1_195_15441\" fill=\"white\"><path d=\"M5.40574 5.41145C5.79071 5.48342 6.0657 5.53741 6.35901 5.57339C6.68899 5.62738 7.01897 5.64537 7.36729 5.68136C7.64227 5.71735 7.88059 5.82531 7.97225 6.11322C8.06391 6.41912 7.91725 6.63504 7.69727 6.81499C7.58727 6.90496 7.45895 6.99493 7.34895 7.0849C6.524 7.66071 5.69905 8.23652 4.8741 8.81233C4.81911 8.84832 4.76411 8.9023 4.69078 8.93829C4.48912 9.04625 4.26914 9.01026 4.12248 8.84832C3.97582 8.68637 3.95749 8.47044 4.08582 8.27251C4.17748 8.12855 4.3058 8.02059 4.43413 7.93062C5.03909 7.49876 5.64406 7.0849 6.32235 6.59906C6.12069 6.54507 5.99237 6.52708 5.86404 6.49109C5.51573 6.41912 5.16742 6.36513 4.83744 6.25717C4.23247 6.04124 4.08582 5.51941 4.50746 5.01558C4.78244 4.69168 5.11242 4.40378 5.4424 4.13387C5.90071 3.79198 6.39568 3.48608 6.89065 3.18018C7.27562 2.92826 7.55061 2.94626 7.7156 3.19818C7.88059 3.45009 7.7706 3.70201 7.40395 3.97192C6.94564 4.29581 6.46901 4.60171 6.0107 4.92561C5.82738 5.05157 5.66239 5.19552 5.40574 5.41145Z\"/></mask><path d=\"M5.40574 5.41145C5.79071 5.48342 6.0657 5.53741 6.35901 5.57339C6.68899 5.62738 7.01897 5.64537 7.36729 5.68136C7.64227 5.71735 7.88059 5.82531 7.97225 6.11322C8.06391 6.41912 7.91725 6.63504 7.69727 6.81499C7.58727 6.90496 7.45895 6.99493 7.34895 7.0849C6.524 7.66071 5.69905 8.23652 4.8741 8.81233C4.81911 8.84832 4.76411 8.9023 4.69078 8.93829C4.48912 9.04625 4.26914 9.01026 4.12248 8.84832C3.97582 8.68637 3.95749 8.47044 4.08582 8.27251C4.17748 8.12855 4.3058 8.02059 4.43413 7.93062C5.03909 7.49876 5.64406 7.0849 6.32235 6.59906C6.12069 6.54507 5.99237 6.52708 5.86404 6.49109C5.51573 6.41912 5.16742 6.36513 4.83744 6.25717C4.23247 6.04124 4.08582 5.51941 4.50746 5.01558C4.78244 4.69168 5.11242 4.40378 5.4424 4.13387C5.90071 3.79198 6.39568 3.48608 6.89065 3.18018C7.27562 2.92826 7.55061 2.94626 7.7156 3.19818C7.88059 3.45009 7.7706 3.70201 7.40395 3.97192C6.94564 4.29581 6.46901 4.60171 6.0107 4.92561C5.82738 5.05157 5.66239 5.19552 5.40574 5.41145Z\" fill=\"black\"/><path d=\"M5.40574 5.41145L4.5495 4.39373L2.39965 6.20247L5.16131 6.71879L5.40574 5.41145ZM6.35901 5.57339L6.57374 4.26084L6.54743 4.25654L6.52098 4.25329L6.35901 5.57339ZM7.36729 5.68136L7.53988 4.3626L7.52196 4.36026L7.50398 4.3584L7.36729 5.68136ZM7.97225 6.11322L9.24628 5.73146L9.24302 5.72057L9.23957 5.70974L7.97225 6.11322ZM7.34895 7.0849L8.11019 8.1755L8.15177 8.14648L8.19102 8.11437L7.34895 7.0849ZM4.8741 8.81233L5.60235 9.92523L5.61901 9.91433L5.63534 9.90293L4.8741 8.81233ZM4.69078 8.93829L4.10481 7.74433L4.08373 7.75467L4.06302 7.76576L4.69078 8.93829ZM4.08582 8.27251L5.20184 8.99605L5.20769 8.98686L4.08582 8.27251ZM4.43413 7.93062L5.19767 9.01967L5.20687 9.0131L4.43413 7.93062ZM6.32235 6.59906L7.09681 7.68031L9.38425 6.04189L6.66628 5.31429L6.32235 6.59906ZM5.86404 6.49109L6.22318 5.2105L6.17857 5.19799L6.13319 5.18861L5.86404 6.49109ZM4.83744 6.25717L4.39035 7.50977L4.40703 7.51572L4.42386 7.52123L4.83744 6.25717ZM4.50746 5.01558L3.49355 4.15478L3.4875 4.16201L4.50746 5.01558ZM5.4424 4.13387L4.64715 3.06781L4.62333 3.08558L4.60033 3.10439L5.4424 4.13387ZM6.89065 3.18018L7.58986 4.31156L7.6045 4.30251L7.6189 4.29308L6.89065 3.18018ZM7.40395 3.97192L8.17155 5.05806L8.18206 5.05063L8.19244 5.04299L7.40395 3.97192ZM6.0107 4.92561L6.76388 6.02179L6.77112 6.01682L6.7783 6.01175L6.0107 4.92561ZM5.40574 5.41145L5.16131 6.71879C5.52602 6.78698 5.85177 6.85113 6.19704 6.89349L6.35901 5.57339L6.52098 4.25329C6.27963 4.22368 6.05541 4.17987 5.65016 4.1041L5.40574 5.41145ZM6.35901 5.57339L6.14429 6.88595C6.52363 6.948 6.96912 6.9773 7.2306 7.00432L7.36729 5.68136L7.50398 4.3584C7.06882 4.31344 6.85436 4.30675 6.57374 4.26084L6.35901 5.57339ZM7.36729 5.68136L7.1947 7.00011C7.22634 7.00425 7.14196 7.00011 7.02146 6.92297C6.95597 6.88104 6.88631 6.82153 6.8252 6.74241C6.76394 6.66312 6.72617 6.58341 6.70493 6.5167L7.97225 6.11322L9.23957 5.70974C8.91877 4.70211 8.03627 4.42757 7.53988 4.3626L7.36729 5.68136ZM7.97225 6.11322L6.69822 6.49497C6.68006 6.43438 6.66478 6.34665 6.67169 6.24031C6.67866 6.13311 6.70632 6.03609 6.74308 5.95606C6.80992 5.81059 6.89001 5.75704 6.8552 5.78551L7.69727 6.81499L8.53934 7.84446C8.72451 7.69299 8.98792 7.4415 9.16017 7.06661C9.35175 6.64963 9.38233 6.18547 9.24628 5.73146L7.97225 6.11322ZM7.69727 6.81499L6.8552 5.78551C6.82379 5.8112 6.78547 5.84036 6.72169 5.88851C6.66631 5.93033 6.58547 5.99114 6.50688 6.05542L7.34895 7.0849L8.19102 8.11437C8.22243 8.08868 8.26075 8.05953 8.32453 8.01137C8.37991 7.96956 8.46075 7.90874 8.53934 7.84446L7.69727 6.81499ZM7.34895 7.0849L6.58772 5.99429C6.17524 6.2822 5.76277 6.5701 5.35029 6.85801C4.93782 7.14591 4.52534 7.43382 4.11287 7.72172L4.8741 8.81233L5.63534 9.90293C6.04781 9.61503 6.46029 9.32712 6.87276 9.03922C7.28524 8.75131 7.69771 8.46341 8.11019 8.1755L7.34895 7.0849ZM4.8741 8.81233L4.14585 7.69943C4.11806 7.71761 4.09401 7.73447 4.07477 7.74837C4.05563 7.7622 4.0391 7.77464 4.027 7.78385C4.01562 7.79251 4.00413 7.8014 3.99881 7.80551C3.99186 7.81087 3.98933 7.8128 3.9879 7.81388C3.98513 7.81597 3.99593 7.80765 4.01438 7.79558C4.03359 7.78301 4.06427 7.76422 4.10481 7.74433L4.69078 8.93829L5.27675 10.1322C5.42744 10.0583 5.53906 9.97575 5.59073 9.93674C5.6042 9.92657 5.61628 9.91725 5.62372 9.91151C5.63279 9.90451 5.63548 9.90243 5.63813 9.90041C5.64392 9.89601 5.62822 9.9083 5.60235 9.92523L4.8741 8.81233ZM4.69078 8.93829L4.06302 7.76576C4.19756 7.69373 4.3847 7.64744 4.59586 7.68118C4.80898 7.71523 4.98718 7.82178 5.10831 7.95555L4.12248 8.84832L3.13665 9.74108C3.71298 10.3775 4.60502 10.4928 5.31854 10.1108L4.69078 8.93829ZM4.12248 8.84832L5.10831 7.95555C5.21881 8.07757 5.31241 8.25969 5.32783 8.48174C5.34315 8.70232 5.27605 8.88149 5.2018 8.99602L4.08582 8.27251L2.96983 7.54899C2.52695 8.2321 2.56882 9.11407 3.13665 9.74108L4.12248 8.84832ZM4.08582 8.27251L5.20769 8.98686C5.18327 9.02521 5.16246 9.04539 5.1591 9.04856C5.15736 9.0502 5.15904 9.04847 5.16559 9.04326C5.17219 9.03802 5.18238 9.03033 5.19764 9.01963L4.43413 7.93062L3.67061 6.84161C3.48482 6.97187 3.19257 7.1991 2.96394 7.55816L4.08582 8.27251ZM4.43413 7.93062L5.20687 9.0131C5.5033 8.80149 5.80019 8.59396 6.1135 8.37462C6.42521 8.1564 6.75125 7.92782 7.09681 7.68031L6.32235 6.59906L5.54788 5.51781C5.21515 5.75613 4.89957 5.97741 4.58798 6.19554C4.27799 6.41256 3.96992 6.62789 3.66139 6.84813L4.43413 7.93062ZM6.32235 6.59906L6.66628 5.31429C6.53197 5.27834 6.42281 5.25469 6.3449 5.23796C6.25853 5.21942 6.2392 5.21499 6.22318 5.2105L5.86404 6.49109L5.50491 7.77169C5.61721 7.80318 5.72621 7.82575 5.78649 7.83869C5.85524 7.85345 5.91107 7.86579 5.97842 7.88382L6.32235 6.59906ZM5.86404 6.49109L6.13319 5.18861C5.69621 5.09831 5.48626 5.07007 5.25102 4.99311L4.83744 6.25717L4.42386 7.52123C4.84858 7.66019 5.33525 7.73992 5.59489 7.79357L5.86404 6.49109ZM4.83744 6.25717L5.28453 5.00457C5.25674 4.99465 5.48421 5.07474 5.5714 5.3973C5.65859 5.71991 5.50148 5.90014 5.52742 5.86915L4.50746 5.01558L3.4875 4.16201C3.0918 4.63485 2.79719 5.32791 3.00354 6.09137C3.20989 6.85479 3.81318 7.30376 4.39035 7.50977L4.83744 6.25717ZM4.50746 5.01558L5.52134 5.87636C5.71543 5.64775 5.96863 5.42169 6.28447 5.16334L5.4424 4.13387L4.60033 3.10439C4.25621 3.38587 3.84945 3.73562 3.49357 4.1548L4.50746 5.01558ZM5.4424 4.13387L6.23766 5.19992C6.6414 4.89874 7.08595 4.62298 7.58986 4.31156L6.89065 3.18018L6.19144 2.04881C5.7054 2.34918 5.16002 2.68522 4.64715 3.06781L5.4424 4.13387ZM6.89065 3.18018L7.6189 4.29308C7.70972 4.23365 7.59836 4.33791 7.34213 4.32953C7.19629 4.32476 7.03451 4.28314 6.88424 4.19249C6.7393 4.10506 6.65077 3.99983 6.60299 3.92687L7.7156 3.19818L8.82821 2.46948C8.54332 2.03449 8.05952 1.69158 7.42911 1.67095C6.8979 1.65357 6.45655 1.87479 6.1624 2.06728L6.89065 3.18018ZM7.7156 3.19818L6.60299 3.92687C6.55028 3.8464 6.48654 3.71395 6.46651 3.53699C6.44602 3.356 6.47885 3.19466 6.52902 3.07261C6.61312 2.86805 6.72392 2.82101 6.61547 2.90085L7.40395 3.97192L8.19244 5.04299C8.45062 4.85292 8.79975 4.54497 8.98925 4.08399C9.22207 3.51765 9.13112 2.93198 8.82821 2.46948L7.7156 3.19818ZM7.40395 3.97192L6.63636 2.88578C6.42142 3.03768 6.20011 3.18682 5.96428 3.34569C5.73264 3.50172 5.48648 3.66747 5.24311 3.83947L6.0107 4.92561L6.7783 6.01175C6.99323 5.85985 7.21454 5.71071 7.45038 5.55184C7.68201 5.39581 7.92817 5.23006 8.17155 5.05806L7.40395 3.97192ZM6.0107 4.92561L5.25752 3.82942C5.00582 4.00236 4.7749 4.20409 4.5495 4.39373L5.40574 5.41145L6.26198 6.42917C6.54988 6.18695 6.64894 6.10077 6.76388 6.02179L6.0107 4.92561Z\" fill=\"black\" mask=\"url(#path-2-inside-1_195_15441)\"/></svg>";
4
+
5
+ const SHAKERS_MARK_WHITE_SVG = "<svg class=\"mk\" viewBox=\"0 0 14 22\" fill=\"none\" title=\"Shakers\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M4.92008 8.84197C6.2675 9.10589 7.22994 9.30382 8.25655 9.43578C9.41148 9.63371 10.5664 9.69969 11.7855 9.83165C12.7479 9.9636 13.5821 10.3595 13.9029 11.4151C14.2237 12.5368 13.7104 13.3285 12.9404 13.9883C12.5555 14.3182 12.1063 14.6481 11.7213 14.978C8.83401 17.0893 5.94668 19.2006 3.05936 21.3119C2.86687 21.4438 2.67438 21.6418 2.41773 21.7737C1.71194 22.1696 0.941983 22.0376 0.42868 21.4438C-0.0846227 20.85 -0.148786 20.0583 0.300354 19.3325C0.621168 18.8047 1.07031 18.4088 1.51945 18.0789C3.63682 16.4955 5.7542 14.978 8.12822 13.1965C7.42243 12.9986 6.97329 12.9326 6.52415 12.8007C5.30506 12.5368 4.08596 12.3388 2.93103 11.943C0.813657 11.1512 0.300354 9.23784 1.7761 7.39045C2.73854 6.20284 3.89347 5.14719 5.0484 4.15751C6.65247 2.90393 8.38487 1.7823 10.1173 0.660666C11.4647 -0.26303 12.4271 -0.197052 13.0046 0.726644C13.5821 1.65034 13.1971 2.57404 11.9138 3.56371C10.3098 4.75132 8.64152 5.87295 7.03745 7.06056C6.39582 7.52241 5.81836 8.05023 4.92008 8.84197Z\" fill=\"white\"/></svg>";
6
+
7
+ const SHAKERS_WORDMARK_SVG = "<svg class=\"wm\" viewBox=\"0 0 87 18\" fill=\"none\" title=\"Shakers\" xmlns=\"http://www.w3.org/2000/svg\"><g clip-path=\"url(#clip0_423_13639)\"><path d=\"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\" fill=\"currentColor\"/><path d=\"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\" fill=\"currentColor\"/><path d=\"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\" fill=\"currentColor\"/><path d=\"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\" fill=\"currentColor\"/><path d=\"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\" fill=\"currentColor\"/><path d=\"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\" fill=\"currentColor\"/><path d=\"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\" fill=\"currentColor\"/></g><defs><clipPath id=\"clip0_423_13639\"><rect fill=\"white\"/></clipPath></defs></svg>";
8
+
9
+ module.exports = { SHAKERS_ICON_SVG, SHAKERS_MARK_WHITE_SVG, SHAKERS_WORDMARK_SVG };
@@ -0,0 +1,21 @@
1
+ 'use strict';
2
+
3
+ // Deterministic (no-LLM) browser tools detector (talents-ai-score, issue 018 / ADR-013-014).
4
+
5
+ const BROWSER_DEPENDENCY_NAMES = new Set(['playwright', '@playwright/test', 'puppeteer', 'puppeteer-core']);
6
+
7
+ function detectBrowserTools(technologies, mcp) {
8
+ const techList = Array.isArray(technologies) ? technologies : [];
9
+ const mcpServers = mcp && Array.isArray(mcp.servers) ? mcp.servers : [];
10
+
11
+ const dependencies = techList.filter((name) => BROWSER_DEPENDENCY_NAMES.has(name));
12
+ const mcpBrowserNames = mcpServers.filter((s) => s.category === 'browser').map((s) => s.name);
13
+
14
+ return {
15
+ detected: dependencies.length > 0 || mcpBrowserNames.length > 0,
16
+ via: { dependencies, mcp: mcpBrowserNames },
17
+ count: dependencies.length + mcpBrowserNames.length,
18
+ };
19
+ }
20
+
21
+ module.exports = { detectBrowserTools };
@@ -0,0 +1,56 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { getRoadmapEntry } = require('./roadmap-content');
6
+
7
+ const TIER_ORDER = ['T0', 'T1', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'];
8
+
9
+ function nextTierKey(tierKey) {
10
+ const idx = TIER_ORDER.indexOf(tierKey);
11
+ if (idx === -1 || idx === TIER_ORDER.length - 1) return null;
12
+ return TIER_ORDER[idx + 1];
13
+ }
14
+
15
+ // Writes one snippet file, respecting the "never overwrite without force"
16
+ // invariant. Creates parent directories as needed.
17
+ function writeSnippetFile(root, filename, code, force) {
18
+ const target = path.join(root, filename);
19
+ const exists = fs.existsSync(target);
20
+ if (exists && !force) {
21
+ return { filename, status: 'skipped-exists' };
22
+ }
23
+ fs.mkdirSync(path.dirname(target), { recursive: true });
24
+ fs.writeFileSync(target, code);
25
+ return { filename, status: exists ? 'overwritten' : 'created' };
26
+ }
27
+
28
+ // Builds the starter artifact(s) that unlock the NEXT tier, deterministic, straight from the curated roadmap content.
29
+ function buildNextLevelStarter(root, currentTierKey, { force = false } = {}) {
30
+ const entry = getRoadmapEntry(currentTierKey, 'es'); // snippets are literal, language-independent
31
+ if (!entry) return { ok: false, reason: 'unrecognized-tier' };
32
+ if (entry.maxTier) return { ok: false, reason: 'max-tier' };
33
+
34
+ const { snippet } = entry;
35
+ if (!snippet || !snippet.filename) {
36
+ // Some jumps (e.g. T0 -> T1) are a shell command to run, not a file to
37
+ // write — never invent a file target for those.
38
+ return { ok: false, reason: 'no-file-target', label: snippet ? snippet.label : null };
39
+ }
40
+
41
+ const targets = [{ filename: snippet.filename, code: snippet.code }];
42
+ if (snippet.secondFile) {
43
+ targets.push({ filename: snippet.secondFile.filename, code: snippet.secondFile.code });
44
+ }
45
+
46
+ const files = targets.map((t) => writeSnippetFile(root, t.filename, t.code, force));
47
+
48
+ return {
49
+ ok: true,
50
+ fromTierKey: currentTierKey,
51
+ targetTierKey: nextTierKey(currentTierKey),
52
+ files,
53
+ };
54
+ }
55
+
56
+ module.exports = { buildNextLevelStarter, nextTierKey };
@@ -0,0 +1,89 @@
1
+ 'use strict';
2
+
3
+ // My-certifications client (hub `works/certifications/me`, talent Bearer). The
4
+ // "By role" page: main-role cluster + its dimension rows (state/band) + ratios.
5
+
6
+ const { postJsonWithTimeout } = require('./backend-request');
7
+ const { reasonForError } = require('./onboarding-client');
8
+
9
+ const DEFAULT_TIMEOUT_MS = 20000;
10
+
11
+ function parseJson(raw) {
12
+ try {
13
+ return raw ? JSON.parse(raw) : null;
14
+ } catch {
15
+ return null;
16
+ }
17
+ }
18
+
19
+ function bearerHeaders(accessToken) {
20
+ const headers = {};
21
+ if (typeof accessToken === 'string' && accessToken) headers.Authorization = `Bearer ${accessToken}`;
22
+ return headers;
23
+ }
24
+
25
+ function normalizeCluster(c) {
26
+ if (!c || typeof c !== 'object') return null;
27
+ const progress = c.progress && typeof c.progress === 'object' ? c.progress : {};
28
+ return {
29
+ clusterId: typeof c.clusterId === 'string' ? c.clusterId : null,
30
+ name: typeof c.name === 'string' ? c.name : null,
31
+ category: typeof c.category === 'string' ? c.category : null,
32
+ certified: typeof progress.certified === 'number' ? progress.certified : null,
33
+ total: typeof progress.total === 'number' ? progress.total : null,
34
+ };
35
+ }
36
+
37
+ function normalizeDimension(d) {
38
+ const it = d && typeof d === 'object' ? d : {};
39
+ return {
40
+ slug: typeof it.slug === 'string' ? it.slug : null,
41
+ name: typeof it.name === 'string' ? it.name : null,
42
+ state: typeof it.state === 'string' ? it.state : 'UNCERTIFIED',
43
+ band: typeof it.band === 'string' ? it.band : null,
44
+ expiresAt: typeof it.expiresAt === 'string' ? it.expiresAt : null,
45
+ };
46
+ }
47
+
48
+ async function requestMeCertifications({ hubAccessToken } = {}, { endpoint, timeoutMs = DEFAULT_TIMEOUT_MS } = {}) {
49
+ if (!endpoint) return { ok: false, reason: 'no-endpoint' };
50
+ if (!hubAccessToken) return { ok: false, reason: 'no-hub-token' };
51
+ let res;
52
+ try {
53
+ res = await postJsonWithTimeout(endpoint, null, timeoutMs, 'GET', null, bearerHeaders(hubAccessToken));
54
+ } catch (e) {
55
+ return { ok: false, reason: (e && e.kind) || 'network-error' };
56
+ }
57
+ if (res.status < 200 || res.status >= 300) return { ok: false, reason: reasonForError(res.status, res.raw) };
58
+
59
+ const env = parseJson(res.raw);
60
+ const data = env && env.status === 'OK' ? env.data : env;
61
+ if (!data || typeof data !== 'object') return { ok: false, reason: 'bad-response' };
62
+ const dims = Array.isArray(data.dimensions) ? data.dimensions.map(normalizeDimension) : [];
63
+ return {
64
+ ok: true,
65
+ mainRole: normalizeCluster(data.mainRole),
66
+ growingInto: Array.isArray(data.growingInto) ? data.growingInto.map(normalizeCluster).filter(Boolean) : [],
67
+ dimensions: dims,
68
+ skillsRatio: data.skillsRatio && typeof data.skillsRatio === 'object'
69
+ ? { certified: data.skillsRatio.certified ?? null, total: data.skillsRatio.total ?? null }
70
+ : null,
71
+ };
72
+ }
73
+
74
+ async function fetchMeCertifications(deps = {}, { hubAccessToken, timeoutMs = DEFAULT_TIMEOUT_MS } = {}) {
75
+ const {
76
+ getMyCertificationsEndpoint = require('./config').getMyCertificationsEndpoint,
77
+ requestMeCertifications: request = requestMeCertifications,
78
+ } = deps;
79
+ const endpoint = getMyCertificationsEndpoint();
80
+ if (!endpoint) return { ok: false, reason: 'no-endpoint' };
81
+ return request({ hubAccessToken }, { endpoint, timeoutMs });
82
+ }
83
+
84
+ module.exports = {
85
+ normalizeCluster,
86
+ normalizeDimension,
87
+ requestMeCertifications,
88
+ fetchMeCertifications,
89
+ };