@oh-my-pi/pi-coding-agent 5.2.0 → 5.3.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 CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.3.0] - 2026-01-15
6
+ ### Changed
7
+
8
+ - Expanded bash tool guidance to explicitly list appropriate use cases including file operations, build commands, and process management
9
+
10
+ ## [5.2.1] - 2026-01-14
11
+ ### Fixed
12
+
13
+ - Fixed stale diagnostic results by tracking diagnostic versions before file sync operations
14
+ - Fixed race condition where LSP diagnostics could return outdated results after file modifications
15
+
5
16
  ## [5.2.0] - 2026-01-14
6
17
 
7
18
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/pi-coding-agent",
3
- "version": "5.2.0",
3
+ "version": "5.3.0",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "ompConfig": {
@@ -39,10 +39,10 @@
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": "5.2.0",
43
- "@oh-my-pi/pi-ai": "5.2.0",
44
- "@oh-my-pi/pi-git-tool": "5.2.0",
45
- "@oh-my-pi/pi-tui": "5.2.0",
42
+ "@oh-my-pi/pi-agent-core": "5.3.0",
43
+ "@oh-my-pi/pi-ai": "5.3.0",
44
+ "@oh-my-pi/pi-git-tool": "5.3.0",
45
+ "@oh-my-pi/pi-tui": "5.3.0",
46
46
  "@openai/agents": "^0.3.7",
47
47
  "@silvia-odwyer/photon-node": "^0.3.4",
48
48
  "@sinclair/typebox": "^0.34.46",
@@ -331,12 +331,14 @@ async function waitForDiagnostics(
331
331
  uri: string,
332
332
  timeoutMs = 3000,
333
333
  signal?: AbortSignal,
334
+ minVersion?: number,
334
335
  ): Promise<Diagnostic[]> {
335
336
  const start = Date.now();
336
337
  while (Date.now() - start < timeoutMs) {
337
338
  signal?.throwIfAborted();
338
339
  const diagnostics = client.diagnostics.get(uri);
339
- if (diagnostics !== undefined) return diagnostics;
340
+ const versionOk = minVersion === undefined || client.diagnosticsVersion > minVersion;
341
+ if (diagnostics !== undefined && versionOk) return diagnostics;
340
342
  await sleep(100);
341
343
  }
342
344
  return client.diagnostics.get(uri) ?? [];
@@ -462,12 +464,35 @@ export interface FileDiagnosticsResult {
462
464
  formatter?: FileFormatResult;
463
465
  }
464
466
 
467
+ /** Captured diagnostic versions per server (before sync) */
468
+ type DiagnosticVersions = Map<string, number>;
469
+
470
+ /**
471
+ * Capture current diagnostic versions for all LSP servers.
472
+ * Call this BEFORE syncing content to detect stale diagnostics later.
473
+ */
474
+ async function captureDiagnosticVersions(
475
+ cwd: string,
476
+ servers: Array<[string, ServerConfig]>,
477
+ ): Promise<DiagnosticVersions> {
478
+ const versions = new Map<string, number>();
479
+ await Promise.allSettled(
480
+ servers.map(async ([serverName, serverConfig]) => {
481
+ if (serverConfig.createClient) return;
482
+ const client = await getOrCreateClient(serverConfig, cwd);
483
+ versions.set(serverName, client.diagnosticsVersion);
484
+ }),
485
+ );
486
+ return versions;
487
+ }
488
+
465
489
  /**
466
490
  * Get diagnostics for a file using LSP or custom linter client.
467
491
  *
468
492
  * @param absolutePath - Absolute path to the file
469
493
  * @param cwd - Working directory for LSP config resolution
470
494
  * @param servers - Servers to query diagnostics for
495
+ * @param minVersions - Minimum diagnostic versions per server (to detect stale results)
471
496
  * @returns Diagnostic results or undefined if no servers
472
497
  */
473
498
  async function getDiagnosticsForFile(
@@ -475,6 +500,7 @@ async function getDiagnosticsForFile(
475
500
  cwd: string,
476
501
  servers: Array<[string, ServerConfig]>,
477
502
  signal?: AbortSignal,
503
+ minVersions?: DiagnosticVersions,
478
504
  ): Promise<FileDiagnosticsResult | undefined> {
479
505
  if (servers.length === 0) {
480
506
  return undefined;
@@ -499,8 +525,9 @@ async function getDiagnosticsForFile(
499
525
  // Default: use LSP
500
526
  const client = await getOrCreateClient(serverConfig, cwd);
501
527
  signal?.throwIfAborted();
502
- // Content already synced + didSave sent, just wait for diagnostics
503
- const diagnostics = await waitForDiagnostics(client, uri, 3000, signal);
528
+ // Content already synced + didSave sent, wait for fresh diagnostics
529
+ const minVersion = minVersions?.get(serverName);
530
+ const diagnostics = await waitForDiagnostics(client, uri, 3000, signal, minVersion);
504
531
  return { serverName, diagnostics };
505
532
  }),
506
533
  );
@@ -675,6 +702,9 @@ export function createLspWritethrough(cwd: string, options?: WritethroughOptions
675
702
  const getWritePromise = once(() => writeContent(finalContent));
676
703
  const useCustomFormatter = enableFormat && customLinterServers.length > 0;
677
704
 
705
+ // Capture diagnostic versions BEFORE syncing to detect stale diagnostics
706
+ const minVersions = enableDiagnostics ? await captureDiagnosticVersions(cwd, servers) : undefined;
707
+
678
708
  let formatter: FileFormatResult | undefined;
679
709
  let diagnostics: FileDiagnosticsResult | undefined;
680
710
  try {
@@ -710,9 +740,9 @@ export function createLspWritethrough(cwd: string, options?: WritethroughOptions
710
740
  // 5. Notify saved to LSP servers
711
741
  await notifyFileSaved(dst, cwd, lspServers, operationSignal);
712
742
 
713
- // 6. Get diagnostics from all servers
743
+ // 6. Get diagnostics from all servers (wait for fresh results)
714
744
  if (enableDiagnostics) {
715
- diagnostics = await getDiagnosticsForFile(dst, cwd, servers, operationSignal);
745
+ diagnostics = await getDiagnosticsForFile(dst, cwd, servers, operationSignal, minVersions);
716
746
  }
717
747
  });
718
748
  } catch {
@@ -829,8 +859,9 @@ export function createLspTool(session: ToolSession): AgentTool<typeof lspSchema,
829
859
  continue;
830
860
  }
831
861
  const client = await getOrCreateClient(serverConfig, session.cwd);
862
+ const minVersion = client.diagnosticsVersion;
832
863
  await refreshFile(client, resolved);
833
- const diagnostics = await waitForDiagnostics(client, uri);
864
+ const diagnostics = await waitForDiagnostics(client, uri, 3000, undefined, minVersion);
834
865
  allDiagnostics.push(...diagnostics);
835
866
  } catch {
836
867
  // Server failed, continue with others
@@ -1091,8 +1122,9 @@ export function createLspTool(session: ToolSession): AgentTool<typeof lspSchema,
1091
1122
  };
1092
1123
  }
1093
1124
 
1125
+ const actionsMinVersion = client.diagnosticsVersion;
1094
1126
  await refreshFile(client, targetFile);
1095
- const diagnostics = await waitForDiagnostics(client, uri);
1127
+ const diagnostics = await waitForDiagnostics(client, uri, 3000, undefined, actionsMinVersion);
1096
1128
  const endLine = (end_line ?? line ?? 1) - 1;
1097
1129
  const endCharacter = (end_character ?? column ?? 1) - 1;
1098
1130
  const range = { start: position, end: { line: endLine, character: endCharacter } };
@@ -67,8 +67,18 @@ This matters. Get it right.
67
67
 
68
68
  Every tool is a choice. The wrong choice is friction. The right choice is invisible.
69
69
 
70
- ### What bash is not for
71
- Bash is the fallback, not the first reach.
70
+ ### What bash IS for
71
+ File and system operations:
72
+ - `mv`, `cp`, `rm`, `ln -s` — moving, copying, deleting, symlinking
73
+ - `mkdir -p`, `chmod` — directory creation, permissions
74
+ - `sd` — bulk find/replace across multiple files (faster than repeated edits)
75
+ - `tar`, `zip`, `unzip` — archives
76
+ - `curl` — downloading files
77
+ - Build commands: `cargo`, `npm`, `make`, `docker`
78
+ - Process management: running servers, background tasks
79
+
80
+ ### What bash is NOT for
81
+ Specialized tools exist. Use them.
72
82
 
73
83
  {{#has tools "read"}}- Reading files: `read` sees. `cat` just runs.{{/has}}
74
84
  {{#has tools "grep"}}- Searching content: `grep` finds. Shell pipelines guess.{{/has}}