shakers-cli-beta 0.14.1 → 0.14.2
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shakers-cli-beta",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.2",
|
|
4
4
|
"description": "Shakers AI usage & skills evaluator: a local-first CLI (`shakers` binary) plus a local MCP server. Commands: ai-usage (scan + classify your AI-tool setup, T0-T7), certify (certify Skills from your code), share (branded LinkedIn card).",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"bin": {
|
|
@@ -58,6 +58,7 @@ function makeFindProjectsTools(deps = {}) {
|
|
|
58
58
|
description: 'Shows the open Shakers projects that match you best.',
|
|
59
59
|
guidance: "List the Positions/Projects available to the talent right now (Shakers Find Positions), paginated and ordered by match desc. Each item carries its parent Project id/name, title, company, budget, work mode and match. Supports server-side pagination (page/limit) and filters (tab all|saved, attendance, country ISO2). `meta` carries {page,pageSize,total,totalPages}. Requires an active session. The positionId/projectId in each item are INTERNAL handles for calling other tools (e.g. show_project, save_project) — NEVER show or mention them to the talent; refer to a project by its title/company only.",
|
|
60
60
|
inputSchema: FIND_PROJECTS_SCHEMA,
|
|
61
|
+
annotations: { readOnlyHint: true },
|
|
61
62
|
handler: findProjects,
|
|
62
63
|
},
|
|
63
64
|
];
|
package/src/mcp-instructions.js
CHANGED
|
@@ -15,11 +15,11 @@ function buildServerInstructions({ elicitation = false } = {}) {
|
|
|
15
15
|
'2. LinkedIn: take it from the CV. If it has none, signup_start with linkedinMissing:true returns the question to print; never ask it in your own words. Only if they say they do not know where to find it, say the linkedinOpening text and call open_linkedin_profile.',
|
|
16
16
|
'3. signup_email with the best email you know (CV first), or none.',
|
|
17
17
|
'4. signup_draft with your draft of their profile (name, role, city, years, stack, languages, work mode, hours, hourly and annual rate in euros, both always: the talent\'s figures with ratesFromTalent:true, else your estimate; sources: cv and/or linkedin, only those you have). It shows the draft, the data notice and how the account window works, and asks them to confirm; if they want changes, ask what and call it again.',
|
|
18
|
-
'5. Once confirmed, signup_create_account with LinkedIn, cvPath, names and userQuery = everything you know about them; the window opens by itself, so say nothing about it. Then signup_status (waitSeconds
|
|
18
|
+
'5. Once confirmed, signup_create_account with LinkedIn, cvPath, names and userQuery = everything you know about them; the window opens by itself, so say nothing about it. Then signup_status (waitSeconds 45) until the account exists, and follow its message (a failed LinkedIn import: its fixed line comes with the next question; retry once, then ask for their website or GitHub).',
|
|
19
19
|
'6. New account: save_profile_details with the confirmed draft, without asking. Existing account (the talent signed in instead): update_existing_profile, which shows what would fill their empty fields and asks; nothing they already set is overwritten.',
|
|
20
20
|
'7. AI usage, a step of its own: ai_usage without consent asks the question with the two disclaimers; nothing is scanned before their yes. On yes it runs in the background: never wait for it. If they skip it, its fixed line comes with the next question.',
|
|
21
21
|
'8. Onboarding interview: skip it if signup_status says it is completed; otherwise onboarding_interview_start without disclaimerAcknowledged (here now or later on the web, default here), then how they want to answer (drafted, own, full), asked with the interview disclaimers. In drafted mode, show each question verbatim with your proposed answer and send it only after the talent approves or edits it; only if they explicitly ask you to answer everything yourself ("hazla entera", "do it all"), complete the remaining questions without per-answer approval and tell them at the end. Full mode is that from the first question. Own mode sends only their words. Never call onboarding_finish in this flow.',
|
|
22
|
-
'9. Main role, also when they skip the interview: list_my_roles (waitSeconds
|
|
22
|
+
'9. Main role, also when they skip the interview: list_my_roles (waitSeconds 45, again while pending), then, only when its next is set_main_role, set_main_role without clusterId and with evidence = one concrete fact per recommended role from their CV, the interview or the AI-usage analysis (with nothing recommended: answer = the role their CV names and skills = their stack). Never set a main role the talent did not pick.',
|
|
23
23
|
'10. Finish with open_web: it opens Shakers with the talent signed in.',
|
|
24
24
|
].join('\n');
|
|
25
25
|
}
|
package/src/mcp-profile-tools.js
CHANGED
|
@@ -37,6 +37,7 @@ function makeProfileTools(deps = {}) {
|
|
|
37
37
|
description: 'Shows your main role, your rate and your "My work with AI" summary on Shakers.',
|
|
38
38
|
guidance: "Get the talent's summary: main role, per-project rate, and the 'My work with AI' read model (setup/usage tier+level, 3x3 cell, vision, how-I-work). Requires an active session. Present it in human terms; any internal ids/codes in the result are handles for tools only — never show or mention them to the talent.",
|
|
39
39
|
inputSchema: GET_PROFILE_SCHEMA,
|
|
40
|
+
annotations: { readOnlyHint: true },
|
|
40
41
|
handler: getProfile,
|
|
41
42
|
},
|
|
42
43
|
];
|
package/src/mcp-role-tools.js
CHANGED
|
@@ -11,7 +11,9 @@ const EMPTY_SCHEMA = { type: 'object', properties: {} };
|
|
|
11
11
|
// Same bounds as the web's growth-direction step: certs finalizes an interview within ~90 s, polled every 2 s.
|
|
12
12
|
const FINALIZATION_WAIT_LIMIT_MS = 90000;
|
|
13
13
|
const POLL_MS = 2000;
|
|
14
|
-
|
|
14
|
+
// Cap on the waitSeconds the model may pass; kept under Claude Desktop's ~60 s tool-call cap, higher than
|
|
15
|
+
// the old 25 so fewer polls (and fewer approval prompts) cover the ~90 s finalization.
|
|
16
|
+
const MAX_WAIT_SECONDS = 45;
|
|
15
17
|
const PENDING_FINALIZATION = new Set(['COLLECTING', 'PROCESSING']);
|
|
16
18
|
// Alma's signal from the interview first, then the CV import, as the web stacks them.
|
|
17
19
|
const SOURCE_ORDER = { ONBOARDING_INTERVIEW: 0, IMPORT: 1 };
|
package/src/mcp-server.js
CHANGED
|
@@ -107,7 +107,7 @@ function createMcpServer({ tools = [], prompts = [], serverInfo = {}, protocolVe
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
// `description` and `title` are what the talent reads in the client; `guidance` is how the model must use the tool, so it rides in the schema.
|
|
110
|
+
// `description` and `title` are what the talent reads in the client; `guidance` is how the model must use the tool, so it rides in the schema. `annotations` (e.g. readOnlyHint) is a standard MCP hint that passes straight through for clients that act on it.
|
|
111
111
|
function shapeTool(t) {
|
|
112
112
|
const schema = t.inputSchema || { type: 'object' };
|
|
113
113
|
return {
|
|
@@ -115,6 +115,7 @@ function createMcpServer({ tools = [], prompts = [], serverInfo = {}, protocolVe
|
|
|
115
115
|
...(t.title ? { title: t.title } : {}),
|
|
116
116
|
description: t.description || '',
|
|
117
117
|
inputSchema: t.guidance ? { ...schema, description: [t.guidance, schema.description].filter(Boolean).join(' ') } : schema,
|
|
118
|
+
...(t.annotations ? { annotations: t.annotations } : {}),
|
|
118
119
|
};
|
|
119
120
|
}
|
|
120
121
|
|
package/src/mcp-signup-tools.js
CHANGED
|
@@ -18,7 +18,11 @@ const { NOOP_FUNNEL, weeklyHours, rateTypes, workSituationValue } = require('./o
|
|
|
18
18
|
const SIGNUP_WINDOW_TIMEOUT_MS = 15 * 60 * 1000;
|
|
19
19
|
// The browser opens this long after the tool answers, so the AI's reply is on screen first.
|
|
20
20
|
const BROWSER_OPEN_DELAY_MS = 4000;
|
|
21
|
-
|
|
21
|
+
// Each signup_status call holds up to this long waiting for a change, so a higher value means fewer calls
|
|
22
|
+
// (and fewer per-call approval prompts in the client) cover the same wait. Kept under the ~60 s hard cap
|
|
23
|
+
// Claude Desktop puts on a tool call (progress notifications do not extend it there), with margin for the
|
|
24
|
+
// round-trip.
|
|
25
|
+
const MAX_WAIT_SECONDS = 45;
|
|
22
26
|
const IMPORT_POLL_MS = 3000;
|
|
23
27
|
// Languages the profile importer writes in.
|
|
24
28
|
const IMPORT_LANGUAGES = new Set(['es', 'en', 'it', 'pt', 'fr']);
|
|
@@ -667,7 +671,7 @@ function makeSignupTools(deps = {}) {
|
|
|
667
671
|
...(cvNote ? { cvNote } : {}),
|
|
668
672
|
next: 'signup_status',
|
|
669
673
|
message: (reused ? 'The sign-up window was already open; it now uses the new data. ' : 'The window opens in the talent\'s browser in a few seconds; they read how it works in the draft they confirmed. ')
|
|
670
|
-
+ 'Say nothing about the window. Only if they say it did not open, call signup_status with windowLink:true. Now call signup_status (waitSeconds
|
|
674
|
+
+ 'Say nothing about the window. Only if they say it did not open, call signup_status with windowLink:true. Now call signup_status (waitSeconds 45) until the account exists.',
|
|
671
675
|
};
|
|
672
676
|
}
|
|
673
677
|
|
|
@@ -703,7 +707,7 @@ function makeSignupTools(deps = {}) {
|
|
|
703
707
|
}
|
|
704
708
|
|
|
705
709
|
function nextSteps(account) {
|
|
706
|
-
const interview = 'Then the onboarding interview: if onboardingInterview.completed is true, skip it without mentioning it; otherwise call onboarding_interview_start without disclaimerAcknowledged (it asks here now or later on the web). Then, also if they skip it, the main role: list_my_roles (waitSeconds
|
|
710
|
+
const interview = 'Then the onboarding interview: if onboardingInterview.completed is true, skip it without mentioning it; otherwise call onboarding_interview_start without disclaimerAcknowledged (it asks here now or later on the web). Then, also if they skip it, the main role: list_my_roles (waitSeconds 45), then set_main_role without clusterId only when its next says so. Finally open_web.';
|
|
707
711
|
const aiUsage = 'Then ai_usage without consent: it returns the two AI-usage disclaimers and the question (show them right before asking; no scan happens before a yes).';
|
|
708
712
|
if (account.state === 'existing') {
|
|
709
713
|
return `This talent already had an account and signed in. Call update_existing_profile: it shows what would change and asks whether to update, and its text tells the talent about the account, so say nothing about it yourself. ${aiUsage} ${interview}`;
|
|
@@ -1121,8 +1125,9 @@ function makeSignupTools(deps = {}) {
|
|
|
1121
1125
|
name: 'signup_status',
|
|
1122
1126
|
title: 'Sign-up progress',
|
|
1123
1127
|
description: 'Shows whether your account is ready and how the import of your profile is going.',
|
|
1124
|
-
guidance: "State of the sign-up: account (waiting | created | existing | failed) and, once it exists, the profile import per source (linkedin, cv: running | done | failed). Pass waitSeconds (up to
|
|
1128
|
+
guidance: "State of the sign-up: account (waiting | created | existing | failed) and, once it exists, the profile import per source (linkedin, cv: running | done | failed). Pass waitSeconds (up to 45) to wait for a change; a high value means fewer calls cover the wait. Follow `message`: it gives the remaining steps. Never show internal codes to the talent.",
|
|
1125
1129
|
inputSchema: STATUS_SCHEMA,
|
|
1130
|
+
annotations: { readOnlyHint: true },
|
|
1126
1131
|
handler: signupStatus,
|
|
1127
1132
|
},
|
|
1128
1133
|
{
|