@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.
- package/README.md +79 -84
- package/dist/cli.js +5025 -1016
- package/dist/commands/config.d.ts +27 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/env.d.ts.map +1 -1
- package/dist/commands/features.d.ts.map +1 -1
- package/dist/commands/info.d.ts.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/install.d.ts +6 -0
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/link.d.ts +1 -0
- package/dist/commands/link.d.ts.map +1 -1
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/outdated.d.ts.map +1 -1
- package/dist/commands/search.d.ts.map +1 -1
- package/dist/commands/uninstall.d.ts +3 -0
- package/dist/commands/uninstall.d.ts.map +1 -1
- package/dist/commands/update.d.ts +1 -0
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/why.d.ts.map +1 -1
- package/dist/conflicts.d.ts +7 -2
- package/dist/conflicts.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/lock.d.ts.map +1 -1
- package/dist/lockfile.d.ts +24 -3
- package/dist/lockfile.d.ts.map +1 -1
- package/dist/manifest.d.ts +12 -1
- package/dist/manifest.d.ts.map +1 -1
- package/dist/npm.d.ts +11 -0
- package/dist/npm.d.ts.map +1 -1
- package/dist/output.d.ts +51 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/paths.d.ts +5 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/progress.d.ts +78 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/symlinks.d.ts +1 -0
- package/dist/symlinks.d.ts.map +1 -1
- package/package.json +24 -10
- package/.github/icon.png +0 -0
- package/.github/logo.png +0 -0
- package/.github/workflows/ci.yml +0 -32
- package/.github/workflows/publish.yml +0 -42
- package/biome.json +0 -29
- package/bun.lock +0 -109
- package/plugins/exa/README.md +0 -153
- package/plugins/exa/package.json +0 -56
- package/plugins/exa/tools/exa/company.ts +0 -35
- package/plugins/exa/tools/exa/index.ts +0 -66
- package/plugins/exa/tools/exa/linkedin.ts +0 -35
- package/plugins/exa/tools/exa/researcher.ts +0 -40
- package/plugins/exa/tools/exa/runtime.json +0 -4
- package/plugins/exa/tools/exa/search.ts +0 -46
- package/plugins/exa/tools/exa/shared.ts +0 -230
- package/plugins/exa/tools/exa/websets.ts +0 -62
- package/plugins/metal-theme/README.md +0 -13
- package/plugins/metal-theme/omp.json +0 -8
- package/plugins/metal-theme/package.json +0 -19
- package/plugins/metal-theme/themes/metal.json +0 -79
- package/plugins/subagents/README.md +0 -25
- package/plugins/subagents/agents/explore.md +0 -71
- package/plugins/subagents/agents/planner.md +0 -51
- package/plugins/subagents/agents/reviewer.md +0 -53
- package/plugins/subagents/agents/task.md +0 -46
- package/plugins/subagents/commands/architect-plan.md +0 -9
- package/plugins/subagents/commands/implement-with-critic.md +0 -10
- package/plugins/subagents/commands/implement.md +0 -10
- package/plugins/subagents/omp.json +0 -15
- package/plugins/subagents/package.json +0 -26
- package/plugins/subagents/tools/task/index.ts +0 -1019
- package/plugins/user-prompt/README.md +0 -130
- package/plugins/user-prompt/package.json +0 -19
- package/plugins/user-prompt/tools/user-prompt/index.ts +0 -235
- package/scripts/bump-version.sh +0 -52
- package/scripts/publish.sh +0 -35
- package/src/cli.ts +0 -242
- package/src/commands/config.ts +0 -384
- package/src/commands/create.ts +0 -203
- package/src/commands/doctor.ts +0 -305
- package/src/commands/enable.ts +0 -122
- package/src/commands/env.ts +0 -38
- package/src/commands/features.ts +0 -295
- package/src/commands/info.ts +0 -120
- package/src/commands/init.ts +0 -60
- package/src/commands/install.ts +0 -700
- package/src/commands/link.ts +0 -159
- package/src/commands/list.ts +0 -186
- package/src/commands/outdated.ts +0 -87
- package/src/commands/search.ts +0 -77
- package/src/commands/uninstall.ts +0 -124
- package/src/commands/update.ts +0 -170
- package/src/commands/why.ts +0 -136
- package/src/conflicts.ts +0 -116
- package/src/errors.ts +0 -22
- package/src/index.ts +0 -46
- package/src/lock.ts +0 -46
- package/src/lockfile.ts +0 -132
- package/src/manifest.ts +0 -360
- package/src/npm.ts +0 -206
- package/src/paths.ts +0 -137
- package/src/runtime.ts +0 -116
- package/src/symlinks.ts +0 -455
- package/tsconfig.json +0 -28
package/src/commands/link.ts
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { mkdir, readFile, rm, symlink, writeFile } from "node:fs/promises";
|
|
3
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
4
|
-
import { createInterface } from "node:readline";
|
|
5
|
-
import { loadPluginsJson, type PluginPackageJson, savePluginsJson } from "@omp/manifest";
|
|
6
|
-
import { NODE_MODULES_DIR, PROJECT_NODE_MODULES, resolveScope } from "@omp/paths";
|
|
7
|
-
import { createPluginSymlinks } from "@omp/symlinks";
|
|
8
|
-
import chalk from "chalk";
|
|
9
|
-
|
|
10
|
-
async function confirmCreate(path: string): Promise<boolean> {
|
|
11
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
12
|
-
console.log(chalk.dim(" Non-interactive mode: auto-creating package.json"));
|
|
13
|
-
return true;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
17
|
-
return new Promise((resolve) => {
|
|
18
|
-
rl.question(chalk.yellow(` Create minimal package.json at ${path}? [Y/n] `), (answer) => {
|
|
19
|
-
rl.close();
|
|
20
|
-
resolve(answer.toLowerCase() !== "n");
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface LinkOptions {
|
|
26
|
-
name?: string;
|
|
27
|
-
global?: boolean;
|
|
28
|
-
local?: boolean;
|
|
29
|
-
force?: boolean;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Link a local plugin directory for development
|
|
34
|
-
* Creates a symlink in node_modules pointing to the local directory
|
|
35
|
-
*/
|
|
36
|
-
export async function linkPlugin(localPath: string, options: LinkOptions = {}): Promise<void> {
|
|
37
|
-
const isGlobal = resolveScope(options);
|
|
38
|
-
const nodeModules = isGlobal ? NODE_MODULES_DIR : PROJECT_NODE_MODULES;
|
|
39
|
-
|
|
40
|
-
// Expand ~ to home directory
|
|
41
|
-
if (localPath.startsWith("~")) {
|
|
42
|
-
localPath = join(process.env.HOME || "", localPath.slice(1));
|
|
43
|
-
}
|
|
44
|
-
localPath = resolve(localPath);
|
|
45
|
-
|
|
46
|
-
// Verify the path exists
|
|
47
|
-
if (!existsSync(localPath)) {
|
|
48
|
-
console.log(chalk.red(`Error: Path does not exist: ${localPath}`));
|
|
49
|
-
process.exitCode = 1;
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Read package.json from local path
|
|
54
|
-
let pkgJson: PluginPackageJson;
|
|
55
|
-
const localPkgJsonPath = join(localPath, "package.json");
|
|
56
|
-
const localOmpJsonPath = join(localPath, "omp.json");
|
|
57
|
-
|
|
58
|
-
if (existsSync(localPkgJsonPath)) {
|
|
59
|
-
pkgJson = JSON.parse(await readFile(localPkgJsonPath, "utf-8"));
|
|
60
|
-
} else if (existsSync(localOmpJsonPath)) {
|
|
61
|
-
// Convert legacy omp.json to package.json format
|
|
62
|
-
const ompJson = JSON.parse(await readFile(localOmpJsonPath, "utf-8"));
|
|
63
|
-
pkgJson = {
|
|
64
|
-
name: ompJson.name || options.name || basename(localPath),
|
|
65
|
-
version: ompJson.version || "0.0.0-dev",
|
|
66
|
-
description: ompJson.description,
|
|
67
|
-
keywords: ["omp-plugin"],
|
|
68
|
-
omp: {
|
|
69
|
-
install: ompJson.install,
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
// Persist the conversion to package.json
|
|
74
|
-
console.log(chalk.dim(" Converting omp.json to package.json..."));
|
|
75
|
-
await writeFile(localPkgJsonPath, JSON.stringify(pkgJson, null, 2));
|
|
76
|
-
} else {
|
|
77
|
-
// Create minimal package.json so npm operations work correctly
|
|
78
|
-
console.log(chalk.yellow(" No package.json found in target directory."));
|
|
79
|
-
const shouldCreate = await confirmCreate(localPkgJsonPath);
|
|
80
|
-
if (!shouldCreate) {
|
|
81
|
-
console.log(chalk.yellow(" Aborted: package.json required for linking"));
|
|
82
|
-
process.exitCode = 1;
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
pkgJson = {
|
|
86
|
-
name: options.name || basename(localPath),
|
|
87
|
-
version: "0.0.0-dev",
|
|
88
|
-
keywords: ["omp-plugin"],
|
|
89
|
-
omp: {
|
|
90
|
-
install: [],
|
|
91
|
-
},
|
|
92
|
-
};
|
|
93
|
-
console.log(chalk.dim(" Creating minimal package.json..."));
|
|
94
|
-
await writeFile(localPkgJsonPath, JSON.stringify(pkgJson, null, 2));
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const pluginName = options.name || pkgJson.name;
|
|
98
|
-
const pluginDir = join(nodeModules, pluginName);
|
|
99
|
-
|
|
100
|
-
// Check if already installed
|
|
101
|
-
const pluginsJson = await loadPluginsJson(isGlobal);
|
|
102
|
-
if (pluginsJson.plugins[pluginName]) {
|
|
103
|
-
const existingSpec = pluginsJson.plugins[pluginName];
|
|
104
|
-
const isLinked = existingSpec.startsWith("file:");
|
|
105
|
-
|
|
106
|
-
if (isLinked) {
|
|
107
|
-
console.log(chalk.yellow(`Plugin "${pluginName}" is already linked.`));
|
|
108
|
-
console.log(chalk.dim(` Current link: ${existingSpec}`));
|
|
109
|
-
console.log(chalk.dim(" Re-linking..."));
|
|
110
|
-
// Continue with the linking process (will overwrite)
|
|
111
|
-
} else if (options.force) {
|
|
112
|
-
console.log(chalk.yellow(`Plugin "${pluginName}" is installed from npm. Overwriting with link...`));
|
|
113
|
-
// Continue with the linking process (will overwrite)
|
|
114
|
-
} else {
|
|
115
|
-
console.log(chalk.yellow(`Plugin "${pluginName}" is already installed from npm.`));
|
|
116
|
-
console.log(chalk.dim("Use omp uninstall first, or specify a different name with -n"));
|
|
117
|
-
console.log(chalk.dim("Or use --force to overwrite the npm installation"));
|
|
118
|
-
process.exitCode = 1;
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
console.log(chalk.blue(`Linking ${localPath}...`));
|
|
125
|
-
|
|
126
|
-
// Create parent directory (handles scoped packages like @org/name)
|
|
127
|
-
await mkdir(dirname(pluginDir), { recursive: true });
|
|
128
|
-
|
|
129
|
-
// Remove existing if present
|
|
130
|
-
if (existsSync(pluginDir)) {
|
|
131
|
-
await rm(pluginDir, { force: true, recursive: true });
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Create symlink to the plugin directory
|
|
135
|
-
await symlink(localPath, pluginDir);
|
|
136
|
-
console.log(chalk.dim(` Symlinked: ${pluginDir} → ${localPath}`));
|
|
137
|
-
|
|
138
|
-
// Update plugins.json with file: protocol
|
|
139
|
-
pluginsJson.plugins[pluginName] = `file:${localPath}`;
|
|
140
|
-
await savePluginsJson(pluginsJson, isGlobal);
|
|
141
|
-
|
|
142
|
-
// Create symlinks for omp.install entries
|
|
143
|
-
if (pkgJson.omp?.install?.length) {
|
|
144
|
-
await createPluginSymlinks(pluginName, pkgJson, isGlobal);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
console.log(
|
|
148
|
-
chalk.green(`\n✓ Linked "${pluginName}"${pkgJson.version ? ` v${pkgJson.version}` : ""} (development mode)`),
|
|
149
|
-
);
|
|
150
|
-
console.log(chalk.dim(" Changes to the source will be reflected immediately"));
|
|
151
|
-
} catch (err) {
|
|
152
|
-
console.log(chalk.red(`Error linking plugin: ${(err as Error).message}`));
|
|
153
|
-
process.exitCode = 1;
|
|
154
|
-
// Cleanup on failure
|
|
155
|
-
try {
|
|
156
|
-
await rm(pluginDir, { force: true });
|
|
157
|
-
} catch {}
|
|
158
|
-
}
|
|
159
|
-
}
|
package/src/commands/list.ts
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { loadPluginsJson, readPluginPackageJson } from "@omp/manifest";
|
|
2
|
-
import { resolveScope } from "@omp/paths";
|
|
3
|
-
import chalk from "chalk";
|
|
4
|
-
|
|
5
|
-
export interface ListOptions {
|
|
6
|
-
global?: boolean;
|
|
7
|
-
local?: boolean;
|
|
8
|
-
json?: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Known file categories with their patterns and display info
|
|
13
|
-
*/
|
|
14
|
-
interface FileCategory {
|
|
15
|
-
pattern: RegExp;
|
|
16
|
-
label: string;
|
|
17
|
-
color: (s: string) => string;
|
|
18
|
-
extractName: (dest: string) => string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const FILE_CATEGORIES: FileCategory[] = [
|
|
22
|
-
{
|
|
23
|
-
pattern: /^agent\/tools\/([^/]+)\//,
|
|
24
|
-
label: "Tools",
|
|
25
|
-
color: chalk.cyan,
|
|
26
|
-
extractName: (dest) => dest.match(/^agent\/tools\/([^/]+)\//)?.[1] || dest,
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
pattern: /^agent\/agents\/(.+)\.md$/,
|
|
30
|
-
label: "Agents",
|
|
31
|
-
color: chalk.magenta,
|
|
32
|
-
extractName: (dest) => dest.match(/^agent\/agents\/(.+)\.md$/)?.[1] || dest,
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
pattern: /^agent\/commands\/(.+)\.md$/,
|
|
36
|
-
label: "Commands",
|
|
37
|
-
color: chalk.yellow,
|
|
38
|
-
extractName: (dest) => dest.match(/^agent\/commands\/(.+)\.md$/)?.[1] || dest,
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
pattern: /^agent\/themes\/(.+)\.json$/,
|
|
42
|
-
label: "Themes",
|
|
43
|
-
color: chalk.green,
|
|
44
|
-
extractName: (dest) => dest.match(/^agent\/themes\/(.+)\.json$/)?.[1] || dest,
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
pattern: /^agent\/prompts?\//,
|
|
48
|
-
label: "Prompts",
|
|
49
|
-
color: chalk.blue,
|
|
50
|
-
extractName: (dest) => dest.split("/").pop()?.replace(/\.[^.]+$/, "") || dest,
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
pattern: /^agent\/hooks?\//,
|
|
54
|
-
label: "Hooks",
|
|
55
|
-
color: chalk.red,
|
|
56
|
-
extractName: (dest) => dest.split("/").pop()?.replace(/\.[^.]+$/, "") || dest,
|
|
57
|
-
},
|
|
58
|
-
];
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Categorize installed files into known categories
|
|
62
|
-
*/
|
|
63
|
-
function categorizeFiles(files: string[]): { categorized: Map<string, string[]>; uncategorized: string[] } {
|
|
64
|
-
const categorized = new Map<string, string[]>();
|
|
65
|
-
const uncategorized: string[] = [];
|
|
66
|
-
|
|
67
|
-
for (const file of files) {
|
|
68
|
-
let matched = false;
|
|
69
|
-
for (const category of FILE_CATEGORIES) {
|
|
70
|
-
if (category.pattern.test(file)) {
|
|
71
|
-
const name = category.extractName(file);
|
|
72
|
-
const existing = categorized.get(category.label) || [];
|
|
73
|
-
if (!existing.includes(name)) {
|
|
74
|
-
existing.push(name);
|
|
75
|
-
categorized.set(category.label, existing);
|
|
76
|
-
}
|
|
77
|
-
matched = true;
|
|
78
|
-
break;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if (!matched) {
|
|
82
|
-
uncategorized.push(file);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return { categorized, uncategorized };
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Format categorized files for display
|
|
91
|
-
*/
|
|
92
|
-
function formatContributes(files: string[]): string[] {
|
|
93
|
-
const { categorized, uncategorized } = categorizeFiles(files);
|
|
94
|
-
const lines: string[] = [];
|
|
95
|
-
|
|
96
|
-
if (categorized.size > 0 || uncategorized.length > 0) {
|
|
97
|
-
lines.push(` ${chalk.white("Contributes:")}`);
|
|
98
|
-
|
|
99
|
-
for (const category of FILE_CATEGORIES) {
|
|
100
|
-
const items = categorized.get(category.label);
|
|
101
|
-
if (items && items.length > 0) {
|
|
102
|
-
const count = category.color(`${items.length}`);
|
|
103
|
-
const names = items.map((n) => chalk.dim(n)).join(chalk.dim(", "));
|
|
104
|
-
lines.push(` ${chalk.green("+")} ${category.label} (${count}): ${names}`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (uncategorized.length > 0) {
|
|
109
|
-
const count = chalk.gray(`${uncategorized.length}`);
|
|
110
|
-
const names = uncategorized.map((n) => chalk.dim(n)).join(chalk.dim(", "));
|
|
111
|
-
lines.push(` ${chalk.green("+")} Files (${count}): ${names}`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return lines;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* List all installed plugins
|
|
120
|
-
*/
|
|
121
|
-
export async function listPlugins(options: ListOptions = {}): Promise<void> {
|
|
122
|
-
const isGlobal = resolveScope(options);
|
|
123
|
-
const pluginsJson = await loadPluginsJson(isGlobal);
|
|
124
|
-
const pluginNames = Object.keys(pluginsJson.plugins);
|
|
125
|
-
|
|
126
|
-
if (pluginNames.length === 0) {
|
|
127
|
-
console.log(chalk.yellow("No plugins installed."));
|
|
128
|
-
console.log(chalk.dim("Install one with: omp install <package>"));
|
|
129
|
-
process.exitCode = 1;
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (options.json) {
|
|
134
|
-
const plugins: Record<string, unknown> = {};
|
|
135
|
-
for (const name of pluginNames) {
|
|
136
|
-
const pkgJson = await readPluginPackageJson(name, isGlobal);
|
|
137
|
-
const disabled = pluginsJson.disabled?.includes(name) || false;
|
|
138
|
-
plugins[name] = {
|
|
139
|
-
version: pkgJson?.version || pluginsJson.plugins[name],
|
|
140
|
-
description: pkgJson?.description,
|
|
141
|
-
disabled,
|
|
142
|
-
files: pkgJson?.omp?.install?.map((e) => e.dest) || [],
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
console.log(JSON.stringify({ plugins }, null, 2));
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const location = isGlobal ? "~/.pi/plugins" : ".pi";
|
|
150
|
-
console.log(chalk.bold(`Installed plugins (${pluginNames.length}) [${location}]:\n`));
|
|
151
|
-
|
|
152
|
-
for (const name of pluginNames.sort()) {
|
|
153
|
-
const pkgJson = await readPluginPackageJson(name, isGlobal);
|
|
154
|
-
const specifier = pluginsJson.plugins[name];
|
|
155
|
-
const isLocal = specifier.startsWith("file:");
|
|
156
|
-
const disabled = pluginsJson.disabled?.includes(name);
|
|
157
|
-
const isMissing = !pkgJson;
|
|
158
|
-
|
|
159
|
-
const version = pkgJson?.version ? chalk.dim(`v${pkgJson.version}`) : chalk.dim(`(${specifier})`);
|
|
160
|
-
const localBadge = isLocal ? chalk.cyan(" (local)") : "";
|
|
161
|
-
const disabledBadge = disabled ? chalk.yellow(" (disabled)") : "";
|
|
162
|
-
const missingBadge = isMissing ? chalk.red(" (missing)") : "";
|
|
163
|
-
const icon = disabled ? chalk.gray("○") : isMissing ? chalk.red("✗") : chalk.green("◆");
|
|
164
|
-
|
|
165
|
-
console.log(`${icon} ${chalk.bold(name)} ${version}${localBadge}${disabledBadge}${missingBadge}`);
|
|
166
|
-
|
|
167
|
-
if (pkgJson?.description) {
|
|
168
|
-
console.log(chalk.dim(` ${pkgJson.description}`));
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (isLocal) {
|
|
172
|
-
const localPath = specifier.replace("file:", "");
|
|
173
|
-
console.log(chalk.dim(` Path: ${localPath}`));
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (pkgJson?.omp?.install?.length) {
|
|
177
|
-
const files = pkgJson.omp.install.map((e) => e.dest);
|
|
178
|
-
const contributeLines = formatContributes(files);
|
|
179
|
-
for (const line of contributeLines) {
|
|
180
|
-
console.log(line);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
console.log();
|
|
185
|
-
}
|
|
186
|
-
}
|
package/src/commands/outdated.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { loadPluginsJson } from "@omp/manifest";
|
|
2
|
-
import { npmOutdated } from "@omp/npm";
|
|
3
|
-
import { PLUGINS_DIR, resolveScope } from "@omp/paths";
|
|
4
|
-
import chalk from "chalk";
|
|
5
|
-
|
|
6
|
-
export interface OutdatedOptions {
|
|
7
|
-
global?: boolean;
|
|
8
|
-
local?: boolean;
|
|
9
|
-
json?: boolean;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* List plugins with newer versions available
|
|
14
|
-
*/
|
|
15
|
-
export async function showOutdated(options: OutdatedOptions = {}): Promise<void> {
|
|
16
|
-
const isGlobal = resolveScope(options);
|
|
17
|
-
const prefix = isGlobal ? PLUGINS_DIR : ".pi";
|
|
18
|
-
|
|
19
|
-
console.log(chalk.blue("Checking for outdated plugins..."));
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
const outdated = await npmOutdated(prefix);
|
|
23
|
-
const pluginsJson = await loadPluginsJson(isGlobal);
|
|
24
|
-
|
|
25
|
-
// Filter to only show plugins we manage AND are not local
|
|
26
|
-
const managedOutdated = Object.entries(outdated).filter(([name]) => {
|
|
27
|
-
const specifier = pluginsJson.plugins[name];
|
|
28
|
-
if (!specifier) return false; // Not in our manifest
|
|
29
|
-
if (specifier.startsWith("file:")) return false; // Local plugin, skip
|
|
30
|
-
return true;
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
if (managedOutdated.length === 0) {
|
|
34
|
-
console.log(chalk.green("\n✓ All plugins are up to date!"));
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (options.json) {
|
|
39
|
-
const result = Object.fromEntries(managedOutdated);
|
|
40
|
-
console.log(JSON.stringify({ outdated: result }, null, 2));
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
console.log(chalk.bold(`\nOutdated plugins (${managedOutdated.length}):\n`));
|
|
45
|
-
|
|
46
|
-
// Header
|
|
47
|
-
console.log(
|
|
48
|
-
chalk.dim(" Package".padEnd(30)) +
|
|
49
|
-
chalk.dim("Current".padEnd(15)) +
|
|
50
|
-
chalk.dim("Wanted".padEnd(15)) +
|
|
51
|
-
chalk.dim("Latest"),
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
for (const [name, versions] of managedOutdated) {
|
|
55
|
-
const current = versions.current || "?";
|
|
56
|
-
const wanted = versions.wanted || "?";
|
|
57
|
-
const latest = versions.latest || "?";
|
|
58
|
-
|
|
59
|
-
const hasMinorUpdate = wanted !== current;
|
|
60
|
-
const hasMajorUpdate = latest !== wanted;
|
|
61
|
-
|
|
62
|
-
const wantedColor = hasMinorUpdate ? chalk.yellow : chalk.dim;
|
|
63
|
-
const latestColor = hasMajorUpdate ? chalk.red : wantedColor;
|
|
64
|
-
|
|
65
|
-
console.log(
|
|
66
|
-
` ${chalk.white(name.padEnd(28))}` +
|
|
67
|
-
`${chalk.dim(current.padEnd(15))}` +
|
|
68
|
-
`${wantedColor(wanted.padEnd(15))}` +
|
|
69
|
-
`${latestColor(latest)}`,
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Note about local plugins excluded from check
|
|
74
|
-
const localPlugins = Object.entries(pluginsJson.plugins).filter(([_, spec]) => spec.startsWith("file:"));
|
|
75
|
-
if (localPlugins.length > 0) {
|
|
76
|
-
console.log(chalk.dim(`\nNote: ${localPlugins.length} local plugin(s) excluded from check`));
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
console.log();
|
|
80
|
-
console.log(chalk.dim("Update with: omp update [package]"));
|
|
81
|
-
console.log(chalk.dim(" - 'wanted' = latest within semver range"));
|
|
82
|
-
console.log(chalk.dim(" - 'latest' = latest available version"));
|
|
83
|
-
} catch (err) {
|
|
84
|
-
console.log(chalk.red(`Error checking outdated: ${(err as Error).message}`));
|
|
85
|
-
process.exitCode = 1;
|
|
86
|
-
}
|
|
87
|
-
}
|
package/src/commands/search.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { npmSearch } from "@omp/npm";
|
|
2
|
-
import chalk from "chalk";
|
|
3
|
-
|
|
4
|
-
function truncate(str: string, maxLen: number): string {
|
|
5
|
-
if (!str || str.length <= maxLen) return str;
|
|
6
|
-
return `${str.slice(0, maxLen - 3)}...`;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface SearchOptions {
|
|
10
|
-
json?: boolean;
|
|
11
|
-
limit?: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Search npm for plugins with omp-plugin keyword
|
|
16
|
-
*/
|
|
17
|
-
export async function searchPlugins(query: string, options: SearchOptions = {}): Promise<void> {
|
|
18
|
-
console.log(chalk.blue(`Searching npm for "${query}" with omp-plugin keyword...`));
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
const results = await npmSearch(query, "omp-plugin");
|
|
22
|
-
|
|
23
|
-
if (results.length === 0) {
|
|
24
|
-
console.log(chalk.yellow("\nNo plugins found."));
|
|
25
|
-
console.log(chalk.dim("Try a different search term, or search without keyword:"));
|
|
26
|
-
console.log(chalk.dim(" npm search omp-plugin"));
|
|
27
|
-
process.exitCode = 1;
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const limit = options.limit || 20;
|
|
32
|
-
const displayResults = results.slice(0, limit);
|
|
33
|
-
|
|
34
|
-
if (options.json) {
|
|
35
|
-
console.log(JSON.stringify({ results: displayResults }, null, 2));
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
console.log(chalk.bold(`\nFound ${results.length} plugin(s):\n`));
|
|
40
|
-
|
|
41
|
-
for (const result of displayResults) {
|
|
42
|
-
console.log(chalk.green("◆ ") + chalk.bold(result.name) + chalk.dim(` v${result.version}`));
|
|
43
|
-
|
|
44
|
-
if (result.description) {
|
|
45
|
-
console.log(chalk.dim(` ${truncate(result.description, 100)}`));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (result.keywords?.length) {
|
|
49
|
-
const otherKeywords = result.keywords.filter((k) => k !== "omp-plugin");
|
|
50
|
-
if (otherKeywords.length > 0) {
|
|
51
|
-
console.log(chalk.dim(` tags: ${otherKeywords.join(", ")}`));
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
console.log();
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (results.length > limit) {
|
|
59
|
-
console.log(chalk.dim(`... and ${results.length - limit} more. Use --limit to see more.`));
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
console.log(chalk.dim("Install with: omp install <package-name>"));
|
|
63
|
-
} catch (err) {
|
|
64
|
-
const error = err as Error;
|
|
65
|
-
if (
|
|
66
|
-
error.message.includes("ENOTFOUND") ||
|
|
67
|
-
error.message.includes("ETIMEDOUT") ||
|
|
68
|
-
error.message.includes("EAI_AGAIN")
|
|
69
|
-
) {
|
|
70
|
-
console.log(chalk.red("\nNetwork error: Unable to reach npm registry."));
|
|
71
|
-
console.log(chalk.dim(" Check your internet connection and try again."));
|
|
72
|
-
} else {
|
|
73
|
-
console.log(chalk.red(`\nSearch failed: ${error.message}`));
|
|
74
|
-
}
|
|
75
|
-
process.exitCode = 1;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { rm } from "node:fs/promises";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { createInterface } from "node:readline";
|
|
5
|
-
import { getInstalledPlugins, loadPluginsJson, readPluginPackageJson, savePluginsJson } from "@omp/manifest";
|
|
6
|
-
import { npmUninstall } from "@omp/npm";
|
|
7
|
-
import { NODE_MODULES_DIR, PLUGINS_DIR, PROJECT_NODE_MODULES, resolveScope } from "@omp/paths";
|
|
8
|
-
import { removePluginSymlinks } from "@omp/symlinks";
|
|
9
|
-
import chalk from "chalk";
|
|
10
|
-
|
|
11
|
-
export interface UninstallOptions {
|
|
12
|
-
global?: boolean;
|
|
13
|
-
local?: boolean;
|
|
14
|
-
json?: boolean;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Uninstall a plugin
|
|
19
|
-
*/
|
|
20
|
-
export async function uninstallPlugin(name: string, options: UninstallOptions = {}): Promise<void> {
|
|
21
|
-
const isGlobal = resolveScope(options);
|
|
22
|
-
const prefix = isGlobal ? PLUGINS_DIR : ".pi";
|
|
23
|
-
const nodeModules = isGlobal ? NODE_MODULES_DIR : PROJECT_NODE_MODULES;
|
|
24
|
-
|
|
25
|
-
// Check if plugin is installed
|
|
26
|
-
const pluginsJson = await loadPluginsJson(isGlobal);
|
|
27
|
-
if (!pluginsJson.plugins[name]) {
|
|
28
|
-
console.log(chalk.yellow(`Plugin "${name}" is not installed.`));
|
|
29
|
-
process.exitCode = 1;
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
console.log(chalk.blue(`Uninstalling ${name}...`));
|
|
35
|
-
|
|
36
|
-
// 1. Read package.json for omp.install entries before uninstalling
|
|
37
|
-
const pkgJson = await readPluginPackageJson(name, isGlobal);
|
|
38
|
-
|
|
39
|
-
// Check for shared dependencies
|
|
40
|
-
if (pkgJson?.dependencies) {
|
|
41
|
-
const allPlugins = await getInstalledPlugins(isGlobal);
|
|
42
|
-
const sharedDeps: string[] = [];
|
|
43
|
-
|
|
44
|
-
for (const depName of Object.keys(pkgJson.dependencies)) {
|
|
45
|
-
for (const [otherName, otherPkgJson] of allPlugins) {
|
|
46
|
-
if (otherName !== name && otherPkgJson.dependencies?.[depName]) {
|
|
47
|
-
sharedDeps.push(`${depName} (also used by ${otherName})`);
|
|
48
|
-
break;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (sharedDeps.length > 0) {
|
|
54
|
-
console.log(chalk.yellow("\n⚠ Warning: This plugin shares dependencies with other plugins:"));
|
|
55
|
-
for (const dep of sharedDeps) {
|
|
56
|
-
console.log(chalk.dim(` - ${dep}`));
|
|
57
|
-
}
|
|
58
|
-
console.log(chalk.dim(" These dependencies will remain installed."));
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// 2. Remove symlinks
|
|
63
|
-
if (pkgJson) {
|
|
64
|
-
const result = await removePluginSymlinks(name, pkgJson, isGlobal);
|
|
65
|
-
|
|
66
|
-
if (result.skippedNonSymlinks.length > 0) {
|
|
67
|
-
console.log(chalk.yellow("\nThe following files are not symlinks and were not removed:"));
|
|
68
|
-
for (const file of result.skippedNonSymlinks) {
|
|
69
|
-
console.log(chalk.dim(` - ${file}`));
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
73
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
74
|
-
const answer = await new Promise<string>((resolve) => {
|
|
75
|
-
rl.question(chalk.yellow("Delete these files anyway? [y/N] "), (ans) => {
|
|
76
|
-
rl.close();
|
|
77
|
-
resolve(ans);
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
if (answer.toLowerCase() === "y") {
|
|
82
|
-
for (const file of result.skippedNonSymlinks) {
|
|
83
|
-
await rm(file, { force: true, recursive: true });
|
|
84
|
-
console.log(chalk.dim(` Deleted: ${file}`));
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// 3. npm uninstall
|
|
92
|
-
try {
|
|
93
|
-
await npmUninstall([name], prefix);
|
|
94
|
-
} catch (_err) {
|
|
95
|
-
// Package might have been installed via file: protocol
|
|
96
|
-
// Try to remove manually from node_modules
|
|
97
|
-
const pluginDir = join(nodeModules, name);
|
|
98
|
-
if (existsSync(pluginDir)) {
|
|
99
|
-
await rm(pluginDir, { recursive: true, force: true });
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// 4. Update plugins.json/package.json
|
|
104
|
-
delete pluginsJson.plugins[name];
|
|
105
|
-
// Also remove from disabled list if present
|
|
106
|
-
if (pluginsJson.disabled) {
|
|
107
|
-
pluginsJson.disabled = pluginsJson.disabled.filter((n) => n !== name);
|
|
108
|
-
}
|
|
109
|
-
await savePluginsJson(pluginsJson, isGlobal);
|
|
110
|
-
|
|
111
|
-
console.log(chalk.green(`✓ Uninstalled "${name}"`));
|
|
112
|
-
|
|
113
|
-
if (options.json) {
|
|
114
|
-
console.log(JSON.stringify({ name, success: true }, null, 2));
|
|
115
|
-
}
|
|
116
|
-
} catch (err) {
|
|
117
|
-
console.log(chalk.red(`Error uninstalling plugin: ${(err as Error).message}`));
|
|
118
|
-
process.exitCode = 1;
|
|
119
|
-
|
|
120
|
-
if (options.json) {
|
|
121
|
-
console.log(JSON.stringify({ name, success: false, error: (err as Error).message }, null, 2));
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|