@vyuhlabs/dxkit 2.5.1 → 2.6.0

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 (200) hide show
  1. package/CHANGELOG.md +318 -0
  2. package/README.md +150 -28
  3. package/dist/allowlist/categories.d.ts +120 -0
  4. package/dist/allowlist/categories.d.ts.map +1 -0
  5. package/dist/allowlist/categories.js +194 -0
  6. package/dist/allowlist/categories.js.map +1 -0
  7. package/dist/allowlist/cli.d.ts +95 -0
  8. package/dist/allowlist/cli.d.ts.map +1 -0
  9. package/dist/allowlist/cli.js +454 -0
  10. package/dist/allowlist/cli.js.map +1 -0
  11. package/dist/allowlist/diff.d.ts +67 -0
  12. package/dist/allowlist/diff.d.ts.map +1 -0
  13. package/dist/allowlist/diff.js +147 -0
  14. package/dist/allowlist/diff.js.map +1 -0
  15. package/dist/allowlist/file.d.ts +249 -0
  16. package/dist/allowlist/file.d.ts.map +1 -0
  17. package/dist/allowlist/file.js +497 -0
  18. package/dist/allowlist/file.js.map +1 -0
  19. package/dist/allowlist/gather.d.ts +61 -0
  20. package/dist/allowlist/gather.d.ts.map +1 -0
  21. package/dist/allowlist/gather.js +143 -0
  22. package/dist/allowlist/gather.js.map +1 -0
  23. package/dist/allowlist/hint.d.ts +80 -0
  24. package/dist/allowlist/hint.d.ts.map +1 -0
  25. package/dist/allowlist/hint.js +271 -0
  26. package/dist/allowlist/hint.js.map +1 -0
  27. package/dist/allowlist/inline.d.ts +149 -0
  28. package/dist/allowlist/inline.d.ts.map +1 -0
  29. package/dist/allowlist/inline.js +306 -0
  30. package/dist/allowlist/inline.js.map +1 -0
  31. package/dist/analyzers/tools/tool-registry.d.ts.map +1 -1
  32. package/dist/analyzers/tools/tool-registry.js +25 -8
  33. package/dist/analyzers/tools/tool-registry.js.map +1 -1
  34. package/dist/baseline/baseline-file.d.ts +7 -0
  35. package/dist/baseline/baseline-file.d.ts.map +1 -1
  36. package/dist/baseline/baseline-file.js +22 -1
  37. package/dist/baseline/baseline-file.js.map +1 -1
  38. package/dist/baseline/check-renderers.d.ts +13 -1
  39. package/dist/baseline/check-renderers.d.ts.map +1 -1
  40. package/dist/baseline/check-renderers.js +67 -1
  41. package/dist/baseline/check-renderers.js.map +1 -1
  42. package/dist/baseline/check.d.ts +33 -7
  43. package/dist/baseline/check.d.ts.map +1 -1
  44. package/dist/baseline/check.js +90 -64
  45. package/dist/baseline/check.js.map +1 -1
  46. package/dist/baseline/create.d.ts +35 -7
  47. package/dist/baseline/create.d.ts.map +1 -1
  48. package/dist/baseline/create.js +43 -5
  49. package/dist/baseline/create.js.map +1 -1
  50. package/dist/baseline/entry-to-located.d.ts +6 -1
  51. package/dist/baseline/entry-to-located.d.ts.map +1 -1
  52. package/dist/baseline/entry-to-located.js +20 -2
  53. package/dist/baseline/entry-to-located.js.map +1 -1
  54. package/dist/baseline/finding-identity.d.ts.map +1 -1
  55. package/dist/baseline/finding-identity.js +15 -13
  56. package/dist/baseline/finding-identity.js.map +1 -1
  57. package/dist/baseline/modes.d.ts +140 -0
  58. package/dist/baseline/modes.d.ts.map +1 -0
  59. package/dist/baseline/modes.js +179 -0
  60. package/dist/baseline/modes.js.map +1 -0
  61. package/dist/baseline/policy.d.ts +64 -0
  62. package/dist/baseline/policy.d.ts.map +1 -1
  63. package/dist/baseline/policy.js +102 -1
  64. package/dist/baseline/policy.js.map +1 -1
  65. package/dist/baseline/producers/health.d.ts +2 -2
  66. package/dist/baseline/producers/health.d.ts.map +1 -1
  67. package/dist/baseline/producers/health.js.map +1 -1
  68. package/dist/baseline/producers/index.d.ts +11 -5
  69. package/dist/baseline/producers/index.d.ts.map +1 -1
  70. package/dist/baseline/producers/index.js +12 -9
  71. package/dist/baseline/producers/index.js.map +1 -1
  72. package/dist/baseline/producers/quality.d.ts +3 -3
  73. package/dist/baseline/producers/quality.d.ts.map +1 -1
  74. package/dist/baseline/producers/quality.js.map +1 -1
  75. package/dist/baseline/producers/secret-hmac.d.ts +2 -2
  76. package/dist/baseline/producers/secret-hmac.d.ts.map +1 -1
  77. package/dist/baseline/producers/secret-hmac.js.map +1 -1
  78. package/dist/baseline/producers/security.d.ts +2 -2
  79. package/dist/baseline/producers/security.d.ts.map +1 -1
  80. package/dist/baseline/producers/security.js.map +1 -1
  81. package/dist/baseline/producers/stale-allow.d.ts +70 -0
  82. package/dist/baseline/producers/stale-allow.d.ts.map +1 -0
  83. package/dist/baseline/producers/stale-allow.js +111 -0
  84. package/dist/baseline/producers/stale-allow.js.map +1 -0
  85. package/dist/baseline/producers/tests.d.ts +2 -2
  86. package/dist/baseline/producers/tests.d.ts.map +1 -1
  87. package/dist/baseline/producers/tests.js.map +1 -1
  88. package/dist/baseline/ref-baseline.d.ts +114 -0
  89. package/dist/baseline/ref-baseline.d.ts.map +1 -0
  90. package/dist/baseline/ref-baseline.js +260 -0
  91. package/dist/baseline/ref-baseline.js.map +1 -0
  92. package/dist/baseline/sanitize.d.ts +80 -0
  93. package/dist/baseline/sanitize.d.ts.map +1 -0
  94. package/dist/baseline/sanitize.js +91 -0
  95. package/dist/baseline/sanitize.js.map +1 -0
  96. package/dist/baseline/show.d.ts.map +1 -1
  97. package/dist/baseline/show.js +9 -3
  98. package/dist/baseline/show.js.map +1 -1
  99. package/dist/baseline/types.d.ts +73 -26
  100. package/dist/baseline/types.d.ts.map +1 -1
  101. package/dist/baseline/types.js +7 -1
  102. package/dist/baseline/types.js.map +1 -1
  103. package/dist/baseline/visibility.d.ts +61 -0
  104. package/dist/baseline/visibility.d.ts.map +1 -0
  105. package/dist/baseline/visibility.js +121 -0
  106. package/dist/baseline/visibility.js.map +1 -0
  107. package/dist/cli.d.ts.map +1 -1
  108. package/dist/cli.js +154 -13
  109. package/dist/cli.js.map +1 -1
  110. package/dist/constants.d.ts.map +1 -1
  111. package/dist/constants.js +0 -10
  112. package/dist/constants.js.map +1 -1
  113. package/dist/detect.d.ts.map +1 -1
  114. package/dist/detect.js +0 -15
  115. package/dist/detect.js.map +1 -1
  116. package/dist/doctor.d.ts +78 -1
  117. package/dist/doctor.d.ts.map +1 -1
  118. package/dist/doctor.js +590 -101
  119. package/dist/doctor.js.map +1 -1
  120. package/dist/generator.d.ts.map +1 -1
  121. package/dist/generator.js +15 -0
  122. package/dist/generator.js.map +1 -1
  123. package/dist/issue-cli.d.ts +62 -0
  124. package/dist/issue-cli.d.ts.map +1 -0
  125. package/dist/issue-cli.js +252 -0
  126. package/dist/issue-cli.js.map +1 -0
  127. package/dist/languages/csharp.d.ts.map +1 -1
  128. package/dist/languages/csharp.js +2 -0
  129. package/dist/languages/csharp.js.map +1 -1
  130. package/dist/languages/go.d.ts.map +1 -1
  131. package/dist/languages/go.js +2 -0
  132. package/dist/languages/go.js.map +1 -1
  133. package/dist/languages/index.d.ts +25 -0
  134. package/dist/languages/index.d.ts.map +1 -1
  135. package/dist/languages/index.js +44 -0
  136. package/dist/languages/index.js.map +1 -1
  137. package/dist/languages/java.d.ts.map +1 -1
  138. package/dist/languages/java.js +2 -0
  139. package/dist/languages/java.js.map +1 -1
  140. package/dist/languages/kotlin.d.ts.map +1 -1
  141. package/dist/languages/kotlin.js +2 -0
  142. package/dist/languages/kotlin.js.map +1 -1
  143. package/dist/languages/python.d.ts.map +1 -1
  144. package/dist/languages/python.js +11 -1
  145. package/dist/languages/python.js.map +1 -1
  146. package/dist/languages/ruby.d.ts.map +1 -1
  147. package/dist/languages/ruby.js +2 -0
  148. package/dist/languages/ruby.js.map +1 -1
  149. package/dist/languages/rust.d.ts.map +1 -1
  150. package/dist/languages/rust.js +2 -0
  151. package/dist/languages/rust.js.map +1 -1
  152. package/dist/languages/types.d.ts +45 -0
  153. package/dist/languages/types.d.ts.map +1 -1
  154. package/dist/languages/typescript.d.ts.map +1 -1
  155. package/dist/languages/typescript.js +2 -0
  156. package/dist/languages/typescript.js.map +1 -1
  157. package/dist/prompts.d.ts.map +1 -1
  158. package/dist/prompts.js +0 -5
  159. package/dist/prompts.js.map +1 -1
  160. package/dist/setup-branch-protection.d.ts +34 -0
  161. package/dist/setup-branch-protection.d.ts.map +1 -0
  162. package/dist/setup-branch-protection.js +190 -0
  163. package/dist/setup-branch-protection.js.map +1 -0
  164. package/dist/setup-gh.d.ts +75 -0
  165. package/dist/setup-gh.d.ts.map +1 -0
  166. package/dist/setup-gh.js +213 -0
  167. package/dist/setup-gh.js.map +1 -0
  168. package/dist/setup-prebuild.d.ts +34 -0
  169. package/dist/setup-prebuild.d.ts.map +1 -0
  170. package/dist/setup-prebuild.js +181 -0
  171. package/dist/setup-prebuild.js.map +1 -0
  172. package/dist/ship-installers.d.ts.map +1 -1
  173. package/dist/ship-installers.js +19 -4
  174. package/dist/ship-installers.js.map +1 -1
  175. package/dist/types.d.ts +24 -6
  176. package/dist/types.d.ts.map +1 -1
  177. package/dist/update.d.ts +41 -0
  178. package/dist/update.d.ts.map +1 -1
  179. package/dist/update.js +154 -15
  180. package/dist/update.js.map +1 -1
  181. package/dist/upgrade.d.ts +88 -0
  182. package/dist/upgrade.d.ts.map +1 -0
  183. package/dist/upgrade.js +324 -0
  184. package/dist/upgrade.js.map +1 -0
  185. package/package.json +1 -1
  186. package/templates/.claude/skills/dxkit-action/SKILL.md +111 -17
  187. package/templates/.claude/skills/dxkit-config/SKILL.md +7 -7
  188. package/templates/.claude/skills/dxkit-fix/SKILL.md +165 -0
  189. package/templates/.claude/skills/dxkit-hooks/SKILL.md +8 -8
  190. package/templates/.claude/skills/dxkit-init/SKILL.md +3 -3
  191. package/templates/.claude/skills/dxkit-learn/SKILL.md +9 -9
  192. package/templates/.claude/skills/dxkit-onboard/SKILL.md +274 -0
  193. package/templates/.claude/skills/dxkit-reports/SKILL.md +18 -18
  194. package/templates/.claude/skills/dxkit-update/SKILL.md +164 -0
  195. package/templates/.devcontainer/devcontainer.json +6 -15
  196. package/templates/.devcontainer/post-create.sh +19 -4
  197. package/dist/baseline/producers/licenses.d.ts +0 -23
  198. package/dist/baseline/producers/licenses.d.ts.map +0 -1
  199. package/dist/baseline/producers/licenses.js +0 -46
  200. package/dist/baseline/producers/licenses.js.map +0 -1
@@ -0,0 +1,88 @@
1
+ /**
2
+ * `vyuh-dxkit upgrade` — combined CLI for the dxkit upgrade flow.
3
+ *
4
+ * Two modes, one subcommand:
5
+ *
6
+ * `--plan [--json]` — preview only. Emits UpgradePlan JSON
7
+ * (consumed by dxkit-update skill) or text-prose summary. No
8
+ * mutations. Used to inspect what an upgrade would do before
9
+ * committing.
10
+ *
11
+ * (no flag, or `--yes`) — execute. Runs the three-step upgrade:
12
+ * 1. `npm install @vyuhlabs/dxkit@<target>` (binary)
13
+ * 2. `npx vyuh-dxkit update` (scaffold refresh)
14
+ * 3. `npx vyuh-dxkit doctor` (verify)
15
+ * Then prints devcontainer-rebuild instructions if .devcontainer/
16
+ * was refreshed.
17
+ *
18
+ * Architectural mirror of the doctor → dxkit-fix pattern: structured
19
+ * CLI output (--plan --json) for skill consumption, execution mode
20
+ * for direct human use. Same shape, different content.
21
+ */
22
+ export type DeltaKind = 'none' | 'patch' | 'minor' | 'major' | 'downgrade';
23
+ export interface UpgradeStep {
24
+ /** Shell command to run (verbatim — what gets executed). */
25
+ command: string;
26
+ /** One-line purpose shown in plan + before each execution. */
27
+ purpose: string;
28
+ /** Optional steps the customer can decline (e.g. devcontainer rebuild). */
29
+ optional?: boolean;
30
+ }
31
+ export interface UpgradePlan {
32
+ schema: 'upgrade-plan.v1';
33
+ generatedAt: string;
34
+ cwd: string;
35
+ current: {
36
+ /** Installed binary version (from `npx vyuh-dxkit --version`). */
37
+ binary: string | null;
38
+ /** Scaffold version recorded in manifest. */
39
+ scaffold: string | null;
40
+ };
41
+ /** Target version — `--target=X.Y.Z` or 'latest' from npm. */
42
+ target: string;
43
+ delta: DeltaKind;
44
+ /**
45
+ * Recommended execution sequence. Each step is independently
46
+ * idempotent — re-running is safe.
47
+ */
48
+ steps: UpgradeStep[];
49
+ /**
50
+ * Customer-facing warnings (peer-dep risks, breaking changes,
51
+ * "devcontainer will need rebuild after"). Empty when nothing
52
+ * actionable.
53
+ */
54
+ warnings: string[];
55
+ /**
56
+ * Brief note pointing the customer at the canonical changelog.
57
+ * Detailed per-version highlight parsing is a future enhancement —
58
+ * for now we keep this simple and link to the source of truth.
59
+ */
60
+ changelogNote: string;
61
+ }
62
+ export interface UpgradeOpts {
63
+ /** Pin to a specific version. Default: latest from npm. */
64
+ target?: string;
65
+ /** Skip interactive confirmations. */
66
+ yes?: boolean;
67
+ /** Print commands without executing. */
68
+ dryRun?: boolean;
69
+ /** Emit plan only; don't execute. */
70
+ planOnly?: boolean;
71
+ /** Emit plan as JSON instead of prose (only meaningful with planOnly). */
72
+ json?: boolean;
73
+ /**
74
+ * Injectable version reads. Useful for tests that need deterministic
75
+ * plans without subprocess fanout; production code leaves these
76
+ * undefined and the readers shell out as needed.
77
+ */
78
+ _readBinary?: (cwd: string) => string | null;
79
+ _readLatest?: () => string;
80
+ }
81
+ /**
82
+ * Classify the delta between two semver-shaped strings. Returns
83
+ * `'none'` if equal, `'downgrade'` if target < current.
84
+ */
85
+ export declare function classifyDelta(current: string | null, target: string): DeltaKind;
86
+ export declare function buildUpgradePlan(cwd: string, opts?: UpgradeOpts): UpgradePlan;
87
+ export declare function runUpgrade(cwd: string, opts?: UpgradeOpts): Promise<void>;
88
+ //# sourceMappingURL=upgrade.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../src/upgrade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAQH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,WAAW,CAAC;AAE3E,MAAM,WAAW,WAAW;IAC1B,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE;QACP,kEAAkE;QAClE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,6CAA6C;QAC7C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;KACzB,CAAC;IACF,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,SAAS,CAAC;IACjB;;;OAGG;IACH,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB;;;;OAIG;IACH,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,wCAAwC;IACxC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0EAA0E;IAC1E,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC7C,WAAW,CAAC,EAAE,MAAM,MAAM,CAAC;CAC5B;AA4CD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,CAY/E;AAMD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,WAAW,CA6FjF;AAyHD,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAcnF"}
@@ -0,0 +1,324 @@
1
+ "use strict";
2
+ /**
3
+ * `vyuh-dxkit upgrade` — combined CLI for the dxkit upgrade flow.
4
+ *
5
+ * Two modes, one subcommand:
6
+ *
7
+ * `--plan [--json]` — preview only. Emits UpgradePlan JSON
8
+ * (consumed by dxkit-update skill) or text-prose summary. No
9
+ * mutations. Used to inspect what an upgrade would do before
10
+ * committing.
11
+ *
12
+ * (no flag, or `--yes`) — execute. Runs the three-step upgrade:
13
+ * 1. `npm install @vyuhlabs/dxkit@<target>` (binary)
14
+ * 2. `npx vyuh-dxkit update` (scaffold refresh)
15
+ * 3. `npx vyuh-dxkit doctor` (verify)
16
+ * Then prints devcontainer-rebuild instructions if .devcontainer/
17
+ * was refreshed.
18
+ *
19
+ * Architectural mirror of the doctor → dxkit-fix pattern: structured
20
+ * CLI output (--plan --json) for skill consumption, execution mode
21
+ * for direct human use. Same shape, different content.
22
+ */
23
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ var desc = Object.getOwnPropertyDescriptor(m, k);
26
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
27
+ desc = { enumerable: true, get: function() { return m[k]; } };
28
+ }
29
+ Object.defineProperty(o, k2, desc);
30
+ }) : (function(o, m, k, k2) {
31
+ if (k2 === undefined) k2 = k;
32
+ o[k2] = m[k];
33
+ }));
34
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
35
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
36
+ }) : function(o, v) {
37
+ o["default"] = v;
38
+ });
39
+ var __importStar = (this && this.__importStar) || (function () {
40
+ var ownKeys = function(o) {
41
+ ownKeys = Object.getOwnPropertyNames || function (o) {
42
+ var ar = [];
43
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
44
+ return ar;
45
+ };
46
+ return ownKeys(o);
47
+ };
48
+ return function (mod) {
49
+ if (mod && mod.__esModule) return mod;
50
+ var result = {};
51
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
52
+ __setModuleDefault(result, mod);
53
+ return result;
54
+ };
55
+ })();
56
+ Object.defineProperty(exports, "__esModule", { value: true });
57
+ exports.classifyDelta = classifyDelta;
58
+ exports.buildUpgradePlan = buildUpgradePlan;
59
+ exports.runUpgrade = runUpgrade;
60
+ const fs = __importStar(require("fs"));
61
+ const path = __importStar(require("path"));
62
+ const child_process_1 = require("child_process");
63
+ const logger = __importStar(require("./logger"));
64
+ // ────────────────────────────────────────────────────────────────────
65
+ // Version helpers
66
+ // ────────────────────────────────────────────────────────────────────
67
+ function readScaffoldVersion(cwd) {
68
+ const manifestPath = path.join(cwd, '.vyuh-dxkit.json');
69
+ if (!fs.existsSync(manifestPath))
70
+ return null;
71
+ try {
72
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
73
+ return manifest.version ?? null;
74
+ }
75
+ catch {
76
+ return null;
77
+ }
78
+ }
79
+ function readBinaryVersion(cwd) {
80
+ try {
81
+ const out = (0, child_process_1.execSync)('npx --no-install vyuh-dxkit --version 2>/dev/null', {
82
+ cwd,
83
+ stdio: ['ignore', 'pipe', 'ignore'],
84
+ encoding: 'utf-8',
85
+ });
86
+ return out.trim() || null;
87
+ }
88
+ catch {
89
+ return null;
90
+ }
91
+ }
92
+ function readLatestPublished() {
93
+ try {
94
+ const out = (0, child_process_1.execSync)('npm view @vyuhlabs/dxkit version 2>/dev/null', {
95
+ stdio: ['ignore', 'pipe', 'ignore'],
96
+ encoding: 'utf-8',
97
+ timeout: 15000,
98
+ });
99
+ return out.trim();
100
+ }
101
+ catch {
102
+ // npm registry unreachable — return empty so caller can decide
103
+ return '';
104
+ }
105
+ }
106
+ /**
107
+ * Classify the delta between two semver-shaped strings. Returns
108
+ * `'none'` if equal, `'downgrade'` if target < current.
109
+ */
110
+ function classifyDelta(current, target) {
111
+ if (!current || !target)
112
+ return 'none';
113
+ const [c1, c2, c3] = current.split('.').map((n) => parseInt(n, 10));
114
+ const [t1, t2, t3] = target.split('.').map((n) => parseInt(n, 10));
115
+ if (Number.isNaN(c1) || Number.isNaN(t1))
116
+ return 'none';
117
+ if (t1 > c1)
118
+ return 'major';
119
+ if (t1 < c1)
120
+ return 'downgrade';
121
+ if (t2 > c2)
122
+ return 'minor';
123
+ if (t2 < c2)
124
+ return 'downgrade';
125
+ if (t3 > c3)
126
+ return 'patch';
127
+ if (t3 < c3)
128
+ return 'downgrade';
129
+ return 'none';
130
+ }
131
+ // ────────────────────────────────────────────────────────────────────
132
+ // Plan construction
133
+ // ────────────────────────────────────────────────────────────────────
134
+ function buildUpgradePlan(cwd, opts = {}) {
135
+ const scaffold = readScaffoldVersion(cwd);
136
+ const binary = (opts._readBinary ?? readBinaryVersion)(cwd);
137
+ const latest = opts.target ?? (opts._readLatest ?? readLatestPublished)();
138
+ // Use binary version as the "current" anchor for delta classification —
139
+ // it's what npm install will replace. Scaffold version informs whether
140
+ // vyuh-dxkit update is needed even when binary is already up to date.
141
+ const delta = classifyDelta(binary, latest);
142
+ const steps = [];
143
+ const warnings = [];
144
+ if (!latest) {
145
+ warnings.push('Could not query npm for the latest version (registry unreachable or rate-limited). ' +
146
+ 'Pass --target=X.Y.Z to upgrade to a specific version.');
147
+ }
148
+ // Step 1: binary upgrade. Always included unless delta is none AND
149
+ // scaffold already matches.
150
+ if (delta !== 'none' || (scaffold && binary && scaffold !== binary)) {
151
+ if (latest) {
152
+ steps.push({
153
+ command: `npm install @vyuhlabs/dxkit@${latest}`,
154
+ purpose: `Install dxkit binary ${binary ?? '(missing)'} → ${latest}`,
155
+ });
156
+ }
157
+ }
158
+ // Step 2: scaffold refresh. Always run when scaffold ≠ binary OR after
159
+ // a binary upgrade (scaffold may need updating to match the new binary).
160
+ if (latest) {
161
+ steps.push({
162
+ command: 'npx vyuh-dxkit update',
163
+ purpose: 'Refresh scaffold (.devcontainer, .githooks, .claude/skills, CI workflows)',
164
+ });
165
+ }
166
+ // Step 3: verify with doctor.
167
+ if (latest) {
168
+ steps.push({
169
+ command: 'npx vyuh-dxkit doctor',
170
+ purpose: 'Verify operational health post-upgrade',
171
+ });
172
+ }
173
+ // Optional: devcontainer rebuild reminder. We mark this optional so
174
+ // dxkit-update can surface it as a "you also need to do this manually"
175
+ // step rather than something the CLI can execute.
176
+ const hasDevcontainer = fs.existsSync(path.join(cwd, '.devcontainer', 'devcontainer.json'));
177
+ if (hasDevcontainer && delta !== 'none') {
178
+ steps.push({
179
+ command: '# Rebuild devcontainer: VSCode Command Palette → "Dev Containers: Rebuild Container"',
180
+ purpose: 'Pick up devcontainer.json changes (if any) — manual step',
181
+ optional: true,
182
+ });
183
+ }
184
+ // Warnings.
185
+ if (delta === 'major') {
186
+ warnings.push(`Major version jump (${binary} → ${latest}). Read CHANGELOG.md for breaking changes ` +
187
+ 'before running the upgrade.');
188
+ }
189
+ if (delta === 'downgrade') {
190
+ warnings.push(`Target version ${latest} is OLDER than installed ${binary}. ` +
191
+ 'Downgrades are not officially supported; baseline + manifest schemas may differ.');
192
+ }
193
+ if (scaffold && binary && scaffold !== binary) {
194
+ warnings.push(`Scaffold version (${scaffold}) doesn't match binary (${binary}). ` +
195
+ 'Step 2 (vyuh-dxkit update) will reconcile.');
196
+ }
197
+ return {
198
+ schema: 'upgrade-plan.v1',
199
+ generatedAt: new Date().toISOString(),
200
+ cwd,
201
+ current: { binary, scaffold },
202
+ target: latest,
203
+ delta,
204
+ steps,
205
+ warnings,
206
+ changelogNote: latest
207
+ ? `For per-version details: https://github.com/vyuh-labs/dxkit/blob/main/CHANGELOG.md`
208
+ : '',
209
+ };
210
+ }
211
+ // ────────────────────────────────────────────────────────────────────
212
+ // Renderers
213
+ // ────────────────────────────────────────────────────────────────────
214
+ function renderPlanProse(plan) {
215
+ logger.header('vyuh-dxkit upgrade --plan');
216
+ logger.info(`Current: scaffold ${plan.current.scaffold ?? '(none)'} + binary ${plan.current.binary ?? '(none)'}`);
217
+ logger.info(`Target: ${plan.target || '(latest unavailable)'}`);
218
+ logger.info(`Delta: ${plan.delta}`);
219
+ if (plan.warnings.length) {
220
+ console.log(''); // slop-ok
221
+ for (const w of plan.warnings)
222
+ logger.warn(w);
223
+ }
224
+ if (plan.steps.length) {
225
+ console.log(''); // slop-ok
226
+ logger.info('Plan:');
227
+ plan.steps.forEach((s, i) => {
228
+ const marker = s.optional ? '○' : '●';
229
+ logger.dim(` ${marker} [${i + 1}/${plan.steps.length}] ${s.purpose}`);
230
+ logger.dim(` ${s.command}`);
231
+ });
232
+ }
233
+ if (plan.changelogNote) {
234
+ console.log(''); // slop-ok
235
+ logger.dim(plan.changelogNote);
236
+ }
237
+ }
238
+ // ────────────────────────────────────────────────────────────────────
239
+ // Execution
240
+ // ────────────────────────────────────────────────────────────────────
241
+ function runStep(step, cwd, dryRun) {
242
+ if (step.optional) {
243
+ // Optional steps are never auto-executed — surfaced for the
244
+ // customer to do manually.
245
+ return true;
246
+ }
247
+ logger.info(`→ ${step.purpose}`);
248
+ logger.dim(` ${step.command}`);
249
+ if (dryRun) {
250
+ logger.dim(' (dry-run; skipping)');
251
+ return true;
252
+ }
253
+ // Shell out to a real shell so npx/npm work the same way as the
254
+ // customer's terminal would. We DON'T use spawnSync's shell:true
255
+ // bash escaping concerns because the commands here are all dxkit-
256
+ // controlled — no customer input flows into them.
257
+ const result = (0, child_process_1.spawnSync)('bash', ['-c', step.command], {
258
+ cwd,
259
+ stdio: 'inherit',
260
+ });
261
+ return result.status === 0;
262
+ }
263
+ async function runUpgradeExecution(cwd, plan, opts) {
264
+ // Print the plan so the customer sees what's about to happen.
265
+ renderPlanProse(plan);
266
+ if (plan.steps.length === 0) {
267
+ console.log(''); // slop-ok
268
+ logger.success('Already up to date — nothing to do.');
269
+ return;
270
+ }
271
+ if (!opts.yes && !opts.dryRun) {
272
+ // We don't bundle a real prompt library here — the upgrade is
273
+ // intended either non-interactive (--yes) or driven by the
274
+ // dxkit-update skill (which handles confirmation). Surface the
275
+ // hint so a direct human invocation knows to add --yes.
276
+ console.log(''); // slop-ok
277
+ logger.warn('Interactive confirmation is not implemented for this command. ' +
278
+ 'Re-run with --yes to execute, --dry-run to print without executing, ' +
279
+ 'or use the dxkit-update skill for a guided upgrade.');
280
+ return;
281
+ }
282
+ console.log(''); // slop-ok
283
+ logger.header('Executing upgrade');
284
+ const optionalAfter = [];
285
+ for (const step of plan.steps) {
286
+ if (step.optional) {
287
+ optionalAfter.push(step);
288
+ continue;
289
+ }
290
+ const ok = runStep(step, cwd, !!opts.dryRun);
291
+ if (!ok) {
292
+ logger.fail(`Step failed: ${step.purpose}`);
293
+ logger.dim(' Upgrade aborted. Re-run `vyuh-dxkit doctor` to see current state.');
294
+ process.exitCode = 1;
295
+ return;
296
+ }
297
+ }
298
+ console.log(''); // slop-ok
299
+ logger.success(`Upgraded to ${plan.target}.`);
300
+ if (optionalAfter.length) {
301
+ console.log(''); // slop-ok
302
+ logger.info('Manual steps still required:');
303
+ for (const s of optionalAfter)
304
+ logger.dim(` • ${s.purpose}\n ${s.command}`);
305
+ }
306
+ }
307
+ // ────────────────────────────────────────────────────────────────────
308
+ // Entry point
309
+ // ────────────────────────────────────────────────────────────────────
310
+ async function runUpgrade(cwd, opts = {}) {
311
+ const plan = buildUpgradePlan(cwd, opts);
312
+ if (opts.planOnly) {
313
+ if (opts.json) {
314
+ // Logger already routes to stderr in --json mode (cli.ts sets it).
315
+ console.log(JSON.stringify(plan, null, 2)); // slop-ok
316
+ }
317
+ else {
318
+ renderPlanProse(plan);
319
+ }
320
+ return;
321
+ }
322
+ await runUpgradeExecution(cwd, plan, opts);
323
+ }
324
+ //# sourceMappingURL=upgrade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../src/upgrade.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vyuhlabs/dxkit",
3
- "version": "2.5.1",
3
+ "version": "2.6.0",
4
4
  "description": "AI-native developer experience toolkit for any codebase",
5
5
  "license": "MIT",
6
6
  "author": "Vyuh Labs",
@@ -42,20 +42,22 @@ Skip items where reachability is "no" (graphify can't find a call path) UNLESS t
42
42
  # 2. Remove the secret from the file
43
43
  # 3. If the secret was committed: `git filter-repo` or BFG to scrub history
44
44
  # 4. Re-scan to confirm gitleaks no longer reports it
45
- vyuh-dxkit vulnerabilities --json | jq '.summary.findings'
45
+ npx vyuh-dxkit vulnerabilities --json | jq '.summary.findings'
46
46
  ```
47
47
 
48
48
  Don't try to redact the secret in place — the git history still has it. Rotation is the only true fix.
49
49
 
50
+ If the "secret" is actually a placeholder in test code (e.g., `"sk_test_xxxxxxxxxxxx"` with no real credential value), confirm with the developer and allowlist via `dxkit-allow:test-fixture` — see "Allowlisting (when fix is not viable)" below.
51
+
50
52
  ### SAST finding (semgrep)
51
53
 
52
54
  ```bash
53
55
  # 1. Read the finding's rule + line range from the report
54
56
  # 2. Open the file, understand why semgrep flagged it
55
- # 3. Either FIX (preferred) or SUPPRESS (carefully)
57
+ # 3. Either FIX (preferred) or ALLOWLIST (carefully — see below)
56
58
  ```
57
59
 
58
- Suppression is `// nosemgrep: <rule-id>` on the offending line. Use sparingly every suppression is a future maintenance burden. Better: fix the underlying issue.
60
+ If the finding is a true false positive or intentional pattern (test fixture, mitigated externally), suppress via dxkit's allowlist — NOT via semgrep's `// nosemgrep:`. The dxkit allowlist is the canonical surface (single source of truth across every scanner), carries a typed category + reason, and is audit-trackable through `vyuh-dxkit allowlist audit`. See "Allowlisting (when fix is not viable)" below.
59
61
 
60
62
  ### Dependency vulnerability
61
63
 
@@ -63,13 +65,15 @@ Suppression is `// nosemgrep: <rule-id>` on the offending line. Use sparingly
63
65
  # Find the patched version (osv-scanner / npm-audit / etc. report it)
64
66
  npm install <pkg>@<patched-version>
65
67
  # Re-run the scan
66
- vyuh-dxkit vulnerabilities
68
+ npx vyuh-dxkit vulnerabilities
67
69
  ```
68
70
 
69
71
  For peer-dep conflicts: `npm install <pkg>@<patched-version> --legacy-peer-deps` (matches the post-create.sh fallback chain).
70
72
 
71
73
  For Python: `pip install --upgrade <pkg>=<patched>` then re-pip-freeze. For Go: `go get <pkg>@<patched>` then `go mod tidy`. For Ruby: edit Gemfile, `bundle update <pkg>`. For Rust: `cargo update -p <pkg> --precise <patched>`.
72
74
 
75
+ If no patched version exists OR the upgrade breaks other constraints AND the risk is mitigated externally (network policy, WAF, runtime guard), allowlist with `category=mitigated-externally` and a reason describing the mitigation. If the team is accepting the risk while waiting on a fix, `category=accepted-risk` + an expiry tied to the fix deadline.
76
+
73
77
  ### Test gap
74
78
 
75
79
  ```bash
@@ -78,7 +82,7 @@ For Python: `pip install --upgrade <pkg>=<patched>` then re-pip-freeze. For Go:
78
82
  # 3. Run the test runner to confirm it passes
79
83
  npm test # or pytest, go test, cargo test, etc.
80
84
  # 4. Re-run test-gaps to confirm the file dropped off the list
81
- vyuh-dxkit test-gaps
85
+ npx vyuh-dxkit test-gaps
82
86
  ```
83
87
 
84
88
  Don't write tests that just import the module — write tests that exercise behavior. Useless tests inflate the count but don't move the dimension.
@@ -93,13 +97,95 @@ Don't write tests that just import the module — write tests that exercise beha
93
97
 
94
98
  If the finding is a false positive, add `// slop-ok: <reason>` on the offending line (or `# slop-ok` for non-JS).
95
99
 
100
+ ## Allowlisting (when fix is not viable)
101
+
102
+ **Fix first.** The allowlist is the SECOND option, not the first. When you reach for it, choose deliberately — every allowlist entry is a future maintenance burden the customer's team will revisit.
103
+
104
+ Five typed categories signal WHY the suppression is in place:
105
+
106
+ | Category | Meaning | Where it lives |
107
+ |---|---|---|
108
+ | `false-positive` | Scanner is wrong about this code | Inline annotation OR file-level |
109
+ | `test-fixture` | Intentional pattern in a fixture / test file | Inline annotation OR file-level |
110
+ | `mitigated-externally` | Real risk but neutralized at runtime (WAF, env, etc.) | Inline annotation OR file-level |
111
+ | `accepted-risk` | Real risk, accepted by the team, signed off | File-level only (needs expiry + acknowledged severity) |
112
+ | `deferred` | Real, will fix later, tracked work | File-level only (needs expiry) |
113
+
114
+ `accepted-risk` and `deferred` require an `expiresAt` date because they describe assertions that should age out — by default the CLI sets 90 days. `false-positive`, `test-fixture`, and `mitigated-externally` describe assertions that don't naturally stale; they may omit expiry.
115
+
116
+ ### The two surfaces
117
+
118
+ **Inline annotation** is the natural fit for source-anchored findings (secrets, code, config, dep-vuln, hygiene) with an inline-compatible category. The annotation lives next to the line it suppresses:
119
+
120
+ ```python
121
+ api_key = "sk_test_xxxx" # dxkit-allow:test-fixture reason="placeholder in unit test"
122
+ ```
123
+
124
+ Or, for long source lines, above:
125
+
126
+ ```typescript
127
+ // dxkit-allow:false-positive reason="regex matches intentional placeholder"
128
+ const apiKey = "sk_test_xxxx";
129
+ ```
130
+
131
+ The grammar is uniform across every language; only the comment marker varies (`#` for python/ruby, `//` for typescript/go/rust/csharp/kotlin/java). Don't type it by hand — let dxkit insert it for you (see CLI below).
132
+
133
+ **File-level allowlist** lives at `.dxkit/allowlist.json` and is the surface for:
134
+
135
+ - Cross-file or whole-file findings (duplication, coverage-gap, test-gap, god-file, large-file, stale-file)
136
+ - Findings with no stable single-line attachment (dep-vuln, secret-hmac)
137
+ - Any `accepted-risk` or `deferred` suppression regardless of kind
138
+
139
+ ### Add an allowlist entry (canonical path)
140
+
141
+ Don't hand-edit the annotation comment or the JSON file — let the CLI insert it correctly:
142
+
143
+ ```bash
144
+ # Inline annotation at file:line — for source-anchored findings
145
+ # with an inline-compatible category
146
+ npx vyuh-dxkit allowlist add src/auth/oauth.ts:42 \
147
+ --category=test-fixture --reason="placeholder in unit test"
148
+
149
+ # File-level entry — for everything else (kind + fingerprint required;
150
+ # both come straight from the guardrail check's output)
151
+ npx vyuh-dxkit allowlist add --fingerprint=a3f9c0e8b7d2e1f4 \
152
+ --kind=dep-vuln --category=accepted-risk \
153
+ --reason="WAF rule X mitigates this CVE" --expires=2026-08-22
154
+ ```
155
+
156
+ The guardrail's block message gives you the exact command to paste for every blocked finding — file path + line for inline-compatible kinds, fingerprint + kind for everything else. Just copy-paste.
157
+
158
+ ### When the finding only carries an id (sanitized / ref-based baseline)
159
+
160
+ If you're seeing a blocked finding labelled `<sanitized>` (in `baseline show` output) OR with no `file`/`line` columns in the PR-comment table, the repo's baseline mode is `committed-sanitized` or `ref-based` and the human-readable locator was stripped at write time. Two options:
161
+
162
+ - **Inspect the finding in the current scan.** The fingerprint pairs against the live scan's output, which is rich (always). Re-run the matching analyzer (`vyuh-dxkit vulnerabilities`, `vyuh-dxkit health`, etc.) and grep the JSON output for the fingerprint — that finding has full file:line context.
163
+ - **Allowlist by fingerprint anyway.** File-level allowlist entries only need fingerprint + kind, both of which the guardrail message still provides. The category + reason apply regardless of whether the locator is visible at baseline time.
164
+
165
+ The fingerprint contract is preserved across all three modes — `committed-full`, `committed-sanitized`, `ref-based` all produce the same identity bytes for the same finding. Sanitization only strips the human-readable rendering; it doesn't change which findings pair across runs.
166
+
167
+ ### Review what's allowlisted
168
+
169
+ ```bash
170
+ npx vyuh-dxkit allowlist list # all entries (text)
171
+ npx vyuh-dxkit allowlist show <fingerprint> # one entry's full detail
172
+ npx vyuh-dxkit allowlist audit # expired / soon-to-expire / missing-rationale
173
+ npx vyuh-dxkit allowlist prune # remove expired entries
174
+ ```
175
+
176
+ Run `audit` periodically — `accepted-risk` and `deferred` entries that pass their expiry should either be re-justified (renew expiry) or pruned (remove the entry; the underlying finding will re-flag on the next scan).
177
+
178
+ ### Stale annotations
179
+
180
+ If the underlying finding is fixed but the inline annotation lingers, the next scan emits a `stale-allow` finding pointing at the orphaned comment. The remediation is always to remove the annotation — dxkit refuses to allowlist a stale-allow finding (allowlisting the warning that an annotation is stale would defeat the entire model).
181
+
96
182
  ## Verification — never skip
97
183
 
98
184
  After each fix:
99
185
 
100
186
  ```bash
101
187
  # Re-run the SPECIFIC analyzer that flagged the finding
102
- vyuh-dxkit vulnerabilities # or quality / test-gaps / health
188
+ npx vyuh-dxkit vulnerabilities # or quality / test-gaps / health
103
189
  ```
104
190
 
105
191
  The fix is verified when:
@@ -111,14 +197,19 @@ If the finding's still there: the fix didn't work, try again.
111
197
 
112
198
  ## Baseline decisions
113
199
 
114
- Once a finding is fixed AND verified gone, the workflow depends on what changed:
200
+ Once a finding is processed (fixed, allowlisted, or accepted), the workflow depends on which path you took:
115
201
 
116
202
  | Scenario | Action |
117
203
  |---|---|
118
- | Fix landed via a code change | Commit the code. Baseline is unchanged. Future scans confirm the fix held. |
119
- | Fix landed via a config change (e.g., new entry in `.dxkit-ignore`) | Re-baseline: `vyuh-dxkit baseline create --force`. Commit both `.dxkit-ignore` and the new baseline. |
120
- | Finding accepted as known + not blocking | Re-baseline with explicit reason in the commit message. Future scans treat it as pre-existing, not net-new. |
121
- | Finding is genuinely a false positive | First try suppression on the offending line. If you can't suppress, re-baseline. |
204
+ | Fix landed via a code change | Commit the code. Baseline + allowlist are unchanged. Future scans confirm the fix held. |
205
+ | Genuine false positive OR intentional pattern | `vyuh-dxkit allowlist add` with `category=false-positive` or `test-fixture`. Commit the annotation / allowlist file. Baseline is unchanged. |
206
+ | Real risk neutralized externally (WAF, runtime guard) | `vyuh-dxkit allowlist add` with `category=mitigated-externally` + a reason describing the mitigation. Baseline unchanged. |
207
+ | Real risk, accepted by team, won't fix | `vyuh-dxkit allowlist add` with `category=accepted-risk` + `--expires=YYYY-MM-DD` (defaults 90 days). Acknowledged-severity required for high/critical. |
208
+ | Real risk, will fix later (tracked work) | `vyuh-dxkit allowlist add` with `category=deferred` + `--expires=YYYY-MM-DD`. The expiry forces re-review when the deadline passes. |
209
+ | Fix landed via a config change (e.g., new entry in `.dxkit-ignore`) | Re-baseline: `npx vyuh-dxkit baseline create --force`. Commit both `.dxkit-ignore` and the new baseline. |
210
+ | Brownfield acceptance (the whole CURRENT state is known mess; future regressions must be net-new) | Re-baseline with an explicit reason in the commit message. Reserve this for the deliberate "draw a line here" moment, not per-finding suppression. |
211
+
212
+ **Prefer the allowlist over re-baselining for per-finding decisions.** The allowlist carries a typed category + reason + (when relevant) expiry; the baseline carries only "this finding was here." Future maintainers reading `vyuh-dxkit allowlist show <fingerprint>` see WHY the suppression is in place; reading the baseline file shows only that the finding existed at capture time. Per-finding decisions belong in the allowlist; codebase-wide brownfield acceptance belongs in the baseline.
122
213
 
123
214
  **Never** re-baseline a finding silently — the commit message should explain why the regression is accepted. Future maintainers reading `git log .dxkit/baselines/` should see the rationale.
124
215
 
@@ -127,24 +218,27 @@ Once a finding is fixed AND verified gone, the workflow depends on what changed:
127
218
  After fixing N findings, run the guardrail check before pushing:
128
219
 
129
220
  ```bash
130
- vyuh-dxkit guardrail check
221
+ npx vyuh-dxkit guardrail check
131
222
  ```
132
223
 
133
224
  Exit 0 = your fixes didn't introduce any net-new regressions (you only removed/fixed things). Exit 1 = something new appeared; address that before pushing.
134
225
 
135
226
  ## When fixes get expensive
136
227
 
137
- Sometimes the right call is: don't fix, accept as baseline.
228
+ Sometimes the right call is: don't fix, allowlist (or re-baseline if it's brownfield-wide).
138
229
 
139
230
  Examples:
140
- - Legacy code on a deprecation path (sunset > fix)
141
- - A SAST finding in vendored code you don't maintain
142
- - A test gap on a one-off script that doesn't merit tests
231
+ - Legacy code on a deprecation path (sunset > fix) → `accepted-risk` with expiry matching the sunset date
232
+ - A SAST finding in vendored code you don't maintain → `mitigated-externally` if the vendor patches separately, else `accepted-risk`
233
+ - A test gap on a one-off script that doesn't merit tests → `accepted-risk`
234
+ - An import line flagged by a scanner that you've reviewed and confirmed safe → `false-positive` inline annotation at the import
143
235
 
144
- In those cases: accept-as-baseline with a commit message explaining the call. dxkit's baseline IS designed to support this the brownfield contract is "today's mess is acknowledged; tomorrow's must be a real improvement." Use it.
236
+ In those cases: `vyuh-dxkit allowlist add` is the right tool for per-finding decisions (typed reason + expiry where relevant). Reserve "accept as baseline" for the deliberate one-shot brownfield moment ("this entire current state is known mess; today's findings are the new baseline"). The two surfaces complement each other — allowlist for individual judgment calls, baseline for the codebase-wide line in the sand.
145
237
 
146
238
  ## Hand-offs
147
239
 
148
240
  - For ignore-file edits as part of a fix → `dxkit-config` skill
149
241
  - For hook-related issues during a fix push → `dxkit-hooks` skill
150
242
  - For re-running reports between fixes → `dxkit-reports` skill
243
+ - For broken dxkit install (hooks not firing, vyuh-dxkit not on PATH) → `dxkit-fix` skill
244
+ - For allowlist management beyond the per-finding `add` path (auditing existing entries, pruning expired ones, reviewing the team's overall suppression posture) → run `npx vyuh-dxkit allowlist audit` / `list` / `prune` directly; no separate skill yet