@oh-my-pi/pi-coding-agent 16.4.2 → 16.4.3
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 +42 -0
- package/dist/cli.js +17514 -17799
- package/dist/types/advisor/config.d.ts +4 -3
- package/dist/types/commit/agentic/agent.d.ts +1 -0
- package/dist/types/config/settings-schema.d.ts +28 -0
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +10 -22
- package/dist/types/lsp/deferred-diagnostics.d.ts +11 -0
- package/dist/types/modes/components/move-overlay.d.ts +1 -1
- package/dist/types/modes/components/status-line/component.d.ts +3 -0
- package/dist/types/modes/components/status-line/types.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +8 -0
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +16 -0
- package/dist/types/task/executor.d.ts +31 -0
- package/dist/types/tools/__tests__/vibe-render.test.d.ts +1 -0
- package/dist/types/tools/browser/cmux/cmux-tab.d.ts +3 -1
- package/dist/types/tools/browser/cmux/rpc.d.ts +20 -0
- package/dist/types/tools/browser/run-output.d.ts +25 -0
- package/dist/types/tools/browser/tab-worker.d.ts +16 -0
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/vibe.d.ts +161 -0
- package/dist/types/utils/changelog.d.ts +35 -1
- package/dist/types/vibe/runtime.d.ts +124 -0
- package/dist/types/vibe/state.d.ts +4 -0
- package/dist/types/web/search/provider.d.ts +2 -0
- package/dist/types/web/search/providers/bing.d.ts +14 -0
- package/dist/types/web/search/providers/browser-headers.d.ts +9 -0
- package/dist/types/web/search/providers/browser-page.d.ts +32 -0
- package/dist/types/web/search/providers/ecosia.d.ts +14 -0
- package/dist/types/web/search/providers/google.d.ts +13 -0
- package/dist/types/web/search/providers/mojeek.d.ts +14 -0
- package/dist/types/web/search/providers/public.d.ts +37 -0
- package/dist/types/web/search/providers/startpage.d.ts +14 -0
- package/dist/types/web/search/providers/yahoo.d.ts +14 -0
- package/dist/types/web/search/types.d.ts +28 -0
- package/package.json +15 -18
- package/scripts/build-binary.ts +56 -73
- package/scripts/bundle-dist.ts +36 -40
- package/scripts/compile-binary.ts +68 -0
- package/scripts/generate-docs-index.ts +3 -93
- package/scripts/legacy-pi-virtual-module.ts +192 -0
- package/src/advisor/__tests__/advisor.test.ts +13 -0
- package/src/advisor/__tests__/config.test.ts +36 -0
- package/src/advisor/config.ts +11 -8
- package/src/commit/agentic/agent.ts +4 -0
- package/src/commit/agentic/index.ts +46 -21
- package/src/edit/index.ts +10 -76
- package/src/exec/non-interactive-env.ts +0 -1
- package/src/extensibility/plugins/legacy-pi-compat.ts +328 -162
- package/src/extensibility/plugins/legacy-pi-virtual-modules.d.ts +4 -0
- package/src/internal-urls/docs-index.ts +2 -1
- package/src/internal-urls/skill-protocol.ts +1 -1
- package/src/lsp/deferred-diagnostics.ts +66 -0
- package/src/main.ts +13 -13
- package/src/mcp/transports/stdio.test.ts +45 -1
- package/src/mcp/transports/stdio.ts +6 -3
- package/src/modes/acp/acp-agent.ts +65 -1
- package/src/modes/acp/acp-event-mapper.ts +5 -0
- package/src/modes/acp/acp-mode.ts +11 -0
- package/src/modes/components/__tests__/move-overlay.test.ts +16 -1
- package/src/modes/components/advisor-config.ts +15 -7
- package/src/modes/components/move-overlay.ts +2 -3
- package/src/modes/components/status-line/component.ts +6 -0
- package/src/modes/components/status-line/segments.ts +6 -0
- package/src/modes/components/status-line/types.ts +3 -0
- package/src/modes/controllers/command-controller.ts +8 -10
- package/src/modes/interactive-mode.ts +117 -1
- package/src/modes/types.ts +2 -0
- package/src/prompts/system/eager-task.md +2 -2
- package/src/prompts/system/system-prompt.md +9 -2
- package/src/prompts/system/vibe-mode-active.md +23 -0
- package/src/prompts/tools/browser.md +3 -3
- package/src/prompts/tools/grep.md +1 -1
- package/src/prompts/tools/vibe-kill.md +3 -0
- package/src/prompts/tools/vibe-list.md +3 -0
- package/src/prompts/tools/vibe-send.md +9 -0
- package/src/prompts/tools/vibe-spawn.md +10 -0
- package/src/prompts/tools/vibe-turn-result.md +19 -0
- package/src/prompts/tools/vibe-wait.md +8 -0
- package/src/sdk.ts +5 -0
- package/src/session/agent-session.ts +103 -13
- package/src/session/snapcompact-inline.ts +3 -19
- package/src/slash-commands/builtin-registry.ts +24 -8
- package/src/task/agents.ts +0 -2
- package/src/task/executor.ts +105 -0
- package/src/tools/__tests__/vibe-render.test.ts +210 -0
- package/src/tools/bash-skill-urls.ts +1 -1
- package/src/tools/browser/cmux/cmux-tab.ts +46 -48
- package/src/tools/browser/cmux/rpc.ts +50 -0
- package/src/tools/browser/run-output.ts +76 -0
- package/src/tools/browser/tab-worker.ts +264 -129
- package/src/tools/glob.ts +20 -6
- package/src/tools/index.ts +1 -0
- package/src/tools/read.ts +17 -9
- package/src/tools/renderers.ts +6 -0
- package/src/tools/vibe.ts +608 -0
- package/src/tools/write.ts +15 -2
- package/src/utils/changelog.ts +106 -9
- package/src/utils/git.ts +0 -1
- package/src/utils/title-generator.ts +70 -7
- package/src/vibe/runtime.ts +710 -0
- package/src/vibe/state.ts +4 -0
- package/src/web/search/index.ts +14 -6
- package/src/web/search/provider.ts +37 -1
- package/src/web/search/providers/bing.ts +197 -0
- package/src/web/search/providers/browser-headers.ts +92 -0
- package/src/web/search/providers/browser-page.ts +123 -0
- package/src/web/search/providers/duckduckgo.ts +13 -34
- package/src/web/search/providers/ecosia.ts +178 -0
- package/src/web/search/providers/google.ts +193 -0
- package/src/web/search/providers/mojeek.ts +206 -0
- package/src/web/search/providers/public.ts +201 -0
- package/src/web/search/providers/startpage.ts +213 -0
- package/src/web/search/providers/yahoo.ts +179 -0
- package/src/web/search/types.ts +35 -0
- package/dist/types/extensibility/plugins/legacy-pi-bundled-keys.d.ts +0 -10
- package/dist/types/extensibility/plugins/legacy-pi-bundled-registry.d.ts +0 -10
- package/scripts/generate-legacy-pi-bundled-registry.ts +0 -420
- package/src/extensibility/plugins/legacy-pi-bundled-keys.ts +0 -1011
- package/src/extensibility/plugins/legacy-pi-bundled-registry.ts +0 -3430
- package/src/internal-urls/docs-index.generated.txt +0 -2
- package/src/prompts/agents/plan.md +0 -47
package/src/utils/changelog.ts
CHANGED
|
@@ -7,6 +7,27 @@ export interface ChangelogEntry {
|
|
|
7
7
|
content: string;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
/** Number of changelog releases shown by automatic and default recent views. */
|
|
11
|
+
export const RECENT_CHANGELOG_ENTRY_LIMIT = 3;
|
|
12
|
+
/** Maximum Markdown source bytes allowed in automatic startup release notes. */
|
|
13
|
+
export const STARTUP_CHANGELOG_MAX_BYTES = 64 * 1024;
|
|
14
|
+
/** Hint appended when automatic startup release notes are truncated. */
|
|
15
|
+
export const STARTUP_CHANGELOG_FULL_HINT = "Use `/changelog full` to view the complete changelog.";
|
|
16
|
+
|
|
17
|
+
/** Markdown generated from selected changelog entries and whether it hit a size cap. */
|
|
18
|
+
export interface RenderedChangelog {
|
|
19
|
+
markdown: string;
|
|
20
|
+
truncated: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Automatic startup changelog decision, including whether the marker should advance. */
|
|
24
|
+
export interface StartupChangelogSelection {
|
|
25
|
+
markdown: string | undefined;
|
|
26
|
+
persistCurrentVersion: boolean;
|
|
27
|
+
truncated: boolean;
|
|
28
|
+
selectedEntries: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
10
31
|
/**
|
|
11
32
|
* Parse changelog entries from the file at `changelogPath`. Scans for `## [x.y.z]`
|
|
12
33
|
* headings and collects each block until the next heading or EOF.
|
|
@@ -87,19 +108,95 @@ export function compareVersions(v1: ChangelogEntry, v2: ChangelogEntry): number
|
|
|
87
108
|
}
|
|
88
109
|
|
|
89
110
|
/**
|
|
90
|
-
*
|
|
111
|
+
* Parse an omp changelog marker version into comparable parts.
|
|
91
112
|
*/
|
|
92
|
-
export function
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
113
|
+
export function parseChangelogVersion(version: string | undefined): ChangelogEntry | undefined {
|
|
114
|
+
const match = version?.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
115
|
+
if (!match) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
major: Number.parseInt(match[1], 10),
|
|
121
|
+
minor: Number.parseInt(match[2], 10),
|
|
122
|
+
patch: Number.parseInt(match[3], 10),
|
|
99
123
|
content: "",
|
|
100
124
|
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get entries newer than lastVersion.
|
|
129
|
+
*/
|
|
130
|
+
export function getNewEntries(entries: ChangelogEntry[], lastVersion: string): ChangelogEntry[] {
|
|
131
|
+
const parsedLastVersion = parseChangelogVersion(lastVersion);
|
|
132
|
+
if (!parsedLastVersion) {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return entries.filter(entry => compareVersions(entry, parsedLastVersion) > 0);
|
|
137
|
+
}
|
|
101
138
|
|
|
102
|
-
|
|
139
|
+
/**
|
|
140
|
+
* Render changelog entries oldest-first by default and optionally cap the Markdown source size.
|
|
141
|
+
*/
|
|
142
|
+
export function renderChangelogEntries(
|
|
143
|
+
entries: ChangelogEntry[],
|
|
144
|
+
options: { maxBytes?: number; truncationHint?: string; oldestFirst?: boolean } = {},
|
|
145
|
+
): RenderedChangelog {
|
|
146
|
+
const orderedEntries = options.oldestFirst === false ? entries : [...entries].reverse();
|
|
147
|
+
const markdown = orderedEntries.map(entry => entry.content).join("\n\n");
|
|
148
|
+
if (options.maxBytes === undefined || Buffer.byteLength(markdown) <= options.maxBytes) {
|
|
149
|
+
return { markdown, truncated: false };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const suffix = `\n\n…\n\n${options.truncationHint ?? STARTUP_CHANGELOG_FULL_HINT}`;
|
|
153
|
+
let low = 0;
|
|
154
|
+
let high = markdown.length;
|
|
155
|
+
while (low < high) {
|
|
156
|
+
const middle = Math.floor((low + high + 1) / 2);
|
|
157
|
+
if (Buffer.byteLength(markdown.slice(0, middle) + suffix) <= options.maxBytes) {
|
|
158
|
+
low = middle;
|
|
159
|
+
} else {
|
|
160
|
+
high = middle - 1;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return { markdown: markdown.slice(0, low) + suffix, truncated: true };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Select bounded release notes for interactive startup.
|
|
169
|
+
*/
|
|
170
|
+
export function selectStartupChangelog(
|
|
171
|
+
entries: ChangelogEntry[],
|
|
172
|
+
lastVersion: string | undefined,
|
|
173
|
+
currentVersion: string,
|
|
174
|
+
): StartupChangelogSelection {
|
|
175
|
+
const parsedLastVersion = parseChangelogVersion(lastVersion);
|
|
176
|
+
if (!parsedLastVersion) {
|
|
177
|
+
return { markdown: undefined, persistCurrentVersion: true, truncated: false, selectedEntries: 0 };
|
|
178
|
+
}
|
|
179
|
+
const markerVersion = lastVersion ?? "";
|
|
180
|
+
if (markerVersion === currentVersion) {
|
|
181
|
+
return { markdown: undefined, persistCurrentVersion: false, truncated: false, selectedEntries: 0 };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const newEntries = getNewEntries(entries, markerVersion).slice(0, RECENT_CHANGELOG_ENTRY_LIMIT);
|
|
185
|
+
if (newEntries.length === 0) {
|
|
186
|
+
return { markdown: undefined, persistCurrentVersion: false, truncated: false, selectedEntries: 0 };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const rendered = renderChangelogEntries(newEntries, {
|
|
190
|
+
maxBytes: STARTUP_CHANGELOG_MAX_BYTES,
|
|
191
|
+
truncationHint: STARTUP_CHANGELOG_FULL_HINT,
|
|
192
|
+
oldestFirst: false,
|
|
193
|
+
});
|
|
194
|
+
return {
|
|
195
|
+
markdown: rendered.markdown,
|
|
196
|
+
persistCurrentVersion: true,
|
|
197
|
+
truncated: rendered.truncated,
|
|
198
|
+
selectedEntries: newEntries.length,
|
|
199
|
+
};
|
|
103
200
|
}
|
|
104
201
|
|
|
105
202
|
// Re-export getChangelogPath from paths.ts for convenience
|
package/src/utils/git.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
|
|
6
6
|
import { type Api, type AssistantMessage, completeSimple, type Model } from "@oh-my-pi/pi-ai";
|
|
7
|
+
import { StreamMarkupHealing } from "@oh-my-pi/pi-ai/utils/stream-markup-healing";
|
|
7
8
|
import { isTerminalHeadless, logger, prompt } from "@oh-my-pi/pi-utils";
|
|
8
9
|
import type { ModelRegistry } from "../config/model-registry";
|
|
9
10
|
|
|
@@ -32,7 +33,12 @@ const TERMINAL_TITLE_CONTROL_CHARS = /[\u0000-\u001f\u007f-\u009f]/g;
|
|
|
32
33
|
const TITLE_MAX_TOKENS = 1024;
|
|
33
34
|
|
|
34
35
|
/** Matches the title the model wraps in `<title>...</title>`. */
|
|
35
|
-
const
|
|
36
|
+
const TITLE_MARKER_GLOBAL_RE = /<title>([\s\S]*?)<\/title>/gi;
|
|
37
|
+
const TITLE_VISIBILITY_SENTINEL = "\uE000omp-title-visible\uE000";
|
|
38
|
+
const THINKING_TAG_ENVELOPE_RE = /<(think|thinking|reasoning)>\s*[\s\S]*?<\/\1>/gi;
|
|
39
|
+
const THINKING_FENCE_ENVELOPE_RE = /```(?:thinking|reasoning)\b[\s\S]*?```/gi;
|
|
40
|
+
const LEADING_THINKING_TAG_RE = /^\s*<(think|thinking|reasoning)>\s*[\s\S]*?<\/\1>\s*/i;
|
|
41
|
+
const LEADING_THINKING_FENCE_RE = /^\s*```(?:thinking|reasoning)\b[\s\S]*?```\s*/i;
|
|
36
42
|
|
|
37
43
|
function getTitleModel(registry: ModelRegistry, settings: Settings, currentModel?: Model<Api>): Model<Api> | undefined {
|
|
38
44
|
const availableModels = registry.getAvailable();
|
|
@@ -241,12 +247,69 @@ function extractGeneratedTitle(contentBlocks: AssistantMessage["content"]): stri
|
|
|
241
247
|
textTitle += content.text;
|
|
242
248
|
}
|
|
243
249
|
}
|
|
244
|
-
// Stay lenient: prefer the
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
+
// Stay lenient: prefer the first closed title marker in visible text, then
|
|
251
|
+
// fall back to a plain sentence after stripping only known leading leaked
|
|
252
|
+
// thinking envelopes plus any stray/unclosed title tag fragment.
|
|
253
|
+
const markedTitle = extractVisibleMarkedTitle(textTitle);
|
|
254
|
+
const cleanedTextTitle =
|
|
255
|
+
markedTitle ??
|
|
256
|
+
stripLeadingLeakedThinkingMarkup(textTitle)
|
|
257
|
+
.replace(/<\/?title>/gi, "")
|
|
258
|
+
.trim();
|
|
259
|
+
return unwrapJsonTitle(cleanedTextTitle);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function extractVisibleMarkedTitle(text: string): string | undefined {
|
|
263
|
+
TITLE_MARKER_GLOBAL_RE.lastIndex = 0;
|
|
264
|
+
let marker: RegExpExecArray | null = TITLE_MARKER_GLOBAL_RE.exec(text);
|
|
265
|
+
while (marker !== null) {
|
|
266
|
+
const title = marker[1];
|
|
267
|
+
if (title !== undefined && isVisibleTitleMarker(text, marker.index)) return title.trim();
|
|
268
|
+
marker = TITLE_MARKER_GLOBAL_RE.exec(text);
|
|
269
|
+
}
|
|
270
|
+
return undefined;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function isVisibleTitleMarker(text: string, markerIndex: number): boolean {
|
|
274
|
+
if (isInsideKnownThinkingEnvelope(text, markerIndex)) return false;
|
|
275
|
+
return stripLeakedThinkingMarkup(`${text.slice(0, markerIndex)}${TITLE_VISIBILITY_SENTINEL}`).endsWith(
|
|
276
|
+
TITLE_VISIBILITY_SENTINEL,
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function isInsideKnownThinkingEnvelope(text: string, index: number): boolean {
|
|
281
|
+
return (
|
|
282
|
+
isInsideEnvelopeMatchedBy(THINKING_TAG_ENVELOPE_RE, text, index) ||
|
|
283
|
+
isInsideEnvelopeMatchedBy(THINKING_FENCE_ENVELOPE_RE, text, index)
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function isInsideEnvelopeMatchedBy(pattern: RegExp, text: string, index: number): boolean {
|
|
288
|
+
pattern.lastIndex = 0;
|
|
289
|
+
let marker = pattern.exec(text);
|
|
290
|
+
while (marker !== null) {
|
|
291
|
+
const start = marker.index;
|
|
292
|
+
const end = start + marker[0].length;
|
|
293
|
+
if (index > start && index < end) return true;
|
|
294
|
+
if (start > index) return false;
|
|
295
|
+
marker = pattern.exec(text);
|
|
296
|
+
}
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function stripLeadingLeakedThinkingMarkup(text: string): string {
|
|
301
|
+
let current = text;
|
|
302
|
+
while (true) {
|
|
303
|
+
const withoutTag = current.replace(LEADING_THINKING_TAG_RE, "");
|
|
304
|
+
const withoutFence = withoutTag.replace(LEADING_THINKING_FENCE_RE, "");
|
|
305
|
+
if (withoutFence === current) return current;
|
|
306
|
+
current = withoutFence;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function stripLeakedThinkingMarkup(text: string): string {
|
|
311
|
+
const healer = new StreamMarkupHealing({ pattern: "thinking" });
|
|
312
|
+
return healer.feed(text) + healer.flushPending();
|
|
250
313
|
}
|
|
251
314
|
|
|
252
315
|
/**
|