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.
Files changed (69) hide show
  1. package/dist/commands/build-plugin.d.ts +6 -0
  2. package/dist/commands/build-plugin.d.ts.map +1 -1
  3. package/dist/commands/build-plugin.js +3 -3
  4. package/dist/commands/build-plugin.js.map +1 -1
  5. package/dist/commands/plan.d.ts +7 -0
  6. package/dist/commands/plan.d.ts.map +1 -0
  7. package/dist/commands/plan.js +124 -0
  8. package/dist/commands/plan.js.map +1 -0
  9. package/dist/commands/providers.js +15 -0
  10. package/dist/commands/providers.js.map +1 -1
  11. package/dist/commands/validate.d.ts +7 -1
  12. package/dist/commands/validate.d.ts.map +1 -1
  13. package/dist/commands/validate.js +58 -1
  14. package/dist/commands/validate.js.map +1 -1
  15. package/dist/core/build-inventory.d.ts +16 -0
  16. package/dist/core/build-inventory.d.ts.map +1 -1
  17. package/dist/core/build-inventory.js +35 -0
  18. package/dist/core/build-inventory.js.map +1 -1
  19. package/dist/core/capability.d.ts +24 -24
  20. package/dist/core/check-agent-conformance.d.ts +14 -0
  21. package/dist/core/check-agent-conformance.d.ts.map +1 -0
  22. package/dist/core/check-agent-conformance.js +118 -0
  23. package/dist/core/check-agent-conformance.js.map +1 -0
  24. package/dist/core/check-docs-references.d.ts.map +1 -1
  25. package/dist/core/check-docs-references.js +6 -0
  26. package/dist/core/check-docs-references.js.map +1 -1
  27. package/dist/core/check-gate-authority.d.ts +40 -0
  28. package/dist/core/check-gate-authority.d.ts.map +1 -0
  29. package/dist/core/check-gate-authority.js +313 -0
  30. package/dist/core/check-gate-authority.js.map +1 -0
  31. package/dist/core/check-plugin-parity.d.ts +15 -0
  32. package/dist/core/check-plugin-parity.d.ts.map +1 -0
  33. package/dist/core/check-plugin-parity.js +130 -0
  34. package/dist/core/check-plugin-parity.js.map +1 -0
  35. package/dist/core/check-pseudo-capabilities.d.ts +15 -0
  36. package/dist/core/check-pseudo-capabilities.d.ts.map +1 -0
  37. package/dist/core/check-pseudo-capabilities.js +89 -0
  38. package/dist/core/check-pseudo-capabilities.js.map +1 -0
  39. package/dist/core/plan-parser.d.ts +40 -0
  40. package/dist/core/plan-parser.d.ts.map +1 -0
  41. package/dist/core/plan-parser.js +126 -0
  42. package/dist/core/plan-parser.js.map +1 -0
  43. package/dist/core/plugin-payload.d.ts.map +1 -1
  44. package/dist/core/plugin-payload.js +9 -0
  45. package/dist/core/plugin-payload.js.map +1 -1
  46. package/dist/core/provider-adapter.d.ts +4 -0
  47. package/dist/core/provider-adapter.d.ts.map +1 -1
  48. package/dist/core/provider-adapter.js +2 -0
  49. package/dist/core/provider-adapter.js.map +1 -1
  50. package/dist/core/provider-operations.d.ts +51 -0
  51. package/dist/core/provider-operations.d.ts.map +1 -0
  52. package/dist/core/provider-operations.js +171 -0
  53. package/dist/core/provider-operations.js.map +1 -0
  54. package/dist/index.js +18 -0
  55. package/dist/index.js.map +1 -1
  56. package/dist/migrate/model-taxonomy.d.ts +32 -0
  57. package/dist/migrate/model-taxonomy.d.ts.map +1 -1
  58. package/dist/migrate/model-taxonomy.js +59 -0
  59. package/dist/migrate/model-taxonomy.js.map +1 -1
  60. package/dist/visual-plan/application/export-plan.d.ts.map +1 -1
  61. package/dist/visual-plan/application/export-plan.js +25 -10
  62. package/dist/visual-plan/application/export-plan.js.map +1 -1
  63. package/dist/visual-plan/domain/wireframe-theme.d.ts +24 -0
  64. package/dist/visual-plan/domain/wireframe-theme.d.ts.map +1 -0
  65. package/dist/visual-plan/domain/wireframe-theme.js +169 -0
  66. package/dist/visual-plan/domain/wireframe-theme.js.map +1 -0
  67. package/dist/visual-plan-web/index.css +1 -1
  68. package/dist/visual-plan-web/index.js +150 -14
  69. 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":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,+EAA+E;AAC/E,iFAAiF;AACjF,wEAAwE;AACxE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEnF,2BAA2B;AAC3B,EAAE;AACF,iFAAiF;AACjF,mBAAmB;AACnB,iFAAiF;AACjF,iFAAiF;AACjF,wCAAwC;AACxC,8EAA8E;AAC9E,6DAA6D;AAC7D,EAAE;AACF,gFAAgF;AAChF,0EAA0E;AAC1E,EAAE;AACF,6EAA6E;AAC7E,gFAAgF;AAEhF,8EAA8E;AAC9E,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;AAEzC,8CAA8C;AAC9C,SAAS,YAAY,CAAC,GAAW,EAAE,IAAI,GAAG,GAAG,EAAE,MAAgB,EAAE;IAChE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IACpC,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAClE,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,KAAK,CAAC,WAAW,EAAE;YAAE,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;aACjD,IAAI,KAAK,CAAC,MAAM,EAAE;YAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC;AAOD;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,WAAmB;IACnD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;IAC1D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,EAAE,CAAC;IAEtE,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;QACrC,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAA0B,CAAC,OAAO,IAAI,OAAO,CAAC;QAC9F,CAAC,CAAC,OAAO,CAAC;IAEX,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;IACzE,IAAI,CAAC;QACJ,qBAAqB,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAEzG,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC;QAChD,MAAM,GAAG,GAAuB,EAAE,CAAC;QAEnC,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;YAC5B,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBAAE,SAAS;YAC/C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtB,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC,CAAC;gBACnD,SAAS;YACV,CAAC;YACD,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;YACnD,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC;YACrD,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;gBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAC;QACpE,CAAC;QACD,wEAAwE;QACxE,+CAA+C;QAC/C,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;YAC1B,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBAAE,SAAS;YAC/C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAC;QAC1E,CAAC;QAED,OAAO,GAAG,CAAC;IACZ,CAAC;YAAS,CAAC;QACV,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;AACF,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,iBAAiB,CAAC,WAAmB;IACpD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC;QACtD,OAAO;YACN;gBACC,IAAI,EAAE,eAAe;gBACrB,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,6CAA6C;gBACrD,OAAO,EAAE,QAAQ;aACjB;SACD,CAAC;IACH,CAAC;IAED,IAAI,WAA+B,CAAC;IACpC,IAAI,CAAC;QACJ,WAAW,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnE,OAAO,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,wBAAwB,GAAG,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC9G,CAAC;IAED,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO;YACN;gBACC,IAAI,EAAE,kCAAkC;gBACxC,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,gEAAgE;gBACxE,OAAO,EAAE,QAAQ;aACjB;SACD,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,WAAW;SACxB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;SAC1C,IAAI,CAAC,aAAa,CAAC,CAAC;IACtB,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,mBAAmB,WAAW,CAAC,MAAM,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAE9F,OAAO;QACN;YACC,IAAI,EAAE,mCAAmC,WAAW,CAAC,MAAM,GAAG;YAC9D,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,+FAA+F;YACvH,OAAO,EAAE,QAAQ;SACjB;KACD,CAAC;AACH,CAAC"}
@@ -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"}