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.
- package/README.md +184 -64
- package/bin/superpowers-manager.js +9 -237
- package/dist/adapter-result.js +204 -0
- package/dist/adapter.js +649 -0
- package/dist/atomic.js +142 -0
- package/dist/cli-arguments.js +32 -0
- package/dist/cli.js +293 -0
- package/dist/codex-json.js +89 -0
- package/dist/codex-state.js +78 -0
- package/dist/commands/context.js +1 -0
- package/dist/commands/install.js +376 -0
- package/dist/commands/pin.js +79 -0
- package/dist/commands/prepare.js +527 -0
- package/dist/commands/probe.js +392 -0
- package/dist/commands/track-latest.js +44 -0
- package/dist/commands/uninstall.js +308 -0
- package/dist/commands/unpin.js +95 -0
- package/dist/commands/update.js +138 -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/hooks.js +290 -0
- package/dist/lifecycle.js +220 -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 -2
- package/scripts/adapters/codex/adapter +0 -598
- package/scripts/adapters/codex/lib.sh +0 -391
- package/scripts/adapters/codex/validate-generated-plugin.py +0 -314
- package/scripts/core/adapter.sh +0 -92
- package/scripts/core/common.sh +0 -35
- package/scripts/core/lifecycle.sh +0 -127
- package/scripts/core/provenance.sh +0 -135
- package/scripts/core/status.sh +0 -27
- package/scripts/core/upstream.sh +0 -146
- package/scripts/core/validate-adapter-response.py +0 -245
- package/scripts/install +0 -60
- package/scripts/prepare +0 -126
- package/scripts/probe +0 -61
- package/scripts/uninstall +0 -44
- package/scripts/update +0 -31
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
function byteEscape(byte) {
|
|
2
|
+
return `\\x${byte.toString(16).padStart(2, "0")}`;
|
|
3
|
+
}
|
|
4
|
+
function decodeBackslashReplace(bytes) {
|
|
5
|
+
let result = "";
|
|
6
|
+
for (let index = 0; index < bytes.length;) {
|
|
7
|
+
const first = bytes[index];
|
|
8
|
+
if (first < 0x80) {
|
|
9
|
+
result += String.fromCodePoint(first);
|
|
10
|
+
index += 1;
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
let length = 0;
|
|
14
|
+
let codePoint = 0;
|
|
15
|
+
let secondMinimum = 0x80;
|
|
16
|
+
let secondMaximum = 0xbf;
|
|
17
|
+
if (first >= 0xc2 && first <= 0xdf) {
|
|
18
|
+
length = 2;
|
|
19
|
+
codePoint = first & 0x1f;
|
|
20
|
+
}
|
|
21
|
+
else if (first >= 0xe0 && first <= 0xef) {
|
|
22
|
+
length = 3;
|
|
23
|
+
codePoint = first & 0x0f;
|
|
24
|
+
if (first === 0xe0)
|
|
25
|
+
secondMinimum = 0xa0;
|
|
26
|
+
if (first === 0xed)
|
|
27
|
+
secondMaximum = 0x9f;
|
|
28
|
+
}
|
|
29
|
+
else if (first >= 0xf0 && first <= 0xf4) {
|
|
30
|
+
length = 4;
|
|
31
|
+
codePoint = first & 0x07;
|
|
32
|
+
if (first === 0xf0)
|
|
33
|
+
secondMinimum = 0x90;
|
|
34
|
+
if (first === 0xf4)
|
|
35
|
+
secondMaximum = 0x8f;
|
|
36
|
+
}
|
|
37
|
+
const second = bytes[index + 1];
|
|
38
|
+
let valid = length > 0 &&
|
|
39
|
+
index + length <= bytes.length &&
|
|
40
|
+
second !== undefined &&
|
|
41
|
+
second >= secondMinimum &&
|
|
42
|
+
second <= secondMaximum;
|
|
43
|
+
for (let offset = 2; valid && offset < length; offset += 1) {
|
|
44
|
+
const byte = bytes[index + offset];
|
|
45
|
+
valid = byte >= 0x80 && byte <= 0xbf;
|
|
46
|
+
}
|
|
47
|
+
if (!valid) {
|
|
48
|
+
result += byteEscape(first);
|
|
49
|
+
index += 1;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
for (let offset = 1; offset < length; offset += 1) {
|
|
53
|
+
codePoint = (codePoint << 6) | (bytes[index + offset] & 0x3f);
|
|
54
|
+
}
|
|
55
|
+
result += String.fromCodePoint(codePoint);
|
|
56
|
+
index += length;
|
|
57
|
+
}
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
function pythonUnicodeEscape(value) {
|
|
61
|
+
let result = "";
|
|
62
|
+
for (const character of value) {
|
|
63
|
+
const code = character.codePointAt(0);
|
|
64
|
+
if (character === "\\")
|
|
65
|
+
result += "\\\\";
|
|
66
|
+
else if (code === 0x09)
|
|
67
|
+
result += "\\t";
|
|
68
|
+
else if (code === 0x0a)
|
|
69
|
+
result += "\\n";
|
|
70
|
+
else if (code === 0x0d)
|
|
71
|
+
result += "\\r";
|
|
72
|
+
else if (code >= 0x20 && code <= 0x7e)
|
|
73
|
+
result += character;
|
|
74
|
+
else if (code <= 0xff)
|
|
75
|
+
result += `\\x${code.toString(16).padStart(2, "0")}`;
|
|
76
|
+
else if (code <= 0xffff)
|
|
77
|
+
result += `\\u${code.toString(16).padStart(4, "0")}`;
|
|
78
|
+
else
|
|
79
|
+
result += `\\U${code.toString(16).padStart(8, "0")}`;
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
export function pythonUnicodeEscapeBytes(bytes) {
|
|
84
|
+
return pythonUnicodeEscape(decodeBackslashReplace(bytes));
|
|
85
|
+
}
|
|
86
|
+
export class AdapterMessageLog {
|
|
87
|
+
#messages = [];
|
|
88
|
+
appendBytes(channel, bytes) {
|
|
89
|
+
let start = 0;
|
|
90
|
+
for (let index = 0; index <= bytes.length; index += 1) {
|
|
91
|
+
if (index !== bytes.length && bytes[index] !== 0x0a)
|
|
92
|
+
continue;
|
|
93
|
+
const chunk = bytes.slice(start, index);
|
|
94
|
+
start = index + 1;
|
|
95
|
+
if (chunk.length === 0)
|
|
96
|
+
continue;
|
|
97
|
+
this.#messages.push({
|
|
98
|
+
channel,
|
|
99
|
+
text: pythonUnicodeEscapeBytes(chunk),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
appendText(channel, text) {
|
|
104
|
+
const escaped = pythonUnicodeEscape(text);
|
|
105
|
+
if (escaped.length > 0)
|
|
106
|
+
this.#messages.push({ channel, text: escaped });
|
|
107
|
+
}
|
|
108
|
+
snapshot() {
|
|
109
|
+
return this.#messages.map((message) => ({ ...message }));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export function successResult(operation, result, messages) {
|
|
113
|
+
return {
|
|
114
|
+
status: 0,
|
|
115
|
+
outcome: {
|
|
116
|
+
operation,
|
|
117
|
+
ok: true,
|
|
118
|
+
messages,
|
|
119
|
+
result,
|
|
120
|
+
error: null,
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
export function failureResult(operation, code, message, hints, messages) {
|
|
125
|
+
return {
|
|
126
|
+
status: 1,
|
|
127
|
+
outcome: {
|
|
128
|
+
operation,
|
|
129
|
+
ok: false,
|
|
130
|
+
messages,
|
|
131
|
+
result: null,
|
|
132
|
+
error: { code, message, hints },
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
export function hasTerminalControl(value) {
|
|
137
|
+
for (const character of value) {
|
|
138
|
+
const code = character.codePointAt(0);
|
|
139
|
+
if (code < 0x20 ||
|
|
140
|
+
(code >= 0x7f && code <= 0x9f) ||
|
|
141
|
+
(code >= 0xd800 && code <= 0xdfff)) {
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
// D8b's enforcement point on the LIVE product path. replayOutcome -- which
|
|
148
|
+
// every command replays through -- wrote these three strings to the terminal
|
|
149
|
+
// unfiltered.
|
|
150
|
+
//
|
|
151
|
+
// NOT requireProtocolString: that one message
|
|
152
|
+
// ("protocol strings must not contain terminal control characters") names no
|
|
153
|
+
// member, and D8b requires the thrown message to name the failing one. So this
|
|
154
|
+
// scans with the predicate underneath it instead.
|
|
155
|
+
//
|
|
156
|
+
// Order is code, then message, then hints by ascending index, so the thrown
|
|
157
|
+
// message is a function of the outcome rather than of iteration order.
|
|
158
|
+
//
|
|
159
|
+
// The offending value is never interpolated, never sanitized, and never
|
|
160
|
+
// truncated into the message: the contract is that an unsafe string does not
|
|
161
|
+
// reach the terminal, not that it arrives altered. The hint INDEX is the one
|
|
162
|
+
// interpolated value, and it is a bounded integer. src/cli.ts's handler catch
|
|
163
|
+
// emits `error: <this module's own hand-written message>` with exit 1, which
|
|
164
|
+
// is the sanctioned interpolation under AGENTS.md.
|
|
165
|
+
//
|
|
166
|
+
// Exported separately from writeAdapterFailure because replayOutcome writes
|
|
167
|
+
// every message BEFORE the error line: a caller that validated only at the
|
|
168
|
+
// write would already have put the context lines on the stream when the guard
|
|
169
|
+
// fired. Hoisting this above the message loop is what makes a refused failure
|
|
170
|
+
// leave both streams untouched.
|
|
171
|
+
export function assertFailureWritable(outcome) {
|
|
172
|
+
if (outcome.ok)
|
|
173
|
+
return;
|
|
174
|
+
if (hasTerminalControl(outcome.error.code)) {
|
|
175
|
+
throw new Error("adapter failure code contains a terminal control character");
|
|
176
|
+
}
|
|
177
|
+
if (hasTerminalControl(outcome.error.message)) {
|
|
178
|
+
throw new Error("adapter failure message contains a terminal control character");
|
|
179
|
+
}
|
|
180
|
+
for (const [index, hint] of outcome.error.hints.entries()) {
|
|
181
|
+
if (hasTerminalControl(hint)) {
|
|
182
|
+
throw new Error(`adapter failure hint[${index}] contains a terminal control character`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Validates the WHOLE failure before the first write, so a hint that fails
|
|
187
|
+
// after two safe ones leaves neither of those two on the stream. `ctx` is
|
|
188
|
+
// typed structurally rather than as CommandContext: this reads exactly one of
|
|
189
|
+
// that interface's five members, and naming the wide type would point this
|
|
190
|
+
// module at one that already imports from it.
|
|
191
|
+
export function writeAdapterFailure(ctx, outcome) {
|
|
192
|
+
assertFailureWritable(outcome);
|
|
193
|
+
if (outcome.ok)
|
|
194
|
+
return;
|
|
195
|
+
ctx.stderr.write(`error: ${outcome.error.message}\n`);
|
|
196
|
+
for (const hint of outcome.error.hints) {
|
|
197
|
+
ctx.stderr.write(`hint: ${hint}\n`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
export function requireProtocolString(value) {
|
|
201
|
+
if (hasTerminalControl(value)) {
|
|
202
|
+
throw new Error("protocol strings must not contain terminal control characters");
|
|
203
|
+
}
|
|
204
|
+
}
|