Skip to content
Agentmart
Submit a tool

build-ci

This skill should be used when the user asks to "set up CI", "set up GitLab CI", "set up GitHub Actions CI", "write our .gitlab-ci.yml", "only run pipelines…

Skill by pmvanev

IndexedMIT
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/pmvanev-build-ci

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

This skill should be used when the user asks to "set up CI", "set up GitLab CI", "set up GitHub Actions CI", "write our .gitlab-ci.yml", "only run pipelines for MRs", "make the pipeline match our CI policy", "tell the team when main breaks", "notify us when the main pipeline fails", "tell the author when their pipeline fails", "comment on the MR when CI fails", "add a deploy job", "deploy to staging and production", "promote to production", "roll back a deploy", "speed up the pipeline", "why is CI slow", "find what CI builds twice", or runs /phil:build-ci. It declares the CI policy, builds the pipeline to it, lints it, wires a red-main alert, names whoever started a failed pipeline, deploys, by digest, the one image built after its gates pass, and asks before changing a forge setting; --speed ranks speed-ups on the measured critical path, and --rework finds artifacts built twice.

Use cases: devops, data, writing

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found
Runs as
Python package

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/build-ci

What's inside

Skill name
build-ci
Description
This skill should be used when the user asks to "set up CI", "set up GitLab CI", "set up GitHub Actions CI", "write our .gitlab-ci.yml", "only run pipelines for MRs", "make the pipeline match our CI policy", "tell the team when main breaks", "notify us when the main pipeline fails", "tell the author when their pipeline fails", "comment on the MR when CI fails", "add a deploy job", "deploy to staging and production", "promote to production", "roll back a deploy", "speed up the pipeline", "why is CI slow", "find what CI builds twice", or runs /phil:build-ci. It declares the CI policy, builds the pipeline to it, lints it, wires a red-main alert, names whoever started a failed pipeline, deploys, by digest, the one image built after its gates pass, and asks before changing a forge setting; --speed ranks speed-ups on the measured critical path, and --rework finds artifacts built twice.
License
MIT
Instructions
317 lines in SKILL.md · view on GitHub

Before installing

  • Ships executable scripts

Agent Skills spec issues

  • files: referenced but missing: scripts/ci/run.sh

Files (109)

  • SKILL.md
  • run.sh · examples/
  • alerts.md · references/
  • rework-pass.md · references/
  • speed-pass.md · references/
  • pipeline-timings.py · scripts/
  • expected-matrix.md · self-test/01-empty-project/
  • expected.md · self-test/01-empty-project/
  • manifest.json · self-test/01-empty-project/
  • AGENTS.md · self-test/01-empty-project/project/
  • README.md · self-test/01-empty-project/project/
  • usage.md · self-test/01-empty-project/project/docs/
  • eslint.config.js · self-test/01-empty-project/project/
  • package-lock.json · self-test/01-empty-project/project/
  • package.json · self-test/01-empty-project/project/
  • cli.ts · self-test/01-empty-project/project/src/
  • tally.ts · self-test/01-empty-project/project/src/
  • stryker.config.json · self-test/01-empty-project/project/
  • ledger.ts · self-test/01-empty-project/project/test/support/
  • setup.ts · self-test/01-empty-project/project/test/support/
  • tally.test.ts · self-test/01-empty-project/project/test/
  • tsconfig.json · self-test/01-empty-project/project/
  • vitest.config.ts · self-test/01-empty-project/project/
  • expected-producers.md · self-test/02-rework/
  • expected.md · self-test/02-rework/
  • manifest.json · self-test/02-rework/
  • .gitlab-ci.yml · self-test/02-rework/project/
  • Dockerfile · self-test/02-rework/project/
  • package-lock.json · self-test/02-rework/project/
  • package.json · self-test/02-rework/project/
  • run.sh · self-test/02-rework/project/scripts/ci/
  • deploy.sh · self-test/02-rework/project/scripts/
  • greet.js · self-test/02-rework/project/src/
  • expected.md · self-test/03-speed/
  • manifest.json · self-test/03-speed/
  • .gitlab-ci.yml · self-test/03-speed/project/
  • pipeline-000000009010.json · self-test/03-speed/project/history/
  • pipeline-000000009020.json · self-test/03-speed/project/history/
  • pipeline-000000009030.json · self-test/03-speed/project/history/
  • pipeline-000000009040.json · self-test/03-speed/project/history/
  • pipeline-000000009050.json · self-test/03-speed/project/history/
  • build.log · self-test/03-speed/project/logs/
  • lint.log · self-test/03-speed/project/logs/
  • package.log · self-test/03-speed/project/logs/
  • test.log · self-test/03-speed/project/logs/
  • package-lock.json · self-test/03-speed/project/
  • package.json · self-test/03-speed/project/
  • run.sh · self-test/03-speed/project/scripts/ci/
  • ledger.ts · self-test/03-speed/project/src/
  • ledger.test.ts · self-test/03-speed/project/test/
  • vitest.config.ts · self-test/03-speed/project/
  • expected-matrix.md · self-test/04-github-project/
  • expected.md · self-test/04-github-project/
  • manifest.json · self-test/04-github-project/
  • AGENTS.md · self-test/04-github-project/project/
  • README.md · self-test/04-github-project/project/
  • usage.md · self-test/04-github-project/project/docs/
  • eslint.config.js · self-test/04-github-project/project/
  • package-lock.json · self-test/04-github-project/project/
  • package.json · self-test/04-github-project/project/
  • …and 49 more

SKILL.md (first lines)

# Build CI

Build a project's pipeline to a declared policy, then prove it with the trigger matrix. Read
`${CLAUDE_PLUGIN_ROOT}/rules/ci-pipelines.md` whole on every run: its Principles say how to shape the
pipeline, its declared-policy section gives the default block, and its Always-flag rows say what a built
pipeline must never be. Read from `${CLAUDE_PLUGIN_ROOT}/rules/security.md` the **Supply chain and
build** table, the privilege row and the credential-literal row: a built pipeline pins what it runs,
installs from the lockfile, grants the narrowest token and holds no credential.

**This skill writes repository files and reads the forge.** It writes the CI files, `scripts/ci/run.sh`,
the project's instruction file, a hash-pinned lockfile for the tools CI installs where the project has
none, and `.gitignore` entries for what the jobs leave behind; the speed pass also writes the forge's job
lists under the session's scratch directory; and nothing else.
**Write no forge setting without an answer** — the merge gate, branch protection, auto-cancel, a pipeline
schedule. `glab api` and `gh api` can write as well as read: send a `POST`, `PUT` or `PATCH` only after a
yes, and never one that starts a pipeline. `python3` runs the speed pass's timing script and nothing else.
**Never push**, and never start a pipeline: a lint's dry run
creates none, and a push to find out is a guess sent to a runner. **Write no digest or SHA you did not
read** from a registry or the forge. Every ask follows `${CLAUDE_PLUGIN_ROOT}/skills/shared/decision-request.md`;
load it before the first `AskUserQuestion` call.

## Parse the Argument

| Argument | Runs |
|---|---|
| none, or a directory | Steps 1–9: declare the policy and build the pipeline to it |
| `--speed` | the speed pass alone, over the pipeline already there |
| `--rework` | the rework pass alone, over the pipeline already there |

A directory names the project; without one, the repository root.

## Step 1: Survey the Project

Read before proposing anything; the project may already have decided.

- **The forge and its default branch.** GitLab or GitHub, from `git remote get-url origin` and any CI
  file present. The default branch from `git symbolic-ref --short refs/remotes/origin/HEAD`; failing
  that, the one of `main` and `master` that `git rev-parse --verify -q refs/heads/<name>` finds; failing
  both, ask.
- **The pipeline already there.** With a CI file present this run edits; gather the whole pipeline as
  `${CLAUDE_PLUGIN_ROOT}/skills/review-ci/SKILL.md` Step 1 does, and keep every job a recorded decision
  explains.
- **Recorded decisions.** ADRs, and dated decisions in `CLAUDE.md` or `AGENTS.md` about CI, mutation,
  schedules, deploys, releases and runners. A recorded decision outranks the default block.
- **What each job would run.** The project's own commands: the test runner, linter, type checker, build,
  mutation tool and deploy, from its task runner (`package.json` scripts, a `Makefile`, a `justfile`, a
  repository CLI) and its manifests. Name what is missing; never invent a command. Call each check so its
  failure fails the job: a wrapper that reports and exits 0 gates nothing, however useful it is
  elsewhere. Where the tools CI installs have no lockfile, write one with hashes — `uv pip compile
  --generate-hashes`, `npm install --package-lock-only` — and install from it, `pip` with
  `--require-hashes`. A project with no
  secret scanner gets gitleaks, in its own job and image: the policy's secret scan needs one.
- **Deploys.** Each environment the project deploys to, in the order a change reaches them; the command
  that deploys and whether it takes the artifact it ships; and where each environment's settings come
  from. This run writes neither the Dockerfile nor the deploy script, so where the build bakes an
  environment's settings in (PIPE-29) or the deploy cannot be handed the artifact by digest (PIPE-30),
  write no deploy job around it: report the row it breaks and the change that would clear it.
- **Change classes.** The areas a change can touch — packages, apps, services — and the selector's own
  inputs: the lockfile and manifest, the CI configuration, `scripts/ci/`, shared test files, the build,
  lint and mutation configuration.
- **Documentation paths.** Where the docs live — and a path the suite or a check reads is never docs,
  whatever its folder is called: grep the tests and check scripts for each candidate path. In a
  repository whose product is prose — skills, rules, specifications in markdown — ask which markdown is
  product before calling any of it docs.

## Step 2: Declare the Policy

- **A `## CI policy` block exists:** use it. Raise a question only where the survey contradicts a field.
- **None exists:** propose the default block `ci-pipelines.md` gives, with its Docs-only paths taken from
  the survey and every field a recorded decision changes rewritten to that decision, naming it. Ask once
  whether to adopt it as shown or change a field.
- A field the project cannot yet keep — mutation where no mutation tool is installed — is the user's
  call: ask, and write what they decide. The policy has no field for releases; where the project tags
  releases, ask what a tag pipeline runs.
- **Name in the Andon field where the team hears a red main.** Where the field names no channel, ask for
  one in the same request as the policy. The build wires two: on GitLab an email list, through the Pipeline
  status emails integration; on GitHub an issue labelled `andon`, which the repository's watchers are
  emailed about. Write any other answer — a chat app, a webhook — into the field, and report it as not
  wired.
- **On GitLab, wire the Notify field through a token the user creates**, since the job token cannot
  write a comment. Ask, in the same request, whether the user will create `NOTIFY_TOKEN` as
  `references/alerts.md` describes. The user creates it, so a token's value never passes through this
  run; on a yes, report the field as not wired until the user says the variable exists. On a no, leave
  the Notify field out of the block, write no `tell-author` job, and keep each job meant only to inform
  under `allow_failure`, with a comment that says it gates nothing.

Write the block under `## CI policy` in the project's instruction file, in the rule's shape: its
`CLAUDE.md`, or its `AGENTS.md` where that is the file it keeps; with neither, create `CLAUDE.md`.

## Step 3: Map Each Change to Its Jobs

Write a table for the report: each change class against the jobs it starts and their scope. Choose the
selection the project's tools already offer, in this order:

1. **The build tool's dependency graph** — Nx `affected`, Bazel reverse dependencies, Turborepo filters.
2. **The test runner's own** — Vitest `--changed`, Jest `--changedSince`.
3. **Path lists per area**, with a guard that fails closed: a changed path no list names, outside the
   docs allow-list, runs the full suite (PIPE-22).

A change-scoped task scopes where its tool can and runs whole where it cannot; a type check and a build
run whole. A changed selector input widens the tests only (PIPE-20): mutation stays on the MR's changed
source files, and the schedule mutates the whole codebase (PIPE-23). Pass the changed files to the
mutation tool's own filter — StrykerJS `--mutate`, Stryker.NET `--since`, cargo-mutants `--in-diff` —
and exit 0 when no source file changed. Shard the scheduled run with GitLab `parallel:` or a GitHub
matrix, into no more shards than there are source files; a tool with no shard flag, such as StrykerJS,
gets its file list split by shard index in the script, which `./scripts/ci/run.sh shard` prints.

## Step 4: Write the Entry-Point Script

**Every job calls the entry-point script, one task per job; the CI file holds triggers, stages and
wiring.** Write `scripts/ci/run.sh` in POSIX `sh` from `${CLAUDE_PLUGIN_ROOT}/skills/build-ci/examples/run.sh`,
whose decision `tests/test_build_ci.py` pins. Keep its decision and its broad patterns
(`vitest.config.*`, not `vitest.config.ts`); add this project's own paths, drop only another forge's CI
paths, and point each task at the project's own task runner. On GitLab, drop the `andon` and `promote`
tasks, which only GitHub runs. For a project that deploys nothing, drop the `image`, `deploy` and `promote`
tasks; otherwise list in `.gitignore` the `image.env` and `image.ref` the `image` task leaves. Mark it
executable with `chmod +x scripts/ci/run.sh`.

The decision, which `./scripts/ci/run.sh scope` prints as `docs-only`, `scoped` or `full`:

From pmvanev/phil-claude-plugin, MIT.

More from pmvanev/phil-claude-plugin

Signals

Stars
0
Forks
0
Open issues
10
Last commit (this folder)
Oct 23, 2026 (6 days ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from pmvanev

Cite this page

build-ci skill. Agent Mart. https://agentmart.dev/skills/pmvanev-build-ci (last updated 2026-10-24)