shakers-cli 0.15.0 → 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.
Files changed (123) hide show
  1. package/README.md +51 -1203
  2. package/bin/ai-usage.js +11 -56
  3. package/bin/login.js +10 -10
  4. package/bin/mcp.js +13 -9
  5. package/bin/mcpb.js +1 -1
  6. package/bin/onboarding.js +3 -2
  7. package/bin/register.js +17 -4
  8. package/bin/report-html.js +2 -2
  9. package/bin/shakers-cli.js +352 -0
  10. package/bin/shakers.js +20 -344
  11. package/bin/share.js +1 -1
  12. package/bin/superadmin.js +5 -5
  13. package/bin/version.js +3 -0
  14. package/package.json +12 -3
  15. package/src/agent-category.js +1 -1
  16. package/src/agent-evaluation.js +3 -3
  17. package/src/agent-org-chart.js +5 -5
  18. package/src/agent-synthesis.js +3 -3
  19. package/src/agent-usage.js +1 -1
  20. package/src/agents-client.js +4 -4
  21. package/src/ai.js +1 -1
  22. package/src/ansi.js +1 -1
  23. package/src/auth-client.js +18 -4
  24. package/src/auth-method-picker.js +1 -1
  25. package/src/auth-methods.js +1 -1
  26. package/src/auth-session-store.js +11 -8
  27. package/src/authorship-mismatch.js +1 -1
  28. package/src/authorship.js +2 -2
  29. package/src/automations-detector.js +1 -1
  30. package/src/backend-request.js +3 -3
  31. package/src/brand-ansi.js +1 -1
  32. package/src/browser-tools-detector.js +1 -1
  33. package/src/certify-disclaimer.js +1 -1
  34. package/src/certify-remediation-prompt.js +1 -1
  35. package/src/cli-args.js +2 -2
  36. package/src/command-audit.js +8 -5
  37. package/src/config-dir.js +1 -1
  38. package/src/config.js +112 -48
  39. package/src/consent-flow.js +4 -4
  40. package/src/consent-skip.js +1 -1
  41. package/src/detection-evidence.js +2 -2
  42. package/src/detection-noise.js +1 -1
  43. package/src/detectors.js +1 -1
  44. package/src/device-login.js +12 -1
  45. package/src/email-verification.js +3 -3
  46. package/src/env-paths.js +1 -1
  47. package/src/find-projects-client.js +12 -0
  48. package/src/find-projects-flow.js +1 -13
  49. package/src/git-activity.js +1 -1
  50. package/src/graph-certs.js +3 -3
  51. package/src/graph-scan.js +9 -9
  52. package/src/i18n.js +124 -124
  53. package/src/interactive-select.js +1 -1
  54. package/src/interview-client.js +6 -1
  55. package/src/interview-transcript.js +1 -1
  56. package/src/legal-notice.js +1 -1
  57. package/src/maturity.js +6 -6
  58. package/src/mcp-ai-usage-tool.js +1 -1
  59. package/src/mcp-auth-form.js +1 -1
  60. package/src/mcp-detector.js +3 -3
  61. package/src/mcp-device-login-tools.js +2 -1
  62. package/src/mcp-find-projects-tools.js +1 -2
  63. package/src/mcp-onboarding-tools.js +4 -4
  64. package/src/mcp-prompts.js +2 -2
  65. package/src/mcp-register-tools.js +1 -0
  66. package/src/mcp-server.js +5 -1
  67. package/src/mcp-signup-client.js +2 -2
  68. package/src/mcp-signup-tools.js +26 -2
  69. package/src/memory-structure-detector.js +1 -1
  70. package/src/model-call-record.js +2 -2
  71. package/src/onboarding-flow.js +16 -16
  72. package/src/onboarding-funnel.js +110 -17
  73. package/src/open-file.js +1 -1
  74. package/src/osc-link.js +1 -1
  75. package/src/portfolio-client.js +11 -1
  76. package/src/portfolio-git-info.js +1 -1
  77. package/src/profile-flow.js +1 -1
  78. package/src/profile-switch.js +5 -12
  79. package/src/render-certification.js +10 -10
  80. package/src/render-html.js +6 -6
  81. package/src/render-sheet.js +10 -10
  82. package/src/render-terminal.js +6 -6
  83. package/src/repo-scope.js +132 -0
  84. package/src/report-gating.js +1 -1
  85. package/src/report-store.js +6 -6
  86. package/src/roadmap-content.js +5 -5
  87. package/src/roadmap-personalization.js +3 -3
  88. package/src/roadmap-prompt.js +2 -2
  89. package/src/scan-exclusions.js +1 -1
  90. package/src/scanner.js +14 -14
  91. package/src/secret-prompt.js +1 -1
  92. package/src/session-gate.js +1 -1
  93. package/src/session-refresh.js +33 -3
  94. package/src/session-scan.js +1 -1
  95. package/src/shakers-wordmark.js +1 -1
  96. package/src/share-card.js +2 -2
  97. package/src/share-certification.js +4 -4
  98. package/src/share-consent-state.js +7 -7
  99. package/src/share-payload.js +7 -7
  100. package/src/share.js +7 -7
  101. package/src/signup-client.js +1 -1
  102. package/src/signup-copy.js +1 -1
  103. package/src/skills-client.js +1 -1
  104. package/src/start-add-agents.js +2 -2
  105. package/src/start-add-portfolio.js +3 -13
  106. package/src/start-add-skills.js +2 -2
  107. package/src/stdin-ask.js +3 -3
  108. package/src/strip-internal-comments.js +1 -1
  109. package/src/tech-detector.js +2 -2
  110. package/src/templates/report-sheet.html +11 -13
  111. package/src/terminal-agents.js +8 -8
  112. package/src/terminal-format.js +3 -3
  113. package/src/terminal-sections.js +11 -11
  114. package/src/tier-engine.js +3 -3
  115. package/src/tier-projection.js +2 -2
  116. package/src/transcript-turns.js +1 -1
  117. package/src/typewriter.js +1 -1
  118. package/src/usage-model-calls.js +6 -6
  119. package/src/usage-oneshot-commands.js +5 -5
  120. package/src/usage-repo-scope.js +11 -129
  121. package/src/usage-run-steps.js +32 -184
  122. package/src/usage-scan.js +235 -0
  123. 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 (talents-ai-score, ADR-042 — the PRIMARY -> FALLBACK chain is retired), via src/backend-request.js.
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
- // ADR-006: never SEND under an email whose ownership wasn't proven — every
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 (ADR-011 retires the
96
- // server-side kill switch that used to be the main 503 cause).
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 (skill-code-certification, issue 005) ---------- */
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
- // ADR-006 (mirror of autoShare): don't persist under an unverified email.
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
- // ADR-027: forward the superadmin session token so the backend stamps test_origin=true (server-authoritative, non-prod only).
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
@@ -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 (owner decision, supersedes ADR-031 for the email path — WEB-login parity): 1.
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
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- // Talent-facing copy of the MCP sign-up (ticket 39540, UX review 39574): fixed text the AI shows word for word, in the talent's language.
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
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { postJsonWithTimeout } = require('./backend-request');
4
4
 
5
- // Talent-skills client (talents-ai-score, ADR-054/055 — `start`'s "Añadir skills" route, src/start-add-skills.js).
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
 
@@ -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 (talents-ai-score Phase 2), mirror of src/start-add-portfolio.js: the IA pre-drafts, the Talent edits.
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 (ADR-052) before any egress.
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 (talents-ai-score, ADR-059).
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 (dueño, 2026-08-12): 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.
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
  };
@@ -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 (talents-ai-score, ADR-054/055).
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 (ADR-041): the multi-select over the Talent's portfolios, reused verbatim.
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 (issue 102), not a `const`.
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 (skill-code-certification, certify agents): in a TTY, `readline` runs in terminal mode and redraws the current line on every keystroke (wrap, cursor).
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 (issue 102).
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} `);
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- // Our comments do not travel in the talent's shareable report (issue 098).
3
+ // Our comments do not travel in the talent's shareable report.
4
4
 
5
5
  // `<!-- … -->`, including multi-line.
6
6
  const HTML_COMMENT_RE = /<!--[\s\S]*?-->/g;
@@ -10,7 +10,7 @@ const {
10
10
  isExcludedWalkPath,
11
11
  } = require('./scan-exclusions');
12
12
 
13
- // Monorepo support (skill-code-certification, issue 008): manifests are discovered RECURSIVELY under `root`, not just at the top level.
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 (skill-code-certification, issue 008).
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 (issue 096): one line under the column head, for copy that has to
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 (issue 090, kept for issue 089).
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 issue 089 (the
219
- * shareable report has no agents at all) needs, so that design does not have to
220
- * re-invent the nesting rail. Recovering the markup that used to pair with it:
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 089 is dropped or solved another way, this goes with it. What it must not
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 (issue 054): one block per quote, under its own area row, with
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
- (issue 067). `load`, `resize` and the theme toggle each chain several
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 in
452
- issue 066, when a report with only an agent certification became reachable
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 (issue 067): each isolated so one failing never
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(){
@@ -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 (structure refactor, issue 020).
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 (ADR-016: one line per agent) As SIMPLE as possible: ONE line per agent.
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, ADR-010) content; card.name is
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 (issue 120).
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 (ADR-039): recency-windowed signals from the local scan; agentsOnProfile
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 (issue 086 — restored, not invented).
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 (issue 116).
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 — and ADR-042 is what made three of them distinguishable at all.
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;
@@ -21,7 +21,7 @@ function bar(score, width = 24) {
21
21
  return '█'.repeat(filled) + '░'.repeat(width - filled);
22
22
  }
23
23
 
24
- // ADR-016: a light visual break between the reordered sections — a dim rule
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, added for issue 087.
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 (user feedback, 2026-07-16): the earlier condense (commit 465badb) over-trimmed — it left the terminal as headings + copyable prompts with the prose stripped out.
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;
@@ -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 (structure refactor, issue 020).
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 (issue 096).
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 (ADR-016): each SETUP LEVEL, then its tiers indented beneath.
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
- // Issue 083: the register, stated before the content.
142
- // Wrapped like every other prose line in this block (issue 087): it was the one line still running past the 80th column, which is the shape the squad read as a dump.
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
- // Issue 087: the jump title gets air and its own line, and everything below it hangs off it.
156
- // THE BEFORE AND THE AFTER (issue 084), above the steps and not below them.
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
- // Issue 109: whether this roadmap is YOURS or the template, said out loud.
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
- // ADR-008 (skill-code-certification): T7 is NOT a dead end.
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
- // Issue 087: THE wall of text.
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 (issue 021) 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).
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();
@@ -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, ADR-016): the 3-value rollup that REPLACES the retired 0-4 band on every display surface.
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 (issue 112).
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 (ADR-016) — the shown rollup. `{key, code, rank, emoji}`;
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,
@@ -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 (issue 084).
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 (issue 112 landing after issue 084).
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;
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { iterateSessionObjects } = require('./session-scan');
4
4
 
5
- // Human-turn reader for the steering/decisions extractors (hybrid evaluation, Slice 2).
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 (talents-ai-score, camino A).
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.
@@ -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 (structure refactor, issue 020).
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 (talents-ai-score, ADR-010/ADR-011, "ephemeral" — not persisted; ADR-051 amends WHEN it runs, not that).
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 (ADR-016): scores each agent's DEFINITION quality server-side (gemini-2.5-flash — see src/agent-evaluation.js).
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
- // ADR-026: pass the report language so the rationale + the one-line description come back translated (a Spanish-authored definition still yields report-language prose).
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
- // ADR-026: pass the report language so the personalized prose is localized.
107
- // ADR-028: fresh consent read, see maybeSynthesizeAgents above.
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 (structure refactor, issue 020).
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 (issue 007). Mirror the retired
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
- // skill-code-certification / ADR-003: clears the decision back to "no decision yet" so the consent question is asked again next run — distinct from revoke (which persists `denied`).
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
- // ADR-042: there is no second hop any more.
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" (issue 021): optional, explicit phase — never runs unless the talent asks for it via --build-next-level.
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 });