@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.
Files changed (89) hide show
  1. package/.github/workflows/ci.yml +32 -0
  2. package/.github/workflows/publish.yml +42 -0
  3. package/CHECK.md +352 -0
  4. package/README.md +224 -0
  5. package/biome.json +29 -0
  6. package/bun.lock +50 -0
  7. package/dist/cli.d.ts +3 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +3941 -0
  10. package/dist/commands/create.d.ts +9 -0
  11. package/dist/commands/create.d.ts.map +1 -0
  12. package/dist/commands/doctor.d.ts +10 -0
  13. package/dist/commands/doctor.d.ts.map +1 -0
  14. package/dist/commands/enable.d.ts +13 -0
  15. package/dist/commands/enable.d.ts.map +1 -0
  16. package/dist/commands/info.d.ts +9 -0
  17. package/dist/commands/info.d.ts.map +1 -0
  18. package/dist/commands/init.d.ts +8 -0
  19. package/dist/commands/init.d.ts.map +1 -0
  20. package/dist/commands/install.d.ts +13 -0
  21. package/dist/commands/install.d.ts.map +1 -0
  22. package/dist/commands/link.d.ts +10 -0
  23. package/dist/commands/link.d.ts.map +1 -0
  24. package/dist/commands/list.d.ts +9 -0
  25. package/dist/commands/list.d.ts.map +1 -0
  26. package/dist/commands/outdated.d.ts +9 -0
  27. package/dist/commands/outdated.d.ts.map +1 -0
  28. package/dist/commands/search.d.ts +9 -0
  29. package/dist/commands/search.d.ts.map +1 -0
  30. package/dist/commands/uninstall.d.ts +9 -0
  31. package/dist/commands/uninstall.d.ts.map +1 -0
  32. package/dist/commands/update.d.ts +9 -0
  33. package/dist/commands/update.d.ts.map +1 -0
  34. package/dist/commands/why.d.ts +9 -0
  35. package/dist/commands/why.d.ts.map +1 -0
  36. package/dist/conflicts.d.ts +21 -0
  37. package/dist/conflicts.d.ts.map +1 -0
  38. package/dist/index.d.ts +20 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/manifest.d.ts +81 -0
  41. package/dist/manifest.d.ts.map +1 -0
  42. package/dist/migrate.d.ts +9 -0
  43. package/dist/migrate.d.ts.map +1 -0
  44. package/dist/npm.d.ts +77 -0
  45. package/dist/npm.d.ts.map +1 -0
  46. package/dist/paths.d.ts +27 -0
  47. package/dist/paths.d.ts.map +1 -0
  48. package/dist/symlinks.d.ts +33 -0
  49. package/dist/symlinks.d.ts.map +1 -0
  50. package/package.json +36 -0
  51. package/plugins/metal-theme/README.md +13 -0
  52. package/plugins/metal-theme/omp.json +8 -0
  53. package/plugins/metal-theme/package.json +14 -0
  54. package/plugins/metal-theme/themes/metal.json +79 -0
  55. package/plugins/subagents/README.md +25 -0
  56. package/plugins/subagents/agents/explore.md +71 -0
  57. package/plugins/subagents/agents/planner.md +51 -0
  58. package/plugins/subagents/agents/reviewer.md +53 -0
  59. package/plugins/subagents/agents/task.md +46 -0
  60. package/plugins/subagents/commands/architect-plan.md +9 -0
  61. package/plugins/subagents/commands/implement-with-critic.md +10 -0
  62. package/plugins/subagents/commands/implement.md +10 -0
  63. package/plugins/subagents/omp.json +15 -0
  64. package/plugins/subagents/package.json +21 -0
  65. package/plugins/subagents/tools/task/index.ts +1019 -0
  66. package/scripts/bump-version.sh +52 -0
  67. package/scripts/publish.sh +35 -0
  68. package/src/cli.ts +167 -0
  69. package/src/commands/create.ts +153 -0
  70. package/src/commands/doctor.ts +217 -0
  71. package/src/commands/enable.ts +105 -0
  72. package/src/commands/info.ts +84 -0
  73. package/src/commands/init.ts +42 -0
  74. package/src/commands/install.ts +327 -0
  75. package/src/commands/link.ts +108 -0
  76. package/src/commands/list.ts +71 -0
  77. package/src/commands/outdated.ts +76 -0
  78. package/src/commands/search.ts +60 -0
  79. package/src/commands/uninstall.ts +73 -0
  80. package/src/commands/update.ts +112 -0
  81. package/src/commands/why.ts +105 -0
  82. package/src/conflicts.ts +84 -0
  83. package/src/index.ts +53 -0
  84. package/src/manifest.ts +212 -0
  85. package/src/migrate.ts +181 -0
  86. package/src/npm.ts +150 -0
  87. package/src/paths.ts +72 -0
  88. package/src/symlinks.ts +199 -0
  89. package/tsconfig.json +24 -0
@@ -0,0 +1,212 @@
1
+ import { existsSync } from "node:fs";
2
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import {
5
+ GLOBAL_PACKAGE_JSON,
6
+ LEGACY_MANIFEST_PATH,
7
+ NODE_MODULES_DIR,
8
+ PLUGINS_DIR,
9
+ PROJECT_PLUGINS_JSON,
10
+ } from "./paths.js";
11
+
12
+ /**
13
+ * OMP field in package.json - defines what files to install
14
+ */
15
+ export interface OmpInstallEntry {
16
+ src: string;
17
+ dest: string;
18
+ }
19
+
20
+ export interface OmpField {
21
+ install?: OmpInstallEntry[];
22
+ disabled?: boolean;
23
+ }
24
+
25
+ /**
26
+ * Package.json structure for plugins
27
+ */
28
+ export interface PluginPackageJson {
29
+ name: string;
30
+ version: string;
31
+ description?: string;
32
+ keywords?: string[];
33
+ omp?: OmpField;
34
+ dependencies?: Record<string, string>;
35
+ devDependencies?: Record<string, string>;
36
+ files?: string[];
37
+ }
38
+
39
+ /**
40
+ * Global/project plugins.json structure
41
+ */
42
+ export interface PluginsJson {
43
+ plugins: Record<string, string>; // name -> version specifier
44
+ disabled?: string[]; // disabled plugin names
45
+ }
46
+
47
+ /**
48
+ * Legacy manifest structure (for migration)
49
+ */
50
+ export interface LegacyPluginInfo {
51
+ type: "github" | "local" | "npm";
52
+ repo?: string;
53
+ package?: string;
54
+ path?: string;
55
+ subdir?: string;
56
+ version?: string;
57
+ linked?: boolean;
58
+ installed: string[];
59
+ installedAt: string;
60
+ }
61
+
62
+ export interface LegacyManifest {
63
+ plugins: Record<string, LegacyPluginInfo>;
64
+ }
65
+
66
+ /**
67
+ * Initialize the global plugins directory with package.json
68
+ */
69
+ export async function initGlobalPlugins(): Promise<void> {
70
+ await mkdir(PLUGINS_DIR, { recursive: true });
71
+
72
+ if (!existsSync(GLOBAL_PACKAGE_JSON)) {
73
+ const packageJson = {
74
+ name: "pi-plugins",
75
+ version: "1.0.0",
76
+ private: true,
77
+ description: "Global pi plugins managed by omp",
78
+ dependencies: {},
79
+ };
80
+ await writeFile(GLOBAL_PACKAGE_JSON, JSON.stringify(packageJson, null, 2));
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Load plugins.json (global or project)
86
+ */
87
+ export async function loadPluginsJson(global = true): Promise<PluginsJson> {
88
+ const path = global ? GLOBAL_PACKAGE_JSON : PROJECT_PLUGINS_JSON;
89
+
90
+ try {
91
+ const data = await readFile(path, "utf-8");
92
+ const parsed = JSON.parse(data);
93
+
94
+ if (global) {
95
+ // Global uses standard package.json format
96
+ return {
97
+ plugins: parsed.dependencies || {},
98
+ disabled: parsed.omp?.disabled || [],
99
+ };
100
+ }
101
+
102
+ // Project uses plugins.json format
103
+ return {
104
+ plugins: parsed.plugins || {},
105
+ disabled: parsed.disabled || [],
106
+ };
107
+ } catch (err) {
108
+ if ((err as NodeJS.ErrnoException).code === "ENOENT") {
109
+ return { plugins: {}, disabled: [] };
110
+ }
111
+ throw err;
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Save plugins.json (global or project)
117
+ */
118
+ export async function savePluginsJson(data: PluginsJson, global = true): Promise<void> {
119
+ const path = global ? GLOBAL_PACKAGE_JSON : PROJECT_PLUGINS_JSON;
120
+ await mkdir(dirname(path), { recursive: true });
121
+
122
+ if (global) {
123
+ // Read existing package.json and update dependencies
124
+ let existing: Record<string, unknown> = {};
125
+ try {
126
+ existing = JSON.parse(await readFile(path, "utf-8"));
127
+ } catch {
128
+ existing = {
129
+ name: "pi-plugins",
130
+ version: "1.0.0",
131
+ private: true,
132
+ description: "Global pi plugins managed by omp",
133
+ };
134
+ }
135
+
136
+ existing.dependencies = data.plugins;
137
+ if (data.disabled?.length) {
138
+ existing.omp = { ...((existing.omp as Record<string, unknown>) || {}), disabled: data.disabled };
139
+ }
140
+
141
+ await writeFile(path, JSON.stringify(existing, null, 2));
142
+ } else {
143
+ // Project uses simple plugins.json format
144
+ await writeFile(path, JSON.stringify(data, null, 2));
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Read a plugin's package.json from node_modules
150
+ */
151
+ export async function readPluginPackageJson(pluginName: string, global = true): Promise<PluginPackageJson | null> {
152
+ const nodeModules = global ? NODE_MODULES_DIR : ".pi/node_modules";
153
+ let pkgPath: string;
154
+
155
+ // Handle scoped packages
156
+ if (pluginName.startsWith("@")) {
157
+ pkgPath = join(nodeModules, pluginName, "package.json");
158
+ } else {
159
+ pkgPath = join(nodeModules, pluginName, "package.json");
160
+ }
161
+
162
+ try {
163
+ const data = await readFile(pkgPath, "utf-8");
164
+ return JSON.parse(data) as PluginPackageJson;
165
+ } catch {
166
+ return null;
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Get the source directory for a plugin in node_modules
172
+ */
173
+ export function getPluginSourceDir(pluginName: string, global = true): string {
174
+ const nodeModules = global ? NODE_MODULES_DIR : ".pi/node_modules";
175
+ return join(nodeModules, pluginName);
176
+ }
177
+
178
+ /**
179
+ * Check if legacy manifest.json exists
180
+ */
181
+ export function hasLegacyManifest(): boolean {
182
+ return existsSync(LEGACY_MANIFEST_PATH);
183
+ }
184
+
185
+ /**
186
+ * Load legacy manifest.json
187
+ */
188
+ export async function loadLegacyManifest(): Promise<LegacyManifest> {
189
+ try {
190
+ const data = await readFile(LEGACY_MANIFEST_PATH, "utf-8");
191
+ return JSON.parse(data) as LegacyManifest;
192
+ } catch {
193
+ return { plugins: {} };
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Get all installed plugins with their info
199
+ */
200
+ export async function getInstalledPlugins(global = true): Promise<Map<string, PluginPackageJson>> {
201
+ const pluginsJson = await loadPluginsJson(global);
202
+ const plugins = new Map<string, PluginPackageJson>();
203
+
204
+ for (const name of Object.keys(pluginsJson.plugins)) {
205
+ const pkgJson = await readPluginPackageJson(name, global);
206
+ if (pkgJson) {
207
+ plugins.set(name, pkgJson);
208
+ }
209
+ }
210
+
211
+ return plugins;
212
+ }
package/src/migrate.ts ADDED
@@ -0,0 +1,181 @@
1
+ import { existsSync } from "node:fs";
2
+ import { mkdir, readFile, rename, rm, symlink, writeFile } from "node:fs/promises";
3
+ import { basename, join } from "node:path";
4
+ import { createInterface } from "node:readline";
5
+ import chalk from "chalk";
6
+ import {
7
+ hasLegacyManifest,
8
+ type LegacyPluginInfo,
9
+ loadLegacyManifest,
10
+ type PluginPackageJson,
11
+ type PluginsJson,
12
+ savePluginsJson,
13
+ } from "./manifest.js";
14
+ import { LEGACY_MANIFEST_PATH, NODE_MODULES_DIR, PLUGINS_DIR } from "./paths.js";
15
+
16
+ /**
17
+ * Prompt user for migration
18
+ */
19
+ async function promptMigration(): Promise<boolean> {
20
+ const rl = createInterface({
21
+ input: process.stdin,
22
+ output: process.stdout,
23
+ });
24
+
25
+ return new Promise((resolve) => {
26
+ rl.question(chalk.yellow("Migrate to npm-native format? [y/N] "), (answer) => {
27
+ rl.close();
28
+ resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
29
+ });
30
+ });
31
+ }
32
+
33
+ /**
34
+ * Check for and prompt migration if legacy manifest exists
35
+ */
36
+ export async function checkMigration(auto = false): Promise<boolean> {
37
+ if (!hasLegacyManifest()) {
38
+ return false;
39
+ }
40
+
41
+ console.log(chalk.yellow("\n⚠ Legacy manifest.json detected"));
42
+ console.log(chalk.dim(" oh-my-pi has been updated to use npm-native plugin management."));
43
+ console.log();
44
+
45
+ if (!auto) {
46
+ const shouldMigrate = await promptMigration();
47
+ if (!shouldMigrate) {
48
+ console.log(chalk.dim(" Migration skipped. Use 'omp migrate' to migrate later."));
49
+ return false;
50
+ }
51
+ }
52
+
53
+ return await migrateToNpm();
54
+ }
55
+
56
+ /**
57
+ * Migrate from legacy manifest.json to npm-native format
58
+ */
59
+ export async function migrateToNpm(): Promise<boolean> {
60
+ console.log(chalk.blue("\nMigrating to npm-native format..."));
61
+
62
+ try {
63
+ const legacyManifest = await loadLegacyManifest();
64
+ const plugins = Object.entries(legacyManifest.plugins);
65
+
66
+ if (plugins.length === 0) {
67
+ console.log(chalk.dim(" No plugins to migrate"));
68
+ await archiveLegacyManifest();
69
+ return true;
70
+ }
71
+
72
+ // Create node_modules directory
73
+ await mkdir(NODE_MODULES_DIR, { recursive: true });
74
+
75
+ const newPluginsJson: PluginsJson = { plugins: {} };
76
+ const migrated: string[] = [];
77
+ const failed: string[] = [];
78
+
79
+ for (const [name, info] of plugins) {
80
+ try {
81
+ console.log(chalk.dim(` Migrating ${name}...`));
82
+ await migratePlugin(name, info);
83
+
84
+ // Determine version specifier for plugins.json
85
+ if (info.type === "npm" && info.package) {
86
+ newPluginsJson.plugins[info.package] = info.version ? `^${info.version}` : "latest";
87
+ } else if (info.type === "local" && info.path) {
88
+ newPluginsJson.plugins[name] = `file:${info.path}`;
89
+ } else if (info.type === "github" && info.repo) {
90
+ // GitHub plugins become local after clone
91
+ newPluginsJson.plugins[name] = `file:${join(NODE_MODULES_DIR, name)}`;
92
+ }
93
+
94
+ migrated.push(name);
95
+ } catch (err) {
96
+ console.log(chalk.yellow(` ⚠ Failed to migrate ${name}: ${(err as Error).message}`));
97
+ failed.push(name);
98
+ }
99
+ }
100
+
101
+ // Save new plugins.json
102
+ await savePluginsJson(newPluginsJson, true);
103
+
104
+ // Archive legacy manifest
105
+ await archiveLegacyManifest();
106
+
107
+ console.log();
108
+ console.log(chalk.green(`✓ Migrated ${migrated.length} plugin(s)`));
109
+ if (failed.length > 0) {
110
+ console.log(chalk.yellow(`⚠ Failed to migrate ${failed.length} plugin(s): ${failed.join(", ")}`));
111
+ }
112
+
113
+ return true;
114
+ } catch (err) {
115
+ console.log(chalk.red(`Error during migration: ${(err as Error).message}`));
116
+ return false;
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Migrate a single plugin to the new structure
122
+ */
123
+ async function migratePlugin(name: string, info: LegacyPluginInfo): Promise<void> {
124
+ const oldPluginDir = join(PLUGINS_DIR, name);
125
+ const newPluginDir = join(NODE_MODULES_DIR, name);
126
+
127
+ if (!existsSync(oldPluginDir)) {
128
+ throw new Error(`Plugin directory not found: ${oldPluginDir}`);
129
+ }
130
+
131
+ // For linked plugins, create symlink in node_modules
132
+ if (info.linked && info.path) {
133
+ await mkdir(NODE_MODULES_DIR, { recursive: true });
134
+ if (existsSync(newPluginDir)) {
135
+ await rm(newPluginDir, { force: true, recursive: true });
136
+ }
137
+ await symlink(info.path, newPluginDir);
138
+ // Remove old symlink
139
+ await rm(oldPluginDir, { force: true });
140
+ return;
141
+ }
142
+
143
+ // For regular plugins, move to node_modules
144
+ if (existsSync(newPluginDir)) {
145
+ await rm(newPluginDir, { force: true, recursive: true });
146
+ }
147
+
148
+ // Rename/move the directory
149
+ await rename(oldPluginDir, newPluginDir);
150
+
151
+ // Create package.json if it doesn't exist (convert from omp.json)
152
+ const pkgJsonPath = join(newPluginDir, "package.json");
153
+ const ompJsonPath = join(newPluginDir, "omp.json");
154
+
155
+ if (!existsSync(pkgJsonPath) && existsSync(ompJsonPath)) {
156
+ const ompJson = JSON.parse(await readFile(ompJsonPath, "utf-8"));
157
+ const pkgJson: PluginPackageJson = {
158
+ name: ompJson.name || name,
159
+ version: ompJson.version || info.version || "0.0.0",
160
+ description: ompJson.description,
161
+ keywords: ["omp-plugin"],
162
+ omp: {
163
+ install: ompJson.install,
164
+ },
165
+ };
166
+ await writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Archive the legacy manifest.json
172
+ */
173
+ async function archiveLegacyManifest(): Promise<void> {
174
+ if (!existsSync(LEGACY_MANIFEST_PATH)) {
175
+ return;
176
+ }
177
+
178
+ const archivePath = join(PLUGINS_DIR, `manifest.json.bak.${Date.now()}`);
179
+ await rename(LEGACY_MANIFEST_PATH, archivePath);
180
+ console.log(chalk.dim(` Archived old manifest to ${basename(archivePath)}`));
181
+ }
package/src/npm.ts ADDED
@@ -0,0 +1,150 @@
1
+ import { execSync } from "node:child_process";
2
+
3
+ export interface NpmPackageInfo {
4
+ name: string;
5
+ version: string;
6
+ description?: string;
7
+ keywords?: string[];
8
+ author?: string | { name: string; email?: string };
9
+ homepage?: string;
10
+ repository?: { type: string; url: string } | string;
11
+ versions?: string[];
12
+ "dist-tags"?: Record<string, string>;
13
+ }
14
+
15
+ export interface NpmSearchResult {
16
+ name: string;
17
+ version: string;
18
+ description?: string;
19
+ keywords?: string[];
20
+ date?: string;
21
+ author?: { name: string };
22
+ }
23
+
24
+ /**
25
+ * Execute npm command and return output
26
+ */
27
+ export function npmExec(args: string[], cwd?: string): string {
28
+ const cmd = `npm ${args.join(" ")}`;
29
+ return execSync(cmd, {
30
+ cwd,
31
+ stdio: ["pipe", "pipe", "pipe"],
32
+ encoding: "utf-8",
33
+ });
34
+ }
35
+
36
+ /**
37
+ * Execute npm command with prefix (for installing to specific directory)
38
+ */
39
+ export function npmExecWithPrefix(args: string[], prefix: string): string {
40
+ return npmExec(["--prefix", prefix, ...args]);
41
+ }
42
+
43
+ /**
44
+ * Install packages using npm
45
+ */
46
+ export async function npmInstall(
47
+ packages: string[],
48
+ prefix: string,
49
+ options: { save?: boolean; saveDev?: boolean } = {},
50
+ ): Promise<void> {
51
+ const args = ["install"];
52
+
53
+ if (options.save) {
54
+ args.push("--save");
55
+ } else if (options.saveDev) {
56
+ args.push("--save-dev");
57
+ }
58
+
59
+ args.push(...packages);
60
+
61
+ npmExecWithPrefix(args, prefix);
62
+ }
63
+
64
+ /**
65
+ * Uninstall packages using npm
66
+ */
67
+ export async function npmUninstall(packages: string[], prefix: string): Promise<void> {
68
+ npmExecWithPrefix(["uninstall", ...packages], prefix);
69
+ }
70
+
71
+ /**
72
+ * Get package info from npm registry
73
+ */
74
+ export async function npmInfo(packageName: string): Promise<NpmPackageInfo | null> {
75
+ try {
76
+ const output = npmExec(["info", packageName, "--json"]);
77
+ return JSON.parse(output);
78
+ } catch {
79
+ return null;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Search npm for packages with a keyword
85
+ */
86
+ export async function npmSearch(query: string, keyword = "omp-plugin"): Promise<NpmSearchResult[]> {
87
+ try {
88
+ // Search for packages with the omp-plugin keyword
89
+ const searchTerm = keyword ? `keywords:${keyword} ${query}` : query;
90
+ const output = npmExec(["search", searchTerm, "--json"]);
91
+ return JSON.parse(output);
92
+ } catch {
93
+ return [];
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Check for outdated packages
99
+ */
100
+ export async function npmOutdated(
101
+ prefix: string,
102
+ ): Promise<Record<string, { current: string; wanted: string; latest: string }>> {
103
+ try {
104
+ const output = npmExecWithPrefix(["outdated", "--json"], prefix);
105
+ return JSON.parse(output);
106
+ } catch (err) {
107
+ // npm outdated exits with code 1 if there are outdated packages
108
+ const error = err as { stdout?: string };
109
+ if (error.stdout) {
110
+ try {
111
+ return JSON.parse(error.stdout);
112
+ } catch {
113
+ return {};
114
+ }
115
+ }
116
+ return {};
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Update packages using npm
122
+ */
123
+ export async function npmUpdate(packages: string[], prefix: string): Promise<void> {
124
+ const args = ["update"];
125
+ if (packages.length > 0) {
126
+ args.push(...packages);
127
+ }
128
+ npmExecWithPrefix(args, prefix);
129
+ }
130
+
131
+ /**
132
+ * Get list of installed packages
133
+ */
134
+ export async function npmList(prefix: string): Promise<Record<string, { version: string }>> {
135
+ try {
136
+ const output = npmExecWithPrefix(["list", "--json", "--depth=0"], prefix);
137
+ const parsed = JSON.parse(output);
138
+ return parsed.dependencies || {};
139
+ } catch {
140
+ return {};
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Resolve a package version from the registry
146
+ */
147
+ export async function resolveVersion(packageName: string, versionRange = "latest"): Promise<string | null> {
148
+ const info = await npmInfo(`${packageName}@${versionRange}`);
149
+ return info?.version || null;
150
+ }
package/src/paths.ts ADDED
@@ -0,0 +1,72 @@
1
+ import { homedir } from "node:os";
2
+ import { join } from "node:path";
3
+
4
+ // Global pi configuration directory
5
+ export const PI_CONFIG_DIR = join(homedir(), ".pi");
6
+
7
+ // Global plugins directory
8
+ export const PLUGINS_DIR = join(PI_CONFIG_DIR, "plugins");
9
+
10
+ // npm node_modules within plugins directory
11
+ export const NODE_MODULES_DIR = join(PLUGINS_DIR, "node_modules");
12
+
13
+ // Global package.json for plugin management
14
+ export const GLOBAL_PACKAGE_JSON = join(PLUGINS_DIR, "package.json");
15
+
16
+ // Global package-lock.json
17
+ export const GLOBAL_LOCK_FILE = join(PLUGINS_DIR, "package-lock.json");
18
+
19
+ // Legacy manifest (for migration)
20
+ export const LEGACY_MANIFEST_PATH = join(PLUGINS_DIR, "manifest.json");
21
+
22
+ // Project-local config directory
23
+ export const PROJECT_PI_DIR = ".pi";
24
+
25
+ // Project-local plugins.json
26
+ export const PROJECT_PLUGINS_JSON = join(PROJECT_PI_DIR, "plugins.json");
27
+
28
+ // Project-local lock file
29
+ export const PROJECT_PLUGINS_LOCK = join(PROJECT_PI_DIR, "plugins-lock.json");
30
+
31
+ // Project-local node_modules
32
+ export const PROJECT_NODE_MODULES = join(PROJECT_PI_DIR, "node_modules");
33
+
34
+ /**
35
+ * Get the agent directory (where symlinks are installed)
36
+ */
37
+ export function getAgentDir(global = true): string {
38
+ if (global) {
39
+ return join(PI_CONFIG_DIR, "agent");
40
+ }
41
+ return join(PROJECT_PI_DIR, "agent");
42
+ }
43
+
44
+ /**
45
+ * Get the plugins directory for the given scope
46
+ */
47
+ export function getPluginsDir(global = true): string {
48
+ if (global) {
49
+ return PLUGINS_DIR;
50
+ }
51
+ return PROJECT_PI_DIR;
52
+ }
53
+
54
+ /**
55
+ * Get the node_modules directory for the given scope
56
+ */
57
+ export function getNodeModulesDir(global = true): string {
58
+ if (global) {
59
+ return NODE_MODULES_DIR;
60
+ }
61
+ return PROJECT_NODE_MODULES;
62
+ }
63
+
64
+ /**
65
+ * Get the package.json path for the given scope
66
+ */
67
+ export function getPackageJsonPath(global = true): string {
68
+ if (global) {
69
+ return GLOBAL_PACKAGE_JSON;
70
+ }
71
+ return PROJECT_PLUGINS_JSON;
72
+ }