@jualopezmo/codeforge 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +368 -0
  3. package/VERSION +1 -0
  4. package/bin/codeforge.mjs +64 -0
  5. package/install.ps1 +317 -0
  6. package/install.sh +326 -0
  7. package/package.json +39 -0
  8. package/src/CLAUDE.md +88 -0
  9. package/src/CONTINUITY.template.md +16 -0
  10. package/src/PROJECT.template.md +27 -0
  11. package/src/configs/claude/settings.json +6 -0
  12. package/src/configs/codex/config.toml +17 -0
  13. package/src/configs/opencode.json +14 -0
  14. package/src/docs/CHANGELOG.md +9 -0
  15. package/src/docs/adr/.gitkeep +0 -0
  16. package/src/docs/e2e/reports/.gitkeep +0 -0
  17. package/src/docs/e2e/use-cases/.gitkeep +0 -0
  18. package/src/docs/extending.md +134 -0
  19. package/src/docs/plans/.gitkeep +0 -0
  20. package/src/docs/prds/.gitkeep +0 -0
  21. package/src/docs/research/.gitkeep +0 -0
  22. package/src/docs/solutions/.gitkeep +0 -0
  23. package/src/shared/rules/approach-comparison.md +29 -0
  24. package/src/shared/rules/continuity.md +32 -0
  25. package/src/shared/rules/docs-layout.md +23 -0
  26. package/src/shared/rules/memory.md +34 -0
  27. package/src/shared/rules/models.md +59 -0
  28. package/src/shared/rules/project-rules.md +31 -0
  29. package/src/shared/rules/research.md +30 -0
  30. package/src/shared/rules/severity.md +17 -0
  31. package/src/shared/rules/ship-gates.md +164 -0
  32. package/src/shared/rules/tdd.md +20 -0
  33. package/src/shared/rules/workflow.md +38 -0
  34. package/src/shared/scripts/check-gates.ps1 +197 -0
  35. package/src/shared/scripts/check-gates.sh +201 -0
  36. package/src/shared/scripts/claude-gate-hook.ps1 +35 -0
  37. package/src/shared/scripts/claude-gate-hook.sh +43 -0
  38. package/src/shared/state.template.md +33 -0
  39. package/src/skills/adr/SKILL.md +63 -0
  40. package/src/skills/checkpoint/SKILL.md +50 -0
  41. package/src/skills/council/SKILL.md +87 -0
  42. package/src/skills/finish-branch/SKILL.md +85 -0
  43. package/src/skills/fix-bug/SKILL.md +84 -0
  44. package/src/skills/index/SKILL.md +52 -0
  45. package/src/skills/new-feature/SKILL.md +84 -0
  46. package/src/skills/plan/SKILL.md +63 -0
  47. package/src/skills/prd/SKILL.md +62 -0
  48. package/src/skills/quick-fix/SKILL.md +60 -0
  49. package/src/skills/research/SKILL.md +61 -0
  50. package/src/skills/review/SKILL.md +66 -0
  51. package/src/skills/simplify/SKILL.md +59 -0
  52. package/src/skills/verify-e2e/SKILL.md +102 -0
  53. package/src/sync.ps1 +69 -0
  54. package/src/sync.sh +69 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Juan Motta
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,368 @@
1
+ # codeforge
2
+
3
+ **One workflow discipline that runs identically on Claude Code, Codex, and OpenCode.**
4
+
5
+ codeforge gives an AI coding agent a consistent, opinionated way of working — research →
6
+ plan → TDD → cross-engine review → verify → ship — plus shared memory and session
7
+ continuity. The discipline is **skills + config first** — no runtime hooks by default. The
8
+ build-time scripts (the installer and the `sync` generator) run outside the agent's turn; a
9
+ couple of small helper scripts ship into the project too (`check-gates`, and the opt-in
10
+ `--with-hooks` gate hook), invoked by the workflow rather than by a default hook. Point any of
11
+ the three CLIs at the project and they pick up the same rules, skills, and guardrails.
12
+
13
+ ---
14
+
15
+ ## What it does
16
+
17
+ - **Interoperable discipline.** The same workflow works whether you drive with Claude
18
+ Code, Codex, or OpenCode — no per-engine fork to maintain.
19
+ - **Guided workflows** for the common cases: new feature, bug fix, quick fix, PRD,
20
+ research, planning, review, multi-engine council, and branch wrap-up.
21
+ - **Cross-engine review.** The reviewer/advisor always runs on a *different* engine than
22
+ the driver, so you get real model diversity, not an echo chamber.
23
+ - **Portable memory + docs layout.** Solved bugs, decisions (ADRs), plans, and research
24
+ live in the repo so the next session — or the next engine — inherits the context.
25
+ - **Session continuity.** A tiny handoff file lets a fresh session (or a reset context)
26
+ resume exactly where you left off.
27
+ - **Native ship guardrails.** `git push` / `gh pr create` pause for human approval on
28
+ each engine, using its own native config — no custom scripts.
29
+
30
+ ---
31
+
32
+ ## How it works
33
+
34
+ ### One neutral source, generated per engine (no symlinks)
35
+
36
+ The shippable payload is a single **engine-neutral source** in **`src/`** — the
37
+ instructions, skills, rules, and per-engine configs. This source and its generators live in
38
+ the **codeforge repo**; they never travel into a target project. `install.sh` copies only the
39
+ runtime files a target needs and runs a generator (`sync.sh` / `sync.ps1`) that produces each
40
+ engine's config and skills **by plain copy** straight into the target — no symlinks, so it
41
+ works identically on macOS, Linux, and Windows. Editing is centralized: change the neutral
42
+ source in codeforge, then re-run the installer against the target (**thin install** — the
43
+ target gets only what the agent needs at runtime, none of the build machinery).
44
+
45
+ ```mermaid
46
+ flowchart TD
47
+ subgraph SRC["Neutral source (lives in codeforge, edit here)"]
48
+ INS["CLAUDE.md"]
49
+ SK["skills/<name>/SKILL.md"]
50
+ RU["shared/rules/*.md"]
51
+ CF["configs/claude · configs/codex · configs/opencode"]
52
+ end
53
+ SRC -->|install.sh runs sync --out target| GEN["Generated into the target (committed with the project)"]
54
+ GEN --> CLAUDE["Claude Code<br/>CLAUDE.md · .claude/settings.json · .claude/skills"]
55
+ GEN --> CODEX["Codex<br/>AGENTS.md · .codex/config.toml · .agents/skills"]
56
+ GEN --> OPEN["OpenCode<br/>AGENTS.md · opencode.json · reads .claude/.agents skills"]
57
+ ```
58
+
59
+ - **Neutral source vs generated:** you edit only the neutral source in codeforge (`CLAUDE.md`,
60
+ `skills/`, `shared/rules/`, `configs/`). The per-engine artifacts (`AGENTS.md`,
61
+ `opencode.json`, `.claude/`, `.agents/`, `.codex/`) are **generated into the target and
62
+ committed with it** — so a clone works immediately — but never hand-edited. The target holds
63
+ no source or generator; re-run the installer against it after editing the source.
64
+ - **Instructions:** `CLAUDE.md` is the canonical set. Sync copies it to `AGENTS.md` so Claude
65
+ Code reads `CLAUDE.md` and Codex/OpenCode read `AGENTS.md` — same content, no drift.
66
+ - **Skills:** `skills/` is the single source of truth. Sync copies it into the two paths that
67
+ cover all three engines: `.claude/skills` (Claude Code, also read by OpenCode) and
68
+ `.agents/skills` (Codex, also read by OpenCode). Codex only discovers project skills under
69
+ `.agents/skills` — not `.codex/skills`.
70
+ - **Configs:** `configs/claude/settings.json`, `configs/codex/config.toml`,
71
+ `configs/opencode.json` are the editable gate configs in the codeforge source. Sync places
72
+ each where its engine looks for it in the target (`.claude/settings.json`,
73
+ `.codex/config.toml`, root `opencode.json`) as a generated baseline. Per-project Claude
74
+ overrides go in `.claude/settings.local.json` (gitignored, never touched by the installer).
75
+ - **Rules:** `shared/rules/*.md` hold the discipline (severity, TDD, ship-gates, memory,
76
+ continuity, models, …), read in place and referenced by the skills.
77
+
78
+ > **Why copies, not symlinks?** Duplication is deliberate: symlinks are fragile on Windows
79
+ > and across zip/clone mirrors. One neutral source + a generator gives a single place to
80
+ > edit without ever fighting symlink support.
81
+
82
+ ### Enforcement model — honest about what each signal is worth
83
+
84
+ This is **discipline, not a hard gate.** No hook conditionally blocks an action. Be precise
85
+ about the strength of each signal (see [`ship-gates.md`](src/shared/rules/ship-gates.md)):
86
+
87
+ - **Advisory** — the skills *instruct* the agent to pass the gates before shipping.
88
+ - **Attested** — `finish-branch` runs `shared/scripts/check-gates.sh` (`.ps1` on Windows),
89
+ a deterministic Tier-B check that reads `.workflow/state.md` and exits non-zero listing any
90
+ unchecked box. It turns "eyeball the file" into "run a command that fails loudly" — but it
91
+ validates the *record*, not the work (a checked box is a claim), and it only runs when
92
+ invoked.
93
+ - **Verified** — the only signal independent of the agent's say-so: run `check-gates.sh` plus
94
+ your tests **in CI with branch protection**, so the check binds to the exact PR commit
95
+ outside the agent's turn. This is the honest place to put a real gate.
96
+
97
+ On top of that, each engine shows a **best-effort native prompt** on outward actions — it
98
+ reads no gate state and matches by command pattern, so it's bypassable:
99
+
100
+ | Engine | Native prompt | Config |
101
+ | --- | --- | --- |
102
+ | Claude Code | `git push` / `gh pr create` are `ask`-tier | `.claude/settings.json` |
103
+ | Codex | `approval_policy` asks when a command crosses the sandbox boundary | `.codex/config.toml` |
104
+ | OpenCode | `git push*` / `gh pr create*` set to `ask` (force-push `deny`) | `opencode.json` |
105
+
106
+ The prompt is a commit-confirmation, **not** proof the gates are green: the approver must run
107
+ `check-gates.sh` or read `.workflow/state.md` first.
108
+
109
+ **Optional hard block (Claude Code only):** `npx @jualopezmo/codeforge --with-hooks` (or
110
+ `install.sh --with-hooks`) installs a Claude Code `PreToolUse` hook — the same `check-gates`
111
+ behind a hook — that **actually blocks** a ship action when the gates are incomplete. It's
112
+ per-developer (written to gitignored `.claude/settings.local.json`), Claude-specific by design
113
+ (so it stays opt-in, off the cross-engine default), and fails open. Codex/OpenCode can do the
114
+ same (Tier C in [`src/docs/extending.md`](src/docs/extending.md)); no adapter ships yet.
115
+
116
+ ### Repo layout
117
+
118
+ The payload lives in `src/`, keeping the repo root free of files that would collide when
119
+ working ON codeforge (a root `CLAUDE.md`, `docs/`, etc.). `install.sh` reads `src/` but copies
120
+ only the runtime subset into a target; the source, generators, and seed templates stay here:
121
+
122
+ ```
123
+ codeforge/
124
+ ├── src/ # ── SOURCE (stays in codeforge; only runtime is copied) ──
125
+ │ ├── CLAUDE.md # canonical instructions (copied to the target)
126
+ │ ├── skills/<name>/SKILL.md # canonical skills → generated into .claude/ + .agents/
127
+ │ ├── shared/rules/*.md # discipline: severity, tdd, ship-gates, memory, …
128
+ │ ├── shared/scripts/*.{sh,ps1} # agent-invoked helpers: check-gates, claude-gate-hook (copied)
129
+ │ ├── shared/state.template.md # workflow-state seed (copied to the target)
130
+ │ ├── configs/ # gate-config source → generated engine configs (not copied)
131
+ │ ├── sync.sh · sync.ps1 # the generator (never copied into the target)
132
+ │ ├── docs/extending.md + empty prds/ plans/ research/ solutions/ adr/ # scaffold
133
+ │ └── CONTINUITY.template.md · PROJECT.template.md # seed-only (never copied)
134
+
135
+ ├── VERSION # single source of truth for the version (→ .forge-version)
136
+ ├── bin/codeforge.mjs # npx entry point (wraps the installer) ┐
137
+ ├── tools/ # dev-only quality machinery (linter + evals) │ framework only
138
+ ├── install.sh · install.ps1 # installers (bash + PowerShell) │ (never copied
139
+ └── package.json · README.md · LICENSE # npm package + docs + license ┘ into a target)
140
+ ```
141
+
142
+ After a **thin install**, a target project holds only runtime files: the managed
143
+ `CLAUDE.md`, `shared/rules/`, `shared/scripts/`, `shared/state.template.md`, `.forge-version`;
144
+ the project-owned `PROJECT.md`, `CONTINUITY.md`, `docs/`; and the generated engine artifacts —
145
+ `AGENTS.md`, `opencode.json`,
146
+ `.claude/`, `.agents/`, `.codex/`. Committing the generated layer means a fresh clone of the
147
+ project works immediately, with no post-clone step and no dependency on codeforge. There is no
148
+ source or generator in the target — to customize or upgrade, edit the codeforge source and
149
+ re-run the installer against the project. (Only local state — `.workflow/`,
150
+ `.claude/settings.local.json` — is gitignored.) Running an upgrade over a project installed
151
+ by an older, non-thin version cleans up the leftover machinery automatically (`sync.sh`,
152
+ templates, `docs/extending.md` removed; `configs/` and a neutral `skills/` backed up to
153
+ `*.pre-forge.bak`).
154
+
155
+ ---
156
+
157
+ ## The workflow
158
+
159
+ ```mermaid
160
+ flowchart LR
161
+ prd["prd<br/>(what/why)"] --> research["research<br/>(sourced brief)"]
162
+ research --> plan["plan<br/>(compare + choose)"]
163
+ plan --> build["new-feature / fix-bug<br/>TDD + cross-engine review"]
164
+ build --> finish["finish-branch<br/>verify → commit → push/PR"]
165
+ review["review / council"] -.consulted by.-> plan
166
+ review -.consulted by.-> build
167
+ checkpoint["checkpoint"] -.writes.-> CONT["CONTINUITY.md"]
168
+ CONT -.resumed at session start.-> prd
169
+ ```
170
+
171
+ ### Skills
172
+
173
+ | Skill | Purpose |
174
+ | --- | --- |
175
+ | `prd` | Capture problem/users/goals before designing → `docs/prds/` |
176
+ | `research` | Check current docs + prior art, write a sourced brief → `docs/research/` |
177
+ | `plan` | Clarify intent, compare approaches, write a reviewed plan → `docs/plans/` |
178
+ | `new-feature` | Full feature flow: research → plan → review → TDD → review → verify → ship |
179
+ | `fix-bug` | Systematic debugging: reproduce → root cause → failing test → fix → ship |
180
+ | `quick-fix` | Trivial changes (<3 files); escalates if scope grows |
181
+ | `review` | Cross-engine second opinion on a plan or diff (P0–P3 findings) |
182
+ | `simplify` | Post-green, behavior-preserving cleanup pass (tests stay green) |
183
+ | `verify-e2e` | Execute API/CLI user-journey use cases, write an evidence report, and bind the E2E ship-gate box to it |
184
+ | `council` | Multi-engine advisors → verdict + minority report (hard, expensive forks) |
185
+ | `adr` | Record an architecture decision (context, alternatives, consequences) → `docs/adr/` |
186
+ | `finish-branch` | Confirm gates → final verify → commit → push → PR |
187
+ | `checkpoint` | Write a clean session handoff to `CONTINUITY.md` before closing |
188
+ | `index` | Generate/refresh `docs/index.md` — a high-level project map for fast orientation |
189
+
190
+ ### Memory & continuity
191
+
192
+ - **Portable memory (repo-first):** durable knowledge lives in the repo — solved bugs in
193
+ `docs/solutions/`, decisions in `docs/adr/`, history in `docs/CHANGELOG.md` — because
194
+ all three engines read it. Personal per-engine memory is used only where it exists.
195
+ - **Continuity:** `CONTINUITY.md` holds the current focus, the single **Next step**, and
196
+ blockers. Golden rule #1 tells the agent to read it first every session, so a new
197
+ session or a reset context resumes correctly.
198
+
199
+ ### Models (cross-engine roles)
200
+
201
+ The reviewer/advisor always runs on a **different engine than the driver** (model diversity is
202
+ the point). The concrete **model IDs, effort, and read-only invocation** for each engine live
203
+ in **one place** — `src/shared/rules/models.md` — so a CLI or model bump is a single-file
204
+ edit; the skills read from there rather than hard-coding commands.
205
+
206
+ `council` consults all three engines at once; `review`/`research` use the non-driver engine.
207
+
208
+ ---
209
+
210
+ ## Installation
211
+
212
+ codeforge is the framework repo — install its discipline into a target project. It's a
213
+ **thin install**: only the agent's runtime files land in the target (the generated engine
214
+ artifacts + a small managed baseline), so a clone of that project works with no dependency on
215
+ codeforge, while all build machinery stays here. With no target argument the installer uses
216
+ the current directory.
217
+
218
+ ### Fastest — `npx` (no clone)
219
+
220
+ ```bash
221
+ cd /path/to/your-project
222
+ npx @jualopezmo/codeforge # install into the current directory
223
+ npx @jualopezmo/codeforge --upgrade # refresh framework files later
224
+ npx @jualopezmo/codeforge --version # print the installed codeforge version
225
+ ```
226
+
227
+ The Node wrapper just runs the platform installer bundled in the package (`sh` / `pwsh`); the
228
+ content it installs is plain markdown + config, and nothing from npm lands in the target beyond
229
+ the same thin payload. Each install stamps `.forge-version` into the target, and a later
230
+ `--upgrade` from a different version prints an advisory. _(Publishing to npm is pending name
231
+ confirmation; until then use the clone method below.)_
232
+
233
+ ### From a clone
234
+
235
+ ```bash
236
+ # macOS / Linux
237
+ cd /path/to/your-project && /path/to/codeforge/install.sh # install into the current dir
238
+ ./install.sh /path/to/your-project # or name the target explicitly
239
+ ./install.sh /path/to/your-project --upgrade # refresh framework files later
240
+ ```
241
+
242
+ ```powershell
243
+ # Windows (PowerShell)
244
+ pwsh /path/to/codeforge/install.ps1 # install into the current dir
245
+ pwsh ./install.ps1 C:\path\to\your-project # or name the target explicitly
246
+ pwsh ./install.ps1 C:\path\to\your-project -Upgrade # refresh framework files later
247
+ ```
248
+
249
+ What it does:
250
+
251
+ - **Copies the managed runtime baseline** (overwritten on upgrade): `CLAUDE.md`,
252
+ `shared/state.template.md`, the framework's own entries in `shared/rules/` (refreshed **by
253
+ name**), and the docs scaffolding. Your own rules dropped into `shared/rules/` are left
254
+ untouched, so they **survive upgrades**. The source, generators (`sync.sh`/`sync.ps1`),
255
+ `configs/`, and seed templates are **not** copied — they live only in codeforge.
256
+ - **Creates project-owned files only if missing** (never clobbered on re-run): `PROJECT.md`,
257
+ `CONTINUITY.md`, a seed `docs/CHANGELOG.md`. Per-project Claude overrides go in
258
+ `.claude/settings.local.json` (gitignored, never touched).
259
+ - **Generates the engine artifacts** by running `sync --out <target>` (no symlinks):
260
+ `AGENTS.md`, `opencode.json`, and `.claude/`, `.agents/`, `.codex/` (config + skills) as a
261
+ baseline from the codeforge source. These are **committed with the project** (so clones work
262
+ as-is); to change them, edit the codeforge source and re-run the installer.
263
+ - **Self-heals an older, non-thin install** on upgrade: leftover machinery (`sync.sh`,
264
+ `sync.ps1`, root templates, `docs/extending.md`) is removed, and a pre-existing `configs/`
265
+ or neutral `skills/` is backed up to `*.pre-forge.bak` (never silently deleted).
266
+ - An existing `CLAUDE.md` is backed up to `CLAUDE.md.pre-forge.bak` (move its
267
+ project-specifics into `PROJECT.md`), and `.gitignore` is merged, not replaced.
268
+ - Runs a **post-install validation** (skill-discovery paths `.claude`/`.agents`, `AGENTS.md`,
269
+ and engine configs generated) that **exits non-zero** if anything is missing, and warns if
270
+ a config lacks the push/PR gate.
271
+ - **Checks for git.** The workflow (branches, commits) and the ship gates operate on git. If the
272
+ target isn't a repo, the installer **warns** (it never touches your VCS on its own); pass
273
+ `--git-init` to have it run `git init` + a baseline commit for you.
274
+ - **Auto-isolates Claude Code** from ancestor instructions. Codex and OpenCode already scope to
275
+ the project root, but Claude Code walks to the filesystem root and **blends every ancestor
276
+ `CLAUDE.md`/`.claude/rules` into your project**. So if the target sits under a directory that
277
+ has its own `CLAUDE.md` (e.g. a `~/projects/` you keep instructions in), the installer writes
278
+ `claudeMdExcludes` into the gitignored `.claude/settings.local.json` to block those ancestors —
279
+ making the project run its **own** discipline, consistently across all three engines. Pass
280
+ `--no-isolate` to keep inheritance (e.g. an intentional monorepo root). It only manages a
281
+ `settings.local.json` it created; one you own is never touched.
282
+
283
+ Then fill in `PROJECT.md` in the target, edit the neutral source in codeforge as needed
284
+ (`skills/`, `configs/`, `CLAUDE.md`) and re-run the installer against the project, and open
285
+ the project in any of the three engines.
286
+
287
+ > **Windows:** no symlinks are used, so nothing special is required — `install.ps1` and
288
+ > `sync.ps1` are plain PowerShell copies. (Needs PowerShell 7 / `pwsh`.)
289
+
290
+ ## How to use it
291
+
292
+ ### 1. Open the project in any engine
293
+
294
+ - **Claude Code** — open the folder; `CLAUDE.md` and `.claude/skills/` load automatically.
295
+ - **Codex** — open the folder; `AGENTS.md` and `.agents/skills/` load automatically. Trust
296
+ the project when prompted.
297
+ - **OpenCode** — open the folder; `AGENTS.md` and the skills load automatically;
298
+ `opencode.json` applies the push/PR approval gate.
299
+
300
+ At session start the agent reads `CONTINUITY.md` and resumes from its **Next step**.
301
+
302
+ ### 2. Run a workflow
303
+
304
+ Skills load **on demand** — there's no special slash syntax (these are `SKILL.md` skills,
305
+ not slash-commands). Two ways to trigger one, the same across all three engines:
306
+
307
+ - **Implicitly** — just describe the task; the engine matches it to a skill's
308
+ `description` and loads it (e.g. "add a feature that …" → `new-feature`; "there's a bug
309
+ where …" → `fix-bug`).
310
+ - **Explicitly** — name it: *"use the `new-feature` skill"*, *"run `council` on whether to
311
+ do A or B"*, *"`checkpoint` before I stop"*.
312
+
313
+ Per engine, if you want to confirm what's available: ask *"what skills do you have in this
314
+ project?"* — all three list the `skills/` folder. The skill then walks its phases, writes
315
+ artifacts to the right `docs/` folder, and tracks progress in `.workflow/state.md`.
316
+
317
+ ### 3. Ship behind the gate
318
+
319
+ Before shipping, the workflow checks that the `.workflow/state.md` gates are green
320
+ (branch, plan reviewed, tests passing, review clean, verified). `git push` / `gh pr create`
321
+ then prompt for approval — approve only when the gates are green.
322
+
323
+ ### 4. Close a session cleanly
324
+
325
+ Run **`checkpoint`** before you stop (or when context gets tight). It writes a concrete
326
+ handoff to `CONTINUITY.md` so the next session — same engine or different — picks up
327
+ exactly where you left off.
328
+
329
+ ---
330
+
331
+ ## Project-specific rules
332
+
333
+ Two rule layers apply, both always-on:
334
+
335
+ - **Global baseline** (`CLAUDE.md` golden rules + `shared/rules/*`) — the framework
336
+ discipline, applies without exception.
337
+ - **Project rules** (`PROJECT.md`) — this project's **Persona**, **Project info**,
338
+ **Variables**, and **Special rules**. Editable per project.
339
+
340
+ Project rules **add and refine** (tone, context, variables, special behavior); they never
341
+ override the safety/ship-gate baseline (on conflict, the baseline wins). All three engines
342
+ load `PROJECT.md` via golden rule #2 (OpenCode also force-loads it via `opencode.json`
343
+ `instructions`).
344
+
345
+ **To add project rules:** the installer already seeds `PROJECT.md` in the target — fill the
346
+ four sections and commit. No per-engine config needed. See `shared/rules/project-rules.md`.
347
+
348
+ ## Extending
349
+
350
+ See [`src/docs/extending.md`](src/docs/extending.md) — it defines three tiers (skills-only,
351
+ skills + invoked scripts, hooks), a decision checklist, and the steps to add a new skill.
352
+ Most new functionality is a single `skills/<name>/SKILL.md` that all three engines
353
+ discover automatically.
354
+
355
+ ## Status
356
+
357
+ **v0.2.0 — quality + distribution.** Verified end-to-end on all three engines — **Claude Code,
358
+ Codex, and OpenCode** — driving a real project. Adds a CI-enforced skill linter + routing
359
+ evals, anti-rationalization anatomy across every skill, a deterministic `check-gates` ship-gate
360
+ validator (with an opt-in Claude Code hard-block via `--with-hooks`), the `adr` and `simplify`
361
+ skills, and `.forge-version` + an `npx @jualopezmo/codeforge` entry point.
362
+
363
+ Engines: Claude Code, Codex, OpenCode. 13 skills, 11 rules. Neutral-source + generator model
364
+ (no symlinks), **thin install** (only runtime lands in the target; machinery stays in codeforge)
365
+ — cross-platform (`install.sh` + `install.ps1`), validated by dry-run install on both bash and
366
+ PowerShell (engine dirs, configs, and `AGENTS.md` generate; bare run targets the cwd; `--upgrade`
367
+ preserves project-owned files and rules and self-heals an older non-thin install; bash↔pwsh
368
+ parity), and by interactive use of the native push/PR gate in each engine.
package/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.0
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+ // codeforge — npx entry point. Thin wrapper that runs the platform installer
3
+ // bundled in this package (install.sh on POSIX, install.ps1 on Windows),
4
+ // passing through all arguments. The payload (src/, VERSION) travels in the
5
+ // package, so `npx @jualopezmo/codeforge [target] [--upgrade]` works with no repo clone.
6
+ //
7
+ // npx @jualopezmo/codeforge # install into the current directory
8
+ // npx @jualopezmo/codeforge ./my-project # install into a target
9
+ // npx @jualopezmo/codeforge --upgrade # refresh an existing install
10
+ // npx @jualopezmo/codeforge --version # print the codeforge version
11
+
12
+ import { spawnSync } from 'node:child_process';
13
+ import { fileURLToPath } from 'node:url';
14
+ import { dirname, join } from 'node:path';
15
+ import { readFileSync } from 'node:fs';
16
+ import { platform } from 'node:os';
17
+
18
+ const pkgRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
19
+ const args = process.argv.slice(2);
20
+
21
+ function version() {
22
+ try {
23
+ return readFileSync(join(pkgRoot, 'VERSION'), 'utf8').trim() || 'unknown';
24
+ } catch {
25
+ return 'unknown';
26
+ }
27
+ }
28
+
29
+ if (args.includes('--version') || args.includes('-v')) {
30
+ console.log(version());
31
+ process.exit(0);
32
+ }
33
+ if (args.includes('--help') || args.includes('-h')) {
34
+ console.log(`codeforge ${version()} — install the cross-engine workflow discipline into a project.
35
+
36
+ npx @jualopezmo/codeforge [target-dir] [--upgrade] [--with-hooks] [--git-init] [--no-isolate]
37
+
38
+ target-dir where to install (default: current directory)
39
+ --upgrade refresh framework files in an existing install
40
+ --with-hooks also install the opt-in Claude Code hard-block gate hook (Claude only)
41
+ --git-init if the target is not a git repo, initialize one + baseline commit
42
+ --no-isolate keep inheriting ancestor CLAUDE.md (default: auto-isolate via claudeMdExcludes)
43
+ --version print the codeforge version`);
44
+ process.exit(0);
45
+ }
46
+
47
+ const isWin = platform() === 'win32';
48
+ // Windows: install.ps1 declares PowerShell switches (-Upgrade, ...), not POSIX long-flags,
49
+ // so translate them; an unmapped arg (e.g. the target dir) passes through untouched.
50
+ const winFlag = { '--upgrade': '-Upgrade', '--with-hooks': '-WithHooks', '--git-init': '-GitInit', '--no-isolate': '-NoIsolate' };
51
+ // POSIX: run with `bash`, NOT `sh` — install.sh uses `set -o pipefail`, which dash (the /bin/sh
52
+ // on Debian/Ubuntu) does not support, so `sh install.sh` would abort immediately.
53
+ const cmd = isWin ? 'pwsh' : 'bash';
54
+ const cmdArgs = isWin
55
+ ? ['-NoProfile', '-File', join(pkgRoot, 'install.ps1'), ...args.map((a) => winFlag[a] ?? a)]
56
+ : [join(pkgRoot, 'install.sh'), ...args];
57
+
58
+ const r = spawnSync(cmd, cmdArgs, { stdio: 'inherit' });
59
+ if (r.error) {
60
+ const hint = isWin ? 'PowerShell 7 (pwsh) is required on Windows.' : '';
61
+ console.error(`codeforge: could not launch ${cmd}: ${r.error.message}. ${hint}`.trim());
62
+ process.exit(1);
63
+ }
64
+ process.exit(r.status ?? 1);