superpowers-manager 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +180 -82
- package/bin/superpowers-manager.js +85 -30
- package/package.json +2 -1
- package/scripts/adapters/codex/adapter +603 -0
- package/scripts/adapters/codex/lib.sh +449 -0
- package/scripts/{validate-generated-plugin.py → adapters/codex/validate-generated-plugin.py} +3 -3
- package/scripts/core/adapter.sh +103 -0
- package/scripts/core/common.sh +59 -0
- package/scripts/core/lifecycle.sh +141 -0
- package/scripts/core/provenance.sh +135 -0
- package/scripts/core/selection-state.py +414 -0
- package/scripts/core/selection.sh +168 -0
- package/scripts/core/status.sh +27 -0
- package/scripts/core/upstream.sh +280 -0
- package/scripts/core/validate-adapter-response.py +284 -0
- package/scripts/install +30 -29
- package/scripts/pin +51 -0
- package/scripts/prepare +60 -79
- package/scripts/probe +62 -30
- package/scripts/track-latest +30 -0
- package/scripts/uninstall +21 -47
- package/scripts/unpin +41 -0
- package/scripts/update +8 -10
- package/scripts/lib.sh +0 -651
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ and explicit install/update/probe/uninstall lifecycle control.
|
|
|
31
31
|
|
|
32
32
|
```sh
|
|
33
33
|
npx superpowers-wrapper@0.1.1 uninstall
|
|
34
|
-
npx superpowers-manager
|
|
34
|
+
npx superpowers-manager install
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
The manager detects legacy wrapper-owned Codex state and stops before mutation.
|
|
@@ -41,28 +41,33 @@ It never removes the legacy provider automatically.
|
|
|
41
41
|
|
|
42
42
|
Superpowers Manager requires Node 24+, `git`, Python 3, and a POSIX `sh`.
|
|
43
43
|
Codex CLI is required for `probe`, `install`, `update`, and `uninstall`;
|
|
44
|
-
`prepare`
|
|
44
|
+
`prepare`, `pin`, `track-latest`, and `unpin` do not require it. Codex is the
|
|
45
|
+
only supported integration today.
|
|
45
46
|
|
|
46
47
|
macOS and Linux are tested. WSL2 is supported. The native Windows path is
|
|
47
48
|
untested; the launcher looks for Git Bash, `git`, and `python3`, but path
|
|
48
49
|
handling between MSYS and Codex remains a known risk area.
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
Network needs depend on the saved policy. `pin` verifies its target;
|
|
52
|
+
`track-latest` and invocation overrides resolve from upstream when applied; and
|
|
53
|
+
`prepare`, `install`, and `update` fetch or verify the effective source. A probe
|
|
54
|
+
of a saved exact pin can reuse its recorded identity without upstream access.
|
|
55
|
+
Updates remain user-triggered; the manager does not run automatic or background
|
|
56
|
+
updates.
|
|
53
57
|
|
|
54
58
|
## What it does
|
|
55
59
|
|
|
56
60
|
- Resolves an upstream ref, defaulting to the latest stable `vX.Y.Z` release
|
|
57
61
|
tag.
|
|
58
|
-
- Clones
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
-
|
|
63
|
-
|
|
64
|
-
- Validates the
|
|
65
|
-
|
|
62
|
+
- Clones/fetches upstream at that commit and assembles a Codex plugin tree under
|
|
63
|
+
`plugins/superpowers/` (skills, assets, license/readme, and manifest;
|
|
64
|
+
upstream's `hooks/` directory is deliberately excluded).
|
|
65
|
+
- Stamps the generated manifest with a ref-aware manager version ending in
|
|
66
|
+
`+manager.<short-sha>` and writes the upstream provenance to
|
|
67
|
+
`.superpowers-upstream.json`.
|
|
68
|
+
- Validates the generated tree with the manager's shipped, Python-standard-library
|
|
69
|
+
contract validator before swapping it into place (a failed run never destroys
|
|
70
|
+
a previously generated tree).
|
|
66
71
|
- Registers the `superpowers-manager` marketplace and installs or refreshes
|
|
67
72
|
`superpowers@superpowers-manager` in Codex.
|
|
68
73
|
|
|
@@ -73,31 +78,29 @@ claim about how Superpowers should be packaged for future or other harnesses.
|
|
|
73
78
|
Changing the hook-free policy requires a separate design and current
|
|
74
79
|
compatibility evidence.
|
|
75
80
|
|
|
76
|
-
##
|
|
77
|
-
|
|
78
|
-
`SUPERPOWERS_REF` selects a stable release tag, full commit SHA, branch, or
|
|
79
|
-
other resolvable upstream ref for that invocation. The stateless npx package
|
|
80
|
-
records the requested ref, resolved ref, and exact commit, but does not persist
|
|
81
|
-
the selection for a later invocation that omits `SUPERPOWERS_REF`.
|
|
81
|
+
## Runtime architecture
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
- `scripts/core/` owns the shared lifecycle, status, and protocol validation;
|
|
84
|
+
see the [version-1 adapter response protocol](https://github.com/j7an/superpowers-manager/blob/main/docs/adapter-protocol.md).
|
|
85
|
+
- `scripts/adapters/codex/` owns build, inspection, reconciliation, and Codex
|
|
86
|
+
mutation.
|
|
87
|
+
- Codex is the only supported adapter today; no public harness selector ships
|
|
88
|
+
yet.
|
|
85
89
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
Validation checks the manager-owned manifest overlay, generated-tree structure,
|
|
91
|
+
skill frontmatter envelope, known local paths, and provenance. It deliberately
|
|
92
|
+
does not implement a general YAML parser or mirror every Codex ingestion rule;
|
|
93
|
+
upstream owns skill semantics and Codex owns its evolving schema.
|
|
90
94
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
SUPERPOWERS_REF=latest-release npx superpowers-manager update
|
|
96
|
-
```
|
|
95
|
+
`SUPERPOWERS_VALIDATOR=/path/to/validator.py` adds an optional Python validator
|
|
96
|
+
after the built-in check. It receives the candidate plugin root as its only
|
|
97
|
+
argument. It cannot replace or bypass built-in validation, and either check
|
|
98
|
+
failing prevents the tree swap and all Codex mutation.
|
|
97
99
|
|
|
98
|
-
`
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
Direct `scripts/install` keeps harness-specific validation in the Codex adapter:
|
|
101
|
+
phase 1 prepares the exact candidate first, then the adapter performs its Codex
|
|
102
|
+
and refresh-mode preflight before any Codex mutation. The Node dispatcher keeps
|
|
103
|
+
its existing Codex preflight before dispatching to the shell lifecycle.
|
|
101
104
|
|
|
102
105
|
## Provider ownership
|
|
103
106
|
|
|
@@ -123,6 +126,9 @@ manager will not remove it for you.
|
|
|
123
126
|
|
|
124
127
|
| Command | Codex side effects | Purpose |
|
|
125
128
|
|---|---|---|
|
|
129
|
+
| `npx superpowers-manager pin REF` | None | Resolve and save an exact upstream release tag or full commit |
|
|
130
|
+
| `npx superpowers-manager track-latest` | None | Save a policy that resolves the latest stable release when applied |
|
|
131
|
+
| `npx superpowers-manager unpin` | None | Remove saved selection and return to the packaged fallback policy |
|
|
126
132
|
| `npx superpowers-manager prepare` | None | Resolve upstream, build a staged plugin tree, validate it, and replace the generated tree only on success |
|
|
127
133
|
| `npx superpowers-manager probe` | None | Report requested and resolved refs, desired/generated/installed commits, identity state, and status |
|
|
128
134
|
| `npx superpowers-manager install` | Marketplace and plugin state | Prepare and validate, register the marketplace, install the plugin, and verify installed state |
|
|
@@ -130,74 +136,166 @@ manager will not remove it for you.
|
|
|
130
136
|
| `npx superpowers-manager uninstall` | Marketplace and plugin state | Remove only manager-owned Codex state and verify removal |
|
|
131
137
|
|
|
132
138
|
Calling `npx superpowers-manager` without a subcommand is equivalent to
|
|
133
|
-
`update`. `probe` is read-only. Install and update
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
139
|
+
`update`. `probe` is read-only. Install and update stop before prepare or Codex
|
|
140
|
+
mutation when legacy state is present. Uninstall removes only manager-owned
|
|
141
|
+
state and reports any legacy residue without modifying it. These commands fail
|
|
142
|
+
closed when required state cannot be inspected and refuse to report success
|
|
143
|
+
when resulting manager state cannot be verified.
|
|
137
144
|
|
|
138
|
-
|
|
145
|
+
## Choosing the upstream version
|
|
139
146
|
|
|
140
|
-
|
|
147
|
+
Save a tag pin, commit pin, or explicit latest-stable policy for future
|
|
148
|
+
invocations:
|
|
149
|
+
|
|
150
|
+
```sh
|
|
151
|
+
npx superpowers-manager pin v6.1.1
|
|
152
|
+
npx superpowers-manager pin 0123456789abcdef0123456789abcdef01234567 # replace with a real upstream commit
|
|
153
|
+
npx superpowers-manager track-latest
|
|
154
|
+
npx superpowers-manager unpin
|
|
155
|
+
```
|
|
141
156
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
157
|
+
The selection commands save intent only; they do not prepare generated content
|
|
158
|
+
or change Codex state. Run `npx superpowers-manager install` for a first
|
|
159
|
+
installation or `npx superpowers-manager update` to apply the saved policy to
|
|
160
|
+
an existing installation. There is no background updater: selection changes
|
|
161
|
+
take effect only through a user-triggered `prepare`, `install`, or `update`.
|
|
145
162
|
|
|
146
|
-
|
|
163
|
+
`pin` accepts only an exact `vMAJOR.MINOR.PATCH` tag (optionally with a SemVer
|
|
164
|
+
prerelease suffix) or a full 40-character commit. It resolves and verifies the
|
|
165
|
+
target before saving both the exact commit identity and its source. The saved
|
|
166
|
+
identity is the contract, not the contents of a clone cache: clearing or moving
|
|
167
|
+
`SUPERPOWERS_CACHE_DIR` does not change the selection. `track-latest` saves the
|
|
168
|
+
source and resolves the highest stable `vX.Y.Z` tag each time the policy is
|
|
169
|
+
applied. `unpin` removes only the saved selection and restores the packaged
|
|
170
|
+
`config/upstream-ref` fallback, currently `latest-release`.
|
|
171
|
+
|
|
172
|
+
The user-wide selection file is `selection.json` under the first configured
|
|
173
|
+
location:
|
|
174
|
+
|
|
175
|
+
1. `$SUPERPOWERS_CONFIG_DIR`
|
|
176
|
+
2. `$XDG_CONFIG_HOME/superpowers-manager`
|
|
177
|
+
3. `$HOME/.config/superpowers-manager`
|
|
178
|
+
|
|
179
|
+
Each location must be absolute. The ref and source precedence chains are
|
|
180
|
+
independent:
|
|
181
|
+
|
|
182
|
+
- Ref: `SUPERPOWERS_REF`, then saved selection, then `config/upstream-ref`.
|
|
183
|
+
- Source: `SUPERPOWERS_UPSTREAM_URL`, then saved source, then the official
|
|
184
|
+
`https://github.com/obra/superpowers` source.
|
|
185
|
+
|
|
186
|
+
`SUPERPOWERS_REF` is an invocation-only override and is not persisted. Unlike
|
|
187
|
+
an exact saved pin, it accepts stable tags, full commit SHAs, branches, and
|
|
188
|
+
other resolvable upstream refs for that invocation:
|
|
147
189
|
|
|
148
190
|
```sh
|
|
149
|
-
|
|
191
|
+
SUPERPOWERS_REF=feature/foo npx superpowers-manager probe
|
|
150
192
|
```
|
|
151
193
|
|
|
152
|
-
|
|
194
|
+
`pin` and `track-latest` bind the current `SUPERPOWERS_UPSTREAM_URL`, or the
|
|
195
|
+
official source when that variable is unset, to the saved intent. An invocation
|
|
196
|
+
may override only the ref or only the source; `probe` exposes
|
|
197
|
+
`selection_origin`, `selection_mode`, `upstream_source_origin`,
|
|
198
|
+
`effective_source`, the saved-selection fields, and a mixed-origin warning in
|
|
199
|
+
human output so that combination remains visible.
|
|
200
|
+
|
|
201
|
+
A saved exact pin lets `probe` determine the desired upstream identity without
|
|
202
|
+
contacting Git, even when the saved source is temporarily unavailable. This is
|
|
203
|
+
not a general offline installation promise: `pin` verifies the requested
|
|
204
|
+
target, `track-latest` must resolve when applied, and `prepare`/`install`/`update`
|
|
205
|
+
must fetch or verify content from the effective source. Codex inspection is
|
|
206
|
+
still required for lifecycle status.
|
|
207
|
+
|
|
208
|
+
HTTP(S) upstream URLs containing userinfo are now rejected, including
|
|
209
|
+
token-only userinfo. This is an intentional compatibility break: use a Git
|
|
210
|
+
credential helper for HTTP(S) authentication or an SSH source instead of
|
|
211
|
+
embedding credentials in the URL.
|
|
212
|
+
|
|
213
|
+
Saved selection does not claim or transfer provider ownership. The manager
|
|
214
|
+
continues to mutate only `superpowers@superpowers-manager` and never removes or
|
|
215
|
+
updates the official provider or any other provider automatically.
|
|
216
|
+
|
|
217
|
+
## How versioning works
|
|
218
|
+
|
|
219
|
+
- **Upstream manifest first:** when upstream provides
|
|
220
|
+
`.codex-plugin/plugin.json`, `prepare` uses it as the generated manifest base
|
|
221
|
+
so future upstream metadata fields are preserved by default.
|
|
222
|
+
- **Fallback template:** `plugins/superpowers/.codex-plugin/plugin.template.json`
|
|
223
|
+
is committed as a minimal fallback for older upstream refs that do not ship a
|
|
224
|
+
Codex manifest. It carries the placeholder version
|
|
225
|
+
`0.0.0+manager.template`.
|
|
226
|
+
- **Manager overlay:** `prepare` replaces the version with a ref-aware manager
|
|
227
|
+
version, forces `skills` to `./skills/`, and enforces the manager's current
|
|
228
|
+
hook-free policy: no manifest `hooks` key and no copied `hooks/` directory.
|
|
229
|
+
Unknown upstream manifest fields remain preserved.
|
|
230
|
+
- Stable tags generate release-looking versions such as
|
|
231
|
+
`6.0.3+manager.896224c`; explicit prerelease tags generate versions such as
|
|
232
|
+
`6.1.0-beta.1+manager.abc1234`.
|
|
233
|
+
- Branch builds deliberately stay below real releases:
|
|
234
|
+
`main` generates `0.0.0-main+manager.<short-sha>` and other named refs
|
|
235
|
+
generate `0.0.0-ref-<sanitized-ref>+manager.<short-sha>`.
|
|
236
|
+
- Raw 40-character commit SHAs generate `0.0.0+manager.<short-sha>`.
|
|
237
|
+
- **`.superpowers-upstream.json`** records the authoritative provenance:
|
|
238
|
+
`source`, `requested_ref`, `resolved_ref`, `commit`, and the upstream manifest
|
|
239
|
+
version. The generated manifest version is for human readability and Codex
|
|
240
|
+
package identity; the upstream `commit` is what `probe`/`update` compare
|
|
241
|
+
against.
|
|
242
|
+
|
|
243
|
+
## Refresh modes
|
|
153
244
|
|
|
154
|
-
|
|
155
|
-
manifest base and preserves unknown upstream fields.
|
|
156
|
-
- The committed fallback template supports older refs without an upstream
|
|
157
|
-
manifest and uses the placeholder version `0.0.0+manager.template`.
|
|
158
|
-
- Stable tags produce versions such as `6.0.3+manager.896224c`; explicit
|
|
159
|
-
prereleases produce versions such as `6.1.0-beta.1+manager.abc1234`.
|
|
160
|
-
- `main` produces `0.0.0-main+manager.<short-sha>`; other named refs produce
|
|
161
|
-
`0.0.0-ref-<sanitized-ref>+manager.<short-sha>`.
|
|
162
|
-
- Full commit SHAs produce `0.0.0+manager.<short-sha>`.
|
|
245
|
+
`install` and `update` accept `SUPERPOWERS_INSTALL_REFRESH_MODE`:
|
|
163
246
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
247
|
+
- `add-only` (default) — `plugin add` re-reads the local source, which refreshes
|
|
248
|
+
a mutated tree. Verified sufficient for local marketplaces.
|
|
249
|
+
- `remove-add` — removes the manager's own plugin first, then re-adds it. Use
|
|
250
|
+
only if a refresh ever fails to take:
|
|
167
251
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
check failing prevents replacement and Codex mutation.
|
|
252
|
+
```sh
|
|
253
|
+
SUPERPOWERS_INSTALL_REFRESH_MODE=remove-add npx superpowers-manager update
|
|
254
|
+
```
|
|
172
255
|
|
|
173
256
|
## Tests
|
|
174
257
|
|
|
175
258
|
```sh
|
|
176
|
-
sh tests/run.sh
|
|
177
|
-
sh tests/container.sh
|
|
259
|
+
sh tests/run.sh # Layers 1-3: host-side hermetic checks while iterating
|
|
260
|
+
sh tests/container.sh # Layers 1-4: blocking Docker acceptance command
|
|
261
|
+
sh tests/manual/codex-behavior-probe.sh # optional native-only compatibility residue
|
|
178
262
|
```
|
|
179
263
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
264
|
+
Layers 1-3 stay offline and hermetic: they use a fake local upstream repo plus
|
|
265
|
+
host-side fixtures, and they perform no mutation of the developer's or runner's
|
|
266
|
+
real Codex state.
|
|
267
|
+
|
|
268
|
+
Layer 4 is the Docker acceptance path. It is the required completion command
|
|
269
|
+
because the isolated-container Codex probe graduated from a temporary
|
|
270
|
+
nonblocking spike to a blocking acceptance gate. `sh tests/container.sh` runs
|
|
271
|
+
the inner `sh tests/run.sh` suite and then the real Codex offline probe inside
|
|
272
|
+
an isolated container home with networking disabled. That container run may
|
|
273
|
+
mutate the throwaway container-local Codex state, but it still performs no
|
|
274
|
+
mutation of the developer's or runner's real Codex state.
|
|
275
|
+
|
|
276
|
+
The manual probe is opt-in and covers native-only compatibility residue such as
|
|
277
|
+
path/cache behavior against an intentionally real local Codex install. It is
|
|
278
|
+
not part of acceptance. GitHub Actions runs the blocking container acceptance
|
|
279
|
+
command on pull requests and pushes to `main`.
|
|
184
280
|
|
|
185
281
|
## Repository layout
|
|
186
282
|
|
|
187
|
-
```
|
|
188
|
-
.agents/plugins/marketplace.json
|
|
189
|
-
|
|
190
|
-
config/upstream-ref # default upstream ref
|
|
283
|
+
```
|
|
284
|
+
.agents/plugins/marketplace.json # local marketplace definition (tracked)
|
|
285
|
+
config/upstream-ref # which upstream ref to track (tracked)
|
|
191
286
|
plugins/superpowers/
|
|
192
|
-
.codex-plugin/plugin.template.json #
|
|
193
|
-
.codex-plugin/plugin.json # generated manifest
|
|
194
|
-
skills/ assets/ LICENSE ... # generated from upstream
|
|
195
|
-
.superpowers-upstream.json # generated provenance
|
|
196
|
-
scripts/
|
|
197
|
-
|
|
198
|
-
|
|
287
|
+
.codex-plugin/plugin.template.json # fallback manifest template (tracked)
|
|
288
|
+
.codex-plugin/plugin.json # generated manifest (gitignored)
|
|
289
|
+
skills/ assets/ LICENSE ... # generated from upstream (gitignored)
|
|
290
|
+
.superpowers-upstream.json # generated provenance (gitignored)
|
|
291
|
+
scripts/
|
|
292
|
+
adapters/codex/ # Codex adapter entrypoint + validator helpers
|
|
293
|
+
core/ # shared lifecycle, selection, provenance, and status modules
|
|
294
|
+
pin track-latest unpin # persistent-selection entrypoints
|
|
295
|
+
prepare probe install update uninstall # lifecycle entrypoints
|
|
296
|
+
tests/ # hermetic suite + manual Codex probe
|
|
297
|
+
.cache/upstream/ # upstream clone cache (gitignored)
|
|
199
298
|
```
|
|
200
299
|
|
|
201
|
-
Everything under `plugins/superpowers/` except the fallback manifest template
|
|
202
|
-
|
|
203
|
-
content; regenerate it instead.
|
|
300
|
+
Everything under `plugins/superpowers/` except the fallback manifest template is
|
|
301
|
+
generated by `prepare` and ignored by Git; re-run `prepare` to regenerate it.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
// @ts-check
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import * as fs from 'node:fs';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import { spawnSync } from 'node:child_process';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* @typedef {'prepare' | 'probe' | 'install' | 'update' | 'uninstall'} Subcommand
|
|
9
|
+
* @typedef {'pin' | 'track-latest' | 'unpin' | 'prepare' | 'probe' | 'install' | 'update' | 'uninstall'} Subcommand
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -27,8 +27,22 @@ const { spawnSync } = require('child_process');
|
|
|
27
27
|
* @typedef {{ file: string, argv: string[] }} SpawnDescriptor
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
-
const SUBCOMMANDS = [
|
|
31
|
-
|
|
30
|
+
const SUBCOMMANDS = [
|
|
31
|
+
'pin', 'track-latest', 'unpin', 'prepare', 'probe', 'install', 'update', 'uninstall',
|
|
32
|
+
];
|
|
33
|
+
const PIN_TAG_RE = /^v(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)(?:-(?:(?:0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*))?$/;
|
|
34
|
+
const PIN_COMMIT_RE = /^[0-9A-Fa-f]{40}$/;
|
|
35
|
+
/** @type {Record<Subcommand, string[]>} */
|
|
36
|
+
const COMMAND_REQUIREMENTS = {
|
|
37
|
+
pin: ['git', 'python3'],
|
|
38
|
+
'track-latest': ['python3'],
|
|
39
|
+
unpin: [],
|
|
40
|
+
prepare: ['git', 'python3'],
|
|
41
|
+
probe: ['git', 'python3', 'codex'],
|
|
42
|
+
install: ['git', 'python3', 'codex'],
|
|
43
|
+
update: ['git', 'python3', 'codex'],
|
|
44
|
+
uninstall: ['python3', 'codex'],
|
|
45
|
+
};
|
|
32
46
|
// Mirrors upstream Superpowers' hooks/run-hook.cmd discovery order.
|
|
33
47
|
const GIT_BASH_CANDIDATES = [
|
|
34
48
|
'C:\\Program Files\\Git\\bin\\bash.exe',
|
|
@@ -57,18 +71,41 @@ function resolvePackageRoot(scriptPath) {
|
|
|
57
71
|
}
|
|
58
72
|
}
|
|
59
73
|
|
|
74
|
+
/**
|
|
75
|
+
* @param {string} moduleFilename
|
|
76
|
+
* @param {string | undefined} argvPath
|
|
77
|
+
* @returns {boolean}
|
|
78
|
+
*/
|
|
79
|
+
function isMain(moduleFilename, argvPath) {
|
|
80
|
+
if (!argvPath) return false;
|
|
81
|
+
return moduleFilename === fs.realpathSync(argvPath);
|
|
82
|
+
}
|
|
83
|
+
|
|
60
84
|
/**
|
|
61
85
|
* @param {string[]} argv
|
|
62
86
|
* @returns {ParseResult}
|
|
63
87
|
*/
|
|
64
88
|
function parseArgs(argv) {
|
|
65
|
-
if (argv.length === 0) return { kind: 'run', cmd: 'update', args: [] };
|
|
66
89
|
/** @type {string | undefined} */
|
|
67
90
|
const first = argv[0];
|
|
91
|
+
if (argv.length === 0) return { kind: 'run', cmd: 'update', args: [] };
|
|
68
92
|
if (first === '--help' || first === '-h') return { kind: 'help' };
|
|
69
93
|
if (first === '--version') return { kind: 'version' };
|
|
70
94
|
if (first && SUBCOMMANDS.includes(first)) {
|
|
71
|
-
|
|
95
|
+
const args = argv.slice(1);
|
|
96
|
+
if (first === 'pin' && args.length !== 1) {
|
|
97
|
+
return { kind: 'usage-error', message: 'usage: superpowers-manager pin REF' };
|
|
98
|
+
}
|
|
99
|
+
if (first === 'pin' && !PIN_TAG_RE.test(args[0]) && !PIN_COMMIT_RE.test(args[0])) {
|
|
100
|
+
return {
|
|
101
|
+
kind: 'usage-error',
|
|
102
|
+
message: 'pin REF must be an exact v-prefixed SemVer tag or full 40-hex commit',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if ((first === 'track-latest' || first === 'unpin') && args.length !== 0) {
|
|
106
|
+
return { kind: 'usage-error', message: `usage: superpowers-manager ${first}` };
|
|
107
|
+
}
|
|
108
|
+
return { kind: 'run', cmd: /** @type {Subcommand} */ (first), args };
|
|
72
109
|
}
|
|
73
110
|
return { kind: 'usage-error', message: `unknown subcommand: ${first}` };
|
|
74
111
|
}
|
|
@@ -117,8 +154,15 @@ function discoverShell(env, platform) {
|
|
|
117
154
|
return findTool('bash', env, platform);
|
|
118
155
|
}
|
|
119
156
|
|
|
120
|
-
|
|
121
|
-
|
|
157
|
+
/**
|
|
158
|
+
* @returns {Record<Subcommand, string[]>}
|
|
159
|
+
*/
|
|
160
|
+
function commandRequirements() {
|
|
161
|
+
return COMMAND_REQUIREMENTS;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Tool preflight; never touches Codex state. Requirements are specific to the
|
|
165
|
+
// selected command, while a POSIX shell remains mandatory for every command.
|
|
122
166
|
/**
|
|
123
167
|
* @param {Subcommand} cmd
|
|
124
168
|
* @param {NodeJS.ProcessEnv} env
|
|
@@ -127,8 +171,17 @@ function discoverShell(env, platform) {
|
|
|
127
171
|
*/
|
|
128
172
|
function preflight(cmd, env, platform) {
|
|
129
173
|
const errors = [];
|
|
130
|
-
for (const tool of [
|
|
131
|
-
if (
|
|
174
|
+
for (const tool of COMMAND_REQUIREMENTS[cmd]) {
|
|
175
|
+
if (tool === 'codex') {
|
|
176
|
+
const codexBin = env.SUPERPOWERS_CODEX || 'codex';
|
|
177
|
+
// An explicit override may be a path rather than a PATH-resolvable name.
|
|
178
|
+
const found = codexBin.includes(path.sep)
|
|
179
|
+
? fs.existsSync(codexBin)
|
|
180
|
+
: Boolean(findTool(codexBin, env, platform));
|
|
181
|
+
if (!found) {
|
|
182
|
+
errors.push(`required command not found: ${codexBin} — install the Codex CLI or set SUPERPOWERS_CODEX`);
|
|
183
|
+
}
|
|
184
|
+
} else if (!findTool(tool, env, platform)) {
|
|
132
185
|
errors.push(`required command not found: ${tool} — install ${tool} and re-run`);
|
|
133
186
|
}
|
|
134
187
|
}
|
|
@@ -138,19 +191,8 @@ function preflight(cmd, env, platform) {
|
|
|
138
191
|
? 'no POSIX shell found — install Git for Windows (provides bash) or use WSL2'
|
|
139
192
|
: 'required command not found: sh');
|
|
140
193
|
}
|
|
141
|
-
if (
|
|
142
|
-
|
|
143
|
-
// An explicit override may be a path rather than a PATH-resolvable name.
|
|
144
|
-
const found = codexBin.includes(path.sep)
|
|
145
|
-
? fs.existsSync(codexBin)
|
|
146
|
-
: Boolean(findTool(codexBin, env, platform));
|
|
147
|
-
if (!found) {
|
|
148
|
-
errors.push(`required command not found: ${codexBin} — install the Codex CLI or set SUPERPOWERS_CODEX`);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
return errors.length
|
|
152
|
-
? { ok: false, errors }
|
|
153
|
-
: { ok: true, shell: /** @type {string} */ (shell) };
|
|
194
|
+
if (errors.length) return { ok: false, errors };
|
|
195
|
+
return { ok: true, shell: /** @type {string} */ (shell) };
|
|
154
196
|
}
|
|
155
197
|
|
|
156
198
|
// POSIX executes the script directly (#!/bin/sh shebang); Windows cannot
|
|
@@ -174,8 +216,14 @@ function buildSpawn(cmd, args, root, shell, platform) {
|
|
|
174
216
|
|
|
175
217
|
function usage() {
|
|
176
218
|
return [
|
|
177
|
-
'usage: superpowers-manager [
|
|
219
|
+
'usage: superpowers-manager [command] [args...]',
|
|
178
220
|
'',
|
|
221
|
+
'Selection commands (save intent only; they do not prepare or install it):',
|
|
222
|
+
' pin REF save an exact upstream release tag or commit',
|
|
223
|
+
' track-latest save selection of the latest stable upstream release',
|
|
224
|
+
' unpin remove the saved selection and return to the packaged fallback',
|
|
225
|
+
'',
|
|
226
|
+
'Apply and lifecycle commands:',
|
|
179
227
|
' prepare fetch the pinned upstream ref and generate the plugin tree',
|
|
180
228
|
' probe report upstream/generated/installed status (accepts --porcelain)',
|
|
181
229
|
' install register this package root as a Codex marketplace and install the plugin',
|
|
@@ -184,9 +232,13 @@ function usage() {
|
|
|
184
232
|
'',
|
|
185
233
|
'Environment overrides (passed through to the scripts): SUPERPOWERS_REF,',
|
|
186
234
|
'SUPERPOWERS_UPSTREAM_URL, SUPERPOWERS_CODEX, SUPERPOWERS_CACHE_DIR,',
|
|
235
|
+
'SUPERPOWERS_CONFIG_DIR, XDG_CONFIG_HOME,',
|
|
187
236
|
'SUPERPOWERS_PLUGIN_ROOT, SUPERPOWERS_MANIFEST_TEMPLATE,',
|
|
188
237
|
'SUPERPOWERS_VALIDATOR,',
|
|
189
238
|
'SUPERPOWERS_INSTALLED_SEARCH_ROOT, SUPERPOWERS_INSTALL_REFRESH_MODE',
|
|
239
|
+
'',
|
|
240
|
+
'Selection state uses SUPERPOWERS_CONFIG_DIR when set; otherwise it uses',
|
|
241
|
+
'$XDG_CONFIG_HOME/superpowers-manager, then $HOME/.config/superpowers-manager.',
|
|
190
242
|
].join('\n');
|
|
191
243
|
}
|
|
192
244
|
|
|
@@ -196,7 +248,7 @@ function main() {
|
|
|
196
248
|
console.log(usage());
|
|
197
249
|
process.exit(0);
|
|
198
250
|
}
|
|
199
|
-
const root = resolvePackageRoot(
|
|
251
|
+
const root = resolvePackageRoot(import.meta.filename);
|
|
200
252
|
if (!root) {
|
|
201
253
|
console.error('error: cannot resolve the superpowers-manager package root');
|
|
202
254
|
process.exit(1);
|
|
@@ -230,6 +282,9 @@ function main() {
|
|
|
230
282
|
process.exit(res.status === null ? 1 : res.status);
|
|
231
283
|
}
|
|
232
284
|
|
|
233
|
-
|
|
285
|
+
export {
|
|
286
|
+
resolvePackageRoot, isMain, parseArgs, findTool, discoverShell,
|
|
287
|
+
commandRequirements, preflight, buildSpawn, usage,
|
|
288
|
+
};
|
|
234
289
|
|
|
235
|
-
if (
|
|
290
|
+
if (isMain(import.meta.filename, process.argv[1])) main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "superpowers-manager",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Install and update the latest stable obra/superpowers release directly from upstream, without waiting for agent marketplaces to catch up. Codex supported today.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"superpowers",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"updater"
|
|
18
18
|
],
|
|
19
19
|
"license": "MIT",
|
|
20
|
+
"type": "module",
|
|
20
21
|
"repository": {
|
|
21
22
|
"type": "git",
|
|
22
23
|
"url": "git+https://github.com/j7an/superpowers-manager.git"
|