@vyuhlabs/dxkit 2.9.2 → 2.9.4

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 (54) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +20 -9
  3. package/dist/analyzers/developer/gather.d.ts +16 -0
  4. package/dist/analyzers/developer/gather.d.ts.map +1 -1
  5. package/dist/analyzers/developer/gather.js +2 -0
  6. package/dist/analyzers/developer/gather.js.map +1 -1
  7. package/dist/analyzers/developer/ownership.d.ts +86 -0
  8. package/dist/analyzers/developer/ownership.d.ts.map +1 -0
  9. package/dist/analyzers/developer/ownership.js +180 -0
  10. package/dist/analyzers/developer/ownership.js.map +1 -0
  11. package/dist/analyzers/quality/detailed.d.ts +5 -1
  12. package/dist/analyzers/quality/detailed.d.ts.map +1 -1
  13. package/dist/analyzers/quality/detailed.js +30 -29
  14. package/dist/analyzers/quality/detailed.js.map +1 -1
  15. package/dist/analyzers/security/detailed.d.ts +7 -1
  16. package/dist/analyzers/security/detailed.d.ts.map +1 -1
  17. package/dist/analyzers/security/detailed.js +31 -15
  18. package/dist/analyzers/security/detailed.js.map +1 -1
  19. package/dist/analyzers/tests/actions.d.ts +18 -1
  20. package/dist/analyzers/tests/actions.d.ts.map +1 -1
  21. package/dist/analyzers/tests/actions.js +37 -1
  22. package/dist/analyzers/tests/actions.js.map +1 -1
  23. package/dist/analyzers/tests/detailed.d.ts +5 -1
  24. package/dist/analyzers/tests/detailed.d.ts.map +1 -1
  25. package/dist/analyzers/tests/detailed.js +42 -23
  26. package/dist/analyzers/tests/detailed.js.map +1 -1
  27. package/dist/analyzers/tests/types.d.ts +10 -0
  28. package/dist/analyzers/tests/types.d.ts.map +1 -1
  29. package/dist/attribution/attribute.d.ts +57 -0
  30. package/dist/attribution/attribute.d.ts.map +1 -0
  31. package/dist/attribution/attribute.js +149 -0
  32. package/dist/attribution/attribute.js.map +1 -0
  33. package/dist/cli.d.ts.map +1 -1
  34. package/dist/cli.js +53 -5
  35. package/dist/cli.js.map +1 -1
  36. package/dist/generator.d.ts.map +1 -1
  37. package/dist/generator.js +12 -0
  38. package/dist/generator.js.map +1 -1
  39. package/dist/reviewers-cli.d.ts +57 -0
  40. package/dist/reviewers-cli.d.ts.map +1 -0
  41. package/dist/reviewers-cli.js +263 -0
  42. package/dist/reviewers-cli.js.map +1 -0
  43. package/package.json +1 -1
  44. package/templates/.claude/skills/dxkit-action/SKILL.md +42 -1
  45. package/templates/.claude/skills/dxkit-docs/SKILL.md +2 -0
  46. package/templates/.claude/skills/dxkit-feature/SKILL.md +14 -3
  47. package/templates/.claude/skills/dxkit-init/SKILL.md +1 -1
  48. package/templates/.claude/skills/dxkit-onboard/SKILL.md +2 -2
  49. package/templates/.claude/skills/dxkit-pr/SKILL.md +163 -0
  50. package/templates/.claude/skills/dxkit-reports/SKILL.md +1 -1
  51. package/templates/.claude/skills/dxkit-test/SKILL.md +130 -0
  52. package/templates/.claude/skills/dxkit-update/SKILL.md +4 -0
  53. package/templates/AGENTS.md.template +9 -3
  54. package/templates/CLAUDE.md.template +9 -3
@@ -0,0 +1,263 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.parseCodeowners = parseCodeowners;
37
+ exports.matchCodeowners = matchCodeowners;
38
+ exports.buildSuggestions = buildSuggestions;
39
+ exports.runReviewers = runReviewers;
40
+ /**
41
+ * `vyuh-dxkit reviewers` — suggest reviewers for a change, grounded on the
42
+ * active-owner model (dev-report git history) rather than naive last-touch
43
+ * blame, blended with CODEOWNERS.
44
+ *
45
+ * The differentiation over a platform's built-in suggested-reviewers: the
46
+ * candidates are activity-weighted (recent, sustained work ranks highest),
47
+ * scoped to who is still active (a departed owner is never silently
48
+ * suggested), bot-free, exclude the change's own author, and carry a
49
+ * bus-factor signal. CODEOWNERS, when present, is authoritative and merged
50
+ * in. Output renders names + GitHub @handles — never raw emails.
51
+ *
52
+ * Pure helpers (`parseCodeowners`, `matchCodeowners`, `buildSuggestions`)
53
+ * are unit-tested without git; `runReviewers` is the IO entry point.
54
+ */
55
+ const fs = __importStar(require("fs"));
56
+ const path = __importStar(require("path"));
57
+ const child_process_1 = require("child_process");
58
+ const logger = __importStar(require("./logger"));
59
+ const ownership_1 = require("./analyzers/developer/ownership");
60
+ const gather_1 = require("./analyzers/developer/gather");
61
+ /**
62
+ * Parse a CODEOWNERS file. Each non-comment line is `<pattern> <owner>...`.
63
+ * Returns rules in file order; CODEOWNERS semantics are "last matching rule
64
+ * wins," so callers iterate in reverse.
65
+ */
66
+ function parseCodeowners(content) {
67
+ const rules = [];
68
+ for (const raw of content.split(/\r?\n/)) {
69
+ const line = raw.replace(/#.*$/, '').trim();
70
+ if (!line)
71
+ continue;
72
+ const parts = line.split(/\s+/);
73
+ const pattern = parts[0];
74
+ const owners = parts.slice(1).filter(Boolean);
75
+ if (pattern && owners.length > 0)
76
+ rules.push({ pattern, owners });
77
+ }
78
+ return rules;
79
+ }
80
+ /** Translate a CODEOWNERS pattern to a RegExp (subset: `*`, `**`, leading
81
+ * `/`, trailing `/`). Good enough for the common cases; unmatched exotic
82
+ * globs simply don't match (conservative — never over-claims ownership). */
83
+ function patternToRegExp(pattern) {
84
+ let p = pattern;
85
+ const anchored = p.startsWith('/');
86
+ if (anchored)
87
+ p = p.slice(1);
88
+ const dirOnly = p.endsWith('/');
89
+ if (dirOnly)
90
+ p = p.slice(0, -1);
91
+ // Tokenize so `**` -> `.*` and `*` -> `[^/]*` without a fragile placeholder;
92
+ // every other regex-special char is escaped.
93
+ let body = '';
94
+ for (let i = 0; i < p.length; i++) {
95
+ const c = p[i];
96
+ if (c === '*') {
97
+ if (p[i + 1] === '*') {
98
+ body += '.*';
99
+ i++;
100
+ }
101
+ else {
102
+ body += '[^/]*';
103
+ }
104
+ }
105
+ else if (/[.+^${}()|[\]\\?]/.test(c)) {
106
+ body += '\\' + c;
107
+ }
108
+ else {
109
+ body += c;
110
+ }
111
+ }
112
+ const full = dirOnly ? `${body}/.*` : `${body}(?:/.*)?`;
113
+ return new RegExp(anchored ? `^${full}$` : `(?:^|/)${full}$`);
114
+ }
115
+ /** Owners for one file: the LAST matching CODEOWNERS rule wins. Empty when
116
+ * no rule matches. */
117
+ function matchCodeowners(rules, file) {
118
+ for (let i = rules.length - 1; i >= 0; i--) {
119
+ if (patternToRegExp(rules[i].pattern).test(file))
120
+ return [...rules[i].owners];
121
+ }
122
+ return [];
123
+ }
124
+ /**
125
+ * Compose the active-owner ranking with CODEOWNERS into a ranked reviewer
126
+ * list. Pure. CODEOWNERS owners are authoritative (always included, flagged);
127
+ * active git owners follow, ranked by score; inactive owners are dropped from
128
+ * the suggestion list (they're surfaced only via the `note` fallback).
129
+ */
130
+ function buildSuggestions(ownership, opts = {}) {
131
+ const limit = opts.limit ?? 3;
132
+ const codeowners = dedupe(opts.codeowners ?? []);
133
+ const out = [];
134
+ const seen = new Set();
135
+ // 1. CODEOWNERS first — authoritative.
136
+ for (const token of codeowners) {
137
+ const handle = token.startsWith('@') ? token.slice(1) : undefined;
138
+ const key = token.toLowerCase();
139
+ if (seen.has(key))
140
+ continue;
141
+ seen.add(key);
142
+ out.push({
143
+ name: token,
144
+ ...(handle ? { handle } : {}),
145
+ active: true, // CODEOWNERS is a maintained, current declaration
146
+ isCodeowner: true,
147
+ reason: 'listed in CODEOWNERS for the touched paths',
148
+ score: Number.POSITIVE_INFINITY,
149
+ });
150
+ }
151
+ // 2. Active git owners, ranked.
152
+ const activeOwners = ownership.ranked.filter((o) => o.active);
153
+ for (const o of activeOwners) {
154
+ const handle = o.githubHandle;
155
+ const key = (handle ? `@${handle}` : o.name).toLowerCase();
156
+ if (seen.has(key))
157
+ continue;
158
+ seen.add(key);
159
+ out.push({
160
+ name: o.name,
161
+ ...(handle ? { handle } : {}),
162
+ active: true,
163
+ isCodeowner: false,
164
+ reason: `${o.commits} recent commit${o.commits === 1 ? '' : 's'} to the touched files (last ${o.lastTouched})`,
165
+ score: o.score,
166
+ });
167
+ }
168
+ const reviewers = out.slice(0, limit);
169
+ let note;
170
+ if (activeOwners.length === 0 && codeowners.length === 0) {
171
+ note =
172
+ ownership.ranked.length > 0
173
+ ? 'Every contributor who has touched these files is inactive — no current owner to suggest. Route by current team ownership.'
174
+ : 'No git history or CODEOWNERS for the touched files — no reviewer signal.';
175
+ }
176
+ else if (activeOwners.length === 0) {
177
+ note = 'Original authors are inactive — suggesting by CODEOWNERS only.';
178
+ }
179
+ return {
180
+ touchedFiles: [],
181
+ reviewers,
182
+ busFactor: ownership.busFactor,
183
+ ...(note ? { note } : {}),
184
+ };
185
+ }
186
+ function dedupe(xs) {
187
+ const seen = new Set();
188
+ const out = [];
189
+ for (const x of xs) {
190
+ const k = x.toLowerCase();
191
+ if (!seen.has(k)) {
192
+ seen.add(k);
193
+ out.push(x);
194
+ }
195
+ }
196
+ return out;
197
+ }
198
+ function gitOut(cmd, cwd) {
199
+ try {
200
+ return (0, child_process_1.execSync)(cmd, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
201
+ }
202
+ catch {
203
+ return '';
204
+ }
205
+ }
206
+ function touchedFiles(cwd, opts) {
207
+ if (opts.staged) {
208
+ return gitOut('git diff --cached --name-only', cwd).split('\n').filter(Boolean);
209
+ }
210
+ const base = opts.base || gitOut('git rev-parse --abbrev-ref origin/HEAD', cwd) || 'origin/main';
211
+ const out = gitOut(`git diff --name-only ${base}...HEAD`, cwd);
212
+ return out.split('\n').filter(Boolean);
213
+ }
214
+ function readCodeowners(cwd) {
215
+ for (const rel of ['.github/CODEOWNERS', 'CODEOWNERS', 'docs/CODEOWNERS']) {
216
+ const p = path.join(cwd, rel);
217
+ if (fs.existsSync(p))
218
+ return parseCodeowners(fs.readFileSync(p, 'utf8'));
219
+ }
220
+ return [];
221
+ }
222
+ function runReviewers(cwd, opts) {
223
+ const files = touchedFiles(cwd, opts);
224
+ if (files.length === 0) {
225
+ logger.info('No changed files detected (pass --base <ref> or --staged). Nothing to suggest.');
226
+ return;
227
+ }
228
+ // Exclude the change author from suggestions (never review your own PR).
229
+ const authorEmail = gitOut('git config --get user.email', cwd);
230
+ const excludeEmails = authorEmail ? new Set([(0, gather_1.normalizeEmail)(authorEmail)]) : undefined;
231
+ const ownership = (0, ownership_1.ownersFor)(cwd, files, { ...(excludeEmails ? { excludeEmails } : {}) });
232
+ // CODEOWNERS owners for the touched files (deduped across files).
233
+ const rules = readCodeowners(cwd);
234
+ const coOwners = [];
235
+ for (const f of files)
236
+ coOwners.push(...matchCodeowners(rules, f));
237
+ const result = {
238
+ ...buildSuggestions(ownership, {
239
+ ...(opts.limit !== undefined ? { limit: opts.limit } : {}),
240
+ codeowners: coOwners,
241
+ }),
242
+ touchedFiles: files,
243
+ };
244
+ if (opts.json) {
245
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
246
+ return;
247
+ }
248
+ logger.info(`Suggested reviewers for ${files.length} changed file${files.length === 1 ? '' : 's'}:`);
249
+ if (result.reviewers.length === 0) {
250
+ logger.info(' (none)');
251
+ }
252
+ for (const r of result.reviewers) {
253
+ const who = r.handle ? `@${r.handle}` : r.name;
254
+ const tag = r.isCodeowner ? ' [CODEOWNERS]' : '';
255
+ logger.info(` ${who}${tag} — ${r.reason}`);
256
+ }
257
+ if (result.busFactor === 1) {
258
+ logger.warn(' Bus factor 1: a single active owner covers these files — consider spreading knowledge.');
259
+ }
260
+ if (result.note)
261
+ logger.dim(` ${result.note}`);
262
+ }
263
+ //# sourceMappingURL=reviewers-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reviewers-cli.js","sourceRoot":"","sources":["../src/reviewers-cli.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vyuhlabs/dxkit",
3
- "version": "2.9.2",
3
+ "version": "2.9.4",
4
4
  "description": "AI-native developer experience toolkit for any codebase",
5
5
  "license": "MIT",
6
6
  "author": "Vyuh Labs",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: dxkit-action
3
- description: Read a dxkit report and execute fixes — prioritize findings by severity, plan the fix sequence, run the fix, verify the score moved, re-baseline if appropriate. Use when the user says "fix these findings", "act on the health report", "close out these vulnerabilities", or after dxkit-reports has surfaced something concrete.
3
+ description: Read a dxkit report and execute fixes — prioritize findings by severity, plan the fix sequence, run the fix, verify the score moved, re-baseline if appropriate. Supports a SCOPED pass to burn down one category at a time (dependency/BOM vulnerabilities, security, code quality, tests, docs). Use when the user says "fix these findings", "act on the health report", "close out these vulnerabilities", "just fix the dependency vulns", "only the security findings", or after dxkit-reports has surfaced something concrete.
4
4
  ---
5
5
 
6
6
  # dxkit-action
@@ -28,6 +28,40 @@ npx vyuh-dxkit vulnerabilities --detailed --graph-context # or test-gaps / qua
28
28
 
29
29
  `--graph-context` adds a "Graph context" column (the module a finding lives in + its blast radius — how many files call into it) so you can plan the fix without separately discovering structure. It's a structural HINT, not ground truth — read "Graph context" below for how to use it safely.
30
30
 
31
+ Add **`--attribute`** when you need to know *who to ask* about a pre-existing finding — it adds a "Who to ask" column from `git blame` routed through the active-owner model, so an inactive author is forwarded to the file's current owner (names + @handles, never emails). It's opt-in and **historical only**: a net-new finding the guardrail just blocked was introduced by your own change, so "who to ask" there is simply the PR author — no blame needed. Honor the honesty caveat (blame is last-touch, not necessarily who introduced it).
32
+
33
+ ## Scoped fix — fix one category at a time
34
+
35
+ Often the user doesn't want "fix everything" — they want to burn down **one
36
+ category**: "just the dependency vulns", "only the security findings", "the
37
+ code-quality stuff." Honor that. A scoped pass keeps the change reviewable and
38
+ lets the team sequence the work.
39
+
40
+ When the user names a scope (or you propose one), run the report that already
41
+ partitions that dimension and work **only** that worklist — prioritization
42
+ (severity → reachability → blast radius) still applies, but within the scope:
43
+
44
+ | Scope the user asks for | Run | Findings in scope | Notes / hand-off |
45
+ |---|---|---|---|
46
+ | "dependency / BOM vulnerabilities" | `npx vyuh-dxkit bom` (or `vulnerabilities --detailed`) | `dep-vuln` | upgrade-first; see "Dependency vulnerability" below |
47
+ | "security" | `npx vyuh-dxkit vulnerabilities --detailed --graph-context` | code-SAST, secrets, dep-vuln | reachable findings first (the report orders them); secrets always top |
48
+ | "code quality" | `npx vyuh-dxkit quality --detailed --graph-context` | duplication, slop, lint, complexity | see "Slop / code-pattern finding" below |
49
+ | "tests" / "coverage" | `npx vyuh-dxkit test-gaps --detailed --graph-context` | test-gap | hand off to **dxkit-test** for a real test-writing push |
50
+ | "documentation" | `npx vyuh-dxkit health --detailed` (Documentation) | doc-gap | hand off to **dxkit-docs** |
51
+
52
+ Rules for a scoped pass:
53
+
54
+ - **Stay in the lane.** If the user said "just deps," don't also start fixing
55
+ SAST findings you notice — surface them ("I also see 3 HIGH SAST findings;
56
+ want a separate pass?") but don't expand scope unasked.
57
+ - **Tests / docs scopes are hand-offs, not inline work.** Closing test gaps or
58
+ writing docs is a dedicated loop — route to `dxkit-test` / `dxkit-docs`
59
+ rather than half-doing it here.
60
+ - **Verify within the scope.** Step [5] re-runs the scope's own report; the
61
+ scoped dimension should move and nothing else should regress (guardrail).
62
+
63
+ Without a named scope, work the full **Priority order** below.
64
+
31
65
  ## Priority order
32
66
 
33
67
  Walk findings in this order (highest to lowest):
@@ -105,6 +139,8 @@ If no patched version exists OR the upgrade breaks other constraints AND the ris
105
139
 
106
140
  ### Test gap
107
141
 
142
+ For one test gap inside a broader fix pass, close it inline:
143
+
108
144
  ```bash
109
145
  # 1. Read the source file the test-gap analyzer flagged
110
146
  # 2. Write a test that exercises the file's primary contract
@@ -116,6 +152,8 @@ npx vyuh-dxkit test-gaps
116
152
 
117
153
  Don't write tests that just import the module — write tests that exercise behavior. Useless tests inflate the count but don't move the dimension.
118
154
 
155
+ For a dedicated push to close many gaps / raise the Tests score — reading the blast-radius-weighted worklist, orienting via the graph, writing meaningful tests in the repo's framework — hand off to the **dxkit-test** skill (the testing mirror of dxkit-docs).
156
+
119
157
  ### Slop / code-pattern finding
120
158
 
121
159
  ```bash
@@ -272,4 +310,7 @@ In those cases: `vyuh-dxkit allowlist add` is the right tool for per-finding dec
272
310
  - For hook-related issues during a fix push → `dxkit-hooks` skill
273
311
  - For re-running reports between fixes → `dxkit-reports` skill
274
312
  - For broken dxkit install (hooks not firing, vyuh-dxkit not on PATH) → `dxkit-fix` skill
313
+ - For a dedicated test-writing push (close many gaps / raise the Tests score) → `dxkit-test` skill
314
+ - For generating missing documentation → `dxkit-docs` skill
315
+ - For raising the PR once fixes are done + guardrail-green → `dxkit-pr` skill (PR body from the diff + the guardrail/allowlist signals + a reviewer checklist)
275
316
  - For allowlist management beyond the per-finding `add` path — auditing existing entries (including orphans after a re-baseline), removing stale fingerprints, pruning expired ones, exporting to a `.snyk`, or reviewing the team's overall suppression posture → **dxkit-allowlist** skill
@@ -146,3 +146,5 @@ npx vyuh-dxkit guardrail check
146
146
  should leave the new surface documented).
147
147
  - Slop findings outside docs (AI-generated code prose, CHANGELOG slop) →
148
148
  `dxkit-action`'s slop recipe.
149
+ - Closing test gaps / raising the Tests score (the sibling generator skill) →
150
+ `dxkit-test`.
@@ -154,8 +154,7 @@ Exit 0 = the feature added no net-new regressions. Exit 1 = something new
154
154
  appeared — **a finding you introduced.** Address it before pushing:
155
155
 
156
156
  - A real finding in your new code → fix it now (hand off to `dxkit-action`
157
- for the fix recipes — secret rotation, dep upgrade, writing the missing
158
- test, etc.).
157
+ for the fix recipes — secret rotation, dep upgrade, SAST, etc.).
159
158
  - A genuine false positive / intentional pattern → allowlist with a typed
160
159
  category + reason (see `dxkit-action`'s allowlisting section). Fix first;
161
160
  allowlist second.
@@ -163,6 +162,15 @@ appeared — **a finding you introduced.** Address it before pushing:
163
162
  The feature isn't done when it works — it's done when it works **and** the
164
163
  guardrail is green.
165
164
 
165
+ ### Offer to test the new surface
166
+
167
+ A new feature is the most common source of a fresh test gap. When step [5]'s
168
+ `test-gaps` shows the code you just added is untested — especially if it has
169
+ callers (a non-trivial blast radius) — **offer to write tests for it, and on
170
+ the user's confirmation hand off to `dxkit-test`** to write them grounded in
171
+ the behavior you just built. Keep it an offer: don't auto-generate tests the
172
+ user didn't ask for, but don't let a new untested surface ship silently either.
173
+
166
174
  ## [6] Baseline decision
167
175
 
168
176
  | Scenario | Action |
@@ -180,7 +188,10 @@ own feature introduced.
180
188
  ## Hand-offs
181
189
 
182
190
  - A finding the guardrail blocked needs fixing → `dxkit-action` (the fix-loop
183
- recipes for secrets, dep-vulns, SAST, test gaps).
191
+ recipes for secrets, dep-vulns, SAST).
192
+ - Writing tests for the new (or any untested) surface → `dxkit-test`.
193
+ - Raising the PR once the feature is built + green → `dxkit-pr` (title + body
194
+ from the diff, dxkit signals, reviewer checklist).
184
195
  - Re-running reports between iterations → `dxkit-reports`.
185
196
  - Ignore-file / config edits as part of the feature → `dxkit-config`.
186
197
  - Hook problems on the verify push → `dxkit-hooks`.
@@ -21,7 +21,7 @@ Ask the user what they want, then pick the right invocation:
21
21
 
22
22
  | Flag | What it ships | Default under `--full`? |
23
23
  |---|---|---|
24
- | `--with-dxkit-agents` | The 6 dxkit-* skills + AGENTS.md + CLAUDE.md shim | Yes |
24
+ | `--with-dxkit-agents` | The dxkit-* skills + AGENTS.md + CLAUDE.md shim | Yes |
25
25
  | `--with-hooks` | `.githooks/pre-push` + postinstall activation wire-up | Yes |
26
26
  | `--with-precommit-hook` | Adds `.githooks/pre-commit` (slow on large repos) | No (still opt-in) |
27
27
  | `--with-devcontainer` | `.devcontainer/devcontainer.json` (per-stack features) + post-create.sh | Yes |
@@ -82,7 +82,7 @@ Before running, ASK:
82
82
 
83
83
  Optional flags worth surfacing if the customer pushes back on "full":
84
84
 
85
- - `--with-dxkit-agents` — just the 8 dxkit-* skills (no hooks, no CI)
85
+ - `--with-dxkit-agents` — just the dxkit-* skills (no hooks, no CI)
86
86
  - `--with-hooks --with-dxkit-agents` — skills + pre-push hook
87
87
  - `--with-precommit-hook` — also pre-commit (slow on large repos)
88
88
 
@@ -262,7 +262,7 @@ When in doubt, dxkit-onboard handles the full first-install journey and delegate
262
262
  ```
263
263
  ✓ Fresh dxkit install complete:
264
264
  • Binary: 2.5.X installed globally + project-local
265
- • Scaffold: 8 dxkit-* skills, AGENTS.md, CLAUDE.md, devcontainer, hooks, CI workflows
265
+ • Scaffold: dxkit-* skills, AGENTS.md, CLAUDE.md, devcontainer, hooks, CI workflows
266
266
  • Doctor: 14/14 (Reports + Agent DX + Operational health)
267
267
  • Baseline: N findings locked in (or "skipped — you're triaging first")
268
268
  • Pre-commit: yes/no (your choice)
@@ -0,0 +1,163 @@
1
+ ---
2
+ name: dxkit-pr
3
+ description: Open a pull request with a title + body grounded in the branch's real commits and diff — what changed, features implemented, findings fixed — plus a reviewer checklist and the dxkit guardrail/allowlist/score signals a reviewer needs. Use when the user says "raise a PR", "open a pull request", "create the PR", "write the PR description", or after dxkit-feature / dxkit-action finishes a change and it's ready to push for review.
4
+ ---
5
+
6
+ # dxkit-pr
7
+
8
+ This skill turns a finished branch into a **reviewable** pull request: a title
9
+ and body grounded in what actually changed (not a generic template), and a
10
+ checklist that guides the reviewer through what to verify. It's the natural
11
+ close of `dxkit-feature` (built something) and `dxkit-action` (fixed findings) —
12
+ both hand off here when the work is ready for review.
13
+
14
+ A good PR description is written from the diff, not from memory. This skill
15
+ reads the branch, summarizes it honestly, and attaches the dxkit signals
16
+ (guardrail verdict, allowlist activity, score movement) a reviewer would
17
+ otherwise have to reconstruct by hand.
18
+
19
+ ## The PR loop
20
+
21
+ ```
22
+ [1] Survey → branch vs base: commits, diff stat, files touched
23
+ [2] Classify → features / fixes / refactors / docs / findings closed
24
+ [3] Signals → guardrail verdict + allowlist activity + score deltas
25
+ [4] Draft → title + body grounded in [1]–[3] + a reviewer checklist
26
+ [5] Confirm → show the user the draft; open with `gh pr create` on yes
27
+ ```
28
+
29
+ Don't skip [5]. A PR is outward-facing — show the draft and get a yes before
30
+ opening it.
31
+
32
+ ## [1] Survey — read the branch, don't guess
33
+
34
+ ```bash
35
+ git fetch origin
36
+ BASE=origin/main # or the repo's default branch
37
+ git log --oneline $BASE..HEAD # every commit on this branch
38
+ git diff --stat $BASE...HEAD # files + churn
39
+ git diff $BASE...HEAD # the actual change, when you need detail
40
+ ```
41
+
42
+ Read the commit messages first — on a well-kept branch they already narrate the
43
+ work. Use the diff to verify and fill gaps, not to re-derive everything.
44
+
45
+ ## [2] Classify — group the change for a reviewer
46
+
47
+ Sort the commits/diff into the buckets a reviewer cares about:
48
+
49
+ - **Features** — new capability, with the entry point / surface it adds.
50
+ - **Fixes** — bugs or findings closed (name the finding if it came from a dxkit
51
+ report: rule, file, severity).
52
+ - **Refactors** — behavior-preserving structure changes (flag these — they're
53
+ where "looks big, reads safe" lives).
54
+ - **Docs / tests / chore** — supporting changes.
55
+
56
+ Lead the body with the *why* (the problem) and the *what* (the approach), then
57
+ the bucketed change list. Keep it proportional — a one-commit fix gets a short
58
+ body; a multi-commit feature gets sections.
59
+
60
+ ## [3] Signals — attach what dxkit knows
61
+
62
+ Run the guardrail so the PR states its own verdict, and surface any suppression
63
+ activity a reviewer must sign off on:
64
+
65
+ ```bash
66
+ npx vyuh-dxkit guardrail check # PASS/FAIL the PR will get in CI
67
+ npx vyuh-dxkit allowlist audit # any new/expiring suppressions?
68
+ npx vyuh-dxkit health --detailed | head -40 # score movement, if relevant
69
+ ```
70
+
71
+ Put in the body:
72
+
73
+ - **Guardrail verdict** — PASS, or FAIL with the net-new findings named (a
74
+ reviewer should know before CI tells them).
75
+ - **Allowlist activity** — any suppression added on this branch, with its
76
+ category + reason + expiry, called out for explicit review (suppressions are
77
+ the highest-trust thing a reviewer approves).
78
+ - **Score deltas** — only when the change targeted a dimension (e.g. "Tests
79
+ 62 → 71 after closing the auth gaps"). Don't pad with unchanged scores.
80
+
81
+ ### Suggested reviewers
82
+
83
+ ```bash
84
+ npx vyuh-dxkit reviewers --base <base-branch> --json
85
+ ```
86
+
87
+ This ranks reviewers by the **active-owner model** — recency-weighted git
88
+ history on the touched files, with bots and departed contributors filtered, the
89
+ PR author excluded, blended with `CODEOWNERS`. Better signal than the platform's
90
+ naive last-touch suggestion. Surface the top few in the body with the *why*
91
+ ("@alice — owns 3/4 touched files, active"), and you can pass them to
92
+ `gh pr create --reviewer`. Honor the output's caveats: if it returns a
93
+ `busFactor: 1`, note the single-point-of-failure; if it returns a `note`
94
+ (original authors inactive / no signal), say so rather than inventing a
95
+ reviewer. Renders `@handle`s, never emails.
96
+
97
+ ## [4] Draft — title, body, reviewer checklist
98
+
99
+ **Title** — imperative, scoped, specific. `feat(auth): add refresh-token
100
+ rotation`, not `Updates`. Match the repo's existing PR/commit convention
101
+ (check `git log` on the base branch).
102
+
103
+ **Body** — structure:
104
+
105
+ ```markdown
106
+ ## What & why
107
+ <the problem this solves, in 1–3 sentences>
108
+
109
+ ## Changes
110
+ - **Feature:** …
111
+ - **Fix:** … (closes <finding/issue>)
112
+ - **Refactor:** … (behavior-preserving)
113
+
114
+ ## dxkit signals
115
+ - Guardrail: ✅ PASS (or ❌ + the net-new findings)
116
+ - Allowlist: <new suppressions + reason + expiry, or "no changes">
117
+ - Scores: <dimension deltas, if the change targeted one>
118
+
119
+ ## Suggested reviewers
120
+ - @alice — owns 3/4 touched files, active · @bob — CODEOWNERS
121
+ (or the `note` when there's no active-owner signal)
122
+
123
+ ## Reviewer checklist
124
+ - [ ] Change matches the description; scope isn't broader than stated
125
+ - [ ] <feature>: behavior verified (how to exercise it)
126
+ - [ ] Refactors are behavior-preserving (no silent semantic change)
127
+ - [ ] New/changed code is tested; test gaps addressed or noted
128
+ - [ ] Any allowlist suppression is justified (category + reason + expiry)
129
+ - [ ] No secrets/keys/tokens in the diff
130
+ - [ ] Docs updated if behavior or interfaces changed
131
+ ```
132
+
133
+ Tailor the checklist to the *actual* change — drop rows that don't apply, add
134
+ specific ones (a migration step, a config flag to set, a caller to re-test from
135
+ the blast radius). A generic checklist is noise; a targeted one guides the review.
136
+
137
+ ## [5] Confirm + open
138
+
139
+ Show the user the full draft (title + body) and confirm. On yes:
140
+
141
+ ```bash
142
+ git push -u origin HEAD # if not already pushed
143
+ gh pr create --base main --title "<title>" --body "<body>" \
144
+ --reviewer <handle1>,<handle2> # the active owners from `reviewers`, if any
145
+ ```
146
+
147
+ If `gh` isn't authenticated, print the title + body for the user to paste, and
148
+ point them at `gh auth login`. Never open a PR the user hasn't seen.
149
+
150
+ ## Scope — what NOT to do
151
+
152
+ - Don't invent changes the diff doesn't show, or claim a finding is fixed
153
+ without having verified it (re-run the analyzer first — see `dxkit-action`).
154
+ - Don't open the PR before the guardrail is green, unless the user explicitly
155
+ wants a draft PR for early review — and then mark it draft and say so in the body.
156
+ - Don't restate every commit verbatim — synthesize. The commit log is one click
157
+ away; the body's job is the narrative + the review guidance.
158
+
159
+ ## Hand-offs
160
+
161
+ - A guardrail FAIL blocking the PR → `dxkit-action` to fix the net-new findings first.
162
+ - Writing the feature being PR'd → `dxkit-feature`; closing test gaps it opened → `dxkit-test`.
163
+ - Branch-protection / required-check setup so the PR is actually gated → `dxkit-hooks` / repo settings.
@@ -103,7 +103,7 @@ Surface those three when summarizing a dep-vuln finding. The detailed JSON has t
103
103
 
104
104
  ## When the user wants to ACT on findings
105
105
 
106
- Hand off to the `dxkit-action` skill — that's the workflow for prioritizing + fixing + re-baselining. This skill stops at "here's what's wrong."
106
+ Hand off to the `dxkit-action` skill — that's the workflow for prioritizing + fixing + re-baselining. This skill stops at "here's what's wrong." For a dimension-focused push, hand to the specialist generator skills instead: **dxkit-test** to close test-gaps / raise the Tests score, **dxkit-docs** to write missing documentation.
107
107
 
108
108
  ## Troubleshooting
109
109