@ethlete/agent-rules 0.1.0-next.3 → 0.1.0-next.31

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 (200) hide show
  1. package/AGENTS.md +28 -0
  2. package/CHANGELOG.md +480 -0
  3. package/CLAUDE.md +1 -0
  4. package/README.md +362 -29
  5. package/THIRD-PARTY-LICENSES.md +35 -0
  6. package/content/defaults.json +1 -1
  7. package/content/git-hooks/post-checkout.sh +10 -0
  8. package/content/git-hooks/pre-push.sh +5 -0
  9. package/content/hooks/context-warning.py +872 -110
  10. package/content/hooks/subagent-model-policy.py +194 -0
  11. package/content/mods/.claude-plugin/marketplace.json +11 -0
  12. package/content/mods/plugins/e2e-progress/.claude-plugin/plugin.json +6 -0
  13. package/content/mods/plugins/e2e-progress/hooks/hooks.json +1 -0
  14. package/content/mods/plugins/e2e-progress/hooks/register.tsx +442 -0
  15. package/content/mods/plugins/e2e-progress/tsconfig.json +3 -0
  16. package/content/mods/plugins/e2e-progress/types/index.d.ts +23 -0
  17. package/content/output-styles/ste-clarity.md +131 -0
  18. package/content/rules/agent-sessions-wake-up.md +11 -0
  19. package/content/rules/app-styling.md +66 -0
  20. package/content/rules/comments.md +4 -1
  21. package/content/rules/lint-and-format.md +7 -5
  22. package/content/rules/nx-layout.md +50 -0
  23. package/content/rules/reactive-state.md +1 -1
  24. package/content/rules/styling.md +9 -7
  25. package/content/rules/subagent-models.md +39 -0
  26. package/content/skills/agent-sessions/SKILL.md +81 -0
  27. package/content/skills/agent-sessions/agent-sessions.mjs +498 -0
  28. package/content/skills/angular-patterns/SKILL.md +30 -7
  29. package/content/skills/api-source/SKILL.md +156 -0
  30. package/content/skills/app-testing/SKILL.md +141 -0
  31. package/content/skills/codex-subagent/SKILL.md +90 -0
  32. package/content/skills/codex-subagent/codex-agent.mjs +229 -0
  33. package/content/skills/design-exploration/SKILL.md +687 -0
  34. package/content/skills/design-exploration/check-story.mjs +139 -0
  35. package/content/skills/design-exploration/shoot-template.mjs +61 -0
  36. package/content/skills/domain-modeling/ADR-FORMAT.md +47 -0
  37. package/content/skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
  38. package/content/skills/domain-modeling/SKILL.md +80 -0
  39. package/content/skills/e2e-triage/SKILL.md +80 -0
  40. package/content/skills/figma-export/SKILL.md +191 -0
  41. package/content/skills/figma-export/dump-figma-layers.py +83 -0
  42. package/content/skills/figma-export/dump-figma-svg.py +235 -0
  43. package/content/skills/figma-export/measure-template.mjs +87 -0
  44. package/content/skills/git-commit/SKILL.md +11 -7
  45. package/content/skills/git-flow/SKILL.md +87 -0
  46. package/content/skills/grill-with-docs/SKILL.md +13 -0
  47. package/content/skills/grilling/SKILL.md +51 -0
  48. package/content/skills/handoff/SKILL.md +125 -5
  49. package/content/skills/query/SKILL.md +165 -64
  50. package/content/skills/rxjs-signals/SKILL.md +33 -4
  51. package/content/skills/sdk-docs/SKILL.md +50 -14
  52. package/content/skills/sdk-local-build/SKILL.md +26 -8
  53. package/content/skills/sdk-local-build/sdk-local-baseline.mjs +64 -0
  54. package/content/skills/sdk-source/SKILL.md +37 -34
  55. package/content/skills/sdk-update/SKILL.md +103 -0
  56. package/content/skills/story-styling/SKILL.md +10 -8
  57. package/content/skills/styleguide/SKILL.md +12 -7
  58. package/content/skills/styleguide/assets.md +54 -0
  59. package/content/skills/styleguide/changesets.md +17 -0
  60. package/content/skills/styleguide/file-structure.md +73 -0
  61. package/content/skills/styleguide/lint-rule-lookup.md +51 -0
  62. package/content/skills/styleguide/storybook-structure.md +25 -0
  63. package/content/skills/theming/SKILL.md +50 -28
  64. package/content/skills/timetrack/SKILL.md +397 -0
  65. package/content/skills/verify-in-app/SKILL.md +107 -0
  66. package/migrations/app-styling-utilities.md +63 -0
  67. package/migrations/list-state-query-form.md +20 -0
  68. package/migrations/nx-layout.md +29 -0
  69. package/migrations/sdk-components-over-hand-built-ui.md +27 -0
  70. package/migrations/search-query-field.md +52 -0
  71. package/migrations.json +48 -0
  72. package/package.json +19 -3
  73. package/src/index.js +85 -30
  74. package/src/index.js.map +1 -1
  75. package/src/lib/cli-args.d.ts +9 -0
  76. package/src/lib/cli-args.js +48 -0
  77. package/src/lib/cli-args.js.map +1 -0
  78. package/src/lib/commitlint.d.ts +10 -0
  79. package/src/lib/commitlint.js +51 -0
  80. package/src/lib/commitlint.js.map +1 -0
  81. package/src/lib/config.d.ts +41 -5
  82. package/src/lib/config.js +117 -10
  83. package/src/lib/config.js.map +1 -1
  84. package/src/lib/filter.d.ts +2 -0
  85. package/src/lib/filter.js +4 -4
  86. package/src/lib/filter.js.map +1 -1
  87. package/src/lib/frontmatter.d.ts +2 -0
  88. package/src/lib/frontmatter.js +33 -5
  89. package/src/lib/frontmatter.js.map +1 -1
  90. package/src/lib/git-flow/build.d.ts +35 -0
  91. package/src/lib/git-flow/build.js +24 -0
  92. package/src/lib/git-flow/build.js.map +1 -0
  93. package/src/lib/git-flow/config.d.ts +59 -0
  94. package/src/lib/git-flow/config.js +50 -0
  95. package/src/lib/git-flow/config.js.map +1 -0
  96. package/src/lib/git-flow/index.d.ts +6 -0
  97. package/src/lib/git-flow/index.js +23 -0
  98. package/src/lib/git-flow/index.js.map +1 -0
  99. package/src/lib/git-flow/parse.d.ts +49 -0
  100. package/src/lib/git-flow/parse.js +300 -0
  101. package/src/lib/git-flow/parse.js.map +1 -0
  102. package/src/lib/git-flow/rename.d.ts +24 -0
  103. package/src/lib/git-flow/rename.js +70 -0
  104. package/src/lib/git-flow/rename.js.map +1 -0
  105. package/src/lib/git-flow/start.d.ts +49 -0
  106. package/src/lib/git-flow/start.js +57 -0
  107. package/src/lib/git-flow/start.js.map +1 -0
  108. package/src/lib/git-flow/validate.d.ts +34 -0
  109. package/src/lib/git-flow/validate.js +72 -0
  110. package/src/lib/git-flow/validate.js.map +1 -0
  111. package/src/lib/git-flow-command.d.ts +4 -0
  112. package/src/lib/git-flow-command.js +165 -0
  113. package/src/lib/git-flow-command.js.map +1 -0
  114. package/src/lib/git-flow-repair.d.ts +17 -0
  115. package/src/lib/git-flow-repair.js +165 -0
  116. package/src/lib/git-flow-repair.js.map +1 -0
  117. package/src/lib/git-flow-start.d.ts +20 -0
  118. package/src/lib/git-flow-start.js +140 -0
  119. package/src/lib/git-flow-start.js.map +1 -0
  120. package/src/lib/git.d.ts +64 -0
  121. package/src/lib/git.js +131 -0
  122. package/src/lib/git.js.map +1 -0
  123. package/src/lib/gitlab.d.ts +35 -0
  124. package/src/lib/gitlab.js +116 -0
  125. package/src/lib/gitlab.js.map +1 -0
  126. package/src/lib/index.d.ts +2 -0
  127. package/src/lib/index.js +26 -11
  128. package/src/lib/index.js.map +1 -1
  129. package/src/lib/migrate.js +6 -1
  130. package/src/lib/migrate.js.map +1 -1
  131. package/src/lib/output-style-command.d.ts +3 -0
  132. package/src/lib/output-style-command.js +69 -0
  133. package/src/lib/output-style-command.js.map +1 -0
  134. package/src/lib/output-style.d.ts +38 -0
  135. package/src/lib/output-style.js +134 -0
  136. package/src/lib/output-style.js.map +1 -0
  137. package/src/lib/owned-paths.js +27 -3
  138. package/src/lib/owned-paths.js.map +1 -1
  139. package/src/lib/package-runner.d.ts +4 -0
  140. package/src/lib/package-runner.js +86 -0
  141. package/src/lib/package-runner.js.map +1 -0
  142. package/src/lib/plain-text.d.ts +9 -0
  143. package/src/lib/plain-text.js +22 -0
  144. package/src/lib/plain-text.js.map +1 -0
  145. package/src/lib/plan.d.ts +6 -6
  146. package/src/lib/plan.js +164 -40
  147. package/src/lib/plan.js.map +1 -1
  148. package/src/lib/prompt.d.ts +8 -0
  149. package/src/lib/prompt.js +35 -0
  150. package/src/lib/prompt.js.map +1 -0
  151. package/src/lib/render.d.ts +25 -2
  152. package/src/lib/render.js +43 -10
  153. package/src/lib/render.js.map +1 -1
  154. package/src/lib/sessions-command.d.ts +4 -0
  155. package/src/lib/sessions-command.js +16 -0
  156. package/src/lib/sessions-command.js.map +1 -0
  157. package/src/lib/sync.d.ts +0 -1
  158. package/src/lib/sync.js +30 -17
  159. package/src/lib/sync.js.map +1 -1
  160. package/src/lib/targets/agents-skills.js +1 -1
  161. package/src/lib/targets/agents-skills.js.map +1 -1
  162. package/src/lib/targets/claude-hooks.d.ts +3 -23
  163. package/src/lib/targets/claude-hooks.js +22 -86
  164. package/src/lib/targets/claude-hooks.js.map +1 -1
  165. package/src/lib/targets/claude-mods.d.ts +20 -0
  166. package/src/lib/targets/claude-mods.js +47 -0
  167. package/src/lib/targets/claude-mods.js.map +1 -0
  168. package/src/lib/targets/claude.js +2 -3
  169. package/src/lib/targets/claude.js.map +1 -1
  170. package/src/lib/targets/codex-hooks.d.ts +12 -0
  171. package/src/lib/targets/codex-hooks.js +35 -0
  172. package/src/lib/targets/codex-hooks.js.map +1 -0
  173. package/src/lib/targets/codex.js +4 -4
  174. package/src/lib/targets/codex.js.map +1 -1
  175. package/src/lib/targets/copilot.js +4 -4
  176. package/src/lib/targets/copilot.js.map +1 -1
  177. package/src/lib/targets/cursor.js +3 -3
  178. package/src/lib/targets/cursor.js.map +1 -1
  179. package/src/lib/targets/git-hooks.d.ts +25 -0
  180. package/src/lib/targets/git-hooks.js +71 -0
  181. package/src/lib/targets/git-hooks.js.map +1 -0
  182. package/src/lib/targets/hooks-shared.d.ts +60 -0
  183. package/src/lib/targets/hooks-shared.js +130 -0
  184. package/src/lib/targets/hooks-shared.js.map +1 -0
  185. package/src/lib/targets/mcp.d.ts +16 -0
  186. package/src/lib/targets/mcp.js +68 -0
  187. package/src/lib/targets/mcp.js.map +1 -0
  188. package/src/lib/targets/shared.d.ts +1 -9
  189. package/src/lib/targets/shared.js +4 -8
  190. package/src/lib/targets/shared.js.map +1 -1
  191. package/src/lib/timetrack-command.d.ts +11 -0
  192. package/src/lib/timetrack-command.js +1063 -0
  193. package/src/lib/timetrack-command.js.map +1 -0
  194. package/src/lib/timetrack-snapshot.d.ts +55 -0
  195. package/src/lib/timetrack-snapshot.js +120 -0
  196. package/src/lib/timetrack-snapshot.js.map +1 -0
  197. package/src/lib/timetrack.d.ts +561 -0
  198. package/src/lib/timetrack.js +279 -0
  199. package/src/lib/timetrack.js.map +1 -0
  200. package/content/skills/styleguide/STYLEGUIDE.md +0 -520
@@ -17,7 +17,7 @@ published yet, or when you are iterating on it.
17
17
 
18
18
  ## 1. Prerequisites
19
19
 
20
- - The checkout path comes from `sdkSourcePath` in `ethlete-agents.config.local.json`;
20
+ - The checkout path comes from `sdkSourcePath` in `ethlete.config.local.json`;
21
21
  {%skill:sdk-source%} covers resolving it and what to check before trusting it.
22
22
  - The checkout has its dependencies installed (`yarn install` in the checkout root - the
23
23
  SDK repo is a Yarn 4 workspace).
@@ -44,6 +44,21 @@ If a build stalls trying to reach Nx Cloud, re-run it with `NX_NO_CLOUD=true`.
44
44
 
45
45
  ## 3. Point this repo at the build
46
46
 
47
+ Preflight the manifest and lockfile before changing either:
48
+
49
+ Use {%resource:sdk-local-baseline.mjs%} to capture their exact bytes:
50
+
51
+ ```bash
52
+ git status --short -- <manifest> yarn.lock
53
+ sdk_local_baseline_dir=$(mktemp -d)
54
+ node <path-to-sdk-local-baseline.mjs> capture <manifest> yarn.lock "$sdk_local_baseline_dir"
55
+ ```
56
+
57
+ If either file already differs and you cannot tell who owns the edit, stop and ask. If
58
+ the user authorizes the experiment, keep the exact baseline above; pre-existing edits
59
+ are part of it and must survive byte-for-byte. If either file changes for another reason
60
+ during the experiment, stop and take a new agreed baseline before cleanup.
61
+
47
62
  Edit the version specifiers in `package.json` (the one declaring the dependency - in a
48
63
  workspace that is the workspace package, not necessarily the root):
49
64
 
@@ -97,17 +112,20 @@ reinstalling; the restart in step 3 is still required.)
97
112
  ## 6. Clean up when you are done
98
113
 
99
114
  Leaving a `file:` dependency behind breaks every other checkout and CI, because the path
100
- does not exist there. Restore it as part of the same task, not later:
115
+ does not exist there. Restore the exact recorded baseline as part of the same task:
101
116
 
102
117
  ```bash
103
- git checkout package.json # or hand-restore the original version specifier
104
- yarn install
105
- git status --short # package.json and yarn.lock must both be clean
118
+ node <path-to-sdk-local-baseline.mjs> restore <manifest> yarn.lock "$sdk_local_baseline_dir"
119
+ yarn install --immutable
120
+ node <path-to-sdk-local-baseline.mjs> verify <manifest> yarn.lock "$sdk_local_baseline_dir"
106
121
  ```
107
122
 
108
- Never commit a `file:` specifier or the lockfile it produced. If the verified fix is
109
- still unreleased, say what has to be published (which lib, which version) instead of
110
- shipping a local path.
123
+ The verification proves that the local-build delta is gone while preserving any edits
124
+ that existed before it. Do not require either file to be globally clean, and do
125
+ not run whole-file `git checkout` or `git restore`. Report the temporary baseline path;
126
+ it can be removed after verification because this workflow created it. Never commit a
127
+ `file:` specifier or the lockfile it produced. If the verified fix is still unreleased,
128
+ say what has to be published instead of shipping a local path.
111
129
 
112
130
  ## Related
113
131
 
@@ -0,0 +1,64 @@
1
+ import { copyFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { join, resolve } from 'node:path';
4
+
5
+ const baselinePaths = (baselineDir) => ({
6
+ manifest: join(baselineDir, 'package.json.baseline'),
7
+ lockfile: join(baselineDir, 'yarn.lock.baseline'),
8
+ });
9
+
10
+ export const captureBaseline = (manifestPath, lockfilePath, baselineDir) => {
11
+ const baseline = baselinePaths(baselineDir);
12
+
13
+ mkdirSync(baselineDir, { recursive: true });
14
+
15
+ if (existsSync(baseline.manifest) || existsSync(baseline.lockfile)) {
16
+ throw new Error(`Baseline already exists in ${baselineDir}.`);
17
+ }
18
+
19
+ copyFileSync(manifestPath, baseline.manifest);
20
+ copyFileSync(lockfilePath, baseline.lockfile);
21
+ };
22
+
23
+ export const restoreBaseline = (manifestPath, lockfilePath, baselineDir) => {
24
+ const baseline = baselinePaths(baselineDir);
25
+
26
+ copyFileSync(baseline.manifest, manifestPath);
27
+ copyFileSync(baseline.lockfile, lockfilePath);
28
+ };
29
+
30
+ export const verifyBaseline = (manifestPath, lockfilePath, baselineDir) => {
31
+ const baseline = baselinePaths(baselineDir);
32
+
33
+ for (const [current, recorded] of [
34
+ [manifestPath, baseline.manifest],
35
+ [lockfilePath, baseline.lockfile],
36
+ ]) {
37
+ if (!readFileSync(current).equals(readFileSync(recorded))) {
38
+ throw new Error(`${current} does not match its recorded baseline.`);
39
+ }
40
+ }
41
+ };
42
+
43
+ const run = () => {
44
+ const [mode, manifestPath, lockfilePath, baselineDir] = process.argv.slice(2);
45
+
46
+ if (!mode || !manifestPath || !lockfilePath || !baselineDir) {
47
+ throw new Error('Usage: sdk-local-baseline.mjs <capture|restore|verify> <manifest> <lockfile> <baseline-dir>');
48
+ }
49
+
50
+ const actions = {
51
+ capture: captureBaseline,
52
+ restore: restoreBaseline,
53
+ verify: verifyBaseline,
54
+ };
55
+ const action = actions[mode];
56
+
57
+ if (!action) throw new Error(`Unknown mode "${mode}".`);
58
+
59
+ action(manifestPath, lockfilePath, baselineDir);
60
+ };
61
+
62
+ if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
63
+ run();
64
+ }
@@ -21,7 +21,7 @@ first. Reach for the source when the docs genuinely cannot answer the question:
21
21
 
22
22
  ## 1. Resolve the checkout
23
23
 
24
- The path is per machine, so it lives in the gitignored `ethlete-agents.config.local.json`
24
+ The path is per machine, so it lives in the gitignored `ethlete.config.local.json`
25
25
  at the repo root:
26
26
 
27
27
  ```json
@@ -30,6 +30,9 @@ at the repo root:
30
30
  }
31
31
  ```
32
32
 
33
+ Older checkouts still keep this key in `ethlete-agents.config.local.json`. Read that file as a
34
+ fallback when `ethlete.config.local.json` is absent.
35
+
33
36
  Read that file before searching anywhere. A relative path is resolved from the repo root.
34
37
 
35
38
  If the file or the key is missing, **do not guess a path** and do not clone the
@@ -38,48 +41,48 @@ holds the full `.d.ts` surface of exactly the version this repo runs - and to
38
41
  {%docsBaseUrl%}. Then tell the user a local checkout would help, and offer the snippet
39
42
  above (the file is gitignored, so adding it changes nothing for anyone else).
40
43
 
41
- ## 2. Check the branch before you read anything
44
+ ## 2. Match evidence to the installed package
42
45
 
43
- A checkout sits on whatever branch the developer left it on, so the first command you
44
- run against it is the one that tells you what you are looking at:
46
+ Start with this routing:
45
47
 
46
- ```bash
47
- git -C <sdkSourcePath> fetch --quiet # read-only, safe
48
- git -C <sdkSourcePath> status -sb # branch, ahead/behind, dirty files
49
- ```
48
+ - Installed `.d.ts` files are authoritative for the public type surface available to
49
+ this consumer.
50
+ - Source matching the installed build is authoritative for runtime implementation
51
+ details.
52
+ - Docs explain intended usage and defaults, but can drift from a prerelease.
53
+ - A dirty or ahead `next` checkout is not automatically source for the installed build.
50
54
 
51
- **Unless the user says otherwise, the expected state is `next`, up to date with
52
- `origin/next`.** That is the branch the SDK develops on, and the one the `-next`
53
- prereleases are cut from. Anything else and you are reading a different SDK than the
54
- one you are about to describe.
55
+ Compare the installed package version with the checkout version before attributing
56
+ runtime behavior to the checkout:
55
57
 
56
- When it is not in that state, **say so and ask** - never switch, pull, stash or reset
57
- it yourself. It is someone's working tree, and the read-only rule below applies:
58
+ ```bash
59
+ grep '"@ethlete/' package.json
60
+ grep -m1 '"version"' <sdkSourcePath>/libs/<lib>/package.json
61
+ ```
58
62
 
59
- - **On another branch** - name it and ask whether to read it as-is or whether they want
60
- `next`. A feature branch may be exactly what you were sent to look at.
61
- - **Behind `origin/next`** - report how far. The code you would quote may already be
62
- fixed upstream, so read the missing commits before calling anything a bug:
63
- `git -C <sdkSourcePath> log --oneline HEAD..origin/next`.
64
- - **Dirty** - it may contain someone's work in progress. Say so rather than quoting it
65
- as SDK behaviour.
63
+ Matching version strings are necessary but not sufficient when the checkout is ahead of
64
+ the published commit. Never write consumer code against source that the installed types
65
+ do not expose.
66
66
 
67
- ## 3. Check the checkout matches what is installed
67
+ ## 3. Check only the paths that answer the question
68
68
 
69
- Even on a clean `next`, the checkout can be months of work ahead of the installed
70
- package - or behind it:
69
+ Resolve the implementation, public barrel, specs, and docs relevant to the question.
70
+ Then inspect state for those paths:
71
71
 
72
72
  ```bash
73
- grep '"@ethlete/' package.json # what this repo runs
74
- grep -m1 '"version"' <sdkSourcePath>/libs/<lib>/package.json # what the checkout is at
73
+ git -C <sdkSourcePath> status -sb
74
+ git -C <sdkSourcePath> status --short -- <relevant-paths>
75
+ git -C <sdkSourcePath> diff -- <relevant-paths>
75
76
  ```
76
77
 
77
- Rules when they differ:
78
+ Unrelated dirty files, another branch, or unrelated ahead commits do not block a source
79
+ lookup. If relevant paths are dirty or differ in the relevant commit range, label the
80
+ worktree behavior separately from published behavior. Ask only when that distinction
81
+ changes the answer.
78
82
 
79
- - **The installed package wins** for anything about how this repo behaves today. The
80
- `.d.ts` in `node_modules` is the truth about the API you are calling.
81
- - Source that is ahead describes an **unreleased** API. Never write consumer code
82
- against it, and never assume it is available - say what release it needs.
83
+ Use existing remote refs first. `git fetch` preserves worktree files but uses the network
84
+ and mutates remote refs. Fetch only when freshness is material, and request any approval
85
+ the environment requires. Never switch, pull, stash, or reset the checkout yourself.
83
86
 
84
87
  ## 4. Where things live
85
88
 
@@ -94,7 +97,7 @@ Paths are relative to the checkout root:
94
97
  | `libs/cdk/` | The predecessor UI toolkit, maintenance mode - only for code still on it |
95
98
  | `libs/eslint-plugin/src/` | The lint rules, including the message text explaining each one |
96
99
  | `apps/docs/` | The markdown behind {%docsBaseUrl%} |
97
- | `apps/playground/` | The Storybook app - stories also live next to each component |
100
+ | `apps/storybook/` | The Storybook app - stories also live next to each component |
98
101
 
99
102
  Inside a component domain: `<name>.component.ts` with its `.css` next to it,
100
103
  `<name>.imports.ts` (the imports array to spread into a consumer component),
@@ -118,8 +121,8 @@ implementation does.
118
121
 
119
122
  It is a different repository with its own branch, lint, docs and changeset workflow.
120
123
  Never edit it while working on a task in this repo - that covers its git state too, so
121
- no `checkout`, `pull`, `stash` or `reset` without the user asking for it (`fetch` is
122
- fine). Never copy its internals into consumer code either - a private helper is not a
124
+ no `checkout`, `pull`, `stash` or `reset` without the user asking for it. Never copy its
125
+ internals into consumer code either - a private helper is not a
123
126
  supported API and disappears without a major version (the same goes for anything under
124
127
  a `subtle` namespace).
125
128
 
@@ -0,0 +1,103 @@
1
+ ---
2
+ name: sdk-update
3
+ description: Update the @ethlete/* packages in this repo with `et update`, and work the migration tasks it leaves behind - the codemods it runs itself, the recommendations a human must decide, and the tasks written as a prompt for you. Read whenever this repo moves to a newer @ethlete version, when a task list under .ethlete/update is present, or when the user asks you to apply an SDK migration.
4
+ kind: skill
5
+ scope: consumer
6
+ requires: ['@ethlete/cli']
7
+ vars: [docsBaseUrl, lintCommand]
8
+ ---
9
+
10
+ # Updating the @ethlete SDK
11
+
12
+ `et update` moves this repo's `@ethlete/*` dependencies to a newer version and runs the migrations
13
+ those versions ship. Never bump an `@ethlete/*` range by hand: the version that lands is what selects
14
+ the migrations, so a hand-written bump skips every one of them silently.
15
+
16
+ ## 1. See what is pending
17
+
18
+ ```bash
19
+ {%packageRunner%} et update --check
20
+ ```
21
+
22
+ It prints one line per package, `installed → target`, and exits 1 while an update is pending. It
23
+ writes nothing. The target follows the dist tag the installed version is on: a repo on a `-next`
24
+ prerelease stays on `next`.
25
+
26
+ Name a package to limit the run, short or in full:
27
+
28
+ ```bash
29
+ {%packageRunner%} et update core # only @ethlete/core
30
+ {%packageRunner%} et update core --to 5.0.0-next.55
31
+ {%packageRunner%} et update --tag latest # leave the prerelease line
32
+ ```
33
+
34
+ ## 2. Run it
35
+
36
+ The working tree must be clean - the codemods rewrite files, and you need a diff you can read.
37
+ Commit or stash first, then:
38
+
39
+ ```bash
40
+ {%packageRunner%} et update
41
+ ```
42
+
43
+ In order, it writes the new ranges into every `package.json` in the repo that declares the package -
44
+ an Nx library manifest included, not only the root one - runs the install, reads the migrations out of
45
+ the freshly installed packages, runs every codemod, regenerates the agent rules and skills with
46
+ `ethlete-agents sync` when `@ethlete/agent-rules` moved, and writes what is left to `.ethlete/update`.
47
+
48
+ An older `@ethlete/cli` skips the sync. If the run moved `@ethlete/agent-rules` and printed no
49
+ `ethlete-agents sync` line, run `{%packageRunner%} ethlete-agents sync` yourself before the first task: the
50
+ tasks assume the guidance of the new version.
51
+
52
+ If the install or a codemod fails, the run stops and leaves `.ethlete/update/pending.json` behind.
53
+ Fix the cause, then continue - do not start over, or the migrations of the versions already installed
54
+ are skipped:
55
+
56
+ ```bash
57
+ {%packageRunner%} et update --continue
58
+ ```
59
+
60
+ ## 3. Work the task list
61
+
62
+ Two files describe what is left. Read the JSON one when you work through the list yourself:
63
+
64
+ | File | What it holds |
65
+ | --------------------------------- | --------------------------------------------------------------------- |
66
+ | `.ethlete/update/tasks.md` | The report for a human: what moved, what applied, what is left |
67
+ | `.ethlete/update/tasks.json` | The same tasks as data: `kind`, `instructionsFile`, `docsUrl` |
68
+ | `.ethlete/update/<pkg>-<name>.md` | One task in full: what moved, plus the instructions the package ships |
69
+
70
+ Every task carries a `kind`, and the kind decides who acts:
71
+
72
+ - **`assisted`** - written for you. The task file states one bounded change to apply across this repo.
73
+ Read the whole file before the first edit, then work it until its `Done when` list holds. These are
74
+ the changes no codemod can make, so expect a decision per call site rather than one pattern. What the
75
+ file leaves for later stays for later: report it, do not do it. A view you cannot open in the browser
76
+ (a login fails, no API) goes into your report as unchecked; it does not block the task.
77
+ - **`manual`** - a recommendation for the developer. It needs a product or design decision, or a
78
+ command with answers only they have. Do not guess the answer. Report the task and what it needs.
79
+ - **`unsupported`** - a codemod that could not run here, because this repo has no Nx. The task carries
80
+ the exact command. Ask before running it: it rewrites files.
81
+
82
+ ## 4. Finish
83
+
84
+ 1. Run the type check and `{%lintCommand%}` for every project you changed.
85
+ 2. Run the tests that cover what you touched.
86
+ 3. Delete the task files you finished under `.ethlete/update`, and leave the ones you could not decide.
87
+ 4. Tell the user, per task: what you changed, and what still needs their decision.
88
+
89
+ ## Known traps
90
+
91
+ - **Angular 22: `a?.b` gives `undefined`, not `null`.** Once the `$safeNavigationMigration` flag is
92
+ gone, a pipe that treats `null` and `undefined` differently breaks at runtime, with no type error. A
93
+ pipe that resets its state on `undefined` returns `null`, and the template then throws on a member
94
+ read. Pass `null` explicitly: `(x?.store ?? null) | suspense`.
95
+
96
+ ## Rules
97
+
98
+ - **Never invent a migration.** Only the tasks in `.ethlete/update` and the guides they link are real.
99
+ A rewrite you reasoned out from a version number is a guess.
100
+ - **Never edit the generated report to make it look done.** Deleting a finished task file is right;
101
+ deleting an entry you did not work is not.
102
+ - **One task at a time.** Each has its own diff, so a failure stays readable.
103
+ - The version notes and every guide a task links live at {%docsBaseUrl%}.
@@ -12,9 +12,10 @@ vars: [themeStylesheet]
12
12
 
13
13
  Two separate rules, often confused:
14
14
 
15
- 1. **Component source is plain CSS, never Tailwind.** The `.css` next to a
16
- component, wrapped in `@layer components`, using surface/color tokens - see
17
- {%skill:theming%}.
15
+ 1. **A library component's source is plain CSS, never Tailwind.** The `.css` next
16
+ to a component a library ships, wrapped in `@layer components`, using
17
+ surface/color tokens - see {%skill:theming%}. Application components are the
18
+ opposite: they use Tailwind utilities in their templates.
18
19
  2. **Story files may use Tailwind** (`*.stories.ts`, anything under a `stories/`
19
20
  folder) for demo layout only - the frame around the component, not the
20
21
  component's own look.
@@ -33,11 +34,11 @@ error and no warning, the element just renders unstyled. A theme that does this:
33
34
  leaves `bg-blue-500`, `text-gray-700` and `text-sm` as dead strings, even though
34
35
  they are "real" Tailwind classes.
35
36
 
36
- **Read the theme before reaching for an unfamiliar utility.** This project's is
37
- `{%themeStylesheet%}`:
37
+ **Read the theme before reaching for an unfamiliar utility.** This project's lives in
38
+ `{%themeStylesheet%}` (a file, or a folder of theme files):
38
39
 
39
40
  ```bash
40
- grep -nE -- '--(color|text|font|spacing)-' {%themeStylesheet%}
41
+ grep -rnE -- '--(color|text|font|spacing)-' {%themeStylesheet%}
41
42
  ```
42
43
 
43
44
  If `@theme` doesn't define the token, the class doesn't exist. Check rather than
@@ -62,7 +63,7 @@ paints must come from those tokens - the same rule as component CSS
62
63
 
63
64
  - A tinted panel: put `etAutoSurface` on it (next elevation up) and read the token:
64
65
  `style="background: var(--et-surface-background-solid)"`.
65
- - An accent: scope it with `[etProvideColor]="'brand'"` and read
66
+ - An accent: scope it with `[etProvideColor]="storyAccentTheme"` and read
66
67
  `--et-theme-color-primary-solid` / `--et-theme-color-ink-solid`. Theme names are
67
68
  whatever **your app** registers - fine to name in a story, never in library code.
68
69
  - Better still: don't hand-paint. Compose the component that already does it
@@ -80,4 +81,5 @@ paints must come from those tokens - the same rule as component CSS
80
81
  - Text sizes from your theme's scale, not Tailwind's default `text-sm`/`text-xs`.
81
82
  - No `dark:` variants.
82
83
  - Width/height that matters is px, not the rem container scale.
83
- - Then verify it actually renders - {%skill:verify-in-storybook%}.
84
+ - Then verify it actually renders with the repository's installed Storybook verification
85
+ workflow, or inspect the story directly when no such guide was emitted.
@@ -14,18 +14,23 @@ Don't hand-check or hand-fix those - **run lint with `--fix` first**; many rules
14
14
  ship auto-fixers, so most violations are corrected for you:
15
15
 
16
16
  ```bash
17
- {%lintFixCommand%} # auto-fixes first (case, ordering, $ suffix, metadata, …)
17
+ {%lintFixCommand%} # auto-fixes first (case, ordering, metadata, …); rename to the $ suffix by hand
18
18
  {%lintCommand%} # then re-run to see what needs a manual fix
19
19
  ```
20
20
 
21
- Full prose reference, including the rule → lint-rule table and the worked
22
- file-structure example: {%resource:STYLEGUIDE.md%}.
21
+ Load only the reference needed for the current question:
23
22
 
24
- ## Focused guides
23
+ - {%resource:lint-rule-lookup.md%} when a lint fix or diagnostic needs explanation.
24
+ - {%resource:file-structure.md%} when placing routes, views, reusable components,
25
+ providers, or local utilities.
26
+ - {%resource:assets.md%} when deciding where build, serve, shared, or Storybook assets
27
+ belong.
28
+ - {%resource:storybook-structure.md%} when placing a story file or its private helpers.
29
+ - {%resource:changesets.md%} when deciding whether an application or library change
30
+ needs a release note and no repository-specific release skill exists.
25
31
 
26
- - {%skill:rxjs-signals%} - synchronous state vs async, subscriptions, effects.
27
- - {%skill:angular-patterns%} - components, directives, services, pipes, templates,
28
- lifecycle.
32
+ For reactive state or Angular building blocks, load the corresponding focused repository
33
+ guide if one was emitted. This entry point does not duplicate those guides.
29
34
 
30
35
  The rest of this guide is the cross-cutting judgment that doesn't belong to one
31
36
  of those.
@@ -0,0 +1,54 @@
1
+ # Assets
2
+
3
+ Read this file when deciding whether an asset belongs to production, local serve, a
4
+ specific app, Storybook, or shared output.
5
+
6
+ Keep assets in the workspace assets library and separate them by consumer and purpose:
7
+
8
+ ```plaintext
9
+ assets/
10
+ ├── my-app/
11
+ │ ├── build/
12
+ │ ├── serve/
13
+ │ └── storybook/
14
+ ├── other-app/
15
+ │ ├── build/
16
+ │ ├── serve/
17
+ │ └── storybook/
18
+ └── shared/
19
+ ├── build/
20
+ ├── serve/
21
+ └── storybook/
22
+ ```
23
+
24
+ - `build/` contains production assets.
25
+ - `serve/` contains development-only placeholders or fixtures.
26
+ - `storybook/` contains assets used only by stories.
27
+ - `shared/` contains assets genuinely used by more than one app.
28
+ - Preserve the intended public URL through the project's `assets` configuration. Do not
29
+ expose a `build` directory segment in the final URL.
30
+
31
+ ```json
32
+ {
33
+ "assets": [
34
+ {
35
+ "input": "libs/assets/src/my-app/build",
36
+ "glob": "**/*",
37
+ "output": "assets"
38
+ },
39
+ {
40
+ "input": "libs/assets/src/my-app/serve",
41
+ "glob": "**/*",
42
+ "output": "assets/serve"
43
+ },
44
+ {
45
+ "input": "libs/assets/src/shared/build",
46
+ "glob": "**/*",
47
+ "output": "assets/shared"
48
+ }
49
+ ]
50
+ }
51
+ ```
52
+
53
+ Add only build assets to a production configuration. Add serve and Storybook assets
54
+ only to the configurations that need them.
@@ -0,0 +1,17 @@
1
+ # Changesets
2
+
3
+ Read this fallback only when the repository has no more specific release workflow.
4
+
5
+ - Add a changeset for a consumer-visible change to a published package.
6
+ - Do not add one for formatting, comments, tests, or an internal refactor with no
7
+ consumer-visible effect.
8
+ - Use `patch` for fixes, `minor` for backward-compatible API additions, and `major` for
9
+ breaking API or behavior changes.
10
+ - Keep one logical release note focused. Split unrelated changes.
11
+ - Write the note for the package consumer: state what they now get, not how the change
12
+ was implemented.
13
+ - Keep the note to one sentence, two at most, under 40 words.
14
+ - Do not run an interactive changeset command in a non-interactive agent environment;
15
+ write the repository's changeset file format directly.
16
+
17
+ When a repository supplies its own release skill or policy, that local guidance wins.
@@ -0,0 +1,73 @@
1
+ # File structure
2
+
3
+ Read this file when placing routes, views, components, providers, or local utilities.
4
+
5
+ Mirror the route tree in folders and suffix routing components with `-view`:
6
+
7
+ ```ts
8
+ import { Routes } from '@angular/router';
9
+
10
+ export const SHOP_ROUTES: Routes = [
11
+ {
12
+ path: 'items',
13
+ loadComponent: () =>
14
+ import('./items-list-view/items-list-view.component').then((module) => module.ItemsListViewComponent),
15
+ },
16
+ {
17
+ path: 'items/:id',
18
+ loadComponent: () =>
19
+ import('./item-detail-host-view/item-detail-host-view.component').then(
20
+ (module) => module.ItemDetailHostViewComponent,
21
+ ),
22
+ children: [
23
+ {
24
+ path: '',
25
+ loadComponent: () =>
26
+ import('./item-detail-host-view/item-detail-view/item-detail-view.component').then(
27
+ (module) => module.ItemDetailViewComponent,
28
+ ),
29
+ },
30
+ {
31
+ path: 'reviews',
32
+ loadComponent: () =>
33
+ import('./item-detail-host-view/item-reviews-view/item-reviews-view.component').then(
34
+ (module) => module.ItemReviewsViewComponent,
35
+ ),
36
+ },
37
+ ],
38
+ },
39
+ ];
40
+ ```
41
+
42
+ Use these placement rules:
43
+
44
+ - Put reusable view-level pieces in `components/`.
45
+ - Put children used only by one parent in that parent's `partials/`.
46
+ - Put generic presentation-only pieces in `uikit/`.
47
+ - Put application-shell pieces in `shell/`.
48
+ - Keep a directive, pipe, provider, form configuration, or utility beside its narrowest
49
+ consumer. Move it upward only when more consumers appear.
50
+ - Use simple utility names such as `items-list-filter-form.ts`, not `.utils.ts`.
51
+ - Give exportable folders an `index.ts`, but import source files directly. Do not import
52
+ a parent barrel from its subdirectory.
53
+ - Keep application projects thin; move shared or domain logic into buildable libraries
54
+ with clear import paths.
55
+
56
+ ```plaintext
57
+ shop/
58
+ ├── items-list-view/
59
+ │ ├── components/
60
+ │ │ └── item-card/
61
+ │ │ ├── partials/
62
+ │ │ │ ├── item-image/
63
+ │ │ │ └── item-price/
64
+ │ │ ├── item-card.component.ts
65
+ │ │ └── item-card.component.html
66
+ │ ├── items-list-filter-form.ts
67
+ │ └── items-list-view.component.ts
68
+ └── item-detail-host-view/
69
+ ├── item-detail-view/
70
+ ├── item-reviews-view/
71
+ ├── item-detail.provider.ts
72
+ └── item-detail-host-view.component.ts
73
+ ```
@@ -0,0 +1,51 @@
1
+ # Lint rule lookup
2
+
3
+ Read this file only to explain a lint result or identify which rule owns a mechanical
4
+ convention. Run lint with `--fix`; do not hand-check this table before linting.
5
+
6
+ | Convention | Rule |
7
+ | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
8
+ | No `any` / `$any()`; narrow `unknown` | `@typescript-eslint/no-explicit-any`, `@angular-eslint/template/no-any` |
9
+ | Static strings and booleans use attributes | `@angular-eslint/template/prefer-static-string-properties`, `ethlete/prefer-static-boolean-properties` |
10
+ | `type`, not `interface` (module augmentations stay interfaces) | `ethlete/consistent-type-definitions` |
11
+ | No `enum` or `const enum` | `ethlete/no-enum` |
12
+ | `const` by default; one declaration per statement | `no-var`, `prefer-const`, `one-var` |
13
+ | Strict equality | `eqeqeq` |
14
+ | Maximum two positional parameters | `max-params` |
15
+ | No type-only imports | `ethlete/no-type-only-import` |
16
+ | Descriptive `T`-prefixed generic names | `@typescript-eslint/naming-convention` |
17
+ | No `async` / `await` | `ethlete/no-async-await` |
18
+ | Arrow functions standalone; methods in classes | `no-restricted-syntax` |
19
+ | Blank line before a multiline guard return | `ethlete/guard-return-newline` |
20
+ | No trivially inferable return type | `ethlete/no-trivial-return-type` |
21
+ | Member and local naming conventions | `@typescript-eslint/naming-convention`, `ethlete/no-leading-underscore-class-member`, `ethlete/no-screaming-case-local` |
22
+ | Class constants are `readonly` and upper case | `ethlete/class-constant-property` |
23
+ | No `readonly` on reactive members | `ethlete/no-readonly-signal` |
24
+ | Injected and template-visible member accessibility | `ethlete/inject-member-accessibility`, `ethlete/template-member-accessibility` |
25
+ | No inject chains, pure aliases, or trivial wrappers | `ethlete/no-inject-chain`, `ethlete/no-member-alias`, `ethlete/no-trivial-wrapper-method` |
26
+ | No unused private or protected members | `ethlete/no-unused-class-member` |
27
+ | Pure module-scope provider calls | `ethlete/no-impure-top-level-provider` |
28
+ | No redundant `@internal` | `ethlete/no-redundant-internal` |
29
+ | Observable names end in `$` | `ethlete/require-dollar-suffix` |
30
+ | No subscribe body, subscribe in pipe, or RxJS in signal derivations | `ethlete/no-subscribe-with-body`, `ethlete/no-subscribe-in-pipe`, `ethlete/no-rxjs-in-effect` |
31
+ | `takeUntilDestroyed()` is the last operator in its pipe | `ethlete/take-until-destroyed-last` |
32
+ | Effect teardown uses `onCleanup` or `DestroyRef` | `ethlete/no-effect-cleanup-return` |
33
+ | Prefer `linkedSignal` for writable derived state | `ethlete/prefer-linked-signal` |
34
+ | Global component CSS encapsulation | `ethlete/require-view-encapsulation-none` |
35
+ | Angular-version-correct change detection metadata | `ethlete/require-on-push-change-detection`, `ethlete/no-redundant-on-push-change-detection` |
36
+ | No redundant standalone flag or empty metadata arrays | `ethlete/no-standalone-flag`, `ethlete/no-empty-angular-metadata-arrays` |
37
+ | No legacy lifecycle hooks or decorators | `no-restricted-syntax`, `ethlete/no-legacy-angular-decorators` |
38
+ | No injectable services, guards, or resolvers | `no-restricted-syntax` |
39
+ | Output naming | `@angular-eslint/no-output-on-prefix`, `@angular-eslint/no-output-native`, `ethlete/prefer-present-tense-output` |
40
+ | Inputs do not collide with global HTML attributes | `ethlete/no-native-html-input-name` |
41
+ | No pipe logic | `ethlete/no-pipe-logic` |
42
+ | Class members and decorator metadata stay ordered | `ethlete/class-member-order`, `ethlete/angular-decorator-property-order` |
43
+ | Routing views use `-view` / `ViewComponent` | `ethlete/enforce-routing-view-naming` |
44
+ | No direct browser globals, DOM queries, native observers, or cookies | `no-restricted-globals`, `ethlete/no-direct-dom-manipulation`, `ethlete/no-dom-query`, `ethlete/no-native-observers`, `ethlete/no-document-cookie` |
45
+ | No barrel imports | `no-restricted-syntax` |
46
+ | Prefer the SDK wrappers for platform utilities | `ethlete/prefer-clone-equal`, `ethlete/prefer-rxjs-timer`, `ethlete/prefer-match-media`, `ethlete/prefer-viewport-size`, `ethlete/no-angular-seo-services`, `ethlete/no-locale-id`, `ethlete/no-angular-router-api` |
47
+
48
+ Judgment still belongs outside lint: name things after their behavior, use an object
49
+ parameter after two positional parameters, expose injected members directly instead of
50
+ aliasing them, and use `/** @internal */` only on technically public library API that
51
+ must be stripped from declarations.