supipowers 1.2.3 → 1.2.5
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/bin/install.mjs +5 -1
- package/bin/install.ts +120 -5
- package/bin/local-install.sh +17 -13
- package/package.json +10 -1
- package/src/deps/registry.ts +4 -4
package/bin/install.mjs
CHANGED
|
@@ -5,7 +5,11 @@ import { dirname, join } from "node:path";
|
|
|
5
5
|
|
|
6
6
|
const isWindows = process.platform === "win32";
|
|
7
7
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
|
|
8
|
+
// When invoked via bunx/npx, always force a full install to guarantee
|
|
9
|
+
// node_modules/ and a consistent extension directory. The --force flag
|
|
10
|
+
// bypasses the "already up to date" version check.
|
|
11
|
+
const args = [join(__dirname, "install.ts"), "--force", ...process.argv.slice(2)];
|
|
12
|
+
const result = spawnSync("bun", args, {
|
|
9
13
|
stdio: "inherit",
|
|
10
14
|
env: process.env,
|
|
11
15
|
shell: isWindows,
|
package/bin/install.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { spawnSync } from "node:child_process";
|
|
|
14
14
|
import {
|
|
15
15
|
readFileSync,
|
|
16
16
|
writeFileSync,
|
|
17
|
+
appendFileSync,
|
|
17
18
|
existsSync,
|
|
18
19
|
mkdirSync,
|
|
19
20
|
cpSync,
|
|
@@ -123,10 +124,37 @@ async function exec(cmd: string, args: string[]): Promise<ExecResult> {
|
|
|
123
124
|
return { stdout: r.stdout ?? "", stderr: r.stderr ?? "", code: r.status ?? 1 };
|
|
124
125
|
}
|
|
125
126
|
|
|
126
|
-
// ── CLI Flags
|
|
127
|
+
// ── CLI Flags ────────────────────────────────────────────────────
|
|
127
128
|
|
|
128
129
|
const cliArgs = process.argv.slice(2);
|
|
129
130
|
const skipDeps = cliArgs.includes("--skip-deps");
|
|
131
|
+
const FORCE = cliArgs.includes("--force");
|
|
132
|
+
const DEBUG = cliArgs.includes("--debug");
|
|
133
|
+
|
|
134
|
+
// ── Debug logging ────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
const LOG_FILE = resolve(process.cwd(), "supipowers-install.log");
|
|
137
|
+
|
|
138
|
+
function log(msg: string): void {
|
|
139
|
+
if (!DEBUG) return;
|
|
140
|
+
const line = `[${new Date().toISOString()}] ${msg}\n`;
|
|
141
|
+
appendFileSync(LOG_FILE, line);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (DEBUG) {
|
|
145
|
+
// Start fresh log
|
|
146
|
+
writeFileSync(LOG_FILE, `supipowers installer debug log\n`);
|
|
147
|
+
log(`platform: ${process.platform}`);
|
|
148
|
+
log(`arch: ${process.arch}`);
|
|
149
|
+
log(`bun: ${process.versions?.bun ?? "N/A"}`);
|
|
150
|
+
log(`node: ${process.version}`);
|
|
151
|
+
log(`cwd: ${process.cwd()}`);
|
|
152
|
+
log(`homedir: ${homedir()}`);
|
|
153
|
+
log(`argv: ${JSON.stringify(process.argv)}`);
|
|
154
|
+
log(`__dirname: ${__dirname}`);
|
|
155
|
+
log(`packageRoot will be: ${resolve(__dirname, "..")}`);
|
|
156
|
+
log(`isWindows: ${isWindows}`);
|
|
157
|
+
}
|
|
130
158
|
|
|
131
159
|
// ── Install to platform ──────────────────────────────────────
|
|
132
160
|
|
|
@@ -143,18 +171,29 @@ function installToPlatform(platformDir: string, packageRoot: string): string {
|
|
|
143
171
|
const extDir = join(agentDir, "extensions", "supipowers");
|
|
144
172
|
const installedPkgPath = join(extDir, "package.json");
|
|
145
173
|
|
|
174
|
+
log(`installToPlatform(platformDir=${platformDir}, packageRoot=${packageRoot})`);
|
|
175
|
+
log(` agentDir: ${agentDir}`);
|
|
176
|
+
log(` extDir: ${extDir}`);
|
|
177
|
+
|
|
146
178
|
// Check for existing installation
|
|
147
179
|
let installedVersion: string | null = null;
|
|
148
180
|
if (existsSync(installedPkgPath)) {
|
|
149
181
|
try {
|
|
150
182
|
const installed = JSON.parse(readFileSync(installedPkgPath, "utf8"));
|
|
151
183
|
installedVersion = installed.version;
|
|
184
|
+
log(` existing version: ${installedVersion}`);
|
|
152
185
|
} catch {
|
|
153
|
-
|
|
186
|
+
log(` existing package.json corrupted, treating as fresh install`);
|
|
154
187
|
}
|
|
188
|
+
} else {
|
|
189
|
+
log(` no existing installation found`);
|
|
155
190
|
}
|
|
156
191
|
|
|
157
|
-
|
|
192
|
+
const hasNodeModules = existsSync(join(extDir, "node_modules"));
|
|
193
|
+
log(` node_modules present: ${hasNodeModules}`);
|
|
194
|
+
|
|
195
|
+
if (installedVersion === VERSION && hasNodeModules && !FORCE) {
|
|
196
|
+
log(` already up to date with deps, skipping`);
|
|
158
197
|
note(
|
|
159
198
|
`supipowers v${VERSION} is already installed and up to date.`,
|
|
160
199
|
`Up to date (${platformDir})`,
|
|
@@ -162,9 +201,16 @@ function installToPlatform(platformDir: string, packageRoot: string): string {
|
|
|
162
201
|
return extDir;
|
|
163
202
|
}
|
|
164
203
|
|
|
204
|
+
if (installedVersion === VERSION && !hasNodeModules) {
|
|
205
|
+
log(` same version but node_modules missing — reinstalling deps`);
|
|
206
|
+
}
|
|
207
|
+
if (FORCE) {
|
|
208
|
+
log(` --force flag set, reinstalling`);
|
|
209
|
+
}
|
|
210
|
+
|
|
165
211
|
const action = installedVersion ? "Updating" : "Installing";
|
|
166
212
|
if (installedVersion) {
|
|
167
|
-
note(`v${installedVersion}
|
|
213
|
+
note(`v${installedVersion} \u2192 v${VERSION}`, `Updating supipowers (${platformDir})`);
|
|
168
214
|
}
|
|
169
215
|
|
|
170
216
|
const s = spinner();
|
|
@@ -173,14 +219,40 @@ function installToPlatform(platformDir: string, packageRoot: string): string {
|
|
|
173
219
|
try {
|
|
174
220
|
// Clean previous installation to remove stale files
|
|
175
221
|
if (existsSync(extDir)) {
|
|
222
|
+
log(` removing old extDir`);
|
|
176
223
|
rmSync(extDir, { recursive: true });
|
|
177
224
|
}
|
|
178
225
|
|
|
179
|
-
// Copy extension (src/ + bin/ + package.json)
|
|
226
|
+
// Copy extension (src/ + bin/ + package.json) \u2192 ~/<platform>/agent/extensions/supipowers/
|
|
227
|
+
log(` creating extDir and copying files`);
|
|
180
228
|
mkdirSync(extDir, { recursive: true });
|
|
181
229
|
cpSync(join(packageRoot, "src"), join(extDir, "src"), { recursive: true });
|
|
182
230
|
cpSync(join(packageRoot, "bin"), join(extDir, "bin"), { recursive: true });
|
|
183
231
|
cpSync(join(packageRoot, "package.json"), join(extDir, "package.json"));
|
|
232
|
+
log(` files copied to ${extDir}`);
|
|
233
|
+
|
|
234
|
+
// Rewrite package.json for the installed extension.
|
|
235
|
+
// The npm-published package.json has bin, scripts, prepare, devDeps —
|
|
236
|
+
// all of which cause problems during `bun install` in the extension dir.
|
|
237
|
+
// We keep only what OMP needs (omp.extensions) and the runtime dependencies.
|
|
238
|
+
const sourcePkg = JSON.parse(readFileSync(join(extDir, "package.json"), "utf8"));
|
|
239
|
+
const runtimePkg = {
|
|
240
|
+
name: sourcePkg.name,
|
|
241
|
+
version: sourcePkg.version,
|
|
242
|
+
type: sourcePkg.type,
|
|
243
|
+
omp: sourcePkg.omp,
|
|
244
|
+
dependencies: {
|
|
245
|
+
// Only packages imported at runtime by src/ code:
|
|
246
|
+
// - config/schema.ts → @sinclair/typebox
|
|
247
|
+
// - commands/model.ts, model-picker.ts → @oh-my-pi/pi-ai
|
|
248
|
+
// - commands/model-picker.ts → @oh-my-pi/pi-tui
|
|
249
|
+
"@sinclair/typebox": "*",
|
|
250
|
+
"@oh-my-pi/pi-ai": "*",
|
|
251
|
+
"@oh-my-pi/pi-tui": "*",
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
writeFileSync(join(extDir, "package.json"), JSON.stringify(runtimePkg, null, 2));
|
|
255
|
+
log(` rewrote package.json: ${JSON.stringify(runtimePkg, null, 2)}`);
|
|
184
256
|
|
|
185
257
|
// Copy skills → ~/<platform>/agent/skills/<skillname>/SKILL.md
|
|
186
258
|
const skillsSource = join(packageRoot, "skills");
|
|
@@ -196,12 +268,44 @@ function installToPlatform(platformDir: string, packageRoot: string): string {
|
|
|
196
268
|
}
|
|
197
269
|
}
|
|
198
270
|
|
|
271
|
+
// Install runtime dependencies so the extension's imports resolve.
|
|
272
|
+
// Without node_modules/, external imports (@sinclair/typebox, @oh-my-pi/*)
|
|
273
|
+
// fail on systems where these packages aren't in Bun's global install.
|
|
274
|
+
log(` running: bun install (cwd=${extDir})`);
|
|
275
|
+
s.message("Installing extension dependencies...");
|
|
276
|
+
const install = run("bun", ["install"], { cwd: extDir });
|
|
277
|
+
log(` bun install exit code: ${install.status}`);
|
|
278
|
+
log(` bun install stdout: ${install.stdout ?? "(null)"}`);
|
|
279
|
+
log(` bun install stderr: ${install.stderr ?? "(null)"}`);
|
|
280
|
+
if (install.error) log(` bun install error: ${install.error.message}`);
|
|
281
|
+
if (install.status !== 0) {
|
|
282
|
+
// Non-fatal: the extension may still work if OMP provides the deps
|
|
283
|
+
// via its own module resolution (e.g. Bun global install on macOS).
|
|
284
|
+
note(
|
|
285
|
+
"Could not install extension dependencies.\n" +
|
|
286
|
+
"If /supi commands don't appear in OMP, run:\n" +
|
|
287
|
+
` cd ~/${platformDir}/agent/extensions/supipowers && bun install`,
|
|
288
|
+
"Warning",
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Verify node_modules was created
|
|
293
|
+
const nmExists = existsSync(join(extDir, "node_modules"));
|
|
294
|
+
log(` node_modules exists after install: ${nmExists}`);
|
|
295
|
+
if (nmExists) {
|
|
296
|
+
try {
|
|
297
|
+
const nmContents = readdirSync(join(extDir, "node_modules"));
|
|
298
|
+
log(` node_modules top-level: ${nmContents.join(", ")}`);
|
|
299
|
+
} catch { /* ignore */ }
|
|
300
|
+
}
|
|
301
|
+
|
|
199
302
|
s.stop(
|
|
200
303
|
installedVersion
|
|
201
304
|
? `supipowers updated to v${VERSION} (${platformDir})`
|
|
202
305
|
: `supipowers v${VERSION} installed (${platformDir})`,
|
|
203
306
|
);
|
|
204
307
|
} catch (err: unknown) {
|
|
308
|
+
log(` installToPlatform FAILED: ${err instanceof Error ? err.stack : String(err)}`);
|
|
205
309
|
s.stop(`${action} failed (${platformDir})`);
|
|
206
310
|
const message = err instanceof Error ? err.message : `Failed to copy files to ~/${platformDir}/agent/`;
|
|
207
311
|
bail(message);
|
|
@@ -358,9 +462,14 @@ async function main(): Promise<void> {
|
|
|
358
462
|
const piBin = findPiBinary();
|
|
359
463
|
const ompBin = findOmpBinary();
|
|
360
464
|
|
|
465
|
+
log(`findPiBinary() => ${piBin ?? "null"}`);
|
|
466
|
+
log(`findOmpBinary() => ${ompBin ?? "null"}`);
|
|
467
|
+
|
|
361
468
|
const piVer = piBin ? run(piBin, ["--version"]).stdout?.trim() || "unknown" : null;
|
|
362
469
|
const ompVer = ompBin ? run(ompBin, ["--version"]).stdout?.trim() || "unknown" : null;
|
|
363
470
|
|
|
471
|
+
log(`piVer: ${piVer ?? "N/A"}, ompVer: ${ompVer ?? "N/A"}`);
|
|
472
|
+
|
|
364
473
|
const detected: string[] = [];
|
|
365
474
|
if (piBin) detected.push(`Pi ${piVer}`);
|
|
366
475
|
if (ompBin) detected.push(`OMP ${ompVer}`);
|
|
@@ -429,6 +538,8 @@ async function main(): Promise<void> {
|
|
|
429
538
|
// ── Step 3: Install supipowers to each chosen target ──────
|
|
430
539
|
|
|
431
540
|
const packageRoot = resolve(__dirname, "..");
|
|
541
|
+
log(`packageRoot: ${packageRoot}`);
|
|
542
|
+
log(`targets: ${JSON.stringify(targets)}`);
|
|
432
543
|
|
|
433
544
|
for (const target of targets) {
|
|
434
545
|
installToPlatform(target.dir, packageRoot);
|
|
@@ -437,6 +548,10 @@ async function main(): Promise<void> {
|
|
|
437
548
|
await installContextMode(target.dir);
|
|
438
549
|
}
|
|
439
550
|
|
|
551
|
+
if (DEBUG) {
|
|
552
|
+
note(`Debug log written to:\n${LOG_FILE}`, "Debug");
|
|
553
|
+
}
|
|
554
|
+
|
|
440
555
|
// ── Step 4: Unified dependency check (--skip-deps to skip) ──
|
|
441
556
|
|
|
442
557
|
if (skipDeps) {
|
package/bin/local-install.sh
CHANGED
|
@@ -2,38 +2,42 @@
|
|
|
2
2
|
# Install supipowers locally from the current working tree.
|
|
3
3
|
# Usage: ./bin/local-install.sh
|
|
4
4
|
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# Re-run after pulling new changes
|
|
5
|
+
# Creates a global symlink so `supipowers` CLI works, then runs the
|
|
6
|
+
# installer with --debug to deploy the extension and write a log file.
|
|
7
|
+
# Re-run after pulling new changes.
|
|
8
8
|
|
|
9
9
|
set -euo pipefail
|
|
10
10
|
|
|
11
11
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
12
12
|
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
13
13
|
|
|
14
|
-
echo "
|
|
14
|
+
echo "-> Installing supipowers locally from $PROJECT_DIR"
|
|
15
15
|
|
|
16
16
|
# 1. Install dependencies (fast no-op when lock is current)
|
|
17
|
-
echo "
|
|
17
|
+
echo "-> Installing dependencies..."
|
|
18
18
|
cd "$PROJECT_DIR"
|
|
19
19
|
bun install --frozen-lockfile 2>/dev/null || bun install
|
|
20
20
|
|
|
21
21
|
# 2. Create a global symlink via bun link
|
|
22
|
-
|
|
23
|
-
# and Pi/OMP can resolve it by name.
|
|
24
|
-
echo "→ Linking supipowers globally…"
|
|
22
|
+
echo "-> Linking supipowers globally..."
|
|
25
23
|
bun link
|
|
26
24
|
|
|
27
25
|
# 3. Verify the link
|
|
28
26
|
if command -v supipowers &>/dev/null; then
|
|
29
|
-
echo "
|
|
27
|
+
echo "[OK] 'supipowers' CLI is available at $(which supipowers)"
|
|
30
28
|
else
|
|
31
|
-
echo "
|
|
29
|
+
echo "[WARN] CLI not on PATH -- you may need to add bun's global bin to \$PATH:"
|
|
32
30
|
echo " export PATH=\"\$HOME/.bun/bin:\$PATH\""
|
|
33
31
|
fi
|
|
34
32
|
|
|
35
|
-
# 4.
|
|
33
|
+
# 4. Run the installer with --debug to deploy extension + write log
|
|
34
|
+
echo "-> Running installer (--debug mode)..."
|
|
35
|
+
bun run "$PROJECT_DIR/bin/install.ts" --debug --force
|
|
36
|
+
|
|
37
|
+
# 5. Show version and log location
|
|
36
38
|
VERSION=$(node -e "console.log(require('$PROJECT_DIR/package.json').version)")
|
|
37
39
|
echo ""
|
|
38
|
-
echo "
|
|
39
|
-
|
|
40
|
+
echo "[OK] supipowers v${VERSION} installed locally"
|
|
41
|
+
if [ -f "$PROJECT_DIR/supipowers-install.log" ]; then
|
|
42
|
+
echo " Debug log: $PROJECT_DIR/supipowers-install.log"
|
|
43
|
+
fi
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "supipowers",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.5",
|
|
4
4
|
"description": "Workflow extension for OMP coding agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -43,17 +43,26 @@
|
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"@oh-my-pi/pi-coding-agent": "*",
|
|
46
|
+
"@oh-my-pi/pi-ai": "*",
|
|
47
|
+
"@oh-my-pi/pi-tui": "*",
|
|
46
48
|
"@sinclair/typebox": "*"
|
|
47
49
|
},
|
|
48
50
|
"peerDependenciesMeta": {
|
|
49
51
|
"@oh-my-pi/pi-coding-agent": {
|
|
50
52
|
"optional": true
|
|
51
53
|
},
|
|
54
|
+
"@oh-my-pi/pi-ai": {
|
|
55
|
+
"optional": true
|
|
56
|
+
},
|
|
52
57
|
"@oh-my-pi/pi-tui": {
|
|
53
58
|
"optional": true
|
|
59
|
+
},
|
|
60
|
+
"@sinclair/typebox": {
|
|
61
|
+
"optional": true
|
|
54
62
|
}
|
|
55
63
|
},
|
|
56
64
|
"devDependencies": {
|
|
65
|
+
"@oh-my-pi/pi-ai": "latest",
|
|
57
66
|
"@oh-my-pi/pi-coding-agent": "latest",
|
|
58
67
|
"@oh-my-pi/pi-tui": "latest",
|
|
59
68
|
"@sinclair/typebox": "^0.34.48",
|
package/src/deps/registry.ts
CHANGED
|
@@ -34,10 +34,10 @@ export async function checkBinary(
|
|
|
34
34
|
exec: ExecFn,
|
|
35
35
|
binary: string,
|
|
36
36
|
): Promise<{ installed: boolean; version?: string }> {
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
if (
|
|
37
|
+
// Bun.which() is cross-platform (handles .cmd/.exe/.bat on Windows)
|
|
38
|
+
// and doesn't require shelling out to `which` (Unix) or `where` (Windows).
|
|
39
|
+
const found = Bun.which(binary);
|
|
40
|
+
if (!found) return { installed: false };
|
|
41
41
|
|
|
42
42
|
const ver = await exec(binary, ["--version"]);
|
|
43
43
|
const version = ver.code === 0 ? ver.stdout.trim().split("\n")[0] : undefined;
|