@oh-my-pi/pi-coding-agent 4.2.0 → 4.2.2
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/CHANGELOG.md +46 -0
- package/docs/sdk.md +5 -5
- package/examples/sdk/10-settings.ts +2 -2
- package/package.json +5 -5
- package/src/capability/fs.ts +90 -0
- package/src/capability/index.ts +41 -227
- package/src/capability/types.ts +1 -11
- package/src/cli/args.ts +4 -0
- package/src/core/agent-session.ts +7 -7
- package/src/core/agent-storage.ts +50 -0
- package/src/core/auth-storage.ts +102 -3
- package/src/core/bash-executor.ts +1 -1
- package/src/core/custom-tools/loader.ts +2 -2
- package/src/core/export-html/index.ts +1 -33
- package/src/core/extensions/loader.ts +2 -2
- package/src/core/extensions/types.ts +1 -1
- package/src/core/hooks/loader.ts +2 -2
- package/src/core/mcp/config.ts +2 -2
- package/src/core/model-registry.ts +46 -0
- package/src/core/sdk.ts +37 -29
- package/src/core/settings-manager.ts +152 -135
- package/src/core/skills.ts +72 -51
- package/src/core/slash-commands.ts +3 -3
- package/src/core/system-prompt.ts +52 -10
- package/src/core/tools/complete.ts +5 -2
- package/src/core/tools/edit.ts +7 -4
- package/src/core/tools/index.test.ts +16 -0
- package/src/core/tools/index.ts +21 -8
- package/src/core/tools/lsp/index.ts +4 -1
- package/src/core/tools/ssh.ts +6 -6
- package/src/core/tools/task/commands.ts +3 -9
- package/src/core/tools/task/executor.ts +88 -3
- package/src/core/tools/task/index.ts +4 -0
- package/src/core/tools/task/model-resolver.ts +10 -7
- package/src/core/tools/task/worker-protocol.ts +48 -2
- package/src/core/tools/task/worker.ts +152 -7
- package/src/core/tools/write.ts +7 -4
- package/src/discovery/agents-md.ts +13 -19
- package/src/discovery/builtin.ts +368 -293
- package/src/discovery/claude.ts +183 -345
- package/src/discovery/cline.ts +30 -10
- package/src/discovery/codex.ts +188 -272
- package/src/discovery/cursor.ts +106 -121
- package/src/discovery/gemini.ts +72 -97
- package/src/discovery/github.ts +7 -10
- package/src/discovery/helpers.ts +114 -57
- package/src/discovery/index.ts +1 -2
- package/src/discovery/mcp-json.ts +15 -18
- package/src/discovery/ssh.ts +9 -17
- package/src/discovery/vscode.ts +10 -5
- package/src/discovery/windsurf.ts +52 -86
- package/src/main.ts +5 -1
- package/src/modes/interactive/components/extensions/extension-dashboard.ts +24 -11
- package/src/modes/interactive/components/extensions/state-manager.ts +19 -15
- package/src/modes/interactive/controllers/selector-controller.ts +9 -5
- package/src/modes/interactive/interactive-mode.ts +22 -15
- package/src/prompts/agents/plan.md +107 -30
- package/src/prompts/agents/task.md +5 -4
- package/src/prompts/system/system-prompt.md +5 -0
- package/src/prompts/tools/task.md +25 -19
- package/src/utils/shell.ts +2 -2
- package/src/prompts/agents/architect-plan.md +0 -10
- package/src/prompts/agents/implement-with-critic.md +0 -11
- package/src/prompts/agents/implement.md +0 -11
|
@@ -33,22 +33,35 @@ import { applyFilter, createInitialState, filterByProvider, refreshState, toggle
|
|
|
33
33
|
import type { DashboardState } from "./types";
|
|
34
34
|
|
|
35
35
|
export class ExtensionDashboard extends Container {
|
|
36
|
-
private state
|
|
37
|
-
private mainList
|
|
38
|
-
private inspector
|
|
36
|
+
private state!: DashboardState;
|
|
37
|
+
private mainList!: ExtensionList;
|
|
38
|
+
private inspector!: InspectorPanel;
|
|
39
39
|
private settingsManager: SettingsManager | null;
|
|
40
40
|
private cwd: string;
|
|
41
41
|
private terminalHeight: number;
|
|
42
42
|
|
|
43
43
|
public onClose?: () => void;
|
|
44
44
|
|
|
45
|
-
constructor(cwd: string, settingsManager: SettingsManager | null
|
|
45
|
+
private constructor(cwd: string, settingsManager: SettingsManager | null, terminalHeight: number) {
|
|
46
46
|
super();
|
|
47
47
|
this.cwd = cwd;
|
|
48
48
|
this.settingsManager = settingsManager;
|
|
49
|
-
this.terminalHeight = terminalHeight
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
this.terminalHeight = terminalHeight;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static async create(
|
|
53
|
+
cwd: string,
|
|
54
|
+
settingsManager: SettingsManager | null = null,
|
|
55
|
+
terminalHeight?: number,
|
|
56
|
+
): Promise<ExtensionDashboard> {
|
|
57
|
+
const dashboard = new ExtensionDashboard(cwd, settingsManager, terminalHeight ?? process.stdout.rows ?? 24);
|
|
58
|
+
await dashboard.init();
|
|
59
|
+
return dashboard;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private async init(): Promise<void> {
|
|
63
|
+
const disabledIds = this.settingsManager?.getDisabledExtensions() ?? [];
|
|
64
|
+
this.state = await createInitialState(this.cwd, disabledIds);
|
|
52
65
|
|
|
53
66
|
// Calculate max visible items based on terminal height
|
|
54
67
|
// Reserve ~10 lines for header, tabs, help text, borders
|
|
@@ -150,7 +163,7 @@ export class ExtensionDashboard extends Container {
|
|
|
150
163
|
|
|
151
164
|
private handleProviderToggle(providerId: string): void {
|
|
152
165
|
toggleProvider(providerId);
|
|
153
|
-
this.refreshFromState();
|
|
166
|
+
void this.refreshFromState();
|
|
154
167
|
}
|
|
155
168
|
|
|
156
169
|
private handleExtensionToggle(extensionId: string, enabled: boolean): void {
|
|
@@ -162,15 +175,15 @@ export class ExtensionDashboard extends Container {
|
|
|
162
175
|
this.settingsManager.disableExtension(extensionId);
|
|
163
176
|
}
|
|
164
177
|
|
|
165
|
-
this.refreshFromState();
|
|
178
|
+
void this.refreshFromState();
|
|
166
179
|
}
|
|
167
180
|
|
|
168
|
-
private refreshFromState(): void {
|
|
181
|
+
private async refreshFromState(): Promise<void> {
|
|
169
182
|
// Remember current tab ID before refresh
|
|
170
183
|
const currentTabId = this.state.tabs[this.state.activeTabIndex]?.id;
|
|
171
184
|
|
|
172
185
|
const disabledIds = this.settingsManager?.getDisabledExtensions() ?? [];
|
|
173
|
-
this.state = refreshState(this.state, this.cwd, disabledIds);
|
|
186
|
+
this.state = await refreshState(this.state, this.cwd, disabledIds);
|
|
174
187
|
|
|
175
188
|
// Find the same tab in the new (re-sorted) list
|
|
176
189
|
if (currentTabId) {
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
enableProvider,
|
|
19
19
|
getAllProvidersInfo,
|
|
20
20
|
isProviderEnabled,
|
|
21
|
-
|
|
21
|
+
loadCapability,
|
|
22
22
|
} from "../../../../discovery";
|
|
23
23
|
import type {
|
|
24
24
|
DashboardState,
|
|
@@ -42,7 +42,7 @@ export interface ExtensionSettingsManager {
|
|
|
42
42
|
/**
|
|
43
43
|
* Load all extensions from all capabilities.
|
|
44
44
|
*/
|
|
45
|
-
export function loadAllExtensions(cwd?: string, disabledIds?: string[]): Extension[] {
|
|
45
|
+
export async function loadAllExtensions(cwd?: string, disabledIds?: string[]): Promise<Extension[]> {
|
|
46
46
|
const extensions: Extension[] = [];
|
|
47
47
|
const disabledExtensions = new Set<string>(disabledIds ?? []);
|
|
48
48
|
|
|
@@ -100,7 +100,7 @@ export function loadAllExtensions(cwd?: string, disabledIds?: string[]): Extensi
|
|
|
100
100
|
|
|
101
101
|
// Load skills
|
|
102
102
|
try {
|
|
103
|
-
const skills =
|
|
103
|
+
const skills = await loadCapability<Skill>("skills", loadOpts);
|
|
104
104
|
addItems(skills.all, "skill", {
|
|
105
105
|
getDescription: (s) => s.frontmatter?.description,
|
|
106
106
|
getTrigger: (s) => s.frontmatter?.globs?.join(", "),
|
|
@@ -111,7 +111,7 @@ export function loadAllExtensions(cwd?: string, disabledIds?: string[]): Extensi
|
|
|
111
111
|
|
|
112
112
|
// Load rules
|
|
113
113
|
try {
|
|
114
|
-
const rules =
|
|
114
|
+
const rules = await loadCapability<Rule>("rules", loadOpts);
|
|
115
115
|
addItems(rules.all, "rule", {
|
|
116
116
|
getDescription: (r) => r.description,
|
|
117
117
|
getTrigger: (r) => r.globs?.join(", ") || (r.alwaysApply ? "always" : undefined),
|
|
@@ -122,7 +122,7 @@ export function loadAllExtensions(cwd?: string, disabledIds?: string[]): Extensi
|
|
|
122
122
|
|
|
123
123
|
// Load custom tools
|
|
124
124
|
try {
|
|
125
|
-
const tools =
|
|
125
|
+
const tools = await loadCapability<CustomTool>("tools", loadOpts);
|
|
126
126
|
addItems(tools.all, "tool", {
|
|
127
127
|
getDescription: (t) => t.description,
|
|
128
128
|
});
|
|
@@ -132,7 +132,7 @@ export function loadAllExtensions(cwd?: string, disabledIds?: string[]): Extensi
|
|
|
132
132
|
|
|
133
133
|
// Load extension modules
|
|
134
134
|
try {
|
|
135
|
-
const modules =
|
|
135
|
+
const modules = await loadCapability<ExtensionModule>("extension-modules", loadOpts);
|
|
136
136
|
const nativeModules = modules.all.filter((module) => module._source.provider === "native");
|
|
137
137
|
addItems(nativeModules, "extension-module");
|
|
138
138
|
} catch {
|
|
@@ -141,7 +141,7 @@ export function loadAllExtensions(cwd?: string, disabledIds?: string[]): Extensi
|
|
|
141
141
|
|
|
142
142
|
// Load MCP servers
|
|
143
143
|
try {
|
|
144
|
-
const mcps =
|
|
144
|
+
const mcps = await loadCapability<MCPServer>("mcps", loadOpts);
|
|
145
145
|
for (const server of mcps.all) {
|
|
146
146
|
const id = makeExtensionId("mcp", server.name);
|
|
147
147
|
const isDisabled = disabledExtensions.has(id);
|
|
@@ -184,7 +184,7 @@ export function loadAllExtensions(cwd?: string, disabledIds?: string[]): Extensi
|
|
|
184
184
|
|
|
185
185
|
// Load prompts
|
|
186
186
|
try {
|
|
187
|
-
const prompts =
|
|
187
|
+
const prompts = await loadCapability<Prompt>("prompts", loadOpts);
|
|
188
188
|
addItems(prompts.all, "prompt", {
|
|
189
189
|
getDescription: () => undefined,
|
|
190
190
|
getTrigger: (p) => `/prompts:${p.name}`,
|
|
@@ -195,7 +195,7 @@ export function loadAllExtensions(cwd?: string, disabledIds?: string[]): Extensi
|
|
|
195
195
|
|
|
196
196
|
// Load slash commands
|
|
197
197
|
try {
|
|
198
|
-
const commands =
|
|
198
|
+
const commands = await loadCapability<SlashCommand>("slash-commands", loadOpts);
|
|
199
199
|
addItems(commands.all, "slash-command", {
|
|
200
200
|
getDescription: () => undefined,
|
|
201
201
|
getTrigger: (c) => `/${c.name}`,
|
|
@@ -206,7 +206,7 @@ export function loadAllExtensions(cwd?: string, disabledIds?: string[]): Extensi
|
|
|
206
206
|
|
|
207
207
|
// Load hooks
|
|
208
208
|
try {
|
|
209
|
-
const hooks =
|
|
209
|
+
const hooks = await loadCapability<Hook>("hooks", loadOpts);
|
|
210
210
|
for (const hook of hooks.all) {
|
|
211
211
|
const id = makeExtensionId("hook", `${hook.type}:${hook.tool}:${hook.name}`);
|
|
212
212
|
const isDisabled = disabledExtensions.has(id);
|
|
@@ -249,7 +249,7 @@ export function loadAllExtensions(cwd?: string, disabledIds?: string[]): Extensi
|
|
|
249
249
|
|
|
250
250
|
// Load context files
|
|
251
251
|
try {
|
|
252
|
-
const contextFiles =
|
|
252
|
+
const contextFiles = await loadCapability<ContextFile>("context-files", loadOpts);
|
|
253
253
|
for (const file of contextFiles.all) {
|
|
254
254
|
// Extract filename from path for display
|
|
255
255
|
const name = file.path.split("/").pop() || file.path;
|
|
@@ -511,8 +511,8 @@ export function filterByProvider(extensions: Extension[], providerId: string): E
|
|
|
511
511
|
/**
|
|
512
512
|
* Create initial dashboard state.
|
|
513
513
|
*/
|
|
514
|
-
export function createInitialState(cwd?: string, disabledIds?: string[]): DashboardState {
|
|
515
|
-
const extensions = loadAllExtensions(cwd, disabledIds);
|
|
514
|
+
export async function createInitialState(cwd?: string, disabledIds?: string[]): Promise<DashboardState> {
|
|
515
|
+
const extensions = await loadAllExtensions(cwd, disabledIds);
|
|
516
516
|
const tabs = buildProviderTabs(extensions);
|
|
517
517
|
const tabFiltered = extensions; // "all" tab by default
|
|
518
518
|
const searchFiltered = tabFiltered;
|
|
@@ -546,8 +546,12 @@ export function toggleProvider(providerId: string): boolean {
|
|
|
546
546
|
/**
|
|
547
547
|
* Refresh state after toggle.
|
|
548
548
|
*/
|
|
549
|
-
export function refreshState(
|
|
550
|
-
|
|
549
|
+
export async function refreshState(
|
|
550
|
+
state: DashboardState,
|
|
551
|
+
cwd?: string,
|
|
552
|
+
disabledIds?: string[],
|
|
553
|
+
): Promise<DashboardState> {
|
|
554
|
+
const extensions = await loadAllExtensions(cwd, disabledIds);
|
|
551
555
|
const tabs = buildProviderTabs(extensions);
|
|
552
556
|
|
|
553
557
|
// Get current provider from tabs
|
|
@@ -2,7 +2,7 @@ import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
|
2
2
|
import type { OAuthProvider } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
4
4
|
import { Input, Loader, Spacer, Text } from "@oh-my-pi/pi-tui";
|
|
5
|
-
import {
|
|
5
|
+
import { getAgentDbPath } from "../../../config";
|
|
6
6
|
import { SessionManager } from "../../../core/session-manager";
|
|
7
7
|
import { setPreferredImageProvider, setPreferredWebSearchProvider } from "../../../core/tools/index";
|
|
8
8
|
import { disableProvider, enableProvider } from "../../../discovery";
|
|
@@ -111,9 +111,13 @@ export class SelectorController {
|
|
|
111
111
|
* Show the Extension Control Center dashboard.
|
|
112
112
|
* Replaces /status with a unified view of all providers and extensions.
|
|
113
113
|
*/
|
|
114
|
-
showExtensionsDashboard(): void {
|
|
114
|
+
async showExtensionsDashboard(): Promise<void> {
|
|
115
|
+
const dashboard = await ExtensionDashboard.create(
|
|
116
|
+
process.cwd(),
|
|
117
|
+
this.ctx.settingsManager,
|
|
118
|
+
this.ctx.ui.terminal.rows,
|
|
119
|
+
);
|
|
115
120
|
this.showSelector((done) => {
|
|
116
|
-
const dashboard = new ExtensionDashboard(process.cwd(), this.ctx.settingsManager, this.ctx.ui.terminal.rows);
|
|
117
121
|
dashboard.onClose = () => {
|
|
118
122
|
done();
|
|
119
123
|
this.ctx.ui.requestRender();
|
|
@@ -546,7 +550,7 @@ export class SelectorController {
|
|
|
546
550
|
),
|
|
547
551
|
);
|
|
548
552
|
this.ctx.chatContainer.addChild(
|
|
549
|
-
new Text(theme.fg("dim", `Credentials saved to ${
|
|
553
|
+
new Text(theme.fg("dim", `Credentials saved to ${getAgentDbPath()}`), 1, 0),
|
|
550
554
|
);
|
|
551
555
|
this.ctx.ui.requestRender();
|
|
552
556
|
} catch (error: unknown) {
|
|
@@ -566,7 +570,7 @@ export class SelectorController {
|
|
|
566
570
|
),
|
|
567
571
|
);
|
|
568
572
|
this.ctx.chatContainer.addChild(
|
|
569
|
-
new Text(theme.fg("dim", `Credentials removed from ${
|
|
573
|
+
new Text(theme.fg("dim", `Credentials removed from ${getAgentDbPath()}`), 1, 0),
|
|
570
574
|
);
|
|
571
575
|
this.ctx.ui.requestRender();
|
|
572
576
|
} catch (error: unknown) {
|
|
@@ -112,6 +112,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
112
112
|
public lastStatusText: Text | undefined = undefined;
|
|
113
113
|
public fileSlashCommands: Set<string> = new Set();
|
|
114
114
|
|
|
115
|
+
private pendingSlashCommands: SlashCommand[] = [];
|
|
115
116
|
private cleanupUnsubscribe?: () => void;
|
|
116
117
|
private readonly version: string;
|
|
117
118
|
private readonly changelogMarkdown: string | undefined;
|
|
@@ -149,6 +150,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
149
150
|
this.statusContainer = new Container();
|
|
150
151
|
this.editor = new CustomEditor(getEditorTheme());
|
|
151
152
|
this.editor.setUseTerminalCursor(true);
|
|
153
|
+
this.editor.onAutocompleteCancel = () => {
|
|
154
|
+
this.ui.requestRender(true);
|
|
155
|
+
};
|
|
152
156
|
try {
|
|
153
157
|
this.historyStorage = HistoryStorage.open();
|
|
154
158
|
this.editor.setHistoryStorage(this.historyStorage);
|
|
@@ -207,14 +211,6 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
207
211
|
{ name: "exit", description: "Exit the application" },
|
|
208
212
|
];
|
|
209
213
|
|
|
210
|
-
// Load and convert file commands to SlashCommand format
|
|
211
|
-
const fileCommands = loadSlashCommands({ cwd: process.cwd() });
|
|
212
|
-
this.fileSlashCommands = new Set(fileCommands.map((cmd) => cmd.name));
|
|
213
|
-
const fileSlashCommands: SlashCommand[] = fileCommands.map((cmd) => ({
|
|
214
|
-
name: cmd.name,
|
|
215
|
-
description: cmd.description,
|
|
216
|
-
}));
|
|
217
|
-
|
|
218
214
|
// Convert hook commands to SlashCommand format
|
|
219
215
|
const hookCommands: SlashCommand[] = (this.session.extensionRunner?.getRegisteredCommands() ?? []).map((cmd) => ({
|
|
220
216
|
name: cmd.name,
|
|
@@ -227,12 +223,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
227
223
|
description: `${loaded.command.description} (${loaded.source})`,
|
|
228
224
|
}));
|
|
229
225
|
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
[...slashCommands, ...fileSlashCommands, ...hookCommands, ...customCommands],
|
|
233
|
-
process.cwd(),
|
|
234
|
-
);
|
|
235
|
-
this.editor.setAutocompleteProvider(autocompleteProvider);
|
|
226
|
+
// Store pending commands for init() where file commands are loaded async
|
|
227
|
+
this.pendingSlashCommands = [...slashCommands, ...hookCommands, ...customCommands];
|
|
236
228
|
|
|
237
229
|
this.uiHelpers = new UiHelpers(this);
|
|
238
230
|
this.voiceManager = new VoiceManager(this);
|
|
@@ -249,6 +241,21 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
249
241
|
// Register session manager flush for signal handlers (SIGINT, SIGTERM, SIGHUP)
|
|
250
242
|
this.cleanupUnsubscribe = registerAsyncCleanup(() => this.sessionManager.flush());
|
|
251
243
|
|
|
244
|
+
// Load and convert file commands to SlashCommand format (async)
|
|
245
|
+
const fileCommands = await loadSlashCommands({ cwd: process.cwd() });
|
|
246
|
+
this.fileSlashCommands = new Set(fileCommands.map((cmd) => cmd.name));
|
|
247
|
+
const fileSlashCommands: SlashCommand[] = fileCommands.map((cmd) => ({
|
|
248
|
+
name: cmd.name,
|
|
249
|
+
description: cmd.description,
|
|
250
|
+
}));
|
|
251
|
+
|
|
252
|
+
// Setup autocomplete with all commands
|
|
253
|
+
const autocompleteProvider = new CombinedAutocompleteProvider(
|
|
254
|
+
[...this.pendingSlashCommands, ...fileSlashCommands],
|
|
255
|
+
process.cwd(),
|
|
256
|
+
);
|
|
257
|
+
this.editor.setAutocompleteProvider(autocompleteProvider);
|
|
258
|
+
|
|
252
259
|
// Get current model info for welcome screen
|
|
253
260
|
const modelName = this.session.model?.name ?? "Unknown";
|
|
254
261
|
const providerName = this.session.model?.provider ?? "Unknown";
|
|
@@ -557,7 +564,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
557
564
|
}
|
|
558
565
|
|
|
559
566
|
showExtensionsDashboard(): void {
|
|
560
|
-
this.selectorController.showExtensionsDashboard();
|
|
567
|
+
void this.selectorController.showExtensionsDashboard();
|
|
561
568
|
}
|
|
562
569
|
|
|
563
570
|
showModelSelector(options?: { temporaryOnly?: boolean }): void {
|
|
@@ -1,54 +1,131 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: plan
|
|
3
|
-
description: Software architect
|
|
3
|
+
description: Software architect for complex multi-file architectural decisions. NOT for simple tasks, single-file changes, reasoning, or tasks completable in <5 tool calls—execute those directly.
|
|
4
4
|
tools: read, grep, find, ls, bash
|
|
5
|
-
|
|
5
|
+
spawns: explore
|
|
6
|
+
model: pi/slow, gpt-5.2-codex, gpt-5.2, codex, gpt
|
|
6
7
|
---
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
<role>Senior software architect producing implementation plans. READ-ONLY — no file modifications, no state changes.</role>
|
|
9
10
|
|
|
10
11
|
=== CRITICAL: READ-ONLY MODE ===
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
You are STRICTLY PROHIBITED from:
|
|
13
13
|
- Creating or modifying files (no Write, Edit, touch, rm, mv, cp)
|
|
14
14
|
- Creating temporary files anywhere, including /tmp
|
|
15
15
|
- Using redirect operators (>, >>, |) or heredocs to write files
|
|
16
16
|
- Running commands that change system state (git add, git commit, npm install, pip install)
|
|
17
|
+
- Use bash ONLY for git status/log/diff; use read/grep/find/ls tools for file and search operations
|
|
18
|
+
|
|
19
|
+
<context>
|
|
20
|
+
Another engineer will execute your plan without re-exploring the codebase. Your plan must be specific enough to implement directly.
|
|
21
|
+
</context>
|
|
22
|
+
|
|
23
|
+
<process>
|
|
24
|
+
## Phase 1: Understand
|
|
25
|
+
|
|
26
|
+
1. Parse the task requirements precisely
|
|
27
|
+
2. Identify ambiguities — list assumptions you're making
|
|
28
|
+
3. Spawn parallel `explore` agents if the task spans multiple areas
|
|
29
|
+
|
|
30
|
+
## Phase 2: Explore
|
|
31
|
+
|
|
32
|
+
Investigate thoroughly before designing:
|
|
33
|
+
|
|
34
|
+
1. Find existing patterns via grep/find
|
|
35
|
+
2. Read key files to understand current architecture
|
|
36
|
+
3. Trace data flow through relevant code paths
|
|
37
|
+
4. Identify types, interfaces, and contracts involved
|
|
38
|
+
5. Note dependencies between components
|
|
39
|
+
|
|
40
|
+
Spawn `explore` agents for independent search areas. Synthesize findings.
|
|
41
|
+
|
|
42
|
+
## Phase 3: Design
|
|
43
|
+
|
|
44
|
+
Create implementation approach:
|
|
45
|
+
|
|
46
|
+
1. List concrete changes required (files, functions, types)
|
|
47
|
+
2. Define the sequence — what depends on what
|
|
48
|
+
3. Identify edge cases and error conditions
|
|
49
|
+
4. Consider alternatives; justify your choice
|
|
50
|
+
5. Note potential pitfalls or tricky parts
|
|
51
|
+
|
|
52
|
+
## Phase 4: Produce Plan
|
|
53
|
+
|
|
54
|
+
Write a plan another engineer can execute without re-exploring the codebase.
|
|
55
|
+
</process>
|
|
56
|
+
|
|
57
|
+
<example>
|
|
58
|
+
## Summary
|
|
59
|
+
What we're building and why (one paragraph).
|
|
60
|
+
|
|
61
|
+
## Changes
|
|
62
|
+
|
|
63
|
+
1. **`path/to/file.ts`** — What to change
|
|
64
|
+
- Specific modifications
|
|
65
|
+
2. **`path/to/other.ts`** — ...
|
|
66
|
+
|
|
67
|
+
## Sequence
|
|
68
|
+
|
|
69
|
+
1. X (no dependencies)
|
|
70
|
+
2. Y (depends on X)
|
|
71
|
+
3. Z (integration)
|
|
72
|
+
|
|
73
|
+
## Edge Cases
|
|
74
|
+
|
|
75
|
+
- Case: How to handle
|
|
76
|
+
|
|
77
|
+
## Verification
|
|
78
|
+
|
|
79
|
+
- [ ] Test command or check
|
|
80
|
+
- [ ] Expected behavior
|
|
81
|
+
|
|
82
|
+
## Critical Files
|
|
83
|
+
|
|
84
|
+
- `path/to/file.ts` (lines 50-120) — Why to read
|
|
85
|
+
</example>
|
|
86
|
+
|
|
87
|
+
<example>
|
|
88
|
+
## Summary
|
|
89
|
+
Add rate limiting to the API gateway to prevent abuse. Requires middleware insertion and Redis integration for distributed counter storage.
|
|
17
90
|
|
|
18
|
-
|
|
91
|
+
## Changes
|
|
19
92
|
|
|
20
|
-
|
|
93
|
+
1. **`src/middleware/rate-limit.ts`** — New file
|
|
94
|
+
- Create `RateLimitMiddleware` class using sliding window algorithm
|
|
95
|
+
- Accept `maxRequests`, `windowMs`, `keyGenerator` options
|
|
96
|
+
2. **`src/gateway/index.ts`** — Wire middleware
|
|
97
|
+
- Import and register before auth middleware (line 45)
|
|
98
|
+
3. **`src/config/redis.ts`** — Add rate limit key prefix
|
|
99
|
+
- Add `RATE_LIMIT_PREFIX` constant
|
|
21
100
|
|
|
22
|
-
|
|
101
|
+
## Sequence
|
|
23
102
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
- Understand the current architecture
|
|
28
|
-
- Identify similar features as reference
|
|
29
|
-
- Trace through relevant code paths
|
|
30
|
-
- Use bash ONLY for git status/log/diff; use read/grep/find/ls tools for file and search operations
|
|
103
|
+
1. `rate-limit.ts` (standalone, no deps)
|
|
104
|
+
2. `redis.ts` (config only)
|
|
105
|
+
3. `gateway/index.ts` (integration)
|
|
31
106
|
|
|
32
|
-
|
|
33
|
-
- Create implementation approach
|
|
34
|
-
- Consider trade-offs and architectural decisions
|
|
35
|
-
- Follow existing patterns where appropriate
|
|
107
|
+
## Edge Cases
|
|
36
108
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
- Identify dependencies and sequencing
|
|
40
|
-
- Anticipate potential challenges
|
|
109
|
+
- Redis unavailable: fail open with warning log
|
|
110
|
+
- IPv6 addresses: normalize before using as key
|
|
41
111
|
|
|
42
|
-
##
|
|
112
|
+
## Verification
|
|
43
113
|
|
|
44
|
-
|
|
114
|
+
- [ ] `curl -X GET localhost:3000/api/test` 100x rapidly → 429 after limit
|
|
115
|
+
- [ ] Redis CLI: `KEYS rate:*` shows entries
|
|
45
116
|
|
|
46
|
-
|
|
117
|
+
## Critical Files
|
|
47
118
|
|
|
48
|
-
|
|
119
|
+
- `src/middleware/auth.ts` (lines 20-50) — Pattern to follow
|
|
120
|
+
- `src/types/middleware.ts` — Interface to implement
|
|
121
|
+
</example>
|
|
49
122
|
|
|
50
|
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
123
|
+
<requirements>
|
|
124
|
+
- Plan must be specific enough to implement without additional exploration
|
|
125
|
+
- Include exact file paths and line ranges where relevant
|
|
126
|
+
- Sequence must respect dependencies
|
|
127
|
+
- Verification must be concrete and testable
|
|
128
|
+
</requirements>
|
|
53
129
|
|
|
130
|
+
Keep going until complete. This matters — get it right.
|
|
54
131
|
REMEMBER: You can ONLY explore and plan. You CANNOT write, edit, or modify any files.
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
You are a worker agent for delegated tasks
|
|
1
|
+
You are a worker agent for delegated tasks. You have FULL access to all tools (edit, write, bash, grep, read, etc.) - use them as needed to complete your task.
|
|
2
|
+
|
|
3
|
+
Finish only the assigned work and return the minimum useful result.
|
|
2
4
|
|
|
3
5
|
Principles:
|
|
4
6
|
|
|
7
|
+
- You CAN and SHOULD make file edits, run commands, and create files when your task requires it.
|
|
5
8
|
- Be concise. No filler, repetition, or tool transcripts.
|
|
6
|
-
- If blocked, ask a single focused question; otherwise proceed autonomously.
|
|
7
9
|
- Prefer narrow search (grep/find) then read only needed ranges.
|
|
8
10
|
- Avoid full-file reads unless necessary.
|
|
9
|
-
-
|
|
11
|
+
- Prefer edits to existing files over creating new ones.
|
|
10
12
|
- NEVER create documentation files (\*.md) unless explicitly requested.
|
|
11
|
-
- Any file paths in your response MUST be absolute.
|
|
12
13
|
- When spawning subagents with the Task tool, include a 5-8 word user-facing description.
|
|
13
14
|
- Include the smallest relevant code snippet when discussing code or config.
|
|
14
15
|
- Follow the main agent's instructions.
|
|
@@ -137,6 +137,11 @@ Before reading any file:
|
|
|
137
137
|
|
|
138
138
|
## Project Integration
|
|
139
139
|
- Follow AGENTS.md by scope: nearest file applies, deeper overrides higher.
|
|
140
|
+
- Do not search for AGENTS.md during execution; use this list as authoritative.
|
|
141
|
+
{{#if agentsMdSearch.files.length}}
|
|
142
|
+
Relevant files are:
|
|
143
|
+
{{#list agentsMdSearch.files join="\n"}}- {{this}}{{/list}}
|
|
144
|
+
{{/if}}
|
|
140
145
|
- Resolve blockers before yielding.
|
|
141
146
|
</instructions>
|
|
142
147
|
|
|
@@ -2,6 +2,13 @@ Launch a new agent to handle complex, multi-step tasks autonomously.
|
|
|
2
2
|
|
|
3
3
|
The Task tool launches specialized agents (workers) that autonomously handle complex tasks. Each agent type has specific capabilities and tools available to it.
|
|
4
4
|
|
|
5
|
+
**CRITICAL: Subagents have NO access to conversation history.** They only see:
|
|
6
|
+
1. Their agent-specific system prompt
|
|
7
|
+
2. The `context` string you provide
|
|
8
|
+
3. The `task` string you provide
|
|
9
|
+
|
|
10
|
+
If you discussed requirements, plans, schemas, or decisions with the user, you MUST include that information in `context`. Subagents cannot see prior messages - they start fresh with only what you explicitly pass them.
|
|
11
|
+
|
|
5
12
|
## Available Agents
|
|
6
13
|
|
|
7
14
|
{{#list agents prefix="- " join="\n"}}
|
|
@@ -26,8 +33,9 @@ The Task tool launches specialized agents (workers) that autonomously handle com
|
|
|
26
33
|
- **Minimize tool chatter**: Avoid repeating large context; use Output tool with output ids for full logs
|
|
27
34
|
- **Structured completion**: If `output` is provided, subagents must call `complete` to finish
|
|
28
35
|
- **Parallelize**: Launch multiple agents concurrently whenever possible
|
|
36
|
+
- **Isolate file scopes**: Assign each task distinct files or directories so agents don't conflict
|
|
29
37
|
- **Results are intermediate data**: Agent findings provide context for YOU to perform actual work. Do not treat agent reports as "task complete" signals.
|
|
30
|
-
- **Stateless invocations**:
|
|
38
|
+
- **Stateless invocations**: Subagents have zero memory of your conversation. Pass ALL relevant context: requirements discussed, decisions made, schemas agreed upon, file paths mentioned. If you reference something from earlier discussion without including it, the subagent will fail.
|
|
31
39
|
- **Trust outputs**: Agent results should generally be trusted
|
|
32
40
|
- **Clarify intent**: Tell the agent whether you expect code changes or just research (search, file reads, web fetches)
|
|
33
41
|
- **Proactive use**: If an agent description says to use it proactively, do so without waiting for explicit user request
|
|
@@ -35,7 +43,7 @@ The Task tool launches specialized agents (workers) that autonomously handle com
|
|
|
35
43
|
## Parameters
|
|
36
44
|
|
|
37
45
|
- `agent`: Agent type to use for all tasks
|
|
38
|
-
- `context`:
|
|
46
|
+
- `context`: **Required context from conversation** - include ALL relevant info: requirements, schemas, decisions, constraints. Subagents cannot see chat history.
|
|
39
47
|
- `model`: (optional) Model override (fuzzy matching, e.g., "sonnet", "opus")
|
|
40
48
|
- `tasks`: Array of `{id, task, description}` - tasks to run in parallel (max {{MAX_PARALLEL_TASKS}}, {{MAX_CONCURRENCY}} concurrent)
|
|
41
49
|
- `id`: Short CamelCase identifier for display (max 20 chars, e.g., "SessionStore", "LspRefactor")
|
|
@@ -46,30 +54,28 @@ The Task tool launches specialized agents (workers) that autonomously handle com
|
|
|
46
54
|
## Example
|
|
47
55
|
|
|
48
56
|
<example>
|
|
49
|
-
user: "
|
|
50
|
-
assistant: I'll
|
|
57
|
+
user: "Looks good, execute the plan"
|
|
58
|
+
assistant: I'll execute the refactoring plan.
|
|
51
59
|
assistant: Uses the Task tool:
|
|
52
60
|
{
|
|
53
|
-
"agent": "
|
|
54
|
-
"context": "
|
|
61
|
+
"agent": "task",
|
|
62
|
+
"context": "Refactoring the auth module into separate concerns.\n\nPlan:\n1. AuthProvider - Extract React context and provider from src/auth/index.tsx\n2. AuthApi - Extract API calls to src/auth/api.ts, use existing fetchJson helper\n3. AuthTypes - Move types to src/auth/types.ts, re-export from index\n\nConstraints:\n- Preserve all existing exports from src/auth/index.tsx\n- Use project's fetchJson (src/utils/http.ts), don't use raw fetch\n- No new dependencies",
|
|
55
63
|
"output": {
|
|
56
64
|
"properties": {
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"file": { "type": "string" },
|
|
61
|
-
"line": { "type": "uint32" },
|
|
62
|
-
"text": { "type": "string" },
|
|
63
|
-
"suggestedKey": { "type": "string" }
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
65
|
+
"summary": { "type": "string" },
|
|
66
|
+
"decisions": { "elements": { "type": "string" } },
|
|
67
|
+
"concerns": { "elements": { "type": "string" } }
|
|
67
68
|
}
|
|
68
69
|
},
|
|
69
70
|
"tasks": [
|
|
70
|
-
{ "id": "
|
|
71
|
-
{ "id": "
|
|
72
|
-
{ "id": "
|
|
71
|
+
{ "id": "AuthProvider", "task": "Execute step 1: Extract AuthProvider and AuthContext", "description": "Extract React context" },
|
|
72
|
+
{ "id": "AuthApi", "task": "Execute step 2: Extract API calls to api.ts", "description": "Extract API layer" },
|
|
73
|
+
{ "id": "AuthTypes", "task": "Execute step 3: Move types to types.ts", "description": "Extract types" }
|
|
73
74
|
]
|
|
74
75
|
}
|
|
75
76
|
</example>
|
|
77
|
+
|
|
78
|
+
Key points:
|
|
79
|
+
- **Plan in context**: The full plan is written once; each task references its step without repeating shared constraints
|
|
80
|
+
- **Parallel execution**: 3 agents run concurrently, each owning one step - no duplicated work
|
|
81
|
+
- **Structured output**: JTD schema ensures consistent reporting across all agents
|
package/src/utils/shell.ts
CHANGED
|
@@ -92,12 +92,12 @@ function buildConfig(shell: string): ShellConfig {
|
|
|
92
92
|
* 3. On Unix: $SHELL if bash/zsh, then fallback paths
|
|
93
93
|
* 4. Fallback: sh
|
|
94
94
|
*/
|
|
95
|
-
export function getShellConfig(): ShellConfig {
|
|
95
|
+
export async function getShellConfig(): Promise<ShellConfig> {
|
|
96
96
|
if (cachedShellConfig) {
|
|
97
97
|
return cachedShellConfig;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
const settings = SettingsManager.create();
|
|
100
|
+
const settings = await SettingsManager.create();
|
|
101
101
|
const customShellPath = settings.getShellPath();
|
|
102
102
|
|
|
103
103
|
// 1. Check user-specified shell path
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Explore gathers context, planner creates implementation plan (no implementation)
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
Use the subagent tool with the chain parameter to execute this workflow:
|
|
6
|
-
|
|
7
|
-
1. First, use the "explore" agent to find all code relevant to: $@
|
|
8
|
-
2. Then, use the "planner" agent to create an implementation plan for "$@" using the context from the previous step (use {previous} placeholder)
|
|
9
|
-
|
|
10
|
-
Execute this as a chain, passing output between steps via {previous}. Do NOT implement - just return the plan.
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Task implements, reviewer reviews, task applies feedback
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
Use the subagent tool with the chain parameter to execute this workflow:
|
|
6
|
-
|
|
7
|
-
1. First, use the "task" agent to implement: $@
|
|
8
|
-
2. Then, use the "reviewer" agent to review the implementation from the previous step (use {previous} placeholder)
|
|
9
|
-
3. Finally, use the "task" agent to apply the feedback from the review (use {previous} placeholder)
|
|
10
|
-
|
|
11
|
-
Execute this as a chain, passing output between steps via {previous}.
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Full implementation workflow - explore gathers context, planner creates plan, task implements
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
Use the subagent tool with the chain parameter to execute this workflow:
|
|
6
|
-
|
|
7
|
-
1. First, use the "explore" agent to find all code relevant to: $@
|
|
8
|
-
2. Then, use the "planner" agent to create an implementation plan for "$@" using the context from the previous step (use {previous} placeholder)
|
|
9
|
-
3. Finally, use the "task" agent to implement the plan from the previous step (use {previous} placeholder)
|
|
10
|
-
|
|
11
|
-
Execute this as a chain, passing output between steps via {previous}.
|