@westbayberry/dg 1.0.59 → 1.0.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.mjs +2047 -733
  2. package/package.json +1 -1
  3. package/README.md +0 -129
package/dist/index.mjs CHANGED
@@ -6477,7 +6477,7 @@ var require_has_flag = __commonJS({
6477
6477
  var require_supports_color = __commonJS({
6478
6478
  "node_modules/supports-color/index.js"(exports, module2) {
6479
6479
  "use strict";
6480
- var os6 = __require("os");
6480
+ var os5 = __require("os");
6481
6481
  var tty2 = __require("tty");
6482
6482
  var hasFlag2 = require_has_flag();
6483
6483
  var { env: env3 } = process;
@@ -6525,7 +6525,7 @@ var require_supports_color = __commonJS({
6525
6525
  return min;
6526
6526
  }
6527
6527
  if (process.platform === "win32") {
6528
- const osRelease = os6.release().split(".");
6528
+ const osRelease = os5.release().split(".");
6529
6529
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
6530
6530
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
6531
6531
  }
@@ -6827,7 +6827,7 @@ var require_require_in_the_middle = __commonJS({
6827
6827
  } else {
6828
6828
  throw new Error("'require-in-the-middle' requires Node.js >=v9.3.0 or >=v8.10.0");
6829
6829
  }
6830
- var normalize3 = /([/\\]index)?(\.js)?$/;
6830
+ var normalize4 = /([/\\]index)?(\.js)?$/;
6831
6831
  var ExportsCache = class {
6832
6832
  constructor() {
6833
6833
  this._localCache = /* @__PURE__ */ new Map();
@@ -7024,7 +7024,7 @@ var require_require_in_the_middle = __commonJS({
7024
7024
  };
7025
7025
  function resolveModuleName(stat) {
7026
7026
  const normalizedPath = path3.sep !== "/" ? stat.path.split(path3.sep).join("/") : stat.path;
7027
- return path3.posix.join(stat.name, normalizedPath).replace(normalize3, "");
7027
+ return path3.posix.join(stat.name, normalizedPath).replace(normalize4, "");
7028
7028
  }
7029
7029
  }
7030
7030
  });
@@ -23527,7 +23527,7 @@ var require_ProcessDetector = __commonJS({
23527
23527
  exports.processDetector = void 0;
23528
23528
  var api_1 = (init_esm(), __toCommonJS(esm_exports));
23529
23529
  var semconv_1 = require_semconv3();
23530
- var os6 = __require("os");
23530
+ var os5 = __require("os");
23531
23531
  var ProcessDetector = class {
23532
23532
  detect(_config) {
23533
23533
  const attributes = {
@@ -23547,7 +23547,7 @@ var require_ProcessDetector = __commonJS({
23547
23547
  attributes[semconv_1.ATTR_PROCESS_COMMAND] = process.argv[1];
23548
23548
  }
23549
23549
  try {
23550
- const userInfo = os6.userInfo();
23550
+ const userInfo = os5.userInfo();
23551
23551
  attributes[semconv_1.ATTR_PROCESS_OWNER] = userInfo.username;
23552
23552
  } catch (e) {
23553
23553
  api_1.diag.debug(`error obtaining process owner: ${e}`);
@@ -26807,7 +26807,7 @@ var init_childProcess = __esm({
26807
26807
  import { execFile } from "node:child_process";
26808
26808
  import { readFile, readdir } from "node:fs";
26809
26809
  import * as os from "node:os";
26810
- import { join as join2 } from "node:path";
26810
+ import { join as join3 } from "node:path";
26811
26811
  import { promisify } from "node:util";
26812
26812
  function _updateContext(contexts) {
26813
26813
  if (contexts.app?.app_memory) {
@@ -26935,7 +26935,7 @@ async function getLinuxInfo() {
26935
26935
  if (!distroFile) {
26936
26936
  return linuxInfo;
26937
26937
  }
26938
- const distroPath = join2("/etc", distroFile.name);
26938
+ const distroPath = join3("/etc", distroFile.name);
26939
26939
  const contents = (await readFileAsync(distroPath, { encoding: "utf-8" })).toLowerCase();
26940
26940
  const { distros } = distroFile;
26941
26941
  linuxInfo.name = distros.find((d) => contents.indexOf(getLinuxDistroId(d)) >= 0) || distros[0];
@@ -27847,7 +27847,7 @@ var init_detection = __esm({
27847
27847
 
27848
27848
  // node_modules/@sentry/node-core/build/esm/integrations/modules.js
27849
27849
  import { existsSync, readFileSync } from "node:fs";
27850
- import { dirname as dirname2, join as join3 } from "node:path";
27850
+ import { dirname as dirname2, join as join4 } from "node:path";
27851
27851
  function getRequireCachePaths() {
27852
27852
  try {
27853
27853
  return __require.cache ? Object.keys(__require.cache) : [];
@@ -27878,7 +27878,7 @@ function collectRequireModules() {
27878
27878
  if (mainPaths.indexOf(dir) < 0) {
27879
27879
  return updir();
27880
27880
  }
27881
- const pkgfile = join3(orig, "package.json");
27881
+ const pkgfile = join4(orig, "package.json");
27882
27882
  seen.add(orig);
27883
27883
  if (!existsSync(pkgfile)) {
27884
27884
  return updir();
@@ -27901,7 +27901,7 @@ function _getModules() {
27901
27901
  }
27902
27902
  function getPackageJson() {
27903
27903
  try {
27904
- const filePath = join3(process.cwd(), "package.json");
27904
+ const filePath = join4(process.cwd(), "package.json");
27905
27905
  const packageJson = JSON.parse(readFileSync(filePath, "utf8"));
27906
27906
  return packageJson;
27907
27907
  } catch {
@@ -29172,6 +29172,8 @@ var init_esm5 = __esm({
29172
29172
  init_logger();
29173
29173
  init_instrument();
29174
29174
  init_sdk();
29175
+ init_onuncaughtexception();
29176
+ init_onunhandledrejection();
29175
29177
  init_addOriginToSpan();
29176
29178
  init_getRequestUrl();
29177
29179
  init_nodeVersion();
@@ -46144,6 +46146,7 @@ var init_esm6 = __esm({
46144
46146
  "node_modules/@sentry/node/build/esm/index.js"() {
46145
46147
  init_sdk2();
46146
46148
  init_esm3();
46149
+ init_esm5();
46147
46150
  }
46148
46151
  });
46149
46152
 
@@ -46155,9 +46158,13 @@ function initTelemetry(version) {
46155
46158
  dsn: CLI_DSN,
46156
46159
  release: `dg-cli@${version}`,
46157
46160
  environment: process.env.CI ? "ci" : "local",
46158
- // TODO: reduce sample rate when CLI usage exceeds ~10k DAU
46159
46161
  sampleRate: 1,
46160
46162
  sendDefaultPii: false,
46163
+ defaultIntegrations: false,
46164
+ integrations: [
46165
+ onUncaughtExceptionIntegration({ exitEvenIfOtherHandlersAreRegistered: false }),
46166
+ onUnhandledRejectionIntegration()
46167
+ ],
46161
46168
  beforeSend(event) {
46162
46169
  if (event.exception?.values?.some(
46163
46170
  (ex) => ex.value && /\.(invalid|test|example|localhost)\b/i.test(ex.value)
@@ -46229,6 +46236,92 @@ var init_telemetry = __esm({
46229
46236
  }
46230
46237
  });
46231
46238
 
46239
+ // src/paths.ts
46240
+ import { homedir } from "node:os";
46241
+ import { join as join5, isAbsolute as isAbsolute2 } from "node:path";
46242
+ function safeAbsolute(p, fallback) {
46243
+ if (!p || typeof p !== "string") return fallback;
46244
+ if (!isAbsolute2(p)) return fallback;
46245
+ return p;
46246
+ }
46247
+ function dgRoot() {
46248
+ return join5(homedir(), `.${ROOT_DIR_NAME}`);
46249
+ }
46250
+ function dgConfigDir() {
46251
+ const xdg = safeAbsolute(process.env.XDG_CONFIG_HOME, "");
46252
+ if (xdg) return join5(xdg, ROOT_DIR_NAME);
46253
+ return dgRoot();
46254
+ }
46255
+ function dgCacheDir() {
46256
+ const explicit = safeAbsolute(process.env.DG_CACHE_DIR, "");
46257
+ if (explicit) return explicit;
46258
+ const xdg = safeAbsolute(process.env.XDG_CACHE_HOME, "");
46259
+ if (xdg) return join5(xdg, ROOT_DIR_NAME);
46260
+ return join5(dgRoot(), "cache");
46261
+ }
46262
+ function dgStateDir() {
46263
+ const xdg = safeAbsolute(process.env.XDG_STATE_HOME, "");
46264
+ if (xdg) return join5(xdg, ROOT_DIR_NAME);
46265
+ return join5(dgRoot(), "state");
46266
+ }
46267
+ function dgConfigPath() {
46268
+ return join5(dgConfigDir(), "config.json");
46269
+ }
46270
+ function dgCachePath(name) {
46271
+ return join5(dgCacheDir(), name);
46272
+ }
46273
+ function dgStatePath(name) {
46274
+ return join5(dgStateDir(), name);
46275
+ }
46276
+ function dgMigrationBreadcrumb() {
46277
+ return join5(dgRoot(), ".migrated-from-v1");
46278
+ }
46279
+ function dgMigrationLock() {
46280
+ return join5(dgRoot(), ".migration.lock");
46281
+ }
46282
+ function dgReadme() {
46283
+ return join5(dgRoot(), "README.txt");
46284
+ }
46285
+ function legacyPaths() {
46286
+ const home = homedir();
46287
+ const depGuardianDir = join5(home, ".dependency-guardian");
46288
+ return {
46289
+ dgrc: join5(home, ".dgrc.json"),
46290
+ cacheDir: depGuardianDir,
46291
+ cacheSqlite: join5(home, ".dg", "cache.sqlite"),
46292
+ routingHint: join5(home, ".dg", "scan-routing-hint.json"),
46293
+ updateCheck: join5(home, ".dg-update-check.json"),
46294
+ trialBannerOld: join5(depGuardianDir, "last-trial-banner"),
46295
+ aliasesShOld: join5(depGuardianDir, "aliases.sh"),
46296
+ depGuardianDir
46297
+ };
46298
+ }
46299
+ function dgPaths() {
46300
+ return {
46301
+ root: dgRoot(),
46302
+ configDir: dgConfigDir(),
46303
+ cacheDir: dgCacheDir(),
46304
+ stateDir: dgStateDir(),
46305
+ config: dgConfigPath(),
46306
+ cacheSqlite: dgCachePath("scans.sqlite"),
46307
+ updateCheck: dgCachePath("update-check.json"),
46308
+ routingHint: dgCachePath("routing-hint.json"),
46309
+ trialBanner: dgStatePath("trial-banner"),
46310
+ aliasesSh: dgStatePath("aliases.sh"),
46311
+ hooksRegistry: dgStatePath("hooks-installed.json"),
46312
+ readme: dgReadme(),
46313
+ migrationBreadcrumb: dgMigrationBreadcrumb(),
46314
+ migrationLock: dgMigrationLock()
46315
+ };
46316
+ }
46317
+ var ROOT_DIR_NAME;
46318
+ var init_paths = __esm({
46319
+ "src/paths.ts"() {
46320
+ "use strict";
46321
+ ROOT_DIR_NAME = "dg";
46322
+ }
46323
+ });
46324
+
46232
46325
  // src/auth/auth.ts
46233
46326
  var auth_exports = {};
46234
46327
  __export(auth_exports, {
@@ -46259,9 +46352,8 @@ __export(auth_exports, {
46259
46352
  scrubAuthToken: () => scrubAuthToken,
46260
46353
  setProtectConfig: () => setProtectConfig
46261
46354
  });
46262
- import { readFileSync as readFileSync2, writeFileSync, unlinkSync, existsSync as existsSync2, chmodSync, lstatSync, openSync, fchmodSync, closeSync, constants as fsConstants } from "node:fs";
46263
- import { join as join4 } from "node:path";
46264
- import { homedir } from "node:os";
46355
+ import { readFileSync as readFileSync2, writeFileSync, unlinkSync, existsSync as existsSync2, mkdirSync, chmodSync, lstatSync, openSync, fchmodSync, closeSync, constants as fsConstants } from "node:fs";
46356
+ import { dirname as dirname3 } from "node:path";
46265
46357
  import { spawn } from "node:child_process";
46266
46358
  import { randomUUID } from "node:crypto";
46267
46359
  function authBase() {
@@ -46339,7 +46431,13 @@ async function pollAuthSession(sessionId) {
46339
46431
  };
46340
46432
  }
46341
46433
  function configPath() {
46342
- return join4(homedir(), CONFIG_FILE);
46434
+ return dgConfigPath();
46435
+ }
46436
+ function ensureConfigDir() {
46437
+ const dir = dgConfigDir();
46438
+ if (!existsSync2(dir)) {
46439
+ mkdirSync(dir, { recursive: true, mode: 448 });
46440
+ }
46343
46441
  }
46344
46442
  function ensureConfigPerms(path3) {
46345
46443
  let st;
@@ -46401,7 +46499,19 @@ function readConfig() {
46401
46499
  }
46402
46500
  }
46403
46501
  function writeDgrc(payload) {
46502
+ ensureConfigDir();
46404
46503
  const p = configPath();
46504
+ const parent = dirname3(p);
46505
+ if (parent !== p) {
46506
+ try {
46507
+ const st = lstatSync(parent);
46508
+ if (st && typeof st.isSymbolicLink === "function" && st.isSymbolicLink()) {
46509
+ throw new Error(`refusing to write ${p}: parent ${parent} is a symlink`);
46510
+ }
46511
+ } catch (e) {
46512
+ if (e instanceof Error && e.message.startsWith("refusing")) throw e;
46513
+ }
46514
+ }
46405
46515
  writeFileSync(p, JSON.stringify(payload, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
46406
46516
  try {
46407
46517
  chmodSync(p, 384);
@@ -46651,12 +46761,12 @@ function openBrowser(url) {
46651
46761
  } catch {
46652
46762
  }
46653
46763
  }
46654
- var DEFAULT_WEB_BASE, CONFIG_FILE, MAX_QUEUED_EVENTS;
46764
+ var DEFAULT_WEB_BASE, MAX_QUEUED_EVENTS;
46655
46765
  var init_auth = __esm({
46656
46766
  "src/auth/auth.ts"() {
46657
46767
  "use strict";
46768
+ init_paths();
46658
46769
  DEFAULT_WEB_BASE = "https://westbayberry.com";
46659
- CONFIG_FILE = ".dgrc.json";
46660
46770
  MAX_QUEUED_EVENTS = 50;
46661
46771
  }
46662
46772
  });
@@ -46672,9 +46782,8 @@ __export(config_exports, {
46672
46782
  });
46673
46783
  import { parseArgs } from "node:util";
46674
46784
  import { readFileSync as readFileSync3, existsSync as existsSync3 } from "node:fs";
46675
- import { join as join5, dirname as dirname3 } from "node:path";
46785
+ import { join as join6, dirname as dirname4 } from "node:path";
46676
46786
  import { fileURLToPath } from "node:url";
46677
- import { homedir as homedir2 } from "node:os";
46678
46787
  function levenshtein(a, b) {
46679
46788
  if (a === b) return 0;
46680
46789
  if (a.length === 0) return b.length;
@@ -46714,24 +46823,28 @@ function warnUnknownDgrcKeys(parsed, source) {
46714
46823
  }
46715
46824
  }
46716
46825
  function loadDgrc() {
46717
- const cwdPath = join5(process.cwd(), ".dgrc.json");
46718
- const homePath = join5(homedir2(), ".dgrc.json");
46826
+ const cwdPath = join6(process.cwd(), ".dgrc.json");
46827
+ const homePath = dgConfigPath();
46828
+ const legacyHomePath = legacyPaths().dgrc;
46719
46829
  let config3 = {};
46720
- if (existsSync3(homePath)) {
46830
+ const sources = [homePath, legacyHomePath].filter((p, i, arr) => arr.indexOf(p) === i);
46831
+ for (const src of sources) {
46832
+ if (!existsSync3(src)) continue;
46721
46833
  try {
46722
- const home = JSON.parse(readFileSync3(homePath, "utf-8"));
46723
- warnUnknownDgrcKeys(home, homePath);
46724
- const homeFiltered = {};
46834
+ const data = JSON.parse(readFileSync3(src, "utf-8"));
46835
+ warnUnknownDgrcKeys(data, src);
46836
+ const filtered = {};
46725
46837
  for (const k of KNOWN_DGRC_KEYS) {
46726
- if (k in home) homeFiltered[k] = home[k];
46838
+ if (k in data) filtered[k] = data[k];
46727
46839
  }
46728
- config3 = homeFiltered;
46840
+ config3 = filtered;
46841
+ break;
46729
46842
  } catch {
46730
- process.stderr.write(`Warning: Failed to parse ${homePath}, ignoring.
46843
+ process.stderr.write(`Warning: Failed to parse ${src}, ignoring.
46731
46844
  `);
46732
46845
  }
46733
46846
  }
46734
- if (existsSync3(cwdPath) && cwdPath !== homePath) {
46847
+ if (existsSync3(cwdPath) && cwdPath !== homePath && cwdPath !== legacyHomePath) {
46735
46848
  try {
46736
46849
  const cwd2 = JSON.parse(readFileSync3(cwdPath, "utf-8"));
46737
46850
  warnUnknownDgrcKeys(cwd2, cwdPath);
@@ -46759,10 +46872,7 @@ function validateApiUrl(url) {
46759
46872
  );
46760
46873
  process.exit(1);
46761
46874
  }
46762
- const isLocal = host === "localhost" || // IPv4: loopback, RFC 1918, CGNAT. Proper octet regex — not prefix matching,
46763
- // which would wrongly accept `192.1680.0.1` or `100.1.2.3` (public).
46764
- /^(127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\.)/.test(host) || // IPv6: loopback and unique local (fc00::/7 → fc** or fd**)
46765
- host === "::1" || /^f[cd][0-9a-f]{2}:/i.test(host);
46875
+ const isLocal = host === "localhost" || /^(127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\.)/.test(host) || host === "::1" || /^f[cd][0-9a-f]{2}:/i.test(host);
46766
46876
  if (parsed.protocol !== "https:" && !isLocal) {
46767
46877
  process.stderr.write(`Error: API URL must use HTTPS (got ${parsed.protocol}). Use localhost for local testing.
46768
46878
  `);
@@ -46777,9 +46887,9 @@ function validateApiUrl(url) {
46777
46887
  }
46778
46888
  function getVersion() {
46779
46889
  try {
46780
- const thisDir = dirname3(fileURLToPath(import.meta.url));
46890
+ const thisDir = dirname4(fileURLToPath(import.meta.url));
46781
46891
  const pkg = JSON.parse(
46782
- readFileSync3(join5(thisDir, "..", "package.json"), "utf-8")
46892
+ readFileSync3(join6(thisDir, "..", "package.json"), "utf-8")
46783
46893
  );
46784
46894
  return pkg.version ?? "1.0.0";
46785
46895
  } catch {
@@ -46810,7 +46920,6 @@ function parseConfig(argv, strictFlags = true) {
46810
46920
  strict: { type: "boolean", default: false },
46811
46921
  help: { type: "boolean", default: false },
46812
46922
  version: { type: "boolean", default: false },
46813
- // Recognized but handled server-side via policy dashboard
46814
46923
  "no-config": { type: "boolean", default: false },
46815
46924
  allowlist: { type: "string" },
46816
46925
  "block-threshold": { type: "string" },
@@ -46890,6 +46999,7 @@ var init_config = __esm({
46890
46999
  "src/config.ts"() {
46891
47000
  "use strict";
46892
47001
  init_auth();
47002
+ init_paths();
46893
47003
  KNOWN_DGRC_KEYS = ["apiKey", "apiUrl", "mode", "maxPackages"];
46894
47004
  INTERNAL_DGRC_KEYS = [
46895
47005
  "deviceId",
@@ -46903,26 +47013,27 @@ var init_config = __esm({
46903
47013
  USAGE = `
46904
47014
  Dependency Guardian \u2014 Supply chain security scanner
46905
47015
 
46906
- Get protected in two steps:
46907
- dg login # free account, higher scan limits
46908
- dg protect # opt in to auto-scan on \`npm install\` / \`pip install\`
47016
+ Get protected:
47017
+ dg init Set up this project (hook + protect + first scan)
47018
+ dg login Free account, higher scan limits
47019
+ dg protect Auto-scan on \`npm install\` / \`pip install\`
46909
47020
 
46910
47021
  Common commands:
46911
47022
  dg scan Audit this project's dependencies (read-only)
46912
47023
  dg status Show your coverage at a glance
46913
- dg protect Enable opt-in protection (stored in ~/.dgrc.json)
46914
- dg protect off Disable protection
46915
47024
  dg npm install <pkg> One-shot: scan + install a package
46916
47025
  dg pip install <pkg> Same, for pip
46917
47026
  dg hook install Block risky lockfile changes at commit time
46918
47027
  dg login / dg logout Manage authentication
46919
47028
  dg update Check for and install the latest dg version
47029
+ dg uninstall Remove everything dg has written locally
46920
47030
 
46921
47031
  Common flags:
46922
47032
  --json Machine-readable output (CI/scripts)
46923
47033
  --quiet Suppress login + update nudges
46924
47034
  --output, -o <file> Save scan result to a file (requires \`dg login\`)
46925
47035
 
47036
+ Local state lives in ~/.dg/.
46926
47037
  Need more? Advanced flags + env vars: \`dg --help-all\`.
46927
47038
  `.trimStart();
46928
47039
  USAGE_ALL = `
@@ -46934,22 +47045,24 @@ var init_config = __esm({
46934
47045
  dg pip install <pkg> [pip-flags]
46935
47046
 
46936
47047
  Commands:
47048
+ init Fast per-project setup (hook + protect + first scan)
46937
47049
  scan Read-only audit: scans the project's lockfiles + reports
46938
47050
  npm Protective install wrapper for npm (default mode: BLOCK)
46939
47051
  pip Protective install wrapper for pip (default mode: BLOCK)
46940
- protect Enable opt-in low-friction protection (stored in
46941
- ~/.dgrc.json + per-user shell-alias snippet \u2014 opt in
46942
- by sourcing it)
47052
+ protect Enable opt-in low-friction protection (config under
47053
+ ~/.dg/ + per-user shell-alias snippet
47054
+ \u2014 opt in by sourcing it)
46943
47055
  protect off Disable protection in the current project
46944
47056
  hook install Install git pre-commit hook to scan lockfile changes
46945
47057
  hook uninstall Remove the pre-commit hook
46946
47058
  login Authenticate with your WestBayBerry account
46947
47059
  logout Remove saved credentials
46948
- cleanup Wipe local state (creds, shell snippet) before npm uninstall
47060
+ uninstall Remove every file dg has written locally (config, cache,
47061
+ hooks across repos, shell-rc lines)
46949
47062
  status Show coverage overview (auth, protect, hook, npm/pip, update)
46950
47063
  update Check for and install the latest version
46951
47064
  wrap Show instructions to alias npm to dg (manual)
46952
- kitty Re-run the guided tour
47065
+ kitty Re-run the first-time intro tour
46953
47066
  help Show short help (\`dg --help-all\` for this view)
46954
47067
  version Show version number
46955
47068
 
@@ -47036,7 +47149,7 @@ __export(npm_wrapper_exports, {
47036
47149
  });
47037
47150
  import { spawn as spawn2 } from "node:child_process";
47038
47151
  import { readFileSync as readFileSync4, existsSync as existsSync4, mkdtempSync, writeFileSync as writeFileSync2, rmSync } from "node:fs";
47039
- import { join as join6 } from "node:path";
47152
+ import { join as join7 } from "node:path";
47040
47153
  import { tmpdir } from "node:os";
47041
47154
  function parseNpmArgs(args) {
47042
47155
  let dgForce = false;
@@ -47245,9 +47358,9 @@ async function resolveTreeNpm(specs) {
47245
47358
  if (safe.length === 0) {
47246
47359
  return { packages: [], ok: false, errorMessage: "all specs were flag-shaped (refused)" };
47247
47360
  }
47248
- const dir = mkdtempSync(join6(tmpdir(), "dg-resolve-"));
47361
+ const dir = mkdtempSync(join7(tmpdir(), "dg-resolve-"));
47249
47362
  try {
47250
- writeFileSync2(join6(dir, "package.json"), '{"name":"_dg_resolve","version":"1.0.0","private":true}');
47363
+ writeFileSync2(join7(dir, "package.json"), '{"name":"_dg_resolve","version":"1.0.0","private":true}');
47251
47364
  const stdout = await new Promise((resolve3) => {
47252
47365
  const child = spawn2(
47253
47366
  "npm",
@@ -47327,7 +47440,7 @@ function pinTopLevelArgs(rawArgs, userSpecs, tree) {
47327
47440
  });
47328
47441
  }
47329
47442
  function readLockfilePins(cwd2) {
47330
- const lockPath = join6(cwd2, "package-lock.json");
47443
+ const lockPath = join7(cwd2, "package-lock.json");
47331
47444
  if (!existsSync4(lockPath)) return null;
47332
47445
  try {
47333
47446
  const lock = JSON.parse(readFileSync4(lockPath, "utf-8"));
@@ -47357,7 +47470,7 @@ function readBareInstallPackages(cwd2) {
47357
47470
  return readBareInstallPackagesTyped(cwd2).map((s) => s.spec);
47358
47471
  }
47359
47472
  function readBareInstallPackagesTyped(cwd2) {
47360
- const pkgPath = join6(cwd2, "package.json");
47473
+ const pkgPath = join7(cwd2, "package.json");
47361
47474
  if (!existsSync4(pkgPath)) return [];
47362
47475
  try {
47363
47476
  const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
@@ -48630,14 +48743,14 @@ var require_templates = __commonJS({
48630
48743
  }
48631
48744
  return results;
48632
48745
  }
48633
- function buildStyle(chalk15, styles8) {
48746
+ function buildStyle(chalk17, styles8) {
48634
48747
  const enabled = {};
48635
48748
  for (const layer of styles8) {
48636
48749
  for (const style of layer.styles) {
48637
48750
  enabled[style[0]] = layer.inverse ? null : style.slice(1);
48638
48751
  }
48639
48752
  }
48640
- let current = chalk15;
48753
+ let current = chalk17;
48641
48754
  for (const [styleName, styles9] of Object.entries(enabled)) {
48642
48755
  if (!Array.isArray(styles9)) {
48643
48756
  continue;
@@ -48649,7 +48762,7 @@ var require_templates = __commonJS({
48649
48762
  }
48650
48763
  return current;
48651
48764
  }
48652
- module2.exports = (chalk15, temporary) => {
48765
+ module2.exports = (chalk17, temporary) => {
48653
48766
  const styles8 = [];
48654
48767
  const chunks = [];
48655
48768
  let chunk = [];
@@ -48659,13 +48772,13 @@ var require_templates = __commonJS({
48659
48772
  } else if (style) {
48660
48773
  const string = chunk.join("");
48661
48774
  chunk = [];
48662
- chunks.push(styles8.length === 0 ? string : buildStyle(chalk15, styles8)(string));
48775
+ chunks.push(styles8.length === 0 ? string : buildStyle(chalk17, styles8)(string));
48663
48776
  styles8.push({ inverse, styles: parseStyle(style) });
48664
48777
  } else if (close2) {
48665
48778
  if (styles8.length === 0) {
48666
48779
  throw new Error("Found extraneous } in Chalk template literal");
48667
48780
  }
48668
- chunks.push(buildStyle(chalk15, styles8)(chunk.join("")));
48781
+ chunks.push(buildStyle(chalk17, styles8)(chunk.join("")));
48669
48782
  chunk = [];
48670
48783
  styles8.pop();
48671
48784
  } else {
@@ -48713,16 +48826,16 @@ var require_source = __commonJS({
48713
48826
  }
48714
48827
  };
48715
48828
  var chalkFactory2 = (options) => {
48716
- const chalk16 = {};
48717
- applyOptions2(chalk16, options);
48718
- chalk16.template = (...arguments_) => chalkTag(chalk16.template, ...arguments_);
48719
- Object.setPrototypeOf(chalk16, Chalk.prototype);
48720
- Object.setPrototypeOf(chalk16.template, chalk16);
48721
- chalk16.template.constructor = () => {
48829
+ const chalk18 = {};
48830
+ applyOptions2(chalk18, options);
48831
+ chalk18.template = (...arguments_) => chalkTag(chalk18.template, ...arguments_);
48832
+ Object.setPrototypeOf(chalk18, Chalk.prototype);
48833
+ Object.setPrototypeOf(chalk18.template, chalk18);
48834
+ chalk18.template.constructor = () => {
48722
48835
  throw new Error("`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.");
48723
48836
  };
48724
- chalk16.template.Instance = ChalkClass;
48725
- return chalk16.template;
48837
+ chalk18.template.Instance = ChalkClass;
48838
+ return chalk18.template;
48726
48839
  };
48727
48840
  function Chalk(options) {
48728
48841
  return chalkFactory2(options);
@@ -48833,7 +48946,7 @@ var require_source = __commonJS({
48833
48946
  return openAll + string + closeAll;
48834
48947
  };
48835
48948
  var template;
48836
- var chalkTag = (chalk16, ...strings) => {
48949
+ var chalkTag = (chalk18, ...strings) => {
48837
48950
  const [firstString] = strings;
48838
48951
  if (!isArray(firstString) || !isArray(firstString.raw)) {
48839
48952
  return strings.join(" ");
@@ -48849,25 +48962,27 @@ var require_source = __commonJS({
48849
48962
  if (template === void 0) {
48850
48963
  template = require_templates();
48851
48964
  }
48852
- return template(chalk16, parts.join(""));
48965
+ return template(chalk18, parts.join(""));
48853
48966
  };
48854
48967
  Object.defineProperties(Chalk.prototype, styles8);
48855
- var chalk15 = Chalk();
48856
- chalk15.supportsColor = stdoutColor2;
48857
- chalk15.stderr = Chalk({ level: stderrColor2 ? stderrColor2.level : 0 });
48858
- chalk15.stderr.supportsColor = stderrColor2;
48859
- module2.exports = chalk15;
48968
+ var chalk17 = Chalk();
48969
+ chalk17.supportsColor = stdoutColor2;
48970
+ chalk17.stderr = Chalk({ level: stderrColor2 ? stderrColor2.level : 0 });
48971
+ chalk17.stderr.supportsColor = stderrColor2;
48972
+ module2.exports = chalk17;
48860
48973
  }
48861
48974
  });
48862
48975
 
48863
48976
  // src/update-check.ts
48864
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
48865
- import { homedir as homedir3 } from "node:os";
48866
- import { join as join7 } from "node:path";
48977
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
48978
+ import { dirname as dirname5 } from "node:path";
48867
48979
  import { execFileSync } from "node:child_process";
48980
+ function cacheFile() {
48981
+ return dgCachePath("update-check.json");
48982
+ }
48868
48983
  function readCache() {
48869
48984
  try {
48870
- const raw = readFileSync5(CACHE_FILE, "utf-8");
48985
+ const raw = readFileSync5(cacheFile(), "utf-8");
48871
48986
  const data = JSON.parse(raw);
48872
48987
  if (typeof data.latest === "string" && typeof data.checkedAt === "number") {
48873
48988
  return data;
@@ -48878,7 +48993,10 @@ function readCache() {
48878
48993
  }
48879
48994
  function writeCache(entry) {
48880
48995
  try {
48881
- writeFileSync3(CACHE_FILE, JSON.stringify(entry), "utf-8");
48996
+ const path3 = cacheFile();
48997
+ const parent = dirname5(path3);
48998
+ if (!existsSync5(parent)) mkdirSync2(parent, { recursive: true, mode: 448 });
48999
+ writeFileSync3(path3, JSON.stringify(entry), "utf-8");
48882
49000
  } catch {
48883
49001
  }
48884
49002
  }
@@ -48939,45 +49057,45 @@ async function checkForUpdate(currentVersion) {
48939
49057
  return null;
48940
49058
  }
48941
49059
  async function runUpdate(currentVersion) {
48942
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
48943
- process.stderr.write(chalk15.dim(" Checking for updates...\n"));
49060
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
49061
+ process.stderr.write(chalk17.dim(" Checking for updates...\n"));
48944
49062
  const latest = await fetchLatestVersion();
48945
49063
  if (!latest) {
48946
- process.stderr.write(chalk15.red(" Could not reach npm registry.\n"));
49064
+ process.stderr.write(chalk17.red(" Could not reach npm registry.\n"));
48947
49065
  process.exit(1);
48948
49066
  }
48949
49067
  if (!isNewer(latest, currentVersion)) {
48950
49068
  process.stderr.write(
48951
- chalk15.green(` Already on latest version (${currentVersion}).
49069
+ chalk17.green(` Already on latest version (${currentVersion}).
48952
49070
  `)
48953
49071
  );
48954
49072
  return;
48955
49073
  }
48956
- process.stderr.write(chalk15.dim(` Installing ${PKG_NAME}@${latest}...
49074
+ process.stderr.write(chalk17.dim(` Installing ${PKG_NAME}@${latest}...
48957
49075
  `));
48958
49076
  try {
48959
49077
  execFileSync("npm", ["install", "-g", `${PKG_NAME}@${latest}`], { stdio: "inherit" });
48960
49078
  writeCache({ latest, checkedAt: Date.now() });
48961
49079
  process.stderr.write(
48962
- chalk15.green(`
49080
+ chalk17.green(`
48963
49081
  Updated ${currentVersion} \u2192 ${latest}
48964
49082
  `)
48965
49083
  );
48966
49084
  } catch {
48967
49085
  process.stderr.write(
48968
- chalk15.red(`
49086
+ chalk17.red(`
48969
49087
  Update failed. Try manually: npm i -g ${PKG_NAME}@${latest}
48970
49088
  `)
48971
49089
  );
48972
49090
  process.exit(1);
48973
49091
  }
48974
49092
  }
48975
- var PKG_NAME, CACHE_FILE, CHECK_INTERVAL_MS;
49093
+ var PKG_NAME, CHECK_INTERVAL_MS;
48976
49094
  var init_update_check = __esm({
48977
49095
  "src/update-check.ts"() {
48978
49096
  "use strict";
49097
+ init_paths();
48979
49098
  PKG_NAME = "@westbayberry/dg";
48980
- CACHE_FILE = join7(homedir3(), ".dg-update-check.json");
48981
49099
  CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
48982
49100
  }
48983
49101
  });
@@ -49946,12 +50064,12 @@ var require_react_development = __commonJS({
49946
50064
  var SEPARATOR = ".";
49947
50065
  var SUBSEPARATOR = ":";
49948
50066
  function escape2(key) {
49949
- var escapeRegex = /[=:]/g;
50067
+ var escapeRegex2 = /[=:]/g;
49950
50068
  var escaperLookup = {
49951
50069
  "=": "=0",
49952
50070
  ":": "=2"
49953
50071
  };
49954
- var escapedString = key.replace(escapeRegex, function(match) {
50072
+ var escapedString = key.replace(escapeRegex2, function(match) {
49955
50073
  return escaperLookup[match];
49956
50074
  });
49957
50075
  return "$" + escapedString;
@@ -79974,10 +80092,10 @@ var init_source = __esm({
79974
80092
  object.level = options.level === void 0 ? colorLevel : options.level;
79975
80093
  };
79976
80094
  chalkFactory = (options) => {
79977
- const chalk15 = (...strings) => strings.join(" ");
79978
- applyOptions(chalk15, options);
79979
- Object.setPrototypeOf(chalk15, createChalk.prototype);
79980
- return chalk15;
80095
+ const chalk17 = (...strings) => strings.join(" ");
80096
+ applyOptions(chalk17, options);
80097
+ Object.setPrototypeOf(chalk17, createChalk.prototype);
80098
+ return chalk17;
79981
80099
  };
79982
80100
  Object.setPrototypeOf(createChalk.prototype, Function.prototype);
79983
80101
  for (const [styleName, style] of Object.entries(ansi_styles_default3)) {
@@ -83063,22 +83181,18 @@ var init_skip_list = __esm({
83063
83181
  "src/discover/skip-list.ts"() {
83064
83182
  "use strict";
83065
83183
  SKIP_DIRS = /* @__PURE__ */ new Set([
83066
- // Package caches
83067
83184
  "node_modules",
83068
83185
  ".venv",
83069
83186
  "venv",
83070
83187
  "__pycache__",
83071
83188
  "vendor",
83072
83189
  "bower_components",
83073
- // VCS + tooling
83074
83190
  ".git",
83075
83191
  ".tox",
83076
83192
  ".eggs",
83077
- // Build outputs
83078
83193
  "dist",
83079
83194
  "build",
83080
83195
  "target",
83081
- // target: Rust + Java
83082
83196
  ".next",
83083
83197
  ".nuxt",
83084
83198
  ".turbo",
@@ -83087,24 +83201,17 @@ var init_skip_list = __esm({
83087
83201
  ".parcel-cache",
83088
83202
  ".gradle",
83089
83203
  ".terraform",
83090
- // Test caches
83091
83204
  "coverage",
83092
83205
  ".cache",
83093
83206
  ".pytest_cache",
83094
83207
  ".mypy_cache",
83095
- // Project-local: validation outputs, fixture sandboxes, archives,
83096
- // and temp scratch (no project lives inside a tmp dir; corpus dumps
83097
- // for validation runs frequently land in tmp subtrees with 100k+
83098
- // entries that lock the walker on their readdir).
83099
83208
  "validation-results",
83100
83209
  "test-fixtures",
83101
83210
  "fixtures",
83102
83211
  "archive",
83103
83212
  "tmp",
83104
83213
  "temp",
83105
- // Vendored Ansible collections (WBB monorepo specific; harmless on others)
83106
83214
  "ansible_collections",
83107
- // IDE / editor
83108
83215
  ".idea",
83109
83216
  ".vscode"
83110
83217
  ]);
@@ -83205,7 +83312,7 @@ async function discoverProjectsAsync(root, opts = {}) {
83205
83312
  const nonEmpty = projects.filter((p) => p.packageCount > 0);
83206
83313
  return nonEmpty.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
83207
83314
  }
83208
- function discoverProjects(root) {
83315
+ function discoverProjects(_root) {
83209
83316
  throw new Error(
83210
83317
  "discoverProjects (sync) is no longer implemented. Use `await discoverProjectsAsync(root)` instead. See dependency-guardian/cli/src/discover/walker.ts."
83211
83318
  );
@@ -83268,27 +83375,137 @@ var init_walker = __esm({
83268
83375
  }
83269
83376
  });
83270
83377
 
83378
+ // src/state/hooks_registry.ts
83379
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync5, chmodSync as chmodSync3 } from "node:fs";
83380
+ import { dirname as dirname7, isAbsolute as isAbsolute3, normalize as normalize2 } from "node:path";
83381
+ import { homedir as homedir2 } from "node:os";
83382
+ function registryPath() {
83383
+ return dgStatePath("hooks-installed.json");
83384
+ }
83385
+ function isSafeAbsolute(p) {
83386
+ if (typeof p !== "string" || p.length === 0) return false;
83387
+ if (!isAbsolute3(p)) return false;
83388
+ if (p.includes("\0")) return false;
83389
+ if (p.split("/").some((seg) => seg === "..")) return false;
83390
+ if (normalize2(p) !== p) return false;
83391
+ return true;
83392
+ }
83393
+ function isWithinHomeOrTmp(p) {
83394
+ const home = homedir2();
83395
+ if (p === home || p.startsWith(home + "/")) return true;
83396
+ const rawTmp = process.env.TMPDIR || "/tmp";
83397
+ const tmpdir2 = rawTmp.endsWith("/") ? rawTmp.slice(0, -1) : rawTmp;
83398
+ if (p === tmpdir2 || p.startsWith(tmpdir2 + "/")) return true;
83399
+ if (p === "/tmp" || p.startsWith("/tmp/")) return true;
83400
+ if (p === "/private/tmp" || p.startsWith("/private/tmp/")) return true;
83401
+ if (p.startsWith("/private/var/")) return true;
83402
+ return false;
83403
+ }
83404
+ function validateEntry(raw) {
83405
+ if (!raw || typeof raw !== "object") return null;
83406
+ const o = raw;
83407
+ if (!isSafeAbsolute(o.repoPath)) return null;
83408
+ if (!isSafeAbsolute(o.hookPath)) return null;
83409
+ if (!isWithinHomeOrTmp(o.repoPath)) return null;
83410
+ if (!isWithinHomeOrTmp(o.hookPath)) return null;
83411
+ if (o.framework !== "husky" && o.framework !== "lefthook" && o.framework !== "bare") return null;
83412
+ if (typeof o.installedAt !== "string" || o.installedAt.length === 0) return null;
83413
+ return {
83414
+ repoPath: o.repoPath,
83415
+ framework: o.framework,
83416
+ hookPath: o.hookPath,
83417
+ installedAt: o.installedAt
83418
+ };
83419
+ }
83420
+ function readRegistry() {
83421
+ const path3 = registryPath();
83422
+ if (!existsSync8(path3)) return [];
83423
+ let raw;
83424
+ try {
83425
+ raw = readFileSync8(path3, "utf-8");
83426
+ } catch {
83427
+ return [];
83428
+ }
83429
+ let parsed;
83430
+ try {
83431
+ parsed = JSON.parse(raw);
83432
+ } catch {
83433
+ return [];
83434
+ }
83435
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return [];
83436
+ const file = parsed;
83437
+ if (file.version !== 1 || !Array.isArray(file.entries)) return [];
83438
+ const out = [];
83439
+ for (const e of file.entries) {
83440
+ const v = validateEntry(e);
83441
+ if (v) out.push(v);
83442
+ }
83443
+ return out;
83444
+ }
83445
+ function writeRegistry(entries) {
83446
+ const path3 = registryPath();
83447
+ const parent = dirname7(path3);
83448
+ if (!existsSync8(parent)) mkdirSync4(parent, { recursive: true, mode: 448 });
83449
+ const payload = { version: 1, entries };
83450
+ writeFileSync5(path3, JSON.stringify(payload, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
83451
+ try {
83452
+ chmodSync3(path3, 384);
83453
+ } catch {
83454
+ }
83455
+ }
83456
+ function sameEntry(a, b) {
83457
+ return a.repoPath === b.repoPath && a.hookPath === b.hookPath;
83458
+ }
83459
+ function addEntry(entry) {
83460
+ if (!isSafeAbsolute(entry.repoPath) || !isWithinHomeOrTmp(entry.repoPath)) return;
83461
+ if (!isSafeAbsolute(entry.hookPath) || !isWithinHomeOrTmp(entry.hookPath)) return;
83462
+ const next = { ...entry, installedAt: (/* @__PURE__ */ new Date()).toISOString() };
83463
+ const existing = readRegistry();
83464
+ const filtered = existing.filter((e) => !sameEntry(e, next));
83465
+ filtered.push(next);
83466
+ writeRegistry(filtered);
83467
+ }
83468
+ function removeEntry(repoPath, hookPath) {
83469
+ const existing = readRegistry();
83470
+ const filtered = existing.filter((e) => !(e.repoPath === repoPath && e.hookPath === hookPath));
83471
+ if (filtered.length === existing.length) return false;
83472
+ writeRegistry(filtered);
83473
+ return true;
83474
+ }
83475
+ function listEntries() {
83476
+ return readRegistry();
83477
+ }
83478
+ var init_hooks_registry = __esm({
83479
+ "src/state/hooks_registry.ts"() {
83480
+ "use strict";
83481
+ init_paths();
83482
+ }
83483
+ });
83484
+
83271
83485
  // src/commands/hook.ts
83272
83486
  var hook_exports = {};
83273
83487
  __export(hook_exports, {
83274
83488
  detectHookFramework: () => detectHookFramework,
83275
83489
  handleHookCommand: () => handleHookCommand,
83276
83490
  installHookForFramework: () => installHookForFramework,
83277
- previewHookInstall: () => previewHookInstall
83491
+ previewHookInstall: () => previewHookInstall,
83492
+ stripDgFromHookFile: () => stripDgFromHookFile,
83493
+ uninstallHookFromRepo: () => uninstallHookFromRepo,
83494
+ verifyHookInstalled: () => verifyHookInstalled
83278
83495
  });
83279
83496
  import { execFileSync as execFileSync2 } from "node:child_process";
83280
83497
  import {
83281
- existsSync as existsSync6,
83498
+ existsSync as existsSync9,
83282
83499
  lstatSync as lstatSync2,
83283
- readFileSync as readFileSync7,
83284
- writeFileSync as writeFileSync4,
83285
- mkdirSync,
83286
- chmodSync as chmodSync2,
83287
- unlinkSync as unlinkSync2
83500
+ readFileSync as readFileSync9,
83501
+ writeFileSync as writeFileSync6,
83502
+ mkdirSync as mkdirSync5,
83503
+ chmodSync as chmodSync4,
83504
+ unlinkSync as unlinkSync3
83288
83505
  } from "node:fs";
83289
- import { join as join9, dirname as dirname4 } from "node:path";
83506
+ import { join as join9, dirname as dirname8, resolve as resolvePath, isAbsolute as isAbsolute4 } from "node:path";
83290
83507
  function assertSafeWriteTarget(target) {
83291
- const parent = dirname4(target);
83508
+ const parent = dirname8(target);
83292
83509
  try {
83293
83510
  const parentStat = lstatSync2(parent);
83294
83511
  if (parentStat.isSymbolicLink()) {
@@ -83316,7 +83533,7 @@ function assertSafeWriteTarget(target) {
83316
83533
  }
83317
83534
  function safeWriteFileSync(target, content) {
83318
83535
  assertSafeWriteTarget(target);
83319
- writeFileSync4(target, content);
83536
+ writeFileSync6(target, content);
83320
83537
  }
83321
83538
  function findHooksDir() {
83322
83539
  try {
@@ -83345,8 +83562,8 @@ function detectHookFramework(repoRoot) {
83345
83562
  const lefthookConfigYaml = join9(root, "lefthook.yaml");
83346
83563
  const hooksDir = findHooksDir();
83347
83564
  const bareHook = join9(hooksDir, "pre-commit");
83348
- if (existsSync6(huskyHook)) {
83349
- const content = readFileSync7(huskyHook, "utf-8");
83565
+ if (existsSync9(huskyHook)) {
83566
+ const content = readFileSync9(huskyHook, "utf-8");
83350
83567
  return {
83351
83568
  framework: "husky",
83352
83569
  targetFile: huskyHook,
@@ -83354,8 +83571,8 @@ function detectHookFramework(repoRoot) {
83354
83571
  };
83355
83572
  }
83356
83573
  for (const cfg of [lefthookConfig, lefthookConfigYaml]) {
83357
- if (existsSync6(cfg)) {
83358
- const content = readFileSync7(cfg, "utf-8");
83574
+ if (existsSync9(cfg)) {
83575
+ const content = readFileSync9(cfg, "utf-8");
83359
83576
  const installed2 = /^\s+dependency-guardian\s*:/m.test(content);
83360
83577
  return {
83361
83578
  framework: "lefthook",
@@ -83365,8 +83582,8 @@ function detectHookFramework(repoRoot) {
83365
83582
  }
83366
83583
  }
83367
83584
  let installed = false;
83368
- if (existsSync6(bareHook)) {
83369
- installed = readFileSync7(bareHook, "utf-8").includes(HOOK_MARKER);
83585
+ if (existsSync9(bareHook)) {
83586
+ installed = readFileSync9(bareHook, "utf-8").includes(HOOK_MARKER);
83370
83587
  }
83371
83588
  return {
83372
83589
  framework: "bare",
@@ -83374,6 +83591,37 @@ function detectHookFramework(repoRoot) {
83374
83591
  alreadyInstalled: installed
83375
83592
  };
83376
83593
  }
83594
+ function verifyHookInstalled(info) {
83595
+ if (!existsSync9(info.targetFile)) {
83596
+ return { ok: false, reason: `hook file missing: ${info.targetFile}` };
83597
+ }
83598
+ let content;
83599
+ try {
83600
+ content = readFileSync9(info.targetFile, "utf-8");
83601
+ } catch (e) {
83602
+ return { ok: false, reason: `cannot read ${info.targetFile}: ${e.message}` };
83603
+ }
83604
+ if (info.framework === "lefthook") {
83605
+ if (!/^\s+dependency-guardian\s*:/m.test(content)) {
83606
+ return { ok: false, reason: `lefthook entry missing in ${info.targetFile}` };
83607
+ }
83608
+ return { ok: true };
83609
+ }
83610
+ if (!content.includes(HOOK_MARKER)) {
83611
+ return { ok: false, reason: `hook marker missing in ${info.targetFile}` };
83612
+ }
83613
+ if (info.framework === "bare" || info.framework === "husky") {
83614
+ try {
83615
+ const mode = lstatSync2(info.targetFile).mode & 511;
83616
+ if ((mode & 73) === 0) {
83617
+ return { ok: false, reason: `hook is not executable (mode ${mode.toString(8)})` };
83618
+ }
83619
+ } catch (e) {
83620
+ return { ok: false, reason: `cannot stat ${info.targetFile}: ${e.message}` };
83621
+ }
83622
+ }
83623
+ return { ok: true };
83624
+ }
83377
83625
  function previewHookInstall(info) {
83378
83626
  if (info.alreadyInstalled) {
83379
83627
  return `(already installed in ${info.targetFile} \u2014 no changes needed)`;
@@ -83387,7 +83635,7 @@ ${HUSKY_SNIPPET}`;
83387
83635
 
83388
83636
  ${LEFTHOOK_ENTRY}`;
83389
83637
  case "bare":
83390
- if (existsSync6(info.targetFile)) {
83638
+ if (existsSync9(info.targetFile)) {
83391
83639
  return `Will append to existing hook at ${info.targetFile}:
83392
83640
  ${HOOK_SECTION}`;
83393
83641
  }
@@ -83396,21 +83644,21 @@ ${HOOK_SCRIPT}`;
83396
83644
  }
83397
83645
  }
83398
83646
  function installHuskyHook(targetFile) {
83399
- const existing = readFileSync7(targetFile, "utf-8");
83647
+ const existing = readFileSync9(targetFile, "utf-8");
83400
83648
  if (existing.includes(HOOK_MARKER)) {
83401
83649
  process.stderr.write(" Hook already installed in Husky.\n");
83402
83650
  return;
83403
83651
  }
83404
83652
  safeWriteFileSync(targetFile, existing.trimEnd() + "\n" + HUSKY_SNIPPET);
83405
83653
  try {
83406
- chmodSync2(targetFile, 493);
83654
+ chmodSync4(targetFile, 493);
83407
83655
  } catch {
83408
83656
  }
83409
83657
  process.stderr.write(` Appended Dependency Guardian to ${targetFile}
83410
83658
  `);
83411
83659
  }
83412
83660
  function installLefthookHook(targetFile) {
83413
- const existing = readFileSync7(targetFile, "utf-8");
83661
+ const existing = readFileSync9(targetFile, "utf-8");
83414
83662
  if (/^\s+dependency-guardian\s*:/m.test(existing)) {
83415
83663
  process.stderr.write(" Hook already installed in lefthook.\n");
83416
83664
  return;
@@ -83442,16 +83690,16 @@ function installLefthookHook(targetFile) {
83442
83690
  `);
83443
83691
  }
83444
83692
  function installBareHook(targetFile) {
83445
- const hooksDir = dirname4(targetFile);
83446
- mkdirSync(hooksDir, { recursive: true });
83447
- if (existsSync6(targetFile)) {
83448
- const existing = readFileSync7(targetFile, "utf-8");
83693
+ const hooksDir = dirname8(targetFile);
83694
+ mkdirSync5(hooksDir, { recursive: true });
83695
+ if (existsSync9(targetFile)) {
83696
+ const existing = readFileSync9(targetFile, "utf-8");
83449
83697
  if (existing.includes(HOOK_MARKER)) {
83450
83698
  process.stderr.write(" Hook already installed.\n");
83451
83699
  return;
83452
83700
  }
83453
83701
  safeWriteFileSync(targetFile, existing.trimEnd() + "\n" + HOOK_SECTION);
83454
- chmodSync2(targetFile, 493);
83702
+ chmodSync4(targetFile, 493);
83455
83703
  process.stderr.write(
83456
83704
  ` Appended Dependency Guardian hook to existing ${targetFile}
83457
83705
  `
@@ -83459,7 +83707,7 @@ function installBareHook(targetFile) {
83459
83707
  return;
83460
83708
  }
83461
83709
  safeWriteFileSync(targetFile, HOOK_SCRIPT);
83462
- chmodSync2(targetFile, 493);
83710
+ chmodSync4(targetFile, 493);
83463
83711
  process.stderr.write(` Installed git pre-commit hook at ${targetFile}
83464
83712
  `);
83465
83713
  }
@@ -83479,71 +83727,107 @@ function installHookForFramework(info) {
83479
83727
  async function installHook() {
83480
83728
  const info = detectHookFramework();
83481
83729
  installHookForFramework(info);
83730
+ try {
83731
+ const repoPath = findRepoRoot();
83732
+ const hookPath = isAbsolute4(info.targetFile) ? info.targetFile : resolvePath(repoPath, info.targetFile);
83733
+ addEntry({
83734
+ repoPath,
83735
+ framework: info.framework,
83736
+ hookPath
83737
+ });
83738
+ } catch {
83739
+ }
83482
83740
  try {
83483
83741
  const { pingSetupEvent: pingSetupEvent2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
83484
83742
  await pingSetupEvent2("hook_installed");
83485
83743
  } catch {
83486
83744
  }
83487
83745
  }
83488
- async function uninstallHook() {
83746
+ function stripDgFromHookFile(hookPath, framework, repoPath) {
83747
+ if (!existsSync9(hookPath)) {
83748
+ return { status: "not-found", hookPath, repoPath };
83749
+ }
83750
+ const content = readFileSync9(hookPath, "utf-8");
83751
+ if (framework === "lefthook") {
83752
+ if (!/^\s+dependency-guardian\s*:/m.test(content)) {
83753
+ return { status: "not-found", hookPath, repoPath };
83754
+ }
83755
+ const stripped = content.replace(
83756
+ /^\s+dependency-guardian\s*:\s*\n(?:\s{6,}.*\n)+/gm,
83757
+ ""
83758
+ );
83759
+ safeWriteFileSync(hookPath, stripped);
83760
+ removeEntry(repoPath, hookPath);
83761
+ return { status: "removed", hookPath, repoPath };
83762
+ }
83763
+ if (!content.includes(HOOK_MARKER)) {
83764
+ return { status: "not-found", hookPath, repoPath };
83765
+ }
83766
+ if (framework === "bare" && content.trimStart().startsWith("#!/bin/sh\n" + HOOK_MARKER)) {
83767
+ unlinkSync3(hookPath);
83768
+ removeEntry(repoPath, hookPath);
83769
+ return { status: "removed", hookPath, repoPath };
83770
+ }
83771
+ const startIdx = content.indexOf(MARKER_START);
83772
+ const endIdx = content.indexOf(MARKER_END);
83773
+ if (startIdx === -1 || endIdx === -1) {
83774
+ return { status: "not-found", hookPath, repoPath };
83775
+ }
83776
+ const before = content.slice(0, startIdx).trimEnd();
83777
+ const after = content.slice(endIdx + MARKER_END.length).trimStart();
83778
+ const remaining = (before + (after ? "\n" + after : "")).trimEnd() + "\n";
83779
+ if (remaining.trim() === "" || remaining.trim() === "#!/bin/sh") {
83780
+ unlinkSync3(hookPath);
83781
+ } else {
83782
+ safeWriteFileSync(hookPath, remaining);
83783
+ }
83784
+ removeEntry(repoPath, hookPath);
83785
+ return { status: "removed", hookPath, repoPath };
83786
+ }
83787
+ function uninstallHookFromRepo(repoRootOverride) {
83788
+ const root = repoRootOverride ?? findRepoRoot();
83489
83789
  const hooksDir = findHooksDir();
83490
83790
  const hookPath = join9(hooksDir, "pre-commit");
83491
- try {
83492
- const root = findRepoRoot();
83493
- const huskyPath = join9(root, ".husky", "pre-commit");
83494
- if (existsSync6(huskyPath)) {
83495
- const content2 = readFileSync7(huskyPath, "utf-8");
83496
- if (content2.includes(HOOK_MARKER)) {
83497
- const startIdx2 = content2.indexOf(MARKER_START);
83498
- const endIdx2 = content2.indexOf(MARKER_END);
83499
- if (startIdx2 !== -1 && endIdx2 !== -1) {
83500
- const before = content2.slice(0, startIdx2).trimEnd();
83501
- const after = content2.slice(endIdx2 + MARKER_END.length).trimStart();
83502
- const remaining = (before + (after ? "\n" + after : "")).trimEnd() + "\n";
83503
- safeWriteFileSync(huskyPath, remaining);
83504
- process.stderr.write(
83505
- ` Removed Dependency Guardian section from ${huskyPath}
83506
- `
83507
- );
83508
- return;
83509
- }
83510
- }
83511
- }
83512
- for (const cfg of [join9(root, "lefthook.yml"), join9(root, "lefthook.yaml")]) {
83513
- if (existsSync6(cfg)) {
83514
- const content2 = readFileSync7(cfg, "utf-8");
83515
- if (/^\s+dependency-guardian\s*:/m.test(content2)) {
83516
- const stripped = content2.replace(
83517
- /^\s+dependency-guardian\s*:\s*\n(?:\s{6,}.*\n)+/gm,
83518
- ""
83519
- );
83520
- safeWriteFileSync(cfg, stripped);
83521
- process.stderr.write(
83522
- ` Removed Dependency Guardian section from ${cfg}
83523
- `
83524
- );
83525
- return;
83526
- }
83527
- }
83791
+ const huskyPath = join9(root, ".husky", "pre-commit");
83792
+ if (existsSync9(huskyPath)) {
83793
+ const content2 = readFileSync9(huskyPath, "utf-8");
83794
+ if (content2.includes(HOOK_MARKER)) {
83795
+ const startIdx2 = content2.indexOf(MARKER_START);
83796
+ const endIdx2 = content2.indexOf(MARKER_END);
83797
+ if (startIdx2 !== -1 && endIdx2 !== -1) {
83798
+ const before = content2.slice(0, startIdx2).trimEnd();
83799
+ const after = content2.slice(endIdx2 + MARKER_END.length).trimStart();
83800
+ const remaining = (before + (after ? "\n" + after : "")).trimEnd() + "\n";
83801
+ safeWriteFileSync(huskyPath, remaining);
83802
+ removeEntry(root, huskyPath);
83803
+ return { status: "removed", hookPath: huskyPath, repoPath: root };
83804
+ }
83805
+ }
83806
+ }
83807
+ for (const cfg of [join9(root, "lefthook.yml"), join9(root, "lefthook.yaml")]) {
83808
+ if (!existsSync9(cfg)) continue;
83809
+ const content2 = readFileSync9(cfg, "utf-8");
83810
+ if (/^\s+dependency-guardian\s*:/m.test(content2)) {
83811
+ const stripped = content2.replace(
83812
+ /^\s+dependency-guardian\s*:\s*\n(?:\s{6,}.*\n)+/gm,
83813
+ ""
83814
+ );
83815
+ safeWriteFileSync(cfg, stripped);
83816
+ removeEntry(root, cfg);
83817
+ return { status: "removed", hookPath: cfg, repoPath: root };
83528
83818
  }
83529
- } catch {
83530
83819
  }
83531
- if (!existsSync6(hookPath)) {
83532
- process.stderr.write(" No hook to remove.\n");
83533
- return;
83820
+ if (!existsSync9(hookPath)) {
83821
+ return { status: "not-found", hookPath, repoPath: root };
83534
83822
  }
83535
- const content = readFileSync7(hookPath, "utf-8");
83823
+ const content = readFileSync9(hookPath, "utf-8");
83536
83824
  if (!content.includes(HOOK_MARKER)) {
83537
- process.stderr.write(
83538
- " No Dependency Guardian hook found in pre-commit.\n"
83539
- );
83540
- return;
83825
+ return { status: "not-found", hookPath, repoPath: root };
83541
83826
  }
83542
83827
  if (content.trimStart().startsWith("#!/bin/sh\n" + HOOK_MARKER)) {
83543
- unlinkSync2(hookPath);
83544
- process.stderr.write(` Removed pre-commit hook at ${hookPath}
83545
- `);
83546
- return;
83828
+ unlinkSync3(hookPath);
83829
+ removeEntry(root, hookPath);
83830
+ return { status: "removed", hookPath, repoPath: root };
83547
83831
  }
83548
83832
  const startIdx = content.indexOf(MARKER_START);
83549
83833
  const endIdx = content.indexOf(MARKER_END);
@@ -83552,19 +83836,32 @@ async function uninstallHook() {
83552
83836
  const after = content.slice(endIdx + MARKER_END.length).trimStart();
83553
83837
  const remaining = (before + (after ? "\n" + after : "")).trimEnd() + "\n";
83554
83838
  safeWriteFileSync(hookPath, remaining);
83555
- process.stderr.write(
83556
- ` Removed Dependency Guardian section from ${hookPath}
83557
- `
83558
- );
83839
+ removeEntry(root, hookPath);
83840
+ return { status: "removed", hookPath, repoPath: root };
83841
+ }
83842
+ unlinkSync3(hookPath);
83843
+ removeEntry(root, hookPath);
83844
+ return { status: "removed", hookPath, repoPath: root };
83845
+ }
83846
+ async function uninstallHook() {
83847
+ let result;
83848
+ try {
83849
+ result = uninstallHookFromRepo();
83850
+ } catch (e) {
83851
+ process.stderr.write(` Error: ${e.message}
83852
+ `);
83559
83853
  return;
83560
83854
  }
83561
- unlinkSync2(hookPath);
83562
- process.stderr.write(` Removed pre-commit hook at ${hookPath}
83855
+ if (result.status === "removed") {
83856
+ process.stderr.write(` Removed Dependency Guardian section from ${result.hookPath}
83563
83857
  `);
83564
- try {
83565
- const { pingSetupEvent: pingSetupEvent2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
83566
- await pingSetupEvent2("hook_uninstalled");
83567
- } catch {
83858
+ try {
83859
+ const { pingSetupEvent: pingSetupEvent2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
83860
+ await pingSetupEvent2("hook_uninstalled");
83861
+ } catch {
83862
+ }
83863
+ } else {
83864
+ process.stderr.write(" No Dependency Guardian hook found in this repo.\n");
83568
83865
  }
83569
83866
  }
83570
83867
  async function handleHookCommand(args) {
@@ -83578,11 +83875,6 @@ async function handleHookCommand(args) {
83578
83875
  await installHook();
83579
83876
  } else if (subcommand === "uninstall") {
83580
83877
  await uninstallHook();
83581
- try {
83582
- const { pingSetupEvent: pingSetupEvent2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
83583
- await pingSetupEvent2("hook_uninstalled");
83584
- } catch {
83585
- }
83586
83878
  } else {
83587
83879
  process.stderr.write(` Unknown hook command: ${subcommand}
83588
83880
  `);
@@ -83600,6 +83892,7 @@ var HOOK_MARKER, MARKER_START, MARKER_END, LEFTHOOK_MARKER, HOOK_SCRIPT, HOOK_SE
83600
83892
  var init_hook = __esm({
83601
83893
  "src/commands/hook.ts"() {
83602
83894
  "use strict";
83895
+ init_hooks_registry();
83603
83896
  HOOK_MARKER = "# dependency-guardian-hook";
83604
83897
  MARKER_START = "# dependency-guardian-hook-start";
83605
83898
  MARKER_END = "# dependency-guardian-hook-end";
@@ -83968,14 +84261,14 @@ var init_parse_package_json = __esm({
83968
84261
 
83969
84262
  // src/lockfile/index.ts
83970
84263
  import { execFileSync as execFileSync3 } from "node:child_process";
83971
- import { readFileSync as readFileSync8, existsSync as existsSync7, statSync } from "node:fs";
84264
+ import { readFileSync as readFileSync10, existsSync as existsSync10, statSync as statSync2 } from "node:fs";
83972
84265
  import { join as join10 } from "node:path";
83973
84266
  function readFileSafe(path3) {
83974
- const size = statSync(path3).size;
84267
+ const size = statSync2(path3).size;
83975
84268
  if (size > MAX_LOCKFILE_BYTES) {
83976
84269
  throw new Error(`Lockfile too large (${(size / 1024 / 1024).toFixed(0)} MB, max 50 MB): ${path3}`);
83977
84270
  }
83978
- return readFileSync8(path3, "utf-8");
84271
+ return readFileSync10(path3, "utf-8");
83979
84272
  }
83980
84273
  function discoverChanges(cwd2, config3) {
83981
84274
  if (config3.workspace) {
@@ -83985,7 +84278,7 @@ function discoverChanges(cwd2, config3) {
83985
84278
  const pythonDepFiles = ["requirements.txt", "Pipfile.lock", "poetry.lock"];
83986
84279
  let pythonPackages = [];
83987
84280
  for (const pyFile of pythonDepFiles) {
83988
- if (existsSync7(join10(cwd2, pyFile))) {
84281
+ if (existsSync10(join10(cwd2, pyFile))) {
83989
84282
  const pyPkgs = parsePythonDepFile(cwd2, pyFile);
83990
84283
  for (const p of pyPkgs) {
83991
84284
  if (p.version === "latest") continue;
@@ -84011,7 +84304,7 @@ function discoverChanges(cwd2, config3) {
84011
84304
  const headParsed = parseLockfileByType(headContent, lockfileInfo.type);
84012
84305
  const directDeps = getDirectDeps(cwd2);
84013
84306
  if (config3.baseLockfile) {
84014
- if (!existsSync7(config3.baseLockfile)) {
84307
+ if (!existsSync10(config3.baseLockfile)) {
84015
84308
  throw new Error(`Base lockfile not found: ${config3.baseLockfile}`);
84016
84309
  }
84017
84310
  const baseContent = readFileSafe(config3.baseLockfile);
@@ -84037,7 +84330,7 @@ function discoverChanges(cwd2, config3) {
84037
84330
  };
84038
84331
  }
84039
84332
  const pkgJsonPath = join10(cwd2, "package.json");
84040
- if (existsSync7(pkgJsonPath)) {
84333
+ if (existsSync10(pkgJsonPath)) {
84041
84334
  const headPkgJson = readFileSafe(pkgJsonPath);
84042
84335
  const basePkgJson = getGitBaseFile(cwd2, "package.json");
84043
84336
  if (basePkgJson !== null) {
@@ -84081,7 +84374,7 @@ function findLockfile(cwd2) {
84081
84374
  ];
84082
84375
  for (const [name, type] of candidates) {
84083
84376
  const p = join10(cwd2, name);
84084
- if (existsSync7(p)) return { path: p, type };
84377
+ if (existsSync10(p)) return { path: p, type };
84085
84378
  }
84086
84379
  return null;
84087
84380
  }
@@ -84285,16 +84578,20 @@ var init_sanitize = __esm({
84285
84578
  // src/cache/local_cache.ts
84286
84579
  import * as path2 from "node:path";
84287
84580
  import * as fs2 from "node:fs";
84288
- import * as os5 from "node:os";
84289
84581
  import { createRequire } from "node:module";
84290
84582
  function defaultDbPath() {
84291
- const baseDir = process.env.DG_CACHE_DIR ?? path2.join(os5.homedir(), ".dg");
84292
- return path2.join(baseDir, "cache.sqlite");
84583
+ return dgCachePath("scans.sqlite");
84293
84584
  }
84294
84585
  function tryLoadDriver() {
84295
84586
  try {
84296
84587
  const mod = moduleRequire("node-sqlite3-wasm");
84297
84588
  const Ctor = mod.Database;
84589
+ if (typeof Ctor !== "function") {
84590
+ if (process.env.DG_PERF) {
84591
+ console.warn(`[CLI-CACHE] node-sqlite3-wasm did not export Database (got ${typeof Ctor})`);
84592
+ }
84593
+ return null;
84594
+ }
84298
84595
  return (p) => new Ctor(p);
84299
84596
  } catch (err) {
84300
84597
  if (process.env.DG_PERF) {
@@ -84311,6 +84608,7 @@ var moduleRequire, DEFAULT_MAX_ROWS, DEFAULT_TTL_SECONDS, LocalScanCache, _share
84311
84608
  var init_local_cache = __esm({
84312
84609
  "src/cache/local_cache.ts"() {
84313
84610
  "use strict";
84611
+ init_paths();
84314
84612
  moduleRequire = createRequire(import.meta.url);
84315
84613
  DEFAULT_MAX_ROWS = 1e5;
84316
84614
  DEFAULT_TTL_SECONDS = 24 * 60 * 60;
@@ -84338,6 +84636,15 @@ var init_local_cache = __esm({
84338
84636
  this.db.exec("PRAGMA busy_timeout = 5000");
84339
84637
  this.initSchema();
84340
84638
  this.vacuumExpired();
84639
+ if (!fs2.existsSync(dbPath)) {
84640
+ if (process.env.DG_PERF) {
84641
+ console.warn(`[CLI-CACHE] cache file ${dbPath} did not materialize after init \u2014 disabling`);
84642
+ }
84643
+ this.disabled = true;
84644
+ this.db = null;
84645
+ return;
84646
+ }
84647
+ this.mergeLegacyIfPresent();
84341
84648
  } catch (err) {
84342
84649
  this.disabled = true;
84343
84650
  if (process.env.DG_PERF) {
@@ -84346,6 +84653,61 @@ var init_local_cache = __esm({
84346
84653
  this.db = null;
84347
84654
  }
84348
84655
  }
84656
+ mergeLegacyIfPresent() {
84657
+ if (!this.db || !this.insertStmt) return;
84658
+ const legacy = legacyPaths().cacheSqlite;
84659
+ if (!fs2.existsSync(legacy)) return;
84660
+ const driver = tryLoadDriver();
84661
+ if (!driver) return;
84662
+ let legacyDb = null;
84663
+ try {
84664
+ legacyDb = driver(legacy);
84665
+ const rows = legacyDb.all(
84666
+ "SELECT name, version, score, payload, fetched_at, expires_at FROM scan_cache_v2 WHERE expires_at > ?",
84667
+ [Date.now()]
84668
+ );
84669
+ if (rows.length === 0) {
84670
+ try {
84671
+ legacyDb.close();
84672
+ } catch {
84673
+ }
84674
+ try {
84675
+ fs2.unlinkSync(legacy);
84676
+ } catch {
84677
+ }
84678
+ return;
84679
+ }
84680
+ const stmt = this.insertStmt;
84681
+ this.db.exec("BEGIN");
84682
+ for (const r of rows) {
84683
+ stmt.run([r.name, r.version, r.score, r.payload, r.fetched_at, r.expires_at]);
84684
+ }
84685
+ this.db.exec("COMMIT");
84686
+ if (process.env.DG_PERF) {
84687
+ console.warn(`[CLI-CACHE] merged ${rows.length} rows from legacy ${legacy}`);
84688
+ }
84689
+ try {
84690
+ legacyDb.close();
84691
+ } catch {
84692
+ }
84693
+ try {
84694
+ fs2.unlinkSync(legacy);
84695
+ } catch {
84696
+ }
84697
+ } catch (err) {
84698
+ try {
84699
+ legacyDb?.close();
84700
+ } catch {
84701
+ }
84702
+ try {
84703
+ this.db.exec("ROLLBACK");
84704
+ } catch {
84705
+ }
84706
+ if (process.env.DG_PERF) {
84707
+ console.warn(`[CLI-CACHE] legacy cache merge failed: ${err instanceof Error ? err.message : err}`);
84708
+ }
84709
+ }
84710
+ }
84349
84711
  initSchema() {
84350
84712
  if (!this.db) return;
84351
84713
  this.db.exec(`
@@ -84372,21 +84734,9 @@ var init_local_cache = __esm({
84372
84734
  expires_at = excluded.expires_at
84373
84735
  `);
84374
84736
  }
84375
- /**
84376
- * Collision-free key for the in-memory Map returned by `lookup()`. Uses
84377
- * a NUL separator (illegal in npm + PyPI package names — JS strings
84378
- * handle NUL fine; the v1 `${name}@${version}` form was string-collidable
84379
- * when names contained "@"). Callers must use this same helper to look
84380
- * entries up — never reconstruct by hand.
84381
- */
84382
84737
  static cacheKey(name, version) {
84383
84738
  return `${name}\0${version}`;
84384
84739
  }
84385
- /**
84386
- * Batch lookup. Returns a Map keyed by the v2 key (`name\0version`). Expired
84387
- * rows are excluded by the WHERE clause; the caller can treat absence as
84388
- * "go ask the server."
84389
- */
84390
84740
  lookup(packages) {
84391
84741
  const out = /* @__PURE__ */ new Map();
84392
84742
  if (this.disabled || !this.db || packages.length === 0) return out;
@@ -84427,11 +84777,6 @@ var init_local_cache = __esm({
84427
84777
  }
84428
84778
  return out;
84429
84779
  }
84430
- /**
84431
- * Persist fresh scan results. Uses a single transaction so all rows commit
84432
- * together (fast + crash-safe). Inserts past maxRows trigger LRU eviction
84433
- * on the oldest fetched_at.
84434
- */
84435
84780
  insert(rows) {
84436
84781
  if (this.disabled || !this.db || !this.insertStmt || rows.length === 0) return;
84437
84782
  const now = Date.now();
@@ -84516,7 +84861,10 @@ var init_local_cache = __esm({
84516
84861
  // src/api/client.ts
84517
84862
  var client_exports = {};
84518
84863
  __export(client_exports, {
84864
+ ANON_BATCH_SIZE: () => ANON_BATCH_SIZE,
84519
84865
  APIError: () => APIError,
84866
+ BATCH_SIZE: () => BATCH_SIZE,
84867
+ BatchPoolPartialError: () => BatchPoolPartialError,
84520
84868
  ClientOutdatedError: () => ClientOutdatedError,
84521
84869
  TrialExhaustedError: () => TrialExhaustedError,
84522
84870
  callAnalyzeAPI: () => callAnalyzeAPI,
@@ -84594,23 +84942,32 @@ async function callAnalyzeAPI(packages, config3, onProgress) {
84594
84942
  batches.push(uncached.slice(i, i + batchSize));
84595
84943
  }
84596
84944
  const tTotal = Date.now();
84597
- const results = await runBatchPool(
84598
- batches,
84599
- (batch, onRateLimit, onPackageProgress) => callBatchWithRetry(batch, config3, onRateLimit, onPackageProgress),
84600
- {
84601
- onBatchDone: (_batchIndex, batch, completed) => {
84602
- if (adjustedOnProgress) {
84603
- adjustedOnProgress(completed, uncached.length, batch.map((p) => p.name));
84604
- }
84605
- },
84606
- onPackageProgress: (completed, latestName) => {
84607
- if (adjustedOnProgress) {
84608
- adjustedOnProgress(completed, uncached.length, latestName ? [latestName] : []);
84609
- }
84610
- },
84611
- totalBatches: batches.length
84945
+ let results;
84946
+ try {
84947
+ results = await runBatchPool(
84948
+ batches,
84949
+ (batch, onRateLimit, onPackageProgress) => callBatchWithRetry(batch, config3, onRateLimit, onPackageProgress),
84950
+ {
84951
+ onBatchDone: (_batchIndex, batch, completed) => {
84952
+ if (adjustedOnProgress) {
84953
+ adjustedOnProgress(completed, uncached.length, batch.map((p) => p.name));
84954
+ }
84955
+ },
84956
+ onPackageProgress: (completed, latestName) => {
84957
+ if (adjustedOnProgress) {
84958
+ adjustedOnProgress(completed, uncached.length, latestName ? [latestName] : []);
84959
+ }
84960
+ },
84961
+ totalBatches: batches.length
84962
+ }
84963
+ );
84964
+ } catch (e) {
84965
+ if (e instanceof BatchPoolPartialError && e.partialResults.length > 0) {
84966
+ const partial = mergeResponses(e.partialResults);
84967
+ persistServerResponse(cache3, partial);
84612
84968
  }
84613
- );
84969
+ throw e instanceof BatchPoolPartialError ? e.cause : e;
84970
+ }
84614
84971
  if (process.env.DG_PERF) console.error(`[CLI-PERF] total: ${uncached.length} uncached packages \u2192 ${Date.now() - tTotal}ms`);
84615
84972
  serverResponse = mergeResponses(results);
84616
84973
  }
@@ -84755,8 +85112,8 @@ async function runBatchPool(batches, fn, opts) {
84755
85112
  if (catchup > 0) completedPackages += catchup;
84756
85113
  if (opts.onBatchDone) opts.onBatchDone(i, batch, completedPackages);
84757
85114
  } catch (e) {
84758
- firstError = e;
84759
- throw e;
85115
+ if (firstError === null) firstError = e;
85116
+ return;
84760
85117
  }
84761
85118
  }
84762
85119
  }
@@ -84764,7 +85121,10 @@ async function runBatchPool(batches, fn, opts) {
84764
85121
  const workers = [];
84765
85122
  for (let s = 0; s < workerCount; s++) workers.push(worker(s));
84766
85123
  await Promise.allSettled(workers);
84767
- if (firstError !== null) throw firstError;
85124
+ if (firstError !== null) {
85125
+ const partials = results.filter((r) => r !== void 0);
85126
+ throw new BatchPoolPartialError(firstError, partials);
85127
+ }
84768
85128
  return results;
84769
85129
  }
84770
85130
  async function callBatchWithRetry(packages, config3, onRateLimit, onPackageProgress) {
@@ -84818,7 +85178,6 @@ function mergeResponses(results) {
84818
85178
  action,
84819
85179
  packages: allPackages,
84820
85180
  safeVersions,
84821
- // Sum, not max — batches run sequentially, total wall-clock is the sum
84822
85181
  durationMs: results.reduce((s, r) => s + (r.durationMs || 0), 0)
84823
85182
  };
84824
85183
  }
@@ -84970,26 +85329,52 @@ async function readNdjsonAnalyzeResponse(response, onPackageProgress) {
84970
85329
  async function callPyPIAnalyzeAPI(packages, config3, onProgress) {
84971
85330
  const batchSize = config3.apiKey ? BATCH_SIZE : ANON_BATCH_SIZE;
84972
85331
  _currentScanId = randomUUID2();
84973
- if (packages.length <= batchSize) {
84974
- return callPyPIBatch(packages, config3);
85332
+ const cache3 = getSharedLocalCache();
85333
+ const tLocalLookup = Date.now();
85334
+ const localHits = cache3.lookup(packages.map((p) => ({ name: p.name, version: p.version })));
85335
+ if (process.env.DG_PERF && !cache3.isDisabled) {
85336
+ console.error(`[CLI-PERF] pypi local cache: ${localHits.size}/${packages.length} hits \u2192 ${Date.now() - tLocalLookup}ms`);
84975
85337
  }
84976
- const batches = [];
84977
- for (let i = 0; i < packages.length; i += batchSize) {
84978
- batches.push(packages.slice(i, i + batchSize));
85338
+ const uncached = packages.filter((p) => !localHits.has(LocalScanCache.cacheKey(p.name, p.version)));
85339
+ if (uncached.length === 0) {
85340
+ if (onProgress) onProgress(packages.length, packages.length);
85341
+ return localHitsToResponse(packages, localHits);
84979
85342
  }
84980
- const tTotal = Date.now();
84981
- const results = await runBatchPool(
84982
- batches,
84983
- (batch) => callPyPIBatch(batch, config3),
84984
- {
84985
- onBatchDone: (_i, _batch, completed) => {
84986
- if (onProgress) onProgress(completed, packages.length);
84987
- },
84988
- totalBatches: batches.length
85343
+ let serverResponse;
85344
+ if (uncached.length <= batchSize) {
85345
+ serverResponse = await callPyPIBatch(uncached, config3);
85346
+ if (onProgress) onProgress(localHits.size + uncached.length, packages.length);
85347
+ } else {
85348
+ const batches = [];
85349
+ for (let i = 0; i < uncached.length; i += batchSize) {
85350
+ batches.push(uncached.slice(i, i + batchSize));
84989
85351
  }
84990
- );
84991
- if (process.env.DG_PERF) console.error(`[CLI-PERF] pypi total: ${packages.length} packages \u2192 ${Date.now() - tTotal}ms`);
84992
- return mergeResponses(results);
85352
+ const tTotal = Date.now();
85353
+ let results;
85354
+ try {
85355
+ results = await runBatchPool(
85356
+ batches,
85357
+ (batch) => callPyPIBatch(batch, config3),
85358
+ {
85359
+ onBatchDone: (_i, _batch, completed) => {
85360
+ if (onProgress) onProgress(localHits.size + completed, packages.length);
85361
+ },
85362
+ totalBatches: batches.length
85363
+ }
85364
+ );
85365
+ } catch (e) {
85366
+ if (e instanceof BatchPoolPartialError && e.partialResults.length > 0) {
85367
+ const partial = mergeResponses(e.partialResults);
85368
+ persistServerResponse(cache3, partial);
85369
+ }
85370
+ throw e instanceof BatchPoolPartialError ? e.cause : e;
85371
+ }
85372
+ if (process.env.DG_PERF) console.error(`[CLI-PERF] pypi total: ${uncached.length} uncached \u2192 ${Date.now() - tTotal}ms`);
85373
+ serverResponse = mergeResponses(results);
85374
+ }
85375
+ persistServerResponse(cache3, serverResponse);
85376
+ if (localHits.size === 0) return serverResponse;
85377
+ return mergeLocalHitsWithServer(packages, localHits, serverResponse);
84993
85378
  }
84994
85379
  async function callPyPIBatch(packages, config3) {
84995
85380
  const url = `${config3.apiUrl}/v1/pypi/analyze`;
@@ -85052,7 +85437,7 @@ async function callPyPIBatch(packages, config3) {
85052
85437
  checkVersionFloor(raw);
85053
85438
  return sanitizeResponse(raw);
85054
85439
  }
85055
- var APIError, TrialExhaustedError, ClientOutdatedError, BATCH_SIZE, ANON_BATCH_SIZE, MAX_RETRIES, RETRY_DELAY_MS, DEFAULT_BATCH_CONCURRENCY, _currentScanId;
85440
+ var APIError, TrialExhaustedError, ClientOutdatedError, BATCH_SIZE, ANON_BATCH_SIZE, MAX_RETRIES, RETRY_DELAY_MS, DEFAULT_BATCH_CONCURRENCY, _currentScanId, BatchPoolPartialError;
85056
85441
  var init_client3 = __esm({
85057
85442
  "src/api/client.ts"() {
85058
85443
  "use strict";
@@ -85087,12 +85472,20 @@ var init_client3 = __esm({
85087
85472
  this.name = "ClientOutdatedError";
85088
85473
  }
85089
85474
  };
85090
- BATCH_SIZE = 200;
85091
- ANON_BATCH_SIZE = 200;
85475
+ BATCH_SIZE = 50;
85476
+ ANON_BATCH_SIZE = 50;
85092
85477
  MAX_RETRIES = 2;
85093
85478
  RETRY_DELAY_MS = 5e3;
85094
85479
  DEFAULT_BATCH_CONCURRENCY = 4;
85095
85480
  _currentScanId = "";
85481
+ BatchPoolPartialError = class extends Error {
85482
+ constructor(cause, partialResults) {
85483
+ super(cause instanceof Error ? cause.message : String(cause));
85484
+ this.cause = cause;
85485
+ this.partialResults = partialResults;
85486
+ this.name = "BatchPoolPartialError";
85487
+ }
85488
+ };
85096
85489
  }
85097
85490
  });
85098
85491
 
@@ -85143,6 +85536,19 @@ async function scanProjectAtPath(cwd2, config3, onProgress) {
85143
85536
  cumulativeDone += pyPackages.length;
85144
85537
  }
85145
85538
  const allPackages = responses.flatMap((r) => r.packages);
85539
+ if (allPackages.length === 0) {
85540
+ const elapsed = elapsedMs();
85541
+ return {
85542
+ status: "api_returned_empty",
85543
+ result: {
85544
+ result: { score: 0, action: "pass", packages: [], safeVersions: {}, durationMs: elapsed },
85545
+ durationMs: elapsed,
85546
+ scannedCount: 0,
85547
+ skippedCount: discovery.skipped?.length ?? 0
85548
+ },
85549
+ message: `Analyze API returned 0 results for ${totalDiscovered} discovered packages (npm: ${npmPackages.length}, pypi: ${pyPackages.length}). This is not a 'nothing to scan' state \u2014 the API call succeeded but returned an empty packages list. Check API status and re-run.`
85550
+ };
85551
+ }
85146
85552
  const maxScore = allPackages.length > 0 ? Math.max(0, ...allPackages.map((p) => p.score)) : 0;
85147
85553
  const anyIncomplete = responses.some((r) => r.action === "analysis_incomplete") || allPackages.some((p) => Array.isArray(p.findings) && p.findings.some((f) => f?.id === "analysis_incomplete"));
85148
85554
  const action = maxScore >= 70 ? "block" : maxScore >= 60 ? "warn" : anyIncomplete ? "analysis_incomplete" : "pass";
@@ -85216,11 +85622,11 @@ __export(protect_exports, {
85216
85622
  stripAliasSourceFromRc: () => stripAliasSourceFromRc,
85217
85623
  suggestedShellRc: () => suggestedShellRc
85218
85624
  });
85219
- import { existsSync as existsSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync5, unlinkSync as unlinkSync3, mkdirSync as mkdirSync3, lstatSync as lstatSync3, appendFileSync } from "node:fs";
85220
- import { join as join12, dirname as dirname6 } from "node:path";
85221
- import { homedir as homedir5 } from "node:os";
85625
+ import { existsSync as existsSync12, readFileSync as readFileSync11, writeFileSync as writeFileSync7, unlinkSync as unlinkSync5, mkdirSync as mkdirSync7, lstatSync as lstatSync3, appendFileSync } from "node:fs";
85626
+ import { join as join11, dirname as dirname10 } from "node:path";
85627
+ import { homedir as homedir3 } from "node:os";
85222
85628
  function aliasFile() {
85223
- return join12(homedir5(), ".dependency-guardian", "aliases.sh");
85629
+ return dgStatePath("aliases.sh");
85224
85630
  }
85225
85631
  function safeIsRegularFile(path3) {
85226
85632
  try {
@@ -85231,7 +85637,7 @@ function safeIsRegularFile(path3) {
85231
85637
  }
85232
85638
  }
85233
85639
  function safeWriteFileSync2(target, content, mode = 420) {
85234
- const parent = dirname6(target);
85640
+ const parent = dirname10(target);
85235
85641
  try {
85236
85642
  const parentStat = lstatSync3(parent);
85237
85643
  if (parentStat.isSymbolicLink()) {
@@ -85248,34 +85654,23 @@ function safeWriteFileSync2(target, content, mode = 420) {
85248
85654
  } catch (e) {
85249
85655
  if (e instanceof Error && e.message.startsWith("refusing")) throw e;
85250
85656
  }
85251
- writeFileSync5(target, content, { encoding: "utf-8", mode });
85657
+ writeFileSync7(target, content, { encoding: "utf-8", mode });
85252
85658
  }
85253
- function readProtectConfig(cwd2) {
85254
- const stored = getProtectConfig();
85255
- if (stored) return stored;
85256
- const legacyPath = join12(cwd2, LEGACY_PROJECT_FILE);
85257
- if (safeIsRegularFile(legacyPath)) {
85258
- try {
85259
- const data = JSON.parse(readFileSync9(legacyPath, "utf-8"));
85260
- if (typeof data === "object" && data !== null) {
85261
- const cfg = data;
85262
- setProtectConfig(cfg);
85263
- return cfg;
85264
- }
85265
- } catch {
85266
- }
85267
- }
85268
- return null;
85659
+ function readProtectConfig(_cwd) {
85660
+ return getProtectConfig() ?? null;
85269
85661
  }
85270
85662
  function writeProtectConfig(_cwd, cfg) {
85271
85663
  setProtectConfig(cfg);
85272
85664
  }
85273
85665
  function writeAliasSnippet() {
85274
- const dir = dirname6(aliasFile());
85275
- mkdirSync3(dir, { recursive: true });
85666
+ const dir = dirname10(aliasFile());
85667
+ mkdirSync7(dir, { recursive: true, mode: 448 });
85276
85668
  safeWriteFileSync2(aliasFile(), ALIAS_SNIPPET);
85277
85669
  return aliasFile();
85278
85670
  }
85671
+ function legacyAliasPath() {
85672
+ return legacyPaths().aliasesShOld;
85673
+ }
85279
85674
  function suggestedShellRc() {
85280
85675
  const shell = (process.env.SHELL ?? "").toLowerCase();
85281
85676
  if (shell.includes("zsh")) return { rc: "~/.zshrc", shellName: "zsh" };
@@ -85284,19 +85679,19 @@ function suggestedShellRc() {
85284
85679
  return { rc: "~/.zshrc", shellName: "your shell rc" };
85285
85680
  }
85286
85681
  function expandHome(path3) {
85287
- if (path3.startsWith("~/")) return join12(homedir5(), path3.slice(2));
85288
- if (path3 === "~") return homedir5();
85682
+ if (path3.startsWith("~/")) return join11(homedir3(), path3.slice(2));
85683
+ if (path3 === "~") return homedir3();
85289
85684
  return path3;
85290
85685
  }
85291
85686
  function appendAliasSourceToRc(rcPathRaw, sourceLine) {
85292
85687
  const rcPath = expandHome(rcPathRaw);
85293
- const home = homedir5();
85688
+ const home = homedir3();
85294
85689
  if (!rcPath.startsWith(home + "/") && rcPath !== home) {
85295
85690
  return { status: "failed", rcPath, reason: "rc path is outside the home directory" };
85296
85691
  }
85297
85692
  try {
85298
- const parent = dirname6(rcPath);
85299
- if (existsSync9(parent)) {
85693
+ const parent = dirname10(rcPath);
85694
+ if (existsSync12(parent)) {
85300
85695
  const parentStat = lstatSync3(parent);
85301
85696
  if (parentStat.isSymbolicLink()) {
85302
85697
  return { status: "failed", rcPath, reason: "parent directory is a symlink" };
@@ -85305,7 +85700,7 @@ function appendAliasSourceToRc(rcPathRaw, sourceLine) {
85305
85700
  } catch (e) {
85306
85701
  return { status: "failed", rcPath, reason: e.message };
85307
85702
  }
85308
- if (existsSync9(rcPath)) {
85703
+ if (existsSync12(rcPath)) {
85309
85704
  let st;
85310
85705
  try {
85311
85706
  st = lstatSync3(rcPath);
@@ -85319,8 +85714,8 @@ function appendAliasSourceToRc(rcPathRaw, sourceLine) {
85319
85714
  return { status: "failed", rcPath, reason: "rc path is not a regular file" };
85320
85715
  }
85321
85716
  try {
85322
- const existing = readFileSync9(rcPath, "utf-8");
85323
- if (existing.includes(aliasFile()) || existing.includes(".dependency-guardian/aliases.sh") || existing.includes(sourceLine.trim())) {
85717
+ const existing = readFileSync11(rcPath, "utf-8");
85718
+ if (existing.includes(RC_SENTINEL_OPEN) || existing.includes(aliasFile()) || existing.includes(legacyAliasPath()) || existing.includes(sourceLine.trim())) {
85324
85719
  return { status: "already-present", rcPath };
85325
85720
  }
85326
85721
  } catch (e) {
@@ -85329,8 +85724,9 @@ function appendAliasSourceToRc(rcPathRaw, sourceLine) {
85329
85724
  }
85330
85725
  try {
85331
85726
  const block = `
85332
- # Added by dg kitty \u2014 auto-scan via Dependency Guardian.
85727
+ ${RC_SENTINEL_OPEN}
85333
85728
  ${sourceLine}
85729
+ ${RC_SENTINEL_CLOSE}
85334
85730
  `;
85335
85731
  appendFileSync(rcPath, block, { encoding: "utf-8" });
85336
85732
  return { status: "appended", rcPath };
@@ -85363,12 +85759,14 @@ function aliasSnippetExists() {
85363
85759
  }
85364
85760
  function aliasSnippetSourced() {
85365
85761
  const candidates = [".zshrc", ".bashrc", ".bash_profile", ".profile"];
85762
+ const aliasFilePath = aliasFile();
85763
+ const legacyAlias = legacyAliasPath();
85366
85764
  for (const rc of candidates) {
85367
- const path3 = join12(homedir5(), rc);
85765
+ const path3 = join11(homedir3(), rc);
85368
85766
  if (!safeIsRegularFile(path3)) continue;
85369
85767
  try {
85370
- const content = readFileSync9(path3, "utf-8");
85371
- if (content.includes(aliasFile()) || content.includes(".dependency-guardian/aliases.sh")) {
85768
+ const content = readFileSync11(path3, "utf-8");
85769
+ if (content.includes(RC_SENTINEL_OPEN) || content.includes(aliasFilePath) || content.includes(legacyAlias) || content.includes(".dependency-guardian/aliases.sh")) {
85372
85770
  return { sourced: true, rcFile: path3 };
85373
85771
  }
85374
85772
  } catch {
@@ -85379,33 +85777,51 @@ function aliasSnippetSourced() {
85379
85777
  function stripAliasSourceFromRc() {
85380
85778
  const candidates = [".zshrc", ".bashrc", ".bash_profile", ".profile"];
85381
85779
  const modified = [];
85780
+ const manualReviewNeeded = [];
85382
85781
  const aliasFilePath = aliasFile();
85782
+ const legacyAlias = legacyAliasPath();
85383
85783
  for (const rc of candidates) {
85384
- const path3 = join12(homedir5(), rc);
85784
+ const path3 = join11(homedir3(), rc);
85385
85785
  if (!safeIsRegularFile(path3)) continue;
85386
85786
  let content;
85387
85787
  try {
85388
- content = readFileSync9(path3, "utf-8");
85788
+ content = readFileSync11(path3, "utf-8");
85389
85789
  } catch {
85390
85790
  continue;
85391
85791
  }
85392
- if (!content.includes(aliasFilePath) && !content.includes(".dependency-guardian/aliases.sh")) {
85792
+ const hasOpen = content.includes(RC_SENTINEL_OPEN);
85793
+ const hasClose = content.includes(RC_SENTINEL_CLOSE);
85794
+ const hasPath = content.includes(aliasFilePath) || content.includes(legacyAlias) || content.includes(".dependency-guardian/aliases.sh");
85795
+ if (!hasOpen && !hasPath) continue;
85796
+ if (hasOpen && !hasClose) {
85797
+ manualReviewNeeded.push(path3);
85393
85798
  continue;
85394
85799
  }
85395
- const lines = content.split("\n");
85396
- const out = [];
85397
- for (let i = 0; i < lines.length; i++) {
85398
- const line = lines[i];
85399
- const isSourceLine = line.includes(aliasFilePath) || line.includes(".dependency-guardian/aliases.sh");
85400
- if (isSourceLine && (line.trimStart().startsWith("source ") || line.trimStart().startsWith(". "))) {
85401
- if (out.length > 0 && out[out.length - 1].trim().startsWith("# Added by dg kitty")) {
85402
- out.pop();
85800
+ let next = content;
85801
+ if (hasOpen && hasClose) {
85802
+ const pattern = new RegExp(
85803
+ `\\n?${escapeRegex(RC_SENTINEL_OPEN)}[\\s\\S]*?${escapeRegex(RC_SENTINEL_CLOSE)}\\n?`,
85804
+ "g"
85805
+ );
85806
+ next = next.replace(pattern, "\n");
85807
+ }
85808
+ if (hasPath) {
85809
+ const lines = next.split("\n");
85810
+ const out = [];
85811
+ for (let i = 0; i < lines.length; i++) {
85812
+ const line = lines[i];
85813
+ const isSourceLine = line.includes(aliasFilePath) || line.includes(legacyAlias) || line.includes(".dependency-guardian/aliases.sh");
85814
+ if (isSourceLine && (line.trimStart().startsWith("source ") || line.trimStart().startsWith(". "))) {
85815
+ if (out.length > 0 && out[out.length - 1].trim().startsWith("# Added by dg kitty")) {
85816
+ out.pop();
85817
+ }
85818
+ continue;
85403
85819
  }
85404
- continue;
85820
+ out.push(line);
85405
85821
  }
85406
- out.push(line);
85822
+ next = out.join("\n");
85407
85823
  }
85408
- const next = out.join("\n");
85824
+ next = next.replace(/\n{3,}/g, "\n\n");
85409
85825
  if (next === content) continue;
85410
85826
  try {
85411
85827
  safeWriteFileSync2(path3, next, 420);
@@ -85413,7 +85829,10 @@ function stripAliasSourceFromRc() {
85413
85829
  } catch {
85414
85830
  }
85415
85831
  }
85416
- return modified;
85832
+ return { modified, manualReviewNeeded };
85833
+ }
85834
+ function escapeRegex(s) {
85835
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
85417
85836
  }
85418
85837
  async function runProtectInit(cwd2, opts = {}) {
85419
85838
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -85441,7 +85860,7 @@ async function runProtectInit(cwd2, opts = {}) {
85441
85860
  }
85442
85861
  process.stderr.write("\n");
85443
85862
  process.stderr.write(import_chalk4.default.green(" \u2713 ") + import_chalk4.default.bold("Dependency Guardian protection: ENABLED") + "\n");
85444
- process.stderr.write(import_chalk4.default.dim(" Stored in ~/.dgrc.json.\n"));
85863
+ process.stderr.write(import_chalk4.default.dim(" Stored under ~/.dg/.\n"));
85445
85864
  process.stderr.write(import_chalk4.default.dim(` Mode: ${cfg.mode}${cfg.strict ? " \xB7 strict" : ""}
85446
85865
  `));
85447
85866
  process.stderr.write("\n");
@@ -85475,7 +85894,7 @@ async function runProtectOff(cwd2, opts = {}) {
85475
85894
  if (getProtectConfig()) {
85476
85895
  try {
85477
85896
  clearProtectConfig();
85478
- process.stderr.write(import_chalk4.default.green(" \u2713 ") + "Cleared protect config in ~/.dgrc.json\n");
85897
+ process.stderr.write(import_chalk4.default.green(" \u2713 ") + "Cleared protect config\n");
85479
85898
  removed += 1;
85480
85899
  try {
85481
85900
  const { pingSetupEvent: pingSetupEvent2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
@@ -85489,21 +85908,10 @@ async function runProtectOff(cwd2, opts = {}) {
85489
85908
  } else {
85490
85909
  process.stderr.write(import_chalk4.default.dim(" Protect config already cleared.\n"));
85491
85910
  }
85492
- const legacyPath = join12(cwd2, LEGACY_PROJECT_FILE);
85493
- if (safeIsRegularFile(legacyPath)) {
85494
- try {
85495
- unlinkSync3(legacyPath);
85496
- process.stderr.write(import_chalk4.default.green(" \u2713 ") + `Removed legacy ${legacyPath}
85497
- `);
85498
- removed += 1;
85499
- } catch (e) {
85500
- process.stderr.write(import_chalk4.default.red(` Could not remove ${legacyPath}: ${e.message}
85501
- `));
85502
- }
85503
- }
85911
+ void cwd2;
85504
85912
  if (opts.removeShell && aliasSnippetExists()) {
85505
85913
  try {
85506
- unlinkSync3(aliasFile());
85914
+ unlinkSync5(aliasFile());
85507
85915
  process.stderr.write(import_chalk4.default.green(" \u2713 ") + `Removed ${aliasFile()}
85508
85916
  `);
85509
85917
  removed += 1;
@@ -85511,12 +85919,18 @@ async function runProtectOff(cwd2, opts = {}) {
85511
85919
  process.stderr.write(import_chalk4.default.red(` Could not remove ${aliasFile()}: ${e.message}
85512
85920
  `));
85513
85921
  }
85514
- const stripped = stripAliasSourceFromRc();
85515
- for (const p of stripped) {
85922
+ const stripResult = stripAliasSourceFromRc();
85923
+ for (const p of stripResult.modified) {
85516
85924
  process.stderr.write(import_chalk4.default.green(" \u2713 ") + `Removed source line from ${p}
85517
85925
  `);
85518
85926
  removed += 1;
85519
85927
  }
85928
+ for (const p of stripResult.manualReviewNeeded) {
85929
+ process.stderr.write(
85930
+ import_chalk4.default.yellow(" \u26A0 ") + `Could not safely strip ${p}: opening sentinel without matching close. Remove the dg block by hand.
85931
+ `
85932
+ );
85933
+ }
85520
85934
  } else if (aliasSnippetExists()) {
85521
85935
  process.stderr.write(
85522
85936
  import_chalk4.default.dim(` Shell snippet at ${aliasFile()} kept (other projects may use it).
@@ -85574,13 +85988,15 @@ function runInit(flags, cwd2) {
85574
85988
  }
85575
85989
  return runProtectInit(cwd2, opts);
85576
85990
  }
85577
- var import_chalk4, LEGACY_PROJECT_FILE, DEFAULT_CONFIG, ALIAS_SNIPPET, USAGE3;
85991
+ var import_chalk4, RC_SENTINEL_OPEN, RC_SENTINEL_CLOSE, DEFAULT_CONFIG, ALIAS_SNIPPET, USAGE3;
85578
85992
  var init_protect = __esm({
85579
85993
  "src/commands/protect.ts"() {
85580
85994
  "use strict";
85581
85995
  import_chalk4 = __toESM(require_source());
85582
85996
  init_auth();
85583
- LEGACY_PROJECT_FILE = ".dgprotect.json";
85997
+ init_paths();
85998
+ RC_SENTINEL_OPEN = "# >>> dependency-guardian (managed) >>>";
85999
+ RC_SENTINEL_CLOSE = "# <<< dependency-guardian (managed) <<<";
85584
86000
  DEFAULT_CONFIG = {
85585
86001
  version: 1,
85586
86002
  enabled: true,
@@ -85589,7 +86005,7 @@ var init_protect = __esm({
85589
86005
  };
85590
86006
  ALIAS_SNIPPET = `# Dependency Guardian \u2014 opt-in shell aliases
85591
86007
  # Source this from your shell rc to route npm/pip through dg:
85592
- # echo 'source ~/.dependency-guardian/aliases.sh' >> ~/.zshrc # or ~/.bashrc
86008
+ # echo 'source ~/.dg/aliases.sh' >> ~/.zshrc # or ~/.bashrc
85593
86009
  # To turn it off, remove that line.
85594
86010
 
85595
86011
  # Only alias if a real dg binary is on PATH.
@@ -85616,7 +86032,7 @@ unset __dg_bin
85616
86032
 
85617
86033
  Usage:
85618
86034
  dg protect Enable protection (same as \`dg protect init\`)
85619
- dg protect off Disable protection in this project
86035
+ dg protect off Disable protection
85620
86036
 
85621
86037
  Flags (for init):
85622
86038
  --no-shell Skip generating the shell-alias snippet
@@ -85624,10 +86040,10 @@ unset __dg_bin
85624
86040
  --strict Refuse partial-coverage scans + suppress scripts
85625
86041
 
85626
86042
  What \`dg protect\` does:
85627
- 1. Writes \`.dgprotect.json\` in the project (commit it).
85628
- 2. Generates an opt-in alias snippet at
85629
- \`~/.dependency-guardian/aliases.sh\` that you can \`source\` from your
85630
- shell rc. We do NOT touch your rc ourselves.
86043
+ 1. Stores protection settings under ~/.dg/.
86044
+ 2. Generates a shell-alias snippet at
86045
+ \`~/.dg/state/aliases.sh\` that you can \`source\`
86046
+ from your shell rc. We do NOT touch your rc ourselves.
85631
86047
  3. Tells you exactly what line to add (and how to undo it).
85632
86048
 
85633
86049
  After protection is on, \`npm install\` and \`pip install\` route through
@@ -85635,7 +86051,7 @@ unset __dg_bin
85635
86051
 
85636
86052
  To see current coverage status, run \`dg status\`.
85637
86053
  To disable, run \`dg protect off\`. (Pass --remove-shell to also delete
85638
- the per-user alias snippet.)
86054
+ the alias snippet and strip the source line from your shell rc.)
85639
86055
 
85640
86056
  Why this is opt-in:
85641
86057
  DG never silently hijacks global npm/pip. Every change is explicit
@@ -85645,8 +86061,8 @@ unset __dg_bin
85645
86061
  });
85646
86062
 
85647
86063
  // src/ui/hooks/useInit.ts
85648
- import { statSync as statSync2 } from "node:fs";
85649
- import { join as join13 } from "node:path";
86064
+ import { statSync as statSync3 } from "node:fs";
86065
+ import { join as join12 } from "node:path";
85650
86066
  function initialState(dryRun, firstRun) {
85651
86067
  return {
85652
86068
  phase: firstRun ? "greet" : "detect",
@@ -85677,7 +86093,7 @@ function initialState(dryRun, firstRun) {
85677
86093
  function detectCwdIsProject(cwd2) {
85678
86094
  for (const marker of PROJECT_MARKERS) {
85679
86095
  try {
85680
- const stat = statSync2(join13(cwd2, marker));
86096
+ const stat = statSync3(join12(cwd2, marker));
85681
86097
  if (stat.isFile()) return true;
85682
86098
  } catch {
85683
86099
  }
@@ -85690,7 +86106,6 @@ function reducer(state, action) {
85690
86106
  `);
85691
86107
  }
85692
86108
  switch (action.type) {
85693
- // ── First-run guided tour transitions ──────────────────────────────────
85694
86109
  case "welcome_yes":
85695
86110
  return { ...state, phase: "value_prop", engaged: true };
85696
86111
  case "welcome_no": {
@@ -85770,6 +86185,9 @@ function reducer(state, action) {
85770
86185
  if (!state.protectInfo) {
85771
86186
  return { ...state, phase: "pitch_app" };
85772
86187
  }
86188
+ if (state.protectInfo.alreadySourced) {
86189
+ return { ...state, phase: "pitch_app" };
86190
+ }
85773
86191
  return { ...state, phase: "optional_protection" };
85774
86192
  case "optional_protection_yes":
85775
86193
  return { ...state, phase: "optional_protection_result" };
@@ -85784,7 +86202,6 @@ function reducer(state, action) {
85784
86202
  return { ...state, phase: "pitch_app" };
85785
86203
  case "pitch_acked":
85786
86204
  return { ...state, phase: "done" };
85787
- // ── Existing Phase 1b transitions (unchanged) ──────────────────────────
85788
86205
  case "detected":
85789
86206
  return {
85790
86207
  ...state,
@@ -85872,9 +86289,7 @@ function goBack(state) {
85872
86289
  value_prop: "greet",
85873
86290
  demo_result: "value_prop",
85874
86291
  confirm_wrap: "verify_hook",
85875
- // legacy only
85876
86292
  show_app_link: "confirm_scan"
85877
- // legacy only
85878
86293
  };
85879
86294
  if (state.phase === "confirm_hook") {
85880
86295
  return { ...state, phase: state.firstRun ? "scenario" : "detect" };
@@ -85929,7 +86344,7 @@ function useInit(opts = {}) {
85929
86344
  dispatch({
85930
86345
  type: "hook_installed",
85931
86346
  protectInfo: {
85932
- rcLine: "source ~/.dependency-guardian/aliases.sh",
86347
+ rcLine: "source ~/.dg/state/aliases.sh",
85933
86348
  rcFile: "~/.zshrc",
85934
86349
  shellName: "zsh",
85935
86350
  alreadySourced: false
@@ -85943,8 +86358,15 @@ function useInit(opts = {}) {
85943
86358
  let protectInfo;
85944
86359
  try {
85945
86360
  const { rcLine, rcFile, shellName, alreadySourced } = setupProtectQuietly(cwd2);
86361
+ let sourced = alreadySourced;
86362
+ if (!alreadySourced) {
86363
+ const out = appendAliasSourceToRc(rcFile, rcLine);
86364
+ if (out.status === "appended" || out.status === "already-present") {
86365
+ sourced = true;
86366
+ }
86367
+ }
85946
86368
  void Promise.resolve().then(() => (init_auth(), auth_exports)).then((m) => m.enqueueSetupEvent("protect_enabled"));
85947
- protectInfo = { rcLine, rcFile, shellName, alreadySourced };
86369
+ protectInfo = { rcLine, rcFile, shellName, alreadySourced: sourced };
85948
86370
  } catch {
85949
86371
  }
85950
86372
  dispatch({ type: "hook_installed", protectInfo });
@@ -85960,26 +86382,25 @@ function useInit(opts = {}) {
85960
86382
  dispatch({ type: "hook_verified", durationMs: 0 });
85961
86383
  return;
85962
86384
  }
86385
+ if (!state.hookInfo) {
86386
+ dispatch({ type: "hook_verify_failed", error: "hook framework not detected" });
86387
+ return;
86388
+ }
86389
+ const t0 = Date.now();
85963
86390
  try {
85964
- const cwd3 = opts.cwd ?? process.cwd();
85965
- const config3 = parseConfig([process.argv[0] ?? "node", "init"], false);
85966
- const outcome = await scanFn(cwd3, config3);
85967
- if (outcome.status === "error" && outcome.error) {
85968
- dispatch({
85969
- type: "hook_verify_failed",
85970
- error: outcome.message ?? outcome.error.message
85971
- });
86391
+ const result = verifyHookInstalled(state.hookInfo);
86392
+ if (!result.ok) {
86393
+ dispatch({ type: "hook_verify_failed", error: result.reason ?? "verification failed" });
85972
86394
  return;
85973
86395
  }
85974
- const ms = outcome.result?.durationMs ?? 0;
85975
- dispatch({ type: "hook_verified", durationMs: ms });
86396
+ dispatch({ type: "hook_verified", durationMs: Date.now() - t0 });
85976
86397
  } catch (e) {
85977
86398
  dispatch({
85978
86399
  type: "hook_verify_failed",
85979
86400
  error: e instanceof Error ? e.message : String(e)
85980
86401
  });
85981
86402
  }
85982
- }, [state.dryRun, opts.cwd, scanFn]);
86403
+ }, [state.dryRun, state.hookInfo]);
85983
86404
  const runScan = (0, import_react22.useCallback)(async () => {
85984
86405
  if (state.dryRun) {
85985
86406
  dispatch({
@@ -86001,11 +86422,23 @@ function useInit(opts = {}) {
86001
86422
  const config3 = parseConfig([process.argv[0] ?? "node", "init", "--scan-all"], false);
86002
86423
  const projects = state.projects.length > 0 ? state.projects : [{ path: opts.cwd ?? process.cwd() }];
86003
86424
  const allOutcomes = [];
86004
- for (const proj of projects) {
86425
+ const perProject = /* @__PURE__ */ new Map();
86426
+ const aggregate = (current) => {
86427
+ let done = 0;
86428
+ let total = 0;
86429
+ for (const v of perProject.values()) {
86430
+ done += v.done;
86431
+ total += v.total;
86432
+ }
86433
+ return { done, total, current };
86434
+ };
86435
+ for (let i = 0; i < projects.length; i++) {
86436
+ const proj = projects[i];
86005
86437
  if (process.env.DG_DEBUG_WIZARD) process.stderr.write(`[wizard] scanning ${proj.path}
86006
86438
  `);
86007
86439
  const outcome = await scanFn(proj.path, config3, (p) => {
86008
- dispatch({ type: "scan_progress", progress: p });
86440
+ perProject.set(i, { done: p.done, total: p.total });
86441
+ dispatch({ type: "scan_progress", progress: aggregate(p.current) });
86009
86442
  });
86010
86443
  if (process.env.DG_DEBUG_WIZARD) process.stderr.write(`[wizard] scan done ${proj.path} status=${outcome.status}
86011
86444
  `);
@@ -86029,18 +86462,26 @@ function useInit(opts = {}) {
86029
86462
  const maxScore = allPackages.length > 0 ? Math.max(0, ...allPackages.map((p) => p.score)) : 0;
86030
86463
  const action = maxScore >= 70 ? "block" : maxScore >= 60 ? "warn" : "pass";
86031
86464
  if (totalScanned === 0) {
86032
- dispatch({
86033
- type: "scan_done",
86034
- outcome: {
86035
- status: "no_packages",
86036
- result: {
86037
- result: { score: 0, action: "pass", packages: [], safeVersions: {}, durationMs: totalDuration },
86038
- durationMs: totalDuration,
86039
- scannedCount: 0,
86040
- skippedCount: 0
86465
+ const errOutcome = allOutcomes.find((o) => o.status === "error");
86466
+ const emptyApiOutcome = allOutcomes.find((o) => o.status === "api_returned_empty");
86467
+ const trialOutcome = allOutcomes.find((o) => o.status === "trial_exhausted");
86468
+ const meaningful = errOutcome ?? emptyApiOutcome ?? trialOutcome;
86469
+ if (meaningful) {
86470
+ dispatch({ type: "scan_done", outcome: meaningful });
86471
+ } else {
86472
+ dispatch({
86473
+ type: "scan_done",
86474
+ outcome: {
86475
+ status: "no_packages",
86476
+ result: {
86477
+ result: { score: 0, action: "pass", packages: [], safeVersions: {}, durationMs: totalDuration },
86478
+ durationMs: totalDuration,
86479
+ scannedCount: 0,
86480
+ skippedCount: 0
86481
+ }
86041
86482
  }
86042
- }
86043
- });
86483
+ });
86484
+ }
86044
86485
  } else {
86045
86486
  dispatch({
86046
86487
  type: "scan_done",
@@ -86248,8 +86689,8 @@ __export(ink_controls_exports, {
86248
86689
  setInkClear: () => setInkClear,
86249
86690
  setInkInstance: () => setInkInstance
86250
86691
  });
86251
- import { resolve as resolve2, dirname as dirname7 } from "node:path";
86252
- import { existsSync as existsSync10 } from "node:fs";
86692
+ import { resolve as resolve2, dirname as dirname11 } from "node:path";
86693
+ import { existsSync as existsSync13 } from "node:fs";
86253
86694
  function locateInkInstancesPath() {
86254
86695
  let cur;
86255
86696
  try {
@@ -86259,13 +86700,13 @@ function locateInkInstancesPath() {
86259
86700
  }
86260
86701
  while (cur && cur !== "/" && cur !== ".") {
86261
86702
  const candidate = resolve2(cur, "node_modules", "ink", "build", "instances.js");
86262
- if (existsSync10(candidate)) return candidate;
86263
- const parent = dirname7(cur);
86703
+ if (existsSync13(candidate)) return candidate;
86704
+ const parent = dirname11(cur);
86264
86705
  if (parent === cur) break;
86265
86706
  cur = parent;
86266
86707
  }
86267
86708
  const cwdCandidate = resolve2(process.cwd(), "node_modules", "ink", "build", "instances.js");
86268
- if (existsSync10(cwdCandidate)) return cwdCandidate;
86709
+ if (existsSync13(cwdCandidate)) return cwdCandidate;
86269
86710
  return null;
86270
86711
  }
86271
86712
  async function initInkInstances() {
@@ -89084,7 +89525,16 @@ var init_demo_data = __esm({
89084
89525
  "Postinstall Curl Pipe": "Downloads code and runs it",
89085
89526
  "Suspicious Network": "Talks to suspicious servers",
89086
89527
  "Typosquatting": "Pretending to be a popular package",
89087
- "Sandbox Escape": "Tries to break out of safety checks"
89528
+ "Sandbox Escape": "Tries to break out of safety checks",
89529
+ "cross-signal-correlation": "Multiple detector signals correlated",
89530
+ "lifecycle-risk": "Install-time script risk",
89531
+ "runtime-behavior": "Suspicious runtime behavior",
89532
+ "data-exfiltration": "Sends data off-machine",
89533
+ "code-quality": "Obfuscated or low-quality code",
89534
+ "supply-chain-metadata": "Suspicious package metadata",
89535
+ "filesystem-risk": "Reads sensitive paths",
89536
+ "behavioral-anomaly": "Unusual behavior shape",
89537
+ "license-compliance": "License risk"
89088
89538
  };
89089
89539
  }
89090
89540
  });
@@ -89122,6 +89572,12 @@ var init_ScanResultCard = __esm({
89122
89572
  score = 0;
89123
89573
  packages = [];
89124
89574
  extraNote = "Nothing to scan yet. I'll catch new packages as you add them.";
89575
+ } else if (o.status === "api_returned_empty") {
89576
+ verdict = "EMPTY";
89577
+ verdictColor = "yellow";
89578
+ score = 0;
89579
+ packages = [];
89580
+ extraNote = o.message ?? "Analyze API returned no results for discovered packages.";
89125
89581
  } else if (o.status === "trial_exhausted") {
89126
89582
  verdict = "EMPTY";
89127
89583
  verdictColor = "yellow";
@@ -89160,15 +89616,17 @@ var init_ScanResultCard = __esm({
89160
89616
  packages.length === 1 ? packages[0].findings.slice(0, 3).map((f, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { dimColor: true, children: [
89161
89617
  " \u2022 ",
89162
89618
  friendlyCategory(f.category),
89163
- f.title ? ` \u2014 ${f.title}` : ""
89164
- ] }, `f-${i}`)) : packages.slice(0, 3).map((pkg) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { dimColor: true, children: [
89619
+ f.title && f.title !== f.category ? ` \u2014 ${f.title}` : ""
89620
+ ] }, `f-${i}`)) : [...packages].sort((a, b) => b.score - a.score).slice(0, 3).map((pkg) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { dimColor: true, children: [
89165
89621
  " \u2022 ",
89166
89622
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { bold: true, children: pkg.name }),
89167
89623
  " ",
89168
89624
  pkg.score,
89169
- "/100"
89625
+ "/100",
89626
+ pkg.findings && pkg.findings[0] ? ` \xB7 ${friendlyCategory(pkg.findings[0].category)}` : ""
89170
89627
  ] }, pkg.name)),
89171
- packages.length > 3 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: " + " + (packages.length - 3) + " more" }) : null
89628
+ packages.length > 3 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: " + " + (packages.length - 3) + " more" }) : null,
89629
+ packages.length >= 1 && packages.some((p) => p.findings && p.findings.length > 0 && !p.findings[0].title) ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: " Sign in with `dg login` for full finding details." }) : null
89172
89630
  ] }) : verdict !== "EMPTY" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { children: [
89173
89631
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: verdictColor, bold: true, children: verdict }),
89174
89632
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: " \xB7 " }),
@@ -89388,7 +89846,7 @@ var init_InitApp = __esm({
89388
89846
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Spinner2, { label: "Creating login session..." });
89389
89847
  case "ready":
89390
89848
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { flexDirection: "column", children: [
89391
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, children: "Sign in to Dependency Guardian" }),
89849
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, children: "Sign in to WestBayBerry Dependency Guardian" }),
89392
89850
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: " " }),
89393
89851
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: "Authenticate your account at:" }),
89394
89852
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "cyan", children: state.verifyUrl }),
@@ -89397,7 +89855,7 @@ var init_InitApp = __esm({
89397
89855
  ] });
89398
89856
  case "waiting":
89399
89857
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { flexDirection: "column", children: [
89400
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, children: "Sign in to Dependency Guardian" }),
89858
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, children: "Sign in to WestBayBerry Dependency Guardian" }),
89401
89859
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: " " }),
89402
89860
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { dimColor: true, children: state.verifyUrl }),
89403
89861
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: " " }),
@@ -89409,7 +89867,7 @@ var init_InitApp = __esm({
89409
89867
  "\u2713 Signed in as ",
89410
89868
  state.email
89411
89869
  ] }),
89412
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { dimColor: true, children: "Credentials saved to ~/.dgrc.json" })
89870
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { dimColor: true, children: "Credentials saved to ~/.dg/config.json" })
89413
89871
  ] });
89414
89872
  case "already_logged_in":
89415
89873
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { flexDirection: "column", children: [
@@ -89874,6 +90332,7 @@ var init_InitApp = __esm({
89874
90332
  return "happy";
89875
90333
  }
89876
90334
  if (r.status === "no_packages") return "happy";
90335
+ if (r.status === "api_returned_empty") return "alert";
89877
90336
  return "idle";
89878
90337
  })();
89879
90338
  const content = (() => {
@@ -90078,7 +90537,7 @@ var init_InitApp = __esm({
90078
90537
  ] }, "p1a"),
90079
90538
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Text, { children: [
90080
90539
  installCmd,
90081
- " them. That's where most attacks land."
90540
+ " them. That's where most attacks happen."
90082
90541
  ] }, "p1b"),
90083
90542
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: " " }, "sp2"),
90084
90543
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FlowDiagram, {}, "diagram"),
@@ -90206,7 +90665,7 @@ var init_InitApp = __esm({
90206
90665
  mood: "scanning",
90207
90666
  color: "cyan",
90208
90667
  scrollable: false,
90209
- body: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Spinner2, { label: "Testing it..." })
90668
+ body: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Spinner2, { label: "Verifying the hook..." })
90210
90669
  }
90211
90670
  );
90212
90671
  case "confirm_wrap": {
@@ -90284,6 +90743,7 @@ var init_InitApp = __esm({
90284
90743
  const r = state.scanResult;
90285
90744
  const okScan = r?.status === "ok" && r.result;
90286
90745
  const empty = r?.status === "no_packages";
90746
+ const apiEmpty = r?.status === "api_returned_empty";
90287
90747
  const resultBody = [
90288
90748
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, children: "Scan complete." }, "hdr"),
90289
90749
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: " " }, "sp1"),
@@ -90291,6 +90751,10 @@ var init_InitApp = __esm({
90291
90751
  "I scanned ",
90292
90752
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, color: "cyan", children: projectName }),
90293
90753
  "."
90754
+ ] }, "line") : apiEmpty ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Text, { children: [
90755
+ "I found packages in ",
90756
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, color: "cyan", children: projectName }),
90757
+ " but the analyze API returned nothing back. This is a server-side problem, not your project."
90294
90758
  ] }, "line") : empty ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Text, { children: [
90295
90759
  "I didn't find any packages to scan in ",
90296
90760
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, color: "cyan", children: projectName }),
@@ -90302,6 +90766,13 @@ var init_InitApp = __esm({
90302
90766
  ] }, "line"),
90303
90767
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: " " }, "sp2")
90304
90768
  ];
90769
+ if (apiEmpty && r?.message) {
90770
+ resultBody.push(
90771
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "yellow", children: r.message }, "api-empty-detail")
90772
+ );
90773
+ resultBody.push(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { dimColor: true, children: "Check westbayberry.com/status and re-run." }, "api-empty-hint"));
90774
+ resultBody.push(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: " " }, "api-empty-sp"));
90775
+ }
90305
90776
  if (state.scanResult) {
90306
90777
  resultBody.push(
90307
90778
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ScanResultCard, { maxWidth: cardMaxWidth, outcome: { kind: "real", outcome: state.scanResult } }, "card")
@@ -90415,7 +90886,7 @@ var init_InitApp = __esm({
90415
90886
  );
90416
90887
  case "optional_protection": {
90417
90888
  const p = state.protectInfo;
90418
- const rcLine = p?.rcLine ?? "source ~/.dependency-guardian/aliases.sh";
90889
+ const rcLine = p?.rcLine ?? "source ~/.dg/state/aliases.sh";
90419
90890
  const rcFile = p?.rcFile ?? "~/.zshrc";
90420
90891
  const shellLabel = p?.shellName && p.shellName !== "your shell rc" ? p.shellName : null;
90421
90892
  const stackBoth = state.stackNpm && state.stackPypi;
@@ -90554,7 +91025,7 @@ var init_InitApp = __esm({
90554
91025
  );
90555
91026
  }
90556
91027
  if (r.kind === "failed") {
90557
- const rcLine = state.protectInfo?.rcLine ?? "source ~/.dependency-guardian/aliases.sh";
91028
+ const rcLine = state.protectInfo?.rcLine ?? "source ~/.dg/state/aliases.sh";
90558
91029
  const rcFile = state.protectInfo?.rcFile ?? "~/.zshrc";
90559
91030
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
90560
91031
  Scene,
@@ -90644,6 +91115,8 @@ var init_InitApp = __esm({
90644
91115
  scan.result?.durationMs ?? 0,
90645
91116
  "ms)"
90646
91117
  ] }, "scan-empty"));
91118
+ } else if (scan.status === "api_returned_empty") {
91119
+ scanLine.push(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "yellow", children: "\u26A0 Analyze API returned no results \u2014 try again or check status" }, "scan-api-empty"));
90647
91120
  } else if (scan.status === "trial_exhausted") {
90648
91121
  scanLine.push(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "yellow", children: "\u26A0 Free trial scans used up \u2014 `dg login` to continue" }, "scan-trial"));
90649
91122
  } else if (scan.status === "error") {
@@ -90887,7 +91360,14 @@ var init_first_run = __esm({
90887
91360
  "status",
90888
91361
  "protect",
90889
91362
  "login",
90890
- "cleanup"
91363
+ "cleanup",
91364
+ "uninstall",
91365
+ "init",
91366
+ "scan",
91367
+ "npm",
91368
+ "pip",
91369
+ "wrap",
91370
+ "publish-check"
90891
91371
  ]);
90892
91372
  }
90893
91373
  });
@@ -91089,7 +91569,7 @@ __export(pip_wrapper_exports, {
91089
91569
  runPip: () => runPip
91090
91570
  });
91091
91571
  import { spawn as spawn3 } from "node:child_process";
91092
- import { existsSync as existsSync11 } from "node:fs";
91572
+ import { existsSync as existsSync14 } from "node:fs";
91093
91573
  function parsePipArgs(args) {
91094
91574
  let dgForce = false;
91095
91575
  let dgForceReason;
@@ -91188,7 +91668,7 @@ function parseRequirementsFile(filePath) {
91188
91668
  return parseRequirementsFileInternal(filePath, /* @__PURE__ */ new Set());
91189
91669
  }
91190
91670
  function parseRequirementsFileInternal(filePath, visited) {
91191
- if (!existsSync11(filePath)) return [];
91671
+ if (!existsSync14(filePath)) return [];
91192
91672
  let absPath;
91193
91673
  try {
91194
91674
  absPath = __require("node:path").resolve(filePath);
@@ -91506,7 +91986,7 @@ var init_pip_wrapper = __esm({
91506
91986
  });
91507
91987
 
91508
91988
  // src/security/artifact_integrity.ts
91509
- function normalize2(h, algo) {
91989
+ function normalize3(h, algo) {
91510
91990
  if (!h) return void 0;
91511
91991
  const trimmed = h.trim();
91512
91992
  const re2 = algo === "sha512" ? SHA512_HEX : SHA256_HEX;
@@ -91523,8 +92003,8 @@ function compareArtifactHashes(pairs) {
91523
92003
  };
91524
92004
  for (const p of pairs) {
91525
92005
  const key = `${p.name}@${p.version}`;
91526
- const api = normalize2(p.apiHash, p.algorithm);
91527
- const local = normalize2(p.localHash, p.algorithm);
92006
+ const api = normalize3(p.apiHash, p.algorithm);
92007
+ const local = normalize3(p.localHash, p.algorithm);
91528
92008
  const apiRaw = (p.apiHash ?? "").toString();
91529
92009
  const localRaw = (p.localHash ?? "").toString();
91530
92010
  const apiPresent = apiRaw.length > 0;
@@ -91650,8 +92130,8 @@ var FileSavePrompt_exports = {};
91650
92130
  __export(FileSavePrompt_exports, {
91651
92131
  FileSavePrompt: () => FileSavePrompt
91652
92132
  });
91653
- import { existsSync as existsSync12, readdirSync, writeFileSync as writeFileSync6 } from "node:fs";
91654
- import { dirname as dirname8, join as join14 } from "node:path";
92133
+ import { existsSync as existsSync15, readdirSync, writeFileSync as writeFileSync8 } from "node:fs";
92134
+ import { dirname as dirname12, join as join13 } from "node:path";
91655
92135
  function listDirectory(dir) {
91656
92136
  try {
91657
92137
  const raw = readdirSync(dir, { withFileTypes: true });
@@ -91659,7 +92139,7 @@ function listDirectory(dir) {
91659
92139
  if (a.isDirectory !== b.isDirectory) return a.isDirectory ? -1 : 1;
91660
92140
  return a.name.localeCompare(b.name);
91661
92141
  });
91662
- if (dirname8(dir) !== dir) {
92142
+ if (dirname12(dir) !== dir) {
91663
92143
  entries.unshift({ name: "..", isDirectory: true });
91664
92144
  }
91665
92145
  return entries;
@@ -91751,13 +92231,13 @@ var init_FileSavePrompt = __esm({
91751
92231
  if (state.focus === "filename") {
91752
92232
  if (key.return) {
91753
92233
  const fullName = ensureJsonExtension(state.filename);
91754
- const fullPath = join14(state.directory, fullName);
91755
- if (!state.confirmOverwrite && existsSync12(fullPath)) {
92234
+ const fullPath = join13(state.directory, fullName);
92235
+ if (!state.confirmOverwrite && existsSync15(fullPath)) {
91756
92236
  dispatch({ type: "CONFIRM_OVERWRITE" });
91757
92237
  return;
91758
92238
  }
91759
92239
  try {
91760
- writeFileSync6(fullPath, json + "\n");
92240
+ writeFileSync8(fullPath, json + "\n");
91761
92241
  } catch (err) {
91762
92242
  const msg = err instanceof Error ? err.message : String(err);
91763
92243
  dispatch({ type: "SET_ERROR", error: `Write failed: ${msg}` });
@@ -92009,25 +92489,24 @@ __export(static_output_exports, {
92009
92489
  runStaticNpm: () => runStaticNpm,
92010
92490
  runStaticPip: () => runStaticPip
92011
92491
  });
92012
- import { writeFileSync as writeFileSync7, readFileSync as readFileSync10, existsSync as existsSync13, mkdirSync as mkdirSync4, lstatSync as lstatSync4 } from "node:fs";
92013
- import { resolve as resolvePath, join as join15, dirname as dirname9 } from "node:path";
92014
- import { homedir as homedir6 } from "node:os";
92492
+ import { writeFileSync as writeFileSync9, readFileSync as readFileSync12, existsSync as existsSync16, mkdirSync as mkdirSync8, lstatSync as lstatSync4 } from "node:fs";
92493
+ import { resolve as resolvePath2, dirname as dirname13 } from "node:path";
92015
92494
  function loadPackageLockJson(cwd2 = process.cwd()) {
92016
- const path3 = resolvePath(cwd2, "package-lock.json");
92017
- if (!existsSync13(path3)) return null;
92495
+ const path3 = resolvePath2(cwd2, "package-lock.json");
92496
+ if (!existsSync16(path3)) return null;
92018
92497
  try {
92019
92498
  const st = lstatSync4(path3);
92020
92499
  if (!st.isFile()) return null;
92021
92500
  if (st.size > MAX_PACKAGE_LOCK_BYTES) return null;
92022
- return JSON.parse(readFileSync10(path3, "utf8"));
92501
+ return JSON.parse(readFileSync12(path3, "utf8"));
92023
92502
  } catch {
92024
92503
  return null;
92025
92504
  }
92026
92505
  }
92027
92506
  function writeJsonFile(filepath, json) {
92028
- const resolved = resolvePath(filepath);
92507
+ const resolved = resolvePath2(filepath);
92029
92508
  try {
92030
- writeFileSync7(resolved, json + "\n");
92509
+ writeFileSync9(resolved, json + "\n");
92031
92510
  process.stderr.write(import_chalk8.default.green(` \u2713 Saved to ${resolved}
92032
92511
 
92033
92512
  `));
@@ -92097,12 +92576,12 @@ function printTrialBanner(result, config3) {
92097
92576
  if (config3?.json || config3?.ci || config3?.quiet) return;
92098
92577
  if (process.env.CI === "1" || process.env.CI === "true") return;
92099
92578
  try {
92100
- const stampPath = join15(homedir6(), ".dependency-guardian", "last-trial-banner");
92101
- const lastMs = existsSync13(stampPath) ? Number(readFileSync10(stampPath, "utf-8").trim()) || 0 : 0;
92579
+ const stampPath = dgStatePath("trial-banner");
92580
+ const lastMs = existsSync16(stampPath) ? Number(readFileSync12(stampPath, "utf-8").trim()) || 0 : 0;
92102
92581
  const now = Date.now();
92103
92582
  if (now - lastMs < NUDGE_INTERVAL_MS) return;
92104
- mkdirSync4(dirname9(stampPath), { recursive: true });
92105
- writeFileSync7(stampPath, String(now) + "\n");
92583
+ mkdirSync8(dirname13(stampPath), { recursive: true, mode: 448 });
92584
+ writeFileSync9(stampPath, String(now) + "\n");
92106
92585
  } catch {
92107
92586
  return;
92108
92587
  }
@@ -93041,6 +93520,7 @@ var init_static_output = __esm({
93041
93520
  "src/formatters/static-output.ts"() {
93042
93521
  "use strict";
93043
93522
  import_chalk8 = __toESM(require_source());
93523
+ init_paths();
93044
93524
  init_client3();
93045
93525
  init_auth();
93046
93526
  init_lockfile();
@@ -93058,13 +93538,13 @@ __export(status_exports, {
93058
93538
  handleStatusCommand: () => handleStatusCommand
93059
93539
  });
93060
93540
  async function handleStatusCommand(cliVersion) {
93061
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
93541
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
93062
93542
  const apiKey = getStoredApiKey();
93063
93543
  process.stderr.write("\n");
93064
- process.stderr.write(chalk15.bold(" Dependency Guardian \u2014 coverage status\n\n"));
93544
+ process.stderr.write(chalk17.bold(" Dependency Guardian \u2014 coverage status\n\n"));
93065
93545
  if (!apiKey) {
93066
93546
  process.stderr.write(
93067
- ` ${chalk15.yellow("\xB7")} ${chalk15.bold("Auth")} ` + chalk15.dim("anonymous \u2014 `dg login` (free) for saved scan history, dashboard, and finding titles\n")
93547
+ ` ${chalk17.yellow("\xB7")} ${chalk17.bold("Auth")} ` + chalk17.dim("anonymous \u2014 `dg login` (free) for saved scan history, dashboard, and finding titles\n")
93068
93548
  );
93069
93549
  } else {
93070
93550
  let tierLine = "signed in";
@@ -93090,29 +93570,29 @@ async function handleStatusCommand(cliVersion) {
93090
93570
  } catch {
93091
93571
  tierLine = "signed in (could not reach API)";
93092
93572
  }
93093
- process.stderr.write(` ${chalk15.green("\u2713")} ${chalk15.bold("Auth")} ` + chalk15.dim(tierLine + "\n"));
93573
+ process.stderr.write(` ${chalk17.green("\u2713")} ${chalk17.bold("Auth")} ` + chalk17.dim(tierLine + "\n"));
93094
93574
  }
93095
93575
  const cwd2 = process.cwd();
93096
93576
  const protect = readProtectConfig(cwd2);
93097
93577
  if (protect && protect.enabled) {
93098
93578
  process.stderr.write(
93099
- ` ${chalk15.green("\u2713")} ${chalk15.bold("Protect")} ` + chalk15.dim(`enabled in this project (mode=${protect.mode}${protect.strict ? ", strict" : ""})
93579
+ ` ${chalk17.green("\u2713")} ${chalk17.bold("Protect")} ` + chalk17.dim(`enabled in this project (mode=${protect.mode}${protect.strict ? ", strict" : ""})
93100
93580
  `)
93101
93581
  );
93102
93582
  } else {
93103
93583
  process.stderr.write(
93104
- ` ${chalk15.yellow("\xB7")} ${chalk15.bold("Protect")} ` + chalk15.dim("not enabled in this project \u2014 run `dg protect init` for opt-in protection\n")
93584
+ ` ${chalk17.yellow("\xB7")} ${chalk17.bold("Protect")} ` + chalk17.dim("not enabled in this project \u2014 run `dg protect init` for opt-in protection\n")
93105
93585
  );
93106
93586
  }
93107
93587
  let hookLine = "no pre-commit hook \u2014 `dg hook install` to gate commits";
93108
93588
  let hookOk = false;
93109
93589
  try {
93110
- const { existsSync: existsSync16, readFileSync: readFileSync12 } = await import("node:fs");
93111
- const { join: join18 } = await import("node:path");
93112
- const candidates = [join18(cwd2, ".husky", "pre-commit"), join18(cwd2, ".git", "hooks", "pre-commit")];
93590
+ const { existsSync: existsSync21, readFileSync: readFileSync14 } = await import("node:fs");
93591
+ const { join: join16 } = await import("node:path");
93592
+ const candidates = [join16(cwd2, ".husky", "pre-commit"), join16(cwd2, ".git", "hooks", "pre-commit")];
93113
93593
  for (const path3 of candidates) {
93114
- if (existsSync16(path3)) {
93115
- const content = readFileSync12(path3, "utf-8");
93594
+ if (existsSync21(path3)) {
93595
+ const content = readFileSync14(path3, "utf-8");
93116
93596
  if (content.includes("dependency-guardian")) {
93117
93597
  hookLine = `installed in ${path3}`;
93118
93598
  hookOk = true;
@@ -93123,7 +93603,7 @@ async function handleStatusCommand(cliVersion) {
93123
93603
  } catch {
93124
93604
  }
93125
93605
  process.stderr.write(
93126
- ` ${hookOk ? chalk15.green("\u2713") : chalk15.yellow("\xB7")} ${chalk15.bold("Hook")} ` + chalk15.dim(hookLine + "\n")
93606
+ ` ${hookOk ? chalk17.green("\u2713") : chalk17.yellow("\xB7")} ${chalk17.bold("Hook")} ` + chalk17.dim(hookLine + "\n")
93127
93607
  );
93128
93608
  try {
93129
93609
  const banner = await checkForUpdate(cliVersion).catch(() => null);
@@ -93131,20 +93611,20 @@ async function handleStatusCommand(cliVersion) {
93131
93611
  const m = banner.match(/(\d+\.\d+\.\d+)\s+→\s+(\d+\.\d+\.\d+)/);
93132
93612
  const latest = m ? m[2] : "newer";
93133
93613
  process.stderr.write(
93134
- ` ${chalk15.yellow("!")} ${chalk15.bold("Update")} ` + chalk15.dim(`available \u2014 ${cliVersion} \u2192 ${latest} (run \`dg update\`)
93614
+ ` ${chalk17.yellow("!")} ${chalk17.bold("Update")} ` + chalk17.dim(`available \u2014 ${cliVersion} \u2192 ${latest} (run \`dg update\`)
93135
93615
  `)
93136
93616
  );
93137
93617
  } else {
93138
- process.stderr.write(` ${chalk15.green("\u2713")} ${chalk15.bold("Update")} ` + chalk15.dim(`on latest (${cliVersion})
93618
+ process.stderr.write(` ${chalk17.green("\u2713")} ${chalk17.bold("Update")} ` + chalk17.dim(`on latest (${cliVersion})
93139
93619
  `));
93140
93620
  }
93141
93621
  } catch {
93142
93622
  }
93143
93623
  try {
93144
- const { execFileSync: execFileSync4 } = await import("node:child_process");
93624
+ const { execFileSync: execFileSync5 } = await import("node:child_process");
93145
93625
  const npmV = (() => {
93146
93626
  try {
93147
- return execFileSync4("npm", ["--version"], { encoding: "utf-8", timeout: 5e3 }).trim();
93627
+ return execFileSync5("npm", ["--version"], { encoding: "utf-8", timeout: 5e3 }).trim();
93148
93628
  } catch {
93149
93629
  return null;
93150
93630
  }
@@ -93153,21 +93633,21 @@ async function handleStatusCommand(cliVersion) {
93153
93633
  const pipV = await pipVersion2();
93154
93634
  const pipProbe = pipV ? await pipSupportsDryRunReport2() : null;
93155
93635
  const npmLine = npmV ? `npm ${npmV} (transitive scan supported)` : "npm not found on PATH";
93156
- const npmGlyph = npmV ? chalk15.green("\u2713") : chalk15.yellow("\xB7");
93157
- process.stderr.write(` ${npmGlyph} ${chalk15.bold("npm")} ` + chalk15.dim(npmLine + "\n"));
93636
+ const npmGlyph = npmV ? chalk17.green("\u2713") : chalk17.yellow("\xB7");
93637
+ process.stderr.write(` ${npmGlyph} ${chalk17.bold("npm")} ` + chalk17.dim(npmLine + "\n"));
93158
93638
  let pipLine;
93159
93639
  let pipGlyph;
93160
93640
  if (!pipV) {
93161
93641
  pipLine = "pip not found on PATH (Python projects won't scan)";
93162
- pipGlyph = chalk15.yellow("\xB7");
93642
+ pipGlyph = chalk17.yellow("\xB7");
93163
93643
  } else if (pipProbe?.ok) {
93164
93644
  pipLine = `pip ${pipV} (transitive scan supported)`;
93165
- pipGlyph = chalk15.green("\u2713");
93645
+ pipGlyph = chalk17.green("\u2713");
93166
93646
  } else {
93167
93647
  pipLine = `pip ${pipV} \u2014 transitive scan unavailable (${pipProbe?.reason ?? "old pip"}; \`pip install -U pip\`)`;
93168
- pipGlyph = chalk15.yellow("!");
93648
+ pipGlyph = chalk17.yellow("!");
93169
93649
  }
93170
- process.stderr.write(` ${pipGlyph} ${chalk15.bold("pip")} ` + chalk15.dim(pipLine + "\n"));
93650
+ process.stderr.write(` ${pipGlyph} ${chalk17.bold("pip")} ` + chalk17.dim(pipLine + "\n"));
93171
93651
  } catch {
93172
93652
  }
93173
93653
  process.stderr.write("\n");
@@ -93193,8 +93673,8 @@ __export(publish_check_exports, {
93193
93673
  summarize: () => summarize
93194
93674
  });
93195
93675
  import { spawn as spawn4 } from "node:child_process";
93196
- import { readFileSync as readFileSync11, existsSync as existsSync14, readdirSync as readdirSync2 } from "node:fs";
93197
- import { join as join16, basename as basename4 } from "node:path";
93676
+ import { readFileSync as readFileSync13, existsSync as existsSync17, readdirSync as readdirSync2 } from "node:fs";
93677
+ import { join as join14, basename as basename4 } from "node:path";
93198
93678
  import { createGunzip } from "node:zlib";
93199
93679
  import { createReadStream as createReadStream2 } from "node:fs";
93200
93680
  function sanitizeSnippet(s, max = 40) {
@@ -93241,7 +93721,7 @@ async function npmPackDryRun(cwd2 = process.cwd()) {
93241
93721
  }));
93242
93722
  let packageJson;
93243
93723
  try {
93244
- const raw = readFileSync11(join16(cwd2, "package.json"), "utf-8");
93724
+ const raw = readFileSync13(join14(cwd2, "package.json"), "utf-8");
93245
93725
  packageJson = JSON.parse(raw);
93246
93726
  } catch {
93247
93727
  }
@@ -93253,9 +93733,9 @@ async function npmPackDryRun(cwd2 = process.cwd()) {
93253
93733
  });
93254
93734
  }
93255
93735
  function findPypiArtifacts(cwd2 = process.cwd()) {
93256
- const dist = join16(cwd2, "dist");
93257
- if (!existsSync14(dist)) return [];
93258
- return readdirSync2(dist).filter((f) => f.endsWith(".tar.gz") || f.endsWith(".whl")).map((f) => join16(dist, f));
93736
+ const dist = join14(cwd2, "dist");
93737
+ if (!existsSync17(dist)) return [];
93738
+ return readdirSync2(dist).filter((f) => f.endsWith(".tar.gz") || f.endsWith(".whl")).map((f) => join14(dist, f));
93259
93739
  }
93260
93740
  function scanPayload(files) {
93261
93741
  const findings = [];
@@ -93329,7 +93809,7 @@ async function runNpmPublishCheck(cwd2 = process.cwd()) {
93329
93809
  const payload = pack.files.map((f) => ({
93330
93810
  path: f.path,
93331
93811
  size: f.size,
93332
- read: () => readFileSync11(join16(cwd2, f.path))
93812
+ read: () => readFileSync13(join14(cwd2, f.path))
93333
93813
  }));
93334
93814
  const findings = scanPayload(payload);
93335
93815
  const scripts = pack.packageJson?.scripts ?? {};
@@ -93550,11 +94030,6 @@ var init_publish_check = __esm({
93550
94030
  recommendation: "Revoke the token at github.com/settings/tokens and remove the file from the payload."
93551
94031
  },
93552
94032
  {
93553
- // Slack tokens come in several real shapes:
93554
- // xoxb-{teamId}-{botId}-{secret} (legacy 3-seg)
93555
- // xoxa-2-{teamId}-{userId}-{secret} (4-seg)
93556
- // xoxp-{N}-{N}-{N}-{hex} (4-seg user)
93557
- // Accept ≥2 numeric segments + an alphanumeric tail of length ≥10.
93558
94033
  re: /xox[abp]-[0-9]+-[0-9]+(-[0-9]+)?-[A-Za-z0-9]{10,}/,
93559
94034
  category: "bundled-secret",
93560
94035
  severity: 5,
@@ -93587,9 +94062,7 @@ __export(cleanup_exports, {
93587
94062
  handleCleanupCommand: () => handleCleanupCommand,
93588
94063
  runCleanup: () => runCleanup
93589
94064
  });
93590
- import { existsSync as existsSync15, unlinkSync as unlinkSync4 } from "node:fs";
93591
- import { join as join17 } from "node:path";
93592
- import { homedir as homedir7 } from "node:os";
94065
+ import { existsSync as existsSync18, unlinkSync as unlinkSync6 } from "node:fs";
93593
94066
  async function confirm(prompt) {
93594
94067
  process.stderr.write(prompt);
93595
94068
  return new Promise((resolve3) => {
@@ -93620,16 +94093,19 @@ async function revokeKey(apiKey) {
93620
94093
  }
93621
94094
  }
93622
94095
  async function runCleanup(opts = {}) {
94096
+ const configPath2 = dgConfigPath();
94097
+ const legacyDgrc = legacyPaths().dgrc;
93623
94098
  process.stderr.write(import_chalk9.default.bold("\n Dependency Guardian cleanup\n\n"));
94099
+ process.stderr.write(import_chalk9.default.dim(" Note: `dg cleanup` is deprecated. Use `dg uninstall` for a full removal\n"));
94100
+ process.stderr.write(import_chalk9.default.dim(" that also strips per-repo git hooks and shell-rc lines.\n\n"));
93624
94101
  process.stderr.write(" This will:\n");
93625
94102
  process.stderr.write(" 1. Revoke this device's API key on the server\n");
93626
- process.stderr.write(` 2. Remove ${DGRC_PATH}
94103
+ process.stderr.write(` 2. Remove ${configPath2}
93627
94104
  `);
93628
- process.stderr.write(" 3. Remove the shell-alias snippet (~/.dependency-guardian/aliases.sh)\n");
93629
- process.stderr.write(" 4. Remove any legacy .dgprotect.json in the current directory\n\n");
94105
+ process.stderr.write(" 3. Remove the shell-alias snippet\n\n");
93630
94106
  process.stderr.write(import_chalk9.default.dim(" Git pre-commit hooks in your projects are NOT removed.\n"));
93631
- process.stderr.write(import_chalk9.default.dim(" Run `dg hook uninstall` in each repo separately, or delete\n"));
93632
- process.stderr.write(import_chalk9.default.dim(" the dg lines from `.git/hooks/pre-commit` by hand.\n\n"));
94107
+ process.stderr.write(import_chalk9.default.dim(" Run `dg uninstall` to remove every dg artifact, or `dg hook uninstall`\n"));
94108
+ process.stderr.write(import_chalk9.default.dim(" per repo to remove just the pre-commit hooks.\n\n"));
93633
94109
  if (!opts.assumeYes) {
93634
94110
  if (!process.stdin.isTTY) {
93635
94111
  process.stderr.write(import_chalk9.default.yellow(" Non-interactive shell \u2014 pass --yes to confirm without prompting.\n\n"));
@@ -93653,18 +94129,17 @@ async function runCleanup(opts = {}) {
93653
94129
  } else {
93654
94130
  process.stderr.write(import_chalk9.default.dim(" \xB7 No saved API key found\n"));
93655
94131
  }
93656
- if (existsSync15(DGRC_PATH)) {
93657
- try {
93658
- unlinkSync4(DGRC_PATH);
93659
- process.stderr.write(import_chalk9.default.green(" \u2713 ") + `Removed ${DGRC_PATH}
94132
+ for (const p of [configPath2, legacyDgrc]) {
94133
+ if (existsSync18(p)) {
94134
+ try {
94135
+ unlinkSync6(p);
94136
+ process.stderr.write(import_chalk9.default.green(" \u2713 ") + `Removed ${p}
93660
94137
  `);
93661
- } catch (e) {
93662
- process.stderr.write(import_chalk9.default.red(" \u2718 ") + `Couldn't remove ${DGRC_PATH}: ${e.message}
94138
+ } catch (e) {
94139
+ process.stderr.write(import_chalk9.default.red(" \u2718 ") + `Couldn't remove ${p}: ${e.message}
93663
94140
  `);
94141
+ }
93664
94142
  }
93665
- } else {
93666
- process.stderr.write(import_chalk9.default.dim(` \xB7 ${DGRC_PATH} already absent
93667
- `));
93668
94143
  }
93669
94144
  process.stderr.write("\n");
93670
94145
  await runProtectOff(process.cwd(), { removeShell: true });
@@ -93676,7 +94151,7 @@ async function handleCleanupCommand(args) {
93676
94151
  const assumeYes = args.includes("--yes") || args.includes("-y");
93677
94152
  return runCleanup({ assumeYes });
93678
94153
  }
93679
- var import_chalk9, DGRC_PATH;
94154
+ var import_chalk9;
93680
94155
  var init_cleanup = __esm({
93681
94156
  "src/commands/cleanup.ts"() {
93682
94157
  "use strict";
@@ -93684,7 +94159,572 @@ var init_cleanup = __esm({
93684
94159
  init_protect();
93685
94160
  init_auth();
93686
94161
  init_config();
93687
- DGRC_PATH = join17(homedir7(), ".dgrc.json");
94162
+ init_paths();
94163
+ }
94164
+ });
94165
+
94166
+ // src/commands/uninstall.ts
94167
+ var uninstall_exports = {};
94168
+ __export(uninstall_exports, {
94169
+ handleUninstallCommand: () => handleUninstallCommand,
94170
+ parseUninstallArgs: () => parseUninstallArgs,
94171
+ runUninstall: () => runUninstall
94172
+ });
94173
+ import { existsSync as existsSync19, rmSync as rmSync3, unlinkSync as unlinkSync7, statSync as statSync5 } from "node:fs";
94174
+ function parseUninstallArgs(args) {
94175
+ return {
94176
+ assumeYes: args.includes("--yes") || args.includes("-y"),
94177
+ dryRun: args.includes("--dry-run"),
94178
+ soft: args.includes("--soft"),
94179
+ keepHooks: args.includes("--keep-hooks")
94180
+ };
94181
+ }
94182
+ function safeExists(p) {
94183
+ try {
94184
+ return existsSync19(p);
94185
+ } catch {
94186
+ return false;
94187
+ }
94188
+ }
94189
+ function buildPlan(opts) {
94190
+ const p = dgPaths();
94191
+ const legacy = legacyPaths();
94192
+ const plan = {
94193
+ configFiles: [],
94194
+ cacheDir: null,
94195
+ stateDir: null,
94196
+ rootDir: null,
94197
+ legacyFiles: [],
94198
+ legacyDirs: [],
94199
+ hooks: [],
94200
+ rcCleanup: false
94201
+ };
94202
+ if (safeExists(p.config)) plan.configFiles.push(p.config);
94203
+ if (!opts.soft) {
94204
+ if (safeExists(p.cacheDir)) plan.cacheDir = p.cacheDir;
94205
+ if (safeExists(p.stateDir)) plan.stateDir = p.stateDir;
94206
+ if (safeExists(p.root)) plan.rootDir = p.root;
94207
+ for (const f of [legacy.dgrc, legacy.updateCheck]) {
94208
+ if (safeExists(f)) plan.legacyFiles.push(f);
94209
+ }
94210
+ if (safeExists(legacy.cacheDir)) plan.legacyDirs.push(legacy.cacheDir);
94211
+ if (!opts.keepHooks) {
94212
+ plan.hooks = listEntries();
94213
+ }
94214
+ plan.rcCleanup = true;
94215
+ }
94216
+ return plan;
94217
+ }
94218
+ function printPlan(plan, opts) {
94219
+ process.stderr.write(import_chalk10.default.bold("\n Dependency Guardian uninstall\n\n"));
94220
+ if (opts.soft) {
94221
+ process.stderr.write(import_chalk10.default.dim(" Soft mode: removing config + api key only.\n\n"));
94222
+ } else if (opts.dryRun) {
94223
+ process.stderr.write(import_chalk10.default.yellow(" Dry run \u2014 no files will be touched.\n\n"));
94224
+ }
94225
+ process.stderr.write(" Will remove:\n");
94226
+ if (plan.configFiles.length > 0) {
94227
+ for (const f of plan.configFiles) process.stderr.write(` ${f}
94228
+ `);
94229
+ }
94230
+ if (plan.rootDir && !opts.soft) {
94231
+ process.stderr.write(` ${plan.rootDir}/ (entire tree)
94232
+ `);
94233
+ }
94234
+ for (const f of plan.legacyFiles) process.stderr.write(` ${f} (legacy)
94235
+ `);
94236
+ for (const d of plan.legacyDirs) process.stderr.write(` ${d}/ (legacy)
94237
+ `);
94238
+ if (plan.hooks.length > 0) {
94239
+ process.stderr.write(` pre-commit hooks in ${plan.hooks.length} repo(s):
94240
+ `);
94241
+ for (const h of plan.hooks) {
94242
+ process.stderr.write(import_chalk10.default.dim(` - ${h.repoPath} (${h.framework})
94243
+ `));
94244
+ }
94245
+ } else if (!opts.keepHooks && !opts.soft) {
94246
+ process.stderr.write(import_chalk10.default.dim(` pre-commit hooks: none registered
94247
+ `));
94248
+ }
94249
+ if (plan.rcCleanup) {
94250
+ process.stderr.write(` sentinel block in your shell rc files (~/.zshrc, ~/.bashrc, ~/.bash_profile, ~/.profile)
94251
+ `);
94252
+ }
94253
+ process.stderr.write(" Will also:\n");
94254
+ process.stderr.write(" revoke this device's API key on the server (best-effort, 5s timeout)\n\n");
94255
+ if (!opts.soft) {
94256
+ process.stderr.write(import_chalk10.default.dim(
94257
+ " Note: this does NOT run `npm uninstall -g @westbayberry/dg`.\n After confirming, run that separately to remove the binary itself.\n\n"
94258
+ ));
94259
+ }
94260
+ }
94261
+ async function confirm2(prompt) {
94262
+ if (!process.stdin.isTTY) return false;
94263
+ process.stderr.write(prompt);
94264
+ return new Promise((resolve3) => {
94265
+ let buf = "";
94266
+ const onData = (chunk) => {
94267
+ buf += chunk.toString("utf8");
94268
+ const idx = buf.indexOf("\n");
94269
+ if (idx < 0) return;
94270
+ process.stdin.off("data", onData);
94271
+ const ans = buf.slice(0, idx).trim().toLowerCase();
94272
+ resolve3(ans === "y" || ans === "yes");
94273
+ };
94274
+ process.stdin.on("data", onData);
94275
+ });
94276
+ }
94277
+ async function revokeApiKey(apiKey) {
94278
+ try {
94279
+ const config3 = parseConfig(["node", "dg", "uninstall"], false);
94280
+ const ctrl = new AbortController();
94281
+ const timer = setTimeout(() => ctrl.abort(), REVOKE_TIMEOUT_MS);
94282
+ let resp;
94283
+ try {
94284
+ resp = await fetch(`${config3.apiUrl}/v1/auth/revoke`, {
94285
+ method: "POST",
94286
+ headers: { Authorization: `Bearer ${apiKey}` },
94287
+ signal: ctrl.signal
94288
+ });
94289
+ } finally {
94290
+ clearTimeout(timer);
94291
+ }
94292
+ if (resp.ok || resp.status === 401) return { ok: true };
94293
+ return { ok: false, reason: `HTTP ${resp.status}` };
94294
+ } catch (e) {
94295
+ return { ok: false, reason: e.message };
94296
+ }
94297
+ }
94298
+ function rmFile(path3) {
94299
+ try {
94300
+ unlinkSync7(path3);
94301
+ return { ok: true };
94302
+ } catch (e) {
94303
+ const code = e.code;
94304
+ if (code === "ENOENT") return { ok: true };
94305
+ return { ok: false, reason: e.message };
94306
+ }
94307
+ }
94308
+ function rmTree(path3) {
94309
+ try {
94310
+ rmSync3(path3, { recursive: true, force: true });
94311
+ return { ok: true };
94312
+ } catch (e) {
94313
+ return { ok: false, reason: e.message };
94314
+ }
94315
+ }
94316
+ function isWritableTarget(path3) {
94317
+ try {
94318
+ const st = statSync5(path3);
94319
+ return st.isDirectory() || st.isFile();
94320
+ } catch {
94321
+ return false;
94322
+ }
94323
+ }
94324
+ async function runUninstall(args) {
94325
+ if (args.includes("--help") || args.includes("-h")) {
94326
+ process.stdout.write(USAGE4);
94327
+ return 0;
94328
+ }
94329
+ const opts = parseUninstallArgs(args);
94330
+ const plan = buildPlan(opts);
94331
+ printPlan(plan, opts);
94332
+ if (opts.dryRun) {
94333
+ process.stderr.write(import_chalk10.default.dim(" Dry run: nothing was changed.\n\n"));
94334
+ return 0;
94335
+ }
94336
+ if (!opts.assumeYes) {
94337
+ if (!process.stdin.isTTY) {
94338
+ process.stderr.write(import_chalk10.default.yellow(" Non-interactive shell \u2014 pass --yes to proceed.\n\n"));
94339
+ return 1;
94340
+ }
94341
+ const ok = await confirm2(" Proceed? [y/N] ");
94342
+ if (!ok) {
94343
+ process.stderr.write(import_chalk10.default.dim("\n Cancelled. Nothing changed.\n\n"));
94344
+ return 0;
94345
+ }
94346
+ process.stderr.write("\n");
94347
+ }
94348
+ const apiKey = getStoredApiKey();
94349
+ if (apiKey) {
94350
+ const r = await revokeApiKey(apiKey);
94351
+ if (r.ok) {
94352
+ process.stderr.write(import_chalk10.default.green(" \u2713 ") + "Revoked API key server-side\n");
94353
+ } else {
94354
+ process.stderr.write(
94355
+ import_chalk10.default.yellow(" \u26A0 ") + `Could not revoke API key (${r.reason ?? "unknown"}); local removal continues
94356
+ `
94357
+ );
94358
+ }
94359
+ } else {
94360
+ process.stderr.write(import_chalk10.default.dim(" \xB7 No stored API key\n"));
94361
+ }
94362
+ if (!opts.keepHooks && !opts.soft) {
94363
+ for (const h of plan.hooks) {
94364
+ if (!existsSync19(h.repoPath)) {
94365
+ process.stderr.write(import_chalk10.default.dim(` \xB7 ${h.repoPath} no longer exists \u2014 skipping registered hook
94366
+ `));
94367
+ continue;
94368
+ }
94369
+ try {
94370
+ const r = stripDgFromHookFile(h.hookPath, h.framework, h.repoPath);
94371
+ if (r.status === "removed") {
94372
+ process.stderr.write(import_chalk10.default.green(" \u2713 ") + `Removed hook ${h.hookPath}
94373
+ `);
94374
+ } else {
94375
+ process.stderr.write(import_chalk10.default.dim(` \xB7 No dg hook found at ${h.hookPath}
94376
+ `));
94377
+ }
94378
+ } catch (e) {
94379
+ process.stderr.write(
94380
+ import_chalk10.default.yellow(" \u26A0 ") + `Could not remove hook ${h.hookPath}: ${e.message}
94381
+ `
94382
+ );
94383
+ }
94384
+ }
94385
+ }
94386
+ if (plan.rcCleanup) {
94387
+ try {
94388
+ const r = stripAliasSourceFromRc();
94389
+ for (const f of r.modified) {
94390
+ process.stderr.write(import_chalk10.default.green(" \u2713 ") + `Stripped sentinel block from ${f}
94391
+ `);
94392
+ }
94393
+ for (const f of r.manualReviewNeeded) {
94394
+ process.stderr.write(
94395
+ import_chalk10.default.yellow(" \u26A0 ") + `${f} has an open sentinel but no close \u2014 leaving it; remove the block by hand
94396
+ `
94397
+ );
94398
+ }
94399
+ } catch (e) {
94400
+ process.stderr.write(import_chalk10.default.yellow(` \u26A0 Shell-rc cleanup failed: ${e.message}
94401
+ `));
94402
+ }
94403
+ }
94404
+ if (opts.soft) {
94405
+ for (const f of plan.configFiles) {
94406
+ const r = rmFile(f);
94407
+ if (r.ok) process.stderr.write(import_chalk10.default.green(" \u2713 ") + `Removed ${f}
94408
+ `);
94409
+ else process.stderr.write(import_chalk10.default.red(" \u2718 ") + `${f}: ${r.reason}
94410
+ `);
94411
+ }
94412
+ } else {
94413
+ for (const f of plan.legacyFiles) {
94414
+ if (!isWritableTarget(f)) continue;
94415
+ const r = rmFile(f);
94416
+ if (r.ok) process.stderr.write(import_chalk10.default.green(" \u2713 ") + `Removed ${f}
94417
+ `);
94418
+ else process.stderr.write(import_chalk10.default.red(" \u2718 ") + `${f}: ${r.reason}
94419
+ `);
94420
+ }
94421
+ for (const d of plan.legacyDirs) {
94422
+ if (!isWritableTarget(d)) continue;
94423
+ const r = rmTree(d);
94424
+ if (r.ok) process.stderr.write(import_chalk10.default.green(" \u2713 ") + `Removed ${d}
94425
+ `);
94426
+ else process.stderr.write(import_chalk10.default.red(" \u2718 ") + `${d}: ${r.reason}
94427
+ `);
94428
+ }
94429
+ if (plan.rootDir) {
94430
+ const r = rmTree(plan.rootDir);
94431
+ if (r.ok) process.stderr.write(import_chalk10.default.green(" \u2713 ") + `Removed ${plan.rootDir}
94432
+ `);
94433
+ else process.stderr.write(import_chalk10.default.red(" \u2718 ") + `${plan.rootDir}: ${r.reason}
94434
+ `);
94435
+ }
94436
+ }
94437
+ process.stderr.write("\n");
94438
+ if (opts.soft) {
94439
+ process.stderr.write(import_chalk10.default.bold(" Soft cleanup complete.\n"));
94440
+ } else {
94441
+ process.stderr.write(import_chalk10.default.bold.green(" Uninstall complete.\n\n"));
94442
+ process.stderr.write(" To remove the binary itself, run:\n");
94443
+ process.stderr.write(import_chalk10.default.cyan(" npm uninstall -g @westbayberry/dg\n\n"));
94444
+ }
94445
+ return 0;
94446
+ }
94447
+ function handleUninstallCommand(args) {
94448
+ return runUninstall(args);
94449
+ }
94450
+ var import_chalk10, REVOKE_TIMEOUT_MS, USAGE4;
94451
+ var init_uninstall = __esm({
94452
+ "src/commands/uninstall.ts"() {
94453
+ "use strict";
94454
+ import_chalk10 = __toESM(require_source());
94455
+ init_paths();
94456
+ init_hooks_registry();
94457
+ init_hook();
94458
+ init_protect();
94459
+ init_auth();
94460
+ init_config();
94461
+ REVOKE_TIMEOUT_MS = 5e3;
94462
+ USAGE4 = `
94463
+ dg uninstall \u2014 remove every file dg has written locally
94464
+
94465
+ Usage:
94466
+ dg uninstall [flags]
94467
+
94468
+ Flags:
94469
+ --yes, -y Skip the confirmation prompt
94470
+ --dry-run Show what would be removed; make no changes
94471
+ --soft Only revoke API key + remove the config file
94472
+ (leaves cache, hooks, and rc-source-line in place)
94473
+ --keep-hooks Leave per-repo pre-commit hooks installed
94474
+
94475
+ Removes:
94476
+ ~/.dg/ (config + cache + state + hooks registry)
94477
+ ~/.dgrc.json (legacy, if still present)
94478
+ ~/.dg/ (legacy, if still present)
94479
+ ~/.dg-update-check.json (legacy, if still present)
94480
+ every per-repo pre-commit hook installed via 'dg hook install'
94481
+ the dg sentinel block in your shell rc files
94482
+
94483
+ Also revokes this device's API key server-side (5s timeout; offline is OK,
94484
+ local cleanup proceeds either way).
94485
+
94486
+ Does NOT run 'npm uninstall -g @westbayberry/dg' \u2014 run that separately.
94487
+ `.trimStart();
94488
+ }
94489
+ });
94490
+
94491
+ // src/commands/init.ts
94492
+ var init_exports3 = {};
94493
+ __export(init_exports3, {
94494
+ handleInitCommand: () => handleInitCommand,
94495
+ parseInitArgs: () => parseInitArgs,
94496
+ runInit: () => runInit2
94497
+ });
94498
+ import { existsSync as existsSync20 } from "node:fs";
94499
+ import { join as join15 } from "node:path";
94500
+ import * as readline2 from "node:readline";
94501
+ import { execFileSync as execFileSync4 } from "node:child_process";
94502
+ function parseInitArgs(args) {
94503
+ const opts = {
94504
+ assumeYes: false,
94505
+ installHook: true,
94506
+ enableProtect: true,
94507
+ appendRc: false,
94508
+ runScan: true
94509
+ };
94510
+ for (const a of args) {
94511
+ if (a === "--yes" || a === "-y") opts.assumeYes = true;
94512
+ else if (a === "--no-hook") opts.installHook = false;
94513
+ else if (a === "--no-protect") opts.enableProtect = false;
94514
+ else if (a === "--no-scan") opts.runScan = false;
94515
+ else if (a === "--append-rc") opts.appendRc = true;
94516
+ }
94517
+ return opts;
94518
+ }
94519
+ async function confirm3(prompt, defaultYes, assumeYes) {
94520
+ if (assumeYes) {
94521
+ process.stderr.write(prompt + (defaultYes ? "yes" : "no") + " (--yes)\n");
94522
+ return defaultYes;
94523
+ }
94524
+ if (!process.stdin.isTTY) {
94525
+ process.stderr.write(prompt + (defaultYes ? "yes" : "no") + " (non-interactive default)\n");
94526
+ return defaultYes;
94527
+ }
94528
+ process.stderr.write(prompt);
94529
+ return new Promise((resolve3) => {
94530
+ const rl = readline2.createInterface({ input: process.stdin });
94531
+ rl.once("line", (line) => {
94532
+ rl.close();
94533
+ const ans = line.trim().toLowerCase();
94534
+ if (ans === "") resolve3(defaultYes);
94535
+ else if (ans === "y" || ans === "yes") resolve3(true);
94536
+ else resolve3(false);
94537
+ });
94538
+ });
94539
+ }
94540
+ function detectEcosystem(cwd2) {
94541
+ const npm = existsSync20(join15(cwd2, "package.json")) || existsSync20(join15(cwd2, "package-lock.json")) || existsSync20(join15(cwd2, "pnpm-lock.yaml")) || existsSync20(join15(cwd2, "yarn.lock"));
94542
+ const pypi = existsSync20(join15(cwd2, "pyproject.toml")) || existsSync20(join15(cwd2, "setup.py")) || existsSync20(join15(cwd2, "requirements.txt")) || existsSync20(join15(cwd2, "Pipfile")) || existsSync20(join15(cwd2, "poetry.lock"));
94543
+ return { npm, pypi };
94544
+ }
94545
+ function findRepoRootOrNull(cwd2) {
94546
+ try {
94547
+ return execFileSync4("git", ["-C", cwd2, "rev-parse", "--show-toplevel"], {
94548
+ encoding: "utf-8",
94549
+ stdio: ["pipe", "pipe", "pipe"]
94550
+ }).trim();
94551
+ } catch {
94552
+ return null;
94553
+ }
94554
+ }
94555
+ async function runInit2(args) {
94556
+ if (args.includes("--help") || args.includes("-h")) {
94557
+ process.stdout.write(USAGE5);
94558
+ return 0;
94559
+ }
94560
+ const opts = parseInitArgs(args);
94561
+ const cwd2 = process.cwd();
94562
+ if (getFirstRunCompletedAt() === null) {
94563
+ process.stderr.write(
94564
+ import_chalk11.default.yellow(" This looks like your first time using dg.\n") + import_chalk11.default.dim(" Run ") + import_chalk11.default.cyan("dg kitty") + import_chalk11.default.dim(" first for the guided intro,\n") + import_chalk11.default.dim(" then come back to ") + import_chalk11.default.cyan("dg init") + import_chalk11.default.dim(" to set up this project.\n\n")
94565
+ );
94566
+ return 1;
94567
+ }
94568
+ process.stderr.write(import_chalk11.default.bold("\n Setting up Dependency Guardian for this project.\n\n"));
94569
+ const eco = detectEcosystem(cwd2);
94570
+ if (!eco.npm && !eco.pypi) {
94571
+ process.stderr.write(
94572
+ import_chalk11.default.yellow(" No lockfile or manifest found in this directory.\n") + import_chalk11.default.dim(" Expected one of: package.json, pnpm-lock.yaml, yarn.lock,\n") + import_chalk11.default.dim(" pyproject.toml, requirements.txt, Pipfile.\n") + import_chalk11.default.dim(" cd into your project root and re-run dg init.\n\n")
94573
+ );
94574
+ return 1;
94575
+ }
94576
+ const stacks = [];
94577
+ if (eco.npm) stacks.push("npm");
94578
+ if (eco.pypi) stacks.push("pypi");
94579
+ process.stderr.write(import_chalk11.default.green(" \u2713 ") + `Detected: ${stacks.join(" + ")}
94580
+
94581
+ `);
94582
+ if (opts.installHook) {
94583
+ const repoRoot = findRepoRootOrNull(cwd2);
94584
+ if (repoRoot === null) {
94585
+ process.stderr.write(import_chalk11.default.dim(" \xB7 Not a git repo \u2014 skipping pre-commit hook.\n"));
94586
+ } else {
94587
+ let info;
94588
+ try {
94589
+ info = detectHookFramework(repoRoot);
94590
+ } catch (e) {
94591
+ process.stderr.write(import_chalk11.default.yellow(` \u26A0 Hook detection failed: ${e.message}
94592
+ `));
94593
+ info = { framework: "bare", targetFile: join15(repoRoot, ".git", "hooks", "pre-commit"), alreadyInstalled: false };
94594
+ }
94595
+ if (info.alreadyInstalled) {
94596
+ process.stderr.write(import_chalk11.default.dim(` \xB7 Pre-commit hook already installed at ${info.targetFile}.
94597
+ `));
94598
+ } else {
94599
+ const prompt = ` Install git pre-commit hook? (${info.framework} \u2192 ${info.targetFile})
94600
+ Blocks commits that add high-risk lockfile changes. [Y/n] `;
94601
+ const yes = await confirm3(prompt, true, opts.assumeYes);
94602
+ if (yes) {
94603
+ try {
94604
+ installHookForFramework(info);
94605
+ addEntry({
94606
+ repoPath: repoRoot,
94607
+ framework: info.framework,
94608
+ hookPath: info.targetFile
94609
+ });
94610
+ } catch (e) {
94611
+ process.stderr.write(import_chalk11.default.red(` \u2718 Hook install failed: ${e.message}
94612
+ `));
94613
+ }
94614
+ } else {
94615
+ process.stderr.write(import_chalk11.default.dim(" \xB7 Skipped hook install.\n"));
94616
+ }
94617
+ }
94618
+ process.stderr.write("\n");
94619
+ }
94620
+ }
94621
+ if (opts.enableProtect) {
94622
+ const prompt = " Enable 'dg protect' so 'npm install' / 'pip install' route\n through dg automatically? [Y/n] ";
94623
+ const yes = await confirm3(prompt, true, opts.assumeYes);
94624
+ if (yes) {
94625
+ try {
94626
+ await runProtectInit(cwd2, { noShell: true });
94627
+ const { rc } = suggestedShellRc();
94628
+ const rcLine = `source ${dgStatePath("aliases.sh")}`;
94629
+ if (opts.appendRc) {
94630
+ const out = appendAliasSourceToRc(rc, rcLine);
94631
+ if (out.status === "appended") {
94632
+ process.stderr.write(import_chalk11.default.green(" \u2713 ") + `Added source line to ${out.rcPath}
94633
+ `);
94634
+ } else if (out.status === "already-present") {
94635
+ process.stderr.write(import_chalk11.default.dim(` \xB7 Source line already present in ${out.rcPath}
94636
+ `));
94637
+ } else {
94638
+ process.stderr.write(
94639
+ import_chalk11.default.yellow(" \u26A0 ") + `Could not append to ${out.rcPath}: ${out.reason}. Add this line by hand:
94640
+ ` + import_chalk11.default.cyan(` ${rcLine}
94641
+ `)
94642
+ );
94643
+ }
94644
+ } else {
94645
+ process.stderr.write(
94646
+ import_chalk11.default.dim(` Add this line to ${rc} to activate the wrapper:
94647
+ `) + import_chalk11.default.cyan(` ${rcLine}
94648
+ `) + import_chalk11.default.dim(` (re-run with --append-rc to have dg add it for you)
94649
+ `)
94650
+ );
94651
+ }
94652
+ } catch (e) {
94653
+ process.stderr.write(import_chalk11.default.red(` \u2718 Protect setup failed: ${e.message}
94654
+ `));
94655
+ }
94656
+ } else {
94657
+ process.stderr.write(import_chalk11.default.dim(" \xB7 Skipped protect setup.\n"));
94658
+ }
94659
+ process.stderr.write("\n");
94660
+ }
94661
+ if (opts.runScan) {
94662
+ const prompt = ` Run an initial scan now? [Y/n] `;
94663
+ const yes = await confirm3(prompt, true, opts.assumeYes);
94664
+ if (yes) {
94665
+ process.stderr.write(import_chalk11.default.dim(" Running 'dg scan'...\n\n"));
94666
+ try {
94667
+ const { runStatic: runStatic2 } = await Promise.resolve().then(() => (init_static_output(), static_output_exports));
94668
+ const { parseConfig: parseConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
94669
+ const cfg = parseConfig2(["node", "dg", "scan"], false);
94670
+ await runStatic2(cfg);
94671
+ } catch (e) {
94672
+ process.stderr.write(import_chalk11.default.yellow(` \u26A0 Initial scan failed: ${e.message}
94673
+ `));
94674
+ process.stderr.write(import_chalk11.default.dim(` Run 'dg scan' manually when ready.
94675
+ `));
94676
+ }
94677
+ } else {
94678
+ process.stderr.write(import_chalk11.default.dim(" \xB7 Skipped initial scan.\n"));
94679
+ }
94680
+ process.stderr.write("\n");
94681
+ }
94682
+ process.stderr.write(import_chalk11.default.bold.green(" \u2713 Setup complete.\n"));
94683
+ process.stderr.write(import_chalk11.default.dim(" Run 'dg status' anytime to check this project's coverage.\n"));
94684
+ if (!isLoggedIn()) {
94685
+ process.stderr.write(import_chalk11.default.dim(" Run 'dg login' for a free account + higher scan limits.\n"));
94686
+ }
94687
+ process.stderr.write("\n");
94688
+ return 0;
94689
+ }
94690
+ function handleInitCommand(args) {
94691
+ return runInit2(args);
94692
+ }
94693
+ var import_chalk11, USAGE5;
94694
+ var init_init = __esm({
94695
+ "src/commands/init.ts"() {
94696
+ "use strict";
94697
+ import_chalk11 = __toESM(require_source());
94698
+ init_auth();
94699
+ init_hook();
94700
+ init_hooks_registry();
94701
+ init_protect();
94702
+ init_paths();
94703
+ USAGE5 = `
94704
+ dg init \u2014 fast per-project setup
94705
+
94706
+ Usage:
94707
+ dg init [flags]
94708
+
94709
+ Flags:
94710
+ --yes, -y Accept all default answers without prompting (CI-safe)
94711
+ --no-hook Skip git pre-commit hook installation
94712
+ --no-protect Skip enabling 'dg protect' (npm/pip wrapper)
94713
+ --no-scan Skip the initial scan
94714
+ --append-rc Auto-append the alias source-line to your shell rc
94715
+ (default: print the line for you to paste)
94716
+
94717
+ What it does (each step asks first):
94718
+ 1. Detect ecosystem (npm / pypi) in this directory
94719
+ 2. Install a git pre-commit hook that blocks risky lockfile changes
94720
+ 3. Enable 'dg protect' so 'npm install' and 'pip install' route
94721
+ through dg
94722
+ 4. Run a first scan of your current dependencies
94723
+
94724
+ This is the per-project equivalent of 'dg kitty' \u2014 the one-time intro
94725
+ wizard that auto-runs on your very first dg invocation. Re-run
94726
+ 'dg kitty' if you want the full tour again.
94727
+ `.trimStart();
93688
94728
  }
93689
94729
  });
93690
94730
 
@@ -93944,14 +94984,14 @@ function formatRate(ratePerSec) {
93944
94984
  if (ratePerSec >= 1) return `${Math.round(ratePerSec)} pkg/s`;
93945
94985
  return `${ratePerSec.toFixed(1)} pkg/s`;
93946
94986
  }
93947
- var import_react32, import_chalk10, import_jsx_runtime9, ProgressBar;
94987
+ var import_react32, import_chalk12, import_jsx_runtime9, ProgressBar;
93948
94988
  var init_ProgressBar = __esm({
93949
94989
  async "src/ui/components/ProgressBar.tsx"() {
93950
94990
  "use strict";
93951
94991
  import_react32 = __toESM(require_react());
93952
94992
  await init_build2();
93953
94993
  await init_build3();
93954
- import_chalk10 = __toESM(require_source());
94994
+ import_chalk12 = __toESM(require_source());
93955
94995
  await init_useTerminalSize();
93956
94996
  import_jsx_runtime9 = __toESM(require_jsx_runtime());
93957
94997
  ProgressBar = ({
@@ -93992,7 +95032,7 @@ var init_ProgressBar = __esm({
93992
95032
  const filledBar = "\u2501".repeat(filled);
93993
95033
  const emptyBar = "\u2501".repeat(empty);
93994
95034
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 2, children: [
93995
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { children: import_chalk10.default.bold("Dependency Guardian") }),
95035
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { children: import_chalk12.default.bold("Dependency Guardian") }),
93996
95036
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { children: "" }),
93997
95037
  /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { children: [
93998
95038
  done ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: "green", children: "\u2713" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: "cyan", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(build_default, { type: "dots" }) }),
@@ -94021,7 +95061,7 @@ var init_ProgressBar = __esm({
94021
95061
  ] }),
94022
95062
  label && !compact && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { dimColor: true, children: [
94023
95063
  " ",
94024
- import_chalk10.default.dim("\u203A"),
95064
+ import_chalk12.default.dim("\u203A"),
94025
95065
  " ",
94026
95066
  label
94027
95067
  ] }) })
@@ -94032,7 +95072,7 @@ var init_ProgressBar = __esm({
94032
95072
 
94033
95073
  // src/ui/components/ScoreHeader.tsx
94034
95074
  function scoreColor(score, action) {
94035
- const colorFn = action === "block" ? import_chalk11.default.red.bold : action === "warn" ? import_chalk11.default.yellow.bold : action === "analysis_incomplete" ? import_chalk11.default.cyan.bold : import_chalk11.default.green.bold;
95075
+ const colorFn = action === "block" ? import_chalk13.default.red.bold : action === "warn" ? import_chalk13.default.yellow.bold : action === "analysis_incomplete" ? import_chalk13.default.cyan.bold : import_chalk13.default.green.bold;
94036
95076
  return colorFn(String(score));
94037
95077
  }
94038
95078
  function plot(grid, x, y, type) {
@@ -94064,10 +95104,10 @@ function fillCircle(grid, cx, cy, r, type) {
94064
95104
  }
94065
95105
  function renderLogo(action) {
94066
95106
  const colors = {
94067
- 1: import_chalk11.default.dim,
94068
- 2: import_chalk11.default.white,
94069
- 3: (s) => import_chalk11.default.bold.white(s),
94070
- 4: action === "block" ? import_chalk11.default.red : action === "warn" ? import_chalk11.default.yellow : action === "analysis_incomplete" ? import_chalk11.default.cyan : import_chalk11.default.green
95107
+ 1: import_chalk13.default.dim,
95108
+ 2: import_chalk13.default.white,
95109
+ 3: (s) => import_chalk13.default.bold.white(s),
95110
+ 4: action === "block" ? import_chalk13.default.red : action === "warn" ? import_chalk13.default.yellow : action === "analysis_incomplete" ? import_chalk13.default.cyan : import_chalk13.default.green
94071
95111
  };
94072
95112
  const result = [];
94073
95113
  const { chars, types, cols } = LOGO_DATA;
@@ -94076,18 +95116,18 @@ function renderLogo(action) {
94076
95116
  for (let j = 0; j < cols; j++) {
94077
95117
  const ch = chars[i + j];
94078
95118
  const t = types[i + j];
94079
- row += t > 0 ? (colors[t] ?? import_chalk11.default.dim)(ch) : ch;
95119
+ row += t > 0 ? (colors[t] ?? import_chalk13.default.dim)(ch) : ch;
94080
95120
  }
94081
95121
  result.push(row);
94082
95122
  }
94083
95123
  return result;
94084
95124
  }
94085
- var import_chalk11, import_jsx_runtime10, LOGO_DATA, ScoreHeader;
95125
+ var import_chalk13, import_jsx_runtime10, LOGO_DATA, ScoreHeader;
94086
95126
  var init_ScoreHeader = __esm({
94087
95127
  async "src/ui/components/ScoreHeader.tsx"() {
94088
95128
  "use strict";
94089
95129
  await init_build2();
94090
- import_chalk11 = __toESM(require_source());
95130
+ import_chalk13 = __toESM(require_source());
94091
95131
  import_jsx_runtime10 = __toESM(require_jsx_runtime());
94092
95132
  LOGO_DATA = (() => {
94093
95133
  const W = 22, H = 28;
@@ -94152,28 +95192,28 @@ var init_ScoreHeader = __esm({
94152
95192
  children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "row", children: [
94153
95193
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", flexGrow: 1, children: [
94154
95194
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { bold: true, children: [
94155
- import_chalk11.default.cyan("\u25C6"),
95195
+ import_chalk13.default.cyan("\u25C6"),
94156
95196
  " Dependency Guardian ",
94157
- userStatus ? import_chalk11.default.dim(userStatus) : ""
95197
+ userStatus ? import_chalk13.default.dim(userStatus) : ""
94158
95198
  ] }),
94159
95199
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: " " }),
94160
95200
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
94161
- import_chalk11.default.dim("Score"),
95201
+ import_chalk13.default.dim("Score"),
94162
95202
  " ",
94163
95203
  scoreColor(score, action)
94164
95204
  ] }),
94165
95205
  total !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
94166
95206
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: " " }),
94167
95207
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
94168
- import_chalk11.default.dim(`${total} package${total !== 1 ? "s" : ""} scanned`),
95208
+ import_chalk13.default.dim(`${total} package${total !== 1 ? "s" : ""} scanned`),
94169
95209
  flagged !== void 0 && flagged > 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
94170
95210
  " ",
94171
- import_chalk11.default.yellow(`${flagged} flagged`),
95211
+ import_chalk13.default.yellow(`${flagged} flagged`),
94172
95212
  " ",
94173
- import_chalk11.default.green(`${clean ?? 0} clean`)
95213
+ import_chalk13.default.green(`${clean ?? 0} clean`)
94174
95214
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
94175
95215
  " ",
94176
- import_chalk11.default.green("all clean")
95216
+ import_chalk13.default.green("all clean")
94177
95217
  ] })
94178
95218
  ] }),
94179
95219
  scanUsage && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { dimColor: true, children: scanUsage })
@@ -94234,8 +95274,8 @@ var init_useExpandAnimation = __esm({
94234
95274
  });
94235
95275
 
94236
95276
  // src/ui/components/InteractiveResultsView.tsx
94237
- import { writeFileSync as writeFileSync8 } from "node:fs";
94238
- import { resolve as resolvePath2 } from "node:path";
95277
+ import { writeFileSync as writeFileSync10 } from "node:fs";
95278
+ import { resolve as resolvePath3 } from "node:path";
94239
95279
  function groupPackages2(packages) {
94240
95280
  const map = /* @__PURE__ */ new Map();
94241
95281
  for (const pkg of packages) {
@@ -94248,10 +95288,10 @@ function groupPackages2(packages) {
94248
95288
  }
94249
95289
  function actionBadge2(score) {
94250
95290
  if (score >= 70)
94251
- return { label: "Block", color: import_chalk12.default.red };
95291
+ return { label: "Block", color: import_chalk14.default.red };
94252
95292
  if (score >= 60)
94253
- return { label: "Warn", color: import_chalk12.default.yellow };
94254
- return { label: "Pass", color: import_chalk12.default.green };
95293
+ return { label: "Warn", color: import_chalk14.default.yellow };
95294
+ return { label: "Pass", color: import_chalk14.default.green };
94255
95295
  }
94256
95296
  function truncate3(s, max) {
94257
95297
  return s.length <= max ? s : s.slice(0, max - 1) + "\u2026";
@@ -94348,7 +95388,7 @@ function buildDetailLines(group, safeVersion, maxWidth) {
94348
95388
  connector,
94349
95389
  " ",
94350
95390
  sevColor(pad2(sevLabel, 8)),
94351
- import_chalk12.default.dim(f.category ?? "")
95391
+ import_chalk14.default.dim(f.category ?? "")
94352
95392
  ] }, `finding-${i}`)
94353
95393
  );
94354
95394
  }
@@ -94356,7 +95396,7 @@ function buildDetailLines(group, safeVersion, maxWidth) {
94356
95396
  } else if (rep.score > 0) {
94357
95397
  lines.push(
94358
95398
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { color: "yellow", children: [
94359
- import_chalk12.default.yellow("\u2192"),
95399
+ import_chalk14.default.yellow("\u2192"),
94360
95400
  " Upgrade to Pro to see risk categories"
94361
95401
  ] }, "upgrade")
94362
95402
  );
@@ -94365,15 +95405,15 @@ function buildDetailLines(group, safeVersion, maxWidth) {
94365
95405
  if (rep.recommendation) {
94366
95406
  lines.push(
94367
95407
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
94368
- import_chalk12.default.dim("Recommendation:"),
95408
+ import_chalk14.default.dim("Recommendation:"),
94369
95409
  " ",
94370
- import_chalk12.default.cyan(truncate3(rep.recommendation, maxWidth - 18))
95410
+ import_chalk14.default.cyan(truncate3(rep.recommendation, maxWidth - 18))
94371
95411
  ] }, "recommendation")
94372
95412
  );
94373
95413
  }
94374
95414
  if (safeVersion) {
94375
95415
  lines.push(
94376
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: import_chalk12.default.green(`Safe version: ${rep.name}@${safeVersion}`) }, "safe")
95416
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: import_chalk14.default.green(`Safe version: ${rep.name}@${safeVersion}`) }, "safe")
94377
95417
  );
94378
95418
  }
94379
95419
  return lines;
@@ -94393,24 +95433,24 @@ function licenseLine(rep) {
94393
95433
  if (!lc) return null;
94394
95434
  const spdx = lc.spdx ?? lc.raw ?? "";
94395
95435
  const desc = LICENSE_DESCRIPTIONS[lc.riskCategory] ?? "";
94396
- const lcColor = lc.riskCategory === "permissive" ? import_chalk12.default.green : lc.riskCategory === "no-license" || lc.riskCategory === "unlicensed" || lc.riskCategory === "network-copyleft" ? import_chalk12.default.red : import_chalk12.default.yellow;
95436
+ const lcColor = lc.riskCategory === "permissive" ? import_chalk14.default.green : lc.riskCategory === "no-license" || lc.riskCategory === "unlicensed" || lc.riskCategory === "network-copyleft" ? import_chalk14.default.red : import_chalk14.default.yellow;
94397
95437
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
94398
95438
  T.branch,
94399
95439
  " ",
94400
95440
  lcColor(spdx),
94401
95441
  " ",
94402
- import_chalk12.default.dim("\u2014"),
95442
+ import_chalk14.default.dim("\u2014"),
94403
95443
  " ",
94404
- import_chalk12.default.dim(desc)
95444
+ import_chalk14.default.dim(desc)
94405
95445
  ] }, "license-info");
94406
95446
  }
94407
- var import_react34, import_chalk12, import_jsx_runtime11, SEVERITY_LABELS, SEVERITY_COLORS, EVIDENCE_LIMIT, FIXED_CHROME, DETAIL_PANE_CHROME, InteractiveResultsView, T, LICENSE_DESCRIPTIONS, FindingsSummary;
95447
+ var import_react34, import_chalk14, import_jsx_runtime11, SEVERITY_LABELS, SEVERITY_COLORS, EVIDENCE_LIMIT, FIXED_CHROME, DETAIL_PANE_CHROME, InteractiveResultsView, T, LICENSE_DESCRIPTIONS, FindingsSummary;
94408
95448
  var init_InteractiveResultsView = __esm({
94409
95449
  async "src/ui/components/InteractiveResultsView.tsx"() {
94410
95450
  "use strict";
94411
95451
  import_react34 = __toESM(require_react());
94412
95452
  await init_build2();
94413
- import_chalk12 = __toESM(require_source());
95453
+ import_chalk14 = __toESM(require_source());
94414
95454
  init_auth();
94415
95455
  await init_ScoreHeader();
94416
95456
  init_useExpandAnimation();
@@ -94425,11 +95465,11 @@ var init_InteractiveResultsView = __esm({
94425
95465
  1: "INFO"
94426
95466
  };
94427
95467
  SEVERITY_COLORS = {
94428
- 5: (s) => import_chalk12.default.red.bold(s),
94429
- 4: (s) => import_chalk12.default.red(s),
94430
- 3: (s) => import_chalk12.default.yellow(s),
94431
- 2: (s) => import_chalk12.default.cyan(s),
94432
- 1: (s) => import_chalk12.default.gray(s)
95468
+ 5: (s) => import_chalk14.default.red.bold(s),
95469
+ 4: (s) => import_chalk14.default.red(s),
95470
+ 3: (s) => import_chalk14.default.yellow(s),
95471
+ 2: (s) => import_chalk14.default.cyan(s),
95472
+ 1: (s) => import_chalk14.default.gray(s)
94433
95473
  };
94434
95474
  EVIDENCE_LIMIT = 2;
94435
95475
  FIXED_CHROME = 21;
@@ -94746,8 +95786,8 @@ var init_InteractiveResultsView = __esm({
94746
95786
  const { body, ext } = formatExport(payload, scope, format);
94747
95787
  const scopeTag = scope === "current-license" && currentLicenseIdx !== null ? `${(licenseGroups[currentLicenseIdx]?.spdx ?? "license").replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 32)}` : scope;
94748
95788
  const filename = `dg-scan-${ts}-${scopeTag}.${ext}`;
94749
- const path3 = resolvePath2(process.cwd(), filename);
94750
- writeFileSync8(path3, body, "utf-8");
95789
+ const path3 = resolvePath3(process.cwd(), filename);
95790
+ writeFileSync10(path3, body, "utf-8");
94751
95791
  showExportMsg(`\u2713 Exported to ${filename}`);
94752
95792
  } catch (e) {
94753
95793
  showExportMsg(`Export failed: ${e.message}`);
@@ -95119,13 +96159,13 @@ var init_InteractiveResultsView = __esm({
95119
96159
  ];
95120
96160
  const renderRow = (title, rows, current, isActive) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
95121
96161
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95122
- isActive ? import_chalk12.default.cyan("\u258C ") : " ",
95123
- isActive ? import_chalk12.default.bold(title) : import_chalk12.default.dim(title)
96162
+ isActive ? import_chalk14.default.cyan("\u258C ") : " ",
96163
+ isActive ? import_chalk14.default.bold(title) : import_chalk14.default.dim(title)
95124
96164
  ] }),
95125
96165
  rows.map((r) => {
95126
96166
  const selected = r.value === current;
95127
- const bullet = selected ? isActive ? import_chalk12.default.cyan("\u25CF") : import_chalk12.default.green("\u25CF") : import_chalk12.default.dim("\u25CB");
95128
- const text = selected ? import_chalk12.default.bold(r.label) : r.label;
96167
+ const bullet = selected ? isActive ? import_chalk14.default.cyan("\u25CF") : import_chalk14.default.green("\u25CF") : import_chalk14.default.dim("\u25CB");
96168
+ const text = selected ? import_chalk14.default.bold(r.label) : r.label;
95129
96169
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { children: [
95130
96170
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { width: 5, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95131
96171
  " ",
@@ -95151,32 +96191,32 @@ var init_InteractiveResultsView = __esm({
95151
96191
  ),
95152
96192
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingLeft: 2, paddingRight: 2, width: "100%", children: [
95153
96193
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { bold: true, children: [
95154
- import_chalk12.default.cyan("\u25C6"),
96194
+ import_chalk14.default.cyan("\u25C6"),
95155
96195
  " Export ",
95156
- import_chalk12.default.dim("(pick what to export and which format)")
96196
+ import_chalk14.default.dim("(pick what to export and which format)")
95157
96197
  ] }),
95158
96198
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
95159
96199
  renderRow("What", SCOPES_RENDER, exportMenu.scope, exportMenu.activeRow === "scope"),
95160
96200
  renderRow("Format", FORMATS_RENDER, exportMenu.format, exportMenu.activeRow === "format")
95161
96201
  ] }),
95162
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: import_chalk12.default.dim("\u2500".repeat(Math.max(20, termCols - 4))) }),
96202
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: import_chalk14.default.dim("\u2500".repeat(Math.max(20, termCols - 4))) }),
95163
96203
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95164
96204
  " ",
95165
- import_chalk12.default.bold.cyan("\u2191\u2193"),
96205
+ import_chalk14.default.bold.cyan("\u2191\u2193"),
95166
96206
  " ",
95167
- import_chalk12.default.dim("scroll"),
96207
+ import_chalk14.default.dim("scroll"),
95168
96208
  " ",
95169
- import_chalk12.default.bold.cyan("\u2190\u2192/Tab"),
96209
+ import_chalk14.default.bold.cyan("\u2190\u2192/Tab"),
95170
96210
  " ",
95171
- import_chalk12.default.dim("switch row"),
96211
+ import_chalk14.default.dim("switch row"),
95172
96212
  " ",
95173
- import_chalk12.default.bold.cyan("\u23CE"),
96213
+ import_chalk14.default.bold.cyan("\u23CE"),
95174
96214
  " ",
95175
- import_chalk12.default.dim("export"),
96215
+ import_chalk14.default.dim("export"),
95176
96216
  " ",
95177
- import_chalk12.default.bold.cyan("Esc"),
96217
+ import_chalk14.default.bold.cyan("Esc"),
95178
96218
  " ",
95179
- import_chalk12.default.dim("cancel")
96219
+ import_chalk14.default.dim("cancel")
95180
96220
  ] })
95181
96221
  ] });
95182
96222
  }
@@ -95206,97 +96246,97 @@ var init_InteractiveResultsView = __esm({
95206
96246
  width: "100%",
95207
96247
  children: [
95208
96248
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { bold: true, children: [
95209
- import_chalk12.default.cyan("\u25C6"),
96249
+ import_chalk14.default.cyan("\u25C6"),
95210
96250
  " Keyboard Shortcuts"
95211
96251
  ] }),
95212
96252
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
95213
96253
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { bold: true, children: " Navigation" }),
95214
96254
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95215
96255
  " ",
95216
- import_chalk12.default.cyan("\u2191 k"),
96256
+ import_chalk14.default.cyan("\u2191 k"),
95217
96257
  " ",
95218
- import_chalk12.default.dim("Move up")
96258
+ import_chalk14.default.dim("Move up")
95219
96259
  ] }),
95220
96260
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95221
96261
  " ",
95222
- import_chalk12.default.cyan("\u2193 j"),
96262
+ import_chalk14.default.cyan("\u2193 j"),
95223
96263
  " ",
95224
- import_chalk12.default.dim("Move down")
96264
+ import_chalk14.default.dim("Move down")
95225
96265
  ] }),
95226
96266
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95227
96267
  " ",
95228
- import_chalk12.default.cyan("g"),
96268
+ import_chalk14.default.cyan("g"),
95229
96269
  " ",
95230
- import_chalk12.default.dim("Jump to top")
96270
+ import_chalk14.default.dim("Jump to top")
95231
96271
  ] }),
95232
96272
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95233
96273
  " ",
95234
- import_chalk12.default.cyan("G"),
96274
+ import_chalk14.default.cyan("G"),
95235
96275
  " ",
95236
- import_chalk12.default.dim("Jump to bottom")
96276
+ import_chalk14.default.dim("Jump to bottom")
95237
96277
  ] }),
95238
96278
  !isDetail && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95239
96279
  " ",
95240
- import_chalk12.default.cyan("PgUp"),
96280
+ import_chalk14.default.cyan("PgUp"),
95241
96281
  " ",
95242
- import_chalk12.default.dim("Page up")
96282
+ import_chalk14.default.dim("Page up")
95243
96283
  ] }),
95244
96284
  !isDetail && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95245
96285
  " ",
95246
- import_chalk12.default.cyan("PgDn"),
96286
+ import_chalk14.default.cyan("PgDn"),
95247
96287
  " ",
95248
- import_chalk12.default.dim("Page down")
96288
+ import_chalk14.default.dim("Page down")
95249
96289
  ] }),
95250
96290
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
95251
96291
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { bold: true, children: " Actions" }),
95252
96292
  !isDetail && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95253
96293
  " ",
95254
- import_chalk12.default.cyan("\u23CE"),
96294
+ import_chalk14.default.cyan("\u23CE"),
95255
96295
  " ",
95256
- import_chalk12.default.dim("Expand findings")
96296
+ import_chalk14.default.dim("Expand findings")
95257
96297
  ] }),
95258
96298
  isDetail && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95259
96299
  " ",
95260
- import_chalk12.default.cyan("Esc"),
96300
+ import_chalk14.default.cyan("Esc"),
95261
96301
  " ",
95262
- import_chalk12.default.dim("Back to list")
96302
+ import_chalk14.default.dim("Back to list")
95263
96303
  ] }),
95264
96304
  !isDetail && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95265
96305
  " ",
95266
- import_chalk12.default.cyan("/"),
96306
+ import_chalk14.default.cyan("/"),
95267
96307
  " ",
95268
- import_chalk12.default.dim("Search packages")
96308
+ import_chalk14.default.dim("Search packages")
95269
96309
  ] }),
95270
96310
  !isDetail && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95271
96311
  " ",
95272
- import_chalk12.default.cyan("l"),
96312
+ import_chalk14.default.cyan("l"),
95273
96313
  " ",
95274
- import_chalk12.default.dim("License breakdown (browse + drill-in)")
96314
+ import_chalk14.default.dim("License breakdown (browse + drill-in)")
95275
96315
  ] }),
95276
96316
  !isDetail && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95277
96317
  " ",
95278
- import_chalk12.default.cyan("e"),
96318
+ import_chalk14.default.cyan("e"),
95279
96319
  " ",
95280
- import_chalk12.default.dim("Export menu \u2014 pick scope (all / summary / packages / licenses / findings) and format (JSON / CSV / Markdown / text)")
96320
+ import_chalk14.default.dim("Export menu \u2014 pick scope (all / summary / packages / licenses / findings) and format (JSON / CSV / Markdown / text)")
95281
96321
  ] }),
95282
96322
  !isDetail && onBack && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95283
96323
  " ",
95284
- import_chalk12.default.cyan("b"),
96324
+ import_chalk14.default.cyan("b"),
95285
96325
  " ",
95286
- import_chalk12.default.dim("Back to project selector")
96326
+ import_chalk14.default.dim("Back to project selector")
95287
96327
  ] }),
95288
96328
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95289
96329
  " ",
95290
- import_chalk12.default.cyan("q"),
96330
+ import_chalk14.default.cyan("q"),
95291
96331
  " ",
95292
- import_chalk12.default.dim("Quit")
96332
+ import_chalk14.default.dim("Quit")
95293
96333
  ] }),
95294
96334
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
95295
96335
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
95296
96336
  " Press ",
95297
- import_chalk12.default.bold.cyan("?"),
96337
+ import_chalk14.default.bold.cyan("?"),
95298
96338
  " or ",
95299
- import_chalk12.default.bold.cyan("Esc"),
96339
+ import_chalk14.default.bold.cyan("Esc"),
95300
96340
  " to close"
95301
96341
  ] })
95302
96342
  ]
@@ -95306,11 +96346,11 @@ var init_InteractiveResultsView = __esm({
95306
96346
  }
95307
96347
  if (showLicenses) {
95308
96348
  const lcColor = (risk) => {
95309
- if (risk === "permissive") return import_chalk12.default.green;
95310
- if (risk === "weak-copyleft") return import_chalk12.default.yellow;
95311
- if (risk === "strong-copyleft") return import_chalk12.default.yellow.bold;
95312
- if (risk === "no-license" || risk === "network-copyleft" || risk === "unlicensed") return import_chalk12.default.red;
95313
- return import_chalk12.default.dim;
96349
+ if (risk === "permissive") return import_chalk14.default.green;
96350
+ if (risk === "weak-copyleft") return import_chalk14.default.yellow;
96351
+ if (risk === "strong-copyleft") return import_chalk14.default.yellow.bold;
96352
+ if (risk === "no-license" || risk === "network-copyleft" || risk === "unlicensed") return import_chalk14.default.red;
96353
+ return import_chalk14.default.dim;
95314
96354
  };
95315
96355
  const totalCount = result.packages.length;
95316
96356
  const maxCount = licenseGroups[0]?.count ?? 1;
@@ -95342,7 +96382,7 @@ var init_InteractiveResultsView = __esm({
95342
96382
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingLeft: 2, paddingRight: 2, width: "100%", children: [
95343
96383
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { children: [
95344
96384
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { bold: true, children: [
95345
- import_chalk12.default.cyan("\u25C6"),
96385
+ import_chalk14.default.cyan("\u25C6"),
95346
96386
  " ",
95347
96387
  color(g.spdx)
95348
96388
  ] }),
@@ -95358,16 +96398,16 @@ var init_InteractiveResultsView = __esm({
95358
96398
  ] }),
95359
96399
  licenseSearchMode || q ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95360
96400
  " ",
95361
- import_chalk12.default.bold.cyan("/"),
96401
+ import_chalk14.default.bold.cyan("/"),
95362
96402
  " ",
95363
- q || import_chalk12.default.dim("(type to filter; Esc clears, Enter confirms)"),
95364
- licenseSearchMode ? import_chalk12.default.cyan("\u2588") : ""
96403
+ q || import_chalk14.default.dim("(type to filter; Esc clears, Enter confirms)"),
96404
+ licenseSearchMode ? import_chalk14.default.cyan("\u2588") : ""
95365
96405
  ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
95366
96406
  _above > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: ` \u2191 ${_above} more above` }),
95367
96407
  _slice.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: ` (no packages match "${q}")` }),
95368
96408
  _slice.map((p) => {
95369
96409
  const score = p.score;
95370
- const scoreColor2 = score >= 70 ? import_chalk12.default.red : score >= 60 ? import_chalk12.default.yellow : score === 0 ? import_chalk12.default.dim : import_chalk12.default.green;
96410
+ const scoreColor2 = score >= 70 ? import_chalk14.default.red : score >= 60 ? import_chalk14.default.yellow : score === 0 ? import_chalk14.default.dim : import_chalk14.default.green;
95371
96411
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { children: [
95372
96412
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { width: Math.max(28, Math.floor(termCols * 0.45)), children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: p.name }) }),
95373
96413
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { width: 16, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: p.version }) }),
@@ -95376,35 +96416,35 @@ var init_InteractiveResultsView = __esm({
95376
96416
  }),
95377
96417
  _below > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: ` \u2193 ${_below} more below` })
95378
96418
  ] }),
95379
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: import_chalk12.default.dim("\u2500".repeat(Math.max(20, termCols - 4))) }),
96419
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: import_chalk14.default.dim("\u2500".repeat(Math.max(20, termCols - 4))) }),
95380
96420
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95381
96421
  " ",
95382
- import_chalk12.default.bold.cyan("\u2191\u2193"),
96422
+ import_chalk14.default.bold.cyan("\u2191\u2193"),
95383
96423
  " ",
95384
- import_chalk12.default.dim("scroll"),
96424
+ import_chalk14.default.dim("scroll"),
95385
96425
  " ",
95386
- import_chalk12.default.bold.cyan("/"),
96426
+ import_chalk14.default.bold.cyan("/"),
95387
96427
  " ",
95388
- import_chalk12.default.dim("search"),
96428
+ import_chalk14.default.dim("search"),
95389
96429
  " ",
95390
- import_chalk12.default.bold.cyan("e"),
96430
+ import_chalk14.default.bold.cyan("e"),
95391
96431
  " ",
95392
- import_chalk12.default.dim("export"),
96432
+ import_chalk14.default.dim("export"),
95393
96433
  " ",
95394
- import_chalk12.default.bold.cyan("Esc"),
96434
+ import_chalk14.default.bold.cyan("Esc"),
95395
96435
  " ",
95396
- import_chalk12.default.dim("back"),
96436
+ import_chalk14.default.dim("back"),
95397
96437
  " ",
95398
- import_chalk12.default.bold.cyan("l"),
96438
+ import_chalk14.default.bold.cyan("l"),
95399
96439
  " ",
95400
- import_chalk12.default.dim("close"),
96440
+ import_chalk14.default.dim("close"),
95401
96441
  " ",
95402
- import_chalk12.default.bold.cyan("q"),
96442
+ import_chalk14.default.bold.cyan("q"),
95403
96443
  " ",
95404
- import_chalk12.default.dim("quit"),
96444
+ import_chalk14.default.dim("quit"),
95405
96445
  exportMsg && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
95406
96446
  " ",
95407
- import_chalk12.default.green(exportMsg)
96447
+ import_chalk14.default.green(exportMsg)
95408
96448
  ] })
95409
96449
  ] })
95410
96450
  ] });
@@ -95450,9 +96490,9 @@ var init_InteractiveResultsView = __esm({
95450
96490
  width: "100%",
95451
96491
  children: [
95452
96492
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { bold: true, children: [
95453
- import_chalk12.default.cyan("\u25C6"),
96493
+ import_chalk14.default.cyan("\u25C6"),
95454
96494
  " Licenses ",
95455
- import_chalk12.default.dim(`(${licenseGroups.length} unique across ${totalCount} packages)`)
96495
+ import_chalk14.default.dim(`(${licenseGroups.length} unique across ${totalCount} packages)`)
95456
96496
  ] }),
95457
96497
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
95458
96498
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { children: [
@@ -95472,10 +96512,10 @@ var init_InteractiveResultsView = __esm({
95472
96512
  const spdxText = truncate3(g.spdx, spdxCol - 1);
95473
96513
  const riskText = g.risk === "unknown" ? "\u2014" : g.risk;
95474
96514
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { children: [
95475
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { width: 2, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: isSelected ? import_chalk12.default.cyan("\u258C") : " " }) }),
95476
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { width: spdxCol, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: isSelected ? import_chalk12.default.bold(color(spdxText)) : color(spdxText) }) }),
96515
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { width: 2, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: isSelected ? import_chalk14.default.cyan("\u258C") : " " }) }),
96516
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { width: spdxCol, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: isSelected ? import_chalk14.default.bold(color(spdxText)) : color(spdxText) }) }),
95477
96517
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { width: riskCol, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: riskText }) }),
95478
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { width: countCol, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: import_chalk12.default.bold(String(g.count)) }) }),
96518
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { width: countCol, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: import_chalk14.default.bold(String(g.count)) }) }),
95479
96519
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { width: barCol, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: color(bar) }) })
95480
96520
  ] }, `${g.risk}::${g.spdx}::${absIdx}`);
95481
96521
  }),
@@ -95483,31 +96523,31 @@ var init_InteractiveResultsView = __esm({
95483
96523
  ]
95484
96524
  }
95485
96525
  ),
95486
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: import_chalk12.default.dim("\u2500".repeat(Math.max(20, termCols - 4))) }),
96526
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: import_chalk14.default.dim("\u2500".repeat(Math.max(20, termCols - 4))) }),
95487
96527
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95488
96528
  " ",
95489
- import_chalk12.default.bold.cyan("\u2191\u2193"),
96529
+ import_chalk14.default.bold.cyan("\u2191\u2193"),
95490
96530
  " ",
95491
- import_chalk12.default.dim("navigate"),
96531
+ import_chalk14.default.dim("navigate"),
95492
96532
  " ",
95493
- import_chalk12.default.bold.cyan("\u23CE"),
96533
+ import_chalk14.default.bold.cyan("\u23CE"),
95494
96534
  " ",
95495
- import_chalk12.default.dim("view packages"),
96535
+ import_chalk14.default.dim("view packages"),
95496
96536
  " ",
95497
- import_chalk12.default.bold.cyan("e"),
96537
+ import_chalk14.default.bold.cyan("e"),
95498
96538
  " ",
95499
- import_chalk12.default.dim("export"),
96539
+ import_chalk14.default.dim("export"),
95500
96540
  " ",
95501
- import_chalk12.default.bold.cyan("l/Esc"),
96541
+ import_chalk14.default.bold.cyan("l/Esc"),
95502
96542
  " ",
95503
- import_chalk12.default.dim("close"),
96543
+ import_chalk14.default.dim("close"),
95504
96544
  " ",
95505
- import_chalk12.default.bold.cyan("q"),
96545
+ import_chalk14.default.bold.cyan("q"),
95506
96546
  " ",
95507
- import_chalk12.default.dim("quit"),
96547
+ import_chalk14.default.dim("quit"),
95508
96548
  exportMsg && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
95509
96549
  " ",
95510
- import_chalk12.default.green(exportMsg)
96550
+ import_chalk14.default.green(exportMsg)
95511
96551
  ] })
95512
96552
  ] })
95513
96553
  ] });
@@ -95549,19 +96589,19 @@ var init_InteractiveResultsView = __esm({
95549
96589
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { justifyContent: "space-between", children: [
95550
96590
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { bold: true, children: [
95551
96591
  groupNames(dpGroup),
95552
- dpRep.license ? import_chalk12.default.dim(" \xB7 ") + (dpRep.license.riskCategory === "permissive" ? import_chalk12.default.green(dpRep.license.spdx ?? dpRep.license.raw ?? "") : dpRep.license.riskCategory === "no-license" || dpRep.license.riskCategory === "network-copyleft" ? import_chalk12.default.red(dpRep.license.spdx ?? dpRep.license.raw ?? "No license") : import_chalk12.default.yellow(dpRep.license.spdx ?? dpRep.license.raw ?? "")) : ""
96592
+ dpRep.license ? import_chalk14.default.dim(" \xB7 ") + (dpRep.license.riskCategory === "permissive" ? import_chalk14.default.green(dpRep.license.spdx ?? dpRep.license.raw ?? "") : dpRep.license.riskCategory === "no-license" || dpRep.license.riskCategory === "network-copyleft" ? import_chalk14.default.red(dpRep.license.spdx ?? dpRep.license.raw ?? "No license") : import_chalk14.default.yellow(dpRep.license.spdx ?? dpRep.license.raw ?? "")) : ""
95553
96593
  ] }),
95554
96594
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: dpColor(`score ${dpRep.score}`) })
95555
96595
  ] }),
95556
96596
  dpAbove > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
95557
- import_chalk12.default.cyan(" \u2191"),
96597
+ import_chalk14.default.cyan(" \u2191"),
95558
96598
  " ",
95559
96599
  dpAbove,
95560
96600
  " more above"
95561
96601
  ] }),
95562
96602
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { flexDirection: "column", marginLeft: 2, children: dpVisible }),
95563
96603
  dpBelow > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
95564
- import_chalk12.default.cyan(" \u2193"),
96604
+ import_chalk14.default.cyan(" \u2193"),
95565
96605
  " ",
95566
96606
  dpBelow,
95567
96607
  " more below"
@@ -95580,13 +96620,13 @@ var init_InteractiveResultsView = __esm({
95580
96620
  width: "100%",
95581
96621
  children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { justifyContent: "space-between", children: [
95582
96622
  clean.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95583
- import_chalk12.default.green("\u2713"),
96623
+ import_chalk14.default.green("\u2713"),
95584
96624
  " ",
95585
- import_chalk12.default.green.bold(String(clean.length)),
96625
+ import_chalk14.default.green.bold(String(clean.length)),
95586
96626
  " ",
95587
- import_chalk12.default.dim(`package${clean.length !== 1 ? "s" : ""} passed with score 0`),
96627
+ import_chalk14.default.dim(`package${clean.length !== 1 ? "s" : ""} passed with score 0`),
95588
96628
  " ",
95589
- import_chalk12.default.dim(`\xB7 ${(durationMs / 1e3).toFixed(1)}s`)
96629
+ import_chalk14.default.dim(`\xB7 ${(durationMs / 1e3).toFixed(1)}s`)
95590
96630
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
95591
96631
  (durationMs / 1e3).toFixed(1),
95592
96632
  "s"
@@ -95595,20 +96635,20 @@ var init_InteractiveResultsView = __esm({
95595
96635
  ] })
95596
96636
  }
95597
96637
  ),
95598
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: import_chalk12.default.dim("\u2500".repeat(Math.max(20, termCols - 4))) }),
96638
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: import_chalk14.default.dim("\u2500".repeat(Math.max(20, termCols - 4))) }),
95599
96639
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95600
96640
  " ",
95601
- import_chalk12.default.bold.cyan("\u2191\u2193"),
96641
+ import_chalk14.default.bold.cyan("\u2191\u2193"),
95602
96642
  " ",
95603
- import_chalk12.default.dim("scroll"),
96643
+ import_chalk14.default.dim("scroll"),
95604
96644
  " ",
95605
- import_chalk12.default.bold.cyan("Esc"),
96645
+ import_chalk14.default.bold.cyan("Esc"),
95606
96646
  " ",
95607
- import_chalk12.default.dim("back"),
96647
+ import_chalk14.default.dim("back"),
95608
96648
  " ",
95609
- import_chalk12.default.bold.cyan("q"),
96649
+ import_chalk14.default.bold.cyan("q"),
95610
96650
  " ",
95611
- import_chalk12.default.dim("quit")
96651
+ import_chalk14.default.dim("quit")
95612
96652
  ] })
95613
96653
  ] });
95614
96654
  }
@@ -95641,7 +96681,7 @@ var init_InteractiveResultsView = __esm({
95641
96681
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: searchQuery ? `${groups.length} of ${allGroupCount}` : `${clampedCursor + 1}/${groups.length}` })
95642
96682
  ] }),
95643
96683
  aboveCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
95644
- import_chalk12.default.cyan(" \u2191"),
96684
+ import_chalk14.default.cyan(" \u2191"),
95645
96685
  " ",
95646
96686
  aboveCount,
95647
96687
  " more above"
@@ -95656,22 +96696,22 @@ var init_InteractiveResultsView = __esm({
95656
96696
  const scoreStr = String(rep.score);
95657
96697
  const lcInfo = rep.license;
95658
96698
  const lcStr = lcInfo ? truncate3(lcInfo.spdx ?? lcInfo.raw ?? "", lcCol - 2) : "";
95659
- const lcColor = !lcInfo ? import_chalk12.default.dim : lcInfo.riskCategory === "permissive" ? import_chalk12.default.green : lcInfo.riskCategory === "no-license" || lcInfo.riskCategory === "unlicensed" || lcInfo.riskCategory === "network-copyleft" ? import_chalk12.default.red : import_chalk12.default.yellow;
96699
+ const lcColor = !lcInfo ? import_chalk14.default.dim : lcInfo.riskCategory === "permissive" ? import_chalk14.default.green : lcInfo.riskCategory === "no-license" || lcInfo.riskCategory === "unlicensed" || lcInfo.riskCategory === "network-copyleft" ? import_chalk14.default.red : import_chalk14.default.yellow;
95660
96700
  const arrow = level === "summary" ? "\u25BE" : "\u25B8";
95661
96701
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", children: [
95662
96702
  isCursor ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { backgroundColor: "#1a1a2e", children: [
95663
- import_chalk12.default.cyan("\u258C"),
96703
+ import_chalk14.default.cyan("\u258C"),
95664
96704
  " ",
95665
- import_chalk12.default.cyan(arrow),
96705
+ import_chalk14.default.cyan(arrow),
95666
96706
  " ",
95667
96707
  ` `,
95668
96708
  color(pad2(label, 6)),
95669
- import_chalk12.default.bold(pad2(truncate3(names, nameCol - 2), nameCol)),
96709
+ import_chalk14.default.bold(pad2(truncate3(names, nameCol - 2), nameCol)),
95670
96710
  lcColor(pad2(lcStr, lcCol)),
95671
96711
  color(scoreStr.padStart(3)),
95672
96712
  " "
95673
96713
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95674
- ` ${import_chalk12.default.dim(arrow)} `,
96714
+ ` ${import_chalk14.default.dim(arrow)} `,
95675
96715
  color(pad2(label, 6)),
95676
96716
  pad2(truncate3(names, nameCol - 2), nameCol),
95677
96717
  lcColor(pad2(lcStr, lcCol)),
@@ -95688,7 +96728,7 @@ var init_InteractiveResultsView = __esm({
95688
96728
  ] }, group.key);
95689
96729
  }),
95690
96730
  belowCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
95691
- import_chalk12.default.cyan(" \u2193"),
96731
+ import_chalk14.default.cyan(" \u2193"),
95692
96732
  " ",
95693
96733
  belowCount,
95694
96734
  " more below"
@@ -95715,13 +96755,13 @@ var init_InteractiveResultsView = __esm({
95715
96755
  ] }),
95716
96756
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { justifyContent: "space-between", children: [
95717
96757
  clean.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95718
- import_chalk12.default.green("\u2713"),
96758
+ import_chalk14.default.green("\u2713"),
95719
96759
  " ",
95720
- import_chalk12.default.green.bold(String(clean.length)),
96760
+ import_chalk14.default.green.bold(String(clean.length)),
95721
96761
  " ",
95722
- import_chalk12.default.dim(`package${clean.length !== 1 ? "s" : ""} passed with score 0`),
96762
+ import_chalk14.default.dim(`package${clean.length !== 1 ? "s" : ""} passed with score 0`),
95723
96763
  " ",
95724
- import_chalk12.default.dim(`\xB7 ${(durationMs / 1e3).toFixed(1)}s`)
96764
+ import_chalk14.default.dim(`\xB7 ${(durationMs / 1e3).toFixed(1)}s`)
95725
96765
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
95726
96766
  (durationMs / 1e3).toFixed(1),
95727
96767
  "s"
@@ -95731,66 +96771,66 @@ var init_InteractiveResultsView = __esm({
95731
96771
  ]
95732
96772
  }
95733
96773
  ),
95734
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: import_chalk12.default.dim("\u2500".repeat(Math.max(20, termCols - 4))) }),
96774
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: import_chalk14.default.dim("\u2500".repeat(Math.max(20, termCols - 4))) }),
95735
96775
  searchMode ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95736
96776
  " ",
95737
- import_chalk12.default.bold.cyan("/"),
96777
+ import_chalk14.default.bold.cyan("/"),
95738
96778
  " ",
95739
96779
  searchQuery,
95740
- import_chalk12.default.cyan("\u2588"),
96780
+ import_chalk14.default.cyan("\u2588"),
95741
96781
  " ",
95742
- import_chalk12.default.dim("Esc clear")
96782
+ import_chalk14.default.dim("Esc clear")
95743
96783
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95744
96784
  " ",
95745
96785
  groups.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
95746
- import_chalk12.default.bold.cyan("\u2191\u2193"),
96786
+ import_chalk14.default.bold.cyan("\u2191\u2193"),
95747
96787
  " ",
95748
- import_chalk12.default.dim("navigate"),
96788
+ import_chalk14.default.dim("navigate"),
95749
96789
  " ",
95750
- import_chalk12.default.bold.cyan("\u23CE"),
96790
+ import_chalk14.default.bold.cyan("\u23CE"),
95751
96791
  " ",
95752
- import_chalk12.default.dim("expand"),
96792
+ import_chalk14.default.dim("expand"),
95753
96793
  " ",
95754
- import_chalk12.default.bold.cyan("/"),
96794
+ import_chalk14.default.bold.cyan("/"),
95755
96795
  " ",
95756
- import_chalk12.default.dim("search"),
96796
+ import_chalk14.default.dim("search"),
95757
96797
  " ",
95758
- import_chalk12.default.bold.cyan("l"),
96798
+ import_chalk14.default.bold.cyan("l"),
95759
96799
  " ",
95760
- import_chalk12.default.dim("licenses"),
96800
+ import_chalk14.default.dim("licenses"),
95761
96801
  " ",
95762
- import_chalk12.default.bold.cyan("e"),
96802
+ import_chalk14.default.bold.cyan("e"),
95763
96803
  " ",
95764
- import_chalk12.default.dim("export"),
96804
+ import_chalk14.default.dim("export"),
95765
96805
  " ",
95766
96806
  onBack && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
95767
- import_chalk12.default.bold.cyan("b"),
96807
+ import_chalk14.default.bold.cyan("b"),
95768
96808
  " ",
95769
- import_chalk12.default.dim("back"),
96809
+ import_chalk14.default.dim("back"),
95770
96810
  " "
95771
96811
  ] }),
95772
- import_chalk12.default.bold.cyan("q"),
96812
+ import_chalk14.default.bold.cyan("q"),
95773
96813
  " ",
95774
- import_chalk12.default.dim("quit"),
96814
+ import_chalk14.default.dim("quit"),
95775
96815
  exportMsg && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
95776
96816
  " ",
95777
- import_chalk12.default.green(exportMsg)
96817
+ import_chalk14.default.green(exportMsg)
95778
96818
  ] })
95779
96819
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
95780
96820
  "Press ",
95781
- import_chalk12.default.bold.cyan("q"),
96821
+ import_chalk14.default.bold.cyan("q"),
95782
96822
  " or ",
95783
- import_chalk12.default.bold.cyan("Enter"),
96823
+ import_chalk14.default.bold.cyan("Enter"),
95784
96824
  " ",
95785
- import_chalk12.default.dim("to exit")
96825
+ import_chalk14.default.dim("to exit")
95786
96826
  ] })
95787
96827
  ] })
95788
96828
  ] });
95789
96829
  };
95790
96830
  T = {
95791
- branch: import_chalk12.default.dim("\u251C\u2500\u2500"),
95792
- last: import_chalk12.default.dim("\u2514\u2500\u2500"),
95793
- pipe: import_chalk12.default.dim("\u2502"),
96831
+ branch: import_chalk14.default.dim("\u251C\u2500\u2500"),
96832
+ last: import_chalk14.default.dim("\u2514\u2500\u2500"),
96833
+ pipe: import_chalk14.default.dim("\u2502"),
95794
96834
  blank: " "
95795
96835
  };
95796
96836
  LICENSE_DESCRIPTIONS = {
@@ -95816,9 +96856,9 @@ var init_InteractiveResultsView = __esm({
95816
96856
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
95817
96857
  hasAffects ? T.branch : T.last,
95818
96858
  " ",
95819
- import_chalk12.default.yellow("\u2192"),
96859
+ import_chalk14.default.yellow("\u2192"),
95820
96860
  " ",
95821
- import_chalk12.default.yellow("Upgrade to Pro"),
96861
+ import_chalk14.default.yellow("Upgrade to Pro"),
95822
96862
  " for finding details"
95823
96863
  ] }, "upgrade")
95824
96864
  );
@@ -95836,7 +96876,7 @@ var init_InteractiveResultsView = __esm({
95836
96876
  " ",
95837
96877
  sevColor(pad2(sevLabel, 5)),
95838
96878
  " ",
95839
- import_chalk12.default.dim(f.category ?? ""),
96879
+ import_chalk14.default.dim(f.category ?? ""),
95840
96880
  title
95841
96881
  ] }, `finding-${idx}`)
95842
96882
  );
@@ -95882,12 +96922,12 @@ function getHint(error2) {
95882
96922
  return null;
95883
96923
  }
95884
96924
  }
95885
- var import_chalk13, import_jsx_runtime12, ErrorView;
96925
+ var import_chalk15, import_jsx_runtime12, ErrorView;
95886
96926
  var init_ErrorView = __esm({
95887
96927
  async "src/ui/components/ErrorView.tsx"() {
95888
96928
  "use strict";
95889
96929
  await init_build2();
95890
- import_chalk13 = __toESM(require_source());
96930
+ import_chalk15 = __toESM(require_source());
95891
96931
  init_sanitize();
95892
96932
  import_jsx_runtime12 = __toESM(require_jsx_runtime());
95893
96933
  ErrorView = ({ error: error2 }) => {
@@ -95901,10 +96941,10 @@ var init_ErrorView = __esm({
95901
96941
  paddingLeft: 1,
95902
96942
  paddingRight: 1,
95903
96943
  children: [
95904
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { children: import_chalk13.default.red.bold("\u2718 Error") }),
96944
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { children: import_chalk15.default.red.bold("\u2718 Error") }),
95905
96945
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { children: sanitize(error2.message) }),
95906
96946
  hint && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { children: [
95907
- import_chalk13.default.yellow("\u2192"),
96947
+ import_chalk15.default.yellow("\u2192"),
95908
96948
  " ",
95909
96949
  hint
95910
96950
  ] })
@@ -95916,13 +96956,13 @@ var init_ErrorView = __esm({
95916
96956
  });
95917
96957
 
95918
96958
  // src/ui/components/ProjectSelector.tsx
95919
- var import_react35, import_chalk14, import_jsx_runtime13, ProjectSelector;
96959
+ var import_react35, import_chalk16, import_jsx_runtime13, ProjectSelector;
95920
96960
  var init_ProjectSelector = __esm({
95921
96961
  async "src/ui/components/ProjectSelector.tsx"() {
95922
96962
  "use strict";
95923
96963
  import_react35 = __toESM(require_react());
95924
96964
  await init_build2();
95925
- import_chalk14 = __toESM(require_source());
96965
+ import_chalk16 = __toESM(require_source());
95926
96966
  init_sanitize();
95927
96967
  import_jsx_runtime13 = __toESM(require_jsx_runtime());
95928
96968
  ProjectSelector = ({ projects, onConfirm, onCancel, userStatus }) => {
@@ -95952,14 +96992,14 @@ var init_ProjectSelector = __esm({
95952
96992
  onCancel();
95953
96993
  }
95954
96994
  });
95955
- const ecosystemLabel = (eco) => eco === "npm" ? import_chalk14.default.magenta("npm") : import_chalk14.default.blue("pip");
96995
+ const ecosystemLabel = (eco) => eco === "npm" ? import_chalk16.default.magenta("npm") : import_chalk16.default.blue("pip");
95956
96996
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 1, children: [
95957
96997
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Text, { children: [
95958
- import_chalk14.default.cyan("\u25C6"),
96998
+ import_chalk16.default.cyan("\u25C6"),
95959
96999
  " ",
95960
- import_chalk14.default.bold("Dependency Guardian"),
97000
+ import_chalk16.default.bold("Dependency Guardian"),
95961
97001
  " ",
95962
- userStatus ? import_chalk14.default.dim(userStatus) : ""
97002
+ userStatus ? import_chalk16.default.dim(userStatus) : ""
95963
97003
  ] }),
95964
97004
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { children: "" }),
95965
97005
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Text, { bold: true, children: [
@@ -95972,30 +97012,30 @@ var init_ProjectSelector = __esm({
95972
97012
  projects.map((proj, i) => {
95973
97013
  const isCursor = i === cursor;
95974
97014
  const isSelected = selected.has(i);
95975
- const prefix = isCursor ? import_chalk14.default.cyan("\u258C") : " ";
95976
- const check = isSelected ? import_chalk14.default.green("\u25C9") : import_chalk14.default.dim("\u25CB");
97015
+ const prefix = isCursor ? import_chalk16.default.cyan("\u258C") : " ";
97016
+ const check = isSelected ? import_chalk16.default.green("\u25C9") : import_chalk16.default.dim("\u25CB");
95977
97017
  const line = `${prefix}${check} ${sanitize(proj.relativePath).padEnd(40)} ${ecosystemLabel(proj.ecosystem).padEnd(5)} ${proj.packageCount} packages`;
95978
97018
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { backgroundColor: isCursor ? "#1a1a2e" : void 0, children: line }, i);
95979
97019
  }),
95980
97020
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { children: "" }),
95981
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { dimColor: true, children: selected.size === 0 ? import_chalk14.default.yellow("Select at least 1 project to scan") : `${selected.size} of ${projects.length} selected` }),
97021
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { dimColor: true, children: selected.size === 0 ? import_chalk16.default.yellow("Select at least 1 project to scan") : `${selected.size} of ${projects.length} selected` }),
95982
97022
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { children: "" }),
95983
97023
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Text, { children: [
95984
- import_chalk14.default.bold.cyan("space"),
97024
+ import_chalk16.default.bold.cyan("space"),
95985
97025
  " ",
95986
- import_chalk14.default.dim("toggle"),
97026
+ import_chalk16.default.dim("toggle"),
95987
97027
  " ",
95988
- import_chalk14.default.bold.cyan("a"),
97028
+ import_chalk16.default.bold.cyan("a"),
95989
97029
  " ",
95990
- import_chalk14.default.dim("all"),
97030
+ import_chalk16.default.dim("all"),
95991
97031
  " ",
95992
- import_chalk14.default.bold.hex("#FFD700")("\u23CE"),
97032
+ import_chalk16.default.bold.hex("#FFD700")("\u23CE"),
95993
97033
  " ",
95994
- import_chalk14.default.bold.hex("#FFD700")("scan"),
97034
+ import_chalk16.default.bold.hex("#FFD700")("scan"),
95995
97035
  " ",
95996
- import_chalk14.default.bold.cyan("q"),
97036
+ import_chalk16.default.bold.cyan("q"),
95997
97037
  " ",
95998
- import_chalk14.default.dim("quit")
97038
+ import_chalk16.default.dim("quit")
95999
97039
  ] })
96000
97040
  ] });
96001
97041
  };
@@ -96339,9 +97379,9 @@ function useInstallWrapper(rawArgs, config3, opts, exit) {
96339
97379
  const result = await opts.callAnalyze(resolved, config3);
96340
97380
  if (result.action === "pass") {
96341
97381
  if (exit) exit();
96342
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97382
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
96343
97383
  process.stderr.write(
96344
- chalk15.green(` \u2713 ${resolved.length} package${resolved.length !== 1 ? "s" : ""} scanned \u2014 all clear
97384
+ chalk17.green(` \u2713 ${resolved.length} package${resolved.length !== 1 ? "s" : ""} scanned \u2014 all clear
96345
97385
  `)
96346
97386
  );
96347
97387
  process.stderr.write("\n");
@@ -96352,8 +97392,8 @@ function useInstallWrapper(rawArgs, config3, opts, exit) {
96352
97392
  if (result.action === "warn") {
96353
97393
  if (exit) exit();
96354
97394
  process.stdout.write(renderResultStatic(result, config3) + "\n");
96355
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
96356
- process.stderr.write(chalk15.yellow(" Warnings detected. Proceeding with install.\n\n"));
97395
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97396
+ process.stderr.write(chalk17.yellow(" Warnings detected. Proceeding with install.\n\n"));
96357
97397
  const code = await opts.runInstall(parsed.rawArgs);
96358
97398
  process.exit(code);
96359
97399
  return;
@@ -96466,16 +97506,16 @@ function groupPackages3(packages) {
96466
97506
  return [...map.values()].map((pkgs) => ({ packages: pkgs, key: pkgs[0].name })).sort((a, b) => b.packages[0].score - a.packages[0].score);
96467
97507
  }
96468
97508
  function severityColor(sev) {
96469
- if (sev >= 5) return (s) => import_chalk15.default.bold.red(s);
96470
- if (sev >= 4) return import_chalk15.default.magenta;
96471
- if (sev >= 3) return import_chalk15.default.yellow;
96472
- if (sev >= 2) return import_chalk15.default.dim;
96473
- return import_chalk15.default.dim;
97509
+ if (sev >= 5) return (s) => import_chalk17.default.bold.red(s);
97510
+ if (sev >= 4) return import_chalk17.default.magenta;
97511
+ if (sev >= 3) return import_chalk17.default.yellow;
97512
+ if (sev >= 2) return import_chalk17.default.dim;
97513
+ return import_chalk17.default.dim;
96474
97514
  }
96475
97515
  function actionBadge3(score) {
96476
- if (score >= 70) return { label: "BLOCK", color: import_chalk15.default.red };
96477
- if (score >= 60) return { label: "WARN", color: import_chalk15.default.yellow };
96478
- return { label: "PASS", color: import_chalk15.default.green };
97516
+ if (score >= 70) return { label: "BLOCK", color: import_chalk17.default.red };
97517
+ if (score >= 60) return { label: "WARN", color: import_chalk17.default.yellow };
97518
+ return { label: "PASS", color: import_chalk17.default.green };
96479
97519
  }
96480
97520
  function truncate4(s, max) {
96481
97521
  return s.length <= max ? s : s.slice(0, max - 1) + "\u2026";
@@ -96483,12 +97523,12 @@ function truncate4(s, max) {
96483
97523
  function pad3(s, len) {
96484
97524
  return s + " ".repeat(Math.max(0, len - s.length));
96485
97525
  }
96486
- var import_chalk15, import_jsx_runtime17, SEVERITY_LABELS2, EVIDENCE_LIMIT2, ResultsView;
97526
+ var import_chalk17, import_jsx_runtime17, SEVERITY_LABELS2, EVIDENCE_LIMIT2, ResultsView;
96487
97527
  var init_ResultsView = __esm({
96488
97528
  async "src/ui/components/ResultsView.tsx"() {
96489
97529
  "use strict";
96490
97530
  await init_build2();
96491
- import_chalk15 = __toESM(require_source());
97531
+ import_chalk17 = __toESM(require_source());
96492
97532
  await init_ScoreHeader();
96493
97533
  await init_DurationLine();
96494
97534
  import_jsx_runtime17 = __toESM(require_jsx_runtime());
@@ -96514,15 +97554,15 @@ var init_ResultsView = __esm({
96514
97554
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ScoreHeader, { score: result.score, action: result.action }),
96515
97555
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Newline, {}),
96516
97556
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { children: [
96517
- import_chalk15.default.dim(`${total} package${total !== 1 ? "s" : ""} scanned`),
97557
+ import_chalk17.default.dim(`${total} package${total !== 1 ? "s" : ""} scanned`),
96518
97558
  flagged.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
96519
97559
  " ",
96520
- import_chalk15.default.yellow(`${flagged.length} flagged`),
97560
+ import_chalk17.default.yellow(`${flagged.length} flagged`),
96521
97561
  " ",
96522
- import_chalk15.default.green(`${clean.length} clean`)
97562
+ import_chalk17.default.green(`${clean.length} clean`)
96523
97563
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
96524
97564
  " ",
96525
- import_chalk15.default.green("all clean")
97565
+ import_chalk17.default.green("all clean")
96526
97566
  ] })
96527
97567
  ] }),
96528
97568
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Newline, {}),
@@ -96536,18 +97576,18 @@ var init_ResultsView = __esm({
96536
97576
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { children: [
96537
97577
  " ",
96538
97578
  color(pad3(label, 7)),
96539
- import_chalk15.default.bold(pad3(truncate4(names, 50), 52)),
96540
- import_chalk15.default.dim(`score ${rep.score}`)
97579
+ import_chalk17.default.bold(pad3(truncate4(names, 50), 52)),
97580
+ import_chalk17.default.dim(`score ${rep.score}`)
96541
97581
  ] }, group.key);
96542
97582
  }),
96543
97583
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Newline, {})
96544
97584
  ] }),
96545
97585
  clean.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { children: [
96546
- import_chalk15.default.green("\u2713"),
97586
+ import_chalk17.default.green("\u2713"),
96547
97587
  " ",
96548
- import_chalk15.default.green.bold(String(clean.length)),
97588
+ import_chalk17.default.green.bold(String(clean.length)),
96549
97589
  " ",
96550
- import_chalk15.default.dim(`package${clean.length !== 1 ? "s" : ""} passed with score 0`)
97590
+ import_chalk17.default.dim(`package${clean.length !== 1 ? "s" : ""} passed with score 0`)
96551
97591
  ] }),
96552
97592
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Newline, {}),
96553
97593
  groups.filter((g) => g.packages[0].score > 0).map((group) => {
@@ -96557,9 +97597,9 @@ var init_ResultsView = __esm({
96557
97597
  const safeVersion = result.safeVersions[rep.name];
96558
97598
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Box_default, { flexDirection: "column", children: [
96559
97599
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { children: [
96560
- import_chalk15.default.dim("\u2500\u2500"),
97600
+ import_chalk17.default.dim("\u2500\u2500"),
96561
97601
  " ",
96562
- import_chalk15.default.bold(`${names} (score: ${rep.score})`)
97602
+ import_chalk17.default.bold(`${names} (score: ${rep.score})`)
96563
97603
  ] }),
96564
97604
  group.packages.length > 3 && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { dimColor: true, children: [
96565
97605
  " Affects: ",
@@ -96581,18 +97621,18 @@ var init_ResultsView = __esm({
96581
97621
  ] }),
96582
97622
  evidenceSlice.map((ev, i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { children: [
96583
97623
  " ",
96584
- import_chalk15.default.dim(truncate4(ev, 76))
97624
+ import_chalk17.default.dim(truncate4(ev, 76))
96585
97625
  ] }, i)),
96586
97626
  overflow > 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { children: [
96587
97627
  " ",
96588
- import_chalk15.default.dim(`... and ${overflow} more`)
97628
+ import_chalk17.default.dim(`... and ${overflow} more`)
96589
97629
  ] }),
96590
97630
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Text, { children: " " })
96591
97631
  ] }, idx);
96592
97632
  }),
96593
97633
  safeVersion && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { children: [
96594
97634
  " ",
96595
- import_chalk15.default.green(`Safe version: ${rep.name}@${safeVersion}`)
97635
+ import_chalk17.default.green(`Safe version: ${rep.name}@${safeVersion}`)
96596
97636
  ] }),
96597
97637
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Newline, {})
96598
97638
  ] }, group.key);
@@ -96605,13 +97645,13 @@ var init_ResultsView = __esm({
96605
97645
  });
96606
97646
 
96607
97647
  // src/ui/components/ConfirmPrompt.tsx
96608
- var import_chalk16, import_jsx_runtime18, ConfirmPrompt;
97648
+ var import_chalk18, import_jsx_runtime18, ConfirmPrompt;
96609
97649
  var init_ConfirmPrompt = __esm({
96610
97650
  async "src/ui/components/ConfirmPrompt.tsx"() {
96611
97651
  "use strict";
96612
97652
  await init_build2();
96613
97653
  await init_build2();
96614
- import_chalk16 = __toESM(require_source());
97654
+ import_chalk18 = __toESM(require_source());
96615
97655
  import_jsx_runtime18 = __toESM(require_jsx_runtime());
96616
97656
  ConfirmPrompt = ({
96617
97657
  message,
@@ -96627,15 +97667,15 @@ var init_ConfirmPrompt = __esm({
96627
97667
  });
96628
97668
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Box_default, { flexDirection: "column", children: [
96629
97669
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Text, { children: [
96630
- import_chalk16.default.red.bold("\u26A0"),
97670
+ import_chalk18.default.red.bold("\u26A0"),
96631
97671
  " ",
96632
- import_chalk16.default.bold(message)
97672
+ import_chalk18.default.bold(message)
96633
97673
  ] }),
96634
97674
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Text, { children: [
96635
97675
  "Install anyway? [",
96636
- import_chalk16.default.bold.green("y"),
97676
+ import_chalk18.default.bold.green("y"),
96637
97677
  "es / ",
96638
- import_chalk16.default.bold.red("N"),
97678
+ import_chalk18.default.bold.red("N"),
96639
97679
  "o]"
96640
97680
  ] })
96641
97681
  ] });
@@ -97022,10 +98062,272 @@ function closestCommand(input, commands) {
97022
98062
  return bestDist <= 3 ? best : null;
97023
98063
  }
97024
98064
 
98065
+ // src/migrate.ts
98066
+ init_paths();
98067
+ import {
98068
+ existsSync as existsSync6,
98069
+ mkdirSync as mkdirSync3,
98070
+ readFileSync as readFileSync6,
98071
+ writeFileSync as writeFileSync4,
98072
+ copyFileSync,
98073
+ unlinkSync as unlinkSync2,
98074
+ rmSync as rmSync2,
98075
+ chmodSync as chmodSync2,
98076
+ statSync,
98077
+ openSync as openSync2,
98078
+ closeSync as closeSync2,
98079
+ constants as fsConstants2
98080
+ } from "node:fs";
98081
+ import { dirname as dirname6 } from "node:path";
98082
+ var README = `dependency-guardian \u2014 local state for the dg CLI.
98083
+
98084
+ config.json your account + preferences (mode 0600)
98085
+ cache/scans.sqlite 24h package-scan cache
98086
+ cache/update-check.json latest-version probe (24h TTL)
98087
+ cache/routing-hint.json scan-routing hint
98088
+ state/aliases.sh optional shell aliases (npm/pip wrappers)
98089
+ state/trial-banner throttle stamp for the trial nudge
98090
+ state/hooks-installed.json registry of git hooks dg installed
98091
+
98092
+ To remove everything dg has written to your machine, run:
98093
+ dg uninstall
98094
+
98095
+ This directory is created and managed by the dg CLI. Safe to delete.
98096
+ `;
98097
+ function ensureDir(path3, mode) {
98098
+ if (!existsSync6(path3)) {
98099
+ mkdirSync3(path3, { recursive: true, mode });
98100
+ }
98101
+ try {
98102
+ chmodSync2(path3, mode);
98103
+ } catch {
98104
+ }
98105
+ }
98106
+ function moveFile(from, to, mode) {
98107
+ const parent = dirname6(to);
98108
+ ensureDir(parent, 448);
98109
+ copyFileSync(from, to);
98110
+ try {
98111
+ chmodSync2(to, mode);
98112
+ } catch {
98113
+ }
98114
+ if (statSync(to).size !== statSync(from).size) {
98115
+ throw new Error(`size mismatch after copy: ${from} -> ${to}`);
98116
+ }
98117
+ unlinkSync2(from);
98118
+ }
98119
+ function isPidAlive(pid) {
98120
+ if (!Number.isInteger(pid) || pid <= 0) return false;
98121
+ try {
98122
+ process.kill(pid, 0);
98123
+ return true;
98124
+ } catch (e) {
98125
+ const code = e.code;
98126
+ return code === "EPERM";
98127
+ }
98128
+ }
98129
+ var LOCK_STALE_MS = 6e4;
98130
+ function acquireLock(lockPath) {
98131
+ ensureDir(dirname6(lockPath), 448);
98132
+ if (existsSync6(lockPath)) {
98133
+ let stale = false;
98134
+ try {
98135
+ const raw = readFileSync6(lockPath, "utf-8").trim();
98136
+ const data = JSON.parse(raw);
98137
+ const age = Date.now() - (data.at ?? 0);
98138
+ const dead = !isPidAlive(data.pid ?? 0);
98139
+ stale = dead || age > LOCK_STALE_MS;
98140
+ } catch {
98141
+ stale = true;
98142
+ }
98143
+ if (!stale) return "held";
98144
+ try {
98145
+ unlinkSync2(lockPath);
98146
+ } catch {
98147
+ }
98148
+ }
98149
+ let fd;
98150
+ try {
98151
+ fd = openSync2(lockPath, fsConstants2.O_CREAT | fsConstants2.O_EXCL | fsConstants2.O_WRONLY, 384);
98152
+ } catch {
98153
+ return "stale-held";
98154
+ }
98155
+ try {
98156
+ writeFileSync4(lockPath, JSON.stringify({ pid: process.pid, at: Date.now() }) + "\n");
98157
+ } catch {
98158
+ }
98159
+ return { fd, lockPath };
98160
+ }
98161
+ function releaseLock(state) {
98162
+ if (state.fd !== null) {
98163
+ try {
98164
+ closeSync2(state.fd);
98165
+ } catch {
98166
+ }
98167
+ }
98168
+ try {
98169
+ unlinkSync2(state.lockPath);
98170
+ } catch {
98171
+ }
98172
+ }
98173
+ function rollback(moves) {
98174
+ for (const m of moves) {
98175
+ try {
98176
+ if (existsSync6(m.to) && !existsSync6(m.from)) {
98177
+ copyFileSync(m.to, m.from);
98178
+ unlinkSync2(m.to);
98179
+ }
98180
+ } catch {
98181
+ }
98182
+ }
98183
+ }
98184
+ function detectLegacy() {
98185
+ const legacy = legacyPaths();
98186
+ return existsSync6(legacy.dgrc) || existsSync6(legacy.cacheSqlite) || existsSync6(legacy.routingHint) || existsSync6(legacy.updateCheck) || existsSync6(legacy.trialBannerOld) || existsSync6(legacy.aliasesShOld) || existsSync6(legacy.depGuardianDir);
98187
+ }
98188
+ function validateLegacyConfig(path3) {
98189
+ let raw;
98190
+ try {
98191
+ const st = statSync(path3);
98192
+ if (!st.isFile()) return { ok: false, reason: "not a regular file" };
98193
+ if (st.size > 1e6) return { ok: false, reason: `file too large (${st.size} bytes)` };
98194
+ raw = readFileSync6(path3, "utf-8");
98195
+ } catch (e) {
98196
+ return { ok: false, reason: e.message };
98197
+ }
98198
+ let parsed;
98199
+ try {
98200
+ const stripped = raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
98201
+ parsed = JSON.parse(stripped);
98202
+ } catch (e) {
98203
+ return { ok: false, reason: `invalid JSON: ${e.message}` };
98204
+ }
98205
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
98206
+ return { ok: false, reason: "config root is not an object" };
98207
+ }
98208
+ return { ok: true, data: parsed };
98209
+ }
98210
+ function runMigrationIfNeeded() {
98211
+ const p = dgPaths();
98212
+ if (existsSync6(p.migrationBreadcrumb)) {
98213
+ return { status: "no-op" };
98214
+ }
98215
+ if (!detectLegacy()) {
98216
+ return { status: "no-op" };
98217
+ }
98218
+ ensureDir(p.root, 448);
98219
+ const lock = acquireLock(p.migrationLock);
98220
+ if (lock === "held" || lock === "stale-held") {
98221
+ return { status: "skipped", reason: "another dg process is migrating" };
98222
+ }
98223
+ const legacy = legacyPaths();
98224
+ const moves = [];
98225
+ let movedCount = 0;
98226
+ try {
98227
+ if (existsSync6(legacy.dgrc)) {
98228
+ const validation = validateLegacyConfig(legacy.dgrc);
98229
+ if (!validation.ok) {
98230
+ releaseLock(lock);
98231
+ return {
98232
+ status: "failed",
98233
+ reason: `~/.dgrc.json could not be read (${validation.reason}). Fix or remove the file and re-run dg.`
98234
+ };
98235
+ }
98236
+ ensureDir(p.configDir, 448);
98237
+ writeFileSync4(p.config, JSON.stringify(validation.data, null, 2) + "\n", { mode: 384 });
98238
+ try {
98239
+ chmodSync2(p.config, 384);
98240
+ } catch {
98241
+ }
98242
+ try {
98243
+ unlinkSync2(legacy.dgrc);
98244
+ } catch {
98245
+ }
98246
+ moves.push({ from: legacy.dgrc, to: p.config });
98247
+ movedCount++;
98248
+ }
98249
+ ensureDir(p.cacheDir, 448);
98250
+ if (existsSync6(legacy.cacheSqlite)) {
98251
+ moveFile(legacy.cacheSqlite, p.cacheSqlite, 384);
98252
+ moves.push({ from: legacy.cacheSqlite, to: p.cacheSqlite });
98253
+ movedCount++;
98254
+ }
98255
+ if (existsSync6(legacy.routingHint)) {
98256
+ moveFile(legacy.routingHint, p.routingHint, 384);
98257
+ moves.push({ from: legacy.routingHint, to: p.routingHint });
98258
+ movedCount++;
98259
+ }
98260
+ if (existsSync6(legacy.updateCheck)) {
98261
+ moveFile(legacy.updateCheck, p.updateCheck, 384);
98262
+ moves.push({ from: legacy.updateCheck, to: p.updateCheck });
98263
+ movedCount++;
98264
+ }
98265
+ ensureDir(p.stateDir, 448);
98266
+ if (existsSync6(legacy.trialBannerOld)) {
98267
+ moveFile(legacy.trialBannerOld, p.trialBanner, 384);
98268
+ moves.push({ from: legacy.trialBannerOld, to: p.trialBanner });
98269
+ movedCount++;
98270
+ }
98271
+ if (existsSync6(legacy.aliasesShOld)) {
98272
+ moveFile(legacy.aliasesShOld, p.aliasesSh, 420);
98273
+ moves.push({ from: legacy.aliasesShOld, to: p.aliasesSh });
98274
+ movedCount++;
98275
+ }
98276
+ if (existsSync6(legacy.depGuardianDir) && legacy.depGuardianDir !== p.root) {
98277
+ const candidates = [
98278
+ { from: join(legacy.depGuardianDir, "config.json"), to: p.config, mode: 384 },
98279
+ { from: join(legacy.depGuardianDir, "cache", "scans.sqlite"), to: p.cacheSqlite, mode: 384 },
98280
+ { from: join(legacy.depGuardianDir, "cache", "update-check.json"), to: p.updateCheck, mode: 384 },
98281
+ { from: join(legacy.depGuardianDir, "cache", "routing-hint.json"), to: p.routingHint, mode: 384 },
98282
+ { from: join(legacy.depGuardianDir, "state", "aliases.sh"), to: p.aliasesSh, mode: 420 },
98283
+ { from: join(legacy.depGuardianDir, "state", "trial-banner"), to: p.trialBanner, mode: 384 },
98284
+ { from: join(legacy.depGuardianDir, "state", "hooks-installed.json"), to: p.hooksRegistry, mode: 384 },
98285
+ { from: join(legacy.depGuardianDir, "last-trial-banner"), to: p.trialBanner, mode: 384 },
98286
+ { from: join(legacy.depGuardianDir, "aliases.sh"), to: p.aliasesSh, mode: 420 }
98287
+ ];
98288
+ for (const c of candidates) {
98289
+ if (existsSync6(c.from) && !existsSync6(c.to)) {
98290
+ moveFile(c.from, c.to, c.mode);
98291
+ moves.push({ from: c.from, to: c.to });
98292
+ movedCount++;
98293
+ }
98294
+ }
98295
+ try {
98296
+ rmSync2(legacy.depGuardianDir, { recursive: true, force: true });
98297
+ } catch {
98298
+ }
98299
+ }
98300
+ try {
98301
+ if (existsSync6(legacy.cacheDir) && legacy.cacheDir !== p.root) {
98302
+ rmSync2(legacy.cacheDir, { recursive: true, force: true });
98303
+ }
98304
+ } catch {
98305
+ }
98306
+ try {
98307
+ writeFileSync4(p.readme, README, { mode: 420 });
98308
+ } catch {
98309
+ }
98310
+ writeFileSync4(p.migrationBreadcrumb, (/* @__PURE__ */ new Date()).toISOString() + "\n", { mode: 384 });
98311
+ releaseLock(lock);
98312
+ if (process.stderr.isTTY) {
98313
+ process.stderr.write(
98314
+ `dg: reorganized local state into ${p.root} (one-time, ${movedCount} file${movedCount === 1 ? "" : "s"}; see README inside).
98315
+ `
98316
+ );
98317
+ }
98318
+ return { status: "migrated", movedCount };
98319
+ } catch (e) {
98320
+ rollback(moves);
98321
+ releaseLock(lock);
98322
+ return { status: "failed", reason: e.message };
98323
+ }
98324
+ }
98325
+
97025
98326
  // src/bin.ts
97026
98327
  init_alt_screen();
97027
98328
  var CLI_VERSION = getVersion();
97028
98329
  initTelemetry(CLI_VERSION);
98330
+ runMigrationIfNeeded();
97029
98331
  function setProcessTitle(title) {
97030
98332
  try {
97031
98333
  process.title = title;
@@ -97053,8 +98355,8 @@ var isInteractive = process.stdout.isTTY === true && !process.env.CI && !process
97053
98355
  async function main() {
97054
98356
  const rawCommand = process.argv[2];
97055
98357
  if (!rawCommand || rawCommand === "help" || rawCommand === "--help" || rawCommand === "-h") {
97056
- const { USAGE: USAGE4 } = await Promise.resolve().then(() => (init_config(), config_exports));
97057
- process.stdout.write(USAGE4);
98358
+ const { USAGE: USAGE6 } = await Promise.resolve().then(() => (init_config(), config_exports));
98359
+ process.stdout.write(USAGE6);
97058
98360
  return;
97059
98361
  }
97060
98362
  if (rawCommand === "--help-all" || rawCommand === "help-all") {
@@ -97067,15 +98369,15 @@ async function main() {
97067
98369
  `);
97068
98370
  return;
97069
98371
  }
97070
- const KNOWN_COMMANDS = ["scan", "npm", "pip", "wrap", "login", "logout", "status", "hook", "update", "kitty", "help", "version", "protect", "publish-check", "cleanup"];
98372
+ const KNOWN_COMMANDS = ["init", "scan", "npm", "pip", "wrap", "login", "logout", "status", "hook", "update", "kitty", "help", "version", "protect", "publish-check", "cleanup", "uninstall"];
97071
98373
  if (rawCommand && !rawCommand.startsWith("-") && !KNOWN_COMMANDS.includes(rawCommand)) {
97072
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98374
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97073
98375
  const best = closestCommand(rawCommand, KNOWN_COMMANDS);
97074
98376
  const hint = best ? ` Did you mean '${best}'?` : "";
97075
98377
  process.stderr.write(`
97076
- ${chalk15.bold.red("Error:")} Unknown command '${rawCommand}'.${hint}
98378
+ ${chalk17.bold.red("Error:")} Unknown command '${rawCommand}'.${hint}
97077
98379
  `);
97078
- process.stderr.write(chalk15.dim(` Run 'dg --help' for available commands.
98380
+ process.stderr.write(chalk17.dim(` Run 'dg --help' for available commands.
97079
98381
 
97080
98382
  `));
97081
98383
  process.exit(1);
@@ -97111,16 +98413,16 @@ async function main() {
97111
98413
  } catch {
97112
98414
  }
97113
98415
  } else {
97114
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98416
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97115
98417
  process.stderr.write(
97116
- chalk15.dim(
98418
+ chalk17.dim(
97117
98419
  " note: --fresh skipped marking the first-run sentinel.\n The next dg command will re-trigger the wizard.\n\n"
97118
98420
  )
97119
98421
  );
97120
98422
  }
97121
98423
  } else {
97122
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97123
- process.stderr.write(chalk15.yellow(" dg kitty needs an interactive terminal.\n"));
98424
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98425
+ process.stderr.write(chalk17.yellow(" dg kitty needs an interactive terminal.\n"));
97124
98426
  }
97125
98427
  return;
97126
98428
  }
@@ -97168,11 +98470,11 @@ async function main() {
97168
98470
  if (v === "npm" || v === "pypi") ecosystem = v;
97169
98471
  }
97170
98472
  const { runNpmPublishCheck: runNpmPublishCheck2, runPypiPublishCheck: runPypiPublishCheck2, renderPublishCheckResult: renderPublishCheckResult2 } = await Promise.resolve().then(() => (init_publish_check(), publish_check_exports));
97171
- const { existsSync: existsSync16 } = await import("node:fs");
97172
- const { join: join18 } = await import("node:path");
98473
+ const { existsSync: existsSync21 } = await import("node:fs");
98474
+ const { join: join16 } = await import("node:path");
97173
98475
  if (ecosystem === "auto") {
97174
- if (existsSync16(join18(process.cwd(), "package.json"))) ecosystem = "npm";
97175
- else if (existsSync16(join18(process.cwd(), "pyproject.toml")) || existsSync16(join18(process.cwd(), "setup.py"))) ecosystem = "pypi";
98476
+ if (existsSync21(join16(process.cwd(), "package.json"))) ecosystem = "npm";
98477
+ else if (existsSync21(join16(process.cwd(), "pyproject.toml")) || existsSync21(join16(process.cwd(), "setup.py"))) ecosystem = "pypi";
97176
98478
  else {
97177
98479
  process.stderr.write("\n publish-check: no package.json, pyproject.toml, or setup.py in current directory.\n Pass --ecosystem npm|pypi explicitly if your project layout differs.\n\n");
97178
98480
  process.exit(3);
@@ -97183,9 +98485,9 @@ async function main() {
97183
98485
  if (wantJson) {
97184
98486
  process.stdout.write(JSON.stringify({ ok: false, error: out.errorMessage }) + "\n");
97185
98487
  } else {
97186
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98488
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97187
98489
  process.stderr.write(`
97188
- ${chalk15.red("publish-check error:")} ${out.errorMessage}
98490
+ ${chalk17.red("publish-check error:")} ${out.errorMessage}
97189
98491
 
97190
98492
  `);
97191
98493
  }
@@ -97227,8 +98529,8 @@ async function main() {
97227
98529
  await handleHookCommand2(process.argv.slice(3));
97228
98530
  const updateMsg2 = await updatePromise2;
97229
98531
  if (updateMsg2) {
97230
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97231
- process.stderr.write(chalk15.dim(updateMsg2));
98532
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98533
+ process.stderr.write(chalk17.dim(updateMsg2));
97232
98534
  }
97233
98535
  return;
97234
98536
  }
@@ -97241,9 +98543,21 @@ async function main() {
97241
98543
  const code = await handleCleanupCommand2(process.argv.slice(3));
97242
98544
  process.exit(code);
97243
98545
  }
98546
+ if (rawCommand === "uninstall") {
98547
+ const { handleUninstallCommand: handleUninstallCommand2 } = await Promise.resolve().then(() => (init_uninstall(), uninstall_exports));
98548
+ const code = await handleUninstallCommand2(process.argv.slice(3));
98549
+ process.exit(code);
98550
+ }
98551
+ if (rawCommand === "init") {
98552
+ const { gateOrExit: gateOrExit2 } = await Promise.resolve().then(() => (init_terms_gate(), terms_gate_exports));
98553
+ await gateOrExit2();
98554
+ const { handleInitCommand: handleInitCommand2 } = await Promise.resolve().then(() => (init_init(), init_exports3));
98555
+ const code = await handleInitCommand2(process.argv.slice(3));
98556
+ process.exit(code);
98557
+ }
97244
98558
  if (rawCommand === "logout") {
97245
98559
  const { getStoredApiKey: getStoredApiKey2, clearCredentials: clearCredentials2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
97246
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98560
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97247
98561
  const apiKey = getStoredApiKey2();
97248
98562
  if (apiKey) {
97249
98563
  const { parseConfig: parseConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
@@ -97258,7 +98572,7 @@ async function main() {
97258
98572
  }
97259
98573
  }
97260
98574
  clearCredentials2();
97261
- process.stderr.write(chalk15.green(" Logged out.\n"));
98575
+ process.stderr.write(chalk17.green(" Logged out.\n"));
97262
98576
  return;
97263
98577
  }
97264
98578
  const strictFlags = rawCommand !== "npm" && rawCommand !== "pip";
@@ -97282,8 +98596,8 @@ async function main() {
97282
98596
  await runStatic2(config3);
97283
98597
  } else {
97284
98598
  if (config3.mode === "off") {
97285
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97286
- process.stderr.write(chalk15.dim(" Dependency Guardian: mode is off \u2014 skipping.\n"));
98599
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98600
+ process.stderr.write(chalk17.dim(" Dependency Guardian: mode is off \u2014 skipping.\n"));
97287
98601
  process.exit(0);
97288
98602
  }
97289
98603
  const { getStoredApiKey: getStoredApiKey2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
@@ -97297,11 +98611,11 @@ async function main() {
97297
98611
  signal: AbortSignal.timeout(3e3)
97298
98612
  });
97299
98613
  if (resp.ok) {
97300
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98614
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97301
98615
  const data = await resp.json();
97302
98616
  const name = data.name || "authenticated";
97303
98617
  const tier = data.tier || "free";
97304
- const tierColor = tier === "free" ? chalk15.yellow(tier) : chalk15.green(tier);
98618
+ const tierColor = tier === "free" ? chalk17.yellow(tier) : chalk17.green(tier);
97305
98619
  userStatus = `${name} \xB7 ${tierColor}`;
97306
98620
  if (data.scansLimit === null || data.scansLimit === void 0) {
97307
98621
  scanUsage = "unlimited scans";
@@ -97337,8 +98651,8 @@ async function main() {
97337
98651
  }
97338
98652
  const updateMsg2 = await updatePromise;
97339
98653
  if (updateMsg2) {
97340
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97341
- process.stderr.write(chalk15.dim(updateMsg2));
98654
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98655
+ process.stderr.write(chalk17.dim(updateMsg2));
97342
98656
  }
97343
98657
  try {
97344
98658
  const { pingSetupEvent: pingSetupEvent2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
@@ -97357,8 +98671,8 @@ async function main() {
97357
98671
  }
97358
98672
  const updateMsg2 = await updatePromise;
97359
98673
  if (updateMsg2) {
97360
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97361
- process.stderr.write(chalk15.dim(updateMsg2));
98674
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98675
+ process.stderr.write(chalk17.dim(updateMsg2));
97362
98676
  }
97363
98677
  try {
97364
98678
  const { pingSetupEvent: pingSetupEvent2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
@@ -97384,8 +98698,8 @@ async function main() {
97384
98698
  }
97385
98699
  const updateMsg = await updatePromise;
97386
98700
  if (updateMsg) {
97387
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97388
- process.stderr.write(chalk15.dim(updateMsg));
98701
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98702
+ process.stderr.write(chalk17.dim(updateMsg));
97389
98703
  }
97390
98704
  try {
97391
98705
  const { pingSetupEvent: pingSetupEvent2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
@@ -97409,8 +98723,8 @@ main().catch(async (err) => {
97409
98723
  authenticated: isAuthed
97410
98724
  });
97411
98725
  await flush2();
97412
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97413
- const color = err.securityRelease ? chalk15.bold.red : chalk15.yellow;
98726
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98727
+ const color = err.securityRelease ? chalk17.bold.red : chalk17.yellow;
97414
98728
  if (process.argv.includes("--json")) {
97415
98729
  process.stdout.write(JSON.stringify({
97416
98730
  error: true,