From 3dad33e47582d236db4b3dbbd45862436ead1553 Mon Sep 17 00:00:00 2001 From: bastien Date: Wed, 30 Sep 2026 14:26:56 +0200 Subject: [PATCH 01/22] fix(settings): deny the npm global-install aliases The deny list matched `npm install -g` only; `npm i -g` and the `--global` spellings went through. --- settings.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/settings.json b/settings.json index a008190..c99289a 100644 --- a/settings.json +++ b/settings.json @@ -119,6 +119,9 @@ "Bash(systemctl *)", "Bash(service *)", "Bash(npm install -g *)", + "Bash(npm i -g *)", + "Bash(npm install --global *)", + "Bash(npm i --global *)", "Read(**/.env)", "Read(**/.env.*)", "Read(**/secrets/**)", From 4a96ec20b57abe83c5a946d85aaf49b3cc601ad3 Mon Sep 17 00:00:00 2001 From: bastien Date: Wed, 30 Sep 2026 14:26:57 +0200 Subject: [PATCH 02/22] docs(spec): higgsfield pack design --- .../2026-09-30-higgsfield-pack-design.md | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 docs/superpowers/specs/2026-09-30-higgsfield-pack-design.md diff --git a/docs/superpowers/specs/2026-09-30-higgsfield-pack-design.md b/docs/superpowers/specs/2026-09-30-higgsfield-pack-design.md new file mode 100644 index 0000000..66a425f --- /dev/null +++ b/docs/superpowers/specs/2026-09-30-higgsfield-pack-design.md @@ -0,0 +1,158 @@ +# Higgsfield pack: design + +Date: 2026-09-30. Contract: `.claude/tasks/contracts/2026-09-30-higgsfield-pack-1412.md` +(binding: request, clarifications, 14 acceptance criteria). + +## Goal + +Generate images, video, audio and brand media from Claude Code through the +Higgsfield CLI, and have `make plugin` reproduce the setup on any machine. +The pack costs nothing when unused: it is installed on disk, linked on demand. + +## Decisions (validated by the user) + +- Two toggles in `lib/toggle-external.sh`, both off by default, additive on + any profile: `higgsfield` (7 media skills) and `higgsfield-websites` (one + skill). +- `higgsfield-websites` is an aid for landing pages inside the existing + Design work stack and site rules: assets and references. Never + `higgsfield website create|deploy|publish`. +- Skills come from a git clone of `higgsfield-ai/skills` (tracks `main`), + not from `npx skills add`, which re-links all 8 skills on every refresh. +- CLI `@higgsfield/cli` at `latest` (21st precedent). +- Routing lines in `CLAUDE.global.md`: explicit ask → enable the toggle → + follow the skill. +- The two dead login offers already in `install-plugins.sh` (ctx7, 21st) are + fixed here: they test stdout, which is the `tee` pipe. +- `settings.json` denies the `npm i -g` aliases (done by hand, outside the + installer). + +## Units + +### `lib/higgsfield-skills.sh` (new, sourced) + +Owns everything both installers share. + +- `HIGGSFIELD_SKILLS_URL`: defaults to + `https://github.com/higgsfield-ai/skills.git`; an env value wins (tests + point it at a local fixture repo). +- `higgsfield_sync_skills `: `git clone --depth 1` into a `mktemp -d` + stage. Every `higgsfield-*/` directory of the stage that holds a + `SKILL.md` replaces `/skills-external/` (rm, then mv). Prints + the number of skills synced. Returns non-zero, leaving the existing copies + untouched, when the clone fails or yields no skill. Always removes the + stage. Nothing else from upstream is kept (`setup`, `scripts/`, plugin + manifests, `.git`). +- `higgsfield_signed_in`: `timeout 15 higgsfield auth token /dev/null 2>&1`. The CLI is closed source, so the call is bounded and its + output never reaches a terminal or a log. + +Depends on: `git`, `mktemp`, `timeout`. No dependency on the repo's other +libs. + +### `install-plugins.sh`: Step 8.6, between 8.5 and 8.7 + +1. CLI: present → `ok`. Absent → `npm install -g @higgsfield/cli` (or the + pinned version from `plugins.lock.json`), then `higgsfield version` as + the proof; failure prints the manual command, including the + `--allow-scripts=@higgsfield/cli` form, since the package vendors its + binary in a postinstall script that newer npm versions may hold back. +2. Skills: only when the CLI is present, `higgsfield_sync_skills "$REPO"`. + A failed refresh with an existing copy is an `ok` (copy kept); with no + copy, a `warn` naming the manual command. +3. Sign-in: signed in → `ok`. Otherwise, stdin is a terminal → offer + `higgsfield auth login` (default no). Else an `info` line with the + command. +4. Nothing is linked. The summary block lists the pack and both toggles. + +Step 8.6 runs before the `apply_effort_pins` call of Step 8.7, so the +"pins after the last vendoring step" order holds (BDR-108). + +The ctx7 (Step 6) and 21st (Step 8.7) login offers change from +`[ -t 0 ] && [ -t 1 ]` to `[ -t 0 ]`. + +### `update-all.sh`: block before 7.4 + +CLI absent → `info`, skip. Else `npm install -g` the lock version, then +`higgsfield_sync_skills "$REPO"`. A parked skill is a symlink in +`skills-disabled/` pointing at `skills-external/`: replacing the +source keeps the parked state. + +### `lib/toggle-external.sh` + +- `MANAGED_TOOLS` gains `higgsfield` and `higgsfield-websites`. +- `higgsfield_skills()`: the `skills-external/higgsfield-*/` directories + holding a `SKILL.md`, minus `higgsfield-websites`. +- The three pack arms (status, disable, enable) serve `21st|higgsfield` + through `pack_skills `, which dispatches to the right enumerator. + Messages use the tool name. Status is `enabled` when any member is linked. +- After enabling a pack, a per-tool hint warns (never blocks) when the CLI + is missing or signed out. For Higgsfield the probe is inlined: this script + takes no new `source` (four fixture suites copy it). +- `higgsfield-websites` joins the single-symlink arm, source + `skills-external/higgsfield-websites`. +- Header: two tool lines after `21st`; `usage()` prints two more lines. + +### `doctor.sh`: section 4 + +CLI present → `pass` with its version; absent → `info` with the install +command. When present: signed in → `pass`, else `info` naming +`higgsfield auth login`. Never `warn`, never `fail`. + +### Config and docs + +- `plugins.lock.json`: `higgsfield` entry (source, version, note naming the + skills repo and the toggles; no `managed_by`). +- `.gitignore`: `skills/higgsfield-*` and `skills-external/higgsfield-*/`. +- `CLAUDE.global.md`, Skill routing, 8 lines (306 → 314, guard 320): + + ``` + - Media generation (image, video, audio, brand kit), explicit ask → + Higgsfield pack, off by default: `bash ~/.claude/lib/toggle-external.sh + enable higgsfield`, then its skill (not listed yet → Read its SKILL.md + under `~/.claude/skills/`). Metered: `higgsfield generate cost` before + a paid run. Landing page "with Higgsfield", named ask only → `enable + higgsfield-websites` as an aid (assets, references) inside the Design + work stack and the site rules above; never `higgsfield website + create|deploy|publish`. + ``` +- `README.md`: `### Higgsfield CLI` after the 21st section. +- `CHANGELOG.md`: one `### Added` bullet under `[Unreleased]`. + +## Not changed, on purpose + +`link.sh`, `lib/profile.sh`, every `lib/profiles/*.profile`, +`lib/effort-pins.txt`, hooks. Listing the pack in any of them would either +re-enable it on every `make link` or pull it into the profile census. + +## Error handling + +| Case | Behaviour | +|---|---| +| npm install fails or the binary is not vendored | `err` with the manual command; the step goes on, skills skipped | +| clone fails, copy exists | copy kept, `ok` | +| clone fails, no copy | `warn` with the manual command | +| upstream layout changes (no `higgsfield-*/SKILL.md`) | sync returns non-zero, copies kept, `warn` | +| toggle enabled, CLI missing or signed out | links created, `warn` | +| `enable` with no source | `err` naming the path checked, rc 1 | + +## Tests: `lib/tests/higgsfield.test.sh` + +Hermetic: a mktemp repo holding copies of `toggle-external.sh`, +`gstack-removed.sh` and `higgsfield-skills.sh`; a local git repo as the +skills source; a fake `higgsfield` first on PATH, switchable between signed +in and signed out. + +Named cases (the contract's oracle greps them): `SYNC_MOVES_PACK_ONLY`, +`SYNC_REFRESH_DROPS_STALE`, `SYNC_KEEPS_PARKED`, `SYNC_FAIL_KEEPS_COPY`, +`ENABLE_PACK_EXCLUDES_WEBSITES`, `ENABLE_WEBSITES_ALONE`, `DISABLE_PARKS`, +`STATUS_STATES`, `SIGNED_OUT_WARNS`. Plus static locks on the two installers: +the sync call sits before the last `apply_effort_pins`, and no `-t 1` test +remains in `install-plugins.sh`. + +## Known limits + +- Upstream prompts change with no diff to review (same trade-off as 21st). +- Each upstream skill reinstalls the CLI through `curl | sh` when + `higgsfield` is off PATH. With the CLI installed by npm this never fires. +- `higgsfield auth token` locality is unverified. From 64d094f93a2ac482a1e4a46a865e67c92fb56517 Mon Sep 17 00:00:00 2001 From: bastien Date: Wed, 30 Sep 2026 14:45:45 +0200 Subject: [PATCH 03/22] docs(plan): higgsfield pack implementation plan --- .../plans/2026-09-30-higgsfield-pack.md | 1342 +++++++++++++++++ .../01-gitignore.patch | 27 + .../01-lock.patch | 14 + .../02-higgsfield-skills.sh | 53 + .../02-higgsfield.test.sh | 114 ++ .../03-suite.patch | 153 ++ .../03-toggle-external.patch | 200 +++ .../04-install-plugins.patch | 123 ++ .../04-suite.patch | 32 + .../05-suite.patch | 19 + .../05-update-all.patch | 44 + .../06-doctor.patch | 30 + .../07-changelog.patch | 26 + .../07-readme.patch | 49 + .../08-claude-global.patch | 17 + 15 files changed, 2243 insertions(+) create mode 100644 docs/superpowers/plans/2026-09-30-higgsfield-pack.md create mode 100644 docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/01-gitignore.patch create mode 100644 docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/01-lock.patch create mode 100644 docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/02-higgsfield-skills.sh create mode 100644 docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/02-higgsfield.test.sh create mode 100644 docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/03-suite.patch create mode 100644 docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/03-toggle-external.patch create mode 100644 docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/04-install-plugins.patch create mode 100644 docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/04-suite.patch create mode 100644 docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/05-suite.patch create mode 100644 docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/05-update-all.patch create mode 100644 docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/06-doctor.patch create mode 100644 docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/07-changelog.patch create mode 100644 docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/07-readme.patch create mode 100644 docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/08-claude-global.patch diff --git a/docs/superpowers/plans/2026-09-30-higgsfield-pack.md b/docs/superpowers/plans/2026-09-30-higgsfield-pack.md new file mode 100644 index 0000000..a046d7a --- /dev/null +++ b/docs/superpowers/plans/2026-09-30-higgsfield-pack.md @@ -0,0 +1,1342 @@ +# Higgsfield Pack Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use subagent-driven-development to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** `make plugin` installs the Higgsfield CLI and its eight skills, off by default, with two toggles to turn them on. + +**Architecture:** A sourced helper (`lib/higgsfield-skills.sh`) clones the upstream skills into gitignored `skills-external/higgsfield-*`; both installers call it. `lib/toggle-external.sh` links the skills on demand through two tools, `higgsfield` (media pack) and `higgsfield-websites` (single skill). Nothing is listed in `link.sh` or in a profile, which is what keeps the pack off across re-runs. + +**Tech Stack:** bash, git, npm (run by the user only), shellcheck, hermetic suites under `lib/tests/` run through `make test`. + +**Spec:** `docs/superpowers/specs/2026-09-30-higgsfield-pack-design.md` +**Contract:** `.claude/tasks/contracts/2026-09-30-higgsfield-pack-1412.md` (14 criteria, binding) + +## How this plan is packaged + +Every edit below was dry-run in a scratch copy: the final suite prints 14 +PASS, shellcheck is clean, and each patch applies to the branch. The exact +bytes live in `docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/`. Each task shows its code inline for reading and names +the file to apply. Apply the file, never a retyped copy. + +Apply a patch with `git apply `. If `git apply` refuses (the target +moved), stop and report `BLOCKED` with the error. Do not hand-merge. + +## Global Constraints + +- Executors never run `install-plugins.sh`, `update-all.sh`, `link.sh`, `doctor.sh`, `npm install`, `npx`, `lib/toggle-external.sh` against the real repo, or `higgsfield_sync_skills` against the network (BDR-095). The suite is the only thing that runs. +- Tests run through `make test suite=lib/tests/` only. Never call a suite with `bash` directly, never prefix the command with env variables. +- Nothing about Higgsfield goes in `link.sh`, `lib/profile.sh`, `lib/profiles/*.profile` or `lib/effort-pins.txt` (BDR-093, BDR-079, BDR-105, BDR-107). The suite locks this. +- The skill sync sits before the last `apply_effort_pins "$REPO"` in `install-plugins.sh` and `update-all.sh` (BDR-108, BLK-024). +- `lib/toggle-external.sh` takes no new top-level `source` (LRN-178). +- The CLI token never reaches a terminal or a log: every `higgsfield auth token` call redirects to `/dev/null`. +- House limits for new code: functions of 25 logic lines at most, 80 columns, 5 parameters, 5 locals; shellcheck clean; comments state intent. +- Commits: explicit paths only (`git add `), never `git add -A`, never `--no-verify`, no attribution trailer. The hooks push each commit. +- `CLAUDE.global.md` and `settings.json` are hand-curated (BDR-028): only the orchestrator touches them. + +## Review Focus + +1. A refresh while the pack is enabled: the live `skills/` link must keep resolving. Pinned by `live-reads` in `SYNC_KEEPS_PARKED` (Task 2). +2. Upstream changes its layout (no `higgsfield-*/SKILL.md`): the sync must return non-zero and keep the existing copies. Pinned by `no-skills` in `SYNC_FAIL_KEEPS_COPY` (Task 2). +3. `enable higgsfield` on a machine with no CLI on PATH: links are created, one warning, exit 0. Pinned by `absent-*` in `SIGNED_OUT_WARNS` (Task 3). +4. npm holds back the package's postinstall script, so the shim exists but the binary does not: the installer must print the `--allow-scripts=` remedy. Pinned by `remedy` in `INSTALL_WIRING` (Task 4). +5. The generalised pack arms must not change what `21st` prints or does. Pinned by `PACK_21ST_UNCHANGED` (Task 3). + +--- + +### Task 1: Lock entry and gitignore + +**Files:** +- Modify: `plugins.lock.json` (new `higgsfield` entry before `graphifyy`) +- Modify: `.gitignore` (link side after `skills/21st-*`, source side after `skills-external/21st-*/`) + +**Interfaces:** +- Consumes: nothing. +- Produces: lock key `higgsfield` with `version` (read by Tasks 4 and 5); ignore rules `skills/higgsfield-*` and `skills-external/higgsfield-*/` (LRN-025: both states of a toggleable artifact). + +- [ ] **Step 1: Apply the lock patch** + +Run: `git apply docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/01-lock.patch` + +````diff +--- a/plugins.lock.json ++++ b/plugins.lock.json +@@ -25,6 +25,11 @@ + "version": "latest", + "note": "21st.dev CLI (bin `21st`) — standalone CLI + a pack of 7 skills, no MCP, no API key: auth is `21st login` (browser token in ~/.config/21st). Install: npm install -g @21st-dev/cli. The skill pack is staged-installed into skills-external/21st-* by install-plugins.sh Step 8.7 — `21st skills install` refuses to write through the ~/.claude/skills symlink." + }, ++ "higgsfield": { ++ "source": "npm:@higgsfield/cli", ++ "version": "latest", ++ "note": "Higgsfield CLI (bins `higgsfield`, `higgs`) — image, video, audio and brand media generation, metered credits; auth is `higgsfield auth login` (browser). Install: npm install -g @higgsfield/cli. The package vendors its binary in a postinstall script; if npm holds it back, add --allow-scripts=@higgsfield/cli. The 8 skills are git-cloned from https://github.com/higgsfield-ai/skills (tracks main, no pin) into skills-external/higgsfield-* by lib/higgsfield-skills.sh (install-plugins.sh Step 8.6, refreshed by update-all.sh). OFF by default and in no profile: `lib/toggle-external.sh enable higgsfield` links the 7 media skills, `enable higgsfield-websites` the landing-page aid." ++ }, + "graphifyy": { + "source": "pypi:graphifyy", + "version": "latest", +```` + +- [ ] **Step 2: Apply the gitignore patch** + +Run: `git apply docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/01-gitignore.patch` + +````diff +--- a/.gitignore ++++ b/.gitignore +@@ -101,6 +101,12 @@ + # membership, so the pack can gain a skill with no edit here. + skills/21st-* + ++# Higgsfield skill pack symlinks — created on demand by toggle-external.sh ++# (`enable higgsfield` / `enable higgsfield-websites`). The pack is OFF by ++# default and in no profile, so these usually don't exist. A glob: the ++# upstream repo owns the membership. ++skills/higgsfield-* ++ + # Context7 docs-lookup skill — installed by `ctx7 setup --claude --cli` + # (install-plugins.sh Step 6, when absent) into ~/.claude/skills (a symlink to + # this repo's skills/). ctx7-managed and re-created on demand — not vendored here. +@@ -236,6 +242,11 @@ + # layout and the content is sha256-verified against 21st.dev's manifest. + skills-external/21st-*/ + ++# Higgsfield skill pack — machine-owned: a git clone of higgsfield-ai/skills, ++# staged by lib/higgsfield-skills.sh (install-plugins.sh Step 8.6) and moved ++# here, refreshed by update-all.sh. Not vendored: it tracks upstream main. ++skills-external/higgsfield-*/ ++ + # npx `skills add` project-scope artifacts — darwin-skill copies itself into + # the repo's .agents/ and writes skills-lock.json at root. Our own agents live + # in agents/ (no dot) and stay tracked. Anchored to root so only the dotted +```` + +- [ ] **Step 3: Verify** + +Run: +```bash +python3 -c "import json;d=json.load(open('plugins.lock.json'))['higgsfield'];assert d['version']=='latest' and 'managed_by' not in d;print('LOCK_OK')" +git check-ignore -q skills/higgsfield-generate && git check-ignore -q skills-external/higgsfield-generate/SKILL.md && echo IGNORED_BOTH +git check-ignore -q skills/feat/SKILL.md || echo CONTROL_OK +``` +Expected: `LOCK_OK`, `IGNORED_BOTH`, `CONTROL_OK` (a tracked skill is not ignored). + +- [ ] **Step 4: Commit** + +```bash +git add plugins.lock.json .gitignore +git commit -m "chore(higgsfield): lock entry and gitignore for the skill pack" +``` + +--- + +### Task 2: Sync helper and its suite + +**Files:** +- Create: `lib/tests/higgsfield.test.sh` (from `docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/02-higgsfield.test.sh`) +- Create: `lib/higgsfield-skills.sh` (from `docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/02-higgsfield-skills.sh`) + +**Interfaces:** +- Consumes: nothing. +- Produces: + - `HIGGSFIELD_SKILLS_URL` (env value wins over the upstream default). + - `higgsfield_sync_skills `: prints the number of skills synced on stdout; returns 0 when at least one skill was synced, 1 otherwise (existing copies untouched). + - `higgsfield_signed_in`: returns 0 when `higgsfield auth token` succeeds; prints nothing. + - Suite helpers later tasks reuse: `expect`, `expect_has`, `expect_not`, `verdict`, `yn`, `entries`, `git_q`, `$WORK`, `$ROOT`; the file ends with a `# ── tally ──` block that must stay last. + +- [ ] **Step 1: Write the failing suite** + +Run: `cp docs/superpowers/plans/2026-09-30-higgsfield-pack.patches/02-higgsfield.test.sh lib/tests/higgsfield.test.sh` + +````bash +#!/usr/bin/env bash +# lib/tests/higgsfield.test.sh — hermetic suite for the Higgsfield pack. +# sync lib/higgsfield-skills.sh against a local git repo shaped like +# upstream (no network) +# toggle lib/toggle-external.sh `higgsfield` / `higgsfield-websites` +# against a fixture tree, fake CLIs first on PATH +# wiring static locks on the installers (order, off by default) +# Each named case prints one `PASS ` or `FAIL :
` line. +set -u +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +pass=0; fail=0; errs="" + +# expect