diff --git a/.changeset/README.md b/.changeset/README.md index 7f82013..2d5f269 100644 --- a/.changeset/README.md +++ b/.changeset/README.md @@ -4,7 +4,7 @@ Hello and welcome! This folder is managed by `@changesets/cli`. Full docs: https ## Workflow for this repo -The ten publishable packages — `acture`, `@acture/state-zustand`, `@acture/state-redux`, `@acture/palette-react`, `@acture/hotkeys`, `@acture/forms-autoform`, `@acture/forms-rjsf`, `@acture/mcp`, `@acture/ai-vercel`, `@acture/migration` — share a **fixed** version (see `config.json`). When any of them changes, all ten get a matching bump. +The ten publishable packages — `acture`, `acture-state-zustand`, `acture-state-redux`, `acture-palette-react`, `acture-hotkeys`, `acture-forms-autoform`, `acture-forms-rjsf`, `acture-mcp`, `acture-ai-vercel`, `acture-migration` — share a **fixed** version (see `config.json`). When any of them changes, all ten get a matching bump. Add a changeset to any non-trivial PR: diff --git a/.changeset/config.json b/.changeset/config.json index 6e6e3b2..7feaf55 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -5,15 +5,15 @@ "fixed": [ [ "acture", - "@acture/state-zustand", - "@acture/state-redux", - "@acture/palette-react", - "@acture/hotkeys", - "@acture/forms-autoform", - "@acture/forms-rjsf", - "@acture/mcp", - "@acture/ai-vercel", - "@acture/migration" + "acture-state-zustand", + "acture-state-redux", + "acture-palette-react", + "acture-hotkeys", + "acture-forms-autoform", + "acture-forms-rjsf", + "acture-mcp", + "acture-ai-vercel", + "acture-migration" ] ], "linked": [], @@ -21,9 +21,9 @@ "baseBranch": "main", "updateInternalDependencies": "patch", "ignore": [ - "@acture/example-graph-editor", - "@acture/example-drop-in", - "@acture/example-zustand-wrap-before", - "@acture/example-zustand-wrap-after" + "acture-example-graph-editor", + "acture-example-drop-in", + "acture-example-zustand-wrap-before", + "acture-example-zustand-wrap-after" ] } diff --git a/.claude/skills/acture-architecture-primer/SKILL.md b/.claude/skills/acture-architecture-primer/SKILL.md index 58c0a77..9bb4a44 100644 --- a/.claude/skills/acture-architecture-primer/SKILL.md +++ b/.claude/skills/acture-architecture-primer/SKILL.md @@ -1,6 +1,6 @@ --- name: acture-architecture-primer -description: Load the conceptual model of acture's command-dispatch architecture. Use this skill at the start of ANY non-trivial task in the acture repository — it covers the three primitives (state model, command registry, schema bridge), the eight consumer surfaces, the three positioning paths (greenfield-pure, strangler-fig, footprint-minimizer), and the rule of three. Triggers on phrases like "what is acture", "how does the architecture work", "the three primitives", "the consumer surfaces", or when starting a new phase. Do NOT use this for narrow API/typing questions — load `acture-command-record-shape` or another targeted skill instead. +description: Load the conceptual model of acture's command-dispatch architecture. Use this skill at the start of ANY non-trivial task in the acture repository — it covers the three primitives (state model, command registry, schema bridge), the eight consumer surfaces, acture's dev-tool-first positioning and its two flexibility dimensions, and the rule of three. Triggers on phrases like "what is acture", "how does the architecture work", "the three primitives", "the consumer surfaces", or when starting a new phase. Do NOT use this for narrow API/typing questions — load `acture-command-record-shape` or another targeted skill instead. --- # acture architecture primer @@ -11,7 +11,7 @@ You are working on **acture**, a TypeScript library implementing the command-dis Acture is built on three interlocking primitives. They are not optional layers — they are the minimal framework. -1. **State model** — a typed schema over application state. Defines *what exists*. Acture is agnostic about the state library; an adapter interface (`StateAdapter`) lets zustand, Redux Toolkit, Jotai, etc. plug in cleanly. Reference adapters: `@acture/state-zustand` (Phase 1) and `@acture/state-redux` (Phase 2). +1. **State model** — a typed schema over application state. Defines *what exists*. Acture is agnostic about the state library; an adapter interface (`StateAdapter`) lets zustand, Redux Toolkit, Jotai, etc. plug in cleanly. Reference adapters: `acture-state-zustand` (Phase 1) and `acture-state-redux` (Phase 2). 2. **Command registry** — a centralized map from a string `id` to a `CommandRecord`. Defines *what can be done*. Every user-facing operation flows through `registry.dispatch(id, params, ctx?)`. Carries the rest of the metadata: title, description, parameters, when-clauses, keybindings, tier marker, atomic-or-handoff flag. @@ -21,22 +21,27 @@ Acture is built on three interlocking primitives. They are not optional layers A single `CommandRecord` simultaneously serves: -1. **Command palette** + **keyboard shortcuts** (`@acture/palette-react` + `@acture/hotkeys`) -2. **AI tool calling** (`@acture/ai-vercel`) — schema → JSON Schema for LLM function calling -3. **MCP server** (`@acture/mcp`) — `{name, description, inputSchema}` tool emission +1. **Command palette** + **keyboard shortcuts** (`acture-palette-react` + `acture-hotkeys`) +2. **AI tool calling** (`acture-ai-vercel`) — schema → JSON Schema for LLM function calling +3. **MCP server** (`acture-mcp`) — `{name, description, inputSchema}` tool emission 4. **End-to-end testing** — same `dispatch(id, params)` used by tests at unit/component/E2E levels 5. **Telemetry** (post-v1) — middleware logging every dispatch 6. **Undo/redo** (post-v1) — `Result` reserves `patches?` and `effects?` 7. **Macros** (post-v1) — record/replay of `{commandId, params}` pairs 8. **Extensions/plugins** — third-party additions via the registry API -## The three positioning paths +## Positioning: acture is a development tool first -Same core. Different documentation and different adapter package combinations. +**Read `docs/positioning.md` — it is canonical and governs every user-facing word.** The headline: -- **Greenfield-pure** — `acture` + `@acture/state-zustand` + consumer adapters. No migration package. -- **Strangler-fig migration** — `acture` + `@acture/migration` + existing state lib's adapter. Uses `wrapMutation`, `actureMiddleware`, `chooseImplementation`, `shadowCompare` (per research-4 §A.6). Graduates eventually. -- **Footprint-minimizer drop-in** — `acture` + `@acture/palette-react` + `@acture/mcp` + minimal `wrapMutation`. No deeper migration intent. +> acture is a place developers (human or agent) go to get AI-agentic help building, migrating to, and maintaining a command-dispatch architecture. It is delivered primarily as **skills, patterns, and codemods**; the `acture-*` npm packages are an **optional accelerator**, not the product. A developer must be able to use acture purely as a development tool with **no `acture-*` dependency added to their project** — unless they explicitly choose to. + +Every engagement sits on **two independent dimensions** — keep both open, never collapse one into a default: + +1. **Core vs strangler-fig** — is command dispatch designed in, or wrapped into an existing codebase incrementally? (The conceptual paper's three "positioning paths" — greenfield-pure, footprint-minimizer, strangler-fig — collapse onto this axis: the first two are *core*, the third is *strangler-fig*.) +2. **Agent-written vs package-reuse** — does the agent write the integration into the project (zero acture dependency, maximum adaptability), or install an `acture-*` package (less code to own, tested, at the cost of a dependency)? Decided **per consumer**, not per project. + +**Standing rule:** whenever your task touches a consumer surface or a consumer-specific `acture-*` package, also load the **`acture-consumer-integration`** skill. It operationalises the positioning above; this primer only states it. ## The rule of three @@ -69,8 +74,11 @@ From the central paper §6: ## Where the canonical sources live - **Conceptual:** `docs/command_dispatch_journal_article.md` -- **Plan:** `docs/v1_plan.md` -- **Execution:** `docs/implementation_plan.md` +- **Positioning:** `docs/positioning.md` — canonical; governs every user-facing word (dev-tool-first, the two dimensions) +- **Forward plan + status:** `docs/roadmap.md` — what's done, what's next, what's deferred +- **Immediate handoff:** `docs/next_session.md` +- **v1 plan (historical):** `docs/v1_plan.md` +- **Execution (historical):** `docs/implementation_plan.md` - **Don'ts:** `docs/redesign_takeaways.md` §3 (also the `acture-hard-donts` skill) - **Research:** `docs/research/acture_research_{1..5} ...md` — load only what's relevant to your task @@ -80,6 +88,7 @@ You should now be able to answer for any acture task: - Which primitive does this touch? - Which consumer surfaces does this affect? - Does this risk one of the four named anti-patterns? -- Is this in scope for the current phase (per `implementation_plan.md`), or post-v1? +- Does this honour the dev-tool-first positioning — could a developer do this with zero `acture-*` dependency? +- If it touches a consumer surface, did you also load `acture-consumer-integration`? If you can't answer those, re-read the relevant section. diff --git a/.claude/skills/acture-command-record-shape/SKILL.md b/.claude/skills/acture-command-record-shape/SKILL.md index 942975c..cbd08b3 100644 --- a/.claude/skills/acture-command-record-shape/SKILL.md +++ b/.claude/skills/acture-command-record-shape/SKILL.md @@ -61,13 +61,13 @@ type CommandRecord

= { /** Tier (research-5). Authoritative source is the JSDoc tag on the defineCommand * call site (@stable / @experimental / @internal / @deprecated). The build step - * (@acture/build-tier) mirrors the tag into this field. Authors normally do NOT + * (acture-build-tier) mirrors the tag into this field. Authors normally do NOT * write this field manually — they write the JSDoc tag. */ tier?: "stable" | "experimental" | "internal" | "deprecated"; /** Free-text reason injected by the build step from @deprecated . - * Adapter packages (@acture/mcp, @acture/ai-vercel) prepend - * `[DEPRECATED — ]` to the description; @acture/devtools surfaces + * Adapter packages (acture-mcp, acture-ai-vercel) prepend + * `[DEPRECATED — ]` to the description; acture-devtools surfaces * it in the inspector. Added in v1.0 (Phase 4) under the rule of three. */ deprecationReason?: string; @@ -98,7 +98,7 @@ type Result = | { ok: false; error: { code: string; message: string; details?: unknown } }; ``` -The `patches?` and `effects?` fields are **reserved hooks** for `@acture/undo` (post-v1). v1 core ignores them. They exist so adding undo later is non-breaking. +The `patches?` and `effects?` fields are **reserved hooks** for `acture-undo` (post-v1). v1 core ignores them. They exist so adding undo later is non-breaking. ## Fields that are deliberately NOT on this record diff --git a/.claude/skills/acture-consumer-integration/SKILL.md b/.claude/skills/acture-consumer-integration/SKILL.md new file mode 100644 index 0000000..29b4c67 --- /dev/null +++ b/.claude/skills/acture-consumer-integration/SKILL.md @@ -0,0 +1,88 @@ +--- +name: acture-consumer-integration +description: The foundational pattern for building a command-dispatch CONSUMER in a target project — a command palette, keyboard shortcuts, AI tool calling, an MCP endpoint, e2e testing, macros, telemetry, undo. Load this whenever you are adding a consumer surface to a user's app, OR whenever you are working on a consumer-specific acture-* package (acture-palette-react, acture-hotkeys, acture-mcp, acture-ai-vercel, acture-e2e-playwright, …). It encodes acture's dev-tool-first positioning: the agent-written path is always viable, acture packages are an optional opt-in accelerator, and tool-library choices belong to the user. Triggers on "add a command palette", "add hotkeys", "wire up MCP", "expose commands to an AI", "set up e2e testing", "build a consumer", "which acture package should I use", "do I need to install acture". +--- + +# acture consumer integration — the foundational pattern + +A **consumer** is anything that invokes commands from the registry: a command palette, keyboard shortcuts, AI tool calling, an MCP server, e2e tests, macros, telemetry, undo/redo, third-party extensions. Each is a *projection* of the same `CommandRecord` set. + +This skill is the pattern every consumer-specific skill builds on. Load it before adding any consumer to a user's project, and before working on any consumer-specific `acture-*` package. The canonical positioning it enforces lives in `docs/positioning.md` — read that if anything here is unclear. + +## The one rule you cannot break + +> The user must be able to get a working consumer **without adding any `acture-*` dependency to their project**, unless they explicitly choose to. + +If you find yourself reaching for `npm install acture-` as the *default* move, stop. That is one of two options, and not the default one. + +## Two dimensions — always keep both open + +Before writing anything, locate the task on both axes (see `docs/positioning.md` §3): + +1. **Core vs strangler-fig** — is command dispatch being designed in, or wrapped into an existing codebase incrementally? (If strangler-fig, also load the `migration-*` skills.) +2. **Agent-written vs package-reuse** — will you hand-write this consumer following acture's patterns, or install an acture package that implements it? + +Dimension 2 is decided **per consumer**, not per project. Hand-write the palette, reuse `acture-mcp`, skip a state adapter — all in the same project — is normal. + +## The decision procedure + +For each consumer the user wants: + +### Step 1 — Surface the choice to the user + +Do not silently pick. Present the two paths with their real trade-offs: + +- **Agent-written** — you write the integration into their codebase. They own every line, zero acture dependency, maximum adaptability to their stack. Cost: more code in their repo, and they maintain it. +- **Package-reuse** — install the relevant `acture-*` package. Less code to own, tested behaviour, faster. Cost: a dependency to track, a surface they don't control. + +If the user has a stated preference (in `CLAUDE.md`, in conversation, in the migration plan), follow it. Otherwise ask. + +### Step 2 — Identify the tool-library dependency (this one is usually unavoidable) + +Almost every consumer rests on *some* third-party library: + +| Consumer | Typical tool libraries | acture's per-tool package | +| ------------------- | ----------------------------------------------- | ------------------------- | +| Command palette | cmdk, kbar, custom | `acture-palette-react` (cmdk) | +| Keyboard shortcuts | tinykeys, react-hotkeys-hook, custom | `acture-hotkeys` (tinykeys) | +| AI tool calling | Vercel AI SDK, Anthropic SDK, OpenAI SDK | `acture-ai-vercel` (Vercel AI SDK) | +| MCP server | `@modelcontextprotocol/sdk` | `acture-mcp` | +| Parameter forms | react-hook-form + Zod, rjsf, custom | `acture-forms-autoform`, `acture-forms-rjsf` | +| e2e testing | Playwright, Cypress, custom | `acture-e2e-playwright` *(planned)* | + +**This dependency belongs to the user's consumer code, and the choice of tool is theirs.** acture does not mandate cmdk or Playwright or the Vercel SDK. The per-tool `acture-*` packages each bundle *one* known-good integration with *one* tool — useful if the user already chose that tool, irrelevant if they chose another. Name the realistic options; respect the user's pick; never imply acture's choice is the only choice. + +So a consumer integration may legitimately add **a tool-library dependency** while adding **zero acture dependency** — that is the agent-written path, and it fully honours the dev-tool-first principle. + +### Step 3 — Build it + +- **Agent-written path:** write the integration directly in the user's project. The only thing it needs from acture is the *pattern* (how to iterate the registry, how to call `dispatch(id, params)`, how to read `keybinding` / `when` / schema off a `CommandRecord`). It needs nothing *imported* from acture unless the user is also using `acture` core for the registry itself. Reference the per-surface skill and, if helpful, the corresponding `acture-*` package's source as a worked example to adapt — not to import. +- **Package-reuse path:** install the `acture-*` package, wire it to the user's registry. The package takes the registry as input and translates; it never owns business logic. + +### Step 4 — Record the choice + +Note in the project's migration/adoption notes (or wherever decisions are tracked) which path was taken per consumer and why. A later session — or a `migration-graduate` pass — needs to know whether a dependency was a deliberate trade or an accident. + +## When you are working ON a consumer-specific acture-* package + +The same positioning applies inward: + +- The package is an *optional accelerator*. Its README must document the hand-written alternative, not just `npm install`. +- It **translates** the registry to its target surface; it holds no business logic and makes no architectural decisions (hard-don't #3). +- It depends on exactly one tool library, declared as a peer dependency, and its docs frame that tool as the user's choice — naming it, not selling it. +- It must be independently installable and independently optional. Nothing else in acture may assume it is present. + +## Checklist before you finish + +- [ ] Did you present (or follow a stated preference for) the agent-written vs package-reuse choice — rather than defaulting to install? +- [ ] If you added an `acture-*` dependency, was it an explicit, recorded user decision? +- [ ] If you added a tool-library dependency, is it framed as the user's consumer code's dependency and the user's tool choice? +- [ ] Could the user have gotten a working consumer with zero acture dependency? (If no — you drifted. Fix it.) +- [ ] Did you load the per-surface skill (palette / hotkeys / mcp / ai / e2e / …) for the specific consumer, and `migration-*` skills if this is a strangler-fig context? + +## See also + +- `docs/positioning.md` — the canonical positioning this skill enforces. +- `acture-architecture-primer` — the eight consumer surfaces and the three primitives. +- `acture-hard-donts` — the merge checklist; #2 (no god-package) and #3 (adapters translate) are the ones this skill operationalises. +- Per-surface skills (`acture-palette-design`, and the consumer skills added over time) for the specifics of each surface. diff --git a/.claude/skills/acture-hard-donts/SKILL.md b/.claude/skills/acture-hard-donts/SKILL.md index 4f28713..fb3b77f 100644 --- a/.claude/skills/acture-hard-donts/SKILL.md +++ b/.claude/skills/acture-hard-donts/SKILL.md @@ -29,11 +29,11 @@ Source: `docs/redesign_takeaways.md` §3. ## 3. No business logic in adapter packages -**Symptom:** `@acture/mcp` decides which commands to expose based on user preferences. `@acture/palette-react` runs an algorithm to pick command order beyond `defaultScore`. `@acture/ai-vercel` validates parameters in a way that differs from the core dispatcher. +**Symptom:** `acture-mcp` decides which commands to expose based on user preferences. `acture-palette-react` runs an algorithm to pick command order beyond `defaultScore`. `acture-ai-vercel` validates parameters in a way that differs from the core dispatcher. **Why it's wrong:** Logic that should be uniform across surfaces drifts. Adapters become couplers, not translators. -**Fix:** Adapters translate. They iterate the registry and emit per-target format. If you find yourself adding behavior, it belongs in `@acture/core`. If the behavior is per-consumer-specific (e.g., "MCP cares about tier filtering"), it goes in core as a parameterized projection (`registry.toMCPServer({ tiers })`). +**Fix:** Adapters translate. They iterate the registry and emit per-target format. If you find yourself adding behavior, it belongs in `acture-core`. If the behavior is per-consumer-specific (e.g., "MCP cares about tier filtering"), it goes in core as a parameterized projection (`registry.toMCPServer({ tiers })`). ## 4. No `if (mode === ...)` in shared helpers @@ -74,7 +74,7 @@ Set an explicit 3–6 month deadline on every experimental feature. If it hasn't ## 8. No bundling a UI kit -**Symptom:** `@acture/palette-react` imports from `@mui/material` or `shadcn/ui` directly. +**Symptom:** `acture-palette-react` imports from `@mui/material` or `shadcn/ui` directly. **Why it's wrong:** Users have their own design systems. Bundling makes acture unusable for them. @@ -107,6 +107,7 @@ Before merging: 3. If the PR added a field to `CommandRecord`: did three concrete callers ask for it? (See `acture-command-record-shape`.) 4. If the PR added a runtime check or middleware: is it the dispatcher's concern, or did it leak into an adapter? 5. If the PR added a "mode" branch anywhere: stop. Refactor into composition. +6. **Positioning check** (`docs/positioning.md`): could a developer accomplish what this PR enables *without* installing an `acture-*` package? If the honest answer is "no", the dev-tool-first principle is violated — the agent-written path must stay viable and documented. If the PR adds or touches a consumer surface, the `acture-consumer-integration` skill's checklist also applies. ## When the answer is unclear @@ -114,6 +115,8 @@ If you can't tell whether a PR violates a hard don't, the answer is usually yes ## See also -- `docs/redesign_takeaways.md` §3 — the source +- `docs/positioning.md` — canonical positioning; the source of merge-ritual item #6 +- `docs/redesign_takeaways.md` §3 — the source of the ten don'ts - `docs/command_dispatch_journal_article.md` §6 — the underlying risks (Inner Platform, premature generalization, performance, astronaut syndrome) +- `acture-consumer-integration` — the consumer-build pattern that operationalises the positioning - Each individual `acture-*` skill for domain-specific don'ts diff --git a/.claude/skills/acture-migration-package/SKILL.md b/.claude/skills/acture-migration-package/SKILL.md index 4bfa811..a8cb03b 100644 --- a/.claude/skills/acture-migration-package/SKILL.md +++ b/.claude/skills/acture-migration-package/SKILL.md @@ -1,6 +1,6 @@ --- name: acture-migration-package -description: Load context on `@acture/migration` (Phase 3) — its four-function API per research-4 §A.6 (wrapMutation, actureMiddleware, chooseImplementation, shadowCompare), why divertHandler was dropped, the codemod scope deferred to v1.1, and the migration-track skills (migration-diagnose, migration-plan, migration-scaffold, migration-wrap, migration-graduate). Use when working on the migration package, when writing or rewriting migration skills, when designing the strangler-fig adoption workflow, or when an existing-codebase agent is using acture for the first time. Triggers on "migration", "strangler fig", "wrapMutation", "divertHandler", "chooseImplementation", "shadowCompare", "actureMiddleware", "graduate", "codemods", "ast-grep", "jscodeshift", "ts-morph", "event interception", "legacy mimic". +description: Load context on `acture-migration` (Phase 3) — its four-function API per research-4 §A.6 (wrapMutation, actureMiddleware, chooseImplementation, shadowCompare), why divertHandler was dropped, the codemod scope deferred to v1.1, and the migration-track skills (migration-diagnose, migration-plan, migration-scaffold, migration-wrap, migration-graduate). Use when working on the migration package, when writing or rewriting migration skills, when designing the strangler-fig adoption workflow, or when an existing-codebase agent is using acture for the first time. Triggers on "migration", "strangler fig", "wrapMutation", "divertHandler", "chooseImplementation", "shadowCompare", "actureMiddleware", "graduate", "codemods", "ast-grep", "jscodeshift", "ts-morph", "event interception", "legacy mimic". --- # acture migration package @@ -10,7 +10,7 @@ Loads research-4's findings on transitional APIs and codemod tooling for strangl ## What ships in v1 — four functions (research-4 §A.6) ```ts -// From @acture/migration +// From acture-migration export function wrapMutation any>( handler: H, @@ -120,7 +120,7 @@ To be written in Phase 3 (NOT this preparation session) — they are rewrites of | --- | --- | | `migration-diagnose` | Scan codebase; identify command candidates (event handlers, store actions, async thunks); produce a diagnosis report. | | `migration-plan` | Prioritize candidates into a phased backlog. | -| `migration-scaffold` | Set up `acture/core` and `@acture/state-zustand` (or redux) in the host app. | +| `migration-scaffold` | Set up `acture/core` and `acture-state-zustand` (or redux) in the host app. | | `migration-wrap` | Use `wrapMutation` to introduce commands without modifying existing source. | | `migration-graduate` | Retire `wrapMutation` calls once the legacy handler is unused. | @@ -129,7 +129,7 @@ To be written in Phase 3 (NOT this preparation session) — they are rewrites of For a typical migration like "adopt acture for the checkout flow": 1. Claude Code reads target files; identifies handlers and store actions. -2. Plans the migration: install `@acture/migration`, scaffold registry, wrap handlers, add `actureMiddleware` to store. +2. Plans the migration: install `acture-migration`, scaffold registry, wrap handlers, add `actureMiddleware` to store. 3. For each wrap step: invokes `wrapMutation` in-place; runs tsc + tests; iterates if failure. 4. Final review: summary of which calls were wrapped vs. which require manual rewrite. diff --git a/.claude/skills/acture-palette-design/SKILL.md b/.claude/skills/acture-palette-design/SKILL.md index 558d9dc..f083841 100644 --- a/.claude/skills/acture-palette-design/SKILL.md +++ b/.claude/skills/acture-palette-design/SKILL.md @@ -1,12 +1,14 @@ --- name: acture-palette-design -description: Load context on acture's command palette design, including the parameterized-command UX (atomic vs. handoff), the auto-derived `kind` heuristic, the per-parameter-count defaults, and the don't-do list. Use when building or modifying `@acture/palette-react`, when implementing parameter collection, when reviewing palette UX choices, or when working on form adapters (`@acture/forms-autoform`, `@acture/forms-rjsf`). Triggers on "command palette", "parameter collection", "atomic vs handoff", "param collector", "cmdk", "kbar", "form adapter", "picker chain", "Ctrl+K". Do NOT use for keybinding-only work (focus on `acture-command-record-shape` for the `keybinding` field). +description: Load context on acture's command palette design, including the parameterized-command UX (atomic vs. handoff), the auto-derived `kind` heuristic, the per-parameter-count defaults, and the don't-do list. Use when building or modifying `acture-palette-react`, when implementing parameter collection, when reviewing palette UX choices, or when working on form adapters (`acture-forms-autoform`, `acture-forms-rjsf`). Triggers on "command palette", "parameter collection", "atomic vs handoff", "param collector", "cmdk", "kbar", "form adapter", "picker chain", "Ctrl+K". Do NOT use for keybinding-only work (focus on `acture-command-record-shape` for the `keybinding` field). --- # acture palette design Loads research-2's findings on parameterized command palettes and acture's resulting `kind: "atomic" | "handoff"` design. +> **Load `acture-consumer-integration` first.** The command palette is a *consumer* — this skill covers palette-specific UX, but the foundational pattern (agent-written vs `acture-palette-react`, the cmdk/kbar/custom tool choice belongs to the user, the dev-tool-first rule) lives there. + ## The empirical cliff (research-2 §7) Across 18 shipped products, parameterized command palettes have a sharp UX cliff: @@ -71,7 +73,7 @@ function deriveKind(record: CommandRecord): "atomic" | "handoff" { ## Form adapters -`@acture/forms-autoform` and `@acture/forms-rjsf` shipped as separate packages in Phase 2. Both implement the same `PaletteFormAdapterProps` interface (`{ command, defaults?, onSubmit, onCancel }`) consumed by ``. The palette switches to the supplied form for `kind: 'handoff'` commands. +`acture-forms-autoform` and `acture-forms-rjsf` shipped as separate packages in Phase 2. Both implement the same `PaletteFormAdapterProps` interface (`{ command, defaults?, onSubmit, onCancel }`) consumed by ``. The palette switches to the supplied form for `kind: 'handoff'` commands. - **autoform** — Zod-native; lighter; fits the recommended Zod-first authoring path. - **rjsf** — JSON-Schema-native; battle-tested; larger bundle but more themes. diff --git a/.claude/skills/acture-state-adapter/SKILL.md b/.claude/skills/acture-state-adapter/SKILL.md index e39ba7b..39f9339 100644 --- a/.claude/skills/acture-state-adapter/SKILL.md +++ b/.claude/skills/acture-state-adapter/SKILL.md @@ -1,6 +1,6 @@ --- name: acture-state-adapter -description: Load context on acture's StateAdapter interface (per research-3), the PatchCapableAdapter sub-interface for the future undo subsystem, and the strategy of being state-library-agnostic with documented happy-path adapters. Two reference adapters now ship: `@acture/state-zustand` and `@acture/state-redux`. Use when building or modifying a state adapter, when designing the StateAdapter interface, when working on the reference adapters, when integrating acture with a host app's existing store, or when reviewing the get/set/subscribe contract. Triggers on "StateAdapter", "state library", "zustand", "Redux Toolkit", "RTK", "Jotai", "Valtio", "MobX", "Effector", "XState", "state substrate", "patches", "Immer", "produceWithPatches". Do NOT use for the undo subsystem itself (post-v1). +description: Load context on acture's StateAdapter interface (per research-3), the PatchCapableAdapter sub-interface for the future undo subsystem, and the strategy of being state-library-agnostic with documented happy-path adapters. Two reference adapters now ship: `acture-state-zustand` and `acture-state-redux`. Use when building or modifying a state adapter, when designing the StateAdapter interface, when working on the reference adapters, when integrating acture with a host app's existing store, or when reviewing the get/set/subscribe contract. Triggers on "StateAdapter", "state library", "zustand", "Redux Toolkit", "RTK", "Jotai", "Valtio", "MobX", "Effector", "XState", "state substrate", "patches", "Immer", "produceWithPatches". Do NOT use for the undo subsystem itself (post-v1). --- # acture state adapter @@ -64,7 +64,7 @@ Both are valid. Implementations choose whichever fits their substrate. zustand passes `(state, previous)`. RTK passes nothing. Recommendation: keep `previous` in the interface; default to `undefined` for adapters that don't track it. Document the quirk in the adapter's README. -## Reference adapter: `@acture/state-zustand` +## Reference adapter: `acture-state-zustand` The documented happy path. Per research-3 §6, ~50 LOC including tests. @@ -95,7 +95,7 @@ export function createZustandAdapter(initial: S): PatchCapableAdapter { `zustand/vanilla` is essential — it gives first-class support for non-React surfaces (MCP, CLI, keyboard daemons). -## Phase 2 adapter: `@acture/state-redux` +## Phase 2 adapter: `acture-state-redux` Per research-3 §6 second choice. RTK already runs Immer internally; `createSlice` is the exemplar of typed slices. Adapter pattern: wrap the store; expose typed slice accessors. diff --git a/.claude/skills/acture-tier-system/SKILL.md b/.claude/skills/acture-tier-system/SKILL.md index b9aa808..b223bdb 100644 --- a/.claude/skills/acture-tier-system/SKILL.md +++ b/.claude/skills/acture-tier-system/SKILL.md @@ -87,7 +87,7 @@ acture compare-schemas --allow-description-edits # per-invocation, NOT a config acture compare-schemas --snapshot-path .acture/snapshot.json # when args are git refs ``` -Lives in `packages/cli/` (`@acture/cli`). Reads two pre-rendered snapshots and diffs them. The snapshot is produced by `snapshotRegistry(registry)` (programmatic) or by `acture snapshot ` (CLI subcommand, v1.1+): +Lives in `packages/cli/` (`acture-cli`). Reads two pre-rendered snapshots and diffs them. The snapshot is produced by `snapshotRegistry(registry)` (programmatic) or by `acture snapshot ` (CLI subcommand, v1.1+): ```bash acture snapshot ./registry.mjs --out .acture/snapshot.json # write to file diff --git a/.claude/skills/migration-graduate/SKILL.md b/.claude/skills/migration-graduate/SKILL.md index e29be4c..75cb173 100644 --- a/.claude/skills/migration-graduate/SKILL.md +++ b/.claude/skills/migration-graduate/SKILL.md @@ -67,7 +67,7 @@ If you find external callers, **stop**. The wrapper is still earning its keep. N Before (wrapped): ```ts -import { wrapMutation } from '@acture/migration'; +import { wrapMutation } from 'acture-migration'; import { registry } from '../../registry'; import { useTodoStore } from '../../../store'; @@ -170,11 +170,11 @@ In those cases, document the decision in a comment on the wrapper file: ## Validation -- [ ] Every graduated command file imports `defineCommand` from `acture`, not `wrapMutation` from `@acture/migration`. +- [ ] Every graduated command file imports `defineCommand` from `acture`, not `wrapMutation` from `acture-migration`. - [ ] The legacy function has no remaining callers (verified by grep). - [ ] `pnpm test` and `pnpm typecheck` pass. - [ ] Any tests that previously called the legacy function now dispatch via the registry. ## Hand-off -When Phase A graduations are complete, the `@acture/migration` import in the acture barrel is much thinner. Wrapped command counts drop; first-class `defineCommand` counts rise. Run `migration-diagnose` again periodically to find new candidates as the app grows. +When Phase A graduations are complete, the `acture-migration` import in the acture barrel is much thinner. Wrapped command counts drop; first-class `defineCommand` counts rise. Run `migration-diagnose` again periodically to find new candidates as the app grows. diff --git a/.claude/skills/migration-plan/SKILL.md b/.claude/skills/migration-plan/SKILL.md index fc4eb21..7ed151d 100644 --- a/.claude/skills/migration-plan/SKILL.md +++ b/.claude/skills/migration-plan/SKILL.md @@ -37,7 +37,7 @@ Decision points: - **Id prefix.** Options: `app`, the project name, custom. Default: `app`. - **Categories.** Show the categories that emerged from the diagnosis; offer merge / rename. Categories surface in the palette grouping. -- **State adapter.** If zustand → `@acture/state-zustand`. If RTK → `@acture/state-redux`. If neither, ask. +- **State adapter.** If zustand → `acture-state-zustand`. If RTK → `acture-state-redux`. If neither, ask. - **Phase A scope.** Conservative (5–8) / moderate (8–12) / aggressive (12–20). Default: moderate. - **Surface ambitions.** Which of palette / hotkeys / MCP / AI will be wired in this migration? Determines which `migration-wire-*` follow-ons matter. diff --git a/.claude/skills/migration-scaffold/SKILL.md b/.claude/skills/migration-scaffold/SKILL.md index 92b48c2..00bde3b 100644 --- a/.claude/skills/migration-scaffold/SKILL.md +++ b/.claude/skills/migration-scaffold/SKILL.md @@ -32,15 +32,15 @@ Nothing in `src/` outside this directory is modified. ### 1. Install packages ```bash -pnpm add acture @acture/migration zod +pnpm add acture acture-migration zod # State adapter (pick one based on the plan): -pnpm add @acture/state-zustand # if the host uses zustand -pnpm add @acture/state-redux # if the host uses RTK +pnpm add acture-state-zustand # if the host uses zustand +pnpm add acture-state-redux # if the host uses RTK # Wire-up packages — install only what the plan calls for: -pnpm add @acture/palette-react cmdk # if palette is in scope -pnpm add @acture/hotkeys tinykeys # if hotkeys are in scope -pnpm add @acture/mcp # if MCP is in scope -pnpm add @acture/ai-vercel ai # if AI tool calling is in scope +pnpm add acture-palette-react cmdk # if palette is in scope +pnpm add acture-hotkeys tinykeys # if hotkeys are in scope +pnpm add acture-mcp # if MCP is in scope +pnpm add acture-ai-vercel ai # if AI tool calling is in scope ``` Use `npm` / `yarn` instead of `pnpm` if the host uses them. @@ -60,7 +60,7 @@ That's it. No middleware to register — acture's dispatcher already validates p For zustand: ```ts -import { wrapZustandStore } from '@acture/state-zustand'; +import { wrapZustandStore } from 'acture-state-zustand'; import { useTodoStore } from '../store'; // existing host store export const state = wrapZustandStore(useTodoStore); @@ -69,7 +69,7 @@ export const state = wrapZustandStore(useTodoStore); For RTK: ```ts -import { wrapReduxStore } from '@acture/state-redux'; +import { wrapReduxStore } from 'acture-state-redux'; import { store } from '../store'; export const state = wrapReduxStore(store, { @@ -101,7 +101,7 @@ export { state } from './state'; If the plan says to observe store events (e.g. for telemetry or to render dispatches in devtools), update the host's `configureStore` call: ```ts -import { actureMiddleware } from '@acture/migration'; +import { actureMiddleware } from 'acture-migration'; import { registry } from './acture/registry'; export const store = configureStore({ diff --git a/.claude/skills/migration-wrap/SKILL.md b/.claude/skills/migration-wrap/SKILL.md index f4ccc67..5ed3200 100644 --- a/.claude/skills/migration-wrap/SKILL.md +++ b/.claude/skills/migration-wrap/SKILL.md @@ -1,6 +1,6 @@ --- name: migration-wrap -description: Wrap one or more existing handlers / store actions as acture commands using @acture/migration's `wrapMutation`. Use after `migration-scaffold`. Triggers on "wrap this handler", "register as a command", "wrap onClick", "convert this action to a command", "use wrapMutation". The fourth skill in the migration track. Wraps incrementally without touching the legacy call site. +description: Wrap one or more existing handlers / store actions as acture commands using acture-migration's `wrapMutation`. Use after `migration-scaffold`. Triggers on "wrap this handler", "register as a command", "wrap onClick", "convert this action to a command", "use wrapMutation". The fourth skill in the migration track. Wraps incrementally without touching the legacy call site. --- # migration-wrap @@ -41,7 +41,7 @@ Path: `src/acture/commands/{category}/{actionName}.ts`. Lowercase the category, For a parameter-less store action: ```ts -import { wrapMutation } from '@acture/migration'; +import { wrapMutation } from 'acture-migration'; import { registry } from '../../registry'; import { useTodoStore } from '../../../store'; @@ -60,7 +60,7 @@ For a store action with params: ```ts import { z } from 'zod'; -import { wrapMutation } from '@acture/migration'; +import { wrapMutation } from 'acture-migration'; import { registry } from '../../registry'; import { useTodoStore } from '../../../store'; @@ -84,7 +84,7 @@ For a legacy event handler that wraps a mutation: // legacy: const onSignOut = () => { authClient.signOut(); router.push('/login'); } // We wrap the mutation, not the event handler. UI navigation stays in the host. -import { wrapMutation } from '@acture/migration'; +import { wrapMutation } from 'acture-migration'; import { registry } from '../../registry'; import { authClient } from '../../../auth'; @@ -163,6 +163,6 @@ Stop when Phase A is done. Don't drift into Phase B without a plan update — th After Phase A is wrapped: -- Wire the palette (`@acture/palette-react`) — agent skill list will have a `wire-palette` skill (Phase 2-era). -- Wire hotkeys (`@acture/hotkeys`) — same. +- Wire the palette (`acture-palette-react`) — agent skill list will have a `wire-palette` skill (Phase 2-era). +- Wire hotkeys (`acture-hotkeys`) — same. - Run `migration-graduate` to retire wrappers where the legacy handler is no longer called. diff --git a/AGENTS.md b/AGENTS.md index b73f603..8b687e1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,37 +4,44 @@ You are an AI coding agent contributing to **acture**, a typed schema-driven com ## What acture is -A library implementing the **command dispatch architecture** described in `docs/command_dispatch_journal_article.md`. The library exposes three primitives — *state model*, *command registry*, *schema bridge* — that together power eight consumer surfaces: command palette, keyboard shortcuts, AI tool calling, MCP server, automated tests, telemetry (post-v1), undo/redo (post-v1), and macros (post-v1). A command defined once, registered once, serves every surface. +acture is **a place developers (human or AI agent) go to get AI-agentic help building, migrating to, and maintaining a command-dispatch architecture** — the architecture described in `docs/command_dispatch_journal_article.md`, built on three primitives (*state model*, *command registry*, *schema bridge*) that power eight consumer surfaces (command palette, keyboard shortcuts, AI tool calling, MCP server, end-to-end testing, telemetry, undo/redo, macros). -Acture is the successor to `wrapex` (npm `command-wrapex`). That name carried migration-only framing that didn't fit acture's scope. Both `acture` on npm and `acture` on PyPI are reserved. +It is delivered primarily as **skills, patterns, and codemods**; the `acture-*` npm packages are an **optional accelerator**, not the product. **`docs/positioning.md` is canonical — read it before writing any user-facing text or designing any package or skill.** -## Three positioning paths +Acture is the successor to `wrapex` (npm `command-wrapex`). That name carried migration-only framing that didn't fit acture's scope. `acture` is reserved on npm and PyPI; the 13 sub-packages publish unscoped as `acture-*` (the `@acture` npm scope was unavailable). -Same core serves all three. Adapter packages and documentation differ. +## Positioning — the two flexibility dimensions -1. **Greenfield-pure** — design an app command-dispatch-first from day one. -2. **Strangler-fig migration** — incrementally adopt command dispatch in an existing codebase using `acture/migration`. -3. **Footprint-minimizer drop-in** — bolt a command palette + MCP server onto an existing app, no migration intent. +**Dev-tool-first principle:** a developer must be able to use acture purely as a development tool, with **no `acture-*` dependency added to their project** — unless they explicitly choose to. See `docs/positioning.md` for the full statement. + +Every engagement sits on two independent dimensions; keep both open, never default one: + +1. **Core vs strangler-fig** — command dispatch designed in, or wrapped into an existing codebase incrementally. (The conceptual paper's three "paths" — greenfield-pure, footprint-minimizer, strangler-fig — collapse onto this axis.) +2. **Agent-written vs package-reuse** — the agent writes the integration into the project (zero acture dependency, max adaptability), or installs an `acture-*` package (less code to own, tested, at the cost of a dependency). Decided **per consumer**. + +**Standing rule:** any task touching a consumer surface or a consumer-specific `acture-*` package must also load the `acture-consumer-integration` skill. ## Where to look (in this order) 1. **`docs/command_dispatch_journal_article.md`** — the central conceptual paper. Three primitives, eight consumer surfaces, strangler-fig migration, the rule of three, the risks (inner platform effect, premature generalization, performance, architecture astronaut syndrome). READ FIRST. -2. **`docs/v1_plan.md`** — the research-informed plan. What's settled, what's deferred, what each phase delivers. +2. **`docs/positioning.md`** — **canonical.** What acture is (dev-tool-first), the two flexibility dimensions, what the packages are for. Governs every user-facing word. READ SECOND. -3. **`docs/implementation_plan.md`** — phase-by-phase implementation guide with explicit pre-next-phase reflection checklists. **Phase boundaries are gates, not waypoints.** +3. **`docs/roadmap.md`** — the forward plan and status tracker: what's done, what's next, what's deferred. The live planning surface. -4. **`docs/research/`** — five research findings (1–5) that informed the v1 plan. Read the one(s) relevant to your current task; do not read all five every session. +4. **`docs/next_session.md`** — the immediate handoff prompt for the current piece of work. 5. **`docs/redesign_takeaways.md`** — opinionated synthesis of design commitments. The "Hard don'ts" (§3) are the merge checklist. -6. **`docs/parameterized_command_palette_guide.md`** — implementation patterns for parameter collection. (See `docs/research/acture_research_2 ...` for the UX research that overrides any conflict here.) +6. **`docs/research/`** — five research findings (1–5) that informed the v1 plan. Read the one(s) relevant to your current task; do not read all five every session. -7. **`docs/reference_notes.md`** — distilled per-article notes for all 51 references in the central paper. +7. **`docs/v1_plan.md`** / **`docs/implementation_plan.md`** — the research-informed v1 plan and phase-by-phase guide. **Historical** — phases 0–4 are complete; forward work is tracked in `docs/roadmap.md`. -8. **`docs/wrapex_carryover.md`** — file-by-file audit of what we kept, discarded, and what to read from wrapex when needed. +8. **`docs/parameterized_command_palette_guide.md`** — implementation patterns for parameter collection. (See `docs/research/acture_research_2 ...` for the UX research that overrides any conflict here.) -9. **`.claude/skills/`** — task-specific skills you load when working on a particular concern. +9. **`docs/reference_notes.md`** / **`docs/wrapex_carryover.md`** — distilled reference notes; the wrapex carryover audit. + +10. **`.claude/skills/`** — task-specific skills you load when working on a particular concern. ## Skills index @@ -42,12 +49,13 @@ Skills are how you load focused context. Each is a self-contained primer for one | Skill | When to load | | --- | --- | -| `acture-architecture-primer` | Always, for any non-trivial task. The conceptual model in 5 minutes. | +| `acture-architecture-primer` | Always, for any non-trivial task. The conceptual model + positioning in 5 minutes. | +| `acture-consumer-integration` | **Whenever a task touches a consumer surface or a consumer-specific `acture-*` package.** The dev-tool-first build pattern: agent-written vs package-reuse, tool-library choices belong to the user. | | `acture-command-record-shape` | When defining or modifying the `CommandRecord` interface or its fields. | | `acture-schema-bridge` | When working on Zod → JSON Schema projection, MCP tool emission, or AI tool definitions. | | `acture-palette-design` | When building or modifying the command palette UI, especially parameterized commands. | | `acture-state-adapter` | When building or modifying a state adapter (zustand, redux, etc.) or the `StateAdapter` interface. | -| `acture-migration-package` | When working on `acture/migration` (wrapMutation, actureMiddleware, chooseImplementation, shadowCompare) or the migration-track skills. | +| `acture-migration-package` | When working on `acture-migration` (wrapMutation, actureMiddleware, chooseImplementation, shadowCompare) or the migration-track skills. | | `acture-tier-system` | When working on the @stable / @experimental / @internal / @deprecated tier system or `acture compare-schemas`. | | `acture-hard-donts` | Read before every non-trivial PR. The merge checklist of anti-patterns. | | `migration-diagnose` | First step in adopting acture in an existing codebase: scan source for command candidates. | @@ -56,6 +64,8 @@ Skills are how you load focused context. Each is a self-contained primer for one | `migration-wrap` | Fourth step: wrap existing handlers / store actions using `wrapMutation`. | | `migration-graduate` | Final step: retire `wrapMutation` calls once the legacy handler is no longer needed. | +The `acture-*` skills are **dev skills** (working *on* acture). The `migration-*` skills are the **strangler-fig workflow**. `acture-consumer-integration` is the foundation of the (growing) **consumer-integration** family — per-surface skills for building a consumer *in a target project*. Per `docs/positioning.md` §6, dev skills must load `acture-consumer-integration` whenever the work touches a consumer. + ## The hard don'ts (merge checklist) Full discussion is in `docs/redesign_takeaways.md` §3 and the `acture-hard-donts` skill. Headlines: @@ -77,26 +87,28 @@ Full discussion is in `docs/redesign_takeaways.md` §3 and the `acture-hard-dont - Generalizing beyond what `v1_plan.md` commits to. Rule of three. - Modifying the central paper (`docs/command_dispatch_journal_article.md`). It is canonical. -## Current state (v1.4, Phase 4 + v1.1 → v1.4 increments DONE, 2026-05-14) +## Current state (v1.5 — repositioning + namespace migration, 2026-05-14) + +**v1.5 (this increment):** the canonical positioning was clarified (`docs/positioning.md` — dev-tool-first, the two flexibility dimensions), the `acture-consumer-integration` skill was added and wired into the dev skills, and all 13 sub-packages were renamed from `@acture/*` to unscoped `acture-*` (the `@acture` npm scope was unavailable). `acture@1.1.0` and `eslint-plugin-acture-migration@1.0.0` are published on npm; the 13 renamed packages publish next under their new names. Forward plan and full done/not-done tracking live in `docs/roadmap.md`. Fifteen packages ship in the workspace at versions ranging from v1.0.0 to v1.2.0: - Core: `acture@1.1.0` — `enableTierWarnings`, `deprecationReason`, `internalToken`, `DispatchOptions`. -- State: `@acture/state-zustand@1.0.0`, `@acture/state-redux@1.0.0`. -- UI: `@acture/palette-react@1.0.0`, `@acture/hotkeys@1.0.0`, `@acture/forms-autoform@1.0.0`, `@acture/forms-rjsf@1.0.0`. -- Surfaces: `@acture/mcp@1.0.0`, `@acture/ai-vercel@1.0.0` — honour the tier filter and prepend `[DEPRECATED — ]`. -- Migration: `@acture/migration@1.1.0` — `createDomInterceptor` for DOM-event interception. -- Tooling: `@acture/build-tier@1.1.0` (regex + AST mode), `@acture/cli@1.2.0` (deep nested compare-schemas diffs), `@acture/devtools@1.0.0`. -- Codemods: `@acture/codemods@1.1.0` — **research-4 §B.5 codemod set is complete** (5 codemods: `wrap-handler-with-mutation`, `extract-onclick-to-command`, `redux-action-to-command`, `usestate-mutation-to-command`, `rtk-thunk-to-command`). +- State: `acture-state-zustand@1.0.0`, `acture-state-redux@1.0.0`. +- UI: `acture-palette-react@1.0.0`, `acture-hotkeys@1.0.0`, `acture-forms-autoform@1.0.0`, `acture-forms-rjsf@1.0.0`. +- Surfaces: `acture-mcp@1.0.0`, `acture-ai-vercel@1.0.0` — honour the tier filter and prepend `[DEPRECATED — ]`. +- Migration: `acture-migration@1.1.0` — `createDomInterceptor` for DOM-event interception. +- Tooling: `acture-build-tier@1.1.0` (regex + AST mode), `acture-cli@1.2.0` (deep nested compare-schemas diffs), `acture-devtools@1.0.0`. +- Codemods: `acture-codemods@1.1.0` — **research-4 §B.5 codemod set is complete** (5 codemods: `wrap-handler-with-mutation`, `extract-onclick-to-command`, `redux-action-to-command`, `usestate-mutation-to-command`, `rtk-thunk-to-command`). - Lint: `eslint-plugin-acture-migration@1.0.0` — **new in v1.4.** One rule, `acture/no-stale-wrap-mutation`: flags `wrapMutation(...)` calls whose result is never used (the migration has graduated; author with `defineCommand`). Four worked examples: `examples/greenfield/graph-editor/`, `examples/drop-in/`, `examples/migration/zustand-wrap/{before,after}/`, `examples/migration/redux-wrap/` (RTK + `actureMiddleware` end-to-end). -v1.4 also ran the deferred fresh-agent release-gate test against `@acture/codemods` — see `docs/fresh-agent-test-results.md`. The codemod engine passed; README-accuracy fixes are carried to v1.5. See `docs/next_session.md` for the v1.5 backlog. +v1.4 ran the deferred fresh-agent release-gate test against `acture-codemods` — see `docs/fresh-agent-test-results.md`. -## Phase progression +## Working rhythm -You will be told which phase you're working in. Each phase ends with a reflection note at `docs/phase-N-reflection.md`. Do NOT skip the reflection step — it is the gate to the next phase. +Phases 0–4 of the original v1 plan are **complete**. Work now proceeds as small, tracked increments rather than gated phases. The forward plan, the immediate next step, and the full done/not-done tracking live in **`docs/roadmap.md`** and **`docs/next_session.md`**. Each increment still ends with a short reflection or roadmap update — do not skip it. ## When unsure @@ -105,7 +117,7 @@ If a design choice is irreversible (per `docs/implementation_plan.md` §"Sequenc ## Conventions - TypeScript monorepo via pnpm workspaces (or npm; Phase 0 picks). -- Package naming: `acture` (default barrel), `@acture/` (e.g. `@acture/state-zustand`, `@acture/mcp`). +- Package naming: `acture` (default barrel), `acture-` (e.g. `acture-state-zustand`, `acture-mcp`). - Test runner: `vitest`. - Build: `tsup` or `tshy` for ESM+CJS+types. - Code style: standard prettier defaults; no `any` in public API; `unknown` for untyped boundaries. diff --git a/README.md b/README.md index 110d747..9996414 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,77 @@ # acture -> One schema. Palette, hotkeys, AI tools, MCP, and tests — for free. +> Get AI-agentic help building a command-dispatch architecture — one schema, and the palette, hotkeys, AI tools, MCP, and tests fall out of it. -Acture is a typed, schema-driven command dispatch library for TypeScript/React applications. Define an operation once; it becomes available as a command palette entry, a keyboard shortcut, an AI tool call, an MCP server tool, a test action, and (post-v1) a macro step. +acture is **a development tool** for building, migrating to, and maintaining a command-dispatch architecture in TypeScript/React apps. Define an operation once as a command; it becomes a command palette entry, a keyboard shortcut, an AI tool call, an MCP server tool, a test action, and (post-v1) a macro step. -## Install +It is delivered primarily as **skills, patterns, and codemods** an AI agent uses to write command dispatch *into your project* — adapted to your stack and your preferences. The `acture-*` npm packages are an **optional accelerator**: ready-made, tested implementations the agent can reach for instead of hand-writing similar code. + +**You can use acture without adding a single `acture-*` dependency to your project.** Whether you take on a dependency is always your explicit, per-piece choice — see [`docs/positioning.md`](docs/positioning.md), the canonical statement of what acture is and what it asks of you. + +## Install (optional — the accelerator packages) + +You only install what you decide to reuse. Every package is independently optional; the agent-written path needs none of them. ```bash -pnpm add acture # core -pnpm add @acture/state-zustand # state adapter (or @acture/state-redux) -pnpm add @acture/palette-react # command palette UI -pnpm add @acture/hotkeys # keyboard shortcuts -pnpm add @acture/mcp # MCP server projection -pnpm add @acture/ai-vercel # Vercel AI tool definitions -pnpm add @acture/migration # strangler-fig adoption primitives -# …plus @acture/forms-autoform and @acture/forms-rjsf for parameterized commands. - -# Dev / CI tooling (post-v1.0): -pnpm add -D @acture/build-tier # build-step @stable/@experimental/@internal/@deprecated mirror -pnpm add -D @acture/cli # `acture compare-schemas` / `acture snapshot` CLI -pnpm add -D @acture/devtools # embeddable for dev builds +pnpm add acture # core registry + dispatcher + schema bridge (optional even this) +pnpm add acture-state-zustand # state adapter (or acture-state-redux) +pnpm add acture-palette-react # command palette UI (on cmdk) +pnpm add acture-hotkeys # keyboard shortcuts (on tinykeys) +pnpm add acture-mcp # MCP server projection +pnpm add acture-ai-vercel # AI tool definitions (on the Vercel AI SDK) +pnpm add acture-migration # strangler-fig adoption primitives +# …plus acture-forms-autoform and acture-forms-rjsf for parameterized commands. + +# Dev / CI tooling: +pnpm add -D acture-build-tier # build-step @stable/@experimental/@internal/@deprecated mirror +pnpm add -D acture-cli # `acture compare-schemas` / `acture snapshot` CLI +pnpm add -D acture-devtools # embeddable for dev builds pnpm add -D eslint-plugin-acture-migration # ESLint rule that flags stale wrapMutation wrappers ``` -> The `acture` name is also reserved on PyPI as a placeholder; a real Python companion is post-v1. `pip install acture` gives you a no-op package whose only purpose is to keep the name ours. +> The 13 sub-packages publish unscoped as `acture-*` — the `@acture` npm scope was unavailable. `acture` is reserved on npm and PyPI; a real Python companion is post-v1. ## Status -**v1.4.0 (Phase 4 DONE + v1.1 → v1.4 increments, 2026-05-14).** Fifteen packages ship in the workspace: +**v1.5 — repositioning + namespace migration (2026-05-14).** Fifteen packages ship in the workspace. v1.5 clarified the canonical positioning ([`docs/positioning.md`](docs/positioning.md)), added the `acture-consumer-integration` skill, and renamed all 13 sub-packages from `@acture/*` to unscoped `acture-*`. `acture@1.1.0` and `eslint-plugin-acture-migration@1.0.0` are live on npm; the 13 renamed packages publish next under their new names. | Package | Role | | --- | --- | | [`acture`](packages/core) | core registry, dispatcher, when-clause DSL, schema bridge, `enableTierWarnings` | -| [`@acture/state-zustand`](packages/state-zustand) | StateAdapter for zustand+immer | -| [`@acture/state-redux`](packages/state-redux) | StateAdapter for Redux Toolkit | -| [`@acture/palette-react`](packages/palette-react) | command palette with parameterized-command UX | -| [`@acture/hotkeys`](packages/hotkeys) | tinykeys-backed keyboard bindings | -| [`@acture/forms-autoform`](packages/forms-autoform) | Zod-native form adapter | -| [`@acture/forms-rjsf`](packages/forms-rjsf) | JSON-Schema form adapter (rjsf) | -| [`@acture/mcp`](packages/mcp) | MCP server projection | -| [`@acture/ai-vercel`](packages/ai-vercel) | Vercel AI SDK tool definitions | -| [`@acture/migration`](packages/migration) | strangler-fig primitives: `wrapMutation`, `actureMiddleware`, `createDomInterceptor`, `chooseImplementation`, `shadowCompare` | -| [`@acture/build-tier`](packages/build-tier) | build-step plugin that mirrors `@stable`/`@experimental`/`@internal`/`@deprecated` JSDoc into runtime `tier`; regex default + AST mode polish | -| [`@acture/cli`](packages/cli) | `acture compare-schemas` (CI gating, deep nested diffs) + `acture snapshot` (registry → JSON) | -| [`@acture/devtools`](packages/devtools) | embeddable `` and `instrumentRegistry` dispatch log | -| [`@acture/codemods`](packages/codemods) | Codemod CLI: all five research-4 §B.5 codemods now shipped (`wrap-handler-with-mutation`, `extract-onclick-to-command`, `redux-action-to-command`, `usestate-mutation-to-command`, `rtk-thunk-to-command`). `--dry-run` + `--json` for agents | +| [`acture-state-zustand`](packages/state-zustand) | StateAdapter for zustand+immer | +| [`acture-state-redux`](packages/state-redux) | StateAdapter for Redux Toolkit | +| [`acture-palette-react`](packages/palette-react) | command palette with parameterized-command UX | +| [`acture-hotkeys`](packages/hotkeys) | tinykeys-backed keyboard bindings | +| [`acture-forms-autoform`](packages/forms-autoform) | Zod-native form adapter | +| [`acture-forms-rjsf`](packages/forms-rjsf) | JSON-Schema form adapter (rjsf) | +| [`acture-mcp`](packages/mcp) | MCP server projection | +| [`acture-ai-vercel`](packages/ai-vercel) | Vercel AI SDK tool definitions | +| [`acture-migration`](packages/migration) | strangler-fig primitives: `wrapMutation`, `actureMiddleware`, `createDomInterceptor`, `chooseImplementation`, `shadowCompare` | +| [`acture-build-tier`](packages/build-tier) | build-step plugin that mirrors `@stable`/`@experimental`/`@internal`/`@deprecated` JSDoc into runtime `tier`; regex default + AST mode polish | +| [`acture-cli`](packages/cli) | `acture compare-schemas` (CI gating, deep nested diffs) + `acture snapshot` (registry → JSON) | +| [`acture-devtools`](packages/devtools) | embeddable `` and `instrumentRegistry` dispatch log | +| [`acture-codemods`](packages/codemods) | Codemod CLI: all five research-4 §B.5 codemods now shipped (`wrap-handler-with-mutation`, `extract-onclick-to-command`, `redux-action-to-command`, `usestate-mutation-to-command`, `rtk-thunk-to-command`). `--dry-run` + `--json` for agents | | [`eslint-plugin-acture-migration`](packages/eslint-plugin-acture-migration) | ESLint rule `acture/no-stale-wrap-mutation` — flags `wrapMutation(...)` wrappers whose result is never used (the migration has graduated; author with `defineCommand`) | Worked examples: -- [`examples/greenfield/graph-editor/`](examples/greenfield/graph-editor) — greenfield path. Now wires `@acture/devtools`. +- [`examples/greenfield/graph-editor/`](examples/greenfield/graph-editor) — greenfield path. Now wires `acture-devtools`. - [`examples/drop-in/`](examples/drop-in) — 5-minute bolt-on path. - [`examples/migration/zustand-wrap/`](examples/migration/zustand-wrap) — strangler-fig path with side-by-side [`before/`](examples/migration/zustand-wrap/before) and [`after/`](examples/migration/zustand-wrap/after) apps. 6 wrapped commands + 2 graduated. - [`examples/migration/redux-wrap/`](examples/migration/redux-wrap) — **new in v1.2.** Redux Toolkit cart with `actureMiddleware` end-to-end. UI dispatch and palette dispatch converge on the same store, observed as one stream. -Agent skills live under [`.claude/skills/`](.claude/skills/): five migration-track skills (`migration-diagnose`, `migration-plan`, `migration-scaffold`, `migration-wrap`, `migration-graduate`) plus the architecture / tier / schema / hard-don'ts primer skills. +Agent skills live under [`.claude/skills/`](.claude/skills/): five migration-track skills (`migration-diagnose` → `migration-plan` → `migration-scaffold` → `migration-wrap` → `migration-graduate`), the `acture-consumer-integration` foundation for building consumers in a target project, plus the architecture / tier / schema / hard-don'ts dev-skill primers. + +What's new in v1.5 (repositioning + namespace migration): + +- **Canonical positioning.** [`docs/positioning.md`](docs/positioning.md) states what acture is — a dev-tool-first way to get AI-agentic help with command dispatch — and the two flexibility dimensions (core vs strangler-fig; agent-written vs package-reuse). It governs every user-facing word. +- **`acture-consumer-integration` skill.** The foundational pattern for building a consumer (palette, hotkeys, MCP, AI, e2e, …) in a target project: the agent-written path is always viable, packages are an opt-in accelerator, and tool-library choices belong to the user. Dev skills now load it whenever a task touches a consumer. +- **Namespace migration.** All 13 `@acture/*` packages renamed to unscoped `acture-*`. What's new in v1.4 (release-readiness theme): - **`eslint-plugin-acture-migration`.** One rule, `acture/no-stale-wrap-mutation`: flags `wrapMutation(...)` calls whose result is never used — the strangler-fig wrapper has graduated and should become a `defineCommand`. Single-file, conservative detection. +16 tests. Closes a research-4 backlog item carried since v1.1. -- **Fresh-agent release-gate test.** A fresh agent drove `@acture/codemods` from its README alone. The codemod engine + CLI passed; the README's pre-publish `npx` invocation and undocumented `--option` keys did not. Full assessment in [`docs/fresh-agent-test-results.md`](docs/fresh-agent-test-results.md); fixes carried to v1.5. +- **Fresh-agent release-gate test.** A fresh agent drove `acture-codemods` from its README alone. The codemod engine + CLI passed; the README's pre-publish `npx` invocation and undocumented `--option` keys did not. Full assessment in [`docs/fresh-agent-test-results.md`](docs/fresh-agent-test-results.md); fixes carried to v1.5. What's new in v1.3: @@ -67,10 +79,10 @@ What's new in v1.3: What's new in v1.2: -- **`@acture/codemods` package.** `npx @acture/codemods ` with two shipped transforms (`wrap-handler-with-mutation`, `extract-onclick-to-command`), a manifest of planned ones, and `--dry-run` + `--json` so agents can preview before applying. +- **`acture-codemods` package.** `npx acture-codemods ` with two shipped transforms (`wrap-handler-with-mutation`, `extract-onclick-to-command`), a manifest of planned ones, and `--dry-run` + `--json` so agents can preview before applying. - **`createDomInterceptor`.** Companion to `actureMiddleware` — a delegated DOM listener routes `data-acture-command` events through the registry. Plain TS, works in any framework, opt-in scoping per root. - **RTK worked example.** `examples/migration/redux-wrap/` closes the documentation gap for `actureMiddleware`. UI dispatch + palette dispatch converge on the same store, observed as one event stream. -- **AST mode for `@acture/build-tier`.** Second entry point at `@acture/build-tier/ast` uses ts-morph for projects where the regex's 4000-char lookahead is insufficient. ts-morph is an optional peer dep. +- **AST mode for `acture-build-tier`.** Second entry point at `acture-build-tier/ast` uses ts-morph for projects where the regex's 4000-char lookahead is insufficient. ts-morph is an optional peer dep. - **Deep nested diffs in `compare-schemas`.** The classifier now recurses through nested object properties and array `items`. Change paths read `inputSchema.properties.user.properties.email` instead of stopping at the top level. Previously in v1.0 / v1.1: @@ -81,31 +93,31 @@ Previously in v1.0 / v1.1: - **``.** Embeddable React dev-tool with a command list (tier-filterable), dispatch log, and live when-clause evaluator. Mount it behind a toggle in any greenfield app. - **`enableTierWarnings(registry)`.** Once-per-process `console.warn` on first dispatch of each `@experimental` command. Suppress with `ACTURE_SUPPRESS_EXPERIMENTAL_WARNINGS=1`. -What's next: see [`docs/next_session.md`](docs/next_session.md) for the v1.5 backlog. +What's next: see [`docs/roadmap.md`](docs/roadmap.md) for the forward plan and [`docs/next_session.md`](docs/next_session.md) for the immediate next step (a positioning-alignment review of `acture` core). -## Three paths +## Two flexibility dimensions -Acture serves three positioning paths from the same core. Same registry, dispatcher, and schema bridge — different adapter packages and different documentation. +acture adapts along two independent axes — and the agent keeps both open rather than defaulting one. (Full statement in [`docs/positioning.md`](docs/positioning.md).) -- **Greenfield-pure** — Design your app command-dispatch-first from day one. Install `acture` + `@acture/state-zustand` + the consumer adapters you want. -- **Strangler-fig migration** — Use Claude Code with `@acture/migration` to introduce command dispatch in an existing codebase incrementally, then graduate. -- **Drop-in footprint-minimizer** — Bolt a command palette + MCP server onto an existing app in 5 minutes. No deeper migration intent. +- **Core vs strangler-fig** — design command dispatch in from day one, or wrap it into an existing codebase incrementally and graduate over time. +- **Agent-written vs package-reuse** — have the agent write the integration into your project (zero acture dependency, maximum adaptability), or install an `acture-*` package (less code to own, tested — at the cost of a dependency). Decided per consumer, not per project. ## What it isn't -- Not a state library. Acture ships an adapter interface (`StateAdapter`) and reference adapters; the user's app keeps its existing state library. +- **Not a framework you must depend on.** acture is a development tool first. An agent wielding its skills can stand up a full command-dispatch architecture in a project whose `package.json` never gains an `acture-*` line. Depending on a package is always your explicit, opt-in choice. +- Not a state library. acture ships an adapter interface (`StateAdapter`) and reference adapters; your app keeps its existing state library. - Not a React library. Core has zero React dependencies; React lives in adapter packages. -- Not opinionated about your UI kit. Plug in your own design system via adapters. +- Not opinionated about your UI kit or your tools. Bring your own design system, your own e2e runner, your own AI SDK — acture's per-tool packages each integrate *one* known-good choice, never the only choice. ## Documentation map +- **Positioning (canonical):** [`docs/positioning.md`](docs/positioning.md) — what acture is, the dev-tool-first principle, the two dimensions. - **Conceptual:** [`docs/command_dispatch_journal_article.md`](docs/command_dispatch_journal_article.md) — the central architecture paper. -- **Plan:** [`docs/v1_plan.md`](docs/v1_plan.md) — research-informed v1 plan. -- **Implementation:** [`docs/implementation_plan.md`](docs/implementation_plan.md) — phase-by-phase guide with gates. +- **Forward plan + status:** [`docs/roadmap.md`](docs/roadmap.md) — what's done, what's next, what's deferred. - **Design synthesis:** [`docs/redesign_takeaways.md`](docs/redesign_takeaways.md) — opinionated commitments and hard "don'ts." +- **v1 plan (historical):** [`docs/v1_plan.md`](docs/v1_plan.md) / [`docs/implementation_plan.md`](docs/implementation_plan.md) — phases 0–4, all complete. - **Research:** [`docs/research/`](docs/research/) — five research findings (1–5) that informed the v1 plan. - **Patterns:** [`docs/parameterized_command_palette_guide.md`](docs/parameterized_command_palette_guide.md) — implementation patterns. -- **References:** [`docs/reference_notes.md`](docs/reference_notes.md) — distilled per-article notes on the 51 sources. - **For agents:** [`AGENTS.md`](AGENTS.md) and [`.claude/skills/`](.claude/skills/). ## License diff --git a/docs/backlog/codemods-polish-and-tier-mirror.md b/docs/backlog/codemods-polish-and-tier-mirror.md new file mode 100644 index 0000000..fc4936f --- /dev/null +++ b/docs/backlog/codemods-polish-and-tier-mirror.md @@ -0,0 +1,86 @@ +> **PARKED — 2026-05-14.** This was the v1.4-era handoff. v1.5 instead became a *repositioning + namespace-migration* increment (see `docs/roadmap.md`), and the immediate next step is a core positioning-alignment review (see `docs/next_session.md`). The three candidates below — codemods README/CLI polish, the `.d.ts` tier mirror, and the AI-codemod-recipe doc — are still valid backlog items; they are tracked in `docs/roadmap.md` §"Deferred / backlog". Pick this file back up when those items are scheduled. The version numbers and "next session" framing below are stale; read them as a candidate list, not a plan. + +--- + +# Next Session — v1.5 Planning *(superseded — see banner above)* + +**Your role:** You are the v1.5 planning / implementing agent. **v1.4 is DONE as of 2026-05-14.** Phase 4 v1.0 + v1.1 + v1.2 + v1.3 + v1.4 increments have all landed. Your job is to confirm v1.5 scope with the user and ship it. + +**v1.4 finished 2026-05-14.** Repo state at handoff: + +- **15 packages publishable.** New this increment: `eslint-plugin-acture-migration@1.0.0`. Other notable versions: `acture@1.1.0`, `acture-cli@1.2.0`, `acture-migration@1.1.0`, `acture-build-tier@1.1.0`, `acture-codemods@1.1.0`, the rest at `1.0.0`. +- **396 package tests** + **41 example tests** all green. +- **4 worked examples** unchanged. +- All packages typecheck and build via tsup / vite. +- v1.4 reflection: [`docs/v1_4-reflection.md`](v1_4-reflection.md). +- v1.4 ran the deferred fresh-agent release-gate test — results in [`docs/fresh-agent-test-results.md`](fresh-agent-test-results.md). + +What v1.4 shipped on top of v1.3: + +- **`eslint-plugin-acture-migration`** — one rule, `acture/no-stale-wrap-mutation`, flags `wrapMutation(...)` calls whose result is never used (the migration has graduated; author with `defineCommand`). Closes a research-4 backlog item carried since v1.1. The `migration-graduate` skill now points at it. +- **Fresh-agent release-gate test** — a fresh agent drove `acture-codemods` from its README alone. Engine + CLI passed; the README did not (see below). + +What's still in the backlog: + +- **Codemods README + CLI polish.** The v1.4 fresh-agent test found the `acture-codemods` README's headline `npx acture-codemods` invocation fails pre-publish, and that per-codemod `--option` keys, `--manifest`, and `--files-from` are undiscoverable from the docs. The CLI also gives an ambiguous "No files matched" error for missing-vs-nonexistent `--target`. Full finding list and priority order in [`docs/fresh-agent-test-results.md`](fresh-agent-test-results.md) §"Recommended v1.5 follow-up". This is the direct, scoped output of v1.4's release gate. +- **`.d.ts` mirror of resolved tier values.** Optional polish — JSDoc already surfaces in `.d.ts`; this would put the resolved `tier: 'experimental'` on the type-system path too. Slipped through v1.2 → v1.4. +- **Hypermod-style AI-generation recipe doc.** Research-4 recommendation #8 — markdown doc in `docs/` showing how to ask Claude to write a one-off codemod for a handler shape that doesn't match any of the shipped five. + +--- + +## Step 1 — Orient + +Read in this order (~15 minutes total): + +1. `docs/v1_4-reflection.md` — what v1.4 found, especially §"Pre-v1.5 reflection answers". +2. `docs/fresh-agent-test-results.md` — the release-gate findings that drive the top v1.5 candidate. +3. `.claude/skills/acture-hard-donts/SKILL.md` — re-read before merging anything. +4. `docs/v1_plan.md` §"Post-v1 (deferred, not committed)" — long-term backlog (undo, macros, telemetry, Python companion). None promote to v1.5 without explicit user direction. + +## Step 2 — Pick v1.5 scope + +Rule of three. The three candidates below are concrete and bounded. Pick at most TWO unless the user explicitly authorizes more. + +**Strong candidate (v1.5):** + +1. **Codemods README + CLI polish.** Act on `docs/fresh-agent-test-results.md` §"Recommended v1.5 follow-up". In priority order: (a) fix the invocation story — document a monorepo invocation (`node dist/cli.js …` or a workspace bin) alongside the `npx` form, or add an explicit publish-status line so the Quick start contains at least one command that runs today; (b) document per-codemod `--option` keys — a column in the codemod table or a per-codemod sub-section; (c) document `--manifest` and `--files-from` in the README; (d) disambiguate the "No files matched" CLI error for missing-vs-nonexistent `--target`. Mostly a README pass plus one small CLI error-message edit + ~3 tests. This is the direct output of v1.4's release gate — high signal, well-scoped. + +**Medium candidates (could ship as quick polish):** + +2. **`.d.ts` mirror of resolved tier values.** A tsup post-process pass that walks the emitted `.d.ts` files and injects the resolved `tier:` value at the type level. Small lift if implemented as a `acture-build-tier/dts-mirror` companion to the existing JS-side mirror. + +3. **Hypermod-style AI-generation recipe doc.** Pure docs (no code) — markdown in `docs/` showing how to prompt Claude to author a one-off codemod for a handler shape that doesn't match any of the shipped five. References the existing `Codemod` interface so users can drop the generated code into `acture-codemods` or use it standalone. + +**My recommendation if asked:** ship **#1 (codemods polish)** — it closes the loop on the v1.4 release gate and removes a real "first command fails" blocker. Optionally pair with **#3 (AI-recipe doc)** since it's pure docs and thematically adjacent to the codemods surface. #2 is still optional polish and can wait. + +## Step 3 — Things that are still post-v1.5 + +These remain `docs/v1_plan.md` §"Post-v1 (deferred, not committed)": + +- `acture/undo`. +- `acture/macros`, `acture/telemetry`, `acture/sandbox`, `acture/test-property`. +- `acture/state-jotai`, `acture/state-valtio`. +- Python companion (research-6 not executed). + +Do NOT promote any of these without explicit user direction AND three concrete callers. + +## Step 4 — Hard-don'ts still in force + +Re-read `.claude/skills/acture-hard-donts/SKILL.md`. The closed-surface principle held through v1.0 → v1.4. CommandRecord remains at 15 fields. Hold the line. + +## Step 5 — Release ceremony for v1.5 + +When v1.5 deliverables are merged and tests are green: + +1. Bump only the affected packages (e.g. `acture-codemods` if its CLI/README changed — a `patch` for docs + error-message, a `minor` if a new flag is added). +2. `pnpm -r --filter "./packages/*" build && pnpm test` — green. +3. `npm pack --dry-run` clean for each bumped package. +4. Tag and publish (owner discretion). +5. Write `docs/v1_5-reflection.md`. +6. Replace this file with a v1.6 / post-v1 planning prompt. + +## When unsure + +Re-read this file, `docs/v1_4-reflection.md`, `docs/fresh-agent-test-results.md`, `docs/v1_plan.md` §"Post-v1", and `.claude/skills/acture-hard-donts/SKILL.md`. If still unsure, append a note to `docs/escalations.md` (create if missing) and ask the user before locking in any irreversible decision. + +**Good luck.** v1.4 rounded out the release-readiness theme — the ESLint plugin closed a backlog item and the fresh-agent test validated the codemod surface, surfacing a tight, scoped punch list. v1.5 should act on that punch list. Don't promote post-v1 items into v1.x. diff --git a/docs/fresh-agent-test-results.md b/docs/fresh-agent-test-results.md index 3dc67ce..fbf3b2c 100644 --- a/docs/fresh-agent-test-results.md +++ b/docs/fresh-agent-test-results.md @@ -1,14 +1,14 @@ # Fresh-agent release-gate test — results **Run:** 2026-05-14, as part of v1.4 (release-readiness theme). -**Gate origin:** Phase-4 reflection §5 deferred this through v1.0 → v1.3. v1.3's reflection §"Pre-v1.4 reflection answers" §4 named the next session as the right place to run it, with the `@acture/codemods` README as the densest agent-facing surface in the repo. +**Gate origin:** Phase-4 reflection §5 deferred this through v1.0 → v1.3. v1.3's reflection §"Pre-v1.4 reflection answers" §4 named the next session as the right place to run it, with the `acture-codemods` README as the densest agent-facing surface in the repo. ## What was tested A fresh agent — no prior context about acture — was given only: - the location of `packages/codemods/README.md`, -- the fact that `@acture/codemods` is an unpublished workspace package whose CLI is built at `packages/codemods/dist/cli.js`. +- the fact that `acture-codemods` is an unpublished workspace package whose CLI is built at `packages/codemods/dist/cli.js`. It was instructed to read **only the README** (not source, not other docs, not skills), pick one codemod, build a realistic sample file, drive the CLI end-to-end (`--list`, `--help`, `--dry-run --json`, real apply), and deliberately mis-use the CLI to test error messages. The deliverable was a written assessment of where the README + CLI fall short — **no code change in v1.4** (per `docs/next_session.md` §"Strong candidates" #2). @@ -18,7 +18,7 @@ It was instructed to read **only the README** (not source, not other docs, not s ### Findings (verbatim priority order from the fresh agent) -1. **The headline invocation does not work.** Every Quick-start and CLI example uses `npx @acture/codemods …` or the `acture-codemods` bin. The package is unpublished, so the *first command a copy-pasting user runs* fails with an npm 404. The README never mentions `node dist/cli.js`, a workspace bin alias, a build step, or publish status. Single biggest blocker. +1. **The headline invocation does not work.** Every Quick-start and CLI example uses `npx acture-codemods …` or the `acture-codemods` bin. The package is unpublished, so the *first command a copy-pasting user runs* fails with an npm 404. The README never mentions `node dist/cli.js`, a workspace bin alias, a build step, or publish status. Single biggest blocker. 2. **Per-codemod `--option` keys are undiscoverable.** The README mentions `--option key=value` generically and the codemod table alludes to "configurable setter pattern" / "optional slash→dot id rewrite", but no option *names* are listed anywhere a CLI user can see them. `--help` shows one example (`id-prefix=app.button`) but no enumeration. The programmatic example leaks `events: 'onClick,onSubmit'` — so the options exist, just unlisted. diff --git a/docs/implementation_plan.md b/docs/implementation_plan.md index 7f19f9f..4384a91 100644 --- a/docs/implementation_plan.md +++ b/docs/implementation_plan.md @@ -87,8 +87,8 @@ When this reflection note is committed, Phase 0 is done. **Packages to ship:** - `packages/core/` (`acture` on npm) — the core. NO React, NO state library, NO UI deps. -- `packages/state-zustand/` (`@acture/state-zustand` on npm; subject to scoping decision) — production reference adapter, ~50 LOC + tests. -- `packages/palette-react/` (`@acture/palette-react`) — minimal version, parameter-free commands only. +- `packages/state-zustand/` (`acture-state-zustand` on npm; subject to scoping decision) — production reference adapter, ~50 LOC + tests. +- `packages/palette-react/` (`acture-palette-react`) — minimal version, parameter-free commands only. **`acture/core` surface (Phase 1):** - `defineCommand(spec): CommandRecord` — type-checked, frozen result. Validates at registration time that param schemas are in the JSON-Schema-representable subset. @@ -98,10 +98,10 @@ When this reflection note is committed, Phase 0 is done. - Schema bridge: `toJsonSchema(record, options?)` — accepts injected converter; default uses Zod v4's `z.toJSONSchema`. - `Result` type with **reserved** undo hooks (`patches?`, `effects?`). Phase 1 ignores them; they exist so Phase 4+ can add them non-breakingly. -**`@acture/state-zustand` surface:** +**`acture-state-zustand` surface:** - `createZustandAdapter(store): PatchCapableAdapter` — wraps `zustand/vanilla` `createStore`. Uses `zustand/middleware/immer` with `produceWithPatches` for `setStateWithPatches`. -**`@acture/palette-react` surface (Phase 1, minimum):** +**`acture-palette-react` surface (Phase 1, minimum):** - Wrap cmdk's `` primitive. - Iterate registry.list() filtering by tier. - Group by `category`. @@ -113,7 +113,7 @@ When this reflection note is committed, Phase 0 is done. - 6–8 commands: `app.graph.addNode`, `app.graph.removeNode`, `app.graph.connectNodes`, `app.graph.deleteEdge`, `app.view.zoomToFit`, `app.selection.selectAll`, `app.view.toggleGrid` (param-free toggle). - All state mutations go through commands (no direct `setState`). - State held in a zustand store via the new adapter. -- A `` (from `@acture/palette-react`) overlay opens on Ctrl+K and dispatches parameter-free commands. +- A `` (from `acture-palette-react`) overlay opens on Ctrl+K and dispatches parameter-free commands. - One parameterized command (`addNode({x, y, label})`) is registered but **explicitly noted in the README as "Phase 2 will surface this in the palette"**. **Tests:** @@ -212,7 +212,7 @@ Phase 2 is done when these are in `phase-2-reflection.md` and any escalations ar ## Phase 3 — Migration package and skills -**Status:** ✅ DONE — 2026-05-13. Reflection: [`docs/phase-3-reflection.md`](phase-3-reflection.md). `@acture/migration` ships with 36 unit tests; the `examples/migration/zustand-wrap/` before/after pair demonstrates the strangler-fig path end-to-end (6 wraps + 2 graduations, palette overlay, kind-heuristic override rate 0/8 = 0%). +**Status:** ✅ DONE — 2026-05-13. Reflection: [`docs/phase-3-reflection.md`](phase-3-reflection.md). `acture-migration` ships with 36 unit tests; the `examples/migration/zustand-wrap/` before/after pair demonstrates the strangler-fig path end-to-end (6 wraps + 2 graduations, palette overlay, kind-heuristic override rate 0/8 = 0%). **Goal:** Ship `acture/migration` per research-4 §A.6. Ship the migration-track skills. Demonstrate the end-to-end workflow against a small fixture app. diff --git a/docs/next_session.md b/docs/next_session.md index 3a7879a..4aae4dd 100644 --- a/docs/next_session.md +++ b/docs/next_session.md @@ -1,82 +1,58 @@ -# Next Session — v1.5 Planning +# Next Session — `acture` core positioning-alignment review -**Your role:** You are the v1.5 planning / implementing agent. **v1.4 is DONE as of 2026-05-14.** Phase 4 v1.0 + v1.1 + v1.2 + v1.3 + v1.4 increments have all landed. Your job is to confirm v1.5 scope with the user and ship it. +**Your role:** review (and refactor where the review demands it) the `acture` **core** package so it genuinely lives up to `docs/positioning.md`. This is the immediate next step after the v1.5 repositioning + namespace-migration increment. -**v1.4 finished 2026-05-14.** Repo state at handoff: +**This is a review first, a refactor second.** Do not start rewriting. Audit, find the gaps, then make the *minimum* changes the audit surfaces. The closed `CommandRecord` (15 fields) does not change. -- **15 packages publishable.** New this increment: `eslint-plugin-acture-migration@1.0.0`. Other notable versions: `acture@1.1.0`, `@acture/cli@1.2.0`, `@acture/migration@1.1.0`, `@acture/build-tier@1.1.0`, `@acture/codemods@1.1.0`, the rest at `1.0.0`. -- **396 package tests** + **41 example tests** all green. -- **4 worked examples** unchanged. -- All packages typecheck and build via tsup / vite. -- v1.4 reflection: [`docs/v1_4-reflection.md`](v1_4-reflection.md). -- v1.4 ran the deferred fresh-agent release-gate test — results in [`docs/fresh-agent-test-results.md`](fresh-agent-test-results.md). +## Step 0 — Orient -What v1.4 shipped on top of v1.3: +Read, in this order: -- **`eslint-plugin-acture-migration`** — one rule, `acture/no-stale-wrap-mutation`, flags `wrapMutation(...)` calls whose result is never used (the migration has graduated; author with `defineCommand`). Closes a research-4 backlog item carried since v1.1. The `migration-graduate` skill now points at it. -- **Fresh-agent release-gate test** — a fresh agent drove `@acture/codemods` from its README alone. Engine + CLI passed; the README did not (see below). +1. `docs/positioning.md` — **canonical.** The dev-tool-first principle and the two flexibility dimensions. This is the standard you are auditing core against. +2. `docs/roadmap.md` — where this session sits, what's done, what's deferred. +3. `.claude/skills/acture-architecture-primer/SKILL.md` and `.claude/skills/acture-consumer-integration/SKILL.md`. +4. `.claude/skills/acture-hard-donts/SKILL.md` — re-read before changing anything. +5. `.claude/skills/acture-command-record-shape/SKILL.md` — the closed surface you must not widen. +6. `packages/core/` — the actual code under review. -What's still in the backlog: +## Step 1 — The audit -- **Codemods README + CLI polish.** The v1.4 fresh-agent test found the `@acture/codemods` README's headline `npx @acture/codemods` invocation fails pre-publish, and that per-codemod `--option` keys, `--manifest`, and `--files-from` are undiscoverable from the docs. The CLI also gives an ambiguous "No files matched" error for missing-vs-nonexistent `--target`. Full finding list and priority order in [`docs/fresh-agent-test-results.md`](fresh-agent-test-results.md) §"Recommended v1.5 follow-up". This is the direct, scoped output of v1.4's release gate. -- **`.d.ts` mirror of resolved tier values.** Optional polish — JSDoc already surfaces in `.d.ts`; this would put the resolved `tier: 'experimental'` on the type-system path too. Slipped through v1.2 → v1.4. -- **Hypermod-style AI-generation recipe doc.** Research-4 recommendation #8 — markdown doc in `docs/` showing how to ask Claude to write a one-off codemod for a handler shape that doesn't match any of the shipped five. +The positioning makes two promises about core. Check whether the code keeps them. ---- +**Promise A — core is the minimal primitive.** `acture` core is *registry + dispatcher + schema bridge + the state-adapter interface*, and nothing else. Audit `packages/core/src` for anything that is really consumer logic, adapter logic, or convenience that belongs in an optional package. For each thing in core, ask: is this a *primitive*, or is it an *accelerator that crept inward*? Anything that fails belongs in (or as) a separate optional package. -## Step 1 — Orient +**Promise B — the agent-written path is real.** The positioning says a developer can stand up command dispatch with *zero* `acture-*` dependency — the agent hand-writes it. Is that actually achievable today? An agent has the skills (which explain acture's *design*) and core's source (as a *reference implementation*). Could it reproduce an equivalent minimal registry + dispatcher in a target project from those? If not — if the patterns are only encoded as installable code and never as a *legible, reproducible reference* — that is the central gap, and closing it is the most valuable thing this session can do. -Read in this order (~15 minutes total): +Also check the boundary the hard-don'ts care about: core imports zero React, zero state libraries (hard-don'ts #6, and the spirit of #2/#3). Verify, don't assume. -1. `docs/v1_4-reflection.md` — what v1.4 found, especially §"Pre-v1.5 reflection answers". -2. `docs/fresh-agent-test-results.md` — the release-gate findings that drive the top v1.5 candidate. -3. `.claude/skills/acture-hard-donts/SKILL.md` — re-read before merging anything. -4. `docs/v1_plan.md` §"Post-v1 (deferred, not committed)" — long-term backlog (undo, macros, telemetry, Python companion). None promote to v1.5 without explicit user direction. +## Step 2 — Decide the deliverable with the user -## Step 2 — Pick v1.5 scope +The audit will land in one of a few places. **Surface the finding to the user before refactoring** — the right deliverable is a judgment call: -Rule of three. The three candidates below are concrete and bounded. Pick at most TWO unless the user explicitly authorizes more. +- If core is already minimal and the agent-written path is genuinely reproducible from existing material → the deliverable may be just a short written confirmation + any small tightening. That is a fine outcome; do not invent work. +- If non-primitive code has crept into core → propose the extraction (to which package?) and confirm scope before moving code. +- If the agent-written path is *asserted but not reproducible* → the likely deliverable is a new reference artifact: a "hand-written registry" reference doc, and/or a consumer/greenfield skill that walks an agent through writing the dispatch layer by hand. Confirm the shape with the user. -**Strong candidate (v1.5):** +Use `AskUserQuestion` for the scope fork. Do not guess. -1. **Codemods README + CLI polish.** Act on `docs/fresh-agent-test-results.md` §"Recommended v1.5 follow-up". In priority order: (a) fix the invocation story — document a monorepo invocation (`node dist/cli.js …` or a workspace bin) alongside the `npx` form, or add an explicit publish-status line so the Quick start contains at least one command that runs today; (b) document per-codemod `--option` keys — a column in the codemod table or a per-codemod sub-section; (c) document `--manifest` and `--files-from` in the README; (d) disambiguate the "No files matched" CLI error for missing-vs-nonexistent `--target`. Mostly a README pass plus one small CLI error-message edit + ~3 tests. This is the direct output of v1.4's release gate — high signal, well-scoped. +## Step 3 — Refactor (only what Step 1–2 justified) -**Medium candidates (could ship as quick polish):** +- Rule of three and the hard-don'ts still bind. The `CommandRecord` stays at 15 fields. +- If you move code between packages, it is a `minor` for the gaining package and the losing package; changeset accordingly. +- `pnpm build && pnpm test && pnpm typecheck` green across the workspace; example apps still build and pass. -2. **`.d.ts` mirror of resolved tier values.** A tsup post-process pass that walks the emitted `.d.ts` files and injects the resolved `tier:` value at the type level. Small lift if implemented as a `@acture/build-tier/dts-mirror` companion to the existing JS-side mirror. +## Step 4 — Wrap up -3. **Hypermod-style AI-generation recipe doc.** Pure docs (no code) — markdown in `docs/` showing how to prompt Claude to author a one-off codemod for a handler shape that doesn't match any of the shipped five. References the existing `Codemod` interface so users can drop the generated code into `@acture/codemods` or use it standalone. +- Update `docs/roadmap.md`: mark this item done, record what the audit found, move the "macros + e2e tooling" item to NEXT. +- Write a short reflection (`docs/core-review-reflection.md` or fold into the roadmap — your call; keep it short). +- Replace this file with the handoff for the macros + e2e tooling work (see `docs/roadmap.md` §"Next" for what that entails). -**My recommendation if asked:** ship **#1 (codemods polish)** — it closes the loop on the v1.4 release gate and removes a real "first command fails" blocker. Optionally pair with **#3 (AI-recipe doc)** since it's pure docs and thematically adjacent to the codemods surface. #2 is still optional polish and can wait. +## Pre-flight note — publishing the renamed packages -## Step 3 — Things that are still post-v1.5 - -These remain `docs/v1_plan.md` §"Post-v1 (deferred, not committed)": - -- `acture/undo`. -- `acture/macros`, `acture/telemetry`, `acture/sandbox`, `acture/test-property`. -- `acture/state-jotai`, `acture/state-valtio`. -- Python companion (research-6 not executed). - -Do NOT promote any of these without explicit user direction AND three concrete callers. - -## Step 4 — Hard-don'ts still in force - -Re-read `.claude/skills/acture-hard-donts/SKILL.md`. The closed-surface principle held through v1.0 → v1.4. CommandRecord remains at 15 fields. Hold the line. - -## Step 5 — Release ceremony for v1.5 - -When v1.5 deliverables are merged and tests are green: - -1. Bump only the affected packages (e.g. `@acture/codemods` if its CLI/README changed — a `patch` for docs + error-message, a `minor` if a new flag is added). -2. `pnpm -r --filter "./packages/*" build && pnpm test` — green. -3. `npm pack --dry-run` clean for each bumped package. -4. Tag and publish (owner discretion). -5. Write `docs/v1_5-reflection.md`. -6. Replace this file with a v1.6 / post-v1 planning prompt. +The 13 packages renamed from `@acture/*` to `acture-*` in v1.5 are built, tested, and **not yet published**. `acture@1.1.0` and `eslint-plugin-acture-migration@1.0.0` are already on npm. Publishing the other 13 (`pnpm changeset publish`, or the relevant subset) is unblocked and owner-discretion — offer to do it, with explicit user confirmation, but it is independent of the core review and not required to start. ## When unsure -Re-read this file, `docs/v1_4-reflection.md`, `docs/fresh-agent-test-results.md`, `docs/v1_plan.md` §"Post-v1", and `.claude/skills/acture-hard-donts/SKILL.md`. If still unsure, append a note to `docs/escalations.md` (create if missing) and ask the user before locking in any irreversible decision. +Re-read `docs/positioning.md` and `docs/roadmap.md`. If a change is irreversible or you cannot tell whether it honours the positioning, append to `docs/escalations.md` (create if missing) and ask the user. -**Good luck.** v1.4 rounded out the release-readiness theme — the ESLint plugin closed a backlog item and the fresh-agent test validated the codemod surface, surfacing a tight, scoped punch list. v1.5 should act on that punch list. Don't promote post-v1 items into v1.x. +**Good luck.** The point of this session is to make the dev-tool-first promise *true in the code*, not just in the docs. A clean "it already holds" is a perfectly good result — the failure mode is inventing a refactor to look busy. diff --git a/docs/phase-1-acceptance.md b/docs/phase-1-acceptance.md index ff79852..58dc7a0 100644 --- a/docs/phase-1-acceptance.md +++ b/docs/phase-1-acceptance.md @@ -4,7 +4,7 @@ ## 1. Graph editor example runs -`pnpm --filter @acture/example-graph-editor dev` starts a Vite dev server on `http://localhost:5173`. Curl against the dev server returns the example's HTML and the transformed `main.tsx` source. `pnpm --filter @acture/example-graph-editor build` produces a production bundle (`dist/index.html`, `dist/assets/index-*.js`, `dist/assets/index-*.css`, total ~345 KB / ~107 KB gzip). +`pnpm --filter acture-example-graph-editor dev` starts a Vite dev server on `http://localhost:5173`. Curl against the dev server returns the example's HTML and the transformed `main.tsx` source. `pnpm --filter acture-example-graph-editor build` produces a production bundle (`dist/index.html`, `dist/assets/index-*.js`, `dist/assets/index-*.css`, total ~345 KB / ~107 KB gzip). **Note:** browser verification (Ctrl+K opening the palette, click-to-select, etc.) requires a human-driven session. The agent cannot drive a browser. The dev server and build outputs verify wiring; the user should drive the UI once. diff --git a/docs/phase-1-reflection.md b/docs/phase-1-reflection.md index 089ce1c..f96b1de 100644 --- a/docs/phase-1-reflection.md +++ b/docs/phase-1-reflection.md @@ -28,7 +28,7 @@ This file answers the six questions from `docs/implementation_plan.md` §"Phase **The friction I expected but didn't hit:** wrapping `zustand/vanilla`'s native `subscribe(listener: (state, previousState) => void)` against acture's `subscribe(listener: (next, prev) => void)`. Zustand already passes both arguments natively; the adapter just forwards them. RTK won't be able to pass `previous` cleanly (it's not tracked) — the StateAdapter contract documents that adapters that can't track `previous` should pass the same value as `current`. That's still acceptable; Phase 2 will confirm with the RTK adapter. -**Patches-capable extension was clean.** `setStateWithPatches` wraps `produceWithPatches` from `immer`; the resulting Immer patches are mapped to acture's `Patch` type (which is RFC-6902-shaped). `applyPatches` does the reverse mapping and feeds Immer's `applyPatches`. The integration test that round-trips through inverse patches passes — this is the load-bearing capability for the future `@acture/undo`. +**Patches-capable extension was clean.** `setStateWithPatches` wraps `produceWithPatches` from `immer`; the resulting Immer patches are mapped to acture's `Patch` type (which is RFC-6902-shaped). `applyPatches` does the reverse mapping and feeds Immer's `applyPatches`. The integration test that round-trips through inverse patches passes — this is the load-bearing capability for the future `acture-undo`. **Recommendation for Phase 2:** - Pin the `Patch` op enum to `'add' | 'remove' | 'replace'` only for now. Don't add `'copy'`/`'move'` until a real caller asks. Immer does not produce them; only mobx-state-tree's `onPatch` does, and acture's undo design doesn't need them. @@ -77,13 +77,13 @@ This file answers the six questions from `docs/implementation_plan.md` §"Phase Ran the merge checklist from `.claude/skills/acture-hard-donts/SKILL.md`. Going through them: 1. **No conditional logic in command metadata.** ✅ The `when` field is the upper bound; we did not grow callback variants à la Obsidian. -2. **No god-package.** ✅ Core has zero React, zero state-library, zero UI deps. The three packages (`acture`, `@acture/state-zustand`, `@acture/palette-react`) are tightly scoped. -3. **No business logic in adapter packages.** ✅ `@acture/state-zustand` only translates between zustand+immer and the `StateAdapter` contract. `@acture/palette-react` only iterates the registry and renders cmdk items — even the parameterized-command "Phase 2 badge" is a rendering choice, not business logic. +2. **No god-package.** ✅ Core has zero React, zero state-library, zero UI deps. The three packages (`acture`, `acture-state-zustand`, `acture-palette-react`) are tightly scoped. +3. **No business logic in adapter packages.** ✅ `acture-state-zustand` only translates between zustand+immer and the `StateAdapter` contract. `acture-palette-react` only iterates the registry and renders cmdk items — even the parameterized-command "Phase 2 badge" is a rendering choice, not business logic. 4. **No `if (mode === ...)` in shared helpers.** ✅ Core has no mode awareness. 5. **No `eval()`-ing LLM strings.** ✅ The dispatcher takes `(id, args)` and uses `Map.get(id)`. No reflective call. -6. **No coupling the registry to React.** ✅ The `createRegistry` is plain TS. React lives only in `@acture/palette-react` (its hook + JSX). The example's `useGraphState` is in the example, not in core. +6. **No coupling the registry to React.** ✅ The `createRegistry` is plain TS. React lives only in `acture-palette-react` (its hook + JSX). The example's `useGraphState` is in the example, not in core. 7. **No promoting `@experimental` without a migration story.** N/A (no experimental→stable promotions yet). -8. **No bundling a UI kit.** ✅ `@acture/palette-react` uses cmdk's headless primitives; styling is the host's job, demonstrated in the example with a small CSS file. +8. **No bundling a UI kit.** ✅ `acture-palette-react` uses cmdk's headless primitives; styling is the host's job, demonstrated in the example with a small CSS file. 9. **No marketing on category.** ✅ READMEs lead with "One schema. Palette, hotkeys, AI tools, MCP, and tests — for free." 10. **No LLM-as-authorization.** ✅ Schema validation is at the dispatcher (`schema.safeParse(params)`) before `execute` runs. @@ -94,7 +94,7 @@ Ran the merge checklist from `.claude/skills/acture-hard-donts/SKILL.md`. Going **None blocking.** The four escalations from Phase 0's handoff doc were resolved at the start of this session via `AskUserQuestion`: - Branching: Phase 0 committed to `main`, Phase 1 on `phase-1` branch (PR-style). -- Subpackage naming: `@acture/` scoped pattern (`@acture/state-zustand`, `@acture/palette-react`). +- Subpackage naming: `acture-` scoped pattern (`acture-state-zustand`, `acture-palette-react`). - Schema layer: Zod-only for Phase 1. Standard Schema acceptance lands in Phase 2 without breaking Zod authors (additive). - When-clause parser: hand-rolled recursive descent. Confirmed in §3 above as the right call for Phase 1. @@ -111,12 +111,12 @@ Ran the merge checklist from `.claude/skills/acture-hard-donts/SKILL.md`. Going Per `docs/implementation_plan.md` §"Phase 2 — Adapter buildout" the next phase ships: -- `@acture/hotkeys` (tinykeys) -- Extended `@acture/palette-react` with parameterized command support (research-2 §9) -- `@acture/forms-autoform` + `@acture/forms-rjsf` -- `@acture/state-redux` (RTK reference adapter) -- `@acture/mcp` (MCP server adapter) -- `@acture/ai-vercel` (Vercel AI SDK adapter) +- `acture-hotkeys` (tinykeys) +- Extended `acture-palette-react` with parameterized command support (research-2 §9) +- `acture-forms-autoform` + `acture-forms-rjsf` +- `acture-state-redux` (RTK reference adapter) +- `acture-mcp` (MCP server adapter) +- `acture-ai-vercel` (Vercel AI SDK adapter) - Extended worked examples + a new drop-in example None of those are gated by a Phase 1 rethink. The CommandRecord shape, the StateAdapter contract, and the schema bridge are stable. **Phase 1 is DONE.** diff --git a/docs/phase-2-reflection.md b/docs/phase-2-reflection.md index 4036f00..a2ba80f 100644 --- a/docs/phase-2-reflection.md +++ b/docs/phase-2-reflection.md @@ -35,9 +35,9 @@ This file answers the six questions from `docs/implementation_plan.md` §"Phase **Yes, and the dual path is cleaner than expected.** -- **Vercel AI SDK** accepts Zod schemas directly. `@acture/ai-vercel` passes `record.params` through unchanged, preserving every `z.refine`, `z.transform`, and custom error message that JSON Schema would silently lose. The AI SDK's `tool({ parameters: zodSchema, execute })` handles the rest. +- **Vercel AI SDK** accepts Zod schemas directly. `acture-ai-vercel` passes `record.params` through unchanged, preserving every `z.refine`, `z.transform`, and custom error message that JSON Schema would silently lose. The AI SDK's `tool({ parameters: zodSchema, execute })` handles the rest. -- **MCP** wants JSON Schema on the wire. `@acture/mcp/tools.ts` calls `toJsonSchema(record)` and emits the envelope as a `McpToolDescriptor`. Strict mode is opt-in (the OpenAI-style `additionalProperties: false` flavor). +- **MCP** wants JSON Schema on the wire. `acture-mcp/tools.ts` calls `toJsonSchema(record)` and emits the envelope as a `McpToolDescriptor`. Strict mode is opt-in (the OpenAI-style `additionalProperties: false` flavor). **Edge cases I expected but didn't hit:** none of the worked-example commands triggered a Zod feature that JSON Schema can't represent. The `JSON-Schema-representable subset` rule (`acture-schema-bridge` skill §"hard rule") was easy to obey — `z.string().min(1)`, `z.enum([...])`, `z.number()`, `z.object({...})` all project cleanly. @@ -63,22 +63,22 @@ Ran `.claude/skills/acture-hard-donts/SKILL.md` against every new package. 1. **No conditional logic in command metadata.** ✅ The `kind` field is still discrete (`'atomic' | 'handoff'`); auto-derivation happens at palette/render time, not at record-definition time. 2. **No god-package.** ✅ Each new package is single-purpose: - - `@acture/hotkeys` — keyboard binding only. - - `@acture/forms-autoform` — Zod-aware form only. - - `@acture/forms-rjsf` — JSON-Schema form only. - - `@acture/state-redux` — RTK ↔ StateAdapter only. - - `@acture/mcp` — MCP projection + server only. - - `@acture/ai-vercel` — Vercel AI tool definitions only. + - `acture-hotkeys` — keyboard binding only. + - `acture-forms-autoform` — Zod-aware form only. + - `acture-forms-rjsf` — JSON-Schema form only. + - `acture-state-redux` — RTK ↔ StateAdapter only. + - `acture-mcp` — MCP projection + server only. + - `acture-ai-vercel` — Vercel AI tool definitions only. 3. **No business logic in adapter packages.** ✅ Each adapter only translates between an external interface and the registry/state contracts. Spot-checked every `execute` path. 4. **No `if (mode === ...)` in shared helpers.** ✅ Core was not touched in Phase 2. 5. **No `eval()`-ing LLM strings.** ✅ MCP and AI SDK both use `registry.dispatch(name, args)` with Zod validation on the way in — no reflective call construction. -6. **No coupling the registry to React.** ✅ Core has no React import. Hotkeys' main entry is React-free; the React hook lives at `@acture/hotkeys/react`. +6. **No coupling the registry to React.** ✅ Core has no React import. Hotkeys' main entry is React-free; the React hook lives at `acture-hotkeys/react`. 7. **No promoting `@experimental` without a migration story.** N/A. 8. **No bundling a UI kit.** ✅ Both form adapters render bare HTML with CSS hooks (`data-acture-autoform-*`, `data-acture-rjsf-*`); the host owns styling. 9. **No marketing on category.** ✅ READMEs lead with mechanical descriptions. 10. **No LLM-as-authorization.** ✅ Schema validation is at the dispatcher, not at the AI/MCP boundary. -**One borderline call:** `@acture/forms-autoform` and `@acture/forms-rjsf` ship as separate packages even though both implement the same `PaletteFormAdapterProps` contract. This is *intentional* (research-2 §9.4: form library is the host's choice; acture provides the contract). But it means a host that wants the autoform-rjsf-side-by-side comparison has to install both. Not a hard-don't violation; a documented tradeoff. +**One borderline call:** `acture-forms-autoform` and `acture-forms-rjsf` ship as separate packages even though both implement the same `PaletteFormAdapterProps` contract. This is *intentional* (research-2 §9.4: form library is the host's choice; acture provides the contract). But it means a host that wants the autoform-rjsf-side-by-side comparison has to install both. Not a hard-don't violation; a documented tradeoff. ## 6. Decisions to escalate to user @@ -91,13 +91,13 @@ Ran `.claude/skills/acture-hard-donts/SKILL.md` against every new package. **Non-blocking observations for the user:** -1. **The MCP and AI-demo scripts in `examples/greenfield/graph-editor/scripts/`** hold state in the Node process — they do not share the graph state with the browser app. This is documented in both the script comments and the example README. A real production setup would proxy to a single source of truth. If this becomes a friction point, consider a `@acture/sync` package that wires registry events across processes (post-v1). +1. **The MCP and AI-demo scripts in `examples/greenfield/graph-editor/scripts/`** hold state in the Node process — they do not share the graph state with the browser app. This is documented in both the script comments and the example README. A real production setup would proxy to a single source of truth. If this becomes a friction point, consider a `acture-sync` package that wires registry events across processes (post-v1). 2. **Zod introspection is brittle.** `derive-kind.ts` and `auto-form.tsx` both peek into Zod's `_def` / `.def` internals to detect enum / boolean / optional / default. Zod has no public introspection API yet. If Standard Schema's `~standard` marker grows a public field-shape API, migrate to it. 3. **The dev server was not exercised in a real browser** during this session — the time budget was spent on the package surface and tests. The graph-editor's production bundle builds (Vite reports success, ~358KB gzipped), and the existing UI tests verify the dispatcher path; but I did not click through the new palette + form flow in a browser. **Recommended manual smoke test before merging:** open the graph-editor, press Ctrl/Cmd+K, pick "Add node", verify the form renders and dispatches. -4. **`@acture/forms-rjsf`'s test coverage is thin** (3 tests). The package is a thin shim over `@rjsf/core` so most of the failure modes belong upstream. If the user wants stronger coverage, add a "renders rjsf form for nested-object schema" test before declaring v1. +4. **`acture-forms-rjsf`'s test coverage is thin** (3 tests). The package is a thin shim over `@rjsf/core` so most of the failure modes belong upstream. If the user wants stronger coverage, add a "renders rjsf form for nested-object schema" test before declaring v1. 5. **Tinykeys' broken type exports** required an ambient declaration in `packages/hotkeys/src/tinykeys.d.ts`. If upstream fixes its `package.json` `exports` field, remove the shim. diff --git a/docs/phase-3-reflection.md b/docs/phase-3-reflection.md index 5ae8ffb..721a082 100644 --- a/docs/phase-3-reflection.md +++ b/docs/phase-3-reflection.md @@ -1,6 +1,6 @@ # Phase 3 Reflection -**Authored:** 2026-05-13 by the Phase 3 implementing agent. All previous tests still pass; **185 package tests** (was 149 at end of Phase 2; +36 in `@acture/migration`). Plus **36 example tests** (was 10 at end of Phase 2; +5 in `zustand-wrap/before`, +21 in `zustand-wrap/after`). Every package and example typechecks and builds via tsup / vite. Production bundles: `before/` 198KB / 62KB gzipped, `after/` 352KB / 109KB gzipped (palette-react + cmdk dominate the delta). +**Authored:** 2026-05-13 by the Phase 3 implementing agent. All previous tests still pass; **185 package tests** (was 149 at end of Phase 2; +36 in `acture-migration`). Plus **36 example tests** (was 10 at end of Phase 2; +5 in `zustand-wrap/before`, +21 in `zustand-wrap/after`). Every package and example typechecks and builds via tsup / vite. Production bundles: `before/` 198KB / 62KB gzipped, `after/` 352KB / 109KB gzipped (palette-react + cmdk dominate the delta). This file answers the seven questions from `docs/implementation_plan.md` §"Phase 3 → Pre-next-phase reflection checklist." @@ -63,7 +63,7 @@ A future skill `migration-diagnose-graduate-readiness` could grep for remaining Ran `.claude/skills/acture-hard-donts/SKILL.md` against `packages/migration/`. 1. **No conditional logic in command metadata.** ✅ `wrapMutation` builds a `CommandRecord` with discrete fields; nothing decides at registration time based on runtime context. -2. **No god-package.** ✅ `@acture/migration` is single-purpose: strangler-fig adoption primitives. It does not import palette / hotkeys / MCP / AI. +2. **No god-package.** ✅ `acture-migration` is single-purpose: strangler-fig adoption primitives. It does not import palette / hotkeys / MCP / AI. 3. **No business logic in adapter packages.** ✅ The four functions translate between user-supplied handlers and acture's registry / errors-as-data contract. No domain logic. 4. **No `if (mode === ...)` in shared helpers.** ✅ `wrapMutation`'s execute branches on `options.params !== undefined` — that's data-shape branching, not mode branching. Same as the way the dispatcher branches on `cmd.params !== undefined`. 5. **No `eval()`-ing LLM strings.** ✅ The wrapped commands are dispatched through `registry.dispatch(id, params)` with Zod validation at the boundary. No reflective invocation, no string evaluation. @@ -110,14 +110,14 @@ Per `docs/next_session.md` Step 3: | Metric | Phase 2 end | Phase 3 end | Δ | | --- | --- | --- | --- | -| Packages | 9 | 10 | +1 (`@acture/migration`) | +| Packages | 9 | 10 | +1 (`acture-migration`) | | Worked examples | 2 | 3 | +1 (`zustand-wrap` with before/after) | | Tests (packages) | 149 | 185 | +36 | | Tests (examples) | 10 | 36 | +26 | | Public surface (named exports) | ~55 | ~65 | +10 | | Migration-track skills | 0 | 5 | +5 | -The package count includes `@acture/migration`. The worked-example count treats `zustand-wrap/before` and `zustand-wrap/after` as one example with two halves (they share a README and a purpose). Skills count includes `migration-diagnose`, `migration-plan`, `migration-scaffold`, `migration-wrap`, `migration-graduate`. +The package count includes `acture-migration`. The worked-example count treats `zustand-wrap/before` and `zustand-wrap/after` as one example with two halves (they share a README and a purpose). Skills count includes `migration-diagnose`, `migration-plan`, `migration-scaffold`, `migration-wrap`, `migration-graduate`. ## Phase 4 readiness gate diff --git a/docs/phase-4-reflection.md b/docs/phase-4-reflection.md index 244354a..6288aeb 100644 --- a/docs/phase-4-reflection.md +++ b/docs/phase-4-reflection.md @@ -18,7 +18,7 @@ This file answers the five questions from `docs/implementation_plan.md` §"Phase **The caveats I want to flag for v1.1:** -1. **AST-based mode for safety-critical orgs.** A regex transform CAN, in principle, misfire on heavily macro'd source. `transform.test.ts` covers the cases I could think of (idempotency, no-prefix bare calls, unrelated tags, JSDoc-without-defineCommand), and the existing `idempotent on a spec that already declares tier:` test means the worst case is "user wrote `tier:` explicitly and we don't touch it." A v1.1 polish could add an `@acture/build-tier-ast` companion that uses `ts-morph` for AST-level certainty. Not blocking. +1. **AST-based mode for safety-critical orgs.** A regex transform CAN, in principle, misfire on heavily macro'd source. `transform.test.ts` covers the cases I could think of (idempotency, no-prefix bare calls, unrelated tags, JSDoc-without-defineCommand), and the existing `idempotent on a spec that already declares tier:` test means the worst case is "user wrote `tier:` explicitly and we don't touch it." A v1.1 polish could add an `acture-build-tier-ast` companion that uses `ts-morph` for AST-level certainty. Not blocking. 2. **The 4000-char lookahead window in `indexOfMatchingBrace`.** I cap the per-call scan at 4000 chars to keep the transform fast. A spec with a 4001-char body would fall through the idempotency check. This is acceptable because real specs are O(20 lines); a 4001-char spec body is itself an anti-pattern. @@ -55,13 +55,13 @@ The schedule slip predicted in `docs/v1_plan.md` §7 ("Will `acture compare-sche Ran `.claude/skills/acture-hard-donts/SKILL.md` against all new Phase 4 packages. 1. **No conditional logic in command metadata.** ✅ The new fields (`deprecationReason`, `internalToken`) are pure data. `internalToken` is a Symbol; equality-checking is identity, not logic. -2. **No god-package.** ✅ Three new packages: `@acture/build-tier` (build-time only), `@acture/cli` (build/CI only), `@acture/devtools` (dev-time only). All single-purpose. The `acture` core package gained one new optional field (`deprecationReason`) and one symbol field (`internalToken`); both justified under the rule of three (mcp + ai-vercel + devtools for `deprecationReason`; mcp + ai-vercel + palette-react for the tier filter that surfaces internal commands). -3. **No business logic in adapter packages.** ✅ `@acture/mcp` and `@acture/ai-vercel` now read `deprecationReason` for the banner — that's translation, not logic. +2. **No god-package.** ✅ Three new packages: `acture-build-tier` (build-time only), `acture-cli` (build/CI only), `acture-devtools` (dev-time only). All single-purpose. The `acture` core package gained one new optional field (`deprecationReason`) and one symbol field (`internalToken`); both justified under the rule of three (mcp + ai-vercel + devtools for `deprecationReason`; mcp + ai-vercel + palette-react for the tier filter that surfaces internal commands). +3. **No business logic in adapter packages.** ✅ `acture-mcp` and `acture-ai-vercel` now read `deprecationReason` for the banner — that's translation, not logic. 4. **No `if (mode === ...)` in shared helpers.** ✅ The build-tier transform branches on tag value, not on mode. 5. **No `eval()`-ing LLM-produced strings.** ✅ The `Function`-constructor evaluation in `build-tier/end-to-end.test.ts` is on a TEST-OWNED string, not LLM-produced input. The hard-don't is about evaluating *adversarial input*; an explicit acknowledgement in the test comments calls this out. -6. **No coupling the registry to React.** ✅ Core has no new React import. `@acture/devtools` is the React adapter; `instrumentRegistry()` lives there. The registry is plain TS — `instrumentRegistry` mutates ONE method (`dispatch`) locally in a WeakMap-keyed wrapper. Dev-only mutation is the documented escape hatch. +6. **No coupling the registry to React.** ✅ Core has no new React import. `acture-devtools` is the React adapter; `instrumentRegistry()` lives there. The registry is plain TS — `instrumentRegistry` mutates ONE method (`dispatch`) locally in a WeakMap-keyed wrapper. Dev-only mutation is the documented escape hatch. 7. **No promoting `@experimental` to `@stable` without a migration story.** ✅ None of the new exports are `@experimental`. -8. **No bundling a UI kit.** ✅ `@acture/devtools` uses inline styles only — no MUI, no shadcn, no Tailwind. Theming hooks: `data-acture-devtools-*` attributes. +8. **No bundling a UI kit.** ✅ `acture-devtools` uses inline styles only — no MUI, no shadcn, no Tailwind. Theming hooks: `data-acture-devtools-*` attributes. 9. **No marketing on category.** ✅ READMEs were not changed in Phase 4; the existing wording leads with concrete wins ("One schema. Palette, hotkeys, AI tools, MCP, and tests — for free."). 10. **No assuming the LLM's chosen function is authorization.** ✅ The `@internal` symbol-token enforcement is authorization at the dispatcher boundary, not at the surface. Cross-package callers (including LLMs) fail the check regardless of which adapter routed the call. @@ -103,7 +103,7 @@ Per `docs/next_session.md` Step 3: | Metric | Phase 3 end | Phase 4 end | Δ | | --- | --- | --- | --- | -| Packages | 10 | 13 | +3 (`@acture/build-tier`, `@acture/cli`, `@acture/devtools`) | +| Packages | 10 | 13 | +3 (`acture-build-tier`, `acture-cli`, `acture-devtools`) | | Worked examples | 3 | 3 | 0 | | Tests (packages) | 185 | 270 | +85 | | Tests (examples) | 36 | 36 | 0 | diff --git a/docs/positioning.md b/docs/positioning.md new file mode 100644 index 0000000..8279f25 --- /dev/null +++ b/docs/positioning.md @@ -0,0 +1,88 @@ +# acture positioning — what acture is, and what it asks of you + +**Status:** canonical. This document governs how every other doc, README, and skill frames acture. If something contradicts this file, this file wins. (The one document above it is the conceptual paper, `command_dispatch_journal_article.md`, which this refines but does not replace.) + +Read this before writing or editing any user-facing text, before designing a new package, and before designing or editing a skill. + +--- + +## 1. What acture is + +**acture is a place developers (human or AI agent) go to get AI-agentic help building, migrating to, and maintaining a command-dispatch architecture** — the dispatch layer itself and the consumers that hang off it (palette, hotkeys, AI tools, MCP, e2e tests, macros, …). + +It is delivered as two things, and the order matters: + +1. **A body of skills, patterns, and codemods** — the *primary* surface. This is how an agent learns the command-dispatch architecture well enough to write it *into your project*, adapted to your stack, your constraints, and your preferences. +2. **A set of npm packages** — the *optional* surface. Tested, ready-made implementations of the pieces an agent would otherwise hand-write, so it doesn't have to. + +acture is a **development tool first**. The packages are an accelerator, not the product. + +## 2. The dev-tool-first principle + +> **A developer must be able to use acture purely as a development tool, with no acture dependency added to their project — unless they explicitly choose to add one.** + +This is the non-negotiable. An agent wielding acture's skills can stand up a complete command-dispatch architecture in a target project where `package.json` never gains a single `acture-*` line. The registry, the dispatcher, a palette, an MCP endpoint — all of it can be code the agent *wrote*, following acture's patterns, owned outright by the target project. + +Adding an acture package is always a *deliberate, opt-in trade*, never a default and never a side effect of using acture. + +This does **not** mean acture packages are bad or that depending on them is wrong. Code reuse is genuinely good — it spares the agent from rewriting near-identical code every time, and it means battle-tested behaviour instead of a fresh re-derivation. But reuse also has a cost: a dependency, a version to track, a surface you don't control. acture's job is to make that trade *visible and optional*, not to make it for you. + +## 3. The two flexibility dimensions + +Every acture engagement is positioned on two independent axes. Skills and docs must keep both open; never collapse one into a default. + +### Dimension 1 — how command dispatch enters the codebase + +- **Core approach** — command dispatch is a first-class architectural component, designed in (greenfield) or adopted as a deliberate core. The registry is the canonical path for operations. +- **Strangler-fig approach** — command dispatch is introduced incrementally into an existing codebase, wrapping existing handlers and graduating them over time. The legacy code keeps working throughout; acture's involvement shrinks as the migration completes. + +(These two subsume the conceptual paper's three "positioning paths": greenfield-pure and footprint-minimizer are both *core*; strangler-fig is *strangler-fig*.) + +### Dimension 2 — where the implementation comes from + +- **Agent-written** — the agent writes the integration code directly into the project, following acture's documented patterns. Maximum adaptability, minimum dependency. The project depends on nothing from acture (or, at most, on `acture` core if the team wants the registry primitive itself). +- **Package-reuse** — the agent installs an acture package that already implements the piece. Less code to own, faster, tested — at the cost of a dependency the team must accept and track. + +These are not either/or for a whole project. A team can hand-write the palette, reuse `acture-mcp`, and skip a state adapter entirely. The agent picks per-piece, guided by the team's preferences (including *which* third-party tool to build on — see §5). + +## 4. What the acture packages are, and what they are not + +The `acture-*` packages are **optional, independently-installable accelerators**. Each one is a thing an agent could have hand-written; acture ships it tested so it doesn't have to. + +Rules that keep the packages honest to this positioning: + +- **Every package is independently optional.** No package is load-bearing for "using acture". `acture` core itself is optional — a team can take the patterns and own every line. +- **No god-package.** (Hard-don't #2.) A package is a single accelerator, not a bundle you opt into wholesale. +- **The agent-written path is always viable and always documented.** Every package's README and every consumer skill must describe the hand-written alternative, not just the install command. +- **Packages translate; they don't decide.** (Hard-don't #3.) Business logic, command authoring, and architectural choices belong to the target project, never to an acture package. +- **`acture` core stays thin.** It is the registry + dispatcher + schema bridge and nothing else. Thinness is what makes "depend on only core, or on nothing" a real option. + +## 5. Two kinds of dependency — be precise about which is which + +When an agent builds a consumer integration, *some* dependency is usually unavoidable — but it is critical to be clear about **whose** dependency it is: + +- **A dependency on an `acture-*` package** — optional, opt-in, the subject of the dev-tool-first principle above. Avoidable by hand-writing. +- **A dependency on a *tool* library** — e.g. Playwright for e2e, cmdk for a palette, tinykeys for hotkeys, the Vercel AI SDK for tool calling. A consumer integration almost always rests on *some* such library. **This dependency belongs to the consumer code and is chosen by the team**, not imposed by acture. + +acture's per-tool packages (`acture-hotkeys` on tinykeys, `acture-palette-react` on cmdk, `acture-ai-vercel` on the Vercel AI SDK, a future `acture-e2e-playwright` on Playwright) each bundle *one* known-good integration with *one* specific tool. They exist so a team that has already chosen that tool gets a tested integration for free. They must never imply that tool is the only option — the consumer skill documents the choice and the hand-written path for other tools. + +## 6. The role of skills + +Skills are the primary delivery surface. They fall into three families: + +- **Dev skills** (`acture-*`) — for an agent working *on* the acture repository itself. Architecture, the CommandRecord shape, the schema bridge, the tier system, the hard-don'ts. +- **Migration-track skills** (`migration-*`) — the strangler-fig workflow, step by step (diagnose → plan → scaffold → wrap → graduate). +- **Consumer-integration skills** — for an agent building a consumer *in a target project*: how to add a palette, hotkeys, an MCP endpoint, e2e testing, etc. These are *consumer-specific* (one per surface) and *tool-aware* (they name the realistic tool choices and document both the hand-written and the package-reuse path). The foundational pattern lives in the `acture-consumer-integration` skill; per-surface skills build on it. + +**Standing rule for dev skills:** whenever a task touches a consumer surface or a consumer-specific package, the agent must also load `acture-consumer-integration`. The dev skills reference this rule so the positioning is enforced from inside the workflow, not just stated in docs. + +## 7. The test acture must keep passing + +Before merging anything user-facing, ask: + +1. Could a developer accomplish this *without* installing an acture package? If the honest answer is "no", the dev-tool-first principle is being violated — fix it. +2. Does this text/skill/README present *both* dimensions (core vs strangler-fig, agent-written vs package-reuse), or does it quietly assume one? +3. If this introduces a tool dependency, is it clearly framed as the *consumer's* choice, not acture's mandate? +4. Does `acture` core stay thin enough that "depend on core only, or on nothing" remains real? + +If any answer is wrong, the change has drifted from acture's positioning. Re-read this file. diff --git a/docs/redesign_takeaways.md b/docs/redesign_takeaways.md index f72315d..a22332d 100644 --- a/docs/redesign_takeaways.md +++ b/docs/redesign_takeaways.md @@ -127,7 +127,7 @@ These are choices where the references show real, defensible alternatives. Pick - **Option A — Depend on cmdk** [ref_18]. Mature, unstyled, composable slot API. Saves reinventing combobox semantics. - **Option B — Implement a cmdk-shaped store** in acture. More work; avoids the dependency; lets acture own the React-version compatibility story. -**Recommendation:** Option A for the default `` adapter, but keep the registry **fully decoupled from React** so non-React consumers (MCP server, LLM tools, tests) don't pay the cost. cmdk lives in `@acture/palette-react`, never in `@acture/core`. +**Recommendation:** Option A for the default `` adapter, but keep the registry **fully decoupled from React** so non-React consumers (MCP server, LLM tools, tests) don't pay the cost. cmdk lives in `acture-palette-react`, never in `acture-core`. ### 2.3 Param collector UI: AutoForm vs. rjsf vs. both @@ -136,13 +136,13 @@ These are choices where the references show real, defensible alternatives. Pick - **AutoForm** is Zod-native and lighter; fits the recommended Zod-first authoring path. - **rjsf** is JSON-Schema-native and more mature; fits the JSON-Schema-as-const authoring path. -**Recommendation:** ship **two thin adapters** (`@acture/forms-autoform`, `@acture/forms-rjsf`), each with a `paramCollector(schema)` API. The acture registry does not bundle a form library. This matches the `SchemaProvider` pattern AutoForm itself uses. +**Recommendation:** ship **two thin adapters** (`acture-forms-autoform`, `acture-forms-rjsf`), each with a `paramCollector(schema)` API. The acture registry does not bundle a form library. This matches the `SchemaProvider` pattern AutoForm itself uses. ### 2.4 Undo subsystem: in v1 or after? [ref_24] makes a strong case that "undo stops being a feature and starts being part of your architecture" — but [ref_27: YAGNI] warns against shipping speculative subsystems. -**Recommendation:** ship undo as a **separate package** (`@acture/undo`) that depends on `@acture/core` but is **not pulled in by default**. v1 of `@acture/core` should standardize the *hooks* needed to make undo possible later — specifically, `execute` may return `{result, patches?, effects?}` — so opting in is non-disruptive. Do not ship redo-after-edit (GURQ) until a real user needs it. +**Recommendation:** ship undo as a **separate package** (`acture-undo`) that depends on `acture-core` but is **not pulled in by default**. v1 of `acture-core` should standardize the *hooks* needed to make undo possible later — specifically, `execute` may return `{result, patches?, effects?}` — so opting in is non-disruptive. Do not ship redo-after-edit (GURQ) until a real user needs it. ### 2.5 Naming: settled — `acture` @@ -161,8 +161,8 @@ These are choices where the references show real, defensible alternatives. Pick These are anti-patterns the references collectively rule out. They should be enshrined in CONTRIBUTING and code review checklists. 1. **No conditional logic in command metadata.** [ref_13] If you're tempted to add `command.if`, stop. Refactor into two commands, or push the conditional into `execute`. -2. **No god-package.** [ref_14, ref_27] Ship `@acture/core` plus per-consumer adapter packages (`@acture/palette-react`, `@acture/mcp`, `@acture/ai-vercel`, `@acture/hotkeys`, `@acture/undo`, `@acture/forms-*`, `@acture/test-property`). Tree-shake-friendly. Mode-1 users grab one or two; greenfield users grab more. -3. **No business logic in adapter packages.** [ref_14] Adapters translate. If you find yourself adding behavior in `@acture/mcp`, it belongs in `@acture/core`. +2. **No god-package.** [ref_14, ref_27] Ship `acture-core` plus per-consumer adapter packages (`acture-palette-react`, `acture-mcp`, `acture-ai-vercel`, `acture-hotkeys`, `acture-undo`, `acture-forms-*`, `acture-test-property`). Tree-shake-friendly. Mode-1 users grab one or two; greenfield users grab more. +3. **No business logic in adapter packages.** [ref_14] Adapters translate. If you find yourself adding behavior in `acture-mcp`, it belongs in `acture-core`. 4. **No `if (mode === ...)` in shared helpers.** [ref_29] When a new caller almost-fits, prefer composition or a separate helper. Duplication is cheaper than the wrong abstraction. 5. **No `eval()`-ing LLM-produced JSON or argument strings.** [ref_04] The dispatcher takes a `(name, args)` pair, validates against the schema, and routes via the registry's `Map`. Never reflectively call. 6. **No coupling the registry to React.** [ref_19] kbar's `KBarProvider`-only access is the failure mode. The registry is a plain object; React adapters consume it. @@ -200,7 +200,7 @@ The references and the user's framing both push toward a clear branching of docs - `divertHandler(commandId, { legacy, modern, predicate })` — per-command routing between old and new implementations (the dispatcher's analog of nginx edge routing [ref_31]). - Event-interception middleware that lets some events transparently become command dispatches. -These are migration-track APIs. They live in `@acture/migration` (or a sub-export) and are *explicitly* off the greenfield path. +These are migration-track APIs. They live in `acture-migration` (or a sub-export) and are *explicitly* off the greenfield path. --- @@ -209,20 +209,20 @@ These are migration-track APIs. They live in `@acture/migration` (or a sub-expor Concretely, what to ship. (Subject to the YAGNI rule: only what's needed by a real consumer goes in v1.) ``` -@acture/core # Registry, dispatch, schemas, when-clause DSL, owner lifecycle. No React, no UI. -@acture/palette-react # cmdk-based default palette UI. Depends on core. -@acture/hotkeys # tinykeys binding. Plain DOM, optional React hook. -@acture/forms-autoform # Zod → form. Optional. -@acture/forms-rjsf # JSON Schema → form. Optional. -@acture/ai-vercel # Adapter to Vercel AI SDK tools. -@acture/mcp # Adapter to MCP TS SDK (server + client). Errors-as-data. -@acture/test-property # fast-check arbitraries derived from command schemas. -@acture/undo # Patch-based undo, transactions, effect queue. Opt-in. -@acture/migration # wrapMutation, divertHandler, event-interception. Mode-2 only. -@acture/devtools # Inspector: registry contents, dispatch log, when-clause evaluator. +acture-core # Registry, dispatch, schemas, when-clause DSL, owner lifecycle. No React, no UI. +acture-palette-react # cmdk-based default palette UI. Depends on core. +acture-hotkeys # tinykeys binding. Plain DOM, optional React hook. +acture-forms-autoform # Zod → form. Optional. +acture-forms-rjsf # JSON Schema → form. Optional. +acture-ai-vercel # Adapter to Vercel AI SDK tools. +acture-mcp # Adapter to MCP TS SDK (server + client). Errors-as-data. +acture-test-property # fast-check arbitraries derived from command schemas. +acture-undo # Patch-based undo, transactions, effect queue. Opt-in. +acture-migration # wrapMutation, divertHandler, event-interception. Mode-2 only. +acture-devtools # Inspector: registry contents, dispatch log, when-clause evaluator. ``` -`@acture/core` is the only required dependency. Mode-1 users typically take `core + palette-react + hotkeys`. Mode-3 users add `ai-vercel` + `mcp` + `test-property` from day one. Mode-2 users add `migration` until they delete it. +`acture-core` is the only required dependency. Mode-1 users typically take `core + palette-react + hotkeys`. Mode-3 users add `ai-vercel` + `mcp` + `test-property` from day one. Mode-2 users add `migration` until they delete it. --- @@ -273,7 +273,7 @@ These questions are worth a separate discussion thread or a deep-research pass ## 10. TL;DR for an agent picking up the redesign -1. **Ship a small, opinionated `@acture/core` plus tree-shakeable adapter packages.** No god-package. No bundled UI. +1. **Ship a small, opinionated `acture-core` plus tree-shakeable adapter packages.** No god-package. No bundled UI. 2. **The command record is closed and small.** Add capabilities via composition, not by growing the record. 3. **Schema bridge = JSON Schema.** Authoring layer can be Zod, JSON Schema literals, or generated from TS. Standard Schema at the boundary. 4. **Dispatch is the only entry point.** All surfaces go through it. Errors-as-data. Owner-scoped lifecycle. diff --git a/docs/reference_notes.md b/docs/reference_notes.md index d3b6a71..1bbc914 100644 --- a/docs/reference_notes.md +++ b/docs/reference_notes.md @@ -467,7 +467,7 @@ Notes are organized thematically (VS Code core; AI / MCP / plugins; migration & **Direct relevance to acture design:** - The most important article for acture's API surface. The library is at high risk of architecture-astronaut bloat: command palette + LLM tools + MCP + undo + macros + telemetry + extensions is exactly the "messaging" abstraction Joel warns about. - Concrete decisions: don't ship undo/redo, macros, telemetry middleware, or proposed-API channels in v1 unless a real user has them. Ship the registry + dispatcher + palette + zod-schema-driven LLM tool conversion. Everything else stays in docs until pulled. -- Reinforces mode 1 "minimize footprint": tree-shakeable subpackages (`@acture/llm`, `@acture/mcp`, `@acture/undo`) over a god-package. +- Reinforces mode 1 "minimize footprint": tree-shakeable subpackages (`acture-llm`, `acture-mcp`, `acture-undo`) over a god-package. **Quotable lines:** - "Any extensibility point that's never used isn't just wasted effort, it's likely to also get in your way as well." diff --git a/docs/research/acture_research_4 -- Transitional APIs and Codemod Tooling for Command-Dispatch Migration in TypeScript:React.md b/docs/research/acture_research_4 -- Transitional APIs and Codemod Tooling for Command-Dispatch Migration in TypeScript:React.md index 22eb514..859c25a 100644 --- a/docs/research/acture_research_4 -- Transitional APIs and Codemod Tooling for Command-Dispatch Migration in TypeScript:React.md +++ b/docs/research/acture_research_4 -- Transitional APIs and Codemod Tooling for Command-Dispatch Migration in TypeScript:React.md @@ -316,7 +316,7 @@ They report going from ~50 LoC/day per engineer to "hundreds of lines per day, s **Why three tools rather than one?** The cost of multiplexing is low (each codemod is a one-file package consumed via `npx`), and matching tool to job avoids forcing a complex jscodeshift transform when a 10-line ast-grep rule would suffice. -**Packaging shape (borrowed from Nx [19] and `types-react-codemod` [18]):** a single `npx @acture/codemods ` CLI, with a `migrations.json`-style manifest declaring which codemod runs on which acture version boundary. +**Packaging shape (borrowed from Nx [19] and `types-react-codemod` [18]):** a single `npx acture-codemods ` CLI, with a `migrations.json`-style manifest declaring which codemod runs on which acture version boundary. **Deliberately out of scope for v1.1:** any "graduation" codemod that removes `wrapMutation` calls. That requires a runtime telemetry signal acture won't have until users have lived with v1 for several months. Plan for v1.2. @@ -341,7 +341,7 @@ CLAUDE CODE: - Step E: add `actureMiddleware` to the store config 3. For each codemod step: - a. Invokes: npx @acture/codemods extract-onclick-to-command \ + a. Invokes: npx acture-codemods extract-onclick-to-command \ --target src/checkout/ --dry-run b. Reads the diff. If the transformation looks wrong on any file, marks that file as "needs AI rewrite" and skips the codemod for it. diff --git a/docs/roadmap.md b/docs/roadmap.md new file mode 100644 index 0000000..f11241c --- /dev/null +++ b/docs/roadmap.md @@ -0,0 +1,109 @@ +# acture roadmap & status tracker + +The live forward-planning surface. `docs/v1_plan.md` and `docs/implementation_plan.md` are the *historical* v1 plan (phases 0–4, all complete); this file is what's true now and what's next. + +**How work proceeds:** phases are over. Work is small, tracked increments. Each picks one or two items from "Next" or "Deferred", ships them, updates this file, and replaces `docs/next_session.md` with the following handoff. + +Last updated: **2026-05-14** (v1.5 — repositioning + namespace migration). + +--- + +## Status snapshot + +- **15 packages** in the workspace. `acture@1.1.0` and `eslint-plugin-acture-migration@1.0.0` are **published on npm**; the other 13 (renamed `@acture/*` → `acture-*` in v1.5) are built, tested, and **not yet published**. +- **396 package tests + 41 example tests** green; all packages and examples build + typecheck. +- Canonical positioning is now written down (`docs/positioning.md`) and wired into the skills. +- 14 skills: 8 dev (`acture-*`), 5 migration-track (`migration-*`), 1 consumer-integration foundation (`acture-consumer-integration`). + +--- + +## Done + +### Phases 0–4 (the original v1 plan) — complete +Core, state adapters, all consumer adapter packages, the migration package + migration skill track, the tier system, CLI, devtools. See `docs/phase-*-reflection.md`. + +### v1.1 – v1.4 increments — complete +DOM interception, RTK example, build-tier AST mode, deep schema diffs, the full research-4 §B.5 codemod set (5 codemods), `eslint-plugin-acture-migration` (`acture/no-stale-wrap-mutation`), and the deferred fresh-agent release-gate test. See `docs/v1_{1..4}-reflection.md` and `docs/fresh-agent-test-results.md`. + +### v1.5 — repositioning + namespace migration — complete (this increment) +- **`docs/positioning.md`** written — canonical: acture is a development tool first, packages are an optional accelerator, the two flexibility dimensions (core vs strangler-fig; agent-written vs package-reuse), and the dev-tool-first principle (zero `acture-*` dependency unless explicitly chosen). +- **`acture-consumer-integration` skill** created — the foundational pattern for building a consumer in a target project. Dev skills (`acture-architecture-primer`, `acture-hard-donts`, `acture-palette-design`) updated to load it whenever a task touches a consumer surface; `acture-hard-donts` gained a positioning check (merge-ritual item #6). +- **Namespace migration** — all 13 `@acture/*` packages renamed to unscoped `acture-*` (the `@acture` npm scope was unavailable; flat naming also fits the "optional à-la-carte tools" positioning better). All imports, workspace deps, configs, examples, docs, and skills updated; lockfile regenerated; full workspace re-validated. +- **READMEs** — root, `packages/core`, and all 14 sub-package READMEs carry the dev-tool-first framing. `AGENTS.md` updated. + +### npm publishing — partial +`acture@1.1.0` + `eslint-plugin-acture-migration@1.0.0` live. The `@acture` org could not be created (namespace taken); decision was to go unscoped — see v1.5 above. + +--- + +## In progress / immediate next + +**`acture` core positioning-alignment review** — `docs/next_session.md`. Audit `packages/core` against `docs/positioning.md`: (A) is core genuinely the minimal primitive, or has accelerator/adapter logic crept in? (B) is the agent-written path *reproducible*, not just asserted — could an agent hand-write an equivalent registry from the skills + core's source? Refactor only what the audit justifies. The `CommandRecord` stays closed at 15 fields. + +Also unblocked, owner-discretion: **publish the 13 renamed packages** to npm. + +--- + +## Next + +**Macros + e2e testing tooling.** These two consumer surfaces are structurally near-identical — a sequence (or DAG) of `{commandId, params}` pairs, with assertions in the e2e case (journal §3.4, §3.7: "an end-to-end test is a macro with assertions"). They were the least-tooled surfaces. Build them per the positioning: + +- **Core enables; packages are separate and optional; each gets a consumer skill.** The command-sequence *concept* (record / compose / replay) is something the agent can hand-write following a documented pattern. Specialized, tool-bound implementations are *separate optional packages*. +- **`acture-e2e-playwright`** — a separate package with reusable e2e code bound specifically to **Playwright**. (Playwright is the tool choice here; the consumer skill must still document the agent-written path and that other runners are valid choices — per `acture-consumer-integration`.) +- **Macros** — a record/replay tool. Decide during that session whether it ships as a package, a pattern + skill, or both — guided by the positioning and the rule of three. +- Each surface gets a **consumer-integration skill** (`acture-e2e`, `acture-macros`) building on `acture-consumer-integration`. + +Open design question to settle that session: macros and e2e share so much structure that a single command-sequence substrate underneath both may be the right shape — rather than two unrelated packages. Evaluate before committing. + +--- + +## Deferred / backlog + +Valid, not scheduled. Pick up when prioritized. + +- **Codemods README/CLI polish** — from the v1.4 fresh-agent test (`docs/fresh-agent-test-results.md`): the README's `npx acture-codemods` invocation story, undocumented per-codemod `--option` keys, undocumented `--manifest`/`--files-from`, and the ambiguous "No files matched" error. Full candidate list parked in `docs/backlog/codemods-polish-and-tier-mirror.md`. +- **`.d.ts` mirror of resolved tier values** — optional `acture-build-tier` polish. Parked in the same backlog file. +- **AI-codemod-recipe doc** — research-4 recommendation #8: a doc showing how to prompt an agent to author a one-off codemod. Parked in the same backlog file. +- **Per-surface consumer skills** — `acture-consumer-integration` is the foundation; per-surface skills exist only for the palette (`acture-palette-design`). Hotkeys, MCP, AI, e2e, macros, telemetry, undo, extensions still need consumer skills. (e2e + macros are covered by the "Next" item above; the rest are backlog.) +- **Greenfield agent-track skills** — the skill set is currently weighted toward migration and toward acture's internals. There is no greenfield "agent, help me build a command-dispatch app from scratch" track. Worth building once the consumer-skill family fills in. + +--- + +## Post-v1 (deferred, not committed) + +Per `docs/v1_plan.md` §"Post-v1" — none ship without explicit user direction **and** three concrete callers (rule of three): + +- **`acture-undo`** — patch-based undo, transactions, effect queue. `Result` already reserves `patches?` / `effects?`; `PatchCapableAdapter` is implemented by the state adapters. +- **`acture-telemetry`** — middleware logging every dispatch. +- **`acture-sandbox`** — membrane-pattern third-party extension sandboxing. +- **`acture-test-property`** — fast-check arbitraries derived from command param schemas; random command sequences asserting state invariants. (Note: overlaps the macros/e2e "Next" item — revisit scope when that lands.) +- **`acture-state-jotai`, `acture-state-valtio`** — additional reference `StateAdapter` implementations. +- **Python companion** (`acture` on PyPI) — research-6 was never executed. Re-run research-6 before considering it. + +--- + +## Tracking — open threads from recent discussion + +Explicit done/not-done for everything raised in conversation, so nothing is lost: + +| Thread | Status | +| --- | --- | +| `eslint-plugin-acture-migration` | ✅ Done (v1.4), published | +| Fresh-agent release-gate test | ✅ Done (v1.4) — `docs/fresh-agent-test-results.md` | +| Publish acture suite to npm | 🟡 Partial — 2 of 15 published; 13 renamed packages ready, pending | +| `@acture` npm org unavailable | ✅ Resolved — went unscoped `acture-*` (v1.5) | +| Canonical positioning written down | ✅ Done (v1.5) — `docs/positioning.md` | +| `acture-consumer-integration` skill + dev-skill wiring | ✅ Done (v1.5) | +| `@acture/*` → `acture-*` rename | ✅ Done (v1.5) | +| READMEs reflect dev-tool-first positioning | ✅ Done (v1.5) | +| `acture` core positioning-alignment review | ⏭️ Immediate next — `docs/next_session.md` | +| Macros tooling | ⏭️ Next | +| e2e testing tooling (`acture-e2e-playwright`) | ⏭️ Next | +| Codemods README/CLI polish | ⏸️ Deferred — backlog | +| `.d.ts` tier mirror | ⏸️ Deferred — backlog | +| AI-codemod-recipe doc | ⏸️ Deferred — backlog | +| Per-surface consumer skills (hotkeys/mcp/ai/telemetry/undo/extensions) | ⏸️ Deferred — backlog | +| Greenfield agent-track skills | ⏸️ Deferred — backlog | +| `acture-test-property`, `state-jotai`, `state-valtio` | 🔒 Post-v1 | +| `acture-undo`, `acture-telemetry`, `acture-sandbox` | 🔒 Post-v1 | +| Python companion | 🔒 Post-v1 (research-6 not run) | diff --git a/docs/v1_1-reflection.md b/docs/v1_1-reflection.md index 9ce5136..59dd437 100644 --- a/docs/v1_1-reflection.md +++ b/docs/v1_1-reflection.md @@ -22,7 +22,7 @@ Deliberately deferred to v1.2: - `EnableTierWarningsOptions` type — public export. - No CommandRecord shape changes. The `tier` field added in Phase 4 is the load-bearing piece; v1.1 just observes it at dispatch time. -### CLI (`@acture/cli`, bumped to 1.1.0) +### CLI (`acture-cli`, bumped to 1.1.0) - New subcommand `acture snapshot [--out ] [--tiers ]`. - Programmatic helper `runSnapshotCmd(args, io?)` and type `SnapshotCmdArgs` — for hosts that want to invoke the same logic without spawning a child process. @@ -48,7 +48,7 @@ Deliberately deferred to v1.2: Ran `.claude/skills/acture-hard-donts/SKILL.md` against the v1.1 increment. 1. **No conditional logic in command metadata.** ✅ Zero CommandRecord shape changes. -2. **No god-package.** ✅ `enableTierWarnings` lives in core (it observes the closed surface); the `snapshot` subcommand lives in the existing `@acture/cli` (no new package). +2. **No god-package.** ✅ `enableTierWarnings` lives in core (it observes the closed surface); the `snapshot` subcommand lives in the existing `acture-cli` (no new package). 3. **No business logic in adapter packages.** ✅ `enableTierWarnings` is a runtime helper; `snapshot` is a translation from registry → JSON. Neither makes domain decisions. 4. **No `if (mode === ...)` in shared helpers.** ✅ Both new pieces branch only on data (tier value, config shape). 5. **No `eval()`-ing LLM-produced strings.** ✅ `runSnapshotCmd` dynamically imports a user-supplied config — that's evaluating user-owned source, not adversarial input. The behavior matches what `tsx`, `ts-node`, `vite-node` already do for config files in every other ecosystem. @@ -76,7 +76,7 @@ The +3 exports are deliberately small. v1.1 was a polish increment — the struc ## Pre-v1.2 reflection answers (mirroring the Phase 4 checklist style) -1. **Did `enableTierWarnings` survive the design constraints?** Yes. Wrapping `dispatch` is the same idempotent-WeakMap technique `@acture/devtools` already used; the disposer pattern means tests can install and uninstall cleanly. The opt-in (host calls it once at boot) is the right ergonomics — research-5 §7.3 says first-dispatch warning, not "automatic on every registry." +1. **Did `enableTierWarnings` survive the design constraints?** Yes. Wrapping `dispatch` is the same idempotent-WeakMap technique `acture-devtools` already used; the disposer pattern means tests can install and uninstall cleanly. The opt-in (host calls it once at boot) is the right ergonomics — research-5 §7.3 says first-dispatch warning, not "automatic on every registry." 2. **Was the `acture snapshot` subcommand worth shipping in v1.1?** Yes — three-callers test passes trivially: anyone who uses `compare-schemas` needs to produce snapshots, and the programmatic `snapshotRegistry(registry)` helper was a partial answer. The CLI subcommand closes the CI integration story. **Cost was lower than predicted** — about half a session, not a full one. diff --git a/docs/v1_2-reflection.md b/docs/v1_2-reflection.md index 154dbe6..424ecf2 100644 --- a/docs/v1_2-reflection.md +++ b/docs/v1_2-reflection.md @@ -4,10 +4,10 @@ The v1.2 backlog from `docs/next_session.md` named five candidates. The user authorized all five (an explicit override of the "pick at most TWO" rule of three guideline, which was a planning safety net rather than a hard merge gate). Everything in scope shipped: -- **#1: `@acture/codemods` package** (research-4 §B.5). Two of five planned codemods + the manifest pattern + a CLI runner + 23 tests. The other three (`redux-action-to-command`, `usestate-mutation-to-command`, `rtk-thunk-to-command`) are tracked in `manifest.ts` and `migrations.json` as `status: 'planned'` so users can see what's coming. -- **#2: DOM-event interception middleware** (research-4 §A.5). `createDomInterceptor(registry, options)` lives in `@acture/migration` alongside the existing `actureMiddleware`. Plain TS (no React import — hard-don't #6), works in any framework, opt-in scoping per root. 14 tests using jsdom. +- **#1: `acture-codemods` package** (research-4 §B.5). Two of five planned codemods + the manifest pattern + a CLI runner + 23 tests. The other three (`redux-action-to-command`, `usestate-mutation-to-command`, `rtk-thunk-to-command`) are tracked in `manifest.ts` and `migrations.json` as `status: 'planned'` so users can see what's coming. +- **#2: DOM-event interception middleware** (research-4 §A.5). `createDomInterceptor(registry, options)` lives in `acture-migration` alongside the existing `actureMiddleware`. Plain TS (no React import — hard-don't #6), works in any framework, opt-in scoping per root. 14 tests using jsdom. - **#3: RTK worked example.** `examples/migration/redux-wrap/`. 5 integration tests demonstrating UI + palette paths converging on the same RTK store, observed as one stream via `actureMiddleware`. The example doubles as documentation for the slash-vs-dot id mapping (RTK action types vs. acture command ids). -- **#4: AST mode for `@acture/build-tier`.** Second entry point at `@acture/build-tier/ast` using ts-morph. Output is interchangeable with the regex mode on every case the regex handles; AST mode adds coverage for 5000-char spec bodies and template-literal `${...}` substitutions. ts-morph is an optional peer dep — only paid by users who import the AST entry point. +- **#4: AST mode for `acture-build-tier`.** Second entry point at `acture-build-tier/ast` using ts-morph. Output is interchangeable with the regex mode on every case the regex handles; AST mode adds coverage for 5000-char spec bodies and template-literal `${...}` substitutions. ts-morph is an optional peer dep — only paid by users who import the AST entry point. - **#5: Deep nested object diffs in `compare-schemas`.** The classifier now recurses through nested object `properties` and array `items`. Change paths read `inputSchema.properties.user.properties.email` instead of stopping at the top level. 8 new test cases. --- @@ -18,23 +18,23 @@ The v1.2 backlog from `docs/next_session.md` named five candidates. The user aut No changes. The `tier` field added in Phase 4 and the runtime helper added in v1.1 are unchanged. CommandRecord field count holds at 15. -### Migration (`@acture/migration`, bumped to 1.1.0) +### Migration (`acture-migration`, bumped to 1.1.0) - `createDomInterceptor(registry, options?)` — new public export. Delegated DOM listener at a root element / Document; routes `data-acture-command` events through `registry.dispatch`. Configurable events, attribute, params source, capture phase, preventDefault policy. - `DomInterceptorOptions`, `DomInterceptorMount` — public types. - jsdom added as a devDep. -### Build-tier (`@acture/build-tier`, bumped to 1.1.0) +### Build-tier (`acture-build-tier`, bumped to 1.1.0) -- New entry point: `@acture/build-tier/ast` exporting `transformSourceAst(source)`. Uses ts-morph (optional peer) to handle source the regex transform falls through on (large spec bodies, template-literal substitutions with braces). +- New entry point: `acture-build-tier/ast` exporting `transformSourceAst(source)`. Uses ts-morph (optional peer) to handle source the regex transform falls through on (large spec bodies, template-literal substitutions with braces). - README updated with the AST mode section. Caveats list updated to point users toward `/ast` when they trip the regex. -### CLI (`@acture/cli`, bumped to 1.2.0) +### CLI (`acture-cli`, bumped to 1.2.0) - `classifyChanges` recurses through nested object `properties` and array `items`. Change paths are fully qualified (`inputSchema.properties.user.properties.email`). Identical change-kind taxonomy — the recursion uses the same `diffSchemaProperty` logic at every depth. - 8 new test cases covering: nested removal, nested required addition, nested type narrowing, nested enum removal, type-swap *not* recursing (avoids double-counting), array-of-objects recursion, array-of-primitives type narrowing, three-deep nesting. -### Codemods (`@acture/codemods`, new at 1.0.0) +### Codemods (`acture-codemods`, new at 1.0.0) - New publishable package. Single `acture-codemods` CLI with bin, the `MANIFEST` registry, an Nx-style `migrations.json`, and two shipped codemods: - `wrap-handler-with-mutation` — wraps `onClick`/`onChange`/`onSubmit` handler expressions with `wrapMutation(...)`. Adds the import. Idempotent. Configurable via `--option events=...`, `--option import-from=...`, etc. @@ -62,7 +62,7 @@ No changes. The `tier` field added in Phase 4 and the runtime helper added in v1 Ran `.claude/skills/acture-hard-donts/SKILL.md` against the v1.2 increment. 1. **No conditional logic in command metadata.** ✅ Zero CommandRecord shape changes. -2. **No god-package.** ✅ One new package (`@acture/codemods`), single-purpose. DOM interception lives in `@acture/migration` next to `actureMiddleware` because both are event-interception primitives — not bundled into a "god migration" package; the existing migration package's charter is "transitional adoption primitives" and DOM interception fits cleanly. The build-tier AST mode is a second entry point in an existing package, not a new package. +2. **No god-package.** ✅ One new package (`acture-codemods`), single-purpose. DOM interception lives in `acture-migration` next to `actureMiddleware` because both are event-interception primitives — not bundled into a "god migration" package; the existing migration package's charter is "transitional adoption primitives" and DOM interception fits cleanly. The build-tier AST mode is a second entry point in an existing package, not a new package. 3. **No business logic in adapter packages.** ✅ All new code is translation: DOM events → command dispatch, JSX AST → wrapped JSX AST, source string → AST → source string with metadata injected. No domain decisions live in any new module. 4. **No `if (mode === ...)` in shared helpers.** ✅ Codemod options branch on user-supplied configuration (event names, import paths) — that's data, not mode. The build-tier AST mode is exposed as a different ENTRY POINT, not as a conditional inside the existing transform. 5. **No `eval()`-ing LLM-produced strings.** ✅ The codemod runner reads `--option key=value` pairs and JSON-encoded `data-acture-params` attributes; no eval, no `new Function`. The DOM interceptor parses params with `JSON.parse` (data-only) and looks up commands via `registry.has(id)` (not reflective dispatch). @@ -80,7 +80,7 @@ Ran `.claude/skills/acture-hard-donts/SKILL.md` against the v1.2 increment. | Metric | v1.1 end | v1.2 end | Δ | | --- | --- | --- | --- | -| Packages | 13 | 14 | +1 (`@acture/codemods`) | +| Packages | 13 | 14 | +1 (`acture-codemods`) | | Worked examples | 3 | 4 | +1 (`examples/migration/redux-wrap`) | | Tests (packages) | 288 | 350 | +62 (codemods: +23; migration: +14; build-tier: +17; cli: +8) | | Tests (examples) | 36 | 41 | +5 (redux-wrap integration) | diff --git a/docs/v1_3-reflection.md b/docs/v1_3-reflection.md index edd591d..a731b46 100644 --- a/docs/v1_3-reflection.md +++ b/docs/v1_3-reflection.md @@ -38,7 +38,7 @@ Each is a one-file addition that drops into the existing manifest. The CLI, runn Ran `.claude/skills/acture-hard-donts/SKILL.md` against the v1.3 increment. 1. **No conditional logic in command metadata.** ✅ Zero CommandRecord shape changes (still 15 fields). -2. **No god-package.** ✅ Zero new packages. Three new codemods inside the existing `@acture/codemods`. +2. **No god-package.** ✅ Zero new packages. Three new codemods inside the existing `acture-codemods`. 3. **No business logic in adapter packages.** ✅ Codemods are translations: AST in, AST out. No domain decisions. 4. **No `if (mode === ...)` in shared helpers.** ✅ All three codemods branch on data (action shape, setter pattern, thunk shape) and on user-supplied options. No mode awareness. 5. **No `eval()`-ing LLM-produced strings.** ✅ Same as v1.2 — codemods read `--option key=value`, never reflectively invoke. @@ -77,7 +77,7 @@ The +30 codemod tests bring the total package test count to 380. CI green across 3. **Is `--option` syntax enough, or do agents need a config-file fallback?** Probably enough for now. Across all five codemods, the longest `--option` chain we've documented is two flags. If a real migration drives users past that, a `--config ` flag is a one-line addition. -4. **What's the right release gate for v1.3?** Fresh-agent test. With all five codemods shipped, the `@acture/codemods` README is now the densest agent-facing surface in the repo. A fresh agent reading the README and running `acture-codemods --target ... --dry-run` is the natural release-readiness signal. The release gate moves into the v1.4 plan. +4. **What's the right release gate for v1.3?** Fresh-agent test. With all five codemods shipped, the `acture-codemods` README is now the densest agent-facing surface in the repo. A fresh agent reading the README and running `acture-codemods --target ... --dry-run` is the natural release-readiness signal. The release gate moves into the v1.4 plan. 5. **Hard-don'ts audit.** Clean. @@ -87,7 +87,7 @@ The +30 codemod tests bring the total package test count to 380. CI green across - ✅ All packages typecheck and build. - ✅ 380 package tests + 41 example tests green. -- ✅ `npm pack --dry-run` clean for `@acture/codemods@1.1.0`. +- ✅ `npm pack --dry-run` clean for `acture-codemods@1.1.0`. - ✅ Hard-don'ts audit clean. - 🟡 Fresh-agent test remains the recommended release gate for the next session. diff --git a/docs/v1_4-reflection.md b/docs/v1_4-reflection.md index b409967..7ddb9c3 100644 --- a/docs/v1_4-reflection.md +++ b/docs/v1_4-reflection.md @@ -12,7 +12,7 @@ One rule, `acture/no-stale-wrap-mutation`. It flags `wrapMutation(...)` calls wh Detection is single-file and conservative, by design (research-4's codemod principle: a high-confidence partial signal beats a noisy total one): -- Tracks `wrapMutation` imported — named or aliased — from `@acture/migration` (configurable via the `module` option). Namespace imports are not tracked. +- Tracks `wrapMutation` imported — named or aliased — from `acture-migration` (configurable via the `module` option). Namespace imports are not tracked. - Reports two shapes: a bare `ExpressionStatement`, and assignment to a non-exported local binding with zero references beyond its own initializer. - Stays silent on exported bindings, referenced bindings, returned results, and results passed as arguments — any of which may still be load-bearing. @@ -20,7 +20,7 @@ Detection is single-file and conservative, by design (research-4's codemod princ ### 2. Fresh-agent release-gate test — `docs/fresh-agent-test-results.md` -The Phase-4-reflection §5 gate, deferred through v1.0 → v1.3, finally ran. A fresh agent — no acture context — drove `@acture/codemods` from its README alone. Verdict: the **codemod engine + CLI passed** (the abstraction shape, the `--dry-run`/`--json` contract, `--list`/`--help`, the bad-codemod-name error all rated solid), but the **README did not** — its headline `npx @acture/codemods` invocation fails pre-publish, and per-codemod `--option` keys / `--manifest` / `--files-from` are undiscoverable from the docs. Per the next-session scope (#2 = no code change), the fixes are written up and carried to v1.5 as the top candidate. +The Phase-4-reflection §5 gate, deferred through v1.0 → v1.3, finally ran. A fresh agent — no acture context — drove `acture-codemods` from its README alone. Verdict: the **codemod engine + CLI passed** (the abstraction shape, the `--dry-run`/`--json` contract, `--list`/`--help`, the bad-codemod-name error all rated solid), but the **README did not** — its headline `npx acture-codemods` invocation fails pre-publish, and per-codemod `--option` keys / `--manifest` / `--files-from` are undiscoverable from the docs. Per the next-session scope (#2 = no code change), the fixes are written up and carried to v1.5 as the top candidate. ## What v1.4 did NOT ship @@ -36,7 +36,7 @@ Post-v1 items (`undo`, `macros`, `telemetry`, `sandbox`, `test-property`, `state Ran `.claude/skills/acture-hard-donts/SKILL.md` against the v1.4 increment. 1. **No conditional logic in command metadata.** ✅ Zero `CommandRecord` shape changes (still 15 fields). The ESLint rule reads source AST; it never touches the record shape. -2. **No god-package.** ✅ The ESLint plugin is its own package with one rule and zero runtime deps — the opposite of bloat. It does not re-export anything from `acture` or `@acture/migration`. +2. **No god-package.** ✅ The ESLint plugin is its own package with one rule and zero runtime deps — the opposite of bloat. It does not re-export anything from `acture` or `acture-migration`. 3. **No business logic in adapter packages.** ✅ The rule is a static analyzer: AST in, lint report out. It makes no domain decisions — it does not decide *whether* to graduate, only flags the structural signal. The cross-file verification and the actual rewrite stay with the human/agent (per the `migration-graduate` skill). 4. **No `if (mode === ...)` in shared helpers.** ✅ The rule branches on AST shape (bare statement vs. unused binding) and on the user-supplied `module` option. No positioning-path awareness. 5. **No `eval()`-ing LLM-produced strings.** ✅ N/A — a lint rule executes nothing. diff --git a/docs/v1_plan.md b/docs/v1_plan.md index 4147f94..6169288 100644 --- a/docs/v1_plan.md +++ b/docs/v1_plan.md @@ -19,7 +19,7 @@ These are now-locked commitments from the user, the takeaways doc, and the five | `when`-clauses: DSL or function? | **Both.** Small VS Code-style DSL primary; `(ctx) => boolean` allowed as escape hatch (flagged "not exposable to AI/MCP"). | User | | State management library | **Agnostic, with happy path.** Thin three-method `StateAdapter` interface (`getState` / `setState(updater)` / `subscribe(listener)`) plus optional `PatchCapableAdapter` sub-interface for the future undo subsystem. Ship **`acture/state-zustand`** in Phase 1 as the documented default. RTK adapter follows in Phase 2. | Research-3 + user | | Migration package | Real package (`acture/migration`), four functions: `wrapMutation`, `actureMiddleware`, `chooseImplementation`, `shadowCompare`. `divertHandler` from the original wrapex sketch is **dropped**. DOM-event interception deferred to v1.1. Codemods (`acture/codemods`) deferred to v1.1. | Research-4 + user | -| Undo subsystem | **Post-v1.** Reserve `execute` return shape (`Result` with optional `patches?` and `effects?`) so adding `@acture/undo` later is non-breaking. | User | +| Undo subsystem | **Post-v1.** Reserve `execute` return shape (`Result` with optional `patches?` and `effects?`) so adding `acture-undo` later is non-breaking. | User | | Schema as SSOT | JSON Schema as wire format; Zod is recommended authoring layer; Standard Schema accepted at boundary. Keep command param schemas in the JSON-Schema-representable subset (no `z.transform`, `z.date`, `z.bigint`, `z.set`, `z.map`, `z.custom` in params; validate at registration time). | Takeaways §1.3 + research-5 | | Single dispatch entry point | `dispatch(id, args, ctx?)` for all surfaces. Performance carve-out: render-frequency operations stay as direct function calls. | Takeaways §1.4 | | Owner-scoped lifecycle | **Disposable pattern.** Every `register*` returns a disposable. | Takeaways §1.5 | @@ -134,7 +134,7 @@ type Result = | { ok: false; error: { code: string; message: string; details?: unknown } }; ``` -`patches?` and `effects?` are **reserved hooks for `@acture/undo`** (post-v1). v1 core ignores them. They are present so adding undo later is non-breaking. +`patches?` and `effects?` are **reserved hooks for `acture-undo`** (post-v1). v1 core ignores them. They are present so adding undo later is non-breaking. **The metadata surface is closed.** No new fields without three real consumers asking. Compose new capabilities via wrapper functions (`palettable(cmd, ...)`, `toolCallable(cmd, ...)`). @@ -201,7 +201,7 @@ Scope: ### Phase 3 — Migration package and skills -**Status:** ✅ DONE — 2026-05-13. Reflection: [`docs/phase-3-reflection.md`](phase-3-reflection.md). `@acture/migration` ships with 36 unit tests; the `examples/migration/zustand-wrap/` before/after pair demonstrates the strangler-fig path end-to-end. +**Status:** ✅ DONE — 2026-05-13. Reflection: [`docs/phase-3-reflection.md`](phase-3-reflection.md). `acture-migration` ships with 36 unit tests; the `examples/migration/zustand-wrap/` before/after pair demonstrates the strangler-fig path end-to-end. **Goal:** Ship `acture/migration` plus the agent skills that use it. @@ -225,9 +225,9 @@ Scope: **Goal:** Move from "works" to "production-ready." Scope per research-5: -- API tier system: JSDoc tags + build-step metadata mirror (`@acture/build-tier`); runtime gating with `tiers: [...]` opt-in (already in core; deprecation banner reads `deprecationReason` from build-step mirror). -- `acture compare-schemas` CLI per research-5 §6: full-surface diff (descriptions MAJOR by default with `--allow-description-edits` per-invocation), tier-aware, JSON output for machines, colored text for humans. (`@acture/cli` package.) -- `@acture/devtools`: inspector UI for the registry, dispatch log, when-clause evaluator state. +- API tier system: JSDoc tags + build-step metadata mirror (`acture-build-tier`); runtime gating with `tiers: [...]` opt-in (already in core; deprecation banner reads `deprecationReason` from build-step mirror). +- `acture compare-schemas` CLI per research-5 §6: full-surface diff (descriptions MAJOR by default with `--allow-description-edits` per-invocation), tier-aware, JSON output for machines, colored text for humans. (`acture-cli` package.) +- `acture-devtools`: inspector UI for the registry, dispatch log, when-clause evaluator state. - Hardening: error messages, edge cases, JSDoc. - v1.0 release. diff --git a/examples/drop-in/README.md b/examples/drop-in/README.md index 7851959..37b79a6 100644 --- a/examples/drop-in/README.md +++ b/examples/drop-in/README.md @@ -4,7 +4,7 @@ Demonstrates the **drop-in / footprint-minimizer** positioning path: an existing ```bash pnpm install -pnpm --filter @acture/example-drop-in dev +pnpm --filter acture-example-drop-in dev # open http://localhost:5174 ``` @@ -26,15 +26,15 @@ Total new code: ~90 lines. Existing app components: zero changes. ## What you get - **Ctrl/Cmd+K palette** searches and dispatches across all 5 todo commands. -- **⌘⇧1** toggles the first todo; **⌘⇧C** clears done — both via `@acture/hotkeys` reading the same `keybinding` field the palette displays. -- **MCP server** (`pnpm --filter @acture/example-drop-in mcp`) exposes the same commands as MCP tools, so Claude Desktop / mcp-inspector can drive the app. +- **⌘⇧1** toggles the first todo; **⌘⇧C** clears done — both via `acture-hotkeys` reading the same `keybinding` field the palette displays. +- **MCP server** (`pnpm --filter acture-example-drop-in mcp`) exposes the same commands as MCP tools, so Claude Desktop / mcp-inspector can drive the app. - **Errors-as-data** at every surface: invalid params or unknown IDs return `{ ok: false, error: {...} }` instead of throwing. ## The wrap point ```ts // src/store.ts -import { wrapZustandStore } from '@acture/state-zustand'; +import { wrapZustandStore } from 'acture-state-zustand'; import { createExistingStore } from './existing-app.js'; export const store = createExistingStore(); @@ -49,4 +49,4 @@ The greenfield example ([`../greenfield/graph-editor`](../greenfield/graph-edito ## When you'd graduate -Once the palette + MCP coverage feels right, you can start *moving* the action implementations into the command `execute` bodies and deleting the legacy actions. That's the strangler-fig path and is the subject of `@acture/migration` (Phase 3). +Once the palette + MCP coverage feels right, you can start *moving* the action implementations into the command `execute` bodies and deleting the legacy actions. That's the strangler-fig path and is the subject of `acture-migration` (Phase 3). diff --git a/examples/drop-in/package.json b/examples/drop-in/package.json index 2e0ecb2..2750652 100644 --- a/examples/drop-in/package.json +++ b/examples/drop-in/package.json @@ -1,5 +1,5 @@ { - "name": "@acture/example-drop-in", + "name": "acture-example-drop-in", "version": "0.0.0", "private": true, "description": "Phase 2 worked example: an existing tiny app gets a palette + MCP bolt-on in 5 minutes. Demonstrates the drop-in / footprint-minimizer path.", @@ -14,10 +14,10 @@ "mcp": "tsx scripts/mcp-server.ts" }, "dependencies": { - "@acture/hotkeys": "workspace:*", - "@acture/mcp": "workspace:*", - "@acture/palette-react": "workspace:*", - "@acture/state-zustand": "workspace:*", + "acture-hotkeys": "workspace:*", + "acture-mcp": "workspace:*", + "acture-palette-react": "workspace:*", + "acture-state-zustand": "workspace:*", "@modelcontextprotocol/sdk": "^1.0.4", "acture": "workspace:*", "cmdk": "^1.0.0", diff --git a/examples/drop-in/scripts/mcp-server.ts b/examples/drop-in/scripts/mcp-server.ts index 522ee68..ba8f9ad 100644 --- a/examples/drop-in/scripts/mcp-server.ts +++ b/examples/drop-in/scripts/mcp-server.ts @@ -7,7 +7,7 @@ * palette also serves MCP — no separate "AI-tools" definitions. */ -import { createMcpServer, connectStdio } from '@acture/mcp'; +import { createMcpServer, connectStdio } from 'acture-mcp'; import { registry } from '../src/registry.js'; const server = createMcpServer(registry, { diff --git a/examples/drop-in/src/App.tsx b/examples/drop-in/src/App.tsx index d9759d0..6ee25a9 100644 --- a/examples/drop-in/src/App.tsx +++ b/examples/drop-in/src/App.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react'; -import { CommandPalette } from '@acture/palette-react'; -import { useHotkeys } from '@acture/hotkeys/react'; +import { CommandPalette } from 'acture-palette-react'; +import { useHotkeys } from 'acture-hotkeys/react'; import { registry } from './registry.js'; import { store } from './store.js'; import { useTodoState } from './use-store.js'; diff --git a/examples/drop-in/src/store.ts b/examples/drop-in/src/store.ts index 592a414..7591659 100644 --- a/examples/drop-in/src/store.ts +++ b/examples/drop-in/src/store.ts @@ -8,7 +8,7 @@ * source of truth. */ -import { wrapZustandStore } from '@acture/state-zustand'; +import { wrapZustandStore } from 'acture-state-zustand'; import { createExistingStore } from './existing-app.js'; import type { TodoState } from './existing-app.js'; diff --git a/examples/greenfield/graph-editor/README.md b/examples/greenfield/graph-editor/README.md index 3d25e4c..b0b0c98 100644 --- a/examples/greenfield/graph-editor/README.md +++ b/examples/greenfield/graph-editor/README.md @@ -4,7 +4,7 @@ A tiny SVG graph editor whose every state mutation flows through `registry.dispa ```bash pnpm install -pnpm --filter @acture/example-graph-editor dev +pnpm --filter acture-example-graph-editor dev # open http://localhost:5173 ``` @@ -12,11 +12,11 @@ pnpm --filter @acture/example-graph-editor dev | Surface | Adapter | What it does | | --- | --- | --- | -| Ctrl/Cmd+K palette | `@acture/palette-react` + `@acture/forms-autoform` | List, atomic picker chain, handoff form | -| Keyboard shortcuts | `@acture/hotkeys` | Reads each command's `keybinding`, dispatches through the registry | -| MCP server (Node) | `@acture/mcp` | `pnpm mcp` exposes the graph as MCP tools | -| Vercel AI SDK | `@acture/ai-vercel` | `pnpm ai-demo` lets Claude compose multi-step actions | -| State substrate | `@acture/state-zustand` | zustand+immer with patch capture | +| Ctrl/Cmd+K palette | `acture-palette-react` + `acture-forms-autoform` | List, atomic picker chain, handoff form | +| Keyboard shortcuts | `acture-hotkeys` | Reads each command's `keybinding`, dispatches through the registry | +| MCP server (Node) | `acture-mcp` | `pnpm mcp` exposes the graph as MCP tools | +| Vercel AI SDK | `acture-ai-vercel` | `pnpm ai-demo` lets Claude compose multi-step actions | +| State substrate | `acture-state-zustand` | zustand+immer with patch capture | ## Commands @@ -83,14 +83,14 @@ defineCommand({ ### 4. The `getState()` vs `setStateWithPatches` split - **Read** with `state.getState()` — returns the latest immutable snapshot. -- **Mutate** with `state.setStateWithPatches(recipe)` — runs the recipe on an Immer draft and returns the patches for future `@acture/undo`. (`state.setState(updater)` works too, without the patch capture.) +- **Mutate** with `state.setStateWithPatches(recipe)` — runs the recipe on an Immer draft and returns the patches for future `acture-undo`. (`state.setState(updater)` works too, without the patch capture.) The Phase 1 reflection (§4 #4) noted this wasn't called out — fixed here. ## MCP server demo ```bash -pnpm --filter @acture/example-graph-editor mcp +pnpm --filter acture-example-graph-editor mcp # in another shell: npx @modelcontextprotocol/inspector node \ examples/greenfield/graph-editor/scripts/mcp-server.ts @@ -102,7 +102,7 @@ The graph state lives in the Node process that runs the MCP script — it does N ```bash export ANTHROPIC_API_KEY=... -pnpm --filter @acture/example-graph-editor ai-demo +pnpm --filter acture-example-graph-editor ai-demo ``` Claude composes a multi-step "build a triangle of A, B, C nodes" plan through the registry. diff --git a/examples/greenfield/graph-editor/package.json b/examples/greenfield/graph-editor/package.json index f9109bd..2754915 100644 --- a/examples/greenfield/graph-editor/package.json +++ b/examples/greenfield/graph-editor/package.json @@ -1,5 +1,5 @@ { - "name": "@acture/example-graph-editor", + "name": "acture-example-graph-editor", "version": "0.0.0", "private": true, "description": "Phase 1 worked example: a tiny graph editor whose mutations all go through registry.dispatch.", @@ -15,13 +15,13 @@ "ai-demo": "tsx scripts/ai-demo.ts" }, "dependencies": { - "@acture/ai-vercel": "workspace:*", - "@acture/devtools": "workspace:*", - "@acture/forms-autoform": "workspace:*", - "@acture/hotkeys": "workspace:*", - "@acture/mcp": "workspace:*", - "@acture/palette-react": "workspace:*", - "@acture/state-zustand": "workspace:*", + "acture-ai-vercel": "workspace:*", + "acture-devtools": "workspace:*", + "acture-forms-autoform": "workspace:*", + "acture-hotkeys": "workspace:*", + "acture-mcp": "workspace:*", + "acture-palette-react": "workspace:*", + "acture-state-zustand": "workspace:*", "@modelcontextprotocol/sdk": "^1.0.4", "acture": "workspace:*", "ai": "^4.0.40", diff --git a/examples/greenfield/graph-editor/scripts/ai-demo.ts b/examples/greenfield/graph-editor/scripts/ai-demo.ts index 2f870a6..547a680 100644 --- a/examples/greenfield/graph-editor/scripts/ai-demo.ts +++ b/examples/greenfield/graph-editor/scripts/ai-demo.ts @@ -2,14 +2,14 @@ /** * Vercel AI SDK demo for the graph-editor example. Sets up the registry * over an in-process zustand store, builds a tool map via - * `@acture/ai-vercel`, and asks an Anthropic model to compose a + * `acture-ai-vercel`, and asks an Anthropic model to compose a * multi-step graph mutation. * * Requires `ANTHROPIC_API_KEY` in the environment. * * Run via: * - * pnpm --filter @acture/example-graph-editor ai-demo + * pnpm --filter acture-example-graph-editor ai-demo * * Expected behavior: the model issues 3 `app.graph.addNode` calls * (labels A, B, C) followed by 3 `app.selection.set` + `app.graph.connectNodes` @@ -19,8 +19,8 @@ import { generateText } from 'ai'; import { createAnthropic } from '@ai-sdk/anthropic'; import { createRegistry } from 'acture'; -import { createZustandAdapter } from '@acture/state-zustand'; -import { toAITools } from '@acture/ai-vercel'; +import { createZustandAdapter } from 'acture-state-zustand'; +import { toAITools } from 'acture-ai-vercel'; import { buildCommands } from '../src/commands/index.js'; import { initialGraphState, type GraphState } from '../src/state.js'; diff --git a/examples/greenfield/graph-editor/scripts/mcp-server.ts b/examples/greenfield/graph-editor/scripts/mcp-server.ts index aa7ebcc..3e2db01 100644 --- a/examples/greenfield/graph-editor/scripts/mcp-server.ts +++ b/examples/greenfield/graph-editor/scripts/mcp-server.ts @@ -3,7 +3,7 @@ * MCP server entry point for the graph-editor example. Exposes every * stable graph-editor command as an MCP tool. Run via: * - * pnpm --filter @acture/example-graph-editor mcp + * pnpm --filter acture-example-graph-editor mcp * * Inspect with: * @@ -18,9 +18,9 @@ * to a single source of truth via your existing API layer. */ -import { createZustandAdapter } from '@acture/state-zustand'; +import { createZustandAdapter } from 'acture-state-zustand'; import { createRegistry } from 'acture'; -import { createMcpServer, connectStdio } from '@acture/mcp'; +import { createMcpServer, connectStdio } from 'acture-mcp'; import { buildCommands } from '../src/commands/index.js'; import { initialGraphState, type GraphState } from '../src/state.js'; diff --git a/examples/greenfield/graph-editor/src/App.tsx b/examples/greenfield/graph-editor/src/App.tsx index e5b8bf9..4849a41 100644 --- a/examples/greenfield/graph-editor/src/App.tsx +++ b/examples/greenfield/graph-editor/src/App.tsx @@ -1,6 +1,6 @@ import { useMemo, useState } from 'react'; -import { useHotkeys } from '@acture/hotkeys/react'; -import { Inspector, instrumentRegistry } from '@acture/devtools'; +import { useHotkeys } from 'acture-hotkeys/react'; +import { Inspector, instrumentRegistry } from 'acture-devtools'; import { Canvas } from './Canvas.js'; import { PaletteOverlay } from './Palette.js'; import { clearSelection } from './select-node.js'; @@ -26,7 +26,7 @@ export function App(): React.ReactElement { ); // Bind every command's keybinding via the registry. First-registered- - // wins under matching context (per @acture/hotkeys spec). + // wins under matching context (per acture-hotkeys spec). useHotkeys(registry, { context: hotkeyContext }); return ( diff --git a/examples/greenfield/graph-editor/src/Palette.tsx b/examples/greenfield/graph-editor/src/Palette.tsx index ea1e2ab..feffaed 100644 --- a/examples/greenfield/graph-editor/src/Palette.tsx +++ b/examples/greenfield/graph-editor/src/Palette.tsx @@ -2,9 +2,9 @@ * Ctrl/Cmd+K palette overlay. Phase 2 wiring: * - Opens on Ctrl/Cmd+K (handled here so the palette stays a single * component; in a real app the keybinding could itself flow through - * `@acture/hotkeys`). + * `acture-hotkeys`). * - Closes on Esc / outside-click / successful dispatch. - * - Renders @acture/forms-autoform inline for `kind: 'handoff'` + * - Renders acture-forms-autoform inline for `kind: 'handoff'` * commands (e.g. `app.graph.addNode({x, y, label})`). * - Atomic parameterized commands (none in the worked example today, * but the picker chain is wired and ready) render the picker chain @@ -12,8 +12,8 @@ */ import { useEffect, useState, useMemo } from 'react'; -import { CommandPalette } from '@acture/palette-react'; -import { AutoForm } from '@acture/forms-autoform'; +import { CommandPalette } from 'acture-palette-react'; +import { AutoForm } from 'acture-forms-autoform'; import { registry } from './registry.js'; import { useGraphState } from './use-state.js'; import type { Context } from 'acture'; diff --git a/examples/greenfield/graph-editor/src/state.ts b/examples/greenfield/graph-editor/src/state.ts index 6e930a1..8c96581 100644 --- a/examples/greenfield/graph-editor/src/state.ts +++ b/examples/greenfield/graph-editor/src/state.ts @@ -5,7 +5,7 @@ * That rules out Sets, Maps, Date, etc. in the state. */ -import { createZustandAdapter } from '@acture/state-zustand'; +import { createZustandAdapter } from 'acture-state-zustand'; export interface NodeRecord { id: string; diff --git a/examples/migration/redux-wrap/README.md b/examples/migration/redux-wrap/README.md index 92b08c9..388d916 100644 --- a/examples/migration/redux-wrap/README.md +++ b/examples/migration/redux-wrap/README.md @@ -7,7 +7,7 @@ duplicating state. ## What this exercises -`actureMiddleware` from `@acture/migration` is the store-event half of +`actureMiddleware` from `acture-migration` is the store-event half of the strangler-fig pattern (research-4 §A.5). It does NOT replace `store.dispatch` — it observes. RTK's auto-generated action types (`cart/addItem`, `cart/removeItem`, …) are slash-separated, while @@ -40,7 +40,7 @@ palette-driven dispatches as one stream. ## Wiring ```ts -import { actureMiddleware } from '@acture/migration'; +import { actureMiddleware } from 'acture-migration'; import { createCartRegistry, registerCartCommands } from './acture/registry.js'; import { createCartStore } from './store.js'; diff --git a/examples/migration/redux-wrap/package.json b/examples/migration/redux-wrap/package.json index 7a7ac6b..5b1713f 100644 --- a/examples/migration/redux-wrap/package.json +++ b/examples/migration/redux-wrap/package.json @@ -1,5 +1,5 @@ { - "name": "@acture/example-redux-wrap", + "name": "acture-example-redux-wrap", "version": "0.0.0", "private": true, "description": "Migration example: a Redux Toolkit (RTK) app with actureMiddleware wired in. Store actions flow through the registry's observer bus without re-dispatching.", @@ -10,8 +10,8 @@ "clean": "rm -rf dist" }, "dependencies": { - "@acture/migration": "workspace:*", - "@acture/state-redux": "workspace:*", + "acture-migration": "workspace:*", + "acture-state-redux": "workspace:*", "@reduxjs/toolkit": "^2.5.0", "acture": "workspace:*", "zod": "^4.0.0" diff --git a/examples/migration/redux-wrap/src/index.ts b/examples/migration/redux-wrap/src/index.ts index 86e4496..5ad4979 100644 --- a/examples/migration/redux-wrap/src/index.ts +++ b/examples/migration/redux-wrap/src/index.ts @@ -11,7 +11,7 @@ * trails, devtools) sees the events as a single stream. */ -import { actureMiddleware } from '@acture/migration'; +import { actureMiddleware } from 'acture-migration'; import { createCartStore } from './store.js'; import { createCartRegistry, registerCartCommands } from './acture/registry.js'; diff --git a/examples/migration/zustand-wrap/after/acture-output/plan.md b/examples/migration/zustand-wrap/after/acture-output/plan.md index b11b0bc..6c1e992 100644 --- a/examples/migration/zustand-wrap/after/acture-output/plan.md +++ b/examples/migration/zustand-wrap/after/acture-output/plan.md @@ -8,7 +8,7 @@ | --- | --- | --- | --- | | Id prefix | `app` | `app` | Standard convention. | | Categories | `Notes`, `Settings` | `Notes`, `Settings` | Two natural domains. | -| State adapter | `@acture/state-zustand` | `@acture/state-zustand` | Host uses zustand vanilla. | +| State adapter | `acture-state-zustand` | `acture-state-zustand` | Host uses zustand vanilla. | | Phase A scope | moderate (8–12) | moderate | All 8 candidates fit in Phase A; the app is small. | | Surfaces in scope | palette only | palette only | MCP/AI deferred to a follow-on session. | diff --git a/examples/migration/zustand-wrap/after/package.json b/examples/migration/zustand-wrap/after/package.json index b8cbbe7..bed598f 100644 --- a/examples/migration/zustand-wrap/after/package.json +++ b/examples/migration/zustand-wrap/after/package.json @@ -1,5 +1,5 @@ { - "name": "@acture/example-zustand-wrap-after", + "name": "acture-example-zustand-wrap-after", "version": "0.0.0", "private": true, "description": "Migration example: the 'before' notes app with acture bolted on via the migration skills. Five wrapped commands + a palette + two graduated.", @@ -13,10 +13,10 @@ "clean": "rm -rf dist" }, "dependencies": { - "@acture/hotkeys": "workspace:*", - "@acture/migration": "workspace:*", - "@acture/palette-react": "workspace:*", - "@acture/state-zustand": "workspace:*", + "acture-hotkeys": "workspace:*", + "acture-migration": "workspace:*", + "acture-palette-react": "workspace:*", + "acture-state-zustand": "workspace:*", "acture": "workspace:*", "cmdk": "^1.0.0", "immer": "^11.0.0", diff --git a/examples/migration/zustand-wrap/after/src/App.tsx b/examples/migration/zustand-wrap/after/src/App.tsx index d679e7b..6fd9af8 100644 --- a/examples/migration/zustand-wrap/after/src/App.tsx +++ b/examples/migration/zustand-wrap/after/src/App.tsx @@ -10,7 +10,7 @@ */ import { useEffect, useState } from 'react'; -import { CommandPalette } from '@acture/palette-react'; +import { CommandPalette } from 'acture-palette-react'; import { store, useNotesStore, type Theme } from './store.js'; import { registry } from './acture/registry.js'; import './acture/index.js'; // side-effect: registers commands @@ -129,7 +129,7 @@ export function App(): React.ReactElement {

Notes — after acture

Same notes app. ⌘K opens the palette. Five commands - are wrapped via `@acture/migration`; two are graduated to + are wrapped via `acture-migration`; two are graduated to direct `defineCommand` calls.

diff --git a/examples/migration/zustand-wrap/after/src/acture/commands/notes/addNote.ts b/examples/migration/zustand-wrap/after/src/acture/commands/notes/addNote.ts index ccf7ba7..b84d10d 100644 --- a/examples/migration/zustand-wrap/after/src/acture/commands/notes/addNote.ts +++ b/examples/migration/zustand-wrap/after/src/acture/commands/notes/addNote.ts @@ -10,7 +10,7 @@ */ import { z } from 'zod'; -import { wrapMutation } from '@acture/migration'; +import { wrapMutation } from 'acture-migration'; import { registry } from '../../registry.js'; import { store } from '../../../store.js'; diff --git a/examples/migration/zustand-wrap/after/src/acture/commands/notes/removeNote.ts b/examples/migration/zustand-wrap/after/src/acture/commands/notes/removeNote.ts index 1722bcd..bac1287 100644 --- a/examples/migration/zustand-wrap/after/src/acture/commands/notes/removeNote.ts +++ b/examples/migration/zustand-wrap/after/src/acture/commands/notes/removeNote.ts @@ -3,7 +3,7 @@ */ import { z } from 'zod'; -import { wrapMutation } from '@acture/migration'; +import { wrapMutation } from 'acture-migration'; import { registry } from '../../registry.js'; import { store } from '../../../store.js'; diff --git a/examples/migration/zustand-wrap/after/src/acture/commands/notes/setDueDate.ts b/examples/migration/zustand-wrap/after/src/acture/commands/notes/setDueDate.ts index 354cc4e..c0d14ec 100644 --- a/examples/migration/zustand-wrap/after/src/acture/commands/notes/setDueDate.ts +++ b/examples/migration/zustand-wrap/after/src/acture/commands/notes/setDueDate.ts @@ -7,7 +7,7 @@ */ import { z } from 'zod'; -import { wrapMutation } from '@acture/migration'; +import { wrapMutation } from 'acture-migration'; import { registry } from '../../registry.js'; import { store } from '../../../store.js'; diff --git a/examples/migration/zustand-wrap/after/src/acture/commands/notes/toggleDone.ts b/examples/migration/zustand-wrap/after/src/acture/commands/notes/toggleDone.ts index 134b064..d707fc4 100644 --- a/examples/migration/zustand-wrap/after/src/acture/commands/notes/toggleDone.ts +++ b/examples/migration/zustand-wrap/after/src/acture/commands/notes/toggleDone.ts @@ -7,7 +7,7 @@ */ import { z } from 'zod'; -import { wrapMutation } from '@acture/migration'; +import { wrapMutation } from 'acture-migration'; import { registry } from '../../registry.js'; import { store } from '../../../store.js'; diff --git a/examples/migration/zustand-wrap/after/src/acture/commands/settings/setFontSize.ts b/examples/migration/zustand-wrap/after/src/acture/commands/settings/setFontSize.ts index a0e9e1f..5667711 100644 --- a/examples/migration/zustand-wrap/after/src/acture/commands/settings/setFontSize.ts +++ b/examples/migration/zustand-wrap/after/src/acture/commands/settings/setFontSize.ts @@ -8,7 +8,7 @@ */ import { z } from 'zod'; -import { wrapMutation } from '@acture/migration'; +import { wrapMutation } from 'acture-migration'; import { registry } from '../../registry.js'; import { store } from '../../../store.js'; diff --git a/examples/migration/zustand-wrap/after/src/acture/commands/settings/setTheme.ts b/examples/migration/zustand-wrap/after/src/acture/commands/settings/setTheme.ts index 5bc3913..f0530a6 100644 --- a/examples/migration/zustand-wrap/after/src/acture/commands/settings/setTheme.ts +++ b/examples/migration/zustand-wrap/after/src/acture/commands/settings/setTheme.ts @@ -6,7 +6,7 @@ */ import { z } from 'zod'; -import { wrapMutation } from '@acture/migration'; +import { wrapMutation } from 'acture-migration'; import { registry } from '../../registry.js'; import { store } from '../../../store.js'; import type { Theme } from '../../../store.js'; diff --git a/examples/migration/zustand-wrap/after/src/acture/state.ts b/examples/migration/zustand-wrap/after/src/acture/state.ts index 3a25a1b..cbe447f 100644 --- a/examples/migration/zustand-wrap/after/src/acture/state.ts +++ b/examples/migration/zustand-wrap/after/src/acture/state.ts @@ -3,7 +3,7 @@ * This is the SAME store the legacy UI reads/writes — no parallel state. */ -import { wrapZustandStore } from '@acture/state-zustand'; +import { wrapZustandStore } from 'acture-state-zustand'; import { store } from '../store.js'; export const state = wrapZustandStore(store); diff --git a/examples/migration/zustand-wrap/after/src/kind-heuristic.test.ts b/examples/migration/zustand-wrap/after/src/kind-heuristic.test.ts index 2166abc..28e86a7 100644 --- a/examples/migration/zustand-wrap/after/src/kind-heuristic.test.ts +++ b/examples/migration/zustand-wrap/after/src/kind-heuristic.test.ts @@ -1,6 +1,6 @@ /** * Stress test for `deriveKind` (the auto-derived atomic-vs-handoff - * heuristic in `@acture/palette-react`). + * heuristic in `acture-palette-react`). * * Per `next_session.md` Step 3 acceptance criteria §5: at least 5 * parameterized commands of varying shapes; if the override rate @@ -18,7 +18,7 @@ import { describe, it, expect } from 'vitest'; import { z } from 'zod'; import { defineCommand, ok } from 'acture'; -import { deriveKind } from '@acture/palette-react'; +import { deriveKind } from 'acture-palette-react'; import { registry } from './acture/registry.js'; import './acture/index.js'; diff --git a/examples/migration/zustand-wrap/before/package.json b/examples/migration/zustand-wrap/before/package.json index 8e270a7..77f3129 100644 --- a/examples/migration/zustand-wrap/before/package.json +++ b/examples/migration/zustand-wrap/before/package.json @@ -1,5 +1,5 @@ { - "name": "@acture/example-zustand-wrap-before", + "name": "acture-example-zustand-wrap-before", "version": "0.0.0", "private": true, "description": "Migration example: a tiny notes app on zustand with no acture imports anywhere. The 'before' half of the strangler-fig demo.", diff --git a/examples/migration/zustand-wrap/before/src/App.tsx b/examples/migration/zustand-wrap/before/src/App.tsx index 2a61f62..027a27b 100644 --- a/examples/migration/zustand-wrap/before/src/App.tsx +++ b/examples/migration/zustand-wrap/before/src/App.tsx @@ -2,7 +2,7 @@ * The host UI. Renders notes, has a small form to add one, and a tiny * "settings" panel. Every mutation calls a store action directly. * - * No imports from `acture` or `@acture/*` anywhere in this file (or in + * No imports from `acture` or `acture-*` anywhere in this file (or in * the whole `before/` app). This is the starting state of a strangler- * fig migration. */ diff --git a/examples/migration/zustand-wrap/before/src/store.ts b/examples/migration/zustand-wrap/before/src/store.ts index 2d82f13..f18d5a6 100644 --- a/examples/migration/zustand-wrap/before/src/store.ts +++ b/examples/migration/zustand-wrap/before/src/store.ts @@ -5,7 +5,7 @@ * * Three things matter for the migration demo: * 1. The store is created with `zustand/vanilla` so we can wrap it - * with `@acture/state-zustand` later without changing this file. + * with `acture-state-zustand` later without changing this file. * 2. Actions are typed as plain methods (no Redux-action-creator * ceremony). * 3. We use it in the React tree via the `useStore` hook below. diff --git a/packages/ai-vercel/README.md b/packages/ai-vercel/README.md index bb74c7d..0442325 100644 --- a/packages/ai-vercel/README.md +++ b/packages/ai-vercel/README.md @@ -1,11 +1,13 @@ -# @acture/ai-vercel +# acture-ai-vercel + +> **acture is a development tool first.** This package is an *optional accelerator* — an agent can hand-write this integration into your project instead, with no `acture-*` dependency. Installing it is a deliberate, opt-in choice to reuse tested code rather than own it. See [`docs/positioning.md`](../../docs/positioning.md). Project an [acture](https://npm.im/acture) registry as [Vercel AI SDK](https://sdk.vercel.ai) tool definitions. Drop directly into `streamText({ tools })` / `generateText({ tools })`. ## Install ```sh -pnpm add @acture/ai-vercel ai acture zod +pnpm add acture-ai-vercel ai acture zod ``` ## Use @@ -13,7 +15,7 @@ pnpm add @acture/ai-vercel ai acture zod ```ts import { streamText } from 'ai'; import { anthropic } from '@ai-sdk/anthropic'; -import { toAITools } from '@acture/ai-vercel'; +import { toAITools } from 'acture-ai-vercel'; import { registry } from './registry'; const result = streamText({ @@ -49,10 +51,10 @@ The model sees the same shape on every surface (palette, hotkeys, MCP, AI SDK). ## Why pass Zod through (not JSON Schema)? -The Vercel AI SDK accepts Zod schemas directly. Passing the original schema preserves validators (`z.refine`, `z.transform` constraints on output) that JSON Schema would silently drop. The same registry exposed via `@acture/mcp` projects through `toJsonSchema` because MCP wants JSON Schema on the wire. +The Vercel AI SDK accepts Zod schemas directly. Passing the original schema preserves validators (`z.refine`, `z.transform` constraints on output) that JSON Schema would silently drop. The same registry exposed via `acture-mcp` projects through `toJsonSchema` because MCP wants JSON Schema on the wire. ## See also - [`acture-schema-bridge`](https://github.com/thorwhalen/acture/blob/main/.claude/skills/acture-schema-bridge/SKILL.md) - [`acture-tier-system`](https://github.com/thorwhalen/acture/blob/main/.claude/skills/acture-tier-system/SKILL.md) -- [`@acture/mcp`](../mcp) — the MCP server counterpart +- [`acture-mcp`](../mcp) — the MCP server counterpart diff --git a/packages/ai-vercel/package.json b/packages/ai-vercel/package.json index 65b744f..b08e590 100644 --- a/packages/ai-vercel/package.json +++ b/packages/ai-vercel/package.json @@ -1,5 +1,5 @@ { - "name": "@acture/ai-vercel", + "name": "acture-ai-vercel", "version": "1.0.0", "description": "Project an acture registry as Vercel AI SDK tool definitions. Tier-filtered; errors-as-data; passes Zod schemas through directly.", "license": "Apache-2.0", diff --git a/packages/ai-vercel/src/index.ts b/packages/ai-vercel/src/index.ts index 9b33556..1e053b7 100644 --- a/packages/ai-vercel/src/index.ts +++ b/packages/ai-vercel/src/index.ts @@ -1,5 +1,5 @@ /** - * `@acture/ai-vercel` — project an acture registry as Vercel AI SDK + * `acture-ai-vercel` — project an acture registry as Vercel AI SDK * tool definitions. * * The Vercel AI SDK's `tool({ description, parameters, execute })` @@ -7,7 +7,7 @@ * through without re-projection — preserving validators that JSON * Schema would silently drop (e.g., `z.refine` predicates). * - * Tier filter and deprecation banners mirror `@acture/mcp`. + * Tier filter and deprecation banners mirror `acture-mcp`. */ import { tool } from 'ai'; @@ -21,7 +21,7 @@ import type { } from 'acture'; import { isFunctionWhen, isOk } from 'acture'; -/** See `@acture/mcp` tools.ts: identical banner format. */ +/** See `acture-mcp` tools.ts: identical banner format. */ const DEPRECATION_PREFIX_BARE = '[DEPRECATED]'; function deprecationBanner(reason?: string): string { return reason && reason.length > 0 diff --git a/packages/build-tier/README.md b/packages/build-tier/README.md index 1eae157..30e7d8e 100644 --- a/packages/build-tier/README.md +++ b/packages/build-tier/README.md @@ -1,11 +1,13 @@ -# @acture/build-tier +# acture-build-tier + +> **acture is a development tool first.** This is dev/build-time tooling — it never becomes a runtime dependency of the apps it serves, and using it is entirely optional. See [`docs/positioning.md`](../../docs/positioning.md). Build-step plugin. Scans `.ts` / `.tsx` source for JSDoc tier tags on `defineCommand` calls and mirrors them into the runtime command's `tier` metadata field. ## Install ```bash -pnpm add -D @acture/build-tier +pnpm add -D acture-build-tier ``` ## tsup / esbuild @@ -13,7 +15,7 @@ pnpm add -D @acture/build-tier ```ts // tsup.config.ts import { defineConfig } from 'tsup'; -import { actureBuildTier } from '@acture/build-tier/esbuild'; +import { actureBuildTier } from 'acture-build-tier/esbuild'; export default defineConfig({ entry: ['src/index.ts'], @@ -26,7 +28,7 @@ export default defineConfig({ ```ts // vite.config.ts import { defineConfig } from 'vite'; -import { actureBuildTierVite } from '@acture/build-tier/vite'; +import { actureBuildTierVite } from 'acture-build-tier/vite'; export default defineConfig({ plugins: [actureBuildTierVite()], @@ -87,7 +89,7 @@ For projects where AST-level certainty matters more than a fast regex pass, an A ```ts // custom bundler plugin -import { transformSourceAst } from '@acture/build-tier/ast'; +import { transformSourceAst } from 'acture-build-tier/ast'; export function actureTierAstPlugin() { return { diff --git a/packages/build-tier/package.json b/packages/build-tier/package.json index ee1ec5d..0838d0f 100644 --- a/packages/build-tier/package.json +++ b/packages/build-tier/package.json @@ -1,5 +1,5 @@ { - "name": "@acture/build-tier", + "name": "acture-build-tier", "version": "1.1.0", "description": "Build-time tier mirror. Scans .ts source for @stable / @experimental / @internal / @deprecated JSDoc tags on defineCommand calls and mirrors them into the runtime command's tier metadata. Ships an esbuild plugin and a Vite plugin.", "license": "Apache-2.0", @@ -59,7 +59,7 @@ } }, "devDependencies": { - "@acture/mcp": "workspace:*", + "acture-mcp": "workspace:*", "@types/node": "^22.10.0", "acture": "workspace:*", "ts-morph": "^25.0.0", diff --git a/packages/build-tier/src/ast.ts b/packages/build-tier/src/ast.ts index 9bff977..1bbd38a 100644 --- a/packages/build-tier/src/ast.ts +++ b/packages/build-tier/src/ast.ts @@ -19,14 +19,14 @@ * outputs for normal input. * * Cost: ts-morph is a real dependency (~7 MB). It is declared as an - * *optional peer* of `@acture/build-tier` so consumers who only use + * *optional peer* of `acture-build-tier` so consumers who only use * the regex mode don't pay for it. Importing this file pulls ts-morph * in lazily. * * Usage: * * // tsup, vite, etc. — point your plugin at the AST transform: - * import { transformSourceAst } from '@acture/build-tier/ast'; + * import { transformSourceAst } from 'acture-build-tier/ast'; * * export function actureTierAstPlugin(): Plugin { * return { @@ -39,8 +39,8 @@ * }; * } * - * The regex mode at `@acture/build-tier/esbuild` and - * `@acture/build-tier/vite` is the documented default. Use AST mode + * The regex mode at `acture-build-tier/esbuild` and + * `acture-build-tier/vite` is the documented default. Use AST mode * when the regex's lookahead window or string handling is insufficient. */ diff --git a/packages/build-tier/src/end-to-end.test.ts b/packages/build-tier/src/end-to-end.test.ts index cb1ae6b..dbd0ee0 100644 --- a/packages/build-tier/src/end-to-end.test.ts +++ b/packages/build-tier/src/end-to-end.test.ts @@ -19,7 +19,7 @@ import { describe, it, expect } from 'vitest'; import { createRegistry, defineCommand, ok } from 'acture'; -import { buildToolsList } from '@acture/mcp'; +import { buildToolsList } from 'acture-mcp'; import { transformSource } from './transform.js'; function transformAndEvalRegistry(): ReturnType { diff --git a/packages/build-tier/src/esbuild.ts b/packages/build-tier/src/esbuild.ts index bdbc21c..df846dc 100644 --- a/packages/build-tier/src/esbuild.ts +++ b/packages/build-tier/src/esbuild.ts @@ -3,7 +3,7 @@ * * Usage in a consumer's tsup.config.ts: * - * import { actureBuildTier } from '@acture/build-tier/esbuild'; + * import { actureBuildTier } from 'acture-build-tier/esbuild'; * export default defineConfig({ * entry: ['src/index.ts'], * esbuildPlugins: [actureBuildTier()], diff --git a/packages/build-tier/src/index.ts b/packages/build-tier/src/index.ts index 1b6b24f..ab7c506 100644 --- a/packages/build-tier/src/index.ts +++ b/packages/build-tier/src/index.ts @@ -1,5 +1,5 @@ /** - * `@acture/build-tier` — build-time tier mirror. + * `acture-build-tier` — build-time tier mirror. * * Scans source for `@stable` / `@experimental` / `@internal` / * `@deprecated [reason]` JSDoc tags on `defineCommand` calls and mirrors @@ -11,10 +11,10 @@ * Two integration points are exported in separate entry points so users * pay only for the bundler they use: * - * - `@acture/build-tier/esbuild` → an esbuild plugin (also works with + * - `acture-build-tier/esbuild` → an esbuild plugin (also works with * tsup, which is esbuild under the hood). - * - `@acture/build-tier/vite` → a Vite plugin. - * - `@acture/build-tier/ast` → AST-mode polish using `ts-morph`. Same + * - `acture-build-tier/vite` → a Vite plugin. + * - `acture-build-tier/ast` → AST-mode polish using `ts-morph`. Same * contract as the regex transform, but handles 5000-char spec * bodies and template-substitution edge cases the regex caps out * on. Optional peer; only loaded if you import it. (v1.2.) diff --git a/packages/build-tier/src/vite.ts b/packages/build-tier/src/vite.ts index 8476385..5177ba9 100644 --- a/packages/build-tier/src/vite.ts +++ b/packages/build-tier/src/vite.ts @@ -4,7 +4,7 @@ * * Usage in vite.config.ts: * - * import { actureBuildTierVite } from '@acture/build-tier/vite'; + * import { actureBuildTierVite } from 'acture-build-tier/vite'; * export default defineConfig({ * plugins: [actureBuildTierVite()], * }); diff --git a/packages/cli/README.md b/packages/cli/README.md index 2725377..e82937b 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -1,11 +1,13 @@ -# @acture/cli +# acture-cli + +> **acture is a development tool first.** This is dev/build-time tooling — it never becomes a runtime dependency of the apps it serves, and using it is entirely optional. See [`docs/positioning.md`](../../docs/positioning.md). The `acture` binary. Two subcommands today; the package is intentionally small. ## Install ```bash -pnpm add -D @acture/cli +pnpm add -D acture-cli ``` ## `acture compare-schemas` @@ -101,7 +103,7 @@ import { classifyChanges, formatResult, runSnapshotCmd, -} from '@acture/cli'; +} from 'acture-cli'; ``` See [`docs/research/acture_research_5 -- Schema Versioning ...md`](../../docs/research/) §6 for the canonical design. diff --git a/packages/cli/package.json b/packages/cli/package.json index ab38651..c9181be 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,5 +1,5 @@ { - "name": "@acture/cli", + "name": "acture-cli", "version": "1.2.0", "description": "Acture CLI. Includes `compare-schemas` — diff two registry snapshots (per research-5 §6) with classifications, CI gating, and JSON/text output.", "license": "Apache-2.0", diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 2e0c6ce..edfc8f7 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,5 +1,5 @@ /** - * `@acture/cli` programmatic API. + * `acture-cli` programmatic API. * * Exports the building blocks of the CLI so consumers can: * diff --git a/packages/cli/src/snapshot.ts b/packages/cli/src/snapshot.ts index 112c72a..894a0cc 100644 --- a/packages/cli/src/snapshot.ts +++ b/packages/cli/src/snapshot.ts @@ -7,7 +7,7 @@ * * { * "version": 1, - * "generator": "@acture/cli", + * "generator": "acture-cli", * "tools": [ * { * "name": "app.search", @@ -70,7 +70,7 @@ export function snapshotRegistry( const tools = list.map(commandToSnapshotTool); return { version: 1, - generator: '@acture/cli', + generator: 'acture-cli', tools, }; } diff --git a/packages/codemods/README.md b/packages/codemods/README.md index a71d5fd..4fe7f20 100644 --- a/packages/codemods/README.md +++ b/packages/codemods/README.md @@ -1,4 +1,6 @@ -# @acture/codemods +# acture-codemods + +> **acture is a development tool first.** This is dev/build-time tooling — it never becomes a runtime dependency of the apps it serves, and using it is entirely optional. See [`docs/positioning.md`](../../docs/positioning.md). Codemod CLI for adopting acture in an existing TypeScript/React codebase. Single `npx`-invokable runner with a manifest of shipped transforms. @@ -8,14 +10,14 @@ Research-4 §B.5 plans five codemods; v1.3 ships **all five**. v1.2 was the firs ```bash # List shipped codemods -npx @acture/codemods --list +npx acture-codemods --list # Dry-run on a directory, emit JSON for an agent to read -npx @acture/codemods wrap-handler-with-mutation \ +npx acture-codemods wrap-handler-with-mutation \ --target src/ --dry-run --json # Apply for real -npx @acture/codemods wrap-handler-with-mutation --target src/ +npx acture-codemods wrap-handler-with-mutation --target src/ ``` ## Shipped codemods @@ -50,7 +52,7 @@ acture-codemods --target --json ## Programmatic API ```ts -import { runCodemod } from '@acture/codemods'; +import { runCodemod } from 'acture-codemods'; const result = await runCodemod('wrap-handler-with-mutation', { files: ['src/Button.tsx', 'src/Form.tsx'], @@ -74,10 +76,10 @@ for (const f of result.files) { - **No business logic.** Codemods translate code to code. They do not decide which commands are right, when to migrate, or how to author the spec — those are user decisions. - **No "smart" rewrites that need type info we can't get.** When the transform would need to read a generic parameter or infer a return type, we skip and emit a note. (Research-4 §B.2: ts-morph has type info, but every codemod we ship documents which path it uses — purely structural vs. type-aware.) -- **No surprise installs.** The codemods write code that imports `acture` / `@acture/migration`. They do NOT run `npm install` or modify `package.json`. The user installs deps themselves. +- **No surprise installs.** The codemods write code that imports `acture` / `acture-migration`. They do NOT run `npm install` or modify `package.json`. The user installs deps themselves. ## See also - `docs/research/acture_research_4 -- Transitional APIs and Codemod Tooling…` §B.5, §B.6 -- `@acture/migration` for the runtime-only adoption surface (`wrapMutation`, `actureMiddleware`, `createDomInterceptor`, …) +- `acture-migration` for the runtime-only adoption surface (`wrapMutation`, `actureMiddleware`, `createDomInterceptor`, …) - `.claude/skills/migration-wrap/SKILL.md` for the agent workflow that drives these codemods diff --git a/packages/codemods/migrations.json b/packages/codemods/migrations.json index ce674d7..fff93d8 100644 --- a/packages/codemods/migrations.json +++ b/packages/codemods/migrations.json @@ -7,31 +7,31 @@ "name": "wrap-handler-with-mutation", "since": "1.0.0", "description": "Wrap onClick/onChange/onSubmit handler expressions with wrapMutation().", - "version-boundary": "@acture/codemods@1.0.0" + "version-boundary": "acture-codemods@1.0.0" }, { "name": "extract-onclick-to-command", "since": "1.0.0", "description": "Lift inline arrow handlers into module-level defineCommand calls.", - "version-boundary": "@acture/codemods@1.0.0" + "version-boundary": "acture-codemods@1.0.0" }, { "name": "redux-action-to-command", "since": "1.1.0", "description": "Convert dispatch({type, payload}) call sites to registry.dispatch(id, payload).", - "version-boundary": "@acture/codemods@1.1.0" + "version-boundary": "acture-codemods@1.1.0" }, { "name": "usestate-mutation-to-command", "since": "1.1.0", "description": "Wrap inline handlers whose body is composed of useState setter calls with wrapMutation.", - "version-boundary": "@acture/codemods@1.1.0" + "version-boundary": "acture-codemods@1.1.0" }, { "name": "rtk-thunk-to-command", "since": "1.1.0", "description": "Convert createAsyncThunk(id, payloadCreator) into defineCommand({id, title, execute}).", - "version-boundary": "@acture/codemods@1.1.0" + "version-boundary": "acture-codemods@1.1.0" } ] } diff --git a/packages/codemods/package.json b/packages/codemods/package.json index fe2a72a..d0e0e68 100644 --- a/packages/codemods/package.json +++ b/packages/codemods/package.json @@ -1,5 +1,5 @@ { - "name": "@acture/codemods", + "name": "acture-codemods", "version": "1.1.0", "description": "Codemod CLI for adopting acture in an existing TypeScript/React codebase. Single npx-invokable runner with a manifest of structural transforms (research-4 §B.5).", "license": "Apache-2.0", diff --git a/packages/codemods/src/codemods/usestate-mutation-to-command.test.ts b/packages/codemods/src/codemods/usestate-mutation-to-command.test.ts index 3bc20cf..ff71ac9 100644 --- a/packages/codemods/src/codemods/usestate-mutation-to-command.test.ts +++ b/packages/codemods/src/codemods/usestate-mutation-to-command.test.ts @@ -28,7 +28,7 @@ describe('usestate-mutation-to-command', () => { expect(after).toContain( 'wrapMutation(() => setCount(count + 1), { id: "app.state.setCount" })', ); - expect(after).toMatch(/import \{ wrapMutation \} from ["']@acture\/migration["']/); + expect(after).toMatch(/import \{ wrapMutation \} from ["']acture-migration["']/); cleanup(); }); @@ -75,7 +75,7 @@ describe('usestate-mutation-to-command', () => { it('is idempotent on already-wrapped handlers', async () => { const { path, cleanup } = withFile( - `import { wrapMutation } from '@acture/migration'; + `import { wrapMutation } from 'acture-migration'; export function C() { const setX = (n: number) => {}; return ; diff --git a/packages/codemods/src/codemods/usestate-mutation-to-command.ts b/packages/codemods/src/codemods/usestate-mutation-to-command.ts index 7a1e153..009d12a 100644 --- a/packages/codemods/src/codemods/usestate-mutation-to-command.ts +++ b/packages/codemods/src/codemods/usestate-mutation-to-command.ts @@ -45,7 +45,7 @@ * setter identifiers. Override if the codebase * uses a different convention. * - `events` default `onClick,onChange,onSubmit`. - * - `import-from` default `@acture/migration`. + * - `import-from` default `acture-migration`. */ import { @@ -78,7 +78,7 @@ function resolveOptions(opts: Record | undefined): R idPrefix: opts?.['id-prefix'] ?? 'app.state', setterPattern, events, - importFrom: opts?.['import-from'] ?? '@acture/migration', + importFrom: opts?.['import-from'] ?? 'acture-migration', }; } diff --git a/packages/codemods/src/codemods/wrap-handler-with-mutation.test.ts b/packages/codemods/src/codemods/wrap-handler-with-mutation.test.ts index 80002f8..846088b 100644 --- a/packages/codemods/src/codemods/wrap-handler-with-mutation.test.ts +++ b/packages/codemods/src/codemods/wrap-handler-with-mutation.test.ts @@ -25,7 +25,7 @@ describe('wrap-handler-with-mutation', () => { const result = await wrapHandlerWithMutation.run({ files: [path] }); expect(result.summary.changed).toBe(1); expect(result.files[0]!.after).toContain('onClick={wrapMutation(save)}'); - expect(result.files[0]!.after).toMatch(/from ["']@acture\/migration["']/); + expect(result.files[0]!.after).toMatch(/from ["']acture-migration["']/); cleanup(); }); @@ -44,7 +44,7 @@ declare function doStuff(): void; it('is idempotent on already-wrapped handlers', async () => { const { path, cleanup } = withFile( - `import { wrapMutation } from '@acture/migration'; + `import { wrapMutation } from 'acture-migration'; export function Btn({ save }: { save: () => void }) { return ; } @@ -105,7 +105,7 @@ export function Btn({ save }: { save: () => void }) { it('adds named import to existing migration import', async () => { const { path, cleanup } = withFile( - `import { chooseImplementation } from '@acture/migration'; + `import { chooseImplementation } from 'acture-migration'; export function X({ a }: { a: () => void }) { return ; } @@ -113,7 +113,7 @@ export function X({ a }: { a: () => void }) { ); const result = await wrapHandlerWithMutation.run({ files: [path] }); expect(result.files[0]!.after).toMatch( - /import \{ chooseImplementation, wrapMutation \} from ["']@acture\/migration["']/, + /import \{ chooseImplementation, wrapMutation \} from ["']acture-migration["']/, ); cleanup(); }); diff --git a/packages/codemods/src/codemods/wrap-handler-with-mutation.ts b/packages/codemods/src/codemods/wrap-handler-with-mutation.ts index d104c78..51f6ac3 100644 --- a/packages/codemods/src/codemods/wrap-handler-with-mutation.ts +++ b/packages/codemods/src/codemods/wrap-handler-with-mutation.ts @@ -47,7 +47,7 @@ function resolveOptions(opts: Record | undefined): R : DEFAULT_EVENTS; return { events, - importFrom: opts?.['import-from'] ?? '@acture/migration', + importFrom: opts?.['import-from'] ?? 'acture-migration', importName: opts?.['import-name'] ?? 'wrapMutation', }; } diff --git a/packages/codemods/src/index.ts b/packages/codemods/src/index.ts index dc83fa0..7aaa2e3 100644 --- a/packages/codemods/src/index.ts +++ b/packages/codemods/src/index.ts @@ -1,13 +1,13 @@ /** - * `@acture/codemods` — codemod tooling for the strangler-fig adoption - * path. Single `npx @acture/codemods ` CLI with a manifest of + * `acture-codemods` — codemod tooling for the strangler-fig adoption + * path. Single `npx acture-codemods ` CLI with a manifest of * shipped transforms. Research-4 §B.5. * * Programmatic API is also exported here for hosts that want to invoke * a codemod from their own tooling without spawning a child process. * * ```ts - * import { runCodemod } from '@acture/codemods'; + * import { runCodemod } from 'acture-codemods'; * * const result = await runCodemod('wrap-handler-with-mutation', { * files: ['src/Button.tsx'], diff --git a/packages/core/README.md b/packages/core/README.md index fc2f495..fefe364 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -1,10 +1,12 @@ # acture +> **acture is a development tool first.** Even this core package is optional — an agent can write a registry + dispatcher directly into your project following acture's patterns. Installing `acture` is a deliberate choice to reuse the primitive rather than own it. See [`docs/positioning.md`](../../docs/positioning.md). + > One schema. Palette, hotkeys, AI tools, MCP, and tests — for free. Acture is a typed, schema-driven command dispatch library. Define an operation once; expose it as a command palette entry, keyboard shortcut, AI tool, MCP server tool, or test action. -This package is the **core**: registry, dispatcher, when-clause DSL, schema bridge, state-adapter types. Zero React, zero state-library dependencies. Concrete adapters live in `@acture/state-zustand`, `@acture/palette-react`, etc. +This package is the **core**: registry, dispatcher, when-clause DSL, schema bridge, state-adapter types. Zero React, zero state-library dependencies. Concrete adapters live in `acture-state-zustand`, `acture-palette-react`, etc. > **Phase 1 status.** `defineCommand` + `createRegistry` + `toJsonSchema` + when-clause DSL are stable. Parameterized-palette UX, hotkeys, MCP / AI adapters, and the migration package land in Phase 2 / 3. @@ -122,7 +124,7 @@ type Result = | { ok: false; error: { code: string; message: string; details?: unknown } }; ``` -`patches` and `effects` are **reserved hooks** for the post-v1 `@acture/undo` subsystem. Phase 1 ignores them on dispatch but preserves whatever your handler returns. Helpers: `ok(value, { patches?, effects? })` and `err(code, message, details?)`. Predicates: `isOk(r)`, `isErr(r)`. +`patches` and `effects` are **reserved hooks** for the post-v1 `acture-undo` subsystem. Phase 1 ignores them on dispatch but preserves whatever your handler returns. Helpers: `ok(value, { patches?, effects? })` and `err(code, message, details?)`. Predicates: `isOk(r)`, `isErr(r)`. ### Built-in error codes @@ -198,7 +200,7 @@ interface PatchCapableAdapter extends StateAdapter { function isPatchCapable(a: StateAdapter): a is PatchCapableAdapter; ``` -The Phase 1 reference adapter is [`@acture/state-zustand`](https://npm.im/@acture/state-zustand): `createZustandAdapter({ initialState })`. +The Phase 1 reference adapter is [`acture-state-zustand`](https://npm.im/acture-state-zustand): `createZustandAdapter({ initialState })`. ## Writing a new command — pattern @@ -227,7 +229,7 @@ const renameNode = defineCommand({ registry.register(renameNode); ``` -The example below shows registry composition with a host's state adapter — see [`@acture/example-graph-editor`](../../examples/greenfield/graph-editor) for the full worked example. +The example below shows registry composition with a host's state adapter — see [`acture-example-graph-editor`](../../examples/greenfield/graph-editor) for the full worked example. ## Type exports diff --git a/packages/core/src/state-adapter.ts b/packages/core/src/state-adapter.ts index 9db8501..a797895 100644 --- a/packages/core/src/state-adapter.ts +++ b/packages/core/src/state-adapter.ts @@ -1,7 +1,7 @@ /** * State adapter interface. Per research-3 §5, three methods plus an * optional patch-capable sub-interface. The core ships only the types; - * concrete adapters live in `@acture/state-zustand`, `@acture/state-redux`, + * concrete adapters live in `acture-state-zustand`, `acture-state-redux`, * etc. * * Design constraints (research-3 §2): @@ -34,7 +34,7 @@ export interface StateAdapter { /** * Capability extension: adapters whose substrate produces Immer-shaped * patches natively (zustand+immer, RTK, MST). The future - * `@acture/undo` subsystem will check for this capability and fall back + * `acture-undo` subsystem will check for this capability and fall back * to wrapping `produceWithPatches` inside the command's exec otherwise. */ export interface PatchCapableAdapter extends StateAdapter { diff --git a/packages/core/src/tier-warnings.ts b/packages/core/src/tier-warnings.ts index d2f6d04..0dd48a8 100644 --- a/packages/core/src/tier-warnings.ts +++ b/packages/core/src/tier-warnings.ts @@ -10,7 +10,7 @@ * Idempotent — wrapping a registry twice returns the same disposer. * Calling the returned disposer restores the original `dispatch`. * - * Pattern mirrors `@acture/devtools` `instrumentRegistry`: we mutate + * Pattern mirrors `acture-devtools` `instrumentRegistry`: we mutate * one method on the registry. Per `acture-hard-donts` §6, this is * permitted because (a) it's opt-in and dev-leaning, (b) it preserves * the original signature exactly, (c) the wrapper is identifiable diff --git a/packages/core/src/tier.test.ts b/packages/core/src/tier.test.ts index 55bae9f..072459c 100644 --- a/packages/core/src/tier.test.ts +++ b/packages/core/src/tier.test.ts @@ -1,6 +1,6 @@ /** * Tier-system runtime tests. The JSDoc-tag mirror lives in - * `@acture/build-tier`; this file verifies what the registry does once + * `acture-build-tier`; this file verifies what the registry does once * a command already carries `tier` / `deprecationReason` / `internalToken`. */ diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 5e43386..e774cc9 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -25,7 +25,7 @@ export type CommandKind = 'atomic' | 'handoff'; /** RFC 6902-compatible JSON Patch. The shape is intentionally a subset * of Immer's Patch so that cross-substrate interop (Immer, MST) is * trivial. Phase 1 core does NOT consume patches; they are reserved - * hooks for `@acture/undo` (post-v1). */ + * hooks for `acture-undo` (post-v1). */ export interface Patch { op: 'add' | 'remove' | 'replace'; path: readonly (string | number)[]; @@ -86,9 +86,9 @@ export interface CommandRecord

{ readonly kind?: CommandKind; readonly tier?: Tier; /** Free-text reason populated from `@deprecated ` JSDoc by - * the build-step tier mirror. Adapter packages (`@acture/mcp`, - * `@acture/ai-vercel`) prepend `[DEPRECATED — ]` to the - * description when projecting. `@acture/devtools` surfaces it in the + * the build-step tier mirror. Adapter packages (`acture-mcp`, + * `acture-ai-vercel`) prepend `[DEPRECATED — ]` to the + * description when projecting. `acture-devtools` surfaces it in the * inspector. (Three callers; closed-surface addition approved.) */ readonly deprecationReason?: string; /** Opt-in token for `@internal` dispatch. The tier-mirror build step diff --git a/packages/devtools/README.md b/packages/devtools/README.md index d1db6e3..a7d11cf 100644 --- a/packages/devtools/README.md +++ b/packages/devtools/README.md @@ -1,17 +1,19 @@ -# @acture/devtools +# acture-devtools + +> **acture is a development tool first.** This is dev/build-time tooling — it never becomes a runtime dependency of the apps it serves, and using it is entirely optional. See [`docs/positioning.md`](../../docs/positioning.md). Embeddable React Inspector + dispatch-log instrumentation for acture registries. Dev-only — production builds skip the imports and tree-shake to nothing. ## Install ```bash -pnpm add -D @acture/devtools +pnpm add -D acture-devtools ``` ## Mount the Inspector ```tsx -import { Inspector, instrumentRegistry } from '@acture/devtools'; +import { Inspector, instrumentRegistry } from 'acture-devtools'; import { registry } from './registry'; // Instrument once at module load so every dispatch is captured. diff --git a/packages/devtools/package.json b/packages/devtools/package.json index 9412364..5832bc0 100644 --- a/packages/devtools/package.json +++ b/packages/devtools/package.json @@ -1,5 +1,5 @@ { - "name": "@acture/devtools", + "name": "acture-devtools", "version": "1.0.0", "description": "Acture devtools inspector — registry contents, dispatch log, when-clause evaluator, tier filter preview. Embeddable React component for dev builds.", "license": "Apache-2.0", diff --git a/packages/devtools/src/index.ts b/packages/devtools/src/index.ts index d3dd8a0..de3fef4 100644 --- a/packages/devtools/src/index.ts +++ b/packages/devtools/src/index.ts @@ -1,5 +1,5 @@ /** - * `@acture/devtools` — embeddable inspector for acture registries. + * `acture-devtools` — embeddable inspector for acture registries. * * Two pieces: * diff --git a/packages/eslint-plugin-acture-migration/README.md b/packages/eslint-plugin-acture-migration/README.md index d3396ed..f99e50c 100644 --- a/packages/eslint-plugin-acture-migration/README.md +++ b/packages/eslint-plugin-acture-migration/README.md @@ -1,5 +1,7 @@ # eslint-plugin-acture-migration +> **acture is a development tool first.** This is dev/build-time tooling — it never becomes a runtime dependency of the apps it serves, and using it is entirely optional. See [`docs/positioning.md`](../../docs/positioning.md). + One ESLint rule for [acture](https://github.com/thorwhalen/acture) strangler-fig migrations: **catch `wrapMutation` wrappers that have outlived their purpose.** During a migration you wrap an existing handler with `wrapMutation(...)` so the call site stays unchanged while the command becomes visible to the palette, MCP, and AI surfaces. Once the legacy call sites are gone, the wrapper is dead weight — the command should be authored directly with `defineCommand`. This plugin flags those stale wrappers so they don't accumulate. @@ -47,7 +49,7 @@ Flags `wrapMutation(...)` calls whose **return value is never used**. The only t ### Flagged ```ts -import { wrapMutation } from '@acture/migration'; +import { wrapMutation } from 'acture-migration'; // Bare statement — result discarded entirely. wrapMutation(handleSave, { registry }); @@ -61,7 +63,7 @@ Both should become a direct `defineCommand` plus `registry.register(...)`. See t ### Not flagged ```ts -import { wrapMutation } from '@acture/migration'; +import { wrapMutation } from 'acture-migration'; // Result is used — wrapper is still load-bearing. const onSave = wrapMutation(handleSave, { registry }); @@ -78,7 +80,7 @@ register(wrapMutation(handleSave, { registry })); The rule is deliberately **single-file and conservative** — research-4's codemod principle: a high-confidence partial signal beats a noisy total one. -- Tracks `wrapMutation` imported (named or aliased) from `@acture/migration`. Namespace imports (`import * as m`) are not tracked. +- Tracks `wrapMutation` imported (named or aliased) from `acture-migration`. Namespace imports (`import * as m`) are not tracked. - Reports two shapes: a bare `ExpressionStatement`, and assignment to a non-exported local binding with zero references. - An exported binding, a referenced binding, a returned result, or a result passed as an argument are all left alone — any of them may still be load-bearing. @@ -92,10 +94,10 @@ False negatives are expected (a binding exported and unused cross-file won't be | Option | Default | Description | | -------- | -------------------- | ----------- | -| `module` | `@acture/migration` | Module that `wrapMutation` is imported from. Override if your codebase re-exports it under its own package name. | +| `module` | `acture-migration` | Module that `wrapMutation` is imported from. Override if your codebase re-exports it under its own package name. | ## See also -- `@acture/migration` — the runtime adoption surface (`wrapMutation`, `actureMiddleware`, …). -- `@acture/codemods` — structural transforms for adopting acture. +- `acture-migration` — the runtime adoption surface (`wrapMutation`, `actureMiddleware`, …). +- `acture-codemods` — structural transforms for adopting acture. - `.claude/skills/migration-graduate/SKILL.md` — the agent workflow for retiring a `wrapMutation` once this rule fires. diff --git a/packages/eslint-plugin-acture-migration/src/rules/no-stale-wrap-mutation.test.ts b/packages/eslint-plugin-acture-migration/src/rules/no-stale-wrap-mutation.test.ts index 69128e1..2f191bf 100644 --- a/packages/eslint-plugin-acture-migration/src/rules/no-stale-wrap-mutation.test.ts +++ b/packages/eslint-plugin-acture-migration/src/rules/no-stale-wrap-mutation.test.ts @@ -16,25 +16,25 @@ const ruleTester = new RuleTester({ ruleTester.run('no-stale-wrap-mutation', noStaleWrapMutation, { valid: [ // Result is assigned and then referenced — wrapper is load-bearing. - `import { wrapMutation } from '@acture/migration'; + `import { wrapMutation } from 'acture-migration'; const onSave = wrapMutation(handleSave, { registry }); button.addEventListener('click', onSave);`, // Result is exported — may be called from another file. - `import { wrapMutation } from '@acture/migration'; + `import { wrapMutation } from 'acture-migration'; export const onSave = wrapMutation(handleSave, { registry });`, // Result is exported via a specifier — the specifier counts as a use. - `import { wrapMutation } from '@acture/migration'; + `import { wrapMutation } from 'acture-migration'; const onSave = wrapMutation(handleSave, { registry }); export { onSave };`, // Result is returned — still in use. - `import { wrapMutation } from '@acture/migration'; + `import { wrapMutation } from 'acture-migration'; function make() { return wrapMutation(handleSave, { registry }); }`, // Result is passed as an argument — still in use. - `import { wrapMutation } from '@acture/migration'; + `import { wrapMutation } from 'acture-migration'; register(wrapMutation(handleSave, { registry }));`, // No import of wrapMutation — not our function, stay quiet. @@ -45,12 +45,12 @@ ruleTester.run('no-stale-wrap-mutation', noStaleWrapMutation, { wrapMutation(handleSave, { registry });`, // Namespace import — member-expression callee is not tracked. - `import * as m from '@acture/migration'; + `import * as m from 'acture-migration'; m.wrapMutation(handleSave, { registry });`, // Custom module option configured, but the import is from elsewhere. { - code: `import { wrapMutation } from '@acture/migration'; + code: `import { wrapMutation } from 'acture-migration'; wrapMutation(handleSave, { registry });`, options: [{ module: '@myorg/legacy-migration' }], }, @@ -59,21 +59,21 @@ ruleTester.run('no-stale-wrap-mutation', noStaleWrapMutation, { invalid: [ // Bare expression statement — result discarded entirely. { - code: `import { wrapMutation } from '@acture/migration'; + code: `import { wrapMutation } from 'acture-migration'; wrapMutation(handleSave, { registry });`, errors: [{ messageId: 'staleWrapper' }], }, // Assigned to a local, non-exported binding that is never used. { - code: `import { wrapMutation } from '@acture/migration'; + code: `import { wrapMutation } from 'acture-migration'; const onSave = wrapMutation(handleSave, { registry });`, errors: [{ messageId: 'staleWrapper' }], }, // Aliased import — track the local name. { - code: `import { wrapMutation as wm } from '@acture/migration'; + code: `import { wrapMutation as wm } from 'acture-migration'; wm(handleSave, { registry });`, errors: [{ messageId: 'staleWrapper' }], }, @@ -88,7 +88,7 @@ ruleTester.run('no-stale-wrap-mutation', noStaleWrapMutation, { // Multiple stale wrappers in one file — one report each. { - code: `import { wrapMutation } from '@acture/migration'; + code: `import { wrapMutation } from 'acture-migration'; wrapMutation(addTodo, { registry }); const unused = wrapMutation(removeTodo, { registry });`, errors: [{ messageId: 'staleWrapper' }, { messageId: 'staleWrapper' }], @@ -96,7 +96,7 @@ ruleTester.run('no-stale-wrap-mutation', noStaleWrapMutation, { // Inline handler, result discarded — still stale. { - code: `import { wrapMutation } from '@acture/migration'; + code: `import { wrapMutation } from 'acture-migration'; wrapMutation(() => store.save(), { registry });`, errors: [{ messageId: 'staleWrapper' }], }, @@ -105,7 +105,7 @@ ruleTester.run('no-stale-wrap-mutation', noStaleWrapMutation, { // the deferred Program:exit check still resolves it. { code: `wrapMutation(handleSave, { registry }); - import { wrapMutation } from '@acture/migration';`, + import { wrapMutation } from 'acture-migration';`, errors: [{ messageId: 'staleWrapper' }], }, ], diff --git a/packages/eslint-plugin-acture-migration/src/rules/no-stale-wrap-mutation.ts b/packages/eslint-plugin-acture-migration/src/rules/no-stale-wrap-mutation.ts index 3f38d3d..55eb89b 100644 --- a/packages/eslint-plugin-acture-migration/src/rules/no-stale-wrap-mutation.ts +++ b/packages/eslint-plugin-acture-migration/src/rules/no-stale-wrap-mutation.ts @@ -26,7 +26,7 @@ * Anything else is left alone: an exported binding may be called from * another file; a referenced binding is still load-bearing; a result that * is returned or passed as an argument is still in use. Namespace imports - * (`import * as m from '@acture/migration'`) are not tracked. + * (`import * as m from 'acture-migration'`) are not tracked. */ import type { Rule, SourceCode } from 'eslint'; @@ -37,7 +37,7 @@ import type { VariableDeclarator, } from 'estree'; -const DEFAULT_MODULE = '@acture/migration'; +const DEFAULT_MODULE = 'acture-migration'; const WRAP_MUTATION = 'wrapMutation'; interface Options { diff --git a/packages/forms-autoform/README.md b/packages/forms-autoform/README.md index 5596f0b..221f617 100644 --- a/packages/forms-autoform/README.md +++ b/packages/forms-autoform/README.md @@ -1,18 +1,20 @@ -# @acture/forms-autoform +# acture-forms-autoform -Zod-native form adapter for [`@acture/palette-react`](../palette-react). Renders a parameterized command's `params` schema as a small form with per-field validation against the schema. +> **acture is a development tool first.** This package is an *optional accelerator* — an agent can hand-write this integration into your project instead, with no `acture-*` dependency. Installing it is a deliberate, opt-in choice to reuse tested code rather than own it. See [`docs/positioning.md`](../../docs/positioning.md). + +Zod-native form adapter for [`acture-palette-react`](../palette-react). Renders a parameterized command's `params` schema as a small form with per-field validation against the schema. ## Install ```sh -pnpm add @acture/forms-autoform @acture/palette-react acture +pnpm add acture-forms-autoform acture-palette-react acture ``` ## Use as a palette form adapter ```tsx -import { CommandPalette } from '@acture/palette-react'; -import { AutoForm } from '@acture/forms-autoform'; +import { CommandPalette } from 'acture-palette-react'; +import { AutoForm } from 'acture-forms-autoform'; ` | | Optional / default wrappers | unwrapped, defaults seeded | -For richer schemas (nested objects, arrays, discriminated unions), use [`@acture/forms-rjsf`](../forms-rjsf) instead. +For richer schemas (nested objects, arrays, discriminated unions), use [`acture-forms-rjsf`](../forms-rjsf) instead. ## Why "autoform"? diff --git a/packages/forms-autoform/package.json b/packages/forms-autoform/package.json index 3e71564..5191397 100644 --- a/packages/forms-autoform/package.json +++ b/packages/forms-autoform/package.json @@ -1,7 +1,7 @@ { - "name": "@acture/forms-autoform", + "name": "acture-forms-autoform", "version": "1.0.0", - "description": "Zod-native form adapter for @acture/palette-react. Renders a CommandRecord's params schema as a small form with per-field validation.", + "description": "Zod-native form adapter for acture-palette-react. Renders a CommandRecord's params schema as a small form with per-field validation.", "license": "Apache-2.0", "author": "Thor Whalen", "repository": { diff --git a/packages/forms-autoform/src/auto-form.tsx b/packages/forms-autoform/src/auto-form.tsx index ca6436a..af4d5d5 100644 --- a/packages/forms-autoform/src/auto-form.tsx +++ b/packages/forms-autoform/src/auto-form.tsx @@ -1,7 +1,7 @@ /** * `` — render a CommandRecord's params schema as a small * form. Matches the `PaletteFormAdapter` shape expected by - * `@acture/palette-react`. + * `acture-palette-react`. */ /// diff --git a/packages/forms-autoform/src/index.ts b/packages/forms-autoform/src/index.ts index 98cb14a..92e7038 100644 --- a/packages/forms-autoform/src/index.ts +++ b/packages/forms-autoform/src/index.ts @@ -1,6 +1,6 @@ /** - * `@acture/forms-autoform` — Zod-native form adapter for the - * `@acture/palette-react` `formAdapter` prop. + * `acture-forms-autoform` — Zod-native form adapter for the + * `acture-palette-react` `formAdapter` prop. * * For handoff commands (per research-2 §9.4), the palette renders an * `AutoForm` derived from the command's `params` schema. The form @@ -10,7 +10,7 @@ * This is a *minimal* Zod-aware renderer — covers `string`, `number`, * `boolean`, `enum`, plus optional/default wrappers. Complex shapes * (nested objects, arrays, discriminated unions) should reach for - * `@acture/forms-rjsf` or a custom adapter. + * `acture-forms-rjsf` or a custom adapter. */ export { AutoForm } from './auto-form.js'; diff --git a/packages/forms-rjsf/README.md b/packages/forms-rjsf/README.md index 5ea74ca..3d36a1a 100644 --- a/packages/forms-rjsf/README.md +++ b/packages/forms-rjsf/README.md @@ -1,25 +1,27 @@ -# @acture/forms-rjsf +# acture-forms-rjsf -JSON-Schema-native form adapter for [`@acture/palette-react`](../palette-react). Wraps [react-jsonschema-form (`@rjsf/core`)](https://rjsf-team.github.io/react-jsonschema-form/) and projects the command's params schema through acture's `toJsonSchema` bridge. +> **acture is a development tool first.** This package is an *optional accelerator* — an agent can hand-write this integration into your project instead, with no `acture-*` dependency. Installing it is a deliberate, opt-in choice to reuse tested code rather than own it. See [`docs/positioning.md`](../../docs/positioning.md). + +JSON-Schema-native form adapter for [`acture-palette-react`](../palette-react). Wraps [react-jsonschema-form (`@rjsf/core`)](https://rjsf-team.github.io/react-jsonschema-form/) and projects the command's params schema through acture's `toJsonSchema` bridge. Use this when: - the command's `params` are a JSON Schema literal (not Zod), or - the host wants rjsf's mature theme ecosystem (Bootstrap, MUI, Tailwind). -For Zod-first authoring with a leaner runtime, prefer [`@acture/forms-autoform`](../forms-autoform). +For Zod-first authoring with a leaner runtime, prefer [`acture-forms-autoform`](../forms-autoform). ## Install ```sh -pnpm add @acture/forms-rjsf @rjsf/core @rjsf/utils @rjsf/validator-ajv8 react +pnpm add acture-forms-rjsf @rjsf/core @rjsf/utils @rjsf/validator-ajv8 react ``` ## Use as a palette form adapter ```tsx -import { CommandPalette } from '@acture/palette-react'; -import { RjsfForm } from '@acture/forms-rjsf'; +import { CommandPalette } from 'acture-palette-react'; +import { RjsfForm } from 'acture-forms-rjsf'; ; ``` @@ -31,4 +33,4 @@ The default render is rjsf's bare bones. To use a theme, wrap `RjsfForm` and pas ## See also - [`acture-schema-bridge`](https://github.com/thorwhalen/acture/blob/main/.claude/skills/acture-schema-bridge/SKILL.md) — how Zod → JSON Schema flows -- [`@acture/forms-autoform`](../forms-autoform) — Zod-native alternative +- [`acture-forms-autoform`](../forms-autoform) — Zod-native alternative diff --git a/packages/forms-rjsf/package.json b/packages/forms-rjsf/package.json index c6b7ee1..fe9c5cc 100644 --- a/packages/forms-rjsf/package.json +++ b/packages/forms-rjsf/package.json @@ -1,7 +1,7 @@ { - "name": "@acture/forms-rjsf", + "name": "acture-forms-rjsf", "version": "1.0.0", - "description": "JSON-Schema-native form adapter for @acture/palette-react built on react-jsonschema-form (@rjsf/core).", + "description": "JSON-Schema-native form adapter for acture-palette-react built on react-jsonschema-form (@rjsf/core).", "license": "Apache-2.0", "author": "Thor Whalen", "repository": { diff --git a/packages/forms-rjsf/src/index.ts b/packages/forms-rjsf/src/index.ts index b49e75a..8fcc093 100644 --- a/packages/forms-rjsf/src/index.ts +++ b/packages/forms-rjsf/src/index.ts @@ -1,6 +1,6 @@ /** - * `@acture/forms-rjsf` — JSON-Schema-native form adapter for - * `@acture/palette-react`. Wraps [react-jsonschema-form](https://rjsf-team.github.io/react-jsonschema-form/) + * `acture-forms-rjsf` — JSON-Schema-native form adapter for + * `acture-palette-react`. Wraps [react-jsonschema-form](https://rjsf-team.github.io/react-jsonschema-form/) * (`@rjsf/core`) and projects the command's params schema through * acture's `toJsonSchema` bridge. * @@ -9,7 +9,7 @@ * - the host wants a battle-tested form library with rich theming. * * For Zod-first authoring with a leaner runtime, prefer - * [`@acture/forms-autoform`](../forms-autoform). + * [`acture-forms-autoform`](../forms-autoform). */ export { RjsfForm } from './rjsf-form.js'; diff --git a/packages/forms-rjsf/src/rjsf-form.tsx b/packages/forms-rjsf/src/rjsf-form.tsx index 805e709..c70c84e 100644 --- a/packages/forms-rjsf/src/rjsf-form.tsx +++ b/packages/forms-rjsf/src/rjsf-form.tsx @@ -1,7 +1,7 @@ /** * `` — render a CommandRecord's params schema using * react-jsonschema-form. Matches the `PaletteFormAdapter` shape - * expected by `@acture/palette-react`. + * expected by `acture-palette-react`. */ /// diff --git a/packages/hotkeys/README.md b/packages/hotkeys/README.md index f8a80ae..ce16e58 100644 --- a/packages/hotkeys/README.md +++ b/packages/hotkeys/README.md @@ -1,4 +1,6 @@ -# @acture/hotkeys +# acture-hotkeys + +> **acture is a development tool first.** This package is an *optional accelerator* — an agent can hand-write this integration into your project instead, with no `acture-*` dependency. Installing it is a deliberate, opt-in choice to reuse tested code rather than own it. See [`docs/positioning.md`](../../docs/positioning.md). Keyboard-shortcut adapter for [acture](https://npm.im/acture). Reads `keybinding` off every CommandRecord and dispatches through the registry on key match. Built on [tinykeys](https://github.com/jamiebuilds/tinykeys). @@ -9,13 +11,13 @@ Same reason acture has a palette adapter and a state adapter: keyboard shortcuts ## Install ```sh -pnpm add @acture/hotkeys tinykeys +pnpm add acture-hotkeys tinykeys ``` ## Plain DOM API ```ts -import { bindHotkeys } from '@acture/hotkeys'; +import { bindHotkeys } from 'acture-hotkeys'; const stop = bindHotkeys(registry, { contextProvider: () => ({ selection: getSelection() }), @@ -31,7 +33,7 @@ stop(); ## React API ```tsx -import { useHotkeys } from '@acture/hotkeys/react'; +import { useHotkeys } from 'acture-hotkeys/react'; function App() { const selection = useSelection(); diff --git a/packages/hotkeys/package.json b/packages/hotkeys/package.json index 1dc637d..516ed67 100644 --- a/packages/hotkeys/package.json +++ b/packages/hotkeys/package.json @@ -1,5 +1,5 @@ { - "name": "@acture/hotkeys", + "name": "acture-hotkeys", "version": "1.0.0", "description": "acture keyboard-shortcut adapter. Reads `keybinding` off every CommandRecord; dispatches via the registry. tinykeys under the hood.", "license": "Apache-2.0", diff --git a/packages/hotkeys/src/bind.test.ts b/packages/hotkeys/src/bind.test.ts index a669218..fd0b5f9 100644 --- a/packages/hotkeys/src/bind.test.ts +++ b/packages/hotkeys/src/bind.test.ts @@ -23,7 +23,7 @@ function pressKey(target: Window | HTMLElement, key: string, modifiers: { (target as EventTarget).dispatchEvent(event); } -describe('@acture/hotkeys', () => { +describe('acture-hotkeys', () => { beforeEach(() => { // jsdom defaults to 'Linux' navigator.platform — tinykeys treats // non-Mac as Ctrl for $mod, which is what we want for these tests. diff --git a/packages/hotkeys/src/index.ts b/packages/hotkeys/src/index.ts index 4f21301..4b17e61 100644 --- a/packages/hotkeys/src/index.ts +++ b/packages/hotkeys/src/index.ts @@ -1,5 +1,5 @@ /** - * `@acture/hotkeys` — keyboard-shortcut adapter. + * `acture-hotkeys` — keyboard-shortcut adapter. * * Reads `record.keybinding` off every command in a registry, binds it * via [tinykeys](https://github.com/jamiebuilds/tinykeys), and dispatches @@ -15,14 +15,14 @@ * * Surface (plain DOM): * - * import { bindHotkeys } from '@acture/hotkeys'; + * import { bindHotkeys } from 'acture-hotkeys'; * const stop = bindHotkeys(registry, { contextProvider: () => myCtx }); * // ...later * stop(); * * Surface (React, optional sub-export): * - * import { useHotkeys } from '@acture/hotkeys/react'; + * import { useHotkeys } from 'acture-hotkeys/react'; * useHotkeys(registry, { context }); */ diff --git a/packages/hotkeys/src/react.ts b/packages/hotkeys/src/react.ts index 88844ca..7425bd9 100644 --- a/packages/hotkeys/src/react.ts +++ b/packages/hotkeys/src/react.ts @@ -2,7 +2,7 @@ * Optional React entry-point. Imports React lazily so a host that * never uses the React hook does not pull React into its bundle. * - * import { useHotkeys } from '@acture/hotkeys/react'; + * import { useHotkeys } from 'acture-hotkeys/react'; */ import { useEffect, useRef } from 'react'; diff --git a/packages/mcp/README.md b/packages/mcp/README.md index a24e716..924ddfc 100644 --- a/packages/mcp/README.md +++ b/packages/mcp/README.md @@ -1,11 +1,13 @@ -# @acture/mcp +# acture-mcp + +> **acture is a development tool first.** This package is an *optional accelerator* — an agent can hand-write this integration into your project instead, with no `acture-*` dependency. Installing it is a deliberate, opt-in choice to reuse tested code rather than own it. See [`docs/positioning.md`](../../docs/positioning.md). Project an [acture](https://npm.im/acture) registry as a [Model Context Protocol](https://modelcontextprotocol.io) server. Every command becomes a tool; tier-filtered by default; errors are returned as data. ## Install ```sh -pnpm add @acture/mcp @modelcontextprotocol/sdk acture +pnpm add acture-mcp @modelcontextprotocol/sdk acture ``` ## The two layers @@ -13,7 +15,7 @@ pnpm add @acture/mcp @modelcontextprotocol/sdk acture ### Pure functions (transport-agnostic) ```ts -import { buildToolsList, callTool } from '@acture/mcp'; +import { buildToolsList, callTool } from 'acture-mcp'; const tools = buildToolsList(registry); // tier: ['stable'] by default const response = await callTool(registry, 'app.search', { query: 'foo' }); @@ -25,7 +27,7 @@ Use these from any transport — stdio, HTTP, in-browser WebSocket, custom. ### Node-side stdio server (the common path) ```ts -import { createMcpServer, connectStdio } from '@acture/mcp'; +import { createMcpServer, connectStdio } from 'acture-mcp'; import { registry } from './registry'; const server = createMcpServer(registry, { diff --git a/packages/mcp/package.json b/packages/mcp/package.json index 94f6fa6..778a686 100644 --- a/packages/mcp/package.json +++ b/packages/mcp/package.json @@ -1,5 +1,5 @@ { - "name": "@acture/mcp", + "name": "acture-mcp", "version": "1.0.0", "description": "Project an acture registry as an MCP server. Tier-filtered tools/list, deprecation-banner prefixes, errors-as-data Result responses.", "license": "Apache-2.0", diff --git a/packages/mcp/src/index.ts b/packages/mcp/src/index.ts index 61e410e..bf3a7fe 100644 --- a/packages/mcp/src/index.ts +++ b/packages/mcp/src/index.ts @@ -1,5 +1,5 @@ /** - * `@acture/mcp` — project an acture registry as an MCP server. + * `acture-mcp` — project an acture registry as an MCP server. * * Per `acture-schema-bridge` and `acture-tier-system` skills: * diff --git a/packages/migration/README.md b/packages/migration/README.md index fcbb600..e7bce0f 100644 --- a/packages/migration/README.md +++ b/packages/migration/README.md @@ -1,4 +1,6 @@ -# @acture/migration +# acture-migration + +> **acture is a development tool first.** This package is an *optional accelerator* — an agent can hand-write this integration into your project instead, with no `acture-*` dependency. Installing it is a deliberate, opt-in choice to reuse tested code rather than own it. See [`docs/positioning.md`](../../docs/positioning.md). Strangler-fig adoption primitives for bringing acture into an existing app without a rewrite. @@ -21,13 +23,13 @@ migration-diagnose → migration-plan → migration-scaffold → migration-wrap ## Install ```bash -pnpm add @acture/migration acture zod +pnpm add acture-migration acture zod ``` ## `wrapMutation` — the load-bearing primitive ```ts -import { wrapMutation } from '@acture/migration'; +import { wrapMutation } from 'acture-migration'; import { registry } from './acture/registry'; import { useTodoStore } from './store'; @@ -53,7 +55,7 @@ Now `registry.dispatch('app.todo.add', { text: '…' })` works — the palette, ```ts import { configureStore } from '@reduxjs/toolkit'; -import { actureMiddleware } from '@acture/migration'; +import { actureMiddleware } from 'acture-migration'; import { registry } from './acture/registry'; export const store = configureStore({ @@ -74,7 +76,7 @@ See `examples/migration/redux-wrap/` for a worked example exercising both paths ## `createDomInterceptor` — DOM-event interception ```ts -import { createDomInterceptor } from '@acture/migration'; +import { createDomInterceptor } from 'acture-migration'; import { registry } from './acture/registry'; const mount = createDomInterceptor(registry, { @@ -103,7 +105,7 @@ A single delegated listener per event type (default: `click`, `submit`, `change` ## `chooseImplementation` — feature-flag bridge ```ts -import { chooseImplementation } from '@acture/migration'; +import { chooseImplementation } from 'acture-migration'; import { flags } from './flags'; export const submit = chooseImplementation( @@ -117,7 +119,7 @@ Five lines, no flag-SDK opinion baked in. Pair with LaunchDarkly / Statsig / Unl ## `shadowCompare` — Scientist-style A/B ```ts -import { shadowCompare } from '@acture/migration'; +import { shadowCompare } from 'acture-migration'; export const search = shadowCompare( (q: string) => newSearch(q), // modern — always returned @@ -140,6 +142,6 @@ Earlier sketches included a `divertHandler(commandId, { legacy, modern, predicat ## Codemods -See [`@acture/codemods`](../codemods/README.md). The runtime-first principle of research-4 §B.1 still holds — start by wrapping handlers manually, lift to codemods only when scale demands it. +See [`acture-codemods`](../codemods/README.md). The runtime-first principle of research-4 §B.1 still holds — start by wrapping handlers manually, lift to codemods only when scale demands it. See [`.claude/skills/acture-migration-package/SKILL.md`](../../.claude/skills/acture-migration-package/SKILL.md) for the architectural rationale. diff --git a/packages/migration/package.json b/packages/migration/package.json index 5118b69..d27f605 100644 --- a/packages/migration/package.json +++ b/packages/migration/package.json @@ -1,5 +1,5 @@ { - "name": "@acture/migration", + "name": "acture-migration", "version": "1.1.0", "description": "Strangler-fig migration primitives for adopting acture in an existing codebase. Four functions: wrapMutation, actureMiddleware, chooseImplementation, shadowCompare.", "license": "Apache-2.0", diff --git a/packages/migration/src/index.ts b/packages/migration/src/index.ts index 26517b4..a51003d 100644 --- a/packages/migration/src/index.ts +++ b/packages/migration/src/index.ts @@ -1,5 +1,5 @@ /** - * `@acture/migration` — strangler-fig adoption primitives. + * `acture-migration` — strangler-fig adoption primitives. * * Four functions per research-4 §A.6: * - `wrapMutation` — wrap an existing handler as a command without diff --git a/packages/palette-react/README.md b/packages/palette-react/README.md index 41de307..1d1720c 100644 --- a/packages/palette-react/README.md +++ b/packages/palette-react/README.md @@ -1,4 +1,6 @@ -# @acture/palette-react +# acture-palette-react + +> **acture is a development tool first.** This package is an *optional accelerator* — an agent can hand-write this integration into your project instead, with no `acture-*` dependency. Installing it is a deliberate, opt-in choice to reuse tested code rather than own it. See [`docs/positioning.md`](../../docs/positioning.md). Command palette for [acture](https://npm.im/acture). Wraps [cmdk](https://cmdk.paco.me) and reads commands directly off the registry. @@ -22,18 +24,18 @@ The `kind` is auto-derived from the schema unless `record.kind` is set explicitl ## Install ```sh -pnpm add @acture/palette-react cmdk react +pnpm add acture-palette-react cmdk react # pick a form adapter for handoff commands: -pnpm add @acture/forms-autoform # Zod-native (recommended) +pnpm add acture-forms-autoform # Zod-native (recommended) # or -pnpm add @acture/forms-rjsf # JSON-Schema-native +pnpm add acture-forms-rjsf # JSON-Schema-native ``` ## Usage ```tsx -import { CommandPalette } from '@acture/palette-react'; -import { AutoForm } from '@acture/forms-autoform'; +import { CommandPalette } from 'acture-palette-react'; +import { AutoForm } from 'acture-forms-autoform'; import { registry } from './registry'; function PaletteOverlay({ context, onClose }) { @@ -89,5 +91,5 @@ Every meaningful node carries a `data-acture-*` attribute. Style with CSS — th ## See also - [acture-palette-design](https://github.com/thorwhalen/acture/blob/main/.claude/skills/acture-palette-design/SKILL.md) — the design rationale -- [@acture/forms-autoform](../forms-autoform) — Zod-native form adapter -- [@acture/forms-rjsf](../forms-rjsf) — JSON-Schema-native form adapter +- [acture-forms-autoform](../forms-autoform) — Zod-native form adapter +- [acture-forms-rjsf](../forms-rjsf) — JSON-Schema-native form adapter diff --git a/packages/palette-react/package.json b/packages/palette-react/package.json index cf4873f..843f1d9 100644 --- a/packages/palette-react/package.json +++ b/packages/palette-react/package.json @@ -1,5 +1,5 @@ { - "name": "@acture/palette-react", + "name": "acture-palette-react", "version": "1.0.0", "description": "acture command palette for React — minimal Phase 1 version. Parameter-free commands only.", "license": "Apache-2.0", diff --git a/packages/palette-react/src/index.ts b/packages/palette-react/src/index.ts index c9065c3..de41543 100644 --- a/packages/palette-react/src/index.ts +++ b/packages/palette-react/src/index.ts @@ -1,12 +1,12 @@ /** - * `@acture/palette-react` — command palette for React. + * `acture-palette-react` — command palette for React. * * Phase 2 surface: * - List view groups commands by category, sorted by `defaultScore`. * - Parameterized commands route through `deriveKind`: * atomic → in-palette picker chain (Linear/Discord-style) - * handoff → host-supplied form adapter (`@acture/forms-autoform` - * or `@acture/forms-rjsf`) rendered inline, OR a + * handoff → host-supplied form adapter (`acture-forms-autoform` + * or `acture-forms-rjsf`) rendered inline, OR a * fallback callback for the host to open its own view. * - Listens for `commandsChanged` and rebuilds the list incrementally. * diff --git a/packages/palette-react/src/palette.tsx b/packages/palette-react/src/palette.tsx index 32bcf8b..4795cc0 100644 --- a/packages/palette-react/src/palette.tsx +++ b/packages/palette-react/src/palette.tsx @@ -64,7 +64,7 @@ export interface CommandPaletteProps { onParameterizedSelect?: (cmd: AnyCommandRecord) => void; /** Optional form adapter for `handoff` commands. When present, the * palette switches its inner view to this component instead of - * closing. Plug in `@acture/forms-autoform` or `@acture/forms-rjsf`. */ + * closing. Plug in `acture-forms-autoform` or `acture-forms-rjsf`. */ formAdapter?: PaletteFormAdapter; /** Per-field defaults injected into the picker chain / form. Useful * for context-aware prefill (Things-style — research-2 §9.4). */ diff --git a/packages/state-redux/README.md b/packages/state-redux/README.md index d625843..db83cfd 100644 --- a/packages/state-redux/README.md +++ b/packages/state-redux/README.md @@ -1,4 +1,6 @@ -# @acture/state-redux +# acture-state-redux + +> **acture is a development tool first.** This package is an *optional accelerator* — an agent can hand-write this integration into your project instead, with no `acture-*` dependency. Installing it is a deliberate, opt-in choice to reuse tested code rather than own it. See [`docs/positioning.md`](../../docs/positioning.md). Reference state adapter binding [acture](https://npm.im/acture)'s `StateAdapter` to [Redux Toolkit](https://redux-toolkit.js.org/). Implements `PatchCapableAdapter` via Immer (already bundled with RTK). @@ -7,7 +9,7 @@ Reference state adapter binding [acture](https://npm.im/acture)'s `StateAdapter< ### 1. Fresh single-slice store (greenfield) ```ts -import { createReduxAdapter } from '@acture/state-redux'; +import { createReduxAdapter } from 'acture-state-redux'; interface AppState { count: number } @@ -26,7 +28,7 @@ state.applyPatches(inversePatches); ```ts import { configureStore, createSlice } from '@reduxjs/toolkit'; -import { wrapReduxStore } from '@acture/state-redux'; +import { wrapReduxStore } from 'acture-state-redux'; const slice = createSlice({ name: 'graph', @@ -57,4 +59,4 @@ RTK's `store.subscribe(listener)` calls the listener with **no arguments** (unli ## See also - [`acture-state-adapter`](https://github.com/thorwhalen/acture/blob/main/.claude/skills/acture-state-adapter/SKILL.md) — the contract -- [`@acture/state-zustand`](../state-zustand) — sibling adapter for zustand+immer +- [`acture-state-zustand`](../state-zustand) — sibling adapter for zustand+immer diff --git a/packages/state-redux/package.json b/packages/state-redux/package.json index 63c7edb..577b5a2 100644 --- a/packages/state-redux/package.json +++ b/packages/state-redux/package.json @@ -1,5 +1,5 @@ { - "name": "@acture/state-redux", + "name": "acture-state-redux", "version": "1.0.0", "description": "acture StateAdapter for Redux Toolkit (RTK). Wraps an RTK store as a PatchCapableAdapter via RTK's bundled Immer.", "license": "Apache-2.0", diff --git a/packages/state-redux/src/index.test.ts b/packages/state-redux/src/index.test.ts index ff5263d..67e82d7 100644 --- a/packages/state-redux/src/index.test.ts +++ b/packages/state-redux/src/index.test.ts @@ -9,7 +9,7 @@ interface AppState { const initial: AppState = { count: 0, items: ['a'] }; -describe('@acture/state-redux — createReduxAdapter', () => { +describe('acture-state-redux — createReduxAdapter', () => { it('exposes getState / setState / subscribe', () => { const adapter = createReduxAdapter({ initialState: initial }); expect(adapter.getState()).toEqual(initial); @@ -74,7 +74,7 @@ describe('@acture/state-redux — createReduxAdapter', () => { }); }); -describe('@acture/state-redux — wrapReduxStore', () => { +describe('acture-state-redux — wrapReduxStore', () => { it('treats one slice of an existing RTK store as acture state', () => { const counter = createSlice({ name: 'counter', diff --git a/packages/state-redux/src/index.ts b/packages/state-redux/src/index.ts index 6662a66..e8e5268 100644 --- a/packages/state-redux/src/index.ts +++ b/packages/state-redux/src/index.ts @@ -1,8 +1,8 @@ /** - * `@acture/state-redux` — Redux Toolkit reference adapter. + * `acture-state-redux` — Redux Toolkit reference adapter. * * Wraps an RTK store as `PatchCapableAdapter`. RTK bundles Immer, so - * the patches story is identical to `@acture/state-zustand` — same + * the patches story is identical to `acture-state-zustand` — same * `produceWithPatches` plumbing. * * Two forms: diff --git a/packages/state-zustand/README.md b/packages/state-zustand/README.md index 91cabd3..b46ec24 100644 --- a/packages/state-zustand/README.md +++ b/packages/state-zustand/README.md @@ -1,11 +1,13 @@ -# @acture/state-zustand +# acture-state-zustand + +> **acture is a development tool first.** This package is an *optional accelerator* — an agent can hand-write this integration into your project instead, with no `acture-*` dependency. Installing it is a deliberate, opt-in choice to reuse tested code rather than own it. See [`docs/positioning.md`](../../docs/positioning.md). Phase 1 reference adapter binding [acture](https://npm.im/acture)'s `StateAdapter` interface to [zustand](https://github.com/pmndrs/zustand) + [immer](https://immerjs.github.io/immer/). -Implements `PatchCapableAdapter` — patches produced via `produceWithPatches` so the future `@acture/undo` subsystem has the substrate it needs. +Implements `PatchCapableAdapter` — patches produced via `produceWithPatches` so the future `acture-undo` subsystem has the substrate it needs. ```ts -import { createZustandAdapter } from '@acture/state-zustand'; +import { createZustandAdapter } from 'acture-state-zustand'; interface AppState { count: number } diff --git a/packages/state-zustand/package.json b/packages/state-zustand/package.json index 00ff174..2245add 100644 --- a/packages/state-zustand/package.json +++ b/packages/state-zustand/package.json @@ -1,5 +1,5 @@ { - "name": "@acture/state-zustand", + "name": "acture-state-zustand", "version": "1.0.0", "description": "acture StateAdapter for zustand + zustand/middleware/immer. Phase 1's documented happy path.", "license": "Apache-2.0", diff --git a/packages/state-zustand/src/index.ts b/packages/state-zustand/src/index.ts index ff1c14d..376c6ce 100644 --- a/packages/state-zustand/src/index.ts +++ b/packages/state-zustand/src/index.ts @@ -1,5 +1,5 @@ /** - * `@acture/state-zustand` — Phase 1 reference state adapter. + * `acture-state-zustand` — Phase 1 reference state adapter. * * Implements `PatchCapableAdapter` from `acture` on top of * `zustand/vanilla` + `immer`. The vanilla store is essential — diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f30bd2d..41adcc1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,24 +14,24 @@ importers: examples/drop-in: dependencies: - '@acture/hotkeys': + '@modelcontextprotocol/sdk': + specifier: ^1.0.4 + version: 1.29.0(zod@4.4.3) + acture: + specifier: workspace:* + version: link:../../packages/core + acture-hotkeys: specifier: workspace:* version: link:../../packages/hotkeys - '@acture/mcp': + acture-mcp: specifier: workspace:* version: link:../../packages/mcp - '@acture/palette-react': + acture-palette-react: specifier: workspace:* version: link:../../packages/palette-react - '@acture/state-zustand': + acture-state-zustand: specifier: workspace:* version: link:../../packages/state-zustand - '@modelcontextprotocol/sdk': - specifier: ^1.0.4 - version: 1.29.0(zod@4.4.3) - acture: - specifier: workspace:* - version: link:../../packages/core cmdk: specifier: ^1.0.0 version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -84,33 +84,33 @@ importers: examples/greenfield/graph-editor: dependencies: - '@acture/ai-vercel': + '@modelcontextprotocol/sdk': + specifier: ^1.0.4 + version: 1.29.0(zod@4.4.3) + acture: + specifier: workspace:* + version: link:../../../packages/core + acture-ai-vercel: specifier: workspace:* version: link:../../../packages/ai-vercel - '@acture/devtools': + acture-devtools: specifier: workspace:* version: link:../../../packages/devtools - '@acture/forms-autoform': + acture-forms-autoform: specifier: workspace:* version: link:../../../packages/forms-autoform - '@acture/hotkeys': + acture-hotkeys: specifier: workspace:* version: link:../../../packages/hotkeys - '@acture/mcp': + acture-mcp: specifier: workspace:* version: link:../../../packages/mcp - '@acture/palette-react': + acture-palette-react: specifier: workspace:* version: link:../../../packages/palette-react - '@acture/state-zustand': + acture-state-zustand: specifier: workspace:* version: link:../../../packages/state-zustand - '@modelcontextprotocol/sdk': - specifier: ^1.0.4 - version: 1.29.0(zod@4.4.3) - acture: - specifier: workspace:* - version: link:../../../packages/core ai: specifier: ^4.0.40 version: 4.3.19(react@19.2.6)(zod@4.4.3) @@ -172,18 +172,18 @@ importers: examples/migration/redux-wrap: dependencies: - '@acture/migration': - specifier: workspace:* - version: link:../../../packages/migration - '@acture/state-redux': - specifier: workspace:* - version: link:../../../packages/state-redux '@reduxjs/toolkit': specifier: ^2.5.0 version: 2.11.2(react@19.2.6) acture: specifier: workspace:* version: link:../../../packages/core + acture-migration: + specifier: workspace:* + version: link:../../../packages/migration + acture-state-redux: + specifier: workspace:* + version: link:../../../packages/state-redux zod: specifier: ^4.0.0 version: 4.4.3 @@ -197,21 +197,21 @@ importers: examples/migration/zustand-wrap/after: dependencies: - '@acture/hotkeys': + acture: + specifier: workspace:* + version: link:../../../../packages/core + acture-hotkeys: specifier: workspace:* version: link:../../../../packages/hotkeys - '@acture/migration': + acture-migration: specifier: workspace:* version: link:../../../../packages/migration - '@acture/palette-react': + acture-palette-react: specifier: workspace:* version: link:../../../../packages/palette-react - '@acture/state-zustand': + acture-state-zustand: specifier: workspace:* version: link:../../../../packages/state-zustand - acture: - specifier: workspace:* - version: link:../../../../packages/core cmdk: specifier: ^1.0.0 version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -313,15 +313,15 @@ importers: packages/build-tier: devDependencies: - '@acture/mcp': - specifier: workspace:* - version: link:../mcp '@types/node': specifier: ^22.10.0 version: 22.19.19 acture: specifier: workspace:* version: link:../core + acture-mcp: + specifier: workspace:* + version: link:../mcp ts-morph: specifier: ^25.0.0 version: 25.0.1