@viberaven/cli 0.1.0-beta.0 → 0.1.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,9 +1,161 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __commonJS = (cb, mod) => function __require() {
10
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+
29
+ // node_modules/picocolors/picocolors.js
30
+ var require_picocolors = __commonJS({
31
+ "node_modules/picocolors/picocolors.js"(exports2, module2) {
32
+ var p2 = process || {};
33
+ var argv = p2.argv || [];
34
+ var env = p2.env || {};
35
+ var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p2.platform === "win32" || (p2.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
36
+ var formatter = (open, close, replace = open) => (input) => {
37
+ let string = "" + input, index = string.indexOf(close, open.length);
38
+ return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
39
+ };
40
+ var replaceClose = (string, close, replace, index) => {
41
+ let result = "", cursor = 0;
42
+ do {
43
+ result += string.substring(cursor, index) + replace;
44
+ cursor = index + close.length;
45
+ index = string.indexOf(close, cursor);
46
+ } while (~index);
47
+ return result + string.substring(cursor);
48
+ };
49
+ var createColors = (enabled = isColorSupported) => {
50
+ let f = enabled ? formatter : () => String;
51
+ return {
52
+ isColorSupported: enabled,
53
+ reset: f("\x1B[0m", "\x1B[0m"),
54
+ bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
55
+ dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
56
+ italic: f("\x1B[3m", "\x1B[23m"),
57
+ underline: f("\x1B[4m", "\x1B[24m"),
58
+ inverse: f("\x1B[7m", "\x1B[27m"),
59
+ hidden: f("\x1B[8m", "\x1B[28m"),
60
+ strikethrough: f("\x1B[9m", "\x1B[29m"),
61
+ black: f("\x1B[30m", "\x1B[39m"),
62
+ red: f("\x1B[31m", "\x1B[39m"),
63
+ green: f("\x1B[32m", "\x1B[39m"),
64
+ yellow: f("\x1B[33m", "\x1B[39m"),
65
+ blue: f("\x1B[34m", "\x1B[39m"),
66
+ magenta: f("\x1B[35m", "\x1B[39m"),
67
+ cyan: f("\x1B[36m", "\x1B[39m"),
68
+ white: f("\x1B[37m", "\x1B[39m"),
69
+ gray: f("\x1B[90m", "\x1B[39m"),
70
+ bgBlack: f("\x1B[40m", "\x1B[49m"),
71
+ bgRed: f("\x1B[41m", "\x1B[49m"),
72
+ bgGreen: f("\x1B[42m", "\x1B[49m"),
73
+ bgYellow: f("\x1B[43m", "\x1B[49m"),
74
+ bgBlue: f("\x1B[44m", "\x1B[49m"),
75
+ bgMagenta: f("\x1B[45m", "\x1B[49m"),
76
+ bgCyan: f("\x1B[46m", "\x1B[49m"),
77
+ bgWhite: f("\x1B[47m", "\x1B[49m"),
78
+ blackBright: f("\x1B[90m", "\x1B[39m"),
79
+ redBright: f("\x1B[91m", "\x1B[39m"),
80
+ greenBright: f("\x1B[92m", "\x1B[39m"),
81
+ yellowBright: f("\x1B[93m", "\x1B[39m"),
82
+ blueBright: f("\x1B[94m", "\x1B[39m"),
83
+ magentaBright: f("\x1B[95m", "\x1B[39m"),
84
+ cyanBright: f("\x1B[96m", "\x1B[39m"),
85
+ whiteBright: f("\x1B[97m", "\x1B[39m"),
86
+ bgBlackBright: f("\x1B[100m", "\x1B[49m"),
87
+ bgRedBright: f("\x1B[101m", "\x1B[49m"),
88
+ bgGreenBright: f("\x1B[102m", "\x1B[49m"),
89
+ bgYellowBright: f("\x1B[103m", "\x1B[49m"),
90
+ bgBlueBright: f("\x1B[104m", "\x1B[49m"),
91
+ bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
92
+ bgCyanBright: f("\x1B[106m", "\x1B[49m"),
93
+ bgWhiteBright: f("\x1B[107m", "\x1B[49m")
94
+ };
95
+ };
96
+ module2.exports = createColors();
97
+ module2.exports.createColors = createColors;
98
+ }
99
+ });
100
+
101
+ // node_modules/sisteransi/src/index.js
102
+ var require_src = __commonJS({
103
+ "node_modules/sisteransi/src/index.js"(exports2, module2) {
104
+ "use strict";
105
+ var ESC = "\x1B";
106
+ var CSI = `${ESC}[`;
107
+ var beep = "\x07";
108
+ var cursor = {
109
+ to(x2, y3) {
110
+ if (!y3) return `${CSI}${x2 + 1}G`;
111
+ return `${CSI}${y3 + 1};${x2 + 1}H`;
112
+ },
113
+ move(x2, y3) {
114
+ let ret = "";
115
+ if (x2 < 0) ret += `${CSI}${-x2}D`;
116
+ else if (x2 > 0) ret += `${CSI}${x2}C`;
117
+ if (y3 < 0) ret += `${CSI}${-y3}A`;
118
+ else if (y3 > 0) ret += `${CSI}${y3}B`;
119
+ return ret;
120
+ },
121
+ up: (count = 1) => `${CSI}${count}A`,
122
+ down: (count = 1) => `${CSI}${count}B`,
123
+ forward: (count = 1) => `${CSI}${count}C`,
124
+ backward: (count = 1) => `${CSI}${count}D`,
125
+ nextLine: (count = 1) => `${CSI}E`.repeat(count),
126
+ prevLine: (count = 1) => `${CSI}F`.repeat(count),
127
+ left: `${CSI}G`,
128
+ hide: `${CSI}?25l`,
129
+ show: `${CSI}?25h`,
130
+ save: `${ESC}7`,
131
+ restore: `${ESC}8`
132
+ };
133
+ var scroll = {
134
+ up: (count = 1) => `${CSI}S`.repeat(count),
135
+ down: (count = 1) => `${CSI}T`.repeat(count)
136
+ };
137
+ var erase = {
138
+ screen: `${CSI}2J`,
139
+ up: (count = 1) => `${CSI}1J`.repeat(count),
140
+ down: (count = 1) => `${CSI}J`.repeat(count),
141
+ line: `${CSI}2K`,
142
+ lineEnd: `${CSI}K`,
143
+ lineStart: `${CSI}1K`,
144
+ lines(count) {
145
+ let clear = "";
146
+ for (let i = 0; i < count; i++)
147
+ clear += this.line + (i < count - 1 ? cursor.up() : "");
148
+ if (count)
149
+ clear += cursor.left;
150
+ return clear;
151
+ }
152
+ };
153
+ module2.exports = { cursor, scroll, erase, beep };
154
+ }
155
+ });
3
156
 
4
157
  // src/cli.ts
5
- var import_promises4 = require("node:fs/promises");
6
- var import_node_path5 = require("node:path");
158
+ var import_node_path8 = require("node:path");
7
159
 
8
160
  // src/config.ts
9
161
  var import_node_os = require("node:os");
@@ -34,6 +186,43 @@ function getCredentialsPath() {
34
186
  function getProjectArtifactsDir(cwd = process.cwd()) {
35
187
  return (0, import_node_path.join)(cwd, ".viberaven");
36
188
  }
189
+ var LAST_SCAN_FILE = "last-scan.json";
190
+ async function findArtifactsWorkspace(startDir = process.cwd()) {
191
+ let dir = (0, import_node_path.resolve)(startDir);
192
+ const fsRoot = (0, import_node_path.parse)(dir).root;
193
+ while (true) {
194
+ try {
195
+ await (0, import_promises2.access)((0, import_node_path.join)(dir, ".viberaven", LAST_SCAN_FILE), import_node_fs.constants.F_OK);
196
+ return dir;
197
+ } catch {
198
+ }
199
+ const parent = (0, import_node_path.dirname)(dir);
200
+ if (parent === dir || dir === fsRoot) {
201
+ return void 0;
202
+ }
203
+ dir = parent;
204
+ }
205
+ }
206
+ async function resolveWorkspaceRoot(startDir = process.cwd()) {
207
+ const withArtifacts = await findArtifactsWorkspace(startDir);
208
+ if (withArtifacts) {
209
+ return withArtifacts;
210
+ }
211
+ let dir = (0, import_node_path.resolve)(startDir);
212
+ const fsRoot = (0, import_node_path.parse)(dir).root;
213
+ while (true) {
214
+ try {
215
+ await (0, import_promises2.access)((0, import_node_path.join)(dir, ".git"), import_node_fs.constants.F_OK);
216
+ return dir;
217
+ } catch {
218
+ }
219
+ const parent = (0, import_node_path.dirname)(dir);
220
+ if (parent === dir || dir === fsRoot) {
221
+ return (0, import_node_path.resolve)(startDir);
222
+ }
223
+ dir = parent;
224
+ }
225
+ }
37
226
  async function ensureConfigDir() {
38
227
  const dir = getConfigDir();
39
228
  await (0, import_promises.mkdir)(dir, { recursive: true });
@@ -104,21 +293,21 @@ function isNetworkFetchFailure(error) {
104
293
  return false;
105
294
  }
106
295
  if (error instanceof TypeError) {
107
- const m = error.message;
108
- if (m === "fetch failed" || m.toLowerCase().includes("fetch failed") || m.includes("Failed to fetch")) {
296
+ const m2 = error.message;
297
+ if (m2 === "fetch failed" || m2.toLowerCase().includes("fetch failed") || m2.includes("Failed to fetch")) {
109
298
  return true;
110
299
  }
111
300
  }
112
301
  if (error instanceof AggregateError) {
113
- return error.errors.some((e2) => isNetworkFetchFailure(e2));
302
+ return error.errors.some((e3) => isNetworkFetchFailure(e3));
114
303
  }
115
304
  if (error instanceof Error && "cause" in error && error.cause != null) {
116
305
  return isNetworkFetchFailure(error.cause);
117
306
  }
118
- const e = error;
119
- if (typeof e.code === "string") {
307
+ const e2 = error;
308
+ if (typeof e2.code === "string") {
120
309
  if (["ECONNREFUSED", "ECONNRESET", "ENOTFOUND", "EAI_AGAIN", "ETIMEDOUT", "ENETUNREACH", "EHOSTUNREACH"].includes(
121
- e.code
310
+ e2.code
122
311
  )) {
123
312
  return true;
124
313
  }
@@ -249,7 +438,7 @@ function isManagedUsage(value) {
249
438
  if (!Array.isArray(value.unlockedMapCategoryKeys)) {
250
439
  return false;
251
440
  }
252
- return value.unlockedMapCategoryKeys.every((k) => typeof k === "string");
441
+ return value.unlockedMapCategoryKeys.every((k3) => typeof k3 === "string");
253
442
  }
254
443
  var BackendHttpError = class extends Error {
255
444
  constructor(message, status, upgradeUrl) {
@@ -300,6 +489,20 @@ async function syncCredentialsFromAccount(credentials) {
300
489
  await saveCredentials(updated);
301
490
  return { ...updated, account };
302
491
  }
492
+ async function enrichArtifactWithAccount(artifact, apiBaseUrl, accessToken) {
493
+ try {
494
+ const account = await fetchAccountMe(apiBaseUrl, accessToken);
495
+ return {
496
+ ...artifact,
497
+ accountEmail: account.email,
498
+ plan: account.plan,
499
+ usage: account.usage,
500
+ usageLine: formatUsageLine(account.usage)
501
+ };
502
+ } catch {
503
+ return artifact;
504
+ }
505
+ }
303
506
  function formatUsageLine(usage) {
304
507
  const periodLabel = usage.period === "monthly" ? "this month" : "lifetime";
305
508
  return `Scans: ${usage.used}/${usage.limit} (${periodLabel}, ${usage.plan}) \xB7 ${usage.remainingPrompts} remaining`;
@@ -317,7 +520,7 @@ function formatScanLimitMessage(upgradeUrl) {
317
520
 
318
521
  // src/auth.ts
319
522
  function sleep(ms) {
320
- return new Promise((resolve) => setTimeout(resolve, ms));
523
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
321
524
  }
322
525
  async function runDeviceLogin(apiBaseUrl) {
323
526
  const signIn = await startManagedSignIn(apiBaseUrl);
@@ -479,6 +682,7 @@ var STACK_SIGNAL_PATHS = {
479
682
  hasDocker: ["Dockerfile", "docker-compose.yml", "docker-compose.yaml"],
480
683
  hasCI: [".github/workflows", ".gitlab-ci.yml", "circle.yml"],
481
684
  hasStripe: [],
685
+ hasPolar: [],
482
686
  hasClerk: [],
483
687
  hasAuthJs: [],
484
688
  hasPaddle: [],
@@ -524,7 +728,7 @@ async function deepScanWorkspace(workspaceRoot, options = {}) {
524
728
  } catch {
525
729
  return;
526
730
  }
527
- entries.sort((a, b) => a.name.localeCompare(b.name));
731
+ entries.sort((a, b3) => a.name.localeCompare(b3.name));
528
732
  for (const entry of entries) {
529
733
  const fullPath = (0, import_node_path2.join)(dir, entry.name);
530
734
  const relPath = (0, import_node_path2.relative)(workspaceRoot, fullPath);
@@ -555,7 +759,7 @@ async function deepScanWorkspace(workspaceRoot, options = {}) {
555
759
  } catch {
556
760
  return;
557
761
  }
558
- entries.sort((a, b) => a.name.localeCompare(b.name));
762
+ entries.sort((a, b3) => a.name.localeCompare(b3.name));
559
763
  for (const entry of entries) {
560
764
  const fullPath = (0, import_node_path2.join)(dir, entry.name);
561
765
  const relPath = (0, import_node_path2.join)(relDir, entry.name);
@@ -569,7 +773,7 @@ async function deepScanWorkspace(workspaceRoot, options = {}) {
569
773
  }
570
774
  }
571
775
  await walk(workspaceRoot, 0);
572
- const scored = allFiles.map((f) => ({ path: f, score: scoreFile(f) })).sort((a, b) => b.score - a.score || a.path.localeCompare(b.path)).slice(0, opts.maxFiles);
776
+ const scored = allFiles.map((f) => ({ path: f, score: scoreFile(f) })).sort((a, b3) => b3.score - a.score || a.path.localeCompare(b3.path)).slice(0, opts.maxFiles);
573
777
  let totalBytes = 0;
574
778
  const scannedFiles = [];
575
779
  for (const { path: relPath, score } of scored) {
@@ -598,7 +802,7 @@ async function deepScanWorkspace(workspaceRoot, options = {}) {
598
802
  for (const [signal, paths] of Object.entries(STACK_SIGNAL_PATHS)) {
599
803
  if (paths.length > 0) {
600
804
  stackSignals[signal] = paths.some(
601
- (p) => allPathsSet.has(p) || [...allPathsSet].some((ap) => ap.replace(/\\/g, "/").includes(p))
805
+ (p2) => allPathsSet.has(p2) || [...allPathsSet].some((ap) => ap.replace(/\\/g, "/").includes(p2))
602
806
  );
603
807
  }
604
808
  }
@@ -611,6 +815,7 @@ ${pathBlob}
611
815
  ${contentBlob}`;
612
816
  stackSignals.hasSupabase = Boolean(stackSignals.hasSupabase) || hasPackage(packageDepsLower, ["@supabase/"]) || /\bsupabase\b/.test(repoBlob);
613
817
  stackSignals.hasStripe = hasPackage(packageDepsLower, ["stripe"]) || /\bstripe_(secret_key|webhook_secret)\b/.test(repoBlob) || /stripe/.test(pathBlob);
818
+ stackSignals.hasPolar = hasPackage(packageDepsLower, ["@polar-sh/"]) || /\bpolar_(access_token|webhook_secret|pro_product_id|sandbox)\b/.test(repoBlob) || /\bpolar\b/.test(pathBlob);
614
819
  stackSignals.hasClerk = hasPackage(packageDepsLower, ["@clerk/"]) || /\bclerk_(secret_key|publishable_key)\b|\bnext_public_clerk_/.test(repoBlob);
615
820
  stackSignals.hasAuthJs = hasPackage(packageDepsLower, ["next-auth", "@auth/"]) || /\b(auth_secret|nextauth_secret)\b|\bauth\.js\b/.test(repoBlob);
616
821
  stackSignals.hasAuth = Boolean(stackSignals.hasAuth) || Boolean(stackSignals.hasClerk) || Boolean(stackSignals.hasAuthJs) || /\bauthmiddleware\b|\bclerkmiddleware\b/.test(repoBlob);
@@ -628,7 +833,7 @@ ${contentBlob}`;
628
833
  stackSignals.hasMongoose = packageDepsLower.includes("mongoose");
629
834
  stackSignals.hasTests = Boolean(stackSignals.hasTests) || hasPackage(packageDepsLower, ["vitest", "jest", "@playwright/test", "cypress"]) || /\.test\.[jt]sx?\b|\.spec\.[jt]sx?\b/.test(pathBlob);
630
835
  stackSignals.hasRateLimit = packageDeps.some(
631
- (d) => ["express-rate-limit", "rate-limiter-flexible", "@fastify/rate-limit", "upstash"].some((r) => d.includes(r))
836
+ (d3) => ["express-rate-limit", "rate-limiter-flexible", "@fastify/rate-limit", "upstash"].some((r) => d3.includes(r))
632
837
  ) || scanContainsRateLimit(scannedFiles);
633
838
  stackSignals.hasCI = Boolean(stackSignals.hasCI) || /(^|\n)\.github\/workflows\/[^/\n]+\.ya?ml(\n|$)|(^|\n)(\.gitlab-ci\.yml|circle\.yml)(\n|$)/i.test(pathBlob);
634
839
  stackSignals.hasRobots = Boolean(stackSignals.hasRobots) || /(^|\n|\/)robots\.txt(\n|$)/i.test(pathBlob);
@@ -675,7 +880,7 @@ function scoreFile(relPath) {
675
880
  "app.ts"
676
881
  ].includes(name)) score += 5;
677
882
  if (/types?|\.d\.ts/.test(name)) score += 3;
678
- if (parts.some((p) => ["hooks", "store", "stores", "context"].includes(p))) score += 4;
883
+ if (parts.some((p2) => ["hooks", "store", "stores", "context"].includes(p2))) score += 4;
679
884
  if (/db|database/i.test(relPath)) score += 5;
680
885
  score -= Math.max(0, parts.length - 3) * 0.5;
681
886
  return score;
@@ -931,10 +1136,10 @@ function rootGapKey(gap) {
931
1136
  }
932
1137
  return slugify([gap.category, gap.copyPrompt].join(" ")) || gap.id;
933
1138
  }
934
- function mergeToolSuggestions(a, b) {
1139
+ function mergeToolSuggestions(a, b3) {
935
1140
  const seen = /* @__PURE__ */ new Set();
936
1141
  const out = [];
937
- for (const tool of [...a, ...b]) {
1142
+ for (const tool of [...a, ...b3]) {
938
1143
  const key = `${tool.name.trim().toLowerCase()}|${tool.url.trim().toLowerCase()}`;
939
1144
  if (!seen.has(key)) {
940
1145
  seen.add(key);
@@ -943,15 +1148,15 @@ function mergeToolSuggestions(a, b) {
943
1148
  }
944
1149
  return out;
945
1150
  }
946
- function mergeRootGaps(a, b) {
947
- const severity = SEVERITY_RANK[b.severity] > SEVERITY_RANK[a.severity] ? b.severity : a.severity;
1151
+ function mergeRootGaps(a, b3) {
1152
+ const severity = SEVERITY_RANK[b3.severity] > SEVERITY_RANK[a.severity] ? b3.severity : a.severity;
948
1153
  return {
949
1154
  ...a,
950
1155
  severity,
951
- detail: b.detail.length > a.detail.length ? b.detail : a.detail,
952
- copyPrompt: b.copyPrompt.length > a.copyPrompt.length ? b.copyPrompt : a.copyPrompt,
953
- toolSuggestions: mergeToolSuggestions(a.toolSuggestions, b.toolSuggestions),
954
- mcpSuggestion: a.mcpSuggestion ?? b.mcpSuggestion,
1156
+ detail: b3.detail.length > a.detail.length ? b3.detail : a.detail,
1157
+ copyPrompt: b3.copyPrompt.length > a.copyPrompt.length ? b3.copyPrompt : a.copyPrompt,
1158
+ toolSuggestions: mergeToolSuggestions(a.toolSuggestions, b3.toolSuggestions),
1159
+ mcpSuggestion: a.mcpSuggestion ?? b3.mcpSuggestion,
955
1160
  primaryMapCategory: a.primaryMapCategory,
956
1161
  affectedMapCategories: a.affectedMapCategories
957
1162
  };
@@ -995,7 +1200,7 @@ function normalizeChecklist(v) {
995
1200
  };
996
1201
  }
997
1202
  function normalizeModelOutput(raw) {
998
- const gaps = Array.isArray(raw.gaps) ? raw.gaps.map(normalizeGap).filter((g) => g !== null) : [];
1203
+ const gaps = Array.isArray(raw.gaps) ? raw.gaps.map(normalizeGap).filter((g2) => g2 !== null) : [];
999
1204
  const uniqueGaps = dedupeRootGaps(gaps);
1000
1205
  return {
1001
1206
  score: stableVisibleScore(raw.score),
@@ -1301,6 +1506,11 @@ var PROVIDERS = [
1301
1506
  },
1302
1507
  verification: { supportsReadOnly: false }
1303
1508
  }),
1509
+ provider("polar", "Polar", ["polar", "polarsh"], ["payments"], ["payments"], "polar", {
1510
+ docsUrl: "https://polar.sh/docs",
1511
+ dashboardUrl: "https://polar.sh/dashboard",
1512
+ verification: { supportsReadOnly: false }
1513
+ }),
1304
1514
  provider("vercel", "Vercel", ["vercel"], ["deployment"], ["deployment"], "vercel", {
1305
1515
  docsUrl: "https://vercel.com/docs",
1306
1516
  dashboardUrl: "https://vercel.com/dashboard",
@@ -1768,7 +1978,7 @@ function buildAreas(providerMissions) {
1768
1978
  // ../../src/station/promptBuilder.ts
1769
1979
  function buildAnalysisPrompt(scan, specContent, productionConnectionContext, scannerEvidenceContext, stackWiringContext, stackAutomationContext) {
1770
1980
  const sections = [];
1771
- const detectedStack = Object.entries(scan.stackSignals).filter(([, v]) => v === true).map(([k]) => k).join(", ");
1981
+ const detectedStack = Object.entries(scan.stackSignals).filter(([, v]) => v === true).map(([k3]) => k3).join(", ");
1772
1982
  sections.push(`## PROJECT CONTEXT
1773
1983
  Total files in repo: ${scan.totalFilesScanned}
1774
1984
  Files analyzed: ${scan.files.length}
@@ -2285,6 +2495,8 @@ function envPatternFor(provider2) {
2285
2495
  return /\bstripe_(secret_key|webhook_secret)\b/i;
2286
2496
  case "paddle":
2287
2497
  return /\bpaddle_(api_key|webhook_secret|client_token)\b/i;
2498
+ case "polar":
2499
+ return /\bpolar_(access_token|webhook_secret|pro_product_id|sandbox)\b/i;
2288
2500
  case "sentry":
2289
2501
  return /\bsentry_dsn\b/i;
2290
2502
  case "posthog":
@@ -2305,6 +2517,8 @@ function webhookPatternFor(provider2) {
2305
2517
  return /\bstripe\b.*\bwebhook\b|\bwebhook\b.*\bstripe\b/i;
2306
2518
  case "paddle":
2307
2519
  return /\bpaddle\b.*\bwebhook\b|\bwebhook\b.*\bpaddle\b/i;
2520
+ case "polar":
2521
+ return /\bpolar\b.*\bwebhook\b|\bwebhook\b.*\bpolar\b/i;
2308
2522
  default:
2309
2523
  return /\bwebhook\b/i;
2310
2524
  }
@@ -3688,6 +3902,7 @@ function analyzeStackWiring(scan) {
3688
3902
  analyzePlanetScaleDatabase(ctx),
3689
3903
  analyzeStripePayments(ctx),
3690
3904
  analyzePaddlePayments(ctx),
3905
+ analyzePolarPayments(ctx),
3691
3906
  analyzeVercelDeployment(ctx),
3692
3907
  analyzeNetlifyDeployment(ctx),
3693
3908
  analyzeAwsDeployment(ctx),
@@ -4017,6 +4232,26 @@ function analyzePaddlePayments(ctx) {
4017
4232
  ]
4018
4233
  });
4019
4234
  }
4235
+ function analyzePolarPayments(ctx) {
4236
+ return summarize({
4237
+ key: "polar-payments",
4238
+ provider: "polar",
4239
+ providerLabel: "Polar",
4240
+ area: "payments",
4241
+ areaLabel: "Payments",
4242
+ promptSubject: "Polar payments",
4243
+ items: [
4244
+ item2("api-client-found", "Polar SDK or API client found", hasPackage2(ctx, [/@polar-sh\//]) || Boolean(ctx.scan.stackSignals.hasPolar) || /api\.polar\.sh|sandbox-api\.polar\.sh|polarbillingservice/i.test(ctx.contentBlob), packageEvidence2(ctx, [/@polar-sh\//]).concat(fileEvidence(ctx, /api\.polar\.sh|sandbox-api\.polar\.sh|polarbillingservice/i)), "Add a Polar SDK or server-side API client for checkout and customer state."),
4245
+ item2("env-names-documented", "Polar env names documented", /polar_access_token|polar_webhook_secret|polar_pro_product_id/i.test(ctx.contentBlob), envEvidence2(ctx, [/polar_access_token/i, /polar_webhook_secret/i, /polar_pro_product_id/i, /polar_sandbox/i]), "Document POLAR_ACCESS_TOKEN, POLAR_WEBHOOK_SECRET, and product ID env names in safe examples."),
4246
+ item2("checkout-found", "Polar checkout flow found", /polar.*checkout|checkout.*polar|\/checkouts\b|createcheckoutsession/i.test(ctx.contentBlob + "\n" + ctx.pathBlob), fileEvidence(ctx, /polar.*checkout|checkout.*polar|\/checkouts\b|createcheckoutsession/i), "Add a server-side Polar checkout flow for paid plans."),
4247
+ item2("webhook-route-found", "Polar webhook route found", /polar.*webhook|webhook.*polar/i.test(ctx.pathBlob + "\n" + ctx.contentBlob), pathEvidence2(ctx, /polar.*webhook|webhook.*polar/i).concat(fileEvidence(ctx, /polar.*webhook|webhook.*polar/i)), "Add a Polar webhook route for subscription and customer lifecycle events."),
4248
+ item2("webhook-signature-found", "Polar webhook verification found", /polar_webhook_secret|verify.*polar|polar.*signature|webhook.*signature/i.test(ctx.contentBlob), fileEvidence(ctx, /polar_webhook_secret|verify.*polar|polar.*signature|webhook.*signature/i), "Verify Polar webhook signatures before processing billing events."),
4249
+ item2("customer-state-found", "Polar customer or subscription state found", /external_customer_id|customer_portal|customer[_-]?state|polar_subscription_id|polar_customer_id/i.test(ctx.contentBlob), fileEvidence(ctx, /external_customer_id|customer_portal|customer[_-]?state|polar_subscription_id|polar_customer_id/i), "Persist Polar customer/subscription state and expose a customer portal path for paid users."),
4250
+ secretSafetyItem(ctx, "secret-not-exposed", "Polar secret not exposed to frontend", /polar_access_token|polar_webhook_secret/i, "Move Polar access tokens and webhook secrets to server-only code."),
4251
+ manualItem("production-dashboard-checked", "Production Polar products and webhooks checked", "Confirm products, prices, customer portal, webhook URL, and event list in Polar Dashboard.")
4252
+ ]
4253
+ });
4254
+ }
4020
4255
  function analyzeNeonDatabase(ctx) {
4021
4256
  return databaseSummary(ctx, {
4022
4257
  key: "neon-database",
@@ -4950,7 +5185,7 @@ function collectProductionConnectionSummaries(productionConnections) {
4950
5185
  function providerLabel2(provider2) {
4951
5186
  return PROVIDER_LABELS[provider2] ?? provider2.replace(
4952
5187
  /(^|-)([a-z])/g,
4953
- (_, prefix, letter) => `${prefix === "-" ? " " : ""}${letter.toUpperCase()}`
5188
+ (_3, prefix, letter) => `${prefix === "-" ? " " : ""}${letter.toUpperCase()}`
4954
5189
  );
4955
5190
  }
4956
5191
 
@@ -5100,6 +5335,111 @@ async function loadProductionConnectionChoices(deps) {
5100
5335
  }
5101
5336
  }
5102
5337
 
5338
+ // ../../src/station/stackMapProviderOptions.ts
5339
+ var STACK_MAP_PROVIDER_OPTIONS = {
5340
+ appFlow: [
5341
+ { name: "Figma", toolName: "Figma", description: "Map flows, screens, and onboarding states" },
5342
+ { name: "Storybook", toolName: "Storybook", description: "Component states and UI variants" },
5343
+ { name: "Product Spec", toolName: "product-spec", description: "PRD, acceptance criteria, and success goals" },
5344
+ { name: "Route Map", toolName: "route-map", description: "Routes, navigation, and protected paths" }
5345
+ ],
5346
+ frontend: [
5347
+ { name: "React", toolName: "React", description: "Component UI and client state" },
5348
+ { name: "Vue", toolName: "Vue", description: "Vue or Nuxt product UI" },
5349
+ { name: "Svelte", toolName: "Svelte", description: "SvelteKit routes and components" },
5350
+ { name: "Angular", toolName: "Angular", description: "Angular components and router" }
5351
+ ],
5352
+ backend: [
5353
+ { name: "Node.js", toolName: "Node.js", description: "API routes and server behavior" },
5354
+ { name: "Python / FastAPI", toolName: "Python", description: "Python API services and validation" },
5355
+ { name: "Rails", toolName: "Rails", description: "Rails routes, controllers, and models" },
5356
+ { name: "Go", toolName: "Go", description: "Go HTTP handlers and services" }
5357
+ ],
5358
+ security: [
5359
+ { name: "Rate limiting", toolName: "rate-limit", description: "Protect API routes from abuse and retry loops" },
5360
+ { name: "Bot protection", toolName: "bot protection", description: "Screen bots before expensive flows" },
5361
+ { name: "Secrets hygiene", toolName: "secrets hygiene", description: "Keep env examples and secret files safe" }
5362
+ ],
5363
+ auth: [
5364
+ { name: "Clerk", toolName: "Clerk", description: "Managed auth" },
5365
+ { name: "Auth.js", toolName: "Auth.js", description: "Framework auth" },
5366
+ { name: "Supabase Auth", toolName: "Supabase Auth", description: "Auth with Supabase" },
5367
+ { name: "Auth0", toolName: "Auth0", description: "Enterprise identity and social login" },
5368
+ { name: "Better Auth", toolName: "better-auth", description: "Type-safe auth in your codebase" }
5369
+ ],
5370
+ database: [
5371
+ { name: "Supabase", toolName: "Supabase", description: "Postgres + auth + storage" },
5372
+ { name: "Neon", toolName: "Neon", description: "Serverless Postgres" },
5373
+ { name: "Turso", toolName: "Turso", description: "Edge SQLite" },
5374
+ { name: "MongoDB", toolName: "MongoDB", description: "Document database" },
5375
+ { name: "PlanetScale", toolName: "PlanetScale", description: "Serverless MySQL" }
5376
+ ],
5377
+ payments: [
5378
+ { name: "Stripe", toolName: "Stripe", description: "Global standard" },
5379
+ { name: "Paddle", toolName: "Paddle", description: "MoR subscriptions" },
5380
+ { name: "Polar", toolName: "Polar", description: "Developer-native MoR billing" },
5381
+ { name: "Lemon Squeezy", toolName: "Lemon Squeezy", description: "Digital products and SaaS billing" }
5382
+ ],
5383
+ deployment: [
5384
+ { name: "Vercel", toolName: "Vercel", description: "Preview, env, domains" },
5385
+ { name: "Netlify", toolName: "Netlify", description: "Static and serverless deploy" },
5386
+ { name: "Render", toolName: "Render", description: "Simple web services and background jobs" },
5387
+ { name: "Railway", toolName: "Railway", description: "Fast deploy loops and managed infra" },
5388
+ { name: "Cloudflare", toolName: "Cloudflare", description: "Pages, Workers, DNS, and edge stack" },
5389
+ { name: "AWS", toolName: "AWS", description: "Cloud infrastructure" }
5390
+ ],
5391
+ landing: [
5392
+ { name: "Supabase", toolName: "Supabase", description: "Waitlist and onboarding data" },
5393
+ { name: "PostHog", toolName: "PostHog", description: "Activation tracking" }
5394
+ ],
5395
+ monitoring: [
5396
+ { name: "Sentry", toolName: "Sentry", description: "Errors and replay" },
5397
+ { name: "PostHog", toolName: "PostHog", description: "Analytics and events" },
5398
+ { name: "LogRocket", toolName: "LogRocket", description: "Session replay" }
5399
+ ],
5400
+ testing: [
5401
+ { name: "Vitest", toolName: "Vitest", description: "Unit coverage" },
5402
+ { name: "Playwright", toolName: "Playwright", description: "Browser flows" },
5403
+ { name: "GitHub", toolName: "GitHub", description: "Actions CI and PR checks" },
5404
+ { name: "GitLab", toolName: "GitLab", description: "Pipelines and merge checks" }
5405
+ ],
5406
+ errorHandling: [
5407
+ { name: "Sentry", toolName: "Sentry", description: "Exception capture" },
5408
+ { name: "PostHog", toolName: "PostHog", description: "Error events" }
5409
+ ]
5410
+ };
5411
+ function stackMapProviderOptionsForArea(areaKey) {
5412
+ const rows = STACK_MAP_PROVIDER_OPTIONS[areaKey] ?? [];
5413
+ return rows.slice(0, 6).map((row) => ({
5414
+ provider: row.toolName,
5415
+ label: row.name,
5416
+ description: row.description
5417
+ }));
5418
+ }
5419
+
5420
+ // src/report/hydrateArtifact.ts
5421
+ var STACK_MAP_AREAS = [
5422
+ "appFlow",
5423
+ "frontend",
5424
+ "backend",
5425
+ "auth",
5426
+ "database",
5427
+ "payments",
5428
+ "deployment",
5429
+ "monitoring",
5430
+ "security",
5431
+ "testing",
5432
+ "landing",
5433
+ "errorHandling"
5434
+ ];
5435
+ function hydrateArtifactForReport(artifact) {
5436
+ const providerOptions = { ...artifact.providerOptions ?? {} };
5437
+ for (const area of STACK_MAP_AREAS) {
5438
+ providerOptions[area] = stackMapProviderOptionsForArea(area);
5439
+ }
5440
+ return { ...artifact, providerOptions };
5441
+ }
5442
+
5103
5443
  // src/runScan.ts
5104
5444
  function computeProductionCorePercent(missionGraph) {
5105
5445
  const areas = missionGraph.areas ?? [];
@@ -5109,14 +5449,6 @@ function computeProductionCorePercent(missionGraph) {
5109
5449
  const sum = areas.reduce((acc, area) => acc + (area.readinessPercent ?? 0), 0);
5110
5450
  return Math.round(sum / areas.length);
5111
5451
  }
5112
- function buildProviderOptions() {
5113
- const byArea = productionProvidersByArea();
5114
- const options = {};
5115
- for (const [area, providers] of Object.entries(byArea)) {
5116
- options[area] = providers.map((provider2) => ({ provider: provider2, label: providerLabel(provider2) }));
5117
- }
5118
- return options;
5119
- }
5120
5452
  function toArtifact(workspacePath, result, selectedProviders) {
5121
5453
  return {
5122
5454
  version: 1,
@@ -5129,10 +5461,10 @@ function toArtifact(workspacePath, result, selectedProviders) {
5129
5461
  gaps: result.gaps,
5130
5462
  missionGraph: result.missionGraph,
5131
5463
  stackWiring: result.stackWiring,
5464
+ stackAutomation: result.stackAutomation,
5132
5465
  providerRegistry: result.providerRegistry,
5133
5466
  verificationSummary: result.verificationSummary,
5134
5467
  productionCorePercent: computeProductionCorePercent(result.missionGraph),
5135
- providerOptions: buildProviderOptions(),
5136
5468
  selectedProviders,
5137
5469
  usage: result.usage
5138
5470
  };
@@ -5178,7 +5510,9 @@ async function runProjectScan(options) {
5178
5510
  selectedProviders[area] = choice.provider;
5179
5511
  }
5180
5512
  }
5181
- const artifact = toArtifact(workspacePath, result, selectedProviders);
5513
+ const artifact = hydrateArtifactForReport(
5514
+ toArtifact(workspacePath, result, selectedProviders)
5515
+ );
5182
5516
  return { ok: true, artifact };
5183
5517
  } catch (error) {
5184
5518
  const message = error instanceof Error ? error.message : String(error);
@@ -5188,7 +5522,7 @@ async function runProjectScan(options) {
5188
5522
 
5189
5523
  // src/artifacts.ts
5190
5524
  var import_promises3 = require("node:fs/promises");
5191
- var import_node_path4 = require("node:path");
5525
+ var import_node_path5 = require("node:path");
5192
5526
 
5193
5527
  // src/report/agentSummary.ts
5194
5528
  var SEVERITY_ORDER = {
@@ -5197,12 +5531,12 @@ var SEVERITY_ORDER = {
5197
5531
  info: 2
5198
5532
  };
5199
5533
  function sortGaps(gaps) {
5200
- return [...gaps].sort((a, b) => {
5201
- const sev = SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity];
5534
+ return [...gaps].sort((a, b3) => {
5535
+ const sev = SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b3.severity];
5202
5536
  if (sev !== 0) {
5203
5537
  return sev;
5204
5538
  }
5205
- return a.title.localeCompare(b.title);
5539
+ return a.title.localeCompare(b3.title);
5206
5540
  });
5207
5541
  }
5208
5542
  function generateAgentSummary(artifact) {
@@ -5265,449 +5599,2625 @@ function generateAgentSummary(artifact) {
5265
5599
  `;
5266
5600
  }
5267
5601
 
5268
- // src/report/reportStyles.ts
5269
- var REPORT_STYLES = `
5270
- :root {
5271
- color-scheme: dark;
5272
- --vr-gold: rgba(232, 189, 98, 0.92);
5273
- --vr-bg: #050608;
5274
- --vr-panel: #0c0f12;
5275
- --vr-line: rgba(255, 255, 255, 0.1);
5276
- --vr-text: rgba(245, 242, 235, 0.96);
5277
- --vr-muted: rgba(245, 242, 235, 0.62);
5278
- --vr-ease-out: cubic-bezier(0.22, 1, 0.36, 1);
5279
- }
5280
- * { box-sizing: border-box; }
5281
- body {
5282
- margin: 0;
5283
- font-family: "Inter", "Segoe UI", system-ui, sans-serif;
5284
- background:
5285
- radial-gradient(circle at 50% -10%, rgba(232, 189, 98, 0.08), transparent 45%),
5286
- var(--vr-bg);
5287
- color: var(--vr-text);
5288
- min-height: 100vh;
5289
- }
5290
- .report-header {
5291
- display: flex; justify-content: space-between; align-items: center;
5292
- gap: 16px; padding: 18px 26px; border-bottom: 1px solid var(--vr-line);
5293
- }
5294
- .report-header h1 { margin: 0; font-size: 1.05rem; font-weight: 800; letter-spacing: 0.04em; }
5295
- .report-header p { margin: 4px 0 0; color: var(--vr-muted); font-size: 0.8rem; }
5296
- .report-score {
5297
- padding: 6px 14px; border-radius: 999px; border: 1px solid rgba(232, 189, 98, 0.4);
5298
- color: var(--vr-gold); font-size: 0.82rem; font-weight: 800; letter-spacing: 0.04em;
5299
- }
5300
- .report-workspace {
5301
- display: grid; grid-template-columns: minmax(0, 1fr) minmax(320px, 420px);
5302
- gap: 18px; padding: 22px; align-items: start;
5303
- }
5304
- @media (max-width: 920px) { .report-workspace { grid-template-columns: 1fr; } }
5305
-
5306
- .studio-system-map {
5307
- position: relative; min-height: 600px; display: grid;
5308
- grid-template-rows: minmax(560px, 1fr); gap: 14px; padding: 14px;
5309
- border-radius: 22px; border: 1px solid rgba(232, 189, 98, 0.2);
5310
- background:
5311
- radial-gradient(circle at 50% 48%, rgba(232, 189, 98, 0.14), transparent 18%),
5312
- radial-gradient(circle at 20% 20%, rgba(91, 205, 194, 0.08), transparent 25%),
5313
- radial-gradient(circle at 80% 76%, rgba(248, 113, 113, 0.06), transparent 24%),
5314
- linear-gradient(160deg, rgba(16, 16, 18, 0.98), rgba(5, 6, 8, 0.99));
5315
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.07), 0 18px 48px rgba(0, 0, 0, 0.42);
5316
- overflow: hidden;
5317
- }
5318
- .studio-node-layer {
5319
- position: relative; min-height: 560px; max-width: 680px;
5320
- width: min(100%, 680px); justify-self: center; overflow: hidden; border-radius: 16px;
5321
- }
5322
- .studio-connector-layer {
5323
- position: absolute; inset: 32px; pointer-events: none; opacity: 0.5;
5324
- background:
5325
- radial-gradient(circle at 50% 50%, transparent 0 88px, rgba(232, 189, 98, 0.26) 89px 90px, transparent 91px),
5326
- linear-gradient(90deg, transparent 8%, rgba(232, 189, 98, 0.18) 49%, rgba(232, 189, 98, 0.18) 51%, transparent 92%),
5327
- linear-gradient(180deg, transparent 10%, rgba(232, 189, 98, 0.14) 49%, rgba(232, 189, 98, 0.14) 51%, transparent 90%);
5328
- mask-image: radial-gradient(circle at center, black, transparent 76%);
5329
- }
5330
- .studio-core-node {
5331
- position: absolute; left: 50%; top: 50%; z-index: 3; width: 132px; height: 132px;
5332
- transform: translate(-50%, -50%); display: grid; place-items: center; align-content: center;
5333
- gap: 5px; border-radius: 999px; border: 1px solid rgba(232, 189, 98, 0.48);
5334
- background:
5335
- radial-gradient(circle at 50% 22%, rgba(255, 233, 166, 0.24), transparent 42%),
5336
- linear-gradient(180deg, rgba(42, 32, 13, 0.98), rgba(10, 10, 12, 0.99));
5337
- color: rgba(255, 248, 226, 0.98); text-align: center;
5338
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12), 0 18px 44px rgba(0, 0, 0, 0.48), 0 0 0 10px rgba(232, 189, 98, 0.05);
5339
- }
5340
- .studio-core-node span { color: var(--vr-gold); font-size: 11px; font-weight: 900; letter-spacing: 0.12em; line-height: 1; }
5341
- .studio-core-node strong { font-size: 30px; font-weight: 900; line-height: 1; }
5342
- .studio-core-node small { color: rgba(245, 242, 235, 0.66); font-size: 10px; font-weight: 760; letter-spacing: 0.08em; text-transform: uppercase; }
5343
-
5344
- .studio-node {
5345
- position: absolute; z-index: 2; width: 96px; height: 96px; display: grid;
5346
- grid-template-columns: 10px minmax(0, 1fr); align-items: center; align-content: center;
5347
- gap: 2px 6px; padding: 12px 10px; border-radius: 999px;
5348
- border: 1px solid rgba(255, 255, 255, 0.13);
5349
- background: linear-gradient(160deg, rgba(22, 23, 26, 0.96), rgba(8, 9, 11, 0.99));
5350
- color: var(--vr-text); font: inherit; text-align: left; cursor: pointer;
5351
- transform: translate(-50%, -50%);
5352
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.07), 0 12px 28px rgba(0, 0, 0, 0.34);
5353
- transition: transform 170ms var(--vr-ease-out), border-color 150ms ease, box-shadow 150ms ease;
5354
- }
5355
- .studio-node:hover { transform: translate(-50%, calc(-50% - 2px)); border-color: rgba(232, 189, 98, 0.34); }
5356
- .studio-node:focus-visible { outline: 2px solid rgba(232, 189, 98, 0.78); outline-offset: 3px; }
5357
- .studio-node--selected {
5358
- border-color: rgba(232, 189, 98, 0.66);
5359
- background:
5360
- radial-gradient(circle at 20% 10%, rgba(232, 189, 98, 0.18), transparent 42%),
5361
- linear-gradient(160deg, rgba(30, 25, 16, 0.98), rgba(9, 9, 11, 0.99));
5362
- box-shadow: inset 0 1px 0 rgba(255, 248, 226, 0.1), 0 0 0 1px rgba(232, 189, 98, 0.16), 0 16px 36px rgba(0, 0, 0, 0.4);
5363
- }
5364
- .studio-node--critical { border-color: rgba(248, 113, 113, 0.58); }
5365
- .studio-node--warning { border-color: rgba(245, 158, 11, 0.5); }
5366
- .studio-node--in-project {
5367
- border-color: rgba(93, 240, 175, 0.66);
5368
- background:
5369
- radial-gradient(circle at 50% 0%, rgba(93, 240, 175, 0.18), transparent 48%),
5370
- linear-gradient(160deg, rgba(7, 34, 28, 0.92), rgba(4, 10, 14, 0.98));
5371
- }
5372
- .studio-node__dot { grid-row: 1 / 3; width: 9px; height: 9px; border-radius: 999px; background: #d4af37; box-shadow: 0 0 0 4px rgba(212, 175, 55, 0.12); }
5373
- .studio-node--critical .studio-node__dot { background: #ef6f6f; box-shadow: 0 0 0 4px rgba(239, 111, 111, 0.13); }
5374
- .studio-node--warning .studio-node__dot { background: #f0b94e; box-shadow: 0 0 0 4px rgba(240, 185, 78, 0.13); }
5375
- .studio-node__title, .studio-node__provider, .studio-node__meta { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
5376
- .studio-node__title { color: rgba(255, 253, 247, 0.98); font-size: 10.5px; font-weight: 860; line-height: 1.05; }
5377
- .studio-node__provider { color: rgba(245, 242, 235, 0.64); font-size: 9px; font-weight: 700; line-height: 1.05; }
5378
- .studio-node__meta { grid-column: 2; color: var(--vr-gold); font-size: 9px; font-weight: 850; line-height: 1.05; }
5379
- .studio-node--critical .studio-node__meta { color: #ef9a9a; }
5380
-
5381
- .studio-node--appFlow { left: 22%; top: 18%; }
5382
- .studio-node--frontend { left: 50%; top: 12%; }
5383
- .studio-node--backend { left: 78%; top: 18%; }
5384
- .studio-node--auth { left: 14%; top: 38%; }
5385
- .studio-node--database { left: 50%; top: 32%; }
5386
- .studio-node--payments { left: 86%; top: 40%; }
5387
- .studio-node--deployment { left: 18%; top: 64%; }
5388
- .studio-node--monitoring { left: 50%; top: 68%; }
5389
- .studio-node--security { left: 82%; top: 64%; }
5390
- .studio-node--testing { left: 28%; top: 84%; }
5391
- .studio-node--landing { left: 50%; top: 88%; }
5392
- .studio-node--errorHandling { left: 72%; top: 84%; }
5393
-
5394
- .studio-setup-panel {
5395
- border: 1px solid var(--vr-line); border-radius: 18px; background: var(--vr-panel);
5396
- padding: 20px 20px 26px; position: sticky; top: 22px; max-height: calc(100vh - 44px); overflow: auto;
5397
- }
5398
- .studio-setup-panel__eyebrow { margin: 0; font-size: 0.66rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--vr-muted); }
5399
- .studio-setup-panel__title { margin: 6px 0 0; font-size: 1.05rem; font-weight: 800; }
5400
- .studio-setup-panel__hint { margin: 6px 0 0; color: var(--vr-muted); font-size: 0.82rem; line-height: 1.5; }
5401
- .panel-section { margin-top: 18px; }
5402
- .panel-section h3 { margin: 0 0 10px; font-size: 0.7rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--vr-muted); }
5403
- .provider-switch { display: flex; flex-wrap: wrap; gap: 6px; }
5404
- .provider-chip {
5405
- border: 1px solid var(--vr-line); background: rgba(255,255,255,0.02); color: var(--vr-text);
5406
- border-radius: 999px; padding: 5px 11px; font-size: 0.76rem; cursor: pointer; transition: border-color 0.15s;
5407
- }
5408
- .provider-chip:hover { border-color: rgba(232, 189, 98, 0.45); }
5409
- .provider-chip--active { border-color: rgba(93, 240, 175, 0.66); color: rgba(111, 255, 190, 0.96); background: rgba(93, 240, 175, 0.08); }
5410
- .switch-hint { margin-top: 8px; font-size: 0.75rem; color: var(--vr-muted); }
5411
- .switch-hint code { color: var(--vr-gold); }
5412
- .check, .gap-card { border: 1px solid var(--vr-line); border-radius: 12px; padding: 10px 12px; margin-bottom: 8px; background: rgba(0,0,0,0.22); }
5413
- .check strong, .gap-card strong { display: block; font-size: 0.86rem; }
5414
- .check .status, .gap-card p { margin: 4px 0 0; font-size: 0.78rem; color: var(--vr-muted); line-height: 1.45; }
5415
- .status-passed { color: rgba(111, 255, 190, 0.96); }
5416
- .status-missing, .status-failed { color: #ef9a9a; }
5417
- .status-needs-connection { color: #f0b94e; }
5418
- .copy-btn { margin-top: 8px; border: 1px solid var(--vr-line); background: transparent; color: var(--vr-text); border-radius: 8px; padding: 6px 10px; font-size: 0.76rem; cursor: pointer; }
5419
- .copy-btn:hover { border-color: rgba(232, 189, 98, 0.45); }
5420
- `;
5421
-
5422
- // src/report/reportHtml.ts
5423
- function escapeHtml(value) {
5424
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
5425
- }
5426
- var CATEGORY_META = [
5427
- { key: "appFlow", label: "App Flow" },
5428
- { key: "frontend", label: "Frontend" },
5429
- { key: "backend", label: "Backend / API" },
5430
- { key: "auth", label: "Auth" },
5431
- { key: "database", label: "Database" },
5432
- { key: "payments", label: "Payments" },
5433
- { key: "deployment", label: "Deployment" },
5434
- { key: "monitoring", label: "Monitoring" },
5435
- { key: "security", label: "Security" },
5436
- { key: "testing", label: "Testing" },
5437
- { key: "landing", label: "Landing / Onboarding" },
5438
- { key: "errorHandling", label: "Error Handling" }
5439
- ];
5440
- function gapCountForArea(artifact, areaKey) {
5441
- return artifact.gaps.filter((g) => g.primaryMapCategory === areaKey).length;
5602
+ // src/report/missionSelection.ts
5603
+ function normalizeProviderToken2(value) {
5604
+ return value.toLowerCase().replace(/&/g, "and").replace(/[^a-z0-9]+/g, "");
5605
+ }
5606
+ function missionMatchesProvider(mission, provider2) {
5607
+ const current = normalizeProviderToken2(provider2);
5608
+ return normalizeProviderToken2(mission.provider || mission.providerLabel || mission.key) === current || normalizeProviderToken2(mission.providerLabel || mission.provider || mission.key) === current;
5609
+ }
5610
+ function missionEvidenceScore(mission) {
5611
+ const repoVerified = mission.checks.filter((check) => check.evidenceClass === "repo-verified" || check.status === "passed").length;
5612
+ const missing = mission.checks.filter(
5613
+ (check) => check.evidenceClass === "missing-repo-fix" || check.status === "missing" || check.status === "failed"
5614
+ ).length;
5615
+ return repoVerified * 100 + (mission.readinessPercent ?? 0) - missing;
5616
+ }
5617
+ function preferredMissionForArea(area, selectedProvider) {
5618
+ const missions = area?.providerMissions ?? [];
5619
+ if (missions.length === 0) {
5620
+ return void 0;
5621
+ }
5622
+ const selected = selectedProvider ? missions.find((mission) => missionMatchesProvider(mission, selectedProvider)) : void 0;
5623
+ if (selected) {
5624
+ return selected;
5625
+ }
5626
+ return [...missions].sort((a, b3) => missionEvidenceScore(b3) - missionEvidenceScore(a))[0] ?? missions[0];
5442
5627
  }
5443
- function openChecksForArea(area) {
5444
- if (!area) {
5628
+ function openChecksForMission(mission) {
5629
+ if (!mission) {
5445
5630
  return 0;
5446
5631
  }
5447
- return area.providerMissions.flatMap((m) => m.checks).filter((c) => c.status === "missing" || c.status === "failed" || c.status === "needs-connection").length;
5448
- }
5449
- function buildNodeHtml(artifact, meta) {
5450
- const area = (artifact.missionGraph.areas ?? []).find((a) => a.key === meta.key);
5451
- const mission = area?.providerMissions[0];
5452
- const provider2 = mission?.providerLabel ?? "\u2014";
5453
- const readiness = mission?.readinessPercent ?? area?.readinessPercent ?? 0;
5454
- const openChecks = openChecksForArea(area);
5455
- const modelGaps = gapCountForArea(artifact, meta.key);
5456
- const stateClass = modelGaps > 0 ? " studio-node--critical" : openChecks > 0 ? " studio-node--warning" : area ? " studio-node--in-project" : "";
5457
- const meta1 = modelGaps > 0 ? `GAP ${modelGaps}` : openChecks > 0 ? `${openChecks} stack fix${openChecks === 1 ? "" : "es"}` : `${readiness}% health`;
5458
- return `<button type="button" class="studio-node studio-node--${escapeHtml(meta.key)}${stateClass}" data-area-key="${escapeHtml(meta.key)}" aria-label="${escapeHtml(meta.label)}">
5459
- <span class="studio-node__dot" aria-hidden="true"></span>
5460
- <span class="studio-node__title">${escapeHtml(meta.label)}</span>
5461
- <span class="studio-node__provider">${escapeHtml(provider2)}</span>
5462
- <span class="studio-node__meta">${escapeHtml(meta1)}</span>
5463
- </button>`;
5464
- }
5465
- function generateReportHtml(artifact) {
5466
- const dataJson = JSON.stringify(artifact).replace(/</g, "\\u003c");
5467
- const nodesHtml = CATEGORY_META.map((meta) => buildNodeHtml(artifact, meta)).join("\n");
5468
- return `<!DOCTYPE html>
5469
- <html lang="en">
5470
- <head>
5471
- <meta charset="utf-8" />
5472
- <meta name="viewport" content="width=device-width, initial-scale=1" />
5473
- <title>VibeRaven Launch Report</title>
5474
- <style>${REPORT_STYLES}</style>
5475
- </head>
5476
- <body>
5477
- <header class="report-header">
5478
- <div>
5479
- <h1>VIBERAVEN \xB7 LAUNCH REPORT</h1>
5480
- <p>${escapeHtml(artifact.workspacePath)} \xB7 ${escapeHtml(artifact.scannedAt)}</p>
5481
- </div>
5482
- <div class="report-score">Model ${artifact.score} \xB7 ${escapeHtml(artifact.scoreLabel)}</div>
5483
- </header>
5484
- <div class="report-workspace">
5485
- <section class="studio-system-map" aria-label="Production system map">
5486
- <div class="studio-node-layer">
5487
- <div class="studio-connector-layer" aria-hidden="true"></div>
5488
- <div class="studio-core-node" aria-label="Production core">
5489
- <span>VIBERAVEN</span>
5490
- <strong>${artifact.productionCorePercent}%</strong>
5491
- <small>Production core</small>
5492
- </div>
5493
- ${nodesHtml}
5494
- </div>
5495
- </section>
5496
- <aside class="studio-setup-panel" id="detail-panel" aria-live="polite">
5497
- <p class="studio-setup-panel__eyebrow">No section selected</p>
5498
- <p class="studio-setup-panel__title">Choose a node on the map</p>
5499
- <p class="studio-setup-panel__hint">${escapeHtml(artifact.summary || "Click any provider node to see stack checks, launch gaps, and switch providers.")}</p>
5500
- </aside>
5501
- </div>
5502
- <script type="application/json" id="scan-data">${dataJson}</script>
5503
- <script>${CLIENT_SCRIPT}</script>
5504
- </body>
5505
- </html>`;
5632
+ return mission.checks.filter(
5633
+ (check) => check.status === "missing" || check.status === "failed" || check.status === "needs-connection"
5634
+ ).length;
5506
5635
  }
5507
- var CLIENT_SCRIPT = `
5636
+
5637
+ // src/report/panelClientScript.ts
5638
+ var PANEL_CLIENT_SCRIPT = `
5639
+
5508
5640
  (function () {
5641
+
5509
5642
  var artifact = JSON.parse(document.getElementById('scan-data').textContent);
5643
+
5644
+ var defaultAreaKey = JSON.parse(document.getElementById('default-area-key').textContent);
5645
+
5646
+ var logoPayload = JSON.parse(document.getElementById('provider-logos').textContent);
5647
+
5510
5648
  var panel = document.getElementById('detail-panel');
5649
+
5511
5650
  var areas = (artifact.missionGraph && artifact.missionGraph.areas) || [];
5651
+
5512
5652
  var gaps = artifact.gaps || [];
5653
+
5513
5654
  var providerOptions = artifact.providerOptions || {};
5514
- var selectedProviders = artifact.selectedProviders || {};
5655
+
5656
+ var selectedProviders = Object.assign({}, artifact.selectedProviders || {});
5657
+
5658
+ var projectProviders = Object.assign({}, artifact.selectedProviders || {});
5659
+
5660
+ var stackAutomation = artifact.stackAutomation || {};
5661
+
5662
+ var STACK_AREAS = { database: 1, auth: 1, payments: 1, deployment: 1, monitoring: 1, security: 1 };
5663
+
5664
+ var CHOICE_HINTS = {
5665
+
5666
+ appFlow: 'Choose flow focus',
5667
+
5668
+ frontend: 'Choose frontend focus',
5669
+
5670
+ backend: 'Choose backend focus',
5671
+
5672
+ auth: 'Choose auth stack',
5673
+
5674
+ database: 'Choose database stack',
5675
+
5676
+ payments: 'Choose payment stack',
5677
+
5678
+ deployment: 'Choose deployment stack',
5679
+
5680
+ monitoring: 'Choose monitoring stack',
5681
+
5682
+ security: 'Choose security control',
5683
+
5684
+ testing: 'Choose coverage target',
5685
+
5686
+ landing: 'Choose launch item',
5687
+
5688
+ errorHandling: 'Choose reliability control'
5689
+
5690
+ };
5691
+
5515
5692
  var LABELS = {
5693
+
5516
5694
  appFlow: 'App Flow', frontend: 'Frontend', backend: 'Backend / API', auth: 'Auth',
5517
- database: 'Database', payments: 'Payments', deployment: 'Deployment', monitoring: 'Monitoring',
5695
+
5696
+ database: 'Database', payments: 'Payments', deployment: 'Deployment', monitoring: 'Monitoring / Analytics',
5697
+
5518
5698
  security: 'Security', testing: 'Testing', landing: 'Landing / Onboarding', errorHandling: 'Error Handling'
5699
+
5519
5700
  };
5520
5701
 
5702
+ var MAX_GROUP_ITEMS = 6;
5703
+
5704
+
5705
+
5521
5706
  function esc(s) {
5707
+
5522
5708
  return String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
5523
- }
5524
- function statusClass(status) {
5525
- if (status === 'passed') return 'status-passed';
5526
- if (status === 'needs-connection') return 'status-needs-connection';
5527
- return 'status-missing';
5528
- }
5529
5709
 
5530
- function providerSwitchHtml(areaKey, mission) {
5531
- var options = providerOptions[areaKey];
5532
- if (!options || !options.length) return '';
5533
- var current = selectedProviders[areaKey] || (mission ? (mission.provider || '') : '');
5534
- var chips = options.map(function (opt) {
5535
- var active = String(opt.provider).toLowerCase() === String(current).toLowerCase();
5536
- return '<button type="button" class="provider-chip' + (active ? ' provider-chip--active' : '') +
5537
- '" data-switch-area="' + esc(areaKey) + '" data-switch-provider="' + esc(opt.provider) + '">' + esc(opt.label) + '</button>';
5538
- }).join('');
5539
- return '<div class="panel-section"><h3>Switch provider</h3><div class="provider-switch">' + chips +
5540
- '</div><p class="switch-hint" id="switch-hint">Pick a provider, then rescan to re-map this area.</p></div>';
5541
5710
  }
5542
5711
 
5543
- function render(areaKey) {
5544
- var area = areas.find(function (a) { return a.key === areaKey; });
5545
- document.querySelectorAll('.studio-node').forEach(function (n) {
5546
- n.classList.toggle('studio-node--selected', n.getAttribute('data-area-key') === areaKey);
5547
- });
5548
- var label = (area && area.label) || LABELS[areaKey] || areaKey;
5549
- var missions = (area && area.providerMissions) || [];
5550
- var mission = missions[0];
5551
- var areaGaps = gaps.filter(function (g) { return g.primaryMapCategory === areaKey; });
5552
-
5553
- var checksHtml = missions.flatMap(function (m) {
5554
- return (m.checks || []).map(function (c) {
5555
- var ev = (c.evidence && c.evidence[0]) ? c.evidence[0] : (c.promptHint || '');
5556
- return '<div class="check"><strong>' + esc(c.label) + '</strong>' +
5557
- '<div class="status ' + statusClass(c.status) + '">' + esc(c.status) + '</div>' +
5558
- (ev ? '<div class="status">' + esc(ev) + '</div>' : '') + '</div>';
5559
- });
5560
- }).join('');
5561
-
5562
- var gapsHtml = areaGaps.map(function (g) {
5563
- return '<div class="gap-card"><strong>' + esc(g.title) + '</strong><p>' + esc(g.detail) +
5564
- '</p><button type="button" class="copy-btn" data-copy-gap="' + esc(g.id) + '">Copy agent prompt</button></div>';
5565
- }).join('');
5566
5712
 
5567
- var head =
5568
- '<p class="studio-setup-panel__eyebrow">' + esc(areaKey) + '</p>' +
5569
- '<p class="studio-setup-panel__title">' + esc(label) + '</p>' +
5570
- '<p class="studio-setup-panel__hint">' +
5571
- (missions.map(function (m) { return esc(m.providerLabel) + ' \xB7 ' + m.readinessPercent + '% repo readiness'; }).join(' \xB7 ') || 'No provider detected for this area.') +
5572
- '</p>';
5573
5713
 
5574
- panel.innerHTML = head +
5575
- providerSwitchHtml(areaKey, mission) +
5576
- '<div class="panel-section"><h3>Stack checks</h3>' + (checksHtml || '<p class="switch-hint">No checks for this area.</p>') + '</div>' +
5577
- '<div class="panel-section"><h3>Launch gaps</h3>' + (gapsHtml || '<p class="switch-hint">No model gaps tagged here.</p>') + '</div>';
5714
+ function attrEsc(s) {
5578
5715
 
5579
- panel.querySelectorAll('[data-copy-gap]').forEach(function (btn) {
5580
- btn.addEventListener('click', function () {
5581
- var gap = gaps.find(function (g) { return g.id === btn.getAttribute('data-copy-gap'); });
5582
- if (!gap) return;
5583
- navigator.clipboard.writeText(gap.copyPrompt).then(function () {
5584
- btn.textContent = 'Copied';
5585
- setTimeout(function () { btn.textContent = 'Copy agent prompt'; }, 1200);
5586
- });
5587
- });
5588
- });
5716
+ return esc(s).replace(/"/g, '&quot;').replace(/'/g, '&#39;');
5589
5717
 
5590
- panel.querySelectorAll('[data-switch-provider]').forEach(function (btn) {
5591
- btn.addEventListener('click', function () {
5592
- var a = btn.getAttribute('data-switch-area');
5593
- var p = btn.getAttribute('data-switch-provider');
5594
- var cmd = 'viberaven stack set ' + a + ' ' + p + ' && viberaven scan --open';
5595
- navigator.clipboard.writeText(cmd);
5596
- var hint = document.getElementById('switch-hint');
5597
- if (hint) hint.innerHTML = 'Copied: <code>' + esc(cmd) + '</code> \u2014 run it (or ask your agent) to re-map this area.';
5598
- panel.querySelectorAll('[data-switch-area="' + a + '"]').forEach(function (c) {
5599
- c.classList.toggle('provider-chip--active', c === btn);
5600
- });
5601
- });
5602
- });
5603
5718
  }
5604
5719
 
5605
- document.querySelectorAll('.studio-node').forEach(function (n) {
5606
- n.addEventListener('click', function () { render(n.getAttribute('data-area-key')); });
5607
- });
5608
- if (areas[0]) render(areas[0].key);
5609
- })();
5610
- `;
5611
5720
 
5612
- // src/artifacts.ts
5613
- async function writeScanArtifacts(options) {
5614
- const cwd = options.cwd ?? options.artifact.workspacePath;
5615
- const dir = getProjectArtifactsDir(cwd);
5616
- await (0, import_promises3.mkdir)(dir, { recursive: true });
5617
- const jsonPath = (0, import_node_path4.join)(dir, "last-scan.json");
5618
- const summaryPath = (0, import_node_path4.join)(dir, "agent-summary.md");
5619
- const reportPath = (0, import_node_path4.join)(dir, "report.html");
5620
- const json = `${JSON.stringify(options.artifact, null, 2)}
5621
- `;
5622
- const summary = generateAgentSummary(options.artifact);
5623
- const html = generateReportHtml(options.artifact);
5624
- await (0, import_promises3.writeFile)(jsonPath, json, "utf-8");
5625
- await (0, import_promises3.writeFile)(summaryPath, summary, "utf-8");
5626
- await (0, import_promises3.writeFile)(reportPath, html, "utf-8");
5627
- return { dir, jsonPath, summaryPath, reportPath };
5628
- }
5629
5721
 
5630
- // src/openBrowser.ts
5631
- var import_node_child_process = require("node:child_process");
5632
- async function openPathInBrowser(filePath) {
5633
- const absolute = filePath;
5634
- let command;
5635
- let args;
5636
- if (process.platform === "win32") {
5637
- command = "cmd";
5638
- args = ["/c", "start", "", absolute];
5639
- } else if (process.platform === "darwin") {
5640
- command = "open";
5641
- args = [absolute];
5642
- } else {
5643
- command = "xdg-open";
5644
- args = [absolute];
5645
- }
5646
- await new Promise((resolve, reject) => {
5647
- const child = (0, import_node_child_process.spawn)(command, args, { stdio: "ignore", shell: process.platform === "win32" });
5648
- child.on("error", reject);
5649
- child.on("exit", (code) => {
5650
- if (code === 0) {
5651
- resolve();
5652
- } else {
5653
- reject(new Error(`Could not open browser (exit ${code ?? "unknown"}). Open manually: ${absolute}`));
5654
- }
5655
- });
5656
- });
5657
- }
5722
+ function normKey(p) {
5723
+
5724
+ if (!p) return '';
5725
+
5726
+ var raw = String(p).trim().toLowerCase();
5727
+
5728
+ var compact = raw.replace(/[^a-z0-9]+/g, '');
5729
+
5730
+ if (logoPayload.aliases[raw]) return logoPayload.aliases[raw];
5731
+
5732
+ if (logoPayload.aliases[compact]) return logoPayload.aliases[compact];
5733
+
5734
+ if (logoPayload.logos[compact]) return compact;
5735
+
5736
+ var parts = raw.split('-').filter(Boolean);
5737
+
5738
+ if (parts.length >= 2) {
5739
+
5740
+ if (logoPayload.logos[parts[0]] || logoPayload.aliases[parts[0]]) return logoPayload.aliases[parts[0]] || parts[0];
5741
+
5742
+ var two = parts.slice(0, 2).join('');
5743
+
5744
+ if (logoPayload.logos[two] || logoPayload.aliases[two]) return logoPayload.aliases[two] || two;
5745
+
5746
+ var twoHyphen = parts.slice(0, 2).join('-');
5747
+
5748
+ if (logoPayload.aliases[twoHyphen]) return logoPayload.aliases[twoHyphen];
5749
+
5750
+ }
5751
+
5752
+ return compact;
5753
+
5754
+ }
5755
+
5756
+
5757
+
5758
+ function logoClass(p) {
5759
+
5760
+ var key = normKey(p);
5761
+
5762
+ if (!key) return '';
5763
+
5764
+ var cls = ' provider-logo--' + key;
5765
+
5766
+ if (logoPayload.brandKeys && logoPayload.brandKeys.indexOf(key) >= 0) {
5767
+
5768
+ cls += ' provider-logo--brand';
5769
+
5770
+ }
5771
+
5772
+ return cls;
5773
+
5774
+ }
5775
+
5776
+
5777
+
5778
+ function logoHtml(p, label) {
5779
+
5780
+ var key = normKey(p);
5781
+
5782
+ if (key && logoPayload.inlineOnly && logoPayload.inlineOnly.indexOf(key) >= 0 && logoPayload.logos[key]) {
5783
+
5784
+ return logoPayload.logos[key];
5785
+
5786
+ }
5787
+
5788
+ if (key && logoPayload.assetUrls && logoPayload.assetUrls[key]) {
5789
+
5790
+ return '<img class="provider-logo__img" src="' + esc(logoPayload.assetUrls[key]) + '" alt="" decoding="async" data-provider-logo-key="' + esc(key) + '" />';
5791
+
5792
+ }
5793
+
5794
+ if (key && logoPayload.iconUrls && logoPayload.iconUrls[key]) {
5795
+
5796
+ return '<img class="provider-logo__img" src="' + esc(logoPayload.iconUrls[key]) + '" alt="" decoding="async" data-provider-logo-key="' + esc(key) + '" />';
5797
+
5798
+ }
5799
+
5800
+ if (key && logoPayload.logos[key]) return logoPayload.logos[key];
5801
+
5802
+ var t = (label || p || '?').trim();
5803
+
5804
+ return '<span aria-hidden="true">' + esc(t.slice(0, 2).toUpperCase()) + '</span>';
5805
+
5806
+ }
5807
+
5808
+
5809
+
5810
+ function benefitText(p, desc) {
5811
+
5812
+ var key = normKey(p);
5813
+
5814
+ return (key && logoPayload.benefits[key]) || desc || 'Useful path for this section.';
5815
+
5816
+ }
5817
+
5818
+
5819
+
5820
+ function panelTitle(areaKey, label) {
5821
+
5822
+ return esc(String(label || LABELS[areaKey] || areaKey).toUpperCase()) +
5823
+
5824
+ (STACK_AREAS[areaKey] ? ' STACK' : ' CONTROLS');
5825
+
5826
+ }
5827
+
5828
+
5829
+
5830
+ function evidenceBadgeHtml(missions) {
5831
+
5832
+ var open = missions.flatMap(function (m) {
5833
+
5834
+ return (m.checks || []).filter(function (c) {
5835
+
5836
+ return c.status === 'missing' || c.status === 'failed' || c.status === 'needs-connection';
5837
+
5838
+ });
5839
+
5840
+ });
5841
+
5842
+ var tone = open.length > 0 ? 'missing' : 'repo';
5843
+
5844
+ var label = open.length > 0 ? 'Missing repo fixes' : 'Repo evidence found';
5845
+
5846
+ return '<span class="studio-evidence-badge studio-evidence-badge--' + tone + '">' + esc(label) + '</span>';
5847
+
5848
+ }
5849
+
5850
+
5851
+
5852
+ function automationFor(areaKey, mission, currentProvider) {
5853
+
5854
+ var byKey = stackAutomation.byKey || {};
5855
+
5856
+ if (mission && mission.key && byKey[mission.key]) return byKey[mission.key];
5857
+
5858
+ var items = stackAutomation.items || [];
5859
+
5860
+ var current = normKey(currentProvider || (mission && (mission.provider || mission.providerLabel)));
5861
+
5862
+ return items.find(function (item) {
5863
+
5864
+ return item.area === areaKey && (!current || normKey(item.provider || item.providerLabel) === current);
5865
+
5866
+ }) || null;
5867
+
5868
+ }
5869
+
5870
+
5871
+
5872
+ function optionFor(areaKey, currentProvider) {
5873
+
5874
+ var options = providerOptions[areaKey] || [];
5875
+
5876
+ var current = normKey(currentProvider);
5877
+
5878
+ return options.find(function (opt) {
5879
+
5880
+ var p = opt.provider || opt.label;
5881
+
5882
+ return p === currentProvider || normKey(p) === current;
5883
+
5884
+ }) || null;
5885
+
5886
+ }
5887
+
5888
+
5889
+
5890
+ function sameProvider(a, b) {
5891
+
5892
+ if (!a || !b) return false;
5893
+
5894
+ return String(a).toLowerCase() === String(b).toLowerCase() || normKey(a) === normKey(b);
5895
+
5896
+ }
5897
+
5898
+
5899
+
5900
+ function projectProviderFor(areaKey, missions) {
5901
+
5902
+ if (projectProviders[areaKey]) return projectProviders[areaKey];
5903
+
5904
+ var mission = preferredMission(missions, '');
5905
+
5906
+ return (mission && (mission.provider || mission.providerLabel)) || '';
5907
+
5908
+ }
5909
+
5910
+
5911
+
5912
+ function missionMatchesProvider(mission, currentProvider) {
5913
+
5914
+ if (!mission || !currentProvider) return false;
5915
+
5916
+ var current = normKey(currentProvider);
5917
+
5918
+ return normKey(mission.provider || mission.providerLabel || mission.key) === current ||
5919
+
5920
+ normKey(mission.providerLabel || mission.provider || mission.key) === current;
5921
+
5922
+ }
5923
+
5924
+
5925
+
5926
+ function missionForProvider(missions, currentProvider) {
5927
+
5928
+ if (!missions || !missions.length) return null;
5929
+
5930
+ if (!currentProvider) return missions[0];
5931
+
5932
+ return missions.find(function (mission) {
5933
+
5934
+ return missionMatchesProvider(mission, currentProvider);
5935
+
5936
+ }) || null;
5937
+
5938
+ }
5939
+
5940
+ function missionEvidenceScore(mission) {
5941
+
5942
+ if (!mission || !mission.checks) return 0;
5943
+
5944
+ var repoVerified = mission.checks.filter(function (check) {
5945
+
5946
+ return check.evidenceClass === 'repo-verified' || check.status === 'passed';
5947
+
5948
+ }).length;
5949
+
5950
+ var missing = mission.checks.filter(function (check) {
5951
+
5952
+ return check.evidenceClass === 'missing-repo-fix' || check.status === 'missing' || check.status === 'failed';
5953
+
5954
+ }).length;
5955
+
5956
+ return repoVerified * 100 + (mission.readinessPercent || 0) - missing;
5957
+
5958
+ }
5959
+
5960
+
5961
+
5962
+ function preferredMission(missions, selectedProvider) {
5963
+
5964
+ if (!missions || !missions.length) return null;
5965
+
5966
+ var selected = selectedProvider ? missionForProvider(missions, selectedProvider) : null;
5967
+
5968
+ if (selected) return selected;
5969
+
5970
+ return missions.slice().sort(function (a, b) {
5971
+
5972
+ return missionEvidenceScore(b) - missionEvidenceScore(a);
5973
+
5974
+ })[0] || missions[0];
5975
+
5976
+ }
5977
+
5978
+
5979
+
5980
+ function providerLabelFor(areaKey, currentProvider, mission, areaLabel) {
5981
+
5982
+ var opt = optionFor(areaKey, currentProvider);
5983
+
5984
+ return (opt && opt.label) || (mission && mission.providerLabel) || currentProvider || areaLabel;
5985
+
5986
+ }
5987
+
5988
+
5989
+
5990
+ function itemLines(items, fallback, includeEvidence) {
5991
+
5992
+ if (!items || !items.length) return fallback;
5993
+
5994
+ return items.map(function (item) {
5995
+
5996
+ var evidence = includeEvidence && item.evidence && item.evidence.length
5997
+
5998
+ ? ' (' + item.evidence.slice(0, 3).join('; ') + ')'
5999
+
6000
+ : '';
6001
+
6002
+ return '- ' + item.label + evidence + (item.promptHint && !includeEvidence ? ': ' + item.promptHint : '');
6003
+
6004
+ }).join('\\n');
6005
+
6006
+ }
6007
+
6008
+
6009
+
6010
+ function stackPromptFromMission(mission, providerLabel) {
6011
+
6012
+ if (!mission || !(mission.checks && mission.checks.length)) return '';
6013
+
6014
+ var subject = mission.promptSubject || providerLabel || mission.providerLabel || 'this stack';
6015
+
6016
+ var passed = [];
6017
+
6018
+ var missing = [];
6019
+
6020
+ var manual = [];
6021
+
6022
+ mission.checks.forEach(function (check) {
6023
+
6024
+ var item = { label: check.label, promptHint: check.promptHint, evidence: check.evidence || [] };
6025
+
6026
+ if (check.evidenceClass === 'repo-verified' || check.status === 'passed') passed.push(item);
6027
+
6028
+ else if (check.evidenceClass === 'manual-dashboard') manual.push(item);
6029
+
6030
+ else if (check.evidenceClass === 'mcp-verifier') manual.push(item);
6031
+
6032
+ else if (check.evidenceClass === 'missing-repo-fix' || check.status === 'missing' || check.status === 'failed') missing.push(item);
6033
+
6034
+ });
6035
+
6036
+ var total = passed.length + missing.length;
6037
+
6038
+ return [
6039
+
6040
+ 'Wire ' + subject + ' for this app safely.',
6041
+
6042
+ '',
6043
+
6044
+ 'Current ' + subject + ' readiness: ' + passed.length + '/' + Math.max(total, 1) + ' repo checks passed (' + (mission.readinessPercent || 0) + '%).',
6045
+
6046
+ '',
6047
+
6048
+ 'Repo evidence already found:',
6049
+
6050
+ itemLines(passed, '- No ' + subject + ' checks passed yet.', true),
6051
+
6052
+ '',
6053
+
6054
+ 'Missing ' + subject + ' checks:',
6055
+
6056
+ itemLines(missing, '- No missing ' + subject + ' checks were found by VibeRaven.', false),
6057
+
6058
+ '',
6059
+
6060
+ 'Manual checks that repo evidence cannot prove:',
6061
+
6062
+ itemLines(manual, '- No manual dashboard checks were listed.', false),
6063
+
6064
+ '',
6065
+
6066
+ 'First inspect the existing package.json files, env examples, framework routes, provider helpers, and server/client boundaries before editing.',
6067
+
6068
+ '',
6069
+
6070
+ 'Implement:',
6071
+
6072
+ '1. Close only the missing ' + subject + ' checks listed above.',
6073
+
6074
+ '2. Follow the existing file structure and naming patterns.',
6075
+
6076
+ '3. Keep provider secrets in server-only code and documented env templates.',
6077
+
6078
+ '4. Keep external dashboard work explicit instead of claiming it from repo evidence.',
6079
+
6080
+ '',
6081
+
6082
+ 'Constraints:',
6083
+
6084
+ '- Do not rewrite unrelated auth, payments, UI, billing, deployment, or analytics code.',
6085
+
6086
+ '- Do not expose secret keys to browser code, public env variables, or client-executed files.',
6087
+
6088
+ '- Do not claim external provider dashboard setup is complete from repo evidence alone.',
6089
+
6090
+ '',
6091
+
6092
+ 'Verification:',
6093
+
6094
+ '- Run the relevant TypeScript/build/test command for this repo.',
6095
+
6096
+ '- Confirm VibeRaven can rescan and move the missing checks to passed where repo evidence exists.',
6097
+
6098
+ '- Summarize what changed and what still requires manual provider dashboard verification.'
6099
+
6100
+ ].join('\\n');
6101
+
6102
+ }
6103
+
6104
+
6105
+
6106
+ function choiceTilesHtml(areaKey, currentProvider, missions, evidenceMissions) {
6107
+
6108
+ var options = providerOptions[areaKey];
6109
+
6110
+ if (!options || !options.length) return '';
6111
+
6112
+ var projectProvider = projectProviderFor(areaKey, missions);
6113
+
6114
+ var tiles = options.map(function (opt) {
6115
+
6116
+ var p = opt.provider || opt.label;
6117
+
6118
+ var active = sameProvider(p, currentProvider);
6119
+
6120
+ var inProject = sameProvider(p, projectProvider);
6121
+
6122
+ var desc = opt.description || benefitText(p);
6123
+
6124
+ var status = inProject ? 'Using now' : active ? 'Added to setup' : 'Use this path';
6125
+
6126
+ return '<button type="button" class="studio-choice-tile' + (active ? ' studio-choice-tile--selected' : '') + (inProject ? ' studio-choice-tile--in-project' : '') +
6127
+
6128
+ '" data-switch-area="' + esc(areaKey) + '" data-switch-provider="' + esc(opt.provider) + '" aria-pressed="' + (active ? 'true' : 'false') + '">' +
6129
+
6130
+ '<span class="studio-choice-tile__icon provider-logo' + logoClass(p) + '" aria-hidden="true">' + logoHtml(p, opt.label) + '</span>' +
6131
+
6132
+ '<span class="studio-choice-tile__name">' + esc(opt.label) + '</span>' +
6133
+
6134
+ '<span class="studio-choice-tile__desc">' + esc(desc) + '</span>' +
6135
+
6136
+ '<span class="studio-choice-tile__status">' + status + '</span>' +
6137
+
6138
+ '</button>';
6139
+
6140
+ }).join('');
6141
+
6142
+ var hintLabel = CHOICE_HINTS[areaKey] || ('Choose ' + (LABELS[areaKey] || areaKey).toLowerCase());
6143
+
6144
+ return '<div class="studio-setup-panel__hint"><span>' + esc(hintLabel) + '</span>' + evidenceBadgeHtml(evidenceMissions || missions) + '</div>' +
6145
+
6146
+ '<div class="studio-choice-list" role="group" aria-label="' + esc(hintLabel) + '">' + tiles + '</div>';
6147
+
6148
+ }
6149
+
6150
+
6151
+
6152
+ function buildPrompt(areaKey, missions, providerLabel, automation) {
6153
+
6154
+ if (automation) {
6155
+
6156
+ if (automation.automationLevel === 'manual-only' && automation.verificationPrompt) return automation.verificationPrompt;
6157
+
6158
+ if (automation.repoPrompt) return automation.repoPrompt;
6159
+
6160
+ if (automation.promptRoutes && automation.promptRoutes['repo-fix'] && automation.promptRoutes['repo-fix'].body) {
6161
+
6162
+ return automation.promptRoutes['repo-fix'].body;
6163
+
6164
+ }
6165
+
6166
+ }
6167
+
6168
+ var areaGaps = missions[0] ? gaps.filter(function (g) { return g.primaryMapCategory === areaKey; }) : [];
6169
+
6170
+ if (areaGaps[0] && areaGaps[0].copyPrompt) return areaGaps[0].copyPrompt;
6171
+
6172
+ var missionPrompt = stackPromptFromMission(missions[0], providerLabel);
6173
+
6174
+ if (missionPrompt) return missionPrompt;
6175
+
6176
+ var missing = (missions[0] && missions[0].checks || []).filter(function (c) {
6177
+
6178
+ return c.evidenceClass === 'missing-repo-fix' || c.status === 'missing' || c.status === 'failed';
6179
+
6180
+ });
6181
+
6182
+ if (missing[0] && missing[0].promptHint) return missing[0].promptHint;
6183
+
6184
+ return setupPromptForProvider(areaKey, providerLabel);
6185
+
6186
+ }
6187
+
6188
+
6189
+
6190
+ function setupPromptForProvider(areaKey, providerLabel) {
6191
+
6192
+ var areaLabel = LABELS[areaKey] || areaKey;
6193
+
6194
+ var provider = providerLabel || areaLabel;
6195
+
6196
+ return [
6197
+
6198
+ 'Set up ' + provider + ' for the ' + areaLabel + ' area of this app safely.',
6199
+
6200
+ '',
6201
+
6202
+ 'First inspect package.json files, env examples, framework routes, provider helpers, server/client boundaries, and existing billing/auth/deployment patterns before editing.',
6203
+
6204
+ '',
6205
+
6206
+ 'Implement the smallest useful setup:',
6207
+
6208
+ '1. Add the right ' + provider + ' package or SDK only if it is missing.',
6209
+
6210
+ '2. Document required environment variables in safe examples, without reading or exposing real secrets.',
6211
+
6212
+ '3. Add server-side integration points, route handlers, webhooks, or helpers that match this repo structure.',
6213
+
6214
+ '4. Keep external provider dashboard setup explicit as a manual step.',
6215
+
6216
+ '',
6217
+
6218
+ 'Constraints:',
6219
+
6220
+ '- Do not rewrite unrelated product, auth, payment, database, deployment, or analytics code.',
6221
+
6222
+ '- Do not put secret keys in client code, public env variables, or browser-executed files.',
6223
+
6224
+ '- Do not claim live provider configuration is complete from repo changes alone.',
6225
+
6226
+ '',
6227
+
6228
+ 'Verification:',
6229
+
6230
+ '- Run the relevant TypeScript/build/test command for this repo.',
6231
+
6232
+ '- Summarize repo changes and list dashboard/provider checks still needed.',
6233
+
6234
+ '- Re-run VibeRaven so repo evidence can move from setup needed to verified.'
6235
+
6236
+ ].join('\\n');
6237
+
6238
+
6239
+ }
6240
+
6241
+
6242
+
6243
+ function setupActionsHtml(areaKey, missions, providerLabel, automation) {
6244
+
6245
+ var prompt = buildPrompt(areaKey, missions, providerLabel, automation);
6246
+
6247
+ var hasFixes = (missions[0] && missions[0].checks || []).some(function (c) {
6248
+
6249
+ return c.evidenceClass === 'missing-repo-fix' || c.status === 'missing' || c.status === 'failed';
6250
+
6251
+ });
6252
+
6253
+ var isManualOnly = automation && automation.automationLevel === 'manual-only';
6254
+
6255
+ var mcpCheck = (missions[0] && missions[0].checks || []).find(function (c) {
6256
+
6257
+ return c.evidenceClass === 'mcp-verifier';
6258
+
6259
+ });
6260
+
6261
+ var mcpProvider = (automation && automation.mcpProvider) ||
6262
+
6263
+ (mcpCheck && (mcpCheck.providerKey || mcpCheck.provider || normKey(providerLabel)));
6264
+
6265
+ var supportsMcp = Boolean(mcpProvider);
6266
+
6267
+ var title = esc(providerLabel || LABELS[areaKey] || areaKey) + (isManualOnly ? ' manual check' : hasFixes ? ' fix prompt' : ' setup');
6268
+
6269
+ var meta = supportsMcp ? 'MCP verification available' : 'Prompt only';
6270
+
6271
+ var copy = isManualOnly
6272
+
6273
+ ? 'Repo fixes are already clear. Use the manual checklist for provider dashboard work, then rescan.'
6274
+
6275
+ : hasFixes
6276
+
6277
+ ? 'One prompt for the missing repo fixes above. Manual dashboard checks stay separate.'
6278
+
6279
+ : supportsMcp
6280
+
6281
+ ? 'Use this setup prompt when starting with this provider. The MCP helper is optional.'
6282
+
6283
+ : 'Use this setup prompt when starting with this provider. No trusted MCP helper is available yet.';
6284
+
6285
+ var label = isManualOnly ? 'Copy Checklist' : hasFixes ? 'Copy Fix Prompt' : 'Copy Setup Prompt';
6286
+
6287
+ return '<section class="studio-setup-actions" aria-label="Setup actions">' +
6288
+
6289
+ '<div class="studio-setup-actions__head"><strong>' + title + '</strong><span>' + meta + '</span></div>' +
6290
+
6291
+ '<p class="studio-setup-actions__copy">' + esc(copy) + '</p>' +
6292
+
6293
+ '<div class="studio-setup-actions__buttons">' +
6294
+
6295
+ '<button type="button" class="studio-action-button studio-action-button--primary" data-copy-prompt="' + attrEsc(prompt) + '">' +
6296
+
6297
+ label + '</button>' +
6298
+
6299
+ '</div>' +
6300
+
6301
+ (supportsMcp
6302
+
6303
+ ? '<div class="studio-setup-actions__mcp-row"><p class="studio-setup-actions__auth-note"><strong>MCP verifier not configured</strong><br />Optional MCP verification stays read-only and must use credentials already configured by the IDE. VibeRaven does not read real .env files or store provider API keys.</p><button type="button" class="studio-action-button" data-copy-prompt="' + attrEsc('Use read-only ' + mcpProvider + ' MCP verification for ' + (providerLabel || LABELS[areaKey] || areaKey) + '. Report evidence only; do not mutate provider settings or claim dashboard setup from repo edits.') + '">MCP Verify</button></div>'
6304
+
6305
+ : '') +
6306
+
6307
+ '</section>';
6308
+
6309
+ }
6310
+
6311
+
6312
+
6313
+ function addedSetupPathHtml(areaKey, currentProvider, providerLabel, missions) {
6314
+
6315
+ var projectProvider = projectProviderFor(areaKey, missions);
6316
+
6317
+ if (!currentProvider || sameProvider(currentProvider, projectProvider)) return '';
6318
+
6319
+ return '<section class="studio-added-path" aria-label="Added setup path">' +
6320
+
6321
+ '<div class="studio-added-path__title">Added setup path</div>' +
6322
+
6323
+ '<div class="studio-added-path__pill">' +
6324
+
6325
+ '<span class="studio-added-path__icon provider-logo' + logoClass(currentProvider) + '" aria-hidden="true">' + logoHtml(currentProvider, providerLabel) + '</span>' +
6326
+
6327
+ '<strong>' + esc(providerLabel) + '</strong>' +
6328
+
6329
+ '</div>' +
6330
+
6331
+ '</section>';
6332
+
6333
+ }
6334
+
6335
+
6336
+
6337
+ function setupReadinessHtml(areaKey, currentProvider, providerLabel, missions, automation) {
6338
+
6339
+ if (missions[0]) return '';
6340
+
6341
+ var provider = providerLabel || currentProvider || (LABELS[areaKey] || areaKey);
6342
+
6343
+ var repoItems = [
6344
+
6345
+ { label: provider + ' package or SDK installed', detail: 'VibeRaven has not found repo evidence for this setup path yet.' },
6346
+
6347
+ { label: provider + ' env names documented', detail: 'Use safe examples only. Do not expose real provider secrets.' },
6348
+
6349
+ { label: provider + ' server integration or route found', detail: 'Add provider code on the server side before rescanning.' }
6350
+
6351
+ ];
6352
+
6353
+ var manualItems = [
6354
+
6355
+ { label: 'Production ' + provider + ' account and product checked', detail: 'Dashboard confirmation stays manual unless a trusted MCP verifier is configured.' },
6356
+
6357
+ { label: 'Production webhook or provider credentials checked', detail: 'Repo evidence cannot prove live provider settings by itself.' }
6358
+
6359
+ ];
6360
+
6361
+ var external = automation && automation.mcpProvider
6362
+
6363
+ ? groupHtml('MCP verifier', [{ label: provider + ' MCP verifier available', detail: 'Use read-only MCP verification when already configured by the IDE.' }], 'external')
6364
+
6365
+ : '';
6366
+
6367
+ return '<section class="studio-verification studio-provider-readiness" aria-label="' + esc(provider) + ' setup readiness">' +
6368
+
6369
+ '<h3 class="studio-verification__title">' + esc(provider) + '</h3>' +
6370
+
6371
+ readinessMetersHtml(0, 0, 'Not checked') +
6372
+
6373
+ '<p class="studio-provider-readiness__note">Provider live moves after MCP verification or manual dashboard confirmation. Repo scans cannot prove live provider state.</p>' +
6374
+
6375
+ '<p class="studio-wiring__summary">' + esc(provider) + ' is added as a setup path, but VibeRaven has not found repo evidence for it yet.</p>' +
6376
+
6377
+ groupHtml('Repo setup needed', repoItems, 'missing') +
6378
+
6379
+ external +
6380
+
6381
+ groupHtml('Provider live check', manualItems, 'manual') +
6382
+
6383
+ '</section>';
6384
+
6385
+ }
6386
+
6387
+
6388
+
6389
+ function readinessMetersHtml(repoPercent, providerPercent, providerStatus) {
6390
+
6391
+ var repo = Math.max(0, Math.min(100, Math.round(repoPercent || 0)));
6392
+
6393
+ var provider = Math.max(0, Math.min(100, Math.round(providerPercent || 0)));
6394
+ var providerValue = providerStatus || (provider + '%');
6395
+
6396
+ return '<div class="studio-provider-readiness__meters" aria-label="Provider readiness meters">' +
6397
+
6398
+ '<div class="studio-provider-readiness__meter-row"><span>Repo &middot; files</span><span class="studio-provider-readiness__bar" aria-hidden="true"><span class="studio-provider-readiness__bar-fill" style="width:' + repo + '%"></span></span><strong>' + repo + '%</strong></div>' +
6399
+
6400
+ '<div class="studio-provider-readiness__meter-row"><span>Provider &middot; live</span><span class="studio-provider-readiness__bar" aria-hidden="true"><span class="studio-provider-readiness__bar-fill" style="width:' + provider + '%"></span></span><strong>' + esc(providerValue) + '</strong></div>' +
6401
+
6402
+ '</div>';
6403
+
6404
+ }
6405
+
6406
+
6407
+
6408
+ function checkToItem(check) {
6409
+
6410
+ var ev = (check.evidence && check.evidence[0]) ? check.evidence[0] : (check.promptHint || '');
6411
+
6412
+ return { label: check.label, detail: ev };
6413
+
6414
+ }
6415
+
6416
+
6417
+
6418
+ function groupHtml(label, items, tone) {
6419
+
6420
+ if (!items.length) return '';
6421
+
6422
+ var slice = items.slice(0, MAX_GROUP_ITEMS);
6423
+
6424
+ var rows = slice.map(function (item) {
6425
+
6426
+ var title = item.detail ? ' title="' + esc(item.detail) + '"' : '';
6427
+
6428
+ return '<li class="studio-verification__item' + (tone === 'manual' ? ' studio-verification__item--manual' : '') + '"' + title + '><span class="studio-verification__item-label">' + esc(item.label) + '</span></li>';
6429
+
6430
+ }).join('');
6431
+
6432
+ var more = items.length > MAX_GROUP_ITEMS
6433
+
6434
+ ? '<li class="studio-verification__item studio-verification__item--more">+' + (items.length - MAX_GROUP_ITEMS) + ' more</li>'
6435
+
6436
+ : '';
6437
+
6438
+ return '<div class="studio-verification__group studio-verification__group--' + tone + '">' +
6439
+
6440
+ '<div class="studio-verification__group-title"><strong>' + esc(label) + '</strong>' +
6441
+
6442
+ '<span class="studio-verification__count">' + items.length + '</span></div>' +
6443
+
6444
+ '<ul class="studio-verification__list">' + rows + more + '</ul></div>';
6445
+
6446
+ }
6447
+
6448
+
6449
+
6450
+ function missionBlockHtml(missions, providerLabel) {
6451
+
6452
+ var mission = missions[0];
6453
+
6454
+ if (!mission || !(mission.checks && mission.checks.length)) return '';
6455
+
6456
+ var groups = {
6457
+
6458
+ 'repo-verified': [],
6459
+
6460
+ 'missing-repo-fix': [],
6461
+
6462
+ 'mcp-verifier': [],
6463
+
6464
+ 'manual-dashboard': []
6465
+
6466
+ };
6467
+
6468
+ mission.checks.forEach(function (check) {
6469
+
6470
+ var bucket = groups[check.evidenceClass] ? check.evidenceClass : 'manual-dashboard';
6471
+
6472
+ groups[bucket].push(checkToItem(check));
6473
+
6474
+ });
6475
+
6476
+ var actionable = groups['repo-verified'].length + groups['missing-repo-fix'].length;
6477
+ var manualTotal = groups['manual-dashboard'].length;
6478
+ var manualDone = mission.checks.filter(function (check) {
6479
+
6480
+ return check.evidenceClass === 'manual-dashboard' && (check.status === 'passed' || check.status === 'user-confirmed');
6481
+
6482
+ }).length;
6483
+ var providerPercent = manualTotal ? Math.round((manualDone / manualTotal) * 100) : 0;
6484
+ var providerValue = manualTotal ? (manualDone > 0 ? providerPercent + '%' : 'Not checked') : 'No live checks';
6485
+
6486
+ var summary =
6487
+
6488
+ 'Stack scanner: ' + groups['repo-verified'].length + ' / ' + Math.max(actionable, 1) +
6489
+
6490
+ ' repo checks verified (' + (mission.readinessPercent || 0) + '%).';
6491
+
6492
+ if (groups['missing-repo-fix'].length > 0) {
6493
+
6494
+ summary += ' Fix ' + groups['missing-repo-fix'].length + ' repo item' +
6495
+
6496
+ (groups['missing-repo-fix'].length === 1 ? '' : 's') + ', then rescan.';
6497
+
6498
+ }
6499
+
6500
+ var body =
6501
+
6502
+ groupHtml('Repo verified', groups['repo-verified'], 'found') +
6503
+
6504
+ groupHtml('Stack fixes needed', groups['missing-repo-fix'], 'missing') +
6505
+
6506
+ groupHtml('MCP verifier', groups['mcp-verifier'], 'external') +
6507
+
6508
+ groupHtml('Manual dashboard check', groups['manual-dashboard'], 'manual');
6509
+
6510
+ if (!body) return '';
6511
+
6512
+ return '<section class="studio-verification studio-wiring studio-mission-graph" aria-label="Mission evidence">' +
6513
+
6514
+ '<h3 class="studio-verification__title">' + esc(providerLabel || mission.providerLabel || 'Detected evidence') + '</h3>' +
6515
+
6516
+ readinessMetersHtml(mission.readinessPercent || 0, providerPercent, providerValue) +
6517
+
6518
+ '<p class="studio-provider-readiness__note">Provider live moves after MCP verification or manual dashboard confirmation. Repo scans cannot prove live provider state.</p>' +
6519
+
6520
+ '<p class="studio-wiring__summary">' + esc(summary) + '</p>' + body + '</section>';
6521
+
6522
+ }
6523
+
6524
+
6525
+
6526
+ function render(areaKey, providerOverride) {
6527
+
6528
+ if (providerOverride) selectedProviders[areaKey] = providerOverride;
6529
+
6530
+ var area = areas.find(function (a) { return a.key === areaKey; });
6531
+
6532
+ document.querySelectorAll('.studio-node').forEach(function (n) {
6533
+
6534
+ var selected = n.getAttribute('data-area-key') === areaKey;
6535
+
6536
+ n.classList.toggle('studio-node--selected', selected);
6537
+
6538
+ n.setAttribute('aria-pressed', selected ? 'true' : 'false');
6539
+
6540
+ });
6541
+
6542
+ var label = (area && area.label) || LABELS[areaKey] || areaKey;
6543
+
6544
+ var missions = (area && area.providerMissions) || [];
6545
+
6546
+ var selectedBeforeRender = selectedProviders[areaKey] || '';
6547
+
6548
+ var defaultMission = preferredMission(missions, selectedBeforeRender);
6549
+
6550
+ var current = selectedProviders[areaKey] ||
6551
+
6552
+ (defaultMission && (defaultMission.provider || defaultMission.providerLabel)) || '';
6553
+
6554
+ var mission = missionForProvider(missions, current) || (!selectedBeforeRender ? defaultMission : null);
6555
+
6556
+ var panelMissions = mission ? [mission] : [];
6557
+
6558
+ var providerLabel = providerLabelFor(areaKey, current, mission, label);
6559
+
6560
+ var automation = automationFor(areaKey, mission, current);
6561
+
6562
+
6563
+
6564
+ panel.innerHTML =
6565
+
6566
+ '<div class="studio-setup-panel__inner">' +
6567
+
6568
+ '<div class="studio-setup-panel__head">' +
6569
+
6570
+ '<div class="studio-setup-panel__title">' + panelTitle(areaKey, label) + '</div>' +
6571
+
6572
+ '</div>' +
6573
+
6574
+ choiceTilesHtml(areaKey, current, missions, panelMissions) +
6575
+
6576
+ addedSetupPathHtml(areaKey, current, providerLabel, missions) +
6577
+
6578
+ setupActionsHtml(areaKey, panelMissions, providerLabel, automation) +
6579
+
6580
+ setupReadinessHtml(areaKey, current, providerLabel, panelMissions, automation) +
6581
+
6582
+ missionBlockHtml(panelMissions, providerLabel) +
6583
+
6584
+ '</div>';
6585
+
6586
+
6587
+
6588
+ panel.querySelectorAll('[data-copy-prompt]').forEach(function (btn) {
6589
+
6590
+ btn.addEventListener('click', function () {
6591
+
6592
+ var text = btn.getAttribute('data-copy-prompt') || '';
6593
+
6594
+ navigator.clipboard.writeText(text).then(function () {
6595
+
6596
+ var label = btn.textContent;
6597
+
6598
+ btn.textContent = 'Copied';
6599
+
6600
+ setTimeout(function () { btn.textContent = label; }, 1200);
6601
+
6602
+ });
6603
+
6604
+ });
6605
+
6606
+ });
6607
+
6608
+
6609
+
6610
+ panel.querySelectorAll('[data-switch-provider]').forEach(function (btn) {
6611
+
6612
+ btn.addEventListener('click', function () {
6613
+
6614
+ var a = btn.getAttribute('data-switch-area');
6615
+
6616
+ var p = btn.getAttribute('data-switch-provider');
6617
+
6618
+ render(a, p);
6619
+
6620
+ });
6621
+
6622
+ });
6623
+
6624
+ }
6625
+
6626
+
6627
+
6628
+ document.querySelectorAll('.studio-node').forEach(function (n) {
6629
+
6630
+ n.addEventListener('click', function () { render(n.getAttribute('data-area-key')); });
6631
+
6632
+ });
6633
+
6634
+
6635
+
6636
+ document.addEventListener(
6637
+
6638
+ 'error',
6639
+
6640
+ function (ev) {
6641
+
6642
+ var img = ev.target;
6643
+
6644
+ if (!img || !img.classList || !img.classList.contains('provider-logo__img')) return;
6645
+
6646
+ var key = img.getAttribute('data-provider-logo-key');
6647
+
6648
+ if (!key || !logoPayload.logos[key]) return;
6649
+
6650
+ var wrap = document.createElement('span');
6651
+
6652
+ wrap.innerHTML = logoPayload.logos[key];
6653
+
6654
+ var svg = wrap.firstElementChild;
6655
+
6656
+ if (svg) img.replaceWith(svg);
6657
+
6658
+ },
6659
+
6660
+ true
6661
+
6662
+ );
6663
+
6664
+
6665
+
6666
+ render(defaultAreaKey);
6667
+
6668
+ })();
6669
+
6670
+ `;
6671
+
6672
+ // src/report/providerLogos.ts
6673
+ var PROVIDER_LOGOS = {
6674
+ supabase: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M13.8 2.4 5.6 13.1c-.5.7 0 1.7.9 1.7h4.7l-1 6.1c-.2 1 .9 1.6 1.5.8l8.1-10.8c.5-.7 0-1.7-.9-1.7h-4.7l1-6.1c.2-1-.8-1.6-1.4-.7Z"/></svg>',
6675
+ clerk: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#6C47FF" d="M19.2 12c0 3.9-3.1 7.1-7.1 7.1-3.2 0-5.9-2.1-6.8-5h2.9c.7 1.8 2.5 3.1 4.5 3.1 2.7 0 4.9-2.2 4.9-4.9S13.2 7.4 10.5 7.4c-2 0-3.8 1.2-4.5 3H3.3c.9-2.9 3.6-5 6.8-5 4 0 7.1 3.2 7.1 7.1Z"/></svg>',
6676
+ authjs: '<svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" aria-hidden="true"><path fill="#412991" d="M12 2 4 6.5v5.2c0 4.6 3.4 8.9 8 10.3 4.6-1.4 8-5.7 8-10.3V6.5L12 2Z"/><path fill="#EB5424" d="M12 2v18.5c-1.2-.4-2.3-1-3.3-1.7L12 2Z"/><path fill="#FBC22C" d="M12 2 15.3 18.8c-1-.7-2.1-1.3-3.3-1.7V2Z"/></svg>',
6677
+ auth0: '<svg viewBox="0 0 24 24" aria-hidden="true"><rect fill="#EB5424" x="3" y="3" width="18" height="18" rx="3.2"/><path fill="#ffffff" d="M12 7.4c2.5 0 4.6 2.1 4.6 4.6S14.5 16.6 12 16.6 7.4 14.5 7.4 12 9.5 7.4 12 7.4Zm0 2.2a2.4 2.4 0 1 0 0 4.8 2.4 2.4 0 0 0 0-4.8Z"/></svg>',
6678
+ "better-auth": '<svg viewBox="0 0 24 24" aria-hidden="true"><rect fill="#171717" x="3" y="3" width="18" height="18" rx="4"/><path fill="#ffffff" d="M8 8h3v3H8V8Zm5 0h3v3h-3V8ZM8 13h3v3H8v-3Zm5 0h3v3h-3v-3Z"/></svg>',
6679
+ polar: '<svg viewBox="0 0 29 29" aria-hidden="true"><path fill="#0062FF" fill-rule="evenodd" clip-rule="evenodd" d="M9.077 23.057c4.801 3.25 11.328 1.992 14.577-2.808 3.25-4.801 1.993-11.328-2.808-14.578C16.045 2.422 9.519 3.679 6.269 8.48c-3.25 4.801-1.993 11.327 2.808 14.577Zm1.393.086c4.392 2.247 9.963.138 12.444-4.711 2.48-4.848.93-10.6-3.461-12.847-4.392-2.247-9.963-.138-12.443 4.711-2.481 4.848-.932 10.6 3.46 12.847Z"/><path fill="#0062FF" fill-rule="evenodd" clip-rule="evenodd" d="M11.722 24.29c3.965 1.29 8.628-2.118 10.417-7.613 1.788-5.495.024-10.996-3.94-12.286-3.964-1.29-8.628 2.118-10.416 7.613-1.789 5.495-.025 10.995 3.939 12.286Zm1.213-.418c3.355.716 6.982-2.961 8.102-8.212 1.12-5.252-.691-10.089-4.046-10.804-3.356-.716-6.983 2.96-8.103 8.212-1.12 5.251.692 10.088 4.047 10.804Z"/><path fill="#0062FF" fill-rule="evenodd" clip-rule="evenodd" d="M13.854 24.738c2.652.284 5.3-4.14 5.912-9.882.613-5.74-1.04-10.624-3.692-10.907-2.653-.283-5.3 4.141-5.913 9.882-.613 5.741 1.04 10.624 3.693 10.907Zm1.241-1.747c1.92-.031 3.415-3.917 3.34-8.68-.075-4.764-1.693-8.6-3.612-8.57-1.92.03-3.415 3.916-3.34 8.68.076 4.763 1.693 8.6 3.612 8.57Z"/></svg>',
6680
+ "lemon-squeezy": '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M12 3c-3.2 2.8-5 5.8-5 8.6a5 5 0 1 0 10 0c0-2.8-1.8-5.8-5-8.6Zm0 14.2a1.6 1.6 0 1 1 0-3.2 1.6 1.6 0 0 1 0 3.2Z"/></svg>',
6681
+ github: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M12 2C6.48 2 2 6.58 2 12.26c0 4.52 2.87 8.35 6.86 9.71.5.1.69-.22.69-.49 0-.24-.01-.87-.01-1.7-2.78.62-3.37-1.36-3.37-1.36-.45-1.17-1.11-1.48-1.11-1.48-.91-.64.07-.63.07-.63 1 .07 1.53 1.05 1.53 1.05.9 1.56 2.36 1.11 2.94.85.09-.67.35-1.11.63-1.37-2.22-.26-4.56-1.14-4.56-5.07 0-1.12.39-2.03 1.03-2.75-.1-.26-.45-1.3.1-2.7 0 0 .84-.28 2.75 1.05A9.2 9.2 0 0 1 12 6.84c.85 0 1.71.12 2.51.34 1.91-1.33 2.75-1.05 2.75-1.05.55 1.4.2 2.44.1 2.7.64.72 1.03 1.63 1.03 2.75 0 3.94-2.34 4.8-4.57 5.06.36.32.68.94.68 1.9 0 1.37-.01 2.47-.01 2.8 0 .27.18.6.7.49A10.03 10.03 0 0 0 22 12.26C22 6.58 17.52 2 12 2Z"/></svg>',
6682
+ gitlab: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#FC6D26" d="M23.2 9.4 12 2.2.8 9.4l1.4 8.2L12 21.8l9.8-4.2 1.4-8.2Z"/><path fill="#E24329" d="M12 2.2v7.1l3.8 1.9 1.8-5.5L12 2.2Z"/><path fill="#FC6D26" d="M12 10.2 7.7 12l1.8-5.5L12 2.2Z"/><path fill="#FCA326" d="M.8 9.4 12 21.8 7.7 12 12 10.2Z"/><path fill="#FC6D26" d="M12 10.2l4.3 1.8 6.1-2.6L12 2.2Z"/></svg>',
6683
+ neon: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 4.5c0-1 .8-1.7 1.7-1.3l9.5 4.3c1.1.5 1.8 1.6 1.8 2.8v8.6c0 1-.8 1.7-1.7 1.3L6.8 15.9A3 3 0 0 1 5 13.1V4.5Zm4.1 4.1v5l4.9 2.2v-5L9.1 8.6Z"/></svg>',
6684
+ planetscale: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M19.1 4.9A10 10 0 0 0 4.9 19.1L19.1 4.9Zm-12 16A10 10 0 0 0 20.9 7.1L7.1 20.9Z"/></svg>',
6685
+ mongodb: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2c3 3 4.8 6.2 4.8 9.6 0 4.2-2.2 7.1-4.8 9.9-2.6-2.8-4.8-5.7-4.8-9.9C7.2 8.2 9 5 12 2Zm0 5.2v10.2"/></svg>',
6686
+ turso: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7.5h16v3H4v-3Zm0 6h16v3H4v-3ZM7.5 4h9v3h-9V4Zm0 13h9v3h-9v-3Z"/></svg>',
6687
+ stripe: '<svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" aria-hidden="true"><path fill="#635BFF" d="M4.5 15.4c1.5.9 3.5 1.4 5.4 1.4 1.6 0 2.5-.4 2.5-1.2 0-.7-.7-1-3-1.5-3-.7-4.7-1.8-4.7-4.1 0-2.6 2.1-4.4 5.8-4.4 2.1 0 3.9.4 5.3 1.1v3.4a10 10 0 0 0-5.1-1.4c-1.5 0-2.2.4-2.2 1.1 0 .7.8 1 3 1.5 3.1.7 4.8 1.8 4.8 4.1 0 2.7-2.2 4.5-6.2 4.5-2.2 0-4.3-.5-5.6-1.3v-3.2Z"/></svg>',
6688
+ paddle: '<svg viewBox="0 0 90 90" aria-hidden="true"><rect x="11" y="11" width="68" height="68" rx="17" fill="#101318"/><rect x="11.5" y="11.5" width="67" height="67" rx="16.5" fill="none" stroke="#343942"/><path fill="#FFD21E" d="M8.49991 17C8.51217 21.6945 12.3128 25.5001 17 25.5001C12.3128 25.5001 8.51217 29.3055 8.49991 34C8.48783 29.3055 4.68717 25.5001 0 25.5001C4.68717 25.5001 8.48783 21.6945 8.49991 17Z" transform="translate(19.5 -31.5) scale(3)"/></svg>',
6689
+ vercel: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 4 22 20H2L12 4Z"/></svg>',
6690
+ netlify: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m12 2 3.1 5.4 6.2 1.2-4.2 4.7.8 6.3-5.9-2.6-5.9 2.6.8-6.3-4.2-4.7 6.2-1.2L12 2Z"/></svg>',
6691
+ aws: '<svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" aria-hidden="true"><path fill="#FF9900" d="M6.763 10.582c4.95-2.283 10.563-2.283 15.475 0 .532.243.848.741.848 1.256 0 .532-.323 1.026-.85 1.256-4.912 2.283-10.525 2.283-15.474 0-.528-.23-.851-.724-.851-.279 0-.544.098-.754.243-4.95 2.283-10.563 2.283-15.475 0-.532-.243-.848-.741-.848-1.256 0-.532.323-1.026.85-1.256.228-.098.492-.196.754-.196.279 0 .544.098.754.243Z"/><path fill="#FF9900" d="M12 3.65c-2.772 0-5.027 1.147-5.027 2.553S9.228 8.756 12 8.756s5.027-1.147 5.027-2.553S14.772 3.65 12 3.65Z"/></svg>',
6692
+ sentry: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3 22 20H2L12 3Zm0 5.1L6.8 17h2.1l3.1-5.3 3.1 5.3h2.1L12 8.1Z"/></svg>',
6693
+ posthog: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#1D4AFF" d="M5 6h8.06c3.19 0 5.44 2.06 5.44 5.31S16.25 16.62 13.06 16.62H5V6Z"/><path fill="#F54E00" d="M4.13 3.94v4.5h3.94a2.25 2.25 0 0 0 0-4.5H4.13Z"/><circle cx="13.5" cy="11.81" r="1.01" fill="#F9BD2B"/><path fill="#F54E00" d="m16.39 9.49 1.88-1.13.79 1.2-2.03 1.28-.64-1.35Zm.34 4.61 2.33.83-.74 1.54-2.33-.98.74-1.39Z"/></svg>',
6694
+ playwright: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#2EAD33" d="M8.4 8.2 5.4 17.2h2.3l.8-2.8h2.7l.8 2.8h2.3L11.6 8.2H8.4Zm1.1 4.8.8-2.4.8 2.4H9.5Z"/><path fill="#E2574C" d="M15.6 8.2 12.6 17.2h2.3l.8-2.8h2.7l.8 2.8h2.3L18.8 8.2h-3.2Zm1.1 4.8.8-2.4.8 2.4h-1.6Z"/><ellipse fill="#2EAD33" cx="9.5" cy="13.1" rx="1.1" ry="1.4"/><ellipse fill="#E2574C" cx="16.5" cy="13.1" rx="1.1" ry="1.4"/></svg>',
6695
+ logrocket: '<svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" aria-hidden="true"><rect x="2" y="2" width="20" height="20" rx="5" fill="#764ABC"/><path fill="#FFFFFF" d="M12 7.4c2.7 0 4.9 2.2 4.9 4.9s-2.2 4.9-4.9 4.9-4.9-2.2-4.9-4.9 4.9-4.9 4.9-4.9Zm0 1.9a3 3 0 1 0 0 6 3 3 0 0 0 0-6Zm-3.2 7.4h6.4v1.5H8.8v-1.5Z"/></svg>',
6696
+ figma: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M8.5 3h3.5v6H8.5a3 3 0 1 1 0-6Zm3.5 6h3.5a3 3 0 1 0 0-6H12v6Zm0 0h3.5a3 3 0 1 1 0 6H12V9Zm-3.5 0H12v6H8.5a3 3 0 1 1 0-6ZM8.5 15H12v2.5A3.5 3.5 0 1 1 8.5 15Z"/></svg>',
6697
+ storybook: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6.2 3.4 17.9 2l.7 18.1-12.4.7V3.4Zm8.7 3.2.3 2.4 1.7-1.3 1.7 1.1.3-3.4-4 .5v.7Zm-5.2 5.1c0 2 1.6 3.5 4.2 3.5 2.4 0 3.8-1.2 3.8-3 0-1.7-1.1-2.6-3.4-3l-1.2-.2c-.7-.1-1-.4-1-.8 0-.5.5-.8 1.3-.8.9 0 1.5.4 1.8 1.1l2.1-.8c-.5-1.4-1.8-2.2-3.8-2.2-2.3 0-3.8 1.2-3.8 2.9 0 1.6 1.1 2.5 3.3 2.9l1.2.2c.8.1 1.1.4 1.1.9 0 .6-.5.9-1.4.9-1.1 0-1.8-.5-2.1-1.3l-2.1.7Z"/></svg>',
6698
+ "product-spec": '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 3.2h9.2L19 7v13.8H6V3.2Zm8.2 1.9v3h3l-3-3ZM8.4 10h7.2v1.7H8.4V10Zm0 3.2h7.2v1.7H8.4v-1.7Zm0 3.2h4.9v1.7H8.4v-1.7Z"/></svg>',
6699
+ "route-map": '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6.5 4.2a3 3 0 0 1 3 3c0 2-3 5.1-3 5.1s-3-3.1-3-5.1a3 3 0 0 1 3-3Zm0 1.8a1.2 1.2 0 1 0 0 2.4 1.2 1.2 0 0 0 0-2.4Zm11 5.8a3 3 0 0 1 3 3c0 2-3 5.1-3 5.1s-3-3.1-3-5.1a3 3 0 0 1 3-3Zm0 1.8a1.2 1.2 0 1 0 0 2.4 1.2 1.2 0 0 0 0-2.4ZM9.5 7h2.1c2.8 0 4.9 2 4.9 4.8h-2c0-1.7-1.2-2.8-2.9-2.8H9.5V7Zm4.9 10h-2.1c-2.8 0-4.9-2-4.9-4.8h2c0 1.7 1.2 2.8 2.9 2.8h2.1v2Z"/></svg>',
6700
+ react: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="2.2"/><ellipse cx="12" cy="12" rx="9" ry="3.6" fill="none" stroke="currentColor" stroke-width="1.6"/><ellipse cx="12" cy="12" rx="9" ry="3.6" fill="none" stroke="currentColor" stroke-width="1.6" transform="rotate(60 12 12)"/><ellipse cx="12" cy="12" rx="9" ry="3.6" fill="none" stroke="currentColor" stroke-width="1.6" transform="rotate(120 12 12)"/></svg>',
6701
+ vue: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2.6 4h5.1L12 11.4 16.3 4h5.1L12 20 2.6 4Zm5.6 0L12 10.5 15.8 4h-2.7L12 5.9 10.9 4H8.2Z"/></svg>',
6702
+ svelte: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M16.9 3.4a5.2 5.2 0 0 0-6.9 1.5L6.3 9.7a4.6 4.6 0 0 0 .8 6.4 5.1 5.1 0 0 0 6.9-1.2l.6-.8a1.6 1.6 0 0 0-.3-2.2 1.8 1.8 0 0 0-2.4.4l-.6.8a1.3 1.3 0 0 1-1.8.3 1.2 1.2 0 0 1-.2-1.6L13 7a1.3 1.3 0 0 1 1.8-.4c.5.4.7 1.1.3 1.6l-.3.4 2.8 2 .3-.4a4.7 4.7 0 0 0-1-6.8Zm-9.8 17.2a5.2 5.2 0 0 0 6.9-1.5l3.7-4.8a4.6 4.6 0 0 0-.8-6.4A5.1 5.1 0 0 0 10 9.1l-.6.8a1.6 1.6 0 0 0 .3 2.2 1.8 1.8 0 0 0 2.4-.4l.6-.8a1.3 1.3 0 0 1 1.8-.3 1.2 1.2 0 0 1 .2 1.6L11 17a1.3 1.3 0 0 1-1.8.4 1.2 1.2 0 0 1-.3-1.6l.3-.4-2.8-2-.3.4a4.7 4.7 0 0 0 1 6.8Z"/></svg>',
6703
+ angular: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2.5 20.5 5.6 19.2 17 12 21.5 4.8 17 3.5 5.6 12 2.5Zm0 4.2-5 11h2.4l1-2.4h3.2l1 2.4H17l-5-11Zm0 3.8 1.1 2.9h-2.2l1.1-2.9Z"/></svg>',
6704
+ nodejs: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2.5 20.2 7v10L12 21.5 3.8 17V7L12 2.5Zm-3.8 6.7v5.6h1.9v-3.1l3.8 3.1h1.9V9.2h-1.9v3.2l-3.8-3.2H8.2Z"/></svg>',
6705
+ python: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M11.8 3c2.7 0 4.2.8 4.2 2.5V9H9.6A2.6 2.6 0 0 0 7 11.6V13H4.5C3.5 13 3 12.2 3 11c0-3.2 1.9-4.9 5.6-4.9h3.8V5H8.8V3.6A10 10 0 0 1 11.8 3Zm-1.4 1.5a.8.8 0 1 0 0 1.6.8.8 0 0 0 0-1.6ZM12.2 21c-2.7 0-4.2-.8-4.2-2.5V15h6.4a2.6 2.6 0 0 0 2.6-2.6V11h2.5c1 0 1.5.8 1.5 2 0 3.2-1.9 4.9-5.6 4.9h-3.8V19h3.6v1.4a10 10 0 0 1-3 .6Zm1.4-3.1a.8.8 0 1 0 0 1.6.8.8 0 0 0 0-1.6Z"/></svg>',
6706
+ rails: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 17.7C5.8 9.5 11.7 5.2 21 4.8v3.1C13.5 8.3 8.7 11.6 6 18.5L3 17.7Zm4.6.7c2.1-5 5.9-7.5 11.4-7.9v2.4c-4.4.4-7.4 2.5-9 6.2l-2.4-.7Zm4.7.7c1.3-2.4 3.4-3.7 6.7-4.1v2.2c-2.2.4-3.6 1.3-4.5 2.6l-2.2-.7Z"/></svg>',
6707
+ go: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 8.2h7.4v1.5H3V8.2Zm-1 3h7.4v1.5H2v-1.5Zm2 3h5.4v1.5H4v-1.5Zm10.5-6.1c3.1 0 5.5 2 5.5 4.6s-2.4 4.6-5.5 4.6-5.5-2-5.5-4.6 2.4-4.6 5.5-4.6Zm0 2.1c-1.7 0-3 1.1-3 2.5s1.3 2.5 3 2.5 3-1.1 3-2.5-1.3-2.5-3-2.5Zm5.1-2h2.4l-1.2 9h-2.4l1.2-9Z"/></svg>',
6708
+ "testing-library": '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 3h10v4.2l-3.4 4.5 5.4 7.9c.5.8 0 1.9-1 1.9H6c-1 0-1.6-1.1-1-1.9l5.4-7.9L7 7.2V3Zm3 3.5 2 2.7 2-2.7H10Zm1.9 8.4-2.6 3.8h5.4l-2.8-3.8Z"/></svg>',
6709
+ vitest: '<svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" aria-hidden="true"><path fill="#FCC72B" d="M13.4 3 21 7.4 12 21 3 7.4 10.6 3l1.4 4.5L13.4 3Zm-1.4 7.3-2.1 4h4.2l-2.1-4Z"/><path fill="#729B1B" d="M12 10.3 9.9 14.3h4.2L12 10.3Z"/></svg>',
6710
+ "rate-limit": '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3a9 9 0 1 0 9 9h-3a6 6 0 1 1-1.8-4.3L13 11h8V3l-2.7 2.7A9 9 0 0 0 12 3Z"/></svg>',
6711
+ "bot-protection": '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2 20 6v6c0 5-3.5 8.6-8 10-4.5-1.4-8-5-8-10V6l8-4Zm-3 8h6v4H9v-4Zm1.5-3.2h3V10h-3V6.8Z"/></svg>',
6712
+ "secrets-hygiene": '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 10V7a5 5 0 0 1 10 0v3h1.5v11h-13V10H7Zm3 0h4V7a2 2 0 0 0-4 0v3Z"/></svg>'
6713
+ };
6714
+ var ALIASES = {
6715
+ authjs: "authjs",
6716
+ "auth.js": "authjs",
6717
+ nextauth: "authjs",
6718
+ posthog: "posthog",
6719
+ node: "nodejs",
6720
+ nodejs: "nodejs",
6721
+ "node.js": "nodejs",
6722
+ express: "nodejs",
6723
+ expressjs: "nodejs",
6724
+ supabase: "supabase",
6725
+ clerk: "clerk",
6726
+ stripe: "stripe",
6727
+ vercel: "vercel",
6728
+ "supabase auth": "supabase",
6729
+ supabaseauth: "supabase",
6730
+ "bot protection": "bot-protection",
6731
+ botprotection: "bot-protection",
6732
+ "secrets hygiene": "secrets-hygiene",
6733
+ secretshygiene: "secrets-hygiene",
6734
+ "rate limiting": "rate-limit",
6735
+ ratelimiting: "rate-limit",
6736
+ polar: "polar",
6737
+ lemonsqueezy: "lemon-squeezy",
6738
+ "lemon squeezy": "lemon-squeezy",
6739
+ "lemon-squeezy": "lemon-squeezy",
6740
+ github: "github",
6741
+ gitlab: "gitlab",
6742
+ auth0: "auth0",
6743
+ "better-auth": "better-auth",
6744
+ betterauth: "better-auth",
6745
+ "mongodb atlas": "mongodb",
6746
+ logrocket: "logrocket",
6747
+ sentry: "sentry",
6748
+ figma: "figma",
6749
+ react: "react",
6750
+ vitest: "vitest",
6751
+ playwright: "playwright",
6752
+ neon: "neon",
6753
+ planetscale: "planetscale",
6754
+ "planet scale": "planetscale",
6755
+ mongodb: "mongodb",
6756
+ netlify: "netlify",
6757
+ render: "render",
6758
+ rendercom: "render",
6759
+ railway: "railway",
6760
+ railwayapp: "railway",
6761
+ cloudflare: "cloudflare",
6762
+ cloudflarepages: "cloudflare",
6763
+ workers: "cloudflare",
6764
+ aws: "aws",
6765
+ paddle: "paddle",
6766
+ turso: "turso",
6767
+ vue: "vue",
6768
+ svelte: "svelte",
6769
+ angular: "angular",
6770
+ python: "python",
6771
+ rails: "rails",
6772
+ go: "go",
6773
+ storybook: "storybook",
6774
+ productspec: "product-spec",
6775
+ prd: "product-spec",
6776
+ routemap: "route-map",
6777
+ routes: "route-map",
6778
+ ratelimit: "rate-limit"
6779
+ };
6780
+ var INLINE_ONLY_LOGO_KEYS = /* @__PURE__ */ new Set(["better-auth", "paddle", "polar"]);
6781
+ var PROVIDER_ASSET_FILES = {
6782
+ authjs: "provider-authjs.svg",
6783
+ logrocket: "provider-logrocket.svg",
6784
+ aws: "provider-aws.svg"
6785
+ };
6786
+ var REPORT_ASSET_PREFIX = "report/assets";
6787
+ function providerAssetUrl(key) {
6788
+ const file = PROVIDER_ASSET_FILES[key];
6789
+ return file ? `${REPORT_ASSET_PREFIX}/${file}` : void 0;
6790
+ }
6791
+ function providerAssetUrls() {
6792
+ return Object.fromEntries(
6793
+ Object.keys(PROVIDER_ASSET_FILES).map((key) => [key, providerAssetUrl(key)]).filter((entry) => Boolean(entry[1]))
6794
+ );
6795
+ }
6796
+ var PROVIDER_ICON_URLS = {
6797
+ supabase: "https://cdn.simpleicons.org/supabase/3ECF8E",
6798
+ clerk: "https://cdn.simpleicons.org/clerk/6C47FF",
6799
+ auth0: "https://cdn.simpleicons.org/auth0/EB5424",
6800
+ neon: "https://cdn.simpleicons.org/neon/00E599",
6801
+ planetscale: "https://cdn.simpleicons.org/planetscale/000000",
6802
+ mongodb: "https://cdn.simpleicons.org/mongodb/47A248",
6803
+ turso: "https://cdn.simpleicons.org/turso/4FF8D2",
6804
+ "lemon-squeezy": "https://cdn.simpleicons.org/lemonsqueezy/FFC233",
6805
+ stripe: "https://cdn.simpleicons.org/stripe/635BFF",
6806
+ github: "https://cdn.simpleicons.org/github/181717",
6807
+ gitlab: "https://cdn.simpleicons.org/gitlab/FC6D26",
6808
+ vercel: "https://cdn.simpleicons.org/vercel/000000",
6809
+ netlify: "https://cdn.simpleicons.org/netlify/00C7B7",
6810
+ render: "https://cdn.simpleicons.org/render/46E3B7",
6811
+ railway: "https://cdn.simpleicons.org/railway/0B0D0E",
6812
+ cloudflare: "https://cdn.simpleicons.org/cloudflare/F38020",
6813
+ sentry: "https://cdn.simpleicons.org/sentry/362D59",
6814
+ posthog: "https://cdn.simpleicons.org/posthog/F54E00",
6815
+ playwright: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/playwright/playwright-original.svg",
6816
+ figma: "https://cdn.simpleicons.org/figma/F24E1E",
6817
+ storybook: "https://cdn.simpleicons.org/storybook/FF4785",
6818
+ react: "https://cdn.simpleicons.org/react/61DAFB",
6819
+ vue: "https://cdn.simpleicons.org/vuedotjs/4FC08D",
6820
+ svelte: "https://cdn.simpleicons.org/svelte/FF3E00",
6821
+ angular: "https://cdn.simpleicons.org/angular/DD0031",
6822
+ nodejs: "https://cdn.simpleicons.org/nodedotjs/5FA04E",
6823
+ python: "https://cdn.simpleicons.org/python/3776AB",
6824
+ rails: "https://cdn.simpleicons.org/rubyonrails/D30001",
6825
+ go: "https://cdn.simpleicons.org/go/00ADD8",
6826
+ "testing-library": "https://cdn.simpleicons.org/testinglibrary/E33332",
6827
+ vitest: "https://cdn.simpleicons.org/vitest/FCC72B"
6828
+ };
6829
+ var BRAND_LOGO_KEYS = /* @__PURE__ */ new Set([
6830
+ "auth0",
6831
+ "authjs",
6832
+ "aws",
6833
+ "better-auth",
6834
+ "clerk",
6835
+ "gitlab",
6836
+ "logrocket",
6837
+ "auth0",
6838
+ "paddle",
6839
+ "playwright",
6840
+ "polar",
6841
+ "posthog",
6842
+ "stripe",
6843
+ "vitest"
6844
+ ]);
6845
+ function resolveLogoKey(raw, compact) {
6846
+ if (ALIASES[raw]) {
6847
+ return ALIASES[raw];
6848
+ }
6849
+ if (ALIASES[compact]) {
6850
+ return ALIASES[compact];
6851
+ }
6852
+ if (PROVIDER_LOGOS[compact]) {
6853
+ return compact;
6854
+ }
6855
+ const parts = raw.split("-").filter(Boolean);
6856
+ if (parts.length >= 2) {
6857
+ const first = parts[0];
6858
+ if (PROVIDER_LOGOS[first] || ALIASES[first]) {
6859
+ return ALIASES[first] ?? first;
6860
+ }
6861
+ const twoPart = parts.slice(0, 2).join("");
6862
+ if (PROVIDER_LOGOS[twoPart] || ALIASES[twoPart]) {
6863
+ return ALIASES[twoPart] ?? twoPart;
6864
+ }
6865
+ const twoHyphen = parts.slice(0, 2).join("-");
6866
+ if (ALIASES[twoHyphen]) {
6867
+ return ALIASES[twoHyphen];
6868
+ }
6869
+ }
6870
+ return compact;
6871
+ }
6872
+ function normalizeProviderKey2(providerOrLabel) {
6873
+ if (!providerOrLabel) {
6874
+ return "";
6875
+ }
6876
+ const raw = String(providerOrLabel).trim().toLowerCase();
6877
+ const compact = raw.replace(/[^a-z0-9]+/g, "");
6878
+ return resolveLogoKey(raw, compact);
6879
+ }
6880
+ function resolveProviderLogoKey(...candidates) {
6881
+ for (const candidate of candidates) {
6882
+ const key = normalizeProviderKey2(candidate);
6883
+ if (key && PROVIDER_LOGOS[key]) {
6884
+ return key;
6885
+ }
6886
+ }
6887
+ for (const candidate of candidates) {
6888
+ const key = normalizeProviderKey2(candidate);
6889
+ if (key) {
6890
+ return key;
6891
+ }
6892
+ }
6893
+ return "";
6894
+ }
6895
+ function providerLogoClass(providerOrLabel, ...moreCandidates) {
6896
+ const key = resolveProviderLogoKey(providerOrLabel, ...moreCandidates) || normalizeProviderKey2(providerOrLabel);
6897
+ if (!key) {
6898
+ return "";
6899
+ }
6900
+ return ` provider-logo--${key}${BRAND_LOGO_KEYS.has(key) ? " provider-logo--brand" : ""}`;
6901
+ }
6902
+ function providerIconImgHtml(iconUrl, logoKey) {
6903
+ return `<img class="provider-logo__img" src="${iconUrl}" alt="" decoding="async" data-provider-logo-key="${logoKey}" />`;
6904
+ }
6905
+ function providerLogoHtml(providerOrLabel, labelFallback, ...moreCandidates) {
6906
+ const key = resolveProviderLogoKey(providerOrLabel, labelFallback, ...moreCandidates) || normalizeProviderKey2(providerOrLabel);
6907
+ const svg = key && PROVIDER_LOGOS[key];
6908
+ if (key && INLINE_ONLY_LOGO_KEYS.has(key) && svg) {
6909
+ return svg;
6910
+ }
6911
+ const assetUrl = key ? providerAssetUrl(key) : void 0;
6912
+ if (assetUrl) {
6913
+ return providerIconImgHtml(assetUrl, key);
6914
+ }
6915
+ const iconUrl = key && PROVIDER_ICON_URLS[key];
6916
+ if (iconUrl) {
6917
+ return providerIconImgHtml(iconUrl, key);
6918
+ }
6919
+ if (svg) {
6920
+ return svg;
6921
+ }
6922
+ const label = (labelFallback ?? providerOrLabel ?? "?").trim();
6923
+ const initials = label ? label.slice(0, 2).toUpperCase() : "?";
6924
+ return `<span aria-hidden="true">${initials}</span>`;
6925
+ }
6926
+ var PROVIDER_BENEFITS = {
6927
+ supabase: "Best when you want auth, data, and storage in one stack.",
6928
+ clerk: "Fastest managed auth path with clean session primitives.",
6929
+ authjs: "Best when you want framework-owned auth and full control.",
6930
+ neon: "Good serverless Postgres fit for Vercel-style apps.",
6931
+ planetscale: "Good fit for branching MySQL workflows.",
6932
+ mongodb: "Good when the app already models document data.",
6933
+ turso: "Good edge SQLite option for lightweight apps.",
6934
+ stripe: "Best supported global payment ecosystem.",
6935
+ paddle: "Handles merchant-of-record subscription operations.",
6936
+ polar: "MoR billing built for developers shipping SaaS.",
6937
+ "lemon-squeezy": "Sell digital products and subscriptions with less setup.",
6938
+ github: "Best for GitHub Actions CI and required PR checks.",
6939
+ gitlab: "Best for GitLab CI pipelines and merge gates.",
6940
+ vercel: "Best fit for preview deploys, envs, and domains.",
6941
+ netlify: "Good fit for static and serverless deploy workflows.",
6942
+ render: "Good fit for simple web services and background jobs.",
6943
+ railway: "Good fit for fast deploy loops and managed infra.",
6944
+ cloudflare: "Good fit for Pages, Workers, DNS, and edge stack.",
6945
+ aws: "Best when the app needs direct cloud infrastructure control.",
6946
+ sentry: "Best first choice for production errors and traces.",
6947
+ posthog: "Best for product analytics and funnel verification.",
6948
+ auth0: "Enterprise identity with rules, MFA, and social login.",
6949
+ "better-auth": "Type-safe auth you own in your codebase.",
6950
+ playwright: "Best for browser-flow verification and UI regression checks.",
6951
+ logrocket: "Best when session replay is the missing evidence.",
6952
+ "rate-limit": "Protects API routes from abuse and retry loops.",
6953
+ "bot-protection": "Adds bot screening before expensive flows.",
6954
+ "secrets-hygiene": "Hardens env handling and secret exposure risk.",
6955
+ figma: "Best for screen flows, handoff, and UX alignment.",
6956
+ storybook: "Best for proving component states before production.",
6957
+ react: "Best fit for common component UI and app-router projects.",
6958
+ vue: "Good fit for Vue or Nuxt product interfaces.",
6959
+ svelte: "Good fit for SvelteKit route-first apps.",
6960
+ angular: "Good fit for structured enterprise frontend workflows.",
6961
+ nodejs: "Best fit for JavaScript API routes and server behavior.",
6962
+ python: "Good fit for FastAPI-style APIs and typed validation.",
6963
+ rails: "Good fit for full-stack CRUD and convention-heavy APIs.",
6964
+ go: "Good fit for lean, fast HTTP services.",
6965
+ vitest: "Fast unit and integration tests for modern JS apps.",
6966
+ "testing-library": "User-centric component testing primitives."
6967
+ };
6968
+ function providerLogosPayloadJson() {
6969
+ return JSON.stringify({
6970
+ logos: PROVIDER_LOGOS,
6971
+ iconUrls: PROVIDER_ICON_URLS,
6972
+ assetUrls: providerAssetUrls(),
6973
+ inlineOnly: [...INLINE_ONLY_LOGO_KEYS],
6974
+ aliases: ALIASES,
6975
+ benefits: PROVIDER_BENEFITS,
6976
+ brandKeys: [...BRAND_LOGO_KEYS]
6977
+ }).replace(/</g, "\\u003c");
6978
+ }
6979
+
6980
+ // src/report/reportHtml.ts
6981
+ var GEIST_FONTS = "https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&amp;family=JetBrains+Mono:wght@400;600&amp;display=swap";
6982
+ function escapeHtml(value) {
6983
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
6984
+ }
6985
+ var CATEGORY_META = [
6986
+ { key: "appFlow", label: "App Flow" },
6987
+ { key: "frontend", label: "Frontend" },
6988
+ { key: "backend", label: "Backend / API" },
6989
+ { key: "auth", label: "Auth" },
6990
+ { key: "database", label: "Database" },
6991
+ { key: "payments", label: "Payments" },
6992
+ { key: "deployment", label: "Deployment" },
6993
+ { key: "monitoring", label: "Monitoring / Analytics" },
6994
+ { key: "security", label: "Security" },
6995
+ { key: "testing", label: "Testing" },
6996
+ { key: "landing", label: "Landing / Onboarding" },
6997
+ { key: "errorHandling", label: "Error Handling" }
6998
+ ];
6999
+ function gapCountForArea(artifact, areaKey) {
7000
+ return artifact.gaps.filter((g2) => g2.primaryMapCategory === areaKey).length;
7001
+ }
7002
+ function defaultAreaKey(artifact) {
7003
+ const fromGap = artifact.gaps[0]?.primaryMapCategory;
7004
+ if (fromGap) {
7005
+ return fromGap;
7006
+ }
7007
+ const areas = artifact.missionGraph.areas ?? [];
7008
+ if (areas.some((a) => a.key === "frontend")) {
7009
+ return "frontend";
7010
+ }
7011
+ return areas[0]?.key ?? "frontend";
7012
+ }
7013
+ function buildAccountStripHtml(artifact) {
7014
+ if (!artifact.accountEmail) {
7015
+ return "";
7016
+ }
7017
+ const planLabel = artifact.plan === "pro" ? "Pro" : "Free";
7018
+ const usage = artifact.usageLine ? `<span class="account-bar__meta">${escapeHtml(artifact.usageLine)}</span>` : "";
7019
+ return `<div class="station-account-strip mc-session" aria-label="Account">
7020
+ <div class="account-bar">
7021
+ <div class="account-bar__main">
7022
+ <div class="account-bar__identity">
7023
+ <div class="account-bar__id-text">
7024
+ <span class="account-bar__email">${escapeHtml(artifact.accountEmail)}</span>
7025
+ ${usage}
7026
+ </div>
7027
+ </div>
7028
+ <div class="account-bar__actions">
7029
+ <span class="account-bar__plan">${escapeHtml(planLabel)}</span>
7030
+ </div>
7031
+ </div>
7032
+ </div>
7033
+ </div>`;
7034
+ }
7035
+ function buildNodeHtml(artifact, meta, selectedAreaKey) {
7036
+ const area = (artifact.missionGraph.areas ?? []).find((a) => a.key === meta.key);
7037
+ const selectedOverride = artifact.selectedProviders?.[meta.key] ?? "";
7038
+ const mission = preferredMissionForArea(area, selectedOverride);
7039
+ const selectedProvider = selectedOverride || mission?.provider || mission?.providerLabel || "";
7040
+ const providerLabel3 = mission?.providerLabel ?? "Not selected";
7041
+ const readiness = mission?.readinessPercent ?? area?.readinessPercent ?? 0;
7042
+ const openChecks = openChecksForMission(mission);
7043
+ const modelGaps = gapCountForArea(artifact, meta.key);
7044
+ const stateClass = modelGaps > 0 ? " studio-node--critical" : openChecks > 0 ? " studio-node--warning" : area ? " studio-node--in-project" : "";
7045
+ const selectedClass = selectedAreaKey === meta.key ? " studio-node--selected" : "";
7046
+ const metaText = modelGaps > 0 ? `${modelGaps} stack fix${modelGaps === 1 ? "" : "es"}` : openChecks > 0 ? `${openChecks} stack fix${openChecks === 1 ? "" : "es"}` : `${readiness}% health`;
7047
+ const logoClass = providerLogoClass(mission?.provider, mission?.key, String(selectedProvider), providerLabel3);
7048
+ const logoInner = providerLogoHtml(
7049
+ mission?.provider,
7050
+ providerLabel3,
7051
+ mission?.key,
7052
+ String(selectedProvider)
7053
+ );
7054
+ const gapBadge = modelGaps > 0 ? `<span class="studio-node__raven-badge" role="presentation">Gap ${modelGaps}</span>` : "";
7055
+ return `<button type="button" class="studio-node studio-node--${escapeHtml(meta.key)} provider-logo${logoClass}${stateClass}${selectedClass}" data-area-key="${escapeHtml(meta.key)}" aria-label="${escapeHtml(meta.label)}, ${escapeHtml(providerLabel3)}, ${escapeHtml(metaText)}">
7056
+ <span class="studio-node__logo provider-logo${logoClass}" aria-hidden="true">${logoInner}</span>
7057
+ <span class="studio-node__title">${escapeHtml(meta.label)}</span>
7058
+ <span class="studio-node__provider">${escapeHtml(providerLabel3)}</span>
7059
+ <span class="studio-node__meta">${escapeHtml(metaText)}</span>
7060
+ ${gapBadge}
7061
+ </button>`;
7062
+ }
7063
+ function generateReportHtml(artifact) {
7064
+ const hydrated = hydrateArtifactForReport(artifact);
7065
+ const dataJson = JSON.stringify(hydrated).replace(/</g, "\\u003c");
7066
+ const defaultKey = defaultAreaKey(hydrated);
7067
+ const nodesHtml = CATEGORY_META.map((meta) => buildNodeHtml(hydrated, meta, defaultKey)).join("\n");
7068
+ const accountStrip = buildAccountStripHtml(hydrated);
7069
+ const logosJson = providerLogosPayloadJson();
7070
+ const scannedLabel = escapeHtml(hydrated.scannedAt);
7071
+ return `<!DOCTYPE html>
7072
+ <html lang="en" class="cli-mission-report" data-surface="panel" data-skin="editorial">
7073
+ <head>
7074
+ <meta charset="utf-8" />
7075
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7076
+ <title>VibeRaven Mission Map</title>
7077
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
7078
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
7079
+ <link rel="stylesheet" href="${GEIST_FONTS}" />
7080
+ <link rel="stylesheet" href="report/station.css" />
7081
+ <link rel="stylesheet" href="report/report-cli.css" />
7082
+ </head>
7083
+ <body class="station-results-active">
7084
+ <div class="station-app">
7085
+ ${accountStrip}
7086
+ <div class="mc-state mc-state--results" role="region" aria-label="Mission Map results">
7087
+ <div class="studio-shell" aria-label="VibeRaven Studio cockpit">
7088
+ <header class="studio-top-rail" aria-label="Studio status">
7089
+ <img class="studio-top-rail__logo" src="report/assets/viberaven-logo.png" alt="" width="32" height="32" />
7090
+ <span class="studio-top-rail__brand"><span>VIBERAVEN / MISSION MAP</span></span>
7091
+ <div class="studio-top-rail__build">Last scan \xB7 ${scannedLabel}</div>
7092
+ </header>
7093
+ <div class="studio-workspace">
7094
+ <main class="studio-map-canvas" aria-label="Interactive full-stack system map">
7095
+ <section class="studio-system-map" aria-label="Production system map">
7096
+ <div class="studio-node-layer">
7097
+ <div class="studio-connector-layer" aria-hidden="true"></div>
7098
+ <div class="studio-core-group">
7099
+ <div class="studio-core-node" aria-label="Production core score">
7100
+ <img class="studio-core-node__mark" src="report/assets/viberaven-logo.png" alt="VibeRaven" width="92" height="92" />
7101
+ <strong>${hydrated.productionCorePercent}%</strong>
7102
+ <small>Production core</small>
7103
+ </div>
7104
+ </div>
7105
+ ${nodesHtml}
7106
+ </div>
7107
+ </section>
7108
+ </main>
7109
+ <aside class="studio-setup-panel" id="detail-panel" aria-live="polite"></aside>
7110
+ </div>
7111
+ </div>
7112
+ </div>
7113
+ </div>
7114
+ <script type="application/json" id="scan-data">${dataJson}</script>
7115
+ <script type="application/json" id="default-area-key">${JSON.stringify(defaultKey)}</script>
7116
+ <script type="application/json" id="provider-logos">${logosJson}</script>
7117
+ <script>${PANEL_CLIENT_SCRIPT}</script>
7118
+ </body>
7119
+ </html>`;
7120
+ }
7121
+
7122
+ // src/report/reportAssets.ts
7123
+ var import_node_fs4 = require("node:fs");
7124
+ var import_node_path4 = require("node:path");
7125
+ function getBundledReportAssetsDir() {
7126
+ const base = __dirname;
7127
+ const candidates = [
7128
+ (0, import_node_path4.join)(base, "report"),
7129
+ (0, import_node_path4.join)(base, "..", "assets", "report"),
7130
+ (0, import_node_path4.join)(base, "..", "..", "assets", "report")
7131
+ ];
7132
+ for (const dir of candidates) {
7133
+ if ((0, import_node_fs4.existsSync)((0, import_node_path4.join)(dir, "station.css"))) {
7134
+ return dir;
7135
+ }
7136
+ }
7137
+ throw new Error("Report assets missing. Run `npm run sync-report-assets` and `npm run build` in packages/cli.");
7138
+ }
7139
+ var REPORT_ASSET_FILES = [
7140
+ "station.css",
7141
+ "report-cli.css",
7142
+ "assets/viberaven-logo.png",
7143
+ "assets/provider-authjs.svg",
7144
+ "assets/provider-aws.svg",
7145
+ "assets/provider-logrocket.svg"
7146
+ ];
7147
+
7148
+ // src/sanitizeArtifact.ts
7149
+ var INLINE_SECRET_PATTERNS = [
7150
+ /\b(sk_(?:live|test)_[A-Za-z0-9]{12,}|sk-proj-[A-Za-z0-9_-]{16,}|sk-[A-Za-z0-9_-]{20,})\b/g,
7151
+ /\b(whsec_[A-Za-z0-9]{12,}|rk_(?:live|test)_[A-Za-z0-9]{12,})\b/g,
7152
+ /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g
7153
+ ];
7154
+ var SENSITIVE_ENV_KEYS = /^(?:OPENAI_API_KEY|OPENAI_KEY|ANTHROPIC_API_KEY|VIBERAVEN_ACCESS_TOKEN|VRAVEN_.*|SUPABASE_SERVICE_ROLE_KEY|.*_SECRET_KEY)$/i;
7155
+ function redactString(value) {
7156
+ let out = value;
7157
+ for (const pattern of INLINE_SECRET_PATTERNS) {
7158
+ out = out.replace(pattern, "[REDACTED_SECRET]");
7159
+ }
7160
+ return out.replace(
7161
+ /\b([A-Za-z0-9_]*(?:API_KEY|SECRET|TOKEN|PASSWORD|PRIVATE_KEY)[A-Za-z0-9_]*)\s*=\s*["']?[^"'\s;,]+["']?/gi,
7162
+ "$1=[REDACTED]"
7163
+ );
7164
+ }
7165
+ function redactUnknown(value) {
7166
+ if (typeof value === "string") {
7167
+ return redactString(value);
7168
+ }
7169
+ if (Array.isArray(value)) {
7170
+ return value.map(redactUnknown);
7171
+ }
7172
+ if (value && typeof value === "object") {
7173
+ const record = value;
7174
+ const next = {};
7175
+ for (const [key, entry] of Object.entries(record)) {
7176
+ if (SENSITIVE_ENV_KEYS.test(key)) {
7177
+ next[key] = "[REDACTED]";
7178
+ } else {
7179
+ next[key] = redactUnknown(entry);
7180
+ }
7181
+ }
7182
+ return next;
7183
+ }
7184
+ return value;
7185
+ }
7186
+ function sanitizeArtifactForDisk(artifact) {
7187
+ return redactUnknown(artifact);
7188
+ }
7189
+
7190
+ // src/artifacts.ts
7191
+ async function copyReportAssets(reportAssetsDir) {
7192
+ const sourceDir = getBundledReportAssetsDir();
7193
+ await (0, import_promises3.mkdir)((0, import_node_path5.join)(reportAssetsDir, "assets"), { recursive: true });
7194
+ for (const rel of REPORT_ASSET_FILES) {
7195
+ await (0, import_promises3.copyFile)((0, import_node_path5.join)(sourceDir, rel), (0, import_node_path5.join)(reportAssetsDir, rel));
7196
+ }
7197
+ }
7198
+ async function writeScanArtifacts(options) {
7199
+ const cwd = options.cwd ?? options.artifact.workspacePath;
7200
+ const dir = getProjectArtifactsDir(cwd);
7201
+ await (0, import_promises3.mkdir)(dir, { recursive: true });
7202
+ const jsonPath = (0, import_node_path5.join)(dir, "last-scan.json");
7203
+ const summaryPath = (0, import_node_path5.join)(dir, "agent-summary.md");
7204
+ const reportPath = (0, import_node_path5.join)(dir, "report.html");
7205
+ const reportAssetsDir = (0, import_node_path5.join)(dir, "report");
7206
+ const safe = sanitizeArtifactForDisk(options.artifact);
7207
+ const json = `${JSON.stringify(safe, null, 2)}
7208
+ `;
7209
+ const summary = generateAgentSummary(safe);
7210
+ const html = generateReportHtml(safe);
7211
+ await copyReportAssets(reportAssetsDir);
7212
+ await (0, import_promises3.writeFile)(jsonPath, json, "utf-8");
7213
+ await (0, import_promises3.writeFile)(summaryPath, summary, "utf-8");
7214
+ await (0, import_promises3.writeFile)(reportPath, html, "utf-8");
7215
+ return { dir, jsonPath, summaryPath, reportPath, reportAssetsDir };
7216
+ }
7217
+
7218
+ // src/tui/menu.ts
7219
+ var import_promises4 = require("node:fs/promises");
7220
+ var import_node_path6 = require("node:path");
7221
+ var SEVERITY_RANK2 = {
7222
+ critical: 0,
7223
+ warning: 1,
7224
+ info: 2
7225
+ };
7226
+ var ScanNotFoundError = class extends Error {
7227
+ constructor(message = "No scan found. Run a scan first.") {
7228
+ super(message);
7229
+ this.name = "ScanNotFoundError";
7230
+ }
7231
+ };
7232
+ function isScanNotFoundError(error) {
7233
+ return error instanceof ScanNotFoundError;
7234
+ }
7235
+ function needsScanMessage(startDir) {
7236
+ const cwd = startDir ?? process.cwd();
7237
+ return [
7238
+ "No CLI scan found for this folder.",
7239
+ `Looking from: ${cwd}`,
7240
+ 'Choose "Scan project", or run the menu from your repo root (where .viberaven/ lives).',
7241
+ "VS Code extension scans stay inside the editor \u2014 run a CLI scan once to create .viberaven/ on disk."
7242
+ ].join("\n");
7243
+ }
7244
+ function sortGapsByPriority(gaps) {
7245
+ return [...gaps].sort(
7246
+ (a, b3) => SEVERITY_RANK2[a.severity] - SEVERITY_RANK2[b3.severity] || a.title.localeCompare(b3.title)
7247
+ );
7248
+ }
7249
+ function pickGap(artifact, options = {}) {
7250
+ if (options.gapId) {
7251
+ return artifact.gaps.find((g2) => g2.id === options.gapId);
7252
+ }
7253
+ if (options.provider) {
7254
+ const key = options.provider.toLowerCase();
7255
+ return artifact.gaps.find(
7256
+ (g2) => g2.primaryMapCategory === key || g2.title.toLowerCase().includes(key) || g2.id.toLowerCase().includes(key)
7257
+ );
7258
+ }
7259
+ if (options.area) {
7260
+ return artifact.gaps.find((g2) => g2.primaryMapCategory === options.area);
7261
+ }
7262
+ return sortGapsByPriority(artifact.gaps)[0];
7263
+ }
7264
+ function formatTopGapsList(artifact, limit = 10) {
7265
+ const sorted = sortGapsByPriority(artifact.gaps);
7266
+ if (sorted.length === 0) {
7267
+ return "No gaps found \u2014 production core looks solid.";
7268
+ }
7269
+ return sorted.slice(0, limit).map((gap, index) => {
7270
+ const severity = gap.severity.toUpperCase().padEnd(8);
7271
+ const area = gap.primaryMapCategory.padEnd(12);
7272
+ return `${index + 1}. [${severity}] ${area} ${gap.title}`;
7273
+ }).join("\n");
7274
+ }
7275
+ async function loadLastArtifact(startDir) {
7276
+ const workspace = await findArtifactsWorkspace(startDir);
7277
+ if (!workspace) {
7278
+ throw new ScanNotFoundError(needsScanMessage(startDir));
7279
+ }
7280
+ const path = (0, import_node_path6.join)(getProjectArtifactsDir(workspace), "last-scan.json");
7281
+ try {
7282
+ const raw = await (0, import_promises4.readFile)(path, "utf-8");
7283
+ return JSON.parse(raw);
7284
+ } catch {
7285
+ throw new ScanNotFoundError(needsScanMessage(startDir));
7286
+ }
7287
+ }
7288
+
7289
+ // src/report/refreshReport.ts
7290
+ async function refreshReportFromDisk(startDir) {
7291
+ const workspace = await findArtifactsWorkspace(startDir);
7292
+ if (!workspace) {
7293
+ throw new ScanNotFoundError();
7294
+ }
7295
+ const artifact = await loadLastArtifact(startDir);
7296
+ return writeScanArtifacts({ artifact, cwd: workspace });
7297
+ }
7298
+
7299
+ // src/openBrowser.ts
7300
+ var import_node_child_process = require("node:child_process");
7301
+ async function openPathInBrowser(filePath) {
7302
+ const absolute = filePath;
7303
+ let command;
7304
+ let args;
7305
+ if (process.platform === "win32") {
7306
+ command = "cmd";
7307
+ args = ["/c", "start", "", absolute];
7308
+ } else if (process.platform === "darwin") {
7309
+ command = "open";
7310
+ args = [absolute];
7311
+ } else {
7312
+ command = "xdg-open";
7313
+ args = [absolute];
7314
+ }
7315
+ await new Promise((resolve3, reject) => {
7316
+ const child = (0, import_node_child_process.spawn)(command, args, { stdio: "ignore", shell: process.platform === "win32" });
7317
+ child.on("error", reject);
7318
+ child.on("exit", (code) => {
7319
+ if (code === 0) {
7320
+ resolve3();
7321
+ } else {
7322
+ reject(new Error(`Could not open browser (exit ${code ?? "unknown"}). Open manually: ${absolute}`));
7323
+ }
7324
+ });
7325
+ });
7326
+ }
7327
+
7328
+ // src/clipboard.ts
7329
+ var import_node_child_process2 = require("node:child_process");
7330
+ async function copyToClipboard(text) {
7331
+ if (!text) {
7332
+ throw new Error("Nothing to copy.");
7333
+ }
7334
+ if (process.platform === "win32") {
7335
+ await pipeToCommand("clip", text);
7336
+ return;
7337
+ }
7338
+ if (process.platform === "darwin") {
7339
+ await pipeToCommand("pbcopy", text);
7340
+ return;
7341
+ }
7342
+ try {
7343
+ await pipeToCommand("wl-copy", text);
7344
+ return;
7345
+ } catch {
7346
+ }
7347
+ try {
7348
+ await pipeToCommand("xclip", text, ["-selection", "clipboard"]);
7349
+ return;
7350
+ } catch {
7351
+ throw new Error("Clipboard unavailable. Install wl-clipboard or xclip, or copy from the terminal output.");
7352
+ }
7353
+ }
7354
+ function pipeToCommand(command, text, extraArgs = []) {
7355
+ return new Promise((resolve3, reject) => {
7356
+ const child = (0, import_node_child_process2.spawn)(command, extraArgs, { stdio: ["pipe", "ignore", "ignore"] });
7357
+ child.on("error", reject);
7358
+ child.on("close", (code) => {
7359
+ if (code === 0) {
7360
+ resolve3();
7361
+ } else {
7362
+ reject(new Error(`${command} exited with code ${code ?? "unknown"}`));
7363
+ }
7364
+ });
7365
+ child.stdin?.write(text, "utf8", (error) => {
7366
+ if (error) {
7367
+ reject(error);
7368
+ return;
7369
+ }
7370
+ child.stdin?.end();
7371
+ });
7372
+ });
7373
+ }
7374
+
7375
+ // src/terminalSummary.ts
7376
+ var import_picocolors = __toESM(require_picocolors());
7377
+ function boxLine(content, width) {
7378
+ const inner = content.length > width - 4 ? content.slice(0, width - 7) + "\u2026" : content;
7379
+ const padding = " ".repeat(Math.max(0, width - inner.length - 4));
7380
+ return `${import_picocolors.default.dim("\u2502")} ${inner}${padding} ${import_picocolors.default.dim("\u2502")}`;
7381
+ }
7382
+ function readinessColor(percent) {
7383
+ if (percent >= 70) {
7384
+ return import_picocolors.default.green;
7385
+ }
7386
+ if (percent >= 40) {
7387
+ return import_picocolors.default.yellow;
7388
+ }
7389
+ return import_picocolors.default.red;
7390
+ }
7391
+ function gapTagColor(modelGaps, open) {
7392
+ if (modelGaps > 0) {
7393
+ return import_picocolors.default.red;
7394
+ }
7395
+ if (open > 0) {
7396
+ return import_picocolors.default.yellow;
7397
+ }
7398
+ return import_picocolors.default.dim;
7399
+ }
7400
+ function printScanSummary(artifact, paths) {
7401
+ const pct = artifact.productionCorePercent;
7402
+ const gapCount = artifact.gaps.length;
7403
+ const pctColor = readinessColor(pct);
7404
+ const headline = `${import_picocolors.default.bold("VibeRaven")} \xB7 ${pctColor(`Production core ${pct}%`)} \xB7 ${gapCount} gap(s)`;
7405
+ const subline = `Score ${artifact.score} \xB7 ${artifact.scoreLabel}`;
7406
+ const width = Math.max(headline.length, subline.length, 44) + 4;
7407
+ console.log("");
7408
+ console.log(import_picocolors.default.dim("\u250C") + import_picocolors.default.dim("\u2500".repeat(width - 2)) + import_picocolors.default.dim("\u2510"));
7409
+ console.log(boxLine(headline, width));
7410
+ console.log(boxLine(subline, width));
7411
+ console.log(import_picocolors.default.dim("\u2514") + import_picocolors.default.dim("\u2500".repeat(width - 2)) + import_picocolors.default.dim("\u2518"));
7412
+ console.log("");
7413
+ for (const area of artifact.missionGraph.areas ?? []) {
7414
+ const mission = preferredMissionForArea(area, artifact.selectedProviders?.[area.key] ?? "");
7415
+ if (!mission) {
7416
+ continue;
7417
+ }
7418
+ const open = openChecksForMission(mission);
7419
+ const modelGaps = artifact.gaps.filter((g2) => g2.primaryMapCategory === area.key).length;
7420
+ const tag = modelGaps > 0 ? ` GAP ${modelGaps}` : open > 0 ? ` ${open} fix` : "";
7421
+ const tagColored = tag ? gapTagColor(modelGaps, open)(tag) : "";
7422
+ const label = area.label.padEnd(18);
7423
+ const provider2 = mission.providerLabel.padEnd(14);
7424
+ const readiness = readinessColor(mission.readinessPercent)(`${mission.readinessPercent}%`);
7425
+ console.log(` ${import_picocolors.default.dim(label)} ${provider2} ${readiness}${tagColored}`);
7426
+ }
7427
+ console.log("");
7428
+ console.log(import_picocolors.default.bold("Artifacts:"));
7429
+ console.log(import_picocolors.default.dim(` ${paths.reportPath}`));
7430
+ console.log(import_picocolors.default.dim(` ${paths.jsonPath}`));
7431
+ console.log(import_picocolors.default.dim(` ${paths.summaryPath}`));
7432
+ console.log("");
7433
+ console.log(import_picocolors.default.dim("Press Enter in `viberaven` menu to rescan \xB7 `viberaven prompt` for top gap"));
7434
+ console.log(import_picocolors.default.dim("Agents: read .viberaven/agent-summary.md"));
7435
+ console.log("");
7436
+ }
7437
+
7438
+ // src/tui/runInteractive.ts
7439
+ var import_node_path7 = require("node:path");
7440
+
7441
+ // node_modules/@clack/core/dist/index.mjs
7442
+ var import_sisteransi = __toESM(require_src(), 1);
7443
+ var import_node_process = require("node:process");
7444
+ var g = __toESM(require("node:readline"), 1);
7445
+ var import_node_readline = __toESM(require("node:readline"), 1);
7446
+ var import_node_stream = require("node:stream");
7447
+ function DD({ onlyFirst: e2 = false } = {}) {
7448
+ const t = ["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?(?:\\u0007|\\u001B\\u005C|\\u009C))", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"].join("|");
7449
+ return new RegExp(t, e2 ? void 0 : "g");
7450
+ }
7451
+ var uD = DD();
7452
+ function P(e2) {
7453
+ if (typeof e2 != "string") throw new TypeError(`Expected a \`string\`, got \`${typeof e2}\``);
7454
+ return e2.replace(uD, "");
7455
+ }
7456
+ function L(e2) {
7457
+ return e2 && e2.__esModule && Object.prototype.hasOwnProperty.call(e2, "default") ? e2.default : e2;
7458
+ }
7459
+ var W = { exports: {} };
7460
+ (function(e2) {
7461
+ var u2 = {};
7462
+ e2.exports = u2, u2.eastAsianWidth = function(F2) {
7463
+ var s = F2.charCodeAt(0), i = F2.length == 2 ? F2.charCodeAt(1) : 0, D2 = s;
7464
+ return 55296 <= s && s <= 56319 && 56320 <= i && i <= 57343 && (s &= 1023, i &= 1023, D2 = s << 10 | i, D2 += 65536), D2 == 12288 || 65281 <= D2 && D2 <= 65376 || 65504 <= D2 && D2 <= 65510 ? "F" : D2 == 8361 || 65377 <= D2 && D2 <= 65470 || 65474 <= D2 && D2 <= 65479 || 65482 <= D2 && D2 <= 65487 || 65490 <= D2 && D2 <= 65495 || 65498 <= D2 && D2 <= 65500 || 65512 <= D2 && D2 <= 65518 ? "H" : 4352 <= D2 && D2 <= 4447 || 4515 <= D2 && D2 <= 4519 || 4602 <= D2 && D2 <= 4607 || 9001 <= D2 && D2 <= 9002 || 11904 <= D2 && D2 <= 11929 || 11931 <= D2 && D2 <= 12019 || 12032 <= D2 && D2 <= 12245 || 12272 <= D2 && D2 <= 12283 || 12289 <= D2 && D2 <= 12350 || 12353 <= D2 && D2 <= 12438 || 12441 <= D2 && D2 <= 12543 || 12549 <= D2 && D2 <= 12589 || 12593 <= D2 && D2 <= 12686 || 12688 <= D2 && D2 <= 12730 || 12736 <= D2 && D2 <= 12771 || 12784 <= D2 && D2 <= 12830 || 12832 <= D2 && D2 <= 12871 || 12880 <= D2 && D2 <= 13054 || 13056 <= D2 && D2 <= 19903 || 19968 <= D2 && D2 <= 42124 || 42128 <= D2 && D2 <= 42182 || 43360 <= D2 && D2 <= 43388 || 44032 <= D2 && D2 <= 55203 || 55216 <= D2 && D2 <= 55238 || 55243 <= D2 && D2 <= 55291 || 63744 <= D2 && D2 <= 64255 || 65040 <= D2 && D2 <= 65049 || 65072 <= D2 && D2 <= 65106 || 65108 <= D2 && D2 <= 65126 || 65128 <= D2 && D2 <= 65131 || 110592 <= D2 && D2 <= 110593 || 127488 <= D2 && D2 <= 127490 || 127504 <= D2 && D2 <= 127546 || 127552 <= D2 && D2 <= 127560 || 127568 <= D2 && D2 <= 127569 || 131072 <= D2 && D2 <= 194367 || 177984 <= D2 && D2 <= 196605 || 196608 <= D2 && D2 <= 262141 ? "W" : 32 <= D2 && D2 <= 126 || 162 <= D2 && D2 <= 163 || 165 <= D2 && D2 <= 166 || D2 == 172 || D2 == 175 || 10214 <= D2 && D2 <= 10221 || 10629 <= D2 && D2 <= 10630 ? "Na" : D2 == 161 || D2 == 164 || 167 <= D2 && D2 <= 168 || D2 == 170 || 173 <= D2 && D2 <= 174 || 176 <= D2 && D2 <= 180 || 182 <= D2 && D2 <= 186 || 188 <= D2 && D2 <= 191 || D2 == 198 || D2 == 208 || 215 <= D2 && D2 <= 216 || 222 <= D2 && D2 <= 225 || D2 == 230 || 232 <= D2 && D2 <= 234 || 236 <= D2 && D2 <= 237 || D2 == 240 || 242 <= D2 && D2 <= 243 || 247 <= D2 && D2 <= 250 || D2 == 252 || D2 == 254 || D2 == 257 || D2 == 273 || D2 == 275 || D2 == 283 || 294 <= D2 && D2 <= 295 || D2 == 299 || 305 <= D2 && D2 <= 307 || D2 == 312 || 319 <= D2 && D2 <= 322 || D2 == 324 || 328 <= D2 && D2 <= 331 || D2 == 333 || 338 <= D2 && D2 <= 339 || 358 <= D2 && D2 <= 359 || D2 == 363 || D2 == 462 || D2 == 464 || D2 == 466 || D2 == 468 || D2 == 470 || D2 == 472 || D2 == 474 || D2 == 476 || D2 == 593 || D2 == 609 || D2 == 708 || D2 == 711 || 713 <= D2 && D2 <= 715 || D2 == 717 || D2 == 720 || 728 <= D2 && D2 <= 731 || D2 == 733 || D2 == 735 || 768 <= D2 && D2 <= 879 || 913 <= D2 && D2 <= 929 || 931 <= D2 && D2 <= 937 || 945 <= D2 && D2 <= 961 || 963 <= D2 && D2 <= 969 || D2 == 1025 || 1040 <= D2 && D2 <= 1103 || D2 == 1105 || D2 == 8208 || 8211 <= D2 && D2 <= 8214 || 8216 <= D2 && D2 <= 8217 || 8220 <= D2 && D2 <= 8221 || 8224 <= D2 && D2 <= 8226 || 8228 <= D2 && D2 <= 8231 || D2 == 8240 || 8242 <= D2 && D2 <= 8243 || D2 == 8245 || D2 == 8251 || D2 == 8254 || D2 == 8308 || D2 == 8319 || 8321 <= D2 && D2 <= 8324 || D2 == 8364 || D2 == 8451 || D2 == 8453 || D2 == 8457 || D2 == 8467 || D2 == 8470 || 8481 <= D2 && D2 <= 8482 || D2 == 8486 || D2 == 8491 || 8531 <= D2 && D2 <= 8532 || 8539 <= D2 && D2 <= 8542 || 8544 <= D2 && D2 <= 8555 || 8560 <= D2 && D2 <= 8569 || D2 == 8585 || 8592 <= D2 && D2 <= 8601 || 8632 <= D2 && D2 <= 8633 || D2 == 8658 || D2 == 8660 || D2 == 8679 || D2 == 8704 || 8706 <= D2 && D2 <= 8707 || 8711 <= D2 && D2 <= 8712 || D2 == 8715 || D2 == 8719 || D2 == 8721 || D2 == 8725 || D2 == 8730 || 8733 <= D2 && D2 <= 8736 || D2 == 8739 || D2 == 8741 || 8743 <= D2 && D2 <= 8748 || D2 == 8750 || 8756 <= D2 && D2 <= 8759 || 8764 <= D2 && D2 <= 8765 || D2 == 8776 || D2 == 8780 || D2 == 8786 || 8800 <= D2 && D2 <= 8801 || 8804 <= D2 && D2 <= 8807 || 8810 <= D2 && D2 <= 8811 || 8814 <= D2 && D2 <= 8815 || 8834 <= D2 && D2 <= 8835 || 8838 <= D2 && D2 <= 8839 || D2 == 8853 || D2 == 8857 || D2 == 8869 || D2 == 8895 || D2 == 8978 || 9312 <= D2 && D2 <= 9449 || 9451 <= D2 && D2 <= 9547 || 9552 <= D2 && D2 <= 9587 || 9600 <= D2 && D2 <= 9615 || 9618 <= D2 && D2 <= 9621 || 9632 <= D2 && D2 <= 9633 || 9635 <= D2 && D2 <= 9641 || 9650 <= D2 && D2 <= 9651 || 9654 <= D2 && D2 <= 9655 || 9660 <= D2 && D2 <= 9661 || 9664 <= D2 && D2 <= 9665 || 9670 <= D2 && D2 <= 9672 || D2 == 9675 || 9678 <= D2 && D2 <= 9681 || 9698 <= D2 && D2 <= 9701 || D2 == 9711 || 9733 <= D2 && D2 <= 9734 || D2 == 9737 || 9742 <= D2 && D2 <= 9743 || 9748 <= D2 && D2 <= 9749 || D2 == 9756 || D2 == 9758 || D2 == 9792 || D2 == 9794 || 9824 <= D2 && D2 <= 9825 || 9827 <= D2 && D2 <= 9829 || 9831 <= D2 && D2 <= 9834 || 9836 <= D2 && D2 <= 9837 || D2 == 9839 || 9886 <= D2 && D2 <= 9887 || 9918 <= D2 && D2 <= 9919 || 9924 <= D2 && D2 <= 9933 || 9935 <= D2 && D2 <= 9953 || D2 == 9955 || 9960 <= D2 && D2 <= 9983 || D2 == 10045 || D2 == 10071 || 10102 <= D2 && D2 <= 10111 || 11093 <= D2 && D2 <= 11097 || 12872 <= D2 && D2 <= 12879 || 57344 <= D2 && D2 <= 63743 || 65024 <= D2 && D2 <= 65039 || D2 == 65533 || 127232 <= D2 && D2 <= 127242 || 127248 <= D2 && D2 <= 127277 || 127280 <= D2 && D2 <= 127337 || 127344 <= D2 && D2 <= 127386 || 917760 <= D2 && D2 <= 917999 || 983040 <= D2 && D2 <= 1048573 || 1048576 <= D2 && D2 <= 1114109 ? "A" : "N";
7465
+ }, u2.characterLength = function(F2) {
7466
+ var s = this.eastAsianWidth(F2);
7467
+ return s == "F" || s == "W" || s == "A" ? 2 : 1;
7468
+ };
7469
+ function t(F2) {
7470
+ return F2.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[^\uD800-\uDFFF]/g) || [];
7471
+ }
7472
+ u2.length = function(F2) {
7473
+ for (var s = t(F2), i = 0, D2 = 0; D2 < s.length; D2++) i = i + this.characterLength(s[D2]);
7474
+ return i;
7475
+ }, u2.slice = function(F2, s, i) {
7476
+ textLen = u2.length(F2), s = s || 0, i = i || 1, s < 0 && (s = textLen + s), i < 0 && (i = textLen + i);
7477
+ for (var D2 = "", r = 0, n = t(F2), E = 0; E < n.length; E++) {
7478
+ var a = n[E], o2 = u2.length(a);
7479
+ if (r >= s - (o2 == 2 ? 1 : 0)) if (r + o2 <= i) D2 += a;
7480
+ else break;
7481
+ r += o2;
7482
+ }
7483
+ return D2;
7484
+ };
7485
+ })(W);
7486
+ var tD = W.exports;
7487
+ var eD = L(tD);
7488
+ var FD = function() {
7489
+ return /\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62(?:\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73|\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74|\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67)\uDB40\uDC7F|(?:\uD83E\uDDD1\uD83C\uDFFF\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFE])|(?:\uD83E\uDDD1\uD83C\uDFFE\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFD\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFC\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFB\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFB\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFC-\uDFFF])|\uD83D\uDC68(?:\uD83C\uDFFB(?:\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF]))|\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFC-\uDFFF])|[\u2695\u2696\u2708]\uFE0F|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))?|(?:\uD83C[\uDFFC-\uDFFF])\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF]))|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83D\uDC68|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFE])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])\uFE0F|\u200D(?:(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D[\uDC66\uDC67])|\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC)?|(?:\uD83D\uDC69(?:\uD83C\uDFFB\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|(?:\uD83C[\uDFFC-\uDFFF])\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69]))|\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1)(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC69(?:\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83E\uDDD1(?:\u200D(?:\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D\uDC69\u200D\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D\uDC41\uFE0F\u200D\uD83D\uDDE8|\uD83E\uDDD1(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|\uD83D\uDC69(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|\uD83D\uDE36\u200D\uD83C\uDF2B|\uD83C\uDFF3\uFE0F\u200D\u26A7|\uD83D\uDC3B\u200D\u2744|(?:(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF])\u200D[\u2640\u2642]|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|\uD83C\uDFF4\u200D\u2620|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])\u200D[\u2640\u2642]|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u2600-\u2604\u260E\u2611\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26B0\u26B1\u26C8\u26CF\u26D1\u26D3\u26E9\u26F0\u26F1\u26F4\u26F7\u26F8\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u3030\u303D\u3297\u3299]|\uD83C[\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]|\uD83D[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3])\uFE0F|\uD83C\uDFF3\uFE0F\u200D\uD83C\uDF08|\uD83D\uDC69\u200D\uD83D\uDC67|\uD83D\uDC69\u200D\uD83D\uDC66|\uD83D\uDE35\u200D\uD83D\uDCAB|\uD83D\uDE2E\u200D\uD83D\uDCA8|\uD83D\uDC15\u200D\uD83E\uDDBA|\uD83E\uDDD1(?:\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC|\uD83C\uDFFB)?|\uD83D\uDC69(?:\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC|\uD83C\uDFFB)?|\uD83C\uDDFD\uD83C\uDDF0|\uD83C\uDDF6\uD83C\uDDE6|\uD83C\uDDF4\uD83C\uDDF2|\uD83D\uDC08\u200D\u2B1B|\u2764\uFE0F\u200D(?:\uD83D\uDD25|\uD83E\uDE79)|\uD83D\uDC41\uFE0F|\uD83C\uDFF3\uFE0F|\uD83C\uDDFF(?:\uD83C[\uDDE6\uDDF2\uDDFC])|\uD83C\uDDFE(?:\uD83C[\uDDEA\uDDF9])|\uD83C\uDDFC(?:\uD83C[\uDDEB\uDDF8])|\uD83C\uDDFB(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA])|\uD83C\uDDFA(?:\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF])|\uD83C\uDDF9(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF])|\uD83C\uDDF8(?:\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF])|\uD83C\uDDF7(?:\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC])|\uD83C\uDDF5(?:\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE])|\uD83C\uDDF3(?:\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF])|\uD83C\uDDF2(?:\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF])|\uD83C\uDDF1(?:\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE])|\uD83C\uDDF0(?:\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF])|\uD83C\uDDEF(?:\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5])|\uD83C\uDDEE(?:\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9])|\uD83C\uDDED(?:\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA])|\uD83C\uDDEC(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE])|\uD83C\uDDEB(?:\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7])|\uD83C\uDDEA(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA])|\uD83C\uDDE9(?:\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF])|\uD83C\uDDE8(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF])|\uD83C\uDDE7(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF])|\uD83C\uDDE6(?:\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF])|[#\*0-9]\uFE0F\u20E3|\u2764\uFE0F|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])|\uD83C\uDFF4|(?:[\u270A\u270B]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5])(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u261D\u270C\u270D]|\uD83D[\uDD74\uDD90])(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])|[\u270A\u270B]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC08\uDC15\uDC3B\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE2E\uDE35\uDE36\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5]|\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD]|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF]|[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED7\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0D\uDD0E\uDD10-\uDD17\uDD1D\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78\uDD7A-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCB\uDDD0\uDDE0-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6]|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDED5-\uDED7\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDD78\uDD7A-\uDDCB\uDDCD-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26A7\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDED5-\uDED7\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDD78\uDD7A-\uDDCB\uDDCD-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6])\uFE0F|(?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDC8F\uDC91\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1F\uDD26\uDD30-\uDD39\uDD3C-\uDD3E\uDD77\uDDB5\uDDB6\uDDB8\uDDB9\uDDBB\uDDCD-\uDDCF\uDDD1-\uDDDD])/g;
7490
+ };
7491
+ var sD = L(FD);
7492
+ function p(e2, u2 = {}) {
7493
+ if (typeof e2 != "string" || e2.length === 0 || (u2 = { ambiguousIsNarrow: true, ...u2 }, e2 = P(e2), e2.length === 0)) return 0;
7494
+ e2 = e2.replace(sD(), " ");
7495
+ const t = u2.ambiguousIsNarrow ? 1 : 2;
7496
+ let F2 = 0;
7497
+ for (const s of e2) {
7498
+ const i = s.codePointAt(0);
7499
+ if (i <= 31 || i >= 127 && i <= 159 || i >= 768 && i <= 879) continue;
7500
+ switch (eD.eastAsianWidth(s)) {
7501
+ case "F":
7502
+ case "W":
7503
+ F2 += 2;
7504
+ break;
7505
+ case "A":
7506
+ F2 += t;
7507
+ break;
7508
+ default:
7509
+ F2 += 1;
7510
+ }
7511
+ }
7512
+ return F2;
7513
+ }
7514
+ var w = 10;
7515
+ var N = (e2 = 0) => (u2) => `\x1B[${u2 + e2}m`;
7516
+ var I = (e2 = 0) => (u2) => `\x1B[${38 + e2};5;${u2}m`;
7517
+ var R = (e2 = 0) => (u2, t, F2) => `\x1B[${38 + e2};2;${u2};${t};${F2}m`;
7518
+ var C = { modifier: { reset: [0, 0], bold: [1, 22], dim: [2, 22], italic: [3, 23], underline: [4, 24], overline: [53, 55], inverse: [7, 27], hidden: [8, 28], strikethrough: [9, 29] }, color: { black: [30, 39], red: [31, 39], green: [32, 39], yellow: [33, 39], blue: [34, 39], magenta: [35, 39], cyan: [36, 39], white: [37, 39], blackBright: [90, 39], gray: [90, 39], grey: [90, 39], redBright: [91, 39], greenBright: [92, 39], yellowBright: [93, 39], blueBright: [94, 39], magentaBright: [95, 39], cyanBright: [96, 39], whiteBright: [97, 39] }, bgColor: { bgBlack: [40, 49], bgRed: [41, 49], bgGreen: [42, 49], bgYellow: [43, 49], bgBlue: [44, 49], bgMagenta: [45, 49], bgCyan: [46, 49], bgWhite: [47, 49], bgBlackBright: [100, 49], bgGray: [100, 49], bgGrey: [100, 49], bgRedBright: [101, 49], bgGreenBright: [102, 49], bgYellowBright: [103, 49], bgBlueBright: [104, 49], bgMagentaBright: [105, 49], bgCyanBright: [106, 49], bgWhiteBright: [107, 49] } };
7519
+ Object.keys(C.modifier);
7520
+ var iD = Object.keys(C.color);
7521
+ var rD = Object.keys(C.bgColor);
7522
+ [...iD, ...rD];
7523
+ function CD() {
7524
+ const e2 = /* @__PURE__ */ new Map();
7525
+ for (const [u2, t] of Object.entries(C)) {
7526
+ for (const [F2, s] of Object.entries(t)) C[F2] = { open: `\x1B[${s[0]}m`, close: `\x1B[${s[1]}m` }, t[F2] = C[F2], e2.set(s[0], s[1]);
7527
+ Object.defineProperty(C, u2, { value: t, enumerable: false });
7528
+ }
7529
+ return Object.defineProperty(C, "codes", { value: e2, enumerable: false }), C.color.close = "\x1B[39m", C.bgColor.close = "\x1B[49m", C.color.ansi = N(), C.color.ansi256 = I(), C.color.ansi16m = R(), C.bgColor.ansi = N(w), C.bgColor.ansi256 = I(w), C.bgColor.ansi16m = R(w), Object.defineProperties(C, { rgbToAnsi256: { value: (u2, t, F2) => u2 === t && t === F2 ? u2 < 8 ? 16 : u2 > 248 ? 231 : Math.round((u2 - 8) / 247 * 24) + 232 : 16 + 36 * Math.round(u2 / 255 * 5) + 6 * Math.round(t / 255 * 5) + Math.round(F2 / 255 * 5), enumerable: false }, hexToRgb: { value: (u2) => {
7530
+ const t = /[a-f\d]{6}|[a-f\d]{3}/i.exec(u2.toString(16));
7531
+ if (!t) return [0, 0, 0];
7532
+ let [F2] = t;
7533
+ F2.length === 3 && (F2 = [...F2].map((i) => i + i).join(""));
7534
+ const s = Number.parseInt(F2, 16);
7535
+ return [s >> 16 & 255, s >> 8 & 255, s & 255];
7536
+ }, enumerable: false }, hexToAnsi256: { value: (u2) => C.rgbToAnsi256(...C.hexToRgb(u2)), enumerable: false }, ansi256ToAnsi: { value: (u2) => {
7537
+ if (u2 < 8) return 30 + u2;
7538
+ if (u2 < 16) return 90 + (u2 - 8);
7539
+ let t, F2, s;
7540
+ if (u2 >= 232) t = ((u2 - 232) * 10 + 8) / 255, F2 = t, s = t;
7541
+ else {
7542
+ u2 -= 16;
7543
+ const r = u2 % 36;
7544
+ t = Math.floor(u2 / 36) / 5, F2 = Math.floor(r / 6) / 5, s = r % 6 / 5;
7545
+ }
7546
+ const i = Math.max(t, F2, s) * 2;
7547
+ if (i === 0) return 30;
7548
+ let D2 = 30 + (Math.round(s) << 2 | Math.round(F2) << 1 | Math.round(t));
7549
+ return i === 2 && (D2 += 60), D2;
7550
+ }, enumerable: false }, rgbToAnsi: { value: (u2, t, F2) => C.ansi256ToAnsi(C.rgbToAnsi256(u2, t, F2)), enumerable: false }, hexToAnsi: { value: (u2) => C.ansi256ToAnsi(C.hexToAnsi256(u2)), enumerable: false } }), C;
7551
+ }
7552
+ var ED = CD();
7553
+ var d = /* @__PURE__ */ new Set(["\x1B", "\x9B"]);
7554
+ var oD = 39;
7555
+ var y = "\x07";
7556
+ var V = "[";
7557
+ var nD = "]";
7558
+ var G = "m";
7559
+ var _ = `${nD}8;;`;
7560
+ var z = (e2) => `${d.values().next().value}${V}${e2}${G}`;
7561
+ var K = (e2) => `${d.values().next().value}${_}${e2}${y}`;
7562
+ var aD = (e2) => e2.split(" ").map((u2) => p(u2));
7563
+ var k = (e2, u2, t) => {
7564
+ const F2 = [...u2];
7565
+ let s = false, i = false, D2 = p(P(e2[e2.length - 1]));
7566
+ for (const [r, n] of F2.entries()) {
7567
+ const E = p(n);
7568
+ if (D2 + E <= t ? e2[e2.length - 1] += n : (e2.push(n), D2 = 0), d.has(n) && (s = true, i = F2.slice(r + 1).join("").startsWith(_)), s) {
7569
+ i ? n === y && (s = false, i = false) : n === G && (s = false);
7570
+ continue;
7571
+ }
7572
+ D2 += E, D2 === t && r < F2.length - 1 && (e2.push(""), D2 = 0);
7573
+ }
7574
+ !D2 && e2[e2.length - 1].length > 0 && e2.length > 1 && (e2[e2.length - 2] += e2.pop());
7575
+ };
7576
+ var hD = (e2) => {
7577
+ const u2 = e2.split(" ");
7578
+ let t = u2.length;
7579
+ for (; t > 0 && !(p(u2[t - 1]) > 0); ) t--;
7580
+ return t === u2.length ? e2 : u2.slice(0, t).join(" ") + u2.slice(t).join("");
7581
+ };
7582
+ var lD = (e2, u2, t = {}) => {
7583
+ if (t.trim !== false && e2.trim() === "") return "";
7584
+ let F2 = "", s, i;
7585
+ const D2 = aD(e2);
7586
+ let r = [""];
7587
+ for (const [E, a] of e2.split(" ").entries()) {
7588
+ t.trim !== false && (r[r.length - 1] = r[r.length - 1].trimStart());
7589
+ let o2 = p(r[r.length - 1]);
7590
+ if (E !== 0 && (o2 >= u2 && (t.wordWrap === false || t.trim === false) && (r.push(""), o2 = 0), (o2 > 0 || t.trim === false) && (r[r.length - 1] += " ", o2++)), t.hard && D2[E] > u2) {
7591
+ const c = u2 - o2, f = 1 + Math.floor((D2[E] - c - 1) / u2);
7592
+ Math.floor((D2[E] - 1) / u2) < f && r.push(""), k(r, a, u2);
7593
+ continue;
7594
+ }
7595
+ if (o2 + D2[E] > u2 && o2 > 0 && D2[E] > 0) {
7596
+ if (t.wordWrap === false && o2 < u2) {
7597
+ k(r, a, u2);
7598
+ continue;
7599
+ }
7600
+ r.push("");
7601
+ }
7602
+ if (o2 + D2[E] > u2 && t.wordWrap === false) {
7603
+ k(r, a, u2);
7604
+ continue;
7605
+ }
7606
+ r[r.length - 1] += a;
7607
+ }
7608
+ t.trim !== false && (r = r.map((E) => hD(E)));
7609
+ const n = [...r.join(`
7610
+ `)];
7611
+ for (const [E, a] of n.entries()) {
7612
+ if (F2 += a, d.has(a)) {
7613
+ const { groups: c } = new RegExp(`(?:\\${V}(?<code>\\d+)m|\\${_}(?<uri>.*)${y})`).exec(n.slice(E).join("")) || { groups: {} };
7614
+ if (c.code !== void 0) {
7615
+ const f = Number.parseFloat(c.code);
7616
+ s = f === oD ? void 0 : f;
7617
+ } else c.uri !== void 0 && (i = c.uri.length === 0 ? void 0 : c.uri);
7618
+ }
7619
+ const o2 = ED.codes.get(Number(s));
7620
+ n[E + 1] === `
7621
+ ` ? (i && (F2 += K("")), s && o2 && (F2 += z(o2))) : a === `
7622
+ ` && (s && o2 && (F2 += z(s)), i && (F2 += K(i)));
7623
+ }
7624
+ return F2;
7625
+ };
7626
+ function Y(e2, u2, t) {
7627
+ return String(e2).normalize().replace(/\r\n/g, `
7628
+ `).split(`
7629
+ `).map((F2) => lD(F2, u2, t)).join(`
7630
+ `);
7631
+ }
7632
+ var xD = ["up", "down", "left", "right", "space", "enter", "cancel"];
7633
+ var B = { actions: new Set(xD), aliases: /* @__PURE__ */ new Map([["k", "up"], ["j", "down"], ["h", "left"], ["l", "right"], ["", "cancel"], ["escape", "cancel"]]) };
7634
+ function $(e2, u2) {
7635
+ if (typeof e2 == "string") return B.aliases.get(e2) === u2;
7636
+ for (const t of e2) if (t !== void 0 && $(t, u2)) return true;
7637
+ return false;
7638
+ }
7639
+ function BD(e2, u2) {
7640
+ if (e2 === u2) return;
7641
+ const t = e2.split(`
7642
+ `), F2 = u2.split(`
7643
+ `), s = [];
7644
+ for (let i = 0; i < Math.max(t.length, F2.length); i++) t[i] !== F2[i] && s.push(i);
7645
+ return s;
7646
+ }
7647
+ var AD = globalThis.process.platform.startsWith("win");
7648
+ var S = Symbol("clack:cancel");
7649
+ function pD(e2) {
7650
+ return e2 === S;
7651
+ }
7652
+ function m(e2, u2) {
7653
+ const t = e2;
7654
+ t.isTTY && t.setRawMode(u2);
7655
+ }
7656
+ function fD({ input: e2 = import_node_process.stdin, output: u2 = import_node_process.stdout, overwrite: t = true, hideCursor: F2 = true } = {}) {
7657
+ const s = g.createInterface({ input: e2, output: u2, prompt: "", tabSize: 1 });
7658
+ g.emitKeypressEvents(e2, s), e2.isTTY && e2.setRawMode(true);
7659
+ const i = (D2, { name: r, sequence: n }) => {
7660
+ const E = String(D2);
7661
+ if ($([E, r, n], "cancel")) {
7662
+ F2 && u2.write(import_sisteransi.cursor.show), process.exit(0);
7663
+ return;
7664
+ }
7665
+ if (!t) return;
7666
+ const a = r === "return" ? 0 : -1, o2 = r === "return" ? -1 : 0;
7667
+ g.moveCursor(u2, a, o2, () => {
7668
+ g.clearLine(u2, 1, () => {
7669
+ e2.once("keypress", i);
7670
+ });
7671
+ });
7672
+ };
7673
+ return F2 && u2.write(import_sisteransi.cursor.hide), e2.once("keypress", i), () => {
7674
+ e2.off("keypress", i), F2 && u2.write(import_sisteransi.cursor.show), e2.isTTY && !AD && e2.setRawMode(false), s.terminal = false, s.close();
7675
+ };
7676
+ }
7677
+ var gD = Object.defineProperty;
7678
+ var vD = (e2, u2, t) => u2 in e2 ? gD(e2, u2, { enumerable: true, configurable: true, writable: true, value: t }) : e2[u2] = t;
7679
+ var h = (e2, u2, t) => (vD(e2, typeof u2 != "symbol" ? u2 + "" : u2, t), t);
7680
+ var x = class {
7681
+ constructor(u2, t = true) {
7682
+ h(this, "input"), h(this, "output"), h(this, "_abortSignal"), h(this, "rl"), h(this, "opts"), h(this, "_render"), h(this, "_track", false), h(this, "_prevFrame", ""), h(this, "_subscribers", /* @__PURE__ */ new Map()), h(this, "_cursor", 0), h(this, "state", "initial"), h(this, "error", ""), h(this, "value");
7683
+ const { input: F2 = import_node_process.stdin, output: s = import_node_process.stdout, render: i, signal: D2, ...r } = u2;
7684
+ this.opts = r, this.onKeypress = this.onKeypress.bind(this), this.close = this.close.bind(this), this.render = this.render.bind(this), this._render = i.bind(this), this._track = t, this._abortSignal = D2, this.input = F2, this.output = s;
7685
+ }
7686
+ unsubscribe() {
7687
+ this._subscribers.clear();
7688
+ }
7689
+ setSubscriber(u2, t) {
7690
+ const F2 = this._subscribers.get(u2) ?? [];
7691
+ F2.push(t), this._subscribers.set(u2, F2);
7692
+ }
7693
+ on(u2, t) {
7694
+ this.setSubscriber(u2, { cb: t });
7695
+ }
7696
+ once(u2, t) {
7697
+ this.setSubscriber(u2, { cb: t, once: true });
7698
+ }
7699
+ emit(u2, ...t) {
7700
+ const F2 = this._subscribers.get(u2) ?? [], s = [];
7701
+ for (const i of F2) i.cb(...t), i.once && s.push(() => F2.splice(F2.indexOf(i), 1));
7702
+ for (const i of s) i();
7703
+ }
7704
+ prompt() {
7705
+ return new Promise((u2, t) => {
7706
+ if (this._abortSignal) {
7707
+ if (this._abortSignal.aborted) return this.state = "cancel", this.close(), u2(S);
7708
+ this._abortSignal.addEventListener("abort", () => {
7709
+ this.state = "cancel", this.close();
7710
+ }, { once: true });
7711
+ }
7712
+ const F2 = new import_node_stream.Writable();
7713
+ F2._write = (s, i, D2) => {
7714
+ this._track && (this.value = this.rl?.line.replace(/\t/g, ""), this._cursor = this.rl?.cursor ?? 0, this.emit("value", this.value)), D2();
7715
+ }, this.input.pipe(F2), this.rl = import_node_readline.default.createInterface({ input: this.input, output: F2, tabSize: 2, prompt: "", escapeCodeTimeout: 50, terminal: true }), import_node_readline.default.emitKeypressEvents(this.input, this.rl), this.rl.prompt(), this.opts.initialValue !== void 0 && this._track && this.rl.write(this.opts.initialValue), this.input.on("keypress", this.onKeypress), m(this.input, true), this.output.on("resize", this.render), this.render(), this.once("submit", () => {
7716
+ this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), m(this.input, false), u2(this.value);
7717
+ }), this.once("cancel", () => {
7718
+ this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), m(this.input, false), u2(S);
7719
+ });
7720
+ });
7721
+ }
7722
+ onKeypress(u2, t) {
7723
+ if (this.state === "error" && (this.state = "active"), t?.name && (!this._track && B.aliases.has(t.name) && this.emit("cursor", B.aliases.get(t.name)), B.actions.has(t.name) && this.emit("cursor", t.name)), u2 && (u2.toLowerCase() === "y" || u2.toLowerCase() === "n") && this.emit("confirm", u2.toLowerCase() === "y"), u2 === " " && this.opts.placeholder && (this.value || (this.rl?.write(this.opts.placeholder), this.emit("value", this.opts.placeholder))), u2 && this.emit("key", u2.toLowerCase()), t?.name === "return") {
7724
+ if (!this.value && this.opts.placeholder && (this.rl?.write(this.opts.placeholder), this.emit("value", this.opts.placeholder)), this.opts.validate) {
7725
+ const F2 = this.opts.validate(this.value);
7726
+ F2 && (this.error = F2 instanceof Error ? F2.message : F2, this.state = "error", this.rl?.write(this.value));
7727
+ }
7728
+ this.state !== "error" && (this.state = "submit");
7729
+ }
7730
+ $([u2, t?.name, t?.sequence], "cancel") && (this.state = "cancel"), (this.state === "submit" || this.state === "cancel") && this.emit("finalize"), this.render(), (this.state === "submit" || this.state === "cancel") && this.close();
7731
+ }
7732
+ close() {
7733
+ this.input.unpipe(), this.input.removeListener("keypress", this.onKeypress), this.output.write(`
7734
+ `), m(this.input, false), this.rl?.close(), this.rl = void 0, this.emit(`${this.state}`, this.value), this.unsubscribe();
7735
+ }
7736
+ restoreCursor() {
7737
+ const u2 = Y(this._prevFrame, process.stdout.columns, { hard: true }).split(`
7738
+ `).length - 1;
7739
+ this.output.write(import_sisteransi.cursor.move(-999, u2 * -1));
7740
+ }
7741
+ render() {
7742
+ const u2 = Y(this._render(this) ?? "", process.stdout.columns, { hard: true });
7743
+ if (u2 !== this._prevFrame) {
7744
+ if (this.state === "initial") this.output.write(import_sisteransi.cursor.hide);
7745
+ else {
7746
+ const t = BD(this._prevFrame, u2);
7747
+ if (this.restoreCursor(), t && t?.length === 1) {
7748
+ const F2 = t[0];
7749
+ this.output.write(import_sisteransi.cursor.move(0, F2)), this.output.write(import_sisteransi.erase.lines(1));
7750
+ const s = u2.split(`
7751
+ `);
7752
+ this.output.write(s[F2]), this._prevFrame = u2, this.output.write(import_sisteransi.cursor.move(0, s.length - F2 - 1));
7753
+ return;
7754
+ }
7755
+ if (t && t?.length > 1) {
7756
+ const F2 = t[0];
7757
+ this.output.write(import_sisteransi.cursor.move(0, F2)), this.output.write(import_sisteransi.erase.down());
7758
+ const s = u2.split(`
7759
+ `).slice(F2);
7760
+ this.output.write(s.join(`
7761
+ `)), this._prevFrame = u2;
7762
+ return;
7763
+ }
7764
+ this.output.write(import_sisteransi.erase.down());
7765
+ }
7766
+ this.output.write(u2), this.state === "initial" && (this.state = "active"), this._prevFrame = u2;
7767
+ }
7768
+ }
7769
+ };
7770
+ var A;
7771
+ A = /* @__PURE__ */ new WeakMap();
7772
+ var OD = Object.defineProperty;
7773
+ var PD = (e2, u2, t) => u2 in e2 ? OD(e2, u2, { enumerable: true, configurable: true, writable: true, value: t }) : e2[u2] = t;
7774
+ var J = (e2, u2, t) => (PD(e2, typeof u2 != "symbol" ? u2 + "" : u2, t), t);
7775
+ var LD = class extends x {
7776
+ constructor(u2) {
7777
+ super(u2, false), J(this, "options"), J(this, "cursor", 0), this.options = u2.options, this.cursor = this.options.findIndex(({ value: t }) => t === u2.initialValue), this.cursor === -1 && (this.cursor = 0), this.changeValue(), this.on("cursor", (t) => {
7778
+ switch (t) {
7779
+ case "left":
7780
+ case "up":
7781
+ this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
7782
+ break;
7783
+ case "down":
7784
+ case "right":
7785
+ this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
7786
+ break;
7787
+ }
7788
+ this.changeValue();
7789
+ });
7790
+ }
7791
+ get _value() {
7792
+ return this.options[this.cursor];
7793
+ }
7794
+ changeValue() {
7795
+ this.value = this._value.value;
7796
+ }
7797
+ };
7798
+
7799
+ // node_modules/@clack/prompts/dist/index.mjs
7800
+ var import_node_process2 = __toESM(require("node:process"), 1);
7801
+ var import_picocolors2 = __toESM(require_picocolors(), 1);
7802
+ var import_sisteransi2 = __toESM(require_src(), 1);
7803
+ function ce() {
7804
+ return import_node_process2.default.platform !== "win32" ? import_node_process2.default.env.TERM !== "linux" : !!import_node_process2.default.env.CI || !!import_node_process2.default.env.WT_SESSION || !!import_node_process2.default.env.TERMINUS_SUBLIME || import_node_process2.default.env.ConEmuTask === "{cmd::Cmder}" || import_node_process2.default.env.TERM_PROGRAM === "Terminus-Sublime" || import_node_process2.default.env.TERM_PROGRAM === "vscode" || import_node_process2.default.env.TERM === "xterm-256color" || import_node_process2.default.env.TERM === "alacritty" || import_node_process2.default.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
7805
+ }
7806
+ var V2 = ce();
7807
+ var u = (t, n) => V2 ? t : n;
7808
+ var le = u("\u25C6", "*");
7809
+ var L2 = u("\u25A0", "x");
7810
+ var W2 = u("\u25B2", "x");
7811
+ var C2 = u("\u25C7", "o");
7812
+ var ue = u("\u250C", "T");
7813
+ var o = u("\u2502", "|");
7814
+ var d2 = u("\u2514", "\u2014");
7815
+ var k2 = u("\u25CF", ">");
7816
+ var P2 = u("\u25CB", " ");
7817
+ var A2 = u("\u25FB", "[\u2022]");
7818
+ var T = u("\u25FC", "[+]");
7819
+ var F = u("\u25FB", "[ ]");
7820
+ var $e = u("\u25AA", "\u2022");
7821
+ var _2 = u("\u2500", "-");
7822
+ var me = u("\u256E", "+");
7823
+ var de = u("\u251C", "+");
7824
+ var pe = u("\u256F", "+");
7825
+ var q = u("\u25CF", "\u2022");
7826
+ var D = u("\u25C6", "*");
7827
+ var U = u("\u25B2", "!");
7828
+ var K2 = u("\u25A0", "x");
7829
+ var b2 = (t) => {
7830
+ switch (t) {
7831
+ case "initial":
7832
+ case "active":
7833
+ return import_picocolors2.default.cyan(le);
7834
+ case "cancel":
7835
+ return import_picocolors2.default.red(L2);
7836
+ case "error":
7837
+ return import_picocolors2.default.yellow(W2);
7838
+ case "submit":
7839
+ return import_picocolors2.default.green(C2);
7840
+ }
7841
+ };
7842
+ var G2 = (t) => {
7843
+ const { cursor: n, options: r, style: i } = t, s = t.maxItems ?? Number.POSITIVE_INFINITY, c = Math.max(process.stdout.rows - 4, 0), a = Math.min(c, Math.max(s, 5));
7844
+ let l2 = 0;
7845
+ n >= l2 + a - 3 ? l2 = Math.max(Math.min(n - a + 3, r.length - a), 0) : n < l2 + 2 && (l2 = Math.max(n - 2, 0));
7846
+ const $2 = a < r.length && l2 > 0, g2 = a < r.length && l2 + a < r.length;
7847
+ return r.slice(l2, l2 + a).map((p2, v, f) => {
7848
+ const j2 = v === 0 && $2, E = v === f.length - 1 && g2;
7849
+ return j2 || E ? import_picocolors2.default.dim("...") : i(p2, v + l2 === n);
7850
+ });
7851
+ };
7852
+ var ve = (t) => {
7853
+ const n = (r, i) => {
7854
+ const s = r.label ?? String(r.value);
7855
+ switch (i) {
7856
+ case "selected":
7857
+ return `${import_picocolors2.default.dim(s)}`;
7858
+ case "active":
7859
+ return `${import_picocolors2.default.green(k2)} ${s} ${r.hint ? import_picocolors2.default.dim(`(${r.hint})`) : ""}`;
7860
+ case "cancelled":
7861
+ return `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(s))}`;
7862
+ default:
7863
+ return `${import_picocolors2.default.dim(P2)} ${import_picocolors2.default.dim(s)}`;
7864
+ }
7865
+ };
7866
+ return new LD({ options: t.options, initialValue: t.initialValue, render() {
7867
+ const r = `${import_picocolors2.default.gray(o)}
7868
+ ${b2(this.state)} ${t.message}
7869
+ `;
7870
+ switch (this.state) {
7871
+ case "submit":
7872
+ return `${r}${import_picocolors2.default.gray(o)} ${n(this.options[this.cursor], "selected")}`;
7873
+ case "cancel":
7874
+ return `${r}${import_picocolors2.default.gray(o)} ${n(this.options[this.cursor], "cancelled")}
7875
+ ${import_picocolors2.default.gray(o)}`;
7876
+ default:
7877
+ return `${r}${import_picocolors2.default.cyan(o)} ${G2({ cursor: this.cursor, options: this.options, maxItems: t.maxItems, style: (i, s) => n(i, s ? "active" : "inactive") }).join(`
7878
+ ${import_picocolors2.default.cyan(o)} `)}
7879
+ ${import_picocolors2.default.cyan(d2)}
7880
+ `;
7881
+ }
7882
+ } }).prompt();
7883
+ };
7884
+ var xe = (t = "") => {
7885
+ process.stdout.write(`${import_picocolors2.default.gray(d2)} ${import_picocolors2.default.red(t)}
7886
+
7887
+ `);
7888
+ };
7889
+ var Ie = (t = "") => {
7890
+ process.stdout.write(`${import_picocolors2.default.gray(ue)} ${t}
7891
+ `);
7892
+ };
7893
+ var Se = (t = "") => {
7894
+ process.stdout.write(`${import_picocolors2.default.gray(o)}
7895
+ ${import_picocolors2.default.gray(d2)} ${t}
7896
+
7897
+ `);
7898
+ };
7899
+ var M2 = { message: (t = "", { symbol: n = import_picocolors2.default.gray(o) } = {}) => {
7900
+ const r = [`${import_picocolors2.default.gray(o)}`];
7901
+ if (t) {
7902
+ const [i, ...s] = t.split(`
7903
+ `);
7904
+ r.push(`${n} ${i}`, ...s.map((c) => `${import_picocolors2.default.gray(o)} ${c}`));
7905
+ }
7906
+ process.stdout.write(`${r.join(`
7907
+ `)}
7908
+ `);
7909
+ }, info: (t) => {
7910
+ M2.message(t, { symbol: import_picocolors2.default.blue(q) });
7911
+ }, success: (t) => {
7912
+ M2.message(t, { symbol: import_picocolors2.default.green(D) });
7913
+ }, step: (t) => {
7914
+ M2.message(t, { symbol: import_picocolors2.default.green(C2) });
7915
+ }, warn: (t) => {
7916
+ M2.message(t, { symbol: import_picocolors2.default.yellow(U) });
7917
+ }, warning: (t) => {
7918
+ M2.warn(t);
7919
+ }, error: (t) => {
7920
+ M2.message(t, { symbol: import_picocolors2.default.red(K2) });
7921
+ } };
7922
+ var J2 = `${import_picocolors2.default.gray(o)} `;
7923
+ var Y2 = ({ indicator: t = "dots" } = {}) => {
7924
+ const n = V2 ? ["\u25D2", "\u25D0", "\u25D3", "\u25D1"] : ["\u2022", "o", "O", "0"], r = V2 ? 80 : 120, i = process.env.CI === "true";
7925
+ let s, c, a = false, l2 = "", $2, g2 = performance.now();
7926
+ const p2 = (m2) => {
7927
+ const h2 = m2 > 1 ? "Something went wrong" : "Canceled";
7928
+ a && N2(h2, m2);
7929
+ }, v = () => p2(2), f = () => p2(1), j2 = () => {
7930
+ process.on("uncaughtExceptionMonitor", v), process.on("unhandledRejection", v), process.on("SIGINT", f), process.on("SIGTERM", f), process.on("exit", p2);
7931
+ }, E = () => {
7932
+ process.removeListener("uncaughtExceptionMonitor", v), process.removeListener("unhandledRejection", v), process.removeListener("SIGINT", f), process.removeListener("SIGTERM", f), process.removeListener("exit", p2);
7933
+ }, B2 = () => {
7934
+ if ($2 === void 0) return;
7935
+ i && process.stdout.write(`
7936
+ `);
7937
+ const m2 = $2.split(`
7938
+ `);
7939
+ process.stdout.write(import_sisteransi2.cursor.move(-999, m2.length - 1)), process.stdout.write(import_sisteransi2.erase.down(m2.length));
7940
+ }, R2 = (m2) => m2.replace(/\.+$/, ""), O2 = (m2) => {
7941
+ const h2 = (performance.now() - m2) / 1e3, w2 = Math.floor(h2 / 60), I2 = Math.floor(h2 % 60);
7942
+ return w2 > 0 ? `[${w2}m ${I2}s]` : `[${I2}s]`;
7943
+ }, H = (m2 = "") => {
7944
+ a = true, s = fD(), l2 = R2(m2), g2 = performance.now(), process.stdout.write(`${import_picocolors2.default.gray(o)}
7945
+ `);
7946
+ let h2 = 0, w2 = 0;
7947
+ j2(), c = setInterval(() => {
7948
+ if (i && l2 === $2) return;
7949
+ B2(), $2 = l2;
7950
+ const I2 = import_picocolors2.default.magenta(n[h2]);
7951
+ if (i) process.stdout.write(`${I2} ${l2}...`);
7952
+ else if (t === "timer") process.stdout.write(`${I2} ${l2} ${O2(g2)}`);
7953
+ else {
7954
+ const z2 = ".".repeat(Math.floor(w2)).slice(0, 3);
7955
+ process.stdout.write(`${I2} ${l2}${z2}`);
7956
+ }
7957
+ h2 = h2 + 1 < n.length ? h2 + 1 : 0, w2 = w2 < n.length ? w2 + 0.125 : 0;
7958
+ }, r);
7959
+ }, N2 = (m2 = "", h2 = 0) => {
7960
+ a = false, clearInterval(c), B2();
7961
+ const w2 = h2 === 0 ? import_picocolors2.default.green(C2) : h2 === 1 ? import_picocolors2.default.red(L2) : import_picocolors2.default.red(W2);
7962
+ l2 = R2(m2 ?? l2), t === "timer" ? process.stdout.write(`${w2} ${l2} ${O2(g2)}
7963
+ `) : process.stdout.write(`${w2} ${l2}
7964
+ `), E(), s();
7965
+ };
7966
+ return { start: H, stop: N2, message: (m2 = "") => {
7967
+ l2 = R2(m2 ?? l2);
7968
+ } };
7969
+ };
7970
+
7971
+ // src/tui/runInteractive.ts
7972
+ var import_picocolors3 = __toESM(require_picocolors());
7973
+
7974
+ // src/version.ts
7975
+ var VERSION = "0.1.0-beta.1";
7976
+
7977
+ // src/tui/runInteractive.ts
7978
+ async function formatStatusLine() {
7979
+ const creds = await loadCredentials();
7980
+ if (!creds?.accessToken) {
7981
+ return import_picocolors3.default.dim("Not signed in");
7982
+ }
7983
+ try {
7984
+ const synced = await syncCredentialsFromAccount(creds);
7985
+ const plan = synced.plan ?? "unknown";
7986
+ return import_picocolors3.default.green(`Signed in: ${synced.email ?? "(email unknown)"} \xB7 ${plan}`);
7987
+ } catch {
7988
+ return import_picocolors3.default.yellow(`Signed in: ${creds.email ?? "(email unknown)"} (offline)`);
7989
+ }
7990
+ }
7991
+ async function handleScan(cwd) {
7992
+ const apiBaseUrl = resolveApiBaseUrl();
7993
+ const spinner = Y2();
7994
+ spinner.start("Checking credentials\u2026");
7995
+ let accessToken;
7996
+ try {
7997
+ ({ accessToken } = await requireCredentials(apiBaseUrl));
7998
+ } catch (error) {
7999
+ spinner.stop("Sign in required");
8000
+ M2.error(error instanceof Error ? error.message : String(error));
8001
+ M2.message(import_picocolors3.default.dim('Choose "Sign in / Sign out" from the menu.'));
8002
+ return;
8003
+ }
8004
+ spinner.message(`Scanning ${cwd}\u2026`);
8005
+ const result = await runProjectScan({ workspacePath: cwd, accessToken, apiBaseUrl });
8006
+ if (!result.ok) {
8007
+ spinner.stop("Scan failed");
8008
+ if (result.kind === "scan_limit") {
8009
+ M2.error(formatScanLimitMessage(result.upgradeUrl));
8010
+ try {
8011
+ const account = await fetchAccountMe(apiBaseUrl, accessToken);
8012
+ M2.message(formatUsageLine(account.usage));
8013
+ } catch {
8014
+ }
8015
+ return;
8016
+ }
8017
+ M2.error(result.message);
8018
+ return;
8019
+ }
8020
+ const artifact = await enrichArtifactWithAccount(result.artifact, apiBaseUrl, accessToken);
8021
+ const paths = await writeScanArtifacts({ artifact, cwd });
8022
+ spinner.stop("Scan complete");
8023
+ printScanSummary(artifact, paths);
8024
+ if (artifact.usage) {
8025
+ M2.message(formatUsageLine(artifact.usage));
8026
+ }
8027
+ }
8028
+ async function handleViewGaps(cwd) {
8029
+ try {
8030
+ const artifact = await loadLastArtifact(cwd);
8031
+ M2.message(formatTopGapsList(artifact));
8032
+ } catch (error) {
8033
+ if (isScanNotFoundError(error)) {
8034
+ M2.warn(error.message);
8035
+ return;
8036
+ }
8037
+ throw error;
8038
+ }
8039
+ }
8040
+ async function handlePrompt(cwd) {
8041
+ try {
8042
+ const artifact = await loadLastArtifact(cwd);
8043
+ const gap = pickGap(artifact);
8044
+ if (!gap) {
8045
+ M2.warn("No gaps to fix. Run a scan or pick a different project.");
8046
+ return;
8047
+ }
8048
+ try {
8049
+ await copyToClipboard(gap.copyPrompt);
8050
+ M2.success(import_picocolors3.default.green(`Copied top prompt to clipboard \u2014 ${gap.title}`));
8051
+ } catch (error) {
8052
+ M2.warn(error instanceof Error ? error.message : String(error));
8053
+ console.log("");
8054
+ console.log(gap.copyPrompt);
8055
+ console.log("");
8056
+ M2.message(import_picocolors3.default.dim("Copy the text above manually."));
8057
+ }
8058
+ } catch (error) {
8059
+ if (isScanNotFoundError(error)) {
8060
+ M2.warn(error.message);
8061
+ return;
8062
+ }
8063
+ throw error;
8064
+ }
8065
+ }
8066
+ async function handleOpenReport(cwd) {
8067
+ const spinner = Y2();
8068
+ spinner.start("Refreshing report from last scan\u2026");
8069
+ try {
8070
+ const paths = await refreshReportFromDisk(cwd);
8071
+ spinner.stop("Report ready");
8072
+ await openPathInBrowser(paths.reportPath);
8073
+ M2.success(`Opened ${paths.reportPath}`);
8074
+ } catch (error) {
8075
+ spinner.stop("Could not open report");
8076
+ if (isScanNotFoundError(error)) {
8077
+ M2.warn(error.message);
8078
+ return;
8079
+ }
8080
+ M2.warn(error instanceof Error ? error.message : String(error));
8081
+ }
8082
+ }
8083
+ async function handleAuth() {
8084
+ const creds = await loadCredentials();
8085
+ if (creds?.accessToken) {
8086
+ await clearCredentials();
8087
+ M2.success("Signed out.");
8088
+ return;
8089
+ }
8090
+ const apiBaseUrl = resolveApiBaseUrl();
8091
+ await runDeviceLogin(apiBaseUrl);
8092
+ }
8093
+ function buildMenuOptions(isSignedIn) {
8094
+ return [
8095
+ { value: "scan", label: "Scan project", hint: "Map launch readiness" },
8096
+ { value: "gaps", label: "View top gaps", hint: "From last scan" },
8097
+ { value: "prompt", label: "Copy top prompt", hint: "Agent-ready fix prompt" },
8098
+ {
8099
+ value: "open-report",
8100
+ label: "Open report in browser",
8101
+ hint: "Rebuilds UI from last scan (no new scan)"
8102
+ },
8103
+ {
8104
+ value: "auth",
8105
+ label: isSignedIn ? "Sign out" : "Sign in",
8106
+ hint: isSignedIn ? "Clear local credentials" : "Device login flow"
8107
+ },
8108
+ { value: "exit", label: "Exit" }
8109
+ ];
8110
+ }
8111
+ async function runInteractiveSession(startDir = process.cwd()) {
8112
+ Ie(`${import_picocolors3.default.bold("VibeRaven")} ${import_picocolors3.default.dim(VERSION)}`);
8113
+ const cwd = await resolveWorkspaceRoot(startDir);
8114
+ const artifactsAt = await findArtifactsWorkspace(startDir);
8115
+ if (artifactsAt && (0, import_node_path7.resolve)(artifactsAt) !== (0, import_node_path7.resolve)(startDir)) {
8116
+ M2.message(import_picocolors3.default.dim(`Using scan from: ${artifactsAt}`));
8117
+ } else {
8118
+ M2.message(import_picocolors3.default.dim(`Project folder: ${cwd}`));
8119
+ if (!artifactsAt) {
8120
+ M2.message(
8121
+ import_picocolors3.default.dim(
8122
+ "No .viberaven/ here yet. Extension scans are separate \u2014 choose Scan project to write CLI artifacts."
8123
+ )
8124
+ );
8125
+ }
8126
+ }
8127
+ let running = true;
8128
+ while (running) {
8129
+ const statusLine = await formatStatusLine();
8130
+ M2.message(statusLine);
8131
+ const creds = await loadCredentials();
8132
+ const action = await ve({
8133
+ message: "What would you like to do?",
8134
+ options: buildMenuOptions(Boolean(creds?.accessToken))
8135
+ });
8136
+ if (pD(action)) {
8137
+ xe("Goodbye.");
8138
+ return;
8139
+ }
8140
+ switch (action) {
8141
+ case "scan":
8142
+ await handleScan(cwd);
8143
+ break;
8144
+ case "gaps":
8145
+ await handleViewGaps(cwd);
8146
+ break;
8147
+ case "prompt":
8148
+ await handlePrompt(cwd);
8149
+ break;
8150
+ case "open-report":
8151
+ await handleOpenReport(cwd);
8152
+ break;
8153
+ case "auth":
8154
+ await handleAuth();
8155
+ break;
8156
+ case "exit":
8157
+ running = false;
8158
+ break;
8159
+ default:
8160
+ break;
8161
+ }
8162
+ }
8163
+ Se(import_picocolors3.default.dim("Run viberaven anytime for the interactive menu."));
8164
+ }
8165
+
8166
+ // src/cli.ts
8167
+ function printHelp() {
8168
+ console.log(`viberaven ${VERSION} \u2014 launch readiness for AI-built apps
5658
8169
 
5659
- // src/terminalSummary.ts
5660
- function printScanSummary(artifact, paths) {
5661
- console.log("");
5662
- console.log(`VibeRaven \xB7 Production core ${artifact.productionCorePercent}% \xB7 ${artifact.gaps.length} gap(s)`);
5663
- console.log(`Score ${artifact.score} \xB7 ${artifact.scoreLabel}`);
5664
- console.log("");
5665
- for (const area of artifact.missionGraph.areas ?? []) {
5666
- const mission = area.providerMissions[0];
5667
- if (!mission) {
5668
- continue;
5669
- }
5670
- const open = mission.checks.filter(
5671
- (c) => c.status === "missing" || c.status === "failed" || c.status === "needs-connection"
5672
- ).length;
5673
- const modelGaps = artifact.gaps.filter((g) => g.primaryMapCategory === area.key).length;
5674
- const gapTag = modelGaps > 0 ? ` GAP ${modelGaps}` : open > 0 ? ` ${open} fix` : "";
5675
- const label = area.label.padEnd(18);
5676
- const provider2 = mission.providerLabel.padEnd(14);
5677
- console.log(` ${label} ${provider2} ${mission.readinessPercent}%${gapTag}`);
5678
- }
5679
- console.log("");
5680
- console.log("Artifacts:");
5681
- console.log(` ${paths.reportPath}`);
5682
- console.log(` ${paths.jsonPath}`);
5683
- console.log(` ${paths.summaryPath}`);
5684
- console.log("");
5685
- console.log("Next: viberaven prompt (top gap for your coding agent)");
5686
- console.log("Agents: read .viberaven/agent-summary.md");
5687
- console.log("");
5688
- }
5689
8170
 
5690
- // src/cli.ts
5691
- var VERSION = "0.1.0-beta.0";
5692
- function printHelp() {
5693
- console.log(`viberaven ${VERSION} \u2014 launch readiness for AI-built apps
5694
8171
 
5695
8172
  Usage:
8173
+
8174
+ viberaven Interactive menu (default for vibe coders)
8175
+
8176
+ viberaven tui Same interactive menu
8177
+
5696
8178
  viberaven login [--api-url <url>]
8179
+
5697
8180
  viberaven logout
8181
+
5698
8182
  viberaven status
8183
+
5699
8184
  viberaven scan [--open] [--json] [--api-url <url>] [path]
5700
- viberaven prompt [--gap <id>] [--provider <key>] [--area <key>]
8185
+
8186
+ viberaven report [--open] [path]
8187
+ Rebuild report.html from last scan (no new API scan)
8188
+
8189
+ viberaven prompt [--gap <id>] [--provider <key>] [--area <key>] [--no-copy]
8190
+
5701
8191
  viberaven stack set <area> <provider>
8192
+
5702
8193
  viberaven stack clear <area>
8194
+
5703
8195
  viberaven stack list
5704
8196
 
8197
+
8198
+
5705
8199
  Agent workflow (Claude Code / Codex):
5706
- npx -y @viberaven/cli@beta scan --open
8200
+
8201
+ npx -y @viberaven/cli@beta scan
8202
+
5707
8203
  Read .viberaven/agent-summary.md and fix the top gap, then scan again.
5708
8204
 
8205
+
8206
+
8207
+ Humans: run \`viberaven\` or \`viberaven tui\` for the interactive menu.
8208
+
8209
+ Agents: use \`npx -y @viberaven/cli@beta scan\` directly (no --open required).
8210
+
8211
+
8212
+
5709
8213
  Environment:
8214
+
5710
8215
  VIBERAVEN_API_URL Managed API base URL (same server as the VS Code extension)
8216
+
8217
+ Security:
8218
+
8219
+ CLI scans use VibeRaven login \u2014 not OPENAI_API_KEY. See packages/cli/SECURITY.md.
8220
+
5711
8221
  `);
5712
8222
  }
5713
8223
  function parseArgs(argv) {
@@ -5739,29 +8249,6 @@ function parseArgs(argv) {
5739
8249
  }
5740
8250
  return { command, flags, positional };
5741
8251
  }
5742
- async function loadLastArtifact(cwd) {
5743
- const path = (0, import_node_path5.join)(getProjectArtifactsDir(cwd), "last-scan.json");
5744
- const raw = await (0, import_promises4.readFile)(path, "utf-8");
5745
- return JSON.parse(raw);
5746
- }
5747
- function pickGap(artifact, options) {
5748
- if (options.gapId) {
5749
- return artifact.gaps.find((g) => g.id === options.gapId);
5750
- }
5751
- if (options.provider) {
5752
- const key = options.provider.toLowerCase();
5753
- return artifact.gaps.find(
5754
- (g) => g.primaryMapCategory === key || g.title.toLowerCase().includes(key) || g.id.toLowerCase().includes(key)
5755
- );
5756
- }
5757
- if (options.area) {
5758
- return artifact.gaps.find((g) => g.primaryMapCategory === options.area);
5759
- }
5760
- const rank = { critical: 0, warning: 1, info: 2 };
5761
- return [...artifact.gaps].sort(
5762
- (a, b) => rank[a.severity] - rank[b.severity] || a.title.localeCompare(b.title)
5763
- )[0];
5764
- }
5765
8252
  async function cmdLogin(flags) {
5766
8253
  const apiBaseUrl = resolveApiBaseUrl(typeof flags["api-url"] === "string" ? flags["api-url"] : void 0);
5767
8254
  await runDeviceLogin(apiBaseUrl);
@@ -5792,7 +8279,7 @@ async function cmdStatus() {
5792
8279
  }
5793
8280
  }
5794
8281
  async function cmdScan(flags, positional) {
5795
- const workspacePath = positional[0] ? (0, import_node_path5.join)(process.cwd(), positional[0]) : process.cwd();
8282
+ const workspacePath = positional[0] ? (0, import_node_path8.join)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
5796
8283
  const apiBaseUrl = resolveApiBaseUrl(typeof flags["api-url"] === "string" ? flags["api-url"] : void 0);
5797
8284
  let accessToken;
5798
8285
  try {
@@ -5816,14 +8303,15 @@ async function cmdScan(flags, positional) {
5816
8303
  console.error(result.message);
5817
8304
  return 1;
5818
8305
  }
5819
- const paths = await writeScanArtifacts({ artifact: result.artifact, cwd: workspacePath });
8306
+ const artifact = await enrichArtifactWithAccount(result.artifact, apiBaseUrl, accessToken);
8307
+ const paths = await writeScanArtifacts({ artifact, cwd: workspacePath });
5820
8308
  if (flags.json) {
5821
- console.log(JSON.stringify(result.artifact, null, 2));
8309
+ console.log(JSON.stringify(artifact, null, 2));
5822
8310
  return 0;
5823
8311
  }
5824
- printScanSummary(result.artifact, paths);
5825
- if (result.artifact.usage) {
5826
- console.log(formatUsageLine(result.artifact.usage));
8312
+ printScanSummary(artifact, paths);
8313
+ if (artifact.usage) {
8314
+ console.log(formatUsageLine(artifact.usage));
5827
8315
  }
5828
8316
  if (flags.open) {
5829
8317
  try {
@@ -5834,13 +8322,35 @@ async function cmdScan(flags, positional) {
5834
8322
  }
5835
8323
  return 0;
5836
8324
  }
8325
+ async function cmdReport(flags, positional) {
8326
+ const startDir = positional[0] ? (0, import_node_path8.join)(process.cwd(), positional[0]) : process.cwd();
8327
+ try {
8328
+ const paths = await refreshReportFromDisk(startDir);
8329
+ console.log(`Report refreshed: ${paths.reportPath}`);
8330
+ if (flags.open) {
8331
+ try {
8332
+ await openPathInBrowser(paths.reportPath);
8333
+ } catch (error) {
8334
+ console.warn(error instanceof Error ? error.message : String(error));
8335
+ }
8336
+ }
8337
+ return 0;
8338
+ } catch (error) {
8339
+ if (isScanNotFoundError(error)) {
8340
+ console.error(error.message);
8341
+ return 1;
8342
+ }
8343
+ console.error(error instanceof Error ? error.message : String(error));
8344
+ return 1;
8345
+ }
8346
+ }
5837
8347
  async function cmdPrompt(flags, positional) {
5838
- const cwd = positional[0] ? (0, import_node_path5.join)(process.cwd(), positional[0]) : process.cwd();
8348
+ const startDir = positional[0] ? (0, import_node_path8.join)(process.cwd(), positional[0]) : process.cwd();
5839
8349
  let artifact;
5840
8350
  try {
5841
- artifact = await loadLastArtifact(cwd);
5842
- } catch {
5843
- console.error("No scan found. Run: viberaven scan");
8351
+ artifact = await loadLastArtifact(startDir);
8352
+ } catch (error) {
8353
+ console.error(error instanceof Error ? error.message : "No scan found. Run: viberaven scan");
5844
8354
  return 1;
5845
8355
  }
5846
8356
  const gap = pickGap(artifact, {
@@ -5852,6 +8362,16 @@ async function cmdPrompt(flags, positional) {
5852
8362
  console.error("No matching gap. Run `viberaven scan` or pass --gap <id>.");
5853
8363
  return 1;
5854
8364
  }
8365
+ const skipCopy = flags["no-copy"] === true;
8366
+ if (!skipCopy) {
8367
+ try {
8368
+ await copyToClipboard(gap.copyPrompt);
8369
+ console.log(`Copied to clipboard: ${gap.title}`);
8370
+ return 0;
8371
+ } catch (error) {
8372
+ console.warn(error instanceof Error ? error.message : String(error));
8373
+ }
8374
+ }
5855
8375
  console.log(gap.copyPrompt);
5856
8376
  return 0;
5857
8377
  }
@@ -5910,10 +8430,14 @@ async function main() {
5910
8430
  return 0;
5911
8431
  }
5912
8432
  if (!command) {
5913
- printHelp();
5914
- return 1;
8433
+ await runInteractiveSession();
8434
+ return 0;
5915
8435
  }
5916
8436
  switch (command) {
8437
+ case "tui":
8438
+ case "interactive":
8439
+ await runInteractiveSession();
8440
+ return 0;
5917
8441
  case "login":
5918
8442
  await cmdLogin(flags);
5919
8443
  return 0;
@@ -5924,6 +8448,8 @@ async function main() {
5924
8448
  return cmdStatus();
5925
8449
  case "scan":
5926
8450
  return cmdScan(flags, positional);
8451
+ case "report":
8452
+ return cmdReport(flags, positional);
5927
8453
  case "prompt":
5928
8454
  return cmdPrompt(flags, positional);
5929
8455
  case "stack":