@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
@@ -1,332 +0,0 @@
1
- import { checkbox } from "@inquirer/prompts";
2
- import { loadPluginsJson, readPluginPackageJson, savePluginsJson } from "@omp/manifest";
3
- import { resolveScope } from "@omp/paths";
4
- import { getDefaultFeatures, getRuntimeConfigPath, readRuntimeConfig, writeRuntimeConfig } from "@omp/symlinks";
5
- import chalk from "chalk";
6
-
7
- export interface FeaturesOptions {
8
- global?: boolean;
9
- local?: boolean;
10
- json?: boolean;
11
- enable?: string[];
12
- disable?: string[];
13
- set?: string;
14
- }
15
-
16
- /**
17
- * Interactive feature selection for a plugin
18
- * omp features @oh-my-pi/exa
19
- */
20
- export async function interactiveFeatures(name: string, options: FeaturesOptions = {}): Promise<void> {
21
- const isGlobal = resolveScope(options);
22
- const pluginsJson = await loadPluginsJson(isGlobal);
23
-
24
- // Check if plugin exists
25
- if (!pluginsJson.plugins[name]) {
26
- console.log(chalk.yellow(`Plugin "${name}" is not installed.`));
27
- process.exitCode = 1;
28
- return;
29
- }
30
-
31
- const pkgJson = await readPluginPackageJson(name, isGlobal);
32
- if (!pkgJson) {
33
- console.log(chalk.red(`Could not read package.json for ${name}`));
34
- process.exitCode = 1;
35
- return;
36
- }
37
-
38
- const features = pkgJson.omp?.features;
39
- if (!features || Object.keys(features).length === 0) {
40
- console.log(chalk.yellow(`Plugin "${name}" has no configurable features.`));
41
- return;
42
- }
43
-
44
- // Get runtime config path
45
- const runtimePath = getRuntimeConfigPath(pkgJson, isGlobal);
46
- if (!runtimePath) {
47
- console.log(chalk.yellow(`Plugin "${name}" does not have a runtime.json config file.`));
48
- return;
49
- }
50
-
51
- // Determine currently enabled features:
52
- // 1. Check plugins.json config (source of truth after omp features changes)
53
- // 2. Fall back to runtime.json
54
- // 3. Fall back to plugin defaults
55
- const pluginConfig = pluginsJson.config?.[name];
56
- let enabledFeatures: string[];
57
- if (Array.isArray(pluginConfig?.features)) {
58
- enabledFeatures = pluginConfig.features;
59
- } else {
60
- const runtimeConfig = readRuntimeConfig(runtimePath);
61
- enabledFeatures = runtimeConfig.features ?? getDefaultFeatures(features);
62
- }
63
-
64
- // JSON output mode - just list
65
- if (options.json) {
66
- console.log(
67
- JSON.stringify(
68
- {
69
- plugin: name,
70
- features: Object.entries(features).map(([fname, fdef]) => ({
71
- name: fname,
72
- enabled: enabledFeatures.includes(fname),
73
- default: fdef.default !== false,
74
- description: fdef.description,
75
- variables: fdef.variables ? Object.keys(fdef.variables) : [],
76
- })),
77
- },
78
- null,
79
- 2,
80
- ),
81
- );
82
- return;
83
- }
84
-
85
- // Non-interactive mode - just list
86
- if (!process.stdout.isTTY || !process.stdin.isTTY) {
87
- listFeaturesNonInteractive(name, features, enabledFeatures);
88
- return;
89
- }
90
-
91
- // Interactive mode - checkbox prompt
92
- console.log(chalk.bold(`\nConfigure features for ${name}:\n`));
93
-
94
- const choices = Object.entries(features).map(([fname, fdef]) => {
95
- const optIn = fdef.default === false ? chalk.dim(" (opt-in)") : "";
96
- const desc = fdef.description ? chalk.dim(` - ${fdef.description}`) : "";
97
- return {
98
- name: `${fname}${optIn}${desc}`,
99
- value: fname,
100
- checked: enabledFeatures.includes(fname),
101
- };
102
- });
103
-
104
- try {
105
- const selected = await checkbox({
106
- message: "Select features to enable:",
107
- choices,
108
- pageSize: 15,
109
- });
110
-
111
- // Apply changes
112
- await applyFeatureChanges(name, runtimePath, features, enabledFeatures, selected, isGlobal);
113
- } catch (_err) {
114
- // User cancelled (Ctrl+C)
115
- console.log(chalk.dim("\nCancelled."));
116
- }
117
- }
118
-
119
- /**
120
- * Non-interactive feature listing
121
- */
122
- function listFeaturesNonInteractive(
123
- name: string,
124
- features: Record<
125
- string,
126
- {
127
- description?: string;
128
- default?: boolean;
129
- variables?: Record<string, unknown>;
130
- }
131
- >,
132
- enabledFeatures: string[],
133
- ): void {
134
- console.log(chalk.bold(`\nFeatures for ${name}:\n`));
135
-
136
- for (const [fname, fdef] of Object.entries(features)) {
137
- const isEnabled = enabledFeatures.includes(fname);
138
- const icon = isEnabled ? chalk.green("✓") : chalk.gray("○");
139
- const defaultStr = fdef.default === false ? chalk.dim(" (opt-in)") : "";
140
-
141
- console.log(`${icon} ${chalk.bold(fname)}${defaultStr}`);
142
- if (fdef.description) {
143
- console.log(chalk.dim(` ${fdef.description}`));
144
- }
145
- if (fdef.variables && Object.keys(fdef.variables).length > 0) {
146
- console.log(chalk.dim(` Variables: ${Object.keys(fdef.variables).join(", ")}`));
147
- }
148
- }
149
-
150
- console.log();
151
- console.log(chalk.dim(`Configure with: omp features ${name} --enable <feature> --disable <feature>`));
152
- console.log(chalk.dim(`Or set exactly: omp features ${name} --set feature1,feature2`));
153
- }
154
-
155
- /**
156
- * Apply feature changes - update both plugins.json (for config/env) and runtime.json (for runtime)
157
- */
158
- async function applyFeatureChanges(
159
- name: string,
160
- runtimePath: string,
161
- _features: Record<
162
- string,
163
- {
164
- description?: string;
165
- default?: boolean;
166
- variables?: Record<string, unknown>;
167
- }
168
- >,
169
- currentlyEnabled: string[],
170
- newEnabled: string[],
171
- isGlobal: boolean,
172
- ): Promise<void> {
173
- // Compute what changed
174
- const toDisable = currentlyEnabled.filter((f) => !newEnabled.includes(f));
175
- const toEnable = newEnabled.filter((f) => !currentlyEnabled.includes(f));
176
-
177
- if (toDisable.length === 0 && toEnable.length === 0) {
178
- console.log(chalk.yellow("\nNo changes to feature configuration."));
179
- return;
180
- }
181
-
182
- console.log(chalk.blue(`\nApplying changes...`));
183
-
184
- if (toDisable.length > 0) {
185
- console.log(chalk.dim(` Disabling: ${toDisable.join(", ")}`));
186
- }
187
- if (toEnable.length > 0) {
188
- console.log(chalk.dim(` Enabling: ${toEnable.join(", ")}`));
189
- }
190
-
191
- // Update plugins.json (source of truth for config/env commands)
192
- const pluginsJson = await loadPluginsJson(isGlobal);
193
- if (!pluginsJson.config) pluginsJson.config = {};
194
- if (!pluginsJson.config[name]) pluginsJson.config[name] = {};
195
- pluginsJson.config[name].features = newEnabled;
196
- await savePluginsJson(pluginsJson, isGlobal);
197
-
198
- // Also write to runtime.json (for runtime feature detection)
199
- await writeRuntimeConfig(runtimePath, { features: newEnabled });
200
-
201
- console.log(chalk.green(`\n✓ Features updated`));
202
- if (newEnabled.length > 0) {
203
- console.log(chalk.dim(` Enabled: ${newEnabled.join(", ")}`));
204
- } else {
205
- console.log(chalk.dim(` Enabled: none`));
206
- }
207
- }
208
-
209
- /**
210
- * Configure features for an installed plugin via CLI flags
211
- * omp features @oh-my-pi/exa --enable websets --disable search
212
- * omp features @oh-my-pi/exa --set search,websets
213
- */
214
- export async function configureFeatures(name: string, options: FeaturesOptions = {}): Promise<void> {
215
- const isGlobal = resolveScope(options);
216
- const pluginsJson = await loadPluginsJson(isGlobal);
217
-
218
- // Check if plugin exists
219
- if (!pluginsJson.plugins[name]) {
220
- console.log(chalk.yellow(`Plugin "${name}" is not installed.`));
221
- process.exitCode = 1;
222
- return;
223
- }
224
-
225
- const pkgJson = await readPluginPackageJson(name, isGlobal);
226
- if (!pkgJson) {
227
- console.log(chalk.red(`Could not read package.json for ${name}`));
228
- process.exitCode = 1;
229
- return;
230
- }
231
-
232
- const features = pkgJson.omp?.features;
233
- if (!features || Object.keys(features).length === 0) {
234
- console.log(chalk.yellow(`Plugin "${name}" has no configurable features.`));
235
- process.exitCode = 1;
236
- return;
237
- }
238
-
239
- const allFeatureNames = Object.keys(features);
240
-
241
- // Get runtime config path
242
- const runtimePath = getRuntimeConfigPath(pkgJson, isGlobal);
243
- if (!runtimePath) {
244
- console.log(chalk.yellow(`Plugin "${name}" does not have a runtime.json config file.`));
245
- process.exitCode = 1;
246
- return;
247
- }
248
-
249
- // Determine currently enabled features:
250
- // 1. Check plugins.json config (source of truth after omp features changes)
251
- // 2. Fall back to runtime.json
252
- // 3. Fall back to plugin defaults
253
- const pluginConfig = pluginsJson.config?.[name];
254
- let currentlyEnabled: string[];
255
- if (Array.isArray(pluginConfig?.features)) {
256
- currentlyEnabled = pluginConfig.features;
257
- } else {
258
- const runtimeConfig = readRuntimeConfig(runtimePath);
259
- currentlyEnabled = runtimeConfig.features ?? getDefaultFeatures(features);
260
- }
261
-
262
- let newEnabled: string[];
263
-
264
- // Handle --set (explicit list)
265
- if (options.set !== undefined) {
266
- if (options.set === "*") {
267
- newEnabled = allFeatureNames;
268
- } else if (options.set === "") {
269
- newEnabled = [];
270
- } else {
271
- newEnabled = options.set
272
- .split(",")
273
- .map((f) => f.trim())
274
- .filter(Boolean);
275
- // Validate
276
- for (const f of newEnabled) {
277
- if (!features[f]) {
278
- console.log(chalk.red(`Unknown feature "${f}". Available: ${allFeatureNames.join(", ")}`));
279
- process.exitCode = 1;
280
- return;
281
- }
282
- }
283
- }
284
- } else {
285
- // Handle --enable and --disable
286
- newEnabled = [...currentlyEnabled];
287
-
288
- if (options.enable) {
289
- for (const f of options.enable) {
290
- if (!features[f]) {
291
- console.log(chalk.red(`Unknown feature "${f}". Available: ${allFeatureNames.join(", ")}`));
292
- process.exitCode = 1;
293
- return;
294
- }
295
- if (!newEnabled.includes(f)) {
296
- newEnabled.push(f);
297
- }
298
- }
299
- }
300
-
301
- if (options.disable) {
302
- for (const f of options.disable) {
303
- if (!features[f]) {
304
- console.log(chalk.red(`Unknown feature "${f}". Available: ${allFeatureNames.join(", ")}`));
305
- process.exitCode = 1;
306
- return;
307
- }
308
- newEnabled = newEnabled.filter((e) => e !== f);
309
- }
310
- }
311
- }
312
-
313
- await applyFeatureChanges(name, runtimePath, features, currentlyEnabled, newEnabled, isGlobal);
314
-
315
- if (options.json) {
316
- console.log(JSON.stringify({ plugin: name, enabled: newEnabled }, null, 2));
317
- }
318
- }
319
-
320
- /**
321
- * Main features command handler
322
- * Routes to interactive or configure based on options
323
- */
324
- export async function featuresCommand(name: string, options: FeaturesOptions = {}): Promise<void> {
325
- // If any modification options are passed, configure via CLI
326
- if (options.enable || options.disable || options.set !== undefined) {
327
- await configureFeatures(name, options);
328
- } else {
329
- // Otherwise, show interactive UI (or list in non-TTY mode)
330
- await interactiveFeatures(name, options);
331
- }
332
- }
@@ -1,120 +0,0 @@
1
- import { npmInfo } from "@omp/npm";
2
- import chalk from "chalk";
3
-
4
- export interface InfoOptions {
5
- json?: boolean;
6
- versions?: boolean;
7
- allVersions?: boolean;
8
- }
9
-
10
- /**
11
- * Show detailed info about a package before install
12
- */
13
- export async function showInfo(packageName: string, options: InfoOptions = {}): Promise<void> {
14
- console.log(chalk.blue(`Fetching info for ${packageName}...`));
15
-
16
- try {
17
- const info = await npmInfo(packageName);
18
-
19
- if (!info) {
20
- console.log(chalk.red(`Package not found: ${packageName}`));
21
- process.exitCode = 1;
22
- return;
23
- }
24
-
25
- if (options.json) {
26
- console.log(JSON.stringify(info, null, 2));
27
- return;
28
- }
29
-
30
- console.log();
31
- console.log(chalk.bold.green(info.name) + chalk.dim(` v${info.version}`));
32
- console.log();
33
-
34
- if (info.description) {
35
- console.log(chalk.white(info.description));
36
- console.log();
37
- }
38
-
39
- // Author
40
- if (info.author) {
41
- const authorStr =
42
- typeof info.author === "string"
43
- ? info.author
44
- : `${info.author.name}${info.author.email ? ` <${info.author.email}>` : ""}`;
45
- console.log(chalk.dim("author: ") + authorStr);
46
- }
47
-
48
- // Homepage
49
- if (info.homepage) {
50
- console.log(chalk.dim("homepage: ") + info.homepage);
51
- }
52
-
53
- // Repository
54
- if (info.repository) {
55
- const repoUrl = typeof info.repository === "string" ? info.repository : info.repository.url;
56
- console.log(chalk.dim("repo: ") + repoUrl);
57
- }
58
-
59
- // Keywords
60
- if (info.keywords?.length) {
61
- console.log(chalk.dim("keywords: ") + info.keywords.join(", "));
62
- }
63
-
64
- // Dependencies
65
- if (info.dependencies && Object.keys(info.dependencies).length > 0) {
66
- console.log(chalk.dim("\ndependencies:"));
67
- for (const [depName, depVersion] of Object.entries(info.dependencies)) {
68
- console.log(chalk.dim(` ${depName}: ${depVersion}`));
69
- }
70
- }
71
-
72
- // Is it an omp plugin?
73
- const isOmpPlugin = info.keywords?.includes("omp-plugin");
74
- if (isOmpPlugin) {
75
- console.log(chalk.green("\n✓ This is an omp plugin"));
76
- } else {
77
- console.log(chalk.yellow("\n⚠ This package does not have the omp-plugin keyword"));
78
- console.log(chalk.dim(" It may work, but might not have omp.install configuration"));
79
- }
80
-
81
- // Show what files will be installed
82
- if (info.omp?.install?.length) {
83
- console.log(chalk.dim("\nFiles to install:"));
84
- for (const entry of info.omp.install) {
85
- console.log(chalk.dim(` ${entry.src} → ${entry.dest}`));
86
- }
87
- }
88
-
89
- // Versions
90
- if (options.versions || options.allVersions) {
91
- if (info["dist-tags"]) {
92
- console.log(chalk.dim("\ndist-tags:"));
93
- for (const [tag, version] of Object.entries(info["dist-tags"])) {
94
- console.log(chalk.dim(` ${tag}: `) + version);
95
- }
96
- }
97
-
98
- if (info.versions?.length) {
99
- console.log(chalk.dim("\nall versions:"));
100
- if (options.allVersions) {
101
- // Show all versions
102
- console.log(chalk.dim(` ${info.versions.join(", ")}`));
103
- } else {
104
- // Show last 10
105
- const versionsToShow = info.versions.slice(-10);
106
- console.log(chalk.dim(` ${versionsToShow.join(", ")}`));
107
- if (info.versions.length > 10) {
108
- console.log(chalk.dim(` ... and ${info.versions.length - 10} more (use --all-versions to see all)`));
109
- }
110
- }
111
- }
112
- }
113
-
114
- console.log();
115
- console.log(chalk.dim(`Install with: omp install ${packageName}`));
116
- } catch (err) {
117
- console.log(chalk.red(`Error fetching info: ${(err as Error).message}`));
118
- process.exitCode = 1;
119
- }
120
- }
@@ -1,60 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import { mkdir, writeFile } from "node:fs/promises";
3
- import { PROJECT_PI_DIR, PROJECT_PLUGINS_JSON } from "@omp/paths";
4
- import chalk from "chalk";
5
-
6
- /**
7
- * Format permission-related errors with actionable guidance
8
- */
9
- function formatPermissionError(err: NodeJS.ErrnoException, path: string): string {
10
- if (err.code === "EACCES" || err.code === "EPERM") {
11
- return `Permission denied: Cannot write to ${path}. Check directory permissions or run with appropriate privileges.`;
12
- }
13
- return err.message;
14
- }
15
-
16
- export interface InitOptions {
17
- force?: boolean;
18
- }
19
-
20
- /**
21
- * Initialize .pi/plugins.json in current project
22
- */
23
- export async function initProject(options: InitOptions = {}): Promise<void> {
24
- // Check if already exists
25
- if (existsSync(PROJECT_PLUGINS_JSON) && !options.force) {
26
- console.log(chalk.yellow(`${PROJECT_PLUGINS_JSON} already exists.`));
27
- console.log(chalk.dim("Use --force to overwrite"));
28
- process.exitCode = 1;
29
- return;
30
- }
31
-
32
- try {
33
- // Create .pi directory
34
- await mkdir(PROJECT_PI_DIR, { recursive: true });
35
-
36
- // Create plugins.json
37
- const pluginsJson = {
38
- plugins: {},
39
- disabled: [],
40
- };
41
-
42
- await writeFile(PROJECT_PLUGINS_JSON, JSON.stringify(pluginsJson, null, 2));
43
-
44
- console.log(chalk.green(`✓ Created ${PROJECT_PLUGINS_JSON}`));
45
- console.log();
46
- console.log(chalk.dim("Next steps:"));
47
- console.log(chalk.dim(" 1. Add plugins: omp install <package> --save"));
48
- console.log(chalk.dim(" 2. Or edit plugins.json directly"));
49
- console.log(chalk.dim(" 3. Run: omp install (to install all)"));
50
- } catch (err) {
51
- const error = err as NodeJS.ErrnoException;
52
- if (error.code === "EACCES" || error.code === "EPERM") {
53
- console.log(chalk.red(formatPermissionError(error, PROJECT_PI_DIR)));
54
- console.log(chalk.dim(" Check directory permissions or run with appropriate privileges."));
55
- } else {
56
- console.log(chalk.red(`Error initializing project: ${error.message}`));
57
- }
58
- process.exitCode = 1;
59
- }
60
- }