@vandeepunk/pi-coding-agent 0.0.3 → 0.0.4

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 (83) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/dist/cli/args.d.ts.map +1 -1
  3. package/dist/cli/args.js +1 -0
  4. package/dist/cli/args.js.map +1 -1
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/config.js.map +1 -1
  7. package/dist/core/agent-session.d.ts.map +1 -1
  8. package/dist/core/agent-session.js +7 -0
  9. package/dist/core/agent-session.js.map +1 -1
  10. package/dist/core/auth-storage.d.ts.map +1 -1
  11. package/dist/core/auth-storage.js +16 -0
  12. package/dist/core/auth-storage.js.map +1 -1
  13. package/dist/core/export-html/template.css +3 -0
  14. package/dist/core/export-html/template.js +32 -15
  15. package/dist/core/extensions/loader.d.ts.map +1 -1
  16. package/dist/core/extensions/loader.js.map +1 -1
  17. package/dist/core/extensions/runner.d.ts +17 -2
  18. package/dist/core/extensions/runner.d.ts.map +1 -1
  19. package/dist/core/extensions/runner.js +53 -9
  20. package/dist/core/extensions/runner.js.map +1 -1
  21. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  22. package/dist/core/extensions/wrapper.js +3 -3
  23. package/dist/core/extensions/wrapper.js.map +1 -1
  24. package/dist/core/model-registry.d.ts +3 -1
  25. package/dist/core/model-registry.d.ts.map +1 -1
  26. package/dist/core/model-registry.js +133 -37
  27. package/dist/core/model-registry.js.map +1 -1
  28. package/dist/core/model-resolver.d.ts.map +1 -1
  29. package/dist/core/model-resolver.js +5 -5
  30. package/dist/core/model-resolver.js.map +1 -1
  31. package/dist/core/package-manager.d.ts +20 -0
  32. package/dist/core/package-manager.d.ts.map +1 -1
  33. package/dist/core/package-manager.js +122 -21
  34. package/dist/core/package-manager.js.map +1 -1
  35. package/dist/core/prompt-templates.d.ts.map +1 -1
  36. package/dist/core/prompt-templates.js.map +1 -1
  37. package/dist/core/settings-manager.d.ts.map +1 -1
  38. package/dist/core/settings-manager.js.map +1 -1
  39. package/dist/core/skills.d.ts.map +1 -1
  40. package/dist/core/skills.js +57 -3
  41. package/dist/core/skills.js.map +1 -1
  42. package/dist/core/slash-commands.d.ts.map +1 -1
  43. package/dist/core/slash-commands.js +2 -1
  44. package/dist/core/slash-commands.js.map +1 -1
  45. package/dist/main.d.ts.map +1 -1
  46. package/dist/main.js +172 -177
  47. package/dist/main.js.map +1 -1
  48. package/dist/migrations.d.ts.map +1 -1
  49. package/dist/migrations.js.map +1 -1
  50. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  51. package/dist/modes/interactive/components/assistant-message.js +9 -4
  52. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  53. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  54. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  55. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  56. package/dist/modes/interactive/components/model-selector.js +5 -0
  57. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  58. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  59. package/dist/modes/interactive/components/scoped-models-selector.js +5 -0
  60. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  61. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  62. package/dist/modes/interactive/components/tool-execution.js +49 -34
  63. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  64. package/dist/modes/interactive/interactive-mode.d.ts +0 -1
  65. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  66. package/dist/modes/interactive/interactive-mode.js +115 -102
  67. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  68. package/dist/utils/git.d.ts +21 -1
  69. package/dist/utils/git.d.ts.map +1 -1
  70. package/dist/utils/git.js +150 -4
  71. package/dist/utils/git.js.map +1 -1
  72. package/docs/extensions.md +5 -0
  73. package/docs/models.md +40 -1
  74. package/docs/packages.md +20 -0
  75. package/docs/providers.md +13 -0
  76. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  77. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  78. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  79. package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
  80. package/examples/extensions/hello.ts +1 -1
  81. package/examples/extensions/with-deps/package-lock.json +2 -2
  82. package/examples/extensions/with-deps/package.json +1 -1
  83. package/package.json +7 -8
@@ -1,2 +1,22 @@
1
- export declare function looksLikeGitUrl(source: string): boolean;
1
+ /**
2
+ * Parsed git URL information.
3
+ */
4
+ export type GitSource = {
5
+ /** Always "git" for git sources */
6
+ type: "git";
7
+ /** Clone URL (always valid for git clone, without ref suffix) */
8
+ repo: string;
9
+ /** Git host domain (e.g., "github.com") */
10
+ host: string;
11
+ /** Repository path (e.g., "user/repo") */
12
+ path: string;
13
+ /** Git ref (branch, tag, commit) if specified */
14
+ ref?: string;
15
+ /** True if ref was specified (package won't be auto-updated) */
16
+ pinned: boolean;
17
+ };
18
+ /**
19
+ * Parse any git URL (SSH or HTTPS) into a GitSource.
20
+ */
21
+ export declare function parseGitUrl(source: string): GitSource | null;
2
22
  //# sourceMappingURL=git.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"AAEA,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGvD","sourcesContent":["const GIT_HOSTS = [\"github.com\", \"gitlab.com\", \"bitbucket.org\", \"codeberg.org\"];\n\nexport function looksLikeGitUrl(source: string): boolean {\n\tconst normalized = source.replace(/^https?:\\/\\//, \"\");\n\treturn GIT_HOSTS.some((host) => normalized.startsWith(`${host}/`));\n}\n"]}
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACvB,mCAAmC;IACnC,IAAI,EAAE,KAAK,CAAC;IACZ,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,MAAM,EAAE,OAAO,CAAC;CAChB,CAAC;AA2GF;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAkD5D","sourcesContent":["import hostedGitInfo from \"hosted-git-info\";\n\n/**\n * Parsed git URL information.\n */\nexport type GitSource = {\n\t/** Always \"git\" for git sources */\n\ttype: \"git\";\n\t/** Clone URL (always valid for git clone, without ref suffix) */\n\trepo: string;\n\t/** Git host domain (e.g., \"github.com\") */\n\thost: string;\n\t/** Repository path (e.g., \"user/repo\") */\n\tpath: string;\n\t/** Git ref (branch, tag, commit) if specified */\n\tref?: string;\n\t/** True if ref was specified (package won't be auto-updated) */\n\tpinned: boolean;\n};\n\nfunction splitRef(url: string): { repo: string; ref?: string } {\n\tconst scpLikeMatch = url.match(/^git@([^:]+):(.+)$/);\n\tif (scpLikeMatch) {\n\t\tconst pathWithMaybeRef = scpLikeMatch[2] ?? \"\";\n\t\tconst refSeparator = pathWithMaybeRef.indexOf(\"@\");\n\t\tif (refSeparator < 0) return { repo: url };\n\t\tconst repoPath = pathWithMaybeRef.slice(0, refSeparator);\n\t\tconst ref = pathWithMaybeRef.slice(refSeparator + 1);\n\t\tif (!repoPath || !ref) return { repo: url };\n\t\treturn {\n\t\t\trepo: `git@${scpLikeMatch[1] ?? \"\"}:${repoPath}`,\n\t\t\tref,\n\t\t};\n\t}\n\n\tif (url.includes(\"://\")) {\n\t\ttry {\n\t\t\tconst parsed = new URL(url);\n\t\t\tconst pathWithMaybeRef = parsed.pathname.replace(/^\\/+/, \"\");\n\t\t\tconst refSeparator = pathWithMaybeRef.indexOf(\"@\");\n\t\t\tif (refSeparator < 0) return { repo: url };\n\t\t\tconst repoPath = pathWithMaybeRef.slice(0, refSeparator);\n\t\t\tconst ref = pathWithMaybeRef.slice(refSeparator + 1);\n\t\t\tif (!repoPath || !ref) return { repo: url };\n\t\t\tparsed.pathname = `/${repoPath}`;\n\t\t\treturn {\n\t\t\t\trepo: parsed.toString().replace(/\\/$/, \"\"),\n\t\t\t\tref,\n\t\t\t};\n\t\t} catch {\n\t\t\treturn { repo: url };\n\t\t}\n\t}\n\n\tconst slashIndex = url.indexOf(\"/\");\n\tif (slashIndex < 0) {\n\t\treturn { repo: url };\n\t}\n\tconst host = url.slice(0, slashIndex);\n\tconst pathWithMaybeRef = url.slice(slashIndex + 1);\n\tconst refSeparator = pathWithMaybeRef.indexOf(\"@\");\n\tif (refSeparator < 0) {\n\t\treturn { repo: url };\n\t}\n\tconst repoPath = pathWithMaybeRef.slice(0, refSeparator);\n\tconst ref = pathWithMaybeRef.slice(refSeparator + 1);\n\tif (!repoPath || !ref) {\n\t\treturn { repo: url };\n\t}\n\treturn {\n\t\trepo: `${host}/${repoPath}`,\n\t\tref,\n\t};\n}\n\nfunction parseGenericGitUrl(url: string): GitSource | null {\n\tconst { repo: repoWithoutRef, ref } = splitRef(url);\n\tlet repo = repoWithoutRef;\n\tlet host = \"\";\n\tlet path = \"\";\n\n\tconst scpLikeMatch = repoWithoutRef.match(/^git@([^:]+):(.+)$/);\n\tif (scpLikeMatch) {\n\t\thost = scpLikeMatch[1] ?? \"\";\n\t\tpath = scpLikeMatch[2] ?? \"\";\n\t} else if (\n\t\trepoWithoutRef.startsWith(\"https://\") ||\n\t\trepoWithoutRef.startsWith(\"http://\") ||\n\t\trepoWithoutRef.startsWith(\"ssh://\")\n\t) {\n\t\ttry {\n\t\t\tconst parsed = new URL(repoWithoutRef);\n\t\t\thost = parsed.hostname;\n\t\t\tpath = parsed.pathname.replace(/^\\/+/, \"\");\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t} else {\n\t\tconst slashIndex = repoWithoutRef.indexOf(\"/\");\n\t\tif (slashIndex < 0) {\n\t\t\treturn null;\n\t\t}\n\t\thost = repoWithoutRef.slice(0, slashIndex);\n\t\tpath = repoWithoutRef.slice(slashIndex + 1);\n\t\tif (!host.includes(\".\") && host !== \"localhost\") {\n\t\t\treturn null;\n\t\t}\n\t\trepo = `https://${repoWithoutRef}`;\n\t}\n\n\tconst normalizedPath = path.replace(/\\.git$/, \"\").replace(/^\\/+/, \"\");\n\tif (!host || !normalizedPath || normalizedPath.split(\"/\").length < 2) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\ttype: \"git\",\n\t\trepo,\n\t\thost,\n\t\tpath: normalizedPath,\n\t\tref,\n\t\tpinned: Boolean(ref),\n\t};\n}\n\n/**\n * Parse any git URL (SSH or HTTPS) into a GitSource.\n */\nexport function parseGitUrl(source: string): GitSource | null {\n\tconst url = source.startsWith(\"git:\") ? source.slice(4).trim() : source;\n\tconst split = splitRef(url);\n\n\tconst hostedCandidates = [split.ref ? `${split.repo}#${split.ref}` : undefined, url].filter(\n\t\t(value): value is string => Boolean(value),\n\t);\n\tfor (const candidate of hostedCandidates) {\n\t\tconst info = hostedGitInfo.fromUrl(candidate);\n\t\tif (info) {\n\t\t\tif (split.ref && info.project?.includes(\"@\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst useHttpsPrefix =\n\t\t\t\t!split.repo.startsWith(\"http://\") &&\n\t\t\t\t!split.repo.startsWith(\"https://\") &&\n\t\t\t\t!split.repo.startsWith(\"ssh://\") &&\n\t\t\t\t!split.repo.startsWith(\"git@\");\n\t\t\treturn {\n\t\t\t\ttype: \"git\",\n\t\t\t\trepo: useHttpsPrefix ? `https://${split.repo}` : split.repo,\n\t\t\t\thost: info.domain || \"\",\n\t\t\t\tpath: `${info.user}/${info.project}`.replace(/\\.git$/, \"\"),\n\t\t\t\tref: info.committish || split.ref || undefined,\n\t\t\t\tpinned: Boolean(info.committish || split.ref),\n\t\t\t};\n\t\t}\n\t}\n\n\tconst httpsCandidates = [split.ref ? `https://${split.repo}#${split.ref}` : undefined, `https://${url}`].filter(\n\t\t(value): value is string => Boolean(value),\n\t);\n\tfor (const candidate of httpsCandidates) {\n\t\tconst info = hostedGitInfo.fromUrl(candidate);\n\t\tif (info) {\n\t\t\tif (split.ref && info.project?.includes(\"@\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\ttype: \"git\",\n\t\t\t\trepo: `https://${split.repo}`,\n\t\t\t\thost: info.domain || \"\",\n\t\t\t\tpath: `${info.user}/${info.project}`.replace(/\\.git$/, \"\"),\n\t\t\t\tref: info.committish || split.ref || undefined,\n\t\t\t\tpinned: Boolean(info.committish || split.ref),\n\t\t\t};\n\t\t}\n\t}\n\n\treturn parseGenericGitUrl(url);\n}\n"]}
package/dist/utils/git.js CHANGED
@@ -1,6 +1,152 @@
1
- const GIT_HOSTS = ["github.com", "gitlab.com", "bitbucket.org", "codeberg.org"];
2
- export function looksLikeGitUrl(source) {
3
- const normalized = source.replace(/^https?:\/\//, "");
4
- return GIT_HOSTS.some((host) => normalized.startsWith(`${host}/`));
1
+ import hostedGitInfo from "hosted-git-info";
2
+ function splitRef(url) {
3
+ const scpLikeMatch = url.match(/^git@([^:]+):(.+)$/);
4
+ if (scpLikeMatch) {
5
+ const pathWithMaybeRef = scpLikeMatch[2] ?? "";
6
+ const refSeparator = pathWithMaybeRef.indexOf("@");
7
+ if (refSeparator < 0)
8
+ return { repo: url };
9
+ const repoPath = pathWithMaybeRef.slice(0, refSeparator);
10
+ const ref = pathWithMaybeRef.slice(refSeparator + 1);
11
+ if (!repoPath || !ref)
12
+ return { repo: url };
13
+ return {
14
+ repo: `git@${scpLikeMatch[1] ?? ""}:${repoPath}`,
15
+ ref,
16
+ };
17
+ }
18
+ if (url.includes("://")) {
19
+ try {
20
+ const parsed = new URL(url);
21
+ const pathWithMaybeRef = parsed.pathname.replace(/^\/+/, "");
22
+ const refSeparator = pathWithMaybeRef.indexOf("@");
23
+ if (refSeparator < 0)
24
+ return { repo: url };
25
+ const repoPath = pathWithMaybeRef.slice(0, refSeparator);
26
+ const ref = pathWithMaybeRef.slice(refSeparator + 1);
27
+ if (!repoPath || !ref)
28
+ return { repo: url };
29
+ parsed.pathname = `/${repoPath}`;
30
+ return {
31
+ repo: parsed.toString().replace(/\/$/, ""),
32
+ ref,
33
+ };
34
+ }
35
+ catch {
36
+ return { repo: url };
37
+ }
38
+ }
39
+ const slashIndex = url.indexOf("/");
40
+ if (slashIndex < 0) {
41
+ return { repo: url };
42
+ }
43
+ const host = url.slice(0, slashIndex);
44
+ const pathWithMaybeRef = url.slice(slashIndex + 1);
45
+ const refSeparator = pathWithMaybeRef.indexOf("@");
46
+ if (refSeparator < 0) {
47
+ return { repo: url };
48
+ }
49
+ const repoPath = pathWithMaybeRef.slice(0, refSeparator);
50
+ const ref = pathWithMaybeRef.slice(refSeparator + 1);
51
+ if (!repoPath || !ref) {
52
+ return { repo: url };
53
+ }
54
+ return {
55
+ repo: `${host}/${repoPath}`,
56
+ ref,
57
+ };
58
+ }
59
+ function parseGenericGitUrl(url) {
60
+ const { repo: repoWithoutRef, ref } = splitRef(url);
61
+ let repo = repoWithoutRef;
62
+ let host = "";
63
+ let path = "";
64
+ const scpLikeMatch = repoWithoutRef.match(/^git@([^:]+):(.+)$/);
65
+ if (scpLikeMatch) {
66
+ host = scpLikeMatch[1] ?? "";
67
+ path = scpLikeMatch[2] ?? "";
68
+ }
69
+ else if (repoWithoutRef.startsWith("https://") ||
70
+ repoWithoutRef.startsWith("http://") ||
71
+ repoWithoutRef.startsWith("ssh://")) {
72
+ try {
73
+ const parsed = new URL(repoWithoutRef);
74
+ host = parsed.hostname;
75
+ path = parsed.pathname.replace(/^\/+/, "");
76
+ }
77
+ catch {
78
+ return null;
79
+ }
80
+ }
81
+ else {
82
+ const slashIndex = repoWithoutRef.indexOf("/");
83
+ if (slashIndex < 0) {
84
+ return null;
85
+ }
86
+ host = repoWithoutRef.slice(0, slashIndex);
87
+ path = repoWithoutRef.slice(slashIndex + 1);
88
+ if (!host.includes(".") && host !== "localhost") {
89
+ return null;
90
+ }
91
+ repo = `https://${repoWithoutRef}`;
92
+ }
93
+ const normalizedPath = path.replace(/\.git$/, "").replace(/^\/+/, "");
94
+ if (!host || !normalizedPath || normalizedPath.split("/").length < 2) {
95
+ return null;
96
+ }
97
+ return {
98
+ type: "git",
99
+ repo,
100
+ host,
101
+ path: normalizedPath,
102
+ ref,
103
+ pinned: Boolean(ref),
104
+ };
105
+ }
106
+ /**
107
+ * Parse any git URL (SSH or HTTPS) into a GitSource.
108
+ */
109
+ export function parseGitUrl(source) {
110
+ const url = source.startsWith("git:") ? source.slice(4).trim() : source;
111
+ const split = splitRef(url);
112
+ const hostedCandidates = [split.ref ? `${split.repo}#${split.ref}` : undefined, url].filter((value) => Boolean(value));
113
+ for (const candidate of hostedCandidates) {
114
+ const info = hostedGitInfo.fromUrl(candidate);
115
+ if (info) {
116
+ if (split.ref && info.project?.includes("@")) {
117
+ continue;
118
+ }
119
+ const useHttpsPrefix = !split.repo.startsWith("http://") &&
120
+ !split.repo.startsWith("https://") &&
121
+ !split.repo.startsWith("ssh://") &&
122
+ !split.repo.startsWith("git@");
123
+ return {
124
+ type: "git",
125
+ repo: useHttpsPrefix ? `https://${split.repo}` : split.repo,
126
+ host: info.domain || "",
127
+ path: `${info.user}/${info.project}`.replace(/\.git$/, ""),
128
+ ref: info.committish || split.ref || undefined,
129
+ pinned: Boolean(info.committish || split.ref),
130
+ };
131
+ }
132
+ }
133
+ const httpsCandidates = [split.ref ? `https://${split.repo}#${split.ref}` : undefined, `https://${url}`].filter((value) => Boolean(value));
134
+ for (const candidate of httpsCandidates) {
135
+ const info = hostedGitInfo.fromUrl(candidate);
136
+ if (info) {
137
+ if (split.ref && info.project?.includes("@")) {
138
+ continue;
139
+ }
140
+ return {
141
+ type: "git",
142
+ repo: `https://${split.repo}`,
143
+ host: info.domain || "",
144
+ path: `${info.user}/${info.project}`.replace(/\.git$/, ""),
145
+ ref: info.committish || split.ref || undefined,
146
+ pinned: Boolean(info.committish || split.ref),
147
+ };
148
+ }
149
+ }
150
+ return parseGenericGitUrl(url);
5
151
  }
6
152
  //# sourceMappingURL=git.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"AAAA,MAAM,SAAS,GAAG,CAAC,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,CAAC,CAAC;AAEhF,MAAM,UAAU,eAAe,CAAC,MAAc,EAAW;IACxD,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IACtD,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;AAAA,CACnE","sourcesContent":["const GIT_HOSTS = [\"github.com\", \"gitlab.com\", \"bitbucket.org\", \"codeberg.org\"];\n\nexport function looksLikeGitUrl(source: string): boolean {\n\tconst normalized = source.replace(/^https?:\\/\\//, \"\");\n\treturn GIT_HOSTS.some((host) => normalized.startsWith(`${host}/`));\n}\n"]}
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"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","sourcesContent":["import hostedGitInfo from \"hosted-git-info\";\n\n/**\n * Parsed git URL information.\n */\nexport type GitSource = {\n\t/** Always \"git\" for git sources */\n\ttype: \"git\";\n\t/** Clone URL (always valid for git clone, without ref suffix) */\n\trepo: string;\n\t/** Git host domain (e.g., \"github.com\") */\n\thost: string;\n\t/** Repository path (e.g., \"user/repo\") */\n\tpath: string;\n\t/** Git ref (branch, tag, commit) if specified */\n\tref?: string;\n\t/** True if ref was specified (package won't be auto-updated) */\n\tpinned: boolean;\n};\n\nfunction splitRef(url: string): { repo: string; ref?: string } {\n\tconst scpLikeMatch = url.match(/^git@([^:]+):(.+)$/);\n\tif (scpLikeMatch) {\n\t\tconst pathWithMaybeRef = scpLikeMatch[2] ?? \"\";\n\t\tconst refSeparator = pathWithMaybeRef.indexOf(\"@\");\n\t\tif (refSeparator < 0) return { repo: url };\n\t\tconst repoPath = pathWithMaybeRef.slice(0, refSeparator);\n\t\tconst ref = pathWithMaybeRef.slice(refSeparator + 1);\n\t\tif (!repoPath || !ref) return { repo: url };\n\t\treturn {\n\t\t\trepo: `git@${scpLikeMatch[1] ?? \"\"}:${repoPath}`,\n\t\t\tref,\n\t\t};\n\t}\n\n\tif (url.includes(\"://\")) {\n\t\ttry {\n\t\t\tconst parsed = new URL(url);\n\t\t\tconst pathWithMaybeRef = parsed.pathname.replace(/^\\/+/, \"\");\n\t\t\tconst refSeparator = pathWithMaybeRef.indexOf(\"@\");\n\t\t\tif (refSeparator < 0) return { repo: url };\n\t\t\tconst repoPath = pathWithMaybeRef.slice(0, refSeparator);\n\t\t\tconst ref = pathWithMaybeRef.slice(refSeparator + 1);\n\t\t\tif (!repoPath || !ref) return { repo: url };\n\t\t\tparsed.pathname = `/${repoPath}`;\n\t\t\treturn {\n\t\t\t\trepo: parsed.toString().replace(/\\/$/, \"\"),\n\t\t\t\tref,\n\t\t\t};\n\t\t} catch {\n\t\t\treturn { repo: url };\n\t\t}\n\t}\n\n\tconst slashIndex = url.indexOf(\"/\");\n\tif (slashIndex < 0) {\n\t\treturn { repo: url };\n\t}\n\tconst host = url.slice(0, slashIndex);\n\tconst pathWithMaybeRef = url.slice(slashIndex + 1);\n\tconst refSeparator = pathWithMaybeRef.indexOf(\"@\");\n\tif (refSeparator < 0) {\n\t\treturn { repo: url };\n\t}\n\tconst repoPath = pathWithMaybeRef.slice(0, refSeparator);\n\tconst ref = pathWithMaybeRef.slice(refSeparator + 1);\n\tif (!repoPath || !ref) {\n\t\treturn { repo: url };\n\t}\n\treturn {\n\t\trepo: `${host}/${repoPath}`,\n\t\tref,\n\t};\n}\n\nfunction parseGenericGitUrl(url: string): GitSource | null {\n\tconst { repo: repoWithoutRef, ref } = splitRef(url);\n\tlet repo = repoWithoutRef;\n\tlet host = \"\";\n\tlet path = \"\";\n\n\tconst scpLikeMatch = repoWithoutRef.match(/^git@([^:]+):(.+)$/);\n\tif (scpLikeMatch) {\n\t\thost = scpLikeMatch[1] ?? \"\";\n\t\tpath = scpLikeMatch[2] ?? \"\";\n\t} else if (\n\t\trepoWithoutRef.startsWith(\"https://\") ||\n\t\trepoWithoutRef.startsWith(\"http://\") ||\n\t\trepoWithoutRef.startsWith(\"ssh://\")\n\t) {\n\t\ttry {\n\t\t\tconst parsed = new URL(repoWithoutRef);\n\t\t\thost = parsed.hostname;\n\t\t\tpath = parsed.pathname.replace(/^\\/+/, \"\");\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t} else {\n\t\tconst slashIndex = repoWithoutRef.indexOf(\"/\");\n\t\tif (slashIndex < 0) {\n\t\t\treturn null;\n\t\t}\n\t\thost = repoWithoutRef.slice(0, slashIndex);\n\t\tpath = repoWithoutRef.slice(slashIndex + 1);\n\t\tif (!host.includes(\".\") && host !== \"localhost\") {\n\t\t\treturn null;\n\t\t}\n\t\trepo = `https://${repoWithoutRef}`;\n\t}\n\n\tconst normalizedPath = path.replace(/\\.git$/, \"\").replace(/^\\/+/, \"\");\n\tif (!host || !normalizedPath || normalizedPath.split(\"/\").length < 2) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\ttype: \"git\",\n\t\trepo,\n\t\thost,\n\t\tpath: normalizedPath,\n\t\tref,\n\t\tpinned: Boolean(ref),\n\t};\n}\n\n/**\n * Parse any git URL (SSH or HTTPS) into a GitSource.\n */\nexport function parseGitUrl(source: string): GitSource | null {\n\tconst url = source.startsWith(\"git:\") ? source.slice(4).trim() : source;\n\tconst split = splitRef(url);\n\n\tconst hostedCandidates = [split.ref ? `${split.repo}#${split.ref}` : undefined, url].filter(\n\t\t(value): value is string => Boolean(value),\n\t);\n\tfor (const candidate of hostedCandidates) {\n\t\tconst info = hostedGitInfo.fromUrl(candidate);\n\t\tif (info) {\n\t\t\tif (split.ref && info.project?.includes(\"@\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst useHttpsPrefix =\n\t\t\t\t!split.repo.startsWith(\"http://\") &&\n\t\t\t\t!split.repo.startsWith(\"https://\") &&\n\t\t\t\t!split.repo.startsWith(\"ssh://\") &&\n\t\t\t\t!split.repo.startsWith(\"git@\");\n\t\t\treturn {\n\t\t\t\ttype: \"git\",\n\t\t\t\trepo: useHttpsPrefix ? `https://${split.repo}` : split.repo,\n\t\t\t\thost: info.domain || \"\",\n\t\t\t\tpath: `${info.user}/${info.project}`.replace(/\\.git$/, \"\"),\n\t\t\t\tref: info.committish || split.ref || undefined,\n\t\t\t\tpinned: Boolean(info.committish || split.ref),\n\t\t\t};\n\t\t}\n\t}\n\n\tconst httpsCandidates = [split.ref ? `https://${split.repo}#${split.ref}` : undefined, `https://${url}`].filter(\n\t\t(value): value is string => Boolean(value),\n\t);\n\tfor (const candidate of httpsCandidates) {\n\t\tconst info = hostedGitInfo.fromUrl(candidate);\n\t\tif (info) {\n\t\t\tif (split.ref && info.project?.includes(\"@\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\ttype: \"git\",\n\t\t\t\trepo: `https://${split.repo}`,\n\t\t\t\thost: info.domain || \"\",\n\t\t\t\tpath: `${info.user}/${info.project}`.replace(/\\.git$/, \"\"),\n\t\t\t\tref: info.committish || split.ref || undefined,\n\t\t\t\tpinned: Boolean(info.committish || split.ref),\n\t\t\t};\n\t\t}\n\t}\n\n\treturn parseGenericGitUrl(url);\n}\n"]}
@@ -524,6 +524,11 @@ pi.on("tool_call", (event) => {
524
524
 
525
525
  Fired after tool executes. **Can modify result.**
526
526
 
527
+ `tool_result` handlers chain like middleware:
528
+ - Handlers run in extension load order
529
+ - Each handler sees the latest result after previous handler changes
530
+ - Handlers can return partial patches (`content`, `details`, or `isError`); omitted fields keep their current values
531
+
527
532
  ```typescript
528
533
  import { isBashToolResult } from "@mariozechner/pi-coding-agent";
529
534
 
package/docs/models.md CHANGED
@@ -10,6 +10,7 @@ Add custom providers and models (Ollama, vLLM, LM Studio, proxies) via `~/.pi/ag
10
10
  - [Provider Configuration](#provider-configuration)
11
11
  - [Model Configuration](#model-configuration)
12
12
  - [Overriding Built-in Providers](#overriding-built-in-providers)
13
+ - [Per-model Overrides](#per-model-overrides)
13
14
  - [OpenAI Compatibility](#openai-compatibility)
14
15
 
15
16
  ## Minimal Example
@@ -84,6 +85,7 @@ Set `api` at provider level (default for all models) or model level (override pe
84
85
  | `headers` | Custom headers (see value resolution below) |
85
86
  | `authHeader` | Set `true` to add `Authorization: Bearer <apiKey>` automatically |
86
87
  | `models` | Array of model configurations |
88
+ | `modelOverrides` | Per-model overrides for built-in models on this provider |
87
89
 
88
90
  ### Value Resolution
89
91
 
@@ -151,7 +153,7 @@ Route a built-in provider through a proxy without redefining models:
151
153
 
152
154
  All built-in Anthropic models remain available. Existing OAuth or API key auth continues to work.
153
155
 
154
- To fully replace a built-in provider with custom models, include the `models` array:
156
+ To merge custom models into a built-in provider, include the `models` array:
155
157
 
156
158
  ```json
157
159
  {
@@ -166,6 +168,43 @@ To fully replace a built-in provider with custom models, include the `models` ar
166
168
  }
167
169
  ```
168
170
 
171
+ Merge semantics:
172
+ - Built-in models are kept.
173
+ - Custom models are upserted by `id` within the provider.
174
+ - If a custom model `id` matches a built-in model `id`, the custom model replaces that built-in model.
175
+ - If a custom model `id` is new, it is added alongside built-in models.
176
+
177
+ ## Per-model Overrides
178
+
179
+ Use `modelOverrides` to customize specific built-in models without replacing the provider's full model list.
180
+
181
+ ```json
182
+ {
183
+ "providers": {
184
+ "openrouter": {
185
+ "modelOverrides": {
186
+ "anthropic/claude-sonnet-4": {
187
+ "name": "Claude Sonnet 4 (Bedrock Route)",
188
+ "compat": {
189
+ "openRouterRouting": {
190
+ "only": ["amazon-bedrock"]
191
+ }
192
+ }
193
+ }
194
+ }
195
+ }
196
+ }
197
+ }
198
+ ```
199
+
200
+ `modelOverrides` supports these fields per model: `name`, `reasoning`, `input`, `cost` (partial), `contextWindow`, `maxTokens`, `headers`, `compat`.
201
+
202
+ Behavior notes:
203
+ - `modelOverrides` are applied to built-in provider models.
204
+ - Unknown model IDs are ignored.
205
+ - You can combine provider-level `baseUrl`/`headers` with `modelOverrides`.
206
+ - If `models` is also defined for a provider, custom models are merged after built-in overrides. A custom model with the same `id` replaces the overridden built-in model entry.
207
+
169
208
  ## OpenAI Compatibility
170
209
 
171
210
  For providers with partial OpenAI compatibility, use the `compat` field:
package/docs/packages.md CHANGED
@@ -60,13 +60,33 @@ npm:pkg
60
60
  ```
61
61
  git:github.com/user/repo@v1
62
62
  https://github.com/user/repo@v1
63
+ git@github.com:user/repo@v1
64
+ ssh://git@github.com/user/repo@v1
63
65
  ```
64
66
 
67
+ - HTTPS and SSH URLs are both supported.
68
+ - SSH URLs use your configured SSH keys automatically (respects `~/.ssh/config`).
69
+ - For non-interactive runs (for example CI), you can set `GIT_TERMINAL_PROMPT=0` to disable credential prompts and set `GIT_SSH_COMMAND` (for example `ssh -o BatchMode=yes -o ConnectTimeout=5`) to fail fast.
65
70
  - Raw `https://` URLs work without the `git:` prefix.
66
71
  - Refs pin the package and skip `pi update`.
67
72
  - Cloned to `~/.pi/agent/git/<host>/<path>` (global) or `.pi/git/<host>/<path>` (project).
68
73
  - Runs `npm install` after clone or pull if `package.json` exists.
69
74
 
75
+ **SSH examples:**
76
+ ```bash
77
+ # Standard git@host:path format
78
+ pi install git@github.com:user/repo
79
+
80
+ # With git: prefix
81
+ pi install git:git@github.com:user/repo
82
+
83
+ # ssh:// protocol format
84
+ pi install ssh://git@github.com/user/repo
85
+
86
+ # With version ref
87
+ pi install git@github.com:user/repo@v1.0.0
88
+ ```
89
+
70
90
  ### Local Paths
71
91
 
72
92
  ```
package/docs/providers.md CHANGED
@@ -140,6 +140,19 @@ Also supports ECS task roles (`AWS_CONTAINER_CREDENTIALS_*`) and IRSA (`AWS_WEB_
140
140
  pi --provider amazon-bedrock --model us.anthropic.claude-sonnet-4-20250514-v1:0
141
141
  ```
142
142
 
143
+ If you are connecting to a Bedrock API proxy, the following environment variables can be used:
144
+
145
+ ```bash
146
+ # Set the URL for the Bedrock proxy (standard AWS SDK env var)
147
+ export AWS_ENDPOINT_URL_BEDROCK_RUNTIME=https://my.corp.proxy/bedrock
148
+
149
+ # Set if your proxy does not require authentication
150
+ export AWS_BEDROCK_SKIP_AUTH=1
151
+
152
+ # Set if your proxy only supports HTTP/1.1
153
+ export AWS_BEDROCK_FORCE_HTTP1=1
154
+ ```
155
+
143
156
  ### Google Vertex AI
144
157
 
145
158
  Uses Application Default Credentials:
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider",
3
- "version": "1.2.6",
3
+ "version": "1.3.7",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-custom-provider",
9
- "version": "1.2.6",
9
+ "version": "1.3.7",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.52.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-anthropic",
3
3
  "private": true,
4
- "version": "1.2.6",
4
+ "version": "1.3.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-gitlab-duo",
3
3
  "private": true,
4
- "version": "1.2.6",
4
+ "version": "1.3.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-qwen-cli",
3
3
  "private": true,
4
- "version": "1.1.6",
4
+ "version": "1.2.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -2,8 +2,8 @@
2
2
  * Hello Tool - Minimal custom tool example
3
3
  */
4
4
 
5
+ import { Type } from "@mariozechner/pi-ai";
5
6
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
6
- import { Type } from "@sinclair/typebox";
7
7
 
8
8
  export default function (pi: ExtensionAPI) {
9
9
  pi.registerTool({
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
- "version": "1.15.6",
3
+ "version": "1.16.7",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-with-deps",
9
- "version": "1.15.6",
9
+ "version": "1.16.7",
10
10
  "dependencies": {
11
11
  "ms": "^2.1.3"
12
12
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
3
  "private": true,
4
- "version": "1.15.6",
4
+ "version": "1.16.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vandeepunk/pi-coding-agent",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management forked from @mariozechner/pi-coding-agent",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -40,15 +40,16 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@mariozechner/jiti": "^2.6.2",
43
- "@mariozechner/pi-agent-core": "^0.51.6",
44
- "@mariozechner/pi-ai": "^0.51.6",
45
- "@mariozechner/pi-tui": "^0.51.6",
43
+ "@mariozechner/pi-agent-core": "^0.52.7",
44
+ "@mariozechner/pi-ai": "^0.52.7",
45
+ "@mariozechner/pi-tui": "^0.52.7",
46
46
  "@silvia-odwyer/photon-node": "^0.3.4",
47
47
  "chalk": "^5.5.0",
48
48
  "cli-highlight": "^2.1.11",
49
49
  "diff": "^8.0.2",
50
50
  "file-type": "^21.1.1",
51
51
  "glob": "^13.0.1",
52
+ "hosted-git-info": "^9.0.2",
52
53
  "ignore": "^7.0.5",
53
54
  "marked": "^15.0.12",
54
55
  "minimatch": "^10.1.1",
@@ -65,14 +66,12 @@
65
66
  "@mariozechner/clipboard": "^0.3.2"
66
67
  },
67
68
  "devDependencies": {
68
- "@sinclair/typebox": "^0.34.48",
69
69
  "@types/diff": "^7.0.2",
70
+ "@types/hosted-git-info": "^3.0.5",
70
71
  "@types/ms": "^2.1.0",
71
72
  "@types/node": "^24.3.0",
72
73
  "@types/proper-lockfile": "^4.1.4",
73
- "ajv": "^8.17.1",
74
74
  "shx": "^0.4.0",
75
- "strip-ansi": "^7.1.2",
76
75
  "typescript": "^5.7.3",
77
76
  "vitest": "^3.2.4"
78
77
  },
@@ -88,7 +87,7 @@
88
87
  "license": "MIT",
89
88
  "repository": {
90
89
  "type": "git",
91
- "url": "git+https://github.com/vandeefeng/pi-mono.git",
90
+ "url": "git+https://github.com/VandeeFeng/pi-mono.git",
92
91
  "directory": "packages/coding-agent"
93
92
  },
94
93
  "engines": {