@oh-my-pi/pi-coding-agent 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 (337) hide show
  1. package/CHANGELOG.md +1629 -0
  2. package/README.md +1041 -0
  3. package/docs/compaction.md +403 -0
  4. package/docs/config-usage.md +113 -0
  5. package/docs/custom-tools.md +541 -0
  6. package/docs/extension-loading.md +1004 -0
  7. package/docs/hooks.md +867 -0
  8. package/docs/rpc.md +1040 -0
  9. package/docs/sdk.md +994 -0
  10. package/docs/session-tree-plan.md +441 -0
  11. package/docs/session.md +240 -0
  12. package/docs/skills.md +290 -0
  13. package/docs/theme.md +670 -0
  14. package/docs/tree.md +197 -0
  15. package/docs/tui.md +341 -0
  16. package/examples/README.md +21 -0
  17. package/examples/custom-tools/README.md +124 -0
  18. package/examples/custom-tools/hello/index.ts +20 -0
  19. package/examples/custom-tools/question/index.ts +84 -0
  20. package/examples/custom-tools/subagent/README.md +172 -0
  21. package/examples/custom-tools/subagent/agents/planner.md +37 -0
  22. package/examples/custom-tools/subagent/agents/scout.md +50 -0
  23. package/examples/custom-tools/subagent/agents/worker.md +24 -0
  24. package/examples/custom-tools/subagent/agents.ts +156 -0
  25. package/examples/custom-tools/subagent/commands/implement-and-review.md +10 -0
  26. package/examples/custom-tools/subagent/commands/implement.md +10 -0
  27. package/examples/custom-tools/subagent/commands/scout-and-plan.md +9 -0
  28. package/examples/custom-tools/subagent/index.ts +1002 -0
  29. package/examples/custom-tools/todo/index.ts +212 -0
  30. package/examples/hooks/README.md +56 -0
  31. package/examples/hooks/auto-commit-on-exit.ts +49 -0
  32. package/examples/hooks/confirm-destructive.ts +59 -0
  33. package/examples/hooks/custom-compaction.ts +116 -0
  34. package/examples/hooks/dirty-repo-guard.ts +52 -0
  35. package/examples/hooks/file-trigger.ts +41 -0
  36. package/examples/hooks/git-checkpoint.ts +53 -0
  37. package/examples/hooks/handoff.ts +150 -0
  38. package/examples/hooks/permission-gate.ts +34 -0
  39. package/examples/hooks/protected-paths.ts +30 -0
  40. package/examples/hooks/qna.ts +119 -0
  41. package/examples/hooks/snake.ts +343 -0
  42. package/examples/hooks/status-line.ts +40 -0
  43. package/examples/sdk/01-minimal.ts +22 -0
  44. package/examples/sdk/02-custom-model.ts +49 -0
  45. package/examples/sdk/03-custom-prompt.ts +44 -0
  46. package/examples/sdk/04-skills.ts +44 -0
  47. package/examples/sdk/05-tools.ts +90 -0
  48. package/examples/sdk/06-hooks.ts +61 -0
  49. package/examples/sdk/07-context-files.ts +36 -0
  50. package/examples/sdk/08-slash-commands.ts +42 -0
  51. package/examples/sdk/09-api-keys-and-oauth.ts +55 -0
  52. package/examples/sdk/10-settings.ts +38 -0
  53. package/examples/sdk/11-sessions.ts +48 -0
  54. package/examples/sdk/12-full-control.ts +95 -0
  55. package/examples/sdk/README.md +154 -0
  56. package/package.json +89 -0
  57. package/src/bun-imports.d.ts +16 -0
  58. package/src/capability/context-file.ts +40 -0
  59. package/src/capability/extension.ts +48 -0
  60. package/src/capability/hook.ts +40 -0
  61. package/src/capability/index.ts +616 -0
  62. package/src/capability/instruction.ts +37 -0
  63. package/src/capability/mcp.ts +52 -0
  64. package/src/capability/prompt.ts +35 -0
  65. package/src/capability/rule.ts +56 -0
  66. package/src/capability/settings.ts +35 -0
  67. package/src/capability/skill.ts +49 -0
  68. package/src/capability/slash-command.ts +40 -0
  69. package/src/capability/system-prompt.ts +35 -0
  70. package/src/capability/tool.ts +38 -0
  71. package/src/capability/types.ts +166 -0
  72. package/src/cli/args.ts +259 -0
  73. package/src/cli/file-processor.ts +121 -0
  74. package/src/cli/list-models.ts +104 -0
  75. package/src/cli/plugin-cli.ts +661 -0
  76. package/src/cli/session-picker.ts +41 -0
  77. package/src/cli/update-cli.ts +274 -0
  78. package/src/cli.ts +10 -0
  79. package/src/config.ts +391 -0
  80. package/src/core/agent-session.ts +2178 -0
  81. package/src/core/auth-storage.ts +258 -0
  82. package/src/core/bash-executor.ts +197 -0
  83. package/src/core/compaction/branch-summarization.ts +315 -0
  84. package/src/core/compaction/compaction.ts +664 -0
  85. package/src/core/compaction/index.ts +7 -0
  86. package/src/core/compaction/utils.ts +153 -0
  87. package/src/core/custom-commands/bundled/review/index.ts +156 -0
  88. package/src/core/custom-commands/index.ts +15 -0
  89. package/src/core/custom-commands/loader.ts +226 -0
  90. package/src/core/custom-commands/types.ts +112 -0
  91. package/src/core/custom-tools/index.ts +22 -0
  92. package/src/core/custom-tools/loader.ts +248 -0
  93. package/src/core/custom-tools/types.ts +185 -0
  94. package/src/core/custom-tools/wrapper.ts +29 -0
  95. package/src/core/exec.ts +139 -0
  96. package/src/core/export-html/index.ts +159 -0
  97. package/src/core/export-html/template.css +774 -0
  98. package/src/core/export-html/template.generated.ts +2 -0
  99. package/src/core/export-html/template.html +45 -0
  100. package/src/core/export-html/template.js +1185 -0
  101. package/src/core/export-html/template.macro.ts +24 -0
  102. package/src/core/file-mentions.ts +54 -0
  103. package/src/core/hooks/index.ts +16 -0
  104. package/src/core/hooks/loader.ts +288 -0
  105. package/src/core/hooks/runner.ts +434 -0
  106. package/src/core/hooks/tool-wrapper.ts +98 -0
  107. package/src/core/hooks/types.ts +770 -0
  108. package/src/core/index.ts +53 -0
  109. package/src/core/logger.ts +112 -0
  110. package/src/core/mcp/client.ts +185 -0
  111. package/src/core/mcp/config.ts +248 -0
  112. package/src/core/mcp/index.ts +45 -0
  113. package/src/core/mcp/loader.ts +99 -0
  114. package/src/core/mcp/manager.ts +235 -0
  115. package/src/core/mcp/tool-bridge.ts +156 -0
  116. package/src/core/mcp/transports/http.ts +316 -0
  117. package/src/core/mcp/transports/index.ts +6 -0
  118. package/src/core/mcp/transports/stdio.ts +252 -0
  119. package/src/core/mcp/types.ts +228 -0
  120. package/src/core/messages.ts +211 -0
  121. package/src/core/model-registry.ts +334 -0
  122. package/src/core/model-resolver.ts +494 -0
  123. package/src/core/plugins/doctor.ts +67 -0
  124. package/src/core/plugins/index.ts +38 -0
  125. package/src/core/plugins/installer.ts +189 -0
  126. package/src/core/plugins/loader.ts +339 -0
  127. package/src/core/plugins/manager.ts +672 -0
  128. package/src/core/plugins/parser.ts +105 -0
  129. package/src/core/plugins/paths.ts +37 -0
  130. package/src/core/plugins/types.ts +190 -0
  131. package/src/core/sdk.ts +900 -0
  132. package/src/core/session-manager.ts +1837 -0
  133. package/src/core/settings-manager.ts +860 -0
  134. package/src/core/skills.ts +352 -0
  135. package/src/core/slash-commands.ts +132 -0
  136. package/src/core/system-prompt.ts +442 -0
  137. package/src/core/timings.ts +25 -0
  138. package/src/core/title-generator.ts +110 -0
  139. package/src/core/tools/ask.ts +193 -0
  140. package/src/core/tools/bash-interceptor.ts +120 -0
  141. package/src/core/tools/bash.ts +91 -0
  142. package/src/core/tools/context.ts +32 -0
  143. package/src/core/tools/edit-diff.ts +487 -0
  144. package/src/core/tools/edit.ts +140 -0
  145. package/src/core/tools/exa/company.ts +59 -0
  146. package/src/core/tools/exa/index.ts +63 -0
  147. package/src/core/tools/exa/linkedin.ts +59 -0
  148. package/src/core/tools/exa/mcp-client.ts +368 -0
  149. package/src/core/tools/exa/render.ts +200 -0
  150. package/src/core/tools/exa/researcher.ts +90 -0
  151. package/src/core/tools/exa/search.ts +338 -0
  152. package/src/core/tools/exa/types.ts +167 -0
  153. package/src/core/tools/exa/websets.ts +248 -0
  154. package/src/core/tools/find.ts +244 -0
  155. package/src/core/tools/grep.ts +584 -0
  156. package/src/core/tools/index.ts +283 -0
  157. package/src/core/tools/ls.ts +142 -0
  158. package/src/core/tools/lsp/client.ts +767 -0
  159. package/src/core/tools/lsp/clients/biome-client.ts +207 -0
  160. package/src/core/tools/lsp/clients/index.ts +49 -0
  161. package/src/core/tools/lsp/clients/lsp-linter-client.ts +98 -0
  162. package/src/core/tools/lsp/config.ts +845 -0
  163. package/src/core/tools/lsp/edits.ts +110 -0
  164. package/src/core/tools/lsp/index.ts +1364 -0
  165. package/src/core/tools/lsp/render.ts +560 -0
  166. package/src/core/tools/lsp/rust-analyzer.ts +145 -0
  167. package/src/core/tools/lsp/types.ts +495 -0
  168. package/src/core/tools/lsp/utils.ts +526 -0
  169. package/src/core/tools/notebook.ts +182 -0
  170. package/src/core/tools/output.ts +198 -0
  171. package/src/core/tools/path-utils.ts +61 -0
  172. package/src/core/tools/read.ts +507 -0
  173. package/src/core/tools/renderers.ts +820 -0
  174. package/src/core/tools/review.ts +275 -0
  175. package/src/core/tools/rulebook.ts +124 -0
  176. package/src/core/tools/task/agents.ts +158 -0
  177. package/src/core/tools/task/artifacts.ts +114 -0
  178. package/src/core/tools/task/commands.ts +157 -0
  179. package/src/core/tools/task/discovery.ts +217 -0
  180. package/src/core/tools/task/executor.ts +531 -0
  181. package/src/core/tools/task/index.ts +548 -0
  182. package/src/core/tools/task/model-resolver.ts +176 -0
  183. package/src/core/tools/task/parallel.ts +38 -0
  184. package/src/core/tools/task/render.ts +502 -0
  185. package/src/core/tools/task/subprocess-tool-registry.ts +89 -0
  186. package/src/core/tools/task/types.ts +142 -0
  187. package/src/core/tools/truncate.ts +265 -0
  188. package/src/core/tools/web-fetch.ts +2511 -0
  189. package/src/core/tools/web-search/auth.ts +199 -0
  190. package/src/core/tools/web-search/index.ts +583 -0
  191. package/src/core/tools/web-search/providers/anthropic.ts +198 -0
  192. package/src/core/tools/web-search/providers/exa.ts +196 -0
  193. package/src/core/tools/web-search/providers/perplexity.ts +195 -0
  194. package/src/core/tools/web-search/render.ts +372 -0
  195. package/src/core/tools/web-search/types.ts +180 -0
  196. package/src/core/tools/write.ts +63 -0
  197. package/src/core/ttsr.ts +211 -0
  198. package/src/core/utils.ts +187 -0
  199. package/src/discovery/agents-md.ts +75 -0
  200. package/src/discovery/builtin.ts +647 -0
  201. package/src/discovery/claude.ts +623 -0
  202. package/src/discovery/cline.ts +104 -0
  203. package/src/discovery/codex.ts +571 -0
  204. package/src/discovery/cursor.ts +266 -0
  205. package/src/discovery/gemini.ts +368 -0
  206. package/src/discovery/github.ts +120 -0
  207. package/src/discovery/helpers.test.ts +127 -0
  208. package/src/discovery/helpers.ts +249 -0
  209. package/src/discovery/index.ts +84 -0
  210. package/src/discovery/mcp-json.ts +127 -0
  211. package/src/discovery/vscode.ts +99 -0
  212. package/src/discovery/windsurf.ts +219 -0
  213. package/src/index.ts +192 -0
  214. package/src/main.ts +507 -0
  215. package/src/migrations.ts +156 -0
  216. package/src/modes/cleanup.ts +23 -0
  217. package/src/modes/index.ts +48 -0
  218. package/src/modes/interactive/components/armin.ts +382 -0
  219. package/src/modes/interactive/components/assistant-message.ts +86 -0
  220. package/src/modes/interactive/components/bash-execution.ts +199 -0
  221. package/src/modes/interactive/components/bordered-loader.ts +41 -0
  222. package/src/modes/interactive/components/branch-summary-message.ts +42 -0
  223. package/src/modes/interactive/components/compaction-summary-message.ts +45 -0
  224. package/src/modes/interactive/components/custom-editor.ts +122 -0
  225. package/src/modes/interactive/components/diff.ts +147 -0
  226. package/src/modes/interactive/components/dynamic-border.ts +25 -0
  227. package/src/modes/interactive/components/extensions/extension-dashboard.ts +296 -0
  228. package/src/modes/interactive/components/extensions/extension-list.ts +479 -0
  229. package/src/modes/interactive/components/extensions/index.ts +9 -0
  230. package/src/modes/interactive/components/extensions/inspector-panel.ts +313 -0
  231. package/src/modes/interactive/components/extensions/state-manager.ts +558 -0
  232. package/src/modes/interactive/components/extensions/types.ts +191 -0
  233. package/src/modes/interactive/components/hook-editor.ts +117 -0
  234. package/src/modes/interactive/components/hook-input.ts +64 -0
  235. package/src/modes/interactive/components/hook-message.ts +96 -0
  236. package/src/modes/interactive/components/hook-selector.ts +91 -0
  237. package/src/modes/interactive/components/model-selector.ts +560 -0
  238. package/src/modes/interactive/components/oauth-selector.ts +136 -0
  239. package/src/modes/interactive/components/plugin-settings.ts +481 -0
  240. package/src/modes/interactive/components/queue-mode-selector.ts +56 -0
  241. package/src/modes/interactive/components/session-selector.ts +220 -0
  242. package/src/modes/interactive/components/settings-defs.ts +597 -0
  243. package/src/modes/interactive/components/settings-selector.ts +545 -0
  244. package/src/modes/interactive/components/show-images-selector.ts +45 -0
  245. package/src/modes/interactive/components/status-line/index.ts +4 -0
  246. package/src/modes/interactive/components/status-line/presets.ts +94 -0
  247. package/src/modes/interactive/components/status-line/segments.ts +350 -0
  248. package/src/modes/interactive/components/status-line/separators.ts +55 -0
  249. package/src/modes/interactive/components/status-line/types.ts +81 -0
  250. package/src/modes/interactive/components/status-line-segment-editor.ts +357 -0
  251. package/src/modes/interactive/components/status-line.ts +384 -0
  252. package/src/modes/interactive/components/theme-selector.ts +62 -0
  253. package/src/modes/interactive/components/thinking-selector.ts +64 -0
  254. package/src/modes/interactive/components/tool-execution.ts +946 -0
  255. package/src/modes/interactive/components/tree-selector.ts +877 -0
  256. package/src/modes/interactive/components/ttsr-notification.ts +82 -0
  257. package/src/modes/interactive/components/user-message-selector.ts +159 -0
  258. package/src/modes/interactive/components/user-message.ts +18 -0
  259. package/src/modes/interactive/components/visual-truncate.ts +50 -0
  260. package/src/modes/interactive/components/welcome.ts +228 -0
  261. package/src/modes/interactive/interactive-mode.ts +2669 -0
  262. package/src/modes/interactive/theme/dark.json +102 -0
  263. package/src/modes/interactive/theme/defaults/dark-arctic.json +111 -0
  264. package/src/modes/interactive/theme/defaults/dark-catppuccin.json +106 -0
  265. package/src/modes/interactive/theme/defaults/dark-cyberpunk.json +109 -0
  266. package/src/modes/interactive/theme/defaults/dark-dracula.json +105 -0
  267. package/src/modes/interactive/theme/defaults/dark-forest.json +103 -0
  268. package/src/modes/interactive/theme/defaults/dark-github.json +112 -0
  269. package/src/modes/interactive/theme/defaults/dark-gruvbox.json +119 -0
  270. package/src/modes/interactive/theme/defaults/dark-monochrome.json +101 -0
  271. package/src/modes/interactive/theme/defaults/dark-monokai.json +105 -0
  272. package/src/modes/interactive/theme/defaults/dark-nord.json +104 -0
  273. package/src/modes/interactive/theme/defaults/dark-ocean.json +108 -0
  274. package/src/modes/interactive/theme/defaults/dark-one.json +107 -0
  275. package/src/modes/interactive/theme/defaults/dark-retro.json +99 -0
  276. package/src/modes/interactive/theme/defaults/dark-rose-pine.json +95 -0
  277. package/src/modes/interactive/theme/defaults/dark-solarized.json +96 -0
  278. package/src/modes/interactive/theme/defaults/dark-sunset.json +106 -0
  279. package/src/modes/interactive/theme/defaults/dark-synthwave.json +102 -0
  280. package/src/modes/interactive/theme/defaults/dark-tokyo-night.json +108 -0
  281. package/src/modes/interactive/theme/defaults/index.ts +67 -0
  282. package/src/modes/interactive/theme/defaults/light-arctic.json +106 -0
  283. package/src/modes/interactive/theme/defaults/light-catppuccin.json +105 -0
  284. package/src/modes/interactive/theme/defaults/light-cyberpunk.json +103 -0
  285. package/src/modes/interactive/theme/defaults/light-forest.json +107 -0
  286. package/src/modes/interactive/theme/defaults/light-github.json +114 -0
  287. package/src/modes/interactive/theme/defaults/light-gruvbox.json +115 -0
  288. package/src/modes/interactive/theme/defaults/light-monochrome.json +100 -0
  289. package/src/modes/interactive/theme/defaults/light-ocean.json +106 -0
  290. package/src/modes/interactive/theme/defaults/light-one.json +105 -0
  291. package/src/modes/interactive/theme/defaults/light-retro.json +105 -0
  292. package/src/modes/interactive/theme/defaults/light-solarized.json +101 -0
  293. package/src/modes/interactive/theme/defaults/light-sunset.json +106 -0
  294. package/src/modes/interactive/theme/defaults/light-synthwave.json +105 -0
  295. package/src/modes/interactive/theme/defaults/light-tokyo-night.json +118 -0
  296. package/src/modes/interactive/theme/light.json +99 -0
  297. package/src/modes/interactive/theme/theme-schema.json +424 -0
  298. package/src/modes/interactive/theme/theme.ts +2211 -0
  299. package/src/modes/print-mode.ts +163 -0
  300. package/src/modes/rpc/rpc-client.ts +527 -0
  301. package/src/modes/rpc/rpc-mode.ts +494 -0
  302. package/src/modes/rpc/rpc-types.ts +203 -0
  303. package/src/prompts/architect-plan.md +10 -0
  304. package/src/prompts/branch-summary-preamble.md +3 -0
  305. package/src/prompts/branch-summary.md +28 -0
  306. package/src/prompts/browser.md +71 -0
  307. package/src/prompts/compaction-summary.md +34 -0
  308. package/src/prompts/compaction-turn-prefix.md +16 -0
  309. package/src/prompts/compaction-update-summary.md +41 -0
  310. package/src/prompts/explore.md +82 -0
  311. package/src/prompts/implement-with-critic.md +11 -0
  312. package/src/prompts/implement.md +11 -0
  313. package/src/prompts/init.md +30 -0
  314. package/src/prompts/plan.md +54 -0
  315. package/src/prompts/reviewer.md +81 -0
  316. package/src/prompts/summarization-system.md +3 -0
  317. package/src/prompts/system-prompt.md +27 -0
  318. package/src/prompts/task.md +56 -0
  319. package/src/prompts/title-system.md +8 -0
  320. package/src/prompts/tools/ask.md +24 -0
  321. package/src/prompts/tools/bash.md +23 -0
  322. package/src/prompts/tools/edit.md +9 -0
  323. package/src/prompts/tools/find.md +6 -0
  324. package/src/prompts/tools/grep.md +12 -0
  325. package/src/prompts/tools/lsp.md +14 -0
  326. package/src/prompts/tools/output.md +23 -0
  327. package/src/prompts/tools/read.md +25 -0
  328. package/src/prompts/tools/web-fetch.md +8 -0
  329. package/src/prompts/tools/web-search.md +10 -0
  330. package/src/prompts/tools/write.md +10 -0
  331. package/src/utils/changelog.ts +99 -0
  332. package/src/utils/clipboard.ts +265 -0
  333. package/src/utils/fuzzy.ts +108 -0
  334. package/src/utils/mime.ts +30 -0
  335. package/src/utils/shell-snapshot.ts +218 -0
  336. package/src/utils/shell.ts +364 -0
  337. package/src/utils/tools-manager.ts +265 -0
@@ -0,0 +1,647 @@
1
+ /**
2
+ * Builtin Provider (.omp / .pi)
3
+ *
4
+ * Primary provider for OMP native configs. Supports all capabilities.
5
+ * .pi is an alias for backwards compatibility.
6
+ */
7
+
8
+ import { basename, dirname, join } from "node:path";
9
+ import { type ContextFile, contextFileCapability } from "../capability/context-file";
10
+ import { type Extension, type ExtensionManifest, extensionCapability } from "../capability/extension";
11
+ import { type Hook, hookCapability } from "../capability/hook";
12
+ import { registerProvider } from "../capability/index";
13
+ import { type Instruction, instructionCapability } from "../capability/instruction";
14
+ import { type MCPServer, mcpCapability } from "../capability/mcp";
15
+ import { type Prompt, promptCapability } from "../capability/prompt";
16
+ import { type Rule, ruleCapability } from "../capability/rule";
17
+ import { type Settings, settingsCapability } from "../capability/settings";
18
+ import { type Skill, type SkillFrontmatter, skillCapability } from "../capability/skill";
19
+ import { type SlashCommand, slashCommandCapability } from "../capability/slash-command";
20
+ import { type SystemPrompt, systemPromptCapability } from "../capability/system-prompt";
21
+ import { type CustomTool, toolCapability } from "../capability/tool";
22
+ import type { LoadContext, LoadResult } from "../capability/types";
23
+ import {
24
+ createSourceMeta,
25
+ expandEnvVarsDeep,
26
+ loadFilesFromDir,
27
+ parseFrontmatter,
28
+ parseJSON,
29
+ SOURCE_PATHS,
30
+ } from "./helpers";
31
+
32
+ const PROVIDER_ID = "native";
33
+ const DISPLAY_NAME = "OMP";
34
+ const DESCRIPTION = "Native OMP configuration from ~/.omp and .omp/";
35
+ const PRIORITY = 100;
36
+
37
+ const PATHS = SOURCE_PATHS.native;
38
+ const PROJECT_DIRS = [PATHS.projectDir, ...PATHS.aliases];
39
+ const USER_DIRS = [PATHS.userBase, ...PATHS.aliases];
40
+
41
+ function getConfigDirs(ctx: LoadContext): Array<{ dir: string; level: "user" | "project" }> {
42
+ const result: Array<{ dir: string; level: "user" | "project" }> = [];
43
+
44
+ for (const name of PROJECT_DIRS) {
45
+ const projectDir = ctx.fs.walkUp(name, { dir: true });
46
+ if (projectDir) {
47
+ result.push({ dir: projectDir, level: "project" });
48
+ break;
49
+ }
50
+ }
51
+
52
+ for (const name of USER_DIRS) {
53
+ const userDir = join(ctx.home, name, PATHS.userAgent.replace(`${PATHS.userBase}/`, ""));
54
+ if (ctx.fs.isDir(userDir)) {
55
+ result.push({ dir: userDir, level: "user" });
56
+ break;
57
+ }
58
+ }
59
+
60
+ return result;
61
+ }
62
+
63
+ // MCP
64
+ function loadMCPServers(ctx: LoadContext): LoadResult<MCPServer> {
65
+ const items: MCPServer[] = [];
66
+ const warnings: string[] = [];
67
+
68
+ for (const name of PROJECT_DIRS) {
69
+ const projectDir = ctx.fs.walkUp(name, { dir: true });
70
+ if (!projectDir) continue;
71
+
72
+ for (const filename of ["mcp.json", ".mcp.json"]) {
73
+ const path = join(projectDir, filename);
74
+ const content = ctx.fs.readFile(path);
75
+ if (!content) continue;
76
+
77
+ const data = parseJSON<{ mcpServers?: Record<string, unknown> }>(content);
78
+ if (!data?.mcpServers) continue;
79
+
80
+ const expanded = expandEnvVarsDeep(data.mcpServers);
81
+ for (const [serverName, config] of Object.entries(expanded)) {
82
+ const serverConfig = config as Record<string, unknown>;
83
+ items.push({
84
+ name: serverName,
85
+ command: serverConfig.command as string | undefined,
86
+ args: serverConfig.args as string[] | undefined,
87
+ env: serverConfig.env as Record<string, string> | undefined,
88
+ url: serverConfig.url as string | undefined,
89
+ headers: serverConfig.headers as Record<string, string> | undefined,
90
+ transport: serverConfig.type as "stdio" | "sse" | "http" | undefined,
91
+ _source: createSourceMeta(PROVIDER_ID, path, "project"),
92
+ });
93
+ }
94
+ break;
95
+ }
96
+ break;
97
+ }
98
+
99
+ for (const name of USER_DIRS) {
100
+ const userPath = join(ctx.home, name, "mcp.json");
101
+ const content = ctx.fs.readFile(userPath);
102
+ if (!content) continue;
103
+
104
+ const data = parseJSON<{ mcpServers?: Record<string, unknown> }>(content);
105
+ if (!data?.mcpServers) continue;
106
+
107
+ const expanded = expandEnvVarsDeep(data.mcpServers);
108
+ for (const [serverName, config] of Object.entries(expanded)) {
109
+ const serverConfig = config as Record<string, unknown>;
110
+ items.push({
111
+ name: serverName,
112
+ command: serverConfig.command as string | undefined,
113
+ args: serverConfig.args as string[] | undefined,
114
+ env: serverConfig.env as Record<string, string> | undefined,
115
+ url: serverConfig.url as string | undefined,
116
+ headers: serverConfig.headers as Record<string, string> | undefined,
117
+ transport: serverConfig.type as "stdio" | "sse" | "http" | undefined,
118
+ _source: createSourceMeta(PROVIDER_ID, userPath, "user"),
119
+ });
120
+ }
121
+ break;
122
+ }
123
+
124
+ return { items, warnings };
125
+ }
126
+
127
+ registerProvider<MCPServer>(mcpCapability.id, {
128
+ id: PROVIDER_ID,
129
+ displayName: DISPLAY_NAME,
130
+ description: DESCRIPTION,
131
+ priority: PRIORITY,
132
+ load: loadMCPServers,
133
+ });
134
+
135
+ // System Prompt (SYSTEM.md)
136
+ function loadSystemPrompt(ctx: LoadContext): LoadResult<SystemPrompt> {
137
+ const items: SystemPrompt[] = [];
138
+
139
+ // User level: ~/.omp/agent/SYSTEM.md or ~/.pi/agent/SYSTEM.md
140
+ for (const name of USER_DIRS) {
141
+ const userPath = join(ctx.home, name, PATHS.userAgent.replace(`${PATHS.userBase}/`, ""), "SYSTEM.md");
142
+ const userContent = ctx.fs.readFile(userPath);
143
+ if (userContent) {
144
+ items.push({
145
+ path: userPath,
146
+ content: userContent,
147
+ level: "user",
148
+ _source: createSourceMeta(PROVIDER_ID, userPath, "user"),
149
+ });
150
+ break; // First match wins
151
+ }
152
+ }
153
+
154
+ // Project level: walk up looking for .omp/SYSTEM.md or .pi/SYSTEM.md
155
+ let current = ctx.cwd;
156
+ while (true) {
157
+ for (const name of PROJECT_DIRS) {
158
+ const configDir = join(current, name);
159
+ if (ctx.fs.isDir(configDir)) {
160
+ const projectPath = join(configDir, "SYSTEM.md");
161
+ const content = ctx.fs.readFile(projectPath);
162
+ if (content) {
163
+ items.push({
164
+ path: projectPath,
165
+ content,
166
+ level: "project",
167
+ _source: createSourceMeta(PROVIDER_ID, projectPath, "project"),
168
+ });
169
+ break; // First config dir in this directory wins
170
+ }
171
+ }
172
+ }
173
+ const parent = dirname(current);
174
+ if (parent === current) break;
175
+ current = parent;
176
+ }
177
+
178
+ return { items, warnings: [] };
179
+ }
180
+
181
+ registerProvider<SystemPrompt>(systemPromptCapability.id, {
182
+ id: PROVIDER_ID,
183
+ displayName: DISPLAY_NAME,
184
+ description: "Custom system prompt from SYSTEM.md",
185
+ priority: PRIORITY,
186
+ load: loadSystemPrompt,
187
+ });
188
+
189
+ // Skills
190
+ function loadSkillFromFile(ctx: LoadContext, path: string, level: "user" | "project"): Skill | null {
191
+ const content = ctx.fs.readFile(path);
192
+ if (!content) return null;
193
+
194
+ const { frontmatter, body } = parseFrontmatter(content);
195
+ const skillDir = dirname(path);
196
+ const parentDirName = basename(skillDir);
197
+ const name = (frontmatter.name as string) || parentDirName;
198
+
199
+ if (!frontmatter.description) return null;
200
+
201
+ return {
202
+ name,
203
+ path,
204
+ content: body,
205
+ frontmatter: frontmatter as SkillFrontmatter,
206
+ level,
207
+ _source: createSourceMeta(PROVIDER_ID, path, level),
208
+ };
209
+ }
210
+
211
+ function loadSkillsRecursive(ctx: LoadContext, dir: string, level: "user" | "project"): LoadResult<Skill> {
212
+ const items: Skill[] = [];
213
+ const warnings: string[] = [];
214
+
215
+ if (!ctx.fs.isDir(dir)) return { items, warnings };
216
+
217
+ for (const name of ctx.fs.readDir(dir)) {
218
+ if (name.startsWith(".") || name === "node_modules") continue;
219
+
220
+ const path = join(dir, name);
221
+
222
+ if (ctx.fs.isDir(path)) {
223
+ const skillFile = join(path, "SKILL.md");
224
+ if (ctx.fs.isFile(skillFile)) {
225
+ const skill = loadSkillFromFile(ctx, skillFile, level);
226
+ if (skill) items.push(skill);
227
+ }
228
+
229
+ const sub = loadSkillsRecursive(ctx, path, level);
230
+ items.push(...sub.items);
231
+ if (sub.warnings) warnings.push(...sub.warnings);
232
+ } else if (name === "SKILL.md") {
233
+ const skill = loadSkillFromFile(ctx, path, level);
234
+ if (skill) items.push(skill);
235
+ }
236
+ }
237
+
238
+ return { items, warnings };
239
+ }
240
+
241
+ function loadSkills(ctx: LoadContext): LoadResult<Skill> {
242
+ const items: Skill[] = [];
243
+ const warnings: string[] = [];
244
+
245
+ for (const { dir, level } of getConfigDirs(ctx)) {
246
+ const skillsDir = join(dir, "skills");
247
+ const result = loadSkillsRecursive(ctx, skillsDir, level);
248
+ items.push(...result.items);
249
+ if (result.warnings) warnings.push(...result.warnings);
250
+ }
251
+
252
+ return { items, warnings };
253
+ }
254
+
255
+ registerProvider<Skill>(skillCapability.id, {
256
+ id: PROVIDER_ID,
257
+ displayName: DISPLAY_NAME,
258
+ description: DESCRIPTION,
259
+ priority: PRIORITY,
260
+ load: loadSkills,
261
+ });
262
+
263
+ // Slash Commands
264
+ function loadSlashCommands(ctx: LoadContext): LoadResult<SlashCommand> {
265
+ const items: SlashCommand[] = [];
266
+ const warnings: string[] = [];
267
+
268
+ for (const { dir, level } of getConfigDirs(ctx)) {
269
+ const commandsDir = join(dir, "commands");
270
+ const result = loadFilesFromDir<SlashCommand>(ctx, commandsDir, PROVIDER_ID, level, {
271
+ extensions: ["md"],
272
+ transform: (name, content, path, source) => ({
273
+ name: name.replace(/\.md$/, ""),
274
+ path,
275
+ content,
276
+ level,
277
+ _source: source,
278
+ }),
279
+ });
280
+ items.push(...result.items);
281
+ if (result.warnings) warnings.push(...result.warnings);
282
+ }
283
+
284
+ return { items, warnings };
285
+ }
286
+
287
+ registerProvider<SlashCommand>(slashCommandCapability.id, {
288
+ id: PROVIDER_ID,
289
+ displayName: DISPLAY_NAME,
290
+ description: DESCRIPTION,
291
+ priority: PRIORITY,
292
+ load: loadSlashCommands,
293
+ });
294
+
295
+ // Rules
296
+ function loadRules(ctx: LoadContext): LoadResult<Rule> {
297
+ const items: Rule[] = [];
298
+ const warnings: string[] = [];
299
+
300
+ for (const { dir, level } of getConfigDirs(ctx)) {
301
+ const rulesDir = join(dir, "rules");
302
+ const result = loadFilesFromDir<Rule>(ctx, rulesDir, PROVIDER_ID, level, {
303
+ extensions: ["md", "mdc"],
304
+ transform: (name, content, path, source) => {
305
+ const { frontmatter, body } = parseFrontmatter(content);
306
+ return {
307
+ name: name.replace(/\.(md|mdc)$/, ""),
308
+ path,
309
+ content: body,
310
+ globs: frontmatter.globs as string[] | undefined,
311
+ alwaysApply: frontmatter.alwaysApply as boolean | undefined,
312
+ description: frontmatter.description as string | undefined,
313
+ ttsrTrigger: typeof frontmatter.ttsr_trigger === "string" ? frontmatter.ttsr_trigger : undefined,
314
+ _source: source,
315
+ };
316
+ },
317
+ });
318
+ items.push(...result.items);
319
+ if (result.warnings) warnings.push(...result.warnings);
320
+ }
321
+
322
+ return { items, warnings };
323
+ }
324
+
325
+ registerProvider<Rule>(ruleCapability.id, {
326
+ id: PROVIDER_ID,
327
+ displayName: DISPLAY_NAME,
328
+ description: DESCRIPTION,
329
+ priority: PRIORITY,
330
+ load: loadRules,
331
+ });
332
+
333
+ // Prompts
334
+ function loadPrompts(ctx: LoadContext): LoadResult<Prompt> {
335
+ const items: Prompt[] = [];
336
+ const warnings: string[] = [];
337
+
338
+ for (const { dir, level } of getConfigDirs(ctx)) {
339
+ const promptsDir = join(dir, "prompts");
340
+ const result = loadFilesFromDir<Prompt>(ctx, promptsDir, PROVIDER_ID, level, {
341
+ extensions: ["md"],
342
+ transform: (name, content, path, source) => ({
343
+ name: name.replace(/\.md$/, ""),
344
+ path,
345
+ content,
346
+ _source: source,
347
+ }),
348
+ });
349
+ items.push(...result.items);
350
+ if (result.warnings) warnings.push(...result.warnings);
351
+ }
352
+
353
+ return { items, warnings };
354
+ }
355
+
356
+ registerProvider<Prompt>(promptCapability.id, {
357
+ id: PROVIDER_ID,
358
+ displayName: DISPLAY_NAME,
359
+ description: DESCRIPTION,
360
+ priority: PRIORITY,
361
+ load: loadPrompts,
362
+ });
363
+
364
+ // Extensions
365
+ function loadExtensions(ctx: LoadContext): LoadResult<Extension> {
366
+ const items: Extension[] = [];
367
+ const warnings: string[] = [];
368
+
369
+ for (const { dir, level } of getConfigDirs(ctx)) {
370
+ const extensionsDir = join(dir, "extensions");
371
+ if (!ctx.fs.isDir(extensionsDir)) continue;
372
+
373
+ for (const name of ctx.fs.readDir(extensionsDir)) {
374
+ if (name.startsWith(".")) continue;
375
+
376
+ const extDir = join(extensionsDir, name);
377
+ if (!ctx.fs.isDir(extDir)) continue;
378
+
379
+ const manifestPath = join(extDir, "gemini-extension.json");
380
+ const content = ctx.fs.readFile(manifestPath);
381
+ if (!content) continue;
382
+
383
+ const manifest = parseJSON<ExtensionManifest>(content);
384
+ if (!manifest) {
385
+ warnings.push(`Failed to parse ${manifestPath}`);
386
+ continue;
387
+ }
388
+
389
+ items.push({
390
+ name: manifest.name || name,
391
+ path: extDir,
392
+ manifest,
393
+ level,
394
+ _source: createSourceMeta(PROVIDER_ID, manifestPath, level),
395
+ });
396
+ }
397
+ }
398
+
399
+ return { items, warnings };
400
+ }
401
+
402
+ registerProvider<Extension>(extensionCapability.id, {
403
+ id: PROVIDER_ID,
404
+ displayName: DISPLAY_NAME,
405
+ description: DESCRIPTION,
406
+ priority: PRIORITY,
407
+ load: loadExtensions,
408
+ });
409
+
410
+ // Instructions
411
+ function loadInstructions(ctx: LoadContext): LoadResult<Instruction> {
412
+ const items: Instruction[] = [];
413
+ const warnings: string[] = [];
414
+
415
+ for (const { dir, level } of getConfigDirs(ctx)) {
416
+ const instructionsDir = join(dir, "instructions");
417
+ const result = loadFilesFromDir<Instruction>(ctx, instructionsDir, PROVIDER_ID, level, {
418
+ extensions: ["md"],
419
+ transform: (name, content, path, source) => {
420
+ const { frontmatter, body } = parseFrontmatter(content);
421
+ return {
422
+ name: name.replace(/\.instructions\.md$/, "").replace(/\.md$/, ""),
423
+ path,
424
+ content: body,
425
+ applyTo: frontmatter.applyTo as string | undefined,
426
+ _source: source,
427
+ };
428
+ },
429
+ });
430
+ items.push(...result.items);
431
+ if (result.warnings) warnings.push(...result.warnings);
432
+ }
433
+
434
+ return { items, warnings };
435
+ }
436
+
437
+ registerProvider<Instruction>(instructionCapability.id, {
438
+ id: PROVIDER_ID,
439
+ displayName: DISPLAY_NAME,
440
+ description: DESCRIPTION,
441
+ priority: PRIORITY,
442
+ load: loadInstructions,
443
+ });
444
+
445
+ // Hooks
446
+ function loadHooks(ctx: LoadContext): LoadResult<Hook> {
447
+ const items: Hook[] = [];
448
+
449
+ for (const { dir, level } of getConfigDirs(ctx)) {
450
+ const hooksDir = join(dir, "hooks");
451
+ if (!ctx.fs.isDir(hooksDir)) continue;
452
+
453
+ for (const hookType of ["pre", "post"] as const) {
454
+ const typeDir = join(hooksDir, hookType);
455
+ if (!ctx.fs.isDir(typeDir)) continue;
456
+
457
+ for (const name of ctx.fs.readDir(typeDir)) {
458
+ if (name.startsWith(".")) continue;
459
+
460
+ const path = join(typeDir, name);
461
+ if (!ctx.fs.isFile(path)) continue;
462
+
463
+ const baseName = name.includes(".") ? name.slice(0, name.lastIndexOf(".")) : name;
464
+ const tool = baseName === "*" ? "*" : baseName;
465
+
466
+ items.push({
467
+ name,
468
+ path,
469
+ type: hookType,
470
+ tool,
471
+ level,
472
+ _source: createSourceMeta(PROVIDER_ID, path, level),
473
+ });
474
+ }
475
+ }
476
+ }
477
+
478
+ return { items, warnings: [] };
479
+ }
480
+
481
+ registerProvider<Hook>(hookCapability.id, {
482
+ id: PROVIDER_ID,
483
+ displayName: DISPLAY_NAME,
484
+ description: DESCRIPTION,
485
+ priority: PRIORITY,
486
+ load: loadHooks,
487
+ });
488
+
489
+ // Custom Tools
490
+ function loadTools(ctx: LoadContext): LoadResult<CustomTool> {
491
+ const items: CustomTool[] = [];
492
+ const warnings: string[] = [];
493
+
494
+ for (const { dir, level } of getConfigDirs(ctx)) {
495
+ const toolsDir = join(dir, "tools");
496
+ if (!ctx.fs.isDir(toolsDir)) continue;
497
+
498
+ // Load tool files (JSON and Markdown declarative tools)
499
+ const result = loadFilesFromDir<CustomTool>(ctx, toolsDir, PROVIDER_ID, level, {
500
+ extensions: ["json", "md"],
501
+ transform: (name, content, path, source) => {
502
+ if (name.endsWith(".json")) {
503
+ const data = parseJSON<{ name?: string; description?: string }>(content);
504
+ return {
505
+ name: data?.name || name.replace(/\.json$/, ""),
506
+ path,
507
+ description: data?.description,
508
+ level,
509
+ _source: source,
510
+ };
511
+ }
512
+ const { frontmatter } = parseFrontmatter(content);
513
+ return {
514
+ name: (frontmatter.name as string) || name.replace(/\.md$/, ""),
515
+ path,
516
+ description: frontmatter.description as string | undefined,
517
+ level,
518
+ _source: source,
519
+ };
520
+ },
521
+ });
522
+ items.push(...result.items);
523
+ if (result.warnings) warnings.push(...result.warnings);
524
+
525
+ // Load TypeScript tools from subdirectories (tools/mytool/index.ts pattern)
526
+ for (const name of ctx.fs.readDir(toolsDir)) {
527
+ if (name.startsWith(".")) continue;
528
+
529
+ const subDir = join(toolsDir, name);
530
+ if (!ctx.fs.isDir(subDir)) continue;
531
+
532
+ const indexPath = join(subDir, "index.ts");
533
+ if (ctx.fs.isFile(indexPath)) {
534
+ items.push({
535
+ name,
536
+ path: indexPath,
537
+ description: undefined,
538
+ level,
539
+ _source: createSourceMeta(PROVIDER_ID, indexPath, level),
540
+ });
541
+ }
542
+ }
543
+ }
544
+
545
+ return { items, warnings };
546
+ }
547
+
548
+ registerProvider<CustomTool>(toolCapability.id, {
549
+ id: PROVIDER_ID,
550
+ displayName: DISPLAY_NAME,
551
+ description: DESCRIPTION,
552
+ priority: PRIORITY,
553
+ load: loadTools,
554
+ });
555
+
556
+ // Settings
557
+ function loadSettings(ctx: LoadContext): LoadResult<Settings> {
558
+ const items: Settings[] = [];
559
+ const warnings: string[] = [];
560
+
561
+ for (const { dir, level } of getConfigDirs(ctx)) {
562
+ const settingsPath = join(dir, "settings.json");
563
+ const content = ctx.fs.readFile(settingsPath);
564
+ if (!content) continue;
565
+
566
+ const data = parseJSON<Record<string, unknown>>(content);
567
+ if (!data) {
568
+ warnings.push(`Failed to parse ${settingsPath}`);
569
+ continue;
570
+ }
571
+
572
+ items.push({
573
+ path: settingsPath,
574
+ data,
575
+ level,
576
+ _source: createSourceMeta(PROVIDER_ID, settingsPath, level),
577
+ });
578
+ }
579
+
580
+ return { items, warnings };
581
+ }
582
+
583
+ registerProvider<Settings>(settingsCapability.id, {
584
+ id: PROVIDER_ID,
585
+ displayName: DISPLAY_NAME,
586
+ description: DESCRIPTION,
587
+ priority: PRIORITY,
588
+ load: loadSettings,
589
+ });
590
+
591
+ // Context Files (AGENTS.md)
592
+ function loadContextFiles(ctx: LoadContext): LoadResult<ContextFile> {
593
+ const items: ContextFile[] = [];
594
+ const warnings: string[] = [];
595
+
596
+ // User level: ~/.omp/agent/AGENTS.md or ~/.pi/agent/AGENTS.md
597
+ for (const name of USER_DIRS) {
598
+ const userPath = join(ctx.home, name, PATHS.userAgent.replace(`${PATHS.userBase}/`, ""), "AGENTS.md");
599
+ const content = ctx.fs.readFile(userPath);
600
+ if (content) {
601
+ items.push({
602
+ path: userPath,
603
+ content,
604
+ level: "user",
605
+ _source: createSourceMeta(PROVIDER_ID, userPath, "user"),
606
+ });
607
+ break; // First match wins
608
+ }
609
+ }
610
+
611
+ // Project level: walk up looking for .omp/AGENTS.md or .pi/AGENTS.md
612
+ let current = ctx.cwd;
613
+ let depth = 0;
614
+ while (true) {
615
+ for (const name of PROJECT_DIRS) {
616
+ const configDir = join(current, name);
617
+ if (ctx.fs.isDir(configDir)) {
618
+ const projectPath = join(configDir, "AGENTS.md");
619
+ const content = ctx.fs.readFile(projectPath);
620
+ if (content) {
621
+ items.push({
622
+ path: projectPath,
623
+ content,
624
+ level: "project",
625
+ depth,
626
+ _source: createSourceMeta(PROVIDER_ID, projectPath, "project"),
627
+ });
628
+ return { items, warnings }; // First config dir wins
629
+ }
630
+ }
631
+ }
632
+ const parent = dirname(current);
633
+ if (parent === current) break;
634
+ current = parent;
635
+ depth++;
636
+ }
637
+
638
+ return { items, warnings };
639
+ }
640
+
641
+ registerProvider<ContextFile>(contextFileCapability.id, {
642
+ id: PROVIDER_ID,
643
+ displayName: DISPLAY_NAME,
644
+ description: "Load AGENTS.md from .omp/ directories",
645
+ priority: PRIORITY,
646
+ load: loadContextFiles,
647
+ });