@wrongstack/cli 1.0.17 → 1.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/dist/{acp-INCNOH42.js → acp-AJWSKWE5.js} +18 -7
  2. package/dist/arg-parser.d.ts +1 -0
  3. package/dist/{auth-YXK3EDUL.js → auth-GRBZ7Y3D.js} +61 -15
  4. package/dist/auth-menu/direct.d.ts +1 -0
  5. package/dist/auth-menu/local.d.ts +2 -0
  6. package/dist/auth-menu/oauth-menu.d.ts +3 -1
  7. package/dist/auth-menu/panel-service.d.ts +20 -0
  8. package/dist/{bench-YBU6BVQP.js → bench-N5K7Z5O3.js} +2 -2
  9. package/dist/boot/config-backup-recovery.d.ts +2 -1
  10. package/dist/boot/dispatch-webui.d.ts +11 -0
  11. package/dist/boot/short-circuit-desktop.d.ts +1 -0
  12. package/dist/boot/tui-project-picker-callback.d.ts +1 -1
  13. package/dist/boot/tui-settings-adapter.d.ts +15 -2
  14. package/dist/{boot-auto-thin-MANHOYHE.js → boot-auto-thin-6Q56FY3F.js} +3 -2
  15. package/dist/{chunk-6WRKAACA.js → chunk-2BKFWIER.js} +4 -2
  16. package/dist/{chunk-JY52HWL6.js → chunk-2EBVTYJR.js} +2 -2
  17. package/dist/chunk-3OCUS3VN.js +46 -0
  18. package/dist/chunk-424ORSE7.js +56 -0
  19. package/dist/{chunk-KEPABQYT.js → chunk-5SEYGOCH.js} +8 -82
  20. package/dist/chunk-BBHWW6LR.js +549 -0
  21. package/dist/{chunk-CJTBLTS4.js → chunk-BLD5DSVA.js} +4 -3
  22. package/dist/{chunk-K5PAL6KR.js → chunk-DRM7PSW7.js} +75 -29
  23. package/dist/{chunk-XJ7K67PP.js → chunk-JF2MULWF.js} +17 -10
  24. package/dist/{chunk-SIS37L3W.js → chunk-L2RVEQWO.js} +211 -193
  25. package/dist/{chunk-RDHFXOJM.js → chunk-LFMVHT3R.js} +106 -57
  26. package/dist/chunk-LUMETJFO.js +171 -0
  27. package/dist/{chunk-C3Z4N6A6.js → chunk-MHEBWVXL.js} +6 -2
  28. package/dist/{chunk-VGER3HYZ.js → chunk-PGKJ2OQP.js} +5 -5
  29. package/dist/{chunk-672UXYL3.js → chunk-QBBF7GIK.js} +2 -2
  30. package/dist/{chunk-DE7BZDNK.js → chunk-QFPQXKCG.js} +11 -3
  31. package/dist/chunk-R4QGL6GB.js +10 -0
  32. package/dist/chunk-S4FFHDJG.js +93 -0
  33. package/dist/chunk-SFBFYIOW.js +271 -0
  34. package/dist/{chunk-JX6KL7OF.js → chunk-SKPDE62W.js} +4 -3
  35. package/dist/{chunk-RMQNCHGO.js → chunk-U46QR4WR.js} +5 -9
  36. package/dist/{chunk-B4JLSXZB.js → chunk-WSTVGC2C.js} +73 -45
  37. package/dist/chunk-YBUCXXYZ.js +36 -0
  38. package/dist/{chunk-2VMJ3E5Z.js → chunk-YTEOAWOJ.js} +6 -2
  39. package/dist/{chunk-GPPRNAKW.js → chunk-ZMX5HRLI.js} +2 -2
  40. package/dist/{chunk-YLQEGYV4.js → chunk-ZZRSG7VK.js} +14 -4
  41. package/dist/{cli-context-4PTWEK52.js → cli-context-34Q5IJRA.js} +121 -89
  42. package/dist/cli-entry-point.d.ts +2 -0
  43. package/dist/{cli-main-DQ2GBY7M.js → cli-main-AELJDBGU.js} +1471 -1293
  44. package/dist/config-doctor.d.ts +10 -2
  45. package/dist/{config-transfer-2YF3CRTN.js → config-transfer-RUWLDCNK.js} +34 -13
  46. package/dist/{diag-doctor-ANPRUYFM.js → diag-doctor-WIFJH3XH.js} +2 -2
  47. package/dist/execute-deps.d.ts +2 -1
  48. package/dist/{execution-A6WJLSOC.js → execution-PYC5FCS4.js} +303 -256
  49. package/dist/{hq-NU75W3SE.js → hq-JTUTFI4L.js} +274 -258
  50. package/dist/hq-command-controller.d.ts +1 -1
  51. package/dist/hq-server/project-root.d.ts +35 -1
  52. package/dist/{hq-server-M2JWVW4D.js → hq-server-PC7GZMPT.js} +2 -2
  53. package/dist/{hq-service-L5INQFM4.js → hq-service-NW56MUP2.js} +11 -4
  54. package/dist/index.d.ts +1 -1
  55. package/dist/index.js +17 -12
  56. package/dist/kanban-supervisor-67ECABTD.js +7 -0
  57. package/dist/live-settings-input.d.ts +7 -0
  58. package/dist/{mailbox-serve-3GFOK2I7.js → mailbox-serve-WDOCH6KR.js} +3 -3
  59. package/dist/{mcp-DC5WSBOO.js → mcp-LBS45HWZ.js} +4 -4
  60. package/dist/modeldiag-36OGAL5C.js +828 -0
  61. package/dist/{per-subcommand-help-DHTYU524.js → per-subcommand-help-XE77MTGJ.js} +3 -3
  62. package/dist/permission-prompt-mirror.d.ts +1 -1
  63. package/dist/permission-prompt.d.ts +2 -2
  64. package/dist/plugin-config.d.ts +12 -0
  65. package/dist/plugin-management-types.d.ts +31 -0
  66. package/dist/plugin-management.d.ts +2 -30
  67. package/dist/plugin-package-install.d.ts +3 -0
  68. package/dist/{plugin-usage-M6KET4OZ.js → plugin-usage-S5LVGMBW.js} +6 -5
  69. package/dist/{plugins-WJLW6ULV.js → plugins-RHBIVLI7.js} +4 -3
  70. package/dist/provider-config-utils.d.ts +1 -6
  71. package/dist/{providers-models-LFNZ5WI7.js → providers-models-2CFG46RD.js} +41 -31
  72. package/dist/release-asset-download.d.ts +3 -0
  73. package/dist/services/dispatch-classifier.d.ts +39 -5
  74. package/dist/{sessions-config-UL45XKX7.js → sessions-config-IQUH2GXT.js} +13 -5
  75. package/dist/settings-menu.d.ts +2 -0
  76. package/dist/short-circuit-flags-SDT53OML.js +10 -0
  77. package/dist/{simpleui-dist-CXSIDAXZ.js → simpleui-dist-7BQISSEP.js} +7 -3
  78. package/dist/skill-suggest-L2EVWB6R.js +290 -0
  79. package/dist/slash-commands/effort.d.ts +1 -1
  80. package/dist/slash-commands/fallback.d.ts +1 -1
  81. package/dist/slash-commands/gitid.d.ts +1 -1
  82. package/dist/slash-commands/jev.d.ts +4 -0
  83. package/dist/slash-commands/openai-quota.d.ts +15 -0
  84. package/dist/slash-commands/sdd-task-mutations.d.ts +4 -0
  85. package/dist/slash-commands/settings-keys.d.ts +2 -0
  86. package/dist/slash-commands/settings-reset.d.ts +6 -0
  87. package/dist/standalone-update-MW47J3AK.js +164 -0
  88. package/dist/standalone-update.d.ts +19 -0
  89. package/dist/subcommands/handlers/hq-alerts.d.ts +3 -0
  90. package/dist/subcommands/handlers/hq-audit.d.ts +46 -0
  91. package/dist/subcommands/handlers/hq-data-dir.d.ts +13 -0
  92. package/dist/subcommands/handlers/skill-suggest.d.ts +21 -0
  93. package/dist/subcommands/handlers/typesafe-brain-replay.d.ts +26 -0
  94. package/dist/subcommands/handlers/typesafe-judgment-check.d.ts +3 -0
  95. package/dist/subcommands/handlers/typesafe-topic-replay.d.ts +19 -0
  96. package/dist/subcommands/handlers/typesafe-triage-replay.d.ts +18 -0
  97. package/dist/subcommands/handlers/typesafe.d.ts +23 -0
  98. package/dist/{subcommands-ERDDIJ3L.js → subcommands-ZTSIN7LG.js} +2 -2
  99. package/dist/typesafe-HVEVPA6R.js +316 -0
  100. package/dist/typesafe-brain-replay-C5276FVR.js +222 -0
  101. package/dist/typesafe-judgment-check-GGYYV5UF.js +29 -0
  102. package/dist/typesafe-topic-replay-4W73BOLV.js +218 -0
  103. package/dist/typesafe-triage-replay-FGAW7QM7.js +215 -0
  104. package/dist/{update-5DPB4YD4.js → update-7XIOEXTD.js} +4 -3
  105. package/dist/{update-check-BS3DHTSI.js → update-check-2POMBHFB.js} +3 -2
  106. package/dist/update-check.d.ts +1 -1
  107. package/dist/version-help-WZ623NWM.js +10 -0
  108. package/dist/version.d.ts +3 -2
  109. package/dist/webui-client-observability.d.ts +7 -0
  110. package/dist/{webui-server-MRPGTTKL.js → webui-server-L2SICPTB.js} +158 -94
  111. package/dist/webui-server-options.d.ts +11 -0
  112. package/dist/webui-server.d.ts +15 -1
  113. package/dist/wiring/domain-glossary.d.ts +5 -4
  114. package/dist/wiring/plugins.d.ts +1 -1
  115. package/dist/wiring/provider-persisters.d.ts +24 -0
  116. package/dist/wiring/provider-runtime.d.ts +9 -1
  117. package/dist/wiring/runtime-dispatch-state.d.ts +3 -1
  118. package/package.json +28 -28
  119. package/dist/chunk-DR2OXU46.js +0 -174
  120. package/dist/chunk-JACOMTIH.js +0 -275
  121. package/dist/chunk-M4W3QLQW.js +0 -54
  122. package/dist/chunk-XJXDOF63.js +0 -28
  123. package/dist/modeldiag-CJSFHZ3J.js +0 -1356
  124. package/dist/short-circuit-flags-SJQBJ33I.js +0 -10
  125. package/dist/version-help-WXI42PZ3.js +0 -10
@@ -0,0 +1,549 @@
1
+
2
+ // src/subcommands/handlers/modeldiag-profiles.ts
3
+ import { color } from "@wrongstack/core/utils";
4
+ var MODEL_PROFILES = [
5
+ {
6
+ provider: "anthropic",
7
+ pattern: /claude-opus/i,
8
+ family: "Claude Opus",
9
+ strengths: ["reasoning", "planning"],
10
+ bestFor: ["planning", "security", "debugging"],
11
+ costTier: "premium",
12
+ speedTier: "slow"
13
+ },
14
+ {
15
+ provider: "anthropic",
16
+ pattern: /claude-haiku/i,
17
+ family: "Claude Haiku",
18
+ strengths: ["speed"],
19
+ bestFor: ["lightweight", "docs"],
20
+ avoidFor: ["planning"],
21
+ costTier: "budget",
22
+ speedTier: "fast"
23
+ },
24
+ {
25
+ provider: "openai",
26
+ pattern: /gpt-5|o3|o4/i,
27
+ family: "GPT-5/o3/o4",
28
+ strengths: ["reasoning", "coding"],
29
+ bestFor: ["planning", "coding", "debugging"],
30
+ costTier: "premium",
31
+ speedTier: "normal"
32
+ },
33
+ {
34
+ provider: "openai",
35
+ // Must precede /gpt-4/ — every gpt-4o-mini id also contains gpt-4.
36
+ pattern: /gpt-4o-mini/i,
37
+ family: "GPT-4o Mini",
38
+ strengths: ["speed"],
39
+ bestFor: ["lightweight", "docs"],
40
+ avoidFor: ["planning"],
41
+ costTier: "budget",
42
+ speedTier: "fast"
43
+ },
44
+ {
45
+ provider: "openai",
46
+ pattern: /gpt-4/i,
47
+ family: "GPT-4",
48
+ strengths: ["coding"],
49
+ bestFor: ["coding", "docs"],
50
+ costTier: "standard",
51
+ speedTier: "fast"
52
+ },
53
+ {
54
+ provider: "google",
55
+ // Must precede /gemini-(?:2\.5|3)/ — flash variants (gemini-2.5-flash,
56
+ // gemini-3-flash, …) are budget/fast models and need this profile.
57
+ pattern: /gemini.*flash/i,
58
+ family: "Gemini Flash",
59
+ strengths: ["speed"],
60
+ bestFor: ["lightweight", "docs"],
61
+ avoidFor: ["planning"],
62
+ costTier: "budget",
63
+ speedTier: "fast"
64
+ },
65
+ {
66
+ provider: "google",
67
+ pattern: /gemini-(?:2\.5|3)/i,
68
+ family: "Gemini 2.5/3",
69
+ strengths: ["context", "coding"],
70
+ bestFor: ["coding", "data"],
71
+ costTier: "standard",
72
+ speedTier: "normal"
73
+ },
74
+ {
75
+ provider: "deepseek",
76
+ pattern: /deepseek/i,
77
+ family: "DeepSeek",
78
+ strengths: ["coding", "cost-effective"],
79
+ bestFor: ["coding", "general"],
80
+ costTier: "standard",
81
+ speedTier: "normal"
82
+ }
83
+ ];
84
+ function fmtTokens(n) {
85
+ if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
86
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
87
+ return String(n);
88
+ }
89
+ function fmtMs(ms) {
90
+ if (ms >= 1e3) return `${(ms / 1e3).toFixed(1)}s`;
91
+ return `${ms}ms`;
92
+ }
93
+ function fmtPrice(usdPer1M) {
94
+ if (usdPer1M === void 0) return color.dim("?");
95
+ if (usdPer1M >= 10) return `$${usdPer1M.toFixed(1)}`;
96
+ return `$${usdPer1M.toFixed(2)}`;
97
+ }
98
+ function checkMark(ok) {
99
+ return ok ? color.green("\u2713") : color.red("\u2717");
100
+ }
101
+ function costLabel(tier) {
102
+ switch (tier) {
103
+ case "premium":
104
+ return color.red("$$$");
105
+ case "standard":
106
+ return color.amber("$$");
107
+ case "budget":
108
+ return color.green("$");
109
+ default:
110
+ return color.dim("?");
111
+ }
112
+ }
113
+ function speedLabel(tier) {
114
+ switch (tier) {
115
+ case "fast":
116
+ return color.green("\u26A1");
117
+ case "normal":
118
+ return color.amber("\u2192");
119
+ case "slow":
120
+ return color.red("\u{1F422}");
121
+ default:
122
+ return color.dim("?");
123
+ }
124
+ }
125
+ function scoreBar(score, max) {
126
+ const pct = Math.min(1, Math.max(0, score / max));
127
+ const filled = Math.round(pct * 10);
128
+ const bar = color.green("\u2588".repeat(filled)) + color.dim("\u2591".repeat(10 - filled));
129
+ return `${bar} ${score}/${max}`;
130
+ }
131
+ var ROLE_CATEGORY = {
132
+ "security-scanner": "security",
133
+ "security-reviewer": "security",
134
+ "bug-hunter": "debugging",
135
+ debugger: "debugging",
136
+ tracer: "debugging",
137
+ planner: "planning",
138
+ architect: "planning",
139
+ "refactor-planner": "planning",
140
+ verifier: "testing",
141
+ test: "testing",
142
+ e2e: "testing",
143
+ document: "docs",
144
+ simplifier: "docs",
145
+ "code-reviewer": "review",
146
+ reviewer: "review",
147
+ critic: "review",
148
+ executor: "coding",
149
+ refactor: "refactoring",
150
+ migration: "coding",
151
+ frontend: "frontend",
152
+ backend: "backend",
153
+ api: "backend",
154
+ auth: "backend",
155
+ designer: "frontend",
156
+ analyst: "data",
157
+ data: "data",
158
+ database: "data",
159
+ explore: "planning",
160
+ search: "planning",
161
+ researcher: "planning"
162
+ };
163
+ function findProfile(pid, mid) {
164
+ for (const p of MODEL_PROFILES) {
165
+ if (p.provider === pid && p.pattern.test(mid)) return p;
166
+ }
167
+ return void 0;
168
+ }
169
+ function scoreModel(pid, mid, category, ctxWindow) {
170
+ const profile = findProfile(pid, mid);
171
+ let score = 50;
172
+ if (profile) {
173
+ if (profile.bestFor.includes(category)) score += 35;
174
+ if (profile.avoidFor?.includes(category)) score -= 50;
175
+ if (category === "planning" && profile.costTier === "premium") score += 15;
176
+ if (profile.speedTier === "slow" && category === "planning") score += 10;
177
+ if (profile.costTier === "budget" && category !== "planning" && category !== "security")
178
+ score += 10;
179
+ }
180
+ if (ctxWindow > 2e5) score += 10;
181
+ else if (ctxWindow > 1e5) score += 5;
182
+ else if (ctxWindow > 32e3) score += 2;
183
+ return { score, profile };
184
+ }
185
+ function rankModels(providers, hasKey, category, limit) {
186
+ const candidates = [];
187
+ for (const prov of providers) {
188
+ if (!hasKey(prov.id)) continue;
189
+ for (const m of prov.models ?? []) {
190
+ const ctxWindow = m.capabilities?.contextWindow ?? 0;
191
+ const { score, profile } = scoreModel(prov.id, m.id, category, ctxWindow);
192
+ if (score > 0) {
193
+ candidates.push({
194
+ provider: prov.id,
195
+ model: m.id,
196
+ profile,
197
+ score,
198
+ ctxWindow,
199
+ maxOutput: m.capabilities?.maxOutputTokens ?? 0,
200
+ inputPrice: m.pricing?.input,
201
+ outputPrice: m.pricing?.output
202
+ });
203
+ }
204
+ }
205
+ }
206
+ candidates.sort((a, b) => b.score - a.score);
207
+ return candidates.slice(0, limit);
208
+ }
209
+ var EVAL_TASKS = {
210
+ coding: {
211
+ label: "Code Generation",
212
+ prompt: "Write a TypeScript function parseCSV(input: string): { headers: string[]; rows: string[][] } that handles quoted fields, escaped quotes, and empty lines. Return an error string on malformed input. Keep under 40 lines."
213
+ },
214
+ planning: {
215
+ label: "Architecture Planning",
216
+ prompt: "Design the folder structure and key interfaces for a monorepo CLI tool with slash commands, model routing, subagent spawning, and config persistence. List packages, their responsibilities, and the 5 most important TypeScript interfaces."
217
+ },
218
+ security: {
219
+ label: "Vulnerability Detection",
220
+ prompt: 'Review this code for security issues:\n```ts\napp.get("/api/user", (req, res) => {\n const id = req.query.id;\n const user = db.query("SELECT * FROM users WHERE id = " + id);\n res.json(user);\n});\n\napp.post("/api/run", (req, res) => {\n const { cmd } = req.body;\n exec("echo " + cmd, (err, stdout) => res.send(stdout));\n});\n```\nList every vulnerability, its severity (critical/high/medium), and the exact fix.'
221
+ },
222
+ debugging: {
223
+ label: "Bug Diagnosis",
224
+ prompt: 'This async function has 2 bugs. Find and fix both:\n```ts\nasync function processBatch(items: string[]) {\n const results = [];\n for (const item of items) {\n const result = await fetch("https://api.example.com/" + item);\n results.push(result);\n }\n return results.map(r => r.json());\n}\n```\nExplain what each bug is, why it fails, and write the corrected version.'
225
+ },
226
+ testing: {
227
+ label: "Test Authoring",
228
+ prompt: 'Write vitest test cases for this deepMerge function:\n```ts\nfunction deepMerge(base: Record<string, unknown>, overrides: Record<string, unknown>): Record<string, unknown> {\n const merged = { ...base };\n for (const [key, val] of Object.entries(overrides)) {\n if (val === null) { delete merged[key]; continue; }\n if (typeof val === "object" && !Array.isArray(val) && typeof merged[key] === "object" && !Array.isArray(merged[key])) {\n merged[key] = deepMerge(merged[key] as Record<string, unknown>, val as Record<string, unknown>);\n } else { merged[key] = val; }\n }\n return merged;\n}\n```\nCover: happy path, edge cases, and error conditions.'
229
+ },
230
+ docs: {
231
+ label: "Documentation",
232
+ prompt: "Write TSDoc comments for this RateLimiter interface. Include @param, @returns, @throws, and @example for each method:\n```ts\ninterface RateLimiter {\n tryAcquire(key: string, maxPerWindow: number, windowMs: number): Promise<boolean>;\n getRemaining(key: string): Promise<number>;\n reset(key: string): Promise<void>;\n}\n```"
233
+ },
234
+ review: {
235
+ label: "Code Review",
236
+ prompt: 'Review this PR change:\n```diff\n async function loadConfig(path: string) {\n- const raw = await fs.readFile(path, "utf8");\n- return JSON.parse(raw);\n+ const raw = await fs.readFile(path);\n+ const config = JSON.parse(raw);\n+ process.env.API_KEY = config.apiKey;\n+ return config;\n }\n```\nList issues by severity (blocking / should-fix / nit) and explain your reasoning.'
237
+ },
238
+ refactoring: {
239
+ label: "Refactoring",
240
+ prompt: 'Refactor this nested condition into a cleaner pattern:\n```ts\nfunction getDiscount(user: { type: string; years: number; coupon?: string }): number {\n if (user.type === "premium") {\n if (user.years > 5) {\n if (user.coupon === "BLACKFRIDAY") return 0.5;\n return 0.3;\n }\n return 0.2;\n }\n if (user.type === "standard") {\n if (user.years > 3) return 0.15;\n return 0.1;\n }\n return 0;\n}\n```\nShow your refactored code and explain why your approach is cleaner.'
241
+ }
242
+ };
243
+ var EVAL_CATEGORIES = Object.keys(EVAL_TASKS);
244
+ function roleCat(role) {
245
+ return ROLE_CATEGORY[role] ?? "general";
246
+ }
247
+
248
+ // src/subcommands/handlers/modeldiag-eval.ts
249
+ import { color as color2 } from "@wrongstack/core/utils";
250
+ import { makeProviderFromConfig } from "@wrongstack/providers";
251
+ function createProviderForId(providerId, cfg) {
252
+ const savedCfg = cfg.providers?.[providerId];
253
+ const cfgWithType = Object.assign(
254
+ { type: providerId },
255
+ savedCfg ?? { apiKey: cfg.apiKey, baseUrl: cfg.baseUrl }
256
+ );
257
+ try {
258
+ return makeProviderFromConfig(providerId, cfgWithType);
259
+ } catch {
260
+ return void 0;
261
+ }
262
+ }
263
+ async function rankResponses(provider, leaderModel, taskPrompt, responses) {
264
+ const labelToIdx = /* @__PURE__ */ new Map();
265
+ const responseBlock = responses.map((r, i) => {
266
+ const label = String.fromCharCode(65 + i);
267
+ labelToIdx.set(label, i);
268
+ return `=== Response ${label} ===
269
+ ${r.text.slice(0, 800)}`;
270
+ }).join("\n\n");
271
+ const rankingPrompt = `Rank these responses from BEST (1) to WORST.
272
+
273
+ TASK:
274
+ ${taskPrompt.slice(0, 600)}
275
+
276
+ RESPONSES:
277
+ ${responseBlock}
278
+
279
+ Output ONLY a ranked list, one per line:
280
+ 1. Response X \u2014 brief reason
281
+ 2. Response Y \u2014 brief reason`;
282
+ try {
283
+ const resp = await provider.complete(
284
+ {
285
+ model: leaderModel,
286
+ system: [
287
+ {
288
+ type: "text",
289
+ text: "You are an expert evaluator. Rank responses concisely. Output ONLY the ranked list."
290
+ }
291
+ ],
292
+ messages: [{ role: "user", content: [{ type: "text", text: rankingPrompt }] }],
293
+ maxTokens: 400
294
+ },
295
+ { signal: AbortSignal.timeout(3e4) }
296
+ );
297
+ const text = resp.content[0] && "text" in resp.content[0] ? resp.content[0].text : "";
298
+ const rankings = [];
299
+ for (const line of text.split("\n")) {
300
+ const m = line.match(/^\s*(\d+)[.)]\s*Response\s+([A-Z])/i);
301
+ if (m) {
302
+ const label = m[2]?.toUpperCase();
303
+ const idx = label !== void 0 ? labelToIdx.get(label) : void 0;
304
+ if (idx !== void 0 && !rankings.includes(idx)) {
305
+ rankings.push(idx);
306
+ }
307
+ }
308
+ }
309
+ return rankings.length > 0 ? rankings : responses.map((_, i) => i);
310
+ } catch {
311
+ return responses.map((_, i) => i);
312
+ }
313
+ }
314
+ async function runModeldiagEval(args, deps, providers, config, hasKey) {
315
+ const writeLine = (line = "") => {
316
+ deps.renderer.write(`${line}
317
+ `);
318
+ };
319
+ const evalArgs = args.slice(1);
320
+ const providersEq = evalArgs.find((a) => a.startsWith("--providers="));
321
+ const providersValue = providersEq?.replace("--providers=", "") ?? (typeof deps.flags?.["providers"] === "string" ? deps.flags["providers"] : typeof deps.flags?.["provider"] === "string" ? deps.flags["provider"] : void 0);
322
+ const providerFilter = providersValue ? providersValue.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
323
+ const maxEq = evalArgs.find((a) => a.startsWith("--max="));
324
+ const maxValue = maxEq?.replace("--max=", "") ?? (typeof deps.flags?.["max"] === "string" ? deps.flags["max"] : void 0);
325
+ const maxModels = maxValue ? Math.max(1, parseInt(maxValue, 10) || 2) : 2;
326
+ const quick = evalArgs.includes("--quick") || deps.flags?.["quick"] === true || deps.flags?.["quick"] === "true";
327
+ const modelsPerCat = quick ? 1 : maxModels;
328
+ const roleFilter = evalArgs.find((a) => !a.startsWith("--"));
329
+ const targetCategories = roleFilter ? EVAL_CATEGORIES.includes(roleCat(roleFilter)) ? [roleCat(roleFilter)] : [] : EVAL_CATEGORIES;
330
+ if (targetCategories.length === 0 && roleFilter) {
331
+ writeLine(
332
+ `${color2.amber("Unknown role/category")}: "${roleFilter}". Try: ${EVAL_CATEGORIES.join(", ")}`
333
+ );
334
+ return 1;
335
+ }
336
+ const keyedProviderIds = providers.filter((p) => hasKey(p.id)).map((p) => p.id);
337
+ let targetProviderIds;
338
+ if (providerFilter && providerFilter.length > 0) {
339
+ const unknown = providerFilter.filter((pid) => !keyedProviderIds.includes(pid));
340
+ targetProviderIds = providerFilter.filter((pid) => keyedProviderIds.includes(pid));
341
+ if (targetProviderIds.length === 0) {
342
+ const noKeyMsg = unknown.length > 0 ? `None of the specified providers (${unknown.join(", ")}) have API keys. Add keys with wstack auth.` : "None of the specified providers have API keys configured.";
343
+ writeLine(`${color2.amber(noKeyMsg)}`);
344
+ return 0;
345
+ }
346
+ } else if (keyedProviderIds.length === 0) {
347
+ writeLine(`${color2.amber("No providers have API keys. Add keys with wstack auth.")}`);
348
+ return 0;
349
+ } else if (keyedProviderIds.length === 1) {
350
+ targetProviderIds = keyedProviderIds;
351
+ } else {
352
+ const providerList = keyedProviderIds.map((pid, i) => {
353
+ const info = providers.find((p) => p.id === pid);
354
+ return ` ${color2.cyan(String(i + 1))}) ${color2.bold(pid.padEnd(16))} ${color2.dim(info?.name ?? "")}`;
355
+ }).join("\n");
356
+ deps.renderer.write(
357
+ `
358
+ ${color2.bold("Select providers to evaluate")}
359
+
360
+ ${providerList}
361
+
362
+ ${color2.dim('Enter numbers or provider IDs (comma-separated, or "all"):')}
363
+ `
364
+ );
365
+ const input = await deps.reader.readLine(" > ");
366
+ const selected = input.trim().toLowerCase();
367
+ if (selected === "" || selected === "all") {
368
+ targetProviderIds = keyedProviderIds;
369
+ } else {
370
+ targetProviderIds = [];
371
+ for (const part of selected.split(",").map((s) => s.trim())) {
372
+ const idx = parseInt(part, 10);
373
+ if (idx >= 1 && idx <= keyedProviderIds.length) {
374
+ const pid = keyedProviderIds[idx - 1];
375
+ if (!targetProviderIds.includes(pid)) targetProviderIds.push(pid);
376
+ } else if (keyedProviderIds.includes(part)) {
377
+ if (!targetProviderIds.includes(part)) targetProviderIds.push(part);
378
+ }
379
+ }
380
+ }
381
+ if (targetProviderIds.length === 0) {
382
+ writeLine(color2.dim("No providers selected."));
383
+ return 0;
384
+ }
385
+ }
386
+ const leaderModel = config.model ?? "unknown";
387
+ const unknownProviders = providerFilter ? providerFilter.filter((pid) => !keyedProviderIds.includes(pid)) : [];
388
+ const warningLine = unknownProviders.length > 0 ? ` ${color2.amber("\u26A0 skipped (no key):")} ${unknownProviders.join(", ")}
389
+ ` : "";
390
+ writeLine(`${color2.bold("Model Competency Evaluation")}`);
391
+ writeLine(
392
+ color2.dim(
393
+ `Providers: ${targetProviderIds.join(", ")} | ${targetCategories.length} cats | ${modelsPerCat} model(s)/cat/provider`
394
+ )
395
+ );
396
+ writeLine(warningLine);
397
+ writeLine(color2.dim(`Leader (ranker): ${config.provider}/${leaderModel}`));
398
+ writeLine();
399
+ const collected = /* @__PURE__ */ new Map();
400
+ let total = 0;
401
+ let ok = 0;
402
+ for (const pid of targetProviderIds) {
403
+ const prov = createProviderForId(pid, config);
404
+ if (!prov) {
405
+ writeLine(color2.dim(` \u2298 ${pid}: provider unavailable, skipping`));
406
+ continue;
407
+ }
408
+ for (const cat of targetCategories) {
409
+ const task = EVAL_TASKS[cat];
410
+ if (!task) continue;
411
+ const candidates = rankModels(
412
+ providers,
413
+ (providerId) => providerId === pid && hasKey(providerId),
414
+ cat,
415
+ modelsPerCat
416
+ );
417
+ if (candidates.length === 0) continue;
418
+ if (!collected.has(cat)) collected.set(cat, /* @__PURE__ */ new Map());
419
+ for (const c of candidates) {
420
+ total++;
421
+ const modelKey = `${pid}/${c.model}`;
422
+ try {
423
+ const start = Date.now();
424
+ const resp = await prov.complete(
425
+ {
426
+ model: c.model,
427
+ system: [{ type: "text", text: "Be thorough and correct." }],
428
+ messages: [{ role: "user", content: [{ type: "text", text: task.prompt }] }],
429
+ maxTokens: 1024
430
+ },
431
+ { signal: AbortSignal.timeout(45e3) }
432
+ );
433
+ const respText = resp.content[0] && "text" in resp.content[0] ? resp.content[0].text : "";
434
+ const respUsage = resp.usage;
435
+ collected.get(cat)?.set(modelKey, {
436
+ model: modelKey,
437
+ latency: Date.now() - start,
438
+ tokens: (respUsage?.input ?? 0) + (respUsage?.output ?? 0),
439
+ text: respText
440
+ });
441
+ ok++;
442
+ } catch {
443
+ collected.get(cat)?.set(modelKey, {
444
+ model: modelKey,
445
+ latency: -1,
446
+ tokens: 0,
447
+ text: ""
448
+ });
449
+ }
450
+ }
451
+ }
452
+ }
453
+ writeLine(`${color2.dim(`Phase 1: ${ok}/${total} calls succeeded`)}`);
454
+ writeLine();
455
+ if (collected.size === 0) {
456
+ writeLine(color2.amber("No responses collected. Check provider configuration."));
457
+ return 0;
458
+ }
459
+ const leaderProvider = createProviderForId(config.provider, config);
460
+ if (leaderProvider) {
461
+ writeLine(`${color2.bold("Phase 2")} \u2014 ${color2.dim("leader ranks responses")}`);
462
+ writeLine();
463
+ }
464
+ const rankings = /* @__PURE__ */ new Map();
465
+ for (const [cat, responses] of collected) {
466
+ const valid = Array.from(responses.values()).filter((r) => r.latency >= 0);
467
+ if (valid.length < 2) {
468
+ if (valid.length === 1) {
469
+ const m = valid[0]?.model;
470
+ if (m !== void 0) {
471
+ if (!rankings.has(m)) rankings.set(m, /* @__PURE__ */ new Map());
472
+ rankings.get(m)?.set(cat, { rank: 1, total: 1 });
473
+ }
474
+ }
475
+ continue;
476
+ }
477
+ const task = EVAL_TASKS[cat];
478
+ if (leaderProvider) {
479
+ const ranked = await rankResponses(leaderProvider, leaderModel, task.prompt, valid);
480
+ for (let i = 0; i < valid.length; i++) {
481
+ const m = valid[ranked[i] ?? i]?.model;
482
+ if (m === void 0) continue;
483
+ if (!rankings.has(m)) rankings.set(m, /* @__PURE__ */ new Map());
484
+ rankings.get(m)?.set(cat, { rank: i + 1, total: valid.length });
485
+ }
486
+ } else {
487
+ for (const r of valid) {
488
+ if (!rankings.has(r.model)) rankings.set(r.model, /* @__PURE__ */ new Map());
489
+ rankings.get(r.model)?.set(cat, { rank: 1, total: valid.length });
490
+ }
491
+ }
492
+ }
493
+ writeLine(`${color2.bold("Competency Report")}`);
494
+ writeLine();
495
+ const allModels = [.../* @__PURE__ */ new Set([...rankings.keys()])].sort();
496
+ const catList = [...collected.keys()];
497
+ const modelColWidth = Math.max(24, ...allModels.map((m) => m.length)) + 2;
498
+ const cw = 12;
499
+ writeLine(
500
+ ` ${color2.dim("model".padEnd(modelColWidth))}` + catList.map((c) => color2.dim((EVAL_TASKS[c]?.label ?? c).slice(0, cw).padEnd(cw + 2))).join("")
501
+ );
502
+ writeLine(` ${color2.dim("\u2500".repeat(modelColWidth + catList.length * (cw + 2)))}`);
503
+ for (const model of allModels) {
504
+ const mr = rankings.get(model);
505
+ const provFromModel = model.split("/")[0] ?? "";
506
+ const modelColor = provFromModel === config.provider ? color2.cyan : color2.green;
507
+ let row = ` ${modelColor(model.padEnd(modelColWidth))}`;
508
+ for (const cat of catList) {
509
+ const e = mr.get(cat);
510
+ if (e) {
511
+ const pct = Math.round((1 - (e.rank - 1) / Math.max(1, e.total - 1)) * 100);
512
+ const pc = pct >= 80 ? color2.green : pct >= 50 ? color2.amber : color2.red;
513
+ row += `${pc(`#${e.rank} ${pct}%`.padEnd(cw + 2))}`;
514
+ } else {
515
+ row += color2.dim("\u2014".padEnd(cw + 2));
516
+ }
517
+ }
518
+ writeLine(row);
519
+ }
520
+ writeLine();
521
+ writeLine(color2.dim("#1 100% = best in category. \u2014 = not tested."));
522
+ writeLine();
523
+ writeLine(color2.dim("Pin: wstack setmodel set <role> <provider>/<model>"));
524
+ writeLine(
525
+ color2.dim(
526
+ "Full: wstack modeldiag eval Providers: wstack modeldiag eval --providers=id1,id2"
527
+ )
528
+ );
529
+ writeLine(
530
+ color2.dim("Max: wstack modeldiag eval --max=3 Quick: wstack modeldiag eval --quick")
531
+ );
532
+ return 0;
533
+ }
534
+
535
+ export {
536
+ fmtTokens,
537
+ fmtMs,
538
+ fmtPrice,
539
+ checkMark,
540
+ costLabel,
541
+ speedLabel,
542
+ scoreBar,
543
+ findProfile,
544
+ rankModels,
545
+ roleCat,
546
+ createProviderForId,
547
+ runModeldiagEval
548
+ };
549
+ //# sourceMappingURL=chunk-BBHWW6LR.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  CLI_VERSION
3
- } from "./chunk-XJXDOF63.js";
3
+ } from "./chunk-YBUCXXYZ.js";
4
4
 
5
5
  // src/hq-publisher.ts
6
6
  import { createHqPublisherFromEnv, resolveHqConfig } from "@wrongstack/core/hq";
@@ -10,6 +10,7 @@ import { WebSocket } from "ws";
10
10
  import {
11
11
  deriveHqProjectId,
12
12
  MAX_HQ_KANBAN_BOARD_BYTES,
13
+ MAX_HQ_KANBAN_BOARDS,
13
14
  redactHqEventPayload
14
15
  } from "@wrongstack/core/hq";
15
16
  import {
@@ -337,7 +338,7 @@ function chunkSnapshotPayload(projectId, generatedAt, boards, tombstones) {
337
338
  { code: "WRONGSTACK_HQ_KANBAN_OVERSIZED_RECORD" }
338
339
  );
339
340
  }
340
- if ((currentBoards.length > 0 || currentTombstones.length > 0) && currentBytes + additionalBytes > MAX_KANBAN_SNAPSHOT_PAYLOAD_BYTES) {
341
+ if ((currentBoards.length > 0 || currentTombstones.length > 0) && (currentBytes + additionalBytes > MAX_KANBAN_SNAPSHOT_PAYLOAD_BYTES || itemCount >= MAX_HQ_KANBAN_BOARDS)) {
341
342
  pushCurrent();
342
343
  }
343
344
  if (kind === "boards") {
@@ -517,4 +518,4 @@ function startCliHqConnection(options) {
517
518
  export {
518
519
  startCliHqConnection
519
520
  };
520
- //# sourceMappingURL=chunk-CJTBLTS4.js.map
521
+ //# sourceMappingURL=chunk-BLD5DSVA.js.map