@event4u/agent-config 5.7.0 → 5.8.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/.agent-src/commands/agent-handoff.md +1 -1
- package/.agent-src/commands/agent-status.md +1 -1
- package/.agent-src/commands/agents/audit.md +1 -1
- package/.agent-src/commands/agents/init.md +1 -1
- package/.agent-src/commands/agents/user/accept.md +3 -3
- package/.agent-src/commands/agents/user/init.md +4 -4
- package/.agent-src/commands/agents/user/show.md +3 -3
- package/.agent-src/commands/agents/user/update.md +3 -3
- package/.agent-src/commands/agents/user.md +1 -1
- package/.agent-src/commands/agents.md +1 -1
- package/.agent-src/commands/analytics/prune.md +1 -1
- package/.agent-src/commands/analytics/show.md +1 -1
- package/.agent-src/commands/analytics.md +1 -1
- package/.agent-src/commands/bug-fix.md +1 -1
- package/.agent-src/commands/challenge-me.md +1 -1
- package/.agent-src/commands/chat-history/import.md +1 -1
- package/.agent-src/commands/chat-history/learn.md +1 -1
- package/.agent-src/commands/chat-history/show.md +1 -1
- package/.agent-src/commands/chat-history.md +1 -1
- package/.agent-src/commands/check-current-md.md +1 -1
- package/.agent-src/commands/condense.md +1 -1
- package/.agent-src/commands/context.md +1 -1
- package/.agent-src/commands/cost-report.md +1 -1
- package/.agent-src/commands/council.md +3 -3
- package/.agent-src/commands/create-pr/description-only.md +1 -1
- package/.agent-src/commands/create-pr.md +1 -1
- package/.agent-src/commands/e2e-heal.md +1 -1
- package/.agent-src/commands/e2e-plan.md +1 -1
- package/.agent-src/commands/feature.md +1 -1
- package/.agent-src/commands/fix/ci.md +1 -1
- package/.agent-src/commands/fix/portability.md +1 -1
- package/.agent-src/commands/fix/pr-bot-comments.md +1 -1
- package/.agent-src/commands/fix/pr-comments.md +1 -1
- package/.agent-src/commands/fix/pr-developer-comments.md +1 -1
- package/.agent-src/commands/fix/refs.md +1 -1
- package/.agent-src/commands/fix/seeder.md +1 -1
- package/.agent-src/commands/fix.md +1 -1
- package/.agent-src/commands/judge.md +1 -1
- package/.agent-src/commands/knowledge/cross-repo.md +1 -1
- package/.agent-src/commands/knowledge/forget.md +1 -1
- package/.agent-src/commands/knowledge/ingest.md +1 -1
- package/.agent-src/commands/knowledge/list.md +1 -1
- package/.agent-src/commands/knowledge.md +1 -1
- package/.agent-src/commands/memory/add.md +1 -1
- package/.agent-src/commands/memory/learn-low-impact.md +1 -1
- package/.agent-src/commands/memory/load.md +1 -1
- package/.agent-src/commands/memory/mine-session.md +1 -1
- package/.agent-src/commands/memory/promote.md +1 -1
- package/.agent-src/commands/memory/propose.md +1 -1
- package/.agent-src/commands/memory.md +1 -1
- package/.agent-src/commands/mode.md +1 -1
- package/.agent-src/commands/optimize/agents-dir.md +1 -1
- package/.agent-src/commands/optimize/augmentignore.md +1 -1
- package/.agent-src/commands/optimize/rtk.md +1 -1
- package/.agent-src/commands/optimize/skills.md +1 -1
- package/.agent-src/commands/optimize.md +1 -1
- package/.agent-src/commands/orchestrate.md +1 -1
- package/.agent-src/commands/override/create.md +1 -1
- package/.agent-src/commands/override/manage.md +1 -1
- package/.agent-src/commands/override.md +1 -1
- package/.agent-src/commands/package-reset.md +1 -1
- package/.agent-src/commands/prediction-pool.md +31 -12
- package/.agent-src/commands/profile/activate.md +81 -0
- package/.agent-src/commands/profile/deactivate.md +68 -0
- package/.agent-src/commands/profile/show.md +70 -0
- package/.agent-src/commands/profile.md +68 -0
- package/.agent-src/commands/project-health.md +1 -1
- package/.agent-src/commands/quality-fix.md +1 -1
- package/.agent-src/commands/roadmap/process-full.md +1 -1
- package/.agent-src/commands/roadmap/process-phase.md +1 -1
- package/.agent-src/commands/roadmap/process-step.md +1 -1
- package/.agent-src/commands/roadmap.md +1 -1
- package/.agent-src/commands/set-cost-profile.md +1 -1
- package/.agent-src/commands/skill/preview.md +3 -3
- package/.agent-src/commands/skill.md +1 -1
- package/.agent-src/commands/skills/discover.md +1 -1
- package/.agent-src/commands/skills.md +1 -1
- package/.agent-src/commands/sync-agent-settings.md +1 -1
- package/.agent-src/commands/sync-gitignore/fix.md +1 -1
- package/.agent-src/commands/sync-gitignore.md +1 -1
- package/.agent-src/commands/update-form-request-messages.md +1 -1
- package/.agent-src/skills/check-refs/SKILL.md +1 -1
- package/.agent-src/skills/finishing-a-development-branch/SKILL.md +1 -1
- package/.agent-src/skills/git-workflow/SKILL.md +1 -1
- package/.agent-src/skills/jira-integration/SKILL.md +1 -1
- package/.agent-src/skills/markitdown/SKILL.md +1 -1
- package/.agent-src/skills/prediction-pool-optimizer/SKILL.md +195 -77
- package/.agent-src/skills/prediction-pool-optimizer/evals/triggers.json +3 -1
- package/.agent-src/skills/prediction-pool-optimizer/reference/ev-fixtures.md +111 -16
- package/.agent-src/skills/prediction-pool-optimizer/reference/odds-and-bonus.md +109 -0
- package/.agent-src/skills/rtk-output-filtering/SKILL.md +1 -1
- package/.agent-src/skills/script-writing/SKILL.md +1 -1
- package/.agent-src/skills/token-optimizer/SKILL.md +1 -1
- package/.agent-src/skills/using-git-worktrees/SKILL.md +1 -1
- package/.agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/.agent-src/templates/scripts/work_engine/_lib/agent_settings.py +52 -5
- package/.claude-plugin/marketplace.json +370 -366
- package/CHANGELOG.md +60 -0
- package/README.md +2 -2
- package/config/discovery/session-profiles.yml +37 -0
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +183 -95
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +9 -5
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +8 -4
- package/dist/mcp/registry-manifest.json +3 -3
- package/docs/architecture.md +1 -1
- package/docs/catalog.md +7 -3
- package/docs/contracts/command-clusters.md +2 -0
- package/docs/contracts/session-profile-overlay.md +120 -0
- package/docs/customization.md +26 -0
- package/docs/decisions/ADR-010-profile-pack-preset-boundary.md +36 -0
- package/docs/decisions/ADR-038-canonical-settings-path.md +66 -0
- package/docs/decisions/ADR-039-claude-skills-untracked.md +139 -0
- package/docs/decisions/INDEX.md +2 -0
- package/docs/development.md +12 -0
- package/docs/getting-started.md +1 -1
- package/docs/guidelines/agent-infra/layered-settings.md +8 -2
- package/docs/skills-catalog.md +5 -1
- package/llms.txt +4 -0
- package/package.json +1 -1
- package/scripts/__pycache__/validate_frontmatter.cpython-312.pyc +0 -0
- package/scripts/_cli/cmd_doctor.py +3 -2
- package/scripts/_cli/cmd_versions.py +2 -2
- package/scripts/_lib/__pycache__/__init__.cpython-312.pyc +0 -0
- package/scripts/_lib/__pycache__/agent_src.cpython-312.pyc +0 -0
- package/scripts/_lib/agent_settings.py +52 -5
- package/scripts/_lib/agent_src.py +30 -0
- package/scripts/ai_council/session.py +5 -1
- package/scripts/audit_command_surface.py +7 -1
- package/scripts/audit_initial_context.py +10 -2
- package/scripts/check_gate_paths.py +117 -0
- package/scripts/check_references.py +51 -2
- package/scripts/check_test_coverage_diff.py +180 -0
- package/scripts/compile_router.py +5 -1
- package/scripts/condense.py +79 -2
- package/scripts/config/session_profiles.py +492 -0
- package/scripts/council_cli.py +5 -1
- package/scripts/hook_manifest.yaml +15 -7
- package/scripts/hooks/dispatch_hook.py +8 -0
- package/scripts/install-hooks.sh +2 -1
- package/scripts/install.py +76 -5
- package/scripts/inventory_abstraction_budget.py +6 -1
- package/scripts/lint_agents_md.py +11 -4
- package/scripts/lint_hook_concern_budget.py +5 -1
- package/scripts/lint_marketplace.py +18 -7
- package/scripts/lint_roadmap_ci_steps.py +5 -1
- package/scripts/lint_roadmap_complexity.py +5 -1
- package/scripts/mcp_server/prompts.py +5 -1
- package/scripts/prediction-pool/pool_winsim.py +236 -0
- package/scripts/prediction-pool/score_ev.py +188 -0
- package/scripts/profile_staleness_hook.py +69 -0
- package/scripts/roadmap_progress_hook.py +56 -6
- package/scripts/smoke_quickstart.py +3 -2
- package/scripts/sync_agent_settings.py +8 -3
- package/scripts/validate_agent_settings.py +5 -1
- package/scripts/validate_decision_engine.py +5 -1
- package/scripts/measure_roadmap_trajectory.py +0 -112
- package/scripts/verify_roadmap_closure.py +0 -327
package/docs/development.md
CHANGED
|
@@ -127,6 +127,18 @@ task dev:link # Symlink this repo as the global @event4u/agent-
|
|
|
127
127
|
task dev:unlink # Remove the global symlink
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
+
**Switch the global install between dev and release** — one-shot toggles
|
|
131
|
+
that flip BOTH the `agent-config` bin on PATH and the user-scope content:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
task install:use-dev # global = THIS working tree (npm link + dev-build content)
|
|
135
|
+
task install:use-release # global = latest npm release (npm i -g @latest + release content)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Run `install:use-dev` to test the working tree as the live global install,
|
|
139
|
+
then `install:use-release` to switch back to the published version. They are
|
|
140
|
+
symmetric — whichever you run last is the active global `agent-config`.
|
|
141
|
+
|
|
130
142
|
**Typical flow:**
|
|
131
143
|
|
|
132
144
|
1. In this repo: `task dev:link` — once. The `agent-config` bin on PATH
|
package/docs/getting-started.md
CHANGED
|
@@ -169,7 +169,7 @@ Your agent now understands slash commands:
|
|
|
169
169
|
| `/quality-fix` | Run and fix all quality checks |
|
|
170
170
|
| `/chat-history` | Inspect the persistent chat-history log (read-only `show`) |
|
|
171
171
|
|
|
172
|
-
→ [Browse all
|
|
172
|
+
→ [Browse all 150 active commands](../.agent-src/commands/)
|
|
173
173
|
|
|
174
174
|
---
|
|
175
175
|
|
|
@@ -19,7 +19,13 @@ on user request.
|
|
|
19
19
|
|---|---|---|---|---|
|
|
20
20
|
| `.agent-project-settings.yml` | **committed** | team / repo | lead maintainer | `project.stack`, `quality.php.tools`, `memory.dogfood` |
|
|
21
21
|
| `~/.event4u/agent-config/agent-settings.yml` | **n/a** (outside repo) | individual developer · cross-project | individual | `name`, `ide`, `rule_loading_tier`, `personal.bot_icon`, `personal.autonomy`, `telegraph.speak_scope` (legacy `~/.config/agent-config/agent-settings.yml` read as fallback) |
|
|
22
|
-
|
|
|
22
|
+
| `agents/settings/.agent-settings.yml` | **gitignored** | individual developer · this project | individual | `personal.ide`, `personal.user_name`, `subagents.max_parallel`, `onboarding.onboarded` |
|
|
23
|
+
|
|
24
|
+
> **Canonical location (ADR-038):** the developer file lives in the settings
|
|
25
|
+
> layer at `agents/settings/.agent-settings.yml` (alongside
|
|
26
|
+
> `.agent-settings.local.yml`, `contexts/`, `policies/`). A repo-root
|
|
27
|
+
> `.agent-settings.yml` is read as a **back-compat fallback** and is migrated
|
|
28
|
+
> into the canonical location by `install` on the next run.
|
|
23
29
|
|
|
24
30
|
All three are YAML. Schemas:
|
|
25
31
|
|
|
@@ -36,7 +42,7 @@ Lowest priority → highest priority:
|
|
|
36
42
|
1. Package defaults (shipped by event4u/agent-config)
|
|
37
43
|
2. ~/.event4u/agent-config/agent-settings.yml (user-global · whitelist-filtered · legacy ~/.config/agent-config/ read as fallback)
|
|
38
44
|
3. .agent-project-settings.yml (team file, committed)
|
|
39
|
-
4.
|
|
45
|
+
4. agents/settings/.agent-settings.yml (developer file, gitignored; legacy repo-root .agent-settings.yml read as fallback — ADR-038)
|
|
40
46
|
```
|
|
41
47
|
|
|
42
48
|
Keys from higher layers win unless a lower layer marks them
|
package/docs/skills-catalog.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Skills Catalog
|
|
2
2
|
|
|
3
|
-
All **
|
|
3
|
+
All **223 skills** available in this package, in alphabetical order.
|
|
4
4
|
Click a skill name to open its SKILL.md and read the full guidance.
|
|
5
5
|
|
|
6
6
|
> **Regenerate:** `python3 scripts/generate_catalog.py`
|
|
@@ -89,6 +89,8 @@ Click a skill name to open its SKILL.md and read the full guidance.
|
|
|
89
89
|
| [`gtm-launch`](../.agent-src/skills/gtm-launch/SKILL.md) | Use when sequencing a launch — alpha / beta / GA waves, audience-by-wave logic, narrative beats per wave, engineering-readiness gates. Triggers on 'plan the launch', 'sequence GA'. |
|
|
90
90
|
| [`guideline-writing`](../.agent-src/skills/guideline-writing/SKILL.md) | Use when creating or editing a guideline in docs/guidelines/ — reference material cited by skills, no auto-triggers — even when the user just says 'write up our naming conventions'. |
|
|
91
91
|
| [`hiring-loop-design`](../.agent-src/skills/hiring-loop-design/SKILL.md) | Use when shaping an engineering hiring loop — stages, take-home vs live, calibration, bar-raiser, signal-vs-noise audit. Triggers on 'design our interview loop', 'audit our hiring bar'. |
|
|
92
|
+
| [`image-analyser`](../.agent-src/skills/image-analyser/SKILL.md) | Use to analyse a character image down to the smallest mole and diff against a canon — per-feature spec, OCR-reads tattoo text, flags drift. Triggers 'analyse this image', 'match the canon'. |
|
|
93
|
+
| [`image-creator`](../.agent-src/skills/image-creator/SKILL.md) | Use to generate a character image to spec — max-fidelity reproducible prompt from a Canon Spec, anchors-first, provider/governance-gated. Triggers 'generate this character', 'render to spec'. |
|
|
92
94
|
| [`incident-commander`](../.agent-src/skills/incident-commander/SKILL.md) | Use during or right after an incident — frames severity, sets comms cadence, drafts the post-mortem skeleton — even when the user just says 'production is down' or 'wir haben einen Vorfall'. |
|
|
93
95
|
| [`jira-integration`](../.agent-src/skills/jira-integration/SKILL.md) | Use when the user says "check Jira", "create ticket", "update issue", or needs JQL queries, ticket transitions, or branch-to-ticket linking. |
|
|
94
96
|
| [`jobs-events`](../.agent-src/skills/jobs-events/SKILL.md) | Use when creating Laravel jobs, queued workflows, events, or listeners. Covers clear responsibilities, safe serialization, and retry/failure handling. |
|
|
@@ -152,6 +154,7 @@ Click a skill name to open its SKILL.md and read the full guidance.
|
|
|
152
154
|
| [`playwright-testing`](../.agent-src/skills/playwright-testing/SKILL.md) | Use when writing Playwright E2E tests — browser automation, visual regression testing, Page Objects, fixtures, and reliable test patterns. |
|
|
153
155
|
| [`po-discovery`](../.agent-src/skills/po-discovery/SKILL.md) | Use when shaping a fuzzy product ask into a refined backlog item — problem framing, user-story rewrite, AC tightening — even if the user just says 'help me write this ticket'. |
|
|
154
156
|
| [`positioning-strategy`](../.agent-src/skills/positioning-strategy/SKILL.md) | Use when locking the market frame — category, segment, alternative, point-of-view — before messaging, launch, or pricing rides on it. Triggers on 'who are we for', 'opposable audit'. |
|
|
157
|
+
| [`prediction-pool-optimizer`](../.agent-src/skills/prediction-pool-optimizer/SKILL.md) | Optimize prediction-pool tips (kicktipp etc.): rules + multi-book consensus odds → expected-points-max answer for every question, scores AND bonus. Triggers 'optimize my pool tips', 'predict'. |
|
|
155
158
|
| [`privacy-review`](../.agent-src/skills/privacy-review/SKILL.md) | Use when reviewing data flows, support macros, refund templates for GDPR/CCPA/HIPAA fit — regime, consent, PII redaction (email, order-id), breach triage. Triggers 'is this GDPR-safe', 'PII redact'. |
|
|
156
159
|
| [`project-analysis-core`](../.agent-src/skills/project-analysis-core/SKILL.md) | Raw discovery primitives — project discovery, version resolution, docs loading, architecture mapping, execution flow. Called by `universal-project-analysis`. Single-pass scan → `project-analyzer`. |
|
|
157
160
|
| [`project-analysis-hypothesis-driven`](../.agent-src/skills/project-analysis-hypothesis-driven/SKILL.md) | Use when a bug has multiple plausible causes across layers — competing hypotheses, validation loops, evidence-based conclusions — even when the user just says 'why is this happening?'. |
|
|
@@ -199,6 +202,7 @@ Click a skill name to open its SKILL.md and read the full guidance.
|
|
|
199
202
|
| [`skill-management`](../.agent-src/skills/skill-management/SKILL.md) | Use when condensing, decondenseing, refactoring, or improving existing skills. Covers the full skill lifecycle from verbose → sharp → maintained. |
|
|
200
203
|
| [`skill-reviewer`](../.agent-src/skills/skill-reviewer/SKILL.md) | Use when reviewing, auditing, or optimizing skills — validates against the 7 Skill Killers checklist and produces fix recommendations. |
|
|
201
204
|
| [`skill-writing`](../.agent-src/skills/skill-writing/SKILL.md) | Use when deciding 'should this be a skill or a rule?', creating/improving/reviewing agent skills, SKILL.md frontmatter, or procedure sections — even without saying 'skill-writing'. |
|
|
205
|
+
| [`song-to-script`](../.agent-src/skills/song-to-script/SKILL.md) | Turn an audio track into a timed `## Scene N` script: song sections → per-scene durations, auto mode adds mood + lip-sync lines. Triggers 'music video', 'from the song', 'cut to the beat'. |
|
|
202
206
|
| [`sql-writing`](../.agent-src/skills/sql-writing/SKILL.md) | Use when writing raw SQL — MariaDB/MySQL syntax, parameterization, raw migrations, seeders with `DB::statement` — even when the user just pastes a query and asks 'why is this slow' without naming SQL. |
|
|
203
207
|
| [`stakeholder-tradeoff`](../.agent-src/skills/stakeholder-tradeoff/SKILL.md) | Use when stakeholders pull a decision in different directions — frames each lens, builds a trade-off matrix, surfaces the cost of every choice — even if the user just says 'PO and ops disagree'. |
|
|
204
208
|
| [`subagent-orchestration`](../.agent-src/skills/subagent-orchestration/SKILL.md) | Use when orchestrating implementer/judge subagents — seven modes (do-and-judge ±two-stage, do-in-steps/parallel/worktrees, do-competitively, judge-with-debate) — models from .agent-settings.yml. |
|
package/llms.txt
CHANGED
|
@@ -87,6 +87,8 @@ grafana: Use when working with Grafana — dashboards, Loki LogQL queries, alert
|
|
|
87
87
|
gtm-launch: Use when sequencing a launch — alpha / beta / GA waves, audience-by-wave logic, narrative beats per wave, engineering-readiness gates. Triggers on 'plan the launch', 'sequence GA'.
|
|
88
88
|
guideline-writing: Use when creating or editing a guideline in docs/guidelines/ — reference material cited by skills, no auto-triggers — even when the user just says 'write up our naming conventions'.
|
|
89
89
|
hiring-loop-design: Use when shaping an engineering hiring loop — stages, take-home vs live, calibration, bar-raiser, signal-vs-noise audit. Triggers on 'design our interview loop', 'audit our hiring bar'.
|
|
90
|
+
image-analyser: Use to analyse a character image down to the smallest mole and diff against a canon — per-feature spec, OCR-reads tattoo text, flags drift. Triggers 'analyse this image', 'match the canon'.
|
|
91
|
+
image-creator: Use to generate a character image to spec — max-fidelity reproducible prompt from a Canon Spec, anchors-first, provider/governance-gated. Triggers 'generate this character', 'render to spec'.
|
|
90
92
|
incident-commander: Use during or right after an incident — frames severity, sets comms cadence, drafts the post-mortem skeleton — even when the user just says 'production is down' or 'wir haben einen Vorfall'.
|
|
91
93
|
jira-integration: Use when the user says "check Jira", "create ticket", "update issue", or needs JQL queries, ticket transitions, or branch-to-ticket linking.
|
|
92
94
|
jobs-events: Use when creating Laravel jobs, queued workflows, events, or listeners. Covers clear responsibilities, safe serialization, and retry/failure handling.
|
|
@@ -150,6 +152,7 @@ playwright-architect: Use when shaping a Playwright suite — locator strategy,
|
|
|
150
152
|
playwright-testing: Use when writing Playwright E2E tests — browser automation, visual regression testing, Page Objects, fixtures, and reliable test patterns.
|
|
151
153
|
po-discovery: Use when shaping a fuzzy product ask into a refined backlog item — problem framing, user-story rewrite, AC tightening — even if the user just says 'help me write this ticket'.
|
|
152
154
|
positioning-strategy: Use when locking the market frame — category, segment, alternative, point-of-view — before messaging, launch, or pricing rides on it. Triggers on 'who are we for', 'opposable audit'.
|
|
155
|
+
prediction-pool-optimizer: Optimize prediction-pool tips (kicktipp etc.): rules + multi-book consensus odds → expected-points-max answer for every question, scores AND bonus. Triggers 'optimize my pool tips', 'predict'.
|
|
153
156
|
privacy-review: Use when reviewing data flows, support macros, refund templates for GDPR/CCPA/HIPAA fit — regime, consent, PII redaction (email, order-id), breach triage. Triggers 'is this GDPR-safe', 'PII redact'.
|
|
154
157
|
project-analysis-core: Raw discovery primitives — project discovery, version resolution, docs loading, architecture mapping, execution flow. Called by `universal-project-analysis`. Single-pass scan → `project-analyzer`.
|
|
155
158
|
project-analysis-hypothesis-driven: Use when a bug has multiple plausible causes across layers — competing hypotheses, validation loops, evidence-based conclusions — even when the user just says 'why is this happening?'.
|
|
@@ -197,6 +200,7 @@ skill-improvement-pipeline: ONLY when user explicitly requests: run the skill im
|
|
|
197
200
|
skill-management: Use when condensing, decondenseing, refactoring, or improving existing skills. Covers the full skill lifecycle from verbose → sharp → maintained.
|
|
198
201
|
skill-reviewer: Use when reviewing, auditing, or optimizing skills — validates against the 7 Skill Killers checklist and produces fix recommendations.
|
|
199
202
|
skill-writing: Use when deciding 'should this be a skill or a rule?', creating/improving/reviewing agent skills, SKILL.md frontmatter, or procedure sections — even without saying 'skill-writing'.
|
|
203
|
+
song-to-script: Turn an audio track into a timed `## Scene N` script: song sections → per-scene durations, auto mode adds mood + lip-sync lines. Triggers 'music video', 'from the song', 'cut to the beat'.
|
|
200
204
|
sql-writing: Use when writing raw SQL — MariaDB/MySQL syntax, parameterization, raw migrations, seeders with `DB::statement` — even when the user just pastes a query and asks 'why is this slow' without naming SQL.
|
|
201
205
|
stakeholder-tradeoff: Use when stakeholders pull a decision in different directions — frames each lens, builds a trade-off matrix, surfaces the cost of every choice — even if the user just says 'PO and ops disagree'.
|
|
202
206
|
subagent-orchestration: Use when orchestrating implementer/judge subagents — seven modes (do-and-judge ±two-stage, do-in-steps/parallel/worktrees, do-competitively, judge-with-debate) — models from .agent-settings.yml.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@event4u/agent-config",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.8.0",
|
|
4
4
|
"description": "Universal AI Agent OS \u2014 audited skills, governance rules, commands, and templates for AI coding tools (Claude Code, Cursor, Windsurf, Copilot).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
Binary file
|
|
@@ -55,6 +55,7 @@ from scripts._lib.agent_settings import (
|
|
|
55
55
|
ROOT_OVERRIDE_ENV,
|
|
56
56
|
ProjectRootError,
|
|
57
57
|
find_project_root_with_trace,
|
|
58
|
+
project_settings_path,
|
|
58
59
|
resolve_project_root,
|
|
59
60
|
)
|
|
60
61
|
|
|
@@ -131,7 +132,7 @@ def _settings_layer_chain(project_root: Path) -> list[str]:
|
|
|
131
132
|
user_global = user_global_paths.resolve_with_fallback("agent-settings.yml")
|
|
132
133
|
if user_global is not None and user_global.is_file():
|
|
133
134
|
layers.append(str(user_global))
|
|
134
|
-
project_settings = project_root
|
|
135
|
+
project_settings = project_settings_path(project_root)
|
|
135
136
|
if project_settings.is_file():
|
|
136
137
|
layers.append(str(project_settings))
|
|
137
138
|
return layers
|
|
@@ -778,7 +779,7 @@ def _check_tier_usage_readiness(project_root: Path) -> dict[str, Any]:
|
|
|
778
779
|
|
|
779
780
|
Contract: ``docs/contracts/command-clusters.md`` § tier-usage signal.
|
|
780
781
|
"""
|
|
781
|
-
settings_file = project_root
|
|
782
|
+
settings_file = project_settings_path(project_root)
|
|
782
783
|
log_path = project_root / ".agent-tier-usage.jsonl"
|
|
783
784
|
enabled = False
|
|
784
785
|
if settings_file.is_file():
|
|
@@ -18,7 +18,7 @@ import subprocess
|
|
|
18
18
|
import sys
|
|
19
19
|
from pathlib import Path
|
|
20
20
|
|
|
21
|
-
from scripts._lib.agent_settings import resolve_project_root
|
|
21
|
+
from scripts._lib.agent_settings import project_settings_path, resolve_project_root
|
|
22
22
|
|
|
23
23
|
PACKAGE_NAME = "@event4u/agent-config"
|
|
24
24
|
|
|
@@ -51,7 +51,7 @@ def _local_package_version() -> str:
|
|
|
51
51
|
|
|
52
52
|
def _pinned_version() -> str:
|
|
53
53
|
"""Return the ``agent_config_version`` pin from ``.agent-settings.yml``."""
|
|
54
|
-
settings = _project_root()
|
|
54
|
+
settings = project_settings_path(_project_root())
|
|
55
55
|
if not settings.exists():
|
|
56
56
|
return ""
|
|
57
57
|
try:
|
|
Binary file
|
|
Binary file
|
|
@@ -76,6 +76,45 @@ def _local_settings_path(project_root: Path) -> Path:
|
|
|
76
76
|
return project_root.joinpath(*LOCAL_PROJECT_SUBDIR, LOCAL_PROJECT_FILE)
|
|
77
77
|
|
|
78
78
|
|
|
79
|
+
def _canonical_settings_path(project_root: Path) -> Path:
|
|
80
|
+
"""Canonical project settings file: ``<root>/agents/settings/.agent-settings.yml``.
|
|
81
|
+
|
|
82
|
+
The project settings file lives in the project's settings layer
|
|
83
|
+
(``agents/settings/``, alongside ``contexts/`` and ``policies/`` and
|
|
84
|
+
the ``.agent-settings.local.yml`` override), NOT at the repo root.
|
|
85
|
+
The repo-root ``.agent-settings.yml`` is a legacy location read only
|
|
86
|
+
as a back-compat fallback (see :func:`project_settings_path`).
|
|
87
|
+
"""
|
|
88
|
+
return project_root.joinpath(*LOCAL_PROJECT_SUBDIR, DEFAULT_PROJECT_FILE)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def project_settings_path(project_root: Path) -> Path:
|
|
92
|
+
"""Resolve the project settings file for **reading**.
|
|
93
|
+
|
|
94
|
+
Returns the canonical ``agents/settings/.agent-settings.yml`` when it
|
|
95
|
+
exists; otherwise the legacy repo-root ``.agent-settings.yml`` when
|
|
96
|
+
that exists (back-compat for installs predating the relocation);
|
|
97
|
+
otherwise the canonical path (so the caller still names the right
|
|
98
|
+
target on a fresh repo). Existence-checked, never writes.
|
|
99
|
+
"""
|
|
100
|
+
canonical = _canonical_settings_path(project_root)
|
|
101
|
+
if canonical.exists():
|
|
102
|
+
return canonical
|
|
103
|
+
legacy = project_root / DEFAULT_PROJECT_FILE
|
|
104
|
+
if legacy.exists():
|
|
105
|
+
return legacy
|
|
106
|
+
return canonical
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def canonical_settings_write_path(project_root: Path) -> Path:
|
|
110
|
+
"""Always the canonical **write** target: ``agents/settings/.agent-settings.yml``.
|
|
111
|
+
|
|
112
|
+
Writers (install, sync, migrate) target this unconditionally; the
|
|
113
|
+
legacy root file is migrated into it, never written afresh.
|
|
114
|
+
"""
|
|
115
|
+
return _canonical_settings_path(project_root)
|
|
116
|
+
|
|
117
|
+
|
|
79
118
|
DEFAULT_TEAM_FILE = ".agent-project-settings.yml"
|
|
80
119
|
USER_GLOBAL_FILENAME = "agent-settings.yml"
|
|
81
120
|
|
|
@@ -431,12 +470,14 @@ def _resolve_cascade_paths(
|
|
|
431
470
|
"""
|
|
432
471
|
if cwd is None:
|
|
433
472
|
legacy = Path(project_path) if project_path else Path(DEFAULT_PROJECT_FILE)
|
|
434
|
-
return [legacy,
|
|
473
|
+
return [legacy, _canonical_settings_path(legacy.parent),
|
|
474
|
+
_local_settings_path(legacy.parent)]
|
|
435
475
|
|
|
436
476
|
root = find_project_root(cwd)
|
|
437
477
|
if root is None:
|
|
438
478
|
legacy = Path(project_path) if project_path else Path(DEFAULT_PROJECT_FILE)
|
|
439
|
-
return [legacy,
|
|
479
|
+
return [legacy, _canonical_settings_path(legacy.parent),
|
|
480
|
+
_local_settings_path(legacy.parent)]
|
|
440
481
|
|
|
441
482
|
cwd_resolved = cwd.resolve()
|
|
442
483
|
# Build the chain root → … → cwd (shallowest first, deepest last).
|
|
@@ -451,9 +492,15 @@ def _resolve_cascade_paths(
|
|
|
451
492
|
break
|
|
452
493
|
cursor = parent
|
|
453
494
|
chain.reverse()
|
|
454
|
-
#
|
|
455
|
-
#
|
|
456
|
-
|
|
495
|
+
# Legacy per-dir cascade root → cwd (repo-root .agent-settings.yml is the
|
|
496
|
+
# shallowest, back-compat), then the canonical project settings file under
|
|
497
|
+
# agents/settings/ (wins over the legacy root location), then the
|
|
498
|
+
# per-machine local override under agents/settings/ as the deepest (winning)
|
|
499
|
+
# layer.
|
|
500
|
+
return (
|
|
501
|
+
[d / DEFAULT_PROJECT_FILE for d in chain]
|
|
502
|
+
+ [_canonical_settings_path(root), _local_settings_path(root)]
|
|
503
|
+
)
|
|
457
504
|
|
|
458
505
|
|
|
459
506
|
def load_agent_settings(
|
|
@@ -30,6 +30,7 @@ from typing import Iterator
|
|
|
30
30
|
ROOT = Path(__file__).resolve().parents[2]
|
|
31
31
|
LEGACY_SRC = ROOT / ".agent-src.uncondensed"
|
|
32
32
|
PACKAGES = ROOT / "packages"
|
|
33
|
+
PACKAGE_CORE = PACKAGES / "core"
|
|
33
34
|
|
|
34
35
|
# Repo-relative POSIX path prefixes that anchor an artefact source tree.
|
|
35
36
|
# Order: legacy first (kept until the move lands), then packages/*. Each
|
|
@@ -155,3 +156,32 @@ def strip_source_prefix(rel: str) -> str | None:
|
|
|
155
156
|
def is_artefact_path(rel: str) -> bool:
|
|
156
157
|
"""``True`` if a repo-relative POSIX path sits under any source root."""
|
|
157
158
|
return strip_source_prefix(rel) is not None
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def resolve_package_core_path(relative_target: str) -> Path:
|
|
162
|
+
"""Return the canonical ``packages/core/<relative_target>`` path.
|
|
163
|
+
|
|
164
|
+
The single resolution point for every gate that enforces something
|
|
165
|
+
against a fixed ``packages/core/`` target. A future move of the
|
|
166
|
+
``packages/core/`` tree updates :data:`PACKAGE_CORE` here — one
|
|
167
|
+
resolver — instead of N hard-coded ``REPO_ROOT / "packages" / "core"``
|
|
168
|
+
constants scattered across gate scripts (the ``aab5755`` silent-no-op
|
|
169
|
+
class this eliminates).
|
|
170
|
+
|
|
171
|
+
Pure resolver: deterministic, **no filesystem I/O**. ``agent_src`` is
|
|
172
|
+
imported by scanners that must stay usable in the legacy-only and
|
|
173
|
+
pack-only layouts (see :func:`artefact_roots`), so this MUST NOT
|
|
174
|
+
assert existence at import or call time — a packages/core existence
|
|
175
|
+
check here would break those layouts. Callers that need existence
|
|
176
|
+
check it themselves; ``scripts/check_gate_paths.py`` is the single
|
|
177
|
+
gate that asserts the enforced targets resolve under ``packages/core/``.
|
|
178
|
+
|
|
179
|
+
Examples:
|
|
180
|
+
``resolve_package_core_path(".agent-src.uncondensed")``
|
|
181
|
+
→ ``<repo>/packages/core/.agent-src.uncondensed``
|
|
182
|
+
``resolve_package_core_path(".agent-src.uncondensed/commands")``
|
|
183
|
+
→ ``<repo>/packages/core/.agent-src.uncondensed/commands``
|
|
184
|
+
``resolve_package_core_path("")`` → ``<repo>/packages/core``
|
|
185
|
+
"""
|
|
186
|
+
rel = relative_target.replace("\\", "/").lstrip("/")
|
|
187
|
+
return PACKAGE_CORE / rel if rel else PACKAGE_CORE
|
|
@@ -28,6 +28,10 @@ import shutil
|
|
|
28
28
|
import sys
|
|
29
29
|
from dataclasses import dataclass, field
|
|
30
30
|
from pathlib import Path
|
|
31
|
+
try: # invocation-agnostic import (repo-root-on-path vs scripts-on-path)
|
|
32
|
+
from scripts._lib.agent_settings import project_settings_path
|
|
33
|
+
except ModuleNotFoundError: # pragma: no cover
|
|
34
|
+
from _lib.agent_settings import project_settings_path
|
|
31
35
|
from typing import Iterable
|
|
32
36
|
|
|
33
37
|
from scripts.ai_council.clients import CouncilResponse
|
|
@@ -37,7 +41,7 @@ REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
|
37
41
|
SESSIONS_DIR = REPO_ROOT / "agents" / "runtime" / "council" / "sessions"
|
|
38
42
|
QUESTIONS_DIR = REPO_ROOT / "agents" / "runtime" / "council" / "questions"
|
|
39
43
|
RESPONSES_DIR = REPO_ROOT / "agents" / "runtime" / "council" / "responses"
|
|
40
|
-
SETTINGS_FILE = REPO_ROOT
|
|
44
|
+
SETTINGS_FILE = project_settings_path(REPO_ROOT)
|
|
41
45
|
|
|
42
46
|
# Default retention for all council artefacts (questions, responses,
|
|
43
47
|
# sessions). Overridden by `ai_council.session_retention_days`
|
|
@@ -37,12 +37,18 @@ from pathlib import Path
|
|
|
37
37
|
from typing import List
|
|
38
38
|
|
|
39
39
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
40
|
+
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
|
41
|
+
from _lib.agent_src import resolve_package_core_path # noqa: E402
|
|
42
|
+
|
|
40
43
|
# Pre-monorepo: REPO_ROOT/.agent-src.uncondensed/commands. Post-move (ADR-017)
|
|
41
44
|
# the core command surface lives under packages/core/.agent-src.uncondensed.
|
|
42
45
|
# Fall back to the legacy path only if the packages layout is absent.
|
|
43
|
-
_CORE_COMMANDS =
|
|
46
|
+
_CORE_COMMANDS = resolve_package_core_path(".agent-src.uncondensed/commands")
|
|
44
47
|
_LEGACY_COMMANDS = REPO_ROOT / ".agent-src.uncondensed" / "commands"
|
|
45
48
|
DEFAULT_ROOT = _CORE_COMMANDS if _CORE_COMMANDS.is_dir() else _LEGACY_COMMANDS
|
|
49
|
+
# Enforced packages/core target — read by scripts/check_gate_paths.py so a
|
|
50
|
+
# future move that desyncs this path fails CI instead of silently no-opping.
|
|
51
|
+
GATE_CORE_PATHS = (_CORE_COMMANDS,)
|
|
46
52
|
REPORT_DIR = REPO_ROOT / "agents" / "reports"
|
|
47
53
|
OUT_JSON = REPORT_DIR / "command-surface.json"
|
|
48
54
|
OUT_MD = REPORT_DIR / "command-surface.md"
|
|
@@ -35,6 +35,13 @@ from pathlib import Path
|
|
|
35
35
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
36
36
|
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
|
37
37
|
from _lib import token_count # noqa: E402
|
|
38
|
+
from _lib.agent_src import resolve_package_core_path # noqa: E402
|
|
39
|
+
|
|
40
|
+
_CORE_SRC = resolve_package_core_path(".agent-src.uncondensed")
|
|
41
|
+
# Enforced packages/core targets — the skills + commands dirs the
|
|
42
|
+
# description-catalog globs scan. Read by scripts/check_gate_paths.py so a
|
|
43
|
+
# future move that desyncs them fails CI instead of silently no-opping.
|
|
44
|
+
GATE_CORE_PATHS = (_CORE_SRC / "skills", _CORE_SRC / "commands")
|
|
38
45
|
|
|
39
46
|
try:
|
|
40
47
|
import yaml
|
|
@@ -111,10 +118,11 @@ def _catalog(glob_pat: str) -> dict:
|
|
|
111
118
|
|
|
112
119
|
def description_catalog() -> dict:
|
|
113
120
|
"""0B.4 — description-catalog cost (eager progressive-disclosure surface)."""
|
|
121
|
+
core_rel = _CORE_SRC.relative_to(REPO_ROOT).as_posix()
|
|
114
122
|
return {
|
|
115
123
|
"skills_projected": _catalog(".claude/skills/*/SKILL.md"),
|
|
116
|
-
"skills_core_source": _catalog("
|
|
117
|
-
"commands_core_source": _catalog("
|
|
124
|
+
"skills_core_source": _catalog(f"{core_rel}/skills/*/SKILL.md"),
|
|
125
|
+
"commands_core_source": _catalog(f"{core_rel}/commands/**/*.md"),
|
|
118
126
|
}
|
|
119
127
|
|
|
120
128
|
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Gate path-integrity check (R2 of road-to-test-and-gate-integrity).
|
|
3
|
+
|
|
4
|
+
Asserts that every security/quality gate which enforces something against
|
|
5
|
+
a fixed ``packages/core/`` target still resolves that target on disk. A
|
|
6
|
+
``packages/core/`` move that desyncs a gate's hard-coded path fails CI here
|
|
7
|
+
instead of silently no-opping (the ``aab5755`` class: the Iron-Law SHA gate
|
|
8
|
+
pointed at a stale path and enforced nothing while CI stayed green).
|
|
9
|
+
|
|
10
|
+
Design (AI council, claude-sonnet-4-5 + gpt-4o, 2026-06-02):
|
|
11
|
+
|
|
12
|
+
- The check reads each gate's ACTUAL enforced paths via its module-level
|
|
13
|
+
``GATE_CORE_PATHS`` attribute — it does NOT re-declare a copy of the path
|
|
14
|
+
strings. A hand-maintained path registry would reintroduce the very
|
|
15
|
+
desync risk this guards against, one layer down.
|
|
16
|
+
- Scope is strictly the single-root hard-coders. Multi-root gates that
|
|
17
|
+
resolve via ``artefact_roots()`` (e.g. ``iron_law_sha``) are excluded:
|
|
18
|
+
asserting a single ``packages/core/`` path for them would false-pass on a
|
|
19
|
+
legacy layout or false-fail on a pack-only layout.
|
|
20
|
+
|
|
21
|
+
The input set is this gate list (no separate config file).
|
|
22
|
+
|
|
23
|
+
Usage:
|
|
24
|
+
python3 scripts/check_gate_paths.py
|
|
25
|
+
Exit codes: 0 = all enforced targets resolve under packages/core/ ·
|
|
26
|
+
1 = at least one missing / out-of-tree target · 2 = a gate failed to import.
|
|
27
|
+
"""
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import importlib
|
|
31
|
+
import sys
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
|
|
34
|
+
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
35
|
+
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
|
36
|
+
from _lib.agent_src import resolve_package_core_path # noqa: E402
|
|
37
|
+
|
|
38
|
+
PACKAGE_CORE = resolve_package_core_path("")
|
|
39
|
+
|
|
40
|
+
# Single-root gates that enforce against a fixed packages/core/ target and
|
|
41
|
+
# expose it via a module-level GATE_CORE_PATHS tuple. Adding a gate here is
|
|
42
|
+
# the only manual step; its paths are read from the gate, never copied.
|
|
43
|
+
GATES: tuple[str, ...] = (
|
|
44
|
+
"inventory_abstraction_budget",
|
|
45
|
+
"audit_command_surface",
|
|
46
|
+
"lint_agents_md",
|
|
47
|
+
"audit_initial_context",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _is_under_core(p: Path) -> bool:
|
|
52
|
+
try:
|
|
53
|
+
p.resolve().relative_to(PACKAGE_CORE.resolve())
|
|
54
|
+
return True
|
|
55
|
+
except ValueError:
|
|
56
|
+
return False
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def collect_gate_paths(gate_modules: tuple[str, ...]) -> dict[str, list[Path]]:
|
|
60
|
+
"""Import each gate and read its declared ``GATE_CORE_PATHS``.
|
|
61
|
+
|
|
62
|
+
Raises ``ImportError`` (surfaced as exit 2 by ``main``) if a gate cannot
|
|
63
|
+
be imported — a gate whose path logic broke at import time is itself a
|
|
64
|
+
failure this check should not swallow.
|
|
65
|
+
"""
|
|
66
|
+
out: dict[str, list[Path]] = {}
|
|
67
|
+
for name in gate_modules:
|
|
68
|
+
mod = importlib.import_module(name)
|
|
69
|
+
paths = getattr(mod, "GATE_CORE_PATHS", None)
|
|
70
|
+
if not paths:
|
|
71
|
+
raise AttributeError(
|
|
72
|
+
f"{name} has no non-empty GATE_CORE_PATHS — gate cannot be "
|
|
73
|
+
f"checked. Declare the packages/core targets it enforces."
|
|
74
|
+
)
|
|
75
|
+
out[name] = [Path(p) for p in paths]
|
|
76
|
+
return out
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def check_paths(named: dict[str, list[Path]]) -> list[tuple[str, str, Path]]:
|
|
80
|
+
"""Return ``(gate, reason, path)`` for every target that fails.
|
|
81
|
+
|
|
82
|
+
Pure (no import side effects) so tests can drive it with fixtures.
|
|
83
|
+
A target fails when it does not resolve under ``packages/core/`` or
|
|
84
|
+
does not exist on disk.
|
|
85
|
+
"""
|
|
86
|
+
failures: list[tuple[str, str, Path]] = []
|
|
87
|
+
for gate, paths in named.items():
|
|
88
|
+
for p in paths:
|
|
89
|
+
if not _is_under_core(p):
|
|
90
|
+
failures.append((gate, "not under packages/core/", p))
|
|
91
|
+
elif not p.exists():
|
|
92
|
+
failures.append((gate, "target does not exist", p))
|
|
93
|
+
return failures
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def main() -> int:
|
|
97
|
+
try:
|
|
98
|
+
named = collect_gate_paths(GATES)
|
|
99
|
+
except (ImportError, AttributeError) as exc:
|
|
100
|
+
print(f"❌ check-gate-paths: {exc}", file=sys.stderr)
|
|
101
|
+
return 2
|
|
102
|
+
failures = check_paths(named)
|
|
103
|
+
if failures:
|
|
104
|
+
print("❌ check-gate-paths: gate target(s) do not resolve under packages/core/:")
|
|
105
|
+
for gate, reason, path in failures:
|
|
106
|
+
print(f" {gate}: {reason} → {path}")
|
|
107
|
+
print("\n A packages/core/ move likely desynced a gate. Fix the gate's")
|
|
108
|
+
print(" GATE_CORE_PATHS (built via resolve_package_core_path) or the move.")
|
|
109
|
+
return 1
|
|
110
|
+
total = sum(len(v) for v in named.values())
|
|
111
|
+
print(f"✅ check-gate-paths: {total} enforced target(s) across "
|
|
112
|
+
f"{len(named)} gate(s) resolve under packages/core/.")
|
|
113
|
+
return 0
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
if __name__ == "__main__":
|
|
117
|
+
raise SystemExit(main())
|
|
@@ -133,6 +133,53 @@ EXAMPLE_PATH_PATTERNS = [
|
|
|
133
133
|
]
|
|
134
134
|
|
|
135
135
|
|
|
136
|
+
@dataclass(frozen=True)
|
|
137
|
+
class AllowlistPattern:
|
|
138
|
+
"""A token-class allowlist entry. `reason` is mandatory and auditable."""
|
|
139
|
+
pattern: "re.Pattern[str]"
|
|
140
|
+
reason: str
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# Content-class allowlist for known NON-reference token shapes.
|
|
144
|
+
#
|
|
145
|
+
# The skill/rule prose patterns (`X` skill / `X` rule) occasionally match
|
|
146
|
+
# a backtick token that is not an artifact id — an execution-type enum
|
|
147
|
+
# value, a pack identifier, or a bare meta-qualifier keyword. Historically
|
|
148
|
+
# each such false positive was dodged by *rewording the prose per file*
|
|
149
|
+
# (e.g. dc84ed01 "reword execution-type mentions to dodge check-refs
|
|
150
|
+
# false positive", bd02ef0b "avoid check-refs false-positive on pack
|
|
151
|
+
# name"), a treadmill that distorts natural wording release after release.
|
|
152
|
+
# This layer matches the token *class* centrally instead, so the natural
|
|
153
|
+
# wording passes without per-file edits. It is distinct from:
|
|
154
|
+
# - SKIP_DIRS (path-level, whole-directory)
|
|
155
|
+
# - FILE_SKIP_MARKER (file-level opt-out)
|
|
156
|
+
# - LINE_IGNORE_MARKER (per-line opt-out)
|
|
157
|
+
# Every entry carries a mandatory `reason` so the allowlist stays
|
|
158
|
+
# auditable and a future reader can tell why a class is exempt.
|
|
159
|
+
ALLOWLIST_PATTERNS: List[AllowlistPattern] = [
|
|
160
|
+
AllowlistPattern(
|
|
161
|
+
re.compile(r"^(?:manual|assisted|automated)$"),
|
|
162
|
+
"execution-type enum value (runtime-safety frontmatter), e.g. a "
|
|
163
|
+
"`manual` skill — not a skill/rule id (dc84ed01)",
|
|
164
|
+
),
|
|
165
|
+
AllowlistPattern(
|
|
166
|
+
re.compile(r"^pack-[\w-]+$"),
|
|
167
|
+
"pack / workspace identifier, e.g. `pack-ai-video` skills — not a "
|
|
168
|
+
"skill/rule id (bd02ef0b)",
|
|
169
|
+
),
|
|
170
|
+
AllowlistPattern(
|
|
171
|
+
re.compile(r"^(?:skill|rule|command|guideline|persona|context|pack|workspace)$"),
|
|
172
|
+
"bare meta-qualifier keyword used in prose (the `command` vs "
|
|
173
|
+
"`skill` distinction, etc.) — not an artifact id",
|
|
174
|
+
),
|
|
175
|
+
]
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _is_allowlisted(name: str) -> bool:
|
|
179
|
+
"""True when `name` matches a known non-reference token class."""
|
|
180
|
+
return any(entry.pattern.match(name) for entry in ALLOWLIST_PATTERNS)
|
|
181
|
+
|
|
182
|
+
|
|
136
183
|
def collect_artifacts(root: Path) -> dict[str, set[str]]:
|
|
137
184
|
"""Build lookup sets for skills, rules, commands, guidelines, personas."""
|
|
138
185
|
arts: dict[str, set[str]] = {
|
|
@@ -345,7 +392,8 @@ def check_file(filepath: Path, artifacts: dict[str, set[str]], root: Path) -> Li
|
|
|
345
392
|
# Skill name references
|
|
346
393
|
for m in SKILL_REF_PATTERN.finditer(line):
|
|
347
394
|
name = m.group(1)
|
|
348
|
-
if name not in artifacts["skills"] and name not in _SKIP_NAMES
|
|
395
|
+
if name not in artifacts["skills"] and name not in _SKIP_NAMES \
|
|
396
|
+
and not _is_allowlisted(name):
|
|
349
397
|
broken.append(BrokenRef(
|
|
350
398
|
file=str(filepath), line=i, ref=name,
|
|
351
399
|
ref_type="skill", severity="warning",
|
|
@@ -355,7 +403,8 @@ def check_file(filepath: Path, artifacts: dict[str, set[str]], root: Path) -> Li
|
|
|
355
403
|
# Rule name references
|
|
356
404
|
for m in RULE_REF_PATTERN.finditer(line):
|
|
357
405
|
name = m.group(1)
|
|
358
|
-
if name not in artifacts["rules"] and name not in _SKIP_NAMES
|
|
406
|
+
if name not in artifacts["rules"] and name not in _SKIP_NAMES \
|
|
407
|
+
and not _is_allowlisted(name):
|
|
359
408
|
broken.append(BrokenRef(
|
|
360
409
|
file=str(filepath), line=i, ref=name,
|
|
361
410
|
ref_type="rule", severity="warning",
|