Implementation Plan

Publish plugin dist to agentics-kit daily

completed
2026-06-08 agentics feature

Ship a daily GitHub Actions routine that builds and pushes a clean plugin-only distribution from shawn-sandy/agentics to a new shawn-sandy/agentics-kit repo — eliminating docs/, CLAUDE.md, scripts/, and all other non-plugin files from every user install.

Implement Read and implement all steps in the plan at docs/plans/publish-plugins-to-dist-repo-daily.html — Ship a daily publish pipeline from agentics to agentics-kit
Run as workflow — launch parallel subagents
Run a workflow to implement the plan at docs/plans/publish-plugins-to-dist-repo-daily.html — Ship a daily publish pipeline from agentics to agentics-kit
File publish-plugins-to-dist-repo-daily.html
Path docs/plans/publish-plugins-to-dist-repo-daily.html
Acceptance criteria 0 / 8 done

Context

The shawn-sandy/agentics repo currently serves as both the development workspace and the install source for all 12 marketplace plugins. When Claude Code installs a plugin via git-subdir, it clones the full repo and walks the declared path — but everything else in the repo (a 35 KB README.md, the entire docs/ tree, CLAUDE.md, .github/, scripts/, examples/, tests/, session screenshots) comes along for the ride. There is no install-time filter.

scripts/build-dist.mjs already solves the filtering problem: it reads marketplace.json plugins[] as the source of truth, copies only an allowlisted set of component directories per plugin (commands, skills, agents, hooks, .claude-plugin, templates, references, scripts, README.md, CHANGELOG.md, LICENSE) into dist/, and emits a clean marketplace.json (stripped of removed[]). The --check mode verifies no DROP patterns leaked. The --publish flag is a stub that exits non-zero.

The fix is a dedicated shawn-sandy/agentics-kit distribution repo — shaped identically to the source marketplace — populated daily by a GitHub Actions routine. The routine runs build-dist.mjs, verifies cleanliness with --check, then pushes the dist/ tree to the dist repo. Users who install from agentics-kit pull only plugin component files. The existing build-clean-plugin-dist.html plan (status: todo) targeted a release-triggered publish; this plan supersedes that trigger with a daily cron.

Files to Modify

agentics/
  • .github/workflows/
    • publish-dist.yml new daily cron + workflow_dispatch publish action
  • .claude-plugin/
    • marketplace.json modified update 12 source.url values to agentics-kit
  • scripts/
    • build-dist.mjs modified implement --publish mode
  • README.md modified add Distribution section with new install path
  • CLAUDE.md modified document dist repo and manual publish command

Pipeline

Trigger
schedule: cron '0 6 * * *' + workflow_dispatch
Daily at 06:00 UTC — or manual run
Build
node scripts/build-dist.mjs
KEEP allowlist per plugin → dist/
Verify (fail-fast)
node scripts/build-dist.mjs --check
Exits non-zero if any DROP pattern leaked
Publish
node scripts/build-dist.mjs --publish
Clone → clear → copy dist/ → commit → push
Destination
shawn-sandy/agentics-kit
Clean plugin-only marketplace repo

Steps

1
done Create the shawn-sandy/agentics-kit dist repo on GitHub manual
The dist repo must exist before any publish can succeed — an empty push target. Creating it first isolates the one-time setup from the automated pipeline and ensures --publish has something to clone.
Verify
Run gh repo view shawn-sandy/agentics-kit --json name,url and confirm it returns the repo name and URL without error. The repo should have at least one commit (an auto-generated README) so the shallow clone in --publish succeeds.

Command: gh repo create shawn-sandy/agentics-kit --public --description "Clean plugin-only distribution for the agentics-kit Claude Code marketplace" --add-readme
2
done Configure DIST_REPO_TOKEN secret and DIST_REPO_URL variable on the source repo manual
The default GITHUB_TOKEN in GitHub Actions is scoped to the source repo and cannot push to agentics-kit — a fine-grained PAT with Contents: write on the dist repo is required. Storing it as a secret (not a variable) prevents it from appearing in logs.
Verify
1. Create a fine-grained PAT at github.com/settings/personal-access-tokens/new with Repository access: shawn-sandy/agentics-kit and Permissions: Contents = Read and write.

2. gh secret set DIST_REPO_TOKEN --body "<token>" --repo shawn-sandy/agentics
3. gh variable set DIST_REPO_URL --body "https://github.com/shawn-sandy/agentics-kit.git" --repo shawn-sandy/agentics

Confirm: gh secret list --repo shawn-sandy/agentics shows DIST_REPO_TOKEN; gh variable list --repo shawn-sandy/agentics shows DIST_REPO_URL.
3
done Implement --publish in scripts/build-dist.mjs
The flag is a stub that exits non-zero. The implementation shallow-clones the dist repo, clears its tracked files, copies dist/ contents in, commits with --allow-empty (so every daily run produces a timestamped commit even when content is identical), and pushes — authenticating by embedding the PAT as x-access-token in the clone URL so no credential helper is needed.
Verify
Replace the args.includes('--publish') branch stub with a publish() function call. The function should:
1. Add import { execSync } from 'node:child_process'; to the imports.
2. Fail fast if dist/ does not exist (user forgot to build first).
3. Read DIST_REPO_URL and DIST_REPO_TOKEN from process.env; exit non-zero if URL is missing.
4. Build auth URL: distUrl.replace('https://', 'https://x-access-token:' + token + '@').
5. Clone: git clone --depth=1 <authUrl> .dist-checkout/.
6. Clear tracked files: git ls-files | xargs rm -f inside the checkout.
7. Copy: cpSync(OUT_DIR, tmpDir, { recursive: true }).
8. Get source SHA: git rev-parse --short HEAD in ROOT.
9. Set git identity, git add -A, git commit --allow-empty -m "chore: sync from agentics@<sha>", git push.
10. Remove .dist-checkout/.

Test locally: node scripts/build-dist.mjs && DIST_REPO_URL=... DIST_REPO_TOKEN=... node scripts/build-dist.mjs --publish. Confirm process exits 0 and the dist repo on GitHub has a new commit.
4
done Create .github/workflows/publish-dist.yml
The daily cron trigger (0 6 * * *) keeps the dist repo current with any plugin changes merged since the last run. workflow_dispatch enables manual runs for testing and emergency publishes. Separating the build, check, and publish into distinct steps gives clear CI log sections and fails fast on leakage before attempting the push.
Verify
The file should contain:

name: Publish dist to agentics-kit

on:
  schedule:
    - cron: '0 6 * * *'
  workflow_dispatch:

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - name: Build dist
        run: node scripts/build-dist.mjs
      - name: Verify no leaks
        run: node scripts/build-dist.mjs --check
      - name: Publish to agentics-kit
        run: node scripts/build-dist.mjs --publish
        env:
          DIST_REPO_URL: ${{ vars.DIST_REPO_URL }}
          DIST_REPO_TOKEN: ${{ secrets.DIST_REPO_TOKEN }}
Verify: gh workflow list --repo shawn-sandy/agentics shows publish-dist.yml.
5
done Trigger first publish via workflow_dispatch to seed the dist repo
The dist repo was created with an auto-generated README; the first publish overwrites it with the correct plugin marketplace content and establishes the commit-history baseline. Running it manually before the cron fires confirms the token, URL variable, and --publish implementation all work end-to-end in CI.
Verify
Run: gh workflow run publish-dist.yml --repo shawn-sandy/agentics
Watch: gh run watch --repo shawn-sandy/agentics

After the run completes green:
1. gh api repos/shawn-sandy/agentics-kit/commits --jq '.[0].commit.message' should start with chore: sync from agentics@.
2. gh api repos/shawn-sandy/agentics-kit/git/trees/HEAD?recursive=1 --jq '[.tree[].path]' shows kit/plugins/* and .claude-plugin/marketplace.json but no docs/, no CLAUDE.md, no scripts/.
6
done Update all 12 source.url values in .claude-plugin/marketplace.json to point at the dist repo
The source manifest currently sends installers to https://github.com/shawn-sandy/agentics.git, the full dev repo. Switching to https://github.com/shawn-sandy/agentics-kit.git means any /plugin install @agentics-kit command pulls from the clean dist going forward. The build script already rewrites source.url in the generated dist manifest — this step makes the source manifest consistent.
Verify
grep -c "agentics-kit.git" .claude-plugin/marketplace.json returns 12. grep "agentics.git" .claude-plugin/marketplace.json returns no matches (all old URLs replaced).
7
done Add a Distribution section to README.md and update CLAUDE.md
Without documentation, users will not know the install path changed and contributors won't know how to trigger a manual publish or how the build pipeline works.
Verify
README.md — add a "Distribution" section (after the install section) with: the new /plugin marketplace add shawn-sandy/agentics-kit install command; a note that agentics is the dev repo and agentics-kit is the clean dist; link to the publish workflow.

CLAUDE.md — under Repository Structure, note dist/ (local build output, gitignored). Under Common Commands, add gh workflow run publish-dist.yml --repo shawn-sandy/agentics for manual publishes.

Verify: grep -q "agentics-kit" README.md && grep -q "agentics-kit" CLAUDE.md && echo OK prints OK.

Tests

Tier 1 — Code-touching plan
Objective Clean dist contains only plugin files after publish

File: tests/publish/smoke-clean-dist.sh

Type: smoke test

Asserts: After running node scripts/build-dist.mjs, the dist/ tree contains all 12 active plugin directories, the clean marketplace.json, README.md, and LICENSE — and contains no docs/, CLAUDE.md, SOCIAL.md, scripts/, tests/, .github/, or *.png files.

Run: node scripts/build-dist.mjs && node scripts/build-dist.mjs --check && bash tests/publish/smoke-clean-dist.sh

Unit publish() function — authentication and git operations

File: tests/publish/test-publish-fn.mjs

Targets: publish() in scripts/build-dist.mjs

Key cases: exits non-zero when dist/ missing; exits non-zero when DIST_REPO_URL unset; embeds token correctly in clone URL (x-access-token prefix); commit message contains source SHA; .dist-checkout/ is removed on success and on error.

Integration GitHub Actions workflow syntax and trigger configuration

File: tests/publish/test-workflow-config.sh

Targets: .github/workflows/publish-dist.yml

Key cases: YAML is valid (gh workflow view publish-dist.yml returns without error); cron schedule is 0 6 * * *; workflow_dispatch trigger present; DIST_REPO_TOKEN and DIST_REPO_URL are wired from secrets/vars in the publish step.

Acceptance Criteria

Verification

From a clean Claude Code session, run /plugin marketplace add shawn-sandy/agentics-kit followed by /plugin install code-review@agentics-kit. Confirm: (1) the install succeeds; (2) only .claude-plugin/, commands/, skills/, agents/, README.md, and CHANGELOG.md are present in the installed plugin directory — no docs/ tree, no plan files, no CLAUDE.md.

Additionally, wait for (or manually trigger) the next cron run and verify a new commit appears in shawn-sandy/agentics-kit with message chore: sync from agentics@<sha> where <sha> matches the current HEAD of shawn-sandy/agentics.

Completion Checklist

Required

Completion Report

No items to report — all requirements met.

Next Steps

Mark build-clean-plugin-dist.html superseded

Paste this prompt into Claude to execute this follow-up:

Open docs/plans/build-clean-plugin-dist.html and update its status to completed. Add a note in the Context section: "Superseded by publish-plugins-to-dist-repo-daily.html — the --publish implementation, GitHub Action, and dist repo setup described here were completed under that plan with a daily cron trigger instead of release-triggered."
Add a publish-status badge to README

Paste this prompt into Claude to execute this follow-up:

Add a GitHub Actions status badge for the publish-dist.yml workflow to README.md, immediately after the header. Badge URL format: https://github.com/shawn-sandy/agentics/actions/workflows/publish-dist.yml/badge.svg with a link to the workflow runs page. Label it "Publish dist".
Wish List
Add --dry-run to build-dist.mjs Wish List

Speculative / blue-sky idea — not on the critical path. Paste into Claude when ready to explore:

Implement the --dry-run flag in scripts/build-dist.mjs. It should run the full build + check pipeline and print what --publish would push (commit message, file count, bytes) without actually cloning or pushing. This makes it safe to validate the pipeline in development without accidentally publishing to the dist repo.
Protect the agentics-kit main branch from direct pushes Wish List

Speculative / blue-sky idea — not on the critical path. Paste into Claude when ready to explore:

Enable branch protection on shawn-sandy/agentics-kit main branch via: gh api repos/shawn-sandy/agentics-kit/branches/main/protection -X PUT -f required_status_checks=null -F enforce_admins=false -f required_pull_request_reviews=null -f restrictions=null. This ensures all content in the dist repo flows exclusively through the publish-dist.yml workflow, preventing accidental direct pushes. The DIST_REPO_TOKEN PAT will need branch protection bypass rights or the rule should allow the actions bot.