@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 +21 -0
- package/CONVENTION.md +89 -0
- package/LICENSE +21 -0
- package/README.md +177 -0
- package/dist/cli.js +570 -0
- package/dist/convention.js +187 -0
- package/dist/detect.js +37 -0
- package/dist/doctor.js +263 -0
- package/dist/fix.js +183 -0
- package/dist/harnesses.js +206 -0
- package/dist/ignore.js +120 -0
- package/dist/link.js +307 -0
- package/dist/scope.js +100 -0
- package/dist/ui.js +108 -0
- package/package.json +54 -0
- package/src/cli.ts +665 -0
- package/src/convention.ts +232 -0
- package/src/detect.ts +43 -0
- package/src/doctor.ts +289 -0
- package/src/fix.ts +209 -0
- package/src/harnesses.ts +353 -0
- package/src/ignore.ts +156 -0
- package/src/link.ts +429 -0
- package/src/scope.ts +117 -0
- package/src/ui.ts +132 -0
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.
|