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