@oh-my-pi/pi-coding-agent 13.3.7 → 13.3.8
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.
- package/CHANGELOG.md +82 -0
- package/package.json +9 -18
- package/scripts/format-prompts.ts +7 -172
- package/src/config/prompt-templates.ts +2 -54
- package/src/config/settings-schema.ts +24 -0
- package/src/discovery/codex.ts +1 -2
- package/src/discovery/helpers.ts +0 -5
- package/src/lsp/client.ts +8 -0
- package/src/lsp/config.ts +2 -3
- package/src/lsp/index.ts +379 -99
- package/src/lsp/render.ts +21 -31
- package/src/lsp/types.ts +21 -8
- package/src/lsp/utils.ts +193 -1
- package/src/mcp/config-writer.ts +3 -0
- package/src/modes/components/settings-defs.ts +9 -0
- package/src/modes/interactive-mode.ts +8 -1
- package/src/modes/theme/mermaid-cache.ts +4 -4
- package/src/modes/theme/theme.ts +33 -0
- package/src/prompts/system/subagent-user-prompt.md +2 -0
- package/src/prompts/system/system-prompt.md +12 -1
- package/src/prompts/tools/ast-find.md +20 -0
- package/src/prompts/tools/ast-replace.md +21 -0
- package/src/prompts/tools/bash.md +2 -0
- package/src/prompts/tools/hashline.md +26 -8
- package/src/prompts/tools/lsp.md +22 -5
- package/src/sdk.ts +11 -1
- package/src/session/agent-session.ts +261 -82
- package/src/task/executor.ts +8 -5
- package/src/tools/ast-find.ts +316 -0
- package/src/tools/ast-replace.ts +294 -0
- package/src/tools/bash.ts +2 -1
- package/src/tools/browser.ts +2 -8
- package/src/tools/fetch.ts +55 -18
- package/src/tools/index.ts +8 -0
- package/src/tools/path-utils.ts +34 -0
- package/src/tools/python.ts +2 -1
- package/src/tools/renderers.ts +4 -0
- package/src/tools/ssh.ts +2 -1
- package/src/tools/todo-write.ts +34 -0
- package/src/tools/tool-timeouts.ts +29 -0
- package/src/utils/mime.ts +37 -14
- package/src/utils/prompt-format.ts +172 -0
- package/src/web/scrapers/arxiv.ts +12 -12
- package/src/web/scrapers/go-pkg.ts +2 -2
- package/src/web/scrapers/iacr.ts +17 -9
- package/src/web/scrapers/readthedocs.ts +3 -3
- package/src/web/scrapers/twitter.ts +11 -11
- package/src/web/scrapers/wikipedia.ts +4 -5
- 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.
|
|
4
|
+
"version": "13.3.8",
|
|
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.
|
|
45
|
-
"@oh-my-pi/pi-agent-core": "13.3.
|
|
46
|
-
"@oh-my-pi/pi-ai": "13.3.
|
|
47
|
-
"@oh-my-pi/pi-natives": "13.3.
|
|
48
|
-
"@oh-my-pi/pi-tui": "13.3.
|
|
49
|
-
"@oh-my-pi/pi-utils": "13.3.
|
|
44
|
+
"@oh-my-pi/omp-stats": "13.3.8",
|
|
45
|
+
"@oh-my-pi/pi-agent-core": "13.3.8",
|
|
46
|
+
"@oh-my-pi/pi-ai": "13.3.8",
|
|
47
|
+
"@oh-my-pi/pi-natives": "13.3.8",
|
|
48
|
+
"@oh-my-pi/pi-tui": "13.3.8",
|
|
49
|
+
"@oh-my-pi/pi-utils": "13.3.8",
|
|
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
|
-
"
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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 =
|
|
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
|
|
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,
|
package/src/discovery/codex.ts
CHANGED
|
@@ -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
|
|
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;
|
package/src/discovery/helpers.ts
CHANGED
|
@@ -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
|
|
162
|
-
|
|
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 {
|