sigrank 0.18.5 → 1.0.1
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/CHANGELOG.md +158 -0
- package/MCP_SERVER_DESCRIPTION.md +172 -0
- package/README.md +94 -41
- package/adapters/index.mjs +1100 -0
- package/adapters/tokenpull.mjs +568 -0
- package/adapters.mjs +1 -857
- package/analytics/cascade.mjs +338 -0
- package/cascade.mjs +1 -274
- package/cli.mjs +1 -1853
- package/connect.mjs +1 -13
- package/identity/connect.mjs +13 -0
- package/identity/keystore.mjs +231 -0
- package/identity/sign.mjs +106 -0
- package/index.mjs +47 -162
- package/keystore.mjs +1 -242
- package/lib/constants.mjs +137 -0
- package/lib/pkg-version.mjs +59 -0
- package/manifest.json +46 -0
- package/narrate.mjs +1 -77
- package/package.json +41 -16
- package/preflight.mjs +1 -191
- package/presentation/cli.mjs +1926 -0
- package/presentation/narrate.mjs +219 -0
- package/presentation/tui-themes.mjs +189 -0
- package/presentation/tui.mjs +3316 -0
- package/prompts/check-my-efficiency.md +1 -0
- package/prompts/compare-with-leader.md +1 -0
- package/prompts/simulate-improvement.md +1 -0
- package/proxy.mjs +492 -0
- package/resources/class-tiers.md +81 -0
- package/resources/data-policy.md +31 -0
- package/resources/install-guide.md +59 -0
- package/resources/privacy-model.md +44 -0
- package/resources/scoring-formula.md +24 -0
- package/review.mjs +263 -0
- package/sign.mjs +1 -106
- package/submit/index.mjs +297 -0
- package/submit/preflight.mjs +191 -0
- package/submit.mjs +1 -303
- package/tokenpull.mjs +1 -576
- package/tokscale_analytics.mjs +864 -0
- package/tools/_framing.mjs +217 -0
- package/tools/_helpers.mjs +253 -0
- package/tools/_peer-matching.mjs +361 -0
- package/tools/_report.mjs +262 -0
- package/tools/_schemas.mjs +492 -0
- package/tools/_verifiers.mjs +148 -0
- package/tools/compare-operators.mjs +80 -0
- package/tools/compare-self.mjs +124 -0
- package/tools/describe-power-user.mjs +78 -0
- package/tools/diagnose-cascade.mjs +192 -0
- package/tools/discover-peers.mjs +198 -0
- package/tools/enroll.mjs +119 -0
- package/tools/get-best-operator.mjs +59 -0
- package/tools/get-leaderboard.mjs +27 -0
- package/tools/get-operator.mjs +33 -0
- package/tools/index.mjs +229 -0
- package/tools/optimize-efficiency.mjs +170 -0
- package/tools/rank-paste.mjs +51 -0
- package/tools/rank-windows.mjs +120 -0
- package/tools/self-improve.mjs +423 -0
- package/tools/simulate-change.mjs +207 -0
- package/tools/submit-paste.mjs +121 -0
- package/tools/submit-verified.mjs +153 -0
- package/tools/suggest-improvements.mjs +168 -0
- package/tools/tokenpull-compare.mjs +123 -0
- package/tools/tokenpull-submit.mjs +235 -0
- package/tools/tokenpull.mjs +113 -0
- package/tools/tokscale-breakdown.mjs +119 -0
- package/tools/tokscale-competitive-intel.mjs +45 -0
- package/tools/tokscale-cost-analysis.mjs +31 -0
- package/tools/tokscale-developer-profile.mjs +36 -0
- package/tools/tokscale-device-profile.mjs +35 -0
- package/tools/tokscale-market-share.mjs +38 -0
- package/tools/tokscale-mcp-usage.mjs +33 -0
- package/tools/tokscale-model-trends.mjs +32 -0
- package/tools/watch-tokenpull.mjs +134 -0
- package/tools.mjs +1 -3897
- package/tui.mjs +4 -2650
- package/banner.svg +0 -7
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tools/rank-paste.mjs — rank_paste tool.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { cascade, parsePillars } from "../analytics/cascade.mjs";
|
|
6
|
+
import { narrate } from "../presentation/narrate.mjs";
|
|
7
|
+
import { CASCADE_OUTPUT, ANNOTATIONS } from "./_schemas.mjs";
|
|
8
|
+
import { MAX_INPUT, withParseWarnings } from "./_helpers.mjs";
|
|
9
|
+
|
|
10
|
+
export const TOOL_DEF = {
|
|
11
|
+
name: "rank_paste",
|
|
12
|
+
description:
|
|
13
|
+
'Computes the SigRank yield cascade from a paste of token counts. Parses the input, runs the full cascade math locally (no network calls), and returns: yield (Υ, the headline efficiency metric, Υ = Cache Reads × Output / Input²), snr (signal-to-noise ratio), leverage (Cr/I = cache reads divided by input), velocity (O/I = output divided by input), dev10x (10xDEV score), class (operator experience stage — 24 stages = 8 tiers × 3 sub-stages I/II/III, e.g. ARCH+ I, REFINER II, IGNITER III, or UNCLASSED for empty input), mode (detected working mode), and a deterministic prose "card" summarizing the result in plain English. Accepts two input formats: (1) JSON object {"input":N,"output":N,"cacheCreate":N,"cacheRead":N} or (2) four whitespace-separated numbers in order: input output cacheCreate cacheRead. Returns an error if the input is malformed or has negative values. Use this for a quick one-off ranking without submitting to the board. Do NOT use this to submit your score — use submit_paste instead, which both ranks and publishes. Do NOT use this if you want to rank all four time windows at once — use rank_windows for that. After calling this, use submit_paste to publish the result if you want to appear on the leaderboard.',
|
|
14
|
+
annotations: {
|
|
15
|
+
title: "Rank a paste",
|
|
16
|
+
...ANNOTATIONS.readOnlyHint,
|
|
17
|
+
...ANNOTATIONS.destructiveHint,
|
|
18
|
+
...ANNOTATIONS.idempotentHint,
|
|
19
|
+
...ANNOTATIONS.openWorldHint,
|
|
20
|
+
},
|
|
21
|
+
inputSchema: {
|
|
22
|
+
type: "object",
|
|
23
|
+
properties: {
|
|
24
|
+
text: {
|
|
25
|
+
type: "string",
|
|
26
|
+
description:
|
|
27
|
+
'Token counts to rank. Two formats accepted: (1) JSON object {"input":N,"output":N,"cacheCreate":N,"cacheRead":N} where all values are non-negative integers, or (2) four whitespace-separated numbers in order: input output cacheCreate cacheRead. Get these from `ccusage` output, the Claude Max usage dashboard, tokscale, or any token reader. Example valid input: {"input":1000000,"output":500000,"cacheCreate":50000,"cacheRead":800000}',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
required: ["text"],
|
|
31
|
+
description:
|
|
32
|
+
"Requires the token counts as a string. No other parameters are accepted.",
|
|
33
|
+
},
|
|
34
|
+
outputSchema: CASCADE_OUTPUT,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export async function handleRankPaste(args) {
|
|
38
|
+
if (!args?.text)
|
|
39
|
+
throw new Error("rank_paste requires a non-empty `text` argument.");
|
|
40
|
+
// E2: reject oversized pastes before parsing (parity with submit_paste / rank_windows).
|
|
41
|
+
if (typeof args.text === "string" && args.text.length > MAX_INPUT) {
|
|
42
|
+
return {
|
|
43
|
+
status: "error",
|
|
44
|
+
reason: "input_too_large",
|
|
45
|
+
detail: `text exceeds ${MAX_INPUT} chars (${args.text.length}). Paste only the token-count table, not full output.`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const pillars = parsePillars(args.text);
|
|
49
|
+
const c = withParseWarnings(pillars, cascade(pillars));
|
|
50
|
+
return { ...c, card: narrate(c) };
|
|
51
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tools/rank-windows.mjs — rank_windows tool.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { cascade, parsePillars } from "../analytics/cascade.mjs";
|
|
6
|
+
import { narrate } from "../presentation/narrate.mjs";
|
|
7
|
+
import { CASCADE_OUTPUT, ANNOTATIONS } from "./_schemas.mjs";
|
|
8
|
+
import { MAX_INPUT, withParseWarnings } from "./_helpers.mjs";
|
|
9
|
+
|
|
10
|
+
export const TOOL_DEF = {
|
|
11
|
+
name: "rank_windows",
|
|
12
|
+
description:
|
|
13
|
+
"Rank all four time windows (7d/30d/90d/all-time) in one call from a dashboard paste — paste the full table from ccusage, tokscale, or the Claude Max usage dashboard and get the cascade (Υ, SNR, Leverage, Velocity, 10xDEV, class, card) for each window. Each window is parsed and scored independently. Named keys required (input/output/cacheCreate/cacheRead); positional order is NOT safe here (dashboards list cache_read before cache_create — see WINDOWED_PROFILES gotcha). Omit windows you don't have — partial input is allowed (1–4 windows). Does NOT submit to the board; use tokenpull_submit for zero-paste publishing.",
|
|
14
|
+
annotations: {
|
|
15
|
+
title: "Rank all time windows",
|
|
16
|
+
...ANNOTATIONS.readOnlyHint,
|
|
17
|
+
...ANNOTATIONS.idempotentHint,
|
|
18
|
+
...ANNOTATIONS.openWorldHint,
|
|
19
|
+
},
|
|
20
|
+
inputSchema: {
|
|
21
|
+
type: "object",
|
|
22
|
+
properties: {
|
|
23
|
+
"7d": {
|
|
24
|
+
type: "string",
|
|
25
|
+
description:
|
|
26
|
+
"ccusage/tokscale paste or JSON for the 7-day window (optional)",
|
|
27
|
+
},
|
|
28
|
+
"30d": {
|
|
29
|
+
type: "string",
|
|
30
|
+
description:
|
|
31
|
+
"ccusage/tokscale paste or JSON for the 30-day window (optional)",
|
|
32
|
+
},
|
|
33
|
+
"90d": {
|
|
34
|
+
type: "string",
|
|
35
|
+
description:
|
|
36
|
+
"ccusage/tokscale paste or JSON for the 90-day window (optional)",
|
|
37
|
+
},
|
|
38
|
+
all: {
|
|
39
|
+
type: "string",
|
|
40
|
+
description:
|
|
41
|
+
"ccusage/tokscale paste or JSON for the all-time window (optional)",
|
|
42
|
+
},
|
|
43
|
+
source_tool: {
|
|
44
|
+
type: "string",
|
|
45
|
+
enum: [
|
|
46
|
+
"ccusage",
|
|
47
|
+
"tokscale",
|
|
48
|
+
"claude_max",
|
|
49
|
+
"token_dashboard",
|
|
50
|
+
"other",
|
|
51
|
+
],
|
|
52
|
+
description:
|
|
53
|
+
"which token reader produced the paste (for cross-tool variance tracking)",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
// at least one window paste is required (runtime check backs this up)
|
|
57
|
+
anyOf: [
|
|
58
|
+
{ required: ["7d"] },
|
|
59
|
+
{ required: ["30d"] },
|
|
60
|
+
{ required: ["90d"] },
|
|
61
|
+
{ required: ["all"] },
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
outputSchema: {
|
|
65
|
+
type: "object",
|
|
66
|
+
properties: {
|
|
67
|
+
windows: {
|
|
68
|
+
type: "array",
|
|
69
|
+
description: "Cascade results per window",
|
|
70
|
+
items: {
|
|
71
|
+
type: "object",
|
|
72
|
+
properties: {
|
|
73
|
+
window: { type: "string", enum: ["7d", "30d", "90d", "all"] },
|
|
74
|
+
...CASCADE_OUTPUT.properties,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export async function handleRankWindows(args) {
|
|
83
|
+
// Score up to 4 named window pastes independently. Named-key parsing only —
|
|
84
|
+
// positional is unsafe here because dashboards list cache_read before cache_create
|
|
85
|
+
// (the WINDOWED_PROFILES swap gotcha). Each window goes through parsePillars →
|
|
86
|
+
// cascade → narrate individually; results are collected into a windows[] array
|
|
87
|
+
// in the same shape as tokenpull output for easy follow-up with tokenpull_submit.
|
|
88
|
+
const WINDOW_KEYS = ["7d", "30d", "90d", "all"];
|
|
89
|
+
const sourceTool = args?.source_tool || null;
|
|
90
|
+
// E2: reject any oversized window paste up front (token tables are tiny).
|
|
91
|
+
for (const wk of WINDOW_KEYS) {
|
|
92
|
+
const v = args?.[wk];
|
|
93
|
+
if (typeof v === "string" && v.length > MAX_INPUT) {
|
|
94
|
+
return {
|
|
95
|
+
status: "error",
|
|
96
|
+
reason: "input_too_large",
|
|
97
|
+
detail: `window '${wk}' exceeds ${MAX_INPUT} chars (${v.length}). Paste only the token-count table.`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const windows = [];
|
|
102
|
+
for (const wk of WINDOW_KEYS) {
|
|
103
|
+
const text = args?.[wk];
|
|
104
|
+
if (!text || typeof text !== "string" || !text.trim()) continue;
|
|
105
|
+
const pillars = parsePillars(text);
|
|
106
|
+
const c = withParseWarnings(pillars, cascade(pillars));
|
|
107
|
+
const card = narrate(c, `${wk} window`);
|
|
108
|
+
windows.push({ window: wk, pillars, cascade: c, card });
|
|
109
|
+
}
|
|
110
|
+
if (windows.length === 0) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
"rank_windows requires at least one window paste (7d, 30d, 90d, or all).",
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
windows,
|
|
117
|
+
source_tool: sourceTool,
|
|
118
|
+
note: "Local preview only — use tokenpull_submit to publish to the board.",
|
|
119
|
+
};
|
|
120
|
+
}
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tools/self-improve.mjs — self_improve tool.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { cascade, parsePillars, detectMode, qualityScore, MODE_EXPECTED_YIELD } from "../analytics/cascade.mjs";
|
|
6
|
+
import { computeBadges } from "../badges.mjs";
|
|
7
|
+
import { CASCADE_OUTPUT, ANNOTATIONS } from "./_schemas.mjs";
|
|
8
|
+
import { MAX_INPUT, withParseWarnings } from "./_helpers.mjs";
|
|
9
|
+
import {
|
|
10
|
+
_pullDailyRows,
|
|
11
|
+
_compoundWeekly,
|
|
12
|
+
_modeDistribution,
|
|
13
|
+
_modeWeightedYield,
|
|
14
|
+
_peakYield,
|
|
15
|
+
_healthScore,
|
|
16
|
+
_yieldForDays,
|
|
17
|
+
_trajectory,
|
|
18
|
+
_phasePattern,
|
|
19
|
+
_dailyAssessment,
|
|
20
|
+
_dailyAdvice,
|
|
21
|
+
} from "./_report.mjs";
|
|
22
|
+
|
|
23
|
+
export const TOOL_DEF = {
|
|
24
|
+
name: "self_improve",
|
|
25
|
+
description:
|
|
26
|
+
"Runs the full self-improvement cycle in one call: (1) gets your current token pillars — either from the provided text or by running tokenpull on your local logs, (2) diagnoses where you're leaking efficiency (diagnose_cascade), (3) generates ranked improvement suggestions (suggest_improvements), (4) simulates the top suggestion (simulate_change), and (5) returns the complete cycle: diagnosis + suggestions + the simulated impact of the best change. This is the 'one-click optimize' tool — call it at the end of a session to see what to improve next time. If you provide pillars in `text`, it skips the tokenpull step. If you omit `text`, it runs tokenpull first (requires local ccusage logs). Pure local math — no network, no submission. The `scope` parameter adds mode detection (BUILD/EDIT/DEBUG/MAINTAIN/IDLE) and scoped analysis: 'daily' (default — current behavior + mode), 'weekly' (compound into weekly snapshots + report artifact), 'trend' (30d/90d trajectory analysis).",
|
|
27
|
+
annotations: {
|
|
28
|
+
title: "Self-improve plan",
|
|
29
|
+
...ANNOTATIONS.readOnlyHint,
|
|
30
|
+
...ANNOTATIONS.idempotentHint,
|
|
31
|
+
...ANNOTATIONS.openWorldHint,
|
|
32
|
+
},
|
|
33
|
+
inputSchema: {
|
|
34
|
+
type: "object",
|
|
35
|
+
properties: {
|
|
36
|
+
text: {
|
|
37
|
+
type: "string",
|
|
38
|
+
description:
|
|
39
|
+
'Optional: token pillars — ccusage JSON or "input output cacheCreate cacheRead". If omitted, runs tokenpull to get current pillars from local logs.',
|
|
40
|
+
},
|
|
41
|
+
window: {
|
|
42
|
+
type: "string",
|
|
43
|
+
enum: ["7d", "30d", "90d", "all"],
|
|
44
|
+
description:
|
|
45
|
+
"Which time window to pull when running tokenpull (default: 30d). Ignored if `text` is provided.",
|
|
46
|
+
},
|
|
47
|
+
scope: {
|
|
48
|
+
type: "string",
|
|
49
|
+
enum: ["daily", "weekly", "trend"],
|
|
50
|
+
description:
|
|
51
|
+
'Analysis scope: "daily" (default — current behavior + mode detection), "weekly" (compound daily rows into weekly snapshots + report artifact with badges), "trend" (30d/90d trajectory + phase patterns). Daily modes never leave the machine — only weekly distribution goes in submitted reports.',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
outputSchema: {
|
|
56
|
+
type: "object",
|
|
57
|
+
properties: {
|
|
58
|
+
pillars: {
|
|
59
|
+
type: "object",
|
|
60
|
+
description: "The 4 raw token pillars used",
|
|
61
|
+
},
|
|
62
|
+
current_cascade: CASCADE_OUTPUT,
|
|
63
|
+
diagnosis: {
|
|
64
|
+
type: "array",
|
|
65
|
+
description: "Efficiency leaks found (from diagnose_cascade)",
|
|
66
|
+
},
|
|
67
|
+
suggestions: {
|
|
68
|
+
type: "array",
|
|
69
|
+
description: "Ranked improvements (from suggest_improvements)",
|
|
70
|
+
},
|
|
71
|
+
best_simulation: {
|
|
72
|
+
type: "object",
|
|
73
|
+
description: "Simulated result of the top suggestion",
|
|
74
|
+
},
|
|
75
|
+
cycle_summary: {
|
|
76
|
+
type: "string",
|
|
77
|
+
description: "One-line summary of the full cycle",
|
|
78
|
+
},
|
|
79
|
+
// Scope-specific fields
|
|
80
|
+
mode: {
|
|
81
|
+
type: "object",
|
|
82
|
+
description:
|
|
83
|
+
"Detected mode { mode, confidence } — present when scope is daily/weekly/trend",
|
|
84
|
+
},
|
|
85
|
+
quality_score: {
|
|
86
|
+
type: "number",
|
|
87
|
+
description: "Yield relative to mode expectation (daily scope)",
|
|
88
|
+
},
|
|
89
|
+
assessment: {
|
|
90
|
+
type: "string",
|
|
91
|
+
description: "One-line assessment for daily scope",
|
|
92
|
+
},
|
|
93
|
+
advice: {
|
|
94
|
+
type: "string",
|
|
95
|
+
description: "Advice for next session (daily scope)",
|
|
96
|
+
},
|
|
97
|
+
report: {
|
|
98
|
+
type: "object",
|
|
99
|
+
description: "Weekly report artifact (weekly scope)",
|
|
100
|
+
},
|
|
101
|
+
trend: { type: "object", description: "Trend analysis (trend scope)" },
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export async function handleSelfImprove(args, ctx) {
|
|
107
|
+
// The full self-improvement cycle: pull → diagnose → suggest → simulate.
|
|
108
|
+
// If text is provided, use it as pillars. If not, run tokenpull first.
|
|
109
|
+
let pillars;
|
|
110
|
+
let pulledFrom = "provided";
|
|
111
|
+
|
|
112
|
+
if (args?.text) {
|
|
113
|
+
if (typeof args.text === "string" && args.text.length > MAX_INPUT) {
|
|
114
|
+
return {
|
|
115
|
+
status: "error",
|
|
116
|
+
reason: "input_too_large",
|
|
117
|
+
detail: `text exceeds ${MAX_INPUT} chars.`,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
pillars = parsePillars(args.text);
|
|
121
|
+
} else {
|
|
122
|
+
// Run tokenpull to get current pillars from local logs
|
|
123
|
+
const windowType = args?.window || "30d";
|
|
124
|
+
try {
|
|
125
|
+
const pullResult = await ctx.callTool(
|
|
126
|
+
"tokenpull",
|
|
127
|
+
{ window: windowType },
|
|
128
|
+
);
|
|
129
|
+
if (pullResult?.status === "error" || !pullResult?.windows?.length) {
|
|
130
|
+
return {
|
|
131
|
+
status: "error",
|
|
132
|
+
reason: "tokenpull_failed",
|
|
133
|
+
detail:
|
|
134
|
+
"Could not pull pillars from local logs. Provide pillars via `text` argument instead.",
|
|
135
|
+
pull_result: pullResult,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
// Use the first window's pillars
|
|
139
|
+
const w = pullResult.windows[0];
|
|
140
|
+
pillars = w.pillars;
|
|
141
|
+
pulledFrom = `tokenpull ${w.window || windowType}`;
|
|
142
|
+
} catch (e) {
|
|
143
|
+
return {
|
|
144
|
+
status: "error",
|
|
145
|
+
reason: "tokenpull_error",
|
|
146
|
+
detail: String(e.message || e),
|
|
147
|
+
hint: "Provide pillars via `text` argument instead.",
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const currentResult = withParseWarnings(pillars, cascade(pillars));
|
|
153
|
+
const { input: i, output: o, cacheCreate: cw, cacheRead: cr } = pillars;
|
|
154
|
+
|
|
155
|
+
// ── Step 1: Diagnose ──────────────────────────────────────────────
|
|
156
|
+
const diagnosis = [];
|
|
157
|
+
const leverage = currentResult.leverage;
|
|
158
|
+
if (leverage !== null && leverage < 10) {
|
|
159
|
+
diagnosis.push({
|
|
160
|
+
metric: "cache_leverage",
|
|
161
|
+
severity: "critical",
|
|
162
|
+
finding: `Cache leverage is ${leverage}× — top-tier operators hit 200×+.`,
|
|
163
|
+
recommendation:
|
|
164
|
+
"Increase context reuse: load prior session context, use longer threads.",
|
|
165
|
+
});
|
|
166
|
+
} else if (leverage !== null && leverage < 50) {
|
|
167
|
+
diagnosis.push({
|
|
168
|
+
metric: "cache_leverage",
|
|
169
|
+
severity: "warning",
|
|
170
|
+
finding: `Cache leverage is ${leverage}× — below ARCH+ threshold (~100×+).`,
|
|
171
|
+
recommendation: "Push cache reads higher by reusing prior context.",
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const velocity = currentResult.velocity;
|
|
176
|
+
if (velocity !== null && velocity < 0.5) {
|
|
177
|
+
diagnosis.push({
|
|
178
|
+
metric: "velocity",
|
|
179
|
+
severity: "critical",
|
|
180
|
+
finding: `Velocity is ${velocity} — generating only ${velocity}× input as output.`,
|
|
181
|
+
recommendation: "Increase output per turn, reduce over-reading.",
|
|
182
|
+
});
|
|
183
|
+
} else if (velocity !== null && velocity < 1.0) {
|
|
184
|
+
diagnosis.push({
|
|
185
|
+
metric: "velocity",
|
|
186
|
+
severity: "warning",
|
|
187
|
+
finding: `Velocity is ${velocity} — below 1.0. Healthy operators hit 1.5×+.`,
|
|
188
|
+
recommendation: "Generate more output per input token.",
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (currentResult.dev10x === null && cw === 0) {
|
|
193
|
+
diagnosis.push({
|
|
194
|
+
metric: "10xdev",
|
|
195
|
+
severity: "critical",
|
|
196
|
+
finding: "No cache creation — cascade cannot compound.",
|
|
197
|
+
recommendation:
|
|
198
|
+
"Switch to a platform with prompt caching (Claude Code).",
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const total = i + o + cw + cr;
|
|
203
|
+
if (total > 0 && (i / total) * 100 > 10) {
|
|
204
|
+
diagnosis.push({
|
|
205
|
+
metric: "input_bloat",
|
|
206
|
+
severity: "warning",
|
|
207
|
+
finding: `Fresh input is ${((i / total) * 100).toFixed(1)}% of total flow — efficient operators keep it under 1%.`,
|
|
208
|
+
recommendation: "Reduce fresh input by reusing prior context.",
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ── Step 2: Suggest (generate + simulate candidates) ──────────────
|
|
213
|
+
const candidates = [];
|
|
214
|
+
// If cacheRead is 0, suggest absolute amounts instead of percentage boosts
|
|
215
|
+
const crBoosts = cr > 0 ? [1.5, 2, 3, 5] : [];
|
|
216
|
+
for (const mult of crBoosts) {
|
|
217
|
+
const sim = cascade({ ...pillars, cacheRead: Math.round(cr * mult) });
|
|
218
|
+
if (sim.yield !== null)
|
|
219
|
+
candidates.push({
|
|
220
|
+
action: `Increase cache reads by ${Math.round((mult - 1) * 100)}%`,
|
|
221
|
+
pillar: "cacheRead",
|
|
222
|
+
delta: `+${Math.round(cr * (mult - 1)).toLocaleString()}`,
|
|
223
|
+
simulated_yield: sim.yield,
|
|
224
|
+
yield_delta: Number(
|
|
225
|
+
(sim.yield - (currentResult.yield ?? 0)).toFixed(2),
|
|
226
|
+
),
|
|
227
|
+
class_after: sim.class,
|
|
228
|
+
rationale: "Cache reads are the strongest Υ multiplier.",
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
// For zero-cache operators, suggest enabling caching with a starter amount
|
|
232
|
+
if (cr === 0 && cw === 0) {
|
|
233
|
+
const starterAmounts = [
|
|
234
|
+
Math.round(i * 10),
|
|
235
|
+
Math.round(i * 50),
|
|
236
|
+
Math.round(i * 100),
|
|
237
|
+
];
|
|
238
|
+
for (const amt of starterAmounts) {
|
|
239
|
+
const sim = cascade({
|
|
240
|
+
...pillars,
|
|
241
|
+
cacheCreate: Math.round(amt * 0.5),
|
|
242
|
+
cacheRead: amt,
|
|
243
|
+
});
|
|
244
|
+
if (sim.yield !== null && sim.yield > 0)
|
|
245
|
+
candidates.push({
|
|
246
|
+
action: `Enable caching with ${amt.toLocaleString()} cache reads`,
|
|
247
|
+
pillar: "cacheRead",
|
|
248
|
+
delta: `+${amt.toLocaleString()}`,
|
|
249
|
+
simulated_yield: sim.yield,
|
|
250
|
+
yield_delta: Number(
|
|
251
|
+
(sim.yield - (currentResult.yield ?? 0)).toFixed(2),
|
|
252
|
+
),
|
|
253
|
+
class_after: sim.class,
|
|
254
|
+
rationale:
|
|
255
|
+
"You have no cache — enabling it unlocks the cascade. Start by reusing prior context.",
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
for (const mult of [0.9, 0.75, 0.5]) {
|
|
260
|
+
const newInput = Math.round(i * mult);
|
|
261
|
+
if (newInput < 1) continue;
|
|
262
|
+
const sim = cascade({ ...pillars, input: newInput });
|
|
263
|
+
if (sim.yield !== null)
|
|
264
|
+
candidates.push({
|
|
265
|
+
action: `Reduce fresh input by ${Math.round((1 - mult) * 100)}%`,
|
|
266
|
+
pillar: "input",
|
|
267
|
+
delta: `-${Math.round(i * (1 - mult)).toLocaleString()}`,
|
|
268
|
+
simulated_yield: sim.yield,
|
|
269
|
+
yield_delta: Number(
|
|
270
|
+
(sim.yield - (currentResult.yield ?? 0)).toFixed(2),
|
|
271
|
+
),
|
|
272
|
+
class_after: sim.class,
|
|
273
|
+
rationale: "Input is squared in Υ denominator — quadratic payoff.",
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
for (const mult of [1.25, 1.5, 2]) {
|
|
277
|
+
const sim = cascade({ ...pillars, output: Math.round(o * mult) });
|
|
278
|
+
if (sim.yield !== null)
|
|
279
|
+
candidates.push({
|
|
280
|
+
action: `Increase output by ${Math.round((mult - 1) * 100)}%`,
|
|
281
|
+
pillar: "output",
|
|
282
|
+
delta: `+${Math.round(o * (mult - 1)).toLocaleString()}`,
|
|
283
|
+
simulated_yield: sim.yield,
|
|
284
|
+
yield_delta: Number(
|
|
285
|
+
(sim.yield - (currentResult.yield ?? 0)).toFixed(2),
|
|
286
|
+
),
|
|
287
|
+
class_after: sim.class,
|
|
288
|
+
rationale: "Output is a linear multiplier in Υ.",
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
candidates.sort((a, b) => b.yield_delta - a.yield_delta);
|
|
292
|
+
const suggestions = candidates
|
|
293
|
+
.slice(0, 8)
|
|
294
|
+
.map((c, idx) => ({ rank: idx + 1, ...c }));
|
|
295
|
+
|
|
296
|
+
// ── Step 3: Simulate the top suggestion ───────────────────────────
|
|
297
|
+
const best = suggestions[0];
|
|
298
|
+
let bestSimulation = null;
|
|
299
|
+
if (best) {
|
|
300
|
+
const simPillars = { ...pillars };
|
|
301
|
+
const delta = parseInt(best.delta.replace(/[+\-,]/g, ""), 10);
|
|
302
|
+
if (best.pillar === "cacheRead") simPillars.cacheRead = cr + delta;
|
|
303
|
+
else if (best.pillar === "input")
|
|
304
|
+
simPillars.input = Math.max(i - delta, 1);
|
|
305
|
+
else if (best.pillar === "output") simPillars.output = o + delta;
|
|
306
|
+
const simResult = cascade(simPillars);
|
|
307
|
+
bestSimulation = {
|
|
308
|
+
action: best.action,
|
|
309
|
+
current_yield: currentResult.yield,
|
|
310
|
+
simulated_yield: simResult.yield,
|
|
311
|
+
yield_delta: best.yield_delta,
|
|
312
|
+
current_class: currentResult.class,
|
|
313
|
+
simulated_class: simResult.class,
|
|
314
|
+
class_changed: currentResult.class !== simResult.class,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ── Step 4: Cycle summary ─────────────────────────────────────────
|
|
319
|
+
const criticalCount = diagnosis.filter(
|
|
320
|
+
(d) => d.severity === "critical",
|
|
321
|
+
).length;
|
|
322
|
+
const cycleSummary = best
|
|
323
|
+
? `Pulled from ${pulledFrom}. Υ ${currentResult.yield} (${currentResult.class}). ${criticalCount} critical, ${diagnosis.length - criticalCount} other findings. Best: ${best.action} → Υ ${best.simulated_yield} (+${best.yield_delta}).`
|
|
324
|
+
: `Pulled from ${pulledFrom}. Υ ${currentResult.yield} (${currentResult.class}). ${diagnosis.length} findings. No improvements suggested — cascade is optimized.`;
|
|
325
|
+
|
|
326
|
+
// ── Step 5: Scope-specific analysis ───────────────────────────────
|
|
327
|
+
const scope = args?.scope || "daily";
|
|
328
|
+
const modeInfo = currentResult.mode; // { mode, confidence }
|
|
329
|
+
const scopeResult = {};
|
|
330
|
+
|
|
331
|
+
if (scope === "daily") {
|
|
332
|
+
// Daily: mode + quality score + assessment + advice
|
|
333
|
+
const qs = qualityScore(currentResult.yield ?? 0, modeInfo.mode);
|
|
334
|
+
const expected = MODE_EXPECTED_YIELD[modeInfo.mode] ?? 0;
|
|
335
|
+
const assessment = _dailyAssessment(
|
|
336
|
+
modeInfo.mode,
|
|
337
|
+
currentResult.yield,
|
|
338
|
+
qs,
|
|
339
|
+
expected,
|
|
340
|
+
);
|
|
341
|
+
const advice = _dailyAdvice(modeInfo.mode, currentResult.yield, qs);
|
|
342
|
+
scopeResult.mode = modeInfo;
|
|
343
|
+
scopeResult.quality_score = Math.round(qs * 100) / 100;
|
|
344
|
+
scopeResult.assessment = assessment;
|
|
345
|
+
scopeResult.advice = advice;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (scope === "weekly" || scope === "trend") {
|
|
349
|
+
// Pull daily rows from ccusage to build weekly snapshots
|
|
350
|
+
const dailyRows = await _pullDailyRows(ctx.opts);
|
|
351
|
+
const weeklySnapshots = _compoundWeekly(dailyRows);
|
|
352
|
+
const modeDistribution = _modeDistribution(weeklySnapshots);
|
|
353
|
+
const modeWeightedYield = _modeWeightedYield(weeklySnapshots);
|
|
354
|
+
|
|
355
|
+
// Compute badges
|
|
356
|
+
const historyForBadges = weeklySnapshots.map((w) => ({
|
|
357
|
+
date: w.weekStart,
|
|
358
|
+
mode: w.mode,
|
|
359
|
+
yield: w.yield,
|
|
360
|
+
pillars: w.pillars,
|
|
361
|
+
}));
|
|
362
|
+
const badges = computeBadges({
|
|
363
|
+
pillars,
|
|
364
|
+
cascade: currentResult,
|
|
365
|
+
history: historyForBadges,
|
|
366
|
+
isVerified: false, // set by submit_verified
|
|
367
|
+
rank: null, // server-side
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
if (scope === "weekly") {
|
|
371
|
+
scopeResult.mode = modeInfo;
|
|
372
|
+
// Strip dailyModes from weekly_snapshots — privacy boundary (daily modes never leave the machine)
|
|
373
|
+
const safeSnapshots = weeklySnapshots.map(
|
|
374
|
+
({ dailyModes, ...rest }) => rest,
|
|
375
|
+
);
|
|
376
|
+
scopeResult.report = {
|
|
377
|
+
current_mode: modeInfo.mode,
|
|
378
|
+
mode_confidence: modeInfo.confidence,
|
|
379
|
+
mode_distribution: modeDistribution,
|
|
380
|
+
mode_weighted_yield: modeWeightedYield,
|
|
381
|
+
peak_yield: _peakYield(weeklySnapshots),
|
|
382
|
+
health_score: _healthScore(weeklySnapshots, modeWeightedYield),
|
|
383
|
+
weekly_snapshots: safeSnapshots,
|
|
384
|
+
badges,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (scope === "trend") {
|
|
389
|
+
const yield7d = _yieldForDays(weeklySnapshots, 7);
|
|
390
|
+
const yield30d = _yieldForDays(weeklySnapshots, 30);
|
|
391
|
+
const yield90d = _yieldForDays(weeklySnapshots, 90);
|
|
392
|
+
const trajectory = _trajectory(yield7d, yield30d, yield90d);
|
|
393
|
+
scopeResult.mode = modeInfo;
|
|
394
|
+
scopeResult.trend = {
|
|
395
|
+
yield_7d: yield7d,
|
|
396
|
+
yield_30d: yield30d,
|
|
397
|
+
yield_90d: yield90d,
|
|
398
|
+
trajectory,
|
|
399
|
+
mode_distribution: modeDistribution,
|
|
400
|
+
phase_pattern: _phasePattern(weeklySnapshots),
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return {
|
|
406
|
+
pillars,
|
|
407
|
+
current_cascade: {
|
|
408
|
+
yield_: currentResult.yield,
|
|
409
|
+
snr: currentResult.snr,
|
|
410
|
+
leverage: currentResult.leverage,
|
|
411
|
+
velocity: currentResult.velocity,
|
|
412
|
+
tenx_dev: currentResult.dev10x,
|
|
413
|
+
class: currentResult.class,
|
|
414
|
+
mode: modeInfo,
|
|
415
|
+
warnings: currentResult.warnings,
|
|
416
|
+
},
|
|
417
|
+
diagnosis,
|
|
418
|
+
suggestions,
|
|
419
|
+
best_simulation: bestSimulation,
|
|
420
|
+
cycle_summary: cycleSummary,
|
|
421
|
+
...scopeResult,
|
|
422
|
+
};
|
|
423
|
+
}
|