@oh-my-pi/pi-coding-agent 13.1.2 → 13.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.
- package/CHANGELOG.md +5 -0
- package/package.json +7 -7
- package/src/async/job-manager.ts +43 -1
- package/src/capability/tool.ts +1 -1
- package/src/config/settings-schema.ts +10 -0
- package/src/config/settings.ts +17 -1
- package/src/discovery/builtin.ts +16 -5
- package/src/discovery/claude-plugins.ts +1 -0
- package/src/discovery/claude.ts +2 -2
- package/src/modes/components/diff.ts +49 -19
- package/src/tools/await-tool.ts +6 -3
- package/src/tools/render-utils.ts +5 -2
- package/src/tools/todo-write.ts +11 -9
package/CHANGELOG.md
CHANGED
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.
|
|
4
|
+
"version": "13.2.0",
|
|
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,12 +41,12 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@mozilla/readability": "^0.6",
|
|
44
|
-
"@oh-my-pi/omp-stats": "13.
|
|
45
|
-
"@oh-my-pi/pi-agent-core": "13.
|
|
46
|
-
"@oh-my-pi/pi-ai": "13.
|
|
47
|
-
"@oh-my-pi/pi-natives": "13.
|
|
48
|
-
"@oh-my-pi/pi-tui": "13.
|
|
49
|
-
"@oh-my-pi/pi-utils": "13.
|
|
44
|
+
"@oh-my-pi/omp-stats": "13.2.0",
|
|
45
|
+
"@oh-my-pi/pi-agent-core": "13.2.0",
|
|
46
|
+
"@oh-my-pi/pi-ai": "13.2.0",
|
|
47
|
+
"@oh-my-pi/pi-natives": "13.2.0",
|
|
48
|
+
"@oh-my-pi/pi-tui": "13.2.0",
|
|
49
|
+
"@oh-my-pi/pi-utils": "13.2.0",
|
|
50
50
|
"@sinclair/typebox": "^0.34",
|
|
51
51
|
"@xterm/headless": "^6.0",
|
|
52
52
|
"ajv": "^8.18",
|
package/src/async/job-manager.ts
CHANGED
|
@@ -47,6 +47,7 @@ export interface AsyncJobRegisterOptions {
|
|
|
47
47
|
export class AsyncJobManager {
|
|
48
48
|
readonly #jobs = new Map<string, AsyncJob>();
|
|
49
49
|
readonly #deliveries: AsyncJobDelivery[] = [];
|
|
50
|
+
readonly #suppressedDeliveries = new Set<string>();
|
|
50
51
|
readonly #evictionTimers = new Map<string, NodeJS.Timeout>();
|
|
51
52
|
readonly #onJobComplete: AsyncJobManagerOptions["onJobComplete"];
|
|
52
53
|
readonly #maxRunningJobs: number;
|
|
@@ -81,6 +82,7 @@ export class AsyncJobManager {
|
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
const id = this.#resolveJobId(options?.id);
|
|
85
|
+
this.#suppressedDeliveries.delete(id);
|
|
84
86
|
const abortController = new AbortController();
|
|
85
87
|
const startTime = Date.now();
|
|
86
88
|
|
|
@@ -182,6 +184,23 @@ export class AsyncJobManager {
|
|
|
182
184
|
return this.#deliveries.length > 0;
|
|
183
185
|
}
|
|
184
186
|
|
|
187
|
+
acknowledgeDeliveries(jobIds: string[]): number {
|
|
188
|
+
const uniqueJobIds = Array.from(new Set(jobIds.map(id => id.trim()).filter(id => id.length > 0)));
|
|
189
|
+
if (uniqueJobIds.length === 0) return 0;
|
|
190
|
+
|
|
191
|
+
for (const jobId of uniqueJobIds) {
|
|
192
|
+
this.#suppressedDeliveries.add(jobId);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const before = this.#deliveries.length;
|
|
196
|
+
this.#deliveries.splice(
|
|
197
|
+
0,
|
|
198
|
+
this.#deliveries.length,
|
|
199
|
+
...this.#deliveries.filter(delivery => !this.#suppressedDeliveries.has(delivery.jobId)),
|
|
200
|
+
);
|
|
201
|
+
return before - this.#deliveries.length;
|
|
202
|
+
}
|
|
203
|
+
|
|
185
204
|
cancelAll(): void {
|
|
186
205
|
for (const job of this.getRunningJobs()) {
|
|
187
206
|
job.status = "cancelled";
|
|
@@ -234,6 +253,7 @@ export class AsyncJobManager {
|
|
|
234
253
|
this.#clearEvictionTimers();
|
|
235
254
|
this.#jobs.clear();
|
|
236
255
|
this.#deliveries.length = 0;
|
|
256
|
+
this.#suppressedDeliveries.clear();
|
|
237
257
|
return drained;
|
|
238
258
|
}
|
|
239
259
|
|
|
@@ -257,6 +277,7 @@ export class AsyncJobManager {
|
|
|
257
277
|
#scheduleEviction(jobId: string): void {
|
|
258
278
|
if (this.#retentionMs <= 0) {
|
|
259
279
|
this.#jobs.delete(jobId);
|
|
280
|
+
this.#suppressedDeliveries.delete(jobId);
|
|
260
281
|
return;
|
|
261
282
|
}
|
|
262
283
|
const existing = this.#evictionTimers.get(jobId);
|
|
@@ -266,6 +287,7 @@ export class AsyncJobManager {
|
|
|
266
287
|
const timer = setTimeout(() => {
|
|
267
288
|
this.#evictionTimers.delete(jobId);
|
|
268
289
|
this.#jobs.delete(jobId);
|
|
290
|
+
this.#suppressedDeliveries.delete(jobId);
|
|
269
291
|
}, this.#retentionMs);
|
|
270
292
|
timer.unref();
|
|
271
293
|
this.#evictionTimers.set(jobId, timer);
|
|
@@ -278,7 +300,14 @@ export class AsyncJobManager {
|
|
|
278
300
|
this.#evictionTimers.clear();
|
|
279
301
|
}
|
|
280
302
|
|
|
303
|
+
#isDeliverySuppressed(jobId: string): boolean {
|
|
304
|
+
return this.#suppressedDeliveries.has(jobId);
|
|
305
|
+
}
|
|
306
|
+
|
|
281
307
|
#enqueueDelivery(jobId: string, text: string): void {
|
|
308
|
+
if (this.#isDeliverySuppressed(jobId)) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
282
311
|
this.#deliveries.push({
|
|
283
312
|
jobId,
|
|
284
313
|
text,
|
|
@@ -308,10 +337,21 @@ export class AsyncJobManager {
|
|
|
308
337
|
async #runDeliveryLoop(): Promise<void> {
|
|
309
338
|
while (this.#deliveries.length > 0) {
|
|
310
339
|
const delivery = this.#deliveries[0];
|
|
340
|
+
if (this.#isDeliverySuppressed(delivery.jobId)) {
|
|
341
|
+
this.#deliveries.shift();
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
311
344
|
const waitMs = delivery.nextAttemptAt - Date.now();
|
|
312
345
|
if (waitMs > 0) {
|
|
313
346
|
await Bun.sleep(waitMs);
|
|
314
347
|
}
|
|
348
|
+
if (this.#deliveries[0] !== delivery) {
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
if (this.#isDeliverySuppressed(delivery.jobId)) {
|
|
352
|
+
this.#deliveries.shift();
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
315
355
|
|
|
316
356
|
try {
|
|
317
357
|
await this.#onJobComplete(delivery.jobId, delivery.text, this.#jobs.get(delivery.jobId));
|
|
@@ -321,7 +361,9 @@ export class AsyncJobManager {
|
|
|
321
361
|
delivery.lastError = error instanceof Error ? error.message : String(error);
|
|
322
362
|
delivery.nextAttemptAt = Date.now() + this.#getRetryDelay(delivery.attempt);
|
|
323
363
|
this.#deliveries.shift();
|
|
324
|
-
this.#
|
|
364
|
+
if (!this.#isDeliverySuppressed(delivery.jobId)) {
|
|
365
|
+
this.#deliveries.push(delivery);
|
|
366
|
+
}
|
|
325
367
|
logger.warn("Async job completion delivery failed", {
|
|
326
368
|
jobId: delivery.jobId,
|
|
327
369
|
attempt: delivery.attempt,
|
package/src/capability/tool.ts
CHANGED
|
@@ -15,7 +15,7 @@ export interface CustomTool {
|
|
|
15
15
|
/** Absolute path to tool definition file */
|
|
16
16
|
path: string;
|
|
17
17
|
/** Tool description */
|
|
18
|
-
description
|
|
18
|
+
description: string;
|
|
19
19
|
/** Tool implementation (script path or inline) */
|
|
20
20
|
implementation?: string;
|
|
21
21
|
/** Source level */
|
|
@@ -176,6 +176,16 @@ export const SETTINGS_SCHEMA = {
|
|
|
176
176
|
description: "Use blue instead of green for diff additions",
|
|
177
177
|
},
|
|
178
178
|
},
|
|
179
|
+
"display.tabWidth": {
|
|
180
|
+
type: "number",
|
|
181
|
+
default: 3,
|
|
182
|
+
ui: {
|
|
183
|
+
tab: "display",
|
|
184
|
+
label: "Tab width",
|
|
185
|
+
description: "Default number of spaces used when rendering tab characters",
|
|
186
|
+
submenu: true,
|
|
187
|
+
},
|
|
188
|
+
},
|
|
179
189
|
defaultThinkingLevel: {
|
|
180
190
|
type: "enum",
|
|
181
191
|
values: ["off", "minimal", "low", "medium", "high", "xhigh"] as const,
|
package/src/config/settings.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import * as fs from "node:fs";
|
|
15
15
|
import * as path from "node:path";
|
|
16
|
-
import { isEnoent, logger, procmgr } from "@oh-my-pi/pi-utils";
|
|
16
|
+
import { isEnoent, logger, procmgr, setDefaultTabWidth } from "@oh-my-pi/pi-utils";
|
|
17
17
|
import { getAgentDbPath, getAgentDir, getProjectDir } from "@oh-my-pi/pi-utils/dirs";
|
|
18
18
|
import { YAML } from "bun";
|
|
19
19
|
import { type Settings as SettingsCapabilityItem, settingsCapability } from "../capability/settings";
|
|
@@ -438,6 +438,7 @@ export class Settings {
|
|
|
438
438
|
|
|
439
439
|
// Build merged view
|
|
440
440
|
this.#rebuildMerged();
|
|
441
|
+
this.#fireAllHooks();
|
|
441
442
|
return this;
|
|
442
443
|
}
|
|
443
444
|
|
|
@@ -610,6 +611,16 @@ export class Settings {
|
|
|
610
611
|
this.#merged = this.#deepMerge(this.#merged, this.#overrides);
|
|
611
612
|
}
|
|
612
613
|
|
|
614
|
+
#fireAllHooks(): void {
|
|
615
|
+
for (const key of Object.keys(SETTING_HOOKS) as SettingPath[]) {
|
|
616
|
+
const hook = SETTING_HOOKS[key];
|
|
617
|
+
if (hook) {
|
|
618
|
+
const value = this.get(key);
|
|
619
|
+
hook(value, value);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
613
624
|
#deepMerge(base: RawSettings, overrides: RawSettings): RawSettings {
|
|
614
625
|
const result = { ...base };
|
|
615
626
|
for (const key of Object.keys(overrides)) {
|
|
@@ -666,6 +677,11 @@ const SETTING_HOOKS: Partial<Record<SettingPath, SettingHook<any>>> = {
|
|
|
666
677
|
});
|
|
667
678
|
}
|
|
668
679
|
},
|
|
680
|
+
"display.tabWidth": value => {
|
|
681
|
+
if (typeof value === "number") {
|
|
682
|
+
setDefaultTabWidth(value);
|
|
683
|
+
}
|
|
684
|
+
},
|
|
669
685
|
};
|
|
670
686
|
|
|
671
687
|
// ═══════════════════════════════════════════════════════════════════════════
|
package/src/discovery/builtin.ts
CHANGED
|
@@ -656,20 +656,30 @@ async function loadTools(ctx: LoadContext): Promise<LoadResult<CustomTool>> {
|
|
|
656
656
|
transform: (name, content, path, source) => {
|
|
657
657
|
if (name.endsWith(".json")) {
|
|
658
658
|
const data = parseJSON<{ name?: string; description?: string }>(content);
|
|
659
|
+
const toolName = data?.name || name.replace(/\.json$/, "");
|
|
660
|
+
const description =
|
|
661
|
+
typeof data?.description === "string" && data.description.trim()
|
|
662
|
+
? data.description
|
|
663
|
+
: `${toolName} custom tool`;
|
|
659
664
|
return {
|
|
660
|
-
name:
|
|
665
|
+
name: toolName,
|
|
661
666
|
path,
|
|
662
|
-
description
|
|
667
|
+
description,
|
|
663
668
|
level,
|
|
664
669
|
_source: source,
|
|
665
670
|
};
|
|
666
671
|
}
|
|
667
672
|
if (name.endsWith(".md")) {
|
|
668
673
|
const { frontmatter } = parseFrontmatter(content, { source: path });
|
|
674
|
+
const toolName = (frontmatter.name as string) || name.replace(/\.md$/, "");
|
|
675
|
+
const description =
|
|
676
|
+
typeof frontmatter.description === "string" && frontmatter.description.trim()
|
|
677
|
+
? String(frontmatter.description)
|
|
678
|
+
: `${toolName} custom tool`;
|
|
669
679
|
return {
|
|
670
|
-
name:
|
|
680
|
+
name: toolName,
|
|
671
681
|
path,
|
|
672
|
-
description
|
|
682
|
+
description,
|
|
673
683
|
level,
|
|
674
684
|
_source: source,
|
|
675
685
|
};
|
|
@@ -679,6 +689,7 @@ async function loadTools(ctx: LoadContext): Promise<LoadResult<CustomTool>> {
|
|
|
679
689
|
return {
|
|
680
690
|
name: toolName,
|
|
681
691
|
path,
|
|
692
|
+
description: `${toolName} custom tool`,
|
|
682
693
|
level,
|
|
683
694
|
_source: source,
|
|
684
695
|
};
|
|
@@ -715,7 +726,7 @@ async function loadTools(ctx: LoadContext): Promise<LoadResult<CustomTool>> {
|
|
|
715
726
|
items.push({
|
|
716
727
|
name: entryName,
|
|
717
728
|
path: indexPath,
|
|
718
|
-
description:
|
|
729
|
+
description: `${entryName} custom tool`,
|
|
719
730
|
level,
|
|
720
731
|
_source: createSourceMeta(PROVIDER_ID, indexPath, level),
|
|
721
732
|
});
|
package/src/discovery/claude.ts
CHANGED
|
@@ -324,10 +324,10 @@ async function loadTools(ctx: LoadContext): Promise<LoadResult<CustomTool>> {
|
|
|
324
324
|
const userResult = await loadFilesFromDir<CustomTool>(ctx, userToolsDir, PROVIDER_ID, "user", {
|
|
325
325
|
transform: (name, _content, path, source) => {
|
|
326
326
|
const toolName = name.replace(/\.(ts|js|sh|bash|py)$/, "");
|
|
327
|
-
|
|
328
327
|
return {
|
|
329
328
|
name: toolName,
|
|
330
329
|
path,
|
|
330
|
+
description: `${toolName} custom tool`,
|
|
331
331
|
level: "user",
|
|
332
332
|
_source: source,
|
|
333
333
|
};
|
|
@@ -343,10 +343,10 @@ async function loadTools(ctx: LoadContext): Promise<LoadResult<CustomTool>> {
|
|
|
343
343
|
const projectResult = await loadFilesFromDir<CustomTool>(ctx, projectToolsDir, PROVIDER_ID, "project", {
|
|
344
344
|
transform: (name, _content, path, source) => {
|
|
345
345
|
const toolName = name.replace(/\.(ts|js|sh|bash|py)$/, "");
|
|
346
|
-
|
|
347
346
|
return {
|
|
348
347
|
name: toolName,
|
|
349
348
|
path,
|
|
349
|
+
description: `${toolName} custom tool`,
|
|
350
350
|
level: "project",
|
|
351
351
|
_source: source,
|
|
352
352
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getIndentation } from "@oh-my-pi/pi-utils";
|
|
1
2
|
import * as Diff from "diff";
|
|
2
3
|
import { theme } from "../../modes/theme/theme";
|
|
3
4
|
import { replaceTabs } from "../../tools/render-utils";
|
|
@@ -12,26 +13,30 @@ const DIM_OFF = "\x1b[22m";
|
|
|
12
13
|
* before the first non-whitespace character; remaining tabs in code
|
|
13
14
|
* content are replaced with spaces (like replaceTabs).
|
|
14
15
|
*/
|
|
15
|
-
function visualizeIndent(text: string): string {
|
|
16
|
+
function visualizeIndent(text: string, filePath?: string): string {
|
|
16
17
|
const match = text.match(/^([ \t]+)/);
|
|
17
|
-
if (!match) return replaceTabs(text);
|
|
18
|
+
if (!match) return replaceTabs(text, filePath);
|
|
18
19
|
const indent = match[1];
|
|
19
20
|
const rest = text.slice(indent.length);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
21
|
+
const indentation = getIndentation(filePath);
|
|
22
|
+
const tabWidth = indentation.length;
|
|
23
|
+
const leftPadding = Math.floor(tabWidth / 2);
|
|
24
|
+
const rightPadding = Math.max(0, tabWidth - leftPadding - 1);
|
|
25
|
+
const tabMarker = `${DIM}${" ".repeat(leftPadding)}→${" ".repeat(rightPadding)}${DIM_OFF}`;
|
|
26
|
+
// Normalize: collapse configured tab-width groups into tab markers, then handle remaining spaces.
|
|
27
|
+
const normalized = indent.replaceAll("\t", indentation);
|
|
23
28
|
let visible = "";
|
|
24
29
|
let pos = 0;
|
|
25
30
|
while (pos < normalized.length) {
|
|
26
|
-
if (pos +
|
|
27
|
-
visible +=
|
|
28
|
-
pos +=
|
|
31
|
+
if (pos + tabWidth <= normalized.length && normalized.slice(pos, pos + tabWidth) === indentation) {
|
|
32
|
+
visible += tabMarker;
|
|
33
|
+
pos += tabWidth;
|
|
29
34
|
} else {
|
|
30
35
|
visible += `${DIM}·${DIM_OFF}`;
|
|
31
36
|
pos++;
|
|
32
37
|
}
|
|
33
38
|
}
|
|
34
|
-
return `${visible}${replaceTabs(rest)}`;
|
|
39
|
+
return `${visible}${replaceTabs(rest, filePath)}`;
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
/**
|
|
@@ -96,7 +101,7 @@ function renderIntraLineDiff(oldContent: string, newContent: string): { removedL
|
|
|
96
101
|
}
|
|
97
102
|
|
|
98
103
|
export interface RenderDiffOptions {
|
|
99
|
-
/** File path
|
|
104
|
+
/** File path used to resolve indentation (.editorconfig + defaults) */
|
|
100
105
|
filePath?: string;
|
|
101
106
|
}
|
|
102
107
|
|
|
@@ -106,7 +111,7 @@ export interface RenderDiffOptions {
|
|
|
106
111
|
* - Removed lines: red, with inverse on changed tokens
|
|
107
112
|
* - Added lines: green, with inverse on changed tokens
|
|
108
113
|
*/
|
|
109
|
-
export function renderDiff(diffText: string,
|
|
114
|
+
export function renderDiff(diffText: string, options: RenderDiffOptions = {}): string {
|
|
110
115
|
const lines = diffText.split("\n");
|
|
111
116
|
const result: string[] = [];
|
|
112
117
|
|
|
@@ -154,30 +159,55 @@ export function renderDiff(diffText: string, _options: RenderDiffOptions = {}):
|
|
|
154
159
|
const added = addedLines[0];
|
|
155
160
|
|
|
156
161
|
const { removedLine, addedLine } = renderIntraLineDiff(
|
|
157
|
-
replaceTabs(removed.content),
|
|
158
|
-
replaceTabs(added.content),
|
|
162
|
+
replaceTabs(removed.content, options.filePath),
|
|
163
|
+
replaceTabs(added.content, options.filePath),
|
|
159
164
|
);
|
|
160
165
|
|
|
161
|
-
result.push(
|
|
162
|
-
|
|
166
|
+
result.push(
|
|
167
|
+
theme.fg(
|
|
168
|
+
"toolDiffRemoved",
|
|
169
|
+
formatLine("-", removed.lineNum, visualizeIndent(removedLine, options.filePath)),
|
|
170
|
+
),
|
|
171
|
+
);
|
|
172
|
+
result.push(
|
|
173
|
+
theme.fg("toolDiffAdded", formatLine("+", added.lineNum, visualizeIndent(addedLine, options.filePath))),
|
|
174
|
+
);
|
|
163
175
|
} else {
|
|
164
176
|
// Show all removed lines first, then all added lines
|
|
165
177
|
for (const removed of removedLines) {
|
|
166
178
|
result.push(
|
|
167
|
-
theme.fg(
|
|
179
|
+
theme.fg(
|
|
180
|
+
"toolDiffRemoved",
|
|
181
|
+
formatLine("-", removed.lineNum, visualizeIndent(removed.content, options.filePath)),
|
|
182
|
+
),
|
|
168
183
|
);
|
|
169
184
|
}
|
|
170
185
|
for (const added of addedLines) {
|
|
171
|
-
result.push(
|
|
186
|
+
result.push(
|
|
187
|
+
theme.fg(
|
|
188
|
+
"toolDiffAdded",
|
|
189
|
+
formatLine("+", added.lineNum, visualizeIndent(added.content, options.filePath)),
|
|
190
|
+
),
|
|
191
|
+
);
|
|
172
192
|
}
|
|
173
193
|
}
|
|
174
194
|
} else if (parsed.prefix === "+") {
|
|
175
195
|
// Standalone added line
|
|
176
|
-
result.push(
|
|
196
|
+
result.push(
|
|
197
|
+
theme.fg(
|
|
198
|
+
"toolDiffAdded",
|
|
199
|
+
formatLine("+", parsed.lineNum, visualizeIndent(parsed.content, options.filePath)),
|
|
200
|
+
),
|
|
201
|
+
);
|
|
177
202
|
i++;
|
|
178
203
|
} else {
|
|
179
204
|
// Context line
|
|
180
|
-
result.push(
|
|
205
|
+
result.push(
|
|
206
|
+
theme.fg(
|
|
207
|
+
"toolDiffContext",
|
|
208
|
+
formatLine(" ", parsed.lineNum, visualizeIndent(parsed.content, options.filePath)),
|
|
209
|
+
),
|
|
210
|
+
);
|
|
181
211
|
i++;
|
|
182
212
|
}
|
|
183
213
|
}
|
package/src/tools/await-tool.ts
CHANGED
|
@@ -79,7 +79,7 @@ export class AwaitTool implements AgentTool<typeof awaitSchema, AwaitToolDetails
|
|
|
79
79
|
// If all watched jobs are already done, return immediately
|
|
80
80
|
const runningJobs = jobsToWatch.filter(j => j.status === "running");
|
|
81
81
|
if (runningJobs.length === 0) {
|
|
82
|
-
return this.#buildResult(jobsToWatch);
|
|
82
|
+
return this.#buildResult(manager, jobsToWatch);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// Block until at least one running job finishes or the call is aborted
|
|
@@ -100,13 +100,14 @@ export class AwaitTool implements AgentTool<typeof awaitSchema, AwaitToolDetails
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
if (signal?.aborted) {
|
|
103
|
-
return this.#buildResult(jobsToWatch);
|
|
103
|
+
return this.#buildResult(manager, jobsToWatch);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
return this.#buildResult(jobsToWatch);
|
|
106
|
+
return this.#buildResult(manager, jobsToWatch);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
#buildResult(
|
|
110
|
+
manager: NonNullable<ToolSession["asyncJobManager"]>,
|
|
110
111
|
jobs: {
|
|
111
112
|
id: string;
|
|
112
113
|
type: "bash" | "task";
|
|
@@ -128,6 +129,8 @@ export class AwaitTool implements AgentTool<typeof awaitSchema, AwaitToolDetails
|
|
|
128
129
|
...(j.errorText ? { errorText: j.errorText } : {}),
|
|
129
130
|
}));
|
|
130
131
|
|
|
132
|
+
manager.acknowledgeDeliveries(jobResults.filter(j => j.status !== "running").map(j => j.id));
|
|
133
|
+
|
|
131
134
|
const completed = jobResults.filter(j => j.status !== "running");
|
|
132
135
|
const running = jobResults.filter(j => j.status === "running");
|
|
133
136
|
|
|
@@ -6,11 +6,14 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import * as os from "node:os";
|
|
8
8
|
import { type Ellipsis, truncateToWidth } from "@oh-my-pi/pi-tui";
|
|
9
|
-
import { pluralize } from "@oh-my-pi/pi-utils";
|
|
9
|
+
import { getIndentation, pluralize } from "@oh-my-pi/pi-utils";
|
|
10
10
|
import type { Theme } from "../modes/theme/theme";
|
|
11
11
|
|
|
12
|
-
export { Ellipsis,
|
|
12
|
+
export { Ellipsis, truncateToWidth } from "@oh-my-pi/pi-tui";
|
|
13
13
|
|
|
14
|
+
export function replaceTabs(text: string, file?: string): string {
|
|
15
|
+
return text.replaceAll("\t", getIndentation(file));
|
|
16
|
+
}
|
|
14
17
|
// =============================================================================
|
|
15
18
|
// Standardized Display Constants
|
|
16
19
|
// =============================================================================
|
package/src/tools/todo-write.ts
CHANGED
|
@@ -41,16 +41,18 @@ export interface TodoWriteToolDetails {
|
|
|
41
41
|
// Schema
|
|
42
42
|
// =============================================================================
|
|
43
43
|
|
|
44
|
-
const StatusEnum = StringEnum(["pending", "in_progress", "completed", "abandoned"] as const
|
|
44
|
+
const StatusEnum = StringEnum(["pending", "in_progress", "completed", "abandoned"] as const, {
|
|
45
|
+
description: "Task status",
|
|
46
|
+
});
|
|
45
47
|
|
|
46
48
|
const InputTask = Type.Object({
|
|
47
|
-
content: Type.String(),
|
|
49
|
+
content: Type.String({ description: "Task description" }),
|
|
48
50
|
status: Type.Optional(StatusEnum),
|
|
49
|
-
notes: Type.Optional(Type.String()),
|
|
51
|
+
notes: Type.Optional(Type.String({ description: "Additional context or notes" })),
|
|
50
52
|
});
|
|
51
53
|
|
|
52
54
|
const InputPhase = Type.Object({
|
|
53
|
-
name: Type.String(),
|
|
55
|
+
name: Type.String({ description: "Phase name" }),
|
|
54
56
|
tasks: Type.Optional(Type.Array(InputTask)),
|
|
55
57
|
});
|
|
56
58
|
|
|
@@ -63,21 +65,21 @@ const todoWriteSchema = Type.Object({
|
|
|
63
65
|
}),
|
|
64
66
|
Type.Object({
|
|
65
67
|
op: Type.Literal("add_phase"),
|
|
66
|
-
name: Type.String(),
|
|
68
|
+
name: Type.String({ description: "Phase name" }),
|
|
67
69
|
tasks: Type.Optional(Type.Array(InputTask)),
|
|
68
70
|
}),
|
|
69
71
|
Type.Object({
|
|
70
72
|
op: Type.Literal("add_task"),
|
|
71
73
|
phase: Type.String({ description: "Phase ID, e.g. phase-1" }),
|
|
72
|
-
content: Type.String(),
|
|
73
|
-
notes: Type.Optional(Type.String()),
|
|
74
|
+
content: Type.String({ description: "Task description" }),
|
|
75
|
+
notes: Type.Optional(Type.String({ description: "Additional context or notes" })),
|
|
74
76
|
}),
|
|
75
77
|
Type.Object({
|
|
76
78
|
op: Type.Literal("update"),
|
|
77
79
|
id: Type.String({ description: "Task ID, e.g. task-3" }),
|
|
78
80
|
status: Type.Optional(StatusEnum),
|
|
79
|
-
content: Type.Optional(Type.String()),
|
|
80
|
-
notes: Type.Optional(Type.String()),
|
|
81
|
+
content: Type.Optional(Type.String({ description: "Updated task description" })),
|
|
82
|
+
notes: Type.Optional(Type.String({ description: "Additional context or notes" })),
|
|
81
83
|
}),
|
|
82
84
|
Type.Object({
|
|
83
85
|
op: Type.Literal("remove_task"),
|