A review of this PR concluded that MCP was not an apm primitive, that the
.mcp.json edit therefore did nothing, and that the declaration belonged in
plugins/gitea/apm.yml under dependencies.mcp. The first half was right about
the primitive and wrong about everything that followed.
MCP is a first-class apm primitive. But the .mcp.json route already reaches
it: apm resolves a plugin manifest in the order plugin.json,
.github/plugin/plugin.json, .claude-plugin/plugin.json, so the generated
Copilot manifest wins, its mcpServers string pointer is followed, and
.mcp.json is injected into the package's dependencies.mcp with ${VAR} env
references intact. Verified against the real remote: a git-sourced install of
plugins/gitea at this branch deploys the gitea server with both references
unexpanded. No code change is needed and none is made here.
Moving the declaration into plugins/gitea/apm.yml would have broken the
build. apm-audit-ci runs apm audit --ci inside every plugins/*/, so a declared
dependency arms lockfile-exists there, which then demands an apm.lock.yaml in
the package plus that package's whole deployed tree inside the package
directory: 93 missing deployed files and 79 drifted paths, measured.
So this commit documents rather than changes:
- AGENTS.md and docs/spec/architecture.md said .mcp.json was plugin-root
material with no .apm/ source, true of .apm/ and read as 'apm has no MCP
concept'. Both now state what .mcp.json is, how it reaches dependencies.mcp,
and that a plugin's own apm.yml is the one place not to declare it.
- architecture.md also records the env-strip: apm pack inlines .mcp.json into
.claude-plugin/plugin.json and its sanitiser drops env and headers blocks
unconditionally, ${VAR} included. Inert under apm, which never reads that
file, but a native Claude Code plugin install reads exactly it and would
start the server with no credentials.
- README.md gains the go toolchain prerequisite and the two environment
variables the server needs, with placeholder values only.
- LESSONS.md records both process failures, including that three scratch
installs inverted the result by using local ./path dependencies, where apm
skips the plugin normalisation that injects .mcp.json.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EmiHiknxqtZPEBnW7ujgNz
142 lines
9.8 KiB
Markdown
142 lines
9.8 KiB
Markdown
# holocron
|
|
|
|
The global AI development configuration repository — the authoritative source for agent definitions, skills, workflows, and prompts across all projects. Built as a homelab tool intended to scale to professional environments.
|
|
|
|
Content ships as six installable plugins, each an apm (Agent Package Manager) package. This repo consumes its own plugins through apm, so the working copy runs the same released content every other consumer gets.
|
|
|
|
## Repo layout
|
|
|
|
| Path | What it holds |
|
|
| --- | --- |
|
|
| `plugins/` | Six apm packages — `bin`, `core`, `git`, `gitea`, `kyberforge`, `lint` — each carrying skills, and where relevant agents, hooks, MCP servers, and bundled assets |
|
|
| `providers/claude-code/` | Claude Code adapter, deployed to `~/.claude/` via `scripts/install.sh` |
|
|
| `core/` | Provider-agnostic always-on content — `core/AGENTS.md` and `core/instructions/` |
|
|
| `docs/` | Specs (`docs/spec/`), architectural decisions (`docs/adr/`), governance, research, and notes |
|
|
| `scripts/` | Install, sync, and check scripts used by the git hooks |
|
|
| `tests/` | `run-tests.sh`, `run-bats.sh`, the `test-*.sh` suites, and the bats submodules |
|
|
|
|
The six plugins:
|
|
|
|
- **kyberforge** — skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace
|
|
- **git** — conventional commits, branches, history, submodules, worktrees, remotes, pre-commit hook authoring and running (`pc-author` / `pc-run`), and an interactive router (`git-workflow`)
|
|
- **gitea** — issues, pull requests, labels, milestones, releases, branches, files, and an interactive router (`gitea-workflow`)
|
|
- **core** — authoring and auditing a repo's `AGENTS.md` and the provider adapter files that defer to it
|
|
- **lint** — configuring and running linters
|
|
- **bin** — cross-cutting workflow skills not yet split into a focused plugin: research, documentation, TDD, prototyping, triage, diagnosis, architecture review, requirement grilling, compressed output (`caveman`), and re-orienting mid-task (`zoom-out`)
|
|
|
|
## Prerequisites
|
|
|
|
Install all of these before setting up. Each one is a hard dependency of a git hook or a script — several fail with an unhelpful "command not found" if missing.
|
|
|
|
| Tool | Why | Install |
|
|
| --- | --- | --- |
|
|
| `apm` CLI | Four pre-push hooks shell out to it (`apm-marketplace-check`, `apm-audit-ci`, `apm-pack-check-clean`, and `check-plugin-content-sync` via `scripts/sync-plugin-content.sh`) | The `apm-install` skill, or `curl -sSL https://aka.ms/apm-unix \| sh`. Verify with `apm --version` |
|
|
| `jq` | Required by `scripts/check-manifests.sh` and `scripts/sync-plugin-content.sh`, both pre-push | Your package manager |
|
|
| `python3` + PyYAML | Required by `scripts/skill-size-check.sh` (the `skill-size-check` pre-commit hook), which reads folded YAML frontmatter | `python3` is usually present — pre-commit is itself a Python application. `pip install pyyaml` if the hook reports PyYAML missing |
|
|
| `vale` | Required by the `vale-audit-prefilter-skill` / `-agent` pre-commit hooks and the `check-vale-style-sync` pre-push hook | `brew install vale` (macOS), `snap install vale` (Linux), `choco install vale` (Windows), or https://vale.sh/docs/vale-cli/installation/ |
|
|
| `claude` CLI | Required by the `validate-plugins` and `validate-marketplace` pre-push hooks | Claude Code |
|
|
| `go` toolchain | The gitea MCP server runs as `go run gitea.com/gitea/gitea-mcp@v1.7.0`, resolved from `PATH`. Without it the server fails to start and every `gitea-*` skill loses its tools | https://go.dev/dl/ — verify with `go version` |
|
|
|
|
The gitea MCP server additionally needs two environment variables exported in the shell that launches your agent. They are referenced as `${GITEA_ACCESS_TOKEN}` and `${GITEA_HOST}` in `plugins/gitea/.mcp.json`, and apm passes those references through to the deployed config unexpanded, so the values are resolved at server startup and never committed:
|
|
|
|
```bash
|
|
export GITEA_ACCESS_TOKEN='<your-gitea-token>'
|
|
export GITEA_HOST='https://<your-gitea-host>'
|
|
```
|
|
|
|
Generate the token in Gitea under Settings, Applications. Scope it to the repositories you want the agent to reach. If the server starts but every call returns an authorization error, that token is the first thing to check.
|
|
|
|
Two notes worth reading before you skip one:
|
|
|
|
- **PyYAML is a hard requirement, not an optional accelerator.** The hand-rolled fallback frontmatter reader was removed deliberately: a reader that mis-parses an unfamiliar scalar shape reports a clean pass on a file it never measured.
|
|
- **No `vale sync` is needed.** The `Kyberforge` styles are committed under `plugins/kyberforge/.apm/skills/{skill-audit,agent-audit}/assets/vale/styles/`, not downloaded packages (ADR-0014).
|
|
|
|
## Setup
|
|
|
|
Run these in order, from the repo root.
|
|
|
|
```bash
|
|
# 1. Deploy this repo's own skills and agents
|
|
apm install
|
|
|
|
# 2. Install the git hooks — all three stages
|
|
pre-commit install -t pre-commit -t commit-msg -t pre-push
|
|
```
|
|
|
|
**`apm install`** deploys the six plugins into `.claude/skills/` and `.claude/agents/`. Both are gitignored install output, *not* authoring source — `plugins/<name>/.apm/` remains the only place to edit. It needs the network, materializes `apm_modules/` (which stays gitignored), and also configures the `obsidian` MCP server into the repo's `.mcp.json`.
|
|
|
|
**Git hooks** must be wired for **all three stages**. This repo's `.pre-commit-config.yaml` has no `default_install_hook_types`, so a plain `pre-commit install` silently skips `commit-msg` (Conventional Commits) and `pre-push` (the full gate) — the `-t` flags above are not optional. The `pc-run` skill handles this and the troubleshooting around it, if you would rather not remember the flags.
|
|
|
|
## Keeping the install current
|
|
|
|
The six dependencies in root `apm.yml` are unpinned against the default branch, so deployed skills go stale whenever anyone merges. kyberforge ships a `SessionStart` hook that runs `apm outdated` at startup (~0.7s) and, when something is behind, runs `apm update --yes` and asks the host to re-scan skills (~10.4s).
|
|
|
|
That rewrites `apm.lock.yaml` — an unexplained modification to it after opening a session is expected, not a bug. Commit or discard it deliberately.
|
|
|
|
Note the difference between the two commands:
|
|
|
|
- `apm install` deploys from `apm.lock.yaml`. It does **not** pick up remote changes.
|
|
- `apm update` re-resolves refs. This is the command that pulls in a merged `.apm/` edit.
|
|
|
|
## Running tests
|
|
|
|
```bash
|
|
bash tests/run-tests.sh # every test-*.sh script plus the bats suite
|
|
bash tests/run-tests.sh --bats-only # just bats
|
|
```
|
|
|
|
The first run auto-initializes the bats submodules; no manual `git submodule update` needed.
|
|
|
|
A suite that exits 77 because a dependency is missing is reported as SKIPPED and does **not** fail an ad-hoc run. It *does* fail under `--strict` (equivalently `RUN_TESTS_STRICT=1`), which is how the pre-push hook invokes it — at pre-push, a skip means one of the prerequisites above is absent on this machine, and the gate would otherwise report success having run fewer suites than it appears to. The strict failure names each skipped suite and what to install.
|
|
|
|
## Before pushing
|
|
|
|
Run the pre-push gate locally in one command:
|
|
|
|
```bash
|
|
pre-commit run --hook-stage pre-push --all-files
|
|
```
|
|
|
|
One caveat: `check-release-needed` is a silent no-op under this invocation. It exits 0 unless
|
|
`PRE_COMMIT_REMOTE_BRANCH` is `refs/heads/main`, and pre-commit exports that only from the real
|
|
pre-push git hook during an actual `git push` — so the hook reports `Passed` having checked nothing.
|
|
Every other pre-push hook does run.
|
|
|
|
See [`docs/spec/gates.md`](docs/spec/gates.md) for what each hook enforces and why.
|
|
|
|
**Offline?** Exactly two pre-push hooks need the network, because root `apm.yml`'s marketplace contains one remote package entry that must be resolved with `git ls-remote`:
|
|
|
|
```bash
|
|
SKIP=apm-marketplace-check,apm-pack-check-clean git push
|
|
```
|
|
|
|
Skip **only** those two. The remaining pre-push hooks are real local checks and pass offline; adding one of them to `SKIP` disarms it silently.
|
|
|
|
## Editing plugin content
|
|
|
|
`plugins/<name>/.apm/` is the only hand-edited source for plugin content — skills, agents, commands, instructions, extensions, and hooks. The flat `plugins/<name>/{skills,agents,commands,instructions,extensions}/` directories, the merged `hooks/hooks.json`, and every `plugin.json` / `marketplace.json` manifest are generated. Nothing labels a generated file as generated, so check the path before you edit; an edit to the mirror is discarded by the next sync and reported as drift by the `check-plugin-content-sync` pre-push hook.
|
|
|
|
Hand-authored material that is *not* an `.apm/` primitive — `README.md`, `docs/`, `bin/`, `sources.md`, `.mcp.json` — lives at the plugin **root** and is untouched. Never place such a file inside a mirrored directory: the sync removes the destination before every copy, so it is deleted with no drift report.
|
|
|
|
Full detail in [`docs/spec/architecture.md`](docs/spec/architecture.md).
|
|
|
|
## For external consumers
|
|
|
|
Install a plugin natively from the marketplace manifests:
|
|
|
|
```bash
|
|
claude plugin install <name>@holocron
|
|
```
|
|
|
|
Or consume the packages through apm, the way this repo does — declare them as `dependencies.apm` git+path entries against the holocron remote and run `apm install`.
|
|
|
|
## Where to go next
|
|
|
|
- [`AGENTS.md`](AGENTS.md) — the rules for AI agents working in this repo
|
|
- [`CONTEXT.md`](CONTEXT.md) — domain language; read at the start of every session here
|
|
- [`docs/spec/architecture.md`](docs/spec/architecture.md) — directory structure, install pipeline, provider model
|
|
- [`docs/spec/gates.md`](docs/spec/gates.md) — the enforcement gates in depth
|
|
- [`docs/adr/`](docs/adr/) — architectural decisions; read before proposing structural changes
|
|
- [`docs/VISION.md`](docs/VISION.md) — where this is going
|
|
- [`LESSONS.md`](LESSONS.md) — things that went wrong once and should not again
|