@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 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 `pi install`, `pi remove`, `pi update`.
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 `pi install`, `pi remove`, `pi update` subcommands
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 → pi discovers/loads from standard locations
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`, pi automatically creates them with the correct `cwd`
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.1.1337",
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.1.1337",
43
- "@oh-my-pi/pi-ai": "3.1.1337",
44
- "@oh-my-pi/pi-tui": "3.1.1337",
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", "npm install -g @oh-my-pi/pi-coding-agent"),
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
- // Helper to format a section with consistent column alignment
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
- ): string => {
2451
- if (items.length === 0) return "";
2461
+ ): LineSection | null => {
2462
+ if (items.length === 0) return null;
2452
2463
 
2453
- const lineItems = items.map((item) => {
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
- const maxNameWidth = Math.min(
2462
- 60,
2463
- Math.max(...lineItems.map((line) => visibleWidth(line.nameWithSource))),
2464
- );
2465
- const formattedLines = lineItems.map((line) => {
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?.trim();
2484
- const descPart = desc ? ` ${theme.fg("dim", desc.slice(0, 50) + (desc.length > 50 ? "..." : ""))}` : "";
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
- if (contextFiles.length > 0) {
2495
- sections.push(
2496
- formatSection(
2497
- "Context Files",
2498
- contextFiles,
2499
- (f) => basename(f.path),
2500
- () => undefined,
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
- if (skills.length > 0) {
2511
- sections.push(
2512
- formatSection(
2513
- "Skills",
2514
- skills,
2515
- (s) => s.name,
2516
- (s) => s.description,
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
- theme.bold(theme.fg("warning", "Skill Warnings")) +
2524
- "\n" +
2525
- skillWarnings.map((w) => theme.fg("warning", ` ${w.skillPath}: ${w.message}`)).join("\n"),
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
- if (rulesResult.items.length > 0) {
2533
- sections.push(
2534
- formatSection(
2535
- "Rules",
2536
- rulesResult.items,
2537
- (r) => r.name,
2538
- (r) => r.description,
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
- if (promptsResult.items.length > 0) {
2547
- sections.push(
2548
- formatSection(
2549
- "Prompts",
2550
- promptsResult.items,
2551
- (p) => p.name,
2552
- () => undefined,
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
- if (instructionsResult.items.length > 0) {
2561
- sections.push(
2562
- formatSection(
2563
- "Instructions",
2564
- instructionsResult.items,
2565
- (i) => i.name,
2566
- (i) => (i.applyTo ? `applies to: ${i.applyTo}` : undefined),
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
- sections.push(
2581
- formatSection(
2582
- "MCP Tools",
2583
- mcpTools,
2584
- (ct) => ct.tool.label || ct.tool.name,
2585
- () => undefined,
2586
- (ct) => {
2587
- const match = ct.path.match(/^mcp:(.+?) via (.+)$/);
2588
- if (match) {
2589
- const [, serverName, providerName] = match;
2590
- return { mcpServer: serverName, provider: providerName };
2591
- }
2592
- return ct.path.startsWith("mcp:") ? { mcpServer: ct.path.slice(4) } : "unknown";
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
- sections.push(
2601
- formatSection(
2602
- "Custom Tools",
2603
- customTools,
2604
- (ct) => ct.tool.label || ct.tool.name,
2605
- (ct) => ct.tool.description,
2606
- (ct) => {
2607
- if (ct.source?.provider === "builtin") return "builtin";
2608
- if (ct.path === "<exa>") return "builtin";
2609
- return ct.source ? { provider: ct.source.providerName, level: ct.source.level } : "unknown";
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
- if (fileCommands.length > 0) {
2619
- sections.push(
2620
- formatSection(
2621
- "Slash Commands",
2622
- fileCommands,
2623
- (cmd) => `/${cmd.name}`,
2624
- (cmd) => cmd.description,
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
- `${theme.bold(theme.fg("accent", "Hooks"))}\n${hookPaths.map((p) => ` ${theme.bold(basename(p))} ${theme.fg("dim", "hook")}`).join("\n")}`,
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
- if (sections.length === 0) {
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 sections) {
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
  }
@@ -2,8 +2,8 @@
2
2
  * Print mode (single-shot): Send prompts, output result, exit.
3
3
  *
4
4
  * Used for:
5
- * - `pi -p "prompt"` - text output
6
- * - `pi --mode json "prompt"` - JSON event stream
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";