@oh-my-pi/pi-coding-agent 13.9.14 → 13.9.15

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,16 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [13.9.15] - 2026-03-10
6
+ ### Added
7
+
8
+ - Added `ensureLoadingAnimation()` method to manage loading animation lifecycle and prevent duplicate spinners
9
+
10
+ ### Changed
11
+
12
+ - Refactored loading animation initialization to use centralized `ensureLoadingAnimation()` method in event and input controllers
13
+ - Updated `showError()` to properly clean up loading animation state when errors occur
14
+
5
15
  ## [13.9.12] - 2026-03-09
6
16
  ### Added
7
17
 
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.9.14",
4
+ "version": "13.9.15",
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.9.14",
45
- "@oh-my-pi/pi-agent-core": "13.9.14",
46
- "@oh-my-pi/pi-ai": "13.9.14",
47
- "@oh-my-pi/pi-natives": "13.9.14",
48
- "@oh-my-pi/pi-tui": "13.9.14",
49
- "@oh-my-pi/pi-utils": "13.9.14",
44
+ "@oh-my-pi/omp-stats": "13.9.15",
45
+ "@oh-my-pi/pi-agent-core": "13.9.15",
46
+ "@oh-my-pi/pi-ai": "13.9.15",
47
+ "@oh-my-pi/pi-natives": "13.9.15",
48
+ "@oh-my-pi/pi-tui": "13.9.15",
49
+ "@oh-my-pi/pi-utils": "13.9.15",
50
50
  "@sinclair/typebox": "^0.34",
51
51
  "@xterm/headless": "^6.0",
52
52
  "ajv": "^8.18",
@@ -107,19 +107,7 @@ export class EventController {
107
107
  this.ctx.retryLoader = undefined;
108
108
  this.ctx.statusContainer.clear();
109
109
  }
110
- if (this.ctx.loadingAnimation) {
111
- this.ctx.loadingAnimation.stop();
112
- }
113
- this.ctx.statusContainer.clear();
114
- this.ctx.loadingAnimation = new Loader(
115
- this.ctx.ui,
116
- spinner => theme.fg("accent", spinner),
117
- text => theme.fg("muted", text),
118
- `Working… (esc to interrupt)`,
119
- getSymbolTheme().spinnerFrames,
120
- );
121
- this.ctx.statusContainer.addChild(this.ctx.loadingAnimation);
122
- this.ctx.applyPendingWorkingMessage();
110
+ this.ctx.ensureLoadingAnimation();
123
111
  this.ctx.ui.requestRender();
124
112
  break;
125
113
 
@@ -339,6 +339,7 @@ export class InputController {
339
339
  };
340
340
  this.ctx.addMessageToChat(optimisticMessage);
341
341
  this.ctx.editor.setText("");
342
+ this.ctx.ensureLoadingAnimation();
342
343
  this.ctx.ui.requestRender();
343
344
 
344
345
  this.ctx.onInputCallback({ text, images });
@@ -5,8 +5,8 @@
5
5
  import * as path from "node:path";
6
6
  import { type Agent, type AgentMessage, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
7
7
  import type { AssistantMessage, ImageContent, Message, Model, UsageReport } from "@oh-my-pi/pi-ai";
8
- import type { Component, Loader, SlashCommand } from "@oh-my-pi/pi-tui";
9
- import { Container, Markdown, ProcessTerminal, Spacer, Text, TUI } from "@oh-my-pi/pi-tui";
8
+ import type { Component, SlashCommand } from "@oh-my-pi/pi-tui";
9
+ import { Container, Loader, Markdown, ProcessTerminal, Spacer, Text, TUI } from "@oh-my-pi/pi-tui";
10
10
  import { APP_NAME, getProjectDir, hsvToRgb, isEnoent, logger, postmortem } from "@oh-my-pi/pi-utils";
11
11
  import chalk from "chalk";
12
12
  import { KeybindingsManager } from "../config/keybindings";
@@ -46,7 +46,14 @@ import { SSHCommandController } from "./controllers/ssh-command-controller";
46
46
  import { OAuthManualInputManager } from "./oauth-manual-input";
47
47
  import { setMermaidRenderCallback } from "./theme/mermaid-cache";
48
48
  import type { Theme } from "./theme/theme";
49
- import { getEditorTheme, getMarkdownTheme, onTerminalAppearanceChange, onThemeChange, theme } from "./theme/theme";
49
+ import {
50
+ getEditorTheme,
51
+ getMarkdownTheme,
52
+ getSymbolTheme,
53
+ onTerminalAppearanceChange,
54
+ onThemeChange,
55
+ theme,
56
+ } from "./theme/theme";
50
57
  import type { CompactionQueuedMessage, InteractiveModeContext, TodoItem, TodoPhase } from "./types";
51
58
  import { UiHelpers } from "./utils/ui-helpers";
52
59
 
@@ -849,6 +856,12 @@ export class InteractiveMode implements InteractiveModeContext {
849
856
 
850
857
  showError(message: string): void {
851
858
  this.optimisticUserMessageSignature = undefined;
859
+ this.#pendingWorkingMessage = undefined;
860
+ if (this.loadingAnimation) {
861
+ this.loadingAnimation.stop();
862
+ this.loadingAnimation = undefined;
863
+ this.statusContainer.clear();
864
+ }
852
865
  this.#uiHelpers.showError(message);
853
866
  }
854
867
 
@@ -856,6 +869,22 @@ export class InteractiveMode implements InteractiveModeContext {
856
869
  this.#uiHelpers.showWarning(message);
857
870
  }
858
871
 
872
+ ensureLoadingAnimation(): void {
873
+ if (!this.loadingAnimation) {
874
+ this.statusContainer.clear();
875
+ this.loadingAnimation = new Loader(
876
+ this.ui,
877
+ spinner => theme.fg("accent", spinner),
878
+ text => theme.fg("muted", text),
879
+ this.#defaultWorkingMessage,
880
+ getSymbolTheme().spinnerFrames,
881
+ );
882
+ this.statusContainer.addChild(this.loadingAnimation);
883
+ }
884
+
885
+ this.applyPendingWorkingMessage();
886
+ }
887
+
859
888
  setWorkingMessage(message?: string): void {
860
889
  if (message === undefined) {
861
890
  this.#pendingWorkingMessage = undefined;
@@ -128,6 +128,7 @@ export interface InteractiveModeContext {
128
128
  flushPendingModelSwitch(): Promise<void>;
129
129
  setWorkingMessage(message?: string): void;
130
130
  applyPendingWorkingMessage(): void;
131
+ ensureLoadingAnimation(): void;
131
132
  isKnownSlashCommand(text: string): boolean;
132
133
  addMessageToChat(message: AgentMessage, options?: { populateHistory?: boolean }): void;
133
134
  renderSessionContext(