@oh-my-pi/cli 0.1.0 → 0.3.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 (105) hide show
  1. package/.github/icon.png +0 -0
  2. package/.github/logo.png +0 -0
  3. package/.github/workflows/publish.yml +1 -1
  4. package/LICENSE +21 -0
  5. package/README.md +243 -138
  6. package/biome.json +1 -1
  7. package/bun.lock +59 -0
  8. package/dist/cli.js +6311 -2900
  9. package/dist/commands/config.d.ts +32 -0
  10. package/dist/commands/config.d.ts.map +1 -0
  11. package/dist/commands/create.d.ts.map +1 -1
  12. package/dist/commands/doctor.d.ts +1 -0
  13. package/dist/commands/doctor.d.ts.map +1 -1
  14. package/dist/commands/enable.d.ts +1 -0
  15. package/dist/commands/enable.d.ts.map +1 -1
  16. package/dist/commands/env.d.ts +14 -0
  17. package/dist/commands/env.d.ts.map +1 -0
  18. package/dist/commands/features.d.ts +25 -0
  19. package/dist/commands/features.d.ts.map +1 -0
  20. package/dist/commands/info.d.ts +1 -0
  21. package/dist/commands/info.d.ts.map +1 -1
  22. package/dist/commands/init.d.ts.map +1 -1
  23. package/dist/commands/install.d.ts +37 -0
  24. package/dist/commands/install.d.ts.map +1 -1
  25. package/dist/commands/link.d.ts +2 -0
  26. package/dist/commands/link.d.ts.map +1 -1
  27. package/dist/commands/list.d.ts +1 -0
  28. package/dist/commands/list.d.ts.map +1 -1
  29. package/dist/commands/outdated.d.ts +1 -0
  30. package/dist/commands/outdated.d.ts.map +1 -1
  31. package/dist/commands/search.d.ts.map +1 -1
  32. package/dist/commands/uninstall.d.ts +1 -0
  33. package/dist/commands/uninstall.d.ts.map +1 -1
  34. package/dist/commands/update.d.ts +1 -0
  35. package/dist/commands/update.d.ts.map +1 -1
  36. package/dist/commands/why.d.ts +1 -0
  37. package/dist/commands/why.d.ts.map +1 -1
  38. package/dist/conflicts.d.ts +9 -1
  39. package/dist/conflicts.d.ts.map +1 -1
  40. package/dist/errors.d.ts +8 -0
  41. package/dist/errors.d.ts.map +1 -0
  42. package/dist/index.d.ts +18 -19
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/lock.d.ts +3 -0
  45. package/dist/lock.d.ts.map +1 -0
  46. package/dist/lockfile.d.ts +52 -0
  47. package/dist/lockfile.d.ts.map +1 -0
  48. package/dist/manifest.d.ts +60 -25
  49. package/dist/manifest.d.ts.map +1 -1
  50. package/dist/npm.d.ts +14 -2
  51. package/dist/npm.d.ts.map +1 -1
  52. package/dist/paths.d.ts +34 -3
  53. package/dist/paths.d.ts.map +1 -1
  54. package/dist/runtime.d.ts +14 -0
  55. package/dist/runtime.d.ts.map +1 -0
  56. package/dist/symlinks.d.ts +43 -7
  57. package/dist/symlinks.d.ts.map +1 -1
  58. package/package.json +11 -5
  59. package/plugins/exa/README.md +153 -0
  60. package/plugins/exa/package.json +56 -0
  61. package/plugins/exa/tools/exa/company.ts +35 -0
  62. package/plugins/exa/tools/exa/index.ts +66 -0
  63. package/plugins/exa/tools/exa/linkedin.ts +35 -0
  64. package/plugins/exa/tools/exa/researcher.ts +40 -0
  65. package/plugins/exa/tools/exa/runtime.json +4 -0
  66. package/plugins/exa/tools/exa/search.ts +46 -0
  67. package/plugins/exa/tools/exa/shared.ts +230 -0
  68. package/plugins/exa/tools/exa/websets.ts +62 -0
  69. package/plugins/metal-theme/package.json +7 -2
  70. package/plugins/subagents/package.json +7 -2
  71. package/plugins/user-prompt/README.md +130 -0
  72. package/plugins/user-prompt/package.json +19 -0
  73. package/plugins/user-prompt/tools/user-prompt/index.ts +235 -0
  74. package/src/cli.ts +133 -58
  75. package/src/commands/config.ts +384 -0
  76. package/src/commands/create.ts +51 -1
  77. package/src/commands/doctor.ts +95 -7
  78. package/src/commands/enable.ts +25 -8
  79. package/src/commands/env.ts +38 -0
  80. package/src/commands/features.ts +295 -0
  81. package/src/commands/info.ts +41 -5
  82. package/src/commands/init.ts +20 -2
  83. package/src/commands/install.ts +453 -80
  84. package/src/commands/link.ts +60 -9
  85. package/src/commands/list.ts +122 -7
  86. package/src/commands/outdated.ts +17 -6
  87. package/src/commands/search.ts +20 -3
  88. package/src/commands/uninstall.ts +57 -6
  89. package/src/commands/update.ts +67 -9
  90. package/src/commands/why.ts +47 -16
  91. package/src/conflicts.ts +33 -1
  92. package/src/errors.ts +22 -0
  93. package/src/index.ts +18 -25
  94. package/src/lock.ts +46 -0
  95. package/src/lockfile.ts +132 -0
  96. package/src/manifest.ts +219 -71
  97. package/src/npm.ts +74 -18
  98. package/src/paths.ts +77 -12
  99. package/src/runtime.ts +116 -0
  100. package/src/symlinks.ts +291 -35
  101. package/tsconfig.json +7 -3
  102. package/CHECK.md +0 -352
  103. package/dist/migrate.d.ts +0 -9
  104. package/dist/migrate.d.ts.map +0 -1
  105. package/src/migrate.ts +0 -181
package/dist/paths.d.ts CHANGED
@@ -3,15 +3,27 @@ export declare const PLUGINS_DIR: string;
3
3
  export declare const NODE_MODULES_DIR: string;
4
4
  export declare const GLOBAL_PACKAGE_JSON: string;
5
5
  export declare const GLOBAL_LOCK_FILE: string;
6
- export declare const LEGACY_MANIFEST_PATH: string;
7
6
  export declare const PROJECT_PI_DIR = ".pi";
8
7
  export declare const PROJECT_PLUGINS_JSON: string;
8
+ export declare const PROJECT_PACKAGE_JSON: string;
9
9
  export declare const PROJECT_PLUGINS_LOCK: string;
10
10
  export declare const PROJECT_NODE_MODULES: string;
11
11
  /**
12
- * Get the agent directory (where symlinks are installed)
12
+ * Find the project root by walking up parent directories looking for .pi/plugins.json.
13
+ * Similar to how git finds .git directories.
14
+ *
15
+ * @returns The absolute path to the project root, or null if not found
13
16
  */
14
- export declare function getAgentDir(global?: boolean): string;
17
+ export declare function findProjectRoot(): string | null;
18
+ /**
19
+ * Check if a project-local .pi/plugins.json exists in the current directory or any parent
20
+ */
21
+ export declare function hasProjectPlugins(): boolean;
22
+ /**
23
+ * Get the project .pi directory path.
24
+ * Uses findProjectRoot() to locate the project, or falls back to cwd.
25
+ */
26
+ export declare function getProjectPiDir(): string;
15
27
  /**
16
28
  * Get the plugins directory for the given scope
17
29
  */
@@ -24,4 +36,23 @@ export declare function getNodeModulesDir(global?: boolean): string;
24
36
  * Get the package.json path for the given scope
25
37
  */
26
38
  export declare function getPackageJsonPath(global?: boolean): string;
39
+ /**
40
+ * Get the agent directory (where symlinks are installed)
41
+ */
42
+ export declare function getAgentDir(global?: boolean): string;
43
+ /**
44
+ * Resolve whether to use global or local scope based on CLI flags and auto-detection.
45
+ *
46
+ * Logic:
47
+ * - If --global is passed: use global mode
48
+ * - If --local is passed: use local mode
49
+ * - If neither: check if .pi/plugins.json exists in cwd, if so use local, otherwise use global
50
+ *
51
+ * @param options - CLI options containing global and local flags
52
+ * @returns true if global scope should be used, false for local
53
+ */
54
+ export declare function resolveScope(options: {
55
+ global?: boolean;
56
+ local?: boolean;
57
+ }): boolean;
27
58
  //# sourceMappingURL=paths.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,aAAa,QAAyB,CAAC;AAGpD,eAAO,MAAM,WAAW,QAAiC,CAAC;AAG1D,eAAO,MAAM,gBAAgB,QAAoC,CAAC;AAGlE,eAAO,MAAM,mBAAmB,QAAoC,CAAC;AAGrE,eAAO,MAAM,gBAAgB,QAAyC,CAAC;AAGvE,eAAO,MAAM,oBAAoB,QAAqC,CAAC;AAGvE,eAAO,MAAM,cAAc,QAAQ,CAAC;AAGpC,eAAO,MAAM,oBAAoB,QAAuC,CAAC;AAGzE,eAAO,MAAM,oBAAoB,QAA4C,CAAC;AAG9E,eAAO,MAAM,oBAAoB,QAAuC,CAAC;AAEzE;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,UAAO,GAAG,MAAM,CAKjD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,UAAO,GAAG,MAAM,CAKnD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,UAAO,GAAG,MAAM,CAKvD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,UAAO,GAAG,MAAM,CAKxD"}
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,aAAa,QAAyB,CAAC;AAGpD,eAAO,MAAM,WAAW,QAAiC,CAAC;AAG1D,eAAO,MAAM,gBAAgB,QAAoC,CAAC;AAGlE,eAAO,MAAM,mBAAmB,QAAoC,CAAC;AAGrE,eAAO,MAAM,gBAAgB,QAAyC,CAAC;AAGvE,eAAO,MAAM,cAAc,QAAQ,CAAC;AAGpC,eAAO,MAAM,oBAAoB,QAAuC,CAAC;AAGzE,eAAO,MAAM,oBAAoB,QAAuC,CAAC;AAGzE,eAAO,MAAM,oBAAoB,QAA4C,CAAC;AAG9E,eAAO,MAAM,oBAAoB,QAAuC,CAAC;AAEzE;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,MAAM,GAAG,IAAI,CAc/C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAOxC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,UAAO,GAAG,MAAM,CAKnD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,UAAO,GAAG,MAAM,CAKvD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,UAAO,GAAG,MAAM,CAKxD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,UAAO,GAAG,MAAM,CAKjD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CASpF"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Get all environment variables for enabled plugins
3
+ */
4
+ export declare function getPluginEnvVars(global?: boolean): Promise<Record<string, string>>;
5
+ /**
6
+ * Generate shell export statements
7
+ * omp env > ~/.pi/env.sh && source ~/.pi/env.sh
8
+ */
9
+ export declare function generateEnvScript(global?: boolean, shell?: "sh" | "fish"): Promise<string>;
10
+ /**
11
+ * Get environment variables as a JSON object for programmatic use
12
+ */
13
+ export declare function getEnvJson(global?: boolean): Promise<Record<string, string>>;
14
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AA8CA;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,UAAO,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAiCrF;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,UAAO,EAAE,KAAK,GAAE,IAAI,GAAG,MAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAanG;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,MAAM,UAAO,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAE/E"}
@@ -1,18 +1,54 @@
1
- import type { OmpInstallEntry, PluginPackageJson } from "./manifest.js";
1
+ import type { OmpFeature, OmpInstallEntry, PluginPackageJson, PluginRuntimeConfig } from "@omp/manifest";
2
+ /**
3
+ * Get all install entries from package.json.
4
+ * Features no longer have install entries - all files are always installed.
5
+ */
6
+ export declare function getInstallEntries(pkgJson: PluginPackageJson): OmpInstallEntry[];
7
+ /**
8
+ * @deprecated Use getInstallEntries instead. Features no longer have install arrays.
9
+ */
10
+ export declare function getEnabledInstallEntries(pkgJson: PluginPackageJson, _enabledFeatures?: string[]): OmpInstallEntry[];
11
+ /**
12
+ * Get all available feature names from a plugin
13
+ */
14
+ export declare function getAllFeatureNames(pkgJson: PluginPackageJson): string[];
15
+ /**
16
+ * Get features that are enabled by default (default !== false)
17
+ */
18
+ export declare function getDefaultFeatures(features: Record<string, OmpFeature>): string[];
2
19
  export interface SymlinkResult {
3
20
  created: string[];
4
21
  errors: string[];
5
22
  }
23
+ export interface SymlinkRemovalResult {
24
+ removed: string[];
25
+ errors: string[];
26
+ skippedNonSymlinks: string[];
27
+ }
28
+ /**
29
+ * Create symlinks (or copy files with copy:true) for a plugin's omp.install entries
30
+ * @param skipDestinations - Set of destination paths to skip (e.g., due to conflict resolution)
31
+ * @param enabledFeatures - Features to write into runtime.json (if plugin has one)
32
+ */
33
+ export declare function createPluginSymlinks(pluginName: string, pkgJson: PluginPackageJson, global?: boolean, verbose?: boolean, skipDestinations?: Set<string>, enabledFeatures?: string[]): Promise<SymlinkResult>;
34
+ /**
35
+ * Read runtime.json config from a plugin's installed location
36
+ */
37
+ export declare function readRuntimeConfig(runtimePath: string): PluginRuntimeConfig;
38
+ /**
39
+ * Write runtime.json config to a plugin's installed location
40
+ */
41
+ export declare function writeRuntimeConfig(runtimePath: string, config: PluginRuntimeConfig, verbose?: boolean): Promise<void>;
6
42
  /**
7
- * Create symlinks for a plugin's omp.install entries
43
+ * Get the path to a plugin's runtime.json in the installed location
8
44
  */
9
- export declare function createPluginSymlinks(pluginName: string, pkgJson: PluginPackageJson, global?: boolean, verbose?: boolean): Promise<SymlinkResult>;
45
+ export declare function getRuntimeConfigPath(pkgJson: PluginPackageJson, global?: boolean): string | null;
10
46
  /**
11
- * Remove symlinks for a plugin's omp.install entries
47
+ * Remove symlinks and copied files for a plugin's omp.install entries
12
48
  */
13
- export declare function removePluginSymlinks(_pluginName: string, pkgJson: PluginPackageJson, verbose?: boolean): Promise<SymlinkResult>;
49
+ export declare function removePluginSymlinks(_pluginName: string, pkgJson: PluginPackageJson, global?: boolean, verbose?: boolean): Promise<SymlinkRemovalResult>;
14
50
  /**
15
- * Check symlink health for a plugin
51
+ * Check symlink/file health for a plugin
16
52
  */
17
53
  export declare function checkPluginSymlinks(pluginName: string, pkgJson: PluginPackageJson, global?: boolean): Promise<{
18
54
  valid: string[];
@@ -26,7 +62,7 @@ export declare function getPluginForSymlink(dest: string, installedPlugins: Map<
26
62
  /**
27
63
  * Find all symlinks installed by plugins and trace them back
28
64
  */
29
- export declare function traceInstalledFile(filePath: string, installedPlugins: Map<string, PluginPackageJson>): Promise<{
65
+ export declare function traceInstalledFile(filePath: string, installedPlugins: Map<string, PluginPackageJson>, global?: boolean): Promise<{
30
66
  plugin: string;
31
67
  entry: OmpInstallEntry;
32
68
  } | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"symlinks.d.ts","sourceRoot":"","sources":["../src/symlinks.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAIxE,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACzC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,iBAAiB,EAC1B,MAAM,UAAO,EACb,OAAO,UAAO,GACZ,OAAO,CAAC,aAAa,CAAC,CAkDxB;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACzC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,iBAAiB,EAC1B,OAAO,UAAO,GACZ,OAAO,CAAC,aAAa,CAAC,CA4BxB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACxC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,iBAAiB,EAC1B,MAAM,UAAO,GACX,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAoCnE;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACxC,IAAI,EAAE,MAAM,EACZ,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAC9C,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAWxB;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACvC,QAAQ,EAAE,MAAM,EAChB,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAC9C,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,eAAe,CAAA;CAAE,GAAG,IAAI,CAAC,CAkB5D"}
1
+ {"version":3,"file":"symlinks.d.ts","sourceRoot":"","sources":["../src/symlinks.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAKzG;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,GAAG,eAAe,EAAE,CAE/E;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACvC,OAAO,EAAE,iBAAiB,EAC1B,gBAAgB,CAAC,EAAE,MAAM,EAAE,GACzB,eAAe,EAAE,CAEnB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,EAAE,CAEvE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,MAAM,EAAE,CAIjF;AAgCD,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACpC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACzC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,iBAAiB,EAC1B,MAAM,UAAO,EACb,OAAO,UAAO,EACd,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAC9B,eAAe,CAAC,EAAE,MAAM,EAAE,GACxB,OAAO,CAAC,aAAa,CAAC,CAwHxB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,mBAAmB,CAO1E;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACvC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,mBAAmB,EAC3B,OAAO,UAAQ,GACb,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAO,GAAG,MAAM,GAAG,IAAI,CAK7F;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACzC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,iBAAiB,EAC1B,MAAM,UAAO,EACb,OAAO,UAAO,GACZ,OAAO,CAAC,oBAAoB,CAAC,CAkE/B;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACxC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,iBAAiB,EAC1B,MAAM,UAAO,GACX,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAwDnE;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACxC,IAAI,EAAE,MAAM,EACZ,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAC9C,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAWxB;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACvC,QAAQ,EAAE,MAAM,EAChB,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,EAChD,MAAM,UAAO,GACX,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,eAAe,CAAA;CAAE,GAAG,IAAI,CAAC,CAmB5D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/cli",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Plugin manager for pi - install and manage pi config plugins from git repos",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -11,15 +11,20 @@
11
11
  "scripts": {
12
12
  "build": "bun build src/cli.ts --outdir dist --target bun && tsc --emitDeclarationOnly",
13
13
  "dev": "bun --watch src/cli.ts",
14
- "check": "biome check --write . && tsc --noEmit",
14
+ "lint": "biome check --write .",
15
+ "check": "bun run lint && tsc --noEmit",
15
16
  "clean": "rm -rf dist",
16
17
  "publish:all": "./scripts/publish.sh",
17
18
  "publish:dry": "./scripts/publish.sh --dry-run",
18
19
  "version:bump": "./scripts/bump-version.sh"
19
20
  },
20
21
  "keywords": ["pi", "plugin", "manager", "cli"],
21
- "author": "",
22
+ "author": "Can Bölük <me@can.ac>",
22
23
  "license": "MIT",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/can1357/oh-my-pi.git"
27
+ },
23
28
  "devDependencies": {
24
29
  "@biomejs/biome": "2.3.5",
25
30
  "@types/node": "^22.10.5",
@@ -27,8 +32,9 @@
27
32
  "typescript": "^5.9.2"
28
33
  },
29
34
  "dependencies": {
30
- "commander": "^12.0.0",
31
- "chalk": "^5.3.0"
35
+ "@inquirer/prompts": "^8.1.0",
36
+ "chalk": "^5.3.0",
37
+ "commander": "^12.0.0"
32
38
  },
33
39
  "engines": {
34
40
  "bun": ">=1.0.0"
@@ -0,0 +1,153 @@
1
+ # Exa Plugin
2
+
3
+ Exa AI web search and websets tools for pi.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ # Install with default features (search only)
9
+ omp install @oh-my-pi/exa
10
+
11
+ # Install with all features
12
+ omp install @oh-my-pi/exa[*]
13
+
14
+ # Install with specific features
15
+ omp install @oh-my-pi/exa[search,linkedin,websets]
16
+ ```
17
+
18
+ ## Features
19
+
20
+ | Feature | Default | Description | Tools |
21
+ |---------|---------|-------------|-------|
22
+ | `search` | ✓ | Core web search capabilities | 4 tools |
23
+ | `linkedin` | | LinkedIn profile and company search | 1 tool |
24
+ | `company` | | Comprehensive company research | 1 tool |
25
+ | `researcher` | | Long-running AI research tasks | 2 tools |
26
+ | `websets` | | Entity collection management | 14 tools |
27
+
28
+ Manage features after install:
29
+
30
+ ```bash
31
+ omp features @oh-my-pi/exa # Interactive UI or list features
32
+ omp features @oh-my-pi/exa --enable websets # Enable websets
33
+ omp features @oh-my-pi/exa --disable search # Disable search
34
+ omp features @oh-my-pi/exa --set search,linkedin,websets # Set exact features
35
+ ```
36
+
37
+ Feature state is stored in `runtime.json` which is copied (not symlinked) to the install location. You can also edit it directly:
38
+
39
+ ```bash
40
+ cat ~/.pi/agent/tools/exa/runtime.json
41
+ # {"features": ["search"], "options": {}}
42
+ ```
43
+
44
+ ## Setup
45
+
46
+ Set your Exa API key:
47
+
48
+ ```bash
49
+ # Option 1: Use omp config
50
+ omp config @oh-my-pi/exa apiKey YOUR_API_KEY
51
+
52
+ # Option 2: Environment variable
53
+ export EXA_API_KEY=YOUR_API_KEY
54
+
55
+ # Option 3: .env file in current directory or ~/.env
56
+ echo "EXA_API_KEY=YOUR_API_KEY" >> ~/.env
57
+ ```
58
+
59
+ Get your API key from: https://dashboard.exa.ai/api-keys
60
+
61
+ ## Tools
62
+
63
+ ### search (default)
64
+
65
+ | Tool | Description |
66
+ |------|-------------|
67
+ | `web_search_general` | Real-time web searches with content extraction |
68
+ | `web_search_deep` | Natural language web search with synthesized results |
69
+ | `web_search_code_context` | Search code snippets, docs, and examples |
70
+ | `web_search_crawl_url` | Extract content from specific URLs |
71
+
72
+ ### linkedin
73
+
74
+ | Tool | Description |
75
+ |------|-------------|
76
+ | `web_search_linkedin` | Search LinkedIn profiles and companies |
77
+
78
+ ### company
79
+
80
+ | Tool | Description |
81
+ |------|-------------|
82
+ | `web_search_company_research` | Comprehensive company research |
83
+
84
+ ### researcher
85
+
86
+ | Tool | Description |
87
+ |------|-------------|
88
+ | `web_search_researcher_start` | Start comprehensive AI-powered research task |
89
+ | `web_search_researcher_check` | Check research task status and get results |
90
+
91
+ ### websets
92
+
93
+ | Tool | Description |
94
+ |------|-------------|
95
+ | `webset_create` | Create entity collections with search and enrichments |
96
+ | `webset_list` | List all websets in your account |
97
+ | `webset_get` | Get detailed webset information |
98
+ | `webset_update` | Update webset metadata |
99
+ | `webset_delete` | Delete a webset |
100
+ | `webset_items_list` | List items in a webset |
101
+ | `webset_item_get` | Get item details |
102
+ | `webset_search_create` | Add search to find entities for a webset |
103
+ | `webset_search_get` | Check search status |
104
+ | `webset_search_cancel` | Cancel running search |
105
+ | `webset_enrichment_create` | Extract custom data from webset items |
106
+ | `webset_enrichment_get` | Get enrichment details |
107
+ | `webset_enrichment_update` | Update enrichment metadata |
108
+ | `webset_enrichment_delete` | Delete enrichment |
109
+ | `webset_enrichment_cancel` | Cancel running enrichment |
110
+ | `webset_monitor_create` | Auto-update webset on schedule |
111
+
112
+ ## Usage Examples
113
+
114
+ ### Code Search
115
+ ```
116
+ Find examples of how to use React hooks with TypeScript
117
+ ```
118
+
119
+ ### Web Search
120
+ ```
121
+ Search for the latest news about AI regulation in the EU
122
+ ```
123
+
124
+ ### Company Research (requires company feature)
125
+ ```
126
+ Research the company OpenAI and find information about their products
127
+ ```
128
+
129
+ ### Deep Research (requires researcher feature)
130
+ ```
131
+ Start a deep research project on the impact of large language models on software development
132
+ ```
133
+
134
+ ### Websets (requires websets feature)
135
+ ```
136
+ Create a webset of AI startups in San Francisco founded after 2020,
137
+ find 10 companies and enrich with CEO name and funding amount
138
+ ```
139
+
140
+ ## How It Works
141
+
142
+ The plugin connects to Exa's hosted MCP (Model Context Protocol) servers:
143
+ - `https://mcp.exa.ai/mcp` - Search tools
144
+ - `https://websetsmcp.exa.ai/mcp` - Websets tools
145
+
146
+ Tools are dynamically fetched from these servers, so you always get the latest available tools.
147
+
148
+ ## Resources
149
+
150
+ - [Exa Dashboard](https://dashboard.exa.ai/)
151
+ - [Exa MCP Documentation](https://docs.exa.ai/reference/exa-mcp)
152
+ - [Websets MCP Documentation](https://docs.exa.ai/reference/websets-mcp)
153
+ - [Exa API Documentation](https://docs.exa.ai/)
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@oh-my-pi/exa",
3
+ "version": "0.3.0",
4
+ "description": "Exa AI web search and websets tools for pi",
5
+ "keywords": ["omp-plugin", "exa", "web-search", "websets", "ai-search"],
6
+ "author": "Can Bölük <me@can.ac>",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/can1357/oh-my-pi.git",
11
+ "directory": "plugins/exa"
12
+ },
13
+ "omp": {
14
+ "install": [
15
+ { "src": "tools/exa/runtime.json", "dest": "agent/tools/exa/runtime.json", "copy": true },
16
+ { "src": "tools/exa/index.ts", "dest": "agent/tools/exa/index.ts" },
17
+ { "src": "tools/exa/shared.ts", "dest": "agent/tools/exa/shared.ts" },
18
+ { "src": "tools/exa/search.ts", "dest": "agent/tools/exa/search.ts" },
19
+ { "src": "tools/exa/linkedin.ts", "dest": "agent/tools/exa/linkedin.ts" },
20
+ { "src": "tools/exa/company.ts", "dest": "agent/tools/exa/company.ts" },
21
+ { "src": "tools/exa/researcher.ts", "dest": "agent/tools/exa/researcher.ts" },
22
+ { "src": "tools/exa/websets.ts", "dest": "agent/tools/exa/websets.ts" }
23
+ ],
24
+ "variables": {
25
+ "apiKey": {
26
+ "type": "string",
27
+ "env": "EXA_API_KEY",
28
+ "description": "Exa API key for authentication",
29
+ "required": true
30
+ }
31
+ },
32
+ "features": {
33
+ "search": {
34
+ "description": "Core web search (general, deep, code context, URL crawling)",
35
+ "default": true
36
+ },
37
+ "linkedin": {
38
+ "description": "LinkedIn profile and company search",
39
+ "default": false
40
+ },
41
+ "company": {
42
+ "description": "Comprehensive company research",
43
+ "default": false
44
+ },
45
+ "researcher": {
46
+ "description": "Long-running AI research tasks",
47
+ "default": false
48
+ },
49
+ "websets": {
50
+ "description": "Entity collection management (14 tools)",
51
+ "default": false
52
+ }
53
+ }
54
+ },
55
+ "files": ["tools"]
56
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Exa Company Research Tool
3
+ *
4
+ * Tools:
5
+ * - web_search_company_research: Comprehensive company research
6
+ */
7
+
8
+ import type { TSchema } from "@sinclair/typebox";
9
+ import type { CustomAgentTool, CustomToolFactory, ToolAPI } from "@mariozechner/pi-coding-agent";
10
+ import { callExaTool, createToolWrapper, fetchExaTools, findApiKey } from "./shared";
11
+
12
+ // MCP tool names for this feature
13
+ const TOOL_NAMES = ["company_research_exa"];
14
+
15
+ // Tool name mapping: MCP name -> exposed name
16
+ const NAME_MAP: Record<string, string> = {
17
+ "company_research_exa": "web_search_company_research",
18
+ };
19
+
20
+ const factory: CustomToolFactory = async (_toolApi: ToolAPI): Promise<CustomAgentTool<TSchema, unknown>[] | null> => {
21
+ const apiKey = findApiKey();
22
+ if (!apiKey) return null;
23
+
24
+ const mcpTools = await fetchExaTools(apiKey, TOOL_NAMES);
25
+ if (mcpTools.length === 0) return null;
26
+
27
+ const callFn = (toolName: string, args: Record<string, unknown>) =>
28
+ callExaTool(apiKey, TOOL_NAMES, toolName, args);
29
+
30
+ return mcpTools.map((tool) =>
31
+ createToolWrapper(tool, NAME_MAP[tool.name] ?? tool.name, callFn)
32
+ );
33
+ };
34
+
35
+ export default factory;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Exa Tools - Dynamic loader for feature modules
3
+ *
4
+ * Reads runtime.json to determine which features are enabled,
5
+ * then loads and initializes those feature modules.
6
+ *
7
+ * Available features:
8
+ * - search: Core web search (general, deep, code context, URL crawling)
9
+ * - linkedin: LinkedIn profile and company search
10
+ * - company: Comprehensive company research
11
+ * - researcher: Long-running AI research tasks
12
+ * - websets: Entity collection management
13
+ */
14
+
15
+ import type { TSchema } from "@sinclair/typebox";
16
+ import type { CustomAgentTool, CustomToolFactory, ToolAPI } from "@mariozechner/pi-coding-agent";
17
+ import runtime from "./runtime.json";
18
+
19
+ // Map feature names to their module imports
20
+ const FEATURE_LOADERS: Record<string, () => Promise<{ default: CustomToolFactory }>> = {
21
+ search: () => import("./search"),
22
+ linkedin: () => import("./linkedin"),
23
+ company: () => import("./company"),
24
+ researcher: () => import("./researcher"),
25
+ websets: () => import("./websets"),
26
+ };
27
+
28
+ /**
29
+ * Factory function that loads enabled features from runtime.json
30
+ */
31
+ const factory: CustomToolFactory = async (toolApi: ToolAPI): Promise<CustomAgentTool<TSchema, unknown>[] | null> => {
32
+ const allTools: CustomAgentTool<TSchema, unknown>[] = [];
33
+ const enabledFeatures = runtime.features ?? [];
34
+
35
+ for (const feature of enabledFeatures) {
36
+ const loader = FEATURE_LOADERS[feature];
37
+ if (!loader) {
38
+ console.error(`Unknown exa feature: "${feature}"`);
39
+ continue;
40
+ }
41
+
42
+ try {
43
+ const module = await loader();
44
+ const featureFactory = module.default;
45
+
46
+ if (typeof featureFactory === "function") {
47
+ const result = await featureFactory(toolApi);
48
+ // Handle both single tool and array of tools
49
+ if (result) {
50
+ const tools = Array.isArray(result) ? result : [result];
51
+ for (const tool of tools) {
52
+ if (tool && typeof tool === "object" && "name" in tool) {
53
+ allTools.push(tool);
54
+ }
55
+ }
56
+ }
57
+ }
58
+ } catch (error) {
59
+ console.error(`Failed to load exa feature "${feature}":`, error);
60
+ }
61
+ }
62
+
63
+ return allTools.length > 0 ? allTools : null;
64
+ };
65
+
66
+ export default factory;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Exa LinkedIn Search Tool
3
+ *
4
+ * Tools:
5
+ * - web_search_linkedin: Search LinkedIn profiles and companies
6
+ */
7
+
8
+ import type { TSchema } from "@sinclair/typebox";
9
+ import type { CustomAgentTool, CustomToolFactory, ToolAPI } from "@mariozechner/pi-coding-agent";
10
+ import { callExaTool, createToolWrapper, fetchExaTools, findApiKey } from "./shared";
11
+
12
+ // MCP tool names for this feature
13
+ const TOOL_NAMES = ["linkedin_search_exa"];
14
+
15
+ // Tool name mapping: MCP name -> exposed name
16
+ const NAME_MAP: Record<string, string> = {
17
+ "linkedin_search_exa": "web_search_linkedin",
18
+ };
19
+
20
+ const factory: CustomToolFactory = async (_toolApi: ToolAPI): Promise<CustomAgentTool<TSchema, unknown>[] | null> => {
21
+ const apiKey = findApiKey();
22
+ if (!apiKey) return null;
23
+
24
+ const mcpTools = await fetchExaTools(apiKey, TOOL_NAMES);
25
+ if (mcpTools.length === 0) return null;
26
+
27
+ const callFn = (toolName: string, args: Record<string, unknown>) =>
28
+ callExaTool(apiKey, TOOL_NAMES, toolName, args);
29
+
30
+ return mcpTools.map((tool) =>
31
+ createToolWrapper(tool, NAME_MAP[tool.name] ?? tool.name, callFn)
32
+ );
33
+ };
34
+
35
+ export default factory;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Exa Deep Researcher Tools
3
+ *
4
+ * Tools:
5
+ * - web_search_researcher_start: Start comprehensive AI research tasks
6
+ * - web_search_researcher_check: Check research task status
7
+ */
8
+
9
+ import type { TSchema } from "@sinclair/typebox";
10
+ import type { CustomAgentTool, CustomToolFactory, ToolAPI } from "@mariozechner/pi-coding-agent";
11
+ import { callExaTool, createToolWrapper, fetchExaTools, findApiKey } from "./shared";
12
+
13
+ // MCP tool names for this feature
14
+ const TOOL_NAMES = [
15
+ "deep_researcher_start",
16
+ "deep_researcher_check",
17
+ ];
18
+
19
+ // Tool name mapping: MCP name -> exposed name
20
+ const NAME_MAP: Record<string, string> = {
21
+ "deep_researcher_start": "web_search_researcher_start",
22
+ "deep_researcher_check": "web_search_researcher_check",
23
+ };
24
+
25
+ const factory: CustomToolFactory = async (_toolApi: ToolAPI): Promise<CustomAgentTool<TSchema, unknown>[] | null> => {
26
+ const apiKey = findApiKey();
27
+ if (!apiKey) return null;
28
+
29
+ const mcpTools = await fetchExaTools(apiKey, TOOL_NAMES);
30
+ if (mcpTools.length === 0) return null;
31
+
32
+ const callFn = (toolName: string, args: Record<string, unknown>) =>
33
+ callExaTool(apiKey, TOOL_NAMES, toolName, args);
34
+
35
+ return mcpTools.map((tool) =>
36
+ createToolWrapper(tool, NAME_MAP[tool.name] ?? tool.name, callFn)
37
+ );
38
+ };
39
+
40
+ export default factory;
@@ -0,0 +1,4 @@
1
+ {
2
+ "features": ["search"],
3
+ "options": {}
4
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Exa Search Tools - Core web search capabilities
3
+ *
4
+ * Tools:
5
+ * - web_search_general: Real-time web searches
6
+ * - web_search_deep: Natural language web search with synthesis
7
+ * - web_search_code_context: Code search for libraries, docs, examples
8
+ * - web_search_crawl_url: Extract content from specific URLs
9
+ */
10
+
11
+ import type { TSchema } from "@sinclair/typebox";
12
+ import type { CustomAgentTool, CustomToolFactory, ToolAPI } from "@mariozechner/pi-coding-agent";
13
+ import { callExaTool, createToolWrapper, fetchExaTools, findApiKey } from "./shared";
14
+
15
+ // MCP tool names for this feature
16
+ const TOOL_NAMES = [
17
+ "web_search_exa",
18
+ "deep_search_exa",
19
+ "get_code_context_exa",
20
+ "crawling_exa",
21
+ ];
22
+
23
+ // Tool name mapping: MCP name -> exposed name
24
+ const NAME_MAP: Record<string, string> = {
25
+ "web_search_exa": "web_search_general",
26
+ "deep_search_exa": "web_search_deep",
27
+ "get_code_context_exa": "web_search_code_context",
28
+ "crawling_exa": "web_search_crawl_url",
29
+ };
30
+
31
+ const factory: CustomToolFactory = async (_toolApi: ToolAPI): Promise<CustomAgentTool<TSchema, unknown>[] | null> => {
32
+ const apiKey = findApiKey();
33
+ if (!apiKey) return null;
34
+
35
+ const mcpTools = await fetchExaTools(apiKey, TOOL_NAMES);
36
+ if (mcpTools.length === 0) return null;
37
+
38
+ const callFn = (toolName: string, args: Record<string, unknown>) =>
39
+ callExaTool(apiKey, TOOL_NAMES, toolName, args);
40
+
41
+ return mcpTools.map((tool) =>
42
+ createToolWrapper(tool, NAME_MAP[tool.name] ?? tool.name, callFn)
43
+ );
44
+ };
45
+
46
+ export default factory;