@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.
- package/CHANGELOG.md +120 -0
- package/package.json +8 -8
- package/src/async/index.ts +1 -0
- package/src/async/job-manager.ts +43 -10
- package/src/async/support.ts +5 -0
- package/src/cli/list-models.ts +96 -57
- package/src/commit/agentic/tools/analyze-file.ts +1 -2
- package/src/commit/model-selection.ts +16 -13
- package/src/config/mcp-schema.json +1 -1
- package/src/config/model-equivalence.ts +675 -0
- package/src/config/model-registry.ts +242 -45
- package/src/config/model-resolver.ts +282 -65
- package/src/config/settings-schema.ts +27 -3
- package/src/config/settings.ts +1 -1
- package/src/cursor.ts +64 -23
- package/src/edit/index.ts +254 -89
- package/src/edit/modes/chunk.ts +336 -57
- package/src/edit/modes/hashline.ts +51 -26
- package/src/edit/modes/patch.ts +16 -10
- package/src/edit/modes/replace.ts +15 -7
- package/src/edit/renderer.ts +248 -94
- package/src/export/html/template.css +82 -0
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +614 -97
- package/src/extensibility/custom-tools/types.ts +0 -3
- package/src/extensibility/extensions/loader.ts +16 -0
- package/src/extensibility/extensions/runner.ts +2 -7
- package/src/extensibility/extensions/types.ts +8 -4
- package/src/internal-urls/docs-index.generated.ts +4 -4
- package/src/internal-urls/jobs-protocol.ts +2 -1
- package/src/ipy/executor.ts +447 -52
- package/src/ipy/kernel.ts +39 -13
- package/src/lsp/client.ts +55 -1
- package/src/lsp/index.ts +8 -0
- package/src/lsp/types.ts +6 -0
- package/src/main.ts +6 -2
- package/src/memories/index.ts +7 -6
- package/src/modes/acp/acp-agent.ts +4 -1
- package/src/modes/components/bash-execution.ts +16 -4
- package/src/modes/components/model-selector.ts +221 -64
- package/src/modes/components/status-line/presets.ts +17 -6
- package/src/modes/components/status-line/segments.ts +15 -0
- package/src/modes/components/status-line-segment-editor.ts +1 -0
- package/src/modes/components/status-line.ts +7 -1
- package/src/modes/components/tool-execution.ts +145 -75
- package/src/modes/controllers/command-controller.ts +42 -1
- package/src/modes/controllers/event-controller.ts +4 -1
- package/src/modes/controllers/extension-ui-controller.ts +28 -5
- package/src/modes/controllers/input-controller.ts +9 -3
- package/src/modes/controllers/selector-controller.ts +17 -6
- package/src/modes/interactive-mode.ts +19 -3
- package/src/modes/print-mode.ts +13 -4
- package/src/modes/prompt-action-autocomplete.ts +3 -5
- package/src/modes/rpc/rpc-mode.ts +8 -2
- package/src/modes/shared.ts +2 -2
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/prompts/system/system-prompt.md +5 -1
- package/src/prompts/tools/bash.md +16 -1
- package/src/prompts/tools/cancel-job.md +1 -1
- package/src/prompts/tools/chunk-edit.md +191 -163
- package/src/prompts/tools/hashline.md +11 -11
- package/src/prompts/tools/patch.md +10 -5
- package/src/prompts/tools/{await.md → poll.md} +1 -1
- package/src/prompts/tools/read-chunk.md +12 -3
- package/src/prompts/tools/read.md +9 -0
- package/src/prompts/tools/task.md +2 -2
- package/src/prompts/tools/vim.md +98 -0
- package/src/prompts/tools/write.md +1 -0
- package/src/sdk.ts +758 -725
- package/src/session/agent-session.ts +187 -40
- package/src/session/session-manager.ts +50 -4
- package/src/slash-commands/builtin-registry.ts +17 -0
- package/src/task/executor.ts +9 -5
- package/src/task/index.ts +3 -5
- package/src/task/types.ts +2 -2
- package/src/tools/bash.ts +240 -57
- package/src/tools/cancel-job.ts +2 -1
- package/src/tools/find.ts +5 -2
- package/src/tools/grep.ts +77 -8
- package/src/tools/index.ts +48 -19
- package/src/tools/inspect-image.ts +1 -1
- package/src/tools/{await-tool.ts → poll-tool.ts} +38 -31
- package/src/tools/python.ts +293 -278
- package/src/tools/read.ts +218 -1
- package/src/tools/sqlite-reader.ts +623 -0
- package/src/tools/submit-result.ts +5 -2
- package/src/tools/todo-write.ts +8 -2
- package/src/tools/vim.ts +966 -0
- package/src/tools/write.ts +187 -1
- package/src/utils/commit-message-generator.ts +1 -0
- package/src/utils/edit-mode.ts +2 -1
- package/src/utils/git.ts +24 -1
- package/src/utils/session-color.ts +55 -0
- package/src/utils/title-generator.ts +16 -7
- package/src/vim/buffer.ts +309 -0
- package/src/vim/commands.ts +382 -0
- package/src/vim/engine.ts +2426 -0
- package/src/vim/parser.ts +151 -0
- package/src/vim/render.ts +252 -0
- 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
|
|