From 89badb8b3e73f4186c808a1c19f4a03aeda0fcf1 Mon Sep 17 00:00:00 2001 From: Dennis Winter Date: Thu, 9 Jul 2026 23:37:29 +0200 Subject: [PATCH 01/12] Reorganize AI artifacts under ai-artifacts --- .editorconfig | 19 ++ .gitignore | 21 ++- .scratch/AGENTS.md | 18 +- .../artifacts/watch/CHANGELOG.md | 2 +- .scratch/agents-md-folder-guides/PRD.md | 2 +- .scratch/check-updates-detection-scope/PRD.md | 2 +- .scratch/claude-code-skill-adaptation/PRD.md | 8 +- .../issues/01-audit-and-classify.md | 2 +- .scratch/claude-md-planning-defaults/PRD.md | 2 +- .scratch/docs-integrity-pass/PRD.md | 10 +- .scratch/fetch-latest-claude-skills/PRD.md | 2 +- .scratch/import-upstream-skills/PRD.md | 12 +- .../issues/03-import-coding-skills.md | 8 +- .../issues/05-import-setup-hook-skills.md | 4 +- .../incorporate-global-claude-setup/PRD.md | 8 +- .scratch/mail-to-doc/PRD.md | 4 +- .../issues/01-incorporate-latest-changes.md | 2 +- .../issues/02-analyse-effectiveness.md | 2 +- .scratch/profile-improvement/PRD.md | 8 +- .scratch/public-repo-compliance/PRD.md | 18 +- .scratch/repo-scaffold/PRD.md | 16 +- .../HANDOFF-ai-lab-repo-structure.md | 33 ++-- .scratch/repo-scope-strays/PRD.md | 8 +- .scratch/sync-skills-drift-check/PRD.md | 2 +- .scratch/understand-scratch-skill/PRD.md | 10 +- AGENTS.md | 24 +-- CONTEXT.md | 165 ++++++++++++++++++ THIRD-PARTY/README.md | 14 -- .../bradautomates-claude-video.LICENSE | 21 --- THIRD-PARTY/mattpocock-skills.LICENSE | 21 --- .../adoc-preview-menu-1.0.0/extension.js | 0 .../adoc-preview-menu-1.0.0/package.json | 0 ai-artifacts/agents/README.md | 5 + ai-artifacts/hooks/README.md | 6 + ai-artifacts/instructions/README.md | 26 +++ .../anthropic/claude-ai}/profile.md | 0 ai-artifacts/mcp-config/README.md | 5 + ai-artifacts/output-styles/README.md | 5 + ai-artifacts/plugins/README.md | 5 + ai-artifacts/prompts/README.md | 5 + .../skills/shared}/README.md | 6 +- .../shared}/coding/diagnose/METADATA.md | 0 .../skills/shared}/coding/diagnose/SKILL.md | 0 .../diagnose/scripts/hitl-loop.template.ps1 | 0 .../diagnose/scripts/hitl-loop.template.sh | 0 .../DEEPENING.md | 0 .../HTML-REPORT.md | 0 .../INTERFACE-DESIGN.md | 0 .../improve-codebase-architecture/LANGUAGE.md | 0 .../improve-codebase-architecture/METADATA.md | 0 .../improve-codebase-architecture/SKILL.md | 0 .../shared}/coding/prototype/METADATA.md | 0 .../skills/shared}/coding/prototype/SKILL.md | 0 .../skills/shared}/coding/tdd/METADATA.md | 0 .../skills/shared}/coding/tdd/SKILL.md | 0 .../skills/shared}/coding/tdd/behaviors.md | 0 .../skills/shared}/coding/tdd/companions.md | 0 .../coding/tdd/design-for-testability.md | 0 .../shared}/coding/tdd/reviewing-and-cycle.md | 0 .../shared}/coding/tdd/stacks/csharp.md | 0 .../shared}/coding/tdd/stacks/powershell.md | 0 .../shared}/coding/tdd/stacks/python.md | 0 .../skills/shared}/coding/tdd/stacks/sql.md | 0 .../skills/shared}/coding/tdd/test-doubles.md | 0 .../shared}/coding/zoom-out/METADATA.md | 0 .../skills/shared}/coding/zoom-out/SKILL.md | 0 .../documents/mail-to-adoc/METADATA.md | 0 .../shared}/documents/mail-to-adoc/README.md | 0 .../shared}/documents/mail-to-adoc/SKILL.md | 0 .../mail-to-adoc/attachment-blocklist.txt | Bin .../documents/mail-to-adoc/docs/prd/PRD.md | 0 .../mail-to-adoc/docs/prd/PRD_EDGE_CASES.md | 0 .../scripts/Invoke-MailToAdoc.ps1 | 0 .../mail-to-adoc/scripts/Move-Belege.ps1 | 0 .../mail-to-adoc/scripts/mail_to_adoc.py | 0 .../documents/mail-to-adoc/tests/README.md | 0 .../documents/mail-to-adoc/tests/conftest.py | 0 .../mail-to-adoc/tests/test_mail_to_adoc.py | 0 .../shared}/planning/scratch-plan/METADATA.md | 0 .../shared}/planning/scratch-plan/SKILL.md | 0 .../skills/shared}/planning/scratch/LAYOUT.md | 0 .../shared}/planning/scratch/METADATA.md | 0 .../shared}/planning/scratch/RANKING.md | 0 .../skills/shared}/planning/scratch/SKILL.md | 0 .../shared}/session/caveman/METADATA.md | 0 .../skills/shared}/session/caveman/SKILL.md | 0 .../shared}/session/grill-me/ADR-FORMAT.md | 0 .../session/grill-me/CONTEXT-FORMAT.md | 0 .../shared}/session/grill-me/METADATA.md | 0 .../skills/shared}/session/grill-me/SKILL.md | 0 .../shared}/session/handoff/METADATA.md | 0 .../skills/shared}/session/handoff/SKILL.md | 0 .../skills/shared}/session/recon/METADATA.md | 0 .../skills/shared}/session/recon/RECIPES.md | 0 .../skills/shared}/session/recon/SKILL.md | 0 .../shared}/session/write-a-skill/EXAMPLES.md | 0 .../shared}/session/write-a-skill/METADATA.md | 0 .../shared}/session/write-a-skill/SCRIPTS.md | 0 .../shared}/session/write-a-skill/SKILL.md | 0 .../setup/check-skill-updates/METADATA.md | 0 .../setup/check-skill-updates/SKILL.md | 10 +- .../shared}/setup/git-guardrails/METADATA.md | 0 .../shared}/setup/git-guardrails/SKILL.md | 0 .../scripts/block-dangerous-git.ps1 | 0 .../scripts/block-dangerous-git.sh | 0 .../setup/import-upstream-skill/METADATA.md | 0 .../setup/import-upstream-skill/SKILL.md | 8 +- .../setup/setup-pre-commit/METADATA.md | 0 .../shared}/setup/setup-pre-commit/SKILL.md | 0 docs/harnesses/claude-code.md | 4 +- docs/harnesses/codex.md | 32 ++-- docs/harnesses/copilot-cli.md | 6 +- docs/okf-adoption.md | 2 +- docs/repo-layout.adoc | 88 +++++++--- docs/reviews/2026-07-04-fable-repo-review.md | 12 +- instructions/README.md | 22 --- scripts/sync-skills.ps1 | 14 +- 117 files changed, 473 insertions(+), 256 deletions(-) create mode 100644 .editorconfig create mode 100644 CONTEXT.md delete mode 100644 THIRD-PARTY/README.md delete mode 100644 THIRD-PARTY/bradautomates-claude-video.LICENSE delete mode 100644 THIRD-PARTY/mattpocock-skills.LICENSE rename {VSCode_Extsion => VSCode_Extension}/adoc-preview-menu-1.0.0/extension.js (100%) rename {VSCode_Extsion => VSCode_Extension}/adoc-preview-menu-1.0.0/package.json (100%) create mode 100644 ai-artifacts/agents/README.md create mode 100644 ai-artifacts/hooks/README.md create mode 100644 ai-artifacts/instructions/README.md rename {anthropic/claude-ai/instructions => ai-artifacts/instructions/anthropic/claude-ai}/profile.md (100%) create mode 100644 ai-artifacts/mcp-config/README.md create mode 100644 ai-artifacts/output-styles/README.md create mode 100644 ai-artifacts/plugins/README.md create mode 100644 ai-artifacts/prompts/README.md rename {shared/skills => ai-artifacts/skills/shared}/README.md (96%) rename {shared/skills => ai-artifacts/skills/shared}/coding/diagnose/METADATA.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/diagnose/SKILL.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/diagnose/scripts/hitl-loop.template.ps1 (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/diagnose/scripts/hitl-loop.template.sh (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/improve-codebase-architecture/DEEPENING.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/improve-codebase-architecture/HTML-REPORT.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/improve-codebase-architecture/INTERFACE-DESIGN.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/improve-codebase-architecture/LANGUAGE.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/improve-codebase-architecture/METADATA.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/improve-codebase-architecture/SKILL.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/prototype/METADATA.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/prototype/SKILL.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/tdd/METADATA.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/tdd/SKILL.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/tdd/behaviors.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/tdd/companions.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/tdd/design-for-testability.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/tdd/reviewing-and-cycle.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/tdd/stacks/csharp.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/tdd/stacks/powershell.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/tdd/stacks/python.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/tdd/stacks/sql.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/tdd/test-doubles.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/zoom-out/METADATA.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/coding/zoom-out/SKILL.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/documents/mail-to-adoc/METADATA.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/documents/mail-to-adoc/README.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/documents/mail-to-adoc/SKILL.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/documents/mail-to-adoc/attachment-blocklist.txt (100%) rename {shared/skills => ai-artifacts/skills/shared}/documents/mail-to-adoc/docs/prd/PRD.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/documents/mail-to-adoc/docs/prd/PRD_EDGE_CASES.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/documents/mail-to-adoc/scripts/Invoke-MailToAdoc.ps1 (100%) rename {shared/skills => ai-artifacts/skills/shared}/documents/mail-to-adoc/scripts/Move-Belege.ps1 (100%) rename {shared/skills => ai-artifacts/skills/shared}/documents/mail-to-adoc/scripts/mail_to_adoc.py (100%) rename {shared/skills => ai-artifacts/skills/shared}/documents/mail-to-adoc/tests/README.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/documents/mail-to-adoc/tests/conftest.py (100%) rename {shared/skills => ai-artifacts/skills/shared}/documents/mail-to-adoc/tests/test_mail_to_adoc.py (100%) rename {shared/skills => ai-artifacts/skills/shared}/planning/scratch-plan/METADATA.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/planning/scratch-plan/SKILL.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/planning/scratch/LAYOUT.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/planning/scratch/METADATA.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/planning/scratch/RANKING.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/planning/scratch/SKILL.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/session/caveman/METADATA.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/session/caveman/SKILL.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/session/grill-me/ADR-FORMAT.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/session/grill-me/CONTEXT-FORMAT.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/session/grill-me/METADATA.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/session/grill-me/SKILL.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/session/handoff/METADATA.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/session/handoff/SKILL.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/session/recon/METADATA.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/session/recon/RECIPES.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/session/recon/SKILL.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/session/write-a-skill/EXAMPLES.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/session/write-a-skill/METADATA.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/session/write-a-skill/SCRIPTS.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/session/write-a-skill/SKILL.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/setup/check-skill-updates/METADATA.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/setup/check-skill-updates/SKILL.md (92%) rename {shared/skills => ai-artifacts/skills/shared}/setup/git-guardrails/METADATA.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/setup/git-guardrails/SKILL.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/setup/git-guardrails/scripts/block-dangerous-git.ps1 (100%) rename {shared/skills => ai-artifacts/skills/shared}/setup/git-guardrails/scripts/block-dangerous-git.sh (100%) rename {shared/skills => ai-artifacts/skills/shared}/setup/import-upstream-skill/METADATA.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/setup/import-upstream-skill/SKILL.md (92%) rename {shared/skills => ai-artifacts/skills/shared}/setup/setup-pre-commit/METADATA.md (100%) rename {shared/skills => ai-artifacts/skills/shared}/setup/setup-pre-commit/SKILL.md (100%) delete mode 100644 instructions/README.md diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..58b7613 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false +max_line_length = off + +[*.{yml,yaml,json}] +indent_size = 2 diff --git a/.gitignore b/.gitignore index d07bc4f..ebaf5c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,20 @@ +# OS-generated files +.DS_Store +.DS_Store? +Thumbs.db +Desktop.ini +$RECYCLE.BIN/ +.Spotlight-V100/ +.Trashes/ +._* + +# Editor / IDE metadata +/.idea/ +*.iml +*.swp +*.swo +*~ + # VS Code Local History extension /.history/ @@ -9,11 +26,11 @@ /.temp/* !/.temp/.gitkeep -# Generated Claude Code skill mirror. Source of truth is shared/skills/. +# Generated Claude Code skill mirror. Source of truth is ai-artifacts/skills/shared/. # Rebuild after clone or after editing a skill: pwsh scripts/sync-skills.ps1 /.claude/commands/* -# Generated Codex skill mirror. Source of truth is shared/skills/. +# Generated Codex skill mirror. Source of truth is ai-artifacts/skills/shared/. # Rebuild after clone or after editing a skill: pwsh scripts/sync-skills.ps1 /.agents/skills/* diff --git a/.scratch/AGENTS.md b/.scratch/AGENTS.md index 8633784..bc3ad08 100644 --- a/.scratch/AGENTS.md +++ b/.scratch/AGENTS.md @@ -10,8 +10,8 @@ A **vehicle for capturing and tracking ideas** — one folder per feature `BACKLOG.md`. Capture with `/planning:scratch`; rank with `/planning:scratch-plan`. - Structural layout + file templates → the `scratch` skill's - [LAYOUT.md](../shared/skills/planning/scratch/LAYOUT.md) -- Ranking formula → [RANKING.md](../shared/skills/planning/scratch/RANKING.md) + [LAYOUT.md](../ai-artifacts/skills/shared/planning/scratch/LAYOUT.md) +- Ranking formula → [RANKING.md](../ai-artifacts/skills/shared/planning/scratch/RANKING.md) ## Working rules @@ -24,18 +24,18 @@ produces — a skill or script, a report, a durable finding, any shippable outpu and belongs in its proper repo home, with the PRD linking to it. `artifacts/` holds only *supporting inputs* (upstream snapshots, prior drafts, sample inputs) — **never the deliverable**. -| Deliverable | Home (not the scratch) | -| --- | --- | -| Skill / tool | `shared/skills///` (or a `//` skills dir) | -| Report / durable finding / review | `docs/` — `docs/findings/`, `docs/reviews/`, … | -| Instructions / settings / hooks | their harness-scoped home (`//…`) | +Deliverable homes: + +- Skill / tool: `ai-artifacts/skills/shared///` (or `ai-artifacts/skills///…`) +- Report / durable finding / review: `docs/` — `docs/findings/`, `docs/reviews/`, … +- Instructions / hooks: their artifact-type home (`ai-artifacts/instructions///…`, `ai-artifacts/hooks///…`, `ai-artifacts/mcp-config///…`, `ai-artifacts/output-styles///…`, `ai-artifacts/agents///…`, `ai-artifacts/prompts///…`, or `ai-artifacts/plugins///…`) **Don't hand-maintain rankings.** `BACKLOG.md` order and scores are produced by `/planning:scratch-plan`; flipping a single status cell is fine, re-ranking is the skill's job. --- -_This is the first per-folder `AGENTS.md` in the repo. Whether to add folder guides across other +*This is the first per-folder `AGENTS.md` in the repo. Whether to add folder guides across other significant subtrees is gated on the hypothesis test in [`agents-md-folder-guides`](agents-md-folder-guides/PRD.md) — this file is a deliberate single -instance (it houses a decided convention), not a rollout._ +instance (it houses a decided convention), not a rollout.* diff --git a/.scratch/add-watch-skill/artifacts/watch/CHANGELOG.md b/.scratch/add-watch-skill/artifacts/watch/CHANGELOG.md index 6ad03c4..7ae0020 100644 --- a/.scratch/add-watch-skill/artifacts/watch/CHANGELOG.md +++ b/.scratch/add-watch-skill/artifacts/watch/CHANGELOG.md @@ -24,7 +24,7 @@ All notable changes to `/watch` are documented here. ### Fixed - Added `commands/watch.md` shim so `/watch` is callable when installed as a Claude Code plugin. Without it, the plugin loaded but the skill wasn't exposed as a slash command. -- `scripts/build-skill.sh` now strips `commands/` from the claude.ai `.skill` bundle alongside `hooks/` and `.claude-plugin/`. +- `scripts/build-skill.sh` now strips `commands/` from the claude.ai `.skill` bundle alongside `ai-artifacts/hooks/` and `.claude-plugin/`. ## [0.1.0] — 2026-04-24 diff --git a/.scratch/agents-md-folder-guides/PRD.md b/.scratch/agents-md-folder-guides/PRD.md index 83e30cf..789e1c5 100644 --- a/.scratch/agents-md-folder-guides/PRD.md +++ b/.scratch/agents-md-folder-guides/PRD.md @@ -26,7 +26,7 @@ in a future run."** Don't assume it's true — test it. ## Notes / open questions (for triage, not to action now) -- What counts as "significant"? Threshold by depth, file count, or role (e.g. `skills/`, `shared/`, +- What counts as "significant"? Threshold by depth, file count, or role (e.g. `ai-artifacts/skills/`, `shared/`, `.claude/`, `src/`, `docs/`) — not every leaf folder. - `AGENTS.md` as the chosen filename (cross-tool convention) vs. reusing the repo's existing README/CLAUDE.md pattern — pick one and be consistent. diff --git a/.scratch/check-updates-detection-scope/PRD.md b/.scratch/check-updates-detection-scope/PRD.md index 903f036..608581b 100644 --- a/.scratch/check-updates-detection-scope/PRD.md +++ b/.scratch/check-updates-detection-scope/PRD.md @@ -25,7 +25,7 @@ principle **any** change inside the upstream skill directory — scripts include correctly when the dir was renamed/moved (PATH-NOT-FOUND path), and does it false-negative on changes to files the skill depends on but doesn't physically contain? - Hooks specifically: are hooks ever part of a skill's tracked directory in this repo's model, or do - they live under a separate `hooks/`/settings path that detection never looks at? + they live under a separate `ai-artifacts/hooks/`/settings path that detection never looks at? ## Acceptance (what "checked in detail" means) diff --git a/.scratch/claude-code-skill-adaptation/PRD.md b/.scratch/claude-code-skill-adaptation/PRD.md index 07f2e1e..be250d8 100644 --- a/.scratch/claude-code-skill-adaptation/PRD.md +++ b/.scratch/claude-code-skill-adaptation/PRD.md @@ -2,7 +2,7 @@ Status: ready-for-human -The skills under `skills/` were imported from their claude.ai (chat) versions, organized by intent, +The skills under `ai-artifacts/skills/` were imported from their claude.ai (chat) versions, organized by intent, and given upstream provenance. They are **invocable** but still **claude.ai-flavored**. This feature covers the second pass: making each one behave as a native Claude Code skill. @@ -59,7 +59,7 @@ skill's intent or its local customizations — only its mechanics. Windows/pwsh substitutions all stay. This pass changes mechanics, not content decisions. - **`check-skill-updates` rework:** extract the inline PowerShell into `scripts/check-skill-updates.ps1` (staleness check + three-way-merge helper), and have the SKILL.md drive it. Keep the path-detection - already pointed at this repo's `skills/` layout. + already pointed at this repo's `ai-artifacts/skills/` layout. ## Testing Decisions @@ -76,13 +76,13 @@ skill's intent or its local customizations — only its mechanics. - The **setup/init skill design** decision (monolithic vs self-configuring vs config-in-AGENTS.md vs minimal tracker-only) — handoff decision #5. Resolve with a `grill-me` pass in its own session; it shapes any future agentic skills but isn't required to adapt the existing eight. -- Adopting **new** authors' skills, or new skills not already in `skills/`. +- Adopting **new** authors' skills, or new skills not already in `ai-artifacts/skills/`. - The wider `ai-lab` repo scaffold (AGENTS.md, compatibility matrix, instructions/, mcp/, etc.) — tracked by the repo-structure handoff, not this feature. ## Scope note — which skills this covers -The eight skills imported in pass 1 live under `skills/`. Of these, the **engineering-origin** ones — +The eight skills imported in pass 1 live under `ai-artifacts/skills/`. Of these, the **engineering-origin** ones — `tdd`, `prototype` (mattpocock `engineering/`), and `grill-me` (the `productivity/grill-me` + `engineering/grill-with-docs` merge) — are adapted **here** (issues 01, 03, 05). The *additional* mattpocock engineering/misc skills not yet imported are a separate feature: diff --git a/.scratch/claude-code-skill-adaptation/issues/01-audit-and-classify.md b/.scratch/claude-code-skill-adaptation/issues/01-audit-and-classify.md index 8cd4a5c..021d69b 100644 --- a/.scratch/claude-code-skill-adaptation/issues/01-audit-and-classify.md +++ b/.scratch/claude-code-skill-adaptation/issues/01-audit-and-classify.md @@ -11,7 +11,7 @@ from the PRD against the actual bodies. ## Acceptance criteria -- [ ] Every skill under `skills/` listed with its classification (conversational / agentic) +- [ ] Every skill under `ai-artifacts/skills/` listed with its classification (conversational / agentic) - [ ] Per skill: concrete list of mechanics to change (or "none") - [ ] Any additional carried-over content bugs noted (beyond the two already known) diff --git a/.scratch/claude-md-planning-defaults/PRD.md b/.scratch/claude-md-planning-defaults/PRD.md index b0d069f..50a40c2 100644 --- a/.scratch/claude-md-planning-defaults/PRD.md +++ b/.scratch/claude-md-planning-defaults/PRD.md @@ -4,7 +4,7 @@ Status: needs-triage Quick capture — iron out in scratch-planning, don't action yet. These are **general, cross-project** rules (not ai-lab specific). Home: the global Claude Code instructions (`~/.claude/CLAUDE.md`; repo -copy will live at `anthropic/claude-code/instructions/CLAUDE.md` per `incorporate-global-claude-setup`). +copy will live at `instructions/anthropic/claude-code/CLAUDE.md` per `incorporate-global-claude-setup`). Rough rules to encode (not final): diff --git a/.scratch/docs-integrity-pass/PRD.md b/.scratch/docs-integrity-pass/PRD.md index 8af7183..31eb57a 100644 --- a/.scratch/docs-integrity-pass/PRD.md +++ b/.scratch/docs-integrity-pass/PRD.md @@ -8,15 +8,15 @@ Origin: fable (Claude Fable 5 repo review, 2026-07-04) Facts are stated in more than one place and have started to drift. Concrete instances found in the 2026-07-04 review: -1. [shared/skills/README.md](../../shared/skills/README.md) — group table lists only +1. [ai-artifacts/skills/shared/README.md](../../ai-artifacts/skills/shared/README.md) — group table lists only `coding`/`session`/`setup` (missing `planning`), and body text still says `skills//…` - paths (actual: `shared/skills/`). + paths (actual: `ai-artifacts/skills/shared/`). 2. [testing-methodologies-foundation/PRD.md](../testing-methodologies-foundation/PRD.md) — still says "**Blocked:** this repo is not yet a git repository"; `git init` was done 2026-06-04 per [repo-scaffold](../repo-scaffold/PRD.md). The blocker is stale and the issue it points at is unblocked. 3. "Not the same file (common confusion)" exists near-verbatim in both - [instructions/README.md](../../instructions/README.md) and + [ai-artifacts/instructions/README.md](../../ai-artifacts/instructions/README.md) and [docs/repo-layout.adoc](../../docs/repo-layout.adoc) — two owners for one fact. 4. [docs/harnesses/claude-code.md](../../docs/harnesses/claude-code.md) lacks the per-section confidence table and validation smoke tests that [copilot.md](../../docs/harnesses/copilot.md) @@ -38,7 +38,7 @@ _Proposed — refine in triage:_ ## Progress (2026-07-04 — all items closed) -1. ✅ skills README: paths corrected to `shared/skills/…`, `planning` group added to the group +1. ✅ skills README: paths corrected to `ai-artifacts/skills/shared/…`, `planning` group added to the group table, `scratch`/`scratch-plan` added to the origin map as local originals. 2. ✅ Stale git-init blocker removed from the testing PRD **and** its issue 01 (in-place dated fix — consistent with the repo's existing `[RESOLVED …]` house style; the immutability question was @@ -47,7 +47,7 @@ _Proposed — refine in triage:_ 3. ✅ "Not the same file" now owned solely by repo-layout.adoc; instructions/README links to it. The dedupe surfaced and resolved a real contradiction: instructions/README said the future CLAUDE.md repo copy goes to `instructions/claude-code/`, canon is - `anthropic/claude-code/instructions/` — one more instance of why single-owner matters. + `instructions/anthropic/claude-code/` — one more instance of why single-owner matters. (Same stale path still appears as a *proposal* inside `incorporate-global-claude-setup`'s PRD table — left for that scratch to fix when it builds.) 4. ✅ claude-code.md gained §11 evidence metadata (per-section confidence, verified 2026-07-04 from diff --git a/.scratch/fetch-latest-claude-skills/PRD.md b/.scratch/fetch-latest-claude-skills/PRD.md index 1492282..0608ea1 100644 --- a/.scratch/fetch-latest-claude-skills/PRD.md +++ b/.scratch/fetch-latest-claude-skills/PRD.md @@ -19,7 +19,7 @@ process, determine how to diff against local versions, and decide the integratio **Reuse the generic import skill for the integration step.** The "place it in the repo as a first-class, grouped, provenance-tracked skill and adapt it" half is already solved by -[`/setup:import-upstream-skill`](../../shared/skills/setup/import-upstream-skill/SKILL.md) (delivered +[`/setup:import-upstream-skill`](../../ai-artifacts/skills/shared/setup/import-upstream-skill/SKILL.md) (delivered by `import-upstream-skills`). This scratch only needs to solve the **claude.ai-specific source half** — how to *discover and fetch* the latest versions — then hand each fetched skill to `/setup:import-upstream-skill` for placement, `upstream-*` provenance, and the capability-contract diff --git a/.scratch/import-upstream-skills/PRD.md b/.scratch/import-upstream-skills/PRD.md index 55e0b50..da190ce 100644 --- a/.scratch/import-upstream-skills/PRD.md +++ b/.scratch/import-upstream-skills/PRD.md @@ -9,7 +9,7 @@ must be source-agnostic. > **Scope decision (2026-07-05):** this feature delivers **the skill that enables importing** — not > the actual mattpocock import run. The generic process now lives as a first-class skill, -> `/setup:import-upstream-skill` (`shared/skills/setup/import-upstream-skill/SKILL.md`). Running the +> `/setup:import-upstream-skill` (`ai-artifacts/skills/shared/setup/import-upstream-skill/SKILL.md`). Running the > concrete 9-skill mattpocock import stays as follow-up work — issues 03 (coding), 04 (planning), 05 > (setup) — each executed by invoking the new skill. @@ -19,7 +19,7 @@ must be source-agnostic. 8-step process (snapshot → intent group → placement → `METADATA.md` provenance → capability-contract adaptation → verify via `write-a-skill` → origin-map update → sync). Source-agnostic; handles GitHub upstreams, local/global copies, and local forks (no dangling `upstream-commit`). -- ✅ **Origin map updated:** `shared/skills/README.md` lists `import-upstream-skill` as a local original. +- ✅ **Origin map updated:** `ai-artifacts/skills/shared/README.md` lists `import-upstream-skill` as a local original. - ⏳ **Actual imports remain:** issues 03/04/05 (import the coding / planning / setup clusters) are the first consumers of the new skill; the config-placement decision (issue 02) is only needed when the planning cluster is actually imported, so it stays deferred until then. @@ -33,7 +33,7 @@ bash flavored, not adapted to Claude Code on Windows/pwsh), and one of them — ## Solution -Import each missing skill into `shared/skills///` under my intent-based grouping, with +Import each missing skill into `ai-artifacts/skills/shared///` under my intent-based grouping, with `METADATA.md` provenance pointing at Matt's repo/path/checkpoint, then run the Claude Code capability-contract adaptation pass on them. Resolve two design decisions first (grouping + the config-distribution question) because they shape where things land. @@ -177,13 +177,11 @@ comment, omit `upstream-commit` so the staleness check skips it (it's no longer ## Attribution These skills derive from [mattpocock/skills](https://github.com/mattpocock/skills). Per-skill -provenance is in `METADATA.md`; consider adding upstream's `LICENSE` to this repo -(e.g. `THIRD-PARTY/mattpocock-skills.LICENSE`) before publishing, since the repo redistributes adapted -copies of his work. +provenance is in `METADATA.md`. ## Further Notes - Upstream snapshot: `.temp/github.com_mattpocock/skills`; exact checkpoints belong in each imported skill's `METADATA.md`. -- Provenance convention + origin map: `shared/skills/README.md`. Update flow: the `/setup:check-skill-updates` skill. +- Provenance convention + origin map: `ai-artifacts/skills/shared/README.md`. Update flow: the `/setup:check-skill-updates` skill. - Claude Code adaptation principles & pass-1 backlog: `.scratch/claude-code-skill-adaptation/`. diff --git a/.scratch/import-upstream-skills/issues/03-import-coding-skills.md b/.scratch/import-upstream-skills/issues/03-import-coding-skills.md index 6b642ae..26d261b 100644 --- a/.scratch/import-upstream-skills/issues/03-import-coding-skills.md +++ b/.scratch/import-upstream-skills/issues/03-import-coding-skills.md @@ -4,7 +4,7 @@ Status: done (all three coding skills imported + dual-mode adapted 2026-07-05) ## What to build -Import these three from Matt's repo into `shared/skills/coding/` with per-skill `METADATA.md` +Import these three from Matt's repo into `ai-artifacts/skills/shared/coding/` with per-skill `METADATA.md` provenance, copying bundled resources verbatim, then `scripts/sync-skills.ps1`: - `diagnose` (+ `scripts/hitl-loop.template.sh` — keep the file; pwsh equivalent is an adaptation task) @@ -26,13 +26,13 @@ faithful import + grouping + provenance only. Per the corrected scope (actually import + adapt the skills, dual-mode), import and adaptation are done together here rather than deferring adaptation to issue 06: -- ✅ **`diagnose`** → `shared/skills/coding/diagnose/` with `upstream-*` provenance. Added a **Modes +- ✅ **`diagnose`** → `ai-artifacts/skills/shared/coding/diagnose/` with `upstream-*` provenance. Added a **Modes (capability contract)** section — shell/FS present ⇒ build & run the loop; no shell (claude.ai) ⇒ design the loop and drive the user through it, working off the signal they paste. HITL helper now ships **pwsh** (`scripts/hitl-loop.template.ps1`, primary) **+ bash** (`.sh`, cross-platform). -- ✅ **`zoom-out`** → `shared/skills/coding/zoom-out/` with provenance; `disable-model-invocation` +- ✅ **`zoom-out`** → `ai-artifacts/skills/shared/coding/zoom-out/` with provenance; `disable-model-invocation` preserved; added a one-line "if a filesystem is available… else work from pasted context" note. -- ✅ **`improve-codebase-architecture`** → `shared/skills/coding/improve-codebase-architecture/` with +- ✅ **`improve-codebase-architecture`** → `ai-artifacts/skills/shared/coding/improve-codebase-architecture/` with provenance + all four resources (DEEPENING / INTERFACE-DESIGN / LANGUAGE / HTML-REPORT). Added a **Modes** section (Explore subagent + temp-file report vs. reason over pasted code + downloadable report; CONTEXT/ADR edits inline vs. proposed-as-text). Repointed the two `../grill-with-docs/` diff --git a/.scratch/import-upstream-skills/issues/05-import-setup-hook-skills.md b/.scratch/import-upstream-skills/issues/05-import-setup-hook-skills.md index eb36cee..a4bcd82 100644 --- a/.scratch/import-upstream-skills/issues/05-import-setup-hook-skills.md +++ b/.scratch/import-upstream-skills/issues/05-import-setup-hook-skills.md @@ -22,7 +22,7 @@ Status: done (git-guardrails + setup-pre-commit imported 2026-07-05; setup clust ## Progress (2026-07-05) -- ✅ **git-guardrails** → `shared/skills/setup/git-guardrails/`. Imported from the **global-prior** +- ✅ **git-guardrails** → `ai-artifacts/skills/shared/setup/git-guardrails/`. Imported from the **global-prior** (already localized: pwsh + bash, Windows-primary hook wiring) rather than the raw bash-only artifact. Wrote the **pwsh guard** `scripts/block-dangerous-git.ps1` (uses `ConvertFrom-Json`, no `jq` dependency; same regex pattern list as the bash guard) alongside the bash one. Added an @@ -30,7 +30,7 @@ Status: done (git-guardrails + setup-pre-commit imported 2026-07-05; setup clust Provenance tracks `misc/git-guardrails-claude-code` in the skill's `METADATA.md`; README no longer duplicates exact upstream checkpoints. Bash guard smoke-tested: `git push` → exit 2, `git status` → exit 0; pwsh mirrors the same patterns. -- ✅ **setup-pre-commit** → `shared/skills/setup/setup-pre-commit/`. **Decision 3 resolved:** imported +- ✅ **setup-pre-commit** → `ai-artifacts/skills/shared/setup/setup-pre-commit/`. **Decision 3 resolved:** imported the global-prior (pre-commit framework for PS/MD/AsciiDoc/SQL) as a **local fork** — SoT = mine, lineage recorded in an HTML comment, **no `upstream-*` frontmatter** so `/setup:check-skill-updates` skips it (it shares only a name with Matt's Husky/lint-staged version). Added an Applicability note diff --git a/.scratch/incorporate-global-claude-setup/PRD.md b/.scratch/incorporate-global-claude-setup/PRD.md index 0eb5c31..73d9257 100644 --- a/.scratch/incorporate-global-claude-setup/PRD.md +++ b/.scratch/incorporate-global-claude-setup/PRD.md @@ -2,7 +2,7 @@ Status: ready-for-human -The claude.ai profile is now repo-managed (`instructions/claude-ai/profile.md`). The **Claude Code** +The claude.ai profile is now repo-managed (`ai-artifacts/instructions/anthropic/claude-ai/profile.md`). The **Claude Code** global setup (`~/.claude/`) is the other half and is not yet in the repo. Bring the relevant parts in as harness-scoped source-of-truth copies, the same way skills are managed. @@ -28,8 +28,8 @@ instead of being maintained twice. | Source (`~/.claude/`) | Proposed repo location | Notes | | --------------------------------------- | --------------------------------------------------- | -------------------------------------------------------------------- | -| `CLAUDE.md` (global instructions) | `instructions/claude-code/CLAUDE.md` | Harness-scoped; overlaps with the profile | -| `settings.json` (permissions/hooks/MCP) | `settings/claude-code/settings.json` *(or similar)* | Decide: global settings vs project settings; secrets stay out | +| `CLAUDE.md` (global instructions) | `ai-artifacts/instructions/claude-code/CLAUDE.md` | Harness-scoped; overlaps with the profile | +| `settings.json` (permissions/hooks/MCP) | `ai-artifacts/mcp-config/claude-code/settings.json` *(or similar)* | Decide: global settings vs project settings; secrets stay out | | Global skills (`commands/`) | already handled by the skills work | Prior-version globals captured in `import-upstream-skills` artifacts | ## Pending content to fold in when hoisting `CLAUDE.md` @@ -62,7 +62,7 @@ waiting to be merged into it when it lands: ## Out of Scope - Credentials, chat history, sessions — never copied or versioned. -- The claude.ai profile itself (already in `instructions/claude-ai/`). +- The claude.ai profile itself (already in `ai-artifacts/instructions/anthropic/claude-ai/`). - The full instructions/settings taxonomy (vendor tag, other harnesses) — repo-scaffold handoff. ## Further Notes diff --git a/.scratch/mail-to-doc/PRD.md b/.scratch/mail-to-doc/PRD.md index 3fd439a..e3a38a1 100644 --- a/.scratch/mail-to-doc/PRD.md +++ b/.scratch/mail-to-doc/PRD.md @@ -5,7 +5,7 @@ Status: in-progress (issue 01 markers landed 2026-07-05; issues 02/03 pending) ## Problem Statement The existing `mail-to-adoc` tool — now a first-class skill at -[`shared/skills/documents/mail-to-adoc/`](../../shared/skills/documents/mail-to-adoc/) — converts +[`ai-artifacts/skills/shared/documents/mail-to-adoc/`](../../ai-artifacts/skills/shared/documents/mail-to-adoc/) — converts `.eml` files to AsciiDoc but has several issues blocking a 1.0.0 release: 1. The HTML→AsciiDoc conversion logic is tangled with the main orchestration code, making it hard @@ -67,7 +67,7 @@ in the row-close logic of the HTML table→AsciiDoc table converter. - **The deliverable lives outside the scratch (2026-07-05).** A scratch holds the *idea*; a working skill is a *deliverable* and belongs in the repo's skills tree. The tool was promoted from `artifacts/mail-to-adoc/` to the first-class skill - [`shared/skills/documents/mail-to-adoc/`](../../shared/skills/documents/mail-to-adoc/) (new + [`ai-artifacts/skills/shared/documents/mail-to-adoc/`](../../ai-artifacts/skills/shared/documents/mail-to-adoc/) (new `documents` group — no existing group fits document conversion). This scratch keeps only the idea + tracking; it no longer carries the code. Note: the skill's `_PROJECT_ROOT` assumes a `/skills//` layout, so the extra `documents/` level shifts it in-repo — irrelevant here diff --git a/.scratch/mail-to-doc/issues/01-incorporate-latest-changes.md b/.scratch/mail-to-doc/issues/01-incorporate-latest-changes.md index 5fc36aa..4b42cb3 100644 --- a/.scratch/mail-to-doc/issues/01-incorporate-latest-changes.md +++ b/.scratch/mail-to-doc/issues/01-incorporate-latest-changes.md @@ -9,7 +9,7 @@ requirements from the user. 1. **Diff the two drops** — `.temp/mail-to-adoc-2026-06-28.zip` vs `.temp/mail-to-adoc-2026-07-05.zip` (gitignored local backups; the redacted - extracted working copy is `shared/skills/documents/mail-to-adoc/`). Record what changed and check whether any of the four + extracted working copy is `ai-artifacts/skills/shared/documents/mail-to-adoc/`). Record what changed and check whether any of the four problems in PRD.md (tangled HTML→AsciiDoc logic, adoc-only output, image macros instead of links, spurious `+` after table rows) were already fixed upstream. Update PRD.md accordingly. diff --git a/.scratch/mail-to-doc/issues/02-analyse-effectiveness.md b/.scratch/mail-to-doc/issues/02-analyse-effectiveness.md index 2b012fa..7ac799c 100644 --- a/.scratch/mail-to-doc/issues/02-analyse-effectiveness.md +++ b/.scratch/mail-to-doc/issues/02-analyse-effectiveness.md @@ -4,7 +4,7 @@ Status: needs-triage ## What to build -A written analysis of how well the 2026-07-05 version (`shared/skills/documents/mail-to-adoc/`) +A written analysis of how well the 2026-07-05 version (`ai-artifacts/skills/shared/documents/mail-to-adoc/`) actually works, as input for issue 03. Note: the artifacts copy is redacted (placeholder emails/party names); the unredacted original is `.temp/mail-to-adoc-2026-07-05.zip` (local-only). Cover: diff --git a/.scratch/profile-improvement/PRD.md b/.scratch/profile-improvement/PRD.md index 59e740b..2a61dec 100644 --- a/.scratch/profile-improvement/PRD.md +++ b/.scratch/profile-improvement/PRD.md @@ -3,7 +3,7 @@ Status: ready-for-human The live source-of-truth is Settings → Instructions for Claude (claude.ai / Claude Desktop Chat). -The repo copy is `instructions/claude-ai/profile.md` — edit here, paste there. +The repo copy is `ai-artifacts/instructions/anthropic/claude-ai/profile.md` — edit here, paste there. Two versions are in `artifacts/`: - `profile-current.md` — the version refined in a claude.ai session; live as of 2026-06 @@ -27,7 +27,7 @@ review; the behavioral eval is a separate session. Diff the two versions rule by rule. For each rule where they differ, decide: is the current version more precise, or did compression in the prior version lose something, or did the current version introduce unnecessary verbosity? Apply the better formulation back to -`instructions/claude-ai/profile.md` and paste the final result into Settings. +`ai-artifacts/instructions/anthropic/claude-ai/profile.md` and paste the final result into Settings. ## User Stories @@ -40,7 +40,7 @@ version introduce unnecessary verbosity? Apply the better formulation back to ## Implementation Decisions -- **Source of truth:** `instructions/claude-ai/profile.md`; paste into Settings when updated +- **Source of truth:** `ai-artifacts/instructions/anthropic/claude-ai/profile.md`; paste into Settings when updated - **Diff axis:** per-rule comparison, not whole-file; the structure (§1–§9 + Facts) is settled @@ -59,4 +59,4 @@ version introduce unnecessary verbosity? Apply the better formulation back to - The CLAUDE.md at `~/.claude/CLAUDE.md` is a different file (global Claude Code instructions); do not conflate with this profile - The profile header "Paste into: Settings → Instructions for Claude" should stay in - `instructions/claude-ai/profile.md` as a placement reminder but must be stripped before pasting + `ai-artifacts/instructions/anthropic/claude-ai/profile.md` as a placement reminder but must be stripped before pasting diff --git a/.scratch/public-repo-compliance/PRD.md b/.scratch/public-repo-compliance/PRD.md index 856f00c..2f17794 100644 --- a/.scratch/public-repo-compliance/PRD.md +++ b/.scratch/public-repo-compliance/PRD.md @@ -11,12 +11,12 @@ The repo is already **PUBLIC** (`github.com/DenWin/ai-lab`), but the items gated publishing" never ran: 1. **Third-party attribution.** [import-upstream-skills](../import-upstream-skills/PRD.md) says to - add upstream's LICENSE (e.g. `THIRD-PARTY/mattpocock-skills.LICENSE`) "before publishing, since + add upstream's LICENSE (historically tracked in a dedicated third-party attribution folder) "before publishing, since the repo redistributes adapted copies of his work." Vendored copies are committed (mattpocock - skills under `shared/skills/` + `.scratch/*/artifacts/`; MIT-licensed `claude-video` under + skills under `ai-artifacts/skills/shared/` + `.scratch/*/artifacts/`; MIT-licensed `claude-video` under `.scratch/add-watch-skill/artifacts/`), and the repo is live. 2. **Profile exposure.** The personal behavioral profile - ([anthropic/claude-ai/instructions/profile.md](../../anthropic/claude-ai/instructions/profile.md)) + ([ai-artifacts/instructions/anthropic/claude-ai/profile.md](../../ai-artifacts/instructions/anthropic/claude-ai/profile.md)) and committed `.scratch` history are public. Probably fine — but it should be a conscious decision, not a side effect of `gh repo create`. 3. **Free hardening wins.** [[harden-github-repo]] is still needs-triage, and its central open @@ -26,9 +26,9 @@ publishing" never ran: ## Solution -_Proposed — refine in triage:_ +*Proposed — refine in triage:* -- Add `THIRD-PARTY/` attribution files for all vendored upstream content; audit `.scratch/*/artifacts/` +- Add attribution files for all vendored upstream content; audit `.scratch/*/artifacts/` for anything else redistributed. - Explicit go/no-go on public visibility of the profile and scratch history (alternative: flip repo to private until [[harden-github-repo]] lands). @@ -38,7 +38,7 @@ _Proposed — refine in triage:_ ## Progress (2026-07-04) - ✅ **Item 1 — attribution:** both upstreams verified MIT. - [THIRD-PARTY/](../../THIRD-PARTY/README.md) created with license copies for vendored sources + Attribution files were created in a dedicated third-party folder at the time, with license copies for vendored sources (`mattpocock-skills.LICENSE`, `bradautomates-claude-video.LICENSE`) plus a notice map. Exact upstream checkpoints for skills live in each skill's `METADATA.md`, not in summary docs. - ✅ **Item 3 — free hardening wins:** secret scanning, push protection, and Dependabot alerts @@ -47,7 +47,7 @@ _Proposed — refine in triage:_ PR flow anyway) inside [[harden-github-repo]]. - ✅ **Item 2 — decided 2026-07-05: keep public, after a scrub.** Decision: `keep-public`. Before confirming, a redaction audit swept the personal profile - ([anthropic/claude-ai/instructions/profile.md](../../anthropic/claude-ai/instructions/profile.md)) + ([ai-artifacts/instructions/anthropic/claude-ai/profile.md](../../ai-artifacts/instructions/anthropic/claude-ai/profile.md)) and the full `.scratch/` tree (+ the committed config artifacts) for anything personal or sensitive. **Audit scope & result (nothing required redaction):** @@ -65,6 +65,6 @@ _Proposed — refine in triage:_ ## Further Notes -- Related: [[harden-github-repo]] (settings/Actions side), [[import-upstream-skills]] (where the +- Related: [[harden-github-repo]] (GitHub Actions/settings side), [[import-upstream-skills]] (where the attribution requirement was first recorded). -- _Created by Claude Fable 5 via /planning:scratch._ +- *Created by Claude Fable 5 via /planning:scratch.* diff --git a/.scratch/repo-scaffold/PRD.md b/.scratch/repo-scaffold/PRD.md index 858ee41..f2cacc1 100644 --- a/.scratch/repo-scaffold/PRD.md +++ b/.scratch/repo-scaffold/PRD.md @@ -16,8 +16,8 @@ The structure decisions were resolved via grill-me (2026-06-04) and the layout m > superseded. Canonical layout is now `shared/` (default) + `/` + `//`, > most-specific-wins; **loose folders** (no plugin bundles); folders created **on demand**. See > [docs/repo-layout.adoc](../../docs/repo-layout.adoc) — the canonical layout reference. -> Done so far: `git init`; skills → `shared/skills/`; profile → -> `anthropic/claude-ai/instructions/profile.md`; `sync-skills.ps1` retargeted + re-run; layout doc +> Done so far: `git init`; skills → `ai-artifacts/skills/shared/`; profile → +> `ai-artifacts/instructions/anthropic/claude-ai/profile.md`; `sync-skills.ps1` retargeted + re-run; layout doc > authored. Remaining: `AGENTS.md`, file placement, remote, initial commit. `git init` (done) unblocks the `tdd`-skill→foundation-doc git-repo reference (see @@ -26,15 +26,15 @@ This also folded in the earlier `init-git-repo` stub. ## Scope -Phase 1 of the handoff (skill adaptation) is largely done — skills live under `shared/skills/` and +Phase 1 of the handoff (skill adaptation) is largely done — skills live under `ai-artifacts/skills/shared/` and remaining work is tracked in [claude-code-skill-adaptation](../claude-code-skill-adaptation/PRD.md) and [import-upstream-skills](../import-upstream-skills/PRD.md). This feature is **Phase 2 only**: 1. ✅ `git init`, default branch `main`. `.gitignore` already reconciled (`.claude/commands/*` generated; `.temp/*` staged) — no change needed. -2. ✅ Structure resolved + migrated: skills → `shared/skills/`, profile → - `anthropic/claude-ai/instructions/`, `sync-skills.ps1` retargeted, [docs/repo-layout.adoc](../../docs/repo-layout.adoc) - authored. Per-harness artifact folders (`settings/`, `mcp/`, `hooks/`, `output-styles/`) are +2. ✅ Structure resolved + migrated: skills → `ai-artifacts/skills/shared/`, profile → + `ai-artifacts/instructions/anthropic/claude-ai/`, `sync-skills.ps1` retargeted, [docs/repo-layout.adoc](../../docs/repo-layout.adoc) + authored. Per-harness artifact folders (`ai-artifacts/mcp-config/`, `ai-artifacts/hooks/`, `ai-artifacts/output-styles/`) are created **on demand**, not pre-scaffolded. 3. ✅ Root `AGENTS.md` authored (2026-07-04) as an **operational stub**: cross-harness facts only (layout, source-of-truth/mirror rules, `.scratch` workflow, conventions). The behavioral-overlap @@ -53,8 +53,8 @@ and [import-upstream-skills](../import-upstream-skills/PRD.md). This feature is ## Decisions 1. **#6 Taxonomy keying — RESOLVED (grill-me 2026-06-04).** `shared/` (default) + `/` + - `//`, most-specific-wins. Skills default to `shared/skills/`; config - (instructions/settings/mcp/hooks/output-styles) lives under `//`. claude.ai adds + `//`, most-specific-wins. Skills default to `ai-artifacts/skills/shared/`; config + (instructions/mcp-config/hooks/output-styles) lives under `//`. claude.ai adds a `projects//` layer. No vendor-only "platform" key. Ref: docs/repo-layout.adoc. 2. **#7 Packaging — RESOLVED.** Loose folders, not plugin bundles (personal use; can be wrapped into a plugin later if distribution ever matters). diff --git a/.scratch/repo-scaffold/artifacts/HANDOFF-ai-lab-repo-structure.md b/.scratch/repo-scaffold/artifacts/HANDOFF-ai-lab-repo-structure.md index f2e25e7..1d47f8d 100644 --- a/.scratch/repo-scaffold/artifacts/HANDOFF-ai-lab-repo-structure.md +++ b/.scratch/repo-scaffold/artifacts/HANDOFF-ai-lab-repo-structure.md @@ -98,12 +98,15 @@ ai-lab/ ├── instructions/ │ ├── global/ # account-wide / cross-vendor (e.g. profile) │ └── // # specific overrides — keying per #6 -├── skills/ # // OR nested in plugin bundles — per #6/#7 -├── agents/ # subagents -├── hooks/ # hook scripts / hooks.json -├── mcp/ # MCP server defs (.mcp.json) — was missing -├── output-styles/ # was missing -├── settings/ # settings.json / permissions — was missing +├── ai-artifacts/ +│ ├── skills/ # // OR nested in plugin bundles — per #6/#7 +│ ├── agents/ # subagents +│ ├── hooks/ # hook scripts / hooks.json +│ ├── mcp-config/ # MCP server defs (.mcp.json) +│ ├── output-styles/ # reusable output styles +│ ├── prompts/ # reusable prompt packs +│ ├── instructions/ # harness instruction surfaces +│ └── plugins/ # plugin packaging └── eval/ └── INSTRUCTION-EVAL.md ``` @@ -111,10 +114,10 @@ ai-lab/ **Design rationale:** - `AGENTS.md` at root is the broadest-compatible entrypoint; harness/vendor-specific files - (`CLAUDE.md`, `copilot-instructions.md`) live under `instructions/`, keyed per #6. + (`CLAUDE.md`, `copilot-instructions.md`) live under `ai-artifacts/instructions/`, keyed per #6. - The living doc maps (harness × artifact type) with vendor as a tag — not "platform × harness". Start minimal; grow as knowledge grows. -- If plugins win (#7), `skills/`, `agents/`, `hooks/`, `mcp/`, `output-styles/` nest inside +- If plugins win (#7), `ai-artifacts/skills/`, `ai-artifacts/agents/`, `ai-artifacts/hooks/`, `mcp/`, `ai-artifacts/output-styles/` nest inside `plugins//` rather than at top level. - `eval/` holds `INSTRUCTION-EVAL.md` (built) + the pending harness (separate handoff). - `instructions/global/` holds the account-wide profile copy (source of truth stays Settings → @@ -129,9 +132,9 @@ ai-lab/ and grow detail pages lazily? 3. PowerShell-Skripte project-specific files (`02-project-instructions.md`, `CLAUDE.md`, `POWERSHELL.md`, `powershell.yml`) — these belong to a specific claude.ai project, not - the global repo. Options: (a) move under `instructions/` keyed per #6 (e.g. + the global repo. Options: (a) move under `ai-artifacts/instructions/` keyed per #6 (e.g. `…/anthropic/projects/pwsh/`); (b) leave them in the claude.ai project only; (c) both. Confirm. -4. `skills/shared/` concept: define what makes a skill "shared" — a skill whose _intent_ is +4. `ai-artifacts/skills/shared/` concept: define what makes a skill "shared" — a skill whose _intent_ is cross-vendor (logic documented once, adapted per harness), or one whose _file format_ runs on multiple harnesses unchanged? (Ties to the capability-contract principle below.) 5. Setup/init skill shape: monolithic setup skill (Matt's pattern) vs self-configuring vs @@ -140,12 +143,12 @@ ai-lab/ 6. Terminology alignment: the matrix taxonomy below settles on **harness** (primary) + **vendor** (tag), with **model** excluded. The structure proposal and Requirements still say "platform" (and list Copilot as one — it's a harness). When scaffolding, decide the folder keys - accordingly: e.g. `instructions///`, `skills///`, and rename + accordingly: e.g. `ai-artifacts/instructions///`, `ai-artifacts/skills///`, and rename `docs/platforms/` → `docs/harnesses/`. Don't silently keep "platform" — it's the ambiguity this session flagged. This includes the living doc's own filename: rename `platform-matrix.md` → e.g. `compatibility-matrix.md` or `harness-matrix.md`. -7. Packaging: loose artifact folders (`skills/`, `agents/`, `hooks/`, `mcp/`, `output-styles/`, - `settings/`) vs Claude Code **plugin bundles** (`.claude-plugin/plugin.json` wrapping them). +7. Packaging: loose artifact folders (`ai-artifacts/skills/`, `ai-artifacts/agents/`, `ai-artifacts/hooks/`, `mcp/`, `ai-artifacts/output-styles/`, + `ai-artifacts/mcp-config/`, `ai-artifacts/plugins/`) vs Claude Code **plugin bundles** (`.claude-plugin/plugin.json` wrapping them). Plugins are the native share/install unit and compose cleanly; loose folders are simpler but not directly installable. Regardless of choice, the structure currently has no home for MCP defs, output styles, or settings — add them. @@ -193,7 +196,7 @@ in plugin bundles) — treat them as illustrative, not committed. if user wants an adversarial pass). 8. Place existing non-skill files (profile, INSTRUCTION-EVAL, project files) per the artifact inventory above. -9. Place the adapted Claude Code skills from Phase 1 into `skills/`. +9. Place the adapted Claude Code skills from Phase 1 into `ai-artifacts/skills/`. 10. Author `AGENTS.md` root stub and the living compatibility-matrix draft (filename per #6). 11. Initial commit + push. 12. (Optional) In claude.ai: Settings → Connectors → GitHub Integration → connect + add @@ -245,7 +248,7 @@ The "artifact type" axis needs a fuller vocabulary. Claude Code (richest harness | Skills (slash commands now unified in) | `skills//SKILL.md` | Claude family; others vary | | Subagents | `agents/*.md` | Claude Code | | Hooks | `hooks.json` / `settings.json` | Claude Code | -| Output styles | `output-styles/` | Claude Code | +| Output styles | `ai-artifacts/output-styles/` | Claude Code | | **MCP servers** | `.mcp.json`, `~/.claude.json` | **cross-vendor (open protocol)** — Codex, Cursor, etc.; config location varies | | Settings / permissions | `settings.json`, `settings.local.json` | Claude Code | | Plugins (bundle wrapping all the above) | `.claude-plugin/plugin.json` | Claude Code | diff --git a/.scratch/repo-scope-strays/PRD.md b/.scratch/repo-scope-strays/PRD.md index 414f830..cdd9d6c 100644 --- a/.scratch/repo-scope-strays/PRD.md +++ b/.scratch/repo-scope-strays/PRD.md @@ -10,7 +10,7 @@ Quick capture — iron out in scratch-planning, don't action yet. The repo's declared identity (AI-configuration lab: skills, harness docs, instructions, tracker) is being diluted by undeclared strays: -- `VSCode_Extsion/` (note the folder-name typo — "Extsion") — a shipped VS Code extension at repo +- `VSCode_Extension/` (historically created as `VSCode_Extsion/`, with the typo) — a shipped VS Code extension at repo root, unrelated to the lab's stated purpose, with no README tying it in. - [mail-to-doc](../mail-to-doc/PRD.md) — a general software project (eml→AsciiDoc converter) riding in the `.scratch` tracker. @@ -21,7 +21,7 @@ doesn't account for. ## Solution -_Proposed — refine in triage. Two clean options:_ +*Proposed — refine in triage. Two clean options:* 1. **Declare incubation:** "the lab also incubates small tools" — give them a home (e.g. `tools/` or `projects/`), fix the folder-name typo, add a README per tool, and record the @@ -35,8 +35,8 @@ Either answer is fine — the undeclared middle isn't. Renaming/moving the exten ## Further Notes - Touches the repo-layout doc owned by [[repo-scaffold]]; coordinate if both are in flight. -- 2026-07-04 scratch-plan (user input): `VSCode_Extsion/` **needs a README**; it is an outlier but +- 2026-07-04 scratch-plan (user input): `VSCode_Extension/` **needs a README**; it is an outlier but not unrelated — it exists to make reading AsciiDoc files in VS Code easier, in support of AI projects. And [[mail-to-doc]] is a **skill**, not a software project — which weakens the "stray software project" framing above; re-frame during triage. -- _Created by Claude Fable 5 via /planning:scratch._ +- *Created by Claude Fable 5 via /planning:scratch.* diff --git a/.scratch/sync-skills-drift-check/PRD.md b/.scratch/sync-skills-drift-check/PRD.md index e4c5586..8414454 100644 --- a/.scratch/sync-skills-drift-check/PRD.md +++ b/.scratch/sync-skills-drift-check/PRD.md @@ -7,7 +7,7 @@ Origin: fable (Claude Fable 5 repo review, 2026-07-04) The SessionStart hook runs `sync-skills.ps1 -IfMissing`, which by design never refreshes a skill whose target already exists. There is no drift detection between the source of truth -(`shared/skills/`) and the generated mirror (`.claude/commands/`). Consequence: after editing a +(`ai-artifacts/skills/shared/`) and the generated mirror (`.claude/commands/`). Consequence: after editing a skill, the stale-mirror failure mode is the **default** path — the hook silently skips, and the session keeps invoking the old version until someone remembers to re-run the sync manually. diff --git a/.scratch/understand-scratch-skill/PRD.md b/.scratch/understand-scratch-skill/PRD.md index fe22d00..4e7ec72 100644 --- a/.scratch/understand-scratch-skill/PRD.md +++ b/.scratch/understand-scratch-skill/PRD.md @@ -16,20 +16,20 @@ its real behavior, not the one-line description. Establish ground truth before r - What files does quick-capture create/touch — `PRD.md`, the `BACKLOG.md` row, anything else? What status/ranking does a new entry get, and how is the slug chosen? - How does it relate to the sibling skills (`scratch-plan`, `to-issues`, `to-prd`, `triage`) and the - canonical [LAYOUT.md](../../shared/skills/planning/scratch/LAYOUT.md) they all reference? + canonical [LAYOUT.md](../../ai-artifacts/skills/shared/planning/scratch/LAYOUT.md) they all reference? - Does the skill's documented behavior match what's been happening in practice (e.g. the stub PRDs + TBD backlog rows captured this session)? Any gaps between the SKILL.md and actual effect? ## Notes -- Source of truth: `shared/skills/planning/scratch/SKILL.md` (+ `LAYOUT.md`, `RANKING.md`); deployed +- Source of truth: `ai-artifacts/skills/shared/planning/scratch/SKILL.md` (+ `LAYOUT.md`, `RANKING.md`); deployed mirror under `.claude/commands/planning/scratch/`. - Motivation seems to be calibrating trust in the scratch workflow before leaning on it more (cf. [[gated-work-prd-issue-approval]], [[capture-not-execute]]). ## Findings (investigation 2026-07-05) -Ground truth read from `shared/skills/planning/scratch/SKILL.md` (+ `LAYOUT.md`, `RANKING.md`) and +Ground truth read from `ai-artifacts/skills/shared/planning/scratch/SKILL.md` (+ `LAYOUT.md`, `RANKING.md`) and `scratch-plan/SKILL.md`, cross-checked against the actual `.scratch/` tree — and, for the sibling relationship, against the committed mattpocock upstream artifacts of `to-issues`/`to-prd`/`triage` (§3a). @@ -72,7 +72,7 @@ does no ranking. Ranking is `scratch-plan`'s job (§3). runs a one-question-at-a-time interview (priority / importance / effort, with fuzzy-input bucket rounding), computes `P × I × E`, and **rewrites** `BACKLOG.md` sorted with a `Last updated:` line. Division of labour: `scratch` = capture + display; `scratch-plan` = calibrate + rank. -- **`to-issues` / `to-prd` / `triage` aren't imported into `shared/skills/` yet** — but their +- **`to-issues` / `to-prd` / `triage` aren't imported into `ai-artifacts/skills/shared/` yet** — but their mattpocock **upstream versions are committed as artifacts** under `import-upstream-skills/artifacts/engineering/{to-issues,to-prd,triage}/SKILL.md` (plus the config docs `setup-matt-pocock-skills/{issue-tracker-local,triage-labels,domain}.md`). Import into @@ -152,7 +152,7 @@ User-requested recheck of the last open question ("how does it relate to the sib "Tracker-contract prerequisites" checklist on [`import-upstream-skills` issue 04](../import-upstream-skills/issues/04-import-planning-cluster.md). Verified against the repo today: the three siblings are still not imported -(`shared/skills/planning/` = `scratch` + `scratch-plan` only), so the §3a answer stands unchanged. +(`ai-artifacts/skills/shared/planning/` = `scratch` + `scratch-plan` only), so the §3a answer stands unchanged. Nothing remains in this scratch; follow-up work lives in [[import-upstream-skills]] issues 02/04. ### Verdict diff --git a/AGENTS.md b/AGENTS.md index 1a7d30d..75efc31 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,8 +8,10 @@ of truth; each harness loads live copies from its own locations. Canonical reference: [docs/repo-layout.adoc](docs/repo-layout.adoc). Short version: -- `shared/` — vendor- and harness-agnostic artifacts (the default home; all skills live here) -- `//` — harness-specific config (e.g. `anthropic/claude-ai/instructions/`) +- `ai-artifacts/skills/shared/` — source skills; scope-specific skills go under `ai-artifacts/skills///` +- `ai-artifacts/instructions///` — repo copies of harness instruction surfaces +- `ai-artifacts/hooks/`, `ai-artifacts/mcp-config/`, `ai-artifacts/output-styles/`, `ai-artifacts/agents/`, `ai-artifacts/prompts/`, `ai-artifacts/plugins/` — artifact-type roots, each scoped below by + `shared/`, `/`, or `//` - `docs/harnesses/` — per-harness self-descriptions: instruction surfaces, load models, limits - `.scratch/` — committed local-markdown issue tracker (PRDs + issues + ranked `BACKLOG.md`) - `.temp/` — gitignored landing zone for transient local working files, downloads, and scratch output @@ -20,13 +22,13 @@ Most-specific wins; folders are created on demand, never pre-scaffolded empty. ## Rules that prevent damage - **Never edit `.claude/commands/`** — it is a generated, gitignored mirror. Edit the source under - `shared/skills///`, then rebuild: `pwsh scripts/sync-skills.ps1`. + `ai-artifacts/skills/shared///`, then rebuild: `pwsh scripts/sync-skills.ps1`. - **Never edit `.agents/skills/`** — it is the generated Codex skill mirror. Edit the source under - `shared/skills///`, then rebuild: `pwsh scripts/sync-skills.ps1`. + `ai-artifacts/skills/shared///`, then rebuild: `pwsh scripts/sync-skills.ps1`. - On a fresh clone or a cloud/sandbox session the generated mirrors may not exist. Run the sync script once — the SessionStart hook that does this locally lives in machine-local settings and won't be there. -- Files under `instructions/` and `anthropic/*/instructions/` are repo copies for editing; the live - version sits in each harness's own surface (see [instructions/README.md](instructions/README.md)). +- Files under `ai-artifacts/instructions///` are repo copies for editing; the live version sits + in each harness's own surface (see [ai-artifacts/instructions/README.md](ai-artifacts/instructions/README.md)). Editing the repo copy changes nothing until it is deployed there. - Use `git mv` when moving or renaming tracked files. - This repo is **public**. No secrets anywhere — including instruction files and `.scratch/`. @@ -46,25 +48,27 @@ explicitly requested against a specific scratch. The working rules for agents operating in `.scratch/` — capture≠execute, **deliverables live outside the scratch** (`artifacts/` is supporting material only), and ranking hygiene — live in the folder guide [.scratch/AGENTS.md](.scratch/AGENTS.md); structural layout stays in the `scratch` skill's -[LAYOUT.md](shared/skills/planning/scratch/LAYOUT.md). +[LAYOUT.md](ai-artifacts/skills/shared/planning/scratch/LAYOUT.md). ## Conventions - Primary environment: Windows, PowerShell 7 (`pwsh`). Write scripts in pwsh unless the target is cross-platform. - Docs: Markdown by default; AsciiDoc (`docs/*.adoc`) where richer syntax is needed. +- Keep `AGENTS.md` Markdown because harnesses load it directly; keep `docs/repo-layout.adoc` + AsciiDoc because it is the richer canonical layout reference. - OKF: durable markdown reference/catalog docs should follow [docs/okf-adoption.md](docs/okf-adoption.md) unless another local format owns the file. - Skills follow the **capability contract**: if shell/filesystem is available, take the full agentic path; otherwise degrade to a conversational fallback. Write "if shell available" — - never "if ". + never "if [harness name]". - AI-generated code changes must follow the coding policies in `coding-policies/`: load `polyglot-policy.yaml` first, then the resolved language policy from `coding-policies/languages/` per `usage-policy.yaml`. - Vendored/imported skill provenance lives in each skill's `METADATA.md`; the origin map is - [shared/skills/README.md](shared/skills/README.md). + [ai-artifacts/skills/shared/README.md](ai-artifacts/skills/shared/README.md). - **Single owner per fact:** each fact lives in one canonical file; other docs link to it instead - of restating (layout → `docs/repo-layout.adoc`, skill origins → `shared/skills/README.md`, + of restating (layout → `docs/repo-layout.adoc`, skill origins → `ai-artifacts/skills/shared/README.md`, scratch working rules → `.scratch/AGENTS.md`, scratch structural layout → the `scratch` skill's `LAYOUT.md`). diff --git a/CONTEXT.md b/CONTEXT.md new file mode 100644 index 0000000..556a93a --- /dev/null +++ b/CONTEXT.md @@ -0,0 +1,165 @@ +# AI Lab + +AI Lab is a source-of-truth repo for AI-assisted-work configuration. It organizes shared skills, +instruction surfaces, and harness-specific support artifacts so multiple AI harnesses can work from +one maintained repo. + +## Language + +### Core context + +**AI Lab**: +The repo itself: the maintained source of truth for AI-assisted-work configuration, documentation, +and sync tooling. +*Avoid*: workspace, toolkit, playground + +**Harness**: +A concrete AI runtime or product surface that loads instructions, tools, and config in its own +way. Claude Code, claude.ai, Copilot, and Codex are different harnesses. +*Avoid*: model, vendor, agent + +**Vendor**: +The platform/provider namespace above a harness, used for folder scoping such as `anthropic/` or +`openai/`. +*Avoid*: harness, runtime + +**Artifact type**: +A top-level repo family defined by what something is, not by who uses it. Examples include +`ai-artifacts/skills/`, `ai-artifacts/instructions/`, `ai-artifacts/mcp-config/`, +`ai-artifacts/prompts/`, and `ai-artifacts/plugins/`. +*Avoid*: bucket, misc, catch-all + +**Scope tier**: +The level at which an artifact applies: `shared/`, `/`, or `//`. +More-specific tiers override more-general ones. +*Avoid*: environment, layer + +### Skill system + +**Skill**: +An invocable, task-shaped instruction package with a `SKILL.md` and optional bundled resources. +In this repo, skills are grouped by intent under `ai-artifacts/skills/shared///`. +*Avoid*: script, prompt, command + +**Source skill**: +The maintained skill definition under `ai-artifacts/skills/shared/` or another scoped source folder. This is the +editable copy. +*Avoid*: mirror, generated skill + +**Generated mirror**: +A harness-specific build artifact produced from source skills, such as `.claude/commands/` or +`.agents/skills/`. +*Avoid*: source skill, canonical copy + +**Capability contract**: +The rule that a skill should take the full agentic path when shell/filesystem access exists and +degrade to a conversational fallback when it does not. +*Avoid*: harness-specific branch, hardcoded runtime path + +**Sync**: +The act of rebuilding generated mirrors from source artifacts, typically via +`pwsh scripts/sync-skills.ps1`. +*Avoid*: deploy, publish + +### Planning and work tracking + +**Scratch**: +The repo's committed local-markdown work tracker under `.scratch/`, used for PRDs, issues, ranking, +and support artifacts. +*Avoid*: backlog file, temp folder + +**PRD**: +Product Requirements Document. In this repo it is the primary planning document for a scratch +feature folder. +*Avoid*: issue, ADR, spec note + +**Issue**: +A smaller tracked work item inside a scratch feature, usually under `issues/`. +*Avoid*: PRD, artifact + +**Scratch artifact**: +Supporting material stored under `.scratch//artifacts/`. It exists to support planning or +analysis, not to be the final deliverable. +*Avoid*: deliverable, permanent home + +**Deliverable**: +A finished repo output that belongs in its real artifact-type home, such as `ai-artifacts/skills/`, `docs/`, or +`ai-artifacts/mcp-config/`, rather than inside `.scratch/`. +*Avoid*: scratch artifact, draft input + +### Instruction and config surfaces + +**Instruction surface**: +A file or location a harness actually reads as instructions. Repo copies are edited here, but the +live loaded copy may exist elsewhere. +*Avoid*: any markdown file, README + +**Repo copy**: +A source-controlled editing copy of a harness-owned artifact whose live version is stored outside +the repo. +*Avoid*: live version, generated mirror + +**Live version**: +The copy actually loaded by a harness at runtime, such as a profile field, settings location, or +generated mirror. +*Avoid*: repo copy, source file + +**MCP**: +Model Context Protocol. In this repo, MCP-related material belongs under `ai-artifacts/mcp-config/` when it is a +durable repo artifact. +*Avoid*: generic tool config, extension + +**Output style**: +A reusable asset that shapes how a model formats or frames its responses. +*Avoid*: skill, instruction surface + +### Documentation and provenance + +**ADR**: +Architecture Decision Record. It captures a hard-to-reverse, context-sensitive decision that would +otherwise be surprising later. +*Avoid*: PRD, issue, meeting note + +**OKF**: +Open Knowledge Format. In this repo it is the lightweight metadata/documentation convention used +where no harness-owned format already controls the file. +*Avoid*: frontmatter in general, runtime instruction format + +**Origin map**: +The human-readable summary of where skills came from, maintained in `ai-artifacts/skills/shared/README.md`. +*Avoid*: provenance file, metadata file + +## Flagged ambiguities + +**Artifact vs scratch artifact**: +An artifact in the broad sense is any repo asset. A scratch artifact is specifically supporting +material inside `.scratch//artifacts/` and is not the final home of deliverables. + +**Instruction vs settings**: +Instructions tell a harness how to behave. Settings configure a harness or tool. They may overlap in +purpose but do not belong in the same artifact-type folder by default. + +**Source of truth vs live version**: +The repo is the source of truth for maintained copies, but some live harness-loaded files exist +outside the repo. Source of truth does not mean every runtime file is read from the repo directly. + +**Vendor vs harness**: +The vendor is the provider namespace; the harness is the specific runtime/product surface beneath +it. `anthropic/claude-ai/` names both, in that order. + +## Example dialogue + +Dev: Should this Codex-specific config live in `.scratch/` until we wire it up? + +Domain expert: No. If it is a real deliverable, put it in its artifact-type home. For repo config, +that means something like `ai-artifacts/mcp-config/openai/codex/`. + +Dev: Then what belongs in `.scratch/`? + +Domain expert: The PRD, any follow-up issues, and supporting artifacts used to reason about the +change. `.scratch/` tracks the work; it is not the final home of the output. + +Dev: And if I need to adapt a skill for two harnesses? + +Domain expert: Edit the source skill first. The generated mirrors are build artifacts, so you sync +them after the source change rather than editing `.claude/commands/` or `.agents/skills/` directly. diff --git a/THIRD-PARTY/README.md b/THIRD-PARTY/README.md deleted file mode 100644 index 7837e6d..0000000 --- a/THIRD-PARTY/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Third-party notices - -This repo redistributes adapted copies and verbatim snapshots of upstream work. Each upstream's -license is preserved here; per-skill provenance (`upstream-*` frontmatter) records the exact -source path and pinned commit. The human-readable origin map is -[shared/skills/README.md](../shared/skills/README.md). - -| Upstream | License | Pinned commit | Where used in this repo | -|---|---|---|---| -| [mattpocock/skills](https://github.com/mattpocock/skills) | [MIT](mattpocock-skills.LICENSE) | `aaf2453fbdfe7a15c07f11d861224f34ab4b53cb` | Adapted skills under `shared/skills/` (tdd, prototype, caveman, grill-me, handoff, write-a-skill); verbatim snapshots under `.scratch/import-upstream-skills/artifacts/` and `.scratch/claude-code-skill-adaptation/artifacts/` | -| [bradautomates/claude-video](https://github.com/bradautomates/claude-video) | [MIT](bradautomates-claude-video.LICENSE) | `c333c2289e57bf040b32846f18d669e3f8edad9b` | Verbatim snapshot under `.scratch/add-watch-skill/artifacts/watch/` (not yet adapted) | - -When vendoring a new upstream, add its license file here (`-.LICENSE`) and a row to -this table in the same commit that lands the vendored content. diff --git a/THIRD-PARTY/bradautomates-claude-video.LICENSE b/THIRD-PARTY/bradautomates-claude-video.LICENSE deleted file mode 100644 index e23e30f..0000000 --- a/THIRD-PARTY/bradautomates-claude-video.LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 Bradley Bonanno - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/THIRD-PARTY/mattpocock-skills.LICENSE b/THIRD-PARTY/mattpocock-skills.LICENSE deleted file mode 100644 index f1dd2c0..0000000 --- a/THIRD-PARTY/mattpocock-skills.LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 Matt Pocock - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/VSCode_Extsion/adoc-preview-menu-1.0.0/extension.js b/VSCode_Extension/adoc-preview-menu-1.0.0/extension.js similarity index 100% rename from VSCode_Extsion/adoc-preview-menu-1.0.0/extension.js rename to VSCode_Extension/adoc-preview-menu-1.0.0/extension.js diff --git a/VSCode_Extsion/adoc-preview-menu-1.0.0/package.json b/VSCode_Extension/adoc-preview-menu-1.0.0/package.json similarity index 100% rename from VSCode_Extsion/adoc-preview-menu-1.0.0/package.json rename to VSCode_Extension/adoc-preview-menu-1.0.0/package.json diff --git a/ai-artifacts/agents/README.md b/ai-artifacts/agents/README.md new file mode 100644 index 0000000..5fc2376 --- /dev/null +++ b/ai-artifacts/agents/README.md @@ -0,0 +1,5 @@ +# Agents + +Agent and subagent definitions plus any durable support material they need. + +Use `ai-artifacts/agents/shared/`, `ai-artifacts/agents//`, or `ai-artifacts/agents///`. diff --git a/ai-artifacts/hooks/README.md b/ai-artifacts/hooks/README.md new file mode 100644 index 0000000..6ce7c45 --- /dev/null +++ b/ai-artifacts/hooks/README.md @@ -0,0 +1,6 @@ +# Hooks + +Source-controlled hook definitions for AI harnesses. + +Use `ai-artifacts/hooks/shared/`, `ai-artifacts/hooks//`, or `ai-artifacts/hooks///` when a hook artifact itself +belongs in the repo. Hook-related skills remain under `ai-artifacts/skills/`. diff --git a/ai-artifacts/instructions/README.md b/ai-artifacts/instructions/README.md new file mode 100644 index 0000000..3f23b8d --- /dev/null +++ b/ai-artifacts/instructions/README.md @@ -0,0 +1,26 @@ +# Instructions + +Harness-specific instruction files, keyed as `ai-artifacts/instructions///` (matching +`docs/harnesses/`). Each file here is the **repo copy for editing** — the live version lives in that +harness's own location. These are **not interchangeable**: different harnesses read different files, +and overlap between them is expected, not duplication to be merged. + +This folder is intentionally broader than the old name suggested: it holds claude.ai profile +instructions, project instructions, global `CLAUDE.md` copies, Copilot/Codex instruction surfaces, +and equivalent base instruction documents for other AI agents. + +Current tracked file: + +- `anthropic/claude-ai/profile.md` (claude.ai Chat tab). Live location: Settings → Instructions for Claude. + +## Not the same file (common confusion) + +Owned by [docs/repo-layout.adoc](../../docs/repo-layout.adoc) ("Not the same file" section) — not +restated here. Note: earlier versions placed the future Claude Code `CLAUDE.md` repo copy under +`ai-artifacts/instructions/claude-code/` or `ai-artifacts/instructions/anthropic/claude-code/`; the canonical artifact-first +location is now `ai-artifacts/instructions/anthropic/claude-code/CLAUDE.md`. + +See `docs/harnesses/.md` for each harness's instruction surfaces and load model. + +> Remaining instructions-taxonomy questions (which shared content is hoisted into `AGENTS.md`) are +> owned by `.scratch/incorporate-global-claude-setup/` (repo-scaffold itself is done). diff --git a/anthropic/claude-ai/instructions/profile.md b/ai-artifacts/instructions/anthropic/claude-ai/profile.md similarity index 100% rename from anthropic/claude-ai/instructions/profile.md rename to ai-artifacts/instructions/anthropic/claude-ai/profile.md diff --git a/ai-artifacts/mcp-config/README.md b/ai-artifacts/mcp-config/README.md new file mode 100644 index 0000000..67a8bbf --- /dev/null +++ b/ai-artifacts/mcp-config/README.md @@ -0,0 +1,5 @@ +# MCP Config + +Source-controlled MCP server manifests, connection definitions, and adjacent support files. + +Use `ai-artifacts/mcp-config/shared/`, `ai-artifacts/mcp-config//`, or `ai-artifacts/mcp-config///`. diff --git a/ai-artifacts/output-styles/README.md b/ai-artifacts/output-styles/README.md new file mode 100644 index 0000000..876ec31 --- /dev/null +++ b/ai-artifacts/output-styles/README.md @@ -0,0 +1,5 @@ +# Output Styles + +Reusable output-format and response-style assets that shape model output. + +Use `ai-artifacts/output-styles/shared/`, `ai-artifacts/output-styles//`, or `ai-artifacts/output-styles///`. diff --git a/ai-artifacts/plugins/README.md b/ai-artifacts/plugins/README.md new file mode 100644 index 0000000..701449a --- /dev/null +++ b/ai-artifacts/plugins/README.md @@ -0,0 +1,5 @@ +# Plugins + +Plugin packaging, manifests, and durable support material. + +Use `plugins/shared/`, `plugins//`, or `plugins///`. diff --git a/ai-artifacts/prompts/README.md b/ai-artifacts/prompts/README.md new file mode 100644 index 0000000..ed6f3c2 --- /dev/null +++ b/ai-artifacts/prompts/README.md @@ -0,0 +1,5 @@ +# Prompts + +Reusable prompts and prompt packs that are not instruction surfaces. + +Use `ai-artifacts/prompts/shared/`, `ai-artifacts/prompts//`, or `ai-artifacts/prompts///`. diff --git a/shared/skills/README.md b/ai-artifacts/skills/shared/README.md similarity index 96% rename from shared/skills/README.md rename to ai-artifacts/skills/shared/README.md index 7a0284a..431ce1d 100644 --- a/shared/skills/README.md +++ b/ai-artifacts/skills/shared/README.md @@ -6,8 +6,8 @@ the `/setup:check-skill-updates` skill, and anything about that process lives in ## Layout -`shared/skills///SKILL.md` (+ bundled runtime resources) is the **single source of -truth** for skill behavior. `shared/skills///METADATA.md` is the OKF-style catalog and +`ai-artifacts/skills/shared///SKILL.md` (+ bundled runtime resources) is the **single source of +truth** for skill behavior. `ai-artifacts/skills/shared///METADATA.md` is the OKF-style catalog and provenance file for that skill. The invocable copies under generated harness mirrors are build artifacts — never edit them; edit the source and re-run: @@ -25,7 +25,7 @@ Both generated trees are gitignored; never edit either directly. Use `pwsh scripts/sync-skills.ps1 -Target Claude` or `-Target Codex` only when you intentionally want a single mirror. -Namespacing follows the directory: `shared/skills/coding/tdd/` → `/coding:tdd`. +Namespacing follows the directory: `ai-artifacts/skills/shared/coding/tdd/` → `/coding:tdd`. | Group | Intent | | ----------- | ----------------------------------------------------------------- | diff --git a/shared/skills/coding/diagnose/METADATA.md b/ai-artifacts/skills/shared/coding/diagnose/METADATA.md similarity index 100% rename from shared/skills/coding/diagnose/METADATA.md rename to ai-artifacts/skills/shared/coding/diagnose/METADATA.md diff --git a/shared/skills/coding/diagnose/SKILL.md b/ai-artifacts/skills/shared/coding/diagnose/SKILL.md similarity index 100% rename from shared/skills/coding/diagnose/SKILL.md rename to ai-artifacts/skills/shared/coding/diagnose/SKILL.md diff --git a/shared/skills/coding/diagnose/scripts/hitl-loop.template.ps1 b/ai-artifacts/skills/shared/coding/diagnose/scripts/hitl-loop.template.ps1 similarity index 100% rename from shared/skills/coding/diagnose/scripts/hitl-loop.template.ps1 rename to ai-artifacts/skills/shared/coding/diagnose/scripts/hitl-loop.template.ps1 diff --git a/shared/skills/coding/diagnose/scripts/hitl-loop.template.sh b/ai-artifacts/skills/shared/coding/diagnose/scripts/hitl-loop.template.sh similarity index 100% rename from shared/skills/coding/diagnose/scripts/hitl-loop.template.sh rename to ai-artifacts/skills/shared/coding/diagnose/scripts/hitl-loop.template.sh diff --git a/shared/skills/coding/improve-codebase-architecture/DEEPENING.md b/ai-artifacts/skills/shared/coding/improve-codebase-architecture/DEEPENING.md similarity index 100% rename from shared/skills/coding/improve-codebase-architecture/DEEPENING.md rename to ai-artifacts/skills/shared/coding/improve-codebase-architecture/DEEPENING.md diff --git a/shared/skills/coding/improve-codebase-architecture/HTML-REPORT.md b/ai-artifacts/skills/shared/coding/improve-codebase-architecture/HTML-REPORT.md similarity index 100% rename from shared/skills/coding/improve-codebase-architecture/HTML-REPORT.md rename to ai-artifacts/skills/shared/coding/improve-codebase-architecture/HTML-REPORT.md diff --git a/shared/skills/coding/improve-codebase-architecture/INTERFACE-DESIGN.md b/ai-artifacts/skills/shared/coding/improve-codebase-architecture/INTERFACE-DESIGN.md similarity index 100% rename from shared/skills/coding/improve-codebase-architecture/INTERFACE-DESIGN.md rename to ai-artifacts/skills/shared/coding/improve-codebase-architecture/INTERFACE-DESIGN.md diff --git a/shared/skills/coding/improve-codebase-architecture/LANGUAGE.md b/ai-artifacts/skills/shared/coding/improve-codebase-architecture/LANGUAGE.md similarity index 100% rename from shared/skills/coding/improve-codebase-architecture/LANGUAGE.md rename to ai-artifacts/skills/shared/coding/improve-codebase-architecture/LANGUAGE.md diff --git a/shared/skills/coding/improve-codebase-architecture/METADATA.md b/ai-artifacts/skills/shared/coding/improve-codebase-architecture/METADATA.md similarity index 100% rename from shared/skills/coding/improve-codebase-architecture/METADATA.md rename to ai-artifacts/skills/shared/coding/improve-codebase-architecture/METADATA.md diff --git a/shared/skills/coding/improve-codebase-architecture/SKILL.md b/ai-artifacts/skills/shared/coding/improve-codebase-architecture/SKILL.md similarity index 100% rename from shared/skills/coding/improve-codebase-architecture/SKILL.md rename to ai-artifacts/skills/shared/coding/improve-codebase-architecture/SKILL.md diff --git a/shared/skills/coding/prototype/METADATA.md b/ai-artifacts/skills/shared/coding/prototype/METADATA.md similarity index 100% rename from shared/skills/coding/prototype/METADATA.md rename to ai-artifacts/skills/shared/coding/prototype/METADATA.md diff --git a/shared/skills/coding/prototype/SKILL.md b/ai-artifacts/skills/shared/coding/prototype/SKILL.md similarity index 100% rename from shared/skills/coding/prototype/SKILL.md rename to ai-artifacts/skills/shared/coding/prototype/SKILL.md diff --git a/shared/skills/coding/tdd/METADATA.md b/ai-artifacts/skills/shared/coding/tdd/METADATA.md similarity index 100% rename from shared/skills/coding/tdd/METADATA.md rename to ai-artifacts/skills/shared/coding/tdd/METADATA.md diff --git a/shared/skills/coding/tdd/SKILL.md b/ai-artifacts/skills/shared/coding/tdd/SKILL.md similarity index 100% rename from shared/skills/coding/tdd/SKILL.md rename to ai-artifacts/skills/shared/coding/tdd/SKILL.md diff --git a/shared/skills/coding/tdd/behaviors.md b/ai-artifacts/skills/shared/coding/tdd/behaviors.md similarity index 100% rename from shared/skills/coding/tdd/behaviors.md rename to ai-artifacts/skills/shared/coding/tdd/behaviors.md diff --git a/shared/skills/coding/tdd/companions.md b/ai-artifacts/skills/shared/coding/tdd/companions.md similarity index 100% rename from shared/skills/coding/tdd/companions.md rename to ai-artifacts/skills/shared/coding/tdd/companions.md diff --git a/shared/skills/coding/tdd/design-for-testability.md b/ai-artifacts/skills/shared/coding/tdd/design-for-testability.md similarity index 100% rename from shared/skills/coding/tdd/design-for-testability.md rename to ai-artifacts/skills/shared/coding/tdd/design-for-testability.md diff --git a/shared/skills/coding/tdd/reviewing-and-cycle.md b/ai-artifacts/skills/shared/coding/tdd/reviewing-and-cycle.md similarity index 100% rename from shared/skills/coding/tdd/reviewing-and-cycle.md rename to ai-artifacts/skills/shared/coding/tdd/reviewing-and-cycle.md diff --git a/shared/skills/coding/tdd/stacks/csharp.md b/ai-artifacts/skills/shared/coding/tdd/stacks/csharp.md similarity index 100% rename from shared/skills/coding/tdd/stacks/csharp.md rename to ai-artifacts/skills/shared/coding/tdd/stacks/csharp.md diff --git a/shared/skills/coding/tdd/stacks/powershell.md b/ai-artifacts/skills/shared/coding/tdd/stacks/powershell.md similarity index 100% rename from shared/skills/coding/tdd/stacks/powershell.md rename to ai-artifacts/skills/shared/coding/tdd/stacks/powershell.md diff --git a/shared/skills/coding/tdd/stacks/python.md b/ai-artifacts/skills/shared/coding/tdd/stacks/python.md similarity index 100% rename from shared/skills/coding/tdd/stacks/python.md rename to ai-artifacts/skills/shared/coding/tdd/stacks/python.md diff --git a/shared/skills/coding/tdd/stacks/sql.md b/ai-artifacts/skills/shared/coding/tdd/stacks/sql.md similarity index 100% rename from shared/skills/coding/tdd/stacks/sql.md rename to ai-artifacts/skills/shared/coding/tdd/stacks/sql.md diff --git a/shared/skills/coding/tdd/test-doubles.md b/ai-artifacts/skills/shared/coding/tdd/test-doubles.md similarity index 100% rename from shared/skills/coding/tdd/test-doubles.md rename to ai-artifacts/skills/shared/coding/tdd/test-doubles.md diff --git a/shared/skills/coding/zoom-out/METADATA.md b/ai-artifacts/skills/shared/coding/zoom-out/METADATA.md similarity index 100% rename from shared/skills/coding/zoom-out/METADATA.md rename to ai-artifacts/skills/shared/coding/zoom-out/METADATA.md diff --git a/shared/skills/coding/zoom-out/SKILL.md b/ai-artifacts/skills/shared/coding/zoom-out/SKILL.md similarity index 100% rename from shared/skills/coding/zoom-out/SKILL.md rename to ai-artifacts/skills/shared/coding/zoom-out/SKILL.md diff --git a/shared/skills/documents/mail-to-adoc/METADATA.md b/ai-artifacts/skills/shared/documents/mail-to-adoc/METADATA.md similarity index 100% rename from shared/skills/documents/mail-to-adoc/METADATA.md rename to ai-artifacts/skills/shared/documents/mail-to-adoc/METADATA.md diff --git a/shared/skills/documents/mail-to-adoc/README.md b/ai-artifacts/skills/shared/documents/mail-to-adoc/README.md similarity index 100% rename from shared/skills/documents/mail-to-adoc/README.md rename to ai-artifacts/skills/shared/documents/mail-to-adoc/README.md diff --git a/shared/skills/documents/mail-to-adoc/SKILL.md b/ai-artifacts/skills/shared/documents/mail-to-adoc/SKILL.md similarity index 100% rename from shared/skills/documents/mail-to-adoc/SKILL.md rename to ai-artifacts/skills/shared/documents/mail-to-adoc/SKILL.md diff --git a/shared/skills/documents/mail-to-adoc/attachment-blocklist.txt b/ai-artifacts/skills/shared/documents/mail-to-adoc/attachment-blocklist.txt similarity index 100% rename from shared/skills/documents/mail-to-adoc/attachment-blocklist.txt rename to ai-artifacts/skills/shared/documents/mail-to-adoc/attachment-blocklist.txt diff --git a/shared/skills/documents/mail-to-adoc/docs/prd/PRD.md b/ai-artifacts/skills/shared/documents/mail-to-adoc/docs/prd/PRD.md similarity index 100% rename from shared/skills/documents/mail-to-adoc/docs/prd/PRD.md rename to ai-artifacts/skills/shared/documents/mail-to-adoc/docs/prd/PRD.md diff --git a/shared/skills/documents/mail-to-adoc/docs/prd/PRD_EDGE_CASES.md b/ai-artifacts/skills/shared/documents/mail-to-adoc/docs/prd/PRD_EDGE_CASES.md similarity index 100% rename from shared/skills/documents/mail-to-adoc/docs/prd/PRD_EDGE_CASES.md rename to ai-artifacts/skills/shared/documents/mail-to-adoc/docs/prd/PRD_EDGE_CASES.md diff --git a/shared/skills/documents/mail-to-adoc/scripts/Invoke-MailToAdoc.ps1 b/ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/Invoke-MailToAdoc.ps1 similarity index 100% rename from shared/skills/documents/mail-to-adoc/scripts/Invoke-MailToAdoc.ps1 rename to ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/Invoke-MailToAdoc.ps1 diff --git a/shared/skills/documents/mail-to-adoc/scripts/Move-Belege.ps1 b/ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/Move-Belege.ps1 similarity index 100% rename from shared/skills/documents/mail-to-adoc/scripts/Move-Belege.ps1 rename to ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/Move-Belege.ps1 diff --git a/shared/skills/documents/mail-to-adoc/scripts/mail_to_adoc.py b/ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/mail_to_adoc.py similarity index 100% rename from shared/skills/documents/mail-to-adoc/scripts/mail_to_adoc.py rename to ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/mail_to_adoc.py diff --git a/shared/skills/documents/mail-to-adoc/tests/README.md b/ai-artifacts/skills/shared/documents/mail-to-adoc/tests/README.md similarity index 100% rename from shared/skills/documents/mail-to-adoc/tests/README.md rename to ai-artifacts/skills/shared/documents/mail-to-adoc/tests/README.md diff --git a/shared/skills/documents/mail-to-adoc/tests/conftest.py b/ai-artifacts/skills/shared/documents/mail-to-adoc/tests/conftest.py similarity index 100% rename from shared/skills/documents/mail-to-adoc/tests/conftest.py rename to ai-artifacts/skills/shared/documents/mail-to-adoc/tests/conftest.py diff --git a/shared/skills/documents/mail-to-adoc/tests/test_mail_to_adoc.py b/ai-artifacts/skills/shared/documents/mail-to-adoc/tests/test_mail_to_adoc.py similarity index 100% rename from shared/skills/documents/mail-to-adoc/tests/test_mail_to_adoc.py rename to ai-artifacts/skills/shared/documents/mail-to-adoc/tests/test_mail_to_adoc.py diff --git a/shared/skills/planning/scratch-plan/METADATA.md b/ai-artifacts/skills/shared/planning/scratch-plan/METADATA.md similarity index 100% rename from shared/skills/planning/scratch-plan/METADATA.md rename to ai-artifacts/skills/shared/planning/scratch-plan/METADATA.md diff --git a/shared/skills/planning/scratch-plan/SKILL.md b/ai-artifacts/skills/shared/planning/scratch-plan/SKILL.md similarity index 100% rename from shared/skills/planning/scratch-plan/SKILL.md rename to ai-artifacts/skills/shared/planning/scratch-plan/SKILL.md diff --git a/shared/skills/planning/scratch/LAYOUT.md b/ai-artifacts/skills/shared/planning/scratch/LAYOUT.md similarity index 100% rename from shared/skills/planning/scratch/LAYOUT.md rename to ai-artifacts/skills/shared/planning/scratch/LAYOUT.md diff --git a/shared/skills/planning/scratch/METADATA.md b/ai-artifacts/skills/shared/planning/scratch/METADATA.md similarity index 100% rename from shared/skills/planning/scratch/METADATA.md rename to ai-artifacts/skills/shared/planning/scratch/METADATA.md diff --git a/shared/skills/planning/scratch/RANKING.md b/ai-artifacts/skills/shared/planning/scratch/RANKING.md similarity index 100% rename from shared/skills/planning/scratch/RANKING.md rename to ai-artifacts/skills/shared/planning/scratch/RANKING.md diff --git a/shared/skills/planning/scratch/SKILL.md b/ai-artifacts/skills/shared/planning/scratch/SKILL.md similarity index 100% rename from shared/skills/planning/scratch/SKILL.md rename to ai-artifacts/skills/shared/planning/scratch/SKILL.md diff --git a/shared/skills/session/caveman/METADATA.md b/ai-artifacts/skills/shared/session/caveman/METADATA.md similarity index 100% rename from shared/skills/session/caveman/METADATA.md rename to ai-artifacts/skills/shared/session/caveman/METADATA.md diff --git a/shared/skills/session/caveman/SKILL.md b/ai-artifacts/skills/shared/session/caveman/SKILL.md similarity index 100% rename from shared/skills/session/caveman/SKILL.md rename to ai-artifacts/skills/shared/session/caveman/SKILL.md diff --git a/shared/skills/session/grill-me/ADR-FORMAT.md b/ai-artifacts/skills/shared/session/grill-me/ADR-FORMAT.md similarity index 100% rename from shared/skills/session/grill-me/ADR-FORMAT.md rename to ai-artifacts/skills/shared/session/grill-me/ADR-FORMAT.md diff --git a/shared/skills/session/grill-me/CONTEXT-FORMAT.md b/ai-artifacts/skills/shared/session/grill-me/CONTEXT-FORMAT.md similarity index 100% rename from shared/skills/session/grill-me/CONTEXT-FORMAT.md rename to ai-artifacts/skills/shared/session/grill-me/CONTEXT-FORMAT.md diff --git a/shared/skills/session/grill-me/METADATA.md b/ai-artifacts/skills/shared/session/grill-me/METADATA.md similarity index 100% rename from shared/skills/session/grill-me/METADATA.md rename to ai-artifacts/skills/shared/session/grill-me/METADATA.md diff --git a/shared/skills/session/grill-me/SKILL.md b/ai-artifacts/skills/shared/session/grill-me/SKILL.md similarity index 100% rename from shared/skills/session/grill-me/SKILL.md rename to ai-artifacts/skills/shared/session/grill-me/SKILL.md diff --git a/shared/skills/session/handoff/METADATA.md b/ai-artifacts/skills/shared/session/handoff/METADATA.md similarity index 100% rename from shared/skills/session/handoff/METADATA.md rename to ai-artifacts/skills/shared/session/handoff/METADATA.md diff --git a/shared/skills/session/handoff/SKILL.md b/ai-artifacts/skills/shared/session/handoff/SKILL.md similarity index 100% rename from shared/skills/session/handoff/SKILL.md rename to ai-artifacts/skills/shared/session/handoff/SKILL.md diff --git a/shared/skills/session/recon/METADATA.md b/ai-artifacts/skills/shared/session/recon/METADATA.md similarity index 100% rename from shared/skills/session/recon/METADATA.md rename to ai-artifacts/skills/shared/session/recon/METADATA.md diff --git a/shared/skills/session/recon/RECIPES.md b/ai-artifacts/skills/shared/session/recon/RECIPES.md similarity index 100% rename from shared/skills/session/recon/RECIPES.md rename to ai-artifacts/skills/shared/session/recon/RECIPES.md diff --git a/shared/skills/session/recon/SKILL.md b/ai-artifacts/skills/shared/session/recon/SKILL.md similarity index 100% rename from shared/skills/session/recon/SKILL.md rename to ai-artifacts/skills/shared/session/recon/SKILL.md diff --git a/shared/skills/session/write-a-skill/EXAMPLES.md b/ai-artifacts/skills/shared/session/write-a-skill/EXAMPLES.md similarity index 100% rename from shared/skills/session/write-a-skill/EXAMPLES.md rename to ai-artifacts/skills/shared/session/write-a-skill/EXAMPLES.md diff --git a/shared/skills/session/write-a-skill/METADATA.md b/ai-artifacts/skills/shared/session/write-a-skill/METADATA.md similarity index 100% rename from shared/skills/session/write-a-skill/METADATA.md rename to ai-artifacts/skills/shared/session/write-a-skill/METADATA.md diff --git a/shared/skills/session/write-a-skill/SCRIPTS.md b/ai-artifacts/skills/shared/session/write-a-skill/SCRIPTS.md similarity index 100% rename from shared/skills/session/write-a-skill/SCRIPTS.md rename to ai-artifacts/skills/shared/session/write-a-skill/SCRIPTS.md diff --git a/shared/skills/session/write-a-skill/SKILL.md b/ai-artifacts/skills/shared/session/write-a-skill/SKILL.md similarity index 100% rename from shared/skills/session/write-a-skill/SKILL.md rename to ai-artifacts/skills/shared/session/write-a-skill/SKILL.md diff --git a/shared/skills/setup/check-skill-updates/METADATA.md b/ai-artifacts/skills/shared/setup/check-skill-updates/METADATA.md similarity index 100% rename from shared/skills/setup/check-skill-updates/METADATA.md rename to ai-artifacts/skills/shared/setup/check-skill-updates/METADATA.md diff --git a/shared/skills/setup/check-skill-updates/SKILL.md b/ai-artifacts/skills/shared/setup/check-skill-updates/SKILL.md similarity index 92% rename from shared/skills/setup/check-skill-updates/SKILL.md rename to ai-artifacts/skills/shared/setup/check-skill-updates/SKILL.md index b4dc6f7..6a1ca4f 100644 --- a/shared/skills/setup/check-skill-updates/SKILL.md +++ b/ai-artifacts/skills/shared/setup/check-skill-updates/SKILL.md @@ -14,7 +14,7 @@ skill — the actual update is triaged and done later (by a human or an agent pi item), preserving local customizations under review rather than auto-overwriting. - No local clone of any upstream is needed, and it works for **any** `upstream-repo`. -- Source of truth is `shared/skills///SKILL.md`; generated mirrors are rebuilt by +- Source of truth is `ai-artifacts/skills/shared///SKILL.md`; generated mirrors are rebuilt by `scripts/sync-skills.ps1`. - Skills whose `METADATA.md` has no `upstream-commit` are skipped. @@ -33,7 +33,7 @@ changes count, not just `SKILL.md`) and compare to `upstream-commit`. $ErrorActionPreference = 'Stop' $repoRoot = Resolve-Path (Join-Path $PSScriptRoot '..\..\..') -$skillsRoot = Join-Path $repoRoot 'shared\skills' +$skillsRoot = Join-Path $repoRoot 'ai-artifacts\skills\shared' $results = foreach ($metadata in Get-ChildItem $skillsRoot -Recurse -Filter 'METADATA.md') { $content = Get-Content $metadata.FullName -Raw @@ -92,8 +92,8 @@ Upstream has moved past the commit this skill was last reconciled to. ## Action Review the upstream changes and merge the worthwhile ones into -`shared/skills///SKILL.md` (and resources), preserving local customizations, then bump -`upstream-commit` in `shared/skills///METADATA.md` and re-run +`ai-artifacts/skills/shared///SKILL.md` (and resources), preserving local customizations, then bump +`upstream-commit` in `ai-artifacts/skills/shared///METADATA.md` and re-run `scripts/sync-skills.ps1`. See "Appendix: actioning an update" in the check-skill-updates skill for the three-way procedure. ``` @@ -114,7 +114,7 @@ $owner = ''; $repo = '' $upstreamPath = ''; $storedCommit = '' $baseline = (& gh api -H 'Accept: application/vnd.github.raw' "repos/$owner/$repo/contents/$upstreamPath`?ref=$storedCommit") -$installed = Get-Content '\shared\skills\\\SKILL.md' -Raw +$installed = Get-Content '\ai-artifacts\skills\shared\\\SKILL.md' -Raw $newUpstream = (& gh api -H 'Accept: application/vnd.github.raw' "repos/$owner/$repo/contents/$upstreamPath") ``` diff --git a/shared/skills/setup/git-guardrails/METADATA.md b/ai-artifacts/skills/shared/setup/git-guardrails/METADATA.md similarity index 100% rename from shared/skills/setup/git-guardrails/METADATA.md rename to ai-artifacts/skills/shared/setup/git-guardrails/METADATA.md diff --git a/shared/skills/setup/git-guardrails/SKILL.md b/ai-artifacts/skills/shared/setup/git-guardrails/SKILL.md similarity index 100% rename from shared/skills/setup/git-guardrails/SKILL.md rename to ai-artifacts/skills/shared/setup/git-guardrails/SKILL.md diff --git a/shared/skills/setup/git-guardrails/scripts/block-dangerous-git.ps1 b/ai-artifacts/skills/shared/setup/git-guardrails/scripts/block-dangerous-git.ps1 similarity index 100% rename from shared/skills/setup/git-guardrails/scripts/block-dangerous-git.ps1 rename to ai-artifacts/skills/shared/setup/git-guardrails/scripts/block-dangerous-git.ps1 diff --git a/shared/skills/setup/git-guardrails/scripts/block-dangerous-git.sh b/ai-artifacts/skills/shared/setup/git-guardrails/scripts/block-dangerous-git.sh similarity index 100% rename from shared/skills/setup/git-guardrails/scripts/block-dangerous-git.sh rename to ai-artifacts/skills/shared/setup/git-guardrails/scripts/block-dangerous-git.sh diff --git a/shared/skills/setup/import-upstream-skill/METADATA.md b/ai-artifacts/skills/shared/setup/import-upstream-skill/METADATA.md similarity index 100% rename from shared/skills/setup/import-upstream-skill/METADATA.md rename to ai-artifacts/skills/shared/setup/import-upstream-skill/METADATA.md diff --git a/shared/skills/setup/import-upstream-skill/SKILL.md b/ai-artifacts/skills/shared/setup/import-upstream-skill/SKILL.md similarity index 92% rename from shared/skills/setup/import-upstream-skill/SKILL.md rename to ai-artifacts/skills/shared/setup/import-upstream-skill/SKILL.md index 6b6e42c..4673a12 100644 --- a/shared/skills/setup/import-upstream-skill/SKILL.md +++ b/ai-artifacts/skills/shared/setup/import-upstream-skill/SKILL.md @@ -10,7 +10,7 @@ A repeatable, **source-agnostic** process for bringing an outside skill into thi concrete driver was the mattpocock skills repo, but nothing here is specific to it — it works for any `upstream-repo` (or a local/global copy with no repo). -The single source of truth is `shared/skills///SKILL.md` (+ bundled resources). The +The single source of truth is `ai-artifacts/skills/shared///SKILL.md` (+ bundled resources). The `.claude/commands/` copies are a generated mirror — never edit them; rebuild with `pwsh scripts/sync-skills.ps1`. This skill **imports and adapts**; the sibling `/setup:check-skill-updates` is what later detects staleness against upstream. @@ -32,7 +32,7 @@ The single source of truth is `shared/skills///SKILL.md` (+ bundled (conversational/process skills) · `setup` (repo tooling and skill maintenance). Create a new group only when none fit — a new group is a deliberate decision, not a default. -3. **Place it** at `shared/skills///SKILL.md` with resources alongside. Runtime +3. **Place it** at `ai-artifacts/skills/shared///SKILL.md` with resources alongside. Runtime frontmatter should contain only harness-relevant fields plus `version: `; start new local imports at `version: 1.0.0` unless they are explicitly experimental (`0.x.y`). Use `git mv`-style care if you are relocating something already tracked. @@ -70,7 +70,7 @@ The single source of truth is `shared/skills///SKILL.md` (+ bundled 6. **Verify** the result with `/session:write-a-skill` (structure, description triggers, progressive disclosure, references one level deep). -7. **Update the origin map** — add a row to [shared/skills/README.md](../../README.md) (skill, group, +7. **Update the origin map** — add a row to [ai-artifacts/skills/shared/README.md](../../README.md) (skill, group, upstream, notes) so the human-readable summary matches `METADATA.md`. 8. **Rebuild the mirror:** `pwsh scripts/sync-skills.ps1`, then confirm `/group:name` resolves. If the @@ -84,5 +84,5 @@ The single source of truth is `shared/skills///SKILL.md` (+ bundled - [ ] `METADATA.md` has OKF `type` frontmatter and correct `upstream-*` fields — or fork/local-original rules applied (no dangling `upstream-commit`) - [ ] Capability-contract adaptation done; bash → pwsh; intent + local customizations preserved - [ ] Verified via `/session:write-a-skill` -- [ ] `shared/skills/README.md` origin map updated +- [ ] `ai-artifacts/skills/shared/README.md` origin map updated - [ ] `scripts/sync-skills.ps1` run; `/group:name` resolves diff --git a/shared/skills/setup/setup-pre-commit/METADATA.md b/ai-artifacts/skills/shared/setup/setup-pre-commit/METADATA.md similarity index 100% rename from shared/skills/setup/setup-pre-commit/METADATA.md rename to ai-artifacts/skills/shared/setup/setup-pre-commit/METADATA.md diff --git a/shared/skills/setup/setup-pre-commit/SKILL.md b/ai-artifacts/skills/shared/setup/setup-pre-commit/SKILL.md similarity index 100% rename from shared/skills/setup/setup-pre-commit/SKILL.md rename to ai-artifacts/skills/shared/setup/setup-pre-commit/SKILL.md diff --git a/docs/harnesses/claude-code.md b/docs/harnesses/claude-code.md index 1261591..6e801a9 100644 --- a/docs/harnesses/claude-code.md +++ b/docs/harnesses/claude-code.md @@ -26,7 +26,7 @@ Within instruction files, more-specific path wins. Managed > project > user for ## 2. Storage split - **Repo-scoped (committed):** `CLAUDE.md`, `AGENTS.md`, `.claude/settings.json`, - `shared/skills///`, `.mcp.json` + `ai-artifacts/skills/shared///`, `.mcp.json` - **Machine-local (gitignored):** `CLAUDE.local.md`, `.claude/settings.local.json`, `~/.claude/projects//memory/` (auto-memory) - **User-global (all repos):** `~/.claude/CLAUDE.md`, `~/.claude/settings.json`, @@ -56,7 +56,7 @@ Within instruction files, more-specific path wins. Managed > project > user for | Subagents | **Native** — `agents/*.md` | | Hooks | **Native** — `settings.json` (`PreToolUse`, `PostToolUse`, `SessionStart`, etc.) | | MCP servers | **Native** — `.mcp.json` (project), `~/.claude.json` (global), Desktop `claude_desktop_config.json` | -| Output styles | **Native** — `output-styles/` (referenced in settings) | +| Output styles | **Native** — `ai-artifacts/output-styles/` (referenced in settings) | | Settings / permissions | **Native** — `settings.json` / `settings.local.json` | | Plugins / bundles | **Native** — `.claude-plugin/plugin.json` wrapping skills/agents/hooks/mcp | diff --git a/docs/harnesses/codex.md b/docs/harnesses/codex.md index 570dc15..a237878 100644 --- a/docs/harnesses/codex.md +++ b/docs/harnesses/codex.md @@ -32,9 +32,10 @@ For repo-specific working conventions, `AGENTS.md` is the active project contrac ## 2. Storage split - **Repo-scoped (committed):** `AGENTS.md`, `docs/harnesses/*.md`, shared skills under - `shared/skills/`, repo scripts, and any future Codex-specific repo config under `openai/codex/` - if added. -- **Repo-scoped generated (gitignored):** `.agents/skills/`, rebuilt from `shared/skills/` by + `ai-artifacts/skills/shared/`, repo scripts, and any future Codex-specific repo config under + `ai-artifacts/mcp-config/openai/codex/`, `ai-artifacts/plugins/openai/codex/`, or instruction + files under `ai-artifacts/instructions/openai/codex/` if added. +- **Repo-scoped generated (gitignored):** `.agents/skills/`, rebuilt from `ai-artifacts/skills/shared/` by `pwsh scripts/sync-skills.ps1`. - **Machine-local:** Codex home and plugin/skill caches under the user's profile, local approval state, local settings, and sandbox/session metadata. These are not committed to this repo. @@ -45,16 +46,14 @@ For repo-specific working conventions, `AGENTS.md` is the active project contrac ## 3. Disk locations -| Tier | Windows | macOS / Linux | -| -------------------------------- | -------------------------------------------------------- | ------------------------------------------- | -| Repo root | `C:\GIT\DenWin\ai-lab` in this session | `` | -| Repo instruction file | `\AGENTS.md` | `/AGENTS.md` | -| Repo Codex skill mirror | `\.agents\skills\` | `/.agents/skills/` | -| Suggested Codex repo config home | `\openai\codex\` if needed | `/openai/codex/` if needed | -| User Codex home | `%USERPROFILE%\.codex\` observed from skill/plugin paths | `~/.codex/` presumed (`?`) | -| User skills | `%USERPROFILE%\.codex\skills\` observed | `~/.codex/skills/` presumed (`?`) | -| Plugin cache | `%USERPROFILE%\.codex\plugins\cache\` observed | `~/.codex/plugins/cache/` presumed (`?`) | -| Temporary writable area | harness-provided temp directory (`:tmpdir`) | harness-provided temp directory (`:tmpdir`) | +- Repo root: Windows `C:\GIT\DenWin\ai-lab` in this session; macOS / Linux `` +- Repo instruction file: Windows `\AGENTS.md`; macOS / Linux `/AGENTS.md` +- Repo Codex skill mirror: Windows `\.agents\skills\`; macOS / Linux `/.agents/skills/` +- Suggested Codex repo config home: Windows `\ai-artifacts\mcp-config\openai\codex\` if needed; macOS / Linux `/ai-artifacts/mcp-config/openai/codex/` if needed +- User Codex home: Windows `%USERPROFILE%\.codex\` observed from skill/plugin paths; macOS / Linux `~/.codex/` presumed (`?`) +- User skills: Windows `%USERPROFILE%\.codex\skills\` observed; macOS / Linux `~/.codex/skills/` presumed (`?`) +- Plugin cache: Windows `%USERPROFILE%\.codex\plugins\cache\` observed; macOS / Linux `~/.codex/plugins/cache/` presumed (`?`) +- Temporary writable area: harness-provided temp directory (`:tmpdir`) on both platforms Exact Codex settings filenames and all OS-specific config paths are `?` unless observed in a live session or documented by the active runtime. @@ -267,7 +266,7 @@ Operational expectations: ## 16. Operational edge cases - **Generated vs source artifacts:** In this repo, `.claude/commands/` and `.agents/skills/` are - generated mirrors. Edit `shared/skills///`, then rebuild with + generated mirrors. Edit `ai-artifacts/skills/shared///`, then rebuild with `pwsh scripts/sync-skills.ps1`; never edit the mirrors directly. - **Bootstrap requirements:** A fresh clone or sandbox may not have generated mirrors or local harness settings. Use repo scripts and docs to materialize generated state instead of inventing it. @@ -299,8 +298,9 @@ Operational expectations: | Hooks | No verified Codex equivalent | Use scripts, CI, or harness-specific automation elsewhere | | Plugins/connectors | Harness-specific packaging | Underlying tools may map to MCP or app connectors | -**Design principle:** keep shared repo facts in `AGENTS.md`; keep Codex-specific behavior in a -future `openai/codex/` subtree only when it cannot be expressed portably. +**Design principle:** keep shared repo facts in `AGENTS.md`; keep Codex-specific behavior in +`ai-artifacts/instructions/openai/codex/`, `ai-artifacts/mcp-config/openai/codex/`, or +`ai-artifacts/plugins/openai/codex/` only when it cannot be expressed portably. --- diff --git a/docs/harnesses/copilot-cli.md b/docs/harnesses/copilot-cli.md index 10eafb4..5ab5271 100644 --- a/docs/harnesses/copilot-cli.md +++ b/docs/harnesses/copilot-cli.md @@ -132,7 +132,7 @@ window; keep `.github/copilot-instructions.md` and `AGENTS.md` concise. ## 10. Capability limits / notable absences -- No native skill format — skills from `shared/skills/` are not auto-mirrored to a +- No native skill format — skills from `ai-artifacts/skills/shared/` are not auto-mirrored to a Copilot CLI-specific directory (the sync script targets `.claude/commands/` and `.agents/skills/` only); expose skills via extensions or prompt files - No declarative hook system comparable to Claude Code's `settings.json` hooks @@ -253,7 +253,7 @@ edge cases where multiple equally-valid approaches exist. `rename_branch` before the first commit (using `git branch -m` bypasses the session tracking). - **Generated mirrors:** `.claude/commands/` (Claude Code skill mirror) and `.agents/skills/` (Codex skill mirror) are gitignored and rebuilt by `pwsh scripts/sync-skills.ps1`. Run the sync - script after any change to `shared/skills/`; these mirrors may not exist on a fresh clone. + script after any change to `ai-artifacts/skills/shared/`; these mirrors may not exist on a fresh clone. - **Session state persistence:** session state folder (`~/.copilot/session-state//`) persists across checkpoints but is **not** committed. Promote only durable, redacted material to the repo. @@ -281,7 +281,7 @@ edge cases where multiple equally-valid approaches exist. | `.github/extensions/` | Copilot CLI-specific | Extension format is not shared with other harnesses | | `.github/prompts/*.prompt.md` | Copilot family (shared format) | VS Code Copilot invokes as slash commands; Copilot CLI references by path | | MCP (workspace/user config) | Cross-vendor | Config syntax and location differ per harness; protocol is vendor-neutral | -| `shared/skills/` | Source of truth | Mirrored to `.claude/commands/` (Claude Code) and `.agents/skills/` (Codex); not auto-mirrored for Copilot CLI | +| `ai-artifacts/skills/shared/` | Source of truth | Mirrored to `.claude/commands/` (Claude Code) and `.agents/skills/` (Codex); not auto-mirrored for Copilot CLI | | Scheduled workflows | Copilot CLI-specific | No equivalent native primitive in other harnesses | | Canvas panels | Copilot CLI-specific | No equivalent in other harnesses | diff --git a/docs/okf-adoption.md b/docs/okf-adoption.md index 5afb12b..4cd96ad 100644 --- a/docs/okf-adoption.md +++ b/docs/okf-adoption.md @@ -15,7 +15,7 @@ bodies. ## Applied Now -- `shared/skills///METADATA.md` is the OKF-style concept document for each skill. +- `ai-artifacts/skills/shared///METADATA.md` is the OKF-style concept document for each skill. - Skill provenance (`upstream-*`) lives in `METADATA.md`, not in runtime `SKILL.md` frontmatter. - Runtime `SKILL.md` frontmatter keeps harness-facing fields such as `name`, `description`, `argument-hint`, `disable-model-invocation`, and `version`. diff --git a/docs/repo-layout.adoc b/docs/repo-layout.adoc index 21ff5c7..5d7d114 100644 --- a/docs/repo-layout.adoc +++ b/docs/repo-layout.adoc @@ -1,19 +1,21 @@ = Repository Layout -How artifacts (skills, instructions, settings, MCP defs, hooks, output styles, agents) are -organized in this repo, and how they reach each harness. +How AI-assisted-work artifacts (skills, instructions, hooks, MCP defs, +output styles, agents, prompts, plugins, and similar harness assets) are organized in this +repo, and how they reach each harness. -== Organizing principle: specificity, keyed by who an artifact is for +== Organizing principle: artifact type first, then specificity -Every artifact lives at the *most general* level it is valid for. More-specific levels *override* -more-general ones (most-specific wins). +Top-level folders are keyed by *artifact type*. Beneath each artifact type, content is keyed by the +most general scope it is valid for. More-specific levels *override* more-general ones +(most-specific wins). [cols="1,3a",options="header"] |=== | Tier | Meaning | `shared/` -| Vendor- and harness-agnostic. Runs anywhere. The default home — almost everything lives here. +| Vendor- and harness-agnostic. Runs anywhere. The default scope within an artifact-type folder. | `/` | Specific to one vendor (e.g. `anthropic/`, `openai/`), but cross-harness within it. @@ -22,25 +24,28 @@ more-general ones (most-specific wins). | Specific to one harness (e.g. `anthropic/claude-code/`, `anthropic/claude-ai/`). |=== -`shared/skills/coding/tdd` is the cross-everything copy; `anthropic/claude-code/skills/tdd` (if it -existed) would shadow it *only* when running in Claude Code. +`ai-artifacts/skills/shared/coding/tdd` is the cross-everything copy; `ai-artifacts/skills/anthropic/claude-code/coding/tdd` +(if it existed) would shadow it *only* when running in Claude Code. == Top-level layout .... ai-lab/ ├── AGENTS.md # root shared cross-harness instructions (cross-vendor) -├── shared/ # vendor- & harness-agnostic artifacts -│ └── skills/ # ← all current skills (intent-grouped: coding/ documents/ planning/ session/ setup/) -├── anthropic/ -│ ├── claude-ai/ -│ │ ├── instructions/profile.md # account-wide profile (live: Settings → Instructions for Claude) -│ │ └── projects// # per-project: project-instruction.md + knowledge files -│ ├── claude-code/ # settings/ mcp/ hooks/ output-styles/ instructions/ skills/ (on demand) -│ └── claude-desktop/ # (on demand) -├── openai/ # codex, etc. — added via the cross-harness review loop +├── ai-artifacts/ +│ ├── skills/ +│ │ └── shared/ # all current skills (intent-grouped: coding/ documents/ planning/ session/ setup/) +│ ├── instructions/ +│ │ └── anthropic/ +│ │ └── claude-ai/ +│ │ └── profile.md # account-wide profile (live: Settings → Instructions for Claude) +│ ├── hooks/ # hook source definitions, when added; scoped as shared// +│ ├── mcp-config/ # MCP server/config definitions +│ ├── output-styles/ # reusable output-format or style guidance assets +│ ├── agents/ # agent/subagent definitions and related assets +│ ├── prompts/ # reusable prompts and prompt packs +│ └── plugins/ # plugin packaging and support material ├── docs/ # cross-cutting docs: harnesses/, reviews/, findings/, repo-layout.adoc… -├── eval/ # vendor-agnostic: INSTRUCTION-EVAL.md + the eval harness ├── scripts/ # sync-skills.ps1, … ├── .scratch/ # local-markdown issue tracker (PRDs + issues; committed) ├── .temp/ # gitignored transient local working files; keep only .gitkeep @@ -48,19 +53,38 @@ ai-lab/ └── .agents/skills/ # GENERATED Codex skill mirror — never edit; rebuild via sync-skills.ps1 .... -Folders are created *on demand* — a `///` directory is materialized -only when it has content, not pre-scaffolded empty. +Folders are created *on demand* — a `///` directory is materialized +only when it has content, not pre-scaffolded empty. The top-level artifact folders may contain a +small `README.md` before they have scoped content so the taxonomy is visible in fresh clones. == Per-artifact-type keying -* *skills* — usually `shared/` (they are methodology, not vendor-specific). Drop to `/` or - `//` only for a skill that genuinely is specific. Skills follow the +* *skills* — usually `ai-artifacts/skills/shared/` (they are methodology, not vendor-specific). Drop to + `ai-artifacts/skills//` or `ai-artifacts/skills///` only for a skill that genuinely is specific. + Skills follow the *capability contract* (detect shell/filesystem → full path; else conversational fallback) so one copy works across harnesses — avoid per-harness copies unless behavior truly diverges. Each skill's runtime instructions live in `SKILL.md`; catalog/provenance facts live in sibling `METADATA.md` using the repo's OKF convention. -* *instructions, settings, mcp, hooks, output-styles* — almost always `//`. A Claude - Code `settings.json` is meaningless to Codex; the claude-ai profile is not the Claude Code `CLAUDE.md`. +* *instructions* — `ai-artifacts/instructions///`. This folder holds repo copies of loaded + instruction surfaces: claude.ai profile instructions, Claude Code `CLAUDE.md`, project + instructions, global instruction files, Copilot instruction files, Codex instruction notes, and + equivalent base instruction documents for other agents. +* *hooks* — `ai-artifacts/hooks///` when hook definitions become source artifacts. Hook-related + skills still live under `ai-artifacts/skills/` unless the artifact itself is a hook config or script. +* *MCP config* — `ai-artifacts/mcp-config/shared/` or `ai-artifacts/mcp-config///` for MCP server manifests, + connection definitions, and adjacent support files. +* *output styles* — `ai-artifacts/output-styles/shared/` or `ai-artifacts/output-styles///` for reusable + formatting/style assets that shape model output. +* *agents* — `ai-artifacts/agents/shared/` or `ai-artifacts/agents///` for agent/subagent definitions and + related resources. +* *prompts* — `ai-artifacts/prompts/shared/` or `ai-artifacts/prompts///` for reusable prompts or prompt packs + that are not instruction surfaces. +* *plugins* — `ai-artifacts/plugins/shared/` or `ai-artifacts/plugins///` for plugin + packaging, manifests, and durable support files. +* *future artifact families* — when a new class of AI repo asset appears often enough to deserve a + stable home, add a top-level artifact-type folder with the same `shared/`, `/`, + `//` scoping rather than creating another catch-all bucket. * *temporary working files* — `.temp/` at repo root. Its contents are gitignored and may be deleted at any time. Promote only durable, redacted, intentionally committed support material to `.scratch//artifacts/`. @@ -72,10 +96,10 @@ deploys from here to there. * *Default.* `scripts/sync-skills.ps1` refreshes every generated skill mirror supported by this repo. Use `-Target Claude` or `-Target Codex` only when intentionally limiting the sync. -* *Claude Code.* The Claude target mirrors `shared/skills///` to the +* *Claude Code.* The Claude target mirrors `ai-artifacts/skills/shared///` to the Claude Code command tree (`.claude/commands//.md` + resources). The generated tree is gitignored; rebuild after editing a skill. -* *Codex.* The Codex target mirrors `shared/skills///` to Codex's +* *Codex.* The Codex target mirrors `ai-artifacts/skills/shared///` to Codex's repo-local skill tree (`.agents/skills/-/SKILL.md` + resources). The generated tree is gitignored; rebuild after editing a skill. * *Other harnesses (Copilot, ChatGPT, …)* — added incrementally through the cross-harness review loop @@ -90,12 +114,20 @@ with a harness-owned file format. The current concrete use is per-skill `METADAT == Not the same file (common confusion) -* `anthropic/claude-ai/instructions/profile.md` — the claude.ai account profile. Read by the Chat +* `ai-artifacts/instructions/anthropic/claude-ai/profile.md` — the claude.ai account profile. Read by the Chat harness only. Live version: Settings → Instructions for Claude (paste the whole file). * `~/.claude/CLAUDE.md` — Claude Code's global instructions. Different harness, different file. Expected to *overlap* with the profile (same behavioral preferences) but authored and loaded separately. - Repo copy, when added, lives at `anthropic/claude-code/instructions/CLAUDE.md`. + Repo copy, when added, lives at `ai-artifacts/instructions/anthropic/claude-code/CLAUDE.md`. * `AGENTS.md` (repo root) — the cross-harness shared layer read by Claude Code, Codex, and Copilot. The place for anything that should be identical across harnesses. +== Format choices + +* `docs/repo-layout.adoc` stays AsciiDoc because it is the canonical layout reference and benefits + from AsciiDoc tables, examples, and richer cross-reference structure. +* `AGENTS.md` stays Markdown because harnesses load it directly as an instruction file; portability + matters more than richer document syntax. +* Artifact READMEs stay Markdown unless they need AsciiDoc-only structure. + See `docs/harnesses/.md` for each harness's instruction surfaces and load model. diff --git a/docs/reviews/2026-07-04-fable-repo-review.md b/docs/reviews/2026-07-04-fable-repo-review.md index 27fd286..dd0a1a7 100644 --- a/docs/reviews/2026-07-04-fable-repo-review.md +++ b/docs/reviews/2026-07-04-fable-repo-review.md @@ -26,15 +26,15 @@ Also executed on the back of the review: `repo-scaffold` completed (root `AGENTS `ai-lab` is a personal **control plane for AI-assisted work across multiple harnesses**. Four pillars: -1. **Skills as source of truth** — `shared/skills/` holds portable, provenance-tracked skills +1. **Skills as source of truth** — `ai-artifacts/skills/shared/` holds portable, provenance-tracked skills (grouped by intent: coding/planning/session/setup), deployed into Claude Code's `.claude/commands/` via `scripts/sync-skills.ps1`. The mirror is a gitignored build artifact. 2. **Harness knowledge base** — `docs/harnesses/` documents each harness *from the inside* (self-description via `TEMPLATE.md`): instruction surfaces, precedence, storage tiers, capability limits. The "capability contract" (shell available → full path; else conversational fallback) is the portability mechanism that lets one skill copy serve all harnesses. -3. **Instructions as versioned artifacts** — `instructions/` + - `anthropic/claude-ai/instructions/profile.md` treat instruction files as repo-managed source +3. **Instructions as versioned artifacts** — `ai-artifacts/instructions/` + + `ai-artifacts/instructions/anthropic/claude-ai/profile.md` treat instruction files as repo-managed source with harness-specific live locations, organized by the specificity tiers in `docs/repo-layout.adoc` (`shared/` → `/` → `//`, most-specific wins). 4. **A self-hosted tracker** — `.scratch/` is a committed local-markdown issue tracker @@ -56,7 +56,7 @@ code is.* layout shift including link rewriting, has a non-clobbering `-IfMissing` bootstrap mode wired to a SessionStart hook, and the "never edit the mirror" rule is documented consistently. - **Provenance is taken seriously.** Per-skill `METADATA.md` files, an origin map in - `shared/skills/README.md`, explicit fork-vs-downstream decisions (the `setup-pre-commit` case), + `ai-artifacts/skills/shared/README.md`, explicit fork-vs-downstream decisions (the `setup-pre-commit` case), and license/attribution awareness in the import PRD. Exact upstream checkpoints belong in the individual skill metadata, not summary docs. - **Unusual epistemic maturity in the PRDs.** The `[RE-CONFIRM]` annotations on decisions inherited @@ -85,12 +85,12 @@ code is.* near-verbatim in two places — and the copies contradicted each other on the future CLAUDE.md path. The skills README lacked the `planning` group and used pre-migration paths. A resolved git-init blocker was still marked blocking. -- **The repo is public with hardening unranked.** *(largely fixed same day: THIRD-PARTY/ added, +- **The repo is public with hardening unranked.** *(largely fixed same day: attribution files were added, secret scanning + push protection + Dependabot enabled)* Vendored upstream skill copies were committed without the license attribution the import PRD itself required "before publishing" — publishing had already happened. The personal behavioral profile is public; that should be a conscious decision (still open). -- **Stray content dilutes the repo's identity.** `VSCode_Extsion/` (note the typo) is a shipped +- **Stray content dilutes the repo's identity.** `VSCode_Extension/` is a shipped VS Code extension at the root of an AI-configuration repo with no README tying it in; `mail-to-doc` rides in the tracker as a general software project. Neither is wrong — the scope widening is silent (open: `repo-scope-strays`). diff --git a/instructions/README.md b/instructions/README.md deleted file mode 100644 index 687c4de..0000000 --- a/instructions/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Instructions - -Harness-specific instruction files, keyed by harness (matching `docs/harnesses/`). Each file here is -the **repo copy for editing** — the live version lives in that harness's own location. These are -**not interchangeable**: different harnesses read different files, and overlap between them is -expected, not duplication to be merged. - -| File | Harness | Live location | Notes | -|---|---|---|---| -| `claude-ai/profile.md` | claude.ai (web / mobile / Desktop **Chat** tab) | Settings → Instructions for Claude | Account-wide profile — paste the whole file into Settings. Keep it domain-agnostic: PowerShell- or project-specific guidance belongs in the relevant claude.ai project, not here. | - -## Not the same file (common confusion) - -Owned by [docs/repo-layout.adoc](../docs/repo-layout.adoc) ("Not the same file" section) — not -restated here. Note: an earlier version of this section placed the future Claude Code `CLAUDE.md` -repo copy under `instructions/claude-code/`; the canonical location per the layout doc is -`anthropic/claude-code/instructions/CLAUDE.md`. - -See `docs/harnesses/.md` for each harness's instruction surfaces and load model. - -> Remaining instructions-taxonomy questions (which shared content is hoisted into `AGENTS.md`) are -> owned by `.scratch/incorporate-global-claude-setup/` (repo-scaffold itself is done). diff --git a/scripts/sync-skills.ps1 b/scripts/sync-skills.ps1 index 80c94d1..4b00b3d 100644 --- a/scripts/sync-skills.ps1 +++ b/scripts/sync-skills.ps1 @@ -4,7 +4,7 @@ Sync generated skill mirrors for supported local harnesses. .DESCRIPTION - shared/skills/ is the source of truth. This script refreshes generated harness mirrors: + ai-artifacts/skills/shared/ is the source of truth. This script refreshes generated harness mirrors: Claude Code -> .claude/commands/ Codex -> .agents/skills/ @@ -32,7 +32,7 @@ .EXAMPLE pwsh scripts/sync-skills.ps1 - Rebuild Claude Code and Codex skill mirrors from shared/skills. + Rebuild Claude Code and Codex skill mirrors from ai-artifacts/skills/shared. .EXAMPLE pwsh scripts/sync-skills.ps1 -Check @@ -66,8 +66,8 @@ if ($Scope -eq 'User' -and $Target -ne 'Claude') { throw "-Scope User applies only to Claude. Use -Target Claude -Scope User." } -$skillsRoot = Join-Path $RepoRoot 'shared\skills' -if (-not (Test-Path $skillsRoot)) { throw "shared/skills root not found at: $skillsRoot" } +$skillsRoot = Join-Path $RepoRoot 'ai-artifacts\skills\shared' +if (-not (Test-Path $skillsRoot)) { throw "ai-artifacts/skills/shared root not found at: $skillsRoot" } $script:LastSyncExitCode = 0 function Get-SkillDirectories { @@ -178,7 +178,7 @@ function ConvertTo-CodexSkill { $(if ($version) { "version: $(ConvertTo-YamlScalar $version)" }) '---' '' - "" + "" '' ) -join "`n" @@ -195,7 +195,7 @@ function Get-CodexCompatibilityWarnings { 'claude-path' = '\.claude|CLAUDE_PROJECT_DIR' 'claude-command' = '(^|\s)/[A-Za-z0-9_-]+:[A-Za-z0-9_-]+|\$ARGUMENTS|!command' 'codex-case' = '\.Codex' - 'old-skills-root' = 'skills//|(?/' } foreach ($entry in $patterns.GetEnumerator()) { @@ -335,7 +335,7 @@ function Sync-CodexSkills { } $sourceSkill = Join-Path $dir.FullName 'SKILL.md' - $sourceRelPath = "shared/skills/$group/$sourceName/SKILL.md" + $sourceRelPath = "ai-artifacts/skills/shared/$group/$sourceName/SKILL.md" $sourceDocument = Get-Content $sourceSkill -Raw $expectedSkill = ConvertTo-CodexSkill -SourceDocument $sourceDocument -CodexName $codexName -SourceRelPath $sourceRelPath foreach ($warning in Get-CodexCompatibilityWarnings -Document $sourceDocument -SkillName $codexName) { From 239e306a9da2bc450cb4a772c1d77d596362b376 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Jul 2026 21:58:21 +0000 Subject: [PATCH 02/12] Fix follow-up doc path references --- .scratch/public-repo-compliance/PRD.md | 7 ++++--- ai-artifacts/plugins/README.md | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.scratch/public-repo-compliance/PRD.md b/.scratch/public-repo-compliance/PRD.md index 2f17794..a88f21a 100644 --- a/.scratch/public-repo-compliance/PRD.md +++ b/.scratch/public-repo-compliance/PRD.md @@ -38,9 +38,10 @@ publishing" never ran: ## Progress (2026-07-04) - ✅ **Item 1 — attribution:** both upstreams verified MIT. - Attribution files were created in a dedicated third-party folder at the time, with license copies for vendored sources - (`mattpocock-skills.LICENSE`, `bradautomates-claude-video.LICENSE`) plus a notice map. Exact - upstream checkpoints for skills live in each skill's `METADATA.md`, not in summary docs. + Historical note: a dedicated third-party attribution folder existed when this item was first + completed, but those repo copies are no longer tracked after later cleanup. The remaining durable + provenance in this repo is each imported skill's `METADATA.md`; this entry should not be read as a + current inventory of committed license-copy files. - ✅ **Item 3 — free hardening wins:** secret scanning, push protection, and Dependabot alerts enabled via `gh api`. **Branch protection deliberately NOT enabled** — it would block the current direct-to-main workflow; decide it together with [[gated-work-prd-issue-approval]] (which wants a diff --git a/ai-artifacts/plugins/README.md b/ai-artifacts/plugins/README.md index 701449a..268ff0d 100644 --- a/ai-artifacts/plugins/README.md +++ b/ai-artifacts/plugins/README.md @@ -2,4 +2,5 @@ Plugin packaging, manifests, and durable support material. -Use `plugins/shared/`, `plugins//`, or `plugins///`. +Use `ai-artifacts/plugins/shared/`, `ai-artifacts/plugins//`, or +`ai-artifacts/plugins///`. From 87052085ed7ee0f6f41538af5645930b830dfb02 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Jul 2026 05:12:57 +0000 Subject: [PATCH 03/12] fix: resolve ruff linting errors in mail_to_adoc.py (E401, E702, F401) --- .../shared/documents/mail-to-adoc/scripts/mail_to_adoc.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/mail_to_adoc.py b/ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/mail_to_adoc.py index ce62ba9..02021e2 100644 --- a/ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/mail_to_adoc.py +++ b/ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/mail_to_adoc.py @@ -1014,10 +1014,11 @@ def _emit(msg: str) -> None: final_adoc = primary_adoc if counterpart.exists(): - import tempfile, shutil as _shutil + import tempfile tmp_fd, tmp_name = tempfile.mkstemp(suffix=f".{counterpart_ext[1:]}.adoc", prefix=stem + "_cmp_") - import os as _os; _os.close(tmp_fd) + import os as _os + _os.close(tmp_fd) temp_cmp = Path(tmp_name) _, cmp_adoc = _write_converted_mail(counterpart, temp_cmp) _report_adoc_comparison(temp_primary, primary_adoc, temp_cmp, cmp_adoc) @@ -1066,7 +1067,8 @@ def _emit(msg: str) -> None: elif overwrite: # copy2 cannot open a cloud-only file for 'wb'; use rename instead: # write to a sibling temp file, then rename() over the cloud-only target. - import os as _os, tempfile as _tf + import os as _os + import tempfile as _tf tmp_fd, tmp_name = _tf.mkstemp(dir=out_dir, prefix=".tmp_") _os.close(tmp_fd) _shutil2.copy2(str(temp_primary), tmp_name) From bb5fe2237cc80d90c8b7580e6617309e7c083f6c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Jul 2026 05:18:13 +0000 Subject: [PATCH 04/12] fix: format mail-to-adoc Python files --- .../mail-to-adoc/scripts/mail_to_adoc.py | 270 ++++++++++++------ .../mail-to-adoc/tests/test_mail_to_adoc.py | 4 +- 2 files changed, 191 insertions(+), 83 deletions(-) diff --git a/ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/mail_to_adoc.py b/ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/mail_to_adoc.py index 02021e2..5c37d96 100644 --- a/ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/mail_to_adoc.py +++ b/ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/mail_to_adoc.py @@ -19,17 +19,17 @@ try: _LOCAL_TZ = ZoneInfo("Europe/Berlin") except Exception: - raise SystemExit( - "Missing timezone data. Run: pip install tzdata" - ) + raise SystemExit("Missing timezone data. Run: pip install tzdata") _LOG_DIR = _PROJECT_ROOT / ".logs" #: Dennis's own email addresses — used to determine sent vs. received direction. #: [REDACTED for public repo — real values in the .temp/ originals] -_DENNIS_EMAILS: frozenset[str] = frozenset({ - "owner@example.com", - "owner.alt@example.com", -}) +_DENNIS_EMAILS: frozenset[str] = frozenset( + { + "owner@example.com", + "owner.alt@example.com", + } +) #: Maps known email addresses to short party names for filenames. #: [REDACTED for public repo — real values in the .temp/ originals] @@ -82,7 +82,6 @@ REPLY_HEADER_PATTERN = re.compile(r"^\*([^*]+):\*\s*", re.IGNORECASE) - def _log_warning(msg: str) -> None: """Print a warning and append it to .logs/mail_to_adoc.log.""" print(msg) @@ -90,6 +89,7 @@ def _log_warning(msg: str) -> None: _LOG_DIR.mkdir(parents=True, exist_ok=True) log_file = _LOG_DIR / "mail_to_adoc.log" from datetime import datetime + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") with open(log_file, "a", encoding="utf-8") as f: f.write(f"[{timestamp}] {msg}\n") @@ -97,11 +97,11 @@ def _log_warning(msg: str) -> None: pass # log write failed silently - def _decode_rfc2047(value: str) -> str: """Decode RFC 2047 encoded-words in email headers, e.g. =?utf-8?Q?...?=""" from email.header import decode_header, make_header - if not value or '=?' not in value: + + if not value or "=?" not in value: return value try: return str(make_header(decode_header(value))) @@ -112,10 +112,13 @@ def _decode_rfc2047(value: str) -> str: def _decode_qp_body(text: str) -> str: """Decode any residual quoted-printable sequences (=XX) in plain text.""" import quopri - if '=' not in text: + + if "=" not in text: return text try: - return quopri.decodestring(text.encode('ascii', errors='replace')).decode('utf-8', errors='replace') + return quopri.decodestring(text.encode("ascii", errors="replace")).decode( + "utf-8", errors="replace" + ) except Exception: return text @@ -142,17 +145,17 @@ def __init__(self): # List rendering state — one entry per nesting level to support nested ul/ol self.list_depth = 0 self.list_type_stack: list[str] = [] # 'ul' or 'ol' for each open list - self.list_item_stack: list[list[str]] = ( - [] - ) # text fragments collected for each open
  • + self.list_item_stack: list[ + list[str] + ] = [] # text fragments collected for each open
  • # Table rendering state self.table_depth = 0 self.current_cell: list[str] | None = None # None when no / is open self.current_row_cells: list[str] = [] # cells accumulated for the current - self.table_rows: list[list[str]] = ( - [] - ) # all rows accumulated for the current + self.table_rows: list[ + list[str] + ] = [] # all rows accumulated for the current
    # Hyperlink state — href is set on , used while writing inner text, cleared on self.open_link_href: str | None = None @@ -307,7 +310,6 @@ def normalize_body(body: str) -> str: return body - def _load_blocklist(script_dir: Path) -> set[str]: """Load MD5 checksums from attachment-blocklist.txt next to the skills folder.""" blocklist_path = script_dir.parent / "attachment-blocklist.txt" @@ -375,7 +377,6 @@ def _write_unique_file( return dest - def _clean_filename(subject: str) -> str: """Clean subject for filesystem use: 'AW: Foo' → 'AW_ Foo'. @@ -390,10 +391,10 @@ def _clean_filename(subject: str) -> str: _STEM_SEP = " — " # Bracket tags appended at the end of the filename stem (replaces the older emoji # markers ✉/✎𓂃/📎). Order in the stem: '{name} {direction}{[CC] if cc-only}{[+] if attachments}'. -_DIR_RECEIVED = "[FROM]" # mail Dennis received -_DIR_SENT = "[TO]" # mail Dennis sent -_CC_FLAG = "[CC]" # received mail where Dennis was only in Cc (not To) -_ATT_FLAG = "[+]" # mail has attachments +_DIR_RECEIVED = "[FROM]" # mail Dennis received +_DIR_SENT = "[TO]" # mail Dennis sent +_CC_FLAG = "[CC]" # received mail where Dennis was only in Cc (not To) +_ATT_FLAG = "[+]" # mail has attachments def _adoc_stem(mail_date, subject: str) -> str: @@ -425,7 +426,7 @@ def _meta_field(adoc: str, label: str) -> str: continue if m.group(1) == "a": # bulleted list continues on the following '* ' lines vals = [] - for nxt in lines[i + 1:]: + for nxt in lines[i + 1 :]: s = nxt.strip() if s.startswith("* "): vals.append(s[2:]) @@ -447,6 +448,7 @@ def _direction_tag( From/To header values extracted from the adoc metadata table; uses _DENNIS_EMAILS to detect sent mail and _EMAIL_NAME_MAP for party names. """ + def _extract_addr(field: str) -> str: m = re.search(r"<([^>]+)>", field) return (m.group(1) if m else field).strip().lower() @@ -469,8 +471,7 @@ def _name_for(field: str) -> str: from_addr = _extract_addr(from_v) if from_addr in _DENNIS_EMAILS: others = [ - a for a in re.findall(r"<([^>]+)>", to_v) - if a.lower() not in _DENNIS_EMAILS + a for a in re.findall(r"<([^>]+)>", to_v) if a.lower() not in _DENNIS_EMAILS ] _n = "MultipleRecipients" if len(others) > 1 else _name_for(to_v) return f"{_n} {_DIR_SENT}{_att}" @@ -637,7 +638,7 @@ def _addr_table_row(label: str, addr_str: str) -> str: return f"|{label} a|" + "\n".join(f"* {a}" for a in addrs) -_POSTPROCESS_LONG_LINE = 80 # lines longer than this are QP-wrapped paragraphs +_POSTPROCESS_LONG_LINE = 80 # lines longer than this are QP-wrapped paragraphs _POSTPROCESS_LIST_PREFIXES = ("* ", "** ", "*** ", ". ", ".. ", "... ", "- ") @@ -682,10 +683,22 @@ def _unwrap(m: re.Match) -> str: # thin rule in the rendered email. Insert AsciiDoc ''' before them. _REPLY_START = re.compile(r"^\*(Von|From|De|Van):\*", re.IGNORECASE) # All recognised reply-header field names (German + English). - _REPLY_HDR_KEYS = frozenset({ - "from", "sent", "to", "cc", "bcc", "subject", "date", - "von", "gesendet", "an", "betreff", "datum", - }) + _REPLY_HDR_KEYS = frozenset( + { + "from", + "sent", + "to", + "cc", + "bcc", + "subject", + "date", + "von", + "gesendet", + "an", + "betreff", + "datum", + } + ) def _is_reply_hdr(line: str) -> bool: m = re.match(r"^\*([^*]+):\*", line.strip(), re.IGNORECASE) @@ -712,21 +725,23 @@ def _is_reply_hdr(line: str) -> bool: # • Everything else that is followed by non-blank → add ' +' def _is_structural(line: str) -> bool: s = line.strip() - return (s.startswith("[") - or s in ("'''", "---", "***", "___") - or s.startswith("=")) + return ( + s.startswith("[") or s in ("'''", "---", "***", "___") or s.startswith("=") + ) lines = body.split("\n") out = [] for idx, line in enumerate(lines): raw = line.rstrip() next_line = lines[idx + 1] if idx + 1 < len(lines) else "" - if (raw - and next_line.strip() - and not raw.endswith(" +") - and not _is_structural(raw) - and not any(raw.lstrip().startswith(p) for p in _POSTPROCESS_LIST_PREFIXES) - and (_is_reply_hdr(raw) or len(raw) <= _POSTPROCESS_LONG_LINE)): + if ( + raw + and next_line.strip() + and not raw.endswith(" +") + and not _is_structural(raw) + and not any(raw.lstrip().startswith(p) for p in _POSTPROCESS_LIST_PREFIXES) + and (_is_reply_hdr(raw) or len(raw) <= _POSTPROCESS_LONG_LINE) + ): out.append(raw + " +") else: out.append(line) @@ -744,6 +759,7 @@ def _decode_thread_index(raw: str) -> str: Depth 0 = root message (no replies in chain yet). """ import base64 as _b64 + raw = raw.strip() if not raw: return "" @@ -759,18 +775,33 @@ def _decode_thread_index(raw: str) -> str: return raw - def _build_adoc( - subject: str, sender: str, to: str, cc: str, date: str, - attachment_links: list, body: str, - *, reply_to: str = "", bcc: str = "", importance: str = "", - sensitivity: str = "", categories: str = "", - thread_topic: str = "", thread_index: str = "", + subject: str, + sender: str, + to: str, + cc: str, + date: str, + attachment_links: list, + body: str, + *, + reply_to: str = "", + bcc: str = "", + importance: str = "", + sensitivity: str = "", + categories: str = "", + thread_topic: str = "", + thread_index: str = "", ) -> str: """Assemble the final AsciiDoc document from extracted mail fields.""" subject_escaped = subject.replace(": ", ": ") - lines = [f"= {subject_escaped}", "", "[%autowidth]", "|===", - f"|From |{sender}", f"|Sent |{date}"] + lines = [ + f"= {subject_escaped}", + "", + "[%autowidth]", + "|===", + f"|From |{sender}", + f"|Sent |{date}", + ] to_row = _addr_table_row("To ", to) if to_row: lines.append(to_row) @@ -799,7 +830,9 @@ def _build_adoc( lines += ["[NOTE]", "====", "*Attachments:*", ""] for orig_name, link_path, is_image in attachment_links: if is_image: - lines.append(f'image::{link_path}[{orig_name}, 120, link="{link_path}"]') + lines.append( + f'image::{link_path}[{orig_name}, 120, link="{link_path}"]' + ) else: lines.append(f"* link:{link_path}[{orig_name}]") lines += ["", "====", ""] @@ -831,7 +864,11 @@ def msg_to_adoc(msg_path: Path) -> str: # Prefer HTML (has charset) → fallback plain text html_bytes = msg.htmlBody if html_bytes: - html = _decode_msg_html(html_bytes) if isinstance(html_bytes, bytes) else html_bytes + html = ( + _decode_msg_html(html_bytes) + if isinstance(html_bytes, bytes) + else html_bytes + ) body = strip_html(html) else: body = msg.body or "" @@ -840,8 +877,11 @@ def msg_to_adoc(msg_path: Path) -> str: for att in msg.attachments: if getattr(att, "isInline", False): continue - name = re.sub(r"\s+", " ", - (att.longFilename or att.shortFilename or "").replace("\x00", "")).strip() + name = re.sub( + r"\s+", + " ", + (att.longFilename or att.shortFilename or "").replace("\x00", ""), + ).strip() if not name: continue raw_attachments.append((name, getattr(att, "data", None) or b"")) @@ -849,28 +889,57 @@ def msg_to_adoc(msg_path: Path) -> str: if mail_date is not None: # extract_msg may return a timezone-naive UTC datetime; convert to local. import datetime as _dt + if mail_date.tzinfo is None: mail_date = mail_date.replace(tzinfo=_dt.timezone.utc) mail_date = mail_date.astimezone(_LOCAL_TZ) reply_to = re.sub(r"[ \t]+", " ", getattr(msg, "reply_to", "") or "") bcc = re.sub(r"[ \t]+", " ", getattr(msg, "bcc", "") or "") - raw_imp = getattr(msg, "importanceText", None) or getattr(msg, "importance", None) - importance = (IMPORTANCE_LABELS.get(raw_imp, "") if isinstance(raw_imp, int) - else str(raw_imp).capitalize() if raw_imp else "") - raw_sens = getattr(msg, "sensitivityText", None) or getattr(msg, "sensitivity", None) - sensitivity = (SENSITIVITY_LABELS.get(raw_sens, "") if isinstance(raw_sens, int) - else str(raw_sens).capitalize() if raw_sens else "") + raw_imp = getattr(msg, "importanceText", None) or getattr( + msg, "importance", None + ) + importance = ( + IMPORTANCE_LABELS.get(raw_imp, "") + if isinstance(raw_imp, int) + else str(raw_imp).capitalize() + if raw_imp + else "" + ) + raw_sens = getattr(msg, "sensitivityText", None) or getattr( + msg, "sensitivity", None + ) + sensitivity = ( + SENSITIVITY_LABELS.get(raw_sens, "") + if isinstance(raw_sens, int) + else str(raw_sens).capitalize() + if raw_sens + else "" + ) categories = ", ".join(str(c) for c in (getattr(msg, "categories", None) or [])) thread_topic = (getattr(msg, "threadTopic", None) or "").strip() thread_index = (getattr(msg, "threadIndex", None) or "").strip() finally: msg.close() docs_dir = _PROJECT_ROOT / "01_Korrespondenz" / "Attachments" - attachment_links = process_attachments(raw_attachments, mail_date, docs_dir, msg_path) - return _build_adoc(subject, sender, to, cc, date, attachment_links, body, - reply_to=reply_to, bcc=bcc, importance=importance, - sensitivity=sensitivity, categories=categories, - thread_topic=thread_topic, thread_index=thread_index) + attachment_links = process_attachments( + raw_attachments, mail_date, docs_dir, msg_path + ) + return _build_adoc( + subject, + sender, + to, + cc, + date, + attachment_links, + body, + reply_to=reply_to, + bcc=bcc, + importance=importance, + sensitivity=sensitivity, + categories=categories, + thread_topic=thread_topic, + thread_index=thread_index, + ) def _unfold_header(value: str) -> str: @@ -915,16 +984,25 @@ def eml_to_adoc(eml_path: Path) -> str: payload = part.get_payload(decode=True) if isinstance(payload, (bytes, bytearray)) and payload: html_body = bytes(payload).decode( - part.get_content_charset() or "utf-8", errors="replace") + part.get_content_charset() or "utf-8", errors="replace" + ) elif content_type == "text/plain" and plain_body is None: payload = part.get_payload(decode=True) if isinstance(payload, (bytes, bytearray)) and payload: raw_plain = bytes(payload).decode( - part.get_content_charset() or "utf-8", errors="replace") + part.get_content_charset() or "utf-8", errors="replace" + ) # Some clients embed QP-encoded text without declaring CTE - plain_body = _decode_qp_body(raw_plain) if "=3D" in raw_plain or "=C3" in raw_plain else raw_plain - body = normalize_body(strip_html(html_body)) if html_body else ( - normalize_body(plain_body) if plain_body else "") + plain_body = ( + _decode_qp_body(raw_plain) + if "=3D" in raw_plain or "=C3" in raw_plain + else raw_plain + ) + body = ( + normalize_body(strip_html(html_body)) + if html_body + else (normalize_body(plain_body) if plain_body else "") + ) reply_to = _unfold_header(msg.get("Reply-To") or "") bcc = _unfold_header(msg.get("BCC") or msg.get("Bcc") or "") imp_hdr = msg.get("Importance") or msg.get("X-Priority") or "" @@ -934,11 +1012,25 @@ def eml_to_adoc(eml_path: Path) -> str: thread_topic = _decode_rfc2047(_unfold_header(msg.get("Thread-Topic") or "")) thread_index = _unfold_header(msg.get("Thread-Index") or "") docs_dir = _PROJECT_ROOT / "01_Korrespondenz" / "Attachments" - attachment_links = process_attachments(raw_attachments, mail_date, docs_dir, eml_path) - return _build_adoc(subject, sender, to, cc, date, attachment_links, body, - reply_to=reply_to, bcc=bcc, importance=importance, - sensitivity=sensitivity, categories=categories, - thread_topic=thread_topic, thread_index=thread_index) + attachment_links = process_attachments( + raw_attachments, mail_date, docs_dir, eml_path + ) + return _build_adoc( + subject, + sender, + to, + cc, + date, + attachment_links, + body, + reply_to=reply_to, + bcc=bcc, + importance=importance, + sensitivity=sensitivity, + categories=categories, + thread_topic=thread_topic, + thread_index=thread_index, + ) def main(): @@ -979,7 +1071,9 @@ def _emit(msg: str) -> None: _jr: dict = {} if not args: - print("Usage: python mail_to_adoc.py [--root ] [--overwrite] [--json] ") + print( + "Usage: python mail_to_adoc.py [--root ] [--overwrite] [--json] " + ) sys.exit(1) mail_path = Path(args[0]) @@ -1001,6 +1095,7 @@ def _emit(msg: str) -> None: # ── 2. Convert primary into .temp/ ──────────────────────────────────────── import tempfile as _tempfile + temp_dir = Path(_tempfile.gettempdir()) / "mail_to_adoc_temp" temp_dir.mkdir(parents=True, exist_ok=True) temp_primary = temp_dir / (stem + ".adoc") @@ -1015,9 +1110,12 @@ def _emit(msg: str) -> None: if counterpart.exists(): import tempfile - tmp_fd, tmp_name = tempfile.mkstemp(suffix=f".{counterpart_ext[1:]}.adoc", - prefix=stem + "_cmp_") + + tmp_fd, tmp_name = tempfile.mkstemp( + suffix=f".{counterpart_ext[1:]}.adoc", prefix=stem + "_cmp_" + ) import os as _os + _os.close(tmp_fd) temp_cmp = Path(tmp_name) _, cmp_adoc = _write_converted_mail(counterpart, temp_cmp) @@ -1039,7 +1137,7 @@ def _emit(msg: str) -> None: _fm = re.search(r"\|From\s*\|(.+)", final_adoc) _tm = re.search(r"\|To\s*\|(.+)", final_adoc) _from_v = _fm.group(1).strip() if _fm else "" - _to_v = _tm.group(1).strip() if _tm else "" + _to_v = _tm.group(1).strip() if _tm else "" _has_att = "*Attachments:*" in final_adoc # Dennis was only CC'd when he appears in the Cc field but not in To. _cc_only = _dennis_in(_meta_field(final_adoc, "CC")) and not _dennis_in( @@ -1051,6 +1149,7 @@ def _emit(msg: str) -> None: # ── 5. Copy from .temp/ to 01_Korrespondenz/{year}/{month}/ ───────────── import shutil as _shutil2 + out_dir = _PROJECT_ROOT / "01_Korrespondenz" / year / month out_dir.mkdir(parents=True, exist_ok=True) out_path = out_dir / (full_stem + ".adoc") @@ -1058,7 +1157,9 @@ def _emit(msg: str) -> None: if out_path.exists(): try: existing = out_path.read_text(encoding="utf-8") - identical = existing.replace("\r\n", "\n") == final_adoc.replace("\r\n", "\n") + identical = existing.replace("\r\n", "\n") == final_adoc.replace( + "\r\n", "\n" + ) except OSError: identical = False # cloud-only file — treat as needing overwrite if identical: @@ -1069,6 +1170,7 @@ def _emit(msg: str) -> None: # write to a sibling temp file, then rename() over the cloud-only target. import os as _os import tempfile as _tf + tmp_fd, tmp_name = _tf.mkstemp(dir=out_dir, prefix=".tmp_") _os.close(tmp_fd) _shutil2.copy2(str(temp_primary), tmp_name) @@ -1091,16 +1193,21 @@ def _emit(msg: str) -> None: # ── 6. Archive / rename source file ────────────────────────────────────── import filecmp as _filecmp + _parts = [p.lower() for p in mail_path.resolve().parts] _in_archive = any( - _parts[i] == "01_korrespondenz" and i + 1 < len(_parts) and _parts[i + 1] == "original" + _parts[i] == "01_korrespondenz" + and i + 1 < len(_parts) + and _parts[i + 1] == "original" for i in range(len(_parts) - 1) ) archive_ext = mail_path.suffix new_src_name = full_stem + archive_ext # match adoc stem if not _in_archive: - archive_dir = _PROJECT_ROOT / "01_Korrespondenz" / "Original" / f"{year}-{month}" + archive_dir = ( + _PROJECT_ROOT / "01_Korrespondenz" / "Original" / f"{year}-{month}" + ) archive_dir.mkdir(parents=True, exist_ok=True) dest = archive_dir / new_src_name @@ -1142,7 +1249,6 @@ def _emit(msg: str) -> None: _jr["archive_status"] = "skipped_rename" _emit(f"Umbenennung übersprungen (Ziel existiert): {dest.name}") - if json_mode: print(_json.dumps(_jr, ensure_ascii=True)) diff --git a/ai-artifacts/skills/shared/documents/mail-to-adoc/tests/test_mail_to_adoc.py b/ai-artifacts/skills/shared/documents/mail-to-adoc/tests/test_mail_to_adoc.py index ed7d452..8b27cf2 100644 --- a/ai-artifacts/skills/shared/documents/mail-to-adoc/tests/test_mail_to_adoc.py +++ b/ai-artifacts/skills/shared/documents/mail-to-adoc/tests/test_mail_to_adoc.py @@ -92,7 +92,9 @@ def test_process_attachments_skips_blocklist_and_dedupes_existing( msg_path=Path("mails/sample.eml"), ) - assert links == [("duplicate.pdf", "../../../docs/20260511_1452-evidence.pdf", False)] + assert links == [ + ("duplicate.pdf", "../../../docs/20260511_1452-evidence.pdf", False) + ] assert not (docs_dir / "20260511_1452-blocked.png").exists() From 77fbcc432e72ee7715c47c3a40412ee018d09ade Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Jul 2026 05:18:45 +0000 Subject: [PATCH 05/12] Fix markdownlint errors in PR-modified markdown files - Run markdownlint --fix to auto-fix MD049/MD047/MD022/MD032/MD034/MD029 - Fix MD025 (multiple H1): change # Skill Metadata and # Citations to ## in all METADATA.md files; change # Citations to ## in docs/okf-adoption.md - Fix MD025 in .markdownlint.json: set front_matter_title to empty string so files with YAML frontmatter title + body H1 don't trigger false positives - Fix MD040 (fenced code without language): add 'text' language to 6 code blocks - Fix MD036 (emphasis as heading): convert bold examples to #### headings in session/caveman/SKILL.md - Fix MD060 (table column alignment): reformat tables with proper padding, using display-width-aware alignment for emoji characters --- .markdownlint.json | 1 + .scratch/agents-md-folder-guides/PRD.md | 2 +- .scratch/check-updates-detection-scope/PRD.md | 2 +- .scratch/docs-integrity-pass/PRD.md | 4 +- .scratch/fetch-latest-claude-skills/PRD.md | 6 +- .../incorporate-global-claude-setup/PRD.md | 8 +- .scratch/mail-to-doc/PRD.md | 6 +- .scratch/profile-improvement/PRD.md | 1 + .scratch/sync-skills-drift-check/PRD.md | 4 +- .../anthropic/claude-ai/profile.md | 9 ++ .../skills/shared/coding/diagnose/METADATA.md | 6 +- .../skills/shared/coding/diagnose/SKILL.md | 4 +- .../improve-codebase-architecture/LANGUAGE.md | 8 +- .../improve-codebase-architecture/METADATA.md | 6 +- .../improve-codebase-architecture/SKILL.md | 6 +- .../shared/coding/prototype/METADATA.md | 6 +- .../skills/shared/coding/prototype/SKILL.md | 14 +- .../skills/shared/coding/tdd/METADATA.md | 6 +- .../skills/shared/coding/tdd/SKILL.md | 2 +- .../skills/shared/coding/tdd/test-doubles.md | 14 +- .../skills/shared/coding/zoom-out/METADATA.md | 6 +- .../shared/documents/mail-to-adoc/METADATA.md | 4 +- .../shared/documents/mail-to-adoc/SKILL.md | 6 +- .../documents/mail-to-adoc/docs/prd/PRD.md | 14 +- .../mail-to-adoc/docs/prd/PRD_EDGE_CASES.md | 54 +++---- .../shared/planning/scratch-plan/METADATA.md | 2 +- .../shared/planning/scratch-plan/SKILL.md | 18 +-- .../skills/shared/planning/scratch/LAYOUT.md | 3 +- .../shared/planning/scratch/METADATA.md | 2 +- .../skills/shared/planning/scratch/RANKING.md | 43 +++--- .../skills/shared/session/caveman/METADATA.md | 4 +- .../skills/shared/session/caveman/SKILL.md | 4 +- .../shared/session/grill-me/METADATA.md | 6 +- .../skills/shared/session/grill-me/SKILL.md | 11 +- .../skills/shared/session/handoff/METADATA.md | 6 +- .../skills/shared/session/recon/METADATA.md | 4 +- .../skills/shared/session/recon/SKILL.md | 12 +- .../shared/session/write-a-skill/EXAMPLES.md | 16 +-- .../shared/session/write-a-skill/METADATA.md | 6 +- .../shared/session/write-a-skill/SKILL.md | 2 +- .../setup/check-skill-updates/METADATA.md | 4 +- .../shared/setup/git-guardrails/METADATA.md | 6 +- .../setup/import-upstream-skill/METADATA.md | 2 +- .../shared/setup/setup-pre-commit/METADATA.md | 4 +- .../shared/setup/setup-pre-commit/SKILL.md | 2 +- docs/harnesses/claude-code.md | 2 +- docs/harnesses/copilot-cli.md | 133 +++++++++--------- docs/okf-adoption.md | 2 +- 48 files changed, 257 insertions(+), 236 deletions(-) diff --git a/.markdownlint.json b/.markdownlint.json index ac56578..007cfb0 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -2,6 +2,7 @@ "default": true, "MD013": false, + "MD025": { "front_matter_title": "" }, "MD033": false, "MD041": false, diff --git a/.scratch/agents-md-folder-guides/PRD.md b/.scratch/agents-md-folder-guides/PRD.md index 789e1c5..ec0fcfd 100644 --- a/.scratch/agents-md-folder-guides/PRD.md +++ b/.scratch/agents-md-folder-guides/PRD.md @@ -43,4 +43,4 @@ test: does the local guide measurably help an agent working in `.scratch/` vs. r `AGENTS.md` + `LAYOUT.md`? Evaluate it here before any broader rollout — the mass-generation decision stays gated. (Also settles the filename open-question for this instance: `AGENTS.md`, cross-harness.) -_Solution: Fill in (gated on the hypothesis analysis above)._ +*Solution: Fill in (gated on the hypothesis analysis above).* diff --git a/.scratch/check-updates-detection-scope/PRD.md b/.scratch/check-updates-detection-scope/PRD.md index 608581b..cff3326 100644 --- a/.scratch/check-updates-detection-scope/PRD.md +++ b/.scratch/check-updates-detection-scope/PRD.md @@ -34,4 +34,4 @@ principle **any** change inside the upstream skill directory — scripts include - Explicit call-out of any out-of-directory components that escape detection, and whether that's a real risk for current skills. -_Solution: Fill in._ +*Solution: Fill in.* diff --git a/.scratch/docs-integrity-pass/PRD.md b/.scratch/docs-integrity-pass/PRD.md index 31eb57a..5dd92f2 100644 --- a/.scratch/docs-integrity-pass/PRD.md +++ b/.scratch/docs-integrity-pass/PRD.md @@ -25,7 +25,7 @@ Facts are stated in more than one place and have started to drift. Concrete inst ## Solution -_Proposed — refine in triage:_ +*Proposed — refine in triage:* - Single-owner rule: each fact has one canonical location; other files link instead of restating (e.g. repo-layout.adoc owns "Not the same file"; instructions/README links to it). @@ -61,4 +61,4 @@ _Proposed — refine in triage:_ - The duplication cost is exactly what [[backlog-enhancements]] concept 1 (frontmatter → generated BACKLOG) eliminates for status fields; this scratch covers the prose-facts side. -- _Created by Claude Fable 5 via /planning:scratch._ +- *Created by Claude Fable 5 via /planning:scratch.* diff --git a/.scratch/fetch-latest-claude-skills/PRD.md b/.scratch/fetch-latest-claude-skills/PRD.md index 0608ea1..6a56658 100644 --- a/.scratch/fetch-latest-claude-skills/PRD.md +++ b/.scratch/fetch-latest-claude-skills/PRD.md @@ -14,8 +14,8 @@ process of staying current with whatever claude.ai publishes. ## Solution -_Fill in — identify the claude.ai skill source (URL, API, CLI mechanism), define the fetch -process, determine how to diff against local versions, and decide the integration/review step._ +*Fill in — identify the claude.ai skill source (URL, API, CLI mechanism), define the fetch +process, determine how to diff against local versions, and decide the integration/review step.* **Reuse the generic import skill for the integration step.** The "place it in the repo as a first-class, grouped, provenance-tracked skill and adapt it" half is already solved by @@ -39,4 +39,4 @@ adaptation. Don't reinvent the import mechanics. - Related: [import-upstream-skills](../import-upstream-skills/PRD.md) (mattpocock GitHub — different *source*, but its `/setup:import-upstream-skill` is the shared *integration* step to reuse) - Related: [check-updates-detection-scope](../check-updates-detection-scope/PRD.md) -- _Created by /planning:scratch._ +- *Created by /planning:scratch.* diff --git a/.scratch/incorporate-global-claude-setup/PRD.md b/.scratch/incorporate-global-claude-setup/PRD.md index 73d9257..f648330 100644 --- a/.scratch/incorporate-global-claude-setup/PRD.md +++ b/.scratch/incorporate-global-claude-setup/PRD.md @@ -26,11 +26,11 @@ instead of being maintained twice. ## What to bring in -| Source (`~/.claude/`) | Proposed repo location | Notes | -| --------------------------------------- | --------------------------------------------------- | -------------------------------------------------------------------- | -| `CLAUDE.md` (global instructions) | `ai-artifacts/instructions/claude-code/CLAUDE.md` | Harness-scoped; overlaps with the profile | +| Source (`~/.claude/`) | Proposed repo location | Notes | +| --------------------------------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------- | +| `CLAUDE.md` (global instructions) | `ai-artifacts/instructions/claude-code/CLAUDE.md` | Harness-scoped; overlaps with the profile | | `settings.json` (permissions/hooks/MCP) | `ai-artifacts/mcp-config/claude-code/settings.json` *(or similar)* | Decide: global settings vs project settings; secrets stay out | -| Global skills (`commands/`) | already handled by the skills work | Prior-version globals captured in `import-upstream-skills` artifacts | +| Global skills (`commands/`) | already handled by the skills work | Prior-version globals captured in `import-upstream-skills` artifacts | ## Pending content to fold in when hoisting `CLAUDE.md` diff --git a/.scratch/mail-to-doc/PRD.md b/.scratch/mail-to-doc/PRD.md index e3a38a1..f163157 100644 --- a/.scratch/mail-to-doc/PRD.md +++ b/.scratch/mail-to-doc/PRD.md @@ -32,20 +32,24 @@ Reference: Mail "Scratch 'mail-to-adoc'" from 28.06.2026 (see `artifacts/` for t ## Solution ### 1 — Split conversion logic + Extract the HTML→AsciiDoc transformation into a standalone skill or module (e.g. `html-to-adoc`), called by the main `mail-to-doc` orchestrator. This enables independent testing and future reuse. ### 2 — Rename & add Markdown target + Rename the skill from `mail-to-adoc` to `mail-to-doc`. Add a `--format` / `-f` flag (`adoc` | `md`; default `adoc`) so users can select the output format. ### 3 — Fix image attachments (links only) + When an attachment is an image, render it as a link rather than an image macro. Expected AsciiDoc: `* link:../../Attachments/-[]` Expected Markdown: `* [](../../Attachments/-)` ### 4 — Fix table row separator + Diagnose and remove the spurious `+` appended after each table row. Likely an off-by-one in the row-close logic of the HTML table→AsciiDoc table converter. @@ -97,4 +101,4 @@ in the row-close logic of the HTML table→AsciiDoc table converter. two path tests asserted the old `docs/` layout (fixed to `01_Korrespondenz/Attachments/`), and two referenced removed functions (`_add_hardbreaks_to_reply_headers`, `_auto_name`) now skipped pending a delete-vs-restore call. The zip-to-zip diff (`.temp/` originals) still needs the workstation. -- _Created by /planning:scratch._ +- *Created by /planning:scratch.* diff --git a/.scratch/profile-improvement/PRD.md b/.scratch/profile-improvement/PRD.md index 2a61dec..6754bd7 100644 --- a/.scratch/profile-improvement/PRD.md +++ b/.scratch/profile-improvement/PRD.md @@ -6,6 +6,7 @@ The live source-of-truth is Settings → Instructions for Claude (claude.ai / Cl The repo copy is `ai-artifacts/instructions/anthropic/claude-ai/profile.md` — edit here, paste there. Two versions are in `artifacts/`: + - `profile-current.md` — the version refined in a claude.ai session; live as of 2026-06 - `profile-prior.md` — the previous version; use as counterpart to identify what changed and whether any prior formulation was actually sharper diff --git a/.scratch/sync-skills-drift-check/PRD.md b/.scratch/sync-skills-drift-check/PRD.md index 8414454..693e006 100644 --- a/.scratch/sync-skills-drift-check/PRD.md +++ b/.scratch/sync-skills-drift-check/PRD.md @@ -13,7 +13,7 @@ session keeps invoking the old version until someone remembers to re-run the syn ## Solution -_Proposed — refine in triage:_ +*Proposed — refine in triage:* - Add a `-Check` mode to [scripts/sync-skills.ps1](../../scripts/sync-skills.ps1): compare source vs. mirror content (per-skill hash over SKILL.md + resources, with the same link rewriting @@ -44,4 +44,4 @@ _Proposed — refine in triage:_ - Failure mode identified in the Fable repo review (2026-07-04); the `-IfMissing` limitation is already documented in the script's own help — this scratch makes it observable. -- _Created by Claude Fable 5 via /planning:scratch._ +- *Created by Claude Fable 5 via /planning:scratch.* diff --git a/ai-artifacts/instructions/anthropic/claude-ai/profile.md b/ai-artifacts/instructions/anthropic/claude-ai/profile.md index 3e96f05..e856a33 100644 --- a/ai-artifacts/instructions/anthropic/claude-ai/profile.md +++ b/ai-artifacts/instructions/anthropic/claude-ai/profile.md @@ -4,6 +4,7 @@ Behavioral guidelines to reduce common LLM pitfalls. **Tradeoff:** Caution over speed. For trivial asks, scale down effort — not the rules. ## 1. Treat Input as Unverified + **Don't assume assertions are correct. Flag wrong claims explicitly — no softening, no silent fix.** - If the input is wrong, say so. Don't absorb guesses as fact. @@ -12,6 +13,7 @@ Behavioral guidelines to reduce common LLM pitfalls. - If given a hypothetical, engage with it but correct the premise: "Following your assumption the answer is …; that said, this is incorrect because …, so the correct answer should be …" ## 2. Response Hygiene + **Cut the preamble: no acknowledgment, no meta, no restating my point.** - Skip acknowledgment, agreement, and self-narration — not "You're right, that fails because …", just the corrected answer. @@ -20,6 +22,7 @@ Behavioral guidelines to reduce common LLM pitfalls. - Match format to payload: if a table/list carries it, don't wrap it in prose. ## 3. Think Before Answering + **Don't assume. Don't hide uncertainty. Surface tradeoffs.** When a request arrives: @@ -30,6 +33,7 @@ When a request arrives: - If something is unclear or missing, STOP — name what's needed and ask a focused question. ## 4. Precision Over Coverage + **Answer what was asked. No uninvited adjacent topics.** - Stay on the specific question — asked about X, answer X, not the related Y you could also cover. @@ -37,6 +41,7 @@ When a request arrives: - Worthwhile adjacent points go at the end, briefly — never lead with them. ## 5. Concise, Layered Answers + **Answer first. Minimum that fully answers. Depth on request.** - Lead with the direct answer. Add context only if required to act, or if asked why. @@ -45,6 +50,7 @@ When a request arrives: - Evaluate before sending: did I stop at the layer that answers? Did I shorten without losing meaning? ## 6. Honest & Direct + **Say what you actually think. Flag uncertainty clearly.** - If unsure, say so. Don't give a plausible-sounding but constructed answer. @@ -53,6 +59,7 @@ When a request arrives: - Separate facts, opinions, uncertainty. ## 7. How To Ask Clarifying Questions + **Never ask cold when options require domain knowledge.** - Default to acting on low-risk, easily-reversible changes; ask first only when a change is hard to verify or costly to undo. @@ -64,6 +71,7 @@ For non-obvious answers (skip for simple choices): 3. Then ask for confirmation — not open-ended. ## 8. General Code Guidance + **Minimum code, maximum rigor, no silent failure.** - Use the minimum code required to solve the problem. @@ -75,6 +83,7 @@ For non-obvious answers (skip for simple choices): - Separate output from logging: don't mix stdout and stderr unless explicitly required; use appropriate log levels / status streams. ## 9. Surface Conventions Are Mine + **Match source surface style; propose changes, don't silently apply them.** - Mirror punctuation, unicode, emoji, and formatting already in my message or the file being edited — e.g. if I write " - " and "...", don't swap them for "—" and "…". Don't normalize or "improve" it. diff --git a/ai-artifacts/skills/shared/coding/diagnose/METADATA.md b/ai-artifacts/skills/shared/coding/diagnose/METADATA.md index 64bcb75..4ab7fe4 100644 --- a/ai-artifacts/skills/shared/coding/diagnose/METADATA.md +++ b/ai-artifacts/skills/shared/coding/diagnose/METADATA.md @@ -10,7 +10,7 @@ upstream-path: skills/engineering/diagnose/SKILL.md upstream-commit: aaf2453fbdfe7a15c07f11d861224f34ab4b53cb --- -# Skill Metadata +## Skill Metadata - Runtime skill: [SKILL.md](SKILL.md) - Group: `coding` @@ -19,6 +19,6 @@ upstream-commit: aaf2453fbdfe7a15c07f11d861224f34ab4b53cb - Reconciled commit: `aaf2453fbdfe7a15c07f11d861224f34ab4b53cb` - Notes: Dual-mode capability contract; HITL loop ships pwsh (primary) and bash templates. -# Citations +## Citations -[1] [https://github.com/mattpocock/skills](https://github.com/mattpocock/skills) \ No newline at end of file +[1] [https://github.com/mattpocock/skills](https://github.com/mattpocock/skills) diff --git a/ai-artifacts/skills/shared/coding/diagnose/SKILL.md b/ai-artifacts/skills/shared/coding/diagnose/SKILL.md index 8f6de63..f99d897 100644 --- a/ai-artifacts/skills/shared/coding/diagnose/SKILL.md +++ b/ai-artifacts/skills/shared/coding/diagnose/SKILL.md @@ -43,7 +43,7 @@ Spend disproportionate effort here. **Be aggressive. Be creative. Refuse to give 7. **Property / fuzz loop.** If the bug is "sometimes wrong output", run 1000 random inputs and look for the failure mode. 8. **Bisection harness.** If the bug appeared between two known states (commit, dataset, version), automate "boot at state X, check, repeat" so you can `git bisect run` it. 9. **Differential loop.** Run the same input through old-version vs new-version (or two configs) and diff outputs. -10. **HITL script.** Last resort. If a human must click, drive _them_ with a structured loop — +10. **HITL script.** Last resort. If a human must click, drive *them* with a structured loop — `scripts/hitl-loop.template.ps1` (pwsh, primary) or `scripts/hitl-loop.template.sh` (bash). With a shell, generate and run it; without one, hand the user the same numbered steps in chat and collect their answers. Captured output feeds back to you. @@ -52,7 +52,7 @@ Build the right feedback loop, and the bug is 90% fixed. ### Iterate on the loop itself -Treat the loop as a product. Once you have _a_ loop, ask: +Treat the loop as a product. Once you have *a* loop, ask: - Can I make it faster? (Cache setup, skip unrelated init, narrow the test scope.) - Can I make the signal sharper? (Assert on the specific symptom, not "didn't crash".) diff --git a/ai-artifacts/skills/shared/coding/improve-codebase-architecture/LANGUAGE.md b/ai-artifacts/skills/shared/coding/improve-codebase-architecture/LANGUAGE.md index 530c276..de64f8b 100644 --- a/ai-artifacts/skills/shared/coding/improve-codebase-architecture/LANGUAGE.md +++ b/ai-artifacts/skills/shared/coding/improve-codebase-architecture/LANGUAGE.md @@ -6,11 +6,11 @@ Shared vocabulary for every suggestion this skill makes. Use these terms exactly **Module** Anything with an interface and an implementation. Deliberately scale-agnostic — applies equally to a function, class, package, or tier-spanning slice. -_Avoid_: unit, component, service. +*Avoid*: unit, component, service. **Interface** Everything a caller must know to use the module correctly. Includes the type signature, but also invariants, ordering constraints, error modes, required configuration, and performance characteristics. -_Avoid_: API, signature (too narrow — those refer only to the type-level surface). +*Avoid*: API, signature (too narrow — those refer only to the type-level surface). **Implementation** What's inside a module — its body of code. Distinct from **Adapter**: a thing can be a small adapter with a large implementation (a Postgres repo) or a large adapter with a small implementation (an in-memory fake). Reach for "adapter" when the seam is the topic; "implementation" otherwise. @@ -18,9 +18,9 @@ What's inside a module — its body of code. Distinct from **Adapter**: a thing **Depth** Leverage at the interface — the amount of behaviour a caller (or test) can exercise per unit of interface they have to learn. A module is **deep** when a large amount of behaviour sits behind a small interface. A module is **shallow** when the interface is nearly as complex as the implementation. -**Seam** _(from Michael Feathers)_ +**Seam** *(from Michael Feathers)* A place where you can alter behaviour without editing in that place. The *location* at which a module's interface lives. Choosing where to put the seam is its own design decision, distinct from what goes behind it. -_Avoid_: boundary (overloaded with DDD's bounded context). +*Avoid*: boundary (overloaded with DDD's bounded context). **Adapter** A concrete thing that satisfies an interface at a seam. Describes *role* (what slot it fills), not substance (what's inside). diff --git a/ai-artifacts/skills/shared/coding/improve-codebase-architecture/METADATA.md b/ai-artifacts/skills/shared/coding/improve-codebase-architecture/METADATA.md index beb1ab4..9c66f77 100644 --- a/ai-artifacts/skills/shared/coding/improve-codebase-architecture/METADATA.md +++ b/ai-artifacts/skills/shared/coding/improve-codebase-architecture/METADATA.md @@ -10,7 +10,7 @@ upstream-path: skills/engineering/improve-codebase-architecture/SKILL.md upstream-commit: aaf2453fbdfe7a15c07f11d861224f34ab4b53cb --- -# Skill Metadata +## Skill Metadata - Runtime skill: [SKILL.md](SKILL.md) - Group: `coding` @@ -19,6 +19,6 @@ upstream-commit: aaf2453fbdfe7a15c07f11d861224f34ab4b53cb - Reconciled commit: `aaf2453fbdfe7a15c07f11d861224f34ab4b53cb` - Notes: Dual-mode report workflow; grill-with-docs links repointed to session/grill-me. -# Citations +## Citations -[1] [https://github.com/mattpocock/skills](https://github.com/mattpocock/skills) \ No newline at end of file +[1] [https://github.com/mattpocock/skills](https://github.com/mattpocock/skills) diff --git a/ai-artifacts/skills/shared/coding/improve-codebase-architecture/SKILL.md b/ai-artifacts/skills/shared/coding/improve-codebase-architecture/SKILL.md index cb4d800..76fee74 100644 --- a/ai-artifacts/skills/shared/coding/improve-codebase-architecture/SKILL.md +++ b/ai-artifacts/skills/shared/coding/improve-codebase-architecture/SKILL.md @@ -39,7 +39,7 @@ Key principles (see [LANGUAGE.md](LANGUAGE.md) for the full list): - **The interface is the test surface.** - **One adapter = hypothetical seam. Two adapters = real seam.** -This skill is _informed_ by the project's domain model. The domain language gives names to good seams; ADRs record decisions the skill should not re-litigate. +This skill is *informed* by the project's domain model. The domain language gives names to good seams; ADRs record decisions the skill should not re-litigate. ## Process @@ -83,7 +83,7 @@ End the report with a **Top recommendation** section: which candidate you'd tack **Use CONTEXT.md vocabulary for the domain, and [LANGUAGE.md](LANGUAGE.md) vocabulary for the architecture.** If `CONTEXT.md` defines "Order," talk about "the Order intake module" — not "the FooBarHandler," and not "the Order service." -**ADR conflicts**: if a candidate contradicts an existing ADR, only surface it when the friction is real enough to warrant revisiting the ADR. Mark it clearly in the card (e.g. a warning callout: _"contradicts ADR-0007 — but worth reopening because…"_). Don't list every theoretical refactor an ADR forbids. +**ADR conflicts**: if a candidate contradicts an existing ADR, only surface it when the friction is real enough to warrant revisiting the ADR. Mark it clearly in the card (e.g. a warning callout: *"contradicts ADR-0007 — but worth reopening because…"*). Don't list every theoretical refactor an ADR forbids. See [HTML-REPORT.md](HTML-REPORT.md) for the full HTML scaffold, diagram patterns, and styling guidance. @@ -97,5 +97,5 @@ Side effects happen inline as decisions crystallize (with a filesystem: edit the - **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md` — same discipline as `/session:grill-me` (see [CONTEXT-FORMAT.md](../../session/grill-me/CONTEXT-FORMAT.md)). Create the file lazily if it doesn't exist. - **Sharpening a fuzzy term during the conversation?** Update `CONTEXT.md` right there. -- **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones. See [ADR-FORMAT.md](../../session/grill-me/ADR-FORMAT.md). +- **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: *"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"* Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones. See [ADR-FORMAT.md](../../session/grill-me/ADR-FORMAT.md). - **Want to explore alternative interfaces for the deepened module?** See [INTERFACE-DESIGN.md](INTERFACE-DESIGN.md). diff --git a/ai-artifacts/skills/shared/coding/prototype/METADATA.md b/ai-artifacts/skills/shared/coding/prototype/METADATA.md index 027d232..7ebbc1a 100644 --- a/ai-artifacts/skills/shared/coding/prototype/METADATA.md +++ b/ai-artifacts/skills/shared/coding/prototype/METADATA.md @@ -10,7 +10,7 @@ upstream-path: skills/engineering/prototype/SKILL.md upstream-commit: aaf2453fbdfe7a15c07f11d861224f34ab4b53cb --- -# Skill Metadata +## Skill Metadata - Runtime skill: [SKILL.md](SKILL.md) - Group: `coding` @@ -19,6 +19,6 @@ upstream-commit: aaf2453fbdfe7a15c07f11d861224f34ab4b53cb - Reconciled commit: `aaf2453fbdfe7a15c07f11d861224f34ab4b53cb` - Notes: Localized. -# Citations +## Citations -[1] [https://github.com/mattpocock/skills](https://github.com/mattpocock/skills) \ No newline at end of file +[1] [https://github.com/mattpocock/skills](https://github.com/mattpocock/skills) diff --git a/ai-artifacts/skills/shared/coding/prototype/SKILL.md b/ai-artifacts/skills/shared/coding/prototype/SKILL.md index a44af08..0569169 100644 --- a/ai-artifacts/skills/shared/coding/prototype/SKILL.md +++ b/ai-artifacts/skills/shared/coding/prototype/SKILL.md @@ -1,8 +1,8 @@ ---- -name: prototype -version: 1.0.0 -description: Build a throwaway prototype to flesh out a design before committing to it. Build a tiny interactive terminal app that drives a state model, data shape, command surface, or output format by hand — pushing it through cases that are hard to reason about on paper. Use when the user wants to prototype, sanity-check a data model or state machine, feel out an API/cmdlet surface or SQL schema, explore an idea, or says "prototype this", "let me play with it", "does this shape feel right". ---- +--- +name: prototype +version: 1.0.0 +description: Build a throwaway prototype to flesh out a design before committing to it. Build a tiny interactive terminal app that drives a state model, data shape, command surface, or output format by hand — pushing it through cases that are hard to reason about on paper. Use when the user wants to prototype, sanity-check a data model or state machine, feel out an API/cmdlet surface or SQL schema, explore an idea, or says "prototype this", "let me play with it", "does this shape feel right". +--- # Prototype @@ -22,7 +22,7 @@ Typical questions it answers: 1. **Throwaway from day one, and marked as such.** Put it next to the module it's prototyping for so context is obvious, but name it so a casual reader sees it's a prototype, not production (`proto-.ps1`, `Proto/`, `proto_.py`). 2. **One command to run.** Via the project's existing task runner or a single documented invocation — `pwsh ./proto-.ps1`, `dotnet run --project Proto`, `python proto_.py`. The user starts it without thinking. Don't add a new runtime or package manager just for the prototype. -3. **No persistence by default.** State lives in memory. Persistence is usually the thing being _checked_, not depended on. If the question is specifically about persistence, hit a scratch target with an obvious throwaway name (`tempdb` table `Proto_WipeMe`, a `proto-wipe-me.db`), never a real one. +3. **No persistence by default.** State lives in memory. Persistence is usually the thing being *checked*, not depended on. If the question is specifically about persistence, hit a scratch target with an obvious throwaway name (`tempdb` table `Proto_WipeMe`, a `proto-wipe-me.db`), never a real one. 4. **Skip the polish.** No tests, no abstractions, no error handling beyond what makes it runnable. The point is to learn fast and delete. 5. **Surface the state.** Re-render the full relevant state after every action so the user sees exactly what changed. 6. **Delete or absorb when done.** Once it has answered its question, fold the validated decision into the real code or delete it — don't leave it rotting in the repo. @@ -75,7 +75,7 @@ Add it to the project's existing task runner if there is one (`*.psd1`/build scr ### 6. Hand it over -Give the run command. The user drives it; the valuable moments are "wait, that shouldn't be possible" or "huh, I assumed X would differ" — those are bugs in the _idea_, which is the whole point. If they want new actions, add them. Prototypes evolve. +Give the run command. The user drives it; the valuable moments are "wait, that shouldn't be possible" or "huh, I assumed X would differ" — those are bugs in the *idea*, which is the whole point. If they want new actions, add them. Prototypes evolve. ### 7. Capture the answer diff --git a/ai-artifacts/skills/shared/coding/tdd/METADATA.md b/ai-artifacts/skills/shared/coding/tdd/METADATA.md index 1833bd0..b34ac8b 100644 --- a/ai-artifacts/skills/shared/coding/tdd/METADATA.md +++ b/ai-artifacts/skills/shared/coding/tdd/METADATA.md @@ -10,7 +10,7 @@ upstream-path: skills/engineering/tdd/SKILL.md upstream-commit: aaf2453fbdfe7a15c07f11d861224f34ab4b53cb --- -# Skill Metadata +## Skill Metadata - Runtime skill: [SKILL.md](SKILL.md) - Group: `coding` @@ -19,6 +19,6 @@ upstream-commit: aaf2453fbdfe7a15c07f11d861224f34ab4b53cb - Reconciled commit: `aaf2453fbdfe7a15c07f11d861224f34ab4b53cb` - Notes: Heavily localized: stack rules (PowerShell/SQL/Python/C#), reworked resources. -# Citations +## Citations -[1] [https://github.com/mattpocock/skills](https://github.com/mattpocock/skills) \ No newline at end of file +[1] [https://github.com/mattpocock/skills](https://github.com/mattpocock/skills) diff --git a/ai-artifacts/skills/shared/coding/tdd/SKILL.md b/ai-artifacts/skills/shared/coding/tdd/SKILL.md index 19d8bb9..c905f60 100644 --- a/ai-artifacts/skills/shared/coding/tdd/SKILL.md +++ b/ai-artifacts/skills/shared/coding/tdd/SKILL.md @@ -64,7 +64,7 @@ Refactor is non-optional — skipping it degrades TDD into "tests plus mess." ## Per-Cycle Checklist -``` +```text [ ] Test describes behavior, not implementation [ ] Test uses the public interface only [ ] Test would survive an internal refactor diff --git a/ai-artifacts/skills/shared/coding/tdd/test-doubles.md b/ai-artifacts/skills/shared/coding/tdd/test-doubles.md index cfe3c15..ff7d04b 100644 --- a/ai-artifacts/skills/shared/coding/tdd/test-doubles.md +++ b/ai-artifacts/skills/shared/coding/tdd/test-doubles.md @@ -45,13 +45,13 @@ ## Double Taxonomy (Meszaros) -| Kind | Purpose | -| --- | --- | -| Dummy | Fills a parameter slot; never used. | -| Stub | Returns canned answers. Use when the code needs specific inputs from a collaborator. | -| Spy | A stub that records how it was called. Use when verifying an interaction occurred. | -| Mock | A spy with pre-set expectations; fails if they are not met. Strictest. | -| Fake | A working but production-unsuitable implementation (in-memory DB/queue). | +| Kind | Purpose | +| ----- | ------------------------------------------------------------------------------------ | +| Dummy | Fills a parameter slot; never used. | +| Stub | Returns canned answers. Use when the code needs specific inputs from a collaborator. | +| Spy | A stub that records how it was called. Use when verifying an interaction occurred. | +| Mock | A spy with pre-set expectations; fails if they are not met. Strictest. | +| Fake | A working but production-unsuitable implementation (in-memory DB/queue). | Using a mock where a stub would do creates over-specified tests that fail on irrelevant changes. Using a stub where a mock is needed leaves interaction bugs diff --git a/ai-artifacts/skills/shared/coding/zoom-out/METADATA.md b/ai-artifacts/skills/shared/coding/zoom-out/METADATA.md index afb7a50..b328086 100644 --- a/ai-artifacts/skills/shared/coding/zoom-out/METADATA.md +++ b/ai-artifacts/skills/shared/coding/zoom-out/METADATA.md @@ -10,7 +10,7 @@ upstream-path: skills/engineering/zoom-out/SKILL.md upstream-commit: aaf2453fbdfe7a15c07f11d861224f34ab4b53cb --- -# Skill Metadata +## Skill Metadata - Runtime skill: [SKILL.md](SKILL.md) - Group: `coding` @@ -19,6 +19,6 @@ upstream-commit: aaf2453fbdfe7a15c07f11d861224f34ab4b53cb - Reconciled commit: `aaf2453fbdfe7a15c07f11d861224f34ab4b53cb` - Notes: Prompt-only; dual-mode note added. -# Citations +## Citations -[1] [https://github.com/mattpocock/skills](https://github.com/mattpocock/skills) \ No newline at end of file +[1] [https://github.com/mattpocock/skills](https://github.com/mattpocock/skills) diff --git a/ai-artifacts/skills/shared/documents/mail-to-adoc/METADATA.md b/ai-artifacts/skills/shared/documents/mail-to-adoc/METADATA.md index 8ea06b5..e3751e7 100644 --- a/ai-artifacts/skills/shared/documents/mail-to-adoc/METADATA.md +++ b/ai-artifacts/skills/shared/documents/mail-to-adoc/METADATA.md @@ -6,9 +6,9 @@ resource: ./SKILL.md tags: [documents, skill] --- -# Skill Metadata +## Skill Metadata - Runtime skill: [SKILL.md](SKILL.md) - Group: `documents` - Origin: local -- Notes: Local original; personal workflow tool. Beta maturity. \ No newline at end of file +- Notes: Local original; personal workflow tool. Beta maturity. diff --git a/ai-artifacts/skills/shared/documents/mail-to-adoc/SKILL.md b/ai-artifacts/skills/shared/documents/mail-to-adoc/SKILL.md index 2d3362b..dce356a 100644 --- a/ai-artifacts/skills/shared/documents/mail-to-adoc/SKILL.md +++ b/ai-artifacts/skills/shared/documents/mail-to-adoc/SKILL.md @@ -34,7 +34,7 @@ pip install extract-msg Add MD5 checksums of attachments to ignore (e.g. Outlook signature images) to: -``` +```text .github/skills/mail-to-adoc/attachment-blocklist.txt ``` @@ -53,8 +53,8 @@ python '.github/skills/mail-to-adoc/scripts/mail_to_adoc.py' ' python '.github/skills/mail-to-adoc/scripts/mail_to_adoc.py' '' '' ``` -3. Open the generated `.adoc` file to review. -4. The source file is automatically moved to `mails/converted/`. +1. Open the generated `.adoc` file to review. +2. The source file is automatically moved to `mails/converted/`. ## Notes diff --git a/ai-artifacts/skills/shared/documents/mail-to-adoc/docs/prd/PRD.md b/ai-artifacts/skills/shared/documents/mail-to-adoc/docs/prd/PRD.md index 14f3299..7b75c4f 100644 --- a/ai-artifacts/skills/shared/documents/mail-to-adoc/docs/prd/PRD.md +++ b/ai-artifacts/skills/shared/documents/mail-to-adoc/docs/prd/PRD.md @@ -33,18 +33,18 @@ Email formats vary by client and encoding. .msg and .eml representations of the - The tool shall optionally accept an explicit output .adoc path. - The tool shall return a clear error for unsupported extensions. -2. Output naming and placement +1. Output naming and placement - Without explicit output path, output filename shall be auto-derived from date + source stem. - If source is in a raw directory, output shall be placed in the parent directory. -3. Metadata extraction +1. Metadata extraction - The output shall include mail metadata table fields where available: - From, Sent, To, Reply-To, CC, BCC, Importance, Sensitivity, Categories. - Subject shall be used as document title and colon spacing shall be escaped for AsciiDoc safety. -4. Body extraction and normalization +1. Body extraction and normalization - .msg conversion shall prefer plain text body, then HTML fallback. - .eml conversion shall prefer HTML body, then plain text fallback. @@ -52,7 +52,7 @@ Email formats vary by client and encoding. .msg and .eml representations of the - Body normalization shall remove null bytes, NBSP artifacts, invisible unicode, repeated blank lines, and Outlook-specific noise blocks. - Quoted header blocks shall be prefixed with [%hardbreaks] for readability. -5. Attachment processing +1. Attachment processing - All non-inline attachments shall be inspected. - Attachments shall be deduplicated by MD5 checksum. @@ -61,14 +61,14 @@ Email formats vary by client and encoding. .msg and .eml representations of the - Image attachments shall be rendered as image:: entries with links. - Other attachments shall be rendered as link: entries. -6. Nested mail attachment processing +1. Nested mail attachment processing - Attached .msg/.eml files shall not be written to docs/. - Attached .msg/.eml files shall be written to mails/raw/. - Nested mail attachments shall be converted recursively to .adoc. - Parent document attachment list shall link to nested conversion output when available, otherwise to raw source. -7. Sibling parity conversion and comparison +1. Sibling parity conversion and comparison - If counterpart extension exists for the same stem (foo.msg + foo.eml), both shall be converted. - Tool shall compare generated outputs and print: @@ -76,7 +76,7 @@ Email formats vary by client and encoding. .msg and .eml representations of the - Similarity ratio when different - Limited unified diff preview -8. Source lifecycle +1. Source lifecycle - After successful conversion run, the primary source file shall be moved to a converted subfolder unless already inside converted. diff --git a/ai-artifacts/skills/shared/documents/mail-to-adoc/docs/prd/PRD_EDGE_CASES.md b/ai-artifacts/skills/shared/documents/mail-to-adoc/docs/prd/PRD_EDGE_CASES.md index 9b5a76c..cbc5e17 100644 --- a/ai-artifacts/skills/shared/documents/mail-to-adoc/docs/prd/PRD_EDGE_CASES.md +++ b/ai-artifacts/skills/shared/documents/mail-to-adoc/docs/prd/PRD_EDGE_CASES.md @@ -12,123 +12,123 @@ Define edge-case behavior required to faithfully recreate the skill. - Expected: fail fast with explicit file-not-found error. -2. Unsupported extension +1. Unsupported extension - Expected: fail fast and print supported extensions (.msg, .eml). -3. Explicit output path provided +1. Explicit output path provided - Expected: use explicit path and skip auto-name for primary file. -4. Source file already in converted folder +1. Source file already in converted folder - Expected: do not re-move source. ### Date and Naming -5. Missing or unparsable date +1. Missing or unparsable date - Expected: output name derived from stem only, no date prefix. -6. Source stem already includes date prefix +1. Source stem already includes date prefix - Expected: avoid double-prefixing by trimming known date forms. -7. Filename collisions in output dirs +1. Filename collisions in output dirs - Expected: append numeric suffix until unique. ### Metadata Normalization -8. Folded headers in .eml (RFC line wrapping) +1. Folded headers in .eml (RFC line wrapping) - Expected: unfolded values appear as single-line fields. -9. Address fields with mixed display-name/email forms +1. Address fields with mixed display-name/email forms - Expected: parsed, normalized, sorted representation. -10. More than 10 recipients in a field +1. More than 10 recipients in a field - Expected: comma-separated single-cell rendering. ### Body Extraction and Cleanup -11. .msg has empty plain text but valid HTML +1. .msg has empty plain text but valid HTML - Expected: HTML fallback used. -12. .eml has both HTML and plain text +1. .eml has both HTML and plain text - Expected: HTML chosen. -13. HTML contains style/script/comment/o:p/conditional blocks +1. HTML contains style/script/comment/o:p/conditional blocks - Expected: removed before conversion. -14. Body contains null bytes, NBSP, zero-width chars +1. Body contains null bytes, NBSP, zero-width chars - Expected: stripped or normalized. -15. Body has long runs of blank lines +1. Body has long runs of blank lines - Expected: collapsed to stable spacing. -16. Reply chain header block in body +1. Reply chain header block in body - Expected: [%hardbreaks] inserted before each header run. ### Attachment Handling -17. Inline image with filename +1. Inline image with filename - Expected: skipped as inline artifact. -18. Duplicate attachment content with different names +1. Duplicate attachment content with different names - Expected: dedupe by checksum; link to existing stored file. -19. Blocklisted attachment checksum +1. Blocklisted attachment checksum - Expected: skipped without writing file. -20. Attachment filename with directory traversal pattern +1. Attachment filename with directory traversal pattern - Expected: sanitized to basename before writing. -21. Image attachment +1. Image attachment - Expected: rendered with image:: thumbnail and click-through link. -22. Non-image attachment +1. Non-image attachment - Expected: rendered as link: entry. ### Nested Mail Attachments -23. Attached .msg or .eml present +1. Attached .msg or .eml present - Expected: write to mails/raw, convert nested mail, link to nested .adoc. -24. Nested conversion fails +1. Nested conversion fails - Expected: warning logged; parent links to raw nested source. -25. Duplicate nested mail attachment by checksum +1. Duplicate nested mail attachment by checksum - Expected: existing raw file reused. ### Sibling Pair Comparison -26. Input file has sibling counterpart extension +1. Input file has sibling counterpart extension - Expected: both converted; comparison status printed. -27. Outputs differ +1. Outputs differ - Expected: similarity percentage + bounded diff preview printed. -28. Outputs identical +1. Outputs identical - Expected: IDENTICAL status printed. diff --git a/ai-artifacts/skills/shared/planning/scratch-plan/METADATA.md b/ai-artifacts/skills/shared/planning/scratch-plan/METADATA.md index 4bcf452..3ac06e2 100644 --- a/ai-artifacts/skills/shared/planning/scratch-plan/METADATA.md +++ b/ai-artifacts/skills/shared/planning/scratch-plan/METADATA.md @@ -6,7 +6,7 @@ resource: ./SKILL.md tags: [planning, skill] --- -# Skill Metadata +## Skill Metadata - Runtime skill: [SKILL.md](SKILL.md) - Group: `planning` diff --git a/ai-artifacts/skills/shared/planning/scratch-plan/SKILL.md b/ai-artifacts/skills/shared/planning/scratch-plan/SKILL.md index 04bc500..be45816 100644 --- a/ai-artifacts/skills/shared/planning/scratch-plan/SKILL.md +++ b/ai-artifacts/skills/shared/planning/scratch-plan/SKILL.md @@ -41,15 +41,15 @@ If the user enters a non-standard value (e.g. "5h", "3 days"), round to the near midpoints between adjacent buckets (bucket hours: 4h=4, 1day=8, 2days=16, 1week=40, 2weeks=80, 1month=160, 2months=320): -| Input | Maps to | -|-------|---------| -| < 6h | 4h | -| 6h – < 12h | 1day | -| 12h – < 28h | 2days | -| 28h – < 60h | 1week | -| 60h – < 120h | 2weeks | -| 120h – < 240h | 1month | -| ≥ 240h | 2months | +| Input | Maps to | +| ------------- | ------- | +| < 6h | 4h | +| 6h – < 12h | 1day | +| 12h – < 28h | 2days | +| 28h – < 60h | 1week | +| 60h – < 120h | 2weeks | +| 120h – < 240h | 1month | +| ≥ 240h | 2months | Silently apply the rounding and confirm the mapped bucket to the user before continuing. diff --git a/ai-artifacts/skills/shared/planning/scratch/LAYOUT.md b/ai-artifacts/skills/shared/planning/scratch/LAYOUT.md index 9b45ccb..04bbd3e 100644 --- a/ai-artifacts/skills/shared/planning/scratch/LAYOUT.md +++ b/ai-artifacts/skills/shared/planning/scratch/LAYOUT.md @@ -7,7 +7,7 @@ rather than restating the conventions. Every feature lives in its own folder: `.scratch//` -``` +```text .scratch/ BACKLOG.md ← ranked index of all features (repo-level, one file) / @@ -49,6 +49,7 @@ Supporting *inputs* only — reference material that feeds the work, **never the Deliverables (a skill, script, report, or durable finding) live in their proper repo home; the rule and the deliverable→home table are in the folder guide `.scratch/AGENTS.md`. No required structure; common sub-folders: + - `artifacts//` — upstream source files (when `.temp/` is gitignored) - `artifacts/global-prior/` — prior installed versions to mine for local customizations diff --git a/ai-artifacts/skills/shared/planning/scratch/METADATA.md b/ai-artifacts/skills/shared/planning/scratch/METADATA.md index 045ce40..212cbeb 100644 --- a/ai-artifacts/skills/shared/planning/scratch/METADATA.md +++ b/ai-artifacts/skills/shared/planning/scratch/METADATA.md @@ -6,7 +6,7 @@ resource: ./SKILL.md tags: [planning, skill] --- -# Skill Metadata +## Skill Metadata - Runtime skill: [SKILL.md](SKILL.md) - Group: `planning` diff --git a/ai-artifacts/skills/shared/planning/scratch/RANKING.md b/ai-artifacts/skills/shared/planning/scratch/RANKING.md index a7ea4c2..1f1f822 100644 --- a/ai-artifacts/skills/shared/planning/scratch/RANKING.md +++ b/ai-artifacts/skills/shared/planning/scratch/RANKING.md @@ -2,25 +2,25 @@ ## Score formula -``` +```text Score = P × I × E ``` -| Axis | Value | Numeric | -|------|-------|---------| -| **P** (priority) | high | 3 | -| | medium | 2 | -| | low | 1 | -| **I** (importance) | high | 3 | -| | medium | 2 | -| | low | 1 | -| **E** (effort, **inverted** — less effort ranks higher) | 4h | 7 | -| | 1day | 6 | -| | 2days | 5 | -| | 1week | 4 | -| | 2weeks | 3 | -| | 1month | 2 | -| | 2months | 1 | +| Axis | Value | Numeric | +| ------------------------------------------------------- | ------- | ------- | +| **P** (priority) | high | 3 | +| | medium | 2 | +| | low | 1 | +| **I** (importance) | high | 3 | +| | medium | 2 | +| | low | 1 | +| **E** (effort, **inverted** — less effort ranks higher) | 4h | 7 | +| | 1day | 6 | +| | 2days | 5 | +| | 1week | 4 | +| | 2weeks | 3 | +| | 1month | 2 | +| | 2months | 1 | Score range: 3 (low / low / 2months) — 63 (high / high / 4h). Higher score = higher in the backlog. @@ -35,6 +35,7 @@ Higher score = higher in the backlog. ## Escalation rule (used by `/planning:scratch-plan`) When a feature's rank should be raised: + - If `importance < high` → raise importance one level. - If `importance = high` → raise priority one level instead (if `priority < high`). @@ -42,10 +43,10 @@ This prevents phantom "super-high" rankings by routing excess urgency into prior ## Example -| Feature | P | I | E | Score | -|---------|---|---|---|-------| -| auth-refactor | high (3) | high (3) | 1week (4) | 36 | -| fix-flaky-test | medium (2) | high (3) | 4h (7) | 42 | -| docs-overhaul | low (1) | medium (2) | 2months (1) | 2 | +| Feature | P | I | E | Score | +| -------------- | ---------- | ---------- | ----------- | ----- | +| auth-refactor | high (3) | high (3) | 1week (4) | 36 | +| fix-flaky-test | medium (2) | high (3) | 4h (7) | 42 | +| docs-overhaul | low (1) | medium (2) | 2months (1) | 2 | Ranked: fix-flaky-test (42) > auth-refactor (36) > docs-overhaul (2). diff --git a/ai-artifacts/skills/shared/session/caveman/METADATA.md b/ai-artifacts/skills/shared/session/caveman/METADATA.md index e66ffb0..6e5ef03 100644 --- a/ai-artifacts/skills/shared/session/caveman/METADATA.md +++ b/ai-artifacts/skills/shared/session/caveman/METADATA.md @@ -10,7 +10,7 @@ upstream-path: skills/productivity/caveman/SKILL.md upstream-commit: aaf2453fbdfe7a15c07f11d861224f34ab4b53cb --- -# Skill Metadata +## Skill Metadata - Runtime skill: [SKILL.md](SKILL.md) - Group: `session` @@ -19,6 +19,6 @@ upstream-commit: aaf2453fbdfe7a15c07f11d861224f34ab4b53cb - Reconciled commit: `aaf2453fbdfe7a15c07f11d861224f34ab4b53cb` - Notes: Minor edits. -# Citations +## Citations [1] [https://github.com/mattpocock/skills](https://github.com/mattpocock/skills) diff --git a/ai-artifacts/skills/shared/session/caveman/SKILL.md b/ai-artifacts/skills/shared/session/caveman/SKILL.md index 0f60481..c25525f 100644 --- a/ai-artifacts/skills/shared/session/caveman/SKILL.md +++ b/ai-artifacts/skills/shared/session/caveman/SKILL.md @@ -27,11 +27,11 @@ Yes: "Bug in auth middleware. Token expiry check use `<` not `<=`. Fix:" ### Examples -**"Why React component re-render?"** +#### "Why React component re-render?" > Inline obj prop -> new ref -> re-render. `useMemo`. -**"Explain database connection pooling."** +#### "Explain database connection pooling." > Pool = reuse DB conn. Skip handshake -> fast under load. diff --git a/ai-artifacts/skills/shared/session/grill-me/METADATA.md b/ai-artifacts/skills/shared/session/grill-me/METADATA.md index 4bd1118..8792ba3 100644 --- a/ai-artifacts/skills/shared/session/grill-me/METADATA.md +++ b/ai-artifacts/skills/shared/session/grill-me/METADATA.md @@ -10,7 +10,7 @@ upstream-path: skills/productivity/grill-me/SKILL.md upstream-commit: aaf2453fbdfe7a15c07f11d861224f34ab4b53cb --- -# Skill Metadata +## Skill Metadata - Runtime skill: [SKILL.md](SKILL.md) - Group: `session` @@ -19,6 +19,6 @@ upstream-commit: aaf2453fbdfe7a15c07f11d861224f34ab4b53cb - Reconciled commit: `aaf2453fbdfe7a15c07f11d861224f34ab4b53cb` - Notes: Absorbed engineering/grill-with-docs; upstream path tracks grill-me lineage only. -# Citations +## Citations -[1] [https://github.com/mattpocock/skills](https://github.com/mattpocock/skills) \ No newline at end of file +[1] [https://github.com/mattpocock/skills](https://github.com/mattpocock/skills) diff --git a/ai-artifacts/skills/shared/session/grill-me/SKILL.md b/ai-artifacts/skills/shared/session/grill-me/SKILL.md index 121fea2..3154f2b 100644 --- a/ai-artifacts/skills/shared/session/grill-me/SKILL.md +++ b/ai-artifacts/skills/shared/session/grill-me/SKILL.md @@ -1,8 +1,8 @@ ---- -name: grill-me -version: 1.0.0 -description: Interview the user relentlessly about a plan or design until reaching shared understanding, resolving each branch of the decision tree. Optionally challenges the plan against the project's existing language and drafts/revises documentation (CONTEXT.md, ADRs). Use when the user wants to stress-test a plan, get grilled on a design, says "grill me", or wants a plan checked against documented decisions and terminology. ---- +--- +name: grill-me +version: 1.0.0 +description: Interview the user relentlessly about a plan or design until reaching shared understanding, resolving each branch of the decision tree. Optionally challenges the plan against the project's existing language and drafts/revises documentation (CONTEXT.md, ADRs). Use when the user wants to stress-test a plan, get grilled on a design, says "grill me", or wants a plan checked against documented decisions and terminology. +--- @@ -17,6 +17,7 @@ Decide this once, at the start, before grilling — it governs the whole session **Docs mode is OFF by default.** Run a pure grilling session: no glossary, no CONTEXT.md, no ADRs, no documentation output. Don't mention them. **Engage docs mode only if** either is true: + 1. A `CONTEXT.md`, `CONTEXT-MAP.md`, or other project documentation is attached/uploaded, or 2. The user explicitly asks for documentation, a glossary, or ADRs, or signals it by phrasing such as "with docs", "using the docs", or "against the docs". diff --git a/ai-artifacts/skills/shared/session/handoff/METADATA.md b/ai-artifacts/skills/shared/session/handoff/METADATA.md index ca6e0c8..2e9e955 100644 --- a/ai-artifacts/skills/shared/session/handoff/METADATA.md +++ b/ai-artifacts/skills/shared/session/handoff/METADATA.md @@ -10,7 +10,7 @@ upstream-path: skills/productivity/handoff/SKILL.md upstream-commit: aaf2453fbdfe7a15c07f11d861224f34ab4b53cb --- -# Skill Metadata +## Skill Metadata - Runtime skill: [SKILL.md](SKILL.md) - Group: `session` @@ -19,6 +19,6 @@ upstream-commit: aaf2453fbdfe7a15c07f11d861224f34ab4b53cb - Reconciled commit: `aaf2453fbdfe7a15c07f11d861224f34ab4b53cb` - Notes: Minor edits. -# Citations +## Citations -[1] [https://github.com/mattpocock/skills](https://github.com/mattpocock/skills) \ No newline at end of file +[1] [https://github.com/mattpocock/skills](https://github.com/mattpocock/skills) diff --git a/ai-artifacts/skills/shared/session/recon/METADATA.md b/ai-artifacts/skills/shared/session/recon/METADATA.md index 87d3ef2..fb9fa5a 100644 --- a/ai-artifacts/skills/shared/session/recon/METADATA.md +++ b/ai-artifacts/skills/shared/session/recon/METADATA.md @@ -6,9 +6,9 @@ resource: ./SKILL.md tags: [session, skill] --- -# Skill Metadata +## Skill Metadata - Runtime skill: [SKILL.md](SKILL.md) - Group: `session` - Origin: local -- Notes: Local original. \ No newline at end of file +- Notes: Local original. diff --git a/ai-artifacts/skills/shared/session/recon/SKILL.md b/ai-artifacts/skills/shared/session/recon/SKILL.md index 3d034f9..ad47e81 100644 --- a/ai-artifacts/skills/shared/session/recon/SKILL.md +++ b/ai-artifacts/skills/shared/session/recon/SKILL.md @@ -1,8 +1,8 @@ ---- -name: recon -version: 1.0.0 -description: Before generating code (SQL, PowerShell, Bash, Python, or any stack) whose correctness depends on environment facts you cannot see, generate a small read-only probe script the user runs to report ground truth — schema and column names/types, server version and edition, installed modules or importable packages, available cmdlets, file paths, config values, existing object definitions. The user pastes the probe's output back, and you generate the real code against what actually exists instead of against assumptions. Use when a request targets an existing system and correctness hinges on identifiers, versions, or availability you'd otherwise guess at; when the user says "recon", "check my environment first", "confirm what you know", or "help me help you"; or any time you're about to recite a column name, version, module, or path from memory. ---- +--- +name: recon +version: 1.0.0 +description: Before generating code (SQL, PowerShell, Bash, Python, or any stack) whose correctness depends on environment facts you cannot see, generate a small read-only probe script the user runs to report ground truth — schema and column names/types, server version and edition, installed modules or importable packages, available cmdlets, file paths, config values, existing object definitions. The user pastes the probe's output back, and you generate the real code against what actually exists instead of against assumptions. Use when a request targets an existing system and correctness hinges on identifiers, versions, or availability you'd otherwise guess at; when the user says "recon", "check my environment first", "confirm what you know", or "help me help you"; or any time you're about to recite a column name, version, module, or path from memory. +--- # Recon @@ -54,6 +54,7 @@ A probe whose own failures are silent is worse than no probe — it grounds code This needs a **tri-state, not a bare `null`**: distinguish *present-with-value*, *present-but-empty* (0 rows is a real answer), *probed-but-errored*, and *not-probed*. `null` alone is overloaded. **Before emitting the probe, verify:** + - [ ] Every named dependency has a fact key — nothing silently skipped. - [ ] Each fact carries a status; failure is a recorded value, not an absence. - [ ] The flush runs even if a step throws (`finally`/`trap`); a genuine precondition failure (can't connect at all) still stops hard, outside the per-fact guard. @@ -61,6 +62,7 @@ This needs a **tri-state, not a bare `null`**: distinguish *present-with-value*, - [ ] Output is valid parseable JSON — depth sufficient (no silent truncation), quotes/newlines escaped, no BOM, no locale-formatted numbers (decimal comma breaks JSON). **Before generating code from the pasted-back output, verify:** + - [ ] All expected keys present; `attempted == captured`; no errored fact you're about to build on. - [ ] Values match expected shape — version matches a version pattern, counts are integers ≥ 0, expected arrays are arrays. - [ ] A value that contradicts your assumption is surfaced; a fact that errored or is empty gets re-probed or asked about — never generate over a hole. diff --git a/ai-artifacts/skills/shared/session/write-a-skill/EXAMPLES.md b/ai-artifacts/skills/shared/session/write-a-skill/EXAMPLES.md index f334496..54aeef0 100644 --- a/ai-artifacts/skills/shared/session/write-a-skill/EXAMPLES.md +++ b/ai-artifacts/skills/shared/session/write-a-skill/EXAMPLES.md @@ -10,7 +10,7 @@ A realistic minimal skill covering a focused domain. ### File layout -``` +```text csv-import/ ├── SKILL.md └── EXAMPLES.md @@ -57,10 +57,10 @@ description: Parse, validate, and import CSV files into SQL databases or datafra ### What makes this a good skill -| Property | Value | -|---|---| -| Description discriminates | Yes — "CSV", "import", "ingestion", "tabular data" | -| Update trigger covered | Not applicable (no update flow for this domain) | -| Line count | 38 — well under 100 | -| Concrete example | Checklist steps are actionable without guessing | -| Split decision | EXAMPLES.md added because SQL sample output would push SKILL.md over limit | +| Property | Value | +| ------------------------- | -------------------------------------------------------------------------- | +| Description discriminates | Yes — "CSV", "import", "ingestion", "tabular data" | +| Update trigger covered | Not applicable (no update flow for this domain) | +| Line count | 38 — well under 100 | +| Concrete example | Checklist steps are actionable without guessing | +| Split decision | EXAMPLES.md added because SQL sample output would push SKILL.md over limit | diff --git a/ai-artifacts/skills/shared/session/write-a-skill/METADATA.md b/ai-artifacts/skills/shared/session/write-a-skill/METADATA.md index aea051d..e55026f 100644 --- a/ai-artifacts/skills/shared/session/write-a-skill/METADATA.md +++ b/ai-artifacts/skills/shared/session/write-a-skill/METADATA.md @@ -10,7 +10,7 @@ upstream-path: skills/productivity/write-a-skill/SKILL.md upstream-commit: aaf2453fbdfe7a15c07f11d861224f34ab4b53cb --- -# Skill Metadata +## Skill Metadata - Runtime skill: [SKILL.md](SKILL.md) - Group: `session` @@ -19,6 +19,6 @@ upstream-commit: aaf2453fbdfe7a15c07f11d861224f34ab4b53cb - Reconciled commit: `aaf2453fbdfe7a15c07f11d861224f34ab4b53cb` - Notes: Localized. Known issue: links REFERENCE.md but ships EXAMPLES.md. -# Citations +## Citations -[1] [https://github.com/mattpocock/skills](https://github.com/mattpocock/skills) \ No newline at end of file +[1] [https://github.com/mattpocock/skills](https://github.com/mattpocock/skills) diff --git a/ai-artifacts/skills/shared/session/write-a-skill/SKILL.md b/ai-artifacts/skills/shared/session/write-a-skill/SKILL.md index 3f3f440..665beeb 100644 --- a/ai-artifacts/skills/shared/session/write-a-skill/SKILL.md +++ b/ai-artifacts/skills/shared/session/write-a-skill/SKILL.md @@ -26,7 +26,7 @@ description: Create, update, or improve agent skills with proper structure, prog ## Skill Structure -``` +```text skill-name/ ├── SKILL.md # Main instructions (required) ├── REFERENCE.md # Detailed docs (if needed) diff --git a/ai-artifacts/skills/shared/setup/check-skill-updates/METADATA.md b/ai-artifacts/skills/shared/setup/check-skill-updates/METADATA.md index 132bf4d..40be5e7 100644 --- a/ai-artifacts/skills/shared/setup/check-skill-updates/METADATA.md +++ b/ai-artifacts/skills/shared/setup/check-skill-updates/METADATA.md @@ -6,9 +6,9 @@ resource: ./SKILL.md tags: [setup, skill] --- -# Skill Metadata +## Skill Metadata - Runtime skill: [SKILL.md](SKILL.md) - Group: `setup` - Origin: local -- Notes: Local original; reads skill METADATA.md files for upstream drift checks. \ No newline at end of file +- Notes: Local original; reads skill METADATA.md files for upstream drift checks. diff --git a/ai-artifacts/skills/shared/setup/git-guardrails/METADATA.md b/ai-artifacts/skills/shared/setup/git-guardrails/METADATA.md index 4309ec3..6ccb954 100644 --- a/ai-artifacts/skills/shared/setup/git-guardrails/METADATA.md +++ b/ai-artifacts/skills/shared/setup/git-guardrails/METADATA.md @@ -10,7 +10,7 @@ upstream-path: skills/misc/git-guardrails-claude-code/SKILL.md upstream-commit: 62f43a18177be6ec82da242e59ffbc490a4c22ea --- -# Skill Metadata +## Skill Metadata - Runtime skill: [SKILL.md](SKILL.md) - Group: `setup` @@ -19,6 +19,6 @@ upstream-commit: 62f43a18177be6ec82da242e59ffbc490a4c22ea - Reconciled commit: `62f43a18177be6ec82da242e59ffbc490a4c22ea` - Notes: Localized from the global-prior; Claude-Code-hook skill, not generally useful in chat. -# Citations +## Citations -[1] [https://github.com/mattpocock/skills](https://github.com/mattpocock/skills) \ No newline at end of file +[1] [https://github.com/mattpocock/skills](https://github.com/mattpocock/skills) diff --git a/ai-artifacts/skills/shared/setup/import-upstream-skill/METADATA.md b/ai-artifacts/skills/shared/setup/import-upstream-skill/METADATA.md index c6087ee..0962236 100644 --- a/ai-artifacts/skills/shared/setup/import-upstream-skill/METADATA.md +++ b/ai-artifacts/skills/shared/setup/import-upstream-skill/METADATA.md @@ -6,7 +6,7 @@ resource: ./SKILL.md tags: [setup, skill] --- -# Skill Metadata +## Skill Metadata - Runtime skill: [SKILL.md](SKILL.md) - Group: `setup` diff --git a/ai-artifacts/skills/shared/setup/setup-pre-commit/METADATA.md b/ai-artifacts/skills/shared/setup/setup-pre-commit/METADATA.md index 0fd87bd..44eb0f8 100644 --- a/ai-artifacts/skills/shared/setup/setup-pre-commit/METADATA.md +++ b/ai-artifacts/skills/shared/setup/setup-pre-commit/METADATA.md @@ -6,9 +6,9 @@ resource: ./SKILL.md tags: [setup, skill] --- -# Skill Metadata +## Skill Metadata - Runtime skill: [SKILL.md](SKILL.md) - Group: `setup` - Origin: local -- Notes: Local fork of mattpocock skills/misc/setup-pre-commit. Diverged entirely; no upstream commit is tracked. \ No newline at end of file +- Notes: Local fork of mattpocock skills/misc/setup-pre-commit. Diverged entirely; no upstream commit is tracked. diff --git a/ai-artifacts/skills/shared/setup/setup-pre-commit/SKILL.md b/ai-artifacts/skills/shared/setup/setup-pre-commit/SKILL.md index 6fc6a14..a12e7ef 100644 --- a/ai-artifacts/skills/shared/setup/setup-pre-commit/SKILL.md +++ b/ai-artifacts/skills/shared/setup/setup-pre-commit/SKILL.md @@ -201,6 +201,6 @@ This will run through the new hooks — a good smoke test. - `pre-commit` caches environments; first run per hook is slow, subsequent runs are fast - `sqlfluff --fix` is aggressive — review its changes before committing -- For vale, pick a style pack matching your writing style (`Microsoft`, `Google`, `Vale`) from https://vale.sh/hub/ +- For vale, pick a style pack matching your writing style (`Microsoft`, `Google`, `Vale`) from - To skip hooks temporarily: `git commit --no-verify` (use sparingly) - To update all hooks to latest versions: `pre-commit autoupdate` diff --git a/docs/harnesses/claude-code.md b/docs/harnesses/claude-code.md index 6e801a9..df21248 100644 --- a/docs/harnesses/claude-code.md +++ b/docs/harnesses/claude-code.md @@ -56,7 +56,7 @@ Within instruction files, more-specific path wins. Managed > project > user for | Subagents | **Native** — `agents/*.md` | | Hooks | **Native** — `settings.json` (`PreToolUse`, `PostToolUse`, `SessionStart`, etc.) | | MCP servers | **Native** — `.mcp.json` (project), `~/.claude.json` (global), Desktop `claude_desktop_config.json` | -| Output styles | **Native** — `ai-artifacts/output-styles/` (referenced in settings) | +| Output styles | **Native** — `ai-artifacts/output-styles/` (referenced in settings) | | Settings / permissions | **Native** — `settings.json` / `settings.local.json` | | Plugins / bundles | **Native** — `.claude-plugin/plugin.json` wrapping skills/agents/hooks/mcp | diff --git a/docs/harnesses/copilot-cli.md b/docs/harnesses/copilot-cli.md index 5ab5271..99cc8b9 100644 --- a/docs/harnesses/copilot-cli.md +++ b/docs/harnesses/copilot-cli.md @@ -41,34 +41,34 @@ Precedence: platform rules > repo instructions > user messages. The `custom_inst ## 3. Disk locations -| Tier | Windows | macOS / Linux | -| ----------------------- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------- | -| Repo main checkout | `%USERPROFILE%\.copilot\repos\\` | `~/.copilot/repos//` | -| Session worktree | `%USERPROFILE%\.copilot\repos\copilot-worktrees\\\` | `~/.copilot/repos/copilot-worktrees///` | -| Session state folder | `%USERPROFILE%\.copilot\session-state\\` | `~/.copilot/session-state//` | -| Session artifacts | `%USERPROFILE%\.copilot\session-state\\files\` | `~/.copilot/session-state//files/` | -| Repo instructions | `\.github\copilot-instructions.md` | `/.github/copilot-instructions.md` | -| Cross-harness contract | `\AGENTS.md` | `/AGENTS.md` | -| Project extensions | `\.github\extensions\` | `/.github/extensions/` | -| Project prompt files | `\.github\prompts\*.prompt.md` | `/.github/prompts/*.prompt.md` | -| User-scope extensions | `?` (not exposed in-session) | `?` | -| App settings / config | managed by Copilot CLI app — path `?` | `?` | +| Tier | Windows | macOS / Linux | +| ---------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------- | +| Repo main checkout | `%USERPROFILE%\.copilot\repos\\` | `~/.copilot/repos//` | +| Session worktree | `%USERPROFILE%\.copilot\repos\copilot-worktrees\\\` | `~/.copilot/repos/copilot-worktrees///` | +| Session state folder | `%USERPROFILE%\.copilot\session-state\\` | `~/.copilot/session-state//` | +| Session artifacts | `%USERPROFILE%\.copilot\session-state\\files\` | `~/.copilot/session-state//files/` | +| Repo instructions | `\.github\copilot-instructions.md` | `/.github/copilot-instructions.md` | +| Cross-harness contract | `\AGENTS.md` | `/AGENTS.md` | +| Project extensions | `\.github\extensions\` | `/.github/extensions/` | +| Project prompt files | `\.github\prompts\*.prompt.md` | `/.github/prompts/*.prompt.md` | +| User-scope extensions | `?` (not exposed in-session) | `?` | +| App settings / config | managed by Copilot CLI app — path `?` | `?` | ## 4. Artifact mapping -| Artifact type | Support | Notes | -| ------------------------------ | ----------------------- | ---------------------------------------------------------------------------------------- | -| Instruction docs | **Native** | `.github/copilot-instructions.md` + `AGENTS.md`; auto-loaded every session | -| Skills / slash commands | **Emulated** | No dedicated skill format; represented via extensions (tools) or prompt files | -| Subagents | **Native** | `task` tool spawns typed sub-agents (explore, task, general-purpose, code-review, etc.) | -| Hooks | **Partial / Emulated** | No declarative hook config; lifecycle events handled by extensions + app runtime | -| MCP servers | **Native** | Configured via workspace or user settings; protocol is cross-vendor | -| Canvas panels | **Native** (unique) | Interactive side panels (editor, browser, terminal); opened by agent or user | -| Output styles | **Emulated** | Style contracts in instruction files; no native style primitive | -| Settings / permissions | **Native** | App settings + extension `allow/deny` lists | -| Plugins / bundles | **Native** (extensions) | `.github/extensions/*.{js,ts,yml}` contribute tools / canvases to the session | -| Scheduled workflows | **Native** | Built-in workflow scheduler (manual / hourly / daily / weekly / CRON) | -| Session store / history | **Native** | Per-session SQLite (`sql` tool); cross-session DuckDB history (`session_store_sql` tool) | +| Artifact type | Support | Notes | +| ----------------------- | ----------------------- | ---------------------------------------------------------------------------------------- | +| Instruction docs | **Native** | `.github/copilot-instructions.md` + `AGENTS.md`; auto-loaded every session | +| Skills / slash commands | **Emulated** | No dedicated skill format; represented via extensions (tools) or prompt files | +| Subagents | **Native** | `task` tool spawns typed sub-agents (explore, task, general-purpose, code-review, etc.) | +| Hooks | **Partial / Emulated** | No declarative hook config; lifecycle events handled by extensions + app runtime | +| MCP servers | **Native** | Configured via workspace or user settings; protocol is cross-vendor | +| Canvas panels | **Native** (unique) | Interactive side panels (editor, browser, terminal); opened by agent or user | +| Output styles | **Emulated** | Style contracts in instruction files; no native style primitive | +| Settings / permissions | **Native** | App settings + extension `allow/deny` lists | +| Plugins / bundles | **Native** (extensions) | `.github/extensions/*.{js,ts,yml}` contribute tools / canvases to the session | +| Scheduled workflows | **Native** | Built-in workflow scheduler (manual / hourly / daily / weekly / CRON) | +| Session store / history | **Native** | Per-session SQLite (`sql` tool); cross-session DuckDB history (`session_store_sql` tool) | ## 5. Cross-compatibility @@ -93,15 +93,15 @@ Precedence: platform rules > repo instructions > user messages. The `custom_inst ## 7. Activation + load model -| Surface | Load model | -| --------------------------------- | ------------------------------------------------------------------------------------- | -| `.github/copilot-instructions.md` | Auto, every session turn (injected as `custom_instruction`) | -| `AGENTS.md` | Auto, every session turn (base instruction layer) | -| Extensions | Loaded at app / session start; tools contributed are available for the full session | -| MCP servers | On-demand tool calls (not loaded into context text) | -| Canvas | Opened explicitly by agent (`open_canvas`) or user; persists as a side panel | -| Scheduled workflows | Time-triggered or manual; spawn a new session with a configured prompt and mode | -| Sub-agents (task tool) | Spawned on demand; background agents notify on completion | +| Surface | Load model | +| --------------------------------- | ---------------------------------------------------------------------------------------- | +| `.github/copilot-instructions.md` | Auto, every session turn (injected as `custom_instruction`) | +| `AGENTS.md` | Auto, every session turn (base instruction layer) | +| Extensions | Loaded at app / session start; tools contributed are available for the full session | +| MCP servers | On-demand tool calls (not loaded into context text) | +| Canvas | Opened explicitly by agent (`open_canvas`) or user; persists as a side panel | +| Scheduled workflows | Time-triggered or manual; spawn a new session with a configured prompt and mode | +| Sub-agents (task tool) | Spawned on demand; background agents notify on completion | | Prompt files (`.prompt.md`) | User-invoked (`/filename` pattern from VS Code Copilot; not natively slash-invoked here) | Instructions are **not** cached across sessions — each session starts fresh from disk. @@ -149,18 +149,18 @@ window; keep `.github/copilot-instructions.md` and `AGENTS.md` concise. - **Harness/App:** Copilot CLI 1.0.69-0 - **Session model:** Claude Sonnet 4.6 (multi-model; observed model at time of writing) -| Section | Confidence | Why | -| ------------------------------------ | ---------- | -------------------------------------------------------------------------------------------------------- | -| 1. Instruction surfaces + precedence | high | Load model visible in-session via `custom_instruction` injection; additive layer confirmed | -| 2. Storage split | high | Session state and worktree paths are directly observable in the session context | -| 3. Disk locations | medium | Worktree and session paths confirmed from live context; user-scope extension path is `?` | -| 4. Artifact mapping | high | All tool categories exercised in this repo's sessions | -| 5. Cross-compatibility | high | `AGENTS.md` and `.github/copilot-instructions.md` auto-load confirmed in-session | -| 6. Composition mechanics | medium | Additive layering observed; exact precedence for instruction-level conflicts not systematically tested | -| 7. Activation + load model | high | Extension, MCP, canvas, and workflow load patterns all observed in-session | -| 8. Validation | high | Commands verified in live sessions (`extensions_manage`, `list_workflows`, etc.) | -| 9. Security / secrets boundary | high | Follows repo-public constraint and standard env-var injection patterns | -| 10. Capability limits | medium | Known absences confirmed; some edge cases (cloud sessions, extension API limits) not fully tested | +| Section | Confidence | Why | +| ------------------------------------ | ---------- | ------------------------------------------------------------------------------------------------------ | +| 1. Instruction surfaces + precedence | high | Load model visible in-session via `custom_instruction` injection; additive layer confirmed | +| 2. Storage split | high | Session state and worktree paths are directly observable in the session context | +| 3. Disk locations | medium | Worktree and session paths confirmed from live context; user-scope extension path is `?` | +| 4. Artifact mapping | high | All tool categories exercised in this repo's sessions | +| 5. Cross-compatibility | high | `AGENTS.md` and `.github/copilot-instructions.md` auto-load confirmed in-session | +| 6. Composition mechanics | medium | Additive layering observed; exact precedence for instruction-level conflicts not systematically tested | +| 7. Activation + load model | high | Extension, MCP, canvas, and workflow load patterns all observed in-session | +| 8. Validation | high | Commands verified in live sessions (`extensions_manage`, `list_workflows`, etc.) | +| 9. Security / secrets boundary | high | Follows repo-public constraint and standard env-var injection patterns | +| 10. Capability limits | medium | Known absences confirmed; some edge cases (cloud sessions, extension API limits) not fully tested | ## 12. Command + argument mapping @@ -184,16 +184,16 @@ Fallback when no native command invocation exists: ## 13. Capability contract -| Capability | Contract | Notes | -| --------------------------------- | ----------- | ------------------------------------------------------------------------------- | -| Filesystem read | required | Core to codebase grounding; always available | -| Filesystem write | required | Needed for code edits; always available | -| Shell / terminal execution | required | `powershell` tool; always available in local sessions | -| Network access | optional | `web_fetch` for URL retrieval; `gh` CLI for GitHub API | -| External tool calls (MCP/plugins) | optional | Available when extensions or MCP servers are configured | -| Sub-agents | optional | `task` tool for delegation; requires compatible model and agent type | -| Canvas panels | optional | Available in the desktop app; degrade to text output in headless/API contexts | -| Background / scheduled tasks | optional | Workflows for scheduled; background agents for async within a session | +| Capability | Contract | Notes | +| --------------------------------- | -------- | ----------------------------------------------------------------------------- | +| Filesystem read | required | Core to codebase grounding; always available | +| Filesystem write | required | Needed for code edits; always available | +| Shell / terminal execution | required | `powershell` tool; always available in local sessions | +| Network access | optional | `web_fetch` for URL retrieval; `gh` CLI for GitHub API | +| External tool calls (MCP/plugins) | optional | Available when extensions or MCP servers are configured | +| Sub-agents | optional | `task` tool for delegation; requires compatible model and agent type | +| Canvas panels | optional | Available in the desktop app; degrade to text output in headless/API contexts | +| Background / scheduled tasks | optional | Workflows for scheduled; background agents for async within a session | Degradation rule: if an optional capability is missing, continue with the nearest equivalent (e.g., text output instead of canvas; inline shell command instructions if terminal is blocked). @@ -224,6 +224,7 @@ If a required capability is missing, stop and emit explicit manual steps. Update it at major milestones. Skip the plan for straightforward tasks. **Modes:** + - **Interactive** — step-by-step with user approval at each significant change; preferred for exploratory or risky work. - **Autopilot** — agent drives to completion autonomously; use for well-defined tasks. @@ -274,16 +275,16 @@ edge cases where multiple equally-valid approaches exist. ## Cross-compatibility -| Artifact | Cross-compat position | Notes | -| --------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------ | -| `AGENTS.md` | **Primary cross-compat file** | Read by Copilot CLI, VS Code Copilot, Claude Code, Codex | -| `.github/copilot-instructions.md` | Copilot family (CLI + VS Code) | Not read by Claude Code or Codex as a native instruction file | -| `.github/extensions/` | Copilot CLI-specific | Extension format is not shared with other harnesses | -| `.github/prompts/*.prompt.md` | Copilot family (shared format) | VS Code Copilot invokes as slash commands; Copilot CLI references by path | -| MCP (workspace/user config) | Cross-vendor | Config syntax and location differ per harness; protocol is vendor-neutral | -| `ai-artifacts/skills/shared/` | Source of truth | Mirrored to `.claude/commands/` (Claude Code) and `.agents/skills/` (Codex); not auto-mirrored for Copilot CLI | -| Scheduled workflows | Copilot CLI-specific | No equivalent native primitive in other harnesses | -| Canvas panels | Copilot CLI-specific | No equivalent in other harnesses | +| Artifact | Cross-compat position | Notes | +| --------------------------------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------- | +| `AGENTS.md` | **Primary cross-compat file** | Read by Copilot CLI, VS Code Copilot, Claude Code, Codex | +| `.github/copilot-instructions.md` | Copilot family (CLI + VS Code) | Not read by Claude Code or Codex as a native instruction file | +| `.github/extensions/` | Copilot CLI-specific | Extension format is not shared with other harnesses | +| `.github/prompts/*.prompt.md` | Copilot family (shared format) | VS Code Copilot invokes as slash commands; Copilot CLI references by path | +| MCP (workspace/user config) | Cross-vendor | Config syntax and location differ per harness; protocol is vendor-neutral | +| `ai-artifacts/skills/shared/` | Source of truth | Mirrored to `.claude/commands/` (Claude Code) and `.agents/skills/` (Codex); not auto-mirrored for Copilot CLI | +| Scheduled workflows | Copilot CLI-specific | No equivalent native primitive in other harnesses | +| Canvas panels | Copilot CLI-specific | No equivalent in other harnesses | **Cross-capability preference order for Copilot CLI authoring:** diff --git a/docs/okf-adoption.md b/docs/okf-adoption.md index 4cd96ad..8fbcde3 100644 --- a/docs/okf-adoption.md +++ b/docs/okf-adoption.md @@ -49,6 +49,6 @@ tags: [] - **Scratch tracker:** not adopted directly; `.scratch/AGENTS.md` and the scratch skill own that schema. -# Citations +## Citations [1] [Open Knowledge Format specification](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md) From 35cacd1119836afd9846d59d3a5d3b315226a50f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Jul 2026 05:22:30 +0000 Subject: [PATCH 06/12] Fix script tag stripping in mail-to-adoc --- .../documents/mail-to-adoc/scripts/mail_to_adoc.py | 2 +- .../documents/mail-to-adoc/tests/test_mail_to_adoc.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/mail_to_adoc.py b/ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/mail_to_adoc.py index 5c37d96..76010fe 100644 --- a/ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/mail_to_adoc.py +++ b/ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/mail_to_adoc.py @@ -289,7 +289,7 @@ def strip_html(html: str) -> str: """Remove non-content HTML and convert the remainder to AsciiDoc markup.""" html = re.sub(r"]*>.*?", "", html, flags=re.IGNORECASE | re.DOTALL) html = re.sub( - r"]*>.*?", "", html, flags=re.IGNORECASE | re.DOTALL + r"]*>.*?", "", html, flags=re.IGNORECASE | re.DOTALL ) html = re.sub(r"", "", html, flags=re.DOTALL) html = re.sub(r"]*>.*?", "", html, flags=re.IGNORECASE | re.DOTALL) diff --git a/ai-artifacts/skills/shared/documents/mail-to-adoc/tests/test_mail_to_adoc.py b/ai-artifacts/skills/shared/documents/mail-to-adoc/tests/test_mail_to_adoc.py index 8b27cf2..b13f148 100644 --- a/ai-artifacts/skills/shared/documents/mail-to-adoc/tests/test_mail_to_adoc.py +++ b/ai-artifacts/skills/shared/documents/mail-to-adoc/tests/test_mail_to_adoc.py @@ -132,6 +132,16 @@ def test_eml_to_adoc_prefers_html_and_writes_attachment_links(converter, tmp_pat assert f"* link:../../Attachments/{saved_name}[evidence.pdf]" in adoc +def test_strip_html_removes_script_tag_with_whitespace_before_closing_bracket(converter): + html = "

    keep

    also keep

    " + + text = converter.strip_html(html) + + assert "alert('x')" not in text + assert "keep" in text + assert "also keep" in text + + # --- Filename direction/attachment markers (bracket tags, replacing emoji) ------ _DENNIS = "Dennis " From 357eccbf863dcd7a8ecfe3be6508d0825f03f17b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Jul 2026 05:23:28 +0000 Subject: [PATCH 07/12] Harden script stripping in mail-to-adoc --- .../mail-to-adoc/scripts/mail_to_adoc.py | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/mail_to_adoc.py b/ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/mail_to_adoc.py index 76010fe..79c8c89 100644 --- a/ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/mail_to_adoc.py +++ b/ai-artifacts/skills/shared/documents/mail-to-adoc/scripts/mail_to_adoc.py @@ -160,6 +160,9 @@ def __init__(self): # Hyperlink state — href is set on , used while writing inner text, cleared on self.open_link_href: str | None = None + # Tags whose contents should be discarded entirely. + self.ignored_tag_depth = 0 + def _write(self, text: str) -> None: """Append text to the innermost active context: list item, table cell, or top level.""" if self.list_item_stack: @@ -171,6 +174,11 @@ def _write(self, text: str) -> None: def handle_starttag(self, tag, attrs): tag = tag.lower() + if tag in ("script", "style", "o:p"): + self.ignored_tag_depth += 1 + return + if self.ignored_tag_depth: + return attr_dict = dict(attrs) if tag == "table": self.table_depth += 1 @@ -203,6 +211,11 @@ def handle_starttag(self, tag, attrs): def handle_endtag(self, tag): tag = tag.lower() + if tag in ("script", "style", "o:p"): + self.ignored_tag_depth = max(0, self.ignored_tag_depth - 1) + return + if self.ignored_tag_depth: + return if tag == "table": if self.table_depth == 1 and self.table_rows: header_row = " ".join(f"|{c}" for c in self.table_rows[0]) @@ -267,6 +280,8 @@ def handle_endtag(self, tag): self._write("\n") def handle_data(self, data: str) -> None: + if self.ignored_tag_depth: + return data = re.sub( r"[\u200b\u200c\u200d\ufeff\u00ad]", "", data ) # zero-width / soft-hyphen chars @@ -287,12 +302,7 @@ def get_text(self) -> str: def strip_html(html: str) -> str: """Remove non-content HTML and convert the remainder to AsciiDoc markup.""" - html = re.sub(r"]*>.*?", "", html, flags=re.IGNORECASE | re.DOTALL) - html = re.sub( - r"]*>.*?", "", html, flags=re.IGNORECASE | re.DOTALL - ) html = re.sub(r"", "", html, flags=re.DOTALL) - html = re.sub(r"]*>.*?", "", html, flags=re.IGNORECASE | re.DOTALL) html = re.sub( r".*?", "", html, flags=re.IGNORECASE | re.DOTALL ) From d1cc3459e756d9b4bae990b53f7910e44e329911 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Jul 2026 05:38:01 +0000 Subject: [PATCH 08/12] chore: apply requested markdownlint rules --- .markdownlint.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.markdownlint.json b/.markdownlint.json index 007cfb0..1ee8066 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -6,6 +6,9 @@ "MD033": false, "MD041": false, + "MD003": { "style": "atx" }, + "MD004": { "style": "dash" }, + "MD029": { "style": "ordered" }, "MD049": { "style": "asterisk" }, "MD050": { "style": "asterisk" }, "MD060": { "style": "aligned" } From 96477fad3935ced4f16a0f05eec4f80f389c9412 Mon Sep 17 00:00:00 2001 From: Dennis Winter Date: Fri, 10 Jul 2026 07:52:50 +0200 Subject: [PATCH 09/12] markdownlint config sorted properly --- .markdownlint.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.markdownlint.json b/.markdownlint.json index 1ee8066..4e75e47 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -2,14 +2,14 @@ "default": true, "MD013": false, - "MD025": { "front_matter_title": "" }, "MD033": false, "MD041": false, "MD003": { "style": "atx" }, "MD004": { "style": "dash" }, + "MD025": { "front_matter_title": "" }, "MD029": { "style": "ordered" }, "MD049": { "style": "asterisk" }, "MD050": { "style": "asterisk" }, "MD060": { "style": "aligned" } -} \ No newline at end of file +} From fe9ccb6155cedf9aaf4432858c67ad7997fe60bb Mon Sep 17 00:00:00 2001 From: Dennis Winter Date: Fri, 10 Jul 2026 19:42:02 +0200 Subject: [PATCH 10/12] Reorganize shared skills and workflow tooling --- .agents/skills/setup_setup-repo/SKILL.md | 84 +++ .../scripts/Invoke-SetupRepo.ps1 | 584 ++++++++++++++++++ .../setup_setup-repo/tests/test_setup_repo.py | 124 ++++ .claude/commands/setup/setup-repo.md | 83 +++ .../setup-repo/scripts/Invoke-SetupRepo.ps1 | 584 ++++++++++++++++++ .../setup/setup-repo/tests/test_setup_repo.py | 124 ++++ .claude/settings.local.json | 8 +- .claudeignore | 6 +- .editorconfig | 3 - .githooks/pre-commit | 225 ++++--- .github/CODEOWNERS | 28 +- .github/skills/setup_setup-repo/SKILL.md | 84 +++ .../scripts/Invoke-SetupRepo.ps1 | 584 ++++++++++++++++++ .../setup_setup-repo/tests/test_setup_repo.py | 124 ++++ .github/workflows/config-lint.yml | 149 +---- .github/workflows/dependabot-auto-merge.yml | 6 +- .github/workflows/policy-check.yml | 121 +--- .github/workflows/powershell-quality.yml | 72 +-- .../workflows/powershell-runtime-compat.yml | 70 +++ .github/workflows/powershell-tests.yml | 62 ++ .github/workflows/python-quality.yml | 64 +- .github/workflows/python-tests.yml | 69 +++ .github/workflows/scripts/README.md | 34 + .../workflows/scripts/ci/Get-ChangedFiles.ps1 | 74 +++ .../scripts/ci/Resolve-DiffRange.ps1 | 50 ++ .../scripts/ci/WorkflowScript.Common.ps1 | 268 ++++++++ .../scripts/ci/determin-diff-range.sh | 37 ++ .../workflows/scripts/ci/get-changed-files.sh | 137 ++++ .../execute-workflow-config-lint.ps1 | 90 +++ .../scripts/config-lint/install-linters.sh | 42 ++ .../scripts/config-lint/lint-asciidoc.sh | 22 + .../scripts/config-lint/lint-markdown.sh | 20 + .../scripts/config-lint/lint-shell.sh | 20 + .../scripts/config-lint/lint-yaml.sh | 20 + .../scripts/config-lint/validate-js-syntax.sh | 20 + .../scripts/config-lint/validate-json.sh | 30 + .../scripts/config-lint/write-summary.sh | 17 + .../enable-auto-merge.sh | 3 + .../scripts/execute-all-workflow-scripts.ps1 | 67 ++ .../policy-check/enforce-repo-guardrails.sh | 41 ++ .../execute-workflow-policy-check.ps1 | 62 ++ .../policy-check/validate-coding-policies.sh | 28 + .../scripts/policy-check/write-summary.sh | 14 + .../execute-workflow-powershell-quality.ps1 | 86 +++ .../find-powershell-files.sh | 7 + .../run-psscriptanalyzer.sh | 21 + .../validate-powershell-syntax.sh | 21 + .../powershell-quality/write-summary.sh | 14 + ...ute-workflow-powershell-runtime-compat.ps1 | 90 +++ .../validate-pwsh-syntax.sh | 30 + .../validate-windowsps-syntax.sh | 30 + .../write-summary.sh | 7 + .../powershell-tests/discover-test-files.sh | 12 + .../execute-workflow-powershell-tests.ps1 | 48 ++ .../powershell-tests/install-pester.sh | 5 + .../powershell-tests/no-tests-found.sh | 3 + .../scripts/powershell-tests/run-pester.sh | 8 + .../execute-workflow-python-quality.ps1 | 53 ++ .../python-quality/find-python-files.sh | 7 + .../scripts/python-quality/install-tools.sh | 4 + .../scripts/python-quality/ruff-check.sh | 14 + .../python-quality/ruff-format-check.sh | 14 + .../scripts/python-quality/write-summary.sh | 14 + .../python-tests/discover-test-files.sh | 13 + .../execute-workflow-python-tests.ps1 | 48 ++ .../python-tests/install-test-dependencies.sh | 5 + .../scripts/python-tests/no-tests-found.sh | 3 + .../scripts/python-tests/run-pytest.sh | 6 + .../execute-workflow-shell-quality.ps1 | 49 ++ .../scripts/shell-quality/find-shell-files.sh | 7 + .../shell-quality/install-shellcheck.sh | 15 + .../scripts/shell-quality/run-shellcheck.sh | 14 + .../scripts/shell-quality/write-summary.sh | 13 + .../execute-workflow-shell-tests.ps1 | 55 ++ .../scripts/shell-tests/find-bats-files.sh | 7 + .../scripts/shell-tests/install-bats.sh | 15 + .../scripts/shell-tests/no-tests-found.sh | 3 + .../workflows/scripts/shell-tests/run-bats.sh | 7 + .../scripts/shell-tests/write-summary.sh | 13 + .../scripts/shell/run-shell-tests.sh | 20 + .../scripts}/validate-coding-policies.py | 13 +- .../scripts/validate-powershell-runtime.py | 201 ++++++ .github/workflows/shell-quality.yml | 67 +- .github/workflows/shell-tests.yml | 82 +++ .gitignore | 24 +- .scratch/claude-code-skill-adaptation/PRD.md | 6 +- .../02-fix-write-a-skill-resource-link.md | 2 +- .../issues/03-conversational-light-pass.md | 2 +- .../issues/04-adapt-recon-shell-probe.md | 2 +- .../05-adapt-tdd-prototype-tool-access.md | 2 +- .scratch/import-upstream-skills/PRD.md | 4 +- .../issues/03-import-coding-skills.md | 4 +- .../issues/04-import-planning-cluster.md | 4 +- .../issues/05-import-setup-hook-skills.md | 2 +- .../issues/06-adapt-imported-skills.md | 2 +- .../incorporate-global-claude-setup/PRD.md | 2 +- .scratch/repo-scaffold/PRD.md | 4 +- .scratch/sync-skills-drift-check/PRD.md | 8 +- .../issues/01-reference-doc-from-tdd-skill.md | 2 +- .scratch/understand-scratch-skill/PRD.md | 2 +- .vscode/settings.json | 6 + .vscode/tasks.json | 38 ++ AGENTS.md | 17 +- CONTEXT.md | 2 +- ai-artifacts/skills/shared/README.md | 12 +- .../skills/shared/coding/diagnose/SKILL.md | 2 +- .../hitl-loop.template.ps1 | 4 + .../hitl-loop.template.sh | 0 .../improve-codebase-architecture/SKILL.md | 14 +- .../{ => docs}/DEEPENING.md | 0 .../{ => docs}/HTML-REPORT.md | 0 .../{ => docs}/INTERFACE-DESIGN.md | 0 .../{ => docs}/LANGUAGE.md | 0 .../skills/shared/coding/tdd/SKILL.md | 164 ++--- .../shared/coding/tdd/{ => docs}/behaviors.md | 0 .../coding/tdd/{ => docs}/companions.md | 0 .../tdd/{ => docs}/design-for-testability.md | 0 .../tdd/{ => docs}/reviewing-and-cycle.md | 0 .../coding/tdd/{ => docs}/test-doubles.md | 0 .../scripts/Invoke-MailToAdoc.ps1 | 2 + .../mail-to-adoc/scripts/Move-Belege.ps1 | 6 +- .../mail-to-adoc/tests/test_mail_to_adoc.py | 4 +- .../shared/planning/scratch-plan/SKILL.md | 4 +- .../skills/shared/planning/scratch/SKILL.md | 8 +- .../planning/scratch/{ => docs}/LAYOUT.md | 0 .../planning/scratch/{ => docs}/RANKING.md | 0 .../skills/shared/session/grill-me/SKILL.md | 132 ++-- .../session/grill-me/{ => docs}/ADR-FORMAT.md | 0 .../grill-me/{ => docs}/CONTEXT-FORMAT.md | 0 .../skills/shared/session/recon/SKILL.md | 162 ++--- .../session/recon/{ => docs}/RECIPES.md | 0 .../shared/session/write-a-skill/SKILL.md | 177 +++--- .../write-a-skill/{ => docs}/EXAMPLES.md | 0 .../write-a-skill/{ => docs}/SCRIPTS.md | 0 .../shared/setup/check-skill-updates/SKILL.md | 6 +- .../scripts/block-dangerous-git.ps1 | 4 +- .../setup/import-upstream-skill/SKILL.md | 6 +- .../shared/setup/setup-repo/METADATA.md | 14 + .../skills/shared/setup/setup-repo/SKILL.md | 83 +++ .../setup-repo/scripts/Invoke-SetupRepo.ps1 | 584 ++++++++++++++++++ .../setup/setup-repo/tests/test_setup_repo.py | 124 ++++ .../workflow/simulate-workflows/METADATA.md | 14 + .../workflow/simulate-workflows/SKILL.md | 55 ++ .../Invoke-LocalWorkflowSimulation.ps1 | 126 ++++ .../tests/test_simulate_workflows.py | 139 +++++ .../languages/powershell-policy.yaml | 16 + docs/harnesses/claude-code.md | 4 +- docs/harnesses/codex.md | 31 +- docs/harnesses/copilot-cli.md | 14 +- docs/repo-layout.adoc | 45 +- docs/reviews/2026-07-04-fable-repo-review.md | 10 +- reports/coding-policy-check.txt | 3 + reports/powershell-runtime-check.txt | 3 + scripts/install-git-hooks.ps1 | 24 - scripts/setup-repo.ps1 | 60 ++ scripts/simulate-workflows.ps1 | 25 + scripts/sync-skills.ps1 | 436 ------------- 157 files changed, 6763 insertions(+), 1387 deletions(-) create mode 100644 .agents/skills/setup_setup-repo/SKILL.md create mode 100644 .agents/skills/setup_setup-repo/scripts/Invoke-SetupRepo.ps1 create mode 100644 .agents/skills/setup_setup-repo/tests/test_setup_repo.py create mode 100644 .claude/commands/setup/setup-repo.md create mode 100644 .claude/commands/setup/setup-repo/scripts/Invoke-SetupRepo.ps1 create mode 100644 .claude/commands/setup/setup-repo/tests/test_setup_repo.py create mode 100644 .github/skills/setup_setup-repo/SKILL.md create mode 100644 .github/skills/setup_setup-repo/scripts/Invoke-SetupRepo.ps1 create mode 100644 .github/skills/setup_setup-repo/tests/test_setup_repo.py create mode 100644 .github/workflows/powershell-runtime-compat.yml create mode 100644 .github/workflows/powershell-tests.yml create mode 100644 .github/workflows/python-tests.yml create mode 100644 .github/workflows/scripts/README.md create mode 100644 .github/workflows/scripts/ci/Get-ChangedFiles.ps1 create mode 100644 .github/workflows/scripts/ci/Resolve-DiffRange.ps1 create mode 100644 .github/workflows/scripts/ci/WorkflowScript.Common.ps1 create mode 100644 .github/workflows/scripts/ci/determin-diff-range.sh create mode 100644 .github/workflows/scripts/ci/get-changed-files.sh create mode 100644 .github/workflows/scripts/config-lint/execute-workflow-config-lint.ps1 create mode 100644 .github/workflows/scripts/config-lint/install-linters.sh create mode 100644 .github/workflows/scripts/config-lint/lint-asciidoc.sh create mode 100644 .github/workflows/scripts/config-lint/lint-markdown.sh create mode 100644 .github/workflows/scripts/config-lint/lint-shell.sh create mode 100644 .github/workflows/scripts/config-lint/lint-yaml.sh create mode 100644 .github/workflows/scripts/config-lint/validate-js-syntax.sh create mode 100644 .github/workflows/scripts/config-lint/validate-json.sh create mode 100644 .github/workflows/scripts/config-lint/write-summary.sh create mode 100644 .github/workflows/scripts/dependabot-auto-merge/enable-auto-merge.sh create mode 100644 .github/workflows/scripts/execute-all-workflow-scripts.ps1 create mode 100644 .github/workflows/scripts/policy-check/enforce-repo-guardrails.sh create mode 100644 .github/workflows/scripts/policy-check/execute-workflow-policy-check.ps1 create mode 100644 .github/workflows/scripts/policy-check/validate-coding-policies.sh create mode 100644 .github/workflows/scripts/policy-check/write-summary.sh create mode 100644 .github/workflows/scripts/powershell-quality/execute-workflow-powershell-quality.ps1 create mode 100644 .github/workflows/scripts/powershell-quality/find-powershell-files.sh create mode 100644 .github/workflows/scripts/powershell-quality/run-psscriptanalyzer.sh create mode 100644 .github/workflows/scripts/powershell-quality/validate-powershell-syntax.sh create mode 100644 .github/workflows/scripts/powershell-quality/write-summary.sh create mode 100644 .github/workflows/scripts/powershell-runtime-compat/execute-workflow-powershell-runtime-compat.ps1 create mode 100644 .github/workflows/scripts/powershell-runtime-compat/validate-pwsh-syntax.sh create mode 100644 .github/workflows/scripts/powershell-runtime-compat/validate-windowsps-syntax.sh create mode 100644 .github/workflows/scripts/powershell-runtime-compat/write-summary.sh create mode 100644 .github/workflows/scripts/powershell-tests/discover-test-files.sh create mode 100644 .github/workflows/scripts/powershell-tests/execute-workflow-powershell-tests.ps1 create mode 100644 .github/workflows/scripts/powershell-tests/install-pester.sh create mode 100644 .github/workflows/scripts/powershell-tests/no-tests-found.sh create mode 100644 .github/workflows/scripts/powershell-tests/run-pester.sh create mode 100644 .github/workflows/scripts/python-quality/execute-workflow-python-quality.ps1 create mode 100644 .github/workflows/scripts/python-quality/find-python-files.sh create mode 100644 .github/workflows/scripts/python-quality/install-tools.sh create mode 100644 .github/workflows/scripts/python-quality/ruff-check.sh create mode 100644 .github/workflows/scripts/python-quality/ruff-format-check.sh create mode 100644 .github/workflows/scripts/python-quality/write-summary.sh create mode 100644 .github/workflows/scripts/python-tests/discover-test-files.sh create mode 100644 .github/workflows/scripts/python-tests/execute-workflow-python-tests.ps1 create mode 100644 .github/workflows/scripts/python-tests/install-test-dependencies.sh create mode 100644 .github/workflows/scripts/python-tests/no-tests-found.sh create mode 100644 .github/workflows/scripts/python-tests/run-pytest.sh create mode 100644 .github/workflows/scripts/shell-quality/execute-workflow-shell-quality.ps1 create mode 100644 .github/workflows/scripts/shell-quality/find-shell-files.sh create mode 100644 .github/workflows/scripts/shell-quality/install-shellcheck.sh create mode 100644 .github/workflows/scripts/shell-quality/run-shellcheck.sh create mode 100644 .github/workflows/scripts/shell-quality/write-summary.sh create mode 100644 .github/workflows/scripts/shell-tests/execute-workflow-shell-tests.ps1 create mode 100644 .github/workflows/scripts/shell-tests/find-bats-files.sh create mode 100644 .github/workflows/scripts/shell-tests/install-bats.sh create mode 100644 .github/workflows/scripts/shell-tests/no-tests-found.sh create mode 100644 .github/workflows/scripts/shell-tests/run-bats.sh create mode 100644 .github/workflows/scripts/shell-tests/write-summary.sh create mode 100644 .github/workflows/scripts/shell/run-shell-tests.sh rename {scripts => .github/workflows/scripts}/validate-coding-policies.py (94%) create mode 100644 .github/workflows/scripts/validate-powershell-runtime.py create mode 100644 .github/workflows/shell-tests.yml create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json rename ai-artifacts/skills/shared/coding/diagnose/{scripts => templates}/hitl-loop.template.ps1 (94%) rename ai-artifacts/skills/shared/coding/diagnose/{scripts => templates}/hitl-loop.template.sh (100%) rename ai-artifacts/skills/shared/coding/improve-codebase-architecture/{ => docs}/DEEPENING.md (100%) rename ai-artifacts/skills/shared/coding/improve-codebase-architecture/{ => docs}/HTML-REPORT.md (100%) rename ai-artifacts/skills/shared/coding/improve-codebase-architecture/{ => docs}/INTERFACE-DESIGN.md (100%) rename ai-artifacts/skills/shared/coding/improve-codebase-architecture/{ => docs}/LANGUAGE.md (100%) rename ai-artifacts/skills/shared/coding/tdd/{ => docs}/behaviors.md (100%) rename ai-artifacts/skills/shared/coding/tdd/{ => docs}/companions.md (100%) rename ai-artifacts/skills/shared/coding/tdd/{ => docs}/design-for-testability.md (100%) rename ai-artifacts/skills/shared/coding/tdd/{ => docs}/reviewing-and-cycle.md (100%) rename ai-artifacts/skills/shared/coding/tdd/{ => docs}/test-doubles.md (100%) rename ai-artifacts/skills/shared/planning/scratch/{ => docs}/LAYOUT.md (100%) rename ai-artifacts/skills/shared/planning/scratch/{ => docs}/RANKING.md (100%) rename ai-artifacts/skills/shared/session/grill-me/{ => docs}/ADR-FORMAT.md (100%) rename ai-artifacts/skills/shared/session/grill-me/{ => docs}/CONTEXT-FORMAT.md (100%) rename ai-artifacts/skills/shared/session/recon/{ => docs}/RECIPES.md (100%) rename ai-artifacts/skills/shared/session/write-a-skill/{ => docs}/EXAMPLES.md (100%) rename ai-artifacts/skills/shared/session/write-a-skill/{ => docs}/SCRIPTS.md (100%) create mode 100644 ai-artifacts/skills/shared/setup/setup-repo/METADATA.md create mode 100644 ai-artifacts/skills/shared/setup/setup-repo/SKILL.md create mode 100644 ai-artifacts/skills/shared/setup/setup-repo/scripts/Invoke-SetupRepo.ps1 create mode 100644 ai-artifacts/skills/shared/setup/setup-repo/tests/test_setup_repo.py create mode 100644 ai-artifacts/skills/shared/workflow/simulate-workflows/METADATA.md create mode 100644 ai-artifacts/skills/shared/workflow/simulate-workflows/SKILL.md create mode 100644 ai-artifacts/skills/shared/workflow/simulate-workflows/scripts/Invoke-LocalWorkflowSimulation.ps1 create mode 100644 ai-artifacts/skills/shared/workflow/simulate-workflows/tests/test_simulate_workflows.py create mode 100644 reports/coding-policy-check.txt create mode 100644 reports/powershell-runtime-check.txt delete mode 100644 scripts/install-git-hooks.ps1 create mode 100644 scripts/setup-repo.ps1 create mode 100644 scripts/simulate-workflows.ps1 delete mode 100644 scripts/sync-skills.ps1 diff --git a/.agents/skills/setup_setup-repo/SKILL.md b/.agents/skills/setup_setup-repo/SKILL.md new file mode 100644 index 0000000..fe826cd --- /dev/null +++ b/.agents/skills/setup_setup-repo/SKILL.md @@ -0,0 +1,84 @@ +--- +name: "setup_setup-repo" +description: "Bootstrap this repository after clone by enabling git hooks and syncing generated skill mirrors. Use when user asks to set up the repo, bootstrap local tooling, or initialize mirrors/hooks for this clone." +version: "1.0.0" +--- + + +# Setup Repo + +Bootstraps this clone in one pass: + +1. Activates repo git hooks +2. Syncs generated skill mirrors for Claude, Codex, and Copilot + +Implementation layout: + +- Canonical entrypoint: `ai-artifacts/skills/shared/setup/setup-repo/scripts/Invoke-SetupRepo.ps1` +- Repo convenience wrapper: `scripts/setup-repo.ps1` + +## Applicability (capability contract) + +This skill needs shell + filesystem access in a local git checkout. In chat-only environments, +provide the exact commands and explain what each command configures. + +## Default command + +```powershell +pwsh scripts/setup-repo.ps1 +``` + +## Common variants + +Project-local mirrors for one harness: + +```powershell +pwsh scripts/setup-repo.ps1 -Target Copilot -Scope Project +``` + +User-scope mirrors for all harnesses: + +```powershell +pwsh scripts/setup-repo.ps1 -Scope User +``` + +Bootstrap only missing generated mirrors (keeps existing generated files untouched): + +```powershell +pwsh scripts/setup-repo.ps1 -IfMissing +``` + +Refresh generated mirrors without touching git hooks: + +```powershell +pwsh scripts/setup-repo.ps1 -SkipHooks +``` + +Check generated mirrors for drift: + +```powershell +pwsh scripts/setup-repo.ps1 -SkipHooks -Check +pwsh scripts/setup-repo.ps1 -SkipHooks -Target Codex -Skill tdd -Check +``` + +Run only one phase: + +```powershell +pwsh scripts/setup-repo.ps1 -SkipHooks +pwsh scripts/setup-repo.ps1 -SkipSkillSync +``` + +## Verify + +```powershell +git config --get core.hooksPath +Test-Path .claude/commands +Test-Path .agents/skills +Test-Path .github/skills +pwsh scripts/setup-repo.ps1 -SkipHooks -Check +``` + +Expected: + +- `core.hooksPath` prints `.githooks` +- mirror folders exist for selected targets diff --git a/.agents/skills/setup_setup-repo/scripts/Invoke-SetupRepo.ps1 b/.agents/skills/setup_setup-repo/scripts/Invoke-SetupRepo.ps1 new file mode 100644 index 0000000..d1c0f05 --- /dev/null +++ b/.agents/skills/setup_setup-repo/scripts/Invoke-SetupRepo.ps1 @@ -0,0 +1,584 @@ +#Requires -Version 7.0 +#Requires -PSEdition Core +# RuntimePolicy: core-first + +[CmdletBinding()] +param( + [string]$RepoRoot = "", + + [ValidateSet('All', 'Claude', 'Codex', 'Copilot')] + [string]$Target = 'All', + + [Alias('Scope')] + [ValidateSet('Project', 'User')] + [string]$MirrorScope = 'Project', + + [string]$Skill = '', + + [switch]$IfMissing, + [switch]$Check, + [switch]$SkipHooks, + [switch]$SkipSkillSync +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +if ($Check -and $IfMissing) { + throw "-Check is read-only; do not combine it with -IfMissing." +} + +function Get-UserHomePath { + if ($env:HOME) { return $env:HOME } + if ($env:USERPROFILE) { return $env:USERPROFILE } + throw "Cannot resolve user home path from HOME/USERPROFILE." +} + +function Get-RelativePathText { + param( + [string]$BasePath, + [string]$TargetPath + ) + + $rel = [System.IO.Path]::GetRelativePath($BasePath, $TargetPath) + return $rel.TrimStart('\', '/') +} + +function Get-MirrorRootPath { + param( + [ValidateSet('Claude', 'Codex', 'Copilot')] + [string]$TargetName, + + [ValidateSet('Project', 'User')] + [string]$ScopeName, + + [string]$ResolvedRepoRoot + ) + + switch ("$TargetName|$ScopeName") { + 'Claude|Project' { return (Join-Path $ResolvedRepoRoot '.claude/commands') } + 'Claude|User' { return (Join-Path (Get-UserHomePath) '.claude/commands') } + 'Codex|Project' { return (Join-Path $ResolvedRepoRoot '.agents/skills') } + 'Codex|User' { return (Join-Path (Get-UserHomePath) '.codex/skills') } + 'Copilot|Project' { return (Join-Path $ResolvedRepoRoot '.github/skills') } + 'Copilot|User' { return (Join-Path (Get-UserHomePath) '.copilot/skills') } + default { throw "Unsupported mirror target/scope combination: $TargetName / $ScopeName" } + } +} + +function Get-SkillDirectories { + param( + [string]$SkillsRoot, + [string]$SkillName + ) + + $dirs = @(Get-ChildItem $SkillsRoot -Recurse -Filter 'SKILL.md' -File | ForEach-Object { $_.Directory }) + if ($SkillName) { + $dirs = @($dirs | Where-Object { $_.Name -eq $SkillName }) + if ($dirs.Count -eq 0) { + throw "No SKILL.md found for skill '$SkillName' under $SkillsRoot" + } + } + if ($dirs.Count -eq 0) { + throw "No SKILL.md files found under $SkillsRoot" + } + return $dirs +} + +function Get-SyncReportEntry { + param( + [string]$Name, + [string]$Detail + ) + + return [PSCustomObject]@{ + Name = $Name + Detail = $Detail + } +} + +function Write-SyncReport { + param( + [string]$Flavor, + [string]$TargetPath, + [object[]]$Entries, + [string]$Scope + ) + + $scopeText = if ($Scope) { " (scope=$Scope)" } else { '' } + Write-Output "Synced $Flavor -> $TargetPath$scopeText" + + if (-not $Entries -or @($Entries).Count -eq 0) { + return + } + + $nameWidth = ($Entries | ForEach-Object { $_.Name.Length } | Measure-Object -Maximum).Maximum + if (-not $nameWidth) { $nameWidth = 0 } + + foreach ($entry in $Entries) { + Write-Output (" {0} {1}" -f $entry.Name.PadRight($nameWidth), $entry.Detail) + } +} + +function Get-SkillResources { + param([string]$SkillDir) + + return @(Get-ChildItem $SkillDir -Recurse -File | + Where-Object { $_.Name -notin @('SKILL.md', 'METADATA.md') }) +} + +function Copy-MirroredResources { + param( + [System.IO.FileInfo[]]$Resources, + [string]$SourceBase, + [string]$TargetBase + ) + + foreach ($res in $Resources) { + $rel = Get-RelativePathText -BasePath $SourceBase -TargetPath $res.FullName + $dest = Join-Path $TargetBase $rel + New-Item -ItemType Directory -Path (Split-Path $dest -Parent) -Force | Out-Null + Copy-Item $res.FullName $dest -Force + } +} + +function Test-MirroredResourcesMatch { + param( + [System.IO.FileInfo[]]$Resources, + [string]$SourceBase, + [string]$TargetBase, + [switch]$ExcludeTargetSkillFile + ) + + foreach ($res in $Resources) { + $rel = Get-RelativePathText -BasePath $SourceBase -TargetPath $res.FullName + $dest = Join-Path $TargetBase $rel + if (-not (Test-Path $dest) -or + (Get-FileHash $res.FullName).Hash -ne (Get-FileHash $dest).Hash) { + return $false + } + } + + $resourceCount = @($Resources).Count + $mirroredCount = if (Test-Path $TargetBase) { + @(Get-ChildItem $TargetBase -Recurse -File | + Where-Object { -not $ExcludeTargetSkillFile -or $_.Name -ne 'SKILL.md' }).Count + } + else { + 0 + } + + return $mirroredCount -eq $resourceCount +} + +function Get-SkillMirrorState { + param( + [string]$TargetSkillPath, + [string]$ExpectedSkill, + [System.IO.FileInfo[]]$Resources, + [string]$SourceBase, + [string]$TargetResourceBase, + [switch]$ExcludeTargetSkillFile + ) + + if (-not (Test-Path $TargetSkillPath)) { return 'MISSING' } + if ((Get-Content $TargetSkillPath -Raw) -cne $ExpectedSkill) { return 'STALE' } + + if (-not (Test-MirroredResourcesMatch -Resources $Resources -SourceBase $SourceBase -TargetBase $TargetResourceBase -ExcludeTargetSkillFile:$ExcludeTargetSkillFile)) { + return 'STALE' + } + + return 'UP-TO-DATE' +} + +function Convert-ResourceLinks { + param( + [string]$Body, + [string]$Name, + [string[]]$ResourceRelPaths + ) + + foreach ($path in $ResourceRelPaths) { + $Body = $Body.Replace("](./$path)", "]($Name/$path)") + $Body = $Body.Replace("]($path)", "]($Name/$path)") + $Body = $Body -replace ("(?<=@)" + [regex]::Escape($path) + "\b"), "$Name/$path" + } + return $Body +} + +function ConvertTo-YamlScalar { + param([string]$Value) + + if ($null -eq $Value) { return '""' } + return '"' + ($Value -replace '\\', '\\' -replace '"', '\"') + '"' +} + +function Get-FrontmatterValue { + param( + [string]$Document, + [string]$Key + ) + + if ($Document -notmatch "(?s)\A---\r?\n(.*?)\r?\n---\r?\n") { return $null } + $frontmatter = $Matches[1] + $match = [regex]::Match($frontmatter, "(?m)^$([regex]::Escape($Key)):\s*(.*)$") + if (-not $match.Success) { return $null } + + $value = $match.Groups[1].Value.Trim() + if (($value.StartsWith('"') -and $value.EndsWith('"')) -or + ($value.StartsWith("'") -and $value.EndsWith("'"))) { + $value = $value.Substring(1, $value.Length - 2) + } + return $value +} + +function Get-SkillBody { + param([string]$Document) + + if ($Document -match "(?s)\A---\r?\n.*?\r?\n---\r?\n(.*)\z") { + return $Matches[1].TrimStart() + } + return $Document.TrimStart() +} + +function ConvertTo-CodexSkill { + param( + [string]$SourceDocument, + [string]$CodexName, + [string]$SourceRelPath + ) + + $description = Get-FrontmatterValue -Document $SourceDocument -Key 'description' + if (-not $description) { $description = "Repo skill mirrored from $SourceRelPath." } + $version = Get-FrontmatterValue -Document $SourceDocument -Key 'version' + + $body = Get-SkillBody -Document $SourceDocument + $frontmatter = @( + '---' + "name: $(ConvertTo-YamlScalar $CodexName)" + "description: $(ConvertTo-YamlScalar $description)" + $(if ($version) { "version: $(ConvertTo-YamlScalar $version)" }) + '---' + '' + "" + '' + ) -join "`n" + + return $frontmatter + $body +} + +function ConvertTo-CopilotSkill { + param( + [string]$SourceDocument, + [string]$SkillName, + [string]$SourceRelPath + ) + + $description = Get-FrontmatterValue -Document $SourceDocument -Key 'description' + if (-not $description) { $description = "Repo skill mirrored from $SourceRelPath." } + $version = Get-FrontmatterValue -Document $SourceDocument -Key 'version' + + $body = Get-SkillBody -Document $SourceDocument + $frontmatter = @( + '---' + "name: $(ConvertTo-YamlScalar $SkillName)" + "description: $(ConvertTo-YamlScalar $description)" + $(if ($version) { "version: $(ConvertTo-YamlScalar $version)" }) + '---' + '' + "" + '' + ) -join "`n" + + return $frontmatter + $body +} + +function Invoke-InstallGitHooksInternal { + param([string]$ResolvedRepoRoot) + + $hooksDir = Join-Path $ResolvedRepoRoot '.githooks' + if (Test-Path $hooksDir) { + $utf8NoBom = New-Object System.Text.UTF8Encoding($false) + Get-ChildItem -Path $hooksDir -File | ForEach-Object { + $raw = [System.IO.File]::ReadAllText($_.FullName) + $normalized = $raw -replace "`r`n", "`n" -replace "`r", "`n" + if ($normalized -ne $raw) { + [System.IO.File]::WriteAllText($_.FullName, $normalized, $utf8NoBom) + Write-Output "Normalized LF line endings: $($_.Name)" + } + } + } + + Write-Output '== Git hooks ==' + git config core.hooksPath .githooks + Write-Output 'Configured core.hooksPath to .githooks' + + if ($IsLinux -or $IsMacOS) { + $preCommit = Join-Path $hooksDir 'pre-commit' + if (Test-Path $preCommit) { + & chmod +x $preCommit + } + } + + Write-Output 'Git hooks are now active for this clone.' +} + +function Sync-ClaudeSkillsInternal { + param( + [System.IO.DirectoryInfo[]]$SkillDirs, + [string]$ResolvedRepoRoot, + [string]$ScopeName, + [switch]$OnlyIfMissing, + [switch]$ReadOnlyCheck + ) + + $commandsRoot = Get-MirrorRootPath -TargetName 'Claude' -ScopeName $ScopeName -ResolvedRepoRoot $ResolvedRepoRoot + $entries = [System.Collections.Generic.List[object]]::new() + $driftCount = 0 + $script:LastSyncExitCode = 0 + + Write-Output '' + Write-Output '== Claude Code skill mirror ==' + + foreach ($dir in $SkillDirs) { + $name = $dir.Name + $group = $dir.Parent.Name + $groupDir = Join-Path $commandsRoot $group + $targetMd = Join-Path $groupDir "$name.md" + $targetRes = Join-Path $groupDir $name + + if ($OnlyIfMissing -and (Test-Path $targetMd)) { + $entries.Add((Get-SyncReportEntry -Name ("/{0}:{1}" -f $group, $name) -Detail 'present - skipped (-IfMissing)')) + continue + } + + $resources = Get-SkillResources -SkillDir $dir.FullName + $resRelPaths = $resources | ForEach-Object { + (Get-RelativePathText -BasePath $dir.FullName -TargetPath $_.FullName).Replace('\\', '/') + } + + $body = Get-Content (Join-Path $dir.FullName 'SKILL.md') -Raw + if ($resRelPaths) { + $body = Convert-ResourceLinks -Body $body -Name $name -ResourceRelPaths $resRelPaths + } + + if ($ReadOnlyCheck) { + $state = Get-SkillMirrorState -TargetSkillPath $targetMd -ExpectedSkill $body -Resources $resources -SourceBase $dir.FullName -TargetResourceBase $targetRes + if ($state -ne 'UP-TO-DATE') { $driftCount++ } + $entries.Add((Get-SyncReportEntry -Name ("/{0}:{1}" -f $group, $name) -Detail $state)) + continue + } + + if (Test-Path $targetMd) { Remove-Item $targetMd -Force } + if (Test-Path $targetRes) { Remove-Item $targetRes -Recurse -Force } + New-Item -ItemType Directory -Path $groupDir -Force | Out-Null + Set-Content -Path $targetMd -Value $body -Encoding utf8 -NoNewline + Copy-MirroredResources -Resources $resources -SourceBase $dir.FullName -TargetBase $targetRes + + $entries.Add((Get-SyncReportEntry -Name ("/{0}:{1}" -f $group, $name) -Detail ("{0,2} resource file(s)" -f @($resources).Count))) + } + + Write-SyncReport -Flavor 'Claude skills' -TargetPath $commandsRoot -Entries $entries -Scope $ScopeName + if ($ReadOnlyCheck -and $driftCount) { + $script:LastSyncExitCode = 1 + } +} + +function Sync-CodexSkillsInternal { + param( + [System.IO.DirectoryInfo[]]$SkillDirs, + [string]$ResolvedRepoRoot, + [string]$ScopeName, + [switch]$OnlyIfMissing, + [switch]$ReadOnlyCheck, + [string]$SelectedSkill + ) + + $agentsRoot = Get-MirrorRootPath -TargetName 'Codex' -ScopeName $ScopeName -ResolvedRepoRoot $ResolvedRepoRoot + $entries = [System.Collections.Generic.List[object]]::new() + $expectedSkillNames = [System.Collections.Generic.HashSet[string]]::new() + $driftCount = 0 + $script:LastSyncExitCode = 0 + + if (-not $ReadOnlyCheck -and -not $OnlyIfMissing -and -not $SelectedSkill -and (Test-Path $agentsRoot)) { + Remove-Item $agentsRoot -Recurse -Force + } + + Write-Output '' + Write-Output '== Codex skill mirror ==' + + foreach ($dir in $SkillDirs) { + $sourceName = $dir.Name + $group = $dir.Parent.Name + $codexName = "$group`_$sourceName" + [void]$expectedSkillNames.Add($codexName) + $targetDir = Join-Path $agentsRoot $codexName + $targetSkill = Join-Path $targetDir 'SKILL.md' + + if ($OnlyIfMissing -and (Test-Path $targetSkill)) { + $entries.Add((Get-SyncReportEntry -Name $codexName -Detail 'present - skipped (-IfMissing)')) + continue + } + + $sourceRelPath = "ai-artifacts/skills/shared/$group/$sourceName/SKILL.md" + $sourceDocument = Get-Content (Join-Path $dir.FullName 'SKILL.md') -Raw + $expectedSkill = ConvertTo-CodexSkill -SourceDocument $sourceDocument -CodexName $codexName -SourceRelPath $sourceRelPath + $resources = Get-SkillResources -SkillDir $dir.FullName + + if ($ReadOnlyCheck) { + $state = Get-SkillMirrorState -TargetSkillPath $targetSkill -ExpectedSkill $expectedSkill -Resources $resources -SourceBase $dir.FullName -TargetResourceBase $targetDir -ExcludeTargetSkillFile + if ($state -ne 'UP-TO-DATE') { $driftCount++ } + $entries.Add((Get-SyncReportEntry -Name $codexName -Detail $state)) + continue + } + + if (Test-Path $targetDir) { Remove-Item $targetDir -Recurse -Force } + New-Item -ItemType Directory -Path $targetDir -Force | Out-Null + Set-Content -Path $targetSkill -Value $expectedSkill -Encoding utf8 -NoNewline + Copy-MirroredResources -Resources $resources -SourceBase $dir.FullName -TargetBase $targetDir + + $entries.Add((Get-SyncReportEntry -Name $codexName -Detail ("{0,2} resource file(s)" -f @($resources).Count))) + } + + if ($ReadOnlyCheck -and -not $SelectedSkill -and (Test-Path $agentsRoot)) { + foreach ($dir in Get-ChildItem $agentsRoot -Directory) { + if (-not $expectedSkillNames.Contains($dir.Name)) { + $driftCount++ + $entries.Add((Get-SyncReportEntry -Name $dir.Name -Detail 'EXTRA')) + } + } + } + + Write-SyncReport -Flavor 'Codex skills' -TargetPath $agentsRoot -Entries $entries -Scope $ScopeName + if ($ReadOnlyCheck -and $driftCount) { + $script:LastSyncExitCode = 1 + } +} + +function Sync-CopilotSkillsInternal { + param( + [System.IO.DirectoryInfo[]]$SkillDirs, + [string]$ResolvedRepoRoot, + [string]$ScopeName, + [switch]$OnlyIfMissing, + [switch]$ReadOnlyCheck, + [string]$SelectedSkill + ) + + $skillsMirrorRoot = Get-MirrorRootPath -TargetName 'Copilot' -ScopeName $ScopeName -ResolvedRepoRoot $ResolvedRepoRoot + $entries = [System.Collections.Generic.List[object]]::new() + $expectedSkillNames = [System.Collections.Generic.HashSet[string]]::new() + $driftCount = 0 + $script:LastSyncExitCode = 0 + + if (-not $ReadOnlyCheck -and -not $OnlyIfMissing -and -not $SelectedSkill -and (Test-Path $skillsMirrorRoot)) { + Remove-Item $skillsMirrorRoot -Recurse -Force + } + + Write-Output '' + Write-Output '== Copilot skill mirror ==' + + foreach ($dir in $SkillDirs) { + $sourceName = $dir.Name + $group = $dir.Parent.Name + $copilotSkillName = "$group`_$sourceName" + [void]$expectedSkillNames.Add($copilotSkillName) + $targetDir = Join-Path $skillsMirrorRoot $copilotSkillName + $targetSkill = Join-Path $targetDir 'SKILL.md' + + if ($OnlyIfMissing -and (Test-Path $targetSkill)) { + $entries.Add((Get-SyncReportEntry -Name $copilotSkillName -Detail 'present - skipped (-IfMissing)')) + continue + } + + $sourceRelPath = "ai-artifacts/skills/shared/$group/$sourceName/SKILL.md" + $sourceDocument = Get-Content (Join-Path $dir.FullName 'SKILL.md') -Raw + $expectedSkill = ConvertTo-CopilotSkill -SourceDocument $sourceDocument -SkillName $copilotSkillName -SourceRelPath $sourceRelPath + $resources = Get-SkillResources -SkillDir $dir.FullName + + if ($ReadOnlyCheck) { + $state = Get-SkillMirrorState -TargetSkillPath $targetSkill -ExpectedSkill $expectedSkill -Resources $resources -SourceBase $dir.FullName -TargetResourceBase $targetDir -ExcludeTargetSkillFile + if ($state -ne 'UP-TO-DATE') { $driftCount++ } + $entries.Add((Get-SyncReportEntry -Name $copilotSkillName -Detail $state)) + continue + } + + if (Test-Path $targetDir) { Remove-Item $targetDir -Recurse -Force } + New-Item -ItemType Directory -Path $targetDir -Force | Out-Null + Set-Content -Path $targetSkill -Value $expectedSkill -Encoding utf8 -NoNewline + Copy-MirroredResources -Resources $resources -SourceBase $dir.FullName -TargetBase $targetDir + + $entries.Add((Get-SyncReportEntry -Name $copilotSkillName -Detail ("{0,2} resource file(s)" -f @($resources).Count))) + } + + if ($ReadOnlyCheck -and -not $SelectedSkill -and (Test-Path $skillsMirrorRoot)) { + foreach ($dir in Get-ChildItem $skillsMirrorRoot -Directory) { + $skillFile = Join-Path $dir.FullName 'SKILL.md' + if (-not (Test-Path $skillFile)) { continue } + if ((Get-Content $skillFile -Raw) -notmatch '" + '' + ) -join "`n" + + return $frontmatter + $body +} + +function ConvertTo-CopilotSkill { + param( + [string]$SourceDocument, + [string]$SkillName, + [string]$SourceRelPath + ) + + $description = Get-FrontmatterValue -Document $SourceDocument -Key 'description' + if (-not $description) { $description = "Repo skill mirrored from $SourceRelPath." } + $version = Get-FrontmatterValue -Document $SourceDocument -Key 'version' + + $body = Get-SkillBody -Document $SourceDocument + $frontmatter = @( + '---' + "name: $(ConvertTo-YamlScalar $SkillName)" + "description: $(ConvertTo-YamlScalar $description)" + $(if ($version) { "version: $(ConvertTo-YamlScalar $version)" }) + '---' + '' + "" + '' + ) -join "`n" + + return $frontmatter + $body +} + +function Invoke-InstallGitHooksInternal { + param([string]$ResolvedRepoRoot) + + $hooksDir = Join-Path $ResolvedRepoRoot '.githooks' + if (Test-Path $hooksDir) { + $utf8NoBom = New-Object System.Text.UTF8Encoding($false) + Get-ChildItem -Path $hooksDir -File | ForEach-Object { + $raw = [System.IO.File]::ReadAllText($_.FullName) + $normalized = $raw -replace "`r`n", "`n" -replace "`r", "`n" + if ($normalized -ne $raw) { + [System.IO.File]::WriteAllText($_.FullName, $normalized, $utf8NoBom) + Write-Output "Normalized LF line endings: $($_.Name)" + } + } + } + + Write-Output '== Git hooks ==' + git config core.hooksPath .githooks + Write-Output 'Configured core.hooksPath to .githooks' + + if ($IsLinux -or $IsMacOS) { + $preCommit = Join-Path $hooksDir 'pre-commit' + if (Test-Path $preCommit) { + & chmod +x $preCommit + } + } + + Write-Output 'Git hooks are now active for this clone.' +} + +function Sync-ClaudeSkillsInternal { + param( + [System.IO.DirectoryInfo[]]$SkillDirs, + [string]$ResolvedRepoRoot, + [string]$ScopeName, + [switch]$OnlyIfMissing, + [switch]$ReadOnlyCheck + ) + + $commandsRoot = Get-MirrorRootPath -TargetName 'Claude' -ScopeName $ScopeName -ResolvedRepoRoot $ResolvedRepoRoot + $entries = [System.Collections.Generic.List[object]]::new() + $driftCount = 0 + $script:LastSyncExitCode = 0 + + Write-Output '' + Write-Output '== Claude Code skill mirror ==' + + foreach ($dir in $SkillDirs) { + $name = $dir.Name + $group = $dir.Parent.Name + $groupDir = Join-Path $commandsRoot $group + $targetMd = Join-Path $groupDir "$name.md" + $targetRes = Join-Path $groupDir $name + + if ($OnlyIfMissing -and (Test-Path $targetMd)) { + $entries.Add((Get-SyncReportEntry -Name ("/{0}:{1}" -f $group, $name) -Detail 'present - skipped (-IfMissing)')) + continue + } + + $resources = Get-SkillResources -SkillDir $dir.FullName + $resRelPaths = $resources | ForEach-Object { + (Get-RelativePathText -BasePath $dir.FullName -TargetPath $_.FullName).Replace('\\', '/') + } + + $body = Get-Content (Join-Path $dir.FullName 'SKILL.md') -Raw + if ($resRelPaths) { + $body = Convert-ResourceLinks -Body $body -Name $name -ResourceRelPaths $resRelPaths + } + + if ($ReadOnlyCheck) { + $state = Get-SkillMirrorState -TargetSkillPath $targetMd -ExpectedSkill $body -Resources $resources -SourceBase $dir.FullName -TargetResourceBase $targetRes + if ($state -ne 'UP-TO-DATE') { $driftCount++ } + $entries.Add((Get-SyncReportEntry -Name ("/{0}:{1}" -f $group, $name) -Detail $state)) + continue + } + + if (Test-Path $targetMd) { Remove-Item $targetMd -Force } + if (Test-Path $targetRes) { Remove-Item $targetRes -Recurse -Force } + New-Item -ItemType Directory -Path $groupDir -Force | Out-Null + Set-Content -Path $targetMd -Value $body -Encoding utf8 -NoNewline + Copy-MirroredResources -Resources $resources -SourceBase $dir.FullName -TargetBase $targetRes + + $entries.Add((Get-SyncReportEntry -Name ("/{0}:{1}" -f $group, $name) -Detail ("{0,2} resource file(s)" -f @($resources).Count))) + } + + Write-SyncReport -Flavor 'Claude skills' -TargetPath $commandsRoot -Entries $entries -Scope $ScopeName + if ($ReadOnlyCheck -and $driftCount) { + $script:LastSyncExitCode = 1 + } +} + +function Sync-CodexSkillsInternal { + param( + [System.IO.DirectoryInfo[]]$SkillDirs, + [string]$ResolvedRepoRoot, + [string]$ScopeName, + [switch]$OnlyIfMissing, + [switch]$ReadOnlyCheck, + [string]$SelectedSkill + ) + + $agentsRoot = Get-MirrorRootPath -TargetName 'Codex' -ScopeName $ScopeName -ResolvedRepoRoot $ResolvedRepoRoot + $entries = [System.Collections.Generic.List[object]]::new() + $expectedSkillNames = [System.Collections.Generic.HashSet[string]]::new() + $driftCount = 0 + $script:LastSyncExitCode = 0 + + if (-not $ReadOnlyCheck -and -not $OnlyIfMissing -and -not $SelectedSkill -and (Test-Path $agentsRoot)) { + Remove-Item $agentsRoot -Recurse -Force + } + + Write-Output '' + Write-Output '== Codex skill mirror ==' + + foreach ($dir in $SkillDirs) { + $sourceName = $dir.Name + $group = $dir.Parent.Name + $codexName = "$group`_$sourceName" + [void]$expectedSkillNames.Add($codexName) + $targetDir = Join-Path $agentsRoot $codexName + $targetSkill = Join-Path $targetDir 'SKILL.md' + + if ($OnlyIfMissing -and (Test-Path $targetSkill)) { + $entries.Add((Get-SyncReportEntry -Name $codexName -Detail 'present - skipped (-IfMissing)')) + continue + } + + $sourceRelPath = "ai-artifacts/skills/shared/$group/$sourceName/SKILL.md" + $sourceDocument = Get-Content (Join-Path $dir.FullName 'SKILL.md') -Raw + $expectedSkill = ConvertTo-CodexSkill -SourceDocument $sourceDocument -CodexName $codexName -SourceRelPath $sourceRelPath + $resources = Get-SkillResources -SkillDir $dir.FullName + + if ($ReadOnlyCheck) { + $state = Get-SkillMirrorState -TargetSkillPath $targetSkill -ExpectedSkill $expectedSkill -Resources $resources -SourceBase $dir.FullName -TargetResourceBase $targetDir -ExcludeTargetSkillFile + if ($state -ne 'UP-TO-DATE') { $driftCount++ } + $entries.Add((Get-SyncReportEntry -Name $codexName -Detail $state)) + continue + } + + if (Test-Path $targetDir) { Remove-Item $targetDir -Recurse -Force } + New-Item -ItemType Directory -Path $targetDir -Force | Out-Null + Set-Content -Path $targetSkill -Value $expectedSkill -Encoding utf8 -NoNewline + Copy-MirroredResources -Resources $resources -SourceBase $dir.FullName -TargetBase $targetDir + + $entries.Add((Get-SyncReportEntry -Name $codexName -Detail ("{0,2} resource file(s)" -f @($resources).Count))) + } + + if ($ReadOnlyCheck -and -not $SelectedSkill -and (Test-Path $agentsRoot)) { + foreach ($dir in Get-ChildItem $agentsRoot -Directory) { + if (-not $expectedSkillNames.Contains($dir.Name)) { + $driftCount++ + $entries.Add((Get-SyncReportEntry -Name $dir.Name -Detail 'EXTRA')) + } + } + } + + Write-SyncReport -Flavor 'Codex skills' -TargetPath $agentsRoot -Entries $entries -Scope $ScopeName + if ($ReadOnlyCheck -and $driftCount) { + $script:LastSyncExitCode = 1 + } +} + +function Sync-CopilotSkillsInternal { + param( + [System.IO.DirectoryInfo[]]$SkillDirs, + [string]$ResolvedRepoRoot, + [string]$ScopeName, + [switch]$OnlyIfMissing, + [switch]$ReadOnlyCheck, + [string]$SelectedSkill + ) + + $skillsMirrorRoot = Get-MirrorRootPath -TargetName 'Copilot' -ScopeName $ScopeName -ResolvedRepoRoot $ResolvedRepoRoot + $entries = [System.Collections.Generic.List[object]]::new() + $expectedSkillNames = [System.Collections.Generic.HashSet[string]]::new() + $driftCount = 0 + $script:LastSyncExitCode = 0 + + if (-not $ReadOnlyCheck -and -not $OnlyIfMissing -and -not $SelectedSkill -and (Test-Path $skillsMirrorRoot)) { + Remove-Item $skillsMirrorRoot -Recurse -Force + } + + Write-Output '' + Write-Output '== Copilot skill mirror ==' + + foreach ($dir in $SkillDirs) { + $sourceName = $dir.Name + $group = $dir.Parent.Name + $copilotSkillName = "$group`_$sourceName" + [void]$expectedSkillNames.Add($copilotSkillName) + $targetDir = Join-Path $skillsMirrorRoot $copilotSkillName + $targetSkill = Join-Path $targetDir 'SKILL.md' + + if ($OnlyIfMissing -and (Test-Path $targetSkill)) { + $entries.Add((Get-SyncReportEntry -Name $copilotSkillName -Detail 'present - skipped (-IfMissing)')) + continue + } + + $sourceRelPath = "ai-artifacts/skills/shared/$group/$sourceName/SKILL.md" + $sourceDocument = Get-Content (Join-Path $dir.FullName 'SKILL.md') -Raw + $expectedSkill = ConvertTo-CopilotSkill -SourceDocument $sourceDocument -SkillName $copilotSkillName -SourceRelPath $sourceRelPath + $resources = Get-SkillResources -SkillDir $dir.FullName + + if ($ReadOnlyCheck) { + $state = Get-SkillMirrorState -TargetSkillPath $targetSkill -ExpectedSkill $expectedSkill -Resources $resources -SourceBase $dir.FullName -TargetResourceBase $targetDir -ExcludeTargetSkillFile + if ($state -ne 'UP-TO-DATE') { $driftCount++ } + $entries.Add((Get-SyncReportEntry -Name $copilotSkillName -Detail $state)) + continue + } + + if (Test-Path $targetDir) { Remove-Item $targetDir -Recurse -Force } + New-Item -ItemType Directory -Path $targetDir -Force | Out-Null + Set-Content -Path $targetSkill -Value $expectedSkill -Encoding utf8 -NoNewline + Copy-MirroredResources -Resources $resources -SourceBase $dir.FullName -TargetBase $targetDir + + $entries.Add((Get-SyncReportEntry -Name $copilotSkillName -Detail ("{0,2} resource file(s)" -f @($resources).Count))) + } + + if ($ReadOnlyCheck -and -not $SelectedSkill -and (Test-Path $skillsMirrorRoot)) { + foreach ($dir in Get-ChildItem $skillsMirrorRoot -Directory) { + $skillFile = Join-Path $dir.FullName 'SKILL.md' + if (-not (Test-Path $skillFile)) { continue } + if ((Get-Content $skillFile -Raw) -notmatch ' +# Setup Repo + +Bootstraps this clone in one pass: + +1. Activates repo git hooks +2. Syncs generated skill mirrors for Claude, Codex, and Copilot + +Implementation layout: + +- Canonical entrypoint: `ai-artifacts/skills/shared/setup/setup-repo/scripts/Invoke-SetupRepo.ps1` +- Repo convenience wrapper: `scripts/setup-repo.ps1` + +## Applicability (capability contract) + +This skill needs shell + filesystem access in a local git checkout. In chat-only environments, +provide the exact commands and explain what each command configures. + +## Default command + +```powershell +pwsh scripts/setup-repo.ps1 +``` + +## Common variants + +Project-local mirrors for one harness: + +```powershell +pwsh scripts/setup-repo.ps1 -Target Copilot -Scope Project +``` + +User-scope mirrors for all harnesses: + +```powershell +pwsh scripts/setup-repo.ps1 -Scope User +``` + +Bootstrap only missing generated mirrors (keeps existing generated files untouched): + +```powershell +pwsh scripts/setup-repo.ps1 -IfMissing +``` + +Refresh generated mirrors without touching git hooks: + +```powershell +pwsh scripts/setup-repo.ps1 -SkipHooks +``` + +Check generated mirrors for drift: + +```powershell +pwsh scripts/setup-repo.ps1 -SkipHooks -Check +pwsh scripts/setup-repo.ps1 -SkipHooks -Target Codex -Skill tdd -Check +``` + +Run only one phase: + +```powershell +pwsh scripts/setup-repo.ps1 -SkipHooks +pwsh scripts/setup-repo.ps1 -SkipSkillSync +``` + +## Verify + +```powershell +git config --get core.hooksPath +Test-Path .claude/commands +Test-Path .agents/skills +Test-Path .github/skills +pwsh scripts/setup-repo.ps1 -SkipHooks -Check +``` + +Expected: + +- `core.hooksPath` prints `.githooks` +- mirror folders exist for selected targets diff --git a/.github/skills/setup_setup-repo/scripts/Invoke-SetupRepo.ps1 b/.github/skills/setup_setup-repo/scripts/Invoke-SetupRepo.ps1 new file mode 100644 index 0000000..d1c0f05 --- /dev/null +++ b/.github/skills/setup_setup-repo/scripts/Invoke-SetupRepo.ps1 @@ -0,0 +1,584 @@ +#Requires -Version 7.0 +#Requires -PSEdition Core +# RuntimePolicy: core-first + +[CmdletBinding()] +param( + [string]$RepoRoot = "", + + [ValidateSet('All', 'Claude', 'Codex', 'Copilot')] + [string]$Target = 'All', + + [Alias('Scope')] + [ValidateSet('Project', 'User')] + [string]$MirrorScope = 'Project', + + [string]$Skill = '', + + [switch]$IfMissing, + [switch]$Check, + [switch]$SkipHooks, + [switch]$SkipSkillSync +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +if ($Check -and $IfMissing) { + throw "-Check is read-only; do not combine it with -IfMissing." +} + +function Get-UserHomePath { + if ($env:HOME) { return $env:HOME } + if ($env:USERPROFILE) { return $env:USERPROFILE } + throw "Cannot resolve user home path from HOME/USERPROFILE." +} + +function Get-RelativePathText { + param( + [string]$BasePath, + [string]$TargetPath + ) + + $rel = [System.IO.Path]::GetRelativePath($BasePath, $TargetPath) + return $rel.TrimStart('\', '/') +} + +function Get-MirrorRootPath { + param( + [ValidateSet('Claude', 'Codex', 'Copilot')] + [string]$TargetName, + + [ValidateSet('Project', 'User')] + [string]$ScopeName, + + [string]$ResolvedRepoRoot + ) + + switch ("$TargetName|$ScopeName") { + 'Claude|Project' { return (Join-Path $ResolvedRepoRoot '.claude/commands') } + 'Claude|User' { return (Join-Path (Get-UserHomePath) '.claude/commands') } + 'Codex|Project' { return (Join-Path $ResolvedRepoRoot '.agents/skills') } + 'Codex|User' { return (Join-Path (Get-UserHomePath) '.codex/skills') } + 'Copilot|Project' { return (Join-Path $ResolvedRepoRoot '.github/skills') } + 'Copilot|User' { return (Join-Path (Get-UserHomePath) '.copilot/skills') } + default { throw "Unsupported mirror target/scope combination: $TargetName / $ScopeName" } + } +} + +function Get-SkillDirectories { + param( + [string]$SkillsRoot, + [string]$SkillName + ) + + $dirs = @(Get-ChildItem $SkillsRoot -Recurse -Filter 'SKILL.md' -File | ForEach-Object { $_.Directory }) + if ($SkillName) { + $dirs = @($dirs | Where-Object { $_.Name -eq $SkillName }) + if ($dirs.Count -eq 0) { + throw "No SKILL.md found for skill '$SkillName' under $SkillsRoot" + } + } + if ($dirs.Count -eq 0) { + throw "No SKILL.md files found under $SkillsRoot" + } + return $dirs +} + +function Get-SyncReportEntry { + param( + [string]$Name, + [string]$Detail + ) + + return [PSCustomObject]@{ + Name = $Name + Detail = $Detail + } +} + +function Write-SyncReport { + param( + [string]$Flavor, + [string]$TargetPath, + [object[]]$Entries, + [string]$Scope + ) + + $scopeText = if ($Scope) { " (scope=$Scope)" } else { '' } + Write-Output "Synced $Flavor -> $TargetPath$scopeText" + + if (-not $Entries -or @($Entries).Count -eq 0) { + return + } + + $nameWidth = ($Entries | ForEach-Object { $_.Name.Length } | Measure-Object -Maximum).Maximum + if (-not $nameWidth) { $nameWidth = 0 } + + foreach ($entry in $Entries) { + Write-Output (" {0} {1}" -f $entry.Name.PadRight($nameWidth), $entry.Detail) + } +} + +function Get-SkillResources { + param([string]$SkillDir) + + return @(Get-ChildItem $SkillDir -Recurse -File | + Where-Object { $_.Name -notin @('SKILL.md', 'METADATA.md') }) +} + +function Copy-MirroredResources { + param( + [System.IO.FileInfo[]]$Resources, + [string]$SourceBase, + [string]$TargetBase + ) + + foreach ($res in $Resources) { + $rel = Get-RelativePathText -BasePath $SourceBase -TargetPath $res.FullName + $dest = Join-Path $TargetBase $rel + New-Item -ItemType Directory -Path (Split-Path $dest -Parent) -Force | Out-Null + Copy-Item $res.FullName $dest -Force + } +} + +function Test-MirroredResourcesMatch { + param( + [System.IO.FileInfo[]]$Resources, + [string]$SourceBase, + [string]$TargetBase, + [switch]$ExcludeTargetSkillFile + ) + + foreach ($res in $Resources) { + $rel = Get-RelativePathText -BasePath $SourceBase -TargetPath $res.FullName + $dest = Join-Path $TargetBase $rel + if (-not (Test-Path $dest) -or + (Get-FileHash $res.FullName).Hash -ne (Get-FileHash $dest).Hash) { + return $false + } + } + + $resourceCount = @($Resources).Count + $mirroredCount = if (Test-Path $TargetBase) { + @(Get-ChildItem $TargetBase -Recurse -File | + Where-Object { -not $ExcludeTargetSkillFile -or $_.Name -ne 'SKILL.md' }).Count + } + else { + 0 + } + + return $mirroredCount -eq $resourceCount +} + +function Get-SkillMirrorState { + param( + [string]$TargetSkillPath, + [string]$ExpectedSkill, + [System.IO.FileInfo[]]$Resources, + [string]$SourceBase, + [string]$TargetResourceBase, + [switch]$ExcludeTargetSkillFile + ) + + if (-not (Test-Path $TargetSkillPath)) { return 'MISSING' } + if ((Get-Content $TargetSkillPath -Raw) -cne $ExpectedSkill) { return 'STALE' } + + if (-not (Test-MirroredResourcesMatch -Resources $Resources -SourceBase $SourceBase -TargetBase $TargetResourceBase -ExcludeTargetSkillFile:$ExcludeTargetSkillFile)) { + return 'STALE' + } + + return 'UP-TO-DATE' +} + +function Convert-ResourceLinks { + param( + [string]$Body, + [string]$Name, + [string[]]$ResourceRelPaths + ) + + foreach ($path in $ResourceRelPaths) { + $Body = $Body.Replace("](./$path)", "]($Name/$path)") + $Body = $Body.Replace("]($path)", "]($Name/$path)") + $Body = $Body -replace ("(?<=@)" + [regex]::Escape($path) + "\b"), "$Name/$path" + } + return $Body +} + +function ConvertTo-YamlScalar { + param([string]$Value) + + if ($null -eq $Value) { return '""' } + return '"' + ($Value -replace '\\', '\\' -replace '"', '\"') + '"' +} + +function Get-FrontmatterValue { + param( + [string]$Document, + [string]$Key + ) + + if ($Document -notmatch "(?s)\A---\r?\n(.*?)\r?\n---\r?\n") { return $null } + $frontmatter = $Matches[1] + $match = [regex]::Match($frontmatter, "(?m)^$([regex]::Escape($Key)):\s*(.*)$") + if (-not $match.Success) { return $null } + + $value = $match.Groups[1].Value.Trim() + if (($value.StartsWith('"') -and $value.EndsWith('"')) -or + ($value.StartsWith("'") -and $value.EndsWith("'"))) { + $value = $value.Substring(1, $value.Length - 2) + } + return $value +} + +function Get-SkillBody { + param([string]$Document) + + if ($Document -match "(?s)\A---\r?\n.*?\r?\n---\r?\n(.*)\z") { + return $Matches[1].TrimStart() + } + return $Document.TrimStart() +} + +function ConvertTo-CodexSkill { + param( + [string]$SourceDocument, + [string]$CodexName, + [string]$SourceRelPath + ) + + $description = Get-FrontmatterValue -Document $SourceDocument -Key 'description' + if (-not $description) { $description = "Repo skill mirrored from $SourceRelPath." } + $version = Get-FrontmatterValue -Document $SourceDocument -Key 'version' + + $body = Get-SkillBody -Document $SourceDocument + $frontmatter = @( + '---' + "name: $(ConvertTo-YamlScalar $CodexName)" + "description: $(ConvertTo-YamlScalar $description)" + $(if ($version) { "version: $(ConvertTo-YamlScalar $version)" }) + '---' + '' + "" + '' + ) -join "`n" + + return $frontmatter + $body +} + +function ConvertTo-CopilotSkill { + param( + [string]$SourceDocument, + [string]$SkillName, + [string]$SourceRelPath + ) + + $description = Get-FrontmatterValue -Document $SourceDocument -Key 'description' + if (-not $description) { $description = "Repo skill mirrored from $SourceRelPath." } + $version = Get-FrontmatterValue -Document $SourceDocument -Key 'version' + + $body = Get-SkillBody -Document $SourceDocument + $frontmatter = @( + '---' + "name: $(ConvertTo-YamlScalar $SkillName)" + "description: $(ConvertTo-YamlScalar $description)" + $(if ($version) { "version: $(ConvertTo-YamlScalar $version)" }) + '---' + '' + "" + '' + ) -join "`n" + + return $frontmatter + $body +} + +function Invoke-InstallGitHooksInternal { + param([string]$ResolvedRepoRoot) + + $hooksDir = Join-Path $ResolvedRepoRoot '.githooks' + if (Test-Path $hooksDir) { + $utf8NoBom = New-Object System.Text.UTF8Encoding($false) + Get-ChildItem -Path $hooksDir -File | ForEach-Object { + $raw = [System.IO.File]::ReadAllText($_.FullName) + $normalized = $raw -replace "`r`n", "`n" -replace "`r", "`n" + if ($normalized -ne $raw) { + [System.IO.File]::WriteAllText($_.FullName, $normalized, $utf8NoBom) + Write-Output "Normalized LF line endings: $($_.Name)" + } + } + } + + Write-Output '== Git hooks ==' + git config core.hooksPath .githooks + Write-Output 'Configured core.hooksPath to .githooks' + + if ($IsLinux -or $IsMacOS) { + $preCommit = Join-Path $hooksDir 'pre-commit' + if (Test-Path $preCommit) { + & chmod +x $preCommit + } + } + + Write-Output 'Git hooks are now active for this clone.' +} + +function Sync-ClaudeSkillsInternal { + param( + [System.IO.DirectoryInfo[]]$SkillDirs, + [string]$ResolvedRepoRoot, + [string]$ScopeName, + [switch]$OnlyIfMissing, + [switch]$ReadOnlyCheck + ) + + $commandsRoot = Get-MirrorRootPath -TargetName 'Claude' -ScopeName $ScopeName -ResolvedRepoRoot $ResolvedRepoRoot + $entries = [System.Collections.Generic.List[object]]::new() + $driftCount = 0 + $script:LastSyncExitCode = 0 + + Write-Output '' + Write-Output '== Claude Code skill mirror ==' + + foreach ($dir in $SkillDirs) { + $name = $dir.Name + $group = $dir.Parent.Name + $groupDir = Join-Path $commandsRoot $group + $targetMd = Join-Path $groupDir "$name.md" + $targetRes = Join-Path $groupDir $name + + if ($OnlyIfMissing -and (Test-Path $targetMd)) { + $entries.Add((Get-SyncReportEntry -Name ("/{0}:{1}" -f $group, $name) -Detail 'present - skipped (-IfMissing)')) + continue + } + + $resources = Get-SkillResources -SkillDir $dir.FullName + $resRelPaths = $resources | ForEach-Object { + (Get-RelativePathText -BasePath $dir.FullName -TargetPath $_.FullName).Replace('\\', '/') + } + + $body = Get-Content (Join-Path $dir.FullName 'SKILL.md') -Raw + if ($resRelPaths) { + $body = Convert-ResourceLinks -Body $body -Name $name -ResourceRelPaths $resRelPaths + } + + if ($ReadOnlyCheck) { + $state = Get-SkillMirrorState -TargetSkillPath $targetMd -ExpectedSkill $body -Resources $resources -SourceBase $dir.FullName -TargetResourceBase $targetRes + if ($state -ne 'UP-TO-DATE') { $driftCount++ } + $entries.Add((Get-SyncReportEntry -Name ("/{0}:{1}" -f $group, $name) -Detail $state)) + continue + } + + if (Test-Path $targetMd) { Remove-Item $targetMd -Force } + if (Test-Path $targetRes) { Remove-Item $targetRes -Recurse -Force } + New-Item -ItemType Directory -Path $groupDir -Force | Out-Null + Set-Content -Path $targetMd -Value $body -Encoding utf8 -NoNewline + Copy-MirroredResources -Resources $resources -SourceBase $dir.FullName -TargetBase $targetRes + + $entries.Add((Get-SyncReportEntry -Name ("/{0}:{1}" -f $group, $name) -Detail ("{0,2} resource file(s)" -f @($resources).Count))) + } + + Write-SyncReport -Flavor 'Claude skills' -TargetPath $commandsRoot -Entries $entries -Scope $ScopeName + if ($ReadOnlyCheck -and $driftCount) { + $script:LastSyncExitCode = 1 + } +} + +function Sync-CodexSkillsInternal { + param( + [System.IO.DirectoryInfo[]]$SkillDirs, + [string]$ResolvedRepoRoot, + [string]$ScopeName, + [switch]$OnlyIfMissing, + [switch]$ReadOnlyCheck, + [string]$SelectedSkill + ) + + $agentsRoot = Get-MirrorRootPath -TargetName 'Codex' -ScopeName $ScopeName -ResolvedRepoRoot $ResolvedRepoRoot + $entries = [System.Collections.Generic.List[object]]::new() + $expectedSkillNames = [System.Collections.Generic.HashSet[string]]::new() + $driftCount = 0 + $script:LastSyncExitCode = 0 + + if (-not $ReadOnlyCheck -and -not $OnlyIfMissing -and -not $SelectedSkill -and (Test-Path $agentsRoot)) { + Remove-Item $agentsRoot -Recurse -Force + } + + Write-Output '' + Write-Output '== Codex skill mirror ==' + + foreach ($dir in $SkillDirs) { + $sourceName = $dir.Name + $group = $dir.Parent.Name + $codexName = "$group`_$sourceName" + [void]$expectedSkillNames.Add($codexName) + $targetDir = Join-Path $agentsRoot $codexName + $targetSkill = Join-Path $targetDir 'SKILL.md' + + if ($OnlyIfMissing -and (Test-Path $targetSkill)) { + $entries.Add((Get-SyncReportEntry -Name $codexName -Detail 'present - skipped (-IfMissing)')) + continue + } + + $sourceRelPath = "ai-artifacts/skills/shared/$group/$sourceName/SKILL.md" + $sourceDocument = Get-Content (Join-Path $dir.FullName 'SKILL.md') -Raw + $expectedSkill = ConvertTo-CodexSkill -SourceDocument $sourceDocument -CodexName $codexName -SourceRelPath $sourceRelPath + $resources = Get-SkillResources -SkillDir $dir.FullName + + if ($ReadOnlyCheck) { + $state = Get-SkillMirrorState -TargetSkillPath $targetSkill -ExpectedSkill $expectedSkill -Resources $resources -SourceBase $dir.FullName -TargetResourceBase $targetDir -ExcludeTargetSkillFile + if ($state -ne 'UP-TO-DATE') { $driftCount++ } + $entries.Add((Get-SyncReportEntry -Name $codexName -Detail $state)) + continue + } + + if (Test-Path $targetDir) { Remove-Item $targetDir -Recurse -Force } + New-Item -ItemType Directory -Path $targetDir -Force | Out-Null + Set-Content -Path $targetSkill -Value $expectedSkill -Encoding utf8 -NoNewline + Copy-MirroredResources -Resources $resources -SourceBase $dir.FullName -TargetBase $targetDir + + $entries.Add((Get-SyncReportEntry -Name $codexName -Detail ("{0,2} resource file(s)" -f @($resources).Count))) + } + + if ($ReadOnlyCheck -and -not $SelectedSkill -and (Test-Path $agentsRoot)) { + foreach ($dir in Get-ChildItem $agentsRoot -Directory) { + if (-not $expectedSkillNames.Contains($dir.Name)) { + $driftCount++ + $entries.Add((Get-SyncReportEntry -Name $dir.Name -Detail 'EXTRA')) + } + } + } + + Write-SyncReport -Flavor 'Codex skills' -TargetPath $agentsRoot -Entries $entries -Scope $ScopeName + if ($ReadOnlyCheck -and $driftCount) { + $script:LastSyncExitCode = 1 + } +} + +function Sync-CopilotSkillsInternal { + param( + [System.IO.DirectoryInfo[]]$SkillDirs, + [string]$ResolvedRepoRoot, + [string]$ScopeName, + [switch]$OnlyIfMissing, + [switch]$ReadOnlyCheck, + [string]$SelectedSkill + ) + + $skillsMirrorRoot = Get-MirrorRootPath -TargetName 'Copilot' -ScopeName $ScopeName -ResolvedRepoRoot $ResolvedRepoRoot + $entries = [System.Collections.Generic.List[object]]::new() + $expectedSkillNames = [System.Collections.Generic.HashSet[string]]::new() + $driftCount = 0 + $script:LastSyncExitCode = 0 + + if (-not $ReadOnlyCheck -and -not $OnlyIfMissing -and -not $SelectedSkill -and (Test-Path $skillsMirrorRoot)) { + Remove-Item $skillsMirrorRoot -Recurse -Force + } + + Write-Output '' + Write-Output '== Copilot skill mirror ==' + + foreach ($dir in $SkillDirs) { + $sourceName = $dir.Name + $group = $dir.Parent.Name + $copilotSkillName = "$group`_$sourceName" + [void]$expectedSkillNames.Add($copilotSkillName) + $targetDir = Join-Path $skillsMirrorRoot $copilotSkillName + $targetSkill = Join-Path $targetDir 'SKILL.md' + + if ($OnlyIfMissing -and (Test-Path $targetSkill)) { + $entries.Add((Get-SyncReportEntry -Name $copilotSkillName -Detail 'present - skipped (-IfMissing)')) + continue + } + + $sourceRelPath = "ai-artifacts/skills/shared/$group/$sourceName/SKILL.md" + $sourceDocument = Get-Content (Join-Path $dir.FullName 'SKILL.md') -Raw + $expectedSkill = ConvertTo-CopilotSkill -SourceDocument $sourceDocument -SkillName $copilotSkillName -SourceRelPath $sourceRelPath + $resources = Get-SkillResources -SkillDir $dir.FullName + + if ($ReadOnlyCheck) { + $state = Get-SkillMirrorState -TargetSkillPath $targetSkill -ExpectedSkill $expectedSkill -Resources $resources -SourceBase $dir.FullName -TargetResourceBase $targetDir -ExcludeTargetSkillFile + if ($state -ne 'UP-TO-DATE') { $driftCount++ } + $entries.Add((Get-SyncReportEntry -Name $copilotSkillName -Detail $state)) + continue + } + + if (Test-Path $targetDir) { Remove-Item $targetDir -Recurse -Force } + New-Item -ItemType Directory -Path $targetDir -Force | Out-Null + Set-Content -Path $targetSkill -Value $expectedSkill -Encoding utf8 -NoNewline + Copy-MirroredResources -Resources $resources -SourceBase $dir.FullName -TargetBase $targetDir + + $entries.Add((Get-SyncReportEntry -Name $copilotSkillName -Detail ("{0,2} resource file(s)" -f @($resources).Count))) + } + + if ($ReadOnlyCheck -and -not $SelectedSkill -and (Test-Path $skillsMirrorRoot)) { + foreach ($dir in Get-ChildItem $skillsMirrorRoot -Directory) { + $skillFile = Join-Path $dir.FullName 'SKILL.md' + if (-not (Test-Path $skillFile)) { continue } + if ((Get-Content $skillFile -Raw) -notmatch '" + '' + ) -join "`n" + + return $frontmatter + $body +} + +function ConvertTo-CopilotSkill { + param( + [string]$SourceDocument, + [string]$SkillName, + [string]$SourceRelPath + ) + + $description = Get-FrontmatterValue -Document $SourceDocument -Key 'description' + if (-not $description) { $description = "Repo skill mirrored from $SourceRelPath." } + $version = Get-FrontmatterValue -Document $SourceDocument -Key 'version' + + $body = Get-SkillBody -Document $SourceDocument + $frontmatter = @( + '---' + "name: $(ConvertTo-YamlScalar $SkillName)" + "description: $(ConvertTo-YamlScalar $description)" + $(if ($version) { "version: $(ConvertTo-YamlScalar $version)" }) + '---' + '' + "" + '' + ) -join "`n" + + return $frontmatter + $body +} + +function Invoke-InstallGitHooksInternal { + param([string]$ResolvedRepoRoot) + + $hooksDir = Join-Path $ResolvedRepoRoot '.githooks' + if (Test-Path $hooksDir) { + $utf8NoBom = New-Object System.Text.UTF8Encoding($false) + Get-ChildItem -Path $hooksDir -File | ForEach-Object { + $raw = [System.IO.File]::ReadAllText($_.FullName) + $normalized = $raw -replace "`r`n", "`n" -replace "`r", "`n" + if ($normalized -ne $raw) { + [System.IO.File]::WriteAllText($_.FullName, $normalized, $utf8NoBom) + Write-Output "Normalized LF line endings: $($_.Name)" + } + } + } + + Write-Output '== Git hooks ==' + git config core.hooksPath .githooks + Write-Output 'Configured core.hooksPath to .githooks' + + if ($IsLinux -or $IsMacOS) { + $preCommit = Join-Path $hooksDir 'pre-commit' + if (Test-Path $preCommit) { + & chmod +x $preCommit + } + } + + Write-Output 'Git hooks are now active for this clone.' +} + +function Sync-ClaudeSkillsInternal { + param( + [System.IO.DirectoryInfo[]]$SkillDirs, + [string]$ResolvedRepoRoot, + [string]$ScopeName, + [switch]$OnlyIfMissing, + [switch]$ReadOnlyCheck + ) + + $commandsRoot = Get-MirrorRootPath -TargetName 'Claude' -ScopeName $ScopeName -ResolvedRepoRoot $ResolvedRepoRoot + $entries = [System.Collections.Generic.List[object]]::new() + $driftCount = 0 + $script:LastSyncExitCode = 0 + + Write-Output '' + Write-Output '== Claude Code skill mirror ==' + + foreach ($dir in $SkillDirs) { + $name = $dir.Name + $group = $dir.Parent.Name + $groupDir = Join-Path $commandsRoot $group + $targetMd = Join-Path $groupDir "$name.md" + $targetRes = Join-Path $groupDir $name + + if ($OnlyIfMissing -and (Test-Path $targetMd)) { + $entries.Add((Get-SyncReportEntry -Name ("/{0}:{1}" -f $group, $name) -Detail 'present - skipped (-IfMissing)')) + continue + } + + $resources = Get-SkillResources -SkillDir $dir.FullName + $resRelPaths = $resources | ForEach-Object { + (Get-RelativePathText -BasePath $dir.FullName -TargetPath $_.FullName).Replace('\\', '/') + } + + $body = Get-Content (Join-Path $dir.FullName 'SKILL.md') -Raw + if ($resRelPaths) { + $body = Convert-ResourceLinks -Body $body -Name $name -ResourceRelPaths $resRelPaths + } + + if ($ReadOnlyCheck) { + $state = Get-SkillMirrorState -TargetSkillPath $targetMd -ExpectedSkill $body -Resources $resources -SourceBase $dir.FullName -TargetResourceBase $targetRes + if ($state -ne 'UP-TO-DATE') { $driftCount++ } + $entries.Add((Get-SyncReportEntry -Name ("/{0}:{1}" -f $group, $name) -Detail $state)) + continue + } + + if (Test-Path $targetMd) { Remove-Item $targetMd -Force } + if (Test-Path $targetRes) { Remove-Item $targetRes -Recurse -Force } + New-Item -ItemType Directory -Path $groupDir -Force | Out-Null + Set-Content -Path $targetMd -Value $body -Encoding utf8 -NoNewline + Copy-MirroredResources -Resources $resources -SourceBase $dir.FullName -TargetBase $targetRes + + $entries.Add((Get-SyncReportEntry -Name ("/{0}:{1}" -f $group, $name) -Detail ("{0,2} resource file(s)" -f @($resources).Count))) + } + + Write-SyncReport -Flavor 'Claude skills' -TargetPath $commandsRoot -Entries $entries -Scope $ScopeName + if ($ReadOnlyCheck -and $driftCount) { + $script:LastSyncExitCode = 1 + } +} + +function Sync-CodexSkillsInternal { + param( + [System.IO.DirectoryInfo[]]$SkillDirs, + [string]$ResolvedRepoRoot, + [string]$ScopeName, + [switch]$OnlyIfMissing, + [switch]$ReadOnlyCheck, + [string]$SelectedSkill + ) + + $agentsRoot = Get-MirrorRootPath -TargetName 'Codex' -ScopeName $ScopeName -ResolvedRepoRoot $ResolvedRepoRoot + $entries = [System.Collections.Generic.List[object]]::new() + $expectedSkillNames = [System.Collections.Generic.HashSet[string]]::new() + $driftCount = 0 + $script:LastSyncExitCode = 0 + + if (-not $ReadOnlyCheck -and -not $OnlyIfMissing -and -not $SelectedSkill -and (Test-Path $agentsRoot)) { + Remove-Item $agentsRoot -Recurse -Force + } + + Write-Output '' + Write-Output '== Codex skill mirror ==' + + foreach ($dir in $SkillDirs) { + $sourceName = $dir.Name + $group = $dir.Parent.Name + $codexName = "$group`_$sourceName" + [void]$expectedSkillNames.Add($codexName) + $targetDir = Join-Path $agentsRoot $codexName + $targetSkill = Join-Path $targetDir 'SKILL.md' + + if ($OnlyIfMissing -and (Test-Path $targetSkill)) { + $entries.Add((Get-SyncReportEntry -Name $codexName -Detail 'present - skipped (-IfMissing)')) + continue + } + + $sourceRelPath = "ai-artifacts/skills/shared/$group/$sourceName/SKILL.md" + $sourceDocument = Get-Content (Join-Path $dir.FullName 'SKILL.md') -Raw + $expectedSkill = ConvertTo-CodexSkill -SourceDocument $sourceDocument -CodexName $codexName -SourceRelPath $sourceRelPath + $resources = Get-SkillResources -SkillDir $dir.FullName + + if ($ReadOnlyCheck) { + $state = Get-SkillMirrorState -TargetSkillPath $targetSkill -ExpectedSkill $expectedSkill -Resources $resources -SourceBase $dir.FullName -TargetResourceBase $targetDir -ExcludeTargetSkillFile + if ($state -ne 'UP-TO-DATE') { $driftCount++ } + $entries.Add((Get-SyncReportEntry -Name $codexName -Detail $state)) + continue + } + + if (Test-Path $targetDir) { Remove-Item $targetDir -Recurse -Force } + New-Item -ItemType Directory -Path $targetDir -Force | Out-Null + Set-Content -Path $targetSkill -Value $expectedSkill -Encoding utf8 -NoNewline + Copy-MirroredResources -Resources $resources -SourceBase $dir.FullName -TargetBase $targetDir + + $entries.Add((Get-SyncReportEntry -Name $codexName -Detail ("{0,2} resource file(s)" -f @($resources).Count))) + } + + if ($ReadOnlyCheck -and -not $SelectedSkill -and (Test-Path $agentsRoot)) { + foreach ($dir in Get-ChildItem $agentsRoot -Directory) { + if (-not $expectedSkillNames.Contains($dir.Name)) { + $driftCount++ + $entries.Add((Get-SyncReportEntry -Name $dir.Name -Detail 'EXTRA')) + } + } + } + + Write-SyncReport -Flavor 'Codex skills' -TargetPath $agentsRoot -Entries $entries -Scope $ScopeName + if ($ReadOnlyCheck -and $driftCount) { + $script:LastSyncExitCode = 1 + } +} + +function Sync-CopilotSkillsInternal { + param( + [System.IO.DirectoryInfo[]]$SkillDirs, + [string]$ResolvedRepoRoot, + [string]$ScopeName, + [switch]$OnlyIfMissing, + [switch]$ReadOnlyCheck, + [string]$SelectedSkill + ) + + $skillsMirrorRoot = Get-MirrorRootPath -TargetName 'Copilot' -ScopeName $ScopeName -ResolvedRepoRoot $ResolvedRepoRoot + $entries = [System.Collections.Generic.List[object]]::new() + $expectedSkillNames = [System.Collections.Generic.HashSet[string]]::new() + $driftCount = 0 + $script:LastSyncExitCode = 0 + + if (-not $ReadOnlyCheck -and -not $OnlyIfMissing -and -not $SelectedSkill -and (Test-Path $skillsMirrorRoot)) { + Remove-Item $skillsMirrorRoot -Recurse -Force + } + + Write-Output '' + Write-Output '== Copilot skill mirror ==' + + foreach ($dir in $SkillDirs) { + $sourceName = $dir.Name + $group = $dir.Parent.Name + $copilotSkillName = "$group`_$sourceName" + [void]$expectedSkillNames.Add($copilotSkillName) + $targetDir = Join-Path $skillsMirrorRoot $copilotSkillName + $targetSkill = Join-Path $targetDir 'SKILL.md' + + if ($OnlyIfMissing -and (Test-Path $targetSkill)) { + $entries.Add((Get-SyncReportEntry -Name $copilotSkillName -Detail 'present - skipped (-IfMissing)')) + continue + } + + $sourceRelPath = "ai-artifacts/skills/shared/$group/$sourceName/SKILL.md" + $sourceDocument = Get-Content (Join-Path $dir.FullName 'SKILL.md') -Raw + $expectedSkill = ConvertTo-CopilotSkill -SourceDocument $sourceDocument -SkillName $copilotSkillName -SourceRelPath $sourceRelPath + $resources = Get-SkillResources -SkillDir $dir.FullName + + if ($ReadOnlyCheck) { + $state = Get-SkillMirrorState -TargetSkillPath $targetSkill -ExpectedSkill $expectedSkill -Resources $resources -SourceBase $dir.FullName -TargetResourceBase $targetDir -ExcludeTargetSkillFile + if ($state -ne 'UP-TO-DATE') { $driftCount++ } + $entries.Add((Get-SyncReportEntry -Name $copilotSkillName -Detail $state)) + continue + } + + if (Test-Path $targetDir) { Remove-Item $targetDir -Recurse -Force } + New-Item -ItemType Directory -Path $targetDir -Force | Out-Null + Set-Content -Path $targetSkill -Value $expectedSkill -Encoding utf8 -NoNewline + Copy-MirroredResources -Resources $resources -SourceBase $dir.FullName -TargetBase $targetDir + + $entries.Add((Get-SyncReportEntry -Name $copilotSkillName -Detail ("{0,2} resource file(s)" -f @($resources).Count))) + } + + if ($ReadOnlyCheck -and -not $SelectedSkill -and (Test-Path $skillsMirrorRoot)) { + foreach ($dir in Get-ChildItem $skillsMirrorRoot -Directory) { + $skillFile = Join-Path $dir.FullName 'SKILL.md' + if (-not (Test-Path $skillFile)) { continue } + if ((Get-Content $skillFile -Raw) -notmatch '" - '' - ) -join "`n" - - return $frontmatter + $body -} - -function Get-CodexCompatibilityWarnings { - param( - [string]$Document, - [string]$SkillName - ) - - $patterns = [ordered]@{ - 'claude-path' = '\.claude|CLAUDE_PROJECT_DIR' - 'claude-command' = '(^|\s)/[A-Za-z0-9_-]+:[A-Za-z0-9_-]+|\$ARGUMENTS|!command' - 'codex-case' = '\.Codex' - 'old-skills-root' = 'shared/skills|shared\\skills|skills//' - } - - foreach ($entry in $patterns.GetEnumerator()) { - if ($Document -match $entry.Value) { "$SkillName $($entry.Key)" } - } -} - -function Sync-ClaudeSkills { - param( - [System.IO.DirectoryInfo[]]$SkillDirs, - [string]$Root, - [string]$ClaudeScope, - [switch]$OnlyIfMissing, - [switch]$ReadOnlyCheck - ) - - $commandsRoot = switch ($ClaudeScope) { - 'Project' { Join-Path $RepoRoot '.claude\commands' } - 'User' { Join-Path $env:USERPROFILE '.claude\commands' } - } - - $report = [System.Text.StringBuilder]::new() - $driftCount = 0 - $script:LastSyncExitCode = 0 - - foreach ($dir in $SkillDirs) { - $name = $dir.Name - $group = $dir.Parent.Name - $groupDir = Join-Path $commandsRoot $group - $targetMd = Join-Path $groupDir "$name.md" - $targetRes = Join-Path $groupDir $name - - if ($OnlyIfMissing -and (Test-Path $targetMd)) { - [void]$report.AppendLine((" /{0}:{1,-18} present - skipped (-IfMissing)" -f $group, $name)) - continue - } - - $resources = @(Get-ChildItem $dir.FullName -Recurse -File | - Where-Object { $_.Name -notin @('SKILL.md', 'METADATA.md') }) - $resRelPaths = $resources | ForEach-Object { - $_.FullName.Substring($dir.FullName.Length).TrimStart('\').Replace('\', '/') - } - - $body = Get-Content (Join-Path $dir.FullName 'SKILL.md') -Raw - if ($resRelPaths) { - $body = Convert-ResourceLinks -Body $body -Name $name -ResourceRelPaths $resRelPaths - } - - if ($ReadOnlyCheck) { - $state = 'UP-TO-DATE' - if (-not (Test-Path $targetMd)) { $state = 'MISSING' } - elseif ((Get-Content $targetMd -Raw) -cne $body) { $state = 'STALE' } - else { - foreach ($res in $resources) { - $rel = $res.FullName.Substring($dir.FullName.Length).TrimStart('\') - $dest = Join-Path $targetRes $rel - if (-not (Test-Path $dest) -or - (Get-FileHash $res.FullName).Hash -ne (Get-FileHash $dest).Hash) { - $state = 'STALE' - break - } - } - if ($state -eq 'UP-TO-DATE') { - $mirrored = if (Test-Path $targetRes) { @(Get-ChildItem $targetRes -Recurse -File).Count } else { 0 } - if ($mirrored -ne $resources.Count) { $state = 'STALE' } - } - } - - if ($state -ne 'UP-TO-DATE') { $driftCount++ } - [void]$report.AppendLine((" /{0}:{1,-18} {2}" -f $group, $name, $state)) - continue - } - - if (Test-Path $targetMd) { Remove-Item $targetMd -Force } - if (Test-Path $targetRes) { Remove-Item $targetRes -Recurse -Force } - New-Item -ItemType Directory -Path $groupDir -Force | Out-Null - Set-Content -Path $targetMd -Value $body -Encoding utf8 -NoNewline - - foreach ($res in $resources) { - $rel = $res.FullName.Substring($dir.FullName.Length).TrimStart('\') - $dest = Join-Path $targetRes $rel - New-Item -ItemType Directory -Path (Split-Path $dest -Parent) -Force | Out-Null - Copy-Item $res.FullName $dest -Force - } - - [void]$report.AppendLine((" /{0}:{1,-18} {2} resource file(s)" -f $group, $name, $resources.Count)) - } - - if ($ReadOnlyCheck) { - Write-Output "Drift check: $Root -> $commandsRoot (target=Claude, scope=$ClaudeScope)" - Write-Output $report.ToString().TrimEnd() - if ($driftCount) { - Write-Output "STALE MIRROR: $driftCount Claude skill(s) stale or missing - rebuild with: pwsh scripts/sync-skills.ps1 -Target Claude" - $script:LastSyncExitCode = 1 - return - } - Write-Output "Claude skills up to date." - return - } - - Write-Output "Synced Claude skills -> $commandsRoot (scope=$ClaudeScope)" - Write-Output $report.ToString().TrimEnd() - return -} - -function Sync-CodexSkills { - param( - [System.IO.DirectoryInfo[]]$SkillDirs, - [string]$Root, - [string]$SelectedSkill, - [switch]$OnlyIfMissing, - [switch]$ReadOnlyCheck - ) - - $agentsRoot = Join-Path $RepoRoot '.agents\skills' - $report = [System.Text.StringBuilder]::new() - $warnings = [System.Collections.Generic.List[string]]::new() - $expectedSkillNames = [System.Collections.Generic.HashSet[string]]::new() - $driftCount = 0 - $script:LastSyncExitCode = 0 - - if (-not $ReadOnlyCheck -and -not $OnlyIfMissing -and -not $SelectedSkill -and (Test-Path $agentsRoot)) { - Remove-Item $agentsRoot -Recurse -Force - } - - foreach ($dir in $SkillDirs) { - $sourceName = $dir.Name - $group = $dir.Parent.Name - $codexName = "$group-$sourceName" - [void]$expectedSkillNames.Add($codexName) - $targetDir = Join-Path $agentsRoot $codexName - $targetSkill = Join-Path $targetDir 'SKILL.md' - - if ($OnlyIfMissing -and (Test-Path $targetSkill)) { - [void]$report.AppendLine((" {0,-36} present - skipped (-IfMissing)" -f $codexName)) - continue - } - - $sourceSkill = Join-Path $dir.FullName 'SKILL.md' - $sourceRelPath = "ai-artifacts/skills/shared/$group/$sourceName/SKILL.md" - $sourceDocument = Get-Content $sourceSkill -Raw - $expectedSkill = ConvertTo-CodexSkill -SourceDocument $sourceDocument -CodexName $codexName -SourceRelPath $sourceRelPath - foreach ($warning in Get-CodexCompatibilityWarnings -Document $sourceDocument -SkillName $codexName) { - $warnings.Add($warning) - } - - $resources = @(Get-ChildItem $dir.FullName -Recurse -File | - Where-Object { $_.Name -notin @('SKILL.md', 'METADATA.md') }) - - if ($ReadOnlyCheck) { - $state = 'UP-TO-DATE' - if (-not (Test-Path $targetSkill)) { $state = 'MISSING' } - elseif ((Get-Content $targetSkill -Raw) -cne $expectedSkill) { $state = 'STALE' } - else { - foreach ($res in $resources) { - $rel = $res.FullName.Substring($dir.FullName.Length).TrimStart('\') - $dest = Join-Path $targetDir $rel - if (-not (Test-Path $dest) -or - (Get-FileHash $res.FullName).Hash -ne (Get-FileHash $dest).Hash) { - $state = 'STALE' - break - } - } - if ($state -eq 'UP-TO-DATE') { - $mirrored = @(Get-ChildItem $targetDir -Recurse -File | Where-Object { $_.Name -ne 'SKILL.md' }).Count - if ($mirrored -ne $resources.Count) { $state = 'STALE' } - } - } - - if ($state -ne 'UP-TO-DATE') { $driftCount++ } - [void]$report.AppendLine((" {0,-36} {1}" -f $codexName, $state)) - continue - } - - if (Test-Path $targetDir) { Remove-Item $targetDir -Recurse -Force } - New-Item -ItemType Directory -Path $targetDir -Force | Out-Null - Set-Content -Path $targetSkill -Value $expectedSkill -Encoding utf8 -NoNewline - - foreach ($res in $resources) { - $rel = $res.FullName.Substring($dir.FullName.Length).TrimStart('\') - $dest = Join-Path $targetDir $rel - New-Item -ItemType Directory -Path (Split-Path $dest -Parent) -Force | Out-Null - Copy-Item $res.FullName $dest -Force - } - - [void]$report.AppendLine((" {0,-36} {1} resource file(s)" -f $codexName, $resources.Count)) - } - - if ($ReadOnlyCheck -and -not $SelectedSkill -and (Test-Path $agentsRoot)) { - foreach ($dir in Get-ChildItem $agentsRoot -Directory) { - if (-not $expectedSkillNames.Contains($dir.Name)) { - $driftCount++ - [void]$report.AppendLine((" {0,-36} EXTRA" -f $dir.Name)) - } - } - } - - if ($ReadOnlyCheck) { - Write-Output "Drift check: $Root -> $agentsRoot (target=Codex)" - Write-Output $report.ToString().TrimEnd() - if ($warnings.Count) { - Write-Output "Compatibility warnings:" - $warnings | Sort-Object -Unique | ForEach-Object { Write-Output " $_" } - } - if ($driftCount) { - Write-Output "STALE MIRROR: $driftCount Codex skill(s) stale or missing - rebuild with: pwsh scripts/sync-skills.ps1 -Target Codex" - $script:LastSyncExitCode = 1 - return - } - Write-Output "Codex skills up to date." - return - } - - Write-Output "Synced Codex skills -> $agentsRoot" - Write-Output $report.ToString().TrimEnd() - if ($warnings.Count) { - Write-Output "Compatibility warnings:" - $warnings | Sort-Object -Unique | ForEach-Object { Write-Output " $_" } - } - return -} - -$skillDirs = Get-SkillDirectories -Root $skillsRoot -Name $Skill -$exitCodes = [System.Collections.Generic.List[int]]::new() - -if ($Target -in @('All', 'Claude')) { - Write-Output "== Claude Code skill mirror ==" - Sync-ClaudeSkills -SkillDirs $skillDirs -Root $skillsRoot -ClaudeScope $Scope -OnlyIfMissing:$IfMissing -ReadOnlyCheck:$Check - $exitCodes.Add($script:LastSyncExitCode) -} - -if ($Target -in @('All', 'Codex')) { - Write-Output "== Codex skill mirror ==" - Sync-CodexSkills -SkillDirs $skillDirs -Root $skillsRoot -SelectedSkill $Skill -OnlyIfMissing:$IfMissing -ReadOnlyCheck:$Check - $exitCodes.Add($script:LastSyncExitCode) -} - -if ($exitCodes | Where-Object { $_ -ne 0 }) { exit 1 } From 6562c26b7e0ae62a38b1d09ba83590ec331df49f Mon Sep 17 00:00:00 2001 From: Dennis Winter Date: Fri, 10 Jul 2026 20:13:28 +0200 Subject: [PATCH 11/12] Fix workflow lint and policy check failures (#21) - fix yamllint spacing issue in dependabot workflow - remove disallowed global strict mode usage in workflow bash scripts - fix shellcheck SC2320 in write-summary scripts - apply ruff format to simulate-workflows test file Co-authored-by: Dennis Winter Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/workflows/dependabot-auto-merge.yml | 2 +- .github/workflows/scripts/ci/determin-diff-range.sh | 2 -- .github/workflows/scripts/ci/get-changed-files.sh | 2 -- .../workflows/scripts/config-lint/install-linters.sh | 2 -- .../workflows/scripts/config-lint/write-summary.sh | 12 ++++++------ .../workflows/scripts/policy-check/write-summary.sh | 12 ++++++------ .../powershell-quality/run-psscriptanalyzer.sh | 2 -- .../powershell-quality/validate-powershell-syntax.sh | 2 -- .../scripts/powershell-quality/write-summary.sh | 12 ++++++------ .../validate-pwsh-syntax.sh | 2 -- .../validate-windowsps-syntax.sh | 2 -- .../scripts/powershell-tests/install-pester.sh | 2 -- .../workflows/scripts/powershell-tests/run-pester.sh | 2 -- .../scripts/python-quality/write-summary.sh | 12 ++++++------ .../scripts/python-tests/discover-test-files.sh | 1 - .../python-tests/install-test-dependencies.sh | 1 - .github/workflows/scripts/python-tests/run-pytest.sh | 1 - .../workflows/scripts/shell-quality/write-summary.sh | 12 ++++++------ .../workflows/scripts/shell-tests/write-summary.sh | 12 ++++++------ .../tests/test_simulate_workflows.py | 4 ++-- 20 files changed, 39 insertions(+), 60 deletions(-) diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index 098fcb3..f3e854f 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Fetch Dependabot metadata id: metadata - uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0 + uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/scripts/ci/determin-diff-range.sh b/.github/workflows/scripts/ci/determin-diff-range.sh index 3d20b34..85fc09b 100644 --- a/.github/workflows/scripts/ci/determin-diff-range.sh +++ b/.github/workflows/scripts/ci/determin-diff-range.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -set -euo pipefail - base="${1:-}" head="${2:-}" before="${3:-}" diff --git a/.github/workflows/scripts/ci/get-changed-files.sh b/.github/workflows/scripts/ci/get-changed-files.sh index b9829c0..6288201 100644 --- a/.github/workflows/scripts/ci/get-changed-files.sh +++ b/.github/workflows/scripts/ci/get-changed-files.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -set -euo pipefail - declare -a git_cmd=(git) base="" diff --git a/.github/workflows/scripts/config-lint/install-linters.sh b/.github/workflows/scripts/config-lint/install-linters.sh index b5cdd7f..0b3b083 100644 --- a/.github/workflows/scripts/config-lint/install-linters.sh +++ b/.github/workflows/scripts/config-lint/install-linters.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -set -u - warn() { echo "[config-lint install] $*" >&2 } diff --git a/.github/workflows/scripts/config-lint/write-summary.sh b/.github/workflows/scripts/config-lint/write-summary.sh index 1c69c48..b3eaa18 100644 --- a/.github/workflows/scripts/config-lint/write-summary.sh +++ b/.github/workflows/scripts/config-lint/write-summary.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set +e -{ +if { echo "## config-lint summary" echo "- YAML: see reports/yamllint.txt" echo "- JSON: see reports/jsonlint.txt" @@ -9,9 +9,9 @@ set +e echo "- AsciiDoc: see reports/asciidoctor-lint.txt" echo "- Shell: see reports/shellcheck.txt" echo "- JavaScript syntax: see reports/js-syntax.txt" -} >> "$GITHUB_STEP_SUMMARY" -rc=$? -if [ "$rc" -ne 0 ]; then - echo "::error::Failed to write config-lint job summary (rc=$rc)." - exit "$rc" +} >> "$GITHUB_STEP_SUMMARY"; then + : +else + echo "::error::Failed to write config-lint job summary." + exit 1 fi diff --git a/.github/workflows/scripts/policy-check/write-summary.sh b/.github/workflows/scripts/policy-check/write-summary.sh index f10ece5..55f5d9b 100644 --- a/.github/workflows/scripts/policy-check/write-summary.sh +++ b/.github/workflows/scripts/policy-check/write-summary.sh @@ -1,14 +1,14 @@ #!/usr/bin/env bash set +e -{ +if { echo "## policy-check summary" echo "- Guardrail log: reports/policy-check.txt" echo "- Coding policy log: reports/coding-policy-check.txt" echo "- PowerShell runtime log: reports/powershell-runtime-check.txt" -} >> "$GITHUB_STEP_SUMMARY" -rc=$? -if [ "$rc" -ne 0 ]; then - echo "::error::Failed to write policy-check job summary (rc=$rc)." - exit "$rc" +} >> "$GITHUB_STEP_SUMMARY"; then + : +else + echo "::error::Failed to write policy-check job summary." + exit 1 fi diff --git a/.github/workflows/scripts/powershell-quality/run-psscriptanalyzer.sh b/.github/workflows/scripts/powershell-quality/run-psscriptanalyzer.sh index 35dcd4a..3d064dc 100644 --- a/.github/workflows/scripts/powershell-quality/run-psscriptanalyzer.sh +++ b/.github/workflows/scripts/powershell-quality/run-psscriptanalyzer.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -set -euo pipefail - base="$1" head="$2" diff --git a/.github/workflows/scripts/powershell-quality/validate-powershell-syntax.sh b/.github/workflows/scripts/powershell-quality/validate-powershell-syntax.sh index 1db813d..d48755c 100644 --- a/.github/workflows/scripts/powershell-quality/validate-powershell-syntax.sh +++ b/.github/workflows/scripts/powershell-quality/validate-powershell-syntax.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -set -euo pipefail - base="$1" head="$2" diff --git a/.github/workflows/scripts/powershell-quality/write-summary.sh b/.github/workflows/scripts/powershell-quality/write-summary.sh index 9d71701..3245c9b 100644 --- a/.github/workflows/scripts/powershell-quality/write-summary.sh +++ b/.github/workflows/scripts/powershell-quality/write-summary.sh @@ -1,14 +1,14 @@ #!/usr/bin/env bash set +e -{ +if { echo "## powershell-quality summary" echo "- PowerShell files found: $1" echo "- Analyzer report: reports/psscriptanalyzer.txt" echo "- Syntax report: reports/powershell-syntax.txt" -} >> "$GITHUB_STEP_SUMMARY" -rc=$? -if [ "$rc" -ne 0 ]; then - echo "::error::Failed to write powershell-quality job summary (rc=$rc)." - exit "$rc" +} >> "$GITHUB_STEP_SUMMARY"; then + : +else + echo "::error::Failed to write powershell-quality job summary." + exit 1 fi diff --git a/.github/workflows/scripts/powershell-runtime-compat/validate-pwsh-syntax.sh b/.github/workflows/scripts/powershell-runtime-compat/validate-pwsh-syntax.sh index b9dd071..6ffed89 100644 --- a/.github/workflows/scripts/powershell-runtime-compat/validate-pwsh-syntax.sh +++ b/.github/workflows/scripts/powershell-runtime-compat/validate-pwsh-syntax.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -set -euo pipefail - base="$1" head="$2" diff --git a/.github/workflows/scripts/powershell-runtime-compat/validate-windowsps-syntax.sh b/.github/workflows/scripts/powershell-runtime-compat/validate-windowsps-syntax.sh index ad944ea..db476ad 100644 --- a/.github/workflows/scripts/powershell-runtime-compat/validate-windowsps-syntax.sh +++ b/.github/workflows/scripts/powershell-runtime-compat/validate-windowsps-syntax.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -set -euo pipefail - base="$1" head="$2" diff --git a/.github/workflows/scripts/powershell-tests/install-pester.sh b/.github/workflows/scripts/powershell-tests/install-pester.sh index b87f0ec..60965a3 100644 --- a/.github/workflows/scripts/powershell-tests/install-pester.sh +++ b/.github/workflows/scripts/powershell-tests/install-pester.sh @@ -1,5 +1,3 @@ #!/usr/bin/env bash -set -euo pipefail - echo "Skipping Pester installation: workflow scripts run in bash-only mode." diff --git a/.github/workflows/scripts/powershell-tests/run-pester.sh b/.github/workflows/scripts/powershell-tests/run-pester.sh index fd80c2f..e50e000 100644 --- a/.github/workflows/scripts/powershell-tests/run-pester.sh +++ b/.github/workflows/scripts/powershell-tests/run-pester.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -set -euo pipefail - joined_paths="$1" echo "Skipping Pester execution for: ${joined_paths}" diff --git a/.github/workflows/scripts/python-quality/write-summary.sh b/.github/workflows/scripts/python-quality/write-summary.sh index 32d54b7..3180286 100644 --- a/.github/workflows/scripts/python-quality/write-summary.sh +++ b/.github/workflows/scripts/python-quality/write-summary.sh @@ -1,14 +1,14 @@ #!/usr/bin/env bash set +e -{ +if { echo "## python-quality summary" echo "- Python files found: $1" echo "- Ruff check report: reports/ruff-check.txt" echo "- Ruff format report: reports/ruff-format-check.txt" -} >> "$GITHUB_STEP_SUMMARY" -rc=$? -if [ "$rc" -ne 0 ]; then - echo "::error::Failed to write python-quality job summary (rc=$rc)." - exit "$rc" +} >> "$GITHUB_STEP_SUMMARY"; then + : +else + echo "::error::Failed to write python-quality job summary." + exit 1 fi diff --git a/.github/workflows/scripts/python-tests/discover-test-files.sh b/.github/workflows/scripts/python-tests/discover-test-files.sh index 33c862c..92dd862 100644 --- a/.github/workflows/scripts/python-tests/discover-test-files.sh +++ b/.github/workflows/scripts/python-tests/discover-test-files.sh @@ -3,7 +3,6 @@ base="$1" head="$2" -set -euo pipefail files=$(bash .github/workflows/scripts/ci/get-changed-files.sh --base "${base}" --head "${head}" --include 'ai-artifacts/**/tests/test_*.py') { echo "files<> "$GITHUB_STEP_SUMMARY" -rc=$? -if [ "$rc" -ne 0 ]; then - echo "::error::Failed to write shell-quality job summary (rc=$rc)." - exit "$rc" +} >> "$GITHUB_STEP_SUMMARY"; then + : +else + echo "::error::Failed to write shell-quality job summary." + exit 1 fi diff --git a/.github/workflows/scripts/shell-tests/write-summary.sh b/.github/workflows/scripts/shell-tests/write-summary.sh index d258462..20aaa05 100644 --- a/.github/workflows/scripts/shell-tests/write-summary.sh +++ b/.github/workflows/scripts/shell-tests/write-summary.sh @@ -1,13 +1,13 @@ #!/usr/bin/env bash set +e -{ +if { echo "## shell-tests summary" echo "- Shell test files found: $1" echo "- bats report: reports/bats.txt" -} >> "$GITHUB_STEP_SUMMARY" -rc=$? -if [ "$rc" -ne 0 ]; then - echo "::error::Failed to write shell-tests job summary (rc=$rc)." - exit "$rc" +} >> "$GITHUB_STEP_SUMMARY"; then + : +else + echo "::error::Failed to write shell-tests job summary." + exit 1 fi diff --git a/ai-artifacts/skills/shared/workflow/simulate-workflows/tests/test_simulate_workflows.py b/ai-artifacts/skills/shared/workflow/simulate-workflows/tests/test_simulate_workflows.py index cef4046..b4559bd 100644 --- a/ai-artifacts/skills/shared/workflow/simulate-workflows/tests/test_simulate_workflows.py +++ b/ai-artifacts/skills/shared/workflow/simulate-workflows/tests/test_simulate_workflows.py @@ -31,11 +31,11 @@ def _prepare_fake_toolchain(tmp_path: Path, log_path: Path) -> Path: _write_cmd( bin_dir / "python.cmd", - "@echo off\r\n" "exit /b 0\r\n", + "@echo off\r\nexit /b 0\r\n", ) _write_cmd( bin_dir / "git.cmd", - "@echo off\r\n" "exit /b 0\r\n", + "@echo off\r\nexit /b 0\r\n", ) return bin_dir From 4df28c4d5ae58f4a8cc0601af9ef6f6b6fc00274 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Jul 2026 18:21:12 +0000 Subject: [PATCH 12/12] fix: align markdown tables for config-lint --- ai-artifacts/skills/shared/README.md | 2 +- docs/harnesses/copilot-cli.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ai-artifacts/skills/shared/README.md b/ai-artifacts/skills/shared/README.md index 70b72b2..13e1ca1 100644 --- a/ai-artifacts/skills/shared/README.md +++ b/ai-artifacts/skills/shared/README.md @@ -78,7 +78,7 @@ table is the human-readable summary. | `recon` | session | — (local original) | No upstream | | `check-skill-updates` | setup | — (local original) | No upstream; the update tool itself | | `import-upstream-skill` | setup | — (local original) | No upstream; the generic import process itself | -| `setup-repo` | setup | — (local original) | Self-contained bootstrap skill for clone setup (hooks + mirror generation in one command) | +| `setup-repo` | setup | — (local original) | Self-contained bootstrap skill for clone setup (hooks + mirror generation in one command) | | `git-guardrails` | setup | mattpocock `skills/misc/git-guardrails-claude-code` | Localized from the global-prior (pwsh + bash guards); Claude-Code-hook skill, N/A in chat. Exact upstream checkpoint lives only in the skill's `METADATA.md` | | `setup-pre-commit` | setup | mattpocock `skills/misc/setup-pre-commit` (**local fork**) | Diverged entirely: `pre-commit` framework for PS/MD/AsciiDoc/SQL, not Husky/lint-staged/Prettier. Carries **no** `upstream-*` (lineage in a comment); `check-skill-updates` skips it | | `simulate-workflows` | workflow | — (local original) | Deterministic script that runs local CI-equivalent workflow checks (Python, PowerShell, linting) | diff --git a/docs/harnesses/copilot-cli.md b/docs/harnesses/copilot-cli.md index 2345236..9d84247 100644 --- a/docs/harnesses/copilot-cli.md +++ b/docs/harnesses/copilot-cli.md @@ -282,7 +282,7 @@ edge cases where multiple equally-valid approaches exist. | `.github/extensions/` | Copilot CLI-specific | Extension format is not shared with other harnesses | | `.github/prompts/*.prompt.md` | Copilot family (shared format) | VS Code Copilot invokes as slash commands; Copilot CLI references by path | | MCP (workspace/user config) | Cross-vendor | Config syntax and location differ per harness; protocol is vendor-neutral | -| `ai-artifacts/skills/shared/` | Source of truth | Mirrored to `.claude/commands/` (Claude Code), `.agents/skills/` (Codex), and `.github/skills/_/SKILL.md` (Copilot skill mirror) | +| `ai-artifacts/skills/shared/` | Source of truth | Mirrored to Claude, Codex, and Copilot skill mirrors | | Scheduled workflows | Copilot CLI-specific | No equivalent native primitive in other harnesses | | Canvas panels | Copilot CLI-specific | No equivalent in other harnesses |