@thesmurph/agentlink 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ First release.
6
+
7
+ - `agentlink init` creates `AGENTS.md` and `.agents/skills/`, adopts an existing
8
+ `CLAUDE.md` or `GEMINI.md`, folds stray real skill directories into the
9
+ canonical tree, and links every selected harness.
10
+ - Symlinks are relative, so clones and git worktrees resolve at any path.
11
+ - 17 harnesses, with a documentation source per row. Harnesses that already read
12
+ `AGENTS.md` or `.agents/skills/` are reported as native and left alone. Rows
13
+ with no documented path report as unknown instead of linking somewhere nothing
14
+ reads.
15
+ - `sync`, `fix`, `select`, `list`, `doctor`, `adopt` and `unlink`, plus `--global`
16
+ for a home-directory setup.
17
+ - An idempotent clause appended to `AGENTS.md` so future agents follow the
18
+ convention without a human explaining it.
19
+ - A marked `.gitignore` block: instructions aliases are committed, skill links
20
+ are ignored. `--ignore=skills|all|none`.
21
+ - Zero runtime dependencies, 59 tests, verified on Node 20, 22, 24 and 26.
package/CONVENTION.md ADDED
@@ -0,0 +1,89 @@
1
+ # The agentlink convention
2
+
3
+ A convention for repositories and home directories that several coding agents read. It exists so that a team can use different agents without maintaining a copy of the same instructions per agent.
4
+
5
+ ## The rule
6
+
7
+ Two paths hold real content. Every harness-specific path that a tool reads is a symlink to one of them.
8
+
9
+ ```
10
+ AGENTS.md instructions
11
+ .agents/
12
+ skills/<skill-name>/SKILL.md one directory per skill
13
+ <topic>.md optional extra docs
14
+ agentlink.json selection and link state (written by the CLI)
15
+ ```
16
+
17
+ Links point at these, for example `CLAUDE.md -> AGENTS.md` and `.claude/skills/release -> ../../.agents/skills/release`. The links are relative, so a clone or a `git worktree` resolves them at any path.
18
+
19
+ ## Normative rules
20
+
21
+ 1. `AGENTS.md` at the root of the repository, or at `$HOME` for global instructions, is the only instructions file. Other names are links.
22
+ 2. Every skill is a directory under `.agents/skills/` containing `SKILL.md`. Supporting files stay inside that directory.
23
+ 3. A skill directory and its frontmatter `name` are the same lowercase-hyphenated string, 1 to 64 characters, matching `[a-z0-9]+(-[a-z0-9]+)*`.
24
+ 4. `SKILL.md` carries frontmatter with `name` and `description`. The description states what the skill does and when to use it.
25
+ 5. Never edit a file that is a symlink, and never create a sibling next to one. Edit the file it points at.
26
+ 6. Documentation that is not a skill goes under `.agents/`, or in an `AGENTS.md` inside the subdirectory it applies to.
27
+ 7. Run `agentlink sync` after adding, renaming, or moving a skill or doc.
28
+ 8. Harness instruction aliases are committed to git. Skill links are listed in `.gitignore` inside a block marked `# agentlink:begin`, which `agentlink sync` owns. Do not edit the block by hand.
29
+
30
+ Rules 1 to 7 are format requirements, not preferences. Harnesses fail silently when they are broken: a skill whose name disagrees with its directory loads in some tools and not others, and an edited symlink leaves the canonical copy stale.
31
+
32
+ Rule 8 is a trade-off rather than a requirement. Committing an instructions alias costs one small file and makes a fresh clone work for a teammate on a different harness. Committing skill links costs a symlink per skill per harness, and buys nothing that `agentlink sync` does not regenerate on demand.
33
+
34
+ ## The clause
35
+
36
+ `agentlink sync` appends this block to `AGENTS.md`, between markers, so a future agent that has never seen the CLI can follow the convention. Paste it by hand if you are adopting the convention without the tool.
37
+
38
+ ```markdown
39
+ <!-- agentlink:begin v1 -->
40
+ ## Agent docs and skills: one source of truth
41
+
42
+ This repository keeps exactly one copy of every agent instruction file and skill.
43
+ Paths such as `CLAUDE.md`, `GEMINI.md`, `.claude/skills/` and `.cursor/skills/`
44
+ are symlinks maintained by `agentlink`. Never edit a symlink, and never create a
45
+ file next to one — edit or create the source it points to.
46
+
47
+ - **Instructions** live in `AGENTS.md` at the repository root.
48
+ - **Skills** live in `.agents/skills/<skill-name>/SKILL.md`, one directory per skill.
49
+ - **Extra documentation** goes under `.agents/` (for example `.agents/testing.md`),
50
+ or in an `AGENTS.md` in the subdirectory it applies to.
51
+ - **Naming**: the skill directory and its frontmatter `name` are the same
52
+ lowercase-hyphenated string, 1-64 characters (`pdf-forms`, not `PDF_Forms`).
53
+ `SKILL.md` needs frontmatter with `name` and a `description` that states what
54
+ the skill does *and* when to use it. Keep scripts and references inside the
55
+ skill directory and link them with relative paths.
56
+ - **After adding, renaming, or moving a skill or doc**, run `agentlink sync`
57
+ so every harness picks up the change.
58
+ <!-- agentlink:end -->
59
+ ```
60
+
61
+ The markers make the block idempotent and replaceable: sync rewrites the block in place when the convention changes, and never touches the text around it.
62
+
63
+ ## Adopting by hand
64
+
65
+ ```bash
66
+ # 1. Instructions
67
+ git mv CLAUDE.md AGENTS.md # or merge the content by hand
68
+ ln -s AGENTS.md CLAUDE.md
69
+
70
+ # 2. Skills
71
+ mkdir -p .agents/skills
72
+ git mv .claude/skills/release .agents/skills/release
73
+ ln -s ../../.agents/skills/release .claude/skills/release
74
+ cat >> .gitignore <<'EOF'
75
+
76
+ # agentlink:begin
77
+ .claude/skills/
78
+ # agentlink:end
79
+ EOF
80
+
81
+ # 3. The clause
82
+ # paste the block above into AGENTS.md
83
+ ```
84
+
85
+ Then run `agentlink sync` to create the remaining links and `agentlink doctor` to check the result. `doctor` exits non-zero when it finds an error, which makes it usable as a CI check on a repository that has adopted the convention.
86
+
87
+ ## Related
88
+
89
+ The naming and layout rules for skills come from the [Agent Skills specification](https://agentskills.io/specification). The instructions file name comes from [agents.md](https://agents.md/). Both are implemented by most of the harnesses listed in the [README](README.md#what-gets-linked).
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sean Murphy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,177 @@
1
+ # agentlink
2
+
3
+ Every coding agent reads its own files. Claude Code wants `CLAUDE.md` and `.claude/skills/`. Codex reads `AGENTS.md` and `$HOME/.agents/skills`. The other fifteen picked something else again. Use two agents on one repository and you are keeping two copies of the same instructions, which drift.
4
+
5
+ agentlink reduces that to one rule. Two paths are real:
6
+
7
+ - `AGENTS.md` holds the instructions.
8
+ - `.agents/skills/<name>/SKILL.md` holds each skill.
9
+
10
+ Everything any harness reads is a symlink to one of those.
11
+
12
+ ## Quick start
13
+
14
+ ```bash
15
+ cd your-project
16
+ npx @thesmurph/agentlink init
17
+ ```
18
+
19
+ `init` prints a checklist of all 17 known harnesses, with the ones installed on your machine already ticked. Confirm, and it creates `AGENTS.md` and `.agents/skills/`, appends a clause that tells future agents how the convention works, and symlinks each selected harness at the canonical files. The selection is saved in `.agents/agentlink.json`, so later runs need no input.
20
+
21
+ After you add or move a skill:
22
+
23
+ ```bash
24
+ npx @thesmurph/agentlink sync
25
+ ```
26
+
27
+ ## Commands
28
+
29
+ ```
30
+ agentlink pick harnesses (first run), then link
31
+ agentlink init create AGENTS.md + .agents/skills, migrate, link
32
+ agentlink sync re-link after adding or moving a skill
33
+ agentlink fix fold stray real copies into .agents, then link
34
+ agentlink select change which harnesses are linked
35
+ agentlink list every harness, where it reads from, whether it is installed
36
+ agentlink doctor drift, duplicates, invalid skills, broken links
37
+ agentlink adopt move an existing CLAUDE.md or GEMINI.md into AGENTS.md
38
+ agentlink unlink remove the links agentlink created
39
+ ```
40
+
41
+ `init` does the whole conversion in one pass, including skills that already exist as real directories inside a harness folder. An orphan copy is moved into `.agents/skills/` and linked back. A copy identical to the canonical one is deleted. A copy that differs is reported and left alone until you have looked at it; `agentlink fix --force` drops the copy in favour of the canonical one.
42
+
43
+ Options: `--global` (`-g`) for `$HOME` instead of a repository, `--harnesses a,b` to skip the picker, `--all`, `--detected`, `--dry-run`, `--yes`, `--force`, `--json`, `--no-clause`.
44
+
45
+ ## What gets linked
46
+
47
+ Where a harness already reads `AGENTS.md` or `.agents/skills/`, agentlink writes nothing and reports the path as native. Links appear only for harnesses that need a file in a place of their own.
48
+
49
+ | Harness | Project instructions | Project skills | Confirmed |
50
+ | --- | --- | --- | --- |
51
+ | Claude Code | CLAUDE.md | .claude/skills | yes |
52
+ | OpenAI Codex CLI | native | native | yes |
53
+ | Pi | native | native | yes |
54
+ | Oh My Pi (omp) | native | .omp/skills | yes |
55
+ | GitHub Copilot CLI | native | native | yes |
56
+ | Cursor | native | .cursor/skills | no (skills) |
57
+ | opencode | native | .opencode/skills | no (skills) |
58
+ | Qwen Code | native | .qwen/skills | yes |
59
+ | Kimi Code CLI | native | .kimi-code/skills | yes |
60
+ | Kilo Code | native | .claude/skills | no (skills) |
61
+ | Factory Droid | native | .factory/skills | yes |
62
+ | Devin CLI | native | .devin/skills | yes |
63
+ | Mastra Code | native | native | yes |
64
+ | Grok CLI | native | .grok/skills | yes |
65
+ | Qoder CLI | native | .qoder/skills | yes |
66
+ | Antigravity CLI | native | .agent/skills | no (instructions, skills) |
67
+ | Hermes | native | .hermes/skills | no (instructions, skills) |
68
+
69
+ Confirmation is tracked per endpoint, not per harness, and per scope. A harness can have a documented project path and an undocumented global one. `agentlink list --json` prints the flag and the source URL for each endpoint, and `doctor` names every unverified path in your selection.
70
+
71
+ The global scope is the weaker half of the table on purpose. Fewer vendors document where their home-directory instructions file lives, so `--global` links more speculative paths than a project does. Both scopes are listed in `list --json`.
72
+
73
+ ## Global scope
74
+
75
+ `agentlink --global` applies the same convention to your home directory, so personal skills and standing preferences follow you into every repository:
76
+
77
+ ```
78
+ ~/AGENTS.md global instructions
79
+ ~/.agents/skills/ personal skills
80
+ ```
81
+
82
+ Each harness gets a link from its own home-directory file, for example `~/.claude/CLAUDE.md` and `~/.codex/AGENTS.md`. Harnesses that already read `~/.agents/skills/` get nothing, which is the point of the convention: the standard path is the one with the real files.
83
+
84
+ ## Git
85
+
86
+ To git, a symlink is an ordinary tracked file. Nothing is ignored by default, so `agentlink` writes the rules for the paths it creates into a marked block in `.gitignore`. The default split is the one repositories already converge on, including langfuse, which commits `CLAUDE.md` as a symlink and ignores `.claude/skills/`.
87
+
88
+ - Instructions aliases (`CLAUDE.md`, `.codex/AGENTS.md`) are committed. They are one small file, and they are what makes a fresh clone work for a teammate on a different harness.
89
+ - Skill links are ignored. They are derived from `.agents/skills/` and multiply with every skill and harness, and their parent directories hold machine-local harness state.
90
+
91
+ `--ignore=all` ignores both, for a repository where everyone runs `agentlink sync` after cloning. `--ignore=none` leaves `.gitignore` alone. The choice is stored in `.agents/agentlink.json`, so later runs keep it. `agentlink unlink` removes the block and leaves the rest of `.gitignore` untouched.
92
+
93
+ ```gitignore
94
+ # agentlink:begin
95
+ .claude/skills/
96
+ .cursor/skills/
97
+ # agentlink:end
98
+ ```
99
+
100
+ One caveat about committing the aliases. Git checks symlinks out as plain text files when `core.symlinks` is false, which is how Git for Windows behaves until symlink support is enabled. A teammate there gets a `CLAUDE.md` whose contents are the string `AGENTS.md`. Teams with Windows checkouts should use `--ignore=all` and run `sync` after cloning.
101
+
102
+ ## CI
103
+
104
+ `doctor` exits non-zero when it finds a problem, so a repository that has adopted the convention can check itself:
105
+
106
+ ```yaml
107
+ - run: npx @thesmurph/agentlink doctor
108
+ ```
109
+
110
+ The selection comes from `.agents/agentlink.json`, which is committed, so this works on a runner with no harnesses installed. `doctor --harnesses a,b` checks a declared set without depending on that file. With no selection and nothing installed there is nothing to check, and `doctor` says so instead of reporting a pass.
111
+
112
+ ## Safety
113
+
114
+ None of this is done by overwriting.
115
+
116
+ If a harness file already exists as a real file, sync leaves it alone and reports it as blocked. For an instructions file, `agentlink adopt` handles the migration: it renames `CLAUDE.md` to `AGENTS.md`, then links the old name back, so `git diff` shows one rename and you keep every line. When both files contain real content, adopt refuses and says so, because merging is a judgement call.
117
+
118
+ `agentlink unlink` removes a symlink only when it still points at `AGENTS.md` or into `.agents/`. Anything that has become a real file gets kept and reported. Empty directories left behind are pruned.
119
+
120
+ `sync` also removes links it created that are no longer wanted: deselect a harness with `select`, or delete a skill from `.agents/skills/`, and the leftover symlink is pruned on the next run rather than left dangling. Pruning only ever removes a symlink that still points into `.agents/`.
121
+
122
+ Every command takes `--dry-run`.
123
+
124
+ `doctor` exits non-zero when it finds an error, so it works as a CI check. A run that leaves an unresolved conflict (`init`, `fix`) also exits non-zero, which stops a scripted conversion instead of silently leaving two copies of a skill.
125
+
126
+ ## Limits worth knowing
127
+
128
+ Claude Code has open reports of symlinked skill directories working but not appearing in `/skills`. The link is real and the content is reachable; the listing may miss it. If that turns out to matter, `agentlink unlink` restores the previous state and you can copy the skill directory instead.
129
+
130
+ Harnesses that read `.agents/skills/` natively get no link at all, which means there is nothing for agentlink to repair if the harness changes its mind. `doctor` re-checks the table against what is selected, not against the vendor's current docs.
131
+
132
+ The Agent Skills specification requires a skill's `name` to match its directory. Pi relaxes that rule so one skills directory can serve several tools. `doctor` warns when the two differ, since the other harnesses stay strict.
133
+
134
+ ## Repository layout
135
+
136
+ ```
137
+ src/harnesses.ts the path table, one row per harness, with a source URL
138
+ src/link.ts planning and creating symlinks
139
+ src/convention.ts the AGENTS.md clause, scaffolding, adoption
140
+ src/doctor.ts diagnostics and skill validation
141
+ src/cli.ts command dispatch and output
142
+ src/ui.ts the checkbox picker, no dependencies
143
+ ```
144
+
145
+ Zero runtime dependencies. `npm run build` runs `tsc` into `dist/`.
146
+
147
+ ## Publishing
148
+
149
+ The first release needs your npm credentials:
150
+
151
+ ```bash
152
+ npm publish --access public --otp=<code from your authenticator>
153
+ ```
154
+
155
+ The name is scoped (`agentlink` is too similar to the existing, empty `agent-link`, which npm's typosquat check rejects), so publishing needs `--access public`. Publishing is also 2FA-gated, and the token `npm login` writes by default does not bypass 2FA. A granular access token with "Bypass 2FA" enabled also works, if you would rather not type a code.
156
+
157
+ After that, releases go through `.github/workflows/publish.yml` on a version tag, using trusted publishing so there is no token to rotate and provenance is attached automatically:
158
+
159
+ ```bash
160
+ git tag v0.1.0 && git push origin v0.1.0
161
+ ```
162
+
163
+ Configure it once on npmjs.com: package settings, Trusted publishing, GitHub Actions, with the workflow filename `publish.yml`.
164
+
165
+ ## Tests
166
+
167
+ ```bash
168
+ npm test # tsc, then node --test
169
+ ```
170
+
171
+ Runs on Node 20, 22, 24 and 26, which CI checks. Bare `node --test` rather than `node --test test/`: the directory form resolves differently from Node 22 onward and fails.
172
+
173
+ No test framework. The suite covers the path table invariants, symlink planning and repair, the `.gitignore` block, the AGENTS.md clause, duplicate migration, and the CLI run end to end in throwaway git repositories.
174
+
175
+ ## Adding a harness
176
+
177
+ Add one row to `HARNESSES` in `src/harnesses.ts` and cite the documentation you read it from. State for each scope whether the harness reads the canonical path already, needs an alias, or has no documented answer. Rows with no answer print as unknown instead of writing a symlink somewhere the harness never looks.