Ship a one-command /git-agent:ship-pages that takes a user's plans gallery (docs/plans/) and social-share gallery (docs/media/social/) live on GitHub Pages — or any static host — scaffolding every first-run prerequisite so publishing works in any repo with zero manual setup.
Read and implement all steps in the plan at docs/plans/add-ship-pages-skill.html — Add a one-command ship-pages skill to git-agent for publishing galleries
add-ship-pages-skill.html
docs/plans/add-ship-pages-skill.html
Context
Publishing today is a three-stage pipeline split across plugins: plan-agent + social-media-tools generate gallery HTML into docs/, git-agent commits and pushes, and a GitHub Actions workflow auto-deploys docs/ to Pages. No single tool chains generate → commit → push → deploy.
Worse, that automatic deploy only works because this repo already ships .github/workflows/deploy-pages.yml and docs/.nojekyll. A plugin user's own repo has none of that scaffolding — so "just push docs/" silently fails to publish.
ship-pages closes the gap by orchestrating the existing gallery and git skills and scaffolding the missing Pages prerequisites, with a --static escape hatch that treats docs/ as a portable bundle for Netlify, Vercel, Cloudflare Pages, or S3. It lives in git-agent (next to ship) and is command-only, mirroring ship's deliberate, manual-invoke posture. It locates the plans gallery through the same plansDirectory resolution as plans-library (project → global settings, fallback docs/plans/), so it publishes from wherever plans are configured to live.
Files to Modify
- kit/plugins/git-agent/skills/ship-pages/
SKILL.mdnew the command-only ship-pages skillassets/deploy-pages.ymlnew bundled Pages workflow template- kit/plugins/git-agent/
CHANGELOG.mdmodified v3.12.0 entryREADME.mdmodified document /git-agent:ship-pages.claude-plugin/marketplace.jsonmodified bump git-agent to 3.12.0tests/plugins/test-ship-pages.shnew smoke + scaffold testsCLAUDE.mdmodified update git-agent row in plugins table
Diagram
/git-agent:ship-pagesplans-library · media-librarydeploy-pages.yml · .nojekyll · index.htmlgh api …/pagesbranch-agent · pr-agentowner.github.io/repo- scaffolds deploy-pages.yml
- creates docs/.nojekyll
- scaffolds docs/index.html
- enables Pages via gh api
- branch + PR (deploys on merge)
- URL: owner.github.io/repo
- no scaffolding, no gh api
- treats docs/ as the bundle
- netlify deploy --dir=docs
- vercel deploy docs --prod
- wrangler pages deploy docs
- aws s3 sync docs s3://bucket
Steps
/git-agent:ship-pages), mirroring ship so Claude never auto-publishes from loose intent. Create kit/plugins/git-agent/skills/ship-pages/SKILL.md with a three-part description, disable-model-invocation: true, and allowed-tools limited to Bash(git *), Bash(gh *), Bash(python3 *), Read, Write, Skill, ToolSearch, ExitPlanMode.Verify
head -8 of the SKILL.md shows name: ship-pages and disable-model-invocation: true; the file resolves under git-agent/skills/ship-pages/.gh auth status, resolves owner/repo via gh repo view --json owner,name, and parses --static, --plans-only, --media-only, --dir; Step 2 resolves the plans gallery directory via the plansDirectory pattern — read plansDirectory from project .claude/settings.json, then global ~/.claude/settings.json, falling back to docs/plans/ (the exact resolution plans-library uses) — and detects *.html there (excl index.html); cards are detected under docs/media/social/. STOP with guidance if neither gallery exists.Verify
plansDirectory pattern (project → global → docs/plans) rather than a hard-coded path, detects both galleries, and includes a STOP branch when both are empty.index.html is missing or older than the newest plan/card (mtime compare), invoking Skill(plan-agent:plans-library) and/or Skill(social-media-tools:media-library).Verify
.nojekyll, and a root index. Create assets/deploy-pages.yml (the repo's proven workflow with pinned SHAs). When not --static: write the workflow if absent, create docs/.nojekyll if absent, scaffold a minimal docs/index.html linking both galleries if absent (never overwrite), then enable Pages via gh api --method POST repos/{owner}/{repo}/pages -f build_type=workflow, falling back to printed Settings → Pages steps on any error (already-enabled, missing scope). Deploy-source guard: the workflow uploads path: docs, so if the resolved plansDirectory falls outside docs/, warn that the docs-based Pages workflow won't include it and recommend moving plansDirectory under docs/ or using --static pointed at the gallery dir (media always lives under docs/media/social/).Verify
assets/deploy-pages.yml is valid YAML containing path: docs and the .nojekyll assertion; Step 4 covers all four scaffold artifacts plus the fallback path.git add docs/, and stage .github/workflows/deploy-pages.yml only in Pages mode when it exists (in --static mode no workflow is scaffolded, so adding that pathspec would abort git add with a pathspec error before the docs bundle is staged); warn about and leave unrelated changes uncommitted; if on main/master create a branch via Skill(git-agent:branch-agent); make a scoped conventional commit (docs(pages): publish plans/media galleries); push + open a PR via Skill(git-agent:pr-agent). Explicitly do NOT call ship — its git add -A would defeat scoped staging.Verify
docs/ (plus the workflow file only in Pages mode, when present), calls branch-agent and pr-agent, and carries a note explaining why ship is bypassed.https://{owner}.github.io/{repo}/ (handle the {owner}.github.io root-site case), noting it goes live after merge + the Actions run. --static mode: skip scaffolding/enable, treat docs/ as the bundle, and print deploy commands for Netlify (netlify deploy --dir=docs --prod), Vercel (vercel deploy docs --prod), Cloudflare (npx wrangler pages deploy docs), and S3 (aws s3 sync docs s3://<bucket> --delete).Verify
docs/.version 3.11.0 → 3.12.0 (minor, new skill) in .claude-plugin/marketplace.json; add a v3.12.0 entry to kit/plugins/git-agent/CHANGELOG.md; document /git-agent:ship-pages in kit/plugins/git-agent/README.md; update the git-agent row in the root CLAUDE.md plugins table.Verify
3.12.0 in marketplace.json (JSON-validation hook passes) and grep -l ship-pages lists CHANGELOG.md, README.md, and CLAUDE.md.tests/plugins/test-ship-pages.sh following the repo's shell-test convention: assert SKILL.md frontmatter (name, disable-model-invocation), validate the bundled deploy-pages.yml parses as YAML and contains path: docs, assert marketplace registration ≥ 3.12.0, and run a scaffold dry-run against a temp git fixture asserting the three Pages artifacts and a scoped branch/commit are produced without pushing.Verify
bash tests/plugins/test-ship-pages.sh exits 0.Tests
File: tests/plugins/test-ship-pages.sh
Type: smoke test
Asserts: against a temp git repo seeded with docs/plans/ + docs/media/social/ and no pre-existing Pages config, the scaffold+publish path writes .github/workflows/deploy-pages.yml, docs/.nojekyll, and docs/index.html, creates a publish branch with a commit touching only docs/ + the workflow, and prints the owner.github.io/repo URL.
Run: bash tests/plugins/test-ship-pages.sh
File: tests/plugins/test-ship-pages.sh (asset-validation section)
Targets: kit/plugins/git-agent/skills/ship-pages/assets/deploy-pages.yml
Key cases: parses as YAML; triggers on push to docs/**; declares pages: write + id-token: write; asserts docs/.nojekyll; uploads path: docs.
File: tests/plugins/test-ship-pages.sh (registration section)
Targets: .claude-plugin/marketplace.json + kit/plugins/git-agent/ structure
Key cases: marketplace.json is valid JSON; git-agent version ≥ 3.12.0 and higher than main; SKILL.md frontmatter has name: ship-pages + disable-model-invocation: true.
File: tests/plugins/test-ship-pages.sh (e2e section, mocked gh)
Targets: the full Pages-mode and --static flows against a throwaway git fixture
Key cases: first run scaffolds all three Pages artifacts + opens a PR; an unrelated uncommitted file is left unstaged; a second run skips scaffolding and rebuilds only the stale gallery; --static prints docs/-based host commands and writes no workflow.
Acceptance Criteria
Verification
Load git-agent locally (claude --plugin-dir ./kit/plugins/git-agent) in a checkout that has docs/plans/ plans and docs/media/social/ cards. Run /git-agent:ship-pages and confirm it: (1) rebuilds only stale galleries, (2) scaffolds deploy-pages.yml, docs/.nojekyll, and docs/index.html when absent, (3) enables Pages via gh api or prints fallback steps, (4) opens a PR whose diff is limited to docs/ + the workflow file, and (5) prints https://<owner>.github.io/<repo>/. Run /git-agent:ship-pages --static and confirm it skips all scaffolding and prints docs/-based Netlify/Vercel/Cloudflare/S3 commands. Finally run bash tests/plugins/test-ship-pages.sh and confirm it exits 0.
Completion Checklist
Completion Report
No items to report — all requirements met.
Unresolved Questions
-
Scoped-commit reuse vs. extending ship with a --paths flag
In the git-agent ship-pages skill, decide whether to keep the scoped-commit approach (branch-agent + pr-agent + a scoped `git add` of docs/, plus the deploy-pages.yml workflow only in Pages mode) or instead add a `--paths
` scoping flag to the existing ship skill and have ship-pages call ship with it. Compare maintainability and the risk of ship's `git add -A` capturing unrelated changes, recommend one, and implement the chosen approach. -
Exact gh api call + token scopes for enabling Pages
Confirm the exact `gh api` call and token scopes needed to enable GitHub Pages with the GitHub Actions build type (POST repos/{owner}/{repo}/pages with build_type=workflow). Determine whether gh's default auth scopes suffice or whether `gh auth refresh -s` (and which scope) is required, and document the fallback in the git-agent ship-pages skill so first-time users get a clear instruction when enablement fails. -
Handling a plansDirectory that resolves outside docs/
Decide how git-agent ship-pages should handle a configured plansDirectory that resolves outside docs/ (the GitHub Pages deploy source): (a) warn only and tell the user to move plansDirectory under docs/ or use --static, (b) copy/sync the plans gallery into docs/ as a build step before publishing, or (c) parameterize the scaffolded deploy-pages.yml upload `path:` (and the .nojekyll location) to the lowest common ancestor of docs/ and plansDirectory. Weigh simplicity against publishing unintended files (e.g. the whole repo root) and recommend one; implement it in the skill.