diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 370e5c3..14a3fac 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -6,69 +6,41 @@ }, "description": "Reusable AI skills for Claude Code and Antigravity CLI", "plugins": [ + { + "name": "core-essentials", + "source": "./dist/claude-code/core-essentials" + }, { "name": "date-time", - "description": "Retrieves the exact current date and time for time-sensitive queries, scheduling, duration calculations, and validating chronological context.", - "source": "./", - "skills": [ - "./skills/date-time" - ] + "source": "./dist/claude-code/date-time" }, { "name": "docs-sync-audit", - "description": "Analyzes repository delta since the last documentation update and synchronizes the Single Source of Truth (docs/, wiki, or README) with the current codebase state.", - "source": "./", - "skills": [ - "./skills/docs-sync-audit" - ] + "source": "./dist/claude-code/docs-sync-audit" }, { "name": "git-publish-workflow", - "description": "Automates branching, conventional commits, testing, and PR creation for uncommitted or staged work. Handles both independent and stacked PRs.", - "source": "./", - "skills": [ - "./skills/git-publish-workflow" - ] + "source": "./dist/claude-code/git-publish-workflow" }, { "name": "readme-sync-audit", - "description": "Analyzes repository delta since the last documentation update and synchronizes the README.md with the current codebase state.", - "source": "./", - "skills": [ - "./skills/readme-sync-audit" - ] + "source": "./dist/claude-code/readme-sync-audit" }, { "name": "ship-it", - "description": "Runs a comprehensive pre-flight audit, syncs the README, and publishes the changes to a new PR.", - "source": "./", - "skills": [ - "./skills/ship-it" - ] + "source": "./dist/claude-code/ship-it" }, { "name": "systematic-enumeration", - "description": "Forces element-by-element verification for finite sets to prevent counting errors.", - "source": "./", - "skills": [ - "./skills/systematic-enumeration" - ] + "source": "./dist/claude-code/systematic-enumeration" }, { "name": "technical-devlog-scribe", - "description": "Generates a highly structured, objective technical summary of a development session.", - "source": "./", - "skills": [ - "./skills/technical-devlog-scribe" - ] + "source": "./dist/claude-code/technical-devlog-scribe" }, { "name": "rootiest-ai-all", - "description": "Reusable AI skills for Claude Code and Antigravity CLI (all skills)", - "source": "./", - "skills": [ - "./skills/" - ] + "source": "./dist/claude-code/rootiest-ai-all" } ] } diff --git a/.gitea/workflows/plugins.yml b/.gitea/workflows/plugins.yml index c8704b3..91438d7 100644 --- a/.gitea/workflows/plugins.yml +++ b/.gitea/workflows/plugins.yml @@ -3,15 +3,16 @@ name: Generate plugin manifests on: pull_request: paths: - - "skills/**" + - "plugins/**" - "manifest.yaml" - "scripts/**" push: branches: [main] paths: - - "skills/**" + - "plugins/**" - "manifest.yaml" - "scripts/**" + workflow_dispatch: {} jobs: validate: @@ -26,7 +27,7 @@ jobs: - run: python3 scripts/generate_plugins.py --check generate: - if: github.event_name == 'push' + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -39,7 +40,7 @@ jobs: run: | git config user.name "gitea-actions" git config user.email "actions@git.rootiest.dev" - git add .claude-plugin descriptions.json dist + git add .claude-plugin dist if git diff --cached --quiet; then echo "No generated output changes." exit 0 diff --git a/.gitignore b/.gitignore index 060ccfd..bda58eb 100644 --- a/.gitignore +++ b/.gitignore @@ -116,3 +116,12 @@ AGENTS/ docs/plans docs/devlogs # ──────────────────────────────────────────────────────── + +# ─────────────────── Private Overlay Builds ───────────────── +# Output of `generate_plugins.py` when layering a private/PII-bearing +# source on top of the public plugins/ tree. Never commit this. +dist-private/ + +# ─────────────────────────── Python ────────────────────────── +__pycache__/ +*.pyc diff --git a/README.md b/README.md index b296510..3987fe9 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,16 @@ # Rootiest AI Repository -A collection of reusable AI skills (structured prompt protocols) for **Claude Code** and **Antigravity CLI**. Each skill defines a precise execution protocol that guides the AI through complex, multi-step tasks — replacing ad-hoc prompting with consistent, auditable workflows. - -Skills are plain Markdown files. The included `install.sh` script handles discovery and installation in a single command. +A shareable extension library for **Claude Code** and **Antigravity CLI** +(`agy`). The unit of distribution is a **plugin** — a bundle that can carry +any mix of skills (structured prompt protocols), MCP servers, lifecycle +hooks, and tool-specific extras (slash commands, subagents, rules) — built +from one source tree and published as a native marketplace for both tools. --- ## Table of Contents -- [Skills](#skills) +- [Plugins](#plugins) - [systematic-enumeration](#systematic-enumeration) - [git-publish-workflow](#git-publish-workflow) - [readme-sync-audit](#readme-sync-audit) @@ -16,19 +18,19 @@ Skills are plain Markdown files. The included `install.sh` script handles discov - [date-time](#date-time) - [technical-devlog-scribe](#technical-devlog-scribe) - [ship-it](#ship-it) + - [core-essentials](#core-essentials) - [Installation](#installation) - - [Claude Code Plugin Marketplace](#claude-code-plugin-marketplace) - - [Quick Install (curl)](#quick-install-curl) - - [Flags & Options](#flags--options) - - [Environment Variables](#environment-variables) - - [Examples](#examples) - - [Manual Install](#manual-install) + - [Claude Code](#claude-code) + - [Antigravity CLI (agy)](#antigravity-cli-agy) - [Repository Structure](#repository-structure) + - [Anatomy of a Plugin](#anatomy-of-a-plugin) + - [How Generation Works](#how-generation-works) +- [Private Overlay Builds](#private-overlay-builds) - [License](#license) --- -## Skills +## Plugins ### `systematic-enumeration` @@ -121,177 +123,154 @@ Two sequential phases — Phase 2 is blocked until Phase 1 succeeds: --- +### `core-essentials` + +**Purpose:** Cross-tool utilities that don't belong to a single workflow. + +Currently bundles `delegate-agy`, which hands a subtask off to the +Antigravity CLI (`agy`) in headless mode — useful for a second opinion, +external grounded research, or a large multi-file audit (>500 lines) that +would otherwise bloat the current context. + +--- + ## Installation -### Claude Code Plugin Marketplace +This repository is a native plugin marketplace for both tools — there is no +install script. Each plugin can be installed individually, or as one bundle +(`rootiest-ai-all`). -This repository is a Claude Code plugin marketplace. Each skill is installable -individually, or install everything as one bundle. This is the preferred -install path for Claude Code — `install.sh` remains available as a -cross-tool fallback (and is the only supported path for Antigravity CLI today). +### Claude Code ``` /plugin marketplace add https://git.rootiest.dev/rootiest/rootiest-ai.git /plugin install git-publish-workflow@rootiest-ai ``` -Install every skill at once with the `rootiest-ai-all` bundle: +Install everything at once: ``` /plugin install rootiest-ai-all@rootiest-ai ``` -Run `/plugin marketplace update` to pick up newly published skills. +Run `/plugin marketplace update` to pick up newly published plugins. -### Quick Install (curl) +### Antigravity CLI (`agy`) -The intended usage is a single `curl | bash` command. The installer fetches and runs `install.sh` directly — no clone required. - -> [!TIP] -> The short-url `https://url.rootiest.dev/ai-install` can be substituted for the full URL in the examples below. -> e.g. `curl -sL https://url.rootiest.dev/ai-install | bash -s -- --all` - -**Install all skills for both Claude Code and Antigravity CLI:** +`agy` discovers plugins from a `plugins/` folder pointed at by an entry in +`plugins.json` — either globally (`~/.gemini/config/plugins.json`) or per +project (`.agents/plugins.json`). Clone this repo, then add an entry +pointing at the generated `dist/agy` directory: ```bash -curl -sL https://git.rootiest.dev/rootiest/rootiest-ai/raw/branch/main/install.sh | bash -s -- --all +git clone https://git.rootiest.dev/rootiest/rootiest-ai.git ~/rootiest-ai ``` -**Install all skills for Claude Code only:** - -```bash -curl -sL https://git.rootiest.dev/rootiest/rootiest-ai/raw/branch/main/install.sh | bash -s -- --all --claude +```json +// ~/.gemini/config/plugins.json +{ + "entries": [ + { "path": "~/rootiest-ai/dist/agy" } + ] +} ``` -**Install all skills for Antigravity CLI only:** - -```bash -curl -sL https://git.rootiest.dev/rootiest/rootiest-ai/raw/branch/main/install.sh | bash -s -- --all --antigravity -``` - -**Install a single skill for both tools:** - -```bash -curl -sL https://git.rootiest.dev/rootiest/rootiest-ai/raw/branch/main/install.sh | bash -s -- systematic-enumeration -``` - -**Install specific skills for Claude Code only:** - -```bash -curl -sL https://git.rootiest.dev/rootiest/rootiest-ai/raw/branch/main/install.sh | bash -s -- --claude git-publish-workflow readme-sync-audit -``` - -**Install specific skills for Antigravity CLI only:** - -```bash -curl -sL https://git.rootiest.dev/rootiest/rootiest-ai/raw/branch/main/install.sh | bash -s -- --antigravity systematic-enumeration git-publish-workflow -``` - -> **Note:** Providing explicit skill names alongside `--all` causes the named skills to take precedence — only those skills are installed. - ---- - -### Flags & Options - -| Flag | Description | -|---|---| -| `-c`, `--claude` | Install into Claude Code (`~/.claude/skills/`) | -| `-g`, `--antigravity` | Install into Antigravity CLI (`~/.gemini/antigravity-cli/skills/`) | -| `-a`, `--all` | Install every skill available in the repository | -| `SKILL...` | Install one or more named skills (positional arguments) | -| `-l`, `--list` | List all available skills with descriptions and exit | -| `-h`, `--help` | Show the help page | - -**Tool targeting:** if neither `--claude` nor `--antigravity` is specified, the installer targets **both** tools by default. - -**Skill selection precedence:** explicit skill names always override `--all`. Passing `--all skill-name` installs only `skill-name`, not every skill. - -**Dependencies:** `curl` and `git` must be present on `PATH`. The installer checks for both and exits with a clear error if either is missing. - ---- - -### Environment Variables - -| Variable | Default | Description | -|---|---|---| -| `CLAUDE_SKILLS_DIR` | `~/.claude/skills` | Override the Claude Code install directory | -| `ANTIGRAVITY_SKILLS_DIR` | `~/.gemini/antigravity-cli/skills` | Override the Antigravity CLI install directory. Falls back to `GEMINI_SKILLS_DIR` if set (legacy support). | - -Example — installing to a project-local skills directory: - -```bash -curl -sL https://git.rootiest.dev/rootiest/rootiest-ai/raw/branch/main/install.sh \ - | CLAUDE_SKILLS_DIR=./.claude/skills bash -s -- --claude systematic-enumeration -``` - ---- - -### Examples - -```bash -# Install everything, both tools (simplest possible invocation) -curl -sL https://git.rootiest.dev/rootiest/rootiest-ai/raw/branch/main/install.sh | bash -s -- --all - -# Install one skill, Claude only -curl -sL https://git.rootiest.dev/rootiest/rootiest-ai/raw/branch/main/install.sh | bash -s -- --claude git-publish-workflow - -# Install two skills, Antigravity only -curl -sL https://git.rootiest.dev/rootiest/rootiest-ai/raw/branch/main/install.sh | bash -s -- --antigravity systematic-enumeration readme-sync-audit - -# Override install directory, Claude only -curl -sL https://git.rootiest.dev/rootiest/rootiest-ai/raw/branch/main/install.sh \ - | CLAUDE_SKILLS_DIR=~/my-skills bash -s -- --claude --all -``` - ---- - -### Manual Install - -If you prefer to inspect the script before running it: - -```bash -# Download -curl -sL https://git.rootiest.dev/rootiest/rootiest-ai/raw/branch/main/install.sh -o install.sh - -# Review -less install.sh - -# Run -bash install.sh --all -``` - -Or clone the repository and run locally: - -```bash -git clone https://git.rootiest.dev/rootiest/rootiest-ai.git -cd rootiest-ai -bash install.sh --all -``` +`git pull` in the clone to pick up updates; `agy plugin list` / `agy plugin +validate ` to inspect what's loaded. --- ## Repository Structure -Skill content lives entirely in `skills//SKILL.md` — that's the single -source of truth for a skill's metadata (YAML frontmatter: `name`, -`description`, `version`, `author`, `user-invocable`) and instructions. -`manifest.yaml` holds only marketplace-level metadata (owner, marketplace -name, which agent targets to generate for) — it does not list skills -individually; adding a new `skills//` folder is picked up automatically. +### Anatomy of a Plugin -CI (`.gitea/workflows/plugins.yml`) runs `scripts/generate_plugins.py` on -every push to `main` that touches `skills/`, `manifest.yaml`, or the -generator itself, and commits the regenerated output: +Every plugin lives under `plugins//` and is the single source of +truth for that name — nothing under `plugins/` is ever written by the +generator. A plugin can bundle any subset of: + +``` +plugins// +├── plugin.json # required: name, description, version, author +├── skills//SKILL.md # 0+ skills (YAML frontmatter: name, description, version, author) +├── hooks.json # optional: lifecycle hooks, Claude-shaped event → matcher groups +├── mcp.json # optional: {"mcpServers": {...}} +├── rules/AGENTS.md # optional: agy-only, always-on project rules +├── commands/*.md # optional: Claude Code-only slash commands +└── agents/*.md # optional: Claude Code-only subagents +``` + +`hooks.json` and `mcp.json` are translated per target rather than copied +verbatim where the two tools' schemas diverge: + +- MCP: the shared `mcpServers` shape passes straight through to Claude's + `.mcp.json`; a `url`/`serverUrl` remote entry becomes agy's + `serverUrl` field in `mcp_config.json`. +- Hooks: Claude Code has far more event types than agy documents. Events + agy doesn't support (`SessionStart`, `TaskCreated`, etc.) simply stay + Claude-only — the agy output only carries `PreToolUse`/`PostToolUse` + (kept grouped with their `matcher`) and `PreInvocation`/`PostInvocation`/ + `Stop` (flattened to agy's handler-list shape, since agy doesn't group + those by matcher). +- `rules/` has no Claude Code plugin equivalent and is skipped for that + target; `commands/`/`agents/` have no agy equivalent and are skipped for + that target. + +### How Generation Works + +`scripts/generate_plugins.py` reads `manifest.yaml` (marketplace metadata, +bundle id, target list) and every `plugins//`, then regenerates: | Path | Generated for | |---|---| | `.claude-plugin/marketplace.json` | Claude Code plugin marketplace | -| `dist/agy/**` | Antigravity CLI (`agy`) plugins | -| `descriptions.json` | `install.sh` skill listing | +| `dist/claude-code/**` | Claude Code plugin directories (linked from the marketplace) | +| `dist/agy/**` | Antigravity CLI (`agy`) plugin directories | -Don't hand-edit any of the paths above — edit the source skill or -`manifest.yaml` and let CI regenerate them. Pull requests run the same -generator in `--check` mode to catch missing/invalid frontmatter before merge. +CI (`.gitea/workflows/plugins.yml`) runs the generator on every push to +`main` that touches `plugins/`, `manifest.yaml`, or the generator itself, +and commits the regenerated output; it can also be re-run on demand from +the Actions tab (`workflow_dispatch`). Pull requests run the same generator +in `--check` mode to catch missing/invalid `plugin.json`/`SKILL.md` +frontmatter before merge. + +Don't hand-edit anything under `.claude-plugin/` or `dist/` — edit the +source plugin or `manifest.yaml` and let the generator regenerate them. + +--- + +## Private Overlay Builds + +The generator supports layering an additional, non-public source of +plugins on top of this repo — for keeping PII, tokens, API keys, or +personal-only plugins out of a public repo's git history entirely, while +still reusing the same plugin format and generator. + +```bash +# Layer a second, already-cloned repo on top of this one +python3 scripts/generate_plugins.py --source . --source ~/rootiest-ai-private + +# Or have the generator clone it (token read from an env var, never a CLI argument) +export PRIVATE_TOKEN=... +python3 scripts/generate_plugins.py \ + --private-repo https://git.example.com/you/rootiest-ai-private.git \ + --private-ref main \ + --private-token-env PRIVATE_TOKEN +``` + +A later `--source` overlays the earlier ones per plugin: a plugin name that +only exists in the private source is added; a plugin name that exists in +both is merged file-by-file, with the private copy winning on conflicts +(e.g. supplying a real `mcp.json` where the public plugin ships a +placeholder). + +Output never lands in this repo's tracked paths for an overlaid build — +`--out` defaults to `dist-private/` (gitignored) whenever more than one +source is in play. Add `--install-local` to also drop the result straight +into `~/.claude/plugins/marketplaces/-private/` and print the +`~/.gemini/config/plugins.json` entry for agy, so a personal build is +usable immediately without committing anything anywhere. + +--- ## License diff --git a/descriptions.json b/descriptions.json deleted file mode 100644 index 7ca08d7..0000000 --- a/descriptions.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "date-time": "Retrieves the exact current date and time for time-sensitive queries, scheduling, duration calculations, and validating chronological context.", - "docs-sync-audit": "Analyzes repository delta since the last documentation update and synchronizes the Single Source of Truth (docs/, wiki, or README) with the current codebase state.", - "git-publish-workflow": "Automates branching, conventional commits, testing, and PR creation for uncommitted or staged work. Handles both independent and stacked PRs.", - "readme-sync-audit": "Analyzes repository delta since the last documentation update and synchronizes the README.md with the current codebase state.", - "ship-it": "Runs a comprehensive pre-flight audit, syncs the README, and publishes the changes to a new PR.", - "systematic-enumeration": "Forces element-by-element verification for finite sets to prevent counting errors.", - "technical-devlog-scribe": "Generates a highly structured, objective technical summary of a development session." -} diff --git a/dist/README.md b/dist/README.md index 8a1b5da..e721988 100644 --- a/dist/README.md +++ b/dist/README.md @@ -1,3 +1,3 @@ # Generated -This directory is generated by `scripts/generate_plugins.py` from `manifest.yaml` and `skills/`. Do not edit files here directly — edit the source skill instead and regenerate. +This directory is generated by `scripts/generate_plugins.py` from `manifest.yaml` and `plugins/`. Do not edit files here directly — edit the source plugin instead and regenerate. diff --git a/dist/agy/core-essentials/plugin.json b/dist/agy/core-essentials/plugin.json new file mode 100644 index 0000000..0a70d7d --- /dev/null +++ b/dist/agy/core-essentials/plugin.json @@ -0,0 +1,3 @@ +{ + "name": "core-essentials" +} diff --git a/dist/agy/core-essentials/skills/delegate-agy/SKILL.md b/dist/agy/core-essentials/skills/delegate-agy/SKILL.md new file mode 100644 index 0000000..c2d9290 --- /dev/null +++ b/dist/agy/core-essentials/skills/delegate-agy/SKILL.md @@ -0,0 +1,18 @@ +--- +name: delegate-agy +description: Delegates a subtask to the Antigravity CLI (agy) when the user wants a second opinion, external grounded research, or needs a large multi-file audit (>500 lines) processed without bloating the current context. +version: 1.0.0 +user-invocable: true +author: Rootiest +--- + +# Antigravity Subagent Delegation (`delegate-agy`) + +When the user asks for a second opinion, external grounded research, or when a task requires processing large multi-file audits (>500 lines) that would bloat context, delegate the subtask to `agy`. + +## Execution Syntax +Run `agy` in headless, non-interactive mode using the bash tool: + +```bash +agy --dangerously-skip-permissions -p "" +``` diff --git a/dist/agy/date-time/plugin.json b/dist/agy/date-time/plugin.json index a722ce7..6ccfdc3 100644 --- a/dist/agy/date-time/plugin.json +++ b/dist/agy/date-time/plugin.json @@ -1,4 +1,3 @@ { - "name": "date-time", - "description": "Retrieves the exact current date and time for time-sensitive queries, scheduling, duration calculations, and validating chronological context." + "name": "date-time" } diff --git a/dist/agy/docs-sync-audit/plugin.json b/dist/agy/docs-sync-audit/plugin.json index 7eaa449..c57347f 100644 --- a/dist/agy/docs-sync-audit/plugin.json +++ b/dist/agy/docs-sync-audit/plugin.json @@ -1,4 +1,3 @@ { - "name": "docs-sync-audit", - "description": "Analyzes repository delta since the last documentation update and synchronizes the Single Source of Truth (docs/, wiki, or README) with the current codebase state." + "name": "docs-sync-audit" } diff --git a/dist/agy/git-publish-workflow/plugin.json b/dist/agy/git-publish-workflow/plugin.json index 08e2583..a9e062e 100644 --- a/dist/agy/git-publish-workflow/plugin.json +++ b/dist/agy/git-publish-workflow/plugin.json @@ -1,4 +1,3 @@ { - "name": "git-publish-workflow", - "description": "Automates branching, conventional commits, testing, and PR creation for uncommitted or staged work. Handles both independent and stacked PRs." + "name": "git-publish-workflow" } diff --git a/dist/agy/readme-sync-audit/plugin.json b/dist/agy/readme-sync-audit/plugin.json index e6e517e..4aa3e1c 100644 --- a/dist/agy/readme-sync-audit/plugin.json +++ b/dist/agy/readme-sync-audit/plugin.json @@ -1,4 +1,3 @@ { - "name": "readme-sync-audit", - "description": "Analyzes repository delta since the last documentation update and synchronizes the README.md with the current codebase state." + "name": "readme-sync-audit" } diff --git a/dist/agy/rootiest-ai-all/plugin.json b/dist/agy/rootiest-ai-all/plugin.json index 5919517..93ff7f9 100644 --- a/dist/agy/rootiest-ai-all/plugin.json +++ b/dist/agy/rootiest-ai-all/plugin.json @@ -1,4 +1,3 @@ { - "name": "rootiest-ai-all", - "description": "Reusable AI skills for Claude Code and Antigravity CLI (all skills)" + "name": "rootiest-ai-all" } diff --git a/dist/agy/rootiest-ai-all/skills/delegate-agy/SKILL.md b/dist/agy/rootiest-ai-all/skills/delegate-agy/SKILL.md new file mode 100644 index 0000000..c2d9290 --- /dev/null +++ b/dist/agy/rootiest-ai-all/skills/delegate-agy/SKILL.md @@ -0,0 +1,18 @@ +--- +name: delegate-agy +description: Delegates a subtask to the Antigravity CLI (agy) when the user wants a second opinion, external grounded research, or needs a large multi-file audit (>500 lines) processed without bloating the current context. +version: 1.0.0 +user-invocable: true +author: Rootiest +--- + +# Antigravity Subagent Delegation (`delegate-agy`) + +When the user asks for a second opinion, external grounded research, or when a task requires processing large multi-file audits (>500 lines) that would bloat context, delegate the subtask to `agy`. + +## Execution Syntax +Run `agy` in headless, non-interactive mode using the bash tool: + +```bash +agy --dangerously-skip-permissions -p "" +``` diff --git a/dist/agy/ship-it/plugin.json b/dist/agy/ship-it/plugin.json index 28786c8..18ed8d6 100644 --- a/dist/agy/ship-it/plugin.json +++ b/dist/agy/ship-it/plugin.json @@ -1,4 +1,3 @@ { - "name": "ship-it", - "description": "Runs a comprehensive pre-flight audit, syncs the README, and publishes the changes to a new PR." + "name": "ship-it" } diff --git a/dist/agy/systematic-enumeration/plugin.json b/dist/agy/systematic-enumeration/plugin.json index 3fac494..63085f6 100644 --- a/dist/agy/systematic-enumeration/plugin.json +++ b/dist/agy/systematic-enumeration/plugin.json @@ -1,4 +1,3 @@ { - "name": "systematic-enumeration", - "description": "Forces element-by-element verification for finite sets to prevent counting errors." + "name": "systematic-enumeration" } diff --git a/dist/agy/technical-devlog-scribe/plugin.json b/dist/agy/technical-devlog-scribe/plugin.json index c666e0a..3e29eca 100644 --- a/dist/agy/technical-devlog-scribe/plugin.json +++ b/dist/agy/technical-devlog-scribe/plugin.json @@ -1,4 +1,3 @@ { - "name": "technical-devlog-scribe", - "description": "Generates a highly structured, objective technical summary of a development session." + "name": "technical-devlog-scribe" } diff --git a/dist/claude-code/core-essentials/.claude-plugin/plugin.json b/dist/claude-code/core-essentials/.claude-plugin/plugin.json new file mode 100644 index 0000000..e9165e1 --- /dev/null +++ b/dist/claude-code/core-essentials/.claude-plugin/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "core-essentials", + "description": "Core cross-tool utilities, including delegation to the Antigravity CLI (agy) for grounded research and large multi-file audits.", + "version": "1.0.0", + "author": "Rootiest" +} diff --git a/dist/claude-code/core-essentials/skills/delegate-agy/SKILL.md b/dist/claude-code/core-essentials/skills/delegate-agy/SKILL.md new file mode 100644 index 0000000..c2d9290 --- /dev/null +++ b/dist/claude-code/core-essentials/skills/delegate-agy/SKILL.md @@ -0,0 +1,18 @@ +--- +name: delegate-agy +description: Delegates a subtask to the Antigravity CLI (agy) when the user wants a second opinion, external grounded research, or needs a large multi-file audit (>500 lines) processed without bloating the current context. +version: 1.0.0 +user-invocable: true +author: Rootiest +--- + +# Antigravity Subagent Delegation (`delegate-agy`) + +When the user asks for a second opinion, external grounded research, or when a task requires processing large multi-file audits (>500 lines) that would bloat context, delegate the subtask to `agy`. + +## Execution Syntax +Run `agy` in headless, non-interactive mode using the bash tool: + +```bash +agy --dangerously-skip-permissions -p "" +``` diff --git a/dist/claude-code/date-time/.claude-plugin/plugin.json b/dist/claude-code/date-time/.claude-plugin/plugin.json new file mode 100644 index 0000000..9910091 --- /dev/null +++ b/dist/claude-code/date-time/.claude-plugin/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "date-time", + "description": "Retrieves the exact current date and time for time-sensitive queries, scheduling, duration calculations, and validating chronological context.", + "version": "1.0.0", + "author": "Rootiest" +} diff --git a/skills/date-time/SKILL.md b/dist/claude-code/date-time/skills/date-time/SKILL.md similarity index 100% rename from skills/date-time/SKILL.md rename to dist/claude-code/date-time/skills/date-time/SKILL.md diff --git a/dist/claude-code/docs-sync-audit/.claude-plugin/plugin.json b/dist/claude-code/docs-sync-audit/.claude-plugin/plugin.json new file mode 100644 index 0000000..7b1f864 --- /dev/null +++ b/dist/claude-code/docs-sync-audit/.claude-plugin/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "docs-sync-audit", + "description": "Analyzes repository delta since the last documentation update and synchronizes the Single Source of Truth (docs/, wiki, or README) with the current codebase state.", + "version": "1.3.0", + "author": "Rootiest" +} diff --git a/skills/docs-sync-audit/SKILL.md b/dist/claude-code/docs-sync-audit/skills/docs-sync-audit/SKILL.md similarity index 100% rename from skills/docs-sync-audit/SKILL.md rename to dist/claude-code/docs-sync-audit/skills/docs-sync-audit/SKILL.md diff --git a/dist/claude-code/git-publish-workflow/.claude-plugin/plugin.json b/dist/claude-code/git-publish-workflow/.claude-plugin/plugin.json new file mode 100644 index 0000000..128244a --- /dev/null +++ b/dist/claude-code/git-publish-workflow/.claude-plugin/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "git-publish-workflow", + "description": "Automates branching, conventional commits, testing, and PR creation for uncommitted or staged work. Handles both independent and stacked PRs.", + "version": "1.2.0", + "author": "Rootiest" +} diff --git a/skills/git-publish-workflow/SKILL.md b/dist/claude-code/git-publish-workflow/skills/git-publish-workflow/SKILL.md similarity index 100% rename from skills/git-publish-workflow/SKILL.md rename to dist/claude-code/git-publish-workflow/skills/git-publish-workflow/SKILL.md diff --git a/dist/claude-code/readme-sync-audit/.claude-plugin/plugin.json b/dist/claude-code/readme-sync-audit/.claude-plugin/plugin.json new file mode 100644 index 0000000..1429967 --- /dev/null +++ b/dist/claude-code/readme-sync-audit/.claude-plugin/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "readme-sync-audit", + "description": "Analyzes repository delta since the last documentation update and synchronizes the README.md with the current codebase state.", + "version": "1.2.0", + "author": "Rootiest" +} diff --git a/skills/readme-sync-audit/SKILL.md b/dist/claude-code/readme-sync-audit/skills/readme-sync-audit/SKILL.md similarity index 100% rename from skills/readme-sync-audit/SKILL.md rename to dist/claude-code/readme-sync-audit/skills/readme-sync-audit/SKILL.md diff --git a/dist/claude-code/rootiest-ai-all/.claude-plugin/plugin.json b/dist/claude-code/rootiest-ai-all/.claude-plugin/plugin.json new file mode 100644 index 0000000..5919517 --- /dev/null +++ b/dist/claude-code/rootiest-ai-all/.claude-plugin/plugin.json @@ -0,0 +1,4 @@ +{ + "name": "rootiest-ai-all", + "description": "Reusable AI skills for Claude Code and Antigravity CLI (all skills)" +} diff --git a/dist/claude-code/rootiest-ai-all/skills/date-time/SKILL.md b/dist/claude-code/rootiest-ai-all/skills/date-time/SKILL.md new file mode 100644 index 0000000..a3ac53e --- /dev/null +++ b/dist/claude-code/rootiest-ai-all/skills/date-time/SKILL.md @@ -0,0 +1,43 @@ +--- +name: date-time +description: Retrieves the exact current date and time for time-sensitive queries, scheduling, duration calculations, and validating chronological context. +version: 1.0.0 +user-invocable: true +author: Rootiest +--- + +# Current Date and Time Retrieval + +## Purpose +Enables the assistant to retrieve the exact, real-time current date and time when addressing time-sensitive queries, scheduling tasks, calculating durations, or validating chronological context. + +## Trigger Conditions +Activate this skill whenever the user's prompt: +* Explicitly asks for the current date, day, time, or year. +* References relative time expressions (e.g., "today", "yesterday", "next week", "recently"). +* Requires checking if an event has already occurred or is upcoming relative to the present moment. +* Needs to calculate an age, duration, or countdown from the present day. + +## Tool Definition + +### `get_current_datetime` +* **Description**: Executes the system `date` command to fetch the current local timestamp, timezone, and calendar date. +* **Parameters**: None required. + +## Execution Workflow +1. **Detect**: Recognize a time-sensitive trigger in the user's input. +2. **Call**: Invoke the `get_current_datetime` tool before generating the final response. +3. **Process**: Use the returned timestamp to anchor your temporal reasoning. +4. **Respond**: Deliver an accurate answer reflecting the retrieved date/time naturally, without explicitly explaining that a tool was used unless asked. + +## Examples + +### Example 1 +* **User**: "What day of the week is it today?" +* **Assistant Action**: Invoke `get_current_datetime`. +* **Response**: "Today is [Day of Week], [Date]." + +### Example 2 +* **User**: "Is the 2026 World Cup happening this month?" +* **Assistant Action**: Invoke `get_current_datetime`. +* **Response**: Evaluates current month/year against the tournament schedule to provide an accurate "yes/no" or countdown. diff --git a/dist/claude-code/rootiest-ai-all/skills/delegate-agy/SKILL.md b/dist/claude-code/rootiest-ai-all/skills/delegate-agy/SKILL.md new file mode 100644 index 0000000..c2d9290 --- /dev/null +++ b/dist/claude-code/rootiest-ai-all/skills/delegate-agy/SKILL.md @@ -0,0 +1,18 @@ +--- +name: delegate-agy +description: Delegates a subtask to the Antigravity CLI (agy) when the user wants a second opinion, external grounded research, or needs a large multi-file audit (>500 lines) processed without bloating the current context. +version: 1.0.0 +user-invocable: true +author: Rootiest +--- + +# Antigravity Subagent Delegation (`delegate-agy`) + +When the user asks for a second opinion, external grounded research, or when a task requires processing large multi-file audits (>500 lines) that would bloat context, delegate the subtask to `agy`. + +## Execution Syntax +Run `agy` in headless, non-interactive mode using the bash tool: + +```bash +agy --dangerously-skip-permissions -p "" +``` diff --git a/dist/claude-code/rootiest-ai-all/skills/docs-sync-audit/SKILL.md b/dist/claude-code/rootiest-ai-all/skills/docs-sync-audit/SKILL.md new file mode 100644 index 0000000..c7966a6 --- /dev/null +++ b/dist/claude-code/rootiest-ai-all/skills/docs-sync-audit/SKILL.md @@ -0,0 +1,48 @@ +--- +name: docs-sync-audit +description: Analyzes repository delta since the last documentation update and synchronizes the Single Source of Truth (docs/, wiki, or README) with the current codebase state. +version: 1.3.0 +user-invocable: true +author: Rootiest +--- + +# Documentation Synchronization & Audit Skill + +## **Objective** +To ensure project documentation accurately reflects the current state of the codebase by identifying the "Single Source of Truth" (SSoT) (e.g., a `docs/` directory or wiki) and prioritizing updates there. The `README.md` is updated concurrently only for high-level changes or if it serves as the SSoT. + +## **Execution Protocol** + +### **Phase 0: SSoT Discovery** +1. **Locate Documentation Root**: Scan the repository structure for dedicated documentation directories (e.g., `docs/`, `wiki/`, `website/docs/`) or configuration files (e.g., `mkdocs.yml`, `docusaurus.config.js`). +2. **Establish SSoT**: If a dedicated documentation structure exists, designate it as the SSoT. If absent, fall back to `README.md` as the primary SSoT. + +### **Phase 1: Delta Analysis** +1. **Time-Travel Check**: Locate the last commit where the SSoT files were modified. +2. **Feature Diff**: Analyze all code changes (files added, functions modified, dependencies updated) from that commit to the present `HEAD`. +3. **Extraction**: Identify new environment variables, CLI flags, installation steps, or logic changes that are not yet documented. + +### **Phase 2: The Pruning & Update Audit** +Perform a targeted comparison of the SSoT against the current code: +* **Route Updates**: Direct detailed API, configuration, and architectural updates to their respective files within the SSoT (`docs/` or wiki). +* **Prune**: Remove any setup steps, dependencies, or "Coming Soon" features from the SSoT that no longer exist or have been replaced. +* **Correct**: Update version numbers, file paths, and command-line examples to match the current implementation. +* **Synthesize**: Add concise documentation for new features identified in Phase 1. +* **README Alignment**: If `docs/` is the SSoT, update the `README.md` *only* to reflect critical, high-level changes (e.g., Quick Start, Installation) or to ensure it properly links to the newly updated sections in the SSoT. + +### **Phase 3: Structural Integrity Check** +Ensure the updated SSoT (and README, if applicable) includes or updates these critical sections: +1. **Quick Start**: Are the commands (e.g., `cargo run`, `npm start`) still the primary entry points? +2. **Configuration**: Are all current `.env` or config keys listed? +3. **Usage Examples**: Do the provided code snippets actually compile/run with the current API? + +## **Constraints & Rules** +* **Hierarchy Enforcement**: Never duplicate deep technical documentation in the README if a `docs/` folder exists. Use the README as a high-level landing page that points to the SSoT. +* **Minimalism**: Maintain the existing tone of the documentation. Do not add "fluff" or marketing language unless the original document uses it. +* **No Hallucinations**: If a feature's purpose is unclear from the code diff, add a `TODO` comment or ask the user for clarification rather than guessing. +* **Markdown Standards**: Use standard GFM (GitHub Flavored Markdown) or MDX if applicable to the SSoT. Ensure all code blocks have the correct language identifier for syntax highlighting. + +## **Trigger Scenarios** +* **Direct Command:** The user invokes `/docs-sync-audit`, `/readme-sync-audit`, or `/update-docs`. +* **Natural Language:** User says "Update the docs", "Sync the wiki", or "Sync the README with my recent changes." +* **Contextual Suggestion:** Trigger automatically if the model detects significant changes to public APIs, CLI arguments, or environment variables without a corresponding documentation update. diff --git a/dist/claude-code/rootiest-ai-all/skills/git-publish-workflow/SKILL.md b/dist/claude-code/rootiest-ai-all/skills/git-publish-workflow/SKILL.md new file mode 100644 index 0000000..3f78156 --- /dev/null +++ b/dist/claude-code/rootiest-ai-all/skills/git-publish-workflow/SKILL.md @@ -0,0 +1,49 @@ +--- +name: git-publish-workflow +description: Automates branching, conventional commits, testing, and PR creation for uncommitted or staged work. Handles both independent and stacked PRs. +version: 1.2.0 +user-invocable: true +author: Rootiest +--- + +# Git Publish & PR Workflow + +## **Objective** +To provide a hands-off, end-to-end automation for moving local changes into a formal Pull Request, ensuring code quality through automated and manual verification steps, while intelligently routing Stacked PRs. + +## **Execution Protocol** + +### **Phase 1: Scope & Base Determination** +Before execution, check the local git state: +1. **Scope Check**: Determine the work boundary: + * **Case A (Partial):** If staged changes exist, operate **ONLY** on staged changes. + * **Case B (Full):** If no changes are staged, operate on **ALL** modified/untracked files. +2. **Base Branch Detection**: Identify the currently checked-out branch. + * **Independent PR**: If the current branch is `main` (or `master`), the new branch will be based on `main`. The PR target will be `main`. + * **Stacked PR**: If the current branch is a feature branch (e.g., `feat-a`), assume the new changes are dependent. The new branch will be created from the current branch. The PR target will be the current feature branch (NOT `main`). + * *Safety Check*: If creating a Stacked PR, output a brief terminal message stating: "Detected active feature branch. Stacking new PR on top of `[current-branch-name]`." + +### **Phase 2: The "Safe-Commit" Sequence** +1. **Branching**: Generate a `kebab-case` branch name (e.g., `feat-auth-logic` or `fix-header-css`) based off the branch determined in Phase 1. +2. **Naming**: Use **Conventional Commits** for the message (e.g., `feat(ui): add logout button`). +3. **Verification**: + * Identify the project type (e.g., Rust/Cargo, Python/Poetry, Node/NPM). + * Run the primary `test`, `lint`, or `build` command. + * **Abort Policy**: If verification fails, stop the sequence and report the error. Do not push. + +### **Phase 3: Remote Integration** +1. **Push**: Upload the new branch to `origin`. +2. **PR Creation**: Open a Pull Request targeting the base branch determined in Phase 1 (either `main` or the parent feature branch). +3. **Documentation**: Populate the PR description with: + * **Summary**: A high-level overview of "Why" and "What." + * **Manual Verification Checklist**: Provide a Markdown list (`- [ ]`) of 3-5 tactical steps for a human to verify the change in a live environment. + +## **Constraints & Rules** +* **Atomic Commits**: If multiple distinct features are found in the scope, suggest splitting the work instead of one giant commit. +* **No Force Push**: Never use `--force` unless explicitly requested in the follow-up prompt. +* **Clean State**: Ensure the workflow ends with the user on the new branch, not the original branch. + +## **Trigger Scenarios** +* User says: "Ship this." +* User says: "Make a PR for my current changes." +* Invoked via `/git-publish-workflow`. diff --git a/dist/claude-code/rootiest-ai-all/skills/readme-sync-audit/SKILL.md b/dist/claude-code/rootiest-ai-all/skills/readme-sync-audit/SKILL.md new file mode 100644 index 0000000..b7f7bc0 --- /dev/null +++ b/dist/claude-code/rootiest-ai-all/skills/readme-sync-audit/SKILL.md @@ -0,0 +1,41 @@ +--- +name: readme-sync-audit +description: Analyzes repository delta since the last documentation update and synchronizes the README.md with the current codebase state. +version: 1.2.0 +user-invocable: true +author: Rootiest +--- + +# README Synchronization & Audit Skill + +## **Objective** +To ensure the `README.md` serves as a "Single Source of Truth" by programmatically aligning documentation with the actual state of the codebase. This skill prioritizes accuracy and the removal of obsolete instructions. + +## **Execution Protocol** + +### **Phase 1: Delta Analysis** +1. **Time-Travel Check**: Locate the last commit where `README.md` was modified. +2. **Feature Diff**: Analyze all code changes (files added, functions modified, dependencies updated) from that commit to the present `HEAD`. +3. **Extraction**: Identify new environment variables, CLI flags, installation steps, or logic changes that are not yet documented. + +### **Phase 2: The Pruning & Update Audit** +Perform a line-by-line comparison of the existing README against the current code: +* **Prune**: Remove any setup steps, dependencies, or "Coming Soon" features that no longer exist or have been replaced. +* **Correct**: Update version numbers, file paths, and command-line examples to match the current implementation. +* **Synthesize**: Add concise documentation for new features identified in Phase 1. + +### **Phase 3: Structural Integrity Check** +Ensure the updated README includes (or updates) these critical sections: +1. **Quick Start**: Are the commands (e.g., `cargo run`, `npm start`) still the primary entry points? +2. **Configuration**: Are all current `.env` or config keys listed? +3. **Usage Examples**: Do the provided code snippets actually compile/run with the current API? + +## **Constraints & Rules** +* **Minimalism**: Maintain the existing tone of the README. Do not add "fluff" or marketing language unless the original document uses it. +* **No Hallucinations**: If a feature's purpose is unclear from the code diff, add a `TODO` comment or ask the user for clarification rather than guessing. +* **Markdown Standards**: Use standard GFM (GitHub Flavored Markdown). Ensure all code blocks have the correct language identifier for syntax highlighting. + +## **Trigger Scenarios** +* **Direct Command:** The user invokes `/readme-sync-audit` or `/update-docs`. +* **Natural Language:** User says "Update the docs" or "Sync the README with my recent changes." +* **Contextual Suggestion:** Trigger automatically if the model detects significant changes to public APIs, CLI arguments, or environment variables without a corresponding documentation update. diff --git a/skills/ship-it/SKILL.md b/dist/claude-code/rootiest-ai-all/skills/ship-it/SKILL.md similarity index 100% rename from skills/ship-it/SKILL.md rename to dist/claude-code/rootiest-ai-all/skills/ship-it/SKILL.md diff --git a/skills/systematic-enumeration/SKILL.md b/dist/claude-code/rootiest-ai-all/skills/systematic-enumeration/SKILL.md similarity index 100% rename from skills/systematic-enumeration/SKILL.md rename to dist/claude-code/rootiest-ai-all/skills/systematic-enumeration/SKILL.md diff --git a/skills/technical-devlog-scribe/SKILL.md b/dist/claude-code/rootiest-ai-all/skills/technical-devlog-scribe/SKILL.md similarity index 100% rename from skills/technical-devlog-scribe/SKILL.md rename to dist/claude-code/rootiest-ai-all/skills/technical-devlog-scribe/SKILL.md diff --git a/dist/claude-code/ship-it/.claude-plugin/plugin.json b/dist/claude-code/ship-it/.claude-plugin/plugin.json new file mode 100644 index 0000000..356d960 --- /dev/null +++ b/dist/claude-code/ship-it/.claude-plugin/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "ship-it", + "description": "Runs a comprehensive pre-flight audit, syncs the README, and publishes the changes to a new PR.", + "version": "1.0.0", + "author": "Rootiest" +} diff --git a/dist/claude-code/ship-it/skills/ship-it/SKILL.md b/dist/claude-code/ship-it/skills/ship-it/SKILL.md new file mode 100644 index 0000000..4da824e --- /dev/null +++ b/dist/claude-code/ship-it/skills/ship-it/SKILL.md @@ -0,0 +1,25 @@ +--- +name: ship-it +description: Runs a comprehensive pre-flight audit, syncs the README, and publishes the changes to a new PR. +version: 1.0.0 +user-invocable: true +author: Rootiest +--- + +# /ship-it + +Instructions: +Execute the following two phases sequentially. Do not proceed to Phase 2 unless Phase 1 completes successfully. + +1. Phase 1: Documentation Sync & Code Audit + - Act as the `/docs-sync-audit` skill. + - Scan all file changes since the last README edit and update the README to ensure it accurately reflects the current state of the codebase. + - Audit all code files for any syntax errors, regressions, or issues. + - CRITICAL: If any code errors or breaking issues are discovered during the audit, HALT the workflow immediately and report them to the user. Do not proceed to publishing. + +2. Phase 2: Git Publish Workflow + - Act as the `/git-publish-workflow` skill. + - Create a new, descriptively named git branch. + - Stage and commit all pending changes (including the newly updated README from Phase 1). + - Push the branch to the remote repository. + - Generate a Pull Request (PR) from the new branch into 'main'. diff --git a/dist/claude-code/systematic-enumeration/.claude-plugin/plugin.json b/dist/claude-code/systematic-enumeration/.claude-plugin/plugin.json new file mode 100644 index 0000000..aaf1e32 --- /dev/null +++ b/dist/claude-code/systematic-enumeration/.claude-plugin/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "systematic-enumeration", + "description": "Forces element-by-element verification for finite sets to prevent counting errors.", + "version": "1.0.0", + "author": "Rootiest" +} diff --git a/dist/claude-code/systematic-enumeration/skills/systematic-enumeration/SKILL.md b/dist/claude-code/systematic-enumeration/skills/systematic-enumeration/SKILL.md new file mode 100644 index 0000000..efdc52b --- /dev/null +++ b/dist/claude-code/systematic-enumeration/skills/systematic-enumeration/SKILL.md @@ -0,0 +1,40 @@ +--- +name: systematic-enumeration +description: Forces element-by-element verification for finite sets to prevent counting errors. +version: 1.0.0 +user-invocable: true +author: Rootiest +--- + +# Systematic Enumeration & Verification Skill + +## **Objective** +To eliminate heuristic errors and "hallucinated patterns" when analyzing finite sets. This protocol overrides the model's tendency toward "holistic recognition" in favor of systematic, element-by-element verification. + +## **Execution Protocol** +When this skill is triggered, you MUST NOT provide a direct answer immediately. Follow these three phases to ensure accuracy: + +### **Phase 1: Set Definition** +Explicitly define the boundaries and members of the finite set being analyzed. +* **Requirement:** List the members before performing any tests. +* *Example:* "The set consists of the files in the `/src` directory: [main.rs, utils.rs, types.rs]." + +### **Phase 2: Atomic Element Testing (O(n))** +Iterate through every item in the set. For each item, perform a literal check against the target property. +* **Format:** Use a list or table to force token-level focus on each element. +* **Structure:** `[Item] -> [Logic/Observation] -> [Boolean Result]` +* *Note:* For character-based tests, split the string into individual characters to bypass tokenization bias. + +### **Phase 3: Reduction & Summation** +Aggregate the `True` results from Phase 2 to derive the final answer. +* **Self-Correction:** Verify that the count of items tested in Phase 2 exactly matches the count of the set defined in Phase 1. If there is a mismatch, restart Phase 2. + +## **Constraints & Anti-Patterns** +* **STRICT BAN on Heuristics:** Do not use phrases like "typically," "usually," or "it appears that." +* **NO Pattern Matching:** Do not extrapolate a rule (e.g., "every other item") as a substitute for testing every item. +* **Computational Justification:** Treat the process as an $O(n)$ operation where $n$ is small enough that accuracy is the only priority. + +## **Trigger Scenarios** +* Counting specific characters or substrings within a string. +* Verifying property adherence across a list of variables, files, or objects. +* Membership testing in sets where false negatives are high-risk. diff --git a/dist/claude-code/technical-devlog-scribe/.claude-plugin/plugin.json b/dist/claude-code/technical-devlog-scribe/.claude-plugin/plugin.json new file mode 100644 index 0000000..3171855 --- /dev/null +++ b/dist/claude-code/technical-devlog-scribe/.claude-plugin/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "technical-devlog-scribe", + "description": "Generates a highly structured, objective technical summary of a development session.", + "version": "1.0.0", + "author": "Rootiest" +} diff --git a/dist/claude-code/technical-devlog-scribe/skills/technical-devlog-scribe/SKILL.md b/dist/claude-code/technical-devlog-scribe/skills/technical-devlog-scribe/SKILL.md new file mode 100644 index 0000000..c0f77f3 --- /dev/null +++ b/dist/claude-code/technical-devlog-scribe/skills/technical-devlog-scribe/SKILL.md @@ -0,0 +1,32 @@ +--- +name: technical-devlog-scribe +description: Generates a highly structured, objective technical summary of a development session. +version: 1.0.0 +user-invocable: true +author: Rootiest +--- + +# SKILL: Technical Devlog Scribe + +## Description +Generates a comprehensive, highly structured technical summary of a development session. This skill acts as an objective technical scribe, producing a reliable historical record optimized for future context loading and maintaining a single source of truth for project evolution. + +## System Directives +* **Tone & Style:** Maintain an objective, dense, and highly technical tone. Avoid conversational filler or fluff. +* **Accuracy:** Rely strictly on the actions, code snippets, and decisions discussed within the current session. Do not hallucinate external constraints. +* **File Routing:** The output must be saved directly to `AGENTS/devlogs/.md`. Ensure the filename is concise but descriptive (e.g., `AGENTS/devlogs/oauth2-token-refresh-fix.md`). + +## Required Output Structure + +The generated markdown file must adhere strictly to the following format: + +--- +**[START OF FILE FORMAT]** + +```yaml +--- +date: YYYY-MM-DD +title: +tags: [, , , ] +status: +--- diff --git a/install.sh b/install.sh deleted file mode 100755 index 3e1b740..0000000 --- a/install.sh +++ /dev/null @@ -1,377 +0,0 @@ -#!/usr/bin/env bash -# install.sh — AI Skill Installer -# Install skills from rootiest/rootiest-ai into Claude Code and/or Antigravity CLI -# -# Usage: bash <(curl -sL ) [OPTIONS] [SKILL...] - -set -euo pipefail - -# ── Constants ────────────────────────────────────────────────────────────────── -readonly BASE_URL="https://git.rootiest.dev/rootiest/rootiest-ai/raw/branch/main" -readonly REPO_URL="https://git.rootiest.dev/rootiest/rootiest-ai.git" -readonly API_URL="https://git.rootiest.dev/api/v1/repos/rootiest/rootiest-ai/contents/skills" - -# Overridable via environment -CLAUDE_SKILLS_DIR="${CLAUDE_SKILLS_DIR:-${HOME}/.claude/skills}" -# ANTIGRAVITY_SKILLS_DIR takes precedence; fall back to GEMINI_SKILLS_DIR for legacy support -ANTIGRAVITY_SKILLS_DIR="${ANTIGRAVITY_SKILLS_DIR:-${GEMINI_SKILLS_DIR:-${HOME}/.gemini/antigravity-cli/skills}}" - -# ── ANSI Colors (only when writing to a terminal) ───────────────────────────── -if [[ -t 1 ]]; then - BOLD=$'\033[1m' - BOLD_CYAN=$'\033[1;36m' - BOLD_GREEN=$'\033[1;32m' - GREEN=$'\033[0;32m' - YELLOW=$'\033[0;33m' - RED=$'\033[0;31m' - DIM=$'\033[2m' - RESET=$'\033[0m' -else - BOLD='' BOLD_CYAN='' BOLD_GREEN='' GREEN='' YELLOW='' RED='' DIM='' RESET='' -fi - -# ── State ───────────────────────────────────────────────────────────────────── -INSTALL_CLAUDE=false -INSTALL_ANTIGRAVITY=false -INSTALL_ALL=false -LIST_SKILLS=false -declare -a SKILLS=() -DESCRIPTIONS_JSON="" -TMP_DIR="" - -# ── Cleanup ─────────────────────────────────────────────────────────────────── -cleanup() { - if [[ -n "${TMP_DIR:-}" && -d "${TMP_DIR:-}" ]]; then - rm -rf "$TMP_DIR" - fi -} -trap cleanup EXIT - -# ── Output Helpers ──────────────────────────────────────────────────────────── -info() { printf " ${BOLD_CYAN}→${RESET} %b\n" "$*"; } -ok() { printf " ${BOLD_GREEN}✓${RESET} %b\n" "$*"; } -warn() { printf " ${YELLOW}⚠${RESET} %b\n" "$*" >&2; } -die() { printf " ${RED}✗ ERROR:${RESET} %b\n" "$*" >&2; exit 1; } -sep() { printf "${BOLD_CYAN}%s${RESET}\n" "──────────────────────────────────────────"; } - -# ── Help ────────────────────────────────────────────────────────────────────── -show_help() { - printf "\n" - printf "${BOLD_CYAN} AI Skill Installer${RESET}\n" - printf "${DIM} Install skills from rootiest/rootiest-ai into Claude Code and Antigravity CLI${RESET}\n" - printf "\n" - printf "${BOLD} USAGE${RESET}\n" - printf " install.sh [OPTIONS] [SKILL...]\n" - printf " bash <(curl -sL /install.sh) [OPTIONS] [SKILL...]\n" - printf "\n" - printf "${BOLD} TOOL TARGETS${RESET}\n" - printf " ${GREEN}-c, --claude${RESET} Install into Claude Code (\$CLAUDE_SKILLS_DIR)\n" - printf " ${GREEN}-g, --antigravity${RESET} Install into Antigravity CLI (\$ANTIGRAVITY_SKILLS_DIR)\n" - printf " ${DIM} (default: install for both tools)${RESET}\n" - printf "\n" - printf "${BOLD} SKILL SELECTION${RESET}\n" - printf " ${GREEN}-a, --all${RESET} Install every skill in the repository\n" - printf " ${GREEN}SKILL...${RESET} One or more skill names (folder names under skills/)\n" - printf " ${DIM} Specific names always override --all${RESET}\n" - printf "\n" - printf "${BOLD} OTHER${RESET}\n" - printf " ${GREEN}-l, --list${RESET} List all available skills and exit\n" - printf " ${GREEN}-h, --help${RESET} Show this help page\n" - printf "\n" - printf "${BOLD} EXAMPLES${RESET}\n" - printf " ${DIM}# Install all skills for both tools${RESET}\n" - printf " install.sh --all\n" - printf "\n" - printf " ${DIM}# Install one skill, Claude Code only${RESET}\n" - printf " install.sh --claude my-skill\n" - printf "\n" - printf " ${DIM}# Install two skills, Antigravity CLI only${RESET}\n" - printf " install.sh --antigravity skill-one skill-two\n" - printf "\n" - printf " ${DIM}# Named skills override --all (only those listed are installed)${RESET}\n" - printf " install.sh --all --claude skill-name\n" - printf "\n" - printf "${BOLD} ENVIRONMENT${RESET}\n" - printf " ${GREEN}CLAUDE_SKILLS_DIR${RESET} Claude skills directory (default: ~/.claude/skills)\n" - printf " ${GREEN}ANTIGRAVITY_SKILLS_DIR${RESET} Antigravity skills directory (default: ~/.gemini/antigravity-cli/skills)\n" - printf " ${DIM} Falls back to GEMINI_SKILLS_DIR if set (legacy support)${RESET}\n" - printf "\n" - printf "${BOLD} REPOSITORY${RESET}\n" - printf " ${DIM}%s${RESET}\n" "$REPO_URL" - printf "\n" -} - -# ── Dependency Check ────────────────────────────────────────────────────────── -check_deps() { - local -a missing=() - command -v curl &>/dev/null || missing+=(curl) - command -v git &>/dev/null || missing+=(git) - if [[ ${#missing[@]} -gt 0 ]]; then - die "Missing required tools: ${missing[*]}" - fi -} - -# ── Validate Skill Name ─────────────────────────────────────────────────────── -validate_skill_name() { - local name="$1" - if [[ -z "$name" ]]; then die "Skill name cannot be empty."; fi - if [[ "$name" == *".."* ]]; then die "Invalid skill name (path traversal): '${name}'"; fi - if [[ "$name" == *"/"* ]]; then die "Invalid skill name (contains slash): '${name}'"; fi -} - -# ── Parse Directory Names from Gitea API JSON ───────────────────────────────── -# Supports jq, python3, or no-dep grep/awk fallback -parse_dir_names() { - local json="$1" - - if command -v jq &>/dev/null; then - printf '%s' "$json" | jq -r '.[] | select(.type == "dir") | .name' - return - fi - - if command -v python3 &>/dev/null; then - printf '%s' "$json" | python3 -c \ - "import sys,json; [print(e['name']) for e in json.load(sys.stdin) if e.get('type')=='dir']" - return - fi - - # Awk fallback: track name/type fields within each JSON object token stream. - # Reliable for single-line (minified) Gitea API responses. - printf '%s' "$json" | tr ',' '\n' | awk -F'"' ' - /^[[:space:]]*"name"/ { name = $4 } - /^[[:space:]]*"type".*"dir"/ { if (name != "") print name; name = "" } - ' -} - -# ── Discover Available Skills ───────────────────────────────────────────────── -discover_skills() { - info "Discovering available skills from repository..." - - # Attempt 1: Gitea contents API - local api_response - api_response=$(curl -sf --max-time 15 "$API_URL" 2>/dev/null) || api_response="" - - if [[ -n "$api_response" ]]; then - mapfile -t SKILLS < <(parse_dir_names "$api_response" 2>/dev/null | grep -v '^$' || true) - fi - - # Attempt 2: Sparse/no-checkout git clone — no JSON parsing required - if [[ ${#SKILLS[@]} -eq 0 ]]; then - warn "API unavailable or returned no results — falling back to git ls-tree..." - TMP_DIR=$(mktemp -d) - if ! git clone \ - --quiet \ - --depth=1 \ - --filter=blob:none \ - --no-checkout \ - "$REPO_URL" \ - "${TMP_DIR}/repo" 2>/dev/null; then - die "Cannot reach repository.\n URL: ${REPO_URL}\n Check your network connection." - fi - mapfile -t SKILLS < <( - git -C "${TMP_DIR}/repo" ls-tree --name-only HEAD "skills/" 2>/dev/null \ - | grep -v '^\.' \ - | grep -v '^$' \ - || true - ) - fi - - if [[ ${#SKILLS[@]} -eq 0 ]]; then - die "No skills found in repository. Verify the skills/ directory exists:\n ${REPO_URL}" - fi - - ok "Found ${#SKILLS[@]} skill(s): ${DIM}${SKILLS[*]}${RESET}" -} - -# ── Fetch & Query Skill Descriptions ───────────────────────────────────────── -fetch_descriptions() { - # Prefer a local descriptions.json when running from a clone - local script_dir - script_dir="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" 2>/dev/null && pwd)" || script_dir="" - if [[ -n "$script_dir" && -f "${script_dir}/descriptions.json" ]]; then - DESCRIPTIONS_JSON=$(<"${script_dir}/descriptions.json") - return - fi - - local url="${BASE_URL}/descriptions.json" - DESCRIPTIONS_JSON=$(curl -sf --max-time 10 "$url" 2>/dev/null) || DESCRIPTIONS_JSON="" -} - -get_description() { - local skill_name="$1" - if [[ -z "$DESCRIPTIONS_JSON" ]]; then return; fi - - if command -v jq &>/dev/null; then - printf '%s' "$DESCRIPTIONS_JSON" | jq -r --arg n "$skill_name" '.[$n] // empty' - return - fi - - if command -v python3 &>/dev/null; then - printf '%s' "$DESCRIPTIONS_JSON" | python3 -c \ - "import sys,json; d=json.load(sys.stdin); print(d.get('${skill_name}',''))" - return - fi - - # awk fallback: relies on pretty-printed JSON with one key per line - printf '%s' "$DESCRIPTIONS_JSON" | awk -F'"' -v key="$skill_name" '$2 == key { print $4 }' -} - -# ── List Available Skills ───────────────────────────────────────────────────── -list_skills() { - check_deps - discover_skills - fetch_descriptions - printf "\n" - sep - printf " ${BOLD}Available skills${RESET}\n" - sep - printf "\n" - for skill in "${SKILLS[@]}"; do - local desc - desc=$(get_description "$skill") - if [[ -n "$desc" ]]; then - printf " ${GREEN}•${RESET} ${BOLD}%s${RESET}\n ${DIM}%s${RESET}\n\n" "$skill" "$desc" - else - printf " ${GREEN}•${RESET} %s\n" "$skill" - fi - done - printf "\n" -} - -# ── Fetch a Single Skill's SKILL.md ────────────────────────────────────────── -fetch_skill() { - local skill_name="$1" - local url="${BASE_URL}/skills/${skill_name}/SKILL.md" - if ! curl -sf --max-time 30 "$url" 2>/dev/null; then - warn "Download failed: ${url}" - return 1 - fi -} - -# ── Install to Claude Code ──────────────────────────────────────────────────── -install_to_claude() { - local skill_name="$1" - local dest="${CLAUDE_SKILLS_DIR}/${skill_name}/SKILL.md" - - info "[Claude] Downloading ${BOLD}${skill_name}${RESET}..." - - local content - if ! content=$(fetch_skill "$skill_name"); then - warn "[Claude] Skipping '${skill_name}' — download failed." - return 1 - fi - - mkdir -p "${CLAUDE_SKILLS_DIR}/${skill_name}" - printf '%s\n' "$content" > "$dest" - ok "[Claude] ${BOLD}${skill_name}${RESET} → ${DIM}${dest}${RESET}" -} - -# ── Install to Antigravity CLI ──────────────────────────────────────────────── -install_to_antigravity() { - local skill_name="$1" - local dest="${ANTIGRAVITY_SKILLS_DIR}/${skill_name}/SKILL.md" - - info "[Antigravity] Downloading ${BOLD}${skill_name}${RESET}..." - - local content - if ! content=$(fetch_skill "$skill_name"); then - warn "[Antigravity] Skipping '${skill_name}' — download failed." - return 1 - fi - - mkdir -p "${ANTIGRAVITY_SKILLS_DIR}/${skill_name}" - printf '%s\n' "$content" > "$dest" - ok "[Antigravity] ${BOLD}${skill_name}${RESET} → ${DIM}${dest}${RESET}" -} - -# ── Main ────────────────────────────────────────────────────────────────────── -main() { - if [[ $# -eq 0 ]]; then show_help; exit 0; fi - - local -a positional=() - - # ── Parse Arguments ──────────────────────────────────────────────────────── - while [[ $# -gt 0 ]]; do - case "$1" in - -h|--help) show_help; exit 0 ;; - -l|--list) LIST_SKILLS=true ;; - -c|--claude) INSTALL_CLAUDE=true ;; - -g|--antigravity|--gemini) INSTALL_ANTIGRAVITY=true ;; - -a|--all) INSTALL_ALL=true ;; - -*) die "Unknown option: '${1}'. Run with -h for help." ;; - *) positional+=("$1") ;; - esac - shift - done - - if $LIST_SKILLS; then list_skills; exit 0; fi - - # Default: install for both tools when neither is specified - if ! $INSTALL_CLAUDE && ! $INSTALL_ANTIGRAVITY; then - INSTALL_CLAUDE=true - INSTALL_ANTIGRAVITY=true - fi - - check_deps - - # ── Skill Resolution ─────────────────────────────────────────────────────── - # Named positional arguments always take precedence over --all - if [[ ${#positional[@]} -gt 0 ]]; then - SKILLS=("${positional[@]}") - for skill in "${SKILLS[@]}"; do - validate_skill_name "$skill" - done - elif $INSTALL_ALL; then - discover_skills - else - warn "No skills specified. Provide skill name(s) or use --all." - printf "\n" - show_help - exit 1 - fi - - # ── Target Label for Display ────────────────────────────────────────────── - local target_label - if $INSTALL_CLAUDE && $INSTALL_ANTIGRAVITY; then - target_label="Claude Code + Antigravity CLI" - elif $INSTALL_CLAUDE; then - target_label="Claude Code" - else - target_label="Antigravity CLI" - fi - - # ── Header ──────────────────────────────────────────────────────────────── - printf "\n" - sep - printf " ${BOLD}%d skill(s)${RESET} → %s\n" "${#SKILLS[@]}" "$target_label" - sep - printf "\n" - - # ── Install Loop ────────────────────────────────────────────────────────── - local errors=0 - for skill in "${SKILLS[@]}"; do - printf " ${BOLD}%s${RESET}\n" "$skill" - if $INSTALL_CLAUDE; then - install_to_claude "$skill" || errors=$((errors + 1)) - fi - if $INSTALL_ANTIGRAVITY; then - install_to_antigravity "$skill" || errors=$((errors + 1)) - fi - printf "\n" - done - - # ── Summary ─────────────────────────────────────────────────────────────── - sep - if [[ $errors -eq 0 ]]; then - printf " ${BOLD_GREEN}✓ All skills installed successfully.${RESET}\n" - else - printf " ${YELLOW}⚠ %d installation(s) failed.${RESET} Review warnings above.\n" "$errors" - fi - sep - printf "\n" - - if [[ $errors -gt 0 ]]; then exit 1; fi - return 0 -} - -main "$@" diff --git a/plugins/core-essentials/plugin.json b/plugins/core-essentials/plugin.json new file mode 100644 index 0000000..e9165e1 --- /dev/null +++ b/plugins/core-essentials/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "core-essentials", + "description": "Core cross-tool utilities, including delegation to the Antigravity CLI (agy) for grounded research and large multi-file audits.", + "version": "1.0.0", + "author": "Rootiest" +} diff --git a/plugins/core-essentials/skills/delegate-agy/SKILL.md b/plugins/core-essentials/skills/delegate-agy/SKILL.md index 4852a03..c2d9290 100644 --- a/plugins/core-essentials/skills/delegate-agy/SKILL.md +++ b/plugins/core-essentials/skills/delegate-agy/SKILL.md @@ -1,3 +1,11 @@ +--- +name: delegate-agy +description: Delegates a subtask to the Antigravity CLI (agy) when the user wants a second opinion, external grounded research, or needs a large multi-file audit (>500 lines) processed without bloating the current context. +version: 1.0.0 +user-invocable: true +author: Rootiest +--- + # Antigravity Subagent Delegation (`delegate-agy`) When the user asks for a second opinion, external grounded research, or when a task requires processing large multi-file audits (>500 lines) that would bloat context, delegate the subtask to `agy`. diff --git a/plugins/date-time/plugin.json b/plugins/date-time/plugin.json new file mode 100644 index 0000000..9910091 --- /dev/null +++ b/plugins/date-time/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "date-time", + "description": "Retrieves the exact current date and time for time-sensitive queries, scheduling, duration calculations, and validating chronological context.", + "version": "1.0.0", + "author": "Rootiest" +} diff --git a/plugins/date-time/skills/date-time/SKILL.md b/plugins/date-time/skills/date-time/SKILL.md new file mode 100644 index 0000000..a3ac53e --- /dev/null +++ b/plugins/date-time/skills/date-time/SKILL.md @@ -0,0 +1,43 @@ +--- +name: date-time +description: Retrieves the exact current date and time for time-sensitive queries, scheduling, duration calculations, and validating chronological context. +version: 1.0.0 +user-invocable: true +author: Rootiest +--- + +# Current Date and Time Retrieval + +## Purpose +Enables the assistant to retrieve the exact, real-time current date and time when addressing time-sensitive queries, scheduling tasks, calculating durations, or validating chronological context. + +## Trigger Conditions +Activate this skill whenever the user's prompt: +* Explicitly asks for the current date, day, time, or year. +* References relative time expressions (e.g., "today", "yesterday", "next week", "recently"). +* Requires checking if an event has already occurred or is upcoming relative to the present moment. +* Needs to calculate an age, duration, or countdown from the present day. + +## Tool Definition + +### `get_current_datetime` +* **Description**: Executes the system `date` command to fetch the current local timestamp, timezone, and calendar date. +* **Parameters**: None required. + +## Execution Workflow +1. **Detect**: Recognize a time-sensitive trigger in the user's input. +2. **Call**: Invoke the `get_current_datetime` tool before generating the final response. +3. **Process**: Use the returned timestamp to anchor your temporal reasoning. +4. **Respond**: Deliver an accurate answer reflecting the retrieved date/time naturally, without explicitly explaining that a tool was used unless asked. + +## Examples + +### Example 1 +* **User**: "What day of the week is it today?" +* **Assistant Action**: Invoke `get_current_datetime`. +* **Response**: "Today is [Day of Week], [Date]." + +### Example 2 +* **User**: "Is the 2026 World Cup happening this month?" +* **Assistant Action**: Invoke `get_current_datetime`. +* **Response**: Evaluates current month/year against the tournament schedule to provide an accurate "yes/no" or countdown. diff --git a/plugins/docs-sync-audit/plugin.json b/plugins/docs-sync-audit/plugin.json new file mode 100644 index 0000000..7b1f864 --- /dev/null +++ b/plugins/docs-sync-audit/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "docs-sync-audit", + "description": "Analyzes repository delta since the last documentation update and synchronizes the Single Source of Truth (docs/, wiki, or README) with the current codebase state.", + "version": "1.3.0", + "author": "Rootiest" +} diff --git a/plugins/docs-sync-audit/skills/docs-sync-audit/SKILL.md b/plugins/docs-sync-audit/skills/docs-sync-audit/SKILL.md new file mode 100644 index 0000000..c7966a6 --- /dev/null +++ b/plugins/docs-sync-audit/skills/docs-sync-audit/SKILL.md @@ -0,0 +1,48 @@ +--- +name: docs-sync-audit +description: Analyzes repository delta since the last documentation update and synchronizes the Single Source of Truth (docs/, wiki, or README) with the current codebase state. +version: 1.3.0 +user-invocable: true +author: Rootiest +--- + +# Documentation Synchronization & Audit Skill + +## **Objective** +To ensure project documentation accurately reflects the current state of the codebase by identifying the "Single Source of Truth" (SSoT) (e.g., a `docs/` directory or wiki) and prioritizing updates there. The `README.md` is updated concurrently only for high-level changes or if it serves as the SSoT. + +## **Execution Protocol** + +### **Phase 0: SSoT Discovery** +1. **Locate Documentation Root**: Scan the repository structure for dedicated documentation directories (e.g., `docs/`, `wiki/`, `website/docs/`) or configuration files (e.g., `mkdocs.yml`, `docusaurus.config.js`). +2. **Establish SSoT**: If a dedicated documentation structure exists, designate it as the SSoT. If absent, fall back to `README.md` as the primary SSoT. + +### **Phase 1: Delta Analysis** +1. **Time-Travel Check**: Locate the last commit where the SSoT files were modified. +2. **Feature Diff**: Analyze all code changes (files added, functions modified, dependencies updated) from that commit to the present `HEAD`. +3. **Extraction**: Identify new environment variables, CLI flags, installation steps, or logic changes that are not yet documented. + +### **Phase 2: The Pruning & Update Audit** +Perform a targeted comparison of the SSoT against the current code: +* **Route Updates**: Direct detailed API, configuration, and architectural updates to their respective files within the SSoT (`docs/` or wiki). +* **Prune**: Remove any setup steps, dependencies, or "Coming Soon" features from the SSoT that no longer exist or have been replaced. +* **Correct**: Update version numbers, file paths, and command-line examples to match the current implementation. +* **Synthesize**: Add concise documentation for new features identified in Phase 1. +* **README Alignment**: If `docs/` is the SSoT, update the `README.md` *only* to reflect critical, high-level changes (e.g., Quick Start, Installation) or to ensure it properly links to the newly updated sections in the SSoT. + +### **Phase 3: Structural Integrity Check** +Ensure the updated SSoT (and README, if applicable) includes or updates these critical sections: +1. **Quick Start**: Are the commands (e.g., `cargo run`, `npm start`) still the primary entry points? +2. **Configuration**: Are all current `.env` or config keys listed? +3. **Usage Examples**: Do the provided code snippets actually compile/run with the current API? + +## **Constraints & Rules** +* **Hierarchy Enforcement**: Never duplicate deep technical documentation in the README if a `docs/` folder exists. Use the README as a high-level landing page that points to the SSoT. +* **Minimalism**: Maintain the existing tone of the documentation. Do not add "fluff" or marketing language unless the original document uses it. +* **No Hallucinations**: If a feature's purpose is unclear from the code diff, add a `TODO` comment or ask the user for clarification rather than guessing. +* **Markdown Standards**: Use standard GFM (GitHub Flavored Markdown) or MDX if applicable to the SSoT. Ensure all code blocks have the correct language identifier for syntax highlighting. + +## **Trigger Scenarios** +* **Direct Command:** The user invokes `/docs-sync-audit`, `/readme-sync-audit`, or `/update-docs`. +* **Natural Language:** User says "Update the docs", "Sync the wiki", or "Sync the README with my recent changes." +* **Contextual Suggestion:** Trigger automatically if the model detects significant changes to public APIs, CLI arguments, or environment variables without a corresponding documentation update. diff --git a/plugins/git-publish-workflow/plugin.json b/plugins/git-publish-workflow/plugin.json new file mode 100644 index 0000000..128244a --- /dev/null +++ b/plugins/git-publish-workflow/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "git-publish-workflow", + "description": "Automates branching, conventional commits, testing, and PR creation for uncommitted or staged work. Handles both independent and stacked PRs.", + "version": "1.2.0", + "author": "Rootiest" +} diff --git a/plugins/git-publish-workflow/skills/git-publish-workflow/SKILL.md b/plugins/git-publish-workflow/skills/git-publish-workflow/SKILL.md new file mode 100644 index 0000000..3f78156 --- /dev/null +++ b/plugins/git-publish-workflow/skills/git-publish-workflow/SKILL.md @@ -0,0 +1,49 @@ +--- +name: git-publish-workflow +description: Automates branching, conventional commits, testing, and PR creation for uncommitted or staged work. Handles both independent and stacked PRs. +version: 1.2.0 +user-invocable: true +author: Rootiest +--- + +# Git Publish & PR Workflow + +## **Objective** +To provide a hands-off, end-to-end automation for moving local changes into a formal Pull Request, ensuring code quality through automated and manual verification steps, while intelligently routing Stacked PRs. + +## **Execution Protocol** + +### **Phase 1: Scope & Base Determination** +Before execution, check the local git state: +1. **Scope Check**: Determine the work boundary: + * **Case A (Partial):** If staged changes exist, operate **ONLY** on staged changes. + * **Case B (Full):** If no changes are staged, operate on **ALL** modified/untracked files. +2. **Base Branch Detection**: Identify the currently checked-out branch. + * **Independent PR**: If the current branch is `main` (or `master`), the new branch will be based on `main`. The PR target will be `main`. + * **Stacked PR**: If the current branch is a feature branch (e.g., `feat-a`), assume the new changes are dependent. The new branch will be created from the current branch. The PR target will be the current feature branch (NOT `main`). + * *Safety Check*: If creating a Stacked PR, output a brief terminal message stating: "Detected active feature branch. Stacking new PR on top of `[current-branch-name]`." + +### **Phase 2: The "Safe-Commit" Sequence** +1. **Branching**: Generate a `kebab-case` branch name (e.g., `feat-auth-logic` or `fix-header-css`) based off the branch determined in Phase 1. +2. **Naming**: Use **Conventional Commits** for the message (e.g., `feat(ui): add logout button`). +3. **Verification**: + * Identify the project type (e.g., Rust/Cargo, Python/Poetry, Node/NPM). + * Run the primary `test`, `lint`, or `build` command. + * **Abort Policy**: If verification fails, stop the sequence and report the error. Do not push. + +### **Phase 3: Remote Integration** +1. **Push**: Upload the new branch to `origin`. +2. **PR Creation**: Open a Pull Request targeting the base branch determined in Phase 1 (either `main` or the parent feature branch). +3. **Documentation**: Populate the PR description with: + * **Summary**: A high-level overview of "Why" and "What." + * **Manual Verification Checklist**: Provide a Markdown list (`- [ ]`) of 3-5 tactical steps for a human to verify the change in a live environment. + +## **Constraints & Rules** +* **Atomic Commits**: If multiple distinct features are found in the scope, suggest splitting the work instead of one giant commit. +* **No Force Push**: Never use `--force` unless explicitly requested in the follow-up prompt. +* **Clean State**: Ensure the workflow ends with the user on the new branch, not the original branch. + +## **Trigger Scenarios** +* User says: "Ship this." +* User says: "Make a PR for my current changes." +* Invoked via `/git-publish-workflow`. diff --git a/plugins/readme-sync-audit/plugin.json b/plugins/readme-sync-audit/plugin.json new file mode 100644 index 0000000..1429967 --- /dev/null +++ b/plugins/readme-sync-audit/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "readme-sync-audit", + "description": "Analyzes repository delta since the last documentation update and synchronizes the README.md with the current codebase state.", + "version": "1.2.0", + "author": "Rootiest" +} diff --git a/plugins/readme-sync-audit/skills/readme-sync-audit/SKILL.md b/plugins/readme-sync-audit/skills/readme-sync-audit/SKILL.md new file mode 100644 index 0000000..b7f7bc0 --- /dev/null +++ b/plugins/readme-sync-audit/skills/readme-sync-audit/SKILL.md @@ -0,0 +1,41 @@ +--- +name: readme-sync-audit +description: Analyzes repository delta since the last documentation update and synchronizes the README.md with the current codebase state. +version: 1.2.0 +user-invocable: true +author: Rootiest +--- + +# README Synchronization & Audit Skill + +## **Objective** +To ensure the `README.md` serves as a "Single Source of Truth" by programmatically aligning documentation with the actual state of the codebase. This skill prioritizes accuracy and the removal of obsolete instructions. + +## **Execution Protocol** + +### **Phase 1: Delta Analysis** +1. **Time-Travel Check**: Locate the last commit where `README.md` was modified. +2. **Feature Diff**: Analyze all code changes (files added, functions modified, dependencies updated) from that commit to the present `HEAD`. +3. **Extraction**: Identify new environment variables, CLI flags, installation steps, or logic changes that are not yet documented. + +### **Phase 2: The Pruning & Update Audit** +Perform a line-by-line comparison of the existing README against the current code: +* **Prune**: Remove any setup steps, dependencies, or "Coming Soon" features that no longer exist or have been replaced. +* **Correct**: Update version numbers, file paths, and command-line examples to match the current implementation. +* **Synthesize**: Add concise documentation for new features identified in Phase 1. + +### **Phase 3: Structural Integrity Check** +Ensure the updated README includes (or updates) these critical sections: +1. **Quick Start**: Are the commands (e.g., `cargo run`, `npm start`) still the primary entry points? +2. **Configuration**: Are all current `.env` or config keys listed? +3. **Usage Examples**: Do the provided code snippets actually compile/run with the current API? + +## **Constraints & Rules** +* **Minimalism**: Maintain the existing tone of the README. Do not add "fluff" or marketing language unless the original document uses it. +* **No Hallucinations**: If a feature's purpose is unclear from the code diff, add a `TODO` comment or ask the user for clarification rather than guessing. +* **Markdown Standards**: Use standard GFM (GitHub Flavored Markdown). Ensure all code blocks have the correct language identifier for syntax highlighting. + +## **Trigger Scenarios** +* **Direct Command:** The user invokes `/readme-sync-audit` or `/update-docs`. +* **Natural Language:** User says "Update the docs" or "Sync the README with my recent changes." +* **Contextual Suggestion:** Trigger automatically if the model detects significant changes to public APIs, CLI arguments, or environment variables without a corresponding documentation update. diff --git a/plugins/ship-it/plugin.json b/plugins/ship-it/plugin.json new file mode 100644 index 0000000..356d960 --- /dev/null +++ b/plugins/ship-it/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "ship-it", + "description": "Runs a comprehensive pre-flight audit, syncs the README, and publishes the changes to a new PR.", + "version": "1.0.0", + "author": "Rootiest" +} diff --git a/plugins/ship-it/skills/ship-it/SKILL.md b/plugins/ship-it/skills/ship-it/SKILL.md new file mode 100644 index 0000000..4da824e --- /dev/null +++ b/plugins/ship-it/skills/ship-it/SKILL.md @@ -0,0 +1,25 @@ +--- +name: ship-it +description: Runs a comprehensive pre-flight audit, syncs the README, and publishes the changes to a new PR. +version: 1.0.0 +user-invocable: true +author: Rootiest +--- + +# /ship-it + +Instructions: +Execute the following two phases sequentially. Do not proceed to Phase 2 unless Phase 1 completes successfully. + +1. Phase 1: Documentation Sync & Code Audit + - Act as the `/docs-sync-audit` skill. + - Scan all file changes since the last README edit and update the README to ensure it accurately reflects the current state of the codebase. + - Audit all code files for any syntax errors, regressions, or issues. + - CRITICAL: If any code errors or breaking issues are discovered during the audit, HALT the workflow immediately and report them to the user. Do not proceed to publishing. + +2. Phase 2: Git Publish Workflow + - Act as the `/git-publish-workflow` skill. + - Create a new, descriptively named git branch. + - Stage and commit all pending changes (including the newly updated README from Phase 1). + - Push the branch to the remote repository. + - Generate a Pull Request (PR) from the new branch into 'main'. diff --git a/plugins/systematic-enumeration/plugin.json b/plugins/systematic-enumeration/plugin.json new file mode 100644 index 0000000..aaf1e32 --- /dev/null +++ b/plugins/systematic-enumeration/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "systematic-enumeration", + "description": "Forces element-by-element verification for finite sets to prevent counting errors.", + "version": "1.0.0", + "author": "Rootiest" +} diff --git a/plugins/systematic-enumeration/skills/systematic-enumeration/SKILL.md b/plugins/systematic-enumeration/skills/systematic-enumeration/SKILL.md new file mode 100644 index 0000000..efdc52b --- /dev/null +++ b/plugins/systematic-enumeration/skills/systematic-enumeration/SKILL.md @@ -0,0 +1,40 @@ +--- +name: systematic-enumeration +description: Forces element-by-element verification for finite sets to prevent counting errors. +version: 1.0.0 +user-invocable: true +author: Rootiest +--- + +# Systematic Enumeration & Verification Skill + +## **Objective** +To eliminate heuristic errors and "hallucinated patterns" when analyzing finite sets. This protocol overrides the model's tendency toward "holistic recognition" in favor of systematic, element-by-element verification. + +## **Execution Protocol** +When this skill is triggered, you MUST NOT provide a direct answer immediately. Follow these three phases to ensure accuracy: + +### **Phase 1: Set Definition** +Explicitly define the boundaries and members of the finite set being analyzed. +* **Requirement:** List the members before performing any tests. +* *Example:* "The set consists of the files in the `/src` directory: [main.rs, utils.rs, types.rs]." + +### **Phase 2: Atomic Element Testing (O(n))** +Iterate through every item in the set. For each item, perform a literal check against the target property. +* **Format:** Use a list or table to force token-level focus on each element. +* **Structure:** `[Item] -> [Logic/Observation] -> [Boolean Result]` +* *Note:* For character-based tests, split the string into individual characters to bypass tokenization bias. + +### **Phase 3: Reduction & Summation** +Aggregate the `True` results from Phase 2 to derive the final answer. +* **Self-Correction:** Verify that the count of items tested in Phase 2 exactly matches the count of the set defined in Phase 1. If there is a mismatch, restart Phase 2. + +## **Constraints & Anti-Patterns** +* **STRICT BAN on Heuristics:** Do not use phrases like "typically," "usually," or "it appears that." +* **NO Pattern Matching:** Do not extrapolate a rule (e.g., "every other item") as a substitute for testing every item. +* **Computational Justification:** Treat the process as an $O(n)$ operation where $n$ is small enough that accuracy is the only priority. + +## **Trigger Scenarios** +* Counting specific characters or substrings within a string. +* Verifying property adherence across a list of variables, files, or objects. +* Membership testing in sets where false negatives are high-risk. diff --git a/plugins/technical-devlog-scribe/plugin.json b/plugins/technical-devlog-scribe/plugin.json new file mode 100644 index 0000000..3171855 --- /dev/null +++ b/plugins/technical-devlog-scribe/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "technical-devlog-scribe", + "description": "Generates a highly structured, objective technical summary of a development session.", + "version": "1.0.0", + "author": "Rootiest" +} diff --git a/plugins/technical-devlog-scribe/skills/technical-devlog-scribe/SKILL.md b/plugins/technical-devlog-scribe/skills/technical-devlog-scribe/SKILL.md new file mode 100644 index 0000000..c0f77f3 --- /dev/null +++ b/plugins/technical-devlog-scribe/skills/technical-devlog-scribe/SKILL.md @@ -0,0 +1,32 @@ +--- +name: technical-devlog-scribe +description: Generates a highly structured, objective technical summary of a development session. +version: 1.0.0 +user-invocable: true +author: Rootiest +--- + +# SKILL: Technical Devlog Scribe + +## Description +Generates a comprehensive, highly structured technical summary of a development session. This skill acts as an objective technical scribe, producing a reliable historical record optimized for future context loading and maintaining a single source of truth for project evolution. + +## System Directives +* **Tone & Style:** Maintain an objective, dense, and highly technical tone. Avoid conversational filler or fluff. +* **Accuracy:** Rely strictly on the actions, code snippets, and decisions discussed within the current session. Do not hallucinate external constraints. +* **File Routing:** The output must be saved directly to `AGENTS/devlogs/.md`. Ensure the filename is concise but descriptive (e.g., `AGENTS/devlogs/oauth2-token-refresh-fix.md`). + +## Required Output Structure + +The generated markdown file must adhere strictly to the following format: + +--- +**[START OF FILE FORMAT]** + +```yaml +--- +date: YYYY-MM-DD +title: +tags: [, , , ] +status: +--- diff --git a/scripts/generate_plugins.py b/scripts/generate_plugins.py index 090d876..2a79a30 100755 --- a/scripts/generate_plugins.py +++ b/scripts/generate_plugins.py @@ -1,36 +1,62 @@ #!/usr/bin/env python3 -"""Generate per-agent plugin manifests from the skills/ SSoT. +"""Generate per-agent plugin/marketplace trees from the plugins/ SSoT. -Reads manifest.yaml (marketplace/org metadata + target list) and every -skills//SKILL.md (per-skill frontmatter), then regenerates: +A "plugin" is a directory under `plugins//` that may bundle any mix of: + + - plugin.json (required marker + metadata: name, description, version, author) + - skills//SKILL.md (0+ skills) + - hooks.json (canonical, Claude-shaped: {"": [, ...]}) + - mcp.json ({"mcpServers": {...}}, shared shape across targets) + - rules/AGENTS.md (agy-only; ignored by the Claude Code target) + - commands/*.md (Claude Code-only slash commands) + - agents/*.md (Claude Code-only subagents) + +One or more source roots (each containing its own `plugins/` directory) are +layered together — a later source overlays/overrides an earlier one on a +per-plugin, per-file basis. This is how a private repo (PII/tokens/local-only +plugins) can extend or override the public plugin set without either repo +knowing about the other's internals. + +Regenerates, under --out (default: repo root, i.e. today's committed paths): - - descriptions.json (from SKILL.md frontmatter) - .claude-plugin/marketplace.json (Claude Code target) - dist/agy/** (Antigravity CLI target) -Run with --check to only validate the SSoT (frontmatter, manifest.yaml) and -skip writing any output — used as the pull-request gate. +Run with --check to only validate the SSoT (plugin.json/SKILL.md frontmatter, +manifest.yaml) and skip writing any output — used as the pull-request gate. """ from __future__ import annotations +import argparse import json +import os import shutil +import stat +import subprocess import sys +import tempfile from pathlib import Path import yaml ROOT = Path(__file__).resolve().parent.parent -SKILLS_DIR = ROOT / "skills" MANIFEST_PATH = ROOT / "manifest.yaml" -REQUIRED_FRONTMATTER_FIELDS = ("name", "description", "version", "author") +REQUIRED_PLUGIN_FIELDS = ("name", "description") +REQUIRED_SKILL_FIELDS = ("name", "description") + +# agy only documents these five hook events; everything else is Claude-only. +AGY_GROUPED_EVENTS = ("PreToolUse", "PostToolUse") +AGY_FLAT_EVENTS = ("PreInvocation", "PostInvocation", "Stop") class ValidationError(Exception): pass +# ── Manifest / Frontmatter Loading ─────────────────────────────────────────── + + def load_manifest() -> dict: with MANIFEST_PATH.open("r", encoding="utf-8") as f: manifest = yaml.safe_load(f) @@ -40,121 +66,323 @@ def load_manifest() -> dict: return manifest -def parse_frontmatter(skill_md: Path) -> dict: +def load_json(path: Path) -> dict: + try: + return json.loads(path.read_text(encoding="utf-8")) + except json.JSONDecodeError as e: + raise ValidationError(f"{path}: invalid JSON ({e})") + + +def parse_skill_frontmatter(skill_md: Path) -> dict: text = skill_md.read_text(encoding="utf-8") if not text.startswith("---\n"): raise ValidationError(f"{skill_md}: missing YAML frontmatter delimiter") end = text.find("\n---", 4) if end == -1: raise ValidationError(f"{skill_md}: unterminated YAML frontmatter") - raw = text[4:end] - data = yaml.safe_load(raw) or {} - for field in REQUIRED_FRONTMATTER_FIELDS: + data = yaml.safe_load(text[4:end]) or {} + for field in REQUIRED_SKILL_FIELDS: if not data.get(field): raise ValidationError(f"{skill_md}: frontmatter missing required field '{field}'") return data -def discover_skills() -> list[dict]: - skills = [] - seen_names = set() - for skill_dir in sorted(SKILLS_DIR.iterdir()): - if not skill_dir.is_dir(): +# ── Source Discovery & Layering ────────────────────────────────────────────── + + +def collect_layers(source_roots: list[Path]) -> "dict[str, list[Path]]": + """Map plugin name -> ordered list of source dirs (base first, overlays after).""" + layers: dict[str, list[Path]] = {} + for root in source_roots: + plugins_dir = root / "plugins" + if not plugins_dir.is_dir(): continue - skill_md = skill_dir / "SKILL.md" - if not skill_md.exists(): - raise ValidationError(f"{skill_dir}: missing SKILL.md") - frontmatter = parse_frontmatter(skill_md) - name = frontmatter["name"] - if name != skill_dir.name: - raise ValidationError( - f"{skill_md}: frontmatter name '{name}' does not match directory name '{skill_dir.name}'" - ) - if name in seen_names: - raise ValidationError(f"duplicate skill name '{name}'") - seen_names.add(name) - skills.append({"dir": skill_dir, "frontmatter": frontmatter}) - if not skills: - raise ValidationError("no skills found under skills/") - return skills + for plugin_dir in sorted(plugins_dir.iterdir()): + if not plugin_dir.is_dir(): + continue + layers.setdefault(plugin_dir.name, []).append(plugin_dir) + if not layers: + raise ValidationError( + f"no plugins found under plugins/ in any source root: {[str(r) for r in source_roots]}" + ) + return layers -def gen_descriptions_json(skills: list[dict]) -> None: - descriptions = {s["frontmatter"]["name"]: s["frontmatter"]["description"] for s in skills} - out = ROOT / "descriptions.json" - out.write_text(json.dumps(descriptions, indent=2) + "\n", encoding="utf-8") +def merge_layers(layers: "dict[str, list[Path]]", workdir: Path) -> Path: + """Flatten each plugin's layers into a single merged directory (later wins).""" + merged_root = workdir / "merged-plugins" + merged_root.mkdir(parents=True, exist_ok=True) + for name, dirs in layers.items(): + dest = merged_root / name + dest.mkdir(parents=True, exist_ok=True) + for d in dirs: + shutil.copytree(d, dest, dirs_exist_ok=True) + return merged_root -def gen_claude_code(manifest: dict, skills: list[dict]) -> None: - plugin_dir = ROOT / ".claude-plugin" - plugin_dir.mkdir(exist_ok=True) - +def discover_plugins(merged_root: Path) -> list[dict]: plugins = [] - for s in skills: - fm = s["frontmatter"] + for plugin_dir in sorted(merged_root.iterdir()): + if not plugin_dir.is_dir(): + continue + manifest_path = plugin_dir / "plugin.json" + if not manifest_path.exists(): + raise ValidationError(f"{plugin_dir}: missing plugin.json") + meta = load_json(manifest_path) + if meta.get("name") != plugin_dir.name: + raise ValidationError( + f"{manifest_path}: name '{meta.get('name')}' does not match directory name '{plugin_dir.name}'" + ) + for field in REQUIRED_PLUGIN_FIELDS: + if not meta.get(field): + raise ValidationError(f"{manifest_path}: missing required field '{field}'") + + skills = [] + skills_dir = plugin_dir / "skills" + if skills_dir.is_dir(): + for skill_dir in sorted(skills_dir.iterdir()): + if not skill_dir.is_dir(): + continue + skill_md = skill_dir / "SKILL.md" + if not skill_md.exists(): + raise ValidationError(f"{skill_dir}: missing SKILL.md") + fm = parse_skill_frontmatter(skill_md) + if fm["name"] != skill_dir.name: + raise ValidationError( + f"{skill_md}: frontmatter name '{fm['name']}' does not match directory name '{skill_dir.name}'" + ) + skills.append({"dir": skill_dir, "frontmatter": fm}) + + hooks_path = plugin_dir / "hooks.json" + hooks = load_json(hooks_path) if hooks_path.exists() else None + + mcp_path = plugin_dir / "mcp.json" + mcp = load_json(mcp_path) if mcp_path.exists() else None + plugins.append( { - "name": fm["name"], - "description": fm["description"], - "source": "./", - "skills": [f"./skills/{fm['name']}"], + "dir": plugin_dir, + "meta": meta, + "skills": skills, + "hooks": hooks, + "mcp": mcp, + "rules_dir": plugin_dir / "rules" if (plugin_dir / "rules").is_dir() else None, + "commands_dir": plugin_dir / "commands" if (plugin_dir / "commands").is_dir() else None, + "agents_dir": plugin_dir / "agents" if (plugin_dir / "agents").is_dir() else None, } ) - plugins.append( - { - "name": manifest["bundle"]["id"], - "description": f"{manifest['marketplace']['description']} (all skills)", - "source": "./", - "skills": ["./skills/"], - } + seen = set() + for p in plugins: + name = p["meta"]["name"] + if name in seen: + raise ValidationError(f"duplicate plugin name '{name}'") + seen.add(name) + return plugins + + +# ── Private Repo Cloning ───────────────────────────────────────────────────── + +_ASKPASS_SCRIPT = """#!/bin/sh +case "$1" in + *sername*) echo "x-access-token" ;; + *) echo "$GIT_ASKPASS_TOKEN" ;; +esac +""" + + +def clone_private_repo(url: str, ref: str, token_env: str | None, workdir: Path) -> Path: + clone_dir = workdir / "private-repo" + env = os.environ.copy() + env["GIT_TERMINAL_PROMPT"] = "0" + + if token_env: + token = os.environ.get(token_env) + if not token: + raise ValidationError(f"--private-token-env={token_env} is not set in the environment") + askpass_path = workdir / "git-askpass.sh" + askpass_path.write_text(_ASKPASS_SCRIPT, encoding="utf-8") + askpass_path.chmod(askpass_path.stat().st_mode | stat.S_IEXEC) + env["GIT_ASKPASS"] = str(askpass_path) + env["GIT_ASKPASS_TOKEN"] = token + + result = subprocess.run( + ["git", "clone", "--quiet", "--depth", "1", "--branch", ref, url, str(clone_dir)], + env=env, + capture_output=True, + text=True, ) + if result.returncode != 0: + raise ValidationError(f"failed to clone private repo '{url}' (ref {ref}): {result.stderr.strip()}") + return clone_dir + + +# ── Hook / MCP Translation ─────────────────────────────────────────────────── + + +def translate_hooks_for_agy(hooks: dict, plugin_name: str) -> "dict | None": + agy_events: dict = {} + for event in AGY_GROUPED_EVENTS: + if event in hooks: + agy_events[event] = hooks[event] + for event in AGY_FLAT_EVENTS: + if event in hooks: + flat = [] + for group in hooks[event]: + flat.extend(group.get("hooks", [])) + agy_events[event] = flat + if not agy_events: + return None + return {plugin_name: agy_events} + + +def translate_mcp_for_agy(mcp: dict) -> dict: + servers = {} + for name, cfg in mcp.get("mcpServers", {}).items(): + if "command" in cfg: + out = {"command": cfg["command"]} + if "args" in cfg: + out["args"] = cfg["args"] + if "env" in cfg: + out["env"] = cfg["env"] + servers[name] = out + else: + url = cfg.get("serverUrl") or cfg.get("url") + if url: + servers[name] = {"serverUrl": url} + return {"mcpServers": servers} + + +# ── Claude Code Target ─────────────────────────────────────────────────────── + + +def write_claude_plugin(plugin: dict, dest: Path) -> None: + meta = plugin["meta"] + dest.mkdir(parents=True, exist_ok=True) + + claude_plugin_dir = dest / ".claude-plugin" + claude_plugin_dir.mkdir(exist_ok=True) + manifest = {"name": meta["name"]} + for field in ("description", "version", "author"): + if meta.get(field): + manifest[field] = meta[field] + (claude_plugin_dir / "plugin.json").write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8") + + if plugin["skills"]: + skills_out = dest / "skills" + for s in plugin["skills"]: + shutil.copytree(s["dir"], skills_out / s["frontmatter"]["name"]) + + for optional_dir in ("commands_dir", "agents_dir"): + src = plugin[optional_dir] + if src is not None: + shutil.copytree(src, dest / src.name) + + if plugin["hooks"] is not None: + hooks_out = dest / "hooks" + hooks_out.mkdir(exist_ok=True) + (hooks_out / "hooks.json").write_text( + json.dumps({"hooks": plugin["hooks"]}, indent=2) + "\n", encoding="utf-8" + ) + + if plugin["mcp"] is not None: + (dest / ".mcp.json").write_text(json.dumps(plugin["mcp"], indent=2) + "\n", encoding="utf-8") + + +def gen_claude_code(manifest: dict, plugins: list[dict], out_dir: Path) -> None: + # Generated output lives under dist/claude-code/ — never inside plugins/, + # which is the SSoT and must stay untouched by generation. + plugins_out = out_dir / "dist" / "claude-code" + if plugins_out.exists(): + shutil.rmtree(plugins_out) + plugins_out.mkdir(parents=True) + + marketplace_entries = [] + for p in plugins: + name = p["meta"]["name"] + write_claude_plugin(p, plugins_out / name) + marketplace_entries.append({"name": name, "source": f"./dist/claude-code/{name}"}) + + bundle_id = manifest["bundle"]["id"] + bundle_dest = plugins_out / bundle_id + (bundle_dest / ".claude-plugin").mkdir(parents=True) + (bundle_dest / ".claude-plugin" / "plugin.json").write_text( + json.dumps( + {"name": bundle_id, "description": f"{manifest['marketplace']['description']} (all skills)"}, + indent=2, + ) + + "\n", + encoding="utf-8", + ) + bundle_skills = bundle_dest / "skills" + for p in plugins: + for s in p["skills"]: + shutil.copytree(s["dir"], bundle_skills / s["frontmatter"]["name"]) + marketplace_entries.append({"name": bundle_id, "source": f"./dist/claude-code/{bundle_id}"}) marketplace = { "name": manifest["marketplace"]["name"], "owner": manifest["marketplace"]["owner"], "description": manifest["marketplace"]["description"], - "plugins": plugins, + "plugins": marketplace_entries, } - - out = plugin_dir / "marketplace.json" - out.write_text(json.dumps(marketplace, indent=2) + "\n", encoding="utf-8") + plugin_dir = out_dir / ".claude-plugin" + plugin_dir.mkdir(exist_ok=True) + (plugin_dir / "marketplace.json").write_text(json.dumps(marketplace, indent=2) + "\n", encoding="utf-8") -def gen_agy(manifest: dict, skills: list[dict]) -> None: - agy_dir = ROOT / "dist" / "agy" +# ── Antigravity CLI (agy) Target ───────────────────────────────────────────── + + +def write_agy_plugin(plugin: dict, dest: Path) -> None: + meta = plugin["meta"] + name = meta["name"] + dest.mkdir(parents=True, exist_ok=True) + (dest / "plugin.json").write_text(json.dumps({"name": name}, indent=2) + "\n", encoding="utf-8") + + if plugin["skills"]: + skills_out = dest / "skills" + for s in plugin["skills"]: + shutil.copytree(s["dir"], skills_out / s["frontmatter"]["name"]) + + if plugin["rules_dir"] is not None: + shutil.copytree(plugin["rules_dir"], dest / "rules") + + if plugin["hooks"] is not None: + translated = translate_hooks_for_agy(plugin["hooks"], name) + if translated is not None: + (dest / "hooks.json").write_text(json.dumps(translated, indent=2) + "\n", encoding="utf-8") + + if plugin["mcp"] is not None: + (dest / "mcp_config.json").write_text( + json.dumps(translate_mcp_for_agy(plugin["mcp"]), indent=2) + "\n", encoding="utf-8" + ) + + +def gen_agy(manifest: dict, plugins: list[dict], out_dir: Path) -> None: + agy_dir = out_dir / "dist" / "agy" if agy_dir.exists(): shutil.rmtree(agy_dir) agy_dir.mkdir(parents=True) - def write_plugin(plugin_name: str, description: str, members: list[dict]) -> None: - plugin_root = agy_dir / plugin_name - skills_out = plugin_root / "skills" - skills_out.mkdir(parents=True) - for s in members: - dest = skills_out / s["frontmatter"]["name"] - shutil.copytree(s["dir"], dest) - plugin_json = {"name": plugin_name, "description": description} - (plugin_root / "plugin.json").write_text( - json.dumps(plugin_json, indent=2) + "\n", encoding="utf-8" - ) + for p in plugins: + write_agy_plugin(p, agy_dir / p["meta"]["name"]) - for s in skills: - fm = s["frontmatter"] - write_plugin(fm["name"], fm["description"], [s]) + bundle_id = manifest["bundle"]["id"] + bundle_dest = agy_dir / bundle_id + bundle_dest.mkdir(parents=True) + (bundle_dest / "plugin.json").write_text(json.dumps({"name": bundle_id}, indent=2) + "\n", encoding="utf-8") + bundle_skills = bundle_dest / "skills" + for p in plugins: + for s in p["skills"]: + shutil.copytree(s["dir"], bundle_skills / s["frontmatter"]["name"]) - write_plugin( - manifest["bundle"]["id"], - f"{manifest['marketplace']['description']} (all skills)", - skills, - ) - - dist_readme = ROOT / "dist" / "README.md" + dist_readme = out_dir / "dist" / "README.md" dist_readme.write_text( "# Generated\n\n" "This directory is generated by `scripts/generate_plugins.py` from " - "`manifest.yaml` and `skills/`. Do not edit files here directly — " - "edit the source skill instead and regenerate.\n", + "`manifest.yaml` and `plugins/`. Do not edit files here directly — " + "edit the source plugin instead and regenerate.\n", encoding="utf-8", ) @@ -165,21 +393,99 @@ GENERATORS = { } +# ── Local Install Helper ───────────────────────────────────────────────────── + + +def install_local(manifest: dict, out_dir: Path) -> None: + home = Path.home() + mp_name = f"{manifest['marketplace']['name']}-private" + + claude_src = out_dir / ".claude-plugin" + if claude_src.exists(): + claude_dest = home / ".claude" / "plugins" / "marketplaces" / mp_name + if claude_dest.exists(): + shutil.rmtree(claude_dest) + claude_dest.mkdir(parents=True) + shutil.copytree(claude_src, claude_dest / ".claude-plugin") + shutil.copytree(out_dir / "dist" / "claude-code", claude_dest / "dist" / "claude-code") + print(f"Installed Claude Code marketplace locally: {claude_dest}") + print(f" /plugin marketplace add {claude_dest}") + + agy_src = out_dir / "dist" / "agy" + if agy_src.exists(): + print("For agy, add this entry to ~/.gemini/config/plugins.json:") + print(json.dumps({"entries": [{"path": str(agy_src)}]}, indent=2)) + + +# ── CLI ─────────────────────────────────────────────────────────────────────── + + +def parse_args(argv: list[str]) -> argparse.Namespace: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--check", action="store_true", help="validate only, write nothing") + parser.add_argument( + "--source", + action="append", + default=None, + help="a directory containing its own plugins/ folder; repeatable, layered in order (default: repo root)", + ) + parser.add_argument("--private-repo", help="git URL of a private overlay repo to clone and layer on top") + parser.add_argument("--private-ref", default="main", help="branch/tag to clone from --private-repo (default: main)") + parser.add_argument( + "--private-token-env", + help="name of an environment variable holding a token for --private-repo (read at run time, never taken as a literal value)", + ) + parser.add_argument( + "--out", + help="output directory (default: '.' for a pure public build, 'dist-private' when overlaying a private source)", + ) + parser.add_argument( + "--install-local", + action="store_true", + help="after generating, install the result into local Claude Code / agy config", + ) + return parser.parse_args(argv) + + def main() -> int: - check_only = "--check" in sys.argv[1:] + args = parse_args(sys.argv[1:]) + try: manifest = load_manifest() - skills = discover_skills() for target in manifest["targets"]: if target not in GENERATORS: raise ValidationError(f"manifest.yaml: unknown target '{target}'") - if check_only: - print(f"OK: {len(skills)} skill(s), {len(manifest['targets'])} target(s) validated") - return 0 - gen_descriptions_json(skills) - for target in manifest["targets"]: - GENERATORS[target](manifest, skills) - print(f"Generated plugins for {len(skills)} skill(s), targets: {', '.join(manifest['targets'])}") + + source_roots = [Path(s).expanduser().resolve() for s in (args.source or [str(ROOT)])] + has_overlay = bool(args.private_repo) or len(source_roots) > 1 + + with tempfile.TemporaryDirectory(prefix="rootiest-ai-build-") as tmp: + workdir = Path(tmp) + if args.private_repo: + private_dir = clone_private_repo(args.private_repo, args.private_ref, args.private_token_env, workdir) + source_roots.append(private_dir) + + layers = collect_layers(source_roots) + merged_root = merge_layers(layers, workdir) + plugins = discover_plugins(merged_root) + + if args.check: + print(f"OK: {len(plugins)} plugin(s), {len(manifest['targets'])} target(s) validated") + return 0 + + out_dir = Path(args.out).expanduser().resolve() if args.out else ( + (ROOT / "dist-private") if has_overlay else ROOT + ) + out_dir.mkdir(parents=True, exist_ok=True) + + for target in manifest["targets"]: + GENERATORS[target](manifest, plugins, out_dir) + + print(f"Generated plugins for {len(plugins)} plugin(s) -> {out_dir}, targets: {', '.join(manifest['targets'])}") + + if args.install_local: + install_local(manifest, out_dir) + return 0 except ValidationError as e: print(f"error: {e}", file=sys.stderr)