(
input: FileEditInput,
{
readFileState,
userModified,
updateFileHistoryState,
dynamicSkillDirTriggers,
},
_,
parentMessage,
)
| 385 | ) |
| 386 | }, |
| 387 | async call( |
| 388 | input: FileEditInput, |
| 389 | { |
| 390 | readFileState, |
| 391 | userModified, |
| 392 | updateFileHistoryState, |
| 393 | dynamicSkillDirTriggers, |
| 394 | }, |
| 395 | _, |
| 396 | parentMessage, |
| 397 | ) { |
| 398 | const { file_path, old_string, new_string, replace_all = false } = input |
| 399 | |
| 400 | // 1. Get current state |
| 401 | const fs = getFsImplementation() |
| 402 | const absoluteFilePath = expandPath(file_path) |
| 403 | |
| 404 | // Discover skills from this file's path (fire-and-forget, non-blocking) |
| 405 | // Skip in simple mode - no skills available |
| 406 | const cwd = getCwd() |
| 407 | if (!isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)) { |
| 408 | const newSkillDirs = await discoverSkillDirsForPaths( |
| 409 | [absoluteFilePath], |
| 410 | cwd, |
| 411 | ) |
| 412 | if (newSkillDirs.length > 0) { |
| 413 | // Store discovered dirs for attachment display |
| 414 | for (const dir of newSkillDirs) { |
| 415 | dynamicSkillDirTriggers?.add(dir) |
| 416 | } |
| 417 | // Don't await - let skill loading happen in the background |
| 418 | addSkillDirectories(newSkillDirs).catch(() => {}) |
| 419 | } |
| 420 | |
| 421 | // Activate conditional skills whose path patterns match this file |
| 422 | activateConditionalSkillsForPaths([absoluteFilePath], cwd) |
| 423 | } |
| 424 | |
| 425 | await diagnosticTracker.beforeFileEdited(absoluteFilePath) |
| 426 | |
| 427 | // Ensure parent directory exists before the atomic read-modify-write section. |
| 428 | // These awaits must stay OUTSIDE the critical section below — a yield between |
| 429 | // the staleness check and writeTextContent lets concurrent edits interleave. |
| 430 | await fs.mkdir(dirname(absoluteFilePath)) |
| 431 | if (fileHistoryEnabled()) { |
| 432 | // Backup captures pre-edit content — safe to call before the staleness |
| 433 | // check (idempotent v1 backup keyed on content hash; if staleness fails |
| 434 | // later we just have an unused backup, not corrupt state). |
| 435 | await fileHistoryTrackEdit( |
| 436 | updateFileHistoryState, |
| 437 | absoluteFilePath, |
| 438 | parentMessage.uuid, |
| 439 | ) |
| 440 | } |
| 441 | |
| 442 | // 2. Load current state and confirm no changes since last read |
| 443 | // Please avoid async operations between here and writing to disk to preserve atomicity |
| 444 | const { |
nothing calls this directly
no test coverage detected