@oh-my-pi/cli 0.3.0 → 0.4.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 (48) hide show
  1. package/.editorconfig +14 -0
  2. package/.prettierrc +6 -0
  3. package/README.md +65 -71
  4. package/bun.lock +13 -10
  5. package/dist/cli.js +114 -34
  6. package/dist/commands/config.d.ts.map +1 -1
  7. package/dist/commands/features.d.ts.map +1 -1
  8. package/dist/commands/install.d.ts.map +1 -1
  9. package/dist/commands/list.d.ts.map +1 -1
  10. package/dist/commands/uninstall.d.ts.map +1 -1
  11. package/dist/commands/update.d.ts.map +1 -1
  12. package/dist/runtime.d.ts.map +1 -1
  13. package/dist/symlinks.d.ts +1 -0
  14. package/dist/symlinks.d.ts.map +1 -1
  15. package/package.json +11 -4
  16. package/plugins/exa/README.md +44 -38
  17. package/plugins/exa/package.json +44 -11
  18. package/plugins/exa/tools/exa/company.ts +24 -13
  19. package/plugins/exa/tools/exa/index.ts +43 -34
  20. package/plugins/exa/tools/exa/linkedin.ts +24 -13
  21. package/plugins/exa/tools/exa/researcher.ts +26 -18
  22. package/plugins/exa/tools/exa/search.ts +31 -20
  23. package/plugins/exa/tools/exa/shared.ts +182 -156
  24. package/plugins/exa/tools/exa/websets.ts +39 -28
  25. package/plugins/metal-theme/package.json +13 -4
  26. package/plugins/subagents/README.md +3 -0
  27. package/plugins/subagents/agents/explore.md +11 -0
  28. package/plugins/subagents/agents/planner.md +3 -0
  29. package/plugins/subagents/agents/reviewer.md +6 -0
  30. package/plugins/subagents/agents/task.md +8 -1
  31. package/plugins/subagents/commands/architect-plan.md +1 -0
  32. package/plugins/subagents/commands/implement-with-critic.md +1 -0
  33. package/plugins/subagents/commands/implement.md +1 -0
  34. package/plugins/subagents/package.json +43 -11
  35. package/plugins/subagents/tools/task/index.ts +1089 -861
  36. package/plugins/user-prompt/README.md +22 -66
  37. package/plugins/user-prompt/package.json +15 -4
  38. package/plugins/user-prompt/tools/user-prompt/index.ts +185 -157
  39. package/scripts/bump-version.sh +10 -13
  40. package/src/cli.ts +1 -1
  41. package/src/commands/config.ts +21 -6
  42. package/src/commands/features.ts +49 -12
  43. package/src/commands/install.ts +91 -24
  44. package/src/commands/list.ts +14 -3
  45. package/src/commands/uninstall.ts +4 -1
  46. package/src/commands/update.ts +6 -1
  47. package/src/runtime.ts +3 -10
  48. package/src/symlinks.ts +12 -7
@@ -13,218 +13,244 @@ export const EXA_MCP_URL = "https://mcp.exa.ai/mcp";
13
13
  export const WEBSETS_MCP_URL = "https://websetsmcp.exa.ai/mcp";
14
14
 
15
15
  export interface MCPTool {
16
- name: string;
17
- description: string;
18
- inputSchema: TSchema;
16
+ name: string;
17
+ description: string;
18
+ inputSchema: TSchema;
19
19
  }
20
20
 
21
21
  interface MCPToolsResponse {
22
- result?: {
23
- tools: MCPTool[];
24
- };
25
- error?: {
26
- code: number;
27
- message: string;
28
- };
22
+ result?: {
23
+ tools: MCPTool[];
24
+ };
25
+ error?: {
26
+ code: number;
27
+ message: string;
28
+ };
29
29
  }
30
30
 
31
31
  /**
32
32
  * Parse a .env file and return key-value pairs
33
33
  */
34
34
  function parseEnvFile(filePath: string): Record<string, string> {
35
- const result: Record<string, string> = {};
36
- if (!fs.existsSync(filePath)) return result;
37
-
38
- try {
39
- const content = fs.readFileSync(filePath, "utf-8");
40
- for (const line of content.split("\n")) {
41
- const trimmed = line.trim();
42
- if (!trimmed || trimmed.startsWith("#")) continue;
43
-
44
- const eqIndex = trimmed.indexOf("=");
45
- if (eqIndex === -1) continue;
46
-
47
- const key = trimmed.slice(0, eqIndex).trim();
48
- let value = trimmed.slice(eqIndex + 1).trim();
49
-
50
- // Remove surrounding quotes
51
- if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
52
- value = value.slice(1, -1);
53
- }
54
-
55
- result[key] = value;
35
+ const result: Record<string, string> = {};
36
+ if (!fs.existsSync(filePath)) return result;
37
+
38
+ try {
39
+ const content = fs.readFileSync(filePath, "utf-8");
40
+ for (const line of content.split("\n")) {
41
+ const trimmed = line.trim();
42
+ if (!trimmed || trimmed.startsWith("#")) continue;
43
+
44
+ const eqIndex = trimmed.indexOf("=");
45
+ if (eqIndex === -1) continue;
46
+
47
+ const key = trimmed.slice(0, eqIndex).trim();
48
+ let value = trimmed.slice(eqIndex + 1).trim();
49
+
50
+ // Remove surrounding quotes
51
+ if (
52
+ (value.startsWith('"') && value.endsWith('"')) ||
53
+ (value.startsWith("'") && value.endsWith("'"))
54
+ ) {
55
+ value = value.slice(1, -1);
56
56
  }
57
- } catch {
58
- // Ignore read errors
59
- }
60
57
 
61
- return result;
58
+ result[key] = value;
59
+ }
60
+ } catch {
61
+ // Ignore read errors
62
+ }
63
+
64
+ return result;
62
65
  }
63
66
 
64
67
  /**
65
68
  * Find EXA_API_KEY from environment or .env files
66
69
  */
67
70
  export function findApiKey(): string | null {
68
- // 1. Check environment variable
69
- if (process.env.EXA_API_KEY) {
70
- return process.env.EXA_API_KEY;
71
- }
72
-
73
- // 2. Check .env in current directory
74
- const localEnv = parseEnvFile(path.join(process.cwd(), ".env"));
75
- if (localEnv.EXA_API_KEY) {
76
- return localEnv.EXA_API_KEY;
77
- }
78
-
79
- // 3. Check ~/.env
80
- const homeEnv = parseEnvFile(path.join(os.homedir(), ".env"));
81
- if (homeEnv.EXA_API_KEY) {
82
- return homeEnv.EXA_API_KEY;
83
- }
84
-
85
- return null;
71
+ // 1. Check environment variable
72
+ if (process.env.EXA_API_KEY) {
73
+ return process.env.EXA_API_KEY;
74
+ }
75
+
76
+ // 2. Check .env in current directory
77
+ const localEnv = parseEnvFile(path.join(process.cwd(), ".env"));
78
+ if (localEnv.EXA_API_KEY) {
79
+ return localEnv.EXA_API_KEY;
80
+ }
81
+
82
+ // 3. Check ~/.env
83
+ const homeEnv = parseEnvFile(path.join(os.homedir(), ".env"));
84
+ if (homeEnv.EXA_API_KEY) {
85
+ return homeEnv.EXA_API_KEY;
86
+ }
87
+
88
+ return null;
86
89
  }
87
90
 
88
91
  /**
89
92
  * Call an MCP server endpoint
90
93
  */
91
- async function callMCP(url: string, method: string, params?: Record<string, unknown>): Promise<unknown> {
92
- const body = {
93
- jsonrpc: "2.0",
94
- method,
95
- params: params ?? {},
96
- id: 1,
97
- };
98
-
99
- const response = await fetch(url, {
100
- method: "POST",
101
- headers: {
102
- "Content-Type": "application/json",
103
- Accept: "application/json, text/event-stream",
104
- },
105
- body: JSON.stringify(body),
106
- });
107
-
108
- const text = await response.text();
109
-
110
- // Parse SSE response format
111
- let jsonData: string | null = null;
112
- for (const line of text.split("\n")) {
113
- if (line.startsWith("data: ")) {
114
- jsonData = line.slice(6);
115
- break;
116
- }
117
- }
118
-
119
- if (!jsonData) {
120
- // Try parsing as plain JSON
121
- try {
122
- return JSON.parse(text);
123
- } catch {
124
- throw new Error(`Failed to parse MCP response: ${text.slice(0, 500)}`);
125
- }
126
- }
127
-
128
- return JSON.parse(jsonData);
94
+ async function callMCP(
95
+ url: string,
96
+ method: string,
97
+ params?: Record<string, unknown>,
98
+ ): Promise<unknown> {
99
+ const body = {
100
+ jsonrpc: "2.0",
101
+ method,
102
+ params: params ?? {},
103
+ id: 1,
104
+ };
105
+
106
+ const response = await fetch(url, {
107
+ method: "POST",
108
+ headers: {
109
+ "Content-Type": "application/json",
110
+ Accept: "application/json, text/event-stream",
111
+ },
112
+ body: JSON.stringify(body),
113
+ });
114
+
115
+ const text = await response.text();
116
+
117
+ // Parse SSE response format
118
+ let jsonData: string | null = null;
119
+ for (const line of text.split("\n")) {
120
+ if (line.startsWith("data: ")) {
121
+ jsonData = line.slice(6);
122
+ break;
123
+ }
124
+ }
125
+
126
+ if (!jsonData) {
127
+ // Try parsing as plain JSON
128
+ try {
129
+ return JSON.parse(text);
130
+ } catch {
131
+ throw new Error(`Failed to parse MCP response: ${text.slice(0, 500)}`);
132
+ }
133
+ }
134
+
135
+ return JSON.parse(jsonData);
129
136
  }
130
137
 
131
138
  /**
132
139
  * Fetch available tools from Exa MCP server
133
140
  */
134
- export async function fetchExaTools(apiKey: string, toolNames: string[]): Promise<MCPTool[]> {
135
- const url = `${EXA_MCP_URL}?exaApiKey=${apiKey}&tools=${toolNames.join(",")}`;
136
-
137
- try {
138
- const response = (await callMCP(url, "tools/list")) as MCPToolsResponse;
139
- if (response.error) {
140
- throw new Error(response.error.message);
141
- }
142
- return response.result?.tools ?? [];
143
- } catch (error) {
144
- console.error(`Failed to fetch Exa tools:`, error);
145
- return [];
146
- }
141
+ export async function fetchExaTools(
142
+ apiKey: string,
143
+ toolNames: string[],
144
+ ): Promise<MCPTool[]> {
145
+ const url = `${EXA_MCP_URL}?exaApiKey=${apiKey}&tools=${toolNames.join(",")}`;
146
+
147
+ try {
148
+ const response = (await callMCP(url, "tools/list")) as MCPToolsResponse;
149
+ if (response.error) {
150
+ throw new Error(response.error.message);
151
+ }
152
+ return response.result?.tools ?? [];
153
+ } catch (error) {
154
+ console.error(`Failed to fetch Exa tools:`, error);
155
+ return [];
156
+ }
147
157
  }
148
158
 
149
159
  /**
150
160
  * Fetch available tools from Websets MCP server
151
161
  */
152
162
  export async function fetchWebsetsTools(apiKey: string): Promise<MCPTool[]> {
153
- const url = `${WEBSETS_MCP_URL}?exaApiKey=${apiKey}`;
163
+ const url = `${WEBSETS_MCP_URL}?exaApiKey=${apiKey}`;
154
164
 
155
- try {
156
- const response = (await callMCP(url, "tools/list")) as MCPToolsResponse;
157
- if (response.error) {
158
- throw new Error(response.error.message);
159
- }
160
- return response.result?.tools ?? [];
161
- } catch (error) {
162
- console.error(`Failed to fetch Websets tools:`, error);
163
- return [];
164
- }
165
+ try {
166
+ const response = (await callMCP(url, "tools/list")) as MCPToolsResponse;
167
+ if (response.error) {
168
+ throw new Error(response.error.message);
169
+ }
170
+ return response.result?.tools ?? [];
171
+ } catch (error) {
172
+ console.error(`Failed to fetch Websets tools:`, error);
173
+ return [];
174
+ }
165
175
  }
166
176
 
167
177
  /**
168
178
  * Call a tool on Exa MCP server
169
179
  */
170
- export async function callExaTool(apiKey: string, toolNames: string[], toolName: string, args: Record<string, unknown>): Promise<unknown> {
171
- const url = `${EXA_MCP_URL}?exaApiKey=${apiKey}&tools=${toolNames.join(",")}`;
172
- return callMCPTool(url, toolName, args);
180
+ export async function callExaTool(
181
+ apiKey: string,
182
+ toolNames: string[],
183
+ toolName: string,
184
+ args: Record<string, unknown>,
185
+ ): Promise<unknown> {
186
+ const url = `${EXA_MCP_URL}?exaApiKey=${apiKey}&tools=${toolNames.join(",")}`;
187
+ return callMCPTool(url, toolName, args);
173
188
  }
174
189
 
175
190
  /**
176
191
  * Call a tool on Websets MCP server
177
192
  */
178
- export async function callWebsetsTool(apiKey: string, toolName: string, args: Record<string, unknown>): Promise<unknown> {
179
- const url = `${WEBSETS_MCP_URL}?exaApiKey=${apiKey}`;
180
- return callMCPTool(url, toolName, args);
193
+ export async function callWebsetsTool(
194
+ apiKey: string,
195
+ toolName: string,
196
+ args: Record<string, unknown>,
197
+ ): Promise<unknown> {
198
+ const url = `${WEBSETS_MCP_URL}?exaApiKey=${apiKey}`;
199
+ return callMCPTool(url, toolName, args);
181
200
  }
182
201
 
183
202
  /**
184
203
  * Call a tool on an MCP server
185
204
  */
186
- async function callMCPTool(url: string, toolName: string, args: Record<string, unknown>): Promise<unknown> {
187
- const response = (await callMCP(url, "tools/call", {
188
- name: toolName,
189
- arguments: args,
190
- })) as { result?: { content?: Array<{ text?: string }> }; error?: { message: string } };
191
-
192
- if (response.error) {
193
- throw new Error(response.error.message);
194
- }
195
-
196
- // Extract text content from MCP response
197
- const content = response.result?.content;
198
- if (Array.isArray(content)) {
199
- const texts = content.filter((c) => c.text).map((c) => c.text);
200
- if (texts.length === 1) {
201
- // Try to parse as JSON
202
- try {
203
- return JSON.parse(texts[0]!);
204
- } catch {
205
- return texts[0];
206
- }
205
+ async function callMCPTool(
206
+ url: string,
207
+ toolName: string,
208
+ args: Record<string, unknown>,
209
+ ): Promise<unknown> {
210
+ const response = (await callMCP(url, "tools/call", {
211
+ name: toolName,
212
+ arguments: args,
213
+ })) as {
214
+ result?: { content?: Array<{ text?: string }> };
215
+ error?: { message: string };
216
+ };
217
+
218
+ if (response.error) {
219
+ throw new Error(response.error.message);
220
+ }
221
+
222
+ // Extract text content from MCP response
223
+ const content = response.result?.content;
224
+ if (Array.isArray(content)) {
225
+ const texts = content.filter((c) => c.text).map((c) => c.text);
226
+ if (texts.length === 1) {
227
+ // Try to parse as JSON
228
+ try {
229
+ return JSON.parse(texts[0]!);
230
+ } catch {
231
+ return texts[0];
207
232
  }
208
- return texts.join("\n\n");
209
- }
233
+ }
234
+ return texts.join("\n\n");
235
+ }
210
236
 
211
- return response.result;
237
+ return response.result;
212
238
  }
213
239
 
214
240
  /**
215
241
  * Create a tool wrapper for an MCP tool
216
242
  */
217
243
  export function createToolWrapper(
218
- mcpTool: MCPTool,
219
- renamedName: string,
220
- callFn: (toolName: string, args: Record<string, unknown>) => Promise<unknown>
244
+ mcpTool: MCPTool,
245
+ renamedName: string,
246
+ callFn: (toolName: string, args: Record<string, unknown>) => Promise<unknown>,
221
247
  ): CustomAgentTool<TSchema, unknown> {
222
- return {
223
- name: renamedName,
224
- description: mcpTool.description,
225
- parameters: mcpTool.inputSchema,
226
- async execute(args) {
227
- return callFn(mcpTool.name, args as Record<string, unknown>);
228
- },
229
- };
248
+ return {
249
+ name: renamedName,
250
+ description: mcpTool.description,
251
+ parameters: mcpTool.inputSchema,
252
+ async execute(args) {
253
+ return callFn(mcpTool.name, args as Record<string, unknown>);
254
+ },
255
+ };
230
256
  }
@@ -21,42 +21,53 @@
21
21
  */
22
22
 
23
23
  import type { TSchema } from "@sinclair/typebox";
24
- import type { CustomAgentTool, CustomToolFactory, ToolAPI } from "@mariozechner/pi-coding-agent";
25
- import { callWebsetsTool, createToolWrapper, fetchWebsetsTools, findApiKey } from "./shared";
24
+ import type {
25
+ CustomAgentTool,
26
+ CustomToolFactory,
27
+ ToolAPI,
28
+ } from "@mariozechner/pi-coding-agent";
29
+ import {
30
+ callWebsetsTool,
31
+ createToolWrapper,
32
+ fetchWebsetsTools,
33
+ findApiKey,
34
+ } from "./shared";
26
35
 
27
36
  // Tool name mapping: MCP name -> exposed name
28
37
  const NAME_MAP: Record<string, string> = {
29
- "create_webset": "webset_create",
30
- "list_websets": "webset_list",
31
- "get_webset": "webset_get",
32
- "update_webset": "webset_update",
33
- "delete_webset": "webset_delete",
34
- "list_webset_items": "webset_items_list",
35
- "get_item": "webset_item_get",
36
- "create_search": "webset_search_create",
37
- "get_search": "webset_search_get",
38
- "cancel_search": "webset_search_cancel",
39
- "create_enrichment": "webset_enrichment_create",
40
- "get_enrichment": "webset_enrichment_get",
41
- "update_enrichment": "webset_enrichment_update",
42
- "delete_enrichment": "webset_enrichment_delete",
43
- "cancel_enrichment": "webset_enrichment_cancel",
44
- "create_monitor": "webset_monitor_create",
38
+ create_webset: "webset_create",
39
+ list_websets: "webset_list",
40
+ get_webset: "webset_get",
41
+ update_webset: "webset_update",
42
+ delete_webset: "webset_delete",
43
+ list_webset_items: "webset_items_list",
44
+ get_item: "webset_item_get",
45
+ create_search: "webset_search_create",
46
+ get_search: "webset_search_get",
47
+ cancel_search: "webset_search_cancel",
48
+ create_enrichment: "webset_enrichment_create",
49
+ get_enrichment: "webset_enrichment_get",
50
+ update_enrichment: "webset_enrichment_update",
51
+ delete_enrichment: "webset_enrichment_delete",
52
+ cancel_enrichment: "webset_enrichment_cancel",
53
+ create_monitor: "webset_monitor_create",
45
54
  };
46
55
 
47
- const factory: CustomToolFactory = async (_toolApi: ToolAPI): Promise<CustomAgentTool<TSchema, unknown>[] | null> => {
48
- const apiKey = findApiKey();
49
- if (!apiKey) return null;
56
+ const factory: CustomToolFactory = async (
57
+ _toolApi: ToolAPI,
58
+ ): Promise<CustomAgentTool<TSchema, unknown>[] | null> => {
59
+ const apiKey = findApiKey();
60
+ if (!apiKey) return null;
50
61
 
51
- const mcpTools = await fetchWebsetsTools(apiKey);
52
- if (mcpTools.length === 0) return null;
62
+ const mcpTools = await fetchWebsetsTools(apiKey);
63
+ if (mcpTools.length === 0) return null;
53
64
 
54
- const callFn = (toolName: string, args: Record<string, unknown>) =>
55
- callWebsetsTool(apiKey, toolName, args);
65
+ const callFn = (toolName: string, args: Record<string, unknown>) =>
66
+ callWebsetsTool(apiKey, toolName, args);
56
67
 
57
- return mcpTools.map((tool) =>
58
- createToolWrapper(tool, NAME_MAP[tool.name] ?? tool.name, callFn)
59
- );
68
+ return mcpTools.map((tool) =>
69
+ createToolWrapper(tool, NAME_MAP[tool.name] ?? tool.name, callFn),
70
+ );
60
71
  };
61
72
 
62
73
  export default factory;
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "name": "@oh-my-pi/metal-theme",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Metal theme for pi",
5
- "keywords": ["omp-plugin", "theme", "metal"],
5
+ "keywords": [
6
+ "omp-plugin",
7
+ "theme",
8
+ "metal"
9
+ ],
6
10
  "author": "Can Bölük <me@can.ac>",
7
11
  "license": "MIT",
8
12
  "repository": {
@@ -12,8 +16,13 @@
12
16
  },
13
17
  "omp": {
14
18
  "install": [
15
- { "src": "themes/metal.json", "dest": "agent/themes/metal.json" }
19
+ {
20
+ "src": "themes/metal.json",
21
+ "dest": "agent/themes/metal.json"
22
+ }
16
23
  ]
17
24
  },
18
- "files": ["themes"]
25
+ "files": [
26
+ "themes"
27
+ ]
19
28
  }
@@ -11,15 +11,18 @@ omp install oh-my-pi/plugins/subagents
11
11
  ## Contents
12
12
 
13
13
  ### Tool
14
+
14
15
  - `tools/task/index.ts` - The Task tool for launching subagents
15
16
 
16
17
  ### Agents
18
+
17
19
  - `agents/task.md` - General-purpose subagent for delegated tasks
18
20
  - `agents/planner.md` - Software architect for designing implementation plans
19
21
  - `agents/explore.md` - Fast read-only codebase scout
20
22
  - `agents/reviewer.md` - Expert code reviewer
21
23
 
22
24
  ### Commands
25
+
23
26
  - `commands/implement.md` - Implement a feature
24
27
  - `commands/implement-with-critic.md` - Implement with critic review loop
25
28
  - `commands/architect-plan.md` - Create an architecture plan
@@ -9,6 +9,7 @@ You are a file search specialist and codebase scout. Quickly investigate a codeb
9
9
 
10
10
  === CRITICAL: READ-ONLY MODE ===
11
11
  This is a READ-ONLY exploration task. You are STRICTLY PROHIBITED from:
12
+
12
13
  - Creating or modifying files (no Write, Edit, touch, rm, mv, cp)
13
14
  - Creating temporary files anywhere, including /tmp
14
15
  - Using redirect operators (>, >>, |) or heredocs to write files
@@ -17,12 +18,14 @@ This is a READ-ONLY exploration task. You are STRICTLY PROHIBITED from:
17
18
  Your role is EXCLUSIVELY to search and analyze existing code.
18
19
 
19
20
  Your strengths:
21
+
20
22
  - Rapidly finding files using glob patterns
21
23
  - Searching code with powerful regex patterns
22
24
  - Reading and analyzing file contents
23
25
  - Tracing imports and dependencies
24
26
 
25
27
  Guidelines:
28
+
26
29
  - Use glob for broad file pattern matching
27
30
  - Use grep for searching file contents with regex
28
31
  - Use read when you know the specific file path
@@ -32,11 +35,13 @@ Guidelines:
32
35
  - Communicate findings directly as a message—do NOT create output files
33
36
 
34
37
  Thoroughness (infer from task, default medium):
38
+
35
39
  - Quick: Targeted lookups, key files only
36
40
  - Medium: Follow imports, read critical sections
37
41
  - Thorough: Trace all dependencies, check tests/types
38
42
 
39
43
  Strategy:
44
+
40
45
  1. grep/glob to locate relevant code
41
46
  2. Read key sections (not entire files unless small)
42
47
  3. Identify types, interfaces, key functions
@@ -47,15 +52,19 @@ Your output will be passed to an agent who has NOT seen the files you explored.
47
52
  Output format:
48
53
 
49
54
  ## Query
55
+
50
56
  One line summary of what was searched.
51
57
 
52
58
  ## Files Retrieved
59
+
53
60
  List with exact line ranges:
61
+
54
62
  1. `path/to/file.ts` (lines 10-50) - Description of what's here
55
63
  2. `path/to/other.ts` (lines 100-150) - Description
56
64
  3. ...
57
65
 
58
66
  ## Key Code
67
+
59
68
  Critical types, interfaces, or functions (actual code excerpts):
60
69
 
61
70
  ```language
@@ -65,7 +74,9 @@ interface Example {
65
74
  ```
66
75
 
67
76
  ## Architecture
77
+
68
78
  Brief explanation of how the pieces connect.
69
79
 
70
80
  ## Start Here
81
+
71
82
  Which file to look at first and why.
@@ -9,6 +9,7 @@ You are a software architect and planning specialist. Explore the codebase and d
9
9
 
10
10
  === CRITICAL: READ-ONLY MODE ===
11
11
  This is a READ-ONLY planning task. You are STRICTLY PROHIBITED from:
12
+
12
13
  - Creating or modifying files (no Write, Edit, touch, rm, mv, cp)
13
14
  - Creating temporary files anywhere, including /tmp
14
15
  - Using redirect operators (>, >>, |) or heredocs to write files
@@ -43,7 +44,9 @@ Your role is EXCLUSIVELY to explore and plan. You do NOT have access to file edi
43
44
  End your response with:
44
45
 
45
46
  ### Critical Files for Implementation
47
+
46
48
  List 3-5 files most critical for implementing this plan:
49
+
47
50
  - `path/to/file1.ts` - Brief reason (e.g., "Core logic to modify")
48
51
  - `path/to/file2.ts` - Brief reason (e.g., "Interfaces to implement")
49
52
  - `path/to/file3.ts` - Brief reason (e.g., "Pattern to follow")
@@ -18,6 +18,7 @@ You are an expert code reviewer. Analyze code changes and provide thorough revie
18
18
  ## For Implementation Reviews
19
19
 
20
20
  When reviewing implementation output from another agent:
21
+
21
22
  1. Read the files that were changed
22
23
  2. Understand the context and requirements
23
24
  3. Analyze the implementation quality
@@ -34,18 +35,23 @@ When reviewing implementation output from another agent:
34
35
  ## Output Format
35
36
 
36
37
  ### Overview
38
+
37
39
  What the changes do.
38
40
 
39
41
  ### Strengths
42
+
40
43
  What's done well.
41
44
 
42
45
  ### Issues
46
+
43
47
  Problems that should be fixed (with file:line references).
44
48
 
45
49
  ### Suggestions
50
+
46
51
  Improvements to consider (optional, not blocking).
47
52
 
48
53
  ### Verdict
54
+
49
55
  - ✅ **Approve**: Ready to merge/complete
50
56
  - 🔄 **Request Changes**: Issues must be addressed
51
57
  - 💬 **Comment**: Minor suggestions, can proceed