@fleetagent/pi-coding-agent 0.0.6 → 0.0.7

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 (153) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/cli/file-processor.d.ts.map +1 -1
  3. package/dist/cli/file-processor.js +2 -3
  4. package/dist/cli/file-processor.js.map +1 -1
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/config.js +15 -2
  7. package/dist/config.js.map +1 -1
  8. package/dist/core/agent-session.d.ts +9 -0
  9. package/dist/core/agent-session.d.ts.map +1 -1
  10. package/dist/core/agent-session.js +86 -15
  11. package/dist/core/agent-session.js.map +1 -1
  12. package/dist/core/export-html/template.js +6 -3
  13. package/dist/core/extensions/runner.d.ts +1 -1
  14. package/dist/core/extensions/runner.d.ts.map +1 -1
  15. package/dist/core/extensions/runner.js +8 -2
  16. package/dist/core/extensions/runner.js.map +1 -1
  17. package/dist/core/extensions/types.d.ts +4 -2
  18. package/dist/core/extensions/types.d.ts.map +1 -1
  19. package/dist/core/extensions/types.js.map +1 -1
  20. package/dist/core/model-registry.d.ts.map +1 -1
  21. package/dist/core/model-registry.js +65 -13
  22. package/dist/core/model-registry.js.map +1 -1
  23. package/dist/core/output-guard.d.ts +1 -0
  24. package/dist/core/output-guard.d.ts.map +1 -1
  25. package/dist/core/output-guard.js +52 -22
  26. package/dist/core/output-guard.js.map +1 -1
  27. package/dist/core/package-manager.d.ts.map +1 -1
  28. package/dist/core/package-manager.js +31 -12
  29. package/dist/core/package-manager.js.map +1 -1
  30. package/dist/core/pi-agent.d.ts.map +1 -1
  31. package/dist/core/pi-agent.js +12 -3
  32. package/dist/core/pi-agent.js.map +1 -1
  33. package/dist/core/resolve-config-value.d.ts +9 -1
  34. package/dist/core/resolve-config-value.d.ts.map +1 -1
  35. package/dist/core/resolve-config-value.js +134 -11
  36. package/dist/core/resolve-config-value.js.map +1 -1
  37. package/dist/core/session/jsonl-helpers.d.ts +2 -1
  38. package/dist/core/session/jsonl-helpers.d.ts.map +1 -1
  39. package/dist/core/session/jsonl-helpers.js +6 -3
  40. package/dist/core/session/jsonl-helpers.js.map +1 -1
  41. package/dist/core/session/local-session-manager.d.ts +1 -0
  42. package/dist/core/session/local-session-manager.d.ts.map +1 -1
  43. package/dist/core/session/local-session-manager.js +12 -4
  44. package/dist/core/session/local-session-manager.js.map +1 -1
  45. package/dist/core/session/session-manager.d.ts +1 -0
  46. package/dist/core/session/session-manager.d.ts.map +1 -1
  47. package/dist/core/session/session-manager.js.map +1 -1
  48. package/dist/core/session/stores/jsonl-session-store.d.ts +2 -1
  49. package/dist/core/session/stores/jsonl-session-store.d.ts.map +1 -1
  50. package/dist/core/session/stores/jsonl-session-store.js +105 -78
  51. package/dist/core/session/stores/jsonl-session-store.js.map +1 -1
  52. package/dist/core/settings-manager.d.ts +2 -0
  53. package/dist/core/settings-manager.d.ts.map +1 -1
  54. package/dist/core/settings-manager.js +14 -9
  55. package/dist/core/settings-manager.js.map +1 -1
  56. package/dist/core/tools/bash.d.ts.map +1 -1
  57. package/dist/core/tools/bash.js +73 -63
  58. package/dist/core/tools/bash.js.map +1 -1
  59. package/dist/core/tools/edit.d.ts.map +1 -1
  60. package/dist/core/tools/edit.js +45 -76
  61. package/dist/core/tools/edit.js.map +1 -1
  62. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  63. package/dist/core/tools/file-mutation-queue.js +27 -12
  64. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  65. package/dist/core/tools/find.d.ts.map +1 -1
  66. package/dist/core/tools/find.js +11 -2
  67. package/dist/core/tools/find.js.map +1 -1
  68. package/dist/core/tools/grep.d.ts.map +1 -1
  69. package/dist/core/tools/grep.js +3 -3
  70. package/dist/core/tools/grep.js.map +1 -1
  71. package/dist/core/tools/ls.d.ts.map +1 -1
  72. package/dist/core/tools/ls.js +13 -4
  73. package/dist/core/tools/ls.js.map +1 -1
  74. package/dist/core/tools/path-utils.d.ts +1 -0
  75. package/dist/core/tools/path-utils.d.ts.map +1 -1
  76. package/dist/core/tools/path-utils.js +37 -0
  77. package/dist/core/tools/path-utils.js.map +1 -1
  78. package/dist/core/tools/read.d.ts.map +1 -1
  79. package/dist/core/tools/read.js +7 -6
  80. package/dist/core/tools/read.js.map +1 -1
  81. package/dist/core/tools/write.d.ts.map +1 -1
  82. package/dist/core/tools/write.js +24 -32
  83. package/dist/core/tools/write.js.map +1 -1
  84. package/dist/main.d.ts.map +1 -1
  85. package/dist/main.js +3 -2
  86. package/dist/main.js.map +1 -1
  87. package/dist/migrations.d.ts.map +1 -1
  88. package/dist/migrations.js +118 -1
  89. package/dist/migrations.js.map +1 -1
  90. package/dist/modes/interactive/components/footer.d.ts +1 -0
  91. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  92. package/dist/modes/interactive/components/footer.js +14 -5
  93. package/dist/modes/interactive/components/footer.js.map +1 -1
  94. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  95. package/dist/modes/interactive/components/user-message.js +1 -1
  96. package/dist/modes/interactive/components/user-message.js.map +1 -1
  97. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  98. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  99. package/dist/modes/interactive/interactive-mode.js +30 -5
  100. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  101. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  102. package/dist/modes/interactive/theme/theme.js +10 -0
  103. package/dist/modes/interactive/theme/theme.js.map +1 -1
  104. package/dist/modes/rpc/rpc-client.d.ts +3 -0
  105. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  106. package/dist/modes/rpc/rpc-client.js +64 -7
  107. package/dist/modes/rpc/rpc-client.js.map +1 -1
  108. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  109. package/dist/modes/rpc/rpc-mode.js +15 -3
  110. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  111. package/dist/utils/clipboard-native.d.ts +3 -1
  112. package/dist/utils/clipboard-native.d.ts.map +1 -1
  113. package/dist/utils/clipboard-native.js +14 -8
  114. package/dist/utils/clipboard-native.js.map +1 -1
  115. package/dist/utils/deprecation.d.ts +4 -0
  116. package/dist/utils/deprecation.d.ts.map +1 -0
  117. package/dist/utils/deprecation.js +13 -0
  118. package/dist/utils/deprecation.js.map +1 -0
  119. package/dist/utils/image-resize-core.d.ts +30 -0
  120. package/dist/utils/image-resize-core.d.ts.map +1 -0
  121. package/dist/utils/image-resize-core.js +124 -0
  122. package/dist/utils/image-resize-core.js.map +1 -0
  123. package/dist/utils/image-resize-worker.d.ts +2 -0
  124. package/dist/utils/image-resize-worker.d.ts.map +1 -0
  125. package/dist/utils/image-resize-worker.js +31 -0
  126. package/dist/utils/image-resize-worker.js.map +1 -0
  127. package/dist/utils/image-resize.d.ts +6 -27
  128. package/dist/utils/image-resize.d.ts.map +1 -1
  129. package/dist/utils/image-resize.js +60 -116
  130. package/dist/utils/image-resize.js.map +1 -1
  131. package/dist/utils/json.d.ts +3 -0
  132. package/dist/utils/json.d.ts.map +1 -0
  133. package/dist/utils/json.js +7 -0
  134. package/dist/utils/json.js.map +1 -0
  135. package/docs/custom-provider.md +22 -9
  136. package/docs/extensions.md +4 -3
  137. package/docs/models.md +34 -12
  138. package/docs/packages.md +5 -4
  139. package/docs/providers.md +13 -5
  140. package/docs/sdk.md +56 -0
  141. package/docs/settings.md +3 -1
  142. package/docs/terminal-setup.md +6 -0
  143. package/docs/usage.md +2 -2
  144. package/examples/extensions/README.md +1 -0
  145. package/examples/extensions/custom-provider-anthropic/index.ts +1 -1
  146. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  147. package/examples/extensions/custom-provider-gitlab-duo/index.ts +54 -3
  148. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  149. package/examples/extensions/git-merge-and-resolve.ts +115 -0
  150. package/examples/extensions/sandbox/package.json +1 -1
  151. package/examples/extensions/with-deps/package.json +1 -1
  152. package/npm-shrinkwrap.json +13 -12
  153. package/package.json +5 -5
package/docs/sdk.md CHANGED
@@ -47,6 +47,52 @@ const pi = await PiAgent.create({
47
47
  const session = await pi.createAgentSession();
48
48
  ```
49
49
 
50
+ The session manages agent lifecycle, message history, model state, compaction, and event streaming.
51
+
52
+ ```typescript
53
+ interface AgentSession {
54
+ // Send a prompt and wait for completion
55
+ prompt(text: string, options?: PromptOptions): Promise<void>;
56
+
57
+ // Queue messages during streaming
58
+ steer(text: string): Promise<void>;
59
+ followUp(text: string): Promise<void>;
60
+
61
+ // Subscribe to events (returns unsubscribe function)
62
+ subscribe(listener: (event: AgentSessionEvent) => void): () => void;
63
+
64
+ // Session info
65
+ sessionFile: string | undefined;
66
+ sessionId: string;
67
+
68
+ // Model control
69
+ setModel(model: Model): Promise<void>;
70
+ setThinkingLevel(level: ThinkingLevel): void;
71
+ cycleModel(): Promise<ModelCycleResult | undefined>;
72
+ cycleThinkingLevel(): ThinkingLevel | undefined;
73
+
74
+ // State access
75
+ agent: Agent;
76
+ model: Model | undefined;
77
+ thinkingLevel: ThinkingLevel;
78
+ messages: AgentMessage[];
79
+ isStreaming: boolean;
80
+
81
+ // In-place tree navigation within the current session file
82
+ navigateTree(targetId: string, options?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string }): Promise<{ editorText?: string; cancelled: boolean }>;
83
+
84
+ // Compaction
85
+ compact(customInstructions?: string): Promise<CompactionResult>;
86
+ abortCompaction(): void;
87
+
88
+ // Abort current operation
89
+ abort(): Promise<void>;
90
+
91
+ // Cleanup
92
+ dispose(): void;
93
+ }
94
+ ```
95
+
50
96
  Most one-off session options can be passed directly to `PiAgent.create()`:
51
97
 
52
98
  ```typescript
@@ -73,6 +119,16 @@ Use `session.sessionReference` when you need the backend-neutral active session
73
119
 
74
120
  Session replacement APIs such as new-session, resume, fork, clone, and import live on `PiAgent`, not on `AgentSession`.
75
121
 
122
+ ```typescript
123
+ interface PromptOptions {
124
+ expandPromptTemplates?: boolean;
125
+ images?: ImageContent[];
126
+ streamingBehavior?: "steer" | "followUp";
127
+ source?: InputSource;
128
+ preflightResult?: (success: boolean) => void;
129
+ }
130
+ ```
131
+
76
132
  ### SessionManager and Session
77
133
 
78
134
  `SessionManager` handles lifecycle and discovery: create, open, continue, list, fork, and import. It returns a `Session`.
package/docs/settings.md CHANGED
@@ -101,11 +101,13 @@ Set `PI_SKIP_VERSION_CHECK=1` to disable the Pi version update check. Use `--off
101
101
  | `retry.maxRetries` | number | `3` | Maximum agent-level retry attempts |
102
102
  | `retry.baseDelayMs` | number | `2000` | Base delay for agent-level exponential backoff (2s, 4s, 8s) |
103
103
  | `retry.provider.timeoutMs` | number | SDK default | Provider/SDK request timeout in milliseconds |
104
- | `retry.provider.maxRetries` | number | SDK default | Provider/SDK retry attempts |
104
+ | `retry.provider.maxRetries` | number | `0` | Provider/SDK retry attempts |
105
105
  | `retry.provider.maxRetryDelayMs` | number | `60000` | Max server-requested delay before failing (60s) |
106
106
 
107
107
  When a provider requests a retry delay longer than `retry.provider.maxRetryDelayMs` (e.g., Google's "quota will reset after 5h"), the request fails immediately with an informative error instead of waiting silently. Set to `0` to disable the cap.
108
108
 
109
+ Keep `retry.provider.maxRetries` at `0` unless provider-level retries are explicitly needed. Setting it above `0` can make SDK/provider retries handle out-of-usage-limit errors before Pi sees them, which may block the agent until the provider quota resets in some circumstances.
110
+
109
111
  ```json
110
112
  {
111
113
  "retry": {
@@ -6,6 +6,12 @@ Pi uses the [Kitty keyboard protocol](https://sw.kovidgoyal.net/kitty/keyboard-p
6
6
 
7
7
  Work out of the box.
8
8
 
9
+ ## Apple Terminal
10
+
11
+ Pi enables enhanced key reporting when available. If Terminal.app still sends plain Return for `Shift+Enter`, pi uses a local macOS modifier fallback to treat that Return as `Shift+Enter`.
12
+
13
+ This fallback only works when pi runs on the same Mac as Terminal.app. It cannot detect the local keyboard over remote SSH.
14
+
9
15
  ## Ghostty
10
16
 
11
17
  Add to your Ghostty config (`~/Library/Application Support/com.mitchellh.ghostty/config` on macOS, `~/.config/ghostty/config` on Linux):
package/docs/usage.md CHANGED
@@ -129,8 +129,8 @@ pi [options] [@files...] [messages...]
129
129
  pi install <source> [-l] # Install package, -l for project-local
130
130
  pi remove <source> [-l] # Remove package
131
131
  pi uninstall <source> [-l] # Alias for remove
132
- pi update [source|self|pi] # Update pi and packages; skips pinned packages
133
- pi update --extensions # Update packages only
132
+ pi update [source|self|pi] # Update pi and packages; reconcile pinned git refs
133
+ pi update --extensions # Update packages only; reconcile pinned git refs
134
134
  pi update --self # Update pi only
135
135
  pi update --extension <src> # Update one package
136
136
  pi list # List installed packages
@@ -75,6 +75,7 @@ cp permission-gate.ts ~/.pi/agent/extensions/
75
75
  | `reload-runtime.ts` | Adds `/reload-runtime` and `reload_runtime` tool showing safe reload flow |
76
76
  | `interactive-shell.ts` | Run interactive commands (vim, htop) with full terminal via `user_bash` hook |
77
77
  | `inline-bash.ts` | Expands `!{command}` patterns in prompts via `input` event transformation |
78
+ | `input-transform-streaming.ts` | Skips expensive input preprocessing for mid-stream steering via `streamingBehavior` |
78
79
 
79
80
  ### Git Integration
80
81
 
@@ -568,7 +568,7 @@ function streamCustomAnthropic(
568
568
  export default function (pi: ExtensionAPI) {
569
569
  pi.registerProvider("custom-anthropic", {
570
570
  baseUrl: "https://api.anthropic.com",
571
- apiKey: "CUSTOM_ANTHROPIC_API_KEY",
571
+ apiKey: "$CUSTOM_ANTHROPIC_API_KEY",
572
572
  api: "custom-anthropic-api",
573
573
 
574
574
  models: [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-anthropic",
3
3
  "private": true,
4
- "version": "0.0.6",
4
+ "version": "0.0.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -20,6 +20,7 @@ import {
20
20
  type SimpleStreamOptions,
21
21
  streamSimpleAnthropic,
22
22
  streamSimpleOpenAIResponses,
23
+ type ThinkingLevelMap,
23
24
  } from "@fleetagent/pi-ai";
24
25
  import type { ExtensionAPI } from "@fleetagent/pi-coding-agent";
25
26
 
@@ -49,6 +50,7 @@ interface GitLabModel {
49
50
  backend: Backend;
50
51
  baseUrl: string;
51
52
  reasoning: boolean;
53
+ thinkingLevelMap?: ThinkingLevelMap;
52
54
  input: ("text" | "image")[];
53
55
  cost: { input: number; output: number; cacheRead: number; cacheWrite: number };
54
56
  contextWindow: number;
@@ -57,12 +59,37 @@ interface GitLabModel {
57
59
 
58
60
  export const MODELS: GitLabModel[] = [
59
61
  // Anthropic
62
+ {
63
+ id: "claude-opus-4-8",
64
+ name: "Claude Opus 4.8",
65
+ backend: "anthropic",
66
+ baseUrl: ANTHROPIC_PROXY_URL,
67
+ reasoning: true,
68
+ thinkingLevelMap: { xhigh: "max" },
69
+ input: ["text", "image"],
70
+ cost: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
71
+ contextWindow: 1000000,
72
+ maxTokens: 128000,
73
+ },
74
+ {
75
+ id: "claude-sonnet-4-6",
76
+ name: "Claude Sonnet 4.6",
77
+ backend: "anthropic",
78
+ baseUrl: ANTHROPIC_PROXY_URL,
79
+ reasoning: true,
80
+ thinkingLevelMap: { xhigh: "max" },
81
+ input: ["text", "image"],
82
+ cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
83
+ contextWindow: 1000000,
84
+ maxTokens: 64000,
85
+ },
60
86
  {
61
87
  id: "claude-opus-4-5-20251101",
62
88
  name: "Claude Opus 4.5",
63
89
  backend: "anthropic",
64
90
  baseUrl: ANTHROPIC_PROXY_URL,
65
91
  reasoning: true,
92
+ thinkingLevelMap: { xhigh: "max" },
66
93
  input: ["text", "image"],
67
94
  cost: { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
68
95
  contextWindow: 200000,
@@ -74,6 +101,7 @@ export const MODELS: GitLabModel[] = [
74
101
  backend: "anthropic",
75
102
  baseUrl: ANTHROPIC_PROXY_URL,
76
103
  reasoning: true,
104
+ thinkingLevelMap: { xhigh: "max" },
77
105
  input: ["text", "image"],
78
106
  cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
79
107
  contextWindow: 200000,
@@ -85,12 +113,24 @@ export const MODELS: GitLabModel[] = [
85
113
  backend: "anthropic",
86
114
  baseUrl: ANTHROPIC_PROXY_URL,
87
115
  reasoning: true,
116
+ thinkingLevelMap: { xhigh: "max" },
88
117
  input: ["text", "image"],
89
118
  cost: { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },
90
119
  contextWindow: 200000,
91
120
  maxTokens: 8192,
92
121
  },
93
122
  // OpenAI (all use Responses API)
123
+ {
124
+ id: "gpt-5.5-2026-04-23",
125
+ name: "GPT-5.5",
126
+ backend: "openai",
127
+ baseUrl: OPENAI_PROXY_URL,
128
+ reasoning: true,
129
+ input: ["text", "image"],
130
+ cost: { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 0 },
131
+ contextWindow: 272000,
132
+ maxTokens: 128000,
133
+ },
94
134
  {
95
135
  id: "gpt-5.1-2025-11-13",
96
136
  name: "GPT-5.1",
@@ -285,7 +325,17 @@ export function streamGitLabDuo(
285
325
 
286
326
  const innerStream =
287
327
  cfg.backend === "anthropic"
288
- ? streamSimpleAnthropic(modelWithBaseUrl as Model<"anthropic-messages">, context, streamOptions)
328
+ ? streamSimpleAnthropic(
329
+ {
330
+ ...(modelWithBaseUrl as Model<"anthropic-messages">),
331
+ compat: {
332
+ ...(modelWithBaseUrl as Model<"anthropic-messages">).compat,
333
+ forceAdaptiveThinking: true,
334
+ },
335
+ },
336
+ context,
337
+ streamOptions,
338
+ )
289
339
  : streamSimpleOpenAIResponses(modelWithBaseUrl as Model<"openai-responses">, context, streamOptions);
290
340
 
291
341
  for await (const event of innerStream) stream.push(event);
@@ -327,12 +377,13 @@ export function streamGitLabDuo(
327
377
  export default function (pi: ExtensionAPI) {
328
378
  pi.registerProvider("gitlab-duo", {
329
379
  baseUrl: AI_GATEWAY_URL,
330
- apiKey: "GITLAB_TOKEN",
380
+ apiKey: "$GITLAB_TOKEN",
331
381
  api: "gitlab-duo-api",
332
- models: MODELS.map(({ id, name, reasoning, input, cost, contextWindow, maxTokens }) => ({
382
+ models: MODELS.map(({ id, name, reasoning, thinkingLevelMap, input, cost, contextWindow, maxTokens }) => ({
333
383
  id,
334
384
  name,
335
385
  reasoning,
386
+ thinkingLevelMap,
336
387
  input,
337
388
  cost,
338
389
  contextWindow,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-gitlab-duo",
3
3
  "private": true,
4
- "version": "0.0.6",
4
+ "version": "0.0.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Merge and Resolve
3
+ *
4
+ * Keeps the working branch up to date with its upstream tracking ref.
5
+ * After each agent turn, fetches and merges. Clean merges complete
6
+ * silently. When conflicts arise, the working tree is left dirty and
7
+ * the agent receives a follow-up message listing each conflict block
8
+ * with file, line range, and ours/theirs sections so it can resolve them.
9
+ * Also re-sends unresolved conflicts from a previous incomplete merge.
10
+ *
11
+ * Start pi with this extension:
12
+ * pi -e ./examples/extensions/git-merge-and-resolve.ts
13
+ */
14
+ import { createReadStream } from "node:fs";
15
+ import { join } from "node:path";
16
+ import { createInterface } from "node:readline";
17
+ import type { ExtensionAPI } from "@fleetagent/pi-coding-agent";
18
+
19
+ interface ConflictBlock {
20
+ file: string;
21
+ startLine: number;
22
+ separatorLine: number;
23
+ endLine: number;
24
+ }
25
+
26
+ /** Parse conflict markers from working tree files with unmerged paths. */
27
+ async function findConflicts(pi: ExtensionAPI, cwd: string): Promise<ConflictBlock[]> {
28
+ const { stdout, code } = await pi.exec("git", ["diff", "--name-only", "--diff-filter=U"]);
29
+ if (code !== 0 || !stdout.trim()) return [];
30
+
31
+ const blocks: ConflictBlock[] = [];
32
+ for (const file of stdout.trim().split("\n")) {
33
+ try {
34
+ const rl = createInterface({ input: createReadStream(join(cwd, file), "utf-8") });
35
+ let lineNo = 0;
36
+ let blockStart: number | undefined;
37
+ let separatorLine: number | undefined;
38
+ for await (const line of rl) {
39
+ lineNo++;
40
+ if (line.startsWith("<<<<<<<")) {
41
+ blockStart = lineNo;
42
+ separatorLine = undefined;
43
+ } else if (line.startsWith("=======") && blockStart !== undefined) {
44
+ separatorLine = lineNo;
45
+ } else if (line.startsWith(">>>>>>>") && blockStart !== undefined && separatorLine !== undefined) {
46
+ blocks.push({ file, startLine: blockStart, separatorLine, endLine: lineNo });
47
+ blockStart = undefined;
48
+ separatorLine = undefined;
49
+ }
50
+ }
51
+ } catch {}
52
+ }
53
+ return blocks;
54
+ }
55
+
56
+ function formatRange(start: number, end: number): string {
57
+ if (start > end) return "empty";
58
+ if (start === end) return `${start}`;
59
+ return `${start}-${end}`;
60
+ }
61
+
62
+ function formatConflicts(ref: string, blocks: ConflictBlock[]): string {
63
+ const lines = [`Merged ${ref} with conflicts:`, ""];
64
+ for (const b of blocks) {
65
+ const ours = formatRange(b.startLine + 1, b.separatorLine - 1);
66
+ const theirs = formatRange(b.separatorLine + 1, b.endLine - 1);
67
+ lines.push(` ${b.file}:${b.startLine}-${b.endLine} (ours ${ours}, theirs ${theirs})`);
68
+ }
69
+ lines.push("", "Resolve these conflicts.");
70
+ return lines.join("\n");
71
+ }
72
+
73
+ export default function (pi: ExtensionAPI) {
74
+ pi.on("agent_end", async (_event, ctx) => {
75
+ const { code: revParseCode } = await pi.exec("git", ["rev-parse", "--git-dir"]);
76
+ if (revParseCode !== 0) return;
77
+
78
+ let ref = "MERGE_HEAD";
79
+
80
+ // If not already in a merge, attempt one
81
+ const { code: mergeHeadCode } = await pi.exec("git", ["rev-parse", "MERGE_HEAD"]);
82
+ if (mergeHeadCode !== 0) {
83
+ // Only attempt a new merge if the working tree is clean
84
+ const { stdout: status } = await pi.exec("git", ["status", "--porcelain"]);
85
+ if (status.trim()) return;
86
+
87
+ const { stdout: upstream, code: upstreamCode } = await pi.exec("git", [
88
+ "rev-parse",
89
+ "--abbrev-ref",
90
+ "--symbolic-full-name",
91
+ "@{u}",
92
+ ]);
93
+ if (upstreamCode !== 0) return;
94
+
95
+ ref = upstream.trim();
96
+ const remote = ref.split("/")[0];
97
+ ctx.ui.notify(`git-merge-and-resolve: fetching ${remote}, merging ${ref}`, "info");
98
+
99
+ const { code: fetchCode, stderr: fetchErr } = await pi.exec("git", ["fetch", remote]);
100
+ if (fetchCode !== 0) {
101
+ ctx.ui.notify(`git-merge-and-resolve: fetch failed: ${fetchErr.trim()}`, "warning");
102
+ return;
103
+ }
104
+
105
+ const { code: mergeCode } = await pi.exec("git", ["merge", "--no-ff", ref]);
106
+ if (mergeCode === 0) return;
107
+ }
108
+
109
+ // Either we just merged with conflicts, or we were already in an unfinished merge
110
+ const conflicts = await findConflicts(pi, ctx.cwd);
111
+ if (conflicts.length === 0) return;
112
+
113
+ pi.sendUserMessage(formatConflicts(ref, conflicts), { deliverAs: "followUp" });
114
+ });
115
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
3
  "private": true,
4
- "version": "0.0.6",
4
+ "version": "0.0.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-with-deps",
3
3
  "private": true,
4
- "version": "0.0.6",
4
+ "version": "0.0.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@fleetagent/pi-coding-agent",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@fleetagent/pi-coding-agent",
9
- "version": "0.0.6",
9
+ "version": "0.0.7",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
- "@fleetagent/pi-agent-core": "^0.0.6",
13
- "@fleetagent/pi-ai": "^0.0.6",
14
- "@fleetagent/pi-tui": "^0.0.6",
12
+ "@fleetagent/pi-agent-core": "^0.0.7",
13
+ "@fleetagent/pi-ai": "^0.0.7",
14
+ "@fleetagent/pi-tui": "^0.0.7",
15
15
  "@silvia-odwyer/photon-node": "0.3.4",
16
16
  "chalk": "5.6.2",
17
17
  "cross-spawn": "7.0.6",
@@ -473,11 +473,11 @@
473
473
  }
474
474
  },
475
475
  "node_modules/@fleetagent/pi-agent-core": {
476
- "version": "0.0.6",
477
- "resolved": "https://registry.npmjs.org/@fleetagent/pi-agent-core/-/pi-agent-core-0.0.6.tgz",
476
+ "version": "0.0.7",
477
+ "resolved": "https://registry.npmjs.org/@fleetagent/pi-agent-core/-/pi-agent-core-0.0.7.tgz",
478
478
  "license": "MIT",
479
479
  "dependencies": {
480
- "@fleetagent/pi-ai": "^0.0.6",
480
+ "@fleetagent/pi-ai": "^0.0.7",
481
481
  "ignore": "7.0.5",
482
482
  "typebox": "1.1.38",
483
483
  "yaml": "2.9.0"
@@ -487,12 +487,13 @@
487
487
  }
488
488
  },
489
489
  "node_modules/@fleetagent/pi-ai": {
490
- "version": "0.0.6",
491
- "resolved": "https://registry.npmjs.org/@fleetagent/pi-ai/-/pi-ai-0.0.6.tgz",
490
+ "version": "0.0.7",
491
+ "resolved": "https://registry.npmjs.org/@fleetagent/pi-ai/-/pi-ai-0.0.7.tgz",
492
492
  "license": "MIT",
493
493
  "dependencies": {
494
494
  "@anthropic-ai/sdk": "0.91.1",
495
495
  "@aws-sdk/client-bedrock-runtime": "3.1048.0",
496
+ "@smithy/node-http-handler": "4.7.3",
496
497
  "@google/genai": "1.52.0",
497
498
  "@mistralai/mistralai": "2.2.1",
498
499
  "http-proxy-agent": "7.0.2",
@@ -509,8 +510,8 @@
509
510
  }
510
511
  },
511
512
  "node_modules/@fleetagent/pi-tui": {
512
- "version": "0.0.6",
513
- "resolved": "https://registry.npmjs.org/@fleetagent/pi-tui/-/pi-tui-0.0.6.tgz",
513
+ "version": "0.0.7",
514
+ "resolved": "https://registry.npmjs.org/@fleetagent/pi-tui/-/pi-tui-0.0.7.tgz",
514
515
  "license": "MIT",
515
516
  "dependencies": {
516
517
  "get-east-asian-width": "1.6.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fleetagent/pi-coding-agent",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -31,7 +31,7 @@
31
31
  "scripts": {
32
32
  "clean": "shx rm -rf dist",
33
33
  "build": "tsgo -p tsconfig.build.json && shx chmod +x dist/cli.js && npm run copy-assets",
34
- "build:binary": "npm --prefix ../tui run build && npm --prefix ../ai run build && npm --prefix ../agent run build && npm run build && bun build --compile ./dist/bun/cli.js --outfile dist/pi && npm run copy-binary-assets",
34
+ "build:binary": "npm --prefix ../tui run build && npm --prefix ../ai run build && npm --prefix ../agent run build && npm run build && bun build --compile ./dist/bun/cli.js ./dist/utils/image-resize-worker.js --outfile dist/pi && npm run copy-binary-assets",
35
35
  "copy-assets": "shx mkdir -p dist/modes/interactive/theme && shx cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/ && shx mkdir -p dist/modes/interactive/assets && shx cp src/modes/interactive/assets/*.png dist/modes/interactive/assets/ && shx mkdir -p dist/core/export-html/vendor && shx cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/core/export-html/ && shx cp src/core/export-html/vendor/*.js dist/core/export-html/vendor/",
36
36
  "copy-binary-assets": "shx cp package.json dist/ && shx cp README.md dist/ && shx cp CHANGELOG.md dist/ && shx mkdir -p dist/theme && shx cp src/modes/interactive/theme/*.json dist/theme/ && shx mkdir -p dist/assets && shx cp src/modes/interactive/assets/*.png dist/assets/ && shx mkdir -p dist/export-html/vendor && shx cp src/core/export-html/template.html dist/export-html/ && shx cp src/core/export-html/vendor/*.js dist/export-html/vendor/ && shx cp -r docs dist/ && shx cp -r examples dist/ && shx cp ../../node_modules/@silvia-odwyer/photon-node/photon_rs_bg.wasm dist/",
37
37
  "test": "vitest --run",
@@ -39,9 +39,9 @@
39
39
  "prepublishOnly": "npm run clean && npm run build && npm run shrinkwrap"
40
40
  },
41
41
  "dependencies": {
42
- "@fleetagent/pi-agent-core": "^0.0.6",
43
- "@fleetagent/pi-ai": "^0.0.6",
44
- "@fleetagent/pi-tui": "^0.0.6",
42
+ "@fleetagent/pi-agent-core": "^0.0.7",
43
+ "@fleetagent/pi-ai": "^0.0.7",
44
+ "@fleetagent/pi-tui": "^0.0.7",
45
45
  "@silvia-odwyer/photon-node": "0.3.4",
46
46
  "chalk": "5.6.2",
47
47
  "cross-spawn": "7.0.6",