@oh-my-pi/cli 0.1.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/.github/workflows/ci.yml +32 -0
- package/.github/workflows/publish.yml +42 -0
- package/CHECK.md +352 -0
- package/README.md +224 -0
- package/biome.json +29 -0
- package/bun.lock +50 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +3941 -0
- package/dist/commands/create.d.ts +9 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/doctor.d.ts +10 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/enable.d.ts +13 -0
- package/dist/commands/enable.d.ts.map +1 -0
- package/dist/commands/info.d.ts +9 -0
- package/dist/commands/info.d.ts.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/install.d.ts +13 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/link.d.ts +10 -0
- package/dist/commands/link.d.ts.map +1 -0
- package/dist/commands/list.d.ts +9 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/outdated.d.ts +9 -0
- package/dist/commands/outdated.d.ts.map +1 -0
- package/dist/commands/search.d.ts +9 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/uninstall.d.ts +9 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/update.d.ts +9 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/why.d.ts +9 -0
- package/dist/commands/why.d.ts.map +1 -0
- package/dist/conflicts.d.ts +21 -0
- package/dist/conflicts.d.ts.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/manifest.d.ts +81 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/migrate.d.ts +9 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/npm.d.ts +77 -0
- package/dist/npm.d.ts.map +1 -0
- package/dist/paths.d.ts +27 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/symlinks.d.ts +33 -0
- package/dist/symlinks.d.ts.map +1 -0
- package/package.json +36 -0
- package/plugins/metal-theme/README.md +13 -0
- package/plugins/metal-theme/omp.json +8 -0
- package/plugins/metal-theme/package.json +14 -0
- package/plugins/metal-theme/themes/metal.json +79 -0
- package/plugins/subagents/README.md +25 -0
- package/plugins/subagents/agents/explore.md +71 -0
- package/plugins/subagents/agents/planner.md +51 -0
- package/plugins/subagents/agents/reviewer.md +53 -0
- package/plugins/subagents/agents/task.md +46 -0
- package/plugins/subagents/commands/architect-plan.md +9 -0
- package/plugins/subagents/commands/implement-with-critic.md +10 -0
- package/plugins/subagents/commands/implement.md +10 -0
- package/plugins/subagents/omp.json +15 -0
- package/plugins/subagents/package.json +21 -0
- package/plugins/subagents/tools/task/index.ts +1019 -0
- package/scripts/bump-version.sh +52 -0
- package/scripts/publish.sh +35 -0
- package/src/cli.ts +167 -0
- package/src/commands/create.ts +153 -0
- package/src/commands/doctor.ts +217 -0
- package/src/commands/enable.ts +105 -0
- package/src/commands/info.ts +84 -0
- package/src/commands/init.ts +42 -0
- package/src/commands/install.ts +327 -0
- package/src/commands/link.ts +108 -0
- package/src/commands/list.ts +71 -0
- package/src/commands/outdated.ts +76 -0
- package/src/commands/search.ts +60 -0
- package/src/commands/uninstall.ts +73 -0
- package/src/commands/update.ts +112 -0
- package/src/commands/why.ts +105 -0
- package/src/conflicts.ts +84 -0
- package/src/index.ts +53 -0
- package/src/manifest.ts +212 -0
- package/src/migrate.ts +181 -0
- package/src/npm.ts +150 -0
- package/src/paths.ts +72 -0
- package/src/symlinks.ts +199 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
# Bump version across all packages
|
|
5
|
+
# Usage: ./scripts/bump-version.sh <version>
|
|
6
|
+
# Example: ./scripts/bump-version.sh 1.0.0
|
|
7
|
+
|
|
8
|
+
if [[ -z "$1" ]]; then
|
|
9
|
+
echo "Usage: $0 <version>"
|
|
10
|
+
echo "Example: $0 1.0.0"
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
VERSION="$1"
|
|
15
|
+
|
|
16
|
+
echo "📦 Bumping all packages to v$VERSION..."
|
|
17
|
+
|
|
18
|
+
# Update root package.json
|
|
19
|
+
echo " Updating package.json..."
|
|
20
|
+
bun --eval "
|
|
21
|
+
const pkg = require('./package.json');
|
|
22
|
+
pkg.version = '$VERSION';
|
|
23
|
+
require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, '\t') + '\n');
|
|
24
|
+
"
|
|
25
|
+
|
|
26
|
+
# Update plugins/subagents/package.json
|
|
27
|
+
echo " Updating plugins/subagents/package.json..."
|
|
28
|
+
bun --eval "
|
|
29
|
+
const pkg = require('./plugins/subagents/package.json');
|
|
30
|
+
pkg.version = '$VERSION';
|
|
31
|
+
require('fs').writeFileSync('plugins/subagents/package.json', JSON.stringify(pkg, null, 2) + '\n');
|
|
32
|
+
"
|
|
33
|
+
|
|
34
|
+
# Update plugins/metal-theme/package.json
|
|
35
|
+
echo " Updating plugins/metal-theme/package.json..."
|
|
36
|
+
bun --eval "
|
|
37
|
+
const pkg = require('./plugins/metal-theme/package.json');
|
|
38
|
+
pkg.version = '$VERSION';
|
|
39
|
+
require('fs').writeFileSync('plugins/metal-theme/package.json', JSON.stringify(pkg, null, 2) + '\n');
|
|
40
|
+
"
|
|
41
|
+
|
|
42
|
+
# Update version in CLI
|
|
43
|
+
echo " Updating src/cli.ts version..."
|
|
44
|
+
sed -i "s/\.version(\"[^\"]*\")/.version(\"$VERSION\")/" src/cli.ts
|
|
45
|
+
|
|
46
|
+
echo ""
|
|
47
|
+
echo "✅ All packages bumped to v$VERSION"
|
|
48
|
+
echo ""
|
|
49
|
+
echo "Next steps:"
|
|
50
|
+
echo " 1. git add -A && git commit -m 'chore: bump version to $VERSION'"
|
|
51
|
+
echo " 2. git tag v$VERSION"
|
|
52
|
+
echo " 3. git push && git push --tags"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
# Publish all @oh-my-pi packages
|
|
5
|
+
# Usage: ./scripts/publish.sh [--dry-run]
|
|
6
|
+
|
|
7
|
+
DRY_RUN=""
|
|
8
|
+
if [[ "$1" == "--dry-run" ]]; then
|
|
9
|
+
DRY_RUN="--dry-run"
|
|
10
|
+
echo "🔍 Dry run mode - no packages will be published"
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
echo "📦 Publishing @oh-my-pi packages..."
|
|
14
|
+
echo ""
|
|
15
|
+
|
|
16
|
+
# Build first
|
|
17
|
+
echo "🔨 Building CLI..."
|
|
18
|
+
bun run build
|
|
19
|
+
|
|
20
|
+
# Publish CLI
|
|
21
|
+
echo ""
|
|
22
|
+
echo "📤 Publishing @oh-my-pi/cli..."
|
|
23
|
+
npm publish --access public $DRY_RUN
|
|
24
|
+
|
|
25
|
+
# Publish plugins
|
|
26
|
+
echo ""
|
|
27
|
+
echo "📤 Publishing @oh-my-pi/subagents..."
|
|
28
|
+
cd plugins/subagents && npm publish --access public $DRY_RUN && cd ../..
|
|
29
|
+
|
|
30
|
+
echo ""
|
|
31
|
+
echo "📤 Publishing @oh-my-pi/metal-theme..."
|
|
32
|
+
cd plugins/metal-theme && npm publish --access public $DRY_RUN && cd ../..
|
|
33
|
+
|
|
34
|
+
echo ""
|
|
35
|
+
echo "✅ All packages published!"
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { program } from "commander";
|
|
4
|
+
import { createPlugin } from "./commands/create.js";
|
|
5
|
+
import { runDoctor } from "./commands/doctor.js";
|
|
6
|
+
import { disablePlugin, enablePlugin } from "./commands/enable.js";
|
|
7
|
+
import { showInfo } from "./commands/info.js";
|
|
8
|
+
import { initProject } from "./commands/init.js";
|
|
9
|
+
import { installPlugin } from "./commands/install.js";
|
|
10
|
+
import { linkPlugin } from "./commands/link.js";
|
|
11
|
+
import { listPlugins } from "./commands/list.js";
|
|
12
|
+
import { showOutdated } from "./commands/outdated.js";
|
|
13
|
+
import { searchPlugins } from "./commands/search.js";
|
|
14
|
+
import { uninstallPlugin } from "./commands/uninstall.js";
|
|
15
|
+
import { updatePlugin } from "./commands/update.js";
|
|
16
|
+
import { whyFile } from "./commands/why.js";
|
|
17
|
+
import { checkMigration, migrateToNpm } from "./migrate.js";
|
|
18
|
+
|
|
19
|
+
program.name("omp").description("Oh My Pi - Plugin manager for pi configuration").version("0.1.0");
|
|
20
|
+
|
|
21
|
+
// Check for migration on startup (only for commands that need it)
|
|
22
|
+
program.hook("preAction", async (thisCommand) => {
|
|
23
|
+
const migratingCommands = ["install", "uninstall", "update", "list", "link"];
|
|
24
|
+
if (migratingCommands.includes(thisCommand.name())) {
|
|
25
|
+
await checkMigration();
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// Core Commands
|
|
31
|
+
// ============================================================================
|
|
32
|
+
|
|
33
|
+
program
|
|
34
|
+
.command("install [packages...]")
|
|
35
|
+
.alias("i")
|
|
36
|
+
.description("Install plugin(s). No args = install from plugins.json")
|
|
37
|
+
.addHelpText(
|
|
38
|
+
"after",
|
|
39
|
+
`
|
|
40
|
+
Examples:
|
|
41
|
+
$ omp install @oh-my-pi/subagents # Install from npm
|
|
42
|
+
$ omp install @oh-my-pi/subagents@^2.0.0 # Specific version range
|
|
43
|
+
$ omp install @myorg/cool-theme # Scoped package
|
|
44
|
+
$ omp install ./local/path # Local directory (copies)
|
|
45
|
+
$ omp install # Install all from plugins.json
|
|
46
|
+
`,
|
|
47
|
+
)
|
|
48
|
+
.option("-g, --global", "Install globally to ~/.pi (default)")
|
|
49
|
+
.option("-S, --save", "Add to plugins.json")
|
|
50
|
+
.option("-D, --save-dev", "Add as dev dependency")
|
|
51
|
+
.option("--force", "Overwrite conflicts without prompting")
|
|
52
|
+
.option("--json", "Output as JSON")
|
|
53
|
+
.action(installPlugin);
|
|
54
|
+
|
|
55
|
+
program
|
|
56
|
+
.command("uninstall <name>")
|
|
57
|
+
.alias("rm")
|
|
58
|
+
.description("Remove plugin and its symlinks")
|
|
59
|
+
.option("-g, --global", "Uninstall from ~/.pi (default)")
|
|
60
|
+
.option("--json", "Output as JSON")
|
|
61
|
+
.action(uninstallPlugin);
|
|
62
|
+
|
|
63
|
+
program
|
|
64
|
+
.command("update [name]")
|
|
65
|
+
.alias("up")
|
|
66
|
+
.description("Update to latest within semver range")
|
|
67
|
+
.option("-g, --global", "Update global plugins (default)")
|
|
68
|
+
.option("--json", "Output as JSON")
|
|
69
|
+
.action(updatePlugin);
|
|
70
|
+
|
|
71
|
+
program
|
|
72
|
+
.command("list")
|
|
73
|
+
.alias("ls")
|
|
74
|
+
.description("Show installed plugins")
|
|
75
|
+
.option("-g, --global", "List global plugins (default)")
|
|
76
|
+
.option("--json", "Output as JSON")
|
|
77
|
+
.action(listPlugins);
|
|
78
|
+
|
|
79
|
+
program
|
|
80
|
+
.command("link <path>")
|
|
81
|
+
.description("Symlink local plugin (dev mode)")
|
|
82
|
+
.addHelpText(
|
|
83
|
+
"after",
|
|
84
|
+
`
|
|
85
|
+
Unlike install, link creates a symlink to the original directory,
|
|
86
|
+
so changes are reflected immediately without reinstalling.
|
|
87
|
+
`,
|
|
88
|
+
)
|
|
89
|
+
.option("-n, --name <name>", "Custom name for the plugin")
|
|
90
|
+
.option("-g, --global", "Link globally (default)")
|
|
91
|
+
.action(linkPlugin);
|
|
92
|
+
|
|
93
|
+
// ============================================================================
|
|
94
|
+
// New Commands
|
|
95
|
+
// ============================================================================
|
|
96
|
+
|
|
97
|
+
program
|
|
98
|
+
.command("init")
|
|
99
|
+
.description("Create .pi/plugins.json in current project")
|
|
100
|
+
.option("--force", "Overwrite existing plugins.json")
|
|
101
|
+
.action(initProject);
|
|
102
|
+
|
|
103
|
+
program
|
|
104
|
+
.command("search <query>")
|
|
105
|
+
.description("Search npm for omp-plugin keyword")
|
|
106
|
+
.option("--json", "Output as JSON")
|
|
107
|
+
.option("--limit <n>", "Maximum results to show", "20")
|
|
108
|
+
.action((query, options) => searchPlugins(query, { ...options, limit: parseInt(options.limit, 10) }));
|
|
109
|
+
|
|
110
|
+
program
|
|
111
|
+
.command("info <package>")
|
|
112
|
+
.description("Show plugin details before install")
|
|
113
|
+
.option("--json", "Output as JSON")
|
|
114
|
+
.option("--versions", "Show available versions")
|
|
115
|
+
.action(showInfo);
|
|
116
|
+
|
|
117
|
+
program
|
|
118
|
+
.command("outdated")
|
|
119
|
+
.description("List plugins with newer versions")
|
|
120
|
+
.option("-g, --global", "Check global plugins (default)")
|
|
121
|
+
.option("--json", "Output as JSON")
|
|
122
|
+
.action(showOutdated);
|
|
123
|
+
|
|
124
|
+
program
|
|
125
|
+
.command("doctor")
|
|
126
|
+
.description("Check for broken symlinks, conflicts")
|
|
127
|
+
.option("-g, --global", "Check global plugins (default)")
|
|
128
|
+
.option("--fix", "Attempt to fix issues")
|
|
129
|
+
.option("--json", "Output as JSON")
|
|
130
|
+
.action(runDoctor);
|
|
131
|
+
|
|
132
|
+
program
|
|
133
|
+
.command("create <name>")
|
|
134
|
+
.description("Scaffold new plugin from template")
|
|
135
|
+
.option("-d, --description <desc>", "Plugin description")
|
|
136
|
+
.option("-a, --author <author>", "Plugin author")
|
|
137
|
+
.action(createPlugin);
|
|
138
|
+
|
|
139
|
+
program
|
|
140
|
+
.command("why <file>")
|
|
141
|
+
.description("Show which plugin installed a file")
|
|
142
|
+
.option("-g, --global", "Check global plugins (default)")
|
|
143
|
+
.option("--json", "Output as JSON")
|
|
144
|
+
.action(whyFile);
|
|
145
|
+
|
|
146
|
+
program
|
|
147
|
+
.command("enable <name>")
|
|
148
|
+
.description("Enable a disabled plugin")
|
|
149
|
+
.option("-g, --global", "Target global plugins (default)")
|
|
150
|
+
.option("--json", "Output as JSON")
|
|
151
|
+
.action(enablePlugin);
|
|
152
|
+
|
|
153
|
+
program
|
|
154
|
+
.command("disable <name>")
|
|
155
|
+
.description("Disable plugin without uninstalling")
|
|
156
|
+
.option("-g, --global", "Target global plugins (default)")
|
|
157
|
+
.option("--json", "Output as JSON")
|
|
158
|
+
.action(disablePlugin);
|
|
159
|
+
|
|
160
|
+
program
|
|
161
|
+
.command("migrate")
|
|
162
|
+
.description("Migrate from legacy manifest.json to npm-native format")
|
|
163
|
+
.action(async () => {
|
|
164
|
+
await migrateToNpm();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
program.parse();
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
|
|
6
|
+
export interface CreateOptions {
|
|
7
|
+
description?: string;
|
|
8
|
+
author?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Scaffold a new plugin from template
|
|
13
|
+
*/
|
|
14
|
+
export async function createPlugin(name: string, options: CreateOptions = {}): Promise<void> {
|
|
15
|
+
// Ensure name follows conventions
|
|
16
|
+
const pluginName = name.startsWith("omp-") ? name : `omp-${name}`;
|
|
17
|
+
const pluginDir = pluginName;
|
|
18
|
+
|
|
19
|
+
if (existsSync(pluginDir)) {
|
|
20
|
+
console.log(chalk.red(`Error: Directory ${pluginDir} already exists`));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
console.log(chalk.blue(`Creating plugin: ${pluginName}...`));
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// Create directory structure
|
|
28
|
+
await mkdir(pluginDir, { recursive: true });
|
|
29
|
+
await mkdir(join(pluginDir, "agents"), { recursive: true });
|
|
30
|
+
await mkdir(join(pluginDir, "tools"), { recursive: true });
|
|
31
|
+
await mkdir(join(pluginDir, "themes"), { recursive: true });
|
|
32
|
+
await mkdir(join(pluginDir, "commands"), { recursive: true });
|
|
33
|
+
|
|
34
|
+
// Create package.json
|
|
35
|
+
const packageJson = {
|
|
36
|
+
name: pluginName,
|
|
37
|
+
version: "0.1.0",
|
|
38
|
+
description: options.description || `A pi plugin`,
|
|
39
|
+
keywords: ["omp-plugin"],
|
|
40
|
+
author: options.author || "",
|
|
41
|
+
license: "MIT",
|
|
42
|
+
omp: {
|
|
43
|
+
install: [],
|
|
44
|
+
},
|
|
45
|
+
files: ["agents", "tools", "themes", "commands"],
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
await writeFile(join(pluginDir, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
49
|
+
|
|
50
|
+
// Create README.md
|
|
51
|
+
const readme = `# ${pluginName}
|
|
52
|
+
|
|
53
|
+
${options.description || "A pi plugin."}
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
\`\`\`bash
|
|
58
|
+
omp install ${pluginName}
|
|
59
|
+
\`\`\`
|
|
60
|
+
|
|
61
|
+
## Contents
|
|
62
|
+
|
|
63
|
+
### Agents
|
|
64
|
+
|
|
65
|
+
Add agent markdown files to \`agents/\` directory.
|
|
66
|
+
|
|
67
|
+
### Tools
|
|
68
|
+
|
|
69
|
+
Add tool implementations to \`tools/\` directory.
|
|
70
|
+
|
|
71
|
+
### Themes
|
|
72
|
+
|
|
73
|
+
Add theme JSON files to \`themes/\` directory.
|
|
74
|
+
|
|
75
|
+
### Commands
|
|
76
|
+
|
|
77
|
+
Add command markdown files to \`commands/\` directory.
|
|
78
|
+
|
|
79
|
+
## Configuration
|
|
80
|
+
|
|
81
|
+
Edit \`package.json\` to configure which files are installed:
|
|
82
|
+
|
|
83
|
+
\`\`\`json
|
|
84
|
+
{
|
|
85
|
+
"omp": {
|
|
86
|
+
"install": [
|
|
87
|
+
{ "src": "agents/my-agent.md", "dest": "agent/agents/my-agent.md" },
|
|
88
|
+
{ "src": "tools/my-tool/", "dest": "agent/tools/my-tool/" }
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
\`\`\`
|
|
93
|
+
|
|
94
|
+
## Publishing
|
|
95
|
+
|
|
96
|
+
1. Update version in package.json
|
|
97
|
+
2. Run \`npm publish\`
|
|
98
|
+
|
|
99
|
+
Users can then install with: \`omp install ${pluginName}\`
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
MIT
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
await writeFile(join(pluginDir, "README.md"), readme);
|
|
107
|
+
|
|
108
|
+
// Create example agent
|
|
109
|
+
const exampleAgent = `# Example Agent
|
|
110
|
+
|
|
111
|
+
This is an example agent for ${pluginName}.
|
|
112
|
+
|
|
113
|
+
## Description
|
|
114
|
+
|
|
115
|
+
Describe what this agent does.
|
|
116
|
+
|
|
117
|
+
## Instructions
|
|
118
|
+
|
|
119
|
+
Provide instructions for the agent here.
|
|
120
|
+
`;
|
|
121
|
+
|
|
122
|
+
await writeFile(join(pluginDir, "agents", "example.md"), exampleAgent);
|
|
123
|
+
|
|
124
|
+
// Create .gitignore
|
|
125
|
+
const gitignore = `node_modules/
|
|
126
|
+
.DS_Store
|
|
127
|
+
*.log
|
|
128
|
+
`;
|
|
129
|
+
await writeFile(join(pluginDir, ".gitignore"), gitignore);
|
|
130
|
+
|
|
131
|
+
console.log(chalk.green(`\n✓ Created plugin at ${pluginDir}/`));
|
|
132
|
+
console.log();
|
|
133
|
+
console.log(chalk.dim("Directory structure:"));
|
|
134
|
+
console.log(chalk.dim(` ${pluginDir}/`));
|
|
135
|
+
console.log(chalk.dim(" ├── package.json"));
|
|
136
|
+
console.log(chalk.dim(" ├── README.md"));
|
|
137
|
+
console.log(chalk.dim(" ├── .gitignore"));
|
|
138
|
+
console.log(chalk.dim(" ├── agents/"));
|
|
139
|
+
console.log(chalk.dim(" │ └── example.md"));
|
|
140
|
+
console.log(chalk.dim(" ├── tools/"));
|
|
141
|
+
console.log(chalk.dim(" ├── themes/"));
|
|
142
|
+
console.log(chalk.dim(" └── commands/"));
|
|
143
|
+
console.log();
|
|
144
|
+
console.log(chalk.dim("Next steps:"));
|
|
145
|
+
console.log(chalk.dim(` 1. cd ${pluginDir}`));
|
|
146
|
+
console.log(chalk.dim(" 2. Add your agents, tools, themes, or commands"));
|
|
147
|
+
console.log(chalk.dim(" 3. Update omp.install in package.json"));
|
|
148
|
+
console.log(chalk.dim(" 4. Test locally: omp link ."));
|
|
149
|
+
console.log(chalk.dim(" 5. Publish: npm publish"));
|
|
150
|
+
} catch (err) {
|
|
151
|
+
console.log(chalk.red(`Error creating plugin: ${(err as Error).message}`));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { detectAllConflicts, formatConflicts } from "../conflicts.js";
|
|
4
|
+
import { getInstalledPlugins, loadPluginsJson, readPluginPackageJson } from "../manifest.js";
|
|
5
|
+
import { GLOBAL_PACKAGE_JSON, NODE_MODULES_DIR, PLUGINS_DIR } from "../paths.js";
|
|
6
|
+
import { checkPluginSymlinks } from "../symlinks.js";
|
|
7
|
+
|
|
8
|
+
export interface DoctorOptions {
|
|
9
|
+
global?: boolean;
|
|
10
|
+
fix?: boolean;
|
|
11
|
+
json?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface DiagnosticResult {
|
|
15
|
+
check: string;
|
|
16
|
+
status: "ok" | "warning" | "error";
|
|
17
|
+
message: string;
|
|
18
|
+
fix?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Run health checks on the plugin system
|
|
23
|
+
*/
|
|
24
|
+
export async function runDoctor(options: DoctorOptions = {}): Promise<void> {
|
|
25
|
+
const isGlobal = options.global !== false;
|
|
26
|
+
const results: DiagnosticResult[] = [];
|
|
27
|
+
|
|
28
|
+
console.log(chalk.blue("Running health checks...\n"));
|
|
29
|
+
|
|
30
|
+
// 1. Check plugins directory exists
|
|
31
|
+
const pluginsDir = isGlobal ? PLUGINS_DIR : ".pi";
|
|
32
|
+
if (!existsSync(pluginsDir)) {
|
|
33
|
+
results.push({
|
|
34
|
+
check: "Plugins directory",
|
|
35
|
+
status: "warning",
|
|
36
|
+
message: `${pluginsDir} does not exist`,
|
|
37
|
+
fix: "Run: omp install <package>",
|
|
38
|
+
});
|
|
39
|
+
} else {
|
|
40
|
+
results.push({
|
|
41
|
+
check: "Plugins directory",
|
|
42
|
+
status: "ok",
|
|
43
|
+
message: pluginsDir,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 2. Check package.json exists
|
|
48
|
+
const packageJsonPath = isGlobal ? GLOBAL_PACKAGE_JSON : ".pi/plugins.json";
|
|
49
|
+
if (!existsSync(packageJsonPath)) {
|
|
50
|
+
results.push({
|
|
51
|
+
check: "Package manifest",
|
|
52
|
+
status: "warning",
|
|
53
|
+
message: `${packageJsonPath} does not exist`,
|
|
54
|
+
fix: isGlobal ? "Run: omp install <package>" : "Run: omp init",
|
|
55
|
+
});
|
|
56
|
+
} else {
|
|
57
|
+
results.push({
|
|
58
|
+
check: "Package manifest",
|
|
59
|
+
status: "ok",
|
|
60
|
+
message: packageJsonPath,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 3. Check node_modules exists
|
|
65
|
+
const nodeModules = isGlobal ? NODE_MODULES_DIR : ".pi/node_modules";
|
|
66
|
+
if (!existsSync(nodeModules)) {
|
|
67
|
+
results.push({
|
|
68
|
+
check: "Node modules",
|
|
69
|
+
status: "warning",
|
|
70
|
+
message: `${nodeModules} does not exist`,
|
|
71
|
+
});
|
|
72
|
+
} else {
|
|
73
|
+
results.push({
|
|
74
|
+
check: "Node modules",
|
|
75
|
+
status: "ok",
|
|
76
|
+
message: nodeModules,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 4. Check each plugin's symlinks
|
|
81
|
+
const installedPlugins = await getInstalledPlugins(isGlobal);
|
|
82
|
+
const brokenSymlinks: string[] = [];
|
|
83
|
+
const missingSymlinks: string[] = [];
|
|
84
|
+
|
|
85
|
+
for (const [name, pkgJson] of installedPlugins) {
|
|
86
|
+
const symlinkStatus = await checkPluginSymlinks(name, pkgJson, isGlobal);
|
|
87
|
+
|
|
88
|
+
if (symlinkStatus.broken.length > 0) {
|
|
89
|
+
brokenSymlinks.push(...symlinkStatus.broken.map((s) => `${name}: ${s}`));
|
|
90
|
+
}
|
|
91
|
+
if (symlinkStatus.missing.length > 0) {
|
|
92
|
+
missingSymlinks.push(...symlinkStatus.missing.map((s) => `${name}: ${s}`));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (brokenSymlinks.length > 0) {
|
|
97
|
+
results.push({
|
|
98
|
+
check: "Broken symlinks",
|
|
99
|
+
status: "error",
|
|
100
|
+
message: `${brokenSymlinks.length} broken symlink(s)`,
|
|
101
|
+
fix: "Run: omp update <plugin> to re-create symlinks",
|
|
102
|
+
});
|
|
103
|
+
} else {
|
|
104
|
+
results.push({
|
|
105
|
+
check: "Symlinks",
|
|
106
|
+
status: "ok",
|
|
107
|
+
message: "All symlinks valid",
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (missingSymlinks.length > 0) {
|
|
112
|
+
results.push({
|
|
113
|
+
check: "Missing symlinks",
|
|
114
|
+
status: "warning",
|
|
115
|
+
message: `${missingSymlinks.length} expected symlink(s) not found`,
|
|
116
|
+
fix: "Run: omp update <plugin> to re-create symlinks",
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 5. Check for conflicts
|
|
121
|
+
const conflicts = detectAllConflicts(installedPlugins);
|
|
122
|
+
if (conflicts.length > 0) {
|
|
123
|
+
results.push({
|
|
124
|
+
check: "Conflicts",
|
|
125
|
+
status: "warning",
|
|
126
|
+
message: formatConflicts(conflicts).join("; "),
|
|
127
|
+
});
|
|
128
|
+
} else {
|
|
129
|
+
results.push({
|
|
130
|
+
check: "Conflicts",
|
|
131
|
+
status: "ok",
|
|
132
|
+
message: "No conflicts detected",
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 6. Check for orphaned entries in package.json
|
|
137
|
+
const pluginsJson = await loadPluginsJson(isGlobal);
|
|
138
|
+
const orphaned: string[] = [];
|
|
139
|
+
for (const name of Object.keys(pluginsJson.plugins)) {
|
|
140
|
+
const pkgJson = await readPluginPackageJson(name, isGlobal);
|
|
141
|
+
if (!pkgJson) {
|
|
142
|
+
orphaned.push(name);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (orphaned.length > 0) {
|
|
147
|
+
results.push({
|
|
148
|
+
check: "Orphaned entries",
|
|
149
|
+
status: "warning",
|
|
150
|
+
message: `${orphaned.length} plugin(s) in manifest but not in node_modules: ${orphaned.join(", ")}`,
|
|
151
|
+
fix: "Run: omp install (to reinstall) or remove from manifest",
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Output results
|
|
156
|
+
if (options.json) {
|
|
157
|
+
console.log(JSON.stringify({ results }, null, 2));
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
for (const result of results) {
|
|
162
|
+
let icon: string;
|
|
163
|
+
let color: typeof chalk;
|
|
164
|
+
|
|
165
|
+
switch (result.status) {
|
|
166
|
+
case "ok":
|
|
167
|
+
icon = "✓";
|
|
168
|
+
color = chalk.green;
|
|
169
|
+
break;
|
|
170
|
+
case "warning":
|
|
171
|
+
icon = "⚠";
|
|
172
|
+
color = chalk.yellow;
|
|
173
|
+
break;
|
|
174
|
+
case "error":
|
|
175
|
+
icon = "✗";
|
|
176
|
+
color = chalk.red;
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
console.log(color(`${icon} ${result.check}: `) + result.message);
|
|
181
|
+
|
|
182
|
+
if (result.fix && result.status !== "ok") {
|
|
183
|
+
console.log(chalk.dim(` ${result.fix}`));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Summary
|
|
188
|
+
const errors = results.filter((r) => r.status === "error");
|
|
189
|
+
const warnings = results.filter((r) => r.status === "warning");
|
|
190
|
+
|
|
191
|
+
console.log();
|
|
192
|
+
if (errors.length === 0 && warnings.length === 0) {
|
|
193
|
+
console.log(chalk.green("✓ All checks passed!"));
|
|
194
|
+
} else {
|
|
195
|
+
if (errors.length > 0) {
|
|
196
|
+
console.log(chalk.red(`${errors.length} error(s) found`));
|
|
197
|
+
}
|
|
198
|
+
if (warnings.length > 0) {
|
|
199
|
+
console.log(chalk.yellow(`${warnings.length} warning(s) found`));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Show broken symlinks details
|
|
204
|
+
if (brokenSymlinks.length > 0) {
|
|
205
|
+
console.log(chalk.red("\nBroken symlinks:"));
|
|
206
|
+
for (const s of brokenSymlinks) {
|
|
207
|
+
console.log(chalk.dim(` - ${s}`));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (missingSymlinks.length > 0) {
|
|
212
|
+
console.log(chalk.yellow("\nMissing symlinks:"));
|
|
213
|
+
for (const s of missingSymlinks) {
|
|
214
|
+
console.log(chalk.dim(` - ${s}`));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|