@oh-my-pi/pi-coding-agent 14.5.13 → 14.6.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 (105) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/package.json +7 -7
  3. package/src/autoresearch/command-resume.md +5 -8
  4. package/src/autoresearch/git.ts +41 -51
  5. package/src/autoresearch/helpers.ts +43 -359
  6. package/src/autoresearch/index.ts +281 -273
  7. package/src/autoresearch/prompt-setup.md +43 -0
  8. package/src/autoresearch/prompt.md +52 -193
  9. package/src/autoresearch/resume-message.md +2 -8
  10. package/src/autoresearch/state.ts +59 -166
  11. package/src/autoresearch/storage.ts +687 -0
  12. package/src/autoresearch/tools/init-experiment.ts +201 -290
  13. package/src/autoresearch/tools/log-experiment.ts +304 -517
  14. package/src/autoresearch/tools/run-experiment.ts +117 -296
  15. package/src/autoresearch/tools/update-notes.ts +116 -0
  16. package/src/autoresearch/types.ts +16 -66
  17. package/src/commit/pipeline.ts +4 -3
  18. package/src/config/settings-schema.ts +1 -1
  19. package/src/config/settings.ts +20 -1
  20. package/src/config.ts +9 -6
  21. package/src/cursor.ts +1 -1
  22. package/src/edit/index.ts +9 -31
  23. package/src/edit/line-hash.ts +70 -43
  24. package/src/edit/modes/hashline.lark +26 -0
  25. package/src/edit/modes/hashline.ts +898 -1099
  26. package/src/edit/modes/patch.ts +0 -7
  27. package/src/edit/modes/replace.ts +0 -4
  28. package/src/edit/renderer.ts +22 -20
  29. package/src/edit/streaming.ts +8 -28
  30. package/src/eval/eval.lark +24 -30
  31. package/src/eval/js/context-manager.ts +5 -162
  32. package/src/eval/js/prelude.txt +0 -12
  33. package/src/eval/parse.ts +129 -129
  34. package/src/eval/py/kernel.ts +4 -4
  35. package/src/eval/py/prelude.py +1 -219
  36. package/src/export/html/template.generated.ts +1 -1
  37. package/src/export/html/template.js +2 -2
  38. package/src/internal-urls/docs-index.generated.ts +1 -1
  39. package/src/main.ts +10 -0
  40. package/src/mcp/manager.ts +22 -0
  41. package/src/modes/components/session-observer-overlay.ts +5 -2
  42. package/src/modes/components/status-line/segments.ts +1 -1
  43. package/src/modes/components/status-line.ts +3 -5
  44. package/src/modes/components/tree-selector.ts +4 -5
  45. package/src/modes/components/welcome.ts +11 -1
  46. package/src/modes/controllers/command-controller.ts +2 -6
  47. package/src/modes/controllers/event-controller.ts +1 -2
  48. package/src/modes/controllers/extension-ui-controller.ts +3 -15
  49. package/src/modes/controllers/input-controller.ts +0 -1
  50. package/src/modes/controllers/selector-controller.ts +1 -1
  51. package/src/modes/interactive-mode.ts +5 -7
  52. package/src/modes/rpc/rpc-client.ts +9 -0
  53. package/src/modes/rpc/rpc-mode.ts +6 -0
  54. package/src/modes/rpc/rpc-types.ts +9 -0
  55. package/src/prompts/system/system-prompt.md +14 -38
  56. package/src/prompts/tools/ast-edit.md +8 -8
  57. package/src/prompts/tools/ast-grep.md +10 -10
  58. package/src/prompts/tools/eval.md +13 -31
  59. package/src/prompts/tools/find.md +2 -1
  60. package/src/prompts/tools/hashline.md +66 -57
  61. package/src/prompts/tools/search.md +2 -2
  62. package/src/sdk.ts +19 -4
  63. package/src/session/agent-session.ts +110 -4
  64. package/src/session/session-manager.ts +17 -13
  65. package/src/task/agents.ts +4 -5
  66. package/src/tools/archive-reader.ts +9 -3
  67. package/src/tools/ast-edit.ts +141 -44
  68. package/src/tools/ast-grep.ts +112 -36
  69. package/src/tools/browser/readable.ts +11 -6
  70. package/src/tools/browser/tab-supervisor.ts +2 -2
  71. package/src/tools/browser.ts +5 -3
  72. package/src/tools/eval.ts +2 -53
  73. package/src/tools/find.ts +16 -15
  74. package/src/tools/image-gen.ts +2 -2
  75. package/src/tools/path-utils.ts +36 -196
  76. package/src/tools/search.ts +56 -35
  77. package/src/tools/write.ts +8 -1
  78. package/src/utils/edit-mode.ts +2 -11
  79. package/src/utils/file-display-mode.ts +1 -1
  80. package/src/utils/git.ts +17 -0
  81. package/src/utils/session-color.ts +0 -12
  82. package/src/utils/title-generator.ts +22 -38
  83. package/src/web/scrapers/crossref.ts +3 -3
  84. package/src/web/scrapers/devto.ts +1 -1
  85. package/src/web/scrapers/discourse.ts +5 -5
  86. package/src/web/scrapers/firefox-addons.ts +1 -1
  87. package/src/web/scrapers/flathub.ts +2 -2
  88. package/src/web/scrapers/gitlab.ts +1 -1
  89. package/src/web/scrapers/go-pkg.ts +2 -2
  90. package/src/web/scrapers/jetbrains-marketplace.ts +1 -1
  91. package/src/web/scrapers/mastodon.ts +9 -9
  92. package/src/web/scrapers/mdn.ts +11 -7
  93. package/src/web/scrapers/pub-dev.ts +1 -1
  94. package/src/web/scrapers/rawg.ts +3 -3
  95. package/src/web/scrapers/readthedocs.ts +1 -1
  96. package/src/web/scrapers/spdx.ts +1 -1
  97. package/src/web/scrapers/stackoverflow.ts +2 -2
  98. package/src/web/scrapers/types.ts +53 -39
  99. package/src/web/scrapers/w3c.ts +1 -1
  100. package/src/web/search/providers/gemini.ts +2 -2
  101. package/src/autoresearch/apply-contract-to-state.ts +0 -24
  102. package/src/autoresearch/contract.ts +0 -288
  103. package/src/edit/modes/atom.lark +0 -29
  104. package/src/edit/modes/atom.ts +0 -1773
  105. package/src/prompts/tools/atom.md +0 -150
@@ -1,288 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import type { AutoresearchBenchmarkContract, AutoresearchContract, MetricDirection } from "./types";
4
-
5
- export interface AutoresearchContractLoadResult {
6
- contract: AutoresearchContract;
7
- errors: string[];
8
- path: string;
9
- }
10
-
11
- export interface AutoresearchScriptSnapshot {
12
- benchmarkScript: string;
13
- benchmarkScriptPath: string;
14
- checksScript: string | null;
15
- checksScriptPath: string;
16
- errors: string[];
17
- }
18
-
19
- const HEADING_REGEX = /^##\s+(.+?)\s*$/;
20
- const LIST_ITEM_REGEX = /^\s*[-*]\s+(.*)$/;
21
- const KEY_VALUE_REGEX = /^\s*[-*]\s+([^:]+):\s*(.*)$/;
22
-
23
- export function readAutoresearchContract(workDir: string): AutoresearchContractLoadResult {
24
- const contractPath = path.join(workDir, "autoresearch.md");
25
- let content = "";
26
- try {
27
- content = fs.readFileSync(contractPath, "utf8");
28
- } catch {
29
- return {
30
- contract: createEmptyAutoresearchContract(),
31
- errors: [`${contractPath} does not exist. Create it before initializing autoresearch.`],
32
- path: contractPath,
33
- };
34
- }
35
-
36
- const contract = parseAutoresearchContract(content);
37
- const errors = validateAutoresearchContract(contract);
38
- return { contract, errors, path: contractPath };
39
- }
40
-
41
- export function parseAutoresearchContract(markdown: string): AutoresearchContract {
42
- const sections = extractSections(markdown);
43
- return {
44
- benchmark: parseBenchmarkSection(sections.get("benchmark") ?? ""),
45
- scopePaths: parseListSection(sections.get("files in scope") ?? "", normalizeContractPathSpec),
46
- offLimits: parseListSection(sections.get("off limits") ?? "", normalizeContractPathSpec),
47
- constraints: parseListSection(sections.get("constraints") ?? ""),
48
- };
49
- }
50
-
51
- export function validateAutoresearchContract(contract: AutoresearchContract): string[] {
52
- const errors: string[] = [];
53
- if (!contract.benchmark.command) {
54
- errors.push("Benchmark.command is required in autoresearch.md.");
55
- }
56
- if (!contract.benchmark.primaryMetric) {
57
- errors.push("Benchmark.primary metric is required in autoresearch.md.");
58
- }
59
- if (!contract.benchmark.direction) {
60
- errors.push("Benchmark.direction must be `lower` or `higher` in autoresearch.md.");
61
- }
62
- if (contract.scopePaths.length === 0) {
63
- errors.push("Files in Scope must contain at least one path in autoresearch.md.");
64
- }
65
- for (const scopePath of contract.scopePaths) {
66
- if (isUnsafeContractPathSpec(scopePath)) {
67
- errors.push(`Files in Scope contains an invalid path: ${scopePath}`);
68
- }
69
- }
70
- for (const offLimitsPath of contract.offLimits) {
71
- if (isUnsafeContractPathSpec(offLimitsPath)) {
72
- errors.push(`Off Limits contains an invalid path: ${offLimitsPath}`);
73
- }
74
- }
75
- return errors;
76
- }
77
-
78
- export function loadAutoresearchScriptSnapshot(workDir: string): AutoresearchScriptSnapshot {
79
- const benchmarkScriptPath = path.join(workDir, "autoresearch.sh");
80
- const checksScriptPath = path.join(workDir, "autoresearch.checks.sh");
81
- const errors: string[] = [];
82
-
83
- let benchmarkScript = "";
84
- try {
85
- benchmarkScript = fs.readFileSync(benchmarkScriptPath, "utf8");
86
- } catch {
87
- errors.push(`${benchmarkScriptPath} does not exist. Create it before initializing autoresearch.`);
88
- }
89
-
90
- let checksScript: string | null = null;
91
- try {
92
- checksScript = fs.readFileSync(checksScriptPath, "utf8");
93
- } catch {
94
- checksScript = null;
95
- }
96
-
97
- return {
98
- benchmarkScript,
99
- benchmarkScriptPath,
100
- checksScript,
101
- checksScriptPath,
102
- errors,
103
- };
104
- }
105
-
106
- export function normalizeAutoresearchList(values: readonly string[]): string[] {
107
- const normalized: string[] = [];
108
- const seen = new Set<string>();
109
- for (const value of values) {
110
- const trimmed = value.trim();
111
- if (trimmed.length === 0) continue;
112
- if (seen.has(trimmed)) continue;
113
- seen.add(trimmed);
114
- normalized.push(trimmed);
115
- }
116
- return normalized;
117
- }
118
-
119
- export function normalizeContractPathSpec(value: string): string {
120
- const normalized = path.posix.normalize(value.trim().replaceAll("\\", "/"));
121
- if (normalized === "." || normalized === "./") return ".";
122
- return normalized.replace(/^\.\/+/, "").replace(/\/+$/, "");
123
- }
124
-
125
- export function pathMatchesContractPath(pathValue: string, specValue: string): boolean {
126
- const normalizedPath = normalizeContractPathSpec(pathValue);
127
- const normalizedSpec = normalizeContractPathSpec(specValue);
128
- if (normalizedSpec === ".") return true;
129
- return normalizedPath === normalizedSpec || normalizedPath.startsWith(`${normalizedSpec}/`);
130
- }
131
-
132
- export function contractListsEqual(left: readonly string[], right: readonly string[]): boolean {
133
- const normalizedLeft = normalizeAutoresearchList(left);
134
- const normalizedRight = normalizeAutoresearchList(right);
135
- if (normalizedLeft.length !== normalizedRight.length) return false;
136
- return normalizedLeft.every((value, index) => value === normalizedRight[index]);
137
- }
138
-
139
- export function contractPathListsEqual(left: readonly string[], right: readonly string[]): boolean {
140
- const normalizedLeft = normalizeContractPathList(left);
141
- const normalizedRight = normalizeContractPathList(right);
142
- if (normalizedLeft.length !== normalizedRight.length) return false;
143
- return normalizedLeft.every((value, index) => value === normalizedRight[index]);
144
- }
145
-
146
- function createEmptyAutoresearchContract(): AutoresearchContract {
147
- return {
148
- benchmark: {
149
- command: null,
150
- primaryMetric: null,
151
- metricUnit: "",
152
- direction: null,
153
- secondaryMetrics: [],
154
- },
155
- scopePaths: [],
156
- offLimits: [],
157
- constraints: [],
158
- };
159
- }
160
-
161
- function normalizeContractPathList(values: readonly string[]): string[] {
162
- return normalizeAutoresearchList(values.map(normalizeContractPathSpec)).sort((left, right) =>
163
- left.localeCompare(right),
164
- );
165
- }
166
-
167
- function extractSections(markdown: string): Map<string, string> {
168
- const sections = new Map<string, string>();
169
- const lines = markdown.split("\n");
170
- let currentHeading: string | null = null;
171
- let currentLines: string[] = [];
172
-
173
- for (const line of lines) {
174
- const headingMatch = line.match(HEADING_REGEX);
175
- if (headingMatch) {
176
- if (currentHeading) {
177
- sections.set(currentHeading, currentLines.join("\n").trim());
178
- }
179
- currentHeading = headingMatch[1]?.trim().toLowerCase() ?? null;
180
- currentLines = [];
181
- continue;
182
- }
183
- if (currentHeading) {
184
- currentLines.push(line);
185
- }
186
- }
187
-
188
- if (currentHeading) {
189
- sections.set(currentHeading, currentLines.join("\n").trim());
190
- }
191
- return sections;
192
- }
193
-
194
- function parseBenchmarkSection(section: string): AutoresearchBenchmarkContract {
195
- const entries = new Map<string, string>();
196
- const lines = section.split("\n");
197
- for (let index = 0; index < lines.length; index += 1) {
198
- const rawLine = lines[index] ?? "";
199
- const match = rawLine.match(KEY_VALUE_REGEX);
200
- if (!match) continue;
201
- const key = normalizeKey(match[1] ?? "");
202
- let value = (match[2] ?? "").trim();
203
- if (key === "secondarymetrics") {
204
- const nestedItems: string[] = [];
205
- for (let nestedIndex = index + 1; nestedIndex < lines.length; nestedIndex += 1) {
206
- const nestedLine = lines[nestedIndex] ?? "";
207
- if (nestedLine.match(KEY_VALUE_REGEX)) break;
208
- const nestedMatch = nestedLine.match(/^\s{2,}[-*]\s+(.*)$/);
209
- if (!nestedMatch) {
210
- if (nestedLine.trim().length > 0) break;
211
- continue;
212
- }
213
- nestedItems.push((nestedMatch[1] ?? "").trim());
214
- index = nestedIndex;
215
- }
216
- if (nestedItems.length > 0) {
217
- value = [value, ...nestedItems].filter(Boolean).join(", ");
218
- }
219
- }
220
- entries.set(key, value);
221
- }
222
-
223
- const direction = parseDirection(entries.get("direction"));
224
- return {
225
- command: readNullableEntry(entries.get("command")),
226
- primaryMetric: readNullableEntry(entries.get("primarymetric")),
227
- metricUnit: entries.get("metricunit")?.trim() ?? "",
228
- direction,
229
- secondaryMetrics: parseSecondaryMetrics(entries.get("secondarymetrics")),
230
- };
231
- }
232
-
233
- function parseListSection(section: string, normalizeItem?: (value: string) => string): string[] {
234
- const items: string[] = [];
235
- let activeItem: string | null = null;
236
- for (const rawLine of section.split("\n")) {
237
- const line = rawLine.trimEnd();
238
- if (line.trim().length === 0) continue;
239
- const match = rawLine.match(LIST_ITEM_REGEX);
240
- if (match) {
241
- if (activeItem) items.push(activeItem);
242
- activeItem = (match[1] ?? "").trim();
243
- continue;
244
- }
245
- if (activeItem && /^\s{2,}\S/.test(rawLine)) {
246
- activeItem = `${activeItem} ${line.trim()}`;
247
- continue;
248
- }
249
- if (activeItem) {
250
- items.push(activeItem);
251
- activeItem = null;
252
- }
253
- items.push(line.trim());
254
- }
255
- if (activeItem) {
256
- items.push(activeItem);
257
- }
258
- const normalizedItems = normalizeAutoresearchList(items);
259
- return normalizeItem ? normalizedItems.map(normalizeItem) : normalizedItems;
260
- }
261
-
262
- function normalizeKey(value: string): string {
263
- return value.toLowerCase().replace(/[^a-z0-9]+/g, "");
264
- }
265
-
266
- function parseDirection(value: string | undefined): MetricDirection | null {
267
- if (value === "lower" || value === "higher") return value;
268
- return null;
269
- }
270
-
271
- function readNullableEntry(value: string | undefined): string | null {
272
- const trimmed = value?.trim() ?? "";
273
- return trimmed.length > 0 ? trimmed : null;
274
- }
275
-
276
- function parseSecondaryMetrics(value: string | undefined): string[] {
277
- if (!value) return [];
278
- return normalizeAutoresearchList(
279
- value
280
- .split(",")
281
- .map(entry => entry.trim())
282
- .filter(Boolean),
283
- );
284
- }
285
-
286
- function isUnsafeContractPathSpec(value: string): boolean {
287
- return path.posix.isAbsolute(value) || value === ".." || value.startsWith("../");
288
- }
@@ -1,29 +0,0 @@
1
- %import common.LF
2
-
3
- start: file_section+
4
-
5
- file_section: file_header (line_change | whole_file_change)
6
- file_header: "---" filename LF
7
-
8
- filename: /(.+)/
9
-
10
- line_change: line* mutation_line line*
11
- line: insert_line | delete_line | set_block | move_line | blank
12
- mutation_line: insert_line | delete_line | set_block
13
-
14
- whole_file_change: blank* whole_file_line blank*
15
- whole_file_line: remove_file | move_file
16
- remove_file: "!rm" LF
17
- move_file: "!mv" WS destination LF
18
- destination: /(?:[^ \t\r\n]+|"[^"\r\n]+"|'[^'\r\n]+')/
19
-
20
- insert_line: "+" /(.*)/ LF
21
- delete_line: "-" (LID ".." LID | LID) LF
22
- set_line: (LID ".." LID | LID) WS? "=" /(.*)/ LF
23
- set_block: set_line continuation_line*
24
- continuation_line: "\\" /(.*)/ LF
25
- move_line: ("@" LID | "^" LID | "^" | "$") LF
26
-
27
- LID: /[1-9][0-9]*[a-z]{2}/
28
- WS: /[ \t]+/
29
- blank: LF