diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 64a922b99..6b62dd951 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -8,6 +8,9 @@ - Enabled inline prompts with `/loop` commands (e.g., `/loop 10 fix the bug`) - Added support for compound duration formats in `/loop` (e.g., `1h30m`) +### Added + +- Added project/user/plugin `dap.json` and `dap.yaml` support for defining or overriding debugger adapters used by the `debug` tool. ([#2999](https://github.com/can1357/oh-my-pi/issues/2999)) ## [16.1.5] - 2026-06-19 @@ -114,9 +117,6 @@ - Snapcompact compaction summaries now reach the model as ordered history blocks instead of one lead-in text block plus appended images: plain text at the oldest edge, an imaged middle, then plain text at the newest edge. This matches the new text-first snapcompact archive layout and preserves chronological order in the provider prompt. - Fixed `/dump` output repeating the tool inventory twice when `inlineToolDescriptors` is enabled. - Unified TUI border corners on the rounded style: tool-result frames, overlays, code fences, debug frames, and the interactive bash box now draw rounded corners (`╭╮╰╯`) to match the editor and message cards, instead of mixing rounded boxes with sharp (`┌┐└┘`) ones. `boxRound` now carries the sharp tee/cross junction glyphs (no rounded variant exists), so dividers still honor `boxSharp.tee*`/`cross` theme overrides. Markdown tables intentionally keep the fully sharp `boxSharp` set; its corner tokens now affect tables only. -### Added - -- Added project/user/plugin `dap.json` and `dap.yaml` support for defining or overriding debugger adapters used by the `debug` tool. ([#2999](https://github.com/can1357/oh-my-pi/issues/2999)) ## [16.0.11] - 2026-06-19 diff --git a/packages/coding-agent/src/dap/config.ts b/packages/coding-agent/src/dap/config.ts index abeab9f86..fb4e61d5f 100644 --- a/packages/coding-agent/src/dap/config.ts +++ b/packages/coding-agent/src/dap/config.ts @@ -123,6 +123,7 @@ function fileConfigSource(filePath: string): ConfigSource { function readMarketplaceDapConfig(root: ClaudePluginRoot): NormalizedConfig | null { const catalogPaths = [ + path.resolve(root.path, "..", "..", ".omp-plugin", "marketplace.json"), path.resolve(root.path, "..", "..", "marketplace.json"), path.resolve(root.path, "..", "..", ".claude-plugin", "marketplace.json"), ]; diff --git a/packages/coding-agent/test/debug/dap-config.test.ts b/packages/coding-agent/test/debug/dap-config.test.ts index 995198453..bb7557bee 100644 --- a/packages/coding-agent/test/debug/dap-config.test.ts +++ b/packages/coding-agent/test/debug/dap-config.test.ts @@ -2,9 +2,12 @@ import { afterEach, describe, expect, it, vi } from "bun:test"; import * as fs from "node:fs/promises"; import * as os from "node:os"; import * as path from "node:path"; -import { getAdapterConfigs, resolveAdapter, selectLaunchAdapter } from "@oh-my-pi/pi-coding-agent/dap/config"; +import { getAdapterConfigs, resolveAdapter, selectLaunchAdapter } from "../../src/dap/config"; +import { clearClaudePluginRootsCache, injectPluginDirRoots } from "../../src/discovery/helpers"; const tempDirs: string[] = []; +const ORIGINAL_OMP_PLUGIN_DIR = process.env.OMP_PLUGIN_DIR; +const ORIGINAL_OMP_MARKETPLACE_DIR = process.env.OMP_MARKETPLACE_DIR; async function makeTempDir(prefix: string): Promise { const cwd = await fs.mkdtemp(path.join(os.tmpdir(), prefix)); @@ -14,6 +17,18 @@ async function makeTempDir(prefix: string): Promise { afterEach(async () => { vi.restoreAllMocks(); + if (ORIGINAL_OMP_PLUGIN_DIR === undefined) { + delete process.env.OMP_PLUGIN_DIR; + } else { + process.env.OMP_PLUGIN_DIR = ORIGINAL_OMP_PLUGIN_DIR; + } + if (ORIGINAL_OMP_MARKETPLACE_DIR === undefined) { + delete process.env.OMP_MARKETPLACE_DIR; + } else { + process.env.OMP_MARKETPLACE_DIR = ORIGINAL_OMP_MARKETPLACE_DIR; + } + await injectPluginDirRoots(os.homedir(), []); + clearClaudePluginRootsCache(); await Promise.all(tempDirs.splice(0).map(dir => fs.rm(dir, { recursive: true, force: true }))); }); @@ -107,6 +122,40 @@ describe("DAP adapter configuration", () => { expect(selected?.launchDefaults).toEqual({ request: "launch", projectRoot: "." }); }); + it("loads plugin DAP adapters from OMP marketplace catalogs", async () => { + const cwd = await makeTempDir("omp-dap-config-marketplace-"); + const marketplaceRoot = path.join(cwd, "marketplaces", "test-market"); + const pluginRoot = path.join(marketplaceRoot, "plugins", "acme-debug"); + await fs.mkdir(path.join(marketplaceRoot, ".omp-plugin"), { recursive: true }); + await fs.mkdir(path.join(pluginRoot, ".claude-plugin"), { recursive: true }); + await fs.writeFile(path.join(cwd, "app.rb"), "puts 'hi'\n"); + await fs.writeFile( + path.join(pluginRoot, ".claude-plugin", "plugin.json"), + JSON.stringify({ name: "acme-debug" }), + ); + await fs.writeFile( + path.join(marketplaceRoot, ".omp-plugin", "marketplace.json"), + JSON.stringify({ + plugins: [ + { + name: "acme-debug", + dapAdapters: { + "acme-ruby": { + command: "ruby-debug-adapter", + fileTypes: [".rb"], + }, + }, + }, + ], + }), + ); + process.env.OMP_PLUGIN_DIR = path.join(cwd, "plugins"); + process.env.OMP_MARKETPLACE_DIR = path.join(cwd, "marketplaces"); + await injectPluginDirRoots(cwd, [pluginRoot], cwd); + + expect(getAdapterConfigs(cwd)["acme-ruby"]?.command).toBe("ruby-debug-adapter"); + }); + it("ignores invalid custom adapters without discarding valid configs", async () => { const cwd = await makeTempDir("omp-dap-config-invalid-"); await fs.writeFile(