sigrank 0.0.222 → 0.0.223

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/manifest.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://raw.githubusercontent.com/anthropics/mcpb/main/schemas/mcpb-manifest-v0.4.schema.json",
3
3
  "manifest_version": "0.4",
4
4
  "name": "sigrank",
5
- "version": "0.0.221",
5
+ "version": "0.0.222",
6
6
  "description": "Ranks AI coding operators by token cascade efficiency (Yield = cache_read \u00d7 output / input\u00b2). 23 tools for yield calculation, local token usage pulls, signed submission, and tokscale-powered analytics (market share, developer profiles, model trends, cost analysis, device profiles, MCP usage, competitive intelligence). Privacy-first: runs locally, only four token counts leave the machine.",
7
7
  "author": {
8
8
  "name": "SunrisesIllNeverSee",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigrank",
3
- "version": "0.0.222",
3
+ "version": "0.0.223",
4
4
  "author": "SunrisesIllNeverSee (https://github.com/SunrisesIllNeverSee)",
5
5
  "description": "SigRank MCP server — AI operator evaluation and token cascade efficiency tools. The yield cascade + live leaderboard as MCP tools any agent can call.",
6
6
  "mcpName": "io.github.SunrisesIllNeverSee/sigrank-mcp",
@@ -0,0 +1,361 @@
1
+ /**
2
+ * tools/_peer-matching.mjs — pure peer-discovery logic for discover_peers.
3
+ *
4
+ * Shared between _02_sigrank-mcp (operator's agent, uses enrolled identity)
5
+ * and _04_bestuser-router-mcp (marketing layer, takes codename arg). The
6
+ * canonical version lives here; _04 vendors a copy.
7
+ *
8
+ * All functions are pure — no network, no side effects. They operate on
9
+ * leaderboard entry objects as returned by the signalaf.com API:
10
+ * { codename, class_tier, platform, yield_, leverage, velocity, snr,
11
+ * input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens,
12
+ * rank, ... }
13
+ *
14
+ * Three categorization functions:
15
+ * findMentors — operators 1-2 class tiers above, ranked by cascade shape similarity
16
+ * findPeers — operators in the same class tier, ranked by yield proximity
17
+ * findComplementary — operators whose strength maps to the operator's weakness
18
+ *
19
+ * Plus pillar delta computation: the specific token difference that explains
20
+ * the yield gap between an operator and a mentor.
21
+ */
22
+
23
+ import { CLASS_TIERS, UNCLASSED, tierOf } from "../analytics/cascade.mjs";
24
+
25
+ /**
26
+ * Class tier rank index (0 = ARCH+ top, 7 = IGNITER bottom, -1 = UNCLASSED).
27
+ * Used to determine who is "above" an operator in the experience ladder.
28
+ */
29
+ export function tierRank(klass) {
30
+ const base = tierOf(klass);
31
+ if (base === UNCLASSED || base == null) return -1;
32
+ return CLASS_TIERS.indexOf(base);
33
+ }
34
+
35
+ /**
36
+ * Normalize a cascade shape into a 3D vector for similarity comparison.
37
+ * The three dimensions are the cascade ratios that define an operator's
38
+ * "shape" independent of scale:
39
+ * - leverage (Cr/I): how much they reuse prior context
40
+ * - velocity (O/I): how much output they get per input token
41
+ * - snr (O/(I+O)): signal cleanliness
42
+ *
43
+ * Each is log-scaled and normalized to [0,1] so operators at different scales
44
+ * can be compared by shape, not absolute magnitude. An operator with 100x
45
+ * leverage and one with 1000x leverage have similar *shapes* — both are
46
+ * high-leverage operators, just at different scales.
47
+ */
48
+ export function cascadeShape(op) {
49
+ const l = Number(op.leverage) || 0;
50
+ const v = Number(op.velocity) || 0;
51
+ const s = Number(op.snr) || 0;
52
+ // Log-scale, clamp to [0,1]. Log thresholds chosen so typical ranges map well:
53
+ // leverage: 0-1000x → log1p(0)-log1p(1000) ≈ 0-6.9
54
+ // velocity: 0-10x → log1p(0)-log1p(10) ≈ 0-2.4
55
+ // snr: 0-1 → log1p(0)-log1p(1) ≈ 0-0.69
56
+ const norm = (val, max) => Math.min(1, Math.log1p(Math.max(0, val)) / Math.log1p(max));
57
+ return [
58
+ norm(l, 1000),
59
+ norm(v, 10),
60
+ norm(s, 1),
61
+ ];
62
+ }
63
+
64
+ /**
65
+ * Euclidean distance between two cascade shapes, normalized to [0,1].
66
+ * 0 = identical shape, 1 = maximally different. Used to rank mentors:
67
+ * closer shape = "they were where you are but scaled up."
68
+ */
69
+ export function shapeDistance(opA, opB) {
70
+ const a = cascadeShape(opA);
71
+ const b = cascadeShape(opB);
72
+ let sum = 0;
73
+ for (let i = 0; i < a.length; i++) {
74
+ const d = a[i] - b[i];
75
+ sum += d * d;
76
+ }
77
+ return Math.sqrt(sum / a.length);
78
+ }
79
+
80
+ /**
81
+ * Similarity score = 1 - distance. Higher = more similar cascade shape.
82
+ */
83
+ export function shapeSimilarity(opA, opB) {
84
+ return 1 - shapeDistance(opA, opB);
85
+ }
86
+
87
+ /**
88
+ * Compute the pillar delta between an operator and a mentor.
89
+ * Returns the specific token differences that explain the yield gap,
90
+ * expressed as both absolute tokens and multipliers.
91
+ *
92
+ * { pillar, mentor_value, operator_value, multiplier, absolute_delta, explanation }
93
+ *
94
+ * The pillar with the largest yield impact is listed first. Since yield =
95
+ * (cacheRead × output) / input², the impact ranking is:
96
+ * 1. cache_read (linear multiplier in yield)
97
+ * 2. input (quadratic denominator — reducing input has squared impact)
98
+ * 3. output (linear multiplier in yield)
99
+ * 4. cache_create (enables future cache reads — indirect)
100
+ */
101
+ export function pillarDelta(operator, mentor) {
102
+ const opI = Number(operator.input_tokens) || 0;
103
+ const opO = Number(operator.output_tokens) || 0;
104
+ const opCr = Number(operator.cache_read_tokens) || 0;
105
+ const opCw = Number(operator.cache_creation_tokens) || 0;
106
+
107
+ const mtI = Number(mentor.input_tokens) || 0;
108
+ const mtO = Number(mentor.output_tokens) || 0;
109
+ const mtCr = Number(mentor.cache_read_tokens) || 0;
110
+ const mtCw = Number(mentor.cache_creation_tokens) || 0;
111
+
112
+ const safeMult = (mt, op) => (op > 0 ? mt / op : mt > 0 ? Infinity : 1);
113
+ const fmtMult = (m) => (m === Infinity ? "∞" : `${m.toFixed(1)}×`);
114
+
115
+ const deltas = [
116
+ {
117
+ pillar: "cache_read",
118
+ mentor_value: mtCr,
119
+ operator_value: opCr,
120
+ multiplier: safeMult(mtCr, opCr),
121
+ absolute_delta: mtCr - opCr,
122
+ explanation:
123
+ opCr > 0
124
+ ? `${fmtMult(safeMult(mtCr, opCr))} your cache reads — the strongest yield multiplier`
125
+ : `You have no cache reads; mentor has ${mtCr.toLocaleString()} — this is the entire yield gap`,
126
+ },
127
+ {
128
+ pillar: "input",
129
+ mentor_value: mtI,
130
+ operator_value: opI,
131
+ multiplier: safeMult(mtI, opI),
132
+ absolute_delta: mtI - opI,
133
+ explanation:
134
+ mtI < opI
135
+ ? `${fmtMult(opI / mtI)} less input than you — input² is in the denominator, so leaner input has quadratic payoff`
136
+ : `${fmtMult(safeMult(mtI, opI))} your input — they use more fresh tokens but compensate with cache reuse`,
137
+ },
138
+ {
139
+ pillar: "output",
140
+ mentor_value: mtO,
141
+ operator_value: opO,
142
+ multiplier: safeMult(mtO, opO),
143
+ absolute_delta: mtO - opO,
144
+ explanation: `${fmtMult(safeMult(mtO, opO))} your output — linear yield multiplier`,
145
+ },
146
+ {
147
+ pillar: "cache_create",
148
+ mentor_value: mtCw,
149
+ operator_value: opCw,
150
+ multiplier: safeMult(mtCw, opCw),
151
+ absolute_delta: mtCw - opCw,
152
+ explanation:
153
+ opCw > 0
154
+ ? `${fmtMult(safeMult(mtCw, opCw))} your cache writes — enables future cache reads`
155
+ : `You have no cache writes; mentor has ${mtCw.toLocaleString()} — they're investing in future cache`,
156
+ },
157
+ ];
158
+
159
+ // Sort by yield impact: cache_read first (strongest), then input (quadratic),
160
+ // then output (linear), then cache_create (indirect).
161
+ const order = { cache_read: 0, input: 1, output: 2, cache_create: 3 };
162
+ deltas.sort((a, b) => order[a.pillar] - order[b.pillar]);
163
+
164
+ return deltas;
165
+ }
166
+
167
+ /**
168
+ * Find mentors: operators 1-2 class tiers above, ranked by cascade shape
169
+ * similarity. These are operators who were where you are and broke through.
170
+ *
171
+ * @param {object} operator — the operator's leaderboard entry
172
+ * @param {array} board — all leaderboard entries
173
+ * @param {object} opts — { platform: string|null, n: number }
174
+ * @returns {array} mentors sorted by similarity (descending)
175
+ */
176
+ export function findMentors(operator, board, opts = {}) {
177
+ const n = Math.min(20, Math.max(1, opts.n ?? 5));
178
+ const myTier = tierRank(operator.class_tier || operator.class);
179
+ if (myTier < 0) return []; // UNCLASSED — no mentors
180
+
181
+ const myPlatform = operator.platform;
182
+
183
+ return board
184
+ .filter((op) => op.codename !== operator.codename)
185
+ .filter((op) => {
186
+ if (opts.platform && op.platform !== opts.platform) return false;
187
+ if (!opts.platform && myPlatform && op.platform !== myPlatform) return false;
188
+ return true;
189
+ })
190
+ .filter((op) => {
191
+ const theirTier = tierRank(op.class_tier || op.class);
192
+ // 1-2 tiers above (lower index = higher tier)
193
+ return theirTier >= 0 && theirTier < myTier && theirTier >= myTier - 2;
194
+ })
195
+ .map((op) => ({
196
+ ...op,
197
+ similarity_score: Math.round(shapeSimilarity(operator, op) * 100) / 100,
198
+ pillar_delta: pillarDelta(operator, op),
199
+ }))
200
+ .sort((a, b) => b.similarity_score - a.similarity_score)
201
+ .slice(0, n);
202
+ }
203
+
204
+ /**
205
+ * Find peers: operators in the same class tier (same base tier, any sub-stage),
206
+ * ranked by yield proximity. These are your direct competitors.
207
+ *
208
+ * @param {object} operator
209
+ * @param {array} board
210
+ * @param {object} opts
211
+ * @returns {array} peers sorted by yield proximity (ascending delta)
212
+ */
213
+ export function findPeers(operator, board, opts = {}) {
214
+ const n = Math.min(20, Math.max(1, opts.n ?? 5));
215
+ const myTier = tierOf(operator.class_tier || operator.class);
216
+ if (myTier === UNCLASSED || myTier == null) return [];
217
+
218
+ const myPlatform = operator.platform;
219
+ const myYield = Number(operator.yield_) || 0;
220
+
221
+ return board
222
+ .filter((op) => op.codename !== operator.codename)
223
+ .filter((op) => {
224
+ if (opts.platform && op.platform !== opts.platform) return false;
225
+ if (!opts.platform && myPlatform && op.platform !== myPlatform) return false;
226
+ return true;
227
+ })
228
+ .filter((op) => tierOf(op.class_tier || op.class) === myTier)
229
+ .map((op) => ({
230
+ codename: op.codename,
231
+ class_tier: op.class_tier || op.class,
232
+ yield_: op.yield_,
233
+ leverage: op.leverage,
234
+ velocity: op.velocity,
235
+ platform: op.platform,
236
+ rank: op.rank,
237
+ yield_delta_from_you: Math.round((Number(op.yield_) || 0) - myYield),
238
+ shareable_url: op.codename
239
+ ? `${opts.apiBase || "https://signalaf.com"}/operator/${encodeURIComponent(op.codename)}`
240
+ : null,
241
+ }))
242
+ .sort((a, b) => Math.abs(a.yield_delta_from_you) - Math.abs(b.yield_delta_from_you))
243
+ .slice(0, n);
244
+ }
245
+
246
+ /**
247
+ * Find complementary operators: those whose strongest dimension is the
248
+ * operator's weakest. Surfaces operators whose strengths map to your gaps.
249
+ *
250
+ * Determines the operator's weakest cascade dimension (leverage, velocity, or
251
+ * snr) relative to the board, then finds operators who are strongest in that
252
+ * dimension.
253
+ *
254
+ * @param {object} operator
255
+ * @param {array} board
256
+ * @param {object} opts
257
+ * @returns {array} complementary operators with strength/weakness annotation
258
+ */
259
+ export function findComplementary(operator, board, opts = {}) {
260
+ const n = Math.min(20, Math.max(1, opts.n ?? 5));
261
+ const myPlatform = operator.platform;
262
+
263
+ const filtered = board
264
+ .filter((op) => op.codename !== operator.codename)
265
+ .filter((op) => {
266
+ if (opts.platform && op.platform !== opts.platform) return false;
267
+ if (!opts.platform && myPlatform && op.platform !== myPlatform) return false;
268
+ return true;
269
+ });
270
+
271
+ if (filtered.length === 0) return [];
272
+
273
+ // Compute board averages for each dimension
274
+ const avg = (key) =>
275
+ filtered.reduce((s, op) => s + (Number(op[key]) || 0), 0) / filtered.length;
276
+
277
+ const avgLeverage = avg("leverage");
278
+ const avgVelocity = avg("velocity");
279
+ const avgSnr = avg("snr");
280
+
281
+ // Find the operator's weakest dimension (furthest below average)
282
+ const myLeverage = Number(operator.leverage) || 0;
283
+ const myVelocity = Number(operator.velocity) || 0;
284
+ const mySnr = Number(operator.snr) || 0;
285
+
286
+ const weaknesses = [
287
+ { dimension: "leverage", value: myLeverage, avg: avgLeverage, gap: avgLeverage - myLeverage },
288
+ { dimension: "velocity", value: myVelocity, avg: avgVelocity, gap: avgVelocity - myVelocity },
289
+ { dimension: "snr", value: mySnr, avg: avgSnr, gap: avgSnr - mySnr },
290
+ ].sort((a, b) => b.gap - a.gap); // largest gap first
291
+
292
+ const weakest = weaknesses[0];
293
+
294
+ // If the operator is above average in all dimensions, find the dimension
295
+ // where they're closest to average (their "relatively weakest")
296
+ const targetDimension = weakest.gap > 0 ? weakest.dimension : weaknesses[0].dimension;
297
+
298
+ // Find operators strongest in that dimension
299
+ return filtered
300
+ .map((op) => ({
301
+ codename: op.codename,
302
+ class_tier: op.class_tier || op.class,
303
+ yield_: op.yield_,
304
+ platform: op.platform,
305
+ rank: op.rank,
306
+ strength_dimension: targetDimension,
307
+ strength_value: Number(op[targetDimension]) || 0,
308
+ your_weakness: {
309
+ dimension: targetDimension,
310
+ your_value: Number(operator[targetDimension]) || 0,
311
+ board_average: Math.round(weakest.avg * 100) / 100,
312
+ },
313
+ shareable_url: op.codename
314
+ ? `${opts.apiBase || "https://signalaf.com"}/operator/${encodeURIComponent(op.codename)}`
315
+ : null,
316
+ }))
317
+ .sort((a, b) => b.strength_value - a.strength_value)
318
+ .slice(0, n);
319
+ }
320
+
321
+ /**
322
+ * Generate the summary line for discover_peers output.
323
+ */
324
+ export function peerSummary(operator, mentors, peers, complementary) {
325
+ const klass = operator.class_tier || operator.class || UNCLASSED;
326
+ const platform = operator.platform || "all";
327
+ const parts = [];
328
+
329
+ parts.push(`You're ${klass} on ${platform}.`);
330
+
331
+ if (mentors.length > 0) {
332
+ const topMentor = mentors[0];
333
+ const topDelta = topMentor.pillar_delta[0];
334
+ parts.push(
335
+ `${mentors.length} mentor${mentors.length > 1 ? "s" : ""} found — operators whose cascade shape is closest to yours but 1-2 tiers higher.`,
336
+ );
337
+ if (topDelta) {
338
+ parts.push(`Key gap: ${topDelta.explanation.toLowerCase()}.`);
339
+ }
340
+ } else {
341
+ const myTier = tierRank(klass);
342
+ if (myTier === 0) {
343
+ parts.push("You're at the top tier (ARCH+) — you are the mentor.");
344
+ } else {
345
+ parts.push("No mentors found in the 1-2 tier range above you.");
346
+ }
347
+ }
348
+
349
+ if (peers.length > 0) {
350
+ parts.push(`${peers.length} direct peer${peers.length > 1 ? "s" : ""} in the ${tierOf(klass)} tier.`);
351
+ }
352
+
353
+ if (complementary.length > 0) {
354
+ const comp = complementary[0];
355
+ parts.push(
356
+ `Study ${comp.codename} for ${comp.strength_dimension} (${comp.strength_value.toFixed(1)} vs your ${(Number(operator[comp.strength_dimension]) || 0).toFixed(1)}).`,
357
+ );
358
+ }
359
+
360
+ return parts.join(" ");
361
+ }
@@ -0,0 +1,198 @@
1
+ /**
2
+ * tools/discover-peers.mjs — discover_peers tool.
3
+ *
4
+ * Finds mentors, peers, and complementary operators for YOUR operator on the
5
+ * SigRank leaderboard. Uses the enrolled device identity (ensureIdentity) so
6
+ * the operator's agent doesn't need to pass a codename — it knows who you are.
7
+ *
8
+ * This is the "make connections" piece of the agent workflow loop:
9
+ * enroll → submit_verified → discover_peers → self_improve → submit again
10
+ *
11
+ * Fetches the operator's profile + the full leaderboard from signalaf.com's
12
+ * public API, then runs pure client-side peer-matching logic:
13
+ * - Mentors: 1-2 class tiers above, ranked by cascade shape similarity
14
+ * - Peers: same class tier, ranked by yield proximity
15
+ * - Complementary: operators whose strength maps to your weakness
16
+ *
17
+ * For each mentor, computes the pillar delta — the specific token difference
18
+ * that explains the yield gap (e.g. "12× your cache reads").
19
+ */
20
+
21
+ import { ensureIdentity } from "../identity/keystore.mjs";
22
+ import { ANNOTATIONS } from "./_schemas.mjs";
23
+ import { DEFAULT_API_BASE } from "./_helpers.mjs";
24
+ import {
25
+ findMentors,
26
+ findPeers,
27
+ findComplementary,
28
+ peerSummary,
29
+ tierRank,
30
+ } from "./_peer-matching.mjs";
31
+ import { LEADERBOARD_METRIC } from "../lib/constants.mjs";
32
+
33
+ export const TOOL_DEF = {
34
+ name: "discover_peers",
35
+ description:
36
+ "Discovers mentors, peers, and complementary operators for YOUR operator on the SigRank leaderboard. Uses your enrolled device identity — no codename needed. Finds operators you should learn from: (1) Mentors — 1-2 class tiers above you with similar cascade shapes, including the specific pillar delta that explains the yield gap (e.g. '12× your cache reads'). (2) Peers — same class tier, ranked by yield proximity. (3) Complementary — operators whose strength is your weakness. Use this after submit_verified to find who to learn from, then chain into self_improve with the mentor's pillar deltas as context. Requires enrollment (npx sigrank-mcp enroll). Intent: DISCOVER_PEERS.",
37
+ annotations: {
38
+ title: "Discover peers and mentors",
39
+ ...ANNOTATIONS.readOnlyHint,
40
+ ...ANNOTATIONS.openWorldHint,
41
+ },
42
+ inputSchema: {
43
+ type: "object",
44
+ properties: {
45
+ platform: {
46
+ type: "string",
47
+ description:
48
+ "Filter peers by platform (default: your operator's primary platform). Use 'all' to search across all platforms.",
49
+ },
50
+ n: {
51
+ type: "integer",
52
+ description:
53
+ "Number of operators to return per category (default: 5, max: 20).",
54
+ minimum: 1,
55
+ maximum: 20,
56
+ },
57
+ },
58
+ description:
59
+ "Optional: platform filter and result count. Uses your enrolled identity to determine your operator.",
60
+ },
61
+ outputSchema: {
62
+ type: "object",
63
+ properties: {
64
+ your_profile: {
65
+ type: "object",
66
+ description: "Your operator's current profile on the board",
67
+ properties: {
68
+ codename: { type: "string" },
69
+ class_tier: { type: "string" },
70
+ platform: { type: "string" },
71
+ yield_: { type: "number" },
72
+ leverage: { type: "number" },
73
+ velocity: { type: "number" },
74
+ rank: { type: "integer" },
75
+ },
76
+ },
77
+ mentors: {
78
+ type: "array",
79
+ description:
80
+ "Operators 1-2 class tiers above you with similar cascade shapes, including pillar deltas",
81
+ },
82
+ peers: {
83
+ type: "array",
84
+ description: "Operators in your class tier, ranked by yield proximity",
85
+ },
86
+ complementary: {
87
+ type: "array",
88
+ description: "Operators whose strength maps to your weakness",
89
+ },
90
+ summary: { type: "string", description: "One-line summary of findings" },
91
+ cta: { type: "string" },
92
+ },
93
+ },
94
+ };
95
+
96
+ export async function handleDiscoverPeers(args, ctx) {
97
+ const id = ctx.opts.identity || ensureIdentity();
98
+ if (!id.codename) {
99
+ return {
100
+ status: "not_enrolled",
101
+ detail:
102
+ "Run `npx sigrank-mcp enroll` to bind this device first, then submit_verified to appear on the board.",
103
+ };
104
+ }
105
+
106
+ const apiBase = ctx.apiBase || DEFAULT_API_BASE;
107
+ const n = Math.min(20, Math.max(1, args?.n ?? 5));
108
+ const platformFilter = args?.platform === "all" ? null : args?.platform;
109
+
110
+ // Fetch the operator's profile and the full leaderboard in parallel.
111
+ const [profile, board] = await Promise.all([
112
+ ctx.fetchJson(`/api/v1/operators/${encodeURIComponent(id.codename)}`),
113
+ ctx.fetchJson(`/api/v1/leaderboard?metric=${LEADERBOARD_METRIC}&limit=2000`),
114
+ ]);
115
+
116
+ // Find the operator's entry in the board (has raw token pillars + class_tier).
117
+ const entries = board.entries || board.operators || board || [];
118
+ const boardEntry = entries.find(
119
+ (op) => op.codename?.toLowerCase() === id.codename?.toLowerCase(),
120
+ );
121
+
122
+ if (!boardEntry) {
123
+ return {
124
+ status: "not_on_board",
125
+ codename: id.codename,
126
+ detail:
127
+ "You're enrolled but not on the leaderboard yet. Run submit_verified to publish your snapshots.",
128
+ profile,
129
+ };
130
+ }
131
+
132
+ // Merge profile + board entry (board entry has raw pillars, profile has
133
+ // per-window breakdowns). Use board entry as the primary source since it
134
+ // has the fields peer-matching needs.
135
+ const operator = {
136
+ ...boardEntry,
137
+ ...profile,
138
+ // Ensure board entry fields take precedence for peer-matching
139
+ class_tier: boardEntry.class_tier || boardEntry.class || profile.class_tier,
140
+ yield_: boardEntry.yield_ ?? profile.yield_,
141
+ leverage: boardEntry.leverage ?? profile.leverage,
142
+ velocity: boardEntry.velocity ?? profile.velocity,
143
+ snr: boardEntry.snr ?? profile.snr,
144
+ platform: boardEntry.platform || profile.platform,
145
+ };
146
+
147
+ const myTier = tierRank(operator.class_tier);
148
+ if (myTier < 0) {
149
+ // UNCLASSED — no class to match on. Return complementary only.
150
+ const complementary = findComplementary(operator, entries, { n, apiBase, platform: platformFilter });
151
+ return {
152
+ your_profile: {
153
+ codename: operator.codename,
154
+ class_tier: operator.class_tier,
155
+ platform: operator.platform,
156
+ yield_: operator.yield_,
157
+ leverage: operator.leverage,
158
+ velocity: operator.velocity,
159
+ rank: operator.rank,
160
+ },
161
+ mentors: [],
162
+ peers: [],
163
+ complementary,
164
+ summary: `You're UNCLASSED — no class tier to match peers on. ${complementary.length} complementary operators found based on cascade dimensions. Submit more snapshots to get classified.`,
165
+ cta: "Submit snapshots",
166
+ };
167
+ }
168
+
169
+ const opts = { n, apiBase, platform: platformFilter };
170
+ const mentors = findMentors(operator, entries, opts);
171
+ const peers = findPeers(operator, entries, opts);
172
+ const complementary = findComplementary(operator, entries, opts);
173
+
174
+ // Add shareable URLs to mentors
175
+ const mentorsWithUrls = mentors.map((m) => ({
176
+ ...m,
177
+ shareable_url: `${apiBase}/operator/${encodeURIComponent(m.codename)}`,
178
+ }));
179
+
180
+ const summary = peerSummary(operator, mentors, peers, complementary);
181
+
182
+ return {
183
+ your_profile: {
184
+ codename: operator.codename,
185
+ class_tier: operator.class_tier,
186
+ platform: operator.platform,
187
+ yield_: operator.yield_,
188
+ leverage: operator.leverage,
189
+ velocity: operator.velocity,
190
+ rank: operator.rank,
191
+ },
192
+ mentors: mentorsWithUrls,
193
+ peers,
194
+ complementary,
195
+ summary,
196
+ cta: "Study their cascade",
197
+ };
198
+ }
package/tools/index.mjs CHANGED
@@ -29,6 +29,7 @@ import { TOOL_DEF as simulateChangeTool, handleSimulateChange } from "./simulate
29
29
  import { TOOL_DEF as diagnoseCascadeTool, handleDiagnoseCascade } from "./diagnose-cascade.mjs";
30
30
  import { TOOL_DEF as suggestImprovementsTool, handleSuggestImprovements } from "./suggest-improvements.mjs";
31
31
  import { TOOL_DEF as selfImproveTool, handleSelfImprove } from "./self-improve.mjs";
32
+ import { TOOL_DEF as discoverPeersTool, handleDiscoverPeers } from "./discover-peers.mjs";
32
33
  // NOTE: get_best_operator, compare_self, compare_operators, describe_power_user,
33
34
  // and optimize_efficiency were moved to bestuser-router-mcp (the marketing layer).
34
35
  // They are duplicated there with richer params (metric sorting, platform filters,
@@ -61,6 +62,7 @@ export const TOOLS = [
61
62
  diagnoseCascadeTool,
62
63
  suggestImprovementsTool,
63
64
  selfImproveTool,
65
+ discoverPeersTool,
64
66
  // get_best_operator, compare_self, compare_operators, describe_power_user,
65
67
  // optimize_efficiency → moved to bestuser-router-mcp
66
68
  tokscaleBreakdownTool,
@@ -201,6 +203,8 @@ export async function callTool(name, args, opts = {}) {
201
203
  return handleSuggestImprovements(args);
202
204
  case "self_improve":
203
205
  return handleSelfImprove(args, ctx);
206
+ case "discover_peers":
207
+ return handleDiscoverPeers(args, ctx);
204
208
  // get_best_operator, compare_self, compare_operators, describe_power_user,
205
209
  // optimize_efficiency → moved to bestuser-router-mcp
206
210
  case "tokscale_breakdown":