@oh-my-pi/pi-coding-agent 13.3.7 → 13.3.9

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 (49) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/package.json +9 -18
  3. package/scripts/format-prompts.ts +7 -172
  4. package/src/config/prompt-templates.ts +2 -54
  5. package/src/config/settings-schema.ts +24 -0
  6. package/src/discovery/codex.ts +1 -2
  7. package/src/discovery/helpers.ts +0 -5
  8. package/src/lsp/client.ts +8 -0
  9. package/src/lsp/config.ts +2 -3
  10. package/src/lsp/index.ts +379 -99
  11. package/src/lsp/render.ts +21 -31
  12. package/src/lsp/types.ts +21 -8
  13. package/src/lsp/utils.ts +193 -1
  14. package/src/mcp/config-writer.ts +3 -0
  15. package/src/modes/components/settings-defs.ts +9 -0
  16. package/src/modes/interactive-mode.ts +8 -1
  17. package/src/modes/theme/mermaid-cache.ts +4 -4
  18. package/src/modes/theme/theme.ts +33 -0
  19. package/src/prompts/system/subagent-user-prompt.md +2 -0
  20. package/src/prompts/system/system-prompt.md +12 -1
  21. package/src/prompts/tools/ast-find.md +20 -0
  22. package/src/prompts/tools/ast-replace.md +21 -0
  23. package/src/prompts/tools/bash.md +2 -0
  24. package/src/prompts/tools/hashline.md +26 -8
  25. package/src/prompts/tools/lsp.md +22 -5
  26. package/src/sdk.ts +11 -1
  27. package/src/session/agent-session.ts +261 -82
  28. package/src/task/executor.ts +8 -5
  29. package/src/tools/ast-find.ts +316 -0
  30. package/src/tools/ast-replace.ts +294 -0
  31. package/src/tools/bash.ts +2 -1
  32. package/src/tools/browser.ts +2 -8
  33. package/src/tools/fetch.ts +55 -18
  34. package/src/tools/index.ts +8 -0
  35. package/src/tools/path-utils.ts +34 -0
  36. package/src/tools/python.ts +2 -1
  37. package/src/tools/renderers.ts +4 -0
  38. package/src/tools/ssh.ts +2 -1
  39. package/src/tools/todo-write.ts +34 -0
  40. package/src/tools/tool-timeouts.ts +29 -0
  41. package/src/utils/mime.ts +37 -14
  42. package/src/utils/prompt-format.ts +172 -0
  43. package/src/web/scrapers/arxiv.ts +12 -12
  44. package/src/web/scrapers/go-pkg.ts +2 -2
  45. package/src/web/scrapers/iacr.ts +17 -9
  46. package/src/web/scrapers/readthedocs.ts +3 -3
  47. package/src/web/scrapers/twitter.ts +11 -11
  48. package/src/web/scrapers/wikipedia.ts +4 -5
  49. package/src/utils/ignore-files.ts +0 -119
package/CHANGELOG.md CHANGED
@@ -2,6 +2,88 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [13.3.8] - 2026-02-28
6
+
7
+ ### Added
8
+
9
+ - Added `ast_find` tool for structural code search using AST matching via ast-grep, enabling syntax-aware pattern discovery across codebases
10
+ - Added `ast_replace` tool for structural AST-aware rewrites via ast-grep, enabling safe syntax-level codemods without text-based fragility
11
+ - Added `astFind.enabled` and `astReplace.enabled` settings to control availability of AST tools
12
+ - Added system prompt guidance to prefer AST tools over bash text manipulation (grep/sed/awk/perl) for syntax-aware operations
13
+ - Extracted prompt formatting logic into reusable `formatPromptContent()` utility with configurable render phases and formatting options
14
+ - Added `type_definition` action to navigate to symbol type definitions with source context
15
+ - Added `implementation` action to find concrete implementations of symbols with source context
16
+ - Added `code_actions` action to list and apply language server code fixes, refactors, and import suggestions
17
+ - Added `symbol` parameter to automatically resolve column position by searching for substring on target line
18
+ - Added `occurrence` parameter to disambiguate repeated `symbol` matches on the same line
19
+ - Added source code context display (3 lines) for definition, type definition, and implementation results
20
+ - Added context display for first 50 references with remaining references shown location-only to balance detail and performance
21
+ - Added support for glob patterns in `file` parameter for diagnostics action (e.g., `src/**/*.ts`)
22
+ - Added `waitForIdle()` method to ensure prompt completion waits for all deferred recovery work (TTSR continuations, context promotions, compaction retries) to fully settle
23
+ - Added `getLastAssistantMessage()` method to retrieve the most recent assistant message from session state without manual array indexing
24
+ - Implemented TTSR resume gate to ensure `prompt()` blocks until TTSR interrupt continuations complete, preventing race conditions between TTSR injections and subsequent prompts
25
+ - Added `tools.maxTimeout` setting to enforce a global timeout ceiling across all tool calls
26
+
27
+ ### Changed
28
+
29
+ - Replaced `globSync` from `glob` package with native `Bun.Glob` API for glob pattern matching
30
+ - Replaced `fileTypeFromBuffer` from `file-type` package with inline MIME type detection for JPEG, PNG, GIF, and WebP formats
31
+ - Reduced MIME type sniffing buffer size from 4100 bytes to 12 bytes for improved performance
32
+ - Changed mermaid cache key type from `string` to `bigint` for more efficient hashing
33
+ - Replaced `smol-toml` dependency with native `Bun.TOML.parse()` for TOML parsing, reducing external dependencies
34
+ - Replaced `node-html-parser` dependency with `linkedom` for HTML parsing, improving performance and reducing bundle size
35
+ - Updated HTML parsing API calls from `node-html-parser` to `linkedom` across all web scrapers (arXiv, IACR, Go pkg, Read the Docs, Twitter, Wikipedia)
36
+ - Changed element text extraction from `.text` property to `.textContent` property for compatibility with linkedom DOM API
37
+ - Optimized document link extraction to use regex-based parsing with deduplication and a 20-link limit instead of full DOM traversal
38
+ - Unified `path` parameter in ast_find and ast_replace tools to accept files, directories, or glob patterns directly, eliminating the separate `glob` parameter
39
+ - Removed `strictness` parameter from ast_find and ast_replace tools
40
+ - Removed `fail_on_parse_error` parameter from ast_replace tool (now always false)
41
+ - Updated ast_find and ast_replace prompt guidance to clarify that `path` accepts glob patterns and no longer requires separate glob specification
42
+ - Refactored prompt template rendering to use unified `formatPromptContent()` function with phase-aware formatting (pre-render vs post-render)
43
+ - Updated `format-prompts.ts` script to use centralized prompt formatting utility instead of inline implementation
44
+ - Replaced `column` parameter with `symbol` parameter for more intuitive position specification
45
+ - Removed `files` parameter; use glob patterns in `file` parameter instead
46
+ - Removed `end_line` and `end_character` parameters; range operations now use single position
47
+ - Changed `include_declaration` parameter to always be true for references (removed from API)
48
+ - Updated LSP client capabilities to advertise support for `typeDefinition` and `implementation` requests
49
+ - Improved definition results to include source context alongside location information
50
+ - Refactored deferred continuation scheduling to use centralized post-prompt task tracking instead of raw `setTimeout()` calls, improving reliability of concurrent recovery operations
51
+ - Updated subagent executor to explicitly await `waitForIdle()` after each prompt and reminder, ensuring terminal assistant state is determined only after all background work completes
52
+ - Replaced `#waitForRetry()` with `#waitForPostPromptRecovery()` to handle both retry and TTSR resume gates, ensuring prompt completion waits for all post-prompt recovery operations
53
+ - Introduced structured post-prompt recovery task tracking in `AgentSession` and added explicit session completion APIs (`waitForIdle()`, `getLastAssistantMessage()`) for callers that need deterministic turn finalization
54
+ - Updated intent field parameter name from `agent__intent` to `_i` for cleaner tool call contracts
55
+ - Refined intent parameter guidance to require concise 2-6 word sentences in present participle form
56
+ - Centralized per-tool timeout constants and clamping into `tool-timeouts.ts`
57
+
58
+ ### Removed
59
+
60
+ - Removed `file-type` dependency, reducing external dependencies
61
+ - Removed `glob` dependency in favor of native `Bun.Glob` API
62
+ - Removed `ignore` dependency and ignore file handling utilities
63
+ - Removed `marked` dependency
64
+ - Removed `zod` dependency
65
+ - Removed `ms` and `@types/ms` dev dependencies
66
+ - Removed `rootDir` and `ignoreMatcher` parameters from `loadFilesFromDir()` (kept for API compatibility)
67
+ - Removed `smol-toml` dependency from package.json
68
+ - Removed `node-html-parser` dependency from package.json
69
+ - Removed `files` array parameter for batch file operations
70
+ - Removed `column`, `end_line`, and `end_character` parameters in favor of symbol-based positioning
71
+ - Removed `include_declaration` parameter from references action
72
+
73
+ ### Fixed
74
+
75
+ - Fixed TTSR violations during subagent execution aborting the entire subagent run; `#waitForPostPromptRecovery()` now also awaits agent idle after TTSR/retry gates resolve, preventing `prompt()` from returning while a fire-and-forget `agent.continue()` is still streaming
76
+ - Fixed deferred TTSR/context-promotion continuations still racing `prompt()` completion by tracking compaction checks and deferred `agent.continue()` tasks under a shared post-prompt recovery orchestrator
77
+ - Fixed subagent reminder/finalization sequencing to await session-level idle recovery between prompts before determining terminal assistant stop state
78
+ - Fixed `code_actions` apply mode to execute command-based actions via `workspace/executeCommand`
79
+ - Fixed diagnostics glob detection to recognize bracket character class patterns (e.g., `src/[ab].ts`)
80
+ - Fixed LSP render metadata sanitization for `symbol` values to prevent tab/newline layout breakage
81
+ - Fixed LSP diagnostics glob requests that appeared stuck by capping glob expansion and shortening per-file diagnostic waits in batch mode
82
+ - Fixed workspace symbol search to query all configured LSP servers and filter out non-matching results
83
+ - Fixed `references`/`rename`/`hover` symbol targeting to error when `symbol` is missing on the line or `occurrence` is out of bounds
84
+ - Fixed `reload` without a file to reload all active configured language servers instead of only the first server
85
+ - Fixed `todo_write` task normalization to auto-activate the first remaining task and include explicit remaining-items output in tool results, removing the need for an immediate follow-up start update
86
+
5
87
  ## [13.3.7] - 2026-02-27
6
88
  ### Breaking Changes
7
89
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-coding-agent",
4
- "version": "13.3.7",
4
+ "version": "13.3.9",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/can1357/oh-my-pi",
7
7
  "author": "Can Boluk",
@@ -41,32 +41,23 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@mozilla/readability": "^0.6",
44
- "@oh-my-pi/omp-stats": "13.3.7",
45
- "@oh-my-pi/pi-agent-core": "13.3.7",
46
- "@oh-my-pi/pi-ai": "13.3.7",
47
- "@oh-my-pi/pi-natives": "13.3.7",
48
- "@oh-my-pi/pi-tui": "13.3.7",
49
- "@oh-my-pi/pi-utils": "13.3.7",
44
+ "@oh-my-pi/omp-stats": "13.3.9",
45
+ "@oh-my-pi/pi-agent-core": "13.3.9",
46
+ "@oh-my-pi/pi-ai": "13.3.9",
47
+ "@oh-my-pi/pi-natives": "13.3.9",
48
+ "@oh-my-pi/pi-tui": "13.3.9",
49
+ "@oh-my-pi/pi-utils": "13.3.9",
50
50
  "@sinclair/typebox": "^0.34",
51
51
  "@xterm/headless": "^6.0",
52
52
  "ajv": "^8.18",
53
53
  "chalk": "^5.6",
54
54
  "diff": "^8.0",
55
- "file-type": "^21.3",
56
- "glob": "^13.0",
57
55
  "handlebars": "^4.7",
58
- "ignore": "^7.0",
59
56
  "linkedom": "^0.18",
60
- "marked": "^17.0",
61
- "node-html-parser": "^7.0",
62
- "puppeteer": "^24.37",
63
- "smol-toml": "^1.6",
64
- "zod": "^4.3"
57
+ "puppeteer": "^24.37"
65
58
  },
66
59
  "devDependencies": {
67
- "@types/bun": "^1.3",
68
- "@types/ms": "^2.1",
69
- "ms": "^2.1"
60
+ "@types/bun": "^1.3"
70
61
  },
71
62
  "engines": {
72
63
  "bun": ">=1.3.7"
@@ -14,6 +14,7 @@
14
14
  * 9. Bold RFC 2119 keywords (MUST, SHOULD, MAY, etc.) in prompt content
15
15
  */
16
16
  import { Glob } from "bun";
17
+ import { formatPromptContent } from "../src/utils/prompt-format";
17
18
 
18
19
  const PROMPTS_DIR = new URL("../src/prompts/", import.meta.url).pathname;
19
20
  const COMMIT_PROMPTS_DIR = new URL("../src/commit/prompts/", import.meta.url).pathname;
@@ -21,177 +22,11 @@ const AGENTIC_PROMPTS_DIR = new URL("../src/commit/agentic/prompts/", import.met
21
22
 
22
23
  const PROMPT_DIRS = [PROMPTS_DIR, COMMIT_PROMPTS_DIR, AGENTIC_PROMPTS_DIR];
23
24
 
24
- // Opening XML tag (not self-closing, not closing)
25
- const OPENING_XML = /^<([a-z_-]+)(?:\s+[^>]*)?>$/;
26
- // Closing XML tag
27
- const CLOSING_XML = /^<\/([a-z_-]+)>$/;
28
- // Handlebars block start: {{#if}}, {{#has}}, {{#list}}, etc.
29
- const OPENING_HBS = /^\{\{#/;
30
- // Handlebars block end: {{/if}}, {{/has}}, {{/list}}, etc.
31
- const CLOSING_HBS = /^\{\{\//;
32
- // List item (- or * or 1.)
33
- const LIST_ITEM = /^[-*]|\d+\.\s/;
34
- // Code fence
35
- const CODE_FENCE = /^```/;
36
- // Table row
37
- const TABLE_ROW = /^\|.*\|$/;
38
- // Table separator (|---|---|)
39
- const TABLE_SEP = /^\|[-:\s|]+\|$/;
40
-
41
- /** RFC 2119 keywords used in prompts. */
42
- const RFC2119_KEYWORDS = /\b(?:MUST NOT|SHOULD NOT|SHALL NOT|RECOMMENDED|REQUIRED|OPTIONAL|SHOULD|SHALL|MUST|MAY)\b/g;
43
-
44
- function boldRfc2119Keywords(line: string): string {
45
- return line.replace(RFC2119_KEYWORDS, (match, offset, source) => {
46
- const isAlreadyBold =
47
- source[offset - 2] === "*" &&
48
- source[offset - 1] === "*" &&
49
- source[offset + match.length] === "*" &&
50
- source[offset + match.length + 1] === "*";
51
- if (isAlreadyBold) {
52
- return match;
53
- }
54
- return `**${match}**`;
55
- });
56
- }
57
-
58
- /** Compact a table row by trimming cell padding */
59
- function compactTableRow(line: string): string {
60
- // Split by |, trim each cell, rejoin
61
- const cells = line.split("|");
62
- return cells.map((c) => c.trim()).join("|");
63
- }
64
-
65
- /** Compact a table separator row */
66
- function compactTableSep(line: string): string {
67
- // Normalize to minimal |---|---|
68
- const cells = line.split("|").filter((c) => c.trim());
69
- const normalized = cells.map((c) => {
70
- const trimmed = c.trim();
71
- // Preserve alignment markers
72
- const left = trimmed.startsWith(":");
73
- const right = trimmed.endsWith(":");
74
- if (left && right) return ":---:";
75
- if (left) return ":---";
76
- if (right) return "---:";
77
- return "---";
78
- });
79
- return "|" + normalized.join("|") + "|";
80
- }
81
-
82
- function formatPrompt(content: string): string {
83
- const lines = content.split("\n");
84
- const result: string[] = [];
85
- let inCodeBlock = false;
86
- // Stack of tag names whose opening tag was at column 0 (top-level)
87
- const topLevelTags: string[] = [];
88
-
89
- for (let i = 0; i < lines.length; i++) {
90
- let line = lines[i].trimEnd();
91
-
92
- const trimmed = line.trimStart();
93
-
94
- // Track code blocks - don't modify inside them
95
- if (CODE_FENCE.test(trimmed)) {
96
- inCodeBlock = !inCodeBlock;
97
- result.push(line);
98
- continue;
99
- }
100
-
101
- if (inCodeBlock) {
102
- result.push(line);
103
- continue;
104
- }
105
-
106
- // Replace common ascii ellipsis and arrow patterns with their unicode equivalents
107
- line = line
108
- .replace(/\.{3}/g, "…")
109
- .replace(/->/g, "→")
110
- .replace(/<-/g, "←")
111
- .replace(/<->/g, "↔")
112
- .replace(/!=/g, "≠")
113
- .replace(/<=/g, "≤")
114
- .replace(/>=/g, "≥");
115
-
116
- // Track top-level XML opening tags for depth-aware indent stripping
117
- const isOpeningXml = OPENING_XML.test(trimmed) && !trimmed.endsWith("/>");
118
- if (isOpeningXml && line.length === trimmed.length) {
119
- // Opening tag at column 0 — track as top-level
120
- const match = OPENING_XML.exec(trimmed);
121
- if (match) topLevelTags.push(match[1]);
122
- }
123
-
124
- // Strip leading whitespace from top-level closing XML tags and Handlebars
125
- const closingMatch = CLOSING_XML.exec(trimmed);
126
- if (closingMatch) {
127
- const tagName = closingMatch[1];
128
- if (topLevelTags.length > 0 && topLevelTags[topLevelTags.length - 1] === tagName) {
129
- // Closing tag matches a top-level opener — strip indent
130
- line = trimmed;
131
- topLevelTags.pop();
132
- } else {
133
- line = line.trimEnd();
134
- }
135
- } else if (trimmed.startsWith("{{")) {
136
- line = trimmed;
137
- } else if (TABLE_SEP.test(trimmed)) {
138
- // Compact table separator
139
- line = compactTableSep(trimmed);
140
- } else if (TABLE_ROW.test(trimmed)) {
141
- // Compact table row
142
- line = compactTableRow(trimmed);
143
- } else {
144
- // Trim trailing whitespace (preserve leading for non-closing-tags)
145
- line = line.trimEnd();
146
- }
147
- line = boldRfc2119Keywords(line);
148
-
149
- const isBlank = trimmed === "";
150
-
151
- // Skip blank lines that violate our rules
152
- if (isBlank) {
153
- const prevLine = result[result.length - 1]?.trim() ?? "";
154
- const nextLine = lines[i + 1]?.trim() ?? "";
155
-
156
- // Rule 1: No blank line before list items
157
- if (LIST_ITEM.test(nextLine)) {
158
- continue;
159
- }
160
-
161
- // Rule 2: No blank after opening XML tag or Handlebars block
162
- if (OPENING_XML.test(prevLine) || OPENING_HBS.test(prevLine)) {
163
- continue;
164
- }
165
-
166
- // Rule 3: No blank before closing XML tag or Handlebars block
167
- if (CLOSING_XML.test(nextLine) || CLOSING_HBS.test(nextLine)) {
168
- continue;
169
- }
170
-
171
- // Rule 4: Collapse multiple blank lines
172
- const prevIsBlank = prevLine === "";
173
- if (prevIsBlank) {
174
- continue;
175
- }
176
- }
177
-
178
- // Rule 3 (cleanup): Remove trailing blanks before closing tag
179
- if (CLOSING_XML.test(trimmed) || CLOSING_HBS.test(trimmed)) {
180
- while (result.length > 0 && result[result.length - 1].trim() === "") {
181
- result.pop();
182
- }
183
- }
184
-
185
- result.push(line);
186
- }
187
-
188
- // Rule 8: No trailing newline at EOF
189
- while (result.length > 0 && result[result.length - 1].trim() === "") {
190
- result.pop();
191
- }
192
-
193
- return result.join("\n");
194
- }
25
+ const PROMPT_FORMAT_OPTIONS = {
26
+ renderPhase: "pre-render",
27
+ replaceAsciiSymbols: true,
28
+ boldRfc2119Keywords: true,
29
+ } as const;
195
30
 
196
31
  async function main() {
197
32
  const glob = new Glob("**/*.md");
@@ -207,7 +42,7 @@ async function main() {
207
42
 
208
43
  for (const fullPath of files) {
209
44
  const original = await Bun.file(fullPath).text();
210
- const formatted = formatPrompt(original);
45
+ const formatted = formatPromptContent(original, PROMPT_FORMAT_OPTIONS);
211
46
 
212
47
  if (original !== formatted) {
213
48
  if (check) {
@@ -6,6 +6,7 @@ import { computeLineHash } from "../patch/hashline";
6
6
  import { jtdToTypeScript } from "../tools/jtd-to-typescript";
7
7
  import { parseCommandArgs, substituteArgs } from "../utils/command-args";
8
8
  import { parseFrontmatter } from "../utils/frontmatter";
9
+ import { formatPromptContent } from "../utils/prompt-format";
9
10
 
10
11
  /**
11
12
  * Represents a prompt template loaded from a markdown file
@@ -276,60 +277,7 @@ handlebars.registerHelper("hlinefull", (lineNum: unknown, content: unknown): str
276
277
  export function renderPromptTemplate(template: string, context: TemplateContext = {}): string {
277
278
  const compiled = handlebars.compile(template, { noEscape: true, strict: false });
278
279
  const rendered = compiled(context ?? {});
279
- return optimizePromptLayout(rendered);
280
- }
281
-
282
- function optimizePromptLayout(input: string): string {
283
- // 1) strip CR / normalize line endings
284
- let s = input.replace(/\r\n?/g, "\n");
285
-
286
- // normalize NBSP -> space
287
- s = s.replace(/\u00A0/g, " ");
288
-
289
- const lines = s.split("\n").map(line => {
290
- // 2) remove trailing whitespace (spaces/tabs) per line
291
- let l = line.replace(/[ \t]+$/g, "");
292
-
293
- // 3) lines with only whitespace -> empty line
294
- if (/^[ \t]*$/.test(l)) return "";
295
-
296
- // 4) normalize leading indentation: every 2 spaces -> \t (preserve leftover 1 space)
297
- // NOTE: This is intentionally *only* leading indentation to avoid mangling prose.
298
- const m = l.match(/^[ \t]+/);
299
- if (m) {
300
- const indent = m[0];
301
- const rest = l.slice(indent.length);
302
-
303
- let out = "";
304
- let spaces = 0;
305
-
306
- for (const ch of indent) {
307
- if (ch === "\t") {
308
- // flush pending spaces before existing tab
309
- out += "\t".repeat(Math.floor(spaces / 2));
310
- if (spaces % 2) out += " ";
311
- spaces = 0;
312
- out += "\t";
313
- } else {
314
- spaces++;
315
- }
316
- }
317
-
318
- out += "\t".repeat(Math.floor(spaces / 2));
319
- if (spaces % 2) out += " ";
320
-
321
- l = out + rest;
322
- }
323
-
324
- return l;
325
- });
326
-
327
- s = lines.join("\n");
328
-
329
- // 5) collapse excessive blank lines
330
- s = s.replace(/\n{3,}/g, "\n\n");
331
-
332
- return s.trim();
280
+ return formatPromptContent(rendered, { renderPhase: "post-render" });
333
281
  }
334
282
 
335
283
  /**
@@ -455,6 +455,20 @@ export const SETTINGS_SCHEMA = {
455
455
  submenu: true,
456
456
  },
457
457
  },
458
+ "astFind.enabled": {
459
+ type: "boolean",
460
+ default: true,
461
+ ui: { tab: "tools", label: "Enable AST Find", description: "Enable the ast_find tool for structural AST search" },
462
+ },
463
+ "astReplace.enabled": {
464
+ type: "boolean",
465
+ default: true,
466
+ ui: {
467
+ tab: "tools",
468
+ label: "Enable AST Replace",
469
+ description: "Enable the ast_replace tool for structural AST rewrites",
470
+ },
471
+ },
458
472
  "notebook.enabled": {
459
473
  type: "boolean",
460
474
  default: true,
@@ -511,6 +525,16 @@ export const SETTINGS_SCHEMA = {
511
525
  description: "Ask the agent to describe the intent of each tool call before executing it",
512
526
  },
513
527
  },
528
+ "tools.maxTimeout": {
529
+ type: "number",
530
+ default: 0,
531
+ ui: {
532
+ tab: "tools",
533
+ label: "Max tool timeout",
534
+ description: "Maximum timeout in seconds the agent can set for any tool (0 = no limit)",
535
+ submenu: true,
536
+ },
537
+ },
514
538
  "async.enabled": {
515
539
  type: "boolean",
516
540
  default: false,
@@ -8,7 +8,6 @@
8
8
  */
9
9
  import * as path from "node:path";
10
10
  import { logger } from "@oh-my-pi/pi-utils";
11
- import { parse as parseToml } from "smol-toml";
12
11
  import { registerProvider } from "../capability";
13
12
  import type { ContextFile } from "../capability/context-file";
14
13
  import { contextFileCapability } from "../capability/context-file";
@@ -116,7 +115,7 @@ async function loadTomlConfig(_ctx: LoadContext, path: string): Promise<Record<s
116
115
  if (!content) return null;
117
116
 
118
117
  try {
119
- return parseToml(content) as Record<string, unknown>;
118
+ return Bun.TOML.parse(content) as Record<string, unknown>;
120
119
  } catch (error) {
121
120
  logger.warn("Failed to parse TOML config", { path, error: String(error) });
122
121
  return null;
@@ -8,7 +8,6 @@ import { parseRuleConditionAndScope, type Rule, type RuleFrontmatter } from "../
8
8
  import type { Skill, SkillFrontmatter } from "../capability/skill";
9
9
  import type { LoadContext, LoadResult, SourceMeta } from "../capability/types";
10
10
  import { parseFrontmatter } from "../utils/frontmatter";
11
- import type { IgnoreMatcher } from "../utils/ignore-files";
12
11
 
13
12
  const VALID_THINKING_LEVELS: readonly string[] = ["off", "minimal", "low", "medium", "high", "xhigh"];
14
13
 
@@ -378,10 +377,6 @@ export async function loadFilesFromDir<T>(
378
377
  transform: (name: string, content: string, path: string, source: SourceMeta) => T | null;
379
378
  /** Whether to recurse into subdirectories (default: false) */
380
379
  recursive?: boolean;
381
- /** Root directory for ignore file handling (unused, kept for API compat) */
382
- rootDir?: string;
383
- /** Ignore matcher (unused, kept for API compat) */
384
- ignoreMatcher?: IgnoreMatcher;
385
380
  },
386
381
  ): Promise<LoadResult<T>> {
387
382
  const items: T[] = [];
package/src/lsp/client.ts CHANGED
@@ -80,6 +80,14 @@ const CLIENT_CAPABILITIES = {
80
80
  dynamicRegistration: false,
81
81
  linkSupport: true,
82
82
  },
83
+ typeDefinition: {
84
+ dynamicRegistration: false,
85
+ linkSupport: true,
86
+ },
87
+ implementation: {
88
+ dynamicRegistration: false,
89
+ linkSupport: true,
90
+ },
83
91
  references: {
84
92
  dynamicRegistration: false,
85
93
  },
package/src/lsp/config.ts CHANGED
@@ -3,7 +3,6 @@ import * as os from "node:os";
3
3
  import * as path from "node:path";
4
4
  import { isRecord, logger } from "@oh-my-pi/pi-utils";
5
5
  import { YAML } from "bun";
6
- import { globSync } from "glob";
7
6
  import { getConfigDirPaths } from "../config";
8
7
  import { BiomeClient } from "./clients/biome-client";
9
8
  import { SwiftLintClient } from "./clients/swiftlint-client";
@@ -158,8 +157,8 @@ export function hasRootMarkers(cwd: string, markers: string[]): boolean {
158
157
  // Handle glob-like patterns (e.g., "*.cabal")
159
158
  if (marker.includes("*")) {
160
159
  try {
161
- const matches = globSync(path.join(cwd, marker));
162
- if (matches.length > 0) {
160
+ const scan = new Bun.Glob(marker).scanSync({ cwd, onlyFiles: false });
161
+ for (const _ of scan) {
163
162
  return true;
164
163
  }
165
164
  } catch {