From 7b9068cd2b58b7ec0b9651a51aca0866826d53c4 Mon Sep 17 00:00:00 2001 From: oveddan Date: Mon, 31 Aug 2026 14:16:43 -0700 Subject: [PATCH] feat: install on a new machine in one step Installing this on a fresh machine did not work. The README and the skill both told the reader to `npm install -g mft-config`, but the package had never been published, so that command and the `npx -y mft-config` fallback the skill depends on both 404'd. The documented install path described something that did not exist. The CLI is now published to npm, which makes those instructions true, and the remaining steps collapse into one: - A Claude Code plugin. `.claude-plugin/plugin.json` points `skills` at `./.claude/skills`, so the plugin serves the same skill directory the repository already discovers rather than a second copy to keep in sync. With the marketplace manifest alongside it, installing is `/plugin marketplace add oveddan/mft-api` and `/plugin install`. - A release workflow, triggered by hand from the Actions tab. Nothing reaches npm without someone choosing to send it. It refuses a version already on the registry so a forgotten bump fails immediately, `prepack` runs the full check before anything uploads, and the commit is tagged only after the publish succeeds. Authentication is npm trusted publishing, so no write-capable token is stored anywhere. Codex keeps its curl one-liner: there is no marketplace to target there, and a single documented command is more honest than pretending parity. Verified against real hardware from a clean directory, resolving from the registry rather than this checkout: `npx -y mft-config list` discovers the controller on firmware 2026-07-02, and `export` returns all four banks of sixteen encoders with no warnings. Closes #8 Co-Authored-By: Claude Opus 5 --- .claude-plugin/marketplace.json | 16 ++++++++ .claude-plugin/plugin.json | 23 +++++++++++ .github/workflows/release.yml | 72 +++++++++++++++++++++++++++++++++ README.md | 43 +++++++++++++++++++- docs/agent-skill.md | 51 ++++++++++++++--------- 5 files changed, 184 insertions(+), 21 deletions(-) create mode 100644 .claude-plugin/marketplace.json create mode 100644 .claude-plugin/plugin.json create mode 100644 .github/workflows/release.yml diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000..476a9a0 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,16 @@ +{ + "name": "mft-api", + "owner": { + "name": "Dan Oved", + "url": "https://github.com/oveddan" + }, + "description": "MIDI Fighter Twister configuration tooling for coding agents.", + "plugins": [ + { + "name": "mft-configurator", + "source": "./", + "description": "Inspect and configure a DJ TechTools MIDI Fighter Twister in plain language, through the guarded mft-config CLI.", + "category": "hardware" + } + ] +} diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..d006e5c --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,23 @@ +{ + "name": "mft-configurator", + "displayName": "MIDI Fighter Twister Configurator", + "description": "Inspect and configure a DJ TechTools MIDI Fighter Twister in plain language, through the guarded mft-config CLI.", + "version": "0.1.0", + "author": { + "name": "Dan Oved", + "url": "https://github.com/oveddan" + }, + "homepage": "https://github.com/oveddan/mft-api#readme", + "repository": "https://github.com/oveddan/mft-api", + "license": "MIT", + "keywords": [ + "midi-fighter-twister", + "midi", + "djtechtools", + "hardware", + "sysex" + ], + "skills": [ + "./.claude/skills" + ] +} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0189d58 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,72 @@ +name: Release + +# Publishing is deliberately manual: Actions -> Release -> Run workflow. +# Nothing reaches npm without someone choosing to send it. +# +# The version comes from package.json. Bump it, merge, then run this. The job +# refuses a version that is already on npm, so a forgotten bump fails loudly +# here rather than as an opaque registry rejection after a full build. +# +# Authentication is npm trusted publishing (OIDC), so there is no NPM_TOKEN to +# rotate or leak. It has to be enabled once on npmjs.com: +# Settings -> Trusted publisher -> GitHub Actions -> this repo, workflow +# `release.yml`. Until that is done the publish step fails, and the fallback is +# `npm publish` from a logged-in machine. +on: + workflow_dispatch: + +jobs: + publish: + name: Publish to npm + runs-on: ubuntu-latest + permissions: + # Pushing the release tag. + contents: write + # Required for trusted publishing; without it npm falls back to looking + # for a token and fails. + id-token: write + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + registry-url: https://registry.npmjs.org + + # setup-node pins an npm that predates OIDC publishing on some images. + - name: Use an npm that supports trusted publishing + run: npm install -g npm@latest + + - run: pnpm install --frozen-lockfile + + - name: Read the version to publish + id: version + run: echo "version=$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT" + + # A forgotten version bump is the likeliest mistake here. `npm view` on an + # unpublished version exits 0 with empty output, so test the output rather + # than the exit status. + - name: Version is not already published + env: + VERSION: ${{ steps.version.outputs.version }} + run: | + if [ -n "$(npm view "mft-config@$VERSION" version 2>/dev/null)" ]; then + echo "mft-config@$VERSION is already on npm; bump the version in package.json first" >&2 + exit 1 + fi + echo "publishing mft-config@$VERSION" + + # `prepack` runs the full `check` (build + typecheck + tests), so this + # cannot publish something that does not build or pass tests. + - name: Publish + run: npm publish --provenance --access public + + # Only after a successful publish, so a tag never points at a release that + # did not happen. + - name: Tag the released commit + env: + VERSION: ${{ steps.version.outputs.version }} + run: | + git tag "v$VERSION" + git push origin "v$VERSION" diff --git a/README.md b/README.md index 1adf898..77885ea 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,32 @@ bootloader commands are always blocked. Live writes currently require firmware ## Install Requires Node.js 20 or newer. Prebuilt MIDI binaries ship for macOS, Windows, -and Linux, so no compiler is needed on common platforms. +and Linux, so no compiler is needed on common platforms. Nothing below needs a +clone, a build, or a local toolchain. + +### Claude Code + +Two commands, and the agent can talk to the controller: + +```sh +/plugin marketplace add oveddan/mft-api +/plugin install mft-configurator@mft-api +``` + +That installs the `mft-configurator` skill. It reaches the CLI through +`npx -y mft-config`, so there is no second install step — though a global +install (below) makes every command noticeably faster to start. + +### Codex + +Codex has no marketplace, so the skill is fetched directly. This copies only +the skill directory: + +```sh +mkdir -p ~/.codex/skills/mft-configurator && curl -fsSL https://github.com/oveddan/mft-api/archive/refs/heads/main.tar.gz | tar -xz --strip-components=4 -C ~/.codex/skills/mft-configurator mft-api-main/.claude/skills/mft-configurator +``` + +### The CLI on its own ```sh npm install -g mft-config @@ -158,6 +183,9 @@ npx -y mft-config list Every example below uses `mft-config`. The old `mft-export` name still works as a deprecated alias and will be removed in a future release. +Upgrading, removing, and the details of both agent installs are in +[docs/agent-skill.md](docs/agent-skill.md). + **Run every command from the same directory.** `mft-config` writes its backups and its single-use plan journal to `.mft-state/` relative to the current working directory, so switching directories between `plan` and `apply` consults a @@ -498,6 +526,19 @@ it steps around are real, and it is deliberately absent from the agent skill. On Linux you need the ALSA development package required by RtMidi if you are building the native dependency from source rather than using its prebuilds. +### Releasing + +Publishing is manual. Bump the version in `package.json`, merge it, then run +the **Release** workflow from the Actions tab. Nothing reaches npm without +someone choosing to send it. + +The workflow refuses a version that is already published, so a forgotten bump +fails immediately instead of part-way through. `prepack` runs the full check +before anything uploads, and the released commit is tagged `v` only +after the publish succeeds. Authentication is npm trusted publishing, so there +is no token stored in the repository — it has to be enabled once on npmjs.com, +as [.github/workflows/release.yml](.github/workflows/release.yml) describes. + ## License This project is licensed under the MIT License. See the [LICENSE](LICENSE) file diff --git a/docs/agent-skill.md b/docs/agent-skill.md index 5d1ee51..978d739 100644 --- a/docs/agent-skill.md +++ b/docs/agent-skill.md @@ -4,38 +4,47 @@ The `mft-configurator` skill lets Claude Code or Codex inspect and configure a MIDI Fighter Twister in plain language. It drives the `mft-config` CLI; it never talks to the device directly. -## 1. Install the CLI +No clone and no build is required for any path below. + +## Claude Code: install the plugin ```sh -npm install -g mft-config +/plugin marketplace add oveddan/mft-api +/plugin install mft-configurator@mft-api ``` -The skill also works with `npx -y mft-config` if you would rather not install -anything globally. +The plugin bundles the skill. `/plugin update` upgrades it, and +`/plugin uninstall mft-configurator@mft-api` removes it. -## 2. Install the skill +Working inside a checkout of this repository, Claude Code discovers the skill +from `.claude/skills/` automatically and the plugin is unnecessary. -No clone and no build — this fetches only the skill directory. +## Codex: fetch the skill directory -For Claude Code: +There is no marketplace equivalent, so this is a single command rather than +pretending parity exists. It fetches only the skill directory: ```sh -mkdir -p ~/.claude/skills/mft-configurator && curl -fsSL https://github.com/oveddan/mft-api/archive/refs/heads/main.tar.gz | tar -xz --strip-components=4 -C ~/.claude/skills/mft-configurator mft-api-main/.claude/skills/mft-configurator +mkdir -p ~/.codex/skills/mft-configurator && curl -fsSL https://github.com/oveddan/mft-api/archive/refs/heads/main.tar.gz | tar -xz --strip-components=4 -C ~/.codex/skills/mft-configurator mft-api-main/.claude/skills/mft-configurator ``` -For Codex, the same command with a different destination: +It overwrites in place, so re-running it upgrades. Delete the destination +directory first if you want files removed upstream to disappear too. -```sh -mkdir -p ~/.codex/skills/mft-configurator && curl -fsSL https://github.com/oveddan/mft-api/archive/refs/heads/main.tar.gz | tar -xz --strip-components=4 -C ~/.codex/skills/mft-configurator mft-api-main/.claude/skills/mft-configurator -``` +The same command with `~/.claude/skills/mft-configurator` as the destination +still works for Claude Code, if you would rather not use the plugin. -Both commands overwrite in place, so re-running them upgrades. Delete the -destination directory first if you want files removed upstream to disappear too. +## The CLI -Working inside a checkout of this repository, Claude Code discovers the skill -automatically and neither step 2 command is needed. +The skill invokes `npx -y mft-config` when the command is not on `PATH`, so +this is optional. Installing it globally removes the npx startup cost from +every command: + +```sh +npm install -g mft-config +``` -## 3. Use it +## Use it Invoke `/mft-configurator` in Claude Code or `$mft-configurator` in Codex, or just ask the agent to inspect or configure a Twister. @@ -47,13 +56,15 @@ controller requires a separate, explicit instruction. `SKILL.md` uses the portable Agent Skills format understood by both Claude Code and Codex. The optional `agents/openai.yaml`, generated with Codex's skill -creator, adds Codex UI metadata; Claude Code ignores it. +creator, adds Codex UI metadata; Claude Code ignores it. Both the plugin and the +Codex one-liner serve the same single copy of the skill in `.claude/skills/`, so +there is no second copy to keep in sync. **Run the agent from one consistent working directory.** `mft-config` writes its backups and its single-use plan journal to `.mft-state/` relative to the current working directory, so an `apply` run from a different directory than an earlier one consults a different journal. -A Claude Code plugin that installs the skill and an MCP server in a single step -is planned; see [#8](https://github.com/oveddan/mft-api/issues/8) and +An MCP server, so that one install serves any agent that speaks the protocol +rather than one skill copy per tool, is still open; see [#12](https://github.com/oveddan/mft-api/issues/12).