@oh-my-pi/cli 0.3.0 → 0.5.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.
Files changed (108) hide show
  1. package/README.md +79 -84
  2. package/dist/cli.js +5025 -1016
  3. package/dist/commands/config.d.ts +27 -0
  4. package/dist/commands/config.d.ts.map +1 -1
  5. package/dist/commands/create.d.ts.map +1 -1
  6. package/dist/commands/doctor.d.ts +2 -0
  7. package/dist/commands/doctor.d.ts.map +1 -1
  8. package/dist/commands/env.d.ts.map +1 -1
  9. package/dist/commands/features.d.ts.map +1 -1
  10. package/dist/commands/info.d.ts.map +1 -1
  11. package/dist/commands/init.d.ts.map +1 -1
  12. package/dist/commands/install.d.ts +6 -0
  13. package/dist/commands/install.d.ts.map +1 -1
  14. package/dist/commands/link.d.ts +1 -0
  15. package/dist/commands/link.d.ts.map +1 -1
  16. package/dist/commands/list.d.ts.map +1 -1
  17. package/dist/commands/outdated.d.ts.map +1 -1
  18. package/dist/commands/search.d.ts.map +1 -1
  19. package/dist/commands/uninstall.d.ts +3 -0
  20. package/dist/commands/uninstall.d.ts.map +1 -1
  21. package/dist/commands/update.d.ts +1 -0
  22. package/dist/commands/update.d.ts.map +1 -1
  23. package/dist/commands/why.d.ts.map +1 -1
  24. package/dist/conflicts.d.ts +7 -2
  25. package/dist/conflicts.d.ts.map +1 -1
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/lock.d.ts.map +1 -1
  29. package/dist/lockfile.d.ts +24 -3
  30. package/dist/lockfile.d.ts.map +1 -1
  31. package/dist/manifest.d.ts +12 -1
  32. package/dist/manifest.d.ts.map +1 -1
  33. package/dist/npm.d.ts +11 -0
  34. package/dist/npm.d.ts.map +1 -1
  35. package/dist/output.d.ts +51 -0
  36. package/dist/output.d.ts.map +1 -0
  37. package/dist/paths.d.ts +5 -0
  38. package/dist/paths.d.ts.map +1 -1
  39. package/dist/progress.d.ts +78 -0
  40. package/dist/progress.d.ts.map +1 -0
  41. package/dist/runtime.d.ts.map +1 -1
  42. package/dist/symlinks.d.ts +1 -0
  43. package/dist/symlinks.d.ts.map +1 -1
  44. package/package.json +24 -10
  45. package/.github/icon.png +0 -0
  46. package/.github/logo.png +0 -0
  47. package/.github/workflows/ci.yml +0 -32
  48. package/.github/workflows/publish.yml +0 -42
  49. package/biome.json +0 -29
  50. package/bun.lock +0 -109
  51. package/plugins/exa/README.md +0 -153
  52. package/plugins/exa/package.json +0 -56
  53. package/plugins/exa/tools/exa/company.ts +0 -35
  54. package/plugins/exa/tools/exa/index.ts +0 -66
  55. package/plugins/exa/tools/exa/linkedin.ts +0 -35
  56. package/plugins/exa/tools/exa/researcher.ts +0 -40
  57. package/plugins/exa/tools/exa/runtime.json +0 -4
  58. package/plugins/exa/tools/exa/search.ts +0 -46
  59. package/plugins/exa/tools/exa/shared.ts +0 -230
  60. package/plugins/exa/tools/exa/websets.ts +0 -62
  61. package/plugins/metal-theme/README.md +0 -13
  62. package/plugins/metal-theme/omp.json +0 -8
  63. package/plugins/metal-theme/package.json +0 -19
  64. package/plugins/metal-theme/themes/metal.json +0 -79
  65. package/plugins/subagents/README.md +0 -25
  66. package/plugins/subagents/agents/explore.md +0 -71
  67. package/plugins/subagents/agents/planner.md +0 -51
  68. package/plugins/subagents/agents/reviewer.md +0 -53
  69. package/plugins/subagents/agents/task.md +0 -46
  70. package/plugins/subagents/commands/architect-plan.md +0 -9
  71. package/plugins/subagents/commands/implement-with-critic.md +0 -10
  72. package/plugins/subagents/commands/implement.md +0 -10
  73. package/plugins/subagents/omp.json +0 -15
  74. package/plugins/subagents/package.json +0 -26
  75. package/plugins/subagents/tools/task/index.ts +0 -1019
  76. package/plugins/user-prompt/README.md +0 -130
  77. package/plugins/user-prompt/package.json +0 -19
  78. package/plugins/user-prompt/tools/user-prompt/index.ts +0 -235
  79. package/scripts/bump-version.sh +0 -52
  80. package/scripts/publish.sh +0 -35
  81. package/src/cli.ts +0 -242
  82. package/src/commands/config.ts +0 -384
  83. package/src/commands/create.ts +0 -203
  84. package/src/commands/doctor.ts +0 -305
  85. package/src/commands/enable.ts +0 -122
  86. package/src/commands/env.ts +0 -38
  87. package/src/commands/features.ts +0 -295
  88. package/src/commands/info.ts +0 -120
  89. package/src/commands/init.ts +0 -60
  90. package/src/commands/install.ts +0 -700
  91. package/src/commands/link.ts +0 -159
  92. package/src/commands/list.ts +0 -186
  93. package/src/commands/outdated.ts +0 -87
  94. package/src/commands/search.ts +0 -77
  95. package/src/commands/uninstall.ts +0 -124
  96. package/src/commands/update.ts +0 -170
  97. package/src/commands/why.ts +0 -136
  98. package/src/conflicts.ts +0 -116
  99. package/src/errors.ts +0 -22
  100. package/src/index.ts +0 -46
  101. package/src/lock.ts +0 -46
  102. package/src/lockfile.ts +0 -132
  103. package/src/manifest.ts +0 -360
  104. package/src/npm.ts +0 -206
  105. package/src/paths.ts +0 -137
  106. package/src/runtime.ts +0 -116
  107. package/src/symlinks.ts +0 -455
  108. package/tsconfig.json +0 -28
package/src/manifest.ts DELETED
@@ -1,360 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import { mkdir, readFile, writeFile } from "node:fs/promises";
3
- import { dirname, join } from "node:path";
4
- import {
5
- GLOBAL_PACKAGE_JSON,
6
- NODE_MODULES_DIR,
7
- PLUGINS_DIR,
8
- PROJECT_PACKAGE_JSON,
9
- PROJECT_PLUGINS_JSON,
10
- } from "@omp/paths";
11
-
12
- /**
13
- * Format permission-related errors with actionable guidance
14
- */
15
- function formatPermissionError(err: NodeJS.ErrnoException, path: string): string {
16
- if (err.code === "EACCES" || err.code === "EPERM") {
17
- return `Permission denied: Cannot write to ${path}. Check directory permissions or run with appropriate privileges.`;
18
- }
19
- return err.message;
20
- }
21
-
22
- /**
23
- * OMP field in package.json - defines what files to install
24
- */
25
- export interface OmpInstallEntry {
26
- src: string;
27
- dest: string;
28
- /** If true, this file is copied (not symlinked) and can be edited by omp */
29
- copy?: boolean;
30
- }
31
-
32
- /**
33
- * Runtime configuration stored in plugin's runtime.json
34
- * This file is copied (not symlinked) and edited by omp features/config commands
35
- */
36
- export interface PluginRuntimeConfig {
37
- features?: string[];
38
- options?: Record<string, unknown>;
39
- }
40
-
41
- /**
42
- * Runtime variable definition with type, default, and metadata
43
- */
44
- export interface OmpVariable {
45
- type: "string" | "number" | "boolean" | "string[]";
46
- default?: string | number | boolean | string[];
47
- description?: string;
48
- required?: boolean;
49
- /** Environment variable name if injected as env (e.g., "EXA_API_KEY") */
50
- env?: string;
51
- }
52
-
53
- /**
54
- * Feature definition - metadata only, no install entries
55
- * All feature files are always installed; runtime.json controls which are active
56
- */
57
- export interface OmpFeature {
58
- description?: string;
59
- /** Runtime variables specific to this feature */
60
- variables?: Record<string, OmpVariable>;
61
- /** Default enabled state (default: true) */
62
- default?: boolean;
63
- }
64
-
65
- export interface OmpField {
66
- /** Top-level install entries (always installed, not feature-gated) */
67
- install?: OmpInstallEntry[];
68
- /** Top-level runtime variables (always available) */
69
- variables?: Record<string, OmpVariable>;
70
- /** Named features with their own install entries and variables */
71
- features?: Record<string, OmpFeature>;
72
- /** Disabled state (managed by omp, not plugin author) */
73
- disabled?: boolean;
74
- }
75
-
76
- /**
77
- * Package.json structure for plugins
78
- */
79
- export interface PluginPackageJson {
80
- name: string;
81
- version: string;
82
- description?: string;
83
- keywords?: string[];
84
- omp?: OmpField;
85
- dependencies?: Record<string, string>;
86
- devDependencies?: Record<string, string>;
87
- files?: string[];
88
- }
89
-
90
- /**
91
- * Per-plugin configuration stored in plugins.json
92
- */
93
- export interface PluginConfig {
94
- /**
95
- * Enabled feature names:
96
- * - null/undefined: use plugin defaults (first install = all, reinstall = preserve)
97
- * - ["*"]: explicitly all features
98
- * - []: no optional features (core only)
99
- * - ["f1", "f2"]: specific features
100
- */
101
- features?: string[] | null;
102
- /** Runtime variable overrides */
103
- variables?: Record<string, string | number | boolean | string[]>;
104
- }
105
-
106
- /**
107
- * Global/project plugins.json structure
108
- */
109
- export interface PluginsJson {
110
- plugins: Record<string, string>; // name -> version specifier
111
- devDependencies?: Record<string, string>; // dev dependencies
112
- disabled?: string[]; // disabled plugin names
113
- /** Per-plugin feature and variable config */
114
- config?: Record<string, PluginConfig>;
115
- }
116
-
117
- /**
118
- * Initialize the global plugins directory with package.json
119
- */
120
- export async function initGlobalPlugins(): Promise<void> {
121
- try {
122
- await mkdir(PLUGINS_DIR, { recursive: true });
123
-
124
- if (!existsSync(GLOBAL_PACKAGE_JSON)) {
125
- const packageJson = {
126
- name: "pi-plugins",
127
- version: "1.0.0",
128
- private: true,
129
- description: "Global pi plugins managed by omp",
130
- dependencies: {},
131
- };
132
- await writeFile(GLOBAL_PACKAGE_JSON, JSON.stringify(packageJson, null, 2));
133
- }
134
- } catch (err) {
135
- const error = err as NodeJS.ErrnoException;
136
- if (error.code === "EACCES" || error.code === "EPERM") {
137
- throw new Error(formatPermissionError(error, PLUGINS_DIR));
138
- }
139
- throw err;
140
- }
141
- }
142
-
143
- /**
144
- * Initialize the project-local .pi directory with plugins.json and package.json
145
- */
146
- export async function initProjectPlugins(): Promise<void> {
147
- const PROJECT_PI_DIR = dirname(PROJECT_PLUGINS_JSON);
148
- try {
149
- await mkdir(PROJECT_PI_DIR, { recursive: true });
150
-
151
- // Create plugins.json if it doesn't exist
152
- if (!existsSync(PROJECT_PLUGINS_JSON)) {
153
- const pluginsJson = {
154
- plugins: {},
155
- };
156
- await writeFile(PROJECT_PLUGINS_JSON, JSON.stringify(pluginsJson, null, 2));
157
- }
158
-
159
- // Create package.json if it doesn't exist (for npm operations)
160
- if (!existsSync(PROJECT_PACKAGE_JSON)) {
161
- const packageJson = {
162
- name: "pi-project-plugins",
163
- version: "1.0.0",
164
- private: true,
165
- description: "Project-local pi plugins managed by omp",
166
- dependencies: {},
167
- };
168
- await writeFile(PROJECT_PACKAGE_JSON, JSON.stringify(packageJson, null, 2));
169
- }
170
- } catch (err) {
171
- const error = err as NodeJS.ErrnoException;
172
- if (error.code === "EACCES" || error.code === "EPERM") {
173
- throw new Error(formatPermissionError(error, PROJECT_PI_DIR));
174
- }
175
- throw err;
176
- }
177
- }
178
-
179
- /**
180
- * Load plugins.json (global or project)
181
- */
182
- export async function loadPluginsJson(global = true): Promise<PluginsJson> {
183
- const path = global ? GLOBAL_PACKAGE_JSON : PROJECT_PLUGINS_JSON;
184
-
185
- try {
186
- const data = await readFile(path, "utf-8");
187
- const parsed = JSON.parse(data);
188
-
189
- if (global) {
190
- // Global uses standard package.json format
191
- return {
192
- plugins: parsed.dependencies || {},
193
- devDependencies: parsed.devDependencies || {},
194
- disabled: parsed.omp?.disabled || [],
195
- config: parsed.omp?.config || {},
196
- };
197
- }
198
-
199
- // Project uses plugins.json format
200
- return {
201
- plugins: parsed.plugins || {},
202
- devDependencies: parsed.devDependencies || {},
203
- disabled: parsed.disabled || [],
204
- config: parsed.config || {},
205
- };
206
- } catch (err) {
207
- if ((err as NodeJS.ErrnoException).code === "ENOENT") {
208
- return { plugins: {}, devDependencies: {}, disabled: [], config: {} };
209
- }
210
- throw err;
211
- }
212
- }
213
-
214
- /**
215
- * Sync .pi/package.json with plugins.json for npm operations in project-local mode
216
- */
217
- async function syncProjectPackageJson(data: PluginsJson): Promise<void> {
218
- let existing: Record<string, unknown> = {};
219
- try {
220
- existing = JSON.parse(await readFile(PROJECT_PACKAGE_JSON, "utf-8"));
221
- } catch {
222
- existing = {
223
- name: "pi-project-plugins",
224
- version: "1.0.0",
225
- private: true,
226
- description: "Project-local pi plugins managed by omp",
227
- };
228
- }
229
-
230
- existing.dependencies = data.plugins;
231
- if (data.devDependencies && Object.keys(data.devDependencies).length > 0) {
232
- existing.devDependencies = data.devDependencies;
233
- } else {
234
- delete existing.devDependencies;
235
- }
236
-
237
- await writeFile(PROJECT_PACKAGE_JSON, JSON.stringify(existing, null, 2));
238
- }
239
-
240
- /**
241
- * Save plugins.json (global or project)
242
- */
243
- export async function savePluginsJson(data: PluginsJson, global = true): Promise<void> {
244
- const path = global ? GLOBAL_PACKAGE_JSON : PROJECT_PLUGINS_JSON;
245
-
246
- try {
247
- await mkdir(dirname(path), { recursive: true });
248
-
249
- if (global) {
250
- // Read existing package.json and update dependencies
251
- let existing: Record<string, unknown> = {};
252
- try {
253
- existing = JSON.parse(await readFile(path, "utf-8"));
254
- } catch {
255
- existing = {
256
- name: "pi-plugins",
257
- version: "1.0.0",
258
- private: true,
259
- description: "Global pi plugins managed by omp",
260
- };
261
- }
262
-
263
- existing.dependencies = data.plugins;
264
- if (data.devDependencies && Object.keys(data.devDependencies).length > 0) {
265
- existing.devDependencies = data.devDependencies;
266
- } else {
267
- delete existing.devDependencies;
268
- }
269
-
270
- // Build omp field with disabled and config
271
- const ompField: Record<string, unknown> = (existing.omp as Record<string, unknown>) || {};
272
- if (data.disabled?.length) {
273
- ompField.disabled = data.disabled;
274
- } else {
275
- delete ompField.disabled;
276
- }
277
- if (data.config && Object.keys(data.config).length > 0) {
278
- ompField.config = data.config;
279
- } else {
280
- delete ompField.config;
281
- }
282
- if (Object.keys(ompField).length > 0) {
283
- existing.omp = ompField;
284
- } else {
285
- delete existing.omp;
286
- }
287
-
288
- await writeFile(path, JSON.stringify(existing, null, 2));
289
- } else {
290
- // Project uses simple plugins.json format
291
- const output: Record<string, unknown> = { plugins: data.plugins };
292
- if (data.devDependencies && Object.keys(data.devDependencies).length > 0) {
293
- output.devDependencies = data.devDependencies;
294
- }
295
- if (data.disabled?.length) {
296
- output.disabled = data.disabled;
297
- }
298
- if (data.config && Object.keys(data.config).length > 0) {
299
- output.config = data.config;
300
- }
301
- await writeFile(path, JSON.stringify(output, null, 2));
302
-
303
- // Sync .pi/package.json for npm operations
304
- await syncProjectPackageJson(data);
305
- }
306
- } catch (err) {
307
- const error = err as NodeJS.ErrnoException;
308
- if (error.code === "EACCES" || error.code === "EPERM") {
309
- throw new Error(formatPermissionError(error, path));
310
- }
311
- throw err;
312
- }
313
- }
314
-
315
- /**
316
- * Read a plugin's package.json from node_modules
317
- */
318
- export async function readPluginPackageJson(pluginName: string, global = true): Promise<PluginPackageJson | null> {
319
- const nodeModules = global ? NODE_MODULES_DIR : ".pi/node_modules";
320
- let pkgPath: string;
321
-
322
- // Handle scoped packages
323
- if (pluginName.startsWith("@")) {
324
- pkgPath = join(nodeModules, pluginName, "package.json");
325
- } else {
326
- pkgPath = join(nodeModules, pluginName, "package.json");
327
- }
328
-
329
- try {
330
- const data = await readFile(pkgPath, "utf-8");
331
- return JSON.parse(data) as PluginPackageJson;
332
- } catch {
333
- return null;
334
- }
335
- }
336
-
337
- /**
338
- * Get the source directory for a plugin in node_modules
339
- */
340
- export function getPluginSourceDir(pluginName: string, global = true): string {
341
- const nodeModules = global ? NODE_MODULES_DIR : ".pi/node_modules";
342
- return join(nodeModules, pluginName);
343
- }
344
-
345
- /**
346
- * Get all installed plugins with their info
347
- */
348
- export async function getInstalledPlugins(global = true): Promise<Map<string, PluginPackageJson>> {
349
- const pluginsJson = await loadPluginsJson(global);
350
- const plugins = new Map<string, PluginPackageJson>();
351
-
352
- for (const name of Object.keys(pluginsJson.plugins)) {
353
- const pkgJson = await readPluginPackageJson(name, global);
354
- if (pkgJson) {
355
- plugins.set(name, pkgJson);
356
- }
357
- }
358
-
359
- return plugins;
360
- }
package/src/npm.ts DELETED
@@ -1,206 +0,0 @@
1
- import { execFileSync } from "node:child_process";
2
- import type { OmpField } from "@omp/manifest";
3
-
4
- export interface NpmAvailability {
5
- available: boolean;
6
- version?: string;
7
- error?: string;
8
- }
9
-
10
- /**
11
- * Check npm availability and version
12
- */
13
- export function checkNpmAvailable(): NpmAvailability {
14
- try {
15
- const version = execFileSync("npm", ["--version"], {
16
- encoding: "utf-8",
17
- stdio: ["pipe", "pipe", "pipe"],
18
- }).trim();
19
-
20
- // Parse version and check minimum (npm 7+)
21
- const major = parseInt(version.split(".")[0], 10);
22
- if (major < 7) {
23
- return {
24
- available: false,
25
- version,
26
- error: `npm version ${version} is too old. Please upgrade to npm 7 or later.`,
27
- };
28
- }
29
-
30
- return { available: true, version };
31
- } catch {
32
- return {
33
- available: false,
34
- error: "npm is not installed or not in PATH. Please install Node.js/npm.",
35
- };
36
- }
37
- }
38
-
39
- export interface NpmPackageInfo {
40
- name: string;
41
- version: string;
42
- description?: string;
43
- keywords?: string[];
44
- author?: string | { name: string; email?: string };
45
- homepage?: string;
46
- repository?: { type: string; url: string } | string;
47
- versions?: string[];
48
- "dist-tags"?: Record<string, string>;
49
- omp?: OmpField;
50
- dependencies?: Record<string, string>;
51
- }
52
-
53
- export interface NpmSearchResult {
54
- name: string;
55
- version: string;
56
- description?: string;
57
- keywords?: string[];
58
- date?: string;
59
- author?: { name: string };
60
- }
61
-
62
- const DEFAULT_TIMEOUT_MS = 60000; // 60 seconds
63
-
64
- /**
65
- * Execute npm command and return output
66
- */
67
- export function npmExec(args: string[], cwd?: string, timeout = DEFAULT_TIMEOUT_MS): string {
68
- try {
69
- return execFileSync("npm", args, {
70
- cwd,
71
- stdio: ["pipe", "pipe", "pipe"],
72
- encoding: "utf-8",
73
- timeout,
74
- });
75
- } catch (err) {
76
- const error = err as { killed?: boolean; code?: string; message: string };
77
- if (error.killed || error.code === "ETIMEDOUT") {
78
- throw new Error(`npm operation timed out after ${timeout / 1000} seconds`);
79
- }
80
- throw err;
81
- }
82
- }
83
-
84
- /**
85
- * Execute npm command with prefix (for installing to specific directory)
86
- */
87
- export function npmExecWithPrefix(args: string[], prefix: string, timeout = DEFAULT_TIMEOUT_MS): string {
88
- try {
89
- return execFileSync("npm", ["--prefix", prefix, ...args], {
90
- stdio: ["pipe", "pipe", "pipe"],
91
- encoding: "utf-8",
92
- timeout,
93
- });
94
- } catch (err) {
95
- const error = err as { killed?: boolean; code?: string; message: string };
96
- if (error.killed || error.code === "ETIMEDOUT") {
97
- throw new Error(`npm operation timed out after ${timeout / 1000} seconds`);
98
- }
99
- throw err;
100
- }
101
- }
102
-
103
- /**
104
- * Install packages using npm
105
- */
106
- export async function npmInstall(
107
- packages: string[],
108
- prefix: string,
109
- options: { save?: boolean; saveDev?: boolean } = {},
110
- ): Promise<void> {
111
- const args = ["install"];
112
-
113
- if (options.save) {
114
- args.push("--save");
115
- } else if (options.saveDev) {
116
- args.push("--save-dev");
117
- }
118
-
119
- args.push(...packages);
120
-
121
- npmExecWithPrefix(args, prefix);
122
- }
123
-
124
- /**
125
- * Uninstall packages using npm
126
- */
127
- export async function npmUninstall(packages: string[], prefix: string): Promise<void> {
128
- npmExecWithPrefix(["uninstall", ...packages], prefix);
129
- }
130
-
131
- /**
132
- * Get package info from npm registry
133
- */
134
- export async function npmInfo(packageName: string): Promise<NpmPackageInfo | null> {
135
- try {
136
- const output = npmExec(["info", packageName, "--json"]);
137
- return JSON.parse(output);
138
- } catch {
139
- return null;
140
- }
141
- }
142
-
143
- /**
144
- * Search npm for packages with a keyword
145
- */
146
- export async function npmSearch(query: string, keyword = "omp-plugin"): Promise<NpmSearchResult[]> {
147
- // Search for packages with the omp-plugin keyword
148
- const searchTerm = keyword ? `keywords:${keyword} ${query}` : query;
149
- const output = npmExec(["search", searchTerm, "--json"]);
150
- return JSON.parse(output);
151
- }
152
-
153
- /**
154
- * Check for outdated packages
155
- */
156
- export async function npmOutdated(
157
- prefix: string,
158
- ): Promise<Record<string, { current: string; wanted: string; latest: string }>> {
159
- try {
160
- const output = npmExecWithPrefix(["outdated", "--json"], prefix);
161
- return JSON.parse(output);
162
- } catch (err) {
163
- // npm outdated exits with code 1 if there are outdated packages
164
- const error = err as { stdout?: string };
165
- if (error.stdout) {
166
- try {
167
- return JSON.parse(error.stdout);
168
- } catch {
169
- return {};
170
- }
171
- }
172
- return {};
173
- }
174
- }
175
-
176
- /**
177
- * Update packages using npm
178
- */
179
- export async function npmUpdate(packages: string[], prefix: string): Promise<void> {
180
- const args = ["update"];
181
- if (packages.length > 0) {
182
- args.push(...packages);
183
- }
184
- npmExecWithPrefix(args, prefix);
185
- }
186
-
187
- /**
188
- * Get list of installed packages
189
- */
190
- export async function npmList(prefix: string): Promise<Record<string, { version: string }>> {
191
- try {
192
- const output = npmExecWithPrefix(["list", "--json", "--depth=0"], prefix);
193
- const parsed = JSON.parse(output);
194
- return parsed.dependencies || {};
195
- } catch {
196
- return {};
197
- }
198
- }
199
-
200
- /**
201
- * Resolve a package version from the registry
202
- */
203
- export async function resolveVersion(packageName: string, versionRange = "latest"): Promise<string | null> {
204
- const info = await npmInfo(`${packageName}@${versionRange}`);
205
- return info?.version || null;
206
- }
package/src/paths.ts DELETED
@@ -1,137 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import { homedir } from "node:os";
3
- import { dirname, join, resolve } from "node:path";
4
-
5
- // Global pi configuration directory
6
- export const PI_CONFIG_DIR = join(homedir(), ".pi");
7
-
8
- // Global plugins directory
9
- export const PLUGINS_DIR = join(PI_CONFIG_DIR, "plugins");
10
-
11
- // npm node_modules within plugins directory
12
- export const NODE_MODULES_DIR = join(PLUGINS_DIR, "node_modules");
13
-
14
- // Global package.json for plugin management
15
- export const GLOBAL_PACKAGE_JSON = join(PLUGINS_DIR, "package.json");
16
-
17
- // Global package-lock.json
18
- export const GLOBAL_LOCK_FILE = join(PLUGINS_DIR, "package-lock.json");
19
-
20
- // Project-local config directory
21
- export const PROJECT_PI_DIR = ".pi";
22
-
23
- // Project-local plugins.json
24
- export const PROJECT_PLUGINS_JSON = join(PROJECT_PI_DIR, "plugins.json");
25
-
26
- // Project-local package.json (for npm operations)
27
- export const PROJECT_PACKAGE_JSON = join(PROJECT_PI_DIR, "package.json");
28
-
29
- // Project-local lock file
30
- export const PROJECT_PLUGINS_LOCK = join(PROJECT_PI_DIR, "plugins-lock.json");
31
-
32
- // Project-local node_modules
33
- export const PROJECT_NODE_MODULES = join(PROJECT_PI_DIR, "node_modules");
34
-
35
- /**
36
- * Find the project root by walking up parent directories looking for .pi/plugins.json.
37
- * Similar to how git finds .git directories.
38
- *
39
- * @returns The absolute path to the project root, or null if not found
40
- */
41
- export function findProjectRoot(): string | null {
42
- let dir = process.cwd();
43
- const root = resolve("/");
44
-
45
- while (dir !== root) {
46
- if (existsSync(join(dir, ".pi", "plugins.json"))) {
47
- return dir;
48
- }
49
- const parent = dirname(dir);
50
- if (parent === dir) break; // Reached filesystem root
51
- dir = parent;
52
- }
53
-
54
- return null;
55
- }
56
-
57
- /**
58
- * Check if a project-local .pi/plugins.json exists in the current directory or any parent
59
- */
60
- export function hasProjectPlugins(): boolean {
61
- return findProjectRoot() !== null;
62
- }
63
-
64
- /**
65
- * Get the project .pi directory path.
66
- * Uses findProjectRoot() to locate the project, or falls back to cwd.
67
- */
68
- export function getProjectPiDir(): string {
69
- const projectRoot = findProjectRoot();
70
- if (projectRoot) {
71
- return join(projectRoot, ".pi");
72
- }
73
- // Fallback to cwd (e.g., for init command)
74
- return resolve(PROJECT_PI_DIR);
75
- }
76
-
77
- /**
78
- * Get the plugins directory for the given scope
79
- */
80
- export function getPluginsDir(global = true): string {
81
- if (global) {
82
- return PLUGINS_DIR;
83
- }
84
- return getProjectPiDir();
85
- }
86
-
87
- /**
88
- * Get the node_modules directory for the given scope
89
- */
90
- export function getNodeModulesDir(global = true): string {
91
- if (global) {
92
- return NODE_MODULES_DIR;
93
- }
94
- return join(getProjectPiDir(), "node_modules");
95
- }
96
-
97
- /**
98
- * Get the package.json path for the given scope
99
- */
100
- export function getPackageJsonPath(global = true): string {
101
- if (global) {
102
- return GLOBAL_PACKAGE_JSON;
103
- }
104
- return join(getProjectPiDir(), "plugins.json");
105
- }
106
-
107
- /**
108
- * Get the agent directory (where symlinks are installed)
109
- */
110
- export function getAgentDir(global = true): string {
111
- if (global) {
112
- return join(PI_CONFIG_DIR, "agent");
113
- }
114
- return join(getProjectPiDir(), "agent");
115
- }
116
-
117
- /**
118
- * Resolve whether to use global or local scope based on CLI flags and auto-detection.
119
- *
120
- * Logic:
121
- * - If --global is passed: use global mode
122
- * - If --local is passed: use local mode
123
- * - If neither: check if .pi/plugins.json exists in cwd, if so use local, otherwise use global
124
- *
125
- * @param options - CLI options containing global and local flags
126
- * @returns true if global scope should be used, false for local
127
- */
128
- export function resolveScope(options: { global?: boolean; local?: boolean }): boolean {
129
- if (options.global) {
130
- return true;
131
- }
132
- if (options.local) {
133
- return false;
134
- }
135
- // Auto-detect: if project-local plugins.json exists, use local mode
136
- return !hasProjectPlugins();
137
- }