supipowers 0.2.1 → 0.2.3
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 +54 -28
- package/package.json +1 -1
- package/src/commands/config.ts +43 -41
- package/src/commands/status.ts +28 -25
- package/src/commands/supi.ts +34 -30
- package/src/commands/update.ts +100 -0
- package/src/index.ts +33 -3
package/bin/install.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
note,
|
|
12
12
|
} from "@clack/prompts";
|
|
13
13
|
import { spawnSync } from "node:child_process";
|
|
14
|
-
import { readFileSync, existsSync, mkdirSync, cpSync, readdirSync } from "node:fs";
|
|
14
|
+
import { readFileSync, existsSync, mkdirSync, cpSync, rmSync, readdirSync } from "node:fs";
|
|
15
15
|
import { resolve, dirname, join } from "node:path";
|
|
16
16
|
import { fileURLToPath } from "node:url";
|
|
17
17
|
import { homedir } from "node:os";
|
|
@@ -130,39 +130,65 @@ async function main() {
|
|
|
130
130
|
ompSpinner.stop(`OMP ${ver} detected`);
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
// ── Step 2: Install supipowers into ~/.omp/agent/
|
|
134
|
-
|
|
135
|
-
const s = spinner();
|
|
136
|
-
s.start("Installing supipowers...");
|
|
133
|
+
// ── Step 2: Install / update supipowers into ~/.omp/agent/ ──
|
|
137
134
|
|
|
138
135
|
const packageRoot = resolve(__dirname, "..");
|
|
139
136
|
const ompAgent = join(homedir(), ".omp", "agent");
|
|
137
|
+
const extDir = join(ompAgent, "extensions", "supipowers");
|
|
138
|
+
const installedPkgPath = join(extDir, "package.json");
|
|
139
|
+
|
|
140
|
+
// Check for existing installation
|
|
141
|
+
let installedVersion = null;
|
|
142
|
+
if (existsSync(installedPkgPath)) {
|
|
143
|
+
try {
|
|
144
|
+
const installed = JSON.parse(readFileSync(installedPkgPath, "utf8"));
|
|
145
|
+
installedVersion = installed.version;
|
|
146
|
+
} catch {
|
|
147
|
+
// corrupted package.json — treat as not installed
|
|
148
|
+
}
|
|
149
|
+
}
|
|
140
150
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
// Copy skills → ~/.omp/agent/skills/<skillname>/SKILL.md
|
|
149
|
-
const skillsSource = join(packageRoot, "skills");
|
|
150
|
-
if (existsSync(skillsSource)) {
|
|
151
|
-
const skillDirs = readdirSync(skillsSource, { withFileTypes: true });
|
|
152
|
-
for (const entry of skillDirs) {
|
|
153
|
-
if (!entry.isDirectory()) continue;
|
|
154
|
-
const skillFile = join(skillsSource, entry.name, "SKILL.md");
|
|
155
|
-
if (!existsSync(skillFile)) continue;
|
|
156
|
-
const destDir = join(ompAgent, "skills", entry.name);
|
|
157
|
-
mkdirSync(destDir, { recursive: true });
|
|
158
|
-
cpSync(skillFile, join(destDir, "SKILL.md"));
|
|
159
|
-
}
|
|
151
|
+
if (installedVersion === VERSION) {
|
|
152
|
+
note(`supipowers v${VERSION} is already installed and up to date.`, "Up to date");
|
|
153
|
+
} else {
|
|
154
|
+
const action = installedVersion ? "Updating" : "Installing";
|
|
155
|
+
if (installedVersion) {
|
|
156
|
+
note(`v${installedVersion} → v${VERSION}`, "Updating supipowers");
|
|
160
157
|
}
|
|
161
158
|
|
|
162
|
-
s
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
159
|
+
const s = spinner();
|
|
160
|
+
s.start(`${action} supipowers...`);
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
// Clean previous installation to remove stale files
|
|
164
|
+
if (existsSync(extDir)) {
|
|
165
|
+
rmSync(extDir, { recursive: true });
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Copy extension (src/ + package.json) → ~/.omp/agent/extensions/supipowers/
|
|
169
|
+
mkdirSync(extDir, { recursive: true });
|
|
170
|
+
cpSync(join(packageRoot, "src"), join(extDir, "src"), { recursive: true });
|
|
171
|
+
cpSync(join(packageRoot, "package.json"), join(extDir, "package.json"));
|
|
172
|
+
|
|
173
|
+
// Copy skills → ~/.omp/agent/skills/<skillname>/SKILL.md
|
|
174
|
+
const skillsSource = join(packageRoot, "skills");
|
|
175
|
+
if (existsSync(skillsSource)) {
|
|
176
|
+
const skillDirs = readdirSync(skillsSource, { withFileTypes: true });
|
|
177
|
+
for (const entry of skillDirs) {
|
|
178
|
+
if (!entry.isDirectory()) continue;
|
|
179
|
+
const skillFile = join(skillsSource, entry.name, "SKILL.md");
|
|
180
|
+
if (!existsSync(skillFile)) continue;
|
|
181
|
+
const destDir = join(ompAgent, "skills", entry.name);
|
|
182
|
+
mkdirSync(destDir, { recursive: true });
|
|
183
|
+
cpSync(skillFile, join(destDir, "SKILL.md"));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
s.stop(installedVersion ? `supipowers updated to v${VERSION}` : `supipowers v${VERSION} installed`);
|
|
188
|
+
} catch (err) {
|
|
189
|
+
s.stop(`${action} failed`);
|
|
190
|
+
bail(err.message || "Failed to copy files to ~/.omp/agent/");
|
|
191
|
+
}
|
|
166
192
|
}
|
|
167
193
|
|
|
168
194
|
// ── Step 3: LSP setup (optional) ──────────────────────────
|
package/package.json
CHANGED
package/src/commands/config.ts
CHANGED
|
@@ -102,55 +102,57 @@ export function registerConfigCommand(pi: ExtensionAPI): void {
|
|
|
102
102
|
return;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
ctx.ui.setEditorText("");
|
|
106
|
+
void (async () => {
|
|
107
|
+
const settings = buildSettings(ctx.cwd);
|
|
106
108
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const config = loadConfig(ctx.cwd);
|
|
109
|
+
while (true) {
|
|
110
|
+
const config = loadConfig(ctx.cwd);
|
|
110
111
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
const options = settings.map(
|
|
113
|
+
(s) => `${s.label}: ${s.get(config)}`
|
|
114
|
+
);
|
|
115
|
+
options.push("Done");
|
|
115
116
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
const choice = await ctx.ui.select(
|
|
118
|
+
"Supipowers Settings",
|
|
119
|
+
options,
|
|
120
|
+
{ helpText: "Select a setting to change · Esc to close" },
|
|
121
|
+
);
|
|
121
122
|
|
|
122
|
-
|
|
123
|
+
if (choice === undefined || choice === "Done") break;
|
|
123
124
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
125
|
+
const index = options.indexOf(choice);
|
|
126
|
+
const setting = settings[index];
|
|
127
|
+
if (!setting) break;
|
|
127
128
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
129
|
+
if (setting.type === "select" && setting.options) {
|
|
130
|
+
const value = await ctx.ui.select(
|
|
131
|
+
setting.label,
|
|
132
|
+
setting.options,
|
|
133
|
+
{ initialIndex: setting.options.indexOf(setting.get(config)) },
|
|
134
|
+
);
|
|
135
|
+
if (value !== undefined) {
|
|
136
|
+
setting.set(ctx.cwd, value);
|
|
137
|
+
ctx.ui.notify(`${setting.label} → ${value}`, "info");
|
|
138
|
+
}
|
|
139
|
+
} else if (setting.type === "toggle") {
|
|
140
|
+
const current = setting.get(config);
|
|
141
|
+
const newValue = current === "on" ? "off" : "on";
|
|
142
|
+
setting.set(ctx.cwd, newValue);
|
|
143
|
+
ctx.ui.notify(`${setting.label} → ${newValue}`, "info");
|
|
144
|
+
} else if (setting.type === "text") {
|
|
145
|
+
const value = await ctx.ui.input(
|
|
146
|
+
setting.label,
|
|
147
|
+
setting.get(config) === "not set" ? undefined : setting.get(config),
|
|
148
|
+
);
|
|
149
|
+
if (value !== undefined) {
|
|
150
|
+
setting.set(ctx.cwd, value);
|
|
151
|
+
ctx.ui.notify(`${setting.label} → ${value || "cleared"}`, "info");
|
|
152
|
+
}
|
|
151
153
|
}
|
|
152
154
|
}
|
|
153
|
-
}
|
|
155
|
+
})();
|
|
154
156
|
},
|
|
155
157
|
});
|
|
156
158
|
}
|
package/src/commands/status.ts
CHANGED
|
@@ -5,39 +5,42 @@ export function registerStatusCommand(pi: ExtensionAPI): void {
|
|
|
5
5
|
pi.registerCommand("supi:status", {
|
|
6
6
|
description: "Check on running sub-agents and task progress",
|
|
7
7
|
async handler(_args, ctx) {
|
|
8
|
-
|
|
8
|
+
ctx.ui.setEditorText("");
|
|
9
9
|
|
|
10
|
+
const activeRun = findActiveRun(ctx.cwd);
|
|
10
11
|
if (!activeRun) {
|
|
11
12
|
ctx.ui.notify("No active runs — use /supi:run to execute a plan", "info");
|
|
12
13
|
return;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
void (async () => {
|
|
17
|
+
const results = loadAllAgentResults(ctx.cwd, activeRun.id);
|
|
18
|
+
const totalTasks = activeRun.batches.reduce(
|
|
19
|
+
(sum, b) => sum + b.taskIds.length,
|
|
20
|
+
0
|
|
21
|
+
);
|
|
22
|
+
const doneCount = results.filter((r) => r.status === "done").length;
|
|
23
|
+
const concernCount = results.filter((r) => r.status === "done_with_concerns").length;
|
|
24
|
+
const blockedCount = results.filter((r) => r.status === "blocked").length;
|
|
25
|
+
const currentBatch = activeRun.batches.find((b) => b.status !== "completed");
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
27
|
+
const options = [
|
|
28
|
+
`Run: ${activeRun.id}`,
|
|
29
|
+
`Status: ${activeRun.status}`,
|
|
30
|
+
`Plan: ${activeRun.planRef}`,
|
|
31
|
+
`Profile: ${activeRun.profile}`,
|
|
32
|
+
`Progress: ${results.length}/${totalTasks} tasks`,
|
|
33
|
+
` Done: ${doneCount}`,
|
|
34
|
+
` With concerns: ${concernCount}`,
|
|
35
|
+
` Blocked: ${blockedCount}`,
|
|
36
|
+
`Batch: ${currentBatch ? `#${currentBatch.index} (${currentBatch.status})` : "all complete"}`,
|
|
37
|
+
"Close",
|
|
38
|
+
];
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
await ctx.ui.select("Supipowers Status", options, {
|
|
41
|
+
helpText: "Esc to close",
|
|
42
|
+
});
|
|
43
|
+
})();
|
|
41
44
|
},
|
|
42
45
|
});
|
|
43
46
|
}
|
package/src/commands/supi.ts
CHANGED
|
@@ -8,41 +8,45 @@ export function registerSupiCommand(pi: ExtensionAPI): void {
|
|
|
8
8
|
pi.registerCommand("supi", {
|
|
9
9
|
description: "Supipowers overview — show available commands and project status",
|
|
10
10
|
async handler(_args, ctx) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
ctx.ui.setEditorText("");
|
|
12
|
+
void (async () => {
|
|
13
|
+
const config = loadConfig(ctx.cwd);
|
|
14
|
+
const activeRun = findActiveRun(ctx.cwd);
|
|
15
|
+
const latestReport = loadLatestReport(ctx.cwd);
|
|
16
|
+
const plans = listPlans(ctx.cwd);
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
const commands = [
|
|
19
|
+
"/supi:plan — Start collaborative planning",
|
|
20
|
+
"/supi:run — Execute a plan with sub-agents",
|
|
21
|
+
"/supi:review — Run quality gates",
|
|
22
|
+
"/supi:qa — Run QA pipeline",
|
|
23
|
+
"/supi:release — Release automation",
|
|
24
|
+
"/supi:config — Manage configuration",
|
|
25
|
+
"/supi:status — Check running tasks",
|
|
26
|
+
"/supi:update — Update to latest version",
|
|
27
|
+
];
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const status = [
|
|
30
|
+
`Profile: ${config.defaultProfile}`,
|
|
31
|
+
`Plans: ${plans.length}`,
|
|
32
|
+
`Active run: ${activeRun ? activeRun.id : "none"}`,
|
|
33
|
+
`Last review: ${latestReport ? `${latestReport.timestamp.slice(0, 10)} (${latestReport.passed ? "passed" : "failed"})` : "none"}`,
|
|
34
|
+
];
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
const choice = await ctx.ui.select(
|
|
37
|
+
"Supipowers",
|
|
38
|
+
[...commands, "", ...status, "", "Close"],
|
|
39
|
+
{ helpText: "Select a command to run · Esc to close" },
|
|
40
|
+
);
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
if (choice && choice.startsWith("/supi:")) {
|
|
43
|
+
const cmdName = choice.split(" ")[0].slice(1); // remove leading /
|
|
44
|
+
const cmd = pi.getCommands().find((c) => c.name === cmdName);
|
|
45
|
+
if (cmd) {
|
|
46
|
+
await cmd.handler("", ctx);
|
|
47
|
+
}
|
|
44
48
|
}
|
|
45
|
-
}
|
|
49
|
+
})();
|
|
46
50
|
},
|
|
47
51
|
});
|
|
48
52
|
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
|
|
2
|
+
import { readFileSync, existsSync, mkdirSync, cpSync, rmSync, readdirSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { homedir, tmpdir } from "node:os";
|
|
5
|
+
|
|
6
|
+
export function registerUpdateCommand(pi: ExtensionAPI): void {
|
|
7
|
+
pi.registerCommand("supi:update", {
|
|
8
|
+
description: "Update supipowers to the latest version",
|
|
9
|
+
async handler(_args, ctx) {
|
|
10
|
+
ctx.ui.setEditorText("");
|
|
11
|
+
void (async () => {
|
|
12
|
+
const ompAgent = join(homedir(), ".omp", "agent");
|
|
13
|
+
const extDir = join(ompAgent, "extensions", "supipowers");
|
|
14
|
+
const installedPkgPath = join(extDir, "package.json");
|
|
15
|
+
|
|
16
|
+
// Get current installed version
|
|
17
|
+
let currentVersion = "unknown";
|
|
18
|
+
if (existsSync(installedPkgPath)) {
|
|
19
|
+
try {
|
|
20
|
+
const pkg = JSON.parse(readFileSync(installedPkgPath, "utf8"));
|
|
21
|
+
currentVersion = pkg.version;
|
|
22
|
+
} catch {
|
|
23
|
+
// corrupted — will update anyway
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
ctx.ui.notify(`Current version: v${currentVersion}`, "info");
|
|
28
|
+
|
|
29
|
+
// Check latest version on npm
|
|
30
|
+
const checkResult = await pi.exec("npm", ["view", "supipowers", "version"], { cwd: tmpdir() });
|
|
31
|
+
if (checkResult.exitCode !== 0) {
|
|
32
|
+
ctx.ui.notify("Failed to check for updates — npm view failed", "error");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const latestVersion = checkResult.stdout.trim();
|
|
36
|
+
|
|
37
|
+
if (latestVersion === currentVersion) {
|
|
38
|
+
ctx.ui.notify(`supipowers v${currentVersion} is already up to date`, "info");
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
ctx.ui.notify(`Updating v${currentVersion} → v${latestVersion}...`, "info");
|
|
43
|
+
|
|
44
|
+
// Download latest to a temp directory
|
|
45
|
+
const tempDir = join(tmpdir(), `supipowers-update-${Date.now()}`);
|
|
46
|
+
mkdirSync(tempDir, { recursive: true });
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const installResult = await pi.exec(
|
|
50
|
+
"npm", ["install", "--prefix", tempDir, `supipowers@${latestVersion}`],
|
|
51
|
+
{ cwd: tempDir },
|
|
52
|
+
);
|
|
53
|
+
if (installResult.exitCode !== 0) {
|
|
54
|
+
ctx.ui.notify("Failed to download latest version", "error");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const downloadedRoot = join(tempDir, "node_modules", "supipowers");
|
|
59
|
+
if (!existsSync(downloadedRoot)) {
|
|
60
|
+
ctx.ui.notify("Downloaded package not found", "error");
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Clean previous installation
|
|
65
|
+
if (existsSync(extDir)) {
|
|
66
|
+
rmSync(extDir, { recursive: true });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Copy extension files
|
|
70
|
+
mkdirSync(extDir, { recursive: true });
|
|
71
|
+
cpSync(join(downloadedRoot, "src"), join(extDir, "src"), { recursive: true });
|
|
72
|
+
cpSync(join(downloadedRoot, "package.json"), join(extDir, "package.json"));
|
|
73
|
+
|
|
74
|
+
// Copy skills
|
|
75
|
+
const skillsSource = join(downloadedRoot, "skills");
|
|
76
|
+
if (existsSync(skillsSource)) {
|
|
77
|
+
const skillDirs = readdirSync(skillsSource, { withFileTypes: true });
|
|
78
|
+
for (const entry of skillDirs) {
|
|
79
|
+
if (!entry.isDirectory()) continue;
|
|
80
|
+
const skillFile = join(skillsSource, entry.name, "SKILL.md");
|
|
81
|
+
if (!existsSync(skillFile)) continue;
|
|
82
|
+
const destDir = join(ompAgent, "skills", entry.name);
|
|
83
|
+
mkdirSync(destDir, { recursive: true });
|
|
84
|
+
cpSync(skillFile, join(destDir, "SKILL.md"));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
ctx.ui.notify(`supipowers updated to v${latestVersion}`, "info");
|
|
89
|
+
} finally {
|
|
90
|
+
// Clean up temp directory
|
|
91
|
+
try {
|
|
92
|
+
rmSync(tempDir, { recursive: true });
|
|
93
|
+
} catch {
|
|
94
|
+
// best effort cleanup
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
})();
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
|
|
2
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { homedir, tmpdir } from "node:os";
|
|
2
5
|
import { registerSupiCommand } from "./commands/supi.js";
|
|
3
6
|
import { registerConfigCommand } from "./commands/config.js";
|
|
4
7
|
import { registerStatusCommand } from "./commands/status.js";
|
|
@@ -7,6 +10,17 @@ import { registerRunCommand } from "./commands/run.js";
|
|
|
7
10
|
import { registerReviewCommand } from "./commands/review.js";
|
|
8
11
|
import { registerQaCommand } from "./commands/qa.js";
|
|
9
12
|
import { registerReleaseCommand } from "./commands/release.js";
|
|
13
|
+
import { registerUpdateCommand } from "./commands/update.js";
|
|
14
|
+
|
|
15
|
+
function getInstalledVersion(): string | null {
|
|
16
|
+
const pkgPath = join(homedir(), ".omp", "agent", "extensions", "supipowers", "package.json");
|
|
17
|
+
if (!existsSync(pkgPath)) return null;
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(readFileSync(pkgPath, "utf8")).version;
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
10
24
|
|
|
11
25
|
export default function supipowers(pi: ExtensionAPI): void {
|
|
12
26
|
// Register all commands
|
|
@@ -18,11 +32,27 @@ export default function supipowers(pi: ExtensionAPI): void {
|
|
|
18
32
|
registerReviewCommand(pi);
|
|
19
33
|
registerQaCommand(pi);
|
|
20
34
|
registerReleaseCommand(pi);
|
|
35
|
+
registerUpdateCommand(pi);
|
|
21
36
|
|
|
22
37
|
// Session start
|
|
23
38
|
pi.on("session_start", async (_event, ctx) => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
39
|
+
// Check for updates in the background
|
|
40
|
+
const currentVersion = getInstalledVersion();
|
|
41
|
+
if (!currentVersion) return;
|
|
42
|
+
|
|
43
|
+
pi.exec("npm", ["view", "supipowers", "version"], { cwd: tmpdir() })
|
|
44
|
+
.then((result) => {
|
|
45
|
+
if (result.exitCode !== 0) return;
|
|
46
|
+
const latest = result.stdout.trim();
|
|
47
|
+
if (latest && latest !== currentVersion) {
|
|
48
|
+
ctx.ui.notify(
|
|
49
|
+
`supipowers v${latest} available (current: v${currentVersion}). Run /supi:update`,
|
|
50
|
+
"info",
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
.catch(() => {
|
|
55
|
+
// Network error — silently ignore
|
|
56
|
+
});
|
|
27
57
|
});
|
|
28
58
|
}
|