From 3c69a338cfd7574c310b36f5fb81a85982f5300d Mon Sep 17 00:00:00 2001 From: codeonym Date: Mon, 26 Oct 2026 09:43:42 +0000 Subject: [PATCH 1/2] docs: add a usage guide and the Codex install --- README.md | 34 ++++-- docs/usage.md | 323 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 350 insertions(+), 7 deletions(-) create mode 100644 docs/usage.md diff --git a/README.md b/README.md index 4a24ad4..47d3a27 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ # groundwork -**Plan work before building it.** groundwork is a Claude Code plugin that turns an idea into epics, stories and +**Plan work before building it.** groundwork is a plugin for Claude Code and Codex that turns an idea into epics, stories and tickets through short grilling sessions, keeps every decision in a folder tree next to your code, drafts the tracker issues, plans each ticket against the code as it is that day, and builds it. It asks before every write outside the planning folder. +**New here?** Read the [usage guide](docs/usage.md): install, setup, and a walk from idea to merged pull request. + ``` /groundwork:shape "the checkout feels slow and nobody knows why" → a story, split in three tickets /groundwork:create-story grill the design → decisions/ → draft the story and its tickets → create the issues @@ -29,22 +31,39 @@ session, and asked again by the next ticket. groundwork makes them explicit: ## Install +groundwork is published in the [codeonym-oss marketplace](https://github.com/codeonym-oss/claude-plugins). + +**Claude Code:** + ``` /plugin marketplace add codeonym-oss/claude-plugins /plugin install groundwork@codeonym-oss ``` +**Codex** (a Codex CLI that supports `codex plugin`), in your terminal: + +```bash +codex plugin marketplace add codeonym-oss/claude-plugins +codex plugin add groundwork@codeonym-oss +``` + +Then start a new Codex session. If you added the marketplace before it supported Codex, refresh it first with +`codex plugin marketplace upgrade codeonym-oss`. + Then, in your project: ``` /groundwork:init ``` +In Codex, which has no `/groundwork:…` commands, find the skills with `/skills` or ask for one by name ("use +groundwork's init skill"). See [Using groundwork in Codex](docs/usage.md#using-groundwork-in-codex). + `init` checks your tools, detects the forge (`gh` or `glab`), asks which tracker you use and your project's own rules (what a story point means, the language of issue titles…), writes them to `groundwork.md`, offers to add [Andrej Karpathy's coding guidelines](https://github.com/multica-ai/andrej-karpathy-skills) to `AGENTS.md` and `CLAUDE.md`, and offers to install [Matt Pocock's skills](https://github.com/mattpocock/skills) that -`implement` builds with. Every step asks first. +`implement` builds with. Every step asks first. The [usage guide](docs/usage.md#set-up-a-project) goes through each step. ## Skills @@ -95,17 +114,18 @@ linked, moved and closed, so a write to your shared board is never improvised. A ## Works with any coding agent -groundwork is built and tested as a Claude Code plugin. But its skills are plain `SKILL.md` files and its planning -tree is plain Markdown, so any agent can follow the same workflow: +groundwork installs as a plugin in **Claude Code** and **Codex** (see [Install](#install)). Its skills are plain +`SKILL.md` files and its planning tree is plain Markdown, so other agents can follow the same workflow too: -- **Agents that load skills** (Codex, Cursor, Gemini CLI and others): point them at this repo's `skills/` folder; - each skill reads the shared `references/` through relative paths. +- **Agents that load skills** (Cursor, Gemini CLI and others): point them at this repo's `skills/` folder; each + skill reads the shared `references/` through relative paths. - **Any agent at all:** the planning tree is just files. An agent that reads `groundwork.md`, `references/structure.md` and a level's `decisions/` can plan and build within the same rules. - `init` writes the coding guidelines into `AGENTS.md`, which Codex, Cursor and Gemini CLI read, and points `CLAUDE.md` at it. -Only Claude Code is tested today. Reports from other agents are welcome. +groundwork is built and tested in Claude Code. Its Codex install has been checked; reports on how the workflow +behaves in Codex and other agents are welcome. ## How it differs from Matt Pocock's skills diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..657f42f --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,323 @@ +# Using groundwork + +A walk through groundwork from install to merged pull request. The [README](../README.md) says what groundwork is; +this guide says how to work with it day to day. The exact rules the skills follow are in +[`references/`](../references/). + +- [Install](#install) +- [Set up a project](#set-up-a-project) +- [The workflow at a glance](#the-workflow-at-a-glance) +- [1. Shape an idea](#1-shape-an-idea) +- [2. Plan an epic, a story or a ticket](#2-plan-an-epic-a-story-or-a-ticket) +- [3. The ticket pass](#3-the-ticket-pass) +- [4. Implement](#4-implement) +- [Follow-ups](#follow-ups) +- [What groundwork asks before doing](#what-groundwork-asks-before-doing) +- [Picking up where you left off](#picking-up-where-you-left-off) +- [Changing your mind](#changing-your-mind) +- [Using groundwork in Codex](#using-groundwork-in-codex) +- [Troubleshooting](#troubleshooting) + +## Install + +groundwork is published in the [codeonym-oss marketplace](https://github.com/codeonym-oss/claude-plugins), which +serves both Claude Code and Codex. + +**Claude Code:** + +``` +/plugin marketplace add codeonym-oss/claude-plugins +/plugin install groundwork@codeonym-oss +``` + +**Codex** (a Codex CLI that supports `codex plugin`), in your terminal: + +```bash +codex plugin marketplace add codeonym-oss/claude-plugins +codex plugin add groundwork@codeonym-oss +``` + +Start a new Codex session afterwards. If you added the marketplace before it supported Codex, refresh it with +`codex plugin marketplace upgrade codeonym-oss`. See [Using groundwork in Codex](#using-groundwork-in-codex) for how +calling the skills differs. + +**From a checkout** (to try a change, or to pin a version): + +```bash +git clone https://github.com/codeonym-oss/groundwork +claude --plugin-dir /path/to/groundwork +``` + +## Set up a project + +Run once, at the root of the project: + +``` +/groundwork:init +``` + +`init` goes through seven short steps and asks before each one that writes anything. Declining a step skips it. + +1. **Tools.** Checks `git`, `python3` and the forge CLI (`gh` for GitHub, `glab` for GitLab), and whether you are + logged in. When a login is missing it gives you the command to run yourself. +2. **Tracker.** Asks which tracker you use (GitHub Issues, GitLab issues, Jira, Linear or none), which repo, project + or team the issues go to, and how epics, stories and tickets map onto it. Then runs a read-only access check. +3. **Planning root and rules.** Proposes `planning/` at the project root, asks whether it is shared (committed) or + personal (gitignored), and asks for your project's own conventions. +4. **`groundwork.md`.** Shows the settings file and writes it at the project root after a yes. +5. **Coding guidelines.** Offers to download Andrej Karpathy's coding guidelines into `AGENTS.md`, and to point + `CLAUDE.md` at `AGENTS.md`. +6. **Skills groundwork builds with.** Offers to install Matt Pocock's skills (`tdd`, `code-review`, `prototype`). + groundwork works without them; `implement` then reviews on its own. +7. **Report.** What was set, what was skipped, what to run next. + +The result is a `groundwork.md` like this one: + +```markdown +--- +planning-root: planning +forge: github +default-branch: main +tracker: github +tracker-project: acme/shop +tracker-board: "3" +key-prefix: GH- +epic-as: issue with sub-issues +story-as: issue with sub-issues +ticket-as: issue +--- + +# Project rules + +- A story point is one hour of work. +- Write issue titles in French. +``` + +The project rules are plain sentences, and every skill follows them as written. groundwork has no built-in story +points, labels, languages or branch names: if your team has a convention, write it here. Run `/groundwork:init` +again at any time to change a setting; it only reruns the steps you pick. + +## The workflow at a glance + +``` +idea ──► shape ──► create-epic ──► create-story ──► create-ticket ──► ticket pass ──► implement ──► merged + (when (several (several (one pull (impl.md, (branch, tests, + unsure) stories) tickets) request) right before review, PR) + building) +``` + +You can enter at any point. A bug fix you already understand starts at `create-ticket`; a vague complaint starts at +`shape`. Each level is a folder in the planning tree, and each step sets the level's `status` in its `meta.md`: + +`shaping` → `drafted` → `tracked` → `planned` → `building` → `in-review` → `done` + +## 1. Shape an idea + +Use `shape` when you don't yet know what to build, or how big it is. + +``` +/groundwork:shape the checkout feels slow and nobody knows why +``` + +`shape` first searches the planning tree and the code for anything related (an open story it belongs to, a +follow-up describing it, a decision ruling something out) and tells you what it found. Then it grills you on the +**problem**, not the solution: what hurts and for whom, what done looks like, what must not change, how many +separate deliverables it holds. + +It ends with a proposal: + +- **the level:** a ticket, a story or an epic, or "this belongs in an existing story"; +- **a first split:** the children in the order they would ship; +- **the command to run next.** + +`shape` creates nothing but its notes in `planning/shaping//`. The `create-*` skill you run next takes over +its decisions, so nothing is asked twice. + +## 2. Plan an epic, a story or a ticket + +``` +/groundwork:create-epic several stories toward one goal +/groundwork:create-story several tickets toward one goal +/groundwork:create-ticket one deliverable, one pull request +``` + +All three follow the same flow. + +**Grilling.** One question at a time, each with two to four options, what each costs, and a recommendation. Answer +with a number, or in your own words. Facts the agent can look up (in the code, the docs, the tracker) it looks up; +only decisions come to you. A typical question: + +> Where should the price list be cached? +> +> 1. In memory per process: no new dependency, but each instance warms up separately. +> 2. In Redis: shared across instances, but adds a dependency to the checkout path. +> 3. Not at all; precompute it at deploy: fastest reads, but prices change only on deploy. +> +> I recommend **1**: decision 002 says checkout runs on a single instance for now. + +**Every answer becomes a file** in the level's `decisions/`, written the moment it is agreed: + +``` +planning/checkout-speed/decisions/003-cache-the-price-list-in-memory.md +``` + +Each holds the question, the decision, why, what was rejected, and your answer quoted. Decisions are inherited: +a ticket reads its story's and its epic's decisions and never asks them again. + +**Drafting.** When the agent has no more questions, it lists the decisions of the session and asks whether you +share the same understanding. After your yes it writes `draft.md` (the issue text) and, for an epic or story, a +folder with a short draft for each child. You review the drafts and ask for changes. + +**Creating the issues.** After you approve the drafts, it lists exactly what it will create (titles, levels, +parents, board) and asks once more. Then it creates the issues and renames each folder from `` to +`-`, for example `GH-12-checkout-speed`. With `tracker: none`, approval is the last step. + +A story ends up like this: + +``` +planning/ + GH-1-checkout-speed/ + meta.md draft.md + decisions/ 001-where-the-time-goes.md 002-… + history/ + tickets/ + GH-2-cache-the-price-list/ meta.md draft.md + GH-3-lazy-load-the-recommendations/ + GH-4-measure-checkout-in-ci/ +``` + +The children are drafted, not planned: each gets its own grilling later, in its ticket pass. + +## 3. The ticket pass + +The ticket pass turns a drafted ticket into a plan. Run it when the ticket is **next**, not when the story is +planned, because it plans against the code as it is that day. + +``` +/groundwork:create-ticket GH-2 +``` + +It reads the inherited decisions and the ticket's draft, reads the code the change touches, runs the existing tests +that cover it, and tells you what it found. Then it grills you on what the code revealed that the draft didn't +settle (where a seam goes, how an edge case behaves, what a test should prove). If the ticket turns out to be two, +it proposes the split. + +It ends by writing `impl.md`: + +- **Read first:** the files and functions involved, with paths; +- **Steps:** numbered, each with a `→ check:` saying how it is verified; +- **Tests:** what is tested, and where; +- **Risks:** what could break elsewhere, and how it is checked. + +The ticket is now `planned`. + +## 4. Implement + +``` +/groundwork:implement GH-2 +``` + +`implement` refuses a ticket that isn't `planned` and tells you what is missing. For a planned ticket: + +1. **Branch.** Proposes `GH-2-cache-the-price-list` (or your project's branch rule), cut from the latest default + branch, and asks. Uncommitted changes are shown to you, never stashed or discarded. +2. **Build.** Works through `impl.md` test-first: writes the test a step's `→ check:` describes, sees it fail, makes + it pass. If the code disagrees with the plan, it stops and asks rather than improvising. Unrelated problems it + finds become follow-ups, not fixes. +3. **Review.** Reviews the whole diff against `impl.md` and the decisions, and fixes what it finds. +4. **Stop and ask.** Shows the changed files and the test output, then asks, one at a time: commit (with the + proposed message), then push and open the pull request (with its title and body, including `Fixes #2`). + +When the pull request is merged, tell it so (or it checks with `gh pr view` / `glab mr view`): it marks the ticket +`done`, closes the issue if the tracker didn't, and offers to close the follow-ups the ticket took up. When the last +ticket of a story is done, it offers to mark the story done too. + +## Follow-ups + +A follow-up is a reminder of work for later: a bug spotted on the way, a test that flakes, a misleading comment. + +``` +/groundwork:follow-up the products list re-sorts when a row is edited +/groundwork:list-follow-ups +``` + +- It is saved at the level it **belongs** to (an epic, a story, a ticket, or the planning root), not where it was + found. +- Agents draft follow-ups on their own when they notice something unrelated to the task, then show it and save it + only after your yes. +- `list-follow-ups` lists every open one across the tree, oldest first, with how long each has waited. +- Follow-ups are closed (`status: done` or `dropped`), never deleted. A ticket that takes one up links it, and + `implement` offers to close it after the merge. + +## What groundwork asks before doing + +groundwork never writes outside the planning folder without your explicit yes. No setting turns this off. + +| asks first | doesn't ask | +|---|---| +| creating, editing, closing or linking tracker issues | reading anything | +| creating a branch, committing, pushing, opening a pull request | read-only commands (`git status`, `gh issue view`) | +| installing a package, CLI or plugin; logging in | decisions you just agreed, drafts, `meta.md`, `impl.md`, `history/` | +| editing any file outside the planning root, source code included | | +| saving a follow-up an agent drafted | | + +Each question shows exactly what will happen (the commands, the issue titles, the lines that change). A yes covers +what was shown and nothing more; a yes from an earlier session doesn't carry over. A no is final for that action, and +the agent carries on with the rest. + +## Picking up where you left off + +Everything lives in files, so a new session (or a different agent, or a teammate) starts from the tree: + +- `meta.md` says where a level stands (`status`, `branch`, `mr`). +- `decisions/` says what is settled. +- `history/CHANGES.md` says what changed and when. +- the parent's `draft.md` gives the order the children ship in. + +To continue, name the level: `/groundwork:create-ticket GH-3` runs the next ticket's pass, `/groundwork:implement +GH-3` builds it once planned. Commit the planning root if your team should see the plans; keep it gitignored if +they are personal notes. + +## Changing your mind + +- **A decision is never edited.** To change one, say so during a grilling session: a new decision is written that + says `Supersedes 004`, and the old one gets a `Superseded by 011` line. +- **A draft or plan that changes** is copied to `history/` first, with a line in `history/CHANGES.md` saying why. + If the issue text no longer matches, the agent offers to update the tracker issue. +- **Dropping work:** say so, and the level is set to `dropped` with the reason in `history/CHANGES.md`. Nothing is + deleted. + +## Using groundwork in Codex + +groundwork's skills install in Codex from the same marketplace (see [Install](#install)). The workflow, the planning +tree and the approval rules are the same; the differences are in how you call things: + +- **No `/groundwork:…` commands.** That syntax is specific to Claude Code. In Codex, use `/skills` to see the + installed skills, or ask for one by name: "use groundwork's init skill", "run groundwork's create-ticket on GH-2". +- **`init` and `implement` only run when you ask.** Claude Code never starts them on its own; in Codex, ask for + them by name too. +- **Coding guidelines:** Codex reads `AGENTS.md` itself, so the guidelines `init` adds there apply directly. +- **Matt Pocock's skills:** `init` installs them with `claude plugin`, which Codex doesn't have. Install them from + [mattpocock/skills](https://github.com/mattpocock/skills) yourself, or go without: `implement` then writes the + tests and reviews the diff on its own. + +Codex installation has been checked; reports on how the full workflow behaves in Codex are welcome in +[the issues](https://github.com/codeonym-oss/groundwork/issues). + +## Troubleshooting + +**"groundwork.md is missing."** The project hasn't been set up: run `/groundwork:init`. `shape` and `follow-up` work +without it, keeping notes in the conversation or in `follow-ups/` at the project root. + +**`implement` stops at once.** The ticket isn't `planned`: run its ticket pass with `/groundwork:create-ticket +` first. + +**The access check in `init` fails.** Log the forge CLI in yourself (`gh auth login`, `glab auth login`; they are +interactive), check that the repo or project name is right, and run `/groundwork:init` again. You can pick +`tracker: none` meanwhile and switch later. + +**Creating issues failed halfway.** The agent stops, reports which issues exist, and doesn't retry without asking. +The folders of the created ones are already renamed; the others keep their slug and `status: drafted`. + +**A link broke after a folder was renamed.** Folders are renamed once, when the issue is created, and relative links +into them are fixed then. Ask the agent to search the planning root for the old path. From aa99c857ed11482259bb14716e6a102d471fd0ae Mon Sep 17 00:00:00 2001 From: codeonym Date: Mon, 26 Oct 2026 09:46:01 +0000 Subject: [PATCH 2/2] feat: add the portable plugin.json manifest for Codex --- CONTRIBUTING.md | 5 ++++- plugin.json | 42 ++++++++++++++++++++++++++++++++++++++ release-please-config.json | 5 +++++ tests/test_repo.py | 14 +++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 plugin.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a80c5e9..bc77d3e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,6 +20,9 @@ claude --plugin-dir /path/to/groundwork ## How the repo is laid out +- `.claude-plugin/plugin.json` (Claude Code) and `plugin.json` (the portable manifest Codex reads, with its + `extensions.com.openai` display fields): keep their shared fields identical (a test checks it). Claude Code + lists the skills; Codex finds them in `skills/`. - `skills//SKILL.md`: one per skill, short. Each reads the shared files it needs from `references/`. - `references/`: the rules every skill shares. `structure.md` (the planning tree), `grilling.md` (how questions are asked), `approval.md` (what needs a yes), `drafting.md` (the create flow), `trackers/` (one guide per tracker). @@ -44,4 +47,4 @@ A rule belongs in one place. If two skills need it, it goes in `references/` and `main` is protected: work on a branch named after the issue (`-short-name`) and open a pull request whose title is a conventional commit (`feat(shape): …`, `fix(init): …`) with `Fixes #` in the body. release-please builds the -version and the changelog from those titles; don't edit `VERSION` or `plugin.json`'s version by hand. +version and the changelog from those titles; don't edit `VERSION` or either manifest's version by hand. diff --git a/plugin.json b/plugin.json new file mode 100644 index 0000000..3ef0aaf --- /dev/null +++ b/plugin.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", + "name": "groundwork", + "version": "0.1.0", + "description": "Plan work before building it. Grill an idea into epics, stories and tickets, keep every decision in a folder tree, draft the tracker issues, plan each ticket against the code as it is, and build it, asking before every write outside the planning folder.", + "author": { + "name": "codeonym", + "url": "https://github.com/codeonym" + }, + "homepage": "https://github.com/codeonym-oss/groundwork", + "repository": "https://github.com/codeonym-oss/groundwork", + "license": "MIT", + "keywords": [ + "planning", + "tickets", + "stories", + "epics", + "grilling", + "decisions", + "follow-ups", + "github", + "gitlab", + "jira", + "linear" + ], + "extensions": { + "com.openai": { + "onboardingSkill": "./skills/init/SKILL.md", + "interface": { + "displayName": "groundwork", + "shortDescription": "Plan work before building it", + "longDescription": "Grill an idea into epics, stories and tickets, keep every decision in a folder tree next to your code, draft the tracker issues, plan each ticket against the code as it is that day, and build it, asking before every write outside the planning folder.", + "developerName": "codeonym", + "category": "Productivity", + "websiteURL": "https://github.com/codeonym-oss/groundwork", + "defaultPrompt": "Use groundwork's shape skill to work out what to build next.", + "composerIcon": "./.claude-plugin/icon.png", + "logo": "./.claude-plugin/icon.png" + } + } + } +} diff --git a/release-please-config.json b/release-please-config.json index aea26d4..c3e218d 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -51,6 +51,11 @@ "type": "json", "path": ".claude-plugin/plugin.json", "jsonpath": "$.version" + }, + { + "type": "json", + "path": "plugin.json", + "jsonpath": "$.version" } ] } diff --git a/tests/test_repo.py b/tests/test_repo.py index 7f394ce..59ccc3f 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -25,6 +25,7 @@ def frontmatter(path: Path) -> dict: class RepoTest(unittest.TestCase): def setUp(self): self.manifest = json.loads((REPO / ".claude-plugin/plugin.json").read_text()) + self.portable = json.loads((REPO / "plugin.json").read_text()) def test_every_listed_skill_has_a_named_skill_md(self): for entry in self.manifest["skills"]: @@ -42,9 +43,22 @@ def test_every_skill_folder_is_listed(self): def test_versions_agree(self): version = (REPO / "VERSION").read_text().strip() self.assertEqual(self.manifest["version"], version) + self.assertEqual(self.portable["version"], version) manifest = json.loads((REPO / ".release-please-manifest.json").read_text()) self.assertEqual(manifest["."], version) + def test_portable_manifest_matches_the_claude_one(self): + # Codex reads the portable plugin.json and finds the skills in skills/ itself. + allowed = {"$schema", "name", "version", "description", "author", "homepage", "repository", "license", + "keywords", "extensions"} + self.assertLessEqual(set(self.portable), allowed) + for field in allowed - {"$schema", "extensions"}: + self.assertEqual(self.portable[field], self.manifest[field], field) + openai = self.portable["extensions"]["com.openai"] + self.assertTrue((REPO / openai["onboardingSkill"]).is_file()) + for key in ("composerIcon", "logo"): + self.assertTrue((REPO / openai["interface"][key]).is_file(), key) + def test_referenced_files_exist(self): pattern = re.compile(r"/(\.\./\.\./references/[\w./-]+?\.md|scripts/[\w.-]+)") for skill_md in (REPO / "skills").glob("*/SKILL.md"):