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/bin/shakers.js ADDED
@@ -0,0 +1,344 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ var _nodeMajor = parseInt((process.versions && process.versions.node || '0').split('.')[0], 10);
5
+ if (_nodeMajor < 18) {
6
+ process.stderr.write(
7
+ '\n shakers requires Node 18 or newer (you have ' + process.version + ').\n'
8
+ + ' Update Node from https://nodejs.org and re-run.\n\n',
9
+ );
10
+ process.exit(1);
11
+ }
12
+
13
+ const { detectReportLang, getCatalog } = require('../src/i18n');
14
+ const { promptSelect } = require('../src/prompt-select');
15
+ const { createStdinAsk } = require('../src/stdin-ask');
16
+ const { migrateLegacyConfigDir } = require('../src/config-dir');
17
+ const { ensureLocalConfigDefaults } = require('../src/config');
18
+ const { ensureFreshSession } = require('../src/session-refresh');
19
+ const { hasUsableSession } = require('../src/session-gate');
20
+ const { run: runAiUsage } = require('./ai-usage');
21
+ const { run: runCertify } = require('./certify');
22
+ const { run: runShare } = require('./share');
23
+ const { run: runReport } = require('./report-html');
24
+ const { run: runSuperadmin } = require('./superadmin');
25
+ const { run: runLogin, runLogout } = require('./login');
26
+ const { run: runAddSkill } = require('./add-skill');
27
+ const { run: runAddAgent } = require('./add-agent');
28
+ const { run: runAddProject } = require('./add-project');
29
+ const { run: runAddRole } = require('./add-role');
30
+ const { run: runChangeRole } = require('./change-role');
31
+ const { run: runFindProjects } = require('./find-projects');
32
+ const { run: runShowProject } = require('./show-project');
33
+ const { run: runSave, runUnsave } = require('./save');
34
+ const { run: runInvitations } = require('./invitations');
35
+ const { run: runApplications } = require('./applications');
36
+ const { run: runAvailability } = require('./availability');
37
+ const { run: runCertifications } = require('./certifications');
38
+ const { run: runMe } = require('./me');
39
+ const { run: runRate } = require('./rate');
40
+ const { run: runLang } = require('./lang');
41
+ const { run: runSocials } = require('./socials');
42
+ const { run: runExperiences } = require('./experiences');
43
+ const { run: runPortfolios } = require('./portfolios');
44
+ const { run: runAlma } = require('./alma');
45
+ const { run: runRegister } = require('./register');
46
+ const { run: runOnboarding } = require('./onboarding');
47
+ const { run: runConfig } = require('./config');
48
+ const { run: runMcp } = require('./mcp');
49
+ const { checkOnboardingCompleted } = require('../src/onboarding-availability');
50
+
51
+ let VERSION = '';
52
+ try {
53
+ VERSION = require('../package.json').version || '';
54
+ } catch {
55
+ VERSION = '';
56
+ }
57
+
58
+ function parseLang(argv) {
59
+ for (let i = 0; i < argv.length; i++) {
60
+ const a = argv[i];
61
+ if (a === '--lang' && (argv[i + 1] === 'es' || argv[i + 1] === 'en')) return argv[i + 1];
62
+ if (a === '--lang=es') return 'es';
63
+ if (a === '--lang=en') return 'en';
64
+ }
65
+ return null;
66
+ }
67
+
68
+ const HANDLERS = {
69
+ 'ai-usage': runAiUsage,
70
+ certify: runCertify,
71
+ share: runShare,
72
+ report: runReport,
73
+ sheet: runReport,
74
+ 'add-skill': runAddSkill,
75
+ 'add-agent': runAddAgent,
76
+ 'add-project': runAddProject,
77
+ 'add-role': runAddRole,
78
+ 'change-role': runChangeRole,
79
+ 'find-projects': runFindProjects,
80
+ 'show-project': runShowProject,
81
+ 'save-project': runSave,
82
+ 'unsave-project': runUnsave,
83
+ invitations: runInvitations,
84
+ applications: runApplications,
85
+ availability: runAvailability,
86
+ certifications: runCertifications,
87
+ me: runMe,
88
+ profile: runMe,
89
+ rate: runRate,
90
+ lang: runLang,
91
+ socials: runSocials,
92
+ experiences: runExperiences,
93
+ portfolios: runPortfolios,
94
+ alma: runAlma,
95
+ register: runRegister,
96
+ onboarding: runOnboarding,
97
+ config: runConfig,
98
+ login: runLogin,
99
+ logout: runLogout,
100
+ superadmin: runSuperadmin,
101
+ mcp: runMcp,
102
+ };
103
+
104
+ const SESSION_EXEMPT = new Set(['ai-usage', 'register', 'config', 'login', 'logout', 'superadmin', 'mcp', 'share', 'alma']);
105
+
106
+ // Command FAMILIES — a navigation layer over the flat commands (which all stay
107
+ // valid as aliases). `shakers <family> <sub>` resolves to the flat handler; every
108
+ // sub is just a friendlier name for an existing command. Additive, never breaking.
109
+ const FAMILIES = {
110
+ add: { skill: 'add-skill', agent: 'add-agent', project: 'add-project', role: 'add-role' },
111
+ role: { add: 'add-role', change: 'change-role', main: 'change-role' },
112
+ profile: {
113
+ show: 'me', rate: 'rate', languages: 'lang', lang: 'lang', socials: 'socials',
114
+ experiences: 'experiences', portfolios: 'portfolios', availability: 'availability',
115
+ certifications: 'certifications',
116
+ },
117
+ projects: {
118
+ find: 'find-projects', show: 'show-project', save: 'save-project', unsave: 'unsave-project',
119
+ invitations: 'invitations', applications: 'applications',
120
+ },
121
+ usage: { run: 'ai-usage', report: 'report', share: 'share' },
122
+ account: { login: 'login', logout: 'logout', register: 'register', config: 'config' },
123
+ certs: { list: 'certifications', certify: 'certify' },
124
+ };
125
+
126
+ // `shakers <family>` with no sub: obvious default, else the family help.
127
+ const FAMILY_DEFAULTS = { usage: 'ai-usage', profile: 'me', certs: 'certifications' };
128
+
129
+ function familyHelpText(family, lang) {
130
+ const map = FAMILIES[family] || {};
131
+ const subs = Object.keys(map);
132
+ // Show `shakers <family> <sub>` next to the flat alias it maps to, so both forms are discoverable.
133
+ const lines = subs.map((s) => ` shakers ${family} ${s} (alias: shakers ${map[s]})`).join('\n');
134
+ if (lang === 'es') {
135
+ return `\n Familia "${family}" — subcomandos (los comandos planos entre paréntesis siguen valiendo):\n${lines}\n\n Ejecuta \`shakers ${family} <subcomando> --help\` para las opciones.\n\n`;
136
+ }
137
+ return `\n "${family}" family — subcommands (the flat commands in parentheses still work):\n${lines}\n\n Run \`shakers ${family} <subcommand> --help\` for the options.\n\n`;
138
+ }
139
+
140
+ // Resolves a family + sub into a flat command, mutating nothing the flat path relies on.
141
+ function resolveFamily(command, args) {
142
+ const family = FAMILIES[command];
143
+ if (!family) return { command, args };
144
+ const subToken = args.find((a) => !a.startsWith('-'));
145
+ const sub = subToken ? subToken.toLowerCase() : null;
146
+ if (sub && family[sub]) {
147
+ return { command: family[sub], args: args.filter((_, i) => i !== args.indexOf(subToken)) };
148
+ }
149
+ if (!sub && FAMILY_DEFAULTS[command]) return { command: FAMILY_DEFAULTS[command], args };
150
+ // A pure family (not also a flat handler) with no valid sub → signal "show family help".
151
+ if (!HANDLERS[command]) return { command, args, familyHelp: true };
152
+ return { command, args };
153
+ }
154
+
155
+ async function remindAccountForAiUsage(lang, args) {
156
+ if (!process.stdin.isTTY || args.includes('--json')) return;
157
+ const r = getCatalog(lang).cli.accountReminder;
158
+ const ask = createStdinAsk();
159
+ let choice;
160
+ try {
161
+ choice = await promptSelect({
162
+ ask,
163
+ stdinIsTTY: true,
164
+ out: (line) => process.stdout.write(` ${line}\n`),
165
+ header: r.question,
166
+ hint: r.hint,
167
+ items: [{ id: 'yes', label: r.yes }, { id: 'no', label: r.no }],
168
+ labelFor: (it) => it.label,
169
+ });
170
+ } finally {
171
+ ask.close();
172
+ }
173
+ if (choice && choice.id === 'yes') {
174
+ await runLogin(['--lang', lang]);
175
+ }
176
+ }
177
+
178
+ function usageText(lang, { hideOnboarding = false } = {}) {
179
+ const title = VERSION ? `shakers v${VERSION}` : 'shakers';
180
+ if (lang === 'es') {
181
+ const onboardingLine = hideOnboarding ? '' : ` onboarding La entrevista de onboarding (por voz/texto, LiveKit)\n`;
182
+ return `\n ${title} — evaluación de uso de IA y skills\n\n`
183
+ + ` Uso: shakers <comando> [opciones]\n\n`
184
+ + ` Público (sin sesión):\n`
185
+ + ` ai-usage Escanea este proyecto + tu máquina y puntúa tu setup de IA\n`
186
+ + ` register Registro completo: perfil, precio, uso de IA y entrevista\n`
187
+ + ` mcp Servidor MCP local (stdio) para apps de IA de escritorio\n\n`
188
+ + ` Requieren sesión (ejecuta antes: shakers login):\n`
189
+ + onboardingLine
190
+ + ` certify Certifica una dimensión de tu rol (entrevista por LiveKit)\n`
191
+ + ` add-skill Declara Skills detectadas en tu perfil\n`
192
+ + ` add-agent Declara un agente detectado en tu perfil\n`
193
+ + ` add-project Añade este proyecto a tu portfolio\n`
194
+ + ` add-role Añade un rol a tu perfil (rol de crecimiento)\n`
195
+ + ` change-role Cambia tu rol principal por uno de los que ya tienes\n`
196
+ + ` find-projects Lista los proyectos/posiciones disponibles para ti\n`
197
+ + ` show-project Detalle de un proyecto/posición por id\n`
198
+ + ` save-project/unsave-project Guarda o quita un proyecto de tus guardados\n`
199
+ + ` invitations Tus invitaciones a proyectos\n`
200
+ + ` applications Proyectos a los que has aplicado + estado\n`
201
+ + ` availability Ver o fijar tu disponibilidad\n`
202
+ + ` certifications Tus dimensiones certificadas/sin certificar\n`
203
+ + ` me Tu resumen de perfil\n`
204
+ + ` rate Ver o fijar tu tarifa por proyecto\n`
205
+ + ` lang Ver o fijar tus idiomas y nivel\n`
206
+ + ` socials Ver o fijar tus enlaces sociales\n`
207
+ + ` experiences Tus experiencias laborales\n`
208
+ + ` portfolios Tus piezas de portfolio\n`
209
+ + ` alma Mini-shell interactivo para conversar con Alma\n`
210
+ + ` report Abre el informe HTML completo y compartible\n`
211
+ + ` share Tarjeta de uso con marca para LinkedIn\n\n`
212
+ + ` Sesión:\n`
213
+ + ` login Inicia sesión en tu cuenta de Shakers\n`
214
+ + ` logout Cierra la sesión\n`
215
+ + ` config Lee/escribe la configuración (bases de hub/certs)\n\n`
216
+ + ` Familias (agrupan los comandos de arriba; los nombres planos siguen valiendo como alias):\n`
217
+ + ` add <skill|agent|project|role> · role <add|change>\n`
218
+ + ` profile <show|rate|languages|socials|experiences|portfolios|availability|certifications>\n`
219
+ + ` projects <find|show|save|unsave|invitations|applications>\n`
220
+ + ` usage <run|report|share> · certs <list|certify> · account <login|logout|register|config>\n`
221
+ + ` Ejecuta \`shakers <familia>\` (sin subcomando) para ver sus subcomandos.\n\n`
222
+ + ` Ejecuta shakers <comando> --help para las opciones de cada comando.\n\n`;
223
+ }
224
+ const onboardingLine = hideOnboarding ? '' : ` onboarding The onboarding interview (over voice/text, LiveKit)\n`;
225
+ return `\n ${title} — AI usage & skills evaluator\n\n`
226
+ + ` Usage: shakers <command> [options]\n\n`
227
+ + ` Public (no session):\n`
228
+ + ` ai-usage Scan this project + machine and score your AI setup\n`
229
+ + ` register Full registration: profile, pricing, AI usage and interview\n`
230
+ + ` mcp Local MCP server (stdio) for desktop AI apps\n\n`
231
+ + ` Require a session (run \`shakers login\` first):\n`
232
+ + onboardingLine
233
+ + ` certify Certify a dimension of your role (LiveKit interview)\n`
234
+ + ` add-skill Declare detected Skills on your profile\n`
235
+ + ` add-agent Declare a detected agent on your profile\n`
236
+ + ` add-project Add this project to your portfolio\n`
237
+ + ` add-role Add a role to your profile (a growth role)\n`
238
+ + ` change-role Switch your main role to one you already have\n`
239
+ + ` find-projects List the Projects/Positions available to you\n`
240
+ + ` show-project Detail of a project/position by id\n`
241
+ + ` save-project/unsave-project Save or remove a project from your saved list\n`
242
+ + ` invitations Your project invitations\n`
243
+ + ` applications Projects you applied to + status\n`
244
+ + ` availability View or set your availability\n`
245
+ + ` certifications Your certified/uncertified dimensions\n`
246
+ + ` me Your profile summary\n`
247
+ + ` rate View or set your per-project rate\n`
248
+ + ` lang View or set your languages and level\n`
249
+ + ` socials View or set your social links\n`
250
+ + ` experiences Your work experiences\n`
251
+ + ` portfolios Your portfolio pieces\n`
252
+ + ` alma Interactive mini-shell to converse with Alma\n`
253
+ + ` report Open the full shareable HTML report\n`
254
+ + ` share Branded usage card for LinkedIn\n\n`
255
+ + ` Session:\n`
256
+ + ` login Sign in to your Shakers account\n`
257
+ + ` logout Sign out\n`
258
+ + ` config Read/write configuration (hub/certs bases)\n\n`
259
+ + ` Families (group the commands above; the flat names still work as aliases):\n`
260
+ + ` add <skill|agent|project|role> · role <add|change>\n`
261
+ + ` profile <show|rate|languages|socials|experiences|portfolios|availability|certifications>\n`
262
+ + ` projects <find|show|save|unsave|invitations|applications>\n`
263
+ + ` usage <run|report|share> · certs <list|certify> · account <login|logout|register|config>\n`
264
+ + ` Run \`shakers <family>\` (no subcommand) to list its subcommands.\n\n`
265
+ + ` Run \`shakers <command> --help\` for a command's options.\n\n`;
266
+ }
267
+
268
+ function gateText(command, lang) {
269
+ if (lang === 'es') {
270
+ return `\n "${command}" requiere una sesión activa.\n`
271
+ + ` Inicia sesión: shakers login\n`
272
+ + ` ¿Sin cuenta?: shakers register\n\n`;
273
+ }
274
+ return `\n "${command}" requires an active session.\n`
275
+ + ` Sign in: shakers login\n`
276
+ + ` No account?: shakers register\n\n`;
277
+ }
278
+
279
+ function unknownText(command, lang) {
280
+ if (lang === 'es') {
281
+ return `\n Comando desconocido: "${command}".\n`
282
+ + ` Ejecuta shakers para ver los comandos disponibles.\n\n`;
283
+ }
284
+ return `\n Unknown command: "${command}".\n`
285
+ + ` Run \`shakers\` to see the available commands.\n\n`;
286
+ }
287
+
288
+ async function main() {
289
+ const argv = process.argv.slice(2);
290
+ const lang = parseLang(argv) || detectReportLang();
291
+
292
+ migrateLegacyConfigDir(process.env);
293
+ ensureLocalConfigDefaults(process.env);
294
+ // Mint a fresh hub JWT from the stored cookie once per command (email sessions only; a no-op for Google/legacy or when no session exists).
295
+ try { await ensureFreshSession(process.env); } catch { /* never blocks a command */ }
296
+
297
+ const token = argv.find((a) => !a.startsWith('-'));
298
+ if (!token || token.toLowerCase() === 'help') {
299
+ let hideOnboarding = false;
300
+ try {
301
+ hideOnboarding = await checkOnboardingCompleted({ timeoutMs: 3000 });
302
+ } catch {
303
+ hideOnboarding = false;
304
+ }
305
+ process.stdout.write(usageText(lang, { hideOnboarding }));
306
+ return;
307
+ }
308
+
309
+ const rawCommand = token.toLowerCase();
310
+ const rawArgs = argv.filter((_, i) => i !== argv.indexOf(token));
311
+
312
+ // Family navigation layer (flat commands still work as aliases).
313
+ const resolved = resolveFamily(rawCommand, rawArgs);
314
+ if (resolved.familyHelp) {
315
+ process.stdout.write(familyHelpText(rawCommand, lang));
316
+ return;
317
+ }
318
+ const command = resolved.command;
319
+ const args = resolved.args;
320
+
321
+ const handler = HANDLERS[command];
322
+ if (!handler) {
323
+ process.stderr.write(unknownText(command, lang));
324
+ process.exitCode = 1;
325
+ return;
326
+ }
327
+
328
+ const wantsHelp = args.includes('--help') || args.includes('-h');
329
+ const loggedIn = await hasUsableSession(process.env);
330
+
331
+ if (!wantsHelp && !SESSION_EXEMPT.has(command) && !loggedIn) {
332
+ process.stderr.write(gateText(command, lang));
333
+ process.exitCode = 1;
334
+ return;
335
+ }
336
+
337
+ if (command === 'ai-usage' && !wantsHelp && !loggedIn) {
338
+ await remindAccountForAiUsage(lang, args);
339
+ }
340
+
341
+ await handler(args);
342
+ }
343
+
344
+ main();
package/bin/share.js ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ // `share` — turns the project's AI-usage FOOTPRINT result into a branded card to post on LinkedIn (skill-code-certification).
5
+
6
+ const { detectReportLang, getCatalog } = require('./../src/i18n');
7
+ const { generateShareCard } = require('./../src/share-card');
8
+ const { oscLink } = require('./../src/osc-link');
9
+
10
+ const VALID_LANGS = new Set(['es', 'en']);
11
+
12
+ const SHARE_DISABLED = true;
13
+
14
+ // Minimal, share-specific arg parsing: --root <dir>, --lang es|en, --help.
15
+ function parseShareArgs(argv) {
16
+ const opts = { root: null, lang: null, help: false };
17
+ for (let i = 0; i < argv.length; i++) {
18
+ const a = argv[i];
19
+ if (a === '--root') opts.root = argv[++i];
20
+ else if (a.startsWith('--root=')) opts.root = a.slice('--root='.length);
21
+ else if (a === '--lang') opts.lang = VALID_LANGS.has(argv[++i]) ? argv[i] : null;
22
+ else if (a.startsWith('--lang=')) {
23
+ const value = a.slice('--lang='.length);
24
+ opts.lang = VALID_LANGS.has(value) ? value : null;
25
+ } else if (a === '--help' || a === '-h') opts.help = true;
26
+ }
27
+ return opts;
28
+ }
29
+
30
+ // `ask` is accepted for signature parity with the other REPL commands (the
31
+ // shared nested-stdin reader); `share` is non-interactive, so it isn't used.
32
+ async function run(argv = process.argv.slice(2), { ask } = {}) { // eslint-disable-line no-unused-vars
33
+ const opts = parseShareArgs(argv);
34
+ const lang = opts.lang || detectReportLang();
35
+ const catalog = getCatalog(lang);
36
+ const s = catalog.cli.share;
37
+
38
+ if (SHARE_DISABLED) {
39
+ process.stdout.write(`\n ${s.disabled}\n\n`);
40
+ return;
41
+ }
42
+
43
+ if (opts.help) {
44
+ process.stdout.write(`\n ${s.help}\n\n`);
45
+ return;
46
+ }
47
+
48
+ let result;
49
+ try {
50
+ result = generateShareCard({ root: opts.root });
51
+ } catch {
52
+ // Never crash the shell over a failed card write.
53
+ process.stdout.write(`\n ${s.error}\n\n`);
54
+ return;
55
+ }
56
+
57
+ if (!result.ok) {
58
+ process.stdout.write(`\n ${s.noFootprint}\n\n`);
59
+ return;
60
+ }
61
+
62
+ // OSC 8: clickable file:// link to the card in iTerm2 &c.; plain URL elsewhere.
63
+ process.stdout.write(`\n ${s.ready(oscLink(result.fileUrl))}\n`);
64
+ process.stdout.write(` ${s.hint}\n\n`);
65
+ }
66
+
67
+ module.exports = { run, parseShareArgs };
68
+
69
+ // Only auto-run when executed directly (guarded so the REPL can require() it).
70
+ if (require.main === module) {
71
+ run();
72
+ }
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ // `show-project` — detail of one Position/Project (src/show-project-flow.js).
5
+
6
+ const { detectReportLang, getCatalog } = require('../src/i18n');
7
+ const { loadAuthSession, sessionStatus } = require('../src/auth-session-store');
8
+ const { createStdinAsk } = require('../src/stdin-ask');
9
+ const { makeIo } = require('./register');
10
+ const { makeRevealSink } = require('../src/reveal-output');
11
+ const { runShowProject, makeShowProjectDeps } = require('../src/show-project-flow');
12
+
13
+ function parseShowProjectArgs(argv = []) {
14
+ const o = { lang: null, help: false, json: false, id: null };
15
+ for (let i = 0; i < argv.length; i++) {
16
+ const a = argv[i];
17
+ if (a === '--help' || a === '-h') o.help = true;
18
+ else if (a === '--lang') { const v = argv[++i]; o.lang = v === 'es' || v === 'en' ? v : null; }
19
+ else if (a.startsWith('--lang=')) { const v = a.slice(7); o.lang = v === 'es' || v === 'en' ? v : null; }
20
+ else if (a === '--json') o.json = true;
21
+ else if (a === '--id') o.id = argv[++i] != null ? argv[i] : null;
22
+ else if (a.startsWith('--id=')) o.id = a.slice('--id='.length);
23
+ else if (!a.startsWith('-') && o.id == null) o.id = a; // positional <id>
24
+ }
25
+ return o;
26
+ }
27
+
28
+ async function run(
29
+ argv = process.argv.slice(2),
30
+ { ask: injectedAsk = null, stdinIsTTY: ttyOverride = undefined, deps = null, session: injectedSession = null, out = null } = {},
31
+ ) {
32
+ const opts = parseShowProjectArgs(argv);
33
+ const lang = opts.lang || detectReportLang();
34
+ const catalog = getCatalog(lang);
35
+ const fp = catalog.showProject;
36
+ const write = out || ((s) => process.stdout.write(s));
37
+
38
+ if (opts.help) {
39
+ write(fp.help + '\n');
40
+ return;
41
+ }
42
+
43
+ const session = injectedSession || loadAuthSession();
44
+ if (sessionStatus(session) !== 'active') {
45
+ process.stderr.write(`\n ${fp.loginRequired}\n\n`);
46
+ process.exitCode = 1;
47
+ return;
48
+ }
49
+
50
+ const stdinIsTTY = ttyOverride !== undefined ? ttyOverride : !!process.stdin.isTTY;
51
+ const ask = injectedAsk || createStdinAsk();
52
+ const sink = makeRevealSink({ json: opts.json, interactive: false, out, env: process.env });
53
+ try {
54
+ const io = makeIo({ ask, lang, out: sink.write, stdinIsTTY });
55
+ const result = await runShowProject({
56
+ io, rawOut: sink.write, lang, session, catalog,
57
+ opts: { id: opts.id, json: opts.json },
58
+ deps: deps || makeShowProjectDeps(),
59
+ });
60
+ await sink.finish();
61
+ if (result && result.ok === false) process.exitCode = 1;
62
+ return result;
63
+ } finally {
64
+ if (!injectedAsk) ask.close();
65
+ }
66
+ }
67
+
68
+ module.exports = { run, parseShowProjectArgs };
69
+
70
+ if (require.main === module) {
71
+ run();
72
+ }
package/bin/socials.js ADDED
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ // `socials` — view or set the talent's social links (src/socials-flow.js).
5
+
6
+ const { detectReportLang, getCatalog } = require('../src/i18n');
7
+ const { loadAuthSession, sessionStatus } = require('../src/auth-session-store');
8
+ const { createStdinAsk } = require('../src/stdin-ask');
9
+ const { makeIo } = require('./register');
10
+ const { makeRevealSink } = require('../src/reveal-output');
11
+ const { runSocials, makeSocialsDeps } = require('../src/socials-flow');
12
+
13
+ function parseSocialsArgs(argv = []) {
14
+ const o = { lang: null, help: false, json: false, set: false };
15
+ for (let i = 0; i < argv.length; i++) {
16
+ const a = argv[i];
17
+ if (a === '--help' || a === '-h') o.help = true;
18
+ else if (a === '--lang') { const v = argv[++i]; o.lang = v === 'es' || v === 'en' ? v : null; }
19
+ else if (a.startsWith('--lang=')) { const v = a.slice(7); o.lang = v === 'es' || v === 'en' ? v : null; }
20
+ else if (a === '--json') o.json = true;
21
+ else if (a === '--set') o.set = true;
22
+ }
23
+ return o;
24
+ }
25
+
26
+ async function run(
27
+ argv = process.argv.slice(2),
28
+ { ask: injectedAsk = null, stdinIsTTY: ttyOverride = undefined, deps = null, session: injectedSession = null, out = null } = {},
29
+ ) {
30
+ const opts = parseSocialsArgs(argv);
31
+ const lang = opts.lang || detectReportLang();
32
+ const catalog = getCatalog(lang);
33
+ const fp = catalog.socials;
34
+ const write = out || ((s) => process.stdout.write(s));
35
+
36
+ if (opts.help) {
37
+ write(fp.help + '\n');
38
+ return;
39
+ }
40
+
41
+ const session = injectedSession || loadAuthSession();
42
+ if (sessionStatus(session) !== 'active') {
43
+ process.stderr.write(`\n ${fp.loginRequired}\n\n`);
44
+ process.exitCode = 1;
45
+ return;
46
+ }
47
+
48
+ const stdinIsTTY = ttyOverride !== undefined ? ttyOverride : !!process.stdin.isTTY;
49
+ const ask = injectedAsk || createStdinAsk();
50
+ const sink = makeRevealSink({ json: opts.json, interactive: !!opts.set, out, env: process.env });
51
+ try {
52
+ const io = makeIo({ ask, lang, out: sink.write, stdinIsTTY });
53
+ const result = await runSocials({
54
+ io, rawOut: sink.write, ask, session, catalog,
55
+ opts: { json: opts.json, set: opts.set, stdinIsTTY },
56
+ deps: deps || makeSocialsDeps(),
57
+ });
58
+ await sink.finish();
59
+ if (result && result.ok === false) process.exitCode = 1;
60
+ return result;
61
+ } finally {
62
+ if (!injectedAsk) ask.close();
63
+ }
64
+ }
65
+
66
+ module.exports = { run, parseSocialsArgs };
67
+
68
+ if (require.main === module) {
69
+ run();
70
+ }