-
-
Notifications
You must be signed in to change notification settings - Fork 570
Load nested workspace instructions lazily #91
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ import { | |
| import { | ||
| contentText, | ||
| countDiffStats, | ||
| instructionContent, | ||
| logFailedToolResponse, | ||
| logToolCall, | ||
| resultOutputSchema, | ||
|
|
@@ -59,7 +60,12 @@ function registerClaudeMutationTools(context: ToolRegistrationContext): void { | |
| async ({ workspaceId, ...input }) => { | ||
| const startedAt = performance.now(); | ||
| const workspace = workspaces.getWorkspace(workspaceId); | ||
| workspaces.resolvePath(workspace, input.path); | ||
| const path = workspaces.resolvePath(workspace, input.path); | ||
| const agentsFiles = await workspaces.loadAgentsFilesForPath( | ||
| workspace, | ||
| path, | ||
| "file", | ||
| ); | ||
|
Comment on lines
+64
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Return instructions when the operation fails. The write, edit, and shell handlers load and cache instructions before the operation. Their Also applies to: 138-142, 227-231 🤖 Prompt for AI Agents |
||
| const response = await writeFileTool(input, { | ||
| cwd: workspace.root, | ||
| root: workspace.root, | ||
|
|
@@ -89,8 +95,9 @@ function registerClaudeMutationTools(context: ToolRegistrationContext): void { | |
|
|
||
| return { | ||
| ...response, | ||
| content: [...response.content, ...instructionContent(agentsFiles, workspace.root)], | ||
| structuredContent: { | ||
| result: contentText(response.content), | ||
| result: contentText([...response.content, ...instructionContent(agentsFiles, workspace.root)]), | ||
| }, | ||
| }; | ||
| }, | ||
|
|
@@ -127,7 +134,12 @@ function registerClaudeMutationTools(context: ToolRegistrationContext): void { | |
| async ({ workspaceId, ...input }) => { | ||
| const startedAt = performance.now(); | ||
| const workspace = workspaces.getWorkspace(workspaceId); | ||
| workspaces.resolvePath(workspace, input.path); | ||
| const path = workspaces.resolvePath(workspace, input.path); | ||
| const agentsFiles = await workspaces.loadAgentsFilesForPath( | ||
| workspace, | ||
| path, | ||
| "file", | ||
| ); | ||
| const response = await editFileTool(input, { | ||
| cwd: workspace.root, | ||
| root: workspace.root, | ||
|
|
@@ -151,7 +163,10 @@ function registerClaudeMutationTools(context: ToolRegistrationContext): void { | |
| response.details?.patch ?? response.details?.diff, | ||
| ); | ||
| const editResultText = `Edited ${input.path} (+${stats.additions} -${stats.removals}).`; | ||
| const editContent = [textBlock(editResultText)]; | ||
| const editContent = [ | ||
| textBlock(editResultText), | ||
| ...instructionContent(agentsFiles, workspace.root), | ||
| ]; | ||
| logToolCall(config, { | ||
| tool: toolNames.edit, | ||
| workspaceId, | ||
|
|
@@ -209,6 +224,11 @@ function registerShellTool(context: ToolRegistrationContext): void { | |
| workspace, | ||
| workingDirectory, | ||
| ); | ||
| const agentsFiles = await workspaces.loadAgentsFilesForPath( | ||
| workspace, | ||
| cwd, | ||
| "directory", | ||
| ); | ||
| const response = await runShellTool(input, { | ||
| cwd, | ||
| root: workspace.root, | ||
|
|
@@ -242,8 +262,9 @@ function registerShellTool(context: ToolRegistrationContext): void { | |
|
|
||
| return { | ||
| ...response, | ||
| content: [...response.content, ...instructionContent(agentsFiles, workspace.root)], | ||
| structuredContent: { | ||
| result: contentText(response.content), | ||
| result: contentText([...response.content, ...instructionContent(agentsFiles, workspace.root)]), | ||
| }, | ||
| }; | ||
| }, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,18 @@ | ||
| import * as z from "zod/v4"; | ||
| import { applyPatch } from "../apply-patch.js"; | ||
| import type { ProcessSnapshot } from "../process-sessions.js"; | ||
| import type { LoadedAgentsFile } from "../workspaces.js"; | ||
| import { | ||
| EDIT_TOOL_ANNOTATIONS, | ||
| SHELL_TOOL_ANNOTATIONS, | ||
| toolNames, | ||
| workspaceIdDescription, | ||
| type ToolContent, | ||
| type ToolRegistrationContext, | ||
| } from "./types.js"; | ||
| import { | ||
| contentText, | ||
| instructionContent, | ||
| resultOutputSchema, | ||
| runLoggedToolOperation, | ||
| textBlock, | ||
|
|
@@ -56,13 +59,16 @@ function processOutputSchema(): z.ZodRawShape { | |
| }); | ||
| } | ||
|
|
||
| function processToolResponse(snapshot: ProcessSnapshot) { | ||
| function processToolResponse( | ||
| snapshot: ProcessSnapshot, | ||
| additionalContent: ToolContent[] = [], | ||
| ) { | ||
| const result = processResult(snapshot); | ||
| const content = [textBlock(result)]; | ||
| const content = [textBlock(result), ...additionalContent]; | ||
| return { | ||
| content, | ||
| structuredContent: { | ||
| result, | ||
| result: contentText(content), | ||
| sessionId: snapshot.sessionId, | ||
| running: snapshot.running, | ||
| exitCode: snapshot.exitCode, | ||
|
|
@@ -105,18 +111,27 @@ function registerApplyPatchTool(context: ToolRegistrationContext): void { | |
| }, | ||
| async ({ workspaceId, patch }) => { | ||
| const startedAt = performance.now(); | ||
| const workspace = workspaces.getWorkspace(workspaceId); | ||
| const applied = await runLoggedToolOperation( | ||
| config, | ||
| { tool: "apply_patch", workspaceId }, | ||
| startedAt, | ||
| async () => { | ||
| const workspace = workspaces.getWorkspace(workspaceId); | ||
| return applyPatch(workspace.root, patch); | ||
| }, | ||
| async () => applyPatch(workspace.root, patch), | ||
| ); | ||
| const agentsFiles: LoadedAgentsFile[] = []; | ||
| for (const file of applied.files) { | ||
| agentsFiles.push(...await workspaces.loadAgentsFilesForPath( | ||
| workspace, | ||
| workspaces.resolvePath(workspace, file.path), | ||
| "file", | ||
|
Comment on lines
+123
to
+126
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Load instruction files for both sides of a move.
🤖 Prompt for AI Agents |
||
| )); | ||
| } | ||
| const paths = applied.files.map((file) => file.path).join(", "); | ||
| const result = `Applied patch to ${applied.files.length} file(s): ${paths}`; | ||
| const content = [textBlock(result)]; | ||
| const content = [ | ||
| textBlock(result), | ||
| ...instructionContent(agentsFiles, workspace.root), | ||
| ]; | ||
|
Comment on lines
+131
to
+134
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win Include instructions in These lines add instruction blocks to 🤖 Prompt for AI Agents |
||
|
|
||
| return { | ||
| content, | ||
|
|
@@ -200,6 +215,13 @@ function registerCodexProcessTools(context: ToolRegistrationContext): void { | |
| maxOutputTokens, | ||
| }) => { | ||
| const startedAt = performance.now(); | ||
| const workspace = workspaces.getWorkspace(workspaceId); | ||
| const cwd = workspaces.resolveWorkingDirectory(workspace, workingDirectory); | ||
| const agentsFiles = await workspaces.loadAgentsFilesForPath( | ||
| workspace, | ||
| cwd, | ||
| "directory", | ||
| ); | ||
| const snapshot = await runLoggedToolOperation( | ||
| config, | ||
| { | ||
|
|
@@ -210,13 +232,7 @@ function registerCodexProcessTools(context: ToolRegistrationContext): void { | |
| commandLength: cmd.length, | ||
| }, | ||
| startedAt, | ||
| async () => { | ||
| const workspace = workspaces.getWorkspace(workspaceId); | ||
| const cwd = workspaces.resolveWorkingDirectory( | ||
| workspace, | ||
| workingDirectory, | ||
| ); | ||
| return processSessions.start({ | ||
| async () => processSessions.start({ | ||
| workspaceId, | ||
| command: cmd, | ||
| cwd, | ||
|
|
@@ -226,11 +242,13 @@ function registerCodexProcessTools(context: ToolRegistrationContext): void { | |
| rows, | ||
| yieldTimeMs, | ||
| maxOutputTokens, | ||
| }); | ||
| }, | ||
| }), | ||
| ); | ||
|
|
||
| return processToolResponse(snapshot); | ||
| return processToolResponse( | ||
| snapshot, | ||
| instructionContent(agentsFiles, workspace.root), | ||
| ); | ||
| }, | ||
| ); | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Do not cache instructions before a failed read.
These lines mark newly found instruction files as loaded before
readFileToolcan returnisError. If the first read in a nested directory fails, the handler returns no instruction content. A later successful read suppresses those instructions as already loaded.Load and record the files only after a successful read, or restore the loader state on the error path. Add a regression test for a failed read followed by a successful read in the same directory.
🤖 Prompt for AI Agents