shakers-cli 0.17.0 → 0.19.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 (67) hide show
  1. package/README.md +15 -5
  2. package/bin/add-agent.js +12 -6
  3. package/bin/ai-usage.js +2 -1
  4. package/bin/apply.js +66 -0
  5. package/bin/command-audit-worker.js +19 -0
  6. package/bin/login.js +4 -8
  7. package/bin/mcp.js +32 -19
  8. package/bin/register.js +10 -29
  9. package/bin/shakers-cli.js +21 -12
  10. package/package.json +1 -1
  11. package/src/agents-client.js +1 -0
  12. package/src/ai-usage-review.js +5 -3
  13. package/src/apply-client.js +164 -0
  14. package/src/apply-flow.js +105 -0
  15. package/src/auth-client.js +7 -107
  16. package/src/auth-session-store.js +6 -12
  17. package/src/backend-request.js +1 -24
  18. package/src/cli-skill.js +77 -0
  19. package/src/command-audit.js +58 -5
  20. package/src/config.js +15 -16
  21. package/src/consent-flow.js +7 -2
  22. package/src/detect-repos.js +4 -8
  23. package/src/detected-agents.js +40 -0
  24. package/src/detectors.js +23 -0
  25. package/src/device-auth-store.js +2 -2
  26. package/src/device-login.js +11 -9
  27. package/src/env-paths.js +9 -1
  28. package/src/i18n.js +258 -88
  29. package/src/mcp-ai-usage-result-tool.js +6 -4
  30. package/src/mcp-ai-usage-tool.js +73 -47
  31. package/src/mcp-apply-tools.js +152 -0
  32. package/src/mcp-choice.js +171 -34
  33. package/src/mcp-detector.js +26 -2
  34. package/src/mcp-device-login-tools.js +40 -8
  35. package/src/mcp-install.js +15 -11
  36. package/src/mcp-instructions.js +43 -17
  37. package/src/mcp-onboarding-tools.js +90 -3
  38. package/src/mcp-prompts.js +6 -4
  39. package/src/mcp-register-tools.js +25 -14
  40. package/src/mcp-role-tools.js +35 -17
  41. package/src/mcp-server-name.js +7 -0
  42. package/src/mcp-server.js +35 -7
  43. package/src/mcp-signup-client.js +3 -6
  44. package/src/mcp-signup-flow.js +389 -0
  45. package/src/mcp-signup-tools.js +237 -126
  46. package/src/onboarding-flow.js +30 -125
  47. package/src/opencode-sessions.js +92 -0
  48. package/src/profile-agents.js +26 -0
  49. package/src/saved-positions-flow.js +3 -3
  50. package/src/scanner.js +104 -12
  51. package/src/session-gate.js +1 -4
  52. package/src/session-normalizers.js +114 -8
  53. package/src/session-refresh.js +5 -16
  54. package/src/session-scan.js +167 -22
  55. package/src/share-payload.js +4 -1
  56. package/src/share.js +2 -2
  57. package/src/signup-client.js +12 -39
  58. package/src/signup-copy.js +506 -68
  59. package/src/skill-install.js +102 -0
  60. package/src/start-add-agents.js +106 -3
  61. package/src/tier-engine.js +14 -2
  62. package/src/trust-narration.js +32 -0
  63. package/src/usage-run-steps.js +16 -4
  64. package/src/usage-scan.js +3 -0
  65. package/src/brand-svg.js +0 -9
  66. package/src/inter-fonts.js +0 -6
  67. package/src/mcp-auth-form.js +0 -484
@@ -7,35 +7,302 @@ const SIGNUP_LANGUAGES = ['es', 'en', 'it', 'pt'];
7
7
  // Legal texts. The CLI onboarding reads the es/en ones too.
8
8
  const LEGAL = {
9
9
  es: {
10
+ terms: (terms, privacy) => `Al continuar, aceptas los ${terms('Términos')} y la ${privacy('Política de Privacidad')}.`,
11
+ newsletter: 'Quiero recibir contenido sobre el futuro del trabajo',
10
12
  signupNotice: (sources) => `Antes de crear tu cuenta: guardamos los datos que me des (${listOf('es', [...named(sources, COPY.es.importSources), 'lo que sé de ti'])}) y podemos contactarte. Te completo el perfil automáticamente y cualquier campo lo puedes cambiar después en la web. La entrevista de onboarding la puedes hacer luego aquí mismo en el chat o en la web.`,
11
- usageInfoAccessed: 'Se accede a: la estructura de tus herramientas de IA y a extractos anonimizados de tu actividad en este equipo. Tu código y tus prompts en crudo NUNCA salen de tu máquina.',
13
+ usageInfoAccessed: "Se accede a: tus herramientas de IA, el historial de tus proyectos de código y tus conversaciones con asistentes de IA de programación en todo este equipo. Si apruebas la subida, Shakers recibe fragmentos literales de esas conversaciones (hasta 40 extractos de tus mensajes y 20 comandos, de 200 caracteres como máximo), la configuración y las definiciones de tus agentes y recuentos de actividad, tras quitar lo que parezca un secreto, un email o una ruta. Un modelo de IA de un proveedor externo (OpenAI, Google o Anthropic) los analiza por encargo de Shakers para calcular tu evaluación y después los descarta: solo se guarda el resultado (niveles, puntuaciones, recuentos y los nombres de tus herramientas y agentes). No lo uses en un equipo ni con conversaciones que contengan datos que no puedas compartir: de clientes, de tu empresa o de terceros.",
12
14
  usageGoalDuration: 'Objetivo: medir cómo trabajas con IA para tu perfil. Duración: ~1-2 minutos.',
13
15
  interviewInfoAccessed: 'Se accede a: tus respuestas en esta entrevista y tu perfil importado. La conduce el servidor de Shakers.',
14
16
  interviewGoalDuration: 'Objetivo: conocer tu rol, tu dedicación y cómo usas la IA. Duración: ~5 minutos.',
15
17
  },
16
18
  en: {
19
+ terms: (terms, privacy) => `By continuing, you accept the ${terms('Terms')} and the ${privacy('Privacy Policy')}.`,
20
+ newsletter: 'I want to receive content about the future of work',
17
21
  signupNotice: (sources) => `Before we create your account: we store the data you give me (${listOf('en', [...named(sources, COPY.en.importSources), 'what I know about you'])}) and may contact you. I fill in your profile automatically and you can change any field later on the web. You can do the onboarding interview afterwards right here in the chat or on the web.`,
18
- usageInfoAccessed: 'Accessed: the structure of your AI tools and redacted excerpts of your activity on this machine. Your raw code and prompts NEVER leave your machine.',
22
+ usageInfoAccessed: "Accessed: your AI tools, the history of your code projects and your conversations with AI coding assistants across this whole machine. If you approve the upload, Shakers receives literal fragments of those conversations (up to 40 excerpts of your messages and 20 commands, 200 characters at most each), the configuration and definitions of your agents, and activity counts, after removing anything that looks like a secret, an email or a path. An AI model from an external provider (OpenAI, Google or Anthropic) analyses them on Shakers' behalf to compute your evaluation and then discards them: only the result is kept (levels, scores, counts and the names of your tools and agents). Do not use it on a machine or with conversations that hold data you are not allowed to share: client, employer or third-party data.",
19
23
  usageGoalDuration: 'Goal: measure how you work with AI for your profile. Duration: ~1-2 minutes.',
20
24
  interviewInfoAccessed: 'Accessed: your answers in this interview and your imported profile. It is conducted by the Shakers server.',
21
25
  interviewGoalDuration: 'Goal: learn your role, your dedication and how you use AI. Duration: ~5 minutes.',
22
26
  },
23
27
  it: {
28
+ terms: (terms, privacy) => `Continuando, accetti i ${terms('Termini')} e l'${privacy('Informativa sulla privacy')}.`,
29
+ newsletter: 'Voglio ricevere contenuti sul futuro del lavoro',
24
30
  signupNotice: (sources) => `Prima di creare il tuo account: conserviamo i dati che mi dai (${listOf('it', [...named(sources, COPY.it.importSources), 'quello che so di te'])}) e possiamo contattarti. Completo il tuo profilo in automatico e puoi cambiare qualsiasi campo in seguito sul sito. Il colloquio di onboarding puoi farlo dopo sul sito.`,
25
- usageInfoAccessed: 'Accesso a: la struttura dei tuoi strumenti di IA ed estratti anonimizzati della tua attività su questo computer. Il tuo codice e i tuoi prompt originali NON escono MAI dalla tua macchina.',
31
+ usageInfoAccessed: "Accesso a: i tuoi strumenti di IA, la cronologia dei tuoi progetti di codice e le tue conversazioni con assistenti di IA per programmare su tutto questo computer. Se approvi il caricamento, Shakers riceve frammenti letterali di queste conversazioni (fino a 40 estratti dei tuoi messaggi e 20 comandi, al massimo 200 caratteri ciascuno), la configurazione e le definizioni dei tuoi agenti e conteggi di attività, dopo aver tolto ciò che sembra un segreto, un'email o un percorso. Un modello di IA di un fornitore esterno (OpenAI, Google o Anthropic) li analizza per conto di Shakers per calcolare la tua valutazione e poi li scarta: si conserva solo il risultato (livelli, punteggi, conteggi e i nomi dei tuoi strumenti e agenti). Non usarlo su un computer né con conversazioni che contengono dati che non puoi condividere: di clienti, del tuo datore di lavoro o di terzi.",
26
32
  usageGoalDuration: "Obiettivo: misurare come lavori con l'IA per il tuo profilo. Durata: ~1-2 minuti.",
27
33
  interviewInfoAccessed: 'Accesso a: le tue risposte in questo colloquio e il tuo profilo importato. Lo conduce il server di Shakers.',
28
34
  interviewGoalDuration: "Obiettivo: conoscere il tuo ruolo, la tua disponibilità e come usi l'IA. Durata: ~5 minuti.",
29
35
  },
30
36
  pt: {
37
+ terms: (terms, privacy) => `Ao continuar, aceita os ${terms('Termos')} e a ${privacy('Política de Privacidade')}.`,
38
+ newsletter: 'Quero receber conteúdos sobre o futuro do trabalho',
31
39
  signupNotice: (sources) => `Antes de criar a sua conta: guardamos os dados que me der (${listOf('pt', [...named(sources, COPY.pt.importSources), 'o que sei sobre si'])}) e podemos contactá-lo. Preencho o seu perfil automaticamente e pode alterar qualquer campo depois na web. A entrevista de onboarding pode fazê-la depois na web.`,
32
- usageInfoAccessed: 'Acede-se a: a estrutura das suas ferramentas de IA e excertos anonimizados da sua atividade neste computador. O seu código e os seus prompts em bruto NUNCA saem da sua máquina.',
40
+ usageInfoAccessed: "Acede-se a: as suas ferramentas de IA, o histórico dos seus projetos de código e as suas conversas com assistentes de IA de programação em todo este computador. Se aprovar o envio, a Shakers recebe fragmentos literais dessas conversas (até 40 excertos das suas mensagens e 20 comandos, com 200 caracteres no máximo cada), a configuração e as definições dos seus agentes e contagens de atividade, depois de retirar o que pareça um segredo, um email ou um caminho. Um modelo de IA de um fornecedor externo (OpenAI, Google ou Anthropic) analisa-os por conta da Shakers para calcular a sua avaliação e depois descarta-os: só se guarda o resultado (níveis, pontuações, contagens e os nomes das suas ferramentas e agentes). Não o use num computador nem com conversas que contenham dados que não possa partilhar: de clientes, do seu empregador ou de terceiros.",
33
41
  usageGoalDuration: 'Objetivo: medir como trabalha com IA para o seu perfil. Duração: ~1-2 minutos.',
34
42
  interviewInfoAccessed: 'Acede-se a: as suas respostas nesta entrevista e o seu perfil importado. É conduzida pelo servidor da Shakers.',
35
43
  interviewGoalDuration: 'Objetivo: conhecer o seu perfil, a sua dedicação e como usa a IA. Duração: ~5 minutos.',
36
44
  },
37
45
  };
38
46
 
47
+ const TRUST = {
48
+ es: {
49
+ heading: 'Qué hace Shakers en este paso:',
50
+ failed: 'Este paso no se ha completado; no se ha cambiado nada más.',
51
+ mcp: {
52
+ ai_usage: 'si dices que sí, analiza 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). En el alta se analizan todas las conversaciones de este equipo; si analizas un repo concreto, solo las de ese repo. Todavía no se envía nada: antes te enseño qué se enviaría y tú decides. No lo uses en un equipo ni con conversaciones que tengan datos que no puedes compartir (de clientes, de tu empresa o de terceros).',
53
+ ai_usage_upload: 'te enseña qué se enviaría a Shakers y solo lo envía si lo apruebas: hasta 40 extractos literales de tus propios 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. Antes 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). Un modelo de IA de un proveedor externo (OpenAI, Google o Anthropic) lo analiza 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. Revisión humana: «Solicitar revisión humana» en la web o talent@shakersworks.com; para pedir que se borre, el mismo correo.',
54
+ ai_usage_result: 'lee de tu cuenta de Shakers el resultado del análisis que aprobaste. No lee nada de este ordenador ni cambia nada.',
55
+ logout: 'borra la sesión de Shakers guardada en este ordenador (~/.config/shakers) y avisa a Shakers de que la cierras. No cambia nada de tu perfil.',
56
+ list_addable: 'lee de tu cuenta de Shakers las skills y agentes de IA encontrados en el análisis que enviaste. No cambia nada.',
57
+ add_skill: 'añade a tu perfil de Shakers la skill que elijas. No lee nada de este ordenador.',
58
+ add_agent: 'añade a tu perfil de Shakers el agente de IA que elijas, con la descripción que acordéis. No lee nada más de este ordenador.',
59
+ add_detected_agents: 'añade a tu perfil de Shakers los agentes de IA detectados que elijas, solo su nombre y su descripción, y los clientes los verán en tu perfil. No lee ni sube nada más de este ordenador. Puedes quitarlos cuando quieras desde tu perfil en la web de Shakers.',
60
+ add_project: 'te pregunta antes y, si dices que sí, lee el nombre de la carpeta del proyecto, la dirección web de su código y las fechas de su primer y último cambio, y los envía a tu portfolio de Shakers. No envía tu código.',
61
+ suggest_register_context: 'te pregunta antes y, si dices que sí, mira los nombres de los PDF y Word de Escritorio, Documentos, Descargas e iCloud Drive que parezcan un CV, tu zona horaria y el nombre, email y usuario de GitHub de tus herramientas de programación. No abre esos archivos ni envía nada.',
62
+ read_cv: 'te pregunta antes y, si dices que sí, lee el texto del archivo de CV que permitas para preparar el borrador de tu perfil. No envía nada a Shakers.',
63
+ signup: "lleva tu alta en Shakers paso a paso aquí en el chat. Antes de leer algo de este ordenador o de enviar tus datos a Shakers te pregunta, y no crea la cuenta hasta que confirmes tu borrador.",
64
+ signup_start: 'empieza tu alta aquí en el chat. Todavía no lee nada de tu ordenador ni envía nada.',
65
+ signup_email: 'te pregunta con qué email crear tu cuenta. No envía nada todavía.',
66
+ signup_draft: 'te enseña el borrador de tu perfil preparado con tu CV y tu LinkedIn. No crea la cuenta ni envía nada hasta que lo confirmes.',
67
+ signup_create_account: 'tras tu confirmación y tu aceptación de los Términos, envía a Shakers tu LinkedIn, tu CV y el borrador para preparar tu perfil, y abre en tu navegador una página de Shakers (shakersworks.com) con un código donde creas la cuenta. Tu contraseña nunca pasa por este chat.',
68
+ signup_status: 'consulta en Shakers si tu cuenta ya existe y cómo va la importación de tu perfil. No cambia nada.',
69
+ update_existing_profile: 'te enseña qué rellenaría tu borrador en los campos vacíos de tu perfil y solo lo guarda si dices que sí. Nunca sobrescribe lo que ya tenías.',
70
+ import_profile: 'envía a Shakers tu LinkedIn, GitHub o web para rellenar lo vacío de tu perfil; un archivo de CV de este ordenador solo se envía si dices que sí. Lo que ya escribiste se mantiene.',
71
+ save_profile_details: 'guarda en tu perfil de Shakers los datos que confirmaste (situación, tarifas, disponibilidad, ubicación, idiomas y teléfono). En una cuenta existente solo rellena lo vacío.',
72
+ open_web: 'abre Shakers en tu navegador con tu sesión iniciada, con un enlace de un solo uso que caduca en 2 minutos. No cambia nada.',
73
+ open_linkedin_profile: 'abre tu perfil de LinkedIn en tu navegador para que copies su dirección. No lee ni envía nada.',
74
+ social_signin_start: 'prepara el acceso: te da un enlace a una página de Shakers (shakersworks.com) y un código. Allí compruebas que el código coincide y apruebas. Tu contraseña solo se escribe en la web de Shakers, nunca en este chat. Si no has pedido tú este acceso, deniégalo.',
75
+ social_signin_poll: 'comprueba si ya aprobaste el acceso y, si es así, guarda la sesión solo en este ordenador (~/.config/shakers). Desde entonces este chat puede leer tu perfil y guardar en él lo que confirmes. Para cerrarla: pídeme cerrar sesión.',
76
+ onboarding_interview_start: 'empieza la entrevista de onboarding después de preguntarte dónde y cómo responder. Tus respuestas se envían a Shakers para conocer tu perfil. No lee nada de tu ordenador.',
77
+ onboarding_interview_turn: 'envía a Shakers la respuesta que diste o aprobaste y trae la siguiente pregunta.',
78
+ onboarding_interview_complete: 'cierra la entrevista y la guarda en Shakers.',
79
+ repeat_onboarding_interview: 'borra en Shakers tu entrevista de onboarding anterior para que puedas repetirla. No se puede deshacer.',
80
+ onboarding_finish: 'marca tu onboarding como completado en Shakers.',
81
+ list_available_roles: 'lee de Shakers los roles que puedes añadir a tu perfil. No cambia nada.',
82
+ add_role: 'añade a tu perfil de Shakers el rol que elijas.',
83
+ list_my_roles: 'lee tus roles de Shakers y los que te sugieren tu entrevista y tu CV. No cambia nada.',
84
+ set_main_role: 'te pregunta cuál es tu rol principal y lo guarda en tu perfil de Shakers.',
85
+ report: 'enseña tu informe de IA guardado en Shakers o el de tu último análisis en este ordenador, y lo guarda como archivo en este ordenador.',
86
+ share: 'crea en este ordenador una tarjeta con tu informe de IA para compartir en LinkedIn. No envía nada.',
87
+ list_certifiable_dimensions: 'lee de Shakers qué áreas de tu rol principal puedes certificar. No cambia nada.',
88
+ find_projects: 'lee de Shakers los proyectos abiertos que mejor encajan contigo. No cambia nada.',
89
+ show_project: 'lee de Shakers el detalle de un proyecto. No cambia nada.',
90
+ prepare_application: 'lee de Shakers las preguntas de la candidatura a un proyecto. No envía ni cambia nada.',
91
+ apply_to_project: 'envía a Shakers tu mensaje y tus respuestas para el proyecto que elijas, solo después de enseñarte el resumen y de que digas que sí: el cliente verá tu perfil de Shakers y se abre un chat con el equipo. Si el proyecto lo pide, Shakers revisa tu candidatura antes de que la vea el cliente. No cambia tu perfil.',
92
+ save_project: 'guarda ese proyecto en tus proyectos guardados de Shakers.',
93
+ unsave_project: 'quita ese proyecto de tus proyectos guardados de Shakers.',
94
+ list_invitations: 'lee tus invitaciones a proyectos en Shakers. No cambia nada.',
95
+ list_applications: 'lee tus candidaturas en Shakers. No cambia nada.',
96
+ get_availability: 'lee tu disponibilidad de tu perfil de Shakers. No cambia nada.',
97
+ list_certifications: 'lee tus certificaciones de Shakers. No cambia nada.',
98
+ get_profile: 'lee tu perfil de Shakers. No cambia nada.',
99
+ get_rate: 'lee tu tarifa de tu perfil de Shakers. No cambia nada.',
100
+ get_languages: 'lee tus idiomas de tu perfil de Shakers. No cambia nada.',
101
+ list_socials: 'lee tus redes de tu perfil de Shakers. No cambia nada.',
102
+ list_experiences: 'lee tus experiencias de tu perfil de Shakers. No cambia nada.',
103
+ list_portfolios: 'lee tus proyectos de portfolio de tu perfil de Shakers. No cambia nada.',
104
+ alma_chat: 'envía tu mensaje a Alma, la asistente de Shakers, y te enseña su respuesta. Si propone un cambio en tu perfil, te pregunta antes de hacerlo.',
105
+ alma_decision: 'envía a Shakers tu sí o tu no al cambio de perfil que propuso Alma; solo con un sí se cambia tu perfil.',
106
+ },
107
+ },
108
+ en: {
109
+ heading: 'What Shakers does in this step:',
110
+ failed: 'This step did not complete; nothing else was changed.',
111
+ mcp: {
112
+ ai_usage: "if you say yes, it analyses 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). Sign-up analyses every conversation on this computer; analysing a specific repo covers only that repo's conversations. Nothing is sent yet: first I show you what would be sent and you decide. Do not use it on a computer or with conversations holding data you are not allowed to share (client, employer or third-party data).",
113
+ ai_usage_upload: 'shows you what would be sent to Shakers and sends it only if you approve: up to 40 literal excerpts of your own messages (corrections and decisions) and 20 commands, 200 characters max each; the configuration and definitions of your agents; and activity counts. Anything that looks like a secret, an email or a path is removed first, 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). An AI model from an external provider (OpenAI, Google or Anthropic) analyses it 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. Human review: "Request human review" on the web or talent@shakersworks.com; to ask for deletion, the same email.',
114
+ ai_usage_result: 'reads from your Shakers account the result of the analysis you approved. It reads nothing on this computer and changes nothing.',
115
+ logout: 'deletes the Shakers session saved on this computer (~/.config/shakers) and tells Shakers you are signing out. It does not change your profile.',
116
+ list_addable: 'reads from your Shakers account the skills and AI agents found in the analysis you sent. It changes nothing.',
117
+ add_skill: 'adds the skill you pick to your Shakers profile. It reads nothing on this computer.',
118
+ add_agent: 'adds the AI agent you pick, with the description you agree on, to your Shakers profile. It reads nothing else on this computer.',
119
+ add_detected_agents: 'adds the detected AI agents you pick to your Shakers profile, only their name and description, and clients will see them on your profile. It reads or uploads nothing else from this computer. You can remove them any time from your profile on the Shakers website.',
120
+ add_project: 'asks you first and, if you say yes, reads the project folder name, the web address of its code and the dates of its first and last change, and sends them to your Shakers portfolio. It does not send your code.',
121
+ suggest_register_context: 'asks you first and, if you say yes, looks at the names of PDF and Word files in Desktop, Documents, Downloads and iCloud Drive that look like a CV, your time zone and the name, email and GitHub user saved by your coding tools. It does not open those files or send anything.',
122
+ read_cv: 'asks you first and, if you say yes, reads the text of the CV file you allow to prepare your profile draft. It sends nothing to Shakers.',
123
+ signup: "runs your Shakers sign-up step by step here in the chat. It asks you before reading anything on this computer or sending your data to Shakers, and creates no account until you confirm your draft.",
124
+ signup_start: 'starts your sign-up here in the chat. It does not read anything on your computer or send anything yet.',
125
+ signup_email: 'asks which email to create your account with. Nothing is sent yet.',
126
+ signup_draft: 'shows the profile draft prepared from your CV and LinkedIn. No account is created and nothing is sent until you confirm.',
127
+ signup_create_account: 'after your confirmation and your acceptance of the Terms, sends your LinkedIn, CV and draft to Shakers to prepare your profile, and opens in your browser a Shakers page (shakersworks.com) with a code where you create the account. Your password never goes through this chat.',
128
+ signup_status: 'checks on Shakers whether your account exists and how your profile import is going. It changes nothing.',
129
+ update_existing_profile: 'shows what your draft would fill in the empty fields of your profile and saves it only if you say yes. It never overwrites what you already had.',
130
+ import_profile: 'sends your LinkedIn, GitHub or website to Shakers to fill the empty parts of your profile; a CV file from this computer is sent only if you say yes. What you already wrote is kept.',
131
+ save_profile_details: 'saves the details you confirmed (situation, rates, availability, location, languages and phone) to your Shakers profile. On an existing account it only fills empty fields.',
132
+ open_web: 'opens Shakers in your browser already signed in, with a single-use link that expires in 2 minutes. It changes nothing.',
133
+ open_linkedin_profile: 'opens your LinkedIn profile in your browser so you can copy its address. It reads and sends nothing.',
134
+ social_signin_start: 'prepares the sign-in: it gives you a link to a Shakers page (shakersworks.com) and a code. There you check the code matches and approve. Your password is typed only on the Shakers website, never in this chat. If you did not ask for this access, deny it.',
135
+ social_signin_poll: 'checks whether you approved the access and, if so, saves the session only on this computer (~/.config/shakers). From then on this chat can read your profile and save to it what you confirm. To end it: ask me to sign out.',
136
+ onboarding_interview_start: 'starts the onboarding interview after asking where and how you want to answer. Your answers are sent to Shakers to get to know your profile. It reads nothing on your computer.',
137
+ onboarding_interview_turn: 'sends Shakers the answer you gave or approved and brings the next question.',
138
+ onboarding_interview_complete: 'closes the interview and saves it on Shakers.',
139
+ repeat_onboarding_interview: 'deletes your previous onboarding interview on Shakers so you can take it again. This cannot be undone.',
140
+ onboarding_finish: 'marks your onboarding as completed on Shakers.',
141
+ list_available_roles: 'reads from Shakers the roles you can add to your profile. It changes nothing.',
142
+ add_role: 'adds the role you pick to your Shakers profile.',
143
+ list_my_roles: 'reads your roles on Shakers and the ones suggested by your interview and CV. It changes nothing.',
144
+ set_main_role: 'asks which role is your main one and saves it to your Shakers profile.',
145
+ report: 'shows your AI report saved on Shakers or the one from your last analysis on this computer, and saves it as a file on this computer.',
146
+ share: 'creates on this computer a card with your AI report to share on LinkedIn. It sends nothing.',
147
+ list_certifiable_dimensions: 'reads from Shakers which areas of your main role you can certify. It changes nothing.',
148
+ find_projects: 'reads from Shakers the open projects that fit you best. It changes nothing.',
149
+ show_project: "reads a project's details from Shakers. It changes nothing.",
150
+ prepare_application: "reads the application questions of a project from Shakers. It sends or changes nothing.",
151
+ apply_to_project: 'sends your message and your answers for the project you pick to Shakers, only after showing you the summary and you saying yes: the client will see your Shakers profile and a chat with the team opens. If the project asks for it, Shakers reviews your application before the client sees it. It does not change your profile.',
152
+ save_project: 'saves that project to your saved projects on Shakers.',
153
+ unsave_project: 'removes that project from your saved projects on Shakers.',
154
+ list_invitations: 'reads your project invitations on Shakers. It changes nothing.',
155
+ list_applications: 'reads your applications on Shakers. It changes nothing.',
156
+ get_availability: 'reads your availability from your Shakers profile. It changes nothing.',
157
+ list_certifications: 'reads your Shakers certifications. It changes nothing.',
158
+ get_profile: 'reads your Shakers profile. It changes nothing.',
159
+ get_rate: 'reads your rate from your Shakers profile. It changes nothing.',
160
+ get_languages: 'reads your languages from your Shakers profile. It changes nothing.',
161
+ list_socials: 'reads your social links from your Shakers profile. It changes nothing.',
162
+ list_experiences: 'reads your experiences from your Shakers profile. It changes nothing.',
163
+ list_portfolios: 'reads your portfolio projects from your Shakers profile. It changes nothing.',
164
+ alma_chat: 'sends your message to Alma, the Shakers assistant, and shows her answer. If she proposes a change to your profile, you are asked before it is made.',
165
+ alma_decision: 'sends Shakers your yes or no to the profile change Alma proposed; only a yes changes your profile.',
166
+ },
167
+ },
168
+ it: {
169
+ heading: 'Cosa fa Shakers in questo passaggio:',
170
+ failed: "Questo passaggio non è stato completato; non è stato cambiato nient'altro.",
171
+ mcp: {
172
+ ai_usage: 'se dici di sì, analizza su questo computer i tuoi strumenti di IA, la cronologia dei tuoi progetti di codice e le tue conversazioni con assistenti di programmazione con IA (Claude Code, Codex, Gemini, Cursor, Continue, Cline/Roo, Aider, Pi, OpenCode). Nella registrazione si analizzano tutte le conversazioni di questo computer; se analizzi un repo specifico, solo quelle di quel repo. Non viene ancora inviato nulla: prima ti mostro cosa verrebbe inviato e decidi tu. Non usarlo su un computer né con conversazioni che contengono dati che non puoi condividere (di clienti, del tuo datore di lavoro o di terzi).',
173
+ ai_usage_upload: "ti mostra cosa verrebbe inviato a Shakers e lo invia solo se lo approvi: fino a 40 estratti letterali dei tuoi messaggi (correzioni e decisioni) e 20 comandi, di massimo 200 caratteri ciascuno; la configurazione e le definizioni dei tuoi agenti; e conteggi di attività. Prima si rimuove ciò che sembra un segreto, un'email o un percorso, ma il filtro è approssimativo: non approvarlo se ci sono dati che non puoi condividere (di clienti, del tuo datore di lavoro o di terzi). Un modello di IA di un fornitore esterno (OpenAI, Google o Anthropic) lo analizza per conto di Shakers per calcolare la tua valutazione e poi viene scartato; si conserva solo il risultato: livelli, punteggi, conteggi e i nomi dei tuoi strumenti e agenti. Revisione umana: «Richiedi una revisione umana» sul sito o talent@shakersworks.com; per chiederne la cancellazione, la stessa email.",
174
+ ai_usage_result: "legge dal tuo account Shakers il risultato dell'analisi che hai approvato. Non legge nulla di questo computer e non cambia nulla.",
175
+ logout: 'cancella la sessione di Shakers salvata su questo computer (~/.config/shakers) e avvisa Shakers che la chiudi. Non cambia nulla del tuo profilo.',
176
+ list_addable: "legge dal tuo account Shakers le skill e gli agenti di IA trovati nell'analisi che hai inviato. Non cambia nulla.",
177
+ add_skill: 'aggiunge al tuo profilo Shakers la skill che scegli. Non legge nulla di questo computer.',
178
+ add_agent: "aggiunge al tuo profilo Shakers l'agente di IA che scegli, con la descrizione concordata. Non legge nient'altro di questo computer.",
179
+ add_detected_agents: "aggiunge al tuo profilo Shakers gli agenti di IA rilevati che scegli, solo il nome e la descrizione, e i clienti li vedranno nel tuo profilo. Non legge né carica nient'altro da questo computer. Puoi toglierli quando vuoi dal tuo profilo sul sito di Shakers.",
180
+ add_project: "ti chiede prima e, se dici di sì, legge il nome della cartella del progetto, l'indirizzo web del suo codice e le date della prima e dell'ultima modifica, e li invia al tuo portfolio Shakers. Non invia il tuo codice.",
181
+ suggest_register_context: "ti chiede prima e, se dici di sì, guarda i nomi dei PDF e Word in Scrivania, Documenti, Download e iCloud Drive che sembrano un CV, il tuo fuso orario e il nome, l'email e l'utente GitHub dei tuoi strumenti di programmazione. Non apre quei file e non invia nulla.",
182
+ read_cv: 'ti chiede prima e, se dici di sì, legge il testo del CV che permetti per preparare la bozza del tuo profilo. Non invia nulla a Shakers.',
183
+ signup: "porta avanti la tua registrazione a Shakers passo dopo passo qui in chat. Ti chiede prima di leggere qualcosa da questo computer o di inviare i tuoi dati a Shakers, e non crea l'account finché non confermi la bozza.",
184
+ signup_start: 'inizia la tua registrazione qui in chat. Non legge ancora nulla dal tuo computer e non invia nulla.',
185
+ signup_email: 'ti chiede con quale email creare il tuo account. Non viene ancora inviato nulla.',
186
+ signup_draft: "ti mostra la bozza del tuo profilo preparata con il tuo CV e il tuo LinkedIn. Non crea l'account e non invia nulla finché non confermi.",
187
+ signup_create_account: "dopo la tua conferma e la tua accettazione dei Termini, invia a Shakers il tuo LinkedIn, il tuo CV e la bozza per preparare il profilo, e apre nel browser una pagina di Shakers (shakersworks.com) con un codice dove crei l'account. La tua password non passa mai da questa chat.",
188
+ signup_status: "controlla su Shakers se il tuo account esiste già e come procede l'importazione del profilo. Non cambia nulla.",
189
+ update_existing_profile: 'ti mostra cosa riempirebbe la bozza nei campi vuoti del tuo profilo e lo salva solo se dici di sì. Non sovrascrive mai ciò che avevi già.',
190
+ import_profile: 'invia a Shakers il tuo LinkedIn, GitHub o sito per riempire le parti vuote del profilo; un CV di questo computer si invia solo se dici di sì. Ciò che hai già scritto resta.',
191
+ save_profile_details: 'salva nel tuo profilo Shakers i dati che hai confermato (situazione, tariffe, disponibilità, località, lingue e telefono). In un account esistente riempie solo i campi vuoti.',
192
+ open_web: 'apre Shakers nel browser con la sessione già avviata, con un link monouso che scade in 2 minuti. Non cambia nulla.',
193
+ open_linkedin_profile: "apre il tuo profilo LinkedIn nel browser perché tu ne copi l'indirizzo. Non legge né invia nulla.",
194
+ social_signin_start: "prepara l'accesso: ti dà un link a una pagina di Shakers (shakersworks.com) e un codice. Lì controlli che il codice coincida e approvi. La tua password si scrive solo sul sito di Shakers, mai in questa chat. Se non hai chiesto tu questo accesso, rifiutalo.",
195
+ social_signin_poll: "controlla se hai già approvato l'accesso e, in tal caso, salva la sessione solo su questo computer (~/.config/shakers). Da allora questa chat può leggere il tuo profilo e salvarci ciò che confermi. Per chiuderla: chiedimi di uscire.",
196
+ onboarding_interview_start: 'avvia il colloquio di onboarding dopo averti chiesto dove e come rispondere. Le tue risposte vengono inviate a Shakers per conoscere il tuo profilo. Non legge nulla dal tuo computer.',
197
+ onboarding_interview_turn: 'invia a Shakers la risposta che hai dato o approvato e porta la domanda successiva.',
198
+ onboarding_interview_complete: 'chiude il colloquio e lo salva su Shakers.',
199
+ repeat_onboarding_interview: 'cancella su Shakers il tuo colloquio di onboarding precedente per poterlo ripetere. Non si può annullare.',
200
+ onboarding_finish: 'segna il tuo onboarding come completato su Shakers.',
201
+ list_available_roles: 'legge da Shakers i ruoli che puoi aggiungere al tuo profilo. Non cambia nulla.',
202
+ add_role: 'aggiunge al tuo profilo Shakers il ruolo che scegli.',
203
+ list_my_roles: 'legge i tuoi ruoli su Shakers e quelli suggeriti dal colloquio e dal CV. Non cambia nulla.',
204
+ set_main_role: 'ti chiede qual è il tuo ruolo principale e lo salva nel tuo profilo Shakers.',
205
+ report: 'mostra il tuo report di IA salvato su Shakers o quello della tua ultima analisi su questo computer, e lo salva come file su questo computer.',
206
+ share: 'crea su questo computer una scheda con il tuo report di IA da condividere su LinkedIn. Non invia nulla.',
207
+ list_certifiable_dimensions: 'legge da Shakers quali aree del tuo ruolo principale puoi certificare. Non cambia nulla.',
208
+ find_projects: 'legge da Shakers i progetti aperti più adatti a te. Non cambia nulla.',
209
+ show_project: 'legge da Shakers il dettaglio di un progetto. Non cambia nulla.',
210
+ prepare_application: 'legge da Shakers le domande della candidatura a un progetto. Non invia né cambia nulla.',
211
+ apply_to_project: "invia a Shakers il tuo messaggio e le tue risposte per il progetto che scegli, solo dopo averti mostrato il riepilogo e che tu abbia detto sì: il cliente vedrà il tuo profilo Shakers e si apre una chat con il team. Se il progetto lo richiede, Shakers rivede la tua candidatura prima che la veda il cliente. Non cambia il tuo profilo.",
212
+ save_project: 'salva quel progetto tra i tuoi progetti salvati su Shakers.',
213
+ unsave_project: 'toglie quel progetto dai tuoi progetti salvati su Shakers.',
214
+ list_invitations: 'legge i tuoi inviti a progetti su Shakers. Non cambia nulla.',
215
+ list_applications: 'legge le tue candidature su Shakers. Non cambia nulla.',
216
+ get_availability: 'legge la tua disponibilità dal tuo profilo Shakers. Non cambia nulla.',
217
+ list_certifications: 'legge le tue certificazioni Shakers. Non cambia nulla.',
218
+ get_profile: 'legge il tuo profilo Shakers. Non cambia nulla.',
219
+ get_rate: 'legge la tua tariffa dal tuo profilo Shakers. Non cambia nulla.',
220
+ get_languages: 'legge le tue lingue dal tuo profilo Shakers. Non cambia nulla.',
221
+ list_socials: 'legge i tuoi social dal tuo profilo Shakers. Non cambia nulla.',
222
+ list_experiences: 'legge le tue esperienze dal tuo profilo Shakers. Non cambia nulla.',
223
+ list_portfolios: 'legge i progetti del tuo portfolio dal tuo profilo Shakers. Non cambia nulla.',
224
+ alma_chat: "invia il tuo messaggio ad Alma, l'assistente di Shakers, e ti mostra la sua risposta. Se propone una modifica al tuo profilo, ti chiede prima di farla.",
225
+ alma_decision: 'invia a Shakers il tuo sì o il tuo no alla modifica del profilo proposta da Alma; solo un sì cambia il tuo profilo.',
226
+ },
227
+ },
228
+ pt: {
229
+ heading: 'O que a Shakers faz neste passo:',
230
+ failed: 'Este passo não foi concluído; não se alterou mais nada.',
231
+ mcp: {
232
+ ai_usage: 'se disser que sim, analisa neste computador as suas ferramentas de IA, o histórico dos seus projetos de código e as suas conversas com assistentes de programação com IA (Claude Code, Codex, Gemini, Cursor, Continue, Cline/Roo, Aider, Pi, OpenCode). No registo analisam-se todas as conversas deste computador; se analisar um repositório concreto, só as desse repositório. Ainda não se envia nada: primeiro mostro-lhe o que seria enviado e decide você. Não o use num computador nem com conversas que tenham dados que não pode partilhar (de clientes, da sua empresa ou de terceiros).',
233
+ ai_usage_upload: 'mostra-lhe o que seria enviado para a Shakers e só o envia se o aprovar: até 40 excertos literais das suas próprias mensagens (correções e decisões) e 20 comandos, com 200 caracteres no máximo cada um; a configuração e as definições dos seus agentes; e contagens de atividade. Antes retira-se o que parece um segredo, um email ou um caminho, mas o filtro é aproximado: não o aprove se houver dados que não pode partilhar (de clientes, da sua empresa ou de terceiros). Um modelo de IA de um fornecedor externo (OpenAI, Google ou Anthropic) analisa-o por conta da Shakers para calcular a sua avaliação e depois é descartado; só se guarda o resultado: níveis, pontuações, contagens e os nomes das suas ferramentas e agentes. Revisão humana: «Solicitar revisão humana» na web ou talent@shakersworks.com; para pedir que seja apagado, o mesmo email.',
234
+ ai_usage_result: 'lê da sua conta Shakers o resultado da análise que aprovou. Não lê nada deste computador nem altera nada.',
235
+ logout: 'apaga a sessão da Shakers guardada neste computador (~/.config/shakers) e avisa a Shakers de que a fecha. Não altera o seu perfil.',
236
+ list_addable: 'lê da sua conta Shakers as skills e os agentes de IA encontrados na análise que enviou. Não altera nada.',
237
+ add_skill: 'adiciona ao seu perfil Shakers a skill que escolher. Não lê nada deste computador.',
238
+ add_agent: 'adiciona ao seu perfil Shakers o agente de IA que escolher, com a descrição combinada. Não lê mais nada deste computador.',
239
+ add_detected_agents: 'adiciona ao seu perfil Shakers os agentes de IA detetados que escolher, apenas o nome e a descrição, e os clientes vão vê-los no seu perfil. Não lê nem envia mais nada deste computador. Pode removê-los quando quiser a partir do seu perfil na web da Shakers.',
240
+ add_project: 'pergunta-lhe antes e, se disser que sim, lê o nome da pasta do projeto, o endereço web do código e as datas da primeira e da última alteração, e envia-os para o seu portfolio Shakers. Não envia o seu código.',
241
+ suggest_register_context: 'pergunta-lhe antes e, se disser que sim, vê os nomes dos PDF e Word em Secretária, Documentos, Transferências e iCloud Drive que pareçam um CV, o seu fuso horário e o nome, email e utilizador GitHub das suas ferramentas de programação. Não abre esses ficheiros nem envia nada.',
242
+ read_cv: 'pergunta-lhe antes e, se disser que sim, lê o texto do CV que autorizar para preparar o rascunho do seu perfil. Não envia nada para a Shakers.',
243
+ signup: "conduz o seu registo na Shakers passo a passo aqui no chat. Pergunta-lhe antes de ler algo deste computador ou de enviar os seus dados para a Shakers, e não cria a conta até confirmar o rascunho.",
244
+ signup_start: 'começa o seu registo aqui no chat. Ainda não lê nada do seu computador nem envia nada.',
245
+ signup_email: 'pergunta-lhe com que email criar a sua conta. Ainda não se envia nada.',
246
+ signup_draft: 'mostra-lhe o rascunho do seu perfil preparado com o seu CV e o seu LinkedIn. Não cria a conta nem envia nada até confirmar.',
247
+ signup_create_account: 'depois da sua confirmação e da sua aceitação dos Termos, envia para a Shakers o seu LinkedIn, o seu CV e o rascunho para preparar o perfil, e abre no navegador uma página da Shakers (shakersworks.com) com um código onde cria a conta. A sua palavra-passe nunca passa por este chat.',
248
+ signup_status: 'consulta na Shakers se a sua conta já existe e como vai a importação do perfil. Não altera nada.',
249
+ update_existing_profile: 'mostra-lhe o que o rascunho preencheria nos campos vazios do seu perfil e só guarda se disser que sim. Nunca substitui o que já tinha.',
250
+ import_profile: 'envia para a Shakers o seu LinkedIn, GitHub ou site para preencher o que está vazio no perfil; um CV deste computador só é enviado se disser que sim. O que já escreveu mantém-se.',
251
+ save_profile_details: 'guarda no seu perfil Shakers os dados que confirmou (situação, tarifas, disponibilidade, localização, idiomas e telefone). Numa conta existente só preenche o que está vazio.',
252
+ open_web: 'abre a Shakers no navegador com a sessão iniciada, com um link de utilização única que expira em 2 minutos. Não altera nada.',
253
+ open_linkedin_profile: 'abre o seu perfil do LinkedIn no navegador para copiar o endereço. Não lê nem envia nada.',
254
+ social_signin_start: 'prepara o acesso: dá-lhe um link para uma página da Shakers (shakersworks.com) e um código. Lá confirma que o código coincide e aprova. A sua palavra-passe só se escreve no site da Shakers, nunca neste chat. Se não foi você que pediu este acesso, recuse-o.',
255
+ social_signin_poll: 'verifica se já aprovou o acesso e, nesse caso, guarda a sessão só neste computador (~/.config/shakers). A partir daí este chat pode ler o seu perfil e guardar nele o que confirmar. Para a fechar: peça-me para terminar a sessão.',
256
+ onboarding_interview_start: 'começa a entrevista de onboarding depois de lhe perguntar onde e como responder. As suas respostas são enviadas para a Shakers para conhecer o seu perfil. Não lê nada do seu computador.',
257
+ onboarding_interview_turn: 'envia para a Shakers a resposta que deu ou aprovou e traz a pergunta seguinte.',
258
+ onboarding_interview_complete: 'fecha a entrevista e guarda-a na Shakers.',
259
+ repeat_onboarding_interview: 'apaga na Shakers a sua entrevista de onboarding anterior para a poder repetir. Não se pode desfazer.',
260
+ onboarding_finish: 'marca o seu onboarding como concluído na Shakers.',
261
+ list_available_roles: 'lê da Shakers as funções que pode adicionar ao seu perfil. Não altera nada.',
262
+ add_role: 'adiciona ao seu perfil Shakers a função que escolher.',
263
+ list_my_roles: 'lê as suas funções na Shakers e as sugeridas pela entrevista e pelo CV. Não altera nada.',
264
+ set_main_role: 'pergunta-lhe qual é a sua função principal e guarda-a no seu perfil Shakers.',
265
+ report: 'mostra o seu relatório de IA guardado na Shakers ou o da sua última análise neste computador, e guarda-o como ficheiro neste computador.',
266
+ share: 'cria neste computador um cartão com o seu relatório de IA para partilhar no LinkedIn. Não envia nada.',
267
+ list_certifiable_dimensions: 'lê da Shakers que áreas da sua função principal pode certificar. Não altera nada.',
268
+ find_projects: 'lê da Shakers os projetos abertos que mais se ajustam a si. Não altera nada.',
269
+ show_project: 'lê da Shakers o detalhe de um projeto. Não altera nada.',
270
+ prepare_application: 'lê da Shakers as perguntas da candidatura a um projeto. Não envia nem altera nada.',
271
+ apply_to_project: 'envia à Shakers a sua mensagem e as suas respostas para o projeto que escolher, só depois de lhe mostrar o resumo e de dizer que sim: o cliente verá o seu perfil Shakers e abre-se um chat com a equipa. Se o projeto o pedir, a Shakers revê a sua candidatura antes de o cliente a ver. Não altera o seu perfil.',
272
+ save_project: 'guarda esse projeto nos seus projetos guardados na Shakers.',
273
+ unsave_project: 'retira esse projeto dos seus projetos guardados na Shakers.',
274
+ list_invitations: 'lê os seus convites para projetos na Shakers. Não altera nada.',
275
+ list_applications: 'lê as suas candidaturas na Shakers. Não altera nada.',
276
+ get_availability: 'lê a sua disponibilidade do seu perfil Shakers. Não altera nada.',
277
+ list_certifications: 'lê as suas certificações Shakers. Não altera nada.',
278
+ get_profile: 'lê o seu perfil Shakers. Não altera nada.',
279
+ get_rate: 'lê a sua tarifa do seu perfil Shakers. Não altera nada.',
280
+ get_languages: 'lê os seus idiomas do seu perfil Shakers. Não altera nada.',
281
+ list_socials: 'lê as suas redes do seu perfil Shakers. Não altera nada.',
282
+ list_experiences: 'lê as suas experiências do seu perfil Shakers. Não altera nada.',
283
+ list_portfolios: 'lê os projetos do seu portfolio do seu perfil Shakers. Não altera nada.',
284
+ alma_chat: 'envia a sua mensagem à Alma, a assistente da Shakers, e mostra-lhe a resposta. Se propuser uma alteração ao seu perfil, pergunta-lhe antes de a fazer.',
285
+ alma_decision: 'envia para a Shakers o seu sim ou não à alteração de perfil que a Alma propôs; só um sim altera o seu perfil.',
286
+ },
287
+ },
288
+ };
289
+
290
+ const LEGAL_BASE_URL = 'https://www.shakersworks.com';
291
+ const LEGAL_PREFIXES = { es: '', en: 'en/', pt: 'pt/', it: 'it/' };
292
+
293
+ function termsLinks(lang) {
294
+ const code = String(lang || 'en').toLowerCase().split('-')[0];
295
+ const prefix = code in LEGAL_PREFIXES ? LEGAL_PREFIXES[code] : 'en/';
296
+ return { terms: `${LEGAL_BASE_URL}/${prefix}condiciones-generales`, privacy: `${LEGAL_BASE_URL}/${prefix}politica-de-privacidad` };
297
+ }
298
+
299
+ function termsText(lang, link) {
300
+ const code = String(lang || 'en').toLowerCase().split('-')[0];
301
+ const legal = LEGAL[code] || LEGAL.en;
302
+ const links = termsLinks(code);
303
+ return legal.terms((label) => link(label, links.terms), (label) => link(label, links.privacy));
304
+ }
305
+
39
306
  const LOCALES = { es: 'es-ES', en: 'en-GB', it: 'it-IT', pt: 'pt-PT' };
40
307
 
41
308
  // "a, b and c" in the talent's language.
@@ -68,15 +335,14 @@ function amount(lang, value) {
68
335
 
69
336
  const COPY = {
70
337
  es: {
71
- welcome: (name) => `¡Hola${name ? `, ${name}` : ''}! Gracias por querer unirte a Shakers 🙌\n\nSomos una comunidad de profesionales tech freelance. Nuestro objetivo es tener siempre tu versión más actual como profesional (lo que sabes hacer hoy, cómo trabajas con IA y qué buscas) para conectarte con proyectos que de verdad encajen contigo.\n\nEl alta son tres pasos y la mayor parte la hago yo por ti:\n1. Tu perfil (2 min). Lo relleno a partir de tu CV y LinkedIn, tú solo revisas y confirmas.\n2. Cómo trabajas con IA (1-2 min, opcional). Analizo qué herramientas usas en este equipo. Tu código y tus prompts nunca salen de tu máquina.\n3. Una breve entrevista (opcional). Para afinar tu rol y lo que buscas. Puedes hacerla ahora o más tarde.\n\nTodo lo puedes editar después desde la web.`,
72
- welcomeQuestion: '¿Empezamos? Pásame tu CV, o si lo tienes en este equipo, te lo busco yo. Con él te preparo un borrador para que solo tengas que confirmarlo.',
73
- cvSearch: 'Búscalo en mi equipo',
74
- cvAttach: 'Te lo paso',
75
- cvAttachAsk: 'Perfecto, adjúntalo aquí o dime dónde está en tu equipo.',
76
- cvNotFound: 'No he encontrado tu CV en este equipo. ¿Me lo adjuntas aquí?',
338
+ welcome: (name) => `¡Hola${name ? `, ${name}` : ''}! Gracias por querer unirte a Shakers 🙌\n\nSomos una comunidad de profesionales tech freelance. Nuestro objetivo es tener siempre tu versión más actual como profesional (lo que sabes hacer hoy, cómo trabajas con IA y qué buscas) para conectarte con proyectos que de verdad encajen contigo.\n\nEl alta son tres pasos y la mayor parte la hago yo por ti:\n1. Tu perfil (2 min). Lo relleno a partir de tu CV y LinkedIn, tú solo revisas y confirmas.\n2. Cómo trabajas con IA (1-2 min, opcional). Analizo tus herramientas de IA, el historial de tus proyectos y tus conversaciones con asistentes de IA en este ordenador. Antes de enviar nada a Shakers te enseño qué se enviaría y tú decides.\n3. Una breve entrevista (opcional). Para afinar tu rol y lo que buscas. Puedes hacerla ahora o más tarde.\n\nTodo lo puedes editar después desde la web.`,
339
+ welcomeQuestion: 'Pásame tu CV y tu LinkedIn, o si lo prefieres, busco tu CV en este ordenador.',
340
+ cvAttachAsk: 'Perfecto, adjúntalo aquí o dime dónde está en tu ordenador.',
341
+ cvNotFound: 'No he encontrado tu CV en este ordenador. ¿Me lo adjuntas aquí?',
77
342
  linkedinAsk: '¿Me pasas tu perfil de LinkedIn?',
78
343
  linkedinOpening: 'Te abro tu perfil de LinkedIn en el navegador para que copies la dirección.',
79
344
  linkedinOpened: 'Te he abierto tu perfil de LinkedIn; copia la dirección de la barra y pégamela aquí.',
345
+ webNext: "En la web puedes revisar tu perfil y ver los proyectos que encajan contigo.",
80
346
  emailKnown: (email) => `¿Te registro con ${email}? Usa el email que más mires: ahí te llegarán los proyectos que encajen contigo y las novedades de Shakers.`,
81
347
  emailUnknown: '¿Con qué email te registro? Usa el que más mires: ahí te llegarán los proyectos que encajen contigo y las novedades de Shakers.',
82
348
  emailWorkHint: 'Si es el de tu trabajo actual, quizá prefieras uno personal para no perder el acceso si cambias de empresa.',
@@ -86,12 +352,20 @@ const COPY = {
86
352
  years: (n) => `${n} años de experiencia`,
87
353
  hours: (h) => `${h} h/mes`,
88
354
  rates: (hourly, annual, estimated = true) => (estimated ? `Tarifa estimada: ${joinParts([hourly != null ? `${amount('es', hourly)} €/h` : null, annual != null ? `${amount('es', annual)} €/año` : null])} (estimación mía, ajústala)` : `Tarifa: ${joinParts([hourly != null ? `${amount('es', hourly)} €/h` : null, annual != null ? `${amount('es', annual)} €/año` : null])}`),
89
- draftQuestion: '¿Está bien o cambio algo?',
90
- draftOk: 'Todo correcto',
355
+ draftQuestion: '¿Creo tu cuenta en Shakers con estos datos o cambio algo?',
356
+ draftOk: 'Sí, crea mi cuenta en Shakers con estos datos',
91
357
  draftChange: 'Quiero cambiar algo',
92
- windowNotice: 'Cuando me confirmes, solo faltará crear tu cuenta para guardar todo esto y que puedas entrar en Shakers cuando quieras.\n\nTe abro una ventana en el navegador, elige cómo entrar:\n· Con Google\n· Con email y contraseña\n\nLa contraseña la escribes allí directamente, nunca pasa por este chat. En esa misma pantalla aceptas los términos y la política de privacidad.\n\n👉 Mira tu navegador. Cuando termines, vuelve aquí y seguimos.',
93
- windowNoticeSignIn: 'Cuando me confirmes, solo faltará entrar en la cuenta que ya tienes en Shakers con este email para guardar todo esto.\n\nTe abro una ventana en el navegador, elige cómo entrar:\n· Con Google\n· Con email y contraseña\n\nLa contraseña la escribes allí directamente, nunca pasa por este chat.\n\n👉 Mira tu navegador. Cuando termines, vuelve aquí y seguimos.',
94
- windowLink: (link) => `Si no se te ha abierto la ventana, entra desde aquí: ${link}`,
358
+ windowNotice: "Solo falta crear tu cuenta para guardar todo esto y que puedas entrar en Shakers cuando quieras. La creas en la web de Shakers (shakersworks.com) con Google, LinkedIn o email y contraseña.\n\nLa contraseña solo se escribe en la web de Shakers, nunca pasa por este chat. Si no has pedido tú este acceso, deniégalo.",
359
+ windowNoticeSignIn: "Solo falta entrar en la cuenta que ya tienes en Shakers con este email para guardar todo esto. Entras en la web de Shakers (shakersworks.com) como siempre: Google, LinkedIn o email y contraseña.\n\nLa contraseña solo se escribe en la web de Shakers, nunca pasa por este chat. Si no has pedido tú este acceso, deniégalo.",
360
+ windowBack: "👉 Mira tu navegador. Cuando termines, vuelve aquí y seguimos.",
361
+ windowLink: (link, code) => `Te abro esta página de Shakers en el navegador; si no se abre, entra desde aquí: ${link}\n\nCódigo: ${code}\nComprueba que la página muestra este mismo código y pulsa Aprobar.`,
362
+ termsQuestion: '¿Aceptas para crear tu cuenta?',
363
+ termsAccept: 'Acepto',
364
+ termsDecline: 'No acepto',
365
+ termsDeclined: 'Sin aceptar los Términos y la Política de Privacidad no puedo crear tu cuenta. No he creado nada ni he enviado tus datos a Shakers. Si cambias de idea, dímelo y seguimos.',
366
+ newsletterQuestion: 'Opcional: ¿quieres recibir nuestro contenido por email? Puedes cambiarlo cuando quieras.',
367
+ newsletterNo: 'No, gracias',
368
+ accountReady: (created) => `${created ? 'Tu cuenta ya está creada.' : 'Ya has entrado en tu cuenta de Shakers.'} Desde ahora, en este ordenador, puedo usar tu cuenta para guardar en tu perfil lo que me confirmes; nada más se envía sin preguntarte. La sesión se guarda solo en este ordenador; para cerrarla, pídeme que cierre la sesión de Shakers.`,
95
369
  existingTitle: 'He visto que ya tienes cuenta. ¿Actualizo tu perfil con esto?',
96
370
  existingRate: (from, to) => `Tarifa por hora: ${from} → ${to}`,
97
371
  existingAnnual: (from, to) => `Tarifa anual: ${from} → ${to}`,
@@ -108,13 +382,18 @@ const COPY = {
108
382
  situationLabels: { FREELANCE: 'Freelance', EMPLOYED: 'Empleado/a', BETWEEN_JOBS: 'Entre proyectos', STUDYING: 'Estudiando', OTHER: 'Otro' },
109
383
  participationLabels: { FULL_TIME: 'jornada completa', PART_TIME: 'media jornada' },
110
384
  existingUnchanged: (items) => `Estos datos de tu perfil no los he cambiado, se quedan como estaban: ${items.join(' · ')}. Si quieres otros valores, cámbialos en la web.`,
111
- aiUsageQuestion: '¿Quieres que analice cómo trabajas con IA? Es opcional y tarda 1-2 min. Solo veo qué herramientas usas, tu código y tus prompts no salen de tu máquina. Miro el uso de IA de todo este equipo, que doy por tuyo. Te ayuda a destacar en proyectos de IA.',
385
+ aiUsageQuestion: "¿Quieres que analice cómo trabajas con IA? Es opcional y tarda 1-2 min. Miro el uso de IA de todo este equipo, que doy por tuyo, y antes de subir nada te enseño lo que se enviaría. Te ayuda a destacar en proyectos de IA.",
112
386
  aiUsageYes: 'Sí, analízalo',
113
387
  aiUsageSkip: 'Saltar este paso',
114
388
  aiUsageSkipped: "Sin problema, seguimos sin él. Te aviso de que es lo que más pesa para que te lleguen proyectos: lo puedes lanzar cuando quieras diciéndome 'analiza mi uso de IA'.",
389
+ aiUsageAsk: "¿Quieres que analice cómo trabajas con IA? Es opcional y tarda 1-2 min. Te ayuda a destacar en proyectos de IA.",
390
+ aiUsageStart: "¿Empiezo el análisis?",
115
391
  consent: {
116
392
  yes: 'Sí, adelante',
117
393
  no: 'No',
394
+ cvSearchYes: 'Sí, busca mi CV en este ordenador para mi alta en Shakers',
395
+ cvReadYes: (file) => `Sí, lee mi CV (${file}) para mi alta en Shakers`,
396
+ cvSendYes: (file) => `Sí, envía mi CV (${file}) a mi cuenta de Shakers`,
118
397
  cvSearch: 'Shakers buscará en este ordenador archivos que puedan ser tu CV: mira los nombres de los archivos PDF y Word de tus carpetas Escritorio, Documentos, Descargas e iCloud Drive, y lee tu zona horaria y el nombre, el email y el usuario de GitHub que guardan tus herramientas de programación. No abre esos archivos y no envía nada. ¿Continuamos?',
119
398
  cvRead: (file) => `Shakers leerá el archivo ${file} para preparar el borrador de tu perfil. Todavía no se envía nada. ¿Continuamos?`,
120
399
  cvSend: (file) => `Shakers enviará el archivo ${file} a tu cuenta de Shakers para completar tu perfil. ¿Continuamos?`,
@@ -130,10 +409,10 @@ const COPY = {
130
409
  answerModeOwn: 'Las contesto yo',
131
410
  answerModeFull: 'Hazla entera',
132
411
  interviewOnWeb: 'La entrevista de onboarding la puedes hacer en la web: al terminar te abro Shakers con tu sesión iniciada.',
133
- livekitUnavailable: 'La entrevista no puede arrancar en este equipo. Puedes hacerla en la web: te abro Shakers con tu sesión iniciada.',
412
+ livekitUnavailable: 'La entrevista no puede arrancar en este ordenador. Puedes hacerla en la web: te abro Shakers con tu sesión iniciada.',
134
413
  rolesIntro: 'Con todo lo que me has contado, encajas en estos perfiles:',
135
- roleRecommended: (name) => `**${name}** (recomendado)`,
136
- roleName: (name) => `**${name}**`,
414
+ roleRecommended: (name) => `${name} (recomendado)`,
415
+ roleName: (name) => name,
137
416
  roleReason: { IMPORT: 'Por tu CV y tu LinkedIn', ONBOARDING_INTERVIEW: 'Por lo que me contaste en la entrevista', MATCH: 'Por tu perfil en Shakers' },
138
417
  openProjects: (n) => (n === 1 ? 'Ahora mismo hay 1 proyecto abierto que busca este perfil.' : `Ahora mismo hay ${n} proyectos abiertos que buscan este perfil.`),
139
418
  rolesOutro: 'Tu perfil principal es el que ven primero las empresas y el que usamos para recomendarte proyectos. Puedes añadir más y cambiarlo cuando quieras.',
@@ -142,6 +421,7 @@ const COPY = {
142
421
  mainRoleAsk: '¿A qué te dedicas? Dime tu rol con tus palabras y lo busco en el catálogo.',
143
422
  mainRoleNotListed: 'Otro rol',
144
423
  catalogueReason: { role: (words) => `Encaja con tu rol (${words})`, stack: (words) => `Encaja con tu stack (${words})` },
424
+ emailLabel: 'Email',
145
425
  hourlyRateLabel: 'Tarifa por hora',
146
426
  annualRateLabel: 'Tarifa anual',
147
427
  perHour: '€/h',
@@ -152,15 +432,14 @@ const COPY = {
152
432
  no: 'No',
153
433
  },
154
434
  en: {
155
- welcome: (name) => `Hi${name ? `, ${name}` : ''}! Thanks for wanting to join Shakers 🙌\n\nWe are a community of freelance tech professionals. Our goal is to always have the most up-to-date version of you as a professional (what you can do today, how you work with AI and what you are looking for) to connect you with projects that truly fit you.\n\nSigning up takes three steps and I do most of it for you:\n1. Your profile (2 min). I fill it in from your CV and LinkedIn; you just review and confirm.\n2. How you work with AI (1-2 min, optional). I look at which tools you use on this computer. Your code and your prompts never leave your machine.\n3. A short interview (optional). To fine-tune your role and what you are looking for. You can do it now or later.\n\nYou can edit everything later on the web.`,
156
- welcomeQuestion: "Shall we start? Send me your CV, or if it is on this computer, I'll look for it. With it I'll prepare a draft so you only have to confirm it.",
157
- cvSearch: 'Look for it on my computer',
158
- cvAttach: "I'll send it",
435
+ welcome: (name) => `Hi${name ? `, ${name}` : ''}! Thanks for wanting to join Shakers 🙌\n\nWe are a community of freelance tech professionals. Our goal is to always have the most up-to-date version of you as a professional (what you can do today, how you work with AI and what you are looking for) to connect you with projects that truly fit you.\n\nSigning up takes three steps and I do most of it for you:\n1. Your profile (2 min). I fill it in from your CV and LinkedIn; you just review and confirm.\n2. How you work with AI (1-2 min, optional). I analyse your AI tools, the history of your projects and your conversations with AI assistants on this computer. Before anything is sent to Shakers I show you what would be sent and you decide.\n3. A short interview (optional). To fine-tune your role and what you are looking for. You can do it now or later.\n\nYou can edit everything later on the web.`,
436
+ welcomeQuestion: "Send me your CV and your LinkedIn, or if you prefer, I'll look for your CV on this computer.",
159
437
  cvAttachAsk: 'Great, attach it here or tell me where it is on your computer.',
160
438
  cvNotFound: "I couldn't find your CV on this computer. Could you attach it here?",
161
439
  linkedinAsk: 'Could you send me your LinkedIn profile?',
162
440
  linkedinOpening: 'I am opening your LinkedIn profile in your browser so you can copy its address.',
163
441
  linkedinOpened: 'I have opened your LinkedIn profile; copy the address from the address bar and paste it here.',
442
+ webNext: "On the web you can review your profile and see the projects that fit you.",
164
443
  emailKnown: (email) => `Shall I sign you up with ${email}? Use the email you check most: that is where the projects that fit you and Shakers news will arrive.`,
165
444
  emailUnknown: 'Which email shall I sign you up with? Use the one you check most: that is where the projects that fit you and Shakers news will arrive.',
166
445
  emailWorkHint: 'If it is your current work email, you may prefer a personal one so you do not lose access if you change companies.',
@@ -170,12 +449,20 @@ const COPY = {
170
449
  years: (n) => `${n} years of experience`,
171
450
  hours: (h) => `${h} h/month`,
172
451
  rates: (hourly, annual, estimated = true) => (estimated ? `Estimated rate: ${joinParts([hourly != null ? `${amount('en', hourly)} €/h` : null, annual != null ? `${amount('en', annual)} €/year` : null])} (my estimate, adjust it)` : `Rate: ${joinParts([hourly != null ? `${amount('en', hourly)} €/h` : null, annual != null ? `${amount('en', annual)} €/year` : null])}`),
173
- draftQuestion: 'Does it look right, or shall I change something?',
174
- draftOk: 'All correct',
452
+ draftQuestion: 'Shall I create your Shakers account with these details, or change something?',
453
+ draftOk: 'Yes, create my Shakers account with these details',
175
454
  draftChange: 'I want to change something',
176
- windowNotice: "Once you confirm, all that's left is to create your account to save all this and so you can get into Shakers whenever you like.\n\nI'll open a window in your browser; choose how to sign in:\n· With Google\n· With email and password\n\nYou type the password there directly; it never goes through this chat. On that same screen you accept the terms and the privacy policy.\n\n👉 Check your browser. When you're done, come back here and we'll carry on.",
177
- windowNoticeSignIn: "Once you confirm, all that's left is to sign in to the account you already have on Shakers with this email to save all this.\n\nI'll open a window in your browser; choose how to sign in:\n· With Google\n· With email and password\n\nYou type the password there directly; it never goes through this chat.\n\n👉 Check your browser. When you're done, come back here and we'll carry on.",
178
- windowLink: (link) => `If the window did not open, use this link: ${link}`,
455
+ windowNotice: "All that's left is to create your account to save all this and so you can get into Shakers whenever you like. You create it on the Shakers website (shakersworks.com) with Google, LinkedIn or email and password.\n\nYour password is typed only on the Shakers website, it never goes through this chat. If you did not ask for this access, deny it.",
456
+ windowNoticeSignIn: "All that's left is to sign in to the account you already have on Shakers with this email to save all this. You sign in on the Shakers website (shakersworks.com) as usual: Google, LinkedIn or email and password.\n\nYour password is typed only on the Shakers website, it never goes through this chat. If you did not ask for this access, deny it.",
457
+ windowBack: "👉 Check your browser. When you're done, come back here and we'll carry on.",
458
+ windowLink: (link, code) => `I'm opening this Shakers page in your browser; if it does not open, use this link: ${link}\n\nCode: ${code}\nCheck that the page shows this same code and click Approve.`,
459
+ termsQuestion: 'Do you accept, to create your account?',
460
+ termsAccept: 'I accept',
461
+ termsDecline: 'I do not accept',
462
+ termsDeclined: 'Without accepting the Terms and the Privacy Policy I cannot create your account. I have not created anything or sent your data to Shakers. If you change your mind, tell me and we carry on.',
463
+ newsletterQuestion: 'Optional: do you want to receive our content by email? You can change it any time.',
464
+ newsletterNo: 'No, thanks',
465
+ accountReady: (created) => `${created ? 'Your account is now created.' : 'You are now signed in to your Shakers account.'} From now on, on this computer, I can use your account to save to your profile what you confirm; nothing else is sent without asking you. The session is stored only on this computer; to end it, ask me to sign out of Shakers.`,
179
466
  existingTitle: 'I see you already have an account. Shall I update your profile with this?',
180
467
  existingRate: (from, to) => `Hourly rate: ${from} → ${to}`,
181
468
  existingAnnual: (from, to) => `Annual rate: ${from} → ${to}`,
@@ -192,13 +479,18 @@ const COPY = {
192
479
  situationLabels: { FREELANCE: 'Freelance', EMPLOYED: 'Employed', BETWEEN_JOBS: 'Between jobs', STUDYING: 'Studying', OTHER: 'Other' },
193
480
  participationLabels: { FULL_TIME: 'full-time', PART_TIME: 'part-time' },
194
481
  existingUnchanged: (items) => `I did not change these details of your profile; they stay as they were: ${items.join(' · ')}. If you want other values, change them on the web.`,
195
- aiUsageQuestion: 'Do you want me to analyse how you work with AI? It is optional and takes 1-2 min. I only see which tools you use; your code and your prompts do not leave your machine. I look at the AI usage across this whole machine, which I take to be yours. It helps you stand out for AI projects.',
482
+ aiUsageQuestion: "Do you want me to analyse how you work with AI? It is optional and takes 1-2 min. I look at the AI usage across this whole machine, which I take to be yours, and I show you what would be sent before uploading anything. It helps you stand out for AI projects.",
196
483
  aiUsageYes: 'Yes, analyse it',
197
484
  aiUsageSkip: 'Skip this step',
198
485
  aiUsageSkipped: "No problem, we carry on without it. Heads-up: it is what weighs most for projects to reach you. You can run it whenever you like by telling me 'analyse my AI usage'.",
486
+ aiUsageAsk: "Do you want me to analyse how you work with AI? It is optional and takes 1-2 min. It helps you stand out for AI projects.",
487
+ aiUsageStart: "Shall I start the analysis?",
199
488
  consent: {
200
489
  yes: 'Yes, go ahead',
201
490
  no: 'No',
491
+ cvSearchYes: 'Yes, look for my CV on this computer for my Shakers sign-up',
492
+ cvReadYes: (file) => `Yes, read my CV (${file}) for my Shakers sign-up`,
493
+ cvSendYes: (file) => `Yes, send my CV (${file}) to my Shakers account`,
202
494
  cvSearch: 'Shakers will look on this computer for files that may be your CV: it lists the names of the PDF and Word files in your Desktop, Documents, Downloads and iCloud Drive folders, and reads your time zone and the name, email and GitHub user saved by your coding tools. It does not open those files and nothing is sent. Continue?',
203
495
  cvRead: (file) => `Shakers will read the file ${file} to prepare your profile draft. Nothing is sent yet. Continue?`,
204
496
  cvSend: (file) => `Shakers will send the file ${file} to your Shakers account to fill in your profile. Continue?`,
@@ -214,10 +506,10 @@ const COPY = {
214
506
  answerModeOwn: "I'll answer myself",
215
507
  answerModeFull: 'Do it all for me',
216
508
  interviewOnWeb: 'You can do the onboarding interview on the web: when we finish I will open Shakers for you, already signed in.',
217
- livekitUnavailable: 'The interview cannot start on this machine. You can do it on the web: I will open Shakers with you signed in.',
509
+ livekitUnavailable: 'The interview cannot start on this computer. You can do it on the web: I will open Shakers with you signed in.',
218
510
  rolesIntro: 'With everything you have told me, you fit these profiles:',
219
- roleRecommended: (name) => `**${name}** (recommended)`,
220
- roleName: (name) => `**${name}**`,
511
+ roleRecommended: (name) => `${name} (recommended)`,
512
+ roleName: (name) => name,
221
513
  roleReason: { IMPORT: 'From your CV and LinkedIn', ONBOARDING_INTERVIEW: 'From what you told me in the interview', MATCH: 'From your Shakers profile' },
222
514
  openProjects: (n) => (n === 1 ? 'Right now there is 1 open project looking for this profile.' : `Right now there are ${n} open projects looking for this profile.`),
223
515
  rolesOutro: 'Your main profile is the one companies see first and the one we use to recommend projects to you. You can add more and change it whenever you like.',
@@ -226,6 +518,7 @@ const COPY = {
226
518
  mainRoleAsk: 'What do you do? Tell me your role in your own words and I will look it up in the catalogue.',
227
519
  mainRoleNotListed: 'Another role',
228
520
  catalogueReason: { role: (words) => `Matches your role (${words})`, stack: (words) => `Matches your stack (${words})` },
521
+ emailLabel: 'Email',
229
522
  hourlyRateLabel: 'Hourly rate',
230
523
  annualRateLabel: 'Annual rate',
231
524
  perHour: '€/hour',
@@ -236,15 +529,14 @@ const COPY = {
236
529
  no: 'No',
237
530
  },
238
531
  it: {
239
- welcome: (name) => `Ciao${name ? `, ${name}` : ''}! Grazie per voler entrare in Shakers 🙌\n\nSiamo una community di professionisti tech freelance. Il nostro obiettivo è avere sempre la tua versione più aggiornata come professionista (cosa sai fare oggi, come lavori con l'IA e cosa cerchi) per metterti in contatto con progetti che fanno davvero per te.\n\nL'iscrizione richiede tre passaggi e la maggior parte la faccio io per te:\n1. Il tuo profilo (2 min). Lo compilo a partire dal tuo CV e da LinkedIn, tu devi solo rivederlo e confermarlo.\n2. Come lavori con l'IA (1-2 min, facoltativo). Analizzo quali strumenti usi su questo computer. Il tuo codice e i tuoi prompt non escono mai dalla tua macchina.\n3. Un breve colloquio (facoltativo). Per definire meglio il tuo ruolo e cosa cerchi. Lo fai sul sito, quando vuoi.\n\nPuoi modificare tutto in seguito dal sito.`,
240
- welcomeQuestion: 'Iniziamo? Mandami il tuo CV, oppure, se ce l’hai su questo computer, lo cerco io. Con quello ti preparo una bozza da confermare e basta.',
241
- cvSearch: 'Cercalo nel mio computer',
242
- cvAttach: 'Te lo mando',
532
+ welcome: (name) => `Ciao${name ? `, ${name}` : ''}! Grazie per voler entrare in Shakers 🙌\n\nSiamo una community di professionisti tech freelance. Il nostro obiettivo è avere sempre la tua versione più aggiornata come professionista (cosa sai fare oggi, come lavori con l'IA e cosa cerchi) per metterti in contatto con progetti che fanno davvero per te.\n\nL'iscrizione richiede tre passaggi e la maggior parte la faccio io per te:\n1. Il tuo profilo (2 min). Lo compilo a partire dal tuo CV e da LinkedIn, tu devi solo rivederlo e confermarlo.\n2. Come lavori con l'IA (1-2 min, facoltativo). Analizzo i tuoi strumenti di IA, la cronologia dei tuoi progetti e le tue conversazioni con assistenti di IA su questo computer. Prima di inviare qualsiasi cosa a Shakers ti mostro cosa verrebbe inviato e decidi tu.\n3. Un breve colloquio (facoltativo). Per definire meglio il tuo ruolo e cosa cerchi. Lo fai sul sito, quando vuoi.\n\nPuoi modificare tutto in seguito dal sito.`,
533
+ welcomeQuestion: 'Mandami il tuo CV e il tuo LinkedIn, oppure, se preferisci, cerco il tuo CV su questo computer.',
243
534
  cvAttachAsk: 'Perfetto, allegalo qui o dimmi dove si trova nel tuo computer.',
244
535
  cvNotFound: 'Non ho trovato il tuo CV su questo computer. Me lo alleghi qui?',
245
536
  linkedinAsk: 'Mi mandi il tuo profilo LinkedIn?',
246
537
  linkedinOpening: "Ti apro il tuo profilo LinkedIn nel browser così puoi copiarne l'indirizzo.",
247
538
  linkedinOpened: "Ti ho aperto il tuo profilo LinkedIn; copia l'indirizzo dalla barra e incollalo qui.",
539
+ webNext: "Sul sito puoi rivedere il tuo profilo e vedere i progetti adatti a te.",
248
540
  emailKnown: (email) => `Ti registro con ${email}? Usa l'email che controlli di più: lì ti arriveranno i progetti adatti a te e le novità di Shakers.`,
249
541
  emailUnknown: "Con quale email ti registro? Usa quella che controlli di più: lì ti arriveranno i progetti adatti a te e le novità di Shakers.",
250
542
  emailWorkHint: "Se è quella del tuo lavoro attuale, forse ne preferisci una personale per non perdere l'accesso se cambi azienda.",
@@ -254,12 +546,20 @@ const COPY = {
254
546
  years: (n) => `${n} anni di esperienza`,
255
547
  hours: (h) => `${h} h/mese`,
256
548
  rates: (hourly, annual, estimated = true) => (estimated ? `Tariffa stimata: ${joinParts([hourly != null ? `${amount('it', hourly)} €/h` : null, annual != null ? `${amount('it', annual)} €/anno` : null])} (una mia stima, modificala pure)` : `Tariffa: ${joinParts([hourly != null ? `${amount('it', hourly)} €/h` : null, annual != null ? `${amount('it', annual)} €/anno` : null])}`),
257
- draftQuestion: 'Va bene o cambio qualcosa?',
258
- draftOk: 'Tutto corretto',
549
+ draftQuestion: 'Creo il tuo account Shakers con questi dati o cambio qualcosa?',
550
+ draftOk: 'Sì, crea il mio account Shakers con questi dati',
259
551
  draftChange: 'Voglio cambiare qualcosa',
260
- windowNotice: "Quando mi confermi, mancherà solo creare il tuo account per salvare tutto questo e poter entrare in Shakers quando vuoi.\n\nTi apro una finestra nel browser, scegli come entrare:\n· Con Google\n· Con email e password\n\nLa password la scrivi direttamente lì, non passa mai da questa chat. Nella stessa schermata accetti i termini e l'informativa sulla privacy.\n\n👉 Guarda il tuo browser. Quando hai finito, torna qui e continuiamo.",
261
- windowNoticeSignIn: "Quando mi confermi, mancherà solo entrare nell'account che hai già su Shakers con questa email per salvare tutto questo.\n\nTi apro una finestra nel browser, scegli come entrare:\n· Con Google\n· Con email e password\n\nLa password la scrivi direttamente lì, non passa mai da questa chat.\n\n👉 Guarda il tuo browser. Quando hai finito, torna qui e continuiamo.",
262
- windowLink: (link) => `Se la finestra non si è aperta, entra da qui: ${link}`,
552
+ windowNotice: "Manca solo creare il tuo account per salvare tutto questo e poter entrare in Shakers quando vuoi. Lo crei sul sito di Shakers (shakersworks.com) con Google, LinkedIn o email e password.\n\nLa password si scrive solo sul sito di Shakers, non passa mai da questa chat. Se non hai chiesto tu questo accesso, rifiutalo.",
553
+ windowNoticeSignIn: "Manca solo entrare nell'account che hai già su Shakers con questa email per salvare tutto questo. Entri sul sito di Shakers (shakersworks.com) come sempre: Google, LinkedIn o email e password.\n\nLa password si scrive solo sul sito di Shakers, non passa mai da questa chat. Se non hai chiesto tu questo accesso, rifiutalo.",
554
+ windowBack: "👉 Guarda il tuo browser. Quando hai finito, torna qui e continuiamo.",
555
+ windowLink: (link, code) => `Apro questa pagina di Shakers nel tuo browser; se non si apre, entra da qui: ${link}\n\nCodice: ${code}\nControlla che la pagina mostri lo stesso codice e premi Approva.`,
556
+ termsQuestion: 'Accetti, per creare il tuo account?',
557
+ termsAccept: 'Accetto',
558
+ termsDecline: 'Non accetto',
559
+ termsDeclined: "Senza accettare i Termini e l'Informativa sulla privacy non posso creare il tuo account. Non ho creato nulla né inviato i tuoi dati a Shakers. Se cambi idea, dimmelo e continuiamo.",
560
+ newsletterQuestion: 'Facoltativo: vuoi ricevere i nostri contenuti via email? Puoi cambiarlo quando vuoi.',
561
+ newsletterNo: 'No, grazie',
562
+ accountReady: (created) => `${created ? 'Il tuo account è stato creato.' : 'Sei entrato nel tuo account Shakers.'} Da ora, su questo computer, posso usare il tuo account per salvare nel profilo ciò che confermi; nient\'altro viene inviato senza chiedertelo. La sessione resta solo su questo computer; per chiuderla, chiedimi di uscire da Shakers.`,
263
563
  existingTitle: 'Ho visto che hai già un account. Aggiorno il tuo profilo con questi dati?',
264
564
  existingRate: (from, to) => `Tariffa oraria: ${from} → ${to}`,
265
565
  existingAnnual: (from, to) => `Tariffa annuale: ${from} → ${to}`,
@@ -276,13 +576,18 @@ const COPY = {
276
576
  situationLabels: { FREELANCE: 'Freelance', EMPLOYED: 'Dipendente', BETWEEN_JOBS: "Tra un lavoro e l'altro", STUDYING: 'Studente', OTHER: 'Altro' },
277
577
  participationLabels: { FULL_TIME: 'tempo pieno', PART_TIME: 'part-time' },
278
578
  existingUnchanged: (items) => `Questi dati del tuo profilo non li ho cambiati, restano com'erano: ${items.join(' · ')}. Se vuoi altri valori, cambiali sul sito.`,
279
- aiUsageQuestion: "Vuoi che analizzi come lavori con l'IA? È facoltativo e richiede 1-2 min. Vedo solo quali strumenti usi, il tuo codice e i tuoi prompt non escono dalla tua macchina. Guardo l'uso dell'IA su tutto questo computer, che considero tuo. Ti aiuta a distinguerti nei progetti di IA.",
579
+ aiUsageQuestion: "Vuoi che analizzi come lavori con l'IA? È facoltativo e richiede 1-2 min. Guardo l'uso dell'IA su tutto questo computer, che considero tuo, e prima di caricare qualcosa ti mostro cosa verrebbe inviato. Ti aiuta a distinguerti nei progetti di IA.",
280
580
  aiUsageYes: 'Sì, analizzalo',
281
581
  aiUsageSkip: 'Salta questo passaggio',
282
582
  aiUsageSkipped: "Nessun problema, andiamo avanti senza. Ti avviso che è ciò che pesa di più perché ti arrivino progetti: puoi lanciarlo quando vuoi dicendomi 'analizza il mio uso dell'IA'.",
583
+ aiUsageAsk: "Vuoi che analizzi come lavori con l'IA? È facoltativo e richiede 1-2 min. Ti aiuta a distinguerti nei progetti di IA.",
584
+ aiUsageStart: "Avvio l'analisi?",
283
585
  consent: {
284
586
  yes: 'Sì, procedi',
285
587
  no: 'No',
588
+ cvSearchYes: 'Sì, cerca il mio CV in questo computer per la mia iscrizione a Shakers',
589
+ cvReadYes: (file) => `Sì, leggi il mio CV (${file}) per la mia iscrizione a Shakers`,
590
+ cvSendYes: (file) => `Sì, invia il mio CV (${file}) al mio account Shakers`,
286
591
  cvSearch: "Shakers cercherà su questo computer i file che potrebbero essere il tuo CV: guarda i nomi dei file PDF e Word nelle cartelle Scrivania, Documenti, Download e iCloud Drive, e legge il tuo fuso orario e il nome, l'email e l'utente GitHub salvati dai tuoi strumenti di programmazione. Non apre quei file e non invia nulla. Continuiamo?",
287
592
  cvRead: (file) => `Shakers leggerà il file ${file} per preparare la bozza del tuo profilo. Per ora non viene inviato nulla. Continuiamo?`,
288
593
  cvSend: (file) => `Shakers invierà il file ${file} al tuo account Shakers per completare il tuo profilo. Continuiamo?`,
@@ -300,8 +605,8 @@ const COPY = {
300
605
  interviewOnWeb: 'Il colloquio di onboarding puoi farlo sul sito: alla fine ti apro Shakers con la sessione già avviata.',
301
606
  livekitUnavailable: 'Il colloquio non può partire su questo computer. Puoi farlo sul sito: ti apro Shakers con la sessione già avviata.',
302
607
  rolesIntro: 'Con tutto quello che mi hai raccontato, sei adatto a questi profili:',
303
- roleRecommended: (name) => `**${name}** (consigliato)`,
304
- roleName: (name) => `**${name}**`,
608
+ roleRecommended: (name) => `${name} (consigliato)`,
609
+ roleName: (name) => name,
305
610
  roleReason: { IMPORT: 'Dal tuo CV e da LinkedIn', ONBOARDING_INTERVIEW: 'Da quello che mi hai raccontato nel colloquio', MATCH: 'Dal tuo profilo su Shakers' },
306
611
  openProjects: (n) => (n === 1 ? "In questo momento c'è 1 progetto aperto che cerca questo profilo." : `In questo momento ci sono ${n} progetti aperti che cercano questo profilo.`),
307
612
  rolesOutro: 'Il tuo profilo principale è quello che le aziende vedono per primo e quello che usiamo per consigliarti progetti. Puoi aggiungerne altri e cambiarlo quando vuoi.',
@@ -310,6 +615,7 @@ const COPY = {
310
615
  mainRoleAsk: 'Di cosa ti occupi? Dimmi il tuo ruolo con parole tue e lo cerco nel catalogo.',
311
616
  mainRoleNotListed: 'Un altro ruolo',
312
617
  catalogueReason: { role: (words) => `Corrisponde al tuo ruolo (${words})`, stack: (words) => `Corrisponde al tuo stack (${words})` },
618
+ emailLabel: 'Email',
313
619
  hourlyRateLabel: 'Tariffa oraria',
314
620
  annualRateLabel: 'Tariffa annuale',
315
621
  perHour: '€/h',
@@ -320,15 +626,14 @@ const COPY = {
320
626
  no: 'No',
321
627
  },
322
628
  pt: {
323
- welcome: (name) => `Olá${name ? `, ${name}` : ''}! Obrigado por querer juntar-se à Shakers 🙌\n\nSomos uma comunidade de profissionais tech freelance. O nosso objetivo é ter sempre a sua versão mais atual como profissional (o que sabe fazer hoje, como trabalha com IA e o que procura) para o pôr em contacto com projetos que encaixem mesmo consigo.\n\nO registo tem três passos e a maior parte faço eu por si:\n1. O seu perfil (2 min). Preencho-o a partir do seu CV e do LinkedIn; só tem de rever e confirmar.\n2. Como trabalha com IA (1-2 min, opcional). Analiso que ferramentas usa neste computador. O seu código e os seus prompts nunca saem da sua máquina.\n3. Uma breve entrevista (opcional). Para afinar o seu perfil e o que procura. Faz-se na web, quando quiser.\n\nPode editar tudo depois na web.`,
324
- welcomeQuestion: 'Começamos? Envie-me o seu CV ou, se o tiver neste computador, procuro-o eu. Com ele preparo um rascunho para que só tenha de o confirmar.',
325
- cvSearch: 'Procure-o no meu computador',
326
- cvAttach: 'Envio-o eu',
629
+ welcome: (name) => `Olá${name ? `, ${name}` : ''}! Obrigado por querer juntar-se à Shakers 🙌\n\nSomos uma comunidade de profissionais tech freelance. O nosso objetivo é ter sempre a sua versão mais atual como profissional (o que sabe fazer hoje, como trabalha com IA e o que procura) para o pôr em contacto com projetos que encaixem mesmo consigo.\n\nO registo tem três passos e a maior parte faço eu por si:\n1. O seu perfil (2 min). Preencho-o a partir do seu CV e do LinkedIn; só tem de rever e confirmar.\n2. Como trabalha com IA (1-2 min, opcional). Analiso as suas ferramentas de IA, o histórico dos seus projetos e as suas conversas com assistentes de IA neste computador. Antes de enviar alguma coisa para a Shakers mostro-lhe o que seria enviado e decide você.\n3. Uma breve entrevista (opcional). Para afinar o seu perfil e o que procura. Faz-se na web, quando quiser.\n\nPode editar tudo depois na web.`,
630
+ welcomeQuestion: 'Envie-me o seu CV e o seu LinkedIn ou, se preferir, procuro o seu CV neste computador.',
327
631
  cvAttachAsk: 'Perfeito, anexe-o aqui ou diga-me onde está no seu computador.',
328
632
  cvNotFound: 'Não encontrei o seu CV neste computador. Pode anexá-lo aqui?',
329
633
  linkedinAsk: 'Pode enviar-me o seu perfil do LinkedIn?',
330
634
  linkedinOpening: 'Vou abrir o seu perfil do LinkedIn no navegador para que copie o endereço.',
331
635
  linkedinOpened: 'Abri o seu perfil do LinkedIn; copie o endereço da barra e cole-o aqui.',
636
+ webNext: "Na web pode rever o seu perfil e ver os projetos que encaixam consigo.",
332
637
  emailKnown: (email) => `Registo-o com ${email}? Use o email que consulta mais: é aí que vai receber os projetos que encaixam consigo e as novidades da Shakers.`,
333
638
  emailUnknown: 'Com que email o registo? Use o que consulta mais: é aí que vai receber os projetos que encaixam consigo e as novidades da Shakers.',
334
639
  emailWorkHint: 'Se for o do seu trabalho atual, talvez prefira um pessoal para não perder o acesso se mudar de empresa.',
@@ -338,12 +643,20 @@ const COPY = {
338
643
  years: (n) => `${n} anos de experiência`,
339
644
  hours: (h) => `${h} h/mês`,
340
645
  rates: (hourly, annual, estimated = true) => (estimated ? `Tarifa estimada: ${joinParts([hourly != null ? `${amount('pt', hourly)} €/h` : null, annual != null ? `${amount('pt', annual)} €/ano` : null])} (estimativa minha, ajuste-a)` : `Tarifa: ${joinParts([hourly != null ? `${amount('pt', hourly)} €/h` : null, annual != null ? `${amount('pt', annual)} €/ano` : null])}`),
341
- draftQuestion: 'Está bem ou mudo alguma coisa?',
342
- draftOk: 'Está tudo certo',
646
+ draftQuestion: 'Crio a sua conta Shakers com estes dados ou mudo alguma coisa?',
647
+ draftOk: 'Sim, crie a minha conta Shakers com estes dados',
343
648
  draftChange: 'Quero mudar algo',
344
- windowNotice: 'Quando me confirmar, só faltará criar a sua conta para guardar tudo isto e poder entrar na Shakers quando quiser.\n\nAbro-lhe uma janela no navegador, escolha como entrar:\n· Com Google\n· Com email e palavra-passe\n\nEscreve a palavra-passe diretamente lá; nunca passa por este chat. Nesse mesmo ecrã aceita os termos e a política de privacidade.\n\n👉 Veja o seu navegador. Quando terminar, volte aqui e continuamos.',
345
- windowNoticeSignIn: 'Quando me confirmar, só faltará entrar na conta que já tem na Shakers com este email para guardar tudo isto.\n\nAbro-lhe uma janela no navegador, escolha como entrar:\n· Com Google\n· Com email e palavra-passe\n\nEscreve a palavra-passe diretamente lá; nunca passa por este chat.\n\n👉 Veja o seu navegador. Quando terminar, volte aqui e continuamos.',
346
- windowLink: (link) => `Se a janela não abriu, entre por aqui: ${link}`,
649
+ windowNotice: "Só falta criar a sua conta para guardar tudo isto e poder entrar na Shakers quando quiser. Cria-a no site da Shakers (shakersworks.com) com Google, LinkedIn ou email e palavra-passe.\n\nA palavra-passe só se escreve no site da Shakers, nunca passa por este chat. Se não foi você que pediu este acesso, recuse-o.",
650
+ windowNoticeSignIn: "Só falta entrar na conta que já tem na Shakers com este email para guardar tudo isto. Entra no site da Shakers (shakersworks.com) como sempre: Google, LinkedIn ou email e palavra-passe.\n\nA palavra-passe só se escreve no site da Shakers, nunca passa por este chat. Se não foi você que pediu este acesso, recuse-o.",
651
+ windowBack: "👉 Veja o seu navegador. Quando terminar, volte aqui e continuamos.",
652
+ windowLink: (link, code) => `Abro esta página da Shakers no seu navegador; se não abrir, entre por aqui: ${link}\n\nCódigo: ${code}\nConfirme que a página mostra este mesmo código e carregue em Aprovar.`,
653
+ termsQuestion: 'Aceita, para criar a sua conta?',
654
+ termsAccept: 'Aceito',
655
+ termsDecline: 'Não aceito',
656
+ termsDeclined: 'Sem aceitar os Termos e a Política de Privacidade não posso criar a sua conta. Não criei nada nem enviei os seus dados para a Shakers. Se mudar de ideias, diga-me e continuamos.',
657
+ newsletterQuestion: 'Opcional: quer receber os nossos conteúdos por email? Pode mudar isto quando quiser.',
658
+ newsletterNo: 'Não, obrigado',
659
+ accountReady: (created) => `${created ? 'A sua conta já está criada.' : 'Já entrou na sua conta Shakers.'} A partir de agora, neste computador, posso usar a sua conta para guardar no seu perfil o que confirmar; nada mais é enviado sem lhe perguntar. A sessão fica guardada só neste computador; para a fechar, peça-me para terminar a sessão da Shakers.`,
347
660
  existingTitle: 'Vi que já tem conta. Atualizo o seu perfil com isto?',
348
661
  existingRate: (from, to) => `Tarifa por hora: ${from} → ${to}`,
349
662
  existingAnnual: (from, to) => `Tarifa anual: ${from} → ${to}`,
@@ -360,13 +673,18 @@ const COPY = {
360
673
  situationLabels: { FREELANCE: 'Freelancer', EMPLOYED: 'Por conta de outrem', BETWEEN_JOBS: 'Entre trabalhos', STUDYING: 'A estudar', OTHER: 'Outra' },
361
674
  participationLabels: { FULL_TIME: 'tempo inteiro', PART_TIME: 'tempo parcial' },
362
675
  existingUnchanged: (items) => `Estes dados do seu perfil não os alterei, ficam como estavam: ${items.join(' · ')}. Se quiser outros valores, altere-os na web.`,
363
- aiUsageQuestion: 'Quer que analise como trabalha com IA? É opcional e demora 1-2 min. Só vejo que ferramentas usa; o seu código e os seus prompts não saem da sua máquina. Analiso o uso de IA em todo este computador, que considero seu. Ajuda-o a destacar-se em projetos de IA.',
676
+ aiUsageQuestion: "Quer que analise como trabalha com IA? É opcional e demora 1-2 min. Analiso o uso de IA em todo este computador, que considero seu, e antes de enviar algo mostro-lhe o que seria enviado. Ajuda-o a destacar-se em projetos de IA.",
364
677
  aiUsageYes: 'Sim, analise',
365
678
  aiUsageSkip: 'Saltar este passo',
366
679
  aiUsageSkipped: "Sem problema, seguimos sem ele. Aviso-o de que é o que mais pesa para lhe chegarem projetos: pode lançá-lo quando quiser dizendo-me 'analisa o meu uso de IA'.",
680
+ aiUsageAsk: "Quer que analise como trabalha com IA? É opcional e demora 1-2 min. Ajuda-o a destacar-se em projetos de IA.",
681
+ aiUsageStart: "Começo a análise?",
367
682
  consent: {
368
683
  yes: 'Sim, avançar',
369
684
  no: 'Não',
685
+ cvSearchYes: 'Sim, procure o meu CV neste computador para o meu registo na Shakers',
686
+ cvReadYes: (file) => `Sim, leia o meu CV (${file}) para o meu registo na Shakers`,
687
+ cvSendYes: (file) => `Sim, envie o meu CV (${file}) para a minha conta Shakers`,
370
688
  cvSearch: 'A Shakers vai procurar neste computador ficheiros que possam ser o seu CV: vê os nomes dos ficheiros PDF e Word das pastas Secretária, Documentos, Transferências e iCloud Drive, e lê o seu fuso horário e o nome, o email e o utilizador de GitHub guardados pelas suas ferramentas de programação. Não abre esses ficheiros e não envia nada. Continuamos?',
371
689
  cvRead: (file) => `A Shakers vai ler o ficheiro ${file} para preparar o rascunho do seu perfil. Ainda não é enviado nada. Continuamos?`,
372
690
  cvSend: (file) => `A Shakers vai enviar o ficheiro ${file} para a sua conta Shakers para completar o seu perfil. Continuamos?`,
@@ -384,8 +702,8 @@ const COPY = {
384
702
  interviewOnWeb: 'Pode fazer a entrevista de onboarding na web: no fim abro-lhe o Shakers com a sessão iniciada.',
385
703
  livekitUnavailable: 'A entrevista não pode começar neste computador. Pode fazê-la na web: abro-lhe a Shakers com a sessão iniciada.',
386
704
  rolesIntro: 'Com tudo o que me contou, encaixa nestes perfis:',
387
- roleRecommended: (name) => `**${name}** (recomendado)`,
388
- roleName: (name) => `**${name}**`,
705
+ roleRecommended: (name) => `${name} (recomendado)`,
706
+ roleName: (name) => name,
389
707
  roleReason: { IMPORT: 'Pelo seu CV e pelo LinkedIn', ONBOARDING_INTERVIEW: 'Pelo que me contou na entrevista', MATCH: 'Pelo seu perfil na Shakers' },
390
708
  openProjects: (n) => (n === 1 ? 'Neste momento há 1 projeto aberto que procura este perfil.' : `Neste momento há ${n} projetos abertos que procuram este perfil.`),
391
709
  rolesOutro: 'O seu perfil principal é o que as empresas veem primeiro e o que usamos para lhe recomendar projetos. Pode adicionar mais e alterá-lo quando quiser.',
@@ -394,6 +712,7 @@ const COPY = {
394
712
  mainRoleAsk: 'A que se dedica? Diga-me o seu perfil por palavras suas e eu procuro-o no catálogo.',
395
713
  mainRoleNotListed: 'Outro perfil',
396
714
  catalogueReason: { role: (words) => `Corresponde ao seu perfil (${words})`, stack: (words) => `Corresponde ao seu stack (${words})` },
715
+ emailLabel: 'Email',
397
716
  hourlyRateLabel: 'Tarifa por hora',
398
717
  annualRateLabel: 'Tarifa anual',
399
718
  perHour: '€/h',
@@ -419,13 +738,13 @@ const REVIEW = {
419
738
  activity: (sessions, days) => `Tu actividad con IA: ${sessions === 1 ? '1 sesión' : `${sessions} sesiones`}${days === null ? '' : `, unos ${days} días activos por semana`}.`,
420
739
  repos: (list, commits) => `Repositorios analizados: ${list}. De ellos solo se envían cifras (${commits === 1 ? '1 commit' : `${commits} commits`}, líneas cambiadas, tipos de fichero), nunca su nombre ni su código.`,
421
740
  agents: (list, definitions, summaries) => `Agentes de IA detectados: ${list}. De cada uno se envía ${listOf('es', ['su nombre', 'sus herramientas', 'su modelo', 'de qué agente depende', ...(definitions > 0 ? ['el texto de su definición (sin secretos)'] : []), ...(summaries > 0 ? ['el resumen de lo que hace'] : [])])}.`,
422
- excerpts: (conversation, commands) => `Extractos de texto: ${conversation} frases cortas de lo que le pides a la IA y ${commands} comandos, de 200 caracteres como máximo, sin secretos, rutas ni emails.`,
741
+ excerpts: (conversation, commands) => `Extractos de texto: ${conversation} fragmentos literales de tus mensajes a la IA y ${commands} comandos, de 200 caracteres como máximo, tras quitar lo que parezca un secreto, una ruta o un email.`,
423
742
  noExcerpts: 'Extractos de texto: ninguno.',
424
743
  notSent: 'No se envía: tu código, tus conversaciones completas ni los nombres de tus repositorios.',
425
744
  explanationTitle: 'Qué hace Shakers con esto:',
426
745
  explanation: [
427
746
  'Te sitúa en dos ejes. Configuración: qué tienes montado (herramientas, servidores MCP, agentes), de menos a más: Asistido, Extendido u Orquestado. Uso: cómo diriges a la IA (si le das instrucciones concretas, si revisas lo que propone), de menos a más: Reactivo, Deliberado o Riguroso.',
428
- 'La configuración se calcula con reglas fijas a partir de lo detectado. El nivel de uso y los textos sobre cómo trabajas los escribe un modelo de IA en los servidores de Shakers.',
747
+ "La configuración se calcula con reglas fijas a partir de lo detectado. El nivel de uso y los textos sobre cómo trabajas los escribe un modelo de IA de un proveedor externo (OpenAI, Google o Anthropic) por encargo de Shakers. Ese modelo lee los extractos de texto, los comandos y las definiciones de tus agentes para calcular tu evaluación y después los descarta: Shakers guarda el resultado (niveles, puntuaciones, recuentos y los nombres de tus herramientas y agentes), no esos textos.",
429
748
  'En tu perfil aparece la sección «Mi trabajo con IA»: tus dos niveles, tu casilla en la matriz (por ejemplo Practitioner o Architect), tus herramientas y agentes, y dos textos que puedes editar en la web.',
430
749
  ],
431
750
  afterwards: 'Si no estás de acuerdo con el resultado, pulsa «Solicitar revisión humana» en esa sección de la web o escribe a talent@shakersworks.com. Hoy no puedes ocultar la sección tú mismo; para pedir que se borre, escribe a ese mismo correo.',
@@ -436,11 +755,11 @@ const REVIEW = {
436
755
  formExcerpts: 'Incluir los extractos de texto',
437
756
  formRepo: (name) => `Incluir el repositorio ${name}`,
438
757
  formAgent: (name) => `Incluir el agente ${name}`,
439
- declined: 'No se ha subido nada. El análisis se queda solo en tu equipo.',
758
+ declined: 'No se ha subido nada. El análisis se queda solo en tu ordenador.',
440
759
  nothingLeft: 'Has dejado fuera todos los repositorios, así que no se ha subido nada.',
441
760
  throttled: 'Ya subiste un análisis hace menos de una hora, así que no se ha subido nada. Puedes volver a intentarlo más tarde.',
442
761
  uploaded: 'Subido a tu perfil. Shakers lo está evaluando y el resultado tarda unos minutos.',
443
- uploadFailed: 'No se ha podido subir. Tu análisis se queda solo en tu equipo; puedes volver a intentarlo más tarde.',
762
+ uploadFailed: 'No se ha podido subir. Tu análisis se queda solo en tu ordenador; puedes volver a intentarlo más tarde.',
444
763
  resultTitle: 'Tu resultado en «Mi trabajo con IA»:',
445
764
  setupLevel: (level) => `Configuración: ${level}.`,
446
765
  usageLevel: (level) => `Uso: ${level}.`,
@@ -461,13 +780,13 @@ const REVIEW = {
461
780
  activity: (sessions, days) => `Your AI activity: ${sessions === 1 ? '1 session' : `${sessions} sessions`}${days === null ? '' : `, about ${days} active days a week`}.`,
462
781
  repos: (list, commits) => `Repositories analysed: ${list}. Only figures about them are sent (${commits === 1 ? '1 commit' : `${commits} commits`}, lines changed, file types), never their name or their code.`,
463
782
  agents: (list, definitions, summaries) => `AI agents detected: ${list}. For each one we send ${listOf('en', ['its name', 'its tools', 'its model', 'which agent it reports to', ...(definitions > 0 ? ['the text of its definition (with secrets removed)'] : []), ...(summaries > 0 ? ['the summary of what it does'] : [])])}.`,
464
- excerpts: (conversation, commands) => `Text excerpts: ${conversation} short sentences of what you ask the AI and ${commands} commands, 200 characters at most, with secrets, paths and emails removed.`,
783
+ excerpts: (conversation, commands) => `Text excerpts: ${conversation} literal fragments of your messages to the AI and ${commands} commands, 200 characters at most, after removing anything that looks like a secret, a path or an email.`,
465
784
  noExcerpts: 'Text excerpts: none.',
466
785
  notSent: 'Not sent: your code, your full conversations or the names of your repositories.',
467
786
  explanationTitle: 'What Shakers does with it:',
468
787
  explanation: [
469
788
  'It places you on two axes. Setup: what you have in place (tools, MCP servers, agents), from less to more: Assisted, Extended or Orchestrated. Usage: how you steer the AI (whether you give it concrete instructions, whether you review what it proposes), from less to more: Reactive, Deliberate or Rigorous.',
470
- 'Setup is worked out with fixed rules from what was detected. The usage level and the texts about how you work are written by an AI model on Shakers servers.',
789
+ "Setup is worked out with fixed rules from what was detected. The usage level and the texts about how you work are written by an AI model from an external provider (OpenAI, Google or Anthropic) on Shakers' behalf. That model reads the text excerpts, the commands and your agents' definitions to compute your evaluation and then discards them: Shakers keeps the result (levels, scores, counts and the names of your tools and agents), not those texts.",
471
790
  'Your profile shows the "My work with AI" section: your two levels, your square in the matrix (for example Practitioner or Architect), your tools and agents, and two texts you can edit on the web.',
472
791
  ],
473
792
  afterwards: 'If you disagree with the result, press "Request human review" in that section on the web or write to talent@shakersworks.com. Today you cannot hide the section yourself; to ask for it to be deleted, write to that same address.',
@@ -503,13 +822,13 @@ const REVIEW = {
503
822
  activity: (sessions, days) => `La tua attività con l'IA: ${sessions === 1 ? '1 sessione' : `${sessions} sessioni`}${days === null ? '' : `, circa ${days} giorni attivi a settimana`}.`,
504
823
  repos: (list, commits) => `Repository analizzati: ${list}. Di questi si inviano solo cifre (${commits} commit, righe modificate, tipi di file), mai il nome né il codice.`,
505
824
  agents: (list, definitions, summaries) => `Agenti di IA rilevati: ${list}. Di ognuno si inviano ${listOf('it', ['il nome', 'gli strumenti', 'il modello', 'da quale agente dipende', ...(definitions > 0 ? ['il testo della sua definizione (senza segreti)'] : []), ...(summaries > 0 ? ['il riassunto di ciò che fa'] : [])])}.`,
506
- excerpts: (conversation, commands) => `Estratti di testo: ${conversation} frasi brevi di ciò che chiedi all'IA e ${commands} comandi, al massimo 200 caratteri, senza segreti, percorsi né email.`,
825
+ excerpts: (conversation, commands) => `Estratti di testo: ${conversation} frammenti letterali dei tuoi messaggi all'IA e ${commands} comandi, al massimo 200 caratteri, dopo aver tolto ciò che sembra un segreto, un percorso o un'email.`,
507
826
  noExcerpts: 'Estratti di testo: nessuno.',
508
827
  notSent: 'Non si inviano: il tuo codice, le tue conversazioni complete né i nomi dei tuoi repository.',
509
828
  explanationTitle: 'Cosa fa Shakers con questi dati:',
510
829
  explanation: [
511
830
  "Ti colloca su due assi. Configurazione: cosa hai impostato (strumenti, server MCP, agenti), dal meno al più: Assistito, Esteso o Orchestrato. Uso: come guidi l'IA (se le dai istruzioni concrete, se controlli ciò che propone), dal meno al più: Reattivo, Deliberato o Rigoroso.",
512
- "La configurazione si calcola con regole fisse da ciò che è stato rilevato. Il livello di uso e i testi su come lavori li scrive un modello di IA sui server di Shakers.",
831
+ "La configurazione si calcola con regole fisse da ciò che è stato rilevato. Il livello di uso e i testi su come lavori li scrive un modello di IA di un fornitore esterno (OpenAI, Google o Anthropic) per conto di Shakers. Quel modello legge gli estratti di testo, i comandi e le definizioni dei tuoi agenti per calcolare la tua valutazione e poi li scarta: Shakers conserva il risultato (livelli, punteggi, conteggi e i nomi dei tuoi strumenti e agenti), non quei testi.",
513
832
  "Sul tuo profilo compare la sezione «Il mio lavoro con l'IA»: i tuoi due livelli, la tua casella nella matrice (per esempio Practitioner o Architect), i tuoi strumenti e agenti, e due testi che puoi modificare sul sito.",
514
833
  ],
515
834
  afterwards: 'Se non sei d\'accordo con il risultato, premi «Richiedi una revisione umana» in quella sezione del sito o scrivi a talent@shakersworks.com. Oggi non puoi nascondere la sezione da solo; per chiedere che venga cancellata, scrivi allo stesso indirizzo.',
@@ -545,13 +864,13 @@ const REVIEW = {
545
864
  activity: (sessions, days) => `A sua atividade com IA: ${sessions === 1 ? '1 sessão' : `${sessions} sessões`}${days === null ? '' : `, cerca de ${days} dias ativos por semana`}.`,
546
865
  repos: (list, commits) => `Repositórios analisados: ${list}. Deles só se enviam números (${commits === 1 ? '1 commit' : `${commits} commits`}, linhas alteradas, tipos de ficheiro), nunca o nome nem o código.`,
547
866
  agents: (list, definitions, summaries) => `Agentes de IA detetados: ${list}. De cada um envia-se ${listOf('pt', ['o nome', 'as ferramentas', 'o modelo', 'de que agente depende', ...(definitions > 0 ? ['o texto da sua definição (sem segredos)'] : []), ...(summaries > 0 ? ['o resumo do que faz'] : [])])}.`,
548
- excerpts: (conversation, commands) => `Excertos de texto: ${conversation} frases curtas do que pede à IA e ${commands} comandos, com 200 caracteres no máximo, sem segredos, caminhos nem emails.`,
867
+ excerpts: (conversation, commands) => `Excertos de texto: ${conversation} fragmentos literais das suas mensagens à IA e ${commands} comandos, com 200 caracteres no máximo, depois de retirar o que pareça um segredo, um caminho ou um email.`,
549
868
  noExcerpts: 'Excertos de texto: nenhum.',
550
869
  notSent: 'Não se envia: o seu código, as suas conversas completas nem os nomes dos seus repositórios.',
551
870
  explanationTitle: 'O que a Shakers faz com isto:',
552
871
  explanation: [
553
872
  'Situa-o em dois eixos. Configuração: o que tem montado (ferramentas, servidores MCP, agentes), de menos para mais: Assistido, Estendido ou Orquestrado. Uso: como orienta a IA (se lhe dá instruções concretas, se revê o que propõe), de menos para mais: Reativo, Deliberado ou Rigoroso.',
554
- 'A configuração calcula-se com regras fixas a partir do que foi detetado. O nível de uso e os textos sobre como trabalha são escritos por um modelo de IA nos servidores da Shakers.',
873
+ "A configuração calcula-se com regras fixas a partir do que foi detetado. O nível de uso e os textos sobre como trabalha são escritos por um modelo de IA de um fornecedor externo (OpenAI, Google ou Anthropic) por conta da Shakers. Esse modelo lê os excertos de texto, os comandos e as definições dos seus agentes para calcular a sua avaliação e depois descarta-os: a Shakers guarda o resultado (níveis, pontuações, contagens e os nomes das suas ferramentas e agentes), não esses textos.",
555
874
  'No seu perfil aparece a secção «Meu trabalho com IA»: os seus dois níveis, a sua casa na matriz (por exemplo Practitioner ou Architect), as suas ferramentas e agentes, e dois textos que pode editar na web.',
556
875
  ],
557
876
  afterwards: 'Se não concordar com o resultado, carregue em «Solicitar revisão humana» nessa secção da web ou escreva para talent@shakersworks.com. Hoje não pode ocultar a secção sozinho; para pedir que seja apagada, escreva para esse mesmo endereço.',
@@ -581,10 +900,120 @@ function toSignupLanguage(code) {
581
900
  return SIGNUP_LANGUAGES.includes(base) ? base : null;
582
901
  }
583
902
 
903
+ const DETECTED_AGENTS = {
904
+ es: {
905
+ intro: 'Estos agentes se detectaron en tu análisis y aún no están en tu perfil de Shakers. Los que elijas se añaden con su nombre y su descripción, y los clientes los verán.',
906
+ question: 'Responde con los números de los que quieres añadir (por ejemplo «1 3»), «todos» o «ninguno».',
907
+ allOnProfile: 'Todos los agentes detectados ya están en tu perfil.',
908
+ noneChosen: 'No se ha añadido ningún agente a tu perfil.',
909
+ added: (names) => `Añadidos a tu perfil: ${names}. Puedes quitarlos cuando quieras desde tu perfil en la web de Shakers.`,
910
+ failed: (names) => `No se pudieron añadir: ${names}.`,
911
+ },
912
+ en: {
913
+ intro: 'These agents were detected in your analysis and are not on your Shakers profile yet. The ones you pick are added with their name and description, and clients will see them.',
914
+ question: 'Reply with the numbers of the ones you want to add (for example "1 3"), "all" or "none".',
915
+ allOnProfile: 'Every detected agent is already on your profile.',
916
+ noneChosen: 'No agent was added to your profile.',
917
+ added: (names) => `Added to your profile: ${names}. You can remove them any time from your profile on the Shakers website.`,
918
+ failed: (names) => `Could not be added: ${names}.`,
919
+ },
920
+ it: {
921
+ intro: 'Questi agenti sono stati rilevati nella tua analisi e non sono ancora nel tuo profilo Shakers. Quelli che scegli vengono aggiunti con nome e descrizione, e i clienti li vedranno.',
922
+ question: 'Rispondi con i numeri di quelli che vuoi aggiungere (per esempio «1 3»), «tutti» o «nessuno».',
923
+ allOnProfile: 'Tutti gli agenti rilevati sono già nel tuo profilo.',
924
+ noneChosen: 'Nessun agente è stato aggiunto al tuo profilo.',
925
+ added: (names) => `Aggiunti al tuo profilo: ${names}. Puoi toglierli quando vuoi dal tuo profilo sul sito di Shakers.`,
926
+ failed: (names) => `Non è stato possibile aggiungere: ${names}.`,
927
+ },
928
+ pt: {
929
+ intro: 'Estes agentes foram detetados na sua análise e ainda não estão no seu perfil Shakers. Os que escolher são adicionados com o nome e a descrição, e os clientes vão vê-los.',
930
+ question: 'Responda com os números dos que quer adicionar (por exemplo «1 3»), «todos» ou «nenhum».',
931
+ allOnProfile: 'Todos os agentes detetados já estão no seu perfil.',
932
+ noneChosen: 'Nenhum agente foi adicionado ao seu perfil.',
933
+ added: (names) => `Adicionados ao seu perfil: ${names}. Pode removê-los quando quiser a partir do seu perfil na web da Shakers.`,
934
+ failed: (names) => `Não foi possível adicionar: ${names}.`,
935
+ },
936
+ };
937
+
938
+ const APPLY = {
939
+ es: {
940
+ untitled: 'Sin título',
941
+ mainMotivationFallback: '¿Cuál es tu principal motivación para aplicar?',
942
+ notOpen: 'Este proyecto no admite tu candidatura ahora mismo: ya aplicaste, está cerrado o no está disponible para ti.',
943
+ summaryTitle: 'Esto es lo que se enviará:',
944
+ titleLine: (title) => `Proyecto: ${title}`,
945
+ companyLine: (name) => `Empresa: ${name}`,
946
+ messageLine: (message) => `Mensaje: ${message}`,
947
+ empty: '(vacío)',
948
+ answerLine: (question, answer) => `${question}: ${answer}`,
949
+ sendNotice: (title) => `Se envían tu mensaje y tus respuestas para «${title}»: el cliente verá tu perfil de Shakers y se abre un chat con el equipo. Tu perfil no cambia.`,
950
+ reviewNotice: 'En este proyecto Shakers revisa tu candidatura antes de que la vea el cliente.',
951
+ confirmQuestion: '¿La envío? Responde sí o no.',
952
+ sent: 'Candidatura enviada. Puedes seguirla en tus candidaturas.',
953
+ sentPending: 'Candidatura enviada. Shakers la revisa antes de que la vea el cliente.',
954
+ alreadyApplied: (status) => (status ? `Ya aplicaste a este proyecto (estado: ${status}).` : 'Ya aplicaste a este proyecto.'),
955
+ },
956
+ en: {
957
+ untitled: 'Untitled',
958
+ mainMotivationFallback: "What's your main motivation to apply?",
959
+ notOpen: 'This project does not take your application right now: you already applied, it is closed or it is not available to you.',
960
+ summaryTitle: 'This is what will be sent:',
961
+ titleLine: (title) => `Project: ${title}`,
962
+ companyLine: (name) => `Company: ${name}`,
963
+ messageLine: (message) => `Message: ${message}`,
964
+ empty: '(empty)',
965
+ answerLine: (question, answer) => `${question}: ${answer}`,
966
+ sendNotice: (title) => `Your message and your answers for "${title}" are sent: the client will see your Shakers profile and a chat with the team opens. Your profile does not change.`,
967
+ reviewNotice: 'On this project Shakers reviews your application before the client sees it.',
968
+ confirmQuestion: 'Shall I send it? Answer yes or no.',
969
+ sent: 'Application sent. You can follow it in your applications.',
970
+ sentPending: 'Application sent. Shakers reviews it before the client sees it.',
971
+ alreadyApplied: (status) => (status ? `You already applied to this project (status: ${status}).` : 'You already applied to this project.'),
972
+ },
973
+ it: {
974
+ untitled: 'Senza titolo',
975
+ mainMotivationFallback: 'Qual è la tua principale motivazione per candidarti?',
976
+ notOpen: 'Questo progetto non accetta la tua candidatura in questo momento: ti sei già candidato, è chiuso o non è disponibile per te.',
977
+ summaryTitle: 'Ecco cosa verrà inviato:',
978
+ titleLine: (title) => `Progetto: ${title}`,
979
+ companyLine: (name) => `Azienda: ${name}`,
980
+ messageLine: (message) => `Messaggio: ${message}`,
981
+ empty: '(vuoto)',
982
+ answerLine: (question, answer) => `${question}: ${answer}`,
983
+ sendNotice: (title) => `Vengono inviati il tuo messaggio e le tue risposte per «${title}»: il cliente vedrà il tuo profilo Shakers e si apre una chat con il team. Il tuo profilo non cambia.`,
984
+ reviewNotice: 'In questo progetto Shakers rivede la tua candidatura prima che la veda il cliente.',
985
+ confirmQuestion: 'La invio? Rispondi sì o no.',
986
+ sent: 'Candidatura inviata. Puoi seguirla tra le tue candidature.',
987
+ sentPending: 'Candidatura inviata. Shakers la rivede prima che la veda il cliente.',
988
+ alreadyApplied: (status) => (status ? `Ti sei già candidato a questo progetto (stato: ${status}).` : 'Ti sei già candidato a questo progetto.'),
989
+ },
990
+ pt: {
991
+ untitled: 'Sem título',
992
+ mainMotivationFallback: 'Qual é a sua principal motivação para se candidatar?',
993
+ notOpen: 'Este projeto não aceita a sua candidatura neste momento: já se candidatou, está fechado ou não está disponível para si.',
994
+ summaryTitle: 'Isto é o que será enviado:',
995
+ titleLine: (title) => `Projeto: ${title}`,
996
+ companyLine: (name) => `Empresa: ${name}`,
997
+ messageLine: (message) => `Mensagem: ${message}`,
998
+ empty: '(vazio)',
999
+ answerLine: (question, answer) => `${question}: ${answer}`,
1000
+ sendNotice: (title) => `São enviadas a sua mensagem e as suas respostas para «${title}»: o cliente verá o seu perfil Shakers e abre-se um chat com a equipa. O seu perfil não muda.`,
1001
+ reviewNotice: 'Neste projeto a Shakers revê a sua candidatura antes de o cliente a ver.',
1002
+ confirmQuestion: 'Envio-a? Responda sim ou não.',
1003
+ sent: 'Candidatura enviada. Pode acompanhá-la nas suas candidaturas.',
1004
+ sentPending: 'Candidatura enviada. A Shakers revê-a antes de o cliente a ver.',
1005
+ alreadyApplied: (status) => (status ? `Já se candidatou a este projeto (estado: ${status}).` : 'Já se candidatou a este projeto.'),
1006
+ },
1007
+ };
1008
+
584
1009
  function signupCopy(lang) {
585
1010
  return COPY[toSignupLanguage(lang) || 'en'];
586
1011
  }
587
1012
 
1013
+ function trustCopy(lang) {
1014
+ return TRUST[toSignupLanguage(lang) || 'en'];
1015
+ }
1016
+
588
1017
  function legalCopy(lang) {
589
1018
  return LEGAL[toSignupLanguage(lang) || 'en'];
590
1019
  }
@@ -593,7 +1022,15 @@ function reviewCopy(lang) {
593
1022
  return REVIEW[toSignupLanguage(lang) || 'en'];
594
1023
  }
595
1024
 
596
- // The profile draft the talent confirms before the account exists: one line per group, empty parts left out.
1025
+ function applyCopy(lang) {
1026
+ return APPLY[toSignupLanguage(lang) || 'en'];
1027
+ }
1028
+
1029
+ function detectedAgentsCopy(lang) {
1030
+ return DETECTED_AGENTS[toSignupLanguage(lang) || 'en'];
1031
+ }
1032
+
1033
+ // The profile draft the talent confirms before the account exists, with every field the account is created with: one line per group, empty parts left out.
597
1034
  function renderDraft(lang, d = {}) {
598
1035
  const c = signupCopy(lang);
599
1036
  const has = (v) => v !== null && v !== undefined && String(v).trim() !== '';
@@ -606,6 +1043,7 @@ function renderDraft(lang, d = {}) {
606
1043
  joinParts([has(d.yearsOfExperience) ? c.years(d.yearsOfExperience) : null, d.stack]),
607
1044
  joinParts([d.languages, d.workMode, has(d.monthlyHours) ? c.hours(d.monthlyHours) : null]),
608
1045
  has(d.hourlyRate) || has(d.annualRate) ? c.rates(has(d.hourlyRate) ? d.hourlyRate : null, has(d.annualRate) ? d.annualRate : null, d.ratesFromTalent !== true) : '',
1046
+ has(d.email) ? `${c.emailLabel}: ${d.email}` : '',
609
1047
  ].filter(Boolean);
610
1048
  return [c.draftTitle, ...lines].join('\n');
611
1049
  }
@@ -622,4 +1060,4 @@ function renderRole(lang, { name, source, evidence, openProjects, recommended })
622
1060
  ].filter(Boolean).join('\n');
623
1061
  }
624
1062
 
625
- module.exports = { SIGNUP_LANGUAGES, LEGAL, REVIEW, signupCopy, legalCopy, reviewCopy, toSignupLanguage, renderDraft, renderRole, amount };
1063
+ module.exports = { SIGNUP_LANGUAGES, LEGAL, REVIEW, signupCopy, legalCopy, trustCopy, termsText, reviewCopy, detectedAgentsCopy, applyCopy, toSignupLanguage, renderDraft, renderRole, amount };