@ethlete/agent-rules 0.1.0-next.0 → 0.1.0-next.1

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +87 -15
  3. package/content/hooks/context-warning.py +215 -0
  4. package/package.json +1 -1
  5. package/src/index.js +12 -4
  6. package/src/index.js.map +1 -1
  7. package/src/lib/config.d.ts +35 -2
  8. package/src/lib/config.js +30 -8
  9. package/src/lib/config.js.map +1 -1
  10. package/src/lib/index.d.ts +1 -0
  11. package/src/lib/index.js +1 -0
  12. package/src/lib/index.js.map +1 -1
  13. package/src/lib/migrate.d.ts +6 -0
  14. package/src/lib/migrate.js +155 -0
  15. package/src/lib/migrate.js.map +1 -0
  16. package/src/lib/owned-paths.js +4 -1
  17. package/src/lib/owned-paths.js.map +1 -1
  18. package/src/lib/plan.d.ts +4 -0
  19. package/src/lib/plan.js +65 -6
  20. package/src/lib/plan.js.map +1 -1
  21. package/src/lib/sync.js +8 -2
  22. package/src/lib/sync.js.map +1 -1
  23. package/src/lib/targets/agents-skills.d.ts +8 -0
  24. package/src/lib/targets/agents-skills.js +18 -0
  25. package/src/lib/targets/agents-skills.js.map +1 -0
  26. package/src/lib/targets/claude-hooks.d.ts +33 -0
  27. package/src/lib/targets/claude-hooks.js +97 -0
  28. package/src/lib/targets/claude-hooks.js.map +1 -0
  29. package/src/lib/targets/claude.d.ts +3 -1
  30. package/src/lib/targets/claude.js +13 -20
  31. package/src/lib/targets/claude.js.map +1 -1
  32. package/src/lib/targets/codex.d.ts +5 -3
  33. package/src/lib/targets/codex.js +8 -7
  34. package/src/lib/targets/codex.js.map +1 -1
  35. package/src/lib/targets/copilot.d.ts +3 -3
  36. package/src/lib/targets/copilot.js +5 -28
  37. package/src/lib/targets/copilot.js.map +1 -1
  38. package/src/lib/targets/cursor.d.ts +3 -3
  39. package/src/lib/targets/cursor.js +6 -11
  40. package/src/lib/targets/cursor.js.map +1 -1
  41. package/src/lib/targets/shared.d.ts +22 -12
  42. package/src/lib/targets/shared.js +32 -15
  43. package/src/lib/targets/shared.js.map +1 -1
  44. package/src/lib/targets/neutral.d.ts +0 -8
  45. package/src/lib/targets/neutral.js +0 -29
  46. package/src/lib/targets/neutral.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @ethlete/agent-rules
2
2
 
3
+ ## 0.1.0-next.1
4
+
5
+ ### Minor Changes
6
+
7
+ - [#3042](https://github.com/ethlete-io/ethdk/pull/3042) [`28a58eb`](https://github.com/ethlete-io/ethdk/commit/28a58ebc56420b7e067d8c56108b39601d7b367e) Thanks [@github-actions](https://github.com/apps/github-actions)! - - Skills now compile to the cross-tool `.agents/skills/ethlete-*/SKILL.md` format, discovered natively by Codex, Cursor and Copilot; the `.agents/ethlete/` pointer tree is pruned on sync.
8
+ - New `ethlete-agents migrate` converts a repo to the `AGENTS.md`-canonical layout: `CLAUDE.md` becomes an `@AGENTS.md` import and hand-written skills move to `.agents/skills` with symlinks.
9
+ - New opt-in `hooks` config: `context-warning` warns (and instructs Claude) before the context crosses the 200k long-context pricing boundary, recommending `/handoff`.
10
+ - A gitignored `ethlete-agents.config.local.json` (`"disableHooks": true` or a list of hook names) disables generated hooks per machine at runtime — committed files never change, and `sync`/`check` warn about unsupported keys or unknown hook names in it.
11
+
3
12
  ## 0.1.0-next.0
4
13
 
5
14
  ### Minor Changes
package/README.md CHANGED
@@ -23,17 +23,44 @@ npx ethlete-agents check # exits non-zero when the generated files are stale
23
23
 
24
24
  ## What gets written
25
25
 
26
- | | Claude Code | Cursor | Copilot | Codex |
27
- | ------------------- | ----------------------------------- | --------------------------------------------------- | ------------------------------------------------------ | ------------------------------ |
28
- | Always-loaded rules | `.claude/rules/ethlete/*.md` | `.cursor/rules/ethlete-*.mdc` (`alwaysApply: true`) | inlined into `.github/copilot-instructions.md` | inlined into `AGENTS.md` |
29
- | On-demand guides | `.claude/skills/ethlete-*/SKILL.md` | `.cursor/rules/ethlete-*.mdc` | `.github/instructions/*.instructions.md`, or a pointer | a pointer table in `AGENTS.md` |
30
-
31
- `AGENTS.md` supports neither frontmatter nor includes, so anything a target cannot
32
- express on-demand falls back to plain markdown under `.agents/ethlete/` plus a pointer
33
- from the always-loaded file. Marker-block files (`AGENTS.md`,
34
- `.github/copilot-instructions.md`) are only rewritten between
35
- `<!-- ethlete:agent-rules:start -->` and `:end` - everything you wrote around them
36
- survives.
26
+ Skills are compiled once into `.agents/skills/ethlete-*/SKILL.md` - the cross-tool
27
+ [Agent Skills](https://agentskills.io) format that Codex, Cursor, Copilot and VS Code
28
+ discover natively (each skill's body loads on demand from its `description`
29
+ frontmatter). Claude Code only scans `.claude/skills/`, so the claude target writes its
30
+ own copies there. Rules are always-loaded and go into each tool's native mechanism:
31
+
32
+ | | Claude Code | Cursor | Copilot | Codex |
33
+ | ------------------- | ----------------------------------- | --------------------------------------------------- | ---------------------------------------------- | ----------------------------------- |
34
+ | Always-loaded rules | `.claude/rules/ethlete/*.md` | `.cursor/rules/ethlete-*.mdc` (`alwaysApply: true`) | inlined into `.github/copilot-instructions.md` | inlined into `AGENTS.md` |
35
+ | On-demand skills | `.claude/skills/ethlete-*/SKILL.md` | `.agents/skills/ethlete-*/SKILL.md` | `.agents/skills/ethlete-*/SKILL.md` | `.agents/skills/ethlete-*/SKILL.md` |
36
+
37
+ Marker-block files (`AGENTS.md`, `.github/copilot-instructions.md`) are only rewritten
38
+ between `<!-- ethlete:agent-rules:start -->` and `:end` - everything you wrote around
39
+ them survives.
40
+
41
+ ## Migrating a repo to the AGENTS.md layout
42
+
43
+ `AGENTS.md` is the cross-tool standard, and Claude Code officially supports reading it
44
+ through a one-line `CLAUDE.md` import. To restructure a whole repo around that:
45
+
46
+ ```bash
47
+ npx ethlete-agents migrate --dry-run # prints the plan
48
+ npx ethlete-agents migrate
49
+ ```
50
+
51
+ - `CLAUDE.md` content moves to the top of `AGENTS.md`; `CLAUDE.md` becomes `@AGENTS.md`.
52
+ - Hand-written `.claude/skills/<name>` directories move to `.agents/skills/<name>`, with
53
+ a symlink left behind so Claude Code still finds them. (Symlinks need Developer Mode
54
+ on Windows checkouts.)
55
+ - A short layout note is added to `AGENTS.md` so agents know the symlinked skills are
56
+ the same files, not duplicates - skipped if your `AGENTS.md` already explains it.
57
+ - The config gains the `codex` target and `claudeMdImportsAgentsMd: true`, which stops
58
+ the claude target from writing `.claude/rules/ethlete/` - the rules already reach
59
+ Claude through the `AGENTS.md` marker block, and a second copy would load twice.
60
+ - A `sync` runs, which also prunes output from older layouts (`.agents/ethlete/`,
61
+ `.github/instructions/ethlete-*`).
62
+
63
+ The command is idempotent - every step detects the migrated state and skips itself.
37
64
 
38
65
  Every generated file carries a `DO NOT EDIT` banner. Files that disappear from the
39
66
  package are pruned on the next `sync`; nothing outside an `ethlete` directory or an
@@ -47,7 +74,6 @@ Prettier rewrites them and `check` then reports drift on every run:
47
74
  /.claude
48
75
  /.agents
49
76
  /.cursor/rules/ethlete-*
50
- /.github/instructions/ethlete-*
51
77
  ```
52
78
 
53
79
  ## Configuration
@@ -69,8 +95,9 @@ Prettier rewrites them and `check` then reports drift on every run:
69
95
  }
70
96
  ```
71
97
 
72
- - **`targets`** - `"auto"` (default) emits for every agent whose directory already
73
- exists, or list a subset of `claude`, `codex`, `cursor`, `copilot`.
98
+ - **`targets`** - `"auto"` (default) always emits `codex` (`AGENTS.md` plus
99
+ `.agents/skills/` is the cross-tool baseline) and adds `claude`, `cursor` or `copilot`
100
+ when their directory exists; or list an explicit subset.
74
101
  - **`profile`** - `"consumer"` (default) emits `scope: consumer` and `scope: both`
75
102
  content. `"sdk"` emits only `both`; the SDK repo uses it so its own hand-written,
76
103
  authoring-side guides are not overwritten by the consumer-side versions.
@@ -78,10 +105,55 @@ Prettier rewrites them and `check` then reports drift on every run:
78
105
  `content/defaults.json`; a guide whose variable has no default and no value is
79
106
  skipped with a warning rather than emitted with a dangling placeholder.
80
107
  - **`exclude`** - content names to skip entirely.
108
+ - **`claudeMdImportsAgentsMd`** - set (usually by `migrate`) when `CLAUDE.md` is an
109
+ `@AGENTS.md` import or symlink; the claude target then skips `.claude/rules/ethlete/`
110
+ so the rules don't load twice. `sync` warns when the flag is set but the import is
111
+ missing.
81
112
 
82
113
  Content that declares `requires` is only emitted when those packages are installed, so
83
114
  a repo without `@ethlete/query` never sees the query guide.
84
115
 
116
+ ## Hooks (opt-in)
117
+
118
+ Claude Code hooks run commands on the developer's machine, so none are emitted by
119
+ default - opt in per hook in the config:
120
+
121
+ ```json
122
+ {
123
+ "hooks": ["context-warning"]
124
+ }
125
+ ```
126
+
127
+ `sync` writes the script to `.claude/hooks/ethlete/` and registers it in
128
+ `.claude/settings.json` (your own entries are left untouched); removing the name from
129
+ `hooks` unregisters and deletes it again.
130
+
131
+ Available hooks:
132
+
133
+ - **`context-warning`** - warns once per tier (and instructs Claude) when the session
134
+ context crosses 70% / 85% of the token budget, recommending `/handoff`. The budget is
135
+ capped at the 200k long-context pricing boundary: on 1M-window models every request
136
+ past 200k input tokens bills the whole context at a premium rate, so the warnings
137
+ fire at ~140k/~170k instead of deep into the expensive range.
138
+
139
+ ### Disabling hooks per machine
140
+
141
+ A gitignored `ethlete-agents.config.local.json` at the repo root turns generated hooks
142
+ off for one developer without touching any committed file:
143
+
144
+ ```json
145
+ {
146
+ "disableHooks": true
147
+ }
148
+ ```
149
+
150
+ `true` disables every generated hook; an array (`["context-warning"]`) just the named
151
+ ones. The hook scripts read this file at runtime, so toggling takes effect on the next
152
+ prompt - no `sync` needed - and the generated files stay identical on every machine and
153
+ in CI. That is also why the local file supports nothing else: sync output must never
154
+ depend on it, and `sync`/`check` warn if it contains other keys. Add the filename to
155
+ your repo's `.gitignore`.
156
+
85
157
  ## Authoring content
86
158
 
87
159
  `content/rules/<name>.md` for short, always-loaded rules; `content/skills/<name>/SKILL.md`
@@ -94,7 +166,7 @@ description: Read before writing any color, background or border CSS.
94
166
  kind: skill # rule | skill
95
167
  scope: consumer # consumer | sdk | both
96
168
  requires: ['@ethlete/core'] # optional
97
- paths: ['**/*.css'] # optional; becomes Claude `paths`, Cursor `globs`, Copilot `applyTo`
169
+ paths: ['**/*.css'] # optional; becomes Claude `paths` and Cursor `globs` on rules
98
170
  vars: [docsBaseUrl] # optional
99
171
  ---
100
172
  ```
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env python3
2
+ """UserPromptSubmit hook: warn when the context window is getting large.
3
+
4
+ Reads the hook input JSON from stdin, estimates the current context size from
5
+ the last main-chain assistant message in the session transcript, and emits a
6
+ warning (visible to both the user and Claude) when it crosses a threshold.
7
+ Recommends the /handoff skill so work can continue in a fresh session.
8
+
9
+ The thresholds are fractions of a token budget, and the budget is capped at the
10
+ 200k long-context pricing boundary: on models with a larger window, every
11
+ request past that point bills the entire context at the premium rate, which
12
+ costs far more than handing off into a fresh session ever would.
13
+
14
+ Warns once per tier per session (state kept in a temp file); re-arms itself
15
+ if the context shrinks again (e.g. after /compact).
16
+
17
+ Can be disabled per machine via a gitignored ethlete-agents.config.local.json
18
+ at the repo root: {"disableHooks": true} or {"disableHooks": ["context-warning"]}.
19
+
20
+ Fail-safe: any error exits 0 with no output — the hook must never block a prompt.
21
+ """
22
+
23
+ import json
24
+ import os
25
+ import sys
26
+ import tempfile
27
+
28
+ HOOK_NAME = "context-warning"
29
+ LOCAL_CONFIG_FILE = "ethlete-agents.config.local.json"
30
+
31
+ # Warn / critical fire at these fractions of the token budget.
32
+ WARN_FRACTION = 0.70
33
+ CRITICAL_FRACTION = 0.85
34
+
35
+ # On models with a window larger than this, tokens beyond it bill the whole
36
+ # context at the long-context premium rate — so the budget never exceeds it.
37
+ PREMIUM_BOUNDARY = 200_000
38
+
39
+ # Context window (tokens) per model, matched by substring against the model id
40
+ # from the transcript — first match wins. Edit these as model windows change;
41
+ # anything unmatched falls back to DEFAULT_WINDOW.
42
+ CONTEXT_WINDOWS = (
43
+ ("opus-5", 1_000_000),
44
+ ("opus-4-8", 1_000_000),
45
+ ("sonnet-4-5", 1_000_000),
46
+ ("sonnet-5", 1_000_000),
47
+ ("fable-5", 1_000_000),
48
+ # Generic fallbacks — keep these last: the first substring match wins, so a bare
49
+ # "opus"/"sonnet" entry placed above would shadow every versioned entry below it.
50
+ ("opus", 200_000),
51
+ ("sonnet", 200_000),
52
+ ("haiku", 200_000),
53
+ )
54
+ DEFAULT_WINDOW = 200_000
55
+
56
+
57
+ def disabled_locally():
58
+ """True when the local config disables this hook (or all hooks) on this machine."""
59
+ root = os.environ.get("CLAUDE_PROJECT_DIR")
60
+ if not root:
61
+ return False
62
+ try:
63
+ with open(os.path.join(root, LOCAL_CONFIG_FILE), encoding="utf-8") as f:
64
+ disabled = json.load(f).get("disableHooks")
65
+ except (OSError, ValueError, AttributeError):
66
+ return False
67
+ return disabled is True or (isinstance(disabled, list) and HOOK_NAME in disabled)
68
+
69
+
70
+ def window_for(model):
71
+ """Context window for a model id, by first substring match; DEFAULT_WINDOW otherwise."""
72
+ if model:
73
+ for needle, window in CONTEXT_WINDOWS:
74
+ if needle in model:
75
+ return window
76
+ return DEFAULT_WINDOW
77
+
78
+
79
+ def context_state(transcript_path):
80
+ """(tokens, model) from the last main-chain assistant message.
81
+
82
+ tokens ≈ its total input tokens (fresh + cache read + cache creation).
83
+ """
84
+ last_usage = None
85
+ last_model = None
86
+ with open(transcript_path, encoding="utf-8") as f:
87
+ for line in f:
88
+ try:
89
+ obj = json.loads(line)
90
+ except json.JSONDecodeError:
91
+ continue
92
+ if obj.get("type") != "assistant" or obj.get("isSidechain"):
93
+ continue
94
+ message = obj.get("message") or {}
95
+ usage = message.get("usage")
96
+ if usage:
97
+ last_usage = usage
98
+ last_model = message.get("model") or last_model
99
+ if not last_usage:
100
+ return 0, last_model
101
+ tokens = (
102
+ last_usage.get("input_tokens", 0)
103
+ + last_usage.get("cache_read_input_tokens", 0)
104
+ + last_usage.get("cache_creation_input_tokens", 0)
105
+ )
106
+ return tokens, last_model
107
+
108
+
109
+ def messages(tier, tokens, budget, priced):
110
+ """(systemMessage, additionalContext) for a tier.
111
+
112
+ priced: the budget is the pricing boundary, not the window — the reason to
113
+ hand off is cost, not an imminent auto-compact.
114
+ """
115
+ k = f"~{tokens // 1000}k"
116
+ pct = round(tokens / budget * 100)
117
+ budget_k = f"{budget // 1000}k"
118
+ if tier == 2 and priced:
119
+ return (
120
+ f"🔴 Context is at {k} tokens — about to cross the {budget_k} long-context "
121
+ f"pricing boundary, after which every request bills the whole context at the "
122
+ f"premium rate. Run /handoff now and continue in a fresh session.",
123
+ f"[context-warning hook] The context is at {k} tokens — {pct}% of the "
124
+ f"{budget_k} long-context pricing boundary. Past it every request is billed at "
125
+ "the premium rate. Finish only the immediate step, then recommend the user run "
126
+ "/handoff to save state and start a fresh session. Do not start new sub-tasks.",
127
+ )
128
+ if tier == 2:
129
+ return (
130
+ f"🔴 Context is at {k} tokens ({pct}% of the {budget_k} window) — "
131
+ f"auto-compact is imminent. Run /handoff now and continue in a fresh session.",
132
+ f"[context-warning hook] The context window is at {k} tokens — {pct}% of "
133
+ f"this model's {budget_k} window (critical, ≥{int(CRITICAL_FRACTION * 100)}%). "
134
+ "Finish only the immediate step, then recommend the user run /handoff to "
135
+ "save state and start a fresh session. Do not start new sub-tasks.",
136
+ )
137
+ if priced:
138
+ return (
139
+ f"🟡 Context is at {k} tokens, approaching the {budget_k} long-context "
140
+ f"pricing boundary. At the next natural stopping point, consider /handoff "
141
+ f"to continue in a fresh session.",
142
+ f"[context-warning hook] The context is at {k} tokens — {pct}% of the "
143
+ f"{budget_k} long-context pricing boundary, past which every request is "
144
+ "billed at the premium rate. When the current task reaches a natural "
145
+ "stopping point, suggest the user run /handoff to save state and start a "
146
+ "fresh session. Keep working normally until then.",
147
+ )
148
+ return (
149
+ f"🟡 Context is at {k} tokens ({pct}% of the {budget_k} window). At the next "
150
+ f"natural stopping point, consider /handoff to continue in a fresh session.",
151
+ f"[context-warning hook] The context window is at {k} tokens — {pct}% of "
152
+ f"this model's {budget_k} window (≥{int(WARN_FRACTION * 100)}%). When the "
153
+ "current task reaches a natural stopping point, suggest the user run "
154
+ "/handoff to save state and start a fresh session. Keep working normally until then.",
155
+ )
156
+
157
+
158
+ def main():
159
+ if disabled_locally():
160
+ return
161
+ data = json.load(sys.stdin)
162
+ transcript_path = data.get("transcript_path")
163
+ session_id = data.get("session_id", "unknown")
164
+ if not transcript_path or not os.path.isfile(transcript_path):
165
+ return
166
+
167
+ tokens, model = context_state(transcript_path)
168
+ window = window_for(model)
169
+ budget = min(window, PREMIUM_BOUNDARY)
170
+ warn_tokens = int(budget * WARN_FRACTION)
171
+ critical_tokens = int(budget * CRITICAL_FRACTION)
172
+ tier = 2 if tokens >= critical_tokens else 1 if tokens >= warn_tokens else 0
173
+
174
+ state_file = os.path.join(
175
+ tempfile.gettempdir(), f"claude-context-warning-{session_id}"
176
+ )
177
+ prev_tier = 0
178
+ try:
179
+ with open(state_file, encoding="utf-8") as f:
180
+ prev_tier = int(f.read().strip() or 0)
181
+ except (OSError, ValueError):
182
+ pass
183
+
184
+ if tier != prev_tier:
185
+ try:
186
+ with open(state_file, "w", encoding="utf-8") as f:
187
+ f.write(str(tier))
188
+ except OSError:
189
+ pass
190
+
191
+ if tier <= prev_tier:
192
+ return # already warned at this tier (or context shrank — state re-armed above)
193
+
194
+ system_message, additional_context = messages(tier, tokens, budget, budget < window)
195
+
196
+ print(
197
+ json.dumps(
198
+ {
199
+ "systemMessage": system_message,
200
+ "suppressOutput": True,
201
+ "hookSpecificOutput": {
202
+ "hookEventName": "UserPromptSubmit",
203
+ "additionalContext": additional_context,
204
+ },
205
+ }
206
+ )
207
+ )
208
+
209
+
210
+ if __name__ == "__main__":
211
+ try:
212
+ main()
213
+ except Exception:
214
+ pass
215
+ sys.exit(0)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ethlete/agent-rules",
3
- "version": "0.1.0-next.0",
3
+ "version": "0.1.0-next.1",
4
4
  "license": "MIT",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
package/src/index.js CHANGED
@@ -5,17 +5,22 @@ const tslib_1 = require("tslib");
5
5
  const fs_1 = require("fs");
6
6
  const path_1 = require("path");
7
7
  const config_1 = require("./lib/config");
8
+ const migrate_1 = require("./lib/migrate");
8
9
  const sync_1 = require("./lib/sync");
9
10
  const USAGE = `ethlete-agents — compile @ethlete agent rules and skills into your repo
10
11
 
11
- ethlete-agents sync Write the generated rules/skills for every detected agent
12
- ethlete-agents check Exit non-zero when the generated files are out of date (for CI)
13
- ethlete-agents init Write a starter ${config_1.CONFIG_FILE_NAME}
12
+ ethlete-agents sync Write the generated rules/skills for every detected agent
13
+ ethlete-agents check Exit non-zero when the generated files are out of date (for CI)
14
+ ethlete-agents init Write a starter ${config_1.CONFIG_FILE_NAME}
15
+ ethlete-agents migrate Convert the repo to the AGENTS.md + .agents/skills layout:
16
+ CLAUDE.md content moves into AGENTS.md (CLAUDE.md becomes an
17
+ @AGENTS.md import), hand-written .claude/skills move to
18
+ .agents/skills with symlinks left behind, then a sync runs
14
19
 
15
20
  Options
16
21
  --targets <list> Comma-separated subset of: ${config_1.AGENT_TARGETS.join(', ')}
17
22
  --root <path> Repo root to write into (default: current directory)
18
- --dry-run Print what sync would change without writing (sync only)
23
+ --dry-run Print what would change without writing (sync and migrate)
19
24
  `;
20
25
  const readFlag = (args, flag) => {
21
26
  const index = args.indexOf(flag);
@@ -43,6 +48,7 @@ const init = (root) => {
43
48
  targets: (0, config_1.detectTargets)(root),
44
49
  vars: {},
45
50
  exclude: [],
51
+ hooks: [],
46
52
  };
47
53
  (0, fs_1.writeFileSync)(path, `${JSON.stringify(template, null, 2)}\n`, 'utf8');
48
54
  console.log(`Wrote ${config_1.CONFIG_FILE_NAME}. Fill in "vars" for your repo, then run \`ethlete-agents sync\`.`);
@@ -60,6 +66,8 @@ const run = (argv) => {
60
66
  return (0, sync_1.check)(options);
61
67
  case 'init':
62
68
  return init(root);
69
+ case 'migrate':
70
+ return (0, migrate_1.migrate)(Object.assign(Object.assign({}, options), { dryRun: argv.includes('--dry-run') }));
63
71
  default:
64
72
  console.log(USAGE);
65
73
  return command === undefined || command === '--help' || command === '-h' ? 0 : 1;
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/agent-rules/src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/agent-rules/src/index.ts"],"names":[],"mappings":";;;;AACA,2BAA6D;AAC7D,+BAA4B;AAC5B,yCAA2F;AAC3F,2CAAwC;AACxC,qCAAyC;AAEzC,MAAM,KAAK,GAAG;;;;6CAI+B,yBAAgB;;;;;;;kDAOX,sBAAa,CAAC,IAAI,CAAC,IAAI,CAAC;;;CAGzE,CAAC;AAEF,MAAM,QAAQ,GAAG,CAAC,IAAc,EAAE,IAAY,EAAE,EAAE;IAChD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjC,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IAEnC,OAAO,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;AACzB,CAAC,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,IAAc,EAAE,EAAE;IACtC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAExC,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAE3B,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAkB,CAAC;AACtE,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,GAAG,EAAE;IACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAA,iBAAY,EAAC,IAAA,WAAI,EAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAwB,CAAC;IAEhH,OAAO,QAAQ,CAAC,OAAO,CAAC;AAC1B,CAAC,CAAC;AAEF,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,EAAE;IAC5B,MAAM,IAAI,GAAG,IAAA,WAAI,EAAC,IAAI,EAAE,yBAAgB,CAAC,CAAC;IAE1C,IAAI,IAAA,eAAU,EAAC,IAAI,CAAC,EAAE,CAAC;QACrB,OAAO,CAAC,KAAK,CAAC,GAAG,yBAAgB,kBAAkB,CAAC,CAAC;QAErD,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,QAAQ,GAAG;QACf,OAAO,EAAE,IAAA,sBAAa,EAAC,IAAI,CAAC;QAC5B,IAAI,EAAE,EAAE;QACR,OAAO,EAAE,EAAE;QACX,KAAK,EAAE,EAAE;KACV,CAAC;IAEF,IAAA,kBAAa,EAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACtE,OAAO,CAAC,GAAG,CAAC,SAAS,yBAAgB,mEAAmE,CAAC,CAAC;IAE1G,OAAO,CAAC,CAAC;AACX,CAAC,CAAC;AAEF,MAAM,GAAG,GAAG,CAAC,IAAc,EAAE,EAAE;;IAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACxB,MAAM,IAAI,GAAG,MAAA,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,mCAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACvD,MAAM,OAAO,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;IAE9E,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,MAAM;YACT,OAAO,IAAA,WAAI,kCAAM,OAAO,KAAE,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAG,CAAC;QAClE,KAAK,OAAO;YACV,OAAO,IAAA,YAAK,EAAC,OAAO,CAAC,CAAC;QACxB,KAAK,MAAM;YACT,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,KAAK,SAAS;YACZ,OAAO,IAAA,iBAAO,kCAAM,OAAO,KAAE,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAG,CAAC;QACrE;YACE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAEnB,OAAO,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrF,CAAC;AACH,CAAC,CAAC;AAEF,gDAAsB;AAEtB,oFAAoF;AACpF,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;IAC5B,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
@@ -2,6 +2,7 @@ import { ContentScope } from './frontmatter';
2
2
  export declare const AGENT_TARGETS: readonly ["claude", "codex", "cursor", "copilot"];
3
3
  export type AgentTarget = (typeof AGENT_TARGETS)[number];
4
4
  export declare const CONFIG_FILE_NAME = "ethlete-agents.config.json";
5
+ export declare const LOCAL_CONFIG_FILE_NAME = "ethlete-agents.config.local.json";
5
6
  export type SyncConfig = {
6
7
  root: string;
7
8
  targets: AgentTarget[];
@@ -9,10 +10,42 @@ export type SyncConfig = {
9
10
  scopes: ContentScope[];
10
11
  vars: Record<string, string | string[]>;
11
12
  exclude: string[];
13
+ /**
14
+ * Declares that the repo's `CLAUDE.md` is an `@AGENTS.md` import (or symlink). The claude
15
+ * target then skips `.claude/rules/ethlete/` — the rules already reach Claude through the
16
+ * `AGENTS.md` marker block, and a second copy would load twice.
17
+ */
18
+ claudeMdImportsAgentsMd: boolean;
19
+ /** Opt-in Claude Code hooks (they run commands on the developer's machine, so never default). */
20
+ hooks: string[];
12
21
  };
13
22
  /**
14
- * Emit for the agents a repo already uses. Everything falls back to Codex's `AGENTS.md`,
15
- * which Cursor, Copilot and most other agents read as well.
23
+ * The gitignored local config is a per-machine override, read by the generated hook scripts at
24
+ * runtime — never by `sync`. Sync output must be identical on every machine and in CI (the
25
+ * generated files are committed and `check` diffs them), so the only thing a local file can do
26
+ * is turn behavior off after emit: `disableHooks: true` silences every generated hook,
27
+ * `disableHooks: ["context-warning"]` just the named ones.
28
+ */
29
+ export type LocalConfig = {
30
+ disableHooks?: boolean | string[];
31
+ };
32
+ export type LocalConfigState = {
33
+ exists: false;
34
+ } | {
35
+ exists: true;
36
+ valid: false;
37
+ } | {
38
+ exists: true;
39
+ valid: true;
40
+ config: LocalConfig;
41
+ unknownKeys: string[];
42
+ };
43
+ export declare const readLocalConfig: (root: string) => LocalConfigState;
44
+ /**
45
+ * Emit for the agents a repo already uses. `codex` is always included: `AGENTS.md` is the
46
+ * cross-tool standard that Cursor, Copilot and most other agents read as well, and gating it on
47
+ * an existing `AGENTS.md` would mean the file that triggers Codex output is the very file the
48
+ * sync is supposed to create.
16
49
  */
17
50
  export declare const detectTargets: (root: string) => AgentTarget[];
18
51
  export declare const loadConfig: (options: {
package/src/lib/config.js CHANGED
@@ -1,32 +1,52 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.loadConfig = exports.detectTargets = exports.CONFIG_FILE_NAME = exports.AGENT_TARGETS = void 0;
3
+ exports.loadConfig = exports.detectTargets = exports.readLocalConfig = exports.LOCAL_CONFIG_FILE_NAME = exports.CONFIG_FILE_NAME = exports.AGENT_TARGETS = void 0;
4
4
  const fs_1 = require("fs");
5
5
  const path_1 = require("path");
6
6
  const load_content_1 = require("./load-content");
7
7
  exports.AGENT_TARGETS = ['claude', 'codex', 'cursor', 'copilot'];
8
8
  exports.CONFIG_FILE_NAME = 'ethlete-agents.config.json';
9
+ exports.LOCAL_CONFIG_FILE_NAME = 'ethlete-agents.config.local.json';
9
10
  const readRawConfig = (root) => {
10
11
  const path = (0, path_1.join)(root, exports.CONFIG_FILE_NAME);
11
12
  if (!(0, fs_1.existsSync)(path))
12
13
  return {};
13
14
  return JSON.parse((0, fs_1.readFileSync)(path, 'utf8'));
14
15
  };
16
+ const LOCAL_CONFIG_KEYS = ['disableHooks'];
17
+ const readLocalConfig = (root) => {
18
+ const path = (0, path_1.join)(root, exports.LOCAL_CONFIG_FILE_NAME);
19
+ if (!(0, fs_1.existsSync)(path))
20
+ return { exists: false };
21
+ let parsed;
22
+ try {
23
+ parsed = JSON.parse((0, fs_1.readFileSync)(path, 'utf8'));
24
+ }
25
+ catch (_a) {
26
+ return { exists: true, valid: false };
27
+ }
28
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
29
+ return { exists: true, valid: false };
30
+ const config = parsed;
31
+ const unknownKeys = Object.keys(config).filter((key) => !LOCAL_CONFIG_KEYS.includes(key));
32
+ return { exists: true, valid: true, config, unknownKeys };
33
+ };
34
+ exports.readLocalConfig = readLocalConfig;
15
35
  /**
16
- * Emit for the agents a repo already uses. Everything falls back to Codex's `AGENTS.md`,
17
- * which Cursor, Copilot and most other agents read as well.
36
+ * Emit for the agents a repo already uses. `codex` is always included: `AGENTS.md` is the
37
+ * cross-tool standard that Cursor, Copilot and most other agents read as well, and gating it on
38
+ * an existing `AGENTS.md` would mean the file that triggers Codex output is the very file the
39
+ * sync is supposed to create.
18
40
  */
19
41
  const detectTargets = (root) => {
20
- const detected = [];
42
+ const detected = ['codex'];
21
43
  if ((0, fs_1.existsSync)((0, path_1.join)(root, '.claude')))
22
44
  detected.push('claude');
23
- if ((0, fs_1.existsSync)((0, path_1.join)(root, 'AGENTS.md')) || (0, fs_1.existsSync)((0, path_1.join)(root, '.codex')))
24
- detected.push('codex');
25
45
  if ((0, fs_1.existsSync)((0, path_1.join)(root, '.cursor')))
26
46
  detected.push('cursor');
27
47
  if ((0, fs_1.existsSync)((0, path_1.join)(root, '.github')))
28
48
  detected.push('copilot');
29
- return detected.length > 0 ? detected : ['codex'];
49
+ return detected;
30
50
  };
31
51
  exports.detectTargets = detectTargets;
32
52
  const assertKnownTargets = (targets) => {
@@ -36,7 +56,7 @@ const assertKnownTargets = (targets) => {
36
56
  }
37
57
  };
38
58
  const loadConfig = (options) => {
39
- var _a, _b, _c;
59
+ var _a, _b, _c, _d, _e;
40
60
  const { root, targetOverride } = options;
41
61
  const raw = readRawConfig(root);
42
62
  const configured = raw.targets && raw.targets !== 'auto' ? raw.targets : undefined;
@@ -48,6 +68,8 @@ const loadConfig = (options) => {
48
68
  scopes: raw.profile === 'sdk' ? ['both'] : ['consumer', 'both'],
49
69
  vars: Object.assign(Object.assign({}, (0, load_content_1.loadDefaultVars)()), ((_b = raw.vars) !== null && _b !== void 0 ? _b : {})),
50
70
  exclude: (_c = raw.exclude) !== null && _c !== void 0 ? _c : [],
71
+ claudeMdImportsAgentsMd: (_d = raw.claudeMdImportsAgentsMd) !== null && _d !== void 0 ? _d : false,
72
+ hooks: (_e = raw.hooks) !== null && _e !== void 0 ? _e : [],
51
73
  };
52
74
  };
53
75
  exports.loadConfig = loadConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../../libs/agent-rules/src/lib/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../../libs/agent-rules/src/lib/config.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ export * from './config';
2
2
  export * from './filter';
3
3
  export * from './frontmatter';
4
4
  export * from './load-content';
5
+ export * from './migrate';
5
6
  export * from './owned-paths';
6
7
  export * from './plan';
7
8
  export * from './render';
package/src/lib/index.js CHANGED
@@ -5,6 +5,7 @@ tslib_1.__exportStar(require("./config"), exports);
5
5
  tslib_1.__exportStar(require("./filter"), exports);
6
6
  tslib_1.__exportStar(require("./frontmatter"), exports);
7
7
  tslib_1.__exportStar(require("./load-content"), exports);
8
+ tslib_1.__exportStar(require("./migrate"), exports);
8
9
  tslib_1.__exportStar(require("./owned-paths"), exports);
9
10
  tslib_1.__exportStar(require("./plan"), exports);
10
11
  tslib_1.__exportStar(require("./render"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../libs/agent-rules/src/lib/index.ts"],"names":[],"mappings":";;;AAAA,mDAAyB;AACzB,mDAAyB;AACzB,wDAA8B;AAC9B,yDAA+B;AAC/B,wDAA8B;AAC9B,iDAAuB;AACvB,mDAAyB;AACzB,iDAAuB;AACvB,2DAAiC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../libs/agent-rules/src/lib/index.ts"],"names":[],"mappings":";;;AAAA,mDAAyB;AACzB,mDAAyB;AACzB,wDAA8B;AAC9B,yDAA+B;AAC/B,oDAA0B;AAC1B,wDAA8B;AAC9B,iDAAuB;AACvB,mDAAyB;AACzB,iDAAuB;AACvB,2DAAiC"}
@@ -0,0 +1,6 @@
1
+ import { RunOptions } from './sync';
2
+ /**
3
+ * Converts a repo to the `AGENTS.md`-canonical layout and re-syncs. Safe to re-run: every step
4
+ * detects the migrated state and skips itself.
5
+ */
6
+ export declare const migrate: (options: RunOptions) => number;