@oh-my-pi/cli 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +79 -84
  2. package/dist/cli.js +5025 -1016
  3. package/dist/commands/config.d.ts +27 -0
  4. package/dist/commands/config.d.ts.map +1 -1
  5. package/dist/commands/create.d.ts.map +1 -1
  6. package/dist/commands/doctor.d.ts +2 -0
  7. package/dist/commands/doctor.d.ts.map +1 -1
  8. package/dist/commands/env.d.ts.map +1 -1
  9. package/dist/commands/features.d.ts.map +1 -1
  10. package/dist/commands/info.d.ts.map +1 -1
  11. package/dist/commands/init.d.ts.map +1 -1
  12. package/dist/commands/install.d.ts +6 -0
  13. package/dist/commands/install.d.ts.map +1 -1
  14. package/dist/commands/link.d.ts +1 -0
  15. package/dist/commands/link.d.ts.map +1 -1
  16. package/dist/commands/list.d.ts.map +1 -1
  17. package/dist/commands/outdated.d.ts.map +1 -1
  18. package/dist/commands/search.d.ts.map +1 -1
  19. package/dist/commands/uninstall.d.ts +3 -0
  20. package/dist/commands/uninstall.d.ts.map +1 -1
  21. package/dist/commands/update.d.ts +1 -0
  22. package/dist/commands/update.d.ts.map +1 -1
  23. package/dist/commands/why.d.ts.map +1 -1
  24. package/dist/conflicts.d.ts +7 -2
  25. package/dist/conflicts.d.ts.map +1 -1
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/lock.d.ts.map +1 -1
  29. package/dist/lockfile.d.ts +24 -3
  30. package/dist/lockfile.d.ts.map +1 -1
  31. package/dist/manifest.d.ts +12 -1
  32. package/dist/manifest.d.ts.map +1 -1
  33. package/dist/npm.d.ts +11 -0
  34. package/dist/npm.d.ts.map +1 -1
  35. package/dist/output.d.ts +51 -0
  36. package/dist/output.d.ts.map +1 -0
  37. package/dist/paths.d.ts +5 -0
  38. package/dist/paths.d.ts.map +1 -1
  39. package/dist/progress.d.ts +78 -0
  40. package/dist/progress.d.ts.map +1 -0
  41. package/dist/runtime.d.ts.map +1 -1
  42. package/dist/symlinks.d.ts +1 -0
  43. package/dist/symlinks.d.ts.map +1 -1
  44. package/package.json +24 -10
  45. package/.github/icon.png +0 -0
  46. package/.github/logo.png +0 -0
  47. package/.github/workflows/ci.yml +0 -32
  48. package/.github/workflows/publish.yml +0 -42
  49. package/biome.json +0 -29
  50. package/bun.lock +0 -109
  51. package/plugins/exa/README.md +0 -153
  52. package/plugins/exa/package.json +0 -56
  53. package/plugins/exa/tools/exa/company.ts +0 -35
  54. package/plugins/exa/tools/exa/index.ts +0 -66
  55. package/plugins/exa/tools/exa/linkedin.ts +0 -35
  56. package/plugins/exa/tools/exa/researcher.ts +0 -40
  57. package/plugins/exa/tools/exa/runtime.json +0 -4
  58. package/plugins/exa/tools/exa/search.ts +0 -46
  59. package/plugins/exa/tools/exa/shared.ts +0 -230
  60. package/plugins/exa/tools/exa/websets.ts +0 -62
  61. package/plugins/metal-theme/README.md +0 -13
  62. package/plugins/metal-theme/omp.json +0 -8
  63. package/plugins/metal-theme/package.json +0 -19
  64. package/plugins/metal-theme/themes/metal.json +0 -79
  65. package/plugins/subagents/README.md +0 -25
  66. package/plugins/subagents/agents/explore.md +0 -71
  67. package/plugins/subagents/agents/planner.md +0 -51
  68. package/plugins/subagents/agents/reviewer.md +0 -53
  69. package/plugins/subagents/agents/task.md +0 -46
  70. package/plugins/subagents/commands/architect-plan.md +0 -9
  71. package/plugins/subagents/commands/implement-with-critic.md +0 -10
  72. package/plugins/subagents/commands/implement.md +0 -10
  73. package/plugins/subagents/omp.json +0 -15
  74. package/plugins/subagents/package.json +0 -26
  75. package/plugins/subagents/tools/task/index.ts +0 -1019
  76. package/plugins/user-prompt/README.md +0 -130
  77. package/plugins/user-prompt/package.json +0 -19
  78. package/plugins/user-prompt/tools/user-prompt/index.ts +0 -235
  79. package/scripts/bump-version.sh +0 -52
  80. package/scripts/publish.sh +0 -35
  81. package/src/cli.ts +0 -242
  82. package/src/commands/config.ts +0 -384
  83. package/src/commands/create.ts +0 -203
  84. package/src/commands/doctor.ts +0 -305
  85. package/src/commands/enable.ts +0 -122
  86. package/src/commands/env.ts +0 -38
  87. package/src/commands/features.ts +0 -295
  88. package/src/commands/info.ts +0 -120
  89. package/src/commands/init.ts +0 -60
  90. package/src/commands/install.ts +0 -700
  91. package/src/commands/link.ts +0 -159
  92. package/src/commands/list.ts +0 -186
  93. package/src/commands/outdated.ts +0 -87
  94. package/src/commands/search.ts +0 -77
  95. package/src/commands/uninstall.ts +0 -124
  96. package/src/commands/update.ts +0 -170
  97. package/src/commands/why.ts +0 -136
  98. package/src/conflicts.ts +0 -116
  99. package/src/errors.ts +0 -22
  100. package/src/index.ts +0 -46
  101. package/src/lock.ts +0 -46
  102. package/src/lockfile.ts +0 -132
  103. package/src/manifest.ts +0 -360
  104. package/src/npm.ts +0 -206
  105. package/src/paths.ts +0 -137
  106. package/src/runtime.ts +0 -116
  107. package/src/symlinks.ts +0 -455
  108. package/tsconfig.json +0 -28
package/src/cli.ts DELETED
@@ -1,242 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- import { configCommand } from "@omp/commands/config";
4
- import { createPlugin } from "@omp/commands/create";
5
- import { runDoctor } from "@omp/commands/doctor";
6
- import { disablePlugin, enablePlugin } from "@omp/commands/enable";
7
- import { envCommand } from "@omp/commands/env";
8
- import { featuresCommand } from "@omp/commands/features";
9
- import { showInfo } from "@omp/commands/info";
10
- import { initProject } from "@omp/commands/init";
11
- import { installPlugin } from "@omp/commands/install";
12
- import { linkPlugin } from "@omp/commands/link";
13
- import { listPlugins } from "@omp/commands/list";
14
- import { showOutdated } from "@omp/commands/outdated";
15
- import { searchPlugins } from "@omp/commands/search";
16
- import { uninstallPlugin } from "@omp/commands/uninstall";
17
- import { updatePlugin } from "@omp/commands/update";
18
- import { whyFile } from "@omp/commands/why";
19
- import { withErrorHandling } from "@omp/errors";
20
- import { checkNpmAvailable } from "@omp/npm";
21
- import chalk from "chalk";
22
- import { program } from "commander";
23
-
24
- // Check npm availability at startup
25
- const npmCheck = checkNpmAvailable();
26
- if (!npmCheck.available) {
27
- console.log(chalk.red(npmCheck.error));
28
- process.exit(1);
29
- }
30
-
31
- program.name("omp").description("Oh My Pi - Plugin manager for pi configuration").version("0.2.0");
32
-
33
- // ============================================================================
34
- // Core Commands
35
- // ============================================================================
36
-
37
- program
38
- .command("install [packages...]")
39
- .alias("i")
40
- .description("Install plugin(s). No args = install from plugins.json")
41
- .addHelpText(
42
- "after",
43
- `
44
- Examples:
45
- $ omp install @oh-my-pi/subagents # Install from npm (all features)
46
- $ omp install @oh-my-pi/exa[search] # Install with specific features
47
- $ omp install @oh-my-pi/exa[search,websets] # Multiple features
48
- $ omp install @oh-my-pi/exa[*] # Explicitly all features
49
- $ omp install @oh-my-pi/exa[] # No optional features (core only)
50
- $ omp install @oh-my-pi/subagents@^2.0.0 # Specific version range
51
- $ omp install ./local/path # Local directory (copies)
52
- $ omp install # Install all from plugins.json
53
- `,
54
- )
55
- .option("-g, --global", "Install globally to ~/.pi")
56
- .option("-l, --local", "Install to project-local .pi/")
57
- .option("-S, --save", "Add to plugins.json")
58
- .option("-D, --save-dev", "Add as dev dependency")
59
- .option("--force", "Overwrite conflicts without prompting")
60
- .option("--json", "Output as JSON")
61
- .action(withErrorHandling(installPlugin));
62
-
63
- program
64
- .command("uninstall <name>")
65
- .alias("rm")
66
- .description("Remove plugin and its symlinks")
67
- .option("-g, --global", "Uninstall from ~/.pi")
68
- .option("-l, --local", "Uninstall from project-local .pi/")
69
- .option("--json", "Output as JSON")
70
- .action(withErrorHandling(uninstallPlugin));
71
-
72
- program
73
- .command("update [name]")
74
- .alias("up")
75
- .description("Update to latest within semver range")
76
- .option("-g, --global", "Update global plugins")
77
- .option("-l, --local", "Update project-local plugins")
78
- .option("--json", "Output as JSON")
79
- .action(withErrorHandling(updatePlugin));
80
-
81
- program
82
- .command("list")
83
- .alias("ls")
84
- .description("Show installed plugins")
85
- .option("-g, --global", "List global plugins")
86
- .option("-l, --local", "List project-local plugins")
87
- .option("--json", "Output as JSON")
88
- .action(withErrorHandling(listPlugins));
89
-
90
- program
91
- .command("link <path>")
92
- .description("Symlink local plugin (dev mode)")
93
- .addHelpText(
94
- "after",
95
- `
96
- Unlike install, link creates a symlink to the original directory,
97
- so changes are reflected immediately without reinstalling.
98
- `,
99
- )
100
- .option("-n, --name <name>", "Custom name for the plugin")
101
- .option("-g, --global", "Link globally")
102
- .option("-l, --local", "Link to project-local .pi/")
103
- .option("--force", "Overwrite existing npm-installed plugin")
104
- .action(withErrorHandling(linkPlugin));
105
-
106
- // ============================================================================
107
- // New Commands
108
- // ============================================================================
109
-
110
- program
111
- .command("init")
112
- .description("Create .pi/plugins.json in current project")
113
- .option("--force", "Overwrite existing plugins.json")
114
- .action(withErrorHandling(initProject));
115
-
116
- program
117
- .command("search <query>")
118
- .description("Search npm for omp-plugin keyword")
119
- .option("--json", "Output as JSON")
120
- .option("--limit <n>", "Maximum results to show", "20")
121
- .action(
122
- withErrorHandling((query, options) => searchPlugins(query, { ...options, limit: parseInt(options.limit, 10) })),
123
- );
124
-
125
- program
126
- .command("info <package>")
127
- .description("Show plugin details before install")
128
- .option("--json", "Output as JSON")
129
- .option("--versions", "Show available versions")
130
- .option("--all-versions", "Show all published versions")
131
- .action(withErrorHandling(showInfo));
132
-
133
- program
134
- .command("outdated")
135
- .description("List plugins with newer versions")
136
- .option("-g, --global", "Check global plugins")
137
- .option("-l, --local", "Check project-local plugins")
138
- .option("--json", "Output as JSON")
139
- .action(withErrorHandling(showOutdated));
140
-
141
- program
142
- .command("doctor")
143
- .description("Check for broken symlinks, conflicts")
144
- .option("-g, --global", "Check global plugins")
145
- .option("-l, --local", "Check project-local plugins")
146
- .option("--fix", "Attempt to fix issues")
147
- .option("--json", "Output as JSON")
148
- .action(withErrorHandling(runDoctor));
149
-
150
- program
151
- .command("create <name>")
152
- .description("Scaffold new plugin from template")
153
- .option("-d, --description <desc>", "Plugin description")
154
- .option("-a, --author <author>", "Plugin author")
155
- .action(withErrorHandling(createPlugin));
156
-
157
- program
158
- .command("why <file>")
159
- .description("Show which plugin installed a file")
160
- .option("-g, --global", "Check global plugins")
161
- .option("-l, --local", "Check project-local plugins")
162
- .option("--json", "Output as JSON")
163
- .action(withErrorHandling(whyFile));
164
-
165
- program
166
- .command("enable <name>")
167
- .description("Enable a disabled plugin")
168
- .option("-g, --global", "Target global plugins")
169
- .option("-l, --local", "Target project-local plugins")
170
- .option("--json", "Output as JSON")
171
- .action(withErrorHandling(enablePlugin));
172
-
173
- program
174
- .command("disable <name>")
175
- .description("Disable plugin without uninstalling")
176
- .option("-g, --global", "Target global plugins")
177
- .option("-l, --local", "Target project-local plugins")
178
- .option("--json", "Output as JSON")
179
- .action(withErrorHandling(disablePlugin));
180
-
181
- program
182
- .command("features <name>")
183
- .description("List or configure plugin features")
184
- .addHelpText(
185
- "after",
186
- `
187
- Examples:
188
- $ omp features @oh-my-pi/exa # List available features
189
- $ omp features @oh-my-pi/exa --enable websets # Enable a feature
190
- $ omp features @oh-my-pi/exa --disable search # Disable a feature
191
- $ omp features @oh-my-pi/exa --set search,websets # Set exact features
192
- $ omp features @oh-my-pi/exa --set '*' # Enable all features
193
- $ omp features @oh-my-pi/exa --set '' # Disable all optional features
194
- `,
195
- )
196
- .option("-g, --global", "Target global plugins")
197
- .option("-l, --local", "Target project-local plugins")
198
- .option("--enable <features...>", "Enable specific features")
199
- .option("--disable <features...>", "Disable specific features")
200
- .option("--set <features>", "Set exact feature list (comma-separated, '*' for all, '' for none)")
201
- .option("--json", "Output as JSON")
202
- .action(withErrorHandling(featuresCommand));
203
-
204
- program
205
- .command("config <name> [key] [value]")
206
- .description("Get or set plugin configuration variables")
207
- .addHelpText(
208
- "after",
209
- `
210
- Examples:
211
- $ omp config @oh-my-pi/exa # List all variables
212
- $ omp config @oh-my-pi/exa apiKey # Get value of apiKey
213
- $ omp config @oh-my-pi/exa apiKey sk-xxx # Set apiKey to sk-xxx
214
- $ omp config @oh-my-pi/exa apiKey --delete # Reset apiKey to default
215
- `,
216
- )
217
- .option("-g, --global", "Target global plugins")
218
- .option("-l, --local", "Target project-local plugins")
219
- .option("--delete", "Delete/reset the variable to its default")
220
- .option("--json", "Output as JSON")
221
- .action(withErrorHandling(configCommand));
222
-
223
- program
224
- .command("env")
225
- .description("Print plugin environment variables for shell eval")
226
- .addHelpText(
227
- "after",
228
- `
229
- Examples:
230
- $ eval "$(omp env)" # Load env vars in current shell
231
- $ omp env >> ~/.bashrc # Persist to shell config
232
- $ omp env --fish | source # Fish shell syntax
233
- $ omp env --json # JSON format for scripts
234
- `,
235
- )
236
- .option("-g, --global", "Target global plugins")
237
- .option("-l, --local", "Target project-local plugins")
238
- .option("--fish", "Output fish shell syntax instead of POSIX")
239
- .option("--json", "Output as JSON")
240
- .action(withErrorHandling(envCommand));
241
-
242
- program.parse();
@@ -1,384 +0,0 @@
1
- import type { OmpVariable } from "@omp/manifest";
2
- import { loadPluginsJson, readPluginPackageJson, savePluginsJson } from "@omp/manifest";
3
- import { resolveScope } from "@omp/paths";
4
- import chalk from "chalk";
5
-
6
- export interface ConfigOptions {
7
- global?: boolean;
8
- local?: boolean;
9
- json?: boolean;
10
- delete?: boolean;
11
- }
12
-
13
- /**
14
- * Collect all variables from a plugin (top-level + enabled features)
15
- */
16
- function collectVariables(
17
- pkgJson: { omp?: { variables?: Record<string, OmpVariable>; features?: Record<string, { variables?: Record<string, OmpVariable> }> } },
18
- enabledFeatures: string[],
19
- ): Record<string, OmpVariable> {
20
- const vars: Record<string, OmpVariable> = {};
21
-
22
- // Top-level variables
23
- if (pkgJson.omp?.variables) {
24
- Object.assign(vars, pkgJson.omp.variables);
25
- }
26
-
27
- // Variables from enabled features
28
- if (pkgJson.omp?.features) {
29
- for (const fname of enabledFeatures) {
30
- const feature = pkgJson.omp.features[fname];
31
- if (feature?.variables) {
32
- Object.assign(vars, feature.variables);
33
- }
34
- }
35
- }
36
-
37
- return vars;
38
- }
39
-
40
- /**
41
- * Parse a string value to the appropriate type based on variable definition
42
- */
43
- function parseValue(value: string, varDef: OmpVariable): string | number | boolean | string[] {
44
- switch (varDef.type) {
45
- case "number":
46
- const num = Number(value);
47
- if (isNaN(num)) {
48
- throw new Error(`Invalid number: ${value}`);
49
- }
50
- return num;
51
- case "boolean":
52
- if (value === "true" || value === "1" || value === "yes") return true;
53
- if (value === "false" || value === "0" || value === "no") return false;
54
- throw new Error(`Invalid boolean: ${value}. Use true/false, 1/0, or yes/no`);
55
- case "string[]":
56
- return value.split(",").map((s) => s.trim()).filter(Boolean);
57
- default:
58
- return value;
59
- }
60
- }
61
-
62
- /**
63
- * Format a value for display
64
- */
65
- function formatValue(value: unknown, varDef: OmpVariable): string {
66
- if (value === undefined) {
67
- return chalk.dim("(not set)");
68
- }
69
- if (varDef.type === "string[]" && Array.isArray(value)) {
70
- return value.join(", ");
71
- }
72
- if (typeof value === "string" && varDef.env) {
73
- // Mask sensitive values (likely API keys)
74
- if (value.length > 8) {
75
- return `${value.slice(0, 4)}...${value.slice(-4)}`;
76
- }
77
- }
78
- return String(value);
79
- }
80
-
81
- /**
82
- * Resolve which features are currently enabled
83
- *
84
- * - null/undefined: use plugin defaults (features with default !== false)
85
- * - ["*"]: explicitly all features
86
- * - []: no optional features
87
- * - ["f1", "f2"]: specific features
88
- */
89
- function resolveEnabledFeatures(
90
- allFeatureNames: string[],
91
- storedFeatures: string[] | null | undefined,
92
- pluginFeatures: Record<string, { default?: boolean }>,
93
- ): string[] {
94
- // Explicit "all features" request
95
- if (Array.isArray(storedFeatures) && storedFeatures.includes("*")) return allFeatureNames;
96
- // Explicit feature list (including empty array = no features)
97
- if (Array.isArray(storedFeatures)) return storedFeatures;
98
- // null/undefined = use defaults
99
- return Object.entries(pluginFeatures)
100
- .filter(([_, f]) => f.default !== false)
101
- .map(([name]) => name);
102
- }
103
-
104
- /**
105
- * List all configurable variables for a plugin
106
- * omp config @oh-my-pi/exa
107
- */
108
- export async function listConfig(name: string, options: ConfigOptions = {}): Promise<void> {
109
- const isGlobal = resolveScope(options);
110
- const pluginsJson = await loadPluginsJson(isGlobal);
111
-
112
- if (!pluginsJson.plugins[name]) {
113
- console.log(chalk.yellow(`Plugin "${name}" is not installed.`));
114
- process.exitCode = 1;
115
- return;
116
- }
117
-
118
- const pkgJson = await readPluginPackageJson(name, isGlobal);
119
- if (!pkgJson) {
120
- console.log(chalk.red(`Could not read package.json for ${name}`));
121
- process.exitCode = 1;
122
- return;
123
- }
124
-
125
- const allFeatureNames = Object.keys(pkgJson.omp?.features || {});
126
- const config = pluginsJson.config?.[name];
127
- const enabledFeatures = resolveEnabledFeatures(allFeatureNames, config?.features, pkgJson.omp?.features || {});
128
- const variables = collectVariables(pkgJson, enabledFeatures);
129
-
130
- if (Object.keys(variables).length === 0) {
131
- console.log(chalk.yellow(`Plugin "${name}" has no configurable variables.`));
132
- return;
133
- }
134
-
135
- const userVars = config?.variables || {};
136
-
137
- if (options.json) {
138
- console.log(
139
- JSON.stringify(
140
- {
141
- plugin: name,
142
- variables: Object.entries(variables).map(([vname, vdef]) => ({
143
- name: vname,
144
- type: vdef.type,
145
- value: userVars[vname],
146
- default: vdef.default,
147
- required: vdef.required,
148
- env: vdef.env,
149
- description: vdef.description,
150
- })),
151
- },
152
- null,
153
- 2,
154
- ),
155
- );
156
- return;
157
- }
158
-
159
- console.log(chalk.bold(`\nVariables for ${name}:\n`));
160
-
161
- for (const [vname, vdef] of Object.entries(variables)) {
162
- const currentValue = userVars[vname];
163
- const hasValue = currentValue !== undefined;
164
- const hasDefault = vdef.default !== undefined;
165
-
166
- const icon = hasValue ? chalk.green("✓") : hasDefault ? chalk.blue("○") : vdef.required ? chalk.red("!") : chalk.gray("○");
167
- const requiredStr = vdef.required && !hasValue ? chalk.red(" (required)") : "";
168
- const envStr = vdef.env ? chalk.dim(` [${vdef.env}]`) : "";
169
-
170
- console.log(`${icon} ${chalk.bold(vname)}${requiredStr}${envStr}`);
171
-
172
- if (vdef.description) {
173
- console.log(chalk.dim(` ${vdef.description}`));
174
- }
175
-
176
- console.log(chalk.dim(` Type: ${vdef.type}`));
177
-
178
- if (hasValue) {
179
- console.log(` Value: ${formatValue(currentValue, vdef)}`);
180
- } else if (hasDefault) {
181
- console.log(` Default: ${formatValue(vdef.default, vdef)}`);
182
- }
183
- }
184
-
185
- console.log();
186
- console.log(chalk.dim(`Set a value: omp config ${name} <variable> <value>`));
187
- console.log(chalk.dim(`Delete a value: omp config ${name} <variable> --delete`));
188
- }
189
-
190
- /**
191
- * Get a specific variable value
192
- * omp config @oh-my-pi/exa apiKey
193
- */
194
- export async function getConfig(name: string, key: string, options: ConfigOptions = {}): Promise<void> {
195
- const isGlobal = resolveScope(options);
196
- const pluginsJson = await loadPluginsJson(isGlobal);
197
-
198
- if (!pluginsJson.plugins[name]) {
199
- console.log(chalk.yellow(`Plugin "${name}" is not installed.`));
200
- process.exitCode = 1;
201
- return;
202
- }
203
-
204
- const pkgJson = await readPluginPackageJson(name, isGlobal);
205
- if (!pkgJson) {
206
- console.log(chalk.red(`Could not read package.json for ${name}`));
207
- process.exitCode = 1;
208
- return;
209
- }
210
-
211
- const allFeatureNames = Object.keys(pkgJson.omp?.features || {});
212
- const config = pluginsJson.config?.[name];
213
- const enabledFeatures = resolveEnabledFeatures(allFeatureNames, config?.features, pkgJson.omp?.features || {});
214
- const variables = collectVariables(pkgJson, enabledFeatures);
215
-
216
- const varDef = variables[key];
217
- if (!varDef) {
218
- console.log(chalk.red(`Unknown variable "${key}".`));
219
- console.log(chalk.dim(`Available: ${Object.keys(variables).join(", ") || "(none)"}`));
220
- process.exitCode = 1;
221
- return;
222
- }
223
-
224
- const userValue = config?.variables?.[key];
225
- const value = userValue ?? varDef.default;
226
-
227
- if (options.json) {
228
- console.log(JSON.stringify({ plugin: name, variable: key, value, default: varDef.default }, null, 2));
229
- return;
230
- }
231
-
232
- if (value !== undefined) {
233
- console.log(formatValue(value, varDef));
234
- } else {
235
- console.log(chalk.dim("(not set)"));
236
- }
237
- }
238
-
239
- /**
240
- * Set a variable value
241
- * omp config @oh-my-pi/exa apiKey sk-xxx
242
- */
243
- export async function setConfig(name: string, key: string, value: string, options: ConfigOptions = {}): Promise<void> {
244
- const isGlobal = resolveScope(options);
245
- const pluginsJson = await loadPluginsJson(isGlobal);
246
-
247
- if (!pluginsJson.plugins[name]) {
248
- console.log(chalk.yellow(`Plugin "${name}" is not installed.`));
249
- process.exitCode = 1;
250
- return;
251
- }
252
-
253
- const pkgJson = await readPluginPackageJson(name, isGlobal);
254
- if (!pkgJson) {
255
- console.log(chalk.red(`Could not read package.json for ${name}`));
256
- process.exitCode = 1;
257
- return;
258
- }
259
-
260
- const allFeatureNames = Object.keys(pkgJson.omp?.features || {});
261
- const config = pluginsJson.config?.[name];
262
- const enabledFeatures = resolveEnabledFeatures(allFeatureNames, config?.features, pkgJson.omp?.features || {});
263
- const variables = collectVariables(pkgJson, enabledFeatures);
264
-
265
- const varDef = variables[key];
266
- if (!varDef) {
267
- console.log(chalk.red(`Unknown variable "${key}".`));
268
- console.log(chalk.dim(`Available: ${Object.keys(variables).join(", ") || "(none)"}`));
269
- process.exitCode = 1;
270
- return;
271
- }
272
-
273
- // Parse and validate value
274
- let parsed: string | number | boolean | string[];
275
- try {
276
- parsed = parseValue(value, varDef);
277
- } catch (err) {
278
- console.log(chalk.red((err as Error).message));
279
- process.exitCode = 1;
280
- return;
281
- }
282
-
283
- // Update config
284
- if (!pluginsJson.config) pluginsJson.config = {};
285
- if (!pluginsJson.config[name]) pluginsJson.config[name] = {};
286
- if (!pluginsJson.config[name].variables) pluginsJson.config[name].variables = {};
287
-
288
- pluginsJson.config[name].variables[key] = parsed;
289
- await savePluginsJson(pluginsJson, isGlobal);
290
-
291
- console.log(chalk.green(`✓ Set ${name}.${key} = ${JSON.stringify(parsed)}`));
292
-
293
- if (varDef.env) {
294
- console.log(chalk.dim(` Environment variable: ${varDef.env}`));
295
- console.log(chalk.dim(` Export with: omp env`));
296
- }
297
-
298
- if (options.json) {
299
- console.log(JSON.stringify({ plugin: name, variable: key, value: parsed }, null, 2));
300
- }
301
- }
302
-
303
- /**
304
- * Delete a variable override (revert to default)
305
- * omp config @oh-my-pi/exa apiKey --delete
306
- */
307
- export async function deleteConfig(name: string, key: string, options: ConfigOptions = {}): Promise<void> {
308
- const isGlobal = resolveScope(options);
309
- const pluginsJson = await loadPluginsJson(isGlobal);
310
-
311
- if (!pluginsJson.plugins[name]) {
312
- console.log(chalk.yellow(`Plugin "${name}" is not installed.`));
313
- process.exitCode = 1;
314
- return;
315
- }
316
-
317
- const config = pluginsJson.config?.[name];
318
- // Check key presence with hasOwnProperty, not truthiness (allows deleting falsy values like false, 0, "", [])
319
- if (!config?.variables || !Object.prototype.hasOwnProperty.call(config.variables, key)) {
320
- console.log(chalk.yellow(`Variable "${key}" is not set for ${name}.`));
321
- return;
322
- }
323
-
324
- delete pluginsJson.config![name].variables![key];
325
-
326
- // Clean up empty objects
327
- if (Object.keys(pluginsJson.config![name].variables!).length === 0) {
328
- delete pluginsJson.config![name].variables;
329
- }
330
- if (Object.keys(pluginsJson.config![name]).length === 0) {
331
- delete pluginsJson.config![name];
332
- }
333
- if (Object.keys(pluginsJson.config!).length === 0) {
334
- delete pluginsJson.config;
335
- }
336
-
337
- await savePluginsJson(pluginsJson, isGlobal);
338
-
339
- console.log(chalk.green(`✓ Deleted ${name}.${key} (reverted to default)`));
340
-
341
- if (options.json) {
342
- console.log(JSON.stringify({ plugin: name, variable: key, deleted: true }, null, 2));
343
- }
344
- }
345
-
346
- /**
347
- * Main config command handler
348
- * Routes to list, get, set, or delete based on arguments
349
- */
350
- export async function configCommand(
351
- name: string,
352
- keyOrOptions?: string | ConfigOptions,
353
- valueOrOptions?: string | ConfigOptions,
354
- options: ConfigOptions = {},
355
- ): Promise<void> {
356
- // Handle different argument patterns
357
- let key: string | undefined;
358
- let value: string | undefined;
359
- let opts: ConfigOptions;
360
-
361
- if (typeof keyOrOptions === "object") {
362
- // omp config <name> [options]
363
- opts = keyOrOptions;
364
- } else if (typeof valueOrOptions === "object") {
365
- // omp config <name> <key> [options]
366
- key = keyOrOptions;
367
- opts = valueOrOptions;
368
- } else {
369
- // omp config <name> <key> <value> [options]
370
- key = keyOrOptions;
371
- value = valueOrOptions;
372
- opts = options;
373
- }
374
-
375
- if (!key) {
376
- await listConfig(name, opts);
377
- } else if (opts.delete) {
378
- await deleteConfig(name, key, opts);
379
- } else if (value !== undefined) {
380
- await setConfig(name, key, value, opts);
381
- } else {
382
- await getConfig(name, key, opts);
383
- }
384
- }