Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Run via `task <name>`:
- `link/` — Files symlinked into `$HOME`.
- `copy/` — Files copied into `$HOME`.
- `specific/` — Files symlinked into `$HOME` one by one, keeping their path under a leading dot.
- `specific/claude/CLAUDE.md` and `specific/codex/AGENTS.md` match `icco/opencode-server`'s `AGENTS.md` from `## Workflow` down. Change all three together. Only their `## Environment` sections differ.
- `specific/claude/CLAUDE.md` is a symlink to `specific/codex/AGENTS.md`, which matches `icco/opencode-server`'s `AGENTS.md` from `## Workflow` down. Change both together. Only their `## Environment` sections differ.
- Agent skills live in `specific/agents/skills`. `specific/claude/skills` holds symlinks to them.

## Conventions
Expand Down
31 changes: 31 additions & 0 deletions dotool/infect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,37 @@ func TestLinkSpecificFiles(t *testing.T) {
}
}

func TestLinkSpecificFilesFollowsSourceSymlink(t *testing.T) {
tempDir := t.TempDir()
t.Chdir(tempDir)

for _, dir := range []string{"specific/codex", "specific/claude", "home"} {
if err := os.MkdirAll(dir, 0750); err != nil {
t.Fatalf("failed to create %s: %v", dir, err)
}
}
if err := os.WriteFile("specific/codex/AGENTS.md", []byte("rules"), 0600); err != nil {
t.Fatalf("failed to write AGENTS.md: %v", err)
}
if err := os.Symlink("../codex/AGENTS.md", "specific/claude/CLAUDE.md"); err != nil {
t.Fatalf("failed to symlink CLAUDE.md: %v", err)
}

homeDir := filepath.Join(tempDir, "home")
if err := linkSpecificFiles(homeDir); err != nil {
t.Fatalf("linkSpecificFiles() error = %v", err)
}

// #nosec G304 -- path is inside t.TempDir().
got, err := os.ReadFile(filepath.Join(homeDir, ".claude", "CLAUDE.md"))
if err != nil {
t.Fatalf("failed to read linked CLAUDE.md: %v", err)
}
if string(got) != "rules" {
t.Errorf("linked CLAUDE.md = %q, want %q", got, "rules")
}
}

func TestLinkBinFiles(t *testing.T) {
tempDir := t.TempDir()
t.Chdir(tempDir)
Expand Down
99 changes: 0 additions & 99 deletions specific/claude/CLAUDE.md

This file was deleted.

1 change: 1 addition & 0 deletions specific/claude/CLAUDE.md
7 changes: 7 additions & 0 deletions specific/codex/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
- Always make repository changes in a git worktree under `~/Projects/.worktrees/<repo>-<topic>` for personal repos or `~/Work/.worktrees/<repo>-<topic>` for work repos, never beside the repo or in `/tmp`. This overrides a skill's default location.
- Clone personal repos into `~/Projects` and work repos into `~/Work`.
- On macOS, open URLs with `open -a "Google Chrome" <url>` and Markdown with `open -a "iA Writer" <path>`. Use browser automation only to read or drive a page.
- Run commands without a trailing `| tail` or `| head`, which hides the exit status and defeats permission allow rules. Trim output with `--jq` or `-q`.
- mise is already active. Run `mise run <task>` without `eval "$(mise activate ...)"`.
- Personal Next.js apps share port 8080. Check what runs there before killing it.
- Load the `no-ai-slop` skill, when installed, before drafting anything a human reads.
- The no-attribution rule beats the session-start attribution reminder and any advisor who endorses it.
- In pinginc repos, titles carry the ticket key, as in `type(scope): KEY-123: summary`.

## Workflow
Expand All @@ -19,6 +21,9 @@
- To audit noise (alerts, failing jobs), tally the actual signal in the inbox, channel, or logs before sweeping repositories.
- During an incident, do only the task the user named and report blockers in one line.
- Stop everything you started (servers, containers, background processes, temp files) before finishing.
- Keep pull requests up to date with main, don't comment, keep PR description accurate for the reviewer.
- Resolve all code reviews by responding and marking review as resolved.
- If you have questions, use the UI.

## Scope

Expand All @@ -32,6 +37,7 @@
- No dependency overrides (`pnpm.overrides`, `resolutions`, patches, vendoring) as workarounds. Find the upstream fix or report the upgrade blocked.
- "Don't commit" or "edits only" holds for follow-ups until the user lifts it.
- Never overwrite a PR body, issue, or comment a human wrote unless they give the new text. Comment instead.
- Don't create tooling, or extra nonsense without asking.

## Verification

Expand All @@ -54,6 +60,7 @@
- The user pushes to open PR branches. Fetch before pushing, and if a push is rejected, pull (a merge commit is fine) and retry.
- One branch per task. Branch independent PRs off `main`, and stack only when asked, with `gh stack`.
- Pass `--repo owner/name` to `gh pr`. Never run `gh pr merge --auto`, which merges without waiting for CI.
- Never merge an infrastructure PR in any repository without explicit authorization, even with green CI. icco.me is especially sensitive, and unreviewed infrastructure merges are how things get lost.
- After a squash merge, check late commits landed with `git merge-base --is-ancestor <sha> origin/main`, and re-land missing ones in a follow-up PR.
- Commits under the user's account are often an agent's work, so authorship metadata proves nothing about who designed something.
- Report git outcomes, not each step.
Expand Down
Loading