superpowers-manager 0.1.6 → 0.3.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.
Files changed (59) hide show
  1. package/README.md +184 -64
  2. package/bin/superpowers-manager.js +9 -237
  3. package/dist/adapter-result.js +204 -0
  4. package/dist/adapter.js +649 -0
  5. package/dist/atomic.js +142 -0
  6. package/dist/cli-arguments.js +32 -0
  7. package/dist/cli.js +293 -0
  8. package/dist/codex-json.js +89 -0
  9. package/dist/codex-state.js +78 -0
  10. package/dist/commands/context.js +1 -0
  11. package/dist/commands/install.js +376 -0
  12. package/dist/commands/pin.js +79 -0
  13. package/dist/commands/prepare.js +527 -0
  14. package/dist/commands/probe.js +392 -0
  15. package/dist/commands/track-latest.js +44 -0
  16. package/dist/commands/uninstall.js +308 -0
  17. package/dist/commands/unpin.js +95 -0
  18. package/dist/commands/update.js +138 -0
  19. package/dist/domain/fingerprint.js +4 -0
  20. package/dist/domain/refs.js +37 -0
  21. package/dist/effective-selection.js +123 -0
  22. package/dist/generated-plugin.js +748 -0
  23. package/dist/git.js +38 -0
  24. package/dist/hooks.js +290 -0
  25. package/dist/lifecycle.js +220 -0
  26. package/dist/manifest-overlay.js +125 -0
  27. package/dist/provenance.js +111 -0
  28. package/dist/python-json-format.js +83 -0
  29. package/dist/python-json.js +29 -0
  30. package/dist/python-text.js +74 -0
  31. package/dist/safe-path.js +137 -0
  32. package/dist/safety-error.js +10 -0
  33. package/dist/selection-state-cli.js +85 -0
  34. package/dist/selection-store.js +172 -0
  35. package/dist/selection.js +200 -0
  36. package/dist/status.js +24 -0
  37. package/dist/strict-json.js +248 -0
  38. package/dist/upstream-cli.js +110 -0
  39. package/dist/upstream-version.js +40 -0
  40. package/dist/upstream.js +253 -0
  41. package/dist/validate-generated-plugin-cli.js +118 -0
  42. package/dist/validator.js +317 -0
  43. package/dist/workspace.js +139 -0
  44. package/package.json +25 -2
  45. package/scripts/adapters/codex/adapter +0 -598
  46. package/scripts/adapters/codex/lib.sh +0 -391
  47. package/scripts/adapters/codex/validate-generated-plugin.py +0 -314
  48. package/scripts/core/adapter.sh +0 -92
  49. package/scripts/core/common.sh +0 -35
  50. package/scripts/core/lifecycle.sh +0 -127
  51. package/scripts/core/provenance.sh +0 -135
  52. package/scripts/core/status.sh +0 -27
  53. package/scripts/core/upstream.sh +0 -146
  54. package/scripts/core/validate-adapter-response.py +0 -245
  55. package/scripts/install +0 -60
  56. package/scripts/prepare +0 -126
  57. package/scripts/probe +0 -61
  58. package/scripts/uninstall +0 -44
  59. package/scripts/update +0 -31
@@ -1,245 +0,0 @@
1
- #!/usr/bin/env python3
2
- from __future__ import annotations
3
-
4
- import argparse
5
- import json
6
- import re
7
- import sys
8
- from pathlib import Path
9
-
10
- ENVELOPE_KEYS = {"protocol", "operation", "ok", "messages", "result", "error"}
11
- MESSAGE_KEYS = {"channel", "text"}
12
- ERROR_KEYS = {"code", "message", "hints"}
13
- OWNERSHIP_KEYS = {"plugin", "marketplace"}
14
- VERIFICATION_HINT_KEYS = {"mismatch", "missing"}
15
- OPERATIONS = {"build", "inspect", "install", "uninstall"}
16
- FINGERPRINT_RE = re.compile(r"(?:[0-9a-fA-F]{7}|[0-9a-fA-F]{40})\Z")
17
- MAX_NESTING = 64
18
-
19
-
20
- class ProtocolError(ValueError):
21
- pass
22
-
23
-
24
- def reject_constant(value: str) -> None:
25
- raise ProtocolError(f"non-standard JSON constant: {value}")
26
-
27
-
28
- def nesting_exceeds_limit(value: object) -> bool:
29
- stack = [(value, 0)]
30
- while stack:
31
- current, depth = stack.pop()
32
- if isinstance(current, dict):
33
- next_depth = depth + 1
34
- if next_depth > MAX_NESTING:
35
- return True
36
- stack.extend((child, next_depth) for child in current.values())
37
- elif isinstance(current, list):
38
- next_depth = depth + 1
39
- if next_depth > MAX_NESTING:
40
- return True
41
- stack.extend((child, next_depth) for child in current)
42
- return False
43
-
44
-
45
- def require_object(value: object, label: str) -> dict[str, object]:
46
- if not isinstance(value, dict):
47
- raise ProtocolError(f"{label} must be an object")
48
- return value
49
-
50
-
51
- def require_exact_keys(value: dict[str, object], expected: set[str], label: str) -> None:
52
- actual = set(value)
53
- if actual != expected:
54
- raise ProtocolError(
55
- f"{label} keys must be {sorted(expected)}, got {sorted(actual)}"
56
- )
57
-
58
-
59
- def contains_terminal_control(value: str) -> bool:
60
- return any(
61
- ord(character) < 0x20
62
- or 0x7F <= ord(character) <= 0x9F
63
- or 0xD800 <= ord(character) <= 0xDFFF
64
- for character in value
65
- )
66
-
67
-
68
- def require_non_empty_string(value: object, label: str) -> str:
69
- if not isinstance(value, str) or not value or "\n" in value or "\r" in value:
70
- raise ProtocolError(f"{label} must be a non-empty single-line string")
71
- if contains_terminal_control(value):
72
- raise ProtocolError(f"{label} must not contain terminal control characters")
73
- return value
74
-
75
-
76
- def validate_messages(value: object) -> list[dict[str, str]]:
77
- if not isinstance(value, list):
78
- raise ProtocolError("messages must be an array")
79
- messages: list[dict[str, str]] = []
80
- for index, item in enumerate(value):
81
- message = require_object(item, f"messages[{index}]")
82
- require_exact_keys(message, MESSAGE_KEYS, f"messages[{index}]")
83
- channel = message.get("channel")
84
- if channel not in {"stdout", "stderr"}:
85
- raise ProtocolError(f"messages[{index}].channel is invalid")
86
- text = require_non_empty_string(message.get("text"), f"messages[{index}].text")
87
- messages.append({"channel": str(channel), "text": text})
88
- return messages
89
-
90
-
91
- def validate_error(value: object) -> dict[str, object]:
92
- error = require_object(value, "error")
93
- require_exact_keys(error, ERROR_KEYS, "error")
94
- code = require_non_empty_string(error.get("code"), "error.code")
95
- message = require_non_empty_string(error.get("message"), "error.message")
96
- hints_value = error.get("hints")
97
- if not isinstance(hints_value, list):
98
- raise ProtocolError("error.hints must be an array")
99
- hints = [
100
- require_non_empty_string(hint, f"error.hints[{index}]")
101
- for index, hint in enumerate(hints_value)
102
- ]
103
- return {"code": code, "message": message, "hints": hints}
104
-
105
-
106
- def validate_result(
107
- operation: str, value: object, inspect_view: str | None
108
- ) -> dict[str, object]:
109
- result = require_object(value, "result")
110
- if operation in {"build", "uninstall"}:
111
- require_exact_keys(result, set(), f"{operation} result")
112
- return {}
113
- if operation == "inspect":
114
- if inspect_view == "fingerprint":
115
- require_exact_keys(result, {"view", "fingerprint"}, "inspect result")
116
- if result.get("view") != "fingerprint":
117
- raise ProtocolError("inspect result view must be fingerprint")
118
- fingerprint = result.get("fingerprint")
119
- if fingerprint is not None and (
120
- not isinstance(fingerprint, str)
121
- or FINGERPRINT_RE.fullmatch(fingerprint) is None
122
- ):
123
- raise ProtocolError("fingerprint must be null, 7 hex, or 40 hex")
124
- return {"view": "fingerprint", "fingerprint": fingerprint}
125
- if inspect_view == "ownership":
126
- require_exact_keys(
127
- result,
128
- {"view", "resources", "legacy_resources", "identity_state"},
129
- "inspect result",
130
- )
131
- if result.get("view") != "ownership":
132
- raise ProtocolError("inspect result view must be ownership")
133
- resources = require_object(result.get("resources"), "resources")
134
- legacy_resources = require_object(
135
- result.get("legacy_resources"), "legacy_resources"
136
- )
137
- for label, group in (
138
- ("resources", resources),
139
- ("legacy_resources", legacy_resources),
140
- ):
141
- require_exact_keys(group, OWNERSHIP_KEYS, label)
142
- if not all(isinstance(group[key], bool) for key in OWNERSHIP_KEYS):
143
- raise ProtocolError(f"{label} values must be Boolean")
144
- manager_present = any(resources.values())
145
- legacy_present = any(legacy_resources.values())
146
- expected_state = {
147
- (False, False): "neither",
148
- (True, False): "manager",
149
- (False, True): "legacy",
150
- (True, True): "both",
151
- }[(manager_present, legacy_present)]
152
- identity_state = result.get("identity_state")
153
- if identity_state != expected_state:
154
- raise ProtocolError(
155
- f"identity_state must be {expected_state} for the reported resources"
156
- )
157
- return {
158
- "view": "ownership",
159
- "resources": resources,
160
- "legacy_resources": legacy_resources,
161
- "identity_state": identity_state,
162
- }
163
- raise ProtocolError("inspect view must be fingerprint or ownership")
164
- if operation == "install":
165
- require_exact_keys(result, {"verification_hints"}, "install result")
166
- hints = require_object(result.get("verification_hints"), "verification_hints")
167
- unknown = set(hints) - VERIFICATION_HINT_KEYS
168
- if unknown:
169
- raise ProtocolError(f"unknown verification hint keys: {sorted(unknown)}")
170
- normalized = {
171
- key: require_non_empty_string(value, f"verification_hints.{key}")
172
- for key, value in hints.items()
173
- }
174
- return {"verification_hints": normalized}
175
- raise ProtocolError(f"unsupported operation: {operation}")
176
-
177
-
178
- def validate_envelope(
179
- raw: object, operation: str, adapter_exit: int, inspect_view: str | None
180
- ) -> tuple[list[dict[str, str]], dict[str, object] | None, dict[str, object] | None]:
181
- envelope = require_object(raw, "response")
182
- require_exact_keys(envelope, ENVELOPE_KEYS, "response")
183
- protocol = envelope.get("protocol")
184
- if type(protocol) is not int or protocol != 1:
185
- raise ProtocolError("protocol must equal integer 1")
186
- if envelope.get("operation") != operation:
187
- raise ProtocolError("response operation does not match invocation")
188
- ok = envelope.get("ok")
189
- if not isinstance(ok, bool):
190
- raise ProtocolError("ok must be Boolean")
191
- messages = validate_messages(envelope.get("messages"))
192
- if ok:
193
- if adapter_exit != 0:
194
- raise ProtocolError("successful response requires adapter exit 0")
195
- if envelope.get("error") is not None:
196
- raise ProtocolError("successful response error must be null")
197
- result = validate_result(operation, envelope.get("result"), inspect_view)
198
- return messages, result, None
199
- if adapter_exit == 0:
200
- raise ProtocolError("failure response requires nonzero adapter exit")
201
- if envelope.get("result") is not None:
202
- raise ProtocolError("failure response result must be null")
203
- error = validate_error(envelope.get("error"))
204
- return messages, None, error
205
-
206
-
207
- def replay(messages: list[dict[str, str]]) -> None:
208
- for message in messages:
209
- stream = sys.stdout if message["channel"] == "stdout" else sys.stderr
210
- print(message["text"], file=stream)
211
-
212
-
213
- def main() -> int:
214
- parser = argparse.ArgumentParser()
215
- parser.add_argument("--operation", choices=sorted(OPERATIONS), required=True)
216
- parser.add_argument("--adapter-exit", type=int, required=True)
217
- parser.add_argument("--response", type=Path, required=True)
218
- parser.add_argument("--result", type=Path, required=True)
219
- parser.add_argument("--inspect-view", choices=("fingerprint", "ownership"))
220
- args = parser.parse_args()
221
- try:
222
- with args.response.open(encoding="utf-8") as handle:
223
- raw = json.load(handle, parse_constant=reject_constant)
224
- if nesting_exceeds_limit(raw):
225
- raise ProtocolError("response JSON nesting exceeds limit")
226
- messages, result, error = validate_envelope(
227
- raw, args.operation, args.adapter_exit, args.inspect_view
228
- )
229
- replay(messages)
230
- if error is not None:
231
- print(f"error: {error['message']}", file=sys.stderr)
232
- for hint in error["hints"]:
233
- print(f"hint: {hint}", file=sys.stderr)
234
- return 1
235
- with args.result.open("w", encoding="utf-8") as handle:
236
- json.dump(result, handle, allow_nan=False, separators=(",", ":"))
237
- handle.write("\n")
238
- return 0
239
- except (OSError, UnicodeError, json.JSONDecodeError, ProtocolError, RecursionError) as exc:
240
- print(f"error: invalid adapter response: {exc}", file=sys.stderr)
241
- return 2
242
-
243
-
244
- if __name__ == "__main__":
245
- raise SystemExit(main())
package/scripts/install DELETED
@@ -1,60 +0,0 @@
1
- #!/bin/sh
2
- set -eu
3
-
4
- root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
5
- . "$root/scripts/core/common.sh"
6
- . "$root/scripts/core/provenance.sh"
7
- . "$root/scripts/core/status.sh"
8
- . "$root/scripts/core/lifecycle.sh"
9
- . "$root/scripts/core/adapter.sh"
10
-
11
- spw_require_command python3
12
-
13
- echo "Note: remove or disable conflicting Superpowers providers yourself before relying on manager skills."
14
-
15
- # Prepare-before-mutate: probe, and (re)generate the plugin tree if it is
16
- # missing or stale — including validation — entirely before any Codex
17
- # marketplace or plugin mutation. A failed prepare leaves Codex untouched.
18
- probe_output=$(sh "$root/scripts/probe" --porcelain)
19
- identity_state=$(printf '%s\n' "$probe_output" | awk -F= '$1 == "identity_state" { print $2 }')
20
- [ -n "$identity_state" ] || spw_die "probe did not report adapter identity state"
21
- spw_require_no_legacy_state "$identity_state"
22
- status=$(printf '%s\n' "$probe_output" | awk -F= '$1 == "status" { print $2 }')
23
- case "$status" in
24
- needs\ prepare)
25
- sh "$root/scripts/prepare"
26
- ;;
27
- needs\ install|current)
28
- ;;
29
- *)
30
- printf '%s\n' "$probe_output"
31
- spw_die "unknown probe status: $status"
32
- ;;
33
- esac
34
-
35
- desired_commit=$(spw_metadata_commit_or_empty "$root/plugins/superpowers/.superpowers-upstream.json" || true)
36
- [ -n "$desired_commit" ] || spw_die "generated metadata missing desired commit after prepare"
37
-
38
- tmp_parent="${TMPDIR:-/tmp}"
39
- if ! install_workspace=$(mktemp -d "$tmp_parent/superpowers-manager.install.XXXXXX"); then
40
- spw_die "cannot create install workspace under $tmp_parent"
41
- fi
42
- cleanup_install_workspace() {
43
- status=$?
44
- trap - 0 HUP INT TERM
45
- rm -rf "$install_workspace" || :
46
- exit "$status"
47
- }
48
- trap cleanup_install_workspace 0
49
- trap 'exit 129' HUP
50
- trap 'exit 130' INT
51
- trap 'exit 143' TERM
52
-
53
- TMPDIR="$install_workspace"
54
- export TMPDIR
55
- install_result="$install_workspace/install.json"
56
- inspect_result="$install_workspace/inspect.json"
57
- spw_adapter_install "$install_result" "$root"
58
- if ! spw_verify_installed_fingerprint "$desired_commit" "$install_result" "$inspect_result"; then
59
- exit 1
60
- fi
package/scripts/prepare DELETED
@@ -1,126 +0,0 @@
1
- #!/bin/sh
2
- set -eu
3
-
4
- root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
5
- . "$root/scripts/core/common.sh"
6
- . "$root/scripts/core/upstream.sh"
7
- . "$root/scripts/core/provenance.sh"
8
- . "$root/scripts/core/lifecycle.sh"
9
- . "$root/scripts/core/adapter.sh"
10
-
11
- upstream_url="${SUPERPOWERS_UPSTREAM_URL:-$SPW_UPSTREAM_URL_DEFAULT}"
12
- cache_parent="${SUPERPOWERS_CACHE_DIR:-$root/.cache/upstream}"
13
- plugin_root="${SUPERPOWERS_PLUGIN_ROOT:-$root/plugins/superpowers}"
14
- manifest_template="${SUPERPOWERS_MANIFEST_TEMPLATE:-$root/plugins/superpowers/.codex-plugin/plugin.template.json}"
15
- additional_validator="${SUPERPOWERS_VALIDATOR:-}"
16
- invocation_root=$(pwd -P)
17
- case "$cache_parent" in
18
- /*) ;;
19
- *) cache_parent="$invocation_root/$cache_parent" ;;
20
- esac
21
- case "$plugin_root" in
22
- /*) ;;
23
- *) plugin_root="$invocation_root/$plugin_root" ;;
24
- esac
25
- cache="$cache_parent/superpowers"
26
- tmp_parent=$(dirname "$plugin_root")
27
- mkdir -p "$tmp_parent"
28
- if ! prepare_workspace=$(mktemp -d "$tmp_parent/.superpowers.prepare.XXXXXX"); then
29
- spw_die "cannot create prepare workspace under $tmp_parent"
30
- fi
31
- tmp_plugin="$prepare_workspace/superpowers"
32
- adapter_result="$prepare_workspace/adapter-build.json"
33
-
34
- cleanup_tmp_plugin() {
35
- status=$?
36
- trap - 0 HUP INT TERM
37
- rm -rf "$prepare_workspace" || :
38
- exit "$status"
39
- }
40
-
41
- trap cleanup_tmp_plugin 0
42
- trap 'exit 129' HUP
43
- trap 'exit 130' INT
44
- trap 'exit 143' TERM
45
-
46
- TMPDIR="$prepare_workspace"
47
- export TMPDIR
48
-
49
- requested_ref=$(spw_config_ref "$root")
50
-
51
- spw_require_command git
52
- spw_require_command python3
53
- spw_require_command "$SPW_ADAPTER"
54
- [ -f "$manifest_template" ] || spw_die "missing fallback manifest template: $manifest_template"
55
-
56
- resolved_record=$(spw_resolve_ref "$upstream_url" "$requested_ref")
57
- resolution_kind=$(printf '%s\n' "$resolved_record" | awk '{ print $1 }')
58
- resolved_ref=$(printf '%s\n' "$resolved_record" | awk '{ print $2 }')
59
- commit=$(printf '%s\n' "$resolved_record" | awk '{ print $3 }')
60
-
61
- mkdir -p "$cache_parent" "$tmp_parent"
62
- if [ -d "$cache/.git" ]; then
63
- if ! git -C "$cache" fetch --tags --prune "$upstream_url"; then
64
- spw_die "cannot fetch upstream repo: $upstream_url"
65
- fi
66
- else
67
- if ! git clone "$upstream_url" "$cache"; then
68
- spw_die "cannot clone upstream repo: $upstream_url"
69
- fi
70
- fi
71
- if ! git -C "$cache" checkout --detach "$commit" >/dev/null; then
72
- spw_die "cannot check out upstream commit: $commit"
73
- fi
74
-
75
- spw_require_upstream_path "$cache/skills" "skills/"
76
- spw_require_upstream_path "$cache/LICENSE" "LICENSE"
77
- spw_require_upstream_path "$cache/README.md" "README.md"
78
- spw_require_upstream_path "$cache/CODE_OF_CONDUCT.md" "CODE_OF_CONDUCT.md"
79
-
80
- rm -rf "$tmp_plugin"
81
- mkdir -p "$tmp_plugin/.codex-plugin"
82
- upstream_manifest="$cache/.codex-plugin/plugin.json"
83
-
84
- spw_copy_path_if_present "$cache/skills" "$tmp_plugin/skills"
85
- spw_copy_path_if_present "$cache/assets" "$tmp_plugin/assets"
86
- spw_copy_path_if_present "$cache/LICENSE" "$tmp_plugin/LICENSE"
87
- spw_copy_path_if_present "$cache/README.md" "$tmp_plugin/README.md"
88
- spw_copy_path_if_present "$cache/CODE_OF_CONDUCT.md" "$tmp_plugin/CODE_OF_CONDUCT.md"
89
-
90
- upstream_manifest_version=""
91
- if [ -f "$upstream_manifest" ]; then
92
- upstream_manifest_version=$(spw_json_get "$upstream_manifest" "version")
93
- fi
94
-
95
- spw_write_metadata_json \
96
- "$tmp_plugin/.superpowers-upstream.json" \
97
- "$upstream_url" \
98
- "$requested_ref" \
99
- "$resolved_ref" \
100
- "$commit" \
101
- "$upstream_manifest_version"
102
-
103
- manager_version=$(
104
- spw_manifest_version_for_ref "$requested_ref" "$resolution_kind" "$resolved_ref" "$commit"
105
- )
106
- spw_invoke_adapter build "$adapter_result" "" -- \
107
- --upstream-root "$cache" \
108
- --candidate-root "$tmp_plugin" \
109
- --requested-ref "$requested_ref" \
110
- --resolved-ref "$resolved_ref" \
111
- --commit "$commit" \
112
- --manager-version "$manager_version" \
113
- --upstream-manifest-version "$upstream_manifest_version" \
114
- --fallback-manifest "$manifest_template"
115
-
116
- if [ -n "$additional_validator" ]; then
117
- [ -f "$additional_validator" ] || \
118
- spw_die "additional plugin validator not found: $additional_validator"
119
- if ! python3 "$additional_validator" "$tmp_plugin"; then
120
- spw_die "additional plugin validation failed"
121
- fi
122
- fi
123
-
124
- spw_replace_generated_tree "$tmp_plugin" "$plugin_root"
125
-
126
- echo "prepared $resolved_ref at $commit"
package/scripts/probe DELETED
@@ -1,61 +0,0 @@
1
- #!/bin/sh
2
- set -eu
3
-
4
- root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
5
- . "$root/scripts/core/common.sh"
6
- . "$root/scripts/core/upstream.sh"
7
- . "$root/scripts/core/provenance.sh"
8
- . "$root/scripts/core/status.sh"
9
- . "$root/scripts/core/lifecycle.sh"
10
- . "$root/scripts/core/adapter.sh"
11
-
12
- upstream_url="${SUPERPOWERS_UPSTREAM_URL:-$SPW_UPSTREAM_URL_DEFAULT}"
13
- requested_ref=$(spw_config_ref "$root")
14
- resolved_record=$(spw_resolve_ref "$upstream_url" "$requested_ref")
15
- resolved_ref=$(printf '%s\n' "$resolved_record" | awk '{ print $2 }')
16
- desired_commit=$(printf '%s\n' "$resolved_record" | awk '{ print $3 }')
17
-
18
- generated_commit=$(spw_generated_commit_or_empty "$root")
19
- tmp_parent="${TMPDIR:-/tmp}"
20
- if ! probe_workspace=$(mktemp -d "$tmp_parent/superpowers-manager.probe.XXXXXX"); then
21
- spw_die "cannot create probe workspace under $tmp_parent"
22
- fi
23
- fingerprint_result="$probe_workspace/fingerprint.json"
24
- ownership_result="$probe_workspace/ownership.json"
25
- cleanup() {
26
- status=$?
27
- trap - 0 HUP INT TERM
28
- rm -rf "$probe_workspace" || :
29
- exit "$status"
30
- }
31
- trap cleanup 0
32
- trap 'exit 129' HUP
33
- trap 'exit 130' INT
34
- trap 'exit 143' TERM
35
-
36
- TMPDIR="$probe_workspace"
37
- export TMPDIR
38
- spw_inspect_fingerprint "$fingerprint_result"
39
- installed_commit=$(spw_adapter_result_get "$fingerprint_result" "fingerprint")
40
- spw_inspect_ownership "$ownership_result"
41
- identity_state=$(spw_adapter_result_get "$ownership_result" "identity_state")
42
-
43
- status=$(spw_status_for_commits "$desired_commit" "$generated_commit" "$installed_commit")
44
-
45
- if [ "${1:-}" = "--porcelain" ]; then
46
- printf 'requested_ref=%s\n' "$requested_ref"
47
- printf 'resolved_ref=%s\n' "$resolved_ref"
48
- printf 'desired_commit=%s\n' "$desired_commit"
49
- printf 'generated_commit=%s\n' "$generated_commit"
50
- printf 'installed_commit=%s\n' "$installed_commit"
51
- printf 'identity_state=%s\n' "$identity_state"
52
- printf 'status=%s\n' "$status"
53
- else
54
- printf 'requested ref: %s\n' "$requested_ref"
55
- printf 'resolved ref: %s\n' "$resolved_ref"
56
- printf 'desired commit: %s\n' "$desired_commit"
57
- printf 'generated plugin commit: %s\n' "${generated_commit:-not present}"
58
- printf 'installed manager commit or fingerprint: %s\n' "${installed_commit:-not detected}"
59
- printf 'Codex identity state: %s\n' "$identity_state"
60
- printf 'status: %s\n' "$status"
61
- fi
package/scripts/uninstall DELETED
@@ -1,44 +0,0 @@
1
- #!/bin/sh
2
- set -eu
3
-
4
- root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
5
- . "$root/scripts/core/common.sh"
6
- . "$root/scripts/core/provenance.sh"
7
- . "$root/scripts/core/lifecycle.sh"
8
- . "$root/scripts/core/adapter.sh"
9
-
10
- spw_require_command python3
11
-
12
- tmp_parent="${TMPDIR:-/tmp}"
13
- if ! uninstall_workspace=$(mktemp -d "$tmp_parent/superpowers-manager.uninstall.XXXXXX"); then
14
- spw_die "cannot create uninstall workspace under $tmp_parent"
15
- fi
16
- inspect_result="$uninstall_workspace/inspect.json"
17
- uninstall_result="$uninstall_workspace/uninstall.json"
18
- cleanup() {
19
- status=$?
20
- trap - 0 HUP INT TERM
21
- rm -rf "$uninstall_workspace" || :
22
- exit "$status"
23
- }
24
- trap cleanup 0
25
- trap 'exit 129' HUP
26
- trap 'exit 130' INT
27
- trap 'exit 143' TERM
28
-
29
- TMPDIR="$uninstall_workspace"
30
- export TMPDIR
31
-
32
- spw_inspect_ownership "$inspect_result"
33
- plugin_present=$(spw_adapter_result_boolean "$inspect_result" "resources.plugin")
34
- marketplace_present=$(spw_adapter_result_boolean "$inspect_result" "resources.marketplace")
35
-
36
- spw_adapter_uninstall "$uninstall_result" "$plugin_present" "$marketplace_present"
37
-
38
- spw_inspect_ownership "$inspect_result"
39
- spw_verify_uninstalled_resources "$inspect_result"
40
- identity_state=$(spw_adapter_result_get "$inspect_result" "identity_state")
41
- spw_report_legacy_state "$identity_state"
42
-
43
- echo "uninstall complete"
44
- echo "note: local generated artifacts under plugins/superpowers/ and .cache/upstream/ were left in place; remove them manually or regenerate with scripts/prepare."
package/scripts/update DELETED
@@ -1,31 +0,0 @@
1
- #!/bin/sh
2
- set -eu
3
-
4
- root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
5
- . "$root/scripts/core/common.sh"
6
- . "$root/scripts/core/lifecycle.sh"
7
-
8
- probe_output=$(sh "$root/scripts/probe" --porcelain)
9
- identity_state=$(printf '%s\n' "$probe_output" | awk -F= '$1 == "identity_state" { print $2 }')
10
- [ -n "$identity_state" ] || spw_die "probe did not report adapter identity state"
11
- spw_require_no_legacy_state "$identity_state"
12
- status=$(printf '%s\n' "$probe_output" | awk -F= '$1 == "status" { print $2 }')
13
-
14
- case "$status" in
15
- current)
16
- printf '%s\n' "$probe_output"
17
- echo "manager is current"
18
- ;;
19
- needs\ prepare)
20
- sh "$root/scripts/prepare"
21
- sh "$root/scripts/install"
22
- ;;
23
- needs\ install)
24
- sh "$root/scripts/install"
25
- ;;
26
- *)
27
- echo "$probe_output"
28
- echo "error: unknown probe status: $status" >&2
29
- exit 1
30
- ;;
31
- esac