@oh-my-pi/cli 0.1.0 → 0.2.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 (78) hide show
  1. package/.github/icon.png +0 -0
  2. package/.github/logo.png +0 -0
  3. package/.github/workflows/publish.yml +1 -1
  4. package/LICENSE +21 -0
  5. package/README.md +131 -145
  6. package/biome.json +1 -1
  7. package/dist/cli.js +2032 -1136
  8. package/dist/commands/create.d.ts.map +1 -1
  9. package/dist/commands/doctor.d.ts +1 -0
  10. package/dist/commands/doctor.d.ts.map +1 -1
  11. package/dist/commands/enable.d.ts +1 -0
  12. package/dist/commands/enable.d.ts.map +1 -1
  13. package/dist/commands/info.d.ts +1 -0
  14. package/dist/commands/info.d.ts.map +1 -1
  15. package/dist/commands/init.d.ts.map +1 -1
  16. package/dist/commands/install.d.ts +1 -0
  17. package/dist/commands/install.d.ts.map +1 -1
  18. package/dist/commands/link.d.ts +2 -0
  19. package/dist/commands/link.d.ts.map +1 -1
  20. package/dist/commands/list.d.ts +1 -0
  21. package/dist/commands/list.d.ts.map +1 -1
  22. package/dist/commands/outdated.d.ts +1 -0
  23. package/dist/commands/outdated.d.ts.map +1 -1
  24. package/dist/commands/search.d.ts.map +1 -1
  25. package/dist/commands/uninstall.d.ts +1 -0
  26. package/dist/commands/uninstall.d.ts.map +1 -1
  27. package/dist/commands/update.d.ts +1 -0
  28. package/dist/commands/update.d.ts.map +1 -1
  29. package/dist/commands/why.d.ts +1 -0
  30. package/dist/commands/why.d.ts.map +1 -1
  31. package/dist/conflicts.d.ts +9 -1
  32. package/dist/conflicts.d.ts.map +1 -1
  33. package/dist/errors.d.ts +8 -0
  34. package/dist/errors.d.ts.map +1 -0
  35. package/dist/index.d.ts +19 -19
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/lock.d.ts +3 -0
  38. package/dist/lock.d.ts.map +1 -0
  39. package/dist/lockfile.d.ts +52 -0
  40. package/dist/lockfile.d.ts.map +1 -0
  41. package/dist/manifest.d.ts +5 -0
  42. package/dist/manifest.d.ts.map +1 -1
  43. package/dist/migrate.d.ts.map +1 -1
  44. package/dist/npm.d.ts +14 -2
  45. package/dist/npm.d.ts.map +1 -1
  46. package/dist/paths.d.ts +34 -2
  47. package/dist/paths.d.ts.map +1 -1
  48. package/dist/symlinks.d.ts +10 -4
  49. package/dist/symlinks.d.ts.map +1 -1
  50. package/package.json +7 -2
  51. package/plugins/metal-theme/package.json +6 -1
  52. package/plugins/subagents/package.json +6 -1
  53. package/src/cli.ts +69 -43
  54. package/src/commands/create.ts +51 -1
  55. package/src/commands/doctor.ts +95 -7
  56. package/src/commands/enable.ts +25 -8
  57. package/src/commands/info.ts +41 -5
  58. package/src/commands/init.ts +20 -2
  59. package/src/commands/install.ts +266 -52
  60. package/src/commands/link.ts +60 -9
  61. package/src/commands/list.ts +10 -5
  62. package/src/commands/outdated.ts +17 -6
  63. package/src/commands/search.ts +20 -3
  64. package/src/commands/uninstall.ts +57 -6
  65. package/src/commands/update.ts +67 -9
  66. package/src/commands/why.ts +47 -16
  67. package/src/conflicts.ts +33 -1
  68. package/src/errors.ts +22 -0
  69. package/src/index.ts +19 -25
  70. package/src/lock.ts +46 -0
  71. package/src/lockfile.ts +132 -0
  72. package/src/manifest.ts +143 -35
  73. package/src/migrate.ts +14 -3
  74. package/src/npm.ts +74 -18
  75. package/src/paths.ts +77 -9
  76. package/src/symlinks.ts +134 -17
  77. package/tsconfig.json +7 -3
  78. package/CHECK.md +0 -352
package/dist/cli.js CHANGED
@@ -17,203 +17,826 @@ var __toESM = (mod, isNodeMode, target) => {
17
17
  return to;
18
18
  };
19
19
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
20
+ var __export = (target, all) => {
21
+ for (var name in all)
22
+ __defProp(target, name, {
23
+ get: all[name],
24
+ enumerable: true,
25
+ configurable: true,
26
+ set: (newValue) => all[name] = () => newValue
27
+ });
28
+ };
29
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
20
30
  var __require = import.meta.require;
21
31
 
22
- // node_modules/commander/lib/error.js
23
- var require_error = __commonJS((exports) => {
24
- class CommanderError extends Error {
25
- constructor(exitCode, code, message) {
26
- super(message);
27
- Error.captureStackTrace(this, this.constructor);
28
- this.name = this.constructor.name;
29
- this.code = code;
30
- this.exitCode = exitCode;
31
- this.nestedError = undefined;
32
+ // node_modules/chalk/source/vendor/ansi-styles/index.js
33
+ function assembleStyles() {
34
+ const codes = new Map;
35
+ for (const [groupName, group] of Object.entries(styles)) {
36
+ for (const [styleName, style] of Object.entries(group)) {
37
+ styles[styleName] = {
38
+ open: `\x1B[${style[0]}m`,
39
+ close: `\x1B[${style[1]}m`
40
+ };
41
+ group[styleName] = styles[styleName];
42
+ codes.set(style[0], style[1]);
32
43
  }
44
+ Object.defineProperty(styles, groupName, {
45
+ value: group,
46
+ enumerable: false
47
+ });
33
48
  }
34
-
35
- class InvalidArgumentError extends CommanderError {
36
- constructor(message) {
37
- super(1, "commander.invalidArgument", message);
38
- Error.captureStackTrace(this, this.constructor);
39
- this.name = this.constructor.name;
49
+ Object.defineProperty(styles, "codes", {
50
+ value: codes,
51
+ enumerable: false
52
+ });
53
+ styles.color.close = "\x1B[39m";
54
+ styles.bgColor.close = "\x1B[49m";
55
+ styles.color.ansi = wrapAnsi16();
56
+ styles.color.ansi256 = wrapAnsi256();
57
+ styles.color.ansi16m = wrapAnsi16m();
58
+ styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
59
+ styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
60
+ styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
61
+ Object.defineProperties(styles, {
62
+ rgbToAnsi256: {
63
+ value(red, green, blue) {
64
+ if (red === green && green === blue) {
65
+ if (red < 8) {
66
+ return 16;
67
+ }
68
+ if (red > 248) {
69
+ return 231;
70
+ }
71
+ return Math.round((red - 8) / 247 * 24) + 232;
72
+ }
73
+ return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
74
+ },
75
+ enumerable: false
76
+ },
77
+ hexToRgb: {
78
+ value(hex) {
79
+ const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
80
+ if (!matches) {
81
+ return [0, 0, 0];
82
+ }
83
+ let [colorString] = matches;
84
+ if (colorString.length === 3) {
85
+ colorString = [...colorString].map((character) => character + character).join("");
86
+ }
87
+ const integer = Number.parseInt(colorString, 16);
88
+ return [
89
+ integer >> 16 & 255,
90
+ integer >> 8 & 255,
91
+ integer & 255
92
+ ];
93
+ },
94
+ enumerable: false
95
+ },
96
+ hexToAnsi256: {
97
+ value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
98
+ enumerable: false
99
+ },
100
+ ansi256ToAnsi: {
101
+ value(code) {
102
+ if (code < 8) {
103
+ return 30 + code;
104
+ }
105
+ if (code < 16) {
106
+ return 90 + (code - 8);
107
+ }
108
+ let red;
109
+ let green;
110
+ let blue;
111
+ if (code >= 232) {
112
+ red = ((code - 232) * 10 + 8) / 255;
113
+ green = red;
114
+ blue = red;
115
+ } else {
116
+ code -= 16;
117
+ const remainder = code % 36;
118
+ red = Math.floor(code / 36) / 5;
119
+ green = Math.floor(remainder / 6) / 5;
120
+ blue = remainder % 6 / 5;
121
+ }
122
+ const value = Math.max(red, green, blue) * 2;
123
+ if (value === 0) {
124
+ return 30;
125
+ }
126
+ let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
127
+ if (value === 2) {
128
+ result += 60;
129
+ }
130
+ return result;
131
+ },
132
+ enumerable: false
133
+ },
134
+ rgbToAnsi: {
135
+ value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
136
+ enumerable: false
137
+ },
138
+ hexToAnsi: {
139
+ value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
140
+ enumerable: false
40
141
  }
41
- }
42
- exports.CommanderError = CommanderError;
43
- exports.InvalidArgumentError = InvalidArgumentError;
142
+ });
143
+ return styles;
144
+ }
145
+ var ANSI_BACKGROUND_OFFSET = 10, wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`, wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`, wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`, styles, modifierNames, foregroundColorNames, backgroundColorNames, colorNames, ansiStyles, ansi_styles_default;
146
+ var init_ansi_styles = __esm(() => {
147
+ styles = {
148
+ modifier: {
149
+ reset: [0, 0],
150
+ bold: [1, 22],
151
+ dim: [2, 22],
152
+ italic: [3, 23],
153
+ underline: [4, 24],
154
+ overline: [53, 55],
155
+ inverse: [7, 27],
156
+ hidden: [8, 28],
157
+ strikethrough: [9, 29]
158
+ },
159
+ color: {
160
+ black: [30, 39],
161
+ red: [31, 39],
162
+ green: [32, 39],
163
+ yellow: [33, 39],
164
+ blue: [34, 39],
165
+ magenta: [35, 39],
166
+ cyan: [36, 39],
167
+ white: [37, 39],
168
+ blackBright: [90, 39],
169
+ gray: [90, 39],
170
+ grey: [90, 39],
171
+ redBright: [91, 39],
172
+ greenBright: [92, 39],
173
+ yellowBright: [93, 39],
174
+ blueBright: [94, 39],
175
+ magentaBright: [95, 39],
176
+ cyanBright: [96, 39],
177
+ whiteBright: [97, 39]
178
+ },
179
+ bgColor: {
180
+ bgBlack: [40, 49],
181
+ bgRed: [41, 49],
182
+ bgGreen: [42, 49],
183
+ bgYellow: [43, 49],
184
+ bgBlue: [44, 49],
185
+ bgMagenta: [45, 49],
186
+ bgCyan: [46, 49],
187
+ bgWhite: [47, 49],
188
+ bgBlackBright: [100, 49],
189
+ bgGray: [100, 49],
190
+ bgGrey: [100, 49],
191
+ bgRedBright: [101, 49],
192
+ bgGreenBright: [102, 49],
193
+ bgYellowBright: [103, 49],
194
+ bgBlueBright: [104, 49],
195
+ bgMagentaBright: [105, 49],
196
+ bgCyanBright: [106, 49],
197
+ bgWhiteBright: [107, 49]
198
+ }
199
+ };
200
+ modifierNames = Object.keys(styles.modifier);
201
+ foregroundColorNames = Object.keys(styles.color);
202
+ backgroundColorNames = Object.keys(styles.bgColor);
203
+ colorNames = [...foregroundColorNames, ...backgroundColorNames];
204
+ ansiStyles = assembleStyles();
205
+ ansi_styles_default = ansiStyles;
44
206
  });
45
207
 
46
- // node_modules/commander/lib/argument.js
47
- var require_argument = __commonJS((exports) => {
48
- var { InvalidArgumentError } = require_error();
49
-
50
- class Argument {
51
- constructor(name, description) {
52
- this.description = description || "";
53
- this.variadic = false;
54
- this.parseArg = undefined;
55
- this.defaultValue = undefined;
56
- this.defaultValueDescription = undefined;
57
- this.argChoices = undefined;
58
- switch (name[0]) {
59
- case "<":
60
- this.required = true;
61
- this._name = name.slice(1, -1);
62
- break;
63
- case "[":
64
- this.required = false;
65
- this._name = name.slice(1, -1);
66
- break;
67
- default:
68
- this.required = true;
69
- this._name = name;
70
- break;
71
- }
72
- if (this._name.length > 3 && this._name.slice(-3) === "...") {
73
- this.variadic = true;
74
- this._name = this._name.slice(0, -3);
75
- }
76
- }
77
- name() {
78
- return this._name;
79
- }
80
- _concatValue(value, previous) {
81
- if (previous === this.defaultValue || !Array.isArray(previous)) {
82
- return [value];
83
- }
84
- return previous.concat(value);
85
- }
86
- default(value, description) {
87
- this.defaultValue = value;
88
- this.defaultValueDescription = description;
89
- return this;
90
- }
91
- argParser(fn) {
92
- this.parseArg = fn;
93
- return this;
208
+ // node_modules/chalk/source/vendor/supports-color/index.js
209
+ import process2 from "process";
210
+ import os from "os";
211
+ import tty from "tty";
212
+ function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process2.argv) {
213
+ const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
214
+ const position = argv.indexOf(prefix + flag);
215
+ const terminatorPosition = argv.indexOf("--");
216
+ return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
217
+ }
218
+ function envForceColor() {
219
+ if ("FORCE_COLOR" in env) {
220
+ if (env.FORCE_COLOR === "true") {
221
+ return 1;
94
222
  }
95
- choices(values) {
96
- this.argChoices = values.slice();
97
- this.parseArg = (arg, previous) => {
98
- if (!this.argChoices.includes(arg)) {
99
- throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(", ")}.`);
100
- }
101
- if (this.variadic) {
102
- return this._concatValue(arg, previous);
103
- }
104
- return arg;
105
- };
106
- return this;
223
+ if (env.FORCE_COLOR === "false") {
224
+ return 0;
107
225
  }
108
- argRequired() {
109
- this.required = true;
110
- return this;
226
+ return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
227
+ }
228
+ }
229
+ function translateLevel(level) {
230
+ if (level === 0) {
231
+ return false;
232
+ }
233
+ return {
234
+ level,
235
+ hasBasic: true,
236
+ has256: level >= 2,
237
+ has16m: level >= 3
238
+ };
239
+ }
240
+ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
241
+ const noFlagForceColor = envForceColor();
242
+ if (noFlagForceColor !== undefined) {
243
+ flagForceColor = noFlagForceColor;
244
+ }
245
+ const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
246
+ if (forceColor === 0) {
247
+ return 0;
248
+ }
249
+ if (sniffFlags) {
250
+ if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
251
+ return 3;
111
252
  }
112
- argOptional() {
113
- this.required = false;
114
- return this;
253
+ if (hasFlag("color=256")) {
254
+ return 2;
115
255
  }
116
256
  }
117
- function humanReadableArgName(arg) {
118
- const nameOutput = arg.name() + (arg.variadic === true ? "..." : "");
119
- return arg.required ? "<" + nameOutput + ">" : "[" + nameOutput + "]";
257
+ if ("TF_BUILD" in env && "AGENT_NAME" in env) {
258
+ return 1;
120
259
  }
121
- exports.Argument = Argument;
122
- exports.humanReadableArgName = humanReadableArgName;
123
- });
124
-
125
- // node_modules/commander/lib/help.js
126
- var require_help = __commonJS((exports) => {
127
- var { humanReadableArgName } = require_argument();
128
-
129
- class Help {
130
- constructor() {
131
- this.helpWidth = undefined;
132
- this.sortSubcommands = false;
133
- this.sortOptions = false;
134
- this.showGlobalOptions = false;
135
- }
136
- visibleCommands(cmd) {
137
- const visibleCommands = cmd.commands.filter((cmd2) => !cmd2._hidden);
138
- const helpCommand = cmd._getHelpCommand();
139
- if (helpCommand && !helpCommand._hidden) {
140
- visibleCommands.push(helpCommand);
141
- }
142
- if (this.sortSubcommands) {
143
- visibleCommands.sort((a, b) => {
144
- return a.name().localeCompare(b.name());
145
- });
146
- }
147
- return visibleCommands;
260
+ if (haveStream && !streamIsTTY && forceColor === undefined) {
261
+ return 0;
262
+ }
263
+ const min = forceColor || 0;
264
+ if (env.TERM === "dumb") {
265
+ return min;
266
+ }
267
+ if (process2.platform === "win32") {
268
+ const osRelease = os.release().split(".");
269
+ if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
270
+ return Number(osRelease[2]) >= 14931 ? 3 : 2;
148
271
  }
149
- compareOptions(a, b) {
150
- const getSortKey = (option) => {
151
- return option.short ? option.short.replace(/^-/, "") : option.long.replace(/^--/, "");
152
- };
153
- return getSortKey(a).localeCompare(getSortKey(b));
272
+ return 1;
273
+ }
274
+ if ("CI" in env) {
275
+ if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => (key in env))) {
276
+ return 3;
154
277
  }
155
- visibleOptions(cmd) {
156
- const visibleOptions = cmd.options.filter((option) => !option.hidden);
157
- const helpOption = cmd._getHelpOption();
158
- if (helpOption && !helpOption.hidden) {
159
- const removeShort = helpOption.short && cmd._findOption(helpOption.short);
160
- const removeLong = helpOption.long && cmd._findOption(helpOption.long);
161
- if (!removeShort && !removeLong) {
162
- visibleOptions.push(helpOption);
163
- } else if (helpOption.long && !removeLong) {
164
- visibleOptions.push(cmd.createOption(helpOption.long, helpOption.description));
165
- } else if (helpOption.short && !removeShort) {
166
- visibleOptions.push(cmd.createOption(helpOption.short, helpOption.description));
167
- }
168
- }
169
- if (this.sortOptions) {
170
- visibleOptions.sort(this.compareOptions);
171
- }
172
- return visibleOptions;
278
+ if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => (sign in env)) || env.CI_NAME === "codeship") {
279
+ return 1;
173
280
  }
174
- visibleGlobalOptions(cmd) {
175
- if (!this.showGlobalOptions)
176
- return [];
177
- const globalOptions = [];
178
- for (let ancestorCmd = cmd.parent;ancestorCmd; ancestorCmd = ancestorCmd.parent) {
179
- const visibleOptions = ancestorCmd.options.filter((option) => !option.hidden);
180
- globalOptions.push(...visibleOptions);
281
+ return min;
282
+ }
283
+ if ("TEAMCITY_VERSION" in env) {
284
+ return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
285
+ }
286
+ if (env.COLORTERM === "truecolor") {
287
+ return 3;
288
+ }
289
+ if (env.TERM === "xterm-kitty") {
290
+ return 3;
291
+ }
292
+ if (env.TERM === "xterm-ghostty") {
293
+ return 3;
294
+ }
295
+ if (env.TERM === "wezterm") {
296
+ return 3;
297
+ }
298
+ if ("TERM_PROGRAM" in env) {
299
+ const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
300
+ switch (env.TERM_PROGRAM) {
301
+ case "iTerm.app": {
302
+ return version >= 3 ? 3 : 2;
181
303
  }
182
- if (this.sortOptions) {
183
- globalOptions.sort(this.compareOptions);
304
+ case "Apple_Terminal": {
305
+ return 2;
184
306
  }
185
- return globalOptions;
186
307
  }
187
- visibleArguments(cmd) {
188
- if (cmd._argsDescription) {
189
- cmd.registeredArguments.forEach((argument) => {
190
- argument.description = argument.description || cmd._argsDescription[argument.name()] || "";
191
- });
192
- }
193
- if (cmd.registeredArguments.find((argument) => argument.description)) {
194
- return cmd.registeredArguments;
195
- }
196
- return [];
308
+ }
309
+ if (/-256(color)?$/i.test(env.TERM)) {
310
+ return 2;
311
+ }
312
+ if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
313
+ return 1;
314
+ }
315
+ if ("COLORTERM" in env) {
316
+ return 1;
317
+ }
318
+ return min;
319
+ }
320
+ function createSupportsColor(stream, options = {}) {
321
+ const level = _supportsColor(stream, {
322
+ streamIsTTY: stream && stream.isTTY,
323
+ ...options
324
+ });
325
+ return translateLevel(level);
326
+ }
327
+ var env, flagForceColor, supportsColor, supports_color_default;
328
+ var init_supports_color = __esm(() => {
329
+ ({ env } = process2);
330
+ if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
331
+ flagForceColor = 0;
332
+ } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
333
+ flagForceColor = 1;
334
+ }
335
+ supportsColor = {
336
+ stdout: createSupportsColor({ isTTY: tty.isatty(1) }),
337
+ stderr: createSupportsColor({ isTTY: tty.isatty(2) })
338
+ };
339
+ supports_color_default = supportsColor;
340
+ });
341
+
342
+ // node_modules/chalk/source/utilities.js
343
+ function stringReplaceAll(string, substring, replacer) {
344
+ let index = string.indexOf(substring);
345
+ if (index === -1) {
346
+ return string;
347
+ }
348
+ const substringLength = substring.length;
349
+ let endIndex = 0;
350
+ let returnValue = "";
351
+ do {
352
+ returnValue += string.slice(endIndex, index) + substring + replacer;
353
+ endIndex = index + substringLength;
354
+ index = string.indexOf(substring, endIndex);
355
+ } while (index !== -1);
356
+ returnValue += string.slice(endIndex);
357
+ return returnValue;
358
+ }
359
+ function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
360
+ let endIndex = 0;
361
+ let returnValue = "";
362
+ do {
363
+ const gotCR = string[index - 1] === "\r";
364
+ returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? `\r
365
+ ` : `
366
+ `) + postfix;
367
+ endIndex = index + 1;
368
+ index = string.indexOf(`
369
+ `, endIndex);
370
+ } while (index !== -1);
371
+ returnValue += string.slice(endIndex);
372
+ return returnValue;
373
+ }
374
+
375
+ // node_modules/chalk/source/index.js
376
+ function createChalk(options) {
377
+ return chalkFactory(options);
378
+ }
379
+ var stdoutColor, stderrColor, GENERATOR, STYLER, IS_EMPTY, levelMapping, styles2, applyOptions = (object, options = {}) => {
380
+ if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) {
381
+ throw new Error("The `level` option should be an integer from 0 to 3");
382
+ }
383
+ const colorLevel = stdoutColor ? stdoutColor.level : 0;
384
+ object.level = options.level === undefined ? colorLevel : options.level;
385
+ }, chalkFactory = (options) => {
386
+ const chalk = (...strings) => strings.join(" ");
387
+ applyOptions(chalk, options);
388
+ Object.setPrototypeOf(chalk, createChalk.prototype);
389
+ return chalk;
390
+ }, getModelAnsi = (model, level, type, ...arguments_) => {
391
+ if (model === "rgb") {
392
+ if (level === "ansi16m") {
393
+ return ansi_styles_default[type].ansi16m(...arguments_);
197
394
  }
198
- subcommandTerm(cmd) {
199
- const args = cmd.registeredArguments.map((arg) => humanReadableArgName(arg)).join(" ");
200
- return cmd._name + (cmd._aliases[0] ? "|" + cmd._aliases[0] : "") + (cmd.options.length ? " [options]" : "") + (args ? " " + args : "");
395
+ if (level === "ansi256") {
396
+ return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_));
201
397
  }
202
- optionTerm(option) {
203
- return option.flags;
398
+ return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_));
399
+ }
400
+ if (model === "hex") {
401
+ return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_));
402
+ }
403
+ return ansi_styles_default[type][model](...arguments_);
404
+ }, usedModels, proto, createStyler = (open, close, parent) => {
405
+ let openAll;
406
+ let closeAll;
407
+ if (parent === undefined) {
408
+ openAll = open;
409
+ closeAll = close;
410
+ } else {
411
+ openAll = parent.openAll + open;
412
+ closeAll = close + parent.closeAll;
413
+ }
414
+ return {
415
+ open,
416
+ close,
417
+ openAll,
418
+ closeAll,
419
+ parent
420
+ };
421
+ }, createBuilder = (self, _styler, _isEmpty) => {
422
+ const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" "));
423
+ Object.setPrototypeOf(builder, proto);
424
+ builder[GENERATOR] = self;
425
+ builder[STYLER] = _styler;
426
+ builder[IS_EMPTY] = _isEmpty;
427
+ return builder;
428
+ }, applyStyle = (self, string) => {
429
+ if (self.level <= 0 || !string) {
430
+ return self[IS_EMPTY] ? "" : string;
431
+ }
432
+ let styler = self[STYLER];
433
+ if (styler === undefined) {
434
+ return string;
435
+ }
436
+ const { openAll, closeAll } = styler;
437
+ if (string.includes("\x1B")) {
438
+ while (styler !== undefined) {
439
+ string = stringReplaceAll(string, styler.close, styler.open);
440
+ styler = styler.parent;
204
441
  }
205
- argumentTerm(argument) {
206
- return argument.name();
442
+ }
443
+ const lfIndex = string.indexOf(`
444
+ `);
445
+ if (lfIndex !== -1) {
446
+ string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
447
+ }
448
+ return openAll + string + closeAll;
449
+ }, chalk, chalkStderr, source_default;
450
+ var init_source = __esm(() => {
451
+ init_ansi_styles();
452
+ init_supports_color();
453
+ ({ stdout: stdoutColor, stderr: stderrColor } = supports_color_default);
454
+ GENERATOR = Symbol("GENERATOR");
455
+ STYLER = Symbol("STYLER");
456
+ IS_EMPTY = Symbol("IS_EMPTY");
457
+ levelMapping = [
458
+ "ansi",
459
+ "ansi",
460
+ "ansi256",
461
+ "ansi16m"
462
+ ];
463
+ styles2 = Object.create(null);
464
+ Object.setPrototypeOf(createChalk.prototype, Function.prototype);
465
+ for (const [styleName, style] of Object.entries(ansi_styles_default)) {
466
+ styles2[styleName] = {
467
+ get() {
468
+ const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);
469
+ Object.defineProperty(this, styleName, { value: builder });
470
+ return builder;
471
+ }
472
+ };
473
+ }
474
+ styles2.visible = {
475
+ get() {
476
+ const builder = createBuilder(this, this[STYLER], true);
477
+ Object.defineProperty(this, "visible", { value: builder });
478
+ return builder;
207
479
  }
208
- longestSubcommandTermLength(cmd, helper) {
209
- return helper.visibleCommands(cmd).reduce((max, command) => {
210
- return Math.max(max, helper.subcommandTerm(command).length);
211
- }, 0);
480
+ };
481
+ usedModels = ["rgb", "hex", "ansi256"];
482
+ for (const model of usedModels) {
483
+ styles2[model] = {
484
+ get() {
485
+ const { level } = this;
486
+ return function(...arguments_) {
487
+ const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]);
488
+ return createBuilder(this, styler, this[IS_EMPTY]);
489
+ };
490
+ }
491
+ };
492
+ const bgModel = "bg" + model[0].toUpperCase() + model.slice(1);
493
+ styles2[bgModel] = {
494
+ get() {
495
+ const { level } = this;
496
+ return function(...arguments_) {
497
+ const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]);
498
+ return createBuilder(this, styler, this[IS_EMPTY]);
499
+ };
500
+ }
501
+ };
502
+ }
503
+ proto = Object.defineProperties(() => {}, {
504
+ ...styles2,
505
+ level: {
506
+ enumerable: true,
507
+ get() {
508
+ return this[GENERATOR].level;
509
+ },
510
+ set(level) {
511
+ this[GENERATOR].level = level;
512
+ }
212
513
  }
213
- longestOptionTermLength(cmd, helper) {
214
- return helper.visibleOptions(cmd).reduce((max, option) => {
215
- return Math.max(max, helper.optionTerm(option).length);
216
- }, 0);
514
+ });
515
+ Object.defineProperties(createChalk.prototype, styles2);
516
+ chalk = createChalk();
517
+ chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
518
+ source_default = chalk;
519
+ });
520
+
521
+ // src/paths.ts
522
+ import { existsSync as existsSync2 } from "fs";
523
+ import { homedir } from "os";
524
+ import { dirname, join as join2, resolve } from "path";
525
+ function findProjectRoot() {
526
+ let dir = process.cwd();
527
+ const root = resolve("/");
528
+ while (dir !== root) {
529
+ if (existsSync2(join2(dir, ".pi", "plugins.json"))) {
530
+ return dir;
531
+ }
532
+ const parent = dirname(dir);
533
+ if (parent === dir)
534
+ break;
535
+ dir = parent;
536
+ }
537
+ return null;
538
+ }
539
+ function hasProjectPlugins() {
540
+ return findProjectRoot() !== null;
541
+ }
542
+ function resolveScope(options) {
543
+ if (options.global) {
544
+ return true;
545
+ }
546
+ if (options.local) {
547
+ return false;
548
+ }
549
+ return !hasProjectPlugins();
550
+ }
551
+ var PI_CONFIG_DIR, PLUGINS_DIR, NODE_MODULES_DIR, GLOBAL_PACKAGE_JSON, GLOBAL_LOCK_FILE, LEGACY_MANIFEST_PATH, PROJECT_PI_DIR = ".pi", PROJECT_PLUGINS_JSON, PROJECT_PACKAGE_JSON, PROJECT_PLUGINS_LOCK, PROJECT_NODE_MODULES;
552
+ var init_paths = __esm(() => {
553
+ PI_CONFIG_DIR = join2(homedir(), ".pi");
554
+ PLUGINS_DIR = join2(PI_CONFIG_DIR, "plugins");
555
+ NODE_MODULES_DIR = join2(PLUGINS_DIR, "node_modules");
556
+ GLOBAL_PACKAGE_JSON = join2(PLUGINS_DIR, "package.json");
557
+ GLOBAL_LOCK_FILE = join2(PLUGINS_DIR, "package-lock.json");
558
+ LEGACY_MANIFEST_PATH = join2(PLUGINS_DIR, "manifest.json");
559
+ PROJECT_PLUGINS_JSON = join2(PROJECT_PI_DIR, "plugins.json");
560
+ PROJECT_PACKAGE_JSON = join2(PROJECT_PI_DIR, "package.json");
561
+ PROJECT_PLUGINS_LOCK = join2(PROJECT_PI_DIR, "plugins-lock.json");
562
+ PROJECT_NODE_MODULES = join2(PROJECT_PI_DIR, "node_modules");
563
+ });
564
+
565
+ // src/lockfile.ts
566
+ var exports_lockfile = {};
567
+ __export(exports_lockfile, {
568
+ validateOrRegenerateLockFile: () => validateOrRegenerateLockFile,
569
+ updateLockFile: () => updateLockFile,
570
+ saveLockFile: () => saveLockFile,
571
+ loadLockFile: () => loadLockFile,
572
+ getLockedVersion: () => getLockedVersion,
573
+ createLockFile: () => createLockFile,
574
+ LOCKFILE_VERSION: () => LOCKFILE_VERSION
575
+ });
576
+ import { existsSync as existsSync7 } from "fs";
577
+ import { readFile as readFile2, writeFile as writeFile4 } from "fs/promises";
578
+ async function loadLockFile(global = true) {
579
+ const path = global ? GLOBAL_LOCK_FILE : PROJECT_PLUGINS_LOCK;
580
+ try {
581
+ if (!existsSync7(path))
582
+ return null;
583
+ const data = await readFile2(path, "utf-8");
584
+ const parsed = JSON.parse(data);
585
+ if (typeof parsed.lockfileVersion !== "number" || typeof parsed.packages !== "object") {
586
+ console.log(source_default.yellow(`Warning: ${path} has invalid schema, ignoring`));
587
+ return null;
588
+ }
589
+ if (parsed.lockfileVersion > LOCKFILE_VERSION) {
590
+ console.log(source_default.yellow(`Warning: ${path} was created by a newer version of omp (lockfile v${parsed.lockfileVersion}), ignoring`));
591
+ return null;
592
+ }
593
+ return parsed;
594
+ } catch (err) {
595
+ if (err.name === "SyntaxError") {
596
+ console.log(source_default.yellow(`Warning: ${path} is corrupted (invalid JSON), ignoring`));
597
+ }
598
+ return null;
599
+ }
600
+ }
601
+ async function saveLockFile(lockFile, global = true) {
602
+ const path = global ? GLOBAL_LOCK_FILE : PROJECT_PLUGINS_LOCK;
603
+ await writeFile4(path, JSON.stringify(lockFile, null, 2));
604
+ }
605
+ function createLockFile() {
606
+ return {
607
+ lockfileVersion: LOCKFILE_VERSION,
608
+ packages: {}
609
+ };
610
+ }
611
+ async function validateOrRegenerateLockFile(global = true) {
612
+ const existing = await loadLockFile(global);
613
+ if (existing) {
614
+ return existing;
615
+ }
616
+ const path = global ? GLOBAL_LOCK_FILE : PROJECT_PLUGINS_LOCK;
617
+ if (existsSync7(path)) {
618
+ console.log(source_default.yellow(`Regenerating corrupted lock file: ${path}`));
619
+ }
620
+ return createLockFile();
621
+ }
622
+ async function getLockedVersion(packageName, global = true) {
623
+ const lockFile = await loadLockFile(global);
624
+ if (!lockFile)
625
+ return null;
626
+ const entry = lockFile.packages[packageName];
627
+ return entry?.version ?? null;
628
+ }
629
+ async function updateLockFile(packageName, version, global = true) {
630
+ let lockFile = await loadLockFile(global);
631
+ if (!lockFile) {
632
+ lockFile = createLockFile();
633
+ }
634
+ lockFile.packages[packageName] = {
635
+ version
636
+ };
637
+ await saveLockFile(lockFile, global);
638
+ }
639
+ var LOCKFILE_VERSION = 1;
640
+ var init_lockfile = __esm(() => {
641
+ init_paths();
642
+ init_source();
643
+ });
644
+
645
+ // node_modules/commander/lib/error.js
646
+ var require_error = __commonJS((exports) => {
647
+ class CommanderError extends Error {
648
+ constructor(exitCode, code, message) {
649
+ super(message);
650
+ Error.captureStackTrace(this, this.constructor);
651
+ this.name = this.constructor.name;
652
+ this.code = code;
653
+ this.exitCode = exitCode;
654
+ this.nestedError = undefined;
655
+ }
656
+ }
657
+
658
+ class InvalidArgumentError extends CommanderError {
659
+ constructor(message) {
660
+ super(1, "commander.invalidArgument", message);
661
+ Error.captureStackTrace(this, this.constructor);
662
+ this.name = this.constructor.name;
663
+ }
664
+ }
665
+ exports.CommanderError = CommanderError;
666
+ exports.InvalidArgumentError = InvalidArgumentError;
667
+ });
668
+
669
+ // node_modules/commander/lib/argument.js
670
+ var require_argument = __commonJS((exports) => {
671
+ var { InvalidArgumentError } = require_error();
672
+
673
+ class Argument {
674
+ constructor(name, description) {
675
+ this.description = description || "";
676
+ this.variadic = false;
677
+ this.parseArg = undefined;
678
+ this.defaultValue = undefined;
679
+ this.defaultValueDescription = undefined;
680
+ this.argChoices = undefined;
681
+ switch (name[0]) {
682
+ case "<":
683
+ this.required = true;
684
+ this._name = name.slice(1, -1);
685
+ break;
686
+ case "[":
687
+ this.required = false;
688
+ this._name = name.slice(1, -1);
689
+ break;
690
+ default:
691
+ this.required = true;
692
+ this._name = name;
693
+ break;
694
+ }
695
+ if (this._name.length > 3 && this._name.slice(-3) === "...") {
696
+ this.variadic = true;
697
+ this._name = this._name.slice(0, -3);
698
+ }
699
+ }
700
+ name() {
701
+ return this._name;
702
+ }
703
+ _concatValue(value, previous) {
704
+ if (previous === this.defaultValue || !Array.isArray(previous)) {
705
+ return [value];
706
+ }
707
+ return previous.concat(value);
708
+ }
709
+ default(value, description) {
710
+ this.defaultValue = value;
711
+ this.defaultValueDescription = description;
712
+ return this;
713
+ }
714
+ argParser(fn) {
715
+ this.parseArg = fn;
716
+ return this;
717
+ }
718
+ choices(values) {
719
+ this.argChoices = values.slice();
720
+ this.parseArg = (arg, previous) => {
721
+ if (!this.argChoices.includes(arg)) {
722
+ throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(", ")}.`);
723
+ }
724
+ if (this.variadic) {
725
+ return this._concatValue(arg, previous);
726
+ }
727
+ return arg;
728
+ };
729
+ return this;
730
+ }
731
+ argRequired() {
732
+ this.required = true;
733
+ return this;
734
+ }
735
+ argOptional() {
736
+ this.required = false;
737
+ return this;
738
+ }
739
+ }
740
+ function humanReadableArgName(arg) {
741
+ const nameOutput = arg.name() + (arg.variadic === true ? "..." : "");
742
+ return arg.required ? "<" + nameOutput + ">" : "[" + nameOutput + "]";
743
+ }
744
+ exports.Argument = Argument;
745
+ exports.humanReadableArgName = humanReadableArgName;
746
+ });
747
+
748
+ // node_modules/commander/lib/help.js
749
+ var require_help = __commonJS((exports) => {
750
+ var { humanReadableArgName } = require_argument();
751
+
752
+ class Help {
753
+ constructor() {
754
+ this.helpWidth = undefined;
755
+ this.sortSubcommands = false;
756
+ this.sortOptions = false;
757
+ this.showGlobalOptions = false;
758
+ }
759
+ visibleCommands(cmd) {
760
+ const visibleCommands = cmd.commands.filter((cmd2) => !cmd2._hidden);
761
+ const helpCommand = cmd._getHelpCommand();
762
+ if (helpCommand && !helpCommand._hidden) {
763
+ visibleCommands.push(helpCommand);
764
+ }
765
+ if (this.sortSubcommands) {
766
+ visibleCommands.sort((a, b) => {
767
+ return a.name().localeCompare(b.name());
768
+ });
769
+ }
770
+ return visibleCommands;
771
+ }
772
+ compareOptions(a, b) {
773
+ const getSortKey = (option) => {
774
+ return option.short ? option.short.replace(/^-/, "") : option.long.replace(/^--/, "");
775
+ };
776
+ return getSortKey(a).localeCompare(getSortKey(b));
777
+ }
778
+ visibleOptions(cmd) {
779
+ const visibleOptions = cmd.options.filter((option) => !option.hidden);
780
+ const helpOption = cmd._getHelpOption();
781
+ if (helpOption && !helpOption.hidden) {
782
+ const removeShort = helpOption.short && cmd._findOption(helpOption.short);
783
+ const removeLong = helpOption.long && cmd._findOption(helpOption.long);
784
+ if (!removeShort && !removeLong) {
785
+ visibleOptions.push(helpOption);
786
+ } else if (helpOption.long && !removeLong) {
787
+ visibleOptions.push(cmd.createOption(helpOption.long, helpOption.description));
788
+ } else if (helpOption.short && !removeShort) {
789
+ visibleOptions.push(cmd.createOption(helpOption.short, helpOption.description));
790
+ }
791
+ }
792
+ if (this.sortOptions) {
793
+ visibleOptions.sort(this.compareOptions);
794
+ }
795
+ return visibleOptions;
796
+ }
797
+ visibleGlobalOptions(cmd) {
798
+ if (!this.showGlobalOptions)
799
+ return [];
800
+ const globalOptions = [];
801
+ for (let ancestorCmd = cmd.parent;ancestorCmd; ancestorCmd = ancestorCmd.parent) {
802
+ const visibleOptions = ancestorCmd.options.filter((option) => !option.hidden);
803
+ globalOptions.push(...visibleOptions);
804
+ }
805
+ if (this.sortOptions) {
806
+ globalOptions.sort(this.compareOptions);
807
+ }
808
+ return globalOptions;
809
+ }
810
+ visibleArguments(cmd) {
811
+ if (cmd._argsDescription) {
812
+ cmd.registeredArguments.forEach((argument) => {
813
+ argument.description = argument.description || cmd._argsDescription[argument.name()] || "";
814
+ });
815
+ }
816
+ if (cmd.registeredArguments.find((argument) => argument.description)) {
817
+ return cmd.registeredArguments;
818
+ }
819
+ return [];
820
+ }
821
+ subcommandTerm(cmd) {
822
+ const args = cmd.registeredArguments.map((arg) => humanReadableArgName(arg)).join(" ");
823
+ return cmd._name + (cmd._aliases[0] ? "|" + cmd._aliases[0] : "") + (cmd.options.length ? " [options]" : "") + (args ? " " + args : "");
824
+ }
825
+ optionTerm(option) {
826
+ return option.flags;
827
+ }
828
+ argumentTerm(argument) {
829
+ return argument.name();
830
+ }
831
+ longestSubcommandTermLength(cmd, helper) {
832
+ return helper.visibleCommands(cmd).reduce((max, command) => {
833
+ return Math.max(max, helper.subcommandTerm(command).length);
834
+ }, 0);
835
+ }
836
+ longestOptionTermLength(cmd, helper) {
837
+ return helper.visibleOptions(cmd).reduce((max, option) => {
838
+ return Math.max(max, helper.optionTerm(option).length);
839
+ }, 0);
217
840
  }
218
841
  longestGlobalOptionTermLength(cmd, helper) {
219
842
  return helper.visibleGlobalOptions(cmd).reduce((max, option) => {
@@ -601,7 +1224,7 @@ var require_command = __commonJS((exports) => {
601
1224
  var childProcess = __require("child_process");
602
1225
  var path = __require("path");
603
1226
  var fs = __require("fs");
604
- var process2 = __require("process");
1227
+ var process3 = __require("process");
605
1228
  var { Argument, humanReadableArgName } = require_argument();
606
1229
  var { CommanderError } = require_error();
607
1230
  var { Help } = require_help();
@@ -643,10 +1266,10 @@ var require_command = __commonJS((exports) => {
643
1266
  this._showHelpAfterError = false;
644
1267
  this._showSuggestionAfterError = true;
645
1268
  this._outputConfiguration = {
646
- writeOut: (str) => process2.stdout.write(str),
647
- writeErr: (str) => process2.stderr.write(str),
648
- getOutHelpWidth: () => process2.stdout.isTTY ? process2.stdout.columns : undefined,
649
- getErrHelpWidth: () => process2.stderr.isTTY ? process2.stderr.columns : undefined,
1269
+ writeOut: (str) => process3.stdout.write(str),
1270
+ writeErr: (str) => process3.stderr.write(str),
1271
+ getOutHelpWidth: () => process3.stdout.isTTY ? process3.stdout.columns : undefined,
1272
+ getErrHelpWidth: () => process3.stderr.isTTY ? process3.stderr.columns : undefined,
650
1273
  outputError: (str, write) => write(str)
651
1274
  };
652
1275
  this._hidden = false;
@@ -842,7 +1465,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
842
1465
  if (this._exitCallback) {
843
1466
  this._exitCallback(new CommanderError(exitCode, code, message));
844
1467
  }
845
- process2.exit(exitCode);
1468
+ process3.exit(exitCode);
846
1469
  }
847
1470
  action(fn) {
848
1471
  const listener = (args) => {
@@ -1037,16 +1660,16 @@ Expecting one of '${allowedValues.join("', '")}'`);
1037
1660
  }
1038
1661
  parseOptions = parseOptions || {};
1039
1662
  if (argv === undefined && parseOptions.from === undefined) {
1040
- if (process2.versions?.electron) {
1663
+ if (process3.versions?.electron) {
1041
1664
  parseOptions.from = "electron";
1042
1665
  }
1043
- const execArgv = process2.execArgv ?? [];
1666
+ const execArgv = process3.execArgv ?? [];
1044
1667
  if (execArgv.includes("-e") || execArgv.includes("--eval") || execArgv.includes("-p") || execArgv.includes("--print")) {
1045
1668
  parseOptions.from = "eval";
1046
1669
  }
1047
1670
  }
1048
1671
  if (argv === undefined) {
1049
- argv = process2.argv;
1672
+ argv = process3.argv;
1050
1673
  }
1051
1674
  this.rawArgs = argv.slice();
1052
1675
  let userArgs;
@@ -1057,7 +1680,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1057
1680
  userArgs = argv.slice(2);
1058
1681
  break;
1059
1682
  case "electron":
1060
- if (process2.defaultApp) {
1683
+ if (process3.defaultApp) {
1061
1684
  this._scriptPath = argv[1];
1062
1685
  userArgs = argv.slice(2);
1063
1686
  } else {
@@ -1128,23 +1751,23 @@ Expecting one of '${allowedValues.join("', '")}'`);
1128
1751
  }
1129
1752
  launchWithNode = sourceExt.includes(path.extname(executableFile));
1130
1753
  let proc;
1131
- if (process2.platform !== "win32") {
1754
+ if (process3.platform !== "win32") {
1132
1755
  if (launchWithNode) {
1133
1756
  args.unshift(executableFile);
1134
- args = incrementNodeInspectorPort(process2.execArgv).concat(args);
1135
- proc = childProcess.spawn(process2.argv[0], args, { stdio: "inherit" });
1757
+ args = incrementNodeInspectorPort(process3.execArgv).concat(args);
1758
+ proc = childProcess.spawn(process3.argv[0], args, { stdio: "inherit" });
1136
1759
  } else {
1137
1760
  proc = childProcess.spawn(executableFile, args, { stdio: "inherit" });
1138
1761
  }
1139
1762
  } else {
1140
1763
  args.unshift(executableFile);
1141
- args = incrementNodeInspectorPort(process2.execArgv).concat(args);
1142
- proc = childProcess.spawn(process2.execPath, args, { stdio: "inherit" });
1764
+ args = incrementNodeInspectorPort(process3.execArgv).concat(args);
1765
+ proc = childProcess.spawn(process3.execPath, args, { stdio: "inherit" });
1143
1766
  }
1144
1767
  if (!proc.killed) {
1145
1768
  const signals = ["SIGUSR1", "SIGUSR2", "SIGTERM", "SIGINT", "SIGHUP"];
1146
1769
  signals.forEach((signal) => {
1147
- process2.on(signal, () => {
1770
+ process3.on(signal, () => {
1148
1771
  if (proc.killed === false && proc.exitCode === null) {
1149
1772
  proc.kill(signal);
1150
1773
  }
@@ -1155,7 +1778,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1155
1778
  proc.on("close", (code) => {
1156
1779
  code = code ?? 1;
1157
1780
  if (!exitCallback) {
1158
- process2.exit(code);
1781
+ process3.exit(code);
1159
1782
  } else {
1160
1783
  exitCallback(new CommanderError(code, "commander.executeSubCommandAsync", "(close)"));
1161
1784
  }
@@ -1172,7 +1795,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1172
1795
  throw new Error(`'${executableFile}' not executable`);
1173
1796
  }
1174
1797
  if (!exitCallback) {
1175
- process2.exit(1);
1798
+ process3.exit(1);
1176
1799
  } else {
1177
1800
  const wrappedError = new CommanderError(1, "commander.executeSubCommandAsync", "(error)");
1178
1801
  wrappedError.nestedError = err;
@@ -1520,11 +2143,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
1520
2143
  }
1521
2144
  _parseOptionsEnv() {
1522
2145
  this.options.forEach((option) => {
1523
- if (option.envVar && option.envVar in process2.env) {
2146
+ if (option.envVar && option.envVar in process3.env) {
1524
2147
  const optionKey = option.attributeName();
1525
2148
  if (this.getOptionValue(optionKey) === undefined || ["default", "config", "env"].includes(this.getOptionValueSource(optionKey))) {
1526
2149
  if (option.required || option.optional) {
1527
- this.emit(`optionEnv:${option.name()}`, process2.env[option.envVar]);
2150
+ this.emit(`optionEnv:${option.name()}`, process3.env[option.envVar]);
1528
2151
  } else {
1529
2152
  this.emit(`optionEnv:${option.name()}`);
1530
2153
  }
@@ -1637,743 +2260,271 @@ Expecting one of '${allowedValues.join("', '")}'`);
1637
2260
  }
1638
2261
  description(str, argsDescription) {
1639
2262
  if (str === undefined && argsDescription === undefined)
1640
- return this._description;
1641
- this._description = str;
1642
- if (argsDescription) {
1643
- this._argsDescription = argsDescription;
1644
- }
1645
- return this;
1646
- }
1647
- summary(str) {
1648
- if (str === undefined)
1649
- return this._summary;
1650
- this._summary = str;
1651
- return this;
1652
- }
1653
- alias(alias) {
1654
- if (alias === undefined)
1655
- return this._aliases[0];
1656
- let command = this;
1657
- if (this.commands.length !== 0 && this.commands[this.commands.length - 1]._executableHandler) {
1658
- command = this.commands[this.commands.length - 1];
1659
- }
1660
- if (alias === command._name)
1661
- throw new Error("Command alias can't be the same as its name");
1662
- const matchingCommand = this.parent?._findCommand(alias);
1663
- if (matchingCommand) {
1664
- const existingCmd = [matchingCommand.name()].concat(matchingCommand.aliases()).join("|");
1665
- throw new Error(`cannot add alias '${alias}' to command '${this.name()}' as already have command '${existingCmd}'`);
1666
- }
1667
- command._aliases.push(alias);
1668
- return this;
1669
- }
1670
- aliases(aliases) {
1671
- if (aliases === undefined)
1672
- return this._aliases;
1673
- aliases.forEach((alias) => this.alias(alias));
1674
- return this;
1675
- }
1676
- usage(str) {
1677
- if (str === undefined) {
1678
- if (this._usage)
1679
- return this._usage;
1680
- const args = this.registeredArguments.map((arg) => {
1681
- return humanReadableArgName(arg);
1682
- });
1683
- return [].concat(this.options.length || this._helpOption !== null ? "[options]" : [], this.commands.length ? "[command]" : [], this.registeredArguments.length ? args : []).join(" ");
1684
- }
1685
- this._usage = str;
1686
- return this;
1687
- }
1688
- name(str) {
1689
- if (str === undefined)
1690
- return this._name;
1691
- this._name = str;
1692
- return this;
1693
- }
1694
- nameFromFilename(filename) {
1695
- this._name = path.basename(filename, path.extname(filename));
1696
- return this;
1697
- }
1698
- executableDir(path2) {
1699
- if (path2 === undefined)
1700
- return this._executableDir;
1701
- this._executableDir = path2;
1702
- return this;
1703
- }
1704
- helpInformation(contextOptions) {
1705
- const helper = this.createHelp();
1706
- if (helper.helpWidth === undefined) {
1707
- helper.helpWidth = contextOptions && contextOptions.error ? this._outputConfiguration.getErrHelpWidth() : this._outputConfiguration.getOutHelpWidth();
1708
- }
1709
- return helper.formatHelp(this, helper);
1710
- }
1711
- _getHelpContext(contextOptions) {
1712
- contextOptions = contextOptions || {};
1713
- const context = { error: !!contextOptions.error };
1714
- let write;
1715
- if (context.error) {
1716
- write = (arg) => this._outputConfiguration.writeErr(arg);
1717
- } else {
1718
- write = (arg) => this._outputConfiguration.writeOut(arg);
1719
- }
1720
- context.write = contextOptions.write || write;
1721
- context.command = this;
1722
- return context;
1723
- }
1724
- outputHelp(contextOptions) {
1725
- let deprecatedCallback;
1726
- if (typeof contextOptions === "function") {
1727
- deprecatedCallback = contextOptions;
1728
- contextOptions = undefined;
1729
- }
1730
- const context = this._getHelpContext(contextOptions);
1731
- this._getCommandAndAncestors().reverse().forEach((command) => command.emit("beforeAllHelp", context));
1732
- this.emit("beforeHelp", context);
1733
- let helpInformation = this.helpInformation(context);
1734
- if (deprecatedCallback) {
1735
- helpInformation = deprecatedCallback(helpInformation);
1736
- if (typeof helpInformation !== "string" && !Buffer.isBuffer(helpInformation)) {
1737
- throw new Error("outputHelp callback must return a string or a Buffer");
1738
- }
1739
- }
1740
- context.write(helpInformation);
1741
- if (this._getHelpOption()?.long) {
1742
- this.emit(this._getHelpOption().long);
1743
- }
1744
- this.emit("afterHelp", context);
1745
- this._getCommandAndAncestors().forEach((command) => command.emit("afterAllHelp", context));
1746
- }
1747
- helpOption(flags, description) {
1748
- if (typeof flags === "boolean") {
1749
- if (flags) {
1750
- this._helpOption = this._helpOption ?? undefined;
1751
- } else {
1752
- this._helpOption = null;
1753
- }
1754
- return this;
1755
- }
1756
- flags = flags ?? "-h, --help";
1757
- description = description ?? "display help for command";
1758
- this._helpOption = this.createOption(flags, description);
1759
- return this;
1760
- }
1761
- _getHelpOption() {
1762
- if (this._helpOption === undefined) {
1763
- this.helpOption(undefined, undefined);
1764
- }
1765
- return this._helpOption;
1766
- }
1767
- addHelpOption(option) {
1768
- this._helpOption = option;
1769
- return this;
1770
- }
1771
- help(contextOptions) {
1772
- this.outputHelp(contextOptions);
1773
- let exitCode = process2.exitCode || 0;
1774
- if (exitCode === 0 && contextOptions && typeof contextOptions !== "function" && contextOptions.error) {
1775
- exitCode = 1;
1776
- }
1777
- this._exit(exitCode, "commander.help", "(outputHelp)");
1778
- }
1779
- addHelpText(position, text) {
1780
- const allowedValues = ["beforeAll", "before", "after", "afterAll"];
1781
- if (!allowedValues.includes(position)) {
1782
- throw new Error(`Unexpected value for position to addHelpText.
1783
- Expecting one of '${allowedValues.join("', '")}'`);
1784
- }
1785
- const helpEvent = `${position}Help`;
1786
- this.on(helpEvent, (context) => {
1787
- let helpStr;
1788
- if (typeof text === "function") {
1789
- helpStr = text({ error: context.error, command: context.command });
1790
- } else {
1791
- helpStr = text;
1792
- }
1793
- if (helpStr) {
1794
- context.write(`${helpStr}
1795
- `);
1796
- }
1797
- });
1798
- return this;
1799
- }
1800
- _outputHelpIfRequested(args) {
1801
- const helpOption = this._getHelpOption();
1802
- const helpRequested = helpOption && args.find((arg) => helpOption.is(arg));
1803
- if (helpRequested) {
1804
- this.outputHelp();
1805
- this._exit(0, "commander.helpDisplayed", "(outputHelp)");
1806
- }
1807
- }
1808
- }
1809
- function incrementNodeInspectorPort(args) {
1810
- return args.map((arg) => {
1811
- if (!arg.startsWith("--inspect")) {
1812
- return arg;
1813
- }
1814
- let debugOption;
1815
- let debugHost = "127.0.0.1";
1816
- let debugPort = "9229";
1817
- let match;
1818
- if ((match = arg.match(/^(--inspect(-brk)?)$/)) !== null) {
1819
- debugOption = match[1];
1820
- } else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+)$/)) !== null) {
1821
- debugOption = match[1];
1822
- if (/^\d+$/.test(match[3])) {
1823
- debugPort = match[3];
1824
- } else {
1825
- debugHost = match[3];
1826
- }
1827
- } else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/)) !== null) {
1828
- debugOption = match[1];
1829
- debugHost = match[3];
1830
- debugPort = match[4];
1831
- }
1832
- if (debugOption && debugPort !== "0") {
1833
- return `${debugOption}=${debugHost}:${parseInt(debugPort) + 1}`;
1834
- }
1835
- return arg;
1836
- });
1837
- }
1838
- exports.Command = Command;
1839
- });
1840
-
1841
- // node_modules/commander/index.js
1842
- var require_commander = __commonJS((exports) => {
1843
- var { Argument } = require_argument();
1844
- var { Command } = require_command();
1845
- var { CommanderError, InvalidArgumentError } = require_error();
1846
- var { Help } = require_help();
1847
- var { Option } = require_option();
1848
- exports.program = new Command;
1849
- exports.createCommand = (name) => new Command(name);
1850
- exports.createOption = (flags, description) => new Option(flags, description);
1851
- exports.createArgument = (name, description) => new Argument(name, description);
1852
- exports.Command = Command;
1853
- exports.Option = Option;
1854
- exports.Argument = Argument;
1855
- exports.Help = Help;
1856
- exports.CommanderError = CommanderError;
1857
- exports.InvalidArgumentError = InvalidArgumentError;
1858
- exports.InvalidOptionArgumentError = InvalidArgumentError;
1859
- });
1860
-
1861
- // node_modules/commander/esm.mjs
1862
- var import__ = __toESM(require_commander(), 1);
1863
- var {
1864
- program,
1865
- createCommand,
1866
- createArgument,
1867
- createOption,
1868
- CommanderError,
1869
- InvalidArgumentError,
1870
- InvalidOptionArgumentError,
1871
- Command,
1872
- Argument,
1873
- Option,
1874
- Help
1875
- } = import__.default;
1876
-
1877
- // src/commands/create.ts
1878
- import { existsSync } from "fs";
1879
- import { mkdir, writeFile } from "fs/promises";
1880
- import { join } from "path";
1881
-
1882
- // node_modules/chalk/source/vendor/ansi-styles/index.js
1883
- var ANSI_BACKGROUND_OFFSET = 10;
1884
- var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
1885
- var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
1886
- var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
1887
- var styles = {
1888
- modifier: {
1889
- reset: [0, 0],
1890
- bold: [1, 22],
1891
- dim: [2, 22],
1892
- italic: [3, 23],
1893
- underline: [4, 24],
1894
- overline: [53, 55],
1895
- inverse: [7, 27],
1896
- hidden: [8, 28],
1897
- strikethrough: [9, 29]
1898
- },
1899
- color: {
1900
- black: [30, 39],
1901
- red: [31, 39],
1902
- green: [32, 39],
1903
- yellow: [33, 39],
1904
- blue: [34, 39],
1905
- magenta: [35, 39],
1906
- cyan: [36, 39],
1907
- white: [37, 39],
1908
- blackBright: [90, 39],
1909
- gray: [90, 39],
1910
- grey: [90, 39],
1911
- redBright: [91, 39],
1912
- greenBright: [92, 39],
1913
- yellowBright: [93, 39],
1914
- blueBright: [94, 39],
1915
- magentaBright: [95, 39],
1916
- cyanBright: [96, 39],
1917
- whiteBright: [97, 39]
1918
- },
1919
- bgColor: {
1920
- bgBlack: [40, 49],
1921
- bgRed: [41, 49],
1922
- bgGreen: [42, 49],
1923
- bgYellow: [43, 49],
1924
- bgBlue: [44, 49],
1925
- bgMagenta: [45, 49],
1926
- bgCyan: [46, 49],
1927
- bgWhite: [47, 49],
1928
- bgBlackBright: [100, 49],
1929
- bgGray: [100, 49],
1930
- bgGrey: [100, 49],
1931
- bgRedBright: [101, 49],
1932
- bgGreenBright: [102, 49],
1933
- bgYellowBright: [103, 49],
1934
- bgBlueBright: [104, 49],
1935
- bgMagentaBright: [105, 49],
1936
- bgCyanBright: [106, 49],
1937
- bgWhiteBright: [107, 49]
1938
- }
1939
- };
1940
- var modifierNames = Object.keys(styles.modifier);
1941
- var foregroundColorNames = Object.keys(styles.color);
1942
- var backgroundColorNames = Object.keys(styles.bgColor);
1943
- var colorNames = [...foregroundColorNames, ...backgroundColorNames];
1944
- function assembleStyles() {
1945
- const codes = new Map;
1946
- for (const [groupName, group] of Object.entries(styles)) {
1947
- for (const [styleName, style] of Object.entries(group)) {
1948
- styles[styleName] = {
1949
- open: `\x1B[${style[0]}m`,
1950
- close: `\x1B[${style[1]}m`
1951
- };
1952
- group[styleName] = styles[styleName];
1953
- codes.set(style[0], style[1]);
1954
- }
1955
- Object.defineProperty(styles, groupName, {
1956
- value: group,
1957
- enumerable: false
1958
- });
1959
- }
1960
- Object.defineProperty(styles, "codes", {
1961
- value: codes,
1962
- enumerable: false
1963
- });
1964
- styles.color.close = "\x1B[39m";
1965
- styles.bgColor.close = "\x1B[49m";
1966
- styles.color.ansi = wrapAnsi16();
1967
- styles.color.ansi256 = wrapAnsi256();
1968
- styles.color.ansi16m = wrapAnsi16m();
1969
- styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
1970
- styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
1971
- styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
1972
- Object.defineProperties(styles, {
1973
- rgbToAnsi256: {
1974
- value(red, green, blue) {
1975
- if (red === green && green === blue) {
1976
- if (red < 8) {
1977
- return 16;
1978
- }
1979
- if (red > 248) {
1980
- return 231;
1981
- }
1982
- return Math.round((red - 8) / 247 * 24) + 232;
1983
- }
1984
- return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
1985
- },
1986
- enumerable: false
1987
- },
1988
- hexToRgb: {
1989
- value(hex) {
1990
- const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
1991
- if (!matches) {
1992
- return [0, 0, 0];
1993
- }
1994
- let [colorString] = matches;
1995
- if (colorString.length === 3) {
1996
- colorString = [...colorString].map((character) => character + character).join("");
1997
- }
1998
- const integer = Number.parseInt(colorString, 16);
1999
- return [
2000
- integer >> 16 & 255,
2001
- integer >> 8 & 255,
2002
- integer & 255
2003
- ];
2004
- },
2005
- enumerable: false
2006
- },
2007
- hexToAnsi256: {
2008
- value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
2009
- enumerable: false
2010
- },
2011
- ansi256ToAnsi: {
2012
- value(code) {
2013
- if (code < 8) {
2014
- return 30 + code;
2015
- }
2016
- if (code < 16) {
2017
- return 90 + (code - 8);
2018
- }
2019
- let red;
2020
- let green;
2021
- let blue;
2022
- if (code >= 232) {
2023
- red = ((code - 232) * 10 + 8) / 255;
2024
- green = red;
2025
- blue = red;
2026
- } else {
2027
- code -= 16;
2028
- const remainder = code % 36;
2029
- red = Math.floor(code / 36) / 5;
2030
- green = Math.floor(remainder / 6) / 5;
2031
- blue = remainder % 6 / 5;
2032
- }
2033
- const value = Math.max(red, green, blue) * 2;
2034
- if (value === 0) {
2035
- return 30;
2036
- }
2037
- let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
2038
- if (value === 2) {
2039
- result += 60;
2040
- }
2041
- return result;
2042
- },
2043
- enumerable: false
2044
- },
2045
- rgbToAnsi: {
2046
- value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
2047
- enumerable: false
2048
- },
2049
- hexToAnsi: {
2050
- value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
2051
- enumerable: false
2263
+ return this._description;
2264
+ this._description = str;
2265
+ if (argsDescription) {
2266
+ this._argsDescription = argsDescription;
2267
+ }
2268
+ return this;
2052
2269
  }
2053
- });
2054
- return styles;
2055
- }
2056
- var ansiStyles = assembleStyles();
2057
- var ansi_styles_default = ansiStyles;
2058
-
2059
- // node_modules/chalk/source/vendor/supports-color/index.js
2060
- import process2 from "process";
2061
- import os from "os";
2062
- import tty from "tty";
2063
- function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process2.argv) {
2064
- const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
2065
- const position = argv.indexOf(prefix + flag);
2066
- const terminatorPosition = argv.indexOf("--");
2067
- return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
2068
- }
2069
- var { env } = process2;
2070
- var flagForceColor;
2071
- if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
2072
- flagForceColor = 0;
2073
- } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
2074
- flagForceColor = 1;
2075
- }
2076
- function envForceColor() {
2077
- if ("FORCE_COLOR" in env) {
2078
- if (env.FORCE_COLOR === "true") {
2079
- return 1;
2270
+ summary(str) {
2271
+ if (str === undefined)
2272
+ return this._summary;
2273
+ this._summary = str;
2274
+ return this;
2080
2275
  }
2081
- if (env.FORCE_COLOR === "false") {
2082
- return 0;
2276
+ alias(alias) {
2277
+ if (alias === undefined)
2278
+ return this._aliases[0];
2279
+ let command = this;
2280
+ if (this.commands.length !== 0 && this.commands[this.commands.length - 1]._executableHandler) {
2281
+ command = this.commands[this.commands.length - 1];
2282
+ }
2283
+ if (alias === command._name)
2284
+ throw new Error("Command alias can't be the same as its name");
2285
+ const matchingCommand = this.parent?._findCommand(alias);
2286
+ if (matchingCommand) {
2287
+ const existingCmd = [matchingCommand.name()].concat(matchingCommand.aliases()).join("|");
2288
+ throw new Error(`cannot add alias '${alias}' to command '${this.name()}' as already have command '${existingCmd}'`);
2289
+ }
2290
+ command._aliases.push(alias);
2291
+ return this;
2083
2292
  }
2084
- return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
2085
- }
2086
- }
2087
- function translateLevel(level) {
2088
- if (level === 0) {
2089
- return false;
2090
- }
2091
- return {
2092
- level,
2093
- hasBasic: true,
2094
- has256: level >= 2,
2095
- has16m: level >= 3
2096
- };
2097
- }
2098
- function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
2099
- const noFlagForceColor = envForceColor();
2100
- if (noFlagForceColor !== undefined) {
2101
- flagForceColor = noFlagForceColor;
2102
- }
2103
- const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
2104
- if (forceColor === 0) {
2105
- return 0;
2106
- }
2107
- if (sniffFlags) {
2108
- if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
2109
- return 3;
2293
+ aliases(aliases) {
2294
+ if (aliases === undefined)
2295
+ return this._aliases;
2296
+ aliases.forEach((alias) => this.alias(alias));
2297
+ return this;
2110
2298
  }
2111
- if (hasFlag("color=256")) {
2112
- return 2;
2299
+ usage(str) {
2300
+ if (str === undefined) {
2301
+ if (this._usage)
2302
+ return this._usage;
2303
+ const args = this.registeredArguments.map((arg) => {
2304
+ return humanReadableArgName(arg);
2305
+ });
2306
+ return [].concat(this.options.length || this._helpOption !== null ? "[options]" : [], this.commands.length ? "[command]" : [], this.registeredArguments.length ? args : []).join(" ");
2307
+ }
2308
+ this._usage = str;
2309
+ return this;
2113
2310
  }
2114
- }
2115
- if ("TF_BUILD" in env && "AGENT_NAME" in env) {
2116
- return 1;
2117
- }
2118
- if (haveStream && !streamIsTTY && forceColor === undefined) {
2119
- return 0;
2120
- }
2121
- const min = forceColor || 0;
2122
- if (env.TERM === "dumb") {
2123
- return min;
2124
- }
2125
- if (process2.platform === "win32") {
2126
- const osRelease = os.release().split(".");
2127
- if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
2128
- return Number(osRelease[2]) >= 14931 ? 3 : 2;
2311
+ name(str) {
2312
+ if (str === undefined)
2313
+ return this._name;
2314
+ this._name = str;
2315
+ return this;
2129
2316
  }
2130
- return 1;
2131
- }
2132
- if ("CI" in env) {
2133
- if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => (key in env))) {
2134
- return 3;
2317
+ nameFromFilename(filename) {
2318
+ this._name = path.basename(filename, path.extname(filename));
2319
+ return this;
2135
2320
  }
2136
- if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => (sign in env)) || env.CI_NAME === "codeship") {
2137
- return 1;
2321
+ executableDir(path2) {
2322
+ if (path2 === undefined)
2323
+ return this._executableDir;
2324
+ this._executableDir = path2;
2325
+ return this;
2138
2326
  }
2139
- return min;
2140
- }
2141
- if ("TEAMCITY_VERSION" in env) {
2142
- return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
2143
- }
2144
- if (env.COLORTERM === "truecolor") {
2145
- return 3;
2146
- }
2147
- if (env.TERM === "xterm-kitty") {
2148
- return 3;
2149
- }
2150
- if (env.TERM === "xterm-ghostty") {
2151
- return 3;
2152
- }
2153
- if (env.TERM === "wezterm") {
2154
- return 3;
2155
- }
2156
- if ("TERM_PROGRAM" in env) {
2157
- const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
2158
- switch (env.TERM_PROGRAM) {
2159
- case "iTerm.app": {
2160
- return version >= 3 ? 3 : 2;
2327
+ helpInformation(contextOptions) {
2328
+ const helper = this.createHelp();
2329
+ if (helper.helpWidth === undefined) {
2330
+ helper.helpWidth = contextOptions && contextOptions.error ? this._outputConfiguration.getErrHelpWidth() : this._outputConfiguration.getOutHelpWidth();
2161
2331
  }
2162
- case "Apple_Terminal": {
2163
- return 2;
2332
+ return helper.formatHelp(this, helper);
2333
+ }
2334
+ _getHelpContext(contextOptions) {
2335
+ contextOptions = contextOptions || {};
2336
+ const context = { error: !!contextOptions.error };
2337
+ let write;
2338
+ if (context.error) {
2339
+ write = (arg) => this._outputConfiguration.writeErr(arg);
2340
+ } else {
2341
+ write = (arg) => this._outputConfiguration.writeOut(arg);
2164
2342
  }
2343
+ context.write = contextOptions.write || write;
2344
+ context.command = this;
2345
+ return context;
2165
2346
  }
2166
- }
2167
- if (/-256(color)?$/i.test(env.TERM)) {
2168
- return 2;
2169
- }
2170
- if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
2171
- return 1;
2172
- }
2173
- if ("COLORTERM" in env) {
2174
- return 1;
2175
- }
2176
- return min;
2177
- }
2178
- function createSupportsColor(stream, options = {}) {
2179
- const level = _supportsColor(stream, {
2180
- streamIsTTY: stream && stream.isTTY,
2181
- ...options
2182
- });
2183
- return translateLevel(level);
2184
- }
2185
- var supportsColor = {
2186
- stdout: createSupportsColor({ isTTY: tty.isatty(1) }),
2187
- stderr: createSupportsColor({ isTTY: tty.isatty(2) })
2188
- };
2189
- var supports_color_default = supportsColor;
2190
-
2191
- // node_modules/chalk/source/utilities.js
2192
- function stringReplaceAll(string, substring, replacer) {
2193
- let index = string.indexOf(substring);
2194
- if (index === -1) {
2195
- return string;
2196
- }
2197
- const substringLength = substring.length;
2198
- let endIndex = 0;
2199
- let returnValue = "";
2200
- do {
2201
- returnValue += string.slice(endIndex, index) + substring + replacer;
2202
- endIndex = index + substringLength;
2203
- index = string.indexOf(substring, endIndex);
2204
- } while (index !== -1);
2205
- returnValue += string.slice(endIndex);
2206
- return returnValue;
2207
- }
2208
- function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
2209
- let endIndex = 0;
2210
- let returnValue = "";
2211
- do {
2212
- const gotCR = string[index - 1] === "\r";
2213
- returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? `\r
2214
- ` : `
2215
- `) + postfix;
2216
- endIndex = index + 1;
2217
- index = string.indexOf(`
2218
- `, endIndex);
2219
- } while (index !== -1);
2220
- returnValue += string.slice(endIndex);
2221
- return returnValue;
2222
- }
2223
-
2224
- // node_modules/chalk/source/index.js
2225
- var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default;
2226
- var GENERATOR = Symbol("GENERATOR");
2227
- var STYLER = Symbol("STYLER");
2228
- var IS_EMPTY = Symbol("IS_EMPTY");
2229
- var levelMapping = [
2230
- "ansi",
2231
- "ansi",
2232
- "ansi256",
2233
- "ansi16m"
2234
- ];
2235
- var styles2 = Object.create(null);
2236
- var applyOptions = (object, options = {}) => {
2237
- if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) {
2238
- throw new Error("The `level` option should be an integer from 0 to 3");
2239
- }
2240
- const colorLevel = stdoutColor ? stdoutColor.level : 0;
2241
- object.level = options.level === undefined ? colorLevel : options.level;
2242
- };
2243
- var chalkFactory = (options) => {
2244
- const chalk = (...strings) => strings.join(" ");
2245
- applyOptions(chalk, options);
2246
- Object.setPrototypeOf(chalk, createChalk.prototype);
2247
- return chalk;
2248
- };
2249
- function createChalk(options) {
2250
- return chalkFactory(options);
2251
- }
2252
- Object.setPrototypeOf(createChalk.prototype, Function.prototype);
2253
- for (const [styleName, style] of Object.entries(ansi_styles_default)) {
2254
- styles2[styleName] = {
2255
- get() {
2256
- const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);
2257
- Object.defineProperty(this, styleName, { value: builder });
2258
- return builder;
2347
+ outputHelp(contextOptions) {
2348
+ let deprecatedCallback;
2349
+ if (typeof contextOptions === "function") {
2350
+ deprecatedCallback = contextOptions;
2351
+ contextOptions = undefined;
2352
+ }
2353
+ const context = this._getHelpContext(contextOptions);
2354
+ this._getCommandAndAncestors().reverse().forEach((command) => command.emit("beforeAllHelp", context));
2355
+ this.emit("beforeHelp", context);
2356
+ let helpInformation = this.helpInformation(context);
2357
+ if (deprecatedCallback) {
2358
+ helpInformation = deprecatedCallback(helpInformation);
2359
+ if (typeof helpInformation !== "string" && !Buffer.isBuffer(helpInformation)) {
2360
+ throw new Error("outputHelp callback must return a string or a Buffer");
2361
+ }
2362
+ }
2363
+ context.write(helpInformation);
2364
+ if (this._getHelpOption()?.long) {
2365
+ this.emit(this._getHelpOption().long);
2366
+ }
2367
+ this.emit("afterHelp", context);
2368
+ this._getCommandAndAncestors().forEach((command) => command.emit("afterAllHelp", context));
2259
2369
  }
2260
- };
2261
- }
2262
- styles2.visible = {
2263
- get() {
2264
- const builder = createBuilder(this, this[STYLER], true);
2265
- Object.defineProperty(this, "visible", { value: builder });
2266
- return builder;
2267
- }
2268
- };
2269
- var getModelAnsi = (model, level, type, ...arguments_) => {
2270
- if (model === "rgb") {
2271
- if (level === "ansi16m") {
2272
- return ansi_styles_default[type].ansi16m(...arguments_);
2370
+ helpOption(flags, description) {
2371
+ if (typeof flags === "boolean") {
2372
+ if (flags) {
2373
+ this._helpOption = this._helpOption ?? undefined;
2374
+ } else {
2375
+ this._helpOption = null;
2376
+ }
2377
+ return this;
2378
+ }
2379
+ flags = flags ?? "-h, --help";
2380
+ description = description ?? "display help for command";
2381
+ this._helpOption = this.createOption(flags, description);
2382
+ return this;
2273
2383
  }
2274
- if (level === "ansi256") {
2275
- return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_));
2384
+ _getHelpOption() {
2385
+ if (this._helpOption === undefined) {
2386
+ this.helpOption(undefined, undefined);
2387
+ }
2388
+ return this._helpOption;
2276
2389
  }
2277
- return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_));
2278
- }
2279
- if (model === "hex") {
2280
- return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_));
2281
- }
2282
- return ansi_styles_default[type][model](...arguments_);
2283
- };
2284
- var usedModels = ["rgb", "hex", "ansi256"];
2285
- for (const model of usedModels) {
2286
- styles2[model] = {
2287
- get() {
2288
- const { level } = this;
2289
- return function(...arguments_) {
2290
- const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]);
2291
- return createBuilder(this, styler, this[IS_EMPTY]);
2292
- };
2390
+ addHelpOption(option) {
2391
+ this._helpOption = option;
2392
+ return this;
2293
2393
  }
2294
- };
2295
- const bgModel = "bg" + model[0].toUpperCase() + model.slice(1);
2296
- styles2[bgModel] = {
2297
- get() {
2298
- const { level } = this;
2299
- return function(...arguments_) {
2300
- const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]);
2301
- return createBuilder(this, styler, this[IS_EMPTY]);
2302
- };
2394
+ help(contextOptions) {
2395
+ this.outputHelp(contextOptions);
2396
+ let exitCode = process3.exitCode || 0;
2397
+ if (exitCode === 0 && contextOptions && typeof contextOptions !== "function" && contextOptions.error) {
2398
+ exitCode = 1;
2399
+ }
2400
+ this._exit(exitCode, "commander.help", "(outputHelp)");
2303
2401
  }
2304
- };
2305
- }
2306
- var proto = Object.defineProperties(() => {}, {
2307
- ...styles2,
2308
- level: {
2309
- enumerable: true,
2310
- get() {
2311
- return this[GENERATOR].level;
2312
- },
2313
- set(level) {
2314
- this[GENERATOR].level = level;
2402
+ addHelpText(position, text) {
2403
+ const allowedValues = ["beforeAll", "before", "after", "afterAll"];
2404
+ if (!allowedValues.includes(position)) {
2405
+ throw new Error(`Unexpected value for position to addHelpText.
2406
+ Expecting one of '${allowedValues.join("', '")}'`);
2407
+ }
2408
+ const helpEvent = `${position}Help`;
2409
+ this.on(helpEvent, (context) => {
2410
+ let helpStr;
2411
+ if (typeof text === "function") {
2412
+ helpStr = text({ error: context.error, command: context.command });
2413
+ } else {
2414
+ helpStr = text;
2415
+ }
2416
+ if (helpStr) {
2417
+ context.write(`${helpStr}
2418
+ `);
2419
+ }
2420
+ });
2421
+ return this;
2315
2422
  }
2316
- }
2317
- });
2318
- var createStyler = (open, close, parent) => {
2319
- let openAll;
2320
- let closeAll;
2321
- if (parent === undefined) {
2322
- openAll = open;
2323
- closeAll = close;
2324
- } else {
2325
- openAll = parent.openAll + open;
2326
- closeAll = close + parent.closeAll;
2327
- }
2328
- return {
2329
- open,
2330
- close,
2331
- openAll,
2332
- closeAll,
2333
- parent
2334
- };
2335
- };
2336
- var createBuilder = (self, _styler, _isEmpty) => {
2337
- const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" "));
2338
- Object.setPrototypeOf(builder, proto);
2339
- builder[GENERATOR] = self;
2340
- builder[STYLER] = _styler;
2341
- builder[IS_EMPTY] = _isEmpty;
2342
- return builder;
2343
- };
2344
- var applyStyle = (self, string) => {
2345
- if (self.level <= 0 || !string) {
2346
- return self[IS_EMPTY] ? "" : string;
2347
- }
2348
- let styler = self[STYLER];
2349
- if (styler === undefined) {
2350
- return string;
2351
- }
2352
- const { openAll, closeAll } = styler;
2353
- if (string.includes("\x1B")) {
2354
- while (styler !== undefined) {
2355
- string = stringReplaceAll(string, styler.close, styler.open);
2356
- styler = styler.parent;
2423
+ _outputHelpIfRequested(args) {
2424
+ const helpOption = this._getHelpOption();
2425
+ const helpRequested = helpOption && args.find((arg) => helpOption.is(arg));
2426
+ if (helpRequested) {
2427
+ this.outputHelp();
2428
+ this._exit(0, "commander.helpDisplayed", "(outputHelp)");
2429
+ }
2357
2430
  }
2358
2431
  }
2359
- const lfIndex = string.indexOf(`
2360
- `);
2361
- if (lfIndex !== -1) {
2362
- string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
2432
+ function incrementNodeInspectorPort(args) {
2433
+ return args.map((arg) => {
2434
+ if (!arg.startsWith("--inspect")) {
2435
+ return arg;
2436
+ }
2437
+ let debugOption;
2438
+ let debugHost = "127.0.0.1";
2439
+ let debugPort = "9229";
2440
+ let match;
2441
+ if ((match = arg.match(/^(--inspect(-brk)?)$/)) !== null) {
2442
+ debugOption = match[1];
2443
+ } else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+)$/)) !== null) {
2444
+ debugOption = match[1];
2445
+ if (/^\d+$/.test(match[3])) {
2446
+ debugPort = match[3];
2447
+ } else {
2448
+ debugHost = match[3];
2449
+ }
2450
+ } else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/)) !== null) {
2451
+ debugOption = match[1];
2452
+ debugHost = match[3];
2453
+ debugPort = match[4];
2454
+ }
2455
+ if (debugOption && debugPort !== "0") {
2456
+ return `${debugOption}=${debugHost}:${parseInt(debugPort) + 1}`;
2457
+ }
2458
+ return arg;
2459
+ });
2363
2460
  }
2364
- return openAll + string + closeAll;
2365
- };
2366
- Object.defineProperties(createChalk.prototype, styles2);
2367
- var chalk = createChalk();
2368
- var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
2369
- var source_default = chalk;
2461
+ exports.Command = Command;
2462
+ });
2463
+
2464
+ // node_modules/commander/index.js
2465
+ var require_commander = __commonJS((exports) => {
2466
+ var { Argument } = require_argument();
2467
+ var { Command } = require_command();
2468
+ var { CommanderError, InvalidArgumentError } = require_error();
2469
+ var { Help } = require_help();
2470
+ var { Option } = require_option();
2471
+ exports.program = new Command;
2472
+ exports.createCommand = (name) => new Command(name);
2473
+ exports.createOption = (flags, description) => new Option(flags, description);
2474
+ exports.createArgument = (name, description) => new Argument(name, description);
2475
+ exports.Command = Command;
2476
+ exports.Option = Option;
2477
+ exports.Argument = Argument;
2478
+ exports.Help = Help;
2479
+ exports.CommanderError = CommanderError;
2480
+ exports.InvalidArgumentError = InvalidArgumentError;
2481
+ exports.InvalidOptionArgumentError = InvalidArgumentError;
2482
+ });
2370
2483
 
2371
2484
  // src/commands/create.ts
2485
+ init_source();
2486
+ import { existsSync } from "fs";
2487
+ import { mkdir, writeFile } from "fs/promises";
2488
+ import { join } from "path";
2489
+ var VALID_NPM_CHARS = new Set("abcdefghijklmnopqrstuvwxyz0123456789-_.");
2490
+ function isValidNpmName(name) {
2491
+ if (!name || name.length === 0)
2492
+ return false;
2493
+ if (name.startsWith(".") || name.startsWith("_"))
2494
+ return false;
2495
+ if (name.includes(" "))
2496
+ return false;
2497
+ for (const char of name) {
2498
+ if (!VALID_NPM_CHARS.has(char))
2499
+ return false;
2500
+ }
2501
+ return true;
2502
+ }
2503
+ function normalizePluginName(name) {
2504
+ let normalized = name.toLowerCase().split(" ").join("-");
2505
+ normalized = Array.from(normalized).filter((char) => VALID_NPM_CHARS.has(char)).join("");
2506
+ while (normalized.startsWith(".") || normalized.startsWith("_") || normalized.startsWith("-")) {
2507
+ normalized = normalized.slice(1);
2508
+ }
2509
+ return normalized;
2510
+ }
2372
2511
  async function createPlugin(name, options = {}) {
2373
- const pluginName = name.startsWith("omp-") ? name : `omp-${name}`;
2512
+ let pluginName = name.startsWith("omp-") ? name : `omp-${name}`;
2513
+ if (!isValidNpmName(pluginName)) {
2514
+ const normalized = normalizePluginName(pluginName);
2515
+ if (!normalized || normalized === "omp-" || normalized === "omp") {
2516
+ console.log(source_default.red(`Error: Invalid plugin name "${name}" cannot be normalized to a valid npm name`));
2517
+ process.exitCode = 1;
2518
+ return;
2519
+ }
2520
+ const finalName = normalized.startsWith("omp-") ? normalized : `omp-${normalized}`;
2521
+ console.log(source_default.yellow(`Invalid plugin name. Normalized to: ${finalName}`));
2522
+ pluginName = finalName;
2523
+ }
2374
2524
  const pluginDir = pluginName;
2375
2525
  if (existsSync(pluginDir)) {
2376
2526
  console.log(source_default.red(`Error: Directory ${pluginDir} already exists`));
2527
+ process.exitCode = 1;
2377
2528
  return;
2378
2529
  }
2379
2530
  console.log(source_default.blue(`Creating plugin: ${pluginName}...`));
@@ -2491,13 +2642,32 @@ Provide instructions for the agent here.
2491
2642
  console.log(source_default.dim(" 5. Publish: npm publish"));
2492
2643
  } catch (err) {
2493
2644
  console.log(source_default.red(`Error creating plugin: ${err.message}`));
2645
+ process.exitCode = 1;
2494
2646
  }
2495
2647
  }
2496
2648
 
2497
2649
  // src/commands/doctor.ts
2498
- import { existsSync as existsSync4 } from "fs";
2650
+ import { existsSync as existsSync5 } from "fs";
2499
2651
 
2500
2652
  // src/conflicts.ts
2653
+ function detectIntraPluginDuplicates(pkgJson) {
2654
+ const duplicates = [];
2655
+ if (!pkgJson.omp?.install?.length) {
2656
+ return duplicates;
2657
+ }
2658
+ const destMap = new Map;
2659
+ for (const entry of pkgJson.omp.install) {
2660
+ const sources = destMap.get(entry.dest) || [];
2661
+ sources.push(entry.src);
2662
+ destMap.set(entry.dest, sources);
2663
+ }
2664
+ for (const [dest, sources] of destMap) {
2665
+ if (sources.length > 1) {
2666
+ duplicates.push({ dest, sources });
2667
+ }
2668
+ }
2669
+ return duplicates;
2670
+ }
2501
2671
  function detectConflicts(newPluginName, newPkgJson, existingPlugins) {
2502
2672
  const conflicts = [];
2503
2673
  if (!newPkgJson.omp?.install?.length) {
@@ -2551,36 +2721,63 @@ function formatConflicts(conflicts) {
2551
2721
  }
2552
2722
 
2553
2723
  // src/manifest.ts
2554
- import { existsSync as existsSync2 } from "fs";
2724
+ init_paths();
2725
+ import { existsSync as existsSync3 } from "fs";
2555
2726
  import { mkdir as mkdir2, readFile, writeFile as writeFile2 } from "fs/promises";
2556
- import { dirname, join as join3 } from "path";
2557
-
2558
- // src/paths.ts
2559
- import { homedir } from "os";
2560
- import { join as join2 } from "path";
2561
- var PI_CONFIG_DIR = join2(homedir(), ".pi");
2562
- var PLUGINS_DIR = join2(PI_CONFIG_DIR, "plugins");
2563
- var NODE_MODULES_DIR = join2(PLUGINS_DIR, "node_modules");
2564
- var GLOBAL_PACKAGE_JSON = join2(PLUGINS_DIR, "package.json");
2565
- var GLOBAL_LOCK_FILE = join2(PLUGINS_DIR, "package-lock.json");
2566
- var LEGACY_MANIFEST_PATH = join2(PLUGINS_DIR, "manifest.json");
2567
- var PROJECT_PI_DIR = ".pi";
2568
- var PROJECT_PLUGINS_JSON = join2(PROJECT_PI_DIR, "plugins.json");
2569
- var PROJECT_PLUGINS_LOCK = join2(PROJECT_PI_DIR, "plugins-lock.json");
2570
- var PROJECT_NODE_MODULES = join2(PROJECT_PI_DIR, "node_modules");
2571
-
2572
- // src/manifest.ts
2727
+ import { dirname as dirname2, join as join3 } from "path";
2728
+ function formatPermissionError(err, path) {
2729
+ if (err.code === "EACCES" || err.code === "EPERM") {
2730
+ return `Permission denied: Cannot write to ${path}. Check directory permissions or run with appropriate privileges.`;
2731
+ }
2732
+ return err.message;
2733
+ }
2573
2734
  async function initGlobalPlugins() {
2574
- await mkdir2(PLUGINS_DIR, { recursive: true });
2575
- if (!existsSync2(GLOBAL_PACKAGE_JSON)) {
2576
- const packageJson = {
2577
- name: "pi-plugins",
2578
- version: "1.0.0",
2579
- private: true,
2580
- description: "Global pi plugins managed by omp",
2581
- dependencies: {}
2582
- };
2583
- await writeFile2(GLOBAL_PACKAGE_JSON, JSON.stringify(packageJson, null, 2));
2735
+ try {
2736
+ await mkdir2(PLUGINS_DIR, { recursive: true });
2737
+ if (!existsSync3(GLOBAL_PACKAGE_JSON)) {
2738
+ const packageJson = {
2739
+ name: "pi-plugins",
2740
+ version: "1.0.0",
2741
+ private: true,
2742
+ description: "Global pi plugins managed by omp",
2743
+ dependencies: {}
2744
+ };
2745
+ await writeFile2(GLOBAL_PACKAGE_JSON, JSON.stringify(packageJson, null, 2));
2746
+ }
2747
+ } catch (err) {
2748
+ const error = err;
2749
+ if (error.code === "EACCES" || error.code === "EPERM") {
2750
+ throw new Error(formatPermissionError(error, PLUGINS_DIR));
2751
+ }
2752
+ throw err;
2753
+ }
2754
+ }
2755
+ async function initProjectPlugins() {
2756
+ const PROJECT_PI_DIR2 = dirname2(PROJECT_PLUGINS_JSON);
2757
+ try {
2758
+ await mkdir2(PROJECT_PI_DIR2, { recursive: true });
2759
+ if (!existsSync3(PROJECT_PLUGINS_JSON)) {
2760
+ const pluginsJson = {
2761
+ plugins: {}
2762
+ };
2763
+ await writeFile2(PROJECT_PLUGINS_JSON, JSON.stringify(pluginsJson, null, 2));
2764
+ }
2765
+ if (!existsSync3(PROJECT_PACKAGE_JSON)) {
2766
+ const packageJson = {
2767
+ name: "pi-project-plugins",
2768
+ version: "1.0.0",
2769
+ private: true,
2770
+ description: "Project-local pi plugins managed by omp",
2771
+ dependencies: {}
2772
+ };
2773
+ await writeFile2(PROJECT_PACKAGE_JSON, JSON.stringify(packageJson, null, 2));
2774
+ }
2775
+ } catch (err) {
2776
+ const error = err;
2777
+ if (error.code === "EACCES" || error.code === "EPERM") {
2778
+ throw new Error(formatPermissionError(error, PROJECT_PI_DIR2));
2779
+ }
2780
+ throw err;
2584
2781
  }
2585
2782
  }
2586
2783
  async function loadPluginsJson(global = true) {
@@ -2591,42 +2788,85 @@ async function loadPluginsJson(global = true) {
2591
2788
  if (global) {
2592
2789
  return {
2593
2790
  plugins: parsed.dependencies || {},
2791
+ devDependencies: parsed.devDependencies || {},
2594
2792
  disabled: parsed.omp?.disabled || []
2595
2793
  };
2596
2794
  }
2597
2795
  return {
2598
2796
  plugins: parsed.plugins || {},
2797
+ devDependencies: parsed.devDependencies || {},
2599
2798
  disabled: parsed.disabled || []
2600
2799
  };
2601
2800
  } catch (err) {
2602
2801
  if (err.code === "ENOENT") {
2603
- return { plugins: {}, disabled: [] };
2802
+ return { plugins: {}, devDependencies: {}, disabled: [] };
2604
2803
  }
2605
2804
  throw err;
2606
2805
  }
2607
2806
  }
2807
+ async function syncProjectPackageJson(data) {
2808
+ let existing = {};
2809
+ try {
2810
+ existing = JSON.parse(await readFile(PROJECT_PACKAGE_JSON, "utf-8"));
2811
+ } catch {
2812
+ existing = {
2813
+ name: "pi-project-plugins",
2814
+ version: "1.0.0",
2815
+ private: true,
2816
+ description: "Project-local pi plugins managed by omp"
2817
+ };
2818
+ }
2819
+ existing.dependencies = data.plugins;
2820
+ if (data.devDependencies && Object.keys(data.devDependencies).length > 0) {
2821
+ existing.devDependencies = data.devDependencies;
2822
+ } else {
2823
+ delete existing.devDependencies;
2824
+ }
2825
+ await writeFile2(PROJECT_PACKAGE_JSON, JSON.stringify(existing, null, 2));
2826
+ }
2608
2827
  async function savePluginsJson(data, global = true) {
2609
2828
  const path = global ? GLOBAL_PACKAGE_JSON : PROJECT_PLUGINS_JSON;
2610
- await mkdir2(dirname(path), { recursive: true });
2611
- if (global) {
2612
- let existing = {};
2613
- try {
2614
- existing = JSON.parse(await readFile(path, "utf-8"));
2615
- } catch {
2616
- existing = {
2617
- name: "pi-plugins",
2618
- version: "1.0.0",
2619
- private: true,
2620
- description: "Global pi plugins managed by omp"
2621
- };
2829
+ try {
2830
+ await mkdir2(dirname2(path), { recursive: true });
2831
+ if (global) {
2832
+ let existing = {};
2833
+ try {
2834
+ existing = JSON.parse(await readFile(path, "utf-8"));
2835
+ } catch {
2836
+ existing = {
2837
+ name: "pi-plugins",
2838
+ version: "1.0.0",
2839
+ private: true,
2840
+ description: "Global pi plugins managed by omp"
2841
+ };
2842
+ }
2843
+ existing.dependencies = data.plugins;
2844
+ if (data.devDependencies && Object.keys(data.devDependencies).length > 0) {
2845
+ existing.devDependencies = data.devDependencies;
2846
+ } else {
2847
+ delete existing.devDependencies;
2848
+ }
2849
+ if (data.disabled?.length) {
2850
+ existing.omp = { ...existing.omp || {}, disabled: data.disabled };
2851
+ }
2852
+ await writeFile2(path, JSON.stringify(existing, null, 2));
2853
+ } else {
2854
+ const output = { plugins: data.plugins };
2855
+ if (data.devDependencies && Object.keys(data.devDependencies).length > 0) {
2856
+ output.devDependencies = data.devDependencies;
2857
+ }
2858
+ if (data.disabled?.length) {
2859
+ output.disabled = data.disabled;
2860
+ }
2861
+ await writeFile2(path, JSON.stringify(output, null, 2));
2862
+ await syncProjectPackageJson(data);
2622
2863
  }
2623
- existing.dependencies = data.plugins;
2624
- if (data.disabled?.length) {
2625
- existing.omp = { ...existing.omp || {}, disabled: data.disabled };
2864
+ } catch (err) {
2865
+ const error = err;
2866
+ if (error.code === "EACCES" || error.code === "EPERM") {
2867
+ throw new Error(formatPermissionError(error, path));
2626
2868
  }
2627
- await writeFile2(path, JSON.stringify(existing, null, 2));
2628
- } else {
2629
- await writeFile2(path, JSON.stringify(data, null, 2));
2869
+ throw err;
2630
2870
  }
2631
2871
  }
2632
2872
  async function readPluginPackageJson(pluginName, global = true) {
@@ -2649,7 +2889,7 @@ function getPluginSourceDir(pluginName, global = true) {
2649
2889
  return join3(nodeModules, pluginName);
2650
2890
  }
2651
2891
  function hasLegacyManifest() {
2652
- return existsSync2(LEGACY_MANIFEST_PATH);
2892
+ return existsSync3(LEGACY_MANIFEST_PATH);
2653
2893
  }
2654
2894
  async function loadLegacyManifest() {
2655
2895
  try {
@@ -2671,11 +2911,32 @@ async function getInstalledPlugins(global = true) {
2671
2911
  return plugins;
2672
2912
  }
2673
2913
 
2914
+ // src/commands/doctor.ts
2915
+ init_paths();
2916
+
2674
2917
  // src/symlinks.ts
2675
- import { existsSync as existsSync3, lstatSync } from "fs";
2918
+ import { existsSync as existsSync4, lstatSync } from "fs";
2676
2919
  import { mkdir as mkdir3, readlink, rm, symlink } from "fs/promises";
2677
- import { dirname as dirname2, join as join4 } from "path";
2678
- async function createPluginSymlinks(pluginName, pkgJson, global = true, verbose = true) {
2920
+ import { platform } from "os";
2921
+ import { dirname as dirname3, join as join4, resolve as resolve2 } from "path";
2922
+ init_paths();
2923
+ init_source();
2924
+ var isWindows = platform() === "win32";
2925
+ function formatPermissionError2(err, path) {
2926
+ if (err.code === "EACCES" || err.code === "EPERM") {
2927
+ return `Permission denied: Cannot write to ${path}. Check directory permissions or run with appropriate privileges.`;
2928
+ }
2929
+ return err.message;
2930
+ }
2931
+ function isPathWithinBase(basePath, targetPath) {
2932
+ const normalizedBase = resolve2(basePath);
2933
+ const resolvedTarget = resolve2(basePath, targetPath);
2934
+ return resolvedTarget === normalizedBase || resolvedTarget.startsWith(`${normalizedBase}/`);
2935
+ }
2936
+ function getBaseDir(global) {
2937
+ return global ? PI_CONFIG_DIR : PROJECT_PI_DIR;
2938
+ }
2939
+ async function createPluginSymlinks(pluginName, pkgJson, global = true, verbose = true, skipDestinations) {
2679
2940
  const result = { created: [], errors: [] };
2680
2941
  const sourceDir = getPluginSourceDir(pluginName, global);
2681
2942
  if (!pkgJson.omp?.install?.length) {
@@ -2684,56 +2945,115 @@ async function createPluginSymlinks(pluginName, pkgJson, global = true, verbose
2684
2945
  }
2685
2946
  return result;
2686
2947
  }
2948
+ const baseDir = getBaseDir(global);
2687
2949
  for (const entry of pkgJson.omp.install) {
2950
+ if (skipDestinations?.has(entry.dest)) {
2951
+ if (verbose) {
2952
+ console.log(source_default.dim(` Skipped: ${entry.dest} (conflict resolved to existing plugin)`));
2953
+ }
2954
+ continue;
2955
+ }
2956
+ if (!isPathWithinBase(baseDir, entry.dest)) {
2957
+ const msg = `Path traversal blocked: ${entry.dest} escapes base directory`;
2958
+ result.errors.push(msg);
2959
+ if (verbose) {
2960
+ console.log(source_default.red(` \u2717 ${msg}`));
2961
+ }
2962
+ continue;
2963
+ }
2688
2964
  try {
2689
2965
  const src = join4(sourceDir, entry.src);
2690
- const dest = join4(PI_CONFIG_DIR, entry.dest);
2691
- if (!existsSync3(src)) {
2966
+ const dest = join4(baseDir, entry.dest);
2967
+ if (!existsSync4(src)) {
2692
2968
  result.errors.push(`Source not found: ${entry.src}`);
2693
2969
  if (verbose) {
2694
2970
  console.log(source_default.yellow(` \u26A0 Source not found: ${entry.src}`));
2695
2971
  }
2696
2972
  continue;
2697
2973
  }
2698
- await mkdir3(dirname2(dest), { recursive: true });
2974
+ await mkdir3(dirname3(dest), { recursive: true });
2699
2975
  try {
2700
2976
  await rm(dest, { force: true, recursive: true });
2701
2977
  } catch {}
2702
- await symlink(src, dest);
2978
+ try {
2979
+ if (isWindows) {
2980
+ const stats = lstatSync(src);
2981
+ if (stats.isDirectory()) {
2982
+ await symlink(src, dest, "junction");
2983
+ } else {
2984
+ await symlink(src, dest, "file");
2985
+ }
2986
+ } else {
2987
+ await symlink(src, dest);
2988
+ }
2989
+ } catch (symlinkErr) {
2990
+ const error = symlinkErr;
2991
+ if (isWindows && error.code === "EPERM") {
2992
+ console.log(source_default.red(` Permission denied creating symlink.`));
2993
+ console.log(source_default.dim(" On Windows, enable Developer Mode or run as Administrator."));
2994
+ console.log(source_default.dim(" Settings > Update & Security > For developers > Developer Mode"));
2995
+ }
2996
+ throw symlinkErr;
2997
+ }
2703
2998
  result.created.push(entry.dest);
2704
2999
  if (verbose) {
2705
3000
  console.log(source_default.dim(` Linked: ${entry.dest} \u2192 ${entry.src}`));
2706
3001
  }
2707
3002
  } catch (err) {
2708
- const msg = `Failed to link ${entry.dest}: ${err.message}`;
3003
+ const error = err;
3004
+ const msg = `Failed to link ${entry.dest}: ${formatPermissionError2(error, join4(baseDir, entry.dest))}`;
2709
3005
  result.errors.push(msg);
2710
3006
  if (verbose) {
2711
3007
  console.log(source_default.red(` \u2717 ${msg}`));
3008
+ if (error.code === "EACCES" || error.code === "EPERM") {
3009
+ console.log(source_default.dim(" Check directory permissions or run with appropriate privileges."));
3010
+ }
2712
3011
  }
2713
3012
  }
2714
3013
  }
2715
3014
  return result;
2716
3015
  }
2717
- async function removePluginSymlinks(_pluginName, pkgJson, verbose = true) {
2718
- const result = { created: [], errors: [] };
3016
+ async function removePluginSymlinks(_pluginName, pkgJson, global = true, verbose = true) {
3017
+ const result = { removed: [], errors: [], skippedNonSymlinks: [] };
2719
3018
  if (!pkgJson.omp?.install?.length) {
2720
3019
  return result;
2721
3020
  }
3021
+ const baseDir = getBaseDir(global);
2722
3022
  for (const entry of pkgJson.omp.install) {
2723
- const dest = join4(PI_CONFIG_DIR, entry.dest);
3023
+ if (!isPathWithinBase(baseDir, entry.dest)) {
3024
+ const msg = `Path traversal blocked: ${entry.dest} escapes base directory`;
3025
+ result.errors.push(msg);
3026
+ if (verbose) {
3027
+ console.log(source_default.red(` \u2717 ${msg}`));
3028
+ }
3029
+ continue;
3030
+ }
3031
+ const dest = join4(baseDir, entry.dest);
2724
3032
  try {
2725
- if (existsSync3(dest)) {
3033
+ if (existsSync4(dest)) {
3034
+ const stats = lstatSync(dest);
3035
+ if (!stats.isSymbolicLink()) {
3036
+ result.skippedNonSymlinks.push(dest);
3037
+ if (verbose) {
3038
+ console.log(source_default.yellow(` \u26A0 Skipping ${entry.dest}: not a symlink (may contain user data)`));
3039
+ }
3040
+ continue;
3041
+ }
2726
3042
  await rm(dest, { force: true, recursive: true });
2727
- result.created.push(entry.dest);
3043
+ result.removed.push(entry.dest);
2728
3044
  if (verbose) {
2729
3045
  console.log(source_default.dim(` Removed: ${entry.dest}`));
2730
3046
  }
2731
3047
  }
2732
3048
  } catch (err) {
2733
- const msg = `Failed to remove ${entry.dest}: ${err.message}`;
3049
+ const error = err;
3050
+ const msg = `Failed to remove ${entry.dest}: ${formatPermissionError2(error, dest)}`;
2734
3051
  result.errors.push(msg);
2735
3052
  if (verbose) {
2736
3053
  console.log(source_default.yellow(` \u26A0 ${msg}`));
3054
+ if (error.code === "EACCES" || error.code === "EPERM") {
3055
+ console.log(source_default.dim(" Check directory permissions or run with appropriate privileges."));
3056
+ }
2737
3057
  }
2738
3058
  }
2739
3059
  }
@@ -2742,13 +3062,18 @@ async function removePluginSymlinks(_pluginName, pkgJson, verbose = true) {
2742
3062
  async function checkPluginSymlinks(pluginName, pkgJson, global = true) {
2743
3063
  const result = { valid: [], broken: [], missing: [] };
2744
3064
  const sourceDir = getPluginSourceDir(pluginName, global);
3065
+ const baseDir = getBaseDir(global);
2745
3066
  if (!pkgJson.omp?.install?.length) {
2746
3067
  return result;
2747
3068
  }
2748
3069
  for (const entry of pkgJson.omp.install) {
3070
+ if (!isPathWithinBase(baseDir, entry.dest)) {
3071
+ result.broken.push(entry.dest);
3072
+ continue;
3073
+ }
2749
3074
  const src = join4(sourceDir, entry.src);
2750
- const dest = join4(PI_CONFIG_DIR, entry.dest);
2751
- if (!existsSync3(dest)) {
3075
+ const dest = join4(baseDir, entry.dest);
3076
+ if (!existsSync4(dest)) {
2752
3077
  result.missing.push(entry.dest);
2753
3078
  continue;
2754
3079
  }
@@ -2756,7 +3081,7 @@ async function checkPluginSymlinks(pluginName, pkgJson, global = true) {
2756
3081
  const stats = lstatSync(dest);
2757
3082
  if (stats.isSymbolicLink()) {
2758
3083
  const _target = await readlink(dest);
2759
- if (existsSync3(src)) {
3084
+ if (existsSync4(src)) {
2760
3085
  result.valid.push(entry.dest);
2761
3086
  } else {
2762
3087
  result.broken.push(entry.dest);
@@ -2770,10 +3095,11 @@ async function checkPluginSymlinks(pluginName, pkgJson, global = true) {
2770
3095
  }
2771
3096
  return result;
2772
3097
  }
2773
- async function traceInstalledFile(filePath, installedPlugins) {
3098
+ async function traceInstalledFile(filePath, installedPlugins, global = true) {
3099
+ const baseDir = getBaseDir(global);
2774
3100
  let relativePath = filePath;
2775
- if (filePath.startsWith(PI_CONFIG_DIR)) {
2776
- relativePath = filePath.slice(PI_CONFIG_DIR.length + 1);
3101
+ if (filePath.startsWith(baseDir)) {
3102
+ relativePath = filePath.slice(baseDir.length + 1);
2777
3103
  }
2778
3104
  for (const [name, pkgJson] of installedPlugins) {
2779
3105
  if (pkgJson.omp?.install) {
@@ -2788,13 +3114,14 @@ async function traceInstalledFile(filePath, installedPlugins) {
2788
3114
  }
2789
3115
 
2790
3116
  // src/commands/doctor.ts
3117
+ init_source();
2791
3118
  async function runDoctor(options = {}) {
2792
- const isGlobal = options.global !== false;
3119
+ const isGlobal = resolveScope(options);
2793
3120
  const results = [];
2794
3121
  console.log(source_default.blue(`Running health checks...
2795
3122
  `));
2796
3123
  const pluginsDir = isGlobal ? PLUGINS_DIR : ".pi";
2797
- if (!existsSync4(pluginsDir)) {
3124
+ if (!existsSync5(pluginsDir)) {
2798
3125
  results.push({
2799
3126
  check: "Plugins directory",
2800
3127
  status: "warning",
@@ -2808,8 +3135,8 @@ async function runDoctor(options = {}) {
2808
3135
  message: pluginsDir
2809
3136
  });
2810
3137
  }
2811
- const packageJsonPath = isGlobal ? GLOBAL_PACKAGE_JSON : ".pi/plugins.json";
2812
- if (!existsSync4(packageJsonPath)) {
3138
+ const packageJsonPath = isGlobal ? GLOBAL_PACKAGE_JSON : PROJECT_PLUGINS_JSON;
3139
+ if (!existsSync5(packageJsonPath)) {
2813
3140
  results.push({
2814
3141
  check: "Package manifest",
2815
3142
  status: "warning",
@@ -2823,8 +3150,8 @@ async function runDoctor(options = {}) {
2823
3150
  message: packageJsonPath
2824
3151
  });
2825
3152
  }
2826
- const nodeModules = isGlobal ? NODE_MODULES_DIR : ".pi/node_modules";
2827
- if (!existsSync4(nodeModules)) {
3153
+ const nodeModules = isGlobal ? NODE_MODULES_DIR : PROJECT_NODE_MODULES;
3154
+ if (!existsSync5(nodeModules)) {
2828
3155
  results.push({
2829
3156
  check: "Node modules",
2830
3157
  status: "warning",
@@ -2901,6 +3228,31 @@ async function runDoctor(options = {}) {
2901
3228
  fix: "Run: omp install (to reinstall) or remove from manifest"
2902
3229
  });
2903
3230
  }
3231
+ const missingDeps = [];
3232
+ for (const [name, pkgJson] of installedPlugins) {
3233
+ if (pkgJson.dependencies) {
3234
+ for (const depName of Object.keys(pkgJson.dependencies)) {
3235
+ const depPkgJson = await readPluginPackageJson(depName, isGlobal);
3236
+ if (!depPkgJson) {
3237
+ if (pluginsJson.plugins[depName]) {
3238
+ missingDeps.push(`${name} requires ${depName} (not in node_modules)`);
3239
+ }
3240
+ } else if (depPkgJson.omp?.install && depPkgJson.omp.install.length > 0) {
3241
+ if (!pluginsJson.plugins[depName]) {
3242
+ missingDeps.push(`${name} requires omp plugin ${depName} (installed but not in manifest)`);
3243
+ }
3244
+ }
3245
+ }
3246
+ }
3247
+ }
3248
+ if (missingDeps.length > 0) {
3249
+ results.push({
3250
+ check: "Missing omp dependencies",
3251
+ status: "warning",
3252
+ message: missingDeps.join("; "),
3253
+ fix: isGlobal ? "Run: npm install in ~/.pi/plugins" : "Run: npm install in .pi"
3254
+ });
3255
+ }
2904
3256
  if (options.json) {
2905
3257
  console.log(JSON.stringify({ results }, null, 2));
2906
3258
  return;
@@ -2935,6 +3287,7 @@ async function runDoctor(options = {}) {
2935
3287
  } else {
2936
3288
  if (errors.length > 0) {
2937
3289
  console.log(source_default.red(`${errors.length} error(s) found`));
3290
+ process.exitCode = 1;
2938
3291
  }
2939
3292
  if (warnings.length > 0) {
2940
3293
  console.log(source_default.yellow(`${warnings.length} warning(s) found`));
@@ -2954,28 +3307,81 @@ Missing symlinks:`));
2954
3307
  console.log(source_default.dim(` - ${s}`));
2955
3308
  }
2956
3309
  }
3310
+ if (options.fix) {
3311
+ let fixedAnything = false;
3312
+ if (brokenSymlinks.length > 0 || missingSymlinks.length > 0) {
3313
+ console.log(source_default.blue(`
3314
+ Attempting to fix broken/missing symlinks...`));
3315
+ for (const [name, pkgJson] of installedPlugins) {
3316
+ const symlinkResult = await createPluginSymlinks(name, pkgJson, isGlobal, false);
3317
+ if (symlinkResult.created.length > 0) {
3318
+ fixedAnything = true;
3319
+ console.log(source_default.green(` \u2713 Re-created symlinks for ${name}`));
3320
+ }
3321
+ if (symlinkResult.errors.length > 0) {
3322
+ for (const err of symlinkResult.errors) {
3323
+ console.log(source_default.red(` \u2717 ${name}: ${err}`));
3324
+ }
3325
+ }
3326
+ }
3327
+ }
3328
+ if (orphaned.length > 0) {
3329
+ console.log(source_default.blue(`
3330
+ Removing orphaned entries from manifest...`));
3331
+ for (const name of orphaned) {
3332
+ delete pluginsJson.plugins[name];
3333
+ console.log(source_default.green(` \u2713 Removed ${name}`));
3334
+ }
3335
+ await savePluginsJson(pluginsJson, isGlobal);
3336
+ fixedAnything = true;
3337
+ }
3338
+ if (conflicts.length > 0) {
3339
+ console.log(source_default.yellow(`
3340
+ Conflicts cannot be auto-fixed. Please resolve manually:`));
3341
+ for (const conflict of formatConflicts(conflicts)) {
3342
+ console.log(source_default.dim(` - ${conflict}`));
3343
+ }
3344
+ }
3345
+ if (fixedAnything) {
3346
+ console.log(source_default.green(`
3347
+ \u2713 Fixes applied. Run 'omp doctor' again to verify.`));
3348
+ } else if (conflicts.length === 0) {
3349
+ console.log(source_default.dim(`
3350
+ No fixable issues found.`));
3351
+ }
3352
+ }
2957
3353
  }
2958
3354
 
2959
3355
  // src/commands/enable.ts
3356
+ init_paths();
3357
+ init_source();
2960
3358
  async function enablePlugin(name, options = {}) {
2961
- const isGlobal = options.global !== false;
3359
+ const isGlobal = resolveScope(options);
2962
3360
  const pluginsJson = await loadPluginsJson(isGlobal);
2963
3361
  if (!pluginsJson.plugins[name]) {
2964
3362
  console.log(source_default.yellow(`Plugin "${name}" is not installed.`));
3363
+ process.exitCode = 1;
2965
3364
  return;
2966
3365
  }
2967
3366
  if (!pluginsJson.disabled?.includes(name)) {
2968
3367
  console.log(source_default.yellow(`Plugin "${name}" is already enabled.`));
3368
+ process.exitCode = 1;
2969
3369
  return;
2970
3370
  }
2971
3371
  try {
2972
3372
  const pkgJson = await readPluginPackageJson(name, isGlobal);
2973
3373
  if (!pkgJson) {
2974
3374
  console.log(source_default.red(`Could not read package.json for ${name}`));
3375
+ process.exitCode = 1;
2975
3376
  return;
2976
3377
  }
2977
- console.log(source_default.blue(`Enabling ${name}...`));
2978
- await createPluginSymlinks(name, pkgJson, isGlobal);
3378
+ const symlinkStatus = await checkPluginSymlinks(name, pkgJson, isGlobal);
3379
+ if (symlinkStatus.valid.length > 0 && symlinkStatus.broken.length === 0 && symlinkStatus.missing.length === 0) {
3380
+ console.log(source_default.yellow(`Plugin "${name}" symlinks are already in place.`));
3381
+ } else {
3382
+ console.log(source_default.blue(`Enabling ${name}...`));
3383
+ await createPluginSymlinks(name, pkgJson, isGlobal);
3384
+ }
2979
3385
  pluginsJson.disabled = pluginsJson.disabled.filter((n) => n !== name);
2980
3386
  await savePluginsJson(pluginsJson, isGlobal);
2981
3387
  console.log(source_default.green(`\u2713 Enabled "${name}"`));
@@ -2984,27 +3390,31 @@ async function enablePlugin(name, options = {}) {
2984
3390
  }
2985
3391
  } catch (err) {
2986
3392
  console.log(source_default.red(`Error enabling plugin: ${err.message}`));
3393
+ process.exitCode = 1;
2987
3394
  }
2988
3395
  }
2989
3396
  async function disablePlugin(name, options = {}) {
2990
- const isGlobal = options.global !== false;
3397
+ const isGlobal = resolveScope(options);
2991
3398
  const pluginsJson = await loadPluginsJson(isGlobal);
2992
3399
  if (!pluginsJson.plugins[name]) {
2993
3400
  console.log(source_default.yellow(`Plugin "${name}" is not installed.`));
3401
+ process.exitCode = 1;
2994
3402
  return;
2995
3403
  }
2996
3404
  if (pluginsJson.disabled?.includes(name)) {
2997
3405
  console.log(source_default.yellow(`Plugin "${name}" is already disabled.`));
3406
+ process.exitCode = 1;
2998
3407
  return;
2999
3408
  }
3000
3409
  try {
3001
3410
  const pkgJson = await readPluginPackageJson(name, isGlobal);
3002
3411
  if (!pkgJson) {
3003
3412
  console.log(source_default.red(`Could not read package.json for ${name}`));
3413
+ process.exitCode = 1;
3004
3414
  return;
3005
3415
  }
3006
3416
  console.log(source_default.blue(`Disabling ${name}...`));
3007
- await removePluginSymlinks(name, pkgJson);
3417
+ await removePluginSymlinks(name, pkgJson, isGlobal);
3008
3418
  if (!pluginsJson.disabled) {
3009
3419
  pluginsJson.disabled = [];
3010
3420
  }
@@ -3018,21 +3428,65 @@ async function disablePlugin(name, options = {}) {
3018
3428
  }
3019
3429
  } catch (err) {
3020
3430
  console.log(source_default.red(`Error disabling plugin: ${err.message}`));
3431
+ process.exitCode = 1;
3021
3432
  }
3022
3433
  }
3023
3434
 
3024
3435
  // src/npm.ts
3025
- import { execSync } from "child_process";
3026
- function npmExec(args, cwd) {
3027
- const cmd = `npm ${args.join(" ")}`;
3028
- return execSync(cmd, {
3029
- cwd,
3030
- stdio: ["pipe", "pipe", "pipe"],
3031
- encoding: "utf-8"
3032
- });
3436
+ import { execFileSync } from "child_process";
3437
+ function checkNpmAvailable() {
3438
+ try {
3439
+ const version = execFileSync("npm", ["--version"], {
3440
+ encoding: "utf-8",
3441
+ stdio: ["pipe", "pipe", "pipe"]
3442
+ }).trim();
3443
+ const major = parseInt(version.split(".")[0], 10);
3444
+ if (major < 7) {
3445
+ return {
3446
+ available: false,
3447
+ version,
3448
+ error: `npm version ${version} is too old. Please upgrade to npm 7 or later.`
3449
+ };
3450
+ }
3451
+ return { available: true, version };
3452
+ } catch {
3453
+ return {
3454
+ available: false,
3455
+ error: "npm is not installed or not in PATH. Please install Node.js/npm."
3456
+ };
3457
+ }
3458
+ }
3459
+ var DEFAULT_TIMEOUT_MS = 60000;
3460
+ function npmExec(args, cwd, timeout = DEFAULT_TIMEOUT_MS) {
3461
+ try {
3462
+ return execFileSync("npm", args, {
3463
+ cwd,
3464
+ stdio: ["pipe", "pipe", "pipe"],
3465
+ encoding: "utf-8",
3466
+ timeout
3467
+ });
3468
+ } catch (err) {
3469
+ const error = err;
3470
+ if (error.killed || error.code === "ETIMEDOUT") {
3471
+ throw new Error(`npm operation timed out after ${timeout / 1000} seconds`);
3472
+ }
3473
+ throw err;
3474
+ }
3033
3475
  }
3034
- function npmExecWithPrefix(args, prefix) {
3035
- return npmExec(["--prefix", prefix, ...args]);
3476
+ function npmExecWithPrefix(args, prefix, timeout = DEFAULT_TIMEOUT_MS) {
3477
+ try {
3478
+ return execFileSync("npm", ["--prefix", prefix, ...args], {
3479
+ stdio: ["pipe", "pipe", "pipe"],
3480
+ encoding: "utf-8",
3481
+ timeout
3482
+ });
3483
+ } catch (err) {
3484
+ const error = err;
3485
+ if (error.killed || error.code === "ETIMEDOUT") {
3486
+ throw new Error(`npm operation timed out after ${timeout / 1000} seconds`);
3487
+ }
3488
+ throw err;
3489
+ }
3036
3490
  }
3037
3491
  async function npmInstall(packages, prefix, options = {}) {
3038
3492
  const args = ["install"];
@@ -3056,13 +3510,9 @@ async function npmInfo(packageName) {
3056
3510
  }
3057
3511
  }
3058
3512
  async function npmSearch(query, keyword = "omp-plugin") {
3059
- try {
3060
- const searchTerm = keyword ? `keywords:${keyword} ${query}` : query;
3061
- const output = npmExec(["search", searchTerm, "--json"]);
3062
- return JSON.parse(output);
3063
- } catch {
3064
- return [];
3065
- }
3513
+ const searchTerm = keyword ? `keywords:${keyword} ${query}` : query;
3514
+ const output = npmExec(["search", searchTerm, "--json"]);
3515
+ return JSON.parse(output);
3066
3516
  }
3067
3517
  async function npmOutdated(prefix) {
3068
3518
  try {
@@ -3089,12 +3539,14 @@ async function npmUpdate(packages, prefix) {
3089
3539
  }
3090
3540
 
3091
3541
  // src/commands/info.ts
3542
+ init_source();
3092
3543
  async function showInfo(packageName, options = {}) {
3093
3544
  console.log(source_default.blue(`Fetching info for ${packageName}...`));
3094
3545
  try {
3095
3546
  const info = await npmInfo(packageName);
3096
3547
  if (!info) {
3097
3548
  console.log(source_default.red(`Package not found: ${packageName}`));
3549
+ process.exitCode = 1;
3098
3550
  return;
3099
3551
  }
3100
3552
  if (options.json) {
@@ -3122,6 +3574,13 @@ async function showInfo(packageName, options = {}) {
3122
3574
  if (info.keywords?.length) {
3123
3575
  console.log(source_default.dim("keywords: ") + info.keywords.join(", "));
3124
3576
  }
3577
+ if (info.dependencies && Object.keys(info.dependencies).length > 0) {
3578
+ console.log(source_default.dim(`
3579
+ dependencies:`));
3580
+ for (const [depName, depVersion] of Object.entries(info.dependencies)) {
3581
+ console.log(source_default.dim(` ${depName}: ${depVersion}`));
3582
+ }
3583
+ }
3125
3584
  const isOmpPlugin = info.keywords?.includes("omp-plugin");
3126
3585
  if (isOmpPlugin) {
3127
3586
  console.log(source_default.green(`
@@ -3131,27 +3590,59 @@ async function showInfo(packageName, options = {}) {
3131
3590
  \u26A0 This package does not have the omp-plugin keyword`));
3132
3591
  console.log(source_default.dim(" It may work, but might not have omp.install configuration"));
3133
3592
  }
3134
- if (options.versions && info["dist-tags"]) {
3593
+ if (info.omp?.install?.length) {
3135
3594
  console.log(source_default.dim(`
3595
+ Files to install:`));
3596
+ for (const entry of info.omp.install) {
3597
+ console.log(source_default.dim(` ${entry.src} \u2192 ${entry.dest}`));
3598
+ }
3599
+ }
3600
+ if (options.versions || options.allVersions) {
3601
+ if (info["dist-tags"]) {
3602
+ console.log(source_default.dim(`
3136
3603
  dist-tags:`));
3137
- for (const [tag, version] of Object.entries(info["dist-tags"])) {
3138
- console.log(source_default.dim(` ${tag}: `) + version);
3604
+ for (const [tag, version] of Object.entries(info["dist-tags"])) {
3605
+ console.log(source_default.dim(` ${tag}: `) + version);
3606
+ }
3607
+ }
3608
+ if (info.versions?.length) {
3609
+ console.log(source_default.dim(`
3610
+ all versions:`));
3611
+ if (options.allVersions) {
3612
+ console.log(source_default.dim(` ${info.versions.join(", ")}`));
3613
+ } else {
3614
+ const versionsToShow = info.versions.slice(-10);
3615
+ console.log(source_default.dim(` ${versionsToShow.join(", ")}`));
3616
+ if (info.versions.length > 10) {
3617
+ console.log(source_default.dim(` ... and ${info.versions.length - 10} more (use --all-versions to see all)`));
3618
+ }
3619
+ }
3139
3620
  }
3140
3621
  }
3141
3622
  console.log();
3142
3623
  console.log(source_default.dim(`Install with: omp install ${packageName}`));
3143
3624
  } catch (err) {
3144
3625
  console.log(source_default.red(`Error fetching info: ${err.message}`));
3626
+ process.exitCode = 1;
3145
3627
  }
3146
3628
  }
3147
3629
 
3148
3630
  // src/commands/init.ts
3149
- import { existsSync as existsSync5 } from "fs";
3631
+ init_paths();
3632
+ init_source();
3633
+ import { existsSync as existsSync6 } from "fs";
3150
3634
  import { mkdir as mkdir4, writeFile as writeFile3 } from "fs/promises";
3635
+ function formatPermissionError3(err, path) {
3636
+ if (err.code === "EACCES" || err.code === "EPERM") {
3637
+ return `Permission denied: Cannot write to ${path}. Check directory permissions or run with appropriate privileges.`;
3638
+ }
3639
+ return err.message;
3640
+ }
3151
3641
  async function initProject(options = {}) {
3152
- if (existsSync5(PROJECT_PLUGINS_JSON) && !options.force) {
3642
+ if (existsSync6(PROJECT_PLUGINS_JSON) && !options.force) {
3153
3643
  console.log(source_default.yellow(`${PROJECT_PLUGINS_JSON} already exists.`));
3154
3644
  console.log(source_default.dim("Use --force to overwrite"));
3645
+ process.exitCode = 1;
3155
3646
  return;
3156
3647
  }
3157
3648
  try {
@@ -3168,22 +3659,49 @@ async function initProject(options = {}) {
3168
3659
  console.log(source_default.dim(" 2. Or edit plugins.json directly"));
3169
3660
  console.log(source_default.dim(" 3. Run: omp install (to install all)"));
3170
3661
  } catch (err) {
3171
- console.log(source_default.red(`Error initializing project: ${err.message}`));
3662
+ const error = err;
3663
+ if (error.code === "EACCES" || error.code === "EPERM") {
3664
+ console.log(source_default.red(formatPermissionError3(error, PROJECT_PI_DIR)));
3665
+ console.log(source_default.dim(" Check directory permissions or run with appropriate privileges."));
3666
+ } else {
3667
+ console.log(source_default.red(`Error initializing project: ${error.message}`));
3668
+ }
3669
+ process.exitCode = 1;
3172
3670
  }
3173
3671
  }
3174
3672
 
3175
3673
  // src/commands/install.ts
3176
- import { execSync as execSync2 } from "child_process";
3177
- import { existsSync as existsSync6 } from "fs";
3178
- import { cp, mkdir as mkdir5, readFile as readFile2, rm as rm2 } from "fs/promises";
3179
- import { basename, join as join5, resolve } from "path";
3674
+ import { execFileSync as execFileSync2 } from "child_process";
3675
+ import { existsSync as existsSync8 } from "fs";
3676
+ import { cp, mkdir as mkdir5, readFile as readFile3, rm as rm2 } from "fs/promises";
3677
+ import { basename, join as join5, resolve as resolve3 } from "path";
3180
3678
  import { createInterface } from "readline";
3679
+ init_lockfile();
3680
+ init_paths();
3681
+ init_source();
3682
+ async function processOmpDependencies(pkgJson, isGlobal, seen) {
3683
+ if (!pkgJson.dependencies)
3684
+ return;
3685
+ for (const depName of Object.keys(pkgJson.dependencies)) {
3686
+ if (seen.has(depName)) {
3687
+ console.log(source_default.yellow(` Skipping circular dependency: ${depName}`));
3688
+ continue;
3689
+ }
3690
+ seen.add(depName);
3691
+ const depPkgJson = await readPluginPackageJson(depName, isGlobal);
3692
+ if (depPkgJson?.omp?.install) {
3693
+ console.log(source_default.dim(` Processing dependency: ${depName}`));
3694
+ await createPluginSymlinks(depName, depPkgJson, isGlobal);
3695
+ await processOmpDependencies(depPkgJson, isGlobal, seen);
3696
+ }
3697
+ }
3698
+ }
3181
3699
  async function promptConflictResolution(conflict) {
3182
3700
  const rl = createInterface({
3183
3701
  input: process.stdin,
3184
3702
  output: process.stdout
3185
3703
  });
3186
- return new Promise((resolve2) => {
3704
+ return new Promise((resolve4) => {
3187
3705
  console.log(source_default.yellow(`
3188
3706
  \u26A0 Conflict: ${formatConflicts([conflict])[0]}`));
3189
3707
  conflict.plugins.forEach((p, i) => {
@@ -3194,9 +3712,9 @@ async function promptConflictResolution(conflict) {
3194
3712
  rl.close();
3195
3713
  const choice = parseInt(answer, 10);
3196
3714
  if (choice > 0 && choice <= conflict.plugins.length) {
3197
- resolve2(choice - 1);
3715
+ resolve4(choice - 1);
3198
3716
  } else {
3199
- resolve2(null);
3717
+ resolve4(null);
3200
3718
  }
3201
3719
  });
3202
3720
  });
@@ -3225,23 +3743,25 @@ function isLocalPath(spec) {
3225
3743
  return spec.startsWith("/") || spec.startsWith("./") || spec.startsWith("../") || spec.startsWith("~");
3226
3744
  }
3227
3745
  async function installPlugin(packages, options = {}) {
3228
- const isGlobal = options.global !== false;
3746
+ const isGlobal = resolveScope(options);
3229
3747
  const prefix = isGlobal ? PLUGINS_DIR : ".pi";
3230
3748
  const _nodeModules = isGlobal ? NODE_MODULES_DIR : PROJECT_NODE_MODULES;
3231
3749
  if (isGlobal) {
3232
3750
  await initGlobalPlugins();
3233
3751
  } else {
3234
- await mkdir5(prefix, { recursive: true });
3235
- if (!existsSync6(PROJECT_PLUGINS_JSON)) {
3236
- await savePluginsJson({ plugins: {} }, false);
3237
- }
3752
+ await initProjectPlugins();
3238
3753
  }
3239
3754
  if (!packages || packages.length === 0) {
3240
3755
  const pluginsJson = await loadPluginsJson(isGlobal);
3241
- packages = Object.entries(pluginsJson.plugins).map(([name, version]) => `${name}@${version}`);
3756
+ const lockFile = await Promise.resolve().then(() => (init_lockfile(), exports_lockfile)).then((m) => m.loadLockFile(isGlobal));
3757
+ packages = await Promise.all(Object.entries(pluginsJson.plugins).map(async ([name, version]) => {
3758
+ const lockedVersion = lockFile?.packages[name]?.version;
3759
+ return `${name}@${lockedVersion || version}`;
3760
+ }));
3242
3761
  if (packages.length === 0) {
3243
3762
  console.log(source_default.yellow("No plugins to install."));
3244
3763
  console.log(source_default.dim(isGlobal ? "Add plugins with: omp install <package>" : "Add plugins to .pi/plugins.json"));
3764
+ process.exitCode = 1;
3245
3765
  return;
3246
3766
  }
3247
3767
  console.log(source_default.blue(`Installing ${packages.length} plugin(s) from ${isGlobal ? "package.json" : "plugins.json"}...`));
@@ -3256,57 +3776,168 @@ async function installPlugin(packages, options = {}) {
3256
3776
  }
3257
3777
  const { name, version } = parsePackageSpec(spec);
3258
3778
  const pkgSpec = version === "latest" ? name : `${name}@${version}`;
3779
+ let npmInstallSucceeded = false;
3780
+ let createdSymlinks = [];
3781
+ let resolvedVersion = version;
3259
3782
  try {
3260
3783
  console.log(source_default.blue(`
3261
3784
  Installing ${pkgSpec}...`));
3262
3785
  const info = await npmInfo(pkgSpec);
3263
3786
  if (!info) {
3264
3787
  console.log(source_default.red(` \u2717 Package not found: ${name}`));
3788
+ process.exitCode = 1;
3265
3789
  results.push({ name, version, success: false, error: "Package not found" });
3266
3790
  continue;
3267
3791
  }
3792
+ resolvedVersion = info.version;
3793
+ const skipDestinations = new Set;
3794
+ const preInstallPkgJson = info.omp?.install ? { name: info.name, version: info.version, omp: info.omp } : null;
3795
+ if (preInstallPkgJson) {
3796
+ const intraDupes = detectIntraPluginDuplicates(preInstallPkgJson);
3797
+ if (intraDupes.length > 0) {
3798
+ console.log(source_default.red(` \u2717 Plugin has duplicate destinations:`));
3799
+ for (const dupe of intraDupes) {
3800
+ console.log(source_default.red(` ${dupe.dest} \u2190 ${dupe.sources.join(", ")}`));
3801
+ }
3802
+ process.exitCode = 1;
3803
+ results.push({ name, version: info.version, success: false, error: "Duplicate destinations in plugin" });
3804
+ continue;
3805
+ }
3806
+ const preInstallConflicts = detectConflicts(name, preInstallPkgJson, existingPlugins);
3807
+ if (preInstallConflicts.length > 0 && !options.force) {
3808
+ if (!process.stdout.isTTY || !process.stdin.isTTY) {
3809
+ console.log(source_default.red("Conflicts detected in non-interactive mode. Use --force to override."));
3810
+ for (const conflict of preInstallConflicts) {
3811
+ console.log(source_default.yellow(` \u26A0 ${formatConflicts([conflict])[0]}`));
3812
+ }
3813
+ process.exitCode = 1;
3814
+ results.push({
3815
+ name,
3816
+ version: info.version,
3817
+ success: false,
3818
+ error: "Conflicts in non-interactive mode"
3819
+ });
3820
+ continue;
3821
+ }
3822
+ let abort = false;
3823
+ for (const conflict of preInstallConflicts) {
3824
+ const choice = await promptConflictResolution(conflict);
3825
+ if (choice === null) {
3826
+ abort = true;
3827
+ break;
3828
+ }
3829
+ const newPluginIndex = conflict.plugins.length - 1;
3830
+ if (choice !== newPluginIndex) {
3831
+ skipDestinations.add(conflict.dest);
3832
+ }
3833
+ }
3834
+ if (abort) {
3835
+ console.log(source_default.yellow(` Aborted due to conflicts (before download)`));
3836
+ process.exitCode = 1;
3837
+ results.push({ name, version: info.version, success: false, error: "Conflicts" });
3838
+ continue;
3839
+ }
3840
+ }
3841
+ }
3268
3842
  console.log(source_default.dim(` Fetching from npm...`));
3269
3843
  await npmInstall([pkgSpec], prefix, { save: options.save || isGlobal });
3844
+ npmInstallSucceeded = true;
3270
3845
  const pkgJson = await readPluginPackageJson(name, isGlobal);
3271
3846
  if (!pkgJson) {
3272
3847
  console.log(source_default.yellow(` \u26A0 Installed but no package.json found`));
3273
3848
  results.push({ name, version: info.version, success: true });
3274
3849
  continue;
3275
3850
  }
3276
- const conflicts = detectConflicts(name, pkgJson, existingPlugins);
3277
- if (conflicts.length > 0 && !options.force) {
3278
- let abort = false;
3279
- for (const conflict of conflicts) {
3280
- const choice = await promptConflictResolution(conflict);
3281
- if (choice === null) {
3282
- abort = true;
3283
- break;
3851
+ if (!preInstallPkgJson) {
3852
+ const intraDupes = detectIntraPluginDuplicates(pkgJson);
3853
+ if (intraDupes.length > 0) {
3854
+ console.log(source_default.red(` \u2717 Plugin has duplicate destinations:`));
3855
+ for (const dupe of intraDupes) {
3856
+ console.log(source_default.red(` ${dupe.dest} \u2190 ${dupe.sources.join(", ")}`));
3284
3857
  }
3285
- }
3286
- if (abort) {
3287
- console.log(source_default.yellow(` Aborted due to conflicts`));
3288
- execSync2(`npm uninstall --prefix ${prefix} ${name}`, { stdio: "pipe" });
3289
- results.push({ name, version: info.version, success: false, error: "Conflicts" });
3858
+ execFileSync2("npm", ["uninstall", "--prefix", prefix, name], { stdio: "pipe" });
3859
+ process.exitCode = 1;
3860
+ results.push({ name, version: info.version, success: false, error: "Duplicate destinations in plugin" });
3290
3861
  continue;
3291
3862
  }
3863
+ const conflicts = detectConflicts(name, pkgJson, existingPlugins);
3864
+ if (conflicts.length > 0 && !options.force) {
3865
+ if (!process.stdout.isTTY || !process.stdin.isTTY) {
3866
+ console.log(source_default.red("Conflicts detected in non-interactive mode. Use --force to override."));
3867
+ for (const conflict of conflicts) {
3868
+ console.log(source_default.yellow(` \u26A0 ${formatConflicts([conflict])[0]}`));
3869
+ }
3870
+ execFileSync2("npm", ["uninstall", "--prefix", prefix, name], { stdio: "pipe" });
3871
+ process.exitCode = 1;
3872
+ results.push({
3873
+ name,
3874
+ version: info.version,
3875
+ success: false,
3876
+ error: "Conflicts in non-interactive mode"
3877
+ });
3878
+ continue;
3879
+ }
3880
+ let abort = false;
3881
+ for (const conflict of conflicts) {
3882
+ const choice = await promptConflictResolution(conflict);
3883
+ if (choice === null) {
3884
+ abort = true;
3885
+ break;
3886
+ }
3887
+ const newPluginIndex = conflict.plugins.length - 1;
3888
+ if (choice !== newPluginIndex) {
3889
+ skipDestinations.add(conflict.dest);
3890
+ }
3891
+ }
3892
+ if (abort) {
3893
+ console.log(source_default.yellow(` Aborted due to conflicts`));
3894
+ execFileSync2("npm", ["uninstall", "--prefix", prefix, name], { stdio: "pipe" });
3895
+ process.exitCode = 1;
3896
+ results.push({ name, version: info.version, success: false, error: "Conflicts" });
3897
+ continue;
3898
+ }
3899
+ }
3292
3900
  }
3293
- const _symlinkResult = await createPluginSymlinks(name, pkgJson, isGlobal);
3294
- if (pkgJson.dependencies) {
3295
- for (const depName of Object.keys(pkgJson.dependencies)) {
3296
- const depPkgJson = await readPluginPackageJson(depName, isGlobal);
3297
- if (depPkgJson?.omp?.install) {
3298
- console.log(source_default.dim(` Processing dependency: ${depName}`));
3299
- await createPluginSymlinks(depName, depPkgJson, isGlobal);
3901
+ const symlinkResult = await createPluginSymlinks(name, pkgJson, isGlobal, true, skipDestinations);
3902
+ createdSymlinks = symlinkResult.created;
3903
+ await processOmpDependencies(pkgJson, isGlobal, new Set([name]));
3904
+ if (options.save || options.saveDev) {
3905
+ const pluginsJson = await loadPluginsJson(isGlobal);
3906
+ if (options.saveDev) {
3907
+ if (!pluginsJson.devDependencies) {
3908
+ pluginsJson.devDependencies = {};
3300
3909
  }
3910
+ pluginsJson.devDependencies[name] = info.version;
3911
+ delete pluginsJson.plugins[name];
3912
+ } else if (!isGlobal) {
3913
+ pluginsJson.plugins[name] = info.version;
3301
3914
  }
3915
+ await savePluginsJson(pluginsJson, isGlobal);
3302
3916
  }
3303
3917
  existingPlugins.set(name, pkgJson);
3918
+ await updateLockFile(name, info.version, isGlobal);
3304
3919
  console.log(source_default.green(`\u2713 Installed ${name}@${info.version}`));
3305
3920
  results.push({ name, version: info.version, success: true });
3306
3921
  } catch (err) {
3307
3922
  const errorMsg = err.message;
3308
3923
  console.log(source_default.red(` \u2717 Failed to install ${name}: ${errorMsg}`));
3309
- results.push({ name, version, success: false, error: errorMsg });
3924
+ if (createdSymlinks.length > 0) {
3925
+ console.log(source_default.dim(" Rolling back symlinks..."));
3926
+ const baseDir = isGlobal ? PI_CONFIG_DIR : PROJECT_PI_DIR;
3927
+ for (const dest of createdSymlinks) {
3928
+ try {
3929
+ await rm2(join5(baseDir, dest), { force: true, recursive: true });
3930
+ } catch {}
3931
+ }
3932
+ }
3933
+ if (npmInstallSucceeded) {
3934
+ console.log(source_default.dim(" Rolling back npm install..."));
3935
+ try {
3936
+ execFileSync2("npm", ["uninstall", "--prefix", prefix, name], { stdio: "pipe" });
3937
+ } catch {}
3938
+ }
3939
+ process.exitCode = 1;
3940
+ results.push({ name, version: resolvedVersion, success: false, error: errorMsg });
3310
3941
  }
3311
3942
  }
3312
3943
  const successful = results.filter((r) => r.success);
@@ -3317,6 +3948,7 @@ Installing ${pkgSpec}...`));
3317
3948
  }
3318
3949
  if (failed.length > 0) {
3319
3950
  console.log(source_default.red(`\u2717 Failed to install ${failed.length} plugin(s)`));
3951
+ process.exitCode = 1;
3320
3952
  }
3321
3953
  if (options.json) {
3322
3954
  console.log(JSON.stringify({ results }, null, 2));
@@ -3326,9 +3958,10 @@ async function installLocalPlugin(localPath, isGlobal, _options) {
3326
3958
  if (localPath.startsWith("~")) {
3327
3959
  localPath = join5(process.env.HOME || "", localPath.slice(1));
3328
3960
  }
3329
- localPath = resolve(localPath);
3330
- if (!existsSync6(localPath)) {
3961
+ localPath = resolve3(localPath);
3962
+ if (!existsSync8(localPath)) {
3331
3963
  console.log(source_default.red(`Error: Path does not exist: ${localPath}`));
3964
+ process.exitCode = 1;
3332
3965
  return { name: basename(localPath), version: "local", success: false, error: "Path not found" };
3333
3966
  }
3334
3967
  const _prefix = isGlobal ? PLUGINS_DIR : ".pi";
@@ -3336,12 +3969,12 @@ async function installLocalPlugin(localPath, isGlobal, _options) {
3336
3969
  try {
3337
3970
  const localPkgJsonPath = join5(localPath, "package.json");
3338
3971
  let pkgJson;
3339
- if (existsSync6(localPkgJsonPath)) {
3340
- pkgJson = JSON.parse(await readFile2(localPkgJsonPath, "utf-8"));
3972
+ if (existsSync8(localPkgJsonPath)) {
3973
+ pkgJson = JSON.parse(await readFile3(localPkgJsonPath, "utf-8"));
3341
3974
  } else {
3342
3975
  const ompJsonPath = join5(localPath, "omp.json");
3343
- if (existsSync6(ompJsonPath)) {
3344
- const ompJson = JSON.parse(await readFile2(ompJsonPath, "utf-8"));
3976
+ if (existsSync8(ompJsonPath)) {
3977
+ const ompJson = JSON.parse(await readFile3(ompJsonPath, "utf-8"));
3345
3978
  pkgJson = {
3346
3979
  name: ompJson.name || basename(localPath),
3347
3980
  version: ompJson.version || "0.0.0",
@@ -3361,49 +3994,91 @@ async function installLocalPlugin(localPath, isGlobal, _options) {
3361
3994
  }
3362
3995
  const pluginName = pkgJson.name;
3363
3996
  const pluginDir = join5(nodeModules, pluginName);
3997
+ const intraDupes = detectIntraPluginDuplicates(pkgJson);
3998
+ if (intraDupes.length > 0) {
3999
+ console.log(source_default.red(`
4000
+ Error: Plugin has duplicate destinations:`));
4001
+ for (const dupe of intraDupes) {
4002
+ console.log(source_default.red(` ${dupe.dest} \u2190 ${dupe.sources.join(", ")}`));
4003
+ }
4004
+ process.exitCode = 1;
4005
+ return {
4006
+ name: pluginName,
4007
+ version: pkgJson.version,
4008
+ success: false,
4009
+ error: "Duplicate destinations in plugin"
4010
+ };
4011
+ }
3364
4012
  console.log(source_default.blue(`
3365
4013
  Installing ${pluginName} from ${localPath}...`));
3366
4014
  await mkdir5(nodeModules, { recursive: true });
3367
- if (existsSync6(pluginDir)) {
4015
+ if (existsSync8(pluginDir)) {
3368
4016
  await rm2(pluginDir, { recursive: true, force: true });
3369
4017
  }
3370
4018
  await cp(localPath, pluginDir, { recursive: true });
3371
4019
  console.log(source_default.dim(` Copied to ${pluginDir}`));
3372
4020
  const pluginsJson = await loadPluginsJson(isGlobal);
3373
- pluginsJson.plugins[pluginName] = `file:${localPath}`;
4021
+ if (_options.saveDev) {
4022
+ if (!pluginsJson.devDependencies) {
4023
+ pluginsJson.devDependencies = {};
4024
+ }
4025
+ pluginsJson.devDependencies[pluginName] = `file:${localPath}`;
4026
+ delete pluginsJson.plugins[pluginName];
4027
+ } else {
4028
+ pluginsJson.plugins[pluginName] = `file:${localPath}`;
4029
+ }
3374
4030
  await savePluginsJson(pluginsJson, isGlobal);
3375
4031
  await createPluginSymlinks(pluginName, pkgJson, isGlobal);
4032
+ await updateLockFile(pluginName, pkgJson.version, isGlobal);
3376
4033
  console.log(source_default.green(`\u2713 Installed ${pluginName}@${pkgJson.version}`));
3377
4034
  return { name: pluginName, version: pkgJson.version, success: true };
3378
4035
  } catch (err) {
3379
4036
  const errorMsg = err.message;
3380
4037
  console.log(source_default.red(` \u2717 Failed: ${errorMsg}`));
4038
+ process.exitCode = 1;
3381
4039
  return { name: basename(localPath), version: "local", success: false, error: errorMsg };
3382
4040
  }
3383
4041
  }
3384
4042
 
3385
4043
  // src/commands/link.ts
3386
- import { existsSync as existsSync7 } from "fs";
3387
- import { mkdir as mkdir6, readFile as readFile3, rm as rm3, symlink as symlink2 } from "fs/promises";
3388
- import { basename as basename2, dirname as dirname3, join as join6, resolve as resolve2 } from "path";
4044
+ import { existsSync as existsSync9 } from "fs";
4045
+ import { mkdir as mkdir6, readFile as readFile4, rm as rm3, symlink as symlink2, writeFile as writeFile5 } from "fs/promises";
4046
+ import { basename as basename2, dirname as dirname4, join as join6, resolve as resolve4 } from "path";
4047
+ import { createInterface as createInterface2 } from "readline";
4048
+ init_paths();
4049
+ init_source();
4050
+ async function confirmCreate(path) {
4051
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
4052
+ console.log(source_default.dim(" Non-interactive mode: auto-creating package.json"));
4053
+ return true;
4054
+ }
4055
+ const rl = createInterface2({ input: process.stdin, output: process.stdout });
4056
+ return new Promise((resolve5) => {
4057
+ rl.question(source_default.yellow(` Create minimal package.json at ${path}? [Y/n] `), (answer) => {
4058
+ rl.close();
4059
+ resolve5(answer.toLowerCase() !== "n");
4060
+ });
4061
+ });
4062
+ }
3389
4063
  async function linkPlugin(localPath, options = {}) {
3390
- const isGlobal = options.global !== false;
4064
+ const isGlobal = resolveScope(options);
3391
4065
  const nodeModules = isGlobal ? NODE_MODULES_DIR : PROJECT_NODE_MODULES;
3392
4066
  if (localPath.startsWith("~")) {
3393
4067
  localPath = join6(process.env.HOME || "", localPath.slice(1));
3394
4068
  }
3395
- localPath = resolve2(localPath);
3396
- if (!existsSync7(localPath)) {
4069
+ localPath = resolve4(localPath);
4070
+ if (!existsSync9(localPath)) {
3397
4071
  console.log(source_default.red(`Error: Path does not exist: ${localPath}`));
4072
+ process.exitCode = 1;
3398
4073
  return;
3399
4074
  }
3400
4075
  let pkgJson;
3401
4076
  const localPkgJsonPath = join6(localPath, "package.json");
3402
4077
  const localOmpJsonPath = join6(localPath, "omp.json");
3403
- if (existsSync7(localPkgJsonPath)) {
3404
- pkgJson = JSON.parse(await readFile3(localPkgJsonPath, "utf-8"));
3405
- } else if (existsSync7(localOmpJsonPath)) {
3406
- const ompJson = JSON.parse(await readFile3(localOmpJsonPath, "utf-8"));
4078
+ if (existsSync9(localPkgJsonPath)) {
4079
+ pkgJson = JSON.parse(await readFile4(localPkgJsonPath, "utf-8"));
4080
+ } else if (existsSync9(localOmpJsonPath)) {
4081
+ const ompJson = JSON.parse(await readFile4(localOmpJsonPath, "utf-8"));
3407
4082
  pkgJson = {
3408
4083
  name: ompJson.name || options.name || basename2(localPath),
3409
4084
  version: ompJson.version || "0.0.0-dev",
@@ -3413,26 +4088,51 @@ async function linkPlugin(localPath, options = {}) {
3413
4088
  install: ompJson.install
3414
4089
  }
3415
4090
  };
4091
+ console.log(source_default.dim(" Converting omp.json to package.json..."));
4092
+ await writeFile5(localPkgJsonPath, JSON.stringify(pkgJson, null, 2));
3416
4093
  } else {
4094
+ console.log(source_default.yellow(" No package.json found in target directory."));
4095
+ const shouldCreate = await confirmCreate(localPkgJsonPath);
4096
+ if (!shouldCreate) {
4097
+ console.log(source_default.yellow(" Aborted: package.json required for linking"));
4098
+ process.exitCode = 1;
4099
+ return;
4100
+ }
3417
4101
  pkgJson = {
3418
4102
  name: options.name || basename2(localPath),
3419
4103
  version: "0.0.0-dev",
3420
- keywords: ["omp-plugin"]
4104
+ keywords: ["omp-plugin"],
4105
+ omp: {
4106
+ install: []
4107
+ }
3421
4108
  };
3422
- console.log(source_default.yellow(" Warning: No package.json or omp.json found"));
4109
+ console.log(source_default.dim(" Creating minimal package.json..."));
4110
+ await writeFile5(localPkgJsonPath, JSON.stringify(pkgJson, null, 2));
3423
4111
  }
3424
4112
  const pluginName = options.name || pkgJson.name;
3425
4113
  const pluginDir = join6(nodeModules, pluginName);
3426
4114
  const pluginsJson = await loadPluginsJson(isGlobal);
3427
4115
  if (pluginsJson.plugins[pluginName]) {
3428
- console.log(source_default.yellow(`Plugin "${pluginName}" is already installed.`));
3429
- console.log(source_default.dim("Use omp uninstall first, or specify a different name with -n"));
3430
- return;
4116
+ const existingSpec = pluginsJson.plugins[pluginName];
4117
+ const isLinked = existingSpec.startsWith("file:");
4118
+ if (isLinked) {
4119
+ console.log(source_default.yellow(`Plugin "${pluginName}" is already linked.`));
4120
+ console.log(source_default.dim(` Current link: ${existingSpec}`));
4121
+ console.log(source_default.dim(" Re-linking..."));
4122
+ } else if (options.force) {
4123
+ console.log(source_default.yellow(`Plugin "${pluginName}" is installed from npm. Overwriting with link...`));
4124
+ } else {
4125
+ console.log(source_default.yellow(`Plugin "${pluginName}" is already installed from npm.`));
4126
+ console.log(source_default.dim("Use omp uninstall first, or specify a different name with -n"));
4127
+ console.log(source_default.dim("Or use --force to overwrite the npm installation"));
4128
+ process.exitCode = 1;
4129
+ return;
4130
+ }
3431
4131
  }
3432
4132
  try {
3433
4133
  console.log(source_default.blue(`Linking ${localPath}...`));
3434
- await mkdir6(dirname3(pluginDir), { recursive: true });
3435
- if (existsSync7(pluginDir)) {
4134
+ await mkdir6(dirname4(pluginDir), { recursive: true });
4135
+ if (existsSync9(pluginDir)) {
3436
4136
  await rm3(pluginDir, { force: true, recursive: true });
3437
4137
  }
3438
4138
  await symlink2(localPath, pluginDir);
@@ -3447,6 +4147,7 @@ async function linkPlugin(localPath, options = {}) {
3447
4147
  console.log(source_default.dim(" Changes to the source will be reflected immediately"));
3448
4148
  } catch (err) {
3449
4149
  console.log(source_default.red(`Error linking plugin: ${err.message}`));
4150
+ process.exitCode = 1;
3450
4151
  try {
3451
4152
  await rm3(pluginDir, { force: true });
3452
4153
  } catch {}
@@ -3454,13 +4155,16 @@ async function linkPlugin(localPath, options = {}) {
3454
4155
  }
3455
4156
 
3456
4157
  // src/commands/list.ts
4158
+ init_paths();
4159
+ init_source();
3457
4160
  async function listPlugins(options = {}) {
3458
- const isGlobal = options.global !== false;
4161
+ const isGlobal = resolveScope(options);
3459
4162
  const pluginsJson = await loadPluginsJson(isGlobal);
3460
4163
  const pluginNames = Object.keys(pluginsJson.plugins);
3461
4164
  if (pluginNames.length === 0) {
3462
4165
  console.log(source_default.yellow("No plugins installed."));
3463
4166
  console.log(source_default.dim("Install one with: omp install <package>"));
4167
+ process.exitCode = 1;
3464
4168
  return;
3465
4169
  }
3466
4170
  if (options.json) {
@@ -3486,11 +4190,13 @@ async function listPlugins(options = {}) {
3486
4190
  const specifier = pluginsJson.plugins[name];
3487
4191
  const isLocal = specifier.startsWith("file:");
3488
4192
  const disabled = pluginsJson.disabled?.includes(name);
3489
- const version = pkgJson?.version ? source_default.dim(` v${pkgJson.version}`) : "";
4193
+ const isMissing = !pkgJson;
4194
+ const version = pkgJson?.version ? source_default.dim(` v${pkgJson.version}`) : source_default.dim(` (${specifier})`);
3490
4195
  const localBadge = isLocal ? source_default.cyan(" (local)") : "";
3491
4196
  const disabledBadge = disabled ? source_default.yellow(" (disabled)") : "";
3492
- const icon = disabled ? source_default.gray("\u25CB") : source_default.green("\u25C6");
3493
- console.log(`${icon} ${source_default.bold(name)}${version}${localBadge}${disabledBadge}`);
4197
+ const missingBadge = isMissing ? source_default.red(" (missing)") : "";
4198
+ const icon = disabled ? source_default.gray("\u25CB") : isMissing ? source_default.red("\u2717") : source_default.green("\u25C6");
4199
+ console.log(`${icon} ${source_default.bold(name)}${version}${localBadge}${disabledBadge}${missingBadge}`);
3494
4200
  if (pkgJson?.description) {
3495
4201
  console.log(source_default.dim(` ${pkgJson.description}`));
3496
4202
  }
@@ -3507,15 +4213,22 @@ async function listPlugins(options = {}) {
3507
4213
  }
3508
4214
 
3509
4215
  // src/commands/outdated.ts
4216
+ init_paths();
4217
+ init_source();
3510
4218
  async function showOutdated(options = {}) {
3511
- const isGlobal = options.global !== false;
4219
+ const isGlobal = resolveScope(options);
3512
4220
  const prefix = isGlobal ? PLUGINS_DIR : ".pi";
3513
4221
  console.log(source_default.blue("Checking for outdated plugins..."));
3514
4222
  try {
3515
4223
  const outdated = await npmOutdated(prefix);
3516
4224
  const pluginsJson = await loadPluginsJson(isGlobal);
3517
4225
  const managedOutdated = Object.entries(outdated).filter(([name]) => {
3518
- return pluginsJson.plugins[name] !== undefined;
4226
+ const specifier = pluginsJson.plugins[name];
4227
+ if (!specifier)
4228
+ return false;
4229
+ if (specifier.startsWith("file:"))
4230
+ return false;
4231
+ return true;
3519
4232
  });
3520
4233
  if (managedOutdated.length === 0) {
3521
4234
  console.log(source_default.green(`
@@ -3541,16 +4254,28 @@ Outdated plugins (${managedOutdated.length}):
3541
4254
  const latestColor = hasMajorUpdate ? source_default.red : wantedColor;
3542
4255
  console.log(` ${source_default.white(name.padEnd(28))}` + `${source_default.dim(current.padEnd(15))}` + `${wantedColor(wanted.padEnd(15))}` + `${latestColor(latest)}`);
3543
4256
  }
4257
+ const localPlugins = Object.entries(pluginsJson.plugins).filter(([_, spec]) => spec.startsWith("file:"));
4258
+ if (localPlugins.length > 0) {
4259
+ console.log(source_default.dim(`
4260
+ Note: ${localPlugins.length} local plugin(s) excluded from check`));
4261
+ }
3544
4262
  console.log();
3545
4263
  console.log(source_default.dim("Update with: omp update [package]"));
3546
4264
  console.log(source_default.dim(" - 'wanted' = latest within semver range"));
3547
4265
  console.log(source_default.dim(" - 'latest' = latest available version"));
3548
4266
  } catch (err) {
3549
4267
  console.log(source_default.red(`Error checking outdated: ${err.message}`));
4268
+ process.exitCode = 1;
3550
4269
  }
3551
4270
  }
3552
4271
 
3553
4272
  // src/commands/search.ts
4273
+ init_source();
4274
+ function truncate(str, maxLen) {
4275
+ if (!str || str.length <= maxLen)
4276
+ return str;
4277
+ return `${str.slice(0, maxLen - 3)}...`;
4278
+ }
3554
4279
  async function searchPlugins(query, options = {}) {
3555
4280
  console.log(source_default.blue(`Searching npm for "${query}" with omp-plugin keyword...`));
3556
4281
  try {
@@ -3560,6 +4285,7 @@ async function searchPlugins(query, options = {}) {
3560
4285
  No plugins found.`));
3561
4286
  console.log(source_default.dim("Try a different search term, or search without keyword:"));
3562
4287
  console.log(source_default.dim(" npm search omp-plugin"));
4288
+ process.exitCode = 1;
3563
4289
  return;
3564
4290
  }
3565
4291
  const limit = options.limit || 20;
@@ -3574,7 +4300,7 @@ Found ${results.length} plugin(s):
3574
4300
  for (const result of displayResults) {
3575
4301
  console.log(source_default.green("\u25C6 ") + source_default.bold(result.name) + source_default.dim(` v${result.version}`));
3576
4302
  if (result.description) {
3577
- console.log(source_default.dim(` ${result.description}`));
4303
+ console.log(source_default.dim(` ${truncate(result.description, 100)}`));
3578
4304
  }
3579
4305
  if (result.keywords?.length) {
3580
4306
  const otherKeywords = result.keywords.filter((k) => k !== "omp-plugin");
@@ -3589,34 +4315,89 @@ Found ${results.length} plugin(s):
3589
4315
  }
3590
4316
  console.log(source_default.dim("Install with: omp install <package-name>"));
3591
4317
  } catch (err) {
3592
- console.log(source_default.red(`Error searching: ${err.message}`));
4318
+ const error = err;
4319
+ if (error.message.includes("ENOTFOUND") || error.message.includes("ETIMEDOUT") || error.message.includes("EAI_AGAIN")) {
4320
+ console.log(source_default.red(`
4321
+ Network error: Unable to reach npm registry.`));
4322
+ console.log(source_default.dim(" Check your internet connection and try again."));
4323
+ } else {
4324
+ console.log(source_default.red(`
4325
+ Search failed: ${error.message}`));
4326
+ }
4327
+ process.exitCode = 1;
3593
4328
  }
3594
4329
  }
3595
4330
 
3596
4331
  // src/commands/uninstall.ts
3597
- import { existsSync as existsSync8 } from "fs";
4332
+ import { existsSync as existsSync10 } from "fs";
3598
4333
  import { rm as rm4 } from "fs/promises";
3599
4334
  import { join as join7 } from "path";
4335
+ import { createInterface as createInterface3 } from "readline";
4336
+ init_paths();
4337
+ init_source();
3600
4338
  async function uninstallPlugin(name, options = {}) {
3601
- const isGlobal = options.global !== false;
4339
+ const isGlobal = resolveScope(options);
3602
4340
  const prefix = isGlobal ? PLUGINS_DIR : ".pi";
3603
4341
  const nodeModules = isGlobal ? NODE_MODULES_DIR : PROJECT_NODE_MODULES;
3604
4342
  const pluginsJson = await loadPluginsJson(isGlobal);
3605
4343
  if (!pluginsJson.plugins[name]) {
3606
4344
  console.log(source_default.yellow(`Plugin "${name}" is not installed.`));
4345
+ process.exitCode = 1;
3607
4346
  return;
3608
4347
  }
3609
4348
  try {
3610
4349
  console.log(source_default.blue(`Uninstalling ${name}...`));
3611
4350
  const pkgJson = await readPluginPackageJson(name, isGlobal);
4351
+ if (pkgJson?.dependencies) {
4352
+ const allPlugins = await getInstalledPlugins(isGlobal);
4353
+ const sharedDeps = [];
4354
+ for (const depName of Object.keys(pkgJson.dependencies)) {
4355
+ for (const [otherName, otherPkgJson] of allPlugins) {
4356
+ if (otherName !== name && otherPkgJson.dependencies?.[depName]) {
4357
+ sharedDeps.push(`${depName} (also used by ${otherName})`);
4358
+ break;
4359
+ }
4360
+ }
4361
+ }
4362
+ if (sharedDeps.length > 0) {
4363
+ console.log(source_default.yellow(`
4364
+ \u26A0 Warning: This plugin shares dependencies with other plugins:`));
4365
+ for (const dep of sharedDeps) {
4366
+ console.log(source_default.dim(` - ${dep}`));
4367
+ }
4368
+ console.log(source_default.dim(" These dependencies will remain installed."));
4369
+ }
4370
+ }
3612
4371
  if (pkgJson) {
3613
- await removePluginSymlinks(name, pkgJson);
4372
+ const result = await removePluginSymlinks(name, pkgJson, isGlobal);
4373
+ if (result.skippedNonSymlinks.length > 0) {
4374
+ console.log(source_default.yellow(`
4375
+ The following files are not symlinks and were not removed:`));
4376
+ for (const file of result.skippedNonSymlinks) {
4377
+ console.log(source_default.dim(` - ${file}`));
4378
+ }
4379
+ if (process.stdin.isTTY && process.stdout.isTTY) {
4380
+ const rl = createInterface3({ input: process.stdin, output: process.stdout });
4381
+ const answer = await new Promise((resolve5) => {
4382
+ rl.question(source_default.yellow("Delete these files anyway? [y/N] "), (ans) => {
4383
+ rl.close();
4384
+ resolve5(ans);
4385
+ });
4386
+ });
4387
+ if (answer.toLowerCase() === "y") {
4388
+ for (const file of result.skippedNonSymlinks) {
4389
+ await rm4(file, { force: true, recursive: true });
4390
+ console.log(source_default.dim(` Deleted: ${file}`));
4391
+ }
4392
+ }
4393
+ }
4394
+ }
3614
4395
  }
3615
4396
  try {
3616
4397
  await npmUninstall([name], prefix);
3617
4398
  } catch (_err) {
3618
4399
  const pluginDir = join7(nodeModules, name);
3619
- if (existsSync8(pluginDir)) {
4400
+ if (existsSync10(pluginDir)) {
3620
4401
  await rm4(pluginDir, { recursive: true, force: true });
3621
4402
  }
3622
4403
  }
@@ -3631,6 +4412,7 @@ async function uninstallPlugin(name, options = {}) {
3631
4412
  }
3632
4413
  } catch (err) {
3633
4414
  console.log(source_default.red(`Error uninstalling plugin: ${err.message}`));
4415
+ process.exitCode = 1;
3634
4416
  if (options.json) {
3635
4417
  console.log(JSON.stringify({ name, success: false, error: err.message }, null, 2));
3636
4418
  }
@@ -3638,18 +4420,24 @@ async function uninstallPlugin(name, options = {}) {
3638
4420
  }
3639
4421
 
3640
4422
  // src/commands/update.ts
4423
+ import { rm as rm5 } from "fs/promises";
4424
+ import { join as join8 } from "path";
4425
+ init_paths();
4426
+ init_source();
3641
4427
  async function updatePlugin(name, options = {}) {
3642
- const isGlobal = options.global !== false;
4428
+ const isGlobal = resolveScope(options);
3643
4429
  const prefix = isGlobal ? PLUGINS_DIR : ".pi";
3644
4430
  const _nodeModules = isGlobal ? NODE_MODULES_DIR : PROJECT_NODE_MODULES;
3645
4431
  const pluginsJson = await loadPluginsJson(isGlobal);
3646
4432
  const pluginNames = Object.keys(pluginsJson.plugins);
3647
4433
  if (pluginNames.length === 0) {
3648
4434
  console.log(source_default.yellow("No plugins installed."));
4435
+ process.exitCode = 1;
3649
4436
  return;
3650
4437
  }
3651
4438
  if (name && !pluginsJson.plugins[name]) {
3652
4439
  console.log(source_default.yellow(`Plugin "${name}" is not installed.`));
4440
+ process.exitCode = 1;
3653
4441
  return;
3654
4442
  }
3655
4443
  const toUpdate = name ? [name] : pluginNames;
@@ -3666,25 +4454,45 @@ async function updatePlugin(name, options = {}) {
3666
4454
  }
3667
4455
  if (npmPlugins.length === 0) {
3668
4456
  console.log(source_default.yellow("No npm plugins to update."));
4457
+ process.exitCode = 1;
3669
4458
  return;
3670
4459
  }
3671
4460
  console.log(source_default.blue(`Updating ${npmPlugins.length} plugin(s)...`));
3672
4461
  const results = [];
4462
+ const oldPkgJsons = new Map;
4463
+ const beforeVersions = {};
4464
+ const oldInstallEntries = new Map;
3673
4465
  try {
3674
- const beforeVersions = {};
3675
4466
  for (const pluginName of npmPlugins) {
3676
4467
  const pkgJson = await readPluginPackageJson(pluginName, isGlobal);
3677
4468
  if (pkgJson) {
4469
+ oldPkgJsons.set(pluginName, pkgJson);
3678
4470
  beforeVersions[pluginName] = pkgJson.version;
3679
- await removePluginSymlinks(pluginName, pkgJson, false);
4471
+ if (pkgJson.omp?.install) {
4472
+ oldInstallEntries.set(pluginName, [...pkgJson.omp.install]);
4473
+ }
4474
+ await removePluginSymlinks(pluginName, pkgJson, isGlobal);
3680
4475
  }
3681
4476
  }
3682
4477
  await npmUpdate(npmPlugins, prefix);
4478
+ const baseDir = isGlobal ? PI_CONFIG_DIR : PROJECT_PI_DIR;
3683
4479
  for (const pluginName of npmPlugins) {
3684
4480
  const pkgJson = await readPluginPackageJson(pluginName, isGlobal);
3685
4481
  if (pkgJson) {
3686
4482
  const beforeVersion = beforeVersions[pluginName] || "unknown";
3687
4483
  const afterVersion = pkgJson.version;
4484
+ const oldEntries = oldInstallEntries.get(pluginName) || [];
4485
+ const newEntries = pkgJson.omp?.install || [];
4486
+ const newDests = new Set(newEntries.map((e) => e.dest));
4487
+ for (const oldEntry of oldEntries) {
4488
+ if (!newDests.has(oldEntry.dest)) {
4489
+ const dest = join8(baseDir, oldEntry.dest);
4490
+ try {
4491
+ await rm5(dest, { force: true });
4492
+ console.log(source_default.dim(` Removed orphaned: ${oldEntry.dest}`));
4493
+ } catch {}
4494
+ }
4495
+ }
3688
4496
  await createPluginSymlinks(pluginName, pkgJson, isGlobal);
3689
4497
  const changed = beforeVersion !== afterVersion;
3690
4498
  if (changed) {
@@ -3707,32 +4515,56 @@ async function updatePlugin(name, options = {}) {
3707
4515
  console.log(JSON.stringify({ results }, null, 2));
3708
4516
  }
3709
4517
  } catch (err) {
4518
+ if (oldPkgJsons.size > 0) {
4519
+ console.log(source_default.yellow(" Update failed, restoring symlinks..."));
4520
+ for (const [pluginName, pkgJson] of oldPkgJsons) {
4521
+ try {
4522
+ await createPluginSymlinks(pluginName, pkgJson, isGlobal);
4523
+ } catch (restoreErr) {
4524
+ console.log(source_default.red(` Failed to restore symlinks for ${pluginName}: ${restoreErr.message}`));
4525
+ }
4526
+ }
4527
+ }
3710
4528
  console.log(source_default.red(`Error updating plugins: ${err.message}`));
4529
+ process.exitCode = 1;
3711
4530
  }
3712
4531
  }
3713
4532
 
3714
4533
  // src/commands/why.ts
3715
- import { existsSync as existsSync9, lstatSync as lstatSync2 } from "fs";
4534
+ import { existsSync as existsSync11, lstatSync as lstatSync2 } from "fs";
3716
4535
  import { readlink as readlink2 } from "fs/promises";
3717
- import { join as join8, relative } from "path";
4536
+ import { join as join9, relative, resolve as resolve5 } from "path";
4537
+ init_paths();
4538
+ init_source();
3718
4539
  async function whyFile(filePath, options = {}) {
3719
- const isGlobal = options.global !== false;
4540
+ const isGlobal = resolveScope(options);
4541
+ const baseDir = isGlobal ? PI_CONFIG_DIR : resolve5(PROJECT_PI_DIR);
3720
4542
  let relativePath = filePath;
3721
- if (filePath.startsWith(PI_CONFIG_DIR)) {
3722
- relativePath = relative(PI_CONFIG_DIR, filePath);
3723
- } else if (filePath.startsWith("~/.pi/")) {
3724
- relativePath = filePath.slice(6);
4543
+ if (isGlobal) {
4544
+ if (filePath.startsWith(PI_CONFIG_DIR)) {
4545
+ relativePath = relative(PI_CONFIG_DIR, filePath);
4546
+ } else if (filePath.startsWith("~/.pi/")) {
4547
+ relativePath = filePath.slice(6);
4548
+ }
4549
+ } else {
4550
+ const resolvedProjectDir = resolve5(PROJECT_PI_DIR);
4551
+ if (filePath.startsWith(resolvedProjectDir)) {
4552
+ relativePath = relative(resolvedProjectDir, filePath);
4553
+ } else if (filePath.startsWith(".pi/")) {
4554
+ relativePath = filePath.slice(4);
4555
+ }
3725
4556
  }
3726
4557
  if (!relativePath.startsWith("agent/")) {
3727
4558
  const withAgent = `agent/${relativePath}`;
3728
- const fullWithAgent = join8(PI_CONFIG_DIR, withAgent);
3729
- if (existsSync9(fullWithAgent)) {
4559
+ const fullWithAgent = join9(baseDir, withAgent);
4560
+ if (existsSync11(fullWithAgent)) {
3730
4561
  relativePath = withAgent;
3731
4562
  }
3732
4563
  }
3733
- const fullPath = join8(PI_CONFIG_DIR, relativePath);
3734
- if (!existsSync9(fullPath)) {
4564
+ const fullPath = join9(baseDir, relativePath);
4565
+ if (!existsSync11(fullPath)) {
3735
4566
  console.log(source_default.yellow(`File not found: ${fullPath}`));
4567
+ process.exitCode = 1;
3736
4568
  return;
3737
4569
  }
3738
4570
  const stats = lstatSync2(fullPath);
@@ -3742,7 +4574,7 @@ async function whyFile(filePath, options = {}) {
3742
4574
  target = await readlink2(fullPath);
3743
4575
  }
3744
4576
  const installedPlugins = await getInstalledPlugins(isGlobal);
3745
- const result = await traceInstalledFile(relativePath, installedPlugins);
4577
+ const result = await traceInstalledFile(relativePath, installedPlugins, isGlobal);
3746
4578
  if (options.json) {
3747
4579
  console.log(JSON.stringify({
3748
4580
  path: relativePath,
@@ -3763,9 +4595,23 @@ async function whyFile(filePath, options = {}) {
3763
4595
  console.log();
3764
4596
  }
3765
4597
  if (result) {
3766
- console.log(source_default.green(`\u2713 Installed by: ${result.plugin}`));
3767
- console.log(source_default.dim(` Source: ${result.entry.src}`));
3768
- console.log(source_default.dim(` Dest: ${result.entry.dest}`));
4598
+ if (!isSymlink) {
4599
+ console.log(source_default.yellow("\u26A0 This file exists but is not a symlink"));
4600
+ console.log(source_default.dim(" It may have been manually created or the symlink was replaced."));
4601
+ console.log(source_default.dim(` Expected to be installed by: ${result.plugin}`));
4602
+ } else {
4603
+ const expectedSrc = join9(getPluginSourceDir(result.plugin, isGlobal), result.entry.src);
4604
+ if (target !== expectedSrc) {
4605
+ console.log(source_default.yellow("\u26A0 Symlink target does not match expected source"));
4606
+ console.log(source_default.dim(` Expected: ${expectedSrc}`));
4607
+ console.log(source_default.dim(` Actual: ${target}`));
4608
+ console.log(source_default.dim(` Expected to be installed by: ${result.plugin}`));
4609
+ } else {
4610
+ console.log(source_default.green(`\u2713 Installed by: ${result.plugin}`));
4611
+ console.log(source_default.dim(` Source: ${result.entry.src}`));
4612
+ console.log(source_default.dim(` Dest: ${result.entry.dest}`));
4613
+ }
4614
+ }
3769
4615
  const pkgJson = await readPluginPackageJson(result.plugin, isGlobal);
3770
4616
  if (pkgJson) {
3771
4617
  console.log();
@@ -3780,20 +4626,39 @@ async function whyFile(filePath, options = {}) {
3780
4626
  }
3781
4627
  }
3782
4628
 
4629
+ // src/errors.ts
4630
+ init_source();
4631
+ function withErrorHandling(fn) {
4632
+ return async (...args) => {
4633
+ try {
4634
+ await fn(...args);
4635
+ } catch (err) {
4636
+ const error = err;
4637
+ console.log(source_default.red(`Error: ${error.message}`));
4638
+ if (process.env.DEBUG) {
4639
+ console.log(source_default.dim(error.stack));
4640
+ }
4641
+ process.exitCode = 1;
4642
+ }
4643
+ };
4644
+ }
4645
+
3783
4646
  // src/migrate.ts
3784
- import { existsSync as existsSync10 } from "fs";
3785
- import { mkdir as mkdir7, readFile as readFile4, rename, rm as rm5, symlink as symlink3, writeFile as writeFile4 } from "fs/promises";
3786
- import { basename as basename3, join as join9 } from "path";
3787
- import { createInterface as createInterface2 } from "readline";
4647
+ import { existsSync as existsSync12 } from "fs";
4648
+ import { mkdir as mkdir7, readFile as readFile5, rename, rm as rm6, symlink as symlink3, writeFile as writeFile6 } from "fs/promises";
4649
+ import { basename as basename3, join as join10 } from "path";
4650
+ import { createInterface as createInterface4 } from "readline";
4651
+ init_paths();
4652
+ init_source();
3788
4653
  async function promptMigration() {
3789
- const rl = createInterface2({
4654
+ const rl = createInterface4({
3790
4655
  input: process.stdin,
3791
4656
  output: process.stdout
3792
4657
  });
3793
- return new Promise((resolve3) => {
4658
+ return new Promise((resolve6) => {
3794
4659
  rl.question(source_default.yellow("Migrate to npm-native format? [y/N] "), (answer) => {
3795
4660
  rl.close();
3796
- resolve3(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
4661
+ resolve6(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
3797
4662
  });
3798
4663
  });
3799
4664
  }
@@ -3838,7 +4703,7 @@ Migrating to npm-native format...`));
3838
4703
  } else if (info.type === "local" && info.path) {
3839
4704
  newPluginsJson.plugins[name] = `file:${info.path}`;
3840
4705
  } else if (info.type === "github" && info.repo) {
3841
- newPluginsJson.plugins[name] = `file:${join9(NODE_MODULES_DIR, name)}`;
4706
+ newPluginsJson.plugins[name] = `file:${join10(NODE_MODULES_DIR, name)}`;
3842
4707
  }
3843
4708
  migrated.push(name);
3844
4709
  } catch (err) {
@@ -3848,6 +4713,13 @@ Migrating to npm-native format...`));
3848
4713
  }
3849
4714
  await savePluginsJson(newPluginsJson, true);
3850
4715
  await archiveLegacyManifest();
4716
+ console.log(source_default.dim(" Creating symlinks..."));
4717
+ for (const [name] of Object.entries(newPluginsJson.plugins)) {
4718
+ const pkgJson = await readPluginPackageJson(name, true);
4719
+ if (pkgJson?.omp?.install?.length) {
4720
+ await createPluginSymlinks(name, pkgJson, true);
4721
+ }
4722
+ }
3851
4723
  console.log();
3852
4724
  console.log(source_default.green(`\u2713 Migrated ${migrated.length} plugin(s)`));
3853
4725
  if (failed.length > 0) {
@@ -3860,28 +4732,28 @@ Migrating to npm-native format...`));
3860
4732
  }
3861
4733
  }
3862
4734
  async function migratePlugin(name, info) {
3863
- const oldPluginDir = join9(PLUGINS_DIR, name);
3864
- const newPluginDir = join9(NODE_MODULES_DIR, name);
3865
- if (!existsSync10(oldPluginDir)) {
4735
+ const oldPluginDir = join10(PLUGINS_DIR, name);
4736
+ const newPluginDir = join10(NODE_MODULES_DIR, name);
4737
+ if (!existsSync12(oldPluginDir)) {
3866
4738
  throw new Error(`Plugin directory not found: ${oldPluginDir}`);
3867
4739
  }
3868
4740
  if (info.linked && info.path) {
3869
4741
  await mkdir7(NODE_MODULES_DIR, { recursive: true });
3870
- if (existsSync10(newPluginDir)) {
3871
- await rm5(newPluginDir, { force: true, recursive: true });
4742
+ if (existsSync12(newPluginDir)) {
4743
+ await rm6(newPluginDir, { force: true, recursive: true });
3872
4744
  }
3873
4745
  await symlink3(info.path, newPluginDir);
3874
- await rm5(oldPluginDir, { force: true });
4746
+ await rm6(oldPluginDir, { force: true });
3875
4747
  return;
3876
4748
  }
3877
- if (existsSync10(newPluginDir)) {
3878
- await rm5(newPluginDir, { force: true, recursive: true });
4749
+ if (existsSync12(newPluginDir)) {
4750
+ await rm6(newPluginDir, { force: true, recursive: true });
3879
4751
  }
3880
4752
  await rename(oldPluginDir, newPluginDir);
3881
- const pkgJsonPath = join9(newPluginDir, "package.json");
3882
- const ompJsonPath = join9(newPluginDir, "omp.json");
3883
- if (!existsSync10(pkgJsonPath) && existsSync10(ompJsonPath)) {
3884
- const ompJson = JSON.parse(await readFile4(ompJsonPath, "utf-8"));
4753
+ const pkgJsonPath = join10(newPluginDir, "package.json");
4754
+ const ompJsonPath = join10(newPluginDir, "omp.json");
4755
+ if (!existsSync12(pkgJsonPath) && existsSync12(ompJsonPath)) {
4756
+ const ompJson = JSON.parse(await readFile5(ompJsonPath, "utf-8"));
3885
4757
  const pkgJson = {
3886
4758
  name: ompJson.name || name,
3887
4759
  version: ompJson.version || info.version || "0.0.0",
@@ -3891,20 +4763,44 @@ async function migratePlugin(name, info) {
3891
4763
  install: ompJson.install
3892
4764
  }
3893
4765
  };
3894
- await writeFile4(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
4766
+ await writeFile6(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
3895
4767
  }
3896
4768
  }
3897
4769
  async function archiveLegacyManifest() {
3898
- if (!existsSync10(LEGACY_MANIFEST_PATH)) {
4770
+ if (!existsSync12(LEGACY_MANIFEST_PATH)) {
3899
4771
  return;
3900
4772
  }
3901
- const archivePath = join9(PLUGINS_DIR, `manifest.json.bak.${Date.now()}`);
4773
+ const archivePath = join10(PLUGINS_DIR, `manifest.json.bak.${Date.now()}`);
3902
4774
  await rename(LEGACY_MANIFEST_PATH, archivePath);
3903
4775
  console.log(source_default.dim(` Archived old manifest to ${basename3(archivePath)}`));
3904
4776
  }
3905
4777
 
3906
4778
  // src/cli.ts
3907
- program.name("omp").description("Oh My Pi - Plugin manager for pi configuration").version("0.1.0");
4779
+ init_source();
4780
+
4781
+ // node_modules/commander/esm.mjs
4782
+ var import__ = __toESM(require_commander(), 1);
4783
+ var {
4784
+ program,
4785
+ createCommand,
4786
+ createArgument,
4787
+ createOption,
4788
+ CommanderError,
4789
+ InvalidArgumentError,
4790
+ InvalidOptionArgumentError,
4791
+ Command,
4792
+ Argument,
4793
+ Option,
4794
+ Help
4795
+ } = import__.default;
4796
+
4797
+ // src/cli.ts
4798
+ var npmCheck = checkNpmAvailable();
4799
+ if (!npmCheck.available) {
4800
+ console.log(source_default.red(npmCheck.error));
4801
+ process.exit(1);
4802
+ }
4803
+ program.name("omp").description("Oh My Pi - Plugin manager for pi configuration").version("0.2.0");
3908
4804
  program.hook("preAction", async (thisCommand) => {
3909
4805
  const migratingCommands = ["install", "uninstall", "update", "list", "link"];
3910
4806
  if (migratingCommands.includes(thisCommand.name())) {
@@ -3918,24 +4814,24 @@ Examples:
3918
4814
  $ omp install @myorg/cool-theme # Scoped package
3919
4815
  $ omp install ./local/path # Local directory (copies)
3920
4816
  $ omp install # Install all from plugins.json
3921
- `).option("-g, --global", "Install globally to ~/.pi (default)").option("-S, --save", "Add to plugins.json").option("-D, --save-dev", "Add as dev dependency").option("--force", "Overwrite conflicts without prompting").option("--json", "Output as JSON").action(installPlugin);
3922
- program.command("uninstall <name>").alias("rm").description("Remove plugin and its symlinks").option("-g, --global", "Uninstall from ~/.pi (default)").option("--json", "Output as JSON").action(uninstallPlugin);
3923
- program.command("update [name]").alias("up").description("Update to latest within semver range").option("-g, --global", "Update global plugins (default)").option("--json", "Output as JSON").action(updatePlugin);
3924
- program.command("list").alias("ls").description("Show installed plugins").option("-g, --global", "List global plugins (default)").option("--json", "Output as JSON").action(listPlugins);
4817
+ `).option("-g, --global", "Install globally to ~/.pi").option("-l, --local", "Install to project-local .pi/").option("-S, --save", "Add to plugins.json").option("-D, --save-dev", "Add as dev dependency").option("--force", "Overwrite conflicts without prompting").option("--json", "Output as JSON").action(withErrorHandling(installPlugin));
4818
+ program.command("uninstall <name>").alias("rm").description("Remove plugin and its symlinks").option("-g, --global", "Uninstall from ~/.pi").option("-l, --local", "Uninstall from project-local .pi/").option("--json", "Output as JSON").action(withErrorHandling(uninstallPlugin));
4819
+ program.command("update [name]").alias("up").description("Update to latest within semver range").option("-g, --global", "Update global plugins").option("-l, --local", "Update project-local plugins").option("--json", "Output as JSON").action(withErrorHandling(updatePlugin));
4820
+ program.command("list").alias("ls").description("Show installed plugins").option("-g, --global", "List global plugins").option("-l, --local", "List project-local plugins").option("--json", "Output as JSON").action(withErrorHandling(listPlugins));
3925
4821
  program.command("link <path>").description("Symlink local plugin (dev mode)").addHelpText("after", `
3926
4822
  Unlike install, link creates a symlink to the original directory,
3927
4823
  so changes are reflected immediately without reinstalling.
3928
- `).option("-n, --name <name>", "Custom name for the plugin").option("-g, --global", "Link globally (default)").action(linkPlugin);
3929
- program.command("init").description("Create .pi/plugins.json in current project").option("--force", "Overwrite existing plugins.json").action(initProject);
3930
- program.command("search <query>").description("Search npm for omp-plugin keyword").option("--json", "Output as JSON").option("--limit <n>", "Maximum results to show", "20").action((query, options) => searchPlugins(query, { ...options, limit: parseInt(options.limit, 10) }));
3931
- program.command("info <package>").description("Show plugin details before install").option("--json", "Output as JSON").option("--versions", "Show available versions").action(showInfo);
3932
- program.command("outdated").description("List plugins with newer versions").option("-g, --global", "Check global plugins (default)").option("--json", "Output as JSON").action(showOutdated);
3933
- program.command("doctor").description("Check for broken symlinks, conflicts").option("-g, --global", "Check global plugins (default)").option("--fix", "Attempt to fix issues").option("--json", "Output as JSON").action(runDoctor);
3934
- program.command("create <name>").description("Scaffold new plugin from template").option("-d, --description <desc>", "Plugin description").option("-a, --author <author>", "Plugin author").action(createPlugin);
3935
- program.command("why <file>").description("Show which plugin installed a file").option("-g, --global", "Check global plugins (default)").option("--json", "Output as JSON").action(whyFile);
3936
- program.command("enable <name>").description("Enable a disabled plugin").option("-g, --global", "Target global plugins (default)").option("--json", "Output as JSON").action(enablePlugin);
3937
- program.command("disable <name>").description("Disable plugin without uninstalling").option("-g, --global", "Target global plugins (default)").option("--json", "Output as JSON").action(disablePlugin);
3938
- program.command("migrate").description("Migrate from legacy manifest.json to npm-native format").action(async () => {
4824
+ `).option("-n, --name <name>", "Custom name for the plugin").option("-g, --global", "Link globally").option("-l, --local", "Link to project-local .pi/").option("--force", "Overwrite existing npm-installed plugin").action(withErrorHandling(linkPlugin));
4825
+ program.command("init").description("Create .pi/plugins.json in current project").option("--force", "Overwrite existing plugins.json").action(withErrorHandling(initProject));
4826
+ program.command("search <query>").description("Search npm for omp-plugin keyword").option("--json", "Output as JSON").option("--limit <n>", "Maximum results to show", "20").action(withErrorHandling((query, options) => searchPlugins(query, { ...options, limit: parseInt(options.limit, 10) })));
4827
+ program.command("info <package>").description("Show plugin details before install").option("--json", "Output as JSON").option("--versions", "Show available versions").option("--all-versions", "Show all published versions").action(withErrorHandling(showInfo));
4828
+ program.command("outdated").description("List plugins with newer versions").option("-g, --global", "Check global plugins").option("-l, --local", "Check project-local plugins").option("--json", "Output as JSON").action(withErrorHandling(showOutdated));
4829
+ program.command("doctor").description("Check for broken symlinks, conflicts").option("-g, --global", "Check global plugins").option("-l, --local", "Check project-local plugins").option("--fix", "Attempt to fix issues").option("--json", "Output as JSON").action(withErrorHandling(runDoctor));
4830
+ program.command("create <name>").description("Scaffold new plugin from template").option("-d, --description <desc>", "Plugin description").option("-a, --author <author>", "Plugin author").action(withErrorHandling(createPlugin));
4831
+ program.command("why <file>").description("Show which plugin installed a file").option("-g, --global", "Check global plugins").option("-l, --local", "Check project-local plugins").option("--json", "Output as JSON").action(withErrorHandling(whyFile));
4832
+ program.command("enable <name>").description("Enable a disabled plugin").option("-g, --global", "Target global plugins").option("-l, --local", "Target project-local plugins").option("--json", "Output as JSON").action(withErrorHandling(enablePlugin));
4833
+ program.command("disable <name>").description("Disable plugin without uninstalling").option("-g, --global", "Target global plugins").option("-l, --local", "Target project-local plugins").option("--json", "Output as JSON").action(withErrorHandling(disablePlugin));
4834
+ program.command("migrate").description("Migrate from legacy manifest.json to npm-native format").action(withErrorHandling(async () => {
3939
4835
  await migrateToNpm();
3940
- });
4836
+ }));
3941
4837
  program.parse();