@event4u/agent-config 14.15.0 → 14.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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +84 -153
- package/README.md +1 -1
- package/dist/agent-src/commands/analyze/inbox.md +172 -6
- package/dist/agent-src/commands/fix/commit-messages.md +336 -0
- package/dist/agent-src/commands/fix.md +5 -3
- package/dist/agent-src/rules/commit-conventions.md +1 -1
- package/dist/agent-src/rules/security-sensitive-stop.md +7 -5
- package/dist/agent-src/skills/accessibility-auditor/SKILL.md +20 -2
- package/dist/agent-src/skills/aws-infrastructure/SKILL.md +41 -0
- package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +234 -4
- package/dist/agent-src/skills/design-intelligence/data/ux-guidelines.csv +99 -99
- package/dist/agent-src/skills/fe-design/SKILL.md +6 -3
- package/dist/agent-src/skills/fe-design/references/design-patterns.md +28 -0
- package/dist/agent-src/skills/iconography/SKILL.md +37 -6
- package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +1 -1
- package/dist/agent-src/skills/terraform/SKILL.md +29 -0
- package/dist/agent-src/skills/threat-modeling/data/reasoning.csv +1 -0
- package/dist/agent-src/skills/threat-modeling/data/surfaces.csv +1 -0
- package/dist/agent-src/skills/threat-modeling/data/threats.csv +5 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli-delegate/cmd_conformance.js +158 -33
- package/dist/cli-delegate/cmd_explain.js +24 -112
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +47 -21
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +6 -5
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +5 -4
- package/dist/hooks/dispatch.js +169 -41
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +160 -35
- package/dist/router.json +1 -1
- package/docs/guidelines/design-antipatterns.md +10 -2
- package/package.json +1 -1
- package/src/config/gate-violation-baselines.json +2 -2
- package/src/config/web-launch-readiness.json +1 -1
- package/src/scripts/_cli/explain_last/index.ts +1 -1
- package/src/scripts/_cli/explain_last/memory.ts +8 -104
- package/src/scripts/check_artefact_count_messaging.ts +9 -0
- package/src/scripts/check_release_highlights.ts +77 -4
- package/src/scripts/check_web_launch_readiness.ts +295 -15
- package/src/scripts/design_slop_rules.ts +19 -9
- package/src/scripts/git_authorization_hook.ts +257 -35
- package/src/scripts/hooks/block_unauthorized_git.ts +16 -4
- package/src/scripts/hooks/merge_impact.ts +68 -0
- package/src/scripts/measure_release_mix.ts +384 -0
- package/src/scripts/release.ts +4 -17
- package/src/scripts/release_drill.ts +154 -1
- package/src/scripts/release_mix_taxonomy.json +172 -0
- package/src/scripts/release_publication.ts +91 -0
- package/src/scripts/attest_artifact.ts +0 -193
package/dist/hooks/dispatch.js
CHANGED
|
@@ -25896,20 +25896,22 @@ var PHRASES = [
|
|
|
25896
25896
|
// "never auto-merge" each returned ["pr-merge"] — a prompt saying DO NOT
|
|
25897
25897
|
// MERGE authorized a merge. The noun-sense lookahead was never a negator.
|
|
25898
25898
|
//
|
|
25899
|
-
// The lookBEHIND
|
|
25900
|
-
//
|
|
25901
|
-
// its negated-claim window (`:490-493`). A sentence boundary ends a
|
|
25902
|
-
// negation's reach: "Do not push. Merge PR #12." is two instructions, and a
|
|
25903
|
-
// guard that let the first suppress the second would silently stop
|
|
25904
|
-
// authorizing merges the user DID order — the failure mode that is worse
|
|
25905
|
-
// than the defect, because nothing happens and nothing says why.
|
|
25906
|
-
//
|
|
25907
|
-
// Reusing that vocabulary rather than inventing a third is deliberate: two
|
|
25899
|
+
// The inline negative lookBEHIND that used to live in this pattern was
|
|
25900
|
+
// REMOVED on 2026-09-03, and its own comment is why. It warned that "two
|
|
25908
25901
|
// negation vocabularies in one tree drift, and the drift is invisible until
|
|
25909
|
-
// a prompt lands in the gap between them
|
|
25902
|
+
// a prompt lands in the gap between them" — and by then they had drifted:
|
|
25903
|
+
// `negatedBefore` became clause-scanned in both directions while this
|
|
25904
|
+
// lookbehind stayed a blind 30-character window with no forward half. The
|
|
25905
|
+
// prompt that landed in the gap is `"nicht pushen, aber mergen"`, which the
|
|
25906
|
+
// lookbehind refused (13 characters, inside its window, across a contrast
|
|
25907
|
+
// cue it could not see) while the clause scan correctly authorizes it.
|
|
25908
|
+
//
|
|
25909
|
+
// Every phrase match already passes through `negatedBefore` at the call
|
|
25910
|
+
// site, so the guard is not weakened by the removal — it is applied once,
|
|
25911
|
+
// by the one vocabulary this file's comment asked for.
|
|
25910
25912
|
{
|
|
25911
25913
|
op: "pr-merge",
|
|
25912
|
-
re:
|
|
25914
|
+
re: /\b(merge|merg(e|en|st|t)|zusammenf(ü|ue)hren|reinmergen)\b(?!\s*[- ]?(conflict|konflikt|commit|base|queue|state|status))/i
|
|
25913
25915
|
},
|
|
25914
25916
|
// A tag is an ACTION here — bare "Tag" is the German word for day, and
|
|
25915
25917
|
// "Version" is an ordinary noun. Both authorized a BLOCK op before this.
|
|
@@ -26031,13 +26033,16 @@ function isInterrogative(prose) {
|
|
|
26031
26033
|
if (!t) {
|
|
26032
26034
|
return false;
|
|
26033
26035
|
}
|
|
26034
|
-
|
|
26036
|
+
if (/\?\s*$/.test(t)) {
|
|
26037
|
+
return true;
|
|
26038
|
+
}
|
|
26039
|
+
const hasImperative = /\b(mach|mache|bitte|leg|lege|erstell|erstelle|f(ü|ue)hr|f(ü|ue)hre|setz|setze|starte|los|jetzt|now|asap|go ahead|do it|ja[,.]?\s|ok[,.]?\s)\b/i.test(
|
|
26035
26040
|
t
|
|
26036
26041
|
);
|
|
26037
26042
|
if (hasImperative) {
|
|
26038
26043
|
return false;
|
|
26039
26044
|
}
|
|
26040
|
-
return
|
|
26045
|
+
return /^(was|wie|warum|wieso|weshalb|wann|wer|welche[rs]?|wo|ist|sind|kann|kannst|k(ö|oe)nnen|soll|sollen|darf|d(ü|ue)rfen|why|what|how|when|which|who|is|are|can|could|should|does|do)\b/i.test(
|
|
26041
26046
|
t
|
|
26042
26047
|
);
|
|
26043
26048
|
}
|
|
@@ -26084,13 +26089,18 @@ function splitFences(prompt) {
|
|
|
26084
26089
|
function _commandLines(fence) {
|
|
26085
26090
|
return fence.split("\n").map((l) => l.trim()).filter((l) => l.length > 0).filter((l) => !/^(remote:|error:|hint:|fatal:|warning:|To |!\s|\s*\^)/i.test(l)).map((l) => l.replace(/^[$>#]\s*/, "")).filter((l) => /^(git|gh|npm|pnpm|yarn|task)\b/.test(l));
|
|
26086
26091
|
}
|
|
26092
|
+
function _errText(err) {
|
|
26093
|
+
const t = err instanceof Error ? err.message : String(err);
|
|
26094
|
+
return t.replace(/\s+/g, " ").trim().slice(0, 200);
|
|
26095
|
+
}
|
|
26096
|
+
var PR_NUMBER_MAX = 2147483647;
|
|
26087
26097
|
function extractMergeTargets(prompt) {
|
|
26088
26098
|
const out = /* @__PURE__ */ new Set();
|
|
26089
|
-
const re = /(?:#|\bpr[- ]?|\bpull[- ]request\s+#?)(\d{1,
|
|
26099
|
+
const re = /(?:#|\bpr[- ]?|\bpull[- ]request\s+#?)(\d{1,10})\b/giu;
|
|
26090
26100
|
let m;
|
|
26091
26101
|
while ((m = re.exec(prompt)) !== null) {
|
|
26092
26102
|
const n = Number.parseInt(m[1], 10);
|
|
26093
|
-
if (Number.isSafeInteger(n) && n > 0) {
|
|
26103
|
+
if (Number.isSafeInteger(n) && n > 0 && n <= PR_NUMBER_MAX) {
|
|
26094
26104
|
out.add(n);
|
|
26095
26105
|
}
|
|
26096
26106
|
}
|
|
@@ -26134,27 +26144,109 @@ function foldGrants(prior, prompt, authorized, session_id, now) {
|
|
|
26134
26144
|
];
|
|
26135
26145
|
}
|
|
26136
26146
|
var NEGATION_WORD = /\b(nicht|nichts|kein(e|en|em|er|es)?|niemals|nie|no|not|dont|don't|never|without|ohne)\b/i;
|
|
26137
|
-
|
|
26147
|
+
var CONTRAST_CUE = /\b(aber|sondern|jedoch|but|however|instead)\b/gi;
|
|
26148
|
+
var ABBREVIATION_BEFORE_DOT = /(?:^|[\s("'\u2018\u201c])(?:[a-z]|z\.b|bzw|ca|usw|d\.h|u\.a|evtl|ggf|inkl|max|min|nr|abb|e\.g|i\.e|etc|vs|approx|fig|no|st|dr|prof)\.$/i;
|
|
26149
|
+
function isSentenceBoundary(text, at) {
|
|
26150
|
+
const mark = text[at];
|
|
26151
|
+
if (mark !== ".") {
|
|
26152
|
+
return true;
|
|
26153
|
+
}
|
|
26154
|
+
if (/\d/.test(text[at - 1] ?? "") && /\d/.test(text[at + 1] ?? "")) {
|
|
26155
|
+
return false;
|
|
26156
|
+
}
|
|
26157
|
+
return !ABBREVIATION_BEFORE_DOT.test(text.slice(0, at + 1));
|
|
26158
|
+
}
|
|
26159
|
+
function sentenceBounds(text, index) {
|
|
26138
26160
|
let start = 0;
|
|
26139
|
-
for (
|
|
26140
|
-
const
|
|
26141
|
-
if (
|
|
26142
|
-
start =
|
|
26161
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
26162
|
+
const c = text[i];
|
|
26163
|
+
if ((c === "." || c === "!" || c === "?" || c === "\n") && isSentenceBoundary(text, i)) {
|
|
26164
|
+
start = i + 1;
|
|
26165
|
+
break;
|
|
26166
|
+
}
|
|
26167
|
+
}
|
|
26168
|
+
let end = text.length;
|
|
26169
|
+
for (let i = index; i < text.length; i += 1) {
|
|
26170
|
+
const c = text[i];
|
|
26171
|
+
if ((c === "." || c === "!" || c === "?" || c === "\n") && isSentenceBoundary(text, i)) {
|
|
26172
|
+
end = i;
|
|
26173
|
+
break;
|
|
26143
26174
|
}
|
|
26144
26175
|
}
|
|
26145
|
-
|
|
26146
|
-
|
|
26176
|
+
return { start, end };
|
|
26177
|
+
}
|
|
26178
|
+
function _sentences(text) {
|
|
26179
|
+
const out = [];
|
|
26180
|
+
let from = 0;
|
|
26181
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
26182
|
+
const c = text[i];
|
|
26183
|
+
if ((c === "." || c === "!" || c === "?" || c === "\n") && isSentenceBoundary(text, i)) {
|
|
26184
|
+
out.push(text.slice(from, i + 1));
|
|
26185
|
+
from = i + 1;
|
|
26186
|
+
}
|
|
26187
|
+
}
|
|
26188
|
+
if (from < text.length) {
|
|
26189
|
+
out.push(text.slice(from));
|
|
26190
|
+
}
|
|
26191
|
+
return out.filter((t) => t.trim() !== "");
|
|
26192
|
+
}
|
|
26193
|
+
function clauseStart(text, from, to) {
|
|
26194
|
+
let start = from;
|
|
26195
|
+
const seg = text.slice(from, to);
|
|
26196
|
+
for (let i = seg.length - 1; i >= 0; i -= 1) {
|
|
26197
|
+
const c = seg[i];
|
|
26198
|
+
if (c === "," || c === ";" || c === ":") {
|
|
26199
|
+
start = from + i + 1;
|
|
26200
|
+
break;
|
|
26201
|
+
}
|
|
26202
|
+
}
|
|
26203
|
+
CONTRAST_CUE.lastIndex = 0;
|
|
26204
|
+
let m;
|
|
26205
|
+
while ((m = CONTRAST_CUE.exec(seg)) !== null) {
|
|
26206
|
+
const after = from + m.index + m[0].length;
|
|
26207
|
+
if (after > start) {
|
|
26208
|
+
start = after;
|
|
26209
|
+
}
|
|
26210
|
+
}
|
|
26211
|
+
return start;
|
|
26212
|
+
}
|
|
26213
|
+
function clauseEnd(text, from, to) {
|
|
26214
|
+
const seg = text.slice(from, to);
|
|
26215
|
+
let end = to;
|
|
26216
|
+
const punct = seg.search(/[,;:]/);
|
|
26217
|
+
if (punct !== -1) {
|
|
26218
|
+
end = from + punct;
|
|
26219
|
+
}
|
|
26220
|
+
CONTRAST_CUE.lastIndex = 0;
|
|
26221
|
+
const m = CONTRAST_CUE.exec(seg);
|
|
26222
|
+
if (m !== null && from + m.index < end) {
|
|
26223
|
+
end = from + m.index;
|
|
26224
|
+
}
|
|
26225
|
+
return end;
|
|
26226
|
+
}
|
|
26227
|
+
function negatedBefore(text, index, matched = "") {
|
|
26228
|
+
const { start, end } = sentenceBounds(text, index);
|
|
26229
|
+
const before = text.slice(clauseStart(text, start, index), index);
|
|
26230
|
+
const afterFrom = index + matched.length;
|
|
26231
|
+
const after = afterFrom < end ? text.slice(afterFrom, clauseEnd(text, afterFrom, end)) : "";
|
|
26232
|
+
return NEGATION_WORD.test(before) || NEGATION_WORD.test(matched) || NEGATION_WORD.test(after);
|
|
26147
26233
|
}
|
|
26148
26234
|
function classifyAuthorization(prompt) {
|
|
26149
26235
|
const { prose, fences } = splitFences(prompt);
|
|
26150
26236
|
const authorized = /* @__PURE__ */ new Set();
|
|
26151
26237
|
const evidence = {};
|
|
26152
26238
|
const instruction = prose.split("\n").filter((l) => !OUTPUT_LINE.test(l)).join("\n");
|
|
26153
|
-
for (const { op, re } of
|
|
26154
|
-
const
|
|
26155
|
-
|
|
26156
|
-
|
|
26157
|
-
|
|
26239
|
+
for (const { op, re } of PHRASES) {
|
|
26240
|
+
for (const sentence of _sentences(instruction)) {
|
|
26241
|
+
if (isInterrogative(sentence)) {
|
|
26242
|
+
continue;
|
|
26243
|
+
}
|
|
26244
|
+
const m = re.exec(sentence);
|
|
26245
|
+
if (m && !negatedBefore(sentence, m.index, m[0])) {
|
|
26246
|
+
authorized.add(op);
|
|
26247
|
+
evidence[op] = `prose: "${m[0]}"`;
|
|
26248
|
+
break;
|
|
26249
|
+
}
|
|
26158
26250
|
}
|
|
26159
26251
|
}
|
|
26160
26252
|
for (const fence of fences) {
|
|
@@ -26232,7 +26324,11 @@ function consumeGrantTarget(consumer_root, session_id, target) {
|
|
|
26232
26324
|
if (touched) {
|
|
26233
26325
|
try {
|
|
26234
26326
|
atomic_write_json(file, decoded);
|
|
26235
|
-
} catch {
|
|
26327
|
+
} catch (err) {
|
|
26328
|
+
process.stderr.write(
|
|
26329
|
+
`git-authorization: grant-consumption write failed (${_errText(err)}) \u2014 the target stays UNSPENT, so one further merge of a PR the user named will still be authorized. Authorization outcome unchanged.
|
|
26330
|
+
`
|
|
26331
|
+
);
|
|
26236
26332
|
}
|
|
26237
26333
|
}
|
|
26238
26334
|
}
|
|
@@ -26325,7 +26421,11 @@ function run10(stdin_text, options) {
|
|
|
26325
26421
|
};
|
|
26326
26422
|
try {
|
|
26327
26423
|
atomic_write_json(path55.join(options.consumer_root, ledgerFileFor(session)), ledger);
|
|
26328
|
-
} catch {
|
|
26424
|
+
} catch (err) {
|
|
26425
|
+
process.stderr.write(
|
|
26426
|
+
`git-authorization: session-ledger write failed (${_errText(err)}) \u2014 the gate degrades to "no ledger" for this session, which denies the irreversible subset and warns on the rest. Authorization outcome unchanged.
|
|
26427
|
+
`
|
|
26428
|
+
);
|
|
26329
26429
|
}
|
|
26330
26430
|
return EXIT_ALLOW10;
|
|
26331
26431
|
}
|
|
@@ -26389,6 +26489,24 @@ var DESTRUCTIVE_MARKERS = [
|
|
|
26389
26489
|
{ label: "a route or endpoint was removed", re: /^-.*\b(Route::(get|post|put|patch|delete)|@(Get|Post|Put|Patch|Delete)Mapping|app\.(get|post|put|patch|delete)\s*\()/m },
|
|
26390
26490
|
{ label: "a whole file was deleted", re: /^deleted file mode /m }
|
|
26391
26491
|
];
|
|
26492
|
+
var SCHEMA_REMOVAL_BODY = /\b(dropColumn|dropTable|dropIfExists|removeColumn|drop_column|drop_table|DROP\s+(TABLE|COLUMN|INDEX|CONSTRAINT)|TRUNCATE\s+TABLE)\b/i;
|
|
26493
|
+
var RENAME_METADATA = /^rename (?:from|to) /m;
|
|
26494
|
+
function fileBlocks(patch) {
|
|
26495
|
+
return patch.split(/^(?=diff --git )/m).filter((b) => b.startsWith("diff --git "));
|
|
26496
|
+
}
|
|
26497
|
+
function renamedSchemaRemoval(patch) {
|
|
26498
|
+
for (const block of fileBlocks(patch)) {
|
|
26499
|
+
if (!RENAME_METADATA.test(block)) {
|
|
26500
|
+
continue;
|
|
26501
|
+
}
|
|
26502
|
+
for (const line of block.split("\n")) {
|
|
26503
|
+
if (line.startsWith("-") && !line.startsWith("---") && SCHEMA_REMOVAL_BODY.test(line)) {
|
|
26504
|
+
return true;
|
|
26505
|
+
}
|
|
26506
|
+
}
|
|
26507
|
+
}
|
|
26508
|
+
return false;
|
|
26509
|
+
}
|
|
26392
26510
|
var MAJOR_BUMP = /^-\s*"version"\s*:\s*"(\d+)\.[^"]*"[\s\S]{0,200}?^\+\s*"version"\s*:\s*"(\d+)\./m;
|
|
26393
26511
|
function classifyDiff(patch) {
|
|
26394
26512
|
if (patch.trim() === "") {
|
|
@@ -26400,6 +26518,9 @@ function classifyDiff(patch) {
|
|
|
26400
26518
|
markers.push(label);
|
|
26401
26519
|
}
|
|
26402
26520
|
}
|
|
26521
|
+
if (renamedSchemaRemoval(patch)) {
|
|
26522
|
+
markers.push("a renamed file drops a column or table");
|
|
26523
|
+
}
|
|
26403
26524
|
const bump = MAJOR_BUMP.exec(patch);
|
|
26404
26525
|
if (bump && bump[1] !== bump[2]) {
|
|
26405
26526
|
markers.push(`major version bump ${bump[1]}.x to ${bump[2]}.x`);
|
|
@@ -26821,12 +26942,16 @@ function commandOp(command) {
|
|
|
26821
26942
|
return found;
|
|
26822
26943
|
}
|
|
26823
26944
|
function mergeTargetOf(command) {
|
|
26824
|
-
const gh = /\bgh\s+pr\s+merge\s+(?:-{1,2}\S+\s+)*?(\d{1,
|
|
26945
|
+
const gh = /\bgh\s+pr\s+merge\s+(?:-{1,2}\S+\s+)*?(\d{1,10})\b/i.exec(command);
|
|
26825
26946
|
if (gh) {
|
|
26826
|
-
return
|
|
26947
|
+
return _boundedTarget(gh[1]);
|
|
26827
26948
|
}
|
|
26828
|
-
const api = /\/pulls\/(\d{1,
|
|
26829
|
-
return api ?
|
|
26949
|
+
const api = /\/pulls\/(\d{1,10})\/merge\b/i.exec(command);
|
|
26950
|
+
return api ? _boundedTarget(api[1]) : null;
|
|
26951
|
+
}
|
|
26952
|
+
function _boundedTarget(digits) {
|
|
26953
|
+
const n = Number.parseInt(digits, 10);
|
|
26954
|
+
return Number.isSafeInteger(n) && n > 0 && n <= PR_NUMBER_MAX ? n : null;
|
|
26830
26955
|
}
|
|
26831
26956
|
function grantCovers(grants, op, target) {
|
|
26832
26957
|
if (op !== "pr-merge" || target === null) {
|
|
@@ -28740,14 +28865,17 @@ var SLOP_RULES = [
|
|
|
28740
28865
|
catalogId: "V1",
|
|
28741
28866
|
severity: "P1",
|
|
28742
28867
|
engines: ["css"],
|
|
28743
|
-
description: "Colored
|
|
28744
|
-
message: "
|
|
28745
|
-
gated: (ctx) => ctx.has("side stripe", "side-stripe", "stripe accent", "accent border"),
|
|
28746
|
-
detect: ({ content }) => cssBlocks(content).filter((b) => !/blockquote|\bquote\b|<q>/i.test(b.selector)).filter(
|
|
28747
|
-
|
|
28748
|
-
|
|
28749
|
-
)
|
|
28750
|
-
|
|
28868
|
+
description: "Colored accent border > 1px \u2014 a side stripe, a four-sided colored border, or a gradient border-image (V1)",
|
|
28869
|
+
message: "Colored accent border reads as scaffold-default. Remove it, or declare an intentional accent border in DESIGN.md (V1).",
|
|
28870
|
+
gated: (ctx) => ctx.has("side stripe", "side-stripe", "stripe accent", "accent border", "card border"),
|
|
28871
|
+
detect: ({ content }) => cssBlocks(content).filter((b) => !/blockquote|\bquote\b|<q>/i.test(b.selector)).filter((b) => {
|
|
28872
|
+
const COLOR = "(?!transparent|currentcolor|inherit|initial|unset)(?:#|rgb|hsl|oklch|var\\(|[a-z])";
|
|
28873
|
+
const WIDTH = "(?:[2-9]|\\d{2,})px";
|
|
28874
|
+
const stripe = new RegExp(`border-(?:left|right)\\s*:\\s*${WIDTH}\\s+\\w+\\s+${COLOR}`, "i");
|
|
28875
|
+
const foursided = new RegExp(`border\\s*:\\s*${WIDTH}\\s+\\w+\\s+${COLOR}`, "i");
|
|
28876
|
+
const gradient = /border-image\s*:[^;]*(?:linear|conic|radial)-gradient/i;
|
|
28877
|
+
return stripe.test(b.body) || foursided.test(b.body) || gradient.test(b.body);
|
|
28878
|
+
}).map((b) => ({ line: b.line, snippet: b.selector.slice(0, 80) }))
|
|
28751
28879
|
},
|
|
28752
28880
|
{
|
|
28753
28881
|
id: "slop-v3-ghost-card",
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"discovery": {
|
|
3
|
-
"artefact_count":
|
|
3
|
+
"artefact_count": 624,
|
|
4
4
|
"scanner_version": "de0a22b4ac4c"
|
|
5
5
|
},
|
|
6
|
-
"generated_at": "2026-09-
|
|
6
|
+
"generated_at": "2026-09-05",
|
|
7
7
|
"package": {
|
|
8
8
|
"description": "Governed skills, rules and commands for AI coding tools (Claude Code, Cursor, Windsurf, Copilot) \u2014 every claim machine-checked, including the counts in these badges.",
|
|
9
9
|
"homepage": "https://event4u-app.github.io/agent-config",
|
|
10
10
|
"name": "@event4u/agent-config",
|
|
11
11
|
"repository": "https://github.com/event4u-app/agent-config",
|
|
12
|
-
"version": "14.
|
|
12
|
+
"version": "14.17.0"
|
|
13
13
|
},
|
|
14
14
|
"registries": [
|
|
15
15
|
{
|
package/dist/mcp/server.json
CHANGED
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
"transport": {
|
|
10
10
|
"type": "stdio"
|
|
11
11
|
},
|
|
12
|
-
"version": "14.
|
|
12
|
+
"version": "14.17.0"
|
|
13
13
|
}
|
|
14
14
|
],
|
|
15
15
|
"repository": {
|
|
16
16
|
"source": "github",
|
|
17
17
|
"url": "https://github.com/event4u-app/agent-config"
|
|
18
18
|
},
|
|
19
|
-
"version": "14.
|
|
19
|
+
"version": "14.17.0"
|
|
20
20
|
}
|
package/dist/mcp/server.mjs
CHANGED
|
@@ -37256,13 +37256,16 @@ function isInterrogative(prose) {
|
|
|
37256
37256
|
if (!t) {
|
|
37257
37257
|
return false;
|
|
37258
37258
|
}
|
|
37259
|
-
|
|
37259
|
+
if (/\?\s*$/.test(t)) {
|
|
37260
|
+
return true;
|
|
37261
|
+
}
|
|
37262
|
+
const hasImperative = /\b(mach|mache|bitte|leg|lege|erstell|erstelle|f(ü|ue)hr|f(ü|ue)hre|setz|setze|starte|los|jetzt|now|asap|go ahead|do it|ja[,.]?\s|ok[,.]?\s)\b/i.test(
|
|
37260
37263
|
t
|
|
37261
37264
|
);
|
|
37262
37265
|
if (hasImperative) {
|
|
37263
37266
|
return false;
|
|
37264
37267
|
}
|
|
37265
|
-
return
|
|
37268
|
+
return /^(was|wie|warum|wieso|weshalb|wann|wer|welche[rs]?|wo|ist|sind|kann|kannst|k(ö|oe)nnen|soll|sollen|darf|d(ü|ue)rfen|why|what|how|when|which|who|is|are|can|could|should|does|do)\b/i.test(
|
|
37266
37269
|
t
|
|
37267
37270
|
);
|
|
37268
37271
|
}
|
|
@@ -37289,13 +37292,17 @@ function splitFences(prompt) {
|
|
|
37289
37292
|
function _commandLines(fence) {
|
|
37290
37293
|
return fence.split("\n").map((l) => l.trim()).filter((l) => l.length > 0).filter((l) => !/^(remote:|error:|hint:|fatal:|warning:|To |!\s|\s*\^)/i.test(l)).map((l) => l.replace(/^[$>#]\s*/, "")).filter((l) => /^(git|gh|npm|pnpm|yarn|task)\b/.test(l));
|
|
37291
37294
|
}
|
|
37295
|
+
function _errText(err) {
|
|
37296
|
+
const t = err instanceof Error ? err.message : String(err);
|
|
37297
|
+
return t.replace(/\s+/g, " ").trim().slice(0, 200);
|
|
37298
|
+
}
|
|
37292
37299
|
function extractMergeTargets(prompt) {
|
|
37293
37300
|
const out = /* @__PURE__ */ new Set();
|
|
37294
|
-
const re = /(?:#|\bpr[- ]?|\bpull[- ]request\s+#?)(\d{1,
|
|
37301
|
+
const re = /(?:#|\bpr[- ]?|\bpull[- ]request\s+#?)(\d{1,10})\b/giu;
|
|
37295
37302
|
let m;
|
|
37296
37303
|
while ((m = re.exec(prompt)) !== null) {
|
|
37297
37304
|
const n = Number.parseInt(m[1], 10);
|
|
37298
|
-
if (Number.isSafeInteger(n) && n > 0) {
|
|
37305
|
+
if (Number.isSafeInteger(n) && n > 0 && n <= PR_NUMBER_MAX) {
|
|
37299
37306
|
out.add(n);
|
|
37300
37307
|
}
|
|
37301
37308
|
}
|
|
@@ -37337,27 +37344,107 @@ function foldGrants(prior, prompt, authorized, session_id, now) {
|
|
|
37337
37344
|
}
|
|
37338
37345
|
];
|
|
37339
37346
|
}
|
|
37340
|
-
function
|
|
37347
|
+
function isSentenceBoundary(text, at) {
|
|
37348
|
+
const mark = text[at];
|
|
37349
|
+
if (mark !== ".") {
|
|
37350
|
+
return true;
|
|
37351
|
+
}
|
|
37352
|
+
if (/\d/.test(text[at - 1] ?? "") && /\d/.test(text[at + 1] ?? "")) {
|
|
37353
|
+
return false;
|
|
37354
|
+
}
|
|
37355
|
+
return !ABBREVIATION_BEFORE_DOT.test(text.slice(0, at + 1));
|
|
37356
|
+
}
|
|
37357
|
+
function sentenceBounds(text, index) {
|
|
37341
37358
|
let start = 0;
|
|
37342
|
-
for (
|
|
37343
|
-
const
|
|
37344
|
-
if (
|
|
37345
|
-
start =
|
|
37359
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
37360
|
+
const c = text[i];
|
|
37361
|
+
if ((c === "." || c === "!" || c === "?" || c === "\n") && isSentenceBoundary(text, i)) {
|
|
37362
|
+
start = i + 1;
|
|
37363
|
+
break;
|
|
37364
|
+
}
|
|
37365
|
+
}
|
|
37366
|
+
let end = text.length;
|
|
37367
|
+
for (let i = index; i < text.length; i += 1) {
|
|
37368
|
+
const c = text[i];
|
|
37369
|
+
if ((c === "." || c === "!" || c === "?" || c === "\n") && isSentenceBoundary(text, i)) {
|
|
37370
|
+
end = i;
|
|
37371
|
+
break;
|
|
37372
|
+
}
|
|
37373
|
+
}
|
|
37374
|
+
return { start, end };
|
|
37375
|
+
}
|
|
37376
|
+
function _sentences(text) {
|
|
37377
|
+
const out = [];
|
|
37378
|
+
let from = 0;
|
|
37379
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
37380
|
+
const c = text[i];
|
|
37381
|
+
if ((c === "." || c === "!" || c === "?" || c === "\n") && isSentenceBoundary(text, i)) {
|
|
37382
|
+
out.push(text.slice(from, i + 1));
|
|
37383
|
+
from = i + 1;
|
|
37384
|
+
}
|
|
37385
|
+
}
|
|
37386
|
+
if (from < text.length) {
|
|
37387
|
+
out.push(text.slice(from));
|
|
37388
|
+
}
|
|
37389
|
+
return out.filter((t) => t.trim() !== "");
|
|
37390
|
+
}
|
|
37391
|
+
function clauseStart(text, from, to) {
|
|
37392
|
+
let start = from;
|
|
37393
|
+
const seg = text.slice(from, to);
|
|
37394
|
+
for (let i = seg.length - 1; i >= 0; i -= 1) {
|
|
37395
|
+
const c = seg[i];
|
|
37396
|
+
if (c === "," || c === ";" || c === ":") {
|
|
37397
|
+
start = from + i + 1;
|
|
37398
|
+
break;
|
|
37346
37399
|
}
|
|
37347
37400
|
}
|
|
37348
|
-
|
|
37349
|
-
|
|
37401
|
+
CONTRAST_CUE.lastIndex = 0;
|
|
37402
|
+
let m;
|
|
37403
|
+
while ((m = CONTRAST_CUE.exec(seg)) !== null) {
|
|
37404
|
+
const after = from + m.index + m[0].length;
|
|
37405
|
+
if (after > start) {
|
|
37406
|
+
start = after;
|
|
37407
|
+
}
|
|
37408
|
+
}
|
|
37409
|
+
return start;
|
|
37410
|
+
}
|
|
37411
|
+
function clauseEnd(text, from, to) {
|
|
37412
|
+
const seg = text.slice(from, to);
|
|
37413
|
+
let end = to;
|
|
37414
|
+
const punct = seg.search(/[,;:]/);
|
|
37415
|
+
if (punct !== -1) {
|
|
37416
|
+
end = from + punct;
|
|
37417
|
+
}
|
|
37418
|
+
CONTRAST_CUE.lastIndex = 0;
|
|
37419
|
+
const m = CONTRAST_CUE.exec(seg);
|
|
37420
|
+
if (m !== null && from + m.index < end) {
|
|
37421
|
+
end = from + m.index;
|
|
37422
|
+
}
|
|
37423
|
+
return end;
|
|
37424
|
+
}
|
|
37425
|
+
function negatedBefore(text, index, matched = "") {
|
|
37426
|
+
const { start, end } = sentenceBounds(text, index);
|
|
37427
|
+
const before = text.slice(clauseStart(text, start, index), index);
|
|
37428
|
+
const afterFrom = index + matched.length;
|
|
37429
|
+
const after = afterFrom < end ? text.slice(afterFrom, clauseEnd(text, afterFrom, end)) : "";
|
|
37430
|
+
return NEGATION_WORD.test(before) || NEGATION_WORD.test(matched) || NEGATION_WORD.test(after);
|
|
37350
37431
|
}
|
|
37351
37432
|
function classifyAuthorization(prompt) {
|
|
37352
37433
|
const { prose, fences } = splitFences(prompt);
|
|
37353
37434
|
const authorized = /* @__PURE__ */ new Set();
|
|
37354
37435
|
const evidence = {};
|
|
37355
37436
|
const instruction = prose.split("\n").filter((l) => !OUTPUT_LINE.test(l)).join("\n");
|
|
37356
|
-
for (const { op, re } of
|
|
37357
|
-
const
|
|
37358
|
-
|
|
37359
|
-
|
|
37360
|
-
|
|
37437
|
+
for (const { op, re } of PHRASES) {
|
|
37438
|
+
for (const sentence of _sentences(instruction)) {
|
|
37439
|
+
if (isInterrogative(sentence)) {
|
|
37440
|
+
continue;
|
|
37441
|
+
}
|
|
37442
|
+
const m = re.exec(sentence);
|
|
37443
|
+
if (m && !negatedBefore(sentence, m.index, m[0])) {
|
|
37444
|
+
authorized.add(op);
|
|
37445
|
+
evidence[op] = `prose: "${m[0]}"`;
|
|
37446
|
+
break;
|
|
37447
|
+
}
|
|
37361
37448
|
}
|
|
37362
37449
|
}
|
|
37363
37450
|
for (const fence of fences) {
|
|
@@ -37435,7 +37522,11 @@ function consumeGrantTarget(consumer_root, session_id, target) {
|
|
|
37435
37522
|
if (touched) {
|
|
37436
37523
|
try {
|
|
37437
37524
|
atomic_write_json(file, decoded);
|
|
37438
|
-
} catch {
|
|
37525
|
+
} catch (err) {
|
|
37526
|
+
process.stderr.write(
|
|
37527
|
+
`git-authorization: grant-consumption write failed (${_errText(err)}) \u2014 the target stays UNSPENT, so one further merge of a PR the user named will still be authorized. Authorization outcome unchanged.
|
|
37528
|
+
`
|
|
37529
|
+
);
|
|
37439
37530
|
}
|
|
37440
37531
|
}
|
|
37441
37532
|
}
|
|
@@ -37528,7 +37619,11 @@ function run2(stdin_text, options) {
|
|
|
37528
37619
|
};
|
|
37529
37620
|
try {
|
|
37530
37621
|
atomic_write_json(path66.join(options.consumer_root, ledgerFileFor(session)), ledger);
|
|
37531
|
-
} catch {
|
|
37622
|
+
} catch (err) {
|
|
37623
|
+
process.stderr.write(
|
|
37624
|
+
`git-authorization: session-ledger write failed (${_errText(err)}) \u2014 the gate degrades to "no ledger" for this session, which denies the irreversible subset and warns on the rest. Authorization outcome unchanged.
|
|
37625
|
+
`
|
|
37626
|
+
);
|
|
37532
37627
|
}
|
|
37533
37628
|
return EXIT_ALLOW2;
|
|
37534
37629
|
}
|
|
@@ -37565,7 +37660,7 @@ function _isCliEntry19() {
|
|
|
37565
37660
|
return false;
|
|
37566
37661
|
}
|
|
37567
37662
|
}
|
|
37568
|
-
var EXIT_ALLOW2, STATE_FILE2, STATE_DIR2, PENDING_DIR, PENDING_FILE, PENDING_MAX_AGE_MS, ALL_OPS, PHRASES, OUTPUT_LINE, PASTED_COMMANDS, REVOKE_RE, NEGATION_WORD;
|
|
37663
|
+
var EXIT_ALLOW2, STATE_FILE2, STATE_DIR2, PENDING_DIR, PENDING_FILE, PENDING_MAX_AGE_MS, ALL_OPS, PHRASES, OUTPUT_LINE, PASTED_COMMANDS, PR_NUMBER_MAX, REVOKE_RE, NEGATION_WORD, CONTRAST_CUE, ABBREVIATION_BEFORE_DOT;
|
|
37569
37664
|
var init_git_authorization_hook = __esm({
|
|
37570
37665
|
"src/scripts/git_authorization_hook.ts"() {
|
|
37571
37666
|
"use strict";
|
|
@@ -37634,20 +37729,22 @@ var init_git_authorization_hook = __esm({
|
|
|
37634
37729
|
// "never auto-merge" each returned ["pr-merge"] — a prompt saying DO NOT
|
|
37635
37730
|
// MERGE authorized a merge. The noun-sense lookahead was never a negator.
|
|
37636
37731
|
//
|
|
37637
|
-
// The lookBEHIND
|
|
37638
|
-
//
|
|
37639
|
-
// its negated-claim window (`:490-493`). A sentence boundary ends a
|
|
37640
|
-
// negation's reach: "Do not push. Merge PR #12." is two instructions, and a
|
|
37641
|
-
// guard that let the first suppress the second would silently stop
|
|
37642
|
-
// authorizing merges the user DID order — the failure mode that is worse
|
|
37643
|
-
// than the defect, because nothing happens and nothing says why.
|
|
37644
|
-
//
|
|
37645
|
-
// Reusing that vocabulary rather than inventing a third is deliberate: two
|
|
37732
|
+
// The inline negative lookBEHIND that used to live in this pattern was
|
|
37733
|
+
// REMOVED on 2026-09-03, and its own comment is why. It warned that "two
|
|
37646
37734
|
// negation vocabularies in one tree drift, and the drift is invisible until
|
|
37647
|
-
// a prompt lands in the gap between them
|
|
37735
|
+
// a prompt lands in the gap between them" — and by then they had drifted:
|
|
37736
|
+
// `negatedBefore` became clause-scanned in both directions while this
|
|
37737
|
+
// lookbehind stayed a blind 30-character window with no forward half. The
|
|
37738
|
+
// prompt that landed in the gap is `"nicht pushen, aber mergen"`, which the
|
|
37739
|
+
// lookbehind refused (13 characters, inside its window, across a contrast
|
|
37740
|
+
// cue it could not see) while the clause scan correctly authorizes it.
|
|
37741
|
+
//
|
|
37742
|
+
// Every phrase match already passes through `negatedBefore` at the call
|
|
37743
|
+
// site, so the guard is not weakened by the removal — it is applied once,
|
|
37744
|
+
// by the one vocabulary this file's comment asked for.
|
|
37648
37745
|
{
|
|
37649
37746
|
op: "pr-merge",
|
|
37650
|
-
re:
|
|
37747
|
+
re: /\b(merge|merg(e|en|st|t)|zusammenf(ü|ue)hren|reinmergen)\b(?!\s*[- ]?(conflict|konflikt|commit|base|queue|state|status))/i
|
|
37651
37748
|
},
|
|
37652
37749
|
// A tag is an ACTION here — bare "Tag" is the German word for day, and
|
|
37653
37750
|
// "Version" is an ordinary noun. Both authorized a BLOCK op before this.
|
|
@@ -37784,8 +37881,11 @@ var init_git_authorization_hook = __esm({
|
|
|
37784
37881
|
{ op: "commit", re: /\bgit\s+commit\b/i },
|
|
37785
37882
|
{ op: "branch", re: /\bgit\s+(checkout\s+-b|switch\s+-c|branch)\b/i }
|
|
37786
37883
|
];
|
|
37884
|
+
PR_NUMBER_MAX = 2147483647;
|
|
37787
37885
|
REVOKE_RE = /\b(stop|stopp|halt|abbrechen|abbruch|cancel|revoke|widerruf(en)?|zur(ü|ue)ck(nehmen|ziehen)|hold\s+(everything|on|the\s+merge)|warte|wait)\b|(?:\b(nicht|kein(e|en)?|no|not|dont|don't|never|niemals|nie)\b[^.!?\n]{0,30}\b(merg(e|en|ing)|zusammenf(ü|ue)hren)\b)/iu;
|
|
37788
37886
|
NEGATION_WORD = /\b(nicht|nichts|kein(e|en|em|er|es)?|niemals|nie|no|not|dont|don't|never|without|ohne)\b/i;
|
|
37887
|
+
CONTRAST_CUE = /\b(aber|sondern|jedoch|but|however|instead)\b/gi;
|
|
37888
|
+
ABBREVIATION_BEFORE_DOT = /(?:^|[\s("'\u2018\u201c])(?:[a-z]|z\.b|bzw|ca|usw|d\.h|u\.a|evtl|ggf|inkl|max|min|nr|abb|e\.g|i\.e|etc|vs|approx|fig|no|st|dr|prof)\.$/i;
|
|
37789
37889
|
if (_isCliEntry19()) {
|
|
37790
37890
|
process.exit(main23());
|
|
37791
37891
|
}
|
|
@@ -37794,6 +37894,22 @@ var init_git_authorization_hook = __esm({
|
|
|
37794
37894
|
|
|
37795
37895
|
// src/scripts/hooks/merge_impact.ts
|
|
37796
37896
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
37897
|
+
function fileBlocks(patch) {
|
|
37898
|
+
return patch.split(/^(?=diff --git )/m).filter((b) => b.startsWith("diff --git "));
|
|
37899
|
+
}
|
|
37900
|
+
function renamedSchemaRemoval(patch) {
|
|
37901
|
+
for (const block of fileBlocks(patch)) {
|
|
37902
|
+
if (!RENAME_METADATA.test(block)) {
|
|
37903
|
+
continue;
|
|
37904
|
+
}
|
|
37905
|
+
for (const line of block.split("\n")) {
|
|
37906
|
+
if (line.startsWith("-") && !line.startsWith("---") && SCHEMA_REMOVAL_BODY.test(line)) {
|
|
37907
|
+
return true;
|
|
37908
|
+
}
|
|
37909
|
+
}
|
|
37910
|
+
}
|
|
37911
|
+
return false;
|
|
37912
|
+
}
|
|
37797
37913
|
function classifyDiff(patch) {
|
|
37798
37914
|
if (patch.trim() === "") {
|
|
37799
37915
|
return { verdict: "undecidable", markers: [], reason: "empty diff" };
|
|
@@ -37804,6 +37920,9 @@ function classifyDiff(patch) {
|
|
|
37804
37920
|
markers.push(label);
|
|
37805
37921
|
}
|
|
37806
37922
|
}
|
|
37923
|
+
if (renamedSchemaRemoval(patch)) {
|
|
37924
|
+
markers.push("a renamed file drops a column or table");
|
|
37925
|
+
}
|
|
37807
37926
|
const bump = MAJOR_BUMP.exec(patch);
|
|
37808
37927
|
if (bump && bump[1] !== bump[2]) {
|
|
37809
37928
|
markers.push(`major version bump ${bump[1]}.x to ${bump[2]}.x`);
|
|
@@ -37861,7 +37980,7 @@ function describeImpact(pr, impact) {
|
|
|
37861
37980
|
return `Impact scan: could not read the diff for PR #${pr} (${impact.reason ?? "unknown"}), so nothing is known about what it changes. ${say}`;
|
|
37862
37981
|
}
|
|
37863
37982
|
}
|
|
37864
|
-
var DESTRUCTIVE_MARKERS, MAJOR_BUMP;
|
|
37983
|
+
var DESTRUCTIVE_MARKERS, SCHEMA_REMOVAL_BODY, RENAME_METADATA, MAJOR_BUMP;
|
|
37865
37984
|
var init_merge_impact = __esm({
|
|
37866
37985
|
"src/scripts/hooks/merge_impact.ts"() {
|
|
37867
37986
|
"use strict";
|
|
@@ -37881,6 +38000,8 @@ var init_merge_impact = __esm({
|
|
|
37881
38000
|
{ label: "a route or endpoint was removed", re: /^-.*\b(Route::(get|post|put|patch|delete)|@(Get|Post|Put|Patch|Delete)Mapping|app\.(get|post|put|patch|delete)\s*\()/m },
|
|
37882
38001
|
{ label: "a whole file was deleted", re: /^deleted file mode /m }
|
|
37883
38002
|
];
|
|
38003
|
+
SCHEMA_REMOVAL_BODY = /\b(dropColumn|dropTable|dropIfExists|removeColumn|drop_column|drop_table|DROP\s+(TABLE|COLUMN|INDEX|CONSTRAINT)|TRUNCATE\s+TABLE)\b/i;
|
|
38004
|
+
RENAME_METADATA = /^rename (?:from|to) /m;
|
|
37884
38005
|
MAJOR_BUMP = /^-\s*"version"\s*:\s*"(\d+)\.[^"]*"[\s\S]{0,200}?^\+\s*"version"\s*:\s*"(\d+)\./m;
|
|
37885
38006
|
}
|
|
37886
38007
|
});
|
|
@@ -38076,12 +38197,16 @@ function commandOp(command) {
|
|
|
38076
38197
|
return found;
|
|
38077
38198
|
}
|
|
38078
38199
|
function mergeTargetOf(command) {
|
|
38079
|
-
const gh = /\bgh\s+pr\s+merge\s+(?:-{1,2}\S+\s+)*?(\d{1,
|
|
38200
|
+
const gh = /\bgh\s+pr\s+merge\s+(?:-{1,2}\S+\s+)*?(\d{1,10})\b/i.exec(command);
|
|
38080
38201
|
if (gh) {
|
|
38081
|
-
return
|
|
38202
|
+
return _boundedTarget(gh[1]);
|
|
38082
38203
|
}
|
|
38083
|
-
const api = /\/pulls\/(\d{1,
|
|
38084
|
-
return api ?
|
|
38204
|
+
const api = /\/pulls\/(\d{1,10})\/merge\b/i.exec(command);
|
|
38205
|
+
return api ? _boundedTarget(api[1]) : null;
|
|
38206
|
+
}
|
|
38207
|
+
function _boundedTarget(digits) {
|
|
38208
|
+
const n = Number.parseInt(digits, 10);
|
|
38209
|
+
return Number.isSafeInteger(n) && n > 0 && n <= PR_NUMBER_MAX ? n : null;
|
|
38085
38210
|
}
|
|
38086
38211
|
function grantCovers(grants, op, target) {
|
|
38087
38212
|
if (op !== "pr-merge" || target === null) {
|