44 Commits

Author SHA1 Message Date
911daddbe2 docs(kyberforge): fix stale references in CONTEXT.md
plugin-author was deleted by this PR (ADR-0015, apm-workflow is the
successor) but still appeared as a live example in the skill-composition
list. The Gitea-canonical-issue-tracker line also cited ADR-0017 (the
unrelated plugin-content-mirror ADR added by this PR) instead of ADR-0007.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
2026-08-13 21:29:23 +00:00
b0b1470f2c fix(kyberforge): guard unguarded array expansions in sync-plugin-content.sh
force_flag and plugin_dirs expanded unguarded under set -euo pipefail,
tripping "unbound variable" on bash 3.2 (macOS) whenever the array is
empty -- which is the default --check invocation wired into the
pre-commit drift gate. Apply the same ${arr[@]+"${arr[@]}"} guard
already used for seen_names in this file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
2026-08-13 21:29:18 +00:00
560154c727 docs(kyberforge): note repro caveat for ADR-0017 verification command
Running ADR-0017's cited live behavioral test literally from this
repo's root gives a contaminated signal: this repo's own project-level
.claude/settings.json enables all 6 holocron plugins, so Claude Code
loads all of them rather than isolating kyberforge's discoverability.
Documents the neutral-cwd + absolute --plugin-dir reproduction needed
to isolate the signal.
2026-08-13 20:51:34 +00:00
2c731eb476 test(kyberforge): cover sync-marketplace-mirror.sh drift-guard
Its sibling sync-plugin-content.sh has thorough coverage
(tests/test-sync-plugin-content.sh) but this drift guard -- wired into
pre-commit as check-marketplace-mirror-sync, keeping
.claude-plugin/marketplace.json and .github/plugin/marketplace.json in
sync -- had none. A silent regression here would let the two
marketplace manifests drift without any test catching it.

Covers: missing-source no-op in both real-sync and --check modes,
drift detection when the mirror is missing or stale, real sync
producing a byte-identical mirror, --check clean immediately after
sync, drift from an edited source cleared by re-sync, an
already-in-sync mirror reporting no drift, usage-error rejection of
unrecognized/extra arguments, and idempotency of repeated syncs.
2026-08-13 20:45:10 +00:00
7c3c867e00 docs(kyberforge): fix stale vale-prefilter regex in CONTEXT.md
The Vale-audit-prefilter glossary entry still quoted the pre-.apm/-
migration file-scoping regexes (^plugins/[^/]+/skills/...,
^plugins/[^/]+/agents/...). The actual pre-commit hooks require .apm/
in the path and .agent.md for agent files; updated the glossary to
quote the live patterns from .pre-commit-config.yaml verbatim,
including a third stale occurrence in the skill-size-check description
that the PR #95 review missed.

Refs: #95
2026-08-13 19:47:47 +00:00
4003c6a273 fix(kyberforge): make bats dispatcher bash-3.2 safe
run-bats.sh's new bounded parallel dispatcher used nproc/wait -n,
which are bash 4.3+/GNU-only and silently drop the concurrency cap on
macOS's stock bash 3.2 (the wait -n error is swallowed by `|| true`).
Its sibling tests/run-tests.sh, changed in the same PR and explicitly
bash-3.2-safe, already solves this with getconf + a batched wait.
Ported that same pattern here for consistency and to actually meet the
compatibility goal.

Refs: #95
2026-08-13 19:47:42 +00:00
9c140efa2e fix(kyberforge): harden plugin-content sync, reinject Copilot mcpServers
PR #95's review of the issue #90 apm-conversion work found several
defects in scripts/sync-plugin-content.sh and the gate wired to it:

- --check claimed never to mutate the plugin root, but apm pack still
  wrote .claude-plugin/plugin.json and .github/plugin/plugin.json into
  the real plugin_dir on first-time creation. --check now packs a
  throwaway copy instead.
- check-plugin-content-sync hardcoded the six plugin directories
  instead of deriving them the way check-manifests.sh already does.
  Added an --all flag that parses .claude-plugin/marketplace.json, and
  simplified the pre-commit hook to use it.
- A missing plugin_dir and one that legitimately has no .apm/ yet both
  reported SKIP/success; a missing directory now FAILs.
- The dispatch loop backgrounded every plugin with no concurrency cap,
  unlike the JOBS-bounded pattern this same PR added to
  tests/run-bats.sh and tests/run-tests.sh. Added the same
  bash-3.2-safe getconf + batched-wait cap here for consistency.
- Per-plugin scratch/log/status files were keyed only by basename, with
  no collision guard across arguments; added a fail-fast check.
- sync_hooks_json()'s trailing-newline normalization was duplicated
  between its --check and write branches; factored into one helper.
- tests/test-sync-plugin-content.sh set two competing `trap ... EXIT`
  statements, so the first (cleaning up $FIXTURE) was silently
  replaced by the second and its tmp dir leaked every run. Adopted the
  track()/CLEANUP_DIRS pattern already used in
  tests/test-check-release-needed.sh.

Separately: apm's Copilot-ecosystem plugin.json builder unconditionally
strips mcpServers, citing (in its own docstring) that the field is out
of schema for Copilot -- a claim this repo's own researched Copilot
plugin schema docs contradict. reinject_mcp_servers() narrowly restores
it from the plugin's .mcp.json on real syncs only, regenerating
plugins/bin/.github/plugin/plugin.json (the only plugin that currently
declares any MCP servers). Documented as an amendment to ADR-0017,
since it's a deliberate, narrow exception to that ADR's rejection of
patching apm's compiled output -- apm's premise for stripping
skills/agents/commands/hooks pointers is still accurate; its premise
for stripping mcpServers is not.

All 12 assertions in tests/test-sync-plugin-content.sh pass
individually, plus 5 new regression tests added for this round; the
full bats and shell-script suites are green; shellcheck is clean.

Refs: #95
ADR: 0017
2026-08-13 19:47:36 +00:00
bff9662c52 docs(kyberforge): add keywords/homepage/repository to plugin apm.yml
bin and kyberforge had empty keywords: [], and no plugin set homepage or
repository despite the schema supporting both and the repo having one
canonical Gitea remote -- flagged by an apm-docs-vs-repo audit following
issue #90's plugin->apm conversion.

Compiled .claude-plugin/plugin.json and .github/plugin/plugin.json were
regenerated via scripts/sync-plugin-content.sh to carry the new fields.

Refs: #90
2026-08-13 19:03:12 +00:00
a873e93050 fix(kyberforge): keep .github/plugin/marketplace.json synced
apm.yml only declares claude and codex marketplace output profiles --
codex writes a differently-shaped file to .agents/plugins/marketplace.json,
so nothing regenerates the legacy Copilot CLI path at
.github/plugin/marketplace.json. It was hand-synced once during the #90
conversion and had drifted (missing the category field added when codex
output was enabled) with no gate to catch it.

scripts/sync-marketplace-mirror.sh keeps it byte-identical to the compiled
.claude-plugin/marketplace.json, wired as a pre-push check so it can't go
stale silently again.

Refs: #90
2026-08-13 18:54:10 +00:00
a8beff7d2c perf(kyberforge): parallelize the bats and shell test runners
Both runners spawned one process at a time and let it finish before starting
the next, so wall time scaled with file count even though each file's own
work (bats: mostly repeated apm/git subprocess startup; the vale-heavy
shell scripts: repeated vale binary startup) rarely used a full core.

run-bats.sh now backgrounds one `bats` invocation per file, bounded by core
count, buffering each file's TAP output so concurrent streams can't
interleave, then flushes in stable sorted order once every job finishes.
run-tests.sh does the same for test-*.sh in fixed-size batches (plain `wait`,
not `wait -n`, to stay on the bash-3.2-safe path test-vale-wrap.sh already
enforces for this file) -- verified beforehand that every test-*.sh keeps its
fixtures under its own mktemp dir rather than mutating the live repo tree, so
running them concurrently is safe.

Confirmed correct on both the pass and fail paths (a deliberately failing
bats test and a scratch TEST_DIR with pass/fail/skip-77 scripts) before
measuring: full bats suite 26-30s -> 7.5s, tests/run-tests.sh 68s -> ~45-50s,
full pre-push gate 2m40s -> 1m12s.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
2026-08-13 18:35:18 +00:00
c3a56d89f0 perf(kyberforge): scope skill-frontmatter to .apm/ and parallelize plugin sync
skill-frontmatter's files: pattern matched any SKILL.md, so it ran twice per
sync -- once on the .apm/ source and again on the compiled flat-mirror copy.
Scoped it to .apm/skills/ like its sibling hooks (skill-size-check,
vale-audit-prefilter-skill), which already only check the source.

sync-plugin-content.sh ran `apm pack` once per plugin, serially -- each
invocation is dominated by fixed CLI startup cost rather than per-plugin
work, so 6 plugins paid that cost 6 times over (~3.1s). Backgrounds the
per-plugin work instead, buffering each plugin's output so concurrent
DRIFT/FAIL messages can't interleave, then flushes in stable order after
`wait` (~1.3s, confirmed idempotent on a real sync).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
2026-08-13 18:23:47 +00:00
b0936ad386 fix(kyberforge): force plugin.json regeneration on every real sync
apm pack silently skips regenerating plugin.json when it already exists
("already exists; skipping plugin.json generation. Re-run with --force to
overwrite it."). sync-plugin-content.sh never passed --force, so
.claude-plugin/plugin.json and .github/plugin/plugin.json were written
once on first sync and never refreshed again -- a name/version/description
edit in a plugin's apm.yml would silently stop propagating, with no gate
catching it (check-manifests.sh only checks presence; check-plugin-content-
sync explicitly excludes plugin.json; apm-pack-check-clean is scoped to
marketplace.json only).

Pass --force on real-mode syncs only (--check must not mutate the plugin
root, so plugin.json staleness there stays a known, undetected gap until
the next real sync -- documented in the script's header comment).

Regenerating surfaced a second, unrelated bug: apm's writer and
pretty-format-json's --autofix disagree on JSON key order (semantic vs.
alphabetical), so every real sync would otherwise flip plugin.json's key
order right back after a commit re-alphabetized it. Excluded the four
apm-generated manifests (plugin.json x2, marketplace.json x2) from
pretty-format-json -- their exact formatting is dictated by apm's own
writer as compiled output, not this repo's generic JSON prettifier.

Verified idempotent: a second real sync after this fix produces zero
further diff.

Refs: #90
2026-08-13 18:05:29 +00:00
5f42f57106 feat(kyberforge): enable codex marketplace output
Uncomment marketplace.outputs.codex in apm.yml. Codex output requires
every package to declare a category -- added one per package (Developer
Tools / Utilities / Version Control / Productivity, matching each
plugin's actual scope).

Re-running apm pack after enabling codex auto-synced 'codex' into each
local package's own targets: list (apm.yml, per plugin) -- packages
belonging to a marketplace that publishes a codex output need to declare
codex as a supported compile target for that to be coherent. Verified
idempotent: a second apm pack --check-clean run makes no further changes.

.claude-plugin/marketplace.json regenerated (now carries category per
plugin); .agents/plugins/marketplace.json is the new codex artifact.

Refs: #90
2026-08-13 17:50:41 +00:00
6e77c11474 ci(kyberforge): add apm-native marketplace/audit/pack drift gates
Validated the plugin-content-mirror fix (issue #90) against apm's own
packing/CI documentation and source: no apm-native mechanism replaces the
mirror script (apm's bundler treats .apm/ and root convention dirs as
mutually exclusive, by design), but the investigation surfaced a real,
separate gap -- this repo ran zero apm-native audit/check commands in CI,
relying entirely on custom scripts and Claude Code's own client-side
validator.

Add three pre-push hooks matching apm's documented producer CI pattern:
- apm marketplace check: validates every marketplace.packages[] entry
  resolves, including live network reachability for remote refs -- a
  blind spot check-manifests.sh explicitly skips (local sources only).
- apm audit --ci: apm's own lockfile/policy/hidden-content integrity gate.
- apm pack --check-versions --check-clean: closes issue #90's deferred
  item 3 (a check-clean-equivalent gate) using apm's native flag instead
  of bespoke drift logic, verifying .claude-plugin/marketplace.json still
  matches what apm.yml + .apm/ would currently generate.

All three are network-tolerant and whole-repo in scope, so they belong at
pre-push alongside check-manifests/check-plugin-content-sync/
validate-plugins -- not pre-commit, which stays fast/offline/per-file.

Documented the packing/bundling/releasing/CI findings in
docs/research/docs/microsoft-apm/releasing.md (new) and extended
testing-and-validation.md with the apm-action wrapper and its documented
CI patterns, sourced from Context7 and cross-checked against the
installed apm-cli 0.28.0 package directly.

Refs: #90
2026-08-13 17:50:32 +00:00
38f1ba4e03 fix(kyberforge): bridge apm content to Claude Code's flat plugin discovery
Claude Code's (and Copilot's) native plugin installer has zero awareness of
.apm/ nesting -- it convention-scans only flat skills/, agents/, commands/,
hooks.json at each plugin's root. Confirmed via strings on the installed
claude binary and live installs of git@holocron/gitea@holocron/kyberforge@
holocron, all reporting Skills(0) Agents(0) Hooks(0) post ADR-0015's apm
conversion. Root cause (apm_cli/core/plugin_manifest.py): apm's plugin.json
compiler deliberately strips skills/agents/commands keys, assuming the host
already auto-discovers those convention directories -- it has no model of
.apm/ being host-visible at all. Separately, apm's own bundle exporter
(apm_cli/bundle/plugin_exporter.py, behind `apm pack --format plugin`)
implements the correct .apm/ -> flat mapping, but only ever targeted
build/<name>-<version>/, a path nothing in marketplace.json's source: points
at.

scripts/sync-plugin-content.sh wraps that bundle exporter and copies its
agents/, skills/, commands/, instructions/, extensions/, and merged
hooks.json back into each plugin's own root as a second tracked
compiled-output category -- same governance status as
.claude-plugin/plugin.json: generated from .apm/, never hand-edited. tests/
subdirectories are excluded from the mirror (dev fixtures, not host-visible
runtime content; several hardcode a relative repo-root walk-up sized for the
.apm/-nested depth, which breaks when duplicated one level shallower).
Applied for real across all 6 plugins and verified two ways: `claude plugin
validate --strict` passes on every real plugin directory, and a live
`claude --plugin-dir <path> -p "list skills/agents"` behavioral test
confirms content is now actually discovered.

Also, from the same issue #90 review round:
- scripts/check-manifests.sh pointed at each plugin's root-level plugin.json
  (checking skills/hooks/mcpServers/agents pointer fields) -- that file was a
  stale near-duplicate of .claude-plugin/plugin.json nothing else read or
  wrote, now deleted across all 6 plugins. check-manifests.sh is rewritten to
  validate .claude-plugin/plugin.json instead, and drops the pointer-field
  checks entirely (nothing to check -- those fields are correctly absent by
  design). Content-presence drift is now check-plugin-content-sync's job, a
  new pre-push hook wired in .pre-commit-config.yaml.

docs/adr/0017 records the root cause and decision in full, including two
rejected alternatives (patching plugin.json's path fields directly -- apm's
compiler strips them on every run; pointing marketplace.json at apm pack's
build/ output -- a version-suffixed non-source directory nothing can install
from without an extra build step). ADR-0015 and CONTEXT.md are updated to
point at it.

Refs: #90
2026-08-13 16:59:03 +00:00
7910b8b12c docs(kyberforge): fix path drift and content gaps from apm conversion
The apm conversion (5e23250) moved skills/agents to plugins/<name>/.apm/
and deleted plugin-author/marketplace-author, but a review against issue
#90's Definition of Done found several stale pre-conversion references
left behind by straight git-mv's, plus one real content gap:

- ADR-0014 still documented the old flat vale-prefilter paths in 6
  places, despite ADR-0015 claiming it had been updated.
- ADR-0015 also overclaimed: it said ADR-0014 had both a skills/ and an
  agents/ path regex updated, but ADR-0014 never had an agents/ path
  regex to begin with (its one "agents" mention is a Vale glob-section
  identifier, not a filesystem path). Corrected the wording.
- CONTEXT.md's Skill glossary entry, its Vale-prefilter section, and its
  plugin-author/marketplace-author forward-pointer all still described
  the pre-conversion layout or a "pending issue #90" state that has since
  landed.
- LESSONS.md and two skill test READMEs pointed at skill-author/
  skill-audit/agent-author paths without the .apm/ segment.
- apm-workflow/references/marketplace.md cited plugin-author/SKILL.md's
  Gotchas for the Claude Code reserved plugin-name-prefix list, but that
  list was never actually carried into apm-workflow during the
  conversion despite ADR-0015 claiming it was. Recovered the list from
  git history and inlined it directly rather than leaving a dangling
  citation.
- agent-author/references/deployment-modes.md had an example
  contradicting its own stated .apm/agents/ convention two lines above.

Refs: #90
2026-08-13 07:50:56 +00:00
5e232503c4 feat(kyberforge): execute plugin-to-apm marketplace conversion
Why:
ADR-0015 established that Microsoft APM (apm.yml + .apm/) should replace
this repo's hand-authored plugin.json/marketplace.json model, with those
files becoming compiled output of `apm pack` instead of files edited by
hand via the (now-retired) plugin-author/marketplace-author skills.
Issue #90 was the deferred execution of that decision, gated on #88
(apm tooling) and #89 (apm-native agent-author/skill-author routing).

Implementation notes:
- All six plugins (bin, core, git, gitea, kyberforge, lint) now carry
  apm.yml + .apm/{skills,agents,hooks} as their authoring source. Skills
  moved with a plain git mv (content-identical across targets). Agents
  were re-authored, not moved: per ADR-0016, .apm/agents/*.agent.md
  compiles verbatim to both Claude and Copilot, so plugin-scope agents
  now carry only name/description/model/source_keys -- no tools: field,
  no Claude-only knobs (isolation, maxTurns, effort, memory,
  permissionMode).
- Root apm.yml registers all 7 marketplace packages (6 local plus
  mattpocock-skills as a remote entry) under versioning: per_package,
  matching this repo's existing independent-plugin-versioning practice.
- .claude-plugin/marketplace.json and every plugin's plugin.json are now
  apm-pack-compiled output, verified against the prior hand-maintained
  content: same names/descriptions/versions/licenses/authors, only
  cosmetic serialization differences (JSON key order, owner email vs.
  url, Unicode escaping).
- plugin-author and marketplace-author are retired now that apm-based
  authoring fully replaces their job; kyberforge bumped 1.3.1 -> 1.4.0
  for that removal, and the root marketplace catalog bumped
  0.3.1 -> 0.3.2 to match, per the version-bump convention now
  documented in apm-workflow's reference docs instead of a dedicated
  script (apm has no native version-bump automation).
- Fixed hardcoded pre-.apm/ path assumptions across
  .pre-commit-config.yaml, .pre-commit-hooks.yaml,
  scripts/check-scope-walkup-sync.sh, scripts/sync-vale-styles.sh,
  scripts/check-vale-style-sync.sh, six plugins' root plugin.json
  (stale skills/hooks/agents pointer fields that check-manifests.sh
  validates), and several tests/*.bats and tests/*.sh fixtures --
  including a bats REPO_ROOT relative-path depth bug (10 files, one
  extra .apm/ directory level to walk up) and a vale probe-path
  isolation regression introduced mid-fix.
- Corrected empirically-wrong assumptions surfaced this session in
  apm-workflow/apm-install's own reference docs: `apm marketplace
  package add` does not accept local paths (only owner/repo remote
  shorthand -- local packages are registered by editing apm.yml's
  marketplace.packages[] directly); `apm compile` is a consumer-side
  AGENTS.md/CLAUDE.md generator, not the plugin.json producer, and
  hard-fails on skill/agent-only packages without --clean; `apm plugin
  init <name>` nests a stray subdirectory when run with a positional
  name arg from inside a same-named directory; no native Copilot
  marketplace output profile exists; .mcp.json is merged into the
  compiled plugin.json content-aware and target-scoped, with no
  dependencies.mcp entry needed for simple passthrough; pipx is the
  correct pip fallback on externally-managed Python environments.
- Renamed agent-author's copilot.agent.md template asset to
  copilot.agent.md.template so apm compile's recursive *.agent.md glob
  stops misparsing the placeholder template as a real agent primitive.

Impact:
plugin.json and marketplace.json are compiled artifacts from here on --
editing them by hand is no longer the workflow; edit apm.yml/.apm/ and
run apm pack. CONTEXT.md's Plugin/Plugin marketplace glossary entries
reflect this. ADR-0001 is marked superseded, ADR-0006 moot, and
ADR-0010 updated for the new .apm/agents/ path (project/user scope
unaffected, per ADR-0016). Full local verification: claude plugin
validate --strict on all 6 plugins, apm audit --ci, apm marketplace
check, check-manifests.sh, and the full test suite (165/165 bats,
13/13 shell scripts) all pass clean.

Fixes: #90
Refs: #88, #89
ADR: 0015
ADR: 0016

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ub96PyaSRD9BHPktotj1pC
2026-08-12 18:21:24 +00:00
50d5c30a3c Merge pull request 'feat(kyberforge): retarget forge skills to author/audit APM content' (#93) from feat/89-apm-native-authoring into main
Reviewed-on: https://git.dev.rkdr.net/Defame1297/holocron/pulls/93
Reviewed-by: Defame1297 <gitea@rkdr.net>
2026-08-12 11:48:50 +00:00
eada85db99 docs(kyberforge): document source_keys as 4th apm-agent-allowlist field
field-inventory.md's apm-agent-allowlist and validate.sh's runtime
check already included source_keys as a 4th allowed field, and the
apm-agent.md template already instructed authors to add it for
provenance tracking — but SKILL.md (x2), README.md, ADR-0016, and
deployment-modes.md still described the allowlist as name/description/
model, "nothing else". The template itself even contradicted its own
source_keys guidance with a header claiming "ONLY the three fields
below — full stop" directly above it.

Updates all six locations to document source_keys as the intentional
4th field, resolving the contradiction.
2026-08-12 11:35:33 +00:00
044b2d3f08 fix(kyberforge): fix HOME/git scope-walkup false-FAILs in agent-audit
validate.sh's detect_scope() and validate-provenance.sh's
find_plugin_root() disagreed with new-agent.sh's already-correct,
documented walk-up semantics on three points, each causing validate.sh
to false-FAIL a legitimately-scaffolded project-scope agent pair:

- a marker-less directory walked up into $HOME (no .git/apm.yml of its
  own) was classified as user scope instead of project scope
- the .git-boundary branch returned the walked-to .git location instead
  of the conventional scope root, breaking any <root> that is a
  subdirectory of a larger git-tracked tree (monorepo package dirs)
- the new conventional-root arithmetic introduced to fix the above two
  cases had no guard against non-conventional/hand-placed file paths,
  which could point it at the wrong ancestor

Also adds scripts/check-scope-walkup-sync.sh, a behavioral drift-guard
(per ADR-0014's no-cross-skill-path precedent) that cross-checks the
four independently hand-ported walk-up implementations (validate.sh,
validate-provenance.sh, new-agent.sh, new-skill.sh) against real
fixture scaffolds, wired into .pre-commit-config.yaml at pre-push so
future drift between the ports is caught automatically.

Verified via bash tests/run-tests.sh (13/13) and targeted before/after
reproduction of each bug this closes.
2026-08-12 11:35:23 +00:00
6f6b70781d fix(kyberforge): fix scope walk-up and manifest-parsing bugs from PR #93 review
A fresh /code-review of the APM-native authoring retarget (PR #93) found
several correctness bugs beyond the ones already fixed on this branch:

- new-agent.sh silently walked a marker-less subdirectory under $HOME up
  to user scope, contradicting its own usage text ("user scope is checked
  directly, no walk-up") and risking scaffolding into shared global
  ~/.claude or ~/.copilot directories instead of the intended local path.
- The hand-copied apm.yml type: manifest detector in new-agent.sh and
  new-skill.sh accepted mismatched quotes (e.g. `type: "skill'`) that
  validate.sh's regex correctly rejects, and silently dropped a final
  apm.yml line lacking a trailing newline — causing the scaffolder and
  validator to disagree on scope for identical input.
- Plugin-scope agent frontmatter could still contain the apm-agent.md
  template's HTML comments at ship time with no audit signal, yet
  apm compile copies frontmatter verbatim and <!-- --> breaks YAML
  parsing on both downstream harnesses.
- ADR-0016 asserted agent-audit already implements a SUGGESTION heuristic
  for tool-restriction-needing plugin-scope agents; it doesn't.
- agent-audit/README.md still described the old plugin-pair model this
  PR replaced with a single-file allowlist model.
- validate.sh's project/user-scope CC-only/Copilot-only field checks and
  counterpart-missing check lost their only test coverage when the old
  plugin-pair fixture was deleted.

Also replaces an echo-into-sed two-value parse (4 forks per call) with a
single space-separated echo + read in both scaffolders.

Regression tests added for every fix above, including one for a bug this
pass introduced and the test suite caught: an initial two-line
echo + `read` attempt silently dropped the second value, since `read`
consumes only one line regardless of embedded newlines.

Full suite: 158 bats tests, 39 shell-script tests, 12/12 summary
categories, 0 failures.

Refs: #89, #93
2026-08-11 21:49:38 +00:00
f037d49b5c fix(kyberforge): fix apm-scope validation gaps in agent/skill authoring scripts
Post-implementation review of PR #93 (issue #89's apm.yml-native retargeting
of skill-author/skill-audit/agent-author/agent-audit) found four confirmed
defects across the four scripts' apm.yml `type:` walk-up logic:

- field-inventory.md's apm-agent-allowlist was missing `source_keys`,
  contradicting agent-author/SKILL.md's own instruction (Step 5 checklist)
  to allow it at plugin/APM scope — a correctly-authored file with
  source_keys failed validate.sh.
- validate.sh's APM_TYPE_RE and validate-provenance.sh's TYPE_RE disagreed:
  the former tolerated a quoted `type: "skill"` value, the latter didn't,
  despite agent-audit/SKILL.md explicitly documenting that
  validate-provenance.sh walks up "the same way validate.sh does". Both
  also used `\b` word-boundary matching, which false-matches a malformed
  value like `type: prompts-only` on the `prompts` prefix. Unified both
  regexes to be quote-tolerant and require an exact value.
- All four scripts' `.git` project-boundary check used isdir()/[[ -d ]],
  which misses git worktrees where `.git` is a regular file (`gitdir: ...`)
  rather than a directory. Switched to exists()/[[ -e ]].
- new-agent.sh and new-skill.sh had the same quote-intolerance as above via
  inline `grep -qE` calls (new-skill.sh's also had the `\b` false-match
  bug); replaced both with a shared-shape `is_apm_package_manifest` bash
  helper matching the Python regex's semantics.

Four other findings from the same review turned out not to be bugs: a
bare `plugin.json` no longer signaling plugin scope is documented,
intentional behavior (agent-audit/SKILL.md:30, agent-author/SKILL.md:87),
deferred to issue #90's real plugin.json-to-apm.yml conversion — not
something this fix should reverse.

Verified via direct reproduction of each defect plus the full test suite:
147/147 bats tests, 39/39 shell-script tests, 12/12 summary categories.

Refs: #89
2026-08-11 19:13:26 +00:00
96bc946030 docs(kyberforge): fix provenance chain gap in agent-primitive-schema.md
The file's body cites APM's Python source directly (apm_cli/models/validation.py,
apm_cli/primitives/models.py) to support its "no dedicated Agent validation"
claim, but source_keys only listed context7-microsoft-apm — the apm-github-repo
source_key was missing, and sources.md's apm-github-repo entry didn't list this
file under Contributing files either. The three sibling schema files from the
same commit (prompt/instructions/hooks) all cited both sources correctly.

Found via post-implementation review of issue #89.
2026-08-11 18:34:36 +00:00
ffebdc6584 fix(kyberforge): stop dotfiles-repo $HOME from shadowing user scope in validate.sh
detect_scope() had the same bug class fixed in new-agent.sh (099bdec):
it checked for a .git directory before checking whether it had reached
$HOME, so a dotfiles-managed home directory (yadm, chezmoi bare-repo,
etc.) made validate.sh misresolve to project scope, deriving the
counterpart as ~/.github/agents/<name>.agent.md instead of the correct
~/.copilot/agents/<name>.agent.md and failing with a false "counterpart
file not found". Check the $HOME boundary before the .git check, same
fix shape as 099bdec.

Found via post-implementation review of issue #89.
2026-08-11 18:34:27 +00:00
dc2a41034e fix(kyberforge): remove dead plugin-scope branch from validate.sh
check_file()'s is_plugin_scope param and its plugin-silently-ignored
field check were unreachable dead code left over from the issue #89
restructure: plugin/APM scope now exits via check_apm_agent_file()
before check_file() is ever called, so is_plugin was always False.
Remove the param, its branch, the unused plugin_ignored_fields parse,
the now-stale field-inventory.md section, and the SKILL.md mention.

Found via post-implementation review of issue #89.
2026-08-11 18:21:51 +00:00
099bdec1b2 fix(kyberforge): stop dotfiles-repo $HOME from shadowing user scope
new-agent.sh's walk-up checked for a .git directory before checking
whether it had reached $HOME, so a dotfiles-managed home directory
(e.g. `~/.git` from yadm or a bare-repo checkout) made `new-agent.sh
<name> ~` silently resolve to project scope instead of user scope,
writing .github/agents/ instead of ~/.copilot/agents/. Check the
$HOME boundary before the .git check so it can no longer be shadowed.

Found via post-implementation review of issue #89.
2026-08-11 18:21:44 +00:00
239ea41842 fix(kyberforge): swap plugin.json signals for apm.yml in skill-audit/forge
skill-audit's cross-plugin path check now also recognizes .apm/skills/
<other-skill>/ as a breaking reference shape, alongside the existing
plugins/<plugin>/skills/<other-skill>/ pattern — the APM-native equivalent
of the same cache-isolation problem.

forge.md's Step 4 plugin-version-bump detection switches from "any
directory containing a plugin.json" to the same apm.yml+type: walk-up
used elsewhere in this batch, and hands off to apm-workflow (bump the
single apm.yml version) instead of the deprecated plugin-author.

Refs: #89
2026-08-11 18:05:45 +00:00
675ba40238 feat(kyberforge): restructure agent-audit for plugin-scope apm agents
Validates the new single-file .apm/agents/<name>.agent.md shape agent-author
now produces at plugin/APM scope: frontmatter allowlist (name/description/
model only, from a new apm-agent-allowlist entry in field-inventory.md),
no counterpart derivation, and Pair Consistency dropped from that scope's
report entirely (nothing to pair by design). Adds a plugin/APM-scope-only
SUGGESTION when an agent's description/body implies a tool restriction or
Claude-only behavior the vendor-neutral frontmatter can no longer express
(ADR-0016).

Scope detection in both validate.sh and validate-provenance.sh switches
from a flat plugin.json/.claude-plugin/plugin.json check to a walk-up for
the nearest ancestor apm.yml with a top-level type: field, skipping
type:-less marketplace-only manifests — full switch, no dual-mode fallback
to the old plugin.json signal. validate-provenance.sh's walk-up was fixed
to match validate.sh's (it still used the old plugin.json check, and its
counterpart-merge logic was rewritten to read a single file's source_keys
instead of merging a CC+Copilot pair, since plugin/APM scope has no
counterpart). Project/user scope validation is unchanged in both scripts.

Refs: #89
2026-08-11 18:05:33 +00:00
8cd5c79c0a feat(kyberforge): retarget agent-author to plugin-scope .apm/agents/
Plugin scope now authors a single vendor-neutral .apm/agents/<name>.agent.md
file (name/description/model/body only) instead of a Claude Code + Copilot
CLI pair, per ADR-0016: apm compile has no per-target field integrator, so
tools: and all Claude-only fields (isolation/maxTurns/effort/memory/
permissionMode) are omitted entirely rather than shipping a value that's
guaranteed wrong on one harness. Scope detection switches from a flat
plugin.json check to the same apm.yml+type: walk-up agent-audit's
validate.sh already uses. Project scope and user scope are unaffected —
both keep the existing dual-file pair model.

Refs: #89
2026-08-11 18:05:23 +00:00
922eff3960 feat(kyberforge): retarget skill-author to author into .apm/skills/
Skills now migrate from hand-authored plugin.json to Microsoft APM
(apm.yml + .apm/) per ADR-0015/issue #89. skill-author's write location
switches accordingly: walk up from the destination to the nearest
ancestor apm.yml declaring a type: field (skipping type:-less
marketplace-only manifests) and scaffold into <package-root>/.apm/skills/
<name>/. Standalone authoring (no apm.yml/.git anywhere, e.g.
~/.agents/skills/) is unchanged — only the old plugin.json-based path is
replaced, not standalone mode.

Refs: #89
2026-08-11 18:03:25 +00:00
0dd044a782 docs(kyberforge): add ADR-0016, agent tools-field omitted at plugin scope
#89's grilling session surfaced a real upstream gap: APM's agent
primitive has no per-target compile integrator, so tools: and every
Claude-only field (isolation, maxTurns, effort, memory, permissionMode)
would verbatim-copy into Copilot's file with an incompatible or
unrecognized shape. Omitting them entirely is the only option that
never ships a wrong value to a real harness.

Scoped to plugin-scope agent-author only — project/user scope keep
today's Claude+Copilot pair model unchanged. Partially supersedes
ADR-0005 and ADR-0008's plugin-scope clauses (addenda appended to
both); ADR-0009's field-inventory.md mechanism is unaffected, only
its plugin-scope content shape changes.

Refs: #89
2026-08-11 17:17:32 +00:00
5e296bcfef docs(kyberforge): research all five APM primitive schemas
skill-author/agent-author's #89 retarget needs to know exactly how each
.apm/ primitive compiles to Claude Code and Copilot CLI output. The
existing microsoft-apm corpus only had a full schema for skills and one
minimal example for agents, and nothing for prompts/instructions/hooks.

Deepened via APM's own Python source (not just docs) where prose was
thin. Key finding for #89: agents have no per-target integrator, so
apm compile does a naive verbatim copy to both Claude and Copilot,
unlike prompts/instructions/hooks which each get real per-target
reconstruction. That means the agent primitive's `tools:` field can't
express both harnesses' incompatible vocabularies at once — a real
upstream gap, not something we can schema our way around.
2026-08-11 16:39:54 +00:00
Claude Code AI - Gitea MCP
7a5c50fecc Merge pull request 'feat(kyberforge): add apm-install/apm-workflow/apm-orchestrate, ADR-0015' (#91) from feat/88-marketplace-apm-conversion into main 2026-08-11 16:05:56 +00:00
591b9cccb8 fix(kyberforge): resolve second round of PR #91 review findings
Two review passes on PR #91 (correctness + simplification) found the
prior fixup commit (d6fd9b6) left the deprecation notice it added
half-applied, cited a nonexistent ADR, and introduced/compounded a
few duplicate restatements of the same facts across apm-workflow's
skill files and apm-orchestrate's Hard rules.

- plugin-author/SKILL.md cited ADR-0016 (doesn't exist) instead of
  ADR-0006 for the version-parity invariant.
- The deprecation banner on plugin-author/marketplace-author wasn't
  reflected in their routing `description:` frontmatter, forge's
  routing table, either kyberforge README skill table, or CONTEXT.md
  — now propagated consistently; routing behavior is unchanged since
  issue #90 (repo-wide apm conversion) hasn't landed yet.
- apm-orchestrate's `configure` operation only covered scaffolding a
  new package, not editing an existing apm.yml (e.g. removing a
  marketplace.packages[] entry, which has no dedicated `apm` CLI verb
  and is just a manifest edit). Added `edit-config`, intentionally
  ungated — a local file edit isn't the same class of irreversible
  action as `apm publish`, and normal review already gates the commit.
- Deduplicated facts restated 3-4x across SKILL.md/references/ADR
  boundaries (audit semantics, reserved name prefixes, marketplace-add
  direction, registries precondition), and trimmed configure.md's
  ~55-line apm.yml schema dump — copied near-verbatim from the
  research doc — down to a compact key list with a pointer, matching
  install.md's existing terse cross-referencing style.

Deliberately left as-is: apm-orchestrate's parallel/sequential
fan-out logic stays inline rather than extracted to a shared
reference, since neither git-orchestrate nor gitea-orchestrate need
it yet — premature extraction for a single caller.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186ERbyACLRuRxPRnqwpa4m
2026-08-11 15:20:46 +00:00
d6fd9b6770 fix(kyberforge): resolve PR #91 review findings in apm docs and orchestrators
Addresses 7 verified findings from the /code-review pass on PR #91's
APM-conversion changes:

- apm-workflow's description omitted install triggers, misrouting
  "install my apm dependencies" requests to apm-install instead
- `apm marketplace check` was listed under both the marketplace and
  audit dispatch rows, breaking the "one reference file per action"
  contract; kept under marketplace.md, its real usage-sequence home
- ADR-0015 claimed apm-workflow docs are generic/repo-agnostic while
  marketplace.md cited this repo's AGENTS.md and git-commits/
  git-remotes skills by name; stripped the holocron-specific
  citations (marketplace.md, compile.md) so the claim now holds
- plugin-author/marketplace-author are superseded per ADR-0015 with
  deletion deferred to #90; added deprecation notices pointing to
  the replacing apm-workflow dispatch actions
- apm-orchestrate/gitea-orchestrate/git-orchestrate all instruct
  dispatch "via Skill" without granting the Skill tool; added it to
  all three
- apm-orchestrate mandated strictly sequential fan-out across
  independent packages with no shared state; relaxed to permit
  parallel dispatch, matching this repo's own subagent-parallelization
  guidance

Refs: #91
2026-08-11 14:22:31 +00:00
92e7ff26aa docs(kyberforge): narrow issue #89 scope, migrate CC platform facts to apm-workflow
A grill-me session determined plugin-author/marketplace-author are fully
superseded by the landed apm-install/apm-workflow/apm-orchestrate artifacts
and will be deleted (not adapted) under issue #90, narrowing issue #89 to
skill-author/agent-author only; forge stays untouched. ADR-0015 is amended
to record this correction plus a three-way content-migration audit.

Claude Code platform-constraint facts that lived only in
plugin-author/marketplace-author (reserved name prefixes, the ADR-0010
agents/ stray-.md validator gotcha, claude plugin validate as a terminal
check, and a removal/re-pack confirm-before-proceeding note) remain true
post-conversion and are migrated into apm-workflow's reference docs via
kyberforge:skill-author, which also updated the skill's README and
sources.md provenance records. Dual-manifest-specific conventions (ADR-0006
parity, CC/Copilot field-placement split) do not carry forward since they
no longer apply.

ADR: 0015
2026-08-11 12:09:43 +00:00
394052ff66 docs(kyberforge): fix remaining PR #91 review findings, add apm install routing
Re-review (comment 24) of fix commit e16c3dc found six new issues, mostly
introduced by that fix commit itself: a dangling reference to a Hard Rule
bullet the same commit deleted (apm-orchestrate.md/.agent.md Process step
2 still named "secret indirection"), and an ADR-0015 Decision bullet that
claimed "this ADR does not update CONTEXT.md" while the same commit had
just added a forward-pointer sentence to CONTEXT.md's Plugin/Plugin
marketplace entries. Both reworded to match what actually happened.

apm-install's APM_INSTALL_DIR escape-hatch example dropped the curl pipe
entirely (`APM_INSTALL_DIR=... sh` with nothing piped into it) — fixed in
both apm-install/SKILL.md and the installation.md research doc, verified
against the upstream Microsoft APM docs via Context7.

Neither apm-workflow nor apm-orchestrate routed to plain `apm install
[PACKAGE_REF]`, the CLI command that actually resolves/fetches
dependencies declared in apm.yml — apm-install only bootstraps the apm
binary/runtime, not per-package deps. Added a 5th "install" dispatch
action to apm-workflow (new references/install.md, SKILL.md table row,
README usage/files sync, sources.md provenance entry) and a matching
"install" operation group on apm-orchestrate so it can route there.

configure.md's apm.yml schema block was also missing the "legacy singular
`target:` CSV form is still accepted" caveat its sibling research doc
documents for the same field — added for consistency.

The sixth finding (paired .md/.agent.md Output-contract disagreement) was
checked against git-orchestrate and gitea-orchestrate's existing pairs and
found to match established repo convention (JSON schema in .md, prose
summary without the enum in .agent.md) — left unchanged as a false
positive rather than "fixed."

kyberforge bumped 1.3.0 -> 1.3.1 via agent-author's normal improve flow.

Refs: #91

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186ERbyACLRuRxPRnqwpa4m
2026-08-11 11:09:43 +00:00
e16c3dc95f docs(kyberforge): fix PR #91 review findings in ADR-0015 and apm docs
ADR-0015's Decision bullet stated ADR-0001 was superseded as present
fact while Consequences framed it as conditional on issue #90 —
reworded Decision to match, and added a Supersedes header (repo
convention per ADR-0007/ADR-0010) so the pending change is visible
from the title. Also fixed a forward-note that misattributed the
pre-existing .agent.md convention (ADR-0005/0010) to the APM move,
and reworded the rejected-plugins/apm/ rationale to acknowledge the
shipped skills are actually generic rather than repo-specific.

CONTEXT.md's Plugin/Plugin-marketplace glossary entries got a
forward-pointer to ADR-0015 so a session-start read surfaces the
pending manifest-authoring change.

apm-orchestrate.md/.agent.md: removed a Hard Rule requiring ${VAR}
secret indirection that can never fire (the orchestrator has no
Edit/Write tool and never touches apm.yml — apm-workflow's SKILL.md
already owns this rule where content is actually written); removed
compile-manifest-check, an operation with no backing apm CLI command
anywhere in the research docs; added explicit CLI mappings for
add-package/add-marketplace so an executing agent can't invert them.

marketplace.md's release steps shelled out to raw git add/commit/
tag/push — replaced with a pointer to the git-commits/git-remotes
skills per AGENTS.md's "prefer plugin skills over raw shell" rule.

compile.md now states audit runs after compile/pack (audit scans
deployed output, not the source apm.yml) — answers a reviewer
question that had no documented answer.

Refs: #91

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186ERbyACLRuRxPRnqwpa4m
2026-08-10 21:23:07 +00:00
2305f1c315 chore(config): enable lint plugin in settings
Adds lint@holocron to enabledPlugins so Vale-based prose linting
skills are available in this repo.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186ERbyACLRuRxPRnqwpa4m
2026-08-10 19:27:34 +00:00
0aa66fe65d feat(kyberforge): add apm-orchestrate agent
Deterministic counterpart to apm-workflow for subagent dispatch,
mirroring git-orchestrate/gitea-orchestrate. Scoped to
configure/marketplace/compile/audit, with fan-out across multiple
packages for the future multi-plugin conversion; apm-install has no
orchestrator counterpart since it's a one-time machine bootstrap.

Bumps kyberforge 1.2.8 -> 1.3.0 (new agent, first in the plugin).
2026-08-10 17:49:12 +00:00
fc69553ba7 feat(kyberforge): add apm-workflow skill
Human-facing dispatch over apm's configure/marketplace/compile/audit
lifecycle, one reference file per concern, gitea-issues-style
dispatch table. apm-install handles the one-time binary/runtime
bootstrap that precedes this loop.
2026-08-10 17:46:03 +00:00
c48c9f5490 feat(kyberforge): add apm-install skill
Wraps apm CLI binary install and agent-runtime setup
(apm runtime setup/list/status/remove), the bootstrap step
ahead of apm-workflow's configure/compile/audit loop.
2026-08-10 17:43:23 +00:00
0e421acdbb docs(adr): add ADR-0015 for outright APM conversion
Records the grill-with-docs decision on issue #88: replace the
hand-authored plugin/marketplace manifest model with Microsoft APM
(apm.yml + .apm/) as this repo's authoring source of truth. The
plugins/<name>/ monorepo-hybrid layout survives; marketplace.json and
provider plugin.json files become compiled output. Supersedes
ADR-0001; touches but does not resolve ADR-0006/0010/0014. Follow-up
work tracked in issues #89 and #90.
2026-08-10 17:42:31 +00:00
1d07d1a76b docs(kyberforge): add Microsoft APM research reference set
Capture Microsoft's Agent Package Manager (APM) — overview, install,
config, CLI reference, registries/marketplace, monorepo shapes,
testing/validation, troubleshooting, and examples — as structured
reference docs under plugins/kyberforge/docs/research/docs/microsoft-apm/.

Lays the groundwork for issue #88 (build agents/skills to execute a
marketplace-to-APM conversion of this repo).
2026-08-10 17:06:07 +00:00
350 changed files with 20226 additions and 2767 deletions

View File

@@ -0,0 +1,95 @@
{
"interface": {
"displayName": "holocron"
},
"name": "holocron",
"plugins": [
{
"category": "Developer Tools",
"name": "kyberforge",
"policy": {
"authentication": "ON_INSTALL",
"installation": "AVAILABLE"
},
"source": {
"path": "./plugins/kyberforge",
"source": "local"
}
},
{
"category": "Utilities",
"name": "bin",
"policy": {
"authentication": "ON_INSTALL",
"installation": "AVAILABLE"
},
"source": {
"path": "./plugins/bin",
"source": "local"
}
},
{
"category": "Version Control",
"name": "git",
"policy": {
"authentication": "ON_INSTALL",
"installation": "AVAILABLE"
},
"source": {
"path": "./plugins/git",
"source": "local"
}
},
{
"category": "Version Control",
"name": "gitea",
"policy": {
"authentication": "ON_INSTALL",
"installation": "AVAILABLE"
},
"source": {
"path": "./plugins/gitea",
"source": "local"
}
},
{
"category": "Productivity",
"name": "core",
"policy": {
"authentication": "ON_INSTALL",
"installation": "AVAILABLE"
},
"source": {
"path": "./plugins/core",
"source": "local"
}
},
{
"category": "Productivity",
"name": "mattpocock-skills",
"policy": {
"authentication": "ON_INSTALL",
"installation": "AVAILABLE"
},
"source": {
"ref": "v1.2.3",
"sha": "835450ef244ab7335f75d95b83e7d979eae22a6d",
"source": "url",
"tag_pattern": "v{version}",
"url": "mattpocock/skills"
}
},
{
"category": "Developer Tools",
"name": "lint",
"policy": {
"authentication": "ON_INSTALL",
"installation": "AVAILABLE"
},
"source": {
"path": "./plugins/lint",
"source": "local"
}
}
]
}

View File

@@ -2,48 +2,64 @@
"description": "AI development skills for Claude Code and GitHub Copilot CLI \u2014 factory, design, implement, review, and cross-cutting workflows.", "description": "AI development skills for Claude Code and GitHub Copilot CLI \u2014 factory, design, implement, review, and cross-cutting workflows.",
"name": "holocron", "name": "holocron",
"owner": { "owner": {
"email": "defame1297@rkdr.net", "name": "Defame1297",
"name": "Defame1297" "url": "https://git.dev.rkdr.net/Defame1297/"
}, },
"plugins": [ "plugins": [
{ {
"category": "Developer Tools",
"description": "Skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace.", "description": "Skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace.",
"name": "kyberforge", "name": "kyberforge",
"source": "./plugins/kyberforge" "source": "./plugins/kyberforge",
"version": "1.4.0"
}, },
{ {
"category": "Utilities",
"description": "A place for things to be binned", "description": "A place for things to be binned",
"name": "bin", "name": "bin",
"source": "./plugins/bin" "source": "./plugins/bin",
"version": "1.1.1"
}, },
{ {
"category": "Version Control",
"description": "Skills for working with Git \u2014 conventional commits, branch management, pull requests, and feature flow.", "description": "Skills for working with Git \u2014 conventional commits, branch management, pull requests, and feature flow.",
"name": "git", "name": "git",
"source": "./plugins/git" "source": "./plugins/git",
"version": "1.3.2"
}, },
{ {
"category": "Version Control",
"description": "Skills for managing Gitea repositories \u2014 issues, pull requests, milestones, releases, and wikis.", "description": "Skills for managing Gitea repositories \u2014 issues, pull requests, milestones, releases, and wikis.",
"name": "gitea", "name": "gitea",
"source": "./plugins/gitea" "source": "./plugins/gitea",
"version": "1.3.3"
}, },
{ {
"category": "Productivity",
"description": "Cross-cutting utility skills for everyday AI-assisted coding \u2014 triage, diagnosis, architecture review, and session navigation.", "description": "Cross-cutting utility skills for everyday AI-assisted coding \u2014 triage, diagnosis, architecture review, and session navigation.",
"name": "core", "name": "core",
"source": "./plugins/core" "source": "./plugins/core",
"version": "1.1.0"
}, },
{ {
"category": "Productivity",
"description": "Skills for Real Engineers \u2014 planning, TDD, architecture, and debugging workflows from Matt Pocock's .claude directory.", "description": "Skills for Real Engineers \u2014 planning, TDD, architecture, and debugging workflows from Matt Pocock's .claude directory.",
"name": "mattpocock-skills", "name": "mattpocock-skills",
"source": { "source": {
"ref": "v1.2.3",
"repo": "mattpocock/skills", "repo": "mattpocock/skills",
"source": "github" "sha": "835450ef244ab7335f75d95b83e7d979eae22a6d",
"source": "github",
"tag_pattern": "v{version}"
} }
}, },
{ {
"category": "Developer Tools",
"description": "Skills and agents for configuring and running linters.", "description": "Skills and agents for configuring and running linters.",
"name": "lint", "name": "lint",
"source": "./plugins/lint" "source": "./plugins/lint",
"version": "1.1.5"
} }
], ],
"version": "0.3.1" "version": "0.3.2"
} }

View File

@@ -4,7 +4,8 @@
"core@holocron": true, "core@holocron": true,
"git@holocron": true, "git@holocron": true,
"gitea@holocron": true, "gitea@holocron": true,
"kyberforge@holocron": true "kyberforge@holocron": true,
"lint@holocron": true
}, },
"hooks": { "hooks": {
"PreToolUse": [] "PreToolUse": []

View File

@@ -2,48 +2,64 @@
"description": "AI development skills for Claude Code and GitHub Copilot CLI \u2014 factory, design, implement, review, and cross-cutting workflows.", "description": "AI development skills for Claude Code and GitHub Copilot CLI \u2014 factory, design, implement, review, and cross-cutting workflows.",
"name": "holocron", "name": "holocron",
"owner": { "owner": {
"email": "defame1297@rkdr.net", "name": "Defame1297",
"name": "Defame1297" "url": "https://git.dev.rkdr.net/Defame1297/"
}, },
"plugins": [ "plugins": [
{ {
"category": "Developer Tools",
"description": "Skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace.", "description": "Skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace.",
"name": "kyberforge", "name": "kyberforge",
"source": "./plugins/kyberforge" "source": "./plugins/kyberforge",
"version": "1.4.0"
}, },
{ {
"category": "Utilities",
"description": "A place for things to be binned", "description": "A place for things to be binned",
"name": "bin", "name": "bin",
"source": "./plugins/bin" "source": "./plugins/bin",
"version": "1.1.1"
}, },
{ {
"category": "Version Control",
"description": "Skills for working with Git \u2014 conventional commits, branch management, pull requests, and feature flow.", "description": "Skills for working with Git \u2014 conventional commits, branch management, pull requests, and feature flow.",
"name": "git", "name": "git",
"source": "./plugins/git" "source": "./plugins/git",
"version": "1.3.2"
}, },
{ {
"category": "Version Control",
"description": "Skills for managing Gitea repositories \u2014 issues, pull requests, milestones, releases, and wikis.", "description": "Skills for managing Gitea repositories \u2014 issues, pull requests, milestones, releases, and wikis.",
"name": "gitea", "name": "gitea",
"source": "./plugins/gitea" "source": "./plugins/gitea",
"version": "1.3.3"
}, },
{ {
"category": "Productivity",
"description": "Cross-cutting utility skills for everyday AI-assisted coding \u2014 triage, diagnosis, architecture review, and session navigation.", "description": "Cross-cutting utility skills for everyday AI-assisted coding \u2014 triage, diagnosis, architecture review, and session navigation.",
"name": "core", "name": "core",
"source": "./plugins/core" "source": "./plugins/core",
"version": "1.1.0"
}, },
{ {
"category": "Productivity",
"description": "Skills for Real Engineers \u2014 planning, TDD, architecture, and debugging workflows from Matt Pocock's .claude directory.", "description": "Skills for Real Engineers \u2014 planning, TDD, architecture, and debugging workflows from Matt Pocock's .claude directory.",
"name": "mattpocock-skills", "name": "mattpocock-skills",
"source": { "source": {
"ref": "v1.2.3",
"repo": "mattpocock/skills", "repo": "mattpocock/skills",
"source": "github" "sha": "835450ef244ab7335f75d95b83e7d979eae22a6d",
"source": "github",
"tag_pattern": "v{version}"
} }
}, },
{ {
"category": "Developer Tools",
"description": "Skills and agents for configuring and running linters.", "description": "Skills and agents for configuring and running linters.",
"name": "lint", "name": "lint",
"source": "./plugins/lint" "source": "./plugins/lint",
"version": "1.1.5"
} }
], ],
"version": "0.3.1" "version": "0.3.2"
} }

View File

@@ -28,6 +28,7 @@ repos:
- id: pretty-format-json - id: pretty-format-json
stages: ['pre-commit'] stages: ['pre-commit']
args: [--autofix] args: [--autofix]
exclude: '(^|/)(\.claude-plugin/plugin\.json|\.github/plugin/plugin\.json|\.claude-plugin/marketplace\.json)$|^\.agents/plugins/marketplace\.json$'
- id: check-yaml - id: check-yaml
stages: ['pre-commit'] stages: ['pre-commit']
- id: trailing-whitespace - id: trailing-whitespace
@@ -61,6 +62,51 @@ repos:
pass_filenames: false pass_filenames: false
always_run: true always_run: true
- id: check-plugin-content-sync
name: Check plugin content sync
description: Verify each plugin's flat skills/agents/commands/hooks.json mirror is in sync with .apm/ -- Claude Code has no .apm/ awareness so this compiled mirror must stay current (see issue #90)
entry: bash scripts/sync-plugin-content.sh --check --all
language: system
stages: [pre-push]
pass_filenames: false
always_run: true
- id: check-marketplace-mirror-sync
name: Check marketplace mirror sync
description: Verify .github/plugin/marketplace.json (Copilot CLI's legacy manifest path) is byte-identical to .claude-plugin/marketplace.json -- apm has no output profile for this path, so it must be kept in sync explicitly (see issue #90)
entry: bash scripts/sync-marketplace-mirror.sh --check
language: system
stages: [pre-push]
pass_filenames: false
always_run: true
- id: apm-marketplace-check
name: apm marketplace check
description: Validate every marketplace.packages[] entry resolves, including network reachability of remote refs -- catches stale/unreachable remote package references that check-manifests.sh deliberately skips (local-source checks only)
entry: apm marketplace check
language: system
stages: [pre-push]
pass_filenames: false
always_run: true
- id: apm-audit-ci
name: apm audit --ci
description: apm's own producer-side lockfile/policy/hidden-content integrity gate, per apm's documented recommended CI block (see docs/research/docs/microsoft-apm/testing-and-validation.md)
entry: apm audit --ci
language: system
stages: [pre-push]
pass_filenames: false
always_run: true
- id: apm-pack-check-clean
name: apm pack --check-clean
description: Release gate -- verify .claude-plugin/marketplace.json still matches what apm.yml + .apm/ would currently generate, and that per-package versions agree with the per_package versioning strategy. Closes issue #90's deferred item 3 (a check-clean-equivalent gate) using apm's own flag instead of custom drift logic.
entry: apm pack --check-versions --check-clean --dry-run
language: system
stages: [pre-push]
pass_filenames: false
always_run: true
- id: check-vale-style-sync - id: check-vale-style-sync
name: Check Vale style copies are in sync name: Check Vale style copies are in sync
description: Diff skill-audit's Vale copy against agent-audit's canonical copy description: Diff skill-audit's Vale copy against agent-audit's canonical copy
@@ -70,6 +116,15 @@ repos:
pass_filenames: false pass_filenames: false
always_run: true always_run: true
- id: check-scope-walkup-sync
name: Check scope walk-up implementations agree
description: Behaviorally cross-check validate.sh, validate-provenance.sh, new-agent.sh, and new-skill.sh's independent $HOME/.git/apm.yml walk-up ports against each other
entry: bash scripts/check-scope-walkup-sync.sh
language: system
stages: [pre-push]
pass_filenames: false
always_run: true
- id: check-release-needed - id: check-release-needed
name: Check a release tag covers .pre-commit-hooks.yaml's paths name: Check a release tag covers .pre-commit-hooks.yaml's paths
description: On push to main only, fail if files exposed via .pre-commit-hooks.yaml changed since the last tag description: On push to main only, fail if files exposed via .pre-commit-hooks.yaml changed since the last tag
@@ -103,7 +158,7 @@ repos:
description: Ensure SKILL.md files have required frontmatter fields description: Ensure SKILL.md files have required frontmatter fields
entry: bash entry: bash
language: system language: system
files: 'SKILL\.md$' files: '^plugins/[^/]+/\.apm/skills/[^/]+/SKILL\.md$'
args: args:
- -c - -c
- | - |
@@ -122,25 +177,25 @@ repos:
description: Enforce agentskills.io's 500-line/5,000-token SKILL.md size ceiling description: Enforce agentskills.io's 500-line/5,000-token SKILL.md size ceiling
entry: scripts/skill-size-check.sh entry: scripts/skill-size-check.sh
language: script language: script
files: '^plugins/[^/]+/skills/[^/]+/SKILL\.md$' files: '^plugins/[^/]+/\.apm/skills/[^/]+/SKILL\.md$'
pass_filenames: true pass_filenames: true
- id: vale-audit-prefilter-skill - id: vale-audit-prefilter-skill
stages: ['pre-commit'] stages: ['pre-commit']
name: Vale audit prefilter (SKILL.md) name: Vale audit prefilter (SKILL.md)
description: Run Vale against SKILL.md files as a deterministic prefilter for skill-audit, via skill-audit's own bundled copy description: Run Vale against SKILL.md files as a deterministic prefilter for skill-audit, via skill-audit's own bundled copy
entry: plugins/kyberforge/skills/skill-audit/scripts/vale-wrap.sh entry: plugins/kyberforge/.apm/skills/skill-audit/scripts/vale-wrap.sh
language: script language: script
files: '^plugins/[^/]+/skills/[^/]+/SKILL\.md$' files: '^plugins/[^/]+/\.apm/skills/[^/]+/SKILL\.md$'
pass_filenames: true pass_filenames: true
- id: vale-audit-prefilter-agent - id: vale-audit-prefilter-agent
stages: ['pre-commit'] stages: ['pre-commit']
name: Vale audit prefilter (agent files) name: Vale audit prefilter (agent files)
description: Run Vale against agent markdown files as a deterministic prefilter for agent-audit, via agent-audit's own bundled copy description: Run Vale against agent markdown files as a deterministic prefilter for agent-audit, via agent-audit's own bundled copy
entry: plugins/kyberforge/skills/agent-audit/scripts/vale-wrap.sh entry: plugins/kyberforge/.apm/skills/agent-audit/scripts/vale-wrap.sh
language: script language: script
files: '^plugins/[^/]+/agents/[^/]+\.md$' files: '^plugins/[^/]+/\.apm/agents/[^/]+\.agent\.md$'
pass_filenames: true pass_filenames: true
- repo: meta - repo: meta

View File

@@ -1,14 +1,14 @@
- id: kyberforge-vale-audit-skill - id: kyberforge-vale-audit-skill
name: Kyberforge Vale prose audit (SKILL.md) name: Kyberforge Vale prose audit (SKILL.md)
description: Deterministic prose-pattern prefilter for kyberforge's skill-audit, via its own bundled Vale config/styles description: Deterministic prose-pattern prefilter for kyberforge's skill-audit, via its own bundled Vale config/styles
entry: plugins/kyberforge/skills/skill-audit/scripts/vale-wrap.sh entry: plugins/kyberforge/.apm/skills/skill-audit/scripts/vale-wrap.sh
language: script language: script
files: '(^|/)SKILL\.md$' files: '(^|/)SKILL\.md$'
- id: kyberforge-vale-audit-agent - id: kyberforge-vale-audit-agent
name: Kyberforge Vale prose audit (agent files) name: Kyberforge Vale prose audit (agent files)
description: Deterministic prose-pattern prefilter for kyberforge's agent-audit, via its own bundled Vale config/styles description: Deterministic prose-pattern prefilter for kyberforge's agent-audit, via its own bundled Vale config/styles
entry: plugins/kyberforge/skills/agent-audit/scripts/vale-wrap.sh entry: plugins/kyberforge/.apm/skills/agent-audit/scripts/vale-wrap.sh
language: script language: script
files: '(^|/)agents/[^/]+\.md$|\.agent\.md$' files: '(^|/)agents/[^/]+\.md$|\.agent\.md$'

View File

@@ -22,7 +22,7 @@ Fall back to raw shell only when no skill covers it.
## Setup and testing ## Setup and testing
- Install git hooks via `git:pc-run`, wiring all three stages — this repo's `.pre-commit-config.yaml` has no `default_install_hook_types`, so a plain install silently skips `commit-msg` (Conventional Commits) and `pre-push` (tests, manifest check). - Install git hooks via `git:pc-run`, wiring all three stages — this repo's `.pre-commit-config.yaml` has no `default_install_hook_types`, so a plain install silently skips `commit-msg` (Conventional Commits) and `pre-push` (tests, manifest check).
- Install the `vale` binary — required by the `vale-audit-prefilter-skill`/`-agent` pre-commit hooks, which run on every commit touching a `SKILL.md` or agent `.md` file. Without it the hooks fail with a bare "command not found" and no install pointer. `brew install vale` (macOS), `snap install vale` (Linux), `choco install vale` (Windows), or see https://vale.sh/docs/vale-cli/installation/. No `vale sync` needed — the `Kyberforge` styles are committed under `plugins/kyberforge/skills/{skill-audit,agent-audit}/assets/vale/styles/`, not downloaded packages (see ADR-0014). - Install the `vale` binary — required by the `vale-audit-prefilter-skill`/`-agent` pre-commit hooks, which run on every commit touching a `SKILL.md` or agent `.md` file. Without it the hooks fail with a bare "command not found" and no install pointer. `brew install vale` (macOS), `snap install vale` (Linux), `choco install vale` (Windows), or see https://vale.sh/docs/vale-cli/installation/. No `vale sync` needed — the `Kyberforge` styles are committed under `plugins/kyberforge/.apm/skills/{skill-audit,agent-audit}/assets/vale/styles/`, not downloaded packages (see ADR-0014).
- Run `bash tests/run-tests.sh` before considering any change done — it runs every `test-*.sh` script in the repo plus the bats suite (`--bats-only` for just bats). First run auto-initializes the bats submodules; no manual `git submodule update` needed. - Run `bash tests/run-tests.sh` before considering any change done — it runs every `test-*.sh` script in the repo plus the bats suite (`--bats-only` for just bats). First run auto-initializes the bats submodules; no manual `git submodule update` needed.
- Pushing re-runs the full suite plus `scripts/check-manifests.sh` via the pre-push hook — same commands, so run them locally first. - Pushing re-runs the full suite plus `scripts/check-manifests.sh` via the pre-push hook — same commands, so run them locally first.
- Author commits with `git:git-commits` — it validates Conventional Commits (enforced at `commit-msg`) for you. - Author commits with `git:git-commits` — it validates Conventional Commits (enforced at `commit-msg`) for you.

View File

@@ -24,13 +24,13 @@ Before answering any design or architecture question, check for existing decisio
A separate product (separate repo) for browsing, editing, and configuring AI development configs through a proper product UI. Git is the persistence layer, invisible to the user. The app is repo-agnostic — it works with any git repo that follows these conventions. This repo is the canonical default content (the official starter). See `docs/VISION.md` for the phased roadmap. A separate product (separate repo) for browsing, editing, and configuring AI development configs through a proper product UI. Git is the persistence layer, invisible to the user. The app is repo-agnostic — it works with any git repo that follows these conventions. This repo is the canonical default content (the official starter). See `docs/VISION.md` for the phased roadmap.
### Skills ### Skills
Reusable slash commands for AI coding tools, defined as `SKILL.md` files following the [Agent Skills open standard](https://agentskills.io). Deployed via plugin — `plugins/<plugin-name>/skills/<skill-name>/SKILL.md`, available after the plugin is installed (`claude plugin install <name>@<marketplace>`). Skills are self-contained — they cannot reference files outside the plugin directory after install-time caching. Reusable slash commands for AI coding tools, defined as `SKILL.md` files following the [Agent Skills open standard](https://agentskills.io). Deployed via plugin — `plugins/<plugin-name>/.apm/skills/<skill-name>/SKILL.md`, available after the plugin is installed (`claude plugin install <name>@<marketplace>`). Skills are self-contained — they cannot reference files outside the plugin directory after install-time caching.
### Plugin ### Plugin
The deployable unit in the plugin marketplace. A plugin bundles one or more skills, agents, hooks, prompts, MCP servers, and optionally a `bin/` directory into a single installable directory. Each plugin has two manifests: `.claude-plugin/plugin.json` (Claude Code) and `plugin.json` at the plugin root (Copilot CLI). Plugins are copied to a cache on install — they cannot reference files outside their own directory. In this repo, plugins live under `plugins/<name>/`. Install a plugin with `claude plugin install <name>@<marketplace>`. The deployable unit in the plugin marketplace. A plugin bundles one or more skills, agents, hooks, prompts, MCP servers, and optionally a `bin/` directory into a single installable directory. In this repo, plugins live under `plugins/<name>/`, each with its own `apm.yml` + `.apm/{skills,agents,hooks,...}` — this is the authoring source of truth for the plugin's content (ADR-0015). Two categories of tracked output are compiled from that source, never hand-edited: `.claude-plugin/plugin.json` (Claude Code) and `.github/plugin/plugin.json` (Copilot CLI) via `apm pack`/`apm compile`; and, alongside them, a flat `agents/`, `skills/`, `commands/`, `instructions/`, `extensions/` directory mirror plus a merged `hooks.json` at the plugin root, generated by `scripts/sync-plugin-content.sh` — Claude Code's and Copilot's installers convention-scan only these flat root-level paths and have no awareness of `.apm/` nesting at all, so this mirror is what actually makes `.apm/` content discoverable at install time (ADR-0017). Plugins are copied to a cache on install — they cannot reference files outside their own directory. Install a plugin with `claude plugin install <name>@<marketplace>`.
### Plugin marketplace ### Plugin marketplace
A Git repository with a `marketplace.json` manifest listing installable plugins. No backend, registry, or SaaS required — the Git repo is the marketplace. This repo is the `holocron` marketplace. The manifest lives at `.claude-plugin/marketplace.json` (read by both Claude Code and Copilot CLI) and is mirrored to `.github/plugin/marketplace.json`. A Git repository with a `marketplace.json` manifest listing installable plugins. No backend, registry, or SaaS required — the Git repo is the marketplace. This repo is the `holocron` marketplace. The manifest at `.claude-plugin/marketplace.json` (read by both Claude Code and Copilot CLI) is **compiled output** of `apm pack`, generated from the root `apm.yml`'s `marketplace:` block (owner, build/output config, versioning strategy, and the `packages:` list of installable plugins) — it is not hand-edited. See ADR-0015. `.github/plugin/marketplace.json` is Copilot CLI's legacy manifest path; apm has no output profile for it (only `claude` and `codex`, and `codex`'s is a differently-shaped file at `.agents/plugins/marketplace.json`), so `scripts/sync-marketplace-mirror.sh` keeps it byte-identical to `.claude-plugin/marketplace.json`, checked at pre-push. Each listed package's `source:` still points at that plugin's own `plugins/<name>/` root, not at an `apm pack` build artifact — which is why that root also carries the flat `agents/`/`skills/`/`commands/`/`hooks.json` content mirror described under "Plugin" (ADR-0017): without it, an install from this marketplace finds a valid manifest but no discoverable content.
### HITL (human-in-the-loop) ### HITL (human-in-the-loop)
Agent pauses before a consequential action; human approves before execution. Required for irreversible or high-stakes actions (architecture changes, production deployments, security configuration). The agent drafts the change plan and waits — it does not proceed autonomously. Contrast with HOTL. Agent pauses before a consequential action; human approves before execution. Required for irreversible or high-stakes actions (architecture changes, production deployments, security configuration). The agent drafts the change plan and waits — it does not proceed autonomously. Contrast with HOTL.
@@ -49,10 +49,10 @@ The provider-agnostic always-on instruction entry point. Two files:
Contains always-on rules in plain markdown with no provider-specific syntax (no `@import`). Provider-specific files (`CLAUDE.md`) are thin adapters that import the relevant `AGENTS.md` and add only Claude Code-specific syntax. This pattern means a single source of truth can serve multiple providers without duplication. See ADR-0003. Contains always-on rules in plain markdown with no provider-specific syntax (no `@import`). Provider-specific files (`CLAUDE.md`) are thin adapters that import the relevant `AGENTS.md` and add only Claude Code-specific syntax. This pattern means a single source of truth can serve multiple providers without duplication. See ADR-0003.
### Skill composition ### Skill composition
A skill calling another skill by name to delegate a sub-task. The calling skill focuses on the orchestration decision ("when to do X"); the called skill owns the mechanics ("how to do X"). Established compositions: `grill-me` calls `write-adr` when a decision crystallises; `implement-feature` calls `tdd` as its implementation methodology; `forge` calls `grill-with-docs` to refine intent, classifies the target artifact type (skill / agent / plugin / marketplace entry), then routes to the matching `*-author` skill — which owns its own create/improve logic and, where applicable, its own inline audit closeout (`skill-author` runs `/skill-audit`, `agent-author` runs `kyberforge:agent-audit`, both in the same context as the authoring work). Reserve `forge` for genuinely undecided "which artifact type is this" questions — an already-fully-specified corrective edit (exact file, line, and fix already known) should call the target author skill directly instead (`skill-author`, `plugin-author`, `agentsmd-author`, etc.); routing a known fix through `forge`'s grill-and-classify layer adds unnecessary indirection and, in practice, has been observed to lose track of hard constraints handed down the chain (e.g. "don't commit yet," "edit in this worktree") because each hop re-derives instructions from a shorter brief. `forge` additionally runs its own independent recheck after a skill/agent route finishes: a clean-context subagent (not forked, no inherited context) re-runs the same audit skill against the finished artifact, as a distinct verification layer from the author skill's inline audit — the two can share blind spots since the inline audit runs in the same context as the work it checks. If the clean audit surfaces any unresolved finding, `forge` loops — re-invoke the author skill to resolve it, re-run the clean audit — until the clean audit comes back with nothing unresolved; only then is the route done. `plugin-author` and `marketplace-author` have no audit counterpart and get no recheck; their terminal check is `claude plugin validate`. A skill calling another skill by name to delegate a sub-task. The calling skill focuses on the orchestration decision ("when to do X"); the called skill owns the mechanics ("how to do X"). Established compositions: `grill-me` calls `write-adr` when a decision crystallises; `implement-feature` calls `tdd` as its implementation methodology; `forge` calls `grill-with-docs` to refine intent, classifies the target artifact type (skill / agent / plugin / marketplace entry), then routes to the matching `*-author` skill — which owns its own create/improve logic and, where applicable, its own inline audit closeout (`skill-author` runs `/skill-audit`, `agent-author` runs `kyberforge:agent-audit`, both in the same context as the authoring work). Reserve `forge` for genuinely undecided "which artifact type is this" questions — an already-fully-specified corrective edit (exact file, line, and fix already known) should call the target author skill directly instead (`skill-author`, `apm-workflow`, `agentsmd-author`, etc.); routing a known fix through `forge`'s grill-and-classify layer adds unnecessary indirection and, in practice, has been observed to lose track of hard constraints handed down the chain (e.g. "don't commit yet," "edit in this worktree") because each hop re-derives instructions from a shorter brief. `forge` additionally runs its own independent recheck after a skill/agent route finishes: a clean-context subagent (not forked, no inherited context) re-runs the same audit skill against the finished artifact, as a distinct verification layer from the author skill's inline audit — the two can share blind spots since the inline audit runs in the same context as the work it checks. If the clean audit surfaces any unresolved finding, `forge` loops — re-invoke the author skill to resolve it, re-run the clean audit — until the clean audit comes back with nothing unresolved; only then is the route done. `plugin-author` and `marketplace-author` had no audit counterpart and got no recheck; their terminal check was `claude plugin validate`. Both were deprecated per ADR-0015, superseded by `apm-workflow`, and deleted entirely once issue #90 landed.
### Provider-agnostic issue tracker ### Provider-agnostic issue tracker
Skills and workflows reference "linked issue" generically rather than a specific provider. Gitea is the canonical issue tracker for this repo (see ADR-0017). "Issue" is the cross-provider term (GitHub, GitLab, Gitea all use it). Skills and workflows reference "linked issue" generically rather than a specific provider. Gitea is the canonical issue tracker for this repo (see ADR-0007). "Issue" is the cross-provider term (GitHub, GitLab, Gitea all use it).
### Provenance chain ### Provenance chain
The three-stage traceability record linking a skill back to its research inputs: (1) `/research` produces topic docs and a `sources.md` in `plugins/<plugin>/docs/research/docs/<topic>/`; (2) `/skill-author` reads those docs and records which sources informed which skill files in `references/sources.md` (including a `Research doc:` back-pointer to the upstream research file) and `source_keys` frontmatter on `SKILL.md` and `references/*.md`; (3) `skill-audit` validates the chain is complete and internally consistent via `validate-provenance.sh`. A skill with research input but no `references/sources.md`, or with `source_keys` that don't match `references/sources.md` slugs, has a broken provenance chain. The three-stage traceability record linking a skill back to its research inputs: (1) `/research` produces topic docs and a `sources.md` in `plugins/<plugin>/docs/research/docs/<topic>/`; (2) `/skill-author` reads those docs and records which sources informed which skill files in `references/sources.md` (including a `Research doc:` back-pointer to the upstream research file) and `source_keys` frontmatter on `SKILL.md` and `references/*.md`; (3) `skill-audit` validates the chain is complete and internally consistent via `validate-provenance.sh`. A skill with research input but no `references/sources.md`, or with `source_keys` that don't match `references/sources.md` slugs, has a broken provenance chain.
@@ -70,11 +70,11 @@ A companion skill (`core` plugin) that detects a target repo's provider-specific
A standalone, repo-agnostic plugin (`plugins/lint/`) for configuring and running linters — not scoped to kyberforge's own meta-tooling. First linter is Vale (prose style linting), split into two skills per the git/gitea per-concern pattern: `vale-config` (setup — `.vale.ini`, `StylesPath`, styles) and `vale-run` (invoke Vale, interpret/report findings). A `lint-runner` agent composes these for isolated-context lint sweeps; it is report-only (no `Edit` tool) — it flags findings, it does not rewrite prose. Vale's research docs (`docs/research/docs/vale/`) moved from `plugins/kyberforge/` to `plugins/lint/` to keep the provenance chain same-plugin. A standalone, repo-agnostic plugin (`plugins/lint/`) for configuring and running linters — not scoped to kyberforge's own meta-tooling. First linter is Vale (prose style linting), split into two skills per the git/gitea per-concern pattern: `vale-config` (setup — `.vale.ini`, `StylesPath`, styles) and `vale-run` (invoke Vale, interpret/report findings). A `lint-runner` agent composes these for isolated-context lint sweeps; it is report-only (no `Edit` tool) — it flags findings, it does not rewrite prose. Vale's research docs (`docs/research/docs/vale/`) moved from `plugins/kyberforge/` to `plugins/lint/` to keep the provenance chain same-plugin.
### Vale audit prefilter (skill-audit / agent-audit) ### Vale audit prefilter (skill-audit / agent-audit)
Wiring Vale as a deterministic prefilter for `skill-audit`/`agent-audit`'s Description dimension (ADR motivation: issue #84) is repo-specific, not part of the generic `lint` plugin, so it doesn't live in `plugins/lint/` — but per ADR-0014 it also doesn't live at the repo root anymore. Two copies live inside `plugins/kyberforge/`, one per skill, since a plugin's cache-install only copies each skill's own files (no cross-skill sharing): `plugins/kyberforge/skills/agent-audit/assets/vale/` is canonical (`.vale.ini` plus a custom `Kyberforge` style covering description-opener banning ("This skill/agent..."), vague-capability wording ("helps with", "utilize", ...), and generic "see references/ for details" padding — and a `KyberforgeCopilot` style scoped only to `.agent.md` files for the Copilot-only "Use proactively has no effect" check), and `plugins/kyberforge/skills/skill-audit/assets/vale/` is a smaller duplicate (`Kyberforge` only, scoped to `SKILL.md`) kept in sync by `scripts/check-vale-style-sync.sh` (pre-push). A root-level `.pre-commit-hooks.yaml` exposes both copies (plus `skill-size-check`) so any external repo can enforce the same rules via `repo: <this-repo-url>, rev: <tag>` in its own `.pre-commit-config.yaml` — pre-commit clones the pinned rev into its own cache, independent of whether Claude Code or the `kyberforge` plugin is installed at all, and the same mechanism covers CI (`pre-commit run --all-files`). This repo's own `vale-audit-prefilter-skill`/`-agent` pre-commit hooks consume the identical plugin-bundled copies via `repo: local` (not a third root copy, and not a pinned self-reference — a pinned self-reference would lint working-tree edits against the last tagged release rather than the change being made). Every rule is `level: error` and every alert is a FAIL — no ignorable tier, same as shellcheck, the test suite, and conventional-pre-commit. Graded severities do not work here: Vale's exit code keys on `error` alerts alone, so `warning`/`suggestion` rules exit 0 and pre-commit swallows the output of a passing hook, leaving them invisible and blocking nothing. `MinAlertLevel` and `--minAlertLevel` are correspondingly absent from `.vale.ini` and the hook, being no-ops under this model. Vale covers the pattern-matchable sub-checks named in issue #84 (imperative opener, vague filler, `Use proactively`, generic reference-pointer padding) plus, per ADR-0013, one body-wide prose-pattern check ("There is/are" sentence openers) — everything else about body discipline (defaults-vs-menus, why-rationale, non-pattern-matchable judgment calls), near-miss exclusion strength, and control calibration stays LLM judgment. Wiring Vale as a deterministic prefilter for `skill-audit`/`agent-audit`'s Description dimension (ADR motivation: issue #84) is repo-specific, not part of the generic `lint` plugin, so it doesn't live in `plugins/lint/` — but per ADR-0014 it also doesn't live at the repo root anymore. Two copies live inside `plugins/kyberforge/`, one per skill, since a plugin's cache-install only copies each skill's own files (no cross-skill sharing): `plugins/kyberforge/.apm/skills/agent-audit/assets/vale/` is canonical (`.vale.ini` plus a custom `Kyberforge` style covering description-opener banning ("This skill/agent..."), vague-capability wording ("helps with", "utilize", ...), and generic "see references/ for details" padding — and a `KyberforgeCopilot` style scoped only to `.agent.md` files for the Copilot-only "Use proactively has no effect" check), and `plugins/kyberforge/.apm/skills/skill-audit/assets/vale/` is a smaller duplicate (`Kyberforge` only, scoped to `SKILL.md`) kept in sync by `scripts/check-vale-style-sync.sh` (pre-push). A root-level `.pre-commit-hooks.yaml` exposes both copies (plus `skill-size-check`) so any external repo can enforce the same rules via `repo: <this-repo-url>, rev: <tag>` in its own `.pre-commit-config.yaml` — pre-commit clones the pinned rev into its own cache, independent of whether Claude Code or the `kyberforge` plugin is installed at all, and the same mechanism covers CI (`pre-commit run --all-files`). This repo's own `vale-audit-prefilter-skill`/`-agent` pre-commit hooks consume the identical plugin-bundled copies via `repo: local` (not a third root copy, and not a pinned self-reference — a pinned self-reference would lint working-tree edits against the last tagged release rather than the change being made). Every rule is `level: error` and every alert is a FAIL — no ignorable tier, same as shellcheck, the test suite, and conventional-pre-commit. Graded severities do not work here: Vale's exit code keys on `error` alerts alone, so `warning`/`suggestion` rules exit 0 and pre-commit swallows the output of a passing hook, leaving them invisible and blocking nothing. `MinAlertLevel` and `--minAlertLevel` are correspondingly absent from `.vale.ini` and the hook, being no-ops under this model. Vale covers the pattern-matchable sub-checks named in issue #84 (imperative opener, vague filler, `Use proactively`, generic reference-pointer padding) plus, per ADR-0013, one body-wide prose-pattern check ("There is/are" sentence openers) — everything else about body discipline (defaults-vs-menus, why-rationale, non-pattern-matchable judgment calls), near-miss exclusion strength, and control calibration stays LLM judgment.
Both skills' Step 1, and the `vale-audit-prefilter-skill`/`-agent` pre-commit hooks, call each copy's own `scripts/vale-wrap.sh` rather than `vale` directly — a workaround for a confirmed Vale 3.15.2 limitation (see `vale-config`'s Gotchas): `text.frontmatter.description` silently stops matching on most — not all — multi-line descriptions. Verified by reproduction, not assumed: `>` folded scalars, plain (unquoted) continuation lines, and single- or double-quoted multi-line scalars all yield 0 alerts and exit 0 on a deliberately-bad fixture, while a `|` literal block spanning the same 2+ lines lints normally (alerts fire, exit 1). The wrapper flattens those three broken forms to one physical line in a scratch copy (padding with blank lines so every other line number is unchanged) before handing off to real `vale`; `|` literal blocks and single-line descriptions pass through untouched, already linting correctly. The plain and quoted forms previously passed silently — unflattened and unmatched — so a bad description in either sailed through the prefilter. Handed no `--config` at all, the wrapper falls back to its own sibling `assets/vale/.vale.ini`, located from `${BASH_SOURCE[0]}` rather than from the cwd — which is why both manifests' `entry:` is now the bare script path with no argument after it. pre-commit prefixes only `entry[0]` with the hook-repo clone path (`cmd = (prefix.path(cmd[0]), *cmd[1:])`), so every later argument resolves against the *consuming* repo's root: a `--config` in `.pre-commit-hooks.yaml` pointed at a path no consumer has and hard-failed every external run with `E100 [--config] Runtime error`. `.pre-commit-config.yaml` drops the argument too, deliberately keeping the two entries identical — the local `repo: local` hook resolved its `--config` correctly only because the consuming repo *was* this repo, and that divergence is why three review rounds exercised a path no external consumer takes and missed the defect. An explicit `--config` still wins, in all three argv forms (`--config X`, `--config=/abs`, `--config=rel`), and a relative one still resolves against the caller's cwd, matching bare `vale`, not the repo root. Both audit skills' Step 1 now passes no `--config` either: it resolves the script relative to the skill's own directory so the call works from an installed plugin cache, but a relative `--config` alongside it would still resolve against the cwd, yielding `E100 Runtime error ... does not exist` and exit 2 — which both skills' fallback misreads as "vale unavailable" and silently downgrades to full LLM judgment. `tests/test-vale-wrap.sh` regression-tests this against skill-audit's copy specifically (its fixtures are all `SKILL.md`-shaped, and only skill-audit's `.vale.ini` has that glob section). Each `.vale.ini`'s section globs are path-agnostic (`[**/SKILL.md]` for skill-audit's copy; `[**/agents/*.md]`/`[**/*.agent.md]` for agent-audit's) and do no scoping on their own: Vale's `*` crosses `/`. Scoping comes from each pre-commit hook's own `files:` regex and from the audit skills passing one explicit file per invocation. The two manifests scope differently on purpose: this repo's `.pre-commit-config.yaml` pins its own layout — `^plugins/[^/]+/skills/[^/]+/SKILL\.md$` for `-skill`, `^plugins/[^/]+/agents/[^/]+\.md$` for `-agent` — while the shipped `.pre-commit-hooks.yaml` stays layout-agnostic for external consumers whose skills live anywhere, using `(^|/)SKILL\.md$` and `(^|/)agents/[^/]+\.md$|\.agent\.md$`. Both manifests split the prefilter into two hooks precisely because one combined hook pointed at only one copy would silently 0-file-skip the other file type. A `SKILL.md` outside `plugins/` (e.g. project-scope `.claude/skills/foo/SKILL.md`) still matches `[**/SKILL.md]` and gets linted normally — the globs constrain filename shape, not location. Vale reports 0 files only when the path it is handed matches no glob section at all: a differently-named file, or a directory argument holding nothing that matches. That run prints `✔ 0 errors ... in 0 files.` and exits 0, indistinguishable from a clean pass, so both audits treat a 0-file Vale run as NOT RUN and fall back to full LLM judgment. Both skills' Step 1, and the `vale-audit-prefilter-skill`/`-agent` pre-commit hooks, call each copy's own `scripts/vale-wrap.sh` rather than `vale` directly — a workaround for a confirmed Vale 3.15.2 limitation (see `vale-config`'s Gotchas): `text.frontmatter.description` silently stops matching on most — not all — multi-line descriptions. Verified by reproduction, not assumed: `>` folded scalars, plain (unquoted) continuation lines, and single- or double-quoted multi-line scalars all yield 0 alerts and exit 0 on a deliberately-bad fixture, while a `|` literal block spanning the same 2+ lines lints normally (alerts fire, exit 1). The wrapper flattens those three broken forms to one physical line in a scratch copy (padding with blank lines so every other line number is unchanged) before handing off to real `vale`; `|` literal blocks and single-line descriptions pass through untouched, already linting correctly. The plain and quoted forms previously passed silently — unflattened and unmatched — so a bad description in either sailed through the prefilter. Handed no `--config` at all, the wrapper falls back to its own sibling `assets/vale/.vale.ini`, located from `${BASH_SOURCE[0]}` rather than from the cwd — which is why both manifests' `entry:` is now the bare script path with no argument after it. pre-commit prefixes only `entry[0]` with the hook-repo clone path (`cmd = (prefix.path(cmd[0]), *cmd[1:])`), so every later argument resolves against the *consuming* repo's root: a `--config` in `.pre-commit-hooks.yaml` pointed at a path no consumer has and hard-failed every external run with `E100 [--config] Runtime error`. `.pre-commit-config.yaml` drops the argument too, deliberately keeping the two entries identical — the local `repo: local` hook resolved its `--config` correctly only because the consuming repo *was* this repo, and that divergence is why three review rounds exercised a path no external consumer takes and missed the defect. An explicit `--config` still wins, in all three argv forms (`--config X`, `--config=/abs`, `--config=rel`), and a relative one still resolves against the caller's cwd, matching bare `vale`, not the repo root. Both audit skills' Step 1 now passes no `--config` either: it resolves the script relative to the skill's own directory so the call works from an installed plugin cache, but a relative `--config` alongside it would still resolve against the cwd, yielding `E100 Runtime error ... does not exist` and exit 2 — which both skills' fallback misreads as "vale unavailable" and silently downgrades to full LLM judgment. `tests/test-vale-wrap.sh` regression-tests this against skill-audit's copy specifically (its fixtures are all `SKILL.md`-shaped, and only skill-audit's `.vale.ini` has that glob section). Each `.vale.ini`'s section globs are path-agnostic (`[**/SKILL.md]` for skill-audit's copy; `[**/agents/*.md]`/`[**/*.agent.md]` for agent-audit's) and do no scoping on their own: Vale's `*` crosses `/`. Scoping comes from each pre-commit hook's own `files:` regex and from the audit skills passing one explicit file per invocation. The two manifests scope differently on purpose: this repo's `.pre-commit-config.yaml` pins its own layout — `^plugins/[^/]+/\.apm/skills/[^/]+/SKILL\.md$` for `-skill`, `^plugins/[^/]+/\.apm/agents/[^/]+\.agent\.md$` for `-agent` — while the shipped `.pre-commit-hooks.yaml` stays layout-agnostic for external consumers whose skills live anywhere, using `(^|/)SKILL\.md$` and `(^|/)agents/[^/]+\.md$|\.agent\.md$`. Both manifests split the prefilter into two hooks precisely because one combined hook pointed at only one copy would silently 0-file-skip the other file type. A `SKILL.md` outside `plugins/` (e.g. project-scope `.claude/skills/foo/SKILL.md`) still matches `[**/SKILL.md]` and gets linted normally — the globs constrain filename shape, not location. Vale reports 0 files only when the path it is handed matches no glob section at all: a differently-named file, or a directory argument holding nothing that matches. That run prints `✔ 0 errors ... in 0 files.` and exits 0, indistinguishable from a clean pass, so both audits treat a 0-file Vale run as NOT RUN and fall back to full LLM judgment.
This scope expands per ADR-0013: one cherry-picked low-noise `write-good`/`alex` rule landed in `styles/Kyberforge`, `Kyberforge.SentenceOpenerThereIs` (22 held-out hits, both in-corpus hits clean rewrites, zero suppressions). A second, `Kyberforge.VagueQualifier`, was cherry-picked and then deleted: 2 hits across the 41 skill/agent files, one marginal and one an unfixable false positive (`caveman/SKILL.md` quotes `of course` as an example of filler — a mention, not a use) that forced the repo's only Vale suppression comments. Also new is a sibling pre-commit hook, `skill-size-check` (`scripts/skill-size-check.sh`), enforcing agentskills.io's `SKILL.md` ceiling as two blocking gates: `MAX_LINES=500` and `MAX_WORDS=2770` (a word-count proxy for the 5,000-token limit, calibrated to the densest prose measured in this repo — 1.81 tokens per word — so even a worst-case `SKILL.md` at the ceiling stays under 5,000 tokens). Both are inclusive, and `skill-audit/scripts/validate.sh` checks the same pair on the same terms, so a `SKILL.md` can no longer pass its own audit yet be blocked by the commit hook. Scoped to `^plugins/[^/]+/skills/[^/]+/SKILL\.md$` only, same as `vale-audit-prefilter-skill`, so it never lints `docs/research/examples/` reference skills. It's also exposed in the root-level `.pre-commit-hooks.yaml` as `kyberforge-skill-size-check` — it has no external asset dependency, so it needed no relocation, only exposure to external consumers. File scope (`SKILL.md` + agent files) and enforcement model (rules land directly in `styles/Kyberforge`, blocking immediately, no trial tier) stay unchanged; governance.md/CONTROLS.md were evaluated and excluded as rule sources (nothing prose-pattern-matchable to mine). House convention: banned phrasing that must be mentioned rather than used goes in backticks or a fenced code block — Vale skips code spans and fences, so no suppression is needed; inline `<!-- vale Rule = NO -->` (HTML-comment form; the MDX `{/* */}` form does not work in plain Markdown) is the fallback only where backticking is impossible. This scope expands per ADR-0013: one cherry-picked low-noise `write-good`/`alex` rule landed in `styles/Kyberforge`, `Kyberforge.SentenceOpenerThereIs` (22 held-out hits, both in-corpus hits clean rewrites, zero suppressions). A second, `Kyberforge.VagueQualifier`, was cherry-picked and then deleted: 2 hits across the 41 skill/agent files, one marginal and one an unfixable false positive (`caveman/SKILL.md` quotes `of course` as an example of filler — a mention, not a use) that forced the repo's only Vale suppression comments. Also new is a sibling pre-commit hook, `skill-size-check` (`scripts/skill-size-check.sh`), enforcing agentskills.io's `SKILL.md` ceiling as two blocking gates: `MAX_LINES=500` and `MAX_WORDS=2770` (a word-count proxy for the 5,000-token limit, calibrated to the densest prose measured in this repo — 1.81 tokens per word — so even a worst-case `SKILL.md` at the ceiling stays under 5,000 tokens). Both are inclusive, and `skill-audit/scripts/validate.sh` checks the same pair on the same terms, so a `SKILL.md` can no longer pass its own audit yet be blocked by the commit hook. Scoped to `^plugins/[^/]+/\.apm/skills/[^/]+/SKILL\.md$` only, same as `vale-audit-prefilter-skill`, so it never lints `docs/research/examples/` reference skills. It's also exposed in the root-level `.pre-commit-hooks.yaml` as `kyberforge-skill-size-check` — it has no external asset dependency, so it needed no relocation, only exposure to external consumers. File scope (`SKILL.md` + agent files) and enforcement model (rules land directly in `styles/Kyberforge`, blocking immediately, no trial tier) stay unchanged; governance.md/CONTROLS.md were evaluated and excluded as rule sources (nothing prose-pattern-matchable to mine). House convention: banned phrasing that must be mentioned rather than used goes in backticks or a fenced code block — Vale skips code spans and fences, so no suppression is needed; inline `<!-- vale Rule = NO -->` (HTML-comment form; the MDX `{/* */}` form does not work in plain Markdown) is the fallback only where backticking is impossible.
### LESSONS.md ### LESSONS.md
Long-loop feedback log for patterns observed across sessions. Three or more entries on the same pattern graduate to the relevant standing file (e.g. a coding convention, a governance rule). Updated by the session-handoff skill or directly by the human. Lives at the repo root. Long-loop feedback log for patterns observed across sessions. Three or more entries on the same pattern graduate to the relevant standing file (e.g. a coding convention, a governance rule). Updated by the session-handoff skill or directly by the human. Lives at the repo root.

View File

@@ -124,7 +124,7 @@ Two forks independently fixed `references/sources.md` with different approaches
## 2026-06-28 — Implementation agents must invoke /skill-author, not write skill files directly ## 2026-06-28 — Implementation agents must invoke /skill-author, not write skill files directly
When briefing an agent to implement a new skill, the instinct is to tell it to write the SKILL.md and supporting files directly. This bypasses Step 5 of the skill-author process (provenance), which requires reading all research `sources.md` files and recording every `extracted` slug in META.md. The `validate-provenance.sh` script catches the gap — but only after the commit, requiring a fix round. This pattern recurred twice in one session (plugin-author and marketplace-author initial implementation, then again in the first round of fix agents). Fix: briefs for implementation agents must explicitly say "invoke `/skill-author` (read and follow `plugins/kyberforge/skills/skill-author/SKILL.md`)" — not "write the skill files." Invoking the skill is the only reliable way to ensure all process gates, including provenance, run. When briefing an agent to implement a new skill, the instinct is to tell it to write the SKILL.md and supporting files directly. This bypasses Step 5 of the skill-author process (provenance), which requires reading all research `sources.md` files and recording every `extracted` slug in META.md. The `validate-provenance.sh` script catches the gap — but only after the commit, requiring a fix round. This pattern recurred twice in one session (plugin-author and marketplace-author initial implementation, then again in the first round of fix agents). Fix: briefs for implementation agents must explicitly say "invoke `/skill-author` (read and follow `plugins/kyberforge/.apm/skills/skill-author/SKILL.md`)" — not "write the skill files." Invoking the skill is the only reliable way to ensure all process gates, including provenance, run.
## 2026-07-05 — Repo root is a bare checkout; work happens in worktrees only ## 2026-07-05 — Repo root is a bare checkout; work happens in worktrees only

74
apm.yml Normal file
View File

@@ -0,0 +1,74 @@
name: holocron
version: 0.3.2
description: AI development skills for Claude Code and GitHub Copilot CLI — factory, design, implement, review, and cross-cutting workflows.
license: MIT
marketplace:
# apm's Claude marketplace mapper only emits description:/version: into the
# compiled marketplace.json when set explicitly here (an override) — the
# top-level apm.yml description:/version: above are NOT inherited into the
# compiled output despite being used elsewhere (e.g. by `apm audit`).
description: AI development skills for Claude Code and GitHub Copilot CLI — factory, design, implement, review, and cross-cutting workflows.
version: 0.3.2
owner:
name: Defame1297
url: https://git.dev.rkdr.net/Defame1297/
# Default tag pattern used to resolve version ranges for each package.
build:
tagPattern: "v{version}"
# Output targets (map form). Each output writes to its profile default
# path; add 'path:' under a key to override.
# 'codex' requires every package below to declare 'category:' (satisfied).
outputs:
claude: {}
codex: {}
# CI tip: build one or all formats with a machine-readable manifest:
# apm pack --marketplace=claude,codex --json | jq -r '.marketplace.outputs[].path'
versioning:
strategy: per_package
packages:
- name: kyberforge
description: Skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace.
source: ./plugins/kyberforge
version: 1.4.0
category: Developer Tools
- name: bin
description: A place for things to be binned
source: ./plugins/bin
version: 1.1.1
category: Utilities
- name: git
description: Skills for working with Git — conventional commits, branch management, pull requests, and feature flow.
source: ./plugins/git
version: 1.3.2
category: Version Control
- name: gitea
description: Skills for managing Gitea repositories — issues, pull requests, milestones, releases, and wikis.
source: ./plugins/gitea
version: 1.3.3
category: Version Control
- name: core
description: Cross-cutting utility skills for everyday AI-assisted coding — triage, diagnosis, architecture review, and session navigation.
source: ./plugins/core
version: 1.1.0
category: Productivity
- name: mattpocock-skills
description: Skills for Real Engineers — planning, TDD, architecture, and debugging workflows from Matt Pocock's .claude directory.
source: mattpocock/skills
version: "^1.2.0"
category: Productivity
- name: lint
description: Skills and agents for configuring and running linters.
source: ./plugins/lint
version: 1.1.5
category: Developer Tools

View File

@@ -1,5 +1,16 @@
# Skills are distributed via plugins, not monolithic repo deployment # Skills are distributed via plugins, not monolithic repo deployment
**Superseded by:** ADR-0015 (Microsoft APM replaces the hand-authored plugin/marketplace model
as this repo's authoring source of truth) and, for plugin-scope agent files specifically,
ADR-0016 (plugin-scope `.apm/agents/*.agent.md` drops provider-specific fields). Since issue
#90's conversion executed, plugin content is authored under `plugins/<name>/apm.yml` +
`.apm/{skills,agents,hooks}/` — not the flat `skills/`/`agents/` layout this ADR describes —
and `.claude-plugin/plugin.json`/`.github/plugin/plugin.json` are compiled output of `apm pack`,
not hand-authored. This ADR's content is kept below as the historical record of the
pre-APM decision; it is no longer the current model.
---
Skills (slash commands) are authored and distributed as part of **plugins** — each plugin contains its own `skills/` directory alongside agents and other artifacts. Plugins are installed via `claude plugin install <name>@holocron` rather than deployed from the repo's local tree. This decision decouples skill authoring cadence from core provider deployments and allows independent versioning per plugin. Skills (slash commands) are authored and distributed as part of **plugins** — each plugin contains its own `skills/` directory alongside agents and other artifacts. Plugins are installed via `claude plugin install <name>@holocron` rather than deployed from the repo's local tree. This decision decouples skill authoring cadence from core provider deployments and allows independent versioning per plugin.
## Context ## Context

View File

@@ -44,3 +44,8 @@ separate single-provider skill, adding complexity with no benefit.
file now lives at `<plugin-root>/sources.md`, outside the `agents/` directory, because file now lives at `<plugin-root>/sources.md`, outside the `agents/` directory, because
`claude plugin validate --strict` auto-discovers every `.md` under `agents/` as an agent `claude plugin validate --strict` auto-discovers every `.md` under `agents/` as an agent
requiring frontmatter. See ADR-0010 for the empirical finding and rationale. requiring frontmatter. See ADR-0010 for the empirical finding and rationale.
**Update (ADR-0016):** the plugin-scope clause above is superseded. Plugin scope is no longer
detected via `plugin.json`, and no longer produces a Claude+Copilot file pair — a directory
containing `apm.yml` now gets a single vendor-neutral `.apm/agents/<name>.agent.md` file with
no provider-specific fields. Project scope and user scope are unaffected. See ADR-0016.

View File

@@ -1,5 +1,18 @@
# version field is present in both plugin manifests # version field is present in both plugin manifests
**Moot as of ADR-0015.** This ADR addressed drift risk between two independently
*hand-maintained* manifests. Since issue #90's conversion executed, `.claude-plugin/plugin.json`
and `.github/plugin/plugin.json` are both **compiled output** of `apm pack`, generated in the
same pass from a single `apm.yml` per plugin — there is no longer a second hand-authored file
that could drift out of parity. The invariant this ADR required (`version` present and
identical in both manifests) still holds in the compiled output, but structurally, not because
a skill enforces it: both files are derived from the same `apm.yml` `version:` field, so
divergence is no longer possible by construction. `plugin-author`, the skill that enforced this
invariant, is deleted per ADR-0015 rather than adapted. Kept below as the historical record of
the pre-APM decision.
---
Each plugin has two manifests: `plugin.json` (Copilot CLI) and `.claude-plugin/plugin.json` (Claude Code). Both tools support a `version` field. Prior to this decision, only the CC manifest carried `version`; the Copilot manifest omitted it. Each plugin has two manifests: `plugin.json` (Copilot CLI) and `.claude-plugin/plugin.json` (Claude Code). Both tools support a `version` field. Prior to this decision, only the CC manifest carried `version`; the Copilot manifest omitted it.
We now require `version` in both manifests, always identical. A reader of `plugin.json` alone should be able to determine the plugin version without consulting the CC manifest. The `plugin-author` skill enforces this invariant on every create, update, and release operation. We now require `version` in both manifests, always identical. A reader of `plugin.json` alone should be able to determine the plugin version without consulting the CC manifest. The `plugin-author` skill enforces this invariant on every create, update, and release operation.

View File

@@ -14,3 +14,9 @@
- Scope detection walks up from the input file: first directory containing `plugin.json` → plugin scope; first directory containing `.git` without `plugin.json` → project scope; path under `~` with neither → user scope. - Scope detection walks up from the input file: first directory containing `plugin.json` → plugin scope; first directory containing `.git` without `plugin.json` → project scope; path under `~` with neither → user scope.
- At user scope the derivation crosses filesystem locations (`~/.claude/agents/` ↔ `~/.copilot/agents/`); the script must handle the home directory case explicitly. - At user scope the derivation crosses filesystem locations (`~/.claude/agents/` ↔ `~/.copilot/agents/`); the script must handle the home directory case explicitly.
- The invocation signature is the public contract. Changing it is a breaking change to any caller — treat it as such. - The invocation signature is the public contract. Changing it is a breaking change to any caller — treat it as such.
**Update (ADR-0016):** the plugin-scope clause above is superseded. Plugin scope is no longer
detected via `plugin.json`, and there is no counterpart to derive — a directory containing
`apm.yml` produces a single `.apm/agents/<name>.agent.md` file, and `agent-audit` validates it
directly with no pair-consistency check. Project scope and user scope keep the pair-derivation
mechanism described above unchanged. See ADR-0016.

View File

@@ -5,6 +5,21 @@ claim that "both files share a single `agents/sources.md` for provenance." The r
ADR-0005 (dual-provider generation, scope detection, single-root script interface) is ADR-0005 (dual-provider generation, scope detection, single-root script interface) is
unaffected and remains in force. unaffected and remains in force.
**Path update per ADR-0016:** at plugin scope, agent files no longer live at
`<plugin-root>/agents/<name>.md`. The authoring source is now
`<plugin-root>/.apm/agents/<name>.agent.md` — a single vendor-neutral file (no dual Claude/
Copilot pair) compiled to both targets via `apm pack`. See ADR-0016 for why (the field-dropping
rationale, `tools:` incompatibility, the compiled-output mechanics) — not restated here. This
ADR's own conclusion is unaffected by that move: the provenance file still belongs at
`<plugin-root>/sources.md`, outside any directory `claude plugin validate --strict`
auto-scans, and `.apm/agents/` is, if anything, further removed from plugin-root than the old
flat `agents/` directory was, so the reasoning below still holds. References below to
`<plugin-root>/agents/` describe the pre-APM layout in effect when this decision was made.
**Scope boundary (per ADR-0016):** this path change is plugin scope only. Project scope
(`.claude/agents/` + `.github/agents/`) and user scope (`~/.claude/agents/` +
`~/.copilot/agents/`) are unaffected — they are not APM packages and keep the dual-file
Claude+Copilot pair model this ADR originally described.
`claude plugin validate --strict` auto-discovers every `.md` file directly under a plugin's `claude plugin validate --strict` auto-discovers every `.md` file directly under a plugin's
`agents/` directory and treats it as an agent definition requiring YAML frontmatter (`name`, `agents/` directory and treats it as an agent definition requiring YAML frontmatter (`name`,
`description`, etc.). A flat provenance file at `agents/sources.md` — no frontmatter, by `description`, etc.). A flat provenance file at `agents/sources.md` — no frontmatter, by

View File

@@ -18,9 +18,9 @@ full LLM judgment every time outside this repo — the exact gap ADR-0013 named
the plugin itself, following the no-cross-skill-path rule already established in the plugin itself, following the no-cross-skill-path rule already established in
`skill-author/references/deployment-modes.md` (a plugin's cache-install only copies each skill's `skill-author/references/deployment-modes.md` (a plugin's cache-install only copies each skill's
own files; there is no plugin-level shared directory). `agent-audit` needs both `Kyberforge` and own files; there is no plugin-level shared directory). `agent-audit` needs both `Kyberforge` and
`KyberforgeCopilot` (it lints `.agent.md` files), so `plugins/kyberforge/skills/agent-audit/assets/vale/` `KyberforgeCopilot` (it lints `.agent.md` files), so `plugins/kyberforge/.apm/skills/agent-audit/assets/vale/`
is the canonical, superset copy. `skill-audit` needs a second, smaller copy is the canonical, superset copy. `skill-audit` needs a second, smaller copy
(`plugins/kyberforge/skills/skill-audit/assets/vale/`, `Kyberforge` only) since it cannot (`plugins/kyberforge/.apm/skills/skill-audit/assets/vale/`, `Kyberforge` only) since it cannot
reference agent-audit's copy across the skill boundary. Both skills' Step 1 now resolve reference agent-audit's copy across the skill boundary. Both skills' Step 1 now resolve
`scripts/vale-wrap.sh`/`assets/vale/.vale.ini` relative to their own directory, the same way `scripts/vale-wrap.sh`/`assets/vale/.vale.ini` relative to their own directory, the same way
`scripts/validate.sh <skill-dir>` already does — no new resolution mechanism, just applying the `scripts/validate.sh <skill-dir>` already does — no new resolution mechanism, just applying the
@@ -38,7 +38,7 @@ and gets all three, fully decoupled from Claude Code. CI is the identical `pre-c
**This repo's own dev-time gate** consumes the same plugin-bundled copies instead of a third **This repo's own dev-time gate** consumes the same plugin-bundled copies instead of a third
root-level copy — per explicit instruction, this repo should be set up like any other consumer root-level copy — per explicit instruction, this repo should be set up like any other consumer
would be, not dogfood a special root-only path. The existing `repo: local` hook is retargeted would be, not dogfood a special root-only path. The existing `repo: local` hook is retargeted
(not removed): `entry:` now points at `plugins/kyberforge/skills/{skill-audit,agent-audit}/scripts/vale-wrap.sh`. (not removed): `entry:` now points at `plugins/kyberforge/.apm/skills/{skill-audit,agent-audit}/scripts/vale-wrap.sh`.
`repo: local` is kept rather than switching to a pinned self-reference `repo: local` is kept rather than switching to a pinned self-reference
(`repo: <own-url>, rev: <tag>`) — a pinned self-reference would lint working-tree edits against (`repo: <own-url>, rev: <tag>`) — a pinned self-reference would lint working-tree edits against
the *last tagged release*, not the change actually being made, which is wrong for the repo that the *last tagged release*, not the change actually being made, which is wrong for the repo that
@@ -58,7 +58,7 @@ single hook at agent-audit's copy silently scanned 0 SKILL.md files.)
**The hook `entry:` is the wrapper alone; the wrapper self-locates its config.** pre-commit **The hook `entry:` is the wrapper alone; the wrapper self-locates its config.** pre-commit
prefixes only `entry[0]` with the hook-repo clone path (`cmd = (prefix.path(cmd[0]), *cmd[1:])`); prefixes only `entry[0]` with the hook-repo clone path (`cmd = (prefix.path(cmd[0]), *cmd[1:])`);
every later argument is handed to the process untouched and so resolves against the *consuming* every later argument is handed to the process untouched and so resolves against the *consuming*
repo's root. A `--config plugins/kyberforge/skills/…/assets/vale/.vale.ini` in repo's root. A `--config plugins/kyberforge/.apm/skills/…/assets/vale/.vale.ini` in
`.pre-commit-hooks.yaml` therefore named a path no consumer has, and every external run died with `.pre-commit-hooks.yaml` therefore named a path no consumer has, and every external run died with
`E100 [--config] Runtime error`. The external-consumer contract this ADR exists to establish `E100 [--config] Runtime error`. The external-consumer contract this ADR exists to establish
cannot be expressed as a `--config` argument at all — the config path has to be derived inside cannot be expressed as a `--config` argument at all — the config path has to be derived inside
@@ -105,8 +105,8 @@ doesn't wonder if it was overlooked.
## Consequences ## Consequences
- Root `.vale.ini`, `styles/`, `scripts/vale-wrap.sh` are deleted. Two copies remain: - Root `.vale.ini`, `styles/`, `scripts/vale-wrap.sh` are deleted. Two copies remain:
`plugins/kyberforge/skills/agent-audit/assets/vale/` (canonical, superset) and `plugins/kyberforge/.apm/skills/agent-audit/assets/vale/` (canonical, superset) and
`plugins/kyberforge/skills/skill-audit/assets/vale/` (subset, `Kyberforge` only). `plugins/kyberforge/.apm/skills/skill-audit/assets/vale/` (subset, `Kyberforge` only).
- `plugins/kyberforge`'s `plugin.json` and `.claude-plugin/plugin.json` both patch-bump for every - `plugins/kyberforge`'s `plugin.json` and `.claude-plugin/plugin.json` both patch-bump for every
shipped content change (per ADR-0006's version-parity invariant): `1.2.5` for the relocation shipped content change (per ADR-0006's version-parity invariant): `1.2.5` for the relocation
itself, `1.2.6` for the self-locating `vale-wrap.sh` that followed. itself, `1.2.6` for the self-locating `vale-wrap.sh` that followed.

View File

@@ -0,0 +1,132 @@
# Microsoft APM replaces the hand-authored plugin/marketplace model as this repo's authoring source of truth
**Status: executed (2026-08-12, issue #90).** All six plugins now carry `apm.yml` + `.apm/` as
their authoring source; `.claude-plugin/marketplace.json` and every plugin's `plugin.json` are
`apm pack`-compiled output. **Supersedes ADR-0001** ("Skills are distributed via plugins... each
plugin contains its own `skills/` directory") — in effect.
This repo replaces its hand-maintained Claude Code plugin/marketplace authoring model
(`.claude-plugin/marketplace.json` + per-plugin `plugin.json`) with Microsoft APM (`apm.yml` +
`.apm/`) as the authoring source of truth — an outright replacement of the authoring layer, not an
additive overlay. This ADR records the decision from a `grill-with-docs` session on issue #88.
## Context
Every plugin under `plugins/<name>/` currently ships two hand-maintained manifests
(`.claude-plugin/plugin.json` for Claude Code, root `plugin.json` for Copilot CLI) plus a
hand-maintained root `.claude-plugin/marketplace.json` listing all plugins. Adding a provider means
hand-authoring a third manifest shape; keeping the two existing ones in parity is itself a tracked
concern (ADR-0006).
Research on Microsoft APM (`plugins/kyberforge/docs/research/docs/microsoft-apm/`) found that its
documented "monorepo-hybrid" repo shape maps directly onto this repo's existing `plugins/<name>/`
layout: each plugin becomes its own `apm.yml` + `.apm/{skills,agents,hooks,prompts,instructions}/`
package, listed from a root `apm.yml`'s `marketplace:` block. `apm compile`/`apm pack` generate
per-target output — including a `.claude-plugin/marketplace.json` — from that vendor-neutral
`.apm/` tree, so provider manifests become compiled artifacts instead of hand-authored files, and
new providers (Copilot, Gemini, Codex — all supported by `apm runtime setup`) no longer require a
new hand-maintained manifest format.
## Decision
- **The `plugins/<name>/` monorepo-hybrid directory layout survives.** `.claude-plugin/marketplace.json`
and per-provider `plugin.json` files become **compiled output** via `apm compile`/`apm pack`,
generated from `apm.yml` + `.apm/` per plugin, extensible to other `apm runtime`-supported
providers without hand-maintaining a separate manifest per provider.
- **This supersedes ADR-0001** ("Skills are distributed via plugins... each plugin
contains its own `skills/` directory"). Executed in issue #90: skills and agents physically moved
to `plugins/<name>/.apm/skills/` and `plugins/<name>/.apm/agents/*.agent.md`.
- New operational tooling — `apm-install` (skill), `apm-workflow` (skill), `apm-orchestrate`
(agent) — lands in `kyberforge`, tracked in issue #88
(https://git.dev.rkdr.net/Defame1297/holocron/issues/88).
- Adapting `skill-author`/`agent-author`'s routing to author `.apm/`-native content (retargeting to
`.apm/skills/`, `.apm/agents/` paths — the content these two skills author is still meaningful
post-conversion) is deferred to issue #89
(https://git.dev.rkdr.net/Defame1297/holocron/issues/89). `forge` is out of scope for #89 — it
stays untouched by this whole conversion effort and keeps routing to whatever the live author
skills are at the time.
- **`plugin-author`/`marketplace-author` are not adapted — they are superseded and deleted.**
Unlike `skill-author`/`agent-author`, nothing in these two skills carries forward as authoring
routing: `apm compile`/`apm pack` will generate `.claude-plugin/marketplace.json` and
per-provider `plugin.json` directly from `apm.yml` + `.apm/`, so `apm-install`/`apm-workflow`/
`apm-orchestrate` (issue #88, already landed on this branch) fully replace what these two skills
did. `plugin-author`/`marketplace-author` were deleted in issue #90's execution.
- Translating the existing plugins into `apm.yml` + `.apm/` and running the real conversion was
executed in issue #90 (https://git.dev.rkdr.net/Defame1297/holocron/issues/90, closed).
- `CONTEXT.md`'s "Plugin"/"Plugin marketplace" glossary entries were rewritten in issue #90 to
describe the compiled-output model directly, rather than carrying a forward-pointer to this ADR.
## Considered options
**Additive/compile-layer only, no `apm.yml` (rejected).** Keep `plugin.json`/`marketplace.json`
hand-authored and bolt APM on top as an optional extra. Rejected: doesn't achieve the multi-provider
compile-reuse goal APM's package model provides, and leaves the existing dual-manifest hand
maintenance in place unchanged.
**New standalone `plugins/apm/` plugin (rejected).** `plugins/lint/` was split out of `kyberforge`
specifically because Vale tooling is generic and repo-agnostic, not holocron-marketplace-specific
(see `CONTEXT.md`'s "lint plugin" entry) — the same argument applies to a generic `apm` CLI
wrapper. The shipped `apm-install`/`apm-workflow` skills are, in fact, generic, repo-agnostic APM
CLI documentation with no holocron-specific content, so a standalone `plugins/apm/` would have
been a defensible split on artifact content alone. Rejected anyway, in favor of `kyberforge`,
because holocron is currently the only repo that needs this tooling — standing up a separate
plugin for a single consumer isn't worth it yet. Accepted as an explicit tradeoff (same pattern
as ADR-0011's `gitea-workflow` naming tradeoff) — worth revisiting if this tooling is ever reused
outside holocron's own conversion.
## Content migration out of `plugin-author`/`marketplace-author`
A content audit of `plugin-author`/`marketplace-author` (same `grill-with-docs` session as this
correction) sorted what they document into three buckets:
- **Claude Code platform constraints — carried forward.** Facts that stay true regardless of
authoring model (reserved plugin-name prefixes; the `agents/`-directory stray-`.md`-file
validator gotcha, ADR-0010; `claude plugin validate` as a required terminal check) have been
added into `apm-workflow`'s reference docs, since compiled output still has to satisfy these
constraints post-conversion.
- **Dual-manifest artifacts — obsolete, not carried forward.** Conventions that existed only
because of hand-authored dual manifests (ADR-0006's version-parity/patch-bump rule, the
CC-vs-Copilot field-placement split, dual-file mirroring) are obsolete under `apm.yml`'s
single-manifest model and were deliberately dropped.
- **Holocron policy choice — resolved in #90.** `marketplace-author`'s catalog-version convention
(minor bump for package add/remove, patch bump for field-only updates) isn't an APM mechanic —
`apm` doesn't enforce it, and has no native version-bump automation at all — so rather than
building a new script, the convention is now documented as guidance inside `apm-workflow`'s
reference docs (`references/marketplace.md` for the root catalog version rule,
`references/configure.md` for the per-package version-bump-on-content-edit rule), applied
manually by whoever edits `apm.yml`.
## Consequences
- ADR-0001 is superseded (issue #90).
- ADR-0006 (plugin-version-parity) is moot (issue #90): `plugin.json`/`marketplace.json` are now
compiled output of a single `apm.yml`, so there's no second hand-authored file left to keep in
parity, and `plugin-author` — the skill that enforced ADR-0006 — was deleted rather than adapted
(see "Content migration" above).
- ADR-0010 (agent sources relocated outside agents dir) was updated (issue #90) for agents now
living at `plugins/<name>/.apm/agents/*.agent.md` — the directory path changed; the pre-existing
`.agent.md` extension convention (ADR-0005/ADR-0010) and project/user scope are unaffected, per
ADR-0016.
- ADR-0014 (Vale prefilter ships from the plugin) had its hardcoded `plugins/<name>/skills/...`
paths (the Vale prefilter is skill-scoped only; ADR-0014 never referenced a
`plugins/<name>/agents/...` path) updated for the `.apm/` nesting as part of issue #90's
execution.
- `kyberforge` gained three new artifacts (issue #88) before any conversion of existing content
happened, then lost two (`plugin-author`/`marketplace-author`, deleted once issue #90 verified
parity) — net version bump 1.3.1 → 1.4.0. The root marketplace catalog bumped 0.3.1 → 0.3.2 to
match.
- ADR-0016 (a narrower decision discovered while designing issue #89) turned out to gate how
issue #90 had to re-author plugin-scope agents: `.apm/agents/*.agent.md` compiles verbatim to
both Claude and Copilot, so those files carry only `name`/`description`/`model`/`source_keys` —
existing dual-file `<name>.md`+`<name>.agent.md` pairs could not be raw-moved, only re-authored.
- Two follow-up issues tracked the remaining work, both done: #89 (`skill-author`/`agent-author`
routing adaptation, merged in #93) and #90 (the actual repo conversion, which also deleted
`plugin-author`/`marketplace-author`).
- **Caveat on "Status: executed" above:** issue #90's own execution comment flagged, before merge,
that Claude Code's ability to actually load content out of `.apm/` was unverified — that caveat
turned out to be a real defect, not a formality: the native installer has zero awareness of
`.apm/` and reported `Skills (0) Agents (0) Hooks (0)` on every plugin installed from this
marketplace. The manifest-compilation deliverable this ADR describes was genuinely complete;
runtime discoverability was not. Fixed in ADR-0017 (a second, compiled flat-directory content
mirror at each plugin root, generated by `scripts/sync-plugin-content.sh`) — see that ADR for
the root cause and the fix.

View File

@@ -0,0 +1,92 @@
# Plugin-scope agent-author omits `tools:` and all Claude-only fields from `.apm/agents/*.agent.md`
This ADR is a narrower, downstream consequence discovered while designing issue #89's
implementation under ADR-0015's broader direction (Microsoft APM replaces hand-authored
plugin/marketplace authoring). It does not restate ADR-0015's rationale — see that ADR for
the parent decision.
## Context
APM's agent primitive (`.apm/agents/<name>.agent.md`) has no per-target integrator in
`apm compile` — confirmed via APM's own Python source (`integration/targets.py` and related
files, cited in `plugins/kyberforge/docs/research/docs/microsoft-apm/agent-primitive-schema.md`).
Compilation does a naive verbatim copy of the whole frontmatter and body to both the Claude
Code and Copilot CLI targets. This is unlike:
- The **skill** primitive, which is also a straight copy (confirmed in the same research doc)
but has no field semantics to conflict — `SKILL.md`'s content is target-agnostic already.
- The **prompt**, **instructions**, and **hooks** primitives, which each get real per-target
reconstruction through a dedicated integrator (field allowlisting, key renaming, dropped-field
warnings).
Because the agent primitive ships the same frontmatter unchanged to both harnesses, two
concrete incompatibilities surface:
1. **`tools:`** — Claude Code expects a space-separated tool-name string; Copilot CLI expects a
list drawn from its own alias vocabulary (`execute`/`read`/`edit`/`search`/`agent`/`web`). A
value correct for one harness is wrong for the other.
2. **Claude-only knobs with no Copilot equivalent** — `isolation`, `maxTurns`, `effort`,
`memory`, `permissionMode`. Writing any of these means Copilot's copy carries frontmatter
keys it doesn't recognize at all. Whether Copilot's agent loader ignores unknown keys or
errors on them is unconfirmed by research.
## Decision
At **plugin scope only** (destination package has an `apm.yml` at its root — an APM producer
package compiled via `apm compile`), `.apm/agents/<name>.agent.md` carries only `name`,
`description`, `model`, and the prose body. No `tools:` field, no Claude-only fields, at all.
Absent `tools:` means inherit-all-tools on both harnesses — the one value that is never wrong
on either target, unlike a present, harness-specific value that is guaranteed wrong on at least
one of them.
`agent-audit`, at plugin scope, is intended to flag — as a **SUGGESTION**, not a FAIL, since
this is an upstream schema limitation rather than an authoring mistake — any agent whose
description or body implies a need for tool restriction or a Claude-only behavior the
frontmatter can no longer express. This would give visibility into the gap without pretending
the schema can do something it can't. **Not yet implemented**: `check_apm_agent_file()` in
`validate.sh` currently validates only the field allowlist, `name`, `description`, and
body-emptiness/length — it has no heuristic for this case. Tracked as follow-up work.
### Scope boundary
This decision applies to **plugin-scope `agent-author` only**. Project scope (`.claude/agents/`
+ `.github/agents/`) and user scope (`~/.claude/agents/` + `~/.copilot/agents/`) are not APM
packages — neither goes through `apm compile` — so both keep today's dual-file Claude+Copilot
pair model exactly as ADR-0005 and ADR-0008 already describe. Those two ADRs remain fully
authoritative for project and user scope; only their plugin-scope clauses are affected by this
ADR (see the update notes appended to each).
## Considered options
**Pick one harness's vocabulary and accept breakage on the other (rejected).** E.g. always
write Claude's space-separated `tools:` string. Rejected because it ships a value that is
silently wrong (or possibly a hard error) on Copilot, and which harness "wins" would be an
arbitrary, undocumented asymmetry.
**Same as above, but `agent-audit` flags the cross-harness breakage as a tracked finding
(rejected).** Rejected for the same core reason — it still ships a wrong value to a real
harness. Tracking the breakage doesn't prevent it, and the chosen decision already gets
equivalent visibility (a SUGGESTION finding) without ever shipping the wrong value in the first
place.
## Consequences
- Every plugin-scope APM agent loses per-agent tool restriction and any Claude-only capability
(isolation, maxTurns, effort, memory, permissionMode) until APM ships a real per-target
integrator for the agent primitive. This is a known, accepted regression, not an oversight.
- **ADR-0005 is partially superseded** — its plugin-scope clause ("directory containing
`plugin.json` is plugin scope → both files land in `<root>/agents/`") no longer applies.
Plugin scope is now "directory containing `apm.yml` → single vendor-neutral file lands in
`<root>/.apm/agents/`." Project and user scope, and the rest of ADR-0005, are unaffected.
- **ADR-0008 is partially superseded** — its counterpart-derivation/pair-validation mechanism
no longer applies at plugin scope; `agent-audit` takes the single file directly there. Project
and user scope, where a real pair still exists, are unaffected.
- **ADR-0009 is not superseded.** The mechanism it established — `agent-audit` reading field
lists from `references/field-inventory.md` rather than hardcoding them, with a `source_keys`
provenance chain — survives and is reused. Only the *content shape* changes for plugin scope:
`field-inventory.md` shifts from two side-by-side CC-only/Copilot-only blocklists to one
vendor-neutral allowlist (`name`/`description`/`model`/`source_keys` — the last for provenance
tracking, validated separately by `validate-provenance.sh` against `sources.md`, not a
provider-specific field) for plugin-scope agents, while
continuing to serve its original two-blocklist role for project/user-scope validation.

View File

@@ -0,0 +1,174 @@
# Plugin roots gain a compiled flat-directory mirror of `.apm/` content so Claude Code can discover it
This ADR is a follow-on correction to ADR-0015 (Microsoft APM replaces hand-authored
plugin/marketplace authoring), discovered during issue #90's post-execution review. It does not
restate ADR-0015's rationale for adopting `.apm/` as the authoring source of truth — see that ADR
for the parent decision. It resolves the one question ADR-0015's own execution flagged as open but
did not block on: whether Claude Code's installer can actually load content out of `.apm/`. It
could not.
**Status: executed (2026-08-13, issue #90).** `scripts/sync-plugin-content.sh` has been run
against all 6 plugins; flat `agents/`, `skills/`, `commands/` (etc., wherever `.apm/` populates
them), and a merged `hooks.json` now exist at each plugin root as tracked, generated files.
## Context
ADR-0015's execution comment on issue #90 (2026-08-12) flagged, before merge: "it's currently
unverified whether Claude Code can actually discover any skill/agent content in these plugins...
This needs to be checked... before treating this conversion as functionally complete, not just
manifest-complete." That caveat did not block ADR-0015 from shipping "Status: executed" — the
manifest-compilation deliverable (`.claude-plugin/marketplace.json`/`plugin.json` generated from
`apm.yml` + `.apm/`) was genuinely complete, and every automated gate (`apm audit --ci`,
`claude plugin validate --strict` ×6, `apm marketplace check`) passed clean — so the ADR merged
with the caveat noted but unresolved.
The caveat turned out to be a real defect, not a formality. `claude plugin install` against all
three plugins tested (`git@holocron`, `gitea@holocron`, `kyberforge@holocron`) reported
`Skills (0) Agents (0) Hooks (0)`. Root cause, confirmed two independent ways:
1. **Claude Code's installer scans flat convention directories only.** `strings` on the installed
`claude` binary finds zero references to `.apm/` or `apm.yml` anywhere. The installed plugin
cache (`~/.claude/plugins/cache/holocron/kyberforge/1.3.1/`) mirrors the pre-conversion flat
`skills/`/`agents/`/`hooks/` layout verbatim — that is what the installer actually copies and
reads. `plugins/kyberforge/docs/research/docs/claude-code-plugins/configuration.md`'s own
"Plugin Directory Layout" table documents the same flat convention (`skills/<name>/SKILL.md`,
`agents/`, `hooks/hooks.json`, all "at the plugin root, not inside `.claude-plugin/`") — this
was accurate before ADR-0015 and never stopped being accurate; ADR-0015 moved plugin content
without adding a bridge to it.
2. **apm's own manifest compiler has no `.apm/` → host-path bridge, by design.**
`apm_cli/core/plugin_manifest.py`'s `build_plugin_manifest` docstring states directly:
"Convention directories (`agents/`, `skills/`, `commands/`) are auto-discovered by the host, so
they are never listed explicitly in the manifest." apm's Claude/Copilot compiler assumes plugin
content already lives in those flat root-level directories; it has no model of `.apm/` nesting
being host-visible at all, so it never emits anything that would point a host at `.apm/`.
Separately, `apm_cli/bundle/plugin_exporter.py`'s `export_plugin_bundle` (the engine behind
`apm pack --format plugin`) *does* implement the correct mapping — `.apm/agents` → `agents/`,
`.apm/skills` → `skills/` (subdirs preserved), `.apm/prompts` + `.apm/commands` → `commands/`
(`*.prompt.md` renamed to `*.md`), `.apm/instructions` → `instructions/`, `.apm/extensions` →
`extensions/`, and `.apm/hooks/*.json` merged into one `hooks.json`. But it was only ever wired to
produce a distributable bundle under `build/<name>-<version>/` — a path nothing in root
`apm.yml`'s per-package `marketplace.packages[].source:` fields (e.g. `./plugins/bin`) or
`marketplace.json`'s equivalent points at. The correct mapping existed in apm's own codebase the
whole time; it was simply never connected to the path this repo's marketplace actually installs
plugins from.
## Decision
Each plugin root gains a second, generated content category, produced by
`scripts/sync-plugin-content.sh` (wraps `apm pack --format plugin`, copies the resulting bundle's
`agents/`, `skills/`, `commands/`, `instructions/`, `extensions/`, and merged `hooks.json` back to
the plugin root) — same governance status as `.claude-plugin/plugin.json`/`marketplace.json`:
**compiled output of `.apm/`, never hand-edited.**
- `.apm/` remains the sole hand-edited authoring source, unchanged from ADR-0015.
- The flat mirror is what Claude Code's (and Copilot's) installer actually convention-scans at
install time — it exists purely to satisfy the host's discovery contract, a contract apm's own
manifest compiler deliberately does not bridge.
- `plugin.json`/`apm.lock.yaml`/`.mcp.json` from the bundle are excluded from the copy:
`plugin.json` is already correctly generated by a separate, already-verified apm code path
(`build_plugin_manifest`, run in the same `apm pack` invocation); `.mcp.json` is hand-authored
at the plugin root per ADR-0015 and is not an `.apm/` primitive.
- Drift is enforced by a pre-push gate (`scripts/sync-plugin-content.sh --check`, wired into
`.pre-commit-config.yaml` as hook id `check-plugin-content-sync` by a parallel workstream on
issue #90) — the same enforcement model `check-manifests.sh` already applies to the other
compiled-output category.
- Verified two ways before landing: `claude plugin validate --strict` passes on all 6 real
(non-scratch) plugin directories, and a live behavioral test
(`claude --plugin-dir plugins/kyberforge -p "list your skills and agents"`) against the real
committed directory confirms `kyberforge:*` skills and the `kyberforge:apm-orchestrate` agent
are now actually discovered — they were not, before this fix.
- The stale root-level `plugins/<name>/plugin.json` files (a near-duplicate of
`.claude-plugin/plugin.json` that nothing read or wrote, flagged separately in issue #90's
review) were deleted across all 6 plugins as part of the same cleanup.
## Considered options
**Patch `plugin.json`'s `skills`/`agents`/`commands`/`hooks` fields to point directly at `.apm/`
paths (rejected).** Claude Code's manifest schema documents these as legitimate override fields
that accept custom paths —
`plugins/kyberforge/docs/research/docs/claude-code-plugins/configuration.md` shows a real example
(`"skills": "./custom/skills/"`, `"agents": ["./custom/agents/reviewer.md"]`), so the host side of
this would work. Rejected because apm's compiler is not a passive pass-through: `build_plugin_manifest`
unconditionally strips these keys from every manifest it generates, on the stated assumption that
convention directories are always host-auto-discovered and therefore never need an explicit
pointer. Honoring this option would mean post-processing apm's compiled output on every
`apm pack` run to re-inject fields apm actively removes — fighting a stable, intentional apm code
path indefinitely — rather than reusing `plugin_exporter.py`'s bundle-export mapping, which already
does the right thing and only needed its output redirected to a path the installer reads.
**Point `marketplace.json`'s `source:` at `apm pack`'s `build/<name>-<version>/` output directly
(rejected).** Would reuse the bundle exporter's correct mapping without adding a new script.
Rejected: `build/` is a version-suffixed, regenerate-on-every-pack directory — pointing the
marketplace at it would mean either committing a moving-target build artifact to version control
(defeating the point of it being generated) or requiring every consumer's marketplace to run
`apm pack` before install, a build step Claude Code's installer has no hook for — it clones/fetches
source and scans directories; it does not execute a package manager's build command first.
Copying the relevant subset back to the stable `plugins/<name>/` path — where `marketplace.json`
already points — needed no change to the marketplace source model at all.
## Amendment (2026-08-13): `mcpServers` is narrowly reinjected into Copilot's `plugin.json`
PR #95's review (a follow-on to this same issue #90 workstream) found a second field apm's
compiler strips for the Copilot ecosystem: `build_plugin_manifest` unconditionally removes
`mcpServers` from every Copilot-ecosystem `plugin.json`, its docstring stating the field is "not
part of the Copilot plugin manifest schema." That claim is contradicted by this repo's own
researched documentation — `plugins/kyberforge/docs/research/docs/github-copilot-plugins/
configuration.md:49` documents `mcpServers` as a valid, optional `plugin.json` field for Copilot.
This is not the same situation "Considered options" above rejected. That rejection concerned
fields apm strips *correctly*, on a stable and accurate premise: convention directories
(`skills/`, `agents/`, `commands/`) are host-auto-discovered, so an explicit pointer is redundant
by design. Here, apm's own stated justification for stripping `mcpServers` is factually wrong
against documented Copilot behavior — there is no host-auto-discovery mechanism that makes an
explicit `mcpServers` declaration redundant, the way there is for skills/agents/commands. Applying
the same "don't fight a stable, intentional apm code path" reasoning here would mean shipping a
plugin manifest known to be missing a field Copilot actually reads.
Given that, `scripts/sync-plugin-content.sh`'s `reinject_mcp_servers()` (line 190, called from
`sync_one()` at line 269, real syncs only) narrowly re-injects `mcpServers` into
`.github/plugin/plugin.json` after a real sync, sourced from the plugin's own `.mcp.json`, and
only when it declares at least one server — matching apm's own Claude-ecosystem builder, which
omits the field entirely rather than emitting `mcpServers: {}`. This is scoped to one field found
to be incorrectly stripped, not a reversal of the broader position above: the rejection of
patching `skills`/`agents`/`commands`/`hooks` pointers still holds, since apm's premise for
stripping those remains accurate.
Consequence: if a future apm release corrects the Copilot `mcpServers` omission, `reinject_mcp_servers()`
and its call site become dead code and should be deleted — nothing else in this ADR depends on the
reinjection existing beyond working around this specific upstream gap.
## Consequences
- Git now tracks real, visible duplication: `.apm/skills/<name>/SKILL.md` and
`skills/<name>/SKILL.md` both exist and must match, likewise `.apm/agents/*.agent.md` vs.
`agents/*.agent.md`, and `.apm/hooks/*.json` vs. the merged `hooks.json`. This is an accepted
tradeoff of bridging a gap apm itself doesn't close, not a bug — `.apm/` stays the single
hand-edited source, and the drift gate (`check-plugin-content-sync`) is what keeps the mirror
honest rather than trusting authors to remember to regenerate it by hand.
- `scripts/check-manifests.sh`'s existing blind spot (flagged in the same issue #90 review round:
it validated `plugin.json` fields that ADR-0015 already stopped populating, so a plugin shipping
zero content could pass it silently) is fixed as part of the same workstream: those field checks
are removed (nothing to check — the fields are correctly absent by design), and the
content-presence question they were standing in for is now answered by
`check-plugin-content-sync`, not re-implemented inside `check-manifests.sh`.
- ADR-0015's "Status: executed" now carries a pointer to this ADR (see that ADR's Consequences)
rather than being rewritten — the manifest-compilation half of its execution was correct and
stands; this ADR fixes the second, previously-unverified half.
- `CONTEXT.md`'s "Plugin" and "Plugin marketplace" glossary entries are updated to describe the
flat mirror as a second compiled-output category, alongside the existing
`.claude-plugin/plugin.json`/`marketplace.json` description.
- A future apm release that ships a native `.apm/`-aware plugin.json compiler (closing this gap
upstream) would let `sync-plugin-content.sh` and its drift gate be deleted outright — nothing in
this ADR's decision depends on the flat mirror existing beyond satisfying the current installer's
convention-scan contract.
- **Reproduction note (2026-08-13):** the live behavioral test cited in "Decision" above
(`claude --plugin-dir plugins/kyberforge -p "list your skills and agents"`) is only a clean
kyberforge-only signal when run from a working directory outside this repo. Run literally as
written, from this repo's root, this repo's own project-level `.claude/settings.json` sets
`enabledPlugins` to true for all 6 holocron plugins (kyberforge, git, gitea, core, lint, bin), so
Claude Code loads all 6 plugins' skills/agents, not just kyberforge's — conflating kyberforge's
discoverability with the other 5 plugins' already-enabled content. To isolate the signal, run
from a neutral cwd outside `/root/ai-development` with an absolute `--plugin-dir` path, e.g.
`cd /some/neutral/dir && claude --plugin-dir /root/ai-development/plugins/kyberforge -p "list your skills and agents"`.
- Reference: issue #90 (https://git.dev.rkdr.net/Defame1297/holocron/issues/90).

View File

@@ -0,0 +1,49 @@
---
name: caveman
description: >
Ultra-compressed communication mode. Cuts token usage ~75% by dropping
filler, articles, and pleasantries while keeping full technical accuracy.
Use when user says "caveman mode", "talk like caveman", "use caveman",
"less tokens", "be brief", or invokes /caveman.
---
Respond terse like smart caveman. All technical substance stay. Only fluff die.
## Persistence
ACTIVE EVERY RESPONSE once triggered. No revert after many turns. No filler drift. Still active if unsure. Off only when user says "stop caveman" or "normal mode".
## Rules
Drop: articles (a/an/the), filler (just/really/basically/actually/simply), pleasantries (sure/certainly/of course/happy to), hedging. Fragments OK. Short synonyms (big not extensive, fix not "implement a solution for"). Abbreviate common terms (DB/auth/config/req/res/fn/impl). Strip conjunctions. Use arrows for causality (X -> Y). One word when one word enough.
Technical terms stay exact. Code blocks unchanged. Errors quoted exact.
Pattern: `[thing] [action] [reason]. [next step].`
Not: "Sure! I'd be happy to help you with that. The issue you're experiencing is likely caused by..."
Yes: "Bug in auth middleware. Token expiry check use `<` not `<=`. Fix:"
### Examples
**"Why React component re-render?"**
> Inline obj prop -> new ref -> re-render. `useMemo`.
**"Explain database connection pooling."**
> Pool = reuse DB conn. Skip handshake -> fast under load.
## Auto-Clarity Exception
Drop caveman temporarily for: security warnings, irreversible action confirmations, multi-step sequences where fragment order risks misread, user asks to clarify or repeats question. Resume caveman after clear part done.
Example -- destructive op:
> **Warning:** This will permanently delete all rows in the `users` table and cannot be undone.
>
> ```sql
> DROP TABLE users;
> ```
>
> Caveman resume. Verify backup exist first.

View File

@@ -0,0 +1,117 @@
---
name: diagnose
description: Disciplined diagnosis loop for hard bugs and performance regressions. Reproduce → minimise → hypothesise → instrument → fix → regression-test. Use when user says "diagnose this" / "debug this", reports a bug, says something is broken/throwing/failing, or describes a performance regression.
---
# Diagnose
A discipline for hard bugs. Skip phases only when explicitly justified.
When exploring the codebase, use the project's domain glossary to get a clear mental model of the relevant modules, and check ADRs in the area you're touching.
## Phase 1 — Build a feedback loop
**This is the skill.** Everything else is mechanical. If you have a fast, deterministic, agent-runnable pass/fail signal for the bug, you will find the cause — bisection, hypothesis-testing, and instrumentation all just consume that signal. If you don't have one, no amount of staring at code will save you.
Spend disproportionate effort here. **Be aggressive. Be creative. Refuse to give up.**
### Ways to construct one — try them in roughly this order
1. **Failing test** at whatever seam reaches the bug — unit, integration, e2e.
2. **Curl / HTTP script** against a running dev server.
3. **CLI invocation** with a fixture input, diffing stdout against a known-good snapshot.
4. **Headless browser script** (Playwright / Puppeteer) — drives the UI, asserts on DOM/console/network.
5. **Replay a captured trace.** Save a real network request / payload / event log to disk; replay it through the code path in isolation.
6. **Throwaway harness.** Spin up a minimal subset of the system (one service, mocked deps) that exercises the bug code path with a single function call.
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 bash script.** Last resort. If a human must click, drive _them_ with `scripts/hitl-loop.template.sh` so the loop is still structured. Captured output feeds back to you.
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:
- 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".)
- Can I make it more deterministic? (Pin time, seed RNG, isolate filesystem, freeze network.)
A 30-second flaky loop is barely better than no loop. A 2-second deterministic loop is a debugging superpower.
### Non-deterministic bugs
The goal is not a clean repro but a **higher reproduction rate**. Loop the trigger 100×, parallelise, add stress, narrow timing windows, inject sleeps. A 50%-flake bug is debuggable; 1% is not — keep raising the rate until it's debuggable.
### When you genuinely cannot build a loop
Stop and say so explicitly. List what you tried. Ask the user for: (a) access to whatever environment reproduces it, (b) a captured artifact (HAR file, log dump, core dump, screen recording with timestamps), or (c) permission to add temporary production instrumentation. Do **not** proceed to hypothesise without a loop.
Do not proceed to Phase 2 until you have a loop you believe in.
## Phase 2 — Reproduce
Run the loop. Watch the bug appear.
Confirm:
- [ ] The loop produces the failure mode the **user** described — not a different failure that happens to be nearby. Wrong bug = wrong fix.
- [ ] The failure is reproducible across multiple runs (or, for non-deterministic bugs, reproducible at a high enough rate to debug against).
- [ ] You have captured the exact symptom (error message, wrong output, slow timing) so later phases can verify the fix actually addresses it.
Do not proceed until you reproduce the bug.
## Phase 3 — Hypothesise
Generate **3–5 ranked hypotheses** before testing any of them. Single-hypothesis generation anchors on the first plausible idea.
Each hypothesis must be **falsifiable**: state the prediction it makes.
> Format: "If <X> is the cause, then <changing Y> will make the bug disappear / <changing Z> will make it worse."
If you cannot state the prediction, the hypothesis is a vibe — discard or sharpen it.
**Show the ranked list to the user before testing.** They often have domain knowledge that re-ranks instantly ("we just deployed a change to #3"), or know hypotheses they've already ruled out. Cheap checkpoint, big time saver. Don't block on it — proceed with your ranking if the user is AFK.
## Phase 4 — Instrument
Each probe must map to a specific prediction from Phase 3. **Change one variable at a time.**
Tool preference:
1. **Debugger / REPL inspection** if the env supports it. One breakpoint beats ten logs.
2. **Targeted logs** at the boundaries that distinguish hypotheses.
3. Never "log everything and grep".
**Tag every debug log** with a unique prefix, e.g. `[DEBUG-a4f2]`. Cleanup at the end becomes a single grep. Untagged logs survive; tagged logs die.
**Perf branch.** For performance regressions, logs are usually wrong. Instead: establish a baseline measurement (timing harness, `performance.now()`, profiler, query plan), then bisect. Measure first, fix second.
## Phase 5 — Fix + regression test
Write the regression test **before the fix** — but only if there is a **correct seam** for it.
A correct seam is one where the test exercises the **real bug pattern** as it occurs at the call site. If the only available seam is too shallow (single-caller test when the bug needs multiple callers, unit test that can't replicate the chain that triggered the bug), a regression test there gives false confidence.
**If no correct seam exists, that itself is the finding.** Note it. The codebase architecture is preventing the bug from being locked down. Flag this for the next phase.
If a correct seam exists:
1. Turn the minimised repro into a failing test at that seam.
2. Watch it fail.
3. Apply the fix.
4. Watch it pass.
5. Re-run the Phase 1 feedback loop against the original (un-minimised) scenario.
## Phase 6 — Cleanup + post-mortem
Required before declaring done:
- [ ] Original repro no longer reproduces (re-run the Phase 1 loop)
- [ ] Regression test passes (or absence of seam is documented)
- [ ] All `[DEBUG-...]` instrumentation removed (`grep` the prefix)
- [ ] Throwaway prototypes deleted (or moved to a clearly-marked debug location)
- [ ] The hypothesis that turned out correct is stated in the commit / PR message — so the next debugger learns
**Then ask: what would have prevented this bug?** If the answer involves architectural change (no good test seam, tangled callers, hidden coupling) hand off to the `/improve-codebase-architecture` skill with the specifics. Make the recommendation **after** the fix is in, not before — you have more information now than when you started.

View File

@@ -0,0 +1,41 @@
#!/usr/bin/env bash
# Human-in-the-loop reproduction loop.
# Copy this file, edit the steps below, and run it.
# The agent runs the script; the user follows prompts in their terminal.
#
# Usage:
# bash hitl-loop.template.sh
#
# Two helpers:
# step "<instruction>" → show instruction, wait for Enter
# capture VAR "<question>" → show question, read response into VAR
#
# At the end, captured values are printed as KEY=VALUE for the agent to parse.
set -euo pipefail
step() {
printf '\n>>> %s\n' "$1"
read -r -p " [Enter when done] " _
}
capture() {
local var="$1" question="$2" answer
printf '\n>>> %s\n' "$question"
read -r -p " > " answer
printf -v "$var" '%s' "$answer"
}
# --- edit below ---------------------------------------------------------
step "Open the app at http://localhost:3000 and sign in."
capture ERRORED "Click the 'Export' button. Did it throw an error? (y/n)"
capture ERROR_MSG "Paste the error message (or 'none'):"
# --- edit above ---------------------------------------------------------
printf '\n--- Captured ---\n'
printf 'ERRORED=%s\n' "$ERRORED"
printf 'ERROR_MSG=%s\n' "$ERROR_MSG"

View File

@@ -0,0 +1,10 @@
---
name: grill-me
description: Interview the user relentlessly about a plan or design until reaching shared understanding, resolving each branch of the decision tree. Use when user wants to stress-test a plan, get grilled on their design, or mentions "grill me".
---
Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer.
Ask the questions one at a time.
If a question can be answered by exploring the codebase, explore the codebase instead.

View File

@@ -0,0 +1,47 @@
# ADR Format
ADRs live in `docs/adr/` and use sequential numbering: `0001-slug.md`, `0002-slug.md`, etc.
Create the `docs/adr/` directory lazily — only when the first ADR is needed.
## Template
```md
# {Short title of the decision}
{1-3 sentences: what's the context, what did we decide, and why.}
```
That's it. An ADR can be a single paragraph. The value is in recording *that* a decision was made and *why* — not in filling out sections.
## Optional sections
Only include these when they add genuine value. Most ADRs won't need them.
- **Status** frontmatter (`proposed | accepted | deprecated | superseded by ADR-NNNN`) — useful when decisions are revisited
- **Considered Options** — only when the rejected alternatives are worth remembering
- **Consequences** — only when non-obvious downstream effects need to be called out
## Numbering
Scan `docs/adr/` for the highest existing number and increment by one.
## When to offer an ADR
All three of these must be true:
1. **Hard to reverse** — the cost of changing your mind later is meaningful
2. **Surprising without context** — a future reader will look at the code and wonder "why on earth did they do it this way?"
3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons
If a decision is easy to reverse, skip it — you'll just reverse it. If it's not surprising, nobody will wonder why. If there was no real alternative, there's nothing to record beyond "we did the obvious thing."
### What qualifies
- **Architectural shape.** "We're using a monorepo." "The write model is event-sourced, the read model is projected into Postgres."
- **Integration patterns between contexts.** "Ordering and Billing communicate via domain events, not synchronous HTTP."
- **Technology choices that carry lock-in.** Database, message bus, auth provider, deployment target. Not every library — just the ones that would take a quarter to swap out.
- **Boundary and scope decisions.** "Customer data is owned by the Customer context; other contexts reference it by ID only." The explicit no-s are as valuable as the yes-s.
- **Deliberate deviations from the obvious path.** "We're using manual SQL instead of an ORM because X." Anything where a reasonable reader would assume the opposite. These stop the next engineer from "fixing" something that was deliberate.
- **Constraints not visible in the code.** "We can't use AWS because of compliance requirements." "Response times must be under 200ms because of the partner API contract."
- **Rejected alternatives when the rejection is non-obvious.** If you considered GraphQL and picked REST for subtle reasons, record it — otherwise someone will suggest GraphQL again in six months.

View File

@@ -0,0 +1,77 @@
# CONTEXT.md Format
## Structure
```md
# {Context Name}
{One or two sentence description of what this context is and why it exists.}
## Language
**Order**:
{A concise description of the term}
_Avoid_: Purchase, transaction
**Invoice**:
A request for payment sent to a customer after delivery.
_Avoid_: Bill, payment request
**Customer**:
A person or organization that places orders.
_Avoid_: Client, buyer, account
## Relationships
- An **Order** produces one or more **Invoices**
- An **Invoice** belongs to exactly one **Customer**
## Example dialogue
> **Dev:** "When a **Customer** places an **Order**, do we create the **Invoice** immediately?"
> **Domain expert:** "No — an **Invoice** is only generated once a **Fulfillment** is confirmed."
## Flagged ambiguities
- "account" was used to mean both **Customer** and **User** — resolved: these are distinct concepts.
```
## Rules
- **Be opinionated.** When multiple words exist for the same concept, pick the best one and list the others as aliases to avoid.
- **Flag conflicts explicitly.** If a term is used ambiguously, call it out in "Flagged ambiguities" with a clear resolution.
- **Keep definitions tight.** One sentence max. Define what it IS, not what it does.
- **Show relationships.** Use bold term names and express cardinality where obvious.
- **Only include terms specific to this project's context.** General programming concepts (timeouts, error types, utility patterns) don't belong even if the project uses them extensively. Before adding a term, ask: is this a concept unique to this context, or a general programming concept? Only the former belongs.
- **Group terms under subheadings** when natural clusters emerge. If all terms belong to a single cohesive area, a flat list is fine.
- **Write an example dialogue.** A conversation between a dev and a domain expert that demonstrates how the terms interact naturally and clarifies boundaries between related concepts.
## Single vs multi-context repos
**Single context (most repos):** One `CONTEXT.md` at the repo root.
**Multiple contexts:** A `CONTEXT-MAP.md` at the repo root lists the contexts, where they live, and how they relate to each other:
```md
# Context Map
## Contexts
- [Ordering](./src/ordering/CONTEXT.md) — receives and tracks customer orders
- [Billing](./src/billing/CONTEXT.md) — generates invoices and processes payments
- [Fulfillment](./src/fulfillment/CONTEXT.md) — manages warehouse picking and shipping
## Relationships
- **Ordering → Fulfillment**: Ordering emits `OrderPlaced` events; Fulfillment consumes them to start picking
- **Fulfillment → Billing**: Fulfillment emits `ShipmentDispatched` events; Billing consumes them to generate invoices
- **Ordering ↔ Billing**: Shared types for `CustomerId` and `Money`
```
The skill infers which structure applies:
- If `CONTEXT-MAP.md` exists, read it to find contexts
- If only a root `CONTEXT.md` exists, single context
- If neither exists, create a root `CONTEXT.md` lazily when the first term is resolved
When multiple contexts exist, infer which one the current topic relates to. If unclear, ask.

View File

@@ -0,0 +1,88 @@
---
name: grill-with-docs
description: Grilling session that challenges your plan against the existing domain model, sharpens terminology, and updates documentation (CONTEXT.md, ADRs) inline as decisions crystallise. Use when user wants to stress-test a plan against their project's language and documented decisions.
---
<what-to-do>
Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer.
Ask the questions one at a time, waiting for feedback on each question before continuing.
If a question can be answered by exploring the codebase, explore the codebase instead.
</what-to-do>
<supporting-info>
## Domain awareness
During codebase exploration, also look for existing documentation:
### File structure
Most repos have a single context:
```
/
├── CONTEXT.md
├── docs/
│ └── adr/
│ ├── 0001-event-sourced-orders.md
│ └── 0002-postgres-for-write-model.md
└── src/
```
If a `CONTEXT-MAP.md` exists at the root, the repo has multiple contexts. The map points to where each one lives:
```
/
├── CONTEXT-MAP.md
├── docs/
│ └── adr/ ← system-wide decisions
├── src/
│ ├── ordering/
│ │ ├── CONTEXT.md
│ │ └── docs/adr/ ← context-specific decisions
│ └── billing/
│ ├── CONTEXT.md
│ └── docs/adr/
```
Create files lazily — only when you have something to write. If no `CONTEXT.md` exists, create one when the first term is resolved. If no `docs/adr/` exists, create it when the first ADR is needed.
## During the session
### Challenge against the glossary
When the user uses a term that conflicts with the existing language in `CONTEXT.md`, call it out immediately. "Your glossary defines 'cancellation' as X, but you seem to mean Y — which is it?"
### Sharpen fuzzy language
When the user uses vague or overloaded terms, propose a precise canonical term. "You're saying 'account' — do you mean the Customer or the User? Those are different things."
### Discuss concrete scenarios
When domain relationships are being discussed, stress-test them with specific scenarios. Invent scenarios that probe edge cases and force the user to be precise about the boundaries between concepts.
### Cross-reference with code
When the user states how something works, check whether the code agrees. If you find a contradiction, surface it: "Your code cancels entire Orders, but you just said partial cancellation is possible — which is right?"
### Update CONTEXT.md inline
When a term is resolved, update `CONTEXT.md` right there. Don't batch these up — capture them as they happen. Use the format in [CONTEXT-FORMAT.md](./CONTEXT-FORMAT.md).
Don't couple `CONTEXT.md` to implementation details. Only include terms that are meaningful to domain experts.
### Offer ADRs sparingly
Only offer to create an ADR when all three are true:
1. **Hard to reverse** — the cost of changing your mind later is meaningful
2. **Surprising without context** — a future reader will wonder "why did they do it this way?"
3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons
If any of the three is missing, skip the ADR. Use the format in [ADR-FORMAT.md](./ADR-FORMAT.md).
</supporting-info>

View File

@@ -0,0 +1,37 @@
# Deepening
How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in [LANGUAGE.md](LANGUAGE.md) — **module**, **interface**, **seam**, **adapter**.
## Dependency categories
When assessing a candidate for deepening, classify its dependencies. The category determines how the deepened module is tested across its seam.
### 1. In-process
Pure computation, in-memory state, no I/O. Always deepenable — merge the modules and test through the new interface directly. No adapter needed.
### 2. Local-substitutable
Dependencies that have local test stand-ins (PGLite for Postgres, in-memory filesystem). Deepenable if the stand-in exists. The deepened module is tested with the stand-in running in the test suite. The seam is internal; no port at the module's external interface.
### 3. Remote but owned (Ports & Adapters)
Your own services across a network boundary (microservices, internal APIs). Define a **port** (interface) at the seam. The deep module owns the logic; the transport is injected as an **adapter**. Tests use an in-memory adapter. Production uses an HTTP/gRPC/queue adapter.
Recommendation shape: *"Define a port at the seam, implement an HTTP adapter for production and an in-memory adapter for testing, so the logic sits in one deep module even though it's deployed across a network."*
### 4. True external (Mock)
Third-party services (Stripe, Twilio, etc.) you don't control. The deepened module takes the external dependency as an injected port; tests provide a mock adapter.
## Seam discipline
- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a port unless at least two adapters are justified (typically production + test). A single-adapter seam is just indirection.
- **Internal seams vs external seams.** A deep module can have internal seams (private to its implementation, used by its own tests) as well as the external seam at its interface. Don't expose internal seams through the interface just because tests use them.
## Testing strategy: replace, don't layer
- Old unit tests on shallow modules become waste once tests at the deepened module's interface exist — delete them.
- Write new tests at the deepened module's interface. The **interface is the test surface**.
- Tests assert on observable outcomes through the interface, not internal state.
- Tests should survive internal refactors — they describe behaviour, not implementation. If a test has to change when the implementation changes, it's testing past the interface.

View File

@@ -0,0 +1,44 @@
# Interface Design
When the user wants to explore alternative interfaces for a chosen deepening candidate, use this parallel sub-agent pattern. Based on "Design It Twice" (Ousterhout) — your first idea is unlikely to be the best.
Uses the vocabulary in [LANGUAGE.md](LANGUAGE.md) — **module**, **interface**, **seam**, **adapter**, **leverage**.
## Process
### 1. Frame the problem space
Before spawning sub-agents, write a user-facing explanation of the problem space for the chosen candidate:
- The constraints any new interface would need to satisfy
- The dependencies it would rely on, and which category they fall into (see [DEEPENING.md](DEEPENING.md))
- A rough illustrative code sketch to ground the constraints — not a proposal, just a way to make the constraints concrete
Show this to the user, then immediately proceed to Step 2. The user reads and thinks while the sub-agents work in parallel.
### 2. Spawn sub-agents
Spawn 3+ sub-agents in parallel using the Agent tool. Each must produce a **radically different** interface for the deepened module.
Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from [DEEPENING.md](DEEPENING.md), what sits behind the seam). The brief is independent of the user-facing problem-space explanation in Step 1. Give each agent a different design constraint:
- Agent 1: "Minimize the interface — aim for 1–3 entry points max. Maximise leverage per entry point."
- Agent 2: "Maximise flexibility — support many use cases and extension."
- Agent 3: "Optimise for the most common caller — make the default case trivial."
- Agent 4 (if applicable): "Design around ports & adapters for cross-seam dependencies."
Include both [LANGUAGE.md](LANGUAGE.md) vocabulary and CONTEXT.md vocabulary in the brief so each sub-agent names things consistently with the architecture language and the project's domain language.
Each sub-agent outputs:
1. Interface (types, methods, params — plus invariants, ordering, error modes)
2. Usage example showing how callers use it
3. What the implementation hides behind the seam
4. Dependency strategy and adapters (see [DEEPENING.md](DEEPENING.md))
5. Trade-offs — where leverage is high, where it's thin
### 3. Present and compare
Present designs sequentially so the user can absorb each one, then compare them in prose. Contrast by **depth** (leverage at the interface), **locality** (where change concentrates), and **seam placement**.
After comparing, give your own recommendation: which design you think is strongest and why. If elements from different designs would combine well, propose a hybrid. Be opinionated — the user wants a strong read, not a menu.

View File

@@ -0,0 +1,53 @@
# Language
Shared vocabulary for every suggestion this skill makes. Use these terms exactly — don't substitute "component," "service," "API," or "boundary." Consistent language is the whole point.
## Terms
**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.
**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).
**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.
**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)_
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).
**Adapter**
A concrete thing that satisfies an interface at a seam. Describes *role* (what slot it fills), not substance (what's inside).
**Leverage**
What callers get from depth. More capability per unit of interface they have to learn. One implementation pays back across N call sites and M tests.
**Locality**
What maintainers get from depth. Change, bugs, knowledge, and verification concentrate at one place rather than spreading across callers. Fix once, fixed everywhere.
## Principles
- **Depth is a property of the interface, not the implementation.** A deep module can be internally composed of small, mockable, swappable parts — they just aren't part of the interface. A module can have **internal seams** (private to its implementation, used by its own tests) as well as the **external seam** at its interface.
- **The deletion test.** Imagine deleting the module. If complexity vanishes, the module wasn't hiding anything (it was a pass-through). If complexity reappears across N callers, the module was earning its keep.
- **The interface is the test surface.** Callers and tests cross the same seam. If you want to test *past* the interface, the module is probably the wrong shape.
- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a seam unless something actually varies across it.
## Relationships
- A **Module** has exactly one **Interface** (the surface it presents to callers and tests).
- **Depth** is a property of a **Module**, measured against its **Interface**.
- A **Seam** is where a **Module**'s **Interface** lives.
- An **Adapter** sits at a **Seam** and satisfies the **Interface**.
- **Depth** produces **Leverage** for callers and **Locality** for maintainers.
## Rejected framings
- **Depth as ratio of implementation-lines to interface-lines** (Ousterhout): rewards padding the implementation. We use depth-as-leverage instead.
- **"Interface" as the TypeScript `interface` keyword or a class's public methods**: too narrow — interface here includes every fact a caller must know.
- **"Boundary"**: overloaded with DDD's bounded context. Say **seam** or **interface**.

View File

@@ -0,0 +1,71 @@
---
name: improve-codebase-architecture
description: Find deepening opportunities in a codebase, informed by the domain language in CONTEXT.md and the decisions in docs/adr/. Use when the user wants to improve architecture, find refactoring opportunities, consolidate tightly-coupled modules, or make a codebase more testable and AI-navigable.
---
# Improve Codebase Architecture
Surface architectural friction and propose **deepening opportunities** — refactors that turn shallow modules into deep ones. The aim is testability and AI-navigability.
## Glossary
Use these terms exactly in every suggestion. Consistent language is the point — don't drift into "component," "service," "API," or "boundary." Full definitions in [LANGUAGE.md](LANGUAGE.md).
- **Module** — anything with an interface and an implementation (function, class, package, slice).
- **Interface** — everything a caller must know to use the module: types, invariants, error modes, ordering, config. Not just the type signature.
- **Implementation** — the code inside.
- **Depth** — leverage at the interface: a lot of behaviour behind a small interface. **Deep** = high leverage. **Shallow** = interface nearly as complex as the implementation.
- **Seam** — where an interface lives; a place behaviour can be altered without editing in place. (Use this, not "boundary.")
- **Adapter** — a concrete thing satisfying an interface at a seam.
- **Leverage** — what callers get from depth.
- **Locality** — what maintainers get from depth: change, bugs, knowledge concentrated in one place.
Key principles (see [LANGUAGE.md](LANGUAGE.md) for the full list):
- **Deletion test**: imagine deleting the module. If complexity vanishes, it was a pass-through. If complexity reappears across N callers, it was earning its keep.
- **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.
## Process
### 1. Explore
Read the project's domain glossary and any ADRs in the area you're touching first.
Then use the Agent tool with `subagent_type=Explore` to walk the codebase. Don't follow rigid heuristics — explore organically and note where you experience friction:
- Where does understanding one concept require bouncing between many small modules?
- Where are modules **shallow** — interface nearly as complex as the implementation?
- Where have pure functions been extracted just for testability, but the real bugs hide in how they're called (no **locality**)?
- Where do tightly-coupled modules leak across their seams?
- Which parts of the codebase are untested, or hard to test through their current interface?
Apply the **deletion test** to anything you suspect is shallow: would deleting it concentrate complexity, or just move it? A "yes, concentrates" is the signal you want.
### 2. Present candidates
Present a numbered list of deepening opportunities. For each candidate:
- **Files** — which files/modules are involved
- **Problem** — why the current architecture is causing friction
- **Solution** — plain English description of what would change
- **Benefits** — explained in terms of locality and leverage, and also in how tests would improve
**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 (e.g. _"contradicts ADR-0007 — but worth reopening because…"_). Don't list every theoretical refactor an ADR forbids.
Do NOT propose interfaces yet. Ask the user: "Which of these would you like to explore?"
### 3. Grilling loop
Once the user picks a candidate, drop into a grilling conversation. Walk the design tree with them — constraints, dependencies, the shape of the deepened module, what sits behind the seam, what tests survive.
Side effects happen inline as decisions crystallize:
- **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md` — same discipline as `/grill-with-docs` (see [CONTEXT-FORMAT.md](../grill-with-docs/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](../grill-with-docs/ADR-FORMAT.md).
- **Want to explore alternative interfaces for the deepened module?** See [INTERFACE-DESIGN.md](INTERFACE-DESIGN.md).

View File

@@ -0,0 +1,79 @@
# Logic Prototype
A tiny interactive terminal app that lets the user drive a state model by hand. Use this when the question is about **business logic, state transitions, or data shape** — the kind of thing that looks reasonable on paper but only feels wrong once you push it through real cases.
## When this is the right shape
- "I'm not sure if this state machine handles the edge case where X then Y."
- "Does this data model actually let me represent the case where..."
- "I want to feel out what the API should look like before writing it."
- Anything where the user wants to **press buttons and watch state change**.
If the question is "what should this look like" — wrong branch. Use [UI.md](UI.md).
## Process
### 1. State the question
Before writing code, write down what state model and what question you're prototyping. One paragraph, in the prototype's README or a comment at the top of the file. A logic prototype that answers the wrong question is pure waste — make the question explicit so it can be checked later, whether the user is watching now or returning to it AFK.
### 2. Pick the language
Use whatever the host project uses. If the project has no obvious runtime (e.g. a docs repo), ask.
Match the project's existing conventions for tooling — don't add a new package manager or runtime just for the prototype.
### 3. Isolate the logic in a portable module
Put the actual logic — the bit that's answering the question — behind a small, pure interface that could be lifted out and dropped into the real codebase later. The TUI around it is throwaway; the logic module shouldn't be.
The right shape depends on the question:
- **A pure reducer** — `(state, action) => state`. Good when actions are discrete events and state is a single value.
- **A state machine** — explicit states and transitions. Good when "which actions are even legal right now" is part of the question.
- **A small set of pure functions** over a plain data type. Good when there's no implicit current state — just transformations.
- **A class or module with a clear method surface** when the logic genuinely owns ongoing internal state.
Pick whichever shape best fits the question being asked, *not* whichever is easiest to wire to a TUI. Keep it pure: no I/O, no terminal code, no `console.log` for control flow. The TUI imports it and calls into it; nothing flows the other direction.
This is what makes the prototype useful past its own lifetime. When the question's been answered, the validated reducer / machine / function set can be lifted into the real module — the TUI shell gets deleted.
### 4. Build the smallest TUI that exposes the state
Build it as a **lightweight TUI** — on every tick, clear the screen (`console.clear()` / `print("\033[2J\033[H")` / equivalent) and re-render the whole frame. The user should always see one stable view, not an ever-growing scrollback.
Each frame has two parts, in this order:
1. **Current state**, pretty-printed and diff-friendly (one field per line, or formatted JSON). Use **bold** for field names or section headers and **dim** for less important context (timestamps, IDs, derived values). Native ANSI escape codes are fine — `\x1b[1m` bold, `\x1b[2m` dim, `\x1b[0m` reset. No need to pull in a styling library unless one is already in the project.
2. **Keyboard shortcuts**, listed at the bottom: `[a] add user [d] delete user [t] tick clock [q] quit`. Bold the key, dim the description, or vice-versa — whatever reads cleanly.
Behaviour:
1. **Initialise state** — a single in-memory object/struct. Render the first frame on start.
2. **Read one keystroke (or one line)** at a time, dispatch to a handler that mutates state.
3. **Re-render** the full frame after every action — don't append, replace.
4. **Loop until quit.**
The whole frame should fit on one screen.
### 5. Make it runnable in one command
Add a script to the project's existing task runner (`package.json` scripts, `Makefile`, `justfile`, `pyproject.toml`). The user should run `pnpm run <prototype-name>` or equivalent — never need to remember a path.
If the host project has no task runner, just put the command at the top of the prototype's README.
### 6. Hand it over
Give the user the run command. They'll drive it themselves; the interesting moments are when they say "wait, that shouldn't be possible" or "huh, I assumed X would be different" — those are the bugs in the _idea_, which is the whole point. If they want new actions added, add them. Prototypes evolve.
### 7. Capture the answer
When the prototype has done its job, the answer to the question is the only thing worth keeping. If the user is around, ask what it taught them. If not, leave a `NOTES.md` next to the prototype so the answer can be filled in (or filled in by you, if you've watched the session) before the prototype gets deleted.
## Anti-patterns
- **Don't add tests.** A prototype that needs tests is no longer a prototype.
- **Don't wire it to the real database.** Use an in-memory store unless the question is specifically about persistence.
- **Don't generalise.** No "what if we wanted to support X later." The prototype answers one question.
- **Don't blur the logic and the TUI together.** If the reducer / state machine references `console.log`, prompts, or terminal escape codes, it's no longer portable. Keep the TUI as a thin shell over a pure module.
- **Don't ship the TUI shell into production.** The shell is optimised for being driven by hand from a terminal. The logic module behind it is the bit worth keeping.

View File

@@ -0,0 +1,30 @@
---
name: prototype
description: Build a throwaway prototype to flush out a design before committing to it. Routes between two branches — a runnable terminal app for state/business-logic questions, or several radically different UI variations toggleable from one route. Use when the user wants to prototype, sanity-check a data model or state machine, mock up a UI, explore design options, or says "prototype this", "let me play with it", "try a few designs".
---
# Prototype
A prototype is **throwaway code that answers a question**. The question decides the shape.
## Pick a branch
Identify which question is being answered — from the user's prompt, the surrounding code, or by asking if the user is around:
- **"Does this logic / state model feel right?"** → [LOGIC.md](LOGIC.md). Build a tiny interactive terminal app that pushes the state machine through cases that are hard to reason about on paper.
- **"What should this look like?"** → [UI.md](UI.md). Generate several radically different UI variations on a single route, switchable via a URL search param and a floating bottom bar.
The two branches produce fundamentally different artifacts — getting this wrong wastes the whole prototype. If the question is genuinely ambiguous and the user isn't reachable, default to whichever branch better matches the surrounding code (a backend module → logic; a page or component → UI) and state the assumption at the top of the prototype.
## Rules that apply to both
1. **Throwaway from day one, and clearly marked as such.** Locate the prototype code close to where it will actually be used (next to the module or page it's prototyping for) so context is obvious — but name it so a casual reader can see it's a prototype, not production. For throwaway UI routes, obey whatever routing convention the project already uses; don't invent a new top-level structure.
2. **One command to run.** Whatever the project's existing task runner supports — `pnpm <name>`, `python <path>`, `bun <path>`, etc. The user must be able to start it without thinking.
3. **No persistence by default.** State lives in memory. Persistence is the thing the prototype is *checking*, not something it should depend on. If the question explicitly involves a database, hit a scratch DB or a local file with a clear "PROTOTYPE — wipe me" name.
4. **Skip the polish.** No tests, no error handling beyond what makes the prototype *runnable*, no abstractions. The point is to learn something fast and then delete it.
5. **Surface the state.** After every action (logic) or on every variant switch (UI), print or render the full relevant state so the user can see what changed.
6. **Delete or absorb when done.** When the prototype has answered its question, either delete it or fold the validated decision into the real code — don't leave it rotting in the repo.
## When done
The *answer* is the only thing worth keeping from a prototype. Capture it somewhere durable (commit message, ADR, issue, or a `NOTES.md` next to the prototype) along with the question it was answering. If the user is around, that capture is a quick conversation; if not, leave the placeholder so they (or you, on the next pass) can fill in the verdict before deleting the prototype.

View File

@@ -0,0 +1,112 @@
# UI Prototype
Generate **several radically different UI variations** on a single route, switchable from a floating bottom bar. The user flips between variants in the browser, picks one (or steals bits from each), then throws the rest away.
If the question is about logic/state rather than what something looks like — wrong branch. Use [LOGIC.md](LOGIC.md).
## When this is the right shape
- "What should this page look like?"
- "I want to see a few options for this dashboard before committing."
- "Try a different layout for the settings screen."
- Any time the user would otherwise spend a day picking between three vague mockups in their head.
## Two sub-shapes — strongly prefer sub-shape A
A UI prototype is much easier to judge when it's **butting up against the rest of the app** — real header, real sidebar, real data, real density. A throwaway route on its own is a vacuum: every variant looks fine in isolation. Default to sub-shape A whenever there's a plausible existing page to host the variants. Only reach for sub-shape B if the prototype genuinely has no nearby home.
### Sub-shape A — adjustment to an existing page (preferred)
The route already exists. Variants are rendered **on the same route**, gated by a `?variant=` URL search param. The existing data fetching, params, and auth all stay — only the rendering swaps. This is the default; pick it unless there's a specific reason not to.
If the prototype is for something that doesn't yet have a page but *would naturally live inside one* (a new section of the dashboard, a new card on the settings screen, a new step in an existing flow) — that's still sub-shape A. Mount the variants inside the host page.
### Sub-shape B — a new page (last resort)
Only use this when the thing being prototyped genuinely has no existing page to live inside — e.g. an entirely new top-level surface, or a flow that can't be embedded anywhere sensible.
Create a **throwaway route** following whatever routing convention the project already uses — don't invent a new top-level structure. Name it so it's obviously a prototype (e.g. include the word `prototype` in the path or filename). Same `?variant=` pattern.
Before committing to sub-shape B, sanity-check: is there really no existing page this could be embedded in? An empty route hides design problems that a populated one would expose.
In both sub-shapes the floating bottom bar is identical.
## Process
### 1. State the question and pick N
Default to **3 variants**. More than 5 stops being radically different and starts being noise — cap there.
Write down the plan in one line, in the prototype's location or a top-of-file comment:
> "Three variants of the settings page, switchable via `?variant=`, on the existing `/settings` route."
This works whether the user is here to push back or not.
### 2. Generate radically different variants
Draft each variant. Hold each one to:
- The page's purpose and the data it has access to.
- The project's component library / styling system (TailwindCSS, shadcn, MUI, plain CSS, whatever).
- A clear exported component name, e.g. `VariantA`, `VariantB`, `VariantC`.
Variants must be **structurally different** — different layout, different information hierarchy, different primary affordance, not just different colours. Three slightly-tweaked card grids isn't a UI prototype, it's wallpaper. If two drafts come out too similar, redo one with explicit "do not use a card grid" guidance.
### 3. Wire them together
Create a single switcher component on the route:
```tsx
// pseudo-code — adapt to the project's framework
const variant = searchParams.get('variant') ?? 'A';
return (
<>
{variant === 'A' && <VariantA {...data} />}
{variant === 'B' && <VariantB {...data} />}
{variant === 'C' && <VariantC {...data} />}
<PrototypeSwitcher variants={['A','B','C']} current={variant} />
</>
);
```
For sub-shape A (existing page): keep all the existing data fetching above the switcher; only the rendered subtree changes per variant.
For sub-shape B (new page): the throwaway route under `/prototype/<name>` mounts the same switcher.
### 4. Build the floating switcher
A small fixed-position bar at the bottom-centre of the screen with three pieces:
- **Left arrow** — cycles to the previous variant (wraps around).
- **Variant label** — shows the current variant key and, if the variant exports a name, that name too. e.g. `B — Sidebar layout`.
- **Right arrow** — cycles forward (wraps around).
Behaviour:
- Clicking an arrow updates the URL search param (use the framework's router — `router.replace` on Next, `navigate` on React Router, etc) so the variant is shareable and reload-stable.
- Keyboard: `←` and `→` arrow keys also cycle. Don't intercept arrow keys when an `<input>`, `<textarea>`, or `[contenteditable]` is focused.
- Visually distinct from the page (e.g. high-contrast pill, subtle shadow) so it's obviously not part of the design being evaluated.
- Hidden in production builds — gate on `process.env.NODE_ENV !== 'production'` or an equivalent check, so a stray prototype merge can't ship the bar to users.
Put the switcher in a single shared component so both sub-shapes can reuse it. Locate it wherever shared UI lives in the project.
### 5. Hand it over
Surface the URL (and the `?variant=` keys). The user will flip through whenever they get to it. The interesting feedback is usually **"I want the header from B with the sidebar from C"** — that's the actual design they want.
### 6. Capture the answer and clean up
Once a variant has won, write down which one and why (commit message, ADR, issue, or a `NOTES.md` next to the prototype if running AFK and the user hasn't responded yet). Then:
- **Sub-shape A** — delete the losing variants and the switcher; fold the winner into the existing page.
- **Sub-shape B** — promote the winning variant to a real route, delete the throwaway route and the switcher.
Don't leave variant components or the switcher lying around. They rot fast and confuse the next reader.
## Anti-patterns
- **Variants that differ only in colour or copy.** That's a tweak, not a prototype. Real variants disagree about structure.
- **Sharing too much code between variants.** A shared `<Header>` is fine; a shared `<Layout>` defeats the point. Each variant should be free to throw out the layout.
- **Wiring variants to real mutations.** Read-only prototypes are fine. If a variant needs to mutate, point it at a stub — the question is "what should this look like", not "does the backend work".
- **Promoting the prototype directly to production.** The variant code was written under prototype constraints (no tests, minimal error handling). Rewrite it properly when you fold it in.

View File

@@ -0,0 +1,15 @@
```yaml
version: "1.1"
updated: 2026-06-21
when: >-
Invoked when the user wants to gather structured reference documentation for a
tool, library, or API from MCP documentation indexes or web sources. Typically
run before writing a new skill that wraps an external tool, or any time
reference files are needed for a topic. Triggered explicitly
("/research <topic> <path>") or implicitly when the user asks to look up,
gather, or pull docs for a topic before implementing something.
references:
- .agents/skills/context7-mcp/SKILL.md # context7-mcp — MCP source channel integrated at step 2
```

View File

@@ -0,0 +1,97 @@
---
name: research
description: >-
Use when the user wants to research a topic and generate structured reference
markdown files. Handles: finding canonical docs for a tool/library/API via
Context7 MCP or web sources, reading and deepening into linked pages,
organizing extracted content into topic files (overview, installation,
configuration, cli-reference, api-reference, examples, troubleshooting). Do
NOT use when the user wants to write documentation from existing code or specs
(use write-docs), install or manage the neuledge-context MCP server (use
neuledge-context), or research a bug/incident (use diagnose).
metadata:
category: research
allowed-tools:
- WebSearch
- WebFetch
- Read
- Write
- mcp__context7__resolve-library-id
- mcp__context7__query-docs
model: sonnet
---
<requirements>
## Required inputs
- **Topic** — the subject to research (tool, library, API, concept); inferred from user description if clear, ask if ambiguous
- **Output path** — directory where reference files will be written; must be provided explicitly — do not infer or default
- **Starting URLs** — optional; if provided, skip discovery websearch and read these first
## Constraints
- Never write files outside the explicitly provided output path
- Skip any default topic file if no relevant content is found for it — do not create empty files
- Create additional topic files beyond the default list when content warrants it (e.g. `webhooks.md`, `rate-limits.md`)
- Subagents handle parallel source reading and link deepening — the orchestrator writes all files; subagents return summaries only, never write directly
- Context7 MCP calls (`resolve-library-id`, `query-docs`) are made only by the orchestrator at step 2 — subagents must not call them
- `sources.md` is always written, even if only one source was read
- Each topic file must have frontmatter with `topic` and `source_keys`; body is prose only — no inline URLs
- Source keys in `sources.md` must be kebab-case slugs: derived from the source domain or page title for web sources; for Context7 sources use `context7-<library-slug>` (e.g. `context7-vercel-next-js`)
- Default topic list and file format spec live in `references/` sub-files — read them at step 1
</requirements>
<steps>
## Process
1. **Scan codebase.** Search the working directory for existing usage of the topic — imports, config files, version pins, existing reference files. Use findings to narrow research scope (e.g. target the version already in use, skip topics already documented). Read `references/topics.md` for the default topic list and `references/file-format.md` for the output file format spec.
2. **Try Context7.** If the topic is a library, framework, or API and no starting URLs were provided, call `resolve-library-id` with the topic name and the user's question. If a match resolves, call `query-docs` once per default topic area (see `references/topics.md`). Treat each response as a source summary with slug `context7-<library-slug>` (e.g. `context7-vercel-next-js`). A topic area has sufficient content when the Context7 response contains at least one substantive paragraph — not a "no results" message, redirect notice, or header-only boilerplate. Mark covered topic areas — skip their subagent web reads in step 4. If the library does not resolve, or starting URLs were provided (explicit source choice by the user), skip this step entirely.
3. **Discover sources.** For topics not covered by Context7 (or when no starting URLs were provided and Context7 did not resolve), websearch for canonical documentation (prefer `llms.txt`, developer docs, official API references over tutorials or blog posts). Collect 3–5 candidate URLs before reading any.
4. **Read sources in parallel.** Spawn one subagent per source URL. Each subagent fetches the page, extracts relevant content, identifies links worth deepening, and returns a structured summary (content by topic area + links to follow). Subagents do not write files.
5. **Deepen.** For each subagent that returned links worth following, spawn child subagents per branch. Continue until content becomes repetitive or out of scope. Cap at ~10 additional pages total across all branches.
6. **Consolidate.** Merge all subagent summaries (Context7 and web) by topic area. Identify which default topics have sufficient content and which custom topics emerged.
7. **Write topic files.** For each topic with content, write `<output-path>/<topic>.md` using the format in `references/file-format.md`. Orchestrator writes all files — never delegate file writing to a subagent.
8. **Write `sources.md`.** Write `<output-path>/sources.md` mapping each source slug to its URL (use `context7:<library-id>` as the URL for Context7 sources), description, and list of topic files it contributed to. Include sources that yielded no content, marked `no content extracted`.
## Output format
- `<output-path>/<topic>.md` per topic with content — formatted per `references/file-format.md`
- `<output-path>/sources.md` — always produced; maps slug → URL, description, contributing files
</steps>
<checks>
## Failure handling
- Output path not provided — stop and ask; do not infer or default
- No sources found after websearch — report what was searched, ask user to provide starting URLs
- Subagent returns no usable content — skip that source, log in `sources.md` as `no content extracted`
- All topic files would be empty — stop, report what was searched, do not write any files
## Self-check
- [ ] Codebase scanned before any websearch was performed
- [ ] Output path was explicitly provided — not inferred
- [ ] `references/topics.md` and `references/file-format.md` read at step 1
- [ ] Context7 resolution attempted before websearch when topic is a library/framework/API
- [ ] Context7 calls made only at orchestrator step 2 — no subagent called `resolve-library-id` or `query-docs`
- [ ] Context7 sources recorded in `sources.md` with `context7:<library-id>` as URL
- [ ] No topic file written without content
- [ ] `sources.md` written with all sources read (including those with no content extracted)
- [ ] All file writes performed by the orchestrator, not subagents
- [ ] Each topic file has `topic` and `source_keys` frontmatter fields
- [ ] All source keys in topic files have a matching entry in `sources.md`
- [ ] No files written outside the provided output path
</checks>

View File

@@ -0,0 +1,39 @@
# Reference file format
Every topic file follows this structure.
## Frontmatter
```yaml
---
topic: <topic-slug> # matches the filename without .md (e.g. "api-reference")
source_keys: # kebab-case slugs of sources that contributed; must match sources.md entries
- <slug>
- <slug>
---
```
## Body
Plain prose organized into markdown sections (`##`, `###`). Extract the content most relevant to skill authoring or implementation — not a verbatim copy of the source. Focus on:
- Decisions that affect how to call the API or tool
- Options, flags, or parameters with non-obvious behavior
- Constraints, rate limits, or gotchas
- Canonical patterns the skill should follow
No inline URLs in the body — all source traceability lives in `sources.md` via `source_keys`.
## sources.md format
```markdown
# Sources
## <slug>
- **URL:** <full URL>
- **Description:** <one-line summary of what this source covers>
- **Contributing files:** <comma-separated list of topic files this source contributed to>
- **Status:** `extracted` | `no content extracted`
```
Use one `##` section per source. Slugs are kebab-case derived from the domain or page title (e.g. `stripe-api-docs`, `openai-python-sdk-readme`). For Context7 sources, use the slug `context7-<library-slug>` (e.g. `context7-vercel-next-js`) and set **URL** to `context7:<library-id>` (e.g. `context7:/vercel/next.js`).

View File

@@ -0,0 +1,17 @@
# Default topic list
Create one file per topic when relevant content is found. Skip topics with no content. Add custom topics when content warrants it.
| File | Covers |
|---|---|
| `overview.md` | What it is, key concepts, mental model, architecture summary |
| `installation.md` | Setup, dependencies, prerequisites, version requirements |
| `configuration.md` | Config files, options, environment variables, defaults |
| `cli-reference.md` | Commands, subcommands, flags, exit codes |
| `api-reference.md` | Endpoints, SDK methods, types, request/response shapes |
| `examples.md` | Common usage patterns, recipes, quickstart walkthroughs |
| `troubleshooting.md` | Known issues, error codes, gotchas, workarounds |
## Custom topics
Create additional topic files when content doesn't fit the defaults. Examples: `webhooks.md`, `rate-limits.md`, `authentication.md`, `migrations.md`, `security.md`. Use kebab-case filenames.

View File

@@ -0,0 +1,109 @@
---
name: tdd
description: Test-driven development with red-green-refactor loop. Use when user wants to build features or fix bugs using TDD, mentions "red-green-refactor", wants integration tests, or asks for test-first development.
---
# Test-Driven Development
## Philosophy
**Core principle**: Tests should verify behavior through public interfaces, not implementation details. Code can change entirely; tests shouldn't.
**Good tests** are integration-style: they exercise real code paths through public APIs. They describe _what_ the system does, not _how_ it does it. A good test reads like a specification - "user can checkout with valid cart" tells you exactly what capability exists. These tests survive refactors because they don't care about internal structure.
**Bad tests** are coupled to implementation. They mock internal collaborators, test private methods, or verify through external means (like querying a database directly instead of using the interface). The warning sign: your test breaks when you refactor, but behavior hasn't changed. If you rename an internal function and tests fail, those tests were testing implementation, not behavior.
See [tests.md](tests.md) for examples and [mocking.md](mocking.md) for mocking guidelines.
## Anti-Pattern: Horizontal Slices
**DO NOT write all tests first, then all implementation.** This is "horizontal slicing" - treating RED as "write all tests" and GREEN as "write all code."
This produces **crap tests**:
- Tests written in bulk test _imagined_ behavior, not _actual_ behavior
- You end up testing the _shape_ of things (data structures, function signatures) rather than user-facing behavior
- Tests become insensitive to real changes - they pass when behavior breaks, fail when behavior is fine
- You outrun your headlights, committing to test structure before understanding the implementation
**Correct approach**: Vertical slices via tracer bullets. One test → one implementation → repeat. Each test responds to what you learned from the previous cycle. Because you just wrote the code, you know exactly what behavior matters and how to verify it.
```
WRONG (horizontal):
RED: test1, test2, test3, test4, test5
GREEN: impl1, impl2, impl3, impl4, impl5
RIGHT (vertical):
RED→GREEN: test1→impl1
RED→GREEN: test2→impl2
RED→GREEN: test3→impl3
...
```
## Workflow
### 1. Planning
When exploring the codebase, use the project's domain glossary so that test names and interface vocabulary match the project's language, and respect ADRs in the area you're touching.
Before writing any code:
- [ ] Confirm with user what interface changes are needed
- [ ] Confirm with user which behaviors to test (prioritize)
- [ ] Identify opportunities for [deep modules](deep-modules.md) (small interface, deep implementation)
- [ ] Design interfaces for [testability](interface-design.md)
- [ ] List the behaviors to test (not implementation steps)
- [ ] Get user approval on the plan
Ask: "What should the public interface look like? Which behaviors are most important to test?"
**You can't test everything.** Confirm with the user exactly which behaviors matter most. Focus testing effort on critical paths and complex logic, not every possible edge case.
### 2. Tracer Bullet
Write ONE test that confirms ONE thing about the system:
```
RED: Write test for first behavior → test fails
GREEN: Write minimal code to pass → test passes
```
This is your tracer bullet - proves the path works end-to-end.
### 3. Incremental Loop
For each remaining behavior:
```
RED: Write next test → fails
GREEN: Minimal code to pass → passes
```
Rules:
- One test at a time
- Only enough code to pass current test
- Don't anticipate future tests
- Keep tests focused on observable behavior
### 4. Refactor
After all tests pass, look for [refactor candidates](refactoring.md):
- [ ] Extract duplication
- [ ] Deepen modules (move complexity behind simple interfaces)
- [ ] Apply SOLID principles where natural
- [ ] Consider what new code reveals about existing code
- [ ] Run tests after each refactor step
**Never refactor while RED.** Get to GREEN first.
## Checklist Per Cycle
```
[ ] Test describes behavior, not implementation
[ ] Test uses public interface only
[ ] Test would survive internal refactor
[ ] Code is minimal for this test
[ ] No speculative features added
```

View File

@@ -0,0 +1,33 @@
# Deep Modules
From "A Philosophy of Software Design":
**Deep module** = small interface + lots of implementation
```
┌─────────────────────┐
│ Small Interface │ ← Few methods, simple params
├─────────────────────┤
│ │
│ │
│ Deep Implementation│ ← Complex logic hidden
│ │
│ │
└─────────────────────┘
```
**Shallow module** = large interface + little implementation (avoid)
```
┌─────────────────────────────────┐
│ Large Interface │ ← Many methods, complex params
├─────────────────────────────────┤
│ Thin Implementation │ ← Just passes through
└─────────────────────────────────┘
```
When designing interfaces, ask:
- Can I reduce the number of methods?
- Can I simplify the parameters?
- Can I hide more complexity inside?

View File

@@ -0,0 +1,31 @@
# Interface Design for Testability
Good interfaces make testing natural:
1. **Accept dependencies, don't create them**
```typescript
// Testable
function processOrder(order, paymentGateway) {}
// Hard to test
function processOrder(order) {
const gateway = new StripeGateway();
}
```
2. **Return results, don't produce side effects**
```typescript
// Testable
function calculateDiscount(cart): Discount {}
// Hard to test
function applyDiscount(cart): void {
cart.total -= discount;
}
```
3. **Small surface area**
- Fewer methods = fewer tests needed
- Fewer params = simpler test setup

View File

@@ -0,0 +1,59 @@
# When to Mock
Mock at **system boundaries** only:
- External APIs (payment, email, etc.)
- Databases (sometimes - prefer test DB)
- Time/randomness
- File system (sometimes)
Don't mock:
- Your own classes/modules
- Internal collaborators
- Anything you control
## Designing for Mockability
At system boundaries, design interfaces that are easy to mock:
**1. Use dependency injection**
Pass external dependencies in rather than creating them internally:
```typescript
// Easy to mock
function processPayment(order, paymentClient) {
return paymentClient.charge(order.total);
}
// Hard to mock
function processPayment(order) {
const client = new StripeClient(process.env.STRIPE_KEY);
return client.charge(order.total);
}
```
**2. Prefer SDK-style interfaces over generic fetchers**
Create specific functions for each external operation instead of one generic function with conditional logic:
```typescript
// GOOD: Each function is independently mockable
const api = {
getUser: (id) => fetch(`/users/${id}`),
getOrders: (userId) => fetch(`/users/${userId}/orders`),
createOrder: (data) => fetch('/orders', { method: 'POST', body: data }),
};
// BAD: Mocking requires conditional logic inside the mock
const api = {
fetch: (endpoint, options) => fetch(endpoint, options),
};
```
The SDK approach means:
- Each mock returns one specific shape
- No conditional logic in test setup
- Easier to see which endpoints a test exercises
- Type safety per endpoint

View File

@@ -0,0 +1,10 @@
# Refactor Candidates
After TDD cycle, look for:
- **Duplication** → Extract function/class
- **Long methods** → Break into private helpers (keep tests on public interface)
- **Shallow modules** → Combine or deepen
- **Feature envy** → Move logic to where data lives
- **Primitive obsession** → Introduce value objects
- **Existing code** the new code reveals as problematic

View File

@@ -0,0 +1,61 @@
# Good and Bad Tests
## Good Tests
**Integration-style**: Test through real interfaces, not mocks of internal parts.
```typescript
// GOOD: Tests observable behavior
test("user can checkout with valid cart", async () => {
const cart = createCart();
cart.add(product);
const result = await checkout(cart, paymentMethod);
expect(result.status).toBe("confirmed");
});
```
Characteristics:
- Tests behavior users/callers care about
- Uses public API only
- Survives internal refactors
- Describes WHAT, not HOW
- One logical assertion per test
## Bad Tests
**Implementation-detail tests**: Coupled to internal structure.
```typescript
// BAD: Tests implementation details
test("checkout calls paymentService.process", async () => {
const mockPayment = jest.mock(paymentService);
await checkout(cart, payment);
expect(mockPayment.process).toHaveBeenCalledWith(cart.total);
});
```
Red flags:
- Mocking internal collaborators
- Testing private methods
- Asserting on call counts/order
- Test breaks when refactoring without behavior change
- Test name describes HOW not WHAT
- Verifying through external means instead of interface
```typescript
// BAD: Bypasses interface to verify
test("createUser saves to database", async () => {
await createUser({ name: "Alice" });
const row = await db.query("SELECT * FROM users WHERE name = ?", ["Alice"]);
expect(row).toBeDefined();
});
// GOOD: Verifies through interface
test("createUser makes user retrievable", async () => {
const user = await createUser({ name: "Alice" });
const retrieved = await getUser(user.id);
expect(retrieved.name).toBe("Alice");
});
```

View File

@@ -0,0 +1,168 @@
# Writing Agent Briefs
An agent brief is a structured comment posted on a GitHub issue when it moves to `ready-for-agent`. It is the authoritative specification that an AFK agent will work from. The original issue body and discussion are context — the agent brief is the contract.
## Principles
### Durability over precision
The issue may sit in `ready-for-agent` for days or weeks. The codebase will change in the meantime. Write the brief so it stays useful even as files are renamed, moved, or refactored.
- **Do** describe interfaces, types, and behavioral contracts
- **Do** name specific types, function signatures, or config shapes that the agent should look for or modify
- **Don't** reference file paths — they go stale
- **Don't** reference line numbers
- **Don't** assume the current implementation structure will remain the same
### Behavioral, not procedural
Describe **what** the system should do, not **how** to implement it. The agent will explore the codebase fresh and make its own implementation decisions.
- **Good:** "The `SkillConfig` type should accept an optional `schedule` field of type `CronExpression`"
- **Bad:** "Open src/types/skill.ts and add a schedule field on line 42"
- **Good:** "When a user runs `/triage` with no arguments, they should see a summary of issues needing attention"
- **Bad:** "Add a switch statement in the main handler function"
### Complete acceptance criteria
The agent needs to know when it's done. Every agent brief must have concrete, testable acceptance criteria. Each criterion should be independently verifiable.
- **Good:** "Running `gh issue list --label needs-triage` returns issues that have been through initial classification"
- **Bad:** "Triage should work correctly"
### Explicit scope boundaries
State what is out of scope. This prevents the agent from gold-plating or making assumptions about adjacent features.
## Template
```markdown
## Agent Brief
**Category:** bug / enhancement
**Summary:** one-line description of what needs to happen
**Current behavior:**
Describe what happens now. For bugs, this is the broken behavior.
For enhancements, this is the status quo the feature builds on.
**Desired behavior:**
Describe what should happen after the agent's work is complete.
Be specific about edge cases and error conditions.
**Key interfaces:**
- `TypeName` — what needs to change and why
- `functionName()` return type — what it currently returns vs what it should return
- Config shape — any new configuration options needed
**Acceptance criteria:**
- [ ] Specific, testable criterion 1
- [ ] Specific, testable criterion 2
- [ ] Specific, testable criterion 3
**Out of scope:**
- Thing that should NOT be changed or addressed in this issue
- Adjacent feature that might seem related but is separate
```
## Examples
### Good agent brief (bug)
```markdown
## Agent Brief
**Category:** bug
**Summary:** Skill description truncation drops mid-word, producing broken output
**Current behavior:**
When a skill description exceeds 1024 characters, it is truncated at exactly
1024 characters regardless of word boundaries. This produces descriptions
that end mid-word (e.g. "Use when the user wants to confi").
**Desired behavior:**
Truncation should break at the last word boundary before 1024 characters
and append "..." to indicate truncation.
**Key interfaces:**
- The `SkillMetadata` type's `description` field — no type change needed,
but the validation/processing logic that populates it needs to respect
word boundaries
- Any function that reads SKILL.md frontmatter and extracts the description
**Acceptance criteria:**
- [ ] Descriptions under 1024 chars are unchanged
- [ ] Descriptions over 1024 chars are truncated at the last word boundary
before 1024 chars
- [ ] Truncated descriptions end with "..."
- [ ] The total length including "..." does not exceed 1024 chars
**Out of scope:**
- Changing the 1024 char limit itself
- Multi-line description support
```
### Good agent brief (enhancement)
```markdown
## Agent Brief
**Category:** enhancement
**Summary:** Add `.out-of-scope/` directory support for tracking rejected feature requests
**Current behavior:**
When a feature request is rejected, the issue is closed with a `wontfix` label
and a comment. There is no persistent record of the decision or reasoning.
Future similar requests require the maintainer to recall or search for the
prior discussion.
**Desired behavior:**
Rejected feature requests should be documented in `.out-of-scope/<concept>.md`
files that capture the decision, reasoning, and links to all issues that
requested the feature. When triaging new issues, these files should be
checked for matches.
**Key interfaces:**
- Markdown file format in `.out-of-scope/` — each file should have a
`# Concept Name` heading, a `**Decision:**` line, a `**Reason:**` line,
and a `**Prior requests:**` list with issue links
- The triage workflow should read all `.out-of-scope/*.md` files early
and match incoming issues against them by concept similarity
**Acceptance criteria:**
- [ ] Closing a feature as wontfix creates/updates a file in `.out-of-scope/`
- [ ] The file includes the decision, reasoning, and link to the closed issue
- [ ] If a matching `.out-of-scope/` file already exists, the new issue is
appended to its "Prior requests" list rather than creating a duplicate
- [ ] During triage, existing `.out-of-scope/` files are checked and surfaced
when a new issue matches a prior rejection
**Out of scope:**
- Automated matching (human confirms the match)
- Reopening previously rejected features
- Bug reports (only enhancement rejections go to `.out-of-scope/`)
```
### Bad agent brief
```markdown
## Agent Brief
**Summary:** Fix the triage bug
**What to do:**
The triage thing is broken. Look at the main file and fix it.
The function around line 150 has the issue.
**Files to change:**
- src/triage/handler.ts (line 150)
- src/types.ts (line 42)
```
This is bad because:
- No category
- Vague description ("the triage thing is broken")
- References file paths and line numbers that will go stale
- No acceptance criteria
- No scope boundaries
- No description of current vs desired behavior

View File

@@ -0,0 +1,101 @@
# Out-of-Scope Knowledge Base
The `.out-of-scope/` directory in a repo stores persistent records of rejected feature requests. It serves two purposes:
1. **Institutional memory** — why a feature was rejected, so the reasoning isn't lost when the issue is closed
2. **Deduplication** — when a new issue comes in that matches a prior rejection, the skill can surface the previous decision instead of re-litigating it
## Directory structure
```
.out-of-scope/
├── dark-mode.md
├── plugin-system.md
└── graphql-api.md
```
One file per **concept**, not per issue. Multiple issues requesting the same thing are grouped under one file.
## File format
The file should be written in a relaxed, readable style — more like a short design document than a database entry. Use paragraphs, code samples, and examples to make the reasoning clear and useful to someone encountering it for the first time.
```markdown
# Dark Mode
This project does not support dark mode or user-facing theming.
## Why this is out of scope
The rendering pipeline assumes a single color palette defined in
`ThemeConfig`. Supporting multiple themes would require:
- A theme context provider wrapping the entire component tree
- Per-component theme-aware style resolution
- A persistence layer for user theme preferences
This is a significant architectural change that doesn't align with the
project's focus on content authoring. Theming is a concern for downstream
consumers who embed or redistribute the output.
```ts
// The current ThemeConfig interface is not designed for runtime switching:
interface ThemeConfig {
colors: ColorPalette; // single palette, resolved at build time
fonts: FontStack;
}
```
## Prior requests
- #42 — "Add dark mode support"
- #87 — "Night theme for accessibility"
- #134 — "Dark theme option"
```
### Naming the file
Use a short, descriptive kebab-case name for the concept: `dark-mode.md`, `plugin-system.md`, `graphql-api.md`. The name should be recognizable enough that someone browsing the directory understands what was rejected without opening the file.
### Writing the reason
The reason should be substantive — not "we don't want this" but why. Good reasons reference:
- Project scope or philosophy ("This project focuses on X; theming is a downstream concern")
- Technical constraints ("Supporting this would require Y, which conflicts with our Z architecture")
- Strategic decisions ("We chose to use A instead of B because...")
The reason should be durable. Avoid referencing temporary circumstances ("we're too busy right now") — those aren't real rejections, they're deferrals.
## When to check `.out-of-scope/`
During triage (Step 1: Gather context), read all files in `.out-of-scope/`. When evaluating a new issue:
- Check if the request matches an existing out-of-scope concept
- Matching is by concept similarity, not keyword — "night theme" matches `dark-mode.md`
- If there's a match, surface it to the maintainer: "This is similar to `.out-of-scope/dark-mode.md` — we rejected this before because [reason]. Do you still feel the same way?"
The maintainer may:
- **Confirm** — the new issue gets added to the existing file's "Prior requests" list, then closed
- **Reconsider** — the out-of-scope file gets deleted or updated, and the issue proceeds through normal triage
- **Disagree** — the issues are related but distinct, proceed with normal triage
## When to write to `.out-of-scope/`
Only when an **enhancement** (not a bug) is rejected as `wontfix`. The flow:
1. Maintainer decides a feature request is out of scope
2. Check if a matching `.out-of-scope/` file already exists
3. If yes: append the new issue to the "Prior requests" list
4. If no: create a new file with the concept name, decision, reason, and first prior request
5. Post a comment on the issue explaining the decision and mentioning the `.out-of-scope/` file
6. Close the issue with the `wontfix` label
## Updating or removing out-of-scope files
If the maintainer changes their mind about a previously rejected concept:
- Delete the `.out-of-scope/` file
- The skill does not need to reopen old issues — they're historical records
- The new issue that triggered the reconsideration proceeds through normal triage

View File

@@ -0,0 +1,103 @@
---
name: triage
description: Triage issues through a state machine driven by triage roles. Use when user wants to create an issue, triage issues, review incoming bugs or feature requests, prepare issues for an AFK agent, or manage issue workflow.
---
# Triage
Move issues on the project issue tracker through a small state machine of triage roles.
Every comment or issue posted to the issue tracker during triage **must** start with this disclaimer:
```
> *This was generated by AI during triage.*
```
## Reference docs
- [AGENT-BRIEF.md](AGENT-BRIEF.md) — how to write durable agent briefs
- [OUT-OF-SCOPE.md](OUT-OF-SCOPE.md) — how the `.out-of-scope/` knowledge base works
## Roles
Two **category** roles:
- `bug` — something is broken
- `enhancement` — new feature or improvement
Five **state** roles:
- `needs-triage` — maintainer needs to evaluate
- `needs-info` — waiting on reporter for more information
- `ready-for-agent` — fully specified, ready for an AFK agent
- `ready-for-human` — needs human implementation
- `wontfix` — will not be actioned
Every triaged issue should carry exactly one category role and one state role. If state roles conflict, flag it and ask the maintainer before doing anything else.
These are canonical role names — the actual label strings used in the issue tracker may differ. The mapping should have been provided to you - run `/setup-matt-pocock-skills` if not.
State transitions: an unlabeled issue normally goes to `needs-triage` first; from there it moves to `needs-info`, `ready-for-agent`, `ready-for-human`, or `wontfix`. `needs-info` returns to `needs-triage` once the reporter replies. The maintainer can override at any time — flag transitions that look unusual and ask before proceeding.
## Invocation
The maintainer invokes `/triage` and describes what they want in natural language. Interpret the request and act. Examples:
- "Show me anything that needs my attention"
- "Let's look at #42"
- "Move #42 to ready-for-agent"
- "What's ready for agents to pick up?"
## Show what needs attention
Query the issue tracker and present three buckets, oldest first:
1. **Unlabeled** — never triaged.
2. **`needs-triage`** — evaluation in progress.
3. **`needs-info` with reporter activity since the last triage notes** — needs re-evaluation.
Show counts and a one-line summary per issue. Let the maintainer pick.
## Triage a specific issue
1. **Gather context.** Read the full issue (body, comments, labels, reporter, dates). Parse any prior triage notes so you don't re-ask resolved questions. Explore the codebase using the project's domain glossary, respecting ADRs in the area. Read `.out-of-scope/*.md` and surface any prior rejection that resembles this issue.
2. **Recommend.** Tell the maintainer your category and state recommendation with reasoning, plus a brief codebase summary relevant to the issue. Wait for direction.
3. **Reproduce (bugs only).** Before any grilling, attempt reproduction: read the reporter's steps, trace the relevant code, run tests or commands. Report what happened — successful repro with code path, failed repro, or insufficient detail (a strong `needs-info` signal). A confirmed repro makes a much stronger agent brief.
4. **Grill (if needed).** If the issue needs fleshing out, run a `/grill-with-docs` session.
5. **Apply the outcome:**
- `ready-for-agent` — post an agent brief comment ([AGENT-BRIEF.md](AGENT-BRIEF.md)).
- `ready-for-human` — same structure as an agent brief, but note why it can't be delegated (judgment calls, external access, design decisions, manual testing).
- `needs-info` — post triage notes (template below).
- `wontfix` (bug) — polite explanation, then close.
- `wontfix` (enhancement) — write to `.out-of-scope/`, link to it from a comment, then close ([OUT-OF-SCOPE.md](OUT-OF-SCOPE.md)).
- `needs-triage` — apply the role. Optional comment if there's partial progress.
## Quick state override
If the maintainer says "move #42 to ready-for-agent", trust them and apply the role directly. Confirm what you're about to do (role changes, comment, close), then act. Skip grilling. If moving to `ready-for-agent` without a grilling session, ask whether they want to write an agent brief.
## Needs-info template
```markdown
## Triage Notes
**What we've established so far:**
- point 1
- point 2
**What we still need from you (@reporter):**
- question 1
- question 2
```
Capture everything resolved during grilling under "established so far" so the work isn't lost. Questions must be specific and actionable, not "please provide more info".
## Resuming a previous session
If prior triage notes exist on the issue, read them, check whether the reporter has answered any outstanding questions, and present an updated picture before continuing. Don't re-ask resolved questions.

View File

@@ -0,0 +1,103 @@
---
name: write-docs
description: Write documentation for X, document this module, create docs for this feature. Use when the user wants to produce or update technical documentation derived from code, spec, or existing artifacts. Do NOT use when the user wants a PRD, ADR, decision doc, or skill file — those have dedicated skills.
version: "1.0"
updated: 2026-05-17
when: invoked by explicit trigger ("write docs for X", "document this module", "create docs for this feature") or implicit request to produce technical documentation from code or spec
metadata:
category: implement
source:
- repo: anthropics/skills
commit: f458cee31a7577a47ba0c9a101976fa599385174
files:
- skills/doc-coauthoring/SKILL.md # Reader Testing stage, surgical-edit constraint, gap-check step
updated: 2026-05-17
- repo: mattpocock/skills
commit: e74f0061bb67222181640effa98c675bdb2fdaa7
files:
- skills/productivity/write-a-skill/SKILL.md # trigger pattern, review checklist items
updated: 2026-05-17
- repo: bmad-code-org/BMAD-METHOD
commit: 71136bc6af77cbf507d3768494311d5b6ca95cc5
files:
- src/core-skills/bmad-advanced-elicitation/SKILL.md # confirmation gate before applying changes
updated: 2026-05-17
---
## Role
You are a technical writer that produces documentation by reading code and spec — you derive every claim from a source file or explicit user input and never invent behaviour.
## When to use / When not to use
**Use when:**
- User wants to document a module, class, function, feature, CLI flag, API endpoint, config file, or README section
- User says "write docs for X", "document this", "create docs for this feature", "write a README for this"
**Do not use when:**
- User wants a PRD, decision doc, or architecture proposal → `to-prd` or `grill-me`
- User wants to document a skill file (skill files are self-describing)
- User wants marketing or blog copy
- Documentation requires tacit organisational knowledge that cannot be read from code or spec
## Required inputs
- Specific file(s) or module(s) to document, or enough description to propose candidates
- Target audience: developer / user / contributor / internal
- Documentation type: reference, guide, README section, inline comment, changelog entry
## Constraints
- Every claim must be traceable to a source file line, spec section, or explicit user statement — never invent behaviour
- User must approve specific files before the skill reads them; skill may propose candidates but waits for approval
- Stage skipping is allowed only with an explicit user request and a one-sentence logged reason
- Show the full revised section before each confirmation gate — never gate on output the user has not seen
- Never reprint the whole document; all edits are surgical
- Produce a one-line delta summary after each refinement round
- Reader Testing sub-agent receives only the finished doc and the question list — no source files
- Write summary and overview sections last, after all detail sections are stable
## Process
1. **Identify scope.** User names specific files or sections. If not provided, propose candidates based on the description — wait for explicit approval before reading.
2. **Read and extract.** Read approved files. Extract: public API surface, described behaviour, visible constraints, non-obvious invariants. Note what the code does NOT explain (caller intent, error handling rationale, non-obvious side effects).
3. **Gap check.** Present extracted behaviour to the user. Ask them to fill only the gaps — what the code does not explain. Log any explicitly deferred gaps. If the user requests to skip this step, log the reason and proceed.
4. **Draft section by section.** For each section: state the proposed content and its source (code line / spec section / user input). Show; confirm before moving to the next section.
5. **Confirmation gate.** Before finalising any section, show the full revised section. Wait for explicit confirmation or correction — never apply changes the user has not seen.
6. **Delta summary.** After each round of revisions: "Round N: changed [sections], added [X], removed [Y]."
7. **Reader Testing.** Predict 5–10 questions a target reader would ask. Spawn a scoped sub-agent that receives only the finished doc and the questions — no source files. Report its answers. If any answers fail, loop back to step 4.
8. **Finalise.** Write summary and overview sections last. Prompt the user to review the complete document before committing.
## Output format
- Markdown artifact with section headers; produced one section at a time — never as a single large dump
- Delta summary after each refinement round: "Round N: [what changed]"
- Reader Testing report: numbered question list with sub-agent answers
- Final doc at the user-specified or conventionally appropriate path
## Failure handling
- Files not named and description too vague to propose candidates → ask for specific names before reading
- Stage skipped without a logged reason → flag and require the one-sentence log before continuing
- Code behaviour is undocumentable (internal implementation detail, no public spec) → note as out-of-scope in the doc; do not invent an explanation
- Reader Testing sub-agent fails on multiple questions → surface the failures, return to step 4; do not mark complete
- Requested output is a PRD, decision doc, or architecture proposal → redirect to `to-prd`, `grill-me`, or `grill-with-docs`
## Self-check
- [ ] All claims traceable to a source file or explicit user input
- [ ] No invented behaviour — unverifiable claims removed
- [ ] User approved specific files before reading
- [ ] Any stage skips logged with reason
- [ ] Full revised section shown before each confirmation gate
- [ ] Delta summary produced after each refinement round
- [ ] Reader Testing completed with scoped sub-agent (doc + questions only)
- [ ] Summary/overview written last
- [ ] User prompted to review before committing

View File

@@ -0,0 +1,7 @@
---
name: zoom-out
description: Tell the agent to zoom out and give broader context or a higher-level perspective. Use when you're unfamiliar with a section of code or need to understand how it fits into the bigger picture.
disable-model-invocation: true
---
I don't know this area of code well. Go up a layer of abstraction. Give me a map of all the relevant modules and callers, using the project's domain glossary vocabulary.

View File

@@ -1,12 +1,30 @@
{ {
"name": "bin",
"version": "1.1.1",
"description": "A place for things to be binned",
"author": { "author": {
"name": "Defame1297", "name": "Defame1297",
"email": "defame1297@rkdr.net",
"url": "https://git.dev.rkdr.net/Defame1297/" "url": "https://git.dev.rkdr.net/Defame1297/"
}, },
"description": "A place for things to be binned",
"displayName": "bin",
"keywords": [],
"license": "MIT", "license": "MIT",
"name": "bin", "homepage": "https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/bin",
"version": "1.1.1" "repository": "https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/bin",
"keywords": [
"utility",
"diagnostics",
"prototyping",
"tdd",
"research"
],
"mcpServers": {
"obsidian": {
"args": [
"@bitbonsai/mcpvault@latest",
"docs/"
],
"command": "npx",
"type": "stdio"
}
}
} }

30
plugins/bin/.github/plugin/plugin.json vendored Normal file
View File

@@ -0,0 +1,30 @@
{
"name": "bin",
"version": "1.1.1",
"description": "A place for things to be binned",
"author": {
"name": "Defame1297",
"email": "defame1297@rkdr.net",
"url": "https://git.dev.rkdr.net/Defame1297/"
},
"license": "MIT",
"homepage": "https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/bin",
"repository": "https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/bin",
"keywords": [
"utility",
"diagnostics",
"prototyping",
"tdd",
"research"
],
"mcpServers": {
"obsidian": {
"args": [
"@bitbonsai/mcpvault@latest",
"docs/"
],
"command": "npx",
"type": "stdio"
}
}
}

35
plugins/bin/apm.yml Normal file
View File

@@ -0,0 +1,35 @@
name: bin
version: 1.1.1
description: A place for things to be binned
author:
name: Defame1297
email: defame1297@rkdr.net
url: https://git.dev.rkdr.net/Defame1297/
license: MIT
homepage: https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/bin
repository: https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/bin
keywords:
- utility
- diagnostics
- prototyping
- tdd
- research
# Constrains what .apm/ may contain: instructions, skill, hybrid, or prompts
type: skill
# Which agent platforms to deploy to.
# Resolution order: --target flag > this field > auto-detect from filesystem.
# Accepted values: agent-skills, antigravity, claude, codex, copilot, cursor, gemini, grok-build, kiro, opencode, windsurf
targets:
- claude
- copilot
- codex
dependencies:
apm: []
mcp: []
includes: auto
devDependencies:
apm: []
scripts: {}

View File

@@ -1,15 +0,0 @@
{
"author": {
"email": "defame1297@rkdr.net",
"name": "Defame1297"
},
"description": "A place for things to be binned",
"keywords": [],
"license": "MIT",
"mcpServers": ".mcp.json",
"name": "bin",
"skills": [
"skills/"
],
"version": "1.1.1"
}

View File

@@ -0,0 +1,30 @@
# agentsmd-audit
Audit a target repo's AGENTS.md file(s) for embedded secrets, structural completeness, and drift.
## What it does
Runs a single combined pass across every AGENTS.md file in a repo (root and any nested monorepo files): flags embedded secrets/credentials, checks structure against the agents.md common-sections checklist, and resolves referenced commands/paths against the actual repo to catch stale documentation. Outputs a compact findings report — findings only, grouped by dimension, each with Why and Fix. Never inspects provider-specific adapter files (CLAUDE.md, etc.) and never writes or fixes anything.
## Usage
```
/agentsmd-audit
```
Provide the path to the repo root to audit when invoking.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents |
| `scripts/validate-secrets.sh` | Scans AGENTS.md files for embedded secrets, API keys, tokens, connection strings |
| `scripts/validate-structure.sh` | Checks for empty/placeholder content, common-sections checklist, nested-vs-root duplication |
| `scripts/validate-drift.sh` | Resolves referenced npm/make commands and file paths against the repo |
| `references/sources.md` | Provenance record — sources that informed this skill and which files each contributed to |
| `scripts/README.md` | Directory documentation for `scripts/` |
| `tests/README.md` | Bats test dependency and run instructions |
| `tests/validate-secrets.bats` | Bats test suite for `scripts/validate-secrets.sh` |
| `tests/validate-structure.bats` | Bats test suite for `scripts/validate-structure.sh` |
| `tests/validate-drift.bats` | Bats test suite for `scripts/validate-drift.sh` |

View File

@@ -0,0 +1,68 @@
---
name: agentsmd-audit
description: >
Use when the user wants to review a repo's AGENTS.md file, says "audit this
AGENTS.md", "check my AGENTS.md", "is this AGENTS.md any good", or wants to
know if AGENTS.md is safe to commit — even if they don't use the word
"audit". Also invoke proactively after agentsmd-author creates or updates
AGENTS.md, or after a hand-edit made outside agentsmd-author. Audits a
target repo's AGENTS.md file(s) — root and any nested monorepo files — for
embedded secrets/credentials, structural completeness against the
agents.md common-sections checklist, and drift (referenced commands or
paths that no longer resolve against the repo). Produces a compact
findings report (findings only, no PASS noise) with Why and Fix per
finding. Do not use to audit CLAUDE.md, .cursor/rules, or other
provider-specific adapter files — that's provider-adapter-author's
self-contained concern. Do not use to fix or write AGENTS.md content — use
agentsmd-author instead.
allowed-tools: Bash Read
metadata:
category: docs
source_keys:
- agents-md-official
- context7-websites-agents-md
- context7-agentsmd-agents-md
- governance-secrets-hard-prohibition
version: "0.1.1"
---
## Gotchas
- Always run all three checks — this skill does a single combined pass, not staged/gated passes. Don't skip structure or drift checks just because a secrets FAIL was found.
- Never inspect or mention provider-specific adapter files (`CLAUDE.md`, `.cursor/rules/*.mdc`, `copilot-instructions.md`, etc.) — that's out of scope. If one exists and duplicates AGENTS.md content, that's `provider-adapter-author`'s concern, not this skill's.
- A missing common section (e.g. no "Security" heading) is informational, not a failure — not every repo needs every section from the checklist. Only flag a FAIL when the file is empty, entirely unfilled placeholder text, or contains a real embedded secret/stale reference.
- Gather findings internally; don't narrate PASS/FAIL per check as you go — surface them only in the final report.
## Step 1 — Run the validators
```bash
bash scripts/validate-secrets.sh <repo-root>
bash scripts/validate-structure.sh <repo-root>
bash scripts/validate-drift.sh <repo-root>
```
Each script walks the repo for every `AGENTS.md` file (root and nested, excluding `.git`, `node_modules`, `vendor`, and similar) and prints `FAIL`/`INFO`/`SUGGESTION` lines with `Why`/`Fix` (or `Note`) per finding. A nonzero exit means at least one FAIL was found in that dimension. If a script cannot execute (`python3` unavailable, Bash denied), fall back to manual review: scan for real-looking credentials, check common sections are present, and spot-check a few referenced commands/paths by hand.
## Step 2 — Report
Open with a coverage line:
```text
Checked: secrets · structure · drift
```
Then output only findings that were found, in this order within a repo: `### Secrets`, `### Structure`, `### Drift`. Omit a dimension heading entirely if it produced nothing — its absence confirms it passed. Report each finding verbatim as emitted by the scripts (they already carry file:line, Why/Fix or Note).
Close with a result block:
```text
## Result
PASS
PASS · P info
PASS (N suggestions) · P info
FAIL (N fails)
FAIL (N fails) · P info
```
INFO and SUGGESTION findings are observational — they never flip PASS to FAIL. Do not fix anything — this skill reports and proposes only. Point the user to `agentsmd-author` to apply fixes.

View File

@@ -0,0 +1,33 @@
# Sources
## agents-md-official
- **URL:** https://agents.md/
- **Description:** Official agents.md website — format spec, common-sections checklist, precedence rules (nearest-file-wins, no merge across files), monorepo nesting patterns
- **Research doc:** plugins/core/docs/research/docs/agentsmd/sources.md
- **Contributing files:** SKILL.md
- **Status:** `extracted`
## context7-websites-agents-md
- **URL:** context7:/websites/agents_md
- **Description:** Context7 index of the official agents.md website — overview, governance, cross-tool compatibility, configuration examples
- **Research doc:** plugins/core/docs/research/docs/agentsmd/sources.md
- **Contributing files:** SKILL.md
- **Status:** `extracted`
## context7-agentsmd-agents-md
- **URL:** context7:/agentsmd/agents.md
- **Description:** Context7 index of the agentsmd/agents.md repository — format spec, nested monorepo patterns, file structure examples
- **Research doc:** plugins/core/docs/research/docs/agentsmd/sources.md
- **Contributing files:** SKILL.md
- **Status:** `extracted`
## governance-secrets-hard-prohibition
- **URL:** (org convention — not a plugin research corpus entry)
- **Description:** Hard prohibition on placing secrets, API keys, tokens, or credentials in code, config, prompts, or any output. Grounds the secrets/credentials check in `scripts/validate-secrets.sh` and Step 1 of SKILL.md — AGENTS.md is committed content, so an embedded real secret is a hard-prohibition violation, not a style nit.
- **Research doc:** core/instructions/governance.md (org convention file, not a plugin research corpus entry; content is inlined here since plugins must be self-contained and this file may not exist wherever the plugin is installed)
- **Contributing files:** SKILL.md
- **Status:** `extracted`

View File

@@ -0,0 +1,11 @@
# scripts/
Deterministic validators this skill shells out to instead of relying on LLM judgment for mechanical checks.
| File | Purpose |
|------|---------|
| `validate-secrets.sh` | Scans every AGENTS.md file (root + nested) for embedded secrets, API keys, tokens, and connection strings |
| `validate-structure.sh` | Checks for empty/placeholder content, the common-sections checklist, and nested-vs-root duplication |
| `validate-drift.sh` | Resolves referenced npm/make commands and file paths against the actual repo state |
All three take a single `<repo-root>` argument, print `FAIL`/`INFO`/`SUGGESTION` findings to stdout, and exit non-zero only on FAIL.

View File

@@ -0,0 +1,137 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<EOF
Usage: validate-drift.sh <repo-root>
Check every AGENTS.md file in a repo (root and nested) for drift: package
manager scripts and file paths referenced in the text that no longer exist
in the repo. Catches the failure mode that matters most in practice — an
agent running a documented command that was renamed or deleted.
Arguments:
repo-root Path to the repository root to scan.
Exit codes:
0 No FAIL findings (INFO may still be printed, e.g. no package.json found)
1 One or more FAIL findings
EOF
}
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
usage
exit 0
fi
if [[ $# -lt 1 ]]; then
echo "Error: repo-root is required." >&2
echo "" >&2
usage >&2
exit 1
fi
python3 -u - "$1" <<'PYTHON'
import sys
import os
import re
import json
repo_root = os.path.abspath(sys.argv[1])
if not os.path.isdir(repo_root):
print(f"Error: '{repo_root}' is not a directory.", file=sys.stderr)
sys.exit(1)
EXCLUDE_DIRS = {".git", "node_modules", "vendor", ".venv", "venv", "dist", "build"}
def find_agents_md(root):
results = []
for dirpath, dirnames, filenames in os.walk(root):
dirnames[:] = [d for d in dirnames if d not in EXCLUDE_DIRS and not d.startswith(".")]
for fname in filenames:
if fname == "AGENTS.md":
results.append(os.path.join(dirpath, fname))
return sorted(results)
def load_package_scripts(root):
pkg_path = os.path.join(root, "package.json")
if not os.path.isfile(pkg_path):
return None
try:
with open(pkg_path, encoding="utf-8") as f:
data = json.load(f)
except (json.JSONDecodeError, OSError):
return None
return set(data.get("scripts", {}).keys())
def load_make_targets(root):
make_path = os.path.join(root, "Makefile")
if not os.path.isfile(make_path):
return None
with open(make_path, encoding="utf-8", errors="replace") as f:
content = f.read()
return set(re.findall(r'(?m)^([a-zA-Z0-9_-]+)\s*:(?!=)', content))
NPM_RUN_RE = re.compile(r'\b(?:npm|pnpm|yarn)\s+run\s+([a-zA-Z0-9:_-]+)')
MAKE_RE = re.compile(r'\bmake\s+([a-zA-Z0-9_-]+)')
# Backticked relative file paths, e.g. `scripts/bootstrap.sh`, `src/index.ts`.
# Requires a path separator and file extension to avoid matching bare commands/words.
PATH_RE = re.compile(r'`([A-Za-z0-9_.\-]+(?:/[A-Za-z0-9_.\-]+)+\.[A-Za-z0-9]+)`')
has_fail = False
package_scripts = load_package_scripts(repo_root)
make_targets = load_make_targets(repo_root)
for fpath in find_agents_md(repo_root):
rel = os.path.relpath(fpath, repo_root)
with open(fpath, encoding="utf-8", errors="replace") as f:
content = f.read()
for m in NPM_RUN_RE.finditer(content):
script_name = m.group(1)
if package_scripts is None:
print(f"INFO Cannot verify referenced script '{script_name}' — {rel}")
print(f" Note: AGENTS.md references an npm/pnpm/yarn script, but no package.json was found at the repo root to check it against.")
print()
elif script_name not in package_scripts:
has_fail = True
print(f"FAIL Referenced script '{script_name}' not found in package.json — {rel}")
print(f" Why: AGENTS.md tells agents to run '{script_name}', but package.json has no matching \"scripts\" entry — the command will fail.")
print(f" Fix: Update AGENTS.md to reference an existing script, or add '{script_name}' to package.json's scripts.")
print()
for m in MAKE_RE.finditer(content):
target_name = m.group(1)
if make_targets is None:
print(f"INFO Cannot verify referenced make target '{target_name}' — {rel}")
print(f" Note: AGENTS.md references a make target, but no Makefile was found at the repo root to check it against.")
print()
elif target_name not in make_targets:
has_fail = True
print(f"FAIL Referenced make target '{target_name}' not found in Makefile — {rel}")
print(f" Why: AGENTS.md tells agents to run 'make {target_name}', but the Makefile has no matching target — the command will fail.")
print(f" Fix: Update AGENTS.md to reference an existing target, or add '{target_name}' to the Makefile.")
print()
file_dir = os.path.dirname(fpath)
for m in PATH_RE.finditer(content):
candidate = m.group(1)
resolved = (
os.path.isfile(os.path.join(repo_root, candidate))
or os.path.isfile(os.path.join(file_dir, candidate))
or os.path.isdir(os.path.join(repo_root, candidate))
or os.path.isdir(os.path.join(file_dir, candidate))
)
if not resolved:
has_fail = True
print(f"FAIL Referenced path '{candidate}' does not exist — {rel}")
print(f" Why: AGENTS.md points agents to '{candidate}', but it isn't present in the repo (checked relative to repo root and to the AGENTS.md's own directory).")
print(f" Fix: Update AGENTS.md to reference the correct path, or restore/create '{candidate}'.")
print()
if has_fail:
sys.exit(1)
sys.exit(0)
PYTHON

View File

@@ -0,0 +1,120 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<EOF
Usage: validate-secrets.sh <repo-root>
Scan every AGENTS.md file in a repo (root and nested) for embedded secrets,
API keys, tokens, or connection strings. AGENTS.md is committed content —
real credentials in it are a hard-prohibition violation, not a style nit.
Placeholders (<your-key>, \$ENV_VAR, YOUR_TOKEN_HERE, example.com, etc.) are
not flagged.
Arguments:
repo-root Path to the repository root to scan.
Exit codes:
0 No findings
1 One or more FAIL findings
EOF
}
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
usage
exit 0
fi
if [[ $# -lt 1 ]]; then
echo "Error: repo-root is required." >&2
echo "" >&2
usage >&2
exit 1
fi
python3 -u - "$1" <<'PYTHON'
import sys
import os
import re
repo_root = os.path.abspath(sys.argv[1])
if not os.path.isdir(repo_root):
print(f"Error: '{repo_root}' is not a directory.", file=sys.stderr)
sys.exit(1)
EXCLUDE_DIRS = {".git", "node_modules", "vendor", ".venv", "venv", "dist", "build"}
def find_agents_md(root):
results = []
for dirpath, dirnames, filenames in os.walk(root):
dirnames[:] = [d for d in dirnames if d not in EXCLUDE_DIRS and not d.startswith(".")]
for fname in filenames:
if fname == "AGENTS.md":
results.append(os.path.join(dirpath, fname))
return sorted(results)
PLACEHOLDER_RE = re.compile(
r'(?i)(your[_-]|my[_-]|example|xxx+|placeholder|changeme|<[^>]+>|\$\{|\$[A-Z_][A-Z0-9_]*|\.\.\.|redacted)'
)
PATTERNS = [
("AWS access key ID", re.compile(r'AKIA[0-9A-Z]{16}')),
("Private key block", re.compile(r'-----BEGIN [A-Z ]*PRIVATE KEY-----')),
("GitHub token", re.compile(r'gh[pousr]_[A-Za-z0-9]{36,}')),
("Slack token", re.compile(r'xox[baprs]-[A-Za-z0-9-]{10,}')),
("GitLab token", re.compile(r'glpat-[A-Za-z0-9_-]{20,}')),
("Generic API-style secret token", re.compile(r'\bsk-[A-Za-z0-9]{20,}\b')),
(
"Credential-bearing connection string",
re.compile(r'[a-zA-Z][a-zA-Z0-9+.-]*://[^:@/\s]+:[^@/\s]+@[^\s\'"]+'),
),
(
"Assigned secret/password/token literal",
re.compile(
r'(?i)\b(api[_-]?key|secret|token|password|passwd|pwd|access[_-]?key)\b'
r'\s*[:=]\s*[\'"]?([A-Za-z0-9+/_.\-]{12,})[\'"]?'
),
),
]
findings = []
def emit_fail(desc, fpath, lineno, why, fix):
findings.append((desc, fpath, lineno, why, fix))
for fpath in find_agents_md(repo_root):
rel = os.path.relpath(fpath, repo_root)
with open(fpath, encoding="utf-8", errors="replace") as f:
lines = f.readlines()
for i, line in enumerate(lines, start=1):
if PLACEHOLDER_RE.search(line):
continue
for label, pattern in PATTERNS:
m = pattern.search(line)
if not m:
continue
# Re-check placeholder allowlist against just the matched value, in case
# the placeholder marker sits outside the regex's own match span.
value = m.group(0)
if PLACEHOLDER_RE.search(value):
continue
emit_fail(
f"Possible {label}",
f"{rel}:{i}",
i,
"AGENTS.md is committed content; this line matches a real-looking credential pattern rather than a placeholder.",
"Remove the embedded credential and replace it with an environment variable reference or placeholder (e.g. $API_KEY, <your-token>).",
)
break
if not findings:
sys.exit(0)
for desc, fpath, _lineno, why, fix in findings:
print(f"FAIL {desc} — {fpath}")
print(f" Why: {why}")
print(f" Fix: {fix}")
print()
sys.exit(1)
PYTHON

View File

@@ -0,0 +1,118 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<EOF
Usage: validate-structure.sh <repo-root>
Check every AGENTS.md file in a repo (root and nested) for structural
completeness against the agents.md spec's common-sections checklist
(setup/build, code style, testing, security, commit/PR conventions).
Missing individual sections are informational (not every repo needs every
section) — only an empty or entirely unfilled file is a hard failure.
Arguments:
repo-root Path to the repository root to scan.
Exit codes:
0 No FAIL findings (INFO/SUGGESTION may still be printed)
1 One or more FAIL findings
EOF
}
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
usage
exit 0
fi
if [[ $# -lt 1 ]]; then
echo "Error: repo-root is required." >&2
echo "" >&2
usage >&2
exit 1
fi
python3 -u - "$1" <<'PYTHON'
import sys
import os
import re
PLACEHOLDER_RE = re.compile(r'(?i)FILL IN:|TODO:\s*write|lorem ipsum')
COMMON_SECTIONS = [
("setup/build commands", re.compile(r'(?im)^#{1,3}\s*(setup|install|build|getting started)')),
("code style", re.compile(r'(?im)^#{1,3}\s*(code style|style guide|conventions)')),
("testing instructions", re.compile(r'(?im)^#{1,3}\s*(test|testing)')),
("security considerations", re.compile(r'(?im)^#{1,3}\s*security')),
("commit/PR conventions", re.compile(r'(?im)^#{1,3}\s*(commit|pr|pull request)')),
]
repo_root = os.path.abspath(sys.argv[1])
if not os.path.isdir(repo_root):
print(f"Error: '{repo_root}' is not a directory.", file=sys.stderr)
sys.exit(1)
EXCLUDE_DIRS = {".git", "node_modules", "vendor", ".venv", "venv", "dist", "build"}
def find_agents_md(root):
results = []
for dirpath, dirnames, filenames in os.walk(root):
dirnames[:] = [d for d in dirnames if d not in EXCLUDE_DIRS and not d.startswith(".")]
for fname in filenames:
if fname == "AGENTS.md":
results.append(os.path.join(dirpath, fname))
return sorted(results)
has_fail = False
file_contents = {} # rel path -> content, for the duplication pass below
for fpath in find_agents_md(repo_root):
rel = os.path.relpath(fpath, repo_root)
with open(fpath, encoding="utf-8", errors="replace") as f:
content = f.read()
file_contents[rel] = content
if not content.strip():
has_fail = True
print(f"FAIL AGENTS.md is empty — {rel}")
print(" Why: An empty file provides no instructions and gives agents nothing to act on.")
print(" Fix: Add at least a project overview and setup/test commands, per the agents.md common-sections checklist.")
print()
continue
if PLACEHOLDER_RE.search(content):
has_fail = True
print(f"FAIL Unfilled placeholder content — {rel}")
print(" Why: A 'FILL IN:' or template stub left in place means the file has no repo-specific instructions yet.")
print(" Fix: Replace the placeholder with real, repo-specific content.")
print()
continue
for label, pattern in COMMON_SECTIONS:
if not pattern.search(content):
print(f"INFO No {label} section — {rel}")
print(f" Note: The agents.md common-sections checklist includes {label}; not every repo needs every section, but confirm this omission is deliberate.")
print()
# --- Nested-vs-root duplication check ---
root_content = file_contents.get("AGENTS.md")
if root_content:
root_lines = {ln.strip() for ln in root_content.splitlines() if ln.strip()}
for rel, content in file_contents.items():
if rel == "AGENTS.md":
continue
nested_lines = [ln.strip() for ln in content.splitlines() if ln.strip()]
if not nested_lines:
continue
overlap = sum(1 for ln in nested_lines if ln in root_lines)
ratio = overlap / len(nested_lines)
if ratio >= 0.7:
print(f"SUGGESTION Nested AGENTS.md largely duplicates the root file — {rel}")
print(f" Why: {ratio:.0%} of this file's content lines already appear in the root AGENTS.md; per the spec's nearest-file-wins precedence, nested files don't inherit from the root, but they also shouldn't just restate it.")
print(f" Fix: Trim {rel} down to only what's specific to this package/directory.")
print()
if has_fail:
sys.exit(1)
sys.exit(0)
PYTHON

View File

@@ -18,7 +18,7 @@ git clone https://github.com/bats-core/bats-assert tests/test_helper/bats-assert
Run all tests for this skill (from the repo root): Run all tests for this skill (from the repo root):
```bash ```bash
bats plugins/core/skills/agentsmd-audit/tests/ bats plugins/core/.apm/skills/agentsmd-audit/tests/
``` ```
## Files ## Files

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bats #!/usr/bin/env bats
setup() { setup() {
REPO_ROOT="$(cd "$BATS_TEST_DIRNAME/../../../../../" && pwd)" REPO_ROOT="$(cd "$BATS_TEST_DIRNAME/../../../../../../" && pwd)"
load "$REPO_ROOT/tests/test_helper/bats-support/load" load "$REPO_ROOT/tests/test_helper/bats-support/load"
load "$REPO_ROOT/tests/test_helper/bats-assert/load" load "$REPO_ROOT/tests/test_helper/bats-assert/load"

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bats #!/usr/bin/env bats
setup() { setup() {
REPO_ROOT="$(cd "$BATS_TEST_DIRNAME/../../../../../" && pwd)" REPO_ROOT="$(cd "$BATS_TEST_DIRNAME/../../../../../../" && pwd)"
load "$REPO_ROOT/tests/test_helper/bats-support/load" load "$REPO_ROOT/tests/test_helper/bats-support/load"
load "$REPO_ROOT/tests/test_helper/bats-assert/load" load "$REPO_ROOT/tests/test_helper/bats-assert/load"

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bats #!/usr/bin/env bats
setup() { setup() {
REPO_ROOT="$(cd "$BATS_TEST_DIRNAME/../../../../../" && pwd)" REPO_ROOT="$(cd "$BATS_TEST_DIRNAME/../../../../../../" && pwd)"
load "$REPO_ROOT/tests/test_helper/bats-support/load" load "$REPO_ROOT/tests/test_helper/bats-support/load"
load "$REPO_ROOT/tests/test_helper/bats-assert/load" load "$REPO_ROOT/tests/test_helper/bats-assert/load"

View File

@@ -0,0 +1,27 @@
# agentsmd-author
Create or update a target repo's AGENTS.md file(s) by exploring the repo for real conventions.
## What it does
Explores a target repo (package manager scripts, Makefile/task runner, CI config, linter config, existing docs) and writes or updates `AGENTS.md` with only verified commands and conventions — never invented ones. Supports nested monorepo placement, following the agents.md standard's nearest-file-wins precedence. Closes every run by invoking `agentsmd-audit` inline, and hands off to `provider-adapter-author` when an existing provider-specific file (CLAUDE.md, etc.) now duplicates content AGENTS.md owns.
## Before you start
The `agentsmd-audit` skill must be available (co-installed in the `core` plugin) — this skill invokes it as a mandatory closeout step.
## Usage
```
/agentsmd-author
```
Provide the target repo root (defaults to the current directory) and, if relevant, which subdirectory should get a nested AGENTS.md.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents |
| `references/content-guide.md` | Section-by-section AGENTS.md content guidance, a worked example, and monorepo/nested-file precedence rules |
| `references/sources.md` | Provenance record — sources that informed this skill and which files each contributed to |

View File

@@ -0,0 +1,56 @@
---
name: agentsmd-author
description: >
Use when the user wants to create or update a repo's AGENTS.md file
("write an AGENTS.md for this repo", "add setup/test instructions for
agents", "update AGENTS.md", "give this package its own AGENTS.md") — even
if they don't name the file explicitly, e.g. "document this for AI coding
tools" or "make sure agents know how to run tests here". Writes/updates
AGENTS.md by exploring the target repo for real build, test, lint, and
style conventions — never invents commands. Supports nested monorepo
placement (a subdirectory can get its own AGENTS.md following
nearest-file-wins precedence). Closes every run by invoking agentsmd-audit
inline, and calls provider-adapter-author when an existing provider file
(CLAUDE.md, etc.) now duplicates what AGENTS.md owns. Do not use to review
an existing AGENTS.md without changing it — use agentsmd-audit instead. Do
not use to convert CLAUDE.md/.cursor/rules into a thin adapter — use
provider-adapter-author instead.
allowed-tools: Bash Read Write Edit
metadata:
category: docs
source_keys:
- agents-md-official
- context7-websites-agents-md
- context7-agentsmd-agents-md
version: "0.1.1"
---
## Gotchas
- Never invent a command. Every line under a setup/test/build section must come from something you actually found in the repo (`package.json` scripts, a `Makefile` target, a CI workflow step, a README). If you can't verify a command, don't include it.
- AGENTS.md has no required schema — don't force every common-sections-checklist heading into every repo. Include only sections that reflect something real about this repo; a thin, accurate file beats a padded, generic one.
- Nested placement is for genuinely different conventions, not convenience. Only create a subdirectory AGENTS.md when that subtree has its own build tool, stack, or conventions distinct from the root — otherwise you're duplicating content the root already covers, which the nearest-file-wins rule doesn't merge back together.
- This skill never touches CLAUDE.md, `.cursor/rules/*.mdc`, `copilot-instructions.md`, or similar provider files directly — that's `provider-adapter-author`'s job. Detect and hand off; don't reconcile it yourself.
- This skill never audits on its own judgment — the closing `agentsmd-audit` invocation is mandatory, not optional, even when the change looks trivial.
## Step 1 — Explore the target repo
Before writing anything, gather real facts: package manager and scripts (`package.json`, `pyproject.toml`, `Cargo.toml`, etc.), a `Makefile` or task runner, CI config (`.github/workflows/`, etc.) for the commands it actually runs, linter/formatter config files, and any existing docs (`README.md`, existing `AGENTS.md`) describing conventions. Note whether any subdirectory looks like its own package with a different stack.
## Step 2 — Decide placement
- No `AGENTS.md` at the repo root yet → create one there first, covering whole-repo conventions.
- A subdirectory has materially different build/test tooling or conventions than the root → create or update a nested `AGENTS.md` there, scoped to what's different. Don't repeat root-level content — the nearest-file-wins rule means the nested file is read alone, not merged with the root.
- Otherwise → update the existing file(s) in place.
## Step 3 — Write or update
Use only sections that reflect something real about the repo — never fill in every common-sections-checklist heading just because it exists. Read `references/content-guide.md` for section-by-section guidance, a worked example, and what separates useful content from generic padding, before writing.
## Step 4 — Check for an existing provider file
Look for `CLAUDE.md`, `.cursor/rules/*.mdc`, `.github/copilot-instructions.md`, or similar in the target repo. If one exists and now duplicates content the AGENTS.md you just wrote/updated already owns, invoke the `provider-adapter-author` skill on it to reconcile — don't rewrite it yourself.
## Step 5 — Audit and report
Invoke the `agentsmd-audit` skill directly on the AGENTS.md file(s) you just wrote or updated. Resolve any FAIL findings before considering the work done — re-invoke this skill's own writing steps to fix them, then re-run the audit, same as any other close-the-loop check. Report what was created/changed, whether a provider file was reconciled, and the audit's final result.

View File

@@ -0,0 +1,118 @@
---
source_keys:
- agents-md-official
- context7-websites-agents-md
- context7-agentsmd-agents-md
---
# What good AGENTS.md content looks like
AGENTS.md has no required schema — there's no field to fill in, only sections that either
earn their place or don't. Agents treat this file as a set of live directives, not
documentation: they will actually run the commands it lists and fix failures before
finishing a task. That means a wrong or stale line is worse than a missing one. Verify
every command against something real in the repo before writing it down.
## Section-by-section guidance
**Setup / build commands** — the install and dev-server commands, exactly as they appear
in `package.json` scripts, a `Makefile`, or a `Cargo.toml`/`pyproject.toml` equivalent. One
line per command, each with a one-clause note on what it does if the name alone isn't
obvious. Skip this section if there's genuinely nothing beyond "clone and run" — don't pad
it with a restated `git clone`.
**Code style** — only conventions that aren't already enforced by a linter/formatter config
the agent will pick up on its own (a `.eslintrc`, `rustfmt.toml`, etc. speaks for itself).
Write down the conventions that live only in people's heads: naming patterns, module
boundaries, patterns to avoid, anything a linter can't catch. If the repo has no
undocumented conventions beyond what tooling enforces, skip this section.
**Testing instructions** — the exact command(s) to run the suite, where to find
per-package or per-workflow test configuration (e.g. `.github/workflows/`), and any
non-obvious requirement (a service that must be running, an env var that must be set).
State plainly that the agent should run tests before considering a change done and fix
failures — don't leave this implicit.
**Security considerations** — only repo-specific hazards: a data-handling boundary, a
credential pattern to never hardcode, a destructive command that needs a confirmation
step. Do not restate general security advice ("don't commit secrets") that any agent
already assumes — that's padding, not a directive.
**Commit / PR conventions** — the title/format convention if one exists (e.g. a
Conventional Commits type prefix, a ticket-number requirement), and any check that must
pass before a PR is opened (lint, test, type-check). Point at the real command, not
"make sure it passes."
**Dev environment tips** — the handful of things that save real time and are easy to miss:
how to jump to a specific package in a monorepo without `ls`-ing around, how to register a
new package so the toolchain sees it, where to look up a canonical name/id. This section
is for genuine friction points observed in this repo, not generic advice.
## What separates useful content from padding
A useful section names a real file, command, or path that exists in this repo right now.
A padded section could be pasted into any repo unchanged and still "make sense" — that's
the tell. If a sentence would read the same in a different codebase, it doesn't belong.
Prefer four accurate lines over twelve generic ones.
## Worked example (minimal project)
```markdown
# AGENTS.md
## Setup commands
- Install deps: `pnpm install`
- Start dev server: `pnpm dev`
- Run tests: `pnpm test`
## Code style
- TypeScript strict mode
- Single quotes, no semicolons
- Use functional patterns where possible
## Dev environment tips
- Use `pnpm dlx turbo run where <project_name>` to jump to a package instead of scanning with `ls`.
- Run `pnpm install --filter <project_name>` to add the package to your workspace so Vite, ESLint, and TypeScript can see it.
- Check the `name` field inside each package's `package.json` to confirm the right name.
## Testing instructions
- Find the CI plan in the `.github/workflows` folder.
- Run `pnpm turbo run test --filter <project_name>` to run every check defined for that package.
- From the package root you can just call `pnpm test`. The commit should pass all tests before you merge.
- Fix any test or type errors until the whole suite is green.
- Add or update tests for the code you change, even if nobody asked.
## PR instructions
- Title format: [<project_name>] <Title>
- Always run `pnpm lint` and `pnpm test` before committing.
```
Every line above names a real command or path — that's the standard to hold this repo's
version to, not the specific tooling shown (a Python/Cargo/Go repo's AGENTS.md should look
nothing like this one in its specifics, only in how concrete each line is).
## Monorepo / nested placement
```
my-monorepo/
├── AGENTS.md # Root-level: applies to the whole repo
├── packages/
│ ├── api/
│ │ └── AGENTS.md # API-specific instructions; overrides root for this package
│ ├── web/
│ │ └── AGENTS.md # Web app-specific instructions
│ └── shared/
│ └── AGENTS.md # Shared library instructions
```
Precedence rule: the file nearest the edited path wins. Nested files are **not** merged
with the root file — an agent editing inside `packages/api/` reads only
`packages/api/AGENTS.md`, never the root file in addition. Consequences:
- A nested file must stand alone. Don't write "also see the root file" — write what the
agent needs, full stop.
- Don't duplicate root content in a nested file "just in case." If a nested file repeats
root-level setup instructions verbatim, that's a sign it shouldn't exist as a separate
file at all — the subtree isn't actually different enough to warrant one.
- Only create a nested file when the subtree has a genuinely different stack, build tool,
or convention than the root (see `SKILL.md` Step 2 for the placement decision itself).

View File

@@ -0,0 +1,25 @@
# Sources
## agents-md-official
- **URL:** https://agents.md/
- **Description:** Official agents.md website — format spec, common-sections checklist, precedence rules (nearest-file-wins, no merge across files), monorepo nesting patterns
- **Research doc:** plugins/core/docs/research/docs/agentsmd/sources.md
- **Contributing files:** SKILL.md, references/content-guide.md
- **Status:** `extracted`
## context7-websites-agents-md
- **URL:** context7:/websites/agents_md
- **Description:** Context7 index of the official agents.md website — overview, governance, cross-tool compatibility, configuration examples
- **Research doc:** plugins/core/docs/research/docs/agentsmd/sources.md
- **Contributing files:** SKILL.md, references/content-guide.md
- **Status:** `extracted`
## context7-agentsmd-agents-md
- **URL:** context7:/agentsmd/agents.md
- **Description:** Context7 index of the agentsmd/agents.md repository — format spec, nested monorepo patterns, file structure examples
- **Research doc:** plugins/core/docs/research/docs/agentsmd/sources.md
- **Contributing files:** SKILL.md, references/content-guide.md
- **Status:** `extracted`

View File

@@ -0,0 +1,30 @@
# provider-adapter-author
Convert a target repo's provider-specific instruction file (CLAUDE.md, .cursor/rules, copilot-instructions.md, etc.) into a thin adapter over AGENTS.md.
## What it does
Detects a provider-specific AI instruction file in a target repo, diffs it against the repo's `AGENTS.md`, and rewrites it down to a minimal reference — an `@AGENTS.md`-style import for providers that support one, or a text pointer for those that don't — plus only genuinely provider-specific additions. Self-validates its own output with a bundled deterministic script (no LLM judgment, no separate audit skill) before finishing.
## Before you start
The target repo must already have an `AGENTS.md`. If it doesn't, run `agentsmd-author` first — this skill never creates or edits `AGENTS.md` itself.
## Usage
```
/provider-adapter-author
```
Provide the path to the provider-specific file to convert (and the target repo root, if not inferable). Can be invoked directly, or composed into by `agentsmd-author` when it detects an existing provider file with content overlapping AGENTS.md.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents |
| `references/sources.md` | Provenance record — the in-repo ADR precedent this skill's design is modeled on |
| `scripts/validate-adapter.sh` | Self-check gate: reference to AGENTS.md present, no excessive duplication, adapter stays thin |
| `scripts/README.md` | Directory documentation for `scripts/` |
| `tests/README.md` | Bats test dependency and run instructions |
| `tests/validate-adapter.bats` | Bats test suite for `scripts/validate-adapter.sh` |

View File

@@ -0,0 +1,54 @@
---
name: provider-adapter-author
description: >
Use when the user wants to convert a provider-specific AI instruction file
(CLAUDE.md, .cursor/rules/*.mdc, copilot-instructions.md, etc.) into a
thin adapter that defers to a repo's AGENTS.md — e.g. "reduce duplication
between CLAUDE.md and AGENTS.md", "make CLAUDE.md just import AGENTS.md"
— even if the pattern isn't named explicitly. Also invoke when
agentsmd-author detects an existing provider file overlapping with
AGENTS.md it just wrote. Detects redundant content in a provider file
relative to AGENTS.md and rewrites it down to a minimal reference (an
`@AGENTS.md`-style import where supported, or a text pointer otherwise)
plus genuinely provider-specific additions. Self-validates via a bundled
deterministic script before finishing. Do not use to write or audit
AGENTS.md itself — use agentsmd-author or agentsmd-audit.
allowed-tools: Bash Read Edit Write
metadata:
category: docs
source_keys:
- adr-0002-0003-two-tier-claude-md
version: "0.1.0"
---
## Gotchas
- Not every provider supports cross-file imports. Claude Code does — a `CLAUDE.md` can consist of nothing but one or more `@path` lines (e.g. `@AGENTS.md`), with no other content required. Cursor's `.cursor/rules/*.mdc` and GitHub Copilot's `copilot-instructions.md` have no native import mechanism as of current tooling — for those, "thin" means a short text pointer to AGENTS.md plus only what that tool actually needs, not a literal import line. Pass `--no-import-syntax` to `scripts/validate-adapter.sh` for these providers.
- This skill never creates or edits `AGENTS.md` itself. If the target repo has no `AGENTS.md` yet, stop and point the user to `agentsmd-author` first — there's nothing to adapt to.
- Only strip content from the provider file that's genuinely redundant with AGENTS.md. Provider-specific material (IDE settings, tool-only syntax, model-specific instructions) stays — the goal is thin, not empty.
- Works standalone or composed-into by `agentsmd-author` — behave identically either way; don't assume a caller skill exists.
## Step 1 — Detect
Look for known provider instruction files in the target repo: `CLAUDE.md` (repo root, and any deployed copies), `.cursor/rules/*.mdc`, `.github/copilot-instructions.md`, and similar tool-specific files. Confirm `AGENTS.md` exists at the repo root — if not, stop and tell the user to run `agentsmd-author` first.
## Step 2 — Diff and rewrite
Read the provider file and `AGENTS.md` side by side. Separate the provider file's content into two buckets: lines that restate what `AGENTS.md` already owns (universal rules, conventions, project overview) versus lines that are genuinely provider-specific (tool syntax, IDE behavior, model-specific instructions). Rewrite the provider file:
- **Providers with import syntax** (Claude Code): replace the redundant bucket with an `@AGENTS.md` (or correct relative path) import line, keep the provider-specific bucket below it.
- **Providers without import syntax** (Cursor, Copilot, etc.): replace the redundant bucket with a short pointer sentence mentioning `AGENTS.md`, keep the provider-specific bucket.
## Step 3 — Self-validate
Run the bundled check before finishing — this is the skill's own closeout gate; there is no separate paired audit skill for this concern:
```bash
bash scripts/validate-adapter.sh [--no-import-syntax] [--max-lines N] <adapter-file> <agents-md-file>
```
Fix any `FAIL` and re-run until it exits `0`.
## Step 4 — Report
State which file was converted, what was removed versus kept, and the validator's final result.

View File

@@ -0,0 +1,9 @@
# Sources
## adr-0002-0003-two-tier-claude-md
- **URL:** (in-repo precedent — not an external source or plugin research corpus entry)
- **Description:** This repo's own two-tier CLAUDE.md/AGENTS.md pattern: AGENTS.md is the provider-agnostic source of always-on rules; provider-specific files (CLAUDE.md) become thin adapters that import it (`@AGENTS.md` plus provider-specific additions). Grounds this skill's entire adapter-conversion design — the "thin adapter" shape, the `@`-import convention, and the size/duplication expectations enforced by `scripts/validate-adapter.sh`.
- **Research doc:** docs/adr/0002-two-tier-claude-md.md, docs/adr/0003-agents-md-provider-agnostic-entry-point.md, providers/claude-code/CLAUDE.md (in-repo ADRs and a live example, not a plugin research corpus entry; referenced here since this skill's design is modeled directly on an existing implementation rather than external research)
- **Contributing files:** SKILL.md
- **Status:** `extracted`

View File

@@ -0,0 +1,9 @@
# scripts/
Deterministic self-check this skill shells out to instead of relying on LLM judgment for a mechanical check.
| File | Purpose |
|------|---------|
| `validate-adapter.sh` | Checks a rewritten provider file (CLAUDE.md, etc.) has a reference to AGENTS.md, doesn't duplicate its content, and stays under a thin-file line threshold |
Takes `<adapter-file> <agents-md-file>`, with optional `--no-import-syntax` and `--max-lines N` flags. Prints `FAIL` findings to stdout and exits non-zero on any failure.

View File

@@ -0,0 +1,141 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<EOF
Usage: validate-adapter.sh [--no-import-syntax] [--max-lines N] <adapter-file> <agents-md-file>
Self-check gate for provider-adapter-author. Checks that a rewritten
provider-specific instruction file (CLAUDE.md, .cursor/rules/*.mdc,
copilot-instructions.md, etc.) is actually a thin adapter over AGENTS.md,
not a duplicate copy of it.
Arguments:
adapter-file Path to the provider-specific file to check.
agents-md-file Path to the AGENTS.md file it should defer to.
Options:
--no-import-syntax The target provider has no native cross-file import
mechanism. Accept a plain-text pointer mention of
"AGENTS.md" instead of requiring an @import-style line.
--max-lines N Max non-blank lines allowed in the adapter file before
it's considered no longer "thin". Default: 60.
--help, -h Show this help and exit 0.
Exit codes:
0 Adapter file passes all checks
1 One or more checks failed (empty file, no reference to AGENTS.md,
excessive duplication, or file too long)
EOF
}
NO_IMPORT_SYNTAX=0
MAX_LINES=60
ARGS=()
while [[ $# -gt 0 ]]; do
case "$1" in
--help|-h)
usage
exit 0
;;
--no-import-syntax)
NO_IMPORT_SYNTAX=1
shift
;;
--max-lines)
MAX_LINES="${2:-}"
shift 2
;;
*)
ARGS+=("$1")
shift
;;
esac
done
if [[ ${#ARGS[@]} -lt 2 ]]; then
echo "Error: adapter-file and agents-md-file are required." >&2
echo "" >&2
usage >&2
exit 1
fi
python3 -u - "${ARGS[0]}" "${ARGS[1]}" "$NO_IMPORT_SYNTAX" "$MAX_LINES" <<'PYTHON'
import sys
import os
import re
adapter_path, agents_md_path, no_import_syntax, max_lines = sys.argv[1:5]
no_import_syntax = no_import_syntax == "1"
max_lines = int(max_lines)
if not os.path.isfile(adapter_path):
print(f"Error: '{adapter_path}' is not a file.", file=sys.stderr)
sys.exit(1)
if not os.path.isfile(agents_md_path):
print(f"Error: '{agents_md_path}' is not a file.", file=sys.stderr)
sys.exit(1)
with open(adapter_path, encoding="utf-8", errors="replace") as f:
adapter_content = f.read()
with open(agents_md_path, encoding="utf-8", errors="replace") as f:
agents_md_content = f.read()
has_fail = False
if not adapter_content.strip():
print(f"FAIL Adapter file is empty — {adapter_path}")
print(" Why: An empty adapter carries no reference to AGENTS.md and no provider-specific content.")
print(" Fix: Add at least an import (or text pointer) to AGENTS.md.")
print()
sys.exit(1)
IMPORT_RE = re.compile(r'(?m)^\s*@\S*AGENTS\.md\s*$')
lines = adapter_content.splitlines()
import_lines = [ln for ln in lines if IMPORT_RE.match(ln)]
if no_import_syntax:
has_reference = "AGENTS.md" in adapter_content
else:
has_reference = bool(import_lines) or "AGENTS.md" in adapter_content
if not has_reference:
has_fail = True
print(f"FAIL Adapter has no reference to AGENTS.md — {adapter_path}")
if no_import_syntax:
print(" Why: This provider has no import syntax, so the adapter must at least mention AGENTS.md as a text pointer.")
print(" Fix: Add a sentence like \"See AGENTS.md at the repo root for shared conventions.\"")
else:
print(" Why: A thin adapter must import AGENTS.md (e.g. `@AGENTS.md`) rather than silently omitting it.")
print(" Fix: Add an `@AGENTS.md` (or equivalent relative path) import line.")
print()
# --- Duplication check ---
non_import_lines = [ln for ln in lines if not IMPORT_RE.match(ln)]
adapter_lines = [ln.strip() for ln in non_import_lines if ln.strip()]
agents_lines = {ln.strip() for ln in agents_md_content.splitlines() if ln.strip()}
if adapter_lines:
overlap = sum(1 for ln in adapter_lines if ln in agents_lines)
ratio = overlap / len(adapter_lines)
if ratio > 0.3:
has_fail = True
print(f"FAIL Adapter duplicates AGENTS.md content — {adapter_path}")
print(f" Why: {ratio:.0%} of the adapter's non-import lines already appear verbatim in AGENTS.md. A thin adapter should import shared content, not restate it.")
print(" Fix: Remove the duplicated lines and rely on the AGENTS.md import (or pointer) instead.")
print()
# --- Size check ---
non_blank_count = len([ln for ln in lines if ln.strip()])
if non_blank_count > max_lines:
has_fail = True
print(f"FAIL Adapter is not thin — {adapter_path}")
print(f" Why: {non_blank_count} non-blank lines exceeds the {max_lines}-line threshold for a thin adapter.")
print(" Fix: Move provider-agnostic content into AGENTS.md; keep only genuinely provider-specific additions here.")
print()
if has_fail:
sys.exit(1)
sys.exit(0)
PYTHON

View File

@@ -18,7 +18,7 @@ git clone https://github.com/bats-core/bats-assert tests/test_helper/bats-assert
Run all tests for this skill (from the repo root): Run all tests for this skill (from the repo root):
```bash ```bash
bats plugins/core/skills/provider-adapter-author/tests/ bats plugins/core/.apm/skills/provider-adapter-author/tests/
``` ```
## Files ## Files

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bats #!/usr/bin/env bats
setup() { setup() {
REPO_ROOT="$(cd "$BATS_TEST_DIRNAME/../../../../../" && pwd)" REPO_ROOT="$(cd "$BATS_TEST_DIRNAME/../../../../../../" && pwd)"
load "$REPO_ROOT/tests/test_helper/bats-support/load" load "$REPO_ROOT/tests/test_helper/bats-support/load"
load "$REPO_ROOT/tests/test_helper/bats-assert/load" load "$REPO_ROOT/tests/test_helper/bats-assert/load"

View File

@@ -1,18 +1,20 @@
{ {
"name": "core",
"version": "1.1.0",
"description": "Cross-cutting utility skills for everyday AI-assisted coding \u2014 triage, diagnosis, architecture review, and session navigation.",
"author": { "author": {
"name": "Defame1297", "name": "Defame1297",
"email": "defame1297@rkdr.net",
"url": "https://git.dev.rkdr.net/Defame1297/" "url": "https://git.dev.rkdr.net/Defame1297/"
}, },
"description": "Cross-cutting utility skills for everyday AI-assisted coding \u2014 triage, diagnosis, architecture review, and session navigation.", "license": "MIT",
"displayName": "Core", "homepage": "https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/core",
"repository": "https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/core",
"keywords": [ "keywords": [
"cross-cutting", "cross-cutting",
"triage", "triage",
"diagnose", "diagnose",
"architecture", "architecture",
"debug" "debug"
], ]
"license": "MIT",
"name": "core",
"version": "1.1.0"
} }

View File

@@ -1,22 +1,20 @@
{ {
"author": { "name": "core",
"email": "defame1297@rkdr.net", "version": "1.1.0",
"name": "Defame1297"
},
"description": "Cross-cutting utility skills for everyday AI-assisted coding \u2014 triage, diagnosis, architecture review, and session navigation.", "description": "Cross-cutting utility skills for everyday AI-assisted coding \u2014 triage, diagnosis, architecture review, and session navigation.",
"hooks": "hooks.json", "author": {
"name": "Defame1297",
"email": "defame1297@rkdr.net",
"url": "https://git.dev.rkdr.net/Defame1297/"
},
"license": "MIT",
"homepage": "https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/core",
"repository": "https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/core",
"keywords": [ "keywords": [
"cross-cutting", "cross-cutting",
"triage", "triage",
"diagnose", "diagnose",
"architecture", "architecture",
"debug" "debug"
], ]
"license": "MIT",
"mcpServers": ".mcp.json",
"name": "core",
"skills": [
"skills/"
],
"version": "1.1.0"
} }

35
plugins/core/apm.yml Normal file
View File

@@ -0,0 +1,35 @@
name: core
version: 1.1.0
description: Cross-cutting utility skills for everyday AI-assisted coding — triage, diagnosis, architecture review, and session navigation.
author:
name: Defame1297
email: defame1297@rkdr.net
url: https://git.dev.rkdr.net/Defame1297/
license: MIT
homepage: https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/core
repository: https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/core
keywords:
- cross-cutting
- triage
- diagnose
- architecture
- debug
# Constrains what .apm/ may contain: instructions, skill, hybrid, or prompts
type: skill
targets:
- claude
- copilot
- codex
# "auto" publishes the authoritative local source layout, or list explicit
# repo paths to define the complete publication set.
includes: auto
dependencies:
apm: []
mcp: []
devDependencies:
apm: []
scripts: {}

View File

@@ -3,8 +3,6 @@ name: git-orchestrate
description: Orchestrates git workflow operations for other agents. Invoke when a caller needs a multi-step or destructive git operation (rebase, force-push, branch deletion) coordinated across domain skills with safety gates, session context, and structured results. description: Orchestrates git workflow operations for other agents. Invoke when a caller needs a multi-step or destructive git operation (rebase, force-push, branch deletion) coordinated across domain skills with safety gates, session context, and structured results.
tools: Bash, Read, Edit
source_keys: source_keys:
- context7-git-htmldocs - context7-git-htmldocs
- git-scm-docs - git-scm-docs
@@ -12,7 +10,6 @@ source_keys:
- git-scm-submodule-docs - git-scm-submodule-docs
- git-scm-remote-docs - git-scm-remote-docs
- conventional-commits-spec - conventional-commits-spec
--- ---
You are the orchestrator for the git plugin—a composable workflow dispatcher designed for other agents to invoke multi-step git operations reliably. Your one job is routing and safety-gating: you do not execute git logic yourself, you delegate to domain skills and enforce confirmation on destructive operations. You are the orchestrator for the git plugin—a composable workflow dispatcher designed for other agents to invoke multi-step git operations reliably. Your one job is routing and safety-gating: you do not execute git logic yourself, you delegate to domain skills and enforce confirmation on destructive operations.

View File

@@ -0,0 +1,22 @@
# git-branches
Manage the full lifecycle of git branches — create, switch, delete, rename, and track feature/hotfix/release branches under GitHub Flow or Gitflow.
## What it does
This skill handles branch operations within the git workflow suite. It creates branches following GitHub Flow or Gitflow conventions (configurable), switches and tracks branches, handles safe deletion with unmerged-work checks, and retrieves branch intent metadata for use by other skills (e.g., commit message context). It returns structured results suitable for agent composition.
## Usage
```
/git-branches
```
Describe your branch task: create a feature/hotfix/release branch, switch, delete, rename, or track. The skill will determine the branching pattern (GitHub Flow or Gitflow) from config or repo state and handle safety checks for destructive operations.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents |
| `references/sources.md` | Research sources backing the branching/gitflow guidance |

View File

@@ -0,0 +1,112 @@
---
name: git-branches
description: >
Use when managing the full lifecycle of git branches: create feature/hotfix/release branches
(gitflow, GitHub Flow, or custom patterns from config), switch, delete, rename, and track branches,
or retrieve branch intent metadata. Handles branch protection safety checks and returns structured
results for agent composition. Use even if the user doesn't explicitly mention branch names — they
may be asking about "fixing something" or "shipping a feature", which implicitly requires branch
management. Do not use when the user needs only commit operations (use git-commits) or history
inspection (use git-history).
metadata:
category: git
source_keys:
- context7-git-htmldocs
- nvie-gitflow-post
- atlassian-gitflow-tutorial
- gitflow-cheatsheet
---
## Gotchas
- **Branches are cheap; deletion is cheap but risky.** Deleting one requires checking if commits on it are reachable elsewhere; always confirm before deleting, as it may lose unmerged work.
- **Uncommitted changes can block branch switches.** `git switch` aborts if local modifications conflict with the target branch. Offer to stash changes before switching when this happens, don't force a checkout.
- **Tracking relationships matter for coordination.** Agents pushing on behalf of users should always set tracking (`-u origin <branch>`) so later pushes/pulls know the target. Without it, commands fail or target the wrong remote branch.
- **Gitflow vs. GitHub Flow are not compatible.** Gitflow requires `develop` and `release/*` branches with `--no-ff` merges; GitHub Flow uses only `main` and feature branches with fast-forward. Read the repo's config or ask the orchestrator which pattern to use — don't guess.
- **Naming collisions with tags.** A branch and tag can have the same name. Prefer `git switch` over `git checkout` for branch operations — verify which ref you're targeting with `git branch --list <name>` / `git tag --list <name>` if the name could be ambiguous, and disambiguate explicitly with `refs/heads/<name>` (branch) or `refs/tags/<name>` (tag) where a command accepts either.
- **Never force-push `main` or `master`.** This is a hard refusal, not a confirmation gate — it applies even if the caller passes `confirm: true`. Deleting or renaming `main`/`master` in a way that would require a force-push to reconcile the remote (e.g. force-deleting and recreating it, or renaming it out from under in-flight work) must be rejected outright; explain why and suggest a non-destructive alternative (e.g. a new branch) instead of proceeding.
## Branch Patterns
Default to **GitHub Flow** (simpler, modern, CI/CD-friendly). Fall back to **Gitflow** only if the repo's config specifies it or the branch structure shows it in use (presence of `develop` or release branches).
**GitHub Flow:**
- Base: `main`
- Feature branches: `feature/<feature-name>` or `fix/<bug-name>`
- Merge: fast-forward when possible (preserves linear history)
- Delete after merge
**Gitflow:**
- Base: `main` (production) + `develop` (integration)
- Feature branches: `feature/<feature-name>` (from `develop`)
- Release branches: `release/X.Y.Z` (from `develop`, merged to `main` + `develop`)
- Hotfix branches: `hotfix/X.Y.Z` (from `main`, merged to `main` + `develop`)
- Merge: always use `--no-ff` to preserve branch structure
## Workflow
- [ ] **Determine pattern:** Check git plugin config (`.claude/plugins/git/config.json`, if present — see `config.example.json` in the plugin root for the expected shape) for `branching_pattern` (default: `github-flow`). If not set, inspect repo for `develop` branch or `release/*` branches; if present, assume Gitflow.
- [ ] **Create branch:** Use `git switch -c <branch> <base>`. Base defaults to config's `base_branch` (usually `main` or `develop`). Include intent metadata in branch name or return as structured result (e.g., `{ "branch": "feature/x", "intent": "implement feature X" }`).
- [ ] **Track remote:** If pushing, always use `git push -u origin <branch>` to establish tracking.
- [ ] **Safety checks before destructive ops:** Before delete/force-push/rebase with history loss, check: (1) Is this branch tracking a remote? Warn if yes. (2) Are there unpushed commits? Warn if yes. (3) Does the orchestrator call include `confirm: true`? Fail if not. For humans, prompt interactively.
- [ ] **Return structured results:** Always return branch operations as JSON or structured text: `{ "action": "create", "branch": "feature/x", "base": "main", "tracking": "origin/feature/x", "intent": "implement feature X" }`. Agents need to parse this for subsequent operations.
- [ ] **Retrieve intent (`get-intent`):** Git has no native field for free-text branch metadata — this skill doesn't persist it. On `create`, the `intent` value is only ever returned in the structured result; the caller (orchestrator or agent) is responsible for storing it if it needs to be looked up later. On `get-intent`, either parse it back out of the branch name convention (`feature/<intent-slug>`) or return `{ "intent": null }` if the caller never persisted the original create-time value — don't fabricate an intent.
### Command mapping for each action
- **delete:** `git branch -d <branch>` refuses if the branch has unmerged commits — prefer this by default. `git branch -D <branch>` forces deletion and discards unmerged work; only use it after the safety checks above pass and `confirm: true` is set. For a remote branch: `git push origin --delete <branch>`.
- **rename:** `git branch -m <old> <new>`.
- **list:** `git branch` (local only), `git branch -a` (all local + remote-tracking), `git branch -r` (remote-tracking only), `git branch --merged`/`--no-merged` (filter by merge status into current branch).
- **get-intent:** No git command — see Workflow step "Retrieve intent" for how this is resolved.
- **track (existing branch):** `git branch --set-upstream-to=origin/<branch>` sets tracking without a push; `git branch -vv` shows tracking state for all local branches.
- **switch (existing branch):** `git switch <branch>` — switches to an existing local branch (aborts on conflicting local changes, see Gotchas). `git switch -` switches back to the previously checked-out branch.
## Merging
Scope: fast-forward/merge-commit mechanics and conflict resolution only. Rebase, cherry-pick, and revert belong to `git-history`.
- **Fast-forward:** `git merge <branch>` — advances the pointer with no merge commit if the target hasn't diverged.
- **True merge:** `git merge --no-ff <branch>` — forces a merge commit even when fast-forward is possible; required by Gitflow on all supporting-branch merges.
- **Squash merge:** `git merge --squash <branch>` stages the combined diff without committing; follow with a manual `git commit`.
- **Octopus merge:** `git merge branch-a branch-b branch-c` merges more than two branches at once; fails outright on any conflict, so use sequential two-way merges if conflicts are expected.
**Conflict resolution:** when Git can't auto-merge, it inserts conflict markers and stops. Run `git status` to find conflicted files, edit them to resolve the markers, then `git add <file>` and `git merge --continue`. `git merge --abort` reverts to the pre-merge state. `git mergetool` opens the configured merge tool; `git diff --diff-filter=U` shows only conflicted files.
## Comparing Branches
- `git log main..feature` — commits in `feature` not in `main`.
- `git log feature..main` — commits in `main` not in `feature` (reverse direction).
- `git log --left-right main...feature` — both diverging sets (symmetric diff).
- `git diff main...feature` — diff from the common ancestor to `feature`'s tip.
- `git merge-base main feature` — print the common ancestor commit.
## Integration with Orchestrator
When invoked by `git-orchestrate`, accept requests in the form:
```json
{
"action": "create|switch|delete|rename|track|list|get-intent",
"branch": "<branch-name>",
"base": "<base-branch (optional, defaults to config)>",
"intent": "<human-readable intent (optional)>",
"confirm": "<true for destructive ops, omit for read ops>"
}
```
Return results as:
```json
{
"success": true,
"action": "create|switch|...",
"branch": "<name>",
"message": "descriptive message",
"intent": "<intent if tracked>",
"tracking": "origin/<branch (if set)>",
"error": "<error message if success=false>",
"suggestion": "<recovery suggestion if applicable>"
}
```
If error is due to uncommitted changes, include `{ "suggestion": "stash changes and retry" }` so the orchestrator can offer automatic recovery.

View File

@@ -0,0 +1,48 @@
---
# Research sources referenced by this skill
# Each entry documents where the skill's guidance came from.
---
## nvie-gitflow-post
**Description:** Original 2010 post by Vincent Driessen introducing the Gitflow branching model, including a 2020 reflection note recommending GitHub Flow for continuous delivery teams.
**Source:** https://nvie.com/posts/a-successful-git-branching-model/
- **Research doc:** plugins/git/docs/research/docs/git/gitflow.md (whole-document reference)
**Contributing files:**
- SKILL.md (Branch Patterns — Gitflow vs. GitHub Flow structure and defaults)
## atlassian-gitflow-tutorial
**Description:** Atlassian's comprehensive Gitflow tutorial covering all five branch types, lifecycle steps, and CLI usage.
**Source:** https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow
- **Research doc:** plugins/git/docs/research/docs/git/gitflow.md (whole-document reference)
**Contributing files:**
- SKILL.md (Branch Patterns — Gitflow branch types, base/merge targets, `--no-ff` requirement)
## gitflow-cheatsheet
**Description:** Visual cheatsheet for the git-flow CLI commands (git-flow-avh fork), covering all subcommands for feature, release, and hotfix branches.
**Source:** https://danielkummer.github.io/git-flow-cheatsheet/
- **Research doc:** plugins/git/docs/research/docs/git/gitflow.md (whole-document reference)
**Contributing files:**
- SKILL.md (Branch Patterns — feature/release/hotfix naming conventions)
## context7-git-htmldocs
**Description:** Official Git HTML documentation from the git/htmldocs repository — covers all commands, concepts, and internals.
**Source:** context7:/git/htmldocs
- **Research doc:** plugins/git/docs/research/docs/git/branching-merging.md (whole-document reference)
**Contributing files:**
- SKILL.md (Command mapping, Merging, Comparing Branches — `git switch`/`git branch`/`git merge`/`git log`/`git diff`/`git merge-base` command vocabulary and flags)

View File

@@ -0,0 +1,24 @@
# git-commits
Create, amend, squash, and cherry-pick commits with Conventional Commits formatting and validation.
## What it does
This skill handles commit operations within the git workflow suite. It generates well-formatted commit messages following the Conventional Commits spec, validates against commitlint config-conventional constraints, and communicates SemVer impact. It enforces confirmation gates for history-altering operations (amend, rebase, squash) and returns structured JSON output for agent consumption.
## Usage
```
/git-commits
```
Describe your commit task: create a new commit, amend, squash, or cherry-pick. The skill will guide message formatting and handle confirmation for destructive operations.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents |
| `references/conventional-commits-spec.md` | Full Conventional Commits specification |
| `references/commit-template.md` | Why / Implementation Notes / Impact body structure and full trailer list |
| `references/sources.md` | Research sources and provenance |

View File

@@ -0,0 +1,115 @@
---
name: git-commits
description: >
Use when creating, amending, squashing, or cherry-picking commits.
Generates well-formatted commit messages following Conventional Commits spec (type, scope, description, body, footers).
Validates against commitlint config-conventional constraints (header max 100 chars, lowercase subject, no trailing periods, type must be one of 11 standard types).
Communicates SemVer impact (MAJOR for breaking changes, MINOR for features, PATCH for fixes).
Handles confirmation gates for history-altering operations (amend, rebase, squash).
Provides interactive guidance for humans, structured JSON output for agents.
Do not use for: inspecting git history, branch management, or repository state inspection — those are separate skills.
metadata:
version: "0.1.2"
category: git
source_keys:
- conventional-commits-spec
- commitlint-config-conventional
- org-commit-conventions
- context7-git-htmldocs
allowed-tools: Bash
---
## Gotchas
- **Type must be one of 11 standard types** — `feat`, `fix`, `perf`, `revert`, `docs`, `style`, `refactor`, `test`, `build`, `ci`, `chore`. Non-standard types will fail commitlint validation. Note: the Conventional Commits spec itself only mandates `feat`/`fix` — the 11-type set is a commitlint/Angular convention this skill validates against, not a spec requirement.
- **Scope is optional but should be used** — helps identify which part of the system changed. Examples: `api`, `db`, `cli`, `config`.
- **Header max 100 characters** — type + scope + colon + description must fit. If longer, move detail to body.
- **BREAKING CHANGE notation** — use `!` before the colon (`feat!: drop Node 6`) for visibility in `git log --oneline`. Footer notation (`BREAKING CHANGE: ...`) is machine-readable but hidden in log.
- **SemVer mapping is not optional** — agents must communicate: `feat` → MINOR bump, `fix`/`perf`/`revert` → PATCH, any with breaking change → MAJOR.
- **Confirmation gates are mandatory for destructive operations** — amend, rebase, squash require explicit user/agent approval before execution.
- **Never skip hooks with `--no-verify`** — hooks are the automated QA gate; bypassing them breaks the pipeline. Do not add this flag to any commit command unless the user explicitly demands it, and warn them if they do.
- **Never force-push `main`/`master`** — even after an amend or interactive rebase, refuse to force-push a protected branch (`main`, `master`) and explain why; force-push is only safe on branches no one else has based work on.
- **Command examples use the `rtk git` wrapper** — this org's convention routes all git invocations through `rtk git <subcommand>` instead of bare `git <subcommand>`. Follow this prefix in any command you actually run.
- **Never commit secrets, credentials, or environment-specific config** — if staged changes contain what looks like an API key, token, password, or connection string, stop and flag it before committing rather than committing it.
- **Commits must be atomic and leave the repo working** — each commit should be one logical, independently reviewable and reversible change, and should leave the repository in a buildable/testable state. If staged changes bundle unrelated work, suggest splitting before committing.
- **Commit messages explain why, not what** — the diff already shows what changed; the message's job is to capture context the diff can't (motivation, root cause, tradeoffs). See `references/commit-template.md` for the structure this maps to.
## Workflow
### For creating a new commit:
1. **Gather context** — what changed and why? (from staged changes, PR description, issue context). Verify the staged diff is one logical, atomic change and that the repo would still build/test at this commit — if not, suggest splitting before proceeding.
2. **Check for secrets** — scan the staged diff for anything that looks like a credential, API key, token, or environment-specific config. Stop and flag it rather than committing.
3. **Determine type** — is this a feature (`feat`), bug fix (`fix`), or other? Default: check the change itself.
4. **Determine scope** — which system/module? Use scope from plugin config if set, otherwise infer from files changed.
5. **Write description** — imperative mood, no period. Neither source spec sets a length target below the 100-char header max, but convention favors keeping it to ~50 characters where possible for `git log --oneline` readability. Examples: "add user authentication", "fix race condition in cache".
6. **Add body if needed** — explain why (not what). Blank line before body, wrap at 100 chars. For non-trivial changes, follow the Why / Implementation Notes / Impact structure in `references/commit-template.md`.
7. **Add footers if needed** — `Fixes: #123`, `Refs: #123`, `ADR: 0012`, `RFC: 0003`, `Design: <link>`, `Reviewed-by: Name`, `Co-authored-by: Name <email>`, `Signed-off-by: Name <email>`, `BREAKING CHANGE: description`. See `references/commit-template.md` for the full trailer list.
8. **Validate** — check header length, type correctness, no trailing periods, lowercase.
9. **Confirm and execute** — for agents, require explicit approval; for humans, show preview and ask. Never add `--no-verify` to skip hooks.
### For amending a commit:
1. **Stage new changes** (or changes to undo)
2. **Run amend operation** — executes `rtk git commit --amend [--no-edit]` based on user intent
3. **Offer message edit** — if user wants to change commit message, show current message and prompt for new one
4. **Confirm before force-push** — amending is only safe on non-shared branches; if the current branch is `main`/`master`, refuse to force-push and explain why rather than warning and proceeding
### For squashing commits (interactive rebase):
1. **Identify commits to squash** — typically the last N commits on current branch
2. **Confirm operation** — squashing rewrites history; get explicit approval
3. **Execute rebase** — `rtk git rebase -i HEAD~N`, mark older commits as `squash` or `fixup`
4. **Handle merge conflicts** — if rebase halts, offer conflict resolution options or abort; do not resolve automatically without confirmation
5. **Offer message composition** — if squashing interactive, allow message editing
### For squashing commits (autosquash — preferred when tagging at commit time):
Prefer this over manual interactive rebase when a commit is written to be folded into an earlier one, since it removes the manual "mark as squash/fixup" step and the risk of reordering the wrong line:
1. **Create the fixup/squash commit** — `rtk git commit --fixup=<commit>` (keeps target's message) or `rtk git commit --squash=<commit>` (lets you edit the combined message later). Both prefix the message with `fixup!`/`squash!` and target `<commit>`.
2. **Confirm operation** — rewriting history still requires explicit approval before the rebase runs.
3. **Execute** — `rtk git rebase --autosquash HEAD~N` (or `-i --autosquash` to review the plan first); git reorders and marks the `fixup!`/`squash!` commits against their targets automatically.
4. **Handle merge conflicts** — same as manual rebase: offer resolution or abort, never resolve automatically without confirmation.
### For cherry-picking:
1. **Identify source commit(s)** — hash or branch reference
2. **Confirm destination branch** — cherry-pick will replay commits on current branch
3. **Execute cherry-pick** — `rtk git cherry-pick <commit-hash>`
4. **Handle conflicts** — offer conflict resolution or abort
5. **Report outcome** — successful replays, conflicts, or rejected commits
## Output format (for agent consumption)
Return structured JSON:
```json
{
"operation": "create|amend|squash|cherry-pick",
"status": "success|conflict|rejected",
"message": "Commit message or error description",
"commit_hash": "abc1234",
"semver_impact": "MAJOR|MINOR|PATCH|none",
"breaking_change": true|false,
"confirmation_required": true|false,
"details": {
"type": "feat",
"scope": "api",
"description": "add user authentication",
"body": "optional body text",
"footers": ["Fixes: #123", "Refs: #456", "ADR: 0012", "Reviewed-by: Alice", "Co-authored-by: Bob <bob@example.com>", "Signed-off-by: Alice <alice@example.com>"]
}
}
```
For interactive human use, format as readable prose with clear prompts and previews.
## Reference
If a footer or type/scope edge case isn't covered above, read `references/conventional-commits-spec.md` for the full specification.
For the Why / Implementation Notes / Impact body structure and the full trailer list, read `references/commit-template.md`.

View File

@@ -0,0 +1,66 @@
---
source_keys:
- org-commit-conventions
---
# Commit Message Body Template
Use this structure for the body/footer of any non-trivial commit (skip sections that don't apply — do not leave placeholders in the actual commit).
```
<type>(<scope>): <concise summary>
```
The header is required. Describe the intended outcome, not the implementation.
## Why
Explain why this change exists. This is the most valuable part of the commit — the diff already shows *what* changed; future maintainers (human or AI) need *why*.
Include, where applicable:
- Problem being solved
- User or business need
- Bug or root cause
- Important context not visible in the code
Omit if the reason is immediately obvious.
## Implementation Notes
Capture decisions that are difficult to infer from the code:
- Why this approach was chosen
- Important assumptions or invariants
- Constraints imposed by external systems
- Tradeoffs or intentional compromises
- Non-obvious implementation details
- Workarounds or temporary solutions
Do NOT describe the diff ("renamed X", "added Y"). Omit if there's nothing worth preserving.
## Impact
Document effects future developers should know about:
- Behavior changes
- Breaking changes
- Performance implications
- Security considerations
- Migration or deployment requirements
- Compatibility concerns
- Follow-up work or known limitations
Omit if there are no noteworthy impacts.
## Trailers
Structured metadata for traceability and tooling. Use only the trailers that apply:
```
Fixes:
Refs:
ADR:
RFC:
Design:
Co-authored-by:
Reviewed-by:
Signed-off-by:
BREAKING CHANGE:
```

View File

@@ -0,0 +1,170 @@
---
source_keys:
- conventional-commits-spec
- commitlint-config-conventional
---
# Conventional Commits Specification (v1.0.0)
Conventional Commits is a lightweight convention on top of commit messages that provides a set of rules for creating an explicit commit history. It enables automated tooling (CHANGELOG generation, semantic version bumping) and structured filtering.
## Message Format
```
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
```
Each section is separated by a blank line. The header is the only required part.
## Rules
| Element | Rule |
|---|---|
| `type` | Required. Lowercase noun. |
| `scope` | Optional. Noun in parentheses directly after type: `feat(api):`. |
| `description` | Required. Immediately follows `type/scope: `. Imperative mood, no trailing period. |
| `body` | Optional. Begins one blank line after description. Free-form prose, multiple paragraphs allowed. Lines max 100 characters. |
| `footer(s)` | Optional. Begins one blank line after body (or description). `<token>: <value>` format. Lines max 100 characters. |
| `BREAKING CHANGE` | Must be uppercase. Either a footer token or signalled by `!` before the colon. |
## Standard Types
The spec itself mandates only `feat` and `fix`. The 11-type set below is the de-facto standard from `@commitlint/config-conventional` (Angular commit message guidelines), not a spec requirement — but it is what this skill validates against.
### 11-type set (commitlint/config-conventional)
| Type | Meaning | SemVer impact | Appears in CHANGELOG |
|---|---|---|---|
| `feat` | New user-visible feature | MINOR | Yes |
| `fix` | Bug fix | PATCH | Yes |
| `perf` | Performance improvement, no API change | PATCH | Yes |
| `revert` | Reverts a previous commit | PATCH | Yes |
| `docs` | Documentation only | none | No |
| `style` | Formatting, whitespace — no logic change | none | No |
| `refactor` | Code restructuring — no feature or fix | none | No |
| `test` | Adding or fixing tests | none | No |
| `build` | Build system or external dependency changes | none | No |
| `ci` | CI configuration and scripts | none | No |
| `chore` | Anything not fitting above | none | No |
A `BREAKING CHANGE` footer or `!` on **any** type always triggers a MAJOR bump.
## Breaking Changes
Two equivalent notations:
**`!` in header** (preferred — visible in `git log --oneline`):
```
feat!: drop support for Node 6
feat(api)!: remove deprecated endpoint
```
**`BREAKING CHANGE` footer** (machine-readable body):
```
feat: allow config to extend other configs
BREAKING CHANGE: `extends` key now used for extending config files
```
**Both together** (most explicit):
```
feat!: drop support for Node 6
BREAKING CHANGE: use JavaScript features not available in Node 6.
```
Rules:
- `BREAKING CHANGE` must be all caps.
- `BREAKING-CHANGE` (hyphenated) is an accepted synonym.
- Any type can carry a breaking change, not just `feat`.
- The footer value must describe what broke.
## Footer Token Rules
```
<token>: <value>
<token> #<value> # for issue references
```
- Tokens use hyphens for word separation: `Reviewed-by`, `Co-authored-by`, `Refs`.
- Exception: `BREAKING CHANGE` (space allowed, uppercase).
- Multiple footers allowed, one per line.
- Blank line required before the footer block.
Valid footer examples:
```
Reviewed-by: Z
Refs: #123
Co-authored-by: Alice <alice@example.com>
BREAKING CHANGE: the `--format` flag now requires a value
```
## Examples
Minimal — no body, no footer:
```
docs: correct spelling of CHANGELOG
```
With scope:
```
feat(lang): add Polish language
```
Breaking change via `!`:
```
feat!: send an email to the customer when a product is shipped
```
Breaking change via footer:
```
feat: allow provided config object to extend other configs
BREAKING CHANGE: `extends` key in config file is now used for extending other config files
```
Multi-paragraph body with multiple footers:
```
fix: prevent racing of requests
Introduce a request id and a reference to latest request. Dismiss
incoming responses other than from latest request.
Remove timeouts which were used to mitigate the racing issue but are
obsolete now.
Reviewed-by: Z
Refs: #123
```
Revert:
```
revert: let us never again speak of the noodle incident
Refs: 676104e, a215868
```
## commitlint Constraints (config-conventional)
| Constraint | Value |
|---|---|
| Header max length | 100 characters |
| Subject must not end with `.` | enforced |
| Subject must be lowercase (not sentence-case or UPPER-CASE) | enforced |
| Body / footer line max length | 100 characters |
| Type must be one of the 11 standard types | error if not |
| Blank line before body | warning |
| Blank line before footer | warning |
## SemVer Mapping Summary
| Condition | SemVer bump |
|---|---|
| `fix`, `perf`, `revert` | PATCH |
| `feat` | MINOR |
| Any type with `BREAKING CHANGE` or `!` | MAJOR |
| All other types (`docs`, `style`, `refactor`, `test`, `build`, `ci`, `chore`) | none |

View File

@@ -0,0 +1,40 @@
---
topic: commits
source_keys:
- conventional-commits-spec
- commitlint-config-conventional
- org-commit-conventions
- context7-git-htmldocs
---
# Research Sources for git:commits Skill
Sources extracted from the git plugin research phase. Only sources that directly informed this skill are listed; sibling skills (git:branches, git:history, git:remotes, etc.) have their own sources.md.
## conventional-commits-spec
- **Description:** Conventional Commits Specification (v1.0.0) — message format, types, breaking changes, footer rules
- **Research doc:** plugins/git/docs/research/docs/git/commits.md § "Conventional Commits Specification (v1.0.0)"
- **Contributing files:** SKILL.md, references/conventional-commits-spec.md
- **Status:** extracted
## commitlint-config-conventional
- **Description:** commitlint config-conventional preset — validation constraints (max 100 chars header, no trailing periods, lowercase type, 11-type set enforcement)
- **Research doc:** plugins/git/docs/research/docs/git/commits.md § "commitlint Constraints (`config-conventional`)"
- **Contributing files:** SKILL.md, references/conventional-commits-spec.md
- **Status:** extracted
## org-commit-conventions
- **Description:** Organization commit message body template and git conventions (atomic commits, no `--no-verify`, no force-push main/master, `rtk git` wrapper) — content fully embedded in this skill; the org's `core/instructions/git.md` and `core/instructions/commits.md` are provenance only and are not a live dependency
- **Research doc:** core/instructions/commits.md, core/instructions/git.md (org convention, not part of the plugin's research corpus)
- **Contributing files:** SKILL.md, references/commit-template.md
- **Status:** extracted
## context7-git-htmldocs
- **Description:** Official Git HTML documentation — `git commit --squash`/`--fixup` and `git rebase --autosquash` flag semantics
- **Research doc:** plugins/git/docs/research/docs/git/cli-reference.md
- **Contributing files:** SKILL.md
- **Status:** extracted

View File

@@ -0,0 +1,24 @@
# git-history
Inspect git history — log queries, bisect, and locating problematic commits.
## What it does
This skill handles history inspection within the git workflow suite. It queries logs with pickaxe/line-range/custom formats, runs bisect to find bug-introducing commits, and locates commits for downstream cherry-picking or reverting. It returns structured results for agent composition. Rebase, squash, fixup, and other history-rewriting operations are owned by git-commits, not this skill.
## Usage
```
/git-history
```
Describe your history task: search logs, bisect for a regression, or locate a specific commit. The skill will query history and return structured results.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents |
| `references/git-log-format.md` | Full log format placeholders, diff-filter letters, `-L` syntax, ancestry filters, diff output-control flags |
| `references/sources.md` | Research sources and provenance |
| `references/README.md` | Index of the references directory |

View File

@@ -0,0 +1,96 @@
---
name: git-history
description: >
Inspect git history: query logs with pickaxe, line-range, or custom formats; find bug origins via bisect; locate problematic commits for cherry-picking or reverting. Use when investigating history, tracing when a change happened, or finding the commit that broke something. Return structured results for downstream agents. Do not use for authoring or formatting commit messages, or executing rebase/squash/fixup operations — use git-commits for that.
metadata:
category: git
source_keys:
- git-scm-bisect-docs
- git-scm-log-docs
- git-scm-diff-docs
allowed-tools: Bash
---
## Gotchas
- **Pickaxe searches (`-S` vs `-G`)**: `-S"string"` finds commits where string count changed; `-G"regex"` finds any line matching regex in diffs. They're not equivalent: a line replaced (one removal + one addition) matches `-G` but not `-S` if count is unchanged.
- **`--follow` only works for single files**: it traces renames but fails with multiple paths or directory globs. Use `git log -- <single-file>` or query without `--follow`.
- **Bisect with skips**: if bisect cannot pinpoint a commit because the culprit is adjacent to skipped commits, it reports "cannot find exact culprit" and lists candidates. This is not a failure — it's as precise as the skip range allows.
- **Interactive rebase is non-recoverable on mistake**: there's no undo once `rebase -i` starts. Suggest `git reflog` to recover if the user realizes mid-way they selected the wrong commits.
- **`-L` (line-range history) requires exact line numbers or regex patterns**: off-by-one errors omit the target range. Test the range with `git log -L` before offering it to users.
## Query Logs and Locate Commits
Default to `git log --oneline` for quick inspection. For deeper queries:
- **Find when a string appeared or disappeared**: Use `git log -S"string"` (count-sensitive, finds adds/removes). If you need any mention of the string in diffs, use `git log -G"regex"` instead. Add `--pickaxe-regex` to treat the `-S` string as a POSIX ERE, and `--pickaxe-all` to show every changed file in a matching changeset, not just the matching ones. Binary files are searched by `-S`; `-G` ignores them unless `--text` is also supplied.
- **Trace changes to a specific line or function**: Use `git log -L <start>,<end>:<file>` or `git log -L :<function>:<file>` (requires function name heuristic). This shows the evolution of that range across all commits.
- **Filter by change type**: Use `git log --diff-filter=<type>` (A=added, M=modified, D=deleted, R=renamed) to narrow to specific file operations.
- **Mainline-only history through merges**: Use `--first-parent` to follow only the integration branch and skip merged-in side-branch commits; combine with `--merges`/`--no-merges` or `--ancestry-path`/`--min-parents`/`--max-parents` for other ancestry-graph filtering — see `references/git-log-format.md` for the full set.
- **Custom format for structured output**: Construct format string with `%h` (hash), `%s` (subject), `%an` (author), `%ar` (relative date), `%b` (body). Example: `git log --format="%h | %s | %an (%ar)"`.
- **File-specific history with renames**: Use `git log --follow -- <file>` (single file only). Without `--follow`, log stops at the rename boundary.
## Bisect to Find Blame Commit
Use bisect when hunting for the commit that introduced a bug or behaviour change. Binary search reduces iterations from O(N) to O(log N).
**Basic manual flow:**
```bash
git bisect start
git bisect bad [HEAD] # mark current (or specified) as broken
git bisect good <commit> # mark known-good baseline
# Git checks out midpoint; test it manually
git bisect good # if test passes
git bisect bad # if test fails
# Repeat until git reports "X is the first bad commit"
git bisect reset # return to original HEAD
```
**Automated with `git bisect run`:** if a test command exists, use `git bisect run <cmd>`. Git interprets the exit code: `0`=good, `1-124`=bad, `125`=skip (build broken), `126-127`=POSIX shell errors treated as bad, `128+`=**aborts the bisect session entirely** (not treated as bad — a crashed test script can silently end the search).
**With skip:** if a commit is untestable (broken build), use `git bisect skip` to exclude it without manually deciding good/bad. If the first-bad is adjacent to skips, bisect reports it cannot pinpoint but lists candidates.
**Undoing a wrong good/bad call:** `git bisect log` prints the session's decision history; save it (`git bisect log > bisect.log`), edit out the mistaken entry, then `git bisect reset && git bisect replay bisect.log` to resume from the corrected log instead of restarting the whole search.
**Narrowing and speeding up the search:** `git bisect start HEAD v1.2 -- src/` limits bisection to a path, cutting the number of trials. `--no-checkout` updates the `BISECT_HEAD` ref instead of checking out a working tree (useful for tests that don't need one; automatic in bare repos). `--first-parent` follows only first parents at merges, finding the integration commit that introduced a regression while ignoring broken side branches.
**Inspecting remaining candidates visually:** `git bisect visualize` (alias `view`) opens the suspects in gitk; add `--stat` or `-p` to show diffstat or full patches instead. Falls back to `git log` when no graphical display is detected.
**For non-regression hunts:** use `git bisect start --term-new <new> --term-old <old>` to search for a property change instead of a bug (e.g., performance regression). Then use the custom terms instead of `good`/`bad`.
For rebase execution (interactive rebase, squash/fixup/reword, conflict handling) see git-commits — it owns history-rewriting operations. This skill only locates commits and reports on history; it does not execute rebases.
## Find and Manipulate Problematic Commits
Once a commit is identified (via log query or bisect), offer cherry-pick or revert. This section is general git knowledge, not sourced from `history-inspection.md` — `git-branches`'s SKILL.md explicitly delegates cherry-pick/revert here (see its Merging section), which is why this skill carries them rather than treating them as out of scope:
- **Cherry-pick**: `git cherry-pick <commit>` copies a commit's changes onto current HEAD. Use when backporting fixes to other branches.
- **Revert**: `git revert <commit>` creates a new commit that undoes the changes. Use when un-applying a merged commit without rewriting history.
- **Blame for context**: `git blame <file>` shows which commit last changed each line. Use to trace a specific line back to its introducing commit.
## Inspect Diffs
Diff-output tuning is in scope too: `--stat` for a diffstat summary, `--word-diff` for word-level (not line-level) changes, and whitespace flags (`-w`, `--ignore-blank-lines`) to suppress noise from reformatting. See `references/git-log-format.md` for the full flag set.
## Return Results Structured
For agent consumption, return:
- **Commit SHA** (full or abbreviated as appropriate)
- **Subject line** (from `%s`)
- **Author and date** (from `%an` and `%ar`)
- **Action taken or recommended** (e.g., "Found via bisect", "Offer cherry-pick to main", "Rebase conflicts detected")
Example for agent:
```
Found first bad commit: abc1234
Subject: fix null pointer in parser
Author: Alice (2 weeks ago)
Recommendation: Backport to release branch via cherry-pick
```
## Reference
For the full log format placeholder catalogue, named format presets, `--diff-filter` letters, `-L` range syntax, ancestry filters, and `git diff` output-control flags, read `references/git-log-format.md`.

View File

@@ -0,0 +1,15 @@
---
source_keys:
- git-scm-bisect-docs
- git-scm-log-docs
- git-scm-diff-docs
---
# References
This directory contains provenance metadata and research sources for the `git-history` skill.
## Files
- `sources.md` — Extracted research sources and their contributing documents
- `git-log-format.md` — Full `git log` format placeholder catalogue, named format presets, `--diff-filter` letters, `-L` line-range syntax, ancestry filters, and `git diff` output-control flags

View File

@@ -0,0 +1,232 @@
---
topic: git-log-format
source_keys:
- git-scm-log-docs
- git-scm-diff-docs
---
## Named Format Presets (`--format` / `--pretty`)
| Name | Output |
|---|---|
| `oneline` | `<hash> <title>` |
| `short` | hash, author, title |
| `medium` | hash, author, date, full message (default) |
| `full` | adds committer |
| `fuller` | separate author/committer dates |
| `reference` | `<abbrev> (<title>, <date>)` — for use in commit messages |
| `email` | RFC 2822 email format |
| `raw` | full object as stored in the object database |
| `format:<str>` | custom template with placeholders |
## Custom Format Placeholders
**Commit identity:**
| Placeholder | Meaning |
|---|---|
| `%H` | full commit hash |
| `%h` | abbreviated commit hash |
| `%T` | tree hash |
| `%t` | abbreviated tree hash |
| `%P` | full parent hashes |
| `%p` | abbreviated parent hashes |
**Author:**
| Placeholder | Meaning |
|---|---|
| `%an` | author name |
| `%aN` | author name (mailmap-resolved) |
| `%ae` | author email |
| `%aE` | author email (mailmap-resolved) |
| `%ad` | author date (respects `--date=`) |
| `%ar` | author date, relative |
| `%at` | author date, UNIX timestamp |
| `%ai` | author date, ISO 8601-like |
| `%aI` | author date, strict ISO 8601 |
| `%as` | author date, short (YYYY-MM-DD) |
**Committer:**
| Placeholder | Meaning |
|---|---|
| `%cn` | committer name |
| `%ce` | committer email |
| `%cd` | committer date (respects `--date=`) |
| `%cr` | committer date, relative |
| `%ct` | committer date, UNIX timestamp |
| `%ci` | committer date, ISO 8601-like |
| `%cs` | committer date, short |
**Message:**
| Placeholder | Meaning |
|---|---|
| `%s` | subject (first line) |
| `%f` | sanitized subject (filename-safe) |
| `%b` | body (everything after blank line following subject) |
| `%B` | raw body (subject + body) |
| `%N` | commit notes |
**Refs and decorations:**
| Placeholder | Meaning |
|---|---|
| `%d` | ref names (like `--decorate`) |
| `%D` | ref names without surrounding parentheses |
| `%S` | ref name by which commit was reached (requires `--source`) |
| `%(decorate[:opts])` | custom decorated refs; options: `prefix=`, `suffix=`, `separator=`, `pointer=`, `tag=` |
| `%(describe[:opts])` | like `git describe`; options: `tags=`, `abbrev=`, `match=`, `exclude=` |
**GPG signature:**
| Placeholder | Meaning |
|---|---|
| `%G?` | status: `G`=good, `B`=bad, `U`=unknown, `X`=expired, `R`=revoked, `N`=no signature |
| `%GS` | signer name |
| `%GK` | signing key ID |
**Trailers:**
```
%(trailers[:key=<k>][,only][,separator=<s>][,unfold][,keyonly][,valueonly])
```
**Formatting / color:**
| Placeholder | Meaning |
|---|---|
| `%n` | newline |
| `%%` | literal `%` |
| `%Cred` / `%Cgreen` / `%Cblue` / `%Creset` | terminal colors |
| `%C(<spec>)` | color per git-config spec |
| `%<(<n>[,trunc])` | right-pad field to width n |
| `%>(<n>)` | left-pad to width |
**Reflog** (requires `-g` / `--walk-reflogs`):
| Placeholder | Meaning |
|---|---|
| `%gD` | reflog selector (e.g. `refs/stash@{1}`) |
| `%gd` | shortened reflog selector |
| `%gs` | reflog subject |
## Pickaxe Search: -S and -G
**`-S<string>`** — finds commits where the **count** of `<string>` changed (i.e. the string was added or removed net). Does not match commits where the string merely appears in a diff hunk without a count change.
```bash
git log -S"my_function"
git log -S"my_function" --pickaxe-regex # treat as POSIX ERE
git log -S"my_function" --pickaxe-all # show all files in matching changesets
```
**`-G<regex>`** — finds commits where any added or removed **line** in the patch matches `<regex>`. Broader than `-S`: matches whenever the pattern appears in diff text regardless of count.
```bash
git log -G"frotz\(nitfol"
```
**Critical distinction:** given a diff that removes one occurrence of `foo` and adds one occurrence of `foo` (net change = 0):
- `-S"foo"` — does **not** match (count unchanged)
- `-G"foo"` — **matches** (pattern appears in patch text)
Binary files are searched by `-S`; ignored by `-G` unless `--text` is supplied.
## --diff-filter (full table)
Selects commits (in `git log`) or files (in `git diff`) by change type:
| Letter | Meaning |
|---|---|
| `A` | Added |
| `C` | Copied |
| `D` | Deleted |
| `M` | Modified |
| `R` | Renamed |
| `T` | Type changed (regular file ↔ symlink ↔ submodule) |
| `U` | Unmerged (conflict) |
| `X` | Unknown (indicates a git bug) |
| `B` | Pairing broken |
Lowercase letters **exclude** that type:
```bash
git log --diff-filter=ad # exclude added and deleted files
git log --diff-filter=M # only show commits with modified files
```
`C` and `R` only appear when copy/rename detection is enabled (`-C`, `-M` flags or `diff.renames` config).
## -L — Line Range History (full syntax)
Traces the evolution of a specific range of lines or a named function through commits. Implies `--patch`.
```bash
git log -L 10,20:file.txt
git log -L /start_pattern/,/end_pattern/:file.txt
git log -L :myfunction:src/app.c
git log -L /init/,+15:config.py # 15 lines after first match of /init/
```
Range formats:
| Format | Meaning |
|---|---|
| `<n>` | Absolute line number (1-based) |
| `/<regex>/` | First line matching regex from previous range end |
| `^/<regex>/` | First line matching regex from file start |
| `+<n>` / `-<n>` | Offset relative to `<start>` (end position only) |
Limitations: incompatible with `--raw`, `--numstat`, `--shortstat`, `--name-only`, `--name-status`, `--check`. Cannot use pathspec limiters alongside `-L`.
## Graph and Ancestry Filters
```bash
git log --first-parent # at merges, follow only first parent (mainline evolution)
git log --merges # only merge commits (≥2 parents); equivalent to --min-parents=2
git log --no-merges # only non-merge commits; equivalent to --max-parents=1
git log --ancestry-path D..M # only commits actually on the path from D to M
git log --min-parents=<n> # include only commits with ≥ n parents
git log --max-parents=<n> # include only commits with ≤ n parents
```
`--ancestry-path` is significant: without it, `D..M` includes all commits reachable from M but not D — including side branches that merged into the path. With it, only commits directly between D and M are shown.
## git diff — Output Control
### --stat
```bash
git diff --stat # diffstat: file names + ± bar
git diff --stat=<width>,<name-width>,<count>
git diff --compact-summary # alongside --stat: shows new/gone, +x/-x (executable), +l (symlink)
git diff --numstat # machine-readable: <added>\t<deleted>\t<path>; - for binary
```
### --name-only / --name-status
```bash
git diff --name-only # only filenames, one per line
git diff --name-status # status letter + filename per line
```
`--name-status` uses the same status letters as `--diff-filter`.
### --word-diff
```bash
git diff --word-diff # inline word-level diff with [-removed-] {+added+} markers
git diff --word-diff=color # color only, no markers
git diff --word-diff=porcelain # machine-readable: +/- prefixed lines, ~ for newlines
git diff --word-diff-regex=<re> # define what counts as a "word"
```
### Whitespace Flags
| Flag | Effect |
|---|---|
| `-b` / `--ignore-space-change` | Treat any run of whitespace as equivalent; ignore trailing whitespace |
| `-w` / `--ignore-all-space` | Ignore all whitespace completely |
| `--ignore-space-at-eol` | Ignore whitespace at end-of-line only |
| `--ignore-blank-lines` | Ignore changes consisting entirely of blank lines |
| `-I<regex>` / `--ignore-matching-lines=<re>` | Ignore changes where all changed lines match regex |

View File

@@ -0,0 +1,31 @@
---
topic: history-inspection
source_keys:
- git-scm-bisect-docs
- git-scm-log-docs
- git-scm-diff-docs
---
## git-scm-bisect-docs
Git bisect documentation covering binary search through commit history to find the commit that introduced a bug. Includes manual flow, automated mode with exit codes, skip patterns, and visualization options.
- **Research doc:** plugins/git/docs/research/docs/git/history-inspection.md
- **Doc heading:** `## git bisect`
- **Contributing files:** SKILL.md
## git-scm-log-docs
Git log documentation covering format presets, custom format placeholders (commit identity, author, committer, message, refs, GPG signature), pickaxe search (`-S` and `-G`), `--follow` for file renames, `--diff-filter`, and line-range history (`-L`).
- **Research doc:** plugins/git/docs/research/docs/git/history-inspection.md
- **Doc heading:** `## git log — Format and Filtering`
- **Contributing files:** SKILL.md, references/git-log-format.md
## git-scm-diff-docs
Git diff documentation covering output control (--stat, --name-only, --name-status, --word-diff) and whitespace handling flags.
- **Research doc:** plugins/git/docs/research/docs/git/history-inspection.md
- **Doc heading:** `## git diff — Output Control`
- **Contributing files:** references/git-log-format.md

View File

@@ -0,0 +1,24 @@
# git-remotes
Manage git remote repositories — add/remove/configure remotes, push/pull with safety checks, fetch with pruning, and multi-remote workflows.
## What it does
This skill handles remote operations within the git workflow suite. It manages remote configuration (add, remove, rename), fetch operations with pruning, push operations with force-push safety (`--force-with-lease --force-if-includes`), and pull strategies (fast-forward, rebase, merge). It returns structured results suitable for agent composition.
## Usage
```
/git-remotes
```
Describe your remote operation: add a remote, push, pull, fetch, or configure tracking. The skill will handle the operation with appropriate safety checks and return results.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents |
| `references/README.md` | Describes the references directory contents |
| `references/remotes.md` | Full `set-url` variants, shallow-clone/fetch options, force-push mitigation detail, and pull config precedence |
| `references/sources.md` | Research sources and provenance |

View File

@@ -0,0 +1,123 @@
---
name: git-remotes
description: >
Manage git remote repositories — add/remove/configure remotes, push/pull with safety checks,
handle fetch patterns and tracking branch updates, support multi-remote workflows.
Use when automating remote operations, pushing with force-push safety, fetching with pruning,
pulling with divergence resolution, or managing multi-remote tracking. Include indirect triggers:
any git operation that touches a remote, even if the user doesn't explicitly name the remote.
Do not use when working with local git history, commits, branches, or staging — use git-history
or git-branches instead.
metadata:
category: git-workflow
source_keys:
- git-scm-remote-docs
- git-scm-fetch-docs
- git-scm-push-docs
- git-scm-pull-docs
- context7-git-htmldocs
---
## Gotchas
- **Never force-push `main` or `master`, under any circumstances** — this is a hard refusal, not a `confirm: true` gate. If a force-push targets one of these branches, decline and explain why, regardless of how the request is confirmed.
- **Force-push to any other branch requires explicit confirmation** — never execute `git push --force` or `git push -f` without user/agent approval. Always ask or require `confirm: true` flag first.
- **`--force-with-lease` alone is not safe** — background processes (IDE plugins, cron jobs) that run `git fetch` silently defeat the protection. Always combine with `--force-if-includes` or use explicit SHA form `--force-with-lease=<ref>:<sha>`.
- **Prune doesn't touch tags by default** — `git fetch --prune` leaves orphaned tags. Use `git fetch --prune --prune-tags` or configure `fetch.pruneTags true` globally.
- **Pull with rebase rewrites history** — only safe for unpublished work. Rebasing already-pushed commits breaks everyone downstream. Check what's been pushed before rebasing.
- **`git remote show` requires network access** — use `-n` flag for cached data if working offline. `git remote -v` lists URLs without network queries.
- **Pull behavior defaults shift between Git versions** — older versions default to merge, newer versions to `--ff-only`. Always set `pull.ff only` explicitly for deterministic behavior.
## Operations
### Remote Management
Use these to configure which remotes you push to and pull from:
- **Add a remote**: `git remote add <name> <url>` or `git remote add -f <name> <url>` to fetch immediately
- **Remove a remote**: `git remote remove <name>` (deletes remote + all tracking refs + config)
- **Rename a remote**: `git remote rename <old> <new>`
- **Inspect remotes**: `git remote -v` (show URLs) or `git remote show <name>` (live tracking status, requires network)
- **Set-url separately for fetch vs. push**: `git remote set-url --push <name> <url>` changes only where pushes go — but fetch and push URLs must still reference the same repository. For genuine fetch-from-A / push-to-B workflows, use two separate named remotes instead; `--push` cannot do this. Full `set-url` variants (regex-targeted replace, `--add`, `--delete`): `references/remotes.md`.
- **Remove a stale URL**: `git remote set-url --delete <name> <regex>`
- **Inspect effective URLs**: `git remote get-url <name>` (shows URL after `insteadOf` rewrites) or `git remote get-url --push --all <name>` (all push URLs)
- **Track only one branch**: `git remote add -t <branch> <name> <url>` (repeatable), or suppress tag import entirely with `git remote add --no-tags <name> <url>`
- **Mirror a remote**: `git remote add --mirror=fetch <name> <url>` mirrors all refs locally (bare repos only); `--mirror=push` makes every push behave like `--mirror`
- **Prune stale tracking refs without fetching**: `git remote prune <name>` (add `--dry-run` to preview first)
- **Set the remote's default branch pointer**: `git remote set-head <name> -a` (auto-detect, requires a prior fetch), `git remote set-head <name> <branch>` (explicit), or `git remote set-head <name> -d` (delete `refs/remotes/<name>/HEAD`)
### Fetch Operations
Use these to update your tracking branches without touching your local branches:
- **Fetch from one remote**: `git fetch <remote>` — fetches all branches
- **Fetch one branch only**: `git fetch <remote> <branch>` — stores the result in `FETCH_HEAD`, not a tracking ref
- **Fetch from all remotes**: `git fetch --all` with optional `--prune` to clean up stale tracking refs
- **Prune properly**: Use `git fetch --all --prune --prune-tags` to clean both branches and tags
- **Configure auto-prune**: Set `git config --global fetch.prune true` to auto-prune on every fetch across all remotes (or `remote.<name>.prune` to scope it to one remote)
- **Shallow clones**: `--depth=<n>` to deepen or create a shallow clone, `--unshallow` to convert to full history, `--update-shallow` to allow the shallow boundary to move. Details and the default fetch refspec: `references/remotes.md`.
Fetch never modifies your local branches — it only updates remote-tracking branches (`refs/remotes/origin/*`).
### Push Operations
Use these to send your commits upstream. Default: safe push to same-named branch on the remote.
- **Basic push**: `git push <remote> <branch>` — pushes to same-named remote branch
- **Set upstream**: `git push -u <remote> <branch>` — push and configure this branch to track the remote
- **Multi-remote push**: `git push origin develop` and `git push staging develop` sequentially, or use `git remote set-url --add <name> <url>` to push to multiple remotes with one command
- **Force-push safety**: Always use `git push --force-with-lease --force-if-includes <remote> <branch>` over bare `--force`. Require explicit confirmation first — and never for `main`/`master` (see Gotchas). `--force-if-includes` is a no-op without `--force-with-lease`. If background tools (IDE, cron) auto-fetch and could poison the lease check, use a dedicated push-only remote instead — see `references/remotes.md`.
- **Server-side enforcement**: `receive.denyDeletes`, `receive.denyDeleteCurrent`, and `receive.denyNonFastForwards` are enforced on the remote regardless of local flags — a hardened server rejects the push even with `--force`.
- **Delete remote branch**: `git push <remote> --delete <branch>` (not `:<branch>` syntax; clearer and cleaner)
- **Push everything**: `git push --all` (all local branches) or `git push --tags` (all tags)
- **Push a single tag**: `git push origin <tag>`
- **Delete remote branches with no local counterpart**: `git push --prune origin 'refs/heads/*:refs/heads/*'`
- **Force only part of a multi-ref push**: prefix the one refspec that needs it with `+`, e.g. `git push origin +main develop` forces `main` while safe-pushing `develop`
Refspec syntax is `[+]<src>[:<dst>]`:
| Pattern | Meaning |
|---|---|
| `<branch>` | Push to same-named remote branch |
| `<src>:<dst>` | Push `<src>` local ref to `<dst>` remote ref |
| `+<src>:<dst>` | Force this refspec (non-fast-forward allowed) |
| `:<branch>` | Delete remote `<branch>` |
| `refs/heads/*:refs/heads/*` | Glob: push all matching branches |
| `^refs/heads/dev-*` | Negative: exclude matching refs |
| `tag <name>` | Sugar for `refs/tags/<name>:refs/tags/<name>` |
### Pull Operations
Use these to fetch and integrate remote changes. Default strategy: `--ff-only` (fail if diverged, forcing a conscious choice).
- **Pull with fast-forward only**: `git pull --ff-only` (recommended default — fails if you've diverged, forcing a rebase/merge decision)
- **Pull with rebase**: `git pull --rebase` (replays your unpublished commits on top; linear history, but rewrites SHAs — only safe for unpublished work)
- **Pull with merge**: `git pull --no-rebase` (three-way merge commit; preserves original commits, non-linear)
- **Pull with rebase, preserving merges**: `git pull --rebase=merges` (like `--rebase`, but keeps intentional local merge commits during replay)
- **Pull without integrating**: `git pull --squash` collapses incoming commits into staged changes without committing — you write the commit message
- **Set pull strategy globally**: `git config pull.ff only` (or `pull.rebase true`; respects branch-specific overrides via `branch.<name>.rebase`). Full precedence order (CLI flag > `pull.rebase` > `branch.<name>.rebase` > `branch.autoSetupRebase`): `references/remotes.md`.
- **Check before rebasing**: Always verify your commits haven't been pushed before using `--rebase`. Rebasing published commits breaks everyone downstream.
- **Merge strategy default**: Git 2.34+ defaults to the `ort` merge strategy (`recursive` is now just an alias for it). Strategy options like `-X ours`, `-X theirs`, `-X ignore-space-change` still pass through unchanged.
- **Submodules on pull**: `--recurse-submodules` only fetches submodules that are already checked out — newly added submodules are not initialized automatically. Use the `git-submodules` skill to initialize new ones.
If pull diverges and you haven't set a strategy, the operation fails — this is good, forces a conscious choice. Never auto-merge diverged branches without asking.
### Return Format (for agents)
Return structured output:
```json
{
"success": true,
"operation": "push",
"remote": "origin",
"branch": "main",
"output": "...",
"warnings": ["force-with-lease not confirmed"],
"recommendations": ["set pull.ff=only globally"]
}
```
On failure, include `error` field with root cause and recovery suggestion.

View File

@@ -0,0 +1,17 @@
---
source_keys:
- git-scm-remote-docs
- git-scm-fetch-docs
- git-scm-push-docs
- git-scm-pull-docs
- context7-git-htmldocs
---
# References
This directory contains provenance metadata and research sources for the `git-remotes` skill.
## Files
- `sources.md` — Extracted research sources and their contributing documents
- `remotes.md` — Full `set-url` variants, shallow-clone/fetch options, default fetch refspec, force-push mitigation detail, server-side deny policies, and pull config precedence

View File

@@ -0,0 +1,82 @@
---
topic: remotes
source_keys:
- git-scm-remote-docs
- git-scm-fetch-docs
- git-scm-push-docs
- git-scm-pull-docs
---
## `set-url` — full form
```bash
git remote set-url <name> <newurl> # replace the first fetch URL
git remote set-url <name> <newurl> <oldurl-regex> # replace only the URL matching regex
git remote set-url --push <name> <url> # change push URL only (must point at same repo)
git remote set-url --add <name> <url> # add an extra push URL (push to multiple remotes)
git remote set-url --delete <name> <regex> # remove URLs matching regex
```
`--push` changes only where pushes go — fetch and push URLs must still reference the same repository. For genuine fetch-from-A / push-to-B workflows, use two separate named remotes instead.
## Shallow clones and partial fetch
```bash
git fetch <remote> <branch> # fetch one branch only, stored in FETCH_HEAD (not a local/tracking ref)
git fetch --depth=<n> # deepen history, or create a shallow clone
git fetch --unshallow # convert a shallow clone to full history
git fetch --update-shallow # allow the fetch to update the shallow boundary
git fetch --refmap='' <remote> <branch> # fetch without updating any tracking ref (FETCH_HEAD only)
```
## Default fetch refspec
The default fetch refspec is `+refs/heads/*:refs/remotes/<name>/*`. The leading `+` forces the update — remote-tracking branches always mirror the remote exactly and provide no protection for local history. Fetch never touches your local branches, only remote-tracking refs.
## Force-push safety — full detail
`--force-with-lease` rejects the push if the remote ref moved since your last fetch. Three forms:
| Form | What it protects |
|---|---|
| `--force-with-lease` (bare) | All refs being pushed, checked against your remote-tracking branch |
| `--force-with-lease=<refname>` | Named ref only |
| `--force-with-lease=<refname>:<sha>` | Named ref must be at exact SHA — most stable |
**Caveat with the bare form:** any background process that runs `git fetch` (IDE plugin, cron job, editor auto-fetch) updates your remote-tracking branch, which can make the lease check pass even though someone else pushed in between. The protection is silently defeated.
Two mitigations:
```bash
# Option 1 — dedicated push-only remote: background tools fetch `origin`, you push
# through a separate remote that nothing else touches, so its tracking ref can't be
# poisoned by an unrelated fetch.
git remote add origin-push $(git config remote.origin.url)
git push --force-with-lease origin-push
# Option 2 — explicit SHA via a local tag, unaffected by tracking-branch state
git fetch
git tag base master
git rebase -i master
git push --force-with-lease=master:base master:master
```
`--force-if-includes` adds a second check on top of bare `--force-with-lease`: it verifies the remote-tracking tip actually appears in your local branch's reflog, i.e. you genuinely integrated it before rewriting. It is a no-op without `--force-with-lease`, and has no effect when the `--force-with-lease=<ref>:<sha>` form is used (that form already pins an exact SHA).
Safest combination: `git push --force-with-lease --force-if-includes origin`.
Remote-side policies (`receive.denyDeletes`, `receive.denyDeleteCurrent`, `receive.denyNonFastForwards`) are enforced server-side regardless of any local flag — a server configured this way rejects the push even with `--force`.
## Pull config precedence
Highest wins:
1. Command-line flag (`--ff-only` / `--rebase` / `--no-rebase`)
2. `pull.rebase` config (global or local)
3. `branch.<name>.rebase` (branch-specific override)
4. `branch.autoSetupRebase` (set automatically when the tracking branch was created)
```bash
git config --global pull.rebase true
git config branch.develop.rebase false # develop always merges, regardless of the global default
```

View File

@@ -0,0 +1,71 @@
---
# Research sources referenced by this skill
# Each entry documents where the skill's guidance came from.
---
## git-scm-remote-docs
**Description:** Git SCM official documentation for `git remote` command — remote configuration, add/remove/rename, URL management, inspection, and housekeeping.
**Source:** https://git-scm.com/docs/git-remote
- **Research doc:** plugins/git/docs/research/docs/git/remotes.md → `## Remote Management (`git remote`)`
**Contributing files:**
- SKILL.md (Remote Management section)
- references/remotes.md (`set-url` full form)
---
## git-scm-fetch-docs
**Description:** Git SCM official documentation for `git fetch` command — fetching from remotes, tracking branch updates, pruning stale refs, shallow clones, and refspecs.
**Source:** https://git-scm.com/docs/git-fetch
- **Research doc:** plugins/git/docs/research/docs/git/remotes.md → `## Fetching (`git fetch`)`
**Contributing files:**
- SKILL.md (Fetch Operations section, Gotchas)
- references/remotes.md (shallow clones, default fetch refspec)
---
## git-scm-push-docs
**Description:** Git SCM official documentation for `git push` command — pushing branches, tags, force-push safety (--force-with-lease, --force-if-includes), refspecs, and multi-remote workflows.
**Source:** https://git-scm.com/docs/git-push
- **Research doc:** plugins/git/docs/research/docs/git/remotes.md → `## Pushing (`git push`)`
**Contributing files:**
- SKILL.md (Push Operations section, Gotchas)
- references/remotes.md (force-push safety full detail, server-side deny policies)
---
## git-scm-pull-docs
**Description:** Git SCM official documentation for `git pull` command — fetch + merge/rebase strategies, divergence resolution (--ff-only, --rebase, merge), config precedence, and pull-specific gotchas.
**Source:** https://git-scm.com/docs/git-pull
- **Research doc:** plugins/git/docs/research/docs/git/remotes.md → `## Pulling (`git pull`)`
**Contributing files:**
- SKILL.md (Pull Operations section, Gotchas)
- references/remotes.md (pull config precedence)
---
## context7-git-htmldocs
**Description:** Context7 MCP library providing current Git documentation and API reference — used for validation of modern Git syntax, behavior, and config semantics. This is a blanket cross-cutting reference and does not map to a single heading in the research doc; it informed terminology and syntax checks across all sections.
**Source:** Context7 MCP / Git library
- **Research doc:** plugins/git/docs/research/docs/git/remotes.md (cross-cutting — no dedicated section)
**Contributing files:**
- SKILL.md (all sections)

View File

@@ -0,0 +1,24 @@
# git-submodules
Initialize, clone, update, and manage git submodules for multi-repository projects.
## What it does
This skill handles submodule operations within the git workflow suite. It initializes submodules, clones repositories with nested submodule dependencies, updates submodule pinning, and manages version control across multi-repo projects. The skill provides clean workflows for projects with complex dependency structures and returns structured results suitable for agent composition.
## Usage
```
/git-submodules
```
Describe your submodule task: initialize, clone, update, or manage versions. The skill will handle the operation and return structured results (operation, status, per-submodule details, conflicts, and a recovery `next_step` when applicable) suitable for agent composition.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents |
| `references/README.md` | Describes contents of references/ |
| `references/submodules.md` | Deep-dive reference: full flag tables, workflow patterns, safe-removal sequence, `absorbgitdirs`, `foreach` variables |
| `references/sources.md` | Research sources and provenance |

View File

@@ -0,0 +1,91 @@
---
name: git-submodules
description: >
Use when managing Git submodules: add dependencies as submodules, initialize and update nested repositories, sync URLs, inspect status (including detached HEAD and divergence), and safely remove submodules. Handles multi-repo projects with pinning, parallel operations, and recursive traversal. Use for both initial setup and ongoing maintenance workflows, even if the user doesn't explicitly say "submodule". Do not use for general git operations outside of submodule management.
metadata:
category: git
source_keys:
- git-scm-submodule-docs
---
## Concept
A submodule is a full Git repository embedded as a subdirectory inside a parent repository (the superproject). The superproject doesn't store the submodule's files — it stores a pointer to a specific commit SHA in the submodule's own history, and the two repos keep fully independent commit histories.
Two files govern a submodule, and they serve different audiences:
- **`.gitmodules`** — version-controlled, shared with collaborators. Defines each submodule's name, path, and canonical URL.
- **`.git/config`** — local only, populated by `git submodule init`. This is where local URL overrides live (e.g. a private mirror) — they never propagate to other clones.
The submodule's own `.git` directory lives at `.git/modules/<name>/` in the superproject, linked to the submodule's working tree via a `.git` pointer file. After `git submodule update`, the working tree normally ends up in **detached HEAD state** — see Gotchas.
## Gotchas
- **Detached HEAD by default.** `git submodule update` checks out a specific commit, not a branch. Work on a branch first, then update the pointer in the superproject. Commits made in detached state are invisible until pinned.
- **Two pushes required, in order.** Always commit and push the submodule first, then update and push the superproject's pointer. The superproject only stores a commit SHA — if that SHA isn't reachable on the submodule's remote yet, `git submodule update` fails for anyone who pulls the superproject before the submodule push lands.
- **`--recursive` is not default.** Most commands operate one level deep. Pass `--recursive` explicitly for nested submodules.
- **`.git/modules/` persists after `git rm`.** Manual cleanup is needed: `rm -rf .git/modules/<name>/`.
- **Detached HEAD detection.** Status prefix `+` means the checked-out commit differs from the superproject's recorded commit — normal after `update --remote`, but should be re-pinned before committing.
- **Relative URLs resolve against the remote, not the filesystem.** A `../foo.git` entry in `.gitmodules` is relative to the superproject's default remote URL.
- **Custom `update` commands are security-gated.** A `.gitmodules` entry of `update = !some-command` is never copied to `.git/config` by `git submodule init` — this stops a clone from silently executing arbitrary code.
## Conventions
- **Use `rtk git` for parent-repo operations.** Drop into the submodule directory only for submodule-specific git commands (committing/pushing inside the submodule itself) — mixing the two from the wrong working directory targets the wrong repo's history.
- **Check for a dirty submodule before committing the parent pointer.** After adding or updating a submodule, run `git status` in both the parent and the submodule. A `-dirty` suffix means the submodule has uncommitted local changes; committing the parent pointer now would pin a state no one else can reproduce, since those changes exist only in the local working tree.
## Operations
- **Clone a repo that has submodules**: `rtk git clone --recurse-submodules <url>` (one step, Git 2.13+) or `rtk git clone <url>` followed by `rtk git submodule update --init --recursive`.
- **Add a submodule**: `rtk git submodule add <url> <path>` (`-b <branch>` to track a branch instead of a pinned commit, `--depth 1` for a shallow clone, `-f` to force past a gitignored path or name conflict, `--name <name>` when the logical name should differ from the path). Stages a `.gitmodules` entry and a gitlink — a commit is still required.
- **Initialize**: `rtk git submodule init [<path>...]` copies submodule URLs from `.gitmodules` to `.git/config`. This is the point at which local URL overrides can be edited before fetching. Does not clone — use `update` (or `update --init` to run both in one step).
- **Update (clone + checkout)**: `rtk git submodule update --init --recursive` is the common case — checks out the recorded commit in detached HEAD. Add `--remote --merge` (or `--remote --rebase`) to track the branch tip instead, `--jobs <n>` for parallel clones, `-f` to discard local changes. Full flag table: `references/submodules.md`.
- **Inspect status**: `rtk git submodule status --recursive` (add `--cached` to show SHAs in the superproject index instead of the working tree). Status prefixes: `-` not initialized, `+` diverged from the superproject's recorded commit, `U` merge conflict.
- **Sync and rebind URLs**: `rtk git submodule sync --recursive` after an upstream URL rename propagates `.gitmodules` changes into `.git/config`. `rtk git submodule set-url <path> <url>` changes a URL directly; `rtk git submodule set-branch -b <branch> <path>` sets the tracking branch used by `update --remote`.
- **Override a submodule URL locally (private mirror)**: local-only, doesn't propagate to collaborators, and gets overwritten by the next `sync`. Full steps: `references/submodules.md`.
- **Run a command across all submodules**: `rtk git submodule foreach --recursive '<command>'`. Shell variables available inside `<command>` (`$name`, `$sm_path`, `$displaypath`, `$sha1`, `$toplevel`): `references/submodules.md`.
- **Deinit (unregister without removing)**: `rtk git submodule deinit <path>` (`--all` for every submodule, `-f` if local modifications are present) clears the `.git/config` section and empties the working tree. **`deinit` is not removal** — the `.gitmodules` entry and the gitlink in the superproject's index are untouched.
- **Safe removal** (destructive; confirm before executing) — full three-step sequence including the manual `.git/modules/` cleanup: `references/submodules.md`.
- **Move an embedded `.git` into `.git/modules/`**: `rtk git submodule absorbgitdirs [<path>...]` — needed when a submodule was created or copied without going through `git submodule add`. Details: `references/submodules.md`.
## Configuration
`.gitmodules` (version-controlled, shared with collaborators):
| Key | Purpose |
|---|---|
| `submodule.<name>.path` | Working tree path |
| `submodule.<name>.url` | Remote URL |
| `submodule.<name>.branch` | Branch used by `update --remote` |
| `submodule.<name>.update` | Default update procedure |
| `submodule.<name>.shallow` | Recommend shallow clone |
`.git/config` (local only, populated by `init`):
| Key | Purpose |
|---|---|
| `submodule.<name>.url` | Local URL override |
| `submodule.<name>.update` | Local procedure override |
| `submodule.fetchJobs` | Default parallelism for `update --jobs` |
| `submodule.recurse` | Auto-recurse submodule updates on `pull`/`push`/etc. |
```bash
rtk git config submodule.recurse true # keep submodules pinned automatically after every pull
```
## Agent output format
Return results as structured data:
```
operation: <clone|add|init|update|sync|set-url|set-branch|status|summary|absorbgitdirs|remove>
status: <success|error|partial>
message: <human-readable summary>
details:
- <submodule-path>: <state>
conflicts: [<submodule-path>, ...] # if any
next_step: <recovery action if applicable>
```
For errors, include the git command output and recommend recovery (e.g., `git submodule deinit`, force-update, or URL override).

View File

@@ -0,0 +1,15 @@
---
metadata:
source_keys:
- git-scm-submodule-docs
---
# References
## submodules.md
Deep-dive reference: full `update` flag table, workflow patterns (clone, add, keep-pinned, update-to-latest, override URL), the complete safe-removal sequence, `absorbgitdirs`, and `foreach` shell variables. Load when SKILL.md's condensed Operations list isn't enough detail.
## sources.md
Research sources that informed this skill — provenance chain for git-scm-submodule-docs reference material.

View File

@@ -0,0 +1,29 @@
---
topic: submodules
source_keys:
- git-scm-submodule-docs
---
## git-scm-submodule-docs
**Description:** Official git-scm.com reference for `git submodule` — all subcommands, flags, configuration keys, and behaviour details.
**Source:** https://git-scm.com/docs/git-submodule
- **Research doc:** plugins/git/docs/research/docs/git/submodules.md (whole-document reference — the research doc is organized by descriptive prose headings such as "Concept Overview" and "Key Commands" rather than a heading matching this slug; this key covers the entire doc, not a single section)
**Contributing files:**
- SKILL.md (all sections)
- references/submodules.md (all sections)
---
Other source keys extracted during the git plugin research phase inform sibling skills in the git workflow suite, not this one:
- `context7-git-htmldocs` — git:branches, git:history, git:remotes
- `git-scm-docs` — git:configuration
- `git-scm-worktree-docs` — git:worktrees
- `nvie-gitflow-post`, `atlassian-gitflow-tutorial`, `gitflow-cheatsheet` — git:branches
- `conventional-commits-spec`, `commitlint-config-conventional` — git:commits
- `git-scm-push-docs`, `git-scm-fetch-docs`, `git-scm-pull-docs`, `git-scm-remote-docs` — git:remotes
- `git-scm-bisect-docs`, `git-scm-log-docs`, `git-scm-diff-docs` — git:history

Some files were not shown because too many files have changed in this diff Show More