sigrank 0.19.0 → 1.0.2
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 +89 -41
- package/adapters/index.mjs +1100 -0
- package/adapters/tokenpull.mjs +568 -0
- package/adapters.mjs +1 -865
- package/analytics/cascade.mjs +338 -0
- package/bin/sigrank.mjs +137 -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 +44 -18
- 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 +233 -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/standard-record.mjs +108 -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,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cascade.mjs — SigRank yield cascade facade.
|
|
3
|
+
*
|
|
4
|
+
* The canonical cascade math (Υ Yield, SNR, Leverage, Velocity, 10xDEV, and the
|
|
5
|
+
* 24-stage RS05 class taxonomy) now lives in the `@sigrank/cascade` package.
|
|
6
|
+
* This file re-exports the canonical functions and adds local-only helpers that
|
|
7
|
+
* are presentation/MCP-specific and intentionally NOT part of the canonical
|
|
8
|
+
* math package:
|
|
9
|
+
* - CLASS_TIERS, SIGNAL_CLASSES, UNCLASSED (display taxonomy)
|
|
10
|
+
* - tierOf(), stageOf() (display helpers)
|
|
11
|
+
* - detectMode(), MODE_EXPECTED_YIELD, qualityScore() (mode/quality layer)
|
|
12
|
+
* - parsePillars() (text → 4 pillars extractor)
|
|
13
|
+
*
|
|
14
|
+
* The cascade() wrapper preserves the object-signature
|
|
15
|
+
* ({ input, output, cacheCreate, cacheRead }) used throughout this repo and
|
|
16
|
+
* re-attaches the `mode` field (detectMode) that the canonical package
|
|
17
|
+
* intentionally omits (mode is a presentation-layer concern, not canonical
|
|
18
|
+
* math).
|
|
19
|
+
*
|
|
20
|
+
* Canonical reference: MO§ES Υ 18436.98 from (1251211, 11296121, 128196310, 2555179769).
|
|
21
|
+
*
|
|
22
|
+
* Degenerate-input policy (inherited from the canonical package):
|
|
23
|
+
* - Any pillar that collapses a denominator (i=0, o=0, cw=0, cr=0) returns null for
|
|
24
|
+
* the affected metrics rather than Infinity/NaN.
|
|
25
|
+
* - A `warnings[]` array is attached when any metric is null so callers can surface the
|
|
26
|
+
* reason without silently corrupting downstream calculations.
|
|
27
|
+
* - The cascade is NEVER thrown away — even partial results are useful for review/storage.
|
|
28
|
+
* Callers that require a fully-formed result should check `warnings.length === 0`.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
// ─── Canonical math (from @sigrank/cascade) ─────────────────────────────────
|
|
32
|
+
import {
|
|
33
|
+
cascade as cascadeCanonical,
|
|
34
|
+
round,
|
|
35
|
+
classify,
|
|
36
|
+
RS05_CLASS_THRESHOLDS,
|
|
37
|
+
fieldStats,
|
|
38
|
+
percentileOf,
|
|
39
|
+
rankOf,
|
|
40
|
+
operatorSignature,
|
|
41
|
+
evaluateOperator,
|
|
42
|
+
} from "@sigrank/cascade";
|
|
43
|
+
|
|
44
|
+
// Re-export the canonical functions so existing import paths keep working.
|
|
45
|
+
export {
|
|
46
|
+
round,
|
|
47
|
+
classify,
|
|
48
|
+
RS05_CLASS_THRESHOLDS,
|
|
49
|
+
fieldStats,
|
|
50
|
+
percentileOf,
|
|
51
|
+
rankOf,
|
|
52
|
+
operatorSignature,
|
|
53
|
+
evaluateOperator,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The four raw token pillars → the cascade.
|
|
58
|
+
*
|
|
59
|
+
* Object-signature wrapper around the canonical `@sigrank/cascade` cascade()
|
|
60
|
+
* (which takes positional args). Re-attaches the `mode` field via detectMode()
|
|
61
|
+
* so the 20+ call sites in this repo that read `result.mode` keep working.
|
|
62
|
+
*/
|
|
63
|
+
export function cascade({ input, output, cacheCreate, cacheRead }) {
|
|
64
|
+
const result = cascadeCanonical(input, output, cacheCreate, cacheRead);
|
|
65
|
+
// Re-attach mode — the canonical package intentionally omits it (mode is a
|
|
66
|
+
// presentation-layer concern, not canonical math).
|
|
67
|
+
result.mode = detectMode({ input, output, cacheCreate, cacheRead });
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ─── Local-only display taxonomy (not in @sigrank/cascade) ───────────────────
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* CLASS_TIERS — the 8 base tier names (K.01–K.08) for display: glyph, color,
|
|
75
|
+
* meaning. SINGLE SOURCE OF TRUTH for tier-level display info.
|
|
76
|
+
*
|
|
77
|
+
* The permanent class is an EXPERIENCE ladder keyed on TOTAL TOKENS. Each tier
|
|
78
|
+
* has 3 sub-stages (I/II/III) — 24 stages total. The 24 thresholds live in
|
|
79
|
+
* RS05_CLASS_THRESHOLDS (re-exported from @sigrank/cascade above). classify()
|
|
80
|
+
* returns the full sub-stage string (e.g. "REFINER II"). Use tierOf() to
|
|
81
|
+
* extract the base tier name.
|
|
82
|
+
*
|
|
83
|
+
* TRANSMITTER is NOT on this ladder — it is a temporary peak badge (RS.08).
|
|
84
|
+
* The client does not evaluate the badge (owner is still calibrating it);
|
|
85
|
+
* the server does it on read.
|
|
86
|
+
*
|
|
87
|
+
* Mirrors the server's canon-ids.ts CLASS_TIERS (display). Order is descending
|
|
88
|
+
* (ARCH+ → IGNITER).
|
|
89
|
+
*/
|
|
90
|
+
export const CLASS_TIERS = [
|
|
91
|
+
"ARCH+",
|
|
92
|
+
"ARCH",
|
|
93
|
+
"POWER",
|
|
94
|
+
"BASE",
|
|
95
|
+
"SEEKER",
|
|
96
|
+
"REFINER",
|
|
97
|
+
"BEARER",
|
|
98
|
+
"IGNITER",
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* SIGNAL_CLASSES — the full 24 sub-stage names (8 tiers × 3 sub-stages I/II/III).
|
|
103
|
+
* Mirrors the server's SIGNAL_CLASSES set in lib/board/mappers.ts. The API
|
|
104
|
+
* returns class_tier as one of these 24 values. classify() returns one of these
|
|
105
|
+
* (or UNCLASSED). Use this for schema enums and validation.
|
|
106
|
+
*/
|
|
107
|
+
export const SIGNAL_CLASSES = [
|
|
108
|
+
"ARCH+ I", "ARCH+ II", "ARCH+ III",
|
|
109
|
+
"ARCH I", "ARCH II", "ARCH III",
|
|
110
|
+
"POWER I", "POWER II", "POWER III",
|
|
111
|
+
"BASE I", "BASE II", "BASE III",
|
|
112
|
+
"SEEKER I", "SEEKER II", "SEEKER III",
|
|
113
|
+
"REFINER I", "REFINER II", "REFINER III",
|
|
114
|
+
"BEARER I", "BEARER II", "BEARER III",
|
|
115
|
+
"IGNITER I", "IGNITER II", "IGNITER III",
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
/** The degenerate-case class returned when totalTokens is null/undefined
|
|
119
|
+
* (all-zero / empty session). Distinct from IGNITER III (which is a real
|
|
120
|
+
* low-experience tier) so callers can tell "no data" from "bottom tier". */
|
|
121
|
+
export const UNCLASSED = "UNCLASSED";
|
|
122
|
+
|
|
123
|
+
/** Extract the base tier name from a sub-stage string (e.g. "ARCH+ I" → "ARCH+").
|
|
124
|
+
* Mirrors the server's tierOf() in components/sigrank/types.ts. Returns the
|
|
125
|
+
* input as-is if it's not a sub-stage string (e.g. UNCLASSED). */
|
|
126
|
+
export function tierOf(cls) {
|
|
127
|
+
if (cls === UNCLASSED || cls == null) return cls;
|
|
128
|
+
const parts = String(cls).split(" ");
|
|
129
|
+
if (parts.length >= 2 && ["I", "II", "III"].includes(parts[parts.length - 1])) {
|
|
130
|
+
return parts.slice(0, -1).join(" ");
|
|
131
|
+
}
|
|
132
|
+
return cls;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Extract the sub-stage from a sub-stage string (e.g. "ARCH+ I" → "I").
|
|
136
|
+
* Mirrors the server's stageOf(). Returns null for non-sub-stage strings. */
|
|
137
|
+
export function stageOf(cls) {
|
|
138
|
+
if (cls === UNCLASSED || cls == null) return null;
|
|
139
|
+
const parts = String(cls).split(" ");
|
|
140
|
+
const stage = parts[parts.length - 1];
|
|
141
|
+
return stage === "I" || stage === "II" || stage === "III" ? stage : null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ─── Mode / quality layer (not in @sigrank/cascade) ──────────────────────────
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* detectMode — classify an operator's current working mode from 4 token pillars.
|
|
148
|
+
*
|
|
149
|
+
* Pure ratio math, first-match-wins (same pattern as classify() — descending
|
|
150
|
+
* cuts preserve edge semantics). MAINTAIN checked first (high leverage is
|
|
151
|
+
* strongest signal), then DEBUG (low velocity is distinctive), then EDIT,
|
|
152
|
+
* then BUILD as fallback.
|
|
153
|
+
*
|
|
154
|
+
* Modes:
|
|
155
|
+
* BUILD — high input, low/zero cacheRead, output rising (greenfield)
|
|
156
|
+
* EDIT — high input, low cacheRead, high output (polishing)
|
|
157
|
+
* DEBUG — high input, low output, low cacheRead (investigating)
|
|
158
|
+
* MAINTAIN — low input, high cacheRead, high output (compounding)
|
|
159
|
+
* IDLE — near-zero tokens (not working)
|
|
160
|
+
*
|
|
161
|
+
* Returns { mode, confidence }.
|
|
162
|
+
*/
|
|
163
|
+
export function detectMode({ input, output, cacheCreate, cacheRead }) {
|
|
164
|
+
const i = Number(input),
|
|
165
|
+
o = Number(output),
|
|
166
|
+
cw = Number(cacheCreate),
|
|
167
|
+
cr = Number(cacheRead);
|
|
168
|
+
const total = i + o + cw + cr;
|
|
169
|
+
|
|
170
|
+
// IDLE: near-zero tokens
|
|
171
|
+
if (total < 1000) return { mode: "IDLE", confidence: 1.0 };
|
|
172
|
+
|
|
173
|
+
const leverage = i > 0 ? cr / i : 0;
|
|
174
|
+
const velocity = i > 0 ? o / i : 0;
|
|
175
|
+
const input_share = total > 0 ? i / total : 0;
|
|
176
|
+
|
|
177
|
+
// MAINTAIN: high leverage + high velocity (the cascade is compounding)
|
|
178
|
+
if (leverage > 10 && velocity > 1)
|
|
179
|
+
return { mode: "MAINTAIN", confidence: 0.9 };
|
|
180
|
+
if (leverage > 3 && velocity > 0.5)
|
|
181
|
+
return { mode: "MAINTAIN", confidence: 0.7 };
|
|
182
|
+
|
|
183
|
+
// DEBUG: low velocity + high input share (reading, not producing)
|
|
184
|
+
if (velocity < 0.3 && input_share > 0.5)
|
|
185
|
+
return { mode: "DEBUG", confidence: 0.8 };
|
|
186
|
+
|
|
187
|
+
// EDIT: high input share + high velocity (fresh input but producing)
|
|
188
|
+
if (input_share > 0.4 && velocity > 0.5)
|
|
189
|
+
return { mode: "EDIT", confidence: 0.7 };
|
|
190
|
+
|
|
191
|
+
// DEBUG (secondary): high input share + low velocity
|
|
192
|
+
if (input_share > 0.4 && velocity < 0.5)
|
|
193
|
+
return { mode: "DEBUG", confidence: 0.6 };
|
|
194
|
+
|
|
195
|
+
// BUILD: fallback (high input, no cache reuse yet)
|
|
196
|
+
return { mode: "BUILD", confidence: 0.6 };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Expected yield per mode — global defaults used before personal baselines
|
|
201
|
+
* exist (needs 7+ days of history). Used by the quality score computation.
|
|
202
|
+
*/
|
|
203
|
+
export const MODE_EXPECTED_YIELD = {
|
|
204
|
+
BUILD: 15,
|
|
205
|
+
EDIT: 45,
|
|
206
|
+
DEBUG: 10,
|
|
207
|
+
MAINTAIN: 5000,
|
|
208
|
+
IDLE: 0,
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* qualityScore — actual yield relative to mode expectation.
|
|
213
|
+
* Fixes the "debug is bad" problem: a DEBUG session at 80% quality is good.
|
|
214
|
+
* Returns a number 0+ (can exceed 1.0 if outperforming the expected yield).
|
|
215
|
+
*/
|
|
216
|
+
export function qualityScore(actualYield, mode) {
|
|
217
|
+
const expected = MODE_EXPECTED_YIELD[mode] ?? 1;
|
|
218
|
+
if (expected === 0) return actualYield === 0 ? 1.0 : 0;
|
|
219
|
+
return actualYield / expected;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ─── Text parsing (not in @sigrank/cascade) ──────────────────────────────────
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Extract the 4 pillars from pasted text: JSON object OR 4 whitespace numbers.
|
|
226
|
+
*
|
|
227
|
+
* Hardened parse policy:
|
|
228
|
+
* - JSON path: requires named keys and numeric values. Rejects strings/null.
|
|
229
|
+
* - Positional path: requires the input to be ONLY numeric tokens (whitespace/commas
|
|
230
|
+
* allowed as separators). If the text contains non-numeric words the positional
|
|
231
|
+
* extractor attaches a `_parseWarnings` flag so downstream can route it to a review
|
|
232
|
+
* channel instead of treating it as authoritative data.
|
|
233
|
+
* - Negative values are accepted (not thrown away) but flagged — could be valid in
|
|
234
|
+
* some edge-case accounting or a data error; the server is the authority on validity.
|
|
235
|
+
* - We NEVER silently corrupt: if we can't parse 4 pillars we throw. If we parse but
|
|
236
|
+
* something looks suspicious we surface it in `_parseWarnings` on the returned object.
|
|
237
|
+
*/
|
|
238
|
+
export function parsePillars(text) {
|
|
239
|
+
const t = String(text || "").trim();
|
|
240
|
+
const pw = []; // parse warnings to attach
|
|
241
|
+
|
|
242
|
+
// ── JSON path ──────────────────────────────────────────────────────────────
|
|
243
|
+
try {
|
|
244
|
+
const j = JSON.parse(t);
|
|
245
|
+
if (j && typeof j === "object" && !Array.isArray(j)) {
|
|
246
|
+
const g = (...keys) => {
|
|
247
|
+
for (const k of keys) if (j[k] != null) return j[k];
|
|
248
|
+
return null;
|
|
249
|
+
};
|
|
250
|
+
const input = g(
|
|
251
|
+
"input",
|
|
252
|
+
"tokens_input_fresh",
|
|
253
|
+
"inputTokens",
|
|
254
|
+
"input_tokens",
|
|
255
|
+
);
|
|
256
|
+
const output = g(
|
|
257
|
+
"output",
|
|
258
|
+
"tokens_output",
|
|
259
|
+
"outputTokens",
|
|
260
|
+
"output_tokens",
|
|
261
|
+
);
|
|
262
|
+
const cacheCreate = g(
|
|
263
|
+
"cacheCreate",
|
|
264
|
+
"tokens_cache_creation",
|
|
265
|
+
"cache_creation_tokens",
|
|
266
|
+
);
|
|
267
|
+
const cacheRead = g(
|
|
268
|
+
"cacheRead",
|
|
269
|
+
"tokens_cache_read",
|
|
270
|
+
"cache_read_tokens",
|
|
271
|
+
);
|
|
272
|
+
if ([input, output, cacheCreate, cacheRead].every((v) => v != null)) {
|
|
273
|
+
const pillars = {
|
|
274
|
+
input: Number(input),
|
|
275
|
+
output: Number(output),
|
|
276
|
+
cacheCreate: Number(cacheCreate),
|
|
277
|
+
cacheRead: Number(cacheRead),
|
|
278
|
+
};
|
|
279
|
+
if (
|
|
280
|
+
[
|
|
281
|
+
pillars.input,
|
|
282
|
+
pillars.output,
|
|
283
|
+
pillars.cacheCreate,
|
|
284
|
+
pillars.cacheRead,
|
|
285
|
+
].some((v) => !Number.isFinite(v))
|
|
286
|
+
)
|
|
287
|
+
throw new Error(
|
|
288
|
+
"Non-numeric pillar value in JSON (got string or non-finite number).",
|
|
289
|
+
);
|
|
290
|
+
if (
|
|
291
|
+
[
|
|
292
|
+
pillars.input,
|
|
293
|
+
pillars.output,
|
|
294
|
+
pillars.cacheCreate,
|
|
295
|
+
pillars.cacheRead,
|
|
296
|
+
].some((v) => v < 0)
|
|
297
|
+
)
|
|
298
|
+
pw.push(
|
|
299
|
+
"negative_pillar: one or more pillars is negative — may be a data error",
|
|
300
|
+
);
|
|
301
|
+
if (pw.length > 0) pillars._parseWarnings = pw;
|
|
302
|
+
return pillars;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
} catch (e) {
|
|
306
|
+
// JSON.parse syntax error — fall through to positional. Re-throw parse errors we raised ourselves.
|
|
307
|
+
if (e.message.startsWith("Non-numeric")) throw e;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// ── Positional path ────────────────────────────────────────────────────────
|
|
311
|
+
// Guard: if the text contains alphabetic words, the numeric extraction is unreliable.
|
|
312
|
+
// We still attempt it (don't throw away the data) but flag it for review.
|
|
313
|
+
if (/[a-zA-Z]/.test(t))
|
|
314
|
+
pw.push(
|
|
315
|
+
"positional_from_mixed_text: extracted numbers from text that contains alphabetic characters — verify these are the correct 4 pillars",
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
const nums = (t.match(/-?\d[\d,]*\.?\d*/g) || []).map((s) =>
|
|
319
|
+
Number(s.replace(/,/g, "")),
|
|
320
|
+
);
|
|
321
|
+
if (nums.length >= 4) {
|
|
322
|
+
const [input, output, cacheCreate, cacheRead] = nums;
|
|
323
|
+
if (nums.length > 4)
|
|
324
|
+
pw.push(
|
|
325
|
+
`positional_extra_numbers: found ${nums.length} numbers, using first 4 — inspect for positional order error`,
|
|
326
|
+
);
|
|
327
|
+
const pillars = { input, output, cacheCreate, cacheRead };
|
|
328
|
+
if ([input, output, cacheCreate, cacheRead].some((v) => v < 0))
|
|
329
|
+
pw.push(
|
|
330
|
+
"negative_pillar: one or more pillars is negative — may be a data error",
|
|
331
|
+
);
|
|
332
|
+
if (pw.length > 0) pillars._parseWarnings = pw;
|
|
333
|
+
return pillars;
|
|
334
|
+
}
|
|
335
|
+
throw new Error(
|
|
336
|
+
"Could not parse 4 token pillars (input, output, cacheCreate, cacheRead) from the input.",
|
|
337
|
+
);
|
|
338
|
+
}
|
package/bin/sigrank.mjs
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Thin command shim for SigRank Standard commands.
|
|
5
|
+
*
|
|
6
|
+
* All existing commands and MCP/TUI behavior are delegated to ../index.mjs.
|
|
7
|
+
* The shim exists so the standard commands can be added without coupling the
|
|
8
|
+
* large presentation CLI to the standard specification.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const args = process.argv.slice(2);
|
|
12
|
+
const cmd = args[0];
|
|
13
|
+
|
|
14
|
+
const SPEC = "sigrank/0.1-draft";
|
|
15
|
+
const STANDARD_URL = "https://signalaf.com/standard";
|
|
16
|
+
const SCHEMA_URL =
|
|
17
|
+
"https://signalaf.com/standard/sigrank-operator-record-v0.1.schema.json";
|
|
18
|
+
|
|
19
|
+
function flag(name, fallback = undefined) {
|
|
20
|
+
const eq = args.find((a) => a.startsWith(`--${name}=`));
|
|
21
|
+
if (eq) return eq.slice(name.length + 3);
|
|
22
|
+
const i = args.indexOf(`--${name}`);
|
|
23
|
+
if (i >= 0 && i + 1 < args.length && !args[i + 1].startsWith("--")) {
|
|
24
|
+
return args[i + 1];
|
|
25
|
+
}
|
|
26
|
+
return fallback;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function printStandard() {
|
|
30
|
+
const payload = {
|
|
31
|
+
spec: SPEC,
|
|
32
|
+
status: "proposed_open_standard",
|
|
33
|
+
scope: "human operation of generative AI systems",
|
|
34
|
+
telemetry: ["input", "output", "cache_write", "cache_read"],
|
|
35
|
+
metrics: {
|
|
36
|
+
yield: "(cache_read * output) / input^2",
|
|
37
|
+
leverage: "cache_read / input",
|
|
38
|
+
velocity: "output / input",
|
|
39
|
+
snr: "output / (input + output)",
|
|
40
|
+
dev10x: "log10(cache_read / input)",
|
|
41
|
+
},
|
|
42
|
+
privacy:
|
|
43
|
+
"Core metrics do not require prompt text, response text, source code, or repository contents.",
|
|
44
|
+
reference_math: "@sigrank/cascade",
|
|
45
|
+
reference_platform: "SignalAF",
|
|
46
|
+
standard_url: STANDARD_URL,
|
|
47
|
+
schema_url: SCHEMA_URL,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
if (args.includes("--json")) {
|
|
51
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
process.stdout.write(`SigRank Standard ${SPEC}\n`);
|
|
56
|
+
process.stdout.write(`The new standard in operator metrics.\n\n`);
|
|
57
|
+
process.stdout.write(`Scope: ${payload.scope}\n`);
|
|
58
|
+
process.stdout.write(`Primitives: input / output / cache_write / cache_read\n`);
|
|
59
|
+
process.stdout.write(`Core metrics: Yield / Leverage / Velocity / SNR / 10xDEV\n`);
|
|
60
|
+
process.stdout.write(`Reference math: @sigrank/cascade\n`);
|
|
61
|
+
process.stdout.write(`Reference platform: SignalAF\n`);
|
|
62
|
+
process.stdout.write(`Spec: ${STANDARD_URL}\n`);
|
|
63
|
+
process.stdout.write(`Schema: ${SCHEMA_URL}\n`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function exportStandardRecord() {
|
|
67
|
+
const { callTool } = await import("../tools.mjs");
|
|
68
|
+
|
|
69
|
+
const platform = flag("platform", "claude");
|
|
70
|
+
const window = flag("window", "30d");
|
|
71
|
+
|
|
72
|
+
const explicit = ["input", "output", "cache-write", "cache-read"].every(
|
|
73
|
+
(name) => flag(name) !== undefined,
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
let pillars;
|
|
77
|
+
let sourcePlatform = platform;
|
|
78
|
+
|
|
79
|
+
if (explicit) {
|
|
80
|
+
pillars = {
|
|
81
|
+
input: Number(flag("input")),
|
|
82
|
+
output: Number(flag("output")),
|
|
83
|
+
cache_write: Number(flag("cache-write")),
|
|
84
|
+
cache_read: Number(flag("cache-read")),
|
|
85
|
+
};
|
|
86
|
+
} else {
|
|
87
|
+
const pulled = await callTool("tokenpull", { platform });
|
|
88
|
+
const selected = (pulled?.windows || []).find(
|
|
89
|
+
(w) => w.window === window || (window === "all_time" && w.window === "all"),
|
|
90
|
+
);
|
|
91
|
+
if (!selected?.pillars) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`No ${window} telemetry found for ${platform}. Pass --platform/--window or explicit --input --output --cache-write --cache-read.`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
sourcePlatform = pulled.platform || platform;
|
|
97
|
+
pillars = {
|
|
98
|
+
input: selected.pillars.input,
|
|
99
|
+
output: selected.pillars.output,
|
|
100
|
+
cache_write: selected.pillars.cacheCreate,
|
|
101
|
+
cache_read: selected.pillars.cacheRead,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
for (const [key, value] of Object.entries(pillars)) {
|
|
106
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
107
|
+
throw new Error(`${key} must be a non-negative finite number.`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const record = await callTool("get_sigrank_standard_record", {
|
|
112
|
+
...pillars,
|
|
113
|
+
provider: flag("provider", "local"),
|
|
114
|
+
model: flag("model", "unknown"),
|
|
115
|
+
tool: flag("tool", sourcePlatform || "sigrank"),
|
|
116
|
+
timestamp: new Date().toISOString(),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
record.context = {
|
|
120
|
+
...(record.context || {}),
|
|
121
|
+
window,
|
|
122
|
+
source_platform: sourcePlatform,
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
process.stdout.write(`${JSON.stringify(record, null, 2)}\n`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (cmd === "standard") {
|
|
129
|
+
printStandard();
|
|
130
|
+
} else if (cmd === "export" && args.includes("--standard")) {
|
|
131
|
+
exportStandardRecord().catch((err) => {
|
|
132
|
+
process.stderr.write(`[sigrank] ${err?.message || err}\n`);
|
|
133
|
+
process.exitCode = 1;
|
|
134
|
+
});
|
|
135
|
+
} else {
|
|
136
|
+
await import("../index.mjs");
|
|
137
|
+
}
|