@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.
- package/CHANGELOG.md +68 -0
- package/README.md +20 -9
- package/dist/analyzers/developer/gather.d.ts +16 -0
- package/dist/analyzers/developer/gather.d.ts.map +1 -1
- package/dist/analyzers/developer/gather.js +2 -0
- package/dist/analyzers/developer/gather.js.map +1 -1
- package/dist/analyzers/developer/ownership.d.ts +86 -0
- package/dist/analyzers/developer/ownership.d.ts.map +1 -0
- package/dist/analyzers/developer/ownership.js +180 -0
- package/dist/analyzers/developer/ownership.js.map +1 -0
- package/dist/analyzers/quality/detailed.d.ts +5 -1
- package/dist/analyzers/quality/detailed.d.ts.map +1 -1
- package/dist/analyzers/quality/detailed.js +30 -29
- package/dist/analyzers/quality/detailed.js.map +1 -1
- package/dist/analyzers/security/detailed.d.ts +7 -1
- package/dist/analyzers/security/detailed.d.ts.map +1 -1
- package/dist/analyzers/security/detailed.js +31 -15
- package/dist/analyzers/security/detailed.js.map +1 -1
- package/dist/analyzers/tests/actions.d.ts +18 -1
- package/dist/analyzers/tests/actions.d.ts.map +1 -1
- package/dist/analyzers/tests/actions.js +37 -1
- package/dist/analyzers/tests/actions.js.map +1 -1
- package/dist/analyzers/tests/detailed.d.ts +5 -1
- package/dist/analyzers/tests/detailed.d.ts.map +1 -1
- package/dist/analyzers/tests/detailed.js +42 -23
- package/dist/analyzers/tests/detailed.js.map +1 -1
- package/dist/analyzers/tests/types.d.ts +10 -0
- package/dist/analyzers/tests/types.d.ts.map +1 -1
- package/dist/attribution/attribute.d.ts +57 -0
- package/dist/attribution/attribute.d.ts.map +1 -0
- package/dist/attribution/attribute.js +149 -0
- package/dist/attribution/attribute.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +53 -5
- package/dist/cli.js.map +1 -1
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js +12 -0
- package/dist/generator.js.map +1 -1
- package/dist/reviewers-cli.d.ts +57 -0
- package/dist/reviewers-cli.d.ts.map +1 -0
- package/dist/reviewers-cli.js +263 -0
- package/dist/reviewers-cli.js.map +1 -0
- package/package.json +1 -1
- package/templates/.claude/skills/dxkit-action/SKILL.md +42 -1
- package/templates/.claude/skills/dxkit-docs/SKILL.md +2 -0
- package/templates/.claude/skills/dxkit-feature/SKILL.md +14 -3
- package/templates/.claude/skills/dxkit-init/SKILL.md +1 -1
- package/templates/.claude/skills/dxkit-onboard/SKILL.md +2 -2
- package/templates/.claude/skills/dxkit-pr/SKILL.md +163 -0
- package/templates/.claude/skills/dxkit-reports/SKILL.md +1 -1
- package/templates/.claude/skills/dxkit-test/SKILL.md +130 -0
- package/templates/.claude/skills/dxkit-update/SKILL.md +4 -0
- package/templates/AGENTS.md.template +9 -3
- 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: 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,
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
|