mewkit 1.16.0 → 1.17.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.
- package/dist/commands/build-plugin.d.ts +6 -0
- package/dist/commands/build-plugin.d.ts.map +1 -1
- package/dist/commands/build-plugin.js +3 -3
- package/dist/commands/build-plugin.js.map +1 -1
- package/dist/commands/plan.d.ts +7 -0
- package/dist/commands/plan.d.ts.map +1 -0
- package/dist/commands/plan.js +124 -0
- package/dist/commands/plan.js.map +1 -0
- package/dist/commands/providers.js +15 -0
- package/dist/commands/providers.js.map +1 -1
- package/dist/commands/validate.d.ts +7 -1
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +58 -1
- package/dist/commands/validate.js.map +1 -1
- package/dist/core/build-inventory.d.ts +16 -0
- package/dist/core/build-inventory.d.ts.map +1 -1
- package/dist/core/build-inventory.js +35 -0
- package/dist/core/build-inventory.js.map +1 -1
- package/dist/core/capability.d.ts +24 -24
- package/dist/core/check-agent-conformance.d.ts +14 -0
- package/dist/core/check-agent-conformance.d.ts.map +1 -0
- package/dist/core/check-agent-conformance.js +118 -0
- package/dist/core/check-agent-conformance.js.map +1 -0
- package/dist/core/check-docs-references.d.ts.map +1 -1
- package/dist/core/check-docs-references.js +6 -0
- package/dist/core/check-docs-references.js.map +1 -1
- package/dist/core/check-gate-authority.d.ts +40 -0
- package/dist/core/check-gate-authority.d.ts.map +1 -0
- package/dist/core/check-gate-authority.js +313 -0
- package/dist/core/check-gate-authority.js.map +1 -0
- package/dist/core/check-plugin-parity.d.ts +15 -0
- package/dist/core/check-plugin-parity.d.ts.map +1 -0
- package/dist/core/check-plugin-parity.js +130 -0
- package/dist/core/check-plugin-parity.js.map +1 -0
- package/dist/core/check-pseudo-capabilities.d.ts +15 -0
- package/dist/core/check-pseudo-capabilities.d.ts.map +1 -0
- package/dist/core/check-pseudo-capabilities.js +89 -0
- package/dist/core/check-pseudo-capabilities.js.map +1 -0
- package/dist/core/plan-parser.d.ts +40 -0
- package/dist/core/plan-parser.d.ts.map +1 -0
- package/dist/core/plan-parser.js +126 -0
- package/dist/core/plan-parser.js.map +1 -0
- package/dist/core/plugin-payload.d.ts.map +1 -1
- package/dist/core/plugin-payload.js +9 -0
- package/dist/core/plugin-payload.js.map +1 -1
- package/dist/core/provider-adapter.d.ts +4 -0
- package/dist/core/provider-adapter.d.ts.map +1 -1
- package/dist/core/provider-adapter.js +2 -0
- package/dist/core/provider-adapter.js.map +1 -1
- package/dist/core/provider-operations.d.ts +51 -0
- package/dist/core/provider-operations.d.ts.map +1 -0
- package/dist/core/provider-operations.js +171 -0
- package/dist/core/provider-operations.js.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -1
- package/dist/migrate/model-taxonomy.d.ts +32 -0
- package/dist/migrate/model-taxonomy.d.ts.map +1 -1
- package/dist/migrate/model-taxonomy.js +59 -0
- package/dist/migrate/model-taxonomy.js.map +1 -1
- package/dist/visual-plan/application/export-plan.d.ts.map +1 -1
- package/dist/visual-plan/application/export-plan.js +25 -10
- package/dist/visual-plan/application/export-plan.js.map +1 -1
- package/dist/visual-plan/domain/wireframe-theme.d.ts +24 -0
- package/dist/visual-plan/domain/wireframe-theme.d.ts.map +1 -0
- package/dist/visual-plan/domain/wireframe-theme.js +169 -0
- package/dist/visual-plan/domain/wireframe-theme.js.map +1 -0
- package/dist/visual-plan-web/index.css +1 -1
- package/dist/visual-plan-web/index.js +150 -14
- package/package.json +1 -1
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/** Suppression marker for prose that legitimately QUOTES the anti-pattern. */
|
|
4
|
+
const ALLOW_MARKER = "lint-allow-gate-authority";
|
|
5
|
+
/**
|
|
6
|
+
* Statements that grant automated gate approval. Each entry is scoped to an
|
|
7
|
+
* explicit `Gate 1` / `Gate 2` reference (or an unambiguous euphemism) so that
|
|
8
|
+
* unrelated vocabulary — a skill's own "HARD GATE", a domain "gate" metaphor —
|
|
9
|
+
* cannot trip the check.
|
|
10
|
+
*/
|
|
11
|
+
const PATTERNS = [
|
|
12
|
+
{
|
|
13
|
+
name: "auto-approve → gate",
|
|
14
|
+
// The digit is REQUIRED. Without it this matches the bare word "gate" and
|
|
15
|
+
// fires on every domain metaphor ("the quality gate stays manual").
|
|
16
|
+
//
|
|
17
|
+
// The gap is deliberately TIGHT. In every real instance the verb and its
|
|
18
|
+
// gate are adjacent ("auto-approves Gate 2"). A wide gap lets one match
|
|
19
|
+
// span two clauses, so a negated first clause swallows the gate reference
|
|
20
|
+
// belonging to an asserting second clause — and the violation vanishes.
|
|
21
|
+
re: /auto[-\s]?approv\w*[^.\n]{0,20}\bgates?\s*[12]\b/gi,
|
|
22
|
+
expected: "automation executes between gates; a human approves the gate",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: "gate → auto-approved",
|
|
26
|
+
re: /\bgate\s*[12]\b[^.\n]{0,30}auto[-\s]?approv\w*/gi,
|
|
27
|
+
expected: "automation executes between gates; a human approves the gate",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "gate bound to an auto value",
|
|
31
|
+
re: /\bgate\s*[12]\s*:\s*auto/gi,
|
|
32
|
+
expected: "Gate 1 / Gate 2 are human in every mode",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "gate offering an auto alternative",
|
|
36
|
+
re: /\bgate\s*[12]\b[^.\n]{0,30}\[[^\]\n]*auto[^\]\n]*\]/gi,
|
|
37
|
+
expected: "the only gate outcome is human approval — drop the auto alternative",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "stamp treated as authority",
|
|
41
|
+
re: /\bstamp\s+counts\b/gi,
|
|
42
|
+
expected: "an evaluator stamp is evidence presented at the gate, never the approval",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "gate prompt skipped",
|
|
46
|
+
re: /\bskips?\s+gate\s*[12]\b[^.\n]{0,30}(prompt|user)/gi,
|
|
47
|
+
expected: "present the gate; a passing pre-check is evidence, not approval",
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
/**
|
|
51
|
+
* Words that flip a claim from ASSERTING automated approval to FORBIDDING it.
|
|
52
|
+
* "Gate 2 is never auto-approved" states the contract — it must not be flagged.
|
|
53
|
+
*/
|
|
54
|
+
const NEGATION_RE = /\b(never|not|n't|no|none|cannot|can't|forbidden|prohibited|non-negotiable|mandatory|requires?\s+human|human\s+approval|explicitly\s+typed)\b/i;
|
|
55
|
+
/**
|
|
56
|
+
* How far back from a match to look for the word that negates it. A negation
|
|
57
|
+
* governs the clause it introduces, so it sits immediately before the claim
|
|
58
|
+
* ("but never auto-approves Gate 2"), not in some unrelated later clause.
|
|
59
|
+
*/
|
|
60
|
+
const NEGATION_LOOKBEHIND = 48;
|
|
61
|
+
/**
|
|
62
|
+
* True when the matched claim is negated rather than asserted.
|
|
63
|
+
*
|
|
64
|
+
* Scope matters enormously here. Testing the WHOLE line lets any incidental
|
|
65
|
+
* negation elsewhere in the sentence disarm a real violation — e.g.
|
|
66
|
+
* "auto-approves Gate 2 ... so no separate human click is required" is a
|
|
67
|
+
* genuine violation whose "no" belongs to a different clause entirely. A safety
|
|
68
|
+
* lint that misses its own threat model because of a distant "no" is worse than
|
|
69
|
+
* no lint: it reports green over the exact regression it exists to catch.
|
|
70
|
+
*/
|
|
71
|
+
function isNegated(line, matchStart, matchText) {
|
|
72
|
+
const before = line.slice(Math.max(0, matchStart - NEGATION_LOOKBEHIND), matchStart);
|
|
73
|
+
return NEGATION_RE.test(before) || NEGATION_RE.test(matchText);
|
|
74
|
+
}
|
|
75
|
+
/** True when the line is nothing but the suppression comment. */
|
|
76
|
+
function isStandaloneMarker(line) {
|
|
77
|
+
return /^\s*<!--\s*lint-allow-gate-authority\s*-->\s*$/.test(line);
|
|
78
|
+
}
|
|
79
|
+
/** Scan one file's prose for statements granting automated gate authority. */
|
|
80
|
+
export function scanForGateAuthority(root, relPath) {
|
|
81
|
+
const abs = path.join(root, relPath);
|
|
82
|
+
if (!fs.existsSync(abs))
|
|
83
|
+
return [];
|
|
84
|
+
const violations = [];
|
|
85
|
+
const lines = fs.readFileSync(abs, "utf-8").split("\n");
|
|
86
|
+
lines.forEach((text, idx) => {
|
|
87
|
+
// A marker on the offending line suppresses that line. A marker on its own
|
|
88
|
+
// line above suppresses the line below — the form used where a trailing
|
|
89
|
+
// comment would render into prose (table rows, list items).
|
|
90
|
+
//
|
|
91
|
+
// The standalone requirement matters: an INLINE marker must not leak onto
|
|
92
|
+
// the next line, or one suppressed line silently disarms its neighbour.
|
|
93
|
+
if (text.includes(ALLOW_MARKER))
|
|
94
|
+
return;
|
|
95
|
+
if (idx > 0 && isStandaloneMarker(lines[idx - 1]))
|
|
96
|
+
return;
|
|
97
|
+
// Negation is evaluated per MATCH, and EVERY match is examined — not just
|
|
98
|
+
// the first. A line may state the contract and then carve an exception out
|
|
99
|
+
// of it ("Gate 2 is never auto-approved, but fast mode auto-approves Gate 2
|
|
100
|
+
// when tests pass"): the negated first clause must not excuse the second.
|
|
101
|
+
for (const { re, expected } of PATTERNS) {
|
|
102
|
+
for (const m of text.matchAll(re)) {
|
|
103
|
+
if (isNegated(text, m.index, m[0]))
|
|
104
|
+
continue;
|
|
105
|
+
violations.push({ file: relPath, line: idx + 1, found: text.trim(), expected });
|
|
106
|
+
return; // one finding per line is enough to locate and fix it
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
return violations;
|
|
111
|
+
}
|
|
112
|
+
/** Collect every `.md` file under `dir`, relative to `root`. */
|
|
113
|
+
function collectMarkdown(root, dir, acc = []) {
|
|
114
|
+
const abs = path.join(root, dir);
|
|
115
|
+
if (!fs.existsSync(abs))
|
|
116
|
+
return acc;
|
|
117
|
+
for (const entry of fs.readdirSync(abs, { withFileTypes: true })) {
|
|
118
|
+
const rel = path.join(dir, entry.name);
|
|
119
|
+
if (entry.isDirectory()) {
|
|
120
|
+
if (entry.name === "node_modules" || entry.name.startsWith(".git"))
|
|
121
|
+
continue;
|
|
122
|
+
collectMarkdown(root, rel, acc);
|
|
123
|
+
}
|
|
124
|
+
else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
125
|
+
acc.push(rel);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return acc;
|
|
129
|
+
}
|
|
130
|
+
// ─── Command-vs-skill drift ──────────────────────────────────────────────────
|
|
131
|
+
//
|
|
132
|
+
// A slash command and its same-named skill are two descriptions of one behavior,
|
|
133
|
+
// and two descriptions drift. The rule (skill-authoring-rules.md → Commands vs
|
|
134
|
+
// Skills): the command is a DISPATCHER — usage, flags, dispatch, and a concise
|
|
135
|
+
// safety note pointing at the authority. The skill owns the procedure.
|
|
136
|
+
//
|
|
137
|
+
// When a command re-states the workflow, the two copies disagree the moment
|
|
138
|
+
// either changes, and an agent reading the command follows a stale procedure it
|
|
139
|
+
// has no reason to distrust. That is exactly the `/mk:fix` defect this lint
|
|
140
|
+
// exists to keep closed: the command described a Simple path that skipped the
|
|
141
|
+
// scout its own skill marked MANDATORY.
|
|
142
|
+
//
|
|
143
|
+
// Gate semantics are NOT re-detected here — `checkGateAuthority` above is the
|
|
144
|
+
// one gate-phrase detector, deliberately shared. Two blacklists drift apart, and
|
|
145
|
+
// the weaker one becomes the de-facto contract.
|
|
146
|
+
/**
|
|
147
|
+
* The dispatchers this check contracts.
|
|
148
|
+
*
|
|
149
|
+
* Deliberately an allowlist, not "every same-name pair". Calibrating against all
|
|
150
|
+
* paired commands surfaced 7 REAL drift findings (0 false positives) in six
|
|
151
|
+
* commands outside this contract — autobuild, docs-init, help, retro, review,
|
|
152
|
+
* ship — e.g. `review.md` carries a full "### Execution Steps" procedure that
|
|
153
|
+
* duplicates its skill's. Those are pre-existing and each needs a considered
|
|
154
|
+
* rewrite, not a lint flip; widening this set is the follow-up that does them.
|
|
155
|
+
*
|
|
156
|
+
* So this set is a floor that holds the line where it was drawn, not a claim
|
|
157
|
+
* that the rest are clean. They are not. Widen it as each is thinned.
|
|
158
|
+
*/
|
|
159
|
+
const GOVERNED_DISPATCHERS = new Set(["fix", "cook", "plan", "advise"]);
|
|
160
|
+
/** Governed same-name command/skill pairs — the only files this drift check reads. */
|
|
161
|
+
function pairedCommands(root) {
|
|
162
|
+
const cmdDir = path.join(root, ".claude", "commands", "mk");
|
|
163
|
+
const skillsDir = path.join(root, ".claude", "skills");
|
|
164
|
+
if (!fs.existsSync(cmdDir) || !fs.existsSync(skillsDir))
|
|
165
|
+
return [];
|
|
166
|
+
return fs
|
|
167
|
+
.readdirSync(cmdDir)
|
|
168
|
+
.filter((f) => f.endsWith(".md"))
|
|
169
|
+
.filter((f) => GOVERNED_DISPATCHERS.has(f.replace(/\.md$/, "")))
|
|
170
|
+
// A command with no same-name skill has nowhere to delegate its procedure to,
|
|
171
|
+
// so the same prose there is correct (skill-authoring-rules.md: "not every
|
|
172
|
+
// command has a matching SKILL.md, and that is intentional").
|
|
173
|
+
.filter((f) => fs.existsSync(path.join(skillsDir, f.replace(/\.md$/, ""), "SKILL.md")))
|
|
174
|
+
.map((f) => path.join(".claude", "commands", "mk", f));
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Prose that belongs to the SKILL, found in a command file.
|
|
178
|
+
*
|
|
179
|
+
* Scoped tightly on purpose. A command legitimately carries usage, flags, a
|
|
180
|
+
* dispatch line, and a safety note — flagging those would make the lint noise,
|
|
181
|
+
* and a noisy lint gets disabled. These patterns target the two things a
|
|
182
|
+
* dispatcher can never own: step-by-step procedure, and memory writes.
|
|
183
|
+
*/
|
|
184
|
+
const COMMAND_DRIFT_PATTERNS = [
|
|
185
|
+
{
|
|
186
|
+
name: "numbered workflow steps",
|
|
187
|
+
// "1. Do X" / "2. Then Y" — a procedure. One numbered line is a list; the
|
|
188
|
+
// pattern needs a SECOND step on a later line to fire, so a lone "1." in a
|
|
189
|
+
// safety note stays clean.
|
|
190
|
+
//
|
|
191
|
+
// `[ \t]*`, never `\s*`: `\s` matches newlines, so with `m` the match could
|
|
192
|
+
// begin on the preceding blank line and report a line number one too low —
|
|
193
|
+
// which silently pointed the allow-marker lookup at the wrong line.
|
|
194
|
+
re: /^[ \t]*2\.[ \t]+\S/gm,
|
|
195
|
+
expected: "the skill owns the procedure — the command dispatches to it",
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
name: "memory-write instruction",
|
|
199
|
+
re: /\b(Edit|write|append|update)\b[^.\n]{0,40}\.claude\/memory\//gi,
|
|
200
|
+
expected: "memory writes belong to the skill's capture step, not the command",
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: "memory store schema",
|
|
204
|
+
re: /\b(fixes|review-patterns|architecture-decisions|security-findings)\.(json|md)\b/gi,
|
|
205
|
+
expected: "the store's schema is the skill's business; the command must not restate it",
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: "complexity classification table",
|
|
209
|
+
re: /\|\s*(Simple|Standard|Complex|Moderate|Parallel)\s*\|/gi,
|
|
210
|
+
expected: "classification is the skill's routing decision, not the command's",
|
|
211
|
+
},
|
|
212
|
+
];
|
|
213
|
+
/** Scan one command file for skill-owned prose. */
|
|
214
|
+
export function scanCommandForDrift(root, relPath) {
|
|
215
|
+
const abs = path.join(root, relPath);
|
|
216
|
+
if (!fs.existsSync(abs))
|
|
217
|
+
return [];
|
|
218
|
+
const findings = [];
|
|
219
|
+
const lines = fs.readFileSync(abs, "utf-8").split("\n");
|
|
220
|
+
const body = lines.join("\n");
|
|
221
|
+
for (const { re, expected } of COMMAND_DRIFT_PATTERNS) {
|
|
222
|
+
re.lastIndex = 0;
|
|
223
|
+
const m = re.exec(body);
|
|
224
|
+
if (!m)
|
|
225
|
+
continue;
|
|
226
|
+
// Locate the match's line for an actionable file:line.
|
|
227
|
+
const line = body.slice(0, m.index).split("\n").length;
|
|
228
|
+
if (lines[line - 1]?.includes(ALLOW_MARKER))
|
|
229
|
+
continue;
|
|
230
|
+
findings.push({ file: relPath, line, found: m[0].trim().slice(0, 80), expected });
|
|
231
|
+
}
|
|
232
|
+
return findings;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Command-vs-skill drift for every same-name pair.
|
|
236
|
+
*
|
|
237
|
+
* Scoped to PAIRED commands only. A standalone command (no matching skill) has
|
|
238
|
+
* nowhere to delegate its procedure to, so the same prose there is correct —
|
|
239
|
+
* per skill-authoring-rules.md, "not every command has a matching SKILL.md, and
|
|
240
|
+
* that is intentional".
|
|
241
|
+
*/
|
|
242
|
+
export function checkCommandDrift(root) {
|
|
243
|
+
const commands = pairedCommands(root);
|
|
244
|
+
if (commands.length === 0) {
|
|
245
|
+
return [{ name: "Command-vs-skill drift", status: "pass", detail: "no same-name command/skill pairs", section: "Gates" }];
|
|
246
|
+
}
|
|
247
|
+
const findings = commands.flatMap((rel) => scanCommandForDrift(root, rel));
|
|
248
|
+
if (findings.length === 0) {
|
|
249
|
+
return [
|
|
250
|
+
{
|
|
251
|
+
name: "No command-vs-skill drift",
|
|
252
|
+
status: "pass",
|
|
253
|
+
detail: `${commands.length} paired command(s) are dispatchers: ${commands.map((c) => path.basename(c, ".md")).join(", ")}`,
|
|
254
|
+
section: "Gates",
|
|
255
|
+
},
|
|
256
|
+
];
|
|
257
|
+
}
|
|
258
|
+
return [
|
|
259
|
+
{
|
|
260
|
+
name: `Command-vs-skill drift (${findings.length})`,
|
|
261
|
+
status: "fail",
|
|
262
|
+
detail: findings
|
|
263
|
+
.map((f) => `${f.file}:${f.line} found "${f.found}" — expected "${f.expected}"`)
|
|
264
|
+
.join("\n "),
|
|
265
|
+
section: "Gates",
|
|
266
|
+
},
|
|
267
|
+
];
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Run the gate-authority check over a tree of prose.
|
|
271
|
+
*
|
|
272
|
+
* `root` is the directory the scan is relative to; `scanDirs` are subtrees
|
|
273
|
+
* within it. Defaults to the canonical `.claude` tree, and is reused verbatim
|
|
274
|
+
* against the generated `plugin/` tree to prove canonical↔plugin parity.
|
|
275
|
+
*/
|
|
276
|
+
export function checkGateAuthority(root, opts = {}) {
|
|
277
|
+
const scanDirs = opts.scanDirs ?? [".claude"];
|
|
278
|
+
const present = scanDirs.filter((dir) => fs.existsSync(path.join(root, dir)));
|
|
279
|
+
if (present.length === 0) {
|
|
280
|
+
return [
|
|
281
|
+
{
|
|
282
|
+
name: "Gate-authority scan target",
|
|
283
|
+
status: opts.missingRootSeverity ?? "fail",
|
|
284
|
+
detail: `No scan target found under ${root}: ${scanDirs.join(", ")}`,
|
|
285
|
+
section: "Gates",
|
|
286
|
+
},
|
|
287
|
+
];
|
|
288
|
+
}
|
|
289
|
+
const files = present.flatMap((dir) => collectMarkdown(root, dir));
|
|
290
|
+
const violations = files.flatMap((rel) => scanForGateAuthority(root, rel));
|
|
291
|
+
if (violations.length === 0) {
|
|
292
|
+
return [
|
|
293
|
+
{
|
|
294
|
+
name: "No automated gate-approval claims",
|
|
295
|
+
status: "pass",
|
|
296
|
+
detail: `${files.length} prose files scanned across ${present.join(", ")}`,
|
|
297
|
+
section: "Gates",
|
|
298
|
+
},
|
|
299
|
+
];
|
|
300
|
+
}
|
|
301
|
+
const detail = violations
|
|
302
|
+
.map((v) => `${v.file}:${v.line} found "${v.found}" — expected "${v.expected}"`)
|
|
303
|
+
.join("\n ");
|
|
304
|
+
return [
|
|
305
|
+
{
|
|
306
|
+
name: `Automated gate-approval claims (${violations.length})`,
|
|
307
|
+
status: "fail",
|
|
308
|
+
detail,
|
|
309
|
+
section: "Gates",
|
|
310
|
+
},
|
|
311
|
+
];
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=check-gate-authority.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-gate-authority.js","sourceRoot":"","sources":["../../src/core/check-gate-authority.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CheckResult } from "../commands/validate.js";
|
|
2
|
+
export interface ParityDivergence {
|
|
3
|
+
file: string;
|
|
4
|
+
kind: "missing-in-plugin" | "stale-in-plugin" | "extra-in-plugin";
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Regenerate the payload into a temp dir and diff it against `plugin/`.
|
|
8
|
+
*
|
|
9
|
+
* Returns every divergence. The caller decides severity — `validate` fails, so
|
|
10
|
+
* that a stale plugin cannot reach a release.
|
|
11
|
+
*/
|
|
12
|
+
export declare function diffPluginParity(projectRoot: string): ParityDivergence[];
|
|
13
|
+
/** Parity as a validate CheckResult. */
|
|
14
|
+
export declare function checkPluginParity(projectRoot: string): CheckResult[];
|
|
15
|
+
//# sourceMappingURL=check-plugin-parity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-plugin-parity.d.ts","sourceRoot":"","sources":["../../src/core/check-plugin-parity.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAqC3D,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,mBAAmB,GAAG,iBAAiB,GAAG,iBAAiB,CAAC;CAClE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAuCxE;AAED,wCAAwC;AACxC,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,EAAE,CA6CpE"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { generatePluginPayload } from "./plugin-payload.js";
|
|
5
|
+
// The SAME identity `build-plugin` uses. Imported, never re-declared: a second
|
|
6
|
+
// copy of the version/description would make the manifests differ and this check
|
|
7
|
+
// would fail on its own drift — which is the defect it exists to catch.
|
|
8
|
+
import { AUTHOR, DESCRIPTION, PAYLOAD_DIRNAME } from "../commands/build-plugin.js";
|
|
9
|
+
// Canonical↔plugin parity.
|
|
10
|
+
//
|
|
11
|
+
// `plugin/` is GENERATED from `.claude/` by generatePluginPayload. Two ways that
|
|
12
|
+
// silently breaks:
|
|
13
|
+
// 1. someone edits `.claude/` and forgets to regenerate — the plugin ships the
|
|
14
|
+
// OLD behavior while the repo shows the new prose, and every reviewer reads
|
|
15
|
+
// the version that isn't shipping;
|
|
16
|
+
// 2. someone hand-edits `plugin/` — the fix survives exactly until the next
|
|
17
|
+
// regenerate, then vanishes with no diff to explain it.
|
|
18
|
+
//
|
|
19
|
+
// Both produce a repo that looks correct. This check regenerates into a scratch
|
|
20
|
+
// dir and diffs, so divergence is a build failure instead of a discovery.
|
|
21
|
+
//
|
|
22
|
+
// It is NOT covered elsewhere: `validate --plugin` checks the manifests, and
|
|
23
|
+
// check-workflow-drift.ts is a token-level prose check. Neither compares trees.
|
|
24
|
+
/** Files whose bytes legitimately differ per-run and must not fail parity. */
|
|
25
|
+
const VOLATILE = new Set(["hooks.json"]);
|
|
26
|
+
/** Every file under `dir`, relative to it. */
|
|
27
|
+
function collectFiles(dir, base = dir, acc = []) {
|
|
28
|
+
if (!fs.existsSync(dir))
|
|
29
|
+
return acc;
|
|
30
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
31
|
+
const abs = path.join(dir, entry.name);
|
|
32
|
+
if (entry.isDirectory())
|
|
33
|
+
collectFiles(abs, base, acc);
|
|
34
|
+
else if (entry.isFile())
|
|
35
|
+
acc.push(path.relative(base, abs));
|
|
36
|
+
}
|
|
37
|
+
return acc;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Regenerate the payload into a temp dir and diff it against `plugin/`.
|
|
41
|
+
*
|
|
42
|
+
* Returns every divergence. The caller decides severity — `validate` fails, so
|
|
43
|
+
* that a stale plugin cannot reach a release.
|
|
44
|
+
*/
|
|
45
|
+
export function diffPluginParity(projectRoot) {
|
|
46
|
+
const sourceDir = path.join(projectRoot, ".claude");
|
|
47
|
+
const committed = path.join(projectRoot, PAYLOAD_DIRNAME);
|
|
48
|
+
if (!fs.existsSync(sourceDir) || !fs.existsSync(committed))
|
|
49
|
+
return [];
|
|
50
|
+
const pkgPath = path.join(projectRoot, "package.json");
|
|
51
|
+
const version = fs.existsSync(pkgPath)
|
|
52
|
+
? (JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version ?? "0.0.0")
|
|
53
|
+
: "0.0.0";
|
|
54
|
+
const scratch = fs.mkdtempSync(path.join(os.tmpdir(), "mewkit-parity-"));
|
|
55
|
+
try {
|
|
56
|
+
generatePluginPayload({ sourceDir, outDir: scratch, version, description: DESCRIPTION, author: AUTHOR });
|
|
57
|
+
const expected = new Set(collectFiles(scratch));
|
|
58
|
+
const actual = new Set(collectFiles(committed));
|
|
59
|
+
const out = [];
|
|
60
|
+
for (const rel of expected) {
|
|
61
|
+
if (VOLATILE.has(path.basename(rel)))
|
|
62
|
+
continue;
|
|
63
|
+
if (!actual.has(rel)) {
|
|
64
|
+
out.push({ file: rel, kind: "missing-in-plugin" });
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const a = fs.readFileSync(path.join(scratch, rel));
|
|
68
|
+
const b = fs.readFileSync(path.join(committed, rel));
|
|
69
|
+
if (!a.equals(b))
|
|
70
|
+
out.push({ file: rel, kind: "stale-in-plugin" });
|
|
71
|
+
}
|
|
72
|
+
// A file in plugin/ that regeneration does not produce is orphaned — it
|
|
73
|
+
// survives only until the next build wipes it.
|
|
74
|
+
for (const rel of actual) {
|
|
75
|
+
if (VOLATILE.has(path.basename(rel)))
|
|
76
|
+
continue;
|
|
77
|
+
if (!expected.has(rel))
|
|
78
|
+
out.push({ file: rel, kind: "extra-in-plugin" });
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
fs.rmSync(scratch, { recursive: true, force: true });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/** Parity as a validate CheckResult. */
|
|
87
|
+
export function checkPluginParity(projectRoot) {
|
|
88
|
+
if (!fs.existsSync(path.join(projectRoot, "plugin"))) {
|
|
89
|
+
return [
|
|
90
|
+
{
|
|
91
|
+
name: "Plugin parity",
|
|
92
|
+
status: "warn",
|
|
93
|
+
detail: "no plugin/ tree — run `mewkit build-plugin`",
|
|
94
|
+
section: "Plugin",
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
}
|
|
98
|
+
let divergences;
|
|
99
|
+
try {
|
|
100
|
+
divergences = diffPluginParity(projectRoot);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
104
|
+
return [{ name: "Plugin parity", status: "fail", detail: `regeneration failed: ${msg}`, section: "Plugin" }];
|
|
105
|
+
}
|
|
106
|
+
if (divergences.length === 0) {
|
|
107
|
+
return [
|
|
108
|
+
{
|
|
109
|
+
name: "Plugin matches canonical .claude",
|
|
110
|
+
status: "pass",
|
|
111
|
+
detail: "regenerated payload is byte-identical to the committed plugin/",
|
|
112
|
+
section: "Plugin",
|
|
113
|
+
},
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
const detail = divergences
|
|
117
|
+
.slice(0, 12)
|
|
118
|
+
.map((d) => `plugin/${d.file} — ${d.kind}`)
|
|
119
|
+
.join("\n ");
|
|
120
|
+
const more = divergences.length > 12 ? `\n …and ${divergences.length - 12} more` : "";
|
|
121
|
+
return [
|
|
122
|
+
{
|
|
123
|
+
name: `Plugin diverges from canonical (${divergences.length})`,
|
|
124
|
+
status: "fail",
|
|
125
|
+
detail: `${detail}${more}\n Run \`mewkit build-plugin\`. Never hand-edit plugin/ — the next build discards it.`,
|
|
126
|
+
section: "Plugin",
|
|
127
|
+
},
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=check-plugin-parity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-plugin-parity.js","sourceRoot":"","sources":["../../src/core/check-plugin-parity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface PseudoCapabilityFinding {
|
|
2
|
+
file: string;
|
|
3
|
+
line: number;
|
|
4
|
+
found: string;
|
|
5
|
+
}
|
|
6
|
+
/** Scan one file's prose for unresolved pseudo-capability aliases. */
|
|
7
|
+
export declare function scanForPseudoCapabilities(root: string, relPath: string): PseudoCapabilityFinding[];
|
|
8
|
+
/**
|
|
9
|
+
* Scan a projected tree. Returns every finding; the caller decides severity —
|
|
10
|
+
* `build-plugin` throws, `validate` reports.
|
|
11
|
+
*/
|
|
12
|
+
export declare function findPseudoCapabilities(root: string, scanDirs?: string[]): PseudoCapabilityFinding[];
|
|
13
|
+
/** Render findings as a build-failure message with file:line locations. */
|
|
14
|
+
export declare function formatPseudoCapabilityError(findings: PseudoCapabilityFinding[]): string;
|
|
15
|
+
//# sourceMappingURL=check-pseudo-capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-pseudo-capabilities.d.ts","sourceRoot":"","sources":["../../src/core/check-pseudo-capabilities.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,uBAAuB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACd;AAaD,sEAAsE;AACtE,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,uBAAuB,EAAE,CAiBlG;AAkBD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAM,EAAU,GAAG,uBAAuB,EAAE,CAE1G;AAED,2EAA2E;AAC3E,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,uBAAuB,EAAE,GAAG,MAAM,CAUvF"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Projection-time rejection of UNRESOLVED PSEUDO-CAPABILITY ALIASES.
|
|
2
|
+
//
|
|
3
|
+
// The defect class: a skill's prose names a logical operation in a template form
|
|
4
|
+
// that was never substituted — literal text like "use `manage_plan capability`"
|
|
5
|
+
// or "delegate_agent capability" — instead of naming the real host primitive.
|
|
6
|
+
//
|
|
7
|
+
// Why it must fail the BUILD rather than warn: the string reads as if it means
|
|
8
|
+
// something. An agent following that prose looks for a "manage_plan capability",
|
|
9
|
+
// finds nothing, and improvises — which is the failure the operation table exists
|
|
10
|
+
// to prevent. It is a broken instruction that is indistinguishable from a working
|
|
11
|
+
// one until an agent acts on it, so the only safe time to catch it is before the
|
|
12
|
+
// payload ships.
|
|
13
|
+
//
|
|
14
|
+
// Scope is deliberately narrow: the four logical operation names, immediately
|
|
15
|
+
// followed by the word "capability". That phrase has no legitimate use — the
|
|
16
|
+
// operations are adapter-internal (see provider-operations.ts) and are never a
|
|
17
|
+
// "capability" in the manifest sense, so any occurrence is an unsubstituted
|
|
18
|
+
// placeholder. A broader "looks like a dangling reference" check was evaluated and
|
|
19
|
+
// rejected: every candidate it flagged in this tree was legitimate prose, and a
|
|
20
|
+
// build gate that only ever cries wolf gets switched off.
|
|
21
|
+
import fs from "node:fs";
|
|
22
|
+
import path from "node:path";
|
|
23
|
+
import { LOGICAL_OPERATIONS } from "./provider-operations.js";
|
|
24
|
+
/** Suppression marker for prose that must quote the anti-pattern (e.g. this rule's own docs). */
|
|
25
|
+
const ALLOW_MARKER = "lint-allow-pseudo-capability";
|
|
26
|
+
/**
|
|
27
|
+
* `<operation> capability` — an unsubstituted placeholder. Built from the operation
|
|
28
|
+
* list so the two cannot drift: a new operation is covered the day it is added.
|
|
29
|
+
*/
|
|
30
|
+
function pseudoCapabilityRe() {
|
|
31
|
+
return new RegExp(`\\b(${LOGICAL_OPERATIONS.join("|")})\\s+capability\\b`, "gi");
|
|
32
|
+
}
|
|
33
|
+
/** Scan one file's prose for unresolved pseudo-capability aliases. */
|
|
34
|
+
export function scanForPseudoCapabilities(root, relPath) {
|
|
35
|
+
const abs = path.join(root, relPath);
|
|
36
|
+
if (!fs.existsSync(abs))
|
|
37
|
+
return [];
|
|
38
|
+
const findings = [];
|
|
39
|
+
const re = pseudoCapabilityRe();
|
|
40
|
+
fs.readFileSync(abs, "utf-8")
|
|
41
|
+
.split("\n")
|
|
42
|
+
.forEach((text, idx) => {
|
|
43
|
+
if (text.includes(ALLOW_MARKER))
|
|
44
|
+
return;
|
|
45
|
+
re.lastIndex = 0;
|
|
46
|
+
const m = re.exec(text);
|
|
47
|
+
if (m)
|
|
48
|
+
findings.push({ file: relPath, line: idx + 1, found: m[0] });
|
|
49
|
+
});
|
|
50
|
+
return findings;
|
|
51
|
+
}
|
|
52
|
+
/** Every prose file under `dir`, relative to `root`. */
|
|
53
|
+
function collectProse(root, dir, acc = []) {
|
|
54
|
+
const abs = path.join(root, dir);
|
|
55
|
+
if (!fs.existsSync(abs))
|
|
56
|
+
return acc;
|
|
57
|
+
for (const entry of fs.readdirSync(abs, { withFileTypes: true })) {
|
|
58
|
+
const rel = path.join(dir, entry.name);
|
|
59
|
+
if (entry.isDirectory()) {
|
|
60
|
+
if (entry.name === "node_modules" || entry.name.startsWith(".git"))
|
|
61
|
+
continue;
|
|
62
|
+
collectProse(root, rel, acc);
|
|
63
|
+
}
|
|
64
|
+
else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
65
|
+
acc.push(rel);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return acc;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Scan a projected tree. Returns every finding; the caller decides severity —
|
|
72
|
+
* `build-plugin` throws, `validate` reports.
|
|
73
|
+
*/
|
|
74
|
+
export function findPseudoCapabilities(root, scanDirs = ["."]) {
|
|
75
|
+
return scanDirs.flatMap((dir) => collectProse(root, dir)).flatMap((rel) => scanForPseudoCapabilities(root, rel));
|
|
76
|
+
}
|
|
77
|
+
/** Render findings as a build-failure message with file:line locations. */
|
|
78
|
+
export function formatPseudoCapabilityError(findings) {
|
|
79
|
+
const lines = findings.map((f) => ` ${f.file}:${f.line} "${f.found}"`);
|
|
80
|
+
return [
|
|
81
|
+
`Unresolved pseudo-capability alias in ${findings.length} place(s):`,
|
|
82
|
+
...lines,
|
|
83
|
+
"",
|
|
84
|
+
"These are unsubstituted placeholders, not real capabilities. An agent that reads",
|
|
85
|
+
"one will look for something that does not exist and improvise. Name the actual",
|
|
86
|
+
"host primitive instead (see packages/mewkit/src/core/provider-operations.ts).",
|
|
87
|
+
].join("\n");
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=check-pseudo-capabilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-pseudo-capabilities.js","sourceRoot":"","sources":["../../src/core/check-pseudo-capabilities.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** Required sections in a phase file (documentation-management.md → Phase Files). */
|
|
2
|
+
export declare const REQUIRED_PHASE_SECTIONS: readonly ["Overview", "Requirements", "Implementation Steps", "Success Criteria", "Risk Assessment"];
|
|
3
|
+
export interface PhaseChecklist {
|
|
4
|
+
total: number;
|
|
5
|
+
checked: number;
|
|
6
|
+
}
|
|
7
|
+
export interface PhaseFile {
|
|
8
|
+
file: string;
|
|
9
|
+
title: string | null;
|
|
10
|
+
status: string | null;
|
|
11
|
+
phase: string | null;
|
|
12
|
+
dependencies: string[];
|
|
13
|
+
sections: string[];
|
|
14
|
+
missingSections: string[];
|
|
15
|
+
checklist: PhaseChecklist;
|
|
16
|
+
}
|
|
17
|
+
export interface PlanSummary {
|
|
18
|
+
dir: string;
|
|
19
|
+
title: string | null;
|
|
20
|
+
status: string | null;
|
|
21
|
+
planMdPresent: boolean;
|
|
22
|
+
planMdLines: number;
|
|
23
|
+
phases: PhaseFile[];
|
|
24
|
+
/** Aggregate across every phase file. */
|
|
25
|
+
checklist: PhaseChecklist;
|
|
26
|
+
issues: string[];
|
|
27
|
+
}
|
|
28
|
+
/** Parse one phase file. Throws only if the file is unreadable. */
|
|
29
|
+
export declare function parsePhaseFile(abs: string): PhaseFile;
|
|
30
|
+
/** Phase files in a plan dir, sorted by filename (which encodes phase order). */
|
|
31
|
+
export declare function listPhaseFiles(planDir: string): string[];
|
|
32
|
+
/**
|
|
33
|
+
* Parse a plan directory into a summary.
|
|
34
|
+
*
|
|
35
|
+
* `issues` names what a reader should know before trusting the numbers — a
|
|
36
|
+
* missing plan.md, an over-length overview, a phase with no checklist. They are
|
|
37
|
+
* observations, not verdicts: this is an inspector, and Gate 1 belongs to the human.
|
|
38
|
+
*/
|
|
39
|
+
export declare function parsePlan(planDir: string): PlanSummary;
|
|
40
|
+
//# sourceMappingURL=plan-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-parser.d.ts","sourceRoot":"","sources":["../../src/core/plan-parser.ts"],"names":[],"mappings":"AAkBA,qFAAqF;AACrF,eAAO,MAAM,uBAAuB,sGAM1B,CAAC;AAEX,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,SAAS,EAAE,cAAc,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,yCAAyC;IACzC,SAAS,EAAE,cAAc,CAAC;IAC1B,MAAM,EAAE,MAAM,EAAE,CAAC;CACjB;AAkCD,mEAAmE;AACnE,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAqBrD;AAED,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAMxD;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAqCtD"}
|