diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index c28e71d..50246ec 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1,7 +1,7 @@ { "name": "holocron", "description": "AI development skills for Claude Code and GitHub Copilot CLI — factory, design, implement, review, and cross-cutting workflows.", - "version": "0.3.4", + "version": "0.4.1", "owner": { "name": "Defame1297", "email": "defame1297@rkdr.net", @@ -11,14 +11,14 @@ { "name": "kyberforge", "description": "Skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace.", - "version": "1.4.1", + "version": "1.5.0", "category": "Developer Tools", "source": "./plugins/kyberforge" }, { "name": "bin", "description": "A place for things to be binned", - "version": "1.1.2", + "version": "1.1.3", "category": "Utilities", "source": "./plugins/bin" }, diff --git a/.claude/settings.json b/.claude/settings.json index 9b97c07..deffac9 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,13 +1,3 @@ { - "enabledPlugins": { - "bin@holocron": true, - "core@holocron": true, - "git@holocron": true, - "gitea@holocron": true, - "kyberforge@holocron": true, - "lint@holocron": true - }, - "hooks": { - "PreToolUse": [] - } + "hooks": {} } diff --git a/.github/plugin/marketplace.json b/.github/plugin/marketplace.json index c28e71d..50246ec 100644 --- a/.github/plugin/marketplace.json +++ b/.github/plugin/marketplace.json @@ -1,7 +1,7 @@ { "name": "holocron", "description": "AI development skills for Claude Code and GitHub Copilot CLI — factory, design, implement, review, and cross-cutting workflows.", - "version": "0.3.4", + "version": "0.4.1", "owner": { "name": "Defame1297", "email": "defame1297@rkdr.net", @@ -11,14 +11,14 @@ { "name": "kyberforge", "description": "Skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace.", - "version": "1.4.1", + "version": "1.5.0", "category": "Developer Tools", "source": "./plugins/kyberforge" }, { "name": "bin", "description": "A place for things to be binned", - "version": "1.1.2", + "version": "1.1.3", "category": "Utilities", "source": "./plugins/bin" }, diff --git a/.gitignore b/.gitignore index fd6870e..0a023db 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,31 @@ node_modules/ # Claude Code local settings (machine-specific) .claude/settings.local.json + +# APM dependencies +apm_modules/ + +# APM install output — deployed copies of released plugin content, regenerated +# by `apm install`. The authoring source is plugins//.apm/; committing a +# deployed copy would add a third mirror of the same skills to drift against. +.claude/skills/ +.claude/agents/ + +# APM hook deployment output — `apm install` copies each package's referenced +# hook scripts here and tracks its own settings.json entries in the sidecar. +# Regenerated on every install; the authoring source is +# plugins//.apm/hooks/ (ADR-0019). +.claude/hooks/ +.claude/apm-hooks.json + +# `apm pack` bundle output. The pre-push gate runs pack with --dry-run, so this +# only appears after a bare `apm pack` during a release; it is not repo content. +build/ + +# `apm pack`'s manifest for the *root* package. Emitted beside the marketplace +# manifest by a bare `apm pack`, and never tracked on any branch — the repo's +# own paths hide it, since sync-plugin-content.sh redirects `apm pack -o` to a +# scratch tree and the apm-pack-check-clean pre-push hook runs --dry-run. Scoped +# to the file, not the directory: the sibling .claude-plugin/marketplace.json is +# compiled output that IS committed and must stay tracked. +/.claude-plugin/plugin.json diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000..7aa9712 --- /dev/null +++ b/.mcp.json @@ -0,0 +1,12 @@ +{ + "mcpServers": { + "obsidian": { + "args": [ + "@bitbonsai/mcpvault@0.15.0", + "docs/" + ], + "command": "npx", + "type": "stdio" + } + } +} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 58b9c09..67a3cf2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -89,6 +89,15 @@ repos: pass_filenames: false always_run: true + - id: check-executables-allow-sync + name: Check executables allow key sync + description: Verify root apm.yml's executables.allow key names kyberforge's actual version -- apm matches that key by exact "#" lookup, so a version bump on one side alone silently stops deploying kyberforge's hooks/ and bin/ and lets the apm install go stale (see ADR-0019) + entry: bash scripts/check-executables-allow-sync.sh + 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) diff --git a/AGENTS.md b/AGENTS.md index e5a9d43..f071c5d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ This repo is the global AI development configuration repository — the authorit ## Structure -- `plugins/` — installable plugin units; each is an apm package (`apm.yml` + `.apm/`) carrying skills, agents, hooks, MCP servers, and bundled assets; install separately via `claude plugin install @holocron` +- `plugins/` — installable plugin units; each is an apm package (`apm.yml` + `.apm/`) carrying skills, agents, hooks, MCP servers, and bundled assets. This repo consumes them through **apm**, not Claude Code's native plugin install: root `apm.yml` declares all six as `dependencies.apm` git+path entries against the holocron remote, and `apm install` deploys them into `.claude/skills/` and `.claude/agents/` (both gitignored). External consumers can still install natively via `claude plugin install @holocron` — the marketplace manifests are unchanged - `providers/claude-code/` — Claude Code adapter (deployed to `~/.claude/` via `install.sh`) ## Edit `.apm/`, never the flat mirror @@ -25,29 +25,34 @@ Nothing labels a generated file as generated — `plugins/kyberforge/skills/forg This repo dogfoods its own plugins. Before shelling out to git, gitea, or lint tooling directly, check whether an installed skill already owns the operation — it usually does: -- Commits, branches, history, worktrees, remotes → `git:git-commits`, `git:git-branches`, `git:git-history`, `git:git-worktrees`, `git:git-remotes` -- Pre-commit hook install/config/troubleshooting → `git:pc-run` / `git:pc-author` -- Issues, PRs, labels, milestones → `gitea:gitea-issues`, `gitea:gitea-prs`, `gitea:gitea-labels-milestones`; also `gitea:gitea-branches`, `gitea:gitea-files`, `gitea:gitea-releases`, or `gitea:gitea-workflow` when the domain is ambiguous -- Vale prose linting → `lint:vale-config` / `lint:vale-run` -- This repo's own AGENTS.md → `core:agentsmd-author` / `core:agentsmd-audit` +- Commits, branches, history, worktrees, remotes → `git-commits`, `git-branches`, `git-history`, `git-worktrees`, `git-remotes` +- Pre-commit hook install/config/troubleshooting → `pc-run` / `pc-author` +- Issues, PRs, labels, milestones → `gitea-issues`, `gitea-prs`, `gitea-labels-milestones`; also `gitea-branches`, `gitea-files`, `gitea-releases`, or `gitea-workflow` when the domain is ambiguous +- Vale prose linting → `vale-config` / `vale-run` +- This repo's own AGENTS.md → `agentsmd-author` / `agentsmd-audit` + +Use the bare, **unnamespaced** names above. Under the old `claude plugin install` these were `git:git-commits`, `kyberforge:skill-audit`, and so on; `apm install` deploys each skill to `.claude/skills//` as a plain project skill, which has no plugin prefix to carry. The `:` form has not stopped resolving here, though — `~/.claude.json` still enables `core`, `git`, `gitea`, `kyberforge`, and `lint` at **user** scope, and ADR-0018 left those native installs in place on purpose, converting them being a separate decision with a blast radius beyond this repo. Every skill is therefore live under both names right now, and a working `gitea:gitea-prs` is the user-scope copy answering — not evidence that the apm install or this file is broken, and not something to "fix". Prefer the bare name anyway: apm deploys it, an external consumer installing holocron through apm gets it, and it is the form that survives those user-scope installs eventually being converted. The namespaced form also still resolves in any project that installs holocron natively, so a skill body written for both audiences should name the bare skill. Same for agents: `git-orchestrate`, not `git:git-orchestrate`. Fall back to raw shell only when no skill covers it. ## 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` (the 13-hook gate described below). -- Install the `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`, which wraps `apm pack`). `apm-marketplace-check` and `apm-pack-check-clean` are bare `apm …` hook entries and `apm-audit-ci` is a `bash -c` loop calling `apm` once per package, so without it the push dies with an unhelpful "command not found". Use `kyberforge:apm-install`, or `curl -sSL https://aka.ms/apm-unix | sh`; verify with `apm --version`. +- Run `apm install` to deploy this repo's own skills and agents into `.claude/skills/` and `.claude/agents/`. Both are gitignored install output, not authoring source — `plugins//.apm/` remains the only place to edit. The six dependencies in root `apm.yml` resolve from the holocron **remote**, unpinned against the default branch, so a `.apm/` edit is not visible to the running session until it is pushed and `apm update` re-runs (`apm install` deploys from `apm.lock.yaml` and does not re-resolve refs). Needs the network, and needs `apm_modules/` (which it materializes) left gitignored. `apm install` also configures the `obsidian` MCP server into the repo's `.mcp.json`, carried over from `plugins/bin/.mcp.json`. +- Do not add repo-owned keys to `.claude/settings.json`. apm treats that file as its own deployed artifact: `apm audit --ci` replays the install into a scratch tree and diffs, so anything apm would not have written there — an `enabledPlugins` block, a real `hooks` entry — is permanent drift that fails the `apm-audit-ci` pre-push hook. Its committed content is whatever apm last wrote — `{"hooks": {}}` until kyberforge's `SessionStart` hook lands there, after which the merged hook entry is apm's output and belongs in the commit (ADR-0019). What does not change is that nothing repo-authored goes in the file. A hook you want in this repo is authored in `plugins//.apm/hooks/` and deployed by apm, never hand-written here. Machine-specific settings go in the gitignored `.claude/settings.local.json`, which apm does not deploy and the replay does not compare; shared enforcement belongs in `.pre-commit-config.yaml`. +- Keeping the install current is automatic but not free. Because the six dependencies are unpinned, 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`, so an unexplained modification to it after opening a session is expected, not a bug — commit or discard it deliberately. Note `apm install` alone will **not** pick up remote changes; it deploys from the lock. `apm update` is the command that re-resolves refs. +- Install git hooks via `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` (the 14-hook gate described below). +- Install the `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`, which wraps `apm pack`). `apm-marketplace-check` and `apm-pack-check-clean` are bare `apm …` hook entries and `apm-audit-ci` is a `bash -c` loop calling `apm` once per package, so without it the push dies with an unhelpful "command not found". Use `apm-install`, or `curl -sSL https://aka.ms/apm-unix | sh`; verify with `apm --version`. - Install `jq` — required by `scripts/check-manifests.sh` and `scripts/sync-plugin-content.sh`, both pre-push. These at least fail loudly (`Error: jq is required but not installed`). - Install the `vale` binary — required by the `vale-audit-prefilter-skill`/`-agent` pre-commit hooks. Their `files:` patterns are `.apm/`-scoped: `^plugins/[^/]+/\.apm/skills/[^/]+/SKILL\.md$` and `^plugins/[^/]+/\.apm/agents/[^/]+\.agent\.md$`. Only the authoring source triggers them — a `SKILL.md` in the generated mirror matches neither pattern, so prose findings surface only when you edit the file you are supposed to be editing. Without the binary 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). - `vale` is also a **pre-push** dependency, not only pre-commit. `check-vale-style-sync` runs six glob-coverage probes by invoking `vale --config` — they are the only assertions in it that catch a `.vale.ini` glob typo, the failure mode where every text-level check stays clean while vale lints zero files. Missing `vale` is therefore a hard failure there. The opt-out is `CHECK_VALE_STYLE_SYNC_ALLOW_MISSING_VALE=1`, and it is **not** `SKIP=`: the hook still runs and still asserts everything verifiable from file text, but the six probes do not, and its summary says so explicitly — `Vale style sync check passed (text-level only, vale unavailable): … 0 glob probe(s) verified`. Use it only on a machine that genuinely cannot install `vale`, and read that summary line as "the glob axis was not checked", not as a pass. - 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. - A suite that exits 77 because a dependency is missing is reported as SKIPPED, and does **not** fail an ad-hoc run. The pre-push hook invokes the same script as `--strict` (`RUN_TESTS_STRICT=1` is equivalent), where a skip **does** fail the push: at pre-push a skip means one of the dependencies above is absent on this machine, so the gate would otherwise report success having run fewer suites than it appears to. Without vale, for instance, three suites skip (`test-check-vale-style-sync.sh`, `test-vale-hooks-consumer.sh`, `test-vale-wrap.sh`) and the strict failure names each one and what to install. -- `tests/run-bats.sh` derives the set of `.bats` files it expects from `git ls-files`, so a `.bats` file deleted from the worktree but still tracked in the index fails the run rather than silently shrinking the suite. Remove one with `git rm` (or stage the deletion) when the removal is intentional; an untracked new `.bats` file is picked up and needs no ceremony. -- Pushing runs 13 repo-defined pre-push hooks, not just the test suite — `run-tests` and `check-manifests`, plus generated-content drift gates (`check-plugin-content-sync`, `check-marketplace-mirror-sync`, `check-vale-style-sync`, `check-scope-walkup-sync`), artifact validators (`check-apm-agents-valid`, which runs agent-audit's `validate.sh` over every real `plugins/*/.apm/agents/*.agent.md`), apm's own gates (`apm-marketplace-check`, `apm-audit-ci`, `apm-pack-check-clean`), host validators (`validate-plugins`, `validate-marketplace`, both needing the `claude` CLI), and `check-release-needed`. Run `pre-commit run --hook-stage pre-push --all-files` locally — one command, the whole gate. That command reports **15**, not 13: pre-commit's own `meta` hooks, `check-hooks-apply` and `check-useless-excludes`, declare no `stages:` and so run at every stage including this one. +- `tests/run-bats.sh` derives the set of `.bats` files it expects from `git ls-files`, so a `.bats` file deleted from the worktree but still tracked in the index fails the run rather than silently shrinking the suite. Remove one with `git rm` (or stage the deletion) when the removal is intentional; an untracked new `.bats` file is picked up and needs no ceremony. Both discovery walks (`tests/run-bats.sh` and `tests/run-tests.sh`) exclude `apm_modules/`: `apm install` materializes a full copy of every plugin there, and running a dependency's copy of a `.bats` file breaks its relative path to the bats helpers — 167 spurious failures before the exclusion landed. +- Pushing runs 14 repo-defined pre-push hooks, not just the test suite — `run-tests` and `check-manifests`, plus generated-content drift gates (`check-plugin-content-sync`, `check-marketplace-mirror-sync`, `check-vale-style-sync`, `check-scope-walkup-sync`, `check-executables-allow-sync`), artifact validators (`check-apm-agents-valid`, which runs agent-audit's `validate.sh` over every real `plugins/*/.apm/agents/*.agent.md`), apm's own gates (`apm-marketplace-check`, `apm-audit-ci`, `apm-pack-check-clean`), host validators (`validate-plugins`, `validate-marketplace`, both needing the `claude` CLI), and `check-release-needed`. `check-executables-allow-sync` is the odd one in that first group — it guards a silent failure rather than drift in generated text. apm gates a package's `hooks/` and `bin/` on an exact `#` lookup in root `apm.yml`'s `executables.allow`, with no wildcard and no version-less form, so bumping `plugins/kyberforge/apm.yml`'s `version:` without bumping the key errors nowhere: the entry simply stops matching, kyberforge's `SessionStart` hook stops deploying, and the install goes quietly stale — the failure ADR-0019 records as live. Run `pre-commit run --hook-stage pre-push --all-files` locally — one command, the whole gate. That command reports **16**, not 14: pre-commit's own `meta` hooks, `check-hooks-apply` and `check-useless-excludes`, declare no `stages:` and so run at every stage including this one. - `apm-audit-ci` runs `apm audit --ci` once per manifest — the root one and each of the six plugin packages — because the root-only invocation audits the marketplace manifest and **nothing else**, and `apm-pack-check-clean` does not parse plugin `dependencies:` blocks either (verified: a malformed one passes `apm pack --check-versions --check-clean --dry-run` and fails `apm audit --ci` in that package's directory). It verifies two things and claims no more: each `apm.yml` parses as a valid APM manifest, and any package declaring dependencies has a consistent `apm.lock.yaml`. It does **not** enforce an org policy — apm discovers one from the git remote and only understands github.com and Azure DevOps, so against this repo's self-hosted Gitea remote it prints `No org policy found at unknown; enforcement skipped`. Do **not** "fix" that with `policy.fetch_failure_default: block` in `apm.yml`: it was tested and rejected, because with no reachable policy source it makes the hook exit 1 on every push forever. - `check-apm-agents-valid` derives its expected agent-file set from `git ls-files` (same pattern as `tests/run-bats.sh`), so an agent file deleted from the worktree but still tracked fails the run, and discovering zero agent files is an error rather than a pass. An untracked new agent file is still validated — the derivation is one-directional on purpose, so uncommitted work is not blocked but also cannot bypass the gate. -- **Two** pre-push hooks need the network, for one shared reason: root `apm.yml`'s `marketplace.packages[]` contains exactly one remote entry (`mattpocock-skills`, `source: mattpocock/skills`), and resolving it needs a `git ls-remote`. `apm-marketplace-check` resolves every entry and is `always_run`, so it fails with `No cached refs (offline)`. `apm-pack-check-clean` (`apm pack --check-versions --check-clean --dry-run`) re-resolves the same entry and fails with `Error: Git network timeout during ls-remote`. Pinning the entry to an exact version does **not** remove the call — an exact pin still ls-remotes. `--offline` rescues neither. To push without a network, skip both using pre-commit's own mechanism: `SKIP=apm-marketplace-check,apm-pack-check-clean git push`. Skip those two alone — verified under `unshare -rn`, the other eleven pre-push hooks pass offline because they are real local checks, and adding one of them to `SKIP` disarms it silently. `apm-audit-ci` calls `apm` too but stays local: its org-policy discovery resolves nothing on this remote before any network call, so it does not join the pair above. -- Author commits with `git:git-commits` — it validates Conventional Commits (enforced at `commit-msg`) for you. +- **Two** pre-push hooks need the network, for one shared reason: root `apm.yml`'s `marketplace.packages[]` contains exactly one remote entry (`mattpocock-skills`, `source: mattpocock/skills`), and resolving it needs a `git ls-remote`. `apm-marketplace-check` resolves every entry and is `always_run`, so it fails with `No cached refs (offline)`. `apm-pack-check-clean` (`apm pack --check-versions --check-clean --dry-run`) re-resolves the same entry and fails with `Error: Git network timeout during ls-remote`. Pinning the entry to an exact version does **not** remove the call — an exact pin still ls-remotes. `--offline` rescues neither. To push without a network, skip both using pre-commit's own mechanism: `SKIP=apm-marketplace-check,apm-pack-check-clean git push`. Skip those two alone — verified under `unshare -rn`, the other twelve pre-push hooks pass offline because they are real local checks (`check-executables-allow-sync` landed after that run, but reads two local manifests and makes no network call), and adding one of them to `SKIP` disarms it silently. `apm-audit-ci` calls `apm` too but stays local: its org-policy discovery resolves nothing on this remote before any network call, so it does not join the pair above. +- Author commits with `git-commits` — it validates Conventional Commits (enforced at `commit-msg`) for you. ## Key documents diff --git a/CONTEXT.md b/CONTEXT.md index 02f4112..fa44721 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -24,14 +24,25 @@ 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. ### 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//.apm/skills//SKILL.md`, available after the plugin is installed (`claude plugin install @`). 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). Authored at `plugins//.apm/skills//SKILL.md` and reaching a host by one of two install paths: `apm install`, which deploys the skill directory to `.claude/skills//` (this repo's own path — see "apm-consumed install"), or `claude plugin install @`, which caches the whole plugin (still supported for external consumers). Skills are self-contained — they cannot reference files outside the plugin directory after install-time caching. The two paths name skills differently: apm deploys a plain project skill (`skill-audit`), a plugin install namespaces it (`kyberforge:skill-audit`). ### 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. In this repo, plugins live under `plugins//`, 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 at the plugin root plus a merged hooks file at `hooks/hooks.json`, generated by `scripts/sync-plugin-content.sh` — Claude Code's and Copilot's installers convention-scan only these flat paths (`hooks/hooks.json` is the convention path for hooks specifically; a root-level `hooks.json` is scanned by nothing and is deleted as stale by a sync — see ADR-0017's 2026-08-14 amendment) 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 @`. +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//`, 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 at the plugin root plus a merged hooks file at `hooks/hooks.json`, generated by `scripts/sync-plugin-content.sh` — Claude Code's and Copilot's installers convention-scan only these flat paths (`hooks/hooks.json` is the convention path for hooks specifically; a root-level `hooks.json` is scanned by nothing and is deleted as stale by a sync — see ADR-0017's 2026-08-14 amendment) 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 @`, or consume it as an apm dependency (see "apm-consumed install"). ### 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 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//` root, not at an `apm pack` build artifact — which is why that root also carries the flat `agents/`/`skills/`/`commands/`/`hooks/hooks.json` content mirror described under "Plugin" (ADR-0017): without it, an install from this marketplace finds a valid manifest but no discoverable content. +### apm-consumed install +How this repo installs its own plugins, as of 2026-08-14: not `claude plugin install @holocron`, but six `dependencies.apm` entries in the root `apm.yml`, each a `git:`/`path:` object against the holocron remote, deployed by `apm install` into `.claude/skills/` and `.claude/agents/`. Project scope only — apm installs nothing at user scope, so the switch is contained to this repo and any other repo opts in by declaring its own dependencies. The git+path object form is deliberate over the shorter `@holocron` marketplace alias: an alias must first be registered with `apm marketplace add`, which writes to `~/.apm/marketplaces.json` (user scope, outside the repo), whereas the object form needs nothing beyond the committed manifest and so survives a fresh clone. + +Four consequences, each load-bearing: +- **Skills gain an unnamespaced name.** apm deploys plain project skills, so `git:git-commits` also answers to `git-commits`. The `:` form has not stopped resolving here: `~/.claude.json` still enables `core`, `git`, `gitea`, `kyberforge`, and `lint` at user scope, which ADR-0018 left in place deliberately — converting them is a separate decision with a blast radius beyond this repo. Until it is taken, every skill is live under two names, which is the same "present twice under two names" outcome ADR-0018's own "Alternatives considered" rejected for *keeping both install paths* — reached here by leaving user scope alone rather than by adopting it as the install model. Write the bare name regardless: apm deploys it, and a repo consuming holocron through apm gets only that form. The namespaced form still resolves wherever holocron is installed natively, so cross-audience skill bodies should use the bare name. +- **apm owns `.claude/settings.json`.** `apm audit --ci` (an `apm-audit-ci` pre-push hook) replays the install into a scratch tree and diffs it against the worktree, so any key apm would not have written is permanent drift. Committed content is exactly `{"hooks": {}}`; repo-owned settings have nowhere to live in that file. +- **Install output is gitignored.** `.claude/skills/`, `.claude/agents/`, and `apm_modules/` are all regenerated by `apm install`. `apm.lock.yaml` and the generated `.mcp.json` are committed. Committing the deployed skills would add a third mirror of the same content to the two ADR-0017 already governs. +- **Test discovery must skip `apm_modules/`.** It holds a full copy of every plugin, `.bats` files included; both `tests/run-bats.sh` and `tests/run-tests.sh` exclude it. + +Dependencies are unpinned against the default branch, matching the `autoUpdate: true` the native marketplace install had. The practical cost is a round trip: an edit to `plugins//.apm/` is invisible locally until it is pushed and `apm install` re-runs, because the dependency resolves from the remote rather than from the working tree beside it. + ### 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. diff --git a/apm.lock.yaml b/apm.lock.yaml new file mode 100644 index 0000000..951e332 --- /dev/null +++ b/apm.lock.yaml @@ -0,0 +1,2702 @@ +lockfile_version: '1' +generated_at: '2026-08-14T16:50:21.373462+00:00' +apm_version: 0.28.0 +dependencies: +- repo_url: Defame1297/holocron + name: bin + host: git.dev.rkdr.net + resolved_commit: c3ec5f2d3d00d625f632de3c40d1919e16727988 + version: 1.1.2 + virtual_path: plugins/bin + is_virtual: true + package_type: marketplace_plugin + deployed_files: + - .claude/skills/caveman + - .claude/skills/caveman/SKILL.md + - .claude/skills/diagnose + - .claude/skills/diagnose/SKILL.md + - .claude/skills/diagnose/scripts/hitl-loop.template.sh + - .claude/skills/grill-me + - .claude/skills/grill-me/SKILL.md + - .claude/skills/grill-with-docs + - .claude/skills/grill-with-docs/ADR-FORMAT.md + - .claude/skills/grill-with-docs/CONTEXT-FORMAT.md + - .claude/skills/grill-with-docs/SKILL.md + - .claude/skills/improve-codebase-architecture + - .claude/skills/improve-codebase-architecture/DEEPENING.md + - .claude/skills/improve-codebase-architecture/INTERFACE-DESIGN.md + - .claude/skills/improve-codebase-architecture/LANGUAGE.md + - .claude/skills/improve-codebase-architecture/SKILL.md + - .claude/skills/prototype + - .claude/skills/prototype/LOGIC.md + - .claude/skills/prototype/SKILL.md + - .claude/skills/prototype/UI.md + - .claude/skills/research + - .claude/skills/research/META.md + - .claude/skills/research/SKILL.md + - .claude/skills/research/references/file-format.md + - .claude/skills/research/references/topics.md + - .claude/skills/tdd + - .claude/skills/tdd/SKILL.md + - .claude/skills/tdd/deep-modules.md + - .claude/skills/tdd/interface-design.md + - .claude/skills/tdd/mocking.md + - .claude/skills/tdd/refactoring.md + - .claude/skills/tdd/tests.md + - .claude/skills/triage + - .claude/skills/triage/AGENT-BRIEF.md + - .claude/skills/triage/OUT-OF-SCOPE.md + - .claude/skills/triage/SKILL.md + - .claude/skills/write-docs + - .claude/skills/write-docs/SKILL.md + - .claude/skills/zoom-out + - .claude/skills/zoom-out/SKILL.md + deployed_file_hashes: + .claude/skills/caveman/SKILL.md: sha256:b4bb6fe3bf3da222993ca0aee5e7c6f5368b182dd30ce1172e8f8e3280eb7052 + .claude/skills/diagnose/SKILL.md: sha256:28886402bbfa0470248086eab9106a103b964b76ae9496e63ff0c8a6761b6d13 + .claude/skills/diagnose/scripts/hitl-loop.template.sh: sha256:b2932630950e5210075bcd6f850e5accf30c101c5367b29eac3a29b4dd8084c8 + .claude/skills/grill-me/SKILL.md: sha256:74147eb6010a65957efef2b9e0f0b3ff935c1def7fc117697151b1d0f3610556 + .claude/skills/grill-with-docs/ADR-FORMAT.md: sha256:f1f36cd3f8d3b6474ddd5855da4e233bfc4ae1a1c5024909ccf11871819a41b2 + .claude/skills/grill-with-docs/CONTEXT-FORMAT.md: sha256:8f6baaa3b1c91644bd7c600196b1aee781d5f525c7c345db8cdfbfb368329a05 + .claude/skills/grill-with-docs/SKILL.md: sha256:c7176600a8bffc9f359102cb041b6686387618b8e1434ba484dd708d45df6d5e + .claude/skills/improve-codebase-architecture/DEEPENING.md: sha256:9577485f4fc32c0267639a9151bb41c8af0f8f6086e4bf8b84d5b236e30604e9 + .claude/skills/improve-codebase-architecture/INTERFACE-DESIGN.md: sha256:678c3e34f1339015053212b3316bf0b676c70aa251050a0613667d4e755fb35e + .claude/skills/improve-codebase-architecture/LANGUAGE.md: sha256:6feca2140439c54a774749e8367f18350899ff69c777144ed2248cd4407949fa + .claude/skills/improve-codebase-architecture/SKILL.md: sha256:318b3df6a557f3a9216e6c32813d543301fa51954ea79c87edb95e1297213584 + .claude/skills/prototype/LOGIC.md: sha256:da91dc92195c00d5dc33863b8c1a030998025a0f8583ebf2babd770825b7f70c + .claude/skills/prototype/SKILL.md: sha256:913df144c79658394b7ccc984858103a27553eeff9a8bf93dc51486603259d0f + .claude/skills/prototype/UI.md: sha256:d76d565149ee50456c5ddc5e29c27fec8737637874fe5c37d970db085a200b27 + .claude/skills/research/META.md: sha256:81a31d9ba20988b07bd4e141f0a7369d4dda302772d62809cc8b223ca1fbb84f + .claude/skills/research/SKILL.md: sha256:9ca6b3b8c9c1d04f93a6ef55149d0eecc9ffa82a28db9fb3f9b588c661b1b747 + .claude/skills/research/references/file-format.md: sha256:29d5583beaa531d26895c54def112039730490c312401648a6bdb7dd5278fb3c + .claude/skills/research/references/topics.md: sha256:757abb7b6be44c3d3c0dd1f4818a344e08877810d36bf8be60ea7189cba8d12f + .claude/skills/tdd/SKILL.md: sha256:af059705061156fd4845ddbb736fe92b564118ac5f03551e09ef9e8f6d970638 + .claude/skills/tdd/deep-modules.md: sha256:f2123700bf953db1740625686c75bdd145efd486357c47c6818c869fee657a32 + .claude/skills/tdd/interface-design.md: sha256:764c5ff0e3fa6b4ab7095eb65ccc7201e090baf19fa16051dfdb72c06d27417d + .claude/skills/tdd/mocking.md: sha256:3ceb807fdf4a47d6a93d4d9a891e5ba6d362a6247bd08adc451feebfc17361ef + .claude/skills/tdd/refactoring.md: sha256:54fced22dd1911b7094c3fe7979b7c1a40d40be307482c4adf7dc0588f27d6cc + .claude/skills/tdd/tests.md: sha256:e12182f5c4c6a844b7c6ab4b8d2623b642aaaae5460fe58ed087efd4f3d5fb8a + .claude/skills/triage/AGENT-BRIEF.md: sha256:1c71c747aa97d938f75ec7a4b8fe87451763b38a3ce6348acf2b222e5819b2e5 + .claude/skills/triage/OUT-OF-SCOPE.md: sha256:8ed8cf27833444060c81b3961a83c0e3d8e6cf2fcb2ddf6f8b07c6655cbb0d85 + .claude/skills/triage/SKILL.md: sha256:b819f0285e4e5814ac6472d0217f07dc9f23d2fdae3ebb0dd31d98360d8ac029 + .claude/skills/write-docs/SKILL.md: sha256:0d06d0f6836a67532497ea61bd5a1294a8d80f529bf1ffb5c4b1fdc72e9cb51a + .claude/skills/zoom-out/SKILL.md: sha256:2a6894c7f9b1c9c55f451c625a834c4f377e217b623a85280e55db5fe9cacf48 + content_hash: sha256:68316a438855468cd6d1286b3b458e68db4815149bd267c285c11027e98bc8f4 + declared_license: MIT +- repo_url: Defame1297/holocron + name: core + host: git.dev.rkdr.net + resolved_commit: c3ec5f2d3d00d625f632de3c40d1919e16727988 + version: 1.1.1 + virtual_path: plugins/core + is_virtual: true + package_type: marketplace_plugin + deployed_files: + - .claude/skills/agentsmd-audit + - .claude/skills/agentsmd-audit/README.md + - .claude/skills/agentsmd-audit/SKILL.md + - .claude/skills/agentsmd-audit/references/sources.md + - .claude/skills/agentsmd-audit/scripts/README.md + - .claude/skills/agentsmd-audit/scripts/validate-drift.sh + - .claude/skills/agentsmd-audit/scripts/validate-secrets.sh + - .claude/skills/agentsmd-audit/scripts/validate-structure.sh + - .claude/skills/agentsmd-author + - .claude/skills/agentsmd-author/README.md + - .claude/skills/agentsmd-author/SKILL.md + - .claude/skills/agentsmd-author/references/content-guide.md + - .claude/skills/agentsmd-author/references/sources.md + - .claude/skills/provider-adapter-author + - .claude/skills/provider-adapter-author/README.md + - .claude/skills/provider-adapter-author/SKILL.md + - .claude/skills/provider-adapter-author/references/sources.md + - .claude/skills/provider-adapter-author/scripts/README.md + - .claude/skills/provider-adapter-author/scripts/validate-adapter.sh + deployed_file_hashes: + .claude/skills/agentsmd-audit/README.md: sha256:5d487aadb0424eb162f19454488e3b923180163cf1a543bb977b6969ae7a6849 + .claude/skills/agentsmd-audit/SKILL.md: sha256:f4e1d3f1fa79ebd4c2c874dfb2fe1b86834c9ae708ab3f0a3fa07be39e90b552 + .claude/skills/agentsmd-audit/references/sources.md: sha256:ce13d263414c4c8ea96a33a9d3e0d5535c3100fb1e54a26d2bfe218fc7cd5974 + .claude/skills/agentsmd-audit/scripts/README.md: sha256:40a7e90e0823a6caaa0276e8d13a3c1bdc7afc533912166aad3454b979824d6a + .claude/skills/agentsmd-audit/scripts/validate-drift.sh: sha256:941e521498fa77b483529a73afc13d1d1630876d9035bd3bd020ac3bc3e5a18a + .claude/skills/agentsmd-audit/scripts/validate-secrets.sh: sha256:2b53d9d7d0891da4d83ddfec759dcdd7429468360dbc238c0b7e8af816054241 + .claude/skills/agentsmd-audit/scripts/validate-structure.sh: sha256:21507848d60b341165880294feb3a2f80dec55b1814b24e94647988da1fdfdd7 + .claude/skills/agentsmd-author/README.md: sha256:096adbfc44e87da5dd8a8ad0e5eebc0cf4b9d515ab7d7fde4ec80ff3515bfbc7 + .claude/skills/agentsmd-author/SKILL.md: sha256:c32b9fe1cc7966d21d1ec504396d492afa53eb6369bd3b2678aed4ba92f331f8 + .claude/skills/agentsmd-author/references/content-guide.md: sha256:1b10c956254c949492c2993991c5fd86417db8b64aefbc45695087bb9c021e36 + .claude/skills/agentsmd-author/references/sources.md: sha256:aecbe65ba9526f694534989cbb516c65a55f75553195753566abccd6841359fe + .claude/skills/provider-adapter-author/README.md: sha256:9aa132d5ffa7f249fa4c8f0e90f8505c1f1d0dcdd681e7930e5b0236407038b5 + .claude/skills/provider-adapter-author/SKILL.md: sha256:7d2d9c9555373fdac86683c517fcccca1d090347c9c1d13f91942763219d2053 + .claude/skills/provider-adapter-author/references/sources.md: sha256:45dfe2ff95744d7a00fa5b75be216562ff84047f21588ef244e89bdc2e5ae1bb + .claude/skills/provider-adapter-author/scripts/README.md: sha256:196aa7752b2df4710b397e1927f68b0b966caa8a3b1006122ef6e184882af01b + .claude/skills/provider-adapter-author/scripts/validate-adapter.sh: sha256:519c8ab3d33884821a6090ec5ba0322ec1594f9c1ca388b44279133a1e0fa9be + content_hash: sha256:df3239f8ebc54ecbf4d91325bfd056cbd8919f3b20f85721906c9de8776c4927 + declared_license: MIT +- repo_url: Defame1297/holocron + name: git + host: git.dev.rkdr.net + resolved_commit: c3ec5f2d3d00d625f632de3c40d1919e16727988 + version: 1.3.3 + virtual_path: plugins/git + is_virtual: true + package_type: marketplace_plugin + deployed_files: + - .claude/agents/git-orchestrate.md + - .claude/skills/git-branches + - .claude/skills/git-branches/README.md + - .claude/skills/git-branches/SKILL.md + - .claude/skills/git-branches/references/sources.md + - .claude/skills/git-commits + - .claude/skills/git-commits/README.md + - .claude/skills/git-commits/SKILL.md + - .claude/skills/git-commits/references/commit-template.md + - .claude/skills/git-commits/references/conventional-commits-spec.md + - .claude/skills/git-commits/references/sources.md + - .claude/skills/git-history + - .claude/skills/git-history/README.md + - .claude/skills/git-history/SKILL.md + - .claude/skills/git-history/references/README.md + - .claude/skills/git-history/references/git-log-format.md + - .claude/skills/git-history/references/sources.md + - .claude/skills/git-remotes + - .claude/skills/git-remotes/README.md + - .claude/skills/git-remotes/SKILL.md + - .claude/skills/git-remotes/references/README.md + - .claude/skills/git-remotes/references/remotes.md + - .claude/skills/git-remotes/references/sources.md + - .claude/skills/git-submodules + - .claude/skills/git-submodules/README.md + - .claude/skills/git-submodules/SKILL.md + - .claude/skills/git-submodules/references/README.md + - .claude/skills/git-submodules/references/sources.md + - .claude/skills/git-submodules/references/submodules.md + - .claude/skills/git-workflow + - .claude/skills/git-workflow/README.md + - .claude/skills/git-workflow/SKILL.md + - .claude/skills/git-workflow/references/README.md + - .claude/skills/git-workflow/references/sources.md + - .claude/skills/git-worktrees + - .claude/skills/git-worktrees/README.md + - .claude/skills/git-worktrees/SKILL.md + - .claude/skills/git-worktrees/references/README.md + - .claude/skills/git-worktrees/references/sources.md + - .claude/skills/git-worktrees/references/worktrees.md + - .claude/skills/pc-author + - .claude/skills/pc-author/README.md + - .claude/skills/pc-author/SKILL.md + - .claude/skills/pc-author/references/README.md + - .claude/skills/pc-author/references/hooks-by-language.md + - .claude/skills/pc-author/references/sources.md + - .claude/skills/pc-run + - .claude/skills/pc-run/README.md + - .claude/skills/pc-run/SKILL.md + - .claude/skills/pc-run/references/README.md + - .claude/skills/pc-run/references/failure-patterns.md + - .claude/skills/pc-run/references/sources.md + deployed_file_hashes: + .claude/agents/git-orchestrate.md: sha256:95a48c8e62cbb5d8f5bb2aa80c253d2f8b3a3988cf6202c10ae4feae1266d64d + .claude/skills/git-branches/README.md: sha256:1d59fc21bcaad298a81e5b1c13cf2a396b13ad59a09eeb63f95165f9216755d3 + .claude/skills/git-branches/SKILL.md: sha256:29d19cee09cd3be3d26db86a228aca9d41833c53945e53e5b26e4339f5c35f0c + .claude/skills/git-branches/references/sources.md: sha256:ee1c982caf67183acc51226bb5078b3409bb6965b720568f10d67e5f976ccb72 + .claude/skills/git-commits/README.md: sha256:c686fd6dd54c608889168cfeaf62e659d66e681194a95aaf5f84859513fda800 + .claude/skills/git-commits/SKILL.md: sha256:99826526958edffc2a897cc9622e3b2cf11c1bd9995c4a338a23758e62aad9de + .claude/skills/git-commits/references/commit-template.md: sha256:456d80208901106270687906c90bc35eb64595800c05b7b9340fe96cc1cf6a57 + .claude/skills/git-commits/references/conventional-commits-spec.md: sha256:b131b5a2edb9a5acd6bdcb72107099a26bbc172b9a5eb845da4daa2176e962f9 + .claude/skills/git-commits/references/sources.md: sha256:7ef0293a96884c8935e3384cfb1c3c2c274d0aa0771ba8ac275e68f568d433f5 + .claude/skills/git-history/README.md: sha256:b285b5b8d771dd2f3482db8b2bd79835ebb6273bffbd8d01bca17d683baac6fa + .claude/skills/git-history/SKILL.md: sha256:d44ca7f6026624da21f9d31f1bb7a03524d83f30f2e83b54b0f72e9dc1524dfc + .claude/skills/git-history/references/README.md: sha256:96ef45f979c692acc9a8f745570d506efe8ed8a57015de02dc699bd77018c463 + .claude/skills/git-history/references/git-log-format.md: sha256:e3a0711d7d2a7053bbd1bcd70832c65dd3348266c0fb330fd80a0b8f7c8f858b + .claude/skills/git-history/references/sources.md: sha256:4a9c9c2ff80193ad4c751ab4baf4c9c42db038a7ea5156733e3253c335912f5b + .claude/skills/git-remotes/README.md: sha256:7f51c47e1671de9f0a63a8836b16caa68727105b62a4240716e457ed8d401f97 + .claude/skills/git-remotes/SKILL.md: sha256:801a81a26117ee38130d9171f9b16c6ae7a281ec3aa3535fee30578eb29c7908 + .claude/skills/git-remotes/references/README.md: sha256:d590a7f2eb078710f44893b4866e821c840f48b7360f0779420d73331a3de597 + .claude/skills/git-remotes/references/remotes.md: sha256:80229bbd6d9e03e85e5ff49018181fb71003187f2c5e019869807e4e94aa4a46 + .claude/skills/git-remotes/references/sources.md: sha256:33e966aba6946cd78d2b9e325670e5292ccdf73d90fcdfca70afb30ee70c1f2d + .claude/skills/git-submodules/README.md: sha256:1510e9691d5107c436bda3497066950bfd2e9c9612cdf8ebd40f5ec73780ddea + .claude/skills/git-submodules/SKILL.md: sha256:3b4525c5e7145d30f4a945bf5f2429cca270f00e88ed85ff79c6d5fa89fa05ca + .claude/skills/git-submodules/references/README.md: sha256:ef5453d5e87fd62568d8c1c55fccfc41f6522316976a8f2cde7aa90c1f857809 + .claude/skills/git-submodules/references/sources.md: sha256:1786dcf90dd21988379a5ee4f243b50a158d87bc059d4903bf54ef268811c5e7 + .claude/skills/git-submodules/references/submodules.md: sha256:ffab8cef061118a83ef480c5e8077c0e1ac2fa7e5e1fae333cfff9bfd362003f + .claude/skills/git-workflow/README.md: sha256:00844e92a521b46273e1c9ee334440c868348c1795ae86eb7fd95fb86dbbfbed + .claude/skills/git-workflow/SKILL.md: sha256:87a3f39fab4db85ce8d2b23d01d1e8b2180bf0a73eb18183eae2d7e28c2cc42b + .claude/skills/git-workflow/references/README.md: sha256:f6aa9351e7d87421f64573a0b1db5b400b0c2c2686097625070e5f1113d72426 + .claude/skills/git-workflow/references/sources.md: sha256:186c6fec31bcefe10bf4409d8ef9b6fdfff6185b431f30c66712d0669df0be55 + .claude/skills/git-worktrees/README.md: sha256:084c936cf0aa2fc808e5fcf8acaec148d93d8ac03a99a6f3d06137adcc14585c + .claude/skills/git-worktrees/SKILL.md: sha256:2ea1122b4f8bc864a85248bde6983a930ad3f91b60286a761cb0de361b25183f + .claude/skills/git-worktrees/references/README.md: sha256:ea2f3249fe0d0af24fa91f4d050f2aead9fc84841333530bce0ddfc7378d56c5 + .claude/skills/git-worktrees/references/sources.md: sha256:5b7fe81f56eb03360a4719f2a988674f43891f7a2bfc14756d68398088767e36 + .claude/skills/git-worktrees/references/worktrees.md: sha256:38999d4d07d85c6142495c3765d56a0b5fdf861487a9347317f6b054a7d3dbc8 + .claude/skills/pc-author/README.md: sha256:2e21bd50002bd2622ebb31793fa4aece14f5233889c3a054fe99f1974f19bb40 + .claude/skills/pc-author/SKILL.md: sha256:577b9bd06615182ce3b0123640d989c3b74ab05bda8103a63a41e1f5a1abb2ec + .claude/skills/pc-author/references/README.md: sha256:c409caf0e08af23f2ec056b649cee0bba22b55c1fbe4b8f1189ce0bbb9b5d5c4 + .claude/skills/pc-author/references/hooks-by-language.md: sha256:2c4ade2ac32e7a9c539d7e08b7d55739e8d87c634860bb58e8764067293bb35d + .claude/skills/pc-author/references/sources.md: sha256:e1cff11955490e4948fec81d8172b0adc5a37829aa0ffe1791c0f6313ed34a4e + .claude/skills/pc-run/README.md: sha256:2e2ebd5220c3251a40afa6c06243ba12fb0aa03dc60fb76db74b22c44914f353 + .claude/skills/pc-run/SKILL.md: sha256:7a0890f7447a04f464ca8a30e5a967ec4421e9ef41298d469f8f2a6ba03be0a2 + .claude/skills/pc-run/references/README.md: sha256:ad42b9013dbd44b8c4515c15317c5e9e6b235bc656ad5a03e037eb4bc6f8782c + .claude/skills/pc-run/references/failure-patterns.md: sha256:59d913c001483c3770d269fdf5e83cf1d7578e0ae879928a1404bf93bb395994 + .claude/skills/pc-run/references/sources.md: sha256:7ca8b7106e7ee5c2c7d1fbd3dc9e0d2a89c18eba79145cbab55bc8e39eef9087 + content_hash: sha256:6d0c8fcec32d8fb8321b539cc567c4241342cfa33e5d0b6f66697d2f8b784fdd + declared_license: MIT +- repo_url: Defame1297/holocron + name: gitea + host: git.dev.rkdr.net + resolved_commit: c3ec5f2d3d00d625f632de3c40d1919e16727988 + version: 1.3.4 + virtual_path: plugins/gitea + is_virtual: true + package_type: marketplace_plugin + deployed_files: + - .claude/agents/gitea-orchestrate.md + - .claude/skills/gitea-branches + - .claude/skills/gitea-branches/README.md + - .claude/skills/gitea-branches/SKILL.md + - .claude/skills/gitea-branches/references/branches.md + - .claude/skills/gitea-branches/references/commits.md + - .claude/skills/gitea-branches/references/sources.md + - .claude/skills/gitea-files + - .claude/skills/gitea-files/README.md + - .claude/skills/gitea-files/SKILL.md + - .claude/skills/gitea-files/references/examples.md + - .claude/skills/gitea-files/references/sources.md + - .claude/skills/gitea-issues + - .claude/skills/gitea-issues/README.md + - .claude/skills/gitea-issues/SKILL.md + - .claude/skills/gitea-issues/references/enrichments.md + - .claude/skills/gitea-issues/references/issues.md + - .claude/skills/gitea-issues/references/search.md + - .claude/skills/gitea-issues/references/sources.md + - .claude/skills/gitea-labels-milestones + - .claude/skills/gitea-labels-milestones/README.md + - .claude/skills/gitea-labels-milestones/SKILL.md + - .claude/skills/gitea-labels-milestones/references/label-inference.md + - .claude/skills/gitea-labels-milestones/references/labels.md + - .claude/skills/gitea-labels-milestones/references/milestones.md + - .claude/skills/gitea-labels-milestones/references/sources.md + - .claude/skills/gitea-prs + - .claude/skills/gitea-prs/README.md + - .claude/skills/gitea-prs/SKILL.md + - .claude/skills/gitea-prs/references/merging.md + - .claude/skills/gitea-prs/references/pull-requests.md + - .claude/skills/gitea-prs/references/reviews.md + - .claude/skills/gitea-prs/references/sources.md + - .claude/skills/gitea-releases + - .claude/skills/gitea-releases/README.md + - .claude/skills/gitea-releases/SKILL.md + - .claude/skills/gitea-releases/references/call-signatures.md + - .claude/skills/gitea-releases/references/conventions.md + - .claude/skills/gitea-releases/references/sources.md + - .claude/skills/gitea-workflow + - .claude/skills/gitea-workflow/README.md + - .claude/skills/gitea-workflow/SKILL.md + - .claude/skills/gitea-workflow/references/sources.md + deployed_file_hashes: + .claude/agents/gitea-orchestrate.md: sha256:c281972b9c738e35528f9478ee76dc326d2f113312dacc7fd87991f0cd8cc825 + .claude/skills/gitea-branches/README.md: sha256:ce7bff9d60a3edd4655af9b4f3bb51520ffc2ff2a90f98f55e29a1b72dc5e3ee + .claude/skills/gitea-branches/SKILL.md: sha256:0d8a1ac2e450d60c123ae1538dc8cca49b270da410d2e031cf250cdded2bf839 + .claude/skills/gitea-branches/references/branches.md: sha256:f045fb181d2fc1c948a2e026c335453273931f3a8d710a45b05826547ffe89e2 + .claude/skills/gitea-branches/references/commits.md: sha256:ca70a1c32ff6abf296a027c27c678f1574eddfdc069d7ed4ae621524dcd9adc0 + .claude/skills/gitea-branches/references/sources.md: sha256:1bfad24252356c6005935ccc10a87871709d4b41e6b2d69abe9ac7ab188f4bcd + .claude/skills/gitea-files/README.md: sha256:f9d721b7ed8eae43b5eab04324dbc96fa91964accc77ea74d1b889475592256f + .claude/skills/gitea-files/SKILL.md: sha256:9362d82068257d0aee113fd55f2acf4d53e8d20f638453946b76384a8c49a9b7 + .claude/skills/gitea-files/references/examples.md: sha256:26209f202dd0ec12c73bb62984f9fe42c843e2819dab89752fdf478444a5d79f + .claude/skills/gitea-files/references/sources.md: sha256:71981b2987c56589e469f2305afbf7d404e605c8db1a6d50c1576d50eb9eea9c + .claude/skills/gitea-issues/README.md: sha256:2f5844cf2fde1a62844eb26ac19f915f50fe8fdc5bfde982be018ed2d0e15c30 + .claude/skills/gitea-issues/SKILL.md: sha256:9084c5f37d23ebcc6de5940ad8fcc1c699aa7d4693623bf029a031b21412e7d6 + .claude/skills/gitea-issues/references/enrichments.md: sha256:024872fe787688f7ff51c856264bd3405208543fcbccd113cce464135dbc9f90 + .claude/skills/gitea-issues/references/issues.md: sha256:5c8103944f316f7681ee2b3d5ebbc55dc73a2f5a25616325c87af03b93944a04 + .claude/skills/gitea-issues/references/search.md: sha256:dec2a98a9d781122d235b8a3fa6cd9efdcc08be0a2b71eb29f90a6e14338c0af + .claude/skills/gitea-issues/references/sources.md: sha256:86d5c500e103d5eea31595ab04fb79d9e908215d83d2f0edec1e0e73995d2e96 + .claude/skills/gitea-labels-milestones/README.md: sha256:185dd654d9d775fe0b0464a0dbdf724b06f6375fa6d7c7e200ee826fdcc9b4b5 + .claude/skills/gitea-labels-milestones/SKILL.md: sha256:ce07c4c8827747b13b3d9ea9d20f8318ace0803f2bd42804e717ed9201c54254 + .claude/skills/gitea-labels-milestones/references/label-inference.md: sha256:da141129c74d22a441303b910b68d5bb758435361efbd8d2669f46920d699361 + .claude/skills/gitea-labels-milestones/references/labels.md: sha256:be8ff5ce4dbfb31dd2bdcc425a769dca94887c5c8acd47c78e1e21ccfc24dcba + .claude/skills/gitea-labels-milestones/references/milestones.md: sha256:ff1f1b0c8ecb6967c940d1ca8bebf2a2c7995414936f8ac7f5ff23d1dc01ac3f + .claude/skills/gitea-labels-milestones/references/sources.md: sha256:a1a1d3381dfb0dc8f331be0b3ab49f28e94b992fb4809ca09be91ce33047dd1e + .claude/skills/gitea-prs/README.md: sha256:0346388f642641c56cef82ba1ee576a187d048eee66fab839a17417c81122cbc + .claude/skills/gitea-prs/SKILL.md: sha256:bf70832f3d97064c9816d680390da84c256a971d7804317e1814b4ee6a622b4d + .claude/skills/gitea-prs/references/merging.md: sha256:5f8b55b1a729f122ee265fb5da69369fc27434a13dd02fc112801f945d2c9d19 + .claude/skills/gitea-prs/references/pull-requests.md: sha256:631874a7cddfcf678a2beffa5578fa7848bcef36be1251fa13fb0ccc543bcaf7 + .claude/skills/gitea-prs/references/reviews.md: sha256:1089cf45a75a562bbb29de6538848c4f4bd627d5a7e36bf9b2ab6496f45f962b + .claude/skills/gitea-prs/references/sources.md: sha256:ac48a3018443b56acb5c3d55d6c04e62103f5bac71b8185e2bd085fb7e165431 + .claude/skills/gitea-releases/README.md: sha256:102625066c34c7b5a16e25b7506a87f876e9b123b334f5ffba73ab1523a57ed7 + .claude/skills/gitea-releases/SKILL.md: sha256:793a6936c0d56acb4b4aba1d72e1fc91690273df0aafe447e54cb53df00319c1 + .claude/skills/gitea-releases/references/call-signatures.md: sha256:a523dba171d67a7e7899b7d8ecfe9220f250da2c184b0f2806e5d22647b1ffb0 + .claude/skills/gitea-releases/references/conventions.md: sha256:1c8102ed489422706babea4e43e12dc7684b7dacd210fa29b3793bffa9cd9f64 + .claude/skills/gitea-releases/references/sources.md: sha256:37a14495c461120a5bab0013531c9e83e7d9b670e78129a9439a81b48a64cce4 + .claude/skills/gitea-workflow/README.md: sha256:338f79d0b3c25741848fce841a5094ef27cbc7e4a6228404557b2d9594df68fa + .claude/skills/gitea-workflow/SKILL.md: sha256:a8266ceb335c56eb68cc09b3f81690c52c4dada9f25b48c3a46ac46b0ad3b761 + .claude/skills/gitea-workflow/references/sources.md: sha256:171d3a5a36cb9d637302516501a1e9fc7a4bd058968dc9244412c335aa2de68b + content_hash: sha256:8149467df78678963dbb140aa254f606a53491f3cb7e5d97a1211be426e7892e + declared_license: MIT +- repo_url: Defame1297/holocron + name: kyberforge + host: git.dev.rkdr.net + resolved_commit: c3ec5f2d3d00d625f632de3c40d1919e16727988 + version: 1.4.1 + virtual_path: plugins/kyberforge + is_virtual: true + package_type: marketplace_plugin + deployed_files: + - .claude/agents/apm-orchestrate.md + - .claude/skills/agent-audit + - .claude/skills/agent-audit/README.md + - .claude/skills/agent-audit/SKILL.md + - .claude/skills/agent-audit/assets/vale/.vale.ini + - .claude/skills/agent-audit/assets/vale/styles/Kyberforge/DescriptionOpener.yml + - .claude/skills/agent-audit/assets/vale/styles/Kyberforge/PaddingPhrase.yml + - .claude/skills/agent-audit/assets/vale/styles/Kyberforge/SentenceOpenerThereIs.yml + - .claude/skills/agent-audit/assets/vale/styles/Kyberforge/VagueWording.yml + - .claude/skills/agent-audit/assets/vale/styles/KyberforgeCopilot/ProactivePhrase.yml + - .claude/skills/agent-audit/references/README.md + - .claude/skills/agent-audit/references/description-quality.md + - .claude/skills/agent-audit/references/field-inventory.md + - .claude/skills/agent-audit/references/sources.md + - .claude/skills/agent-audit/scripts/README.md + - .claude/skills/agent-audit/scripts/vale-wrap.sh + - .claude/skills/agent-audit/scripts/validate-provenance.sh + - .claude/skills/agent-audit/scripts/validate.sh + - .claude/skills/agent-author + - .claude/skills/agent-author/README.md + - .claude/skills/agent-author/SKILL.md + - .claude/skills/agent-author/assets/README.md + - .claude/skills/agent-author/assets/templates/apm-agent.md + - .claude/skills/agent-author/assets/templates/claude-code.md + - .claude/skills/agent-author/assets/templates/copilot.agent.md.template + - .claude/skills/agent-author/references/README.md + - .claude/skills/agent-author/references/deployment-modes.md + - .claude/skills/agent-author/references/scripts.md + - .claude/skills/agent-author/references/sources.md + - .claude/skills/agent-author/scripts/README.md + - .claude/skills/agent-author/scripts/new-agent.sh + - .claude/skills/apm-install + - .claude/skills/apm-install/README.md + - .claude/skills/apm-install/SKILL.md + - .claude/skills/apm-install/references/sources.md + - .claude/skills/apm-workflow + - .claude/skills/apm-workflow/README.md + - .claude/skills/apm-workflow/SKILL.md + - .claude/skills/apm-workflow/references/audit.md + - .claude/skills/apm-workflow/references/compile.md + - .claude/skills/apm-workflow/references/configure.md + - .claude/skills/apm-workflow/references/install.md + - .claude/skills/apm-workflow/references/marketplace.md + - .claude/skills/apm-workflow/references/sources.md + - .claude/skills/forge + - .claude/skills/forge/README.md + - .claude/skills/forge/SKILL.md + - .claude/skills/forge/references/sources.md + - .claude/skills/skill-audit + - .claude/skills/skill-audit/README.md + - .claude/skills/skill-audit/SKILL.md + - .claude/skills/skill-audit/assets/vale/.vale.ini + - .claude/skills/skill-audit/assets/vale/styles/Kyberforge/DescriptionOpener.yml + - .claude/skills/skill-audit/assets/vale/styles/Kyberforge/PaddingPhrase.yml + - .claude/skills/skill-audit/assets/vale/styles/Kyberforge/SentenceOpenerThereIs.yml + - .claude/skills/skill-audit/assets/vale/styles/Kyberforge/VagueWording.yml + - .claude/skills/skill-audit/references/body-discipline.md + - .claude/skills/skill-audit/references/description-quality.md + - .claude/skills/skill-audit/references/sources.md + - .claude/skills/skill-audit/scripts/vale-wrap.sh + - .claude/skills/skill-audit/scripts/validate-provenance.sh + - .claude/skills/skill-audit/scripts/validate.sh + - .claude/skills/skill-author + - .claude/skills/skill-author/README.md + - .claude/skills/skill-author/SKILL.md + - .claude/skills/skill-author/assets/templates/README.md + - .claude/skills/skill-author/assets/templates/SKILL.md + - .claude/skills/skill-author/assets/templates/assets/README.md + - .claude/skills/skill-author/assets/templates/references/README.md + - .claude/skills/skill-author/assets/templates/references/sources.md + - .claude/skills/skill-author/assets/templates/scripts/README.md + - .claude/skills/skill-author/assets/templates/tests/README.md + - .claude/skills/skill-author/references/deployment-modes.md + - .claude/skills/skill-author/references/scripts.md + - .claude/skills/skill-author/references/sources.md + - .claude/skills/skill-author/scripts/new-skill.sh + deployed_file_hashes: + .claude/agents/apm-orchestrate.md: sha256:fbb78f7c8c58b018639e7a39f2f1b3ce2adcd6bed277b3c8f8cd70893698ec73 + .claude/skills/agent-audit/README.md: sha256:a3a63acb80981975330bd9a86d81c8f3e632f7f2d97ac99951710d035fb52953 + .claude/skills/agent-audit/SKILL.md: sha256:080f391ea25de8afc5a20478cbd382a8d28fcf70a7be7f012033dacb6e9bfed3 + .claude/skills/agent-audit/assets/vale/.vale.ini: sha256:d643677585c603edcc8816b15d9c85c81b677ded02247b521ba8b88ae5bfdf57 + .claude/skills/agent-audit/assets/vale/styles/Kyberforge/DescriptionOpener.yml: sha256:a399516a457d40e0af5536a6575c70221909cecac5722eda2788430ebddd8f67 + .claude/skills/agent-audit/assets/vale/styles/Kyberforge/PaddingPhrase.yml: sha256:67b738f5c393a717bdf249fcd946eb7b0d258215bf2240d2be6902711b46d8e9 + .claude/skills/agent-audit/assets/vale/styles/Kyberforge/SentenceOpenerThereIs.yml: sha256:5dade0238d96730d24bb6e8dba003c3d94694bd9969687f461fe1fada3ca4629 + .claude/skills/agent-audit/assets/vale/styles/Kyberforge/VagueWording.yml: sha256:d4bf14d0bb2dcbe3a89b66b72a9c9c19d175060b5c4c978da3d67010f1a25083 + .claude/skills/agent-audit/assets/vale/styles/KyberforgeCopilot/ProactivePhrase.yml: sha256:ba4f91479f66f9f03c74f791cbc6130b24bbc31a25f3897b424f72d02473b9b4 + .claude/skills/agent-audit/references/README.md: sha256:7664ae08efc8373c0b253c0a5c7a9cc930720c62abb8d31a81b2aedbd573dd30 + .claude/skills/agent-audit/references/description-quality.md: sha256:79c62fd641d6785a3349d8ff1e207c64547ef9435d51e88518fc9fb5c2f4962e + .claude/skills/agent-audit/references/field-inventory.md: sha256:e9abd738c08890994441d215eba96f894e00992cb7bb5ce850796186050aa1ae + .claude/skills/agent-audit/references/sources.md: sha256:9e06addbfb1a58e4db23eaabe9fb13afe0fffa8419afa575525136cec189c558 + .claude/skills/agent-audit/scripts/README.md: sha256:ee427eed6a562a6e86898c69c35508c518d5b3c633ae803e587fbab37514f2b2 + .claude/skills/agent-audit/scripts/vale-wrap.sh: sha256:c17673b3b0de1c1a49fcb73e2b695835a45014ec1c300a77a3ba7b3c7ce4c995 + .claude/skills/agent-audit/scripts/validate-provenance.sh: sha256:0e1d2b3bb1abb9f302ed84a618246ebaa7d5f7fda727b290d323e70dcc26a323 + .claude/skills/agent-audit/scripts/validate.sh: sha256:29727875f419b8d3b6c76f157b7569adb4d2b9a88f01eeb2c09e04dfb8541cb3 + .claude/skills/agent-author/README.md: sha256:015a6b030ff2aa913f982a223f8afad789edadc4e7148d3b1740a56b346c658d + .claude/skills/agent-author/SKILL.md: sha256:39193473a9b1beacb4790cab4e5e31e01f7021a68a5c4cc16a606420bee65d6a + .claude/skills/agent-author/assets/README.md: sha256:a82665201ead91b7fa2b37629327ecb89ca785b7f2b261300c2dd816b4219bcd + .claude/skills/agent-author/assets/templates/apm-agent.md: sha256:c2693395d7ec72645fc1b79c65039ddadc0077f5856de3277d600bde41f17e54 + .claude/skills/agent-author/assets/templates/claude-code.md: sha256:eef5ee46d93b6896e880b3c4aec1121c4077a2556c6d9f4a01b322afc7b63a7e + .claude/skills/agent-author/assets/templates/copilot.agent.md.template: sha256:1e0c0445a8908148e69baeeb44316fb370aa483e17fcd63d7f1b2d3ad1927788 + .claude/skills/agent-author/references/README.md: sha256:bb4cf7a34c512da33f574b208756941f396f4a1e55daaed0fad22569a22af301 + .claude/skills/agent-author/references/deployment-modes.md: sha256:6cd010455e10574566ebf7cfd0926217ae5812d56a62daf4bcdb325abf3b0421 + .claude/skills/agent-author/references/scripts.md: sha256:290f5d8ab0a4f2073e14f7139fd55ab40310137f08ca7a2d59ed35a53488a592 + .claude/skills/agent-author/references/sources.md: sha256:56965f9b660fc9a32dd71ca1039b4c6b8f932420628ddfbf3a2525e7c3336ce4 + .claude/skills/agent-author/scripts/README.md: sha256:e8ea2f3391ca297afeda03911768000617a71df0cf9d673bbb260ee5bbfed7c2 + .claude/skills/agent-author/scripts/new-agent.sh: sha256:e917055957500a72c7f684810552ba8931781c90f96dedaabc07e899dae02a89 + .claude/skills/apm-install/README.md: sha256:d7bd79ee27997fffb5765c03358a700d8c00266ea120f6d067d74300d6c26eb0 + .claude/skills/apm-install/SKILL.md: sha256:4c0db5454bcbf895a89e7405eeefb4521a2b23f2ff28aa96c82c59708d8819ee + .claude/skills/apm-install/references/sources.md: sha256:b80245ff8ae179d458572cb9b03621d00bf7e2f052f0cd71b88112c0ea8a91ce + .claude/skills/apm-workflow/README.md: sha256:40e85d2c2a7487b54c7797fb135b979b8df14b524928da8c7502723a0fc8d36a + .claude/skills/apm-workflow/SKILL.md: sha256:60c2c3351b74cc8b3cb468e1cb353815214e7913ab118c30c9172f4c7f264844 + .claude/skills/apm-workflow/references/audit.md: sha256:a336da288330a9f9d09b79fabeefbfc70f7ca81e9d86ef335c70a4eba3186141 + .claude/skills/apm-workflow/references/compile.md: sha256:f0b7b99e56937290ed6c1db52e542283670e369a2cc55f8a3c5dbc48d4252f7a + .claude/skills/apm-workflow/references/configure.md: sha256:c92d7c23c6bcb4ce0b56cce43fe399c085d698fcf58243f416bc84f0e058d7a0 + .claude/skills/apm-workflow/references/install.md: sha256:1615e31460f8cc820fca162175b8689ade1324604aa1c8f204cbc8924d74b5ee + .claude/skills/apm-workflow/references/marketplace.md: sha256:0f2fdd21d8080f3b34ddee8abd52431903aeb67878842de99198a868dc43ef67 + .claude/skills/apm-workflow/references/sources.md: sha256:4ae44a9e82e8ec3bd4b1a61f32adbd3f3ff5fab21db3b4162765b2c0553e49fd + .claude/skills/forge/README.md: sha256:2151b952f4438cd9212edd97de90eec06f027f01121b3badb9d32dc9c45320c4 + .claude/skills/forge/SKILL.md: sha256:4de4c629cb4aec4c6cac5347163a86fe123299f131510dc43857597ded6ae497 + .claude/skills/forge/references/sources.md: sha256:a7f614c0ab37aa403545e191722bca012836162dcab30ff8ac984cbb25ad68cd + .claude/skills/skill-audit/README.md: sha256:9e3533c3c44cfd249cf02684ef43db822ed41382362e8ce71f1254d10711c8e0 + .claude/skills/skill-audit/SKILL.md: sha256:dbe2adfd0101cf9d6b3c2c3c2aa3ae397c717f835c1059c0f1d805e9057dc397 + .claude/skills/skill-audit/assets/vale/.vale.ini: sha256:0d1108b17a941b514dd9a62a7382d1febdd543118750246c81fba406440a9f9f + .claude/skills/skill-audit/assets/vale/styles/Kyberforge/DescriptionOpener.yml: sha256:a399516a457d40e0af5536a6575c70221909cecac5722eda2788430ebddd8f67 + .claude/skills/skill-audit/assets/vale/styles/Kyberforge/PaddingPhrase.yml: sha256:67b738f5c393a717bdf249fcd946eb7b0d258215bf2240d2be6902711b46d8e9 + .claude/skills/skill-audit/assets/vale/styles/Kyberforge/SentenceOpenerThereIs.yml: sha256:5dade0238d96730d24bb6e8dba003c3d94694bd9969687f461fe1fada3ca4629 + .claude/skills/skill-audit/assets/vale/styles/Kyberforge/VagueWording.yml: sha256:d4bf14d0bb2dcbe3a89b66b72a9c9c19d175060b5c4c978da3d67010f1a25083 + .claude/skills/skill-audit/references/body-discipline.md: sha256:2295d2fc72fa529f3700c21fa93516ad186a7cc66d470370173ab1a784d03d31 + .claude/skills/skill-audit/references/description-quality.md: sha256:ab077e71bbc6e9d8e2a1384bedaf613a4219f28bbe42eb9e89f933b579db6b33 + .claude/skills/skill-audit/references/sources.md: sha256:cb65dd3cc411d7b264fab24de5cd2353dec8fbb7bbcfe2388535d963be240280 + .claude/skills/skill-audit/scripts/vale-wrap.sh: sha256:c17673b3b0de1c1a49fcb73e2b695835a45014ec1c300a77a3ba7b3c7ce4c995 + .claude/skills/skill-audit/scripts/validate-provenance.sh: sha256:015376c5ad1f6bb6b53549537317a55e3ac06cd4e347451dba85879a3928ccbe + .claude/skills/skill-audit/scripts/validate.sh: sha256:ebbef859276135b7fe4189b31396fd98e5d4019685d7cb55e39a10fac8ca546d + .claude/skills/skill-author/README.md: sha256:6c0b36f9b28a33de1646df45bd8e4d733ab3b26c0bdff49743962aa068085f6d + .claude/skills/skill-author/SKILL.md: sha256:7b82d16fe0d61a71c5b59a244cf92b9b5fc2635916ad1572f4f5f6e37172bf09 + .claude/skills/skill-author/assets/templates/README.md: sha256:f7e91356f4c85862f96a9f46a7a7ae8b5dc715c8174add5cf070166a8bae0658 + .claude/skills/skill-author/assets/templates/SKILL.md: sha256:f05a127963874bfbbe4333c42c5208736d84d5bf12dd8478351f6853304cc9d1 + .claude/skills/skill-author/assets/templates/assets/README.md: sha256:b72e51e643c45ded210a257e475d5661035d2aa5ed581e3294fd2aed00765c4b + .claude/skills/skill-author/assets/templates/references/README.md: sha256:63754636f84a7841f31ab1f2854025ee449a29840ed6b9b51bcb1422ab5522d0 + .claude/skills/skill-author/assets/templates/references/sources.md: sha256:fe126720ba890b98e829f2626e3f5a361a0c2ce47eda97bd3f7dc1812ddd62b7 + .claude/skills/skill-author/assets/templates/scripts/README.md: sha256:ee427eed6a562a6e86898c69c35508c518d5b3c633ae803e587fbab37514f2b2 + .claude/skills/skill-author/assets/templates/tests/README.md: sha256:5d331de121105ca79b544b9d3779a316a64cee46fa8d51ba0ea789ab5c233442 + .claude/skills/skill-author/references/deployment-modes.md: sha256:ffe2d928f2b5ec90c509f4d4cc5bd33c71899852cb5cc5a31fe1513b707fb759 + .claude/skills/skill-author/references/scripts.md: sha256:fe71da1fb3d947846ad1a37348f90866b0c4f7dbd81331cfe3300981656f6cc2 + .claude/skills/skill-author/references/sources.md: sha256:27637acad4c0cdc7f1db15bc9cab339ebdac643e084642c58945ac2f81020c75 + .claude/skills/skill-author/scripts/new-skill.sh: sha256:46c6903404b9eabbd6c83892213fdabf9cad193be64613b8c1b84c8dc37e8eb2 + content_hash: sha256:686b272f205a67d2e817e8458bd7ab442e64c29bab7b9123541a39873d3c8e55 + declared_license: MIT +- repo_url: Defame1297/holocron + name: lint + host: git.dev.rkdr.net + resolved_commit: c3ec5f2d3d00d625f632de3c40d1919e16727988 + version: 1.1.6 + virtual_path: plugins/lint + is_virtual: true + package_type: marketplace_plugin + deployed_files: + - .claude/agents/lint-runner.md + - .claude/skills/vale-config + - .claude/skills/vale-config/README.md + - .claude/skills/vale-config/SKILL.md + - .claude/skills/vale-config/references/configuration-reference.md + - .claude/skills/vale-config/references/sources.md + - .claude/skills/vale-run + - .claude/skills/vale-run/README.md + - .claude/skills/vale-run/SKILL.md + - .claude/skills/vale-run/references/sources.md + - .claude/skills/vale-run/references/troubleshooting.md + deployed_file_hashes: + .claude/agents/lint-runner.md: sha256:efeee4fb01c27b9177239f059851cf1b736ca50a12793b94d5d65d30ab98bcd4 + .claude/skills/vale-config/README.md: sha256:faf6bca23cea74be7ecf47a31b0499af2f6ce791bbc82eb67069e1e67eeed07c + .claude/skills/vale-config/SKILL.md: sha256:1674f1256b654779268c42f764fa0c4f09011ce8ea62eb0e01fd8f118495519b + .claude/skills/vale-config/references/configuration-reference.md: sha256:1c2dbee6315178b00e624c002a6ae8d9d20f03f2c7ff1c3fba4eebe38fb39a7f + .claude/skills/vale-config/references/sources.md: sha256:2efa451c3cc6bdc1103cfe56712a3ea35acef9a76fe4c6f71e8cacabd92dc860 + .claude/skills/vale-run/README.md: sha256:1a850c89cf7de288dc72b96cae6fec24a3ccdc237f0ce785f59cca56c7762198 + .claude/skills/vale-run/SKILL.md: sha256:16c42c97de14ef20296b9dfce46b0e2f9be6ad68751c35975251cecff80f79d9 + .claude/skills/vale-run/references/sources.md: sha256:1f46b727e5f3db09d6c8c01c8a615bad60ce2c5b3a01226e1a3fd3b933330bc7 + .claude/skills/vale-run/references/troubleshooting.md: sha256:b4c2bc67b413b102d9fe1cd7e5a248b19aba3dc3b60b4429e777977cf8021cd5 + content_hash: sha256:7bc57b8852680bdcba32a6a6108b99ef6bcdf26eacfecd806c421ec5d1fd47d9 + declared_license: MIT +deployments: +- kind: project-relative + target: claude + value: .claude/agents/apm-orchestrate.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:fbb78f7c8c58b018639e7a39f2f1b3ce2adcd6bed277b3c8f8cd70893698ec73 +- kind: project-relative + target: claude + value: .claude/agents/git-orchestrate.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:95a48c8e62cbb5d8f5bb2aa80c253d2f8b3a3988cf6202c10ae4feae1266d64d +- kind: project-relative + target: claude + value: .claude/agents/gitea-orchestrate.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:c281972b9c738e35528f9478ee76dc326d2f113312dacc7fd87991f0cd8cc825 +- kind: project-relative + target: claude + value: .claude/agents/lint-runner.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/lint + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/lint + content_hash: sha256:efeee4fb01c27b9177239f059851cf1b736ca50a12793b94d5d65d30ab98bcd4 +- kind: project-relative + target: claude + value: .claude/skills/agent-audit + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/agent-audit/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:a3a63acb80981975330bd9a86d81c8f3e632f7f2d97ac99951710d035fb52953 +- kind: project-relative + target: claude + value: .claude/skills/agent-audit/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:080f391ea25de8afc5a20478cbd382a8d28fcf70a7be7f012033dacb6e9bfed3 +- kind: project-relative + target: claude + value: .claude/skills/agent-audit/assets/vale/.vale.ini + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:d643677585c603edcc8816b15d9c85c81b677ded02247b521ba8b88ae5bfdf57 +- kind: project-relative + target: claude + value: .claude/skills/agent-audit/assets/vale/styles/Kyberforge/DescriptionOpener.yml + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:a399516a457d40e0af5536a6575c70221909cecac5722eda2788430ebddd8f67 +- kind: project-relative + target: claude + value: .claude/skills/agent-audit/assets/vale/styles/Kyberforge/PaddingPhrase.yml + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:67b738f5c393a717bdf249fcd946eb7b0d258215bf2240d2be6902711b46d8e9 +- kind: project-relative + target: claude + value: .claude/skills/agent-audit/assets/vale/styles/Kyberforge/SentenceOpenerThereIs.yml + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:5dade0238d96730d24bb6e8dba003c3d94694bd9969687f461fe1fada3ca4629 +- kind: project-relative + target: claude + value: .claude/skills/agent-audit/assets/vale/styles/Kyberforge/VagueWording.yml + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:d4bf14d0bb2dcbe3a89b66b72a9c9c19d175060b5c4c978da3d67010f1a25083 +- kind: project-relative + target: claude + value: .claude/skills/agent-audit/assets/vale/styles/KyberforgeCopilot/ProactivePhrase.yml + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:ba4f91479f66f9f03c74f791cbc6130b24bbc31a25f3897b424f72d02473b9b4 +- kind: project-relative + target: claude + value: .claude/skills/agent-audit/references/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:7664ae08efc8373c0b253c0a5c7a9cc930720c62abb8d31a81b2aedbd573dd30 +- kind: project-relative + target: claude + value: .claude/skills/agent-audit/references/description-quality.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:79c62fd641d6785a3349d8ff1e207c64547ef9435d51e88518fc9fb5c2f4962e +- kind: project-relative + target: claude + value: .claude/skills/agent-audit/references/field-inventory.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:e9abd738c08890994441d215eba96f894e00992cb7bb5ce850796186050aa1ae +- kind: project-relative + target: claude + value: .claude/skills/agent-audit/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:9e06addbfb1a58e4db23eaabe9fb13afe0fffa8419afa575525136cec189c558 +- kind: project-relative + target: claude + value: .claude/skills/agent-audit/scripts/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:ee427eed6a562a6e86898c69c35508c518d5b3c633ae803e587fbab37514f2b2 +- kind: project-relative + target: claude + value: .claude/skills/agent-audit/scripts/vale-wrap.sh + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:c17673b3b0de1c1a49fcb73e2b695835a45014ec1c300a77a3ba7b3c7ce4c995 +- kind: project-relative + target: claude + value: .claude/skills/agent-audit/scripts/validate-provenance.sh + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:0e1d2b3bb1abb9f302ed84a618246ebaa7d5f7fda727b290d323e70dcc26a323 +- kind: project-relative + target: claude + value: .claude/skills/agent-audit/scripts/validate.sh + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:29727875f419b8d3b6c76f157b7569adb4d2b9a88f01eeb2c09e04dfb8541cb3 +- kind: project-relative + target: claude + value: .claude/skills/agent-author + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/agent-author/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:015a6b030ff2aa913f982a223f8afad789edadc4e7148d3b1740a56b346c658d +- kind: project-relative + target: claude + value: .claude/skills/agent-author/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:39193473a9b1beacb4790cab4e5e31e01f7021a68a5c4cc16a606420bee65d6a +- kind: project-relative + target: claude + value: .claude/skills/agent-author/assets/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:a82665201ead91b7fa2b37629327ecb89ca785b7f2b261300c2dd816b4219bcd +- kind: project-relative + target: claude + value: .claude/skills/agent-author/assets/templates/apm-agent.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:c2693395d7ec72645fc1b79c65039ddadc0077f5856de3277d600bde41f17e54 +- kind: project-relative + target: claude + value: .claude/skills/agent-author/assets/templates/claude-code.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:eef5ee46d93b6896e880b3c4aec1121c4077a2556c6d9f4a01b322afc7b63a7e +- kind: project-relative + target: claude + value: .claude/skills/agent-author/assets/templates/copilot.agent.md.template + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:1e0c0445a8908148e69baeeb44316fb370aa483e17fcd63d7f1b2d3ad1927788 +- kind: project-relative + target: claude + value: .claude/skills/agent-author/references/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:bb4cf7a34c512da33f574b208756941f396f4a1e55daaed0fad22569a22af301 +- kind: project-relative + target: claude + value: .claude/skills/agent-author/references/deployment-modes.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:6cd010455e10574566ebf7cfd0926217ae5812d56a62daf4bcdb325abf3b0421 +- kind: project-relative + target: claude + value: .claude/skills/agent-author/references/scripts.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:290f5d8ab0a4f2073e14f7139fd55ab40310137f08ca7a2d59ed35a53488a592 +- kind: project-relative + target: claude + value: .claude/skills/agent-author/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:56965f9b660fc9a32dd71ca1039b4c6b8f932420628ddfbf3a2525e7c3336ce4 +- kind: project-relative + target: claude + value: .claude/skills/agent-author/scripts/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:e8ea2f3391ca297afeda03911768000617a71df0cf9d673bbb260ee5bbfed7c2 +- kind: project-relative + target: claude + value: .claude/skills/agent-author/scripts/new-agent.sh + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:e917055957500a72c7f684810552ba8931781c90f96dedaabc07e899dae02a89 +- kind: project-relative + target: claude + value: .claude/skills/agentsmd-audit + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/core + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/core + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/agentsmd-audit/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/core + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/core + content_hash: sha256:5d487aadb0424eb162f19454488e3b923180163cf1a543bb977b6969ae7a6849 +- kind: project-relative + target: claude + value: .claude/skills/agentsmd-audit/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/core + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/core + content_hash: sha256:f4e1d3f1fa79ebd4c2c874dfb2fe1b86834c9ae708ab3f0a3fa07be39e90b552 +- kind: project-relative + target: claude + value: .claude/skills/agentsmd-audit/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/core + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/core + content_hash: sha256:ce13d263414c4c8ea96a33a9d3e0d5535c3100fb1e54a26d2bfe218fc7cd5974 +- kind: project-relative + target: claude + value: .claude/skills/agentsmd-audit/scripts/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/core + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/core + content_hash: sha256:40a7e90e0823a6caaa0276e8d13a3c1bdc7afc533912166aad3454b979824d6a +- kind: project-relative + target: claude + value: .claude/skills/agentsmd-audit/scripts/validate-drift.sh + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/core + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/core + content_hash: sha256:941e521498fa77b483529a73afc13d1d1630876d9035bd3bd020ac3bc3e5a18a +- kind: project-relative + target: claude + value: .claude/skills/agentsmd-audit/scripts/validate-secrets.sh + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/core + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/core + content_hash: sha256:2b53d9d7d0891da4d83ddfec759dcdd7429468360dbc238c0b7e8af816054241 +- kind: project-relative + target: claude + value: .claude/skills/agentsmd-audit/scripts/validate-structure.sh + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/core + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/core + content_hash: sha256:21507848d60b341165880294feb3a2f80dec55b1814b24e94647988da1fdfdd7 +- kind: project-relative + target: claude + value: .claude/skills/agentsmd-author + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/core + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/core + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/agentsmd-author/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/core + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/core + content_hash: sha256:096adbfc44e87da5dd8a8ad0e5eebc0cf4b9d515ab7d7fde4ec80ff3515bfbc7 +- kind: project-relative + target: claude + value: .claude/skills/agentsmd-author/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/core + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/core + content_hash: sha256:c32b9fe1cc7966d21d1ec504396d492afa53eb6369bd3b2678aed4ba92f331f8 +- kind: project-relative + target: claude + value: .claude/skills/agentsmd-author/references/content-guide.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/core + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/core + content_hash: sha256:1b10c956254c949492c2993991c5fd86417db8b64aefbc45695087bb9c021e36 +- kind: project-relative + target: claude + value: .claude/skills/agentsmd-author/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/core + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/core + content_hash: sha256:aecbe65ba9526f694534989cbb516c65a55f75553195753566abccd6841359fe +- kind: project-relative + target: claude + value: .claude/skills/apm-install + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/apm-install/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:d7bd79ee27997fffb5765c03358a700d8c00266ea120f6d067d74300d6c26eb0 +- kind: project-relative + target: claude + value: .claude/skills/apm-install/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:4c0db5454bcbf895a89e7405eeefb4521a2b23f2ff28aa96c82c59708d8819ee +- kind: project-relative + target: claude + value: .claude/skills/apm-install/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:b80245ff8ae179d458572cb9b03621d00bf7e2f052f0cd71b88112c0ea8a91ce +- kind: project-relative + target: claude + value: .claude/skills/apm-workflow + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/apm-workflow/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:40e85d2c2a7487b54c7797fb135b979b8df14b524928da8c7502723a0fc8d36a +- kind: project-relative + target: claude + value: .claude/skills/apm-workflow/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:60c2c3351b74cc8b3cb468e1cb353815214e7913ab118c30c9172f4c7f264844 +- kind: project-relative + target: claude + value: .claude/skills/apm-workflow/references/audit.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:a336da288330a9f9d09b79fabeefbfc70f7ca81e9d86ef335c70a4eba3186141 +- kind: project-relative + target: claude + value: .claude/skills/apm-workflow/references/compile.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:f0b7b99e56937290ed6c1db52e542283670e369a2cc55f8a3c5dbc48d4252f7a +- kind: project-relative + target: claude + value: .claude/skills/apm-workflow/references/configure.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:c92d7c23c6bcb4ce0b56cce43fe399c085d698fcf58243f416bc84f0e058d7a0 +- kind: project-relative + target: claude + value: .claude/skills/apm-workflow/references/install.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:1615e31460f8cc820fca162175b8689ade1324604aa1c8f204cbc8924d74b5ee +- kind: project-relative + target: claude + value: .claude/skills/apm-workflow/references/marketplace.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:0f2fdd21d8080f3b34ddee8abd52431903aeb67878842de99198a868dc43ef67 +- kind: project-relative + target: claude + value: .claude/skills/apm-workflow/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:4ae44a9e82e8ec3bd4b1a61f32adbd3f3ff5fab21db3b4162765b2c0553e49fd +- kind: project-relative + target: claude + value: .claude/skills/caveman + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/caveman/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:b4bb6fe3bf3da222993ca0aee5e7c6f5368b182dd30ce1172e8f8e3280eb7052 +- kind: project-relative + target: claude + value: .claude/skills/diagnose + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/diagnose/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:28886402bbfa0470248086eab9106a103b964b76ae9496e63ff0c8a6761b6d13 +- kind: project-relative + target: claude + value: .claude/skills/diagnose/scripts/hitl-loop.template.sh + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:b2932630950e5210075bcd6f850e5accf30c101c5367b29eac3a29b4dd8084c8 +- kind: project-relative + target: claude + value: .claude/skills/forge + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/forge/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:2151b952f4438cd9212edd97de90eec06f027f01121b3badb9d32dc9c45320c4 +- kind: project-relative + target: claude + value: .claude/skills/forge/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:4de4c629cb4aec4c6cac5347163a86fe123299f131510dc43857597ded6ae497 +- kind: project-relative + target: claude + value: .claude/skills/forge/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:a7f614c0ab37aa403545e191722bca012836162dcab30ff8ac984cbb25ad68cd +- kind: project-relative + target: claude + value: .claude/skills/git-branches + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/git-branches/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:1d59fc21bcaad298a81e5b1c13cf2a396b13ad59a09eeb63f95165f9216755d3 +- kind: project-relative + target: claude + value: .claude/skills/git-branches/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:29d19cee09cd3be3d26db86a228aca9d41833c53945e53e5b26e4339f5c35f0c +- kind: project-relative + target: claude + value: .claude/skills/git-branches/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:ee1c982caf67183acc51226bb5078b3409bb6965b720568f10d67e5f976ccb72 +- kind: project-relative + target: claude + value: .claude/skills/git-commits + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/git-commits/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:c686fd6dd54c608889168cfeaf62e659d66e681194a95aaf5f84859513fda800 +- kind: project-relative + target: claude + value: .claude/skills/git-commits/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:99826526958edffc2a897cc9622e3b2cf11c1bd9995c4a338a23758e62aad9de +- kind: project-relative + target: claude + value: .claude/skills/git-commits/references/commit-template.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:456d80208901106270687906c90bc35eb64595800c05b7b9340fe96cc1cf6a57 +- kind: project-relative + target: claude + value: .claude/skills/git-commits/references/conventional-commits-spec.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:b131b5a2edb9a5acd6bdcb72107099a26bbc172b9a5eb845da4daa2176e962f9 +- kind: project-relative + target: claude + value: .claude/skills/git-commits/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:7ef0293a96884c8935e3384cfb1c3c2c274d0aa0771ba8ac275e68f568d433f5 +- kind: project-relative + target: claude + value: .claude/skills/git-history + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/git-history/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:b285b5b8d771dd2f3482db8b2bd79835ebb6273bffbd8d01bca17d683baac6fa +- kind: project-relative + target: claude + value: .claude/skills/git-history/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:d44ca7f6026624da21f9d31f1bb7a03524d83f30f2e83b54b0f72e9dc1524dfc +- kind: project-relative + target: claude + value: .claude/skills/git-history/references/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:96ef45f979c692acc9a8f745570d506efe8ed8a57015de02dc699bd77018c463 +- kind: project-relative + target: claude + value: .claude/skills/git-history/references/git-log-format.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:e3a0711d7d2a7053bbd1bcd70832c65dd3348266c0fb330fd80a0b8f7c8f858b +- kind: project-relative + target: claude + value: .claude/skills/git-history/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:4a9c9c2ff80193ad4c751ab4baf4c9c42db038a7ea5156733e3253c335912f5b +- kind: project-relative + target: claude + value: .claude/skills/git-remotes + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/git-remotes/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:7f51c47e1671de9f0a63a8836b16caa68727105b62a4240716e457ed8d401f97 +- kind: project-relative + target: claude + value: .claude/skills/git-remotes/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:801a81a26117ee38130d9171f9b16c6ae7a281ec3aa3535fee30578eb29c7908 +- kind: project-relative + target: claude + value: .claude/skills/git-remotes/references/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:d590a7f2eb078710f44893b4866e821c840f48b7360f0779420d73331a3de597 +- kind: project-relative + target: claude + value: .claude/skills/git-remotes/references/remotes.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:80229bbd6d9e03e85e5ff49018181fb71003187f2c5e019869807e4e94aa4a46 +- kind: project-relative + target: claude + value: .claude/skills/git-remotes/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:33e966aba6946cd78d2b9e325670e5292ccdf73d90fcdfca70afb30ee70c1f2d +- kind: project-relative + target: claude + value: .claude/skills/git-submodules + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/git-submodules/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:1510e9691d5107c436bda3497066950bfd2e9c9612cdf8ebd40f5ec73780ddea +- kind: project-relative + target: claude + value: .claude/skills/git-submodules/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:3b4525c5e7145d30f4a945bf5f2429cca270f00e88ed85ff79c6d5fa89fa05ca +- kind: project-relative + target: claude + value: .claude/skills/git-submodules/references/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:ef5453d5e87fd62568d8c1c55fccfc41f6522316976a8f2cde7aa90c1f857809 +- kind: project-relative + target: claude + value: .claude/skills/git-submodules/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:1786dcf90dd21988379a5ee4f243b50a158d87bc059d4903bf54ef268811c5e7 +- kind: project-relative + target: claude + value: .claude/skills/git-submodules/references/submodules.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:ffab8cef061118a83ef480c5e8077c0e1ac2fa7e5e1fae333cfff9bfd362003f +- kind: project-relative + target: claude + value: .claude/skills/git-workflow + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/git-workflow/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:00844e92a521b46273e1c9ee334440c868348c1795ae86eb7fd95fb86dbbfbed +- kind: project-relative + target: claude + value: .claude/skills/git-workflow/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:87a3f39fab4db85ce8d2b23d01d1e8b2180bf0a73eb18183eae2d7e28c2cc42b +- kind: project-relative + target: claude + value: .claude/skills/git-workflow/references/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:f6aa9351e7d87421f64573a0b1db5b400b0c2c2686097625070e5f1113d72426 +- kind: project-relative + target: claude + value: .claude/skills/git-workflow/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:186c6fec31bcefe10bf4409d8ef9b6fdfff6185b431f30c66712d0669df0be55 +- kind: project-relative + target: claude + value: .claude/skills/git-worktrees + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/git-worktrees/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:084c936cf0aa2fc808e5fcf8acaec148d93d8ac03a99a6f3d06137adcc14585c +- kind: project-relative + target: claude + value: .claude/skills/git-worktrees/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:2ea1122b4f8bc864a85248bde6983a930ad3f91b60286a761cb0de361b25183f +- kind: project-relative + target: claude + value: .claude/skills/git-worktrees/references/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:ea2f3249fe0d0af24fa91f4d050f2aead9fc84841333530bce0ddfc7378d56c5 +- kind: project-relative + target: claude + value: .claude/skills/git-worktrees/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:5b7fe81f56eb03360a4719f2a988674f43891f7a2bfc14756d68398088767e36 +- kind: project-relative + target: claude + value: .claude/skills/git-worktrees/references/worktrees.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:38999d4d07d85c6142495c3765d56a0b5fdf861487a9347317f6b054a7d3dbc8 +- kind: project-relative + target: claude + value: .claude/skills/gitea-branches + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/gitea-branches/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:ce7bff9d60a3edd4655af9b4f3bb51520ffc2ff2a90f98f55e29a1b72dc5e3ee +- kind: project-relative + target: claude + value: .claude/skills/gitea-branches/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:0d8a1ac2e450d60c123ae1538dc8cca49b270da410d2e031cf250cdded2bf839 +- kind: project-relative + target: claude + value: .claude/skills/gitea-branches/references/branches.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:f045fb181d2fc1c948a2e026c335453273931f3a8d710a45b05826547ffe89e2 +- kind: project-relative + target: claude + value: .claude/skills/gitea-branches/references/commits.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:ca70a1c32ff6abf296a027c27c678f1574eddfdc069d7ed4ae621524dcd9adc0 +- kind: project-relative + target: claude + value: .claude/skills/gitea-branches/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:1bfad24252356c6005935ccc10a87871709d4b41e6b2d69abe9ac7ab188f4bcd +- kind: project-relative + target: claude + value: .claude/skills/gitea-files + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/gitea-files/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:f9d721b7ed8eae43b5eab04324dbc96fa91964accc77ea74d1b889475592256f +- kind: project-relative + target: claude + value: .claude/skills/gitea-files/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:9362d82068257d0aee113fd55f2acf4d53e8d20f638453946b76384a8c49a9b7 +- kind: project-relative + target: claude + value: .claude/skills/gitea-files/references/examples.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:26209f202dd0ec12c73bb62984f9fe42c843e2819dab89752fdf478444a5d79f +- kind: project-relative + target: claude + value: .claude/skills/gitea-files/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:71981b2987c56589e469f2305afbf7d404e605c8db1a6d50c1576d50eb9eea9c +- kind: project-relative + target: claude + value: .claude/skills/gitea-issues + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/gitea-issues/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:2f5844cf2fde1a62844eb26ac19f915f50fe8fdc5bfde982be018ed2d0e15c30 +- kind: project-relative + target: claude + value: .claude/skills/gitea-issues/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:9084c5f37d23ebcc6de5940ad8fcc1c699aa7d4693623bf029a031b21412e7d6 +- kind: project-relative + target: claude + value: .claude/skills/gitea-issues/references/enrichments.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:024872fe787688f7ff51c856264bd3405208543fcbccd113cce464135dbc9f90 +- kind: project-relative + target: claude + value: .claude/skills/gitea-issues/references/issues.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:5c8103944f316f7681ee2b3d5ebbc55dc73a2f5a25616325c87af03b93944a04 +- kind: project-relative + target: claude + value: .claude/skills/gitea-issues/references/search.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:dec2a98a9d781122d235b8a3fa6cd9efdcc08be0a2b71eb29f90a6e14338c0af +- kind: project-relative + target: claude + value: .claude/skills/gitea-issues/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:86d5c500e103d5eea31595ab04fb79d9e908215d83d2f0edec1e0e73995d2e96 +- kind: project-relative + target: claude + value: .claude/skills/gitea-labels-milestones + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/gitea-labels-milestones/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:185dd654d9d775fe0b0464a0dbdf724b06f6375fa6d7c7e200ee826fdcc9b4b5 +- kind: project-relative + target: claude + value: .claude/skills/gitea-labels-milestones/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:ce07c4c8827747b13b3d9ea9d20f8318ace0803f2bd42804e717ed9201c54254 +- kind: project-relative + target: claude + value: .claude/skills/gitea-labels-milestones/references/label-inference.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:da141129c74d22a441303b910b68d5bb758435361efbd8d2669f46920d699361 +- kind: project-relative + target: claude + value: .claude/skills/gitea-labels-milestones/references/labels.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:be8ff5ce4dbfb31dd2bdcc425a769dca94887c5c8acd47c78e1e21ccfc24dcba +- kind: project-relative + target: claude + value: .claude/skills/gitea-labels-milestones/references/milestones.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:ff1f1b0c8ecb6967c940d1ca8bebf2a2c7995414936f8ac7f5ff23d1dc01ac3f +- kind: project-relative + target: claude + value: .claude/skills/gitea-labels-milestones/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:a1a1d3381dfb0dc8f331be0b3ab49f28e94b992fb4809ca09be91ce33047dd1e +- kind: project-relative + target: claude + value: .claude/skills/gitea-prs + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/gitea-prs/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:0346388f642641c56cef82ba1ee576a187d048eee66fab839a17417c81122cbc +- kind: project-relative + target: claude + value: .claude/skills/gitea-prs/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:bf70832f3d97064c9816d680390da84c256a971d7804317e1814b4ee6a622b4d +- kind: project-relative + target: claude + value: .claude/skills/gitea-prs/references/merging.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:5f8b55b1a729f122ee265fb5da69369fc27434a13dd02fc112801f945d2c9d19 +- kind: project-relative + target: claude + value: .claude/skills/gitea-prs/references/pull-requests.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:631874a7cddfcf678a2beffa5578fa7848bcef36be1251fa13fb0ccc543bcaf7 +- kind: project-relative + target: claude + value: .claude/skills/gitea-prs/references/reviews.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:1089cf45a75a562bbb29de6538848c4f4bd627d5a7e36bf9b2ab6496f45f962b +- kind: project-relative + target: claude + value: .claude/skills/gitea-prs/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:ac48a3018443b56acb5c3d55d6c04e62103f5bac71b8185e2bd085fb7e165431 +- kind: project-relative + target: claude + value: .claude/skills/gitea-releases + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/gitea-releases/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:102625066c34c7b5a16e25b7506a87f876e9b123b334f5ffba73ab1523a57ed7 +- kind: project-relative + target: claude + value: .claude/skills/gitea-releases/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:793a6936c0d56acb4b4aba1d72e1fc91690273df0aafe447e54cb53df00319c1 +- kind: project-relative + target: claude + value: .claude/skills/gitea-releases/references/call-signatures.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:a523dba171d67a7e7899b7d8ecfe9220f250da2c184b0f2806e5d22647b1ffb0 +- kind: project-relative + target: claude + value: .claude/skills/gitea-releases/references/conventions.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:1c8102ed489422706babea4e43e12dc7684b7dacd210fa29b3793bffa9cd9f64 +- kind: project-relative + target: claude + value: .claude/skills/gitea-releases/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:37a14495c461120a5bab0013531c9e83e7d9b670e78129a9439a81b48a64cce4 +- kind: project-relative + target: claude + value: .claude/skills/gitea-workflow + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/gitea-workflow/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:338f79d0b3c25741848fce841a5094ef27cbc7e4a6228404557b2d9594df68fa +- kind: project-relative + target: claude + value: .claude/skills/gitea-workflow/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:a8266ceb335c56eb68cc09b3f81690c52c4dada9f25b48c3a46ac46b0ad3b761 +- kind: project-relative + target: claude + value: .claude/skills/gitea-workflow/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/gitea + content_hash: sha256:171d3a5a36cb9d637302516501a1e9fc7a4bd058968dc9244412c335aa2de68b +- kind: project-relative + target: claude + value: .claude/skills/grill-me + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/grill-me/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:74147eb6010a65957efef2b9e0f0b3ff935c1def7fc117697151b1d0f3610556 +- kind: project-relative + target: claude + value: .claude/skills/grill-with-docs + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/grill-with-docs/ADR-FORMAT.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:f1f36cd3f8d3b6474ddd5855da4e233bfc4ae1a1c5024909ccf11871819a41b2 +- kind: project-relative + target: claude + value: .claude/skills/grill-with-docs/CONTEXT-FORMAT.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:8f6baaa3b1c91644bd7c600196b1aee781d5f525c7c345db8cdfbfb368329a05 +- kind: project-relative + target: claude + value: .claude/skills/grill-with-docs/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:c7176600a8bffc9f359102cb041b6686387618b8e1434ba484dd708d45df6d5e +- kind: project-relative + target: claude + value: .claude/skills/improve-codebase-architecture + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/improve-codebase-architecture/DEEPENING.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:9577485f4fc32c0267639a9151bb41c8af0f8f6086e4bf8b84d5b236e30604e9 +- kind: project-relative + target: claude + value: .claude/skills/improve-codebase-architecture/INTERFACE-DESIGN.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:678c3e34f1339015053212b3316bf0b676c70aa251050a0613667d4e755fb35e +- kind: project-relative + target: claude + value: .claude/skills/improve-codebase-architecture/LANGUAGE.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:6feca2140439c54a774749e8367f18350899ff69c777144ed2248cd4407949fa +- kind: project-relative + target: claude + value: .claude/skills/improve-codebase-architecture/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:318b3df6a557f3a9216e6c32813d543301fa51954ea79c87edb95e1297213584 +- kind: project-relative + target: claude + value: .claude/skills/pc-author + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/pc-author/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:2e21bd50002bd2622ebb31793fa4aece14f5233889c3a054fe99f1974f19bb40 +- kind: project-relative + target: claude + value: .claude/skills/pc-author/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:577b9bd06615182ce3b0123640d989c3b74ab05bda8103a63a41e1f5a1abb2ec +- kind: project-relative + target: claude + value: .claude/skills/pc-author/references/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:c409caf0e08af23f2ec056b649cee0bba22b55c1fbe4b8f1189ce0bbb9b5d5c4 +- kind: project-relative + target: claude + value: .claude/skills/pc-author/references/hooks-by-language.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:2c4ade2ac32e7a9c539d7e08b7d55739e8d87c634860bb58e8764067293bb35d +- kind: project-relative + target: claude + value: .claude/skills/pc-author/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:e1cff11955490e4948fec81d8172b0adc5a37829aa0ffe1791c0f6313ed34a4e +- kind: project-relative + target: claude + value: .claude/skills/pc-run + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/pc-run/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:2e2ebd5220c3251a40afa6c06243ba12fb0aa03dc60fb76db74b22c44914f353 +- kind: project-relative + target: claude + value: .claude/skills/pc-run/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:7a0890f7447a04f464ca8a30e5a967ec4421e9ef41298d469f8f2a6ba03be0a2 +- kind: project-relative + target: claude + value: .claude/skills/pc-run/references/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:ad42b9013dbd44b8c4515c15317c5e9e6b235bc656ad5a03e037eb4bc6f8782c +- kind: project-relative + target: claude + value: .claude/skills/pc-run/references/failure-patterns.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:59d913c001483c3770d269fdf5e83cf1d7578e0ae879928a1404bf93bb395994 +- kind: project-relative + target: claude + value: .claude/skills/pc-run/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/git + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/git + content_hash: sha256:7ca8b7106e7ee5c2c7d1fbd3dc9e0d2a89c18eba79145cbab55bc8e39eef9087 +- kind: project-relative + target: claude + value: .claude/skills/prototype + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/prototype/LOGIC.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:da91dc92195c00d5dc33863b8c1a030998025a0f8583ebf2babd770825b7f70c +- kind: project-relative + target: claude + value: .claude/skills/prototype/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:913df144c79658394b7ccc984858103a27553eeff9a8bf93dc51486603259d0f +- kind: project-relative + target: claude + value: .claude/skills/prototype/UI.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:d76d565149ee50456c5ddc5e29c27fec8737637874fe5c37d970db085a200b27 +- kind: project-relative + target: claude + value: .claude/skills/provider-adapter-author + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/core + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/core + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/provider-adapter-author/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/core + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/core + content_hash: sha256:9aa132d5ffa7f249fa4c8f0e90f8505c1f1d0dcdd681e7930e5b0236407038b5 +- kind: project-relative + target: claude + value: .claude/skills/provider-adapter-author/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/core + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/core + content_hash: sha256:7d2d9c9555373fdac86683c517fcccca1d090347c9c1d13f91942763219d2053 +- kind: project-relative + target: claude + value: .claude/skills/provider-adapter-author/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/core + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/core + content_hash: sha256:45dfe2ff95744d7a00fa5b75be216562ff84047f21588ef244e89bdc2e5ae1bb +- kind: project-relative + target: claude + value: .claude/skills/provider-adapter-author/scripts/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/core + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/core + content_hash: sha256:196aa7752b2df4710b397e1927f68b0b966caa8a3b1006122ef6e184882af01b +- kind: project-relative + target: claude + value: .claude/skills/provider-adapter-author/scripts/validate-adapter.sh + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/core + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/core + content_hash: sha256:519c8ab3d33884821a6090ec5ba0322ec1594f9c1ca388b44279133a1e0fa9be +- kind: project-relative + target: claude + value: .claude/skills/research + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/research/META.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:81a31d9ba20988b07bd4e141f0a7369d4dda302772d62809cc8b223ca1fbb84f +- kind: project-relative + target: claude + value: .claude/skills/research/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:9ca6b3b8c9c1d04f93a6ef55149d0eecc9ffa82a28db9fb3f9b588c661b1b747 +- kind: project-relative + target: claude + value: .claude/skills/research/references/file-format.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:29d5583beaa531d26895c54def112039730490c312401648a6bdb7dd5278fb3c +- kind: project-relative + target: claude + value: .claude/skills/research/references/topics.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:757abb7b6be44c3d3c0dd1f4818a344e08877810d36bf8be60ea7189cba8d12f +- kind: project-relative + target: claude + value: .claude/skills/skill-audit + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/skill-audit/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:9e3533c3c44cfd249cf02684ef43db822ed41382362e8ce71f1254d10711c8e0 +- kind: project-relative + target: claude + value: .claude/skills/skill-audit/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:dbe2adfd0101cf9d6b3c2c3c2aa3ae397c717f835c1059c0f1d805e9057dc397 +- kind: project-relative + target: claude + value: .claude/skills/skill-audit/assets/vale/.vale.ini + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:0d1108b17a941b514dd9a62a7382d1febdd543118750246c81fba406440a9f9f +- kind: project-relative + target: claude + value: .claude/skills/skill-audit/assets/vale/styles/Kyberforge/DescriptionOpener.yml + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:a399516a457d40e0af5536a6575c70221909cecac5722eda2788430ebddd8f67 +- kind: project-relative + target: claude + value: .claude/skills/skill-audit/assets/vale/styles/Kyberforge/PaddingPhrase.yml + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:67b738f5c393a717bdf249fcd946eb7b0d258215bf2240d2be6902711b46d8e9 +- kind: project-relative + target: claude + value: .claude/skills/skill-audit/assets/vale/styles/Kyberforge/SentenceOpenerThereIs.yml + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:5dade0238d96730d24bb6e8dba003c3d94694bd9969687f461fe1fada3ca4629 +- kind: project-relative + target: claude + value: .claude/skills/skill-audit/assets/vale/styles/Kyberforge/VagueWording.yml + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:d4bf14d0bb2dcbe3a89b66b72a9c9c19d175060b5c4c978da3d67010f1a25083 +- kind: project-relative + target: claude + value: .claude/skills/skill-audit/references/body-discipline.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:2295d2fc72fa529f3700c21fa93516ad186a7cc66d470370173ab1a784d03d31 +- kind: project-relative + target: claude + value: .claude/skills/skill-audit/references/description-quality.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:ab077e71bbc6e9d8e2a1384bedaf613a4219f28bbe42eb9e89f933b579db6b33 +- kind: project-relative + target: claude + value: .claude/skills/skill-audit/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:cb65dd3cc411d7b264fab24de5cd2353dec8fbb7bbcfe2388535d963be240280 +- kind: project-relative + target: claude + value: .claude/skills/skill-audit/scripts/vale-wrap.sh + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:c17673b3b0de1c1a49fcb73e2b695835a45014ec1c300a77a3ba7b3c7ce4c995 +- kind: project-relative + target: claude + value: .claude/skills/skill-audit/scripts/validate-provenance.sh + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:015376c5ad1f6bb6b53549537317a55e3ac06cd4e347451dba85879a3928ccbe +- kind: project-relative + target: claude + value: .claude/skills/skill-audit/scripts/validate.sh + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:ebbef859276135b7fe4189b31396fd98e5d4019685d7cb55e39a10fac8ca546d +- kind: project-relative + target: claude + value: .claude/skills/skill-author + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/skill-author/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:6c0b36f9b28a33de1646df45bd8e4d733ab3b26c0bdff49743962aa068085f6d +- kind: project-relative + target: claude + value: .claude/skills/skill-author/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:7b82d16fe0d61a71c5b59a244cf92b9b5fc2635916ad1572f4f5f6e37172bf09 +- kind: project-relative + target: claude + value: .claude/skills/skill-author/assets/templates/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:f7e91356f4c85862f96a9f46a7a7ae8b5dc715c8174add5cf070166a8bae0658 +- kind: project-relative + target: claude + value: .claude/skills/skill-author/assets/templates/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:f05a127963874bfbbe4333c42c5208736d84d5bf12dd8478351f6853304cc9d1 +- kind: project-relative + target: claude + value: .claude/skills/skill-author/assets/templates/assets/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:b72e51e643c45ded210a257e475d5661035d2aa5ed581e3294fd2aed00765c4b +- kind: project-relative + target: claude + value: .claude/skills/skill-author/assets/templates/references/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:63754636f84a7841f31ab1f2854025ee449a29840ed6b9b51bcb1422ab5522d0 +- kind: project-relative + target: claude + value: .claude/skills/skill-author/assets/templates/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:fe126720ba890b98e829f2626e3f5a361a0c2ce47eda97bd3f7dc1812ddd62b7 +- kind: project-relative + target: claude + value: .claude/skills/skill-author/assets/templates/scripts/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:ee427eed6a562a6e86898c69c35508c518d5b3c633ae803e587fbab37514f2b2 +- kind: project-relative + target: claude + value: .claude/skills/skill-author/assets/templates/tests/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:5d331de121105ca79b544b9d3779a316a64cee46fa8d51ba0ea789ab5c233442 +- kind: project-relative + target: claude + value: .claude/skills/skill-author/references/deployment-modes.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:ffe2d928f2b5ec90c509f4d4cc5bd33c71899852cb5cc5a31fe1513b707fb759 +- kind: project-relative + target: claude + value: .claude/skills/skill-author/references/scripts.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:fe71da1fb3d947846ad1a37348f90866b0c4f7dbd81331cfe3300981656f6cc2 +- kind: project-relative + target: claude + value: .claude/skills/skill-author/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:27637acad4c0cdc7f1db15bc9cab339ebdac643e084642c58945ac2f81020c75 +- kind: project-relative + target: claude + value: .claude/skills/skill-author/scripts/new-skill.sh + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/kyberforge + content_hash: sha256:46c6903404b9eabbd6c83892213fdabf9cad193be64613b8c1b84c8dc37e8eb2 +- kind: project-relative + target: claude + value: .claude/skills/tdd + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/tdd/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:af059705061156fd4845ddbb736fe92b564118ac5f03551e09ef9e8f6d970638 +- kind: project-relative + target: claude + value: .claude/skills/tdd/deep-modules.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:f2123700bf953db1740625686c75bdd145efd486357c47c6818c869fee657a32 +- kind: project-relative + target: claude + value: .claude/skills/tdd/interface-design.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:764c5ff0e3fa6b4ab7095eb65ccc7201e090baf19fa16051dfdb72c06d27417d +- kind: project-relative + target: claude + value: .claude/skills/tdd/mocking.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:3ceb807fdf4a47d6a93d4d9a891e5ba6d362a6247bd08adc451feebfc17361ef +- kind: project-relative + target: claude + value: .claude/skills/tdd/refactoring.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:54fced22dd1911b7094c3fe7979b7c1a40d40be307482c4adf7dc0588f27d6cc +- kind: project-relative + target: claude + value: .claude/skills/tdd/tests.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:e12182f5c4c6a844b7c6ab4b8d2623b642aaaae5460fe58ed087efd4f3d5fb8a +- kind: project-relative + target: claude + value: .claude/skills/triage + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/triage/AGENT-BRIEF.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:1c71c747aa97d938f75ec7a4b8fe87451763b38a3ce6348acf2b222e5819b2e5 +- kind: project-relative + target: claude + value: .claude/skills/triage/OUT-OF-SCOPE.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:8ed8cf27833444060c81b3961a83c0e3d8e6cf2fcb2ddf6f8b07c6655cbb0d85 +- kind: project-relative + target: claude + value: .claude/skills/triage/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:b819f0285e4e5814ac6472d0217f07dc9f23d2fdae3ebb0dd31d98360d8ac029 +- kind: project-relative + target: claude + value: .claude/skills/vale-config + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/lint + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/lint + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/vale-config/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/lint + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/lint + content_hash: sha256:faf6bca23cea74be7ecf47a31b0499af2f6ce791bbc82eb67069e1e67eeed07c +- kind: project-relative + target: claude + value: .claude/skills/vale-config/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/lint + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/lint + content_hash: sha256:1674f1256b654779268c42f764fa0c4f09011ce8ea62eb0e01fd8f118495519b +- kind: project-relative + target: claude + value: .claude/skills/vale-config/references/configuration-reference.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/lint + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/lint + content_hash: sha256:1c2dbee6315178b00e624c002a6ae8d9d20f03f2c7ff1c3fba4eebe38fb39a7f +- kind: project-relative + target: claude + value: .claude/skills/vale-config/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/lint + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/lint + content_hash: sha256:2efa451c3cc6bdc1103cfe56712a3ea35acef9a76fe4c6f71e8cacabd92dc860 +- kind: project-relative + target: claude + value: .claude/skills/vale-run + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/lint + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/lint + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/vale-run/README.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/lint + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/lint + content_hash: sha256:1a850c89cf7de288dc72b96cae6fec24a3ccdc237f0ce785f59cca56c7762198 +- kind: project-relative + target: claude + value: .claude/skills/vale-run/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/lint + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/lint + content_hash: sha256:16c42c97de14ef20296b9dfce46b0e2f9be6ad68751c35975251cecff80f79d9 +- kind: project-relative + target: claude + value: .claude/skills/vale-run/references/sources.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/lint + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/lint + content_hash: sha256:1f46b727e5f3db09d6c8c01c8a615bad60ce2c5b3a01226e1a3fd3b933330bc7 +- kind: project-relative + target: claude + value: .claude/skills/vale-run/references/troubleshooting.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/lint + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/lint + content_hash: sha256:b4c2bc67b413b102d9fe1cd7e5a248b19aba3dc3b60b4429e777977cf8021cd5 +- kind: project-relative + target: claude + value: .claude/skills/write-docs + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/write-docs/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:0d06d0f6836a67532497ea61bd5a1294a8d80f529bf1ffb5c4b1fdc72e9cb51a +- kind: project-relative + target: claude + value: .claude/skills/zoom-out + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: null +- kind: project-relative + target: claude + value: .claude/skills/zoom-out/SKILL.md + runtime: null + scope: project + owners: + - git.dev.rkdr.net/Defame1297/holocron/plugins/bin + active_owner: git.dev.rkdr.net/Defame1297/holocron/plugins/bin + content_hash: sha256:2a6894c7f9b1c9c55f451c625a834c4f377e217b623a85280e55db5fe9cacf48 +mcp_servers: +- obsidian +mcp_configs: + obsidian: + name: obsidian + transport: stdio + args: + - '@bitbonsai/mcpvault@latest' + - docs/ + registry: false + command: npx +mcp_config_provenance: + obsidian: bin diff --git a/apm.yml b/apm.yml index 4afd41e..bfaa15a 100644 --- a/apm.yml +++ b/apm.yml @@ -1,14 +1,58 @@ name: holocron -version: 0.3.4 +version: 0.4.1 description: AI development skills for Claude Code and GitHub Copilot CLI — factory, design, implement, review, and cross-cutting workflows. license: MIT + +# Consumer side: this repo installs its own published plugins from the holocron +# remote, so the working copy runs the same released content every other +# consumer gets. Addressed as git+path objects rather than @holocron +# marketplace aliases — an alias needs a `apm marketplace add` registration in +# ~/.apm/marketplaces.json (user scope, outside this repo), the object form +# needs nothing beyond this manifest. +# Unpinned (default branch) on purpose: parity with the Claude Code plugin +# install this replaced, which ran autoUpdate against main. Add `ref: ` +# per entry to pin. +targets: + - claude +dependencies: + apm: + - git: git@git.dev.rkdr.net:Defame1297/holocron.git + path: plugins/bin + - git: git@git.dev.rkdr.net:Defame1297/holocron.git + path: plugins/core + - git: git@git.dev.rkdr.net:Defame1297/holocron.git + path: plugins/git + - git: git@git.dev.rkdr.net:Defame1297/holocron.git + path: plugins/gitea + - git: git@git.dev.rkdr.net:Defame1297/holocron.git + path: plugins/kyberforge + - git: git@git.dev.rkdr.net:Defame1297/holocron.git + path: plugins/lint + mcp: [] + +# Turns apm's executable-trust gate ON. Without this block the gate is disabled +# and every hook, bin and MCP primitive a dependency ships deploys silently — +# verified: `apm approve --list` reports "Executable-trust gate disabled -- all +# executables deploy" until an `executables:` block exists. +# +# kyberforge ships the SessionStart hook that keeps this install level with the +# remote (ADR-0019). The key is version-pinned by apm's own design, so a +# kyberforge version bump makes this entry stop matching and the hook stops +# deploying until the version here is bumped too. If skills silently go stale +# after a kyberforge release, check this first. +executables: + allow: + kyberforge#1.5.0: + hooks: true + bin: true + 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.4 + version: 0.4.1 owner: name: Defame1297 email: defame1297@rkdr.net @@ -35,13 +79,13 @@ marketplace: - name: kyberforge description: Skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace. source: ./plugins/kyberforge - version: 1.4.1 + version: 1.5.0 category: Developer Tools - name: bin description: A place for things to be binned source: ./plugins/bin - version: 1.1.2 + version: 1.1.3 category: Utilities - name: git diff --git a/docs/adr/0018-repo-consumes-its-own-plugins-through-apm.md b/docs/adr/0018-repo-consumes-its-own-plugins-through-apm.md new file mode 100644 index 0000000..8b2eb95 --- /dev/null +++ b/docs/adr/0018-repo-consumes-its-own-plugins-through-apm.md @@ -0,0 +1,132 @@ +# This repo installs its own plugins through apm, not Claude Code's native plugin install + +ADR-0015 moved plugin **authoring** to apm; ADR-0017 added the flat content mirror that keeps the +authored `.apm/` tree discoverable by hosts that install natively. Both are about producing the +marketplace. This ADR is about consuming it: how the plugins get onto the machine this repo is +worked on. + +**Status: executed (2026-08-14).** All six packages are installed into `/root/ai-development` by +`apm install`; the six native project-scope installs (`claude plugin uninstall @holocron +--scope project`) are gone and `.claude/settings.json`'s `enabledPlugins` block is empty. + +## Context + +Until now the repo consumed its own output the same way any user would: `claude plugin install +@holocron`, six plugins enabled per-project in `.claude/settings.json`, the `holocron` +marketplace registered in `~/.claude/plugins/known_marketplaces.json` with `autoUpdate: true`. +That worked. It also meant the repo's dogfooding stopped one layer short of the tooling it +publishes: `kyberforge` ships `apm-workflow` and `apm-install` skills describing an install path +the repo itself did not take. + +apm supports both scopes. `apm install --global` deploys to `~/.claude/`; plain `apm install` +deploys to the project. Global was rejected deliberately — the switch should be provable in one +repo before it changes how every other project on the machine resolves its skills. + +## Decision + +Root `apm.yml` declares all six packages under `dependencies.apm`, each as a `git:`/`path:` object +against the holocron remote: + +```yaml +dependencies: + apm: + - git: git@git.dev.rkdr.net:Defame1297/holocron.git + path: plugins/core +``` + +`apm install` deploys them to `.claude/skills//` and `.claude/agents/.md`. + +Three sub-decisions inside that: + +- **Object form over the `@holocron` marketplace alias.** The alias is shorter and apm + resolves it correctly (verified end-to-end against this remote), but it first requires + `apm marketplace add`, which writes to `~/.apm/marketplaces.json` — user scope, outside the + repo, and absent on a fresh clone. The object form needs nothing beyond the committed manifest. +- **Remote source over local path.** apm accepts `path: /root/ai-development/plugins/` as a + local dependency, which would make the working tree live instantly. Rejected: it erases the + distinction between editing a skill and shipping one, which is the entire point of having a + marketplace. The remote form keeps the repo running the same released content every other + consumer gets. +- **Unpinned against the default branch.** Parity with the `autoUpdate: true` the native install + had. apm warns on every install (`6 dependencies unpinned`); accepted knowingly. Pinning is a + per-entry `ref:` away once the repo tags releases per package — today `git tag` lists one tag + total, so there is nothing meaningful to pin to. + +## Consequences + +**Skills gain an unnamespaced name.** apm deploys plain project skills, so `git:git-commits` also +answers to `git-commits` and `kyberforge:skill-audit` to `skill-audit`. This is not configurable — +a project skill has no plugin to prefix. `AGENTS.md` and `CONTEXT.md` are updated to name the bare +form, which is what apm deploys and the only form a repo consuming holocron through apm gets. + +**Correction (2026-08-14): the namespaced form did not stop resolving.** An earlier revision of +this consequence said every `:` reference "was stale the moment the switch landed", +and `AGENTS.md`/`CONTEXT.md` were written to match. That contradicts the "User scope is untouched, +deliberately" consequence below, and the contradiction resolves against it: `~/.claude.json` still +enables `core`, `git`, `gitea`, `kyberforge` and `lint` at user scope, so both names are live at +once and a working `gitea:gitea-prs` is the user-scope copy answering. That doubling is the same +"present twice under two names" outcome the "Keeping both install paths" alternative was rejected +for — reached by leaving user scope alone rather than by adopting it, which is why it is a +consequence to record rather than a decision to revisit. Prefer the bare name regardless: it +survives those user-scope installs eventually being converted, and the namespaced form still +resolves for anyone installing holocron natively, so skill bodies written for both audiences +should name the bare skill. + +**apm owns `.claude/settings.json`.** (ADR-0019 supersedes the "exactly `{"hooks": {}}`" claim +below — once a package ships a hook, apm merges it into that file and the merged entry is apm's own +output. The rule that nothing repo-authored goes in the file is unchanged.) `apm audit --ci` replays the install into a scratch tree and +diffs it against the worktree. apm's hook integrator writes that file, so the replay expects +exactly what apm would have written — `{"hooks": {}}` — and any repo-owned key in it is permanent +drift that fails the `apm-audit-ci` pre-push hook. Verified both directions: with the pre-existing +`enabledPlugins` block present, `1 of 10 check(s) failed`; reduced to `{"hooks": {}}`, +`All 10 check(s) passed`. Nothing was lost in that reduction — `enabledPlugins` was empty after the +native uninstall and the only `hooks` entry was an empty `PreToolUse: []` — but it does mean the +file is no longer available for repo-owned settings. Machine-specific settings go in the gitignored +`.claude/settings.local.json`, which apm does not deploy; shared enforcement belongs in +`.pre-commit-config.yaml`, where this repo already keeps it. + +**`apm_modules/` breaks naive tree walks.** apm materializes a full copy of every dependency there +— including this repo's own plugins, `.bats` files and all. The dependency copies resolve their +bats helpers relative to their own root, not this repo's, so `tests/run-tests.sh` went from 167 +tests passing to `334 tests, 167 failures` on the first install. Both discovery walks +(`tests/run-bats.sh`, `tests/run-tests.sh`) now exclude `apm_modules/`, on the find side and on the +`git ls-files` side that derives the expected set. Any future script that walks the repo tree needs +the same exclusion. + +**Install output is gitignored; the lockfile is not.** `.claude/skills/`, `.claude/agents/`, and +`apm_modules/` are regenerated by `apm install`. Committing the deployed skills would add a third +mirror of content ADR-0017 already governs two copies of. `apm.lock.yaml` is committed — it is what +makes the install reproducible, and `apm audit --ci` checks it. + +**MCP survived the switch; hooks were never at risk.** apm read `plugins/bin/.mcp.json` as a +self-defined direct-dependency MCP server and configured `obsidian` into the repo's `.mcp.json` +unprompted. The `gitea` and `context7` servers were never plugin-provided — they live in +`~/.claude.json` and are untouched. Every plugin's `.apm/hooks/hooks.json` is `{"hooks": {}}`, so +apm's "contributed no entries to claude settings; skipped" warning on `kyberforge` and `lint` is +accurate and harmless. + +**A `.apm/` edit now needs a round trip.** The dependency resolves from the remote, so an edit is +invisible to the running session until it is pushed and the install is refreshed. Under the native +install with `autoUpdate` the shape was the same; it was more noticeable here at first because the +refresh is a manual step where marketplace auto-update was not — ADR-0019 automates it at +`SessionStart`. + +**Correction (2026-08-14): the refresh command is `apm update`, not `apm install`.** An earlier +revision of this paragraph named `apm install`, which is wrong: `apm install` deploys from the +pinned `resolved_commit` in `apm.lock.yaml` and does not re-resolve refs (`apm install --force` +documents this explicitly — "does NOT refresh refs; use 'apm update' for that"). Running it after a +merge redeploys the same content and reports success. + +**User scope is untouched, deliberately.** `bin@holocron`, `gitea@holocron`, and a stale +`hello-world@holocron` remain natively installed at user scope, and every project other than this +one still resolves its skills that way. Converting them is a separate decision with a blast radius +beyond this repo. + +## Alternatives considered + +- **`apm install --global`.** Verified working in an isolated `HOME`: user-scope deploys land in + `~/.claude/skills/` and `~/.claude/agents/`, and it is the only scope where a plugin's `bin/` + executables deploy (moot here — every `bin/` in this repo is empty but for a README). Deferred, + not rejected: it changes skill resolution for every project on the machine at once. +- **Keeping both install paths.** Rejected: the same skill would be present twice under two names, + and `.claude/settings.json` cannot hold `enabledPlugins` without failing `apm audit --ci`. diff --git a/docs/adr/0019-session-start-hook-keeps-the-apm-install-current.md b/docs/adr/0019-session-start-hook-keeps-the-apm-install-current.md new file mode 100644 index 0000000..f3f42a1 --- /dev/null +++ b/docs/adr/0019-session-start-hook-keeps-the-apm-install-current.md @@ -0,0 +1,167 @@ +# A SessionStart hook keeps the apm install current, replacing a git hook that never ran + +ADR-0018 switched this repo to consuming its own plugins through `apm install`, with the six +packages declared as unpinned git refs against the holocron remote's default branch. That decision +left a hole it named but did not fill: the deployed content goes stale the moment anyone merges, +and nothing detects it. + +**Status: accepted (2026-08-14).** + +## Context + +The pre-existing answer was `scripts/git-hooks/post-push`, which pulled the marketplace clone and +ran `claude plugin update kyberforge`. Issue #78 filed it as a bug — the hook updated `kyberforge` +but not `gitea`, so gitea skills stayed pinned at a pre-refactor version after #67 merged. + +The issue's premise was wrong in a way nobody had noticed for six weeks. **Git has no client-side +`post-push` hook.** `githooks(5)` does not list one, and git 2.39.5 does not invoke one. +`scripts/install.sh` copies every file in `scripts/git-hooks/` into `.git/hooks/`, so +`.git/hooks/post-push` existed on disk and looked installed. It had never fired. The hook did not +skip `gitea`; it skipped everything. Both tests that appeared to cover it — `test-post-push.sh` and +`test-git-hooks-install.sh` — asserted only that the script behaved correctly when invoked directly +and that install.sh copied the file. Neither asserted that git ever runs it. + +That also makes the original framing wrong. Refreshing on push assumes the person who pushes is the +person who goes stale, which is backwards: your install goes stale when *someone else* merges, and a +push of your own is neither necessary nor sufficient for it to have happened. + +## Decision + +A `SessionStart` hook, shipped in `plugins/kyberforge/.apm/hooks/`, checks whether the install is +behind and refreshes it in place. + +`SessionStart` is the correct trigger because the thing that goes stale is the skill content a +*session* loads, and that is the moment the staleness does damage. It also enables two things a git +hook structurally cannot do: `additionalContext` puts the notice into the agent's context rather +than terminal scrollback nobody reads, and `reloadSkills: true` makes the host re-scan the skill +directories after the hook returns, so a refresh lands in the running session without a restart. + +apm's own lifecycle events (`pre-/post-install`, `pre-/post-update`, `pre-/post-uninstall`) were +rejected: they fire around apm operations already chosen, so they can announce a refresh but never +detect that one is needed. + +Three sub-decisions: + +- **Refresh automatically rather than report.** The hook runs `apm update --yes` and asks for a skill + reload. The rejected alternative was to report and let a human run it. Auto-refresh costs a + rewritten `apm.lock.yaml` — a committed file — appearing as an unexplained modification in the + working tree, on any branch, at any time. The emitted notice says so explicitly for that reason. +- **`plugins/kyberforge/.apm/hooks/`, not `.claude/settings.json`.** ADR-0018 established that apm + owns `.claude/settings.json` and that any repo-authored key in it is permanent `apm audit --ci` + drift. A hook shipped in a package is written into that file by apm itself, so it is apm's output + and does not drift. `.claude/settings.local.json` also works but is gitignored and machine-local, + which fails the requirement that this travel with the repo. +- **`startup` matcher only.** `resume`, `clear`, `compact` and `fork` would re-run the check on every + compaction, and a compaction is not an event after which the remote can have moved. + +The executable-trust gate is switched on at the same time. Root `apm.yml` gains an `executables:` +block allowing kyberforge's hooks and bin. + +## Consequences + +**The gate is off until something turns it on, and this repo had it off.** `apm approve --list` +reports `Executable-trust gate disabled -- all executables deploy` until an `executables:` block +exists in `apm.yml`. Any hook, bin, or MCP primitive a dependency shipped would have deployed with +no prompt and no record. The block added here closes that for this repo; every other apm project on +this machine still has it open. + +**The allow key is version-pinned, and that is a live failure mode.** apm writes +`kyberforge#1.5.0`, not `kyberforge` — and the release that ships this hook proved the point +immediately, since bumping kyberforge to 1.5.0 required editing the key in the same commit. A +kyberforge version bump makes the entry stop matching, the +gate blocks the hook, and the install silently stops refreshing — the exact failure this ADR exists +to end, reintroduced through the mechanism meant to secure it. + +Matching is an exact dictionary lookup on the composed `name#version` string +(`apm_cli/security/executables.py`, `is_package_approved`), so there is no wildcard or +version-less key that would sidestep this — the key has to be edited on every bump, and the +question is only what catches a missed edit. A comment in the `executables:` block is not enough: +this repo gates generated-content drift, marketplace mirror drift and vale style drift +deterministically, and a silent-staleness failure is strictly worse than any of them. So +`scripts/check-executables-allow-sync.sh` runs at pre-push, parsing `version:` out of +`plugins/kyberforge/apm.yml` and asserting root `apm.yml` carries the matching +`kyberforge#` key. The comment stays as the human-facing pointer; the hook is what +actually holds. It parses with PyYAML where importable and falls back to a two-shape scan +otherwise, so a missing pip package cannot become the thing that blocks every push. + +**Trust is keyed on the version, not on the content.** `kyberforge#1.5.0` approves whatever +`check-apm-current.sh` contains at the moment it is fetched, not the bytes that were reviewed when +the key was written. Because the dependency is unpinned against the default branch and the hook +runs `apm update --yes` unattended, an edit to that script landing on `main` deploys and executes +on every contributor's machine at their next session start, with no second approval prompt and no +diff shown. The trust gate constrains *which package* may ship an executable; it does not constrain +what that executable does between version bumps. That is an accepted property of this design rather +than an oversight — the remote is self-hosted, push access to `main` is already sufficient to +change any skill body an agent will follow — but it is the reason the gate should not be read as a +supply-chain control. Pinning each dependency to a `ref:` is what would make it one, and ADR-0018 +defers that until per-package release tags exist. + +**A referenced hook script must be addressed at its `.apm/` path.** apm resolves +`${CLAUDE_PLUGIN_ROOT}/...` against the installed package root, and `apm pack` keeps only `*.json` +from `.apm/hooks/` when it builds the flat mirror. So `${CLAUDE_PLUGIN_ROOT}/hooks/check-apm-current.sh` +resolves to the mirror, where the script does not exist — verified, apm reports +`Hook script not found` and deploys a hook pointing at nothing. The working reference is +`${CLAUDE_PLUGIN_ROOT}/.apm/hooks/check-apm-current.sh`. The script cannot simply be placed in +`plugins/kyberforge/hooks/` either: that directory is `rm -rf`'d by every content sync (ADR-0017). +A test pins the reference. + +**Session startup gets slower when the install is stale.** Measured: ~0.7 s for the `apm outdated` +check when everything is current, ~10.4 s when six packages are behind and the refresh runs. The +hook declares `timeout: 380` to cover a cold multi-package fetch. That number is not free-standing: +the script imposes its own `timeout 60` on `apm outdated` and `timeout 300` on `apm update`, so the +host-side timeout has to exceed their sum or the host kills the hook mid-update and leaves +`.claude/skills/` half-deployed with no notice emitted. An earlier revision declared `320`, which +was below the 360 s the script can legitimately take. A test asserts the invariant rather than the +literal — it parses every `timeout N` out of the script, sums them, and requires the `hooks.json` +value to be larger — so changing either side without the other fails the suite. + +**Reading a human-readable CLI for a control decision cost a silent failure, again.** `apm outdated` +has no `--json` or other machine-readable flag (confirmed against 0.28.0), so the hook must match +its prose. The first attempt matched `outdated dependencies found` — plural only. apm emits +`1 outdated dependency found` in the singular when exactly one package is behind +(`apm_cli/commands/outdated.py`), so a single stale package was invisible: the hook exited 0 +silently and no refresh ran. With six packages merging independently, one-behind is the ordinary +case rather than an edge, which means the mechanism failed most often in exactly the situation it +exists for. The match is now `outdated dependenc(y|ies) found`. + +The deeper lesson is the one `post-push` already taught and this repeated: every assertion about the +hook mocked `apm`, so the suite was green while the hook could not detect the common case. Mocks +verify the code against its author's belief about the interface, never the interface. The suite now +carries one probe that stages a genuinely outdated dependency against a local git remote — offline, +via `url..insteadOf`, so the twelve-hooks-pass-under-`unshare -rn` property survives — runs +the real `apm outdated`, and replays its genuine output through the real hook. Reverting the grep +to plural-only fails it. + +**The hook cannot install itself.** Dependencies resolve from the remote, so the hook does not +deploy until this change is merged and `apm update` has run once against the new default branch. +Until then the repo has the mechanism in source and not in effect. + +**`.claude/settings.json` stops being `{"hooks": {}}`.** apm merges the hook into it and tracks +ownership in a `.claude/apm-hooks.json` sidecar, with the script copied to +`.claude/hooks//`. The sidecar and the script directory are gitignored install output; the +settings file remains committed, now with apm-generated content in it. ADR-0018's statement that the +committed content is exactly `{"hooks": {}}` is superseded on that point only — the rule it was +protecting, that nothing repo-authored goes in that file, is unchanged. + +**Native consumers are protected by a guard, not by the gate.** A host installing holocron through +`claude plugin install` auto-discovers `hooks/hooks.json` and does not consult apm's trust gate at +all. The script therefore exits silently when there is no `apm.lock.yaml` in the working directory, +which is what makes it inert in a repo that does not consume packages through apm. Copilot CLI sees +no hook at all, for the reasons already documented in `plugins/kyberforge/docs/hooks.md`. + +**`scripts/git-hooks/` is now empty.** `post-push` and `test-post-push.sh` are deleted. +`install.sh`'s copy block is generic and is kept; `test-git-hooks-install.sh` now synthesizes its +own fixture hook instead of depending on a real one existing, so the mechanism stays tested and can +be used again if a hook git actually invokes is ever wanted. + +## Alternatives considered + +- **A `post-merge` git hook.** Real, unlike `post-push`, and verified to fire on both a + fast-forward `git pull` and a `git pull --rebase`. Rejected as the primary mechanism because a + pull is the wrong signal, and because it cannot reload skills in a running session. It remains + the only option for a project that consumes apm packages without a Claude-family host. +- **Reporting instead of refreshing.** See the sub-decision above. +- **A seventh plugin holding only this hook**, to avoid shipping it to external kyberforge + consumers. Rejected as disproportionate: the `apm.lock.yaml` guard already makes the hook inert + for anyone not consuming through apm, and a package exists to be maintained, versioned, and + registered in the marketplace. diff --git a/docs/spec/architecture.md b/docs/spec/architecture.md index 2c4a4ad..72f299c 100644 --- a/docs/spec/architecture.md +++ b/docs/spec/architecture.md @@ -19,13 +19,13 @@ project repo (local overrides) - **Executables** (`DEPLOY_EXECUTABLES`): `providers/claude-code/statusline-command.sh` → `~/.claude/statusline-command.sh` (with `+x`) - **Directories** (`DEPLOY_DIRS`): `core/` → `~/.claude/core/` (destination fully replaced on each deploy) -Skills are **not** deployed by `install.sh`. They are distributed as plugins and installed separately via `claude plugin install @holocron`. +Skills are **not** deployed by `install.sh`. They are distributed as plugins and installed separately — in this repo by `apm install` against the `dependencies.apm` entries in the root `apm.yml`, which lands them in `.claude/skills/` and `.claude/agents/` (ADR-0018); elsewhere by `claude plugin install @holocron`. `~/.claude/CLAUDE.md` is a thin adapter, not a content source. It imports `~/.agents/AGENTS.md` (always-on rules) and `governance.md` (always-on governance), then lists the content index. All always-on content lives in `AGENTS.md` files so other providers can import the same source without duplication. ## Plugin model -Skills, agents, MCP servers, and hooks are distributed as self-contained plugin units under `plugins/`, installed independently via `claude plugin install @holocron`. Each plugin is an **apm package**: `plugins//apm.yml` plus a hand-authored `plugins//.apm/{skills,agents,hooks,commands,instructions,extensions}/` tree (ADR-0015). There is no hand-maintained `plugin.json` — every manifest and every host-visible content directory is compiled from that source. +Skills, agents, MCP servers, and hooks are distributed as self-contained plugin units under `plugins/`, installed independently — via `apm install` here, or `claude plugin install @holocron` for a host consuming the marketplace natively (ADR-0018). Each plugin is an **apm package**: `plugins//apm.yml` plus a hand-authored `plugins//.apm/{skills,agents,hooks,commands,instructions,extensions}/` tree (ADR-0015). There is no hand-maintained `plugin.json` — every manifest and every host-visible content directory is compiled from that source. Two compilers produce the plugin roots you see in the tree: @@ -63,4 +63,4 @@ This repo also has a `CLAUDE.md` at its root — the Claude Code entry point for ## Architectural decisions -Key hard-to-reverse decisions are recorded as ADRs in `docs/adr/`. There is no index file — the directory holds 17 numbered ADRs whose filenames state their decision, so `ls docs/adr/` is the index. Read a superseding ADR before the one it supersedes: ADR-0015 (apm as the authoring source of truth) supersedes ADR-0001 and moots ADR-0006, and ADR-0017 corrects ADR-0015's host-discovery gap. Entry points for the structure described on this page: ADR-0002 (two-tier CLAUDE.md), ADR-0003 (AGENTS.md as the provider-agnostic entry point), ADR-0015 and ADR-0017 (the two compilers behind the plugin roots). +Key hard-to-reverse decisions are recorded as ADRs in `docs/adr/`. There is no index file — the directory holds 19 numbered ADRs whose filenames state their decision, so `ls docs/adr/` is the index. Read a superseding ADR before the one it supersedes: ADR-0015 (apm as the authoring source of truth) supersedes ADR-0001 and moots ADR-0006, ADR-0017 corrects ADR-0015's host-discovery gap, and ADR-0019 supersedes one claim in ADR-0018 (that `.claude/settings.json`'s committed content is exactly `{"hooks": {}}`) while keeping the rule behind it. Entry points for the structure described on this page: ADR-0002 (two-tier CLAUDE.md), ADR-0003 (AGENTS.md as the provider-agnostic entry point), ADR-0015 and ADR-0017 (the two compilers behind the plugin roots). diff --git a/plugins/bin/.claude-plugin/plugin.json b/plugins/bin/.claude-plugin/plugin.json index 18e4804..7e40868 100644 --- a/plugins/bin/.claude-plugin/plugin.json +++ b/plugins/bin/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "bin", - "version": "1.1.2", + "version": "1.1.3", "description": "A place for things to be binned", "author": { "name": "Defame1297", @@ -20,7 +20,7 @@ "mcpServers": { "obsidian": { "args": [ - "@bitbonsai/mcpvault@latest", + "@bitbonsai/mcpvault@0.15.0", "docs/" ], "command": "npx", diff --git a/plugins/bin/.github/plugin/plugin.json b/plugins/bin/.github/plugin/plugin.json index 74e5109..c43b71d 100644 --- a/plugins/bin/.github/plugin/plugin.json +++ b/plugins/bin/.github/plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "bin", - "version": "1.1.2", + "version": "1.1.3", "description": "A place for things to be binned", "author": { "name": "Defame1297", diff --git a/plugins/bin/.mcp.json b/plugins/bin/.mcp.json index a1e4c58..7aa9712 100644 --- a/plugins/bin/.mcp.json +++ b/plugins/bin/.mcp.json @@ -2,7 +2,7 @@ "mcpServers": { "obsidian": { "args": [ - "@bitbonsai/mcpvault@latest", + "@bitbonsai/mcpvault@0.15.0", "docs/" ], "command": "npx", diff --git a/plugins/bin/README.md b/plugins/bin/README.md index 4ba6fd9..ec49fea 100644 --- a/plugins/bin/README.md +++ b/plugins/bin/README.md @@ -33,7 +33,7 @@ copilot plugin install ./plugins/bin | Component | Path | Description | |---|---|---| | Skills | `.apm/skills/` → `skills/` | Slash commands available after install | -| MCP servers | `.mcp.json` | The `obsidian` server (`npx @bitbonsai/mcpvault@latest docs/`), hand-authored at the plugin root | +| MCP servers | `.mcp.json` | The `obsidian` server (`npx @bitbonsai/mcpvault@0.15.0 docs/`), hand-authored at the plugin root | `.apm/` is the authoring source; `skills/` is the generated mirror plugin hosts scan (ADR-0017). This plugin ships no agents. It is the only plugin here with a non-empty `.mcp.json`, which is why its compiled manifests are the only ones carrying an `mcpServers` block. diff --git a/plugins/bin/apm.yml b/plugins/bin/apm.yml index 8499010..f9c8e65 100644 --- a/plugins/bin/apm.yml +++ b/plugins/bin/apm.yml @@ -1,5 +1,5 @@ name: bin -version: 1.1.2 +version: 1.1.3 description: A place for things to be binned author: name: Defame1297 diff --git a/plugins/kyberforge/.apm/hooks/check-apm-current.sh b/plugins/kyberforge/.apm/hooks/check-apm-current.sh new file mode 100755 index 0000000..ba989fd --- /dev/null +++ b/plugins/kyberforge/.apm/hooks/check-apm-current.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# SessionStart: keep an apm-consumed install level with its remote. +# +# Packages declared as unpinned git refs resolve against the remote default +# branch, so the deployed .claude/skills/ and .claude/agents/ go stale the +# moment anyone merges. The staleness bites when a session loads skills, which +# is why this runs at SessionStart rather than off a git hook — a pull is +# neither necessary nor sufficient for the install to have drifted. +# +# Refreshes in place and asks the host to re-scan, so the running session picks +# the new content up without a restart. +# +# Inert in any project that does not consume packages through apm. +set -uo pipefail + +# Anchor on the project root, not the session's cwd. Claude Code exports +# CLAUDE_PROJECT_DIR for SessionStart hooks; a session opened in a subdirectory +# would otherwise miss the lockfile, no-op silently, and — worse — run the apm +# calls below against that wrong directory. Fall back to the cwd when the +# variable is absent, which keeps the hook inert-but-harmless under a host that +# does not set it. +project_dir="${CLAUDE_PROJECT_DIR:-$PWD}" + +# No lockfile means nothing was installed through apm here — e.g. a host that +# installed this plugin natively. Say nothing and cost nothing. +[[ -f "$project_dir/apm.lock.yaml" ]] || exit 0 +command -v apm > /dev/null 2>&1 || exit 0 + +# Every apm call below must see the same directory the guard just checked — +# `apm outdated` and `apm update` both resolve the lockfile from the cwd. +cd "$project_dir" || exit 0 + +# `apm outdated` exits 0 whether or not anything is stale, so the answer has to +# come from its output. ~0.7s against six remote refs; a hung remote must not +# hold the session open. +# +# There is no --json/machine-readable flag on `apm outdated` (verified against +# apm 0.28.0), so the phrase match is forced rather than chosen. Note the +# singular: apm prints "1 outdated dependency found" when exactly one package is +# behind, so matching only "dependencies" would silently miss a one-package +# drift. tests/test-apm-current-hook.sh pins both spellings against the real apm. +outdated_output="$(timeout 60 apm outdated 2>&1)" || exit 0 +grep -qE 'outdated dependenc(y|ies) found' <<< "$outdated_output" || exit 0 + +stale_count="$(grep -oE '[0-9]+ outdated dependenc(y|ies) found' <<< "$outdated_output" | grep -oE '^[0-9]+' || true)" +[[ "$stale_count" =~ ^[0-9]+$ ]] || stale_count="some" + +# Only ever emit fixed text plus a digit-checked count — never interpolate +# command output into the JSON, which would need escaping this cannot do safely. +emit() { + printf '{"hookSpecificOutput":{"hookEventName":"SessionStart","reloadSkills":%s,"additionalContext":"%s"}}\n' "$1" "$2" +} + +if timeout 300 apm update --yes > /dev/null 2>&1; then + emit true "apm install was ${stale_count} package(s) behind the remote default branch and has been refreshed automatically; skills and agents were redeployed and re-scanned. apm.lock.yaml has been rewritten and is now a modified file in the working tree - commit it or discard it deliberately." +else + emit false "apm install is ${stale_count} package(s) behind the remote default branch and the automatic refresh failed. Deployed skills and agents may be stale. Run: apm update --yes" +fi + +exit 0 diff --git a/plugins/kyberforge/.apm/hooks/hooks.json b/plugins/kyberforge/.apm/hooks/hooks.json index deffac9..4d1c7c4 100644 --- a/plugins/kyberforge/.apm/hooks/hooks.json +++ b/plugins/kyberforge/.apm/hooks/hooks.json @@ -1,3 +1,16 @@ { - "hooks": {} + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "command": "${CLAUDE_PLUGIN_ROOT}/.apm/hooks/check-apm-current.sh", + "timeout": 380, + "type": "command" + } + ], + "matcher": "startup" + } + ] + } } diff --git a/plugins/kyberforge/.claude-plugin/plugin.json b/plugins/kyberforge/.claude-plugin/plugin.json index 0df9483..d668e6a 100644 --- a/plugins/kyberforge/.claude-plugin/plugin.json +++ b/plugins/kyberforge/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "kyberforge", - "version": "1.4.1", + "version": "1.5.0", "description": "Skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace.", "author": { "name": "Defame1297", diff --git a/plugins/kyberforge/.github/plugin/plugin.json b/plugins/kyberforge/.github/plugin/plugin.json index 0df9483..d668e6a 100644 --- a/plugins/kyberforge/.github/plugin/plugin.json +++ b/plugins/kyberforge/.github/plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "kyberforge", - "version": "1.4.1", + "version": "1.5.0", "description": "Skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace.", "author": { "name": "Defame1297", diff --git a/plugins/kyberforge/apm.yml b/plugins/kyberforge/apm.yml index 19e04b9..2ba3fe9 100644 --- a/plugins/kyberforge/apm.yml +++ b/plugins/kyberforge/apm.yml @@ -1,5 +1,5 @@ name: kyberforge -version: 1.4.1 +version: 1.5.0 description: Skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace. author: name: Defame1297 diff --git a/plugins/kyberforge/docs/hooks.md b/plugins/kyberforge/docs/hooks.md index 65fc4ea..66ddd9a 100644 --- a/plugins/kyberforge/docs/hooks.md +++ b/plugins/kyberforge/docs/hooks.md @@ -34,16 +34,82 @@ mirrored file; the `check-plugin-content-sync` pre-push hook reports it as drift } ``` -Events (**partial list**): `PreToolUse`, `PostToolUse`, `Notification`, `Stop`. Claude Code's plugin -hook set is larger — `SessionStart`, `SessionEnd`, `UserPromptSubmit`, `PreCompact` and -`SubagentStop` also exist — and this repo's vendored corpus does not enumerate it anywhere: -`docs/research/docs/claude-code-plugins/configuration.md:100` describes the file as "Event handlers -(PreToolUse, PostToolUse, etc.)", and `agent-definition.md:53` covers only the per-agent `hooks` -field, not the plugin-level set. Treat the four names above as the ones this repo has verified, not -as the schema. Check Claude Code's own hooks documentation before wiring an event not listed here. +Events (**partial list**): `PreToolUse`, `PostToolUse`, `Notification`, `Stop`, and `SessionStart` +(verified end-to-end by the hook below). Claude Code's plugin hook set is larger — `SessionEnd`, +`UserPromptSubmit`, `PreCompact` and `SubagentStop` also exist — and this repo's vendored corpus does +not enumerate it anywhere: `docs/research/docs/claude-code-plugins/configuration.md:100` describes +the file as "Event handlers (PreToolUse, PostToolUse, etc.)", and `agent-definition.md:53` covers +only the per-agent `hooks` field, not the plugin-level set. Treat the five names above as the ones +this repo has verified, not as the schema. Check Claude Code's own hooks documentation before wiring +an event not listed here. -Use `${CLAUDE_PLUGIN_ROOT}` to reference scripts inside this plugin — the plugin runs from a cache -path after install, not its original repo location. +## Referencing a script — use the `.apm/` path, not the mirror + +Use `${CLAUDE_PLUGIN_ROOT}` to reference scripts inside this plugin; the plugin runs from a cache or +`apm_modules/` path after install, not its original repo location. **Address the script at its +`.apm/` path:** + +```json +"command": "${CLAUDE_PLUGIN_ROOT}/.apm/hooks/check-apm-current.sh" +``` + +The obvious-looking `${CLAUDE_PLUGIN_ROOT}/hooks/check-apm-current.sh` does not work, and fails +quietly enough to be worth spelling out. apm resolves the placeholder against the installed package +root, where `hooks/` is the **generated mirror** — and `apm pack` merges only `*.json` out of +`.apm/hooks/`, dropping every non-JSON file. So the mirror contains `hooks.json` and nothing else. +apm prints `Hook script not found: .../hooks/check-apm-current.sh` and then deploys the hook anyway, +pointing at a path with no file behind it. + +Nor can the script be hand-placed in `plugins/kyberforge/hooks/` to satisfy that path: +`sync-plugin-content.sh` runs `rm -rf` on the directory before every rebuild (ADR-0017), so it would +be deleted on the next sync with no drift warning — the same trap that ate this document's +predecessor. + +`tests/test-apm-current-hook.sh` pins the reference so a well-meaning "simplification" back to +`hooks/` fails the suite rather than silently disabling the hook. + +## Deployed shape + +At install, apm merges the event bindings into `.claude/settings.json`, copies the referenced script +to `.claude/hooks//` (preserving its executable bit, preserving the `.apm/hooks/` subpath), and +rewrites `command` to a `${CLAUDE_PROJECT_DIR}`-relative path. Ownership of its own entries is +tracked in a `.claude/apm-hooks.json` sidecar, so an uninstall removes them without touching +hand-authored hooks. Both `.claude/hooks/` and the sidecar are gitignored install output. + +Note that apm's **executable-trust gate is off** unless the consuming project's `apm.yml` has an +`executables:` block — without one, package hooks deploy with no prompt. See ADR-0019. + +## The SessionStart hook + +`check-apm-current.sh` keeps an apm-consumed install level with its remote: it runs `apm outdated`, +and if anything is behind, runs `apm update --yes` and returns `reloadSkills: true` so the running +session picks up the redeployed content. Rationale, measurements, and the failure modes are in +ADR-0019. + +**Where it looks for the lockfile.** The hook resolves a project directory as `${CLAUDE_PROJECT_DIR}` +when the host exports it (Claude Code does, for SessionStart hooks) and the current directory +otherwise, then exits silently unless that directory holds an `apm.lock.yaml` — which is what makes +it inert for any host that installed this plugin natively rather than through apm. Both `apm` +invocations run against the same resolved directory. The earlier spelling checked a bare +`apm.lock.yaml` against the session's cwd, so a session opened in a subdirectory of an +apm-consuming repo no-opped silently. Keep the cwd fallback: a host that sets no +`CLAUDE_PROJECT_DIR` must still get inert-but-harmless behaviour, not an unset-variable error. + +**The `timeout` in `hooks.json` must exceed the script's own budget.** The script spends at most +`timeout 60 apm outdated` plus `timeout 300 apm update`; the hook entry declares `timeout: 380`, the +sum plus a buffer. Set it lower and a slow remote gets the hook SIGKILLed mid-`apm update`, leaving a +partially redeployed `.claude/skills/` and emitting no notice — precisely the silent failure the hook +exists to prevent. `tests/test-apm-current-hook.sh` pins the relationship (host timeout > sum of the +script's internal timeouts) rather than the literal, so raising either side alone fails the suite. + +**Staleness is detected by matching apm's summary line, and both spellings count.** `apm outdated` +has no `--json` or otherwise machine-readable output (verified against apm 0.28.0), so the hook +greps its text. apm prints `1 outdated dependency found` in the singular when exactly one package is +behind and `N outdated dependencies found` otherwise; matching only the plural silently misses a +one-package drift. Because a mocked `apm` would keep a reworded release invisible, the test suite +stages a genuinely outdated dependency against the **real** `apm` — a local git repo reached through +`url..insteadOf` rewrites, so it needs no network — and replays that genuine output through the +hook. ## GitHub Copilot CLI diff --git a/plugins/kyberforge/hooks/hooks.json b/plugins/kyberforge/hooks/hooks.json index deffac9..4d1c7c4 100644 --- a/plugins/kyberforge/hooks/hooks.json +++ b/plugins/kyberforge/hooks/hooks.json @@ -1,3 +1,16 @@ { - "hooks": {} + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "command": "${CLAUDE_PLUGIN_ROOT}/.apm/hooks/check-apm-current.sh", + "timeout": 380, + "type": "command" + } + ], + "matcher": "startup" + } + ] + } } diff --git a/scripts/check-executables-allow-sync.sh b/scripts/check-executables-allow-sync.sh new file mode 100755 index 0000000..632438e --- /dev/null +++ b/scripts/check-executables-allow-sync.sh @@ -0,0 +1,231 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Fails the push when root apm.yml's executables.allow key stops naming +# kyberforge's actual version. +# +# apm gates a package's hooks/ and bin/ on an EXACT dict lookup of +# "#" in executables.allow (apm_cli/security/executables.py, +# `allow_executables.get(package_key)`) — there is no wildcard and no +# version-less form. So bumping plugins/kyberforge/apm.yml's `version:` without +# bumping the key in root apm.yml does not error anywhere: the entry simply +# stops matching, kyberforge's SessionStart hook stops deploying, and the apm +# install goes quietly stale — the exact failure ADR-0019 records as live and +# mitigates only with a comment. Nothing else in the pre-push gate compares the +# two files, which is why this exists. +# +# Run from repo root or pass REPO_ROOT as arg. + +REPO_ROOT="${1:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}" +# A nonexistent REPO_ROOT fails loudly rather than falling through to the +# "no kyberforge plugin here" no-op below: that no-op is for a repo that +# legitimately does not ship the plugin, not for a typo'd or stale path, and +# exit 0 would read as "checked, in sync" when nothing was compared at all. +if [[ ! -d "$REPO_ROOT" ]]; then + echo "FAIL: executables-allow sync check: REPO_ROOT '$REPO_ROOT' is not a directory." >&2 + echo " Fix: run this from the repo root, or pass a real repo path as the first argument." >&2 + exit 1 +fi +REPO_ROOT="$(cd "$REPO_ROOT" && pwd)" + +PLUGIN_DIR="$REPO_ROOT/plugins/kyberforge" +PLUGIN_MANIFEST="$PLUGIN_DIR/apm.yml" +ROOT_MANIFEST="$REPO_ROOT/apm.yml" + +# Only a repo with no kyberforge plugin at all is a legitimate no-op — there is +# no version to pin and no hook to deploy. +if [[ ! -d "$PLUGIN_DIR" ]]; then + exit 0 +fi + +if [[ ! -f "$PLUGIN_MANIFEST" ]]; then + echo "FAIL: plugins/kyberforge/ exists but has no apm.yml, so its version cannot be read." >&2 + echo " Why: this gate compares that version against root apm.yml's executables.allow key;" >&2 + echo " without the manifest it would exit 0 having compared nothing." >&2 + echo " Fix: restore plugins/kyberforge/apm.yml, or remove plugins/kyberforge/ entirely." >&2 + exit 1 +fi + +if [[ ! -f "$ROOT_MANIFEST" ]]; then + echo "FAIL: root apm.yml is missing, so kyberforge's executables allow key cannot be verified." >&2 + echo " Why: apm reads executables.allow from the consuming package's manifest; with no root" >&2 + echo " manifest nothing grants kyberforge's hooks/ and bin/ permission to deploy." >&2 + echo " Fix: restore apm.yml at the repo root." >&2 + exit 1 +fi + +# --------------------------------------------------------------------------- +# Manifest facts +# --------------------------------------------------------------------------- +# Both readers emit the same line protocol, so the rest of the script does not +# care which ran: +# +# v exactly once +# apresent|absent exactly once — is executables.allow a mapping? +# k zero or more +# +# python3 + PyYAML is preferred where importable, because it is a real parser. +# It is deliberately NOT a hard requirement: no other hook in this repo's +# pre-push gate needs PyYAML, and making a version-pin check the one thing that +# can block every push on a missing pip package is a worse failure than reading +# two known shapes by hand. The fallback below is not a YAML parser — it +# recognises exactly the two shapes these manifests use and nothing else. + +read_facts_python() { + python3 - "$ROOT_MANIFEST" "$PLUGIN_MANIFEST" << 'PY' +import sys +import yaml + + +def load(path): + with open(path) as fh: + data = yaml.safe_load(fh) + return data if isinstance(data, dict) else {} + + +root, plugin = load(sys.argv[1]), load(sys.argv[2]) + +version = plugin.get("version") +# A 2-component version parses as a YAML float and would render back as e.g. +# "1.5" from 1.50 — a mismatch invented by the reader rather than found in the +# files. Refuse instead of guessing; semver keys here are always strings. +if version is not None and not isinstance(version, str): + sys.stderr.write( + "kyberforge's version: is not a string (%r) — quote it so the " + "executables.allow key can be compared verbatim.\n" % (version,) + ) + raise SystemExit(2) +print("v\t%s" % ("" if version is None else version)) + +executables = root.get("executables") +allow = executables.get("allow") if isinstance(executables, dict) else None +if isinstance(allow, dict): + print("a\tpresent") + for key in allow: + print("k\t%s" % key) +else: + print("a\tabsent") +PY +} + +# Strips one layer of matching quotes plus surrounding whitespace from a scalar. +unquote() { + local value="$1" + read -r value <<< "$value" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +read_facts_bash() { + local line version="" allow_state="absent" in_executables=0 in_allow=0 key + + while IFS= read -r line || [[ -n "$line" ]]; do + [[ "$line" == version:* ]] || continue + version="$(unquote "${line#version:}")" + break + done < "$PLUGIN_MANIFEST" + + while IFS= read -r line || [[ -n "$line" ]]; do + # Blank and full-line comments carry no structure at any depth. + case "$line" in + '' | '#'*) continue ;; + esac + # A top-level key (column 0) closes whatever block was open. Checked before + # anything else so `marketplace:` after `executables:` cannot leak keys in. + if [[ "$line" != [[:space:]]* ]]; then + if [[ "$line" == executables:* ]]; then + in_executables=1 + else + in_executables=0 + fi + in_allow=0 + continue + fi + [[ $in_executables -eq 1 ]] || continue + # 2-space indent: a key directly under executables:. `allow:` opens the + # mapping this gate reads; any sibling key closes it. + if [[ "$line" =~ ^\ \ [^[:space:]#] ]]; then + if [[ "$line" =~ ^\ \ allow: ]]; then + in_allow=1 + allow_state="present" + else + in_allow=0 + fi + continue + fi + # 4-space indent while inside allow: — an allow key. Keys contain '#' by + # construction ("kyberforge#1.5.0"), so a trailing-comment strip would eat + # them; there is none, and inline comments are not used on these lines. + if [[ $in_allow -eq 1 && "$line" =~ ^\ \ \ \ ([^[:space:]#][^:]*): ]]; then + key="$(unquote "${BASH_REMATCH[1]}")" + printf 'k\t%s\n' "$key" + fi + done < "$ROOT_MANIFEST" + + printf 'v\t%s\n' "$version" + printf 'a\t%s\n' "$allow_state" +} + +if command -v python3 > /dev/null 2>&1 && python3 -c 'import yaml' > /dev/null 2>&1; then + READER="python3 + PyYAML" + if ! FACTS="$(read_facts_python)"; then + echo "FAIL: could not read apm.yml / plugins/kyberforge/apm.yml (see the parser error above)." >&2 + echo " Why: this gate compares kyberforge's version against root apm.yml's executables.allow" >&2 + echo " key; an unreadable manifest means the comparison did not happen." >&2 + echo " Fix: make both manifests valid YAML, then re-run." >&2 + exit 1 + fi +else + READER="shape-scan fallback (PyYAML unavailable)" + FACTS="$(read_facts_bash)" +fi + +VERSION="$(printf '%s\n' "$FACTS" | sed -n 's/^v\t//p')" +ALLOW_STATE="$(printf '%s\n' "$FACTS" | sed -n 's/^a\t//p')" +ALLOW_KEYS="$(printf '%s\n' "$FACTS" | sed -n 's/^k\t//p')" + +if [[ -z "$VERSION" ]]; then + echo "FAIL: plugins/kyberforge/apm.yml declares no version:, so no allow key can be checked against it." >&2 + echo " Why: apm's executables.allow lookup is keyed on '#' exactly; with no" >&2 + echo " version there is nothing for root apm.yml's key to stay in sync with." >&2 + echo " Fix: give plugins/kyberforge/apm.yml a top-level version: (read by $READER)." >&2 + exit 1 +fi + +EXPECTED_KEY="kyberforge#$VERSION" + +if [[ "$ALLOW_STATE" != "present" ]]; then + echo "FAIL: root apm.yml has no executables.allow mapping, but plugins/kyberforge is version $VERSION." >&2 + echo " Why: without an allow entry apm refuses to deploy kyberforge's hooks/ and bin/, so the" >&2 + echo " SessionStart hook that keeps this install level with the remote never runs and the" >&2 + echo " deployed skills go stale silently (ADR-0019)." >&2 + echo " Fix: add to root apm.yml:" >&2 + echo " executables:" >&2 + echo " allow:" >&2 + echo " $EXPECTED_KEY:" >&2 + echo " hooks: true" >&2 + echo " bin: true" >&2 + exit 1 +fi + +if printf '%s\n' "$ALLOW_KEYS" | grep -qxF "$EXPECTED_KEY"; then + exit 0 +fi + +STALE_KEYS="$(printf '%s\n' "$ALLOW_KEYS" | grep '^kyberforge#' || true)" + +echo "FAIL: root apm.yml's executables.allow has no '$EXPECTED_KEY' key, but that is kyberforge's version." >&2 +if [[ -n "$STALE_KEYS" ]]; then + echo " Found instead:" >&2 + printf '%s\n' "$STALE_KEYS" | sed 's/^/ /' >&2 +fi +echo " Why: apm matches this key by exact dict lookup — there is no wildcard and no version-less" >&2 +echo " form — so a key naming any other version silently stops granting kyberforge's hooks/" >&2 +echo " and bin/. The SessionStart hook then stops deploying and the apm install goes stale" >&2 +echo " with no error anywhere (ADR-0019, 'The allow key is version-pinned')." >&2 +echo " Fix: bump the key in root apm.yml to '$EXPECTED_KEY:' — the version bump in" >&2 +echo " plugins/kyberforge/apm.yml is not complete without it." >&2 +exit 1 diff --git a/scripts/git-hooks/post-push b/scripts/git-hooks/post-push deleted file mode 100755 index 63c149e..0000000 --- a/scripts/git-hooks/post-push +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash -# Refresh the kyberforge plugin cache after every push -set -euo pipefail - -echo "→ Refreshing holocron marketplace clone..." -if git -C ~/.claude/plugins/marketplaces/holocron pull --quiet; then - echo "→ Updating kyberforge plugin cache..." - if claude plugin update kyberforge; then - echo "✓ kyberforge cache updated" - else - echo "⚠ claude plugin update kyberforge failed — run it manually" >&2 - fi -else - echo "⚠ Failed to pull holocron marketplace clone — run 'git -C ~/.claude/plugins/marketplaces/holocron pull' manually" >&2 -fi - -exit 0 diff --git a/tests/run-bats.sh b/tests/run-bats.sh index 1020cf1..a394504 100755 --- a/tests/run-bats.sh +++ b/tests/run-bats.sh @@ -21,6 +21,13 @@ fi # keeps the loop in this shell so the appends survive. `sort` is still fed # newline-delimited output, exactly as before. Same convention as # tests/run-tests.sh. +# +# apm_modules/ is excluded because `apm install` materializes a full copy of +# every dependency there — including this repo's own plugins, which it consumes +# from the holocron remote. Those copies carry their own .bats files whose +# relative paths (`$BATS_TEST_DIRNAME/../../../../../../`) resolve to the +# dependency's root, not this repo's, so they fail on a missing bats-support +# helper. They are the same tests already discovered under plugins/. TEST_FILES=() while IFS= read -r f; do TEST_FILES+=("$f") @@ -29,6 +36,7 @@ done < <( -not -path "*/tests/bats/*" \ -not -path "*/test_helper/*" \ -not -path "*/.claude/worktrees/*" \ + -not -path "*/apm_modules/*" \ | sort ) @@ -67,7 +75,7 @@ if [[ -n "$GIT_TOPLEVEL" && "$GIT_TOPLEVEL" == "$REPO_ROOT" ]]; then [[ -n "$f" ]] && EXPECTED_FILES+=("$REPO_ROOT/$f") done < <( git -C "$REPO_ROOT" ls-files -- '*.bats' \ - | grep -Ev '(^|/)tests/bats/|(^|/)test_helper/|(^|/)\.claude/worktrees/' \ + | grep -Ev '(^|/)tests/bats/|(^|/)test_helper/|(^|/)\.claude/worktrees/|(^|/)apm_modules/' \ | sort || true ) else diff --git a/tests/run-tests.sh b/tests/run-tests.sh index 4161ab7..9aa3152 100755 --- a/tests/run-tests.sh +++ b/tests/run-tests.sh @@ -119,6 +119,10 @@ run_bats # /bin/bash 3.2, which has no `mapfile`. Process substitution (not a pipe) # keeps the loop in this shell so the appends survive. `sort` is still fed # newline-delimited output, exactly as before. +# +# apm_modules/ is excluded for the same reason tests/run-bats.sh excludes it: +# `apm install` materializes dependency copies of this repo's own plugins there, +# and re-running a dependency's tests re-runs what plugins/ already covers. SCRIPTS=() while IFS= read -r script; do SCRIPTS+=("$script") @@ -126,6 +130,7 @@ done < <( find "$SEARCH_ROOT" -name "test-*.sh" \ -not -path "*/.git/*" \ -not -path "*/.claude/worktrees/*" \ + -not -path "*/apm_modules/*" \ | sort ) diff --git a/tests/test-apm-current-hook.sh b/tests/test-apm-current-hook.sh new file mode 100755 index 0000000..631270d --- /dev/null +++ b/tests/test-apm-current-hook.sh @@ -0,0 +1,368 @@ +#!/usr/bin/env bash +# Tests for plugins/kyberforge/.apm/hooks/check-apm-current.sh — the SessionStart +# hook that keeps an apm-consumed install level with its remote. +# +# `apm` is mocked throughout: the hook's contract is "read `apm outdated`, decide, +# emit SessionStart JSON", and that is testable without a network or a real +# install. +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +HOOK="$REPO_ROOT/plugins/kyberforge/.apm/hooks/check-apm-current.sh" +HOOKS_JSON="$REPO_ROOT/plugins/kyberforge/.apm/hooks/hooks.json" +PASS=0 +FAIL=0 + +pass() { echo " PASS: $1"; PASS=$((PASS + 1)); } +fail() { echo " FAIL: $1"; FAIL=$((FAIL + 1)); } + +command -v python3 > /dev/null 2>&1 || { echo "python3 required"; exit 77; } + +FAKE_BIN="$(mktemp -d)" +WORK="$(mktemp -d)" +trap 'rm -rf "$FAKE_BIN" "$WORK"' EXIT + +# Mock `apm`. $1 chooses what `apm outdated` reports; $2 the exit code of +# `apm update`. Sentinel files record whether update was actually invoked and +# which directory the calls ran in — the hook must run them against the same +# directory its lockfile guard checked, not the session's cwd. +make_apm() { + local outdated_line="$1" update_exit="$2" + cat > "$FAKE_BIN/apm" << EOF +#!/usr/bin/env bash +pwd > "$WORK/apm-cwd" +case "\$1" in + outdated) echo "$outdated_line"; exit 0 ;; + update) touch "$WORK/update-was-called"; exit $update_exit ;; +esac +exit 0 +EOF + chmod +x "$FAKE_BIN/apm" +} + +# CLAUDE_PROJECT_DIR is cleared rather than merely left alone: a session in this +# repo exports it, and an inherited value would point every case at the real +# repo root (which has a real apm.lock.yaml) instead of the fixture. The +# project-directory cases below set it deliberately. +run_hook() { (cd "$WORK" && env -u CLAUDE_PROJECT_DIR PATH="$FAKE_BIN:$PATH" bash "$HOOK" 2>/dev/null); } + +# Same, with an explicit cwd and CLAUDE_PROJECT_DIR. $1 is the cwd; $2 the value +# for CLAUDE_PROJECT_DIR, or the literal `-` to leave it unset. +run_hook_in() { + local cwd="$1" project_dir="$2" + if [[ "$project_dir" == "-" ]]; then + (cd "$cwd" && env -u CLAUDE_PROJECT_DIR PATH="$FAKE_BIN:$PATH" bash "$HOOK" 2>/dev/null) + else + (cd "$cwd" && env CLAUDE_PROJECT_DIR="$project_dir" PATH="$FAKE_BIN:$PATH" bash "$HOOK" 2>/dev/null) + fi +} + +json_field() { python3 -c 'import json,sys; print(json.load(sys.stdin)["hookSpecificOutput"][sys.argv[1]])' "$1"; } + +# --------------------------------------------------------------------------- +echo "--- inert without an apm-consumed install ---" +# --------------------------------------------------------------------------- + +make_apm "[!] 6 outdated dependencies found" 0 +rm -f "$WORK/apm.lock.yaml" "$WORK/update-was-called" +out="$(run_hook)"; rc=$? +[[ $rc -eq 0 ]] && pass "exits 0 with no apm.lock.yaml" || fail "should exit 0 with no apm.lock.yaml" +[[ -z "$out" ]] && pass "emits nothing with no apm.lock.yaml" || fail "should stay silent with no apm.lock.yaml" +[[ ! -f "$WORK/update-was-called" ]] && pass "does not run apm update with no apm.lock.yaml" \ + || fail "must not touch a project that does not use apm" + +# A native (non-apm) install of this plugin hits exactly this path, so it is the +# guard that keeps the hook from acting on someone else's repo. +touch "$WORK/apm.lock.yaml" + +# --------------------------------------------------------------------------- +echo "" +echo "--- inert when apm is absent ---" +# --------------------------------------------------------------------------- + +rm -f "$WORK/update-was-called" +out="$( (cd "$WORK" && PATH="$(dirname "$(command -v bash)")" bash "$HOOK" 2>/dev/null) )"; rc=$? +[[ $rc -eq 0 ]] && pass "exits 0 when apm is not on PATH" || fail "should exit 0 when apm is missing" +[[ -z "$out" ]] && pass "emits nothing when apm is not on PATH" || fail "should stay silent when apm is missing" + +# --------------------------------------------------------------------------- +echo "" +echo "--- install already current ---" +# --------------------------------------------------------------------------- + +make_apm "[*] All dependencies are up-to-date" 0 +rm -f "$WORK/update-was-called" +out="$(run_hook)"; rc=$? +[[ $rc -eq 0 ]] && pass "exits 0 when current" || fail "should exit 0 when current" +[[ -z "$out" ]] && pass "emits nothing when current" || fail "should stay silent when current" +[[ ! -f "$WORK/update-was-called" ]] && pass "does not run apm update when current" \ + || fail "must not update when nothing is stale" + +# --------------------------------------------------------------------------- +echo "" +echo "--- stale, refresh succeeds ---" +# --------------------------------------------------------------------------- + +make_apm "[!] 6 outdated dependencies found" 0 +rm -f "$WORK/update-was-called" +out="$(run_hook)"; rc=$? +[[ $rc -eq 0 ]] && pass "exits 0 when stale" || fail "should exit 0 when stale" +[[ -f "$WORK/update-was-called" ]] && pass "runs apm update when stale" || fail "should run apm update when stale" +if echo "$out" | python3 -m json.tool > /dev/null 2>&1; then + pass "emits valid JSON" + [[ "$(echo "$out" | json_field hookEventName)" == "SessionStart" ]] \ + && pass "declares hookEventName SessionStart" || fail "wrong hookEventName" + [[ "$(echo "$out" | json_field reloadSkills)" == "True" ]] \ + && pass "asks the host to reload skills after a successful refresh" || fail "reloadSkills should be true" + echo "$out" | json_field additionalContext | grep -q "6 package" \ + && pass "reports the stale package count" || fail "should report the count" + # The lockfile rewrite is the surprising part of auto-updating; the notice has + # to say so or a dirty worktree looks like something else went wrong. + echo "$out" | json_field additionalContext | grep -q "apm.lock.yaml" \ + && pass "warns that apm.lock.yaml was rewritten" || fail "should warn about the lockfile rewrite" +else + fail "emits valid JSON" +fi + +# --------------------------------------------------------------------------- +echo "" +echo "--- stale, refresh fails ---" +# --------------------------------------------------------------------------- + +make_apm "[!] 3 outdated dependencies found" 1 +out="$(run_hook)"; rc=$? +[[ $rc -eq 0 ]] && pass "exits 0 when the refresh fails" || fail "must never fail the session start" +if echo "$out" | python3 -m json.tool > /dev/null 2>&1; then + pass "emits valid JSON on failure" + [[ "$(echo "$out" | json_field reloadSkills)" == "False" ]] \ + && pass "does not ask for a skill reload when nothing was deployed" || fail "reloadSkills should be false" + echo "$out" | json_field additionalContext | grep -q "apm update" \ + && pass "tells the reader how to refresh by hand" || fail "should name the manual command" +else + fail "emits valid JSON on failure" +fi + +# --------------------------------------------------------------------------- +echo "" +echo "--- unparseable count degrades instead of breaking the JSON ---" +# --------------------------------------------------------------------------- + +make_apm "[!] lots of outdated dependencies found" 0 +out="$(run_hook)" +echo "$out" | python3 -m json.tool > /dev/null 2>&1 \ + && pass "still emits valid JSON when the count cannot be parsed" || fail "JSON broke on an unparseable count" + +# --------------------------------------------------------------------------- +echo "" +echo "--- anchors on the project root, not the session cwd ---" +# --------------------------------------------------------------------------- + +# `[[ -f apm.lock.yaml ]]` resolves against the cwd, and a session started in a +# subdirectory of an apm-consuming repo therefore no-opped silently — and would +# have run `apm outdated`/`apm update` against that wrong directory had the +# guard passed. Claude Code exports CLAUDE_PROJECT_DIR for SessionStart hooks, +# so that is the anchor; the cwd is only the fallback. +ELSEWHERE="$WORK/elsewhere" +mkdir -p "$ELSEWHERE" +rm -f "$ELSEWHERE/apm.lock.yaml" + +make_apm "[!] 6 outdated dependencies found" 0 +rm -f "$WORK/update-was-called" "$WORK/apm-cwd" +out="$(run_hook_in "$ELSEWHERE" "$WORK")" +[[ -f "$WORK/update-was-called" ]] \ + && pass "finds the lockfile via CLAUDE_PROJECT_DIR when the cwd is elsewhere" \ + || fail "a session started in a subdirectory must still see the project's lockfile" +[[ "$(cat "$WORK/apm-cwd" 2>/dev/null)" == "$WORK" ]] \ + && pass "runs apm in the directory the guard checked, not the cwd" \ + || fail "apm ran in '$(cat "$WORK/apm-cwd" 2>/dev/null)' — must run in the resolved project directory" +echo "$out" | json_field additionalContext | grep -q "6 package" \ + && pass "reports the count found via CLAUDE_PROJECT_DIR" || fail "should report the count" + +# The fallback is not cosmetic: a host that installed this plugin natively sets +# no CLAUDE_PROJECT_DIR, and the hook must stay inert-but-harmless there rather +# than erroring on an unset variable (the script runs under `set -u`). +rm -f "$WORK/update-was-called" "$WORK/apm-cwd" +out="$(run_hook_in "$WORK" "-")" +[[ -f "$WORK/update-was-called" ]] \ + && pass "falls back to the cwd when CLAUDE_PROJECT_DIR is unset" \ + || fail "must still work with no CLAUDE_PROJECT_DIR in the environment" +[[ "$(cat "$WORK/apm-cwd" 2>/dev/null)" == "$WORK" ]] \ + && pass "runs apm in the cwd under the fallback" \ + || fail "apm ran in '$(cat "$WORK/apm-cwd" 2>/dev/null)' — should be the cwd" + +rm -f "$WORK/update-was-called" "$WORK/apm-cwd" +out="$(run_hook_in "$ELSEWHERE" "$ELSEWHERE")"; rc=$? +[[ $rc -eq 0 ]] && pass "exits 0 when neither the project dir nor the cwd has a lockfile" \ + || fail "should exit 0 when there is no lockfile anywhere" +[[ -z "$out" ]] && pass "stays silent when neither the project dir nor the cwd has a lockfile" \ + || fail "should stay silent when there is no lockfile anywhere" +[[ ! -f "$WORK/update-was-called" ]] \ + && pass "does not run apm update when there is no lockfile anywhere" \ + || fail "must not touch a project that does not use apm" + +# --------------------------------------------------------------------------- +echo "" +echo "--- hooks.json wiring ---" +# --------------------------------------------------------------------------- + +# apm resolves script paths relative to the package root, and `apm pack` keeps +# only *.json from .apm/hooks/ — so a ${CLAUDE_PLUGIN_ROOT}/hooks/... reference +# points at a directory the script never reaches. It must be .apm/-relative. +referenced="$(python3 -c 'import json,sys; d=json.load(open(sys.argv[1])); print(d["hooks"]["SessionStart"][0]["hooks"][0]["command"])' "$HOOKS_JSON")" +[[ "$referenced" == '${CLAUDE_PLUGIN_ROOT}/.apm/hooks/check-apm-current.sh' ]] \ + && pass "hooks.json references the script at its .apm/ path" \ + || fail "hooks.json references '$referenced' — must be \${CLAUDE_PLUGIN_ROOT}/.apm/hooks/check-apm-current.sh" + +[[ -x "$HOOK" ]] && pass "hook script is executable" || fail "hook script must be executable" + +matcher="$(python3 -c 'import json,sys; d=json.load(open(sys.argv[1])); print(d["hooks"]["SessionStart"][0]["matcher"])' "$HOOKS_JSON")" +[[ "$matcher" == "startup" ]] && pass "fires on startup only" \ + || fail "matcher is '$matcher' — resume/clear/compact would re-run this every compaction" + +# The host's timeout must strictly exceed everything the script can spend, or +# the host SIGKILLs the hook mid-`apm update` and leaves a half-redeployed +# .claude/skills/ with no notice emitted — the silent failure this hook exists +# to prevent. Asserted as an invariant over both files rather than against a +# literal, so raising either internal `timeout` without raising the host budget +# fails here instead of reintroducing the gap quietly. +# +# Every `timeout N` in the script counts, comments included: a stray "timeout +# 300" in prose only makes this stricter, which is the safe direction. +script_budget=0 +timeout_count=0 +while read -r n; do + [[ -n "$n" ]] || continue + script_budget=$((script_budget + n)) + timeout_count=$((timeout_count + 1)) +done < <(grep -oE '\btimeout [0-9]+\b' "$HOOK" | grep -oE '[0-9]+') + +hook_timeout="$(python3 -c 'import json,sys; d=json.load(open(sys.argv[1])); print(d["hooks"]["SessionStart"][0]["hooks"][0]["timeout"])' "$HOOKS_JSON")" + +if [[ $timeout_count -eq 0 ]]; then + fail "found no 'timeout N' in $HOOK — the budget assertion below would be vacuous" +else + pass "parsed $timeout_count internal timeout(s) totalling ${script_budget}s from the hook script" + [[ $hook_timeout -gt $script_budget ]] \ + && pass "hooks.json timeout (${hook_timeout}s) exceeds the script's own budget (${script_budget}s)" \ + || fail "hooks.json timeout is ${hook_timeout}s but the script can spend ${script_budget}s — the host would SIGKILL it mid-update" +fi + +# --------------------------------------------------------------------------- +echo "" +echo "--- the greped phrase, against the real apm ---" +# --------------------------------------------------------------------------- + +# Everything above mocks `apm`, so an apm release that reworded its summary line +# would revert this hook to its pre-fix behaviour with a fully green suite. +# `apm outdated` has no --json/machine-readable flag (verified against 0.28.0), +# so the phrase match cannot be replaced — it can only be pinned. +# +# The probe stages a genuinely outdated dependency with no network: a local git +# repo stands in for the upstream, reached through `url..insteadOf` +# rewrites of every URL spelling apm may build (it picks ssh or https depending +# on ambient auth config, so all three are mapped). apm appends `.git` to the +# repo URL, which is why the local repo is named `upstream.git` and the rewrite +# target omits the suffix. HOME is redirected so no user-level apm cache or +# credential state can influence the result. +# +# The genuine output is then replayed into the real hook through the mock, so +# what is asserted is the hook's own matching logic against real apm text — +# no pattern is duplicated here to drift out of sync. +SKIP_REASON="" +if ! command -v apm > /dev/null 2>&1 || ! command -v git > /dev/null 2>&1; then + SKIP_REASON="SKIP: apm and git are both required to verify the hook's phrase match against real \`apm outdated\` output — everything above ran, this axis did not" + echo " $SKIP_REASON" +else + PROBE="$(mktemp -d)" + trap 'rm -rf "$FAKE_BIN" "$WORK" "$PROBE"' EXIT + + UPSTREAM="$PROBE/upstream.git" + git init -q "$UPSTREAM" + git -C "$UPSTREAM" -c user.email=probe@example.invalid -c user.name=probe \ + commit -q --allow-empty -m one + LOCKED_SHA="$(git -C "$UPSTREAM" rev-parse HEAD)" + git -C "$UPSTREAM" -c user.email=probe@example.invalid -c user.name=probe \ + commit -q --allow-empty -m two + BRANCH="$(git -C "$UPSTREAM" symbolic-ref --short HEAD)" + + { + for repo in alpha beta; do + printf '[url "%s/upstream"]\n' "$PROBE" + printf '\tinsteadOf = git@apm-probe.invalid:probe/%s\n' "$repo" + printf '\tinsteadOf = https://apm-probe.invalid/probe/%s\n' "$repo" + printf '\tinsteadOf = ssh://git@apm-probe.invalid/probe/%s\n' "$repo" + done + } > "$PROBE/gitconfig" + + mkdir -p "$PROBE/consumer" "$PROBE/home" + + # $1 = how many stale dependencies to stage. Writes a lockfile and echoes what + # the real `apm outdated` printed for it. + real_apm_outdated() { + local want="$1" repo + { + echo "lockfile_version: '1'" + echo "generated_at: '2026-01-01T00:00:00+00:00'" + echo "apm_version: 0.0.0" + echo "dependencies:" + for repo in $( [[ "$want" == 1 ]] && echo alpha || echo alpha beta ); do + echo "- host: apm-probe.invalid" + echo " name: probe-$repo" + echo " package_type: apm_package" + echo " repo_url: probe/$repo" + echo " resolved_ref: $BRANCH" + echo " resolved_commit: $LOCKED_SHA" + echo " version: 1.0.0" + done + echo "deployments: []" + } > "$PROBE/consumer/apm.lock.yaml" + ( + cd "$PROBE/consumer" && + env HOME="$PROBE/home" \ + GIT_CONFIG_GLOBAL="$PROBE/gitconfig" \ + GIT_CONFIG_NOSYSTEM=1 \ + GIT_TERMINAL_PROMPT=0 \ + apm outdated 2>&1 + ) + } + + # Replay genuine output through the hook. A harness that stages nothing would + # make every assertion below vacuously true, so the staged row is checked + # first and a failure to stage is a FAIL, not a quiet pass. + for want in 1 2; do + genuine="$(real_apm_outdated "$want" || true)" + if ! grep -q "outdated" <<< "$genuine"; then + fail "probe staged no outdated dependency against the real apm (harness broken, not the hook): $(tr '\n' ' ' <<< "$genuine" | cut -c1-160)" + continue + fi + printf '%s\n' "$genuine" > "$PROBE/genuine-$want.txt" + cat > "$FAKE_BIN/apm" << EOF +#!/usr/bin/env bash +pwd > "$WORK/apm-cwd" +case "\$1" in + outdated) cat "$PROBE/genuine-$want.txt"; exit 0 ;; + update) touch "$WORK/update-was-called"; exit 0 ;; +esac +exit 0 +EOF + chmod +x "$FAKE_BIN/apm" + rm -f "$WORK/update-was-called" + out="$(run_hook)" + if [[ -n "$out" ]] && echo "$out" | json_field additionalContext 2>/dev/null | grep -q "$want package"; then + pass "detects staleness in real \`apm outdated\` output and counts $want package(s)" + else + fail "real apm reported $want outdated dependency/dependencies but the hook did not act on it — apm reworded its summary line. Real output: $(tr '\n' ' ' < "$PROBE/genuine-$want.txt" | tail -c 120)" + fi + done +fi + +# --------------------------------------------------------------------------- +echo "" +echo "Results: $PASS passed, $FAIL failed" +[[ $FAIL -eq 0 ]] || exit 1 +# A skip only after everything runnable has run and passed: the mocked axis is +# still worth executing on a machine without apm, but the suite must not read +# as green when the real-apm axis was not verified. run-tests.sh reports 77 as +# SKIPPED and, at pre-push (--strict), as a setup error naming this reason. +[[ -z "$SKIP_REASON" ]] || exit 77 +exit 0 diff --git a/tests/test-check-executables-allow-sync.sh b/tests/test-check-executables-allow-sync.sh new file mode 100644 index 0000000..6dd7c3a --- /dev/null +++ b/tests/test-check-executables-allow-sync.sh @@ -0,0 +1,243 @@ +#!/usr/bin/env bash +# Tests for scripts/check-executables-allow-sync.sh — the pre-push gate that +# keeps root apm.yml's executables.allow key level with kyberforge's version. +# +# Fixtures are two-file skeletons (root apm.yml + plugins/kyberforge/apm.yml) +# rather than copies of the real repo: the gate reads exactly those two files, +# and a hand-built fixture is the only way to construct the drift it exists to +# catch without editing the real manifests. +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +SCRIPT="$REPO_ROOT/scripts/check-executables-allow-sync.sh" +PASS=0 +FAIL=0 + +pass() { echo " PASS: $1"; PASS=$((PASS + 1)); } +fail() { echo " FAIL: $1"; FAIL=$((FAIL + 1)); } + +# Same `exit 77` (automake convention; run-tests.sh renders it as SKIPPED) guard +# the vale suites use. The script itself runs fine without python3 — it falls +# back to a shape scan — but this suite asserts BOTH readers agree, and the +# PyYAML path cannot be exercised at all on a machine without it. Reporting +# those cases as failures would say "a regression landed" when the truth is +# "this machine is missing a dev dependency". +command -v python3 > /dev/null 2>&1 || { echo "SKIP: python3 is required to exercise the PyYAML reader"; exit 77; } +python3 -c 'import yaml' > /dev/null 2>&1 || { echo "SKIP: PyYAML is required to exercise the PyYAML reader"; exit 77; } + +FIXTURES=() +cleanup() { [[ ${#FIXTURES[@]} -eq 0 ]] || rm -rf "${FIXTURES[@]}"; } +trap cleanup EXIT + +# make_fixture +# The executables block is passed verbatim (may be empty) so a fixture can omit +# it entirely, which is one of the failure modes under test. +make_fixture() { + local version_line="$1" executables_block="$2" dir + dir="$(mktemp -d)" + FIXTURES+=("$dir") + mkdir -p "$dir/plugins/kyberforge" + + { + echo "name: kyberforge" + echo "$version_line" + echo "description: fixture" + } > "$dir/plugins/kyberforge/apm.yml" + + { + echo "name: ai-development" + echo "version: 0.0.1" + echo "dependencies:" + echo " apm:" + echo " - name: kyberforge" + [[ -n "$executables_block" ]] && printf '%s\n' "$executables_block" + # A top-level key after the block: the fallback reader must stop collecting + # allow keys here rather than reading on into the next section. + echo "marketplace:" + echo " owner:" + echo " name: fixture" + } > "$dir/apm.yml" + + printf '%s\n' "$dir" +} + +MATCHING_BLOCK='executables: + allow: + kyberforge#1.5.0: + hooks: true + bin: true' + +STALE_BLOCK='executables: + allow: + kyberforge#1.4.0: + hooks: true + bin: true' + +OTHER_PACKAGE_BLOCK='executables: + allow: + git#1.0.0: + hooks: true' + +# run_gate — echoes combined output, sets GATE_RC. +GATE_RC=0 +run_gate() { + GATE_RC=0 + bash "$SCRIPT" "$1" > /dev/null 2>&1 || GATE_RC=$? +} + +# --------------------------------------------------------------------------- +echo "--- the real repo passes ---" +# --------------------------------------------------------------------------- + +# The gate's whole value is that it is green on a correct tree and red on drift; +# a version bump landing in only one of the two real manifests must show up here. +run_gate "$REPO_ROOT" +[[ $GATE_RC -eq 0 ]] && pass "current repo state passes" \ + || fail "current repo state should pass — the gate said: $(bash "$SCRIPT" "$REPO_ROOT" 2>&1 | head -3)" + +# --------------------------------------------------------------------------- +echo "" +echo "--- matching version ---" +# --------------------------------------------------------------------------- + +F="$(make_fixture "version: 1.5.0" "$MATCHING_BLOCK")" +run_gate "$F" +[[ $GATE_RC -eq 0 ]] && pass "exits 0 when the allow key names the plugin's version" \ + || fail "should pass when key and version agree, got rc=$GATE_RC" + +F="$(make_fixture 'version: "1.5.0"' "$MATCHING_BLOCK")" +run_gate "$F" +[[ $GATE_RC -eq 0 ]] && pass "exits 0 when the version is quoted" \ + || fail "a quoted version must compare the same as an unquoted one, got rc=$GATE_RC" + +# --------------------------------------------------------------------------- +echo "" +echo "--- mismatched version ---" +# --------------------------------------------------------------------------- + +F="$(make_fixture "version: 1.5.0" "$STALE_BLOCK")" +run_gate "$F" +[[ $GATE_RC -ne 0 ]] && pass "fails when the allow key names a different version" \ + || fail "a stale allow key must fail the push" +OUT="$(bash "$SCRIPT" "$F" 2>&1 || true)" +grep -q "kyberforge#1.5.0" <<< "$OUT" && pass "names the key that should be there" \ + || fail "the failure must state the expected key" +grep -q "kyberforge#1.4.0" <<< "$OUT" && pass "names the stale key it found instead" \ + || fail "the failure must quote back the stale key" +grep -q "Why:" <<< "$OUT" && grep -q "Fix:" <<< "$OUT" && pass "uses the FAIL/Why/Fix message block" \ + || fail "message must carry Why: and Fix: lines" + +# --------------------------------------------------------------------------- +echo "" +echo "--- missing executables.allow ---" +# --------------------------------------------------------------------------- + +F="$(make_fixture "version: 1.5.0" "")" +run_gate "$F" +[[ $GATE_RC -ne 0 ]] && pass "fails when there is no executables block at all" \ + || fail "a missing executables.allow must fail — apm deploys no hooks without it" +OUT="$(bash "$SCRIPT" "$F" 2>&1 || true)" +grep -q "executables:" <<< "$OUT" && grep -q "kyberforge#1.5.0" <<< "$OUT" \ + && pass "shows the block to add" || fail "the failure must show the block to add" + +# An `executables:` key that is not a mapping is the same hole as no key at all. +F="$(make_fixture "version: 1.5.0" "executables:")" +run_gate "$F" +[[ $GATE_RC -ne 0 ]] && pass "fails when executables: exists but allow: does not" \ + || fail "an empty executables: block grants nothing and must fail" + +# --------------------------------------------------------------------------- +echo "" +echo "--- allow present, kyberforge key missing ---" +# --------------------------------------------------------------------------- + +F="$(make_fixture "version: 1.5.0" "$OTHER_PACKAGE_BLOCK")" +run_gate "$F" +[[ $GATE_RC -ne 0 ]] && pass "fails when allow: exists but names no kyberforge key" \ + || fail "an allow block covering only other packages must still fail" + +# --------------------------------------------------------------------------- +echo "" +echo "--- degenerate inputs fail loudly rather than passing silently ---" +# --------------------------------------------------------------------------- + +run_gate "$REPO_ROOT/definitely-not-a-directory" +[[ $GATE_RC -ne 0 ]] && pass "fails on a nonexistent REPO_ROOT" \ + || fail "a bad path must not exit 0 — that reads as 'checked, in sync'" + +# No kyberforge plugin at all is the one legitimate no-op: nothing to pin. +NO_PLUGIN="$(mktemp -d)"; FIXTURES+=("$NO_PLUGIN") +echo "name: someone-else" > "$NO_PLUGIN/apm.yml" +run_gate "$NO_PLUGIN" +[[ $GATE_RC -eq 0 ]] && pass "no-ops in a repo with no kyberforge plugin" \ + || fail "a repo without plugins/kyberforge/ has nothing to check" + +# ...but a kyberforge directory with no manifest is drift, not a no-op. +mkdir -p "$NO_PLUGIN/plugins/kyberforge" +run_gate "$NO_PLUGIN" +[[ $GATE_RC -ne 0 ]] && pass "fails when plugins/kyberforge/ has no apm.yml" \ + || fail "a plugin dir with no manifest must not silently pass" + +F="$(make_fixture "description: no version here" "$MATCHING_BLOCK")" +run_gate "$F" +[[ $GATE_RC -ne 0 ]] && pass "fails when the plugin manifest declares no version" \ + || fail "no version means nothing to compare — must fail, not pass" + +# --------------------------------------------------------------------------- +echo "" +echo "--- the fallback reader agrees with the PyYAML reader ---" +# --------------------------------------------------------------------------- + +# PyYAML is deliberately not a hard dependency of this gate (no other pre-push +# hook needs it), so the shape-scan fallback carries the same verdicts. Masking +# is done with a python3 stub whose `import yaml` fails, which is the exact +# condition on a machine that has python3 without PyYAML. +NO_YAML_BIN="$(mktemp -d)"; FIXTURES+=("$NO_YAML_BIN") +printf '#!/usr/bin/env bash\nexit 1\n' > "$NO_YAML_BIN/python3" +chmod +x "$NO_YAML_BIN/python3" + +run_fallback() { + GATE_RC=0 + PATH="$NO_YAML_BIN:$PATH" bash "$SCRIPT" "$1" > /dev/null 2>&1 || GATE_RC=$? +} + +F="$(make_fixture "version: 1.5.0" "$MATCHING_BLOCK")" +run_fallback "$F" +[[ $GATE_RC -eq 0 ]] && pass "fallback passes a matching fixture" || fail "fallback should pass when in sync" + +F="$(make_fixture 'version: "1.5.0"' "$MATCHING_BLOCK")" +run_fallback "$F" +[[ $GATE_RC -eq 0 ]] && pass "fallback strips quotes from the version" || fail "fallback mishandled a quoted version" + +F="$(make_fixture "version: 1.5.0" "$STALE_BLOCK")" +run_fallback "$F" +[[ $GATE_RC -ne 0 ]] && pass "fallback fails a stale key" || fail "fallback missed a stale key" + +F="$(make_fixture "version: 1.5.0" "")" +run_fallback "$F" +[[ $GATE_RC -ne 0 ]] && pass "fallback fails a missing executables block" || fail "fallback missed a missing block" + +F="$(make_fixture "version: 1.5.0" "$OTHER_PACKAGE_BLOCK")" +run_fallback "$F" +[[ $GATE_RC -ne 0 ]] && pass "fallback fails when no kyberforge key is present" || fail "fallback missed an absent key" + +run_fallback "$REPO_ROOT" +[[ $GATE_RC -eq 0 ]] && pass "fallback passes the real repo" || fail "fallback disagrees with PyYAML on the real repo" + +# --------------------------------------------------------------------------- +echo "" +echo "--- wired into the pre-push gate ---" +# --------------------------------------------------------------------------- + +# A gate nobody runs is not a gate; this is the only assertion that the script +# is actually reachable from `git push`. +CONFIG="$REPO_ROOT/.pre-commit-config.yaml" +grep -q "id: check-executables-allow-sync" "$CONFIG" \ + && pass ".pre-commit-config.yaml declares the hook" || fail "hook is not declared in .pre-commit-config.yaml" +grep -q "scripts/check-executables-allow-sync.sh" "$CONFIG" \ + && pass ".pre-commit-config.yaml points at the script" || fail "hook does not reference the script path" + +# --------------------------------------------------------------------------- +echo "" +echo "Results: $PASS passed, $FAIL failed" +[[ $FAIL -eq 0 ]] diff --git a/tests/test-git-hooks-install.sh b/tests/test-git-hooks-install.sh index 8a36350..15be37e 100755 --- a/tests/test-git-hooks-install.sh +++ b/tests/test-git-hooks-install.sh @@ -40,11 +40,25 @@ DEPLOY_EXECUTABLES=() DEPLOY_DIRS=() EOF -# Copy the real install.sh and git-hooks into the temp repo +# Copy the real install.sh into the temp repo. cp "$REPO_ROOT/scripts/install.sh" "$TEMP_REPO/scripts/install.sh" -cp -r "$REPO_ROOT/scripts/git-hooks" "$TEMP_REPO/scripts/git-hooks" HOOKS_SRC="$TEMP_REPO/scripts/git-hooks" +mkdir -p "$HOOKS_SRC" + +# Copy whatever real hooks exist, then add a synthetic fixture. The repo +# currently ships none — the only entry was `post-push`, removed once it was +# found that git has no such client-side hook, so it had never fired (see +# ADR-0019). install.sh's copy block is generic and stays worth testing, so the +# fixture keeps that coverage alive independently of whether any real hook +# happens to exist. Real hooks are still picked up by the loops below. +if [[ -d "$REPO_ROOT/scripts/git-hooks" ]]; then + find "$REPO_ROOT/scripts/git-hooks" -maxdepth 1 -type f -exec cp {} "$HOOKS_SRC/" \; +fi + +FIXTURE_HOOK="fixture-hook" +printf '#!/usr/bin/env bash\nexit 0\n' > "$HOOKS_SRC/$FIXTURE_HOOK" +chmod +x "$HOOKS_SRC/$FIXTURE_HOOK" run_install() { HOME="$TEMP_HOME" bash "$TEMP_REPO/scripts/install.sh" > /dev/null 2>&1 @@ -108,7 +122,7 @@ OTHER_REPO="$(mktemp -d)" git -C "$OTHER_REPO" init -q if HOME="$TEMP_HOME" GIT_DIR="$OTHER_REPO/.git" GIT_WORK_TREE="$OTHER_REPO" \ bash "$TEMP_REPO/scripts/install.sh" > /dev/null 2>&1; then - if [[ -f "$TEMP_REPO/.git/hooks/post-push" ]]; then + if [[ -f "$TEMP_REPO/.git/hooks/$FIXTURE_HOOK" ]]; then pass "resolves \$TEMP_REPO/.git/hooks/ even with inherited GIT_DIR" else fail "installed into inherited GIT_DIR instead of \$TEMP_REPO" diff --git a/tests/test-post-push.sh b/tests/test-post-push.sh deleted file mode 100755 index a6fa4eb..0000000 --- a/tests/test-post-push.sh +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -PASS=0 -FAIL=0 - -pass() { echo " PASS: $1"; PASS=$((PASS + 1)); } -fail() { echo " FAIL: $1"; FAIL=$((FAIL + 1)); } - -HOOK="$REPO_ROOT/scripts/git-hooks/post-push" -FAKE_BIN="$(mktemp -d)" -FAKE_HOME="$(mktemp -d)" -trap 'rm -rf "$FAKE_BIN" "$FAKE_HOME"' EXIT - -# Helper: write a fake git stub that exits with the given code -make_git() { - local exit_code="$1" - printf '#!/usr/bin/env bash\nexit %s\n' "$exit_code" > "$FAKE_BIN/git" - chmod +x "$FAKE_BIN/git" -} - -# Helper: write a fake claude stub; optionally touches a sentinel file on invocation -make_claude() { - local exit_code="$1" - local log="${2:-}" - if [[ -n "$log" ]]; then - printf '#!/usr/bin/env bash\ntouch "%s"\nexit %s\n' "$log" "$exit_code" > "$FAKE_BIN/claude" - else - printf '#!/usr/bin/env bash\nexit %s\n' "$exit_code" > "$FAKE_BIN/claude" - fi - chmod +x "$FAKE_BIN/claude" -} - -# Run the hook with mocked PATH and HOME; suppress all output -run_hook() { - PATH="$FAKE_BIN:$PATH" HOME="$FAKE_HOME" bash "$HOOK" > /dev/null 2>&1 -} - -# Run the hook and capture combined stdout+stderr -capture_hook() { - PATH="$FAKE_BIN:$PATH" HOME="$FAKE_HOME" bash "$HOOK" 2>&1 || true -} - -# --------------------------------------------------------------------------- -echo "--- post-push: always exits 0 ---" -# --------------------------------------------------------------------------- - -make_git 1; make_claude 0 -if run_hook; then - pass "exits 0 when git pull fails" -else - fail "should exit 0 when git pull fails" -fi - -make_git 0; make_claude 1 -if run_hook; then - pass "exits 0 when claude plugin update fails" -else - fail "should exit 0 when claude plugin update fails" -fi - -# --------------------------------------------------------------------------- -echo "" -echo "--- post-push: success output ---" -# --------------------------------------------------------------------------- - -make_git 0; make_claude 0 -output=$(capture_hook) -echo "$output" | grep -q "kyberforge cache updated" \ - && pass "prints success message when both git pull and claude succeed" \ - || fail "should print success message when both commands succeed" - -# --------------------------------------------------------------------------- -echo "" -echo "--- post-push: warnings on failure ---" -# --------------------------------------------------------------------------- - -make_git 1; make_claude 0 -output=$(capture_hook) -echo "$output" | grep -qi "failed to pull" \ - && pass "warns when git pull fails" \ - || fail "should warn when git pull fails" - -make_git 0; make_claude 1 -output=$(capture_hook) -echo "$output" | grep -qi "failed" \ - && pass "warns when claude plugin update fails" \ - || fail "should warn when claude plugin update fails" - -# --------------------------------------------------------------------------- -echo "" -echo "--- post-push: claude not called when git pull fails ---" -# --------------------------------------------------------------------------- - -CLAUDE_LOG="$FAKE_HOME/claude-was-called" -make_git 1; make_claude 0 "$CLAUDE_LOG" -run_hook -if [[ ! -f "$CLAUDE_LOG" ]]; then - pass "claude not called when git pull fails" -else - fail "claude should not be called when git pull fails" -fi - -# --------------------------------------------------------------------------- -echo "" -echo "Results: $PASS passed, $FAIL failed" -[[ $FAIL -eq 0 ]]