@event4u/agent-config 9.33.0 → 9.35.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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +154 -145
- package/CONTRIBUTING.md +23 -14
- package/dist/agent-src/commands/agent-handoff.md +15 -3
- package/dist/agent-src/commands/roadmap/next.md +37 -5
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +35 -1
- package/dist/agent-src/scripts/roadmap_gates.ts +119 -4
- package/dist/agent-src/skills/corpus-grounding/SKILL.md +21 -12
- package/dist/agent-src/skills/design-tokens/SKILL.md +15 -0
- package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +24 -0
- package/dist/agent-src/skills/rule-writing/SKILL.md +23 -0
- package/dist/agent-src/skills/skill-writing/SKILL.md +80 -2
- package/dist/agent-src/skills/systematic-debugging/SKILL.md +5 -3
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +24 -0
- package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +25 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/scripts/memory_lookup.ts +1 -2
- package/dist/agent-src/templates/scripts/memory_signal.ts +2 -4
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_gate.ts +35 -5
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/staged_confirmation.ts +252 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/staged_confirmation_store.ts +307 -0
- package/dist/cli/registry.js +2 -2
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/{chunk-PMQ6Z3T3.js → chunk-2TGMGLBL.js} +11 -4
- package/dist/cli-delegate/{chunk-YPAWXXXR.js → chunk-55RI5UXK.js} +17 -0
- package/dist/cli-delegate/{chunk-MA4GFWDY.js → chunk-H6F2CQFI.js} +1 -1
- package/dist/cli-delegate/{chunk-2WELHO3P.js → chunk-I5EAFRII.js} +35 -0
- package/dist/cli-delegate/{chunk-3EWYC3YK.js → chunk-UEZ7D35A.js} +4 -3
- package/dist/cli-delegate/{chunk-UVKNIJH2.js → chunk-ZAXH3ZWU.js} +3 -0
- package/dist/cli-delegate/cmd_conformance.js +2 -2
- package/dist/cli-delegate/cmd_doctor.js +2 -2
- package/dist/cli-delegate/cmd_export.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +3 -3
- package/dist/cli-delegate/cmd_memory_get.js +1 -1
- package/dist/cli-delegate/cmd_prune.js +2 -2
- package/dist/cli-delegate/cmd_route_audit.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +7 -2
- package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +2 -2
- package/dist/cli-delegate/cmd_uninstall.js +2 -2
- package/dist/cli-delegate/cmd_validate.js +2 -2
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +13 -13
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +2 -2
- package/dist/discovery/dormancy-report.md +17 -0
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +3 -3
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +3 -3
- package/dist/hooks/dispatch.js +355 -85
- package/dist/install/install.mjs +3 -0
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +192 -12
- package/dist/server/schemas/settings.js +1 -0
- package/dist/server/schemas/settings.js.map +1 -1
- package/docs/contracts/settings-classes.md +3 -2
- package/docs/guidelines/agent-infra/failure-signatures.md +47 -12
- package/package.json +2 -2
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +35 -1
- package/src/agent-src/scripts/roadmap_gates.ts +119 -4
- package/src/agent-src/templates/scripts/memory_lookup.ts +1 -2
- package/src/agent-src/templates/scripts/memory_signal.ts +2 -4
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/decision_gate.ts +35 -5
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/staged_confirmation.ts +252 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/staged_confirmation_store.ts +307 -0
- package/src/config/agent-settings.template.yml +11 -2
- package/src/config/quorum-attendance-budget.json +11 -3
- package/src/scripts/_cli/cmd_session_recycle.ts +6 -1
- package/src/scripts/_dispatch.bash +6 -2
- package/src/scripts/_lib/env_kill_switch.ts +37 -0
- package/src/scripts/_lib/security_lint.ts +18 -4
- package/src/scripts/_lib/self_repair.ts +60 -0
- package/src/scripts/_lib/self_repair_store.ts +65 -5
- package/src/scripts/_lib/session_register.ts +67 -1
- package/src/scripts/_lib/subagent_capsule.ts +78 -1
- package/src/scripts/ai_council/config.ts +60 -0
- package/src/scripts/ai_council/events_log.ts +105 -6
- package/src/scripts/ai_council/orchestrator.ts +38 -0
- package/src/scripts/ai_council/prompts.ts +165 -3
- package/src/scripts/ai_council/quorum.ts +87 -0
- package/src/scripts/ai_team/review_gate.ts +2 -2
- package/src/scripts/build_discovery_manifest.ts +165 -0
- package/src/scripts/check_claims.ts +51 -2
- package/src/scripts/ci_time_ratio.ts +3 -1
- package/src/scripts/council_cli.ts +36 -1
- package/src/scripts/discovery_graph.ts +201 -27
- package/src/scripts/dispatch_r2_reviewer.ts +102 -1
- package/src/scripts/hooks/turn_end_gate_hook.ts +172 -11
- package/src/scripts/lint_framework_leakage.ts +195 -28
- package/src/scripts/lint_framework_leakage_allowlist.json +94 -86
- package/src/scripts/lint_handoffs.ts +92 -2
- package/src/scripts/lint_hedge_words.ts +317 -0
- package/src/scripts/lint_mcp_config_security.ts +1 -1
- package/src/scripts/lint_skill_frontmatter_safety.ts +1 -1
- package/src/scripts/memory_lookup.ts +1 -2
- package/src/scripts/schemas/command.schema.json +4 -0
- package/src/scripts/schemas/skill.schema.json +4 -0
- package/src/scripts/self_repair_cli.ts +26 -1
- package/src/scripts/session_register_hook.ts +238 -18
- package/src/scripts/sessions_cli.ts +377 -12
- package/src/scripts/skill_linter.ts +58 -2
- package/src/server/schemas/settings.ts +3 -0
|
@@ -1,152 +1,160 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
|
-
"_doc": "Each entry: { file: relative path from repo root,
|
|
3
|
+
"_doc": "Each entry: { file: relative path from repo root, anchor: a substring of the exempted line (the ONLY per-line key — position keys are refused by validate_allowlist), or lines: \"*\" for the whole file, reason: short justification }. Use sparingly — first ask whether the file should be neutralized or carved out. Policy cap: > 20 exempted FILES means the linter (or the content) is wrong — fix the tool or the text, never grow the list. The cap counts files because that is the unit it was written in, when one entry meant one file; entries are now per-line, so the two numbers differ on purpose (15 files / 31 entries as of the anchor migration) and the entry count is not the cap. Rebuilt 2026-07-12 (road-to-opt-hygiene-and-debt Phase 1): 104 -> minimal set after heuristic upgrades + 25 content neutralizations + 2 carve-outs. Re-keyed to content anchors 2026-08-12: 8 of the 39 exempted lines were found to exempt nothing — three named blank or out-of-range lines, five named lines that produce no hit — and were dropped rather than carried forward; the gate now refuses both shapes.",
|
|
4
4
|
"entries": [
|
|
5
|
-
{
|
|
6
|
-
"file": "src/rules/code-comment-discipline.md",
|
|
7
|
-
"lines": [
|
|
8
|
-
100
|
|
9
|
-
],
|
|
10
|
-
"reason": "Worked PHP docblock demo inside the rule's own wrong/right example block — quoted teaching content."
|
|
11
|
-
},
|
|
12
5
|
{
|
|
13
6
|
"file": "src/skills/api-testing/SKILL.md",
|
|
14
|
-
"
|
|
15
|
-
218
|
|
16
|
-
],
|
|
7
|
+
"anchor": "- Don't test framework internals (e.g., \"does Laravel return",
|
|
17
8
|
"reason": "Quoted example of a framework-internal behavior explicitly marked as what NOT to test — meta content, not a mandate."
|
|
18
9
|
},
|
|
19
10
|
{
|
|
20
11
|
"file": "src/skills/learning-to-rule-or-skill/SKILL.md",
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
"anchor": "Decision: Create focused skill for Laravel route inspection ",
|
|
13
|
+
"reason": "Example learnings/decisions quoted verbatim in the promotion-flow walkthrough — meta content."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"file": "src/skills/learning-to-rule-or-skill/SKILL.md",
|
|
17
|
+
"anchor": "Learning: \"I forgot to run PHPStan once.\"",
|
|
25
18
|
"reason": "Example learnings/decisions quoted verbatim in the promotion-flow walkthrough — meta content."
|
|
26
19
|
},
|
|
27
20
|
{
|
|
28
21
|
"file": "src/skills/markitdown/SKILL.md",
|
|
29
|
-
"
|
|
30
|
-
74,
|
|
31
|
-
111
|
|
32
|
-
],
|
|
22
|
+
"anchor": "uv pip install 'markitdown-mcp==0.0.1a4'",
|
|
33
23
|
"reason": "Tool-intrinsic install lines — markitdown IS a pip package; the install command is the tool's identity, not stack leakage."
|
|
34
24
|
},
|
|
25
|
+
{
|
|
26
|
+
"file": "src/skills/markitdown/SKILL.md",
|
|
27
|
+
"anchor": "Plugins enable arbitrary code paths inside the conversion pi",
|
|
28
|
+
"reason": "Tool-intrinsic install lines — markitdown IS a pip package; the install command is the tool's identity, not stack leakage."
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"file": "src/skills/module-management/SKILL.md",
|
|
32
|
+
"anchor": "### Laravel HMVC carve-out",
|
|
33
|
+
"reason": "Canonical multi-stack module catalogue (Laravel HMVC / Symfony DDD / Node / Python sections) — keep-in-place is a recorded council decision; per-stack sections sit farther apart than the ±10-line window."
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"file": "src/skills/module-management/SKILL.md",
|
|
37
|
+
"anchor": "│ ├── api.php web.php console.php",
|
|
38
|
+
"reason": "Canonical multi-stack module catalogue (Laravel HMVC / Symfony DDD / Node / Python sections) — keep-in-place is a recorded council decision; per-stack sections sit farther apart than the ±10-line window."
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"file": "src/skills/module-management/SKILL.md",
|
|
42
|
+
"anchor": "**Auto-loading:** `app/Providers/ModuleServiceProvider.php` ",
|
|
43
|
+
"reason": "Canonical multi-stack module catalogue (Laravel HMVC / Symfony DDD / Node / Python sections) — keep-in-place is a recorded council decision; per-stack sections sit farther apart than the ±10-line window."
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"file": "src/skills/module-management/SKILL.md",
|
|
47
|
+
"anchor": "`app/Modules/` and registers routes (`Routes/api.php` → `/ap",
|
|
48
|
+
"reason": "Canonical multi-stack module catalogue (Laravel HMVC / Symfony DDD / Node / Python sections) — keep-in-place is a recorded council decision; per-stack sections sit farther apart than the ±10-line window."
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"file": "src/skills/module-management/SKILL.md",
|
|
52
|
+
"anchor": "`api` middleware; `Routes/web.php` → `web` middleware) plus ",
|
|
53
|
+
"reason": "Canonical multi-stack module catalogue (Laravel HMVC / Symfony DDD / Node / Python sections) — keep-in-place is a recorded council decision; per-stack sections sit farther apart than the ±10-line window."
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"file": "src/skills/module-management/SKILL.md",
|
|
57
|
+
"anchor": "// Routes/api.php — auto-prefixed /api",
|
|
58
|
+
"reason": "Canonical multi-stack module catalogue (Laravel HMVC / Symfony DDD / Node / Python sections) — keep-in-place is a recorded council decision; per-stack sections sit farther apart than the ±10-line window."
|
|
59
|
+
},
|
|
35
60
|
{
|
|
36
61
|
"file": "src/skills/module-management/SKILL.md",
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
115,
|
|
44
|
-
124,
|
|
45
|
-
156,
|
|
46
|
-
172
|
|
47
|
-
],
|
|
62
|
+
"anchor": "### Symfony DDD-lite carve-out",
|
|
63
|
+
"reason": "Canonical multi-stack module catalogue (Laravel HMVC / Symfony DDD / Node / Python sections) — keep-in-place is a recorded council decision; per-stack sections sit farther apart than the ±10-line window."
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"file": "src/skills/module-management/SKILL.md",
|
|
67
|
+
"anchor": "metadata in `pyproject.toml`.",
|
|
48
68
|
"reason": "Canonical multi-stack module catalogue (Laravel HMVC / Symfony DDD / Node / Python sections) — keep-in-place is a recorded council decision; per-stack sections sit farther apart than the ±10-line window."
|
|
49
69
|
},
|
|
50
70
|
{
|
|
51
71
|
"file": "src/skills/po-discovery/SKILL.md",
|
|
52
|
-
"
|
|
53
|
-
45
|
|
54
|
-
],
|
|
72
|
+
"anchor": "(\"upgrade Laravel to 11\") — discovery adds noise; skip.",
|
|
55
73
|
"reason": "Quoted example ticket title ('upgrade Laravel to 11') illustrating when discovery adds noise — meta content."
|
|
56
74
|
},
|
|
57
75
|
{
|
|
58
76
|
"file": "src/skills/repomix-packer/SKILL.md",
|
|
59
|
-
"
|
|
60
|
-
44
|
|
61
|
-
],
|
|
77
|
+
"anchor": "npm install -g repomix",
|
|
62
78
|
"reason": "Tool-intrinsic install line — repomix IS an npm package."
|
|
63
79
|
},
|
|
64
80
|
{
|
|
65
81
|
"file": "src/skills/rule-writing/SKILL.md",
|
|
66
|
-
"
|
|
67
|
-
78
|
|
68
|
-
],
|
|
82
|
+
"anchor": "description: \"Writing or reviewing PHP code — strict types, ",
|
|
69
83
|
"reason": "Quoted rule-description example (the php-coding rule's own description string) — meta content. Re-anchored 76→78 after the description-cap edit above it."
|
|
70
84
|
},
|
|
71
|
-
{
|
|
72
|
-
"file": "src/skills/security-audit/SKILL.md",
|
|
73
|
-
"lines": [
|
|
74
|
-
122
|
|
75
|
-
],
|
|
76
|
-
"reason": "Multi-ecosystem XSS row (Blade/JSX/Jinja peers on one line) + a quoted false-positive example ('Laravel's CSRF is already handled') — documentation, not a mandate."
|
|
77
|
-
},
|
|
78
85
|
{
|
|
79
86
|
"file": "src/skills/skill-reviewer/SKILL.md",
|
|
80
|
-
"
|
|
81
|
-
70
|
|
82
|
-
],
|
|
87
|
+
"anchor": "- ❌ Describing how Laravel middleware works (in a middlewar",
|
|
83
88
|
"reason": "Quoted anti-example ('❌ Describing how Laravel middleware works') — the line teaches what NOT to write."
|
|
84
89
|
},
|
|
85
90
|
{
|
|
86
91
|
"file": "src/skills/skill-writing/SKILL.md",
|
|
87
|
-
"
|
|
88
|
-
148,
|
|
89
|
-
456
|
|
90
|
-
],
|
|
92
|
+
"anchor": "Use when creating Laravel middleware for request filtering",
|
|
91
93
|
"reason": "Quoted description examples + the 'too-broad skill (Laravel skill)' anti-pattern — meta content."
|
|
92
94
|
},
|
|
93
95
|
{
|
|
94
96
|
"file": "src/skills/supply-chain-intake/SKILL.md",
|
|
95
|
-
"
|
|
96
|
-
80,
|
|
97
|
-
82
|
|
98
|
-
],
|
|
97
|
+
"anchor": "rg -n '\"[^\"]+\":\\s*\"(\\^|~|\\*|latest)' package.json",
|
|
99
98
|
"reason": "Per-ecosystem check commands in fenced blocks; the pip/composer siblings sit farther apart than the ±10-line window — genuine multi-ecosystem documentation."
|
|
100
99
|
},
|
|
101
100
|
{
|
|
102
|
-
"file": "src/skills/
|
|
103
|
-
"
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
"reason": "
|
|
101
|
+
"file": "src/skills/supply-chain-intake/SKILL.md",
|
|
102
|
+
"anchor": "[ -f package.json ] && [ ! -f package-lock.json ] && echo \"n",
|
|
103
|
+
"reason": "Per-ecosystem check commands in fenced blocks; the pip/composer siblings sit farther apart than the ±10-line window — genuine multi-ecosystem documentation."
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"file": "src/skills/testing-anti-patterns/SKILL.md",
|
|
107
|
+
"anchor": "Replacement: a `tests/Support/cleanupSession.php` helper or ",
|
|
108
|
+
"reason": "Multi-ecosystem capture-tools line and skip-marker grep spanning pytest/PHPUnit/JS in one pattern — documentation across stacks."
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"file": "src/skills/testing-anti-patterns/SKILL.md",
|
|
112
|
+
"anchor": "**Concrete capture tools** for recording the real shape: `cu",
|
|
113
|
+
"reason": "Multi-ecosystem capture-tools line and skip-marker grep spanning pytest/PHPUnit/JS in one pattern — documentation across stacks."
|
|
110
114
|
},
|
|
111
115
|
{
|
|
112
116
|
"file": "src/skills/testing-anti-patterns/SKILL.md",
|
|
113
|
-
"
|
|
114
|
-
87,
|
|
115
|
-
124,
|
|
116
|
-
185
|
|
117
|
-
],
|
|
117
|
+
"anchor": "rg -n '\\b(it|test|describe)\\.skip\\b|\\.only\\b|xit\\(|xdescribe",
|
|
118
118
|
"reason": "Multi-ecosystem capture-tools line and skip-marker grep spanning pytest/PHPUnit/JS in one pattern — documentation across stacks."
|
|
119
119
|
},
|
|
120
120
|
{
|
|
121
121
|
"file": "src/skills/test-driven-development/SKILL.md",
|
|
122
|
-
"
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
122
|
+
"anchor": "### Example A — PHP / Pest",
|
|
123
|
+
"reason": "Example A of a deliberate A/B pair: '### Example A — PHP / Pest' (L228) sits opposite '### Example B — TypeScript / Vitest' (L259). That is framework-neutrality's textbook 'multi-stack peers' shape, but the two headings are 31 lines apart and the cross-stack window is +/-10, so the heuristic cannot see the sibling. Allowlisted rather than widening the window for a single document — if a second per-stack A/B skill appears, build the section-aware window instead of adding a second entry here."
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"file": "src/skills/test-driven-development/SKILL.md",
|
|
127
|
+
"anchor": "// tests/Unit/EmailValidatorTest.php — RED",
|
|
128
|
+
"reason": "Example A of a deliberate A/B pair: '### Example A — PHP / Pest' (L228) sits opposite '### Example B — TypeScript / Vitest' (L259). That is framework-neutrality's textbook 'multi-stack peers' shape, but the two headings are 31 lines apart and the cross-stack window is +/-10, so the heuristic cannot see the sibling. Allowlisted rather than widening the window for a single document — if a second per-stack A/B skill appears, build the section-aware window instead of adding a second entry here."
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"file": "src/skills/test-driven-development/SKILL.md",
|
|
132
|
+
"anchor": "Run: `./vendor/bin/pest --filter='rejects empty email'` → fa",
|
|
133
|
+
"reason": "Example A of a deliberate A/B pair: '### Example A — PHP / Pest' (L228) sits opposite '### Example B — TypeScript / Vitest' (L259). That is framework-neutrality's textbook 'multi-stack peers' shape, but the two headings are 31 lines apart and the cross-stack window is +/-10, so the heuristic cannot see the sibling. Allowlisted rather than widening the window for a single document — if a second per-stack A/B skill appears, build the section-aware window instead of adding a second entry here."
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"file": "src/skills/test-driven-development/SKILL.md",
|
|
137
|
+
"anchor": "// app/Validators/EmailValidator.php — GREEN (minimum)",
|
|
128
138
|
"reason": "Example A of a deliberate A/B pair: '### Example A — PHP / Pest' (L228) sits opposite '### Example B — TypeScript / Vitest' (L259). That is framework-neutrality's textbook 'multi-stack peers' shape, but the two headings are 31 lines apart and the cross-stack window is +/-10, so the heuristic cannot see the sibling. Allowlisted rather than widening the window for a single document — if a second per-stack A/B skill appears, build the section-aware window instead of adding a second entry here."
|
|
129
139
|
},
|
|
130
140
|
{
|
|
131
141
|
"file": "src/skills/image-editing/SKILL.md",
|
|
132
|
-
"
|
|
133
|
-
48
|
|
134
|
-
],
|
|
142
|
+
"anchor": "`node_modules/@event4u/agent-config/src/scripts/ai-image/ada",
|
|
135
143
|
"reason": "npm install-location path of THIS package's own image-adapter scripts (node_modules/@event4u/agent-config/...) — the factual consumer path after the consumer-internal-ref decoupling (road-to-credible-install Phase 4), not a JS-stack mandate."
|
|
136
144
|
},
|
|
137
145
|
{
|
|
138
146
|
"file": "src/skills/image-generation/SKILL.md",
|
|
139
|
-
"
|
|
140
|
-
|
|
141
|
-
|
|
147
|
+
"anchor": "`node_modules/@event4u/agent-config/src/scripts/ai-image/ada",
|
|
148
|
+
"reason": "npm install-location path of THIS package's own image-adapter scripts (node_modules/@event4u/agent-config/...) — the factual consumer path after the consumer-internal-ref decoupling (road-to-credible-install Phase 4), not a JS-stack mandate."
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"file": "src/skills/prompt-engineering-image/SKILL.md",
|
|
152
|
+
"anchor": "`node_modules/@event4u/agent-config/src/scripts/ai-image/adapters/<provider>.sh`",
|
|
142
153
|
"reason": "npm install-location path of THIS package's own image-adapter scripts (node_modules/@event4u/agent-config/...) — the factual consumer path after the consumer-internal-ref decoupling (road-to-credible-install Phase 4), not a JS-stack mandate."
|
|
143
154
|
},
|
|
144
155
|
{
|
|
145
156
|
"file": "src/skills/prompt-engineering-image/SKILL.md",
|
|
146
|
-
"
|
|
147
|
-
74,
|
|
148
|
-
115
|
|
149
|
-
],
|
|
157
|
+
"anchor": "- `node_modules/@event4u/agent-config/src/scripts/ai-image/a",
|
|
150
158
|
"reason": "npm install-location path of THIS package's own image-adapter scripts (node_modules/@event4u/agent-config/...) — the factual consumer path after the consumer-internal-ref decoupling (road-to-credible-install Phase 4), not a JS-stack mandate."
|
|
151
159
|
}
|
|
152
160
|
]
|
|
@@ -360,6 +360,91 @@ export function validate_handoff_artifact(text: string): string[] {
|
|
|
360
360
|
return missing;
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
+
/**
|
|
364
|
+
* The body of a `## <heading>` section, up to the next heading of the same or a
|
|
365
|
+
* higher level. Fenced blocks are stripped first so a `##` inside a code fence
|
|
366
|
+
* cannot end the section early — a resume artefact routinely quotes commands.
|
|
367
|
+
*/
|
|
368
|
+
export function handoff_section_body(text: string, heading: string): string | null {
|
|
369
|
+
const withoutFences = text.replace(/^```[\s\S]*?^```\s*$/gm, '');
|
|
370
|
+
const start = new RegExp(`^##\\s+${heading}\\s*$`, 'mi').exec(withoutFences);
|
|
371
|
+
if (start === null) {
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
const rest = withoutFences.slice(start.index + start[0].length);
|
|
375
|
+
const next = /^#{1,2}\s+\S/m.exec(rest);
|
|
376
|
+
return next === null ? rest : rest.slice(0, next.index);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// An explicit "there are none" answer. Kept as a closed list rather than a
|
|
380
|
+
// heuristic so the escape cannot be widened by accident: a section saying
|
|
381
|
+
// nothing and a section saying "none" are different facts, and only the first is
|
|
382
|
+
// a defect.
|
|
383
|
+
const _NO_OPEN_QUESTIONS = new Set(['none', 'keine', 'n/a', 'na', 'no open questions', 'nothing']);
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Strip list markers, checkbox glyphs, emphasis and trailing sentence
|
|
387
|
+
* punctuation. The trailing strip is skipped when it would empty the line: a
|
|
388
|
+
* line that is ONLY punctuation (`...`) is a placeholder to name, not an empty
|
|
389
|
+
* section to report as blank, and the two findings say different things.
|
|
390
|
+
*/
|
|
391
|
+
function _bareLine(line: string): string {
|
|
392
|
+
const stripped = line
|
|
393
|
+
.trim()
|
|
394
|
+
.replace(/^[-*+]\s*/, '')
|
|
395
|
+
.replace(/^\[[ x~-]\]\s*/i, '')
|
|
396
|
+
.replace(/[*_`]/g, '')
|
|
397
|
+
.trim()
|
|
398
|
+
.toLowerCase();
|
|
399
|
+
const trimmed = stripped.replace(/[.!]+$/, '').trim();
|
|
400
|
+
return trimmed.length > 0 ? trimmed : stripped;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Shape check on the one required section whose emptiness is the defect rather
|
|
405
|
+
* than a style nit. `validate_handoff_artifact` only tests that the heading
|
|
406
|
+
* exists, so `## Open questions` followed by nothing passed while carrying
|
|
407
|
+
* nothing — and a resume artefact whose open questions are blank hands the next
|
|
408
|
+
* session a false all-clear.
|
|
409
|
+
*
|
|
410
|
+
* **The step this implements claimed the false-positive class was empty because
|
|
411
|
+
* one `?`-terminated line is required. It is not, and the counterexample ships
|
|
412
|
+
* in this repo:** `tests/scripts/lint_handoffs_artifact.test.ts` carries
|
|
413
|
+
* `## Open questions` / `- none`, which is an honest answer and would be forced
|
|
414
|
+
* to grow a fake question. So the check accepts EITHER a real question OR an
|
|
415
|
+
* explicit none-marker, and fires only on a section that answers neither —
|
|
416
|
+
* blank, or a bare placeholder. That keeps the defect the step names without
|
|
417
|
+
* turning a truthful "none" into a lint error.
|
|
418
|
+
*
|
|
419
|
+
* Returns a finding string, or null when the shape holds. A MISSING section is
|
|
420
|
+
* not this check's business — `validate_handoff_artifact` already reports it.
|
|
421
|
+
*/
|
|
422
|
+
export function validate_handoff_open_questions(text: string): string | null {
|
|
423
|
+
const body = handoff_section_body(text, 'Open questions');
|
|
424
|
+
if (body === null) {
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
const lines = body
|
|
428
|
+
.split('\n')
|
|
429
|
+
.map((l) => l.trim())
|
|
430
|
+
.filter((l) => l.length > 0);
|
|
431
|
+
if (lines.some((l) => l.length > 1 && l.endsWith('?'))) {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
const bare = lines.map(_bareLine).filter((l) => l.length > 0);
|
|
435
|
+
if (bare.some((l) => _NO_OPEN_QUESTIONS.has(l))) {
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
if (bare.length === 0) {
|
|
439
|
+
return 'section `## Open questions` is empty — carry a `?`-terminated question, or state `none`';
|
|
440
|
+
}
|
|
441
|
+
const placeholder = new Set(['tbd', 'todo', 'wip', '...', '…', '?']);
|
|
442
|
+
if (bare.every((l) => placeholder.has(l))) {
|
|
443
|
+
return 'section `## Open questions` carries only a placeholder — carry a `?`-terminated question, or state `none`';
|
|
444
|
+
}
|
|
445
|
+
return null;
|
|
446
|
+
}
|
|
447
|
+
|
|
363
448
|
export function main(argv?: readonly string[]): number {
|
|
364
449
|
let skills_dir = SKILLS_DIR;
|
|
365
450
|
// Positional-only: flags are NOT paths. Before this filter the CI invocation
|
|
@@ -373,14 +458,19 @@ export function main(argv?: readonly string[]): number {
|
|
|
373
458
|
// artifact mode — validate the workflow-resume file's required fields
|
|
374
459
|
const text = fs.readFileSync(_resolve(args[0] as string), 'utf-8');
|
|
375
460
|
const missing = validate_handoff_artifact(text);
|
|
376
|
-
|
|
461
|
+
const shape = validate_handoff_open_questions(text);
|
|
462
|
+
if (missing.length === 0 && shape === null) {
|
|
377
463
|
if (!QUIET) process.stdout.write('✅ HANDOFF artifact: all required fields present\n');
|
|
378
464
|
return 0;
|
|
379
465
|
}
|
|
380
466
|
for (const m of missing) {
|
|
381
467
|
process.stdout.write(`${args[0]}:1:handoff-artifact-missing-field: missing required section \`## ${m}\`\n`);
|
|
382
468
|
}
|
|
383
|
-
|
|
469
|
+
if (shape !== null) {
|
|
470
|
+
process.stdout.write(`${args[0]}:1:handoff-artifact-empty-section: ${shape}\n`);
|
|
471
|
+
}
|
|
472
|
+
const problems = missing.length + (shape === null ? 0 : 1);
|
|
473
|
+
process.stderr.write(`❌ ${problems} HANDOFF artifact problem(s)\n`);
|
|
384
474
|
return 1;
|
|
385
475
|
}
|
|
386
476
|
if (args.length > 0) {
|