shakers-cli 0.15.0 → 0.17.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 +56 -1203
- package/bin/ai-usage.js +32 -58
- package/bin/login.js +10 -10
- package/bin/mcp.js +17 -12
- package/bin/mcpb.js +1 -1
- package/bin/onboarding.js +3 -2
- package/bin/register.js +19 -5
- 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/account-marker.js +40 -0
- package/src/account-trace.js +23 -0
- 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-usage-review.js +101 -0
- 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 +18 -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 +5 -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 +114 -48
- package/src/consent-flow.js +7 -6
- 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 +148 -138
- 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-result-tool.js +34 -12
- package/src/mcp-ai-usage-tool.js +282 -163
- package/src/mcp-auth-form.js +7 -3
- 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-install.js +279 -44
- package/src/mcp-instructions.js +1 -1
- package/src/mcp-onboarding-tools.js +4 -4
- package/src/mcp-prompts.js +4 -4
- package/src/mcp-register-tools.js +1 -0
- package/src/mcp-role-tools.js +24 -1
- package/src/mcp-server.js +5 -1
- package/src/mcp-signup-client.js +2 -2
- package/src/mcp-signup-tools.js +39 -9
- package/src/memory-structure-detector.js +1 -1
- package/src/model-call-record.js +2 -2
- package/src/onboarding-flow.js +63 -42
- package/src/onboarding-funnel.js +153 -20
- 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-report-text.js +1 -1
- 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 +5 -3
- package/src/report-store.js +13 -8
- package/src/roadmap-content.js +5 -5
- package/src/roadmap-personalization.js +3 -3
- package/src/roadmap-prompt.js +2 -2
- package/src/roles-flow.js +8 -5
- 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 +9 -9
- 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 +177 -2
- 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 +16 -4
- 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 +82 -181
- package/src/usage-scan.js +235 -0
- package/src/work-streams.js +1 -1
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 });
|
package/src/usage-repo-scope.js
CHANGED
|
@@ -1,69 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
// Repo-scoping for the `ai-usage` command, extracted from bin/ai-usage.js
|
|
3
|
+
// Repo-scoping for the `ai-usage` command, extracted from bin/ai-usage.js.
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
const {
|
|
7
|
-
detectRepos,
|
|
8
|
-
repoIdentityForCwd,
|
|
9
|
-
matchReposByFlag,
|
|
10
|
-
scopeFromSelection,
|
|
11
|
-
indexOfCurrentRepo,
|
|
12
|
-
} = require('./detect-repos');
|
|
5
|
+
const core = require('./repo-scope');
|
|
13
6
|
const { promptSelect } = require('./prompt-select');
|
|
14
7
|
const { createStdinAsk } = require('./stdin-ask');
|
|
15
8
|
|
|
16
|
-
const
|
|
17
|
-
const SCOPE_MACHINE = 'machine';
|
|
18
|
-
const SCOPE_REPO = 'repo';
|
|
19
|
-
|
|
20
|
-
function currentRepoToplevel(cwd, repoIdentityFn = repoIdentityForCwd) {
|
|
21
|
-
try {
|
|
22
|
-
return repoIdentityFn(cwd).toplevel || cwd;
|
|
23
|
-
} catch {
|
|
24
|
-
return cwd;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// The real git toplevel of `cwd`, or null when `cwd` is not a repo (or is a refused system root like `/`, the MCP `cwd=/` case).
|
|
29
|
-
function cwdRepoToplevel(cwd, repoIdentityFn = repoIdentityForCwd) {
|
|
30
|
-
if (isRefusedWalkRoot(cwd)) return null;
|
|
31
|
-
try {
|
|
32
|
-
return repoIdentityFn(cwd).toplevel || null;
|
|
33
|
-
} catch {
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Pure: builds the machine-wide root set.
|
|
39
|
-
function machineWideToplevels(cwdRepoTop, detectedRepos, cap = MAX_MACHINE_WIDE_REPOS) {
|
|
40
|
-
const ordered = [];
|
|
41
|
-
const seen = new Set();
|
|
42
|
-
const push = (t) => {
|
|
43
|
-
if (typeof t !== 'string' || !t) return;
|
|
44
|
-
if (isRefusedWalkRoot(t)) return;
|
|
45
|
-
if (seen.has(t)) return;
|
|
46
|
-
seen.add(t);
|
|
47
|
-
ordered.push(t);
|
|
48
|
-
};
|
|
49
|
-
push(cwdRepoTop);
|
|
50
|
-
for (const r of detectedRepos || []) push(r && r.toplevel);
|
|
51
|
-
const capped = ordered.slice(0, cap);
|
|
52
|
-
return { toplevels: capped, total: ordered.length, capped: ordered.length > capped.length };
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function cwdOnlyScope(repos, cwdToplevel) {
|
|
56
|
-
const idx = indexOfCurrentRepo(repos, cwdToplevel);
|
|
57
|
-
if (idx === -1) {
|
|
58
|
-
return { selectedCwds: new Set(), toplevels: [cwdToplevel], mode: 'cwd' };
|
|
59
|
-
}
|
|
60
|
-
const { selectedCwds, selectedToplevels } = scopeFromSelection(repos, [idx]);
|
|
61
|
-
return {
|
|
62
|
-
selectedCwds,
|
|
63
|
-
toplevels: selectedToplevels.length ? selectedToplevels : [cwdToplevel],
|
|
64
|
-
mode: 'cwd',
|
|
65
|
-
};
|
|
66
|
-
}
|
|
9
|
+
const { SCOPE_MACHINE, SCOPE_REPO, machineWideToplevels } = core;
|
|
67
10
|
|
|
68
11
|
async function promptScope({ catalog, stdinIsTTY = true, mkAsk = createStdinAsk } = {}) {
|
|
69
12
|
const c = (catalog && catalog.cli) || {};
|
|
@@ -88,75 +31,14 @@ async function promptScope({ catalog, stdinIsTTY = true, mkAsk = createStdinAsk
|
|
|
88
31
|
}
|
|
89
32
|
}
|
|
90
33
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
chooseScope = promptScope,
|
|
100
|
-
isTTY = !!process.stdin.isTTY && !!process.stdout.isTTY,
|
|
101
|
-
}) {
|
|
102
|
-
const cwdToplevel = currentRepoToplevel(cwd, repoIdentityFn);
|
|
103
|
-
const cwdRepoTop = cwdRepoToplevel(cwd, repoIdentityFn);
|
|
104
|
-
const note = (s) => {
|
|
105
|
-
if (!opts.json) process.stdout.write(`\n ${s}\n`);
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
const detect = () => {
|
|
109
|
-
try {
|
|
110
|
-
return detectReposFn(process.env);
|
|
111
|
-
} catch {
|
|
112
|
-
return { repos: [], unassignedSessionCount: 0 };
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
// 1. Explicit repo list always wins.
|
|
117
|
-
if (opts.repos) {
|
|
118
|
-
const { repos, unassignedSessionCount } = detect();
|
|
119
|
-
note(catalog.cli.reposDetectedNote(repos.length, unassignedSessionCount));
|
|
120
|
-
const { indices, unmatched } = matchReposByFlag(repos, opts.repos);
|
|
121
|
-
if (unmatched.length > 0) {
|
|
122
|
-
process.stderr.write(` ${catalog.cli.reposFlagUnmatched(unmatched.join(', '))}\n`);
|
|
123
|
-
}
|
|
124
|
-
if (indices.length > 0) {
|
|
125
|
-
const { selectedCwds, selectedToplevels } = scopeFromSelection(repos, indices);
|
|
126
|
-
return { selectedCwds, toplevels: [...new Set(selectedToplevels)], mode: 'flag' };
|
|
127
|
-
}
|
|
128
|
-
return cwdOnlyScope(repos, cwdToplevel);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const wantsMachine = opts.allRepos || opts.machine || opts.scope === SCOPE_MACHINE;
|
|
132
|
-
const wantsRepo = opts.repo || opts.scope === SCOPE_REPO;
|
|
133
|
-
|
|
134
|
-
let choice = null;
|
|
135
|
-
if (wantsMachine) choice = SCOPE_MACHINE;
|
|
136
|
-
else if (wantsRepo) choice = SCOPE_REPO;
|
|
137
|
-
else if (isTTY && !injectedAsk && !opts.json) {
|
|
138
|
-
choice = await chooseScope({ catalog, stdinIsTTY: true });
|
|
139
|
-
}
|
|
140
|
-
if (choice === null) choice = SCOPE_REPO;
|
|
141
|
-
|
|
142
|
-
if (choice === SCOPE_MACHINE) {
|
|
143
|
-
const { repos, unassignedSessionCount } = detect();
|
|
144
|
-
note(catalog.cli.reposDetectedNote(repos.length, unassignedSessionCount));
|
|
145
|
-
const mw = machineWideToplevels(cwdRepoTop, repos);
|
|
146
|
-
if (mw.capped) {
|
|
147
|
-
process.stderr.write(`\n ${catalog.cli.reposScopeCapped(mw.toplevels.length, mw.total)}\n`);
|
|
148
|
-
}
|
|
149
|
-
note(catalog.cli.reposScopeAll);
|
|
150
|
-
return {
|
|
151
|
-
selectedCwds: null,
|
|
152
|
-
toplevels: mw.toplevels.length ? mw.toplevels : [cwdToplevel],
|
|
153
|
-
mode: 'all',
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Current repo.
|
|
158
|
-
const { repos } = detect();
|
|
159
|
-
return cwdOnlyScope(repos, cwdToplevel);
|
|
34
|
+
function resolveRepoScope(args) {
|
|
35
|
+
return core.resolveRepoScope({
|
|
36
|
+
...args,
|
|
37
|
+
chooseScope: args.chooseScope || promptScope,
|
|
38
|
+
note: (s) => {
|
|
39
|
+
if (!args.opts.json) process.stdout.write(`\n ${s}\n`);
|
|
40
|
+
},
|
|
41
|
+
});
|
|
160
42
|
}
|
|
161
43
|
|
|
162
44
|
module.exports = { resolveRepoScope, machineWideToplevels, promptScope };
|