@oh-my-pi/pi-coding-agent 16.1.23 → 16.2.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 (254) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/dist/cli.js +3817 -3735
  3. package/dist/types/advisor/__tests__/emission-guard.test.d.ts +1 -0
  4. package/dist/types/advisor/emission-guard.d.ts +73 -0
  5. package/dist/types/advisor/index.d.ts +1 -0
  6. package/dist/types/advisor/runtime.d.ts +7 -0
  7. package/dist/types/advisor/watchdog.d.ts +2 -0
  8. package/dist/types/cli/flag-tables.d.ts +1 -0
  9. package/dist/types/cli/gallery-fixtures/search.d.ts +1 -1
  10. package/dist/types/collab/display-name.d.ts +3 -0
  11. package/dist/types/collab/host.d.ts +0 -2
  12. package/dist/types/commands/worktree.d.ts +0 -3
  13. package/dist/types/config/inline-tool-descriptors-mode.d.ts +13 -0
  14. package/dist/types/config/model-discovery.d.ts +6 -1
  15. package/dist/types/config/model-registry.d.ts +6 -2
  16. package/dist/types/config/model-resolver.d.ts +12 -0
  17. package/dist/types/config/models-config-schema.d.ts +29 -2
  18. package/dist/types/config/models-config.d.ts +22 -1
  19. package/dist/types/config/settings-schema.d.ts +91 -21
  20. package/dist/types/config/settings.d.ts +2 -0
  21. package/dist/types/dap/config.d.ts +1 -1
  22. package/dist/types/edit/hashline/filesystem.d.ts +4 -2
  23. package/dist/types/edit/renderer.d.ts +4 -0
  24. package/dist/types/extensibility/extensions/types.d.ts +17 -17
  25. package/dist/types/extensibility/hooks/types.d.ts +10 -10
  26. package/dist/types/extensibility/plugins/marketplace/types.d.ts +1 -0
  27. package/dist/types/internal-urls/__tests__/ssh-protocol.test.d.ts +1 -0
  28. package/dist/types/internal-urls/index.d.ts +1 -0
  29. package/dist/types/internal-urls/registry-helpers.d.ts +2 -0
  30. package/dist/types/internal-urls/router.d.ts +1 -1
  31. package/dist/types/internal-urls/ssh-protocol.d.ts +10 -0
  32. package/dist/types/internal-urls/types.d.ts +19 -2
  33. package/dist/types/irc/bus.d.ts +6 -0
  34. package/dist/types/modes/components/__tests__/move-overlay.test.d.ts +1 -0
  35. package/dist/types/modes/components/custom-editor.d.ts +7 -0
  36. package/dist/types/modes/components/move-overlay.d.ts +23 -0
  37. package/dist/types/modes/components/plugin-selector.d.ts +2 -1
  38. package/dist/types/modes/components/queue-mode-selector.d.ts +2 -1
  39. package/dist/types/modes/components/select-list-mouse-routing.d.ts +2 -0
  40. package/dist/types/modes/components/settings-defs.d.ts +4 -1
  41. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  42. package/dist/types/modes/components/show-images-selector.d.ts +2 -1
  43. package/dist/types/modes/components/status-line/component.d.ts +2 -0
  44. package/dist/types/modes/components/status-line/types.d.ts +2 -0
  45. package/dist/types/modes/components/theme-selector.d.ts +2 -1
  46. package/dist/types/modes/components/thinking-selector.d.ts +2 -1
  47. package/dist/types/modes/controllers/command-controller.d.ts +10 -1
  48. package/dist/types/modes/controllers/streaming-reveal.d.ts +6 -0
  49. package/dist/types/modes/interactive-mode.d.ts +10 -1
  50. package/dist/types/modes/internal-url-autocomplete.d.ts +1 -1
  51. package/dist/types/modes/running-subagent-badge.d.ts +6 -0
  52. package/dist/types/modes/theme/theme.d.ts +1 -1
  53. package/dist/types/modes/types.d.ts +9 -1
  54. package/dist/types/sdk.d.ts +2 -2
  55. package/dist/types/session/agent-session.d.ts +2 -1
  56. package/dist/types/session/session-manager.d.ts +13 -0
  57. package/dist/types/slash-commands/builtin-registry.d.ts +1 -1
  58. package/dist/types/ssh/__tests__/connection-manager-args.test.d.ts +1 -0
  59. package/dist/types/ssh/__tests__/file-transfer-posix-guard.test.d.ts +1 -0
  60. package/dist/types/ssh/connection-manager.d.ts +2 -0
  61. package/dist/types/ssh/file-transfer.d.ts +79 -0
  62. package/dist/types/ssh/utils.d.ts +6 -0
  63. package/dist/types/system-prompt.d.ts +3 -0
  64. package/dist/types/task/executor.d.ts +16 -0
  65. package/dist/types/tiny/text.d.ts +1 -1
  66. package/dist/types/tools/builtin-names.d.ts +5 -1
  67. package/dist/types/tools/{find.d.ts → glob.d.ts} +15 -15
  68. package/dist/types/tools/{search.d.ts → grep.d.ts} +14 -14
  69. package/dist/types/tools/index.d.ts +3 -3
  70. package/dist/types/tools/path-utils.d.ts +29 -0
  71. package/dist/types/tools/read.d.ts +2 -2
  72. package/dist/types/tools/render-utils.d.ts +8 -0
  73. package/dist/types/tools/renderers.d.ts +11 -0
  74. package/dist/types/tools/ssh.d.ts +2 -0
  75. package/dist/types/tools/todo.d.ts +0 -16
  76. package/dist/types/tools/write.d.ts +2 -2
  77. package/dist/types/utils/active-repo-context.d.ts +8 -0
  78. package/dist/types/utils/image-resize.d.ts +1 -0
  79. package/dist/types/utils/markit-cache.d.ts +23 -0
  80. package/dist/types/utils/markit.d.ts +5 -1
  81. package/dist/types/utils/prompt-path.d.ts +1 -0
  82. package/dist/types/web/search/providers/duckduckgo.d.ts +14 -0
  83. package/dist/types/web/search/providers/firecrawl.d.ts +28 -0
  84. package/dist/types/web/search/providers/tinyfish.d.ts +29 -0
  85. package/dist/types/web/search/providers/xai.d.ts +13 -0
  86. package/dist/types/web/search/types.d.ts +18 -2
  87. package/package.json +30 -15
  88. package/scripts/build-binary.ts +9 -9
  89. package/scripts/bundle-dist.ts +2 -2
  90. package/src/advisor/__tests__/advisor.test.ts +40 -4
  91. package/src/advisor/__tests__/emission-guard.test.ts +147 -0
  92. package/src/advisor/advise-tool.ts +1 -1
  93. package/src/advisor/emission-guard.ts +172 -0
  94. package/src/advisor/index.ts +1 -0
  95. package/src/advisor/runtime.ts +11 -0
  96. package/src/advisor/watchdog.ts +12 -1
  97. package/src/cli/args.ts +5 -2
  98. package/src/cli/auth-broker-cli.ts +17 -0
  99. package/src/cli/config-cli.ts +4 -0
  100. package/src/cli/flag-tables.ts +7 -4
  101. package/src/cli/gallery-cli.ts +14 -2
  102. package/src/cli/gallery-fixtures/edit.ts +60 -0
  103. package/src/cli/gallery-fixtures/fs.ts +17 -17
  104. package/src/cli/gallery-fixtures/search.ts +4 -4
  105. package/src/cli/usage-cli.ts +20 -1
  106. package/src/cli/web-search-cli.ts +1 -1
  107. package/src/collab/display-name.ts +13 -0
  108. package/src/collab/guest.ts +5 -1
  109. package/src/collab/host.ts +2 -13
  110. package/src/commands/worktree.ts +6 -0
  111. package/src/config/inline-tool-descriptors-mode.ts +27 -0
  112. package/src/config/model-discovery.ts +77 -8
  113. package/src/config/model-registry.ts +89 -11
  114. package/src/config/model-resolver.ts +39 -1
  115. package/src/config/models-config-schema.ts +41 -5
  116. package/src/config/models-config.ts +4 -1
  117. package/src/config/settings-schema.ts +85 -27
  118. package/src/config/settings.ts +172 -1
  119. package/src/cursor.ts +1 -1
  120. package/src/dap/config.ts +152 -8
  121. package/src/dap/session.ts +1 -1
  122. package/src/discovery/helpers.ts +3 -1
  123. package/src/edit/hashline/diff.ts +14 -3
  124. package/src/edit/hashline/execute.ts +42 -6
  125. package/src/edit/hashline/filesystem.ts +38 -2
  126. package/src/edit/index.ts +1 -0
  127. package/src/edit/modes/patch.ts +11 -1
  128. package/src/edit/renderer.ts +140 -13
  129. package/src/eval/__tests__/agent-bridge.test.ts +101 -0
  130. package/src/eval/agent-bridge.ts +26 -3
  131. package/src/eval/js/tool-bridge.ts +2 -2
  132. package/src/export/html/index.ts +1 -1
  133. package/src/export/html/template.js +3 -1
  134. package/src/export/html/tool-views.generated.js +20 -20
  135. package/src/extensibility/extensions/types.ts +22 -22
  136. package/src/extensibility/hooks/types.ts +11 -11
  137. package/src/extensibility/plugins/legacy-pi-bundled-keys.ts +8 -3
  138. package/src/extensibility/plugins/legacy-pi-bundled-registry.ts +25 -10
  139. package/src/extensibility/plugins/marketplace/manager.ts +22 -0
  140. package/src/extensibility/plugins/marketplace/types.ts +1 -0
  141. package/src/internal-urls/__tests__/ssh-protocol.test.ts +331 -0
  142. package/src/internal-urls/docs-index.generated.txt +2 -2
  143. package/src/internal-urls/docs-index.ts +2 -3
  144. package/src/internal-urls/index.ts +1 -0
  145. package/src/internal-urls/registry-helpers.ts +19 -4
  146. package/src/internal-urls/router.ts +5 -3
  147. package/src/internal-urls/ssh-protocol.ts +367 -0
  148. package/src/internal-urls/types.ts +19 -2
  149. package/src/irc/bus.ts +11 -3
  150. package/src/mcp/tool-bridge.ts +32 -2
  151. package/src/memories/index.ts +1 -1
  152. package/src/modes/acp/acp-event-mapper.ts +2 -2
  153. package/src/modes/components/__tests__/move-overlay.test.ts +166 -0
  154. package/src/modes/components/agent-hub.ts +3 -0
  155. package/src/modes/components/agent-transcript-viewer.ts +9 -7
  156. package/src/modes/components/custom-editor.ts +90 -5
  157. package/src/modes/components/move-overlay.ts +282 -0
  158. package/src/modes/components/plan-review-overlay.ts +35 -35
  159. package/src/modes/components/plugin-selector.ts +6 -1
  160. package/src/modes/components/queue-mode-selector.ts +6 -1
  161. package/src/modes/components/select-list-mouse-routing.ts +35 -0
  162. package/src/modes/components/session-selector.ts +11 -10
  163. package/src/modes/components/settings-defs.ts +14 -1
  164. package/src/modes/components/settings-selector.ts +196 -29
  165. package/src/modes/components/show-images-selector.ts +6 -1
  166. package/src/modes/components/status-line/component.ts +108 -28
  167. package/src/modes/components/status-line/segments.ts +5 -1
  168. package/src/modes/components/status-line/types.ts +2 -0
  169. package/src/modes/components/theme-selector.ts +6 -1
  170. package/src/modes/components/thinking-selector.ts +6 -1
  171. package/src/modes/components/tool-execution.ts +25 -9
  172. package/src/modes/components/tree-selector.ts +5 -5
  173. package/src/modes/controllers/command-controller.ts +140 -47
  174. package/src/modes/controllers/event-controller.ts +59 -3
  175. package/src/modes/controllers/input-controller.ts +68 -2
  176. package/src/modes/controllers/selector-controller.ts +5 -2
  177. package/src/modes/controllers/streaming-reveal.ts +17 -0
  178. package/src/modes/controllers/tool-args-reveal.ts +1 -1
  179. package/src/modes/interactive-mode.ts +116 -50
  180. package/src/modes/internal-url-autocomplete.ts +17 -2
  181. package/src/modes/prompt-action-autocomplete.ts +3 -1
  182. package/src/modes/running-subagent-badge.ts +13 -0
  183. package/src/modes/setup-wizard/scenes/glyph.ts +2 -13
  184. package/src/modes/setup-wizard/scenes/providers.ts +2 -1
  185. package/src/modes/setup-wizard/scenes/theme.ts +6 -12
  186. package/src/modes/setup-wizard/scenes/web-search.ts +8 -13
  187. package/src/modes/setup-wizard/wizard-overlay.ts +6 -5
  188. package/src/modes/theme/theme.ts +9 -1
  189. package/src/modes/types.ts +9 -1
  190. package/src/modes/utils/interactive-context-helpers.ts +1 -1
  191. package/src/priority.json +15 -0
  192. package/src/prompts/advisor/active-repo-watchdog.md +6 -0
  193. package/src/prompts/advisor/system.md +21 -7
  194. package/src/prompts/agents/designer.md +1 -1
  195. package/src/prompts/agents/explore.md +1 -1
  196. package/src/prompts/agents/librarian.md +2 -2
  197. package/src/prompts/agents/plan.md +2 -2
  198. package/src/prompts/agents/reviewer.md +1 -1
  199. package/src/prompts/agents/task.md +2 -2
  200. package/src/prompts/system/active-repo-context.md +4 -0
  201. package/src/prompts/system/gemini-tool-call-reminder.md +9 -0
  202. package/src/prompts/system/plan-mode-active.md +3 -3
  203. package/src/prompts/system/project-prompt.md +2 -2
  204. package/src/prompts/system/system-prompt.md +6 -6
  205. package/src/prompts/tools/bash.md +2 -2
  206. package/src/prompts/tools/checkpoint.md +1 -1
  207. package/src/prompts/tools/{find.md → glob.md} +1 -1
  208. package/src/prompts/tools/{search.md → grep.md} +3 -3
  209. package/src/prompts/tools/read.md +4 -2
  210. package/src/sdk.ts +55 -25
  211. package/src/session/agent-session.ts +508 -132
  212. package/src/session/session-history-format.ts +2 -2
  213. package/src/session/session-manager.ts +46 -0
  214. package/src/slash-commands/builtin-registry.ts +138 -20
  215. package/src/slash-commands/helpers/usage-report.ts +23 -2
  216. package/src/ssh/__tests__/connection-manager-args.test.ts +69 -0
  217. package/src/ssh/__tests__/file-transfer-posix-guard.test.ts +68 -0
  218. package/src/ssh/connection-manager.ts +12 -15
  219. package/src/ssh/file-transfer.ts +209 -0
  220. package/src/ssh/utils.ts +24 -0
  221. package/src/system-prompt.ts +56 -28
  222. package/src/task/executor.ts +96 -36
  223. package/src/task/index.ts +3 -3
  224. package/src/task/render.ts +14 -13
  225. package/src/task/worktree.ts +38 -1
  226. package/src/tiny/text.ts +49 -4
  227. package/src/tiny/worker.ts +3 -3
  228. package/src/tools/bash.ts +2 -2
  229. package/src/tools/builtin-names.ts +26 -2
  230. package/src/tools/{find.ts → glob.ts} +48 -42
  231. package/src/tools/{search.ts → grep.ts} +311 -129
  232. package/src/tools/index.ts +13 -14
  233. package/src/tools/irc.ts +6 -2
  234. package/src/tools/path-utils.ts +64 -1
  235. package/src/tools/read.ts +35 -6
  236. package/src/tools/render-utils.ts +14 -0
  237. package/src/tools/renderers.ts +15 -4
  238. package/src/tools/ssh.ts +17 -2
  239. package/src/tools/todo.ts +4 -26
  240. package/src/tools/write.ts +25 -9
  241. package/src/utils/active-repo-context.ts +143 -0
  242. package/src/utils/image-resize.ts +88 -7
  243. package/src/utils/lang-from-path.ts +3 -3
  244. package/src/utils/markit-cache.ts +166 -0
  245. package/src/utils/markit.ts +86 -18
  246. package/src/utils/prompt-path.ts +3 -0
  247. package/src/utils/title-generator.ts +1 -1
  248. package/src/web/search/provider.ts +54 -34
  249. package/src/web/search/providers/duckduckgo.ts +140 -0
  250. package/src/web/search/providers/firecrawl.ts +144 -0
  251. package/src/web/search/providers/tinyfish.ts +159 -0
  252. package/src/web/search/providers/xai.ts +292 -0
  253. package/src/web/search/providers/zai.ts +142 -56
  254. package/src/web/search/types.ts +6 -2
package/src/dap/config.ts CHANGED
@@ -1,11 +1,38 @@
1
+ import * as fs from "node:fs";
2
+ import * as os from "node:os";
1
3
  import * as path from "node:path";
2
- import { isRecord } from "@oh-my-pi/pi-utils";
4
+ import { isRecord, logger } from "@oh-my-pi/pi-utils";
5
+ import { YAML } from "bun";
6
+ import { getConfigDirPaths } from "../config";
7
+ import { getPreloadedPluginRoots } from "../discovery/helpers";
3
8
  import { hasRootMarkers, resolveCommand } from "../lsp/config";
4
9
  import DEFAULTS from "./defaults.json" with { type: "json" };
5
10
  import type { DapAdapterConfig, DapResolvedAdapter } from "./types";
6
11
 
7
12
  const EXTENSIONLESS_DEBUGGER_ORDER = ["gdb", "lldb-dap"] as const;
8
13
 
14
+ interface NormalizedConfig {
15
+ adapters: Record<string, unknown>;
16
+ }
17
+
18
+ interface ConfigSource {
19
+ read(): NormalizedConfig | null;
20
+ }
21
+
22
+ function parseConfigContent(content: string, filePath: string): unknown {
23
+ const extension = path.extname(filePath).toLowerCase();
24
+ if (extension === ".yaml" || extension === ".yml") {
25
+ return YAML.parse(content) as unknown;
26
+ }
27
+ return JSON.parse(content) as unknown;
28
+ }
29
+
30
+ function normalizeConfig(value: unknown): NormalizedConfig | null {
31
+ if (!isRecord(value)) return null;
32
+ if (isRecord(value.adapters)) return { adapters: value.adapters };
33
+ return { adapters: value };
34
+ }
35
+
9
36
  function normalizeStringArray(value: unknown): string[] {
10
37
  if (!Array.isArray(value)) return [];
11
38
  return value.filter((entry): entry is string => typeof entry === "string" && entry.length > 0);
@@ -32,6 +59,15 @@ function normalizeAdapterConfig(config: unknown): DapAdapterConfig | null {
32
59
  };
33
60
  }
34
61
 
62
+ function readConfigFile(filePath: string): NormalizedConfig | null {
63
+ try {
64
+ const content = fs.readFileSync(filePath, "utf-8");
65
+ return normalizeConfig(parseConfigContent(content, filePath));
66
+ } catch {
67
+ return null;
68
+ }
69
+ }
70
+
35
71
  function getDefaults(): Record<string, DapAdapterConfig> {
36
72
  const adapters: Record<string, DapAdapterConfig> = {};
37
73
  for (const [name, config] of Object.entries(DEFAULTS)) {
@@ -45,14 +81,117 @@ function getDefaults(): Record<string, DapAdapterConfig> {
45
81
 
46
82
  const DEFAULT_ADAPTERS = getDefaults();
47
83
 
48
- export function getAdapterConfigs(): Record<string, DapAdapterConfig> {
49
- return { ...DEFAULT_ADAPTERS };
84
+ function mergeAdapters(
85
+ base: Record<string, DapAdapterConfig>,
86
+ overrides: Record<string, unknown>,
87
+ ): Record<string, DapAdapterConfig> {
88
+ const merged: Record<string, DapAdapterConfig> = { ...base };
89
+ for (const [name, config] of Object.entries(overrides)) {
90
+ const existing = merged[name];
91
+ const candidate =
92
+ isRecord(existing) && isRecord(config)
93
+ ? {
94
+ ...existing,
95
+ ...config,
96
+ launchDefaults:
97
+ isRecord(existing.launchDefaults) || isRecord(config.launchDefaults)
98
+ ? { ...existing.launchDefaults, ...normalizeObject(config.launchDefaults) }
99
+ : undefined,
100
+ attachDefaults:
101
+ isRecord(existing.attachDefaults) || isRecord(config.attachDefaults)
102
+ ? { ...existing.attachDefaults, ...normalizeObject(config.attachDefaults) }
103
+ : undefined,
104
+ }
105
+ : config;
106
+ const normalized = normalizeAdapterConfig(candidate);
107
+ if (normalized) {
108
+ merged[name] = normalized;
109
+ } else if (merged[name]) {
110
+ logger.warn("Ignoring invalid DAP adapter override (keeping previous config).", { name });
111
+ } else {
112
+ logger.warn("Ignoring invalid DAP adapter config.", { name });
113
+ }
114
+ }
115
+ return merged;
50
116
  }
51
117
 
52
- export function resolveAdapter(adapterName: string, cwd: string): DapResolvedAdapter | null {
53
- const config = DEFAULT_ADAPTERS[adapterName];
118
+ function fileConfigSource(filePath: string): ConfigSource {
119
+ return {
120
+ read: () => readConfigFile(filePath),
121
+ };
122
+ }
123
+
124
+ function getConfigSources(cwd: string): ConfigSource[] {
125
+ const filenames = ["dap.json", ".dap.json", "dap.yaml", ".dap.yaml", "dap.yml", ".dap.yml"];
126
+ const sources: ConfigSource[] = [];
127
+
128
+ for (const filename of filenames) {
129
+ sources.push(fileConfigSource(path.join(cwd, filename)));
130
+ }
131
+
132
+ const projectDirs = getConfigDirPaths("", { user: false, project: true, cwd });
133
+ for (const dir of projectDirs) {
134
+ for (const filename of filenames) {
135
+ sources.push(fileConfigSource(path.join(dir, filename)));
136
+ }
137
+ }
138
+
139
+ const userDirs = getConfigDirPaths("", { user: true, project: false });
140
+ for (const dir of userDirs) {
141
+ for (const filename of filenames) {
142
+ sources.push(fileConfigSource(path.join(dir, filename)));
143
+ }
144
+ }
145
+
146
+ const pluginRoots = getPreloadedPluginRoots();
147
+ for (const root of pluginRoots) {
148
+ for (const filename of filenames) {
149
+ sources.push(fileConfigSource(path.join(root.path, filename)));
150
+ }
151
+ }
152
+
153
+ for (const filename of filenames) {
154
+ sources.push(fileConfigSource(path.join(os.homedir(), filename)));
155
+ }
156
+
157
+ return sources;
158
+ }
159
+
160
+ function loadAdapterConfigs(cwd: string): Record<string, DapAdapterConfig> {
161
+ let adapters = { ...DEFAULT_ADAPTERS };
162
+ for (const source of getConfigSources(cwd).reverse()) {
163
+ const parsed = source.read();
164
+ if (!parsed) continue;
165
+ adapters = mergeAdapters(adapters, parsed.adapters);
166
+ }
167
+ return adapters;
168
+ }
169
+
170
+ export function getAdapterConfigs(cwd?: string): Record<string, DapAdapterConfig> {
171
+ return cwd ? loadAdapterConfigs(cwd) : { ...DEFAULT_ADAPTERS };
172
+ }
173
+
174
+ function normalizeCommandForCwd(command: string, cwd: string): string {
175
+ if (path.isAbsolute(command)) return command;
176
+ if (
177
+ command.startsWith("./") ||
178
+ command.startsWith("../") ||
179
+ command.startsWith(".\\") ||
180
+ command.startsWith("..\\")
181
+ ) {
182
+ return path.resolve(cwd, command);
183
+ }
184
+ return command;
185
+ }
186
+
187
+ function resolveAdapterFromConfig(
188
+ adapterName: string,
189
+ configs: Record<string, DapAdapterConfig>,
190
+ cwd: string,
191
+ ): DapResolvedAdapter | null {
192
+ const config = configs[adapterName];
54
193
  if (!config) return null;
55
- const resolvedCommand = resolveCommand(config.command, cwd);
194
+ const resolvedCommand = resolveCommand(normalizeCommandForCwd(config.command, cwd), cwd);
56
195
  if (!resolvedCommand) return null;
57
196
  return {
58
197
  name: adapterName,
@@ -69,9 +208,14 @@ export function resolveAdapter(adapterName: string, cwd: string): DapResolvedAda
69
208
  };
70
209
  }
71
210
 
211
+ export function resolveAdapter(adapterName: string, cwd: string): DapResolvedAdapter | null {
212
+ return resolveAdapterFromConfig(adapterName, getAdapterConfigs(cwd), cwd);
213
+ }
214
+
72
215
  export function getAvailableAdapters(cwd: string): DapResolvedAdapter[] {
73
- return Object.keys(DEFAULT_ADAPTERS)
74
- .map(name => resolveAdapter(name, cwd))
216
+ const configs = getAdapterConfigs(cwd);
217
+ return Object.keys(configs)
218
+ .map(name => resolveAdapterFromConfig(name, configs, cwd))
75
219
  .filter((adapter): adapter is DapResolvedAdapter => adapter !== null);
76
220
  }
77
221
 
@@ -289,7 +289,7 @@ export class DapSessionManager {
289
289
  ...(options.extraLaunchArguments ?? {}),
290
290
  program: options.program,
291
291
  cwd: options.cwd,
292
- args: options.args,
292
+ ...(options.args !== undefined ? { args: options.args } : {}),
293
293
  };
294
294
  // Subscribe to stop events BEFORE launching so we don't miss
295
295
  // stopOnEntry events that arrive before we start listening.
@@ -18,6 +18,7 @@ import { parseRuleConditionAndScope, type Rule, type RuleFrontmatter } from "../
18
18
  import type { Skill, SkillFrontmatter } from "../capability/skill";
19
19
  import type { LoadContext, LoadResult, SourceMeta } from "../capability/types";
20
20
  import { parseThinkingLevel } from "../thinking";
21
+ import { normalizeToolNames } from "../tools/builtin-names";
21
22
 
22
23
  import { buildPluginDirRoot } from "./plugin-dir-roots";
23
24
 
@@ -246,7 +247,8 @@ export function parseAgentFields(frontmatter: Record<string, unknown>): ParsedAg
246
247
  return null;
247
248
  }
248
249
 
249
- let tools = parseArrayOrCSV(frontmatter.tools)?.map(tool => tool.toLowerCase());
250
+ let tools = parseArrayOrCSV(frontmatter.tools);
251
+ if (tools) tools = normalizeToolNames(tools);
250
252
 
251
253
  // Subagents with explicit tool lists always need yield
252
254
  if (tools && !tools.includes("yield")) {
@@ -199,9 +199,15 @@ function buildStreamingSectionDiff(
199
199
  section: PatchSection,
200
200
  normalized: string,
201
201
  ): { diff: string; firstChangedLine: number | undefined } | { error: string } {
202
- const { edits } = parsePatchStreaming(section.diff);
202
+ const { edits, fileOp } = parsePatchStreaming(section.diff);
203
203
  const resolved = resolveBlockEdits(edits, normalized, section.path, nativeBlockResolver, { onUnresolved: "drop" });
204
- if (resolved.length === 0) return { error: `No changes would be made to ${section.path}.` };
204
+ if (resolved.length === 0) {
205
+ // A whole-file op (REM / MV) carries no line edits: the change is the
206
+ // delete/move itself, conveyed by the result header, so emit an empty
207
+ // diff rather than a misleading "No changes" error.
208
+ if (fileOp) return { diff: "", firstChangedLine: undefined };
209
+ return { error: `No changes would be made to ${section.path}.` };
210
+ }
205
211
 
206
212
  const fileLines = normalized.split("\n");
207
213
  const rows: string[] = [];
@@ -264,7 +270,12 @@ export async function computeHashlineSectionDiff(
264
270
  // (`streaming` unset) falls through to the real Myers diff below.
265
271
  if (options.streaming) return buildStreamingSectionDiff(section, normalized);
266
272
  const result = applyPreviewEdits({ section, absolutePath, normalized, snapshots, options });
267
- if (normalized === result.text) return { error: `No changes would be made to ${section.path}.` };
273
+ if (normalized === result.text) {
274
+ // REM/MV-only sections change no text; the header conveys the
275
+ // delete/move, so don't surface a "No changes" error.
276
+ if (section.fileOp) return { diff: "", firstChangedLine: undefined };
277
+ return { error: `No changes would be made to ${section.path}.` };
278
+ }
268
279
  return generateDiffString(normalized, result.text, undefined, { path: section.path });
269
280
  } catch (err) {
270
281
  return { error: err instanceof Error ? err.message : String(err) };
@@ -106,7 +106,28 @@ function formatBlockResolution(resolution: BlockResolution): string {
106
106
  return `${op} ${resolution.anchorLine} → resolved ${span} (${lines} line${lines === 1 ? "" : "s"})${suffix}`;
107
107
  }
108
108
 
109
- function renderSection(result: PatchSectionResult, diagnostics: FileDiagnosticsResult | undefined): RenderedSection {
109
+ function renderSection(
110
+ result: PatchSectionResult,
111
+ diagnostics: FileDiagnosticsResult | undefined,
112
+ sourcePath: string,
113
+ ): RenderedSection {
114
+ if (result.op === "delete") {
115
+ const toolResult: AgentToolResult<EditToolDetails, typeof hashlineEditParamsSchema> = {
116
+ content: [{ type: "text", text: `Deleted ${result.path}` }],
117
+ details: {
118
+ diff: "",
119
+ op: "delete",
120
+ path: result.path,
121
+ oldText: result.before,
122
+ meta: outputMeta().get(),
123
+ },
124
+ };
125
+ return {
126
+ toolResult,
127
+ perFileResult: { path: result.path, diff: "", op: "delete", oldText: result.before },
128
+ };
129
+ }
130
+
110
131
  if (result.op === "noop") {
111
132
  const toolResult: AgentToolResult<EditToolDetails, typeof hashlineEditParamsSchema> = {
112
133
  content: [{ type: "text", text: noChangeDiagnostic(result.path) }],
@@ -130,24 +151,39 @@ function renderSection(result: PatchSectionResult, diagnostics: FileDiagnosticsR
130
151
  result.blockResolutions && result.blockResolutions.length > 0
131
152
  ? `\n${result.blockResolutions.map(formatBlockResolution).join("\n")}`
132
153
  : "";
154
+ const moveBlock = result.moveDest ? `\nMoved to ${result.moveDest}` : "";
133
155
  const firstChangedLine = result.firstChangedLine ?? diff.firstChangedLine;
134
156
  return {
135
157
  toolResult: {
136
- content: [{ type: "text", text: `${result.header}${blockBlock}${previewBlock}${warningsBlock}` }],
158
+ content: [
159
+ {
160
+ type: "text",
161
+ text: `${result.header}${blockBlock}${moveBlock}${previewBlock}${warningsBlock}`,
162
+ },
163
+ ],
137
164
  details: {
138
165
  diff: diff.diff,
139
166
  firstChangedLine,
140
167
  diagnostics,
141
168
  op: result.op,
169
+ move: result.moveDest,
170
+ path: result.moveDest ?? result.path,
171
+ sourcePath: result.moveDest ? sourcePath : undefined,
172
+ oldText: result.before,
173
+ newText: result.after,
142
174
  meta,
143
175
  },
144
176
  },
145
177
  perFileResult: {
146
- path: result.path,
178
+ path: result.moveDest ?? result.path,
147
179
  diff: diff.diff,
148
180
  firstChangedLine,
149
181
  diagnostics,
150
182
  op: result.op,
183
+ move: result.moveDest,
184
+ sourcePath: result.moveDest ? sourcePath : undefined,
185
+ oldText: result.before,
186
+ newText: result.after,
151
187
  },
152
188
  };
153
189
  }
@@ -181,10 +217,10 @@ export async function executeHashlineSingle(
181
217
  if (escalate) {
182
218
  throw new ToolError(noChangeLoopDiagnostic(sectionResult.path, count));
183
219
  }
184
- return renderSection(sectionResult, undefined).toolResult;
220
+ return renderSection(sectionResult, undefined, prepared.section.path).toolResult;
185
221
  }
186
222
  resetNoopEdit(options.session, sectionResult.canonicalPath);
187
- return renderSection(sectionResult, fs.consumeDiagnostics(sectionResult.path)).toolResult;
223
+ return renderSection(sectionResult, fs.consumeDiagnostics(sectionResult.path), prepared.section.path).toolResult;
188
224
  }
189
225
 
190
226
  // Multi-section: prepare every section up front so we fail fast before
@@ -215,7 +251,7 @@ export async function executeHashlineSingle(
215
251
  : new ToolError(noChangeDiagnostic(sectionResult.path));
216
252
  }
217
253
  resetNoopEdit(options.session, sectionResult.canonicalPath);
218
- rendered.push(renderSection(sectionResult, fs.consumeDiagnostics(sectionResult.path)));
254
+ rendered.push(renderSection(sectionResult, fs.consumeDiagnostics(sectionResult.path), prepared[i].section.path));
219
255
  }
220
256
 
221
257
  return {
@@ -16,8 +16,9 @@
16
16
  * (batch request, diagnostics) lives on the instance and isn't safe to
17
17
  * share across concurrent edit tools.
18
18
  */
19
+ import * as fs from "node:fs/promises";
19
20
  import * as path from "node:path";
20
- import { Filesystem, NotFoundError, type WriteResult } from "@oh-my-pi/hashline";
21
+ import { Filesystem, NotFoundError, type PreflightWriteOptions, type WriteResult } from "@oh-my-pi/hashline";
21
22
  import { isEnoent } from "@oh-my-pi/pi-utils";
22
23
  import type { FileDiagnosticsResult, WritethroughCallback, WritethroughDeferredHandle } from "../../lsp";
23
24
  import type { ToolSession } from "../../tools";
@@ -122,10 +123,45 @@ export class HashlineFilesystem extends Filesystem {
122
123
  return content;
123
124
  }
124
125
 
125
- async preflightWrite(relativePath: string): Promise<void> {
126
+ async preflightWrite(relativePath: string, options?: PreflightWriteOptions): Promise<void> {
127
+ const fileOp = options?.fileOp;
128
+ if (fileOp?.kind === "rem") {
129
+ enforcePlanModeWrite(this.session, relativePath, { op: "delete" });
130
+ return;
131
+ }
132
+ if (fileOp?.kind === "move") {
133
+ enforcePlanModeWrite(this.session, relativePath, { op: "update", move: fileOp.dest });
134
+ return;
135
+ }
126
136
  enforcePlanModeWrite(this.session, relativePath, { op: "update" });
127
137
  }
128
138
 
139
+ async delete(relativePath: string): Promise<void> {
140
+ enforcePlanModeWrite(this.session, relativePath, { op: "delete" });
141
+ const absolutePath = this.resolveAbsolute(relativePath);
142
+ try {
143
+ await fs.rm(absolutePath);
144
+ } catch (error) {
145
+ if (isEnoent(error)) throw new NotFoundError(relativePath, error);
146
+ throw error;
147
+ }
148
+ invalidateFsScanAfterWrite(absolutePath);
149
+ }
150
+
151
+ async move(fromRelative: string, toRelative: string, content?: string): Promise<void> {
152
+ enforcePlanModeWrite(this.session, fromRelative, { op: "update", move: toRelative });
153
+ const fromAbsolute = this.resolveAbsolute(fromRelative);
154
+ const toAbsolute = this.resolveAbsolute(toRelative);
155
+ if (content !== undefined) {
156
+ await Bun.write(toAbsolute, content);
157
+ await fs.rm(fromAbsolute);
158
+ } else {
159
+ await fs.rename(fromAbsolute, toAbsolute);
160
+ }
161
+ invalidateFsScanAfterWrite(fromAbsolute);
162
+ invalidateFsScanAfterWrite(toAbsolute);
163
+ }
164
+
129
165
  async writeText(relativePath: string, content: string): Promise<WriteResult> {
130
166
  await this.preflightWrite(relativePath);
131
167
  const absolutePath = this.resolveAbsolute(relativePath);
package/src/edit/index.ts CHANGED
@@ -154,6 +154,7 @@ async function executeApplyPatchPerFile(
154
154
  diagnostics: details?.diagnostics,
155
155
  op: details?.op,
156
156
  move: details?.move,
157
+ sourcePath: details?.sourcePath,
157
158
  meta: details?.meta,
158
159
  oldText: details?.oldText,
159
160
  newText: details?.newText,
@@ -1849,12 +1849,21 @@ export async function executePatchSingle(
1849
1849
  diff: "",
1850
1850
  firstChangedLine: undefined,
1851
1851
  };
1852
- if (result.change.type === "update" && result.change.oldContent && result.change.newContent) {
1852
+ if (
1853
+ result.change.type === "update" &&
1854
+ result.change.oldContent !== undefined &&
1855
+ result.change.newContent !== undefined
1856
+ ) {
1853
1857
  const normalizedOld = normalizeToLF(stripBom(result.change.oldContent).text);
1854
1858
  const normalizedNew = normalizeToLF(stripBom(result.change.newContent).text);
1855
1859
  diffResult = generateUnifiedDiffString(normalizedOld, normalizedNew, undefined, {
1856
1860
  path: result.change.newPath ?? result.change.path,
1857
1861
  });
1862
+ } else if (result.change.type === "create" && result.change.newContent !== undefined) {
1863
+ // The result is authoritative for rendering, so emit the added-content
1864
+ // diff here rather than relying on the call-phase streaming preview.
1865
+ const normalizedNew = normalizeToLF(stripBom(result.change.newContent).text);
1866
+ diffResult = generateUnifiedDiffString("", normalizedNew, undefined, { path: result.change.path });
1858
1867
  }
1859
1868
 
1860
1869
  let resultText: string;
@@ -1896,6 +1905,7 @@ export async function executePatchSingle(
1896
1905
  diagnostics: mergedDiagnostics,
1897
1906
  op,
1898
1907
  move: effectiveRename,
1908
+ sourcePath: result.change.newPath ? resolvedPath : undefined,
1899
1909
  meta,
1900
1910
  oldText,
1901
1911
  newText,