kit/plugins/plan-agent/skills/setup-sites/SKILL.md
Scaffolds GitHub Pages publishing into any repo. Adds the deploy workflow, .nojekyll, hub, and preview script so docs/ HTML reaches a public URL. Use when asked to set up or publish GitHub Pages.
npx skillsauth add shawn-sandy/agentics setup-sitesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Scaffold the GitHub Pages deployment pipeline into the current repository so anything generated under docs/ (plan galleries from plan-agent, social cards from social-media-tools, or any static HTML) publishes to a public URL. This drops four artifacts and guides the one-time GitHub setting — it does not generate plans or cards (the owning plugins do that) and does not commit (you commit when ready).
The pipeline it installs: a push to main touching docs/** fires a path-filtered Actions workflow that uploads docs/ and deploys it to Pages. See the companion guide docs/guides/publish-docs-to-github-pages.md in the agentics repo for the full reference.
ExitPlanMode is a deferred tool. Only call it if currently in plan mode — skip this step entirely when not in plan mode. When calling: use ToolSearch with select:ExitPlanMode first, then call ExitPlanMode silently. This skill performs filesystem writes, so it runs directly rather than producing a plan document.
Confirm the cwd is a git work tree, then derive the published URL from the origin remote. A project repo publishes under a path prefix (https://<owner>.github.io/<repo>/); a user/org root repo named <owner>.github.io publishes at the apex (https://<owner>.github.io/).
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || {
echo "Not inside a git repository. cd into your project first."; exit 1; }
python3 - <<'EOF'
import re, subprocess
try:
url = subprocess.check_output(["git", "remote", "get-url", "origin"],
text=True, stderr=subprocess.DEVNULL).strip()
except Exception:
url = ""
owner = repo = ""
m = re.search(r'github\.com[:/]+([^/]+)/(.+?)(?:\.git)?/?$', url)
if m:
owner, repo = m.group(1), m.group(2)
if not owner:
print("REMOTE=none")
print("Note: no GitHub origin remote found — using placeholders for the hub.")
elif repo.lower() == f"{owner.lower()}.github.io":
print(f"REMOTE={owner}/{repo}")
print(f"LIVE_URL=https://{owner}.github.io/")
print("SITE_KIND=root (served at the apex, no path prefix)")
else:
print(f"REMOTE={owner}/{repo}")
print(f"LIVE_URL=https://{owner}.github.io/{repo}/")
print("SITE_KIND=project (served under /%s/ — links MUST stay relative)" % repo)
EOF
Keep owner, repo, and the computed LIVE_URL for Steps 4, 5, and 7. If there is no remote, continue anyway with placeholder values and warn the user that the live URL is unknown until a GitHub remote exists.
plansDirectoryThe deploy workflow uploads docs/ and nothing else, so generated HTML must live under docs/. Two failure modes to close here:
plansDirectory set but outside docs/ — plans would generate where Pages never looks. Warn and ask before proceeding.plansDirectory unset and docs/plans/ missing — implementation-plan resolves its output dir as --dir → plansDirectory (project-local → project → global) → ${PWD}/docs/plans as the final fallback, so the first plan always lands inside the repo. Seed docs/plans/ now (with a committed .gitkeep) anyway, so the directory is tracked and Pages serves it from the first deploy.mkdir -p docs
python3 - <<'EOF'
import json, os
def read(p):
try: return json.load(open(p)).get("plansDirectory", "").strip()
except Exception: return ""
val = read(os.path.join(".claude", "settings.local.json")) or \
read(os.path.join(".claude", "settings.json")) or \
read(os.path.join(os.path.expanduser("~"), ".claude", "settings.json"))
if val:
norm = val[2:] if val.startswith("./") else val
inside = (not os.path.isabs(norm)) and (norm.rstrip("/") + "/").startswith("docs/")
print(f"plansDirectory = {val}")
if not inside:
print("WARNING: plansDirectory is OUTSIDE docs/ — Pages only serves docs/.")
print(" Move plans under docs/ (e.g. docs/plans) or edit the")
print(" workflow's upload `path:` to match, or plans won't publish.")
else:
# Unset: implementation-plan falls back to ${PWD}/docs/plans. Seed it (tracked
# via .gitkeep) so the directory exists and Pages serves it from the first deploy.
os.makedirs(os.path.join("docs", "plans"), exist_ok=True)
open(os.path.join("docs", "plans", ".gitkeep"), "a").close()
print("plansDirectory unset — seeded docs/plans/ (with .gitkeep) so generated")
print("plans are tracked and deploy from the first commit.")
EOF
If the warning fires, surface it to the user and ask whether to proceed before scaffolding.
The four artifacts ship as templates inside this plugin. The plugin may be a versioned cached copy or loaded directly — try every layout:
TEMPLATES_DIR=$( { \
find ~/.claude/plugins -path "*/plan-agent/*/templates/pages" -type d 2>/dev/null | sort -rV; \
find ~/.claude/plugins -path "*/plan-agent/templates/pages" -type d 2>/dev/null; \
find "$PWD" -path "*/plan-agent/templates/pages" -type d 2>/dev/null; \
} | head -1 )
[ -n "$TEMPLATES_DIR" ] || { echo "Templates not found. Install plan-agent or load it with --plugin-dir."; exit 1; }
echo "TEMPLATES_DIR=$TEMPLATES_DIR"
If empty, output that message and STOP.
Write each target only if it does not already exist. If it exists, do not clobber — report exists, skipped and move on, so re-running the skill is safe. Track what was created vs. skipped for the Step 7 summary.
4a. The deploy workflow → .github/workflows/deploy-pages.yml (verbatim, SHA-pinned):
mkdir -p .github/workflows
if [ -e .github/workflows/deploy-pages.yml ]; then
echo "deploy-pages.yml: exists, skipped"
else
cp "$TEMPLATES_DIR/deploy-pages.yml" .github/workflows/deploy-pages.yml
echo "deploy-pages.yml: created"
fi
4b. The no-Jekyll marker → docs/.nojekyll (0-byte; without it Jekyll mangles underscore filenames and the build job fails its assertion):
if [ -e docs/.nojekyll ]; then echo ".nojekyll: exists, skipped"; else touch docs/.nojekyll; echo ".nojekyll: created"; fi
4c. The local preview script → scripts/serve-docs.sh (executable):
mkdir -p scripts
if [ -e scripts/serve-docs.sh ]; then
echo "serve-docs.sh: exists, skipped"
else
cp "$TEMPLATES_DIR/serve-docs.sh" scripts/serve-docs.sh && chmod +x scripts/serve-docs.sh
echo "serve-docs.sh: created"
fi
4d. The landing hub → docs/index.html. Skip entirely if a docs/index.html already exists. Otherwise read $TEMPLATES_DIR/hub.html, substitute the three placeholders, and prune cards for galleries the repo does not use:
{{SITE_TITLE}} → the repo name title-cased, or a name the user supplies.{{SITE_TAGLINE}} → a one-line description (ask the user, or default to "Browse generated plans and social cards.").{{SITE_FOOTER}} → the site title.<!-- CARD:plans -->…<!-- /CARD:plans --> and <!-- CARD:social -->…<!-- /CARD:social -->. Remove the plans card block if neither docs/plans/ exists nor plan-agent is in use; remove the social card block if neither docs/media/social/ exists nor social-media-tools is in use. If pruning would remove both (a fresh repo), keep both as scaffolding and tell the user to delete whichever they don't need.Use Read to load the template and Write to emit docs/index.html with substitutions applied. Never introduce absolute-root links (href="/...") — the project site is served under a path prefix, so they break. Keep the relative plans/index.html and media/social/index.html hrefs.
Without this repo setting there is no Pages environment to deploy into, and the workflow has nowhere to land. This is an outward-facing change (it makes the repo publicly published), so confirm before doing it automatically.
Check whether gh is authenticated:
gh auth status >/dev/null 2>&1 && echo "GH_READY=yes" || echo "GH_READY=no"
If GH_READY=yes: use AskUserQuestion to offer "Enable Pages now via gh" vs. "I'll do it manually". Only if the user picks the automatic path, run (idempotent — create, else update):
gh api -X POST "repos/{owner}/{repo}/pages" -f build_type=workflow 2>/dev/null \
|| gh api -X PUT "repos/{owner}/{repo}/pages" -f build_type=workflow
Substitute the real owner/repo from Step 1.
If GH_READY=no, or the user declines: print the manual steps —
Open the repo on GitHub → Settings → Pages → Build and deployment → Source → GitHub Actions. (One-time; nothing publishes until this is set.)
Run fast structural checks and report a pass/fail line for each:
echo "--- verification ---"
test -f docs/.nojekyll && echo "PASS .nojekyll present" || echo "FAIL .nojekyll missing"
grep -q "upload-pages-artifact" .github/workflows/deploy-pages.yml && echo "PASS workflow installed" || echo "FAIL workflow missing"
! grep -Eq 'uses:[^#]*@v[0-9]' .github/workflows/deploy-pages.yml && echo "PASS actions SHA-pinned" || echo "FAIL an action is tag-pinned, not SHA-pinned"
if [ -f docs/index.html ]; then
grep -q 'href="/' docs/index.html && echo "FAIL hub has absolute-root link(s)" || echo "PASS hub uses relative links"
fi
Any FAIL must be fixed before the user commits.
Report concisely:
What was created vs. skipped (the Step 4 tally).
The live URL from Step 1 (or note it's pending a GitHub remote).
Whether the Pages source is set (done via gh, or the manual reminder).
The exact next commands the user runs to publish:
git add docs .github/workflows/deploy-pages.yml scripts/serve-docs.sh
git commit -m "chore: set up GitHub Pages publishing for docs/"
git push origin main # or open a PR and merge to main
Only a push to main touching docs/** triggers a deploy; feature branches never publish.
Preview locally first: bash scripts/serve-docs.sh → open the printed http://localhost:<port>/.
Gallery refresh asymmetry: the Plans gallery index rebuilds automatically (the rebuild-plans-index hook). The Social gallery has no hook — run the media-library skill before committing a new card, or Pages deploys a stale index.
STOP. Do not commit, push, or invoke other skills automatically — the user commits when ready.
development
Checks whether the branch's PR is ready and merges it when green. Runs the readiness gate, lint, and an approval prompt. Use when the user asks "merge?" or if a PR is ready to merge.
development
Implements a plan file that already exists. Walks its steps, ticks the spec, re-renders, and runs the completion gates. Use when asked to implement an existing plan.
development
Audits and optimizes CLAUDE.md project memory files. Checks adherence to Claude Code best practices and produces actionable fixes. Use when the user asks to audit, optimize, or diagnose a CLAUDE.md.
development
Converts an HTML artifact or Markdown file into a draft post for a static site. Scopes CSS to keep interactive blocks alive and escapes prose for MDX. Use when asked to turn an artifact into a post.