@@ -211,36 +211,89 @@ async function _runQuery(requestId, prompt, projectPath, model, signal) {
211211 hooks : [
212212 async ( input ) => {
213213 console . log ( "[Phoenix AI] Intercepted Edit tool" ) ;
214- collectedEdits . push ( {
214+ const edit = {
215215 file : input . tool_input . file_path ,
216216 oldText : input . tool_input . old_string ,
217217 newText : input . tool_input . new_string
218- } ) ;
218+ } ;
219+ collectedEdits . push ( edit ) ;
220+ try {
221+ await nodeConnector . execPeer ( "applyEditToBuffer" , edit ) ;
222+ } catch ( err ) {
223+ console . warn ( "[Phoenix AI] Failed to apply edit to buffer:" , err . message ) ;
224+ }
219225 return {
220226 hookSpecificOutput : {
221227 hookEventName : "PreToolUse" ,
222228 permissionDecision : "deny" ,
223- permissionDecisionReason : "Edit delegated to Phoenix editor"
229+ permissionDecisionReason : "Edit applied successfully via Phoenix editor. "
224230 }
225231 } ;
226232 }
227233 ]
228234 } ,
235+ {
236+ matcher : "Read" ,
237+ hooks : [
238+ async ( input ) => {
239+ const filePath = input . tool_input . file_path ;
240+ if ( ! filePath ) {
241+ return undefined ;
242+ }
243+ try {
244+ const result = await nodeConnector . execPeer ( "getFileContent" , { filePath } ) ;
245+ if ( result && result . isDirty && result . content !== null ) {
246+ const MAX_LINES = 2000 ;
247+ const MAX_LINE_LENGTH = 2000 ;
248+ const lines = result . content . split ( "\n" ) ;
249+ const offset = input . tool_input . offset || 0 ;
250+ const limit = input . tool_input . limit || MAX_LINES ;
251+ const selected = lines . slice ( offset , offset + limit ) ;
252+ let formatted = selected . map ( ( line , i ) => {
253+ const truncated = line . length > MAX_LINE_LENGTH
254+ ? line . slice ( 0 , MAX_LINE_LENGTH ) + "..."
255+ : line ;
256+ return String ( offset + i + 1 ) . padStart ( 6 ) + "\t" + truncated ;
257+ } ) . join ( "\n" ) ;
258+ formatted = filePath + " (unsaved editor content, " +
259+ lines . length + " lines total)\n\n" + formatted ;
260+ console . log ( "[Phoenix AI] Serving dirty file content for:" , filePath ) ;
261+ return {
262+ hookSpecificOutput : {
263+ hookEventName : "PreToolUse" ,
264+ permissionDecision : "deny" ,
265+ permissionDecisionReason : formatted
266+ }
267+ } ;
268+ }
269+ } catch ( err ) {
270+ console . warn ( "[Phoenix AI] Failed to check dirty state:" , filePath , err . message ) ;
271+ }
272+ return undefined ;
273+ }
274+ ]
275+ } ,
229276 {
230277 matcher : "Write" ,
231278 hooks : [
232279 async ( input ) => {
233280 console . log ( "[Phoenix AI] Intercepted Write tool" ) ;
234- collectedEdits . push ( {
281+ const edit = {
235282 file : input . tool_input . file_path ,
236283 oldText : null ,
237284 newText : input . tool_input . content
238- } ) ;
285+ } ;
286+ collectedEdits . push ( edit ) ;
287+ try {
288+ await nodeConnector . execPeer ( "applyEditToBuffer" , edit ) ;
289+ } catch ( err ) {
290+ console . warn ( "[Phoenix AI] Failed to apply write to buffer:" , err . message ) ;
291+ }
239292 return {
240293 hookSpecificOutput : {
241294 hookEventName : "PreToolUse" ,
242295 permissionDecision : "deny" ,
243- permissionDecisionReason : "Write delegated to Phoenix editor"
296+ permissionDecisionReason : "Write applied successfully via Phoenix editor. "
244297 }
245298 } ;
246299 }
@@ -279,6 +332,7 @@ async function _runQuery(requestId, prompt, projectPath, model, signal) {
279332 let activeToolIndex = null ;
280333 let activeToolInputJson = "" ;
281334 let toolCounter = 0 ;
335+ let lastToolStreamTime = 0 ;
282336
283337 for await ( const message of result ) {
284338 // Check abort
@@ -310,11 +364,21 @@ async function _runQuery(requestId, prompt, projectPath, model, signal) {
310364 } ) ;
311365 }
312366
313- // Accumulate tool input JSON
367+ // Accumulate tool input JSON and stream preview
314368 if ( event . type === "content_block_delta" &&
315369 event . delta ?. type === "input_json_delta" &&
316370 event . index === activeToolIndex ) {
317371 activeToolInputJson += event . delta . partial_json ;
372+ const now = Date . now ( ) ;
373+ if ( now - lastToolStreamTime >= TEXT_STREAM_THROTTLE_MS ) {
374+ lastToolStreamTime = now ;
375+ nodeConnector . triggerPeer ( "aiToolStream" , {
376+ requestId : requestId ,
377+ toolId : toolCounter ,
378+ toolName : activeToolName ,
379+ partialJson : activeToolInputJson
380+ } ) ;
381+ }
318382 }
319383
320384 // Tool block complete — parse input and send details
0 commit comments