superpowers-manager 0.1.6 → 0.2.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.
@@ -22,6 +22,139 @@ spw_is_semver_base() {
22
22
  printf '%s' "$version" | grep -Eq '^(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-]*))*))?$'
23
23
  }
24
24
 
25
+ spw_is_pinnable_tag() {
26
+ ref="$1"
27
+ case "$ref" in
28
+ *'
29
+ '*|*"$(printf '\r')"*) return 1 ;;
30
+ v*) spw_is_semver_base "${ref#v}" ;;
31
+ *) return 1 ;;
32
+ esac
33
+ }
34
+
35
+ spw_is_full_commit() {
36
+ ref="$1"
37
+ [ "${#ref}" -eq 40 ] || return 1
38
+ case "$ref" in
39
+ *[!0-9A-Fa-f]*) return 1 ;;
40
+ *) return 0 ;;
41
+ esac
42
+ }
43
+
44
+ spw_git_safe_source() {
45
+ source="$1"
46
+ case "$source" in
47
+ /*|*://*|*:*|~*) printf '%s\n' "$source" ;;
48
+ *) printf '%s/%s\n' "$(pwd -P)" "$source" ;;
49
+ esac
50
+ }
51
+
52
+ spw_resolve_exact_tag() {
53
+ source="$1"
54
+ ref="$2"
55
+ source_display=$(spw_display_source "$source")
56
+ query_source=$(spw_git_safe_source "$source")
57
+ if ! output=$(git ls-remote --tags -- "$query_source" "refs/tags/$ref" "refs/tags/$ref^{}" 2>&1); then
58
+ spw_die "cannot query exact upstream tag $ref from $source_display: $output"
59
+ fi
60
+ commit=$(printf '%s\n' "$output" | awk -v direct="refs/tags/$ref" '
61
+ $2 == direct { direct_sha = $1 }
62
+ $2 == direct "^{}" { peeled_sha = $1 }
63
+ END { if (peeled_sha != "") print peeled_sha; else if (direct_sha != "") print direct_sha }
64
+ ')
65
+ [ -n "$commit" ] || spw_die "upstream tag not found: $ref"
66
+ printf '%s\n' "$commit"
67
+ }
68
+
69
+ spw_verify_raw_commit() (
70
+ source="$1"
71
+ commit=$(printf '%s' "$2" | tr '[:upper:]' '[:lower:]')
72
+ workspace="$3"
73
+ source_display=$(spw_display_source "$source")
74
+ fetch_source=$(spw_git_safe_source "$source")
75
+
76
+ if ! verify_workspace=$(spw_make_workspace "$workspace" "superpowers-manager.commit"); then
77
+ spw_die "cannot create raw-commit verification workspace under $workspace"
78
+ fi
79
+ spw_install_workspace_trap "$verify_workspace"
80
+
81
+ if ! init_output=$(git init "$verify_workspace" 2>&1); then
82
+ spw_die "cannot initialize raw-commit verification workspace: $init_output"
83
+ fi
84
+ if fetch_output=$(git -C "$verify_workspace" fetch --no-tags -- "$fetch_source" "$commit" 2>&1); then
85
+ :
86
+ else
87
+ case "$fetch_output" in
88
+ *'not our ref'*|*'unadvertised object'*|*"couldn't find remote ref"*)
89
+ spw_die "source cannot supply requested commit: $commit"
90
+ ;;
91
+ *)
92
+ spw_die "cannot fetch requested commit from $source_display"
93
+ ;;
94
+ esac
95
+ fi
96
+ if ! object_type=$(git -C "$verify_workspace" cat-file -t "$commit" 2>/dev/null); then
97
+ spw_die "requested object is not a commit: $commit"
98
+ fi
99
+ if [ "$object_type" != commit ]; then
100
+ spw_die "requested object is not a commit: $commit"
101
+ fi
102
+ printf '%s\n' "$commit"
103
+ )
104
+
105
+ spw_fetch_exact_commit() (
106
+ source="$1"
107
+ commit="$2"
108
+ repository="$3"
109
+ workspace="$4"
110
+ source_display=$(spw_display_source "$source")
111
+ fetch_source=$(spw_git_safe_source "$source")
112
+
113
+ if ! fetch_workspace=$(spw_make_workspace "$workspace" "superpowers-manager.fetch"); then
114
+ spw_die "cannot create exact-commit fetch workspace under $workspace"
115
+ fi
116
+ spw_install_workspace_trap "$fetch_workspace"
117
+
118
+ if ! init_output=$(git init "$fetch_workspace" 2>&1); then
119
+ spw_die "cannot initialize exact-commit fetch workspace: $init_output"
120
+ fi
121
+ if fetch_output=$(git -C "$fetch_workspace" fetch --no-tags -- "$fetch_source" "$commit" 2>&1); then
122
+ :
123
+ else
124
+ case "$fetch_output" in
125
+ *'not our ref'*|*'unadvertised object'*|*"couldn't find remote ref"*)
126
+ spw_die "source cannot supply requested commit: $commit"
127
+ ;;
128
+ *)
129
+ spw_die "cannot fetch requested commit from $source_display"
130
+ ;;
131
+ esac
132
+ fi
133
+ if ! object_type=$(git -C "$fetch_workspace" cat-file -t "$commit" 2>/dev/null); then
134
+ spw_die "requested object is not a commit: $commit"
135
+ fi
136
+ if [ "$object_type" != commit ]; then
137
+ spw_die "requested object is not a commit: $commit"
138
+ fi
139
+
140
+ if [ ! -d "$repository/.git" ]; then
141
+ if ! init_output=$(git init "$repository" 2>&1); then
142
+ spw_die "cannot initialize upstream cache repository: $init_output"
143
+ fi
144
+ fi
145
+ if ! transfer_output=$(
146
+ git -C "$repository" fetch --no-tags -- "$fetch_workspace" "$commit" 2>&1
147
+ ); then
148
+ spw_die "cannot transfer requested commit into upstream cache: $transfer_output"
149
+ fi
150
+ if ! object_type=$(git -C "$repository" cat-file -t "$commit" 2>/dev/null); then
151
+ spw_die "cannot verify requested commit in upstream cache: $commit"
152
+ fi
153
+ if [ "$object_type" != commit ]; then
154
+ spw_die "cannot verify requested commit in upstream cache: $commit"
155
+ fi
156
+ )
157
+
25
158
  spw_sanitize_ref_for_version() {
26
159
  ref="$1"
27
160
  sanitized=$(printf '%s' "$ref" | sed 's/[^0-9A-Za-z-][^0-9A-Za-z-]*/-/g; s/^-*//; s/-*$//')
@@ -102,9 +235,10 @@ spw_resolve_ref() {
102
235
  upstream_url="$1"
103
236
  requested_ref="$2"
104
237
  spw_require_command git
238
+ query_source=$(spw_git_safe_source "$upstream_url")
105
239
 
106
240
  if [ "$requested_ref" = "latest-release" ]; then
107
- if ! output=$(git ls-remote --tags "$upstream_url" 'refs/tags/v*' 2>&1); then
241
+ if ! output=$(git ls-remote --tags -- "$query_source" 'refs/tags/v*' 2>&1); then
108
242
  spw_die "cannot query upstream tags from $upstream_url: $output"
109
243
  fi
110
244
  if ! selected=$(printf '%s\n' "$output" | spw_select_latest_release_from_ls_remote); then
@@ -119,7 +253,7 @@ spw_resolve_ref() {
119
253
  return
120
254
  fi
121
255
 
122
- if ! tag_output=$(git ls-remote --tags "$upstream_url" "refs/tags/$requested_ref" "refs/tags/$requested_ref^{}" 2>&1); then
256
+ if ! tag_output=$(git ls-remote --tags -- "$query_source" "refs/tags/$requested_ref" "refs/tags/$requested_ref^{}" 2>&1); then
123
257
  spw_die "cannot query upstream tag $requested_ref from $upstream_url: $tag_output"
124
258
  fi
125
259
  resolved=$(printf '%s\n' "$tag_output" | awk -v tag_ref="refs/tags/$requested_ref" '
@@ -133,7 +267,7 @@ spw_resolve_ref() {
133
267
  return
134
268
  fi
135
269
 
136
- if ! ref_output=$(git ls-remote "$upstream_url" "$requested_ref" 2>&1); then
270
+ if ! ref_output=$(git ls-remote -- "$query_source" "$requested_ref" 2>&1); then
137
271
  spw_die "cannot query upstream ref $requested_ref from $upstream_url: $ref_output"
138
272
  fi
139
273
  resolved=$(printf '%s\n' "$ref_output" | awk 'NF >= 2 { print $1; exit }')
@@ -15,6 +15,7 @@ VERIFICATION_HINT_KEYS = {"mismatch", "missing"}
15
15
  OPERATIONS = {"build", "inspect", "install", "uninstall"}
16
16
  FINGERPRINT_RE = re.compile(r"(?:[0-9a-fA-F]{7}|[0-9a-fA-F]{40})\Z")
17
17
  MAX_NESTING = 64
18
+ MAX_RESPONSE_BYTES = 1_048_576
18
19
 
19
20
 
20
21
  class ProtocolError(ValueError):
@@ -25,6 +26,17 @@ def reject_constant(value: str) -> None:
25
26
  raise ProtocolError(f"non-standard JSON constant: {value}")
26
27
 
27
28
 
29
+ def reject_duplicate_keys(
30
+ pairs: list[tuple[str, object]],
31
+ ) -> dict[str, object]:
32
+ obj: dict[str, object] = {}
33
+ for key, value in pairs:
34
+ if key in obj:
35
+ raise ProtocolError("duplicate object key")
36
+ obj[key] = value
37
+ return obj
38
+
39
+
28
40
  def nesting_exceeds_limit(value: object) -> bool:
29
41
  stack = [(value, 0)]
30
42
  while stack:
@@ -160,7 +172,23 @@ def validate_result(
160
172
  "legacy_resources": legacy_resources,
161
173
  "identity_state": identity_state,
162
174
  }
163
- raise ProtocolError("inspect view must be fingerprint or ownership")
175
+ if inspect_view == "update-control":
176
+ require_exact_keys(
177
+ result,
178
+ {"view", "update_control"},
179
+ "update-control inspect result",
180
+ )
181
+ if result.get("view") != "update-control":
182
+ raise ProtocolError("inspect result view must be update-control")
183
+ value = result.get("update_control")
184
+ if value not in {"managed", "unsupported"}:
185
+ raise ProtocolError(
186
+ "update-control value must be managed or unsupported"
187
+ )
188
+ return {"view": "update-control", "update_control": value}
189
+ raise ProtocolError(
190
+ "inspect view must be fingerprint or ownership, or update-control"
191
+ )
164
192
  if operation == "install":
165
193
  require_exact_keys(result, {"verification_hints"}, "install result")
166
194
  hints = require_object(result.get("verification_hints"), "verification_hints")
@@ -216,11 +244,22 @@ def main() -> int:
216
244
  parser.add_argument("--adapter-exit", type=int, required=True)
217
245
  parser.add_argument("--response", type=Path, required=True)
218
246
  parser.add_argument("--result", type=Path, required=True)
219
- parser.add_argument("--inspect-view", choices=("fingerprint", "ownership"))
247
+ parser.add_argument(
248
+ "--inspect-view", choices=("fingerprint", "ownership", "update-control")
249
+ )
220
250
  args = parser.parse_args()
221
251
  try:
252
+ response_size = args.response.stat().st_size
253
+ if response_size > MAX_RESPONSE_BYTES:
254
+ raise ProtocolError(
255
+ f"response exceeds {MAX_RESPONSE_BYTES}-byte limit"
256
+ )
222
257
  with args.response.open(encoding="utf-8") as handle:
223
- raw = json.load(handle, parse_constant=reject_constant)
258
+ raw = json.load(
259
+ handle,
260
+ parse_constant=reject_constant,
261
+ object_pairs_hook=reject_duplicate_keys,
262
+ )
224
263
  if nesting_exceeds_limit(raw):
225
264
  raise ProtocolError("response JSON nesting exceeds limit")
226
265
  messages, result, error = validate_envelope(
package/scripts/install CHANGED
@@ -16,10 +16,10 @@ echo "Note: remove or disable conflicting Superpowers providers yourself before
16
16
  # missing or stale — including validation — entirely before any Codex
17
17
  # marketplace or plugin mutation. A failed prepare leaves Codex untouched.
18
18
  probe_output=$(sh "$root/scripts/probe" --porcelain)
19
- identity_state=$(printf '%s\n' "$probe_output" | awk -F= '$1 == "identity_state" { print $2 }')
19
+ identity_state=$(spw_probe_field "$probe_output" identity_state)
20
20
  [ -n "$identity_state" ] || spw_die "probe did not report adapter identity state"
21
21
  spw_require_no_legacy_state "$identity_state"
22
- status=$(printf '%s\n' "$probe_output" | awk -F= '$1 == "status" { print $2 }')
22
+ status=$(spw_probe_field "$probe_output" status)
23
23
  case "$status" in
24
24
  needs\ prepare)
25
25
  sh "$root/scripts/prepare"
@@ -36,24 +36,23 @@ desired_commit=$(spw_metadata_commit_or_empty "$root/plugins/superpowers/.superp
36
36
  [ -n "$desired_commit" ] || spw_die "generated metadata missing desired commit after prepare"
37
37
 
38
38
  tmp_parent="${TMPDIR:-/tmp}"
39
- if ! install_workspace=$(mktemp -d "$tmp_parent/superpowers-manager.install.XXXXXX"); then
39
+ if ! install_workspace=$(spw_make_workspace "$tmp_parent" "superpowers-manager.install"); then
40
40
  spw_die "cannot create install workspace under $tmp_parent"
41
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
42
+ spw_install_workspace_trap "$install_workspace"
52
43
 
53
44
  TMPDIR="$install_workspace"
54
45
  export TMPDIR
55
46
  install_result="$install_workspace/install.json"
56
47
  inspect_result="$install_workspace/inspect.json"
48
+ ownership_result="$install_workspace/ownership.json"
49
+ update_control_result="$install_workspace/update-control.json"
50
+ spw_inspect_ownership "$ownership_result"
51
+ spw_inspect_update_control "$update_control_result"
52
+ identity_state=$(spw_adapter_result_get "$ownership_result" "identity_state")
53
+ update_control=$(spw_adapter_result_get "$update_control_result" "update_control")
54
+ spw_require_no_legacy_state "$identity_state"
55
+ spw_require_managed_update_control "$update_control"
57
56
  spw_adapter_install "$install_result" "$root"
58
57
  if ! spw_verify_installed_fingerprint "$desired_commit" "$install_result" "$inspect_result"; then
59
58
  exit 1
package/scripts/pin ADDED
@@ -0,0 +1,51 @@
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/selection.sh"
8
+
9
+ [ "$#" -eq 1 ] || spw_usage_error 'usage: superpowers-manager pin REF'
10
+ requested_ref="$1"
11
+
12
+ if spw_is_pinnable_tag "$requested_ref"; then
13
+ requested_kind=tag
14
+ elif spw_is_full_commit "$requested_ref"; then
15
+ requested_kind=raw-commit
16
+ else
17
+ spw_usage_error 'pin REF must be an exact v-prefixed SemVer tag or full 40-hex commit'
18
+ fi
19
+
20
+ spw_require_command python3
21
+ state_path=$(spw_selection_state_path)
22
+ tmp_parent="${TMPDIR:-/tmp}"
23
+ if ! pin_workspace=$(spw_make_workspace "$tmp_parent" "superpowers-manager.pin"); then
24
+ spw_die "cannot create pin workspace under $tmp_parent"
25
+ fi
26
+ spw_install_workspace_trap "$pin_workspace"
27
+
28
+ normalized="$pin_workspace/selection.json"
29
+ spw_selection_state "$root" read \
30
+ --path="$state_path" --output="$normalized"
31
+
32
+ source="${SUPERPOWERS_UPSTREAM_URL:-$SPW_UPSTREAM_URL_DEFAULT}"
33
+ spw_selection_state "$root" \
34
+ validate-source --source="$source"
35
+
36
+ if [ "$requested_kind" = tag ]; then
37
+ spw_require_command git
38
+ commit=$(spw_resolve_exact_tag "$source" "$requested_ref")
39
+ resolved_ref="$requested_ref"
40
+ else
41
+ spw_require_command git
42
+ requested_ref=$(printf '%s' "$requested_ref" | tr '[:upper:]' '[:lower:]')
43
+ resolved_ref="$requested_ref"
44
+ commit=$(spw_verify_raw_commit "$source" "$requested_ref" "$pin_workspace")
45
+ fi
46
+
47
+ spw_selection_state "$root" write-pinned \
48
+ --path="$state_path" --source="$source" --requested-ref="$requested_ref" \
49
+ --resolved-ref="$resolved_ref" --commit="$commit"
50
+
51
+ printf 'pinned upstream selection to %s at %s\n' "$requested_ref" "$commit"
package/scripts/prepare CHANGED
@@ -4,11 +4,11 @@ set -eu
4
4
  root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
5
5
  . "$root/scripts/core/common.sh"
6
6
  . "$root/scripts/core/upstream.sh"
7
+ . "$root/scripts/core/selection.sh"
7
8
  . "$root/scripts/core/provenance.sh"
8
9
  . "$root/scripts/core/lifecycle.sh"
9
10
  . "$root/scripts/core/adapter.sh"
10
11
 
11
- upstream_url="${SUPERPOWERS_UPSTREAM_URL:-$SPW_UPSTREAM_URL_DEFAULT}"
12
12
  cache_parent="${SUPERPOWERS_CACHE_DIR:-$root/.cache/upstream}"
13
13
  plugin_root="${SUPERPOWERS_PLUGIN_ROOT:-$root/plugins/superpowers}"
14
14
  manifest_template="${SUPERPOWERS_MANIFEST_TEMPLATE:-$root/plugins/superpowers/.codex-plugin/plugin.template.json}"
@@ -25,51 +25,40 @@ esac
25
25
  cache="$cache_parent/superpowers"
26
26
  tmp_parent=$(dirname "$plugin_root")
27
27
  mkdir -p "$tmp_parent"
28
- if ! prepare_workspace=$(mktemp -d "$tmp_parent/.superpowers.prepare.XXXXXX"); then
28
+ if ! prepare_workspace=$(spw_make_workspace "$tmp_parent" ".superpowers.prepare"); then
29
29
  spw_die "cannot create prepare workspace under $tmp_parent"
30
30
  fi
31
+ spw_install_workspace_trap "$prepare_workspace"
31
32
  tmp_plugin="$prepare_workspace/superpowers"
32
33
  adapter_result="$prepare_workspace/adapter-build.json"
33
34
 
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
35
  TMPDIR="$prepare_workspace"
47
36
  export TMPDIR
48
37
 
49
- requested_ref=$(spw_config_ref "$root")
50
-
51
- spw_require_command git
52
38
  spw_require_command python3
39
+ spw_compute_effective_selection "$root" "$prepare_workspace"
40
+ spw_require_command git
53
41
  spw_require_command "$SPW_ADAPTER"
54
42
  [ -f "$manifest_template" ] || spw_die "missing fallback manifest template: $manifest_template"
55
43
 
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
44
  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
45
+ if [ "$SPW_SELECTION_MODE" = "pinned" ]; then
46
+ spw_fetch_exact_commit \
47
+ "$SPW_EFFECTIVE_SOURCE" "$SPW_DESIRED_COMMIT" "$cache" "$prepare_workspace"
66
48
  else
67
- if ! git clone "$upstream_url" "$cache"; then
68
- spw_die "cannot clone upstream repo: $upstream_url"
49
+ git_source=$(spw_git_safe_source "$SPW_EFFECTIVE_SOURCE")
50
+ if [ -d "$cache/.git" ]; then
51
+ if ! git -C "$cache" fetch --tags --prune -- "$git_source"; then
52
+ spw_die "cannot fetch upstream repo: $SPW_EFFECTIVE_SOURCE"
53
+ fi
54
+ else
55
+ if ! git clone -- "$git_source" "$cache"; then
56
+ spw_die "cannot clone upstream repo: $SPW_EFFECTIVE_SOURCE"
57
+ fi
69
58
  fi
70
59
  fi
71
- if ! git -C "$cache" checkout --detach "$commit" >/dev/null; then
72
- spw_die "cannot check out upstream commit: $commit"
60
+ if ! git -C "$cache" checkout --detach "$SPW_DESIRED_COMMIT" >/dev/null; then
61
+ spw_die "cannot check out upstream commit: $SPW_DESIRED_COMMIT"
73
62
  fi
74
63
 
75
64
  spw_require_upstream_path "$cache/skills" "skills/"
@@ -94,21 +83,23 @@ fi
94
83
 
95
84
  spw_write_metadata_json \
96
85
  "$tmp_plugin/.superpowers-upstream.json" \
97
- "$upstream_url" \
98
- "$requested_ref" \
99
- "$resolved_ref" \
100
- "$commit" \
86
+ "$SPW_EFFECTIVE_SOURCE" \
87
+ "$SPW_REQUESTED_REF" \
88
+ "$SPW_RESOLVED_REF" \
89
+ "$SPW_DESIRED_COMMIT" \
101
90
  "$upstream_manifest_version"
102
91
 
103
92
  manager_version=$(
104
- spw_manifest_version_for_ref "$requested_ref" "$resolution_kind" "$resolved_ref" "$commit"
93
+ spw_manifest_version_for_ref \
94
+ "$SPW_REQUESTED_REF" "$SPW_RESOLUTION_KIND" \
95
+ "$SPW_RESOLVED_REF" "$SPW_DESIRED_COMMIT"
105
96
  )
106
97
  spw_invoke_adapter build "$adapter_result" "" -- \
107
98
  --upstream-root "$cache" \
108
99
  --candidate-root "$tmp_plugin" \
109
- --requested-ref "$requested_ref" \
110
- --resolved-ref "$resolved_ref" \
111
- --commit "$commit" \
100
+ --requested-ref "$SPW_REQUESTED_REF" \
101
+ --resolved-ref "$SPW_RESOLVED_REF" \
102
+ --commit "$SPW_DESIRED_COMMIT" \
112
103
  --manager-version "$manager_version" \
113
104
  --upstream-manifest-version "$upstream_manifest_version" \
114
105
  --fallback-manifest "$manifest_template"
@@ -123,4 +114,4 @@ fi
123
114
 
124
115
  spw_replace_generated_tree "$tmp_plugin" "$plugin_root"
125
116
 
126
- echo "prepared $resolved_ref at $commit"
117
+ echo "prepared $SPW_RESOLVED_REF at $SPW_DESIRED_COMMIT"
package/scripts/probe CHANGED
@@ -4,58 +4,79 @@ set -eu
4
4
  root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
5
5
  . "$root/scripts/core/common.sh"
6
6
  . "$root/scripts/core/upstream.sh"
7
+ . "$root/scripts/core/selection.sh"
7
8
  . "$root/scripts/core/provenance.sh"
8
9
  . "$root/scripts/core/status.sh"
9
10
  . "$root/scripts/core/lifecycle.sh"
10
11
  . "$root/scripts/core/adapter.sh"
11
12
 
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
13
  tmp_parent="${TMPDIR:-/tmp}"
20
- if ! probe_workspace=$(mktemp -d "$tmp_parent/superpowers-manager.probe.XXXXXX"); then
14
+ if ! probe_workspace=$(spw_make_workspace "$tmp_parent" "superpowers-manager.probe"); then
21
15
  spw_die "cannot create probe workspace under $tmp_parent"
22
16
  fi
17
+ spw_install_workspace_trap "$probe_workspace"
23
18
  fingerprint_result="$probe_workspace/fingerprint.json"
24
19
  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
20
+ update_control_result="$probe_workspace/update-control.json"
35
21
 
36
22
  TMPDIR="$probe_workspace"
37
23
  export TMPDIR
24
+ spw_compute_effective_selection "$root" "$probe_workspace"
25
+ effective_source=$(spw_display_source "$SPW_EFFECTIVE_SOURCE")
26
+ if [ -n "$SPW_SAVED_SOURCE" ]; then
27
+ saved_source=$(spw_display_source "$SPW_SAVED_SOURCE")
28
+ else
29
+ saved_source=""
30
+ fi
31
+
32
+ generated_commit=$(spw_generated_commit_or_empty "$root")
38
33
  spw_inspect_fingerprint "$fingerprint_result"
39
34
  installed_commit=$(spw_adapter_result_get "$fingerprint_result" "fingerprint")
40
35
  spw_inspect_ownership "$ownership_result"
41
36
  identity_state=$(spw_adapter_result_get "$ownership_result" "identity_state")
37
+ spw_inspect_update_control "$update_control_result"
38
+ update_control=$(spw_adapter_result_get "$update_control_result" "update_control")
42
39
 
43
- status=$(spw_status_for_commits "$desired_commit" "$generated_commit" "$installed_commit")
40
+ status=$(spw_status_for_commits "$SPW_DESIRED_COMMIT" "$generated_commit" "$installed_commit")
44
41
 
45
42
  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"
43
+ printf 'requested_ref=%s\n' "$SPW_REQUESTED_REF"
44
+ printf 'resolved_ref=%s\n' "$SPW_RESOLVED_REF"
45
+ printf 'desired_commit=%s\n' "$SPW_DESIRED_COMMIT"
49
46
  printf 'generated_commit=%s\n' "$generated_commit"
50
47
  printf 'installed_commit=%s\n' "$installed_commit"
51
48
  printf 'identity_state=%s\n' "$identity_state"
52
49
  printf 'status=%s\n' "$status"
50
+ printf 'selection_origin=%s\n' "$SPW_SELECTION_ORIGIN"
51
+ printf 'selection_mode=%s\n' "$SPW_SELECTION_MODE"
52
+ printf 'upstream_source_origin=%s\n' "$SPW_UPSTREAM_SOURCE_ORIGIN"
53
+ printf 'effective_source=%s\n' "$effective_source"
54
+ printf 'saved_mode=%s\n' "$SPW_SAVED_MODE"
55
+ printf 'saved_source=%s\n' "$saved_source"
56
+ printf 'saved_requested_ref=%s\n' "$SPW_SAVED_REQUESTED_REF"
57
+ printf 'saved_resolved_ref=%s\n' "$SPW_SAVED_RESOLVED_REF"
58
+ printf 'saved_commit=%s\n' "$SPW_SAVED_COMMIT"
59
+ printf 'update_control=%s\n' "$update_control"
53
60
  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"
61
+ printf 'requested ref: %s\n' "$SPW_REQUESTED_REF"
62
+ printf 'resolved ref: %s\n' "$SPW_RESOLVED_REF"
63
+ printf 'desired commit: %s\n' "$SPW_DESIRED_COMMIT"
57
64
  printf 'generated plugin commit: %s\n' "${generated_commit:-not present}"
58
65
  printf 'installed manager commit or fingerprint: %s\n' "${installed_commit:-not detected}"
59
66
  printf 'Codex identity state: %s\n' "$identity_state"
60
67
  printf 'status: %s\n' "$status"
68
+ printf 'selection origin: %s\n' "$SPW_SELECTION_ORIGIN"
69
+ printf 'selection mode: %s\n' "$SPW_SELECTION_MODE"
70
+ printf 'upstream source origin: %s\n' "$SPW_UPSTREAM_SOURCE_ORIGIN"
71
+ printf 'effective source: %s\n' "$effective_source"
72
+ printf 'saved mode: %s\n' "$SPW_SAVED_MODE"
73
+ printf 'saved source: %s\n' "$saved_source"
74
+ printf 'saved requested ref: %s\n' "$SPW_SAVED_REQUESTED_REF"
75
+ printf 'saved resolved ref: %s\n' "$SPW_SAVED_RESOLVED_REF"
76
+ printf 'saved commit: %s\n' "$SPW_SAVED_COMMIT"
77
+ printf 'update control: %s\n' "$update_control"
78
+ if [ "$SPW_SELECTION_ORIGIN" != "$SPW_UPSTREAM_SOURCE_ORIGIN" ]; then
79
+ printf 'warning: effective ref and source have mixed origins (ref: %s, source: %s)\n' \
80
+ "$SPW_SELECTION_ORIGIN" "$SPW_UPSTREAM_SOURCE_ORIGIN"
81
+ fi
61
82
  fi
@@ -0,0 +1,30 @@
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/selection.sh"
8
+
9
+ [ "$#" -eq 0 ] || spw_usage_error 'usage: superpowers-manager track-latest'
10
+
11
+ spw_require_command python3
12
+ state_path=$(spw_selection_state_path)
13
+ tmp_parent="${TMPDIR:-/tmp}"
14
+ if ! track_workspace=$(spw_make_workspace "$tmp_parent" "superpowers-manager.track-latest"); then
15
+ spw_die "cannot create track-latest workspace under $tmp_parent"
16
+ fi
17
+ spw_install_workspace_trap "$track_workspace"
18
+
19
+ normalized="$track_workspace/selection.json"
20
+ spw_selection_state "$root" read \
21
+ --path="$state_path" --output="$normalized"
22
+
23
+ source="${SUPERPOWERS_UPSTREAM_URL:-$SPW_UPSTREAM_URL_DEFAULT}"
24
+ spw_selection_state "$root" \
25
+ validate-source --source="$source"
26
+
27
+ spw_selection_state "$root" write-track-latest \
28
+ --path="$state_path" --source="$source"
29
+
30
+ printf '%s\n' 'saved upstream selection: latest stable release'
package/scripts/uninstall CHANGED
@@ -10,21 +10,12 @@ root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
10
10
  spw_require_command python3
11
11
 
12
12
  tmp_parent="${TMPDIR:-/tmp}"
13
- if ! uninstall_workspace=$(mktemp -d "$tmp_parent/superpowers-manager.uninstall.XXXXXX"); then
13
+ if ! uninstall_workspace=$(spw_make_workspace "$tmp_parent" "superpowers-manager.uninstall"); then
14
14
  spw_die "cannot create uninstall workspace under $tmp_parent"
15
15
  fi
16
+ spw_install_workspace_trap "$uninstall_workspace"
16
17
  inspect_result="$uninstall_workspace/inspect.json"
17
18
  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
19
 
29
20
  TMPDIR="$uninstall_workspace"
30
21
  export TMPDIR
package/scripts/unpin ADDED
@@ -0,0 +1,41 @@
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/selection.sh"
8
+
9
+ [ "$#" -eq 0 ] || spw_usage_error 'usage: superpowers-manager unpin'
10
+
11
+ state_path=$(spw_selection_state_path)
12
+ fallback=$(SUPERPOWERS_REF= spw_config_ref "$root")
13
+ removed=false
14
+
15
+ if [ -L "$state_path" ]; then
16
+ spw_die "selection state path is not a regular file; remove it manually after inspecting: $state_path"
17
+ elif [ -e "$state_path" ]; then
18
+ if [ ! -f "$state_path" ]; then
19
+ spw_die "selection state path is not a regular file; remove it manually after inspecting: $state_path"
20
+ fi
21
+ if ! rm -f "$state_path"; then
22
+ spw_die "cannot remove selection state: $state_path"
23
+ fi
24
+ removed=true
25
+ fi
26
+
27
+ if [ -e "$state_path" ] || [ -L "$state_path" ]; then
28
+ spw_die "selection state remains after removal attempt: $state_path"
29
+ fi
30
+
31
+ if [ "$removed" = true ]; then
32
+ printf 'removed saved upstream selection; packaged fallback is %s\n' "$fallback"
33
+ else
34
+ printf 'no saved upstream selection; packaged fallback is %s\n' "$fallback"
35
+ fi
36
+ if [ -n "${SUPERPOWERS_REF:-}" ]; then
37
+ printf '%s\n' 'note: active SUPERPOWERS_REF override remains effective'
38
+ fi
39
+ if [ -n "${SUPERPOWERS_UPSTREAM_URL:-}" ]; then
40
+ printf '%s\n' 'note: active SUPERPOWERS_UPSTREAM_URL override remains effective'
41
+ fi