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.
@@ -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
+ }
@@ -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"