superpowers-manager 0.8.0 → 0.9.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 +36 -16
- package/dist/atomic.js +1 -3
- package/dist/cli-arguments.js +4 -1
- package/dist/cli.js +14 -43
- package/dist/commands/adapter-call.js +24 -0
- package/dist/commands/install.js +16 -63
- package/dist/commands/mutation.js +5 -5
- package/dist/commands/pin.js +10 -5
- package/dist/commands/prepare.js +42 -108
- package/dist/commands/probe.js +33 -19
- package/dist/commands/uninstall.js +18 -46
- package/dist/commands/unpin.js +2 -1
- package/dist/commands/update.js +8 -29
- package/dist/domain/refs.js +0 -3
- package/dist/effective-selection.js +11 -15
- package/dist/git-tree.js +4 -6
- package/dist/harness-command-result.js +33 -3
- package/dist/harnesses/claude-code/harness.js +39 -0
- package/dist/harnesses/claude-code/install.js +380 -0
- package/dist/harnesses/claude-code/native.js +79 -0
- package/dist/harnesses/claude-code/paths.js +35 -0
- package/dist/harnesses/claude-code/prepare.js +117 -0
- package/dist/harnesses/claude-code/presentation.js +25 -0
- package/dist/harnesses/claude-code/state.js +253 -0
- package/dist/harnesses/codex/adapter.js +27 -68
- package/dist/harnesses/codex/conflicts.js +3 -8
- package/dist/harnesses/codex/generated-plugin.js +4 -11
- package/dist/harnesses/codex/harness.js +4 -26
- package/dist/harnesses/codex/hooks.js +11 -27
- package/dist/harnesses/codex/lifecycle.js +22 -107
- package/dist/harnesses/codex/manifest-overlay.js +9 -27
- package/dist/harnesses/codex/paths.js +29 -0
- package/dist/harnesses/codex/prepare.js +9 -43
- package/dist/harnesses/codex/presentation.js +1 -1
- package/dist/harnesses/codex/publication.js +5 -40
- package/dist/harnesses/codex/state.js +1 -6
- package/dist/harnesses/codex/stored-state.js +1 -6
- package/dist/harnesses/opencode/config.js +2 -4
- package/dist/harnesses/opencode/discovery.js +1 -5
- package/dist/harnesses/opencode/install.js +12 -93
- package/dist/harnesses/opencode/native.js +28 -49
- package/dist/harnesses/opencode/package.js +11 -55
- package/dist/harnesses/opencode/prepare.js +2 -7
- package/dist/harnesses/opencode/presentation.js +2 -7
- package/dist/harnesses/pi/install.js +31 -120
- package/dist/harnesses/pi/native.js +9 -34
- package/dist/harnesses/pi/package.js +9 -50
- package/dist/harnesses/pi/prepare.js +3 -8
- package/dist/harnesses/pi/presentation.js +2 -7
- package/dist/harnesses/pi/settings.js +5 -9
- package/dist/harnesses/pi/shared-skills.js +23 -53
- package/dist/harnesses/pi/state.js +3 -10
- package/dist/provenance.js +3 -19
- package/dist/resource-lock.js +11 -16
- package/dist/safe-path.js +28 -1
- package/dist/selection.js +15 -19
- package/dist/snapshot-journal.js +101 -0
- package/dist/snapshot-package.js +62 -1
- package/dist/snapshot-prepare.js +9 -2
- package/dist/snapshot-presentation.js +9 -0
- package/dist/upstream.js +8 -12
- package/dist/validator.js +26 -23
- package/dist/workspace.js +29 -35
- package/package.json +10 -9
- package/dist/python-json.js +0 -4
- package/dist/upstream-workspace.js +0 -8
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Superpowers Manager
|
|
2
2
|
|
|
3
|
-
Install [`obra/superpowers`](https://github.com/obra/superpowers) directly from upstream for Codex, the Pi coding agent, and
|
|
3
|
+
Install [`obra/superpowers`](https://github.com/obra/superpowers) directly from upstream for Codex, the Pi coding agent, OpenCode, and Claude Code. Stay on a known-good release or commit, try a branch, and upgrade when you choose—without waiting for a marketplace copy to catch up.
|
|
4
4
|
|
|
5
5
|
> Unofficial community integration. Not affiliated with the `obra/superpowers` maintainers.
|
|
6
6
|
|
|
@@ -21,7 +21,7 @@ npx superpowers-manager pin v6.1.1
|
|
|
21
21
|
npx superpowers-manager install --harness codex
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
Use `--harness pi` for Pi or `--harness
|
|
24
|
+
Use `--harness pi` for Pi, `--harness opencode` for OpenCode, or `--harness claude-code` for Claude Code. The pin is shared by all harnesses, but each installation changes only when you run its `install` or `update` command. A saved pin keeps subsequent installs and updates on that version unless you change or override the selection.
|
|
25
25
|
|
|
26
26
|
To try a branch, replace `feature/foo` with an existing upstream branch:
|
|
27
27
|
|
|
@@ -42,7 +42,7 @@ See [version selection](https://github.com/j7an/superpowers-manager/blob/main/do
|
|
|
42
42
|
|
|
43
43
|
## Start with your agent
|
|
44
44
|
|
|
45
|
-
The
|
|
45
|
+
The four harnesses share upstream selection, while each has independent prepared and installed state. Omitting `--harness` selects Codex.
|
|
46
46
|
|
|
47
47
|
### Codex
|
|
48
48
|
|
|
@@ -84,6 +84,19 @@ OpenCode after a successful install, activating update, or removal. See the
|
|
|
84
84
|
[OpenCode reference](https://github.com/j7an/superpowers-manager/blob/main/docs/opencode.md)
|
|
85
85
|
for global configuration, conflicts, and recovery behavior.
|
|
86
86
|
|
|
87
|
+
### Claude Code
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
npx superpowers-manager install --harness claude-code
|
|
91
|
+
npx superpowers-manager probe --harness claude-code
|
|
92
|
+
npx superpowers-manager update --harness claude-code
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Claude Code installs a validated, Manager-owned local marketplace snapshot.
|
|
96
|
+
Restart Claude Code or run `/reload-plugins` after an install, activating update,
|
|
97
|
+
or removal. See the [Claude Code reference](https://github.com/j7an/superpowers-manager/blob/main/docs/claude-code.md)
|
|
98
|
+
for conflicts, hooks, and recovery.
|
|
99
|
+
|
|
87
100
|
To remove a Manager-owned installation, choose one harness explicitly:
|
|
88
101
|
|
|
89
102
|
### Uninstall Codex
|
|
@@ -104,23 +117,30 @@ npx superpowers-manager uninstall --harness pi
|
|
|
104
117
|
npx superpowers-manager uninstall --harness opencode
|
|
105
118
|
```
|
|
106
119
|
|
|
120
|
+
### Uninstall Claude Code
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
npx superpowers-manager uninstall --harness claude-code
|
|
124
|
+
```
|
|
125
|
+
|
|
107
126
|
The manager never removes another Superpowers provider. See the [ownership guidance](https://github.com/j7an/superpowers-manager/blob/main/docs/usage.md#provider-ownership) before switching providers.
|
|
108
127
|
|
|
109
128
|
## Shared selection and lifecycle
|
|
110
129
|
|
|
111
|
-
The [selection commands above](#choose-what-you-run) save shared upstream intent. They do not activate Codex, Pi, or
|
|
130
|
+
The [selection commands above](#choose-what-you-run) save shared upstream intent. They do not activate Codex, Pi, OpenCode, or Claude Code; run the selected harness's `install` or `update` to apply that choice. `unpin` restores the packaged fallback policy.
|
|
112
131
|
|
|
113
132
|
See [targeted lifecycle commands](https://github.com/j7an/superpowers-manager/blob/main/docs/usage.md#targeted-lifecycle) for preparation, inspection, installation, updates, and removal.
|
|
114
133
|
|
|
115
|
-
Use `--harness codex`, `--harness pi`, or `--harness
|
|
134
|
+
Use `--harness codex`, `--harness pi`, `--harness opencode`, or `--harness claude-code` with each targeted lifecycle command.
|
|
116
135
|
|
|
117
136
|
```text
|
|
118
137
|
shared selection -> prepare / inspect / activate Codex
|
|
119
138
|
-> prepare / inspect / activate Pi
|
|
120
139
|
-> prepare / inspect / activate OpenCode
|
|
140
|
+
-> prepare / inspect / activate Claude Code
|
|
121
141
|
```
|
|
122
142
|
|
|
123
|
-
The [usage reference](https://github.com/j7an/superpowers-manager/blob/main/docs/usage.md) covers version selection, precedence, offline behavior, `prepare`, validators, and every lifecycle command. The harness references cover [Codex registration and hooks](https://github.com/j7an/superpowers-manager/blob/main/docs/codex.md)
|
|
143
|
+
The [usage reference](https://github.com/j7an/superpowers-manager/blob/main/docs/usage.md) covers version selection, precedence, offline behavior, `prepare`, validators, and every lifecycle command. The harness references cover [Codex registration and hooks](https://github.com/j7an/superpowers-manager/blob/main/docs/codex.md), [Pi snapshots and recovery](https://github.com/j7an/superpowers-manager/blob/main/docs/pi.md), [OpenCode snapshots](https://github.com/j7an/superpowers-manager/blob/main/docs/opencode.md), and [Claude Code marketplace registration](https://github.com/j7an/superpowers-manager/blob/main/docs/claude-code.md).
|
|
124
144
|
|
|
125
145
|
## Requirements and platforms
|
|
126
146
|
|
|
@@ -128,16 +148,16 @@ The installed package requires Node >=24. Native source, tests, and packaging to
|
|
|
128
148
|
|
|
129
149
|
<!-- requirements:begin -->
|
|
130
150
|
|
|
131
|
-
| Command | git | Codex CLI (default) | Pi CLI (`--harness pi`) | OpenCode CLI (`--harness opencode`) |
|
|
132
|
-
| -------------- | --- | ------------------- | ----------------------- | ----------------------------------- |
|
|
133
|
-
| `pin` | yes | no | no | no |
|
|
134
|
-
| `track-latest` | no | no | no | no |
|
|
135
|
-
| `unpin` | no | no | no | no |
|
|
136
|
-
| `prepare` | yes | no | no | no |
|
|
137
|
-
| `probe` | yes | yes | no | no |
|
|
138
|
-
| `install` | yes | yes | yes | yes |
|
|
139
|
-
| `update` | yes | yes | yes | yes |
|
|
140
|
-
| `uninstall` | no | yes | yes | no |
|
|
151
|
+
| Command | git | Codex CLI (default) | Pi CLI (`--harness pi`) | OpenCode CLI (`--harness opencode`) | Claude Code CLI (`--harness claude-code`) |
|
|
152
|
+
| -------------- | --- | ------------------- | ----------------------- | ----------------------------------- | ----------------------------------------- |
|
|
153
|
+
| `pin` | yes | no | no | no | no |
|
|
154
|
+
| `track-latest` | no | no | no | no | no |
|
|
155
|
+
| `unpin` | no | no | no | no | no |
|
|
156
|
+
| `prepare` | yes | no | no | no | no |
|
|
157
|
+
| `probe` | yes | yes | no | no | yes |
|
|
158
|
+
| `install` | yes | yes | yes | yes | yes |
|
|
159
|
+
| `update` | yes | yes | yes | yes | yes |
|
|
160
|
+
| `uninstall` | no | yes | yes | no | yes |
|
|
141
161
|
|
|
142
162
|
<!-- requirements:end -->
|
|
143
163
|
|
package/dist/atomic.js
CHANGED
package/dist/cli-arguments.js
CHANGED
|
@@ -20,7 +20,10 @@ export function extractHarnessOptions(command, argv) {
|
|
|
20
20
|
: token.slice("--harness=".length);
|
|
21
21
|
if (!value || value.startsWith("--"))
|
|
22
22
|
throw new UsageError("option --harness requires a value");
|
|
23
|
-
if (value !== "codex" &&
|
|
23
|
+
if (value !== "codex" &&
|
|
24
|
+
value !== "pi" &&
|
|
25
|
+
value !== "opencode" &&
|
|
26
|
+
value !== "claude-code")
|
|
24
27
|
throw new UsageError(`unknown harness: ${value}`);
|
|
25
28
|
harness = value;
|
|
26
29
|
}
|
package/dist/cli.js
CHANGED
|
@@ -3,6 +3,7 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { extractHarnessOptions, oneLine, UsageError } from "./cli-arguments.js";
|
|
5
5
|
import { codexHarness } from "./harnesses/codex/harness.js";
|
|
6
|
+
import { claudeCodeHarness } from "./harnesses/claude-code/harness.js";
|
|
6
7
|
import { piHarness } from "./harnesses/pi/harness.js";
|
|
7
8
|
import { openCodeHarness } from "./harnesses/opencode/harness.js";
|
|
8
9
|
import { createResourceCoordinator } from "./resource-lock.js";
|
|
@@ -16,23 +17,8 @@ import { runUnpin } from "./commands/unpin.js";
|
|
|
16
17
|
import { runUpdate } from "./commands/update.js";
|
|
17
18
|
import { COMMIT_INPUT_RE, TAG_RE } from "./domain/refs.js";
|
|
18
19
|
import { configurationErrors } from "./validator.js";
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"track-latest",
|
|
22
|
-
"unpin",
|
|
23
|
-
"prepare",
|
|
24
|
-
"probe",
|
|
25
|
-
"install",
|
|
26
|
-
"update",
|
|
27
|
-
"uninstall",
|
|
28
|
-
];
|
|
29
|
-
// Keyed by Subcommand itself. Until slice 6 this was keyed by a mapped type
|
|
30
|
-
// that filtered Subcommand through a DISPATCH table of "spawn" | "in-process"
|
|
31
|
-
// literals; with every command in-process that filter was the identity, so the
|
|
32
|
-
// table, its mode union and the mapped type were one indirection with nothing
|
|
33
|
-
// left to discriminate. The guarantee is unchanged and is the whole protection
|
|
34
|
-
// — tests/bin/bin-dispatch.test.js records that decision: a Subcommand added
|
|
35
|
-
// without a handler registered here is a compile error, not a runtime surprise.
|
|
20
|
+
// Keyed by HarnessCommand: a command added to that type without a handler
|
|
21
|
+
// here is a compile error, not a runtime surprise.
|
|
36
22
|
const IN_PROCESS_HANDLERS = {
|
|
37
23
|
pin: runPin,
|
|
38
24
|
"track-latest": runTrackLatest,
|
|
@@ -98,7 +84,7 @@ function parseArgs(argv) {
|
|
|
98
84
|
return { kind: "help" };
|
|
99
85
|
if (first === "--version")
|
|
100
86
|
return { kind: "version" };
|
|
101
|
-
if (first &&
|
|
87
|
+
if (first && Object.hasOwn(IN_PROCESS_HANDLERS, first)) {
|
|
102
88
|
const command = first;
|
|
103
89
|
let extracted;
|
|
104
90
|
try {
|
|
@@ -228,7 +214,7 @@ function usage() {
|
|
|
228
214
|
" install --harness opencode [--allow-experimental]",
|
|
229
215
|
" update --harness opencode [--allow-experimental]",
|
|
230
216
|
" uninstall --harness opencode",
|
|
231
|
-
" --harness=codex, --harness=pi, and --harness=
|
|
217
|
+
" --harness=codex, --harness=pi, --harness=opencode, and --harness=claude-code are also accepted after the command.",
|
|
232
218
|
"",
|
|
233
219
|
"Environment overrides (used by in-process commands): SUPERPOWERS_REF,",
|
|
234
220
|
"SUPERPOWERS_UPSTREAM_URL, SUPERPOWERS_CODEX, SUPERPOWERS_CACHE_DIR,",
|
|
@@ -236,7 +222,7 @@ function usage() {
|
|
|
236
222
|
"SUPERPOWERS_PLUGIN_ROOT, SUPERPOWERS_MANIFEST_TEMPLATE,",
|
|
237
223
|
"SUPERPOWERS_VALIDATOR_EXECUTABLE,",
|
|
238
224
|
"SUPERPOWERS_INSTALLED_SEARCH_ROOT, SUPERPOWERS_INSTALL_REFRESH_MODE,",
|
|
239
|
-
"SUPERPOWERS_OPENCODE",
|
|
225
|
+
"SUPERPOWERS_OPENCODE, SUPERPOWERS_CLAUDE_CODE, CLAUDE_CONFIG_DIR",
|
|
240
226
|
"",
|
|
241
227
|
"SUPERPOWERS_VALIDATOR is removed; unset it and use",
|
|
242
228
|
"SUPERPOWERS_VALIDATOR_EXECUTABLE with an executable validator.",
|
|
@@ -266,38 +252,23 @@ async function main() {
|
|
|
266
252
|
console.error(usage());
|
|
267
253
|
process.exit(2);
|
|
268
254
|
}
|
|
269
|
-
const status = parsed.options.harness === "
|
|
270
|
-
? await dispatch(
|
|
271
|
-
: parsed.options.harness === "
|
|
272
|
-
? await dispatch(
|
|
273
|
-
:
|
|
255
|
+
const status = parsed.options.harness === "claude-code"
|
|
256
|
+
? await dispatch(claudeCodeHarness, parsed, root)
|
|
257
|
+
: parsed.options.harness === "opencode"
|
|
258
|
+
? await dispatch(openCodeHarness, parsed, root)
|
|
259
|
+
: parsed.options.harness === "pi"
|
|
260
|
+
? await dispatch(piHarness, parsed, root)
|
|
261
|
+
: await dispatch(codexHarness, parsed, root);
|
|
274
262
|
process.exit(status);
|
|
275
263
|
}
|
|
276
|
-
async function dispatch(adapter, parsed) {
|
|
277
|
-
const root = resolvePackageRoot(import.meta.filename);
|
|
278
|
-
if (!root) {
|
|
279
|
-
console.error("error: cannot resolve the superpowers-manager package root");
|
|
280
|
-
return 1;
|
|
281
|
-
}
|
|
264
|
+
async function dispatch(adapter, parsed, root) {
|
|
282
265
|
const pf = preflight(parsed.cmd, process.env, process.platform, adapter);
|
|
283
266
|
if (!pf.ok) {
|
|
284
267
|
for (const e of pf.errors)
|
|
285
268
|
console.error(`error: ${e}`);
|
|
286
269
|
return 1;
|
|
287
270
|
}
|
|
288
|
-
// Dispatch is no longer a branch: slice 4b flipped the last spawned command,
|
|
289
|
-
// so every subcommand runs here, and slice 6 deleted the DISPATCH table whose
|
|
290
|
-
// only remaining job was narrowing this registry's key type. `parsed.cmd` is
|
|
291
|
-
// a Subcommand and the registry is keyed by Subcommand, so no cast is needed.
|
|
292
|
-
// The exhaustiveness check on IN_PROCESS_HANDLERS makes an unregistered
|
|
293
|
-
// handler a compile error; the `!handler` guard below is the runtime backstop
|
|
294
|
-
// for that guarantee. It is unreachable through production code, and no
|
|
295
|
-
// fixture reaches it either.
|
|
296
271
|
const handler = IN_PROCESS_HANDLERS[parsed.cmd];
|
|
297
|
-
if (!handler) {
|
|
298
|
-
console.error(`error: no in-process handler registered for: ${parsed.cmd}`);
|
|
299
|
-
return 1;
|
|
300
|
-
}
|
|
301
272
|
const ctx = {
|
|
302
273
|
root,
|
|
303
274
|
env: process.env,
|
|
@@ -26,3 +26,27 @@ export class GatherFailure extends Error {
|
|
|
26
26
|
this.outcomes = outcomes;
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
+
export function writeOutput(output, ctx) {
|
|
30
|
+
for (const line of output.stdout)
|
|
31
|
+
ctx.stdout.write(`${line}\n`);
|
|
32
|
+
for (const line of output.stderr)
|
|
33
|
+
ctx.stderr.write(`${line}\n`);
|
|
34
|
+
}
|
|
35
|
+
// callAdapter plus outcome collection: every returned result's outcome is
|
|
36
|
+
// recorded for replay. acceptValue turns a successful value it rejects into an
|
|
37
|
+
// invalid status without recording that outcome.
|
|
38
|
+
export async function invoke(call, failure, outcomes, acceptValue) {
|
|
39
|
+
let result = await callAdapter(call, failure);
|
|
40
|
+
if (result.ok && acceptValue !== undefined) {
|
|
41
|
+
try {
|
|
42
|
+
if (!acceptValue(result.result.outcome.result))
|
|
43
|
+
result = { ok: false, result: null, message: failure.invalidStatus };
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
result = { ok: false, result: null, message: failure.invalidStatus };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (result.result !== null)
|
|
50
|
+
outcomes.push(result.result.outcome);
|
|
51
|
+
return result;
|
|
52
|
+
}
|
package/dist/commands/install.js
CHANGED
|
@@ -1,40 +1,21 @@
|
|
|
1
1
|
import { tmpdir } from "node:os";
|
|
2
2
|
import { oneLine } from "../cli-arguments.js";
|
|
3
|
-
import {
|
|
4
|
-
import { GatherFailure,
|
|
5
|
-
import {
|
|
3
|
+
import { withWorkspaceReporting, } from "../workspace.js";
|
|
4
|
+
import { GatherFailure, invoke, writeOutput } from "./adapter-call.js";
|
|
5
|
+
import { probeFacts, replayOutcome } from "./probe.js";
|
|
6
6
|
import { runPrepare } from "./prepare.js";
|
|
7
7
|
import { runWithMutation } from "./mutation.js";
|
|
8
8
|
import { activationBlock } from "../harness-compatibility.js";
|
|
9
|
-
export function writeOutput(output, ctx) {
|
|
10
|
-
for (const line of output.stdout)
|
|
11
|
-
ctx.stdout.write(`${line}\n`);
|
|
12
|
-
for (const line of output.stderr)
|
|
13
|
-
ctx.stderr.write(`${line}\n`);
|
|
14
|
-
}
|
|
15
|
-
async function invoke(call, failure, outcomes, acceptValue) {
|
|
16
|
-
let result = await callAdapter(call, failure);
|
|
17
|
-
if (result.ok && acceptValue !== undefined) {
|
|
18
|
-
try {
|
|
19
|
-
if (!acceptValue(result.result.outcome.result))
|
|
20
|
-
result = { ok: false, result: null, message: failure.invalidStatus };
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
result = { ok: false, result: null, message: failure.invalidStatus };
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
if (result.result !== null)
|
|
27
|
-
outcomes.push(result.result.outcome);
|
|
28
|
-
return result;
|
|
29
|
-
}
|
|
30
9
|
// Collect outcomes without writing so output failures cannot be classified as
|
|
31
10
|
// inspection failures. Replay collected outcomes after gathering completes.
|
|
11
|
+
// The callback never throws: invoke() catches every ctx.adapter failure and
|
|
12
|
+
// every predicate here is pure, so a post-success cleanup failure is the only
|
|
13
|
+
// case withWorkspaceReporting's cleanupWarning has to carry.
|
|
32
14
|
async function gatherInstallStages(ctx, selection, artifact) {
|
|
33
15
|
const parent = ctx.env.TMPDIR ?? tmpdir();
|
|
34
16
|
const outcomes = [];
|
|
35
|
-
let cleanupWarning = null;
|
|
36
17
|
try {
|
|
37
|
-
|
|
18
|
+
return await withWorkspaceReporting(parent, "superpowers-manager.install.", async (workspace) => {
|
|
38
19
|
const env = { ...ctx.env, TMPDIR: workspace };
|
|
39
20
|
const failed = (message) => ({
|
|
40
21
|
kind: "failed",
|
|
@@ -187,53 +168,25 @@ async function gatherInstallStages(ctx, selection, artifact) {
|
|
|
187
168
|
stdout: output.stdout,
|
|
188
169
|
stderr: output.stderr,
|
|
189
170
|
};
|
|
190
|
-
}, {
|
|
191
|
-
// Suppresses withWorkspace's throw on a POST-SUCCESS cleanup failure,
|
|
192
|
-
// so the StageOutcome the callback already computed still comes back
|
|
193
|
-
// as `outcome` below instead of being discarded. `report` runs
|
|
194
|
-
// synchronously, as the option requires (src/workspace.ts).
|
|
195
|
-
onCleanupFailure: (path) => {
|
|
196
|
-
cleanupWarning = workspaceRemovalFailure(path);
|
|
197
|
-
},
|
|
198
171
|
});
|
|
199
|
-
return { outcome, cleanupWarning };
|
|
200
172
|
}
|
|
201
173
|
catch (cause) {
|
|
202
|
-
// Reachable only for mkdtemp failure (nothing collected yet)
|
|
203
|
-
// callback
|
|
204
|
-
//
|
|
174
|
+
// Reachable only for mkdtemp failure (nothing collected yet): the
|
|
175
|
+
// callback never throws, and a post-success cleanup failure comes back
|
|
176
|
+
// as cleanupWarning.
|
|
205
177
|
throw new GatherFailure("install gather failed", cause, outcomes);
|
|
206
178
|
}
|
|
207
179
|
}
|
|
208
180
|
export async function runInstall(argv, ctx) {
|
|
209
181
|
if (ctx.adapter.presentation.installNotice !== "")
|
|
210
182
|
ctx.stdout.write(`${ctx.adapter.presentation.installNotice}\n`);
|
|
211
|
-
|
|
183
|
+
// argv is ignored: scripts/install never reads "$@".
|
|
184
|
+
return await runWithMutation("install", ctx, async (scoped) => performInstall(scoped));
|
|
212
185
|
}
|
|
213
|
-
async function performInstall(
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
void argv;
|
|
217
|
-
let probe;
|
|
218
|
-
try {
|
|
219
|
-
probe = await gatherProbe(ctx);
|
|
220
|
-
}
|
|
221
|
-
catch (cause) {
|
|
222
|
-
// Gathering does not write, so this catch cannot misclassify an output
|
|
223
|
-
// failure. oneLine() bounds any inherited diagnostic to one line.
|
|
224
|
-
ctx.stderr.write(`error: ${oneLine(cause)}\n`);
|
|
225
|
-
return 1;
|
|
226
|
-
}
|
|
227
|
-
// Replay first, on both paths, before any decision -- clause 1.
|
|
228
|
-
for (const outcome of probe.outcomes)
|
|
229
|
-
replayOutcome(outcome, ctx);
|
|
230
|
-
if (probe.status === 1) {
|
|
231
|
-
if (probe.message !== null) {
|
|
232
|
-
ctx.stderr.write(`error: ${probe.message}\n`);
|
|
233
|
-
}
|
|
186
|
+
async function performInstall(ctx) {
|
|
187
|
+
const facts = await probeFacts(ctx);
|
|
188
|
+
if (facts === null)
|
|
234
189
|
return 1;
|
|
235
|
-
}
|
|
236
|
-
const facts = probe.facts;
|
|
237
190
|
if (facts.ownership.installEligibility.kind === "blocked") {
|
|
238
191
|
writeOutput(facts.ownership.installEligibility.output, ctx);
|
|
239
192
|
return 1;
|
|
@@ -282,7 +235,7 @@ async function performInstall(argv, ctx) {
|
|
|
282
235
|
ctx.stderr.write(`error: ${oneLine(inner)}\n`);
|
|
283
236
|
return 1;
|
|
284
237
|
}
|
|
285
|
-
const { outcome, cleanupWarning } = stage;
|
|
238
|
+
const { value: outcome, cleanupWarning } = stage;
|
|
286
239
|
for (const each of outcome.outcomes)
|
|
287
240
|
replayOutcome(each, ctx);
|
|
288
241
|
let status;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { computeEffectiveSelection } from "../effective-selection.js";
|
|
2
|
-
import { upstreamCacheRoot } from "../upstream
|
|
2
|
+
import { upstreamCacheRoot } from "../upstream.js";
|
|
3
3
|
import { SafetyError } from "../safety-error.js";
|
|
4
4
|
import { isAbsolute } from "node:path";
|
|
5
5
|
import { oneLine } from "../cli-arguments.js";
|
|
@@ -42,13 +42,13 @@ export async function withMutation(command, ctx, action) {
|
|
|
42
42
|
}
|
|
43
43
|
const resources = [location.destinationRoot, ...mutationRoots];
|
|
44
44
|
if (command === "prepare") {
|
|
45
|
-
resources.push(upstreamCacheRoot(ctx.root, ctx.env
|
|
45
|
+
resources.push(upstreamCacheRoot(ctx.root, ctx.env));
|
|
46
46
|
}
|
|
47
47
|
// The coordinator sorts each acquisition and refuses conflicts immediately.
|
|
48
48
|
// A nested prepare may add a cache lock that sorts before an outer target;
|
|
49
49
|
// it cannot deadlock because no acquisition waits, while already-held roots
|
|
50
50
|
// are reentrant for this invocation.
|
|
51
|
-
return await ctx.coordination.withResources(resources, async () => action(scoped));
|
|
51
|
+
return await ctx.coordination.withResources(resources, async () => action(scoped, location));
|
|
52
52
|
}
|
|
53
53
|
export async function runWithMutation(command, ctx, action) {
|
|
54
54
|
const errors = configurationErrors(command, ctx.env);
|
|
@@ -59,9 +59,9 @@ export async function runWithMutation(command, ctx, action) {
|
|
|
59
59
|
}
|
|
60
60
|
let actionThrew = false;
|
|
61
61
|
try {
|
|
62
|
-
return await withMutation(command, ctx, async (scoped) => {
|
|
62
|
+
return await withMutation(command, ctx, async (scoped, location) => {
|
|
63
63
|
try {
|
|
64
|
-
return await action(scoped);
|
|
64
|
+
return await action(scoped, location);
|
|
65
65
|
}
|
|
66
66
|
catch (cause) {
|
|
67
67
|
actionThrew = true;
|
package/dist/commands/pin.js
CHANGED
|
@@ -2,7 +2,7 @@ import { tmpdir } from "node:os";
|
|
|
2
2
|
import { oneLine } from "../cli-arguments.js";
|
|
3
3
|
import { isTagRef, normalizeCommitInput } from "../domain/refs.js";
|
|
4
4
|
import { loadSavedSelection, selectionStatePath, UPSTREAM_URL_DEFAULT, } from "../effective-selection.js";
|
|
5
|
-
import {
|
|
5
|
+
import { validateSource } from "../selection.js";
|
|
6
6
|
import { writeSelectionState } from "../selection-store.js";
|
|
7
7
|
import { resolveExactTag, verifyRawCommit } from "../upstream.js";
|
|
8
8
|
// Runs every step that can throw, and performs no writes of its own: that is
|
|
@@ -36,12 +36,17 @@ async function attemptPin(requested, ctx) {
|
|
|
36
36
|
resolvedRef = normalizeCommitInput(requested);
|
|
37
37
|
commit = await verifyRawCommit(source, resolvedRef, tmpdir());
|
|
38
38
|
}
|
|
39
|
-
|
|
39
|
+
// resolvedRef is already normalized (lowercase for a raw commit; a v-tag is
|
|
40
|
+
// unchanged), and commit comes lowercase from verifyRawCommit or git
|
|
41
|
+
// ls-remote. validateRecord still rejects anything else.
|
|
42
|
+
await writeSelectionState(selectionStatePath(ctx.env), {
|
|
43
|
+
schema_version: 1,
|
|
44
|
+
mode: "pinned",
|
|
40
45
|
source,
|
|
41
|
-
|
|
42
|
-
resolvedRef,
|
|
46
|
+
requested_ref: resolvedRef,
|
|
47
|
+
resolved_ref: resolvedRef,
|
|
43
48
|
commit,
|
|
44
|
-
})
|
|
49
|
+
});
|
|
45
50
|
return { resolvedRef, commit };
|
|
46
51
|
}
|
|
47
52
|
export async function runPin(argv, ctx) {
|