@wrongstack/cli 1.0.19 → 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 (124) 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-KK2LB3ZS.js → chunk-DRM7PSW7.js} +9 -5
  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-2ENZU66X.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-33F74CSZ.js → cli-context-34Q5IJRA.js} +121 -89
  42. package/dist/cli-entry-point.d.ts +2 -0
  43. package/dist/{cli-main-FNGMKPBA.js → cli-main-AELJDBGU.js} +1439 -1276
  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-XQWQTRDA.js → hq-JTUTFI4L.js} +274 -258
  50. package/dist/hq-command-controller.d.ts +1 -1
  51. package/dist/{hq-server-IIBZQZU4.js → hq-server-PC7GZMPT.js} +2 -2
  52. package/dist/{hq-service-L5INQFM4.js → hq-service-NW56MUP2.js} +11 -4
  53. package/dist/index.d.ts +1 -1
  54. package/dist/index.js +17 -12
  55. package/dist/kanban-supervisor-67ECABTD.js +7 -0
  56. package/dist/live-settings-input.d.ts +7 -0
  57. package/dist/{mailbox-serve-3GFOK2I7.js → mailbox-serve-WDOCH6KR.js} +3 -3
  58. package/dist/{mcp-DC5WSBOO.js → mcp-LBS45HWZ.js} +4 -4
  59. package/dist/modeldiag-36OGAL5C.js +828 -0
  60. package/dist/{per-subcommand-help-DHTYU524.js → per-subcommand-help-XE77MTGJ.js} +3 -3
  61. package/dist/permission-prompt-mirror.d.ts +1 -1
  62. package/dist/permission-prompt.d.ts +2 -2
  63. package/dist/plugin-config.d.ts +12 -0
  64. package/dist/plugin-management-types.d.ts +31 -0
  65. package/dist/plugin-management.d.ts +2 -30
  66. package/dist/plugin-package-install.d.ts +3 -0
  67. package/dist/{plugin-usage-M6KET4OZ.js → plugin-usage-S5LVGMBW.js} +6 -5
  68. package/dist/{plugins-WJLW6ULV.js → plugins-RHBIVLI7.js} +4 -3
  69. package/dist/provider-config-utils.d.ts +1 -6
  70. package/dist/{providers-models-LFNZ5WI7.js → providers-models-2CFG46RD.js} +41 -31
  71. package/dist/release-asset-download.d.ts +3 -0
  72. package/dist/services/dispatch-classifier.d.ts +39 -5
  73. package/dist/{sessions-config-UL45XKX7.js → sessions-config-IQUH2GXT.js} +13 -5
  74. package/dist/settings-menu.d.ts +2 -0
  75. package/dist/short-circuit-flags-SDT53OML.js +10 -0
  76. package/dist/{simpleui-dist-CXSIDAXZ.js → simpleui-dist-7BQISSEP.js} +7 -3
  77. package/dist/skill-suggest-L2EVWB6R.js +290 -0
  78. package/dist/slash-commands/effort.d.ts +1 -1
  79. package/dist/slash-commands/fallback.d.ts +1 -1
  80. package/dist/slash-commands/gitid.d.ts +1 -1
  81. package/dist/slash-commands/jev.d.ts +4 -0
  82. package/dist/slash-commands/openai-quota.d.ts +15 -0
  83. package/dist/slash-commands/sdd-task-mutations.d.ts +4 -0
  84. package/dist/slash-commands/settings-keys.d.ts +2 -0
  85. package/dist/slash-commands/settings-reset.d.ts +6 -0
  86. package/dist/standalone-update-MW47J3AK.js +164 -0
  87. package/dist/standalone-update.d.ts +19 -0
  88. package/dist/subcommands/handlers/hq-alerts.d.ts +3 -0
  89. package/dist/subcommands/handlers/hq-audit.d.ts +46 -0
  90. package/dist/subcommands/handlers/hq-data-dir.d.ts +13 -0
  91. package/dist/subcommands/handlers/skill-suggest.d.ts +21 -0
  92. package/dist/subcommands/handlers/typesafe-brain-replay.d.ts +26 -0
  93. package/dist/subcommands/handlers/typesafe-judgment-check.d.ts +3 -0
  94. package/dist/subcommands/handlers/typesafe-topic-replay.d.ts +19 -0
  95. package/dist/subcommands/handlers/typesafe-triage-replay.d.ts +18 -0
  96. package/dist/subcommands/handlers/typesafe.d.ts +23 -0
  97. package/dist/{subcommands-XSLXMIDP.js → subcommands-ZTSIN7LG.js} +2 -2
  98. package/dist/typesafe-HVEVPA6R.js +316 -0
  99. package/dist/typesafe-brain-replay-C5276FVR.js +222 -0
  100. package/dist/typesafe-judgment-check-GGYYV5UF.js +29 -0
  101. package/dist/typesafe-topic-replay-4W73BOLV.js +218 -0
  102. package/dist/typesafe-triage-replay-FGAW7QM7.js +215 -0
  103. package/dist/{update-5DPB4YD4.js → update-7XIOEXTD.js} +4 -3
  104. package/dist/{update-check-BS3DHTSI.js → update-check-2POMBHFB.js} +3 -2
  105. package/dist/update-check.d.ts +1 -1
  106. package/dist/version-help-WZ623NWM.js +10 -0
  107. package/dist/version.d.ts +3 -2
  108. package/dist/webui-client-observability.d.ts +7 -0
  109. package/dist/{webui-server-MRPGTTKL.js → webui-server-L2SICPTB.js} +158 -94
  110. package/dist/webui-server-options.d.ts +11 -0
  111. package/dist/webui-server.d.ts +15 -1
  112. package/dist/wiring/domain-glossary.d.ts +5 -4
  113. package/dist/wiring/plugins.d.ts +1 -1
  114. package/dist/wiring/provider-persisters.d.ts +24 -0
  115. package/dist/wiring/provider-runtime.d.ts +9 -1
  116. package/dist/wiring/runtime-dispatch-state.d.ts +3 -1
  117. package/package.json +28 -28
  118. package/dist/chunk-2CZEORLV.js +0 -54
  119. package/dist/chunk-DR2OXU46.js +0 -174
  120. package/dist/chunk-JACOMTIH.js +0 -275
  121. package/dist/chunk-XJXDOF63.js +0 -28
  122. package/dist/modeldiag-CJSFHZ3J.js +0 -1356
  123. package/dist/short-circuit-flags-7GQIGMVD.js +0 -10
  124. 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
@@ -641,6 +641,7 @@ import * as path6 from "node:path";
641
641
  import * as fs2 from "node:fs";
642
642
  import { createRequire } from "node:module";
643
643
  import * as path2 from "node:path";
644
+ import { wrongstackPackageJsonPath } from "@wrongstack/core/utils";
644
645
  var requireFromHere = createRequire(import.meta.url);
645
646
  var MIME = {
646
647
  ".html": "text/html; charset=utf-8",
@@ -660,7 +661,10 @@ var cachedDistDir;
660
661
  function resolveHqDistDir() {
661
662
  if (cachedDistDir !== void 0) return cachedDistDir;
662
663
  try {
663
- const pkgJson = requireFromHere.resolve("@wrongstack/webui-hq/package.json");
664
+ const pkgJson = wrongstackPackageJsonPath(
665
+ "@wrongstack/webui-hq",
666
+ (id) => requireFromHere.resolve(id)
667
+ );
664
668
  const dist = path2.join(path2.dirname(pkgJson), "dist");
665
669
  if (fs2.existsSync(path2.join(dist, "index.html"))) {
666
670
  cachedDistDir = dist;
@@ -687,7 +691,7 @@ function resolveHqDistDir() {
687
691
  }
688
692
  function isInsideDist(filePath, distDir) {
689
693
  const rel = path2.relative(distDir, filePath);
690
- return rel !== "" && !rel.startsWith("..") && !path2.isAbsolute(rel);
694
+ return rel !== "" && rel !== ".." && !rel.startsWith(`..${path2.sep}`) && !path2.isAbsolute(rel);
691
695
  }
692
696
  function isMissingPath(error) {
693
697
  const code = error.code;
@@ -2843,8 +2847,8 @@ async function handleApiAgentMessages(req, res, match, agentMessages) {
2843
2847
  // src/hq-server/routes/system-handlers.ts
2844
2848
  async function handleApiSystemUpdate(res) {
2845
2849
  const [{ checkForUpdate }, { detectUpdatePackageName }] = await Promise.all([
2846
- import("./update-check-BS3DHTSI.js"),
2847
- import("./update-5DPB4YD4.js")
2850
+ import("./update-check-2POMBHFB.js"),
2851
+ import("./update-7XIOEXTD.js")
2848
2852
  ]);
2849
2853
  const packageName = detectUpdatePackageName();
2850
2854
  const info = await checkForUpdate({ packageName });
@@ -5146,4 +5150,4 @@ export {
5146
5150
  startHqServer,
5147
5151
  HqInsecureExposureError2 as HqInsecureExposureError
5148
5152
  };
5149
- //# sourceMappingURL=chunk-KK2LB3ZS.js.map
5153
+ //# sourceMappingURL=chunk-DRM7PSW7.js.map
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  PLUGIN_AUDIT_ENTRIES,
6
6
  loadExternalPlugins
7
- } from "./chunk-SIS37L3W.js";
7
+ } from "./chunk-L2RVEQWO.js";
8
8
 
9
9
  // src/wiring/plugins.ts
10
10
  import { join } from "node:path";
@@ -20,7 +20,21 @@ function createApi(ownerName, base) {
20
20
  // src/wiring/plugins.ts
21
21
  var DEPRECATED_PLUGIN_NAMES = {
22
22
  "web-search": "use the built-in `search` and `fetch` tools",
23
- "json-path": "use the built-in `json` tool with action: query | validate | transform | merge"
23
+ "json-path": "use the built-in `json` tool with action: query | validate | transform | merge",
24
+ // Deleted in the 2026-09-17 catalog review. The first four produced
25
+ // findings that were noise by construction (each was run against this
26
+ // repo and mis-reported); the rest duplicated infrastructure the host
27
+ // already ships. Kept here so an existing config that still lists one
28
+ // warns and skips instead of failing to resolve the import.
29
+ "interface-contract-guard": "removed \u2014 it reported plain data interfaces as unimplemented",
30
+ "refactor-suggester": "removed \u2014 use `lint-gate` plus the built-in `grep` and `read` tools",
31
+ "feature-flag-tracker": "removed \u2014 use the built-in `grep` tool with your own flag pattern",
32
+ "dead-code-detector": "removed \u2014 use your linter / `tsc` and `duplicate-code-detector`",
33
+ "semantic-search-indexer": "use the built-in `grep` tool or the codebase-index MCP server",
34
+ "knowledge-graph": "use the built-in `remember` / `search_memory` / `find_related_memories`",
35
+ "todo-tracker": "use the built-in `todo` tool and the Kanban board",
36
+ "smart-rename": "use the built-in `replace` tool",
37
+ "code-metrics": "removed \u2014 it reported numbers nothing acted on"
24
38
  };
25
39
  var deprecatedWarningsEmitted = /* @__PURE__ */ new Set();
26
40
  var BUILTIN_PLUGIN_CONFIG_ALIASES = {
@@ -171,13 +185,6 @@ async function setupPlugins(params) {
171
185
  if (allPlugins.length === 0) return;
172
186
  const pluginOptions = buildPluginOptions(config, allPlugins);
173
187
  if (paths?.projectDir) {
174
- if (pluginOptions["todo-tracker"] === void 0) {
175
- pluginOptions["todo-tracker"] = {};
176
- }
177
- const todoTrackerOpts = pluginOptions["todo-tracker"];
178
- if (typeof todoTrackerOpts["filePath"] !== "string" || todoTrackerOpts["filePath"] === "") {
179
- todoTrackerOpts["filePath"] = join(paths.projectDir, "todo-tracker.json");
180
- }
181
188
  const contextPinsOpts = pluginOptions["context-pins"] ?? {};
182
189
  pluginOptions["context-pins"] = contextPinsOpts;
183
190
  if (typeof contextPinsOpts["filePath"] !== "string" || contextPinsOpts["filePath"] === "") {
@@ -267,4 +274,4 @@ export {
267
274
  BUILTIN_PLUGIN_FACTORIES,
268
275
  setupPlugins
269
276
  };
270
- //# sourceMappingURL=chunk-XJ7K67PP.js.map
277
+ //# sourceMappingURL=chunk-JF2MULWF.js.map