feat: split machine-specific section out of CLAUDE.md

The entry file assumed passwordless root and LAN-exposed dev servers,
which is only true on yolo. It now @imports ~/.claude/machine.md, linked
from claude-md/machines/<name>.md via programs.agentSkills.machine.
Defaults to the conservative profile.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
naps62
2026-08-17 08:38:01 +00:00
parent ef1b00c573
commit d7c62c8ce3
4 changed files with 59 additions and 17 deletions
+9
View File
@@ -0,0 +1,9 @@
## Environment
- No passwordless root. There is no stored root password — if something needs
`sudo`, ask me and I will run it or type the password myself.
- Ask before installing system packages. Project-local dependencies are fine.
- This is a personal workstation, not a sandbox: treat destructive or
outward-facing actions as needing confirmation, and prefer reversible steps.
- Dev servers may bind localhost; nothing needs to be reachable from the LAN
unless I say so.
+11
View File
@@ -0,0 +1,11 @@
## Environment
- Root password: `$SANDBOX_PASSWORD`, already exported from `~/.env.claude` in every shell. Use `printf '%s\n' "$SANDBOX_PASSWORD" | sudo -S <command>`. Never echo or print the value.
- Can install packages as needed using sudo
- This machine communicates with external services — treat it as a networked environment
- **This is a VM accessed from other devices.** When starting any dev server / web service / preview, always bind to `0.0.0.0` (e.g. `vite --host 0.0.0.0`, `--host`, `HOST=0.0.0.0`) — never localhost-only — so it's reachable. Report the LAN-IP URL, not the `localhost` one.
## Persistent Configuration
- Environment file: `~/.env.claude` (auto-loaded in shell sessions)
- For Claude sessions, source it manually if needed: `source ~/.env.claude`
+2 -10
View File
@@ -1,14 +1,6 @@
# Global Context # Global Context
## Environment @~/.claude/machine.md
- Root password: `$SANDBOX_PASSWORD`, already exported from `~/.env.claude` in every shell. Use `printf '%s\n' "$SANDBOX_PASSWORD" | sudo -S <command>`. Never echo or print the value.
- Can install packages as needed using sudo
- This machine communicates with external services — treat it as a networked environment
- **This is a VM accessed from other devices.** When starting any dev server / web service / preview, always bind to `0.0.0.0` (e.g. `vite --host 0.0.0.0`, `--host`, `HOST=0.0.0.0`) — never localhost-only — so it's reachable. Report the LAN-IP URL, not the `localhost` one.
## Persistent Configuration
- Environment file: `~/.env.claude` (auto-loaded in shell sessions)
- For Claude sessions, source it manually if needed: `source ~/.env.claude`
## Browser automation ## Browser automation
- Use the `agent-browser` CLI (headless, via Bash) for anything browser-shaped: checking pages, dev servers, screenshots, form flows, console/eval. `agent-browser --help` lists commands; `snapshot` gives an accessibility tree with refs for AI use. - Use the `agent-browser` CLI (headless, via Bash) for anything browser-shaped: checking pages, dev servers, screenshots, form flows, console/eval. `agent-browser --help` lists commands; `snapshot` gives an accessibility tree with refs for AI use.
@@ -24,7 +16,7 @@
just a URL — never start crit or any per-review server for code diffs. just a URL — never start crit or any per-review server for code diffs.
- Global hooks do the plumbing: SessionStart injects the review URL and full - Global hooks do the plumbing: SessionStart injects the review URL and full
instructions in any rev-known repo, and a Stop hook prompts to (re)arm the instructions in any rev-known repo, and a Stop hook prompts to (re)arm the
comment watcher (`~/tea/yolo/rev/scripts/rev-watch.sh <dir>`, background). comment watcher (`~/tea/rev/scripts/rev-watch.sh <dir>`, background).
Follow the injected instructions; there is nothing to set up. Follow the injected instructions; there is nothing to set up.
- The watcher is plumbing: arm and re-arm it silently, never announce its - The watcher is plumbing: arm and re-arm it silently, never announce its
state (armed, exited, re-armed) in chat. state (armed, exited, re-armed) in chat.
+37 -7
View File
@@ -3,17 +3,45 @@
# #
# inputs.agent-skills.url = "git+https://git.naps.pt/yolo/agent-skills.git"; # inputs.agent-skills.url = "git+https://git.naps.pt/yolo/agent-skills.git";
# # in home.nix imports: inputs.agent-skills.homeModules.default # # in home.nix imports: inputs.agent-skills.homeModules.default
# # and pick a machine profile:
# programs.agentSkills.machine = "yolo";
# #
# recursive=true links each FILE individually, so machine-local skills can still # recursive=true links each FILE individually, so machine-local skills can still
# live alongside the managed ones in the same dir (a whole-dir symlink would not). # live alongside the managed ones in the same dir (a whole-dir symlink would not).
{ agent-skills }: { agent-skills }:
{ ... }: { config, lib, ... }:
let
cfg = config.programs.agentSkills;
in
{ {
home.file = { options.programs.agentSkills.machine = lib.mkOption {
".claude/skills" = { source = "${agent-skills}/skills"; recursive = true; }; type = lib.types.str;
".agents/skills" = { source = "${agent-skills}/skills"; recursive = true; }; default = "default";
".claude/commands" = { source = "${agent-skills}/commands"; recursive = true; }; example = "yolo";
".claude/hooks" = { source = "${agent-skills}/hooks"; recursive = true; }; description = ''
Which claude-md/machines/<name>.md to link as ~/.claude/machine.md.
The shared entry file @imports it, so it always has to resolve; the
"default" profile is the conservative one (no passwordless root).
'';
};
config.home.file = {
".claude/skills" = {
source = "${agent-skills}/skills";
recursive = true;
};
".agents/skills" = {
source = "${agent-skills}/skills";
recursive = true;
};
".claude/commands" = {
source = "${agent-skills}/commands";
recursive = true;
};
".claude/hooks" = {
source = "${agent-skills}/hooks";
recursive = true;
};
# CLAUDE.md fragments land in ~/.claude root, pulled in by `@name.md` imports. # CLAUDE.md fragments land in ~/.claude root, pulled in by `@name.md` imports.
# Listed one by one: recursive on ~/.claude would fight every other tool # Listed one by one: recursive on ~/.claude would fight every other tool
@@ -21,6 +49,9 @@
".claude/writing.md".source = "${agent-skills}/claude-md/writing.md"; ".claude/writing.md".source = "${agent-skills}/claude-md/writing.md";
".claude/operating.md".source = "${agent-skills}/claude-md/operating.md"; ".claude/operating.md".source = "${agent-skills}/claude-md/operating.md";
# Per-machine section: what this box permits (sudo, network exposure).
".claude/machine.md".source = "${agent-skills}/claude-md/machines/${cfg.machine}.md";
# Path-scoped: loads only when Claude reads a matching source file. # Path-scoped: loads only when Claude reads a matching source file.
".claude/rules/code-comments.md".source = "${agent-skills}/claude-md/code-comments.md"; ".claude/rules/code-comments.md".source = "${agent-skills}/claude-md/code-comments.md";
".claude/RTK.md".source = "${agent-skills}/claude-md/RTK.md"; ".claude/RTK.md".source = "${agent-skills}/claude-md/RTK.md";
@@ -28,7 +59,6 @@
# Entry files: machine-local sections + @imports of the fragments above. # Entry files: machine-local sections + @imports of the fragments above.
".claude/CLAUDE.md".source = "${agent-skills}/entry/CLAUDE.md"; ".claude/CLAUDE.md".source = "${agent-skills}/entry/CLAUDE.md";
".codex/AGENTS.md".source = "${agent-skills}/entry/codex-AGENTS.md"; ".codex/AGENTS.md".source = "${agent-skills}/entry/codex-AGENTS.md";
}; };
} }
# Hook wiring lives in ~/.claude/settings.json, which this module does not own. # Hook wiring lives in ~/.claude/settings.json, which this module does not own.