@vyuhlabs/dxkit 2.4.8 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. package/CHANGELOG.md +312 -0
  2. package/README.md +360 -439
  3. package/dist/analyzers/security/aggregator.d.ts.map +1 -1
  4. package/dist/analyzers/security/aggregator.js +4 -46
  5. package/dist/analyzers/security/aggregator.js.map +1 -1
  6. package/dist/analyzers/tools/fingerprint.d.ts +91 -26
  7. package/dist/analyzers/tools/fingerprint.d.ts.map +1 -1
  8. package/dist/analyzers/tools/fingerprint.js +111 -22
  9. package/dist/analyzers/tools/fingerprint.js.map +1 -1
  10. package/dist/analyzers/tools/generic.d.ts.map +1 -1
  11. package/dist/analyzers/tools/generic.js +6 -1
  12. package/dist/analyzers/tools/generic.js.map +1 -1
  13. package/dist/analyzers/tools/gitleaks.d.ts +24 -1
  14. package/dist/analyzers/tools/gitleaks.d.ts.map +1 -1
  15. package/dist/analyzers/tools/gitleaks.js +20 -11
  16. package/dist/analyzers/tools/gitleaks.js.map +1 -1
  17. package/dist/analyzers/tools/graphify.d.ts.map +1 -1
  18. package/dist/analyzers/tools/graphify.js +9 -5
  19. package/dist/analyzers/tools/graphify.js.map +1 -1
  20. package/dist/analyzers/tools/tool-registry.d.ts +19 -1
  21. package/dist/analyzers/tools/tool-registry.d.ts.map +1 -1
  22. package/dist/analyzers/tools/tool-registry.js +25 -0
  23. package/dist/analyzers/tools/tool-registry.js.map +1 -1
  24. package/dist/analyzers/types.d.ts +6 -4
  25. package/dist/analyzers/types.d.ts.map +1 -1
  26. package/dist/baseline/baseline-file.d.ts +104 -0
  27. package/dist/baseline/baseline-file.d.ts.map +1 -0
  28. package/dist/baseline/baseline-file.js +110 -0
  29. package/dist/baseline/baseline-file.js.map +1 -0
  30. package/dist/baseline/check-renderers.d.ts +108 -0
  31. package/dist/baseline/check-renderers.d.ts.map +1 -0
  32. package/dist/baseline/check-renderers.js +379 -0
  33. package/dist/baseline/check-renderers.js.map +1 -0
  34. package/dist/baseline/check.d.ts +127 -0
  35. package/dist/baseline/check.d.ts.map +1 -0
  36. package/dist/baseline/check.js +462 -0
  37. package/dist/baseline/check.js.map +1 -0
  38. package/dist/baseline/content-hash.d.ts +83 -0
  39. package/dist/baseline/content-hash.d.ts.map +1 -0
  40. package/dist/baseline/content-hash.js +131 -0
  41. package/dist/baseline/content-hash.js.map +1 -0
  42. package/dist/baseline/create.d.ts +96 -0
  43. package/dist/baseline/create.d.ts.map +1 -0
  44. package/dist/baseline/create.js +339 -0
  45. package/dist/baseline/create.js.map +1 -0
  46. package/dist/baseline/entry-to-located.d.ts +35 -0
  47. package/dist/baseline/entry-to-located.d.ts.map +1 -0
  48. package/dist/baseline/entry-to-located.js +72 -0
  49. package/dist/baseline/entry-to-located.js.map +1 -0
  50. package/dist/baseline/finding-identity.d.ts +47 -0
  51. package/dist/baseline/finding-identity.d.ts.map +1 -0
  52. package/dist/baseline/finding-identity.js +292 -0
  53. package/dist/baseline/finding-identity.js.map +1 -0
  54. package/dist/baseline/git-aware-match.d.ts +146 -0
  55. package/dist/baseline/git-aware-match.d.ts.map +1 -0
  56. package/dist/baseline/git-aware-match.js +439 -0
  57. package/dist/baseline/git-aware-match.js.map +1 -0
  58. package/dist/baseline/policy.d.ts +171 -0
  59. package/dist/baseline/policy.d.ts.map +1 -0
  60. package/dist/baseline/policy.js +206 -0
  61. package/dist/baseline/policy.js.map +1 -0
  62. package/dist/baseline/producers/health.d.ts +30 -0
  63. package/dist/baseline/producers/health.d.ts.map +1 -0
  64. package/dist/baseline/producers/health.js +42 -0
  65. package/dist/baseline/producers/health.js.map +1 -0
  66. package/dist/baseline/producers/index.d.ts +164 -0
  67. package/dist/baseline/producers/index.d.ts.map +1 -0
  68. package/dist/baseline/producers/index.js +200 -0
  69. package/dist/baseline/producers/index.js.map +1 -0
  70. package/dist/baseline/producers/licenses.d.ts +23 -0
  71. package/dist/baseline/producers/licenses.d.ts.map +1 -0
  72. package/dist/baseline/producers/licenses.js +46 -0
  73. package/dist/baseline/producers/licenses.js.map +1 -0
  74. package/dist/baseline/producers/quality.d.ts +39 -0
  75. package/dist/baseline/producers/quality.d.ts.map +1 -0
  76. package/dist/baseline/producers/quality.js +84 -0
  77. package/dist/baseline/producers/quality.js.map +1 -0
  78. package/dist/baseline/producers/secret-hmac.d.ts +45 -0
  79. package/dist/baseline/producers/secret-hmac.d.ts.map +1 -0
  80. package/dist/baseline/producers/secret-hmac.js +70 -0
  81. package/dist/baseline/producers/secret-hmac.js.map +1 -0
  82. package/dist/baseline/producers/security.d.ts +59 -0
  83. package/dist/baseline/producers/security.d.ts.map +1 -0
  84. package/dist/baseline/producers/security.js +135 -0
  85. package/dist/baseline/producers/security.js.map +1 -0
  86. package/dist/baseline/producers/tests.d.ts +36 -0
  87. package/dist/baseline/producers/tests.d.ts.map +1 -0
  88. package/dist/baseline/producers/tests.js +69 -0
  89. package/dist/baseline/producers/tests.js.map +1 -0
  90. package/dist/baseline/salt.d.ts +45 -0
  91. package/dist/baseline/salt.d.ts.map +1 -0
  92. package/dist/baseline/salt.js +113 -0
  93. package/dist/baseline/salt.js.map +1 -0
  94. package/dist/baseline/show.d.ts +79 -0
  95. package/dist/baseline/show.d.ts.map +1 -0
  96. package/dist/baseline/show.js +233 -0
  97. package/dist/baseline/show.js.map +1 -0
  98. package/dist/baseline/types.d.ts +482 -0
  99. package/dist/baseline/types.d.ts.map +1 -0
  100. package/dist/baseline/types.js +53 -0
  101. package/dist/baseline/types.js.map +1 -0
  102. package/dist/cli.d.ts.map +1 -1
  103. package/dist/cli.js +398 -82
  104. package/dist/cli.js.map +1 -1
  105. package/dist/constants.d.ts.map +1 -1
  106. package/dist/constants.js +0 -4
  107. package/dist/constants.js.map +1 -1
  108. package/dist/doctor.d.ts.map +1 -1
  109. package/dist/doctor.js +39 -35
  110. package/dist/doctor.js.map +1 -1
  111. package/dist/fail-on.d.ts +84 -0
  112. package/dist/fail-on.d.ts.map +1 -0
  113. package/dist/fail-on.js +128 -0
  114. package/dist/fail-on.js.map +1 -0
  115. package/dist/generator.d.ts +1 -1
  116. package/dist/generator.d.ts.map +1 -1
  117. package/dist/generator.js +81 -274
  118. package/dist/generator.js.map +1 -1
  119. package/dist/hooks-cli.d.ts +20 -0
  120. package/dist/hooks-cli.d.ts.map +1 -0
  121. package/dist/hooks-cli.js +145 -0
  122. package/dist/hooks-cli.js.map +1 -0
  123. package/dist/languages/csharp.d.ts.map +1 -1
  124. package/dist/languages/csharp.js +4 -9
  125. package/dist/languages/csharp.js.map +1 -1
  126. package/dist/languages/go.d.ts.map +1 -1
  127. package/dist/languages/go.js +3 -14
  128. package/dist/languages/go.js.map +1 -1
  129. package/dist/languages/index.d.ts +19 -1
  130. package/dist/languages/index.d.ts.map +1 -1
  131. package/dist/languages/index.js +32 -0
  132. package/dist/languages/index.js.map +1 -1
  133. package/dist/languages/java.d.ts.map +1 -1
  134. package/dist/languages/java.js +4 -6
  135. package/dist/languages/java.js.map +1 -1
  136. package/dist/languages/kotlin.d.ts.map +1 -1
  137. package/dist/languages/kotlin.js +9 -11
  138. package/dist/languages/kotlin.js.map +1 -1
  139. package/dist/languages/python.d.ts.map +1 -1
  140. package/dist/languages/python.js +4 -15
  141. package/dist/languages/python.js.map +1 -1
  142. package/dist/languages/ruby.d.ts.map +1 -1
  143. package/dist/languages/ruby.js +4 -6
  144. package/dist/languages/ruby.js.map +1 -1
  145. package/dist/languages/rust.d.ts.map +1 -1
  146. package/dist/languages/rust.js +4 -4
  147. package/dist/languages/rust.js.map +1 -1
  148. package/dist/languages/types.d.ts +29 -28
  149. package/dist/languages/types.d.ts.map +1 -1
  150. package/dist/languages/typescript.d.ts.map +1 -1
  151. package/dist/languages/typescript.js +31 -4
  152. package/dist/languages/typescript.js.map +1 -1
  153. package/dist/lib.d.ts +2 -3
  154. package/dist/lib.d.ts.map +1 -1
  155. package/dist/lib.js +3 -6
  156. package/dist/lib.js.map +1 -1
  157. package/dist/prompts.d.ts.map +1 -1
  158. package/dist/prompts.js +0 -10
  159. package/dist/prompts.js.map +1 -1
  160. package/dist/report-schema.d.ts +42 -0
  161. package/dist/report-schema.d.ts.map +1 -0
  162. package/dist/report-schema.js +54 -0
  163. package/dist/report-schema.js.map +1 -0
  164. package/dist/ship-installers.d.ts +112 -0
  165. package/dist/ship-installers.d.ts.map +1 -0
  166. package/dist/ship-installers.js +530 -0
  167. package/dist/ship-installers.js.map +1 -0
  168. package/dist/tools-cli.d.ts.map +1 -1
  169. package/dist/tools-cli.js +45 -9
  170. package/dist/tools-cli.js.map +1 -1
  171. package/dist/types.d.ts +0 -4
  172. package/dist/types.d.ts.map +1 -1
  173. package/dist/update.d.ts.map +1 -1
  174. package/dist/update.js +0 -4
  175. package/dist/update.js.map +1 -1
  176. package/package.json +17 -11
  177. package/templates/.claude/skills/dxkit-action/SKILL.md +150 -0
  178. package/templates/.claude/skills/dxkit-config/SKILL.md +124 -0
  179. package/templates/.claude/skills/dxkit-hooks/SKILL.md +109 -0
  180. package/templates/.claude/skills/dxkit-init/SKILL.md +93 -0
  181. package/templates/.claude/skills/dxkit-learn/SKILL.md +84 -0
  182. package/templates/.claude/skills/dxkit-reports/SKILL.md +111 -0
  183. package/templates/.devcontainer/devcontainer.json +55 -0
  184. package/templates/.devcontainer/install-agent-clis.sh +42 -0
  185. package/templates/.devcontainer/post-create.sh +81 -0
  186. package/templates/.githooks/pre-commit +55 -0
  187. package/templates/.githooks/pre-push +63 -0
  188. package/templates/.github/workflows/dxkit-baseline-refresh.yml +78 -0
  189. package/templates/.github/workflows/dxkit-guardrails.yml +98 -0
  190. package/templates/AGENTS.md.template +137 -0
  191. package/templates/CLAUDE.md.template +16 -245
  192. package/dist/codebase-scanner.d.ts +0 -36
  193. package/dist/codebase-scanner.d.ts.map +0 -1
  194. package/dist/codebase-scanner.js +0 -688
  195. package/dist/codebase-scanner.js.map +0 -1
  196. package/dist/project-yaml.d.ts +0 -13
  197. package/dist/project-yaml.d.ts.map +0 -1
  198. package/dist/project-yaml.js +0 -188
  199. package/dist/project-yaml.js.map +0 -1
  200. package/templates/.ai/README.md +0 -117
  201. package/templates/.ai/prompts/execution-prompt.md +0 -9
  202. package/templates/.ai/prompts/planning-prompt.md +0 -18
  203. package/templates/.ai/prompts/session-end-template.md +0 -182
  204. package/templates/.ai/prompts/session-end.md +0 -132
  205. package/templates/.ai/prompts/session-start.md +0 -109
  206. package/templates/.ai/prompts/step-by-step.md +0 -113
  207. package/templates/.ai/sessions/.gitkeep +0 -0
  208. package/templates/.claude/agents/doc-writer.md +0 -107
  209. package/templates/.claude/agents/knowledge-bot.md +0 -64
  210. package/templates/.claude/agents/onboarding.md +0 -61
  211. package/templates/.claude/agents/quality-reviewer.md +0 -85
  212. package/templates/.claude/agents-available/code-reviewer.md +0 -29
  213. package/templates/.claude/agents-available/codebase-explorer.md +0 -100
  214. package/templates/.claude/agents-available/dashboard-builder.md +0 -433
  215. package/templates/.claude/agents-available/debugger.md +0 -29
  216. package/templates/.claude/agents-available/dependency-mapper.md +0 -80
  217. package/templates/.claude/agents-available/dev-report.md +0 -108
  218. package/templates/.claude/agents-available/doc-writer.md +0 -107
  219. package/templates/.claude/agents-available/feature-builder.md +0 -163
  220. package/templates/.claude/agents-available/feature-planner.md +0 -185
  221. package/templates/.claude/agents-available/health-auditor.md +0 -95
  222. package/templates/.claude/agents-available/hooks-configurator.md +0 -211
  223. package/templates/.claude/agents-available/knowledge-bot.md +0 -62
  224. package/templates/.claude/agents-available/plan-executor.md +0 -133
  225. package/templates/.claude/agents-available/strategic-planner.md +0 -141
  226. package/templates/.claude/agents-available/test-gap-finder.md +0 -67
  227. package/templates/.claude/agents-available/test-writer.md +0 -34
  228. package/templates/.claude/agents-available/vulnerability-scanner.md +0 -173
  229. package/templates/.claude/commands/ask.md +0 -7
  230. package/templates/.claude/commands/build-feature.md +0 -26
  231. package/templates/.claude/commands/build.md.template +0 -30
  232. package/templates/.claude/commands/check.md.template +0 -43
  233. package/templates/.claude/commands/dashboard.md +0 -28
  234. package/templates/.claude/commands/deps.md +0 -15
  235. package/templates/.claude/commands/dev-report.md +0 -50
  236. package/templates/.claude/commands/docs.md +0 -21
  237. package/templates/.claude/commands/doctor.md +0 -21
  238. package/templates/.claude/commands/enable-agent.md +0 -12
  239. package/templates/.claude/commands/execute-plan.md +0 -25
  240. package/templates/.claude/commands/explore-codebase.md +0 -12
  241. package/templates/.claude/commands/export-pdf.md +0 -30
  242. package/templates/.claude/commands/feature.md +0 -25
  243. package/templates/.claude/commands/fix-issue.md +0 -12
  244. package/templates/.claude/commands/fix.md.template +0 -32
  245. package/templates/.claude/commands/health.md +0 -58
  246. package/templates/.claude/commands/help.md +0 -36
  247. package/templates/.claude/commands/learn.md +0 -48
  248. package/templates/.claude/commands/onboarding.md +0 -21
  249. package/templates/.claude/commands/plan.md +0 -20
  250. package/templates/.claude/commands/quality.md.template +0 -65
  251. package/templates/.claude/commands/session-end.md +0 -40
  252. package/templates/.claude/commands/session-start.md +0 -30
  253. package/templates/.claude/commands/setup-hooks.md +0 -18
  254. package/templates/.claude/commands/setup-pr-review.md +0 -72
  255. package/templates/.claude/commands/stealth-mode.md +0 -17
  256. package/templates/.claude/commands/test-gaps.md +0 -49
  257. package/templates/.claude/commands/test.md.template +0 -40
  258. package/templates/.claude/commands/vulnerabilities.md +0 -49
  259. package/templates/.claude/skills/build/SKILL.md.template +0 -98
  260. package/templates/.claude/skills/deploy/SKILL.md.template +0 -131
  261. package/templates/.claude/skills/deploy/references/gotchas.md +0 -5
  262. package/templates/.claude/skills/doctor/SKILL.md +0 -54
  263. package/templates/.claude/skills/gcloud/SKILL.md +0 -66
  264. package/templates/.claude/skills/gcloud/references/gotchas.md +0 -5
  265. package/templates/.claude/skills/learned/SKILL.md +0 -55
  266. package/templates/.claude/skills/learned/references/conventions.md +0 -11
  267. package/templates/.claude/skills/learned/references/deny-recommendations.md +0 -18
  268. package/templates/.claude/skills/learned/references/gotchas.md +0 -11
  269. package/templates/.claude/skills/pulumi/SKILL.md +0 -73
  270. package/templates/.claude/skills/quality/SKILL.md.template +0 -108
  271. package/templates/.claude/skills/quality/references/gotchas.md +0 -5
  272. package/templates/.claude/skills/review/SKILL.md.template +0 -73
  273. package/templates/.claude/skills/scaffold/SKILL.md.template +0 -123
  274. package/templates/.claude/skills/secrets/SKILL.md +0 -52
  275. package/templates/.claude/skills/session/SKILL.md +0 -43
  276. package/templates/.claude/skills/test/SKILL.md.template +0 -122
  277. package/templates/.claude/skills/test/references/gotchas.md +0 -5
  278. package/templates/.devcontainer/Dockerfile.dev.template +0 -89
  279. package/templates/.devcontainer/devcontainer.json.template +0 -184
  280. package/templates/.devcontainer/docker-compose.yml.template +0 -105
  281. package/templates/.devcontainer/init-scripts/01-init.sql.template +0 -12
  282. package/templates/.devcontainer/post-create.sh.template +0 -298
  283. package/templates/.github/workflows/ci.yml.template +0 -399
  284. package/templates/.github/workflows/quality.yml.template +0 -376
  285. package/templates/.pre-commit-config.yaml.template +0 -106
  286. package/templates/.project/config/edit_config.py +0 -275
  287. package/templates/.project/config/project_config.py +0 -894
  288. package/templates/.project/scripts/codegen/generate-all.sh +0 -20
  289. package/templates/.project/scripts/codegen/validate-all.sh +0 -17
  290. package/templates/.project/scripts/docs/generate-all.sh +0 -30
  291. package/templates/.project/scripts/docs/serve.sh +0 -20
  292. package/templates/.project/scripts/quality/fix-all.sh +0 -138
  293. package/templates/.project/scripts/quality/lint-go.sh +0 -34
  294. package/templates/.project/scripts/quality/lint-python.sh +0 -54
  295. package/templates/.project/scripts/quality/run-all.sh +0 -497
  296. package/templates/.project/scripts/session/commit.sh +0 -70
  297. package/templates/.project/scripts/session/create-pr.sh +0 -165
  298. package/templates/.project/scripts/session/end.sh +0 -207
  299. package/templates/.project/scripts/session/start.sh +0 -233
  300. package/templates/.project/scripts/setup/doctor.sh +0 -404
  301. package/templates/.project/scripts/setup/interactive-setup.sh +0 -585
  302. package/templates/.project/scripts/sync/sync-template.sh +0 -328
  303. package/templates/.project/scripts/test/run-all.sh +0 -179
  304. package/templates/.project/scripts/test/run-quick.sh +0 -25
  305. package/templates/Makefile +0 -514
  306. package/templates/config/versions.yaml +0 -57
  307. package/templates/configs/go/.golangci.yml.template +0 -172
  308. package/templates/configs/go/go.mod.template +0 -15
  309. package/templates/configs/java/README.md +0 -6
  310. package/templates/configs/kotlin/README.md +0 -6
  311. package/templates/configs/node/package.json.template +0 -67
  312. package/templates/configs/node/tsconfig.json.template +0 -53
  313. package/templates/configs/python/pyproject.toml.template +0 -92
  314. package/templates/configs/python/pytest.ini.template +0 -64
  315. package/templates/configs/python/ruff.toml.template +0 -79
  316. package/templates/configs/ruby/README.md +0 -6
  317. package/templates/configs/rust/Cargo.toml.template +0 -51
  318. package/templates/configs/shared/.editorconfig +0 -67
  319. package/templates/scripts/validate-templates.sh +0 -449
package/dist/cli.js CHANGED
@@ -39,7 +39,6 @@ const vendored_advisor_1 = require("./analyzers/tools/vendored-advisor");
39
39
  const detect_1 = require("./detect");
40
40
  const generator_1 = require("./generator");
41
41
  const prompts_1 = require("./prompts");
42
- const project_yaml_1 = require("./project-yaml");
43
42
  const update_1 = require("./update");
44
43
  const doctor_1 = require("./doctor");
45
44
  const constants_1 = require("./constants");
@@ -47,6 +46,9 @@ const logger = __importStar(require("./logger"));
47
46
  const scoring_1 = require("./scoring");
48
47
  const tools_unavailable_prose_1 = require("./analyzers/tools/tools-unavailable-prose");
49
48
  const report_date_1 = require("./analyzers/tools/report-date");
49
+ const fail_on_1 = require("./fail-on");
50
+ const report_schema_1 = require("./report-schema");
51
+ const ship_installers_1 = require("./ship-installers");
50
52
  const fs = __importStar(require("fs"));
51
53
  const path = __importStar(require("path"));
52
54
  // process.stdout.write returns false when the OS pipe buffer is full
@@ -60,12 +62,51 @@ async function emitJson(payload) {
60
62
  await new Promise((resolve) => process.stdout.once('drain', resolve));
61
63
  }
62
64
  }
65
+ /**
66
+ * Apply `--fail-on-score` to a higher-is-better score. Exits with
67
+ * code 1 + a logged reason when the gate fires. Skips when the user
68
+ * didn't pass the flag. Centralized so every analyzer that supports
69
+ * the flag fires consistent messages.
70
+ */
71
+ function applyFailOnScore(raw, score, scoreLabel) {
72
+ if (raw === undefined)
73
+ return;
74
+ const threshold = (0, fail_on_1.parseScoreThreshold)(raw);
75
+ if (threshold === null) {
76
+ logger.fail(`--fail-on-score: invalid value "${raw}". Expected a number in [0, 100].`);
77
+ process.exit(1);
78
+ }
79
+ const verdict = (0, fail_on_1.checkFailOnScore)(score, threshold);
80
+ if (verdict.fails) {
81
+ logger.fail(`${scoreLabel} ${verdict.reason}`);
82
+ process.exit(1);
83
+ }
84
+ }
85
+ /**
86
+ * Apply `--fail-on-severity` to a per-severity count map. Exits
87
+ * with code 1 + a logged reason when the gate fires. Skips when
88
+ * the user didn't pass the flag.
89
+ */
90
+ function applyFailOnSeverity(raw, counts, countsLabel) {
91
+ if (raw === undefined)
92
+ return;
93
+ const tier = (0, fail_on_1.parseSeverityTier)(raw);
94
+ if (tier === null) {
95
+ logger.fail(`--fail-on-severity: invalid tier "${raw}". Expected one of: critical, high, medium, low.`);
96
+ process.exit(1);
97
+ }
98
+ const verdict = (0, fail_on_1.checkFailOnSeverity)(counts, tier);
99
+ if (verdict.fails) {
100
+ logger.fail(`${countsLabel}: ${verdict.reason}`);
101
+ process.exit(1);
102
+ }
103
+ }
63
104
  function printUsage() {
64
105
  console.log(`
65
- ${logger.bold('vyuh-dxkit')} v${constants_1.VERSION} — AI-native developer experience toolkit
106
+ ${logger.bold('vyuh-dxkit')} v${constants_1.VERSION} — AI-native developer experience toolkit for any codebase
66
107
 
67
108
  ${logger.bold('Usage:')}
68
- vyuh-dxkit init [options] Initialize Claude Code DX in this repo
109
+ vyuh-dxkit init [options] Install dxkit agent DX in this repo
69
110
  vyuh-dxkit update [options] Re-generate (preserves evolved files)
70
111
  vyuh-dxkit doctor Verify setup
71
112
  vyuh-dxkit health [path] Run deterministic health analysis
@@ -81,23 +122,56 @@ function printUsage() {
81
122
  vyuh-dxkit to-xlsx <json> Convert a dxkit JSON report to 15-col XLSX
82
123
  vyuh-dxkit tools [path] Show required analysis tools status
83
124
  vyuh-dxkit tools install Interactively install missing tools
125
+ vyuh-dxkit baseline create [path] [--name <name>] [--force]
126
+ Capture per-finding identities to .dxkit/baselines/<name>.json
127
+ (read later by guardrail check to gate new regressions)
128
+ vyuh-dxkit baseline show [path] [--name <n>] [--baseline <path>]
129
+ [--kind <kind>] [--json]
130
+ Pretty-print the on-disk baseline. Default: summary +
131
+ per-kind counts. --kind drills into one kind. --json
132
+ emits a schema-banner-wrapped payload.
133
+ vyuh-dxkit guardrail check [path] [--name <n>] [--baseline <path>]
134
+ [--changed-only] [--policy <path>]
135
+ [--json | --markdown]
136
+ Diff current scan against the named baseline; block on net-new
137
+ regressions per brownfield policy. Exit code 1 when blocked.
138
+ vyuh-dxkit hooks activate [path]
139
+ Idempotently set core.hooksPath = .githooks. Wired into
140
+ package.json postinstall by 'init --with-hooks' so every
141
+ clone + 'npm install' activates the dxkit hooks
142
+ automatically. Safe to run by hand; always exits 0.
84
143
 
85
144
  ${logger.bold('Init options:')}
86
- --dx-only Just .claude/ + CLAUDE.md (default)
87
- --full Everything: DX + quality + hooks + CI
88
- --detect Auto-detect stack, minimal prompts
89
- --yes Accept all defaults, no prompts
90
- --force Overwrite existing files (except evolved)
91
- --stealth Gitignore generated files (local-only, not committed)
92
- --name <n> Override project name
93
- --no-scan Skip codebase analysis
145
+ --dx-only Just .claude/ + CLAUDE.md (default)
146
+ --full Everything: DX + quality + hooks + devcontainer +
147
+ CI guardrails + baseline-refresh workflow
148
+ --with-hooks Install .githooks/pre-push guardrail hook (pre-commit opt-in)
149
+ --with-precommit-hook Also install .githooks/pre-commit (slow on large repos)
150
+ --with-devcontainer Install .devcontainer/ with pinned toolchains +
151
+ dxkit + Claude Code & Codex CLIs
152
+ --with-dxkit-agents Install AGENTS.md + CLAUDE.md shim + the 6 dxkit-
153
+ specific skills (learn/init/config/hooks/reports/
154
+ action) for Claude Code auto-discovery
155
+ --with-ci Install .github/workflows/dxkit-guardrails.yml
156
+ (PR-gate that posts a markdown summary comment)
157
+ --with-baseline-refresh Install .github/workflows/dxkit-baseline-refresh.yml
158
+ --with-pr-review Install .github/workflows/pr-review.yml (AI PR review; opt-in)
159
+ (post-merge auto-regen of .dxkit/baselines/main.json)
160
+ --detect Auto-detect stack, minimal prompts
161
+ --yes Accept all defaults, no prompts
162
+ --force Overwrite existing files (incl. existing hooks/
163
+ devcontainer instead of writing .dxkit sidecars)
164
+ --stealth Gitignore generated files (local-only, not committed)
165
+ --name <n> Override project name
166
+ --no-scan Skip codebase analysis
94
167
 
95
168
  ${logger.bold('Update options:')}
96
169
  --force Overwrite modified files (except evolved)
97
170
  --rescan Re-run codebase analysis
98
171
 
99
172
  ${logger.bold('Analyzer options (health, vulnerabilities, test-gaps, quality, dev-report, licenses, bom):')}
100
- --json Print report as JSON to stdout
173
+ --json Print report as JSON to stdout (top-level 'schema' field
174
+ carries the dxkit.<kind>-report.v1 banner for version-gating)
101
175
  --verbose Print per-tool timing to stderr
102
176
  --no-save Skip writing the markdown report file
103
177
  --detailed Also write <name>-detailed.md + .json with evidence + ranked actions
@@ -107,6 +181,12 @@ function printUsage() {
107
181
  advisory rollup under byTopLevelDep still reflects transitives)
108
182
  --with-coverage Health/test-gaps: materialize coverage artifacts via per-pack
109
183
  runTests() before analysis (line-coverage truth vs filename match)
184
+ --fail-on-score <N> Exit 1 when the analyzer's headline score drops below N.
185
+ Applies to: health (overallScore), test-gaps (effectiveCoverage).
186
+ --fail-on-severity <tier>
187
+ Exit 1 when any finding at <tier> or higher exists.
188
+ tier ∈ critical|high|medium|low.
189
+ Applies to: vulnerabilities, bom.
110
190
 
111
191
  ${logger.bold('Examples:')}
112
192
  npx vyuh-dxkit init # Interactive
@@ -147,6 +227,21 @@ async function run(argv) {
147
227
  timeout: { type: 'string' },
148
228
  'no-fail-fast': { type: 'boolean', default: false },
149
229
  'with-coverage': { type: 'boolean', default: false },
230
+ 'changed-only': { type: 'boolean', default: false },
231
+ baseline: { type: 'string' },
232
+ policy: { type: 'string' },
233
+ markdown: { type: 'boolean', default: false },
234
+ 'fail-on-score': { type: 'string' },
235
+ 'fail-on-severity': { type: 'string' },
236
+ summary: { type: 'boolean', default: false },
237
+ kind: { type: 'string' },
238
+ 'with-hooks': { type: 'boolean', default: false },
239
+ 'with-precommit-hook': { type: 'boolean', default: false },
240
+ 'with-devcontainer': { type: 'boolean', default: false },
241
+ 'with-dxkit-agents': { type: 'boolean', default: false },
242
+ 'with-ci': { type: 'boolean', default: false },
243
+ 'with-baseline-refresh': { type: 'boolean', default: false },
244
+ 'with-pr-review': { type: 'boolean', default: false },
150
245
  },
151
246
  allowPositionals: true,
152
247
  strict: false,
@@ -174,66 +269,117 @@ async function run(argv) {
174
269
  switch (command) {
175
270
  case 'init': {
176
271
  logger.header('vyuh-dxkit init');
177
- let config;
178
- let finalMode = values.full ? 'full' : 'dx-only';
179
- // If .project.yaml exists (written by create-devstack), try using it as config source
180
- if ((0, project_yaml_1.hasProjectYaml)(cwd)) {
181
- const yamlConfig = (0, project_yaml_1.readProjectYaml)(cwd);
182
- if (yamlConfig) {
183
- logger.info('Found .project.yaml using as config source.');
184
- config = yamlConfig;
185
- const langs = Object.entries(config.languages)
186
- .filter(([, v]) => v)
187
- .map(([k]) => k);
188
- const tools = Object.entries(config.tools)
189
- .filter(([, v]) => v)
190
- .map(([k]) => k);
191
- if (langs.length)
192
- logger.success(`Languages: ${langs.join(', ')}`);
193
- if (tools.length)
194
- logger.success(`Tools: ${tools.join(', ')}`);
195
- console.log('');
196
- // .project.yaml implies full mode (create-devstack handles the wizard)
197
- finalMode = values['dx-only'] ? 'dx-only' : 'full';
198
- }
199
- else {
200
- logger.warn('Found .project.yaml but it is malformed — falling back to detection.');
201
- }
272
+ logger.info('Detecting stack...');
273
+ const detected = (0, detect_1.detect)(cwd);
274
+ const langs = Object.entries(detected.languages)
275
+ .filter(([, v]) => v)
276
+ .map(([k]) => k);
277
+ const tools = Object.entries(detected.tools)
278
+ .filter(([, v]) => v)
279
+ .map(([k]) => k);
280
+ if (langs.length === 0) {
281
+ logger.warn('No languages detected. Generating with minimal config.');
202
282
  }
203
- if (!config) {
204
- // No .project.yaml — detect stack and prompt as before
205
- logger.info('Detecting stack...');
206
- const detected = (0, detect_1.detect)(cwd);
207
- const langs = Object.entries(detected.languages)
208
- .filter(([, v]) => v)
209
- .map(([k]) => k);
210
- const tools = Object.entries(detected.tools)
211
- .filter(([, v]) => v)
212
- .map(([k]) => k);
213
- if (langs.length === 0) {
214
- logger.warn('No languages detected. Generating with minimal config.');
215
- }
216
- else {
217
- logger.success(`Languages: ${langs.join(', ')}`);
218
- }
219
- if (tools.length)
220
- logger.success(`Tools: ${tools.join(', ')}`);
221
- if (detected.framework)
222
- logger.success(`Framework: ${detected.framework}`);
223
- if (detected.testRunner)
224
- logger.success(`Tests: ${detected.testRunner.framework} (${detected.testRunner.command})`);
225
- console.log('');
226
- // Resolve config via prompts
227
- const promptOpts = {
228
- yes: !!(values.yes || values.detect),
229
- detect: !!values.detect,
230
- name: values.name,
231
- };
232
- const result = await (0, prompts_1.promptForConfig)(detected, promptOpts);
233
- config = result.config;
234
- finalMode = values.full ? 'full' : values['dx-only'] ? 'dx-only' : result.mode;
283
+ else {
284
+ logger.success(`Languages: ${langs.join(', ')}`);
285
+ }
286
+ if (tools.length)
287
+ logger.success(`Tools: ${tools.join(', ')}`);
288
+ if (detected.framework)
289
+ logger.success(`Framework: ${detected.framework}`);
290
+ if (detected.testRunner)
291
+ logger.success(`Tests: ${detected.testRunner.framework} (${detected.testRunner.command})`);
292
+ console.log(''); // slop-ok
293
+ const promptOpts = {
294
+ yes: !!(values.yes || values.detect),
295
+ detect: !!values.detect,
296
+ name: values.name,
297
+ };
298
+ const promptResult = await (0, prompts_1.promptForConfig)(detected, promptOpts);
299
+ const config = promptResult.config;
300
+ const finalMode = values.full
301
+ ? 'full'
302
+ : values['dx-only']
303
+ ? 'dx-only'
304
+ : promptResult.mode;
305
+ // The six dxkit-* skills + AGENTS.md + CLAUDE.md shim are the
306
+ // marquee 2.5.1 surface. Default-off on bare `init` (keep the
307
+ // first-install quiet); default-on under `--full` (matches the
308
+ // rest of the ship surface — hooks/devcontainer/CI all opt-in
309
+ // via flags but bundled under --full).
310
+ const wantDxkitAgents = !!values.full || !!values['with-dxkit-agents'];
311
+ const result = await (0, generator_1.generate)(cwd, config, finalMode, !!values.force, !!values['no-scan'], wantDxkitAgents);
312
+ // Phase Ship installers (additive). `--full` implies every flag
313
+ // so a one-command setup gets the full 2.5.0 ship surface.
314
+ const isFull = !!values.full;
315
+ // pre-commit hook stays opt-in even under --full because it
316
+ // re-runs every analyzer on every commit (slow on large
317
+ // codebases until incremental scanning lands). Pre-push +
318
+ // CI catch the same regressions before code leaves the
319
+ // developer's machine.
320
+ const wantPrecommitHook = !!values['with-precommit-hook'];
321
+ // --with-precommit-hook implies --with-hooks (so the
322
+ // installer actually runs to install pre-commit alongside
323
+ // pre-push).
324
+ const wantHooks = isFull || !!values['with-hooks'] || wantPrecommitHook;
325
+ const wantDevcontainer = isFull || !!values['with-devcontainer'];
326
+ const wantCi = isFull || !!values['with-ci'];
327
+ const wantBaselineRefresh = isFull || !!values['with-baseline-refresh'];
328
+ // pr-review is opt-in even under --full because the workflow
329
+ // is inert without `ANTHROPIC_API_KEY` + `ENABLE_AI_REVIEW=true`
330
+ // configured separately. Shipping it by default just clutters
331
+ // the Actions tab on repos that don't intend to enable it.
332
+ const wantPrReview = !!values['with-pr-review'];
333
+ const shipResults = [];
334
+ if (wantHooks) {
335
+ shipResults.push({
336
+ label: 'Git hooks',
337
+ result: (0, ship_installers_1.installHooks)(cwd, {
338
+ force: !!values.force,
339
+ withPrecommit: wantPrecommitHook,
340
+ }),
341
+ });
342
+ // Auto-activate hooksPath via package.json postinstall when a
343
+ // package.json is present. No-ops for non-Node repos. Skipping
344
+ // requires explicit user choice rather than a flag: customers
345
+ // who don't want automation can delete the line from
346
+ // scripts.postinstall after init.
347
+ shipResults.push({
348
+ label: 'Hooks auto-activation',
349
+ result: (0, ship_installers_1.installHooksPostinstall)(cwd, { force: !!values.force }),
350
+ });
351
+ }
352
+ if (wantDevcontainer) {
353
+ shipResults.push({
354
+ label: 'Devcontainer',
355
+ result: (0, ship_installers_1.installDevcontainer)(cwd, { force: !!values.force }),
356
+ });
357
+ }
358
+ if (wantCi) {
359
+ shipResults.push({
360
+ label: 'CI guardrails workflow',
361
+ result: (0, ship_installers_1.installCiGuardrails)(cwd, { force: !!values.force }),
362
+ });
363
+ }
364
+ if (wantBaselineRefresh) {
365
+ shipResults.push({
366
+ label: 'CI baseline-refresh workflow',
367
+ result: (0, ship_installers_1.installCiBaselineRefresh)(cwd, { force: !!values.force }),
368
+ });
369
+ }
370
+ if (wantPrReview) {
371
+ shipResults.push({
372
+ label: 'AI PR-review workflow',
373
+ result: (0, ship_installers_1.installPrReview)(cwd, { force: !!values.force }),
374
+ });
235
375
  }
236
- const result = await (0, generator_1.generate)(cwd, config, finalMode, !!values.force, !!values['no-scan']);
376
+ // .gitignore + .dxkit-ignore seeding: default-on, no flag.
377
+ // Additive (existing entries preserved); safe for both fresh
378
+ // installs and re-runs.
379
+ shipResults.push({
380
+ label: 'Ignore files',
381
+ result: (0, ship_installers_1.installIgnoreFiles)(cwd, { force: !!values.force }),
382
+ });
237
383
  // Summary
238
384
  console.log('');
239
385
  logger.header('Summary');
@@ -243,6 +389,23 @@ async function run(argv) {
243
389
  logger.warn(`Skipped: ${result.skipped.length} files (already exist)`);
244
390
  if (result.overwritten.length)
245
391
  logger.info(`Overwritten: ${result.overwritten.length} files`);
392
+ for (const { label, result: r } of shipResults) {
393
+ if (r.installed.length) {
394
+ logger.success(`${label}: installed ${r.installed.length} file(s)`);
395
+ for (const f of r.installed)
396
+ logger.dim(` ${f}`);
397
+ }
398
+ if (r.sidecars.length) {
399
+ logger.warn(`${label}: ${r.sidecars.length} sidecar(s) written (existing files preserved)`);
400
+ for (const f of r.sidecars)
401
+ logger.dim(` ${f}`);
402
+ }
403
+ if (r.skipped.length) {
404
+ logger.dim(`${label}: ${r.skipped.length} file(s) skipped (already present)`);
405
+ }
406
+ for (const note of r.notes)
407
+ logger.info(note);
408
+ }
246
409
  console.log('');
247
410
  logger.info('Manifest written to .vyuh-dxkit.json');
248
411
  // Stealth mode: gitignore only files we just created
@@ -254,6 +417,9 @@ async function run(argv) {
254
417
  console.log('');
255
418
  logger.dim(' Run `vyuh-dxkit doctor` to verify setup');
256
419
  logger.dim(' Run `vyuh-dxkit update` to re-generate after changes');
420
+ if (shipResults.length > 0) {
421
+ logger.dim(" Run `vyuh-dxkit baseline create` to capture today's state");
422
+ }
257
423
  break;
258
424
  }
259
425
  case 'update': {
@@ -311,7 +477,7 @@ async function run(argv) {
311
477
  const healthMetrics = healthResult.metrics;
312
478
  const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
313
479
  if (values.json) {
314
- await emitJson(report);
480
+ await emitJson((0, report_schema_1.stampSchema)(report, 'health'));
315
481
  }
316
482
  else {
317
483
  // Console output
@@ -368,13 +534,17 @@ async function run(argv) {
368
534
  const detailed = buildHealthDetailed(report, healthMetrics);
369
535
  const detailedJsonPath = path.join(reportDir, `health-audit-${date}-detailed.json`);
370
536
  const detailedMdPath = path.join(reportDir, `health-audit-${date}-detailed.md`);
371
- fs.writeFileSync(detailedJsonPath, JSON.stringify(detailed, null, 2));
537
+ fs.writeFileSync(detailedJsonPath, JSON.stringify((0, report_schema_1.stampSchema)(detailed, 'health-detailed'), null, 2));
372
538
  fs.writeFileSync(detailedMdPath, formatHealthDetailedMarkdown(detailed, elapsed));
373
539
  if (values.detailed) {
374
540
  logger.success(`Detailed report saved to ${path.relative(targetPath, detailedMdPath)}`);
375
541
  logger.success(`Detailed JSON saved to ${path.relative(targetPath, detailedJsonPath)}`);
376
542
  }
377
543
  }
544
+ // --fail-on-score: applies to the overall health score. Runs
545
+ // after disk writes so a failure still leaves a complete
546
+ // report behind for inspection.
547
+ applyFailOnScore(values['fail-on-score'], report.summary.overallScore, 'health overallScore');
378
548
  if (!values.json) {
379
549
  // Hint about missing tools (exclude project-side config errors).
380
550
  const PROJECT_ISSUES = ['config error', 'legacy .eslintrc', 'no eslint config'];
@@ -436,7 +606,7 @@ async function run(argv) {
436
606
  const report = await analyzeSecurity(targetPath, { verbose: !!values.verbose });
437
607
  const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
438
608
  if (values.json) {
439
- await emitJson(report);
609
+ await emitJson((0, report_schema_1.stampSchema)(report, 'vulnerabilities'));
440
610
  }
441
611
  else {
442
612
  const s = report.summary.findings;
@@ -470,13 +640,20 @@ async function run(argv) {
470
640
  const securityDetailed = buildSecurityDetailed(report);
471
641
  const securityDetailedJsonPath = path.join(reportDir, `vulnerability-scan-${date}-detailed.json`);
472
642
  const securityDetailedMdPath = path.join(reportDir, `vulnerability-scan-${date}-detailed.md`);
473
- fs.writeFileSync(securityDetailedJsonPath, JSON.stringify(securityDetailed, null, 2));
643
+ fs.writeFileSync(securityDetailedJsonPath, JSON.stringify((0, report_schema_1.stampSchema)(securityDetailed, 'vulnerabilities-detailed'), null, 2));
474
644
  fs.writeFileSync(securityDetailedMdPath, formatSecurityDetailedMarkdown(securityDetailed, elapsed));
475
645
  if (values.detailed) {
476
646
  logger.success(`Detailed report saved to ${path.relative(targetPath, securityDetailedMdPath)}`);
477
647
  logger.success(`Detailed JSON saved to ${path.relative(targetPath, securityDetailedJsonPath)}`);
478
648
  }
479
649
  }
650
+ // --fail-on-severity: applies to both code findings and
651
+ // dependency advisories. Code findings fire first because
652
+ // they're typically actionable (a SAST hit you wrote);
653
+ // dependency advisories second (transitive issue you may need
654
+ // to triage).
655
+ applyFailOnSeverity(values['fail-on-severity'], report.summary.findings, 'vulnerabilities (code)');
656
+ applyFailOnSeverity(values['fail-on-severity'], report.summary.dependencies, 'vulnerabilities (dependencies)');
480
657
  break;
481
658
  }
482
659
  case 'test-gaps': {
@@ -510,7 +687,7 @@ async function run(argv) {
510
687
  const report = await analyzeTestGaps(targetPath, { verbose: !!values.verbose });
511
688
  const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
512
689
  if (values.json) {
513
- await emitJson(report);
690
+ await emitJson((0, report_schema_1.stampSchema)(report, 'test-gaps'));
514
691
  }
515
692
  else {
516
693
  const s = report.summary;
@@ -540,13 +717,18 @@ async function run(argv) {
540
717
  const testGapsDetailed = buildTestGapsDetailed(report);
541
718
  const testGapsDetailedJsonPath = path.join(reportDir, `test-gaps-${date}-detailed.json`);
542
719
  const testGapsDetailedMdPath = path.join(reportDir, `test-gaps-${date}-detailed.md`);
543
- fs.writeFileSync(testGapsDetailedJsonPath, JSON.stringify(testGapsDetailed, null, 2));
720
+ fs.writeFileSync(testGapsDetailedJsonPath, JSON.stringify((0, report_schema_1.stampSchema)(testGapsDetailed, 'test-gaps-detailed'), null, 2));
544
721
  fs.writeFileSync(testGapsDetailedMdPath, formatTestGapsDetailedMarkdown(testGapsDetailed, elapsed));
545
722
  if (values.detailed) {
546
723
  logger.success(`Detailed report saved to ${path.relative(targetPath, testGapsDetailedMdPath)}`);
547
724
  logger.success(`Detailed JSON saved to ${path.relative(targetPath, testGapsDetailedJsonPath)}`);
548
725
  }
549
726
  }
727
+ // --fail-on-score: applies to the headline effectiveCoverage
728
+ // percentage. Tests-gap reports use a higher-is-better
729
+ // coverage scale, so the same threshold semantics work as
730
+ // for the health overall score.
731
+ applyFailOnScore(values['fail-on-score'], report.summary.effectiveCoverage, 'test-gaps effectiveCoverage');
550
732
  break;
551
733
  }
552
734
  case 'quality': {
@@ -561,7 +743,7 @@ async function run(argv) {
561
743
  });
562
744
  const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
563
745
  if (values.json) {
564
- await emitJson(report);
746
+ await emitJson((0, report_schema_1.stampSchema)(report, 'quality'));
565
747
  }
566
748
  else {
567
749
  const m = report.metrics;
@@ -633,7 +815,7 @@ async function run(argv) {
633
815
  });
634
816
  const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
635
817
  if (values.json) {
636
- await emitJson(report);
818
+ await emitJson((0, report_schema_1.stampSchema)(report, 'dev-report'));
637
819
  }
638
820
  else {
639
821
  const s = report.summary;
@@ -690,7 +872,7 @@ async function run(argv) {
690
872
  const report = await analyzeLicenses(targetPath, { verbose: !!values.verbose });
691
873
  const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
692
874
  if (values.json) {
693
- await emitJson(report); // slop-ok
875
+ await emitJson((0, report_schema_1.stampSchema)(report, 'licenses')); // slop-ok
694
876
  }
695
877
  else {
696
878
  const s = report.summary;
@@ -766,7 +948,7 @@ async function run(argv) {
766
948
  });
767
949
  const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
768
950
  if (values.json) {
769
- await emitJson(report); // slop-ok
951
+ await emitJson((0, report_schema_1.stampSchema)(report, 'bom')); // slop-ok
770
952
  }
771
953
  else {
772
954
  const s = report.summary;
@@ -835,6 +1017,12 @@ async function run(argv) {
835
1017
  logger.success(`XLSX saved to ${path.relative(targetPath, xlsxPath)}`);
836
1018
  }
837
1019
  }
1020
+ // --fail-on-severity: BomReport.summary.bySeverity carries
1021
+ // per-package max-severity counts. A package with multiple
1022
+ // advisories is counted once at its highest severity, which
1023
+ // is what a "block at this tier" gate wants — not double
1024
+ // counting.
1025
+ applyFailOnSeverity(values['fail-on-severity'], report.summary.bySeverity, 'bom severity');
838
1026
  break;
839
1027
  }
840
1028
  case 'dashboard': {
@@ -1118,6 +1306,130 @@ async function run(argv) {
1118
1306
  logger.dim(`Converted in ${elapsed}s · report kind: ${kind}`);
1119
1307
  break;
1120
1308
  }
1309
+ case 'hooks': {
1310
+ const subCommand = positionals[1];
1311
+ if (subCommand === 'activate') {
1312
+ const targetPath = resolveRepoPath(positionals[2]);
1313
+ const { runHooksActivate } = await Promise.resolve().then(() => __importStar(require('./hooks-cli')));
1314
+ runHooksActivate(targetPath);
1315
+ break;
1316
+ }
1317
+ logger.fail(`Unknown hooks subcommand: ${subCommand ?? '(missing)'}. ` +
1318
+ `Available: vyuh-dxkit hooks activate [path]`);
1319
+ process.exit(1);
1320
+ break;
1321
+ }
1322
+ case 'baseline': {
1323
+ const subCommand = positionals[1];
1324
+ if (subCommand === 'create') {
1325
+ const targetPath = resolveRepoPath(positionals[2]);
1326
+ const { createBaseline } = await Promise.resolve().then(() => __importStar(require('./baseline/create')));
1327
+ logger.header('vyuh-dxkit baseline create');
1328
+ logger.info(`Capturing baseline for ${targetPath}...`);
1329
+ const startTime = Date.now();
1330
+ try {
1331
+ const result = await createBaseline({
1332
+ cwd: targetPath,
1333
+ name: values.name,
1334
+ force: !!values.force,
1335
+ verbose: !!values.verbose,
1336
+ });
1337
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
1338
+ const rel = path.relative(targetPath, result.path);
1339
+ logger.success(`Wrote ${rel} — ${result.file.findings.length} findings, salt: ${result.file.saltMode} (${elapsed}s)`);
1340
+ }
1341
+ catch (err) {
1342
+ logger.fail(err.message);
1343
+ process.exit(1);
1344
+ }
1345
+ break;
1346
+ }
1347
+ if (subCommand === 'show') {
1348
+ const targetPath = resolveRepoPath(positionals[2]);
1349
+ const { DEFAULT_BASELINE_NAME, pathForBaseline, readBaselineFile } = await Promise.resolve().then(() => __importStar(require('./baseline/baseline-file')));
1350
+ const { parseKindFilter, renderSummary, renderKind, renderJson, FILTER_KINDS } = await Promise.resolve().then(() => __importStar(require('./baseline/show')));
1351
+ const name = values.name ?? DEFAULT_BASELINE_NAME;
1352
+ const filePath = values.baseline ?? pathForBaseline(targetPath, name);
1353
+ let file;
1354
+ try {
1355
+ file = readBaselineFile(filePath);
1356
+ }
1357
+ catch (err) {
1358
+ logger.fail(err.message);
1359
+ process.exit(1);
1360
+ }
1361
+ // Optional kind filter. Validated up-front so a typo surfaces
1362
+ // a clear error rather than a silently-empty result.
1363
+ let kindFilter;
1364
+ if (values.kind !== undefined) {
1365
+ const parsed = parseKindFilter(values.kind);
1366
+ if (parsed === null) {
1367
+ logger.fail(`--kind: unknown value "${values.kind}". Expected one of: ${FILTER_KINDS.join(', ')}.`);
1368
+ process.exit(1);
1369
+ }
1370
+ kindFilter = parsed;
1371
+ }
1372
+ if (values.json) {
1373
+ await emitJson(renderJson(file, kindFilter ? { kind: kindFilter } : {}));
1374
+ }
1375
+ else if (kindFilter) {
1376
+ process.stdout.write(renderKind(file, kindFilter) + '\n');
1377
+ }
1378
+ else {
1379
+ process.stdout.write(renderSummary(file) + '\n');
1380
+ }
1381
+ break;
1382
+ }
1383
+ logger.fail(`Unknown baseline subcommand: ${subCommand ?? '(missing)'}. ` +
1384
+ `Available: vyuh-dxkit baseline create [path] [--name <name>] [--force] · ` +
1385
+ `vyuh-dxkit baseline show [path] [--name <name>] [--baseline <path>] [--kind <kind>] [--json]`);
1386
+ process.exit(1);
1387
+ break;
1388
+ }
1389
+ case 'guardrail': {
1390
+ const subCommand = positionals[1];
1391
+ if (subCommand !== 'check') {
1392
+ logger.fail(`Unknown guardrail subcommand: ${subCommand ?? '(missing)'}. ` +
1393
+ `Available: vyuh-dxkit guardrail check [path] [--name <n>] [--baseline <path>] ` +
1394
+ `[--changed-only] [--policy <path>] [--json | --markdown]`);
1395
+ process.exit(1);
1396
+ }
1397
+ const targetPath = resolveRepoPath(positionals[2]);
1398
+ const { runGuardrailCheck } = await Promise.resolve().then(() => __importStar(require('./baseline/check')));
1399
+ const { renderConsole, renderJson, renderMarkdown } = await Promise.resolve().then(() => __importStar(require('./baseline/check-renderers')));
1400
+ if (!values.json)
1401
+ logger.header('vyuh-dxkit guardrail check');
1402
+ if (!values.json)
1403
+ logger.info(`Checking ${targetPath} against baseline...`);
1404
+ const startTime = Date.now();
1405
+ try {
1406
+ const result = await runGuardrailCheck({
1407
+ cwd: targetPath,
1408
+ name: values.name,
1409
+ baselinePath: values.baseline,
1410
+ changedOnly: !!values['changed-only'],
1411
+ policyPath: values.policy,
1412
+ verbose: !!values.verbose,
1413
+ });
1414
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
1415
+ if (values.json) {
1416
+ await emitJson(renderJson(result));
1417
+ }
1418
+ else if (values.markdown) {
1419
+ process.stdout.write(renderMarkdown(result) + '\n');
1420
+ }
1421
+ else {
1422
+ process.stdout.write(renderConsole(result) + '\n');
1423
+ logger.dim(`Completed in ${elapsed}s`);
1424
+ }
1425
+ process.exit(result.blocks ? 1 : 0);
1426
+ }
1427
+ catch (err) {
1428
+ logger.fail(err.message);
1429
+ process.exit(1);
1430
+ }
1431
+ break;
1432
+ }
1121
1433
  default:
1122
1434
  console.error(`Unknown command: ${command}`);
1123
1435
  printUsage();
@@ -1184,7 +1496,9 @@ function formatMarkdownReport(report, elapsed) {
1184
1496
  lines.push('');
1185
1497
  lines.push('| Rank | File | Lines |');
1186
1498
  lines.push('|-----:|------|------:|');
1187
- report.largestFiles.forEach((f, i) => {
1499
+ // Top 10 is the render contract — the underlying metric carries
1500
+ // every file over the threshold (consumed by the baseline producer).
1501
+ report.largestFiles.slice(0, 10).forEach((f, i) => {
1188
1502
  lines.push(`| ${i + 1} | \`${f.path}\` | ${f.lines.toLocaleString()} |`);
1189
1503
  });
1190
1504
  lines.push('');
@@ -1196,7 +1510,9 @@ function formatMarkdownReport(report, elapsed) {
1196
1510
  // etc.; the remaining cases (most commonly `/libs/`) live in
1197
1511
  // customer-specific paths that can't be defaulted-away without
1198
1512
  // false-positives on first-party monorepo layouts.
1199
- const suspects = (0, vendored_advisor_1.suspectVendoredEntries)(report.largestFiles);
1513
+ // Scope the vendored advisor to the rendered top 10 — the tip
1514
+ // calls out files the user can see in the table above.
1515
+ const suspects = (0, vendored_advisor_1.suspectVendoredEntries)(report.largestFiles.slice(0, 10));
1200
1516
  if (suspects.length > 0) {
1201
1517
  lines.push(`> **Tip — possibly vendored:** ${suspects
1202
1518
  .map((s) => `\`${s.path}\``)