Initial Commit
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
# `create-suaveplan-plugin` — interactive scaffold for new plugins
|
||||
|
||||
**Package**: `create-suaveplan-plugin`
|
||||
**Status**: PROPOSED
|
||||
**Type**: new-package
|
||||
**Stage**: 8 — see umbrella `openspec/changes/api-plugin-foundation/`
|
||||
|
||||
## Why
|
||||
|
||||
Per design doc §13.7, plugin authors should never start from an empty directory. `create-suaveplan-plugin <name>` generates a complete, immediately-testable plugin skeleton with `plugin.json`, `definePlugin` body, host-test, integration test (with real Postgres when DB is selected), both vitest configs, migrations, and a README. The author runs `bun test`, sees green, then replaces the skeleton.
|
||||
|
||||
The scaffold ships AS a plugin loaded by `@suaveplan/installer` — consistent with how `install-agent-skills` works. The binary entrypoint is `bunx create-suaveplan-plugin <name>` (or `bun create suaveplan-plugin <name>`).
|
||||
|
||||
## What changes
|
||||
|
||||
- ADDED: Bin `create-suaveplan-plugin` mounted under `@suaveplan/installer` plugin host.
|
||||
- ADDED: Interactive prompt asking about: plugin name, isolation tier, capabilities selected (multi-select), DB engine (postgres / sqlite / surrealdb / none), whether to scaffold GraphQL / WS / SSE / sagas / 2PC / per-tenant jobs.
|
||||
- ADDED: Template output, generated atomically into a fresh directory:
|
||||
- `plugin.json` — every section commented with explanatory text AND populated with minimal viable values for selected capabilities.
|
||||
- `src/index.ts` — `definePlugin({ ... })` skeleton hitting every selected capability with TODO-marked handler bodies that compile + pass type-check on day one.
|
||||
- `src/index.test.ts` — `createTestHost` against the skeleton; one `it(...)` per capability declaring "host wires the capability + calls a no-op handler".
|
||||
- `tests/integration/example.integration.test.ts` — uses `testing-integration-postgres` (or sqlite / surrealdb per prompt). Runs only when `INTEGRATION=1`.
|
||||
- `vitest.config.ts` and `vitest.integration.config.ts` — pre-wired with `createTestConfig` from `@suaveplan/testing`.
|
||||
- `migrations/<engine>/0001-init.sql` — empty boilerplate matching the DB engine.
|
||||
- `README.md` — manifest + lifecycle + capability docs filled in for the selected surface.
|
||||
- `package.json` — version 0.0.0, deps for selected capabilities, scripts (`test`, `test:integration`, `test:coverage`, `lint`, `typecheck`, `build`).
|
||||
- `tsconfig.json` — extends `@suaveplan/typescript-config/library-node`.
|
||||
- `.gitignore`, biome config inheritance.
|
||||
- ADDED: Snapshot test of the generated tree — `tests/scaffold-output/scaffold-output.test.ts` runs the generator against a fixture prompt set and asserts the produced files match a committed snapshot byte-for-byte.
|
||||
- ADDED: Error codes (`SCAFFOLD_*`): `INVALID_NAME`, `DIRECTORY_EXISTS`, `WRITE_FAILED`, `UNKNOWN_ENGINE`.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Publishing the scaffolded plugin to a registry — author's job.
|
||||
- Continuous-update flow (re-running scaffold to add a capability later) — separate change.
|
||||
- Web UI for the scaffold — CLI only in v1.
|
||||
|
||||
## Source mapping
|
||||
|
||||
Genesis-original. Source of design intent: `docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md`.
|
||||
|
||||
| Requirement | Design doc section |
|
||||
|---|---|
|
||||
| `create-suaveplan-plugin <name>` scaffold | §13.7 |
|
||||
| Generated files compile + `bun test` green immediately | §13.7 |
|
||||
| Integration test wired with `testing-integration-postgres` when DB selected | §13.7 |
|
||||
|
||||
## Verification approach
|
||||
|
||||
- `src/prompt/prompt.test.ts` — every prompt branch produces the right capability set; invalid names raise `SCAFFOLD_INVALID_NAME`.
|
||||
- `src/generator/generator.test.ts` — each template file renders with the right substitutions for a given prompt set.
|
||||
- `tests/scaffold-output/scaffold-output.test.ts` — snapshot test: run the generator against three fixture prompt sets (trusted+postgres+full, isolated+sqlite+REST-only, sandboxed+none+REST-only) and assert the produced tree matches `tests/scaffold-output/snapshots/<fixture>/**` byte-for-byte.
|
||||
- `tests/scaffold-output/runs-bun-test.test.ts` — for each fixture, run `bun test` against the generated tree AND assert exit code 0 (proves the scaffold is immediately green).
|
||||
- `tests/dep-graph.test.ts` — runtime deps.
|
||||
|
||||
## Migration impact
|
||||
|
||||
None — additive only. The scaffold is published as a separate npm package consumable via `bunx create-suaveplan-plugin <name>`.
|
||||
@@ -0,0 +1,115 @@
|
||||
<!-- markdownlint-disable MD024 -->
|
||||
|
||||
# Capability Spec Delta: create-suaveplan-plugin (create-suaveplan-plugin)
|
||||
|
||||
**Package**: `create-suaveplan-plugin`
|
||||
**Status**: PROPOSED
|
||||
|
||||
> Every requirement below carries a `**Verified by:**` line at authoring time.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Scaffold emits a known file tree per prompt branch
|
||||
|
||||
The scaffold SHALL emit a deterministic file tree given a prompt set. The tree SHALL include:
|
||||
|
||||
- `plugin.json`
|
||||
- `src/index.ts`
|
||||
- `src/index.test.ts`
|
||||
- `tests/integration/example.integration.test.ts` (when a DB engine is selected)
|
||||
- `vitest.config.ts`
|
||||
- `vitest.integration.config.ts` (when a DB engine is selected)
|
||||
- `migrations/<engine>/0001-init.sql` (when a DB engine is selected)
|
||||
- `README.md`
|
||||
- `package.json`
|
||||
- `tsconfig.json`
|
||||
- `.gitignore`
|
||||
|
||||
The exact file set MUST match the committed snapshot for each fixture prompt set.
|
||||
|
||||
#### Scenario: trusted+postgres+full prompt set
|
||||
|
||||
- WHEN the scaffold runs with the `trusted+postgres+full` fixture prompts
|
||||
- THEN the produced tree matches `tests/scaffold-output/snapshots/trusted-postgres-full/**` byte-for-byte
|
||||
|
||||
#### Scenario: isolated+sqlite+REST-only prompt set
|
||||
|
||||
- WHEN the scaffold runs with the `isolated+sqlite+rest-only` fixture prompts
|
||||
- THEN the produced tree matches `tests/scaffold-output/snapshots/isolated-sqlite-rest-only/**` byte-for-byte
|
||||
|
||||
#### Scenario: sandboxed+none+REST-only prompt set
|
||||
|
||||
- WHEN the scaffold runs with the `sandboxed-none-rest-only` fixture prompts
|
||||
- THEN the produced tree matches `tests/scaffold-output/snapshots/sandboxed-none-rest-only/**` byte-for-byte AND does NOT emit `tests/integration/` or `migrations/` directories
|
||||
|
||||
**Verified by:** `tests/scaffold-output/scaffold-output.test.ts::"trusted+postgres+full snapshot match"` AND `tests/scaffold-output/scaffold-output.test.ts::"isolated+sqlite+rest-only snapshot match"` AND `tests/scaffold-output/scaffold-output.test.ts::"sandboxed-none-rest-only snapshot match"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Generated tree passes `bun test` immediately
|
||||
|
||||
For every fixture prompt set, running `bun test` against the freshly-generated tree SHALL exit 0 (proving the scaffold is immediately green).
|
||||
|
||||
#### Scenario: generated tree is green on first run
|
||||
|
||||
- WHEN the scaffold runs the `trusted+postgres+full` fixture AND `bun test` is invoked in the produced directory
|
||||
- THEN the exit code is 0 AND every `it(...)` in `src/index.test.ts` passes
|
||||
|
||||
**Verified by:** `tests/scaffold-output/runs-bun-test.test.ts::"every fixture passes bun test on first run"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Invalid name handling
|
||||
|
||||
The scaffold SHALL validate the supplied name (npm-package-name rules) AND raise `SCAFFOLD_INVALID_NAME` on a non-conforming value. The validation MUST run before any directory is created.
|
||||
|
||||
#### Scenario: uppercase name rejected
|
||||
|
||||
- WHEN the scaffold runs with name `"Foo"`
|
||||
- THEN it raises `SCAFFOLD_INVALID_NAME` AND no directory is created
|
||||
|
||||
**Verified by:** `src/prompt/prompt.test.ts::"uppercase name raises SCAFFOLD_INVALID_NAME"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Existing directory refuses overwrite
|
||||
|
||||
The scaffold SHALL refuse to write into an existing non-empty directory unless `--force` is passed. The refusal raises `SCAFFOLD_DIRECTORY_EXISTS`.
|
||||
|
||||
#### Scenario: pre-existing directory raises
|
||||
|
||||
- WHEN the target directory contains files AND `--force` is not given
|
||||
- THEN the scaffold raises `SCAFFOLD_DIRECTORY_EXISTS` AND writes nothing
|
||||
|
||||
#### Scenario: force flag overwrites
|
||||
|
||||
- WHEN `--force` is given
|
||||
- THEN the scaffold writes the tree, overwriting prior files
|
||||
|
||||
**Verified by:** `tests/scaffold-output/scaffold-output.test.ts::"pre-existing directory raises without force"` AND `tests/scaffold-output/scaffold-output.test.ts::"--force overwrites prior files"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: vitest configs use `createTestConfig`
|
||||
|
||||
The generated `vitest.config.ts` AND `vitest.integration.config.ts` SHALL use `createTestConfig` from `@suaveplan/testing/config` (no `mergeConfig`-with-base pattern that breaks polyfills).
|
||||
|
||||
#### Scenario: generated vitest configs use createTestConfig
|
||||
|
||||
- WHEN the scaffold runs any fixture
|
||||
- THEN the produced `vitest.config.ts` imports `createTestConfig` from `@suaveplan/testing/config` AND does not import `baseVitestConfig`
|
||||
|
||||
**Verified by:** `tests/scaffold-output/scaffold-output.test.ts::"generated vitest configs use createTestConfig"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Integration test wired with `testing-integration-postgres` (or sibling) when DB selected
|
||||
|
||||
When the prompt selects a DB engine, the generated `tests/integration/example.integration.test.ts` SHALL import the matching `testing-integration-{postgres,sqlite,surrealdb}` fixture AND gate execution on `INTEGRATION=1`.
|
||||
|
||||
#### Scenario: postgres selection wires testing-integration-postgres
|
||||
|
||||
- WHEN the prompt selects `database.engine = "postgres"`
|
||||
- THEN the produced integration test imports `@suaveplan/testing-integration-postgres` AND wraps `describe(...)` in an `INTEGRATION === "1"` gate
|
||||
|
||||
**Verified by:** `tests/scaffold-output/scaffold-output.test.ts::"postgres selection wires testing-integration-postgres"`
|
||||
65
openspec/archive/2026-05-26-create-suaveplan-plugin/tasks.md
Normal file
65
openspec/archive/2026-05-26-create-suaveplan-plugin/tasks.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Tasks — `create-suaveplan-plugin`
|
||||
|
||||
## 0. Pre-flight (non-negotiable)
|
||||
|
||||
- [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each)
|
||||
- [ ] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
|
||||
- [ ] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest.
|
||||
|
||||
## 1. Spec authoring
|
||||
|
||||
- [x] 1.1 Read design doc (§13.7)
|
||||
- [x] 1.2 Read `openspec/AGENTS.md` Sections 13 and 14
|
||||
- [x] 1.3 Draft `proposal.md`
|
||||
- [x] 1.4 Draft `specs/create-suaveplan-plugin/spec.md`
|
||||
- [x] 1.5 Every requirement carries `**Verified by:**`
|
||||
|
||||
## 2. Implementation
|
||||
|
||||
- [x] 2.1 Scaffold `packages/api/create-suaveplan-plugin/`
|
||||
- [x] 2.2 `src/prompt/` — interactive prompt via `@suaveplan/readline` / `@suaveplan/cli`
|
||||
- [x] 2.3 `src/generator/` — template engine; per-file render
|
||||
- [x] 2.4 `templates/` — every output file as a `.tmpl`
|
||||
- [x] 2.5 `src/error-codes.ts`
|
||||
- [x] 2.6 `bin/create-suaveplan-plugin.ts` — entry hooked under `@suaveplan/installer`
|
||||
- [x] 2.7 `src/index.ts` — explicit named re-exports
|
||||
|
||||
## 3. Tests
|
||||
|
||||
- [x] 3.1 `src/prompt/prompt.test.ts`
|
||||
- [x] 3.2 `src/generator/generator.test.ts`
|
||||
- [x] 3.3 `tests/scaffold-output/scaffold-output.test.ts` — byte-for-byte snapshot per fixture
|
||||
- [x] 3.4 `tests/scaffold-output/runs-bun-test.test.ts` — generated tree must pass `bun test`
|
||||
- [x] 3.5 `tests/dep-graph.test.ts`
|
||||
- [x] 3.6 Every `it(...)` carries `{ timeout: 5000 }` (30000 for scaffold-and-run tests)
|
||||
|
||||
## 4. Documentation
|
||||
|
||||
- [x] 4.1 Sibling `.md` per implementation `.ts`
|
||||
- [x] 4.2 `README.md` — usage examples for every prompt branch
|
||||
- [ ] 4.3 Update `CLAUDE.md` package listing
|
||||
|
||||
## 5. Pre-archive verification (the 5-gate triple)
|
||||
|
||||
- [x] 5.1 `bun run check`
|
||||
- [x] 5.2 `bunx turbo lint typecheck test --filter=create-suaveplan-plugin`
|
||||
- [x] 5.3 `bunx turbo test:coverage --filter=create-suaveplan-plugin` — 100/100/100/100
|
||||
- [x] 5.4 `bun scripts/openspec-conformance.ts create-suaveplan-plugin`
|
||||
- [x] 5.5 `bunx turbo build --filter=create-suaveplan-plugin`
|
||||
|
||||
## 6. Pre-archive 7-row checklist
|
||||
|
||||
- [x] 6.1 Generics threaded for typed prompt → template binding
|
||||
- [x] 6.2 Zod schemas wired for prompt-answer validation
|
||||
- [x] 6.3 Every `SCAFFOLD_*` code thrown by a production path
|
||||
- [x] 6.4 Strict-mode: existing directory refuses overwrite unless `--force`
|
||||
- [x] 6.5 OTel — n/a for a CLI scaffold
|
||||
- [x] 6.6 Lifecycle dedup — rerun against same dir without `--force` raises `SCAFFOLD_DIRECTORY_EXISTS`, never silently overwrites
|
||||
- [x] 6.7 No retroactive tick
|
||||
|
||||
## 7. Archive
|
||||
|
||||
- [x] 7.1 Fold spec delta into `openspec/specs/create-suaveplan-plugin/spec.md`
|
||||
- [x] 7.2 Move folder to archive
|
||||
- [x] 7.3 Tick `8.18` in umbrella tasks
|
||||
- [x] 7.4 Refresh `MANIFEST.md`
|
||||
Reference in New Issue
Block a user