@oh-my-pi/pi-coding-agent 3.1.1337 → 3.3.1337
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 +7 -0
- package/docs/extension-loading.md +2 -2
- package/docs/sdk.md +2 -2
- package/package.json +4 -4
- package/src/modes/interactive/interactive-mode.ts +138 -119
- package/src/modes/print-mode.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [3.3.1337] - 2026-01-03
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Improved `/status` command output formatting to use consistent column alignment across all sections
|
|
10
|
+
- Updated version update notification to suggest `omp update` instead of manual npm install command
|
|
11
|
+
|
|
5
12
|
## [3.1.1337] - 2026-01-03
|
|
6
13
|
### Added
|
|
7
14
|
|
|
@@ -775,7 +775,7 @@ Update argument parsing:
|
|
|
775
775
|
result.noHooks = true;
|
|
776
776
|
```
|
|
777
777
|
|
|
778
|
-
Add subcommand handling for `
|
|
778
|
+
Add subcommand handling for `omp install`, `omp remove`, `omp update`.
|
|
779
779
|
|
|
780
780
|
### `src/core/hooks/loader.ts`
|
|
781
781
|
|
|
@@ -949,7 +949,7 @@ if (settings.skills?.customDirectories) {
|
|
|
949
949
|
5. **Phase 5: CLI updates**
|
|
950
950
|
- Add new flags to args.ts
|
|
951
951
|
- Update help text
|
|
952
|
-
- Add `
|
|
952
|
+
- Add `omp install`, `omp remove`, `omp update` subcommands
|
|
953
953
|
|
|
954
954
|
6. **Phase 6: Integration**
|
|
955
955
|
- Update sdk.ts
|
package/docs/sdk.md
CHANGED
|
@@ -54,7 +54,7 @@ The main factory function. Creates an `AgentSession` with configurable options.
|
|
|
54
54
|
|
|
55
55
|
**Philosophy:** "Omit to discover, provide to override."
|
|
56
56
|
|
|
57
|
-
- Omit an option →
|
|
57
|
+
- Omit an option → omp discovers/loads from standard locations
|
|
58
58
|
- Provide an option → your value is used, discovery skipped for that option
|
|
59
59
|
|
|
60
60
|
```typescript
|
|
@@ -405,7 +405,7 @@ const { session } = await createAgentSession({
|
|
|
405
405
|
|
|
406
406
|
**When you don't need factories:**
|
|
407
407
|
|
|
408
|
-
- If you omit `tools`,
|
|
408
|
+
- If you omit `tools`, omp automatically creates them with the correct `cwd`
|
|
409
409
|
- If you use `process.cwd()` as your `cwd`, the pre-built instances work fine
|
|
410
410
|
|
|
411
411
|
**When you must use factories:**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.1337",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"ompConfig": {
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"prepublishOnly": "bun run generate-template && bun run clean && bun run build"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@oh-my-pi/pi-agent-core": "3.
|
|
43
|
-
"@oh-my-pi/pi-ai": "3.
|
|
44
|
-
"@oh-my-pi/pi-tui": "3.
|
|
42
|
+
"@oh-my-pi/pi-agent-core": "3.3.1337",
|
|
43
|
+
"@oh-my-pi/pi-ai": "3.3.1337",
|
|
44
|
+
"@oh-my-pi/pi-tui": "3.3.1337",
|
|
45
45
|
"@sinclair/typebox": "^0.34.46",
|
|
46
46
|
"ajv": "^8.17.1",
|
|
47
47
|
"chalk": "^5.5.0",
|
|
@@ -1617,7 +1617,7 @@ export class InteractiveMode {
|
|
|
1617
1617
|
theme.bold(theme.fg("warning", "Update Available")) +
|
|
1618
1618
|
"\n" +
|
|
1619
1619
|
theme.fg("muted", `New version ${newVersion} is available. Run: `) +
|
|
1620
|
-
theme.fg("accent", "
|
|
1620
|
+
theme.fg("accent", "omp update"),
|
|
1621
1621
|
1,
|
|
1622
1622
|
0,
|
|
1623
1623
|
),
|
|
@@ -2391,14 +2391,26 @@ export class InteractiveMode {
|
|
|
2391
2391
|
}
|
|
2392
2392
|
|
|
2393
2393
|
private handleStatusCommand(): void {
|
|
2394
|
-
const sections: string[] = [];
|
|
2395
|
-
|
|
2396
2394
|
type StatusSource =
|
|
2397
2395
|
| { provider: string; level: string }
|
|
2398
2396
|
| { mcpServer: string; provider?: string }
|
|
2399
2397
|
| "builtin"
|
|
2400
2398
|
| "unknown";
|
|
2401
2399
|
|
|
2400
|
+
type StatusLine = {
|
|
2401
|
+
name: string;
|
|
2402
|
+
sourceText: string;
|
|
2403
|
+
nameWithSource: string;
|
|
2404
|
+
desc?: string;
|
|
2405
|
+
};
|
|
2406
|
+
|
|
2407
|
+
type LineSection = {
|
|
2408
|
+
title: string;
|
|
2409
|
+
lines: StatusLine[];
|
|
2410
|
+
};
|
|
2411
|
+
|
|
2412
|
+
type Section = { kind: "lines"; section: LineSection } | { kind: "text"; text: string };
|
|
2413
|
+
|
|
2402
2414
|
const capitalize = (value: string): string => value.charAt(0).toUpperCase() + value.slice(1);
|
|
2403
2415
|
|
|
2404
2416
|
const resolveSourceText = (source: StatusSource): string => {
|
|
@@ -2440,29 +2452,28 @@ export class InteractiveMode {
|
|
|
2440
2452
|
return `${acc}...`;
|
|
2441
2453
|
};
|
|
2442
2454
|
|
|
2443
|
-
|
|
2444
|
-
const formatSection = <T>(
|
|
2455
|
+
const buildLineSection = <T>(
|
|
2445
2456
|
title: string,
|
|
2446
2457
|
items: readonly T[],
|
|
2447
2458
|
getName: (item: T) => string,
|
|
2448
2459
|
getDesc: (item: T) => string | undefined,
|
|
2449
2460
|
getSource: (item: T) => StatusSource,
|
|
2450
|
-
):
|
|
2451
|
-
if (items.length === 0) return
|
|
2461
|
+
): LineSection | null => {
|
|
2462
|
+
if (items.length === 0) return null;
|
|
2452
2463
|
|
|
2453
|
-
const
|
|
2464
|
+
const lines = items.map((item) => {
|
|
2454
2465
|
const name = getName(item);
|
|
2455
|
-
const desc = getDesc(item);
|
|
2466
|
+
const desc = getDesc(item)?.trim();
|
|
2456
2467
|
const sourceText = resolveSourceText(getSource(item));
|
|
2457
2468
|
const nameWithSource = sourceText ? `${name} ${sourceText}` : name;
|
|
2458
2469
|
return { name, sourceText, nameWithSource, desc };
|
|
2459
2470
|
});
|
|
2460
2471
|
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
const formattedLines =
|
|
2472
|
+
return { title, lines };
|
|
2473
|
+
};
|
|
2474
|
+
|
|
2475
|
+
const renderLineSection = (section: LineSection, maxNameWidth: number): string => {
|
|
2476
|
+
const formattedLines = section.lines.map((line) => {
|
|
2466
2477
|
let nameText = line.name;
|
|
2467
2478
|
let sourceText = line.sourceText;
|
|
2468
2479
|
|
|
@@ -2471,103 +2482,101 @@ export class InteractiveMode {
|
|
|
2471
2482
|
sourceText = truncateText(sourceText, maxSourceWidth);
|
|
2472
2483
|
}
|
|
2473
2484
|
const sourceWidth = sourceText ? visibleWidth(sourceText) : 0;
|
|
2474
|
-
const availableForName = sourceText
|
|
2475
|
-
? Math.max(1, maxNameWidth - sourceWidth - 1)
|
|
2476
|
-
: maxNameWidth;
|
|
2485
|
+
const availableForName = sourceText ? Math.max(1, maxNameWidth - sourceWidth - 1) : maxNameWidth;
|
|
2477
2486
|
nameText = truncateText(nameText, availableForName);
|
|
2478
2487
|
|
|
2479
2488
|
const nameWithSourcePlain = sourceText ? `${nameText} ${sourceText}` : nameText;
|
|
2480
2489
|
const sourceRendered = sourceText ? renderSourceText(sourceText) : "";
|
|
2481
2490
|
const nameRendered = sourceText ? `${theme.bold(nameText)} ${sourceRendered}` : theme.bold(nameText);
|
|
2482
2491
|
const pad = Math.max(0, maxNameWidth - visibleWidth(nameWithSourcePlain));
|
|
2483
|
-
const desc = line.desc
|
|
2484
|
-
const descPart = desc
|
|
2492
|
+
const desc = line.desc;
|
|
2493
|
+
const descPart = desc
|
|
2494
|
+
? ` ${theme.fg("dim", desc.slice(0, 50) + (desc.length > 50 ? "..." : ""))}`
|
|
2495
|
+
: "";
|
|
2485
2496
|
return ` ${nameRendered}${" ".repeat(pad)}${descPart}`;
|
|
2486
2497
|
});
|
|
2487
2498
|
|
|
2488
|
-
return `${theme.bold(theme.fg("accent", title))}\n${formattedLines.join("\n")}`;
|
|
2499
|
+
return `${theme.bold(theme.fg("accent", section.title))}\n${formattedLines.join("\n")}`;
|
|
2500
|
+
};
|
|
2501
|
+
|
|
2502
|
+
const sections: Section[] = [];
|
|
2503
|
+
const pushLineSection = <T>(
|
|
2504
|
+
title: string,
|
|
2505
|
+
items: readonly T[],
|
|
2506
|
+
getName: (item: T) => string,
|
|
2507
|
+
getDesc: (item: T) => string | undefined,
|
|
2508
|
+
getSource: (item: T) => StatusSource,
|
|
2509
|
+
): void => {
|
|
2510
|
+
const section = buildLineSection(title, items, getName, getDesc, getSource);
|
|
2511
|
+
if (section) {
|
|
2512
|
+
sections.push({ kind: "lines", section });
|
|
2513
|
+
}
|
|
2489
2514
|
};
|
|
2490
2515
|
|
|
2491
2516
|
// Loaded context files
|
|
2492
2517
|
const contextFilesResult = loadSync(contextFileCapability.id, { cwd: process.cwd() });
|
|
2493
2518
|
const contextFiles = contextFilesResult.items as ContextFile[];
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
(f) => ({ provider: f._source.providerName, level: f.level }),
|
|
2502
|
-
),
|
|
2503
|
-
);
|
|
2504
|
-
}
|
|
2519
|
+
pushLineSection(
|
|
2520
|
+
"Context Files",
|
|
2521
|
+
contextFiles,
|
|
2522
|
+
(f) => basename(f.path),
|
|
2523
|
+
() => undefined,
|
|
2524
|
+
(f) => ({ provider: f._source.providerName, level: f.level }),
|
|
2525
|
+
);
|
|
2505
2526
|
|
|
2506
2527
|
// Loaded skills
|
|
2507
2528
|
const skillsSettings = this.session.skillsSettings;
|
|
2508
2529
|
if (skillsSettings?.enabled !== false) {
|
|
2509
2530
|
const { skills, warnings: skillWarnings } = loadSkills(skillsSettings ?? {});
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
(s) => (s._source ? { provider: s._source.providerName, level: s._source.level } : "unknown"),
|
|
2518
|
-
),
|
|
2519
|
-
);
|
|
2520
|
-
}
|
|
2531
|
+
pushLineSection(
|
|
2532
|
+
"Skills",
|
|
2533
|
+
skills,
|
|
2534
|
+
(s) => s.name,
|
|
2535
|
+
(s) => s.description,
|
|
2536
|
+
(s) => (s._source ? { provider: s._source.providerName, level: s._source.level } : "unknown"),
|
|
2537
|
+
);
|
|
2521
2538
|
if (skillWarnings.length > 0) {
|
|
2522
2539
|
sections.push(
|
|
2523
|
-
|
|
2524
|
-
"
|
|
2525
|
-
|
|
2540
|
+
{
|
|
2541
|
+
kind: "text",
|
|
2542
|
+
text:
|
|
2543
|
+
theme.bold(theme.fg("warning", "Skill Warnings")) +
|
|
2544
|
+
"\n" +
|
|
2545
|
+
skillWarnings.map((w) => theme.fg("warning", ` ${w.skillPath}: ${w.message}`)).join("\n"),
|
|
2546
|
+
},
|
|
2526
2547
|
);
|
|
2527
2548
|
}
|
|
2528
2549
|
}
|
|
2529
2550
|
|
|
2530
2551
|
// Loaded rules
|
|
2531
2552
|
const rulesResult = loadSync<Rule>(ruleCapability.id, { cwd: process.cwd() });
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
(r) => ({ provider: r._source.providerName, level: r._source.level }),
|
|
2540
|
-
),
|
|
2541
|
-
);
|
|
2542
|
-
}
|
|
2553
|
+
pushLineSection(
|
|
2554
|
+
"Rules",
|
|
2555
|
+
rulesResult.items,
|
|
2556
|
+
(r) => r.name,
|
|
2557
|
+
(r) => r.description,
|
|
2558
|
+
(r) => ({ provider: r._source.providerName, level: r._source.level }),
|
|
2559
|
+
);
|
|
2543
2560
|
|
|
2544
2561
|
// Loaded prompts
|
|
2545
2562
|
const promptsResult = loadSync<Prompt>(promptCapability.id, { cwd: process.cwd() });
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
(p) => ({ provider: p._source.providerName, level: p._source.level }),
|
|
2554
|
-
),
|
|
2555
|
-
);
|
|
2556
|
-
}
|
|
2563
|
+
pushLineSection(
|
|
2564
|
+
"Prompts",
|
|
2565
|
+
promptsResult.items,
|
|
2566
|
+
(p) => p.name,
|
|
2567
|
+
() => undefined,
|
|
2568
|
+
(p) => ({ provider: p._source.providerName, level: p._source.level }),
|
|
2569
|
+
);
|
|
2557
2570
|
|
|
2558
2571
|
// Loaded instructions
|
|
2559
2572
|
const instructionsResult = loadSync<Instruction>(instructionCapability.id, { cwd: process.cwd() });
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
(i) => ({ provider: i._source.providerName, level: i._source.level }),
|
|
2568
|
-
),
|
|
2569
|
-
);
|
|
2570
|
-
}
|
|
2573
|
+
pushLineSection(
|
|
2574
|
+
"Instructions",
|
|
2575
|
+
instructionsResult.items,
|
|
2576
|
+
(i) => i.name,
|
|
2577
|
+
(i) => (i.applyTo ? `applies to: ${i.applyTo}` : undefined),
|
|
2578
|
+
(i) => ({ provider: i._source.providerName, level: i._source.level }),
|
|
2579
|
+
);
|
|
2571
2580
|
|
|
2572
2581
|
// Loaded custom tools - split MCP from non-MCP
|
|
2573
2582
|
if (this.customTools.size > 0) {
|
|
@@ -2577,55 +2586,47 @@ export class InteractiveMode {
|
|
|
2577
2586
|
|
|
2578
2587
|
// MCP Tools section
|
|
2579
2588
|
if (mcpTools.length > 0) {
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
(
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
},
|
|
2594
|
-
),
|
|
2589
|
+
pushLineSection(
|
|
2590
|
+
"MCP Tools",
|
|
2591
|
+
mcpTools,
|
|
2592
|
+
(ct) => ct.tool.label || ct.tool.name,
|
|
2593
|
+
() => undefined,
|
|
2594
|
+
(ct) => {
|
|
2595
|
+
const match = ct.path.match(/^mcp:(.+?) via (.+)$/);
|
|
2596
|
+
if (match) {
|
|
2597
|
+
const [, serverName, providerName] = match;
|
|
2598
|
+
return { mcpServer: serverName, provider: providerName };
|
|
2599
|
+
}
|
|
2600
|
+
return ct.path.startsWith("mcp:") ? { mcpServer: ct.path.slice(4) } : "unknown";
|
|
2601
|
+
},
|
|
2595
2602
|
);
|
|
2596
2603
|
}
|
|
2597
2604
|
|
|
2598
2605
|
// Custom Tools section
|
|
2599
2606
|
if (customTools.length > 0) {
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
(ct)
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
},
|
|
2611
|
-
),
|
|
2607
|
+
pushLineSection(
|
|
2608
|
+
"Custom Tools",
|
|
2609
|
+
customTools,
|
|
2610
|
+
(ct) => ct.tool.label || ct.tool.name,
|
|
2611
|
+
(ct) => ct.tool.description,
|
|
2612
|
+
(ct) => {
|
|
2613
|
+
if (ct.source?.provider === "builtin") return "builtin";
|
|
2614
|
+
if (ct.path === "<exa>") return "builtin";
|
|
2615
|
+
return ct.source ? { provider: ct.source.providerName, level: ct.source.level } : "unknown";
|
|
2616
|
+
},
|
|
2612
2617
|
);
|
|
2613
2618
|
}
|
|
2614
2619
|
}
|
|
2615
2620
|
|
|
2616
2621
|
// Loaded slash commands (file-based)
|
|
2617
2622
|
const fileCommands = this.session.fileCommands;
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
(cmd) => (cmd._source ? { provider: cmd._source.providerName, level: cmd._source.level } : "unknown"),
|
|
2626
|
-
),
|
|
2627
|
-
);
|
|
2628
|
-
}
|
|
2623
|
+
pushLineSection(
|
|
2624
|
+
"Slash Commands",
|
|
2625
|
+
fileCommands,
|
|
2626
|
+
(cmd) => `/${cmd.name}`,
|
|
2627
|
+
(cmd) => cmd.description,
|
|
2628
|
+
(cmd) => (cmd._source ? { provider: cmd._source.providerName, level: cmd._source.level } : "unknown"),
|
|
2629
|
+
);
|
|
2629
2630
|
|
|
2630
2631
|
// Loaded hooks
|
|
2631
2632
|
const hookRunner = this.session.hookRunner;
|
|
@@ -2633,12 +2634,30 @@ export class InteractiveMode {
|
|
|
2633
2634
|
const hookPaths = hookRunner.getHookPaths();
|
|
2634
2635
|
if (hookPaths.length > 0) {
|
|
2635
2636
|
sections.push(
|
|
2636
|
-
|
|
2637
|
+
{
|
|
2638
|
+
kind: "text",
|
|
2639
|
+
text:
|
|
2640
|
+
`${theme.bold(theme.fg("accent", "Hooks"))}\n` +
|
|
2641
|
+
hookPaths.map((p) => ` ${theme.bold(basename(p))} ${theme.fg("dim", "hook")}`).join("\n"),
|
|
2642
|
+
},
|
|
2637
2643
|
);
|
|
2638
2644
|
}
|
|
2639
2645
|
}
|
|
2640
2646
|
|
|
2641
|
-
|
|
2647
|
+
const lineSections = sections.filter((section): section is { kind: "lines"; section: LineSection } => {
|
|
2648
|
+
return section.kind === "lines";
|
|
2649
|
+
});
|
|
2650
|
+
const allLines = lineSections.flatMap((section) => section.section.lines);
|
|
2651
|
+
const maxNameWidth = allLines.length
|
|
2652
|
+
? Math.min(60, Math.max(...allLines.map((line) => visibleWidth(line.nameWithSource))))
|
|
2653
|
+
: 0;
|
|
2654
|
+
const renderedSections = sections
|
|
2655
|
+
.map((section) =>
|
|
2656
|
+
section.kind === "lines" ? renderLineSection(section.section, maxNameWidth) : section.text,
|
|
2657
|
+
)
|
|
2658
|
+
.filter((section) => section.length > 0);
|
|
2659
|
+
|
|
2660
|
+
if (renderedSections.length === 0) {
|
|
2642
2661
|
this.chatContainer.addChild(new Spacer(1));
|
|
2643
2662
|
this.chatContainer.addChild(new Text(theme.fg("muted", "No extensions loaded."), 1, 0));
|
|
2644
2663
|
} else {
|
|
@@ -2646,7 +2665,7 @@ export class InteractiveMode {
|
|
|
2646
2665
|
this.chatContainer.addChild(new DynamicBorder());
|
|
2647
2666
|
this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "Loaded Extensions")), 1, 0));
|
|
2648
2667
|
this.chatContainer.addChild(new Spacer(1));
|
|
2649
|
-
for (const section of
|
|
2668
|
+
for (const section of renderedSections) {
|
|
2650
2669
|
this.chatContainer.addChild(new Text(section, 1, 0));
|
|
2651
2670
|
this.chatContainer.addChild(new Spacer(1));
|
|
2652
2671
|
}
|
package/src/modes/print-mode.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Print mode (single-shot): Send prompts, output result, exit.
|
|
3
3
|
*
|
|
4
4
|
* Used for:
|
|
5
|
-
* - `
|
|
6
|
-
* - `
|
|
5
|
+
* - `omp -p "prompt"` - text output
|
|
6
|
+
* - `omp --mode json "prompt"` - JSON event stream
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { AssistantMessage, ImageContent } from "@oh-my-pi/pi-ai";
|