@oh-my-pi/pi-coding-agent 13.14.2 → 13.15.3

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 (85) hide show
  1. package/CHANGELOG.md +150 -0
  2. package/package.json +10 -8
  3. package/src/autoresearch/command-initialize.md +34 -0
  4. package/src/autoresearch/command-resume.md +17 -0
  5. package/src/autoresearch/contract.ts +332 -0
  6. package/src/autoresearch/dashboard.ts +447 -0
  7. package/src/autoresearch/git.ts +243 -0
  8. package/src/autoresearch/helpers.ts +458 -0
  9. package/src/autoresearch/index.ts +693 -0
  10. package/src/autoresearch/prompt.md +227 -0
  11. package/src/autoresearch/resume-message.md +16 -0
  12. package/src/autoresearch/state.ts +386 -0
  13. package/src/autoresearch/tools/init-experiment.ts +310 -0
  14. package/src/autoresearch/tools/log-experiment.ts +833 -0
  15. package/src/autoresearch/tools/run-experiment.ts +640 -0
  16. package/src/autoresearch/types.ts +218 -0
  17. package/src/cli/args.ts +8 -2
  18. package/src/cli/initial-message.ts +58 -0
  19. package/src/config/keybindings.ts +423 -212
  20. package/src/config/model-registry.ts +1 -0
  21. package/src/config/model-resolver.ts +57 -9
  22. package/src/config/settings-schema.ts +38 -10
  23. package/src/config/settings.ts +1 -4
  24. package/src/export/html/template.css +43 -13
  25. package/src/export/html/template.generated.ts +1 -1
  26. package/src/export/html/template.html +1 -0
  27. package/src/export/html/template.js +107 -0
  28. package/src/extensibility/extensions/types.ts +31 -8
  29. package/src/internal-urls/docs-index.generated.ts +1 -1
  30. package/src/lsp/index.ts +1 -1
  31. package/src/main.ts +44 -44
  32. package/src/mcp/oauth-discovery.ts +1 -1
  33. package/src/modes/acp/acp-agent.ts +957 -0
  34. package/src/modes/acp/acp-event-mapper.ts +531 -0
  35. package/src/modes/acp/acp-mode.ts +13 -0
  36. package/src/modes/acp/index.ts +2 -0
  37. package/src/modes/components/agent-dashboard.ts +5 -4
  38. package/src/modes/components/custom-editor.ts +53 -51
  39. package/src/modes/components/extensions/extension-dashboard.ts +2 -1
  40. package/src/modes/components/history-search.ts +2 -1
  41. package/src/modes/components/hook-editor.ts +2 -1
  42. package/src/modes/components/hook-input.ts +8 -7
  43. package/src/modes/components/hook-selector.ts +15 -10
  44. package/src/modes/components/keybinding-hints.ts +9 -9
  45. package/src/modes/components/login-dialog.ts +3 -3
  46. package/src/modes/components/mcp-add-wizard.ts +2 -1
  47. package/src/modes/components/model-selector.ts +14 -3
  48. package/src/modes/components/oauth-selector.ts +2 -1
  49. package/src/modes/components/session-selector.ts +2 -1
  50. package/src/modes/components/settings-selector.ts +2 -1
  51. package/src/modes/components/status-line-segment-editor.ts +2 -1
  52. package/src/modes/components/tree-selector.ts +3 -2
  53. package/src/modes/components/user-message-selector.ts +3 -8
  54. package/src/modes/components/user-message.ts +16 -0
  55. package/src/modes/controllers/extension-ui-controller.ts +89 -4
  56. package/src/modes/controllers/input-controller.ts +48 -29
  57. package/src/modes/controllers/mcp-command-controller.ts +1 -1
  58. package/src/modes/index.ts +1 -0
  59. package/src/modes/interactive-mode.ts +17 -5
  60. package/src/modes/print-mode.ts +1 -1
  61. package/src/modes/prompt-action-autocomplete.ts +7 -7
  62. package/src/modes/rpc/rpc-mode.ts +7 -2
  63. package/src/modes/rpc/rpc-types.ts +1 -0
  64. package/src/modes/theme/theme.ts +53 -44
  65. package/src/modes/types.ts +9 -2
  66. package/src/modes/utils/hotkeys-markdown.ts +20 -20
  67. package/src/modes/utils/keybinding-matchers.ts +21 -0
  68. package/src/modes/utils/ui-helpers.ts +1 -1
  69. package/src/patch/hashline.ts +139 -127
  70. package/src/patch/index.ts +77 -59
  71. package/src/patch/shared.ts +19 -11
  72. package/src/prompts/tools/hashline.md +43 -116
  73. package/src/sdk.ts +34 -17
  74. package/src/session/agent-session.ts +436 -86
  75. package/src/session/messages.ts +23 -0
  76. package/src/session/session-manager.ts +97 -31
  77. package/src/tools/ask.ts +56 -30
  78. package/src/tools/bash-interceptor.ts +1 -39
  79. package/src/tools/bash-skill-urls.ts +1 -1
  80. package/src/tools/browser.ts +1 -1
  81. package/src/tools/gemini-image.ts +1 -1
  82. package/src/tools/resolve.ts +1 -1
  83. package/src/utils/child-process.ts +88 -0
  84. package/src/utils/image-input.ts +11 -1
  85. package/src/web/search/providers/codex.ts +10 -3
@@ -141,6 +141,55 @@ function isAlias(id: string): boolean {
141
141
  return !datePattern.test(id);
142
142
  }
143
143
 
144
+ /**
145
+ * Find an exact model reference match.
146
+ * Supports either a bare model id or a canonical provider/modelId reference.
147
+ * When matching by bare id, ambiguous matches across providers are rejected.
148
+ */
149
+ export function findExactModelReferenceMatch(
150
+ modelReference: string,
151
+ availableModels: Model<Api>[],
152
+ ): Model<Api> | undefined {
153
+ const trimmedReference = modelReference.trim();
154
+ if (!trimmedReference) {
155
+ return undefined;
156
+ }
157
+
158
+ const normalizedReference = trimmedReference.toLowerCase();
159
+
160
+ const canonicalMatches = availableModels.filter(
161
+ model => `${model.provider}/${model.id}`.toLowerCase() === normalizedReference,
162
+ );
163
+ if (canonicalMatches.length === 1) {
164
+ return canonicalMatches[0];
165
+ }
166
+ if (canonicalMatches.length > 1) {
167
+ return undefined;
168
+ }
169
+
170
+ const slashIndex = trimmedReference.indexOf("/");
171
+ if (slashIndex !== -1) {
172
+ const provider = trimmedReference.substring(0, slashIndex).trim();
173
+ const modelId = trimmedReference.substring(slashIndex + 1).trim();
174
+ if (provider && modelId) {
175
+ const providerMatches = availableModels.filter(
176
+ model =>
177
+ model.provider.toLowerCase() === provider.toLowerCase() &&
178
+ model.id.toLowerCase() === modelId.toLowerCase(),
179
+ );
180
+ if (providerMatches.length === 1) {
181
+ return providerMatches[0];
182
+ }
183
+ if (providerMatches.length > 1) {
184
+ return undefined;
185
+ }
186
+ }
187
+ }
188
+
189
+ const idMatches = availableModels.filter(model => model.id.toLowerCase() === normalizedReference);
190
+ return idMatches.length === 1 ? idMatches[0] : undefined;
191
+ }
192
+
144
193
  /**
145
194
  * Try to match a pattern to a model from the available models list.
146
195
  * Returns the matched model or undefined if no match found.
@@ -150,17 +199,17 @@ function tryMatchModel(
150
199
  availableModels: Model<Api>[],
151
200
  context: ModelPreferenceContext,
152
201
  ): Model<Api> | undefined {
153
- // Check for provider/modelId format (provider is everything before the first /)
202
+ // Try exact reference match first (handles provider/modelId and bare id with ambiguity rejection)
203
+ const exactRefMatch = findExactModelReferenceMatch(modelPattern, availableModels);
204
+ if (exactRefMatch) {
205
+ return exactRefMatch;
206
+ }
207
+
208
+ // Check for provider/modelId format — fuzzy match within provider
154
209
  const slashIndex = modelPattern.indexOf("/");
155
210
  if (slashIndex !== -1) {
156
211
  const provider = modelPattern.substring(0, slashIndex);
157
212
  const modelId = modelPattern.substring(slashIndex + 1);
158
- const providerMatch = availableModels.find(
159
- m => m.provider.toLowerCase() === provider.toLowerCase() && m.id.toLowerCase() === modelId.toLowerCase(),
160
- );
161
- if (providerMatch) {
162
- return providerMatch;
163
- }
164
213
 
165
214
  const providerModels = availableModels.filter(m => m.provider.toLowerCase() === provider.toLowerCase());
166
215
  if (providerModels.length > 0) {
@@ -187,10 +236,9 @@ function tryMatchModel(
187
236
  return scored[0]?.model;
188
237
  }
189
238
  }
190
- // No exact provider/model match - fall through to other matching
191
239
  }
192
240
 
193
- // Check for exact ID match (case-insensitive)
241
+ // Exact ID match (case-insensitive) — with ambiguity across providers handled by preference
194
242
  const exactMatches = availableModels.filter(m => m.id.toLowerCase() === modelPattern.toLowerCase());
195
243
  if (exactMatches.length > 0) {
196
244
  return pickPreferredModel(exactMatches, context);
@@ -139,6 +139,43 @@ type SettingDef =
139
139
  // under `as const` while still letting SettingValue infer the correct element type.
140
140
  const EMPTY_STRING_ARRAY: string[] = [];
141
141
  const EMPTY_STRING_RECORD: Record<string, string> = {};
142
+ export const DEFAULT_BASH_INTERCEPTOR_RULES: BashInterceptorRule[] = [
143
+ {
144
+ pattern: "^\\s*(cat|head|tail|less|more)\\s+",
145
+ tool: "read",
146
+ message: "Use the `read` tool instead of cat/head/tail. It provides better context and handles binary files.",
147
+ },
148
+ {
149
+ pattern: "^\\s*(grep|rg|ripgrep|ag|ack)\\s+",
150
+ tool: "grep",
151
+ message: "Use the `grep` tool instead of grep/rg. It respects .gitignore and provides structured output.",
152
+ },
153
+ {
154
+ pattern: "^\\s*(find|fd|locate)\\s+.*(-name|-iname|-type|--type|-glob)",
155
+ tool: "find",
156
+ message: "Use the `find` tool instead of find/fd. It respects .gitignore and is faster for glob patterns.",
157
+ },
158
+ {
159
+ pattern: "^\\s*sed\\s+(-i|--in-place)",
160
+ tool: "edit",
161
+ message: "Use the `edit` tool instead of sed -i. It provides diff preview and fuzzy matching.",
162
+ },
163
+ {
164
+ pattern: "^\\s*perl\\s+.*-[pn]?i",
165
+ tool: "edit",
166
+ message: "Use the `edit` tool instead of perl -i. It provides diff preview and fuzzy matching.",
167
+ },
168
+ {
169
+ pattern: "^\\s*awk\\s+.*-i\\s+inplace",
170
+ tool: "edit",
171
+ message: "Use the `edit` tool instead of awk -i inplace. It provides diff preview and fuzzy matching.",
172
+ },
173
+ {
174
+ pattern: "^\\s*(echo|printf|cat\\s*<<)\\s+.*[^|]>\\s*\\S",
175
+ tool: "write",
176
+ message: "Use the `write` tool instead of echo/cat redirection. It handles encoding and provides confirmation.",
177
+ },
178
+ ];
142
179
 
143
180
  export const SETTINGS_SCHEMA = {
144
181
  // ────────────────────────────────────────────────────────────────────────
@@ -943,16 +980,7 @@ export const SETTINGS_SCHEMA = {
943
980
  default: false,
944
981
  ui: { tab: "editing", label: "Bash Interceptor", description: "Block shell commands that have dedicated tools" },
945
982
  },
946
-
947
- "bashInterceptor.simpleLs": {
948
- type: "boolean",
949
- default: true,
950
- ui: {
951
- tab: "editing",
952
- label: "Intercept `ls`",
953
- description: "Intercept bare ls commands (when interceptor is enabled)",
954
- },
955
- },
983
+ "bashInterceptor.patterns": { type: "array", default: DEFAULT_BASH_INTERCEPTOR_RULES },
956
984
 
957
985
  // Python
958
986
  "python.toolMode": {
@@ -341,10 +341,7 @@ export class Settings {
341
341
  * Get bash interceptor rules (typed accessor for complex array config).
342
342
  */
343
343
  getBashInterceptorRules(): BashInterceptorRule[] {
344
- const patterns = (this.#merged.bashInterceptor as { patterns?: unknown[] })?.patterns;
345
- if (!Array.isArray(patterns)) return [];
346
-
347
- return patterns.filter((p): p is BashInterceptorRule => typeof p === "object" && p !== null && "pattern" in p);
344
+ return this.get("bashInterceptor.patterns");
348
345
  }
349
346
 
350
347
  /**
@@ -2,6 +2,10 @@
2
2
 
3
3
  :root {
4
4
  --line-height: 18px; /* 12px font * 1.5 */
5
+ --sidebar-width: 400px;
6
+ --sidebar-min-width: 240px;
7
+ --sidebar-max-width: 840px;
8
+ --sidebar-resizer-width: 6px;
5
9
  }
6
10
 
7
11
  body {
@@ -12,6 +16,11 @@
12
16
  background: var(--body-bg);
13
17
  }
14
18
 
19
+ body.sidebar-resizing {
20
+ cursor: col-resize;
21
+ user-select: none;
22
+ }
23
+
15
24
  #app {
16
25
  display: flex;
17
26
  min-height: 100vh;
@@ -19,7 +28,9 @@
19
28
 
20
29
  /* Sidebar */
21
30
  #sidebar {
22
- width: 400px;
31
+ width: var(--sidebar-width);
32
+ min-width: var(--sidebar-width);
33
+ max-width: var(--sidebar-width);
23
34
  background: var(--container-bg);
24
35
  flex-shrink: 0;
25
36
  display: flex;
@@ -203,8 +214,28 @@
203
214
  flex-shrink: 0;
204
215
  }
205
216
 
217
+ #sidebar-resizer {
218
+ width: var(--sidebar-resizer-width);
219
+ flex-shrink: 0;
220
+ position: sticky;
221
+ top: 0;
222
+ height: 100vh;
223
+ cursor: col-resize;
224
+ touch-action: none;
225
+ background: transparent;
226
+ border-right: 1px solid transparent;
227
+ }
228
+
229
+ #sidebar-resizer:hover,
230
+ body.sidebar-resizing #sidebar-resizer {
231
+ background: var(--selectedBg);
232
+ border-right-color: var(--dim);
233
+ }
234
+
206
235
  /* Main content */
207
236
  #content {
237
+ flex: 1;
238
+ min-width: 0;
208
239
  flex: 1;
209
240
  overflow-y: auto;
210
241
  padding: var(--line-height) calc(var(--line-height) * 2);
@@ -841,17 +872,19 @@
841
872
  @media (max-width: 900px) {
842
873
  #sidebar {
843
874
  position: fixed;
844
- left: -400px;
845
- width: 400px;
875
+ transform: translateX(-100%);
876
+ width: min(var(--sidebar-width), 100vw);
877
+ min-width: 0;
878
+ max-width: 100vw;
846
879
  top: 0;
847
880
  bottom: 0;
848
881
  height: 100vh;
849
882
  z-index: 99;
850
- transition: left 0.3s;
883
+ transition: transform 0.3s;
851
884
  }
852
885
 
853
886
  #sidebar.open {
854
- left: 0;
887
+ transform: translateX(0);
855
888
  }
856
889
 
857
890
  #sidebar-overlay.open {
@@ -866,6 +899,10 @@
866
899
  display: block;
867
900
  }
868
901
 
902
+ #sidebar-resizer {
903
+ display: none;
904
+ }
905
+
869
906
  #content {
870
907
  padding: var(--line-height) 16px;
871
908
  }
@@ -875,15 +912,8 @@
875
912
  }
876
913
  }
877
914
 
878
- @media (max-width: 500px) {
879
- #sidebar {
880
- width: 100vw;
881
- left: -100vw;
882
- }
883
- }
884
-
885
915
  @media print {
886
- #sidebar, #sidebar-toggle { display: none !important; }
916
+ #sidebar, #sidebar-toggle, #sidebar-resizer { display: none !important; }
887
917
  body { background: white; color: black; }
888
918
  #content { max-width: none; }
889
919
  }