lsd-pi 1.1.9 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/dist/resources/extensions/slash-commands/context.js +15 -8
  2. package/dist/resources/extensions/slash-commands/index.js +2 -0
  3. package/dist/resources/extensions/slash-commands/init.js +47 -0
  4. package/dist/resources/extensions/slash-commands/plan.js +241 -54
  5. package/dist/resources/extensions/slash-commands/tools.js +47 -21
  6. package/dist/resources/extensions/subagent/index.js +5 -10
  7. package/dist/startup-model-validation.d.ts +1 -1
  8. package/package.json +1 -1
  9. package/packages/pi-agent-core/dist/types.d.ts +2 -1
  10. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  11. package/packages/pi-agent-core/dist/types.js.map +1 -1
  12. package/packages/pi-agent-core/src/types.ts +2 -1
  13. package/packages/pi-ai/dist/providers/amazon-bedrock.js +10 -3
  14. package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  15. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts +1 -1
  16. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  17. package/packages/pi-ai/dist/providers/anthropic-shared.js +4 -1
  18. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  19. package/packages/pi-ai/dist/providers/anthropic.d.ts +2 -2
  20. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  21. package/packages/pi-ai/dist/providers/anthropic.js +2 -2
  22. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  23. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
  24. package/packages/pi-ai/dist/providers/azure-openai-responses.js +2 -1
  25. package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
  26. package/packages/pi-ai/dist/providers/google-gemini-cli.js +2 -0
  27. package/packages/pi-ai/dist/providers/google-gemini-cli.js.map +1 -1
  28. package/packages/pi-ai/dist/providers/google-vertex.js +2 -0
  29. package/packages/pi-ai/dist/providers/google-vertex.js.map +1 -1
  30. package/packages/pi-ai/dist/providers/google.js +2 -0
  31. package/packages/pi-ai/dist/providers/google.js.map +1 -1
  32. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  33. package/packages/pi-ai/dist/providers/openai-codex-responses.js +2 -1
  34. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  35. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  36. package/packages/pi-ai/dist/providers/openai-completions.js +2 -1
  37. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  38. package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  39. package/packages/pi-ai/dist/providers/openai-responses.js +2 -1
  40. package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
  41. package/packages/pi-ai/dist/providers/simple-options.d.ts +1 -1
  42. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  43. package/packages/pi-ai/dist/providers/simple-options.js +6 -2
  44. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  45. package/packages/pi-ai/dist/types.d.ts +1 -1
  46. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  47. package/packages/pi-ai/dist/types.js.map +1 -1
  48. package/packages/pi-ai/src/providers/amazon-bedrock.ts +11 -4
  49. package/packages/pi-ai/src/providers/anthropic-shared.ts +5 -2
  50. package/packages/pi-ai/src/providers/anthropic.ts +2 -2
  51. package/packages/pi-ai/src/providers/azure-openai-responses.ts +2 -1
  52. package/packages/pi-ai/src/providers/google-gemini-cli.ts +3 -1
  53. package/packages/pi-ai/src/providers/google-vertex.ts +3 -1
  54. package/packages/pi-ai/src/providers/google.ts +3 -1
  55. package/packages/pi-ai/src/providers/openai-codex-responses.ts +2 -1
  56. package/packages/pi-ai/src/providers/openai-completions.ts +2 -1
  57. package/packages/pi-ai/src/providers/openai-responses.ts +2 -1
  58. package/packages/pi-ai/src/providers/simple-options.ts +5 -3
  59. package/packages/pi-ai/src/types.ts +1 -1
  60. package/packages/pi-coding-agent/dist/cli/args.js +1 -1
  61. package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
  62. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +10 -0
  63. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  64. package/packages/pi-coding-agent/dist/core/agent-session.js +57 -20
  65. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  66. package/packages/pi-coding-agent/dist/core/classifier-service.d.ts.map +1 -1
  67. package/packages/pi-coding-agent/dist/core/classifier-service.js +34 -61
  68. package/packages/pi-coding-agent/dist/core/classifier-service.js.map +1 -1
  69. package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts +1 -1
  70. package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts.map +1 -1
  71. package/packages/pi-coding-agent/dist/core/compaction/utils.js +3 -1
  72. package/packages/pi-coding-agent/dist/core/compaction/utils.js.map +1 -1
  73. package/packages/pi-coding-agent/dist/core/resource-loader-lsd-md.test.js +59 -7
  74. package/packages/pi-coding-agent/dist/core/resource-loader-lsd-md.test.js.map +1 -1
  75. package/packages/pi-coding-agent/dist/core/resource-loader.js +4 -4
  76. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  77. package/packages/pi-coding-agent/dist/core/sdk.d.ts +1 -1
  78. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  79. package/packages/pi-coding-agent/dist/core/sdk.js +19 -20
  80. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  81. package/packages/pi-coding-agent/dist/core/sdk.test.js +80 -0
  82. package/packages/pi-coding-agent/dist/core/sdk.test.js.map +1 -1
  83. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +15 -5
  84. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  85. package/packages/pi-coding-agent/dist/core/settings-manager.js +31 -5
  86. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  87. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +6 -1
  88. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  89. package/packages/pi-coding-agent/dist/core/system-prompt.js +28 -68
  90. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  91. package/packages/pi-coding-agent/dist/core/tools/bash-interceptor.js +1 -1
  92. package/packages/pi-coding-agent/dist/core/tools/bash-interceptor.js.map +1 -1
  93. package/packages/pi-coding-agent/dist/core/tools/bash-interceptor.test.js +5 -0
  94. package/packages/pi-coding-agent/dist/core/tools/bash-interceptor.test.js.map +1 -1
  95. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  96. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +28 -0
  97. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -1
  98. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +8 -0
  99. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  100. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +44 -1
  101. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  102. package/packages/pi-coding-agent/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  103. package/packages/pi-coding-agent/dist/modes/interactive/components/thinking-selector.js +1 -0
  104. package/packages/pi-coding-agent/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  105. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  106. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +36 -0
  107. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  108. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  109. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +41 -5
  110. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  111. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
  112. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  113. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +2 -0
  114. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  115. package/packages/pi-coding-agent/package.json +1 -1
  116. package/packages/pi-coding-agent/src/cli/args.ts +1 -1
  117. package/packages/pi-coding-agent/src/core/agent-session.ts +62 -19
  118. package/packages/pi-coding-agent/src/core/classifier-service.ts +35 -63
  119. package/packages/pi-coding-agent/src/core/compaction/utils.ts +3 -1
  120. package/packages/pi-coding-agent/src/core/resource-loader-lsd-md.test.ts +67 -7
  121. package/packages/pi-coding-agent/src/core/resource-loader.ts +4 -4
  122. package/packages/pi-coding-agent/src/core/sdk.test.ts +100 -0
  123. package/packages/pi-coding-agent/src/core/sdk.ts +24 -21
  124. package/packages/pi-coding-agent/src/core/settings-manager.ts +42 -8
  125. package/packages/pi-coding-agent/src/core/system-prompt.ts +39 -82
  126. package/packages/pi-coding-agent/src/core/tools/bash-interceptor.test.ts +6 -0
  127. package/packages/pi-coding-agent/src/core/tools/bash-interceptor.ts +1 -1
  128. package/packages/pi-coding-agent/src/modes/interactive/components/bash-execution.ts +26 -0
  129. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +53 -1
  130. package/packages/pi-coding-agent/src/modes/interactive/components/thinking-selector.ts +1 -0
  131. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +41 -0
  132. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +50 -7
  133. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +3 -1
  134. package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
  135. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  136. package/pkg/dist/modes/interactive/theme/theme.js +2 -0
  137. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  138. package/pkg/package.json +1 -1
  139. package/src/resources/extensions/slash-commands/context.ts +15 -8
  140. package/src/resources/extensions/slash-commands/index.ts +2 -0
  141. package/src/resources/extensions/slash-commands/init.ts +55 -0
  142. package/src/resources/extensions/slash-commands/plan.ts +277 -55
  143. package/src/resources/extensions/slash-commands/tools.ts +47 -21
  144. package/src/resources/extensions/subagent/index.ts +5 -10
@@ -127,9 +127,12 @@ export interface Settings {
127
127
  planModeReasoningModel?: string;
128
128
  planModeReviewModel?: string;
129
129
  planModeCodingModel?: string;
130
+ autoSuggestPlanMode?: boolean; // default: false — append a system-prompt instruction so the LLM proposes plan mode for large tasks
131
+ autoSwitchPlanModel?: boolean; // default: false — enable opusplan-style model switching (auto-switch to reasoning model on entry, new-session option on approval)
130
132
  permissionMode?: "danger-full-access" | "accept-on-edit" | "auto" | "plan";
131
133
  classifierModel?: string;
132
- defaultThinkingLevel?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
134
+ defaultThinkingLevel?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "adaptive";
135
+ anthropicAdaptiveByDefault?: boolean; // default: false — prefer adaptive thinking when using supported Anthropic models
133
136
  transport?: TransportSetting; // default: "sse"
134
137
  steeringMode?: "all" | "one-at-a-time";
135
138
  followUpMode?: "all" | "one-at-a-time";
@@ -149,7 +152,8 @@ export interface Settings {
149
152
  prompts?: string[]; // Array of local prompt template paths or directories
150
153
  themes?: string[]; // Array of local theme file paths or directories
151
154
  enableSkillCommands?: boolean; // default: true - register skills as /skill:name commands
152
- toolSearch?: boolean; // default: false - start with a minimal tool set and use tool_search/tool_enable to lazily activate tools
155
+ toolSearch?: boolean; // legacy boolean toggle from deprecated minimal profile; retained for migration only
156
+ toolProfile?: "balanced" | "full"; // default: "balanced"
153
157
  terminal?: TerminalSettings;
154
158
  images?: ImageSettings;
155
159
  enabledModels?: string[]; // Model patterns for cycling (same format as --models CLI flag)
@@ -805,6 +809,22 @@ export class SettingsManager {
805
809
  this.setGlobalSetting("planModeCodingModel", modelRef.trim());
806
810
  }
807
811
 
812
+ getAutoSuggestPlanMode(): boolean {
813
+ return this.settings.autoSuggestPlanMode ?? false;
814
+ }
815
+
816
+ setAutoSuggestPlanMode(enabled: boolean): void {
817
+ this.setGlobalSetting("autoSuggestPlanMode", enabled);
818
+ }
819
+
820
+ getAutoSwitchPlanModel(): boolean {
821
+ return this.settings.autoSwitchPlanModel ?? false;
822
+ }
823
+
824
+ setAutoSwitchPlanModel(enabled: boolean): void {
825
+ this.setGlobalSetting("autoSwitchPlanModel", enabled);
826
+ }
827
+
808
828
  setPermissionMode(mode: "danger-full-access" | "accept-on-edit" | "auto" | "plan"): void {
809
829
  this.setGlobalSetting("permissionMode", mode);
810
830
  }
@@ -873,14 +893,22 @@ export class SettingsManager {
873
893
  this.setGlobalSetting("themeAccent", accent);
874
894
  }
875
895
 
876
- getDefaultThinkingLevel(): "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | undefined {
896
+ getDefaultThinkingLevel(): "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "adaptive" | undefined {
877
897
  return this.settings.defaultThinkingLevel;
878
898
  }
879
899
 
880
- setDefaultThinkingLevel(level: "off" | "minimal" | "low" | "medium" | "high" | "xhigh"): void {
900
+ setDefaultThinkingLevel(level: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "adaptive"): void {
881
901
  this.setGlobalSetting("defaultThinkingLevel", level);
882
902
  }
883
903
 
904
+ getAnthropicAdaptiveByDefault(): boolean {
905
+ return this.settings.anthropicAdaptiveByDefault ?? false;
906
+ }
907
+
908
+ setAnthropicAdaptiveByDefault(enabled: boolean): void {
909
+ this.setGlobalSetting("anthropicAdaptiveByDefault", enabled);
910
+ }
911
+
884
912
  getTransport(): TransportSetting {
885
913
  return this.settings.transport ?? "sse";
886
914
  }
@@ -1066,12 +1094,18 @@ export class SettingsManager {
1066
1094
  this.setGlobalSetting("enableSkillCommands", enabled);
1067
1095
  }
1068
1096
 
1069
- getToolSearch(): boolean {
1070
- return this.settings.toolSearch ?? false;
1097
+ getToolProfile(): "balanced" | "full" {
1098
+ const profile = this.settings.toolProfile;
1099
+ if (profile === "balanced" || profile === "full") return profile;
1100
+ // Migrate legacy minimal/toolSearch settings to balanced.
1101
+ if (this.settings.toolSearch !== undefined) return "balanced";
1102
+ return "balanced";
1071
1103
  }
1072
1104
 
1073
- setToolSearch(enabled: boolean): void {
1074
- this.setGlobalSetting("toolSearch", enabled);
1105
+ setToolProfile(profile: "balanced" | "full"): void {
1106
+ this.setGlobalSetting("toolProfile", profile);
1107
+ // Keep legacy field in sync with deprecated minimal mode removal.
1108
+ this.setGlobalSetting("toolSearch", false);
1075
1109
  }
1076
1110
 
1077
1111
  getThinkingBudgets(): ThinkingBudgetsSettings | undefined {
@@ -2,6 +2,7 @@
2
2
  * System prompt construction and project context loading
3
3
  */
4
4
 
5
+ import { existsSync } from "node:fs";
5
6
  import { getDocsPath, getExamplesPath, getReadmePath } from "../config.js";
6
7
  import { toPosixPath } from "../utils/path-display.js";
7
8
  import { formatSkillsForPrompt, type Skill } from "./skills.js";
@@ -19,7 +20,12 @@ const toolDescriptions: Record<string, string> = {
19
20
  };
20
21
 
21
22
  export interface BuildSystemPromptOptions {
22
- /** Custom system prompt (replaces default). */
23
+ /**
24
+ * Custom system prompt that replaces the default role header, tool list,
25
+ * and guidelines entirely. You are responsible for providing your own
26
+ * tool-usage guidance. Project context, skills, extension promptGuidelines,
27
+ * date/cwd, and appendSystemPrompt are still applied on top.
28
+ */
23
29
  customPrompt?: string;
24
30
  /** Tools to include in prompt. Default: [read, bash, edit, write] */
25
31
  selectedTools?: string[];
@@ -90,10 +96,6 @@ export function buildSystemPrompt(options: BuildSystemPromptOptions = {}): strin
90
96
  prompt += formatSkillsForPrompt(skills);
91
97
  }
92
98
 
93
- // Add date/time and working directory last
94
- prompt += `\nCurrent date and time: ${dateTime}`;
95
- prompt += `\nCurrent working directory: ${resolvedCwd}`;
96
-
97
99
  // Append promptGuidelines from extension-registered tools.
98
100
  // Without this, tools registered via pi.registerTool() with promptGuidelines
99
101
  // have their definitions reach the API but the model has no guidance on when
@@ -105,6 +107,10 @@ export function buildSystemPrompt(options: BuildSystemPromptOptions = {}): strin
105
107
  }
106
108
  }
107
109
 
110
+ // Add date/time and working directory last
111
+ prompt += `\nCurrent date and time: ${dateTime}`;
112
+ prompt += `\nCurrent working directory: ${resolvedCwd}`;
113
+
108
114
  return prompt;
109
115
  }
110
116
 
@@ -137,90 +143,45 @@ export function buildSystemPrompt(options: BuildSystemPromptOptions = {}): strin
137
143
  guidelinesList.push(guideline);
138
144
  };
139
145
 
140
- const hasBash = tools.includes("bash");
141
146
  const hasEdit = tools.includes("edit");
142
147
  const hasWrite = tools.includes("write");
143
- const hasGrep = tools.includes("grep");
144
- const hasFind = tools.includes("find");
145
- const hasLs = tools.includes("ls");
146
148
  const hasRead = tools.includes("read");
147
149
  const hasLsp = tools.includes("lsp");
148
150
 
149
- // LSP guideline — must come FIRST so it outranks grep/bash in the model's priority
150
- if (hasLsp) {
151
- addGuideline(
152
- `Use lsp as the primary tool for code navigation in typed codebases:
153
- - Navigation: definition, type_definition, implementation, references, incoming_calls, outgoing_calls
154
- - Understanding: hover (types + docs), signature (parameter info), symbols (file/workspace search)
155
- - Refactoring: rename (project-wide), code_actions (quick-fixes, imports, refactors), format (formatter)
156
- - Verification: diagnostics after edits to catch type errors immediately
157
- - Before choosing a code-navigation tool, classify your intent: symbol lookup (definition, references, type info, callers, rename, formatting, diagnostics) or text search. Symbols → lsp. Text patterns / non-code files → grep/find/ls
158
- - Tool selection — code navigation:
159
- • Find where a symbol is defined → lsp definition (not grep)
160
- • Find all usages of a symbol → lsp references (not grep)
161
- • Find implementations of an interface or method → lsp implementation (not grep)
162
- • Get type info or docs for a symbol → lsp hover/signature (not reading source first)
163
- • Find all callers of a function → lsp incoming_calls (not grep)
164
- • Search for a text pattern, TODO, log message, or non-code content → grep
165
- • Search for filenames or directory structure → find/ls
166
- • Rename a symbol across the codebase → lsp rename (not find-and-replace)
167
- • Check typed errors after edits → lsp diagnostics (not manual scans)
168
- • Format a file → lsp format when available (not shelling out)
169
- - When lsp is available, ALWAYS prefer it over grep/bash/find for: finding definitions, finding references, getting type info, renaming symbols, listing symbols in a file or workspace, checking diagnostics/errors, and formatting
170
- - Never grep for a symbol definition when lsp can resolve it semantically
171
- - Never shell out to a formatter when lsp format is available`,
172
- );
173
- }
151
+ // Priority-ordered compact guidelines
152
+ addGuideline("Be concise. Prefer short, direct answers over preamble.");
153
+ addGuideline("For conceptual, product, or UX questions, answer first; inspect code only if implementation detail is needed.");
174
154
 
175
- // If the request is primarily conceptual or product/design oriented, answer the design or strategy question first and inspect the codebase only after confirming implementation detail is needed.
176
- addGuideline(
177
- "If the request is primarily conceptual, product, or UX/design oriented, answer the question first with concise recommendations and inspect the codebase only after confirming implementation detail is needed",
178
- );
179
-
180
- // Use subagents for broad reconnaissance before spending expensive-model tokens on discovery
181
- if (tools.includes("subagent")) {
155
+ if (hasLsp) {
182
156
  addGuideline(
183
- "Use a scout subagent for broad repo reconnaissance when file ownership or architecture is unclear; keep the scout read-only, use a cheaper model when available, and ask it to return a high-signal handoff with exact files/symbols to inspect next",
157
+ "Code navigation in typed codebases: use lsp for symbols (definition, references, implementation, hover, diagnostics, rename, format). Use grep/find/ls for text patterns, filenames, and non-code files.",
184
158
  );
159
+ } else {
160
+ addGuideline("Use grep/find/ls for code search and file exploration (faster than bash, respects .gitignore)");
185
161
  }
186
162
 
187
- addGuideline(
188
- "Do not deep-dive backend schemas, migrations, or infrastructure for a conceptual/product request until you confirm that implementation constraints actually matter",
189
- );
190
-
191
- // File exploration guidelines (only for tasks lsp cannot do: raw text search, non-code files, etc.)
192
- if (hasBash && !hasGrep && !hasFind && !hasLs) {
193
- addGuideline("Use bash for file operations like ls, rg, find");
194
- } else if (hasBash && (hasGrep || hasFind || hasLs)) {
195
- if (hasLsp) {
196
- addGuideline("Prefer grep/find/ls tools over bash for file exploration (faster, respects .gitignore) — but prefer lsp over grep/find when navigating typed code");
197
- } else {
198
- addGuideline("Prefer grep/find/ls tools over bash for file exploration (faster, respects .gitignore)");
199
- }
200
- }
201
-
202
- // Read before edit guideline
203
163
  if (hasRead && hasEdit) {
204
- addGuideline("Use read to examine files before editing. You must use this tool instead of cat or sed.");
164
+ addGuideline("Read files before editing them. Never use cat or sed to inspect or modify files.");
205
165
  }
206
166
 
207
- // Edit guideline
208
167
  if (hasEdit) {
209
- addGuideline("Use edit for precise changes (old text must match exactly)");
168
+ addGuideline("edit requires exact text match; write is for new files or full rewrites.");
169
+ }
170
+
171
+ if (hasWrite && !hasEdit) {
172
+ addGuideline("write is for new files or full rewrites.");
210
173
  }
211
174
 
212
- // Write guideline
213
- if (hasWrite) {
214
- addGuideline("Use write only for new files or complete rewrites");
175
+ if (hasLsp && hasEdit) {
176
+ addGuideline("Run lsp diagnostics after edits to catch type errors.");
215
177
  }
216
178
 
217
- // Output guideline (only when actually writing or executing)
218
179
  if (hasEdit || hasWrite) {
219
- addGuideline(
220
- "When summarizing your actions, output plain text directly - do NOT use cat or bash to display what you did",
221
- );
180
+ addGuideline("Output plain text directly when summarizing your work — do not cat or echo to display what you did.");
222
181
  }
223
182
 
183
+ addGuideline("Show file paths clearly when referencing files.");
184
+
224
185
  for (const guideline of promptGuidelines ?? []) {
225
186
  const normalized = guideline.trim();
226
187
  if (normalized.length > 0) {
@@ -228,13 +189,17 @@ export function buildSystemPrompt(options: BuildSystemPromptOptions = {}): strin
228
189
  }
229
190
  }
230
191
 
231
- // Always include these
232
- addGuideline("Be concise in your responses");
233
- addGuideline("Show file paths clearly when working with files");
234
-
235
192
  const guidelines = guidelinesList.map((g) => `- ${g}`).join("\n");
236
193
 
237
- let prompt = `You are an expert coding assistant operating inside pi, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.
194
+ const piDocsBlock = existsSync(readmePath)
195
+ ? `\n\nPi documentation (read only when the user asks about pi itself, its SDK, extensions, themes, skills, or TUI):
196
+ - Main documentation: ${readmePath}
197
+ - Additional docs: ${docsPath}
198
+ - Examples: ${examplesPath} (extensions, custom tools, SDK)
199
+ - When working on pi topics, read the docs and follow .md cross-references before implementing`
200
+ : "";
201
+
202
+ let prompt = `You are an expert coding assistant operating inside pi, a coding agent harness.
238
203
 
239
204
  Available tools:
240
205
  ${toolsList}
@@ -242,15 +207,7 @@ ${toolsList}
242
207
  In addition to the tools above, you may have access to other custom tools depending on the project.
243
208
 
244
209
  Guidelines:
245
- ${guidelines}
246
-
247
- Pi documentation (read only when the user asks about pi itself, its SDK, extensions, themes, skills, or TUI):
248
- - Main documentation: ${readmePath}
249
- - Additional docs: ${docsPath}
250
- - Examples: ${examplesPath} (extensions, custom tools, SDK)
251
- - When asked about: extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), pi packages (docs/packages.md)
252
- - When working on pi topics, read the docs and examples, and follow .md cross-references before implementing
253
- - Always read pi .md files completely and follow links to related docs (e.g., tui.md for TUI API details)`;
210
+ ${guidelines}${piDocsBlock}`;
254
211
 
255
212
  if (appendSection) {
256
213
  prompt += appendSection;
@@ -145,6 +145,12 @@ describe("checkBashInterception", () => {
145
145
  assert.ok(r.message?.includes("cat README.md"), "message should contain original command");
146
146
  });
147
147
 
148
+ it("uses 'Tool routing:' prefix to distinguish from permission blocks", () => {
149
+ const r = checkBashInterception("cat README.md", ALL_TOOLS);
150
+ assert.ok(r.message?.startsWith("Tool routing:"), "message should start with 'Tool routing:'");
151
+ assert.ok(r.message?.includes("not a permission block"), "message should clarify it's not a permission block");
152
+ });
153
+
148
154
  it("returns block:false with no message when not blocked", () => {
149
155
  const r = checkBashInterception("npm install", ALL_TOOLS);
150
156
  assert.equal(r.block, false);
@@ -86,7 +86,7 @@ export function compileInterceptor(rules: BashInterceptorRule[]): CompiledInterc
86
86
  if (regex.test(trimmed) && availableTools.includes(rule.tool)) {
87
87
  return {
88
88
  block: true,
89
- message: `Blocked: ${rule.message}\n\nOriginal command: ${command}`,
89
+ message: `Tool routing: ${rule.message}\n\nThis is a routing suggestion, not a permission block. Use the suggested tool to perform this action.\n\nOriginal command: ${command}`,
90
90
  suggestedTool: rule.tool,
91
91
  };
92
92
  }
@@ -17,6 +17,25 @@ import { truncateToVisualLines } from "./visual-truncate.js";
17
17
  // Preview line limit when not expanded (matches tool execution behavior)
18
18
  const PREVIEW_LINES = 20;
19
19
 
20
+ /**
21
+ * Returns true if the command failure looks like a privilege/sudo escalation
22
+ * error that the agent cannot resolve — distinct from an OS sandbox policy block.
23
+ */
24
+ function isPrivilegeError(output: string, command: string): boolean {
25
+ // Already handled by sandbox-policy path
26
+ if (/sandbox blocked this operation/i.test(output)) return false;
27
+ // sudo explicitly rejected
28
+ if (/sudo.*operation not permitted/i.test(output)) return true;
29
+ if (/command exited with code 126/i.test(output) && /sudo/i.test(output)) return true;
30
+ // Agent process itself hit EPERM (e.g. npm cache owned by root)
31
+ if (/EPERM|operation not permitted/i.test(output) && /sudo/i.test(command)) return true;
32
+ // npm cache root-owned files pattern
33
+ if (/cache folder contains root-owned files/i.test(output)) return true;
34
+ // Generic privilege denial patterns
35
+ if (/permission denied/i.test(output) && /sudo/i.test(command)) return true;
36
+ return false;
37
+ }
38
+
20
39
  type ToolOutputMode = "minimal" | "normal";
21
40
 
22
41
  export class BashExecutionComponent extends Container {
@@ -217,6 +236,13 @@ export class BashExecutionComponent extends Container {
217
236
  "Sandbox blocked this operation. Run /sandbox to inspect the active policy and allowed paths.",
218
237
  ),
219
238
  );
239
+ } else if (!this.sandboxed && isPrivilegeError(fullOutput, this.command)) {
240
+ statusParts.push(
241
+ theme.fg("warning", "⚠ This command requires elevated privileges the agent cannot use."),
242
+ );
243
+ statusParts.push(
244
+ theme.fg("muted", `Run it manually in your terminal:\n ${this.command}`),
245
+ );
220
246
  }
221
247
  }
222
248
 
@@ -16,6 +16,7 @@ import { DynamicBorder } from "./dynamic-border.js";
16
16
 
17
17
  export const THINKING_DESCRIPTIONS: Record<ThinkingLevel, string> = {
18
18
  off: "No reasoning",
19
+ adaptive: "Claude decides when and how much to think (Claude 4.6+ only)",
19
20
  minimal: "Very brief reasoning (~1k tokens)",
20
21
  low: "Light reasoning (~2k tokens)",
21
22
  medium: "Moderate reasoning (~8k tokens)",
@@ -32,10 +33,13 @@ export interface SettingsConfig {
32
33
  planModeReasoningModel: string;
33
34
  planModeReviewModel: string;
34
35
  planModeCodingModel: string;
36
+ autoSuggestPlanMode: boolean;
37
+ autoSwitchPlanModel: boolean;
35
38
  showImages: boolean;
36
39
  autoResizeImages: boolean;
37
40
  blockImages: boolean;
38
41
  enableSkillCommands: boolean;
42
+ toolProfile: "balanced" | "full";
39
43
  codexRotate: boolean;
40
44
  cacheTimer: boolean;
41
45
  pinLastPrompt: boolean;
@@ -43,6 +47,7 @@ export interface SettingsConfig {
43
47
  followUpMode: "all" | "one-at-a-time";
44
48
  transport: Transport;
45
49
  thinkingLevel: ThinkingLevel;
50
+ anthropicAdaptiveByDefault: boolean;
46
51
  availableThinkingLevels: ThinkingLevel[];
47
52
  currentTheme: string;
48
53
  availableThemes: string[];
@@ -84,6 +89,8 @@ export interface SettingsCallbacks {
84
89
  onPlanModeReasoningModelChange: (modelRef: string) => void;
85
90
  onPlanModeReviewModelChange: (modelRef: string) => void;
86
91
  onPlanModeCodingModelChange: (modelRef: string) => void;
92
+ onAutoSuggestPlanModeChange: (enabled: boolean) => void;
93
+ onAutoSwitchPlanModelChange: (enabled: boolean) => void;
87
94
  onAutoDreamChange: (enabled: boolean) => void;
88
95
  onAutoMemoryChange: (enabled: boolean) => void;
89
96
  onTelegramLiveRelayAutoConnectChange: (enabled: boolean) => void;
@@ -93,6 +100,7 @@ export interface SettingsCallbacks {
93
100
  onAutoResizeImagesChange: (enabled: boolean) => void;
94
101
  onBlockImagesChange: (blocked: boolean) => void;
95
102
  onEnableSkillCommandsChange: (enabled: boolean) => void;
103
+ onToolProfileChange: (profile: "balanced" | "full") => void;
96
104
  onCodexRotateChange: (enabled: boolean) => void;
97
105
  onCacheTimerChange: (enabled: boolean) => void;
98
106
  onPinLastPromptChange: (enabled: boolean) => void;
@@ -100,6 +108,7 @@ export interface SettingsCallbacks {
100
108
  onFollowUpModeChange: (mode: "all" | "one-at-a-time") => void;
101
109
  onTransportChange: (transport: Transport) => void;
102
110
  onThinkingLevelChange: (level: ThinkingLevel) => void;
111
+ onAnthropicAdaptiveByDefaultChange: (enabled: boolean) => void;
103
112
  onThemeChange: (theme: string) => void;
104
113
  onThemePreview?: (theme: string) => void;
105
114
  onThemeAccentChange: (accent: string) => void;
@@ -250,6 +259,20 @@ export class SettingsSelectorComponent extends Container {
250
259
  currentValue: config.planModeCodingModel,
251
260
  submenu: config.planModeCodingModelSubmenu,
252
261
  },
262
+ {
263
+ id: "auto-suggest-plan-mode",
264
+ label: "Auto-suggest plan mode",
265
+ description: "Ask the LLM to suggest plan mode for large/complex tasks",
266
+ currentValue: config.autoSuggestPlanMode ? "true" : "false",
267
+ values: ["true", "false"],
268
+ },
269
+ {
270
+ id: "auto-switch-plan-model",
271
+ label: "OpusPlan mode",
272
+ description: "On plan entry: auto-switch to Plan reasoning model (must be set). On approval: offer new session with Plan coding model + worker agent",
273
+ currentValue: config.autoSwitchPlanModel ? "true" : "false",
274
+ values: ["true", "false"],
275
+ },
253
276
  {
254
277
  id: "steering-mode",
255
278
  label: "Steering mode",
@@ -351,6 +374,13 @@ export class SettingsSelectorComponent extends Container {
351
374
  () => done(),
352
375
  ),
353
376
  },
377
+ {
378
+ id: "anthropic-adaptive-default",
379
+ label: "Anthropic adaptive",
380
+ description: "Default to adaptive thinking for supported Anthropic models",
381
+ currentValue: config.anthropicAdaptiveByDefault ? "true" : "false",
382
+ values: ["true", "false"],
383
+ },
354
384
  {
355
385
  id: "theme",
356
386
  label: "Theme",
@@ -445,9 +475,19 @@ export class SettingsSelectorComponent extends Container {
445
475
  values: ["true", "false"],
446
476
  });
447
477
 
448
- // Hardware cursor toggle (insert after skill-commands)
478
+ // Tool profile selector (insert after skill-commands)
449
479
  const skillCommandsIndex = items.findIndex((item) => item.id === "skill-commands");
450
480
  items.splice(skillCommandsIndex + 1, 0, {
481
+ id: "tool-profile",
482
+ label: "Tool profile",
483
+ description: "Choose the default active tool set: balanced for day-to-day coding, full to enable all available tools",
484
+ currentValue: config.toolProfile,
485
+ values: ["balanced", "full"],
486
+ });
487
+
488
+ // Codex rotate toggle (insert after tool-profile)
489
+ const toolProfileIndex = items.findIndex((item) => item.id === "tool-profile");
490
+ items.splice(toolProfileIndex + 1, 0, {
451
491
  id: "codex-rotate",
452
492
  label: "Codex rotate",
453
493
  description: "Enable automatic Codex account rotation extension",
@@ -618,6 +658,12 @@ export class SettingsSelectorComponent extends Container {
618
658
  case "plan-mode-coding-model":
619
659
  callbacks.onPlanModeCodingModelChange(newValue);
620
660
  break;
661
+ case "auto-suggest-plan-mode":
662
+ callbacks.onAutoSuggestPlanModeChange(newValue === "true");
663
+ break;
664
+ case "auto-switch-plan-model":
665
+ callbacks.onAutoSwitchPlanModelChange(newValue === "true");
666
+ break;
621
667
  case "show-images":
622
668
  callbacks.onShowImagesChange(newValue === "true");
623
669
  break;
@@ -630,6 +676,9 @@ export class SettingsSelectorComponent extends Container {
630
676
  case "skill-commands":
631
677
  callbacks.onEnableSkillCommandsChange(newValue === "true");
632
678
  break;
679
+ case "tool-profile":
680
+ callbacks.onToolProfileChange(newValue as "balanced" | "full");
681
+ break;
633
682
  case "codex-rotate":
634
683
  callbacks.onCodexRotateChange(newValue === "true");
635
684
  break;
@@ -660,6 +709,9 @@ export class SettingsSelectorComponent extends Container {
660
709
  case "transport":
661
710
  callbacks.onTransportChange(newValue as Transport);
662
711
  break;
712
+ case "anthropic-adaptive-default":
713
+ callbacks.onAnthropicAdaptiveByDefaultChange(newValue === "true");
714
+ break;
663
715
  case "hide-thinking":
664
716
  callbacks.onHideThinkingBlockChange(newValue === "true");
665
717
  break;
@@ -5,6 +5,7 @@ import { DynamicBorder } from "./dynamic-border.js";
5
5
 
6
6
  const LEVEL_DESCRIPTIONS: Record<ThinkingLevel, string> = {
7
7
  off: "No reasoning",
8
+ adaptive: "Claude decides when and how much to think (Claude 4.6+ only)",
8
9
  minimal: "Very brief reasoning (~1k tokens)",
9
10
  low: "Light reasoning (~2k tokens)",
10
11
  medium: "Moderate reasoning (~8k tokens)",
@@ -59,6 +59,25 @@ function str(value: unknown): string | null {
59
59
  return null; // Invalid type
60
60
  }
61
61
 
62
+ /**
63
+ * Returns true if the bash output indicates a privilege escalation failure
64
+ * that the agent process cannot resolve (sudo blocked, EPERM on root-owned files, etc.).
65
+ */
66
+ function isBashPrivilegeError(output: string, command: string): boolean {
67
+ // Already handled by OS sandbox-policy path
68
+ if (/sandbox blocked this operation/i.test(output)) return false;
69
+ // sudo explicitly rejected by OS
70
+ if (/sudo.*operation not permitted/i.test(output)) return true;
71
+ if (/command exited with code 126/i.test(output) && /sudo/i.test(output)) return true;
72
+ // npm / other tools hitting root-owned cache files
73
+ if (/cache folder contains root-owned files/i.test(output)) return true;
74
+ // Generic EPERM when command involves sudo
75
+ if (/EPERM|operation not permitted/i.test(output) && /sudo/i.test(command)) return true;
76
+ // Permission denied when command involves sudo
77
+ if (/permission denied/i.test(output) && /sudo/i.test(command)) return true;
78
+ return false;
79
+ }
80
+
62
81
  export interface ToolExecutionOptions {
63
82
  showImages?: boolean; // default: true (only used if terminal supports images)
64
83
  renderMode?: "minimal" | "normal";
@@ -489,6 +508,17 @@ export class ToolExecutionComponent extends Container {
489
508
  if (resultComponent !== undefined) {
490
509
  this.contentBox.addChild(resultComponent);
491
510
  customRendererHasContent = true;
511
+ // bg_shell: show /bg hint after start/list/digest actions
512
+ if (this.toolName === 'bg_shell' && this.result && !this.isPartial) {
513
+ const details = this.result.details as Record<string, unknown> | undefined;
514
+ const action = details?.action as string | undefined;
515
+ if (action === 'start' || action === 'list' || action === 'digest') {
516
+ this.contentBox.addChild(new Text(
517
+ '\n' + theme.fg('muted', 'Tip: run /bg to manage background processes'),
518
+ 0, 0,
519
+ ));
520
+ }
521
+ }
492
522
  }
493
523
  }
494
524
  } catch {
@@ -673,6 +703,17 @@ export class ToolExecutionComponent extends Container {
673
703
  }
674
704
  body.addChild(new Text(`\n${theme.fg("warning", `[${warnings.join(". ")}]`)}`, 0, 0));
675
705
  }
706
+
707
+ // Privilege escalation error — agent cannot sudo, show manual step callout
708
+ if (this.result.isError && command !== null && isBashPrivilegeError(output, command)) {
709
+ body.addChild(
710
+ new Text(
711
+ `\n${theme.fg("warning", "⚠ This command requires elevated privileges the agent cannot use.")}\n${theme.fg("muted", `Run it manually in your terminal:\n ${command}`)}`,
712
+ 0,
713
+ 0,
714
+ ),
715
+ );
716
+ }
676
717
  }
677
718
  }
678
719