plain-forge 1.0.1

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 (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +247 -0
  3. package/bin/cli.mjs +143 -0
  4. package/forge/docs/.gitkeep +0 -0
  5. package/forge/rules/definitions.md +57 -0
  6. package/forge/rules/exported-concepts.md +39 -0
  7. package/forge/rules/func-specs.md +72 -0
  8. package/forge/rules/impl-reqs.md +50 -0
  9. package/forge/rules/import-modules.md +51 -0
  10. package/forge/rules/required-concepts.md +45 -0
  11. package/forge/rules/requires-modules.md +59 -0
  12. package/forge/rules/test-reqs.md +47 -0
  13. package/forge/skills/add-acceptance-test/SKILL.md +98 -0
  14. package/forge/skills/add-concept/SKILL.md +67 -0
  15. package/forge/skills/add-feature/SKILL.md +136 -0
  16. package/forge/skills/add-functional-spec/SKILL.md +81 -0
  17. package/forge/skills/add-functional-specs/SKILL.md +115 -0
  18. package/forge/skills/add-implementation-requirement/SKILL.md +73 -0
  19. package/forge/skills/add-resource/SKILL.md +108 -0
  20. package/forge/skills/add-template/SKILL.md +65 -0
  21. package/forge/skills/add-test-requirement/SKILL.md +68 -0
  22. package/forge/skills/analyze-2-func-specs/SKILL.md +102 -0
  23. package/forge/skills/analyze-func-specs/SKILL.md +124 -0
  24. package/forge/skills/analyze-if-func-spec-too-complex/SKILL.md +152 -0
  25. package/forge/skills/break-down-func-spec/SKILL.md +156 -0
  26. package/forge/skills/check-plain-env/SKILL.md +288 -0
  27. package/forge/skills/consolidate-concepts/SKILL.md +193 -0
  28. package/forge/skills/create-import-module/SKILL.md +98 -0
  29. package/forge/skills/create-requires-module/SKILL.md +104 -0
  30. package/forge/skills/debug-specs/SKILL.md +189 -0
  31. package/forge/skills/forge-integration/SKILL.md +443 -0
  32. package/forge/skills/forge-plain/SKILL.md +333 -0
  33. package/forge/skills/implement-conformance-testing-script/SKILL.md +247 -0
  34. package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_cypress.ps1 +324 -0
  35. package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_golang.ps1 +100 -0
  36. package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_java.sh +102 -0
  37. package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_python.ps1 +92 -0
  38. package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_python.sh +100 -0
  39. package/forge/skills/implement-prepare-environment-script/SKILL.md +242 -0
  40. package/forge/skills/implement-prepare-environment-script/assets/prepare_environment_java.sh +42 -0
  41. package/forge/skills/implement-prepare-environment-script/assets/prepare_environment_python.sh +81 -0
  42. package/forge/skills/implement-unit-testing-script/SKILL.md +133 -0
  43. package/forge/skills/implement-unit-testing-script/assets/run_unittests_flutter.ps1 +82 -0
  44. package/forge/skills/implement-unit-testing-script/assets/run_unittests_golang.ps1 +68 -0
  45. package/forge/skills/implement-unit-testing-script/assets/run_unittests_java.sh +45 -0
  46. package/forge/skills/implement-unit-testing-script/assets/run_unittests_python.ps1 +76 -0
  47. package/forge/skills/implement-unit-testing-script/assets/run_unittests_python.sh +90 -0
  48. package/forge/skills/implement-unit-testing-script/assets/run_unittests_react.ps1 +83 -0
  49. package/forge/skills/init-config-file/SKILL.md +261 -0
  50. package/forge/skills/init-plain-project/SKILL.md +124 -0
  51. package/forge/skills/load-plain-reference/SKILL.md +646 -0
  52. package/forge/skills/plain-healthcheck/SKILL.md +132 -0
  53. package/forge/skills/refactor-module/SKILL.md +197 -0
  54. package/forge/skills/resolve-spec-conflict/SKILL.md +88 -0
  55. package/forge/skills/run-codeplain/SKILL.md +540 -0
  56. package/package.json +42 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Codeplain Inc.
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,247 @@
1
+ <p align="center">
2
+ <img src="assets/plain-forge.png" alt="plain-forge" width="600" />
3
+ </p>
4
+
5
+ # plain-forge
6
+
7
+ A conversational spec-writing tool that runs in any AI coding agent (Claude Code, Codex, OpenCode, and more) and is built on the [***plain](https://plainlang.org) specification language. Describe what you want to build in plain English, and plain-forge guides you through a structured interview to produce complete `.plain` spec files — which then generate production-ready code via the [Codeplain](https://codeplain.ai) renderer.
8
+
9
+ ## How It Works
10
+
11
+ The main entry point is `forge-plain`. It turns a conversation into ***plain specs through four phases:
12
+
13
+ 1. **What are we building?** — Walk through the product: description, users, scope, core entities, key features, user flows, business rules, and (if applicable) UI behavior. Produces the `***definitions***` and `***functional specs***` for each module.
14
+ 2. **What technologies should it use?** — Pick the stack and architecture: language, frameworks, data storage, external services, project structure, and any other stack-wide constraints. Produces the `***implementation reqs***`.
15
+ 3. **How should testing be done?** — Decide the testing strategy: framework, test types in scope, conformance/acceptance tests, environment-preparation scripts, layout, and execution. Produces the `***test reqs***`, any `***acceptance tests***`, the runnable scripts under `test_scripts/`, and the `config.yaml`(s) wiring them in. plain-forge then probes your machine to confirm everything those scripts need is actually installed.
16
+ 4. **Validate and hand off** — plain-forge identifies the final module in the dependency chain and runs `codeplain <module>.plain --dry-run` itself to catch any static errors (syntax, undefined concepts, broken `import`/`requires` chains, complexity violations, conflicts). It fixes the `.plain` files until the dry-run passes, then hands you the exact `codeplain <module>.plain` command (plus any test scripts) so the real render starts from a clean spec.
17
+
18
+ Each phase is **incremental**, not a single long questionnaire. plain-forge walks one topic at a time, runs an **ask → author → review** loop on every topic — structured questions, immediate edits to the `.plain` files (and `test_scripts/` / `config.yaml` in Phase 3), then snippet-by-snippet confirmation — and only moves on once every flagged snippet is explicitly approved.
19
+
20
+ ## Getting Started
21
+
22
+ plain-forge ships as a set of skills that plug into your AI coding tool of choice. Install it once, then invoke `forge-plain` (or `add-feature` to add a feature to an existing ***plain project) from any project.
23
+
24
+ ### Install with the `skills` CLI (any runtime)
25
+
26
+ The fastest way to add plain-forge is the `skills` CLI. The `--all` flag installs **every** plain-forge skill at once:
27
+
28
+ ```bash
29
+ npx skills add Codeplain-ai/plain-forge --skill '*'
30
+ ```
31
+
32
+ #### Install into a specific runtime
33
+
34
+ If you only use one runtime, pass `--agent` to target just that one (you can repeat the flag to pick several):
35
+
36
+ ```bash
37
+ # Just Claude Code
38
+ npx skills add Codeplain-ai/plain-forge --skill '*' --agent claude-code
39
+
40
+ # Just Codex
41
+ npx skills add Codeplain-ai/plain-forge --skill '*' --agent codex
42
+
43
+ # Just OpenCode
44
+ npx skills add Codeplain-ai/plain-forge --skill '*' --agent opencode
45
+
46
+ # Any combination, non-interactive
47
+ npx skills add Codeplain-ai/plain-forge --skill '*' --agent opencode --agent codex --agent claude-code
48
+ ```
49
+
50
+ If you'd rather use the native install flow for a specific runtime, the per-tool instructions below still work.
51
+
52
+ ### Install in Claude Code
53
+
54
+ Requires the [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) installed and configured. Inside any Claude Code session, run the following **three commands**, one after the other (copy and paste each one separately):
55
+
56
+ **1.** Register this repository as a plugin marketplace:
57
+
58
+ ```text
59
+ /plugin marketplace add Codeplain-ai/plain-forge
60
+ ```
61
+
62
+ **2.** Install the `plain-forge` plugin from it:
63
+
64
+ ```text
65
+ /plugin install plain-forge@plain-forge
66
+ ```
67
+
68
+ **3.** Reload plugins so Claude Code picks up the newly installed skills:
69
+
70
+ ```text
71
+ /reload-plugins
72
+ ```
73
+
74
+ Without the reload, the plain-forge skills won't be visible in the current session even though the install succeeded. Once all three commands have run, all plain-forge skills become available.
75
+
76
+ ### Install in Codex
77
+
78
+ Requires the [OpenAI Codex CLI](https://developers.openai.com/codex/cli/reference) installed and signed in. Installation is **two steps**, but only the first one is a shell command:
79
+
80
+ **1.** From your shell, register this repository as a Codex marketplace:
81
+
82
+ ```bash
83
+ codex plugin marketplace add Codeplain-ai/plain-forge
84
+ ```
85
+
86
+ **2.** Inside Codex, open the plugin directory, pick the `plain-forge` marketplace, and install the plugin from there. (The Codex CLI does not currently expose a `codex plugin install` equivalent — installation has to be triggered from the in-app plugin directory.)
87
+
88
+ Once the plugin is installed, all plain-forge skills become available in your Codex sessions.
89
+
90
+ ## Usage
91
+
92
+ ### Prerequisites
93
+
94
+ 1. Open your project folder and start a session in your favorite AI coding agent (Claude Code, OpenCode, Codex, …).
95
+ 2. Make sure the plain-forge skills are available in that session.
96
+
97
+ ### Starting a new project
98
+
99
+ 1. Invoke `forge-plain` to launch the structured QA workflow.
100
+ 2. Answer the questions. plain-forge writes the `.plain` files for you as you go through the four phases.
101
+ 3. Render the specs into code (see [Rendering specs](#rendering-specs) below).
102
+
103
+ ### Starting a new project — incremental workflow
104
+
105
+ If you'd rather skip the full upfront interview and build the specs feature-by-feature, use this lighter loop:
106
+
107
+ 1. Invoke `init-plain-project`. It asks just for the base technology, the project kind, and whether conformance testing is enabled, then scaffolds the project skeleton: `template/base.plain` with the base `***implementation reqs***` and `***test reqs***`, a stub top-level `<project>.plain` (frontmatter only — no functional specs, no concepts), the unit-test script, an optional conformance-test script, an optional prepare-environment script, and a `config.yaml` wired to whichever scripts were generated. No `codeplain --dry-run` is run.
108
+ 2. From there, either:
109
+ - **Converse with the agent.** Just describe the next feature in plain English; the agent will invoke `add-feature` for you and run its one-question-at-a-time loop until the feature is on disk.
110
+ - **Invoke `add-feature` manually** whenever you want to drive the loop yourself.
111
+ 3. Repeat step 2 for each feature you want to add. The specs grow incrementally and `plain-healthcheck` is run as the final automated step of every `add-feature` pass.
112
+ 4. Render the specs into code (see [Rendering specs](#rendering-specs) below).
113
+
114
+ ### Adding a feature to an existing project
115
+
116
+ 1. Invoke `add-feature`.
117
+ 2. Describe the feature in plain English. plain-forge runs the same **ask → author → review** loop scoped to that feature and updates the relevant `.plain` file(s).
118
+ 3. Re-render to regenerate the code (see [Rendering specs](#rendering-specs)).
119
+
120
+ ### Rendering specs
121
+
122
+ Once your `.plain` files are ready (and `plain-healthcheck` is green), render the specs into code with the [Codeplain](https://codeplain.ai) renderer:
123
+
124
+ ```bash
125
+ codeplain <module>.plain
126
+ ```
127
+
128
+ plain-forge prints the exact command (with the right final module name) at the end of Phase 4.
129
+
130
+ #### Supervised render (experimental)
131
+
132
+ If you'd rather have plain-forge babysit the run from your AI coding agent, invoke `run-codeplain`. It launches the renderer for you, tails `codeplain.log`, watches generated code appear under `plain_modules/`, and surfaces what's happening in plain English. If it detects a pathology (stuck conformance loop, complexity error, missing concept, render failure), it asks for approval to stop the renderer, hands off to the right spec-edit skill (`debug-specs`, `resolve-spec-conflict`, `break-down-func-spec`, …), and resumes the render from the last completed functionality via `--render-from`.
133
+
134
+ This is an **experimental** feature — the default and most reliable way to render is still the manual `codeplain <module>.plain` invocation above.
135
+
136
+ ### Debugging specs
137
+
138
+ Hit a bug in the rendered app, a failing test, or behavior that doesn't match what you specified?
139
+
140
+ 1. Invoke `debug-specs`. plain-forge reads the generated code in `plain_modules/` (and the failing tests, if any), traces the issue back to the responsible `.plain` spec, and diagnoses the root cause — **ambiguous spec**, **missing spec**, **conflicting specs**, **incorrect spec**, or a **missing implementation req**.
141
+ 2. plain-forge applies the fix in the `.plain` file(s) only and summarizes what changed.
142
+ 3. Re-render to regenerate the code (see [Rendering specs](#rendering-specs)).
143
+
144
+ > **Important:** Never edit generated code under `plain_modules/` or `conformance_tests/` directly — your changes will be overwritten on the next render. Always fix the spec and re-render.
145
+
146
+
147
+ ## Repository Structure
148
+
149
+ plain-forge keeps a single canonical source of truth under `forge/` and uses tiny per-runtime adapters to regenerate the directory layout each AI tool expects. The generated outputs are committed so existing install commands keep working — no build step is needed for end users.
150
+
151
+ ```
152
+ forge/ # canonical, runtime-neutral content
153
+ skills/ # all skills used during spec writing
154
+ rules/ # workspace rules for spec validation
155
+ docs/ # shared docs (PLAIN_REFERENCE.md, etc.)
156
+
157
+ runtimes/ # per-runtime adapters
158
+ claude/
159
+ build.ts # generates .claude/ + .claude-plugin/ from forge/
160
+ templates/ # Claude-specific files: settings.json, hook script, plugin manifests
161
+ codex/
162
+ build.ts # generates .codex-plugin/ and .agents/plugins/ (manifest points at forge/skills/)
163
+ templates/ # Codex-specific files: plugin.json, marketplace catalog
164
+ opencode/
165
+ build.ts # generates .opencode/ from forge/
166
+ templates/ # OpenCode-specific files: package.json, .gitignore
167
+
168
+ bin/
169
+ forge-build.ts # orchestrator: runs every runtimes/*/build.ts
170
+ lib.ts # shared symlink/copy helpers
171
+
172
+ # Generated outputs (committed, do not edit by hand):
173
+ .claude/ # Claude Code plugin layout
174
+ .claude-plugin/ # Claude Code plugin manifests
175
+ .codex-plugin/ # Codex plugin manifest (its "skills" field points at forge/skills/)
176
+ .agents/plugins/ # Codex marketplace catalog
177
+ .opencode/ # OpenCode plugin layout
178
+ ```
179
+
180
+ ### Contributing
181
+
182
+ After editing anything under `forge/` or `runtimes/*/templates/`, regenerate the runtime outputs:
183
+
184
+ ```bash
185
+ npm install # required after every fresh clone (node_modules/ is gitignored)
186
+ npm run build # regenerate runtime outputs for Claude, Codex, OpenCode
187
+ npm run clean # remove generated outputs and rebuild from scratch
188
+ ```
189
+
190
+ If `npm run build` errors with `sh: tsx: command not found`, it means `node_modules/` is missing — run `npm install` first.
191
+
192
+ The build is idempotent — re-running it produces no `git diff`.
193
+
194
+ ## Available Skills
195
+
196
+ ### Core Workflow
197
+
198
+ | Skill | Description |
199
+ |-------|-------------|
200
+ | `forge-plain` | End-to-end QA interview that produces complete `.plain` spec files for a new project |
201
+ | `init-plain-project` | Lightweight project initializer — scaffolds `template/base.plain` (base impl + test reqs), a stub top-level module, the testing scripts, and `config.yaml`. No functional specs, no concepts, no dry-run. Pair with `add-feature` to grow the project feature-by-feature. |
202
+ | `add-feature` | Interview the user about a single feature, then write all the specs for it |
203
+ | `run-codeplain` | **Experimental.** Launch a `codeplain` render and supervise it end-to-end — tails `codeplain.log`, watches generated code appear, detects pathologies (stuck conformance loops, complexity errors, missing concepts, render failures), and on approval stops the renderer, hands off to the right spec-edit skill, and resumes with `--render-from`. The default render path is still the manual `codeplain <module>.plain` command. |
204
+
205
+ ### Spec Authoring
206
+
207
+ | Skill | Description |
208
+ |-------|-------------|
209
+ | `add-functional-spec` | Add a single feature spec to `***functional specs***` |
210
+ | `add-functional-specs` | Add multiple feature specs to `***functional specs***` in one pass (same per-spec checks as `add-functional-spec`) |
211
+ | `add-implementation-requirement` | Add a non-functional requirement to `***implementation reqs***` |
212
+ | `add-test-requirement` | Add a testing requirement to `***test reqs***` |
213
+ | `add-concept` | Define a new concept in `***definitions***` |
214
+ | `add-acceptance-test` | Add verification criteria under a functional spec |
215
+ | `add-resource` | Link an external file (schema, API spec) to a spec |
216
+ | `add-template` | Create or include a reusable Liquid template |
217
+
218
+ ### Module Management
219
+
220
+ | Skill | Description |
221
+ |-------|-------------|
222
+ | `create-import-module` | Create a shared template module (definitions + reqs, no functional specs) |
223
+ | `create-requires-module` | Create a module that depends on a previously built module |
224
+ | `refactor-module` | Split a large module into smaller modules connected via a requires chain |
225
+ | `consolidate-concepts` | Gather scattered concept definitions into a single shared import module |
226
+
227
+ ### Analysis and Quality
228
+
229
+ | Skill | Description |
230
+ |-------|-------------|
231
+ | `init-config-file` | Build / finalize the project's `config.yaml` file(s) from the decisions made in Phase 3. Knows the full set of valid keys derived from the `codeplain` CLI, refuses to write secrets or per-invocation flags, and produces one config per part of the project. Run at the end of `forge-plain` (just before `plain-healthcheck`) and any time the testing surface changes. |
232
+ | `plain-healthcheck` | Verification gate: validates every `config.yaml`, confirms each `*-script` field points at a real file in `test_scripts/`, and dry-runs every top module. Run whenever anything in the project is finalized — at the end of `forge-plain`, at the end of `add-feature`, after `debug-specs`, and after any single-skill edit that touches the renderable surface. |
233
+ | `check-plain-env` | Read the project's `.plain` files, `test_scripts/`, `config.yaml`(s), and `resources/`, then probe the host for every requirement **the package manager can't install**: language toolchains (`python` + `pip`, `node` + `npm`, JDK + `mvn`, Go, Rust, .NET, etc.), external services (Postgres, Redis, Docker, ...), system binaries that language packages wrap (`ffmpeg`, `tesseract`, `pdftoppm`, browser binaries, ...), hardware / drivers / accelerators (NVIDIA driver → CUDA toolkit → cuDNN → framework-sees-GPU chain), `codeplain` itself, and credential env vars. Does **not** probe individual language packages — `pip install -r requirements.txt` (and equivalents) handle those when the test scripts run. Emits a `PASS` / `WARN` / `FAIL` report with OS-specific install commands for any gaps. Read-only — never installs anything. Run on first-time setup, before rendering on a new machine, after adding a new tech to a project, or any time `command not found` shows up in test output. |
234
+ | `analyze-if-func-spec-too-complex` | Check if a spec exceeds the 200-line complexity limit |
235
+ | `analyze-func-specs` | Check a batch of specs (2+) against each other in one call and return every conflicting pair |
236
+ | `analyze-2-func-specs` | Legacy: check exactly two specs for conflicts (prefer `analyze-func-specs`) |
237
+ | `break-down-func-spec` | Split an overly complex spec into smaller specs (each ≤ 200 LOC) |
238
+ | `resolve-spec-conflict` | Resolve a conflict between two functional specs |
239
+
240
+ ### Debugging and Testing
241
+
242
+ | Skill | Description |
243
+ |-------|-------------|
244
+ | `debug-specs` | Investigate a bug by tracing generated code back to specs and fixing only the `.plain` files |
245
+ | `implement-unit-testing-script` | Generate a per-language unit-test runner (`run_unittests_<lang>.sh` / `.ps1`) |
246
+ | `implement-conformance-testing-script` | Generate a per-language conformance-test runner; picks the install-inline or activate-only variant based on whether `prepare_environment_<lang>` exists |
247
+ | `implement-prepare-environment-script` | Generate a per-language one-time setup script (`prepare_environment_<lang>.sh` / `.ps1`) that stages the build and pre-warms dependencies so conformance tests start cold; reconciles any existing conformance script to remove its now-redundant install step |
package/bin/cli.mjs ADDED
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import readline from "node:readline/promises";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const pkgRoot = path.resolve(path.dirname(__filename), "..");
10
+ const forgeDir = path.join(pkgRoot, "forge");
11
+
12
+ const AGENTS = {
13
+ claude: ".claude",
14
+ codex: ".codex",
15
+ forgecode: ".forgecode",
16
+ universal: ".agents",
17
+ };
18
+ const SCOPES = ["project", "global"];
19
+
20
+ function usage() {
21
+ console.log(`Usage: plain-forge install [options]
22
+
23
+ Options:
24
+ --agent <claude|codex|forgecode|universal> Target agent layout
25
+ --scope <project|global> Install into cwd or $HOME
26
+ --skill <name> Install only the named skill (repeatable; default: all)
27
+ -h, --help Show this help
28
+
29
+ Examples:
30
+ plain-forge install --agent claude --scope project
31
+ plain-forge install --agent universal --scope global
32
+ plain-forge install --agent claude --skill add-functional-spec --skill add-concept
33
+
34
+ Missing flags are prompted interactively.`);
35
+ }
36
+
37
+ function parseArgs(argv) {
38
+ const out = { _: [], skills: [] };
39
+ for (let i = 0; i < argv.length; i++) {
40
+ const a = argv[i];
41
+ if (a === "--agent") out.agent = argv[++i];
42
+ else if (a === "--scope") out.scope = argv[++i];
43
+ else if (a === "--skill") out.skills.push(argv[++i]);
44
+ else if (a === "-h" || a === "--help") out.help = true;
45
+ else out._.push(a);
46
+ }
47
+ return out;
48
+ }
49
+
50
+ async function promptChoice(question, choices) {
51
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
52
+ try {
53
+ while (true) {
54
+ const ans = (await rl.question(`${question} [${choices.join("/")}]: `)).trim().toLowerCase();
55
+ if (choices.includes(ans)) return ans;
56
+ console.log(` please answer one of: ${choices.join(", ")}`);
57
+ }
58
+ } finally {
59
+ rl.close();
60
+ }
61
+ }
62
+
63
+ function copyTree(srcDir, destDir, filterNames) {
64
+ if (!fs.existsSync(srcDir)) return 0;
65
+ fs.mkdirSync(destDir, { recursive: true });
66
+ let count = 0;
67
+ for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
68
+ if (filterNames && !filterNames.has(entry.name)) continue;
69
+ const src = path.join(srcDir, entry.name);
70
+ const dest = path.join(destDir, entry.name);
71
+ if (entry.isDirectory()) {
72
+ fs.cpSync(src, dest, { recursive: true, force: true, dereference: true });
73
+ } else {
74
+ fs.copyFileSync(src, dest);
75
+ }
76
+ count++;
77
+ }
78
+ return count;
79
+ }
80
+
81
+ async function cmdInstall(args) {
82
+ let agent = args.agent;
83
+ if (!agent) agent = await promptChoice("Which agent?", Object.keys(AGENTS));
84
+ if (!Object.hasOwn(AGENTS, agent)) {
85
+ console.error(`unknown agent "${agent}". valid: ${Object.keys(AGENTS).join(", ")}`);
86
+ process.exit(2);
87
+ }
88
+
89
+ let scope = args.scope;
90
+ if (!scope) scope = await promptChoice("Scope?", SCOPES);
91
+ if (!SCOPES.includes(scope)) {
92
+ console.error(`unknown scope "${scope}". valid: ${SCOPES.join(", ")}`);
93
+ process.exit(2);
94
+ }
95
+
96
+ const root = scope === "global" ? os.homedir() : process.cwd();
97
+ const baseDir = path.join(root, AGENTS[agent]);
98
+
99
+ const explicit = args.skills.filter((s) => s !== "*");
100
+ const skillFilter = explicit.length ? new Set(explicit) : null;
101
+
102
+ const skillsSrc = path.join(forgeDir, "skills");
103
+ if (skillFilter) {
104
+ for (const name of skillFilter) {
105
+ if (!fs.existsSync(path.join(skillsSrc, name))) {
106
+ console.error(`skill "${name}" not found under ${skillsSrc}`);
107
+ process.exit(2);
108
+ }
109
+ }
110
+ }
111
+
112
+ const skillsCount = copyTree(skillsSrc, path.join(baseDir, "skills"), skillFilter);
113
+ const rulesCount = copyTree(path.join(forgeDir, "rules"), path.join(baseDir, "rules"), null);
114
+ const docsCount = copyTree(path.join(forgeDir, "docs"), path.join(baseDir, "docs"), null);
115
+
116
+ console.log(`installed into ${baseDir}`);
117
+ console.log(` skills: ${skillsCount}`);
118
+ console.log(` rules: ${rulesCount}`);
119
+ console.log(` docs: ${docsCount}`);
120
+ }
121
+
122
+ async function main() {
123
+ const args = parseArgs(process.argv.slice(2));
124
+ if (args.help || args._.length === 0) {
125
+ usage();
126
+ return;
127
+ }
128
+ const cmd = args._[0];
129
+ switch (cmd) {
130
+ case "install":
131
+ await cmdInstall(args);
132
+ break;
133
+ default:
134
+ console.error(`unknown command "${cmd}"`);
135
+ usage();
136
+ process.exit(2);
137
+ }
138
+ }
139
+
140
+ main().catch((err) => {
141
+ console.error(err instanceof Error ? err.stack ?? err.message : err);
142
+ process.exit(1);
143
+ });
File without changes
@@ -0,0 +1,57 @@
1
+ ---
2
+ description: Rules for writing ***definitions*** sections in .plain files
3
+ globs: "**/*.plain"
4
+ ---
5
+
6
+ # Rules for writing `***definitions***`
7
+
8
+ When writing or editing a `***definitions***` section in a `.plain` file, always follow these rules:
9
+
10
+ ## Concept syntax
11
+ - Wrap concept names in colons: `:ConceptName:`
12
+ - Use CamelCase starting with an uppercase letter
13
+ - Valid characters: letters, digits, `+`, `-`, `.`, `_`
14
+
15
+ ## Uniqueness
16
+ - Concept names must be globally unique across the spec and all its imports
17
+ - Check for collisions with imported templates, `import` and `requires` modules before adding
18
+
19
+ ## Define before use
20
+ - A concept must be defined before it is referenced in any section (definitions, implementation reqs, functional specs, test reqs)
21
+ - Sources of definitions: the module's own `***definitions***`, an `import`ed module's definitions, or a `require`d module's `exported_concepts`
22
+
23
+ ## No circular references
24
+ - Concept references must not form cycles — if A references B, then B must not reference A (directly or indirectly)
25
+ - Insert each concept after any concepts it references
26
+
27
+ Bad — circular:
28
+
29
+ ```plain
30
+ - :Customer: is a user who has placed at least one :Order:.
31
+ - :Order: is placed by :Customer: and contains :OrderItem: entries.
32
+ ```
33
+
34
+ `:Order:` references `:Customer:`, and `:Customer:` references `:Order:`. Fix by removing the back-reference:
35
+
36
+ ```plain
37
+ - :Customer: is a user of the system.
38
+ - :Order: is placed by :Customer: and contains :OrderItem: entries.
39
+ ```
40
+
41
+ ## Exported concepts are not transitive
42
+ - If module A exports a concept and module B `requires` A, module C `requires` B does **not** gain access to A's exports
43
+ - Shared concepts belong in a common import module
44
+
45
+ ## Description quality
46
+ - Descriptions must be clear, concise, and language-agnostic
47
+ - Nest attributes and constraints as sub-bullets
48
+ - Do not use programming language constructs (generics, annotations, framework types) in definitions
49
+
50
+ ## Format
51
+
52
+ ```plain
53
+ ***definitions***
54
+ - :ConceptName: is a description of what it represents.
55
+ - Attribute one (required)
56
+ - Attribute two (optional)
57
+ ```
@@ -0,0 +1,39 @@
1
+ ---
2
+ description: Rules for using exported_concepts in .plain files
3
+ globs: "**/*.plain"
4
+ ---
5
+
6
+ # Rules for `exported_concepts`
7
+
8
+ When adding or editing `exported_concepts` in a `.plain` file's frontmatter, always follow these rules:
9
+
10
+ ## What exported_concepts does
11
+ - `exported_concepts` declares which concepts from this module are visible to modules that `require` it
12
+ - Concepts not listed in `exported_concepts` are internal to the module and invisible to downstream modules
13
+ - Only modules that use `requires` receive exported concepts — `import` gives access to all definitions, not just exports
14
+
15
+ ## When to use it
16
+ - Use `exported_concepts` on any module that other modules will `require`
17
+ - List only the concepts that downstream modules actually need to reference
18
+ - Keep the exported surface small — expose only what is necessary
19
+
20
+ ## Concepts must be defined
21
+ - Every concept listed in `exported_concepts` must be defined in the module's own `***definitions***` section
22
+ - Do not export concepts that are not defined in the module
23
+
24
+ ## Exports are not transitive
25
+ - If module A exports `:Foo:` and module B `requires` A, module C `requires` B does **not** gain access to `:Foo:`
26
+ - If module C also needs `:Foo:`, it must either `require` A directly or get it through a common import module
27
+
28
+ ## Format
29
+
30
+ ```plain
31
+ ---
32
+ import:
33
+ - airplain
34
+ exported_concepts: [":User:", ":JwtToken:"]
35
+ description: Exports User and JwtToken for downstream modules
36
+ ---
37
+ ```
38
+
39
+ List concepts as a YAML array with each concept in `:ConceptName:` notation.
@@ -0,0 +1,72 @@
1
+ ---
2
+ description: Rules for writing ***functional specs*** and ***acceptance tests*** in .plain files
3
+ globs: "**/*.plain"
4
+ ---
5
+
6
+ # Rules for writing `***functional specs***`
7
+
8
+ When writing or editing a `***functional specs***` section in a `.plain` file, always follow these rules:
9
+
10
+ ## Complexity limit
11
+ - Each functional spec must imply a **maximum of 200 changed lines of code**
12
+ - If a spec is too large, use `break-down-func-spec` to split it into multiple smaller, independent specs
13
+ - Use `analyze-if-func-spec-too-complex` to verify before inserting
14
+ - Use `analyze-func-specs` to check a spec (or a batch of specs) against all relevant existing specs in a single batched call; use `resolve-spec-conflict` for each conflicting pair it reports
15
+
16
+ ## Chronological ordering
17
+ - Specs are rendered incrementally, top to bottom
18
+ - The renderer has **no knowledge of future specs** — only previously rendered specs are in context
19
+ - A new spec can reference behavior from earlier specs but cannot assume anything about specs that come after it
20
+ - Functional specs from `requires` modules are treated as previous requirements
21
+
22
+ ## No conflicts
23
+ - The new spec must not contradict any existing functional spec
24
+ - Before adding, review all existing specs and verify compatibility
25
+ - If ambiguity exists, add explicit detail to eliminate any conflicting interpretation
26
+
27
+ ## Language agnosticism
28
+ - Write in terms of behavior, concepts, and domain logic
29
+ - Language-specific guidance belongs in `***implementation reqs***`
30
+
31
+ ## Disambiguation
32
+ - Each functional spec must be unambiguous — the renderer should have only one reasonable interpretation
33
+ - If a single line is not enough to fully disambiguate the behavior, use **nested sub-bullets** to add detail
34
+ - Nested lines clarify the parent spec — they do not introduce separate functionality
35
+ - Even with nested detail, the spec must still imply ≤ 200 lines of code
36
+
37
+ ## Deterministic interface
38
+ - Specs must be detailed enough that a developer can use the built software without reading the generated code
39
+ - All external interfaces must be explicit: REST endpoint paths and HTTP methods, CLI command names and arguments, file formats, message schemas, etc.
40
+ - Never leave interface details up to the renderer's discretion
41
+
42
+ ## Encapsulation
43
+ - Functionality must be self-contained in the spec text
44
+ - `requires` modules only receive functional specs — do not rely on implementation reqs to convey behavior
45
+ - Behavior that downstream modules need must be expressed in functional specs, not elsewhere
46
+
47
+ ## Acceptance tests
48
+ - Nest `***acceptance tests***` under a functional spec when verification criteria are needed
49
+ - Each acceptance test must be a **full workflow test** — a specific scenario that exercises the functional spec end-to-end, not a unit-level check of a single field or condition
50
+ - Do not restate the obvious behavior from the functional spec — simple, direct verifications are already auto-generated as conformance tests. Acceptance tests must go beyond that: multi-step workflows, interactions between concepts, edge-case scenarios, or end-to-end sequences that prove the feature works in a realistic context
51
+ - Each acceptance test must be a direct logical consequence of the parent spec — it illustrates, not extends
52
+ - Acceptance tests must describe concrete, verifiable outcomes — not vague qualities
53
+ - Acceptance tests must not contradict, narrow, or extend beyond the parent spec
54
+
55
+ ## Format
56
+
57
+ ```plain
58
+ ***functional specs***
59
+
60
+ - Implement the entry point for :App:.
61
+
62
+ - :User: should be able to add :Task:. Only valid :Task: items can be added.
63
+
64
+ - :User: should be able to send a :Message: to a :Conversation:.
65
+ - A :Message: must have non-empty content.
66
+ - The :Message: is appended to the end of the :Conversation:.
67
+ - All :Participant: members of the :Conversation: can see the new :Message:.
68
+
69
+ ***acceptance tests***
70
+ - Sending a :Message: to a :Conversation: with three participants should
71
+ make the message visible to all three.
72
+ ```
@@ -0,0 +1,50 @@
1
+ ---
2
+ description: Rules for writing ***implementation reqs*** sections in .plain files
3
+ globs: "**/*.plain"
4
+ ---
5
+
6
+ # Rules for writing `***implementation reqs***`
7
+
8
+ When writing or editing an `***implementation reqs***` section in a `.plain` file, always follow these rules:
9
+
10
+ ## HOW, not WHAT
11
+ - Implementation reqs describe **how** the software should be built, not **what** it should do
12
+ - Observable behavior (endpoints, business rules, user-facing features) belongs in `***functional specs***`
13
+ - Internal structure, technology choices, and coding guidance belong here
14
+
15
+ ## What belongs here
16
+ - Technology choices: language, framework, runtime version
17
+ - Architectural constraints: patterns, layering, dependency rules
18
+ - Coding standards: naming conventions, style guidelines
19
+ - Data formats: serialization, encoding, transformation rules
20
+ - Error handling: strategies, retry logic, exception hierarchies
21
+ - Algorithm descriptions: specific approaches when behavior alone is insufficient
22
+ - Performance guidance: memory constraints, streaming requirements, batching strategies
23
+ - Language-specific constructs: generics, annotations, framework-specific types and idioms
24
+
25
+ ## What does NOT belong here
26
+ - Behavior and features → `***functional specs***`
27
+ - Concept definitions → `***definitions***`
28
+ - Conformance test instructions → `***test reqs***`
29
+
30
+ ## Encapsulation warning
31
+ - `requires` modules only receive functional specs from their dependencies — not implementation reqs
32
+ - If downstream modules need certain behavior to be visible, express it in functional specs, not here
33
+
34
+ ## No duplication
35
+ - Do not duplicate guidance already present in the file or its imports
36
+ - Check imported templates before adding a new req
37
+
38
+ ## Concept references
39
+ - Reference defined `:Concepts:` where they add clarity
40
+ - All referenced concepts must already be defined in `***definitions***`
41
+ - Implementation reqs in non-leaf sections apply to all subsections
42
+
43
+ ## Format
44
+
45
+ ```plain
46
+ ***implementation reqs***
47
+ - :Implementation: should be in Python 3.12.
48
+ - :Implementation: should use pip for dependency management.
49
+ - When writing CSV files, :Implementation: should use streaming writes to avoid holding large datasets in memory.
50
+ ```