@oh-my-pi/cli 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +14 -13
  2. package/dist/cli.js +4787 -858
  3. package/dist/commands/config.d.ts +27 -0
  4. package/dist/commands/config.d.ts.map +1 -1
  5. package/dist/commands/create.d.ts.map +1 -1
  6. package/dist/commands/doctor.d.ts +2 -0
  7. package/dist/commands/doctor.d.ts.map +1 -1
  8. package/dist/commands/env.d.ts.map +1 -1
  9. package/dist/commands/features.d.ts.map +1 -1
  10. package/dist/commands/info.d.ts.map +1 -1
  11. package/dist/commands/init.d.ts.map +1 -1
  12. package/dist/commands/install.d.ts +6 -0
  13. package/dist/commands/install.d.ts.map +1 -1
  14. package/dist/commands/link.d.ts +1 -0
  15. package/dist/commands/link.d.ts.map +1 -1
  16. package/dist/commands/list.d.ts.map +1 -1
  17. package/dist/commands/outdated.d.ts.map +1 -1
  18. package/dist/commands/search.d.ts.map +1 -1
  19. package/dist/commands/uninstall.d.ts +3 -0
  20. package/dist/commands/uninstall.d.ts.map +1 -1
  21. package/dist/commands/update.d.ts +1 -0
  22. package/dist/commands/update.d.ts.map +1 -1
  23. package/dist/commands/why.d.ts.map +1 -1
  24. package/dist/conflicts.d.ts +7 -2
  25. package/dist/conflicts.d.ts.map +1 -1
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/lock.d.ts.map +1 -1
  29. package/dist/lockfile.d.ts +24 -3
  30. package/dist/lockfile.d.ts.map +1 -1
  31. package/dist/manifest.d.ts +12 -1
  32. package/dist/manifest.d.ts.map +1 -1
  33. package/dist/npm.d.ts +11 -0
  34. package/dist/npm.d.ts.map +1 -1
  35. package/dist/output.d.ts +51 -0
  36. package/dist/output.d.ts.map +1 -0
  37. package/dist/paths.d.ts +5 -0
  38. package/dist/paths.d.ts.map +1 -1
  39. package/dist/progress.d.ts +78 -0
  40. package/dist/progress.d.ts.map +1 -0
  41. package/dist/symlinks.d.ts.map +1 -1
  42. package/package.json +14 -7
  43. package/.editorconfig +0 -14
  44. package/.github/icon.png +0 -0
  45. package/.github/logo.png +0 -0
  46. package/.github/workflows/ci.yml +0 -32
  47. package/.github/workflows/publish.yml +0 -42
  48. package/.prettierrc +0 -6
  49. package/biome.json +0 -29
  50. package/bun.lock +0 -112
  51. package/plugins/exa/README.md +0 -159
  52. package/plugins/exa/package.json +0 -89
  53. package/plugins/exa/tools/exa/company.ts +0 -46
  54. package/plugins/exa/tools/exa/index.ts +0 -75
  55. package/plugins/exa/tools/exa/linkedin.ts +0 -46
  56. package/plugins/exa/tools/exa/researcher.ts +0 -48
  57. package/plugins/exa/tools/exa/runtime.json +0 -4
  58. package/plugins/exa/tools/exa/search.ts +0 -57
  59. package/plugins/exa/tools/exa/shared.ts +0 -256
  60. package/plugins/exa/tools/exa/websets.ts +0 -73
  61. package/plugins/metal-theme/README.md +0 -13
  62. package/plugins/metal-theme/omp.json +0 -8
  63. package/plugins/metal-theme/package.json +0 -28
  64. package/plugins/metal-theme/themes/metal.json +0 -79
  65. package/plugins/subagents/README.md +0 -28
  66. package/plugins/subagents/agents/explore.md +0 -82
  67. package/plugins/subagents/agents/planner.md +0 -54
  68. package/plugins/subagents/agents/reviewer.md +0 -59
  69. package/plugins/subagents/agents/task.md +0 -53
  70. package/plugins/subagents/commands/architect-plan.md +0 -10
  71. package/plugins/subagents/commands/implement-with-critic.md +0 -11
  72. package/plugins/subagents/commands/implement.md +0 -11
  73. package/plugins/subagents/omp.json +0 -15
  74. package/plugins/subagents/package.json +0 -58
  75. package/plugins/subagents/tools/task/index.ts +0 -1247
  76. package/plugins/user-prompt/README.md +0 -86
  77. package/plugins/user-prompt/package.json +0 -30
  78. package/plugins/user-prompt/tools/user-prompt/index.ts +0 -263
  79. package/scripts/bump-version.sh +0 -49
  80. package/scripts/publish.sh +0 -35
  81. package/src/cli.ts +0 -242
  82. package/src/commands/config.ts +0 -399
  83. package/src/commands/create.ts +0 -203
  84. package/src/commands/doctor.ts +0 -305
  85. package/src/commands/enable.ts +0 -122
  86. package/src/commands/env.ts +0 -38
  87. package/src/commands/features.ts +0 -332
  88. package/src/commands/info.ts +0 -120
  89. package/src/commands/init.ts +0 -60
  90. package/src/commands/install.ts +0 -767
  91. package/src/commands/link.ts +0 -159
  92. package/src/commands/list.ts +0 -197
  93. package/src/commands/outdated.ts +0 -87
  94. package/src/commands/search.ts +0 -77
  95. package/src/commands/uninstall.ts +0 -127
  96. package/src/commands/update.ts +0 -175
  97. package/src/commands/why.ts +0 -136
  98. package/src/conflicts.ts +0 -116
  99. package/src/errors.ts +0 -22
  100. package/src/index.ts +0 -46
  101. package/src/lock.ts +0 -46
  102. package/src/lockfile.ts +0 -132
  103. package/src/manifest.ts +0 -360
  104. package/src/npm.ts +0 -206
  105. package/src/paths.ts +0 -137
  106. package/src/runtime.ts +0 -109
  107. package/src/symlinks.ts +0 -460
  108. package/tsconfig.json +0 -28
package/src/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.4.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,399 +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: {
18
- omp?: {
19
- variables?: Record<string, OmpVariable>;
20
- features?: Record<string, { variables?: Record<string, OmpVariable> }>;
21
- };
22
- },
23
- enabledFeatures: string[],
24
- ): Record<string, OmpVariable> {
25
- const vars: Record<string, OmpVariable> = {};
26
-
27
- // Top-level variables
28
- if (pkgJson.omp?.variables) {
29
- Object.assign(vars, pkgJson.omp.variables);
30
- }
31
-
32
- // Variables from enabled features
33
- if (pkgJson.omp?.features) {
34
- for (const fname of enabledFeatures) {
35
- const feature = pkgJson.omp.features[fname];
36
- if (feature?.variables) {
37
- Object.assign(vars, feature.variables);
38
- }
39
- }
40
- }
41
-
42
- return vars;
43
- }
44
-
45
- /**
46
- * Parse a string value to the appropriate type based on variable definition
47
- */
48
- function parseValue(value: string, varDef: OmpVariable): string | number | boolean | string[] {
49
- switch (varDef.type) {
50
- case "number": {
51
- const num = Number(value);
52
- if (Number.isNaN(num)) {
53
- throw new Error(`Invalid number: ${value}`);
54
- }
55
- return num;
56
- }
57
- case "boolean":
58
- if (value === "true" || value === "1" || value === "yes") return true;
59
- if (value === "false" || value === "0" || value === "no") return false;
60
- throw new Error(`Invalid boolean: ${value}. Use true/false, 1/0, or yes/no`);
61
- case "string[]":
62
- return value
63
- .split(",")
64
- .map((s) => s.trim())
65
- .filter(Boolean);
66
- default:
67
- return value;
68
- }
69
- }
70
-
71
- /**
72
- * Format a value for display
73
- */
74
- function formatValue(value: unknown, varDef: OmpVariable): string {
75
- if (value === undefined) {
76
- return chalk.dim("(not set)");
77
- }
78
- if (varDef.type === "string[]" && Array.isArray(value)) {
79
- return value.join(", ");
80
- }
81
- if (typeof value === "string" && varDef.env) {
82
- // Mask sensitive values (likely API keys)
83
- if (value.length > 8) {
84
- return `${value.slice(0, 4)}...${value.slice(-4)}`;
85
- }
86
- }
87
- return String(value);
88
- }
89
-
90
- /**
91
- * Resolve which features are currently enabled
92
- *
93
- * - null/undefined: use plugin defaults (features with default !== false)
94
- * - ["*"]: explicitly all features
95
- * - []: no optional features
96
- * - ["f1", "f2"]: specific features
97
- */
98
- function resolveEnabledFeatures(
99
- allFeatureNames: string[],
100
- storedFeatures: string[] | null | undefined,
101
- pluginFeatures: Record<string, { default?: boolean }>,
102
- ): string[] {
103
- // Explicit "all features" request
104
- if (Array.isArray(storedFeatures) && storedFeatures.includes("*")) return allFeatureNames;
105
- // Explicit feature list (including empty array = no features)
106
- if (Array.isArray(storedFeatures)) return storedFeatures;
107
- // null/undefined = use defaults
108
- return Object.entries(pluginFeatures)
109
- .filter(([_, f]) => f.default !== false)
110
- .map(([name]) => name);
111
- }
112
-
113
- /**
114
- * List all configurable variables for a plugin
115
- * omp config @oh-my-pi/exa
116
- */
117
- export async function listConfig(name: string, options: ConfigOptions = {}): Promise<void> {
118
- const isGlobal = resolveScope(options);
119
- const pluginsJson = await loadPluginsJson(isGlobal);
120
-
121
- if (!pluginsJson.plugins[name]) {
122
- console.log(chalk.yellow(`Plugin "${name}" is not installed.`));
123
- process.exitCode = 1;
124
- return;
125
- }
126
-
127
- const pkgJson = await readPluginPackageJson(name, isGlobal);
128
- if (!pkgJson) {
129
- console.log(chalk.red(`Could not read package.json for ${name}`));
130
- process.exitCode = 1;
131
- return;
132
- }
133
-
134
- const allFeatureNames = Object.keys(pkgJson.omp?.features || {});
135
- const config = pluginsJson.config?.[name];
136
- const enabledFeatures = resolveEnabledFeatures(allFeatureNames, config?.features, pkgJson.omp?.features || {});
137
- const variables = collectVariables(pkgJson, enabledFeatures);
138
-
139
- if (Object.keys(variables).length === 0) {
140
- console.log(chalk.yellow(`Plugin "${name}" has no configurable variables.`));
141
- return;
142
- }
143
-
144
- const userVars = config?.variables || {};
145
-
146
- if (options.json) {
147
- console.log(
148
- JSON.stringify(
149
- {
150
- plugin: name,
151
- variables: Object.entries(variables).map(([vname, vdef]) => ({
152
- name: vname,
153
- type: vdef.type,
154
- value: userVars[vname],
155
- default: vdef.default,
156
- required: vdef.required,
157
- env: vdef.env,
158
- description: vdef.description,
159
- })),
160
- },
161
- null,
162
- 2,
163
- ),
164
- );
165
- return;
166
- }
167
-
168
- console.log(chalk.bold(`\nVariables for ${name}:\n`));
169
-
170
- for (const [vname, vdef] of Object.entries(variables)) {
171
- const currentValue = userVars[vname];
172
- const hasValue = currentValue !== undefined;
173
- const hasDefault = vdef.default !== undefined;
174
-
175
- const icon = hasValue
176
- ? chalk.green("✓")
177
- : hasDefault
178
- ? chalk.blue("○")
179
- : vdef.required
180
- ? chalk.red("!")
181
- : chalk.gray("○");
182
- const requiredStr = vdef.required && !hasValue ? chalk.red(" (required)") : "";
183
- const envStr = vdef.env ? chalk.dim(` [${vdef.env}]`) : "";
184
-
185
- console.log(`${icon} ${chalk.bold(vname)}${requiredStr}${envStr}`);
186
-
187
- if (vdef.description) {
188
- console.log(chalk.dim(` ${vdef.description}`));
189
- }
190
-
191
- console.log(chalk.dim(` Type: ${vdef.type}`));
192
-
193
- if (hasValue) {
194
- console.log(` Value: ${formatValue(currentValue, vdef)}`);
195
- } else if (hasDefault) {
196
- console.log(` Default: ${formatValue(vdef.default, vdef)}`);
197
- }
198
- }
199
-
200
- console.log();
201
- console.log(chalk.dim(`Set a value: omp config ${name} <variable> <value>`));
202
- console.log(chalk.dim(`Delete a value: omp config ${name} <variable> --delete`));
203
- }
204
-
205
- /**
206
- * Get a specific variable value
207
- * omp config @oh-my-pi/exa apiKey
208
- */
209
- export async function getConfig(name: string, key: string, options: ConfigOptions = {}): Promise<void> {
210
- const isGlobal = resolveScope(options);
211
- const pluginsJson = await loadPluginsJson(isGlobal);
212
-
213
- if (!pluginsJson.plugins[name]) {
214
- console.log(chalk.yellow(`Plugin "${name}" is not installed.`));
215
- process.exitCode = 1;
216
- return;
217
- }
218
-
219
- const pkgJson = await readPluginPackageJson(name, isGlobal);
220
- if (!pkgJson) {
221
- console.log(chalk.red(`Could not read package.json for ${name}`));
222
- process.exitCode = 1;
223
- return;
224
- }
225
-
226
- const allFeatureNames = Object.keys(pkgJson.omp?.features || {});
227
- const config = pluginsJson.config?.[name];
228
- const enabledFeatures = resolveEnabledFeatures(allFeatureNames, config?.features, pkgJson.omp?.features || {});
229
- const variables = collectVariables(pkgJson, enabledFeatures);
230
-
231
- const varDef = variables[key];
232
- if (!varDef) {
233
- console.log(chalk.red(`Unknown variable "${key}".`));
234
- console.log(chalk.dim(`Available: ${Object.keys(variables).join(", ") || "(none)"}`));
235
- process.exitCode = 1;
236
- return;
237
- }
238
-
239
- const userValue = config?.variables?.[key];
240
- const value = userValue ?? varDef.default;
241
-
242
- if (options.json) {
243
- console.log(JSON.stringify({ plugin: name, variable: key, value, default: varDef.default }, null, 2));
244
- return;
245
- }
246
-
247
- if (value !== undefined) {
248
- console.log(formatValue(value, varDef));
249
- } else {
250
- console.log(chalk.dim("(not set)"));
251
- }
252
- }
253
-
254
- /**
255
- * Set a variable value
256
- * omp config @oh-my-pi/exa apiKey sk-xxx
257
- */
258
- export async function setConfig(name: string, key: string, value: string, options: ConfigOptions = {}): Promise<void> {
259
- const isGlobal = resolveScope(options);
260
- const pluginsJson = await loadPluginsJson(isGlobal);
261
-
262
- if (!pluginsJson.plugins[name]) {
263
- console.log(chalk.yellow(`Plugin "${name}" is not installed.`));
264
- process.exitCode = 1;
265
- return;
266
- }
267
-
268
- const pkgJson = await readPluginPackageJson(name, isGlobal);
269
- if (!pkgJson) {
270
- console.log(chalk.red(`Could not read package.json for ${name}`));
271
- process.exitCode = 1;
272
- return;
273
- }
274
-
275
- const allFeatureNames = Object.keys(pkgJson.omp?.features || {});
276
- const config = pluginsJson.config?.[name];
277
- const enabledFeatures = resolveEnabledFeatures(allFeatureNames, config?.features, pkgJson.omp?.features || {});
278
- const variables = collectVariables(pkgJson, enabledFeatures);
279
-
280
- const varDef = variables[key];
281
- if (!varDef) {
282
- console.log(chalk.red(`Unknown variable "${key}".`));
283
- console.log(chalk.dim(`Available: ${Object.keys(variables).join(", ") || "(none)"}`));
284
- process.exitCode = 1;
285
- return;
286
- }
287
-
288
- // Parse and validate value
289
- let parsed: string | number | boolean | string[];
290
- try {
291
- parsed = parseValue(value, varDef);
292
- } catch (err) {
293
- console.log(chalk.red((err as Error).message));
294
- process.exitCode = 1;
295
- return;
296
- }
297
-
298
- // Update config
299
- if (!pluginsJson.config) pluginsJson.config = {};
300
- if (!pluginsJson.config[name]) pluginsJson.config[name] = {};
301
- if (!pluginsJson.config[name].variables) pluginsJson.config[name].variables = {};
302
-
303
- pluginsJson.config[name].variables[key] = parsed;
304
- await savePluginsJson(pluginsJson, isGlobal);
305
-
306
- console.log(chalk.green(`✓ Set ${name}.${key} = ${JSON.stringify(parsed)}`));
307
-
308
- if (varDef.env) {
309
- console.log(chalk.dim(` Environment variable: ${varDef.env}`));
310
- console.log(chalk.dim(` Export with: omp env`));
311
- }
312
-
313
- if (options.json) {
314
- console.log(JSON.stringify({ plugin: name, variable: key, value: parsed }, null, 2));
315
- }
316
- }
317
-
318
- /**
319
- * Delete a variable override (revert to default)
320
- * omp config @oh-my-pi/exa apiKey --delete
321
- */
322
- export async function deleteConfig(name: string, key: string, options: ConfigOptions = {}): Promise<void> {
323
- const isGlobal = resolveScope(options);
324
- const pluginsJson = await loadPluginsJson(isGlobal);
325
-
326
- if (!pluginsJson.plugins[name]) {
327
- console.log(chalk.yellow(`Plugin "${name}" is not installed.`));
328
- process.exitCode = 1;
329
- return;
330
- }
331
-
332
- const config = pluginsJson.config?.[name];
333
- // Check key presence with hasOwnProperty, not truthiness (allows deleting falsy values like false, 0, "", [])
334
- if (!config?.variables || !Object.hasOwn(config.variables, key)) {
335
- console.log(chalk.yellow(`Variable "${key}" is not set for ${name}.`));
336
- return;
337
- }
338
-
339
- delete pluginsJson.config![name].variables![key];
340
-
341
- // Clean up empty objects
342
- if (Object.keys(pluginsJson.config![name].variables!).length === 0) {
343
- delete pluginsJson.config![name].variables;
344
- }
345
- if (Object.keys(pluginsJson.config![name]).length === 0) {
346
- delete pluginsJson.config![name];
347
- }
348
- if (Object.keys(pluginsJson.config!).length === 0) {
349
- delete pluginsJson.config;
350
- }
351
-
352
- await savePluginsJson(pluginsJson, isGlobal);
353
-
354
- console.log(chalk.green(`✓ Deleted ${name}.${key} (reverted to default)`));
355
-
356
- if (options.json) {
357
- console.log(JSON.stringify({ plugin: name, variable: key, deleted: true }, null, 2));
358
- }
359
- }
360
-
361
- /**
362
- * Main config command handler
363
- * Routes to list, get, set, or delete based on arguments
364
- */
365
- export async function configCommand(
366
- name: string,
367
- keyOrOptions?: string | ConfigOptions,
368
- valueOrOptions?: string | ConfigOptions,
369
- options: ConfigOptions = {},
370
- ): Promise<void> {
371
- // Handle different argument patterns
372
- let key: string | undefined;
373
- let value: string | undefined;
374
- let opts: ConfigOptions;
375
-
376
- if (typeof keyOrOptions === "object") {
377
- // omp config <name> [options]
378
- opts = keyOrOptions;
379
- } else if (typeof valueOrOptions === "object") {
380
- // omp config <name> <key> [options]
381
- key = keyOrOptions;
382
- opts = valueOrOptions;
383
- } else {
384
- // omp config <name> <key> <value> [options]
385
- key = keyOrOptions;
386
- value = valueOrOptions;
387
- opts = options;
388
- }
389
-
390
- if (!key) {
391
- await listConfig(name, opts);
392
- } else if (opts.delete) {
393
- await deleteConfig(name, key, opts);
394
- } else if (value !== undefined) {
395
- await setConfig(name, key, value, opts);
396
- } else {
397
- await getConfig(name, key, opts);
398
- }
399
- }