Skip to content
Open
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
50 changes: 50 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## What this repo is

`plain-forge` is an **npm-distributed installer CLI**. Its job is to copy a tree of authoring content (skills, rules, docs) into an AI coding agent's directory (`.claude/`, `.codex/`, `.forgecode/`, or `.agents/`) so that agent can help users write `***plain` specs for the [codeplain](https://codeplain.ai) renderer.

There are two distinct kinds of content here, and they almost never change together:

- **The CLI** (`bin/cli.mjs`) — the install/update/uninstall machinery. This is the only executable code in the repo.
- **The payload** (`forge/skills/`, `forge/rules/`) — Markdown skills and rules that get copied verbatim onto disk. Editing a skill is editing documentation/prompts, not program logic.

## Commands

```bash
npm test # run the full test suite (node --test over test/**/*.test.mjs)
node --test test/cli.test.mjs # run one test file
node --test --test-name-pattern "compareVersions" test/cli.test.mjs # run tests matching a name

node bin/cli.mjs install --agent claude --scope project # exercise the CLI locally
node bin/cli.mjs update
node bin/cli.mjs uninstall --agent claude --scope project
```

There is **no build step.** Install copies `forge/` straight to disk. The `build`/`clean` scripts in `package.json` (and the `runtimes/**` glob in `tsconfig.json`) reference `bin/forge-build.ts` and a `runtimes/` directory that do not exist — they are stale; ignore them. `tsx`/`typescript` are devDependencies but unused; there are no runtime dependencies.

## Architecture of the CLI (`bin/cli.mjs`)

A single ~700-line ESM file, pure Node stdlib, no deps. It exports its internals at the bottom so `test/cli.test.mjs` can unit-test them directly (alongside spawning the CLI as a subprocess for end-to-end checks). Keep that export list in sync when adding testable helpers.

The whole design centers on the **install manifest** at `<agent-dir>/.plain-forge/manifest.json`, which records exactly which files plain-forge wrote. This is what lets `update` and `uninstall` touch only plain-forge's own files and never the user's or third-party skills sharing the same directory.

- `AGENTS` maps an agent name → its directory (`.claude`, etc.); `SCOPES` is `project` (cwd) vs `global` ($HOME). `CONTENT_DIRS` = `skills`, `rules`, `docs` — note `forge/docs/` may not exist; `copyTreeTracked` silently skips a missing source dir, so `docs` is effectively optional.
- **install** (`cmdInstall`): refuses if a manifest OR a signature install already exists; otherwise `writeContent` copies the tree and `writeManifest` records it.
- **update** (`cmdUpdate`): `detectInstalls` scans both scopes × all agents; `isUpToDate`/`compareVersions` skip installs whose manifest version ≥ package version; re-copies, then **prunes** files in the old manifest but not the new copy (`collectPruneCandidates`), confirming each deletion unless `--yes`.
- **uninstall** (`cmdUninstall`): deletes exactly the manifest's files, then the manifest, then empties upward. **Refuses** if there's no manifest (can't tell which files are ours).
- **Legacy detection**: installs predating manifests are recognized by `hasForgeSignature` — all of `FORGE_SIGNATURE_SKILLS` present. Legacy installs are refreshed without pruning and gain a manifest going forward.

Two non-obvious invariants the code documents in comments and tests enforce:
- Non-TTY runs never delete anything without `--yes` (`promptConfirm` returns false when stdin isn't a TTY).
- `isInvokedDirectly` realpath-resolves both `argv[1]` and `import.meta.url` so the CLI still runs when invoked through a symlinked bin (npx / global install) but stays dormant on `import` from tests.

When changing prune/uninstall/detection logic, update the manifest format and the legacy-signature list together — a mismatch silently strips or orphans users' installs.

## The `forge/` payload

`forge/skills/` holds ~29 skills (each a directory with `SKILL.md` and frontmatter `name`/`description`); `forge/rules/` holds the `***plain` spec-writing rules installed as workspace instructions. This content is the product — the README documents what each skill does. When editing skills, you are writing the prompts/instructions the downstream agent follows; match the existing one-question-at-a-time, write-immediately authoring style described in the skills themselves. The skills operate on `.plain` spec files and treat generated code under `plain_modules/` as a read-only artifact — never a place to edit.

`package.json` ships only `bin/cli.mjs` and `forge/` to npm (see `files`), so anything outside those (README, tests, assets) is dev-only and not delivered to users.
96 changes: 96 additions & 0 deletions forge/skills/welcome-to-plain/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
---
name: welcome-to-plain
description: >-
Friendly front door to *codeplain and the ***plain spec language. Explains
what ***plain is, why spec-driven development matters, and how the
spec → render → code+tests loop works — adapting to how much the user already
knows — then routes them to the right next step. Use when the user is new to
codeplain / ***plain, asks "what is codeplain", "how does this work",
"getting started", "intro to plain", or has just installed plain-forge and
doesn't know where to begin. Hands off to forge-plain, init-plain-project, or
add-feature. Does NOT teach syntax — forge-plain does that by writing specs.
---

# Welcome to *codeplain

Always use the skill `load-plain-reference` to retrieve the ***plain syntax rules — but only if you haven't done so yet.

## Your Role

You are the **front door** for someone new to `*codeplain` and the `***plain` spec language. Your only two jobs are:

1. **Orient** — explain *what* `***plain` is, *why* spec-driven development is worth caring about, and *how* the spec → render → code loop works, at whatever depth the user needs.
2. **Route** — hand them off to the skill that starts the real work.

You do **not** teach `***plain` syntax here, and you do **not** author any specs. That is `forge-plain`'s job — the user learns the syntax by watching `forge-plain` write the spec *with* them. Do **not** invoke `load-plain-reference`: this skill is orientation, not authoring, and pulling the full language reference into context here is wasted effort.

Keep the whole interaction **short and conversational**. This is a warm welcome and a signpost, not a lecture. Never block the user — if they want to start building immediately, let them skip straight to the routing step.

## Step 1 — Calibrate

Before explaining anything, gauge how much the user already knows so the explanation lands at the right depth. Ask **one** `AskUserQuestion`:

> "Have you written `***plain` specs before?"

Offer these options (plus the automatic free-form catch-all):

- **Yes, I know `***plain`** — ready to start building; skip the tour entirely.
- **No, but I know spec-driven dev** — familiar with keeping specs as the source of truth (OpenAPI, Protobuf, interface definitions, etc.); wants the differentiator, not the basics. Note: using AI coding assistants like Cursor or Copilot is *not* spec-driven dev — if that's their background, this is the wrong option.
- **No, I'm new to spec-driven dev** — may use agentic coding tools (Claude Code, Copilot, Cursor), but has not worked with a spec-as-source-of-truth workflow before.

If the user picks **"Yes, I know `***plain`,"** skip Step 2 and go straight to Step 3.

## Step 2 — Explain (depth set by Step 1)

Cover the four points below, tightly. For a **new** user, walk all four. For an **experienced** user, lead with the differentiator and skip the basics.

- **What it is.** `***plain` is a markdown-like language for describing software *intent* in natural language. `*codeplain` is the renderer that turns a `.plain` spec into production-ready, tested code.

- **Why it matters.** You maintain the **spec**, not the code. The generated code is a regeneratable artifact — it lands in `plain_modules/` and is read-only. When something needs to change, you change the spec and re-render; you never hand-edit the output. Conformance tests are part of the contract that gates correctness.

- **How it works.** Write a `.plain` spec → run `codeplain` → code (and tests) land under `plain_modules/` → you iterate on the *spec*, never the generated code. A `.plain` file has four sections — name them at a glance, but do **not** drill into syntax:
- `***definitions***` — the concepts in your system.
- `***functional specs***` — what the software should do.
- `***implementation reqs***` — how it should be built (language, frameworks, conventions, unit tests).
- `***test reqs***` — how conformance tests are run.

- **A concrete moment.** Show a tiny side-by-side so it clicks — the spec a person writes vs. what `codeplain` produces from it. Keep it illustrative, not a lesson:

```plain
***functional specs***

- Display "hello, world"

***acceptance tests***
- :App: should exit with status code 0.

***implementation reqs***

- :Implementation: should be in Python.
- :MainExecutableFile: of :App: should be called "hello_world.py".
```

From that, `codeplain` renders a working `hello_world.py` under `plain_modules/` **plus** the tests that prove it satisfies the spec. Offer to draw the **spec → render → code + tests** flow as a quick diagram if the user is a visual thinker.

- **For experienced users, lead with the differentiator:** unlike one-shot code generators, the spec is the **durable source of truth** and is *re-rendered* — not generated once and then edited by hand. Conformance tests are part of that contract, not an afterthought. That's the whole point: you evolve the spec, the code follows.

Then move straight to routing.

## Step 3 — Route

Ask **one** `AskUserQuestion` — "What would you like to do next?" — and hand off by invoking the matching skill by name (the same way `forge-plain` invokes its sub-skills). Offer:

| Choice | Hand off to | When |
|--------|-------------|------|
| **Build something new, guided** | `forge-plain` | New project from scratch — full one-question-at-a-time interview ending in a real render. |
| **Add to an existing `***plain` project** | `add-feature` | The `.plain` project already exists and they want to extend it. |
| **Just learning / not yet** | *(none)* | Point them at the docs (`https://plainlang.org/`), the example projects, and the community on Discord (`https://discord.gg/cgbynb9hFq`), then stop. |

Once the user chooses, invoke the corresponding skill immediately. Do not re-explain — the next skill takes it from here.

## Guardrails

- **Never block the user.** They can jump to building at any point; "Just get me building" must always short-circuit to Step 3.
- **Never author specs in this skill** and **never teach `***plain` syntax** — that belongs to `forge-plain` and `load-plain-reference`.
- **Keep it brief.** Orientation and a signpost, nothing more.
- **Question style:** keep questions short and plain-worded — one idea per sentence, concrete options plus a free-form catch-all.