@visulima/vis 1.0.0-alpha.1 → 1.0.0-alpha.11

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 (120) hide show
  1. package/CHANGELOG.md +403 -12
  2. package/LICENSE.md +283 -0
  3. package/README.md +254 -9
  4. package/dist/bin.js +9 -146
  5. package/dist/config/index.d.ts +1818 -0
  6. package/dist/config/index.js +2 -0
  7. package/dist/generate/index.d.ts +157 -0
  8. package/dist/generate/index.js +3 -0
  9. package/dist/packem_chunks/applyDefaults.js +336 -0
  10. package/dist/packem_chunks/bin.js +9577 -0
  11. package/dist/packem_chunks/doctor-probe.js +112 -0
  12. package/dist/packem_chunks/fix.js +234 -0
  13. package/dist/packem_chunks/handler.js +99 -0
  14. package/dist/packem_chunks/handler10.js +53 -0
  15. package/dist/packem_chunks/handler11.js +32 -0
  16. package/dist/packem_chunks/handler12.js +100 -0
  17. package/dist/packem_chunks/handler13.js +25 -0
  18. package/dist/packem_chunks/handler14.js +916 -0
  19. package/dist/packem_chunks/handler15.js +206 -0
  20. package/dist/packem_chunks/handler16.js +124 -0
  21. package/dist/packem_chunks/handler17.js +13 -0
  22. package/dist/packem_chunks/handler18.js +106 -0
  23. package/dist/packem_chunks/handler19.js +19 -0
  24. package/dist/packem_chunks/handler2.js +75 -0
  25. package/dist/packem_chunks/handler20.js +29 -0
  26. package/dist/packem_chunks/handler21.js +222 -0
  27. package/dist/packem_chunks/handler22.js +237 -0
  28. package/dist/packem_chunks/handler23.js +101 -0
  29. package/dist/packem_chunks/handler24.js +110 -0
  30. package/dist/packem_chunks/handler25.js +402 -0
  31. package/dist/packem_chunks/handler26.js +13 -0
  32. package/dist/packem_chunks/handler27.js +63 -0
  33. package/dist/packem_chunks/handler28.js +34 -0
  34. package/dist/packem_chunks/handler29.js +458 -0
  35. package/dist/packem_chunks/handler3.js +95 -0
  36. package/dist/packem_chunks/handler30.js +170 -0
  37. package/dist/packem_chunks/handler31.js +530 -0
  38. package/dist/packem_chunks/handler32.js +214 -0
  39. package/dist/packem_chunks/handler33.js +119 -0
  40. package/dist/packem_chunks/handler34.js +630 -0
  41. package/dist/packem_chunks/handler35.js +283 -0
  42. package/dist/packem_chunks/handler36.js +542 -0
  43. package/dist/packem_chunks/handler37.js +762 -0
  44. package/dist/packem_chunks/handler38.js +989 -0
  45. package/dist/packem_chunks/handler39.js +574 -0
  46. package/dist/packem_chunks/handler4.js +90 -0
  47. package/dist/packem_chunks/handler40.js +1685 -0
  48. package/dist/packem_chunks/handler41.js +1088 -0
  49. package/dist/packem_chunks/handler42.js +797 -0
  50. package/dist/packem_chunks/handler43.js +2658 -0
  51. package/dist/packem_chunks/handler44.js +3886 -0
  52. package/dist/packem_chunks/handler45.js +2574 -0
  53. package/dist/packem_chunks/handler46.js +3769 -0
  54. package/dist/packem_chunks/handler47.js +1491 -0
  55. package/dist/packem_chunks/handler5.js +174 -0
  56. package/dist/packem_chunks/handler6.js +95 -0
  57. package/dist/packem_chunks/handler7.js +115 -0
  58. package/dist/packem_chunks/handler8.js +12 -0
  59. package/dist/packem_chunks/handler9.js +29 -0
  60. package/dist/packem_chunks/heal-accept.js +522 -0
  61. package/dist/packem_chunks/heal.js +673 -0
  62. package/dist/packem_chunks/index.js +873 -0
  63. package/dist/packem_chunks/loader.js +23 -0
  64. package/dist/packem_shared/VisUpdateApp-D-Yz_wvg.js +1316 -0
  65. package/dist/packem_shared/_commonjsHelpers-BqLXS_qQ.js +5 -0
  66. package/dist/packem_shared/ai-analysis-CHeB1joD.js +367 -0
  67. package/dist/packem_shared/ai-cache-Be_jexe4.js +142 -0
  68. package/dist/packem_shared/ai-fix-B9iQVcD2.js +379 -0
  69. package/dist/packem_shared/cache-directory-2qvs4goY.js +98 -0
  70. package/dist/packem_shared/catalog-BJTtyi-O.js +1371 -0
  71. package/dist/packem_shared/dependency-scan-A0KSklpG.js +188 -0
  72. package/dist/packem_shared/docker-2iZzc280.js +181 -0
  73. package/dist/packem_shared/failure-log-Cz3Z4SKL.js +100 -0
  74. package/dist/packem_shared/flakiness-goTxXuCX.js +180 -0
  75. package/dist/packem_shared/otel-DCvqCTz_.js +158 -0
  76. package/dist/packem_shared/otelPlugin-DFaLDvJf.js +3 -0
  77. package/dist/packem_shared/registry-CbqXI0rc.js +272 -0
  78. package/dist/packem_shared/run-summary-utils-PVMl4aIh.js +130 -0
  79. package/dist/packem_shared/runtime-check-Cobi3p6l.js +127 -0
  80. package/dist/packem_shared/selectors-SM69TfqC.js +194 -0
  81. package/dist/packem_shared/symbols-Ta7g2nU-.js +14 -0
  82. package/dist/packem_shared/toolchain-BdZd9eBi.js +975 -0
  83. package/dist/packem_shared/typosquats-C-bCh3PX.js +1210 -0
  84. package/dist/packem_shared/use-measured-height-CNP0vT4M.js +20 -0
  85. package/dist/packem_shared/utils-CthVdBPS.js +40 -0
  86. package/dist/packem_shared/xxh3-Ck8mXNg1.js +239 -0
  87. package/index.js +773 -0
  88. package/package.json +82 -21
  89. package/schemas/project.schema.json +420 -0
  90. package/schemas/vis-config.schema.json +501 -0
  91. package/skills/vis/SKILL.md +96 -0
  92. package/templates/buildkite-ci/.buildkite/pipeline.yml.tera +85 -0
  93. package/templates/buildkite-ci/template.yml +20 -0
  94. package/dist/ai-analysis.d.ts +0 -40
  95. package/dist/ai-cache.d.ts +0 -21
  96. package/dist/bin.d.ts +0 -1
  97. package/dist/catalog.d.ts +0 -110
  98. package/dist/commands/affected.d.ts +0 -3
  99. package/dist/commands/ai.d.ts +0 -3
  100. package/dist/commands/analyze.d.ts +0 -3
  101. package/dist/commands/check.d.ts +0 -3
  102. package/dist/commands/graph.d.ts +0 -3
  103. package/dist/commands/hook/constants.d.ts +0 -8
  104. package/dist/commands/hook/index.d.ts +0 -3
  105. package/dist/commands/hook/install.d.ts +0 -7
  106. package/dist/commands/hook/migrate.d.ts +0 -27
  107. package/dist/commands/hook/uninstall.d.ts +0 -3
  108. package/dist/commands/migrate/constants.d.ts +0 -12
  109. package/dist/commands/migrate/deps.d.ts +0 -32
  110. package/dist/commands/migrate/index.d.ts +0 -3
  111. package/dist/commands/migrate/json.d.ts +0 -20
  112. package/dist/commands/migrate/lint-staged.d.ts +0 -62
  113. package/dist/commands/migrate/types.d.ts +0 -20
  114. package/dist/commands/run.d.ts +0 -3
  115. package/dist/commands/staged.d.ts +0 -3
  116. package/dist/commands/update.d.ts +0 -3
  117. package/dist/config.d.ts +0 -40
  118. package/dist/config.js +0 -1
  119. package/dist/package-manager.d.ts +0 -23
  120. package/dist/workspace.d.ts +0 -58
@@ -0,0 +1,170 @@
1
+ import { detectAllProviders, runProvider } from '@visulima/find-ai-runner';
2
+ import { renderToString, Table } from '@visulima/tui';
3
+ import React from 'react';
4
+ import { a as resolveProvider, D as DEFAULT_PRIORITY } from '../packem_shared/ai-analysis-CHeB1joD.js';
5
+ import { bold, dim, cyan, green, yellow } from '@visulima/colorize';
6
+
7
+ const AI_DISCOVERY_META = {
8
+ command: "ai",
9
+ description: "AI-assisted commands: provider detection, cache management, and failure-fix proposals."
10
+ };
11
+ const typeName = (type) => {
12
+ if (typeof type !== "function") {
13
+ return String(type);
14
+ }
15
+ const { name } = type;
16
+ if (name === "Boolean") {
17
+ return "boolean";
18
+ }
19
+ if (name === "Number") {
20
+ return "number";
21
+ }
22
+ if (name === "String") {
23
+ return "string";
24
+ }
25
+ return name ?? "unknown";
26
+ };
27
+ const buildSubcommand = (command) => {
28
+ const path = [...command.commandPath ?? [], command.name].join(" ");
29
+ const examples = (command.examples ?? []).map(([cmd, description]) => {
30
+ return {
31
+ command: cmd ?? "",
32
+ description: description ?? ""
33
+ };
34
+ });
35
+ const options = (command.options ?? []).map((option) => {
36
+ return {
37
+ defaultValue: option.defaultValue,
38
+ description: option.description,
39
+ name: option.name,
40
+ type: typeName(option.type)
41
+ };
42
+ });
43
+ return {
44
+ argument: command.argument ? { description: command.argument.description, name: command.argument.name } : void 0,
45
+ description: command.description ?? "",
46
+ examples,
47
+ name: command.name,
48
+ options,
49
+ path
50
+ };
51
+ };
52
+ const buildDiscoveryPayload = (subcommands, meta = AI_DISCOVERY_META) => {
53
+ return {
54
+ command: meta.command,
55
+ description: meta.description,
56
+ subcommands: subcommands.map((subcommand) => buildSubcommand(subcommand))
57
+ };
58
+ };
59
+ const renderDiscoveryJson = (subcommands, meta = AI_DISCOVERY_META) => `${JSON.stringify(buildDiscoveryPayload(subcommands, meta), void 0, 2)}
60
+ `;
61
+ const renderDiscoveryText = (subcommands, meta = AI_DISCOVERY_META) => {
62
+ const payload = buildDiscoveryPayload(subcommands, meta);
63
+ const lines = [];
64
+ lines.push(bold(`vis ${payload.command} — ${payload.description}`));
65
+ lines.push("");
66
+ lines.push(dim("Subcommands:"));
67
+ for (const subcommand of payload.subcommands) {
68
+ const argumentSegment = subcommand.argument ? ` ${cyan(`<${subcommand.argument.name}>`)}` : "";
69
+ lines.push("");
70
+ lines.push(` ${green(`vis ${subcommand.path}`)}${argumentSegment}`);
71
+ if (subcommand.description) {
72
+ lines.push(` ${subcommand.description}`);
73
+ }
74
+ if (subcommand.options.length > 0) {
75
+ const optionList = subcommand.options.map((option) => `--${option.name}${option.type === "boolean" ? "" : `=<${option.type}>`}`).join(", ");
76
+ lines.push(dim(` options: ${optionList}`));
77
+ }
78
+ if (subcommand.examples.length > 0) {
79
+ lines.push(dim(" examples:"));
80
+ for (const example of subcommand.examples) {
81
+ const trailing = example.description ? dim(` — ${example.description}`) : "";
82
+ lines.push(` ${yellow(example.command)}${trailing}`);
83
+ }
84
+ }
85
+ }
86
+ lines.push("");
87
+ lines.push(dim(`Run \`vis ${payload.command} discover-help\` for the machine-readable JSON catalogue (designed for AI agents).`));
88
+ lines.push(dim(`Run \`vis ${payload.command} <subcommand> --help\` for full usage of a specific subcommand.`));
89
+ return `${lines.join("\n")}
90
+ `;
91
+ };
92
+
93
+ const loadDiscoverableSubcommands = async () => {
94
+ const { default: aiCommands } = await import('./bin.js').then(n => n.ac);
95
+ return aiCommands.filter((cmd) => cmd.name !== "ai");
96
+ };
97
+ const aiRootExecute = async () => {
98
+ const subcommands = await loadDiscoverableSubcommands();
99
+ process.stderr.write(renderDiscoveryText(subcommands));
100
+ };
101
+ const aiDiscoverHelpExecute = async () => {
102
+ const subcommands = await loadDiscoverableSubcommands();
103
+ process.stdout.write(renderDiscoveryJson(subcommands));
104
+ };
105
+ const aiTestExecute = async ({ logger, visConfig }) => {
106
+ const aiConfig = visConfig?.ai;
107
+ const provider = resolveProvider(aiConfig);
108
+ if (!provider) {
109
+ logger.error("No AI provider available to test.");
110
+ process.exitCode = 1;
111
+ return;
112
+ }
113
+ logger.info(`Testing ${provider.name}...`);
114
+ try {
115
+ const result = await runProvider(provider, "Reply with exactly: OK", { timeoutMs: 3e4 });
116
+ logger.info(`Provider ${provider.name} responded: ${result.stdout.trim().slice(0, 200)}`);
117
+ } catch (error) {
118
+ const message = error instanceof Error ? error.message : String(error);
119
+ logger.error(`Provider ${provider.name} failed: ${message}`);
120
+ process.exitCode = 1;
121
+ }
122
+ };
123
+ const aiProvidersExecute = ({ logger, options, visConfig }) => {
124
+ const format = options.format ?? "table";
125
+ const aiConfig = visConfig?.ai;
126
+ const allProviders = detectAllProviders();
127
+ const selected = resolveProvider(aiConfig);
128
+ if (format === "json") {
129
+ const output2 = allProviders.map((p) => {
130
+ return {
131
+ available: p.available,
132
+ method: p.detectionMethod,
133
+ name: p.name,
134
+ path: p.path,
135
+ priority: DEFAULT_PRIORITY[p.name] ?? 0,
136
+ selected: p.name === selected?.name,
137
+ version: p.version
138
+ };
139
+ });
140
+ process.stdout.write(`${JSON.stringify(output2, void 0, 2)}
141
+ `);
142
+ return;
143
+ }
144
+ const tableData = allProviders.map((provider) => {
145
+ return {
146
+ method: provider.detectionMethod ?? "-",
147
+ path: provider.path ?? "-",
148
+ priority: String(DEFAULT_PRIORITY[provider.name] ?? 0),
149
+ provider: provider.name,
150
+ selected: provider.name === selected?.name ? ">>>" : "",
151
+ status: provider.available ? "available" : "not found",
152
+ version: provider.version ?? "-"
153
+ };
154
+ });
155
+ const columns = process.stdout.columns || 80;
156
+ const output = renderToString(React.createElement(Table, { data: tableData }), { columns });
157
+ logger.info(output);
158
+ if (selected) {
159
+ logger.info(`
160
+ Selected provider: ${selected.name} (priority ${String(DEFAULT_PRIORITY[selected.name] ?? 0)})`);
161
+ } else {
162
+ logger.info("\nNo AI provider available. Install one of the supported AI CLI tools.");
163
+ }
164
+ };
165
+ const aiFixExecute = async (toolbox) => {
166
+ const { aiFix } = await import('./fix.js');
167
+ await aiFix(toolbox);
168
+ };
169
+
170
+ export { aiDiscoverHelpExecute, aiFixExecute, aiProvidersExecute, aiRootExecute, aiTestExecute };
@@ -0,0 +1,530 @@
1
+ import { dim, yellow, cyan, magenta, red } from '@visulima/colorize';
2
+ import { isAccessibleSync, readFileSync, writeFileSync } from '@visulima/fs';
3
+ import { readYamlSync } from '@visulima/fs/yaml';
4
+ import { join } from '@visulima/path';
5
+ import { c as detectPm, p as pail, f as fetchSocketReports, t as findAcceptedRisk, D as DEFAULT_LOW_SCORE_THRESHOLD, a as buildSocketOptions, H as scoreLabel, J as getFullPackageName } from './bin.js';
6
+ import { l as lockedPackages, f as findDuplicateDependencies, s as startScanProgress } from '../packem_shared/dependency-scan-A0KSklpG.js';
7
+ import { a as fetchVulnerabilities } from '../packem_shared/catalog-BJTtyi-O.js';
8
+
9
+ const toStringArray = (value) => {
10
+ if (!Array.isArray(value)) {
11
+ return [];
12
+ }
13
+ return value.filter((v) => typeof v === "string");
14
+ };
15
+ const matchesGlobList = (value, patterns) => {
16
+ for (const pattern of patterns) {
17
+ if (pattern === value) {
18
+ return true;
19
+ }
20
+ if (pattern.endsWith("*") && value.startsWith(pattern.slice(0, -1))) {
21
+ return true;
22
+ }
23
+ }
24
+ return false;
25
+ };
26
+ const readPnpmAuditExclusions = (workspaceRoot) => {
27
+ const filePath = join(workspaceRoot, "pnpm-workspace.yaml");
28
+ if (!isAccessibleSync(filePath)) {
29
+ return { excludedPackages: [], ignoredAdvisories: [] };
30
+ }
31
+ try {
32
+ const data = readYamlSync(filePath);
33
+ return {
34
+ excludedPackages: [],
35
+ ignoredAdvisories: [...toStringArray(data?.auditConfig?.ignoreCves), ...toStringArray(data?.auditConfig?.ignoreGhsas)]
36
+ };
37
+ } catch {
38
+ return { excludedPackages: [], ignoredAdvisories: [] };
39
+ }
40
+ };
41
+ const readYarnAuditExclusions = (workspaceRoot) => {
42
+ const filePath = join(workspaceRoot, ".yarnrc.yml");
43
+ if (!isAccessibleSync(filePath)) {
44
+ return { excludedPackages: [], ignoredAdvisories: [] };
45
+ }
46
+ try {
47
+ const data = readYamlSync(filePath);
48
+ return {
49
+ excludedPackages: toStringArray(data?.npmAuditExcludePackages),
50
+ ignoredAdvisories: toStringArray(data?.npmAuditIgnoreAdvisories)
51
+ };
52
+ } catch {
53
+ return { excludedPackages: [], ignoredAdvisories: [] };
54
+ }
55
+ };
56
+ const readNativeAuditExclusions = (workspaceRoot, pm) => {
57
+ switch (pm) {
58
+ case "pnpm": {
59
+ return readPnpmAuditExclusions(workspaceRoot);
60
+ }
61
+ case "yarn": {
62
+ return readYarnAuditExclusions(workspaceRoot);
63
+ }
64
+ default: {
65
+ return { excludedPackages: [], ignoredAdvisories: [] };
66
+ }
67
+ }
68
+ };
69
+ const isAdvisoryExcluded = (vulnId, exclusions, aliases) => {
70
+ if (matchesGlobList(vulnId, exclusions.ignoredAdvisories)) {
71
+ return true;
72
+ }
73
+ if (aliases) {
74
+ for (const alias of aliases) {
75
+ if (matchesGlobList(alias, exclusions.ignoredAdvisories)) {
76
+ return true;
77
+ }
78
+ }
79
+ }
80
+ return false;
81
+ };
82
+ const isPackageExcluded = (packageName, exclusions) => matchesGlobList(packageName, exclusions.excludedPackages);
83
+ const syncAcceptedRisksToNativeConfig = (pm, workspaceRoot, advisoryIds) => {
84
+ if (advisoryIds.length === 0) {
85
+ return ["No advisory IDs to sync."];
86
+ }
87
+ const actions = [];
88
+ switch (pm) {
89
+ case "bun": {
90
+ actions.push(`bun has no audit config file. Use CLI flags: bun audit ${advisoryIds.map((id) => `--ignore ${id}`).join(" ")}`);
91
+ break;
92
+ }
93
+ case "npm": {
94
+ actions.push("npm has no native audit exclusion config. vis accepted risks are the only layer.");
95
+ break;
96
+ }
97
+ case "pnpm": {
98
+ const filePath = join(workspaceRoot, "pnpm-workspace.yaml");
99
+ if (!isAccessibleSync(filePath)) {
100
+ actions.push("pnpm-workspace.yaml not found. Cannot sync.");
101
+ break;
102
+ }
103
+ const existing = readPnpmAuditExclusions(workspaceRoot);
104
+ const existingCves = new Set(existing.ignoredAdvisories.filter((id) => id.startsWith("CVE-")));
105
+ const existingGhsas = new Set(existing.ignoredAdvisories.filter((id) => id.startsWith("GHSA-")));
106
+ const newCves = advisoryIds.filter((id) => id.startsWith("CVE-"));
107
+ const newGhsas = advisoryIds.filter((id) => id.startsWith("GHSA-"));
108
+ const mergedCves = [.../* @__PURE__ */ new Set([...existingCves, ...newCves])];
109
+ const mergedGhsas = [.../* @__PURE__ */ new Set([...existingGhsas, ...newGhsas])];
110
+ const addedCves = newCves.filter((id) => !existingCves.has(id)).length;
111
+ const addedGhsas = newGhsas.filter((id) => !existingGhsas.has(id)).length;
112
+ if (addedCves === 0 && addedGhsas === 0) {
113
+ actions.push("All advisory IDs already present in pnpm-workspace.yaml.");
114
+ break;
115
+ }
116
+ let content = readFileSync(filePath);
117
+ if (mergedCves.length > 0) {
118
+ const cveBlock = ` ignoreCves:
119
+ ${mergedCves.map((id) => ` - ${id}`).join("\n")}
120
+ `;
121
+ if (/auditConfig:/.test(content)) {
122
+ content = /ignoreCves:/.test(content) ? content.replace(/ignoreCves:\s*\n(?:\s+-\s+(?:\S.*|[\t\v\f \u00A0\u1680\u2000-\u200A\u202F\u205F\u3000\uFEFF])\n)*/, cveBlock) : content.replace(/auditConfig:\s*\n/, `auditConfig:
123
+ ${cveBlock}`);
124
+ } else {
125
+ content = `${content.trimEnd()}
126
+
127
+ auditConfig:
128
+ ${cveBlock}`;
129
+ }
130
+ if (addedCves > 0) {
131
+ actions.push(`Added ${String(addedCves)} new CVE${addedCves === 1 ? "" : "s"} to pnpm-workspace.yaml (${String(mergedCves.length)} total)`);
132
+ }
133
+ }
134
+ if (mergedGhsas.length > 0) {
135
+ const ghsaBlock = ` ignoreGhsas:
136
+ ${mergedGhsas.map((id) => ` - ${id}`).join("\n")}
137
+ `;
138
+ if (/auditConfig:/.test(content)) {
139
+ content = /ignoreGhsas:/.test(content) ? content.replace(/ignoreGhsas:\s*\n(?:\s+-\s+(?:\S.*|[\t\v\f \u00A0\u1680\u2000-\u200A\u202F\u205F\u3000\uFEFF])\n)*/, ghsaBlock) : content.replace(/(auditConfig:[\s\S]*?)(\n\S|\n?$)/m, `$1${ghsaBlock}$2`);
140
+ }
141
+ if (addedGhsas > 0) {
142
+ actions.push(
143
+ `Added ${String(addedGhsas)} new GHSA${addedGhsas === 1 ? "" : "s"} to pnpm-workspace.yaml (${String(mergedGhsas.length)} total)`
144
+ );
145
+ }
146
+ }
147
+ writeFileSync(filePath, content);
148
+ break;
149
+ }
150
+ case "yarn": {
151
+ const filePath = join(workspaceRoot, ".yarnrc.yml");
152
+ if (!isAccessibleSync(filePath)) {
153
+ actions.push(".yarnrc.yml not found. Cannot sync.");
154
+ break;
155
+ }
156
+ const existingYarn = readYarnAuditExclusions(workspaceRoot);
157
+ const existingSet = new Set(existingYarn.ignoredAdvisories);
158
+ const merged = [.../* @__PURE__ */ new Set([...existingSet, ...advisoryIds])];
159
+ const added = advisoryIds.filter((id) => !existingSet.has(id)).length;
160
+ if (added === 0) {
161
+ actions.push("All advisory IDs already present in .yarnrc.yml.");
162
+ break;
163
+ }
164
+ let content = readFileSync(filePath);
165
+ const advisoryBlock = `npmAuditIgnoreAdvisories:
166
+ ${merged.map((id) => ` - "${id}"`).join("\n")}
167
+ `;
168
+ content = /npmAuditIgnoreAdvisories:/.test(content) ? content.replace(
169
+ /npmAuditIgnoreAdvisories:\s*\n(?:\s+-\s+(?:\S.*|[\t\v\f \u00A0\u1680\u2000-\u200A\u202F\u205F\u3000\uFEFF])\n)*/,
170
+ advisoryBlock
171
+ ) : `${content.trimEnd()}
172
+
173
+ ${advisoryBlock}`;
174
+ writeFileSync(filePath, content);
175
+ actions.push(`Synced ${String(added)} advisor${added === 1 ? "y" : "ies"} to .yarnrc.yml (${String(merged.length)} total)`);
176
+ break;
177
+ }
178
+ default: {
179
+ actions.push(`Unknown package manager: ${pm}`);
180
+ }
181
+ }
182
+ return actions;
183
+ };
184
+
185
+ const SEVERITY_ORDER = {
186
+ CRITICAL: 0,
187
+ HIGH: 1,
188
+ LOW: 3,
189
+ MODERATE: 2,
190
+ UNKNOWN: 4
191
+ };
192
+ const SOCKET_ALERT_COLORS = {
193
+ critical: red,
194
+ high: magenta,
195
+ low: cyan,
196
+ medium: yellow
197
+ };
198
+ const severityPassesFilter = (severity, filter) => {
199
+ const filterLevel = SEVERITY_ORDER[filter.toUpperCase()] ?? SEVERITY_ORDER.MODERATE ?? 2;
200
+ const vulnLevel = SEVERITY_ORDER[severity.toUpperCase()] ?? 4;
201
+ return vulnLevel <= filterLevel;
202
+ };
203
+ const SEVERITY_COLOR_FN = {
204
+ CRITICAL: red,
205
+ HIGH: magenta,
206
+ LOW: cyan,
207
+ MODERATE: yellow,
208
+ UNKNOWN: dim
209
+ };
210
+ const formatVulnLine = (name, version, vuln, isAccepted) => {
211
+ const colorFunction = SEVERITY_COLOR_FN[vuln.severity] ?? dim;
212
+ const badge = isAccepted ? ` ${dim("[acknowledged]")}` : "";
213
+ const fixedVersions = vuln.fixedVersions ?? [];
214
+ const fixed = fixedVersions.length > 0 ? ` (fix: ${fixedVersions.join(", ")})` : "";
215
+ return ` ${colorFunction(vuln.severity)} ${vuln.id} — ${name}@${version}${badge}
216
+ ${vuln.summary}${fixed}`;
217
+ };
218
+ const formatSocketLine = (report, isAccepted) => {
219
+ const name = getFullPackageName(report);
220
+ const pct = `${String(Math.round(report.score.overall * 100))}%`;
221
+ const badge = isAccepted ? ` ${dim("[acknowledged]")}` : "";
222
+ const alerts = report.alerts.length > 0 ? `, ${String(report.alerts.length)} alert${report.alerts.length === 1 ? "" : "s"}` : "";
223
+ return ` ${pct} ${name}@${report.version} (${scoreLabel(report.score.overall)}${alerts})${badge}`;
224
+ };
225
+ const executeAudit = async (workspaceRoot, options, visConfig, _logger) => {
226
+ const severityFilter = options.severity ?? "low";
227
+ const isJson = options.format === "json" || Boolean(options.json);
228
+ const showFixes = Boolean(options.fix);
229
+ const showAccepted = Boolean(options.showAccepted);
230
+ const socketConfig = visConfig?.security?.socket;
231
+ const acceptedRisks = socketConfig?.acceptedRisks;
232
+ const pm = detectPm(workspaceRoot);
233
+ const nativeExclusions = readNativeAuditExclusions(workspaceRoot, pm.name);
234
+ if (nativeExclusions.ignoredAdvisories.length > 0 || nativeExclusions.excludedPackages.length > 0) {
235
+ pail.info(
236
+ `Loaded ${String(nativeExclusions.ignoredAdvisories.length)} ignored advisor${nativeExclusions.ignoredAdvisories.length === 1 ? "y" : "ies"} and ${String(nativeExclusions.excludedPackages.length)} excluded package${nativeExclusions.excludedPackages.length === 1 ? "" : "s"} from ${pm.name} config.`
237
+ );
238
+ }
239
+ const installed = lockedPackages(workspaceRoot, pm.name);
240
+ if (installed.length === 0) {
241
+ pail.info(`No ${pm.name} lockfile entries found. Run ${pm.name} install first.`);
242
+ return;
243
+ }
244
+ if (!isJson) {
245
+ pail.info(`Scanning ${String(installed.length)} installed packages…`);
246
+ }
247
+ const packagesToScan = installed.map((p) => {
248
+ return { name: p.name, version: p.version };
249
+ });
250
+ const socketOptions = buildSocketOptions(socketConfig);
251
+ const duplicates = findDuplicateDependencies(workspaceRoot, pm.name);
252
+ const progressTasks = [
253
+ { id: "vulnerabilities", label: "Known vulnerabilities (OSV)" },
254
+ ...socketOptions ? [{ id: "socket", label: "Socket.dev supply-chain reports" }] : []
255
+ ];
256
+ const progress = startScanProgress(progressTasks, { live: !isJson });
257
+ const startedAt = Date.now();
258
+ const fmtElapsed = (start) => {
259
+ const ms = Date.now() - start;
260
+ return ms >= 1e3 ? `${(ms / 1e3).toFixed(1)}s` : `${String(Math.round(ms))}ms`;
261
+ };
262
+ let vulnMap;
263
+ let socketReports;
264
+ try {
265
+ const vulnStart = Date.now();
266
+ const socketStart = Date.now();
267
+ progress.start("vulnerabilities");
268
+ if (socketOptions) {
269
+ progress.start("socket");
270
+ }
271
+ [vulnMap, socketReports] = await Promise.all([
272
+ fetchVulnerabilities(packagesToScan).then((map) => {
273
+ let count = 0;
274
+ for (const list of map.values()) {
275
+ count += list.length;
276
+ }
277
+ progress.finish(
278
+ "vulnerabilities",
279
+ count > 0 ? "warn" : "ok",
280
+ count > 0 ? `${String(count)} found · ${fmtElapsed(vulnStart)}` : `none found · ${fmtElapsed(vulnStart)}`
281
+ );
282
+ return map;
283
+ }).catch((error) => {
284
+ const message = error instanceof Error ? error.message : String(error);
285
+ progress.finish("vulnerabilities", "error", message);
286
+ return /* @__PURE__ */ new Map();
287
+ }),
288
+ socketOptions ? fetchSocketReports(packagesToScan, socketOptions).then((reports) => {
289
+ let alerts = 0;
290
+ let low = 0;
291
+ for (const report of reports.values()) {
292
+ alerts += report.alerts.length;
293
+ if (report.score.overall < DEFAULT_LOW_SCORE_THRESHOLD) {
294
+ low += 1;
295
+ }
296
+ }
297
+ const total = alerts + low;
298
+ progress.finish(
299
+ "socket",
300
+ total > 0 ? "warn" : "ok",
301
+ total > 0 ? `${String(alerts)} alert${alerts === 1 ? "" : "s"}, ${String(low)} low-score · ${fmtElapsed(socketStart)}` : `clean · ${fmtElapsed(socketStart)}`
302
+ );
303
+ return reports;
304
+ }).catch((error) => {
305
+ const message = error instanceof Error ? error.message : String(error);
306
+ progress.finish("socket", "error", message);
307
+ return /* @__PURE__ */ new Map();
308
+ }) : Promise.resolve(/* @__PURE__ */ new Map())
309
+ ]);
310
+ } finally {
311
+ progress.stop();
312
+ }
313
+ if (!isJson) {
314
+ pail.info(dim(`Scan completed in ${fmtElapsed(startedAt)}`));
315
+ }
316
+ const entries = [];
317
+ for (const pkg of installed) {
318
+ if (isPackageExcluded(pkg.name, nativeExclusions)) {
319
+ continue;
320
+ }
321
+ const vulns = vulnMap.get(pkg.name) ?? [];
322
+ const report = socketReports.get(`${pkg.name}@${pkg.version}`);
323
+ const accepted = findAcceptedRisk(pkg.name, pkg.version, acceptedRisks);
324
+ const hasVulns = vulns.length > 0;
325
+ const hasLowScore = report ? report.score.overall < DEFAULT_LOW_SCORE_THRESHOLD : false;
326
+ const hasAlerts = report ? report.alerts.length > 0 : false;
327
+ if (hasVulns || hasLowScore || hasAlerts) {
328
+ entries.push({
329
+ acceptedRisk: accepted,
330
+ name: pkg.name,
331
+ socketReport: report,
332
+ version: pkg.version,
333
+ vulnerabilities: vulns
334
+ });
335
+ }
336
+ }
337
+ const filtered = entries.filter((entry) => {
338
+ const vulnPasses = entry.vulnerabilities.some((v) => severityPassesFilter(v.severity, severityFilter));
339
+ const socketPasses = entry.socketReport?.alerts.some(
340
+ (a) => severityPassesFilter(a.severity === "medium" ? "MODERATE" : a.severity.toUpperCase(), severityFilter)
341
+ );
342
+ const lowScorePasses = entry.socketReport && entry.socketReport.score.overall < DEFAULT_LOW_SCORE_THRESHOLD;
343
+ return vulnPasses || socketPasses || lowScorePasses;
344
+ });
345
+ if (isJson) {
346
+ const jsonResult = {
347
+ duplicates: duplicates.map((d) => {
348
+ return {
349
+ name: d.name,
350
+ versionCount: d.versions.length,
351
+ versions: d.versions
352
+ };
353
+ }),
354
+ packages: installed.length,
355
+ results: filtered.map((e) => {
356
+ return {
357
+ acceptedRisk: e.acceptedRisk ?? null,
358
+ name: e.name,
359
+ socketAlerts: e.socketReport?.alerts ?? [],
360
+ socketScore: e.socketReport?.score.overall ?? null,
361
+ version: e.version,
362
+ vulnerabilities: e.vulnerabilities
363
+ };
364
+ }),
365
+ summary: {
366
+ accepted: filtered.filter((e) => e.acceptedRisk).length,
367
+ duplicatePackages: duplicates.length,
368
+ issues: filtered.filter((e) => !e.acceptedRisk).length,
369
+ total: filtered.length
370
+ }
371
+ };
372
+ process.stdout.write(`${JSON.stringify(jsonResult, void 0, 2)}
373
+ `);
374
+ if (options.exitCode && jsonResult.summary.issues > 0) {
375
+ process.exitCode = 1;
376
+ }
377
+ return;
378
+ }
379
+ if (filtered.length === 0) {
380
+ pail.success(`No security issues found across ${String(installed.length)} packages.`);
381
+ return;
382
+ }
383
+ const vulnsBySeverity = {
384
+ CRITICAL: [],
385
+ HIGH: [],
386
+ LOW: [],
387
+ MODERATE: []
388
+ };
389
+ for (const entry of filtered) {
390
+ for (const vuln of entry.vulnerabilities) {
391
+ if (severityPassesFilter(vuln.severity, severityFilter)) {
392
+ const key = vuln.severity === "UNKNOWN" ? "LOW" : vuln.severity;
393
+ vulnsBySeverity[key]?.push({ entry, vuln });
394
+ }
395
+ }
396
+ }
397
+ let totalVulns = 0;
398
+ let acknowledgedVulns = 0;
399
+ for (const severity of ["CRITICAL", "HIGH", "MODERATE", "LOW"]) {
400
+ const items = vulnsBySeverity[severity];
401
+ if (!items || items.length === 0) {
402
+ continue;
403
+ }
404
+ pail.info(`
405
+ ── ${severity} (${String(items.length)}) ──`);
406
+ for (const { entry, vuln } of items) {
407
+ const isExcluded = Boolean(entry.acceptedRisk) || isAdvisoryExcluded(vuln.id, nativeExclusions, vuln.aliases);
408
+ if (isExcluded) {
409
+ acknowledgedVulns++;
410
+ if (!showAccepted) {
411
+ continue;
412
+ }
413
+ }
414
+ totalVulns++;
415
+ pail.info(formatVulnLine(entry.name, entry.version, vuln, isExcluded));
416
+ if (showFixes && (vuln.fixedVersions ?? []).length > 0) {
417
+ pail.notice(` Fix: update to ${vuln.fixedVersions.at(-1)}`);
418
+ }
419
+ }
420
+ }
421
+ const socketIssues = filtered.filter(
422
+ (e) => e.socketReport && (e.socketReport.score.overall < DEFAULT_LOW_SCORE_THRESHOLD || e.socketReport.alerts.length > 0)
423
+ );
424
+ if (socketIssues.length > 0) {
425
+ pail.info(`
426
+ ── Socket.dev Supply Chain (${String(socketIssues.length)}) ──`);
427
+ for (const entry of socketIssues) {
428
+ if (!entry.socketReport) {
429
+ continue;
430
+ }
431
+ const isExcluded = Boolean(entry.acceptedRisk);
432
+ if (isExcluded && !showAccepted) {
433
+ continue;
434
+ }
435
+ pail.info(formatSocketLine(entry.socketReport, isExcluded));
436
+ for (const alert of entry.socketReport.alerts) {
437
+ const alertColorFunction = SOCKET_ALERT_COLORS[alert.severity] ?? dim;
438
+ pail.info(` ${alertColorFunction(`[${alert.severity.toUpperCase()}]`)} ${alert.type} — ${alert.category}`);
439
+ }
440
+ }
441
+ }
442
+ if (duplicates.length > 0) {
443
+ pail.info(`
444
+ ── Duplicate Dependencies (${String(duplicates.length)}) ──`);
445
+ for (const dup of duplicates) {
446
+ const versionList = dup.versions.join(", ");
447
+ pail.info(` ${dup.name} — ${String(dup.versions.length)} versions: ${yellow(versionList)}`);
448
+ }
449
+ }
450
+ const isEntryExcluded = (e) => Boolean(e.acceptedRisk) || e.vulnerabilities.length > 0 && e.vulnerabilities.every((v) => isAdvisoryExcluded(v.id, nativeExclusions, v.aliases));
451
+ const unacknowledgedCount = filtered.filter((e) => !isEntryExcluded(e)).length;
452
+ pail.info("");
453
+ pail.info(`─ Audit Summary`);
454
+ pail.info(` ${String(installed.length)} packages scanned`);
455
+ if (nativeExclusions.ignoredAdvisories.length > 0) {
456
+ pail.info(
457
+ ` ${String(nativeExclusions.ignoredAdvisories.length)} ${pm.name} audit exclusion${nativeExclusions.ignoredAdvisories.length === 1 ? "" : "s"} applied`
458
+ );
459
+ }
460
+ if (totalVulns > 0) {
461
+ const critCount = vulnsBySeverity.CRITICAL?.filter((i) => !isEntryExcluded(i.entry)).length ?? 0;
462
+ const highCount = vulnsBySeverity.HIGH?.filter((i) => !isEntryExcluded(i.entry)).length ?? 0;
463
+ pail.error(` ${String(totalVulns)} vulnerabilit${totalVulns === 1 ? "y" : "ies"} found`);
464
+ if (critCount > 0) {
465
+ pail.error(` ${String(critCount)} critical`);
466
+ }
467
+ if (highCount > 0) {
468
+ pail.warn(` ${String(highCount)} high`);
469
+ }
470
+ } else {
471
+ pail.success(" No vulnerabilities found");
472
+ }
473
+ if (socketIssues.length > 0) {
474
+ const unacknowledgedSocket = socketIssues.filter((e) => !isEntryExcluded(e)).length;
475
+ pail.warn(` ${String(unacknowledgedSocket)} package${unacknowledgedSocket === 1 ? "" : "s"} with Socket.dev supply chain issues`);
476
+ }
477
+ if (duplicates.length > 0) {
478
+ pail.warn(` ${String(duplicates.length)} package${duplicates.length === 1 ? "" : "s"} with duplicate versions`);
479
+ pail.notice(" Run 'vis dedupe' or your package manager's dedupe command to reduce duplicates.");
480
+ }
481
+ if (acknowledgedVulns > 0) {
482
+ pail.info(` ${String(acknowledgedVulns)} acknowledged (accepted risks)`);
483
+ if (!showAccepted) {
484
+ pail.notice(" Use --show-accepted to see acknowledged issues.");
485
+ }
486
+ }
487
+ if (unacknowledgedCount === 0) {
488
+ pail.success("\n All issues are acknowledged. No action required.");
489
+ }
490
+ if (options.sync && acceptedRisks) {
491
+ const idSet = /* @__PURE__ */ new Set();
492
+ for (const entry of entries) {
493
+ if (entry.acceptedRisk) {
494
+ for (const vuln of entry.vulnerabilities) {
495
+ if (vuln.id.startsWith("CVE-") || vuln.id.startsWith("GHSA-")) {
496
+ idSet.add(vuln.id);
497
+ }
498
+ if (vuln.aliases) {
499
+ for (const alias of vuln.aliases) {
500
+ if (alias.startsWith("CVE-") || alias.startsWith("GHSA-")) {
501
+ idSet.add(alias);
502
+ }
503
+ }
504
+ }
505
+ }
506
+ }
507
+ }
508
+ const advisoryIds = [...idSet];
509
+ if (advisoryIds.length > 0) {
510
+ pail.info("");
511
+ const actions = syncAcceptedRisksToNativeConfig(pm.name, workspaceRoot, advisoryIds);
512
+ for (const action of actions) {
513
+ pail.success(` ${action}`);
514
+ }
515
+ } else {
516
+ pail.info("\nNo advisory IDs to sync to native PM config.");
517
+ }
518
+ }
519
+ if (options.exitCode && unacknowledgedCount > 0) {
520
+ process.exitCode = 1;
521
+ }
522
+ };
523
+ const execute = async ({ logger, options, visConfig, workspaceRoot: wsRoot }) => {
524
+ if (!wsRoot) {
525
+ throw new Error("Could not determine workspace root. Run this command inside a monorepo.");
526
+ }
527
+ await executeAudit(wsRoot, options, visConfig);
528
+ };
529
+
530
+ export { execute as default };