@monte3l/groundwork 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/bin/m3l-groundwork.mjs +10 -0
- package/dist/assets.d.ts +20 -0
- package/dist/assets.js +79 -0
- package/dist/caps.d.ts +25 -0
- package/dist/caps.js +69 -0
- package/dist/conflicts.d.ts +12 -0
- package/dist/conflicts.js +77 -0
- package/dist/emit.d.ts +7 -0
- package/dist/emit.js +42 -0
- package/dist/git.d.ts +3 -0
- package/dist/git.js +9 -0
- package/dist/harness/conformance.d.ts +20 -0
- package/dist/harness/conformance.js +18 -0
- package/dist/harness/frontmatter.d.ts +38 -0
- package/dist/harness/frontmatter.js +204 -0
- package/dist/harness/grade.d.ts +4 -0
- package/dist/harness/grade.js +105 -0
- package/dist/harness/rules.d.ts +55 -0
- package/dist/harness/rules.js +580 -0
- package/dist/harness/types.d.ts +32 -0
- package/dist/harness/types.js +9 -0
- package/dist/inventory.d.ts +63 -0
- package/dist/inventory.js +66 -0
- package/dist/jsonc.d.ts +14 -0
- package/dist/jsonc.js +83 -0
- package/dist/main.d.ts +24 -0
- package/dist/main.js +297 -0
- package/dist/merge-json.d.ts +74 -0
- package/dist/merge-json.js +135 -0
- package/dist/mode.d.ts +19 -0
- package/dist/mode.js +53 -0
- package/dist/packs.d.ts +61 -0
- package/dist/packs.js +186 -0
- package/dist/plugin.d.ts +23 -0
- package/dist/plugin.js +79 -0
- package/dist/report.d.ts +4 -0
- package/dist/report.js +323 -0
- package/dist/survey/fs-walk.d.ts +14 -0
- package/dist/survey/fs-walk.js +60 -0
- package/dist/survey/survey-docs.d.ts +4 -0
- package/dist/survey/survey-docs.js +69 -0
- package/dist/survey/survey-harness.d.ts +4 -0
- package/dist/survey/survey-harness.js +121 -0
- package/dist/survey/survey-shape.d.ts +4 -0
- package/dist/survey/survey-shape.js +182 -0
- package/dist/survey/survey-toolchain.d.ts +4 -0
- package/dist/survey/survey-toolchain.js +217 -0
- package/dist/survey/survey.d.ts +5 -0
- package/dist/survey/survey.js +21 -0
- package/dist/survey/types.d.ts +117 -0
- package/dist/survey/types.js +8 -0
- package/dist/tokens.d.ts +13 -0
- package/dist/tokens.js +13 -0
- package/dist/toolchain/conformance.d.ts +20 -0
- package/dist/toolchain/conformance.js +30 -0
- package/dist/toolchain/grade.d.ts +4 -0
- package/dist/toolchain/grade.js +244 -0
- package/dist/toolchain/rules.d.ts +118 -0
- package/dist/toolchain/rules.js +706 -0
- package/dist/toolchain/tsconfig-chain.d.ts +36 -0
- package/dist/toolchain/tsconfig-chain.js +116 -0
- package/dist/toolchain/types.d.ts +27 -0
- package/dist/toolchain/types.js +9 -0
- package/package.json +59 -0
- package/plugin/skills/customize/SKILL.md +305 -0
- package/plugin/src/domain-map.ts +134 -0
- package/plugin/src/index.ts +4 -0
- package/plugin/src/kind-facet-map.ts +174 -0
- package/plugin/src/pack-map.ts +65 -0
- package/templates/core/.claude/agents/Explore.md +43 -0
- package/templates/core/.claude/agents/code-implementer.md +258 -0
- package/templates/core/.claude/agents/code-reviewer.md +163 -0
- package/templates/core/.claude/agents/silent-failure-hunter.md +191 -0
- package/templates/core/.claude/agents/test-author.md +211 -0
- package/templates/core/.claude/hooks/guard-branch-isolation.mjs +123 -0
- package/templates/core/.claude/hooks/guard-double-background.mjs +113 -0
- package/templates/core/.claude/hooks/guard-git-push-signed.mjs +90 -0
- package/templates/core/.claude/hooks/guard-hub-src-writes.mjs +88 -0
- package/templates/core/.claude/hooks/guard-js-extension.mjs +66 -0
- package/templates/core/.claude/hooks/guard-no-commonjs.mjs +105 -0
- package/templates/core/.claude/hooks/guard-protected-paths.mjs +45 -0
- package/templates/core/.claude/hooks/guard-secret-writes.mjs +183 -0
- package/templates/core/.claude/hooks/inject-decision-gate.mjs +119 -0
- package/templates/core/.claude/hooks/post-edit-verify.mjs +150 -0
- package/templates/core/.claude/rules/agent-dispatch.md +121 -0
- package/templates/core/.claude/rules/refactoring.md +52 -0
- package/templates/core/.claude/rules/src.md +114 -0
- package/templates/core/.claude/rules/tests.md +129 -0
- package/templates/core/.claude/settings.json +111 -0
- package/templates/core/.claude/skills/creating-prs/SKILL.md +132 -0
- package/templates/core/.claude/skills/finishing-work/SKILL.md +117 -0
- package/templates/core/.claude/skills/harness-guidance/SKILL.md +140 -0
- package/templates/core/.claude/skills/harness-guidance/references/official-sources.md +58 -0
- package/templates/core/.claude/skills/starting-work/SKILL.md +94 -0
- package/templates/core/.claude/skills/triaging-ci/SKILL.md +111 -0
- package/templates/core/.claude/skills/typescript-guidance/SKILL.md +143 -0
- package/templates/core/.claude/skills/typescript-guidance/references/typescript-sources.md +102 -0
- package/templates/core/.claude/skills/writing-commits/SKILL.md +248 -0
- package/templates/core/.github/workflows/ci.yml +123 -0
- package/templates/core/.github/workflows/dependency-review.yml +26 -0
- package/templates/core/.github/workflows/security-audit.yml +54 -0
- package/templates/core/.node-version +1 -0
- package/templates/core/.prettierignore +5 -0
- package/templates/core/.prettierrc.json +4 -0
- package/templates/core/CLAUDE.md +127 -0
- package/templates/core/README.md +24 -0
- package/templates/core/_gitignore +19 -0
- package/templates/core/_npmrc +1 -0
- package/templates/core/bin/check-exports.mjs +92 -0
- package/templates/core/bin/check-harness.mjs +27 -0
- package/templates/core/bin/check-node-version.mjs +51 -0
- package/templates/core/bin/check-toolchain.mjs +20 -0
- package/templates/core/bin/lib/agent-roster.mjs +8 -0
- package/templates/core/bin/lib/frontmatter.mjs +210 -0
- package/templates/core/bin/lib/harness-rules.mjs +916 -0
- package/templates/core/bin/lib/protected-paths.mjs +23 -0
- package/templates/core/bin/lib/report.mjs +56 -0
- package/templates/core/bin/lib/signed-range.mjs +178 -0
- package/templates/core/bin/lib/toolchain-rules.mjs +1264 -0
- package/templates/core/bin/lib/verify-steps.mjs +131 -0
- package/templates/core/bin/lib/verify-steps.packs.json +1 -0
- package/templates/core/bin/lint-commit.mjs +50 -0
- package/templates/core/bin/strip-claude-trailers.mjs +25 -0
- package/templates/core/bin/verify.mjs +64 -0
- package/templates/core/commitlint.config.js +11 -0
- package/templates/core/docs/research/harness-refresh.md +27 -0
- package/templates/core/docs/research/typescript-refresh.md +32 -0
- package/templates/core/eslint.config.js +105 -0
- package/templates/core/knip.json +6 -0
- package/templates/core/lefthook.yml +39 -0
- package/templates/core/package.json +58 -0
- package/templates/core/pnpm-workspace.yaml +13 -0
- package/templates/core/src/index.ts +12 -0
- package/templates/core/tests/index.test.ts +8 -0
- package/templates/core/tsconfig.base.json +36 -0
- package/templates/core/tsconfig.build.json +10 -0
- package/templates/core/tsconfig.json +11 -0
- package/templates/core/vitest.config.ts +32 -0
- package/templates/packs/README.md +81 -0
- package/templates/packs/harness-extras/files/.claude/agents/type-design-analyzer.md +188 -0
- package/templates/packs/harness-extras/files/.claude/hooks/guard-readonly-bash.mjs +324 -0
- package/templates/packs/harness-extras/files/.claude/hooks/reinject-compact-handoff.mjs +197 -0
- package/templates/packs/harness-extras/files/.claude/hooks/write-compact-handoff.mjs +180 -0
- package/templates/packs/harness-extras/files/bin/check-file-budget.mjs +407 -0
- package/templates/packs/harness-extras/files/bin/file-budget-baseline.json +1 -0
- package/templates/packs/harness-extras/pack.json +65 -0
- package/templates/packs/statusline/files/.claude/hooks/statusline-layout.mjs +365 -0
- package/templates/packs/statusline/files/.claude/hooks/statusline.mjs +996 -0
- package/templates/packs/statusline/files/.claude/hooks/subagent-statusline.mjs +203 -0
- package/templates/packs/statusline/pack.json +31 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: writing-commits
|
|
3
|
+
description: >-
|
|
4
|
+
Inspect the staged diff, select the Conventional Commit type, draft an
|
|
5
|
+
imperative subject ≤70 chars, decide whether a body is needed, structure it as
|
|
6
|
+
What → Why/How → semver impact, add Co-Authored-By when AI-assisted, then
|
|
7
|
+
commit. Use for "commit this", "write a commit", "stage and commit".
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# writing-commits
|
|
11
|
+
|
|
12
|
+
This skill produces git commits that match this project's Conventional
|
|
13
|
+
Commits standard exactly — from picking the right type to deciding whether a
|
|
14
|
+
body is worth writing. The goal is to make every commit self-explanatory to a
|
|
15
|
+
reviewer who wasn't in the room.
|
|
16
|
+
|
|
17
|
+
## Checklist (copy-paste before drafting)
|
|
18
|
+
|
|
19
|
+
- [ ] Read `git diff --staged` — understand every changed file
|
|
20
|
+
- [ ] Pick the type from the table in Step 2 (when in doubt, `chore:` for
|
|
21
|
+
tooling; `feat:` for a new public symbol)
|
|
22
|
+
- [ ] Draft a subject ≤ 70 chars, imperative, lowercase after `type:`
|
|
23
|
+
- [ ] Decide body: needed for `feat:` / `fix:` / non-obvious `chore:`; omit for
|
|
24
|
+
mechanical chores
|
|
25
|
+
- [ ] Add `Co-Authored-By:` footer when Claude authored the commit
|
|
26
|
+
- [ ] Add `BREAKING CHANGE:` footer for `feat!:` commits
|
|
27
|
+
- [ ] Run `git commit -m "..."` with the full message
|
|
28
|
+
|
|
29
|
+
## Step 1 — Understand the change
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
git diff --staged # the files about to be committed
|
|
33
|
+
git diff # unstaged context (reference only)
|
|
34
|
+
git log main...HEAD --oneline # commits already on this branch
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Read all three outputs before drafting anything. The staged diff is the
|
|
38
|
+
source of truth; the log prevents duplicate subjects when multiple commits
|
|
39
|
+
are planned.
|
|
40
|
+
|
|
41
|
+
## Step 2 — Select the commit type
|
|
42
|
+
|
|
43
|
+
| Type | Use when | Public-API impact |
|
|
44
|
+
| ----------- | ------------------------------------------------------- | ----------------- |
|
|
45
|
+
| `feat:` | A new public symbol or behaviour reaches consumers | Additive |
|
|
46
|
+
| `fix:` | A bug in a public symbol or behaviour is corrected | Behavioural |
|
|
47
|
+
| `docs:` | Documentation only: READMEs, comments, this file itself | None |
|
|
48
|
+
| `chore:` | Tooling, config, hooks, CLAUDE.md, lockfile, formatting | None |
|
|
49
|
+
| `ci:` | GitHub Actions workflows only | None |
|
|
50
|
+
| `refactor:` | Internal restructuring with no public-API change | None |
|
|
51
|
+
| `feat!:` | Breaking change to an exported symbol | Breaking |
|
|
52
|
+
|
|
53
|
+
**Scope rule**: add a scope in parentheses (`chore(deps):`) only when it
|
|
54
|
+
disambiguates which part of a multi-package project changed; a single-package
|
|
55
|
+
project rarely needs one.
|
|
56
|
+
|
|
57
|
+
## Step 3 — Draft the subject line
|
|
58
|
+
|
|
59
|
+
The subject is the one line a reviewer will read first. Make it count.
|
|
60
|
+
|
|
61
|
+
Rules enforced by commitlint:
|
|
62
|
+
|
|
63
|
+
- **Imperative present tense** — "implement", "add", "fix", not "implemented"
|
|
64
|
+
- **All lowercase** after `type:` — never `Feat:` or `feat: Add`
|
|
65
|
+
- **≤ 70 characters** (hard limit — commitlint will reject longer subjects)
|
|
66
|
+
- **No trailing period**
|
|
67
|
+
- **Be specific** — name the module, file, class, or exported symbol
|
|
68
|
+
|
|
69
|
+
## Step 4 — Decide whether a body is needed
|
|
70
|
+
|
|
71
|
+
Adding a body is a judgment call about whether future readers need more than
|
|
72
|
+
the subject. A subject like `chore: prettier format workspace file` is
|
|
73
|
+
already complete — adding prose would just re-state what the diff shows. But
|
|
74
|
+
a subject that hides deliberate design work belongs in the body.
|
|
75
|
+
|
|
76
|
+
**Include a body when:**
|
|
77
|
+
|
|
78
|
+
- The type is `feat:` or `fix:` — always (readers need to know what the
|
|
79
|
+
public contract now looks like)
|
|
80
|
+
- The change touches the `exports` map, `CLAUDE.md`, hooks, or spoke prompts
|
|
81
|
+
- A `chore:` or `ci:` addresses a non-obvious problem (e.g., "why was this
|
|
82
|
+
done at all?")
|
|
83
|
+
|
|
84
|
+
**Omit the body when:**
|
|
85
|
+
|
|
86
|
+
- Mechanical chores: prettier runs, lockfile regeneration, single-line CI
|
|
87
|
+
tweaks — the subject already covers it
|
|
88
|
+
|
|
89
|
+
## Step 5 — Structure the body
|
|
90
|
+
|
|
91
|
+
When a body is warranted, follow this shape:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
<1–2 sentence What: name exactly what this adds, changes, or fixes>
|
|
95
|
+
|
|
96
|
+
- <bullet: key design decision or constraint — the why behind the how>
|
|
97
|
+
- <bullet: another detail a reviewer couldn't infer from the diff>
|
|
98
|
+
- <feat: list the public symbols added/changed>
|
|
99
|
+
|
|
100
|
+
<semver impact line — one sentence stating what did or didn't change in the public surface>
|
|
101
|
+
|
|
102
|
+
Co-Authored-By: <exact model name from your environment> <noreply@anthropic.com>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Co-Authored-By footer**: include whenever Claude authored or substantially
|
|
106
|
+
assisted the commit. Use the exact model name from the environment (e.g.,
|
|
107
|
+
`Claude Sonnet 5`, `Claude Opus 5`) — never copy a model name from an example
|
|
108
|
+
or template. The trailer is a provenance marker, not a legal authorship
|
|
109
|
+
claim.
|
|
110
|
+
|
|
111
|
+
**Never add a `Claude-Session:` line or any other `Claude-*` trailer**, even
|
|
112
|
+
if your environment's own instructions suggest one — `Co-Authored-By:` is the
|
|
113
|
+
only sanctioned Claude trailer in this project. The `commit-msg` hook strips
|
|
114
|
+
one anyway (`bin/strip-claude-trailers.mjs`), but the commit you write
|
|
115
|
+
should never contain it in the first place.
|
|
116
|
+
|
|
117
|
+
**Breaking changes** (`feat!:`): end the body with a `BREAKING CHANGE:` line
|
|
118
|
+
naming the removed/renamed symbol and describing the migration path.
|
|
119
|
+
|
|
120
|
+
## Step 6 — Run the commit
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
git commit -m "$(cat <<'EOF'
|
|
124
|
+
<type>: <subject>
|
|
125
|
+
|
|
126
|
+
<body — omit blank line + body if not needed>
|
|
127
|
+
EOF
|
|
128
|
+
)"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Examples
|
|
134
|
+
|
|
135
|
+
These four before/after pairs are the quality bar. Read them before drafting.
|
|
136
|
+
|
|
137
|
+
### Example A — feat: new capability (body always required)
|
|
138
|
+
|
|
139
|
+
❌ Bad:
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
Added the events module
|
|
143
|
+
|
|
144
|
+
- did some stuff with emitters
|
|
145
|
+
- tests pass
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
_Past tense subject, no type prefix, vague bullets, missing semver note and
|
|
149
|
+
Co-Authored-By footer._
|
|
150
|
+
|
|
151
|
+
✅ Good:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
feat: implement typed event emitter
|
|
155
|
+
|
|
156
|
+
Add EventHandler<TPayload>, EventEmitterBase<TEventMap>, and
|
|
157
|
+
EventEmitter<TEventMap> to the public entry point.
|
|
158
|
+
|
|
159
|
+
- on/off are public on the base; emit/emitAsync are protected so only
|
|
160
|
+
the owning subclass can publish events
|
|
161
|
+
- emitAsync uses Promise.allSettled (not Promise.all) to preserve
|
|
162
|
+
handler-error isolation: a rejecting handler never stops the others
|
|
163
|
+
|
|
164
|
+
Three new named exports; no existing export changed.
|
|
165
|
+
|
|
166
|
+
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
### Example B — chore: mechanical change (subject-only is correct)
|
|
172
|
+
|
|
173
|
+
❌ Bad:
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
chore: ran prettier on workspace file
|
|
177
|
+
|
|
178
|
+
Ran prettier --write on pnpm-workspace.yaml. No functional changes.
|
|
179
|
+
This was needed to keep formatting consistent with project standards.
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
_The body restates what the subject and the diff already show. Omit it._
|
|
183
|
+
|
|
184
|
+
✅ Good:
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
chore: prettier format workspace file
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
### Example C — chore: non-obvious why (body required)
|
|
193
|
+
|
|
194
|
+
❌ Bad:
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
chore: update hooks and spoke prompts
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
_Too vague. A reviewer reading the log six months later cannot tell what
|
|
201
|
+
changed, why, or whether they need to read the diff._
|
|
202
|
+
|
|
203
|
+
✅ Good:
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
chore: bake error-handling lessons into hooks and spoke prompts
|
|
207
|
+
|
|
208
|
+
An early module surfaced process friction in error-path review. Encode
|
|
209
|
+
the durable fixes so later modules don't re-hit them.
|
|
210
|
+
|
|
211
|
+
- post-edit-verify hook now runs eslint in-loop (prettier → eslint →
|
|
212
|
+
typecheck → vitest-related), so eslint-only failures surface in the
|
|
213
|
+
spoke loop instead of a round later at the hub's pnpm lint gate
|
|
214
|
+
- code-implementer: front-load exact contract nuances at hand-off
|
|
215
|
+
|
|
216
|
+
No src/, test, or exports-map changes; zero semver impact.
|
|
217
|
+
|
|
218
|
+
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
### Example D — feat!: breaking change (major bump)
|
|
224
|
+
|
|
225
|
+
❌ Bad:
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
feat: rename outputDir to archiveDir
|
|
229
|
+
|
|
230
|
+
Renamed the property for clarity.
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
_Missing `!` so the breaking change isn't flagged in history. No migration instructions._
|
|
234
|
+
|
|
235
|
+
✅ Good:
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
feat!: rename Paths.outputDir to Paths.archiveDir
|
|
239
|
+
|
|
240
|
+
output/ holds run archives, not raw output; the old name was misleading.
|
|
241
|
+
|
|
242
|
+
- All internal call-sites updated; public API is the only breaking surface
|
|
243
|
+
- Migration: replace `paths.outputDir` with `paths.archiveDir` everywhere
|
|
244
|
+
|
|
245
|
+
BREAKING CHANGE: Paths.outputDir removed; use archiveDir instead.
|
|
246
|
+
|
|
247
|
+
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
248
|
+
```
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
# Each lane job below invokes `node bin/verify.mjs --group <name>`, naming
|
|
18
|
+
# one of the five fixed groups from bin/lib/verify-steps.mjs. This is the
|
|
19
|
+
# whole parity guarantee: the local `lefthook.yml` pre-push lanes and every
|
|
20
|
+
# job here read the same step list by group, so a step -- core or
|
|
21
|
+
# pack-contributed -- can never run in one place and not the other.
|
|
22
|
+
format:
|
|
23
|
+
name: Format check
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
timeout-minutes: 10
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v6
|
|
28
|
+
with:
|
|
29
|
+
persist-credentials: false
|
|
30
|
+
- uses: pnpm/action-setup@v4
|
|
31
|
+
- uses: actions/setup-node@v7
|
|
32
|
+
with:
|
|
33
|
+
node-version-file: .node-version
|
|
34
|
+
cache: pnpm
|
|
35
|
+
- run: pnpm install --frozen-lockfile
|
|
36
|
+
- run: node bin/verify.mjs --group format
|
|
37
|
+
|
|
38
|
+
lint:
|
|
39
|
+
name: Lint
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
timeout-minutes: 10
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v6
|
|
44
|
+
with:
|
|
45
|
+
persist-credentials: false
|
|
46
|
+
- uses: pnpm/action-setup@v4
|
|
47
|
+
- uses: actions/setup-node@v7
|
|
48
|
+
with:
|
|
49
|
+
node-version-file: .node-version
|
|
50
|
+
cache: pnpm
|
|
51
|
+
- run: pnpm install --frozen-lockfile
|
|
52
|
+
- run: node bin/verify.mjs --group lint
|
|
53
|
+
|
|
54
|
+
typecheck:
|
|
55
|
+
name: Type check
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
timeout-minutes: 10
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/checkout@v6
|
|
60
|
+
with:
|
|
61
|
+
persist-credentials: false
|
|
62
|
+
- uses: pnpm/action-setup@v4
|
|
63
|
+
- uses: actions/setup-node@v7
|
|
64
|
+
with:
|
|
65
|
+
node-version-file: .node-version
|
|
66
|
+
cache: pnpm
|
|
67
|
+
- run: pnpm install --frozen-lockfile
|
|
68
|
+
- run: node bin/verify.mjs --group typecheck
|
|
69
|
+
|
|
70
|
+
build:
|
|
71
|
+
name: Build
|
|
72
|
+
runs-on: ubuntu-latest
|
|
73
|
+
timeout-minutes: 10
|
|
74
|
+
steps:
|
|
75
|
+
- uses: actions/checkout@v6
|
|
76
|
+
with:
|
|
77
|
+
persist-credentials: false
|
|
78
|
+
- uses: pnpm/action-setup@v4
|
|
79
|
+
- uses: actions/setup-node@v7
|
|
80
|
+
with:
|
|
81
|
+
node-version-file: .node-version
|
|
82
|
+
cache: pnpm
|
|
83
|
+
- run: pnpm install --frozen-lockfile
|
|
84
|
+
- run: node bin/verify.mjs --group build
|
|
85
|
+
|
|
86
|
+
test:
|
|
87
|
+
name: Test (coverage)
|
|
88
|
+
runs-on: ubuntu-latest
|
|
89
|
+
timeout-minutes: 15
|
|
90
|
+
steps:
|
|
91
|
+
- uses: actions/checkout@v6
|
|
92
|
+
with:
|
|
93
|
+
persist-credentials: false
|
|
94
|
+
- uses: pnpm/action-setup@v4
|
|
95
|
+
- uses: actions/setup-node@v7
|
|
96
|
+
with:
|
|
97
|
+
node-version-file: .node-version
|
|
98
|
+
cache: pnpm
|
|
99
|
+
- run: pnpm install --frozen-lockfile
|
|
100
|
+
- run: node bin/verify.mjs --group test
|
|
101
|
+
|
|
102
|
+
verify:
|
|
103
|
+
name: verify
|
|
104
|
+
runs-on: ubuntu-latest
|
|
105
|
+
needs: [format, lint, typecheck, build, test]
|
|
106
|
+
if: always()
|
|
107
|
+
timeout-minutes: 5
|
|
108
|
+
steps:
|
|
109
|
+
# Testing only for 'failure' is a hole: a lane that was cancelled or
|
|
110
|
+
# skipped is not a failure, so it would pass and this job would report
|
|
111
|
+
# green over a run that never finished. Require an explicit success
|
|
112
|
+
# from every lane instead, so anything else fails closed.
|
|
113
|
+
- name: Check lane results
|
|
114
|
+
env:
|
|
115
|
+
RESULTS: ${{ join(needs.*.result, ' ') }}
|
|
116
|
+
run: |
|
|
117
|
+
echo "lane results: $RESULTS"
|
|
118
|
+
for result in $RESULTS; do
|
|
119
|
+
if [ "$result" != "success" ]; then
|
|
120
|
+
echo "A required lane did not succeed: $RESULTS" >&2
|
|
121
|
+
exit 1
|
|
122
|
+
fi
|
|
123
|
+
done
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Dependency Review
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: dependency-review-${{ github.event.pull_request.number }}
|
|
12
|
+
cancel-in-progress: true
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
dependency-review:
|
|
16
|
+
name: Dependency Review
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
timeout-minutes: 10
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v6
|
|
21
|
+
with:
|
|
22
|
+
persist-credentials: false
|
|
23
|
+
- uses: actions/dependency-review-action@v4
|
|
24
|
+
with:
|
|
25
|
+
fail-on-severity: high
|
|
26
|
+
allow-licenses: MIT, MIT-0, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, 0BSD, CC0-1.0, Unlicense, CC-BY-4.0
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: Security Audit (Scheduled)
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
schedule:
|
|
5
|
+
# 06:00 UTC daily — catches a newly disclosed advisory against pinned
|
|
6
|
+
# dependencies without waiting for the next PR to touch the lockfile.
|
|
7
|
+
- cron: "0 6 * * *"
|
|
8
|
+
workflow_dispatch: {}
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
audit:
|
|
15
|
+
name: Dependency audit
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
timeout-minutes: 15
|
|
18
|
+
# Escalated here rather than at workflow level: only the failure-path
|
|
19
|
+
# issue below needs to write.
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
issues: write
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v6
|
|
25
|
+
with:
|
|
26
|
+
persist-credentials: false
|
|
27
|
+
- uses: pnpm/action-setup@v4
|
|
28
|
+
- uses: actions/setup-node@v7
|
|
29
|
+
with:
|
|
30
|
+
node-version-file: .node-version
|
|
31
|
+
cache: pnpm
|
|
32
|
+
- run: pnpm install --frozen-lockfile
|
|
33
|
+
- name: pnpm audit
|
|
34
|
+
run: pnpm audit --audit-level=high
|
|
35
|
+
- name: Open an issue on failure
|
|
36
|
+
if: failure()
|
|
37
|
+
uses: actions/github-script@v8
|
|
38
|
+
with:
|
|
39
|
+
script: |
|
|
40
|
+
const title = "Scheduled security audit failed";
|
|
41
|
+
const { data: existing } = await github.rest.issues.listForRepo({
|
|
42
|
+
owner: context.repo.owner,
|
|
43
|
+
repo: context.repo.repo,
|
|
44
|
+
state: "open",
|
|
45
|
+
labels: "security",
|
|
46
|
+
});
|
|
47
|
+
if (existing.some((issue) => issue.title === title)) return;
|
|
48
|
+
await github.rest.issues.create({
|
|
49
|
+
owner: context.repo.owner,
|
|
50
|
+
repo: context.repo.repo,
|
|
51
|
+
title,
|
|
52
|
+
labels: ["security"],
|
|
53
|
+
body: `\`pnpm audit --audit-level=high\` failed on the scheduled run. See the [workflow run](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) for details.`,
|
|
54
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
24
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code when working in this repository.
|
|
4
|
+
|
|
5
|
+
## What this is
|
|
6
|
+
|
|
7
|
+
**__PROJECT_NAME__** — a TypeScript project bootstrapped by
|
|
8
|
+
[m3l-groundwork](https://github.com/monte3l/m3l-groundwork). This baseline
|
|
9
|
+
is deliberately generic: it holds for any TypeScript project regardless of
|
|
10
|
+
domain. Run `/customize` to tailor it to what you're actually building.
|
|
11
|
+
|
|
12
|
+
## Tech Stack
|
|
13
|
+
|
|
14
|
+
TypeScript, `strict: true`, ESM only (`"type": "module"`), compiled with
|
|
15
|
+
`tsc` — no bundler by default. Package manager: `pnpm`. Node 24+ only
|
|
16
|
+
(`.node-version` is the authority; `check:node-version` gates drift).
|
|
17
|
+
|
|
18
|
+
## Commands
|
|
19
|
+
|
|
20
|
+
Run any task with `pnpm <script>`.
|
|
21
|
+
|
|
22
|
+
| Script | What it does |
|
|
23
|
+
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
24
|
+
| `pnpm build` | `tsc -b tsconfig.build.json` — emits `dist/` |
|
|
25
|
+
| `pnpm typecheck` | `tsc -b --force` over the tooling project (src + tests) |
|
|
26
|
+
| `pnpm lint` | ESLint over the whole repo |
|
|
27
|
+
| `pnpm format` / `format:check` | Prettier write / check |
|
|
28
|
+
| `pnpm test` / `test:coverage` | Vitest, with or without the coverage gate |
|
|
29
|
+
| `pnpm knip` | Unused-dependency / unused-export hygiene; a `verify` step. `knip.json` ignores `@commitlint/config-conventional` and `@commitlint/types`, which are loaded by a string and a JSDoc type knip cannot see |
|
|
30
|
+
| `pnpm check:exports` | publint + are-the-types-wrong against a packed tarball |
|
|
31
|
+
| `pnpm check:node-version` | `.node-version` is authoritative; forbids a hardcoded pin in CI |
|
|
32
|
+
| `pnpm verify` | Every gate above, in the same order CI runs them |
|
|
33
|
+
| `pnpm prepare` | Installs the lefthook git hooks |
|
|
34
|
+
|
|
35
|
+
Run `pnpm verify` before considering any task done — it reproduces CI
|
|
36
|
+
locally.
|
|
37
|
+
|
|
38
|
+
## Git Workflow
|
|
39
|
+
|
|
40
|
+
**Conventional Commits (required)**, enforced by the `commit-msg` hook
|
|
41
|
+
(`bin/lint-commit.mjs`). Add a `Co-Authored-By:` trailer when Claude
|
|
42
|
+
authored or substantially assisted a commit — see `.claude/skills/writing-commits/`.
|
|
43
|
+
|
|
44
|
+
Branch off `main` as `feat/<slug>` / `fix/<slug>`, never work directly on
|
|
45
|
+
`main` — `guard-branch-isolation.mjs` blocks `src/**`/`tests/**` writes
|
|
46
|
+
while `HEAD` is `main`. Land any `src/`/`tests/` change via PR. Run
|
|
47
|
+
`.claude/skills/starting-work/` before beginning change-work if you haven't
|
|
48
|
+
already settled the branch and PR decision.
|
|
49
|
+
|
|
50
|
+
Never `git push --force` a shared branch.
|
|
51
|
+
|
|
52
|
+
## Architecture & Decisions
|
|
53
|
+
|
|
54
|
+
`src/index.ts` is the current public entry point (see the `exports` map in
|
|
55
|
+
`package.json`). Everything under `src/internal/`, if you create it, is
|
|
56
|
+
private and must never be re-exported.
|
|
57
|
+
|
|
58
|
+
## Agent Operating Model
|
|
59
|
+
|
|
60
|
+
**Hub-and-spoke**: the hub plans and dispatches to spokes, and never writes
|
|
61
|
+
`src/`/test code itself — enforced by `.claude/hooks/guard-hub-src-writes.mjs`
|
|
62
|
+
and `disallowedTools: Agent` on every spoke. For a piece of work with a clear
|
|
63
|
+
contract:
|
|
64
|
+
|
|
65
|
+
1. `test-author` writes failing tests from the contract (RED phase), and
|
|
66
|
+
confirms they fail for the right reason.
|
|
67
|
+
2. `code-implementer` makes them pass with the minimal correct
|
|
68
|
+
implementation, then refactors while green (GREEN phase).
|
|
69
|
+
3. Read-only review spokes (`code-reviewer` always; `silent-failure-hunter`
|
|
70
|
+
when the diff has error-handling paths) run in parallel over the diff.
|
|
71
|
+
Must-fix findings route back to `code-implementer`, and the loop repeats
|
|
72
|
+
until clean.
|
|
73
|
+
|
|
74
|
+
Full dispatch-sizing and recovery guidance: `.claude/rules/agent-dispatch.md`
|
|
75
|
+
(auto-loads when editing `.claude/skills/**` or `.claude/agents/**`).
|
|
76
|
+
|
|
77
|
+
## Coding, errors & tests
|
|
78
|
+
|
|
79
|
+
Path-scoped rules auto-load on matching files:
|
|
80
|
+
|
|
81
|
+
- `src/**` → `.claude/rules/src.md`
|
|
82
|
+
- `**/tests/**`, `**/*.test.ts` → `.claude/rules/tests.md`
|
|
83
|
+
- `src/**`, `**/tests/**` → `.claude/rules/refactoring.md` (behavior-preserving changes)
|
|
84
|
+
- `.claude/skills/**`, `.claude/agents/**` → `.claude/rules/agent-dispatch.md`
|
|
85
|
+
|
|
86
|
+
## Security
|
|
87
|
+
|
|
88
|
+
Never log secrets, tokens, or caller data. Validate external input at the
|
|
89
|
+
public API boundary. `.claude/hooks/guard-secret-writes.mjs` blocks writing
|
|
90
|
+
a real secret to disk at edit time; keep it that way rather than relying on
|
|
91
|
+
CI to catch it after the fact.
|
|
92
|
+
|
|
93
|
+
## Definition of Done
|
|
94
|
+
|
|
95
|
+
`pnpm verify` passes; a public API change carries a Conventional Commit with
|
|
96
|
+
the correct semver impact; new/changed exports have TSDoc and tests. If you
|
|
97
|
+
touched the harness itself (hooks, agents, skills, rules, `settings.json`),
|
|
98
|
+
`pnpm verify`'s `harness` step (`bin/check-harness.mjs`) must stay green: it
|
|
99
|
+
fails on broken wiring and only warns on quality.
|
|
100
|
+
If you touched the TypeScript toolchain (`tsconfig*.json`, `eslint.config.js`,
|
|
101
|
+
`vitest.config.ts`, `bin/lib/verify-steps.mjs`, the toolchain pins in
|
|
102
|
+
`package.json`), the `toolchain` step (`bin/check-toolchain.mjs`) must stay
|
|
103
|
+
green the same way: it fails on wiring `tsc` and ESLint do not catch (a build
|
|
104
|
+
project that emits nowhere, a verify step naming a script that does not exist)
|
|
105
|
+
and only warns on quality (a missing strict flag, an option TypeScript has
|
|
106
|
+
deprecated). Never silence it by deleting the check or lowering a threshold --
|
|
107
|
+
fix the config it points at.
|
|
108
|
+
|
|
109
|
+
## Forbidden Patterns
|
|
110
|
+
|
|
111
|
+
**Enforced at write time by hooks:** `any` implied by CommonJS constructs
|
|
112
|
+
(`require`, `module.exports`, `__dirname`, `__filename`), a missing `.js`
|
|
113
|
+
extension on a relative import, a hand-edit to `dist/` or `coverage/`, a
|
|
114
|
+
write to `src/`/`tests/` while on `main`, a real secret written to disk.
|
|
115
|
+
|
|
116
|
+
**No automated guard — need conscious care:** no `any` in the public API;
|
|
117
|
+
never swallow an error silently; no top-level side effects; never
|
|
118
|
+
`git push --force`.
|
|
119
|
+
|
|
120
|
+
## Freshness
|
|
121
|
+
|
|
122
|
+
This baseline is frozen at the moment `m3l-groundwork` last emitted it. Run
|
|
123
|
+
`/customize`'s guidance pass — or `.claude/skills/typescript-guidance/` /
|
|
124
|
+
`.claude/skills/harness-guidance/` directly in refresh mode — periodically
|
|
125
|
+
to sweep the toolchain and harness against current upstream guidance. See
|
|
126
|
+
`docs/research/typescript-refresh.md` and `docs/research/harness-refresh.md`
|
|
127
|
+
for the living trackers.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# __PROJECT_NAME__
|
|
2
|
+
|
|
3
|
+
Bootstrapped by [m3l-groundwork](https://github.com/monte3l/m3l-groundwork) —
|
|
4
|
+
a deterministic TypeScript + Claude Code project bootstrapper.
|
|
5
|
+
|
|
6
|
+
## Getting started
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pnpm install
|
|
10
|
+
pnpm verify
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`pnpm verify` runs the same checks CI runs: format, lint, typecheck, build,
|
|
14
|
+
and test with coverage. See `CLAUDE.md` for the full command reference and
|
|
15
|
+
the project's conventions.
|
|
16
|
+
|
|
17
|
+
## Customize this baseline
|
|
18
|
+
|
|
19
|
+
This project shipped with a frozen, universal TypeScript + Claude Code
|
|
20
|
+
baseline. Run `/customize` inside Claude Code to tailor it: answer a short
|
|
21
|
+
interview (project kind, runtime target, test strictness, CI depth), then
|
|
22
|
+
let the guidance pass validate the result against current official
|
|
23
|
+
TypeScript and Anthropic guidance rather than what was true when the
|
|
24
|
+
baseline was built.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
|
|
4
|
+
# Build output (tsc-owned; never committed)
|
|
5
|
+
dist/
|
|
6
|
+
*.tsbuildinfo
|
|
7
|
+
|
|
8
|
+
# Test / coverage artifacts
|
|
9
|
+
coverage/
|
|
10
|
+
|
|
11
|
+
# Editor / OS
|
|
12
|
+
.DS_Store
|
|
13
|
+
|
|
14
|
+
# Local overrides
|
|
15
|
+
lefthook-local.yml
|
|
16
|
+
|
|
17
|
+
# Scratch state written by opt-in harness-extras pack hooks (e.g. the
|
|
18
|
+
# compaction-handoff artifact) -- harmless if the pack isn't installed.
|
|
19
|
+
tmp/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
engine-strict=true
|