@westbayberry/dg 1.0.60 → 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 (2) hide show
  1. package/dist/index.mjs +1982 -727
  2. package/package.json +1 -1
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 {
@@ -46158,16 +46158,8 @@ function initTelemetry(version) {
46158
46158
  dsn: CLI_DSN,
46159
46159
  release: `dg-cli@${version}`,
46160
46160
  environment: process.env.CI ? "ci" : "local",
46161
- // TODO: reduce sample rate when CLI usage exceeds ~10k DAU
46162
46161
  sampleRate: 1,
46163
46162
  sendDefaultPii: false,
46164
- // Sentry's default Node integrations patch `fs`, which collides
46165
- // with node-sqlite3-wasm's NODEFS layer — every Database open
46166
- // returned "unable to open database file" and the local scan
46167
- // cache stayed empty (0 rows even after thousands of scans).
46168
- // Opt out of the default integrations entirely; we keep crash +
46169
- // unhandled-rejection reporting via the two explicit integrations
46170
- // below, which don't touch fs.
46171
46163
  defaultIntegrations: false,
46172
46164
  integrations: [
46173
46165
  onUncaughtExceptionIntegration({ exitEvenIfOtherHandlersAreRegistered: false }),
@@ -46244,6 +46236,92 @@ var init_telemetry = __esm({
46244
46236
  }
46245
46237
  });
46246
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
+
46247
46325
  // src/auth/auth.ts
46248
46326
  var auth_exports = {};
46249
46327
  __export(auth_exports, {
@@ -46274,9 +46352,8 @@ __export(auth_exports, {
46274
46352
  scrubAuthToken: () => scrubAuthToken,
46275
46353
  setProtectConfig: () => setProtectConfig
46276
46354
  });
46277
- import { readFileSync as readFileSync2, writeFileSync, unlinkSync, existsSync as existsSync2, chmodSync, lstatSync, openSync, fchmodSync, closeSync, constants as fsConstants } from "node:fs";
46278
- import { join as join4 } from "node:path";
46279
- 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";
46280
46357
  import { spawn } from "node:child_process";
46281
46358
  import { randomUUID } from "node:crypto";
46282
46359
  function authBase() {
@@ -46354,7 +46431,13 @@ async function pollAuthSession(sessionId) {
46354
46431
  };
46355
46432
  }
46356
46433
  function configPath() {
46357
- 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
+ }
46358
46441
  }
46359
46442
  function ensureConfigPerms(path3) {
46360
46443
  let st;
@@ -46416,7 +46499,19 @@ function readConfig() {
46416
46499
  }
46417
46500
  }
46418
46501
  function writeDgrc(payload) {
46502
+ ensureConfigDir();
46419
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
+ }
46420
46515
  writeFileSync(p, JSON.stringify(payload, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
46421
46516
  try {
46422
46517
  chmodSync(p, 384);
@@ -46666,12 +46761,12 @@ function openBrowser(url) {
46666
46761
  } catch {
46667
46762
  }
46668
46763
  }
46669
- var DEFAULT_WEB_BASE, CONFIG_FILE, MAX_QUEUED_EVENTS;
46764
+ var DEFAULT_WEB_BASE, MAX_QUEUED_EVENTS;
46670
46765
  var init_auth = __esm({
46671
46766
  "src/auth/auth.ts"() {
46672
46767
  "use strict";
46768
+ init_paths();
46673
46769
  DEFAULT_WEB_BASE = "https://westbayberry.com";
46674
- CONFIG_FILE = ".dgrc.json";
46675
46770
  MAX_QUEUED_EVENTS = 50;
46676
46771
  }
46677
46772
  });
@@ -46687,9 +46782,8 @@ __export(config_exports, {
46687
46782
  });
46688
46783
  import { parseArgs } from "node:util";
46689
46784
  import { readFileSync as readFileSync3, existsSync as existsSync3 } from "node:fs";
46690
- import { join as join5, dirname as dirname3 } from "node:path";
46785
+ import { join as join6, dirname as dirname4 } from "node:path";
46691
46786
  import { fileURLToPath } from "node:url";
46692
- import { homedir as homedir2 } from "node:os";
46693
46787
  function levenshtein(a, b) {
46694
46788
  if (a === b) return 0;
46695
46789
  if (a.length === 0) return b.length;
@@ -46729,24 +46823,28 @@ function warnUnknownDgrcKeys(parsed, source) {
46729
46823
  }
46730
46824
  }
46731
46825
  function loadDgrc() {
46732
- const cwdPath = join5(process.cwd(), ".dgrc.json");
46733
- const homePath = join5(homedir2(), ".dgrc.json");
46826
+ const cwdPath = join6(process.cwd(), ".dgrc.json");
46827
+ const homePath = dgConfigPath();
46828
+ const legacyHomePath = legacyPaths().dgrc;
46734
46829
  let config3 = {};
46735
- 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;
46736
46833
  try {
46737
- const home = JSON.parse(readFileSync3(homePath, "utf-8"));
46738
- warnUnknownDgrcKeys(home, homePath);
46739
- const homeFiltered = {};
46834
+ const data = JSON.parse(readFileSync3(src, "utf-8"));
46835
+ warnUnknownDgrcKeys(data, src);
46836
+ const filtered = {};
46740
46837
  for (const k of KNOWN_DGRC_KEYS) {
46741
- if (k in home) homeFiltered[k] = home[k];
46838
+ if (k in data) filtered[k] = data[k];
46742
46839
  }
46743
- config3 = homeFiltered;
46840
+ config3 = filtered;
46841
+ break;
46744
46842
  } catch {
46745
- process.stderr.write(`Warning: Failed to parse ${homePath}, ignoring.
46843
+ process.stderr.write(`Warning: Failed to parse ${src}, ignoring.
46746
46844
  `);
46747
46845
  }
46748
46846
  }
46749
- if (existsSync3(cwdPath) && cwdPath !== homePath) {
46847
+ if (existsSync3(cwdPath) && cwdPath !== homePath && cwdPath !== legacyHomePath) {
46750
46848
  try {
46751
46849
  const cwd2 = JSON.parse(readFileSync3(cwdPath, "utf-8"));
46752
46850
  warnUnknownDgrcKeys(cwd2, cwdPath);
@@ -46774,10 +46872,7 @@ function validateApiUrl(url) {
46774
46872
  );
46775
46873
  process.exit(1);
46776
46874
  }
46777
- const isLocal = host === "localhost" || // IPv4: loopback, RFC 1918, CGNAT. Proper octet regex — not prefix matching,
46778
- // which would wrongly accept `192.1680.0.1` or `100.1.2.3` (public).
46779
- /^(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**)
46780
- 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);
46781
46876
  if (parsed.protocol !== "https:" && !isLocal) {
46782
46877
  process.stderr.write(`Error: API URL must use HTTPS (got ${parsed.protocol}). Use localhost for local testing.
46783
46878
  `);
@@ -46792,9 +46887,9 @@ function validateApiUrl(url) {
46792
46887
  }
46793
46888
  function getVersion() {
46794
46889
  try {
46795
- const thisDir = dirname3(fileURLToPath(import.meta.url));
46890
+ const thisDir = dirname4(fileURLToPath(import.meta.url));
46796
46891
  const pkg = JSON.parse(
46797
- readFileSync3(join5(thisDir, "..", "package.json"), "utf-8")
46892
+ readFileSync3(join6(thisDir, "..", "package.json"), "utf-8")
46798
46893
  );
46799
46894
  return pkg.version ?? "1.0.0";
46800
46895
  } catch {
@@ -46825,7 +46920,6 @@ function parseConfig(argv, strictFlags = true) {
46825
46920
  strict: { type: "boolean", default: false },
46826
46921
  help: { type: "boolean", default: false },
46827
46922
  version: { type: "boolean", default: false },
46828
- // Recognized but handled server-side via policy dashboard
46829
46923
  "no-config": { type: "boolean", default: false },
46830
46924
  allowlist: { type: "string" },
46831
46925
  "block-threshold": { type: "string" },
@@ -46905,6 +46999,7 @@ var init_config = __esm({
46905
46999
  "src/config.ts"() {
46906
47000
  "use strict";
46907
47001
  init_auth();
47002
+ init_paths();
46908
47003
  KNOWN_DGRC_KEYS = ["apiKey", "apiUrl", "mode", "maxPackages"];
46909
47004
  INTERNAL_DGRC_KEYS = [
46910
47005
  "deviceId",
@@ -46918,26 +47013,27 @@ var init_config = __esm({
46918
47013
  USAGE = `
46919
47014
  Dependency Guardian \u2014 Supply chain security scanner
46920
47015
 
46921
- Get protected in two steps:
46922
- dg login # free account, higher scan limits
46923
- 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\`
46924
47020
 
46925
47021
  Common commands:
46926
47022
  dg scan Audit this project's dependencies (read-only)
46927
47023
  dg status Show your coverage at a glance
46928
- dg protect Enable opt-in protection (stored in ~/.dgrc.json)
46929
- dg protect off Disable protection
46930
47024
  dg npm install <pkg> One-shot: scan + install a package
46931
47025
  dg pip install <pkg> Same, for pip
46932
47026
  dg hook install Block risky lockfile changes at commit time
46933
47027
  dg login / dg logout Manage authentication
46934
47028
  dg update Check for and install the latest dg version
47029
+ dg uninstall Remove everything dg has written locally
46935
47030
 
46936
47031
  Common flags:
46937
47032
  --json Machine-readable output (CI/scripts)
46938
47033
  --quiet Suppress login + update nudges
46939
47034
  --output, -o <file> Save scan result to a file (requires \`dg login\`)
46940
47035
 
47036
+ Local state lives in ~/.dg/.
46941
47037
  Need more? Advanced flags + env vars: \`dg --help-all\`.
46942
47038
  `.trimStart();
46943
47039
  USAGE_ALL = `
@@ -46949,22 +47045,24 @@ var init_config = __esm({
46949
47045
  dg pip install <pkg> [pip-flags]
46950
47046
 
46951
47047
  Commands:
47048
+ init Fast per-project setup (hook + protect + first scan)
46952
47049
  scan Read-only audit: scans the project's lockfiles + reports
46953
47050
  npm Protective install wrapper for npm (default mode: BLOCK)
46954
47051
  pip Protective install wrapper for pip (default mode: BLOCK)
46955
- protect Enable opt-in low-friction protection (stored in
46956
- ~/.dgrc.json + per-user shell-alias snippet \u2014 opt in
46957
- 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)
46958
47055
  protect off Disable protection in the current project
46959
47056
  hook install Install git pre-commit hook to scan lockfile changes
46960
47057
  hook uninstall Remove the pre-commit hook
46961
47058
  login Authenticate with your WestBayBerry account
46962
47059
  logout Remove saved credentials
46963
- 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)
46964
47062
  status Show coverage overview (auth, protect, hook, npm/pip, update)
46965
47063
  update Check for and install the latest version
46966
47064
  wrap Show instructions to alias npm to dg (manual)
46967
- kitty Re-run the guided tour
47065
+ kitty Re-run the first-time intro tour
46968
47066
  help Show short help (\`dg --help-all\` for this view)
46969
47067
  version Show version number
46970
47068
 
@@ -47051,7 +47149,7 @@ __export(npm_wrapper_exports, {
47051
47149
  });
47052
47150
  import { spawn as spawn2 } from "node:child_process";
47053
47151
  import { readFileSync as readFileSync4, existsSync as existsSync4, mkdtempSync, writeFileSync as writeFileSync2, rmSync } from "node:fs";
47054
- import { join as join6 } from "node:path";
47152
+ import { join as join7 } from "node:path";
47055
47153
  import { tmpdir } from "node:os";
47056
47154
  function parseNpmArgs(args) {
47057
47155
  let dgForce = false;
@@ -47260,9 +47358,9 @@ async function resolveTreeNpm(specs) {
47260
47358
  if (safe.length === 0) {
47261
47359
  return { packages: [], ok: false, errorMessage: "all specs were flag-shaped (refused)" };
47262
47360
  }
47263
- const dir = mkdtempSync(join6(tmpdir(), "dg-resolve-"));
47361
+ const dir = mkdtempSync(join7(tmpdir(), "dg-resolve-"));
47264
47362
  try {
47265
- 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}');
47266
47364
  const stdout = await new Promise((resolve3) => {
47267
47365
  const child = spawn2(
47268
47366
  "npm",
@@ -47342,7 +47440,7 @@ function pinTopLevelArgs(rawArgs, userSpecs, tree) {
47342
47440
  });
47343
47441
  }
47344
47442
  function readLockfilePins(cwd2) {
47345
- const lockPath = join6(cwd2, "package-lock.json");
47443
+ const lockPath = join7(cwd2, "package-lock.json");
47346
47444
  if (!existsSync4(lockPath)) return null;
47347
47445
  try {
47348
47446
  const lock = JSON.parse(readFileSync4(lockPath, "utf-8"));
@@ -47372,7 +47470,7 @@ function readBareInstallPackages(cwd2) {
47372
47470
  return readBareInstallPackagesTyped(cwd2).map((s) => s.spec);
47373
47471
  }
47374
47472
  function readBareInstallPackagesTyped(cwd2) {
47375
- const pkgPath = join6(cwd2, "package.json");
47473
+ const pkgPath = join7(cwd2, "package.json");
47376
47474
  if (!existsSync4(pkgPath)) return [];
47377
47475
  try {
47378
47476
  const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
@@ -48645,14 +48743,14 @@ var require_templates = __commonJS({
48645
48743
  }
48646
48744
  return results;
48647
48745
  }
48648
- function buildStyle(chalk15, styles8) {
48746
+ function buildStyle(chalk17, styles8) {
48649
48747
  const enabled = {};
48650
48748
  for (const layer of styles8) {
48651
48749
  for (const style of layer.styles) {
48652
48750
  enabled[style[0]] = layer.inverse ? null : style.slice(1);
48653
48751
  }
48654
48752
  }
48655
- let current = chalk15;
48753
+ let current = chalk17;
48656
48754
  for (const [styleName, styles9] of Object.entries(enabled)) {
48657
48755
  if (!Array.isArray(styles9)) {
48658
48756
  continue;
@@ -48664,7 +48762,7 @@ var require_templates = __commonJS({
48664
48762
  }
48665
48763
  return current;
48666
48764
  }
48667
- module2.exports = (chalk15, temporary) => {
48765
+ module2.exports = (chalk17, temporary) => {
48668
48766
  const styles8 = [];
48669
48767
  const chunks = [];
48670
48768
  let chunk = [];
@@ -48674,13 +48772,13 @@ var require_templates = __commonJS({
48674
48772
  } else if (style) {
48675
48773
  const string = chunk.join("");
48676
48774
  chunk = [];
48677
- chunks.push(styles8.length === 0 ? string : buildStyle(chalk15, styles8)(string));
48775
+ chunks.push(styles8.length === 0 ? string : buildStyle(chalk17, styles8)(string));
48678
48776
  styles8.push({ inverse, styles: parseStyle(style) });
48679
48777
  } else if (close2) {
48680
48778
  if (styles8.length === 0) {
48681
48779
  throw new Error("Found extraneous } in Chalk template literal");
48682
48780
  }
48683
- chunks.push(buildStyle(chalk15, styles8)(chunk.join("")));
48781
+ chunks.push(buildStyle(chalk17, styles8)(chunk.join("")));
48684
48782
  chunk = [];
48685
48783
  styles8.pop();
48686
48784
  } else {
@@ -48728,16 +48826,16 @@ var require_source = __commonJS({
48728
48826
  }
48729
48827
  };
48730
48828
  var chalkFactory2 = (options) => {
48731
- const chalk16 = {};
48732
- applyOptions2(chalk16, options);
48733
- chalk16.template = (...arguments_) => chalkTag(chalk16.template, ...arguments_);
48734
- Object.setPrototypeOf(chalk16, Chalk.prototype);
48735
- Object.setPrototypeOf(chalk16.template, chalk16);
48736
- 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 = () => {
48737
48835
  throw new Error("`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.");
48738
48836
  };
48739
- chalk16.template.Instance = ChalkClass;
48740
- return chalk16.template;
48837
+ chalk18.template.Instance = ChalkClass;
48838
+ return chalk18.template;
48741
48839
  };
48742
48840
  function Chalk(options) {
48743
48841
  return chalkFactory2(options);
@@ -48848,7 +48946,7 @@ var require_source = __commonJS({
48848
48946
  return openAll + string + closeAll;
48849
48947
  };
48850
48948
  var template;
48851
- var chalkTag = (chalk16, ...strings) => {
48949
+ var chalkTag = (chalk18, ...strings) => {
48852
48950
  const [firstString] = strings;
48853
48951
  if (!isArray(firstString) || !isArray(firstString.raw)) {
48854
48952
  return strings.join(" ");
@@ -48864,25 +48962,27 @@ var require_source = __commonJS({
48864
48962
  if (template === void 0) {
48865
48963
  template = require_templates();
48866
48964
  }
48867
- return template(chalk16, parts.join(""));
48965
+ return template(chalk18, parts.join(""));
48868
48966
  };
48869
48967
  Object.defineProperties(Chalk.prototype, styles8);
48870
- var chalk15 = Chalk();
48871
- chalk15.supportsColor = stdoutColor2;
48872
- chalk15.stderr = Chalk({ level: stderrColor2 ? stderrColor2.level : 0 });
48873
- chalk15.stderr.supportsColor = stderrColor2;
48874
- 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;
48875
48973
  }
48876
48974
  });
48877
48975
 
48878
48976
  // src/update-check.ts
48879
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
48880
- import { homedir as homedir3 } from "node:os";
48881
- 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";
48882
48979
  import { execFileSync } from "node:child_process";
48980
+ function cacheFile() {
48981
+ return dgCachePath("update-check.json");
48982
+ }
48883
48983
  function readCache() {
48884
48984
  try {
48885
- const raw = readFileSync5(CACHE_FILE, "utf-8");
48985
+ const raw = readFileSync5(cacheFile(), "utf-8");
48886
48986
  const data = JSON.parse(raw);
48887
48987
  if (typeof data.latest === "string" && typeof data.checkedAt === "number") {
48888
48988
  return data;
@@ -48893,7 +48993,10 @@ function readCache() {
48893
48993
  }
48894
48994
  function writeCache(entry) {
48895
48995
  try {
48896
- 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");
48897
49000
  } catch {
48898
49001
  }
48899
49002
  }
@@ -48954,45 +49057,45 @@ async function checkForUpdate(currentVersion) {
48954
49057
  return null;
48955
49058
  }
48956
49059
  async function runUpdate(currentVersion) {
48957
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
48958
- 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"));
48959
49062
  const latest = await fetchLatestVersion();
48960
49063
  if (!latest) {
48961
- process.stderr.write(chalk15.red(" Could not reach npm registry.\n"));
49064
+ process.stderr.write(chalk17.red(" Could not reach npm registry.\n"));
48962
49065
  process.exit(1);
48963
49066
  }
48964
49067
  if (!isNewer(latest, currentVersion)) {
48965
49068
  process.stderr.write(
48966
- chalk15.green(` Already on latest version (${currentVersion}).
49069
+ chalk17.green(` Already on latest version (${currentVersion}).
48967
49070
  `)
48968
49071
  );
48969
49072
  return;
48970
49073
  }
48971
- process.stderr.write(chalk15.dim(` Installing ${PKG_NAME}@${latest}...
49074
+ process.stderr.write(chalk17.dim(` Installing ${PKG_NAME}@${latest}...
48972
49075
  `));
48973
49076
  try {
48974
49077
  execFileSync("npm", ["install", "-g", `${PKG_NAME}@${latest}`], { stdio: "inherit" });
48975
49078
  writeCache({ latest, checkedAt: Date.now() });
48976
49079
  process.stderr.write(
48977
- chalk15.green(`
49080
+ chalk17.green(`
48978
49081
  Updated ${currentVersion} \u2192 ${latest}
48979
49082
  `)
48980
49083
  );
48981
49084
  } catch {
48982
49085
  process.stderr.write(
48983
- chalk15.red(`
49086
+ chalk17.red(`
48984
49087
  Update failed. Try manually: npm i -g ${PKG_NAME}@${latest}
48985
49088
  `)
48986
49089
  );
48987
49090
  process.exit(1);
48988
49091
  }
48989
49092
  }
48990
- var PKG_NAME, CACHE_FILE, CHECK_INTERVAL_MS;
49093
+ var PKG_NAME, CHECK_INTERVAL_MS;
48991
49094
  var init_update_check = __esm({
48992
49095
  "src/update-check.ts"() {
48993
49096
  "use strict";
49097
+ init_paths();
48994
49098
  PKG_NAME = "@westbayberry/dg";
48995
- CACHE_FILE = join7(homedir3(), ".dg-update-check.json");
48996
49099
  CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
48997
49100
  }
48998
49101
  });
@@ -49961,12 +50064,12 @@ var require_react_development = __commonJS({
49961
50064
  var SEPARATOR = ".";
49962
50065
  var SUBSEPARATOR = ":";
49963
50066
  function escape2(key) {
49964
- var escapeRegex = /[=:]/g;
50067
+ var escapeRegex2 = /[=:]/g;
49965
50068
  var escaperLookup = {
49966
50069
  "=": "=0",
49967
50070
  ":": "=2"
49968
50071
  };
49969
- var escapedString = key.replace(escapeRegex, function(match) {
50072
+ var escapedString = key.replace(escapeRegex2, function(match) {
49970
50073
  return escaperLookup[match];
49971
50074
  });
49972
50075
  return "$" + escapedString;
@@ -79989,10 +80092,10 @@ var init_source = __esm({
79989
80092
  object.level = options.level === void 0 ? colorLevel : options.level;
79990
80093
  };
79991
80094
  chalkFactory = (options) => {
79992
- const chalk15 = (...strings) => strings.join(" ");
79993
- applyOptions(chalk15, options);
79994
- Object.setPrototypeOf(chalk15, createChalk.prototype);
79995
- return chalk15;
80095
+ const chalk17 = (...strings) => strings.join(" ");
80096
+ applyOptions(chalk17, options);
80097
+ Object.setPrototypeOf(chalk17, createChalk.prototype);
80098
+ return chalk17;
79996
80099
  };
79997
80100
  Object.setPrototypeOf(createChalk.prototype, Function.prototype);
79998
80101
  for (const [styleName, style] of Object.entries(ansi_styles_default3)) {
@@ -83078,22 +83181,18 @@ var init_skip_list = __esm({
83078
83181
  "src/discover/skip-list.ts"() {
83079
83182
  "use strict";
83080
83183
  SKIP_DIRS = /* @__PURE__ */ new Set([
83081
- // Package caches
83082
83184
  "node_modules",
83083
83185
  ".venv",
83084
83186
  "venv",
83085
83187
  "__pycache__",
83086
83188
  "vendor",
83087
83189
  "bower_components",
83088
- // VCS + tooling
83089
83190
  ".git",
83090
83191
  ".tox",
83091
83192
  ".eggs",
83092
- // Build outputs
83093
83193
  "dist",
83094
83194
  "build",
83095
83195
  "target",
83096
- // target: Rust + Java
83097
83196
  ".next",
83098
83197
  ".nuxt",
83099
83198
  ".turbo",
@@ -83102,24 +83201,17 @@ var init_skip_list = __esm({
83102
83201
  ".parcel-cache",
83103
83202
  ".gradle",
83104
83203
  ".terraform",
83105
- // Test caches
83106
83204
  "coverage",
83107
83205
  ".cache",
83108
83206
  ".pytest_cache",
83109
83207
  ".mypy_cache",
83110
- // Project-local: validation outputs, fixture sandboxes, archives,
83111
- // and temp scratch (no project lives inside a tmp dir; corpus dumps
83112
- // for validation runs frequently land in tmp subtrees with 100k+
83113
- // entries that lock the walker on their readdir).
83114
83208
  "validation-results",
83115
83209
  "test-fixtures",
83116
83210
  "fixtures",
83117
83211
  "archive",
83118
83212
  "tmp",
83119
83213
  "temp",
83120
- // Vendored Ansible collections (WBB monorepo specific; harmless on others)
83121
83214
  "ansible_collections",
83122
- // IDE / editor
83123
83215
  ".idea",
83124
83216
  ".vscode"
83125
83217
  ]);
@@ -83220,7 +83312,7 @@ async function discoverProjectsAsync(root, opts = {}) {
83220
83312
  const nonEmpty = projects.filter((p) => p.packageCount > 0);
83221
83313
  return nonEmpty.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
83222
83314
  }
83223
- function discoverProjects(root) {
83315
+ function discoverProjects(_root) {
83224
83316
  throw new Error(
83225
83317
  "discoverProjects (sync) is no longer implemented. Use `await discoverProjectsAsync(root)` instead. See dependency-guardian/cli/src/discover/walker.ts."
83226
83318
  );
@@ -83283,27 +83375,137 @@ var init_walker = __esm({
83283
83375
  }
83284
83376
  });
83285
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
+
83286
83485
  // src/commands/hook.ts
83287
83486
  var hook_exports = {};
83288
83487
  __export(hook_exports, {
83289
83488
  detectHookFramework: () => detectHookFramework,
83290
83489
  handleHookCommand: () => handleHookCommand,
83291
83490
  installHookForFramework: () => installHookForFramework,
83292
- previewHookInstall: () => previewHookInstall
83491
+ previewHookInstall: () => previewHookInstall,
83492
+ stripDgFromHookFile: () => stripDgFromHookFile,
83493
+ uninstallHookFromRepo: () => uninstallHookFromRepo,
83494
+ verifyHookInstalled: () => verifyHookInstalled
83293
83495
  });
83294
83496
  import { execFileSync as execFileSync2 } from "node:child_process";
83295
83497
  import {
83296
- existsSync as existsSync6,
83498
+ existsSync as existsSync9,
83297
83499
  lstatSync as lstatSync2,
83298
- readFileSync as readFileSync7,
83299
- writeFileSync as writeFileSync4,
83300
- mkdirSync,
83301
- chmodSync as chmodSync2,
83302
- unlinkSync as unlinkSync2
83500
+ readFileSync as readFileSync9,
83501
+ writeFileSync as writeFileSync6,
83502
+ mkdirSync as mkdirSync5,
83503
+ chmodSync as chmodSync4,
83504
+ unlinkSync as unlinkSync3
83303
83505
  } from "node:fs";
83304
- 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";
83305
83507
  function assertSafeWriteTarget(target) {
83306
- const parent = dirname4(target);
83508
+ const parent = dirname8(target);
83307
83509
  try {
83308
83510
  const parentStat = lstatSync2(parent);
83309
83511
  if (parentStat.isSymbolicLink()) {
@@ -83331,7 +83533,7 @@ function assertSafeWriteTarget(target) {
83331
83533
  }
83332
83534
  function safeWriteFileSync(target, content) {
83333
83535
  assertSafeWriteTarget(target);
83334
- writeFileSync4(target, content);
83536
+ writeFileSync6(target, content);
83335
83537
  }
83336
83538
  function findHooksDir() {
83337
83539
  try {
@@ -83360,8 +83562,8 @@ function detectHookFramework(repoRoot) {
83360
83562
  const lefthookConfigYaml = join9(root, "lefthook.yaml");
83361
83563
  const hooksDir = findHooksDir();
83362
83564
  const bareHook = join9(hooksDir, "pre-commit");
83363
- if (existsSync6(huskyHook)) {
83364
- const content = readFileSync7(huskyHook, "utf-8");
83565
+ if (existsSync9(huskyHook)) {
83566
+ const content = readFileSync9(huskyHook, "utf-8");
83365
83567
  return {
83366
83568
  framework: "husky",
83367
83569
  targetFile: huskyHook,
@@ -83369,8 +83571,8 @@ function detectHookFramework(repoRoot) {
83369
83571
  };
83370
83572
  }
83371
83573
  for (const cfg of [lefthookConfig, lefthookConfigYaml]) {
83372
- if (existsSync6(cfg)) {
83373
- const content = readFileSync7(cfg, "utf-8");
83574
+ if (existsSync9(cfg)) {
83575
+ const content = readFileSync9(cfg, "utf-8");
83374
83576
  const installed2 = /^\s+dependency-guardian\s*:/m.test(content);
83375
83577
  return {
83376
83578
  framework: "lefthook",
@@ -83380,8 +83582,8 @@ function detectHookFramework(repoRoot) {
83380
83582
  }
83381
83583
  }
83382
83584
  let installed = false;
83383
- if (existsSync6(bareHook)) {
83384
- installed = readFileSync7(bareHook, "utf-8").includes(HOOK_MARKER);
83585
+ if (existsSync9(bareHook)) {
83586
+ installed = readFileSync9(bareHook, "utf-8").includes(HOOK_MARKER);
83385
83587
  }
83386
83588
  return {
83387
83589
  framework: "bare",
@@ -83389,6 +83591,37 @@ function detectHookFramework(repoRoot) {
83389
83591
  alreadyInstalled: installed
83390
83592
  };
83391
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
+ }
83392
83625
  function previewHookInstall(info) {
83393
83626
  if (info.alreadyInstalled) {
83394
83627
  return `(already installed in ${info.targetFile} \u2014 no changes needed)`;
@@ -83402,7 +83635,7 @@ ${HUSKY_SNIPPET}`;
83402
83635
 
83403
83636
  ${LEFTHOOK_ENTRY}`;
83404
83637
  case "bare":
83405
- if (existsSync6(info.targetFile)) {
83638
+ if (existsSync9(info.targetFile)) {
83406
83639
  return `Will append to existing hook at ${info.targetFile}:
83407
83640
  ${HOOK_SECTION}`;
83408
83641
  }
@@ -83411,21 +83644,21 @@ ${HOOK_SCRIPT}`;
83411
83644
  }
83412
83645
  }
83413
83646
  function installHuskyHook(targetFile) {
83414
- const existing = readFileSync7(targetFile, "utf-8");
83647
+ const existing = readFileSync9(targetFile, "utf-8");
83415
83648
  if (existing.includes(HOOK_MARKER)) {
83416
83649
  process.stderr.write(" Hook already installed in Husky.\n");
83417
83650
  return;
83418
83651
  }
83419
83652
  safeWriteFileSync(targetFile, existing.trimEnd() + "\n" + HUSKY_SNIPPET);
83420
83653
  try {
83421
- chmodSync2(targetFile, 493);
83654
+ chmodSync4(targetFile, 493);
83422
83655
  } catch {
83423
83656
  }
83424
83657
  process.stderr.write(` Appended Dependency Guardian to ${targetFile}
83425
83658
  `);
83426
83659
  }
83427
83660
  function installLefthookHook(targetFile) {
83428
- const existing = readFileSync7(targetFile, "utf-8");
83661
+ const existing = readFileSync9(targetFile, "utf-8");
83429
83662
  if (/^\s+dependency-guardian\s*:/m.test(existing)) {
83430
83663
  process.stderr.write(" Hook already installed in lefthook.\n");
83431
83664
  return;
@@ -83457,16 +83690,16 @@ function installLefthookHook(targetFile) {
83457
83690
  `);
83458
83691
  }
83459
83692
  function installBareHook(targetFile) {
83460
- const hooksDir = dirname4(targetFile);
83461
- mkdirSync(hooksDir, { recursive: true });
83462
- if (existsSync6(targetFile)) {
83463
- 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");
83464
83697
  if (existing.includes(HOOK_MARKER)) {
83465
83698
  process.stderr.write(" Hook already installed.\n");
83466
83699
  return;
83467
83700
  }
83468
83701
  safeWriteFileSync(targetFile, existing.trimEnd() + "\n" + HOOK_SECTION);
83469
- chmodSync2(targetFile, 493);
83702
+ chmodSync4(targetFile, 493);
83470
83703
  process.stderr.write(
83471
83704
  ` Appended Dependency Guardian hook to existing ${targetFile}
83472
83705
  `
@@ -83474,7 +83707,7 @@ function installBareHook(targetFile) {
83474
83707
  return;
83475
83708
  }
83476
83709
  safeWriteFileSync(targetFile, HOOK_SCRIPT);
83477
- chmodSync2(targetFile, 493);
83710
+ chmodSync4(targetFile, 493);
83478
83711
  process.stderr.write(` Installed git pre-commit hook at ${targetFile}
83479
83712
  `);
83480
83713
  }
@@ -83494,71 +83727,107 @@ function installHookForFramework(info) {
83494
83727
  async function installHook() {
83495
83728
  const info = detectHookFramework();
83496
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
+ }
83497
83740
  try {
83498
83741
  const { pingSetupEvent: pingSetupEvent2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
83499
83742
  await pingSetupEvent2("hook_installed");
83500
83743
  } catch {
83501
83744
  }
83502
83745
  }
83503
- 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();
83504
83789
  const hooksDir = findHooksDir();
83505
83790
  const hookPath = join9(hooksDir, "pre-commit");
83506
- try {
83507
- const root = findRepoRoot();
83508
- const huskyPath = join9(root, ".husky", "pre-commit");
83509
- if (existsSync6(huskyPath)) {
83510
- const content2 = readFileSync7(huskyPath, "utf-8");
83511
- if (content2.includes(HOOK_MARKER)) {
83512
- const startIdx2 = content2.indexOf(MARKER_START);
83513
- const endIdx2 = content2.indexOf(MARKER_END);
83514
- if (startIdx2 !== -1 && endIdx2 !== -1) {
83515
- const before = content2.slice(0, startIdx2).trimEnd();
83516
- const after = content2.slice(endIdx2 + MARKER_END.length).trimStart();
83517
- const remaining = (before + (after ? "\n" + after : "")).trimEnd() + "\n";
83518
- safeWriteFileSync(huskyPath, remaining);
83519
- process.stderr.write(
83520
- ` Removed Dependency Guardian section from ${huskyPath}
83521
- `
83522
- );
83523
- return;
83524
- }
83525
- }
83526
- }
83527
- for (const cfg of [join9(root, "lefthook.yml"), join9(root, "lefthook.yaml")]) {
83528
- if (existsSync6(cfg)) {
83529
- const content2 = readFileSync7(cfg, "utf-8");
83530
- if (/^\s+dependency-guardian\s*:/m.test(content2)) {
83531
- const stripped = content2.replace(
83532
- /^\s+dependency-guardian\s*:\s*\n(?:\s{6,}.*\n)+/gm,
83533
- ""
83534
- );
83535
- safeWriteFileSync(cfg, stripped);
83536
- process.stderr.write(
83537
- ` Removed Dependency Guardian section from ${cfg}
83538
- `
83539
- );
83540
- return;
83541
- }
83542
- }
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 };
83543
83818
  }
83544
- } catch {
83545
83819
  }
83546
- if (!existsSync6(hookPath)) {
83547
- process.stderr.write(" No hook to remove.\n");
83548
- return;
83820
+ if (!existsSync9(hookPath)) {
83821
+ return { status: "not-found", hookPath, repoPath: root };
83549
83822
  }
83550
- const content = readFileSync7(hookPath, "utf-8");
83823
+ const content = readFileSync9(hookPath, "utf-8");
83551
83824
  if (!content.includes(HOOK_MARKER)) {
83552
- process.stderr.write(
83553
- " No Dependency Guardian hook found in pre-commit.\n"
83554
- );
83555
- return;
83825
+ return { status: "not-found", hookPath, repoPath: root };
83556
83826
  }
83557
83827
  if (content.trimStart().startsWith("#!/bin/sh\n" + HOOK_MARKER)) {
83558
- unlinkSync2(hookPath);
83559
- process.stderr.write(` Removed pre-commit hook at ${hookPath}
83560
- `);
83561
- return;
83828
+ unlinkSync3(hookPath);
83829
+ removeEntry(root, hookPath);
83830
+ return { status: "removed", hookPath, repoPath: root };
83562
83831
  }
83563
83832
  const startIdx = content.indexOf(MARKER_START);
83564
83833
  const endIdx = content.indexOf(MARKER_END);
@@ -83567,19 +83836,32 @@ async function uninstallHook() {
83567
83836
  const after = content.slice(endIdx + MARKER_END.length).trimStart();
83568
83837
  const remaining = (before + (after ? "\n" + after : "")).trimEnd() + "\n";
83569
83838
  safeWriteFileSync(hookPath, remaining);
83570
- process.stderr.write(
83571
- ` Removed Dependency Guardian section from ${hookPath}
83572
- `
83573
- );
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
+ `);
83574
83853
  return;
83575
83854
  }
83576
- unlinkSync2(hookPath);
83577
- 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}
83578
83857
  `);
83579
- try {
83580
- const { pingSetupEvent: pingSetupEvent2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
83581
- await pingSetupEvent2("hook_uninstalled");
83582
- } 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");
83583
83865
  }
83584
83866
  }
83585
83867
  async function handleHookCommand(args) {
@@ -83593,11 +83875,6 @@ async function handleHookCommand(args) {
83593
83875
  await installHook();
83594
83876
  } else if (subcommand === "uninstall") {
83595
83877
  await uninstallHook();
83596
- try {
83597
- const { pingSetupEvent: pingSetupEvent2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
83598
- await pingSetupEvent2("hook_uninstalled");
83599
- } catch {
83600
- }
83601
83878
  } else {
83602
83879
  process.stderr.write(` Unknown hook command: ${subcommand}
83603
83880
  `);
@@ -83615,6 +83892,7 @@ var HOOK_MARKER, MARKER_START, MARKER_END, LEFTHOOK_MARKER, HOOK_SCRIPT, HOOK_SE
83615
83892
  var init_hook = __esm({
83616
83893
  "src/commands/hook.ts"() {
83617
83894
  "use strict";
83895
+ init_hooks_registry();
83618
83896
  HOOK_MARKER = "# dependency-guardian-hook";
83619
83897
  MARKER_START = "# dependency-guardian-hook-start";
83620
83898
  MARKER_END = "# dependency-guardian-hook-end";
@@ -83983,14 +84261,14 @@ var init_parse_package_json = __esm({
83983
84261
 
83984
84262
  // src/lockfile/index.ts
83985
84263
  import { execFileSync as execFileSync3 } from "node:child_process";
83986
- 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";
83987
84265
  import { join as join10 } from "node:path";
83988
84266
  function readFileSafe(path3) {
83989
- const size = statSync(path3).size;
84267
+ const size = statSync2(path3).size;
83990
84268
  if (size > MAX_LOCKFILE_BYTES) {
83991
84269
  throw new Error(`Lockfile too large (${(size / 1024 / 1024).toFixed(0)} MB, max 50 MB): ${path3}`);
83992
84270
  }
83993
- return readFileSync8(path3, "utf-8");
84271
+ return readFileSync10(path3, "utf-8");
83994
84272
  }
83995
84273
  function discoverChanges(cwd2, config3) {
83996
84274
  if (config3.workspace) {
@@ -84000,7 +84278,7 @@ function discoverChanges(cwd2, config3) {
84000
84278
  const pythonDepFiles = ["requirements.txt", "Pipfile.lock", "poetry.lock"];
84001
84279
  let pythonPackages = [];
84002
84280
  for (const pyFile of pythonDepFiles) {
84003
- if (existsSync7(join10(cwd2, pyFile))) {
84281
+ if (existsSync10(join10(cwd2, pyFile))) {
84004
84282
  const pyPkgs = parsePythonDepFile(cwd2, pyFile);
84005
84283
  for (const p of pyPkgs) {
84006
84284
  if (p.version === "latest") continue;
@@ -84026,7 +84304,7 @@ function discoverChanges(cwd2, config3) {
84026
84304
  const headParsed = parseLockfileByType(headContent, lockfileInfo.type);
84027
84305
  const directDeps = getDirectDeps(cwd2);
84028
84306
  if (config3.baseLockfile) {
84029
- if (!existsSync7(config3.baseLockfile)) {
84307
+ if (!existsSync10(config3.baseLockfile)) {
84030
84308
  throw new Error(`Base lockfile not found: ${config3.baseLockfile}`);
84031
84309
  }
84032
84310
  const baseContent = readFileSafe(config3.baseLockfile);
@@ -84052,7 +84330,7 @@ function discoverChanges(cwd2, config3) {
84052
84330
  };
84053
84331
  }
84054
84332
  const pkgJsonPath = join10(cwd2, "package.json");
84055
- if (existsSync7(pkgJsonPath)) {
84333
+ if (existsSync10(pkgJsonPath)) {
84056
84334
  const headPkgJson = readFileSafe(pkgJsonPath);
84057
84335
  const basePkgJson = getGitBaseFile(cwd2, "package.json");
84058
84336
  if (basePkgJson !== null) {
@@ -84096,7 +84374,7 @@ function findLockfile(cwd2) {
84096
84374
  ];
84097
84375
  for (const [name, type] of candidates) {
84098
84376
  const p = join10(cwd2, name);
84099
- if (existsSync7(p)) return { path: p, type };
84377
+ if (existsSync10(p)) return { path: p, type };
84100
84378
  }
84101
84379
  return null;
84102
84380
  }
@@ -84300,16 +84578,20 @@ var init_sanitize = __esm({
84300
84578
  // src/cache/local_cache.ts
84301
84579
  import * as path2 from "node:path";
84302
84580
  import * as fs2 from "node:fs";
84303
- import * as os5 from "node:os";
84304
84581
  import { createRequire } from "node:module";
84305
84582
  function defaultDbPath() {
84306
- const baseDir = process.env.DG_CACHE_DIR ?? path2.join(os5.homedir(), ".dg");
84307
- return path2.join(baseDir, "cache.sqlite");
84583
+ return dgCachePath("scans.sqlite");
84308
84584
  }
84309
84585
  function tryLoadDriver() {
84310
84586
  try {
84311
84587
  const mod = moduleRequire("node-sqlite3-wasm");
84312
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
+ }
84313
84595
  return (p) => new Ctor(p);
84314
84596
  } catch (err) {
84315
84597
  if (process.env.DG_PERF) {
@@ -84326,6 +84608,7 @@ var moduleRequire, DEFAULT_MAX_ROWS, DEFAULT_TTL_SECONDS, LocalScanCache, _share
84326
84608
  var init_local_cache = __esm({
84327
84609
  "src/cache/local_cache.ts"() {
84328
84610
  "use strict";
84611
+ init_paths();
84329
84612
  moduleRequire = createRequire(import.meta.url);
84330
84613
  DEFAULT_MAX_ROWS = 1e5;
84331
84614
  DEFAULT_TTL_SECONDS = 24 * 60 * 60;
@@ -84353,6 +84636,15 @@ var init_local_cache = __esm({
84353
84636
  this.db.exec("PRAGMA busy_timeout = 5000");
84354
84637
  this.initSchema();
84355
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();
84356
84648
  } catch (err) {
84357
84649
  this.disabled = true;
84358
84650
  if (process.env.DG_PERF) {
@@ -84361,6 +84653,61 @@ var init_local_cache = __esm({
84361
84653
  this.db = null;
84362
84654
  }
84363
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
+ }
84364
84711
  initSchema() {
84365
84712
  if (!this.db) return;
84366
84713
  this.db.exec(`
@@ -84387,21 +84734,9 @@ var init_local_cache = __esm({
84387
84734
  expires_at = excluded.expires_at
84388
84735
  `);
84389
84736
  }
84390
- /**
84391
- * Collision-free key for the in-memory Map returned by `lookup()`. Uses
84392
- * a NUL separator (illegal in npm + PyPI package names — JS strings
84393
- * handle NUL fine; the v1 `${name}@${version}` form was string-collidable
84394
- * when names contained "@"). Callers must use this same helper to look
84395
- * entries up — never reconstruct by hand.
84396
- */
84397
84737
  static cacheKey(name, version) {
84398
84738
  return `${name}\0${version}`;
84399
84739
  }
84400
- /**
84401
- * Batch lookup. Returns a Map keyed by the v2 key (`name\0version`). Expired
84402
- * rows are excluded by the WHERE clause; the caller can treat absence as
84403
- * "go ask the server."
84404
- */
84405
84740
  lookup(packages) {
84406
84741
  const out = /* @__PURE__ */ new Map();
84407
84742
  if (this.disabled || !this.db || packages.length === 0) return out;
@@ -84442,11 +84777,6 @@ var init_local_cache = __esm({
84442
84777
  }
84443
84778
  return out;
84444
84779
  }
84445
- /**
84446
- * Persist fresh scan results. Uses a single transaction so all rows commit
84447
- * together (fast + crash-safe). Inserts past maxRows trigger LRU eviction
84448
- * on the oldest fetched_at.
84449
- */
84450
84780
  insert(rows) {
84451
84781
  if (this.disabled || !this.db || !this.insertStmt || rows.length === 0) return;
84452
84782
  const now = Date.now();
@@ -84534,6 +84864,7 @@ __export(client_exports, {
84534
84864
  ANON_BATCH_SIZE: () => ANON_BATCH_SIZE,
84535
84865
  APIError: () => APIError,
84536
84866
  BATCH_SIZE: () => BATCH_SIZE,
84867
+ BatchPoolPartialError: () => BatchPoolPartialError,
84537
84868
  ClientOutdatedError: () => ClientOutdatedError,
84538
84869
  TrialExhaustedError: () => TrialExhaustedError,
84539
84870
  callAnalyzeAPI: () => callAnalyzeAPI,
@@ -84611,23 +84942,32 @@ async function callAnalyzeAPI(packages, config3, onProgress) {
84611
84942
  batches.push(uncached.slice(i, i + batchSize));
84612
84943
  }
84613
84944
  const tTotal = Date.now();
84614
- const results = await runBatchPool(
84615
- batches,
84616
- (batch, onRateLimit, onPackageProgress) => callBatchWithRetry(batch, config3, onRateLimit, onPackageProgress),
84617
- {
84618
- onBatchDone: (_batchIndex, batch, completed) => {
84619
- if (adjustedOnProgress) {
84620
- adjustedOnProgress(completed, uncached.length, batch.map((p) => p.name));
84621
- }
84622
- },
84623
- onPackageProgress: (completed, latestName) => {
84624
- if (adjustedOnProgress) {
84625
- adjustedOnProgress(completed, uncached.length, latestName ? [latestName] : []);
84626
- }
84627
- },
84628
- 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);
84629
84968
  }
84630
- );
84969
+ throw e instanceof BatchPoolPartialError ? e.cause : e;
84970
+ }
84631
84971
  if (process.env.DG_PERF) console.error(`[CLI-PERF] total: ${uncached.length} uncached packages \u2192 ${Date.now() - tTotal}ms`);
84632
84972
  serverResponse = mergeResponses(results);
84633
84973
  }
@@ -84772,8 +85112,8 @@ async function runBatchPool(batches, fn, opts) {
84772
85112
  if (catchup > 0) completedPackages += catchup;
84773
85113
  if (opts.onBatchDone) opts.onBatchDone(i, batch, completedPackages);
84774
85114
  } catch (e) {
84775
- firstError = e;
84776
- throw e;
85115
+ if (firstError === null) firstError = e;
85116
+ return;
84777
85117
  }
84778
85118
  }
84779
85119
  }
@@ -84781,7 +85121,10 @@ async function runBatchPool(batches, fn, opts) {
84781
85121
  const workers = [];
84782
85122
  for (let s = 0; s < workerCount; s++) workers.push(worker(s));
84783
85123
  await Promise.allSettled(workers);
84784
- 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
+ }
84785
85128
  return results;
84786
85129
  }
84787
85130
  async function callBatchWithRetry(packages, config3, onRateLimit, onPackageProgress) {
@@ -84835,7 +85178,6 @@ function mergeResponses(results) {
84835
85178
  action,
84836
85179
  packages: allPackages,
84837
85180
  safeVersions,
84838
- // Sum, not max — batches run sequentially, total wall-clock is the sum
84839
85181
  durationMs: results.reduce((s, r) => s + (r.durationMs || 0), 0)
84840
85182
  };
84841
85183
  }
@@ -84987,26 +85329,52 @@ async function readNdjsonAnalyzeResponse(response, onPackageProgress) {
84987
85329
  async function callPyPIAnalyzeAPI(packages, config3, onProgress) {
84988
85330
  const batchSize = config3.apiKey ? BATCH_SIZE : ANON_BATCH_SIZE;
84989
85331
  _currentScanId = randomUUID2();
84990
- if (packages.length <= batchSize) {
84991
- 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`);
84992
85337
  }
84993
- const batches = [];
84994
- for (let i = 0; i < packages.length; i += batchSize) {
84995
- 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);
84996
85342
  }
84997
- const tTotal = Date.now();
84998
- const results = await runBatchPool(
84999
- batches,
85000
- (batch) => callPyPIBatch(batch, config3),
85001
- {
85002
- onBatchDone: (_i, _batch, completed) => {
85003
- if (onProgress) onProgress(completed, packages.length);
85004
- },
85005
- 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));
85006
85351
  }
85007
- );
85008
- if (process.env.DG_PERF) console.error(`[CLI-PERF] pypi total: ${packages.length} packages \u2192 ${Date.now() - tTotal}ms`);
85009
- 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);
85010
85378
  }
85011
85379
  async function callPyPIBatch(packages, config3) {
85012
85380
  const url = `${config3.apiUrl}/v1/pypi/analyze`;
@@ -85069,7 +85437,7 @@ async function callPyPIBatch(packages, config3) {
85069
85437
  checkVersionFloor(raw);
85070
85438
  return sanitizeResponse(raw);
85071
85439
  }
85072
- 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;
85073
85441
  var init_client3 = __esm({
85074
85442
  "src/api/client.ts"() {
85075
85443
  "use strict";
@@ -85110,6 +85478,14 @@ var init_client3 = __esm({
85110
85478
  RETRY_DELAY_MS = 5e3;
85111
85479
  DEFAULT_BATCH_CONCURRENCY = 4;
85112
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
+ };
85113
85489
  }
85114
85490
  });
85115
85491
 
@@ -85246,11 +85622,11 @@ __export(protect_exports, {
85246
85622
  stripAliasSourceFromRc: () => stripAliasSourceFromRc,
85247
85623
  suggestedShellRc: () => suggestedShellRc
85248
85624
  });
85249
- import { existsSync as existsSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync5, unlinkSync as unlinkSync3, mkdirSync as mkdirSync3, lstatSync as lstatSync3, appendFileSync } from "node:fs";
85250
- import { join as join12, dirname as dirname6 } from "node:path";
85251
- 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";
85252
85628
  function aliasFile() {
85253
- return join12(homedir5(), ".dependency-guardian", "aliases.sh");
85629
+ return dgStatePath("aliases.sh");
85254
85630
  }
85255
85631
  function safeIsRegularFile(path3) {
85256
85632
  try {
@@ -85261,7 +85637,7 @@ function safeIsRegularFile(path3) {
85261
85637
  }
85262
85638
  }
85263
85639
  function safeWriteFileSync2(target, content, mode = 420) {
85264
- const parent = dirname6(target);
85640
+ const parent = dirname10(target);
85265
85641
  try {
85266
85642
  const parentStat = lstatSync3(parent);
85267
85643
  if (parentStat.isSymbolicLink()) {
@@ -85278,34 +85654,23 @@ function safeWriteFileSync2(target, content, mode = 420) {
85278
85654
  } catch (e) {
85279
85655
  if (e instanceof Error && e.message.startsWith("refusing")) throw e;
85280
85656
  }
85281
- writeFileSync5(target, content, { encoding: "utf-8", mode });
85657
+ writeFileSync7(target, content, { encoding: "utf-8", mode });
85282
85658
  }
85283
- function readProtectConfig(cwd2) {
85284
- const stored = getProtectConfig();
85285
- if (stored) return stored;
85286
- const legacyPath = join12(cwd2, LEGACY_PROJECT_FILE);
85287
- if (safeIsRegularFile(legacyPath)) {
85288
- try {
85289
- const data = JSON.parse(readFileSync9(legacyPath, "utf-8"));
85290
- if (typeof data === "object" && data !== null) {
85291
- const cfg = data;
85292
- setProtectConfig(cfg);
85293
- return cfg;
85294
- }
85295
- } catch {
85296
- }
85297
- }
85298
- return null;
85659
+ function readProtectConfig(_cwd) {
85660
+ return getProtectConfig() ?? null;
85299
85661
  }
85300
85662
  function writeProtectConfig(_cwd, cfg) {
85301
85663
  setProtectConfig(cfg);
85302
85664
  }
85303
85665
  function writeAliasSnippet() {
85304
- const dir = dirname6(aliasFile());
85305
- mkdirSync3(dir, { recursive: true });
85666
+ const dir = dirname10(aliasFile());
85667
+ mkdirSync7(dir, { recursive: true, mode: 448 });
85306
85668
  safeWriteFileSync2(aliasFile(), ALIAS_SNIPPET);
85307
85669
  return aliasFile();
85308
85670
  }
85671
+ function legacyAliasPath() {
85672
+ return legacyPaths().aliasesShOld;
85673
+ }
85309
85674
  function suggestedShellRc() {
85310
85675
  const shell = (process.env.SHELL ?? "").toLowerCase();
85311
85676
  if (shell.includes("zsh")) return { rc: "~/.zshrc", shellName: "zsh" };
@@ -85314,19 +85679,19 @@ function suggestedShellRc() {
85314
85679
  return { rc: "~/.zshrc", shellName: "your shell rc" };
85315
85680
  }
85316
85681
  function expandHome(path3) {
85317
- if (path3.startsWith("~/")) return join12(homedir5(), path3.slice(2));
85318
- if (path3 === "~") return homedir5();
85682
+ if (path3.startsWith("~/")) return join11(homedir3(), path3.slice(2));
85683
+ if (path3 === "~") return homedir3();
85319
85684
  return path3;
85320
85685
  }
85321
85686
  function appendAliasSourceToRc(rcPathRaw, sourceLine) {
85322
85687
  const rcPath = expandHome(rcPathRaw);
85323
- const home = homedir5();
85688
+ const home = homedir3();
85324
85689
  if (!rcPath.startsWith(home + "/") && rcPath !== home) {
85325
85690
  return { status: "failed", rcPath, reason: "rc path is outside the home directory" };
85326
85691
  }
85327
85692
  try {
85328
- const parent = dirname6(rcPath);
85329
- if (existsSync9(parent)) {
85693
+ const parent = dirname10(rcPath);
85694
+ if (existsSync12(parent)) {
85330
85695
  const parentStat = lstatSync3(parent);
85331
85696
  if (parentStat.isSymbolicLink()) {
85332
85697
  return { status: "failed", rcPath, reason: "parent directory is a symlink" };
@@ -85335,7 +85700,7 @@ function appendAliasSourceToRc(rcPathRaw, sourceLine) {
85335
85700
  } catch (e) {
85336
85701
  return { status: "failed", rcPath, reason: e.message };
85337
85702
  }
85338
- if (existsSync9(rcPath)) {
85703
+ if (existsSync12(rcPath)) {
85339
85704
  let st;
85340
85705
  try {
85341
85706
  st = lstatSync3(rcPath);
@@ -85349,8 +85714,8 @@ function appendAliasSourceToRc(rcPathRaw, sourceLine) {
85349
85714
  return { status: "failed", rcPath, reason: "rc path is not a regular file" };
85350
85715
  }
85351
85716
  try {
85352
- const existing = readFileSync9(rcPath, "utf-8");
85353
- 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())) {
85354
85719
  return { status: "already-present", rcPath };
85355
85720
  }
85356
85721
  } catch (e) {
@@ -85359,8 +85724,9 @@ function appendAliasSourceToRc(rcPathRaw, sourceLine) {
85359
85724
  }
85360
85725
  try {
85361
85726
  const block = `
85362
- # Added by dg kitty \u2014 auto-scan via Dependency Guardian.
85727
+ ${RC_SENTINEL_OPEN}
85363
85728
  ${sourceLine}
85729
+ ${RC_SENTINEL_CLOSE}
85364
85730
  `;
85365
85731
  appendFileSync(rcPath, block, { encoding: "utf-8" });
85366
85732
  return { status: "appended", rcPath };
@@ -85393,12 +85759,14 @@ function aliasSnippetExists() {
85393
85759
  }
85394
85760
  function aliasSnippetSourced() {
85395
85761
  const candidates = [".zshrc", ".bashrc", ".bash_profile", ".profile"];
85762
+ const aliasFilePath = aliasFile();
85763
+ const legacyAlias = legacyAliasPath();
85396
85764
  for (const rc of candidates) {
85397
- const path3 = join12(homedir5(), rc);
85765
+ const path3 = join11(homedir3(), rc);
85398
85766
  if (!safeIsRegularFile(path3)) continue;
85399
85767
  try {
85400
- const content = readFileSync9(path3, "utf-8");
85401
- 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")) {
85402
85770
  return { sourced: true, rcFile: path3 };
85403
85771
  }
85404
85772
  } catch {
@@ -85409,33 +85777,51 @@ function aliasSnippetSourced() {
85409
85777
  function stripAliasSourceFromRc() {
85410
85778
  const candidates = [".zshrc", ".bashrc", ".bash_profile", ".profile"];
85411
85779
  const modified = [];
85780
+ const manualReviewNeeded = [];
85412
85781
  const aliasFilePath = aliasFile();
85782
+ const legacyAlias = legacyAliasPath();
85413
85783
  for (const rc of candidates) {
85414
- const path3 = join12(homedir5(), rc);
85784
+ const path3 = join11(homedir3(), rc);
85415
85785
  if (!safeIsRegularFile(path3)) continue;
85416
85786
  let content;
85417
85787
  try {
85418
- content = readFileSync9(path3, "utf-8");
85788
+ content = readFileSync11(path3, "utf-8");
85419
85789
  } catch {
85420
85790
  continue;
85421
85791
  }
85422
- 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);
85423
85798
  continue;
85424
85799
  }
85425
- const lines = content.split("\n");
85426
- const out = [];
85427
- for (let i = 0; i < lines.length; i++) {
85428
- const line = lines[i];
85429
- const isSourceLine = line.includes(aliasFilePath) || line.includes(".dependency-guardian/aliases.sh");
85430
- if (isSourceLine && (line.trimStart().startsWith("source ") || line.trimStart().startsWith(". "))) {
85431
- if (out.length > 0 && out[out.length - 1].trim().startsWith("# Added by dg kitty")) {
85432
- 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;
85433
85819
  }
85434
- continue;
85820
+ out.push(line);
85435
85821
  }
85436
- out.push(line);
85822
+ next = out.join("\n");
85437
85823
  }
85438
- const next = out.join("\n");
85824
+ next = next.replace(/\n{3,}/g, "\n\n");
85439
85825
  if (next === content) continue;
85440
85826
  try {
85441
85827
  safeWriteFileSync2(path3, next, 420);
@@ -85443,7 +85829,10 @@ function stripAliasSourceFromRc() {
85443
85829
  } catch {
85444
85830
  }
85445
85831
  }
85446
- return modified;
85832
+ return { modified, manualReviewNeeded };
85833
+ }
85834
+ function escapeRegex(s) {
85835
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
85447
85836
  }
85448
85837
  async function runProtectInit(cwd2, opts = {}) {
85449
85838
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -85471,7 +85860,7 @@ async function runProtectInit(cwd2, opts = {}) {
85471
85860
  }
85472
85861
  process.stderr.write("\n");
85473
85862
  process.stderr.write(import_chalk4.default.green(" \u2713 ") + import_chalk4.default.bold("Dependency Guardian protection: ENABLED") + "\n");
85474
- process.stderr.write(import_chalk4.default.dim(" Stored in ~/.dgrc.json.\n"));
85863
+ process.stderr.write(import_chalk4.default.dim(" Stored under ~/.dg/.\n"));
85475
85864
  process.stderr.write(import_chalk4.default.dim(` Mode: ${cfg.mode}${cfg.strict ? " \xB7 strict" : ""}
85476
85865
  `));
85477
85866
  process.stderr.write("\n");
@@ -85505,7 +85894,7 @@ async function runProtectOff(cwd2, opts = {}) {
85505
85894
  if (getProtectConfig()) {
85506
85895
  try {
85507
85896
  clearProtectConfig();
85508
- 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");
85509
85898
  removed += 1;
85510
85899
  try {
85511
85900
  const { pingSetupEvent: pingSetupEvent2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
@@ -85519,21 +85908,10 @@ async function runProtectOff(cwd2, opts = {}) {
85519
85908
  } else {
85520
85909
  process.stderr.write(import_chalk4.default.dim(" Protect config already cleared.\n"));
85521
85910
  }
85522
- const legacyPath = join12(cwd2, LEGACY_PROJECT_FILE);
85523
- if (safeIsRegularFile(legacyPath)) {
85524
- try {
85525
- unlinkSync3(legacyPath);
85526
- process.stderr.write(import_chalk4.default.green(" \u2713 ") + `Removed legacy ${legacyPath}
85527
- `);
85528
- removed += 1;
85529
- } catch (e) {
85530
- process.stderr.write(import_chalk4.default.red(` Could not remove ${legacyPath}: ${e.message}
85531
- `));
85532
- }
85533
- }
85911
+ void cwd2;
85534
85912
  if (opts.removeShell && aliasSnippetExists()) {
85535
85913
  try {
85536
- unlinkSync3(aliasFile());
85914
+ unlinkSync5(aliasFile());
85537
85915
  process.stderr.write(import_chalk4.default.green(" \u2713 ") + `Removed ${aliasFile()}
85538
85916
  `);
85539
85917
  removed += 1;
@@ -85541,12 +85919,18 @@ async function runProtectOff(cwd2, opts = {}) {
85541
85919
  process.stderr.write(import_chalk4.default.red(` Could not remove ${aliasFile()}: ${e.message}
85542
85920
  `));
85543
85921
  }
85544
- const stripped = stripAliasSourceFromRc();
85545
- for (const p of stripped) {
85922
+ const stripResult = stripAliasSourceFromRc();
85923
+ for (const p of stripResult.modified) {
85546
85924
  process.stderr.write(import_chalk4.default.green(" \u2713 ") + `Removed source line from ${p}
85547
85925
  `);
85548
85926
  removed += 1;
85549
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
+ }
85550
85934
  } else if (aliasSnippetExists()) {
85551
85935
  process.stderr.write(
85552
85936
  import_chalk4.default.dim(` Shell snippet at ${aliasFile()} kept (other projects may use it).
@@ -85604,13 +85988,15 @@ function runInit(flags, cwd2) {
85604
85988
  }
85605
85989
  return runProtectInit(cwd2, opts);
85606
85990
  }
85607
- 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;
85608
85992
  var init_protect = __esm({
85609
85993
  "src/commands/protect.ts"() {
85610
85994
  "use strict";
85611
85995
  import_chalk4 = __toESM(require_source());
85612
85996
  init_auth();
85613
- LEGACY_PROJECT_FILE = ".dgprotect.json";
85997
+ init_paths();
85998
+ RC_SENTINEL_OPEN = "# >>> dependency-guardian (managed) >>>";
85999
+ RC_SENTINEL_CLOSE = "# <<< dependency-guardian (managed) <<<";
85614
86000
  DEFAULT_CONFIG = {
85615
86001
  version: 1,
85616
86002
  enabled: true,
@@ -85619,7 +86005,7 @@ var init_protect = __esm({
85619
86005
  };
85620
86006
  ALIAS_SNIPPET = `# Dependency Guardian \u2014 opt-in shell aliases
85621
86007
  # Source this from your shell rc to route npm/pip through dg:
85622
- # echo 'source ~/.dependency-guardian/aliases.sh' >> ~/.zshrc # or ~/.bashrc
86008
+ # echo 'source ~/.dg/aliases.sh' >> ~/.zshrc # or ~/.bashrc
85623
86009
  # To turn it off, remove that line.
85624
86010
 
85625
86011
  # Only alias if a real dg binary is on PATH.
@@ -85646,7 +86032,7 @@ unset __dg_bin
85646
86032
 
85647
86033
  Usage:
85648
86034
  dg protect Enable protection (same as \`dg protect init\`)
85649
- dg protect off Disable protection in this project
86035
+ dg protect off Disable protection
85650
86036
 
85651
86037
  Flags (for init):
85652
86038
  --no-shell Skip generating the shell-alias snippet
@@ -85654,10 +86040,10 @@ unset __dg_bin
85654
86040
  --strict Refuse partial-coverage scans + suppress scripts
85655
86041
 
85656
86042
  What \`dg protect\` does:
85657
- 1. Writes \`.dgprotect.json\` in the project (commit it).
85658
- 2. Generates an opt-in alias snippet at
85659
- \`~/.dependency-guardian/aliases.sh\` that you can \`source\` from your
85660
- 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.
85661
86047
  3. Tells you exactly what line to add (and how to undo it).
85662
86048
 
85663
86049
  After protection is on, \`npm install\` and \`pip install\` route through
@@ -85665,7 +86051,7 @@ unset __dg_bin
85665
86051
 
85666
86052
  To see current coverage status, run \`dg status\`.
85667
86053
  To disable, run \`dg protect off\`. (Pass --remove-shell to also delete
85668
- the per-user alias snippet.)
86054
+ the alias snippet and strip the source line from your shell rc.)
85669
86055
 
85670
86056
  Why this is opt-in:
85671
86057
  DG never silently hijacks global npm/pip. Every change is explicit
@@ -85675,8 +86061,8 @@ unset __dg_bin
85675
86061
  });
85676
86062
 
85677
86063
  // src/ui/hooks/useInit.ts
85678
- import { statSync as statSync2 } from "node:fs";
85679
- import { join as join13 } from "node:path";
86064
+ import { statSync as statSync3 } from "node:fs";
86065
+ import { join as join12 } from "node:path";
85680
86066
  function initialState(dryRun, firstRun) {
85681
86067
  return {
85682
86068
  phase: firstRun ? "greet" : "detect",
@@ -85707,7 +86093,7 @@ function initialState(dryRun, firstRun) {
85707
86093
  function detectCwdIsProject(cwd2) {
85708
86094
  for (const marker of PROJECT_MARKERS) {
85709
86095
  try {
85710
- const stat = statSync2(join13(cwd2, marker));
86096
+ const stat = statSync3(join12(cwd2, marker));
85711
86097
  if (stat.isFile()) return true;
85712
86098
  } catch {
85713
86099
  }
@@ -85720,7 +86106,6 @@ function reducer(state, action) {
85720
86106
  `);
85721
86107
  }
85722
86108
  switch (action.type) {
85723
- // ── First-run guided tour transitions ──────────────────────────────────
85724
86109
  case "welcome_yes":
85725
86110
  return { ...state, phase: "value_prop", engaged: true };
85726
86111
  case "welcome_no": {
@@ -85800,6 +86185,9 @@ function reducer(state, action) {
85800
86185
  if (!state.protectInfo) {
85801
86186
  return { ...state, phase: "pitch_app" };
85802
86187
  }
86188
+ if (state.protectInfo.alreadySourced) {
86189
+ return { ...state, phase: "pitch_app" };
86190
+ }
85803
86191
  return { ...state, phase: "optional_protection" };
85804
86192
  case "optional_protection_yes":
85805
86193
  return { ...state, phase: "optional_protection_result" };
@@ -85814,7 +86202,6 @@ function reducer(state, action) {
85814
86202
  return { ...state, phase: "pitch_app" };
85815
86203
  case "pitch_acked":
85816
86204
  return { ...state, phase: "done" };
85817
- // ── Existing Phase 1b transitions (unchanged) ──────────────────────────
85818
86205
  case "detected":
85819
86206
  return {
85820
86207
  ...state,
@@ -85902,9 +86289,7 @@ function goBack(state) {
85902
86289
  value_prop: "greet",
85903
86290
  demo_result: "value_prop",
85904
86291
  confirm_wrap: "verify_hook",
85905
- // legacy only
85906
86292
  show_app_link: "confirm_scan"
85907
- // legacy only
85908
86293
  };
85909
86294
  if (state.phase === "confirm_hook") {
85910
86295
  return { ...state, phase: state.firstRun ? "scenario" : "detect" };
@@ -85959,7 +86344,7 @@ function useInit(opts = {}) {
85959
86344
  dispatch({
85960
86345
  type: "hook_installed",
85961
86346
  protectInfo: {
85962
- rcLine: "source ~/.dependency-guardian/aliases.sh",
86347
+ rcLine: "source ~/.dg/state/aliases.sh",
85963
86348
  rcFile: "~/.zshrc",
85964
86349
  shellName: "zsh",
85965
86350
  alreadySourced: false
@@ -85973,8 +86358,15 @@ function useInit(opts = {}) {
85973
86358
  let protectInfo;
85974
86359
  try {
85975
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
+ }
85976
86368
  void Promise.resolve().then(() => (init_auth(), auth_exports)).then((m) => m.enqueueSetupEvent("protect_enabled"));
85977
- protectInfo = { rcLine, rcFile, shellName, alreadySourced };
86369
+ protectInfo = { rcLine, rcFile, shellName, alreadySourced: sourced };
85978
86370
  } catch {
85979
86371
  }
85980
86372
  dispatch({ type: "hook_installed", protectInfo });
@@ -85990,26 +86382,25 @@ function useInit(opts = {}) {
85990
86382
  dispatch({ type: "hook_verified", durationMs: 0 });
85991
86383
  return;
85992
86384
  }
86385
+ if (!state.hookInfo) {
86386
+ dispatch({ type: "hook_verify_failed", error: "hook framework not detected" });
86387
+ return;
86388
+ }
86389
+ const t0 = Date.now();
85993
86390
  try {
85994
- const cwd3 = opts.cwd ?? process.cwd();
85995
- const config3 = parseConfig([process.argv[0] ?? "node", "init"], false);
85996
- const outcome = await scanFn(cwd3, config3);
85997
- if (outcome.status === "error" && outcome.error) {
85998
- dispatch({
85999
- type: "hook_verify_failed",
86000
- error: outcome.message ?? outcome.error.message
86001
- });
86391
+ const result = verifyHookInstalled(state.hookInfo);
86392
+ if (!result.ok) {
86393
+ dispatch({ type: "hook_verify_failed", error: result.reason ?? "verification failed" });
86002
86394
  return;
86003
86395
  }
86004
- const ms = outcome.result?.durationMs ?? 0;
86005
- dispatch({ type: "hook_verified", durationMs: ms });
86396
+ dispatch({ type: "hook_verified", durationMs: Date.now() - t0 });
86006
86397
  } catch (e) {
86007
86398
  dispatch({
86008
86399
  type: "hook_verify_failed",
86009
86400
  error: e instanceof Error ? e.message : String(e)
86010
86401
  });
86011
86402
  }
86012
- }, [state.dryRun, opts.cwd, scanFn]);
86403
+ }, [state.dryRun, state.hookInfo]);
86013
86404
  const runScan = (0, import_react22.useCallback)(async () => {
86014
86405
  if (state.dryRun) {
86015
86406
  dispatch({
@@ -86031,11 +86422,23 @@ function useInit(opts = {}) {
86031
86422
  const config3 = parseConfig([process.argv[0] ?? "node", "init", "--scan-all"], false);
86032
86423
  const projects = state.projects.length > 0 ? state.projects : [{ path: opts.cwd ?? process.cwd() }];
86033
86424
  const allOutcomes = [];
86034
- 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];
86035
86437
  if (process.env.DG_DEBUG_WIZARD) process.stderr.write(`[wizard] scanning ${proj.path}
86036
86438
  `);
86037
86439
  const outcome = await scanFn(proj.path, config3, (p) => {
86038
- 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) });
86039
86442
  });
86040
86443
  if (process.env.DG_DEBUG_WIZARD) process.stderr.write(`[wizard] scan done ${proj.path} status=${outcome.status}
86041
86444
  `);
@@ -86286,8 +86689,8 @@ __export(ink_controls_exports, {
86286
86689
  setInkClear: () => setInkClear,
86287
86690
  setInkInstance: () => setInkInstance
86288
86691
  });
86289
- import { resolve as resolve2, dirname as dirname7 } from "node:path";
86290
- 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";
86291
86694
  function locateInkInstancesPath() {
86292
86695
  let cur;
86293
86696
  try {
@@ -86297,13 +86700,13 @@ function locateInkInstancesPath() {
86297
86700
  }
86298
86701
  while (cur && cur !== "/" && cur !== ".") {
86299
86702
  const candidate = resolve2(cur, "node_modules", "ink", "build", "instances.js");
86300
- if (existsSync10(candidate)) return candidate;
86301
- const parent = dirname7(cur);
86703
+ if (existsSync13(candidate)) return candidate;
86704
+ const parent = dirname11(cur);
86302
86705
  if (parent === cur) break;
86303
86706
  cur = parent;
86304
86707
  }
86305
86708
  const cwdCandidate = resolve2(process.cwd(), "node_modules", "ink", "build", "instances.js");
86306
- if (existsSync10(cwdCandidate)) return cwdCandidate;
86709
+ if (existsSync13(cwdCandidate)) return cwdCandidate;
86307
86710
  return null;
86308
86711
  }
86309
86712
  async function initInkInstances() {
@@ -89122,7 +89525,16 @@ var init_demo_data = __esm({
89122
89525
  "Postinstall Curl Pipe": "Downloads code and runs it",
89123
89526
  "Suspicious Network": "Talks to suspicious servers",
89124
89527
  "Typosquatting": "Pretending to be a popular package",
89125
- "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"
89126
89538
  };
89127
89539
  }
89128
89540
  });
@@ -89204,15 +89616,17 @@ var init_ScanResultCard = __esm({
89204
89616
  packages.length === 1 ? packages[0].findings.slice(0, 3).map((f, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { dimColor: true, children: [
89205
89617
  " \u2022 ",
89206
89618
  friendlyCategory(f.category),
89207
- f.title ? ` \u2014 ${f.title}` : ""
89208
- ] }, `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: [
89209
89621
  " \u2022 ",
89210
89622
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { bold: true, children: pkg.name }),
89211
89623
  " ",
89212
89624
  pkg.score,
89213
- "/100"
89625
+ "/100",
89626
+ pkg.findings && pkg.findings[0] ? ` \xB7 ${friendlyCategory(pkg.findings[0].category)}` : ""
89214
89627
  ] }, pkg.name)),
89215
- 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
89216
89630
  ] }) : verdict !== "EMPTY" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { children: [
89217
89631
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: verdictColor, bold: true, children: verdict }),
89218
89632
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: " \xB7 " }),
@@ -89432,7 +89846,7 @@ var init_InitApp = __esm({
89432
89846
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Spinner2, { label: "Creating login session..." });
89433
89847
  case "ready":
89434
89848
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { flexDirection: "column", children: [
89435
- /* @__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" }),
89436
89850
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: " " }),
89437
89851
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: "Authenticate your account at:" }),
89438
89852
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "cyan", children: state.verifyUrl }),
@@ -89441,7 +89855,7 @@ var init_InitApp = __esm({
89441
89855
  ] });
89442
89856
  case "waiting":
89443
89857
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { flexDirection: "column", children: [
89444
- /* @__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" }),
89445
89859
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: " " }),
89446
89860
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { dimColor: true, children: state.verifyUrl }),
89447
89861
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: " " }),
@@ -89453,7 +89867,7 @@ var init_InitApp = __esm({
89453
89867
  "\u2713 Signed in as ",
89454
89868
  state.email
89455
89869
  ] }),
89456
- /* @__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" })
89457
89871
  ] });
89458
89872
  case "already_logged_in":
89459
89873
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { flexDirection: "column", children: [
@@ -90123,7 +90537,7 @@ var init_InitApp = __esm({
90123
90537
  ] }, "p1a"),
90124
90538
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Text, { children: [
90125
90539
  installCmd,
90126
- " them. That's where most attacks land."
90540
+ " them. That's where most attacks happen."
90127
90541
  ] }, "p1b"),
90128
90542
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: " " }, "sp2"),
90129
90543
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FlowDiagram, {}, "diagram"),
@@ -90251,7 +90665,7 @@ var init_InitApp = __esm({
90251
90665
  mood: "scanning",
90252
90666
  color: "cyan",
90253
90667
  scrollable: false,
90254
- body: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Spinner2, { label: "Testing it..." })
90668
+ body: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Spinner2, { label: "Verifying the hook..." })
90255
90669
  }
90256
90670
  );
90257
90671
  case "confirm_wrap": {
@@ -90472,7 +90886,7 @@ var init_InitApp = __esm({
90472
90886
  );
90473
90887
  case "optional_protection": {
90474
90888
  const p = state.protectInfo;
90475
- const rcLine = p?.rcLine ?? "source ~/.dependency-guardian/aliases.sh";
90889
+ const rcLine = p?.rcLine ?? "source ~/.dg/state/aliases.sh";
90476
90890
  const rcFile = p?.rcFile ?? "~/.zshrc";
90477
90891
  const shellLabel = p?.shellName && p.shellName !== "your shell rc" ? p.shellName : null;
90478
90892
  const stackBoth = state.stackNpm && state.stackPypi;
@@ -90611,7 +91025,7 @@ var init_InitApp = __esm({
90611
91025
  );
90612
91026
  }
90613
91027
  if (r.kind === "failed") {
90614
- const rcLine = state.protectInfo?.rcLine ?? "source ~/.dependency-guardian/aliases.sh";
91028
+ const rcLine = state.protectInfo?.rcLine ?? "source ~/.dg/state/aliases.sh";
90615
91029
  const rcFile = state.protectInfo?.rcFile ?? "~/.zshrc";
90616
91030
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
90617
91031
  Scene,
@@ -90946,7 +91360,14 @@ var init_first_run = __esm({
90946
91360
  "status",
90947
91361
  "protect",
90948
91362
  "login",
90949
- "cleanup"
91363
+ "cleanup",
91364
+ "uninstall",
91365
+ "init",
91366
+ "scan",
91367
+ "npm",
91368
+ "pip",
91369
+ "wrap",
91370
+ "publish-check"
90950
91371
  ]);
90951
91372
  }
90952
91373
  });
@@ -91148,7 +91569,7 @@ __export(pip_wrapper_exports, {
91148
91569
  runPip: () => runPip
91149
91570
  });
91150
91571
  import { spawn as spawn3 } from "node:child_process";
91151
- import { existsSync as existsSync11 } from "node:fs";
91572
+ import { existsSync as existsSync14 } from "node:fs";
91152
91573
  function parsePipArgs(args) {
91153
91574
  let dgForce = false;
91154
91575
  let dgForceReason;
@@ -91247,7 +91668,7 @@ function parseRequirementsFile(filePath) {
91247
91668
  return parseRequirementsFileInternal(filePath, /* @__PURE__ */ new Set());
91248
91669
  }
91249
91670
  function parseRequirementsFileInternal(filePath, visited) {
91250
- if (!existsSync11(filePath)) return [];
91671
+ if (!existsSync14(filePath)) return [];
91251
91672
  let absPath;
91252
91673
  try {
91253
91674
  absPath = __require("node:path").resolve(filePath);
@@ -91565,7 +91986,7 @@ var init_pip_wrapper = __esm({
91565
91986
  });
91566
91987
 
91567
91988
  // src/security/artifact_integrity.ts
91568
- function normalize2(h, algo) {
91989
+ function normalize3(h, algo) {
91569
91990
  if (!h) return void 0;
91570
91991
  const trimmed = h.trim();
91571
91992
  const re2 = algo === "sha512" ? SHA512_HEX : SHA256_HEX;
@@ -91582,8 +92003,8 @@ function compareArtifactHashes(pairs) {
91582
92003
  };
91583
92004
  for (const p of pairs) {
91584
92005
  const key = `${p.name}@${p.version}`;
91585
- const api = normalize2(p.apiHash, p.algorithm);
91586
- const local = normalize2(p.localHash, p.algorithm);
92006
+ const api = normalize3(p.apiHash, p.algorithm);
92007
+ const local = normalize3(p.localHash, p.algorithm);
91587
92008
  const apiRaw = (p.apiHash ?? "").toString();
91588
92009
  const localRaw = (p.localHash ?? "").toString();
91589
92010
  const apiPresent = apiRaw.length > 0;
@@ -91709,8 +92130,8 @@ var FileSavePrompt_exports = {};
91709
92130
  __export(FileSavePrompt_exports, {
91710
92131
  FileSavePrompt: () => FileSavePrompt
91711
92132
  });
91712
- import { existsSync as existsSync12, readdirSync, writeFileSync as writeFileSync6 } from "node:fs";
91713
- 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";
91714
92135
  function listDirectory(dir) {
91715
92136
  try {
91716
92137
  const raw = readdirSync(dir, { withFileTypes: true });
@@ -91718,7 +92139,7 @@ function listDirectory(dir) {
91718
92139
  if (a.isDirectory !== b.isDirectory) return a.isDirectory ? -1 : 1;
91719
92140
  return a.name.localeCompare(b.name);
91720
92141
  });
91721
- if (dirname8(dir) !== dir) {
92142
+ if (dirname12(dir) !== dir) {
91722
92143
  entries.unshift({ name: "..", isDirectory: true });
91723
92144
  }
91724
92145
  return entries;
@@ -91810,13 +92231,13 @@ var init_FileSavePrompt = __esm({
91810
92231
  if (state.focus === "filename") {
91811
92232
  if (key.return) {
91812
92233
  const fullName = ensureJsonExtension(state.filename);
91813
- const fullPath = join14(state.directory, fullName);
91814
- if (!state.confirmOverwrite && existsSync12(fullPath)) {
92234
+ const fullPath = join13(state.directory, fullName);
92235
+ if (!state.confirmOverwrite && existsSync15(fullPath)) {
91815
92236
  dispatch({ type: "CONFIRM_OVERWRITE" });
91816
92237
  return;
91817
92238
  }
91818
92239
  try {
91819
- writeFileSync6(fullPath, json + "\n");
92240
+ writeFileSync8(fullPath, json + "\n");
91820
92241
  } catch (err) {
91821
92242
  const msg = err instanceof Error ? err.message : String(err);
91822
92243
  dispatch({ type: "SET_ERROR", error: `Write failed: ${msg}` });
@@ -92068,25 +92489,24 @@ __export(static_output_exports, {
92068
92489
  runStaticNpm: () => runStaticNpm,
92069
92490
  runStaticPip: () => runStaticPip
92070
92491
  });
92071
- import { writeFileSync as writeFileSync7, readFileSync as readFileSync10, existsSync as existsSync13, mkdirSync as mkdirSync4, lstatSync as lstatSync4 } from "node:fs";
92072
- import { resolve as resolvePath, join as join15, dirname as dirname9 } from "node:path";
92073
- 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";
92074
92494
  function loadPackageLockJson(cwd2 = process.cwd()) {
92075
- const path3 = resolvePath(cwd2, "package-lock.json");
92076
- if (!existsSync13(path3)) return null;
92495
+ const path3 = resolvePath2(cwd2, "package-lock.json");
92496
+ if (!existsSync16(path3)) return null;
92077
92497
  try {
92078
92498
  const st = lstatSync4(path3);
92079
92499
  if (!st.isFile()) return null;
92080
92500
  if (st.size > MAX_PACKAGE_LOCK_BYTES) return null;
92081
- return JSON.parse(readFileSync10(path3, "utf8"));
92501
+ return JSON.parse(readFileSync12(path3, "utf8"));
92082
92502
  } catch {
92083
92503
  return null;
92084
92504
  }
92085
92505
  }
92086
92506
  function writeJsonFile(filepath, json) {
92087
- const resolved = resolvePath(filepath);
92507
+ const resolved = resolvePath2(filepath);
92088
92508
  try {
92089
- writeFileSync7(resolved, json + "\n");
92509
+ writeFileSync9(resolved, json + "\n");
92090
92510
  process.stderr.write(import_chalk8.default.green(` \u2713 Saved to ${resolved}
92091
92511
 
92092
92512
  `));
@@ -92156,12 +92576,12 @@ function printTrialBanner(result, config3) {
92156
92576
  if (config3?.json || config3?.ci || config3?.quiet) return;
92157
92577
  if (process.env.CI === "1" || process.env.CI === "true") return;
92158
92578
  try {
92159
- const stampPath = join15(homedir6(), ".dependency-guardian", "last-trial-banner");
92160
- 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;
92161
92581
  const now = Date.now();
92162
92582
  if (now - lastMs < NUDGE_INTERVAL_MS) return;
92163
- mkdirSync4(dirname9(stampPath), { recursive: true });
92164
- writeFileSync7(stampPath, String(now) + "\n");
92583
+ mkdirSync8(dirname13(stampPath), { recursive: true, mode: 448 });
92584
+ writeFileSync9(stampPath, String(now) + "\n");
92165
92585
  } catch {
92166
92586
  return;
92167
92587
  }
@@ -93100,6 +93520,7 @@ var init_static_output = __esm({
93100
93520
  "src/formatters/static-output.ts"() {
93101
93521
  "use strict";
93102
93522
  import_chalk8 = __toESM(require_source());
93523
+ init_paths();
93103
93524
  init_client3();
93104
93525
  init_auth();
93105
93526
  init_lockfile();
@@ -93117,13 +93538,13 @@ __export(status_exports, {
93117
93538
  handleStatusCommand: () => handleStatusCommand
93118
93539
  });
93119
93540
  async function handleStatusCommand(cliVersion) {
93120
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
93541
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
93121
93542
  const apiKey = getStoredApiKey();
93122
93543
  process.stderr.write("\n");
93123
- 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"));
93124
93545
  if (!apiKey) {
93125
93546
  process.stderr.write(
93126
- ` ${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")
93127
93548
  );
93128
93549
  } else {
93129
93550
  let tierLine = "signed in";
@@ -93149,29 +93570,29 @@ async function handleStatusCommand(cliVersion) {
93149
93570
  } catch {
93150
93571
  tierLine = "signed in (could not reach API)";
93151
93572
  }
93152
- 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"));
93153
93574
  }
93154
93575
  const cwd2 = process.cwd();
93155
93576
  const protect = readProtectConfig(cwd2);
93156
93577
  if (protect && protect.enabled) {
93157
93578
  process.stderr.write(
93158
- ` ${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" : ""})
93159
93580
  `)
93160
93581
  );
93161
93582
  } else {
93162
93583
  process.stderr.write(
93163
- ` ${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")
93164
93585
  );
93165
93586
  }
93166
93587
  let hookLine = "no pre-commit hook \u2014 `dg hook install` to gate commits";
93167
93588
  let hookOk = false;
93168
93589
  try {
93169
- const { existsSync: existsSync16, readFileSync: readFileSync12 } = await import("node:fs");
93170
- const { join: join18 } = await import("node:path");
93171
- 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")];
93172
93593
  for (const path3 of candidates) {
93173
- if (existsSync16(path3)) {
93174
- const content = readFileSync12(path3, "utf-8");
93594
+ if (existsSync21(path3)) {
93595
+ const content = readFileSync14(path3, "utf-8");
93175
93596
  if (content.includes("dependency-guardian")) {
93176
93597
  hookLine = `installed in ${path3}`;
93177
93598
  hookOk = true;
@@ -93182,7 +93603,7 @@ async function handleStatusCommand(cliVersion) {
93182
93603
  } catch {
93183
93604
  }
93184
93605
  process.stderr.write(
93185
- ` ${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")
93186
93607
  );
93187
93608
  try {
93188
93609
  const banner = await checkForUpdate(cliVersion).catch(() => null);
@@ -93190,20 +93611,20 @@ async function handleStatusCommand(cliVersion) {
93190
93611
  const m = banner.match(/(\d+\.\d+\.\d+)\s+→\s+(\d+\.\d+\.\d+)/);
93191
93612
  const latest = m ? m[2] : "newer";
93192
93613
  process.stderr.write(
93193
- ` ${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\`)
93194
93615
  `)
93195
93616
  );
93196
93617
  } else {
93197
- 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})
93198
93619
  `));
93199
93620
  }
93200
93621
  } catch {
93201
93622
  }
93202
93623
  try {
93203
- const { execFileSync: execFileSync4 } = await import("node:child_process");
93624
+ const { execFileSync: execFileSync5 } = await import("node:child_process");
93204
93625
  const npmV = (() => {
93205
93626
  try {
93206
- return execFileSync4("npm", ["--version"], { encoding: "utf-8", timeout: 5e3 }).trim();
93627
+ return execFileSync5("npm", ["--version"], { encoding: "utf-8", timeout: 5e3 }).trim();
93207
93628
  } catch {
93208
93629
  return null;
93209
93630
  }
@@ -93212,21 +93633,21 @@ async function handleStatusCommand(cliVersion) {
93212
93633
  const pipV = await pipVersion2();
93213
93634
  const pipProbe = pipV ? await pipSupportsDryRunReport2() : null;
93214
93635
  const npmLine = npmV ? `npm ${npmV} (transitive scan supported)` : "npm not found on PATH";
93215
- const npmGlyph = npmV ? chalk15.green("\u2713") : chalk15.yellow("\xB7");
93216
- 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"));
93217
93638
  let pipLine;
93218
93639
  let pipGlyph;
93219
93640
  if (!pipV) {
93220
93641
  pipLine = "pip not found on PATH (Python projects won't scan)";
93221
- pipGlyph = chalk15.yellow("\xB7");
93642
+ pipGlyph = chalk17.yellow("\xB7");
93222
93643
  } else if (pipProbe?.ok) {
93223
93644
  pipLine = `pip ${pipV} (transitive scan supported)`;
93224
- pipGlyph = chalk15.green("\u2713");
93645
+ pipGlyph = chalk17.green("\u2713");
93225
93646
  } else {
93226
93647
  pipLine = `pip ${pipV} \u2014 transitive scan unavailable (${pipProbe?.reason ?? "old pip"}; \`pip install -U pip\`)`;
93227
- pipGlyph = chalk15.yellow("!");
93648
+ pipGlyph = chalk17.yellow("!");
93228
93649
  }
93229
- process.stderr.write(` ${pipGlyph} ${chalk15.bold("pip")} ` + chalk15.dim(pipLine + "\n"));
93650
+ process.stderr.write(` ${pipGlyph} ${chalk17.bold("pip")} ` + chalk17.dim(pipLine + "\n"));
93230
93651
  } catch {
93231
93652
  }
93232
93653
  process.stderr.write("\n");
@@ -93252,8 +93673,8 @@ __export(publish_check_exports, {
93252
93673
  summarize: () => summarize
93253
93674
  });
93254
93675
  import { spawn as spawn4 } from "node:child_process";
93255
- import { readFileSync as readFileSync11, existsSync as existsSync14, readdirSync as readdirSync2 } from "node:fs";
93256
- 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";
93257
93678
  import { createGunzip } from "node:zlib";
93258
93679
  import { createReadStream as createReadStream2 } from "node:fs";
93259
93680
  function sanitizeSnippet(s, max = 40) {
@@ -93300,7 +93721,7 @@ async function npmPackDryRun(cwd2 = process.cwd()) {
93300
93721
  }));
93301
93722
  let packageJson;
93302
93723
  try {
93303
- const raw = readFileSync11(join16(cwd2, "package.json"), "utf-8");
93724
+ const raw = readFileSync13(join14(cwd2, "package.json"), "utf-8");
93304
93725
  packageJson = JSON.parse(raw);
93305
93726
  } catch {
93306
93727
  }
@@ -93312,9 +93733,9 @@ async function npmPackDryRun(cwd2 = process.cwd()) {
93312
93733
  });
93313
93734
  }
93314
93735
  function findPypiArtifacts(cwd2 = process.cwd()) {
93315
- const dist = join16(cwd2, "dist");
93316
- if (!existsSync14(dist)) return [];
93317
- 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));
93318
93739
  }
93319
93740
  function scanPayload(files) {
93320
93741
  const findings = [];
@@ -93388,7 +93809,7 @@ async function runNpmPublishCheck(cwd2 = process.cwd()) {
93388
93809
  const payload = pack.files.map((f) => ({
93389
93810
  path: f.path,
93390
93811
  size: f.size,
93391
- read: () => readFileSync11(join16(cwd2, f.path))
93812
+ read: () => readFileSync13(join14(cwd2, f.path))
93392
93813
  }));
93393
93814
  const findings = scanPayload(payload);
93394
93815
  const scripts = pack.packageJson?.scripts ?? {};
@@ -93609,11 +94030,6 @@ var init_publish_check = __esm({
93609
94030
  recommendation: "Revoke the token at github.com/settings/tokens and remove the file from the payload."
93610
94031
  },
93611
94032
  {
93612
- // Slack tokens come in several real shapes:
93613
- // xoxb-{teamId}-{botId}-{secret} (legacy 3-seg)
93614
- // xoxa-2-{teamId}-{userId}-{secret} (4-seg)
93615
- // xoxp-{N}-{N}-{N}-{hex} (4-seg user)
93616
- // Accept ≥2 numeric segments + an alphanumeric tail of length ≥10.
93617
94033
  re: /xox[abp]-[0-9]+-[0-9]+(-[0-9]+)?-[A-Za-z0-9]{10,}/,
93618
94034
  category: "bundled-secret",
93619
94035
  severity: 5,
@@ -93646,9 +94062,7 @@ __export(cleanup_exports, {
93646
94062
  handleCleanupCommand: () => handleCleanupCommand,
93647
94063
  runCleanup: () => runCleanup
93648
94064
  });
93649
- import { existsSync as existsSync15, unlinkSync as unlinkSync4 } from "node:fs";
93650
- import { join as join17 } from "node:path";
93651
- import { homedir as homedir7 } from "node:os";
94065
+ import { existsSync as existsSync18, unlinkSync as unlinkSync6 } from "node:fs";
93652
94066
  async function confirm(prompt) {
93653
94067
  process.stderr.write(prompt);
93654
94068
  return new Promise((resolve3) => {
@@ -93679,16 +94093,19 @@ async function revokeKey(apiKey) {
93679
94093
  }
93680
94094
  }
93681
94095
  async function runCleanup(opts = {}) {
94096
+ const configPath2 = dgConfigPath();
94097
+ const legacyDgrc = legacyPaths().dgrc;
93682
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"));
93683
94101
  process.stderr.write(" This will:\n");
93684
94102
  process.stderr.write(" 1. Revoke this device's API key on the server\n");
93685
- process.stderr.write(` 2. Remove ${DGRC_PATH}
94103
+ process.stderr.write(` 2. Remove ${configPath2}
93686
94104
  `);
93687
- process.stderr.write(" 3. Remove the shell-alias snippet (~/.dependency-guardian/aliases.sh)\n");
93688
- 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");
93689
94106
  process.stderr.write(import_chalk9.default.dim(" Git pre-commit hooks in your projects are NOT removed.\n"));
93690
- process.stderr.write(import_chalk9.default.dim(" Run `dg hook uninstall` in each repo separately, or delete\n"));
93691
- 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"));
93692
94109
  if (!opts.assumeYes) {
93693
94110
  if (!process.stdin.isTTY) {
93694
94111
  process.stderr.write(import_chalk9.default.yellow(" Non-interactive shell \u2014 pass --yes to confirm without prompting.\n\n"));
@@ -93712,18 +94129,17 @@ async function runCleanup(opts = {}) {
93712
94129
  } else {
93713
94130
  process.stderr.write(import_chalk9.default.dim(" \xB7 No saved API key found\n"));
93714
94131
  }
93715
- if (existsSync15(DGRC_PATH)) {
93716
- try {
93717
- unlinkSync4(DGRC_PATH);
93718
- 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}
93719
94137
  `);
93720
- } catch (e) {
93721
- 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}
93722
94140
  `);
94141
+ }
93723
94142
  }
93724
- } else {
93725
- process.stderr.write(import_chalk9.default.dim(` \xB7 ${DGRC_PATH} already absent
93726
- `));
93727
94143
  }
93728
94144
  process.stderr.write("\n");
93729
94145
  await runProtectOff(process.cwd(), { removeShell: true });
@@ -93735,7 +94151,7 @@ async function handleCleanupCommand(args) {
93735
94151
  const assumeYes = args.includes("--yes") || args.includes("-y");
93736
94152
  return runCleanup({ assumeYes });
93737
94153
  }
93738
- var import_chalk9, DGRC_PATH;
94154
+ var import_chalk9;
93739
94155
  var init_cleanup = __esm({
93740
94156
  "src/commands/cleanup.ts"() {
93741
94157
  "use strict";
@@ -93743,7 +94159,572 @@ var init_cleanup = __esm({
93743
94159
  init_protect();
93744
94160
  init_auth();
93745
94161
  init_config();
93746
- 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();
93747
94728
  }
93748
94729
  });
93749
94730
 
@@ -94003,14 +94984,14 @@ function formatRate(ratePerSec) {
94003
94984
  if (ratePerSec >= 1) return `${Math.round(ratePerSec)} pkg/s`;
94004
94985
  return `${ratePerSec.toFixed(1)} pkg/s`;
94005
94986
  }
94006
- var import_react32, import_chalk10, import_jsx_runtime9, ProgressBar;
94987
+ var import_react32, import_chalk12, import_jsx_runtime9, ProgressBar;
94007
94988
  var init_ProgressBar = __esm({
94008
94989
  async "src/ui/components/ProgressBar.tsx"() {
94009
94990
  "use strict";
94010
94991
  import_react32 = __toESM(require_react());
94011
94992
  await init_build2();
94012
94993
  await init_build3();
94013
- import_chalk10 = __toESM(require_source());
94994
+ import_chalk12 = __toESM(require_source());
94014
94995
  await init_useTerminalSize();
94015
94996
  import_jsx_runtime9 = __toESM(require_jsx_runtime());
94016
94997
  ProgressBar = ({
@@ -94051,7 +95032,7 @@ var init_ProgressBar = __esm({
94051
95032
  const filledBar = "\u2501".repeat(filled);
94052
95033
  const emptyBar = "\u2501".repeat(empty);
94053
95034
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 2, children: [
94054
- /* @__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") }),
94055
95036
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { children: "" }),
94056
95037
  /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { children: [
94057
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" }) }),
@@ -94080,7 +95061,7 @@ var init_ProgressBar = __esm({
94080
95061
  ] }),
94081
95062
  label && !compact && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { dimColor: true, children: [
94082
95063
  " ",
94083
- import_chalk10.default.dim("\u203A"),
95064
+ import_chalk12.default.dim("\u203A"),
94084
95065
  " ",
94085
95066
  label
94086
95067
  ] }) })
@@ -94091,7 +95072,7 @@ var init_ProgressBar = __esm({
94091
95072
 
94092
95073
  // src/ui/components/ScoreHeader.tsx
94093
95074
  function scoreColor(score, action) {
94094
- 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;
94095
95076
  return colorFn(String(score));
94096
95077
  }
94097
95078
  function plot(grid, x, y, type) {
@@ -94123,10 +95104,10 @@ function fillCircle(grid, cx, cy, r, type) {
94123
95104
  }
94124
95105
  function renderLogo(action) {
94125
95106
  const colors = {
94126
- 1: import_chalk11.default.dim,
94127
- 2: import_chalk11.default.white,
94128
- 3: (s) => import_chalk11.default.bold.white(s),
94129
- 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
94130
95111
  };
94131
95112
  const result = [];
94132
95113
  const { chars, types, cols } = LOGO_DATA;
@@ -94135,18 +95116,18 @@ function renderLogo(action) {
94135
95116
  for (let j = 0; j < cols; j++) {
94136
95117
  const ch = chars[i + j];
94137
95118
  const t = types[i + j];
94138
- row += t > 0 ? (colors[t] ?? import_chalk11.default.dim)(ch) : ch;
95119
+ row += t > 0 ? (colors[t] ?? import_chalk13.default.dim)(ch) : ch;
94139
95120
  }
94140
95121
  result.push(row);
94141
95122
  }
94142
95123
  return result;
94143
95124
  }
94144
- var import_chalk11, import_jsx_runtime10, LOGO_DATA, ScoreHeader;
95125
+ var import_chalk13, import_jsx_runtime10, LOGO_DATA, ScoreHeader;
94145
95126
  var init_ScoreHeader = __esm({
94146
95127
  async "src/ui/components/ScoreHeader.tsx"() {
94147
95128
  "use strict";
94148
95129
  await init_build2();
94149
- import_chalk11 = __toESM(require_source());
95130
+ import_chalk13 = __toESM(require_source());
94150
95131
  import_jsx_runtime10 = __toESM(require_jsx_runtime());
94151
95132
  LOGO_DATA = (() => {
94152
95133
  const W = 22, H = 28;
@@ -94211,28 +95192,28 @@ var init_ScoreHeader = __esm({
94211
95192
  children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "row", children: [
94212
95193
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", flexGrow: 1, children: [
94213
95194
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { bold: true, children: [
94214
- import_chalk11.default.cyan("\u25C6"),
95195
+ import_chalk13.default.cyan("\u25C6"),
94215
95196
  " Dependency Guardian ",
94216
- userStatus ? import_chalk11.default.dim(userStatus) : ""
95197
+ userStatus ? import_chalk13.default.dim(userStatus) : ""
94217
95198
  ] }),
94218
95199
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: " " }),
94219
95200
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
94220
- import_chalk11.default.dim("Score"),
95201
+ import_chalk13.default.dim("Score"),
94221
95202
  " ",
94222
95203
  scoreColor(score, action)
94223
95204
  ] }),
94224
95205
  total !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
94225
95206
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: " " }),
94226
95207
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
94227
- import_chalk11.default.dim(`${total} package${total !== 1 ? "s" : ""} scanned`),
95208
+ import_chalk13.default.dim(`${total} package${total !== 1 ? "s" : ""} scanned`),
94228
95209
  flagged !== void 0 && flagged > 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
94229
95210
  " ",
94230
- import_chalk11.default.yellow(`${flagged} flagged`),
95211
+ import_chalk13.default.yellow(`${flagged} flagged`),
94231
95212
  " ",
94232
- import_chalk11.default.green(`${clean ?? 0} clean`)
95213
+ import_chalk13.default.green(`${clean ?? 0} clean`)
94233
95214
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
94234
95215
  " ",
94235
- import_chalk11.default.green("all clean")
95216
+ import_chalk13.default.green("all clean")
94236
95217
  ] })
94237
95218
  ] }),
94238
95219
  scanUsage && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { dimColor: true, children: scanUsage })
@@ -94293,8 +95274,8 @@ var init_useExpandAnimation = __esm({
94293
95274
  });
94294
95275
 
94295
95276
  // src/ui/components/InteractiveResultsView.tsx
94296
- import { writeFileSync as writeFileSync8 } from "node:fs";
94297
- import { resolve as resolvePath2 } from "node:path";
95277
+ import { writeFileSync as writeFileSync10 } from "node:fs";
95278
+ import { resolve as resolvePath3 } from "node:path";
94298
95279
  function groupPackages2(packages) {
94299
95280
  const map = /* @__PURE__ */ new Map();
94300
95281
  for (const pkg of packages) {
@@ -94307,10 +95288,10 @@ function groupPackages2(packages) {
94307
95288
  }
94308
95289
  function actionBadge2(score) {
94309
95290
  if (score >= 70)
94310
- return { label: "Block", color: import_chalk12.default.red };
95291
+ return { label: "Block", color: import_chalk14.default.red };
94311
95292
  if (score >= 60)
94312
- return { label: "Warn", color: import_chalk12.default.yellow };
94313
- 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 };
94314
95295
  }
94315
95296
  function truncate3(s, max) {
94316
95297
  return s.length <= max ? s : s.slice(0, max - 1) + "\u2026";
@@ -94407,7 +95388,7 @@ function buildDetailLines(group, safeVersion, maxWidth) {
94407
95388
  connector,
94408
95389
  " ",
94409
95390
  sevColor(pad2(sevLabel, 8)),
94410
- import_chalk12.default.dim(f.category ?? "")
95391
+ import_chalk14.default.dim(f.category ?? "")
94411
95392
  ] }, `finding-${i}`)
94412
95393
  );
94413
95394
  }
@@ -94415,7 +95396,7 @@ function buildDetailLines(group, safeVersion, maxWidth) {
94415
95396
  } else if (rep.score > 0) {
94416
95397
  lines.push(
94417
95398
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { color: "yellow", children: [
94418
- import_chalk12.default.yellow("\u2192"),
95399
+ import_chalk14.default.yellow("\u2192"),
94419
95400
  " Upgrade to Pro to see risk categories"
94420
95401
  ] }, "upgrade")
94421
95402
  );
@@ -94424,15 +95405,15 @@ function buildDetailLines(group, safeVersion, maxWidth) {
94424
95405
  if (rep.recommendation) {
94425
95406
  lines.push(
94426
95407
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
94427
- import_chalk12.default.dim("Recommendation:"),
95408
+ import_chalk14.default.dim("Recommendation:"),
94428
95409
  " ",
94429
- import_chalk12.default.cyan(truncate3(rep.recommendation, maxWidth - 18))
95410
+ import_chalk14.default.cyan(truncate3(rep.recommendation, maxWidth - 18))
94430
95411
  ] }, "recommendation")
94431
95412
  );
94432
95413
  }
94433
95414
  if (safeVersion) {
94434
95415
  lines.push(
94435
- /* @__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")
94436
95417
  );
94437
95418
  }
94438
95419
  return lines;
@@ -94452,24 +95433,24 @@ function licenseLine(rep) {
94452
95433
  if (!lc) return null;
94453
95434
  const spdx = lc.spdx ?? lc.raw ?? "";
94454
95435
  const desc = LICENSE_DESCRIPTIONS[lc.riskCategory] ?? "";
94455
- 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;
94456
95437
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
94457
95438
  T.branch,
94458
95439
  " ",
94459
95440
  lcColor(spdx),
94460
95441
  " ",
94461
- import_chalk12.default.dim("\u2014"),
95442
+ import_chalk14.default.dim("\u2014"),
94462
95443
  " ",
94463
- import_chalk12.default.dim(desc)
95444
+ import_chalk14.default.dim(desc)
94464
95445
  ] }, "license-info");
94465
95446
  }
94466
- 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;
94467
95448
  var init_InteractiveResultsView = __esm({
94468
95449
  async "src/ui/components/InteractiveResultsView.tsx"() {
94469
95450
  "use strict";
94470
95451
  import_react34 = __toESM(require_react());
94471
95452
  await init_build2();
94472
- import_chalk12 = __toESM(require_source());
95453
+ import_chalk14 = __toESM(require_source());
94473
95454
  init_auth();
94474
95455
  await init_ScoreHeader();
94475
95456
  init_useExpandAnimation();
@@ -94484,11 +95465,11 @@ var init_InteractiveResultsView = __esm({
94484
95465
  1: "INFO"
94485
95466
  };
94486
95467
  SEVERITY_COLORS = {
94487
- 5: (s) => import_chalk12.default.red.bold(s),
94488
- 4: (s) => import_chalk12.default.red(s),
94489
- 3: (s) => import_chalk12.default.yellow(s),
94490
- 2: (s) => import_chalk12.default.cyan(s),
94491
- 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)
94492
95473
  };
94493
95474
  EVIDENCE_LIMIT = 2;
94494
95475
  FIXED_CHROME = 21;
@@ -94805,8 +95786,8 @@ var init_InteractiveResultsView = __esm({
94805
95786
  const { body, ext } = formatExport(payload, scope, format);
94806
95787
  const scopeTag = scope === "current-license" && currentLicenseIdx !== null ? `${(licenseGroups[currentLicenseIdx]?.spdx ?? "license").replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 32)}` : scope;
94807
95788
  const filename = `dg-scan-${ts}-${scopeTag}.${ext}`;
94808
- const path3 = resolvePath2(process.cwd(), filename);
94809
- writeFileSync8(path3, body, "utf-8");
95789
+ const path3 = resolvePath3(process.cwd(), filename);
95790
+ writeFileSync10(path3, body, "utf-8");
94810
95791
  showExportMsg(`\u2713 Exported to ${filename}`);
94811
95792
  } catch (e) {
94812
95793
  showExportMsg(`Export failed: ${e.message}`);
@@ -95178,13 +96159,13 @@ var init_InteractiveResultsView = __esm({
95178
96159
  ];
95179
96160
  const renderRow = (title, rows, current, isActive) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
95180
96161
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95181
- isActive ? import_chalk12.default.cyan("\u258C ") : " ",
95182
- 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)
95183
96164
  ] }),
95184
96165
  rows.map((r) => {
95185
96166
  const selected = r.value === current;
95186
- const bullet = selected ? isActive ? import_chalk12.default.cyan("\u25CF") : import_chalk12.default.green("\u25CF") : import_chalk12.default.dim("\u25CB");
95187
- 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;
95188
96169
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { children: [
95189
96170
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { width: 5, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95190
96171
  " ",
@@ -95210,32 +96191,32 @@ var init_InteractiveResultsView = __esm({
95210
96191
  ),
95211
96192
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingLeft: 2, paddingRight: 2, width: "100%", children: [
95212
96193
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { bold: true, children: [
95213
- import_chalk12.default.cyan("\u25C6"),
96194
+ import_chalk14.default.cyan("\u25C6"),
95214
96195
  " Export ",
95215
- import_chalk12.default.dim("(pick what to export and which format)")
96196
+ import_chalk14.default.dim("(pick what to export and which format)")
95216
96197
  ] }),
95217
96198
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
95218
96199
  renderRow("What", SCOPES_RENDER, exportMenu.scope, exportMenu.activeRow === "scope"),
95219
96200
  renderRow("Format", FORMATS_RENDER, exportMenu.format, exportMenu.activeRow === "format")
95220
96201
  ] }),
95221
- /* @__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))) }),
95222
96203
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95223
96204
  " ",
95224
- import_chalk12.default.bold.cyan("\u2191\u2193"),
96205
+ import_chalk14.default.bold.cyan("\u2191\u2193"),
95225
96206
  " ",
95226
- import_chalk12.default.dim("scroll"),
96207
+ import_chalk14.default.dim("scroll"),
95227
96208
  " ",
95228
- import_chalk12.default.bold.cyan("\u2190\u2192/Tab"),
96209
+ import_chalk14.default.bold.cyan("\u2190\u2192/Tab"),
95229
96210
  " ",
95230
- import_chalk12.default.dim("switch row"),
96211
+ import_chalk14.default.dim("switch row"),
95231
96212
  " ",
95232
- import_chalk12.default.bold.cyan("\u23CE"),
96213
+ import_chalk14.default.bold.cyan("\u23CE"),
95233
96214
  " ",
95234
- import_chalk12.default.dim("export"),
96215
+ import_chalk14.default.dim("export"),
95235
96216
  " ",
95236
- import_chalk12.default.bold.cyan("Esc"),
96217
+ import_chalk14.default.bold.cyan("Esc"),
95237
96218
  " ",
95238
- import_chalk12.default.dim("cancel")
96219
+ import_chalk14.default.dim("cancel")
95239
96220
  ] })
95240
96221
  ] });
95241
96222
  }
@@ -95265,97 +96246,97 @@ var init_InteractiveResultsView = __esm({
95265
96246
  width: "100%",
95266
96247
  children: [
95267
96248
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { bold: true, children: [
95268
- import_chalk12.default.cyan("\u25C6"),
96249
+ import_chalk14.default.cyan("\u25C6"),
95269
96250
  " Keyboard Shortcuts"
95270
96251
  ] }),
95271
96252
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
95272
96253
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { bold: true, children: " Navigation" }),
95273
96254
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95274
96255
  " ",
95275
- import_chalk12.default.cyan("\u2191 k"),
96256
+ import_chalk14.default.cyan("\u2191 k"),
95276
96257
  " ",
95277
- import_chalk12.default.dim("Move up")
96258
+ import_chalk14.default.dim("Move up")
95278
96259
  ] }),
95279
96260
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95280
96261
  " ",
95281
- import_chalk12.default.cyan("\u2193 j"),
96262
+ import_chalk14.default.cyan("\u2193 j"),
95282
96263
  " ",
95283
- import_chalk12.default.dim("Move down")
96264
+ import_chalk14.default.dim("Move down")
95284
96265
  ] }),
95285
96266
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95286
96267
  " ",
95287
- import_chalk12.default.cyan("g"),
96268
+ import_chalk14.default.cyan("g"),
95288
96269
  " ",
95289
- import_chalk12.default.dim("Jump to top")
96270
+ import_chalk14.default.dim("Jump to top")
95290
96271
  ] }),
95291
96272
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95292
96273
  " ",
95293
- import_chalk12.default.cyan("G"),
96274
+ import_chalk14.default.cyan("G"),
95294
96275
  " ",
95295
- import_chalk12.default.dim("Jump to bottom")
96276
+ import_chalk14.default.dim("Jump to bottom")
95296
96277
  ] }),
95297
96278
  !isDetail && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95298
96279
  " ",
95299
- import_chalk12.default.cyan("PgUp"),
96280
+ import_chalk14.default.cyan("PgUp"),
95300
96281
  " ",
95301
- import_chalk12.default.dim("Page up")
96282
+ import_chalk14.default.dim("Page up")
95302
96283
  ] }),
95303
96284
  !isDetail && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95304
96285
  " ",
95305
- import_chalk12.default.cyan("PgDn"),
96286
+ import_chalk14.default.cyan("PgDn"),
95306
96287
  " ",
95307
- import_chalk12.default.dim("Page down")
96288
+ import_chalk14.default.dim("Page down")
95308
96289
  ] }),
95309
96290
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
95310
96291
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { bold: true, children: " Actions" }),
95311
96292
  !isDetail && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95312
96293
  " ",
95313
- import_chalk12.default.cyan("\u23CE"),
96294
+ import_chalk14.default.cyan("\u23CE"),
95314
96295
  " ",
95315
- import_chalk12.default.dim("Expand findings")
96296
+ import_chalk14.default.dim("Expand findings")
95316
96297
  ] }),
95317
96298
  isDetail && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95318
96299
  " ",
95319
- import_chalk12.default.cyan("Esc"),
96300
+ import_chalk14.default.cyan("Esc"),
95320
96301
  " ",
95321
- import_chalk12.default.dim("Back to list")
96302
+ import_chalk14.default.dim("Back to list")
95322
96303
  ] }),
95323
96304
  !isDetail && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95324
96305
  " ",
95325
- import_chalk12.default.cyan("/"),
96306
+ import_chalk14.default.cyan("/"),
95326
96307
  " ",
95327
- import_chalk12.default.dim("Search packages")
96308
+ import_chalk14.default.dim("Search packages")
95328
96309
  ] }),
95329
96310
  !isDetail && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95330
96311
  " ",
95331
- import_chalk12.default.cyan("l"),
96312
+ import_chalk14.default.cyan("l"),
95332
96313
  " ",
95333
- import_chalk12.default.dim("License breakdown (browse + drill-in)")
96314
+ import_chalk14.default.dim("License breakdown (browse + drill-in)")
95334
96315
  ] }),
95335
96316
  !isDetail && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95336
96317
  " ",
95337
- import_chalk12.default.cyan("e"),
96318
+ import_chalk14.default.cyan("e"),
95338
96319
  " ",
95339
- 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)")
95340
96321
  ] }),
95341
96322
  !isDetail && onBack && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95342
96323
  " ",
95343
- import_chalk12.default.cyan("b"),
96324
+ import_chalk14.default.cyan("b"),
95344
96325
  " ",
95345
- import_chalk12.default.dim("Back to project selector")
96326
+ import_chalk14.default.dim("Back to project selector")
95346
96327
  ] }),
95347
96328
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95348
96329
  " ",
95349
- import_chalk12.default.cyan("q"),
96330
+ import_chalk14.default.cyan("q"),
95350
96331
  " ",
95351
- import_chalk12.default.dim("Quit")
96332
+ import_chalk14.default.dim("Quit")
95352
96333
  ] }),
95353
96334
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
95354
96335
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
95355
96336
  " Press ",
95356
- import_chalk12.default.bold.cyan("?"),
96337
+ import_chalk14.default.bold.cyan("?"),
95357
96338
  " or ",
95358
- import_chalk12.default.bold.cyan("Esc"),
96339
+ import_chalk14.default.bold.cyan("Esc"),
95359
96340
  " to close"
95360
96341
  ] })
95361
96342
  ]
@@ -95365,11 +96346,11 @@ var init_InteractiveResultsView = __esm({
95365
96346
  }
95366
96347
  if (showLicenses) {
95367
96348
  const lcColor = (risk) => {
95368
- if (risk === "permissive") return import_chalk12.default.green;
95369
- if (risk === "weak-copyleft") return import_chalk12.default.yellow;
95370
- if (risk === "strong-copyleft") return import_chalk12.default.yellow.bold;
95371
- if (risk === "no-license" || risk === "network-copyleft" || risk === "unlicensed") return import_chalk12.default.red;
95372
- 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;
95373
96354
  };
95374
96355
  const totalCount = result.packages.length;
95375
96356
  const maxCount = licenseGroups[0]?.count ?? 1;
@@ -95401,7 +96382,7 @@ var init_InteractiveResultsView = __esm({
95401
96382
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingLeft: 2, paddingRight: 2, width: "100%", children: [
95402
96383
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { children: [
95403
96384
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { bold: true, children: [
95404
- import_chalk12.default.cyan("\u25C6"),
96385
+ import_chalk14.default.cyan("\u25C6"),
95405
96386
  " ",
95406
96387
  color(g.spdx)
95407
96388
  ] }),
@@ -95417,16 +96398,16 @@ var init_InteractiveResultsView = __esm({
95417
96398
  ] }),
95418
96399
  licenseSearchMode || q ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95419
96400
  " ",
95420
- import_chalk12.default.bold.cyan("/"),
96401
+ import_chalk14.default.bold.cyan("/"),
95421
96402
  " ",
95422
- q || import_chalk12.default.dim("(type to filter; Esc clears, Enter confirms)"),
95423
- 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") : ""
95424
96405
  ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
95425
96406
  _above > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: ` \u2191 ${_above} more above` }),
95426
96407
  _slice.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: ` (no packages match "${q}")` }),
95427
96408
  _slice.map((p) => {
95428
96409
  const score = p.score;
95429
- 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;
95430
96411
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { children: [
95431
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 }) }),
95432
96413
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { width: 16, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: p.version }) }),
@@ -95435,35 +96416,35 @@ var init_InteractiveResultsView = __esm({
95435
96416
  }),
95436
96417
  _below > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: ` \u2193 ${_below} more below` })
95437
96418
  ] }),
95438
- /* @__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))) }),
95439
96420
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95440
96421
  " ",
95441
- import_chalk12.default.bold.cyan("\u2191\u2193"),
96422
+ import_chalk14.default.bold.cyan("\u2191\u2193"),
95442
96423
  " ",
95443
- import_chalk12.default.dim("scroll"),
96424
+ import_chalk14.default.dim("scroll"),
95444
96425
  " ",
95445
- import_chalk12.default.bold.cyan("/"),
96426
+ import_chalk14.default.bold.cyan("/"),
95446
96427
  " ",
95447
- import_chalk12.default.dim("search"),
96428
+ import_chalk14.default.dim("search"),
95448
96429
  " ",
95449
- import_chalk12.default.bold.cyan("e"),
96430
+ import_chalk14.default.bold.cyan("e"),
95450
96431
  " ",
95451
- import_chalk12.default.dim("export"),
96432
+ import_chalk14.default.dim("export"),
95452
96433
  " ",
95453
- import_chalk12.default.bold.cyan("Esc"),
96434
+ import_chalk14.default.bold.cyan("Esc"),
95454
96435
  " ",
95455
- import_chalk12.default.dim("back"),
96436
+ import_chalk14.default.dim("back"),
95456
96437
  " ",
95457
- import_chalk12.default.bold.cyan("l"),
96438
+ import_chalk14.default.bold.cyan("l"),
95458
96439
  " ",
95459
- import_chalk12.default.dim("close"),
96440
+ import_chalk14.default.dim("close"),
95460
96441
  " ",
95461
- import_chalk12.default.bold.cyan("q"),
96442
+ import_chalk14.default.bold.cyan("q"),
95462
96443
  " ",
95463
- import_chalk12.default.dim("quit"),
96444
+ import_chalk14.default.dim("quit"),
95464
96445
  exportMsg && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
95465
96446
  " ",
95466
- import_chalk12.default.green(exportMsg)
96447
+ import_chalk14.default.green(exportMsg)
95467
96448
  ] })
95468
96449
  ] })
95469
96450
  ] });
@@ -95509,9 +96490,9 @@ var init_InteractiveResultsView = __esm({
95509
96490
  width: "100%",
95510
96491
  children: [
95511
96492
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { bold: true, children: [
95512
- import_chalk12.default.cyan("\u25C6"),
96493
+ import_chalk14.default.cyan("\u25C6"),
95513
96494
  " Licenses ",
95514
- import_chalk12.default.dim(`(${licenseGroups.length} unique across ${totalCount} packages)`)
96495
+ import_chalk14.default.dim(`(${licenseGroups.length} unique across ${totalCount} packages)`)
95515
96496
  ] }),
95516
96497
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
95517
96498
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { children: [
@@ -95531,10 +96512,10 @@ var init_InteractiveResultsView = __esm({
95531
96512
  const spdxText = truncate3(g.spdx, spdxCol - 1);
95532
96513
  const riskText = g.risk === "unknown" ? "\u2014" : g.risk;
95533
96514
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { children: [
95534
- /* @__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") : " " }) }),
95535
- /* @__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) }) }),
95536
96517
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { width: riskCol, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: riskText }) }),
95537
- /* @__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)) }) }),
95538
96519
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { width: barCol, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: color(bar) }) })
95539
96520
  ] }, `${g.risk}::${g.spdx}::${absIdx}`);
95540
96521
  }),
@@ -95542,31 +96523,31 @@ var init_InteractiveResultsView = __esm({
95542
96523
  ]
95543
96524
  }
95544
96525
  ),
95545
- /* @__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))) }),
95546
96527
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95547
96528
  " ",
95548
- import_chalk12.default.bold.cyan("\u2191\u2193"),
96529
+ import_chalk14.default.bold.cyan("\u2191\u2193"),
95549
96530
  " ",
95550
- import_chalk12.default.dim("navigate"),
96531
+ import_chalk14.default.dim("navigate"),
95551
96532
  " ",
95552
- import_chalk12.default.bold.cyan("\u23CE"),
96533
+ import_chalk14.default.bold.cyan("\u23CE"),
95553
96534
  " ",
95554
- import_chalk12.default.dim("view packages"),
96535
+ import_chalk14.default.dim("view packages"),
95555
96536
  " ",
95556
- import_chalk12.default.bold.cyan("e"),
96537
+ import_chalk14.default.bold.cyan("e"),
95557
96538
  " ",
95558
- import_chalk12.default.dim("export"),
96539
+ import_chalk14.default.dim("export"),
95559
96540
  " ",
95560
- import_chalk12.default.bold.cyan("l/Esc"),
96541
+ import_chalk14.default.bold.cyan("l/Esc"),
95561
96542
  " ",
95562
- import_chalk12.default.dim("close"),
96543
+ import_chalk14.default.dim("close"),
95563
96544
  " ",
95564
- import_chalk12.default.bold.cyan("q"),
96545
+ import_chalk14.default.bold.cyan("q"),
95565
96546
  " ",
95566
- import_chalk12.default.dim("quit"),
96547
+ import_chalk14.default.dim("quit"),
95567
96548
  exportMsg && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
95568
96549
  " ",
95569
- import_chalk12.default.green(exportMsg)
96550
+ import_chalk14.default.green(exportMsg)
95570
96551
  ] })
95571
96552
  ] })
95572
96553
  ] });
@@ -95608,19 +96589,19 @@ var init_InteractiveResultsView = __esm({
95608
96589
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { justifyContent: "space-between", children: [
95609
96590
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { bold: true, children: [
95610
96591
  groupNames(dpGroup),
95611
- 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 ?? "")) : ""
95612
96593
  ] }),
95613
96594
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: dpColor(`score ${dpRep.score}`) })
95614
96595
  ] }),
95615
96596
  dpAbove > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
95616
- import_chalk12.default.cyan(" \u2191"),
96597
+ import_chalk14.default.cyan(" \u2191"),
95617
96598
  " ",
95618
96599
  dpAbove,
95619
96600
  " more above"
95620
96601
  ] }),
95621
96602
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { flexDirection: "column", marginLeft: 2, children: dpVisible }),
95622
96603
  dpBelow > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
95623
- import_chalk12.default.cyan(" \u2193"),
96604
+ import_chalk14.default.cyan(" \u2193"),
95624
96605
  " ",
95625
96606
  dpBelow,
95626
96607
  " more below"
@@ -95639,13 +96620,13 @@ var init_InteractiveResultsView = __esm({
95639
96620
  width: "100%",
95640
96621
  children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { justifyContent: "space-between", children: [
95641
96622
  clean.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95642
- import_chalk12.default.green("\u2713"),
96623
+ import_chalk14.default.green("\u2713"),
95643
96624
  " ",
95644
- import_chalk12.default.green.bold(String(clean.length)),
96625
+ import_chalk14.default.green.bold(String(clean.length)),
95645
96626
  " ",
95646
- 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`),
95647
96628
  " ",
95648
- import_chalk12.default.dim(`\xB7 ${(durationMs / 1e3).toFixed(1)}s`)
96629
+ import_chalk14.default.dim(`\xB7 ${(durationMs / 1e3).toFixed(1)}s`)
95649
96630
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
95650
96631
  (durationMs / 1e3).toFixed(1),
95651
96632
  "s"
@@ -95654,20 +96635,20 @@ var init_InteractiveResultsView = __esm({
95654
96635
  ] })
95655
96636
  }
95656
96637
  ),
95657
- /* @__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))) }),
95658
96639
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95659
96640
  " ",
95660
- import_chalk12.default.bold.cyan("\u2191\u2193"),
96641
+ import_chalk14.default.bold.cyan("\u2191\u2193"),
95661
96642
  " ",
95662
- import_chalk12.default.dim("scroll"),
96643
+ import_chalk14.default.dim("scroll"),
95663
96644
  " ",
95664
- import_chalk12.default.bold.cyan("Esc"),
96645
+ import_chalk14.default.bold.cyan("Esc"),
95665
96646
  " ",
95666
- import_chalk12.default.dim("back"),
96647
+ import_chalk14.default.dim("back"),
95667
96648
  " ",
95668
- import_chalk12.default.bold.cyan("q"),
96649
+ import_chalk14.default.bold.cyan("q"),
95669
96650
  " ",
95670
- import_chalk12.default.dim("quit")
96651
+ import_chalk14.default.dim("quit")
95671
96652
  ] })
95672
96653
  ] });
95673
96654
  }
@@ -95700,7 +96681,7 @@ var init_InteractiveResultsView = __esm({
95700
96681
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: searchQuery ? `${groups.length} of ${allGroupCount}` : `${clampedCursor + 1}/${groups.length}` })
95701
96682
  ] }),
95702
96683
  aboveCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
95703
- import_chalk12.default.cyan(" \u2191"),
96684
+ import_chalk14.default.cyan(" \u2191"),
95704
96685
  " ",
95705
96686
  aboveCount,
95706
96687
  " more above"
@@ -95715,22 +96696,22 @@ var init_InteractiveResultsView = __esm({
95715
96696
  const scoreStr = String(rep.score);
95716
96697
  const lcInfo = rep.license;
95717
96698
  const lcStr = lcInfo ? truncate3(lcInfo.spdx ?? lcInfo.raw ?? "", lcCol - 2) : "";
95718
- 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;
95719
96700
  const arrow = level === "summary" ? "\u25BE" : "\u25B8";
95720
96701
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", children: [
95721
96702
  isCursor ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { backgroundColor: "#1a1a2e", children: [
95722
- import_chalk12.default.cyan("\u258C"),
96703
+ import_chalk14.default.cyan("\u258C"),
95723
96704
  " ",
95724
- import_chalk12.default.cyan(arrow),
96705
+ import_chalk14.default.cyan(arrow),
95725
96706
  " ",
95726
96707
  ` `,
95727
96708
  color(pad2(label, 6)),
95728
- import_chalk12.default.bold(pad2(truncate3(names, nameCol - 2), nameCol)),
96709
+ import_chalk14.default.bold(pad2(truncate3(names, nameCol - 2), nameCol)),
95729
96710
  lcColor(pad2(lcStr, lcCol)),
95730
96711
  color(scoreStr.padStart(3)),
95731
96712
  " "
95732
96713
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95733
- ` ${import_chalk12.default.dim(arrow)} `,
96714
+ ` ${import_chalk14.default.dim(arrow)} `,
95734
96715
  color(pad2(label, 6)),
95735
96716
  pad2(truncate3(names, nameCol - 2), nameCol),
95736
96717
  lcColor(pad2(lcStr, lcCol)),
@@ -95747,7 +96728,7 @@ var init_InteractiveResultsView = __esm({
95747
96728
  ] }, group.key);
95748
96729
  }),
95749
96730
  belowCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
95750
- import_chalk12.default.cyan(" \u2193"),
96731
+ import_chalk14.default.cyan(" \u2193"),
95751
96732
  " ",
95752
96733
  belowCount,
95753
96734
  " more below"
@@ -95774,13 +96755,13 @@ var init_InteractiveResultsView = __esm({
95774
96755
  ] }),
95775
96756
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { justifyContent: "space-between", children: [
95776
96757
  clean.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95777
- import_chalk12.default.green("\u2713"),
96758
+ import_chalk14.default.green("\u2713"),
95778
96759
  " ",
95779
- import_chalk12.default.green.bold(String(clean.length)),
96760
+ import_chalk14.default.green.bold(String(clean.length)),
95780
96761
  " ",
95781
- 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`),
95782
96763
  " ",
95783
- import_chalk12.default.dim(`\xB7 ${(durationMs / 1e3).toFixed(1)}s`)
96764
+ import_chalk14.default.dim(`\xB7 ${(durationMs / 1e3).toFixed(1)}s`)
95784
96765
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
95785
96766
  (durationMs / 1e3).toFixed(1),
95786
96767
  "s"
@@ -95790,66 +96771,66 @@ var init_InteractiveResultsView = __esm({
95790
96771
  ]
95791
96772
  }
95792
96773
  ),
95793
- /* @__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))) }),
95794
96775
  searchMode ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95795
96776
  " ",
95796
- import_chalk12.default.bold.cyan("/"),
96777
+ import_chalk14.default.bold.cyan("/"),
95797
96778
  " ",
95798
96779
  searchQuery,
95799
- import_chalk12.default.cyan("\u2588"),
96780
+ import_chalk14.default.cyan("\u2588"),
95800
96781
  " ",
95801
- import_chalk12.default.dim("Esc clear")
96782
+ import_chalk14.default.dim("Esc clear")
95802
96783
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
95803
96784
  " ",
95804
96785
  groups.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
95805
- import_chalk12.default.bold.cyan("\u2191\u2193"),
96786
+ import_chalk14.default.bold.cyan("\u2191\u2193"),
95806
96787
  " ",
95807
- import_chalk12.default.dim("navigate"),
96788
+ import_chalk14.default.dim("navigate"),
95808
96789
  " ",
95809
- import_chalk12.default.bold.cyan("\u23CE"),
96790
+ import_chalk14.default.bold.cyan("\u23CE"),
95810
96791
  " ",
95811
- import_chalk12.default.dim("expand"),
96792
+ import_chalk14.default.dim("expand"),
95812
96793
  " ",
95813
- import_chalk12.default.bold.cyan("/"),
96794
+ import_chalk14.default.bold.cyan("/"),
95814
96795
  " ",
95815
- import_chalk12.default.dim("search"),
96796
+ import_chalk14.default.dim("search"),
95816
96797
  " ",
95817
- import_chalk12.default.bold.cyan("l"),
96798
+ import_chalk14.default.bold.cyan("l"),
95818
96799
  " ",
95819
- import_chalk12.default.dim("licenses"),
96800
+ import_chalk14.default.dim("licenses"),
95820
96801
  " ",
95821
- import_chalk12.default.bold.cyan("e"),
96802
+ import_chalk14.default.bold.cyan("e"),
95822
96803
  " ",
95823
- import_chalk12.default.dim("export"),
96804
+ import_chalk14.default.dim("export"),
95824
96805
  " ",
95825
96806
  onBack && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
95826
- import_chalk12.default.bold.cyan("b"),
96807
+ import_chalk14.default.bold.cyan("b"),
95827
96808
  " ",
95828
- import_chalk12.default.dim("back"),
96809
+ import_chalk14.default.dim("back"),
95829
96810
  " "
95830
96811
  ] }),
95831
- import_chalk12.default.bold.cyan("q"),
96812
+ import_chalk14.default.bold.cyan("q"),
95832
96813
  " ",
95833
- import_chalk12.default.dim("quit"),
96814
+ import_chalk14.default.dim("quit"),
95834
96815
  exportMsg && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
95835
96816
  " ",
95836
- import_chalk12.default.green(exportMsg)
96817
+ import_chalk14.default.green(exportMsg)
95837
96818
  ] })
95838
96819
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
95839
96820
  "Press ",
95840
- import_chalk12.default.bold.cyan("q"),
96821
+ import_chalk14.default.bold.cyan("q"),
95841
96822
  " or ",
95842
- import_chalk12.default.bold.cyan("Enter"),
96823
+ import_chalk14.default.bold.cyan("Enter"),
95843
96824
  " ",
95844
- import_chalk12.default.dim("to exit")
96825
+ import_chalk14.default.dim("to exit")
95845
96826
  ] })
95846
96827
  ] })
95847
96828
  ] });
95848
96829
  };
95849
96830
  T = {
95850
- branch: import_chalk12.default.dim("\u251C\u2500\u2500"),
95851
- last: import_chalk12.default.dim("\u2514\u2500\u2500"),
95852
- 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"),
95853
96834
  blank: " "
95854
96835
  };
95855
96836
  LICENSE_DESCRIPTIONS = {
@@ -95875,9 +96856,9 @@ var init_InteractiveResultsView = __esm({
95875
96856
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
95876
96857
  hasAffects ? T.branch : T.last,
95877
96858
  " ",
95878
- import_chalk12.default.yellow("\u2192"),
96859
+ import_chalk14.default.yellow("\u2192"),
95879
96860
  " ",
95880
- import_chalk12.default.yellow("Upgrade to Pro"),
96861
+ import_chalk14.default.yellow("Upgrade to Pro"),
95881
96862
  " for finding details"
95882
96863
  ] }, "upgrade")
95883
96864
  );
@@ -95895,7 +96876,7 @@ var init_InteractiveResultsView = __esm({
95895
96876
  " ",
95896
96877
  sevColor(pad2(sevLabel, 5)),
95897
96878
  " ",
95898
- import_chalk12.default.dim(f.category ?? ""),
96879
+ import_chalk14.default.dim(f.category ?? ""),
95899
96880
  title
95900
96881
  ] }, `finding-${idx}`)
95901
96882
  );
@@ -95941,12 +96922,12 @@ function getHint(error2) {
95941
96922
  return null;
95942
96923
  }
95943
96924
  }
95944
- var import_chalk13, import_jsx_runtime12, ErrorView;
96925
+ var import_chalk15, import_jsx_runtime12, ErrorView;
95945
96926
  var init_ErrorView = __esm({
95946
96927
  async "src/ui/components/ErrorView.tsx"() {
95947
96928
  "use strict";
95948
96929
  await init_build2();
95949
- import_chalk13 = __toESM(require_source());
96930
+ import_chalk15 = __toESM(require_source());
95950
96931
  init_sanitize();
95951
96932
  import_jsx_runtime12 = __toESM(require_jsx_runtime());
95952
96933
  ErrorView = ({ error: error2 }) => {
@@ -95960,10 +96941,10 @@ var init_ErrorView = __esm({
95960
96941
  paddingLeft: 1,
95961
96942
  paddingRight: 1,
95962
96943
  children: [
95963
- /* @__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") }),
95964
96945
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { children: sanitize(error2.message) }),
95965
96946
  hint && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { children: [
95966
- import_chalk13.default.yellow("\u2192"),
96947
+ import_chalk15.default.yellow("\u2192"),
95967
96948
  " ",
95968
96949
  hint
95969
96950
  ] })
@@ -95975,13 +96956,13 @@ var init_ErrorView = __esm({
95975
96956
  });
95976
96957
 
95977
96958
  // src/ui/components/ProjectSelector.tsx
95978
- var import_react35, import_chalk14, import_jsx_runtime13, ProjectSelector;
96959
+ var import_react35, import_chalk16, import_jsx_runtime13, ProjectSelector;
95979
96960
  var init_ProjectSelector = __esm({
95980
96961
  async "src/ui/components/ProjectSelector.tsx"() {
95981
96962
  "use strict";
95982
96963
  import_react35 = __toESM(require_react());
95983
96964
  await init_build2();
95984
- import_chalk14 = __toESM(require_source());
96965
+ import_chalk16 = __toESM(require_source());
95985
96966
  init_sanitize();
95986
96967
  import_jsx_runtime13 = __toESM(require_jsx_runtime());
95987
96968
  ProjectSelector = ({ projects, onConfirm, onCancel, userStatus }) => {
@@ -96011,14 +96992,14 @@ var init_ProjectSelector = __esm({
96011
96992
  onCancel();
96012
96993
  }
96013
96994
  });
96014
- 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");
96015
96996
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 1, children: [
96016
96997
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Text, { children: [
96017
- import_chalk14.default.cyan("\u25C6"),
96998
+ import_chalk16.default.cyan("\u25C6"),
96018
96999
  " ",
96019
- import_chalk14.default.bold("Dependency Guardian"),
97000
+ import_chalk16.default.bold("Dependency Guardian"),
96020
97001
  " ",
96021
- userStatus ? import_chalk14.default.dim(userStatus) : ""
97002
+ userStatus ? import_chalk16.default.dim(userStatus) : ""
96022
97003
  ] }),
96023
97004
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { children: "" }),
96024
97005
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Text, { bold: true, children: [
@@ -96031,30 +97012,30 @@ var init_ProjectSelector = __esm({
96031
97012
  projects.map((proj, i) => {
96032
97013
  const isCursor = i === cursor;
96033
97014
  const isSelected = selected.has(i);
96034
- const prefix = isCursor ? import_chalk14.default.cyan("\u258C") : " ";
96035
- 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");
96036
97017
  const line = `${prefix}${check} ${sanitize(proj.relativePath).padEnd(40)} ${ecosystemLabel(proj.ecosystem).padEnd(5)} ${proj.packageCount} packages`;
96037
97018
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { backgroundColor: isCursor ? "#1a1a2e" : void 0, children: line }, i);
96038
97019
  }),
96039
97020
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { children: "" }),
96040
- /* @__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` }),
96041
97022
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { children: "" }),
96042
97023
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Text, { children: [
96043
- import_chalk14.default.bold.cyan("space"),
97024
+ import_chalk16.default.bold.cyan("space"),
96044
97025
  " ",
96045
- import_chalk14.default.dim("toggle"),
97026
+ import_chalk16.default.dim("toggle"),
96046
97027
  " ",
96047
- import_chalk14.default.bold.cyan("a"),
97028
+ import_chalk16.default.bold.cyan("a"),
96048
97029
  " ",
96049
- import_chalk14.default.dim("all"),
97030
+ import_chalk16.default.dim("all"),
96050
97031
  " ",
96051
- import_chalk14.default.bold.hex("#FFD700")("\u23CE"),
97032
+ import_chalk16.default.bold.hex("#FFD700")("\u23CE"),
96052
97033
  " ",
96053
- import_chalk14.default.bold.hex("#FFD700")("scan"),
97034
+ import_chalk16.default.bold.hex("#FFD700")("scan"),
96054
97035
  " ",
96055
- import_chalk14.default.bold.cyan("q"),
97036
+ import_chalk16.default.bold.cyan("q"),
96056
97037
  " ",
96057
- import_chalk14.default.dim("quit")
97038
+ import_chalk16.default.dim("quit")
96058
97039
  ] })
96059
97040
  ] });
96060
97041
  };
@@ -96398,9 +97379,9 @@ function useInstallWrapper(rawArgs, config3, opts, exit) {
96398
97379
  const result = await opts.callAnalyze(resolved, config3);
96399
97380
  if (result.action === "pass") {
96400
97381
  if (exit) exit();
96401
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97382
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
96402
97383
  process.stderr.write(
96403
- 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
96404
97385
  `)
96405
97386
  );
96406
97387
  process.stderr.write("\n");
@@ -96411,8 +97392,8 @@ function useInstallWrapper(rawArgs, config3, opts, exit) {
96411
97392
  if (result.action === "warn") {
96412
97393
  if (exit) exit();
96413
97394
  process.stdout.write(renderResultStatic(result, config3) + "\n");
96414
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
96415
- 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"));
96416
97397
  const code = await opts.runInstall(parsed.rawArgs);
96417
97398
  process.exit(code);
96418
97399
  return;
@@ -96525,16 +97506,16 @@ function groupPackages3(packages) {
96525
97506
  return [...map.values()].map((pkgs) => ({ packages: pkgs, key: pkgs[0].name })).sort((a, b) => b.packages[0].score - a.packages[0].score);
96526
97507
  }
96527
97508
  function severityColor(sev) {
96528
- if (sev >= 5) return (s) => import_chalk15.default.bold.red(s);
96529
- if (sev >= 4) return import_chalk15.default.magenta;
96530
- if (sev >= 3) return import_chalk15.default.yellow;
96531
- if (sev >= 2) return import_chalk15.default.dim;
96532
- 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;
96533
97514
  }
96534
97515
  function actionBadge3(score) {
96535
- if (score >= 70) return { label: "BLOCK", color: import_chalk15.default.red };
96536
- if (score >= 60) return { label: "WARN", color: import_chalk15.default.yellow };
96537
- 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 };
96538
97519
  }
96539
97520
  function truncate4(s, max) {
96540
97521
  return s.length <= max ? s : s.slice(0, max - 1) + "\u2026";
@@ -96542,12 +97523,12 @@ function truncate4(s, max) {
96542
97523
  function pad3(s, len) {
96543
97524
  return s + " ".repeat(Math.max(0, len - s.length));
96544
97525
  }
96545
- var import_chalk15, import_jsx_runtime17, SEVERITY_LABELS2, EVIDENCE_LIMIT2, ResultsView;
97526
+ var import_chalk17, import_jsx_runtime17, SEVERITY_LABELS2, EVIDENCE_LIMIT2, ResultsView;
96546
97527
  var init_ResultsView = __esm({
96547
97528
  async "src/ui/components/ResultsView.tsx"() {
96548
97529
  "use strict";
96549
97530
  await init_build2();
96550
- import_chalk15 = __toESM(require_source());
97531
+ import_chalk17 = __toESM(require_source());
96551
97532
  await init_ScoreHeader();
96552
97533
  await init_DurationLine();
96553
97534
  import_jsx_runtime17 = __toESM(require_jsx_runtime());
@@ -96573,15 +97554,15 @@ var init_ResultsView = __esm({
96573
97554
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ScoreHeader, { score: result.score, action: result.action }),
96574
97555
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Newline, {}),
96575
97556
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { children: [
96576
- import_chalk15.default.dim(`${total} package${total !== 1 ? "s" : ""} scanned`),
97557
+ import_chalk17.default.dim(`${total} package${total !== 1 ? "s" : ""} scanned`),
96577
97558
  flagged.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
96578
97559
  " ",
96579
- import_chalk15.default.yellow(`${flagged.length} flagged`),
97560
+ import_chalk17.default.yellow(`${flagged.length} flagged`),
96580
97561
  " ",
96581
- import_chalk15.default.green(`${clean.length} clean`)
97562
+ import_chalk17.default.green(`${clean.length} clean`)
96582
97563
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
96583
97564
  " ",
96584
- import_chalk15.default.green("all clean")
97565
+ import_chalk17.default.green("all clean")
96585
97566
  ] })
96586
97567
  ] }),
96587
97568
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Newline, {}),
@@ -96595,18 +97576,18 @@ var init_ResultsView = __esm({
96595
97576
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { children: [
96596
97577
  " ",
96597
97578
  color(pad3(label, 7)),
96598
- import_chalk15.default.bold(pad3(truncate4(names, 50), 52)),
96599
- 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}`)
96600
97581
  ] }, group.key);
96601
97582
  }),
96602
97583
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Newline, {})
96603
97584
  ] }),
96604
97585
  clean.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { children: [
96605
- import_chalk15.default.green("\u2713"),
97586
+ import_chalk17.default.green("\u2713"),
96606
97587
  " ",
96607
- import_chalk15.default.green.bold(String(clean.length)),
97588
+ import_chalk17.default.green.bold(String(clean.length)),
96608
97589
  " ",
96609
- 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`)
96610
97591
  ] }),
96611
97592
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Newline, {}),
96612
97593
  groups.filter((g) => g.packages[0].score > 0).map((group) => {
@@ -96616,9 +97597,9 @@ var init_ResultsView = __esm({
96616
97597
  const safeVersion = result.safeVersions[rep.name];
96617
97598
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Box_default, { flexDirection: "column", children: [
96618
97599
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { children: [
96619
- import_chalk15.default.dim("\u2500\u2500"),
97600
+ import_chalk17.default.dim("\u2500\u2500"),
96620
97601
  " ",
96621
- import_chalk15.default.bold(`${names} (score: ${rep.score})`)
97602
+ import_chalk17.default.bold(`${names} (score: ${rep.score})`)
96622
97603
  ] }),
96623
97604
  group.packages.length > 3 && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { dimColor: true, children: [
96624
97605
  " Affects: ",
@@ -96640,18 +97621,18 @@ var init_ResultsView = __esm({
96640
97621
  ] }),
96641
97622
  evidenceSlice.map((ev, i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { children: [
96642
97623
  " ",
96643
- import_chalk15.default.dim(truncate4(ev, 76))
97624
+ import_chalk17.default.dim(truncate4(ev, 76))
96644
97625
  ] }, i)),
96645
97626
  overflow > 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { children: [
96646
97627
  " ",
96647
- import_chalk15.default.dim(`... and ${overflow} more`)
97628
+ import_chalk17.default.dim(`... and ${overflow} more`)
96648
97629
  ] }),
96649
97630
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Text, { children: " " })
96650
97631
  ] }, idx);
96651
97632
  }),
96652
97633
  safeVersion && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { children: [
96653
97634
  " ",
96654
- import_chalk15.default.green(`Safe version: ${rep.name}@${safeVersion}`)
97635
+ import_chalk17.default.green(`Safe version: ${rep.name}@${safeVersion}`)
96655
97636
  ] }),
96656
97637
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Newline, {})
96657
97638
  ] }, group.key);
@@ -96664,13 +97645,13 @@ var init_ResultsView = __esm({
96664
97645
  });
96665
97646
 
96666
97647
  // src/ui/components/ConfirmPrompt.tsx
96667
- var import_chalk16, import_jsx_runtime18, ConfirmPrompt;
97648
+ var import_chalk18, import_jsx_runtime18, ConfirmPrompt;
96668
97649
  var init_ConfirmPrompt = __esm({
96669
97650
  async "src/ui/components/ConfirmPrompt.tsx"() {
96670
97651
  "use strict";
96671
97652
  await init_build2();
96672
97653
  await init_build2();
96673
- import_chalk16 = __toESM(require_source());
97654
+ import_chalk18 = __toESM(require_source());
96674
97655
  import_jsx_runtime18 = __toESM(require_jsx_runtime());
96675
97656
  ConfirmPrompt = ({
96676
97657
  message,
@@ -96686,15 +97667,15 @@ var init_ConfirmPrompt = __esm({
96686
97667
  });
96687
97668
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Box_default, { flexDirection: "column", children: [
96688
97669
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Text, { children: [
96689
- import_chalk16.default.red.bold("\u26A0"),
97670
+ import_chalk18.default.red.bold("\u26A0"),
96690
97671
  " ",
96691
- import_chalk16.default.bold(message)
97672
+ import_chalk18.default.bold(message)
96692
97673
  ] }),
96693
97674
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Text, { children: [
96694
97675
  "Install anyway? [",
96695
- import_chalk16.default.bold.green("y"),
97676
+ import_chalk18.default.bold.green("y"),
96696
97677
  "es / ",
96697
- import_chalk16.default.bold.red("N"),
97678
+ import_chalk18.default.bold.red("N"),
96698
97679
  "o]"
96699
97680
  ] })
96700
97681
  ] });
@@ -97081,10 +98062,272 @@ function closestCommand(input, commands) {
97081
98062
  return bestDist <= 3 ? best : null;
97082
98063
  }
97083
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
+
97084
98326
  // src/bin.ts
97085
98327
  init_alt_screen();
97086
98328
  var CLI_VERSION = getVersion();
97087
98329
  initTelemetry(CLI_VERSION);
98330
+ runMigrationIfNeeded();
97088
98331
  function setProcessTitle(title) {
97089
98332
  try {
97090
98333
  process.title = title;
@@ -97112,8 +98355,8 @@ var isInteractive = process.stdout.isTTY === true && !process.env.CI && !process
97112
98355
  async function main() {
97113
98356
  const rawCommand = process.argv[2];
97114
98357
  if (!rawCommand || rawCommand === "help" || rawCommand === "--help" || rawCommand === "-h") {
97115
- const { USAGE: USAGE4 } = await Promise.resolve().then(() => (init_config(), config_exports));
97116
- process.stdout.write(USAGE4);
98358
+ const { USAGE: USAGE6 } = await Promise.resolve().then(() => (init_config(), config_exports));
98359
+ process.stdout.write(USAGE6);
97117
98360
  return;
97118
98361
  }
97119
98362
  if (rawCommand === "--help-all" || rawCommand === "help-all") {
@@ -97126,15 +98369,15 @@ async function main() {
97126
98369
  `);
97127
98370
  return;
97128
98371
  }
97129
- 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"];
97130
98373
  if (rawCommand && !rawCommand.startsWith("-") && !KNOWN_COMMANDS.includes(rawCommand)) {
97131
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98374
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97132
98375
  const best = closestCommand(rawCommand, KNOWN_COMMANDS);
97133
98376
  const hint = best ? ` Did you mean '${best}'?` : "";
97134
98377
  process.stderr.write(`
97135
- ${chalk15.bold.red("Error:")} Unknown command '${rawCommand}'.${hint}
98378
+ ${chalk17.bold.red("Error:")} Unknown command '${rawCommand}'.${hint}
97136
98379
  `);
97137
- process.stderr.write(chalk15.dim(` Run 'dg --help' for available commands.
98380
+ process.stderr.write(chalk17.dim(` Run 'dg --help' for available commands.
97138
98381
 
97139
98382
  `));
97140
98383
  process.exit(1);
@@ -97170,16 +98413,16 @@ async function main() {
97170
98413
  } catch {
97171
98414
  }
97172
98415
  } else {
97173
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98416
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97174
98417
  process.stderr.write(
97175
- chalk15.dim(
98418
+ chalk17.dim(
97176
98419
  " note: --fresh skipped marking the first-run sentinel.\n The next dg command will re-trigger the wizard.\n\n"
97177
98420
  )
97178
98421
  );
97179
98422
  }
97180
98423
  } else {
97181
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97182
- 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"));
97183
98426
  }
97184
98427
  return;
97185
98428
  }
@@ -97227,11 +98470,11 @@ async function main() {
97227
98470
  if (v === "npm" || v === "pypi") ecosystem = v;
97228
98471
  }
97229
98472
  const { runNpmPublishCheck: runNpmPublishCheck2, runPypiPublishCheck: runPypiPublishCheck2, renderPublishCheckResult: renderPublishCheckResult2 } = await Promise.resolve().then(() => (init_publish_check(), publish_check_exports));
97230
- const { existsSync: existsSync16 } = await import("node:fs");
97231
- const { join: join18 } = await import("node:path");
98473
+ const { existsSync: existsSync21 } = await import("node:fs");
98474
+ const { join: join16 } = await import("node:path");
97232
98475
  if (ecosystem === "auto") {
97233
- if (existsSync16(join18(process.cwd(), "package.json"))) ecosystem = "npm";
97234
- 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";
97235
98478
  else {
97236
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");
97237
98480
  process.exit(3);
@@ -97242,9 +98485,9 @@ async function main() {
97242
98485
  if (wantJson) {
97243
98486
  process.stdout.write(JSON.stringify({ ok: false, error: out.errorMessage }) + "\n");
97244
98487
  } else {
97245
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98488
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97246
98489
  process.stderr.write(`
97247
- ${chalk15.red("publish-check error:")} ${out.errorMessage}
98490
+ ${chalk17.red("publish-check error:")} ${out.errorMessage}
97248
98491
 
97249
98492
  `);
97250
98493
  }
@@ -97286,8 +98529,8 @@ async function main() {
97286
98529
  await handleHookCommand2(process.argv.slice(3));
97287
98530
  const updateMsg2 = await updatePromise2;
97288
98531
  if (updateMsg2) {
97289
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97290
- process.stderr.write(chalk15.dim(updateMsg2));
98532
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98533
+ process.stderr.write(chalk17.dim(updateMsg2));
97291
98534
  }
97292
98535
  return;
97293
98536
  }
@@ -97300,9 +98543,21 @@ async function main() {
97300
98543
  const code = await handleCleanupCommand2(process.argv.slice(3));
97301
98544
  process.exit(code);
97302
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
+ }
97303
98558
  if (rawCommand === "logout") {
97304
98559
  const { getStoredApiKey: getStoredApiKey2, clearCredentials: clearCredentials2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
97305
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98560
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97306
98561
  const apiKey = getStoredApiKey2();
97307
98562
  if (apiKey) {
97308
98563
  const { parseConfig: parseConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
@@ -97317,7 +98572,7 @@ async function main() {
97317
98572
  }
97318
98573
  }
97319
98574
  clearCredentials2();
97320
- process.stderr.write(chalk15.green(" Logged out.\n"));
98575
+ process.stderr.write(chalk17.green(" Logged out.\n"));
97321
98576
  return;
97322
98577
  }
97323
98578
  const strictFlags = rawCommand !== "npm" && rawCommand !== "pip";
@@ -97341,8 +98596,8 @@ async function main() {
97341
98596
  await runStatic2(config3);
97342
98597
  } else {
97343
98598
  if (config3.mode === "off") {
97344
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97345
- 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"));
97346
98601
  process.exit(0);
97347
98602
  }
97348
98603
  const { getStoredApiKey: getStoredApiKey2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
@@ -97356,11 +98611,11 @@ async function main() {
97356
98611
  signal: AbortSignal.timeout(3e3)
97357
98612
  });
97358
98613
  if (resp.ok) {
97359
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98614
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97360
98615
  const data = await resp.json();
97361
98616
  const name = data.name || "authenticated";
97362
98617
  const tier = data.tier || "free";
97363
- const tierColor = tier === "free" ? chalk15.yellow(tier) : chalk15.green(tier);
98618
+ const tierColor = tier === "free" ? chalk17.yellow(tier) : chalk17.green(tier);
97364
98619
  userStatus = `${name} \xB7 ${tierColor}`;
97365
98620
  if (data.scansLimit === null || data.scansLimit === void 0) {
97366
98621
  scanUsage = "unlimited scans";
@@ -97396,8 +98651,8 @@ async function main() {
97396
98651
  }
97397
98652
  const updateMsg2 = await updatePromise;
97398
98653
  if (updateMsg2) {
97399
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97400
- process.stderr.write(chalk15.dim(updateMsg2));
98654
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98655
+ process.stderr.write(chalk17.dim(updateMsg2));
97401
98656
  }
97402
98657
  try {
97403
98658
  const { pingSetupEvent: pingSetupEvent2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
@@ -97416,8 +98671,8 @@ async function main() {
97416
98671
  }
97417
98672
  const updateMsg2 = await updatePromise;
97418
98673
  if (updateMsg2) {
97419
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97420
- process.stderr.write(chalk15.dim(updateMsg2));
98674
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98675
+ process.stderr.write(chalk17.dim(updateMsg2));
97421
98676
  }
97422
98677
  try {
97423
98678
  const { pingSetupEvent: pingSetupEvent2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
@@ -97443,8 +98698,8 @@ async function main() {
97443
98698
  }
97444
98699
  const updateMsg = await updatePromise;
97445
98700
  if (updateMsg) {
97446
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97447
- process.stderr.write(chalk15.dim(updateMsg));
98701
+ const chalk17 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
98702
+ process.stderr.write(chalk17.dim(updateMsg));
97448
98703
  }
97449
98704
  try {
97450
98705
  const { pingSetupEvent: pingSetupEvent2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
@@ -97468,8 +98723,8 @@ main().catch(async (err) => {
97468
98723
  authenticated: isAuthed
97469
98724
  });
97470
98725
  await flush2();
97471
- const chalk15 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
97472
- 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;
97473
98728
  if (process.argv.includes("--json")) {
97474
98729
  process.stdout.write(JSON.stringify({
97475
98730
  error: true,