10 Commits

Author SHA1 Message Date
6cb47f81f6 fix(apm-workflow): surface the registries precondition in compile/install
SKILL.md says the apm experimental enable registries precondition
applies "anywhere — configure, install or publish," but only
configure.md actually carried it. compile.md's Publish flow and
install.md's dependency resolution can both hit a named registry and
silently no-op with no error if the precondition was never run, yet
neither file mentioned it — contradicting the skill's own promise that
each reference file is self-contained for its concern. Add a one-line
cross-reference to configure.md's Gotchas in each.

Found by an independent post-closure audit of #99 (agent-audit +
skill-audit re-run against every changed skill/agent).
2026-08-30 19:31:21 +00:00
0c0df46ac9 fix(gitea-releases): hedge the unconfirmed tag-deletion direction
The retrofit (dfacf05) collapsed a Gotcha into a bidirectional claim —
"deleting a tag never deletes the release wrapping it" — that
references/call-signatures.md never confirms; that file explicitly
marks the reverse direction unconfirmed and "the more dangerous
direction to get wrong." State only the confirmed direction (deleting
a release doesn't delete its tag) and flag the reverse as unconfirmed
with a verification step, on a destructive, irreversible operation.

Found by an independent post-closure audit of #99 (agent-audit +
skill-audit re-run against every changed skill/agent).
2026-08-30 19:31:18 +00:00
59aaec4ed6 fix(gitea-branches): repoint dangling overview.md citation
references/commits.md cited overview.md as the authority for a
scope-gating claim, but no such file exists in this skill's package —
the only overview.md is an external research doc not shipped with the
skill. Repoint to branches.md's own Token scope section, which states
and confirms the same principle, and drop the unverifiable
write:repository enumeration detail no file in this skill actually
makes.

Found by an independent post-closure audit of #99 (agent-audit +
skill-audit re-run against every changed skill/agent).
2026-08-30 19:31:15 +00:00
92ba9abe7c fix(diagnose): restore skill-root-relative script path
00c1e6b (the ADR-0020 retrofit of diagnose) moved a bullet referencing
scripts/hitl-loop.template.sh out of SKILL.md and into the new
references/feedback-loops.md, and in the move flipped the correct
skill-root-relative path into an incorrect parent-relative one
(../scripts/...) -- despite that commit's own message claiming to fix
"a script path that did not resolve." References in this skill are
written relative to the skill root regardless of which file carries
them, matching the convention used throughout SKILL.md.

Found via an independent post-closure audit of issue #99; validated
clean via skill-audit afterward.

Refs #99
2026-08-30 17:45:56 +00:00
38efd2be67 fix(skills): collapse verb-enumerated descriptions to one capability clause
An independent audit of the ADR-0020 retrofit (issue #99) found that
git-submodules, git-worktrees, and gitea-files each collapsed their
description length correctly during retrofit but left the capability
clause as a verb enumeration (e.g. "Create, list, lock, move, remove,
prune, or repair") instead of ADR-0020's required single clause. The
deterministic char-count gate can't catch this — it's a qualitative
rubric violation the retrofit commits' own messages never claimed to
address, only measurable length/word-count fixes.

Validated clean via skill-audit and skill-size-check after the fix;
boundary clauses and routing targets left untouched.

Refs #99
2026-08-30 17:45:50 +00:00
bdff6fdb3c refactor(bin): make caveman hand-invoked only
Sets `disable-model-invocation: true`, so the host withholds caveman from
the model-visible skill listing. It stops paying preload tax and can only
be reached by typing /caveman.

The reason is not the 287 characters. caveman's description was almost
entirely a trigger list -- "caveman mode", "talk like caveman", "less
tokens", "be brief" -- and "be brief" is a false-positive magnet: a user
asking for brevity wants short answers, not dropped articles and
`Respond terse like smart caveman`. Removing the skill from the router
deletes the bad trigger. User's call, made explicitly.

Safe to flag because the flag also hard-blocks the Skill tool, so any
inbound route from another skill would break. Re-checked all 39
descriptions and bodies after four waves of retrofit rewrites: every
mention of caveman is documentation, research notes or the lockfile.
Nothing routes to it.

The description is rewritten as human-facing text, since it is no longer
a routing surface -- it now says how to turn the mode on and off rather
than listing phrases for a reader that can no longer see it.

The two remaining SUGGESTIONs are #108: the boundary-clause check fires on
hand-invoked skills, which ADR-0020 contractually exempts. Advisory, and
not to be "fixed" in the skill.

Preload tax 10,002 -> 9,732 chars across 37 model-visible skills.

Refs #99

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MWb5RQgCL1ye7cGp2RPb2u
2026-08-30 17:04:45 +00:00
b25412bf39 docs: record that the ADR-0020 corpus is clean, and what the gates still miss
Wave 4 closed the last three FAILs, so the session rule no longer describes
a grandfathered set: all 39 skills clear both tiers, 0 descriptions over 400
chars and 0 bodies over 900 words. Preload tax 21,033 -> 10,201 chars
(~2,550 tokens), under the 12,000 success criterion in #99.

The rule now says what that changes for the reader: nothing is grandfathered,
so the gates bite on first commit rather than waiting for a retrofit.

Also names the second blind spot, found this wave. The Kyberforge Vale style
is scoped [**/SKILL.md], so every references/ file is unlinted -- and the
contract's own remedy is to move prose into references/, which moves it out
of the prose gate's reach. forge's retrofit relocated ~900 words that way and
the moved prose carried a rule violation Vale would have caught in a SKILL.md.

Refs #99

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MWb5RQgCL1ye7cGp2RPb2u
2026-08-30 16:42:59 +00:00
79c60715dc fix(gates): restore byte-identity of the shared ADR-0020 boundary resolver
915eb09 rewrote the bare-arrow carve-out comment in scripts/skill-size-check.sh
without pasting the block over the two other copies, so the resolver stood at
764 lines in the hook and 755 in both audit validators.
tests/test-adr0020-contract.sh exists to catch exactly this and did -- it was
the regression behind that suite's failure, and I reported the suite green
after 915eb09 without re-running it.

No behaviour changes: the drift was comment-only. Restoring identity keeps the
invariant the test enforces, which matters because the three copies are the
only thing making the hook and the two validators agree on what a boundary
target is.

Refs #99

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MWb5RQgCL1ye7cGp2RPb2u
2026-08-30 16:42:51 +00:00
264a5dbd67 docs(kyberforge): refresh the apm-workflow exemplar figures after its retrofit
body-discipline.md and contract.md both cite apm-workflow by hard number
as the reference dispatch shape, and instruct authors to calibrate against
its body word count. The retrofit in 1e4aab5 moved every one of those
figures: body 421 -> 237, whole-file 554 -> 304, references 3,006 ->
3,222. Measured, not estimated.

The quoted closing line was stale too -- it now carries a continuation
clause, so it is quoted with an ellipsis rather than as a full sentence.

These are positive exemplars in live guidance, so they are refreshed
rather than pinned. That is the opposite treatment from the git-commits
negative example in the same file, which is pinned to 5e23250 precisely
so it keeps describing the pre-retrofit state. ADR-0020 carries the same
figures and is deliberately left alone: it self-pins every citation to
base commit f9b919d, and refreshing it would destroy the record of what
the decision was taken against.

Refs #99

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MWb5RQgCL1ye7cGp2RPb2u
2026-08-30 16:31:43 +00:00
aa982b9d26 refactor(kyberforge): retrofit apm-install to the ADR-0020 contract
Description 514 -> 213 chars, Gotchas 5 entries/47% -> 2 entries/17%.
Body 350 -> 397 words: three Gotchas fold into the steps they gate, and
the repairs below add back what the fold dropped.

Cuts the second trigger register, the runtime enumeration (still named in
the body and README) and the enumeration inside the boundary clause.

Fixes four defects the first pass introduced:

- The mirror bullet claimed the piped one-liner "ignores" VERSION and
  GITHUB_URL. The installer reads both from the environment and its own
  usage header documents VERSION working through the pipe. The real
  constraint is that an air-gapped host cannot reach aka.ms, so the
  script must be on disk. Also corrects the variable names --
  APM_RELEASE_BASE_URL is the mirror base, GITHUB_URL is the Enterprise
  host.
- "If apm --version already answers, skip to Step 2" was unconditional on
  intent, so a pin or upgrade request routed past the only pin
  instruction in the skill. Now gated on intent.
- The PEP 668 rule was demoted to post-failure recovery, leaving a
  routing rule that sent a Debian box into a command that hard-fails.
  The prohibition is back on the pip bullet, before the choice.
- The apm-is-not-a-runtime Gotcha lost its operative clause. The two
  step headings cited as carrying it already existed pre-retrofit, so
  nothing had replaced it and nothing stated when Step 2 is required.

Refs #99

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MWb5RQgCL1ye7cGp2RPb2u
2026-08-30 16:31:33 +00:00
31 changed files with 135 additions and 97 deletions

View File

@@ -36,7 +36,7 @@ Fall back to raw shell only when no skill covers it.
- **Do not add repo-owned keys to `.claude/settings.json`.** apm treats it as its own deployed artifact and `apm audit --ci` replays the install and diffs, so anything apm would not have written is permanent drift that fails the `apm-audit-ci` pre-push hook. A hook you want here is authored in `plugins/<name>/.apm/hooks/` and deployed by apm, never hand-written into that file. The `SessionStart` entry already in it is exactly that: kyberforge authors it in `plugins/kyberforge/.apm/hooks/hooks.json` and apm merges it in, so it is apm's own output, it is what the replay expects, and it belongs in the commit — do not strip it (ADR-0019). Machine-specific settings go in the gitignored `.claude/settings.local.json`; shared enforcement goes in `.pre-commit-config.yaml`.
- **`apm.lock.yaml` turning up modified is expected, not a bug.** kyberforge's `SessionStart` hook runs `apm outdated` at startup and `apm update --yes` when something is behind, which rewrites the lock. Commit or discard it deliberately.
- **A `.apm/` edit is not live in this session until it is pushed.** The six dependencies resolve from the holocron remote, unpinned against the default branch. `apm install` deploys from the lock; `apm update` is what re-resolves refs.
- **The ADR-0020 skill gates ship hot, with no baseline.** Three skills still exceed a FAIL tier, all in `kyberforge`: `apm-workflow` (817-char description), `forge` (648 chars, 1,093-word body) and `apm-install` (514 chars). Editing any of those three *for any reason* means retrofitting it to the contract first — a one-line fix cannot be committed until the skill complies. Deliberate; tracked as Gitea issue #99, which is retrofitting the corpus plugin by plugin and has `kyberforge` left. **No routing target dangles any more**, and `tests/test-adr0020-targets.sh` now pins that set as empty, so a new boundary clause naming a non-existent skill fails the suite rather than joining a backlog. The `Kyberforge.CompositionNote` Vale rule fires nowhere, but `skill-size-check` does not cover the Vale half and any new description can reintroduce it, so check both: `pre-commit run --all-files`.
- **The ADR-0020 skill gates ship hot, with no baseline — and the corpus is now clean.** All 39 skills clear both FAIL tiers: no description over 400 characters, no body over 900 words (counted body-only). Issue #99 retrofitted them plugin by plugin and `kyberforge` was the last wave. Because nothing is grandfathered, the gates now bite on first commit — a new skill, or an edit that pushes a description past 400, is blocked until it complies. **No routing target dangles**, and `tests/test-adr0020-targets.sh` pins that set as empty, so a new boundary clause naming a non-existent skill fails the suite rather than joining a backlog. Two blind spots survive: `skill-size-check` does not cover the Vale half, so `Kyberforge.CompositionNote` fires nowhere today but any new description can reintroduce it; and the `Kyberforge` style is scoped `[**/SKILL.md]`, so every `references/` file is unlinted — which matters because the contract's own remedy is to move prose *into* `references/`, out of the prose gate's reach. Check both: `pre-commit run --all-files`.
- **Run `bash tests/run-tests.sh --strict` before considering any change done.** Keep the flag: without it a suite whose dependency is missing exits 77 and is counted SKIPPED rather than failed, so the run goes green having verified less than it claims.
- **Before pushing, rehearse the gate locally:** `pre-commit run --hook-stage pre-push --all-files`. It runs the 14 pre-push hooks this repo authors itself plus pre-commit's 2 `meta` hooks, so it prints 16; `check-release-needed` passes without checking anything, because it needs a real push to `main`. `docs/spec/gates.md` reconciles both.
- **Pushing without a network** needs `SKIP=apm-marketplace-check,apm-pack-check-clean git push` — those two resolve a remote marketplace entry via `git ls-remote`. Skip only those two; the rest are real local checks, and adding one to `SKIP` disarms it silently.

View File

@@ -1,10 +1,11 @@
---
name: caveman
disable-model-invocation: true
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.
Ultra-compressed output mode: drops articles, filler and pleasantries while
keeping technical substance exact. Cuts token usage by roughly 75%. Hand-invoked
only — type /caveman to turn it on, "stop caveman" or "normal mode" to turn it
off. Stays active across turns until you do.
---
Respond terse like smart caveman. All technical substance stay. Only fluff die.

View File

@@ -13,7 +13,7 @@ A feedback loop is a fast, deterministic, agent-runnable pass/fail signal for th
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.
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.
## Iterate on the loop itself

View File

@@ -1,10 +1,11 @@
---
name: caveman
disable-model-invocation: true
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.
Ultra-compressed output mode: drops articles, filler and pleasantries while
keeping technical substance exact. Cuts token usage by roughly 75%. Hand-invoked
only — type /caveman to turn it on, "stop caveman" or "normal mode" to turn it
off. Stays active across turns until you do.
---
Respond terse like smart caveman. All technical substance stay. Only fluff die.

View File

@@ -13,7 +13,7 @@ A feedback loop is a fast, deterministic, agent-runnable pass/fail signal for th
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.
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.
## Iterate on the loop itself

View File

@@ -2,8 +2,8 @@
name: git-submodules
description: >
Use when managing Git submodules — adding, updating, pinning, inspecting,
repointing, or removing a nested repository inside a superproject.
Use when managing Git submodules — the full lifecycle of a nested
repository inside a superproject.
Not multiple checkouts of one repo -> `git-worktrees`.
Not the superproject's own remotes -> `git-remotes`.

View File

@@ -2,8 +2,8 @@
name: git-worktrees
description: >
Use when working on several branches at once without stashing.
Create, list, lock, move, remove, prune, or repair git worktrees.
Use when working on several branches at once without stashing —
manages the full lifecycle of a git worktree.
Not ordinary branch switching or checkout -> `git-branches`.
Not interactive multi-step git guidance -> `git-workflow`.

View File

@@ -2,8 +2,8 @@
name: git-submodules
description: >
Use when managing Git submodules — adding, updating, pinning, inspecting,
repointing, or removing a nested repository inside a superproject.
Use when managing Git submodules — the full lifecycle of a nested
repository inside a superproject.
Not multiple checkouts of one repo -> `git-worktrees`.
Not the superproject's own remotes -> `git-remotes`.

View File

@@ -2,8 +2,8 @@
name: git-worktrees
description: >
Use when working on several branches at once without stashing.
Create, list, lock, move, remove, prune, or repair git worktrees.
Use when working on several branches at once without stashing —
manages the full lifecycle of a git worktree.
Not ordinary branch switching or checkout -> `git-branches`.
Not interactive multi-step git guidance -> `git-workflow`.

View File

@@ -64,10 +64,10 @@ edge cases, `get_commit` will not.
## Token scope
Both tools are believed to require `write:repository`, even though they're read-only — inferred by
analogy with the scope-gating principle in `overview.md` (Gitea gates reads behind write scope for
repo-scoped operations), not a claim `overview.md` makes for commits by name: its explicit
`write:repository` enumeration lists PR, branch, file, release, and tag operations, but doesn't
mention commits. An earlier version of this doc claimed `write:issue` alone worked, based on
analogy with the scope-gating principle confirmed for branch operations in `branches.md`'s Token
scope section (Gitea gates reads behind write scope for repo-scoped operations), not a claim any
doc in this skill makes for commits by name: nothing here enumerates commits under
`write:repository` explicitly. An earlier version of this doc claimed `write:issue` alone worked, based on
empirical testing under a token that held both `write:issue` and `write:repository`
simultaneously — that test didn't isolate the variable either. Treat this as unverified until
tested under a token scoped to `write:issue` only (no `write:repository`).

View File

@@ -2,9 +2,9 @@
name: gitea-files
description: >
Use when reading or writing files in a Gitea repository rather than on the local filesystem —
read, list, walk the tree, create, update, or delete — even when the user does not say "Gitea".
Not commit history -> `gitea-branches`. Not pull requests -> `gitea-prs`.
Use when reading or writing files or directories in a Gitea repository via the MCP server,
rather than the local filesystem — even when the user does not say "Gitea". Not commit
history -> `gitea-branches`. Not pull requests -> `gitea-prs`.
compatibility: Requires the Gitea MCP server configured with a token scoped to at least
write:repository. Tested with a token holding write:issue + write:repository; write:issue

View File

@@ -17,7 +17,7 @@ metadata:
## Gotchas
- **Deleting a release never deletes its tag, and deleting a tag never deletes the release wrapping it.** A release is a metadata wrapper around a tag, so removing both takes two independent destructive calls.
- **Deleting a release never deletes its tag.** A release is a metadata wrapper around a tag, so removing both takes two independent destructive calls. The reverse — whether deleting a tag deletes its release — is *unconfirmed*; verify with `list_releases`/`get_release` after `delete_tag` rather than assume it survives.
- **`is_draft`/`is_pre_release` are booleans the caller sets — Gitea never infers a prerelease from a `-beta`/`-rc` tag name.** The response object names them `draft`/`prerelease`; passing `draft` as an input key is silently ignored, not rejected.
- **`list_releases`/`list_tags` default `per_page` to 20**, where most other gitea-mcp list tools default to 30 — a caller assuming 30 under-counts the pages a full sweep needs.

View File

@@ -64,10 +64,10 @@ edge cases, `get_commit` will not.
## Token scope
Both tools are believed to require `write:repository`, even though they're read-only — inferred by
analogy with the scope-gating principle in `overview.md` (Gitea gates reads behind write scope for
repo-scoped operations), not a claim `overview.md` makes for commits by name: its explicit
`write:repository` enumeration lists PR, branch, file, release, and tag operations, but doesn't
mention commits. An earlier version of this doc claimed `write:issue` alone worked, based on
analogy with the scope-gating principle confirmed for branch operations in `branches.md`'s Token
scope section (Gitea gates reads behind write scope for repo-scoped operations), not a claim any
doc in this skill makes for commits by name: nothing here enumerates commits under
`write:repository` explicitly. An earlier version of this doc claimed `write:issue` alone worked, based on
empirical testing under a token that held both `write:issue` and `write:repository`
simultaneously — that test didn't isolate the variable either. Treat this as unverified until
tested under a token scoped to `write:issue` only (no `write:repository`).

View File

@@ -2,9 +2,9 @@
name: gitea-files
description: >
Use when reading or writing files in a Gitea repository rather than on the local filesystem —
read, list, walk the tree, create, update, or delete — even when the user does not say "Gitea".
Not commit history -> `gitea-branches`. Not pull requests -> `gitea-prs`.
Use when reading or writing files or directories in a Gitea repository via the MCP server,
rather than the local filesystem — even when the user does not say "Gitea". Not commit
history -> `gitea-branches`. Not pull requests -> `gitea-prs`.
compatibility: Requires the Gitea MCP server configured with a token scoped to at least
write:repository. Tested with a token holding write:issue + write:repository; write:issue

View File

@@ -17,7 +17,7 @@ metadata:
## Gotchas
- **Deleting a release never deletes its tag, and deleting a tag never deletes the release wrapping it.** A release is a metadata wrapper around a tag, so removing both takes two independent destructive calls.
- **Deleting a release never deletes its tag.** A release is a metadata wrapper around a tag, so removing both takes two independent destructive calls. The reverse — whether deleting a tag deletes its release — is *unconfirmed*; verify with `list_releases`/`get_release` after `delete_tag` rather than assume it survives.
- **`is_draft`/`is_pre_release` are booleans the caller sets — Gitea never infers a prerelease from a `-beta`/`-rc` tag name.** The response object names them `draft`/`prerelease`; passing `draft` as an input key is silently ignored, not rejected.
- **`list_releases`/`list_tags` default `per_page` to 20**, where most other gitea-mcp list tools default to 30 — a caller assuming 30 under-counts the pages a full sweep needs.

View File

@@ -446,8 +446,17 @@ def known_targets(start_dir):
# condition, pc-run's "run pre-commit hooks" reads as a route to a
# non-existent `pre-commit` skill.
# * A BARE arrow target counts only in ADR-0020's compressed boundary form,
# `Not <thing> -> <skill-name>`. Without that, diagnose's process chain
# "fix -> regression-test" reads as a route to `regression-test`.
# `Not <thing> -> <skill-name>`. The example that motivated it is gone:
# diagnose's process chain "fix -> regression-test", which without the
# gate read as a route to a non-existent `regression-test` skill, was cut
# when issue #99 retrofitted that description. So the gate is currently
# UNEXERCISED — gating and not gating produce the same verdict corpus-wide.
# Keep it anyway. It is a false-positive guard against prose no one has
# written yet, and any new process chain re-arms it. Unexercised is not the
# same as unnecessary, and the branch it guards is still load-bearing: the
# bare-arrow rule is the sole extractor for three real targets in
# kyberforge's audit skills (agent-audit -> agent-author, agent-audit ->
# skill-audit, skill-audit -> skill-author), all written unbackticked.
# * A backticked hyphenated token counts only inside a boundary sentence.
# Unconditionally, `pre-push` or `commit-msg` in a TRIGGER clause is a hard
# FAIL with no escape hatch. Gating it costs nothing (measured over this

View File

@@ -4,7 +4,7 @@ Installs and configures the `apm` (Agent Package Manager) CLI and the agent runt
## What it does
Covers the two provisioning steps for working with apm: installing the `apm` binary itself (quick-install script, pinned version, air-gapped mirror, or pip), and installing/managing an agent runtime apm drives (`apm runtime setup copilot|codex|gemini|llm`, listing installed runtimes, checking which one `apm run` defaults to).
Covers the two provisioning steps for working with apm: installing the `apm` binary itself (quick-install script, pinned version, air-gapped mirror, or pip/pipx), and installing/managing an agent runtime apm drives (`apm runtime setup copilot|codex|gemini|llm`, listing installed runtimes, checking which one `apm run` defaults to).
## Usage

View File

@@ -1,13 +1,9 @@
---
name: apm-install
description: >
Use when the user wants to install the apm (Agent Package Manager) CLI
itself, pin or upgrade its version, set up an air-gapped/enterprise mirror
install, or install and manage an agent runtime that apm drives (Copilot
CLI, Codex, Gemini, generic llm) — "install apm", "set up apm", "pin apm to
a version", "apm runtime setup", "which runtime will apm run pick". Do not
use for authoring apm.yml, scaffolding a package/marketplace, compiling,
packing, publishing, or running apm audit — use apm-workflow for those.
Use when installing, pinning, or upgrading the apm (Agent Package Manager)
CLI itself, or installing and managing an agent runtime apm drives. Not
authoring, publishing, or auditing apm packages -> `apm-workflow`.
metadata:
category: apm
source_keys:
@@ -16,13 +12,12 @@ metadata:
## Gotchas
- apm does not execute agents itself — it only installs and manages the runtimes that do. "Install apm" and "install a runtime apm manages" are two separate steps; don't conflate them or skip the second when the user actually wants a working agent CLI, not just the package manager.
- The air-gapped/enterprise mirror path needs `GITHUB_URL` and `VERSION` set together against a downloaded `install.sh` — it does not work through the piped one-liner form.
- `pip install apm-cli` requires Python 3.10+; the quick-install script has no such prerequisite. Prefer the quick-install script unless the environment is pip-first.
- On a Debian/externally-managed Python environment (PEP 668), `pip install apm-cli` fails immediately with `error: externally-managed-environment`. Fall back to `pipx install apm-cli` — same PyPI package, but pipx creates an isolated venv and correctly exposes the `apm` binary on `PATH`.
- Installing the Copilot CLI runtime through `apm runtime setup copilot` requires Node.js v22+ and npm v10+ already present — apm does not install Node/npm for you.
- apm never executes an agent itself — it only installs and manages the runtimes that do. Installing apm alone leaves the user with a package manager and no working agent CLI, so Step 2 is required whenever the user actually wants one; skip it only when they explicitly want the package manager alone.
- `apm runtime setup copilot` needs Node.js v22+ and npm v10+ already on `PATH`; apm will not install them for you.
## Install apm
## Step 1 — Install the apm CLI
If `apm --version` already answers and the user is not pinning or upgrading, skip to Step 2.
Default:
@@ -31,16 +26,17 @@ curl -sSL https://aka.ms/apm-unix | sh
```
Escape hatches — combine as needed:
- Pin a version: append `@vX.Y.Z` to the piped script's arguments, e.g. `curl -sSL https://aka.ms/apm-unix | sh -s -- @v1.2.3`.
- Custom install directory: set `APM_INSTALL_DIR` on the piped script's command, e.g. `curl -sSL https://aka.ms/apm-unix | APM_INSTALL_DIR=$HOME/.local/bin sh`.
- Air-gapped / GitHub Enterprise mirror: download `install.sh` first, then run it with `GITHUB_URL` and `VERSION` set, e.g. `GITHUB_URL=https://github.corp.com VERSION=v1.2.3 sh install.sh`.
- pip (Python 3.10+ environments): `pip install apm-cli`.
- pipx (externally-managed/PEP 668 environments where plain `pip install` fails, e.g. Debian): `pipx install apm-cli`.
- Manual: download the platform archive from the GitHub releases page, extract, place the binary on `PATH`.
- **Pin a version** — append `@vX.Y.Z` to the piped script's arguments: `curl -sSL https://aka.ms/apm-unix | sh -s -- @v1.2.3`.
- **Custom install directory** — set `APM_INSTALL_DIR` on the piped script's command: `curl -sSL https://aka.ms/apm-unix | APM_INSTALL_DIR=$HOME/.local/bin sh`.
- **Air-gapped mirror / GitHub Enterprise** — an air-gapped host cannot reach `aka.ms` at all, so get `install.sh` onto the box and run it from disk instead of piping. Point it at the mirror with `APM_RELEASE_BASE_URL` and pin `VERSION`: `APM_RELEASE_BASE_URL=https://mirror.corp/apm VERSION=v1.2.3 sh install.sh`; add `APM_RELEASE_METADATA_URL` instead if you leave `VERSION` unset. `GITHUB_URL` is the GitHub Enterprise host, not a release mirror. All four are ordinary environment variables that also work through the pipe — running from disk is a network constraint, not a script one.
- **pip** — `pip install apm-cli` requires Python 3.10+. Not on an externally-managed (PEP 668) Python such as Debian or Ubuntu, where it hard-fails with `error: externally-managed-environment`; use pipx below. The quick-install script has no Python prerequisite, so prefer it unless the environment is pip-first.
- **pipx** — `pipx install apm-cli` on those PEP 668 environments. Same PyPI package, but pipx builds an isolated venv and exposes `apm` on `PATH`.
- **Manual** — download the platform archive from the GitHub releases page, extract, and place the binary on `PATH`.
Verify with `apm --version`.
## Install or manage an agent runtime
## Step 2 — Install or manage an agent runtime
Default:

View File

@@ -51,6 +51,8 @@ apm publish --package acme/my-skill
Publishes a producer package (root containing `apm.yml`, `.apm/`, and optionally a `registries:` block) to a registry. Always dry-run with `-v` first — publishing is not trivially reversible once a version tag is claimed on a registry.
Publishing to a named registry requires `apm experimental enable registries` to have already run — see `references/configure.md`'s Gotchas for the full precondition and its silent-no-op failure mode.
## Run
```bash

View File

@@ -18,3 +18,5 @@ With no arguments, resolves and installs everything declared under `dependencies
`--update` is the escape hatch for a lockfile hash mismatch against upstream — normal `apm install` treats that as drift and won't silently accept it; see `references/audit.md` for the CI-side check (`apm install --frozen`) that fails instead of re-resolving.
`--target agent-skills` generates the vendor-neutral output directory instead of a Claude/Copilot-specific one — for IDE-agnostic tool support.
If a `PACKAGE_REF` resolves through a named registry rather than a plain git source, `apm experimental enable registries` must already have been run — see `references/configure.md`'s Gotchas for the full precondition and its silent-no-op failure mode.

View File

@@ -80,13 +80,13 @@ table** plus the gates common to every branch, and each flow lives in its own se
`references/` file. Inlining all of them is a FAIL regardless of word count, because every
invocation then pays for every branch it did not take.
The reference shape in this repo is `apm-workflow`: a **421-word body** dispatching to roughly
3,000 words of references across five mutually exclusive invocations. Its whole-file count is 554
words — cite 421 when calibrating a body, or the conflation this section warns against reappears
The reference shape in this repo is `apm-workflow`: a **237-word body** dispatching to roughly
3,200 words of references across five mutually exclusive invocations. Its whole-file count is 304
words — cite 237 when calibrating a body, or the conflation this section warns against reappears
in the finding itself.
Note its wiring: a three-column table (invocation, action, reference file) closed by one line,
*"Read only the reference file matching the requested action."* That is the endorsed shape, and it
*"Read only the reference file matching the requested action …"* That is the endorsed shape, and it
is why the literal-conditional requirement above exempts a body that dispatches. Do not flag it.
## Gotchas sections

View File

@@ -372,8 +372,17 @@ def known_targets(start_dir):
# condition, pc-run's "run pre-commit hooks" reads as a route to a
# non-existent `pre-commit` skill.
# * A BARE arrow target counts only in ADR-0020's compressed boundary form,
# `Not <thing> -> <skill-name>`. Without that, diagnose's process chain
# "fix -> regression-test" reads as a route to `regression-test`.
# `Not <thing> -> <skill-name>`. The example that motivated it is gone:
# diagnose's process chain "fix -> regression-test", which without the
# gate read as a route to a non-existent `regression-test` skill, was cut
# when issue #99 retrofitted that description. So the gate is currently
# UNEXERCISED — gating and not gating produce the same verdict corpus-wide.
# Keep it anyway. It is a false-positive guard against prose no one has
# written yet, and any new process chain re-arms it. Unexercised is not the
# same as unnecessary, and the branch it guards is still load-bearing: the
# bare-arrow rule is the sole extractor for three real targets in
# kyberforge's audit skills (agent-audit -> agent-author, agent-audit ->
# skill-audit, skill-audit -> skill-author), all written unbackticked.
# * A backticked hyphenated token counts only inside a boundary sentence.
# Unconditionally, `pre-push` or `commit-msg` in a TRIGGER clause is a hard
# FAIL with no escape hatch. Gating it costs nothing (measured over this

View File

@@ -120,8 +120,8 @@ A generic pointer ("see references/ for details") is a Vale error — the agent
**Dispatch is mandatory at two or more mutually exclusive flows.** The body carries the dispatch
table and the gates common to every branch; each flow gets its own self-contained `references/`
file. Exemplar: the `apm-workflow` skill — a **421-word body** dispatching to 3,006 words of
references. Calibrate against 421: that file's whole-file count is 554 words, and aiming at that
file. Exemplar: the `apm-workflow` skill — a **237-word body** dispatching to 3,222 words of
references. Calibrate against 237: that file's whole-file count is 304 words, and aiming at that
number instead overshoots the body budget by ~30%.
**Length.** 600 words SUGGESTION, 900 words FAIL, counting the **body only** — everything after

View File

@@ -446,8 +446,17 @@ def known_targets(start_dir):
# condition, pc-run's "run pre-commit hooks" reads as a route to a
# non-existent `pre-commit` skill.
# * A BARE arrow target counts only in ADR-0020's compressed boundary form,
# `Not <thing> -> <skill-name>`. Without that, diagnose's process chain
# "fix -> regression-test" reads as a route to `regression-test`.
# `Not <thing> -> <skill-name>`. The example that motivated it is gone:
# diagnose's process chain "fix -> regression-test", which without the
# gate read as a route to a non-existent `regression-test` skill, was cut
# when issue #99 retrofitted that description. So the gate is currently
# UNEXERCISED — gating and not gating produce the same verdict corpus-wide.
# Keep it anyway. It is a false-positive guard against prose no one has
# written yet, and any new process chain re-arms it. Unexercised is not the
# same as unnecessary, and the branch it guards is still load-bearing: the
# bare-arrow rule is the sole extractor for three real targets in
# kyberforge's audit skills (agent-audit -> agent-author, agent-audit ->
# skill-audit, skill-audit -> skill-author), all written unbackticked.
# * A backticked hyphenated token counts only inside a boundary sentence.
# Unconditionally, `pre-push` or `commit-msg` in a TRIGGER clause is a hard
# FAIL with no escape hatch. Gating it costs nothing (measured over this

View File

@@ -4,7 +4,7 @@ Installs and configures the `apm` (Agent Package Manager) CLI and the agent runt
## What it does
Covers the two provisioning steps for working with apm: installing the `apm` binary itself (quick-install script, pinned version, air-gapped mirror, or pip), and installing/managing an agent runtime apm drives (`apm runtime setup copilot|codex|gemini|llm`, listing installed runtimes, checking which one `apm run` defaults to).
Covers the two provisioning steps for working with apm: installing the `apm` binary itself (quick-install script, pinned version, air-gapped mirror, or pip/pipx), and installing/managing an agent runtime apm drives (`apm runtime setup copilot|codex|gemini|llm`, listing installed runtimes, checking which one `apm run` defaults to).
## Usage

View File

@@ -1,13 +1,9 @@
---
name: apm-install
description: >
Use when the user wants to install the apm (Agent Package Manager) CLI
itself, pin or upgrade its version, set up an air-gapped/enterprise mirror
install, or install and manage an agent runtime that apm drives (Copilot
CLI, Codex, Gemini, generic llm) — "install apm", "set up apm", "pin apm to
a version", "apm runtime setup", "which runtime will apm run pick". Do not
use for authoring apm.yml, scaffolding a package/marketplace, compiling,
packing, publishing, or running apm audit — use apm-workflow for those.
Use when installing, pinning, or upgrading the apm (Agent Package Manager)
CLI itself, or installing and managing an agent runtime apm drives. Not
authoring, publishing, or auditing apm packages -> `apm-workflow`.
metadata:
category: apm
source_keys:
@@ -16,13 +12,12 @@ metadata:
## Gotchas
- apm does not execute agents itself — it only installs and manages the runtimes that do. "Install apm" and "install a runtime apm manages" are two separate steps; don't conflate them or skip the second when the user actually wants a working agent CLI, not just the package manager.
- The air-gapped/enterprise mirror path needs `GITHUB_URL` and `VERSION` set together against a downloaded `install.sh` — it does not work through the piped one-liner form.
- `pip install apm-cli` requires Python 3.10+; the quick-install script has no such prerequisite. Prefer the quick-install script unless the environment is pip-first.
- On a Debian/externally-managed Python environment (PEP 668), `pip install apm-cli` fails immediately with `error: externally-managed-environment`. Fall back to `pipx install apm-cli` — same PyPI package, but pipx creates an isolated venv and correctly exposes the `apm` binary on `PATH`.
- Installing the Copilot CLI runtime through `apm runtime setup copilot` requires Node.js v22+ and npm v10+ already present — apm does not install Node/npm for you.
- apm never executes an agent itself — it only installs and manages the runtimes that do. Installing apm alone leaves the user with a package manager and no working agent CLI, so Step 2 is required whenever the user actually wants one; skip it only when they explicitly want the package manager alone.
- `apm runtime setup copilot` needs Node.js v22+ and npm v10+ already on `PATH`; apm will not install them for you.
## Install apm
## Step 1 — Install the apm CLI
If `apm --version` already answers and the user is not pinning or upgrading, skip to Step 2.
Default:
@@ -31,16 +26,17 @@ curl -sSL https://aka.ms/apm-unix | sh
```
Escape hatches — combine as needed:
- Pin a version: append `@vX.Y.Z` to the piped script's arguments, e.g. `curl -sSL https://aka.ms/apm-unix | sh -s -- @v1.2.3`.
- Custom install directory: set `APM_INSTALL_DIR` on the piped script's command, e.g. `curl -sSL https://aka.ms/apm-unix | APM_INSTALL_DIR=$HOME/.local/bin sh`.
- Air-gapped / GitHub Enterprise mirror: download `install.sh` first, then run it with `GITHUB_URL` and `VERSION` set, e.g. `GITHUB_URL=https://github.corp.com VERSION=v1.2.3 sh install.sh`.
- pip (Python 3.10+ environments): `pip install apm-cli`.
- pipx (externally-managed/PEP 668 environments where plain `pip install` fails, e.g. Debian): `pipx install apm-cli`.
- Manual: download the platform archive from the GitHub releases page, extract, place the binary on `PATH`.
- **Pin a version** — append `@vX.Y.Z` to the piped script's arguments: `curl -sSL https://aka.ms/apm-unix | sh -s -- @v1.2.3`.
- **Custom install directory** — set `APM_INSTALL_DIR` on the piped script's command: `curl -sSL https://aka.ms/apm-unix | APM_INSTALL_DIR=$HOME/.local/bin sh`.
- **Air-gapped mirror / GitHub Enterprise** — an air-gapped host cannot reach `aka.ms` at all, so get `install.sh` onto the box and run it from disk instead of piping. Point it at the mirror with `APM_RELEASE_BASE_URL` and pin `VERSION`: `APM_RELEASE_BASE_URL=https://mirror.corp/apm VERSION=v1.2.3 sh install.sh`; add `APM_RELEASE_METADATA_URL` instead if you leave `VERSION` unset. `GITHUB_URL` is the GitHub Enterprise host, not a release mirror. All four are ordinary environment variables that also work through the pipe — running from disk is a network constraint, not a script one.
- **pip** — `pip install apm-cli` requires Python 3.10+. Not on an externally-managed (PEP 668) Python such as Debian or Ubuntu, where it hard-fails with `error: externally-managed-environment`; use pipx below. The quick-install script has no Python prerequisite, so prefer it unless the environment is pip-first.
- **pipx** — `pipx install apm-cli` on those PEP 668 environments. Same PyPI package, but pipx builds an isolated venv and exposes `apm` on `PATH`.
- **Manual** — download the platform archive from the GitHub releases page, extract, and place the binary on `PATH`.
Verify with `apm --version`.
## Install or manage an agent runtime
## Step 2 — Install or manage an agent runtime
Default:

View File

@@ -51,6 +51,8 @@ apm publish --package acme/my-skill
Publishes a producer package (root containing `apm.yml`, `.apm/`, and optionally a `registries:` block) to a registry. Always dry-run with `-v` first — publishing is not trivially reversible once a version tag is claimed on a registry.
Publishing to a named registry requires `apm experimental enable registries` to have already run — see `references/configure.md`'s Gotchas for the full precondition and its silent-no-op failure mode.
## Run
```bash

View File

@@ -18,3 +18,5 @@ With no arguments, resolves and installs everything declared under `dependencies
`--update` is the escape hatch for a lockfile hash mismatch against upstream — normal `apm install` treats that as drift and won't silently accept it; see `references/audit.md` for the CI-side check (`apm install --frozen`) that fails instead of re-resolving.
`--target agent-skills` generates the vendor-neutral output directory instead of a Claude/Copilot-specific one — for IDE-agnostic tool support.
If a `PACKAGE_REF` resolves through a named registry rather than a plain git source, `apm experimental enable registries` must already have been run — see `references/configure.md`'s Gotchas for the full precondition and its silent-no-op failure mode.

View File

@@ -80,13 +80,13 @@ table** plus the gates common to every branch, and each flow lives in its own se
`references/` file. Inlining all of them is a FAIL regardless of word count, because every
invocation then pays for every branch it did not take.
The reference shape in this repo is `apm-workflow`: a **421-word body** dispatching to roughly
3,000 words of references across five mutually exclusive invocations. Its whole-file count is 554
words — cite 421 when calibrating a body, or the conflation this section warns against reappears
The reference shape in this repo is `apm-workflow`: a **237-word body** dispatching to roughly
3,200 words of references across five mutually exclusive invocations. Its whole-file count is 304
words — cite 237 when calibrating a body, or the conflation this section warns against reappears
in the finding itself.
Note its wiring: a three-column table (invocation, action, reference file) closed by one line,
*"Read only the reference file matching the requested action."* That is the endorsed shape, and it
*"Read only the reference file matching the requested action …"* That is the endorsed shape, and it
is why the literal-conditional requirement above exempts a body that dispatches. Do not flag it.
## Gotchas sections

View File

@@ -372,8 +372,17 @@ def known_targets(start_dir):
# condition, pc-run's "run pre-commit hooks" reads as a route to a
# non-existent `pre-commit` skill.
# * A BARE arrow target counts only in ADR-0020's compressed boundary form,
# `Not <thing> -> <skill-name>`. Without that, diagnose's process chain
# "fix -> regression-test" reads as a route to `regression-test`.
# `Not <thing> -> <skill-name>`. The example that motivated it is gone:
# diagnose's process chain "fix -> regression-test", which without the
# gate read as a route to a non-existent `regression-test` skill, was cut
# when issue #99 retrofitted that description. So the gate is currently
# UNEXERCISED — gating and not gating produce the same verdict corpus-wide.
# Keep it anyway. It is a false-positive guard against prose no one has
# written yet, and any new process chain re-arms it. Unexercised is not the
# same as unnecessary, and the branch it guards is still load-bearing: the
# bare-arrow rule is the sole extractor for three real targets in
# kyberforge's audit skills (agent-audit -> agent-author, agent-audit ->
# skill-audit, skill-audit -> skill-author), all written unbackticked.
# * A backticked hyphenated token counts only inside a boundary sentence.
# Unconditionally, `pre-push` or `commit-msg` in a TRIGGER clause is a hard
# FAIL with no escape hatch. Gating it costs nothing (measured over this

View File

@@ -120,8 +120,8 @@ A generic pointer ("see references/ for details") is a Vale error — the agent
**Dispatch is mandatory at two or more mutually exclusive flows.** The body carries the dispatch
table and the gates common to every branch; each flow gets its own self-contained `references/`
file. Exemplar: the `apm-workflow` skill — a **421-word body** dispatching to 3,006 words of
references. Calibrate against 421: that file's whole-file count is 554 words, and aiming at that
file. Exemplar: the `apm-workflow` skill — a **237-word body** dispatching to 3,222 words of
references. Calibrate against 237: that file's whole-file count is 304 words, and aiming at that
number instead overshoots the body budget by ~30%.
**Length.** 600 words SUGGESTION, 900 words FAIL, counting the **body only** — everything after