@event4u/agent-config 2.16.0 → 2.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent-src/commands/ghostwriter/delete.md +118 -0
- package/.agent-src/commands/ghostwriter/fetch.md +185 -0
- package/.agent-src/commands/ghostwriter/list.md +102 -0
- package/.agent-src/commands/ghostwriter/show.md +113 -0
- package/.agent-src/commands/ghostwriter/write.md +160 -0
- package/.agent-src/commands/ghostwriter.md +96 -0
- package/.agent-src/commands/post-as/ghostwriter.md +66 -0
- package/.agent-src/commands/post-as/me.md +124 -0
- package/.agent-src/commands/post-as.md +58 -0
- package/.agent-src/ghostwriter/README.md +61 -0
- package/.agent-src/ghostwriter/fictional-fixture-v1.md +94 -0
- package/.agent-src/personas/README.md +8 -0
- package/.agent-src/rules/domain-safety-disclaimer-consulting.md +52 -0
- package/.agent-src/rules/domain-safety-disclaimer-financial.md +54 -0
- package/.agent-src/rules/domain-safety-disclaimer-legal.md +49 -0
- package/.agent-src/rules/domain-safety-disclaimer-medical.md +56 -0
- package/.agent-src/rules/domain-safety-export-redact.md +65 -0
- package/.agent-src/rules/domain-safety-logging-pii-floor.md +55 -0
- package/.agent-src/rules/domain-safety-pii-finance.md +57 -0
- package/.agent-src/rules/domain-safety-pii-marketing.md +60 -0
- package/.agent-src/rules/domain-safety-pii-recruiting.md +56 -0
- package/.agent-src/rules/domain-safety-pii-support.md +57 -0
- package/.agent-src/rules/domain-safety-retention-finance.md +48 -0
- package/.agent-src/rules/domain-safety-retention-support.md +55 -0
- package/.agent-src/skills/api-design/SKILL.md +3 -0
- package/.agent-src/skills/authz-review/SKILL.md +3 -0
- package/.agent-src/skills/competitive-moat-analysis/SKILL.md +3 -0
- package/.agent-src/skills/competitive-positioning/SKILL.md +3 -0
- package/.agent-src/skills/content-funnel-design/SKILL.md +3 -0
- package/.agent-src/skills/contracts-cognition/SKILL.md +3 -0
- package/.agent-src/skills/dashboard-design/SKILL.md +3 -0
- package/.agent-src/skills/data-handling-judgment/SKILL.md +3 -0
- package/.agent-src/skills/dcf-modeling/SKILL.md +3 -0
- package/.agent-src/skills/deal-qualification-meddic/SKILL.md +3 -0
- package/.agent-src/skills/discovery-interview/SKILL.md +3 -0
- package/.agent-src/skills/editorial-calendar/SKILL.md +3 -0
- package/.agent-src/skills/forecast-accuracy/SKILL.md +3 -0
- package/.agent-src/skills/forecasting/SKILL.md +3 -0
- package/.agent-src/skills/fundraising-narrative/SKILL.md +3 -0
- package/.agent-src/skills/gtm-launch/SKILL.md +3 -0
- package/.agent-src/skills/incident-commander/SKILL.md +3 -0
- package/.agent-src/skills/launch-readiness/SKILL.md +3 -0
- package/.agent-src/skills/messaging-architecture/SKILL.md +3 -0
- package/.agent-src/skills/okr-tree-modeling/SKILL.md +3 -0
- package/.agent-src/skills/pipeline-strategy/SKILL.md +3 -0
- package/.agent-src/skills/playwright-architect/SKILL.md +3 -0
- package/.agent-src/skills/privacy-review/SKILL.md +4 -1
- package/.agent-src/skills/quality-tools/SKILL.md +3 -0
- package/.agent-src/skills/release-comms/SKILL.md +3 -0
- package/.agent-src/skills/runway-cognition/SKILL.md +3 -0
- package/.agent-src/skills/scenario-modeling/SKILL.md +3 -0
- package/.agent-src/skills/secrets-management/SKILL.md +3 -0
- package/.agent-src/skills/tech-debt-tracker/SKILL.md +3 -0
- package/.agent-src/skills/unit-economics-modeling/SKILL.md +3 -0
- package/.agent-src/skills/voc-extract/SKILL.md +3 -0
- package/.agent-src/skills/voice-and-tone-design/SKILL.md +3 -0
- package/.agent-src/templates/agents/agent-project-settings.example.yml +16 -1
- package/.claude-plugin/marketplace.json +10 -1
- package/CHANGELOG.md +69 -0
- package/README.md +44 -23
- package/config/gitignore-block.txt +8 -0
- package/docs/announcements/2026-05-non-dev-launch.md +79 -0
- package/docs/architecture.md +2 -2
- package/docs/case-studies/_template.md +60 -0
- package/docs/catalog.md +24 -3
- package/docs/contracts/agent-user-schema.md +1 -0
- package/docs/contracts/command-clusters.md +2 -0
- package/docs/contracts/file-ownership-matrix.json +490 -0
- package/docs/contracts/ghostwriter-schema.md +337 -0
- package/docs/contracts/init-telemetry.md +133 -0
- package/docs/contracts/router-blending.md +71 -0
- package/docs/contracts/universal-skills.md +92 -0
- package/docs/contracts/write-engine.md +142 -0
- package/docs/getting-started-by-role.md +89 -0
- package/docs/getting-started-laravel.md +72 -0
- package/docs/getting-started.md +2 -2
- package/docs/safety.md +30 -0
- package/package.json +1 -1
- package/scripts/bench_runner.py +158 -0
- package/scripts/check_role_doc_links.py +110 -0
- package/scripts/compress.py +11 -0
- package/scripts/ghostwriter_fixture_allowlist.txt +16 -0
- package/scripts/install.py +133 -1
- package/scripts/lint_ghostwriter_source.py +240 -0
- package/scripts/measure_skill_reduction.py +102 -0
- package/scripts/schemas/rule.schema.json +5 -0
- package/scripts/schemas/skill.schema.json +6 -0
- package/scripts/update-github-metadata.sh +84 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
stability: beta
|
|
3
|
+
keep-beta-until: 2026-08-13
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Write-engine contract (v1)
|
|
7
|
+
|
|
8
|
+
> **Status:** beta — shared procedural contract consumed by
|
|
9
|
+
> `/ghostwriter:write`, `/post-as:ghostwriter` (alias), and
|
|
10
|
+
> `/post-as:me`. Locked alongside the ghostwriter cluster roadmap.
|
|
11
|
+
|
|
12
|
+
The **write engine** is the deterministic procedure that produces a
|
|
13
|
+
copyable markdown draft in a captured voice. It deliberately has no
|
|
14
|
+
implementation file — the engine is a sequence of steps the host
|
|
15
|
+
agent follows verbatim. The same steps are referenced by every
|
|
16
|
+
consumer command; the **only** axis of variation is:
|
|
17
|
+
|
|
18
|
+
1. **Style source** — which file the engine reads to load the voice.
|
|
19
|
+
2. **Disclosure footer** — appended when the style source is *external*
|
|
20
|
+
(a ghostwriter profile), omitted when the style source is *self*
|
|
21
|
+
(`.agent-user.md`).
|
|
22
|
+
|
|
23
|
+
## Style sources
|
|
24
|
+
|
|
25
|
+
| Consumer command | Style source | Footer |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| `/ghostwriter:write` | `agents/ghostwriter/<slug>.md` (selected) | **Mandatory** |
|
|
28
|
+
| `/post-as:ghostwriter` | Same as above (thin alias) | **Mandatory** |
|
|
29
|
+
| `/post-as:me` | `.agent-user.md` (project root) | **Omitted** — user is the author |
|
|
30
|
+
|
|
31
|
+
No other style source is permitted in v1. Consuming `personas/*.md`
|
|
32
|
+
voices is explicitly out of scope — personas are review lenses, not
|
|
33
|
+
author voices.
|
|
34
|
+
|
|
35
|
+
## Procedure (followed verbatim by every consumer)
|
|
36
|
+
|
|
37
|
+
### 1. Resolve the style source
|
|
38
|
+
|
|
39
|
+
Each consumer command resolves a single style source before invoking
|
|
40
|
+
the engine. The engine receives a fully populated style object with:
|
|
41
|
+
|
|
42
|
+
- `identity.name` (for the footer when applicable)
|
|
43
|
+
- `style.fingerprint.*` (sentence length, register, opener / closer
|
|
44
|
+
patterns, hashtag / emoji rules, paragraph cadence)
|
|
45
|
+
- `style.free_form_notes` (or `voice_sample` for `/post-as:me`)
|
|
46
|
+
- `taboos` (ghostwriter only — empty list for `/post-as:me`)
|
|
47
|
+
|
|
48
|
+
Missing style source → consumer command aborts with a pointer at the
|
|
49
|
+
appropriate setup command (`/ghostwriter:fetch` or `/agents user init`).
|
|
50
|
+
|
|
51
|
+
### 2. Collect the topic + modifiers
|
|
52
|
+
|
|
53
|
+
One question per turn, in this order:
|
|
54
|
+
|
|
55
|
+
1. **Topic** — required. Plain prose, no quoting.
|
|
56
|
+
2. **Tone** — optional. Enum: `formal | casual | neutral`. Default
|
|
57
|
+
inherits `style.fingerprint.vocab_register` mapped per the table
|
|
58
|
+
below.
|
|
59
|
+
3. **Length** — optional. Integer word count. Default: see the
|
|
60
|
+
per-channel defaults table.
|
|
61
|
+
4. **Channel** — optional. Enum: `linkedin-post | tweet | blog | freeform`.
|
|
62
|
+
Default: `freeform`.
|
|
63
|
+
5. **Audience** — optional. Free-form one-line descriptor.
|
|
64
|
+
|
|
65
|
+
Modifiers may be supplied via flags (`--tone=casual --length=200
|
|
66
|
+
--channel=linkedin-post --audience="early-stage founders"`) to skip
|
|
67
|
+
the interactive interview. `--as=<slug>` selects the ghostwriter
|
|
68
|
+
non-interactively for `/ghostwriter:write` and the alias.
|
|
69
|
+
|
|
70
|
+
#### Per-channel defaults
|
|
71
|
+
|
|
72
|
+
| Channel | Length default | Cadence guidance |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| `tweet` | 50 words | One paragraph, no lists. |
|
|
75
|
+
| `linkedin-post` | 180 words | 2–4 short paragraphs. Hashtags only if `style.fingerprint.hashtag_rules` allows. |
|
|
76
|
+
| `blog` | 600 words | Inherits `style.fingerprint.paragraph_cadence`. |
|
|
77
|
+
| `freeform` | 250 words | Inherits `style.fingerprint.paragraph_cadence`. |
|
|
78
|
+
|
|
79
|
+
#### Vocab-register → tone mapping (default inheritance)
|
|
80
|
+
|
|
81
|
+
| `vocab_register` | Default `tone` |
|
|
82
|
+
|---|---|
|
|
83
|
+
| `casual` / `conversational` | `casual` |
|
|
84
|
+
| `professional` / `literary` | `neutral` |
|
|
85
|
+
| `academic` | `formal` |
|
|
86
|
+
|
|
87
|
+
### 3. Apply negative-constraint pass (ghostwriter only)
|
|
88
|
+
|
|
89
|
+
Before drafting, the engine surfaces the loaded `taboos` list and
|
|
90
|
+
explicitly excludes those moves from the draft (no political
|
|
91
|
+
endorsements, no profanity, no hashtag-driven posts, etc.). For
|
|
92
|
+
`/post-as:me`, the negative-constraint pass is skipped (the user does
|
|
93
|
+
not pre-declare taboos in v1).
|
|
94
|
+
|
|
95
|
+
### 4. Draft
|
|
96
|
+
|
|
97
|
+
Emit the body as a single fenced markdown block. The body MUST:
|
|
98
|
+
|
|
99
|
+
- Match `style.fingerprint.sentence_length_avg` within ±25%.
|
|
100
|
+
- Honour `style.fingerprint.opener_patterns` for the first sentence.
|
|
101
|
+
- Honour `style.fingerprint.closer_patterns` for the last sentence.
|
|
102
|
+
- Respect `style.fingerprint.hashtag_rules` and `emoji_rules`.
|
|
103
|
+
- Stay within ±15% of the requested length.
|
|
104
|
+
|
|
105
|
+
### 5. Append the disclosure footer (ghostwriter only)
|
|
106
|
+
|
|
107
|
+
For ghostwriter consumers, append on its own line, separated by a
|
|
108
|
+
blank line:
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
Written in the style of <identity.name>, not by them.
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The footer is appended **by the command's output template** as a
|
|
115
|
+
literal string — it is not generated by the model and has no opt-out
|
|
116
|
+
flag. For `/post-as:me` the footer is omitted entirely (the user is
|
|
117
|
+
the author).
|
|
118
|
+
|
|
119
|
+
### 6. Print the draft
|
|
120
|
+
|
|
121
|
+
Print the fenced markdown block. No commit, no save, no file write.
|
|
122
|
+
The user copies the output manually. The engine performs no side
|
|
123
|
+
effects on disk.
|
|
124
|
+
|
|
125
|
+
## Rules
|
|
126
|
+
|
|
127
|
+
- **No `--no-disclosure` flag.** For any ghostwriter consumer, the
|
|
128
|
+
footer is mandatory and deterministic. `task lint-skills` greps the
|
|
129
|
+
ghostwriter command sources for the literal string `no-disclosure`
|
|
130
|
+
and fails CI on a hit.
|
|
131
|
+
- **No multi-voice draft in v1.** One style source per invocation;
|
|
132
|
+
blending voices is deferred.
|
|
133
|
+
- **No file writes.** The engine prints; the user copies. Saving the
|
|
134
|
+
output to `agents/` or anywhere else is a future feature.
|
|
135
|
+
|
|
136
|
+
## See also
|
|
137
|
+
|
|
138
|
+
- [`ghostwriter-schema`](ghostwriter-schema.md) — the source schema
|
|
139
|
+
for `/ghostwriter:write`.
|
|
140
|
+
- [`agent-user-schema`](agent-user-schema.md) — the source schema
|
|
141
|
+
for `/post-as:me`.
|
|
142
|
+
- [`command-clusters`](command-clusters.md) — cluster registration.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Getting started — by role
|
|
2
|
+
|
|
3
|
+
> Pick the entry that matches what you do day-to-day. Each section names the three skills you will reach for first and shows whether MCP (no terminal) or CLI (terminal) is the simpler install path for that role.
|
|
4
|
+
|
|
5
|
+
`agent-config` ships ~210 skills, ~67 rules, and ~124 commands. You do not need all of them. Each role below filters to the slice that pays back in week one; the rest stays available and shows up on demand when a task references it.
|
|
6
|
+
|
|
7
|
+
> **Eval-gated messaging note.** Until `task bench --corpus non-dev` reports `selection_accuracy >= 0.60` (step-12 Phase 1 exit), this page is documentation, not marketing. The skills listed below are the candidates the corpus tests against; their description quality is what the eval validates. See [`agents/roadmaps/step-12-universal-os-reframe.md`](../agents/roadmaps/step-12-universal-os-reframe.md).
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Creator (writer, marketer, indie content shop)
|
|
12
|
+
|
|
13
|
+
**You want this if:** you draft blog posts, marketing emails, launch copy, or release announcements and want a writing assistant that holds a defined brand voice across surfaces. You need brand-voice discipline more than code-quality enforcement. You will spend most of your time in Claude Desktop / ChatGPT, not in a terminal.
|
|
14
|
+
|
|
15
|
+
- [`voice-and-tone-design`](../.agent-src/skills/voice-and-tone-design/SKILL.md) — define and audit brand voice (voice attributes, tone-by-context matrix).
|
|
16
|
+
- [`messaging-architecture`](../.agent-src/skills/messaging-architecture/SKILL.md) — primary message + supporting proofs + audience-by-message matrix.
|
|
17
|
+
- [`editorial-calendar`](../.agent-src/skills/editorial-calendar/SKILL.md) — evergreen vs campaign vs reactive cadence across channels.
|
|
18
|
+
|
|
19
|
+
**Install path:** **MCP recommended.** Claude Desktop is the lowest-friction entry; no terminal required. See [`docs/mcp.md`](mcp.md). CLI install works too if you already use a code editor.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Founder (early-stage operator wearing every hat)
|
|
24
|
+
|
|
25
|
+
**You want this if:** you switch between investor pitch, hiring decision, product spec, and unit-economics modeling in the same week. You need cross-domain skills that respect your time budget, not depth-first specialists. Decisions need to be defensible to a board.
|
|
26
|
+
|
|
27
|
+
- [`runway-cognition`](../.agent-src/skills/runway-cognition/SKILL.md) — cash runway, burn shape, fundraise triggers, cut-vs-grow.
|
|
28
|
+
- [`unit-economics-modeling`](../.agent-src/skills/unit-economics-modeling/SKILL.md) — CAC, LTV, payback, contribution margin per customer.
|
|
29
|
+
- [`fundraising-narrative`](../.agent-src/skills/fundraising-narrative/SKILL.md) — why-now / why-us / why-this framing, market-size reasoning.
|
|
30
|
+
|
|
31
|
+
**Install path:** **MCP for advisory work, CLI when you touch code.** Claude Desktop covers strategy / finance / narrative; CLI is needed only when you sit in the repo with the dev team.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Developer (the original audience)
|
|
36
|
+
|
|
37
|
+
**You want this if:** you write code daily — Laravel, Symfony, Next.js, Node, or stack-agnostic — and want testing / quality / git / CI guardrails baked into the agent's behavior. You will use commands like `/work`, `/commit`, `/create-pr`, `/quality-fix` constantly.
|
|
38
|
+
|
|
39
|
+
- [`laravel`](../.agent-src/skills/laravel/SKILL.md) — Laravel-flavored PHP (Eloquent, Artisan, FormRequests, jobs, policies). See [`docs/getting-started-laravel.md`](getting-started-laravel.md) for the deep dive.
|
|
40
|
+
- [`nextjs-patterns`](../.agent-src/skills/nextjs-patterns/SKILL.md) — App Router, Server Components, Server Actions, caching.
|
|
41
|
+
- [`quality-tools`](../.agent-src/skills/quality-tools/SKILL.md) — PHPStan, Rector, ECS error triage and fix loop.
|
|
42
|
+
|
|
43
|
+
**Install path:** **CLI.** Run `npx @event4u/agent-config init --tools=claude-code,cursor` in the project root. MCP works too but loses git / file-system tooling that the IDE-integrated path gives you.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Consultant (advisory, freelance, fractional)
|
|
48
|
+
|
|
49
|
+
**You want this if:** you sell discovery, positioning, competitive analysis, or roadmap audits. Output is briefs and slide content for a client, not code. You need defensible methodology behind every deliverable.
|
|
50
|
+
|
|
51
|
+
- [`discovery-interview`](../.agent-src/skills/discovery-interview/SKILL.md) — switch-event JTBD guides, bias audit, falsifiable hypothesis.
|
|
52
|
+
- [`competitive-moat-analysis`](../.agent-src/skills/competitive-moat-analysis/SKILL.md) — moat reasoning, where-to-play / where-not-to-play.
|
|
53
|
+
- [`stakeholder-tradeoff`](../.agent-src/skills/stakeholder-tradeoff/SKILL.md) — per-lens framing, trade-off matrix with cost per choice.
|
|
54
|
+
|
|
55
|
+
**Install path:** **MCP recommended.** Most consulting work is doc + slide drafting; the terminal adds friction without payback. Switch to CLI only if you also write code for the client.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Go-To-Market (sales, marketing ops, RevOps)
|
|
60
|
+
|
|
61
|
+
**You want this if:** you own pipeline shape, forecast accuracy, launch sequencing, or post-launch comms. You need deal-level rigor (MEDDIC, exit criteria) and narrative skills (release comms, messaging) in the same agent.
|
|
62
|
+
|
|
63
|
+
- [`pipeline-strategy`](../.agent-src/skills/pipeline-strategy/SKILL.md) — stage exit criteria, per-cell conversion, leak diagnosis.
|
|
64
|
+
- [`deal-qualification-meddic`](../.agent-src/skills/deal-qualification-meddic/SKILL.md) — MEDDIC slots with evidence, inversion test, disqualification heuristic.
|
|
65
|
+
- [`release-comms`](../.agent-src/skills/release-comms/SKILL.md) — value-not-feature framing, audience-segmented surfaces.
|
|
66
|
+
|
|
67
|
+
**Install path:** **MCP recommended.** GTM artifacts are documents, decks, and Notion pages; Claude Desktop is the natural home.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Finance / Ops (CFO, controller, ops lead, founder-finance)
|
|
72
|
+
|
|
73
|
+
**You want this if:** you build forecasts, model scenarios, and review data-handling for compliance. You need the agent to keep accounting / regulatory framing straight, not invent numbers.
|
|
74
|
+
|
|
75
|
+
- [`forecasting`](../.agent-src/skills/forecasting/SKILL.md) — top-down vs bottom-up shape, confidence bands, retro-loop.
|
|
76
|
+
- [`scenario-modeling`](../.agent-src/skills/scenario-modeling/SKILL.md) — base / upside / downside, three-statement modeling, sensitivity.
|
|
77
|
+
- [`privacy-review`](../.agent-src/skills/privacy-review/SKILL.md) — GDPR / CCPA / HIPAA fit, cross-border transfer, breach-impact triage.
|
|
78
|
+
|
|
79
|
+
**Install path:** **MCP recommended.** Finance / ops workflows are spreadsheet- and document-heavy; the CLI buys nothing here unless you also export models into a code repo.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## What is the same regardless of role
|
|
84
|
+
|
|
85
|
+
A short universal-skills allowlist (`git`, `refine-ticket`, `proofread`, `threat-model`, etc.) loads in every profile. The list will live at `docs/contracts/universal-skills.md` once step-12 Phase 3 lands; until then the package loads all skills and the host agent's semantic search picks what the prompt needs.
|
|
86
|
+
|
|
87
|
+
## What this page does not promise
|
|
88
|
+
|
|
89
|
+
This page lists **candidate** skill / role pairings. Whether each skill's `description:` is sharp enough for the agent to retrieve it without manual hint is exactly what `tests/eval/corpus-non-dev.yaml` tests. If a prompt in your role above falls flat, that is a skill-description bug — file an issue or open a PR with a sharper description, do not work around it by naming the skill manually.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Getting started — Laravel
|
|
2
|
+
|
|
3
|
+
> Laravel is the deepest reference stack in the package today. This page collects the Laravel-specific guidance previously embedded in the root README. The relocation is part of step-12 Phase 2; the root README continues to surface Laravel under the dev role until the Phase 6 identity rewrite lands.
|
|
4
|
+
|
|
5
|
+
## Why Laravel is the reference stack
|
|
6
|
+
|
|
7
|
+
Laravel ships with the broadest, battle-tested skill coverage in the package — Pest, PHPStan, Rector, Eloquent, Livewire / Flux, Horizon, Pulse, Reverb, Pennant. That coverage exists because the package was originally extracted from a Laravel monorepo (Galawork). Symfony and Next.js are the second tier (`symfony-workflow`, `nextjs-patterns`); other stacks ship as they are battle-tested, not second-class.
|
|
8
|
+
|
|
9
|
+
## Laravel-flavored skills
|
|
10
|
+
|
|
11
|
+
| Skill | What it covers |
|
|
12
|
+
|---|---|
|
|
13
|
+
| [`laravel`](../.agent-src/skills/laravel/SKILL.md) | Eloquent, Artisan controllers, FormRequests, jobs, events, policies, providers |
|
|
14
|
+
| [`eloquent`](../.agent-src/skills/eloquent/SKILL.md) | Models, relationships, scopes, query patterns |
|
|
15
|
+
| [`artisan-commands`](../.agent-src/skills/artisan-commands/SKILL.md) | Console command structure, signatures, safe execution |
|
|
16
|
+
| [`jobs-events`](../.agent-src/skills/jobs-events/SKILL.md) | Queued workflows, listeners, retry / failure handling |
|
|
17
|
+
| [`laravel-validation`](../.agent-src/skills/laravel-validation/SKILL.md) | Form Requests, rules, custom rule objects |
|
|
18
|
+
| [`laravel-middleware`](../.agent-src/skills/laravel-middleware/SKILL.md) | Request / response filtering, groups, priority |
|
|
19
|
+
| [`laravel-notifications`](../.agent-src/skills/laravel-notifications/SKILL.md) | Mail, Slack, database, custom channels |
|
|
20
|
+
| [`laravel-mail`](../.agent-src/skills/laravel-mail/SKILL.md) | Mailables, Markdown templates, queued sending |
|
|
21
|
+
| [`laravel-scheduling`](../.agent-src/skills/laravel-scheduling/SKILL.md) | Cron expressions, overlap prevention, maintenance mode |
|
|
22
|
+
| [`laravel-horizon`](../.agent-src/skills/laravel-horizon/SKILL.md) | Worker supervision, job metrics, balancing strategies |
|
|
23
|
+
| [`laravel-pulse`](../.agent-src/skills/laravel-pulse/SKILL.md) | Real-time dashboard, custom recorders, performance insights |
|
|
24
|
+
| [`laravel-reverb`](../.agent-src/skills/laravel-reverb/SKILL.md) | First-party WebSocket server, Pusher protocol compatibility |
|
|
25
|
+
| [`laravel-pennant`](../.agent-src/skills/laravel-pennant/SKILL.md) | Feature flags, gradual rollouts, A/B testing |
|
|
26
|
+
|
|
27
|
+
## Quality pipeline
|
|
28
|
+
|
|
29
|
+
The Laravel quality pipeline runs PHPStan + Rector + ECS, with Pest as the test runner:
|
|
30
|
+
|
|
31
|
+
- [`quality-tools`](../.agent-src/skills/quality-tools/SKILL.md) — PHPStan output triage, Rector apply, ECS fix.
|
|
32
|
+
- [`pest-testing`](../.agent-src/skills/pest-testing/SKILL.md) — Pest test authoring patterns.
|
|
33
|
+
- [`/quality-fix`](../.agent-src/commands/quality-fix.md) — runs the full pipeline and fixes reported errors.
|
|
34
|
+
|
|
35
|
+
## Docker and dev environment
|
|
36
|
+
|
|
37
|
+
- [`docker`](../.agent-src/skills/docker/SKILL.md) — Dockerfile, compose, dual-container (fast + Xdebug) setup.
|
|
38
|
+
- [`php-debugging`](../.agent-src/skills/php-debugging/SKILL.md) — Xdebug breakpoints, dual-container, header-based routing.
|
|
39
|
+
- [`traefik`](../.agent-src/skills/traefik/SKILL.md) — local reverse proxy, real domains on 127.0.0.1, mkcert HTTPS.
|
|
40
|
+
|
|
41
|
+
## Multi-tenancy and database
|
|
42
|
+
|
|
43
|
+
- [`multi-tenancy`](../.agent-src/skills/multi-tenancy/SKILL.md) — customer DB switching, FQDN routing, tenant isolation.
|
|
44
|
+
- [`database`](../.agent-src/skills/database/SKILL.md) — MariaDB / MySQL tuning, indexing, multi-connection patterns.
|
|
45
|
+
- [`sql-writing`](../.agent-src/skills/sql-writing/SKILL.md) — raw SQL, parameterization, raw migrations.
|
|
46
|
+
|
|
47
|
+
## Project analysis
|
|
48
|
+
|
|
49
|
+
- [`project-analysis-laravel`](../.agent-src/skills/project-analysis-laravel/SKILL.md) — boot flow, request lifecycle, container usage, async systems, Laravel-specific failure patterns.
|
|
50
|
+
|
|
51
|
+
## Install for a Laravel project
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
cd path/to/your/laravel/app
|
|
55
|
+
npx @event4u/agent-config init --tools=claude-code,cursor
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The installer detects `composer.json` + `artisan` + the Laravel framework dependency, enables stack-aware skills, and writes `.agent-settings.yml` with sensible defaults. The first `/onboard` run captures your name, preferred IDE, and cost profile.
|
|
59
|
+
|
|
60
|
+
## Other PHP stacks
|
|
61
|
+
|
|
62
|
+
| Stack | Coverage |
|
|
63
|
+
|---|---|
|
|
64
|
+
| **Symfony** | [`symfony-workflow`](../.agent-src/skills/symfony-workflow/SKILL.md) — DI, Doctrine, Messenger, voters, Twig; [`project-analysis-symfony`](../.agent-src/skills/project-analysis-symfony/SKILL.md) |
|
|
65
|
+
| **Zend / Laminas** | [`project-analysis-zend-laminas`](../.agent-src/skills/project-analysis-zend-laminas/SKILL.md) + shared PHP coder / quality skills |
|
|
66
|
+
| **Framework-free PHP** | [`php-coder`](../.agent-src/skills/php-coder/SKILL.md) — modern idioms, SOLID refactors, type hints without framework lock-in |
|
|
67
|
+
|
|
68
|
+
## See also
|
|
69
|
+
|
|
70
|
+
- [`docs/getting-started-by-role.md`](getting-started-by-role.md) — pick the entry that matches your day-to-day.
|
|
71
|
+
- [`docs/getting-started.md`](getting-started.md) — generic three-step quickstart.
|
|
72
|
+
- [`docs/installation.md`](installation.md) — detailed install variants (npx, curl, global npm).
|
package/docs/getting-started.md
CHANGED
|
@@ -106,7 +106,7 @@ Your agent is now:
|
|
|
106
106
|
- **Respecting your codebase** — no conflicting patterns
|
|
107
107
|
- **Following standards** — consistent code quality
|
|
108
108
|
|
|
109
|
-
This is enforced automatically by
|
|
109
|
+
This is enforced automatically by 79 rules. No configuration needed.
|
|
110
110
|
|
|
111
111
|
---
|
|
112
112
|
|
|
@@ -146,7 +146,7 @@ Your agent now understands slash commands:
|
|
|
146
146
|
| `/quality-fix` | Run and fix all quality checks |
|
|
147
147
|
| `/chat-history` | Inspect the persistent chat-history log (read-only `show`) |
|
|
148
148
|
|
|
149
|
-
→ [Browse all
|
|
149
|
+
→ [Browse all 124 active commands](../.agent-src/commands/)
|
|
150
150
|
|
|
151
151
|
---
|
|
152
152
|
|
package/docs/safety.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Data governance & domain safety
|
|
2
|
+
|
|
3
|
+
`agent-config` ships **12 domain-safety rules** (`.agent-src.uncompressed/rules/domain-safety-*.md`) that act as a per-domain output floor — PII redaction, disclaimer requirements, and retention guidance. Rules fire automatically via the router when their triggers match.
|
|
4
|
+
|
|
5
|
+
## Surface → rule(s) → floor
|
|
6
|
+
|
|
7
|
+
| Surface | Rule(s) | Floor |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| Support / CRM drafts | `domain-safety-pii-support` · `domain-safety-retention-support` | Redact customer names, emails, phones, account IDs to placeholders before output |
|
|
10
|
+
| Finance / invoicing | `domain-safety-pii-finance` · `domain-safety-retention-finance` | Redact counterparty PII and bank identifiers; flag retention under audit hold |
|
|
11
|
+
| Recruiting | `domain-safety-pii-recruiting` | Redact candidate PII from notes, scorecards, rejection emails |
|
|
12
|
+
| Marketing testimonials | `domain-safety-pii-marketing` | Require consent record before customer-identifying copy ships |
|
|
13
|
+
| Legal · financial · medical · consulting drafts | `domain-safety-disclaimer-*` | "Not legal/financial/medical advice" disclaimers; refuse diagnostic / dosage / specific tax positions |
|
|
14
|
+
| Logs · exports | `domain-safety-logging-pii-floor` · `domain-safety-export-redact` | No raw PII in logs or exports; allowlist-driven structured fields only |
|
|
15
|
+
|
|
16
|
+
## How the floor is enforced
|
|
17
|
+
|
|
18
|
+
- Each rule declares `applies_to_user_types:` in frontmatter — rules load only when the matching user-type is active (forward-compatible with the user-types axis shipping in `step-9-user-types-axis`).
|
|
19
|
+
- Each rule routes to `skill:privacy-review` as the baseline deeper-regime check (GDPR · CCPA · HIPAA).
|
|
20
|
+
- The set is opt-in by domain, never overrides higher Iron Laws (`non-destructive-by-default`, `commit-policy`, `scope-control`).
|
|
21
|
+
|
|
22
|
+
## Related skills
|
|
23
|
+
|
|
24
|
+
- [`privacy-review`](../.agent-src.uncompressed/skills/privacy-review/SKILL.md) — end-to-end data-flow review for a regulatory regime (GDPR / CCPA / HIPAA).
|
|
25
|
+
- [`data-handling-judgment`](../.agent-src.uncompressed/skills/data-handling-judgment/SKILL.md) — classification, retention, cross-border transfer, DSR workflow.
|
|
26
|
+
|
|
27
|
+
## See also
|
|
28
|
+
|
|
29
|
+
- [`non-destructive-by-default`](../.augment/rules/non-destructive-by-default.md) — Hard Floor that overrides every domain-safety carve-out.
|
|
30
|
+
- [`security-sensitive-stop`](../.augment/rules/security-sensitive-stop.md) — threat-model before touching auth / billing / tenant boundaries / uploads.
|
package/package.json
CHANGED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Bench runner for the eval corpora — step-4 measurement-and-benchmark Phase 1.
|
|
3
|
+
|
|
4
|
+
Deterministic, no-API skill-selection baseline. For each prompt in a
|
|
5
|
+
corpus YAML, ranks the 210 skills in `.agent-src.uncompressed/skills/`
|
|
6
|
+
by keyword overlap between the prompt text and each skill's
|
|
7
|
+
`description` frontmatter field. Reports selection accuracy as
|
|
8
|
+
`top-K contains >= 1 expected_skill`.
|
|
9
|
+
|
|
10
|
+
This is a baseline retrieval — not the production router. The
|
|
11
|
+
production router uses semantic embeddings; this runner pins a
|
|
12
|
+
reproducible floor so accuracy regressions in skill descriptions are
|
|
13
|
+
catchable in CI.
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
python3 scripts/bench_runner.py --corpus non-dev
|
|
17
|
+
python3 scripts/bench_runner.py --corpus non-dev --top-k 3 --json
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import argparse
|
|
23
|
+
import json
|
|
24
|
+
import re
|
|
25
|
+
import sys
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from typing import Iterable
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
import yaml
|
|
31
|
+
except ImportError:
|
|
32
|
+
sys.stderr.write("error: PyYAML required (pip install pyyaml)\n")
|
|
33
|
+
sys.exit(2)
|
|
34
|
+
|
|
35
|
+
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
36
|
+
SKILLS_DIR = REPO_ROOT / ".agent-src.uncompressed" / "skills"
|
|
37
|
+
CORPUS_DIR = REPO_ROOT / "tests" / "eval"
|
|
38
|
+
|
|
39
|
+
STOPWORDS = frozenset({
|
|
40
|
+
"a", "an", "and", "are", "as", "at", "be", "by", "for", "from",
|
|
41
|
+
"has", "have", "in", "into", "is", "it", "its", "of", "on", "or",
|
|
42
|
+
"that", "the", "this", "to", "via", "with", "your", "you", "use",
|
|
43
|
+
"when", "what", "which", "who", "how", "why", "be", "do", "i",
|
|
44
|
+
"we", "they", "them", "their", "our", "ours", "but", "not", "no",
|
|
45
|
+
"yes", "all", "any", "some", "more", "less", "than", "then",
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def tokenize(text: str) -> set[str]:
|
|
50
|
+
tokens = re.findall(r"[a-z][a-z0-9\-]+", text.lower())
|
|
51
|
+
return {t for t in tokens if t not in STOPWORDS and len(t) > 2}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def load_skill_descriptions() -> dict[str, str]:
|
|
55
|
+
"""Return {skill_name: description_text} for every skill on disk."""
|
|
56
|
+
skills: dict[str, str] = {}
|
|
57
|
+
for skill_dir in sorted(SKILLS_DIR.iterdir()):
|
|
58
|
+
skill_md = skill_dir / "SKILL.md"
|
|
59
|
+
if not skill_md.is_file():
|
|
60
|
+
continue
|
|
61
|
+
text = skill_md.read_text(encoding="utf-8")
|
|
62
|
+
m = re.search(r"^---\s*\n(.*?)\n---\s*\n", text, re.DOTALL)
|
|
63
|
+
if not m:
|
|
64
|
+
continue
|
|
65
|
+
try:
|
|
66
|
+
fm = yaml.safe_load(m.group(1)) or {}
|
|
67
|
+
except yaml.YAMLError:
|
|
68
|
+
continue
|
|
69
|
+
desc = fm.get("description", "")
|
|
70
|
+
name = fm.get("name") or skill_dir.name
|
|
71
|
+
if desc:
|
|
72
|
+
skills[name] = f"{name} {desc}"
|
|
73
|
+
return skills
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def rank_skills(prompt: str, skills: dict[str, str], top_k: int) -> list[str]:
|
|
77
|
+
"""Rank skills by keyword overlap with the prompt; return top-K names."""
|
|
78
|
+
prompt_tokens = tokenize(prompt)
|
|
79
|
+
if not prompt_tokens:
|
|
80
|
+
return []
|
|
81
|
+
scores: list[tuple[float, str]] = []
|
|
82
|
+
for name, desc in skills.items():
|
|
83
|
+
desc_tokens = tokenize(desc)
|
|
84
|
+
if not desc_tokens:
|
|
85
|
+
continue
|
|
86
|
+
overlap = prompt_tokens & desc_tokens
|
|
87
|
+
if not overlap:
|
|
88
|
+
continue
|
|
89
|
+
# Jaccard with a small IDF-shaped boost for rare matches.
|
|
90
|
+
union = prompt_tokens | desc_tokens
|
|
91
|
+
score = len(overlap) / len(union)
|
|
92
|
+
scores.append((score, name))
|
|
93
|
+
scores.sort(reverse=True)
|
|
94
|
+
return [name for _, name in scores[:top_k]]
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def run_corpus(corpus_path: Path, top_k: int) -> dict:
|
|
98
|
+
corpus = yaml.safe_load(corpus_path.read_text(encoding="utf-8"))
|
|
99
|
+
skills = load_skill_descriptions()
|
|
100
|
+
results = []
|
|
101
|
+
hits = 0
|
|
102
|
+
for p in corpus["prompts"]:
|
|
103
|
+
ranked = rank_skills(p["prompt"], skills, top_k)
|
|
104
|
+
expected = set(p.get("expected_skills", []))
|
|
105
|
+
hit = bool(expected & set(ranked))
|
|
106
|
+
if hit:
|
|
107
|
+
hits += 1
|
|
108
|
+
results.append({
|
|
109
|
+
"id": p["id"],
|
|
110
|
+
"category": p.get("category", ""),
|
|
111
|
+
"expected_skills": sorted(expected),
|
|
112
|
+
"top_k_ranked": ranked,
|
|
113
|
+
"hit": hit,
|
|
114
|
+
})
|
|
115
|
+
n = len(results)
|
|
116
|
+
accuracy = hits / n if n else 0.0
|
|
117
|
+
return {
|
|
118
|
+
"corpus_id": corpus["corpus_id"],
|
|
119
|
+
"target": corpus.get("selection_accuracy_target", 0.60),
|
|
120
|
+
"top_k": top_k,
|
|
121
|
+
"prompts_total": n,
|
|
122
|
+
"prompts_hit": hits,
|
|
123
|
+
"selection_accuracy": round(accuracy, 4),
|
|
124
|
+
"passed": accuracy >= corpus.get("selection_accuracy_target", 0.60),
|
|
125
|
+
"per_prompt": results,
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def main(argv: Iterable[str] | None = None) -> int:
|
|
130
|
+
ap = argparse.ArgumentParser()
|
|
131
|
+
ap.add_argument("--corpus", default="non-dev", help="corpus id (non-dev | dev)")
|
|
132
|
+
ap.add_argument("--top-k", type=int, default=3, help="top-K window for hit-check")
|
|
133
|
+
ap.add_argument("--json", action="store_true", help="emit JSON only")
|
|
134
|
+
args = ap.parse_args(list(argv) if argv is not None else None)
|
|
135
|
+
|
|
136
|
+
corpus_path = CORPUS_DIR / f"corpus-{args.corpus}.yaml"
|
|
137
|
+
if not corpus_path.is_file():
|
|
138
|
+
sys.stderr.write(f"error: corpus not found: {corpus_path}\n")
|
|
139
|
+
return 2
|
|
140
|
+
|
|
141
|
+
summary = run_corpus(corpus_path, args.top_k)
|
|
142
|
+
|
|
143
|
+
if args.json:
|
|
144
|
+
print(json.dumps(summary, indent=2))
|
|
145
|
+
else:
|
|
146
|
+
print(f"corpus: {summary['corpus_id']} target={summary['target']} top-k={summary['top_k']}")
|
|
147
|
+
print(f"prompts: {summary['prompts_hit']} / {summary['prompts_total']} hit")
|
|
148
|
+
print(f"selection_accuracy: {summary['selection_accuracy']:.2%}")
|
|
149
|
+
print(f"verdict: {'PASS' if summary['passed'] else 'FAIL'}")
|
|
150
|
+
for r in summary["per_prompt"]:
|
|
151
|
+
mark = "✓" if r["hit"] else "✗"
|
|
152
|
+
print(f" {mark} {r['id']:14s} expected={r['expected_skills']} got={r['top_k_ranked'][:3]}")
|
|
153
|
+
|
|
154
|
+
return 0 if summary["passed"] else 1
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
if __name__ == "__main__":
|
|
158
|
+
sys.exit(main())
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Verify every skill link in role-based docs resolves to a real file.
|
|
3
|
+
|
|
4
|
+
Part of step-12 Phase 2. Runs in `task ci` to catch link rot when a
|
|
5
|
+
skill is renamed or removed but the role docs still reference it.
|
|
6
|
+
|
|
7
|
+
Scans `docs/getting-started-by-role.md` and `docs/getting-started-laravel.md`
|
|
8
|
+
for markdown links of the form `../.agent-src/skills/<name>/SKILL.md`
|
|
9
|
+
(relative to docs/) and checks that the target file exists on disk.
|
|
10
|
+
|
|
11
|
+
Exit codes:
|
|
12
|
+
0 — every link resolves
|
|
13
|
+
1 — at least one broken link; prints the offending file:line:url tuples
|
|
14
|
+
2 — usage error (one of the role doc files missing)
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
python3 scripts/check_role_doc_links.py
|
|
18
|
+
python3 scripts/check_role_doc_links.py --quiet
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import argparse
|
|
23
|
+
import re
|
|
24
|
+
import sys
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
28
|
+
DOCS_DIR = ROOT / "docs"
|
|
29
|
+
|
|
30
|
+
# (display-path, on-disk path, link-anchor) — anchor is the relative
|
|
31
|
+
# prefix that identifies a skill link from inside docs/.
|
|
32
|
+
ROLE_DOCS = [
|
|
33
|
+
DOCS_DIR / "getting-started-by-role.md",
|
|
34
|
+
DOCS_DIR / "getting-started-laravel.md",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
# Markdown link: [label](path). We only check the (path) part. The
|
|
38
|
+
# regex tolerates trailing #anchor fragments and ignores absolute URLs.
|
|
39
|
+
LINK_RE = re.compile(r"\]\(([^)\s]+)\)")
|
|
40
|
+
|
|
41
|
+
# Anchors we know how to resolve. Each tuple is (prefix, base_dir).
|
|
42
|
+
ANCHORS: list[tuple[str, Path]] = [
|
|
43
|
+
("../.agent-src/skills/", ROOT / ".agent-src" / "skills"),
|
|
44
|
+
("../.agent-src/commands/", ROOT / ".agent-src" / "commands"),
|
|
45
|
+
("../.agent-src/rules/", ROOT / ".agent-src" / "rules"),
|
|
46
|
+
("../agents/", ROOT / "agents"),
|
|
47
|
+
("contracts/", DOCS_DIR / "contracts"),
|
|
48
|
+
("guidelines/", DOCS_DIR / "guidelines"),
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def resolve(url: str, doc_path: Path) -> Path | None:
|
|
53
|
+
"""Return the on-disk target path for a relative link, or None if external."""
|
|
54
|
+
if url.startswith(("http://", "https://", "mailto:")):
|
|
55
|
+
return None
|
|
56
|
+
bare = url.split("#", 1)[0]
|
|
57
|
+
if not bare:
|
|
58
|
+
return None
|
|
59
|
+
# Relative to the doc's own directory.
|
|
60
|
+
target = (doc_path.parent / bare).resolve()
|
|
61
|
+
return target
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def scan(doc_path: Path) -> list[tuple[int, str]]:
|
|
65
|
+
"""Return list of (line_no, url) tuples for every non-external link."""
|
|
66
|
+
if not doc_path.is_file():
|
|
67
|
+
print(f"error: missing role doc: {doc_path}", file=sys.stderr)
|
|
68
|
+
sys.exit(2)
|
|
69
|
+
links: list[tuple[int, str]] = []
|
|
70
|
+
for i, line in enumerate(doc_path.read_text(encoding="utf-8").splitlines(), 1):
|
|
71
|
+
for m in LINK_RE.finditer(line):
|
|
72
|
+
url = m.group(1)
|
|
73
|
+
if url.startswith(("http://", "https://", "mailto:")):
|
|
74
|
+
continue
|
|
75
|
+
links.append((i, url))
|
|
76
|
+
return links
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def main() -> int:
|
|
80
|
+
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
81
|
+
p.add_argument("--quiet", action="store_true", help="Suppress success summary.")
|
|
82
|
+
args = p.parse_args()
|
|
83
|
+
|
|
84
|
+
failures: list[tuple[Path, int, str]] = []
|
|
85
|
+
checked = 0
|
|
86
|
+
|
|
87
|
+
for doc in ROLE_DOCS:
|
|
88
|
+
for line_no, url in scan(doc):
|
|
89
|
+
target = resolve(url, doc)
|
|
90
|
+
if target is None:
|
|
91
|
+
continue
|
|
92
|
+
checked += 1
|
|
93
|
+
if not target.exists():
|
|
94
|
+
failures.append((doc, line_no, url))
|
|
95
|
+
|
|
96
|
+
if failures:
|
|
97
|
+
print("Broken links in role docs:", file=sys.stderr)
|
|
98
|
+
for doc, line_no, url in failures:
|
|
99
|
+
rel = doc.relative_to(ROOT)
|
|
100
|
+
print(f" {rel}:{line_no} -> {url}", file=sys.stderr)
|
|
101
|
+
print(f"\n{len(failures)} broken / {checked} checked", file=sys.stderr)
|
|
102
|
+
return 1
|
|
103
|
+
|
|
104
|
+
if not args.quiet:
|
|
105
|
+
print(f"check_role_doc_links: {checked} links OK across {len(ROLE_DOCS)} files")
|
|
106
|
+
return 0
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
if __name__ == "__main__":
|
|
110
|
+
sys.exit(main())
|
package/scripts/compress.py
CHANGED
|
@@ -77,6 +77,11 @@ def _tool_active(tool_id: str) -> bool:
|
|
|
77
77
|
# Files to copy as-is even if .md (not compressed by agent)
|
|
78
78
|
COPY_AS_IS = {"README.md"}
|
|
79
79
|
|
|
80
|
+
# Directories (relative to SOURCE_DIR) whose .md content is data, not prose,
|
|
81
|
+
# and must be copied verbatim. Ghostwriter fixtures carry voice_samples that
|
|
82
|
+
# would be destroyed by caveman compression.
|
|
83
|
+
COPY_AS_IS_DIRS = frozenset({"ghostwriter"})
|
|
84
|
+
|
|
80
85
|
|
|
81
86
|
def _read_augment_rules_use_symlinks() -> bool:
|
|
82
87
|
"""Read augment.rules_use_symlinks from .agent-settings.yml.
|
|
@@ -235,6 +240,12 @@ def should_compress(filepath: Path) -> bool:
|
|
|
235
240
|
return False
|
|
236
241
|
if filepath.name in COPY_AS_IS:
|
|
237
242
|
return False
|
|
243
|
+
try:
|
|
244
|
+
rel_parts = filepath.relative_to(SOURCE_DIR).parts
|
|
245
|
+
except ValueError:
|
|
246
|
+
rel_parts = filepath.parts
|
|
247
|
+
if rel_parts and rel_parts[0] in COPY_AS_IS_DIRS:
|
|
248
|
+
return False
|
|
238
249
|
return True
|
|
239
250
|
|
|
240
251
|
|