shakers-cli 0.16.0 → 0.18.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 (69) hide show
  1. package/README.md +21 -6
  2. package/bin/ai-usage.js +31 -12
  3. package/bin/command-audit-worker.js +19 -0
  4. package/bin/login.js +4 -8
  5. package/bin/mcp.js +32 -21
  6. package/bin/register.js +12 -30
  7. package/bin/shakers-cli.js +15 -10
  8. package/package.json +1 -1
  9. package/src/account-marker.js +40 -0
  10. package/src/account-trace.js +23 -0
  11. package/src/agents-client.js +1 -0
  12. package/src/ai-usage-review.js +103 -0
  13. package/src/auth-client.js +7 -107
  14. package/src/auth-session-store.js +12 -11
  15. package/src/backend-request.js +3 -24
  16. package/src/cli-skill.js +77 -0
  17. package/src/command-audit.js +47 -4
  18. package/src/config.js +2 -16
  19. package/src/consent-flow.js +9 -3
  20. package/src/detect-repos.js +4 -8
  21. package/src/detectors.js +23 -0
  22. package/src/device-auth-store.js +2 -2
  23. package/src/device-login.js +11 -9
  24. package/src/env-paths.js +9 -1
  25. package/src/i18n.js +186 -102
  26. package/src/mcp-ai-usage-result-tool.js +38 -14
  27. package/src/mcp-ai-usage-tool.js +317 -172
  28. package/src/mcp-choice.js +171 -34
  29. package/src/mcp-detector.js +26 -2
  30. package/src/mcp-device-login-tools.js +40 -8
  31. package/src/mcp-install.js +292 -53
  32. package/src/mcp-instructions.js +43 -17
  33. package/src/mcp-onboarding-tools.js +1 -3
  34. package/src/mcp-prompts.js +8 -6
  35. package/src/mcp-register-tools.js +25 -14
  36. package/src/mcp-role-tools.js +59 -18
  37. package/src/mcp-server-name.js +7 -0
  38. package/src/mcp-server.js +35 -7
  39. package/src/mcp-signup-client.js +3 -6
  40. package/src/mcp-signup-flow.js +389 -0
  41. package/src/mcp-signup-tools.js +250 -133
  42. package/src/onboarding-flow.js +72 -146
  43. package/src/onboarding-funnel.js +46 -6
  44. package/src/opencode-sessions.js +92 -0
  45. package/src/profile-agents.js +26 -0
  46. package/src/render-report-text.js +1 -1
  47. package/src/report-gating.js +4 -2
  48. package/src/report-store.js +7 -2
  49. package/src/roles-flow.js +8 -5
  50. package/src/saved-positions-flow.js +3 -3
  51. package/src/scanner.js +104 -12
  52. package/src/session-gate.js +1 -4
  53. package/src/session-normalizers.js +114 -8
  54. package/src/session-refresh.js +5 -16
  55. package/src/session-scan.js +167 -22
  56. package/src/share-consent-state.js +2 -2
  57. package/src/share-payload.js +4 -1
  58. package/src/share.js +2 -2
  59. package/src/signup-client.js +12 -39
  60. package/src/signup-copy.js +545 -58
  61. package/src/skill-install.js +102 -0
  62. package/src/stdin-ask.js +13 -1
  63. package/src/tier-engine.js +14 -2
  64. package/src/trust-narration.js +32 -0
  65. package/src/usage-run-steps.js +68 -6
  66. package/src/usage-scan.js +11 -8
  67. package/src/brand-svg.js +0 -9
  68. package/src/inter-fonts.js +0 -6
  69. package/src/mcp-auth-form.js +0 -480
package/src/i18n.js CHANGED
@@ -15,13 +15,10 @@ const catalogs = {
15
15
  askCv: 'Ruta a tu CV en PDF (opcional, Enter para omitir):',
16
16
  askGithub: 'Tu URL de GitHub (opcional, Enter para omitir):',
17
17
  askWebsite: 'Tu web personal (opcional, Enter para omitir):',
18
- importStarted: 'Importando tu perfil en segundo plano mientras continuamos…',
19
- importError: 'No se pudo iniciar la importacion de tu perfil.',
20
18
  importUnavailable: 'No pudimos importar tu perfil ahora (servicio no disponible); seguimos con el registro, puedes reintentar el import mas tarde.',
21
19
  importChecking: 'Importando tu perfil…',
22
20
  importDone: 'Perfil importado correctamente.',
23
21
  importFailed: 'No se pudo completar la importacion de tu perfil. Puedes reintentarla mas tarde desde tu perfil.',
24
- importStillRunning: 'Tu perfil se sigue importando en segundo plano; estara listo en unos minutos.',
25
22
  professionalTitle: 'Tu situacion laboral',
26
23
  askWorkSituation: 'Cual es tu situacion laboral actual? Elige una opcion:',
27
24
  workSituationLabels: {
@@ -54,27 +51,13 @@ const catalogs = {
54
51
  professionalSaved: 'Situación laboral guardada.',
55
52
  chooseMethodHeading: '¿Cómo quieres crear tu cuenta?',
56
53
  signupTitle: 'Crea tu cuenta de Shakers',
57
- askName: 'Nombre:',
58
- askLastName: 'Apellidos:',
59
- askEmail: 'Email (usa uno NUEVO; si ya existe te ofrecere iniciar sesion):',
60
- askPassword: 'Contrasena (minimo 8 caracteres):',
61
- askPasswordConfirm: 'Repite la contrasena:',
62
- passwordMismatch: 'Las contrasenas no coinciden. Intentalo de nuevo.',
63
- askFreelanceTypeSelect: 'Como trabajas? Elige una opcion:',
64
- freelanceTypeLabels: {
65
- FREELANCE: 'Freelance',
66
- EMPLOYEE: 'Empleado/a',
67
- AGENCY: 'Agencia o empresa',
68
- POTENTIAL_FREELANCE: 'Me planteo ser freelance',
69
- },
70
- invalidChoice: 'Opcion no valida.',
71
- askNewsletter: 'Quieres recibir la newsletter? (s/n):',
72
- weakPassword: 'La contrasena no cumple los requisitos (minimo 8 caracteres).',
54
+ termsAsk: '¿Aceptas, para crear tu cuenta? [s/N]',
55
+ termsDeclined: 'Sin aceptar los Términos y la Política de Privacidad no se puede crear tu cuenta. No se ha creado nada ni se ha enviado nada a Shakers.',
56
+ newsletterAsk: (label) => `Opcional: ${label} [s/N]`,
73
57
  signupError: 'No se pudo crear la cuenta.',
74
58
  freelanceIntentRequired: 'Tu intencion freelance es obligatoria para ese tipo.',
75
- accountExists: 'Ya existe una cuenta con ese email. Vamos a iniciar sesion.',
76
- accountReady: 'Cuenta creada. Sesion iniciada.',
77
- sessionError: 'La cuenta se creo pero no se pudo iniciar sesion. Ejecuta `shakers login`.',
59
+ accountExists: 'Ya tenías una cuenta en Shakers: has iniciado sesión con ella en este equipo.',
60
+ accountReady: 'Cuenta creada. Sesión iniciada solo en este equipo (~/.config/shakers); para cerrarla: `shakers logout`.',
78
61
  pricingTitle: 'Tu precio por proyecto',
79
62
  pricingSaved: 'Precio guardado.',
80
63
  askPricingFullAmount: (cur) => `Precio por proyecto a jornada completa en ${cur} (numero, sin separadores de miles, p.ej. 60000):`,
@@ -149,7 +132,6 @@ const catalogs = {
149
132
  repeatNotFound: 'No tienes un onboarding todavía. Haz `shakers onboarding` primero.',
150
133
  repeatAuthError: 'Tu sesión no permite esta acción. Inicia sesión de nuevo: shakers login',
151
134
  repeatFailed: (reason) => `No se pudo reiniciar el onboarding (${reason}). Inténtalo de nuevo.`,
152
- interviewAnswer: 'Tu respuesta:',
153
135
  questionHeading: (n) => `Pregunta ${n}`,
154
136
  interviewTurnError: 'No se pudo continuar la entrevista.',
155
137
  thinking: 'Alma esta pensando…',
@@ -207,7 +189,7 @@ const catalogs = {
207
189
  t2Met: (n) => `Dispones de al menos un fichero de contexto persistente — instrucciones, configuración o reglas — para alguna herramienta (tienes ${n}).`,
208
190
  t3Met: (n) => `Tienes al menos un servidor MCP conectado, dando a la IA acceso a datos o herramientas externas (tienes ${n}).`,
209
191
  t4Met: (n) => `Has creado activos propios — skills, comandos o reglas personalizadas — más allá de la configuración por defecto (tienes ${n}).`,
210
- t5Met: (hasAgentic, mcp, custom) => `Operas con una CLI agéntica (Claude Code, Aider, Gemini CLI, Codex CLI o Amazon Q Developer) combinada con MCP y activos propios (${mcp} servidores MCP y ${custom} activos propios).`,
192
+ t5Met: (hasAgentic, mcp, custom) => `Operas con una CLI agéntica (Claude Code, Aider, Gemini CLI, Codex CLI, Pi, OpenCode o Amazon Q Developer) combinada con MCP y activos propios (${mcp} servidores MCP y ${custom} activos propios).`,
211
193
  t6Met: (n) => `Tienes un equipo de al menos 2 agentes especializados definidos (tienes ${n}).`,
212
194
  t7Met: (n) => `Tienes automatización basada en hooks configurada (tienes ${n}).`,
213
195
  t1Blocking: (n) => `Para subir a T1 (Primera herramienta) necesitas al menos una herramienta de IA detectada — ahora tienes ${n}.`,
@@ -216,7 +198,7 @@ const catalogs = {
216
198
  t4Blocking: (n) => `Para subir a T4 (Herramienta propia) necesitas crear al menos un activo propio — skill, comando o regla — más allá de la configuración por defecto — ahora tienes ${n}.`,
217
199
  t5Blocking: (hasAgentic, mcp, custom) => {
218
200
  const missing = [];
219
- if (!hasAgentic) missing.push('una CLI agéntica (Claude Code, Aider, Gemini CLI, Codex CLI o Amazon Q Developer)');
201
+ if (!hasAgentic) missing.push('una CLI agéntica (Claude Code, Aider, Gemini CLI, Codex CLI, Pi, OpenCode o Amazon Q Developer)');
220
202
  if (mcp < 1) missing.push('al menos 1 servidor MCP');
221
203
  if (custom < 1) missing.push('al menos 1 activo propio (skill, comando o regla)');
222
204
  return `Para subir a T5 (Operador agéntico) te falta: ${missing.join('; ')}.`;
@@ -450,7 +432,7 @@ const catalogs = {
450
432
  // Shown only when the 4 prose gaps below were actually replaced by a validated, project-adapted response — never on fallback to the curated content.
451
433
  roadmapPersonalizedNotice: 'Contenido adaptado a tu proyecto.',
452
434
  // Los dos avisos que faltaban, hermanos del `agentsEvalMissing` que añadió la 106.
453
- roadmapNotPersonalizedNotice: 'Este roadmap es el genérico de tu tier: esta vez no se ha podido adaptar a tu proyecto. Vuelve a ejecutar `usage` para intentarlo otra vez.',
435
+ roadmapNotPersonalizedNotice: 'Este roadmap es el genérico de tu tier: esta vez no se ha podido adaptar a tu proyecto. Vuelve a ejecutar `usage --roadmap` para intentarlo otra vez.',
454
436
  // El antes y el después del roadmap.
455
437
  roadmapNowLabel: 'De dónde partes',
456
438
  roadmapProjectionLabel: 'Dónde te deja esta ruta',
@@ -536,9 +518,9 @@ const catalogs = {
536
518
  'Esto mide tu fluidez con IA en dos ejes: Setup (madurez de tu tooling, T0–T7, determinista) y Usage (cómo lo usas, evaluado en entrevista + código). Te sitúa en el mapa 3×3 (Explorer → AI Native). Completarlo posiciona tu perfil agéntico para los proyectos que valoran cómo trabajas con IA. Por defecto leo el uso de IA de todo este equipo y asumo que es tuyo (un equipo = tú).',
537
519
  // Terminal progress feedback: stderr-only status
538
520
  // during the two slow phases (see src/terminal-progress.js).
539
- scanningLabel: 'Escaneando entorno y detectores…',
521
+ scanningLabel: 'Leyendo en este equipo la configuración de tus herramientas de IA y tus proyectos (todavía no sale nada de tu equipo)…',
540
522
  // Loader de la fase de agregación por repo seleccionado (git + sesiones + steering/decisiones + evidencia + org-chart de agentes).
541
- aggregatingLabel: 'Agregando señales de los repos seleccionados…',
523
+ aggregatingLabel: 'Leyendo el historial de git y de sesiones de IA de los repos elegidos para sacar señales (todavía no sale nada de tu equipo)…',
542
524
  reposScopePromptHeader: '¿Qué quieres evaluar?',
543
525
  reposScopePromptHint: 'Flechas para moverte · enter para elegir',
544
526
  reposScopeOptionMachine: 'Toda la máquina (todos los repos con actividad de IA)',
@@ -558,6 +540,7 @@ const catalogs = {
558
540
  // Roadmap personalization: reuses the
559
541
  // same spinner mechanism as synthesizingLabel above.
560
542
  personalizingRoadmapLabel: 'Personalizando roadmap…',
543
+ roadmapNotForAccounts: 'El roadmap no está disponible para cuentas de Shakers: tu nivel sale de certificarte.',
561
544
  // Agent definition-quality evaluation (ephemeral LLM call).
562
545
  evaluatingAgentsLabel: 'Evaluando la calidad de tus agentes…',
563
546
  // "Construir el siguiente nivel ahora": now a SECONDARY, opt-in alternative — the copyable implementation prompt (below) is the PRIMARY "how do I implement this" path.
@@ -575,7 +558,7 @@ const catalogs = {
575
558
  + ' --repo Evalúa solo el repo actual\n'
576
559
  + ' --scope machine|repo Igual que --machine / --repo (salta el selector)\n'
577
560
  + ' --all-repos Alias de --machine\n'
578
- + ' --roadmap Muestra los pasos para subir tu nivel de uso de IA (oculto por defecto)\n'
561
+ + ' --roadmap Muestra los pasos para subir tu nivel de uso de IA (solo sin cuenta de Shakers)\n'
579
562
  + ' --build-next-level Genera el starter del siguiente tier (alternativa secundaria)\n'
580
563
  + ' --force Junto a --build-next-level, sobrescribe un fichero existente\n'
581
564
  + ' --lang es|en Fuerza el idioma (informe + prompt) en vez de detectarlo del sistema\n'
@@ -620,10 +603,20 @@ const catalogs = {
620
603
  persistIntro:
621
604
  'Este informe se ha generado y mostrado en tu equipo, siempre. '
622
605
  + 'Guardarlo en Shakers es opcional y revocable en cualquier momento '
623
- + '(usage --consent-revoke): guarda tu nivel/tier y señales '
606
+ + '(usage --consent-revoke). Se envían tu nivel/tier, señales '
624
607
  + 'estructuradas derivadas (herramientas, MCP, memoria, '
625
- + 'automatizaciones, agentes, tecnologías) — nunca el contenido de '
626
- + 'tus ficheros, prompts, rutas ni credenciales. '
608
+ + 'automatizaciones, agentes, tecnologías) y texto literal: '
609
+ + 'fragmentos de tus conversaciones con asistentes de IA (las de los '
610
+ + 'repositorios analizados, o todas las de este equipo si lo analizas '
611
+ + 'entero), comandos de muestra y las definiciones de tus agentes, '
612
+ + 'tras quitar lo que parezca un secreto. Antes de cada subida verás '
613
+ + 'qué se envía. Un '
614
+ + 'modelo de IA de un proveedor externo (OpenAI, Google o Anthropic) '
615
+ + 'lo analiza por encargo de Shakers para calcular tu evaluación y '
616
+ + 'lo descarta: solo se guarda el resultado (niveles, puntuaciones, '
617
+ + 'recuentos y los nombres de tus herramientas y agentes). No lo uses '
618
+ + 'en equipos o conversaciones con datos que no puedas compartir (de clientes, '
619
+ + 'de tu empresa o de terceros). '
627
620
  + 'Esta misma decisión también autoriza, solo si aceptas, que un '
628
621
  + 'proveedor externo de observabilidad (Datadog) capture el contenido '
629
622
  + 'que envían las llamadas de IA opcionales de este CLI (código '
@@ -647,7 +640,7 @@ const catalogs = {
647
640
  // Esta pregunta se pide ahora ANTES de cualquier llamada de IA/egress (agent-synthesis, agent-evaluation, personalización de roadmap), no después.
648
641
  noReportWithoutConsent: 'Sin tu consentimiento, este informe no incluirá síntesis, evaluación ni roadmap generados por IA esta vez (el resto del informe se muestra igual). Vuelve a ejecutar `usage` y acepta para incluirlos, o usa --consent-reset si quieres que se te pregunte de nuevo.',
649
642
  deniedSaved: 'Entendido, no se guardará nada. Puedes cambiar de opinión más adelante volviendo a ejecutar el comando.',
650
- grantedSaved: (email) => `Gracias. A partir de ahora este informe se guardará automáticamente en Shakers (correo: ${email}, máx. 1 vez por hora).`,
643
+ grantedSaved: (email) => `Gracias. Antes de subir nada a Shakers te enseñaré lo que se enviaría y solo lo subiré si lo apruebas (correo: ${email}, máx. 1 vez por hora).`,
651
644
  // Un talento logueado NUNCA ve el prompt de email (su cuenta ya prueba su identidad).
652
645
  noSessionEmail: 'No hay un correo asociado a tu sesión todavía, así que no se puede guardar el informe esta vez. Se te preguntará de nuevo la próxima ejecución.',
653
646
  skipAlreadyDecided: (decision, path) =>
@@ -656,6 +649,9 @@ const catalogs = {
656
649
  nonInteractiveWarning:
657
650
  'Entrada no interactiva (no-TTY) detectada: si no llega ninguna respuesta por stdin, '
658
651
  + 'el consentimiento no se guardará esta vez y se te volverá a preguntar la próxima vez.',
652
+ nonInteractiveUnanswered:
653
+ 'No ha llegado ninguna respuesta, así que tu consentimiento sigue sin decidir y no se ha guardado ni enviado nada a Shakers. '
654
+ + 'Ejecuta `shakers ai-usage` en tu propia terminal para responder.',
659
655
  status: {
660
656
  heading: 'Estado del consentimiento (guardado en Shakers)',
661
657
  decisionGranted: 'Decisión: concedido (granted)',
@@ -972,12 +968,13 @@ const catalogs = {
972
968
  },
973
969
  mcpInstall: {
974
970
  title: 'Conectando el MCP de Shakers',
975
- clients: { claudeDesktop: 'Claude Desktop', claudeCode: 'Claude Code', cursor: 'Cursor', geminiCli: 'Gemini CLI', codex: 'Codex (y ChatGPT en modo Codex)', vscode: 'VS Code', windsurf: 'Windsurf' },
971
+ clients: { claudeDesktop: 'Claude Desktop', claudeCode: 'Claude Code', cursor: 'Cursor', geminiCli: 'Gemini CLI', codex: 'Codex / ChatGPT', vscode: 'VS Code', windsurf: 'Windsurf' },
976
972
  status: {
977
973
  configured: 'conectado',
978
974
  unchanged: 'ya estaba conectado',
979
975
  'not-found': 'no instalado en esta máquina',
980
- 'invalid-json': 'su configuración no es JSON válido; no la he tocado',
976
+ 'invalid-config': 'no puedo editar su configuración con seguridad; no la he tocado',
977
+ manual: 'ejecuta este comando en tu terminal:',
981
978
  failed: 'no se pudo conectar',
982
979
  },
983
980
  removedLegacy: 'Sustituye a la entrada antigua "shakers-ai-usage".',
@@ -985,10 +982,27 @@ const catalogs = {
985
982
  confirm: '¿Conecto Shakers a ellas para que puedas usarlo desde tu IA? [S/n]',
986
983
  declined: 'No he tocado nada. Para conectarlo más tarde, vuelve a ejecutar el instalador.',
987
984
  noTerminal: 'No hay una terminal donde preguntarte, así que no he tocado nada. Vuelve a ejecutar el instalador en tu terminal para conectarlo.',
988
- restart: 'Reinicia las apps que estén abiertas para que carguen Shakers.',
985
+ restart: (apps) => `Reinicia ${apps} (o recarga sus servidores MCP) para que aparezcan las herramientas de Shakers.`,
986
+ alreadyConnected: 'Shakers ya estaba conectado. Si no ves sus herramientas, reinicia la app o recarga sus servidores MCP.',
987
+ yesHint: 'Para conectarlas sin preguntar (por ejemplo, desde un asistente de IA): shakers mcp install --yes',
988
+ help: 'mcp install — conecta Shakers a las apps de IA de esta máquina (Claude Desktop, Claude Code, Cursor, Gemini CLI, Codex / ChatGPT, VS Code, Windsurf).\n\nUso:\n shakers mcp install pregunta en la terminal y conecta las apps encontradas\n shakers mcp install --yes conecta sin preguntar (-y; para asistentes de IA y scripts)\n shakers mcp install --json imprime el resultado como un único objeto JSON\n\nSin --yes y sin terminal no toca nada. SHAKERS_MCP_INSTALL=1 equivale a --yes.\nAl terminar, reinicia las apps que indique (o recarga sus servidores MCP) para que aparezcan las herramientas de Shakers.',
989
989
  noneFound: 'No he encontrado ninguna app de IA compatible en esta máquina.',
990
990
  failedAll: 'No he podido conectar Shakers a tus apps de IA. Vuelve a ejecutar el instalador para reintentarlo.',
991
991
  },
992
+ skillInstall: {
993
+ title: 'Instalando la skill shakers-cli para tus agentes de IA',
994
+ agents: { claudeCode: 'Claude Code', codex: 'Codex' },
995
+ status: { installed: 'instalada', failed: 'no se pudo instalar' },
996
+ found: (agents) => `He encontrado: ${agents}.`,
997
+ confirm: '¿Instalo la skill para que tu agente sepa usar el CLI de Shakers? [S/n]',
998
+ declined: 'No he tocado nada. Para instalarla más tarde: shakers skill install',
999
+ noTerminal: 'No hay una terminal donde preguntarte, así que no he tocado nada.',
1000
+ yesHint: 'Para instalarla sin preguntar (por ejemplo, desde un asistente de IA): shakers skill install --yes',
1001
+ noneFound: 'No he encontrado Claude Code ni Codex en esta máquina; no he tocado nada.',
1002
+ done: (agents) => `Listo. Si ${agents} no ve la skill, abre una sesión nueva.`,
1003
+ failedAll: 'No he podido instalar la skill. Vuelve a ejecutar shakers skill install para reintentarlo.',
1004
+ help: 'skill install — instala la skill shakers-cli, que enseña a tu agente de IA (Claude Code, Codex) a usar este CLI.\n\nUso:\n shakers skill install pregunta en la terminal e instala en los agentes encontrados\n shakers skill install --yes instala sin preguntar (-y; para asistentes de IA y scripts)\n shakers skill install --json imprime el resultado como un único objeto JSON\n\nSin --yes y sin terminal no toca nada. Solo escribe ~/.claude/skills/shakers-cli/SKILL.md y <CODEX_HOME>/skills/shakers-cli/SKILL.md.',
1005
+ },
992
1006
  rate: {
993
1007
  help: 'rate — ver o fijar tu tarifa por proyecto.\n\nUso:\n rate [--lang es|en] [--json] ver tu tarifa\n rate --set [--lang es|en] fijarla (interactivo, con confirmación)\n\nRequiere una sesión activa (shakers login).',
994
1008
  loginRequired: '"rate" requiere una sesión activa.\n Falta: iniciar sesión. Ejecuta primero: shakers login',
@@ -1213,6 +1227,46 @@ const catalogs = {
1213
1227
  remediationCopiedLabel: 'Copiado ✓',
1214
1228
  },
1215
1229
  },
1230
+ trust: {
1231
+ uploadWarning: 'Antes de aprobar: se enviarían hasta 40 extractos literales de tus mensajes (correcciones y decisiones) y 20 comandos, de 200 caracteres como máximo cada uno, la configuración y la definición de tus agentes y recuentos de actividad. Se quita lo que parece un secreto, un email o una ruta, pero el filtro es aproximado: no lo apruebes si hay datos que no puedes compartir (de clientes, de tu empresa o de terceros).',
1232
+ cli: {
1233
+ 'ai-usage': { before: 'Voy a analizar en este ordenador tus herramientas de IA, el historial de tus proyectos de código y tus conversaciones con asistentes de programación con IA (Claude Code, Codex, Gemini, Cursor, Continue, Cline/Roo, Aider, Pi, OpenCode). Si analizas un repo, solo cubre las conversaciones de ese repo; si eliges toda la máquina o es tu alta, todas las de este equipo. Antes de enviar nada a Shakers te enseño qué se enviaría y te pregunto. No lo uses en un equipo ni con conversaciones que tengan datos que no puedes compartir (de clientes, de tu empresa o de terceros).', after: 'Hecho. Si aprobaste la subida, un modelo de IA de un proveedor externo (OpenAI, Google o Anthropic) analiza lo enviado por encargo de Shakers para calcular tu evaluación y después se descarta; solo se guarda el resultado (niveles, puntuaciones, recuentos y los nombres de tus herramientas y agentes). Si no la aprobaste, no se ha subido nada a tu perfil. Revisión humana: «Solicitar revisión humana» en la web o talent@shakersworks.com; para pedir que se borre lo enviado, el mismo correo. Para dejar de enviar desde este equipo: `shakers ai-usage --consent-revoke`.' },
1234
+ certify: { before: 'Voy a preparar la certificación de tu rol principal con Shakers: te explico cada paso y te pregunto antes de enviar nada.', after: 'Hecho. Lo enviado queda en tu cuenta de Shakers.' },
1235
+ share: { before: 'Voy a crear en este ordenador una tarjeta con tu informe de IA para compartir. No envío nada.', after: 'Hecho. La tarjeta está en este ordenador; no se ha enviado nada.' },
1236
+ report: { before: 'Voy a leer tu informe de IA (de Shakers o de tu último análisis en este ordenador) y a guardarlo como archivo en este ordenador. No cambio tu perfil.' },
1237
+ 'add-skill': { before: 'Voy a leer las skills de tu último análisis en Shakers y a añadir a tu perfil las que elijas.', after: 'Hecho. Tu perfil de Shakers tiene las skills que elegiste.' },
1238
+ 'add-agent': { before: 'Voy a leer los agentes de tu último análisis en Shakers y a añadir a tu perfil el que elijas, con la descripción que apruebes.', after: 'Hecho. Tu perfil de Shakers tiene el agente que elegiste.' },
1239
+ 'add-project': { before: 'Voy a leer el nombre de esta carpeta, la dirección web de su código y las fechas de su primer y último cambio, y te pregunto antes de enviarlos a tu portfolio de Shakers. No envío tu código.', after: 'Hecho. El proyecto está en tu portfolio de Shakers.' },
1240
+ 'add-role': { before: 'Voy a leer los roles de Shakers y a añadir a tu perfil el que elijas.', after: 'Hecho. Tu perfil de Shakers tiene el rol que elegiste.' },
1241
+ 'change-role': { before: 'Voy a leer tus roles de Shakers y a guardar como principal el que elijas.', after: 'Hecho. Tu rol principal está guardado en Shakers.' },
1242
+ 'find-projects': { before: 'Solo leo de Shakers los proyectos que encajan contigo; no cambio nada.' },
1243
+ 'show-project': { before: 'Solo leo de Shakers el detalle del proyecto; no cambio nada.' },
1244
+ 'save-project': { before: 'Voy a guardar este proyecto en tus proyectos guardados de Shakers.', after: 'Hecho. El proyecto está en tus guardados.' },
1245
+ 'unsave-project': { before: 'Voy a quitar este proyecto de tus proyectos guardados de Shakers.', after: 'Hecho. El proyecto ya no está en tus guardados.' },
1246
+ invitations: { before: 'Solo leo tus invitaciones de Shakers; no cambio nada.' },
1247
+ applications: { before: 'Solo leo tus candidaturas de Shakers; no cambio nada.' },
1248
+ availability: { before: 'Solo leo tu disponibilidad de tu perfil de Shakers; no cambio nada.' },
1249
+ 'availability:set': { before: 'Voy a cambiar tu disponibilidad en tu perfil de Shakers.', after: 'Hecho. Tu disponibilidad está guardada en Shakers.' },
1250
+ certifications: { before: 'Solo leo tus certificaciones de Shakers; no cambio nada.' },
1251
+ me: { before: 'Solo leo tu perfil de Shakers; no cambio nada.' },
1252
+ rate: { before: 'Solo leo tu tarifa de tu perfil de Shakers; no cambio nada.' },
1253
+ 'rate:set': { before: 'Voy a cambiar tu tarifa en tu perfil de Shakers.', after: 'Hecho. Tu tarifa está guardada en Shakers.' },
1254
+ lang: { before: 'Solo leo tus idiomas de tu perfil de Shakers; no cambio nada.' },
1255
+ 'lang:set': { before: 'Voy a cambiar tus idiomas en tu perfil de Shakers.', after: 'Hecho. Tus idiomas están guardados en Shakers.' },
1256
+ socials: { before: 'Solo leo tus redes de tu perfil de Shakers; no cambio nada.' },
1257
+ 'socials:set': { before: 'Voy a cambiar tus redes en tu perfil de Shakers.', after: 'Hecho. Tus redes están guardadas en Shakers.' },
1258
+ experiences: { before: 'Solo leo tus experiencias de tu perfil de Shakers; no cambio nada.' },
1259
+ portfolios: { before: 'Solo leo tu portfolio de tu perfil de Shakers; no cambio nada.' },
1260
+ alma: { before: 'Voy a enviar tus mensajes a Alma, la asistente de Shakers. Si propone un cambio en tu perfil, te pregunto antes de hacerlo.' },
1261
+ register: { before: 'Voy a crear tu cuenta de Shakers: te pido aquí que aceptes los Términos y te abro una página de Shakers (shakersworks.com) con un código; tu contraseña solo se escribe allí. Cada paso que envíe datos te lo digo antes.' },
1262
+ onboarding: { before: 'Voy a hacer contigo la entrevista de onboarding: tus respuestas se envían a Shakers para conocer tu perfil. No leo nada de tu ordenador.', after: 'Hecho. Tu entrevista está guardada en Shakers.' },
1263
+ config: { before: 'Trabajo solo con la configuración del CLI en este ordenador (~/.config/shakers); no envío nada a Shakers.' },
1264
+ update: { before: 'Voy a comprobar si hay una versión nueva del CLI y, si la hay, a reemplazar sus archivos instalados en este ordenador. No toco tus proyectos ni tu sesión.' },
1265
+ logout: { before: 'Voy a cerrar tu sesión: aviso a Shakers y borro la sesión y el consentimiento guardados en este ordenador (~/.config/shakers). No cambio tu perfil.' },
1266
+ mcp: { before: 'Voy a añadir el servidor MCP de Shakers a la configuración de tus apps de IA en este ordenador (por ejemplo ~/.cursor/mcp.json o ~/.codex/config.toml). No envío nada a Shakers.', after: 'Hecho. Solo han cambiado los archivos de configuración que se listan arriba.' },
1267
+ skill: { before: 'Voy a escribir la skill del CLI de Shakers en las carpetas de skills de tus agentes en este ordenador (~/.claude/skills o ~/.codex/skills). No envío nada a Shakers.', after: 'Hecho. Solo han cambiado los archivos que se listan arriba.' },
1268
+ },
1269
+ },
1216
1270
  // Branded mini-shell chrome.
1217
1271
  login: {
1218
1272
  intro: 'Inicia sesión con tu email y contraseña de Shakers.',
@@ -1224,28 +1278,14 @@ const catalogs = {
1224
1278
  needInput: 'Se necesitan email y contraseña para iniciar sesión.',
1225
1279
  success: 'Sesión iniciada. Tu informe ya no incluye el roadmap ni las sugerencias de mejora: tu nivel sale de certificarte.',
1226
1280
  expiresAt: (iso) => `La sesión caduca: ${iso}. Cuando expire te lo diré y podrás volver a entrar con \`login\`.`,
1227
- loggedOut: 'Sesión cerrada. Vuelves a ver el informe completo, con roadmap y sugerencias.',
1281
+ loggedOut: 'Sesión cerrada. Vuelves a ver las sugerencias de mejora en tu informe.',
1228
1282
  notLoggedIn: 'No había ninguna sesión iniciada. No hay nada que cerrar.',
1229
- errorInvalidCredentials: 'Credenciales incorrectas: revisa tu email y contraseña e inténtalo otra vez.',
1230
- errorNoEmailPassword: 'Esta cuenta existe pero no tiene contraseña de email: la registraste con Google o LinkedIn. Si fue con Google, usa `login --google`. Si fue con LinkedIn, entra desde la web para añadir una contraseña por ahora.',
1231
1283
  errorUpstream: 'El servidor de identidad de Shakers no responde ahora mismo. No es tu contraseña; inténtalo de nuevo en un rato.',
1232
1284
  errorUnreachable: 'No se pudo conectar con Shakers. Comprueba tu conexión y vuelve a ejecutar `login`.',
1233
1285
  errorConfig: 'La dirección del servidor no es válida. Revisa la configuración del endpoint (`usage --show-endpoint`).',
1234
1286
  errorNoEndpoint: 'No hay endpoint configurado. login usa la misma base que usage: define SHAKERS_CLI_INGEST_ENDPOINT o usa `usage --set-endpoint <url>`.',
1235
1287
  errorPersist: 'El inicio de sesión funcionó, pero no se pudo guardar la sesión en disco. Revisa los permisos de ~/.config/shakers y vuelve a intentarlo.',
1236
1288
  errorGeneric: 'No se pudo iniciar sesión. Inténtalo de nuevo.',
1237
- // Login con Google por loopback (`login --google`).
1238
- googleIntro: 'Inicia sesión con Google. Se abrirá tu navegador para autorizar; al terminar, vuelve aquí.',
1239
- googleOpening: 'Abriendo el navegador para iniciar sesión con Google…',
1240
- googlePasteUrl: 'Si el navegador no se abre solo, pega esta URL en él:',
1241
- googleWaiting: 'Esperando a que completes el acceso en el navegador…',
1242
- googleErrorConfigFailed: 'No se pudo obtener la configuración de login de Google desde Shakers. Comprueba tu conexión y el endpoint (`usage --show-endpoint`) e inténtalo de nuevo.',
1243
- googleErrorConfigInvalid: 'La configuración de Google que devolvió Shakers está incompleta. Es un problema del servidor, no tuyo; inténtalo más tarde.',
1244
- googleErrorPortInUse: 'El puerto local que usa el login de Google está ocupado por otro proceso. Ciérralo y vuelve a ejecutar `login --google`.',
1245
- googleErrorTimeout: 'Se agotó el tiempo esperando a que completaras el acceso en el navegador. Vuelve a ejecutar `login --google` cuando estés listo.',
1246
- googleErrorState: 'La respuesta del navegador no coincidió con lo esperado (posible interferencia). Por seguridad no se inició sesión; inténtalo de nuevo.',
1247
- googleErrorDenied: 'Cancelaste el acceso en Google (o no se concedió). No se inició sesión.',
1248
- googleErrorGeneric: 'No se pudo iniciar sesión con Google. Inténtalo de nuevo.',
1249
1289
  // Device flow (RFC 8628) para TODO método (Google, LinkedIn, email): el Hub tiene la config; abres una URL y, si hace falta, escribes un código.
1250
1290
  deviceIntro: (name) => `Inicia sesión con ${name}. Abre el enlace de abajo e introduce el código para autorizar; luego vuelve aquí.`,
1251
1291
  // Email por device flow: se abre la web de Shakers; reutiliza tu sesión abierta o entra con tu email ahí. Nunca pide contraseña en la terminal.
@@ -1254,6 +1294,8 @@ const catalogs = {
1254
1294
  deviceCodeLabel: (code) => `e introduce este código: ${code}`,
1255
1295
  deviceWaiting: 'Esperando a que autorices en el navegador…',
1256
1296
  deviceApproveHint: 'Abrimos tu navegador: apruébalo ahí para terminar (o entra con tu email si aún no has iniciado sesión). No hace falta contraseña.',
1297
+ deviceTrust: 'Qué va a pasar: se abre una página de Shakers en shakersworks.com. Comprueba que el código de esa página es el mismo que ves aquí y pulsa Aprobar. Tu contraseña solo se escribe en la web de Shakers, nunca en esta terminal. Si no has pedido tú este acceso, deniégalo.',
1298
+ deviceGranted: 'Qué puede hacer ahora el CLI: usar tu cuenta de Shakers desde este equipo para leer tu perfil y, solo cuando ejecutes un comando que lo haga, guardar cambios en él o enviar el análisis de uso de IA que apruebes. La sesión se guarda solo en este equipo (~/.config/shakers). Para cerrarla: `shakers logout`.',
1257
1299
  deviceErrorExpired: 'La solicitud de acceso caducó antes de autorizarse. Vuelve a ejecutar `login` cuando estés listo.',
1258
1300
  deviceErrorDenied: 'Se canceló el acceso (o no se concedió). No se inició sesión.',
1259
1301
  deviceErrorGeneric: 'No se pudo iniciar sesión. Inténtalo de nuevo.',
@@ -1660,13 +1702,10 @@ const catalogs = {
1660
1702
  askCv: 'Path to your CV PDF (optional, Enter to skip):',
1661
1703
  askGithub: 'Your GitHub URL (optional, Enter to skip):',
1662
1704
  askWebsite: 'Your personal website (optional, Enter to skip):',
1663
- importStarted: 'Importing your profile in the background while we continue…',
1664
- importError: 'Could not start your profile import.',
1665
1705
  importUnavailable: 'We could not import your profile right now (service unavailable); we are continuing with your registration, you can retry the import later.',
1666
1706
  importChecking: 'Importing your profile…',
1667
1707
  importDone: 'Profile imported successfully.',
1668
1708
  importFailed: 'We could not finish importing your profile. You can retry it later from your profile.',
1669
- importStillRunning: 'Your profile is still importing in the background; it will be ready in a few minutes.',
1670
1709
  professionalTitle: 'Your work situation',
1671
1710
  askWorkSituation: "What's your current work situation? Choose one:",
1672
1711
  workSituationLabels: {
@@ -1699,27 +1738,13 @@ const catalogs = {
1699
1738
  professionalSaved: 'Work situation saved.',
1700
1739
  chooseMethodHeading: 'How do you want to create your account?',
1701
1740
  signupTitle: 'Create your Shakers account',
1702
- askName: 'First name:',
1703
- askLastName: 'Last name:',
1704
- askEmail: 'Email (use a NEW one; if it already exists I will offer to sign in):',
1705
- askPassword: 'Password (at least 8 characters):',
1706
- askPasswordConfirm: 'Repeat the password:',
1707
- passwordMismatch: 'The passwords do not match. Try again.',
1708
- askFreelanceTypeSelect: 'How do you work? Choose one:',
1709
- freelanceTypeLabels: {
1710
- FREELANCE: 'Freelance',
1711
- EMPLOYEE: 'Employed',
1712
- AGENCY: 'Agency or company',
1713
- POTENTIAL_FREELANCE: 'Considering freelancing',
1714
- },
1715
- invalidChoice: 'Invalid option.',
1716
- askNewsletter: 'Do you want the newsletter? (y/n):',
1717
- weakPassword: 'The password does not meet the requirements (at least 8 characters).',
1741
+ termsAsk: 'Do you accept, to create your account? [y/N]',
1742
+ termsDeclined: 'Without accepting the Terms and the Privacy Policy your account cannot be created. Nothing was created and nothing was sent to Shakers.',
1743
+ newsletterAsk: (label) => `Optional: ${label} [y/N]`,
1718
1744
  signupError: 'Could not create the account.',
1719
1745
  freelanceIntentRequired: 'Your freelance intent is required for that type.',
1720
- accountExists: 'An account with that email already exists. Signing you in.',
1721
- accountReady: 'Account created. Signed in.',
1722
- sessionError: 'The account was created but sign-in failed. Run `shakers login`.',
1746
+ accountExists: 'You already had a Shakers account: you are now signed in with it on this computer.',
1747
+ accountReady: 'Account created. Signed in only on this computer (~/.config/shakers); to end it: `shakers logout`.',
1723
1748
  pricingTitle: 'Your per-project price',
1724
1749
  pricingSaved: 'Price saved.',
1725
1750
  askPricingFullAmount: (cur) => `Full-time project price in ${cur} (number, no thousands separators, e.g. 60000):`,
@@ -1794,7 +1819,6 @@ const catalogs = {
1794
1819
  repeatNotFound: 'You have no onboarding yet. Run `shakers onboarding` first.',
1795
1820
  repeatAuthError: 'Your session does not allow this action. Sign in again: shakers login',
1796
1821
  repeatFailed: (reason) => `Could not reset the onboarding (${reason}). Try again.`,
1797
- interviewAnswer: 'Your answer:',
1798
1822
  questionHeading: (n) => `Question ${n}`,
1799
1823
  interviewTurnError: 'Could not continue the interview.',
1800
1824
  thinking: 'Alma is thinking…',
@@ -1851,7 +1875,7 @@ const catalogs = {
1851
1875
  t2Met: (n) => `You have at least one persistent context file — instructions, config or rules — for some tool (you have ${n}).`,
1852
1876
  t3Met: (n) => `You have at least one connected MCP server, giving the AI access to external data or tools (you have ${n}).`,
1853
1877
  t4Met: (n) => `You've created your own assets — skills, commands or custom rules — beyond the default configuration (you have ${n}).`,
1854
- t5Met: (hasAgentic, mcp, custom) => `You operate an agentic CLI (Claude Code, Aider, Gemini CLI, Codex CLI or Amazon Q Developer) combined with MCP and your own assets (${mcp} MCP servers and ${custom} assets of your own).`,
1878
+ t5Met: (hasAgentic, mcp, custom) => `You operate an agentic CLI (Claude Code, Aider, Gemini CLI, Codex CLI, Pi, OpenCode or Amazon Q Developer) combined with MCP and your own assets (${mcp} MCP servers and ${custom} assets of your own).`,
1855
1879
  t6Met: (n) => `You have a team of at least 2 specialized agents defined (you have ${n}).`,
1856
1880
  t7Met: (n) => `You have hook-based automation configured (you have ${n}).`,
1857
1881
  t1Blocking: (n) => `To reach T1 (First tool) you need at least one detected AI tool — you have ${n} now.`,
@@ -1860,7 +1884,7 @@ const catalogs = {
1860
1884
  t4Blocking: (n) => `To reach T4 (Own tooling) you need to create at least one asset of your own — skill, command or rule — you have ${n} now.`,
1861
1885
  t5Blocking: (hasAgentic, mcp, custom) => {
1862
1886
  const missing = [];
1863
- if (!hasAgentic) missing.push('an agentic CLI (Claude Code, Aider, Gemini CLI, Codex CLI or Amazon Q Developer)');
1887
+ if (!hasAgentic) missing.push('an agentic CLI (Claude Code, Aider, Gemini CLI, Codex CLI, Pi, OpenCode or Amazon Q Developer)');
1864
1888
  if (mcp < 1) missing.push('at least 1 MCP server');
1865
1889
  if (custom < 1) missing.push('at least 1 asset of your own (skill, command or rule)');
1866
1890
  return `To reach T5 (Agentic operator) you're missing: ${missing.join('; ')}.`;
@@ -2094,7 +2118,7 @@ const catalogs = {
2094
2118
  roadmapPersonalizedNotice: 'Content adapted to your project.',
2095
2119
  // See the Spanish catalog for why these say what was lost
2096
2120
  // rather than what went wrong, and when they are allowed to show.
2097
- roadmapNotPersonalizedNotice: 'This roadmap is the generic one for your tier: it could not be adapted to your project this run. Run `usage` again to retry.',
2121
+ roadmapNotPersonalizedNotice: 'This roadmap is the generic one for your tier: it could not be adapted to your project this run. Run `usage --roadmap` again to retry.',
2098
2122
  // See the Spanish catalog for why the tag sits next to the
2099
2123
  // heading instead of in a footnote.
2100
2124
  roadmapNowLabel: 'Where you start from',
@@ -2178,9 +2202,9 @@ const catalogs = {
2178
2202
  },
2179
2203
  frameworkIntroUsage:
2180
2204
  'This measures your AI fluency across two axes: Setup (your tooling\'s maturity, T0–T7, deterministic) and Usage (how you use it, assessed via interview + code). It places you on the 3×3 map (Explorer → AI Native). Completing it positions your agentic profile for projects that value how you work with AI. By default it reads the AI usage across this whole machine and assumes it is yours (one machine = you).',
2181
- scanningLabel: 'Scanning environment and detectors…',
2205
+ scanningLabel: 'Reading the setup of your AI tools and your projects on this computer (nothing has left your computer yet)…',
2182
2206
  // Loader for the per-selected-repo aggregation phase (git + sessions + steering/decisions + evidence + agent org chart).
2183
- aggregatingLabel: 'Aggregating signals across the selected repos…',
2207
+ aggregatingLabel: 'Reading the git and AI-session history of the chosen repos to extract signals (nothing has left your computer yet)…',
2184
2208
  reposScopePromptHeader: 'What do you want to evaluate?',
2185
2209
  reposScopePromptHint: 'Arrows to move · enter to choose',
2186
2210
  reposScopeOptionMachine: 'Whole machine (every repo with AI activity)',
@@ -2198,6 +2222,7 @@ const catalogs = {
2198
2222
  reposScopeAll: 'Evaluating all AI usage on this machine.',
2199
2223
  synthesizingLabel: 'Synthesizing agents with AI…',
2200
2224
  personalizingRoadmapLabel: 'Personalizing roadmap…',
2225
+ roadmapNotForAccounts: 'The roadmap is not available for Shakers accounts: your level comes from certifying.',
2201
2226
  // Agent definition-quality evaluation (ephemeral LLM call).
2202
2227
  evaluatingAgentsLabel: 'Evaluating your agents’ quality…',
2203
2228
  buildNextLevelHint: 'Alternatively, run `usage --build-next-level` to generate the starter file directly in your project.',
@@ -2214,7 +2239,7 @@ const catalogs = {
2214
2239
  + ' --repo Evaluate the current repo only\n'
2215
2240
  + ' --scope machine|repo Same as --machine / --repo (skips the picker)\n'
2216
2241
  + ' --all-repos Alias of --machine\n'
2217
- + ' --roadmap Show the steps to raise your AI-usage level (hidden by default)\n'
2242
+ + ' --roadmap Show the steps to raise your AI-usage level (only without a Shakers account)\n'
2218
2243
  + ' --build-next-level Generate the next tier starter (secondary alternative)\n'
2219
2244
  + ' --force With --build-next-level, overwrite an existing file\n'
2220
2245
  + ' --lang es|en Force the language (report + prompt) instead of OS detection\n'
@@ -2261,10 +2286,20 @@ const catalogs = {
2261
2286
  persistIntro:
2262
2287
  'This report has already been generated and shown on your machine, '
2263
2288
  + 'always. Saving it in Shakers is optional and revocable at any '
2264
- + "time (usage --consent-revoke): it saves your level/tier and "
2289
+ + 'time (usage --consent-revoke). It sends your level/tier, '
2265
2290
  + 'structured signals derived across categories (tools, MCP, memory, '
2266
- + 'automations, agents, technologies) — never the content of your '
2267
- + 'files, prompts, paths or credentials. '
2291
+ + 'automations, agents, technologies) and literal text: fragments of '
2292
+ + 'your conversations with AI assistants (those in the analysed '
2293
+ + 'repositories, or all of them on this machine if you analyse the '
2294
+ + "whole machine), sample commands and your agents' definitions, "
2295
+ + 'after removing anything that looks like a '
2296
+ + 'secret. Before each upload you see what is sent. An AI model from '
2297
+ + 'an external provider (OpenAI, Google or Anthropic) analyses it on '
2298
+ + "Shakers' behalf to compute your evaluation and discards it: "
2299
+ + 'only the result is kept (levels, scores, counts and the names of '
2300
+ + 'your tools and agents). Do not use it on machines or '
2301
+ + 'conversations holding data you are not allowed to share (client, '
2302
+ + 'employer or third-party data). '
2268
2303
  + 'This same decision also authorizes, only if you accept, a '
2269
2304
  + 'third-party observability provider (Datadog) to capture the '
2270
2305
  + "content these optional AI calls send (sampled code, agent "
@@ -2287,7 +2322,7 @@ const catalogs = {
2287
2322
  // This question is now asked BEFORE any AI/egress call (agent-synthesis, agent-evaluation, roadmap personalization), not after.
2288
2323
  noReportWithoutConsent: 'Without your consent, this report will not include AI-generated synthesis, evaluation, or roadmap this time (the rest of the report is shown as usual). Run `usage` again and accept to include them, or use --consent-reset if you want to be asked again.',
2289
2324
  deniedSaved: 'Understood, nothing will be saved. You can change your mind later by running the command again.',
2290
- grantedSaved: (email) => `Thanks. From now on this report will be saved in Shakers automatically (email: ${email}, max. once per hour).`,
2325
+ grantedSaved: (email) => `Thanks. Before anything is uploaded to Shakers I will show you what would be sent, and I will upload it only if you approve it (email: ${email}, at most once an hour).`,
2291
2326
  // A logged-in Talent is NEVER asked for an email (their account already proves identity).
2292
2327
  noSessionEmail: "There's no email on file for your session yet, so this report can't be saved this time. You'll be asked again next run.",
2293
2328
  skipAlreadyDecided: (decision, path) =>
@@ -2296,6 +2331,9 @@ const catalogs = {
2296
2331
  nonInteractiveWarning:
2297
2332
  'Non-interactive input (no TTY) detected: if no answer arrives via stdin, consent will not be '
2298
2333
  + 'saved this run and you will be asked again next time.',
2334
+ nonInteractiveUnanswered:
2335
+ 'No answer arrived, so your consent is still undecided and nothing was saved in or sent to Shakers. '
2336
+ + 'Run `shakers ai-usage` in your own terminal to answer.',
2299
2337
  status: {
2300
2338
  heading: 'Consent status (saved in Shakers)',
2301
2339
  decisionGranted: 'Decision: granted',
@@ -2609,12 +2647,13 @@ const catalogs = {
2609
2647
  },
2610
2648
  mcpInstall: {
2611
2649
  title: 'Connecting the Shakers MCP',
2612
- clients: { claudeDesktop: 'Claude Desktop', claudeCode: 'Claude Code', cursor: 'Cursor', geminiCli: 'Gemini CLI', codex: 'Codex (and ChatGPT in Codex mode)', vscode: 'VS Code', windsurf: 'Windsurf' },
2650
+ clients: { claudeDesktop: 'Claude Desktop', claudeCode: 'Claude Code', cursor: 'Cursor', geminiCli: 'Gemini CLI', codex: 'Codex / ChatGPT', vscode: 'VS Code', windsurf: 'Windsurf' },
2613
2651
  status: {
2614
2652
  configured: 'connected',
2615
2653
  unchanged: 'already connected',
2616
2654
  'not-found': 'not installed on this machine',
2617
- 'invalid-json': 'its config is not valid JSON; left untouched',
2655
+ 'invalid-config': 'its config cannot be edited safely; left untouched',
2656
+ manual: 'run this command in your terminal:',
2618
2657
  failed: 'could not connect',
2619
2658
  },
2620
2659
  removedLegacy: 'Replaces the old "shakers-ai-usage" entry.',
@@ -2622,10 +2661,27 @@ const catalogs = {
2622
2661
  confirm: 'Connect Shakers to them so you can use it from your AI? [Y/n]',
2623
2662
  declined: 'Nothing touched. To connect it later, run the installer again.',
2624
2663
  noTerminal: 'There is no terminal to ask you on, so nothing was touched. Run the installer again in your terminal to connect it.',
2625
- restart: 'Restart any of these apps that is open so it loads Shakers.',
2664
+ restart: (apps) => `Restart ${apps} (or reload their MCP servers) so the Shakers tools appear.`,
2665
+ alreadyConnected: 'Shakers was already connected. If its tools do not appear, restart the app or reload its MCP servers.',
2666
+ yesHint: 'To connect them without being asked (for example from an AI assistant): shakers mcp install --yes',
2667
+ help: 'mcp install — connect Shakers to the AI apps on this machine (Claude Desktop, Claude Code, Cursor, Gemini CLI, Codex / ChatGPT, VS Code, Windsurf).\n\nUsage:\n shakers mcp install ask on the terminal, then connect every app found\n shakers mcp install --yes connect without asking (-y; for AI assistants and scripts)\n shakers mcp install --json print the result as one JSON object\n\nWithout --yes and without a terminal, nothing is touched. SHAKERS_MCP_INSTALL=1 works like --yes.\nWhen it finishes, restart the apps it names (or reload their MCP servers) so the Shakers tools appear.',
2626
2668
  noneFound: 'Found no supported AI app on this machine.',
2627
2669
  failedAll: 'Could not connect Shakers to your AI apps. Re-run this installer to retry.',
2628
2670
  },
2671
+ skillInstall: {
2672
+ title: 'Installing the shakers-cli skill for your AI agents',
2673
+ agents: { claudeCode: 'Claude Code', codex: 'Codex' },
2674
+ status: { installed: 'installed', failed: 'could not install' },
2675
+ found: (agents) => `Found: ${agents}.`,
2676
+ confirm: 'Install the skill so your agent knows how to use the Shakers CLI? [Y/n]',
2677
+ declined: 'Nothing touched. To install it later: shakers skill install',
2678
+ noTerminal: 'There is no terminal to ask you on, so nothing was touched.',
2679
+ yesHint: 'To install it without being asked (for example from an AI assistant): shakers skill install --yes',
2680
+ noneFound: 'Found neither Claude Code nor Codex on this machine; nothing touched.',
2681
+ done: (agents) => `Done. If ${agents} does not see the skill, start a new session.`,
2682
+ failedAll: 'Could not install the skill. Run shakers skill install again to retry.',
2683
+ help: 'skill install — install the shakers-cli skill, which teaches your AI agent (Claude Code, Codex) to use this CLI.\n\nUsage:\n shakers skill install ask on the terminal, then install for every agent found\n shakers skill install --yes install without asking (-y; for AI assistants and scripts)\n shakers skill install --json print the result as one JSON object\n\nWithout --yes and without a terminal, nothing is touched. It writes only ~/.claude/skills/shakers-cli/SKILL.md and <CODEX_HOME>/skills/shakers-cli/SKILL.md.',
2684
+ },
2629
2685
  rate: {
2630
2686
  help: 'rate — view or set your per-project rate.\n\nUsage:\n rate [--lang es|en] [--json] view your rate\n rate --set [--lang es|en] set it (interactive, with confirmation)\n\nRequires an active session (shakers login).',
2631
2687
  loginRequired: '"rate" requires an active session.\n Missing: sign-in. Run `shakers login` first.',
@@ -2846,6 +2902,46 @@ const catalogs = {
2846
2902
  remediationCopiedLabel: 'Copied ✓',
2847
2903
  },
2848
2904
  },
2905
+ trust: {
2906
+ uploadWarning: "Before you approve: up to 40 literal excerpts of your messages (corrections and decisions) and 20 commands, 200 characters max each, your agents' configuration and definitions, and activity counts would be sent. Anything that looks like a secret, an email or a path is removed, but the filter is approximate: do not approve it if there is data you are not allowed to share (client, employer or third-party data).",
2907
+ cli: {
2908
+ 'ai-usage': { before: "I am going to analyse on this computer your AI tools, the history of your code projects and your conversations with AI coding assistants (Claude Code, Codex, Gemini, Cursor, Continue, Cline/Roo, Aider, Pi, OpenCode). Analysing a repo covers only that repo's conversations; choosing the whole machine, or signing up, covers every conversation on this computer. Before sending anything to Shakers I show you what would be sent and ask you. Do not use it on a computer or with conversations holding data you are not allowed to share (client, employer or third-party data).", after: 'Done. If you approved the upload, an AI model from an external provider (OpenAI, Google or Anthropic) analyses what was sent on behalf of Shakers to compute your evaluation and then it is discarded; only the result is kept (levels, scores, counts and the names of your tools and agents). If you did not approve it, nothing went to your profile. Human review: "Request human review" on the web or talent@shakersworks.com; to ask for deletion of what was sent, the same email. To stop sending from this computer: `shakers ai-usage --consent-revoke`.' },
2909
+ certify: { before: 'I am going to prepare the certification of your main role with Shakers: I explain each step and ask before sending anything.', after: 'Done. What was sent stays in your Shakers account.' },
2910
+ share: { before: 'I am going to create on this computer a card with your AI report to share. I send nothing.', after: 'Done. The card is on this computer; nothing was sent.' },
2911
+ report: { before: 'I am going to read your AI report (from Shakers or from your last analysis on this computer) and save it as a file on this computer. I do not change your profile.' },
2912
+ 'add-skill': { before: 'I am going to read the skills from your last analysis on Shakers and add the ones you pick to your profile.', after: 'Done. Your Shakers profile has the skills you picked.' },
2913
+ 'add-agent': { before: 'I am going to read the agents from your last analysis on Shakers and add the one you pick to your profile, with the description you approve.', after: 'Done. Your Shakers profile has the agent you picked.' },
2914
+ 'add-project': { before: 'I am going to read this folder\'s name, the web address of its code and the dates of its first and last change, and ask you before sending them to your Shakers portfolio. I do not send your code.', after: 'Done. The project is in your Shakers portfolio.' },
2915
+ 'add-role': { before: 'I am going to read the Shakers roles and add the one you pick to your profile.', after: 'Done. Your Shakers profile has the role you picked.' },
2916
+ 'change-role': { before: 'I am going to read your Shakers roles and save the one you pick as your main role.', after: 'Done. Your main role is saved on Shakers.' },
2917
+ 'find-projects': { before: 'I only read from Shakers the projects that fit you; I change nothing.' },
2918
+ 'show-project': { before: 'I only read the project details from Shakers; I change nothing.' },
2919
+ 'save-project': { before: 'I am going to save this project to your saved projects on Shakers.', after: 'Done. The project is in your saved projects.' },
2920
+ 'unsave-project': { before: 'I am going to remove this project from your saved projects on Shakers.', after: 'Done. The project is no longer in your saved projects.' },
2921
+ invitations: { before: 'I only read your Shakers invitations; I change nothing.' },
2922
+ applications: { before: 'I only read your Shakers applications; I change nothing.' },
2923
+ availability: { before: 'I only read your availability from your Shakers profile; I change nothing.' },
2924
+ 'availability:set': { before: 'I am going to change your availability on your Shakers profile.', after: 'Done. Your availability is saved on Shakers.' },
2925
+ certifications: { before: 'I only read your Shakers certifications; I change nothing.' },
2926
+ me: { before: 'I only read your Shakers profile; I change nothing.' },
2927
+ rate: { before: 'I only read your rate from your Shakers profile; I change nothing.' },
2928
+ 'rate:set': { before: 'I am going to change your rate on your Shakers profile.', after: 'Done. Your rate is saved on Shakers.' },
2929
+ lang: { before: 'I only read your languages from your Shakers profile; I change nothing.' },
2930
+ 'lang:set': { before: 'I am going to change your languages on your Shakers profile.', after: 'Done. Your languages are saved on Shakers.' },
2931
+ socials: { before: 'I only read your social links from your Shakers profile; I change nothing.' },
2932
+ 'socials:set': { before: 'I am going to change your social links on your Shakers profile.', after: 'Done. Your social links are saved on Shakers.' },
2933
+ experiences: { before: 'I only read your experiences from your Shakers profile; I change nothing.' },
2934
+ portfolios: { before: 'I only read your portfolio from your Shakers profile; I change nothing.' },
2935
+ alma: { before: 'I am going to send your messages to Alma, the Shakers assistant. If she proposes a change to your profile, I ask you before making it.' },
2936
+ register: { before: 'I am going to create your Shakers account: I ask you here to accept the Terms and open a Shakers page (shakersworks.com) with a code; your password is typed only there. I tell you before every step that sends data.' },
2937
+ onboarding: { before: 'I am going to run the onboarding interview with you: your answers are sent to Shakers to get to know your profile. I read nothing on your computer.', after: 'Done. Your interview is saved on Shakers.' },
2938
+ config: { before: 'I only work with the CLI configuration on this computer (~/.config/shakers); I send nothing to Shakers.' },
2939
+ update: { before: 'I am going to check for a new CLI version and, if there is one, replace its installed files on this computer. I do not touch your projects or your session.' },
2940
+ logout: { before: 'I am going to sign you out: I tell Shakers and delete the session and consent saved on this computer (~/.config/shakers). I do not change your profile.' },
2941
+ mcp: { before: 'I am going to add the Shakers MCP server to the configuration of your AI apps on this computer (for example ~/.cursor/mcp.json or ~/.codex/config.toml). I send nothing to Shakers.', after: 'Done. Only the configuration files listed above changed.' },
2942
+ skill: { before: 'I am going to write the Shakers CLI skill into your agents\' skill folders on this computer (~/.claude/skills or ~/.codex/skills). I send nothing to Shakers.', after: 'Done. Only the files listed above changed.' },
2943
+ },
2944
+ },
2849
2945
  // Branded mini-shell chrome.
2850
2946
  login: {
2851
2947
  intro: 'Log in with your Shakers email and password.',
@@ -2857,28 +2953,14 @@ const catalogs = {
2857
2953
  needInput: 'Email and password are required to log in.',
2858
2954
  success: 'Logged in. Your report no longer includes the roadmap or the improvement suggestions: your level comes from certifying.',
2859
2955
  expiresAt: (iso) => `The session expires: ${iso}. When it does I will tell you, and you can log in again with \`login\`.`,
2860
- loggedOut: 'Logged out. You are back to the full report, with roadmap and suggestions.',
2956
+ loggedOut: 'Logged out. Your report shows the improvement suggestions again.',
2861
2957
  notLoggedIn: 'No session was active. Nothing to log out of.',
2862
- errorInvalidCredentials: 'Wrong credentials: check your email and password and try again.',
2863
- errorNoEmailPassword: 'This account exists but has no email password: you registered with Google or LinkedIn. If it was Google, use `login --google`. If it was LinkedIn, sign in on the web to add a password for now.',
2864
2958
  errorUpstream: "Shakers' identity server is not responding right now. It is not your password; try again in a while.",
2865
2959
  errorUnreachable: 'Could not reach Shakers. Check your connection and run `login` again.',
2866
2960
  errorConfig: 'The server address is invalid. Check the endpoint configuration (`usage --show-endpoint`).',
2867
2961
  errorNoEndpoint: 'No endpoint configured. login uses the same base as usage: set SHAKERS_CLI_INGEST_ENDPOINT or use `usage --set-endpoint <url>`.',
2868
2962
  errorPersist: 'Login succeeded, but the session could not be saved to disk. Check the permissions of ~/.config/shakers and try again.',
2869
2963
  errorGeneric: 'Could not log in. Try again.',
2870
- // Google login by loopback (`login --google`).
2871
- googleIntro: 'Log in with Google. Your browser will open to authorize; when you are done, come back here.',
2872
- googleOpening: 'Opening your browser to log in with Google…',
2873
- googlePasteUrl: 'If your browser does not open on its own, paste this URL into it:',
2874
- googleWaiting: 'Waiting for you to finish signing in in the browser…',
2875
- googleErrorConfigFailed: "Could not fetch the Google login configuration from Shakers. Check your connection and the endpoint (`usage --show-endpoint`) and try again.",
2876
- googleErrorConfigInvalid: 'The Google configuration Shakers returned is incomplete. This is a server-side problem, not yours; try again later.',
2877
- googleErrorPortInUse: 'The local port the Google login uses is busy with another process. Close it and run `login --google` again.',
2878
- googleErrorTimeout: 'Timed out waiting for you to finish signing in in the browser. Run `login --google` again when you are ready.',
2879
- googleErrorState: 'The browser response did not match what was expected (possible interference). For safety no session was started; try again.',
2880
- googleErrorDenied: 'You cancelled access in Google (or it was not granted). No session was started.',
2881
- googleErrorGeneric: 'Could not log in with Google. Try again.',
2882
2964
  // Device flow (RFC 8628) for EVERY method (Google, LinkedIn, email): the Hub owns the config; you open a URL and, if needed, type a code.
2883
2965
  deviceIntro: (name) => `Sign in with ${name}. Open the link below and enter the code to authorize; then come back here.`,
2884
2966
  // Email via device flow: the Shakers web opens; reuse your open session or sign in with your email there. It never asks for a password in the terminal.
@@ -2887,6 +2969,8 @@ const catalogs = {
2887
2969
  deviceCodeLabel: (code) => `and enter this code: ${code}`,
2888
2970
  deviceWaiting: 'Waiting for you to authorize in the browser…',
2889
2971
  deviceApproveHint: 'We opened your browser: approve there to finish (or sign in with your email if you are not signed in yet). No password needed.',
2972
+ deviceTrust: 'What happens next: a Shakers page opens on shakersworks.com. Check that the code on that page matches the one shown here and click Approve. Your password is typed only on the Shakers website, never in this terminal. If you did not ask for this access, deny it.',
2973
+ deviceGranted: 'What the CLI can do now: use your Shakers account from this computer to read your profile and, only when you run a command that does so, save changes to it or send the AI-usage analysis you approve. The session is stored only on this computer (~/.config/shakers). To end it: `shakers logout`.',
2890
2974
  deviceErrorExpired: 'The sign-in request expired before it was authorized. Run `login` again when you are ready.',
2891
2975
  deviceErrorDenied: 'The sign-in was cancelled or not granted. No session was started.',
2892
2976
  deviceErrorGeneric: 'Could not sign in. Try again.',