Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .changeset/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-server`, `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:

Expand Down
2 changes: 1 addition & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"acture-hotkeys",
"acture-forms-autoform",
"acture-forms-rjsf",
"acture-mcp",
"acture-mcp-server",
"acture-ai-vercel",
"acture-migration"
]
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/acture-architecture-primer/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ 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
3. **MCP server** (`acture-mcp-server`) — `{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<R>` reserves `patches?` and `effects?`
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/acture-command-record-shape/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ type CommandRecord<P = unknown, R = unknown> = {
tier?: "stable" | "experimental" | "internal" | "deprecated";

/** Free-text reason injected by the build step from @deprecated <reason>.
* Adapter packages (acture-mcp, acture-ai-vercel) prepend
* Adapter packages (acture-mcp-server, acture-ai-vercel) prepend
* `[DEPRECATED — <reason>]` to the description; acture-devtools surfaces
* it in the inspector. Added in v1.0 (Phase 4) under the rule of three. */
deprecationReason?: string;
Expand Down
6 changes: 3 additions & 3 deletions .claude/skills/acture-consumer-integration/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
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".
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-server, 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
Expand All @@ -22,7 +22,7 @@ Before writing anything, locate the task on both axes (see `docs/positioning.md`
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.
Dimension 2 is decided **per consumer**, not per project. Hand-write the palette, reuse `acture-mcp-server`, skip a state adapter — all in the same project — is normal.

## The decision procedure

Expand All @@ -46,7 +46,7 @@ Almost every consumer rests on *some* third-party library:
| 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` |
| MCP server | `@modelcontextprotocol/sdk` | `acture-mcp-server` |
| Parameter forms | react-hook-form + Zod, rjsf, custom | `acture-forms-autoform`, `acture-forms-rjsf` |
| e2e testing | Playwright, Cypress, custom | `acture-e2e-playwright` *(planned)* |

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/acture-hard-donts/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ 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-server` 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.

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/migration-scaffold/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ 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-mcp-server # if MCP is in scope
pnpm add acture-ai-vercel ai # if AI tool calling is in scope
```

Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ 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 — <reason>]`.
- Surfaces: `acture-mcp-server@1.0.0`, `acture-ai-vercel@1.0.0` — honour the tier filter and prepend `[DEPRECATED — <reason>]`.
- 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`).
Expand All @@ -117,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-<subpackage>` (e.g. `acture-state-zustand`, `acture-mcp`).
- Package naming: `acture` (default barrel), `acture-<subpackage>` (e.g. `acture-state-zustand`, `acture-mcp-server`).
- 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.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pnpm add acture # core registry + dispatcher + schema bridge (o
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-mcp-server # 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.
Expand All @@ -44,7 +44,7 @@ pnpm add -D eslint-plugin-acture-migration # ESLint rule that flags stale wrapM
| [`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-mcp-server`](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 |
Expand Down
4 changes: 2 additions & 2 deletions docs/next_session.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ Use `AskUserQuestion` for the scope fork. Do not guess.
- 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).

## Pre-flight note — publishing the renamed packages
## Note — all packages are published

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.
All 15 packages are live on npm as of 2026-05-14. The MCP adapter ships as **`acture-mcp-server`** (the unscoped name `acture-mcp` was already taken by an unrelated project). Nothing to publish before starting this review.

## When unsure

Expand Down
2 changes: 1 addition & 1 deletion docs/phase-1-reflection.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ Per `docs/implementation_plan.md` §"Phase 2 — Adapter buildout" the next phas
- 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-mcp-server` (MCP server adapter)
- `acture-ai-vercel` (Vercel AI SDK adapter)
- Extended worked examples + a new drop-in example

Expand Down
4 changes: 2 additions & 2 deletions docs/phase-2-reflection.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ This file answers the six questions from `docs/implementation_plan.md` §"Phase

- **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-server/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.

Expand Down Expand Up @@ -67,7 +67,7 @@ Ran `.claude/skills/acture-hard-donts/SKILL.md` against every new package.
- `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-mcp-server` — 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.
Expand Down
2 changes: 1 addition & 1 deletion docs/phase-4-reflection.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ 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.
3. **No business logic in adapter packages.** ✅ `acture-mcp-server` 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.
Expand Down
2 changes: 1 addition & 1 deletion docs/positioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Every acture engagement is positioned on two independent axes. Skills and docs m
- **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).
These are not either/or for a whole project. A team can hand-write the palette, reuse `acture-mcp-server`, 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

Expand Down
6 changes: 3 additions & 3 deletions docs/redesign_takeaways.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-server`, `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-server`, 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<string, Command>`. 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.
Expand Down Expand Up @@ -215,7 +215,7 @@ 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-mcp-server # 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.
Expand Down
2 changes: 1 addition & 1 deletion docs/reference_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-server`, `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."
Expand Down
Loading
Loading