superpowers-manager 0.1.4
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/.agents/plugins/marketplace.json +21 -0
- package/LICENSE +21 -0
- package/README.md +203 -0
- package/bin/superpowers-manager.js +235 -0
- package/config/upstream-ref +1 -0
- package/package.json +42 -0
- package/plugins/superpowers/.codex-plugin/plugin.template.json +23 -0
- package/scripts/install +58 -0
- package/scripts/lib.sh +651 -0
- package/scripts/prepare +136 -0
- package/scripts/probe +50 -0
- package/scripts/uninstall +61 -0
- package/scripts/update +36 -0
- package/scripts/validate-generated-plugin.py +314 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "superpowers-manager",
|
|
3
|
+
"interface": {
|
|
4
|
+
"displayName": "Superpowers Manager"
|
|
5
|
+
},
|
|
6
|
+
"plugins": [
|
|
7
|
+
{
|
|
8
|
+
"name": "superpowers",
|
|
9
|
+
"source": {
|
|
10
|
+
"source": "local",
|
|
11
|
+
"path": "./plugins/superpowers"
|
|
12
|
+
},
|
|
13
|
+
"policy": {
|
|
14
|
+
"installation": "AVAILABLE",
|
|
15
|
+
"authentication": "ON_INSTALL",
|
|
16
|
+
"products": ["CODEX"]
|
|
17
|
+
},
|
|
18
|
+
"category": "Developer Tools"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jia "Jay" Tan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# Superpowers Manager
|
|
2
|
+
|
|
3
|
+
Install and update the latest stable
|
|
4
|
+
[`obra/superpowers`](https://github.com/obra/superpowers) release directly from
|
|
5
|
+
upstream, without waiting for agent marketplaces to catch up.
|
|
6
|
+
Codex supported today.
|
|
7
|
+
|
|
8
|
+
> Unofficial community integration. Not affiliated with the
|
|
9
|
+
> `obra/superpowers` maintainers.
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npx superpowers-manager install
|
|
15
|
+
npx superpowers-manager probe
|
|
16
|
+
npx superpowers-manager update
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Use the official marketplace for the simplest native Codex installation. Use
|
|
20
|
+
Superpowers Manager when you want immediate stable-upstream freshness after a
|
|
21
|
+
user-triggered install/update, per-invocation release or commit selection,
|
|
22
|
+
recorded upstream provenance, diagnostics, Codex-specific hook-free packaging,
|
|
23
|
+
and explicit install/update/probe/uninstall lifecycle control.
|
|
24
|
+
|
|
25
|
+
| Choose | Best fit |
|
|
26
|
+
|---|---|
|
|
27
|
+
| Official marketplace | Simplest Codex-native installation and marketplace-managed cadence |
|
|
28
|
+
| `superpowers-manager` | Direct stable-upstream freshness when invoked, exact ref selection, provenance, diagnostics, and lifecycle control |
|
|
29
|
+
|
|
30
|
+
### Moving from `superpowers-wrapper`
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
npx superpowers-wrapper@0.1.1 uninstall
|
|
34
|
+
npx superpowers-manager@0.1.4 install
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The manager detects legacy wrapper-owned Codex state and stops before mutation.
|
|
38
|
+
It never removes the legacy provider automatically.
|
|
39
|
+
|
|
40
|
+
## Requirements and platforms
|
|
41
|
+
|
|
42
|
+
Superpowers Manager requires Node 24+, `git`, Python 3, and a POSIX `sh`.
|
|
43
|
+
Codex CLI is required for `probe`, `install`, `update`, and `uninstall`;
|
|
44
|
+
`prepare` does not require it.
|
|
45
|
+
|
|
46
|
+
macOS and Linux are tested. WSL2 is supported. The native Windows path is
|
|
47
|
+
untested; the launcher looks for Git Bash, `git`, and `python3`, but path
|
|
48
|
+
handling between MSYS and Codex remains a known risk area.
|
|
49
|
+
|
|
50
|
+
Prepare, install, probe, and update resolve the requested upstream ref over the
|
|
51
|
+
network. Updates are user-triggered and need upstream network access; the
|
|
52
|
+
manager does not run automatic or background updates.
|
|
53
|
+
|
|
54
|
+
## What it does
|
|
55
|
+
|
|
56
|
+
- Resolves an upstream ref, defaulting to the latest stable `vX.Y.Z` release
|
|
57
|
+
tag.
|
|
58
|
+
- Clones or fetches that exact upstream commit and assembles a Codex plugin tree
|
|
59
|
+
under `plugins/superpowers/`.
|
|
60
|
+
- Preserves the upstream manifest when available, while applying the
|
|
61
|
+
manager-owned plugin contract.
|
|
62
|
+
- Records `source`, `requested_ref`, `resolved_ref`, `commit`, and the upstream
|
|
63
|
+
manifest version in `.superpowers-upstream.json`.
|
|
64
|
+
- Validates the candidate tree before replacing a previous generated tree or
|
|
65
|
+
mutating Codex state.
|
|
66
|
+
- Registers the `superpowers-manager` marketplace and installs or refreshes
|
|
67
|
+
`superpowers@superpowers-manager` in Codex.
|
|
68
|
+
|
|
69
|
+
The generated plugin carries upstream skills, assets, and documentation. The
|
|
70
|
+
manager excludes upstream `hooks/`, removes the manifest `hooks` field, and
|
|
71
|
+
validates that both stay absent. This is a Codex-specific adapter policy, not a
|
|
72
|
+
claim about how Superpowers should be packaged for future or other harnesses.
|
|
73
|
+
Changing the hook-free policy requires a separate design and current
|
|
74
|
+
compatibility evidence.
|
|
75
|
+
|
|
76
|
+
## Choosing the upstream version
|
|
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`.
|
|
82
|
+
|
|
83
|
+
Without `SUPERPOWERS_REF`, the manager reads `config/upstream-ref`, which ships
|
|
84
|
+
as `latest-release`. Accepted values include:
|
|
85
|
+
|
|
86
|
+
- `latest-release` — the highest stable `vX.Y.Z` tag; prereleases are excluded.
|
|
87
|
+
- A specific tag, such as `v6.0.3`.
|
|
88
|
+
- A full 40-character commit SHA.
|
|
89
|
+
- Any other ref upstream resolves, such as `main` or a branch name.
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
SUPERPOWERS_REF=v6.0.3 npx superpowers-manager prepare
|
|
93
|
+
SUPERPOWERS_REF=main npx superpowers-manager probe
|
|
94
|
+
SUPERPOWERS_REF=feature/foo npx superpowers-manager install
|
|
95
|
+
SUPERPOWERS_REF=latest-release npx superpowers-manager update
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`SUPERPOWERS_CACHE_DIR` may point to a persistent upstream clone cache to avoid
|
|
99
|
+
re-cloning between package materializations. It caches Git objects; it does not
|
|
100
|
+
persist ref selection or trigger updates.
|
|
101
|
+
|
|
102
|
+
## Provider ownership
|
|
103
|
+
|
|
104
|
+
If another `superpowers` provider is installed, remove or disable it yourself
|
|
105
|
+
before installing this one. The manager never removes another provider and
|
|
106
|
+
mutates only `superpowers@superpowers-manager` and the `superpowers-manager`
|
|
107
|
+
marketplace.
|
|
108
|
+
|
|
109
|
+
For example, if you intentionally want the manager to take over from the
|
|
110
|
+
official provider:
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
codex plugin remove superpowers@openai-curated
|
|
114
|
+
npx superpowers-manager install
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Manager install, update, and uninstall commands inspect both current and legacy
|
|
118
|
+
identities and fail closed when Codex state cannot be read or parsed. If legacy
|
|
119
|
+
`superpowers-wrapper` state remains, use the migration commands above; the
|
|
120
|
+
manager will not remove it for you.
|
|
121
|
+
|
|
122
|
+
## Lifecycle commands
|
|
123
|
+
|
|
124
|
+
| Command | Codex side effects | Purpose |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| `npx superpowers-manager prepare` | None | Resolve upstream, build a staged plugin tree, validate it, and replace the generated tree only on success |
|
|
127
|
+
| `npx superpowers-manager probe` | None | Report requested and resolved refs, desired/generated/installed commits, identity state, and status |
|
|
128
|
+
| `npx superpowers-manager install` | Marketplace and plugin state | Prepare and validate, register the marketplace, install the plugin, and verify installed state |
|
|
129
|
+
| `npx superpowers-manager update` | Marketplace and plugin state when stale | Probe, prepare and/or install as needed, then verify the refresh |
|
|
130
|
+
| `npx superpowers-manager uninstall` | Marketplace and plugin state | Remove only manager-owned Codex state and verify removal |
|
|
131
|
+
|
|
132
|
+
Calling `npx superpowers-manager` without a subcommand is equivalent to
|
|
133
|
+
`update`. `probe` is read-only. Install and update prepare and validate before changing Codex state.
|
|
134
|
+
Uninstall inspects and removes only manager-owned Codex state. Those mutating
|
|
135
|
+
commands fail closed when required state cannot be inspected and refuse to
|
|
136
|
+
report success when the resulting state cannot be verified.
|
|
137
|
+
|
|
138
|
+
### Refresh modes
|
|
139
|
+
|
|
140
|
+
`install` and `update` accept `SUPERPOWERS_INSTALL_REFRESH_MODE`:
|
|
141
|
+
|
|
142
|
+
- `add-only` (default) asks Codex to add the plugin from the current local
|
|
143
|
+
marketplace source.
|
|
144
|
+
- `remove-add` removes only the manager-owned plugin before adding it again.
|
|
145
|
+
|
|
146
|
+
Use `remove-add` only when an add-only refresh fails to take:
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
SUPERPOWERS_INSTALL_REFRESH_MODE=remove-add npx superpowers-manager update
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## How generated versions work
|
|
153
|
+
|
|
154
|
+
- If upstream provides `.codex-plugin/plugin.json`, the manager uses it as the
|
|
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>`.
|
|
163
|
+
|
|
164
|
+
The generated version is for readable Codex package identity. The exact commit
|
|
165
|
+
in `.superpowers-upstream.json` is the authoritative value compared by `probe`
|
|
166
|
+
and `update`.
|
|
167
|
+
|
|
168
|
+
`SUPERPOWERS_VALIDATOR=/path/to/validator.py` adds an optional Python validator
|
|
169
|
+
after the built-in generated-tree validation. It receives the candidate plugin
|
|
170
|
+
root as its only argument. It cannot bypass the built-in validator, and either
|
|
171
|
+
check failing prevents replacement and Codex mutation.
|
|
172
|
+
|
|
173
|
+
## Tests
|
|
174
|
+
|
|
175
|
+
```sh
|
|
176
|
+
sh tests/run.sh # Layers 1-3: hermetic, no network or real Codex state
|
|
177
|
+
sh tests/container.sh # Layer 4: blocking isolated real-Codex CLI check
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The container suite runs with networking disabled and a throwaway home, so it
|
|
181
|
+
may mutate only isolated container state. The optional
|
|
182
|
+
`tests/manual/codex-behavior-probe.sh` is reserved for intentional native-only
|
|
183
|
+
compatibility residue and is not part of acceptance.
|
|
184
|
+
|
|
185
|
+
## Repository layout
|
|
186
|
+
|
|
187
|
+
```text
|
|
188
|
+
.agents/plugins/marketplace.json # local manager marketplace definition
|
|
189
|
+
bin/superpowers-manager.js # npm CLI launcher
|
|
190
|
+
config/upstream-ref # default upstream ref
|
|
191
|
+
plugins/superpowers/
|
|
192
|
+
.codex-plugin/plugin.template.json # tracked fallback manifest
|
|
193
|
+
.codex-plugin/plugin.json # generated manifest (ignored)
|
|
194
|
+
skills/ assets/ LICENSE ... # generated from upstream (ignored)
|
|
195
|
+
.superpowers-upstream.json # generated provenance (ignored)
|
|
196
|
+
scripts/ # prepare/probe/install/update/uninstall
|
|
197
|
+
tests/ # hermetic and isolated-container suites
|
|
198
|
+
.cache/upstream/ # optional upstream clone cache (ignored)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Everything under `plugins/superpowers/` except the fallback manifest template
|
|
202
|
+
is generated by `prepare` and ignored by Git. Do not edit generated plugin
|
|
203
|
+
content; regenerate it instead.
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { spawnSync } = require('child_process');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {'prepare' | 'probe' | 'install' | 'update' | 'uninstall'} Subcommand
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {{ kind: 'run', cmd: Subcommand, args: string[] }} RunParseResult
|
|
14
|
+
* @typedef {{ kind: 'help' }} HelpParseResult
|
|
15
|
+
* @typedef {{ kind: 'version' }} VersionParseResult
|
|
16
|
+
* @typedef {{ kind: 'usage-error', message: string }} UsageErrorParseResult
|
|
17
|
+
* @typedef {RunParseResult | HelpParseResult | VersionParseResult | UsageErrorParseResult} ParseResult
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @typedef {{ ok: true, shell: string }} PreflightOk
|
|
22
|
+
* @typedef {{ ok: false, errors: string[] }} PreflightError
|
|
23
|
+
* @typedef {PreflightOk | PreflightError} PreflightResult
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {{ file: string, argv: string[] }} SpawnDescriptor
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const SUBCOMMANDS = ['prepare', 'probe', 'install', 'update', 'uninstall'];
|
|
31
|
+
const CODEX_SUBCOMMANDS = ['probe', 'install', 'update', 'uninstall'];
|
|
32
|
+
// Mirrors upstream Superpowers' hooks/run-hook.cmd discovery order.
|
|
33
|
+
const GIT_BASH_CANDIDATES = [
|
|
34
|
+
'C:\\Program Files\\Git\\bin\\bash.exe',
|
|
35
|
+
'C:\\Program Files (x86)\\Git\\bin\\bash.exe',
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
// Walk upward from the bin's physical location to the directory containing
|
|
39
|
+
// package.json. realpathSync first: npm/npx expose the bin through a symlink
|
|
40
|
+
// or shim outside the package root.
|
|
41
|
+
/**
|
|
42
|
+
* @param {string} scriptPath
|
|
43
|
+
* @returns {string | null}
|
|
44
|
+
*/
|
|
45
|
+
function resolvePackageRoot(scriptPath) {
|
|
46
|
+
let dir;
|
|
47
|
+
try {
|
|
48
|
+
dir = path.dirname(fs.realpathSync(scriptPath));
|
|
49
|
+
} catch (err) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
for (;;) {
|
|
53
|
+
if (fs.existsSync(path.join(dir, 'package.json'))) return dir;
|
|
54
|
+
const parent = path.dirname(dir);
|
|
55
|
+
if (parent === dir) return null;
|
|
56
|
+
dir = parent;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @param {string[]} argv
|
|
62
|
+
* @returns {ParseResult}
|
|
63
|
+
*/
|
|
64
|
+
function parseArgs(argv) {
|
|
65
|
+
if (argv.length === 0) return { kind: 'run', cmd: 'update', args: [] };
|
|
66
|
+
/** @type {string | undefined} */
|
|
67
|
+
const first = argv[0];
|
|
68
|
+
if (first === '--help' || first === '-h') return { kind: 'help' };
|
|
69
|
+
if (first === '--version') return { kind: 'version' };
|
|
70
|
+
if (first && SUBCOMMANDS.includes(first)) {
|
|
71
|
+
return { kind: 'run', cmd: /** @type {Subcommand} */ (first), args: argv.slice(1) };
|
|
72
|
+
}
|
|
73
|
+
return { kind: 'usage-error', message: `unknown subcommand: ${first}` };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Search env.PATH for an executable named `name`; on win32 also try PATHEXT
|
|
77
|
+
// extensions. Returns the full path or null.
|
|
78
|
+
/**
|
|
79
|
+
* @param {string} name
|
|
80
|
+
* @param {NodeJS.ProcessEnv} env
|
|
81
|
+
* @param {NodeJS.Platform} platform
|
|
82
|
+
* @returns {string | null}
|
|
83
|
+
*/
|
|
84
|
+
function findTool(name, env, platform) {
|
|
85
|
+
const pathVar = env.PATH || env.Path || '';
|
|
86
|
+
const dirs = pathVar.split(path.delimiter).filter(Boolean);
|
|
87
|
+
const exts = platform === 'win32'
|
|
88
|
+
? (env.PATHEXT || '.EXE;.CMD;.BAT;.COM').split(';')
|
|
89
|
+
: [''];
|
|
90
|
+
for (const dir of dirs) {
|
|
91
|
+
for (const ext of exts) {
|
|
92
|
+
const candidate = path.join(dir, name + ext.toLowerCase());
|
|
93
|
+
try {
|
|
94
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
95
|
+
return candidate;
|
|
96
|
+
} catch (err) { /* keep looking */ }
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// POSIX: `sh` on PATH. Windows: Git Bash at its standard install paths,
|
|
103
|
+
// then `bash` on PATH.
|
|
104
|
+
/**
|
|
105
|
+
* @param {NodeJS.ProcessEnv} env
|
|
106
|
+
* @param {NodeJS.Platform} platform
|
|
107
|
+
* @returns {string | null}
|
|
108
|
+
*/
|
|
109
|
+
function discoverShell(env, platform) {
|
|
110
|
+
if (platform !== 'win32') return findTool('sh', env, platform);
|
|
111
|
+
for (const candidate of GIT_BASH_CANDIDATES) {
|
|
112
|
+
try {
|
|
113
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
114
|
+
return candidate;
|
|
115
|
+
} catch (err) { /* keep looking */ }
|
|
116
|
+
}
|
|
117
|
+
return findTool('bash', env, platform);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Tool preflight; never touches Codex state. codex is required for subcommands
|
|
121
|
+
// whose scripts read or mutate Codex state.
|
|
122
|
+
/**
|
|
123
|
+
* @param {Subcommand} cmd
|
|
124
|
+
* @param {NodeJS.ProcessEnv} env
|
|
125
|
+
* @param {NodeJS.Platform} platform
|
|
126
|
+
* @returns {PreflightResult}
|
|
127
|
+
*/
|
|
128
|
+
function preflight(cmd, env, platform) {
|
|
129
|
+
const errors = [];
|
|
130
|
+
for (const tool of ['git', 'python3']) {
|
|
131
|
+
if (!findTool(tool, env, platform)) {
|
|
132
|
+
errors.push(`required command not found: ${tool} — install ${tool} and re-run`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const shell = discoverShell(env, platform);
|
|
136
|
+
if (!shell) {
|
|
137
|
+
errors.push(platform === 'win32'
|
|
138
|
+
? 'no POSIX shell found — install Git for Windows (provides bash) or use WSL2'
|
|
139
|
+
: 'required command not found: sh');
|
|
140
|
+
}
|
|
141
|
+
if (CODEX_SUBCOMMANDS.includes(cmd)) {
|
|
142
|
+
const codexBin = env.SUPERPOWERS_CODEX || 'codex';
|
|
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) };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// POSIX executes the script directly (#!/bin/sh shebang); Windows cannot
|
|
157
|
+
// spawn extensionless scripts, so the discovered shell runs the script as
|
|
158
|
+
// its first argument.
|
|
159
|
+
/**
|
|
160
|
+
* @param {Subcommand} cmd
|
|
161
|
+
* @param {string[]} args
|
|
162
|
+
* @param {string} root
|
|
163
|
+
* @param {string} shell
|
|
164
|
+
* @param {NodeJS.Platform} platform
|
|
165
|
+
* @returns {SpawnDescriptor}
|
|
166
|
+
*/
|
|
167
|
+
function buildSpawn(cmd, args, root, shell, platform) {
|
|
168
|
+
const script = path.join(root, 'scripts', cmd);
|
|
169
|
+
if (platform === 'win32') {
|
|
170
|
+
return { file: shell, argv: [script, ...args] };
|
|
171
|
+
}
|
|
172
|
+
return { file: script, argv: args };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function usage() {
|
|
176
|
+
return [
|
|
177
|
+
'usage: superpowers-manager [prepare|probe|install|update|uninstall] [args...]',
|
|
178
|
+
'',
|
|
179
|
+
' prepare fetch the pinned upstream ref and generate the plugin tree',
|
|
180
|
+
' probe report upstream/generated/installed status (accepts --porcelain)',
|
|
181
|
+
' install register this package root as a Codex marketplace and install the plugin',
|
|
182
|
+
' update probe, then prepare/install only if needed (default when no subcommand)',
|
|
183
|
+
' uninstall remove the manager plugin and marketplace from Codex',
|
|
184
|
+
'',
|
|
185
|
+
'Environment overrides (passed through to the scripts): SUPERPOWERS_REF,',
|
|
186
|
+
'SUPERPOWERS_UPSTREAM_URL, SUPERPOWERS_CODEX, SUPERPOWERS_CACHE_DIR,',
|
|
187
|
+
'SUPERPOWERS_PLUGIN_ROOT, SUPERPOWERS_MANIFEST_TEMPLATE,',
|
|
188
|
+
'SUPERPOWERS_VALIDATOR,',
|
|
189
|
+
'SUPERPOWERS_INSTALLED_SEARCH_ROOT, SUPERPOWERS_INSTALL_REFRESH_MODE',
|
|
190
|
+
].join('\n');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function main() {
|
|
194
|
+
const parsed = parseArgs(process.argv.slice(2));
|
|
195
|
+
if (parsed.kind === 'help') {
|
|
196
|
+
console.log(usage());
|
|
197
|
+
process.exit(0);
|
|
198
|
+
}
|
|
199
|
+
const root = resolvePackageRoot(__filename);
|
|
200
|
+
if (!root) {
|
|
201
|
+
console.error('error: cannot resolve the superpowers-manager package root');
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
if (parsed.kind === 'version') {
|
|
205
|
+
console.log(JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')).version);
|
|
206
|
+
process.exit(0);
|
|
207
|
+
}
|
|
208
|
+
if (parsed.kind === 'usage-error') {
|
|
209
|
+
console.error(`error: ${parsed.message}`);
|
|
210
|
+
console.error(usage());
|
|
211
|
+
process.exit(2);
|
|
212
|
+
}
|
|
213
|
+
const pf = preflight(parsed.cmd, process.env, process.platform);
|
|
214
|
+
if (!pf.ok) {
|
|
215
|
+
for (const e of pf.errors) console.error(`error: ${e}`);
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
const script = path.join(root, 'scripts', parsed.cmd);
|
|
219
|
+
if (!fs.existsSync(script)) {
|
|
220
|
+
console.error(`error: missing script: ${script}`);
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
const spawn = buildSpawn(parsed.cmd, parsed.args, root, pf.shell, process.platform);
|
|
224
|
+
// env is inherited wholesale, so every SUPERPOWERS_* override passes through.
|
|
225
|
+
const res = spawnSync(spawn.file, spawn.argv, { stdio: 'inherit', env: process.env });
|
|
226
|
+
if (res.error) {
|
|
227
|
+
console.error(`error: cannot run ${spawn.file}: ${res.error.message}`);
|
|
228
|
+
process.exit(1);
|
|
229
|
+
}
|
|
230
|
+
process.exit(res.status === null ? 1 : res.status);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
module.exports = { resolvePackageRoot, parseArgs, findTool, discoverShell, preflight, buildSpawn, usage };
|
|
234
|
+
|
|
235
|
+
if (require.main === module) main();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
latest-release
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "superpowers-manager",
|
|
3
|
+
"version": "0.1.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
|
+
"keywords": [
|
|
6
|
+
"superpowers",
|
|
7
|
+
"obra-superpowers",
|
|
8
|
+
"agent-skills",
|
|
9
|
+
"ai-coding-agent",
|
|
10
|
+
"coding-agent",
|
|
11
|
+
"agent-harness",
|
|
12
|
+
"codex",
|
|
13
|
+
"codex-plugin",
|
|
14
|
+
"plugin-manager",
|
|
15
|
+
"cli",
|
|
16
|
+
"installer",
|
|
17
|
+
"updater"
|
|
18
|
+
],
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/j7an/superpowers-manager.git"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/j7an/superpowers-manager#readme",
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/j7an/superpowers-manager/issues"
|
|
27
|
+
},
|
|
28
|
+
"bin": {
|
|
29
|
+
"superpowers-manager": "bin/superpowers-manager.js"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=24"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"bin/",
|
|
36
|
+
"scripts/",
|
|
37
|
+
"config/upstream-ref",
|
|
38
|
+
".agents/plugins/marketplace.json",
|
|
39
|
+
"plugins/superpowers/.codex-plugin/plugin.template.json",
|
|
40
|
+
"README.md"
|
|
41
|
+
]
|
|
42
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "superpowers",
|
|
3
|
+
"version": "0.0.0+manager.template",
|
|
4
|
+
"description": "Local manager for upstream Superpowers skills.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "superpowers-manager"
|
|
7
|
+
},
|
|
8
|
+
"repository": "https://github.com/obra/superpowers",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"keywords": ["superpowers", "skills", "codex"],
|
|
11
|
+
"skills": "./skills/",
|
|
12
|
+
"interface": {
|
|
13
|
+
"displayName": "Superpowers",
|
|
14
|
+
"shortDescription": "Upstream Superpowers skills from a local manager.",
|
|
15
|
+
"longDescription": "A local Codex manager that prepares upstream Superpowers runtime files from the configured Git ref.",
|
|
16
|
+
"developerName": "superpowers-manager",
|
|
17
|
+
"category": "Developer Tools",
|
|
18
|
+
"capabilities": ["skills"],
|
|
19
|
+
"defaultPrompt": [
|
|
20
|
+
"Use Superpowers skills when they match the task."
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
}
|
package/scripts/install
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -eu
|
|
3
|
+
|
|
4
|
+
root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
|
|
5
|
+
. "$root/scripts/lib.sh"
|
|
6
|
+
|
|
7
|
+
codex_bin="${SUPERPOWERS_CODEX:-codex}"
|
|
8
|
+
refresh_mode="${SUPERPOWERS_INSTALL_REFRESH_MODE:-add-only}"
|
|
9
|
+
|
|
10
|
+
spw_require_command "$codex_bin"
|
|
11
|
+
spw_require_command python3
|
|
12
|
+
|
|
13
|
+
case "$refresh_mode" in
|
|
14
|
+
add-only|remove-add) ;;
|
|
15
|
+
*) spw_die "unsupported SUPERPOWERS_INSTALL_REFRESH_MODE: $refresh_mode" ;;
|
|
16
|
+
esac
|
|
17
|
+
|
|
18
|
+
echo "Note: remove or disable conflicting Superpowers providers yourself before relying on manager-provided skills."
|
|
19
|
+
|
|
20
|
+
# Prepare-before-mutate: probe, and (re)generate the plugin tree if it is
|
|
21
|
+
# missing or stale — including validation — entirely before any Codex
|
|
22
|
+
# marketplace or plugin mutation. A failed prepare leaves Codex untouched.
|
|
23
|
+
probe_output=$(sh "$root/scripts/probe" --porcelain)
|
|
24
|
+
identity_state=$(printf '%s\n' "$probe_output" | awk -F= '$1 == "identity_state" { print $2 }')
|
|
25
|
+
[ -n "$identity_state" ] || spw_die "probe did not report Codex identity state"
|
|
26
|
+
spw_require_no_legacy_state "$identity_state"
|
|
27
|
+
status=$(printf '%s\n' "$probe_output" | awk -F= '$1 == "status" { print $2 }')
|
|
28
|
+
case "$status" in
|
|
29
|
+
needs\ prepare)
|
|
30
|
+
sh "$root/scripts/prepare"
|
|
31
|
+
;;
|
|
32
|
+
needs\ install|current)
|
|
33
|
+
;;
|
|
34
|
+
*)
|
|
35
|
+
printf '%s\n' "$probe_output"
|
|
36
|
+
spw_die "unknown probe status: $status"
|
|
37
|
+
;;
|
|
38
|
+
esac
|
|
39
|
+
|
|
40
|
+
desired_commit=$(spw_metadata_commit_or_empty "$root/plugins/superpowers/.superpowers-upstream.json" || true)
|
|
41
|
+
[ -n "$desired_commit" ] || spw_die "generated metadata missing desired commit after prepare"
|
|
42
|
+
|
|
43
|
+
# Reconcile the manager marketplace pointer to this package root, then always
|
|
44
|
+
# run the Codex install step: `install` means "make Codex use this root" and
|
|
45
|
+
# never skips it because the generated tree looks current.
|
|
46
|
+
spw_reconcile_marketplace "$codex_bin" "$root"
|
|
47
|
+
|
|
48
|
+
case "$refresh_mode" in
|
|
49
|
+
remove-add)
|
|
50
|
+
"$codex_bin" plugin remove "$SPW_PLUGIN_ID" || true
|
|
51
|
+
;;
|
|
52
|
+
esac
|
|
53
|
+
|
|
54
|
+
if ! "$codex_bin" plugin add "$SPW_PLUGIN_ID"; then
|
|
55
|
+
spw_die "codex plugin add failed for $SPW_PLUGIN_ID"
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
spw_verify_refresh "$desired_commit"
|