shakers-cli-beta 0.10.3 → 0.10.4
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.
- package/package.json +1 -1
- package/src/certify-dimension-client.js +79 -0
- package/src/certify-dimension-flow.js +104 -20
- package/src/config.js +9 -0
- package/src/i18n.js +98 -32
- package/src/mcp-ai-usage-tool.js +13 -32
- package/src/mcp-certify-dimension-tools.js +7 -1
- package/src/mcp-choice.js +41 -19
- package/src/mcp-instructions.js +9 -9
- package/src/mcp-register-tools.js +87 -29
- package/src/mcp-role-tools.js +79 -11
- package/src/mcp-signup-tools.js +187 -59
- package/src/register-context.js +3 -1
- package/src/signup-copy.js +49 -17
package/src/signup-copy.js
CHANGED
|
@@ -21,14 +21,14 @@ const LEGAL = {
|
|
|
21
21
|
interviewGoalDuration: 'Goal: learn your role, your dedication and how you use AI. Duration: ~5 minutes.',
|
|
22
22
|
},
|
|
23
23
|
it: {
|
|
24
|
-
signupNotice: 'Prima di creare il tuo account: conserviamo i dati che mi dai (il tuo LinkedIn, il tuo CV e 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
|
|
24
|
+
signupNotice: 'Prima di creare il tuo account: conserviamo i dati che mi dai (il tuo LinkedIn, il tuo CV e 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
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.',
|
|
26
26
|
usageGoalDuration: "Obiettivo: misurare come lavori con l'IA per il tuo profilo. Durata: ~1-2 minuti.",
|
|
27
27
|
interviewInfoAccessed: 'Accesso a: le tue risposte in questo colloquio e il tuo profilo importato. Lo conduce il server di Shakers.',
|
|
28
28
|
interviewGoalDuration: "Obiettivo: conoscere il tuo ruolo, la tua disponibilità e come usi l'IA. Durata: ~5 minuti.",
|
|
29
29
|
},
|
|
30
30
|
pt: {
|
|
31
|
-
signupNotice: 'Antes de criar a sua conta: guardamos os dados que me der (o seu LinkedIn, o seu CV e 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
|
|
31
|
+
signupNotice: 'Antes de criar a sua conta: guardamos os dados que me der (o seu LinkedIn, o seu CV e 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
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.',
|
|
33
33
|
usageGoalDuration: 'Objetivo: medir como trabalha com IA para o seu perfil. Duração: ~1-2 minutos.',
|
|
34
34
|
interviewInfoAccessed: 'Acede-se a: as suas respostas nesta entrevista e o seu perfil importado. É conduzida pelo servidor da Shakers.',
|
|
@@ -65,11 +65,11 @@ const COPY = {
|
|
|
65
65
|
draftTitle: 'Esto es lo que he preparado para tu perfil:',
|
|
66
66
|
years: (n) => `${n} años de experiencia`,
|
|
67
67
|
hours: (h) => `${h} h/mes`,
|
|
68
|
-
rates: (hourly, annual) => `Tarifa estimada: ${joinParts([hourly != null ? `${amount('es', hourly)} €/h` : null, annual != null ? `${amount('es', annual)} €/año` : null])} (estimación mía, ajústala)
|
|
68
|
+
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])}`),
|
|
69
69
|
draftQuestion: '¿Está bien o cambio algo?',
|
|
70
70
|
draftOk: 'Todo correcto',
|
|
71
71
|
draftChange: 'Quiero cambiar algo',
|
|
72
|
-
|
|
72
|
+
windowNotice: 'Cuando confirmes, te abro una ventana en el navegador para crear tu cuenta y guardar todo esto. Allí eliges 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. Cuando termines, vuelve aquí y seguimos.',
|
|
73
73
|
windowLink: (link) => `Si no se te ha abierto la ventana, entra desde aquí: ${link}`,
|
|
74
74
|
existingTitle: 'He visto que ya tienes cuenta. ¿Actualizo tu perfil con esto?',
|
|
75
75
|
existingRate: (from, to) => `Tarifa por hora: ${from} → ${to}`,
|
|
@@ -80,10 +80,14 @@ const COPY = {
|
|
|
80
80
|
existingYes: 'Sí, actualízalo',
|
|
81
81
|
existingNo: 'No, déjalo como está',
|
|
82
82
|
existingKept: 'Perfecto, dejo tu perfil como está.',
|
|
83
|
+
keptLabels: { workSituation: 'Situación laboral', availability: 'Disponibilidad', languages: 'Idiomas', phone: 'Teléfono' },
|
|
84
|
+
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.`,
|
|
83
85
|
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. Te ayuda a destacar en proyectos de IA.',
|
|
84
86
|
aiUsageYes: 'Sí, analízalo',
|
|
85
87
|
aiUsageSkip: 'Saltar este paso',
|
|
86
88
|
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'.",
|
|
89
|
+
importSources: { linkedin: 'tu LinkedIn', cv: 'tu CV', github: 'tu GitHub', web: 'tu web' },
|
|
90
|
+
importFailed: (names) => `No he podido importar ${names.join(' y ')} ahora mismo. El resto de tu perfil está guardado; lo que falte lo puedes añadir en la web.`,
|
|
87
91
|
interviewWhereQuestion: '¿Hacemos la entrevista de onboarding aquí ahora o luego en la web?',
|
|
88
92
|
interviewHere: 'Aquí ahora',
|
|
89
93
|
interviewLater: 'Luego en la web',
|
|
@@ -91,6 +95,7 @@ const COPY = {
|
|
|
91
95
|
answerModeDrafted: 'Propónmelas tú',
|
|
92
96
|
answerModeOwn: 'Las contesto yo',
|
|
93
97
|
answerModeFull: 'Hazla entera',
|
|
98
|
+
interviewOnWeb: 'La entrevista de onboarding la puedes hacer en la web: al terminar te abro Shakers con tu sesión iniciada.',
|
|
94
99
|
livekitUnavailable: 'La entrevista no puede arrancar en este equipo. Puedes hacerla en la web: te abro Shakers con tu sesión iniciada.',
|
|
95
100
|
rolesIntro: 'Con todo lo que me has contado, encajas en estos perfiles:',
|
|
96
101
|
roleRecommended: (name) => `**${name}** (recomendado)`,
|
|
@@ -100,11 +105,14 @@ const COPY = {
|
|
|
100
105
|
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.',
|
|
101
106
|
mainRoleQuestion: '¿Cuál es tu perfil principal?',
|
|
102
107
|
mainRoleOther: 'Otro rol del catálogo',
|
|
108
|
+
mainRoleAsk: '¿A qué te dedicas? Dime tu rol con tus palabras y lo busco en el catálogo.',
|
|
109
|
+
mainRoleNotListed: 'Otro rol',
|
|
110
|
+
catalogueReason: { role: (words) => `Encaja con tu rol (${words})`, stack: (words) => `Encaja con tu stack (${words})` },
|
|
103
111
|
hourlyRateLabel: 'Tarifa por hora',
|
|
104
112
|
annualRateLabel: 'Tarifa anual',
|
|
105
113
|
perHour: '€/h',
|
|
106
114
|
perYear: '€/año',
|
|
107
|
-
ratesSaved: (rates) => `${rates.join(' · ')}. Es una estimación: la puedes cambiar en la web
|
|
115
|
+
ratesSaved: (rates, estimated = true) => `${rates.join(' · ')}. ${estimated ? 'Es una estimación: la puedes cambiar en la web.' : 'La puedes cambiar en la web.'}`,
|
|
108
116
|
saved: { workSituation: 'Situación laboral guardada.', availability: 'Disponibilidad guardada.', languages: 'Idiomas guardados.', phone: 'Teléfono guardado.' },
|
|
109
117
|
yes: 'Sí',
|
|
110
118
|
no: 'No',
|
|
@@ -127,11 +135,11 @@ const COPY = {
|
|
|
127
135
|
draftTitle: 'Here is what I have prepared for your profile:',
|
|
128
136
|
years: (n) => `${n} years of experience`,
|
|
129
137
|
hours: (h) => `${h} h/month`,
|
|
130
|
-
rates: (hourly, annual) => `Estimated rate: ${joinParts([hourly != null ? `${amount('en', hourly)} €/h` : null, annual != null ? `${amount('en', annual)} €/year` : null])} (my estimate, adjust it)
|
|
138
|
+
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])}`),
|
|
131
139
|
draftQuestion: 'Does it look right, or shall I change something?',
|
|
132
140
|
draftOk: 'All correct',
|
|
133
141
|
draftChange: 'I want to change something',
|
|
134
|
-
|
|
142
|
+
windowNotice: "When you confirm, I'll open a window in your browser to create your account and save all this. There you 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. When you're done, come back here and we'll carry on.",
|
|
135
143
|
windowLink: (link) => `If the window did not open, use this link: ${link}`,
|
|
136
144
|
existingTitle: 'I see you already have an account. Shall I update your profile with this?',
|
|
137
145
|
existingRate: (from, to) => `Hourly rate: ${from} → ${to}`,
|
|
@@ -142,10 +150,14 @@ const COPY = {
|
|
|
142
150
|
existingYes: 'Yes, update it',
|
|
143
151
|
existingNo: 'No, leave it as it is',
|
|
144
152
|
existingKept: 'Sure, I am leaving your profile as it is.',
|
|
153
|
+
keptLabels: { workSituation: 'Work situation', availability: 'Availability', languages: 'Languages', phone: 'Phone' },
|
|
154
|
+
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.`,
|
|
145
155
|
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. It helps you stand out for AI projects.',
|
|
146
156
|
aiUsageYes: 'Yes, analyse it',
|
|
147
157
|
aiUsageSkip: 'Skip this step',
|
|
148
158
|
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'.",
|
|
159
|
+
importSources: { linkedin: 'your LinkedIn', cv: 'your CV', github: 'your GitHub', web: 'your website' },
|
|
160
|
+
importFailed: (names) => `I couldn't import ${names.join(' and ')} right now. The rest of your profile is saved; you can add what is missing on the web.`,
|
|
149
161
|
interviewWhereQuestion: 'Shall we do the onboarding interview here now or later on the web?',
|
|
150
162
|
interviewHere: 'Here now',
|
|
151
163
|
interviewLater: 'Later on the web',
|
|
@@ -153,6 +165,7 @@ const COPY = {
|
|
|
153
165
|
answerModeDrafted: 'Draft them for me',
|
|
154
166
|
answerModeOwn: "I'll answer myself",
|
|
155
167
|
answerModeFull: 'Do it all for me',
|
|
168
|
+
interviewOnWeb: 'You can do the onboarding interview on the web: when we finish I will open Shakers for you, already signed in.',
|
|
156
169
|
livekitUnavailable: 'The interview cannot start on this machine. You can do it on the web: I will open Shakers with you signed in.',
|
|
157
170
|
rolesIntro: 'With everything you have told me, you fit these profiles:',
|
|
158
171
|
roleRecommended: (name) => `**${name}** (recommended)`,
|
|
@@ -162,17 +175,20 @@ const COPY = {
|
|
|
162
175
|
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.',
|
|
163
176
|
mainRoleQuestion: 'Which is your main profile?',
|
|
164
177
|
mainRoleOther: 'Another role from the catalogue',
|
|
178
|
+
mainRoleAsk: 'What do you do? Tell me your role in your own words and I will look it up in the catalogue.',
|
|
179
|
+
mainRoleNotListed: 'Another role',
|
|
180
|
+
catalogueReason: { role: (words) => `Matches your role (${words})`, stack: (words) => `Matches your stack (${words})` },
|
|
165
181
|
hourlyRateLabel: 'Hourly rate',
|
|
166
182
|
annualRateLabel: 'Annual rate',
|
|
167
183
|
perHour: '€/hour',
|
|
168
184
|
perYear: '€/year',
|
|
169
|
-
ratesSaved: (rates) => `${rates.join(' · ')}. It is an estimate: you can change it on the web
|
|
185
|
+
ratesSaved: (rates, estimated = true) => `${rates.join(' · ')}. ${estimated ? 'It is an estimate: you can change it on the web.' : 'You can change it on the web.'}`,
|
|
170
186
|
saved: { workSituation: 'Work situation saved.', availability: 'Availability saved.', languages: 'Languages saved.', phone: 'Phone saved.' },
|
|
171
187
|
yes: 'Yes',
|
|
172
188
|
no: 'No',
|
|
173
189
|
},
|
|
174
190
|
it: {
|
|
175
|
-
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.
|
|
191
|
+
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.`,
|
|
176
192
|
welcomeQuestion: 'Iniziamo? Se mi dai il permesso, cerco il tuo CV su questo computer e ti preparo una bozza da confermare e basta.',
|
|
177
193
|
cvSearch: 'Sì, cercalo',
|
|
178
194
|
cvAttach: 'Te lo allego io',
|
|
@@ -189,11 +205,11 @@ const COPY = {
|
|
|
189
205
|
draftTitle: 'Ecco cosa ho preparato per il tuo profilo:',
|
|
190
206
|
years: (n) => `${n} anni di esperienza`,
|
|
191
207
|
hours: (h) => `${h} h/mese`,
|
|
192
|
-
rates: (hourly, annual) => `Tariffa stimata: ${joinParts([hourly != null ? `${amount('it', hourly)} €/h` : null, annual != null ? `${amount('it', annual)} €/anno` : null])} (una mia stima, modificala pure)
|
|
208
|
+
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])}`),
|
|
193
209
|
draftQuestion: 'Va bene o cambio qualcosa?',
|
|
194
210
|
draftOk: 'Tutto corretto',
|
|
195
211
|
draftChange: 'Voglio cambiare qualcosa',
|
|
196
|
-
|
|
212
|
+
windowNotice: "Quando confermi, ti apro una finestra nel browser per creare il tuo account e salvare tutto questo. Lì 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. Quando hai finito, torna qui e continuiamo.",
|
|
197
213
|
windowLink: (link) => `Se la finestra non si è aperta, entra da qui: ${link}`,
|
|
198
214
|
existingTitle: 'Ho visto che hai già un account. Aggiorno il tuo profilo con questi dati?',
|
|
199
215
|
existingRate: (from, to) => `Tariffa oraria: ${from} → ${to}`,
|
|
@@ -204,10 +220,14 @@ const COPY = {
|
|
|
204
220
|
existingYes: 'Sì, aggiornalo',
|
|
205
221
|
existingNo: 'No, lascialo così',
|
|
206
222
|
existingKept: 'Perfetto, lascio il tuo profilo così com’è.',
|
|
223
|
+
keptLabels: { workSituation: 'Situazione lavorativa', availability: 'Disponibilità', languages: 'Lingue', phone: 'Telefono' },
|
|
224
|
+
existingUnchanged: (items) => `Questi dati del tuo profilo non li ho cambiati, restano com'erano: ${items.join(' · ')}. Se vuoi altri valori, cambiali sul sito.`,
|
|
207
225
|
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. Ti aiuta a distinguerti nei progetti di IA.",
|
|
208
226
|
aiUsageYes: 'Sì, analizzalo',
|
|
209
227
|
aiUsageSkip: 'Salta questo passaggio',
|
|
210
228
|
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'.",
|
|
229
|
+
importSources: { linkedin: 'il tuo LinkedIn', cv: 'il tuo CV', github: 'il tuo GitHub', web: 'il tuo sito' },
|
|
230
|
+
importFailed: (names) => `Non sono riuscito a importare ${names.join(' e ')} in questo momento. Il resto del tuo profilo è salvato; quello che manca puoi aggiungerlo sul sito.`,
|
|
211
231
|
interviewWhereQuestion: 'Facciamo il colloquio di onboarding qui adesso o più tardi sul sito?',
|
|
212
232
|
interviewHere: 'Qui adesso',
|
|
213
233
|
interviewLater: 'Più tardi sul sito',
|
|
@@ -215,6 +235,7 @@ const COPY = {
|
|
|
215
235
|
answerModeDrafted: 'Proponile tu',
|
|
216
236
|
answerModeOwn: 'Rispondo io',
|
|
217
237
|
answerModeFull: 'Fallo tutto tu',
|
|
238
|
+
interviewOnWeb: 'Il colloquio di onboarding puoi farlo sul sito: alla fine ti apro Shakers con la sessione già avviata.',
|
|
218
239
|
livekitUnavailable: 'Il colloquio non può partire su questo computer. Puoi farlo sul sito: ti apro Shakers con la sessione già avviata.',
|
|
219
240
|
rolesIntro: 'Con tutto quello che mi hai raccontato, sei adatto a questi profili:',
|
|
220
241
|
roleRecommended: (name) => `**${name}** (consigliato)`,
|
|
@@ -224,17 +245,20 @@ const COPY = {
|
|
|
224
245
|
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.',
|
|
225
246
|
mainRoleQuestion: 'Qual è il tuo profilo principale?',
|
|
226
247
|
mainRoleOther: 'Un altro ruolo del catalogo',
|
|
248
|
+
mainRoleAsk: 'Di cosa ti occupi? Dimmi il tuo ruolo con parole tue e lo cerco nel catalogo.',
|
|
249
|
+
mainRoleNotListed: 'Un altro ruolo',
|
|
250
|
+
catalogueReason: { role: (words) => `Corrisponde al tuo ruolo (${words})`, stack: (words) => `Corrisponde al tuo stack (${words})` },
|
|
227
251
|
hourlyRateLabel: 'Tariffa oraria',
|
|
228
252
|
annualRateLabel: 'Tariffa annuale',
|
|
229
253
|
perHour: '€/h',
|
|
230
254
|
perYear: '€/anno',
|
|
231
|
-
ratesSaved: (rates) => `${rates.join(' · ')}. È una stima: puoi cambiarla sul sito
|
|
255
|
+
ratesSaved: (rates, estimated = true) => `${rates.join(' · ')}. ${estimated ? 'È una stima: puoi cambiarla sul sito.' : 'Puoi cambiarla sul sito.'}`,
|
|
232
256
|
saved: { workSituation: 'Situazione lavorativa salvata.', availability: 'Disponibilità salvata.', languages: 'Lingue salvate.', phone: 'Telefono salvato.' },
|
|
233
257
|
yes: 'Sì',
|
|
234
258
|
no: 'No',
|
|
235
259
|
},
|
|
236
260
|
pt: {
|
|
237
|
-
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.
|
|
261
|
+
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.`,
|
|
238
262
|
welcomeQuestion: 'Começamos? Se me der autorização, procuro o seu CV neste computador e preparo um rascunho para que só tenha de o confirmar.',
|
|
239
263
|
cvSearch: 'Sim, procure-o',
|
|
240
264
|
cvAttach: 'Eu anexo-o',
|
|
@@ -251,11 +275,11 @@ const COPY = {
|
|
|
251
275
|
draftTitle: 'Isto é o que preparei para o seu perfil:',
|
|
252
276
|
years: (n) => `${n} anos de experiência`,
|
|
253
277
|
hours: (h) => `${h} h/mês`,
|
|
254
|
-
rates: (hourly, annual) => `Tarifa estimada: ${joinParts([hourly != null ? `${amount('pt', hourly)} €/h` : null, annual != null ? `${amount('pt', annual)} €/ano` : null])} (estimativa minha, ajuste-a)
|
|
278
|
+
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])}`),
|
|
255
279
|
draftQuestion: 'Está bem ou mudo alguma coisa?',
|
|
256
280
|
draftOk: 'Está tudo certo',
|
|
257
281
|
draftChange: 'Quero mudar algo',
|
|
258
|
-
|
|
282
|
+
windowNotice: 'Quando confirmar, abro uma janela no seu navegador para criar a sua conta e guardar tudo isto. Lá escolhe 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. Quando terminar, volte aqui e continuamos.',
|
|
259
283
|
windowLink: (link) => `Se a janela não abriu, entre por aqui: ${link}`,
|
|
260
284
|
existingTitle: 'Vi que já tem conta. Atualizo o seu perfil com isto?',
|
|
261
285
|
existingRate: (from, to) => `Tarifa por hora: ${from} → ${to}`,
|
|
@@ -266,10 +290,14 @@ const COPY = {
|
|
|
266
290
|
existingYes: 'Sim, atualize',
|
|
267
291
|
existingNo: 'Não, deixe como está',
|
|
268
292
|
existingKept: 'Perfeito, deixo o seu perfil como está.',
|
|
293
|
+
keptLabels: { workSituation: 'Situação profissional', availability: 'Disponibilidade', languages: 'Idiomas', phone: 'Telefone' },
|
|
294
|
+
existingUnchanged: (items) => `Estes dados do seu perfil não os alterei, ficam como estavam: ${items.join(' · ')}. Se quiser outros valores, altere-os na web.`,
|
|
269
295
|
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. Ajuda-o a destacar-se em projetos de IA.',
|
|
270
296
|
aiUsageYes: 'Sim, analise',
|
|
271
297
|
aiUsageSkip: 'Saltar este passo',
|
|
272
298
|
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'.",
|
|
299
|
+
importSources: { linkedin: 'o seu LinkedIn', cv: 'o seu CV', github: 'o seu GitHub', web: 'o seu site' },
|
|
300
|
+
importFailed: (names) => `Não consegui importar ${names.join(' e ')} neste momento. O resto do seu perfil está guardado; o que faltar pode acrescentá-lo na web.`,
|
|
273
301
|
interviewWhereQuestion: 'Fazemos a entrevista de onboarding aqui agora ou mais tarde na web?',
|
|
274
302
|
interviewHere: 'Aqui agora',
|
|
275
303
|
interviewLater: 'Mais tarde na web',
|
|
@@ -277,6 +305,7 @@ const COPY = {
|
|
|
277
305
|
answerModeDrafted: 'Proponha-as você',
|
|
278
306
|
answerModeOwn: 'Respondo eu',
|
|
279
307
|
answerModeFull: 'Faça-a toda',
|
|
308
|
+
interviewOnWeb: 'Pode fazer a entrevista de onboarding na web: no fim abro-lhe o Shakers com a sessão iniciada.',
|
|
280
309
|
livekitUnavailable: 'A entrevista não pode começar neste computador. Pode fazê-la na web: abro-lhe a Shakers com a sessão iniciada.',
|
|
281
310
|
rolesIntro: 'Com tudo o que me contou, encaixa nestes perfis:',
|
|
282
311
|
roleRecommended: (name) => `**${name}** (recomendado)`,
|
|
@@ -286,11 +315,14 @@ const COPY = {
|
|
|
286
315
|
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.',
|
|
287
316
|
mainRoleQuestion: 'Qual é o seu perfil principal?',
|
|
288
317
|
mainRoleOther: 'Outro perfil do catálogo',
|
|
318
|
+
mainRoleAsk: 'A que se dedica? Diga-me o seu perfil por palavras suas e eu procuro-o no catálogo.',
|
|
319
|
+
mainRoleNotListed: 'Outro perfil',
|
|
320
|
+
catalogueReason: { role: (words) => `Corresponde ao seu perfil (${words})`, stack: (words) => `Corresponde ao seu stack (${words})` },
|
|
289
321
|
hourlyRateLabel: 'Tarifa por hora',
|
|
290
322
|
annualRateLabel: 'Tarifa anual',
|
|
291
323
|
perHour: '€/h',
|
|
292
324
|
perYear: '€/ano',
|
|
293
|
-
ratesSaved: (rates) => `${rates.join(' · ')}. É uma estimativa: pode alterá-la na web
|
|
325
|
+
ratesSaved: (rates, estimated = true) => `${rates.join(' · ')}. ${estimated ? 'É uma estimativa: pode alterá-la na web.' : 'Pode alterá-la na web.'}`,
|
|
294
326
|
saved: { workSituation: 'Situação profissional guardada.', availability: 'Disponibilidade guardada.', languages: 'Idiomas guardados.', phone: 'Telefone guardado.' },
|
|
295
327
|
yes: 'Sim',
|
|
296
328
|
no: 'Não',
|
|
@@ -318,7 +350,7 @@ function renderDraft(lang, d = {}) {
|
|
|
318
350
|
joinParts([d.name, d.role, d.city]),
|
|
319
351
|
joinParts([has(d.yearsOfExperience) ? c.years(d.yearsOfExperience) : null, d.stack]),
|
|
320
352
|
joinParts([d.languages, d.workMode, has(d.monthlyHours) ? c.hours(d.monthlyHours) : null]),
|
|
321
|
-
has(d.hourlyRate) || has(d.annualRate) ? c.rates(has(d.hourlyRate) ? d.hourlyRate : null, has(d.annualRate) ? d.annualRate : null) : '',
|
|
353
|
+
has(d.hourlyRate) || has(d.annualRate) ? c.rates(has(d.hourlyRate) ? d.hourlyRate : null, has(d.annualRate) ? d.annualRate : null, d.ratesFromTalent !== true) : '',
|
|
322
354
|
].filter(Boolean);
|
|
323
355
|
return [c.draftTitle, ...lines].join('\n');
|
|
324
356
|
}
|