Skip to content

Latest commit

 

History

History
381 lines (317 loc) · 17.3 KB

File metadata and controls

381 lines (317 loc) · 17.3 KB

Contributing

Caution

First-time contributors: open a GitHub issue first. Do not open a pull request.

PRs from first-time contributors will be closed without review. Open an issue describing the bug, idea, or proposed change. Wait for a maintainer to acknowledge and agree on scope before writing code. Once you have at least one merged PR, this restriction no longer applies.

Why: this repo encodes opinionated, hand-tuned prompts. Drive-by PRs that tweak wording, add abstractions, or refactor "for cleanliness" cost more to review than they save. Issues are cheap. Aligned PRs ship.

Table of Contents

Ground Rules

  • One source of truth: src/. Hand-edit only files under src/skills/, src/agents/, src/hooks/, src/plugins/, and the top-level scripts/, tests/, docs/, and Makefile. Everything under dist/ and the generated root-level files (.claude-plugin/marketplace.json, .agents/plugins/marketplace.json, gemini-extension.json, AGENTS.md, root skills/agents/hooks symlinks) is regenerated by the compiler — do not edit by hand. dist/ is marked linguist-generated=true so GitHub hides it in diffs.
  • Bases are vendor-neutral. src/skills/<skill>/SKILL.md and src/agents/<agent>/AGENT.md describe intent in generic terms — no $ARGUMENTS, no Task(...), no mcp__*, no ${CLAUDE_*} variables, no Claude-specific preprocessor backticks. Put Claude-only behavior in src/skills/<skill>/claude/body.md (full replacement) or via mirror overlay. See Vendor-Neutral Body Checklist.
  • Run make build after changes. Generated artifacts must be in sync. CI runs make check (build + git diff --exit-code) and will fail on drift.
  • Surgical changes. Match existing style. Don't rename, restructure, or reformat unrelated files in the same PR.
  • No new dependencies without discussion. Open an issue first.

Setup

make setup    # Activate repo-tracked git hooks + install dev deps (uv-managed)

make setup sets git config core.hooksPath scripts/git-hooks so the versioned hooks in scripts/git-hooks/ run on every commit/push. The split:

  • pre-commit — fast path (~3 s): ruff + shellcheck + shfmt + markdownlint
    • plugin frontmatter validation + gitleaks on staged changes.
  • pre-push — heavy path (~30 s): regenerate all derived artifacts, fail on drift, run the full pytest suite. Mirrors what GitHub Actions does.

Skip a hook only when truly necessary: git commit --no-verify / git push --no-verify. CI will still enforce.

Required tools on PATH:

  • uv (Python toolchain)
  • shellcheck and shfmt (shell linting)
  • markdownlint-cli2 (optional but recommended)
  • bun or node if you plan to run make skill-evals*

Repository Layout

cc-thingz/
├── src/                               # ALL hand-edited sources
│   ├── skills/<skill>/
│   │   ├── SKILL.md                   # Vendor-neutral base (agentskills.io)
│   │   ├── scripts/, references/, assets/   # Base support files (optional)
│   │   └── <target>/                  # Per-target overlay (optional)
│   │       ├── body.md                # Body overlay — full or mirror mode
│   │       ├── frontmatter.yaml       # Frontmatter overlay (mergedeep)
│   │       └── scripts/, …            # Support-file overlay
│   ├── agents/<agent>/
│   │   ├── AGENT.md                   # Vendor-neutral base (name + description + body)
│   │   └── <target>/                  # body.md / frontmatter.yaml overlays
│   ├── hooks/<hook>/
│   │   ├── HOOK.sh                    # or HOOK.py
│   │   └── meta.yaml                  # event, timeout, optional status_message
│   ├── pi-extensions/                 # TypeScript Pi extension sources
│   │   └── <extension>/               # Compiled into dist/pi/extensions/
│   └── plugins/<plugin>/plugin.yaml   # Plugin composition (skills/agents/hooks lists)
├── dist/                              # ALL generated — DO NOT EDIT
│   ├── claude/plugins/<plugin>/{skills,agents,hooks,commands}/
│   ├── codex/{agents/*.toml,plugins/<plugin>/{skills,hooks}/}
│   ├── gemini/{skills,agents,hooks/hooks.json}/
│   └── pi/{skills,agents,hooks,extensions}/  # Flat — no plugin grouping
├── scripts/
│   ├── build/                         # Compiler entry points
│   │   ├── compile.py                 # Main entry — `make build`
│   │   ├── overlay.py                 # Frontmatter + body + support-file overlay engine
│   │   ├── compile_skill.py
│   │   ├── compile_agent.py
│   │   ├── compile_hook.py
│   │   ├── compile_pi_extension.py    # Verbatim tree-copy for Pi extensions
│   │   ├── codex_toml.py              # Agent → Codex TOML emitter
│   │   ├── plugin_index.py            # plugin.yaml → output path resolver
│   │   └── manifests.py               # Marketplace manifest generator
│   ├── validate/
│   │   ├── validate-config.py
│   │   ├── validate_genericity.py     # Forbidden-token scan for vendor-neutral bases
│   │   └── lint-instructions.py
│   ├── evals/                         # Paid OpenAI skill-eval workflow
│   ├── release/                       # Mirroring + tagging
│   └── git-hooks/                     # Activated by `make setup`
├── tests/                             # pytest + bats
├── docs/
│   └── instruction-lint-rules.md      # Format rules for skill/agent instruction files
├── .claude-plugin/marketplace.json    # Generated — sources at ./dist/claude/plugins/*
├── .agents/plugins/marketplace.json   # Generated — sources at ./dist/codex/plugins/*
├── gemini-extension.json              # Generated — references ${extensionPath}/dist/gemini/
├── AGENTS.md                          # Generated — AGENTS.md catalog
├── skills      -> dist/gemini/skills  # Generated symlink — Gemini scans extension root by name
├── agents      -> dist/gemini/agents  # Generated symlink — Gemini scans extension root by name
├── hooks       -> dist/gemini/hooks   # Generated symlink — Gemini scans extension root by name
└── Makefile

Development Workflow

# 1. Edit sources under src/skills/, src/agents/, src/hooks/, src/plugins/.

# 2. Recompile dist/ and root-level generated files
make build

# 3. Verify everything passes locally
make ci          # lint + validate + check (drift) + test

# 4. Commit src/ AND dist/ AND root-generated files in the same commit
git add -A
git commit

make check runs make build and then git diff --exit-code. If it fails, either a source change wasn't rebuilt or a generated file was hand-edited. Run make build and commit the dist/ diff. Never use --no-verify to bypass hooks.

Adding a Plugin

  1. Create src/plugins/<your-plugin>/plugin.yaml listing what the plugin ships:

    name: your-plugin
    version: 2.2.0
    description: What it does
    author: Your Name
    skills:
      - some-skill
      - another-skill
    agents:
      - some-agent
    hooks:
      - some-hook
  2. Add a src/plugins/<your-plugin>/README.md describing the plugin (this is copied into each per-target plugin directory by the compiler).

  3. Run make build. The compiler will:

    • emit dist/claude/plugins/<your-plugin>/ with manifests and artifacts
    • emit dist/codex/plugins/<your-plugin>/ with .toml agents
    • emit dist/gemini/{skills,agents}/ entries for each item
    • copy skills/agents into dist/pi/ flat
    • regenerate root marketplace manifests and gemini-extension.json
  4. Update the plugin table in the top-level README.md.

Plugin-level metadata that previously lived in scattered .claude-plugin/, .codex-plugin/, and gemini-extension.json files is now derived from plugin.yaml plus src/plugins/marketplace.yaml. There is no manual JSON to maintain.

Adding a Skill, Agent, Hook, or Command

Use the existing migrated entries as templates. The structure is the same for skills and agents — both follow the base + per-target overlay pattern.

  • Skill: create src/skills/<skill>/SKILL.md with name: and description: frontmatter and a vendor-neutral body. If a target needs divergence, add src/skills/<skill>/<target>/ with body.md and/or frontmatter.yaml. Reference the skill from one or more src/plugins/<p>/plugin.yaml files (or leave it unreferenced to ship only on Pi, which uses a flat layout).
  • Agent: create src/agents/<agent>/AGENT.md with the system prompt. Same overlay model as skills. The compiler emits TOML for Codex automatically.
  • Hook: create src/hooks/<hook>/HOOK.sh (or HOOK.py) and a meta.yaml declaring event, timeout, and optional status_message. Prefer Python (stdlib only) for anything beyond a thin shell wrapper — startup is well under the 5 s timeout. Add the hook to one or more plugin.yaml files. Per-target hook manifests (hooks.json for Claude and Gemini, codex.hooks.json for Codex) are generated.
  • Commands (Claude only): commands now live as skills under src/skills/<command>/ and the Claude target picks them up from claude/frontmatter.yaml (which may carry allowed-tools, argument-hint, etc.). Use an existing skill with a claude/frontmatter.yaml overlay as the reference example.

After any of the above, run make build && make ci.

Vendor-Neutral Body Checklist

Each src/skills/<skill>/SKILL.md and src/agents/<agent>/AGENT.md base must be runnable across Claude Code, Codex CLI, Gemini CLI, and Pi. The validator (scripts/validate/validate_genericity.py) blocks the build when the base contains:

  • $ARGUMENTS, $1, $2, … positional argument substitutions
  • !`cmd` or ```! preprocessor dynamic-context blocks
  • ${CLAUDE_SKILL_DIR}, ${CLAUDE_SESSION_ID}, ${CLAUDE_EFFORT} and other ${CLAUDE_*} variables
  • Task(, AskUserQuestion, TodoWrite, TaskCreate — Claude-only tools
  • mcp__* tool references — bare names leak into other targets

Opt-out: a base with targets: [claude] in its frontmatter is allowed to use the above. Use this sparingly — only when the skill genuinely has no vendor-neutral version (it's pure Claude orchestration).

Also avoid:

  • Markdown tables in skill/agent bodies — they render inconsistently across CLIs. Prefer bullet lists or numbered steps.
  • ASCII boxes / diagrams in bodies — they wrap badly in narrow terminals and confuse the model. Describe layout in prose; use code blocks for literal output examples.
  • Inline platform-specific tool names in prose (e.g. "use the Bash tool", "spawn a subagent via Task"). Talk in capabilities ("run a shell command", "delegate to a sub-task").

To divert a Claude-only chunk: add a mirror overlay in src/skills/<skill>/claude/body.md. The compiler auto-detects mode:

  • Full replacement — overlay has no headers matching the base; the base body is dropped entirely and the overlay is used as-is.
  • Mirror mode — overlay shares at least one header with the base, or uses (_+) (append) / (+_) (prepend) suffixes on a header title. Each overlay header is an anchor matched by full header path; unmatched headers are added as new sections.

Scripts and Tests

  • Compiler modules live under scripts/build/. Public entry point: scripts/build/compile.py. Helpers are organized by concern (overlay.py, compile_skill.py, compile_agent.py, compile_hook.py, codex_toml.py, plugin_index.py, manifests.py). New build-side code should slot into one of these modules rather than introduce a new top-level script.
  • Python tests use pytest (make test). Fixture tests under tests/test_compile_*.py compile real skills/agents from src/ and compare output against goldens — modify a golden only when you intend the output change. Hook tests live under tests/hooks/.
  • Pi extension TypeScript tests use Bun (make test-ts). Test files live alongside the sources they test (src/pi-extensions/**/*.test.ts) and are excluded from dist/ by the build compiler. Run both suites via make ci.

Pi Install

Pi consumes the flat tree under dist/pi/. The root package.json declares pi.extensions and pi.skills pointing at dist/pi/extensions and dist/pi/skills, so pi install handles both automatically.

Extensions and skills — install directly from GitHub (no local clone needed):

pi install git:github.com/alexei-led/cc-thingz

For local development (after make build):

pi install "$(pwd)"

Re-run pi install after make build to pick up changes. Existing extensions from other packages are preserved — Pi's loader merges packages.

Agentspi install does not register agents; the @tintinweb/pi-subagents loader reads them from ~/.pi/agent/agents/. After pi install, the repo is already cloned at ~/.pi/agent/git/github.com/alexei-led/cc-thingz — symlink the agent tree from there:

ln -snf \
  ~/.pi/agent/git/github.com/alexei-led/cc-thingz/dist/pi/agents \
  ~/.pi/agent/agents

Extensions are required, not optional. Several hooks depend on bundled extensions:

  • hook-runner.ts bridges all Pi lifecycle events to CC-compatible hook scripts (file-protector, git-guardrails, skill-enforcer, session-start, smart-lint, test-runner) and ccgram
  • notify.ts fires terminal-notifier on agent completion (requires terminal-notifierbrew install terminal-notifier)
  • ask-user-question.ts provides the ask_user_question tool used by smart-lint.sh

Override the target with PI_CODING_AGENT_DIR=<dir> instead of ~/.pi/agent if you run Pi from a non-default location. Restart Pi or run /reload after installing.

Rules for Pi-compatible content:

  • Pi-allowed tools: read, bash, edit, write, ask_user_question, structured_output, todo, Agent, get_subagent_result, steer_subagent, web_search, web_answer, web_research.
  • For Context7 docs lookup in Pi, use ctx7 or npx ctx7@latest — never the Context7 MCP server.
  • A skill that genuinely can't run on Pi should add a pi/body.md overlay describing an equivalent Pi workflow, or set targets: in its base frontmatter to exclude pi.

Make Targets

make help             # List all targets
make ci               # Full local CI (lint + validate + check + test + test-ts)
make lint             # Python (ruff) + shell (shellcheck/shfmt) + markdown + TypeScript (tsc)
make lint-typescript  # Type-check Pi extension TypeScript (tsc --noEmit)
make test             # pytest (Python tests)
make test-ts          # Bun (TypeScript tests for Pi extensions)
make validate         # Frontmatter, executable bits, plugin layout
make build            # Regenerate every derived artifact
make check            # build + git diff --exit-code (drift detection — CI gate)
make fmt              # Auto-format Python and shell
make skill-evals-fast # Fast paid eval loop (no baseline, no HTML, advisory)

Pull Request Checklist

Before opening a PR, confirm all of the following:

  • You have at least one merged PR, or an existing issue acknowledged by a maintainer.
  • Linked issue number is in the PR description (Closes #123).
  • make ci passes locally.
  • Plugin version: bumped in src/plugins/<plugin>/plugin.yaml if the plugin's surface changed.
  • CHANGELOG.md entry added.
  • Top-level README.md plugin table updated (skill/agent counts) if counts changed.
  • Plugin README.md updated if you added skills, agents, hooks, or commands.
  • make build was run; dist/ and root-level generated files are committed in the same PR.
  • Base SKILL.md / AGENT.md is vendor-neutral (see Vendor-Neutral Body Checklist).
  • PR is focused on one logical change. No drive-by reformatting.

Commit and PR Style

  • Imperative mood, lowercase type prefix: feat: add foo skill, fix(programming): correct lint hook, docs: …, refactor: …, chore: ….
  • Body explains why, not what — the diff already shows what.
  • One logical change per commit. Squash noise locally before pushing.
  • PR title matches the merge commit you'd want in git log.

Reporting Bugs and Security Issues

  • Bugs and feature ideas: open a GitHub issue with a minimal reproduction.
  • Security vulnerabilities: do not open a public issue. Email alexei.led@gmail.com with details and proof of concept. Acknowledgement within 72 hours.