@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
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# Step 0 — Reconcile (adopt mode only): detail
|
|
2
|
+
|
|
3
|
+
> Full text of Step 0 from `SKILL.md`. Read this whole file before acting on Step 0, and re-read it after a compaction: a compacted session keeps only the start of `SKILL.md`.
|
|
4
|
+
|
|
5
|
+
Contents:
|
|
6
|
+
|
|
7
|
+
1. Look for `.groundwork/inventory.json` (fresh vs. interrupted vs. adopt)
|
|
8
|
+
2. The deep read (the survey is an index, not an interpretation)
|
|
9
|
+
3. Write the findings back into `.groundwork/adoption-report.md`
|
|
10
|
+
4. Confirm (one `AskUserQuestion` round: additions, conflicts, packs)
|
|
11
|
+
5. Record the confirmed decisions to `.groundwork/adoption-decisions.json`
|
|
12
|
+
|
|
13
|
+
## Step 0 — Reconcile (adopt mode only)
|
|
14
|
+
|
|
15
|
+
1. Look for `.groundwork/inventory.json`.
|
|
16
|
+
- **Absent, and no `.groundwork/` directory either → this is a fresh
|
|
17
|
+
bootstrap; skip straight to Step 1.** Everything below this step applies
|
|
18
|
+
only when an inventory exists.
|
|
19
|
+
- **Absent, but `.groundwork/` exists → a previous CLI adopt run did not
|
|
20
|
+
complete** (the CLI deletes the old inventory before it stages anything
|
|
21
|
+
and writes the new one last, so no inventory beside a `.groundwork/`
|
|
22
|
+
directory means the run died part-way). **Stop. Never fall through to
|
|
23
|
+
the fresh flow** -- that would run fresh-mode tailoring on an
|
|
24
|
+
established project. Tell the user: "`.groundwork/` is incomplete: a
|
|
25
|
+
previous adopt run did not finish, or `inventory.json` was removed.
|
|
26
|
+
If this project was never adopted (it was bootstrapped fresh), delete
|
|
27
|
+
`.groundwork/` and run `/customize` again. Otherwise re-run
|
|
28
|
+
`npx @monte3l/groundwork@rc .` and then run `/customize` again."
|
|
29
|
+
Change nothing.
|
|
30
|
+
- **Present, but not valid JSON, or its `schemaVersion` is not an integer
|
|
31
|
+
of at least 1 → stop and change nothing.** Tell the user: "`.groundwork/inventory.json`
|
|
32
|
+
is not valid JSON or has no usable `schemaVersion`. Re-run
|
|
33
|
+
`npx @monte3l/groundwork@rc .` and then run `/customize` again."
|
|
34
|
+
|
|
35
|
+
**Check `inventory.schemaVersion` before reading anything else.** This
|
|
36
|
+
skill understands schema versions **1 through 5** (the highest it knows is
|
|
37
|
+
5). If `schemaVersion` is **higher than 5**, the CLI that wrote it is newer
|
|
38
|
+
than this plugin: **stop and change nothing**, do not interpret the
|
|
39
|
+
inventory (a newer schema may have renamed or repurposed fields, and a
|
|
40
|
+
confident misreading is worse than none), and tell the user to update the
|
|
41
|
+
plugin and re-run `/customize`. Say which update applies to the copy
|
|
42
|
+
that is running. For the plugin install, run `/plugin update`. For a
|
|
43
|
+
project-local copy in `.claude/skills/customize/`, `/plugin update` does
|
|
44
|
+
not touch it, and re-running the CLI alone does not either: the CLI
|
|
45
|
+
never overwrites a copy that differs (nor removes any other entry the
|
|
46
|
+
project owns there, nor writes through a symlinked `.claude`), it writes a
|
|
47
|
+
fresh one to `.groundwork/customize/`, which Claude Code does not load. So delete
|
|
48
|
+
that directory first (this discards any local edits the project made to
|
|
49
|
+
its copy) and then re-run `npx @monte3l/groundwork@rc .`. A
|
|
50
|
+
copy in `.groundwork/customize/` is refreshed by re-running the CLI. What
|
|
51
|
+
each version added:
|
|
52
|
+
|
|
53
|
+
| `schemaVersion` | Adds | If absent |
|
|
54
|
+
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
55
|
+
| 1 | the survey, `conflicts` | (the floor) |
|
|
56
|
+
| 2 | `packs` | no packs to offer; skip the pack question |
|
|
57
|
+
| 3 | `harnessGrade`, `harnessConformance` | skip the harness-grade starting point |
|
|
58
|
+
| 4 | `toolchainGrade`, `toolchainConformance` | skip the toolchain-grade starting point |
|
|
59
|
+
| 5 | `stagedBaseline` (`{ dir, suffix, files }`): the baseline additions staged at `.groundwork/baseline/`, each as `{ path, staged, sha256 }`; `stagedPacks` (per pack: `{ name, dir, suffix, manifest, files }`): every pack staged at `.groundwork/packs/<name>/`, its manifest and each file as `{ path, staged, sha256 }` | schema 1-4 only: read baseline additions from `inventory.templateRoot`; packs use the unsuffixed copy under `.groundwork/packs/` |
|
|
60
|
+
|
|
61
|
+
**For a schema 5 inventory only: verify the staged baseline now, before
|
|
62
|
+
anything is offered to the user.** A schema 1-4 inventory has no
|
|
63
|
+
`stagedBaseline`: skip this whole block (every bullet below, including
|
|
64
|
+
its stop list) and use the `inventory.templateRoot` fallback in Round 1.
|
|
65
|
+
The inventory lives in the project tree and is untrusted input, so check
|
|
66
|
+
it before trusting any entry:
|
|
67
|
+
|
|
68
|
+
- `stagedBaseline.dir` must equal `.groundwork/baseline` exactly, and
|
|
69
|
+
`stagedBaseline.suffix` must equal `.staged` exactly. Take both values
|
|
70
|
+
from this list, not from the inventory: a `dir` of `.` with an empty
|
|
71
|
+
`suffix` would make you "verify" live project files.
|
|
72
|
+
- For every entry of `inventory.stagedBaseline.files`, require
|
|
73
|
+
`staged === path + ".staged"`, and require `path` to be relative, free
|
|
74
|
+
of any `..` segment, and not absolute. Reject the entry if `path` is
|
|
75
|
+
`""` or `"."`. Reject it too if `path` contains a `\` or a `:` (a
|
|
76
|
+
Windows `..\..` or `C:foo` would slip past the other checks). Reject a
|
|
77
|
+
duplicate `path` or a duplicate `staged` among the entries. These fields
|
|
78
|
+
use `/` separators on every platform: `conflicts[].relPath`,
|
|
79
|
+
`packs[].fileConflicts[].relPath`, `stagedBaseline.dir`,
|
|
80
|
+
`stagedBaseline.files[].path` and its `.staged` name, `stagedPacks[].dir`,
|
|
81
|
+
`stagedPacks[].files[].path` and `stagedPacks[].files[].staged`. Other paths in
|
|
82
|
+
the inventory are not normalized: `templateRoot` and `targetDir` are
|
|
83
|
+
absolute native paths, as are the survey's tsconfig chain entries.
|
|
84
|
+
- The `absent` conflicts in `inventory.conflicts` and the paths in
|
|
85
|
+
`stagedBaseline.files` must name the same set of files. If they
|
|
86
|
+
disagree (an `absent` conflict with no staged entry, or a staged entry
|
|
87
|
+
that is not an `absent` conflict), that is a mismatch: stop, with the
|
|
88
|
+
same message as a hash mismatch, before anything is offered or approved.
|
|
89
|
+
- Read each staged file at `.groundwork/baseline/<staged>` **once**.
|
|
90
|
+
It must exist, and the SHA-256 of its **raw bytes** (no end-of-line
|
|
91
|
+
normalization, no decoding) must equal the entry's `sha256`. Compute it
|
|
92
|
+
with, for example, this one-liner (it targets a POSIX shell or Git Bash,
|
|
93
|
+
not `cmd.exe` or an old PowerShell), passing the file as a single quoted
|
|
94
|
+
argument in place of `<file>`:
|
|
95
|
+
`node -e 'process.stdout.write(require("crypto").createHash("sha256").update(require("fs").readFileSync(process.argv[1])).digest("hex"))' <file>`
|
|
96
|
+
Keep those same bytes for the install in Round 1 (substitute tokens into
|
|
97
|
+
them) rather than reading the file a second time.
|
|
98
|
+
- **Staged packs, in the same block.** For every entry of
|
|
99
|
+
`inventory.stagedPacks`:
|
|
100
|
+
- `dir` must equal `.groundwork/packs/<name>`, built from the entry's
|
|
101
|
+
own `name`, and `suffix` must equal `.staged`. Take both from this
|
|
102
|
+
list, not from the inventory.
|
|
103
|
+
- `name` must be a single path segment: not empty, not `.` or `..`, and
|
|
104
|
+
free of `/`, `\` and `:`. Reject a duplicate pack `name`.
|
|
105
|
+
- `manifest.path` must equal `pack.json` and `manifest.staged` must
|
|
106
|
+
equal `pack.json.staged`.
|
|
107
|
+
- For every entry of its `files`, require `staged === path + ".staged"`,
|
|
108
|
+
and apply the same path checks as the staged baseline's files above
|
|
109
|
+
(relative, no `..` segment, not absolute, not `""` or `"."`, no `\` or
|
|
110
|
+
`:`). Reject a duplicate `path` or a duplicate `staged` among one
|
|
111
|
+
pack's files.
|
|
112
|
+
- The pack names in `inventory.packs` and in `inventory.stagedPacks`
|
|
113
|
+
must be the same set, and for each pack its `fileConflicts[].relPath`
|
|
114
|
+
set must equal its `files[].path` set.
|
|
115
|
+
- Read the staged manifest at `<dir>/pack.json.staged` and each staged
|
|
116
|
+
file at `<dir>/files/<staged>` once, and apply the SHA-256 check above
|
|
117
|
+
to the raw bytes of each. Keep the bytes Step 0.1 read (hash-checked
|
|
118
|
+
when a command could run): Round 1's install and the prototype-key
|
|
119
|
+
check in Step 0.4(c) use them, with no second read.
|
|
120
|
+
- **No shell tool available.** If a Bash or other command-running tool is
|
|
121
|
+
available, compute the hash as above. If you cannot run a command, do
|
|
122
|
+
not work around it: write no scratch script; while verifying, make no
|
|
123
|
+
`Write` or `Edit` outside `.groundwork/` (Round 1's confirmed installs are
|
|
124
|
+
not part of verification), dispatch no subagent to look for a shell, and
|
|
125
|
+
search for a command tool at most once. Skip only the SHA-256
|
|
126
|
+
comparison. Run every other check in this block with the file tools
|
|
127
|
+
(`Read`, `Glob`, `Grep`): the files exist, `staged === path + ".staged"`,
|
|
128
|
+
the paths are relative with no `..`, `\` or `:`, no duplicates, `dir`
|
|
129
|
+
and `suffix` exact, the `absent` conflicts and `stagedBaseline.files`
|
|
130
|
+
name the same set, and no extra staged file exists: list the staged
|
|
131
|
+
files with the `Glob` pattern `.groundwork/baseline/**/*.staged` and
|
|
132
|
+
compare with `stagedBaseline.files`. A `Glob` can skip hidden paths
|
|
133
|
+
(most staged files are dot-paths), honour an ignore file or truncate a
|
|
134
|
+
long list, so a result shorter than `stagedBaseline.files.length`, or
|
|
135
|
+
that looks truncated, is undetermined: report it in the Step 0.4 summary
|
|
136
|
+
and continue, since every listed entry's existence is already checked
|
|
137
|
+
one by one; only an extra `*.staged` file, one whose path is not in
|
|
138
|
+
`stagedBaseline.files`, stops the run. Run the same structural checks
|
|
139
|
+
for `inventory.stagedPacks`: each pack's `<dir>/pack.json.staged` and
|
|
140
|
+
every `<dir>/files/<staged>` exists, `staged === path + ".staged"` with
|
|
141
|
+
paths that are relative and free of `..`, `\` and `:`, no duplicates,
|
|
142
|
+
`name`, `dir`, `suffix`, `manifest.path` and `manifest.staged` exact,
|
|
143
|
+
the pack names in `inventory.packs` and `inventory.stagedPacks` the
|
|
144
|
+
same set, and each pack's `fileConflicts[].relPath` and `files[].path`
|
|
145
|
+
the same set. List each pack's staged files with the `Glob` pattern
|
|
146
|
+
`.groundwork/packs/**/*.staged` (the same hidden-path and
|
|
147
|
+
truncation caveats apply) and compare with every listed manifest and
|
|
148
|
+
file: an extra `*.staged` file at any depth under `.groundwork/packs/` that is neither a listed manifest nor a listed file stops the run. This is a partial no-shell
|
|
149
|
+
substitute for what the hash proves, and it is not tamper-resistance.
|
|
150
|
+
Then spend no further turns on verification and go on to the deep
|
|
151
|
+
read. State this plainly at the top of your first message to the user (the Step 0.4 summary, not a
|
|
152
|
+
separate earlier stop), and ask whether to continue or stop in that
|
|
153
|
+
same confirmation: the SHA-256 check was skipped because no command
|
|
154
|
+
could be run; a passing check would only have proven that the staging
|
|
155
|
+
is complete and matches the inventory, not that the files are
|
|
156
|
+
untampered; and which structural checks you did verify instead. Also
|
|
157
|
+
record the skip in `.groundwork/adoption-report.md` when you write the
|
|
158
|
+
findings back in Step 0.3. A structural failure, including an extra staged file, still stops, exactly as the stop list below says.
|
|
159
|
+
- An empty `files` list is legitimate (nothing was missing from the
|
|
160
|
+
project) and creates no `.groundwork/baseline/` directory; that alone is
|
|
161
|
+
not a failure.
|
|
162
|
+
- **For a schema 5 inventory, a `stagedBaseline` that is missing, not an
|
|
163
|
+
object, or whose `files` is not an array also stops the run** (a
|
|
164
|
+
`files: {}` is not an empty list); so does a `stagedPacks` that is
|
|
165
|
+
missing or not an array, or a pack entry that is not an object or whose
|
|
166
|
+
`files` is not an array. **Any invalid entry, missing file or hash mismatch (including a
|
|
167
|
+
wrong `dir` or `suffix`), any duplicate `staged` or `path` or pack `name`, any
|
|
168
|
+
`absent` conflicts and `stagedBaseline.files` that do not name the same
|
|
169
|
+
set, any pack whose names or file paths disagree with `inventory.packs`, and
|
|
170
|
+
(no-shell path) an extra `*.staged` file not named in
|
|
171
|
+
`stagedBaseline.files`, a pack's `files` or a pack's manifest
|
|
172
|
+
means stop -- all of Round 1, including conflicts and packs -- and
|
|
173
|
+
change nothing.** This is the single stop list for the staged baseline and the staged packs.
|
|
174
|
+
Tell the user: "The staged baseline in `.groundwork/baseline/` or a staged pack in `.groundwork/packs/` is incomplete or does not match `.groundwork/inventory.json` (<the first entry that failed and why>). Re-run `npx @monte3l/groundwork@rc .` and then run `/customize` again." **Never fall back to `inventory.templateRoot` for a schema 5 inventory, packs included:** that fallback exists for a schema 1-4 inventory only, and only for the baseline additions (a schema 1-4 inventory's packs are read from their unsuffixed copy, see Step 0.4(c) and Round 1).
|
|
175
|
+
- What a passing check proves: the staging is complete and matches the
|
|
176
|
+
inventory. It does **not** prove the files are untampered -- anyone who
|
|
177
|
+
can edit the staged files can edit the inventory's hashes too.
|
|
178
|
+
|
|
179
|
+
An inventory with `schemaVersion` below 5 has no staged copy; its
|
|
180
|
+
approved additions are read from `inventory.templateRoot` (see Round 1).
|
|
181
|
+
|
|
182
|
+
2. **The deep read.** The CLI's survey is an index, not an interpretation —
|
|
183
|
+
it flagged what it found but could not parse (`needsReading: true` on
|
|
184
|
+
git-hook config, workflow files; anything in `survey.undetermined`) and
|
|
185
|
+
what it could only index, not summarize (`docs`). Read all of it for
|
|
186
|
+
real: the eslint config, the git-hook manager's actual stage commands,
|
|
187
|
+
the CI workflow job steps, `CLAUDE.md`, `CONTRIBUTING.md`, and any
|
|
188
|
+
docs/ADR files the survey indexed. Dispatch this as parallel read-only
|
|
189
|
+
`Explore` agents, one per discovery area (shape/toolchain, harness, docs),
|
|
190
|
+
so you aggregate their findings rather than reading everything yourself.
|
|
191
|
+
The harness agent also starts from `inventory.harnessGrade` (the report's
|
|
192
|
+
`## Harness grade` section): a deterministic, offline check of the
|
|
193
|
+
existing `.claude/` wiring. Its **wiring findings** (a hook registration
|
|
194
|
+
naming a missing file, a skill or agent with unreadable frontmatter, a
|
|
195
|
+
`CLAUDE.md` path that no longer exists) are facts to verify against the
|
|
196
|
+
real files, not verdicts to take on trust. Its **quality findings** are
|
|
197
|
+
advisory. `inventory.harnessConformance` counts how far the harness has
|
|
198
|
+
drifted from the baseline's — information only, since divergence from the
|
|
199
|
+
baseline is the point of adopting. An inventory with `schemaVersion` below
|
|
200
|
+
3 carries neither field; skip this and continue.
|
|
201
|
+
|
|
202
|
+
The toolchain agent likewise starts from `inventory.toolchainGrade` (the
|
|
203
|
+
report's `## Toolchain grade` section): a deterministic, offline check of
|
|
204
|
+
the tsconfig chain, ESLint and vitest config, verify-step wiring, and
|
|
205
|
+
toolchain pins. It reads files and never runs them, and it reads
|
|
206
|
+
`eslint.config.js`/`vitest.config.ts` by pattern rather than by evaluating
|
|
207
|
+
them -- so a **wiring finding** (a build project that emits nowhere, a
|
|
208
|
+
verify step naming a script or file that does not exist, a `.node-version`
|
|
209
|
+
that contradicts `engines.node`) is a fact to verify against the real files,
|
|
210
|
+
and a **quality finding** (a missing strict flag, an option TypeScript has
|
|
211
|
+
deprecated, ESLint without type-aware linting, a coverage gate that is not
|
|
212
|
+
per-file) is advisory. Absence is never a finding: a project with no vitest
|
|
213
|
+
config simply has no coverage-gate line. `inventory.toolchainConformance`
|
|
214
|
+
counts drift from the baseline's toolchain files -- information only. An
|
|
215
|
+
inventory with `schemaVersion` below 4 carries neither field; skip this and
|
|
216
|
+
continue.
|
|
217
|
+
|
|
218
|
+
3. **Write the findings back** into `.groundwork/adoption-report.md`,
|
|
219
|
+
replacing the CLI's index-level sections ("a `lefthook.yml` exists")
|
|
220
|
+
with semantic ones ("pre-push runs lint and typecheck; tests do not
|
|
221
|
+
gate"). If the no-shell bullet in step 1 applied, carry its skip note
|
|
222
|
+
into the rewritten report: this step replaces the report's sections, so
|
|
223
|
+
a note written earlier would be dropped.
|
|
224
|
+
4. **Confirm.** Give a short summary in chat, then ask **one**
|
|
225
|
+
`AskUserQuestion` covering: (a) _did this miss anything about your
|
|
226
|
+
project?_ — the free-text option is the point of this question, not a
|
|
227
|
+
formality — (b) the conflict resolutions from the inventory's conflict
|
|
228
|
+
table, batched by facet (toolchain config, harness) rather than one
|
|
229
|
+
question per file (the harness facet's batch also carries any wiring
|
|
230
|
+
findings you confirmed in the deep read, offered as fixes to make, and the
|
|
231
|
+
toolchain facet's batch does the same for confirmed toolchain wiring findings) — and
|
|
232
|
+
(c) **which pack(s) to install**, from `inventory.packs`. For each pack, show its `budget`, its
|
|
233
|
+
`wiringObservations` (facts about how it would land — e.g. "no
|
|
234
|
+
`bin/lib/verify-steps.packs.json` found: no `bin/verify.mjs`-shaped gate
|
|
235
|
+
runner detected", or "`.claude/settings.json` already sets a top-level
|
|
236
|
+
`statusLine`"), and its `adoptNotes` verbatim; a pack whose gate
|
|
237
|
+
dependency the project doesn't have is still offered for its other
|
|
238
|
+
artifacts, with that limitation stated plainly rather than silently
|
|
239
|
+
dropped. This is index-level evidence from the CLI, not a kind-based
|
|
240
|
+
judgment — see Step 3's note on revisiting it once the interview confirms
|
|
241
|
+
the project's kind.
|
|
242
|
+
|
|
243
|
+
The same question also carries (d) if the no-shell bullet applied: say the
|
|
244
|
+
SHA-256 check was skipped, what a passing check would have proven,
|
|
245
|
+
which checks ran instead and any undetermined result, and
|
|
246
|
+
ask whether to continue or stop.
|
|
247
|
+
|
|
248
|
+
Before offering any pack, check each staged `.groundwork/packs/<name>/pack.json.staged` (the bytes Step 0.1 read, hash-checked when a command could run; not a fresh read):
|
|
249
|
+
if `__proto__`, `constructor` or `prototype` is a key of its `wiring.settings`
|
|
250
|
+
(hook event names), `wiring.settingsTopLevel` or `wiring.packageScripts`
|
|
251
|
+
(script names), stop. The project tree is untrusted, and the CLI refuses such a
|
|
252
|
+
pack before staging it, so a staged manifest carrying one was edited after
|
|
253
|
+
staging. Name the pack, the field and the key, tell the user to delete
|
|
254
|
+
`.groundwork/` and re-run the CLI, and offer nothing from this run. Change nothing.
|
|
255
|
+
For a schema 1-4 inventory (the layout of the CLI releases before the `.staged` convention) there is no `.staged` copy and no hash: read the unsuffixed `.groundwork/packs/<name>/pack.json` (not hash-verified) and run this same prototype-key check on it.
|
|
256
|
+
|
|
257
|
+
5. **Record the confirmed decisions** to `.groundwork/adoption-decisions.json`
|
|
258
|
+
so a compacted or resumed session doesn't silently lose them and re-ask.
|
|
259
|
+
A CLI re-run deletes this file along with the old inventory, because its
|
|
260
|
+
decisions were made against the previous staging; a decisions file found
|
|
261
|
+
beside a fresh inventory therefore belongs to this inventory.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Step 3 — Round 1: deterministic tailoring: detail
|
|
2
|
+
|
|
3
|
+
> Full text of Step 3 from `SKILL.md`. Read this whole file before applying Round 1's edits, and re-read it after a compaction: a compacted session keeps only the start of `SKILL.md`.
|
|
4
|
+
|
|
5
|
+
## Step 3 — Round 1: deterministic tailoring
|
|
6
|
+
|
|
7
|
+
**Fresh bootstrap** applies directly, no research needed:
|
|
8
|
+
|
|
9
|
+
- **Project kind ≠ library**: if `check:exports` (publint/attw) doesn't
|
|
10
|
+
apply to the chosen kind (CLI, frontend, service), remove the
|
|
11
|
+
`check:exports` step from `bin/lib/verify-steps.mjs` and the
|
|
12
|
+
corresponding `.github/workflows/ci.yml` line, and drop the `exports`
|
|
13
|
+
field from `package.json` in favor of a `bin` field (CLI) or leave `main`/
|
|
14
|
+
no public export map at all (service).
|
|
15
|
+
- **Runtime target = browser or both**: note that `tsconfig.base.json`'s
|
|
16
|
+
`lib` and `moduleResolution` will very likely need to change — but leave
|
|
17
|
+
the actual edit to Round 2's `typescript-guidance` sweep, which has full
|
|
18
|
+
authority over that file and access to current bundler-resolution
|
|
19
|
+
guidance you don't have without a live source.
|
|
20
|
+
- **Tests mandatory = warn only**: change the `test` lane in `lefthook.yml`
|
|
21
|
+
and `ci.yml` from a hard failure to a non-blocking report.
|
|
22
|
+
- **CI depth = minimal**: drop the `test` lane's coverage gate from CI
|
|
23
|
+
(still run locally); minimal keeps only format/lint/typecheck/build.
|
|
24
|
+
**CI depth = thorough**: note this for Round 2 — `harness-guidance` may
|
|
25
|
+
recommend additional current-best-practice lanes (e.g. a scheduled
|
|
26
|
+
dependency audit) beyond what the baseline ships.
|
|
27
|
+
- **Agents not kept**: delete their `.claude/agents/<name>.md` file. Never
|
|
28
|
+
delete `Explore`, `test-author`, or `code-implementer` even if unselected
|
|
29
|
+
— they're load-bearing for the hub-and-spoke loop `CLAUDE.md` documents.
|
|
30
|
+
- **Packs**: nothing to do here. A fresh bootstrap's packs were installed
|
|
31
|
+
(or not) by the CLI at `m3l-groundwork <dir> --pack <name>` invocation
|
|
32
|
+
time, before this skill ever ran — there is no fresh-mode install path in
|
|
33
|
+
`/customize` itself. To add a pack after the fact, re-run the CLI against
|
|
34
|
+
this now-non-empty directory (it auto-detects adopt mode) and run
|
|
35
|
+
`/customize` again; its Step 0 will offer the pack through the adopt path
|
|
36
|
+
below.
|
|
37
|
+
|
|
38
|
+
**Adopt mode** re-expresses each of the same five outcomes against whatever
|
|
39
|
+
the project actually has, instead of a named baseline path — "tests must not
|
|
40
|
+
hard-fail `pre-push`" is applied to _the gate the inventory found_ (jest in
|
|
41
|
+
CI, husky locally, whatever it is), not to `lefthook.yml`/`ci.yml` by name.
|
|
42
|
+
Concretely, adopt-mode Round 1 applies exactly three things, all already
|
|
43
|
+
confirmed in Step 0.4:
|
|
44
|
+
|
|
45
|
+
- The **approved additions** — files `templates/core` would add that the
|
|
46
|
+
project doesn't have and the user approved adding. For a schema 5 inventory, install them from the staged copy Step 0.1 already verified,
|
|
47
|
+
using the bytes you read then (Step 0.1 also already checked that the
|
|
48
|
+
staged files match the `absent` conflicts). Write them to the project at `path` (the
|
|
49
|
+
`.staged` suffix stripped, never to the staged name), filling in the
|
|
50
|
+
`__KEY__` tokens with the project's real values as you copy, same as
|
|
51
|
+
staged packs. Only a **schema 1-4** inventory has no staged copy and reads
|
|
52
|
+
additions from `inventory.templateRoot`; if that path no longer exists (a
|
|
53
|
+
pruned `npx` cache, a deleted temp checkout), say so and ask for a CLI
|
|
54
|
+
re-run rather than guessing at the baseline's contents.
|
|
55
|
+
- The **approved conflict resolutions** — for each divergent file the user
|
|
56
|
+
decided on, apply that decision (keep theirs / take groundwork's / merge
|
|
57
|
+
the named keys).
|
|
58
|
+
- The **approved packs** — installed from `.groundwork/packs/<name>/` (the
|
|
59
|
+
CLI's staged, self-contained copy of inert `.staged` files — never
|
|
60
|
+
`inventory.templateRoot`, which may not exist by the time this runs). Before installing, call
|
|
61
|
+
`recommendPacks(answers)` from `pack-map.ts` (alongside this file, same
|
|
62
|
+
copy mechanism as `kind-facet-map.ts`) with the now-confirmed
|
|
63
|
+
`InterviewAnswers` and compare its verdict against Step 0.4's decision.
|
|
64
|
+
`harness-extras`, `github`, `supply-chain`, `quality`, and `worktrees`
|
|
65
|
+
never disagree (none of the five's recommendation varies by kind or
|
|
66
|
+
answer -- `worktrees` is always `recommended: false`, since it changes the
|
|
67
|
+
day-to-day workflow rather than adding a nicety, and the other four are
|
|
68
|
+
always `recommended: true`), but `publishing`'s does (recommended for
|
|
69
|
+
`library`/`cli`, not for `frontend`/`service`) — if the comparison surfaces a real
|
|
70
|
+
conflict there or for any future kind-scoped pack, raise it rather than
|
|
71
|
+
silently overriding the user's Step 0.4 answer, mirroring Step 4's "the
|
|
72
|
+
one exception" rule for guidance findings. Before installing, check the
|
|
73
|
+
pack's own `modes`: a pack whose `modes` doesn't include `"adopt"` (today,
|
|
74
|
+
`publishing` — its release flow encodes decisions too project-specific to
|
|
75
|
+
apply blind) is never auto-installed here even if staged and approved;
|
|
76
|
+
instead, state in Step 6 that it needs a manual install (point at `.groundwork/packs/<name>/` and the pack's own `adoptNotes`, and say to strip the `.staged` suffix from every name when copying by hand and to replace every `__KEY__` token with the project's real value) and stop
|
|
77
|
+
there for that pack. Before merging any staged `pack.json.staged` wiring, repeat
|
|
78
|
+
the prototype-key check from Step 0.4(c): if a key of `wiring.settings`,
|
|
79
|
+
`wiring.settingsTopLevel` or `wiring.packageScripts` is `__proto__`,
|
|
80
|
+
`constructor` or `prototype`, stop the same way and change nothing. For an
|
|
81
|
+
adopt-capable pack, install each file from
|
|
82
|
+
`.groundwork/packs/<name>/files/<path>.staged`, using the bytes Step 0.1 read (hash-checked when a command could run), and write it to the project at `path` (the `.staged` suffix
|
|
83
|
+
stripped, never to the staged name), filling in the `__KEY__` tokens with
|
|
84
|
+
the project's real values as you copy (respecting any approved per-file
|
|
85
|
+
conflict decision the same way the baseline's own additions are applied).
|
|
86
|
+
For a schema 1-4 inventory, install each file from the unsuffixed
|
|
87
|
+
`.groundwork/packs/<name>/files/<path>` instead (no `.staged` suffix to strip,
|
|
88
|
+
not hash-verified; token substitution still applies).
|
|
89
|
+
Then, for either schema, translate `pack.json`'s `wiring` by hand, reading it
|
|
90
|
+
from `pack.json.staged` (the bytes Step 0.1 read) for a schema 5 inventory and
|
|
91
|
+
from the unsuffixed `pack.json` for schema 1-4,
|
|
92
|
+
against what Step 0.2's deep read already found — a `.claude/settings.json`
|
|
93
|
+
hook fragment merges the same way the baseline's own hook entries would;
|
|
94
|
+
`wiring.settingsTopLevel` is a set of top-level keys (e.g. `statusLine`)
|
|
95
|
+
planted whole, and only when the project doesn't already define that key —
|
|
96
|
+
when it does, show the existing value and ask, since the CLI's own
|
|
97
|
+
`mergeSettingsTopLevel` treats a differing value as a hard error and this
|
|
98
|
+
hand-applied path must not be laxer than the automated one (also check
|
|
99
|
+
`.claude/settings.local.json` and the user's `~/.claude/settings.json`,
|
|
100
|
+
either of which can shadow a project `statusLine`). For `harness-extras`
|
|
101
|
+
specifically, a `statusLine`/`subagentStatusLine` collision is never a
|
|
102
|
+
reason to fail the whole pack install: skip just those two settings keys
|
|
103
|
+
and the three statusline scripts (`statusline.mjs`, `statusline-layout.mjs`,
|
|
104
|
+
`subagent-statusline.mjs`) and install the pack's other artifacts (the
|
|
105
|
+
compaction-handoff hooks and `guard-readonly-bash`) normally, stating the
|
|
106
|
+
skip plainly in Step 6;
|
|
107
|
+
`wiring.verifySteps` becomes a step in whatever this project's real gate
|
|
108
|
+
runner is (a `package.json` script plus a line in its `lefthook.yml`/
|
|
109
|
+
`.husky/pre-push`/CI workflow, written by hand to match its actual shape)
|
|
110
|
+
— or, if the project has no such gate runner at all, install the pack's
|
|
111
|
+
other artifacts and state plainly in Step 6 that the gate was not wired,
|
|
112
|
+
rather than inventing a runner the project never asked for.
|
|
113
|
+
|
|
114
|
+
**Plugins (both modes), after packs are settled above.** Build a
|
|
115
|
+
`PluginRecommendationContext`: `chosenPacks` is whatever the packs decision
|
|
116
|
+
just above actually landed on (fresh: what `--pack` installed at bootstrap
|
|
117
|
+
time, detectable from the installed files -- e.g. `.changeset/config.json`
|
|
118
|
+
means `publishing`, `.github/workflows/claude-pr-review.yml` means `github`
|
|
119
|
+
(the only marker `plugin-map.ts` actually reads; `harness-extras` has no
|
|
120
|
+
plugin that varies by its presence, so it needs no marker here); adopt: the
|
|
121
|
+
packs Step 0.4(c) confirmed, revised by this step's own `recommendPacks`
|
|
122
|
+
comparison if it changed anything); `hasCustomSkills` is `false` for a fresh
|
|
123
|
+
bootstrap (nothing has authored a skill yet) and, for adopt mode, whatever
|
|
124
|
+
Step 0's survey found beyond the baseline's own known skill names and any
|
|
125
|
+
already-installed pack's skills (e.g. the `github` pack's three);
|
|
126
|
+
`dependencies` is the key names under `dependencies` and `devDependencies`
|
|
127
|
+
in the project's root `package.json` (the same read in both modes; a fresh
|
|
128
|
+
bootstrap carries only the baseline's own devDependencies, so no SDK plugin
|
|
129
|
+
is pre-selected there, and a monorepo whose SDK dependency sits only in a
|
|
130
|
+
workspace package is not pre-selected either -- the user can still pick it). Call
|
|
131
|
+
`recommendPlugins(answers, context)` from `plugin-map.ts`
|
|
132
|
+
(alongside this file, same copy mechanism as `pack-map.ts`) with the
|
|
133
|
+
now-confirmed `InterviewAnswers`, and offer all ten in **one**
|
|
134
|
+
`AskUserQuestion` call holding three multi-select questions (a question
|
|
135
|
+
takes at most four options, so split the list in its fixed order: entries
|
|
136
|
+
1-4, 5-7, 8-10), each option pre-selected per its entry's `recommended`
|
|
137
|
+
boolean with its `because` shown as the evidence -- same
|
|
138
|
+
"visible reasoning" principle as every other inference in this skill.
|
|
139
|
+
|
|
140
|
+
Write every confirmed `true` entry into `.claude/settings.json`'s
|
|
141
|
+
`enabledPlugins` (`{"<id>": true}` per entry, e.g.
|
|
142
|
+
`"context7@claude-plugins-official": true`) -- additive and entry-by-entry,
|
|
143
|
+
same discipline as the CLI's own `mergeSettingsHooks` (which owns the
|
|
144
|
+
`hooks` block entry-by-entry, as opposed to `mergeSettingsTopLevel`'s
|
|
145
|
+
whole-key-at-a-time semantics): never remove or flip an entry the project
|
|
146
|
+
already sets explicitly (an existing `false` is a decision the user made,
|
|
147
|
+
not an oversight to correct), and never touch `.claude/settings.local.json`
|
|
148
|
+
or the user's own `~/.claude/settings.json` scope. `claude-plugins-official`
|
|
149
|
+
is a built-in marketplace, so no `extraKnownMarketplaces` entry is needed
|
|
150
|
+
for any of the ten.
|
|
151
|
+
|
|
152
|
+
**Committing this entry does not install the plugin for anyone.** A
|
|
153
|
+
project-scope `enabledPlugins: true` with no local install produces no
|
|
154
|
+
folder-trust auto-prompt -- Claude Code's `/plugin` Errors tab instead shows
|
|
155
|
+
"enabled in project settings but isn't installed here" until someone runs
|
|
156
|
+
the install by hand. So for every newly-`true` entry, print the exact
|
|
157
|
+
follow-up command in Step 6's report: `claude plugin install <id> --scope
|
|
158
|
+
project` (or `/plugin install <id>` inside a running session) -- the user,
|
|
159
|
+
and every collaborator who pulls this change, still has to run it once. Do
|
|
160
|
+
the same for each recommendation's `prerequisites` (the
|
|
161
|
+
`typescript-language-server` binary, or the Python version a plugin names) --
|
|
162
|
+
print them as
|
|
163
|
+
follow-ups, never attempt to install them.
|
|
164
|
+
|
|
165
|
+
Nothing else is touched. A project file the user didn't approve a change to
|
|
166
|
+
stays exactly as it was.
|
|
167
|
+
|
|
168
|
+
Run `pnpm verify` (fresh) or the project's own equivalent (adopt) after
|
|
169
|
+
Round 1's edits to confirm the tailored result still passes before moving to
|
|
170
|
+
Round 2.
|
package/plugin/src/plugin-map.ts
CHANGED
|
@@ -9,6 +9,29 @@
|
|
|
9
9
|
* `pack-map.ts` applies to `templates/packs/` bundles. A stored, unit-tested
|
|
10
10
|
* module rather than a judgment made afresh each run, so the same interview
|
|
11
11
|
* answers and context always produce the same recommendation.
|
|
12
|
+
*
|
|
13
|
+
* Evaluated and deliberately not offered, recorded so they are not proposed
|
|
14
|
+
* again:
|
|
15
|
+
*
|
|
16
|
+
* - `plugin-dev` -- only for projects that author Claude Code plugins;
|
|
17
|
+
* m3l-groundwork itself enables it, a bootstrapped TypeScript project has
|
|
18
|
+
* no plugin to build, and `skill-creator` covers skill authoring.
|
|
19
|
+
* - `code-review` -- duplicates the built-in `/code-review` and the optional
|
|
20
|
+
* `github` pack's PR-review Action.
|
|
21
|
+
* - `code-simplifier` -- duplicates the built-in `/simplify`; its
|
|
22
|
+
* unrestricted-tools agent is blocked by the baseline's hub-and-spoke
|
|
23
|
+
* write guard on `src/`/`tests/` anyway.
|
|
24
|
+
* - `feature-dev` -- duplicates plan mode, `starting-work` and the TDD loop;
|
|
25
|
+
* its `code-reviewer` competes with the baseline's.
|
|
26
|
+
* - `pr-review-toolkit` -- overlaps the baseline's `code-reviewer` and
|
|
27
|
+
* `silent-failure-hunter` and the optional `quality` pack's
|
|
28
|
+
* `type-design-analyzer`.
|
|
29
|
+
* - `hookify` -- runs `python3` on every tool call, and keeps its rules in
|
|
30
|
+
* gitignored `.local.md` files that bypass the graded, committed hooks and
|
|
31
|
+
* the hook cap.
|
|
32
|
+
* - `remember` -- third-party under a source-available license that forbids
|
|
33
|
+
* modification and redistribution; it sends transcripts to `claude -p`,
|
|
34
|
+
* writes `.remember/`, and can push memory to a remote.
|
|
12
35
|
*/
|
|
13
36
|
import type { InterviewAnswers } from "./kind-facet-map.js";
|
|
14
37
|
|
|
@@ -29,12 +52,18 @@ export interface PluginRecommendation {
|
|
|
29
52
|
|
|
30
53
|
/**
|
|
31
54
|
* What `/customize` knows beyond the interview answers by the time it
|
|
32
|
-
* recommends plugins: which packs the user chose,
|
|
33
|
-
* authors its own skills
|
|
55
|
+
* recommends plugins: which packs the user chose, whether the project
|
|
56
|
+
* authors its own skills, and which packages it depends on. All are
|
|
57
|
+
* optional; an absent field reads as "no" (or "none").
|
|
34
58
|
*/
|
|
35
59
|
export interface PluginRecommendationContext {
|
|
36
60
|
readonly chosenPacks?: readonly string[];
|
|
37
61
|
readonly hasCustomSkills?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Package names from the project's `package.json` `dependencies` and
|
|
64
|
+
* `devDependencies` combined. Absent reads as none.
|
|
65
|
+
*/
|
|
66
|
+
readonly dependencies?: readonly string[];
|
|
38
67
|
}
|
|
39
68
|
|
|
40
69
|
function pluginId(name: string): string {
|
|
@@ -163,10 +192,80 @@ function recommendSecurityGuidance(
|
|
|
163
192
|
};
|
|
164
193
|
}
|
|
165
194
|
|
|
195
|
+
/**
|
|
196
|
+
* `claude-security` is the on-demand counterpart to `security-guidance`: it
|
|
197
|
+
* runs only when invoked, so unlike that plugin's per-turn Stop-hook LLM
|
|
198
|
+
* review it costs nothing while idle, and is pre-selected for every project.
|
|
199
|
+
*/
|
|
200
|
+
function recommendClaudeSecurity(): PluginRecommendation {
|
|
201
|
+
return {
|
|
202
|
+
id: pluginId("claude-security"),
|
|
203
|
+
recommended: true,
|
|
204
|
+
because:
|
|
205
|
+
"it runs on demand -- only when invoked -- so it costs nothing while " +
|
|
206
|
+
"idle, unlike security-guidance's Stop-hook LLM review on every " +
|
|
207
|
+
"turn. When run it scans the repo or a diff, challenges each finding " +
|
|
208
|
+
"before reporting it, and can draft patches. Its post-push scan tip " +
|
|
209
|
+
"can be turned off with CLAUDE_SECURITY_SCAN_TIP=off.",
|
|
210
|
+
prerequisites: ["Python 3.9+ on PATH"],
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function dependsOn(
|
|
215
|
+
context: PluginRecommendationContext | undefined,
|
|
216
|
+
packageName: string,
|
|
217
|
+
): boolean {
|
|
218
|
+
return context?.dependencies?.includes(packageName) ?? false;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* `agent-sdk-dev` scaffolds and verifies Claude Agent SDK applications, so it
|
|
223
|
+
* is pre-selected only when the project depends on
|
|
224
|
+
* `@anthropic-ai/claude-agent-sdk`.
|
|
225
|
+
*/
|
|
226
|
+
function recommendAgentSdkDev(
|
|
227
|
+
context: PluginRecommendationContext | undefined,
|
|
228
|
+
): PluginRecommendation {
|
|
229
|
+
const recommended = dependsOn(context, "@anthropic-ai/claude-agent-sdk");
|
|
230
|
+
return {
|
|
231
|
+
id: pluginId("agent-sdk-dev"),
|
|
232
|
+
recommended,
|
|
233
|
+
because: recommended
|
|
234
|
+
? "the project depends on @anthropic-ai/claude-agent-sdk, and this " +
|
|
235
|
+
"plugin scaffolds new SDK apps (/new-sdk-app) and verifies existing " +
|
|
236
|
+
"ones against the SDK's current guidance."
|
|
237
|
+
: "only useful to a project that builds on the Claude Agent SDK; " +
|
|
238
|
+
"@anthropic-ai/claude-agent-sdk is not among this project's " +
|
|
239
|
+
"dependencies.",
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* `mcp-server-dev` carries skills for building MCP servers and MCP apps and
|
|
245
|
+
* for MCPB packaging, so it is pre-selected only when the project depends on
|
|
246
|
+
* `@modelcontextprotocol/sdk`.
|
|
247
|
+
*/
|
|
248
|
+
function recommendMcpServerDev(
|
|
249
|
+
context: PluginRecommendationContext | undefined,
|
|
250
|
+
): PluginRecommendation {
|
|
251
|
+
const recommended = dependsOn(context, "@modelcontextprotocol/sdk");
|
|
252
|
+
return {
|
|
253
|
+
id: pluginId("mcp-server-dev"),
|
|
254
|
+
recommended,
|
|
255
|
+
because: recommended
|
|
256
|
+
? "the project depends on @modelcontextprotocol/sdk, and this plugin " +
|
|
257
|
+
"brings skills for building MCP servers and MCP apps and for " +
|
|
258
|
+
"packaging them as MCPB bundles."
|
|
259
|
+
: "only useful to a project that builds on the MCP SDK; " +
|
|
260
|
+
"@modelcontextprotocol/sdk is not among this project's " +
|
|
261
|
+
"dependencies.",
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
166
265
|
/**
|
|
167
266
|
* `skill-creator` scaffolds, evaluates and iterates on skills. The baseline's
|
|
168
|
-
* own skills are maintained by
|
|
169
|
-
* pays for itself in a project that writes skills of its own.
|
|
267
|
+
* own skills are maintained upstream by m3l-groundwork, not by the project,
|
|
268
|
+
* so it only pays for itself in a project that writes skills of its own.
|
|
170
269
|
*/
|
|
171
270
|
function recommendSkillCreator(
|
|
172
271
|
context: PluginRecommendationContext | undefined,
|
|
@@ -213,12 +312,31 @@ function recommendClaudeCodeSetup(): PluginRecommendation {
|
|
|
213
312
|
|
|
214
313
|
/**
|
|
215
314
|
* Every built-in marketplace plugin's recommendation for the given interview
|
|
216
|
-
* answers and context, always the same
|
|
217
|
-
* `typescript-lsp
|
|
218
|
-
*
|
|
219
|
-
* `
|
|
220
|
-
* `
|
|
221
|
-
*
|
|
315
|
+
* answers and context, always the same ten in the same order. Pre-selected
|
|
316
|
+
* unconditionally: `context7`, `typescript-lsp`, `claude-md-management` and
|
|
317
|
+
* `claude-security`. Conditionally: `github` when the `github` pack was
|
|
318
|
+
* chosen; `security-guidance` for a `service` or a `thorough` CI depth;
|
|
319
|
+
* `skill-creator` when the project has its own skills; `agent-sdk-dev` and
|
|
320
|
+
* `mcp-server-dev` when the project depends on the Claude Agent SDK or the
|
|
321
|
+
* MCP SDK respectively. Never: `claude-code-setup`. A pure function --
|
|
322
|
+
* identical inputs always produce an equal result.
|
|
323
|
+
*
|
|
324
|
+
* @example
|
|
325
|
+
* ```ts
|
|
326
|
+
* import { recommendPlugins } from "./plugin-map.js";
|
|
327
|
+
*
|
|
328
|
+
* const recommendations = recommendPlugins(
|
|
329
|
+
* {
|
|
330
|
+
* kind: "service",
|
|
331
|
+
* runtime: "node",
|
|
332
|
+
* testsMandatory: true,
|
|
333
|
+
* ciDepth: "standard",
|
|
334
|
+
* keepAgents: ["code-reviewer"],
|
|
335
|
+
* },
|
|
336
|
+
* { chosenPacks: ["github"], dependencies: ["@modelcontextprotocol/sdk"] },
|
|
337
|
+
* );
|
|
338
|
+
* const preselected = recommendations.filter((r) => r.recommended);
|
|
339
|
+
* ```
|
|
222
340
|
*/
|
|
223
341
|
export function recommendPlugins(
|
|
224
342
|
answers: InterviewAnswers,
|
|
@@ -230,7 +348,10 @@ export function recommendPlugins(
|
|
|
230
348
|
recommendClaudeMdManagement(),
|
|
231
349
|
recommendGithub(context),
|
|
232
350
|
recommendSecurityGuidance(answers),
|
|
351
|
+
recommendClaudeSecurity(),
|
|
233
352
|
recommendSkillCreator(context),
|
|
234
353
|
recommendClaudeCodeSetup(),
|
|
354
|
+
recommendAgentSdkDev(context),
|
|
355
|
+
recommendMcpServerDev(context),
|
|
235
356
|
];
|
|
236
357
|
}
|
|
@@ -3,7 +3,7 @@ name: Explore
|
|
|
3
3
|
description: Fast read-only search agent for locating and understanding code. Use it to find files by pattern (e.g. "src/components/**/*.tsx"), grep for symbols or keywords (e.g. "API endpoints"), answer "where is X defined / which files reference Y," or read a bounded set of files in full when the caller says so. Do NOT use it for code review, design-doc auditing, or open-ended cross-file consistency judgment across the whole repo — those need a specialized reviewer. When calling, specify search breadth ("quick" for a single targeted lookup, "medium" for moderate exploration, "very thorough" to search across multiple locations and naming conventions) and say explicitly if the task requires reading matched files in full rather than excerpting them.
|
|
4
4
|
tools: Read, Grep, Glob, Bash, WebSearch, WebFetch
|
|
5
5
|
disallowedTools: Agent
|
|
6
|
-
model: claude-haiku-
|
|
6
|
+
model: claude-haiku-5-5
|
|
7
7
|
maxTurns: 40
|
|
8
8
|
color: cyan
|
|
9
9
|
---
|
|
@@ -97,8 +97,9 @@ if (isEntryPoint()) {
|
|
|
97
97
|
process.stderr.write(
|
|
98
98
|
`Blocked: CommonJS construct(s) found (this project is ESM only):\n` +
|
|
99
99
|
hits.map((h) => ` - ${h}`).join("\n") +
|
|
100
|
-
`\nUse ESM equivalents: import/export, import.meta.
|
|
101
|
-
`
|
|
100
|
+
`\nUse ESM equivalents: import/export, import.meta.dirname and ` +
|
|
101
|
+
`import.meta.filename (stable on Node 24+) in place of __dirname and ` +
|
|
102
|
+
`__filename, or fileURLToPath(import.meta.url).\n`,
|
|
102
103
|
);
|
|
103
104
|
process.exit(2);
|
|
104
105
|
}
|