@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/runtime.ts DELETED
@@ -1,116 +0,0 @@
1
- import type { OmpVariable, PluginPackageJson, PluginsJson } from "@omp/manifest";
2
- import { loadPluginsJson, readPluginPackageJson } from "@omp/manifest";
3
-
4
- /**
5
- * Collect all variables from a plugin (top-level + enabled features)
6
- */
7
- function collectVariables(
8
- pkgJson: PluginPackageJson,
9
- enabledFeatures: string[],
10
- ): Record<string, OmpVariable> {
11
- const vars: Record<string, OmpVariable> = {};
12
-
13
- // Top-level variables
14
- if (pkgJson.omp?.variables) {
15
- Object.assign(vars, pkgJson.omp.variables);
16
- }
17
-
18
- // Variables from enabled features
19
- if (pkgJson.omp?.features) {
20
- for (const fname of enabledFeatures) {
21
- const feature = pkgJson.omp.features[fname];
22
- if (feature?.variables) {
23
- Object.assign(vars, feature.variables);
24
- }
25
- }
26
- }
27
-
28
- return vars;
29
- }
30
-
31
- /**
32
- * Resolve which features are currently enabled
33
- *
34
- * - null/undefined: use plugin defaults (features with default !== false)
35
- * - ["*"]: explicitly all features
36
- * - []: no optional features
37
- * - ["f1", "f2"]: specific features
38
- */
39
- function resolveEnabledFeatures(
40
- allFeatureNames: string[],
41
- storedFeatures: string[] | null | undefined,
42
- pluginFeatures: Record<string, { default?: boolean }>,
43
- ): string[] {
44
- // Explicit "all features" request
45
- if (Array.isArray(storedFeatures) && storedFeatures.includes("*")) return allFeatureNames;
46
- // Explicit feature list (including empty array = no features)
47
- if (Array.isArray(storedFeatures)) return storedFeatures;
48
- // null/undefined = use defaults
49
- return Object.entries(pluginFeatures)
50
- .filter(([_, f]) => f.default !== false)
51
- .map(([name]) => name);
52
- }
53
-
54
- /**
55
- * Get all environment variables for enabled plugins
56
- */
57
- export async function getPluginEnvVars(global = true): Promise<Record<string, string>> {
58
- const pluginsJson = await loadPluginsJson(global);
59
- const env: Record<string, string> = {};
60
-
61
- for (const pluginName of Object.keys(pluginsJson.plugins)) {
62
- // Skip disabled plugins
63
- if (pluginsJson.disabled?.includes(pluginName)) continue;
64
-
65
- const pkgJson = await readPluginPackageJson(pluginName, global);
66
- if (!pkgJson?.omp) continue;
67
-
68
- const config = pluginsJson.config?.[pluginName];
69
- const allFeatureNames = Object.keys(pkgJson.omp.features || {});
70
- const enabledFeatures = resolveEnabledFeatures(
71
- allFeatureNames,
72
- config?.features,
73
- pkgJson.omp.features || {},
74
- );
75
-
76
- // Collect variables from top-level and enabled features
77
- const variables = collectVariables(pkgJson, enabledFeatures);
78
-
79
- for (const [key, varDef] of Object.entries(variables)) {
80
- if (varDef.env) {
81
- const value = config?.variables?.[key] ?? varDef.default;
82
- if (value !== undefined) {
83
- env[varDef.env] = String(value);
84
- }
85
- }
86
- }
87
- }
88
-
89
- return env;
90
- }
91
-
92
- /**
93
- * Generate shell export statements
94
- * omp env > ~/.pi/env.sh && source ~/.pi/env.sh
95
- */
96
- export async function generateEnvScript(global = true, shell: "sh" | "fish" = "sh"): Promise<string> {
97
- const vars = await getPluginEnvVars(global);
98
-
99
- if (shell === "fish") {
100
- return Object.entries(vars)
101
- .map(([k, v]) => `set -gx ${k} ${JSON.stringify(v)}`)
102
- .join("\n");
103
- }
104
-
105
- // POSIX sh/bash/zsh
106
- return Object.entries(vars)
107
- .map(([k, v]) => `export ${k}=${JSON.stringify(v)}`)
108
- .join("\n");
109
- }
110
-
111
- /**
112
- * Get environment variables as a JSON object for programmatic use
113
- */
114
- export async function getEnvJson(global = true): Promise<Record<string, string>> {
115
- return getPluginEnvVars(global);
116
- }
package/src/symlinks.ts DELETED
@@ -1,455 +0,0 @@
1
- import { existsSync, lstatSync, readFileSync, writeFileSync } from "node:fs";
2
- import { copyFile, mkdir, readlink, rm, symlink } from "node:fs/promises";
3
- import { platform } from "node:os";
4
- import { dirname, join, resolve } from "node:path";
5
- import type { OmpFeature, OmpInstallEntry, PluginPackageJson, PluginRuntimeConfig } from "@omp/manifest";
6
- import { getPluginSourceDir } from "@omp/manifest";
7
- import { PI_CONFIG_DIR, PROJECT_PI_DIR } from "@omp/paths";
8
- import chalk from "chalk";
9
-
10
- /**
11
- * Get all install entries from package.json.
12
- * Features no longer have install entries - all files are always installed.
13
- */
14
- export function getInstallEntries(pkgJson: PluginPackageJson): OmpInstallEntry[] {
15
- return pkgJson.omp?.install ?? [];
16
- }
17
-
18
- /**
19
- * @deprecated Use getInstallEntries instead. Features no longer have install arrays.
20
- */
21
- export function getEnabledInstallEntries(
22
- pkgJson: PluginPackageJson,
23
- _enabledFeatures?: string[],
24
- ): OmpInstallEntry[] {
25
- return getInstallEntries(pkgJson);
26
- }
27
-
28
- /**
29
- * Get all available feature names from a plugin
30
- */
31
- export function getAllFeatureNames(pkgJson: PluginPackageJson): string[] {
32
- return Object.keys(pkgJson.omp?.features || {});
33
- }
34
-
35
- /**
36
- * Get features that are enabled by default (default !== false)
37
- */
38
- export function getDefaultFeatures(features: Record<string, OmpFeature>): string[] {
39
- return Object.entries(features)
40
- .filter(([_, f]) => f.default !== false)
41
- .map(([name]) => name);
42
- }
43
-
44
- const isWindows = platform() === "win32";
45
-
46
- /**
47
- * Format permission-related errors with actionable guidance
48
- */
49
- function formatPermissionError(err: NodeJS.ErrnoException, path: string): string {
50
- if (err.code === "EACCES" || err.code === "EPERM") {
51
- return `Permission denied: Cannot write to ${path}. Check directory permissions or run with appropriate privileges.`;
52
- }
53
- return err.message;
54
- }
55
-
56
- /**
57
- * Validates that a target path stays within the base directory.
58
- * Prevents path traversal attacks via malicious dest entries like '../../../etc/passwd'.
59
- */
60
- function isPathWithinBase(basePath: string, targetPath: string): boolean {
61
- const normalizedBase = resolve(basePath);
62
- const resolvedTarget = resolve(basePath, targetPath);
63
- // Must start with base path followed by separator (or be exactly the base)
64
- return resolvedTarget === normalizedBase || resolvedTarget.startsWith(`${normalizedBase}/`);
65
- }
66
-
67
- /**
68
- * Get the base directory for symlink destinations based on scope
69
- */
70
- function getBaseDir(global: boolean): string {
71
- return global ? PI_CONFIG_DIR : PROJECT_PI_DIR;
72
- }
73
-
74
- export interface SymlinkResult {
75
- created: string[];
76
- errors: string[];
77
- }
78
-
79
- export interface SymlinkRemovalResult {
80
- removed: string[];
81
- errors: string[];
82
- skippedNonSymlinks: string[]; // Files that exist but aren't symlinks
83
- }
84
-
85
- /**
86
- * Create symlinks (or copy files with copy:true) for a plugin's omp.install entries
87
- * @param skipDestinations - Set of destination paths to skip (e.g., due to conflict resolution)
88
- * @param enabledFeatures - Features to write into runtime.json (if plugin has one)
89
- */
90
- export async function createPluginSymlinks(
91
- pluginName: string,
92
- pkgJson: PluginPackageJson,
93
- global = true,
94
- verbose = true,
95
- skipDestinations?: Set<string>,
96
- enabledFeatures?: string[],
97
- ): Promise<SymlinkResult> {
98
- const result: SymlinkResult = { created: [], errors: [] };
99
- const sourceDir = getPluginSourceDir(pluginName, global);
100
-
101
- const installEntries = getInstallEntries(pkgJson);
102
- if (installEntries.length === 0) {
103
- if (verbose) {
104
- console.log(chalk.dim(" No omp.install entries found"));
105
- }
106
- return result;
107
- }
108
-
109
- const baseDir = getBaseDir(global);
110
-
111
- for (const entry of installEntries) {
112
- // Skip destinations that the user chose to keep from existing plugins
113
- if (skipDestinations?.has(entry.dest)) {
114
- if (verbose) {
115
- console.log(chalk.dim(` Skipped: ${entry.dest} (conflict resolved to existing plugin)`));
116
- }
117
- continue;
118
- }
119
-
120
- // Validate dest path stays within base directory (prevents path traversal attacks)
121
- if (!isPathWithinBase(baseDir, entry.dest)) {
122
- const msg = `Path traversal blocked: ${entry.dest} escapes base directory`;
123
- result.errors.push(msg);
124
- if (verbose) {
125
- console.log(chalk.red(` ✗ ${msg}`));
126
- }
127
- continue;
128
- }
129
-
130
- try {
131
- const src = join(sourceDir, entry.src);
132
- const dest = join(baseDir, entry.dest);
133
-
134
- // Check if source exists
135
- if (!existsSync(src)) {
136
- result.errors.push(`Source not found: ${entry.src}`);
137
- if (verbose) {
138
- console.log(chalk.yellow(` ⚠ Source not found: ${entry.src}`));
139
- }
140
- continue;
141
- }
142
-
143
- // Create parent directory
144
- await mkdir(dirname(dest), { recursive: true });
145
-
146
- // Handle copy vs symlink
147
- if (entry.copy) {
148
- // For copy entries (like runtime.json), copy the file
149
- // But DON'T overwrite if it already exists (preserves user edits)
150
- if (!existsSync(dest)) {
151
- await copyFile(src, dest);
152
- result.created.push(entry.dest);
153
- if (verbose) {
154
- console.log(chalk.dim(` Copied: ${entry.dest} (from ${entry.src})`));
155
- }
156
- } else {
157
- if (verbose) {
158
- console.log(chalk.dim(` Exists: ${entry.dest} (preserved)`));
159
- }
160
- }
161
- } else {
162
- // Remove existing symlink/file if it exists
163
- try {
164
- await rm(dest, { force: true, recursive: true });
165
- } catch {}
166
-
167
- // Create symlink (use junctions on Windows for directories to avoid admin requirement)
168
- try {
169
- if (isWindows) {
170
- const stats = lstatSync(src);
171
- if (stats.isDirectory()) {
172
- await symlink(src, dest, "junction");
173
- } else {
174
- await symlink(src, dest, "file");
175
- }
176
- } else {
177
- await symlink(src, dest);
178
- }
179
- } catch (symlinkErr) {
180
- const error = symlinkErr as NodeJS.ErrnoException;
181
- if (isWindows && error.code === "EPERM") {
182
- console.log(chalk.red(` Permission denied creating symlink.`));
183
- console.log(chalk.dim(" On Windows, enable Developer Mode or run as Administrator."));
184
- console.log(chalk.dim(" Settings > Update & Security > For developers > Developer Mode"));
185
- }
186
- throw symlinkErr;
187
- }
188
- result.created.push(entry.dest);
189
-
190
- if (verbose) {
191
- console.log(chalk.dim(` Linked: ${entry.dest} → ${entry.src}`));
192
- }
193
- }
194
- } catch (err) {
195
- const error = err as NodeJS.ErrnoException;
196
- const msg = `Failed to install ${entry.dest}: ${formatPermissionError(error, join(baseDir, entry.dest))}`;
197
- result.errors.push(msg);
198
- if (verbose) {
199
- console.log(chalk.red(` ✗ ${msg}`));
200
- if (error.code === "EACCES" || error.code === "EPERM") {
201
- console.log(chalk.dim(" Check directory permissions or run with appropriate privileges."));
202
- }
203
- }
204
- }
205
- }
206
-
207
- // If enabledFeatures provided and plugin has a runtime.json entry, update it
208
- if (enabledFeatures !== undefined) {
209
- const runtimeEntry = installEntries.find((e) => e.copy && e.dest.endsWith("runtime.json"));
210
- if (runtimeEntry) {
211
- const runtimePath = join(baseDir, runtimeEntry.dest);
212
- await writeRuntimeConfig(runtimePath, { features: enabledFeatures }, verbose);
213
- }
214
- }
215
-
216
- return result;
217
- }
218
-
219
- /**
220
- * Read runtime.json config from a plugin's installed location
221
- * Returns {} on failure so callers can detect missing/corrupt config and fall back to defaults
222
- */
223
- export function readRuntimeConfig(runtimePath: string): PluginRuntimeConfig {
224
- try {
225
- const content = readFileSync(runtimePath, "utf-8");
226
- return JSON.parse(content) as PluginRuntimeConfig;
227
- } catch {
228
- // Return empty object (not {features: []}) so callers detect missing config
229
- // and can fall back to plugin defaults instead of treating as "all disabled"
230
- return {};
231
- }
232
- }
233
-
234
- /**
235
- * Write runtime.json config to a plugin's installed location
236
- */
237
- export async function writeRuntimeConfig(
238
- runtimePath: string,
239
- config: PluginRuntimeConfig,
240
- verbose = false,
241
- ): Promise<void> {
242
- try {
243
- const existing = readRuntimeConfig(runtimePath);
244
- const merged: PluginRuntimeConfig = {
245
- features: config.features ?? existing.features ?? [],
246
- options: { ...existing.options, ...config.options },
247
- };
248
- writeFileSync(runtimePath, JSON.stringify(merged, null, 2) + "\n");
249
- if (verbose) {
250
- console.log(chalk.dim(` Updated: ${runtimePath}`));
251
- }
252
- } catch (err) {
253
- if (verbose) {
254
- console.log(chalk.yellow(` ⚠ Failed to update runtime config: ${err}`));
255
- }
256
- }
257
- }
258
-
259
- /**
260
- * Get the path to a plugin's runtime.json in the installed location
261
- */
262
- export function getRuntimeConfigPath(pkgJson: PluginPackageJson, global = true): string | null {
263
- const entries = getInstallEntries(pkgJson);
264
- const runtimeEntry = entries.find((e) => e.copy && e.dest.endsWith("runtime.json"));
265
- if (!runtimeEntry) return null;
266
- return join(getBaseDir(global), runtimeEntry.dest);
267
- }
268
-
269
- /**
270
- * Remove symlinks and copied files for a plugin's omp.install entries
271
- */
272
- export async function removePluginSymlinks(
273
- _pluginName: string,
274
- pkgJson: PluginPackageJson,
275
- global = true,
276
- verbose = true,
277
- ): Promise<SymlinkRemovalResult> {
278
- const result: SymlinkRemovalResult = { removed: [], errors: [], skippedNonSymlinks: [] };
279
-
280
- const installEntries = getInstallEntries(pkgJson);
281
- if (installEntries.length === 0) {
282
- return result;
283
- }
284
-
285
- const baseDir = getBaseDir(global);
286
-
287
- for (const entry of installEntries) {
288
- // Validate dest path stays within base directory (prevents path traversal attacks)
289
- if (!isPathWithinBase(baseDir, entry.dest)) {
290
- const msg = `Path traversal blocked: ${entry.dest} escapes base directory`;
291
- result.errors.push(msg);
292
- if (verbose) {
293
- console.log(chalk.red(` ✗ ${msg}`));
294
- }
295
- continue;
296
- }
297
-
298
- const dest = join(baseDir, entry.dest);
299
-
300
- try {
301
- if (existsSync(dest)) {
302
- const stats = lstatSync(dest);
303
-
304
- // For copy entries (like runtime.json), we can safely remove them
305
- if (entry.copy) {
306
- await rm(dest, { force: true });
307
- result.removed.push(entry.dest);
308
- if (verbose) {
309
- console.log(chalk.dim(` Removed: ${entry.dest}`));
310
- }
311
- continue;
312
- }
313
-
314
- // For symlinks, check they're actually symlinks
315
- if (!stats.isSymbolicLink()) {
316
- result.skippedNonSymlinks.push(dest);
317
- if (verbose) {
318
- console.log(chalk.yellow(` ⚠ Skipping ${entry.dest}: not a symlink (may contain user data)`));
319
- }
320
- continue;
321
- }
322
-
323
- await rm(dest, { force: true, recursive: true });
324
- result.removed.push(entry.dest);
325
- if (verbose) {
326
- console.log(chalk.dim(` Removed: ${entry.dest}`));
327
- }
328
- }
329
- } catch (err) {
330
- const error = err as NodeJS.ErrnoException;
331
- const msg = `Failed to remove ${entry.dest}: ${formatPermissionError(error, dest)}`;
332
- result.errors.push(msg);
333
- if (verbose) {
334
- console.log(chalk.yellow(` ⚠ ${msg}`));
335
- if (error.code === "EACCES" || error.code === "EPERM") {
336
- console.log(chalk.dim(" Check directory permissions or run with appropriate privileges."));
337
- }
338
- }
339
- }
340
- }
341
-
342
- return result;
343
- }
344
-
345
- /**
346
- * Check symlink/file health for a plugin
347
- */
348
- export async function checkPluginSymlinks(
349
- pluginName: string,
350
- pkgJson: PluginPackageJson,
351
- global = true,
352
- ): Promise<{ valid: string[]; broken: string[]; missing: string[] }> {
353
- const result = { valid: [] as string[], broken: [] as string[], missing: [] as string[] };
354
- const sourceDir = getPluginSourceDir(pluginName, global);
355
- const baseDir = getBaseDir(global);
356
-
357
- const installEntries = getInstallEntries(pkgJson);
358
- if (installEntries.length === 0) {
359
- return result;
360
- }
361
-
362
- for (const entry of installEntries) {
363
- // Skip entries with path traversal (treat as broken)
364
- if (!isPathWithinBase(baseDir, entry.dest)) {
365
- result.broken.push(entry.dest);
366
- continue;
367
- }
368
-
369
- const src = join(sourceDir, entry.src);
370
- const dest = join(baseDir, entry.dest);
371
-
372
- if (!existsSync(dest)) {
373
- result.missing.push(entry.dest);
374
- continue;
375
- }
376
-
377
- try {
378
- const stats = lstatSync(dest);
379
-
380
- // For copy entries, just check the file exists
381
- if (entry.copy) {
382
- if (stats.isFile()) {
383
- result.valid.push(entry.dest);
384
- } else {
385
- result.broken.push(entry.dest);
386
- }
387
- continue;
388
- }
389
-
390
- // For symlinks, verify they point to valid sources
391
- if (stats.isSymbolicLink()) {
392
- const _target = await readlink(dest);
393
- if (existsSync(src)) {
394
- result.valid.push(entry.dest);
395
- } else {
396
- result.broken.push(entry.dest);
397
- }
398
- } else {
399
- // Not a symlink, might be a file that was overwritten
400
- result.broken.push(entry.dest);
401
- }
402
- } catch {
403
- result.broken.push(entry.dest);
404
- }
405
- }
406
-
407
- return result;
408
- }
409
-
410
- /**
411
- * Get plugin name from an installed symlink destination
412
- */
413
- export async function getPluginForSymlink(
414
- dest: string,
415
- installedPlugins: Map<string, PluginPackageJson>,
416
- ): Promise<string | null> {
417
- for (const [name, pkgJson] of installedPlugins) {
418
- if (pkgJson.omp?.install) {
419
- for (const entry of pkgJson.omp.install) {
420
- if (entry.dest === dest) {
421
- return name;
422
- }
423
- }
424
- }
425
- }
426
- return null;
427
- }
428
-
429
- /**
430
- * Find all symlinks installed by plugins and trace them back
431
- */
432
- export async function traceInstalledFile(
433
- filePath: string,
434
- installedPlugins: Map<string, PluginPackageJson>,
435
- global = true,
436
- ): Promise<{ plugin: string; entry: OmpInstallEntry } | null> {
437
- // Normalize the path relative to the base directory
438
- const baseDir = getBaseDir(global);
439
- let relativePath = filePath;
440
- if (filePath.startsWith(baseDir)) {
441
- relativePath = filePath.slice(baseDir.length + 1);
442
- }
443
-
444
- for (const [name, pkgJson] of installedPlugins) {
445
- if (pkgJson.omp?.install) {
446
- for (const entry of pkgJson.omp.install) {
447
- if (entry.dest === relativePath) {
448
- return { plugin: name, entry };
449
- }
450
- }
451
- }
452
- }
453
-
454
- return null;
455
- }
package/tsconfig.json DELETED
@@ -1,28 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "lib": ["ES2022"],
6
- "strict": true,
7
- "esModuleInterop": true,
8
- "skipLibCheck": true,
9
- "forceConsistentCasingInFileNames": true,
10
- "declaration": true,
11
- "declarationMap": true,
12
- "sourceMap": true,
13
- "inlineSources": true,
14
- "inlineSourceMap": false,
15
- "moduleResolution": "bundler",
16
- "resolveJsonModule": true,
17
- "allowImportingTsExtensions": false,
18
- "outDir": "./dist",
19
- "rootDir": "./src",
20
- "types": ["node", "bun-types"],
21
- "baseUrl": ".",
22
- "paths": {
23
- "@omp/*": ["src/*"]
24
- }
25
- },
26
- "include": ["src/**/*"],
27
- "exclude": ["node_modules", "dist"]
28
- }