@event4u/agent-config 7.1.0 → 7.2.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 (75) hide show
  1. package/.claude-plugin/marketplace.json +2 -1
  2. package/CHANGELOG.md +39 -0
  3. package/README.md +1 -1
  4. package/dist/agent-src/rules/brand-source-of-truth.md +10 -0
  5. package/dist/agent-src/rules/delegation-policy.md +1 -0
  6. package/dist/agent-src/rules/legal-safety-floor.md +42 -4
  7. package/dist/agent-src/rules/output-discipline.md +85 -0
  8. package/dist/agent-src/rules/telegraph-speak.md +4 -1
  9. package/dist/agent-src/rules/token-budget-discipline.md +94 -0
  10. package/dist/agent-src/skills/accessibility-auditor/SKILL.md +20 -5
  11. package/dist/agent-src/skills/blade-ui/SKILL.md +9 -0
  12. package/dist/agent-src/skills/contract-review/SKILL.md +3 -2
  13. package/dist/agent-src/skills/dashboard-design/SKILL.md +21 -5
  14. package/dist/agent-src/skills/design-intelligence/SKILL.md +96 -0
  15. package/dist/agent-src/skills/design-review/SKILL.md +83 -0
  16. package/dist/agent-src/skills/design-system-capture/SKILL.md +234 -0
  17. package/dist/agent-src/skills/design-tokens/SKILL.md +1 -0
  18. package/dist/agent-src/skills/dpa-review/SKILL.md +3 -2
  19. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +15 -4
  20. package/dist/agent-src/skills/fe-design/SKILL.md +98 -16
  21. package/dist/agent-src/skills/flux/SKILL.md +8 -0
  22. package/dist/agent-src/skills/iconography/SKILL.md +1 -0
  23. package/dist/agent-src/skills/legal-intake-triage/SKILL.md +3 -2
  24. package/dist/agent-src/skills/legal-practice-profile/SKILL.md +3 -2
  25. package/dist/agent-src/skills/livewire/SKILL.md +8 -0
  26. package/dist/agent-src/skills/nda-triage/SKILL.md +3 -2
  27. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +21 -9
  28. package/dist/agent-src/skills/skill-writing/SKILL.md +18 -0
  29. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +7 -10
  30. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +17 -7
  31. package/dist/agent-src/skills/typography-system/SKILL.md +14 -0
  32. package/dist/agent-src/skills/ui-component-architect/SKILL.md +9 -0
  33. package/dist/discovery/deprecation-report.md +1 -1
  34. package/dist/discovery/discovery-manifest.json +123 -59
  35. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  36. package/dist/discovery/discovery-manifest.summary.md +8 -8
  37. package/dist/discovery/orphan-report.md +1 -1
  38. package/dist/discovery/packs.json +18 -15
  39. package/dist/discovery/trust-report.md +5 -5
  40. package/dist/discovery/workspaces.json +15 -12
  41. package/dist/mcp/registry-manifest.json +2 -2
  42. package/dist/router.json +1 -1
  43. package/dist/server/routes/wizard.js +4 -3
  44. package/dist/server/routes/wizard.js.map +1 -1
  45. package/dist/server/schemas/settings.js +9 -0
  46. package/dist/server/schemas/settings.js.map +1 -1
  47. package/dist/ui/assets/{index-CoQo90sz.js → index-UjcTG_9O.js} +2 -2
  48. package/dist/ui/assets/index-UjcTG_9O.js.map +1 -0
  49. package/dist/ui/index.html +1 -1
  50. package/docs/RELEASE_STORY_TEMPLATE.md +37 -0
  51. package/docs/architecture.md +3 -3
  52. package/docs/contracts/capability-boundary.md +64 -0
  53. package/docs/contracts/discovery-manifest.schema.json +2 -2
  54. package/docs/contracts/package-self-orientation.md +3 -0
  55. package/docs/contracts/subagent-boundary.md +54 -0
  56. package/docs/decisions/ADR-013-discovery-frontmatter-contract.md +13 -0
  57. package/docs/decisions/ADR-107-legal-domain-pack-adoption.md +19 -0
  58. package/docs/getting-started.md +1 -1
  59. package/docs/guidelines/agent-infra/domain-pack-architecture.md +1 -0
  60. package/docs/guidelines/design-antipatterns-triggers.json +17 -0
  61. package/docs/guidelines/design-antipatterns.md +170 -0
  62. package/docs/guidelines/design-modes.md +97 -0
  63. package/package.json +1 -1
  64. package/src/config/agent-settings.template.yml +31 -0
  65. package/src/config/discovery/packs.yml +16 -5
  66. package/src/config/discovery/workspaces.yml +4 -4
  67. package/src/scripts/check_trunk_drift.ts +67 -0
  68. package/src/scripts/lint_design_quality.ts +321 -0
  69. package/src/scripts/lint_discovery_vocabulary.ts +4 -4
  70. package/src/scripts/lint_legal_pack.ts +62 -4
  71. package/src/scripts/lint_output_slop.ts +223 -0
  72. package/src/scripts/lint_pack_risk_class.ts +79 -0
  73. package/src/scripts/schemas/agent-settings.schema.json +12 -0
  74. package/src/scripts/schemas/skill.schema.json +5 -0
  75. package/dist/ui/assets/index-CoQo90sz.js.map +0 -1
@@ -0,0 +1,223 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * lint_output_slop — detect banned placeholder-prose patterns in generated
4
+ * code and UI output. Six zero-false-positive rules; exit 2 on any finding.
5
+ *
6
+ * Scans: src/skills/**\/SKILL.md, src/rules/*.md, docs/guidelines/*.md
7
+ * (i.e., the package's own authored artifacts). Consumer projects should
8
+ * run this against their own src/ directories.
9
+ *
10
+ * Rules (all zero-false-positive in the package tree):
11
+ * P1 lorem-ipsum — Lorem ipsum / dolor sit amet filler text
12
+ * P2 bracket-placeholder — [Your ... here], <YourComponentName> patterns
13
+ * P3 impl-placeholder — // rest of component, // ... (unchanged), etc.
14
+ * P4 for-brevity — "for brevity" inside code content
15
+ * P5 ellipsis-trunc — … or ... as standalone code-continuation markers
16
+ * P6 todo-implement — // TODO.*implement (unimplemented stubs)
17
+ *
18
+ * Inline-ignore: add a comment on the same or previous line:
19
+ * lint-output-slop-disable-next-line <rule-id> [-- reason]
20
+ * lint-output-slop-disable-line <rule-id> [-- reason]
21
+ * lint-output-slop-disable-file <rule-id> [-- reason] (top of file)
22
+ *
23
+ * Exit codes: 0 = clean, 2 = findings present, 1 = internal error.
24
+ */
25
+ import fs from "node:fs";
26
+ import path from "node:path";
27
+ import { fileURLToPath } from "node:url";
28
+
29
+ const _HERE = path.dirname(fileURLToPath(import.meta.url));
30
+ const REPO_ROOT = path.resolve(_HERE, "..", "..");
31
+
32
+ interface Finding {
33
+ rule: string;
34
+ file: string;
35
+ line: number;
36
+ snippet: string;
37
+ }
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // Rule definitions — each is (id, label, regex-on-a-line-of-content)
41
+ // We only scan INSIDE fenced code blocks in markdown, and in plain .ts/.tsx
42
+ // source files. This prevents false positives from inline-ignore comments,
43
+ // documentation paragraphs, or test descriptions.
44
+ // ---------------------------------------------------------------------------
45
+ const RULES: Array<{ id: string; label: string; pattern: RegExp }> = [
46
+ {
47
+ id: "lorem-ipsum",
48
+ label: "Filler text: Lorem ipsum / dolor sit amet",
49
+ pattern: /Lorem\s+ipsum|dolor\s+sit\s+amet/i,
50
+ },
51
+ {
52
+ id: "bracket-placeholder",
53
+ label: "Bracket placeholder: [Your ... here] or <YourComponentName>",
54
+ pattern: /\[Your\s+\w[\w\s]*here\]|<Your\w+Name>/,
55
+ },
56
+ {
57
+ id: "impl-placeholder",
58
+ label: "Implementation placeholder: // rest of component / // ... (unchanged)",
59
+ pattern:
60
+ /\/\/\s*(rest\s+of\s+(component|implementation|code)|\.{3}\s*\(unchanged\)|same\s+pattern\s+follows)/i,
61
+ },
62
+ {
63
+ id: "for-brevity",
64
+ label: "Truncation excuse: 'for brevity' inside code",
65
+ pattern: /\/\/.*for\s+brevity|\/\*.*for\s+brevity.*\*\//i,
66
+ },
67
+ {
68
+ id: "ellipsis-trunc",
69
+ label: "Ellipsis-as-truncation: standalone … (Unicode) as a continuation placeholder",
70
+ // Match lines that are ONLY a Unicode ellipsis character (not `// ...`
71
+ // which is a legitimate code-documentation shorthand in SKILL.md examples).
72
+ pattern: /^\s*…\s*$/,
73
+ },
74
+ {
75
+ id: "todo-implement",
76
+ label: "Unimplemented stub: // TODO.*implement",
77
+ pattern: /\/\/\s*TODO[:\s].*implement/i,
78
+ },
79
+ ];
80
+
81
+ // ---------------------------------------------------------------------------
82
+ // File discovery
83
+ // ---------------------------------------------------------------------------
84
+ function* walkDir(dir: string, ext: RegExp): Generator<string> {
85
+ if (!fs.existsSync(dir)) return;
86
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
87
+ const full = path.join(dir, entry.name);
88
+ if (entry.isDirectory()) {
89
+ yield* walkDir(full, ext);
90
+ } else if (ext.test(entry.name)) {
91
+ yield full;
92
+ }
93
+ }
94
+ }
95
+
96
+ function collectFiles(): string[] {
97
+ const files: string[] = [];
98
+ // Package-authored markdown (skills, rules, guidelines)
99
+ for (const f of walkDir(path.join(REPO_ROOT, "src", "skills"), /SKILL\.md$/)) files.push(f);
100
+ for (const f of walkDir(path.join(REPO_ROOT, "src", "rules"), /\.md$/)) files.push(f);
101
+ for (const f of walkDir(path.join(REPO_ROOT, "docs", "guidelines"), /\.md$/)) files.push(f);
102
+ return files;
103
+ }
104
+
105
+ // ---------------------------------------------------------------------------
106
+ // Inline-ignore parsing
107
+ // ---------------------------------------------------------------------------
108
+ type IgnoreScope = "file" | "line" | "next-line";
109
+ interface Ignore {
110
+ scope: IgnoreScope;
111
+ rules: string[]; // empty = all rules
112
+ lineNum?: number;
113
+ }
114
+
115
+ function parseIgnores(lines: string[]): Ignore[] {
116
+ const ignores: Ignore[] = [];
117
+ for (let i = 0; i < lines.length; i++) {
118
+ const line = lines[i] ?? "";
119
+ const fileMatch = /lint-output-slop-disable-file\s*([\w-]+(?:,\s*[\w-]+)*)?/i.exec(line);
120
+ if (fileMatch) {
121
+ const rules = fileMatch[1] ? fileMatch[1].split(",").map((r) => r.trim()) : [];
122
+ ignores.push({ scope: "file", rules });
123
+ }
124
+ const lineMatch = /lint-output-slop-disable-line\s*([\w-]+(?:,\s*[\w-]+)*)?/i.exec(line);
125
+ if (lineMatch) {
126
+ const rules = lineMatch[1] ? lineMatch[1].split(",").map((r) => r.trim()) : [];
127
+ ignores.push({ scope: "line", rules, lineNum: i + 1 });
128
+ }
129
+ const nextMatch = /lint-output-slop-disable-next-line\s*([\w-]+(?:,\s*[\w-]+)*)?/i.exec(line);
130
+ if (nextMatch) {
131
+ const rules = nextMatch[1] ? nextMatch[1].split(",").map((r) => r.trim()) : [];
132
+ ignores.push({ scope: "next-line", rules, lineNum: i + 2 }); // 1-indexed, next line
133
+ }
134
+ }
135
+ return ignores;
136
+ }
137
+
138
+ function isIgnored(ignores: Ignore[], ruleId: string, lineNum: number): boolean {
139
+ for (const ig of ignores) {
140
+ const ruleMatch = ig.rules.length === 0 || ig.rules.includes(ruleId);
141
+ if (!ruleMatch) continue;
142
+ if (ig.scope === "file") return true;
143
+ if (ig.scope === "line" && ig.lineNum === lineNum) return true;
144
+ if (ig.scope === "next-line" && ig.lineNum === lineNum) return true;
145
+ }
146
+ return false;
147
+ }
148
+
149
+ // ---------------------------------------------------------------------------
150
+ // Scanning — only within fenced code blocks in markdown
151
+ // ---------------------------------------------------------------------------
152
+ function scanFile(filePath: string): Finding[] {
153
+ const content = fs.readFileSync(filePath, "utf-8");
154
+ const lines = content.split("\n");
155
+ const ignores = parseIgnores(lines);
156
+ const findings: Finding[] = [];
157
+
158
+ let inCodeBlock = false;
159
+ for (let i = 0; i < lines.length; i++) {
160
+ const lineNum = i + 1;
161
+ const line = lines[i] ?? "";
162
+
163
+ // Toggle code-fence state
164
+ if (/^```/.test(line) || /^~~~/.test(line)) {
165
+ inCodeBlock = !inCodeBlock;
166
+ continue;
167
+ }
168
+
169
+ // Only scan inside code fences in markdown
170
+ if (!inCodeBlock) continue;
171
+
172
+ for (const rule of RULES) {
173
+ if (rule.pattern.test(line) && !isIgnored(ignores, rule.id, lineNum)) {
174
+ findings.push({
175
+ rule: rule.id,
176
+ file: path.relative(REPO_ROOT, filePath),
177
+ line: lineNum,
178
+ snippet: line.trim().slice(0, 120),
179
+ });
180
+ }
181
+ }
182
+ }
183
+ return findings;
184
+ }
185
+
186
+ // ---------------------------------------------------------------------------
187
+ // Main
188
+ // ---------------------------------------------------------------------------
189
+ function main(): void {
190
+ const jsonMode = process.argv.includes("--json");
191
+ const quiet = process.argv.includes("--quiet");
192
+
193
+ const files = collectFiles();
194
+ const allFindings: Finding[] = [];
195
+
196
+ for (const file of files) {
197
+ try {
198
+ allFindings.push(...scanFile(file));
199
+ } catch (err) {
200
+ if (!quiet) process.stderr.write(`[lint_output_slop] error reading ${file}: ${err}\n`);
201
+ }
202
+ }
203
+
204
+ if (jsonMode) {
205
+ process.stdout.write(JSON.stringify(allFindings, null, 2) + "\n");
206
+ process.exit(allFindings.length > 0 ? 2 : 0);
207
+ }
208
+
209
+ if (allFindings.length === 0) {
210
+ if (!quiet) process.stdout.write("✅ lint_output_slop: clean — no placeholder-prose patterns found.\n");
211
+ process.exit(0);
212
+ }
213
+
214
+ for (const f of allFindings) {
215
+ process.stdout.write(`❌ ${f.file}:${f.line} [${f.rule}] ${f.snippet}\n`);
216
+ }
217
+ process.stdout.write(
218
+ `\nlint_output_slop: ${allFindings.length} finding(s). Fix or use lint-output-slop-disable-next-line <rule> -- reason.\n`,
219
+ );
220
+ process.exit(2);
221
+ }
222
+
223
+ main();
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Lint the `risk_class` invariant on packs (road-to-capability-governance Phase 2).
3
+ *
4
+ * `risk_class: high` is the regulated-advice-adjacent tier. A high-risk pack MUST
5
+ * ship conservatively — off by default, consent-gated, lab-tier — or it is a
6
+ * governance hole. This linter makes that invariant a hard CI failure.
7
+ *
8
+ * high ⇒ default_install: false AND requires_explicit_consent: true AND surface_tier: lab
9
+ *
10
+ * `medium` / `low` carry no structural requirement here (their disclaimers live
11
+ * in the domain safety floors). Absent `risk_class` = `low`.
12
+ *
13
+ * Pure core (`riskClassViolations`) takes the packs.yml text so it is unit-testable
14
+ * without the filesystem. Exit 0 = clean, 1 = at least one violation.
15
+ */
16
+
17
+ import * as fs from 'node:fs';
18
+ import * as path from 'node:path';
19
+ import { fileURLToPath, pathToFileURL } from 'node:url';
20
+
21
+ const QUIET = process.argv.includes('--quiet');
22
+ const _HERE = fileURLToPath(import.meta.url);
23
+ const REPO = path.resolve(path.dirname(_HERE), '..', '..');
24
+ const PACKS_YML = path.join(REPO, 'src', 'config', 'discovery', 'packs.yml');
25
+
26
+ export interface RiskViolation {
27
+ pack: string;
28
+ msg: string;
29
+ }
30
+
31
+ function _field(block: string, key: string): string | undefined {
32
+ return new RegExp(`(^|\\n)\\s*${key}:\\s*([^\\n#]+)`).exec(block)?.[2]?.trim();
33
+ }
34
+
35
+ /** Pure invariant check over packs.yml text. */
36
+ export function riskClassViolations(packsYml: string): RiskViolation[] {
37
+ const out: RiskViolation[] = [];
38
+ // Split into per-pack blocks on top-level `- id:` entries.
39
+ const re = /(^|\n)- id:\s*([^\n#]+?)\s*\n([\s\S]*?)(?=\n- id:|\n*$)/g;
40
+ let m: RegExpExecArray | null;
41
+ while ((m = re.exec(packsYml)) !== null) {
42
+ const id = (m[2] ?? '').trim();
43
+ const block = m[3] ?? '';
44
+ const risk = _field(block, 'risk_class') ?? 'low';
45
+ if (risk !== 'high') continue;
46
+ const checks: Array<[string, string, string]> = [
47
+ ['default_install', 'false', `${id}: risk_class high requires default_install: false`],
48
+ ['requires_explicit_consent', 'true', `${id}: risk_class high requires requires_explicit_consent: true`],
49
+ ['surface_tier', 'lab', `${id}: risk_class high requires surface_tier: lab`],
50
+ ];
51
+ for (const [key, want, msg] of checks) {
52
+ if (_field(block, key) !== want) out.push({ pack: id, msg });
53
+ }
54
+ }
55
+ return out;
56
+ }
57
+
58
+ function _print(s: string): void {
59
+ if (!QUIET) process.stdout.write(`${s}\n`);
60
+ }
61
+
62
+ export function main(): number {
63
+ if (!fs.existsSync(PACKS_YML)) {
64
+ _print('⚠️ packs.yml not found — skipping risk_class lint');
65
+ return 0;
66
+ }
67
+ const violations = riskClassViolations(fs.readFileSync(PACKS_YML, 'utf-8'));
68
+ if (violations.length === 0) {
69
+ _print('✅ risk_class invariant OK — every high-risk pack is off-by-default + consent + lab');
70
+ return 0;
71
+ }
72
+ _print(`❌ risk_class invariant — ${violations.length} violation(s):`);
73
+ for (const v of violations) _print(` - ${v.msg}`);
74
+ return 1;
75
+ }
76
+
77
+ if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
78
+ process.exit(main());
79
+ }
@@ -83,6 +83,18 @@
83
83
  "description": "How strictly the agent must follow a user-provided prototype / mockup / design system. Consumed by the design-fidelity rule. strict = build 1:1, every visible deviation needs confirmation; structural = structure locked, silent gaps fillable with a stated assumption; hard-floor = any deviation is never autonomous."
84
84
  }
85
85
  }
86
+ },
87
+ "tokens": {
88
+ "type": "object",
89
+ "additionalProperties": true,
90
+ "description": "Token-spend governance for skills marked token_budget_class: rich. See token-budget-discipline rule.",
91
+ "properties": {
92
+ "rich_skills": {
93
+ "type": "string",
94
+ "enum": ["on", "ask", "off"],
95
+ "description": "Controls whether skills marked token_budget_class: rich may load in full (exempt from telegraph-speak and thin-projector trimming). on = allowed (default); off = fall back to standard condensed behavior; ask = surface an estimated token delta (tokens, not dollars) and ask the user once per session before loading."
96
+ }
97
+ }
86
98
  }
87
99
  }
88
100
  }
@@ -182,6 +182,11 @@
182
182
  "removable": {"type": "boolean", "default": false}
183
183
  },
184
184
  "description": "ADR-013 install hints (default-on at consumer install; user-removable)."
185
+ },
186
+ "token_budget_class": {
187
+ "type": "string",
188
+ "enum": ["lean", "standard", "rich"],
189
+ "description": "Token-cost class for this skill. `lean` (default): subject to telegraph-speak condensation and thin-projector trimming; optimize for <500 tokens. `standard`: balanced guidance, normal condensation. `rich`: exempt from telegraph-speak and thin-projector trimming; detailed examples preserved; eager-load eligible; ≤15% of skills in a suite may be `rich` (CI-enforced). A `rich` skill MUST carry a '## Why this skill is rich' section explaining the irreducible complexity that makes condensation harmful. Governed by `token-budget-discipline` rule + `tokens.rich_skills` consumer setting. See docs/guidelines/token-budget-governance.md."
185
190
  }
186
191
  }
187
192
  }