supipowers 0.1.0 → 0.1.4

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 CHANGED
@@ -12,8 +12,8 @@ import {
12
12
  note,
13
13
  } from "@clack/prompts";
14
14
  import { spawnSync } from "node:child_process";
15
- import { readdirSync, readFileSync, existsSync } from "node:fs";
16
- import { resolve, extname, dirname, join } from "node:path";
15
+ import { readFileSync, existsSync } from "node:fs";
16
+ import { resolve, dirname, join } from "node:path";
17
17
  import { fileURLToPath } from "node:url";
18
18
  import { homedir } from "node:os";
19
19
 
@@ -59,53 +59,29 @@ function findOmpBinary() {
59
59
  const LSP_SERVERS = [
60
60
  {
61
61
  language: "TypeScript / JavaScript",
62
- extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"],
63
62
  server: "typescript-language-server",
64
63
  installCmd: "bun add -g typescript-language-server typescript",
65
64
  },
66
65
  {
67
66
  language: "Python",
68
- extensions: [".py"],
69
67
  server: "pyright",
70
68
  installCmd: "pip install pyright",
71
69
  },
72
70
  {
73
71
  language: "Rust",
74
- extensions: [".rs"],
75
72
  server: "rust-analyzer",
76
73
  installCmd: "rustup component add rust-analyzer",
77
74
  },
78
75
  {
79
76
  language: "Go",
80
- extensions: [".go"],
81
77
  server: "gopls",
82
78
  installCmd: "go install golang.org/x/tools/gopls@latest",
83
79
  },
84
80
  ];
85
81
 
86
- function detectLanguages(dir) {
87
- const detected = new Set();
88
- try {
89
- const entries = readdirSync(dir, { withFileTypes: true });
90
- for (const entry of entries) {
91
- if (entry.isFile()) {
92
- detected.add(extname(entry.name));
93
- } else if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
94
- // One level deep
95
- try {
96
- const subEntries = readdirSync(join(dir, entry.name), { withFileTypes: true });
97
- for (const sub of subEntries) {
98
- if (sub.isFile()) detected.add(extname(sub.name));
99
- }
100
- } catch {
101
- // skip unreadable dirs
102
- }
103
- }
104
- }
105
- } catch {
106
- // skip
107
- }
108
- return detected;
82
+ function isInstalled(binary) {
83
+ const result = run("which", [binary]);
84
+ return result.status === 0;
109
85
  }
110
86
 
111
87
  // ── Main ─────────────────────────────────────────────────────
@@ -115,9 +91,12 @@ async function main() {
115
91
 
116
92
  // ── Step 1: OMP check ──────────────────────────────────────
117
93
 
94
+ const ompSpinner = spinner();
95
+ ompSpinner.start("Looking for OMP...");
118
96
  let omp = findOmpBinary();
119
97
 
120
98
  if (!omp) {
99
+ ompSpinner.stop("OMP not found");
121
100
  note(
122
101
  "OMP (oh-my-pi) is an AI coding agent that supipowers extends.\n" +
123
102
  "It adds sub-agents, LSP integration, and plugin support to pi.\n" +
@@ -149,7 +128,7 @@ async function main() {
149
128
  } else {
150
129
  const version = run(omp, ["--version"]);
151
130
  const ver = version.stdout?.trim() || "unknown";
152
- note(`OMP ${ver}`, "OMP detected");
131
+ ompSpinner.stop(`OMP ${ver} detected`);
153
132
  }
154
133
 
155
134
  // ── Step 2: Install supipowers ─────────────────────────────
@@ -178,33 +157,39 @@ async function main() {
178
157
 
179
158
  // ── Step 3: LSP setup (optional) ──────────────────────────
180
159
 
181
- const detectedExts = detectLanguages(process.cwd());
182
- const matchingServers = LSP_SERVERS.filter((srv) =>
183
- srv.extensions.some((ext) => detectedExts.has(ext))
184
- );
185
-
186
- if (matchingServers.length > 0) {
187
- const selected = await multiselect({
188
- message: "Detected project languages. Install LSP servers for better code intelligence?",
189
- options: matchingServers.map((srv) => ({
190
- value: srv,
191
- label: srv.language,
192
- hint: srv.server,
193
- })),
194
- required: false,
195
- });
160
+ const lspSpinner = spinner();
161
+ lspSpinner.start("Checking installed LSP servers...");
162
+ const lspOptions = LSP_SERVERS.map((srv) => {
163
+ const installed = isInstalled(srv.server);
164
+ return {
165
+ value: srv,
166
+ label: srv.language,
167
+ hint: installed ? `${srv.server} (installed)` : srv.server,
168
+ };
169
+ });
170
+ const installedCount = lspOptions.filter((o) => o.hint.includes("(installed)")).length;
171
+ lspSpinner.stop(`Found ${installedCount}/${LSP_SERVERS.length} LSP servers installed`);
172
+
173
+ const selected = await multiselect({
174
+ message: "Install LSP servers for better code intelligence?",
175
+ options: lspOptions,
176
+ required: false,
177
+ });
196
178
 
197
- if (!isCancel(selected) && selected.length > 0) {
198
- for (const srv of selected) {
199
- const ls = spinner();
200
- ls.start(`Installing ${srv.server}...`);
201
- const [cmd, ...args] = srv.installCmd.split(" ");
202
- const r = run(cmd, args);
203
- if (r.status !== 0) {
204
- ls.stop(`Failed to install ${srv.server} — you can install manually: ${srv.installCmd}`);
205
- } else {
206
- ls.stop(`${srv.server} installed`);
207
- }
179
+ if (!isCancel(selected) && selected.length > 0) {
180
+ for (const srv of selected) {
181
+ if (isInstalled(srv.server)) {
182
+ note(`${srv.server} is already installed, skipping.`, srv.language);
183
+ continue;
184
+ }
185
+ const ls = spinner();
186
+ ls.start(`Installing ${srv.server}...`);
187
+ const [cmd, ...args] = srv.installCmd.split(" ");
188
+ const r = run(cmd, args);
189
+ if (r.status !== 0) {
190
+ ls.stop(`Failed to install ${srv.server} — you can install manually: ${srv.installCmd}`);
191
+ } else {
192
+ ls.stop(`${srv.server} installed`);
208
193
  }
209
194
  }
210
195
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supipowers",
3
- "version": "0.1.0",
3
+ "version": "0.1.4",
4
4
  "description": "OMP-native workflow extension inspired by Superpowers.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -21,7 +21,7 @@ export function registerConfigCommand(pi: ExtensionAPI): void {
21
21
  `Max fix retries: ${config.orchestration.maxFixRetries}`,
22
22
  `Max nesting depth: ${config.orchestration.maxNestingDepth}`,
23
23
  `Model preference: ${config.orchestration.modelPreference}`,
24
- `LSP auto-detect: ${config.lsp.autoDetect}`,
24
+ `LSP setup guide: ${config.lsp.setupGuide}`,
25
25
  `Notification verbosity: ${config.notifications.verbosity}`,
26
26
  `QA framework: ${config.qa.framework ?? "not detected"}`,
27
27
  `Release pipeline: ${config.release.pipeline ?? "not configured"}`,
@@ -11,7 +11,6 @@ export const DEFAULT_CONFIG: SupipowersConfig = {
11
11
  modelPreference: "auto",
12
12
  },
13
13
  lsp: {
14
- autoDetect: true,
15
14
  setupGuide: true,
16
15
  },
17
16
  notifications: {
@@ -13,7 +13,6 @@ const ConfigSchema = Type.Object({
13
13
  modelPreference: Type.String(),
14
14
  }),
15
15
  lsp: Type.Object({
16
- autoDetect: Type.Boolean(),
17
16
  setupGuide: Type.Boolean(),
18
17
  }),
19
18
  notifications: Type.Object({
@@ -1,36 +1,31 @@
1
1
  // src/lsp/detector.ts
2
- import type { SupipowersConfig } from "../types.js";
2
+ import { LSP_SERVERS, type LspServerEntry } from "./setup-guide.js";
3
3
 
4
- export interface LspStatus {
5
- available: boolean;
6
- servers: LspServerInfo[];
7
- }
8
-
9
- export interface LspServerInfo {
10
- name: string;
11
- status: "running" | "stopped" | "error";
12
- fileTypes: string[];
13
- error?: string;
4
+ export interface LspServerStatus {
5
+ server: LspServerEntry;
6
+ installed: boolean;
14
7
  }
15
8
 
16
9
  /**
17
- * Check LSP availability by invoking the lsp tool's "status" action.
18
- * Uses pi.exec to call the lsp tool programmatically.
10
+ * Check which LSP servers are installed by looking for their binaries.
19
11
  */
20
- export async function detectLsp(
12
+ export async function checkInstalledServers(
21
13
  exec: (cmd: string, args: string[]) => Promise<{ stdout: string; exitCode: number }>
22
- ): Promise<LspStatus> {
23
- try {
24
- // We check by looking for LSP config files or running servers
25
- // In OMP, LSP is a built-in tool — we check if it's in active tools
26
- return { available: false, servers: [] };
27
- } catch {
28
- return { available: false, servers: [] };
14
+ ): Promise<LspServerStatus[]> {
15
+ const results: LspServerStatus[] = [];
16
+ for (const server of LSP_SERVERS) {
17
+ try {
18
+ const result = await exec("which", [server.server]);
19
+ results.push({ server, installed: result.exitCode === 0 });
20
+ } catch {
21
+ results.push({ server, installed: false });
22
+ }
29
23
  }
24
+ return results;
30
25
  }
31
26
 
32
27
  /**
33
- * Check if LSP is available from the extension context.
28
+ * Check if LSP is available from the OMP extension context at runtime.
34
29
  * Reads the active tools list to see if "lsp" is registered.
35
30
  */
36
31
  export function isLspAvailable(activeTools: string[]): boolean {
@@ -1,13 +1,13 @@
1
1
  // src/lsp/setup-guide.ts
2
2
 
3
- export interface SetupInstruction {
3
+ export interface LspServerEntry {
4
4
  language: string;
5
5
  server: string;
6
6
  installCommand: string;
7
7
  notes: string;
8
8
  }
9
9
 
10
- const COMMON_LSP_SERVERS: SetupInstruction[] = [
10
+ export const LSP_SERVERS: LspServerEntry[] = [
11
11
  {
12
12
  language: "TypeScript/JavaScript",
13
13
  server: "typescript-language-server",
@@ -34,47 +34,16 @@ const COMMON_LSP_SERVERS: SetupInstruction[] = [
34
34
  },
35
35
  ];
36
36
 
37
- /** Get setup instructions for detected project languages */
38
- export function getSetupInstructions(detectedLanguages: string[]): SetupInstruction[] {
39
- return COMMON_LSP_SERVERS.filter((s) =>
40
- detectedLanguages.some((lang) =>
41
- s.language.toLowerCase().includes(lang.toLowerCase())
42
- )
43
- );
44
- }
45
-
46
- /** Detect project languages from file extensions */
47
- export function detectProjectLanguages(files: string[]): string[] {
48
- const extMap: Record<string, string> = {
49
- ".ts": "typescript",
50
- ".tsx": "typescript",
51
- ".js": "javascript",
52
- ".jsx": "javascript",
53
- ".py": "python",
54
- ".rs": "rust",
55
- ".go": "go",
56
- ".java": "java",
57
- ".rb": "ruby",
58
- ".php": "php",
59
- };
60
- const languages = new Set<string>();
61
- for (const file of files) {
62
- const ext = file.slice(file.lastIndexOf("."));
63
- if (extMap[ext]) languages.add(extMap[ext]);
64
- }
65
- return [...languages];
66
- }
67
-
68
- /** Format setup instructions as readable text */
69
- export function formatSetupGuide(instructions: SetupInstruction[]): string {
70
- if (instructions.length === 0) {
71
- return "No LSP setup instructions available for your project languages.";
37
+ /** Format all LSP servers as readable text */
38
+ export function formatSetupGuide(servers: LspServerEntry[] = LSP_SERVERS): string {
39
+ if (servers.length === 0) {
40
+ return "No LSP servers available.";
72
41
  }
73
42
  const lines = ["LSP Setup Guide:", ""];
74
- for (const inst of instructions) {
75
- lines.push(`## ${inst.language} — ${inst.server}`);
76
- lines.push(`Install: ${inst.installCommand}`);
77
- lines.push(`Note: ${inst.notes}`);
43
+ for (const srv of servers) {
44
+ lines.push(`## ${srv.language} — ${srv.server}`);
45
+ lines.push(`Install: ${srv.installCommand}`);
46
+ lines.push(`Note: ${srv.notes}`);
78
47
  lines.push("");
79
48
  }
80
49
  return lines.join("\n");
package/src/types.ts CHANGED
@@ -110,7 +110,6 @@ export interface SupipowersConfig {
110
110
  modelPreference: string;
111
111
  };
112
112
  lsp: {
113
- autoDetect: boolean;
114
113
  setupGuide: boolean;
115
114
  };
116
115
  notifications: {