@monte3l/groundwork 1.0.0-rc.4 → 1.0.0-rc.5
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/dist/customize-paths.d.ts +51 -15
- package/dist/customize-paths.js +32 -13
- package/dist/fs-guard.d.ts +2 -2
- package/dist/harness/rules.d.ts +2 -1
- package/dist/harness/rules.js +3 -1
- package/dist/plugin.d.ts +12 -12
- package/dist/plugin.js +34 -26
- package/package.json +1 -1
- package/plugin/skills/customize/SKILL.md +33 -403
- package/plugin/skills/customize/step-0-reconcile.md +261 -0
- package/plugin/skills/customize/step-3-round-1.md +170 -0
- package/plugin/src/plugin-map.ts +131 -10
- package/templates/core/.claude/agents/Explore.md +1 -1
- package/templates/core/.claude/hooks/guard-no-commonjs.mjs +3 -2
- package/templates/core/.claude/hooks/inject-decision-gate.mjs +7 -7
- package/templates/core/.claude/skills/triaging-ci/SKILL.md +2 -2
- package/templates/core/.claude/skills/writing-commits/SKILL.md +4 -4
- package/templates/core/.prettierignore +1 -0
- package/templates/core/_gitignore +1 -0
- package/templates/core/bin/check-exports.mjs +5 -3
- package/templates/core/bin/lib/harness-rules.mjs +3 -1
- package/templates/core/tsconfig.base.json +10 -6
- package/templates/packs/README.md +6 -0
- package/templates/packs/github/files/.github/workflows/claude-pr-review.yml +1 -1
- package/templates/packs/github/files/.github/workflows/claude.yml +1 -1
- package/templates/packs/github/pack.json +1 -1
- package/templates/packs/harness-extras/files/.claude/hooks/subagent-statusline.mjs +29 -14
- package/templates/packs/harness-extras/pack.json +1 -1
- package/templates/packs/publishing/pack.json +1 -1
- package/templates/packs/quality/files/.claude/agents/type-design-analyzer.md +1 -1
- package/templates/packs/quality/pack.json +1 -1
|
@@ -73,13 +73,13 @@ export function buildContext(branch) {
|
|
|
73
73
|
: `on \`${branch}\``;
|
|
74
74
|
return [
|
|
75
75
|
"Decision gate (before editing code/tests) -- currently " +
|
|
76
|
-
`${branchLine}.
|
|
77
|
-
" • Branch -- `feat/<slug>` or `fix/<slug>` off `main
|
|
78
|
-
(onMain ? "
|
|
79
|
-
"
|
|
80
|
-
" • PR -- any `src/`/`tests/` change lands via PR,
|
|
81
|
-
" • Push -- `origin <branch>`, not `origin main`.",
|
|
82
|
-
"guard-branch-isolation.mjs
|
|
76
|
+
`${branchLine}. These decisions come first, ideally via the \`starting-work\` skill:`,
|
|
77
|
+
" • Branch -- a `feat/<slug>` or `fix/<slug>` branch off `main`, not `main` itself" +
|
|
78
|
+
(onMain ? "; HEAD may be on/at `main` now" : "") +
|
|
79
|
+
".",
|
|
80
|
+
" • PR -- any `src/`/`tests/` change lands via a PR, not a direct commit to `main`.",
|
|
81
|
+
" • Push -- the target is `origin <branch>`, not `origin main`.",
|
|
82
|
+
"guard-branch-isolation.mjs blocks src/test writes while HEAD is on `main`.",
|
|
83
83
|
].join("\n");
|
|
84
84
|
}
|
|
85
85
|
|
|
@@ -4,8 +4,8 @@ description: >-
|
|
|
4
4
|
Diagnose a CI failure via gh CLI: resolve the failing run, fetch logs, map the
|
|
5
5
|
failure to its pipeline step, report root cause plus the exact local repro
|
|
6
6
|
command, and present 3-5 fix options. Use for /triaging-ci, "why did CI fail",
|
|
7
|
-
"CI is failing", "
|
|
8
|
-
gh CLI.
|
|
7
|
+
"CI is failing", "the build is red", "find the root cause of a failing
|
|
8
|
+
build", "debug the CI run", or a specific run ID/URL. GitHub stance: gh CLI.
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
Diagnose why a GitHub Actions CI run failed by fetching its logs via `gh` and
|
|
@@ -104,7 +104,7 @@ Co-Authored-By: <exact model name from your environment> <noreply@anthropic.com>
|
|
|
104
104
|
|
|
105
105
|
**Co-Authored-By footer**: include whenever Claude authored or substantially
|
|
106
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
|
|
107
|
+
`Claude Sonnet 5.5`, `Claude Opus 5.5`) — never copy a model name from an example
|
|
108
108
|
or template. The trailer is a provenance marker, not a legal authorship
|
|
109
109
|
claim.
|
|
110
110
|
|
|
@@ -163,7 +163,7 @@ EventEmitter<TEventMap> to the public entry point.
|
|
|
163
163
|
|
|
164
164
|
Three new named exports; no existing export changed.
|
|
165
165
|
|
|
166
|
-
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
166
|
+
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
167
167
|
```
|
|
168
168
|
|
|
169
169
|
---
|
|
@@ -215,7 +215,7 @@ the durable fixes so later modules don't re-hit them.
|
|
|
215
215
|
|
|
216
216
|
No src/, test, or exports-map changes; zero semver impact.
|
|
217
217
|
|
|
218
|
-
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
218
|
+
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
219
219
|
```
|
|
220
220
|
|
|
221
221
|
---
|
|
@@ -244,5 +244,5 @@ output/ holds run archives, not raw output; the old name was misleading.
|
|
|
244
244
|
|
|
245
245
|
BREAKING CHANGE: Paths.outputDir removed; use archiveDir instead.
|
|
246
246
|
|
|
247
|
-
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
247
|
+
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
248
248
|
```
|
|
@@ -79,9 +79,11 @@ try {
|
|
|
79
79
|
// This project is ESM-only by design (no CommonJS -- see
|
|
80
80
|
// guard-no-commonjs.mjs); attw's default node16-from-CJS check
|
|
81
81
|
// flags exactly the interop this package intentionally doesn't
|
|
82
|
-
// support. Ignoring this one rule is
|
|
83
|
-
//
|
|
84
|
-
//
|
|
82
|
+
// support. Ignoring this one rule (`--ignore-rules`) is a narrow
|
|
83
|
+
// suppression of that known interop gap, not of a real problem. attw's
|
|
84
|
+
// documented ESM-only mechanism is the broader `--profile esm-only`,
|
|
85
|
+
// which ignores every CJS-mode resolution failure; this keeps the
|
|
86
|
+
// narrower rule on purpose so other CJS-side problems still surface.
|
|
85
87
|
"--ignore-rules",
|
|
86
88
|
"cjs-resolves-to-esm",
|
|
87
89
|
],
|
|
@@ -19,7 +19,8 @@ import { fieldList, fieldText, parseFrontmatter } from "./frontmatter.mjs";
|
|
|
19
19
|
* Model ids and aliases the rubric accepts: the current ids and aliases, plus
|
|
20
20
|
* ids that were once listed here, kept until Anthropic deprecates them. The
|
|
21
21
|
* ids follow Anthropic's models overview and model-deprecations pages
|
|
22
|
-
* (retrieved 2026-10-
|
|
22
|
+
* (retrieved 2026-10-08): Haiku 5.5 (released 2026-10-07) is current, and
|
|
23
|
+
* Haiku 4.5 is legacy but still active. A legacy id that was never listed here is
|
|
23
24
|
* deliberately not added, so the rule keeps nudging pins toward current
|
|
24
25
|
* models. Bump alongside a harness-guidance refresh sweep.
|
|
25
26
|
* @public Not imported anywhere else in this project -- exported only for
|
|
@@ -36,6 +37,7 @@ export const CURRENT_MODELS = [
|
|
|
36
37
|
"claude-sonnet-5",
|
|
37
38
|
"claude-sonnet-5-5",
|
|
38
39
|
"claude-fable-5-1",
|
|
40
|
+
"claude-haiku-5-5",
|
|
39
41
|
"claude-haiku-4-5",
|
|
40
42
|
"claude-haiku-4-5-20251001",
|
|
41
43
|
];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
3
|
"compilerOptions": {
|
|
4
|
-
"target": "
|
|
5
|
-
"lib": ["
|
|
4
|
+
"target": "es2025",
|
|
5
|
+
"lib": ["es2025"],
|
|
6
6
|
"types": ["node"],
|
|
7
7
|
"module": "nodenext",
|
|
8
8
|
"moduleResolution": "nodenext",
|
|
@@ -12,20 +12,24 @@
|
|
|
12
12
|
"noFallthroughCasesInSwitch": true,
|
|
13
13
|
"exactOptionalPropertyTypes": true,
|
|
14
14
|
"verbatimModuleSyntax": true,
|
|
15
|
+
"erasableSyntaxOnly": true,
|
|
15
16
|
"isolatedModules": true,
|
|
16
17
|
"noUncheckedSideEffectImports": true,
|
|
17
18
|
"noPropertyAccessFromIndexSignature": true,
|
|
18
19
|
"noImplicitReturns": true,
|
|
19
20
|
"allowUnreachableCode": false,
|
|
20
21
|
// isolatedDeclarations stays out of the base config deliberately: the
|
|
21
|
-
// build
|
|
22
|
-
//
|
|
23
|
-
//
|
|
22
|
+
// per-package build projects set it individually, but a tooling project
|
|
23
|
+
// that includes tests/ can't tolerate it (a test file re-exporting a
|
|
24
|
+
// fixture without an explicit return type is normal and fine). Promoting
|
|
25
|
+
// it here would fail typecheck on every test file rather than catch a
|
|
26
|
+
// real declaration-emit gap.
|
|
24
27
|
//
|
|
25
28
|
// noUnusedLocals / noUnusedParameters are deliberately NOT set:
|
|
26
29
|
// @typescript-eslint/no-unused-vars already covers both at error level
|
|
27
30
|
// (eslint.config.js) with an `^_`-prefix escape hatch this codebase
|
|
28
|
-
// relies on; tsc's flags honor no such pattern for locals
|
|
31
|
+
// relies on; tsc's flags honor no such pattern for locals, so adding
|
|
32
|
+
// them would duplicate an existing gate while breaking that convention.
|
|
29
33
|
"declaration": true,
|
|
30
34
|
"declarationMap": true,
|
|
31
35
|
"sourceMap": true,
|
|
@@ -16,6 +16,12 @@ templates/packs/<name>/
|
|
|
16
16
|
# exactly as templates/core/ does -- emitted the same way
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
A pack has no release of its own: it ships inside the CLI tarball, so a change
|
|
20
|
+
to one that is user-visible takes a CLI changeset whose summary starts with
|
|
21
|
+
`pack(<name>):`. The checklist for working on a pack lives in the source
|
|
22
|
+
repository's own `.claude/rules/pack-<name>.md` (m3l-groundwork, not shipped),
|
|
23
|
+
not in the pack, since a pack cannot ship a rule (see below).
|
|
24
|
+
|
|
19
25
|
## The wiring contract
|
|
20
26
|
|
|
21
27
|
**A pack never edits YAML or JavaScript.** It may add files under `files/`.
|
|
@@ -87,7 +87,7 @@ jobs:
|
|
|
87
87
|
fetch-depth: 0
|
|
88
88
|
|
|
89
89
|
- id: claude
|
|
90
|
-
uses: anthropics/claude-code-action@
|
|
90
|
+
uses: anthropics/claude-code-action@6fed3ca145920b639991cb756090506e1bcaf515 # v1.0.245
|
|
91
91
|
with:
|
|
92
92
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
93
93
|
# Swap in CLAUDE_CODE_OAUTH_TOKEN or the Workload Identity
|
|
@@ -70,7 +70,7 @@ jobs:
|
|
|
70
70
|
fetch-depth: 1
|
|
71
71
|
|
|
72
72
|
- name: Run Claude Code
|
|
73
|
-
uses: anthropics/claude-code-action@
|
|
73
|
+
uses: anthropics/claude-code-action@6fed3ca145920b639991cb756090506e1bcaf515 # v1.0.245
|
|
74
74
|
with:
|
|
75
75
|
# Option 1 (active default): a stored API key. Install the Claude
|
|
76
76
|
# GitHub App (https://github.com/apps/claude) and add
|
|
@@ -15,5 +15,5 @@
|
|
|
15
15
|
"packageScripts": {},
|
|
16
16
|
"verifySteps": []
|
|
17
17
|
},
|
|
18
|
-
"adoptNotes": "claude.yml only reacts to @claude mentions from a repository owner, member or collaborator (author_association), so an outside commenter on a public repository cannot start a job that holds contents: write and your auth secret; an org member with private membership may surface as CONTRIBUTOR and be skipped. It also commits through the GitHub API (use_commit_signing) so a signed-commits ruleset accepts its work. Both workflows need one thing this pack cannot verify or install: an ANTHROPIC_API_KEY repository secret (the active default), or a CLAUDE_CODE_OAUTH_TOKEN from a Claude subscription, or a Workload Identity Federation setup in the Anthropic Console -- see the comments in .github/workflows/claude.yml for all three. Until one is configured, either workflow runs and fails cleanly (an auth error in the run log, not a crash and not a silent no-op): claude.yml on an @claude mention, claude-pr-review.yml on the next PR open/push. An adopted project that already has its own .github/workflows/claude.yml or claude-pr-review.yml surfaces as an ordinary file conflict -- keep the existing one if it's already configured for that project's own auth choice, since this pack's version would just overwrite a working setup with its own generic defaults. triaging-scan-alerts reads code-scanning alerts, and nothing in the baseline or this pack produces them: turn on GitHub's CodeQL default setup for the repository (Settings > Code security), or install the `
|
|
18
|
+
"adoptNotes": "claude.yml only reacts to @claude mentions from a repository owner, member or collaborator (author_association), so an outside commenter on a public repository cannot start a job that holds contents: write and your auth secret; an org member with private membership may surface as CONTRIBUTOR and be skipped. It also commits through the GitHub API (use_commit_signing) so a signed-commits ruleset accepts its work. Both workflows need one thing this pack cannot verify or install: an ANTHROPIC_API_KEY repository secret (the active default), or a CLAUDE_CODE_OAUTH_TOKEN from a Claude subscription, or a Workload Identity Federation setup in the Anthropic Console -- see the comments in .github/workflows/claude.yml for all three. Until one is configured, either workflow runs and fails cleanly (an auth error in the run log, not a crash and not a silent no-op): claude.yml on an @claude mention, claude-pr-review.yml on the next PR open/push. An adopted project that already has its own .github/workflows/claude.yml or claude-pr-review.yml surfaces as an ordinary file conflict -- keep the existing one if it's already configured for that project's own auth choice, since this pack's version would just overwrite a working setup with its own generic defaults. triaging-scan-alerts reads code-scanning alerts, and nothing in the baseline or this pack produces them: turn on GitHub's CodeQL default setup for the repository (Settings > Code security), or install the `supply-chain` pack, whose scorecard.yml uploads SARIF, otherwise the skill's first call returns \"code scanning is not enabled\" and reports nothing. The other two skills have no such dependency: they install anywhere a .claude/ directory exists and need only `gh` CLI auth (already required by every other gh-CLI-based skill this baseline ships). watching-pr-checks' handoff to /triaging-ci degrades gracefully if the project doesn't have that skill installed -- it still reports the real failure, just without the handoff."
|
|
19
19
|
}
|
|
@@ -5,17 +5,19 @@
|
|
|
5
5
|
*
|
|
6
6
|
* The command receives one JSON object on stdin per refresh tick: the base
|
|
7
7
|
* hook fields, a `columns` field (usable row width), and a `tasks` array.
|
|
8
|
-
* Each task may carry `id`, `name`, `type`, `
|
|
9
|
-
* `startTime`, `model`, `effort`, `contextWindowSize`,
|
|
10
|
-
* `tokenSamples`, `cwd` -- `model`/`contextWindowSize` require
|
|
11
|
-
* v2.1.205+ and `
|
|
12
|
-
* simply absent and the row omits them.
|
|
8
|
+
* Each task may carry `id`, `name`, `type`, `agentType`, `status`,
|
|
9
|
+
* `description`, `label`, `startTime`, `model`, `effort`, `contextWindowSize`,
|
|
10
|
+
* `tokenCount`, `tokenSamples`, `cwd` -- `model`/`contextWindowSize` require
|
|
11
|
+
* Claude Code v2.1.205+, `effort` v2.1.214+ and `agentType` v2.1.293+. On an
|
|
12
|
+
* older version those fields are simply absent and the row omits them.
|
|
13
13
|
*
|
|
14
14
|
* Output is one JSON line per row to override: `{"id": "<task id>", "content":
|
|
15
15
|
* "<row body>"}`. A task is left with Claude Code's own default rendering
|
|
16
16
|
* (name · description · token count) by omitting it from the output entirely
|
|
17
|
-
* -- this happens whenever `id`
|
|
18
|
-
* guessing at a row.
|
|
17
|
+
* -- this happens whenever `id` is missing/unusable, or both `name` and
|
|
18
|
+
* `agentType` are, rather than guessing at a row. `name` is the row's label
|
|
19
|
+
* when present; `agentType` (the same value hooks receive as `agent_type`)
|
|
20
|
+
* stands in for a task that has no name.
|
|
19
21
|
*
|
|
20
22
|
* The elapsed-time color thresholds (15/30 minutes) mark a subagent worth a
|
|
21
23
|
* glance and one that has probably stalled.
|
|
@@ -46,9 +48,9 @@ export const ELAPSED_WARN_THRESHOLD_SEC = 15 * 60;
|
|
|
46
48
|
export const ELAPSED_HIGH_THRESHOLD_SEC = 30 * 60;
|
|
47
49
|
|
|
48
50
|
/**
|
|
49
|
-
* @param {unknown} value a task's `startTime` field. The
|
|
50
|
-
*
|
|
51
|
-
*
|
|
51
|
+
* @param {unknown} value a task's `startTime` field. The statusline docs
|
|
52
|
+
* document it as a number of milliseconds since the Unix epoch; an ISO 8601
|
|
53
|
+
* string is still accepted leniently.
|
|
52
54
|
* @returns {number | null} epoch milliseconds, or null when unparseable.
|
|
53
55
|
*/
|
|
54
56
|
export function parseStartTime(value) {
|
|
@@ -124,12 +126,25 @@ export function formatEffort(effort) {
|
|
|
124
126
|
*/
|
|
125
127
|
export function formatSubagentRow(task, env) {
|
|
126
128
|
if (typeof task !== "object" || task === null) return null;
|
|
127
|
-
const {
|
|
128
|
-
|
|
129
|
+
const {
|
|
130
|
+
id,
|
|
131
|
+
name,
|
|
132
|
+
agentType,
|
|
133
|
+
effort,
|
|
134
|
+
startTime,
|
|
135
|
+
tokenCount,
|
|
136
|
+
contextWindowSize,
|
|
137
|
+
} = /** @type {Record<string, unknown>} */ (task);
|
|
129
138
|
if (typeof id !== "string" || id.length === 0) return null;
|
|
130
|
-
|
|
139
|
+
const label =
|
|
140
|
+
typeof name === "string" && name.length > 0
|
|
141
|
+
? name
|
|
142
|
+
: typeof agentType === "string" && agentType.length > 0
|
|
143
|
+
? agentType
|
|
144
|
+
: null;
|
|
145
|
+
if (label === null) return null;
|
|
131
146
|
|
|
132
|
-
const segments = [
|
|
147
|
+
const segments = [label];
|
|
133
148
|
|
|
134
149
|
const effortText = formatEffort(effort);
|
|
135
150
|
if (effortText !== null) segments.push(`${MAGENTA}${effortText}${RESET}`);
|
|
@@ -66,5 +66,5 @@
|
|
|
66
66
|
"packageScripts": {},
|
|
67
67
|
"verifySteps": []
|
|
68
68
|
},
|
|
69
|
-
"adoptNotes": "The statusline and compaction hooks install anywhere a .claude/ directory exists, but guard-readonly-bash.mjs imports ../../bin/lib/agent-roster.mjs (the baseline's list of writer spokes) -- copy that file alongside it in an adopted project, or skip that one hook. The PreCompact hook writes tmp/compact-handoff-<session_id>.json under the git worktree the session runs in (from the hook payload's cwd, since CLAUDE_PROJECT_DIR does not follow a session into a linked worktree) -- fresh mode's baseline .gitignore already lists tmp/ unconditionally, but an adopted project's own .gitignore was never touched by adopt mode, so add tmp/ there too before this pack's hooks run, or the handoff artifact can end up committed. The statusline scripts read only the stdin payload, .git/HEAD (via node:fs, never a git subprocess) and process.availableMemory()/os.totalmem(), so they install anywhere a .claude/ directory exists -- no dependency on the baseline's file layout. The one real adopt risk is the two top-level settings keys: a project that already defines statusLine or subagentStatusLine collides, and its existing value must be shown and decided on, never overwritten -- if either is already set, skip that key and the three statusline scripts, and install the rest of this pack (the hooks) normally rather than failing the whole install. A .claude/settings.local.json or the user's own ~/.claude/settings.json statusLine also shadows the project one -- check both before concluding the pack is wired. rate_limits.spend_limit and prompt_cache need Claude Code v2.1.251+, and the per-subagent model/contextWindowSize/effort fields need v2.1.205+/v2.1.214+; each renders only when present, so an older Claude Code degrades to fewer segments rather than breaking. The scripts behave the same on macOS and Linux; the memory segment uses process.availableMemory() (Node 22+), and on macOS with an older Node it is hidden rather than shown from os.freemem(), which undercounts there. Below roughly 40 columns the layout drops segments by priority rather than wrapping. The size-ratchet gate and the type-design-analyzer agent that used to ship here are now the separate `quality` pack."
|
|
69
|
+
"adoptNotes": "The statusline and compaction hooks install anywhere a .claude/ directory exists, but guard-readonly-bash.mjs imports ../../bin/lib/agent-roster.mjs (the baseline's list of writer spokes) -- copy that file alongside it in an adopted project, or skip that one hook. The PreCompact hook writes tmp/compact-handoff-<session_id>.json under the git worktree the session runs in (from the hook payload's cwd, since CLAUDE_PROJECT_DIR does not follow a session into a linked worktree) -- fresh mode's baseline .gitignore already lists tmp/ unconditionally, but an adopted project's own .gitignore was never touched by adopt mode, so add tmp/ there too before this pack's hooks run, or the handoff artifact can end up committed. The statusline scripts read only the stdin payload, .git/HEAD (via node:fs, never a git subprocess) and process.availableMemory()/os.totalmem(), so they install anywhere a .claude/ directory exists -- no dependency on the baseline's file layout. The one real adopt risk is the two top-level settings keys: a project that already defines statusLine or subagentStatusLine collides, and its existing value must be shown and decided on, never overwritten -- if either is already set, skip that key and the three statusline scripts, and install the rest of this pack (the hooks) normally rather than failing the whole install. A .claude/settings.local.json or the user's own ~/.claude/settings.json statusLine also shadows the project one -- check both before concluding the pack is wired. rate_limits.spend_limit and prompt_cache need Claude Code v2.1.251+, and the per-subagent model/contextWindowSize/effort fields need v2.1.205+/v2.1.214+, and the agentType label a row falls back to when a subagent has no name needs v2.1.293+; each renders only when present, so an older Claude Code degrades to fewer segments rather than breaking. The scripts behave the same on macOS and Linux; the memory segment uses process.availableMemory() (Node 22+), and on macOS with an older Node it is hidden rather than shown from os.freemem(), which undercounts there. Below roughly 40 columns the layout drops segments by priority rather than wrapping. The size-ratchet gate and the type-design-analyzer agent that used to ship here are now the separate `quality` pack."
|
|
70
70
|
}
|
|
@@ -37,5 +37,5 @@
|
|
|
37
37
|
"node bin/check-license-headers.mjs --fix",
|
|
38
38
|
"git add -A"
|
|
39
39
|
],
|
|
40
|
-
"adoptNotes": "REQUIRED right after install, before the first pnpm verify -- two one-time steps, or pnpm verify fails immediately, not from a real regression but from two setup gaps this pack cannot close itself: (1) `pnpm add -D @changesets/cli` -- the wiring contract only extends package.json's scripts, never its dependencies (see templates/packs/README.md), so the wired `changeset`/`version:packages` scripts reference a binary knip's own unlisted-binaries check correctly flags as missing until this is installed; (2) `git add -A`, then `node bin/check-license-headers.mjs --fix`, then `git add -A` again and commit the result (the gate only checks git-tracked files, so a fresh project with nothing staged passes it vacuously and fails on every file at its first push) -- templates/core has never had a license-header gate before this pack, so every one of its own files (hooks, bin/ scripts, CI workflows, the placeholder src/tests, and the workflows of any other installed pack such as `github` or `supply-chain`) is missing the SPDX header this pack's own license-headers gate now requires, the same one-time cost this repo itself paid when it adopted the same gate (see CLAUDE.md's Definition of Done). In a hand-copied install the CLI does not substitute tokens, so replace every literal `__PROJECT_NAME__` in the copied files (REUSE.toml, the SPDX headers in bin/, the workflows) with the project's real name first. Fresh mode only: a release pipeline encodes decisions (whether the package is actually public, which registry, whether a GitHub App backs the version PR) this pack cannot survey or guess at safely, so it is staged like every other pack (at .groundwork/packs/publishing/) but never auto-installed in adopt mode -- if an adopted project wants it, copy .groundwork/packs/publishing/files/ by hand, dropping the `.staged` suffix from every name (or copy templates/packs/publishing/files/ from this repo directly) and work through the one-time setup below. check-publish-version.mjs is deliberately NOT one of this pack's wired verify steps: with changesets, package.json's version on the release branch is always the one just published between releases, so a version-already-published check running on every ordinary push (via pnpm verify / pre-push) would fail every single time until the next version PR lands -- it is instead invoked directly as a step inside release.yml's pack job, the one place \"is this version about to collide with an already-published one\" is actually the right question to ask. The two remaining verify-group gates (dts-deps, license-headers) install anywhere a bin/verify.mjs-shaped gate runner exists; wire them into the project's real one if it differs. Both check-publish-version.mjs and check-dts-deps.mjs no-op (checked: 0, not a failure) on a package.json with `private: true` (the baseline's own default) or (check-dts-deps.mjs only) with no dist/**/*.d.ts yet -- flip `private` to `false` (and set a real `name`) once the package is actually meant to publish. The pack assumes a public package: `.changeset/config.json` ships `access: public`, npm refuses `restricted` for an unscoped name, and the publish shim always passes `--provenance`, which npm ties to a public source repository -- a private scoped package needs `access: restricted` and provenance removed from bin/lib/npm-publish-args.mjs. `@changesets/changelog-github` (and its `repo` option) is a drop-in swap for `.changeset/config.json`'s plain default changelog generator, once this project's GitHub repository is known, for changelog entries that link back to the originating PR/commit -- see `.changeset/README.md`. One-time setup before release.yml's first real run (distinct from the two pnpm-verify prerequisites above), distilled from this repo's own .claude/rules/releases.md: (1) npm cannot configure a trusted publisher for a package that doesn't exist yet -- publish once by hand with a temporary token first; (2) the trusted publisher is bound to the exact workflow filename `release.yml` -- renaming or moving it breaks publishing until reconfigured on npmjs.com; (3) set the trusted publisher's allowed actions to staged-only (`npm stage publish`, npm's own default and recommendation since 2026-09-03) -- a maintainer runs `npm stage approve <id>` (2FA, never automatable) before a version actually installs; (4) create an `npm-publish` GitHub environment (via `gh api`, not committed as JSON) with a required reviewer, restricted to the release branch, so the `publish` job itself pauses before the git tag/Release/stage-publish exist; (5) if branch protection requires status checks on every PR with no bypass actor, the version-PR job needs a GitHub App installation token (`APP_CLIENT_ID`/`APP_PRIVATE_KEY` repo secrets), not the default GITHUB_TOKEN, or those checks never trigger and the PR can never merge -- if this project has no such rule, `github-token: ${{ secrets.GITHUB_TOKEN }}` is enough and the app-token step can be dropped. release.yml pins `.github/release-tools/` (the npm CLI version) but the baseline's dependabot.yml only covers the github-actions ecosystem, so that pin goes stale unless you add an `npm` entry with `directory: \"/.github/release-tools\"` to the project's own .github/dependabot.yml. Security note, as of 2026-10-
|
|
40
|
+
"adoptNotes": "REQUIRED right after install, before the first pnpm verify -- two one-time steps, or pnpm verify fails immediately, not from a real regression but from two setup gaps this pack cannot close itself: (1) `pnpm add -D @changesets/cli` -- the wiring contract only extends package.json's scripts, never its dependencies (see templates/packs/README.md), so the wired `changeset`/`version:packages` scripts reference a binary knip's own unlisted-binaries check correctly flags as missing until this is installed; (2) `git add -A`, then `node bin/check-license-headers.mjs --fix`, then `git add -A` again and commit the result (the gate only checks git-tracked files, so a fresh project with nothing staged passes it vacuously and fails on every file at its first push) -- templates/core has never had a license-header gate before this pack, so every one of its own files (hooks, bin/ scripts, CI workflows, the placeholder src/tests, and the workflows of any other installed pack such as `github` or `supply-chain`) is missing the SPDX header this pack's own license-headers gate now requires, the same one-time cost this repo itself paid when it adopted the same gate (see CLAUDE.md's Definition of Done). In a hand-copied install the CLI does not substitute tokens, so replace every literal `__PROJECT_NAME__` in the copied files (REUSE.toml, the SPDX headers in bin/, the workflows) with the project's real name first. Fresh mode only: a release pipeline encodes decisions (whether the package is actually public, which registry, whether a GitHub App backs the version PR) this pack cannot survey or guess at safely, so it is staged like every other pack (at .groundwork/packs/publishing/) but never auto-installed in adopt mode -- if an adopted project wants it, copy .groundwork/packs/publishing/files/ by hand, dropping the `.staged` suffix from every name (or copy templates/packs/publishing/files/ from this repo directly) and work through the one-time setup below. check-publish-version.mjs is deliberately NOT one of this pack's wired verify steps: with changesets, package.json's version on the release branch is always the one just published between releases, so a version-already-published check running on every ordinary push (via pnpm verify / pre-push) would fail every single time until the next version PR lands -- it is instead invoked directly as a step inside release.yml's pack job, the one place \"is this version about to collide with an already-published one\" is actually the right question to ask. The two remaining verify-group gates (dts-deps, license-headers) install anywhere a bin/verify.mjs-shaped gate runner exists; wire them into the project's real one if it differs. Both check-publish-version.mjs and check-dts-deps.mjs no-op (checked: 0, not a failure) on a package.json with `private: true` (the baseline's own default) or (check-dts-deps.mjs only) with no dist/**/*.d.ts yet -- flip `private` to `false` (and set a real `name`) once the package is actually meant to publish. The pack assumes a public package: `.changeset/config.json` ships `access: public`, npm refuses `restricted` for an unscoped name, and the publish shim always passes `--provenance`, which npm ties to a public source repository -- a private scoped package needs `access: restricted` and provenance removed from bin/lib/npm-publish-args.mjs. `@changesets/changelog-github` (and its `repo` option) is a drop-in swap for `.changeset/config.json`'s plain default changelog generator, once this project's GitHub repository is known, for changelog entries that link back to the originating PR/commit -- see `.changeset/README.md`. One-time setup before release.yml's first real run (distinct from the two pnpm-verify prerequisites above), distilled from this repo's own .claude/rules/releases.md: (1) npm cannot configure a trusted publisher for a package that doesn't exist yet -- publish once by hand with a temporary token first; (2) the trusted publisher is bound to the exact workflow filename `release.yml` -- renaming or moving it breaks publishing until reconfigured on npmjs.com; (3) set the trusted publisher's allowed actions to staged-only (`npm stage publish`, npm's own default and recommendation since 2026-09-03) -- a maintainer runs `npm stage approve <id>` (2FA, never automatable) before a version actually installs; (4) create an `npm-publish` GitHub environment (via `gh api`, not committed as JSON) with a required reviewer, restricted to the release branch, so the `publish` job itself pauses before the git tag/Release/stage-publish exist; (5) if branch protection requires status checks on every PR with no bypass actor, the version-PR job needs a GitHub App installation token (`APP_CLIENT_ID`/`APP_PRIVATE_KEY` repo secrets), not the default GITHUB_TOKEN, or those checks never trigger and the PR can never merge -- if this project has no such rule, `github-token: ${{ secrets.GITHUB_TOKEN }}` is enough and the app-token step can be dropped. release.yml pins `.github/release-tools/` (the npm CLI version) but the baseline's dependabot.yml only covers the github-actions ecosystem, so that pin goes stale unless you add an `npm` entry with `directory: \"/.github/release-tools\"` to the project's own .github/dependabot.yml. Security note, as of 2026-10-08: the npm pinned in the shipped `.github/release-tools/package-lock.json` (12.2.0) bundles undici 6.28.0, ip-address 10.5.0, brace-expansion 5.0.9, postcss-selector-parser 7.1.4 and http-cache-semantics 4.2.0, which carry open advisories; every npm release found (11.20.0, 11.21.0, 12.2.0) bundles the same versions, and overrides cannot change bundled dependencies. Reading the npm tarballs and lockfile, no reach was found from what the shipped workflow runs (`npm ci --ignore-scripts`, `npm stage publish <tarball>` and `npm stage list`; the tarball is packed earlier by pnpm, not npm, and the shipped workflow configures no proxy; if your runner sets one, for example HTTPS_PROXY on a self-hosted runner behind an egress proxy, the ip-address advisories may apply), the minimatch callers on those commands (Arborist, and tuf-js under sigstore) take their patterns from your own lockfile and from signed registry metadata, postcss-selector-parser is called only by `npm query` and `npm sbom`, and http-cache-semantics sits behind make-fetch-happen's private (`shared: false`) cache, but that is analysis, not a run of the code. Re-check on the day of each release and bump the pinned npm as soon as a release bundling undici >= 6.28.1, ip-address >= 10.7.1, brace-expansion >= 5.0.12 and postcss-selector-parser >= 7.1.6 exists; fixes are open upstream as npm/cli#10088 and #10089. http-cache-semantics has no fixed release (4.3.0 is outside the advisory's range, but upstream closed the report as not planned), so a bump past 4.2.0 only clears its alert. The secret-scanning and Scorecard workflows that used to ship here are now the separate `supply-chain` pack."
|
|
41
41
|
}
|
|
@@ -3,7 +3,7 @@ name: type-design-analyzer
|
|
|
3
3
|
description: Read-only type-design reviewer. Rates the type design quality of changed exports on four dimensions (encapsulation, invariant expression, invariant usefulness, invariant enforcement), each scored 1–10, and flags violations of strict-TS / branded-type / make-illegal-states-unrepresentable rules. Use after writing or changing any exported TypeScript types, interfaces, or function signatures. Complements code-reviewer (general structure/SOLID).
|
|
4
4
|
tools: Read, Grep, Glob, Bash
|
|
5
5
|
disallowedTools: Agent
|
|
6
|
-
model: claude-opus-5
|
|
6
|
+
model: claude-opus-5-5
|
|
7
7
|
effort: xhigh
|
|
8
8
|
maxTurns: 40
|
|
9
9
|
color: orange
|
|
@@ -25,5 +25,5 @@
|
|
|
25
25
|
}
|
|
26
26
|
]
|
|
27
27
|
},
|
|
28
|
-
"adoptNotes": "check-file-budget.mjs's ROOTS default assumes a flat src/+tests/ layout and its ceilings are this baseline's defaults -- in an adopted project, re-point ROOTS at the project's real source layout (or drop the gate) before wiring it. check-file-budget.mjs imports ./lib/report.mjs, a baseline file that an adopted project will not have -- copy bin/lib/report.mjs alongside the gate, or treat the gate as not installable. The verify step assumes a bin/verify.mjs-shaped gate runner; if the project has none, install the agent and report the gate as not installable rather than inventing one. The type-design-analyzer agent has no such dependency and installs anywhere a .claude/agents/ directory exists; it is a read-only review spoke (model claude-opus-5 at xhigh effort, more expensive than the baseline's reviewers, which run claude-opus-5-5 at medium effort), and nothing in the baseline's hub-and-spoke instructions dispatches it by name -- ask for it after changing exported types, or add it to the project's own CLAUDE.md review step."
|
|
28
|
+
"adoptNotes": "check-file-budget.mjs's ROOTS default assumes a flat src/+tests/ layout and its ceilings are this baseline's defaults -- in an adopted project, re-point ROOTS at the project's real source layout (or drop the gate) before wiring it. check-file-budget.mjs imports ./lib/report.mjs, a baseline file that an adopted project will not have -- copy bin/lib/report.mjs alongside the gate, or treat the gate as not installable. The verify step assumes a bin/verify.mjs-shaped gate runner; if the project has none, install the agent and report the gate as not installable rather than inventing one. The type-design-analyzer agent has no such dependency and installs anywhere a .claude/agents/ directory exists; it is a read-only review spoke (model claude-opus-5-5 at xhigh effort, more expensive than the baseline's reviewers, which run claude-opus-5-5 at medium effort), and nothing in the baseline's hub-and-spoke instructions dispatches it by name -- ask for it after changing exported types, or add it to the project's own CLAUDE.md review step."
|
|
29
29
|
}
|