shakers-cli 0.14.2 → 0.16.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/README.md +51 -1203
- package/bin/ai-usage.js +11 -56
- package/bin/login.js +10 -10
- package/bin/mcp.js +13 -9
- package/bin/mcpb.js +1 -1
- package/bin/onboarding.js +3 -2
- package/bin/register.js +17 -4
- package/bin/report-html.js +2 -2
- package/bin/shakers-cli.js +352 -0
- package/bin/shakers.js +20 -344
- package/bin/share.js +1 -1
- package/bin/superadmin.js +5 -5
- package/bin/version.js +3 -0
- package/package.json +12 -3
- package/src/agent-category.js +1 -1
- package/src/agent-evaluation.js +3 -3
- package/src/agent-org-chart.js +5 -5
- package/src/agent-synthesis.js +3 -3
- package/src/agent-usage.js +1 -1
- package/src/agents-client.js +4 -4
- package/src/ai.js +1 -1
- package/src/ansi.js +1 -1
- package/src/auth-client.js +18 -4
- package/src/auth-method-picker.js +1 -1
- package/src/auth-methods.js +1 -1
- package/src/auth-session-store.js +11 -8
- package/src/authorship-mismatch.js +1 -1
- package/src/authorship.js +2 -2
- package/src/automations-detector.js +1 -1
- package/src/backend-request.js +20 -3
- package/src/brand-ansi.js +1 -1
- package/src/browser-tools-detector.js +1 -1
- package/src/certify-disclaimer.js +1 -1
- package/src/certify-remediation-prompt.js +1 -1
- package/src/cli-args.js +2 -2
- package/src/command-audit.js +8 -5
- package/src/config-dir.js +1 -1
- package/src/config.js +112 -48
- package/src/consent-flow.js +4 -4
- package/src/consent-skip.js +1 -1
- package/src/detection-evidence.js +2 -2
- package/src/detection-noise.js +1 -1
- package/src/detectors.js +1 -1
- package/src/device-login.js +12 -1
- package/src/email-verification.js +3 -3
- package/src/env-paths.js +1 -1
- package/src/find-projects-client.js +12 -0
- package/src/find-projects-flow.js +1 -13
- package/src/git-activity.js +1 -1
- package/src/graph-certs.js +3 -3
- package/src/graph-scan.js +9 -9
- package/src/i18n.js +124 -124
- package/src/interactive-select.js +1 -1
- package/src/interview-client.js +6 -1
- package/src/interview-transcript.js +1 -1
- package/src/legal-notice.js +1 -1
- package/src/maturity.js +6 -6
- package/src/mcp-ai-usage-tool.js +1 -1
- package/src/mcp-auth-form.js +1 -1
- package/src/mcp-detector.js +3 -3
- package/src/mcp-device-login-tools.js +2 -1
- package/src/mcp-find-projects-tools.js +1 -2
- package/src/mcp-onboarding-tools.js +4 -4
- package/src/mcp-prompts.js +2 -2
- package/src/mcp-register-tools.js +1 -0
- package/src/mcp-server.js +5 -1
- package/src/mcp-signup-client.js +2 -2
- package/src/mcp-signup-tools.js +26 -2
- package/src/memory-structure-detector.js +1 -1
- package/src/model-call-record.js +2 -2
- package/src/onboarding-flow.js +16 -16
- package/src/onboarding-funnel.js +110 -17
- package/src/open-file.js +1 -1
- package/src/osc-link.js +1 -1
- package/src/portfolio-client.js +11 -1
- package/src/portfolio-git-info.js +1 -1
- package/src/profile-flow.js +1 -1
- package/src/profile-switch.js +5 -12
- package/src/render-certification.js +10 -10
- package/src/render-html.js +6 -6
- package/src/render-sheet.js +10 -10
- package/src/render-terminal.js +6 -6
- package/src/repo-scope.js +132 -0
- package/src/report-gating.js +1 -1
- package/src/report-store.js +6 -6
- package/src/roadmap-content.js +5 -5
- package/src/roadmap-personalization.js +3 -3
- package/src/roadmap-prompt.js +2 -2
- package/src/scan-exclusions.js +1 -1
- package/src/scanner.js +14 -14
- package/src/secret-prompt.js +1 -1
- package/src/session-gate.js +1 -1
- package/src/session-refresh.js +33 -3
- package/src/session-scan.js +1 -1
- package/src/shakers-wordmark.js +1 -1
- package/src/share-card.js +2 -2
- package/src/share-certification.js +4 -4
- package/src/share-consent-state.js +7 -7
- package/src/share-payload.js +7 -7
- package/src/share.js +7 -7
- package/src/signup-client.js +1 -1
- package/src/signup-copy.js +1 -1
- package/src/skills-client.js +1 -1
- package/src/start-add-agents.js +2 -2
- package/src/start-add-portfolio.js +3 -13
- package/src/start-add-skills.js +2 -2
- package/src/stdin-ask.js +3 -3
- package/src/strip-internal-comments.js +1 -1
- package/src/tech-detector.js +2 -2
- package/src/templates/report-sheet.html +11 -13
- package/src/terminal-agents.js +8 -8
- package/src/terminal-format.js +3 -3
- package/src/terminal-sections.js +11 -11
- package/src/tier-engine.js +3 -3
- package/src/tier-projection.js +2 -2
- package/src/transcript-turns.js +1 -1
- package/src/typewriter.js +1 -1
- package/src/usage-model-calls.js +6 -6
- package/src/usage-oneshot-commands.js +5 -5
- package/src/usage-repo-scope.js +11 -129
- package/src/usage-run-steps.js +32 -184
- package/src/usage-scan.js +235 -0
- package/src/work-streams.js +1 -1
package/src/share.js
CHANGED
|
@@ -33,7 +33,7 @@ const {
|
|
|
33
33
|
|
|
34
34
|
/* ---------- minimal HTTP utility (no dependencies) ---------- */
|
|
35
35
|
|
|
36
|
-
// POSTs `body` to the ONE backend (
|
|
36
|
+
// POSTs `body` to the ONE backend (the PRIMARY -> FALLBACK chain is retired), via src/backend-request.js.
|
|
37
37
|
async function requestJsonBackend(endpoint, body, { timeoutMs = 15000, idempotencyKey = null } = {}) {
|
|
38
38
|
const { status, raw, backend } = await requestBackend({ endpoint, body, timeoutMs, idempotencyKey });
|
|
39
39
|
let json = null;
|
|
@@ -53,7 +53,7 @@ async function autoShare(report, maturity, { root, bypassThrottle = false } = {}
|
|
|
53
53
|
if (!state.email) {
|
|
54
54
|
return { ok: false, skipped: true, reason: 'no-email' };
|
|
55
55
|
}
|
|
56
|
-
//
|
|
56
|
+
// Never SEND under an email whose ownership wasn't proven — every
|
|
57
57
|
// profile, `external` included (legal requirement), must have verified.
|
|
58
58
|
if (state.emailVerified === false) {
|
|
59
59
|
return { ok: false, skipped: true, reason: 'email-unverified' };
|
|
@@ -92,14 +92,14 @@ async function autoShare(report, maturity, { root, bypassThrottle = false } = {}
|
|
|
92
92
|
return { ok: false, skipped: false, reason: 'rate-limited' };
|
|
93
93
|
}
|
|
94
94
|
if (res.status === 503) {
|
|
95
|
-
// Generic "service unavailable" from the server (
|
|
96
|
-
// server-side kill switch
|
|
95
|
+
// Generic "service unavailable" from the server (the retired
|
|
96
|
+
// server-side kill switch used to be the main 503 cause).
|
|
97
97
|
return { ok: false, skipped: false, reason: 'service-unavailable' };
|
|
98
98
|
}
|
|
99
99
|
return { ok: false, skipped: false, reason: `http-${res.status}` };
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
/* ---------- certification persistence
|
|
102
|
+
/* ---------- certification persistence ---------- */
|
|
103
103
|
|
|
104
104
|
// Persists the analyzed certification result if (and only if) consent is granted.
|
|
105
105
|
async function shareCertification(items, { repository = null, commitRange = null, toolVersion = null, superadminToken = null, root = null } = {}) {
|
|
@@ -112,7 +112,7 @@ async function shareCertification(items, { repository = null, commitRange = null
|
|
|
112
112
|
if (!state.email) {
|
|
113
113
|
return { ok: false, skipped: true, reason: 'no-email' };
|
|
114
114
|
}
|
|
115
|
-
//
|
|
115
|
+
// Don't persist under an unverified email.
|
|
116
116
|
if (state.emailVerified === false) {
|
|
117
117
|
return { ok: false, skipped: true, reason: 'email-unverified' };
|
|
118
118
|
}
|
|
@@ -132,7 +132,7 @@ async function shareCertification(items, { repository = null, commitRange = null
|
|
|
132
132
|
|
|
133
133
|
let res;
|
|
134
134
|
try {
|
|
135
|
-
//
|
|
135
|
+
// Forward the superadmin session token so the backend stamps test_origin=true (server-authoritative, non-prod only).
|
|
136
136
|
const body = { email: state.email, payload };
|
|
137
137
|
if (superadminToken) body.superadminToken = superadminToken;
|
|
138
138
|
// Idempotency-Key (three-way security review, cross-repo): ONE key per
|
package/src/signup-client.js
CHANGED
|
@@ -4,7 +4,7 @@ const { postJsonWithTimeout, extractSetCookie } = require('./backend-request');
|
|
|
4
4
|
const { SESSION_COOKIE_NAME } = require('./auth-client');
|
|
5
5
|
const { toUpperLang } = require('./lang-codes');
|
|
6
6
|
|
|
7
|
-
// Register a talent via the Hub's better-auth email engine, in two steps (
|
|
7
|
+
// Register a talent via the Hub's better-auth email engine, in two steps (WEB-login parity): 1.
|
|
8
8
|
|
|
9
9
|
const DEFAULT_TIMEOUT_MS = 30000;
|
|
10
10
|
|
package/src/signup-copy.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
// Talent-facing copy of the MCP sign-up
|
|
3
|
+
// Talent-facing copy of the MCP sign-up: fixed text the AI shows word for word, in the talent's language.
|
|
4
4
|
|
|
5
5
|
const SIGNUP_LANGUAGES = ['es', 'en', 'it', 'pt'];
|
|
6
6
|
|
package/src/skills-client.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { postJsonWithTimeout } = require('./backend-request');
|
|
4
4
|
|
|
5
|
-
// Talent-skills client (
|
|
5
|
+
// Talent-skills client (`start`'s "Añadir skills" route, src/start-add-skills.js).
|
|
6
6
|
|
|
7
7
|
const DEFAULT_TIMEOUT_MS = 20000;
|
|
8
8
|
|
package/src/start-add-agents.js
CHANGED
|
@@ -24,7 +24,7 @@ const { reasonCopy, pickPortfolios } = require('./start-add-skills');
|
|
|
24
24
|
const { askWithDefault } = require('./start-add-portfolio');
|
|
25
25
|
const { styleQuestion, styleSuccess } = require('./ansi');
|
|
26
26
|
|
|
27
|
-
// `start`'s "Add agent to profile" route
|
|
27
|
+
// `start`'s "Add agent to profile" route, mirror of src/start-add-portfolio.js: the IA pre-drafts, the Talent edits.
|
|
28
28
|
|
|
29
29
|
const MAX_DEFINITION_CHARS = 4000;
|
|
30
30
|
|
|
@@ -68,7 +68,7 @@ async function chooseAgent({ ask, stdinIsTTY, agents, c, input, output }) {
|
|
|
68
68
|
return idx === null ? null : agents[idx];
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
// Step 4 — the two AI-drafted fields, consent-gated
|
|
71
|
+
// Step 4 — the two AI-drafted fields, consent-gated before any egress.
|
|
72
72
|
async function resolveAgentFields({ ask, catalog, c, stdinIsTTY, agent, accessToken, preAccepted = false }) {
|
|
73
73
|
const freeText = async () => ({
|
|
74
74
|
whatItDoes: await askFreeText(ask, c.agentWhatItDoesFreeTextPrompt),
|
|
@@ -8,7 +8,7 @@ const {
|
|
|
8
8
|
getPortfoliosListEndpoint, getPortfoliosDeclareEndpoint, getPortfolioDraftDescriptionEndpoint,
|
|
9
9
|
getSkillsResolveMatchedEndpoint, getSkillsResolveAddableEndpoint, getSkillsDeclareEndpoint,
|
|
10
10
|
} = require('./config');
|
|
11
|
-
const { requestListPortfolios, requestDeclarePortfolio, requestDraftDescription } = require('./portfolio-client');
|
|
11
|
+
const { requestListPortfolios, requestDeclarePortfolio, requestDraftDescription, normalizeDomain } = require('./portfolio-client');
|
|
12
12
|
const { requestResolveAddableSkills: requestResolveMatchedSkills, requestResolveAddableSkills } = require('./skills-client');
|
|
13
13
|
const { getRemoteUrl, getCommitDateRange } = require('./portfolio-git-info');
|
|
14
14
|
const { confirmDisclaimerAcceptance } = require('./certify-disclaimer');
|
|
@@ -21,7 +21,7 @@ const { palette, styleQuestion, styleSuccess } = require('./ansi');
|
|
|
21
21
|
// Only `dim`/`reset` for the numbered fallback's descriptions — the exact treatment bin/start.js's own menu and bin/certify.js's picker give theirs.
|
|
22
22
|
const ANSI = palette({ reset: '\x1b[0m', dim: '\x1b[2m' });
|
|
23
23
|
|
|
24
|
-
// `start`'s "Add project to portfolio" route
|
|
24
|
+
// `start`'s "Add project to portfolio" route.
|
|
25
25
|
|
|
26
26
|
const MAX_README_CHARS = 4000;
|
|
27
27
|
const MAX_PACKAGE_DESCRIPTION_CHARS = 500;
|
|
@@ -56,15 +56,6 @@ async function askFreeText(ask, promptText) {
|
|
|
56
56
|
return String(await ask(styleQuestion(promptText))).trim();
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
function normalizeDomain(raw) {
|
|
60
|
-
let s = String(raw || '').trim().toLowerCase();
|
|
61
|
-
if (!s) return '';
|
|
62
|
-
s = s.replace(/^https?:\/\//, '');
|
|
63
|
-
s = s.split('/')[0].split('?')[0].split('#')[0];
|
|
64
|
-
s = s.replace(/^www\./, '');
|
|
65
|
-
return s;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
59
|
// Step 2 — Type.
|
|
69
60
|
function typeOptions(c) {
|
|
70
61
|
return [
|
|
@@ -345,7 +336,7 @@ async function runAddPortfolio({
|
|
|
345
336
|
|
|
346
337
|
if (declared.ok) {
|
|
347
338
|
process.stdout.write(`\n ${styleSuccess(c.portfolioDeclaredSuccess(title))}\n\n`);
|
|
348
|
-
// OPT-IN
|
|
339
|
+
// OPT-IN: offer the REST of the detected skills for the Talent's PROFILE, now that the portfolio itself (and whatever skillIds it carried) is safely declared.
|
|
349
340
|
await offerAddableSkillsForProfile({ ask, catalog, c, technologies, session, stdinIsTTY, input, output, runLogin });
|
|
350
341
|
return;
|
|
351
342
|
}
|
|
@@ -383,6 +374,5 @@ module.exports = {
|
|
|
383
374
|
askWithDefault,
|
|
384
375
|
chooseType,
|
|
385
376
|
typeOptions,
|
|
386
|
-
normalizeDomain,
|
|
387
377
|
offerAddableSkillsForProfile,
|
|
388
378
|
};
|
package/src/start-add-skills.js
CHANGED
|
@@ -9,7 +9,7 @@ const { runInteractiveMultiSelect } = require('./interactive-select');
|
|
|
9
9
|
const { loadAuthSession, sessionStatus } = require('./auth-session-store');
|
|
10
10
|
const { styleSuccess } = require('./ansi');
|
|
11
11
|
|
|
12
|
-
// `start`'s "Añadir skills" route
|
|
12
|
+
// `start`'s "Añadir skills" route.
|
|
13
13
|
|
|
14
14
|
// Maps a technical failure reason to the localized `start.errorReason` copy.
|
|
15
15
|
function reasonCopy(reason, c) {
|
|
@@ -255,6 +255,6 @@ module.exports = {
|
|
|
255
255
|
declareAll,
|
|
256
256
|
printProfileBoost,
|
|
257
257
|
offerRelateToPortfolios,
|
|
258
|
-
// Exported for src/start-add-agents.js's own relate step
|
|
258
|
+
// Exported for src/start-add-agents.js's own relate step: the multi-select over the Talent's portfolios, reused verbatim.
|
|
259
259
|
pickPortfolios,
|
|
260
260
|
};
|
package/src/stdin-ask.js
CHANGED
|
@@ -49,14 +49,14 @@ function createLineQueueAsk(writeQuestion) {
|
|
|
49
49
|
|
|
50
50
|
// `onInterrupt` (optional): a Ctrl-C (SIGINT) handler for a TTY.
|
|
51
51
|
function createStdinAsk({ onInterrupt } = {}) {
|
|
52
|
-
// `rl` is re-createable
|
|
52
|
+
// `rl` is re-createable, not a `const`.
|
|
53
53
|
let rl = null;
|
|
54
54
|
let ended = false;
|
|
55
55
|
let suspending = false;
|
|
56
56
|
let currentPrompt = '';
|
|
57
57
|
|
|
58
58
|
const { ask, pushLine, markEnded, drain } = createLineQueueAsk((question) => {
|
|
59
|
-
// Long-answer wrap bug (
|
|
59
|
+
// Long-answer wrap bug (certify agents): in a TTY, `readline` runs in terminal mode and redraws the current line on every keystroke (wrap, cursor).
|
|
60
60
|
const text = ` ${question} `;
|
|
61
61
|
const lastNewline = text.lastIndexOf('\n');
|
|
62
62
|
if (lastNewline >= 0) {
|
|
@@ -98,7 +98,7 @@ function createStdinAsk({ onInterrupt } = {}) {
|
|
|
98
98
|
r.close();
|
|
99
99
|
suspending = false;
|
|
100
100
|
}
|
|
101
|
-
// SECRETS DO NOT ECHO
|
|
101
|
+
// SECRETS DO NOT ECHO.
|
|
102
102
|
ask.secret = async (question) => {
|
|
103
103
|
if (!canPromptSecretly(process.stdin)) return ask(question);
|
|
104
104
|
process.stdout.write(` ${question} `);
|
package/src/tech-detector.js
CHANGED
|
@@ -10,7 +10,7 @@ const {
|
|
|
10
10
|
isExcludedWalkPath,
|
|
11
11
|
} = require('./scan-exclusions');
|
|
12
12
|
|
|
13
|
-
// Monorepo support
|
|
13
|
+
// Monorepo support: manifests are discovered RECURSIVELY under `root`, not just at the top level.
|
|
14
14
|
|
|
15
15
|
// Exact dependency-name -> canonical framework/library name (npm + pip).
|
|
16
16
|
const EXACT_FRAMEWORK_MAP = {
|
|
@@ -202,7 +202,7 @@ function fromPyprojectTomlFile(filePath) {
|
|
|
202
202
|
return names;
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
-
// Manifest filename -> reader
|
|
205
|
+
// Manifest filename -> reader.
|
|
206
206
|
const MANIFEST_READERS = {
|
|
207
207
|
'package.json': fromPackageJsonFile,
|
|
208
208
|
'requirements.txt': fromRequirementsTxtFile,
|
|
@@ -146,7 +146,7 @@ header.top{
|
|
|
146
146
|
.col-head{display:flex; align-items:baseline; gap:10px; margin:2px 0 14px}
|
|
147
147
|
.col-head h2{margin:0; font-size:13px; letter-spacing:.12em; text-transform:uppercase; color:var(--fg-muted); font-weight:650}
|
|
148
148
|
.col-head .dot{width:6px;height:6px;border-radius:50%; background:var(--accent)}
|
|
149
|
-
/* Column note
|
|
149
|
+
/* Column note: one line under the column head, for copy that has to
|
|
150
150
|
be read before the numbers below it. `.sub` only exists inside `.card`. */
|
|
151
151
|
.col-note{margin:-8px 0 14px; font-size:12.5px; line-height:1.5; color:var(--fg-muted)}
|
|
152
152
|
.col-note b{color:var(--fg); font-weight:650}
|
|
@@ -211,16 +211,15 @@ header.top{
|
|
|
211
211
|
.chip .d{width:7px;height:7px;border-radius:50%; background:var(--accent)}
|
|
212
212
|
|
|
213
213
|
/* --- Agent tree --------------------------------------------- *
|
|
214
|
-
* ORPHANED BUT OWNED
|
|
214
|
+
* ORPHANED BUT OWNED.
|
|
215
215
|
*
|
|
216
216
|
* Nothing emits `class="agent"` today: `render-sheet.js` builds no agent tree, so
|
|
217
217
|
* this block styles markup that does not exist. It is NOT dead by accident — it
|
|
218
|
-
* came with the approved mockup and it is exactly the material
|
|
219
|
-
* shareable report has no agents at all) needs, so that design does not
|
|
220
|
-
* re-invent the nesting rail.
|
|
221
|
-
* `git show 7fae2c8^:src/render-html.js` (see `agentNodeHtml`/`agentCardsSection`).
|
|
218
|
+
* came with the approved mockup and it is exactly the material an agent section in
|
|
219
|
+
* the shareable report (which has no agents at all) needs, so that design does not
|
|
220
|
+
* have to re-invent the nesting rail.
|
|
222
221
|
*
|
|
223
|
-
* If
|
|
222
|
+
* If that section is dropped or solved another way, this goes with it. What it must not
|
|
224
223
|
* do is sit here with no owner, which is how it got orphaned in the first place.
|
|
225
224
|
* ------------------------------------------------------------ */
|
|
226
225
|
.tree{margin:0; padding:0; list-style:none}
|
|
@@ -312,7 +311,7 @@ header.top{
|
|
|
312
311
|
.tag-na{background:var(--chip-bg); color:var(--fg-muted)}
|
|
313
312
|
.area .icn{width:16px; text-align:center}
|
|
314
313
|
|
|
315
|
-
/* anchored quotes
|
|
314
|
+
/* anchored quotes: one block per quote, under its own area row, with
|
|
316
315
|
a left rail whose colour differs per SOURCE. The .qt-k label is the real
|
|
317
316
|
distinction between the two — the rail is lost in greyscale, the label is not. */
|
|
318
317
|
.area-block{display:flex; flex-direction:column; gap:3px}
|
|
@@ -401,7 +400,7 @@ __BODY__
|
|
|
401
400
|
"use strict";
|
|
402
401
|
|
|
403
402
|
/* Runs ONE independent init/update step in isolation from its siblings
|
|
404
|
-
|
|
403
|
+
`load`, `resize` and the theme toggle each chain several
|
|
405
404
|
UNRELATED effects (paint the ring, count up the score, slide the tab
|
|
406
405
|
indicator, sync a label…) as a plain sequence of statements. Before this,
|
|
407
406
|
one throwing — as `paintRing()` did pre-066, on ITS OWN unguarded lookup —
|
|
@@ -448,9 +447,8 @@ __BODY__
|
|
|
448
447
|
reached countUp / moveInd / syncExpandLabel — leaving the tab indicator
|
|
449
448
|
unpositioned and the expand-all label unsynced — and the theme toggle, whose
|
|
450
449
|
handler ends in paintRing(), threw on every single click. A broken script
|
|
451
|
-
fails no test; it silently degrades a report someone is reading. Found
|
|
452
|
-
|
|
453
|
-
for the first time. */
|
|
450
|
+
fails no test; it silently degrades a report someone is reading. Found when
|
|
451
|
+
a report with only an agent certification first became reachable. */
|
|
454
452
|
if(!fill) return;
|
|
455
453
|
var circumference = 2 * Math.PI * 54; // r=54
|
|
456
454
|
fill.style.strokeDasharray = circumference;
|
|
@@ -556,7 +554,7 @@ __BODY__
|
|
|
556
554
|
});
|
|
557
555
|
|
|
558
556
|
/* ---- init ---- */
|
|
559
|
-
// Four UNRELATED effects
|
|
557
|
+
// Four UNRELATED effects: each isolated so one failing never
|
|
560
558
|
// takes the other three down with it — see safeStep() above for why this,
|
|
561
559
|
// and not a guard on #ringFill/#scoreNum alone, is the fix at this level.
|
|
562
560
|
window.addEventListener('load', function(){
|
package/src/terminal-agents.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
// The agents + activity blocks of the terminal report, extracted from src/render-terminal.js
|
|
3
|
+
// The agents + activity blocks of the terminal report, extracted from src/render-terminal.js.
|
|
4
4
|
|
|
5
5
|
const { label } = require('./i18n');
|
|
6
6
|
const { buildAgentCardTree } = require('./render-html');
|
|
@@ -11,7 +11,7 @@ const { DEFAULT_REPORT_GATE } = require('./report-gating');
|
|
|
11
11
|
const { c, wrap, summarize } = require('./terminal-format');
|
|
12
12
|
const { detectedToolNames } = require('./detected-tools');
|
|
13
13
|
|
|
14
|
-
// agents
|
|
14
|
+
// agents: As SIMPLE as possible: ONE line per agent.
|
|
15
15
|
|
|
16
16
|
// AI product (from the source, e.g. Claude Code) shown in place of the LLM model.
|
|
17
17
|
function agentProductLabel(card, t) {
|
|
@@ -24,7 +24,7 @@ function agentLine(card, depth, t) {
|
|
|
24
24
|
const indent = ' '.repeat(depth);
|
|
25
25
|
// Root = a filled bullet; every deeper level = that many stacked down-arrows.
|
|
26
26
|
const marker = depth === 0 ? `${c.success}●${c.reset}` : `${c.primary}${'↓'.repeat(depth)}${c.reset}`;
|
|
27
|
-
// card.symbolicName is agent-synthesis (LLM
|
|
27
|
+
// card.symbolicName is agent-synthesis (LLM) content; card.name is
|
|
28
28
|
// the local, deterministic agent id — only the former needs stripping.
|
|
29
29
|
const title = card.symbolicName
|
|
30
30
|
? `${sanitizeRenderText(card.symbolicName)} ${c.muted}(${card.name})${c.reset}`
|
|
@@ -67,7 +67,7 @@ function agentClassLine(card, depth, t) {
|
|
|
67
67
|
const catLabel = (category && cc.categories[category]) || sanitizeRenderText(category);
|
|
68
68
|
const levelLabel = (level && cc.levels[level]) || sanitizeRenderText(level);
|
|
69
69
|
const bits = [];
|
|
70
|
-
// The FLOOR category is dimmed, not accented
|
|
70
|
+
// The FLOOR category is dimmed, not accented.
|
|
71
71
|
const floor = isFloorCategory(category);
|
|
72
72
|
if (catLabel) {
|
|
73
73
|
bits.push(floor ? `${c.dim}${catLabel}${c.reset}` : `${c.primary}${catLabel}${c.reset}`);
|
|
@@ -127,7 +127,7 @@ function printActivity(report, t, p) {
|
|
|
127
127
|
` ${c.success}●${c.reset} ${a.workStreams(w.streamCount, w.multiDayStreams, w.maxStreamSpanDays)}`,
|
|
128
128
|
);
|
|
129
129
|
}
|
|
130
|
-
// Traction
|
|
130
|
+
// Traction: recency-windowed signals from the local scan; agentsOnProfile
|
|
131
131
|
// is hub-only, shown only when the ai-profile preview has already been fetched.
|
|
132
132
|
const tr = tractionParts(report, s, a);
|
|
133
133
|
if (tr.length) p(` ${c.success}●${c.reset} ${a.tractionLabel}: ${tr.join(' · ')}`);
|
|
@@ -157,7 +157,7 @@ function printAgents(report, t, p, gate = DEFAULT_REPORT_GATE) {
|
|
|
157
157
|
p();
|
|
158
158
|
return;
|
|
159
159
|
}
|
|
160
|
-
// THE ROOT THE AGENTS HANG FROM (
|
|
160
|
+
// THE ROOT THE AGENTS HANG FROM (restored, not invented).
|
|
161
161
|
p(` ${c.muted}${label(t.html.orchestratorLabel, 'Orchestrator')}${c.reset}`);
|
|
162
162
|
const visited = new Set();
|
|
163
163
|
const walk = (card, depth) => {
|
|
@@ -187,11 +187,11 @@ function printAgents(report, t, p, gate = DEFAULT_REPORT_GATE) {
|
|
|
187
187
|
const names = [...omittedNames].map(sanitizeRenderText).join(', ');
|
|
188
188
|
for (const line of wrap(t.classification.agentsEvalPartial(names, omittedNames.size))) p(` ${c.dim}${line}${c.reset}`);
|
|
189
189
|
}
|
|
190
|
-
// THE CASE THAT SAID NOTHING AT ALL
|
|
190
|
+
// THE CASE THAT SAID NOTHING AT ALL.
|
|
191
191
|
if (callFailed(report, MODEL_CALL.EVALUATION)) {
|
|
192
192
|
const detected = Array.isArray(report.agents) ? report.agents.length : 0;
|
|
193
193
|
const cls = t.classification;
|
|
194
|
-
// FOUR outcomes, because they ask the talent to do different things
|
|
194
|
+
// FOUR outcomes, because they ask the talent to do different things.
|
|
195
195
|
const record = modelCall(report, MODEL_CALL.EVALUATION);
|
|
196
196
|
const reason = record && record.reason;
|
|
197
197
|
let notice;
|
package/src/terminal-format.js
CHANGED
|
@@ -21,7 +21,7 @@ function bar(score, width = 24) {
|
|
|
21
21
|
return '█'.repeat(filled) + '░'.repeat(width - filled);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
//
|
|
24
|
+
// A light visual break between the reordered sections — a dim rule
|
|
25
25
|
// with air around it, keeping the branded ANSI look and zero deps.
|
|
26
26
|
function sep(p) {
|
|
27
27
|
p();
|
|
@@ -29,7 +29,7 @@ function sep(p) {
|
|
|
29
29
|
p();
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
// WRAPPING
|
|
32
|
+
// WRAPPING.
|
|
33
33
|
const WRAP_COLS = 76;
|
|
34
34
|
|
|
35
35
|
function wrap(text, width = WRAP_COLS) {
|
|
@@ -71,7 +71,7 @@ function labelledBlock(p, label, value, indent = ' ') {
|
|
|
71
71
|
for (const line of wrap(value, WRAP_COLS - indent.length)) p(`${indent}${c.muted}${line}${c.reset}`);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
// Terminal-SUMMARIZE
|
|
74
|
+
// Terminal-SUMMARIZE: the earlier condense over-trimmed — it left the terminal as headings + copyable prompts with the prose stripped out.
|
|
75
75
|
function summarize(text, max = 140) {
|
|
76
76
|
const s = String(text || '').trim().replace(/\s+/g, ' ');
|
|
77
77
|
if (s.length <= max) return s;
|
package/src/terminal-sections.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
// The MCP-services, tier-analysis, progression-ladder and tier-roadmap sections of the terminal report, extracted from src/render-terminal.js
|
|
3
|
+
// The MCP-services, tier-analysis, progression-ladder and tier-roadmap sections of the terminal report, extracted from src/render-terminal.js.
|
|
4
4
|
|
|
5
5
|
const { getRoadmapEntry } = require('./roadmap-content');
|
|
6
6
|
const { analyzeTier, buildLadder } = require('./tier-analysis');
|
|
@@ -27,7 +27,7 @@ function printMcpServices(report, t, p) {
|
|
|
27
27
|
p(` ${c.primary}${shown.join(`${c.reset}${c.muted} · ${c.reset}${c.primary}`)}${c.reset}`);
|
|
28
28
|
}
|
|
29
29
|
// Stated, never omitted: the list has to account for every server the tier
|
|
30
|
-
// counted, or the screen shows two different numbers for one set
|
|
30
|
+
// counted, or the screen shows two different numbers for one set.
|
|
31
31
|
if (unidentified > 0) {
|
|
32
32
|
for (const line of wrap(t.html.mcpUnidentified(unidentified))) p(` ${c.dim}${line}${c.reset}`);
|
|
33
33
|
}
|
|
@@ -82,7 +82,7 @@ function printLadder(report, t, p) {
|
|
|
82
82
|
p(` ${c.muted}${ld.setupIntro}${c.reset}`);
|
|
83
83
|
p(` ${c.dim}${ld.legend(done, current, pending)}${c.reset}`);
|
|
84
84
|
p();
|
|
85
|
-
// NESTED
|
|
85
|
+
// NESTED: each SETUP LEVEL, then its tiers indented beneath.
|
|
86
86
|
for (const lvl of setupLevels) {
|
|
87
87
|
const nameStyle = lvl.status === 'pending' ? c.muted : `${c.bold}${c.white}`;
|
|
88
88
|
const keys = lvl.tierKeys.length ? ` ${c.muted}— [${lvl.tierKeys.join(', ')}]${c.reset}` : '';
|
|
@@ -138,8 +138,8 @@ function printRoadmap(report, maturity, t, lang, p) {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
p(` ${c.bold}${c.warning}${t.html.roadmapHeading}${c.reset}`);
|
|
141
|
-
//
|
|
142
|
-
// Wrapped like every other prose line in this block
|
|
141
|
+
// The register, stated before the content.
|
|
142
|
+
// Wrapped like every other prose line in this block: it was the one line still running past the 80th column, which is the shape the squad read as a dump.
|
|
143
143
|
for (const line of wrap(t.html.roadmapSubheading)) p(` ${c.dim}${line}${c.reset}`);
|
|
144
144
|
|
|
145
145
|
if (curatedEntry.contentUnavailable) {
|
|
@@ -152,15 +152,15 @@ function printRoadmap(report, maturity, t, lang, p) {
|
|
|
152
152
|
const personalization = report && report.roadmapPersonalization;
|
|
153
153
|
const entry = mergeRoadmapPersonalization(curatedEntry, personalization);
|
|
154
154
|
|
|
155
|
-
//
|
|
156
|
-
// THE BEFORE AND THE AFTER
|
|
155
|
+
// The jump title gets air and its own line, and everything below it hangs off it.
|
|
156
|
+
// THE BEFORE AND THE AFTER, above the steps and not below them.
|
|
157
157
|
printProjection(report, t, p);
|
|
158
158
|
|
|
159
159
|
p();
|
|
160
160
|
p(` ${c.white}${c.bold}${sanitizeRenderText(entry.title)}${c.reset}`);
|
|
161
161
|
p();
|
|
162
162
|
|
|
163
|
-
//
|
|
163
|
+
// Whether this roadmap is YOURS or the template, said out loud.
|
|
164
164
|
if (personalization) {
|
|
165
165
|
p(` ${c.dim}${t.html.roadmapPersonalizedNotice}${c.reset}`);
|
|
166
166
|
p();
|
|
@@ -170,7 +170,7 @@ function printRoadmap(report, maturity, t, lang, p) {
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
if (entry.maxTier) {
|
|
173
|
-
//
|
|
173
|
+
// T7 is NOT a dead end.
|
|
174
174
|
if (entry.whatRemains) {
|
|
175
175
|
for (const line of wrap(sanitizeRenderText(entry.whatRemains))) p(` ${c.muted}${line}${c.reset}`);
|
|
176
176
|
p();
|
|
@@ -198,7 +198,7 @@ function printRoadmap(report, maturity, t, lang, p) {
|
|
|
198
198
|
{
|
|
199
199
|
const promptText = buildImplementationPrompt(entry, report, maturity, lang);
|
|
200
200
|
if (promptText) {
|
|
201
|
-
//
|
|
201
|
+
// THE wall of text.
|
|
202
202
|
sep(p);
|
|
203
203
|
p(` ${c.bold}${t.html.implementationPromptHeading}${c.reset}`);
|
|
204
204
|
for (const line of wrap(t.html.implementationPromptHint)) p(` ${c.dim}${line}${c.reset}`);
|
|
@@ -210,7 +210,7 @@ function printRoadmap(report, maturity, t, lang, p) {
|
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
// --build-next-level
|
|
213
|
+
// --build-next-level is now a SECONDARY, opt-in alternative to the prompt above — never when already at the terminal T7 entry (there is nothing left to build).
|
|
214
214
|
if (!entry.maxTier) p(` ${c.dim}${t.cli.buildNextLevelHint}${c.reset}`);
|
|
215
215
|
|
|
216
216
|
p();
|
package/src/tier-engine.js
CHANGED
|
@@ -20,7 +20,7 @@ const AGENTIC_IDS = ['claude-code', 'aider', 'gemini-cli', 'codex-cli', 'amazon-
|
|
|
20
20
|
// Band 0-4 derived from tier (level-model.md, single source of truth): index = tier -> band.
|
|
21
21
|
const BAND_BY_TIER = [0, 1, 2, 3, 3, 4, 4, 4];
|
|
22
22
|
|
|
23
|
-
// Setup Level (Talent Certification Framework
|
|
23
|
+
// Setup Level (Talent Certification Framework): the 3-value rollup that REPLACES the retired 0-4 band on every display surface.
|
|
24
24
|
const SETUP_LEVELS = [
|
|
25
25
|
{ key: 'none', code: null, rank: 0, emoji: '○' },
|
|
26
26
|
{ key: 'S1', code: 'S1', rank: 1, emoji: '◔' },
|
|
@@ -60,7 +60,7 @@ function aggregateTierSignals(report) {
|
|
|
60
60
|
? report.agentCounts.agents
|
|
61
61
|
: 0;
|
|
62
62
|
|
|
63
|
-
// THE MCP SIGNAL IS THE DEDUPED SET THE REPORT SHOWS
|
|
63
|
+
// THE MCP SIGNAL IS THE DEDUPED SET THE REPORT SHOWS.
|
|
64
64
|
const mcpTotal =
|
|
65
65
|
report && report.mcp && typeof report.mcp.total === 'number' ? report.mcp.total : mcp;
|
|
66
66
|
|
|
@@ -93,7 +93,7 @@ function computeTierResult(report) {
|
|
|
93
93
|
tierKey: meta.key,
|
|
94
94
|
tierName: meta.name,
|
|
95
95
|
band: bandForTier(tier),
|
|
96
|
-
// Setup Level
|
|
96
|
+
// Setup Level — the shown rollup. `{key, code, rank, emoji}`;
|
|
97
97
|
// the label is resolved from i18n by `key` at the render layer.
|
|
98
98
|
setupLevel: setupLevelForTier(tier),
|
|
99
99
|
signals,
|
package/src/tier-projection.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { classify } = require('./maturity');
|
|
4
4
|
const { computeTierResult, aggregateTierSignals, computeTier } = require('./tier-engine');
|
|
5
5
|
|
|
6
|
-
// Where this roadmap leaves you, computed with the REAL engine
|
|
6
|
+
// Where this roadmap leaves you, computed with the REAL engine.
|
|
7
7
|
|
|
8
8
|
// One entry per TARGET tier: the smallest report-shape changes that satisfy that tier's criterion.
|
|
9
9
|
const AGENTIC_TOOL_ID = 'claude-code';
|
|
@@ -42,7 +42,7 @@ const bumpDepth = (field) => (r) => {
|
|
|
42
42
|
tool.depth[field] = (tool.depth[field] || 0) + 1;
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
// "Configure one MCP server" needs its own delta, and the reason is a collision worth recording
|
|
45
|
+
// "Configure one MCP server" needs its own delta, and the reason is a collision worth recording.
|
|
46
46
|
const addMcpServer = (r) => {
|
|
47
47
|
const tool = ensureDetectedTool(r);
|
|
48
48
|
tool.depth.mcpServers = (tool.depth.mcpServers || 0) + 1;
|
package/src/transcript-turns.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { iterateSessionObjects } = require('./session-scan');
|
|
4
4
|
|
|
5
|
-
// Human-turn reader for the steering/decisions extractors
|
|
5
|
+
// Human-turn reader for the steering/decisions extractors.
|
|
6
6
|
|
|
7
7
|
function isHumanTurn(obj) {
|
|
8
8
|
if (!obj || typeof obj !== 'object') return false;
|
package/src/typewriter.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { isInteractive } = require('./terminal-progress');
|
|
4
4
|
|
|
5
|
-
// Progressive reveal of text that is ALREADY IN HAND
|
|
5
|
+
// Progressive reveal of text that is ALREADY IN HAND.
|
|
6
6
|
|
|
7
7
|
// 60fps. Fine enough that the reveal reads as continuous, coarse enough that a
|
|
8
8
|
// 700ms budget is ~43 writes rather than one per character.
|
package/src/usage-model-calls.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
// The three ephemeral model-call orchestrators of the `ai-usage` flow, extracted from bin/ai-usage.js
|
|
3
|
+
// The three ephemeral model-call orchestrators of the `ai-usage` flow, extracted from bin/ai-usage.js.
|
|
4
4
|
|
|
5
5
|
const { parseAgentDescriptions, parseAgentDefinitionsForRoots } = require('./agent-org-chart');
|
|
6
6
|
const {
|
|
@@ -21,7 +21,7 @@ const { hasTraceContentConsent } = require('./share');
|
|
|
21
21
|
const { getRoadmapEntry } = require('./roadmap-content');
|
|
22
22
|
const { computeTierResult } = require('./tier-engine');
|
|
23
23
|
|
|
24
|
-
// Agent-synthesis call (
|
|
24
|
+
// Agent-synthesis call ("ephemeral" — not persisted).
|
|
25
25
|
async function maybeSynthesizeAgents(report, root) {
|
|
26
26
|
const KEY = MODEL_CALL.SYNTHESIS;
|
|
27
27
|
if (!Array.isArray(report.agents) || report.agents.length === 0) {
|
|
@@ -51,7 +51,7 @@ async function maybeSynthesizeAgents(report, root) {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
// Ephemeral agent-evaluation call
|
|
54
|
+
// Ephemeral agent-evaluation call: scores each agent's DEFINITION quality server-side (gemini-2.5-flash — see src/agent-evaluation.js).
|
|
55
55
|
async function maybeEvaluateAgents(report, roots, lang) {
|
|
56
56
|
const KEY = MODEL_CALL.EVALUATION;
|
|
57
57
|
if (!Array.isArray(report.agents) || report.agents.length === 0) {
|
|
@@ -66,7 +66,7 @@ async function maybeEvaluateAgents(report, roots, lang) {
|
|
|
66
66
|
|
|
67
67
|
try {
|
|
68
68
|
const definitions = parseAgentDefinitionsForRoots(roots);
|
|
69
|
-
//
|
|
69
|
+
// Pass the report language so the rationale + the one-line description come back translated (a Spanish-authored definition still yields report-language prose).
|
|
70
70
|
const requestBody = buildAgentEvaluationRequest(report.agents, definitions, lang, hasTraceContentConsent());
|
|
71
71
|
return await requestAgentEvaluation(requestBody, {
|
|
72
72
|
endpoint,
|
|
@@ -103,8 +103,8 @@ async function maybePersonalizeRoadmap(report, maturity, lang) {
|
|
|
103
103
|
|
|
104
104
|
try {
|
|
105
105
|
const tierResult = computeTierResult(report);
|
|
106
|
-
//
|
|
107
|
-
//
|
|
106
|
+
// Pass the report language so the personalized prose is localized.
|
|
107
|
+
// Fresh consent read, see maybeSynthesizeAgents above.
|
|
108
108
|
const requestBody = buildRoadmapPersonalizationRequest(entry, tierResult, report, lang, hasTraceContentConsent());
|
|
109
109
|
return await requestRoadmapPersonalization(requestBody, {
|
|
110
110
|
endpoint,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
// One-shot command handlers extracted from bin/ai-usage.js
|
|
3
|
+
// One-shot command handlers extracted from bin/ai-usage.js.
|
|
4
4
|
|
|
5
5
|
const {
|
|
6
6
|
getConsentStatus,
|
|
@@ -17,7 +17,7 @@ const {
|
|
|
17
17
|
} = require('./config');
|
|
18
18
|
const { buildNextLevelStarter } = require('./build-next-level');
|
|
19
19
|
|
|
20
|
-
// One-shot consent management commands
|
|
20
|
+
// One-shot consent management commands. Mirror the retired
|
|
21
21
|
// `--enroll` pattern: they act immediately and do NOT scan.
|
|
22
22
|
|
|
23
23
|
function doConsentStatus(catalog) {
|
|
@@ -41,7 +41,7 @@ function doConsentRevoke(catalog) {
|
|
|
41
41
|
process.stdout.write(`\n ${catalog.consent.revoked}\n\n`);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
//
|
|
44
|
+
// Clears the decision back to "no decision yet" so the consent question is asked again next run — distinct from revoke (which persists `denied`).
|
|
45
45
|
function doConsentReset(catalog) {
|
|
46
46
|
resetConsent();
|
|
47
47
|
process.stdout.write(`\n ${catalog.consent.reset}\n\n`);
|
|
@@ -102,14 +102,14 @@ function doShowEndpoint(catalog) {
|
|
|
102
102
|
process.stdout.write(`\n ${e.showNone}\n\n`);
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
//
|
|
105
|
+
// There is no second hop any more.
|
|
106
106
|
if (hasRetiredFallbackConfig()) {
|
|
107
107
|
process.stdout.write(` ${e.showFallbackRetired}\n\n`);
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
// "Construir el siguiente nivel ahora"
|
|
112
|
+
// "Construir el siguiente nivel ahora": optional, explicit phase — never runs unless the talent asks for it via --build-next-level.
|
|
113
113
|
function doBuildNextLevel(root, maturity, force, catalog) {
|
|
114
114
|
const b = catalog.buildNextLevel;
|
|
115
115
|
const result = buildNextLevelStarter(root || process.cwd(), maturity.tierKey, { force });
|