shakers-cli-beta 0.11.0 → 0.13.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.
- package/bin/certify.js +1 -1
- package/bin/login.js +44 -111
- package/package.json +1 -1
- package/src/agents-client.js +1 -1
- package/src/ai-profile-client.js +1 -1
- package/src/applications-client.js +1 -1
- package/src/authorship-mismatch.js +62 -0
- package/src/availability-client.js +1 -1
- package/src/certifications-client.js +1 -1
- package/src/certify-dimension-client.js +0 -79
- package/src/certify-dimension-flow.js +24 -105
- package/src/config.js +40 -22
- package/src/find-projects-client.js +1 -1
- package/src/i18n.js +34 -84
- package/src/lang-client.js +1 -1
- package/src/mcp-auth-form.js +39 -10
- package/src/mcp-choice.js +3 -3
- package/src/mcp-device-login-tools.js +3 -3
- package/src/mcp-instructions.js +7 -7
- package/src/mcp-onboarding-tools.js +10 -16
- package/src/mcp-prompts.js +1 -1
- package/src/mcp-register-tools.js +1 -1
- package/src/mcp-role-tools.js +26 -10
- package/src/mcp-server.js +9 -2
- package/src/mcp-signup-client.js +1 -1
- package/src/mcp-signup-tools.js +142 -50
- package/src/profile-client.js +12 -4
- package/src/rate-client.js +1 -2
- package/src/roles-client.js +0 -4
- package/src/saved-positions-client.js +1 -1
- package/src/show-project-client.js +1 -1
- package/src/signup-copy.js +59 -33
- package/src/terminal-agents.js +8 -0
- package/src/usage-run-steps.js +3 -0
package/src/signup-copy.js
CHANGED
|
@@ -4,31 +4,31 @@
|
|
|
4
4
|
|
|
5
5
|
const SIGNUP_LANGUAGES = ['es', 'en', 'it', 'pt'];
|
|
6
6
|
|
|
7
|
-
// Legal texts.
|
|
7
|
+
// Legal texts. The CLI onboarding reads the es/en ones too.
|
|
8
8
|
const LEGAL = {
|
|
9
9
|
es: {
|
|
10
|
-
signupNotice:
|
|
10
|
+
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
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.',
|
|
12
12
|
usageGoalDuration: 'Objetivo: medir cómo trabajas con IA para tu perfil. Duración: ~1-2 minutos.',
|
|
13
13
|
interviewInfoAccessed: 'Se accede a: tus respuestas en esta entrevista y tu perfil importado. La conduce el servidor de Shakers.',
|
|
14
14
|
interviewGoalDuration: 'Objetivo: conocer tu rol, tu dedicación y cómo usas la IA. Duración: ~5 minutos.',
|
|
15
15
|
},
|
|
16
16
|
en: {
|
|
17
|
-
signupNotice:
|
|
17
|
+
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
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.',
|
|
19
19
|
usageGoalDuration: 'Goal: measure how you work with AI for your profile. Duration: ~1-2 minutes.',
|
|
20
20
|
interviewInfoAccessed: 'Accessed: your answers in this interview and your imported profile. It is conducted by the Shakers server.',
|
|
21
21
|
interviewGoalDuration: 'Goal: learn your role, your dedication and how you use AI. Duration: ~5 minutes.',
|
|
22
22
|
},
|
|
23
23
|
it: {
|
|
24
|
-
signupNotice:
|
|
24
|
+
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
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:
|
|
31
|
+
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
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.',
|
|
@@ -38,6 +38,16 @@ const LEGAL = {
|
|
|
38
38
|
|
|
39
39
|
const LOCALES = { es: 'es-ES', en: 'en-GB', it: 'it-IT', pt: 'pt-PT' };
|
|
40
40
|
|
|
41
|
+
// "a, b and c" in the talent's language.
|
|
42
|
+
function listOf(lang, items) {
|
|
43
|
+
return new Intl.ListFormat(LOCALES[lang], { type: 'conjunction' }).format(items);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// The names of only the sources the talent actually gave (cv, linkedin), from a per-language table.
|
|
47
|
+
function named(sources, names) {
|
|
48
|
+
return (sources || []).map((s) => names[s]).filter(Boolean);
|
|
49
|
+
}
|
|
50
|
+
|
|
41
51
|
function joinParts(parts) {
|
|
42
52
|
return parts.filter((p) => p !== null && p !== undefined && String(p).trim() !== '').join(' · ');
|
|
43
53
|
}
|
|
@@ -49,10 +59,10 @@ function amount(lang, value) {
|
|
|
49
59
|
const COPY = {
|
|
50
60
|
es: {
|
|
51
61
|
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.`,
|
|
52
|
-
welcomeQuestion: '¿Empezamos?
|
|
53
|
-
cvSearch: '
|
|
54
|
-
cvAttach: 'Te lo
|
|
55
|
-
cvAttachAsk: 'Perfecto,
|
|
62
|
+
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.',
|
|
63
|
+
cvSearch: 'Búscalo en mi equipo',
|
|
64
|
+
cvAttach: 'Te lo paso',
|
|
65
|
+
cvAttachAsk: 'Perfecto, adjúntalo aquí o dime dónde está en tu equipo.',
|
|
56
66
|
cvNotFound: 'No he encontrado tu CV en este equipo. ¿Me lo adjuntas aquí?',
|
|
57
67
|
linkedinAsk: '¿Me pasas tu perfil de LinkedIn?',
|
|
58
68
|
linkedinOpening: 'Te abro tu perfil de LinkedIn en el navegador para que copies la dirección.',
|
|
@@ -69,20 +79,24 @@ const COPY = {
|
|
|
69
79
|
draftQuestion: '¿Está bien o cambio algo?',
|
|
70
80
|
draftOk: 'Todo correcto',
|
|
71
81
|
draftChange: 'Quiero cambiar algo',
|
|
72
|
-
windowNotice: 'Cuando confirmes,
|
|
82
|
+
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.',
|
|
83
|
+
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.',
|
|
73
84
|
windowLink: (link) => `Si no se te ha abierto la ventana, entra desde aquí: ${link}`,
|
|
74
85
|
existingTitle: 'He visto que ya tienes cuenta. ¿Actualizo tu perfil con esto?',
|
|
75
86
|
existingRate: (from, to) => `Tarifa por hora: ${from} → ${to}`,
|
|
76
87
|
existingAnnual: (from, to) => `Tarifa anual: ${from} → ${to}`,
|
|
77
88
|
existingHours: (from, to) => `Disponibilidad: ${from} → ${to}`,
|
|
78
89
|
existingEmpty: 'sin indicar',
|
|
79
|
-
|
|
90
|
+
existingUpdateQuestion: '¿Actualizo tu perfil con esto?',
|
|
91
|
+
existingStays: (label, kept, requested) => (kept ? `${label}: se queda en ${kept}${kept === requested ? '' : `, no ${requested}`}` : `${label}: sin cambios`),
|
|
92
|
+
existingStaysNote: 'Lo que ya tenías relleno no lo cambio desde aquí: si quieres otros valores, cámbialos en la web.',
|
|
93
|
+
existingLists: (sources) => `Añado lo nuevo de ${listOf('es', named(sources, COPY.es.importSources))} (experiencias, proyectos, formación) y relleno lo que tengas vacío, sin borrar nada.`,
|
|
80
94
|
existingYes: 'Sí, actualízalo',
|
|
81
95
|
existingNo: 'No, déjalo como está',
|
|
82
96
|
existingKept: 'Perfecto, dejo tu perfil como está.',
|
|
83
97
|
keptLabels: { workSituation: 'Situación laboral', availability: 'Disponibilidad', languages: 'Idiomas', phone: 'Teléfono' },
|
|
84
98
|
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.`,
|
|
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.',
|
|
99
|
+
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.',
|
|
86
100
|
aiUsageYes: 'Sí, analízalo',
|
|
87
101
|
aiUsageSkip: 'Saltar este paso',
|
|
88
102
|
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'.",
|
|
@@ -119,10 +133,10 @@ const COPY = {
|
|
|
119
133
|
},
|
|
120
134
|
en: {
|
|
121
135
|
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.`,
|
|
122
|
-
welcomeQuestion: "Shall we start?
|
|
123
|
-
cvSearch: '
|
|
124
|
-
cvAttach: "I'll
|
|
125
|
-
cvAttachAsk: 'Great, attach it here.',
|
|
136
|
+
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.",
|
|
137
|
+
cvSearch: 'Look for it on my computer',
|
|
138
|
+
cvAttach: "I'll send it",
|
|
139
|
+
cvAttachAsk: 'Great, attach it here or tell me where it is on your computer.',
|
|
126
140
|
cvNotFound: "I couldn't find your CV on this computer. Could you attach it here?",
|
|
127
141
|
linkedinAsk: 'Could you send me your LinkedIn profile?',
|
|
128
142
|
linkedinOpening: 'I am opening your LinkedIn profile in your browser so you can copy its address.',
|
|
@@ -139,20 +153,24 @@ const COPY = {
|
|
|
139
153
|
draftQuestion: 'Does it look right, or shall I change something?',
|
|
140
154
|
draftOk: 'All correct',
|
|
141
155
|
draftChange: 'I want to change something',
|
|
142
|
-
windowNotice: "
|
|
156
|
+
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.",
|
|
157
|
+
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.",
|
|
143
158
|
windowLink: (link) => `If the window did not open, use this link: ${link}`,
|
|
144
159
|
existingTitle: 'I see you already have an account. Shall I update your profile with this?',
|
|
145
160
|
existingRate: (from, to) => `Hourly rate: ${from} → ${to}`,
|
|
146
161
|
existingAnnual: (from, to) => `Annual rate: ${from} → ${to}`,
|
|
147
162
|
existingHours: (from, to) => `Availability: ${from} → ${to}`,
|
|
148
163
|
existingEmpty: 'not set',
|
|
149
|
-
|
|
164
|
+
existingUpdateQuestion: 'Shall I update your profile with this?',
|
|
165
|
+
existingStays: (label, kept, requested) => (kept ? `${label}: stays at ${kept}${kept === requested ? '' : `, not ${requested}`}` : `${label}: no change`),
|
|
166
|
+
existingStaysNote: 'I do not change from here what you had already filled in: if you want other values, change them on the web.',
|
|
167
|
+
existingLists: (sources) => `I add what is new from ${listOf('en', named(sources, COPY.en.importSources))} (experience, projects, education) and fill in what is empty, without deleting anything.`,
|
|
150
168
|
existingYes: 'Yes, update it',
|
|
151
169
|
existingNo: 'No, leave it as it is',
|
|
152
170
|
existingKept: 'Sure, I am leaving your profile as it is.',
|
|
153
171
|
keptLabels: { workSituation: 'Work situation', availability: 'Availability', languages: 'Languages', phone: 'Phone' },
|
|
154
172
|
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.`,
|
|
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.',
|
|
173
|
+
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.',
|
|
156
174
|
aiUsageYes: 'Yes, analyse it',
|
|
157
175
|
aiUsageSkip: 'Skip this step',
|
|
158
176
|
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'.",
|
|
@@ -189,10 +207,10 @@ const COPY = {
|
|
|
189
207
|
},
|
|
190
208
|
it: {
|
|
191
209
|
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.`,
|
|
192
|
-
welcomeQuestion: 'Iniziamo?
|
|
193
|
-
cvSearch: '
|
|
194
|
-
cvAttach: 'Te lo
|
|
195
|
-
cvAttachAsk: 'Perfetto, allegalo qui.',
|
|
210
|
+
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.',
|
|
211
|
+
cvSearch: 'Cercalo nel mio computer',
|
|
212
|
+
cvAttach: 'Te lo mando',
|
|
213
|
+
cvAttachAsk: 'Perfetto, allegalo qui o dimmi dove si trova nel tuo computer.',
|
|
196
214
|
cvNotFound: 'Non ho trovato il tuo CV su questo computer. Me lo alleghi qui?',
|
|
197
215
|
linkedinAsk: 'Mi mandi il tuo profilo LinkedIn?',
|
|
198
216
|
linkedinOpening: "Ti apro il tuo profilo LinkedIn nel browser così puoi copiarne l'indirizzo.",
|
|
@@ -209,20 +227,24 @@ const COPY = {
|
|
|
209
227
|
draftQuestion: 'Va bene o cambio qualcosa?',
|
|
210
228
|
draftOk: 'Tutto corretto',
|
|
211
229
|
draftChange: 'Voglio cambiare qualcosa',
|
|
212
|
-
windowNotice: "Quando confermi,
|
|
230
|
+
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.",
|
|
231
|
+
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.",
|
|
213
232
|
windowLink: (link) => `Se la finestra non si è aperta, entra da qui: ${link}`,
|
|
214
233
|
existingTitle: 'Ho visto che hai già un account. Aggiorno il tuo profilo con questi dati?',
|
|
215
234
|
existingRate: (from, to) => `Tariffa oraria: ${from} → ${to}`,
|
|
216
235
|
existingAnnual: (from, to) => `Tariffa annuale: ${from} → ${to}`,
|
|
217
236
|
existingHours: (from, to) => `Disponibilità: ${from} → ${to}`,
|
|
218
237
|
existingEmpty: 'non indicata',
|
|
219
|
-
|
|
238
|
+
existingUpdateQuestion: 'Aggiorno il tuo profilo con questi dati?',
|
|
239
|
+
existingStays: (label, kept, requested) => (kept ? `${label}: resta ${kept}${kept === requested ? '' : `, non ${requested}`}` : `${label}: nessuna modifica`),
|
|
240
|
+
existingStaysNote: 'Quello che avevi già compilato non lo cambio da qui: se vuoi altri valori, cambiali sul sito.',
|
|
241
|
+
existingLists: (sources) => `Aggiungo le novità ${listOf('it', named(sources, { cv: 'del tuo CV', linkedin: 'del tuo LinkedIn' }))} (esperienze, progetti, formazione) e compilo quello che hai vuoto, senza cancellare nulla.`,
|
|
220
242
|
existingYes: 'Sì, aggiornalo',
|
|
221
243
|
existingNo: 'No, lascialo così',
|
|
222
244
|
existingKept: 'Perfetto, lascio il tuo profilo così com’è.',
|
|
223
245
|
keptLabels: { workSituation: 'Situazione lavorativa', availability: 'Disponibilità', languages: 'Lingue', phone: 'Telefono' },
|
|
224
246
|
existingUnchanged: (items) => `Questi dati del tuo profilo non li ho cambiati, restano com'erano: ${items.join(' · ')}. Se vuoi altri valori, cambiali sul sito.`,
|
|
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.",
|
|
247
|
+
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.",
|
|
226
248
|
aiUsageYes: 'Sì, analizzalo',
|
|
227
249
|
aiUsageSkip: 'Salta questo passaggio',
|
|
228
250
|
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'.",
|
|
@@ -259,10 +281,10 @@ const COPY = {
|
|
|
259
281
|
},
|
|
260
282
|
pt: {
|
|
261
283
|
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.`,
|
|
262
|
-
welcomeQuestion: 'Começamos?
|
|
263
|
-
cvSearch: '
|
|
264
|
-
cvAttach: '
|
|
265
|
-
cvAttachAsk: 'Perfeito, anexe-o aqui.',
|
|
284
|
+
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.',
|
|
285
|
+
cvSearch: 'Procure-o no meu computador',
|
|
286
|
+
cvAttach: 'Envio-o eu',
|
|
287
|
+
cvAttachAsk: 'Perfeito, anexe-o aqui ou diga-me onde está no seu computador.',
|
|
266
288
|
cvNotFound: 'Não encontrei o seu CV neste computador. Pode anexá-lo aqui?',
|
|
267
289
|
linkedinAsk: 'Pode enviar-me o seu perfil do LinkedIn?',
|
|
268
290
|
linkedinOpening: 'Vou abrir o seu perfil do LinkedIn no navegador para que copie o endereço.',
|
|
@@ -279,20 +301,24 @@ const COPY = {
|
|
|
279
301
|
draftQuestion: 'Está bem ou mudo alguma coisa?',
|
|
280
302
|
draftOk: 'Está tudo certo',
|
|
281
303
|
draftChange: 'Quero mudar algo',
|
|
282
|
-
windowNotice: 'Quando confirmar,
|
|
304
|
+
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.',
|
|
305
|
+
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.',
|
|
283
306
|
windowLink: (link) => `Se a janela não abriu, entre por aqui: ${link}`,
|
|
284
307
|
existingTitle: 'Vi que já tem conta. Atualizo o seu perfil com isto?',
|
|
285
308
|
existingRate: (from, to) => `Tarifa por hora: ${from} → ${to}`,
|
|
286
309
|
existingAnnual: (from, to) => `Tarifa anual: ${from} → ${to}`,
|
|
287
310
|
existingHours: (from, to) => `Disponibilidade: ${from} → ${to}`,
|
|
288
311
|
existingEmpty: 'sem indicar',
|
|
289
|
-
|
|
312
|
+
existingUpdateQuestion: 'Atualizo o seu perfil com isto?',
|
|
313
|
+
existingStays: (label, kept, requested) => (kept ? `${label}: fica em ${kept}${kept === requested ? '' : `, não ${requested}`}` : `${label}: sem alterações`),
|
|
314
|
+
existingStaysNote: 'O que já tinha preenchido não o altero daqui: se quiser outros valores, altere-os na web.',
|
|
315
|
+
existingLists: (sources) => `Acrescento o que há de novo ${listOf('pt', named(sources, { cv: 'no seu CV', linkedin: 'no seu LinkedIn' }))} (experiências, projetos, formação) e preencho o que tiver vazio, sem apagar nada.`,
|
|
290
316
|
existingYes: 'Sim, atualize',
|
|
291
317
|
existingNo: 'Não, deixe como está',
|
|
292
318
|
existingKept: 'Perfeito, deixo o seu perfil como está.',
|
|
293
319
|
keptLabels: { workSituation: 'Situação profissional', availability: 'Disponibilidade', languages: 'Idiomas', phone: 'Telefone' },
|
|
294
320
|
existingUnchanged: (items) => `Estes dados do seu perfil não os alterei, ficam como estavam: ${items.join(' · ')}. Se quiser outros valores, altere-os na web.`,
|
|
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.',
|
|
321
|
+
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.',
|
|
296
322
|
aiUsageYes: 'Sim, analise',
|
|
297
323
|
aiUsageSkip: 'Saltar este passo',
|
|
298
324
|
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'.",
|
package/src/terminal-agents.js
CHANGED
|
@@ -114,6 +114,14 @@ function printActivity(report, t, p) {
|
|
|
114
114
|
` ${c.success}●${c.reset} ${a.commits(g.authoredCommitCount, g.commitCount)} ${c.muted}·${c.reset} ${a.lines(g.linesAdded, g.linesDeleted)} ${c.muted}·${c.reset} ${a.velocity(g.velocity)}`,
|
|
115
115
|
);
|
|
116
116
|
}
|
|
117
|
+
// Transparency warning: the talent's own commits are going unattributed because
|
|
118
|
+
// the repo's git identity is not their Shakers email (display-only, never scored).
|
|
119
|
+
const mm = report && report.authorshipMismatch;
|
|
120
|
+
if (mm && Array.isArray(mm.emails) && mm.emails.length && mm.accountEmail) {
|
|
121
|
+
for (const line of wrap(a.authorshipMismatch(mm.emails, mm.accountEmail))) {
|
|
122
|
+
p(` ${c.warning}${line}${c.reset}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
117
125
|
if (w) {
|
|
118
126
|
p(
|
|
119
127
|
` ${c.success}●${c.reset} ${a.workStreams(w.streamCount, w.multiDayStreams, w.maxStreamSpanDays)}`,
|
package/src/usage-run-steps.js
CHANGED
|
@@ -30,6 +30,7 @@ const {
|
|
|
30
30
|
collectScopedWorkStreams,
|
|
31
31
|
collectScopedDetectionEvidence,
|
|
32
32
|
} = require('./scope-signals');
|
|
33
|
+
const { detectAuthorshipMismatch } = require('./authorship-mismatch');
|
|
33
34
|
const { MODEL_CALL, REASON, skipped, attachModelCall } = require('./model-call-record');
|
|
34
35
|
const {
|
|
35
36
|
getSynthesisEndpoint,
|
|
@@ -164,6 +165,8 @@ async function resolveAiConsent({ opts, catalog, gate, injectedAsk }) {
|
|
|
164
165
|
function runAggregation({ report, scope, consentEmail, agentRoots, root, catalog }) {
|
|
165
166
|
withStaticStatus(catalog.cli.aggregatingLabel, () => {
|
|
166
167
|
report.gitActivity = collectScopedGitActivity(scope.toplevels, consentEmail);
|
|
168
|
+
// Display-only transparency warning (never feeds scoring, see authorship-mismatch.js).
|
|
169
|
+
report.authorshipMismatch = detectAuthorshipMismatch(scope.toplevels, consentEmail, report.gitActivity);
|
|
167
170
|
const sessionSignals = collectSessionSignals(process.env, scope.selectedCwds);
|
|
168
171
|
report.sessions = sessionSignals.sessions;
|
|
169
172
|
report.workStreams = collectScopedWorkStreams(scope.toplevels);
|