superpowers-manager 0.2.0 → 0.4.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 +340 -66
- package/dist/adapter-result.js +204 -0
- package/dist/adapter.js +690 -0
- package/dist/atomic.js +142 -0
- package/dist/cli-arguments.js +32 -0
- package/dist/cli.js +313 -0
- package/dist/codex-harness.js +271 -0
- package/dist/codex-json.js +89 -0
- package/dist/codex-prepare.js +199 -0
- package/dist/codex-presentation.js +259 -0
- package/dist/codex-state.js +78 -0
- package/dist/commands/context.js +1 -0
- package/dist/commands/install.js +291 -0
- package/dist/commands/pin.js +79 -0
- package/dist/commands/prepare.js +420 -0
- package/dist/commands/probe.js +257 -0
- package/dist/commands/track-latest.js +44 -0
- package/dist/commands/uninstall.js +217 -0
- package/dist/commands/unpin.js +95 -0
- package/dist/commands/update.js +79 -0
- package/dist/domain/fingerprint.js +4 -0
- package/dist/domain/refs.js +37 -0
- package/dist/effective-selection.js +123 -0
- package/dist/generated-plugin.js +748 -0
- package/dist/git.js +38 -0
- package/dist/harness.js +1 -0
- package/dist/hooks.js +290 -0
- package/dist/lifecycle.js +135 -0
- package/dist/manifest-overlay.js +125 -0
- package/dist/provenance.js +111 -0
- package/dist/python-json-format.js +83 -0
- package/dist/python-json.js +29 -0
- package/dist/python-text.js +74 -0
- package/dist/safe-path.js +137 -0
- package/dist/safety-error.js +10 -0
- package/dist/selection-state-cli.js +85 -0
- package/dist/selection-store.js +172 -0
- package/dist/selection.js +200 -0
- package/dist/status.js +24 -0
- package/dist/strict-json.js +248 -0
- package/dist/upstream-cli.js +110 -0
- package/dist/upstream-version.js +40 -0
- package/dist/upstream.js +253 -0
- package/dist/validate-generated-plugin-cli.js +118 -0
- package/dist/validator.js +317 -0
- package/dist/workspace.js +139 -0
- package/package.json +25 -4
- package/bin/superpowers-manager.js +0 -290
- package/scripts/adapters/codex/adapter +0 -603
- package/scripts/adapters/codex/lib.sh +0 -449
- package/scripts/adapters/codex/validate-generated-plugin.py +0 -314
- package/scripts/core/adapter.sh +0 -103
- package/scripts/core/common.sh +0 -59
- package/scripts/core/lifecycle.sh +0 -141
- package/scripts/core/provenance.sh +0 -135
- package/scripts/core/selection-state.py +0 -414
- package/scripts/core/selection.sh +0 -168
- package/scripts/core/status.sh +0 -27
- package/scripts/core/upstream.sh +0 -280
- package/scripts/core/validate-adapter-response.py +0 -284
- package/scripts/install +0 -59
- package/scripts/pin +0 -51
- package/scripts/prepare +0 -117
- package/scripts/probe +0 -82
- package/scripts/track-latest +0 -30
- package/scripts/uninstall +0 -35
- package/scripts/unpin +0 -41
- package/scripts/update +0 -34
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// @ts-check
|
|
3
|
-
|
|
4
|
-
import * as fs from 'node:fs';
|
|
5
|
-
import * as path from 'node:path';
|
|
6
|
-
import { spawnSync } from 'node:child_process';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @typedef {'pin' | 'track-latest' | 'unpin' | '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 = [
|
|
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
|
-
};
|
|
46
|
-
// Mirrors upstream Superpowers' hooks/run-hook.cmd discovery order.
|
|
47
|
-
const GIT_BASH_CANDIDATES = [
|
|
48
|
-
'C:\\Program Files\\Git\\bin\\bash.exe',
|
|
49
|
-
'C:\\Program Files (x86)\\Git\\bin\\bash.exe',
|
|
50
|
-
];
|
|
51
|
-
|
|
52
|
-
// Walk upward from the bin's physical location to the directory containing
|
|
53
|
-
// package.json. realpathSync first: npm/npx expose the bin through a symlink
|
|
54
|
-
// or shim outside the package root.
|
|
55
|
-
/**
|
|
56
|
-
* @param {string} scriptPath
|
|
57
|
-
* @returns {string | null}
|
|
58
|
-
*/
|
|
59
|
-
function resolvePackageRoot(scriptPath) {
|
|
60
|
-
let dir;
|
|
61
|
-
try {
|
|
62
|
-
dir = path.dirname(fs.realpathSync(scriptPath));
|
|
63
|
-
} catch (err) {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
for (;;) {
|
|
67
|
-
if (fs.existsSync(path.join(dir, 'package.json'))) return dir;
|
|
68
|
-
const parent = path.dirname(dir);
|
|
69
|
-
if (parent === dir) return null;
|
|
70
|
-
dir = parent;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
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
|
-
|
|
84
|
-
/**
|
|
85
|
-
* @param {string[]} argv
|
|
86
|
-
* @returns {ParseResult}
|
|
87
|
-
*/
|
|
88
|
-
function parseArgs(argv) {
|
|
89
|
-
/** @type {string | undefined} */
|
|
90
|
-
const first = argv[0];
|
|
91
|
-
if (argv.length === 0) return { kind: 'run', cmd: 'update', args: [] };
|
|
92
|
-
if (first === '--help' || first === '-h') return { kind: 'help' };
|
|
93
|
-
if (first === '--version') return { kind: 'version' };
|
|
94
|
-
if (first && SUBCOMMANDS.includes(first)) {
|
|
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 };
|
|
109
|
-
}
|
|
110
|
-
return { kind: 'usage-error', message: `unknown subcommand: ${first}` };
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Search env.PATH for an executable named `name`; on win32 also try PATHEXT
|
|
114
|
-
// extensions. Returns the full path or null.
|
|
115
|
-
/**
|
|
116
|
-
* @param {string} name
|
|
117
|
-
* @param {NodeJS.ProcessEnv} env
|
|
118
|
-
* @param {NodeJS.Platform} platform
|
|
119
|
-
* @returns {string | null}
|
|
120
|
-
*/
|
|
121
|
-
function findTool(name, env, platform) {
|
|
122
|
-
const pathVar = env.PATH || env.Path || '';
|
|
123
|
-
const dirs = pathVar.split(path.delimiter).filter(Boolean);
|
|
124
|
-
const exts = platform === 'win32'
|
|
125
|
-
? (env.PATHEXT || '.EXE;.CMD;.BAT;.COM').split(';')
|
|
126
|
-
: [''];
|
|
127
|
-
for (const dir of dirs) {
|
|
128
|
-
for (const ext of exts) {
|
|
129
|
-
const candidate = path.join(dir, name + ext.toLowerCase());
|
|
130
|
-
try {
|
|
131
|
-
fs.accessSync(candidate, fs.constants.X_OK);
|
|
132
|
-
return candidate;
|
|
133
|
-
} catch (err) { /* keep looking */ }
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// POSIX: `sh` on PATH. Windows: Git Bash at its standard install paths,
|
|
140
|
-
// then `bash` on PATH.
|
|
141
|
-
/**
|
|
142
|
-
* @param {NodeJS.ProcessEnv} env
|
|
143
|
-
* @param {NodeJS.Platform} platform
|
|
144
|
-
* @returns {string | null}
|
|
145
|
-
*/
|
|
146
|
-
function discoverShell(env, platform) {
|
|
147
|
-
if (platform !== 'win32') return findTool('sh', env, platform);
|
|
148
|
-
for (const candidate of GIT_BASH_CANDIDATES) {
|
|
149
|
-
try {
|
|
150
|
-
fs.accessSync(candidate, fs.constants.X_OK);
|
|
151
|
-
return candidate;
|
|
152
|
-
} catch (err) { /* keep looking */ }
|
|
153
|
-
}
|
|
154
|
-
return findTool('bash', env, platform);
|
|
155
|
-
}
|
|
156
|
-
|
|
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.
|
|
166
|
-
/**
|
|
167
|
-
* @param {Subcommand} cmd
|
|
168
|
-
* @param {NodeJS.ProcessEnv} env
|
|
169
|
-
* @param {NodeJS.Platform} platform
|
|
170
|
-
* @returns {PreflightResult}
|
|
171
|
-
*/
|
|
172
|
-
function preflight(cmd, env, platform) {
|
|
173
|
-
const errors = [];
|
|
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)) {
|
|
185
|
-
errors.push(`required command not found: ${tool} — install ${tool} and re-run`);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
const shell = discoverShell(env, platform);
|
|
189
|
-
if (!shell) {
|
|
190
|
-
errors.push(platform === 'win32'
|
|
191
|
-
? 'no POSIX shell found — install Git for Windows (provides bash) or use WSL2'
|
|
192
|
-
: 'required command not found: sh');
|
|
193
|
-
}
|
|
194
|
-
if (errors.length) return { ok: false, errors };
|
|
195
|
-
return { ok: true, shell: /** @type {string} */ (shell) };
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// POSIX executes the script directly (#!/bin/sh shebang); Windows cannot
|
|
199
|
-
// spawn extensionless scripts, so the discovered shell runs the script as
|
|
200
|
-
// its first argument.
|
|
201
|
-
/**
|
|
202
|
-
* @param {Subcommand} cmd
|
|
203
|
-
* @param {string[]} args
|
|
204
|
-
* @param {string} root
|
|
205
|
-
* @param {string} shell
|
|
206
|
-
* @param {NodeJS.Platform} platform
|
|
207
|
-
* @returns {SpawnDescriptor}
|
|
208
|
-
*/
|
|
209
|
-
function buildSpawn(cmd, args, root, shell, platform) {
|
|
210
|
-
const script = path.join(root, 'scripts', cmd);
|
|
211
|
-
if (platform === 'win32') {
|
|
212
|
-
return { file: shell, argv: [script, ...args] };
|
|
213
|
-
}
|
|
214
|
-
return { file: script, argv: args };
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function usage() {
|
|
218
|
-
return [
|
|
219
|
-
'usage: superpowers-manager [command] [args...]',
|
|
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:',
|
|
227
|
-
' prepare fetch the pinned upstream ref and generate the plugin tree',
|
|
228
|
-
' probe report upstream/generated/installed status (accepts --porcelain)',
|
|
229
|
-
' install register this package root as a Codex marketplace and install the plugin',
|
|
230
|
-
' update probe, then prepare/install only if needed (default when no subcommand)',
|
|
231
|
-
' uninstall remove the manager plugin and marketplace from Codex',
|
|
232
|
-
'',
|
|
233
|
-
'Environment overrides (passed through to the scripts): SUPERPOWERS_REF,',
|
|
234
|
-
'SUPERPOWERS_UPSTREAM_URL, SUPERPOWERS_CODEX, SUPERPOWERS_CACHE_DIR,',
|
|
235
|
-
'SUPERPOWERS_CONFIG_DIR, XDG_CONFIG_HOME,',
|
|
236
|
-
'SUPERPOWERS_PLUGIN_ROOT, SUPERPOWERS_MANIFEST_TEMPLATE,',
|
|
237
|
-
'SUPERPOWERS_VALIDATOR,',
|
|
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.',
|
|
242
|
-
].join('\n');
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function main() {
|
|
246
|
-
const parsed = parseArgs(process.argv.slice(2));
|
|
247
|
-
if (parsed.kind === 'help') {
|
|
248
|
-
console.log(usage());
|
|
249
|
-
process.exit(0);
|
|
250
|
-
}
|
|
251
|
-
const root = resolvePackageRoot(import.meta.filename);
|
|
252
|
-
if (!root) {
|
|
253
|
-
console.error('error: cannot resolve the superpowers-manager package root');
|
|
254
|
-
process.exit(1);
|
|
255
|
-
}
|
|
256
|
-
if (parsed.kind === 'version') {
|
|
257
|
-
console.log(JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')).version);
|
|
258
|
-
process.exit(0);
|
|
259
|
-
}
|
|
260
|
-
if (parsed.kind === 'usage-error') {
|
|
261
|
-
console.error(`error: ${parsed.message}`);
|
|
262
|
-
console.error(usage());
|
|
263
|
-
process.exit(2);
|
|
264
|
-
}
|
|
265
|
-
const pf = preflight(parsed.cmd, process.env, process.platform);
|
|
266
|
-
if (!pf.ok) {
|
|
267
|
-
for (const e of pf.errors) console.error(`error: ${e}`);
|
|
268
|
-
process.exit(1);
|
|
269
|
-
}
|
|
270
|
-
const script = path.join(root, 'scripts', parsed.cmd);
|
|
271
|
-
if (!fs.existsSync(script)) {
|
|
272
|
-
console.error(`error: missing script: ${script}`);
|
|
273
|
-
process.exit(1);
|
|
274
|
-
}
|
|
275
|
-
const spawn = buildSpawn(parsed.cmd, parsed.args, root, pf.shell, process.platform);
|
|
276
|
-
// env is inherited wholesale, so every SUPERPOWERS_* override passes through.
|
|
277
|
-
const res = spawnSync(spawn.file, spawn.argv, { stdio: 'inherit', env: process.env });
|
|
278
|
-
if (res.error) {
|
|
279
|
-
console.error(`error: cannot run ${spawn.file}: ${res.error.message}`);
|
|
280
|
-
process.exit(1);
|
|
281
|
-
}
|
|
282
|
-
process.exit(res.status === null ? 1 : res.status);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
export {
|
|
286
|
-
resolvePackageRoot, isMain, parseArgs, findTool, discoverShell,
|
|
287
|
-
commandRequirements, preflight, buildSpawn, usage,
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
if (isMain(import.meta.filename, process.argv[1])) main();
|