@oh-my-pi/pi-coding-agent 14.0.5 → 14.1.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.
Files changed (101) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/package.json +8 -8
  3. package/src/async/index.ts +1 -0
  4. package/src/async/job-manager.ts +43 -10
  5. package/src/async/support.ts +5 -0
  6. package/src/cli/list-models.ts +96 -57
  7. package/src/commit/agentic/tools/analyze-file.ts +1 -2
  8. package/src/commit/model-selection.ts +16 -13
  9. package/src/config/mcp-schema.json +1 -1
  10. package/src/config/model-equivalence.ts +675 -0
  11. package/src/config/model-registry.ts +242 -45
  12. package/src/config/model-resolver.ts +282 -65
  13. package/src/config/settings-schema.ts +27 -3
  14. package/src/config/settings.ts +1 -1
  15. package/src/cursor.ts +64 -23
  16. package/src/edit/index.ts +254 -89
  17. package/src/edit/modes/chunk.ts +336 -57
  18. package/src/edit/modes/hashline.ts +51 -26
  19. package/src/edit/modes/patch.ts +16 -10
  20. package/src/edit/modes/replace.ts +15 -7
  21. package/src/edit/renderer.ts +248 -94
  22. package/src/export/html/template.css +82 -0
  23. package/src/export/html/template.generated.ts +1 -1
  24. package/src/export/html/template.js +614 -97
  25. package/src/extensibility/custom-tools/types.ts +0 -3
  26. package/src/extensibility/extensions/loader.ts +16 -0
  27. package/src/extensibility/extensions/runner.ts +2 -7
  28. package/src/extensibility/extensions/types.ts +8 -4
  29. package/src/internal-urls/docs-index.generated.ts +4 -4
  30. package/src/internal-urls/jobs-protocol.ts +2 -1
  31. package/src/ipy/executor.ts +447 -52
  32. package/src/ipy/kernel.ts +39 -13
  33. package/src/lsp/client.ts +55 -1
  34. package/src/lsp/index.ts +8 -0
  35. package/src/lsp/types.ts +6 -0
  36. package/src/main.ts +6 -2
  37. package/src/memories/index.ts +7 -6
  38. package/src/modes/acp/acp-agent.ts +4 -1
  39. package/src/modes/components/bash-execution.ts +16 -4
  40. package/src/modes/components/model-selector.ts +221 -64
  41. package/src/modes/components/status-line/presets.ts +17 -6
  42. package/src/modes/components/status-line/segments.ts +15 -0
  43. package/src/modes/components/status-line-segment-editor.ts +1 -0
  44. package/src/modes/components/status-line.ts +7 -1
  45. package/src/modes/components/tool-execution.ts +145 -75
  46. package/src/modes/controllers/command-controller.ts +42 -1
  47. package/src/modes/controllers/event-controller.ts +4 -1
  48. package/src/modes/controllers/extension-ui-controller.ts +28 -5
  49. package/src/modes/controllers/input-controller.ts +9 -3
  50. package/src/modes/controllers/selector-controller.ts +17 -6
  51. package/src/modes/interactive-mode.ts +19 -3
  52. package/src/modes/print-mode.ts +13 -4
  53. package/src/modes/prompt-action-autocomplete.ts +3 -5
  54. package/src/modes/rpc/rpc-mode.ts +8 -2
  55. package/src/modes/shared.ts +2 -2
  56. package/src/modes/types.ts +1 -0
  57. package/src/modes/utils/ui-helpers.ts +1 -0
  58. package/src/prompts/system/system-prompt.md +5 -1
  59. package/src/prompts/tools/bash.md +16 -1
  60. package/src/prompts/tools/cancel-job.md +1 -1
  61. package/src/prompts/tools/chunk-edit.md +191 -163
  62. package/src/prompts/tools/hashline.md +11 -11
  63. package/src/prompts/tools/patch.md +10 -5
  64. package/src/prompts/tools/{await.md → poll.md} +1 -1
  65. package/src/prompts/tools/read-chunk.md +12 -3
  66. package/src/prompts/tools/read.md +9 -0
  67. package/src/prompts/tools/task.md +2 -2
  68. package/src/prompts/tools/vim.md +98 -0
  69. package/src/prompts/tools/write.md +1 -0
  70. package/src/sdk.ts +758 -725
  71. package/src/session/agent-session.ts +187 -40
  72. package/src/session/session-manager.ts +50 -4
  73. package/src/slash-commands/builtin-registry.ts +17 -0
  74. package/src/task/executor.ts +9 -5
  75. package/src/task/index.ts +3 -5
  76. package/src/task/types.ts +2 -2
  77. package/src/tools/bash.ts +240 -57
  78. package/src/tools/cancel-job.ts +2 -1
  79. package/src/tools/find.ts +5 -2
  80. package/src/tools/grep.ts +77 -8
  81. package/src/tools/index.ts +48 -19
  82. package/src/tools/inspect-image.ts +1 -1
  83. package/src/tools/{await-tool.ts → poll-tool.ts} +38 -31
  84. package/src/tools/python.ts +293 -278
  85. package/src/tools/read.ts +218 -1
  86. package/src/tools/sqlite-reader.ts +623 -0
  87. package/src/tools/submit-result.ts +5 -2
  88. package/src/tools/todo-write.ts +8 -2
  89. package/src/tools/vim.ts +966 -0
  90. package/src/tools/write.ts +187 -1
  91. package/src/utils/commit-message-generator.ts +1 -0
  92. package/src/utils/edit-mode.ts +2 -1
  93. package/src/utils/git.ts +24 -1
  94. package/src/utils/session-color.ts +55 -0
  95. package/src/utils/title-generator.ts +16 -7
  96. package/src/vim/buffer.ts +309 -0
  97. package/src/vim/commands.ts +382 -0
  98. package/src/vim/engine.ts +2426 -0
  99. package/src/vim/parser.ts +151 -0
  100. package/src/vim/render.ts +252 -0
  101. package/src/vim/types.ts +197 -0
package/src/tools/read.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { Database } from "bun:sqlite";
1
2
  import * as fs from "node:fs/promises";
2
3
  import * as path from "node:path";
3
4
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
@@ -38,7 +39,6 @@ import { resolveFileDisplayMode } from "../utils/file-display-mode";
38
39
  import { ImageInputTooLargeError, loadImageInput, MAX_IMAGE_INPUT_BYTES } from "../utils/image-loading";
39
40
  import { convertFileWithMarkit } from "../utils/markit";
40
41
  import { type ArchiveReader, openArchive, parseArchivePathCandidates } from "./archive-reader";
41
-
42
42
  import {
43
43
  executeReadUrl,
44
44
  isReadableUrlPath,
@@ -52,6 +52,22 @@ import { applyListLimit } from "./list-limit";
52
52
  import { formatFullOutputReference, formatStyledTruncationWarning, type OutputMeta } from "./output-meta";
53
53
  import { expandPath, resolveReadPath } from "./path-utils";
54
54
  import { formatAge, formatBytes, shortenPath, wrapBrackets } from "./render-utils";
55
+ import {
56
+ executeReadQuery,
57
+ getRowByKey,
58
+ getRowByRowId,
59
+ getTableSchema,
60
+ isSqliteFile,
61
+ listTables,
62
+ parseSqlitePathCandidates,
63
+ parseSqliteSelector,
64
+ queryRows,
65
+ renderRow,
66
+ renderSchema,
67
+ renderTable,
68
+ renderTableList,
69
+ resolveTableRowLookup,
70
+ } from "./sqlite-reader";
55
71
  import { ToolAbortError, ToolError, throwIfAborted } from "./tool-errors";
56
72
  import { toolResult } from "./tool-result";
57
73
 
@@ -420,6 +436,29 @@ interface ResolvedArchiveReadPath {
420
436
  suffixResolution?: { from: string; to: string };
421
437
  }
422
438
 
439
+ interface ResolvedSqliteReadPath {
440
+ absolutePath: string;
441
+ sqliteSubPath: string;
442
+ queryString: string;
443
+ suffixResolution?: { from: string; to: string };
444
+ }
445
+
446
+ function parseSqliteSelectorInput(selector: string | undefined): { subPath: string; queryString: string } {
447
+ if (!selector) {
448
+ return { subPath: "", queryString: "" };
449
+ }
450
+
451
+ const queryIndex = selector.indexOf("?");
452
+ if (queryIndex === -1) {
453
+ return { subPath: selector.replace(/^:+/, ""), queryString: "" };
454
+ }
455
+
456
+ return {
457
+ subPath: selector.slice(0, queryIndex).replace(/^:+/, ""),
458
+ queryString: selector.slice(queryIndex + 1),
459
+ };
460
+ }
461
+
423
462
  /**
424
463
  * Read tool implementation.
425
464
  *
@@ -502,6 +541,53 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
502
541
  return null;
503
542
  }
504
543
 
544
+ async #resolveSqliteReadPath(readPath: string, signal?: AbortSignal): Promise<ResolvedSqliteReadPath | null> {
545
+ const candidates = parseSqlitePathCandidates(readPath);
546
+ for (const candidate of candidates) {
547
+ let absolutePath = resolveReadPath(candidate.sqlitePath, this.session.cwd);
548
+ let suffixResolution: { from: string; to: string } | undefined;
549
+
550
+ try {
551
+ const stat = await Bun.file(absolutePath).stat();
552
+ if (stat.isDirectory()) continue;
553
+ if (!(await isSqliteFile(absolutePath))) continue;
554
+
555
+ return {
556
+ absolutePath,
557
+ sqliteSubPath: candidate.subPath,
558
+ queryString: candidate.queryString,
559
+ suffixResolution,
560
+ };
561
+ } catch (error) {
562
+ if (!isNotFoundError(error) || isRemoteMountPath(absolutePath)) continue;
563
+
564
+ const suffixMatch = await findUniqueSuffixMatch(candidate.sqlitePath, this.session.cwd, signal);
565
+ if (!suffixMatch) continue;
566
+
567
+ try {
568
+ const retryStat = await Bun.file(suffixMatch.absolutePath).stat();
569
+ if (retryStat.isDirectory()) continue;
570
+ if (!(await isSqliteFile(suffixMatch.absolutePath))) continue;
571
+
572
+ absolutePath = suffixMatch.absolutePath;
573
+ suffixResolution = { from: candidate.sqlitePath, to: suffixMatch.displayPath };
574
+ return {
575
+ absolutePath,
576
+ sqliteSubPath: candidate.subPath,
577
+ queryString: candidate.queryString,
578
+ suffixResolution,
579
+ };
580
+ } catch (retryError) {
581
+ if (!isNotFoundError(retryError)) {
582
+ throw retryError;
583
+ }
584
+ }
585
+ }
586
+ }
587
+
588
+ return null;
589
+ }
590
+
505
591
  #buildInMemoryTextResult(
506
592
  text: string,
507
593
  offset: number | undefined,
@@ -709,6 +795,132 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
709
795
  return result;
710
796
  }
711
797
 
798
+ async #readSqlite(
799
+ sel: string | undefined,
800
+ resolvedSqlitePath: ResolvedSqliteReadPath,
801
+ signal?: AbortSignal,
802
+ ): Promise<AgentToolResult<ReadToolDetails>> {
803
+ throwIfAborted(signal);
804
+
805
+ const selectorInput = sel
806
+ ? parseSqliteSelectorInput(sel)
807
+ : { subPath: resolvedSqlitePath.sqliteSubPath, queryString: resolvedSqlitePath.queryString };
808
+ const selector = parseSqliteSelector(selectorInput.subPath, selectorInput.queryString);
809
+ const details: ReadToolDetails = {
810
+ resolvedPath: resolvedSqlitePath.absolutePath,
811
+ suffixResolution: resolvedSqlitePath.suffixResolution,
812
+ };
813
+
814
+ let db: Database | null = null;
815
+ try {
816
+ db = new Database(resolvedSqlitePath.absolutePath, { readonly: true, strict: true });
817
+ db.run("PRAGMA busy_timeout = 3000");
818
+ throwIfAborted(signal);
819
+
820
+ switch (selector.kind) {
821
+ case "list": {
822
+ const listLimit = applyListLimit(listTables(db), { limit: 500 });
823
+ const output = prependSuffixResolutionNotice(
824
+ renderTableList(listLimit.items),
825
+ resolvedSqlitePath.suffixResolution,
826
+ );
827
+ const truncation = truncateHead(output, { maxLines: Number.MAX_SAFE_INTEGER });
828
+ details.truncation = truncation.truncated ? truncation : undefined;
829
+ const resultBuilder = toolResult<ReadToolDetails>(details)
830
+ .text(truncation.content)
831
+ .sourcePath(resolvedSqlitePath.absolutePath)
832
+ .limits({ resultLimit: listLimit.meta.resultLimit?.reached });
833
+ if (truncation.truncated) {
834
+ resultBuilder.truncation(truncation, { direction: "head" });
835
+ }
836
+ return resultBuilder.done();
837
+ }
838
+ case "schema": {
839
+ const sampleRows = queryRows(db, selector.table, { limit: selector.sampleLimit, offset: 0 });
840
+ let output = renderSchema(getTableSchema(db, selector.table), {
841
+ columns: sampleRows.columns,
842
+ rows: sampleRows.rows,
843
+ });
844
+ if (sampleRows.rows.length < sampleRows.totalCount) {
845
+ const remaining = sampleRows.totalCount - sampleRows.rows.length;
846
+ output += `\n[${remaining} more rows; use sel="${selector.table}?limit=20&offset=${sampleRows.rows.length}" to continue]`;
847
+ }
848
+ return toolResult<ReadToolDetails>(details)
849
+ .text(prependSuffixResolutionNotice(output, resolvedSqlitePath.suffixResolution))
850
+ .sourcePath(resolvedSqlitePath.absolutePath)
851
+ .done();
852
+ }
853
+ case "row": {
854
+ const lookup = resolveTableRowLookup(db, selector.table);
855
+ const row =
856
+ lookup.kind === "pk"
857
+ ? getRowByKey(db, selector.table, lookup, selector.key)
858
+ : getRowByRowId(db, selector.table, selector.key);
859
+ if (!row) {
860
+ return toolResult<ReadToolDetails>(details)
861
+ .text(
862
+ prependSuffixResolutionNotice(
863
+ `No row found in table '${selector.table}' for key '${selector.key}'.`,
864
+ resolvedSqlitePath.suffixResolution,
865
+ ),
866
+ )
867
+ .sourcePath(resolvedSqlitePath.absolutePath)
868
+ .done();
869
+ }
870
+ return toolResult<ReadToolDetails>(details)
871
+ .text(prependSuffixResolutionNotice(renderRow(row), resolvedSqlitePath.suffixResolution))
872
+ .sourcePath(resolvedSqlitePath.absolutePath)
873
+ .done();
874
+ }
875
+ case "query": {
876
+ const page = queryRows(db, selector.table, selector);
877
+ return toolResult<ReadToolDetails>(details)
878
+ .text(
879
+ prependSuffixResolutionNotice(
880
+ renderTable(page.columns, page.rows, {
881
+ totalCount: page.totalCount,
882
+ offset: selector.offset,
883
+ limit: selector.limit,
884
+ table: selector.table,
885
+ dbPath: resolvedSqlitePath.absolutePath,
886
+ }),
887
+ resolvedSqlitePath.suffixResolution,
888
+ ),
889
+ )
890
+ .sourcePath(resolvedSqlitePath.absolutePath)
891
+ .done();
892
+ }
893
+ case "raw": {
894
+ const result = executeReadQuery(db, selector.sql);
895
+ return toolResult<ReadToolDetails>(details)
896
+ .text(
897
+ prependSuffixResolutionNotice(
898
+ renderTable(result.columns, result.rows, {
899
+ totalCount: result.rows.length,
900
+ offset: 0,
901
+ limit: result.rows.length || DEFAULT_MAX_LINES,
902
+ table: "query",
903
+ dbPath: resolvedSqlitePath.absolutePath,
904
+ }),
905
+ resolvedSqlitePath.suffixResolution,
906
+ ),
907
+ )
908
+ .sourcePath(resolvedSqlitePath.absolutePath)
909
+ .done();
910
+ }
911
+ }
912
+
913
+ throw new ToolError("Unsupported SQLite selector");
914
+ } catch (error) {
915
+ if (error instanceof ToolError) {
916
+ throw error;
917
+ }
918
+ throw new ToolError(error instanceof Error ? error.message : String(error));
919
+ } finally {
920
+ db?.close();
921
+ }
922
+ }
923
+
712
924
  async execute(
713
925
  _toolCallId: string,
714
926
  params: ReadParams,
@@ -769,6 +981,11 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
769
981
  return this.#readArchive(readPath, offset, limit, archivePath, signal);
770
982
  }
771
983
 
984
+ const sqlitePath = await this.#resolveSqliteReadPath(readPath, signal);
985
+ if (sqlitePath) {
986
+ return this.#readSqlite(sel, sqlitePath, signal);
987
+ }
988
+
772
989
  let absolutePath = resolveReadPath(localReadPath, this.session.cwd);
773
990
  let suffixResolution: { from: string; to: string } | undefined;
774
991