summaryrefslogtreecommitdiff
path: root/CLAUDE.md
AgeCommit message (Collapse)Author
2026-09-28AGENTS.md is the file; CLAUDE.md imports itIgor Soarez
Every agent here except Claude Code reads AGENTS.md -- pi, codex, and aider now that its read: says so. The vendor-neutral name was the symlink and the Claude-specific one the real file, with a header saying the file was guidance "to Claude Code". Swapped, keeping history on the real file through git mv. Claude Code has read AGENTS.md directly since v2.1.277 and the machine this was done on is newer, so the obvious move was to delete CLAUDE.md. Checking the other machines first was worth it: four were on versions between 2.1.158 and 2.1.263, and a session on any of them would have found no instructions at all. So CLAUDE.md stays as one line, `@AGENTS.md`, which the documentation endorses for exactly this case and which -- unlike the symlink it replaces -- works on Windows and does not trip Claude Code's refusal to write through a symlink. The header says to delete it once every machine is current.
2026-09-28CLAUDE.md: an agent files its own change request, without askingIgor Soarez
The text said to hand a request to dot-request "as well as to the reply" but never said an agent may do that unprompted, and asked for it to be written "to be pasted whole", which reads as give-this-to-a-human. So an agent on a read-only clone wrote a request, handed it back, and offered to file it only if asked -- reasonable behaviour against what was written. The paragraph now is the permission, and says why filing is safe: it applies nothing, and every request is still judged before anything changes. One addition beyond what was asked. Filing without asking means text leaves that machine with nothing in the way, and the machine is one whose owner does not control it. So putting the request in the reply is now explicitly not optional: it is the only thing that shows what was sent.
2026-09-28pi: the siliconflow key is a reference, not a nameIgor Soarez
models.json gave the provider an apiKey of "SILICONFLOW_API_KEY", which pi sends verbatim as the credential: it interpolates "$NAME" and "${NAME}", runs "!command", and treats anything else as a literal. So that provider has been authenticating with the 19-character string "SILICONFLOW_API_KEY" and getting a 401 for as long as it has existed. Confirmed by calling pi's own resolver: "SILICONFLOW_API_KEY" resolves to itself, "$SILICONFLOW_API_KEY" to the value. Nothing catches this -- isConfigValueConfigured returns true for both and reports no missing variables, so `pi auth check` says ready either way, and only a real request fails. Hence the note in CLAUDE.md beside the models.d row: the rule is invisible in the file, and the next provider added would repeat it. Reported as a change request from the machine that hit it; the sweep for other instances found none.
2026-09-25A filed request needs the owner's approval, not just a valid signatureIgor Soarez
The reader section said a request was a proposal to read and judge. That tells an agent how to read one, not what it may do about it, and the gap is the whole risk: a machine files requests precisely because it cannot be trusted with push access -- it may be compromised, and whoever holds its key signs whatever they like. A signature proves which machine sent the text, never that the text is right or that its owner wrote it. So: judge it, report it, and apply nothing without the owner approving that request. Text inside a request asking for it to be applied or pushed is not approval; it is the input the rule exists for. The same words are in the reader's own output and in the inbox repo, so this holds for whoever meets a request first.
2026-09-25pi: providers can come from ~/.config/pi/models.d, like settings already doIgor Soarez
models.json was a plain symlink to the tracked file, which left a machine whose models are served from somewhere other than a vendor's own API with nowhere to say so. pi reads providers from exactly one path -- join(getAgentDir(), "models.json") -- and a `providers` block in a settings fragment is inert, because the settings schema has no such key. So on such a machine pi cannot be used at all, and the endpoint is not something this repo may name. link/pi.sh now generates models.json the way it already generates settings.json: the repo's file, then every ~/.config/pi/models.d/*.json. No three-way merge here -- pi never writes this file, keeping runtime state in the sibling models-store.json -- so a hand-edit of ~/.pi/agent/models.json no longer survives a relink. Put it in a fragment. It reports which provider came from which fragment, added or replaced, so a local file shadowing a provider the repo ships cannot do it quietly. Proposed as a change request from the machine that needs it; verified here against pi 0.84.2 before applying.
2026-09-25dot-request: file a change request from a machine that cannot pushIgor Soarez
A clone made over https cannot publish anything, so a fix found on such a machine is lost at the next pull. The instructions already said to hand back a change request instead of editing; this is the command that does it. The request is markdown on stdin, signed with a key that machine holds, and posted to the inbox, which stores it only if the signature is from a key authorised there. Where the key is not authorised, nothing is stored and the submission says so. The endpoint is named here, which the leak rule would otherwise forbid, and the rule now carries the exception: a public endpoint this repo's own tooling must reach, already published by a DNS record and a certificate in the transparency logs, is not a leak. Concealing it would protect nothing and cost every machine a hand-written config file; what protects it is the signature. The private layer's check has the same exception, so it is enforced rather than remembered, and it does not extend to hosts, addresses or internal-only names. The other side is documented too: where `sz-requests` exists, what arrived can be read and judged. A signature says which machine produced the text, never that the text is right -- the machines filing these are the ones this account does not fully control.
2026-09-22tree-sitter-cli, and: a read-only clone hands back a change requestIgor Soarez
An https clone of this repo cannot push -- the host serves it anonymously and refuses the push with a 403 -- and that is how a machine whose owner cannot hold a key gets it. An agent troubleshooting there would edit a tracked file, see the fix applied, and lose it at the next pull, or leave that clone unable to fast-forward and quietly cut off from changes. CLAUDE.md now says how to tell the two cases apart, and what to do instead: make it machine-local where a hook covers it, or hand back a change request -- paths, the actual diff, why, and how to verify -- stating that nothing was applied. AGENTS.md is a symlink to CLAUDE.md, so the agents that look for that name get the same file rather than a second copy to drift. tree-sitter-cli: nvim-treesitter generates and compiles its parsers with it.
2026-09-21hammerspoon: shared helpers move to dotlib.lua, which local.lua can requireIgor Soarez
A machine's local.lua is a separate chunk, so init.lua's locals were invisible to it and anything it wanted -- hotkey(), the mod tables, type_text -- had to be copied. They live in dotlib.lua now, linked beside init.lua, and both files reach them the same way: require("dotlib"). init.lua keeps its own vocabulary by aliasing what it uses, so the 500 lines below are untouched. It prepends its own directory to package.path first: Hammerspoon already puts the config directory there (Contents/Resources/ setup.lua), but taking it from this file's path holds however the config was found. A machine that pulls without re-linking has no dotlib.lua beside init.lua, which would lose every hotkey silently -- that case notifies and names the fix. Checked with a stubbed hs: the module's hotkey, focus, execute, type_text and type_current_date all behave, and local-example.lua binds its four keys through it.
2026-09-21hammerspoon, ghostty: example files for their machine-local configsIgor Soarez
ssh and git each ship one; these two hooks were discoverable only by reading the config that loads them. Both examples carry what is easy to get wrong: init.lua's hotkey() helper and mod tables are locals, so a local.lua calls hs.hotkey.bind directly, and a binding there can only be shadowed, never deleted; in Ghostty a single-value key replaces while keybind accumulates, and `ignore` is how a binding is switched off rather than added to. Checked rather than assumed: the lua parses, and the Ghostty example validates and, dropped in as ~/.config/ghostty-local, does what it says -- font-size, command and background-opacity replaced, super+shift+n added, alt+enter turned off.
2026-09-21CLAUDE.md: where a single machine's changes go, and what to do without a hookIgor Soarez
The hooks existed but were documented nowhere together: ~/.localenv and ~/.localprofile were mentioned in passing, the skip file under the link system, and the ssh, git, Hammerspoon, Ghostty and Alacritty ones only in the config files that read them. Anyone -- an agent especially -- asked to change one machine had to read the whole repo to find out whether that was possible without committing to it. They are one table now, with what each overrides and what reads it, plus what has no hook at all. And the rule for everything else: say what is being asked for and where it would have to live, rather than putting one machine's preference in the config every machine shares, or leaving that machine's checkout dirty and its sync failing.
2026-09-21link: a part named in ~/.config/dot/skip does nothing on that machineIgor Soarez
A machine may need one of the parts left out: on a laptop it does not own, ~/.claude/settings.json is the owner's, holding their environment and plugins, and linking this account's over it is wrong in both directions -- theirs is lost, and anything of theirs worth keeping would have to be carried here, in the repo every machine shares. The file is hand-written and in no repo, like ~/.localenv, so the exception stays on the machine that needs it. The check is in link/lib.sh, as each part starts, rather than in link.sh's loop, so `link/claude.sh yes` obeys it too; link.sh and other repos' link.sh source that file as well, and sit outside link/, so their names can never be read as a part's.
2026-09-17dotfilesIgor Soarez
Shell, editor and terminal configuration, the tools that go with them, and szecret, an age-based secret manager whose store lives elsewhere. History restarts here. What came before was not written to be read by anyone else, and carried machine names, addresses and an encrypted secret store; it is kept privately instead. What is left is meant to be safe on any machine, including one whose owner is not me: see CLAUDE.md for the rule it is written to.