@fleetagent/pi-coding-agent 0.0.5 → 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 (201) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +28 -5
  3. package/dist/cli/args.d.ts +2 -0
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +9 -0
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/file-processor.d.ts.map +1 -1
  8. package/dist/cli/file-processor.js +2 -3
  9. package/dist/cli/file-processor.js.map +1 -1
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +15 -2
  12. package/dist/config.js.map +1 -1
  13. package/dist/core/agent-session.d.ts +13 -3
  14. package/dist/core/agent-session.d.ts.map +1 -1
  15. package/dist/core/agent-session.js +130 -23
  16. package/dist/core/agent-session.js.map +1 -1
  17. package/dist/core/diagnostics.d.ts +1 -1
  18. package/dist/core/diagnostics.d.ts.map +1 -1
  19. package/dist/core/diagnostics.js.map +1 -1
  20. package/dist/core/export-html/template.js +6 -3
  21. package/dist/core/extensions/runner.d.ts +5 -1
  22. package/dist/core/extensions/runner.d.ts.map +1 -1
  23. package/dist/core/extensions/runner.js +13 -3
  24. package/dist/core/extensions/runner.js.map +1 -1
  25. package/dist/core/extensions/types.d.ts +6 -3
  26. package/dist/core/extensions/types.d.ts.map +1 -1
  27. package/dist/core/extensions/types.js.map +1 -1
  28. package/dist/core/model-registry.d.ts.map +1 -1
  29. package/dist/core/model-registry.js +65 -13
  30. package/dist/core/model-registry.js.map +1 -1
  31. package/dist/core/output-guard.d.ts +1 -0
  32. package/dist/core/output-guard.d.ts.map +1 -1
  33. package/dist/core/output-guard.js +52 -22
  34. package/dist/core/output-guard.js.map +1 -1
  35. package/dist/core/package-manager.d.ts +1 -0
  36. package/dist/core/package-manager.d.ts.map +1 -1
  37. package/dist/core/package-manager.js +161 -24
  38. package/dist/core/package-manager.js.map +1 -1
  39. package/dist/core/pi-agent.d.ts.map +1 -1
  40. package/dist/core/pi-agent.js +12 -3
  41. package/dist/core/pi-agent.js.map +1 -1
  42. package/dist/core/resolve-config-value.d.ts +9 -1
  43. package/dist/core/resolve-config-value.d.ts.map +1 -1
  44. package/dist/core/resolve-config-value.js +134 -11
  45. package/dist/core/resolve-config-value.js.map +1 -1
  46. package/dist/core/resource-loader.d.ts +30 -0
  47. package/dist/core/resource-loader.d.ts.map +1 -1
  48. package/dist/core/resource-loader.js +94 -0
  49. package/dist/core/resource-loader.js.map +1 -1
  50. package/dist/core/rules.d.ts +57 -0
  51. package/dist/core/rules.d.ts.map +1 -0
  52. package/dist/core/rules.js +384 -0
  53. package/dist/core/rules.js.map +1 -0
  54. package/dist/core/session/jsonl-helpers.d.ts +2 -1
  55. package/dist/core/session/jsonl-helpers.d.ts.map +1 -1
  56. package/dist/core/session/jsonl-helpers.js +6 -3
  57. package/dist/core/session/jsonl-helpers.js.map +1 -1
  58. package/dist/core/session/local-session-manager.d.ts +1 -0
  59. package/dist/core/session/local-session-manager.d.ts.map +1 -1
  60. package/dist/core/session/local-session-manager.js +12 -4
  61. package/dist/core/session/local-session-manager.js.map +1 -1
  62. package/dist/core/session/session-manager.d.ts +1 -0
  63. package/dist/core/session/session-manager.d.ts.map +1 -1
  64. package/dist/core/session/session-manager.js.map +1 -1
  65. package/dist/core/session/stores/jsonl-session-store.d.ts +2 -1
  66. package/dist/core/session/stores/jsonl-session-store.d.ts.map +1 -1
  67. package/dist/core/session/stores/jsonl-session-store.js +105 -78
  68. package/dist/core/session/stores/jsonl-session-store.js.map +1 -1
  69. package/dist/core/settings-manager.d.ts +7 -0
  70. package/dist/core/settings-manager.d.ts.map +1 -1
  71. package/dist/core/settings-manager.js +28 -9
  72. package/dist/core/settings-manager.js.map +1 -1
  73. package/dist/core/slash-commands.d.ts +1 -1
  74. package/dist/core/slash-commands.d.ts.map +1 -1
  75. package/dist/core/slash-commands.js +1 -1
  76. package/dist/core/slash-commands.js.map +1 -1
  77. package/dist/core/system-prompt.d.ts +3 -0
  78. package/dist/core/system-prompt.d.ts.map +1 -1
  79. package/dist/core/system-prompt.js +11 -3
  80. package/dist/core/system-prompt.js.map +1 -1
  81. package/dist/core/tools/bash.d.ts.map +1 -1
  82. package/dist/core/tools/bash.js +73 -63
  83. package/dist/core/tools/bash.js.map +1 -1
  84. package/dist/core/tools/edit.d.ts.map +1 -1
  85. package/dist/core/tools/edit.js +45 -76
  86. package/dist/core/tools/edit.js.map +1 -1
  87. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  88. package/dist/core/tools/file-mutation-queue.js +27 -12
  89. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  90. package/dist/core/tools/find.d.ts.map +1 -1
  91. package/dist/core/tools/find.js +11 -2
  92. package/dist/core/tools/find.js.map +1 -1
  93. package/dist/core/tools/grep.d.ts.map +1 -1
  94. package/dist/core/tools/grep.js +3 -3
  95. package/dist/core/tools/grep.js.map +1 -1
  96. package/dist/core/tools/ls.d.ts.map +1 -1
  97. package/dist/core/tools/ls.js +13 -4
  98. package/dist/core/tools/ls.js.map +1 -1
  99. package/dist/core/tools/path-utils.d.ts +1 -0
  100. package/dist/core/tools/path-utils.d.ts.map +1 -1
  101. package/dist/core/tools/path-utils.js +37 -0
  102. package/dist/core/tools/path-utils.js.map +1 -1
  103. package/dist/core/tools/read.d.ts.map +1 -1
  104. package/dist/core/tools/read.js +13 -8
  105. package/dist/core/tools/read.js.map +1 -1
  106. package/dist/core/tools/write.d.ts.map +1 -1
  107. package/dist/core/tools/write.js +24 -32
  108. package/dist/core/tools/write.js.map +1 -1
  109. package/dist/index.d.ts +1 -0
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/index.js +2 -0
  112. package/dist/index.js.map +1 -1
  113. package/dist/main.d.ts.map +1 -1
  114. package/dist/main.js +6 -2
  115. package/dist/main.js.map +1 -1
  116. package/dist/migrations.d.ts.map +1 -1
  117. package/dist/migrations.js +118 -1
  118. package/dist/migrations.js.map +1 -1
  119. package/dist/modes/interactive/components/config-selector.d.ts +1 -1
  120. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  121. package/dist/modes/interactive/components/config-selector.js +12 -3
  122. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  123. package/dist/modes/interactive/components/footer.d.ts +1 -0
  124. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  125. package/dist/modes/interactive/components/footer.js +14 -5
  126. package/dist/modes/interactive/components/footer.js.map +1 -1
  127. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  128. package/dist/modes/interactive/components/settings-selector.js +1 -1
  129. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  130. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  131. package/dist/modes/interactive/components/user-message.js +1 -1
  132. package/dist/modes/interactive/components/user-message.js.map +1 -1
  133. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  134. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  135. package/dist/modes/interactive/interactive-mode.js +64 -9
  136. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  137. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  138. package/dist/modes/interactive/theme/theme.js +10 -0
  139. package/dist/modes/interactive/theme/theme.js.map +1 -1
  140. package/dist/modes/rpc/rpc-client.d.ts +5 -0
  141. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  142. package/dist/modes/rpc/rpc-client.js +91 -18
  143. package/dist/modes/rpc/rpc-client.js.map +1 -1
  144. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  145. package/dist/modes/rpc/rpc-mode.js +23 -3
  146. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  147. package/dist/modes/rpc/rpc-types.d.ts +1 -1
  148. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  149. package/dist/modes/rpc/rpc-types.js.map +1 -1
  150. package/dist/utils/clipboard-native.d.ts +3 -1
  151. package/dist/utils/clipboard-native.d.ts.map +1 -1
  152. package/dist/utils/clipboard-native.js +14 -8
  153. package/dist/utils/clipboard-native.js.map +1 -1
  154. package/dist/utils/deprecation.d.ts +4 -0
  155. package/dist/utils/deprecation.d.ts.map +1 -0
  156. package/dist/utils/deprecation.js +13 -0
  157. package/dist/utils/deprecation.js.map +1 -0
  158. package/dist/utils/image-resize-core.d.ts +30 -0
  159. package/dist/utils/image-resize-core.d.ts.map +1 -0
  160. package/dist/utils/image-resize-core.js +124 -0
  161. package/dist/utils/image-resize-core.js.map +1 -0
  162. package/dist/utils/image-resize-worker.d.ts +2 -0
  163. package/dist/utils/image-resize-worker.d.ts.map +1 -0
  164. package/dist/utils/image-resize-worker.js +31 -0
  165. package/dist/utils/image-resize-worker.js.map +1 -0
  166. package/dist/utils/image-resize.d.ts +6 -27
  167. package/dist/utils/image-resize.d.ts.map +1 -1
  168. package/dist/utils/image-resize.js +60 -116
  169. package/dist/utils/image-resize.js.map +1 -1
  170. package/dist/utils/json.d.ts +3 -0
  171. package/dist/utils/json.d.ts.map +1 -0
  172. package/dist/utils/json.js +7 -0
  173. package/dist/utils/json.js.map +1 -0
  174. package/docs/custom-provider.md +22 -9
  175. package/docs/extensions.md +13 -11
  176. package/docs/index.md +3 -2
  177. package/docs/models.md +34 -12
  178. package/docs/packages.md +11 -8
  179. package/docs/providers.md +13 -5
  180. package/docs/quickstart.md +1 -1
  181. package/docs/rpc.md +4 -2
  182. package/docs/rules.md +102 -0
  183. package/docs/sdk.md +57 -1
  184. package/docs/settings.md +6 -3
  185. package/docs/terminal-setup.md +6 -0
  186. package/docs/usage.md +6 -4
  187. package/examples/extensions/README.md +2 -1
  188. package/examples/extensions/custom-provider-anthropic/index.ts +1 -1
  189. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  190. package/examples/extensions/custom-provider-gitlab-duo/index.ts +54 -3
  191. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  192. package/examples/extensions/dynamic-resources/RULES.md +8 -0
  193. package/examples/extensions/dynamic-resources/index.ts +1 -0
  194. package/examples/extensions/git-merge-and-resolve.ts +115 -0
  195. package/examples/extensions/reload-runtime.ts +2 -2
  196. package/examples/extensions/sandbox/package.json +1 -1
  197. package/examples/extensions/with-deps/package.json +1 -1
  198. package/examples/sdk/12-full-control.ts +1 -0
  199. package/examples/sdk/README.md +1 -1
  200. package/npm-shrinkwrap.json +13 -12
  201. 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`.
@@ -164,7 +220,7 @@ await pi.createAgentSession();
164
220
 
165
221
  ### Resource loading
166
222
 
167
- Use `DefaultResourceLoader` to override or discover extensions, skills, prompt templates, themes, and context files.
223
+ Use `DefaultResourceLoader` to override or discover extensions, skills, rules, prompt templates, themes, and context files.
168
224
 
169
225
  ```typescript
170
226
  const loader = new DefaultResourceLoader({
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": {
@@ -187,7 +189,7 @@ When multiple sources specify a session directory, precedence is `--session-dir`
187
189
 
188
190
  ### Resources
189
191
 
190
- These settings define where to load extensions, skills, prompts, and themes from.
192
+ These settings define where to load extensions, skills, rules, prompts, and themes from.
191
193
 
192
194
  Paths in `~/.pi/agent/settings.json` resolve relative to `~/.pi/agent`. Paths in `.pi/settings.json` resolve relative to `.pi`. Absolute paths and `~` are supported.
193
195
 
@@ -196,9 +198,10 @@ Paths in `~/.pi/agent/settings.json` resolve relative to `~/.pi/agent`. Paths in
196
198
  | `packages` | array | `[]` | npm/git packages to load resources from |
197
199
  | `extensions` | string[] | `[]` | Local extension file paths or directories |
198
200
  | `skills` | string[] | `[]` | Local skill file paths or directories |
201
+ | `rules` | string[] | `[]` | Local rule file paths or directories |
199
202
  | `prompts` | string[] | `[]` | Local prompt template paths or directories |
200
203
  | `themes` | string[] | `[]` | Local theme file paths or directories |
201
- | `enableSkillCommands` | boolean | `true` | Register skills as `/skill:name` commands |
204
+ | `enableSkillCommands` | boolean | `true` | Register skills and rules as `/skill:name` and `/rule:name` commands |
202
205
 
203
206
  Arrays support glob patterns and exclusions. Use `!pattern` to exclude. Use `+path` to force-include an exact path and `-path` to force-exclude an exact path.
204
207
 
@@ -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
@@ -50,7 +50,7 @@ Type `/` in the editor to open command completion. Extensions can register custo
50
50
  | `/copy` | Copy last assistant message to clipboard |
51
51
  | `/export [file]` | Export session to HTML |
52
52
  | `/share` | Upload as private GitHub gist with shareable HTML link |
53
- | `/reload` | Reload keybindings, extensions, skills, prompts, and context files |
53
+ | `/reload` | Reload keybindings, extensions, skills, rules, prompts, and context files |
54
54
  | `/hotkeys` | Show all keyboard shortcuts |
55
55
  | `/changelog` | Display version history |
56
56
  | `/quit` | Quit pi |
@@ -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
@@ -197,6 +197,8 @@ Built-in tools: `read`, `bash`, `edit`, `write`, `grep`, `find`, `ls`.
197
197
  | `--no-extensions` | Disable extension discovery |
198
198
  | `--skill <path>` | Load a skill; repeatable |
199
199
  | `--no-skills` | Disable skill discovery |
200
+ | `--rule <path>` | Load a rule; repeatable |
201
+ | `--no-rules` | Disable rule discovery |
200
202
  | `--prompt-template <path>` | Load a prompt template; repeatable |
201
203
  | `--no-prompt-templates` | Disable prompt template discovery |
202
204
  | `--theme <path>` | Load a theme; repeatable |
@@ -272,7 +274,7 @@ pi --tools read,grep,find,ls -p "Review the code"
272
274
 
273
275
  ## Design Principles
274
276
 
275
- Pi keeps the core small and pushes workflow-specific behavior into extensions, skills, prompt templates, and packages.
277
+ Pi keeps the core small and pushes workflow-specific behavior into extensions, skills, rules, prompt templates, and packages.
276
278
 
277
279
  It intentionally does not include built-in MCP, sub-agents, permission popups, plan mode, to-dos, or background bash. You can build or install those workflows as extensions or packages, or use external tools such as containers and tmux.
278
280
 
@@ -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
 
@@ -102,7 +103,7 @@ cp permission-gate.ts ~/.pi/agent/extensions/
102
103
 
103
104
  | Extension | Description |
104
105
  |-----------|-------------|
105
- | `dynamic-resources/` | Loads skills, prompts, and themes using `resources_discover` |
106
+ | `dynamic-resources/` | Loads skills, rules, prompts, and themes using `resources_discover` |
106
107
 
107
108
  ### Messages & Communication
108
109
 
@@ -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.5",
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.5",
4
+ "version": "0.0.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -0,0 +1,8 @@
1
+ ---
2
+ name: dynamic-rule
3
+ description: Example rule loaded from resources_discover. Use when demonstrating dynamic extension resources.
4
+ ---
5
+
6
+ # Dynamic Rule
7
+
8
+ This rule is provided by the dynamic-resources extension.
@@ -8,6 +8,7 @@ export default function (pi: ExtensionAPI) {
8
8
  pi.on("resources_discover", () => {
9
9
  return {
10
10
  skillPaths: [join(baseDir, "SKILL.md")],
11
+ rulePaths: [join(baseDir, "RULES.md")],
11
12
  promptPaths: [join(baseDir, "dynamic.md")],
12
13
  themePaths: [join(baseDir, "dynamic.json")],
13
14
  };
@@ -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
+ }
@@ -12,7 +12,7 @@ export default function (pi: ExtensionAPI) {
12
12
  // Command entrypoint for reload.
13
13
  // Treat reload as terminal for this handler.
14
14
  pi.registerCommand("reload-runtime", {
15
- description: "Reload extensions, skills, prompts, and themes",
15
+ description: "Reload extensions, skills, rules, prompts, and themes",
16
16
  handler: async (_args, ctx) => {
17
17
  await ctx.reload();
18
18
  return;
@@ -24,7 +24,7 @@ export default function (pi: ExtensionAPI) {
24
24
  pi.registerTool({
25
25
  name: "reload_runtime",
26
26
  label: "Reload Runtime",
27
- description: "Reload extensions, skills, prompts, and themes",
27
+ description: "Reload extensions, skills, rules, prompts, and themes",
28
28
  parameters: Type.Object({}),
29
29
  async execute() {
30
30
  pi.sendUserMessage("/reload-runtime", { deliverAs: "followUp" });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
3
  "private": true,
4
- "version": "0.0.5",
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.5",
4
+ "version": "0.0.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -40,6 +40,7 @@ const cwd = process.cwd();
40
40
  const resourceLoader: ResourceLoader = {
41
41
  getExtensions: () => ({ extensions: [], errors: [], runtime: createExtensionRuntime() }),
42
42
  getSkills: () => ({ skills: [], diagnostics: [] }),
43
+ getRules: () => ({ rules: [], diagnostics: [] }),
43
44
  getPrompts: () => ({ prompts: [], diagnostics: [] }),
44
45
  getThemes: () => ({ themes: [], diagnostics: [] }),
45
46
  getAgentsFiles: () => ({ agentsFiles: [] }),
@@ -88,7 +88,7 @@ Most one-off session options can be passed directly to `PiAgent.create()`:
88
88
  | `tools` | `[
89
89
  "read", "bash", "edit", "write"]` built-ins | Allowlist tool names across built-in, extension, and custom tools |
90
90
  | `customTools` | `[]` | Additional tool definitions |
91
- | `resourceLoader` | DefaultResourceLoader | Resource loader for extensions, skills, prompts, themes |
91
+ | `resourceLoader` | DefaultResourceLoader | Resource loader for extensions, skills, rules, prompts, themes |
92
92
  | `settingsManager` | SettingsManager.create(cwd, agentDir) | Settings source |
93
93
  | `resolveSessionOptions` | undefined | Per-session override hook for model, tools, and diagnostics |
94
94
 
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@fleetagent/pi-coding-agent",
3
- "version": "0.0.5",
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.5",
9
+ "version": "0.0.7",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
- "@fleetagent/pi-agent-core": "^0.0.5",
13
- "@fleetagent/pi-ai": "^0.0.5",
14
- "@fleetagent/pi-tui": "^0.0.5",
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.5",
477
- "resolved": "https://registry.npmjs.org/@fleetagent/pi-agent-core/-/pi-agent-core-0.0.5.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.5",
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.5",
491
- "resolved": "https://registry.npmjs.org/@fleetagent/pi-ai/-/pi-ai-0.0.5.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.5",
513
- "resolved": "https://registry.npmjs.org/@fleetagent/pi-tui/-/pi-tui-0.0.5.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.5",
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.5",
43
- "@fleetagent/pi-ai": "^0.0.5",
44
- "@fleetagent/pi-tui": "^0.0.5",
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",