@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,143 @@
1
+ "use strict";
2
+ /**
3
+ * Inline allowlist annotation gather pass.
4
+ *
5
+ * Walks the source tree looking for `<lineComment> dxkit-allow:<category>`
6
+ * comments and records each occurrence as a `(file, line, category)`
7
+ * tuple. The `stale-allow` producer uses this list together with the
8
+ * current scan's secret/code/config findings to detect orphaned
9
+ * annotations — annotations whose underlying finding is no longer
10
+ * present, which the developer should remove.
11
+ *
12
+ * Architectural posture:
13
+ *
14
+ * - File walk goes through the canonical `walkSourceFiles` helper
15
+ * so `.gitignore` + `.dxkit-ignore` + bundled defaults are
16
+ * honored uniformly. No custom recursion / exclusion logic
17
+ * (per CLAUDE.md G_v4_7).
18
+ * - Per-language comment marker comes from each pack's
19
+ * `LanguageSupport.commentSyntax` via the inline annotation
20
+ * parser. No hardcoded `'//'` / `'#'` literals in this module
21
+ * (arch-check rule 2 enforces).
22
+ * - Annotation parsing reuses `parseAnnotation` from `inline.ts`
23
+ * — single source of grammar truth.
24
+ *
25
+ * Test files ARE walked (intentional — annotations often live in
26
+ * test fixtures suppressing scanner findings against deliberate
27
+ * placeholder credentials). Auto-generated files are NOT walked
28
+ * (the developer doesn't author annotations in generated code, and
29
+ * `walkSourceFiles` already excludes them by default).
30
+ */
31
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
32
+ if (k2 === undefined) k2 = k;
33
+ var desc = Object.getOwnPropertyDescriptor(m, k);
34
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
35
+ desc = { enumerable: true, get: function() { return m[k]; } };
36
+ }
37
+ Object.defineProperty(o, k2, desc);
38
+ }) : (function(o, m, k, k2) {
39
+ if (k2 === undefined) k2 = k;
40
+ o[k2] = m[k];
41
+ }));
42
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
43
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
44
+ }) : function(o, v) {
45
+ o["default"] = v;
46
+ });
47
+ var __importStar = (this && this.__importStar) || (function () {
48
+ var ownKeys = function(o) {
49
+ ownKeys = Object.getOwnPropertyNames || function (o) {
50
+ var ar = [];
51
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
52
+ return ar;
53
+ };
54
+ return ownKeys(o);
55
+ };
56
+ return function (mod) {
57
+ if (mod && mod.__esModule) return mod;
58
+ var result = {};
59
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
60
+ __setModuleDefault(result, mod);
61
+ return result;
62
+ };
63
+ })();
64
+ Object.defineProperty(exports, "__esModule", { value: true });
65
+ exports.gatherInlineAllowlistAnnotations = gatherInlineAllowlistAnnotations;
66
+ const fs = __importStar(require("fs"));
67
+ const path = __importStar(require("path"));
68
+ const languages_1 = require("../languages");
69
+ const walk_source_files_1 = require("../analyzers/tools/walk-source-files");
70
+ const inline_1 = require("./inline");
71
+ /**
72
+ * Walk source files under `cwd` and collect every inline allowlist
73
+ * annotation occurrence. Cheap on small repos; on large ones the
74
+ * `walkSourceFiles` cache amortizes the cost across multiple gather
75
+ * passes within a single baseline-create run.
76
+ *
77
+ * Returns occurrences in stable order (file path lexicographic, then
78
+ * line ascending) so downstream deterministic-output requirements
79
+ * are satisfied automatically.
80
+ */
81
+ function gatherInlineAllowlistAnnotations(cwd, opts = {}) {
82
+ const includeTests = opts.includeTests ?? true;
83
+ const files = (0, walk_source_files_1.walkSourceFiles)(cwd, { includeTests });
84
+ const out = [];
85
+ // Build an extension → language lookup once per call. Cheap; the
86
+ // LANGUAGES registry is small (8 packs today).
87
+ const langByExt = buildLanguageByExtension();
88
+ for (const relPath of files) {
89
+ const ext = path.extname(relPath).toLowerCase();
90
+ const lang = langByExt.get(ext);
91
+ if (!lang || !lang.commentSyntax)
92
+ continue;
93
+ const abs = path.join(cwd, relPath);
94
+ let raw;
95
+ try {
96
+ raw = fs.readFileSync(abs, 'utf8');
97
+ }
98
+ catch {
99
+ // File disappeared mid-walk (race) or unreadable — skip.
100
+ continue;
101
+ }
102
+ // Fast path: source has no `dxkit-allow:` substring at all.
103
+ // Avoids per-line regex on the vast majority of files.
104
+ if (!raw.includes('dxkit-allow:'))
105
+ continue;
106
+ const lines = raw.split(/\r?\n/);
107
+ for (let i = 0; i < lines.length; i++) {
108
+ const line = lines[i];
109
+ const parsed = (0, inline_1.parseAnnotation)(line, lang);
110
+ if (!parsed)
111
+ continue;
112
+ // `parseAnnotation` returns the category + reason but not
113
+ // the position. Determine position by inspecting whether the
114
+ // line is standalone (only whitespace + comment marker + body)
115
+ // or has source code preceding the comment.
116
+ const position = (0, inline_1.isStandaloneAnnotationLine)(line, lang)
117
+ ? 'above'
118
+ : 'same-line';
119
+ out.push({
120
+ file: relPath,
121
+ line: i + 1,
122
+ category: parsed.category,
123
+ position,
124
+ });
125
+ }
126
+ }
127
+ return out;
128
+ }
129
+ // ─── Internals ────────────────────────────────────────────────────────────
130
+ function buildLanguageByExtension() {
131
+ const map = new Map();
132
+ for (const lang of languages_1.LANGUAGES) {
133
+ for (const ext of lang.sourceExtensions) {
134
+ const lower = ext.toLowerCase();
135
+ // First pack wins on duplicate extensions (none today, but
136
+ // the deterministic-order guarantee survives a future overlap).
137
+ if (!map.has(lower))
138
+ map.set(lower, lang);
139
+ }
140
+ }
141
+ return map;
142
+ }
143
+ //# sourceMappingURL=gather.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gather.js","sourceRoot":"","sources":["../../src/allowlist/gather.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCH,4EAkDC;AAxFD,uCAAyB;AACzB,2CAA6B;AAC7B,4CAAyC;AAEzC,4EAAuE;AACvE,qCAAgG;AAuBhG;;;;;;;;;GASG;AACH,SAAgB,gCAAgC,CAC9C,GAAW,EACX,OAAyB,EAAE;IAE3B,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC;IAC/C,MAAM,KAAK,GAAG,IAAA,mCAAe,EAAC,GAAG,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;IACrD,MAAM,GAAG,GAAgC,EAAE,CAAC;IAE5C,iEAAiE;IACjE,+CAA+C;IAC/C,MAAM,SAAS,GAAG,wBAAwB,EAAE,CAAC;IAE7C,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QAChD,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa;YAAE,SAAS;QAE3C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACpC,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACrC,CAAC;QAAC,MAAM,CAAC;YACP,yDAAyD;YACzD,SAAS;QACX,CAAC;QACD,4DAA4D;QAC5D,uDAAuD;QACvD,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC;YAAE,SAAS;QAE5C,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACtB,MAAM,MAAM,GAAG,IAAA,wBAAe,EAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC3C,IAAI,CAAC,MAAM;gBAAE,SAAS;YACtB,0DAA0D;YAC1D,6DAA6D;YAC7D,+DAA+D;YAC/D,4CAA4C;YAC5C,MAAM,QAAQ,GAAuB,IAAA,mCAA0B,EAAC,IAAI,EAAE,IAAI,CAAC;gBACzE,CAAC,CAAC,OAAO;gBACT,CAAC,CAAC,WAAW,CAAC;YAChB,GAAG,CAAC,IAAI,CAAC;gBACP,IAAI,EAAE,OAAO;gBACb,IAAI,EAAE,CAAC,GAAG,CAAC;gBACX,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,QAAQ;aACT,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,6EAA6E;AAE7E,SAAS,wBAAwB;IAC/B,MAAM,GAAG,GAAG,IAAI,GAAG,EAA2B,CAAC;IAC/C,KAAK,MAAM,IAAI,IAAI,qBAAS,EAAE,CAAC;QAC7B,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACxC,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;YAChC,2DAA2D;YAC3D,gEAAgE;YAChE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Block-time hint generation.
3
+ *
4
+ * When the guardrail check rejects a finding, the developer needs
5
+ * three things to act on it:
6
+ *
7
+ * 1. **Remediation** — the recommended fix (rotate the secret,
8
+ * upgrade the package, split the file, etc.). Generic per-kind
9
+ * prose; the conversational `dxkit-fix` skill produces
10
+ * LLM-backed, code-aware fixes when available.
11
+ * 2. **Inline example** — the exact annotation comment to paste
12
+ * when the finding has a stable single-line attachment point
13
+ * and the chosen category is inline-compatible.
14
+ * 3. **CLI command** — the exact `npx vyuh-dxkit allowlist add`
15
+ * invocation that handles the mutation without the developer
16
+ * typing annotation syntax.
17
+ *
18
+ * # Canonical input shape (CLAUDE.md Rule 9)
19
+ *
20
+ * This module consumes `BaselineEntry` directly — the canonical
21
+ * per-kind discriminated union from `src/baseline/types.ts`. NO
22
+ * intermediate "BlockingFinding" projection. The discriminated
23
+ * union flows into the switch statements below so TypeScript's
24
+ * exhaustiveness check fails the build when a new `IdentityKind`
25
+ * variant is added without matching `case` branches here.
26
+ *
27
+ * Language is inferred from the entry's file path (when present)
28
+ * via the canonical `LANGUAGES` registry. No language-specific
29
+ * branches; pack additions auto-propagate.
30
+ *
31
+ * The returned `BlockHint` is a plain data object so callers can
32
+ * render it as terminal output, emit it as JSON for the skill /
33
+ * future MCP surface, etc. Rendering text lives at the call site,
34
+ * NOT here.
35
+ */
36
+ import type { BaselineEntry, FindingSeverity } from '../baseline/types';
37
+ import { type AllowlistCategory } from './categories';
38
+ export interface BlockHint {
39
+ /** Generic per-kind remediation text. Always present. */
40
+ readonly remediation: string;
41
+ /** Categories that semantically apply to this kind. Empty for
42
+ * `license` (which drops out of the baseline producer registry
43
+ * in 2.6+). */
44
+ readonly applicableCategories: readonly AllowlistCategory[];
45
+ /** Inline annotation example. Populated only when the entry has
46
+ * a stable single-line attachment, the kind's first applicable
47
+ * category is inline-compatible, and the language is inferable
48
+ * from the file extension. */
49
+ readonly inlineExample?: string;
50
+ /** Shell command for the CLI write path. Always present. */
51
+ readonly cliCommand: string;
52
+ /** True when the kind has no inline-compatible category
53
+ * applicable (e.g. hygiene — only accepted-risk + deferred,
54
+ * both file-only). The CLI / skill routes the dev directly to
55
+ * the file-level surface when true. */
56
+ readonly fileLevelOnly: boolean;
57
+ /** Pointer to the file-level allowlist surface when an expiring
58
+ * category (accepted-risk / deferred) might apply. */
59
+ readonly fileLevelHint?: string;
60
+ }
61
+ /**
62
+ * Build the structured block-time hint from a baseline entry.
63
+ * Pure function — no IO, no side effects. The caller renders the
64
+ * fields into its medium of choice.
65
+ *
66
+ * The optional `severity` parameter is reserved for future
67
+ * remediation-prose tuning (e.g. "rotate IMMEDIATELY" for
68
+ * critical-severity secrets). Currently unused but threaded
69
+ * through so callers don't need to refactor when the prose
70
+ * starts varying on severity.
71
+ */
72
+ export declare function formatBlockHint(entry: BaselineEntry, severity?: FindingSeverity): BlockHint;
73
+ /**
74
+ * Generic per-kind remediation text. Exhaustive switch on the
75
+ * canonical `BaselineEntry['kind']` union — TypeScript enforces
76
+ * every variant has prose so a new kind can't ship without
77
+ * matching `case` branches.
78
+ */
79
+ export declare function remediationFor(kind: BaselineEntry['kind']): string;
80
+ //# sourceMappingURL=hint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hint.d.ts","sourceRoot":"","sources":["../../src/allowlist/hint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGxE,OAAO,EAKL,KAAK,iBAAiB,EACvB,MAAM,cAAc,CAAC;AAStB,MAAM,WAAW,SAAS;IACxB,yDAAyD;IACzD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;oBAEgB;IAChB,QAAQ,CAAC,oBAAoB,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAC5D;;;mCAG+B;IAC/B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,4DAA4D;IAC5D,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;;4CAGwC;IACxC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;2DACuD;IACvD,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,eAAe,GAAG,SAAS,CAyB3F;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,MAAM,CAsFlE"}
@@ -0,0 +1,271 @@
1
+ "use strict";
2
+ /**
3
+ * Block-time hint generation.
4
+ *
5
+ * When the guardrail check rejects a finding, the developer needs
6
+ * three things to act on it:
7
+ *
8
+ * 1. **Remediation** — the recommended fix (rotate the secret,
9
+ * upgrade the package, split the file, etc.). Generic per-kind
10
+ * prose; the conversational `dxkit-fix` skill produces
11
+ * LLM-backed, code-aware fixes when available.
12
+ * 2. **Inline example** — the exact annotation comment to paste
13
+ * when the finding has a stable single-line attachment point
14
+ * and the chosen category is inline-compatible.
15
+ * 3. **CLI command** — the exact `npx vyuh-dxkit allowlist add`
16
+ * invocation that handles the mutation without the developer
17
+ * typing annotation syntax.
18
+ *
19
+ * # Canonical input shape (CLAUDE.md Rule 9)
20
+ *
21
+ * This module consumes `BaselineEntry` directly — the canonical
22
+ * per-kind discriminated union from `src/baseline/types.ts`. NO
23
+ * intermediate "BlockingFinding" projection. The discriminated
24
+ * union flows into the switch statements below so TypeScript's
25
+ * exhaustiveness check fails the build when a new `IdentityKind`
26
+ * variant is added without matching `case` branches here.
27
+ *
28
+ * Language is inferred from the entry's file path (when present)
29
+ * via the canonical `LANGUAGES` registry. No language-specific
30
+ * branches; pack additions auto-propagate.
31
+ *
32
+ * The returned `BlockHint` is a plain data object so callers can
33
+ * render it as terminal output, emit it as JSON for the skill /
34
+ * future MCP surface, etc. Rendering text lives at the call site,
35
+ * NOT here.
36
+ */
37
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
38
+ if (k2 === undefined) k2 = k;
39
+ var desc = Object.getOwnPropertyDescriptor(m, k);
40
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
41
+ desc = { enumerable: true, get: function() { return m[k]; } };
42
+ }
43
+ Object.defineProperty(o, k2, desc);
44
+ }) : (function(o, m, k, k2) {
45
+ if (k2 === undefined) k2 = k;
46
+ o[k2] = m[k];
47
+ }));
48
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
49
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
50
+ }) : function(o, v) {
51
+ o["default"] = v;
52
+ });
53
+ var __importStar = (this && this.__importStar) || (function () {
54
+ var ownKeys = function(o) {
55
+ ownKeys = Object.getOwnPropertyNames || function (o) {
56
+ var ar = [];
57
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
58
+ return ar;
59
+ };
60
+ return ownKeys(o);
61
+ };
62
+ return function (mod) {
63
+ if (mod && mod.__esModule) return mod;
64
+ var result = {};
65
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
66
+ __setModuleDefault(result, mod);
67
+ return result;
68
+ };
69
+ })();
70
+ Object.defineProperty(exports, "__esModule", { value: true });
71
+ exports.formatBlockHint = formatBlockHint;
72
+ exports.remediationFor = remediationFor;
73
+ const path = __importStar(require("path"));
74
+ const sanitize_1 = require("../baseline/sanitize");
75
+ const languages_1 = require("../languages");
76
+ const categories_1 = require("./categories");
77
+ const inline_1 = require("./inline");
78
+ /**
79
+ * Subcommand string used in every `cliCommand` rendered by this
80
+ * module. One place to update if the subcommand ever renames.
81
+ */
82
+ const ALLOWLIST_ADD_CMD = 'npx vyuh-dxkit allowlist add';
83
+ /**
84
+ * Build the structured block-time hint from a baseline entry.
85
+ * Pure function — no IO, no side effects. The caller renders the
86
+ * fields into its medium of choice.
87
+ *
88
+ * The optional `severity` parameter is reserved for future
89
+ * remediation-prose tuning (e.g. "rotate IMMEDIATELY" for
90
+ * critical-severity secrets). Currently unused but threaded
91
+ * through so callers don't need to refactor when the prose
92
+ * starts varying on severity.
93
+ */
94
+ function formatBlockHint(entry, severity) {
95
+ const applicableCategories = categories_1.CATEGORIES_BY_KIND[entry.kind];
96
+ const inlineCompatibleApplicable = applicableCategories.filter((c) => categories_1.INLINE_COMPATIBLE_CATEGORIES.has(c));
97
+ const fileLevelOnly = !categories_1.INLINE_COMPATIBLE_KINDS.has(entry.kind) || inlineCompatibleApplicable.length === 0;
98
+ const firstInlineCategory = inlineCompatibleApplicable[0];
99
+ const inlineExample = buildInlineExample(entry, firstInlineCategory);
100
+ const cliCommand = buildCliCommand(entry, applicableCategories);
101
+ const fileLevelHint = buildFileLevelHint(applicableCategories);
102
+ // Severity threaded for future use; not consumed yet — declare it
103
+ // touched so an unused-parameter rule doesn't complain.
104
+ void severity;
105
+ return {
106
+ remediation: remediationFor(entry.kind),
107
+ applicableCategories,
108
+ inlineExample,
109
+ cliCommand,
110
+ fileLevelOnly,
111
+ fileLevelHint,
112
+ };
113
+ }
114
+ /**
115
+ * Generic per-kind remediation text. Exhaustive switch on the
116
+ * canonical `BaselineEntry['kind']` union — TypeScript enforces
117
+ * every variant has prose so a new kind can't ship without
118
+ * matching `case` branches.
119
+ */
120
+ function remediationFor(kind) {
121
+ switch (kind) {
122
+ case 'secret':
123
+ case 'secret-hmac':
124
+ return ('Rotate this credential immediately and load it from an environment variable ' +
125
+ 'or secret manager instead of source. Do not commit the replacement to git ' +
126
+ 'history — clean previous commits with git-filter-repo if necessary.');
127
+ case 'code':
128
+ return ('Review the flagged code pattern. If the scanner is wrong (false positive), ' +
129
+ 'suppress via the allowlist with category=false-positive. Otherwise fix the ' +
130
+ 'underlying issue — the scanner caught it for a reason.');
131
+ case 'config':
132
+ return ('Review the configuration setting. Many config-level findings ' +
133
+ '(TLS validation disabled, debug mode in production, etc.) reflect ' +
134
+ 'operational risk; fix at the deployment / infrastructure layer where ' +
135
+ 'possible rather than in source.');
136
+ case 'dep-vuln':
137
+ return ('Upgrade the vulnerable dependency to the patched version. Run ' +
138
+ '`npx vyuh-dxkit vulnerabilities` to see the suggested install command ' +
139
+ 'for this ecosystem.');
140
+ case 'duplication':
141
+ return ('Extract the duplicated logic into a shared helper or accept the ' +
142
+ 'duplication via the allowlist when it is intentional ' +
143
+ '(e.g., parallel test fixtures, generated code that must stay literal).');
144
+ case 'coverage-gap':
145
+ return ('Add a test covering the uncovered region. If the code path is ' +
146
+ 'intentionally untested (build-time only, defensive error paths that ' +
147
+ 'cannot be reached from tests), accept the gap via the allowlist.');
148
+ case 'test-gap':
149
+ return ('Create a test file for this source file. The convention follows the ' +
150
+ "language's standard (e.g., `*_test.go` for go, `*.test.ts` for " +
151
+ 'typescript, `test_*.py` for python).');
152
+ case 'test-file-degradation':
153
+ return ('Restore the test body. Empty or fully commented-out test functions pass ' +
154
+ 'silently in the test runner but provide no real coverage signal — they ' +
155
+ 'are worse than no test because they hide gaps from view.');
156
+ case 'hygiene':
157
+ return ('Resolve the hygiene marker (TODO, FIXME, HACK, debug-print, or loose ' +
158
+ '`any` type). Complete the work it points at, file it as a tracked issue ' +
159
+ 'in your task management system, or remove the marker if the underlying ' +
160
+ 'concern is already addressed.');
161
+ case 'god-file':
162
+ return ('Split this file. It has grown beyond the maintainability threshold for ' +
163
+ 'this codebase — large files concentrate change risk and make focused ' +
164
+ 'review difficult. Extract cohesive subsets (related functions, related ' +
165
+ 'types) into separate modules.');
166
+ case 'large-file':
167
+ return ('Split this file into smaller modules. Long files concentrate ' +
168
+ 'cognitive load and make change risk harder to assess; smaller ' +
169
+ 'modules with clear boundaries are easier to maintain.');
170
+ case 'stale-file':
171
+ return ('Remove the stale on-disk artifact (typically `.swp`, `.bak`, `.orig`, ' +
172
+ 'or editor backup files). These should not be tracked in git — add the ' +
173
+ 'pattern to `.gitignore` and untrack the file.');
174
+ case 'stale-allow':
175
+ return ('Remove the orphaned `dxkit-allow:` annotation — the finding it ' +
176
+ 'suppressed is no longer present, so the annotation is dead code. ' +
177
+ 'Allowlisting THIS finding is not supported; the only remediation is ' +
178
+ 'to delete the annotation comment.');
179
+ }
180
+ }
181
+ // ─── Internals ───────────────────────────────────────────────────────────
182
+ /**
183
+ * Project a `BaselineEntry` to the file + line locator the hint
184
+ * formatter needs. Exhaustive switch on entry.kind — TypeScript
185
+ * enforces every variant is handled. Adding a new kind requires
186
+ * extending this projection.
187
+ *
188
+ * Kinds without a stable file:line locator (`dep-vuln`, `duplication`,
189
+ * `secret-hmac`) return `{}` and route to the `--fingerprint=<id>`
190
+ * CLI form. Sanitized entries also return `{}` — the location was
191
+ * stripped at write time and can only be addressed by fingerprint.
192
+ */
193
+ function entryLocator(entry) {
194
+ if ((0, sanitize_1.isSanitized)(entry))
195
+ return {};
196
+ switch (entry.kind) {
197
+ case 'secret':
198
+ case 'code':
199
+ case 'config':
200
+ case 'hygiene':
201
+ case 'stale-allow':
202
+ return { file: entry.file, line: entry.line };
203
+ case 'coverage-gap':
204
+ case 'test-gap':
205
+ case 'test-file-degradation':
206
+ case 'god-file':
207
+ case 'large-file':
208
+ case 'stale-file':
209
+ return { file: entry.file };
210
+ case 'secret-hmac':
211
+ case 'dep-vuln':
212
+ case 'duplication':
213
+ return {};
214
+ }
215
+ }
216
+ /**
217
+ * Look up the language pack matching a file's extension. Reads
218
+ * from the canonical `LANGUAGES` registry — no per-language
219
+ * branching here. A new language pack auto-propagates.
220
+ */
221
+ function inferLanguage(file) {
222
+ const ext = path.extname(file).toLowerCase();
223
+ if (!ext)
224
+ return undefined;
225
+ for (const lang of languages_1.LANGUAGES) {
226
+ if (lang.sourceExtensions.includes(ext))
227
+ return lang;
228
+ }
229
+ return undefined;
230
+ }
231
+ function buildInlineExample(entry, firstInlineCategory) {
232
+ if (!firstInlineCategory)
233
+ return undefined;
234
+ if (!categories_1.INLINE_COMPATIBLE_KINDS.has(entry.kind))
235
+ return undefined;
236
+ const loc = entryLocator(entry);
237
+ if (!loc.file || loc.line === undefined)
238
+ return undefined;
239
+ const lang = inferLanguage(loc.file);
240
+ if (!lang || !lang.commentSyntax)
241
+ return undefined;
242
+ return (0, inline_1.renderAnnotation)({ category: firstInlineCategory, reason: '<your reason here>' }, lang);
243
+ }
244
+ function buildCliCommand(entry, applicableCategories) {
245
+ const firstCategory = applicableCategories[0];
246
+ const reasonArg = `--reason="<rationale here>"`;
247
+ const categoryArg = firstCategory ? `--category=${firstCategory}` : '--category=<category>';
248
+ const loc = entryLocator(entry);
249
+ // Two CLI forms, matching the two paths the `allowlist add` command
250
+ // accepts. Both are directly executable — no inferred missing args.
251
+ //
252
+ // 1. `<file>:<line>` — inline annotation insertion (kind-agnostic,
253
+ // grammar-driven). Only chosen when the kind supports inline
254
+ // attachment AND the entry carries file + line.
255
+ // 2. `--fingerprint=<id> --kind=<kind>` — file-level allowlist
256
+ // entry. The fingerprint is the identity; the kind is needed
257
+ // so the validator can apply per-kind rules.
258
+ if (categories_1.INLINE_COMPATIBLE_KINDS.has(entry.kind) && loc.file && loc.line !== undefined) {
259
+ return `${ALLOWLIST_ADD_CMD} ${loc.file}:${loc.line} ${categoryArg} ${reasonArg}`;
260
+ }
261
+ return `${ALLOWLIST_ADD_CMD} --fingerprint=${entry.id} --kind=${entry.kind} ${categoryArg} ${reasonArg}`;
262
+ }
263
+ function buildFileLevelHint(applicableCategories) {
264
+ const hasExpiringCategory = applicableCategories.some((c) => categories_1.EXPIRING_CATEGORIES.has(c));
265
+ if (!hasExpiringCategory)
266
+ return undefined;
267
+ return ('For accepted-risk or deferred suppression (both require an expiry date), ' +
268
+ 'use --category=accepted-risk or --category=deferred with --expires=YYYY-MM-DD, ' +
269
+ 'or edit .dxkit/allowlist.json directly.');
270
+ }
271
+ //# sourceMappingURL=hint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hint.js","sourceRoot":"","sources":["../../src/allowlist/hint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Inline allowlist annotations.
3
+ *
4
+ * Grammar:
5
+ *
6
+ * <lineComment> dxkit-allow:<category> reason="<text>"
7
+ *
8
+ * Examples:
9
+ *
10
+ * # dxkit-allow:test-fixture reason="placeholder in unit test"
11
+ * // dxkit-allow:false-positive reason="regex matches our intentional placeholder"
12
+ *
13
+ * Two positions:
14
+ *
15
+ * - **Same-line** — annotation appears on the same source line as
16
+ * the finding, separated by at least one space:
17
+ *
18
+ * api_key = "sk_test_xxxx" # dxkit-allow:test-fixture reason="..."
19
+ *
20
+ * - **Above-line** — annotation occupies its own line, immediately
21
+ * preceding the finding (matching its indentation):
22
+ *
23
+ * # dxkit-allow:test-fixture reason="..."
24
+ * api_key = "sk_test_xxxx"
25
+ *
26
+ * The position is chosen at insert time by `insertAnnotation`: short
27
+ * source lines (under the configurable threshold) get same-line
28
+ * annotation; longer lines get above-line so the result stays
29
+ * readable.
30
+ *
31
+ * The grammar applies uniformly across every language; only the
32
+ * `<lineComment>` token varies (`#` for python/ruby, `//` for
33
+ * typescript/go/rust/csharp/kotlin/java). Each language pack
34
+ * declares its token via `LanguageSupport.commentSyntax`.
35
+ *
36
+ * # Quoting + escaping
37
+ *
38
+ * The reason is a JSON-style string. To embed a literal `"`, escape
39
+ * as `\"`; to embed a literal `\`, escape as `\\`. The parser
40
+ * un-escapes both on read; the writer escapes both on insert. No
41
+ * other escape sequences are supported (no `\n`, `\t`, etc.) — keep
42
+ * the reason single-line for human review.
43
+ */
44
+ import type { LanguageSupport } from '../languages/types';
45
+ import { type AllowlistCategory } from './categories';
46
+ export type AnnotationPosition = 'same-line' | 'above';
47
+ export interface InlineAnnotation {
48
+ readonly category: AllowlistCategory;
49
+ /** Free-form rationale. `undefined` when the annotation is
50
+ * syntactically valid but the `reason="..."` clause was omitted —
51
+ * callers typically treat this as a "malformed annotation"
52
+ * warning. */
53
+ readonly reason?: string;
54
+ }
55
+ export interface AnnotationMatch {
56
+ readonly annotation: InlineAnnotation;
57
+ readonly position: AnnotationPosition;
58
+ /** 1-indexed line where the annotation comment LIVES (not the
59
+ * finding's line — though they're the same for `same-line`). */
60
+ readonly annotationLine: number;
61
+ }
62
+ export interface InsertOptions {
63
+ /**
64
+ * Maximum source-line length that still gets a same-line annotation.
65
+ * Longer lines get above-line annotation to keep the result
66
+ * readable. The annotation itself is typically 50-80 characters,
67
+ * so totaling source + annotation around 120-140 characters at
68
+ * this threshold.
69
+ */
70
+ readonly sameLineThreshold?: number;
71
+ }
72
+ /**
73
+ * Render a complete annotation comment for a given language.
74
+ * Exposed so callers (the CLI, the block-time hint formatter) can
75
+ * surface the exact string a developer would paste, without going
76
+ * through the file-mutation path.
77
+ */
78
+ export declare function renderAnnotation(annotation: InlineAnnotation, lang: LanguageSupport): string;
79
+ /**
80
+ * Parse a single line for an allowlist annotation. Returns `null`
81
+ * when no annotation marker is found OR when the category isn't
82
+ * one of the canonical values.
83
+ *
84
+ * Tolerates the `reason="..."` clause being absent — callers decide
85
+ * whether a missing reason invalidates the suppression.
86
+ *
87
+ * False-positive surface: a string literal containing the literal
88
+ * substring `<lineComment> dxkit-allow:<category>` would match.
89
+ * Discriminating "is this inside a string" requires real language
90
+ * parsing and isn't worth the complexity — the worst case is a
91
+ * legitimate finding gets suppressed when a developer wrote the
92
+ * grammar literally inside a string. Vanishingly rare in practice.
93
+ */
94
+ export declare function parseAnnotation(line: string, lang: LanguageSupport): InlineAnnotation | null;
95
+ /**
96
+ * Look for an annotation matching a finding at `filePath:lineNumber`
97
+ * (1-indexed). Checks two positions:
98
+ *
99
+ * 1. Same-line: the finding's line itself carries the annotation.
100
+ * 2. Above-line: the immediately-preceding line is a standalone
101
+ * annotation comment.
102
+ *
103
+ * Returns `null` when neither position has a parseable annotation.
104
+ * Throws when the file doesn't exist or `lineNumber` is out of
105
+ * range — callers are typically operating on findings whose line
106
+ * numbers came from a fresh scan, so an out-of-range value
107
+ * indicates a bug worth surfacing.
108
+ */
109
+ export declare function findAnnotationAt(filePath: string, lineNumber: number, lang: LanguageSupport): AnnotationMatch | null;
110
+ /**
111
+ * Insert an allowlist annotation at `filePath:lineNumber`. Chooses
112
+ * same-line vs above-line based on the target line's length:
113
+ *
114
+ * - Target shorter than `sameLineThreshold` → append to target.
115
+ * - Target longer than threshold → prepend a new comment line
116
+ * above target, copying its indentation.
117
+ *
118
+ * Returns the chosen position + the 1-indexed line where the
119
+ * annotation comment ended up (useful for the CLI to print "added
120
+ * at file:line").
121
+ *
122
+ * Refuses to insert when the category is not inline-compatible —
123
+ * the caller should route accepted-risk + deferred suppressions
124
+ * through the file-level allowlist instead.
125
+ *
126
+ * Preserves CRLF vs LF line endings: the file is read, split, and
127
+ * re-joined using the detected style. Files without a trailing
128
+ * newline keep that property.
129
+ */
130
+ export declare function insertAnnotation(filePath: string, lineNumber: number, annotation: InlineAnnotation, lang: LanguageSupport, options?: InsertOptions): {
131
+ position: AnnotationPosition;
132
+ annotationLine: number;
133
+ };
134
+ /**
135
+ * Whether a line is a "standalone annotation comment" — meaning it
136
+ * contains nothing but leading whitespace + the comment marker +
137
+ * the annotation body. Used to distinguish an above-line annotation
138
+ * from a same-line annotation on a different finding's line.
139
+ *
140
+ * Exported because the gather pass (`src/allowlist/gather.ts`) needs
141
+ * the same predicate to label per-occurrence `position`. Single
142
+ * source of truth for the "what counts as standalone" rule.
143
+ *
144
+ * Example (python):
145
+ * ` # dxkit-allow:test-fixture reason="..."` ← standalone
146
+ * `x = 1 # dxkit-allow:test-fixture reason="..."` ← NOT standalone
147
+ */
148
+ export declare function isStandaloneAnnotationLine(line: string, lang: LanguageSupport): boolean;
149
+ //# sourceMappingURL=inline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline.d.ts","sourceRoot":"","sources":["../../src/allowlist/inline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAgD,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AASpG,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,OAAO,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC;;;mBAGe;IACf,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC;qEACiE;IACjE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACrC;AAiBD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,gBAAgB,EAAE,IAAI,EAAE,eAAe,GAAG,MAAM,CAQ5F;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,gBAAgB,GAAG,IAAI,CAwB5F;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,eAAe,GACpB,eAAe,GAAG,IAAI,CA6BxB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,gBAAgB,EAC5B,IAAI,EAAE,eAAe,EACrB,OAAO,GAAE,aAAkB,GAC1B;IAAE,QAAQ,EAAE,kBAAkB,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAoD1D;AAQD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAOvF"}