@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
@@ -1,170 +0,0 @@
1
- import { rm } from "node:fs/promises";
2
- import { join } from "node:path";
3
- import { loadPluginsJson, type OmpInstallEntry, type PluginPackageJson, readPluginPackageJson } from "@omp/manifest";
4
- import { npmUpdate } from "@omp/npm";
5
- import {
6
- NODE_MODULES_DIR,
7
- PI_CONFIG_DIR,
8
- PLUGINS_DIR,
9
- PROJECT_NODE_MODULES,
10
- PROJECT_PI_DIR,
11
- resolveScope,
12
- } from "@omp/paths";
13
- import { createPluginSymlinks, removePluginSymlinks } from "@omp/symlinks";
14
- import chalk from "chalk";
15
-
16
- export interface UpdateOptions {
17
- global?: boolean;
18
- local?: boolean;
19
- json?: boolean;
20
- }
21
-
22
- /**
23
- * Update plugin(s) to latest within semver range
24
- */
25
- export async function updatePlugin(name?: string, options: UpdateOptions = {}): Promise<void> {
26
- const isGlobal = resolveScope(options);
27
- const prefix = isGlobal ? PLUGINS_DIR : ".pi";
28
- const _nodeModules = isGlobal ? NODE_MODULES_DIR : PROJECT_NODE_MODULES;
29
-
30
- const pluginsJson = await loadPluginsJson(isGlobal);
31
- const pluginNames = Object.keys(pluginsJson.plugins);
32
-
33
- if (pluginNames.length === 0) {
34
- console.log(chalk.yellow("No plugins installed."));
35
- process.exitCode = 1;
36
- return;
37
- }
38
-
39
- // If specific plugin name provided, verify it's installed
40
- if (name && !pluginsJson.plugins[name]) {
41
- console.log(chalk.yellow(`Plugin "${name}" is not installed.`));
42
- process.exitCode = 1;
43
- return;
44
- }
45
-
46
- const toUpdate = name ? [name] : pluginNames;
47
-
48
- // Filter out file: dependencies (local plugins)
49
- const npmPlugins = toUpdate.filter((n) => {
50
- const version = pluginsJson.plugins[n];
51
- return !version.startsWith("file:");
52
- });
53
-
54
- const localPlugins = toUpdate.filter((n) => {
55
- const version = pluginsJson.plugins[n];
56
- return version.startsWith("file:");
57
- });
58
-
59
- if (localPlugins.length > 0) {
60
- console.log(chalk.dim(`Skipping ${localPlugins.length} local plugin(s): ${localPlugins.join(", ")}`));
61
- }
62
-
63
- if (npmPlugins.length === 0) {
64
- console.log(chalk.yellow("No npm plugins to update."));
65
- process.exitCode = 1;
66
- return;
67
- }
68
-
69
- console.log(chalk.blue(`Updating ${npmPlugins.length} plugin(s)...`));
70
-
71
- const results: Array<{ name: string; from: string; to: string; success: boolean }> = [];
72
-
73
- // Save old package info before removing symlinks (for recovery on failure)
74
- const oldPkgJsons = new Map<string, PluginPackageJson>();
75
- const beforeVersions: Record<string, string> = {};
76
- const oldInstallEntries = new Map<string, OmpInstallEntry[]>();
77
-
78
- try {
79
- // Get current versions and install entries before update
80
- for (const pluginName of npmPlugins) {
81
- const pkgJson = await readPluginPackageJson(pluginName, isGlobal);
82
- if (pkgJson) {
83
- oldPkgJsons.set(pluginName, pkgJson);
84
- beforeVersions[pluginName] = pkgJson.version;
85
-
86
- // Save old install entries for later comparison
87
- if (pkgJson.omp?.install) {
88
- oldInstallEntries.set(pluginName, [...pkgJson.omp.install]);
89
- }
90
-
91
- // Remove old symlinks before update
92
- await removePluginSymlinks(pluginName, pkgJson, isGlobal);
93
- }
94
- }
95
-
96
- // npm update
97
- await npmUpdate(npmPlugins, prefix);
98
-
99
- // Base directory for symlink destinations
100
- const baseDir = isGlobal ? PI_CONFIG_DIR : PROJECT_PI_DIR;
101
-
102
- // Re-process symlinks for each updated plugin
103
- for (const pluginName of npmPlugins) {
104
- const pkgJson = await readPluginPackageJson(pluginName, isGlobal);
105
- if (pkgJson) {
106
- const beforeVersion = beforeVersions[pluginName] || "unknown";
107
- const afterVersion = pkgJson.version;
108
-
109
- // Handle changed omp.install entries: remove orphaned symlinks
110
- const oldEntries = oldInstallEntries.get(pluginName) || [];
111
- const newEntries = pkgJson.omp?.install || [];
112
- const newDests = new Set(newEntries.map((e) => e.dest));
113
-
114
- for (const oldEntry of oldEntries) {
115
- if (!newDests.has(oldEntry.dest)) {
116
- // This destination was in the old version but not in the new one
117
- const dest = join(baseDir, oldEntry.dest);
118
- try {
119
- await rm(dest, { force: true });
120
- console.log(chalk.dim(` Removed orphaned: ${oldEntry.dest}`));
121
- } catch {
122
- // Ignore removal errors for orphaned symlinks
123
- }
124
- }
125
- }
126
-
127
- // Create new symlinks (handles overwrites for existing destinations)
128
- await createPluginSymlinks(pluginName, pkgJson, isGlobal);
129
-
130
- const changed = beforeVersion !== afterVersion;
131
- if (changed) {
132
- console.log(chalk.green(` ✓ ${pluginName}: ${beforeVersion} → ${afterVersion}`));
133
- } else {
134
- console.log(chalk.dim(` · ${pluginName}: ${afterVersion} (already latest)`));
135
- }
136
-
137
- results.push({
138
- name: pluginName,
139
- from: beforeVersion,
140
- to: afterVersion,
141
- success: true,
142
- });
143
- }
144
- }
145
-
146
- const updated = results.filter((r) => r.from !== r.to);
147
- console.log();
148
- console.log(chalk.dim(`Updated: ${updated.length}, Already latest: ${results.length - updated.length}`));
149
-
150
- if (options.json) {
151
- console.log(JSON.stringify({ results }, null, 2));
152
- }
153
- } catch (err) {
154
- // Restore old symlinks on failure
155
- if (oldPkgJsons.size > 0) {
156
- console.log(chalk.yellow(" Update failed, restoring symlinks..."));
157
- for (const [pluginName, pkgJson] of oldPkgJsons) {
158
- try {
159
- await createPluginSymlinks(pluginName, pkgJson, isGlobal);
160
- } catch (restoreErr) {
161
- console.log(
162
- chalk.red(` Failed to restore symlinks for ${pluginName}: ${(restoreErr as Error).message}`),
163
- );
164
- }
165
- }
166
- }
167
- console.log(chalk.red(`Error updating plugins: ${(err as Error).message}`));
168
- process.exitCode = 1;
169
- }
170
- }
@@ -1,136 +0,0 @@
1
- import { existsSync, lstatSync } from "node:fs";
2
- import { readlink } from "node:fs/promises";
3
- import { join, relative, resolve } from "node:path";
4
- import { getInstalledPlugins, getPluginSourceDir, readPluginPackageJson } from "@omp/manifest";
5
- import { PI_CONFIG_DIR, PROJECT_PI_DIR, resolveScope } from "@omp/paths";
6
- import { traceInstalledFile } from "@omp/symlinks";
7
- import chalk from "chalk";
8
-
9
- export interface WhyOptions {
10
- global?: boolean;
11
- local?: boolean;
12
- json?: boolean;
13
- }
14
-
15
- /**
16
- * Show which plugin installed a file
17
- */
18
- export async function whyFile(filePath: string, options: WhyOptions = {}): Promise<void> {
19
- const isGlobal = resolveScope(options);
20
-
21
- // Determine the base directory based on scope
22
- const baseDir = isGlobal ? PI_CONFIG_DIR : resolve(PROJECT_PI_DIR);
23
-
24
- // Normalize path - make it relative to the appropriate base directory
25
- let relativePath = filePath;
26
- if (isGlobal) {
27
- if (filePath.startsWith(PI_CONFIG_DIR)) {
28
- relativePath = relative(PI_CONFIG_DIR, filePath);
29
- } else if (filePath.startsWith("~/.pi/")) {
30
- relativePath = filePath.slice(6); // Remove ~/.pi/
31
- }
32
- } else {
33
- // Project-local mode
34
- const resolvedProjectDir = resolve(PROJECT_PI_DIR);
35
- if (filePath.startsWith(resolvedProjectDir)) {
36
- relativePath = relative(resolvedProjectDir, filePath);
37
- } else if (filePath.startsWith(".pi/")) {
38
- relativePath = filePath.slice(4); // Remove .pi/
39
- }
40
- }
41
-
42
- // Check if it's a path in agent/ directory
43
- if (!relativePath.startsWith("agent/")) {
44
- // Try prepending agent/
45
- const withAgent = `agent/${relativePath}`;
46
- const fullWithAgent = join(baseDir, withAgent);
47
- if (existsSync(fullWithAgent)) {
48
- relativePath = withAgent;
49
- }
50
- }
51
-
52
- const fullPath = join(baseDir, relativePath);
53
-
54
- // Check if file exists
55
- if (!existsSync(fullPath)) {
56
- console.log(chalk.yellow(`File not found: ${fullPath}`));
57
- process.exitCode = 1;
58
- return;
59
- }
60
-
61
- // Check if it's a symlink
62
- const stats = lstatSync(fullPath);
63
- const isSymlink = stats.isSymbolicLink();
64
-
65
- let target: string | null = null;
66
- if (isSymlink) {
67
- target = await readlink(fullPath);
68
- }
69
-
70
- // Search through installed plugins
71
- const installedPlugins = await getInstalledPlugins(isGlobal);
72
- const result = await traceInstalledFile(relativePath, installedPlugins, isGlobal);
73
-
74
- if (options.json) {
75
- console.log(
76
- JSON.stringify(
77
- {
78
- path: relativePath,
79
- fullPath,
80
- isSymlink,
81
- target,
82
- plugin: result?.plugin || null,
83
- source: result?.entry.src || null,
84
- },
85
- null,
86
- 2,
87
- ),
88
- );
89
- return;
90
- }
91
-
92
- console.log(chalk.bold(`File: ${relativePath}`));
93
- console.log(chalk.dim(`Full path: ${fullPath}`));
94
- console.log();
95
-
96
- if (isSymlink && target) {
97
- console.log(`${chalk.dim("Type: ")}symlink`);
98
- console.log(chalk.dim("Target: ") + target);
99
- console.log();
100
- }
101
-
102
- if (result) {
103
- // Verify it's actually a symlink pointing to the right place
104
- if (!isSymlink) {
105
- console.log(chalk.yellow("⚠ This file exists but is not a symlink"));
106
- console.log(chalk.dim(" It may have been manually created or the symlink was replaced."));
107
- console.log(chalk.dim(` Expected to be installed by: ${result.plugin}`));
108
- } else {
109
- // Verify symlink points to correct source
110
- const expectedSrc = join(getPluginSourceDir(result.plugin, isGlobal), result.entry.src);
111
- if (target !== expectedSrc) {
112
- console.log(chalk.yellow("⚠ Symlink target does not match expected source"));
113
- console.log(chalk.dim(` Expected: ${expectedSrc}`));
114
- console.log(chalk.dim(` Actual: ${target}`));
115
- console.log(chalk.dim(` Expected to be installed by: ${result.plugin}`));
116
- } else {
117
- console.log(chalk.green(`✓ Installed by: ${result.plugin}`));
118
- console.log(chalk.dim(` Source: ${result.entry.src}`));
119
- console.log(chalk.dim(` Dest: ${result.entry.dest}`));
120
- }
121
- }
122
-
123
- // Get plugin info
124
- const pkgJson = await readPluginPackageJson(result.plugin, isGlobal);
125
- if (pkgJson) {
126
- console.log();
127
- console.log(chalk.dim(`Plugin version: ${pkgJson.version}`));
128
- if (pkgJson.description) {
129
- console.log(chalk.dim(`Description: ${pkgJson.description}`));
130
- }
131
- }
132
- } else {
133
- console.log(chalk.yellow("⚠ Not installed by any tracked plugin"));
134
- console.log(chalk.dim(" This file may have been created manually or by a plugin that was uninstalled."));
135
- }
136
- }
package/src/conflicts.ts DELETED
@@ -1,116 +0,0 @@
1
- import type { PluginPackageJson } from "@omp/manifest";
2
-
3
- export interface Conflict {
4
- dest: string;
5
- plugins: Array<{ name: string; src: string }>;
6
- }
7
-
8
- export interface IntraPluginDuplicate {
9
- dest: string;
10
- sources: string[];
11
- }
12
-
13
- /**
14
- * Detect duplicate destinations within a single plugin's omp.install array
15
- */
16
- export function detectIntraPluginDuplicates(pkgJson: PluginPackageJson): IntraPluginDuplicate[] {
17
- const duplicates: IntraPluginDuplicate[] = [];
18
-
19
- if (!pkgJson.omp?.install?.length) {
20
- return duplicates;
21
- }
22
-
23
- const destMap = new Map<string, string[]>();
24
-
25
- for (const entry of pkgJson.omp.install) {
26
- const sources = destMap.get(entry.dest) || [];
27
- sources.push(entry.src);
28
- destMap.set(entry.dest, sources);
29
- }
30
-
31
- for (const [dest, sources] of destMap) {
32
- if (sources.length > 1) {
33
- duplicates.push({ dest, sources });
34
- }
35
- }
36
-
37
- return duplicates;
38
- }
39
-
40
- /**
41
- * Detect conflicts between a new plugin and existing plugins
42
- */
43
- export function detectConflicts(
44
- newPluginName: string,
45
- newPkgJson: PluginPackageJson,
46
- existingPlugins: Map<string, PluginPackageJson>,
47
- ): Conflict[] {
48
- const conflicts: Conflict[] = [];
49
-
50
- if (!newPkgJson.omp?.install?.length) {
51
- return conflicts;
52
- }
53
-
54
- // Build a map of existing destinations
55
- const destMap = new Map<string, Array<{ name: string; src: string }>>();
56
-
57
- for (const [name, pkgJson] of existingPlugins) {
58
- if (pkgJson.omp?.install) {
59
- for (const entry of pkgJson.omp.install) {
60
- const existing = destMap.get(entry.dest) || [];
61
- existing.push({ name, src: entry.src });
62
- destMap.set(entry.dest, existing);
63
- }
64
- }
65
- }
66
-
67
- // Check new plugin's destinations
68
- for (const entry of newPkgJson.omp.install) {
69
- const existing = destMap.get(entry.dest);
70
- if (existing && existing.length > 0) {
71
- conflicts.push({
72
- dest: entry.dest,
73
- plugins: [...existing, { name: newPluginName, src: entry.src }],
74
- });
75
- }
76
- }
77
-
78
- return conflicts;
79
- }
80
-
81
- /**
82
- * Detect all conflicts among a set of plugins
83
- */
84
- export function detectAllConflicts(plugins: Map<string, PluginPackageJson>): Conflict[] {
85
- const conflicts: Conflict[] = [];
86
- const destMap = new Map<string, Array<{ name: string; src: string }>>();
87
-
88
- for (const [name, pkgJson] of plugins) {
89
- if (pkgJson.omp?.install) {
90
- for (const entry of pkgJson.omp.install) {
91
- const existing = destMap.get(entry.dest) || [];
92
- existing.push({ name, src: entry.src });
93
- destMap.set(entry.dest, existing);
94
- }
95
- }
96
- }
97
-
98
- // Find destinations with multiple sources
99
- for (const [dest, sources] of destMap) {
100
- if (sources.length > 1) {
101
- conflicts.push({ dest, plugins: sources });
102
- }
103
- }
104
-
105
- return conflicts;
106
- }
107
-
108
- /**
109
- * Format conflicts for display
110
- */
111
- export function formatConflicts(conflicts: Conflict[]): string[] {
112
- return conflicts.map((conflict) => {
113
- const plugins = conflict.plugins.map((p) => p.name).join(" and ");
114
- return `${plugins} both install ${conflict.dest}`;
115
- });
116
- }
package/src/errors.ts DELETED
@@ -1,22 +0,0 @@
1
- import chalk from "chalk";
2
-
3
- /**
4
- * Wraps a command function with consistent error handling.
5
- * - Catches errors and logs user-friendly messages
6
- * - Shows stack trace only when DEBUG env var is set
7
- * - Sets non-zero exit code on error
8
- */
9
- export function withErrorHandling<T extends (...args: any[]) => Promise<void>>(fn: T): T {
10
- return (async (...args: any[]) => {
11
- try {
12
- await fn(...args);
13
- } catch (err) {
14
- const error = err as Error;
15
- console.log(chalk.red(`Error: ${error.message}`));
16
- if (process.env.DEBUG) {
17
- console.log(chalk.dim(error.stack));
18
- }
19
- process.exitCode = 1;
20
- }
21
- }) as T;
22
- }
package/src/index.ts DELETED
@@ -1,46 +0,0 @@
1
- export { createPlugin } from "@omp/commands/create";
2
- export { runDoctor } from "@omp/commands/doctor";
3
- export { disablePlugin, enablePlugin } from "@omp/commands/enable";
4
- export { showInfo } from "@omp/commands/info";
5
- export { initProject } from "@omp/commands/init";
6
- export { installPlugin } from "@omp/commands/install";
7
- export { linkPlugin } from "@omp/commands/link";
8
- export { listPlugins } from "@omp/commands/list";
9
- export { showOutdated } from "@omp/commands/outdated";
10
- export { searchPlugins } from "@omp/commands/search";
11
- export { uninstallPlugin } from "@omp/commands/uninstall";
12
- export { updatePlugin } from "@omp/commands/update";
13
- export { whyFile } from "@omp/commands/why";
14
- export {
15
- detectAllConflicts,
16
- detectConflicts,
17
- formatConflicts,
18
- } from "@omp/conflicts";
19
-
20
- export type {
21
- OmpField,
22
- OmpInstallEntry,
23
- PluginPackageJson,
24
- PluginsJson,
25
- } from "@omp/manifest";
26
-
27
- export {
28
- getInstalledPlugins,
29
- initGlobalPlugins,
30
- loadPluginsJson,
31
- readPluginPackageJson,
32
- savePluginsJson,
33
- } from "@omp/manifest";
34
- export {
35
- npmInfo,
36
- npmInstall,
37
- npmOutdated,
38
- npmSearch,
39
- npmUninstall,
40
- npmUpdate,
41
- } from "@omp/npm";
42
- export {
43
- checkPluginSymlinks,
44
- createPluginSymlinks,
45
- removePluginSymlinks,
46
- } from "@omp/symlinks";
package/src/lock.ts DELETED
@@ -1,46 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
3
- import { dirname, join } from "node:path";
4
- import { PI_CONFIG_DIR, PROJECT_PI_DIR } from "@omp/paths";
5
-
6
- const LOCK_TIMEOUT_MS = 60000; // 1 minute
7
-
8
- export async function acquireLock(global = true): Promise<boolean> {
9
- const lockPath = global ? join(PI_CONFIG_DIR, ".lock") : join(PROJECT_PI_DIR, ".lock");
10
-
11
- try {
12
- await mkdir(dirname(lockPath), { recursive: true });
13
-
14
- // Check for existing lock
15
- if (existsSync(lockPath)) {
16
- const content = await readFile(lockPath, "utf-8");
17
- const { pid, timestamp } = JSON.parse(content);
18
-
19
- // Check if stale (older than timeout)
20
- if (Date.now() - timestamp > LOCK_TIMEOUT_MS) {
21
- // Stale lock, remove it
22
- await rm(lockPath, { force: true });
23
- } else {
24
- // Check if process is still alive
25
- try {
26
- process.kill(pid, 0); // Signal 0 = check existence
27
- return false; // Process alive, can't acquire
28
- } catch {
29
- // Process dead, remove stale lock
30
- await rm(lockPath, { force: true });
31
- }
32
- }
33
- }
34
-
35
- // Create lock
36
- await writeFile(lockPath, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
37
- return true;
38
- } catch {
39
- return false;
40
- }
41
- }
42
-
43
- export async function releaseLock(global = true): Promise<void> {
44
- const lockPath = global ? join(PI_CONFIG_DIR, ".lock") : join(PROJECT_PI_DIR, ".lock");
45
- await rm(lockPath, { force: true });
46
- }
package/src/lockfile.ts DELETED
@@ -1,132 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import { readFile, writeFile } from "node:fs/promises";
3
- import { GLOBAL_LOCK_FILE, PROJECT_PLUGINS_LOCK } from "@omp/paths";
4
- import chalk from "chalk";
5
-
6
- /**
7
- * Lock file schema version
8
- */
9
- export const LOCKFILE_VERSION = 1;
10
-
11
- /**
12
- * Package entry in the lock file
13
- */
14
- export interface LockFilePackage {
15
- version: string;
16
- resolved?: string;
17
- integrity?: string;
18
- dependencies?: Record<string, string>;
19
- }
20
-
21
- /**
22
- * Lock file structure
23
- */
24
- export interface LockFile {
25
- lockfileVersion: number;
26
- packages: Record<string, LockFilePackage>;
27
- }
28
-
29
- /**
30
- * Load and validate a lock file.
31
- *
32
- * Returns null if:
33
- * - File doesn't exist
34
- * - File contains invalid JSON (corrupted)
35
- * - File has invalid/incompatible schema
36
- */
37
- export async function loadLockFile(global = true): Promise<LockFile | null> {
38
- const path = global ? GLOBAL_LOCK_FILE : PROJECT_PLUGINS_LOCK;
39
- try {
40
- if (!existsSync(path)) return null;
41
- const data = await readFile(path, "utf-8");
42
- const parsed = JSON.parse(data);
43
-
44
- // Validate schema
45
- if (typeof parsed.lockfileVersion !== "number" || typeof parsed.packages !== "object") {
46
- console.log(chalk.yellow(`Warning: ${path} has invalid schema, ignoring`));
47
- return null;
48
- }
49
-
50
- // Check for incompatible version
51
- if (parsed.lockfileVersion > LOCKFILE_VERSION) {
52
- console.log(
53
- chalk.yellow(
54
- `Warning: ${path} was created by a newer version of omp (lockfile v${parsed.lockfileVersion}), ignoring`,
55
- ),
56
- );
57
- return null;
58
- }
59
-
60
- return parsed as LockFile;
61
- } catch (err) {
62
- if ((err as Error).name === "SyntaxError") {
63
- console.log(chalk.yellow(`Warning: ${path} is corrupted (invalid JSON), ignoring`));
64
- }
65
- return null;
66
- }
67
- }
68
-
69
- /**
70
- * Save lock file
71
- */
72
- export async function saveLockFile(lockFile: LockFile, global = true): Promise<void> {
73
- const path = global ? GLOBAL_LOCK_FILE : PROJECT_PLUGINS_LOCK;
74
- await writeFile(path, JSON.stringify(lockFile, null, 2));
75
- }
76
-
77
- /**
78
- * Create a new empty lock file
79
- */
80
- export function createLockFile(): LockFile {
81
- return {
82
- lockfileVersion: LOCKFILE_VERSION,
83
- packages: {},
84
- };
85
- }
86
-
87
- /**
88
- * Validate and optionally regenerate a corrupted lock file.
89
- *
90
- * @returns The loaded lock file, a new empty lock file if corrupted/missing, or null if validation fails
91
- */
92
- export async function validateOrRegenerateLockFile(global = true): Promise<LockFile> {
93
- const existing = await loadLockFile(global);
94
- if (existing) {
95
- return existing;
96
- }
97
-
98
- // Lock file is missing or corrupted - create a fresh one
99
- const path = global ? GLOBAL_LOCK_FILE : PROJECT_PLUGINS_LOCK;
100
- if (existsSync(path)) {
101
- console.log(chalk.yellow(`Regenerating corrupted lock file: ${path}`));
102
- }
103
-
104
- return createLockFile();
105
- }
106
-
107
- /**
108
- * Get the locked version for a package, if it exists in the lock file.
109
- */
110
- export async function getLockedVersion(packageName: string, global = true): Promise<string | null> {
111
- const lockFile = await loadLockFile(global);
112
- if (!lockFile) return null;
113
-
114
- const entry = lockFile.packages[packageName];
115
- return entry?.version ?? null;
116
- }
117
-
118
- /**
119
- * Update the lock file with a package's exact version.
120
- */
121
- export async function updateLockFile(packageName: string, version: string, global = true): Promise<void> {
122
- let lockFile = await loadLockFile(global);
123
- if (!lockFile) {
124
- lockFile = createLockFile();
125
- }
126
-
127
- lockFile.packages[packageName] = {
128
- version,
129
- };
130
-
131
- await saveLockFile(lockFile, global);
132
- }