@mariozechner/pi-coding-agent 0.58.4 → 0.60.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 +56 -0
- package/README.md +14 -11
- package/dist/bun/cli.d.ts +3 -0
- package/dist/bun/cli.d.ts.map +1 -0
- package/dist/bun/cli.js +6 -0
- package/dist/bun/cli.js.map +1 -0
- package/dist/bun/register-bedrock.d.ts +2 -0
- package/dist/bun/register-bedrock.d.ts.map +1 -0
- package/dist/bun/register-bedrock.js +85 -0
- package/dist/bun/register-bedrock.js.map +1 -0
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +4 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/initial-message.d.ts +18 -0
- package/dist/cli/initial-message.d.ts.map +1 -0
- package/dist/cli/initial-message.js +22 -0
- package/dist/cli/initial-message.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +0 -3
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-session.d.ts +1 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +23 -3
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/bash-executor.d.ts +6 -7
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +8 -107
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +1 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +2 -0
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +5 -2
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +21 -4
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +1 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +6 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +83 -37
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/keybindings.d.ts +3 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +21 -11
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/package-manager.d.ts +15 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +194 -15
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +6 -7
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +3 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +8 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +75 -69
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +1 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +50 -19
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +12 -1
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +2 -1
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +2 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +50 -6
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/extensions.md +17 -3
- package/docs/keybindings.md +2 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +5 -5
|
@@ -8,6 +8,7 @@ import { minimatch } from "minimatch";
|
|
|
8
8
|
import { CONFIG_DIR_NAME } from "../config.js";
|
|
9
9
|
import { parseGitUrl } from "../utils/git.js";
|
|
10
10
|
const NETWORK_TIMEOUT_MS = 10000;
|
|
11
|
+
const UPDATE_CHECK_CONCURRENCY = 4;
|
|
11
12
|
function isOfflineModeEnabled() {
|
|
12
13
|
const value = process.env.PI_OFFLINE;
|
|
13
14
|
if (!value)
|
|
@@ -708,6 +709,62 @@ export class DefaultPackageManager {
|
|
|
708
709
|
return;
|
|
709
710
|
}
|
|
710
711
|
}
|
|
712
|
+
async checkForAvailableUpdates() {
|
|
713
|
+
if (isOfflineModeEnabled()) {
|
|
714
|
+
return [];
|
|
715
|
+
}
|
|
716
|
+
const globalSettings = this.settingsManager.getGlobalSettings();
|
|
717
|
+
const projectSettings = this.settingsManager.getProjectSettings();
|
|
718
|
+
const allPackages = [];
|
|
719
|
+
for (const pkg of projectSettings.packages ?? []) {
|
|
720
|
+
allPackages.push({ pkg, scope: "project" });
|
|
721
|
+
}
|
|
722
|
+
for (const pkg of globalSettings.packages ?? []) {
|
|
723
|
+
allPackages.push({ pkg, scope: "user" });
|
|
724
|
+
}
|
|
725
|
+
const packageSources = this.dedupePackages(allPackages);
|
|
726
|
+
const checks = packageSources
|
|
727
|
+
.filter((entry) => entry.scope !== "temporary")
|
|
728
|
+
.map((entry) => async () => {
|
|
729
|
+
const source = typeof entry.pkg === "string" ? entry.pkg : entry.pkg.source;
|
|
730
|
+
const parsed = this.parseSource(source);
|
|
731
|
+
if (parsed.type === "local" || parsed.pinned) {
|
|
732
|
+
return undefined;
|
|
733
|
+
}
|
|
734
|
+
if (parsed.type === "npm") {
|
|
735
|
+
const installedPath = this.getNpmInstallPath(parsed, entry.scope);
|
|
736
|
+
if (!existsSync(installedPath)) {
|
|
737
|
+
return undefined;
|
|
738
|
+
}
|
|
739
|
+
const hasUpdate = await this.npmHasAvailableUpdate(parsed, installedPath);
|
|
740
|
+
if (!hasUpdate) {
|
|
741
|
+
return undefined;
|
|
742
|
+
}
|
|
743
|
+
return {
|
|
744
|
+
source,
|
|
745
|
+
displayName: parsed.name,
|
|
746
|
+
type: "npm",
|
|
747
|
+
scope: entry.scope,
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
const installedPath = this.getGitInstallPath(parsed, entry.scope);
|
|
751
|
+
if (!existsSync(installedPath)) {
|
|
752
|
+
return undefined;
|
|
753
|
+
}
|
|
754
|
+
const hasUpdate = await this.gitHasAvailableUpdate(installedPath);
|
|
755
|
+
if (!hasUpdate) {
|
|
756
|
+
return undefined;
|
|
757
|
+
}
|
|
758
|
+
return {
|
|
759
|
+
source,
|
|
760
|
+
displayName: `${parsed.host}/${parsed.path}`,
|
|
761
|
+
type: "git",
|
|
762
|
+
scope: entry.scope,
|
|
763
|
+
};
|
|
764
|
+
});
|
|
765
|
+
const results = await this.runWithConcurrency(checks, UPDATE_CHECK_CONCURRENCY);
|
|
766
|
+
return results.filter((result) => result !== undefined);
|
|
767
|
+
}
|
|
711
768
|
async resolvePackageSources(sources, accumulator, onMissing) {
|
|
712
769
|
for (const { pkg, scope } of sources) {
|
|
713
770
|
const sourceStr = typeof pkg === "string" ? pkg : pkg.source;
|
|
@@ -737,7 +794,8 @@ export class DefaultPackageManager {
|
|
|
737
794
|
};
|
|
738
795
|
if (parsed.type === "npm") {
|
|
739
796
|
const installedPath = this.getNpmInstallPath(parsed, scope);
|
|
740
|
-
const needsInstall = !existsSync(installedPath) ||
|
|
797
|
+
const needsInstall = !existsSync(installedPath) ||
|
|
798
|
+
(parsed.pinned && !(await this.installedNpmMatchesPinnedVersion(parsed, installedPath)));
|
|
741
799
|
if (needsInstall) {
|
|
742
800
|
const installed = await installMissing();
|
|
743
801
|
if (!installed)
|
|
@@ -863,30 +921,30 @@ export class DefaultPackageManager {
|
|
|
863
921
|
}
|
|
864
922
|
return { type: "local", path: source };
|
|
865
923
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
924
|
+
async installedNpmMatchesPinnedVersion(source, installedPath) {
|
|
925
|
+
const installedVersion = this.getInstalledNpmVersion(installedPath);
|
|
926
|
+
if (!installedVersion) {
|
|
927
|
+
return false;
|
|
928
|
+
}
|
|
929
|
+
const { version: pinnedVersion } = this.parseNpmSpec(source.spec);
|
|
930
|
+
if (!pinnedVersion) {
|
|
931
|
+
return true;
|
|
932
|
+
}
|
|
933
|
+
return installedVersion === pinnedVersion;
|
|
934
|
+
}
|
|
935
|
+
async npmHasAvailableUpdate(source, installedPath) {
|
|
872
936
|
if (isOfflineModeEnabled()) {
|
|
873
937
|
return false;
|
|
874
938
|
}
|
|
875
939
|
const installedVersion = this.getInstalledNpmVersion(installedPath);
|
|
876
|
-
if (!installedVersion)
|
|
877
|
-
return
|
|
878
|
-
const { version: pinnedVersion } = this.parseNpmSpec(source.spec);
|
|
879
|
-
if (pinnedVersion) {
|
|
880
|
-
// Pinned: check if installed matches pinned (exact match for now)
|
|
881
|
-
return installedVersion !== pinnedVersion;
|
|
940
|
+
if (!installedVersion) {
|
|
941
|
+
return false;
|
|
882
942
|
}
|
|
883
|
-
// Unpinned: check registry for latest version
|
|
884
943
|
try {
|
|
885
944
|
const latestVersion = await this.getLatestNpmVersion(source.name);
|
|
886
945
|
return latestVersion !== installedVersion;
|
|
887
946
|
}
|
|
888
947
|
catch {
|
|
889
|
-
// If we can't check registry, assume it's fine
|
|
890
948
|
return false;
|
|
891
949
|
}
|
|
892
950
|
}
|
|
@@ -912,6 +970,84 @@ export class DefaultPackageManager {
|
|
|
912
970
|
const data = (await response.json());
|
|
913
971
|
return data.version;
|
|
914
972
|
}
|
|
973
|
+
async gitHasAvailableUpdate(installedPath) {
|
|
974
|
+
if (isOfflineModeEnabled()) {
|
|
975
|
+
return false;
|
|
976
|
+
}
|
|
977
|
+
try {
|
|
978
|
+
const localHead = await this.runCommandCapture("git", ["rev-parse", "HEAD"], {
|
|
979
|
+
cwd: installedPath,
|
|
980
|
+
timeoutMs: NETWORK_TIMEOUT_MS,
|
|
981
|
+
});
|
|
982
|
+
const remoteHead = await this.getRemoteGitHead(installedPath);
|
|
983
|
+
return localHead.trim() !== remoteHead.trim();
|
|
984
|
+
}
|
|
985
|
+
catch {
|
|
986
|
+
return false;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
async getRemoteGitHead(installedPath) {
|
|
990
|
+
const upstreamRef = await this.getGitUpstreamRef(installedPath);
|
|
991
|
+
if (upstreamRef) {
|
|
992
|
+
const remoteHead = await this.runGitRemoteCommand(installedPath, ["ls-remote", "origin", upstreamRef]);
|
|
993
|
+
const match = remoteHead.match(/^([0-9a-f]{40})\s+/m);
|
|
994
|
+
if (match?.[1]) {
|
|
995
|
+
return match[1];
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
const remoteHead = await this.runGitRemoteCommand(installedPath, ["ls-remote", "origin", "HEAD"]);
|
|
999
|
+
const match = remoteHead.match(/^([0-9a-f]{40})\s+HEAD$/m);
|
|
1000
|
+
if (!match?.[1]) {
|
|
1001
|
+
throw new Error("Failed to determine remote HEAD");
|
|
1002
|
+
}
|
|
1003
|
+
return match[1];
|
|
1004
|
+
}
|
|
1005
|
+
async getGitUpstreamRef(installedPath) {
|
|
1006
|
+
try {
|
|
1007
|
+
const upstream = await this.runCommandCapture("git", ["rev-parse", "--abbrev-ref", "@{upstream}"], {
|
|
1008
|
+
cwd: installedPath,
|
|
1009
|
+
timeoutMs: NETWORK_TIMEOUT_MS,
|
|
1010
|
+
});
|
|
1011
|
+
const trimmed = upstream.trim();
|
|
1012
|
+
if (!trimmed.startsWith("origin/")) {
|
|
1013
|
+
return undefined;
|
|
1014
|
+
}
|
|
1015
|
+
const branch = trimmed.slice("origin/".length);
|
|
1016
|
+
return branch ? `refs/heads/${branch}` : undefined;
|
|
1017
|
+
}
|
|
1018
|
+
catch {
|
|
1019
|
+
return undefined;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
runGitRemoteCommand(installedPath, args) {
|
|
1023
|
+
return this.runCommandCapture("git", args, {
|
|
1024
|
+
cwd: installedPath,
|
|
1025
|
+
timeoutMs: NETWORK_TIMEOUT_MS,
|
|
1026
|
+
env: {
|
|
1027
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
1028
|
+
},
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
async runWithConcurrency(tasks, limit) {
|
|
1032
|
+
if (tasks.length === 0) {
|
|
1033
|
+
return [];
|
|
1034
|
+
}
|
|
1035
|
+
const results = new Array(tasks.length);
|
|
1036
|
+
let nextIndex = 0;
|
|
1037
|
+
const workerCount = Math.max(1, Math.min(limit, tasks.length));
|
|
1038
|
+
const worker = async () => {
|
|
1039
|
+
while (true) {
|
|
1040
|
+
const index = nextIndex;
|
|
1041
|
+
nextIndex += 1;
|
|
1042
|
+
if (index >= tasks.length) {
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
results[index] = await tasks[index]();
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
await Promise.all(Array.from({ length: workerCount }, () => worker()));
|
|
1049
|
+
return results;
|
|
1050
|
+
}
|
|
915
1051
|
/**
|
|
916
1052
|
* Get a unique identity for a package, ignoring version/ref.
|
|
917
1053
|
* Used to detect when the same package is in both global and project settings.
|
|
@@ -1447,6 +1583,49 @@ export class DefaultPackageManager {
|
|
|
1447
1583
|
themes: toResolved(accumulator.themes),
|
|
1448
1584
|
};
|
|
1449
1585
|
}
|
|
1586
|
+
runCommandCapture(command, args, options) {
|
|
1587
|
+
return new Promise((resolvePromise, reject) => {
|
|
1588
|
+
const child = spawn(command, args, {
|
|
1589
|
+
cwd: options?.cwd,
|
|
1590
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1591
|
+
shell: process.platform === "win32",
|
|
1592
|
+
env: options?.env ? { ...process.env, ...options.env } : process.env,
|
|
1593
|
+
});
|
|
1594
|
+
let stdout = "";
|
|
1595
|
+
let stderr = "";
|
|
1596
|
+
let timedOut = false;
|
|
1597
|
+
const timeout = typeof options?.timeoutMs === "number"
|
|
1598
|
+
? setTimeout(() => {
|
|
1599
|
+
timedOut = true;
|
|
1600
|
+
child.kill();
|
|
1601
|
+
}, options.timeoutMs)
|
|
1602
|
+
: undefined;
|
|
1603
|
+
child.stdout?.on("data", (data) => {
|
|
1604
|
+
stdout += data.toString();
|
|
1605
|
+
});
|
|
1606
|
+
child.stderr?.on("data", (data) => {
|
|
1607
|
+
stderr += data.toString();
|
|
1608
|
+
});
|
|
1609
|
+
child.on("error", (error) => {
|
|
1610
|
+
if (timeout)
|
|
1611
|
+
clearTimeout(timeout);
|
|
1612
|
+
reject(error);
|
|
1613
|
+
});
|
|
1614
|
+
child.on("exit", (code) => {
|
|
1615
|
+
if (timeout)
|
|
1616
|
+
clearTimeout(timeout);
|
|
1617
|
+
if (timedOut) {
|
|
1618
|
+
reject(new Error(`${command} ${args.join(" ")} timed out after ${options?.timeoutMs}ms`));
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1621
|
+
if (code === 0) {
|
|
1622
|
+
resolvePromise(stdout.trim());
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
reject(new Error(`${command} ${args.join(" ")} failed with code ${code}: ${stderr || stdout}`));
|
|
1626
|
+
});
|
|
1627
|
+
});
|
|
1628
|
+
}
|
|
1450
1629
|
runCommand(command, args, options) {
|
|
1451
1630
|
return new Promise((resolvePromise, reject) => {
|
|
1452
1631
|
const child = spawn(command, args, {
|