@ethlete/agent-rules 0.1.0-next.5 → 0.1.0-next.7

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 (87) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +175 -4
  3. package/content/git-hooks/post-checkout.sh +10 -0
  4. package/content/git-hooks/pre-push.sh +5 -0
  5. package/content/hooks/context-warning.py +39 -11
  6. package/content/skills/git-commit/SKILL.md +6 -7
  7. package/content/skills/git-flow/SKILL.md +83 -0
  8. package/content/skills/handoff/SKILL.md +4 -0
  9. package/content/skills/query/SKILL.md +17 -13
  10. package/package.json +12 -1
  11. package/src/index.js +11 -10
  12. package/src/index.js.map +1 -1
  13. package/src/lib/commitlint.d.ts +10 -0
  14. package/src/lib/commitlint.js +51 -0
  15. package/src/lib/commitlint.js.map +1 -0
  16. package/src/lib/config.d.ts +28 -0
  17. package/src/lib/config.js +21 -3
  18. package/src/lib/config.js.map +1 -1
  19. package/src/lib/git-flow/build.d.ts +35 -0
  20. package/src/lib/git-flow/build.js +24 -0
  21. package/src/lib/git-flow/build.js.map +1 -0
  22. package/src/lib/git-flow/config.d.ts +59 -0
  23. package/src/lib/git-flow/config.js +50 -0
  24. package/src/lib/git-flow/config.js.map +1 -0
  25. package/src/lib/git-flow/index.d.ts +5 -0
  26. package/src/lib/git-flow/index.js +9 -0
  27. package/src/lib/git-flow/index.js.map +1 -0
  28. package/src/lib/git-flow/parse.d.ts +49 -0
  29. package/src/lib/git-flow/parse.js +274 -0
  30. package/src/lib/git-flow/parse.js.map +1 -0
  31. package/src/lib/git-flow/start.d.ts +22 -0
  32. package/src/lib/git-flow/start.js +24 -0
  33. package/src/lib/git-flow/start.js.map +1 -0
  34. package/src/lib/git-flow/validate.d.ts +34 -0
  35. package/src/lib/git-flow/validate.js +72 -0
  36. package/src/lib/git-flow/validate.js.map +1 -0
  37. package/src/lib/git-flow-command.d.ts +4 -0
  38. package/src/lib/git-flow-command.js +157 -0
  39. package/src/lib/git-flow-command.js.map +1 -0
  40. package/src/lib/git-flow-repair.d.ts +17 -0
  41. package/src/lib/git-flow-repair.js +150 -0
  42. package/src/lib/git-flow-repair.js.map +1 -0
  43. package/src/lib/git-flow-start.d.ts +20 -0
  44. package/src/lib/git-flow-start.js +147 -0
  45. package/src/lib/git-flow-start.js.map +1 -0
  46. package/src/lib/git.d.ts +27 -0
  47. package/src/lib/git.js +49 -0
  48. package/src/lib/git.js.map +1 -0
  49. package/src/lib/gitlab.d.ts +35 -0
  50. package/src/lib/gitlab.js +98 -0
  51. package/src/lib/gitlab.js.map +1 -0
  52. package/src/lib/jira.d.ts +28 -0
  53. package/src/lib/jira.js +83 -0
  54. package/src/lib/jira.js.map +1 -0
  55. package/src/lib/plan.d.ts +0 -1
  56. package/src/lib/plan.js +20 -4
  57. package/src/lib/plan.js.map +1 -1
  58. package/src/lib/prompt.d.ts +8 -0
  59. package/src/lib/prompt.js +27 -0
  60. package/src/lib/prompt.js.map +1 -0
  61. package/src/lib/render.d.ts +20 -2
  62. package/src/lib/render.js +31 -8
  63. package/src/lib/render.js.map +1 -1
  64. package/src/lib/sync.d.ts +0 -1
  65. package/src/lib/sync.js +2 -2
  66. package/src/lib/sync.js.map +1 -1
  67. package/src/lib/targets/claude-hooks.js +1 -1
  68. package/src/lib/targets/claude-hooks.js.map +1 -1
  69. package/src/lib/targets/claude.js +1 -1
  70. package/src/lib/targets/claude.js.map +1 -1
  71. package/src/lib/targets/codex-hooks.js +1 -1
  72. package/src/lib/targets/codex-hooks.js.map +1 -1
  73. package/src/lib/targets/codex.js +1 -1
  74. package/src/lib/targets/codex.js.map +1 -1
  75. package/src/lib/targets/copilot.js +1 -1
  76. package/src/lib/targets/copilot.js.map +1 -1
  77. package/src/lib/targets/cursor.js +1 -1
  78. package/src/lib/targets/cursor.js.map +1 -1
  79. package/src/lib/targets/git-hooks.d.ts +24 -0
  80. package/src/lib/targets/git-hooks.js +70 -0
  81. package/src/lib/targets/git-hooks.js.map +1 -0
  82. package/src/lib/targets/hooks-shared.d.ts +0 -1
  83. package/src/lib/targets/hooks-shared.js +7 -7
  84. package/src/lib/targets/hooks-shared.js.map +1 -1
  85. package/src/lib/targets/shared.d.ts +0 -1
  86. package/src/lib/targets/shared.js +1 -1
  87. package/src/lib/targets/shared.js.map +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @ethlete/agent-rules
2
2
 
3
+ ## 0.1.0-next.7
4
+
5
+ ### Minor Changes
6
+
7
+ - [#3056](https://github.com/ethlete-io/ethdk/pull/3056) [`8aec5ff`](https://github.com/ethlete-io/ethdk/commit/8aec5ff1a3ea6dd459d0e00bc691468019dcaca6) Thanks [@github-actions](https://github.com/apps/github-actions)! - Generated files no longer stamp the package version into their banner, so a release bump alone no longer reports every repo as out of sync - only real content changes do.
8
+
9
+ ### Patch Changes
10
+
11
+ - [#3056](https://github.com/ethlete-io/ethdk/pull/3056) [`b5f72d6`](https://github.com/ethlete-io/ethdk/commit/b5f72d6bf35dd0e49cf14588b651012fb5d6a7c5) Thanks [@github-actions](https://github.com/apps/github-actions)! - The git-commit guide now presents its format as the repo's own convention unless a commitlint config is actually there, instead of pointing every repo at a `commitlint.config.js` it may not have.
12
+
13
+ ## 0.1.0-next.6
14
+
15
+ ### Minor Changes
16
+
17
+ - [#3055](https://github.com/ethlete-io/ethdk/pull/3055) [`86cd6e9`](https://github.com/ethlete-io/ethdk/commit/86cd6e97072d38436650ddc94a15527da34fa946) Thanks [@github-actions](https://github.com/apps/github-actions)! - Add the git-flow branch convention: a `git-flow` skill, `ethlete-agents git-flow start|check|repair|explain`, opt-in git hooks, and the parser at `@ethlete/agent-rules/git-flow`.
18
+
19
+ ### Patch Changes
20
+
21
+ - [#3055](https://github.com/ethlete-io/ethdk/pull/3055) [`01b0797`](https://github.com/ethlete-io/ethdk/commit/01b0797d80df17e740419402034bc3eec739daaf) Thanks [@github-actions](https://github.com/apps/github-actions)! - The context-warning hook now points at `/ethlete-handoff`, the name the generated skill actually has, instead of a `/handoff` command that does not exist in a consumer repo.
22
+
3
23
  ## 0.1.0-next.5
4
24
 
5
25
  ### Minor Changes
package/README.md CHANGED
@@ -103,7 +103,14 @@ Prettier rewrites them and `check` then reports drift on every run:
103
103
  authoring-side guides are not overwritten by the consumer-side versions.
104
104
  - **`vars`** - values for the template tokens a guide declares. Defaults live in
105
105
  `content/defaults.json`; a guide whose variable has no default and no value is
106
- skipped with a warning rather than emitted with a dangling placeholder.
106
+ skipped with a warning rather than emitted with a dangling placeholder. Some are
107
+ derived from the repo instead of defaulted - the `gitFlow*` ones from the `gitFlow`
108
+ block, and `commitRuleSource` / `commitValidation` from whether a commitlint config
109
+ exists (`commitlint.config.*`, `.commitlintrc*`, or a `commitlint` key in
110
+ `package.json`). Without one, the git-commit guide presents the format as the repo's
111
+ convention and never mentions a `commitlint` run - an agent that goes looking for a
112
+ promised validator and finds nothing reports the discrepancy instead of just
113
+ committing. Setting either one in `vars` overrides the detection.
107
114
  - **`exclude`** - content names to skip entirely.
108
115
  - **`claudeMdImportsAgentsMd`** - set (usually by `migrate`) when `CLAUDE.md` is an
109
116
  `@AGENTS.md` import or symlink; the claude target then skips `.claude/rules/ethlete/`
@@ -113,6 +120,118 @@ Prettier rewrites them and `check` then reports drift on every run:
113
120
  Content that declares `requires` is only emitted when those packages are installed, so
114
121
  a repo without `@ethlete/query` never sees the query guide.
115
122
 
123
+ ## Git flow
124
+
125
+ The branch convention lives in the same config, as one machine-readable grammar that the
126
+ CLI, a git hook, a CI job and `@ethlete/timetrack` all read:
127
+
128
+ ```json
129
+ {
130
+ "gitFlow": {
131
+ "keyPrefixes": ["FIP"],
132
+ "baseBranches": { "development": "next", "production": "main" }
133
+ }
134
+ }
135
+ ```
136
+
137
+ ```bash
138
+ npx ethlete-agents git-flow start FIP-2177 # name it and branch off the right base
139
+ npx ethlete-agents git-flow check # the current branch
140
+ npx ethlete-agents git-flow check "$SOURCE" --target "$TARGET"
141
+ npx ethlete-agents git-flow check --all # adoption report
142
+ npx ethlete-agents git-flow repair dev-game-codes --key FIP-2900
143
+ npx ethlete-agents git-flow explain feat/FIP-2177-user-management
144
+ ```
145
+
146
+ The shapes:
147
+
148
+ | Shape | Branch from | Merges into |
149
+ | ------------------------------------------ | ----------------------- | ------------------------------ |
150
+ | `feat/<KEY>-<subject>` | development | development |
151
+ | `sub/feat/<KEY>-<subject>/<KEY>-<subject>` | the main feature branch | the main feature branch |
152
+ | `release/<YYYY.MM.DD>` | development | development **and** production |
153
+ | `sub/release/<YYYY.MM.DD>/<KEY>-<subject>` | the release branch | the release branch |
154
+ | `hotfix/<KEY>-<subject>` | production | production |
155
+
156
+ **Why nested branches carry a `sub/` prefix.** Git refuses a ref that is both a branch and
157
+ a directory of branches, so `feat/FIP-2177-user-management/FIP-2178-reset` cannot exist
158
+ while `feat/FIP-2177-user-management` does - the push is rejected with `refname conflict`.
159
+ The prefix moves the nested tree out of the way while keeping the parent's full path inside
160
+ the child's name, so the merge request target is still derivable from the name alone. The
161
+ unprefixed spelling still parses, reports why it cannot exist, and `repair` moves it.
162
+ Configurable as `subPrefix`.
163
+
164
+ - **`enforcement`** - `"advisory"` (default) reports everything and blocks nothing, so a
165
+ repo can adopt the convention before it gates on it. `"gated"` applies each rule's
166
+ `severity`. A direct push to a base branch is blocked in both modes, and
167
+ `wrong-mr-target` can be raised to `"error"` on its own without ending the naming
168
+ grace period.
169
+ - **`keyPrefixes`** - the project's issue prefixes. Leave it empty and anything shaped
170
+ like `keyPattern` counts, which reads `chore/angular-22` as issue `ANGULAR-22`.
171
+ - **`severity`** - per rule: `unknown-type`, `missing-key`, `key-case`,
172
+ `missing-subject`, `type-alias`, `deprecated-prefix`, `release-date`,
173
+ `wrong-mr-target`, `protected-push`.
174
+ - **`deprecatedShapes`** - legacy spellings that still classify correctly and only earn a
175
+ rename suggestion. `dev-*` ships as the old spelling of a main feature branch.
176
+
177
+ The grammar is also importable on its own - `@ethlete/agent-rules/git-flow` has no
178
+ dependencies and touches no Node built-ins, so it runs in a browser:
179
+
180
+ ```ts
181
+ import { parseBranch, planStart, resolveGitFlowConfig } from '@ethlete/agent-rules/git-flow';
182
+
183
+ const { storyKey, taskKey, findings } = parseBranch({ branch, config: resolveGitFlowConfig() });
184
+ ```
185
+
186
+ ### `start` - the prospective flow
187
+
188
+ `git-flow start <KEY>` reads the issue from Jira, computes the name from the grammar and
189
+ creates the branch off the correct base. It prints the plan first and asks before writing;
190
+ `--dry-run` stops after the plan and `--yes` skips the question. It refuses on a dirty
191
+ working tree, when the branch already exists, and when the base branch is nowhere to be
192
+ found.
193
+
194
+ A Task with a parent Story nests under that Story's feature branch, which therefore has to
195
+ exist already - `start` says so rather than inventing a parent. `--of <branch>` picks the
196
+ parent explicitly, `--hotfix` branches off production, `--release <date>` makes a release
197
+ branch, and `--subject <text>` skips Jira entirely.
198
+
199
+ Jira needs a host, an email and an API token. Only the host belongs in the committed
200
+ config; the two secrets come from `JIRA_EMAIL` / `JIRA_API_TOKEN` or from the gitignored
201
+ local config.
202
+
203
+ ```json
204
+ {
205
+ "jira": {
206
+ "host": "https://your-team.atlassian.net",
207
+ "subjectField": "customfield_10050",
208
+ "typeByIssueType": { "Bug": "fix" }
209
+ }
210
+ }
211
+ ```
212
+
213
+ - **`subjectField`** - the field holding a Story's branch subject. Without it the summary
214
+ is slugified, which is a paraphrase rather than the agreed subject.
215
+ - **`typeByIssueType`** - the branch type per Jira issue type; anything unlisted becomes
216
+ `feat`. `--type` overrides it per call.
217
+
218
+ ### `repair` - renaming a branch that does not conform
219
+
220
+ `git-flow repair [ref]` derives the conforming name (`--key FIP-2900` when the old name
221
+ carries no issue key, `--to <branch>` to override), renames the branch locally and on the
222
+ remote, and retargets the open merge requests aimed at it through the GitLab API.
223
+ `GITLAB_TOKEN` needs the `api` scope.
224
+
225
+ Everything is checked before the first mutation, and it refuses rather than half-finishing:
226
+
227
+ - An open merge request whose **source** is the branch blocks the repair. GitLab cannot
228
+ move a merge request to another source branch, and closing it would lose its discussion -
229
+ merge or close it first.
230
+ - A branch that is pushed but whose merge requests cannot be listed (no token, or a remote
231
+ that is not GitLab) blocks too. `--no-mr-check` asserts that none point at it.
232
+ - If a retarget fails halfway, the old branch is still there and the recovery commands are
233
+ printed.
234
+
116
235
  ## Hooks (opt-in)
117
236
 
118
237
  Hooks run commands on the developer's machine, so none are emitted by default - opt in
@@ -152,6 +271,54 @@ Available hooks:
152
271
 
153
272
  Hooks can be turned off per machine - see the local config below.
154
273
 
274
+ ## Git hooks (opt-in)
275
+
276
+ Separate from the agent hooks above, and opt-in for the same reason - a generated block
277
+ that can reject a push is a higher-stakes artifact than a markdown one:
278
+
279
+ ```json
280
+ {
281
+ "gitHooks": ["pre-push", "post-checkout"]
282
+ }
283
+ ```
284
+
285
+ Each one is written as an `# ethlete:git-flow:start` … `end` block **appended** to your
286
+ `.husky/<name>`, so an existing hook there (a git-lfs hook, typically) keeps working and
287
+ keeps reading stdin first - which is why the block never reads stdin itself. Removing the
288
+ name from `gitHooks` takes the block back out and leaves the rest of the file alone.
289
+
290
+ - **`pre-push`** - runs `git-flow check --push` on the current branch. In `advisory` mode
291
+ only a direct push to a base branch can actually stop it.
292
+ - **`post-checkout`** - reports a non-conforming name on a branch that is on no remote yet,
293
+ which is the whole window in which renaming it is free.
294
+
295
+ Only `.husky/` is written, never `.git/hooks/`: the generated files are committed and CI's
296
+ `check` diffs them, so a hook outside the working tree could never be in sync. Without a
297
+ `.husky/` directory `sync` warns and writes nothing. The block calls
298
+ `node_modules/.bin/ethlete-agents` directly rather than through `npx`, so a repo where the
299
+ package is missing gets silence instead of a registry lookup that would fail the push.
300
+ `ETHLETE_GIT_FLOW_SKIP=1` silences both hooks on one machine.
301
+
302
+ ## CI job
303
+
304
+ On GitLab, the merge request target is the half no local hook can see. The job needs no
305
+ configuration beyond the predefined variables:
306
+
307
+ ```yaml
308
+ Git Flow:
309
+ stage: Checks
310
+ rules:
311
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event"
312
+ allow_failure: true
313
+ script:
314
+ - >
315
+ npx ethlete-agents git-flow check "$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME"
316
+ --target "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME"
317
+ ```
318
+
319
+ `allow_failure: true` on top of `advisory` mode is deliberate belt and braces: the job
320
+ reports for a whole grace period before it can ever be the reason a merge request is red.
321
+
155
322
  ## Per-machine local config
156
323
 
157
324
  A gitignored `ethlete-agents.config.local.json` at the repo root holds the values that
@@ -160,7 +327,8 @@ differ per developer, without touching any committed file:
160
327
  ```json
161
328
  {
162
329
  "disableHooks": true,
163
- "sdkSourcePath": "/absolute/path/to/ethlete-sdk"
330
+ "sdkSourcePath": "/absolute/path/to/ethlete-sdk",
331
+ "jira": { "email": "you@example.com", "token": "…" }
164
332
  }
165
333
  ```
166
334
 
@@ -168,8 +336,11 @@ differ per developer, without touching any committed file:
168
336
  (`["context-warning"]`) just the named ones. The hook scripts read the file at
169
337
  runtime, so toggling takes effect on the next prompt - no `sync` needed.
170
338
  - **`disableAutoHandoffSave`** - keeps the `context-warning` hook's tiered warnings but
171
- drops the auto-mode escalation: at the critical tier it recommends `/handoff` instead
172
- of saving the handoff file itself.
339
+ drops the auto-mode escalation: at the critical tier it recommends `/ethlete-handoff`
340
+ instead of saving the handoff file itself.
341
+ - **`jira`** - the credentials `git-flow start` needs (`host`, `email`, `token`). This is
342
+ the one place in a repo a secret may sit, and only because the file is gitignored;
343
+ `JIRA_EMAIL` / `JIRA_API_TOKEN` in the environment are the alternative and win over it.
173
344
  - **`sdkSourcePath`** - a local `ethlete-sdk` checkout. The `sdk-source` and
174
345
  `sdk-local-build` skills read it when the agent needs the SDK's own sources, or has to
175
346
  build the SDK and install it here through a `file:` dependency. A relative path is
@@ -0,0 +1,10 @@
1
+ if [ "$3" = "1" ] && [ -z "$ETHLETE_GIT_FLOW_SKIP" ] && [ -x node_modules/.bin/ethlete-agents ]; then
2
+ ethlete_branch=$(git rev-parse --abbrev-ref HEAD)
3
+
4
+ # Only while the branch is on no remote: that is the whole window in which renaming it is free.
5
+ if [ -z "$(git for-each-ref --format='%(refname)' "refs/remotes/*/$ethlete_branch")" ]; then
6
+ node_modules/.bin/ethlete-agents git-flow check || true
7
+ fi
8
+
9
+ unset ethlete_branch
10
+ fi
@@ -0,0 +1,5 @@
1
+ # Never `npx`: it reaches the registry when the package is absent, and a network error here would
2
+ # reject the push. A missing binary must make this block do nothing at all.
3
+ if [ -z "$ETHLETE_GIT_FLOW_SKIP" ] && [ -x node_modules/.bin/ethlete-agents ]; then
4
+ node_modules/.bin/ethlete-agents git-flow check --push || exit 1
5
+ fi
@@ -21,8 +21,10 @@ that matter here, all captured in AGENT_PROFILES:
21
21
  bills the entire context at the premium rate, which costs far more than
22
22
  handing off into a fresh session ever would. Codex has no such boundary, so
23
23
  its budget is just the window.
24
- * How a handoff is invoked. Claude has /handoff and /clear; Codex has neither
25
- and reads the skill from disk.
24
+ * How a handoff is invoked. Claude has a slash command and /clear; Codex has
25
+ neither and reads the skill from disk. The skill's own name differs per repo
26
+ (`ethlete-handoff` where the generator installed it, `handoff` where the repo
27
+ ships its own copy), so it is resolved at runtime — see handoff_skill().
26
28
 
27
29
  At the critical tier, if the session's permission_mode is "auto", the
28
30
  instruction escalates from "recommend" to "just do it": auto mode already
@@ -85,14 +87,14 @@ AGENT_PROFILES = {
85
87
  "default_window": None,
86
88
  "auto_modes": ("auto",),
87
89
  "emits_system_message": True,
88
- "act_now": "Run /handoff now and continue in a fresh session.",
89
- "act_later": "consider /handoff to continue in a fresh session",
90
- "recommend": "recommend the user run /handoff to save state and start a fresh session",
91
- "suggest": "suggest the user run /handoff to save state and start a fresh session",
90
+ "act_now": "Run /{handoff} now and continue in a fresh session.",
91
+ "act_later": "consider /{handoff} to continue in a fresh session",
92
+ "recommend": "recommend the user run /{handoff} to save state and start a fresh session",
93
+ "suggest": "suggest the user run /{handoff} to save state and start a fresh session",
92
94
  "save_now": (
93
95
  "run the handoff skill's save mode right now (finish only an in-flight atomic "
94
96
  "edit first, nothing new). Then tell the user exactly which handoff file was "
95
- "written and that they should run /clear, then '/handoff resume <slug>', to "
97
+ "written and that they should run /clear, then '/{handoff} resume <slug>', to "
96
98
  "continue — clearing and resuming can't be done programmatically, so this is "
97
99
  "the one step still on them."
98
100
  ),
@@ -112,15 +114,15 @@ AGENT_PROFILES = {
112
114
  "act_later": "consider saving a handoff and continuing in a fresh session",
113
115
  "recommend": (
114
116
  "tell the user you are near the context limit, then follow "
115
- ".agents/skills/ethlete-handoff/SKILL.md to save a handoff and have them start a "
117
+ ".agents/skills/{handoff}/SKILL.md to save a handoff and have them start a "
116
118
  "fresh session"
117
119
  ),
118
120
  "suggest": (
119
- "suggest saving a handoff via .agents/skills/ethlete-handoff/SKILL.md and "
121
+ "suggest saving a handoff via .agents/skills/{handoff}/SKILL.md and "
120
122
  "continuing in a fresh session"
121
123
  ),
122
124
  "save_now": (
123
- "follow .agents/skills/ethlete-handoff/SKILL.md and save a handoff right now "
125
+ "follow .agents/skills/{handoff}/SKILL.md and save a handoff right now "
124
126
  "(finish only an in-flight atomic edit first, nothing new). Then tell the user "
125
127
  "exactly which handoff file was written and that they should start a fresh "
126
128
  "codex session and resume from it."
@@ -129,6 +131,29 @@ AGENT_PROFILES = {
129
131
  }
130
132
 
131
133
 
134
+ def handoff_skill(root):
135
+ """Name of the handoff skill installed in this repo.
136
+
137
+ The generator prefixes every skill it writes, so the command is /ethlete-handoff in a
138
+ consumer repo — but a repo that excludes the generated copy and ships its own (the SDK
139
+ itself) has a plain /handoff instead. Naming the wrong one sends the user to a slash
140
+ command that does not exist, so it is read off disk rather than assumed.
141
+ """
142
+ for name in ("ethlete-handoff", "handoff"):
143
+ for skills_dir in (".claude/skills", ".agents/skills"):
144
+ if os.path.isdir(os.path.join(root or ".", skills_dir, name)):
145
+ return name
146
+ return "ethlete-handoff"
147
+
148
+
149
+ def resolve_profile(profile, skill):
150
+ """Fills the installed handoff skill's name into a profile's message fragments."""
151
+ return {
152
+ key: value.replace("{handoff}", skill) if isinstance(value, str) else value
153
+ for key, value in profile.items()
154
+ }
155
+
156
+
132
157
  def agent_name(argv):
133
158
  """The --agent value, defaulting to claude — older registrations pass no flag."""
134
159
  for index, arg in enumerate(argv):
@@ -336,10 +361,13 @@ def main():
336
361
  return
337
362
 
338
363
  data = json.load(sys.stdin)
339
- local_config = load_local_config(repo_root(data))
364
+ root = repo_root(data)
365
+ local_config = load_local_config(root)
340
366
  if disabled_locally(local_config):
341
367
  return
342
368
 
369
+ profile = resolve_profile(profile, handoff_skill(root))
370
+
343
371
  transcript_path = data.get("transcript_path")
344
372
  session_id = data.get("session_id", "unknown")
345
373
  auto_mode = data.get("permission_mode") in profile["auto_modes"] and not auto_handoff_save_disabled(local_config)
@@ -1,23 +1,22 @@
1
1
  ---
2
2
  name: git-commit
3
- description: How to write git commits in this repo - commitlint format (type(scope): Subject), lean messages, no trailers. Read before committing anything (e.g. the user says "commit this").
3
+ description: How to write git commits in this repo - conventional format (type(scope): Subject), lean messages, no trailers. Read before committing anything (e.g. the user says "commit this").
4
4
  kind: skill
5
5
  scope: both
6
- vars: [commitScopes]
6
+ vars: [commitScopes, commitRuleSource, commitValidation]
7
7
  ---
8
8
 
9
9
  # Git commits
10
10
 
11
- Commits are **lean** and follow the **commitlint rules** in `commitlint.config.js`
12
- (conventional commits with a required scope):
11
+ Commits are **lean** and follow {%commitRuleSource%}:
13
12
 
14
- - **Format: `type(scope): Subject`** - one line. All three parts are enforced:
13
+ - **Format: `type(scope): Subject`** - one line, all three parts required:
15
14
  - `type` ∈ `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`,
16
15
  `build`, `ci`, `chore`, `revert`
17
- - `scope` is **required**, ∈ {%commitScopes%}
16
+ - `scope` ∈ {%commitScopes%}
18
17
  - Subject is **sentence-case** ("Add the search filter", not
19
18
  "add the search filter")
20
- - When unsure a message passes, check it: `echo "<msg>" | npx commitlint`.
19
+ - {%commitValidation%}
21
20
  - Add a short body only when the change genuinely needs context that the diff
22
21
  can't convey.
23
22
  - **No trailers.** Never append `Co-Authored-By`, `Claude-Session`, or similar
@@ -0,0 +1,83 @@
1
+ ---
2
+ name: git-flow
3
+ description: How to name and base a branch in this repo, and what its merge request must target - feature, sub-feature, release, release fix and hotfix. Read BEFORE creating a branch, opening a merge request, or choosing what to branch from (e.g. the user says "start work on FIP-2177" or "open an MR").
4
+ kind: skill
5
+ scope: both
6
+ vars: [gitFlowDevelopmentBranch, gitFlowProductionBranch, gitFlowTypes, gitFlowEnforcement, gitFlowSubPrefix]
7
+ ---
8
+
9
+ # Git flow
10
+
11
+ Work is organised in two levels: a **main feature branch** per Jira Story, into which
12
+ **sub-feature branches** (one per Task) are merged after review. The main feature branch is
13
+ what gets deployed to a test environment and, once accepted, merged into
14
+ `{%gitFlowDevelopmentBranch%}`.
15
+
16
+ **Never name a branch by hand - `start` does it from the grammar:**
17
+
18
+ ```bash
19
+ npx ethlete-agents git-flow start FIP-2178 # reads the issue, names it, branches off the right base
20
+ npx ethlete-agents git-flow check # is the current branch conforming?
21
+ npx ethlete-agents git-flow explain <branch> # what the parser sees, and what it expects
22
+ npx ethlete-agents git-flow repair <branch> # rename a non-conforming one, retarget its MRs
23
+ ```
24
+
25
+ `start` prints its plan (branch, base, MR target) and asks before writing anything; add
26
+ `--dry-run` to see the plan alone. It refuses on a dirty working tree, and a Task nests under
27
+ its parent Story's branch, so that branch has to exist first.
28
+
29
+ ## The five shapes
30
+
31
+ | Shape | Issue | Branch from | MR targets |
32
+ | --------------------------------------------------------------------------------- | ----- | ------------------------------ | ---------------------------------------------------------------- |
33
+ | `feat/FIP-2177-user-management` | Story | `{%gitFlowDevelopmentBranch%}` | `{%gitFlowDevelopmentBranch%}` |
34
+ | `{%gitFlowSubPrefix%}/feat/FIP-2177-user-management/FIP-2178-user-password-reset` | Task | the main feature branch | the main feature branch |
35
+ | `release/2026.04.28` | - | `{%gitFlowDevelopmentBranch%}` | `{%gitFlowDevelopmentBranch%}` and `{%gitFlowProductionBranch%}` |
36
+ | `{%gitFlowSubPrefix%}/release/2026.04.28/FIP-2222-button-not-visible` | Bug | the release branch | the release branch |
37
+ | `hotfix/FIP-2799-password-recovery-broken` | Bug | `{%gitFlowProductionBranch%}` | `{%gitFlowProductionBranch%}` |
38
+
39
+ - The **type** is one of {%gitFlowTypes%}, and a nested branch carries its parent's **full**
40
+ name - that path is what makes the parent machine-readable.
41
+ - The **key** is the Jira issue, uppercase, immediately after the type. The **subject** is the
42
+ Story's subject meta field in kebab-case, not a paraphrase of the summary.
43
+ - A branch with no key still works, but nothing can attribute it to an issue. Add the key.
44
+
45
+ ### Why a nested branch starts with `{%gitFlowSubPrefix%}/`
46
+
47
+ Git refuses a ref that is both a branch and a directory of branches. So
48
+ `feat/FIP-2177-user-management/FIP-2178-user-password-reset` **cannot exist** while
49
+ `feat/FIP-2177-user-management` does - git rejects it locally and the push comes back as
50
+ `refname conflict`. The prefix moves the nested tree out of the way and keeps the parent's
51
+ full path inside the child's name, so the MR target is still readable off the name.
52
+
53
+ Do not "fix" a name by dropping the prefix; the branch it produces cannot be created.
54
+
55
+ ## Rules that are not about naming
56
+
57
+ - **Never rebase a shared branch.** A main feature branch is published and other people's
58
+ sub-features are based on it - bring it up to date by **merging**
59
+ `{%gitFlowDevelopmentBranch%}` into it. Rebase only a local branch you have not pushed.
60
+ - **A sub-feature merges into its parent, never straight into
61
+ `{%gitFlowDevelopmentBranch%}`** - that is the whole point of the two levels, since the
62
+ parent is what gets tested as a unit.
63
+ - **Delete the source branch on merge** (the checkbox in the merge request) for sub-features,
64
+ release fixes and merged main features.
65
+ - **A hotfix branches off `{%gitFlowProductionBranch%}`** and, after rollout, that branch is
66
+ merged back into `{%gitFlowDevelopmentBranch%}` so the two do not drift.
67
+ - **Never push directly to `{%gitFlowDevelopmentBranch%}` or
68
+ `{%gitFlowProductionBranch%}`.** Open a merge request; it needs another developer's review.
69
+
70
+ ## Enforcement is `{%gitFlowEnforcement%}`
71
+
72
+ In `advisory` mode every rule reports and nothing blocks - the older naming shapes (`feature/`
73
+ instead of `feat/`, a lowercase key, no key at all, a `dev-*` integration branch) are accepted
74
+ on purpose while the team adapts. Report the suggestion, do not "fix" someone's existing
75
+ branch unasked - `repair` is how a rename happens, and only when asked for.
76
+
77
+ `dev-*` is the **old spelling of a main feature branch**, not a stray name: sub-features
78
+ legitimately target it. Leave a live one alone.
79
+
80
+ ## Commit messages are a separate thing
81
+
82
+ Commits stay conventional (`feat(platform): Prefer a player's common name`) and carry **no
83
+ issue key** - the branch already has it. See {%skill:git-commit%}.
@@ -15,6 +15,10 @@ continue in a fresh session. Two modes:
15
15
  - **save** ("handoff", "wrap up") - write a handoff file.
16
16
  - **resume** ("continue from the handoff") - read one and continue the work.
17
17
 
18
+ This skill is installed as `ethlete-handoff`, so under Claude Code the commands
19
+ are `/ethlete-handoff` and `/ethlete-handoff resume [name]` - there is no
20
+ `/handoff`. Name them that way whenever you tell the user what to run.
21
+
18
22
  Handoff files live in `{%handoffDir%}/` (gitignored - they are personal,
19
23
  ephemeral working state, not team docs).
20
24
 
@@ -16,17 +16,17 @@ paged queries, bearer auth, GraphQL, and a socket.io realtime client.
16
16
  non-trivial query work.** This guide is the index plus the load-bearing facts, so you
17
17
  don't re-derive them from source.
18
18
 
19
- | Page | Covers |
20
- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
21
- | {%docsBaseUrl%}/query/ | Overview + the two-generations note |
22
- | {%docsBaseUrl%}/query/queries | **Start here** - client, creators, the query object's signals, auto-execution |
23
- | {%docsBaseUrl%}/query/features | `withArgs`, `withPolling`, `withAutoRefresh`, side-effect handlers |
24
- | {%docsBaseUrl%}/query/http | REST creators, typing requests, response transforms, upload progress |
25
- | {%docsBaseUrl%}/query/auth | Bearer auth: login/refresh, auto token refresh, multi-tab sync |
26
- | {%docsBaseUrl%}/query/caching · `/stacks` · `/errors` · `/gql` · `/ws` | Caching/dedup, pagination, error/retry, GraphQL, WebSockets |
27
- | {%docsBaseUrl%}/query/multi-tab | Opt-in cross-tab sync: shared responses, per-key polling election, mutation fan-out |
28
- | {%docsBaseUrl%}/query/query-forms | Router-synced filter/search forms |
29
- | {%docsBaseUrl%}/query/legacy | The maintenance-mode `V2QueryClient` |
19
+ | Page | Covers |
20
+ | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
21
+ | {%docsBaseUrl%}/query/ | Overview + the two-generations note |
22
+ | {%docsBaseUrl%}/query/queries | **Start here** - client, creators, the query object's signals, auto-execution |
23
+ | {%docsBaseUrl%}/query/features | `withArgs`, `withPolling`, `withLongPolling`, `withAutoRefresh`, side-effect handlers |
24
+ | {%docsBaseUrl%}/query/http | REST creators, typing requests, response transforms, upload progress |
25
+ | {%docsBaseUrl%}/query/auth | Bearer auth: login/refresh, auto token refresh, multi-tab sync |
26
+ | {%docsBaseUrl%}/query/caching · `/stacks` · `/errors` · `/gql` · `/ws` | Caching/dedup, pagination, error/retry, GraphQL, WebSockets |
27
+ | {%docsBaseUrl%}/query/multi-tab | Opt-in cross-tab sync: shared responses, per-key polling election, mutation fan-out |
28
+ | {%docsBaseUrl%}/query/query-forms | Router-synced filter/search forms |
29
+ | {%docsBaseUrl%}/query/legacy | The maintenance-mode `V2QueryClient` |
30
30
 
31
31
  ## Two generations - use the current one
32
32
 
@@ -77,8 +77,8 @@ raw `toObservable`). It emits `null` first - `pipe(filter(r => r !== null))`.
77
77
  - **`withArgs(() => ({ pathParams, queryParams, body }))`** - runs like a `computed`;
78
78
  re-runs when a signal it reads changes and re-executes the query. This is how you
79
79
  drive **search-as-you-type**: back it with a search signal
80
- (`withArgs(() => ({ queryParams: { search: this.search() } }))`). Return
81
- `CLEAR_QUERY_ARGS` to reset args to `null` (pauses polling/auto-refresh).
80
+ (`withArgs(() => ({ queryParams: { search: this.search() } }))`). Return `null`
81
+ to park the query - args reset to `null`, pausing polling/auto-refresh.
82
82
  - **Prefer `withArgs` over passing `args` to `execute()`.** Args declared on the query
83
83
  stay reactive: a `GET` re-executes itself when they change, and `withPolling` /
84
84
  `withAutoRefresh` restart off the same signal - none of which happens for args handed
@@ -86,6 +86,10 @@ raw `toObservable`). It emits `null` first - `pipe(filter(r => r !== null))`.
86
86
  place a mutation is just `.execute()`, which reuses the current `args()`. Reserve
87
87
  `execute({ args })` for a one-off payload no signal holds (a form submit).
88
88
  - `withPolling({ interval })`, `withAutoRefresh({ onSignalChanges: [...] })`.
89
+ - **`withLongPolling({ nextArgs })`** for a completion-driven chain instead of an interval: each
90
+ round starts once the previous settled, with args (a cursor) derived from its response. `nextArgs`
91
+ returning `null` ends the chain. Not `withPolling` with a small interval - and the two throw when
92
+ combined.
89
93
  - Side-effects: `withSuccessHandling`, `withErrorHandling`, `withLogging`.
90
94
 
91
95
  There is no built-in debounce operator - dedup/caching handles repeated identical
package/package.json CHANGED
@@ -1,8 +1,19 @@
1
1
  {
2
2
  "name": "@ethlete/agent-rules",
3
- "version": "0.1.0-next.5",
3
+ "version": "0.1.0-next.7",
4
4
  "license": "MIT",
5
5
  "type": "commonjs",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./src/index.d.ts",
9
+ "default": "./src/index.js"
10
+ },
11
+ "./git-flow": {
12
+ "types": "./src/lib/git-flow/index.d.ts",
13
+ "default": "./src/lib/git-flow/index.js"
14
+ },
15
+ "./package.json": "./package.json"
16
+ },
6
17
  "main": "./src/index.js",
7
18
  "typings": "./src/index.d.ts",
8
19
  "bin": {
package/src/index.js CHANGED
@@ -5,6 +5,7 @@ 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 git_flow_command_1 = require("./lib/git-flow-command");
8
9
  const migrate_1 = require("./lib/migrate");
9
10
  const sync_1 = require("./lib/sync");
10
11
  const USAGE = `ethlete-agents — compile @ethlete agent rules and skills into your repo
@@ -12,6 +13,8 @@ const USAGE = `ethlete-agents — compile @ethlete agent rules and skills into y
12
13
  ethlete-agents sync Write the generated rules/skills for every detected agent
13
14
  ethlete-agents check Exit non-zero when the generated files are out of date (for CI)
14
15
  ethlete-agents init Write a starter ${config_1.CONFIG_FILE_NAME}
16
+ ethlete-agents git-flow Name, check and repair branches against the repo's git flow
17
+ (start, check, repair, explain)
15
18
  ethlete-agents migrate Convert the repo to the AGENTS.md + .agents/skills layout:
16
19
  CLAUDE.md content moves into AGENTS.md (CLAUDE.md becomes an
17
20
  @AGENTS.md import), hand-written .claude/skills move to
@@ -34,10 +37,6 @@ const parseTargets = (args) => {
34
37
  return undefined;
35
38
  return raw.split(',').map((entry) => entry.trim());
36
39
  };
37
- const readVersion = () => {
38
- const manifest = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '..', 'package.json'), 'utf8'));
39
- return manifest.version;
40
- };
41
40
  const init = (root) => {
42
41
  const path = (0, path_1.join)(root, config_1.CONFIG_FILE_NAME);
43
42
  if ((0, fs_1.existsSync)(path)) {
@@ -58,7 +57,7 @@ const run = (argv) => {
58
57
  var _a;
59
58
  const command = argv[0];
60
59
  const root = (_a = readFlag(argv, '--root')) !== null && _a !== void 0 ? _a : process.cwd();
61
- const options = { root, version: readVersion(), targets: parseTargets(argv) };
60
+ const options = { root, targets: parseTargets(argv) };
62
61
  switch (command) {
63
62
  case 'sync':
64
63
  return (0, sync_1.sync)(Object.assign(Object.assign({}, options), { dryRun: argv.includes('--dry-run') }));
@@ -66,6 +65,8 @@ const run = (argv) => {
66
65
  return (0, sync_1.check)(options);
67
66
  case 'init':
68
67
  return init(root);
68
+ case 'git-flow':
69
+ return (0, git_flow_command_1.gitFlowCommand)({ root, argv: argv.slice(1) });
69
70
  case 'migrate':
70
71
  return (0, migrate_1.migrate)(Object.assign(Object.assign({}, options), { dryRun: argv.includes('--dry-run') }));
71
72
  default:
@@ -76,12 +77,12 @@ const run = (argv) => {
76
77
  tslib_1.__exportStar(require("./lib"), exports);
77
78
  // Guarded so the package can also be imported as a library without running the CLI.
78
79
  if (require.main === module) {
79
- try {
80
- process.exit(run(process.argv.slice(2)));
81
- }
82
- catch (error) {
80
+ Promise.resolve()
81
+ .then(() => run(process.argv.slice(2)))
82
+ .then((code) => process.exit(code))
83
+ .catch((error) => {
83
84
  console.error(error instanceof Error ? error.message : error);
84
85
  process.exit(1);
85
- }
86
+ });
86
87
  }
87
88
  //# sourceMappingURL=index.js.map
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":"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"}