Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"name": "guardrails",
"source": "./plugins/guardrails",
"description": "Mod: /careful asks before destructive shell commands; /freeze <path> blocks edits outside a folder. Enforced by hooks.",
"version": "0.1.0",
"version": "0.1.1",
"category": "mod"
},
{
Expand All @@ -54,14 +54,14 @@
"name": "po-skills",
"source": "./plugins/po-skills",
"description": "Skills: brainstorming (with YC forcing questions) and writing-plans (with decision briefs and ponytail debt tracking), plus jira-stories and release-notes.",
"version": "0.3.0",
"version": "0.3.1",
"category": "skills"
},
{
"name": "eng-skills",
"source": "./plugins/eng-skills",
"description": "Skills: investigate (root cause debugging, 3-strike rule), qa (exploratory verification with mandatory regression tests), ship (pre-flight checks, doc sync, PR).",
"version": "0.2.0",
"version": "0.2.1",
"category": "skills"
},
{
Expand Down
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
126 changes: 126 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
name: CI

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
name: Repo consistency
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 22
- run: node scripts/check.mjs

shell:
name: ShellCheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- run: shellcheck setup.sh scripts/*.sh plugins/po-skills/skills/brainstorming/scripts/*.sh

powershell:
name: PowerShell lint
runs-on: windows-latest
steps:
- uses: actions/checkout@v7
- name: Parse setup.ps1
shell: pwsh
run: |
$errors = $null
[System.Management.Automation.Language.Parser]::ParseFile("$PWD\setup.ps1", [ref]$null, [ref]$errors) | Out-Null
if ($errors) { $errors | ForEach-Object { Write-Error $_ }; exit 1 }
- name: PSScriptAnalyzer
shell: pwsh
run: |
Install-Module PSScriptAnalyzer -Force -Scope CurrentUser
$issues = Invoke-ScriptAnalyzer -Path setup.ps1 -Severity Error
$issues | Format-Table -AutoSize
if ($issues) { exit 1 }

plugins:
name: Validate and test plugins
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 22
- run: npm install -g @anthropic-ai/claude-code
- name: Validate marketplace and plugins
run: |
claude plugin validate .
for p in plugins/*/; do claude plugin validate "$p"; done
- name: Test mods
run: |
for p in plugins/*/; do
if [ -d "$p/tests" ]; then echo "::group::$p"; claude plugin test "$p"; echo "::endgroup::"; fi
done

skills-portable:
name: Skills install for other agents
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 22
- name: Install every skill for Codex and Gemini CLI
run: |
expected=$(find plugins/*/skills -mindepth 2 -maxdepth 2 -name SKILL.md | wc -l)
work=$(mktemp -d)
(cd "$work" && git init -q && npx -y skills add "$GITHUB_WORKSPACE" -a codex -a gemini-cli --skill '*' --copy -y)
got=$(find "$work" -name SKILL.md | sort -u | xargs -n1 dirname | xargs -n1 basename | sort -u | wc -l)
echo "expected $expected skills, installed $got"
test "$expected" -eq "$got"

install-smoke:
name: Install (${{ matrix.os }})
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 22
- run: npm install -g @anthropic-ai/claude-code
- name: setup.sh --agents codex
if: runner.os != 'Windows'
run: ./setup.sh --agents codex
- name: setup.ps1 -Agents codex
if: runner.os == 'Windows'
shell: pwsh
run: .\setup.ps1 -Agents codex
- name: Every lippstack plugin is installed for Claude Code
shell: bash
run: |
claude plugin list > installed.txt
node -e '
const fs = require("fs")
const list = fs.readFileSync("installed.txt", "utf8")
const names = JSON.parse(fs.readFileSync(".claude-plugin/marketplace.json", "utf8")).plugins.map(p => p.name)
const missing = names.filter(n => !list.includes(`${n}@lippstack`))
if (missing.length) { console.error("not installed:", missing.join(", ")); process.exit(1) }
console.log(`all ${names.length} lippstack plugins installed`)
'
- name: Skills were installed for Codex
shell: bash
run: |
n=$(find "$HOME/.agents/skills" -name SKILL.md | wc -l)
echo "$n skills in ~/.agents/skills"
test "$n" -ge 30
38 changes: 38 additions & 0 deletions .github/workflows/vendor-drift.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Vendor drift

on:
schedule:
- cron: '0 9 * * 1'
workflow_dispatch:

permissions:
contents: write
pull-requests: write

jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Sync vendored design skills
id: sync
run: |
{
echo 'log<<EOF'
./scripts/sync-vendor.sh
echo EOF
} >> "$GITHUB_OUTPUT"
- uses: peter-evans/create-pull-request@v8
with:
branch: chore/sync-vendor
title: Sync vendored design skills
commit-message: Sync vendored design skills from upstream
body: |
`scripts/sync-vendor.sh` found upstream changes.

```
${{ steps.sync.outputs.log }}
```

Before merging: update the commits in `plugins/design-skills/README.md` and bump `design-skills` in `plugin.json` and `.claude-plugin/marketplace.json` (CI checks they match).
delete-branch: true
39 changes: 39 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# AGENTS.md

Instructions for any coding agent (Claude Code, Codex, Gemini CLI, Kimi, OpenCode, Cursor…) working on this repo. `CLAUDE.md` and `GEMINI.md` point here.

## What this repo is

A Claude Code plugin marketplace (`.claude-plugin/marketplace.json`) whose skills also install into other agents through the `npx skills` CLI.

```
plugins/<name>/.claude-plugin/plugin.json manifest of each plugin
plugins/<skills plugin>/skills/<skill>/SKILL.md Agent Skills: portable to any agent
plugins/<mod>/hooks/register.ts(x) mods: Claude Code hooks, not portable
plugins/<mod>/tests/*.test.ts mod tests (claude plugin test)
plugins/design-skills/ vendored by scripts/sync-vendor.sh; don't edit by hand
setup.sh / setup.ps1 installers (macOS+Linux / Windows)
scripts/check.mjs consistency checks CI runs
```

## Rules

- **Versions move together.** Any change under `plugins/<name>/` bumps its `version` in both `plugins/<name>/.claude-plugin/plugin.json` and `.claude-plugin/marketplace.json`. Patch for wording and fixes, minor for new skills or behaviour.
- **A new plugin** goes in `marketplace.json`, `setup.sh` (`LIPPSTACK_PLUGINS`), `setup.ps1` (`$lippPlugins`), and the README: its table and the "Or by hand" block. `scripts/check.mjs` fails if one is missing.
- **Skills stay agent-neutral.** `SKILL.md` needs `name` (equal to its folder; lowercase, digits, hyphens; ≤ 64) and `description` (≤ 1024 chars). Don't depend on one agent's tools without a fallback: subagents → "or do it yourself in a fresh pass"; ponytail → "if installed"; Jira → "an Atlassian MCP server". `disable-model-invocation: true` only works in Claude Code, so also say in the `description` that the skill runs only when explicitly asked.
- **Mods** are Claude Code only (TypeScript against the `claude-code` hooks API). Every mod has tests.
- **Vendored skills** (`plugins/design-skills/skills/*`) are refreshed by `scripts/sync-vendor.sh`, which overwrites local edits. A weekly workflow opens a PR when upstream changes.
- **Installers** must keep working on macOS (bash 3.2), Linux and Windows PowerShell 5.1+. In `setup.ps1`, run native commands through `Invoke-Native`/`Invoke-Claude` so a failure stops the script.
- Write README and skill prose in plain English, short sentences, no marketing.

## Verify before committing

```bash
node scripts/check.mjs # manifests, versions, skill frontmatter, installers, links
claude plugin validate . # marketplace; also each plugins/<name>
claude plugin test plugins/<mod> # for each mod you touched
bash -n setup.sh # and shellcheck setup.sh if you have it
npx -y skills add . --list # every skill is still discoverable for other agents
```

CI (`.github/workflows/ci.yml`) runs all of this plus a real install of `setup.sh` / `setup.ps1` on Ubuntu, macOS and Windows.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
1 change: 1 addition & 0 deletions GEMINI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# lippstack

Mods and skills for Claude Code, tuned to how we work.
[![CI](https://github.com/lippdev/lippstack/actions/workflows/ci.yml/badge.svg)](https://github.com/lippdev/lippstack/actions/workflows/ci.yml)

Mods and skills for Claude Code, tuned to how we work. The skills also run in Codex, Gemini CLI, Kimi, OpenCode, Cursor and other agents: see [Other agents](#other-agents-codex-gemini-cli-kimi-opencode-cursor).

By [Filipe Moreira](https://filipemoreira.top).

Expand Down Expand Up @@ -92,6 +94,37 @@ claude plugin install ponytail@lippstack

Then restart Claude Code, or run `/reload-plugins` in an open session.

### Other agents (Codex, Gemini CLI, Kimi, OpenCode, Cursor…)

The skills follow the open [Agent Skills](https://agentskills.io) format, so any agent that reads `SKILL.md` can use them. The mods can't: they run on Claude Code's own hooks.

| | Claude Code | Other agents |
| :- | :- | :- |
| Skills: `design-skills`, `po-skills`, `eng-skills`, ponytail, PM skills | ✅ | ✅ |
| Mods: `savvy-progress`, `stage-bar`, `done-ping`, `where-line`, `guardrails` | ✅ | ❌ |
| Official Anthropic plugins, `cc-plugin-you-should-know` | ✅ | ❌ |

From a clone, add `--agents` with the agent ids. Claude Code is installed too when its CLI is present, and skipped when it isn't:

```bash
./setup.sh --agents codex,gemini-cli,kimi-code-cli
```

```powershell
powershell -ExecutionPolicy Bypass -File .\setup.ps1 -Agents codex,gemini-cli,kimi-code-cli
```

Or without a clone, with the [skills CLI](https://github.com/vercel-labs/skills) (needs Node.js):

```bash
npx skills add lippdev/lippstack -g # pick agents and skills interactively
npx skills add lippdev/lippstack -g -a codex -a opencode --skill '*' -y
npx skills add DietrichGebert/ponytail -g --skill '*'
npx skills add deanpeters/Product-Manager-Skills -g -s user-story -s prd-development # pick the PM skills you want
```

Common ids: `codex`, `gemini-cli`, `kimi-code-cli`, `opencode`, `cursor`, `github-copilot`, `windsurf`, `antigravity`; `'*'` targets every agent the CLI knows. A few things differ outside Claude Code: `/skill-name` may be spelled differently (Kimi uses `/skill:name`), skills that offer subagents fall back to doing the work in a fresh pass, and `jira-stories` / `release-notes` need an Atlassian MCP server in that agent.

## How to use

Skills load on their own when a request matches their description. To make sure one runs, name it in the request ("use user-story to…") or type `/` and pick it from the list; plugin skills show up as `plugin:skill`, for example `/po-skills:brainstorming` or `/eng-skills:investigate`.
Expand Down
2 changes: 1 addition & 1 deletion plugins/eng-skills/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eng-skills",
"version": "0.2.0",
"version": "0.2.1",
"description": "Engineering, QA and delivery skills: investigate (systematic debugging with 3-strike rule), qa (exploratory verification with mandatory regression tests), and ship (pre-flight checks, doc sync, and PR creation).",
"author": {
"name": "lippstack"
Expand Down
4 changes: 2 additions & 2 deletions plugins/eng-skills/skills/investigate/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Follow this methodology strictly whenever diagnosing or fixing a bug.
Before touching anything:
- Identify the exact component, module, or files involved in the failure.
- Mentally **freeze** the rest of the codebase: do NOT edit or refactor unrelated files.
- If `/freeze` is available, lock edits to the relevant directory:
- If `/freeze` is available (the lippstack guardrails mod on Claude Code), lock edits to the relevant directory:
```bash
/freeze <target_directory>
```
Expand Down Expand Up @@ -79,7 +79,7 @@ Follow the 3-strike rule:

Once the root cause is proven:
1. **Write the regression test first:** Add an automated test that reproduces the bug. Run it and verify it **FAILS** for the identified root cause.
2. **Apply the minimal fix:** Use `ponytail` style — the simplest code that does the job. Avoid refactoring unrelated abstractions.
2. **Apply the minimal fix:** The simplest code that does the job (`ponytail` style, if installed). Avoid refactoring unrelated abstractions.
3. **Verify the test passes:** Run the regression test and confirm it now **PASSES**.
4. **Run the full test suite:** Ensure no other tests were broken by the fix.
5. **Clean up:** Remove any temporary debug logs or prints.
6 changes: 3 additions & 3 deletions plugins/eng-skills/skills/ship/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: ship
description: Pre-flight release workflow. Verifies clean git status, runs test suites, checks documentation synchronization (Diataxis sanity check), creates atomic commit, pushes branch, and opens a Pull Request.
description: Pre-flight release workflow. Verifies clean git status, runs test suites, checks documentation synchronization (Diataxis sanity check), creates atomic commit, pushes branch, and opens a Pull Request. Only runs when the user explicitly asks to ship; never start it on your own, since it pushes.
disable-model-invocation: true
argument-hint: "[optional PR title or target branch]"
---
Expand Down Expand Up @@ -57,7 +57,7 @@ Craft an atomic, conventional commit:
- Provide a clear bulleted list in the commit body:
- What changed.
- Why the change was made.
- Any architectural trade-offs or `shortcut:` annotations left for `ponytail-debt`.
- Any architectural trade-offs or `shortcut:` annotations (listed by `/ponytail-debt` if ponytail is installed).

---

Expand All @@ -84,4 +84,4 @@ Craft an atomic, conventional commit:
## 6. Next Steps

- If working with Jira/Confluence: suggest running `release-notes` to update the release notes on Confluence.
- If running under review: suggest running `/ponytail-review` to double-check the final diff.
- If ponytail is installed: suggest `/ponytail-review` to double-check the final diff; otherwise offer a fresh review of the diff.
2 changes: 1 addition & 1 deletion plugins/guardrails/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "guardrails",
"version": "0.1.0",
"version": "0.1.1",
"description": "Real guardrails, enforced by hooks: /careful asks you before a destructive shell command runs; /freeze <path> blocks edits outside a folder.",
"author": { "name": "lippstack" },
"types": "./types/index.d.ts"
Expand Down
4 changes: 4 additions & 0 deletions plugins/guardrails/hooks/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@ async function outsideFreeze($: Engine, cwd: string, path: string): Promise<{ de

export const register: Register = on => {
let cwd = '/'
let interactive = true

on('session.start', async ($, e, next) => {
const started = await next(e)
cwd = e.cwd
interactive = e.isInteractive
await $.command.register({ name: 'careful', description: 'Ask before destructive shell commands: on, off, or status (default on)' })
await $.command.register({ name: 'freeze', description: 'Confine file edits to a folder: /freeze <path>, /freeze off, or /freeze for status' })
return started
Expand All @@ -61,6 +63,8 @@ export const register: Register = on => {
on('tool.call', { tool: 'Bash' }, async ($, e, next) => {
const risk = riskOf(e.command)
if (!risk || !(await read($, careful))) return next(e)
// Hosts without a prompt (SDK, T3 Code, CI) fail the whole turn on a question, so refuse instead.
if (!interactive) return { deny: `guardrails: this command ${risk}, and this session cannot ask the user. Use a safer alternative, or ask the user in chat to run it or turn /careful off.` }
const shown = e.command.length > 160 ? `${e.command.slice(0, 159)}…` : e.command
const answer = await $.ui.ask(`This command ${risk}: \`${shown}\`. Run it?`, { header: 'careful', options: [RUN, 'Block it'] }).catch(() => 'Block it')
if (answer === RUN) return next(e)
Expand Down
8 changes: 8 additions & 0 deletions plugins/guardrails/tests/guardrails.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ test('careful asks, and a block keeps the command from running', async ($, on) =
expect(ran.deny).toContain('blocked')
})

test('careful refuses without asking when the session is not interactive', async ($, on) => {
const asked = engine(on, 'Run it')
await $.session.start({ ...start, isInteractive: false })
expect((await $.tool.call({ tool: 'Bash', command: 'rm -rf build' })).deny).toContain('cannot ask')
expect((await $.tool.call({ tool: 'Bash', command: 'ls' })).deny).toBeUndefined()
expect(asked.length).toBe(0)
})

test('careful lets the command run once approved, and /careful off stops asking', async ($, on) => {
const asked = engine(on, 'Run it')
await $.session.start(start)
Expand Down
2 changes: 1 addition & 1 deletion plugins/po-skills/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "po-skills",
"version": "0.3.0",
"version": "0.3.1",
"description": "Product owner skills: brainstorming (with YC forcing questions) and writing-plans (with decision briefs and ponytail debt tracking) take an idea to an approved design and plan; jira-stories and release-notes put the work in Jira and Confluence.",
"author": {
"name": "lippstack"
Expand Down
Loading
Loading