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.
- package/dist/resources/extensions/slash-commands/context.js +15 -8
- package/dist/resources/extensions/slash-commands/index.js +2 -0
- package/dist/resources/extensions/slash-commands/init.js +47 -0
- package/dist/resources/extensions/slash-commands/plan.js +241 -54
- package/dist/resources/extensions/slash-commands/tools.js +47 -21
- package/dist/resources/extensions/subagent/index.js +5 -10
- package/dist/startup-model-validation.d.ts +1 -1
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/types.d.ts +2 -1
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/src/types.ts +2 -1
- package/packages/pi-ai/dist/providers/amazon-bedrock.js +10 -3
- package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +4 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts +2 -2
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +2 -2
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.js +2 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-gemini-cli.js +2 -0
- package/packages/pi-ai/dist/providers/google-gemini-cli.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-vertex.js +2 -0
- package/packages/pi-ai/dist/providers/google-vertex.js.map +1 -1
- package/packages/pi-ai/dist/providers/google.js +2 -0
- package/packages/pi-ai/dist/providers/google.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js +2 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +2 -1
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.js +2 -1
- package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.d.ts +1 -1
- package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.js +6 -2
- package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +1 -1
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/src/providers/amazon-bedrock.ts +11 -4
- package/packages/pi-ai/src/providers/anthropic-shared.ts +5 -2
- package/packages/pi-ai/src/providers/anthropic.ts +2 -2
- package/packages/pi-ai/src/providers/azure-openai-responses.ts +2 -1
- package/packages/pi-ai/src/providers/google-gemini-cli.ts +3 -1
- package/packages/pi-ai/src/providers/google-vertex.ts +3 -1
- package/packages/pi-ai/src/providers/google.ts +3 -1
- package/packages/pi-ai/src/providers/openai-codex-responses.ts +2 -1
- package/packages/pi-ai/src/providers/openai-completions.ts +2 -1
- package/packages/pi-ai/src/providers/openai-responses.ts +2 -1
- package/packages/pi-ai/src/providers/simple-options.ts +5 -3
- package/packages/pi-ai/src/types.ts +1 -1
- package/packages/pi-coding-agent/dist/cli/args.js +1 -1
- package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +10 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +57 -20
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/classifier-service.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/classifier-service.js +34 -61
- package/packages/pi-coding-agent/dist/core/classifier-service.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/utils.js +3 -1
- package/packages/pi-coding-agent/dist/core/compaction/utils.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader-lsd-md.test.js +59 -7
- package/packages/pi-coding-agent/dist/core/resource-loader-lsd-md.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +4 -4
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +19 -20
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.test.js +80 -0
- package/packages/pi-coding-agent/dist/core/sdk.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +15 -5
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +31 -5
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +6 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +28 -68
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash-interceptor.js +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash-interceptor.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash-interceptor.test.js +5 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-interceptor.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +28 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +8 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +44 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/thinking-selector.js +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +36 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +41 -5
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/cli/args.ts +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +62 -19
- package/packages/pi-coding-agent/src/core/classifier-service.ts +35 -63
- package/packages/pi-coding-agent/src/core/compaction/utils.ts +3 -1
- package/packages/pi-coding-agent/src/core/resource-loader-lsd-md.test.ts +67 -7
- package/packages/pi-coding-agent/src/core/resource-loader.ts +4 -4
- package/packages/pi-coding-agent/src/core/sdk.test.ts +100 -0
- package/packages/pi-coding-agent/src/core/sdk.ts +24 -21
- package/packages/pi-coding-agent/src/core/settings-manager.ts +42 -8
- package/packages/pi-coding-agent/src/core/system-prompt.ts +39 -82
- package/packages/pi-coding-agent/src/core/tools/bash-interceptor.test.ts +6 -0
- package/packages/pi-coding-agent/src/core/tools/bash-interceptor.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/bash-execution.ts +26 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +53 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/thinking-selector.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +41 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +50 -7
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +3 -1
- package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.js +2 -0
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/slash-commands/context.ts +15 -8
- package/src/resources/extensions/slash-commands/index.ts +2 -0
- package/src/resources/extensions/slash-commands/init.ts +55 -0
- package/src/resources/extensions/slash-commands/plan.ts +277 -55
- package/src/resources/extensions/slash-commands/tools.ts +47 -21
- 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; //
|
|
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
|
-
|
|
1070
|
-
|
|
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
|
-
|
|
1074
|
-
this.setGlobalSetting("
|
|
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
|
-
/**
|
|
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
|
-
//
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
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("
|
|
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("
|
|
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
|
-
|
|
213
|
-
|
|
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
|
-
|
|
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: `
|
|
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
|
-
//
|
|
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
|
|