@oh-my-pi/pi-coding-agent 13.4.0 → 13.4.1

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,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [13.4.1] - 2026-03-01
6
+
7
+ ### Fixed
8
+
9
+ - Pending resolve reminders now trigger as soon as a preview action is queued, before the next assistant turn, with regression coverage in `agent-session-resolve-reminder` tests
10
+
5
11
  ## [13.4.0] - 2026-03-01
6
12
 
7
13
  ### Breaking Changes
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.0",
4
+ "version": "13.4.1",
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.4.0",
45
- "@oh-my-pi/pi-agent-core": "13.4.0",
46
- "@oh-my-pi/pi-ai": "13.4.0",
47
- "@oh-my-pi/pi-natives": "13.4.0",
48
- "@oh-my-pi/pi-tui": "13.4.0",
49
- "@oh-my-pi/pi-utils": "13.4.0",
44
+ "@oh-my-pi/omp-stats": "13.4.1",
45
+ "@oh-my-pi/pi-agent-core": "13.4.1",
46
+ "@oh-my-pi/pi-ai": "13.4.1",
47
+ "@oh-my-pi/pi-natives": "13.4.1",
48
+ "@oh-my-pi/pi-tui": "13.4.1",
49
+ "@oh-my-pi/pi-utils": "13.4.1",
50
50
  "@sinclair/typebox": "^0.34",
51
51
  "@xterm/headless": "^6.0",
52
52
  "ajv": "^8.18",
@@ -4,22 +4,20 @@ Performs structural AST-aware rewrites via native ast-grep.
4
4
  - Use for codemods and structural rewrites where plain text replace is unsafe
5
5
  - Narrow scope with `path` before replacing (`path` accepts files, directories, or glob patterns)
6
6
  - Default to language-scoped rewrites in mixed repositories: set `lang` and keep `path` narrow
7
- - Always returns a preview; after reviewing, call `resolve` with `action: "apply"` or `action: "discard"`
8
7
  - Treat parse issues as a scoping signal: tighten `path`/`lang` before retrying
9
8
  - Metavariables captured in each rewrite pattern (`$A`, `$$$ARGS`) are substituted into that entry's rewrite template
10
9
  - Each matched rewrite is a 1:1 structural substitution; you cannot split one capture into multiple nodes or merge multiple captures into one node
11
10
  </instruction>
12
11
 
13
12
  <output>
14
- - Returns replacement summary, per-file replacement counts, and change previews
15
- - Reports whether changes were applied or only previewed
13
+ - Returns replacement summary, per-file replacement counts, and change diffs
16
14
  - Includes parse issues when files cannot be processed
17
15
  </output>
18
16
 
19
17
  <examples>
20
- - Rename a call site across a directory, preview first:
18
+ - Rename a call site across a directory:
21
19
  `{"ops":[{"pat":"oldApi($$$ARGS)","out":"newApi($$$ARGS)"}],"lang":"typescript","path":"src/"}`
22
- - Multi-op codemod preview before resolving:
20
+ - Multi-op codemod:
23
21
  `{"ops":[{"pat":"require($A)","out":"import $A"},{"pat":"module.exports = $E","out":"export default $E"}],"lang":"javascript","path":"src/"}`
24
22
  - Swap two arguments using captures:
25
23
  `{"ops":[{"pat":"assertEqual($A, $B)","out":"assertEqual($B, $A)"}],"lang":"typescript","path":"tests/"}`
@@ -28,6 +26,5 @@ Performs structural AST-aware rewrites via native ast-grep.
28
26
  <critical>
29
27
  - `ops` **MUST** contain at least one concrete `{ pat, out }` entry
30
28
  - If the path pattern spans multiple languages, set `lang` explicitly for deterministic rewrites
31
- - Review preview output, then use the `resolve` tool to apply or discard (with a reason)
32
29
  - For one-off local text edits, prefer the Edit tool instead of AST edit
33
30
  </critical>
@@ -43,6 +43,7 @@ Every edit has `op`, `pos`, and `lines`. Range replaces also have `end`. Both `p
43
43
  3. **Range end tag (inclusive):** `end` is inclusive and **MUST** point to the final line being replaced.
44
44
  - If `lines` includes a closing boundary token (`}`, `]`, `)`, `);`, `},`), `end` **MUST** include the original boundary line.
45
45
  - You **MUST NOT** set `end` to an interior line and then re-add the boundary token in `lines`; that duplicates the next surviving line.
46
+ - To remove a line while keeping its neighbors, **delete** it (`lines: null`). You **MUST NOT** replace it with the content of an adjacent line — that line still exists and will be duplicated.
46
47
  </rules>
47
48
 
48
49
  <recovery>
@@ -291,6 +291,7 @@ export class AgentSession {
291
291
 
292
292
  // Event subscription state
293
293
  #unsubscribeAgent?: () => void;
294
+ #unsubscribePendingActionPush?: () => void;
294
295
  #eventListeners: AgentSessionEventListener[] = [];
295
296
 
296
297
  /** Tracks pending steering messages for UI display. Removed when delivered. */
@@ -397,6 +398,21 @@ export class AgentSession {
397
398
  this.#obfuscator = config.obfuscator;
398
399
  this.agent.providerSessionState = this.#providerSessionState;
399
400
  this.#pendingActionStore = config.pendingActionStore;
401
+ this.#unsubscribePendingActionPush = this.#pendingActionStore?.subscribePush(action => {
402
+ const reminderText = [
403
+ "<system-reminder>",
404
+ "This is a preview. Call the `resolve` tool to apply or discard these changes.",
405
+ "</system-reminder>",
406
+ ].join("\n");
407
+ this.agent.steer({
408
+ role: "custom",
409
+ customType: "resolve-reminder",
410
+ content: reminderText,
411
+ display: false,
412
+ details: { toolName: action.sourceToolName },
413
+ timestamp: Date.now(),
414
+ });
415
+ });
400
416
  this.#syncTodoPhasesFromBranch();
401
417
 
402
418
  // Always subscribe to agent events for internal handling
@@ -688,22 +704,6 @@ export class AgentSession {
688
704
  { deliverAs: "nextTurn" },
689
705
  );
690
706
  }
691
- if (!isError && this.#pendingActionStore?.hasPending) {
692
- const reminderText = [
693
- "<system-reminder>",
694
- "This is a preview. Call the `resolve` tool to apply or discard these changes.",
695
- "</system-reminder>",
696
- ].join("\n");
697
- await this.sendCustomMessage(
698
- {
699
- customType: "resolve-reminder",
700
- content: reminderText,
701
- display: false,
702
- details: { toolName },
703
- },
704
- { deliverAs: "nextTurn" },
705
- );
706
- }
707
707
  }
708
708
  }
709
709
 
@@ -1443,6 +1443,8 @@ export class AgentSession {
1443
1443
  state.close();
1444
1444
  }
1445
1445
  this.#providerSessionState.clear();
1446
+ this.#unsubscribePendingActionPush?.();
1447
+ this.#unsubscribePendingActionPush = undefined;
1446
1448
  this.#disconnectFromAgent();
1447
1449
  this.#eventListeners = [];
1448
1450
  }
@@ -10,9 +10,14 @@ export interface PendingAction {
10
10
 
11
11
  export class PendingActionStore {
12
12
  #actions: PendingAction[] = [];
13
+ #pushListeners = new Set<(action: PendingAction, count: number) => void>();
13
14
 
14
15
  push(action: PendingAction): void {
15
16
  this.#actions.push(action);
17
+ const count = this.#actions.length;
18
+ for (const listener of this.#pushListeners) {
19
+ listener(action, count);
20
+ }
16
21
  }
17
22
 
18
23
  peek(): PendingAction | null {
@@ -23,10 +28,21 @@ export class PendingActionStore {
23
28
  return this.#actions.pop() ?? null;
24
29
  }
25
30
 
31
+ subscribePush(listener: (action: PendingAction, count: number) => void): () => void {
32
+ this.#pushListeners.add(listener);
33
+ return () => {
34
+ this.#pushListeners.delete(listener);
35
+ };
36
+ }
37
+
26
38
  clear(): void {
27
39
  this.#actions = [];
28
40
  }
29
41
 
42
+ get count(): number {
43
+ return this.#actions.length;
44
+ }
45
+
30
46
  get hasPending(): boolean {
31
47
  return this.#actions.length > 0;
32
48
  }