files-git-search
Skill9 starsChanged 4 days agoMITView on GitHub
Disk, git and search patterns in main: atomic writes, the editor saves coming back through the watcher, git paths in the window folder path space, network git and clone, the shared search replace engine and text encodings. Use when creating or reviewing code that writes files, runs git, searches/replaces, or reads text that is compared with an editor tab.
In this repository17 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- app-testingSkill
- claude-panelSkill
- commands-settingsSkill
- containersSkill
- debuggerSkill
- i18nSkill
- lspSkill
- main-ipcSkill
- main-processesSkill
- packaging-releaseSkill
- renderer-componentsSkill
- renderer-editorSkill
- renderer-storeSkill
- renderer-stylesSkill
- renderer-terminalSkill
- workspacesSkill
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 0 are distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 28
Never write a user/state file with a bare writeFile.
- Very specific: line 68
Every git call in git/run.ts is one runCollect (process/runCollect.ts) plus a classifier (runRaw: cancelled / timeout / spawn error / …
- Very specific: line 73
Owned local mode: every command that changes the repo (commit, switch, stash incl.
- Very specific: line 102
Every rg run (ipc/search.ts, files/fileListCache.ts) goes through search/rg.ts runRg (base args, 8 KiB stderr head, exit code 1/2 …
- Very specific: line 105
Search results leave main sorted with comparePaths (shared/pathCompare.ts): rg scans in parallel, so arrival order changes between runs.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Files, git and search
Main owns every disk write and every git/rg process; the renderer never decides a path outside the view folder.
Atomic writes
- Every disk write goes through
writeFileAtomic/writeFileAtomicSync/writeJsonAtomicSync(fs/atomicWrite.ts): hidden.<name>.walph-<12 hex>.tmpin the target's folder (wx), keeps the existingmode, follows a symlink target (the link stays a link;followSymlinks:falsefor thewalphlauncher), removes the tmp on failure, retries EPERM/EBUSY on Windows. Both variantsfsyncthe tmp (handle.sync()/fsyncSync(fd)) before the rename, toleratingEINVAL/ENOTSUP/ENOSYS(filesystems without fsync: logged once, the save continues). Only the async variant also syncs the parent folder after the rename (POSIX, best effort: its errors are ignored); the sync variant runs on the main thread for frequent small state files and skips it. The existing-file info and the mode choice are shared helpers (existingInfo*,pickMode): never duplicate them between the async and sync paths. - That includes the editor's
fs:writeFile(the inode changes: hardlinks stop sharing). It is gated first byassertWritable(fs/writeAccess.ts: inside the view folder, or a filefs:readTextreturned to that view; seemain-ipcSecurity): a new code path that reads a file into a tab must go throughfs:readText, neverreadFile. - The workspace watcher drops those tmp names (
isAtomicTempPathinclassifyBatch) and LSPfileChangesskipsaddof a mirrored doc. - Creating a NEW file (explorer "new file", the settings templates) uses
createFileExclusive(path, data, { mode })(O_EXCL, creates the parent, an occupied target throwsAlreadyExistsError: match the type, not the text). The only rawwriteFile/appendFileinsrc/mainlive infs/atomicWrite.tsand tests. - Never write a user/state file with a bare
writeFile. - Translating a write failure (
EACCES,ENOSPC,EISDIR…) isdescribeWriteError(err, path, { overrides, fallback })(fs/describeWriteError.ts):overridesmaps an errno to a ready message (e.g.ENOENT-> "file gone" in the replace engine),fallback(name, message)formats unknown errors (default: keep the original error). Never write a local errnoswitchfor writes. Reads, listings and creation usedescribeFsError(err, path)(fs/describeFsError.ts, same idea for ENOENT/EEXIST/EACCES…); both return aUserError. - The workspace settings file is written/created through
resolveSettingsWriteTarget(settings/writeSetting.ts): it rejects a workspace-settings directory or file link that leaves the folder before anything is created. - Reading text for the editor (
readTextFile) opens withO_NONBLOCK,fstats the handle (!isFile()is refused: a FIFO/socket would pin a libuv thread) and reads capped (readCapped, shared withreadImageFile).
The editor's own saves
- The editor's own saves come back through the watcher (
WorkspaceChange.files): the renderer compares the disk text with the tab's saved baseline (EOL-normalized) and ignores matches; never treat everychangeas external. - Anything that rewrites the text at save time (whitespace transforms, EditorConfig) runs before
saveDocumenttakes the new baseline (renderer-editorskill, "Save pipeline"); changing the text after it would make the echo look external. .editorconfigfiles are read throughfs.exists+fs.readFile(reads are free;existsfirst so a missing file is not a rejected IPC per folder), walking up from the file's folder untilroot = true; the renderer caches them per folder and drops an entry when the workspace watcher reports that file.
Git paths and network git
- Git handlers are wiring:
ipc/git.tsisownRoot+ a handle table; the work is ingit/ops/{status,history,stash, branches,worktree}.ts(writes take(sender, root, rawArgs), reads take(root, rawArgs);statusalso takessender). Argument checks are ingit/validateArgs.ts(repoRelative, commit hashes…), the repo root ingit/repoTop.ts(overrepoTopCache.ts), the ignored list ingit/ignoredCache.ts(TTL 10 s),HEADdetection ingit/hasHead.tsand the quit sweep ingit/killAll.ts. - The file list for Quick Open is
files/fileListCache.ts(getFileList, one rg run per root shared by concurrent calls;invalidateFileListis called by the tree watcher on structural changes). - Git paths live in the window folder's path space: the repo root comes from
rev-parse --show-cdupresolved against the window folder, never--show-toplevel(it resolves symlinks, e.g. macOS/tmpand/var, and the renderer's paths don't). - Commands taking repo-relative paths run with
cwd= repo root (the folder may be a subfolder). git runs withLC_MESSAGES=CsogitErrors.tscan classify stderr into translated messages; pull/push/sync usessh -o BatchMode=yes(unless the user configured SSH) and an explicit<remote> HEAD:<upstream>refspec. - Code that decides by the exit code (e.g.
claude/gitExclude.ts,check-ignore) usesgitStatusCode(cwd, args, { timeoutMs })fromgit/run.ts(same env asgit(), never rejects,-1= did not run); a git process spawned outsidegit/run.tsis forbidden. - Every git call in
git/run.tsis onerunCollect(process/runCollect.ts) plus a classifier (runRaw: cancelled / timeout / spawn error / overflow / exit code →GitCommandError); there is no second runner. Network git (pull/push/sync, owned per view;git/remote.ts) and clone (git/clone.ts) are process-group leaders (process/spawnGroup.ts): the 10-minute timeout and a view reset (reload, crash, close;onViewReset) kill the whole tree (ssh, git-remote-*), not justgit. - Owned local mode: every command that changes the repo (
commit,switch,stashincl. pop/apply/drop/push,add,restore,rm --cached,reset,update-ref,init) passes{ localOwner: sender }togit()/runGit()(git/run.ts): same process-group path as network git (registryownedProcs,onViewReset, eviction guardhasOwnedGit,killAllOwnedGitatwill-quit) with a 5-minute timeout and the translatedgit.errors.timeout/cancelled; no SSH tweaks. These processes aregraceful(spawnGroup(..., { graceful })): view reset and timeout callproc.terminate()(process/tree.tskillLiveTreeGracefully: SIGTERM to the group /taskkill /T, SIGKILL //Fafter 4 s) so git removesindex.lockand undoes half-done work. Atwill-quitkill(true)cannot wait, so graceful procs get a synchronous SIGTERM and the others (network, docker) SIGKILL. Only read-only plumbing runs without an owner (still a process group, so aliases and filters die with it), with a 60 s timeout and a 64 MiB stdout cap that fails (owned modes truncate at 16 MiB instead). A new mutating command must passlocalOwner. - Git failures are
GitCommandError(git/gitErrors.ts):messageis classified/translated,stderris the raw git output,abortedmarks timeout/cancel. Decide onstderr, never on the message, and never retry-on-catch whenisAbortedGit(err)(statusOnce,unstage). git show/baselinemap to''/nullonly whenisPathNotInRefError(raw stderr,git/gitErrors.ts) matches the "path not in ref" stderr; any other failure is rethrown. Repo-relative paths come frompathTail(top, path, { strict: true }), neverrelative()+startsWith('..')(a file named..foois inside).
Clone
git:clonevalidates withgit/cloneUrl.ts(resolveCloneUrllowercases the scheme;GIT_ALLOW_PROTOCOL=https:sshis case-sensitive;file://only in test mode), shows the destination dialog itself (the renderer never sends a destination), creates the folder with a NON-recursivemkdirso cleanup (cancel, failure, view navigate/destroy,will-quitviarmSync) only ever removes a folder the clone created; one clone per view, slot taken before the dialog.- The SSH-configured check for clone reads only global/system config.
Search replace
- Every
rgrun (ipc/search.ts,files/fileListCache.ts) goes throughsearch/rg.tsrunRg(base args, 8 KiB stderr head, exit code 1/2 semantics,describeRgSpawnError); a search is owned by its view throughsearches.scope(wc). - Search results leave main sorted with
comparePaths(shared/pathCompare.ts): rg scans in parallel, so arrival order changes between runs. Any list of paths that needs a stable cross-OS order usescomparePaths, never<on the raw string (\\and/sort differently against letters). - Search replace engine is shared (
shared/searchReplace.ts): main uses it for disk, the renderer for the preview and dirty tabs. - A match is replaced only if its line still equals
match.text(dirty buffers first go throughrelocateMatches); the JS regex gets rg's Unicode\w/\d/\bviatoUnicodeClasses, and when that's impossible non-ASCII lines are skipped rather than risking different groups. src/shared/*.test.tsrun only in thenodeproject (excluded fromtsconfig.web.json).
Encodings
main/fs/encoding.tsdetects only by BOM (no heuristics; Windows-1252/Latin-1 only when the user chooses, own tables, no ICU).fs.readFile/readTextstrip the BOM; a file tab reads withreadTextand saves withwriteFile(…, tab.encoding), and every other reader that compares with a tab (gitshow/baseline/commitDiffviagitFileText, Claude proposals viareadTextDetailedOrNull,applyWorkspaceEditunloaded files) decodes the same way; a user-chosen encoding (forcedEncodingFor(path)) is passed togit.show/baseline.writeFileencodes before touching the disk (unencodable char → translated error, nothing written); a UTF-16 BOM exempts the null-byte binary check; UTF-16 files get the generic Claudetoolcard (the CLI writes them).search.replacestill refuses non-UTF-8.- The preload gets the home dir via
--walph-home=(additionalArguments). - Toggling the EOL dirties a tab without changing its text (
markFormatDirty/formatDirtyEolindiskSync.ts):replaceDocumentdispatches synchronously intosetDirty, so clear the tab's dirty/format state BEFORE replacing.
Hunks, conflicts and blame
- Stage/unstage by hunk or by selected lines (
git:stageRanges/git:unstageRanges,git/ops/ranges.ts): the renderer sends only repo paths + 1-based line ranges of the new side (working tree for stage, index for unstage); main reads both sides itself (git cat-file blob, disk), picks the target withselectLines(shared/hunkSelection.tsovershared/lineDiff.ts, the same Myers diff as the editor gutter) and builds a unified patch (git/rangePatch.ts) applied withgit apply --cached -through stdin (runGit(..., { input }),localOwner).git applychecks the index preimage, so a stale request fails instead of corrupting the index. Text is handled aslatin1(1 char = 1 byte): any ASCII-compatible encoding and BOMs round-trip; NUL bytes (binary, UTF-16) are refused. Lines compare without their EOL (CRLF = LF) but a missing final newline differs; an LF index never receives CRLF lines. Untracked/conflicted/deleted files fall back to whole-filestage/unstage. - Stage from the editor saves first (main reads the disk); revert never runs git: in a loaded tab it edits the
buffer (
applyTabEdits, undoable, not saved); from the diff editor it edits+saves a clean loaded tab, refuses a dirty one, orreadText+writeFile(atomic, write-access gated) after a confirmation. - Conflicts:
isConflictStatus(shared/gitStatusCodes.ts) is the only porcelain conflict check (Git view "Merge Changes",discardTargets).GitStatus.operationcomes from markers in the git dir (findGitDir, cached like the repo top:rebase-merge/rebase-apply/MERGE_HEAD/CHERRY_PICK_HEAD/REVERT_HEAD);git:abortOperationruns the matching--abort.git:showacceptsOURS/THEIRS(:2:/:3:) for "Compare Changes" (DiffTab.mergeCompare, never persisted). Marker parsing islib/mergeConflicts.ts(pure); the editor extension iseditor/mergeConflicts.ts. - Blame (
git:blame,git/parseBlame.ts):--porcelain, with--contents -(stdin) only when the tab is dirty. The renderer (gitBlameActions.ts) refetches only on HEAD change, save (dirty → clean) or a text change with a clean tab (load/disk reload); between fetcheseditor/gitBlame.tsmaps marks through edits and re-diffs the blamed text against the buffer (debounced), so edited lines become "not committed yet". No blame per keystroke.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

