@codewithjuber/forgekit 0.8.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/.claude-plugin/marketplace.json +12 -0
- package/.claude-plugin/plugin.json +20 -0
- package/.codex-plugin/plugin.json +29 -0
- package/.mcp.json +8 -0
- package/ARCHITECTURE.md +314 -0
- package/CHANGELOG.md +467 -0
- package/LICENSE +21 -0
- package/ONBOARDING.md +180 -0
- package/README.md +286 -0
- package/bin/claude-init.sh +90 -0
- package/bin/claude-taste.sh +33 -0
- package/bin/learn-consolidate.sh +51 -0
- package/brand.json +15 -0
- package/global/CLAUDE.md +31 -0
- package/global/crew/frontend-verifier.md +36 -0
- package/global/crew/independent-reviewer.md +29 -0
- package/global/crew/scout.md +24 -0
- package/global/crew/verifier.md +28 -0
- package/global/guards/_guardlib.sh +36 -0
- package/global/guards/cortex.sh +14 -0
- package/global/guards/cost-budget.sh +41 -0
- package/global/guards/doom-loop.sh +25 -0
- package/global/guards/format-on-edit.sh +32 -0
- package/global/guards/lean-guard.sh +20 -0
- package/global/guards/protect-paths.sh +45 -0
- package/global/guards/recall-load.sh +22 -0
- package/global/guards/secret-redact.sh +18 -0
- package/global/guards/session-learner.sh +72 -0
- package/global/recall/MEMORY.md +7 -0
- package/global/rules/self-correction.md +17 -0
- package/global/rules/stack-notes.md +24 -0
- package/global/rules/tech-currency.md +19 -0
- package/global/settings.template.json +183 -0
- package/global/statusline.sh +51 -0
- package/global/taste/brutalist.json +9 -0
- package/global/taste/brutalist.md +19 -0
- package/global/taste/corporate.json +9 -0
- package/global/taste/corporate.md +19 -0
- package/global/taste/editorial.json +9 -0
- package/global/taste/editorial.md +19 -0
- package/global/taste/minimalist.json +9 -0
- package/global/taste/minimalist.md +20 -0
- package/global/taste/playful.json +9 -0
- package/global/taste/playful.md +19 -0
- package/global/tools/atlas/SKILL.md +27 -0
- package/global/tools/code-modernization/SKILL.md +275 -0
- package/global/tools/code-modernization/references/cost-impact-preflight.md +54 -0
- package/global/tools/code-modernization/references/design-patterns-cheatsheet.md +24 -0
- package/global/tools/code-modernization/references/research-protocol.md +42 -0
- package/global/tools/code-modernization/scripts/preflight_scan.py +190 -0
- package/global/tools/cognitive-substrate/SKILL.md +56 -0
- package/global/tools/cognitive-substrate/references/capability-map.md +17 -0
- package/global/tools/cost-guard/SKILL.md +50 -0
- package/global/tools/design-md/SKILL.md +54 -0
- package/global/tools/dev-radar/SKILL.md +56 -0
- package/global/tools/explore-plan-code/SKILL.md +24 -0
- package/global/tools/lean/SKILL.md +41 -0
- package/global/tools/recall/SKILL.md +31 -0
- package/global/tools/reuse-first/SKILL.md +64 -0
- package/global/tools/self-improve/SKILL.md +44 -0
- package/global/tools/taste/SKILL.md +26 -0
- package/global/tools/tech-selector/SKILL.md +35 -0
- package/global/tools/ui-workflow/SKILL.md +44 -0
- package/hooks/hooks.json +107 -0
- package/install.sh +88 -0
- package/package.json +93 -0
- package/public/index.html +45 -0
- package/scripts/build-pages.mjs +180 -0
- package/scripts/bump.mjs +322 -0
- package/skills/cognitive-substrate/SKILL.md +56 -0
- package/skills/cognitive-substrate/references/capability-map.md +17 -0
- package/source/mcp.json +10 -0
- package/source/rules.json +106 -0
- package/source/substrate.json +41 -0
- package/src/adjudicate.js +84 -0
- package/src/anchor.js +210 -0
- package/src/atlas.js +487 -0
- package/src/brain.js +84 -0
- package/src/brand.js +25 -0
- package/src/cli.js +1509 -0
- package/src/context.js +273 -0
- package/src/cortex.js +251 -0
- package/src/cortex_distill.js +55 -0
- package/src/cortex_features.js +81 -0
- package/src/cortex_hook.js +197 -0
- package/src/cortex_hook_main.js +139 -0
- package/src/cortex_mcp.js +352 -0
- package/src/cost_report.js +271 -0
- package/src/dash.html +396 -0
- package/src/dash.js +220 -0
- package/src/diagnose.js +0 -0
- package/src/doctor.js +315 -0
- package/src/embed.js +244 -0
- package/src/emit/_shared.js +39 -0
- package/src/emit/aider.js +22 -0
- package/src/emit/claude.js +44 -0
- package/src/emit/codex.js +17 -0
- package/src/emit/continue.js +28 -0
- package/src/emit/copilot.js +12 -0
- package/src/emit/cursor.js +23 -0
- package/src/emit/gemini.js +40 -0
- package/src/emit/mcp.js +94 -0
- package/src/emit/windsurf.js +22 -0
- package/src/emit/zed.js +34 -0
- package/src/eval.js +47 -0
- package/src/extract.js +82 -0
- package/src/harden.js +44 -0
- package/src/imagine.js +301 -0
- package/src/init.js +178 -0
- package/src/lean.js +149 -0
- package/src/ledger.js +475 -0
- package/src/ledger_bridge.js +279 -0
- package/src/ledger_read.js +152 -0
- package/src/ledger_store.js +360 -0
- package/src/lessons.js +185 -0
- package/src/lessons_store.js +137 -0
- package/src/metrics.js +54 -0
- package/src/model_tiers.js +17 -0
- package/src/model_tiers.json +39 -0
- package/src/predictor.js +143 -0
- package/src/preflight.js +410 -0
- package/src/providers.js +320 -0
- package/src/recall.js +103 -0
- package/src/reuse.js +0 -0
- package/src/route.js +323 -0
- package/src/scope.js +122 -0
- package/src/skillgate.js +89 -0
- package/src/speclock.js +64 -0
- package/src/substrate.js +492 -0
- package/src/sync.js +132 -0
- package/src/taste.js +55 -0
- package/src/uicheck.js +96 -0
- package/src/uifingerprint.js +861 -0
- package/src/uivisual.js +334 -0
- package/src/util.js +71 -0
- package/src/verify.js +117 -0
- package/templates/project-layer/.claude/settings.json +22 -0
- package/templates/project-layer/.claude/skills/hostlelo-deploy/SKILL.md +38 -0
- package/templates/project-layer/AGENTS.md +28 -0
- package/templates/project-layer/CLAUDE.md +40 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-modernizer
|
|
3
|
+
description: >-
|
|
4
|
+
Modernize or refactor a legacy codebase incrementally while preserving business
|
|
5
|
+
logic. Replaces hand-rolled logic with the standard library, a native feature,
|
|
6
|
+
an already-installed dependency, or (only after live research) a well-vetted new
|
|
7
|
+
package, then applies minimal design patterns to whatever custom code remains.
|
|
8
|
+
Confirms scope first (full-codebase, one module/partial, or only the exact lines
|
|
9
|
+
named) and flags file count, line count, and breaking-change risk before
|
|
10
|
+
generating a large diff, asking instead of assuming when scope, a library choice,
|
|
11
|
+
or a behavior change is unclear.
|
|
12
|
+
Use for migrations (framework/language/version upgrades), killing tech debt,
|
|
13
|
+
refactoring an old project, or "bring this up to date". Use whenever the user
|
|
14
|
+
asks to refactor, modernize, de-bloat, clean up, simplify, upgrade, or audit
|
|
15
|
+
code; asks "is there a library for this", "don't reinvent the wheel", "replace
|
|
16
|
+
with a framework", "reduce boilerplate", or "apply proper design patterns"; wants
|
|
17
|
+
a root-cause fix over a quick patch; says "full upgrade" vs "partial upgrade" vs
|
|
18
|
+
"just fix this one thing"; or wants a legacy-code/tech-debt audit or a
|
|
19
|
+
dependency-choice recommendation.
|
|
20
|
+
license: MIT
|
|
21
|
+
attribution: >-
|
|
22
|
+
This skill's ladder is adapted from DietrichGebert/ponytail (MIT licensed).
|
|
23
|
+
See "Relationship to ponytail" below.
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# Code Modernizer
|
|
27
|
+
|
|
28
|
+
## Philosophy
|
|
29
|
+
|
|
30
|
+
A senior engineer isn't measured by how much code they wrote today, but by how
|
|
31
|
+
little the system needs. Before touching anything, decide whether the code should
|
|
32
|
+
be replaced with something already established. If custom code is genuinely
|
|
33
|
+
necessary, write the least of it that solves the real problem without dropping
|
|
34
|
+
behavior anyone relies on.
|
|
35
|
+
|
|
36
|
+
This skill's core decision procedure is adapted from Dietrich Gebert's `ponytail`
|
|
37
|
+
project (MIT licensed, github.com/DietrichGebert/ponytail). It extends ponytail
|
|
38
|
+
two ways: it applies the same ladder retroactively to audit code that already
|
|
39
|
+
exists, not just to gate new code being written, and it adds scope control, a
|
|
40
|
+
pre-flight cost/impact gate, and a mandatory live-research step for whenever a
|
|
41
|
+
genuinely new dependency is on the table. See "Relationship to ponytail" at the
|
|
42
|
+
end.
|
|
43
|
+
|
|
44
|
+
## The ladder
|
|
45
|
+
|
|
46
|
+
Before writing, keeping, or replacing any piece of logic, stop at the first rung
|
|
47
|
+
that holds:
|
|
48
|
+
|
|
49
|
+
1. **Does this need to exist at all?** (YAGNI). If a feature isn't used, delete it
|
|
50
|
+
instead of modernizing it.
|
|
51
|
+
2. **Already solved elsewhere in this codebase?** Reuse it, don't rewrite it.
|
|
52
|
+
3. **Standard library does it?** Use it.
|
|
53
|
+
4. **Native platform feature does it?** Use it. The browser already has
|
|
54
|
+
`<input type="date">`, so don't reach for a date-picker library.
|
|
55
|
+
5. **Already-installed dependency does it?** Use it. Zero new install cost.
|
|
56
|
+
6. **Is a new dependency genuinely justified?** Only after the research pass in
|
|
57
|
+
Step 3 below. Never skip straight here.
|
|
58
|
+
7. **Can what's left be one line?** Make it one line.
|
|
59
|
+
8. **Only then:** write the minimum code that works, shaped by whichever design
|
|
60
|
+
pattern (if any) removes real duplication. See
|
|
61
|
+
`references/design-patterns-cheatsheet.md`.
|
|
62
|
+
|
|
63
|
+
The ladder runs after understanding the problem, not instead of it. Read the code
|
|
64
|
+
and its tests before picking a rung (Step 2).
|
|
65
|
+
|
|
66
|
+
## Two modes, same ladder
|
|
67
|
+
|
|
68
|
+
- **Forward** (writing new code): apply the ladder before writing anything.
|
|
69
|
+
- **Audit** (existing code): apply the same ladder retroactively. For every
|
|
70
|
+
hand-rolled block in scope, ask: "would rung 3, 4, 5, or a researched rung 6
|
|
71
|
+
now produce less code, or safer code, than what's already here?" If yes, and the
|
|
72
|
+
check in Step 4 clears it, propose the swap. This audit mode is the part
|
|
73
|
+
`ponytail` doesn't cover. It governs new code; this skill governs the code
|
|
74
|
+
that's already sitting in the repo.
|
|
75
|
+
|
|
76
|
+
## Step 0: Scope handshake (every time, before any real work)
|
|
77
|
+
|
|
78
|
+
Refactoring has a blast radius. Before generating anything beyond a one-file,
|
|
79
|
+
one-function fix, pin down which of these three the user wants:
|
|
80
|
+
|
|
81
|
+
- **Full**: a comprehensive pass across the named codebase or repo. Expect
|
|
82
|
+
multiple files and possibly breaking changes; produce a migration note.
|
|
83
|
+
- **Partial**: bounded to one named module, directory, or feature area. No
|
|
84
|
+
drive-by edits outside that boundary, even if something nearby looks equally
|
|
85
|
+
outdated.
|
|
86
|
+
- **Targeted**: only the exact file, function, or lines named. Nothing else
|
|
87
|
+
changes.
|
|
88
|
+
|
|
89
|
+
If the request already states scope clearly ("just fix this function", "modernize
|
|
90
|
+
the whole `utils/` folder"), proceed without asking. If it's ambiguous ("clean up
|
|
91
|
+
this codebase", "make this better"), ask one direct question offering these three
|
|
92
|
+
options before reading any more files than needed to ask it well. Don't default to
|
|
93
|
+
Full because it seems more thorough. Full is the most expensive and highest-risk
|
|
94
|
+
option, not the safe default.
|
|
95
|
+
|
|
96
|
+
## Step 1: Pre-flight scan and cost/impact flag (Full/Partial only, skip for Targeted)
|
|
97
|
+
|
|
98
|
+
Ground the estimate in real numbers instead of a guess. Run:
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
python3 scripts/preflight_scan.py <path>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
It's stdlib-only Python, nothing to install. Report back to the user in a short
|
|
105
|
+
block before generating anything:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
Scope: <Full|Partial> on <path>
|
|
109
|
+
~<N> files, ~<X> lines in range
|
|
110
|
+
Dependencies already available: <short list from the manifest>
|
|
111
|
+
Flagged high-cost: <any file over ~800 lines, or the pass touching 15-20+ files, name them>
|
|
112
|
+
Estimated: <rough tool-call / diff count>, <low|medium|high> breaking-change risk
|
|
113
|
+
Proceed as scoped, narrow it, or switch to Targeted?
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Treat anything the scan flags as high-cost as its own decision point. Offer full
|
|
117
|
+
detail there or a summarized diff, rather than silently generating all of it. Full
|
|
118
|
+
rubric, including the flexibility trade-off, in `references/cost-impact-preflight.md`.
|
|
119
|
+
|
|
120
|
+
## Step 2: Read before you touch (root cause, not a patch)
|
|
121
|
+
|
|
122
|
+
Before proposing any swap, read what's actually there: the function, its tests,
|
|
123
|
+
nearby comments, and the commit that introduced the "ugly" version if it's
|
|
124
|
+
available. Hand-rolled code is sometimes just unmaintained. Sometimes it's
|
|
125
|
+
load-bearing for an edge case the clean replacement doesn't handle. Confirm which
|
|
126
|
+
one is in front of you before replacing it. If the tests don't cover a case the
|
|
127
|
+
old code was clearly written for, say so instead of silently dropping it.
|
|
128
|
+
|
|
129
|
+
## Step 3: Research before recommending a new dependency (mandatory, live)
|
|
130
|
+
|
|
131
|
+
Rungs 1 through 5 need no research; they're free and already known to be safe.
|
|
132
|
+
Rung 6, a genuinely new dependency, is the one place memory goes stale fast: a
|
|
133
|
+
library that was the obvious choice eighteen months ago can be unmaintained,
|
|
134
|
+
superseded, or carrying an open CVE today. Never recommend a package from memory
|
|
135
|
+
alone. Check its current state first, live, with search. Checklist,
|
|
136
|
+
current-as-of-lookup sources, and red/green flags are in
|
|
137
|
+
`references/research-protocol.md`. Summarize the choice in one or two sentences
|
|
138
|
+
per swap in the final answer; this step is not the place to write an essay.
|
|
139
|
+
|
|
140
|
+
## Step 4: Impact vs. flexibility, before writing the change
|
|
141
|
+
|
|
142
|
+
Before writing the replacement, work out:
|
|
143
|
+
|
|
144
|
+
- who else calls this (the blast radius may be bigger than the one call site that
|
|
145
|
+
prompted the change),
|
|
146
|
+
- what flexibility the current code buys and whether anyone actually uses it (grep
|
|
147
|
+
before removing), and
|
|
148
|
+
- whether the migration cost (call-site updates, type changes, test churn) is
|
|
149
|
+
worth what's gained.
|
|
150
|
+
|
|
151
|
+
If the trade isn't clearly worth it, say what was found and ask. The user knows
|
|
152
|
+
release timing and risk appetite that the code alone doesn't reveal. Full rubric
|
|
153
|
+
in `references/cost-impact-preflight.md`.
|
|
154
|
+
|
|
155
|
+
## Step 5: Output discipline
|
|
156
|
+
|
|
157
|
+
- Show the diff or the changed function, not the whole file, unless the whole file
|
|
158
|
+
is asked for.
|
|
159
|
+
- One consolidated summary per batch of related changes, not a running commentary
|
|
160
|
+
per file.
|
|
161
|
+
- Batch logically related edits together instead of narrating each one separately.
|
|
162
|
+
- Don't restate the ladder or the research trail in the final answer. The user
|
|
163
|
+
needs the change and a one-line reason, not the process that produced it.
|
|
164
|
+
|
|
165
|
+
## Incremental modernization methodology (Full/Partial scope)
|
|
166
|
+
|
|
167
|
+
For legacy/dated codebases, modernize in safe, verifiable increments — never a
|
|
168
|
+
big-bang rewrite. Keep a human in the loop at each gate.
|
|
169
|
+
|
|
170
|
+
### Assess (read-only first)
|
|
171
|
+
- Map dependencies and the module graph; identify dead code and the highest-churn,
|
|
172
|
+
highest-complexity, highest-business-value hotspots.
|
|
173
|
+
- Use the `serena` LSP + `scout` subagent so this exploration doesn't fill main
|
|
174
|
+
context. Write findings to `MODERNIZATION.md`.
|
|
175
|
+
- Verify target frameworks/versions with `tech-selector` (current best, not
|
|
176
|
+
training-data defaults).
|
|
177
|
+
|
|
178
|
+
### Safety net before changing anything
|
|
179
|
+
- Characterize current behavior with tests. If coverage is thin on the code you'll
|
|
180
|
+
touch, add regression tests that pin the *existing* output first — so a refactor
|
|
181
|
+
that changes behavior fails loudly.
|
|
182
|
+
|
|
183
|
+
### Transform incrementally
|
|
184
|
+
- One bounded slice at a time (a module, a route, a component). Preserve business
|
|
185
|
+
logic exactly; modernize the form around it.
|
|
186
|
+
- After each slice: run tests + build + lint, and for UI use `ui-workflow`'s
|
|
187
|
+
screenshot check. Commit per slice with a conventional message so each step is
|
|
188
|
+
revertible.
|
|
189
|
+
|
|
190
|
+
### Verify each slice
|
|
191
|
+
- Tests green, build passes, behavior unchanged (diff against the pinned regression
|
|
192
|
+
output). Use the `verifier` subagent on non-trivial slices.
|
|
193
|
+
- Patch security issues surfaced along the way (semgrep is installed) but keep them
|
|
194
|
+
as separate commits.
|
|
195
|
+
|
|
196
|
+
### Document
|
|
197
|
+
- Update `MODERNIZATION.md` with what changed and why; capture any institutional
|
|
198
|
+
knowledge recovered from the old code before it's lost.
|
|
199
|
+
|
|
200
|
+
### Guardrails
|
|
201
|
+
- Business continuity first: if a slice can't be proven behavior-preserving, stop
|
|
202
|
+
and surface it rather than guessing.
|
|
203
|
+
- No scope creep — modernize what the slice covers, not everything you notice.
|
|
204
|
+
|
|
205
|
+
## Never lazy about (unchanged from ponytail)
|
|
206
|
+
|
|
207
|
+
Input validation at trust boundaries, error handling that prevents data loss,
|
|
208
|
+
security, accessibility, and anything the user explicitly asked for. These are
|
|
209
|
+
never traded away for a smaller diff, no matter which rung of the ladder applies.
|
|
210
|
+
|
|
211
|
+
## Marking deferred opportunities
|
|
212
|
+
|
|
213
|
+
When a swap is correct in principle but out of scope for a Targeted request, don't
|
|
214
|
+
make it. Leave a one-line marker instead, mirroring ponytail's `ponytail:` comment:
|
|
215
|
+
|
|
216
|
+
```js
|
|
217
|
+
// modernize: could use structuredClone() (Node 17+) instead of this deep-clone helper
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
At the end of a session, list any `modernize:` markers left behind, so "later"
|
|
221
|
+
leaves a paper trail instead of quietly becoming "never."
|
|
222
|
+
|
|
223
|
+
## Ask, don't assume
|
|
224
|
+
|
|
225
|
+
Stop and ask one direct question, don't proceed on a guess, when:
|
|
226
|
+
|
|
227
|
+
- Scope isn't stated and can't be inferred confidently (Step 0).
|
|
228
|
+
- Two well-maintained libraries are both reasonable and the right one depends on a
|
|
229
|
+
constraint the user hasn't stated (bundle size vs. features, already using a
|
|
230
|
+
sibling package from one ecosystem, etc.).
|
|
231
|
+
- The swap is a breaking change to a public API or an exported function.
|
|
232
|
+
- The "clean" replacement doesn't obviously cover an edge case the current code
|
|
233
|
+
handles.
|
|
234
|
+
|
|
235
|
+
## Example
|
|
236
|
+
|
|
237
|
+
**Request:** "This `debounce` function in `hooks/useDebounce.ts` looks
|
|
238
|
+
hand-rolled, can you clean it up?"
|
|
239
|
+
|
|
240
|
+
This names an exact file and function, so it's **Targeted**; skip Step 1. Step 2:
|
|
241
|
+
read it and its tests; it correctly cancels on unmount, which a naive replacement
|
|
242
|
+
might miss. Step 3: `lodash` is already an installed dependency (rung 5) with a
|
|
243
|
+
well-tested `debounce`, so no new dependency and no research pass are needed.
|
|
244
|
+
Step 4: one call site, low risk, no flexibility lost. Result: a 3-line wrapper
|
|
245
|
+
around `lodash.debounce` that preserves the unmount-cancel behavior, with a
|
|
246
|
+
one-line note on why. Not a rewrite, not an essay, not a new package.
|
|
247
|
+
|
|
248
|
+
## Relationship to ponytail
|
|
249
|
+
|
|
250
|
+
This skill deliberately does not re-implement ponytail's own commands
|
|
251
|
+
(`/ponytail-review`, `/ponytail-audit`, `/ponytail-debt`, its lite/full/ultra
|
|
252
|
+
intensity dial). That would be exactly the kind of reinventing this skill exists
|
|
253
|
+
to prevent. For governing new code as it's written, install ponytail itself in
|
|
254
|
+
Claude Code: it's free, MIT-licensed, actively maintained, and benchmarked at
|
|
255
|
+
roughly half the code and 20% lower cost against a no-skill baseline.
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
/plugin marketplace add DietrichGebert/ponytail
|
|
259
|
+
/plugin install ponytail@ponytail
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
The two compose cleanly. Ponytail keeps new code minimal as it's written; this
|
|
263
|
+
skill audits and modernizes what's already in the repo, with the scope control,
|
|
264
|
+
cost/impact gate, and live research discipline the ladder alone doesn't cover.
|
|
265
|
+
|
|
266
|
+
## Reference files (load only when the step above points here)
|
|
267
|
+
|
|
268
|
+
- `references/research-protocol.md`: vetting a candidate library, current sources
|
|
269
|
+
to check, red/green flags, decision template.
|
|
270
|
+
- `references/design-patterns-cheatsheet.md`: smell → pattern → when to skip it,
|
|
271
|
+
compact table.
|
|
272
|
+
- `references/cost-impact-preflight.md`: full pre-flight output template and the
|
|
273
|
+
impact/flexibility rubric.
|
|
274
|
+
- `scripts/preflight_scan.py`: stdlib-only scanner for file count, LOC, and
|
|
275
|
+
dependency manifests.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Cost / impact pre-flight and flexibility rubric
|
|
2
|
+
|
|
3
|
+
## Pre-flight output template (Step 1)
|
|
4
|
+
|
|
5
|
+
Run `python3 scripts/preflight_scan.py <path>` and report:
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Scope: <Full|Partial> on <path>
|
|
9
|
+
~<N> files, ~<X> lines in range
|
|
10
|
+
Dependencies already available: <short list from the manifest>
|
|
11
|
+
Flagged high-cost: <any file over ~800 lines, or the pass touching 15-20+ files, name them>
|
|
12
|
+
Estimated: <rough tool-call / diff count>, <low|medium|high> breaking-change risk
|
|
13
|
+
Proceed as scoped, narrow it, or switch to Targeted?
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### Breaking-change risk levels
|
|
17
|
+
|
|
18
|
+
| Level | Criteria |
|
|
19
|
+
|---|---|
|
|
20
|
+
| **Low** | Internal/private functions only; no exported API changes; no type signature changes |
|
|
21
|
+
| **Medium** | Exported functions change signature but callers are all in-repo; or a dependency is swapped that has a slightly different API |
|
|
22
|
+
| **High** | Public API / SDK changes; cross-repo consumers possible; type changes that propagate through generics; removal of a feature flag or config option |
|
|
23
|
+
|
|
24
|
+
### High-cost file handling
|
|
25
|
+
|
|
26
|
+
When a file exceeds ~800 lines or the total pass touches 15–20+ files:
|
|
27
|
+
|
|
28
|
+
1. Name the files explicitly in the pre-flight report.
|
|
29
|
+
2. Offer the user a choice: full detailed diff, summarized diff (key changes
|
|
30
|
+
only), or skip that file for now.
|
|
31
|
+
3. Don't silently generate a massive diff — the user may want to split the work
|
|
32
|
+
across PRs.
|
|
33
|
+
|
|
34
|
+
## Impact vs. flexibility rubric (Step 4)
|
|
35
|
+
|
|
36
|
+
Before writing each swap, evaluate:
|
|
37
|
+
|
|
38
|
+
| Question | If yes → | If no → |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| Does anyone else call this? | Check all call sites; mention them in the summary | Safe to change in isolation |
|
|
41
|
+
| Does the current code handle edge cases the replacement doesn't? | Keep the edge-case handling; wrap the replacement if needed | Straight swap |
|
|
42
|
+
| Does the current API surface offer flexibility someone might rely on? | Grep for usage of that flexibility; if unused, remove | Keep the simpler replacement |
|
|
43
|
+
| Is the migration cost (call-site updates, type changes, test churn) < the maintenance cost of keeping the old code? | Proceed | Defer — leave a `// modernize:` marker |
|
|
44
|
+
| Does the swap change observable behavior (return types, error messages, side effects)? | Treat as a breaking change; flag in the pre-flight report | Transparent swap |
|
|
45
|
+
|
|
46
|
+
### When to defer instead of swap
|
|
47
|
+
|
|
48
|
+
- The swap is correct but the module is in active development (merge conflicts).
|
|
49
|
+
- The user said "Targeted" and the swap is outside the named scope.
|
|
50
|
+
- The migration cost is high and the old code works fine — it's debt, not a bug.
|
|
51
|
+
- Two equally good replacements exist and the choice depends on constraints the
|
|
52
|
+
user hasn't stated.
|
|
53
|
+
|
|
54
|
+
In all these cases, leave a `// modernize:` marker and list it at session end.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Design patterns cheatsheet
|
|
2
|
+
|
|
3
|
+
Apply a pattern only when it removes real duplication or coupling. If the code is
|
|
4
|
+
already clear and short, a pattern adds ceremony for no gain — skip it.
|
|
5
|
+
|
|
6
|
+
| Smell | Pattern | When to skip |
|
|
7
|
+
|---|---|---|
|
|
8
|
+
| Copy-pasted logic with minor variations | **Strategy** — extract the varying part into a function/object, pass it in | Fewer than 3 copies, or the variations are trivial one-liners |
|
|
9
|
+
| Long `if/else` or `switch` choosing behavior by type/string | **Strategy** or **Map lookup** — a plain `Record<string, handler>` is usually enough | Only 2–3 branches and unlikely to grow |
|
|
10
|
+
| Object construction with many optional params | **Builder** or plain options object | A single config object with defaults already reads well |
|
|
11
|
+
| Multiple callers need the same setup/teardown around a core operation | **Template Method** (or just a higher-order function) | Only one caller — inline the setup |
|
|
12
|
+
| Need to react to state changes across unrelated modules | **Observer / Event emitter** | Two modules — a direct callback is simpler |
|
|
13
|
+
| Expensive object creation, same inputs → same output | **Flyweight / Cache / Memoize** | Object is cheap, or inputs rarely repeat |
|
|
14
|
+
| Access to a resource that needs lifecycle management | **Dispose / using / context manager** | Resource is process-scoped (no cleanup needed) |
|
|
15
|
+
| External API that doesn't match your domain model | **Adapter** | You control both sides — just change the source |
|
|
16
|
+
| Deep nesting of decorators or wrappers | **Middleware / Pipeline** — compose a flat list | Two wrappers max — nesting is still readable |
|
|
17
|
+
| Repeated null-checks or fallback chains | **Null Object** or optional chaining (`?.`) | Language already has `??` / `?.` — use it (rung 3/4) |
|
|
18
|
+
|
|
19
|
+
## Anti-patterns to avoid
|
|
20
|
+
|
|
21
|
+
- **Singleton for testability**: makes mocking hard. Prefer dependency injection.
|
|
22
|
+
- **Factory for one type**: a constructor call is simpler.
|
|
23
|
+
- **Abstract base class with one subclass**: remove the abstraction.
|
|
24
|
+
- **Pattern just to match a textbook**: the code is the product, not the diagram.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Research protocol — vetting a candidate library (rung 6)
|
|
2
|
+
|
|
3
|
+
Only reach this file when rungs 1–5 failed and a new dependency is genuinely on
|
|
4
|
+
the table. Never skip straight here.
|
|
5
|
+
|
|
6
|
+
## Mandatory live checks
|
|
7
|
+
|
|
8
|
+
Run these before recommending. Stale memory is the failure mode this protocol
|
|
9
|
+
exists to prevent.
|
|
10
|
+
|
|
11
|
+
| Check | Source | Red flag |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| Last commit / release | GitHub releases or npm/PyPI page | > 12 months with open issues |
|
|
14
|
+
| Open CVEs | `npm audit` / `pip-audit` / Snyk DB / GitHub advisories | Any unpatched critical/high |
|
|
15
|
+
| Maintenance signal | Issue tracker, PR merge cadence | Dozens of unanswered issues, no maintainer response in months |
|
|
16
|
+
| Download trend | npm trends, PyPI stats | Steep decline or flatline vs. competitors |
|
|
17
|
+
| License compatibility | `package.json` / `setup.cfg` license field | Copyleft (GPL) in an MIT project, or no license |
|
|
18
|
+
| Bundle size (frontend) | bundlephobia.com or `import-cost` | > 50 kB gzipped for a single utility |
|
|
19
|
+
| Peer / transitive deps | `npm ls` / `pip show` | Pulls in a heavy tree for a small task |
|
|
20
|
+
|
|
21
|
+
## Green flags (not required, but strengthen the case)
|
|
22
|
+
|
|
23
|
+
- Active maintainer with a history of responding to security reports.
|
|
24
|
+
- TypeScript types shipped (not DefinitelyTyped-only).
|
|
25
|
+
- Used by well-known projects (check dependents on GitHub).
|
|
26
|
+
- Clear migration path if abandoned (small API surface, stdlib fallback exists).
|
|
27
|
+
|
|
28
|
+
## Decision template (one per candidate)
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
Library: <name@version>
|
|
32
|
+
Rung: 6 (new dependency)
|
|
33
|
+
Replaces: <what hand-rolled code or older dep>
|
|
34
|
+
Last release: <date> | Weekly downloads: <N> | License: <X>
|
|
35
|
+
CVEs: <none | list>
|
|
36
|
+
Bundle impact: <+N kB gzipped> (frontend only)
|
|
37
|
+
Verdict: adopt / skip / ask user
|
|
38
|
+
Reason (1–2 sentences): ...
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Include this block in the final answer when recommending a new dependency. Keep it
|
|
42
|
+
short — the user needs the decision, not the research log.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Pre-flight scanner for code-modernizer skill.
|
|
3
|
+
|
|
4
|
+
Stdlib-only — nothing to install.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
python3 scripts/preflight_scan.py <path> [--json]
|
|
8
|
+
|
|
9
|
+
Reports file count, total lines of code, detected dependency manifests,
|
|
10
|
+
and flags high-cost files (> 800 lines).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import os
|
|
15
|
+
import sys
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
SKIP_DIRS = {
|
|
19
|
+
"node_modules", ".git", "__pycache__", ".venv", "venv",
|
|
20
|
+
"dist", "build", ".next", ".nuxt", "coverage", ".tox",
|
|
21
|
+
"vendor", "target", "out", ".cache", ".turbo",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
CODE_EXTENSIONS = {
|
|
25
|
+
".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs",
|
|
26
|
+
".py", ".pyx",
|
|
27
|
+
".rs", ".go", ".java", ".kt", ".kts",
|
|
28
|
+
".c", ".cpp", ".cc", ".h", ".hpp",
|
|
29
|
+
".rb", ".php", ".swift", ".scala",
|
|
30
|
+
".vue", ".svelte", ".astro",
|
|
31
|
+
".css", ".scss", ".less",
|
|
32
|
+
".sql", ".sh", ".bash", ".zsh",
|
|
33
|
+
".lua", ".zig", ".nim", ".ex", ".exs",
|
|
34
|
+
".cs", ".fs",
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
MANIFEST_FILES = {
|
|
38
|
+
"package.json", "package-lock.json", "yarn.lock", "pnpm-lock.yaml",
|
|
39
|
+
"requirements.txt", "pyproject.toml", "setup.py", "setup.cfg", "Pipfile",
|
|
40
|
+
"Cargo.toml", "go.mod", "Gemfile", "composer.json",
|
|
41
|
+
"build.gradle", "build.gradle.kts", "pom.xml",
|
|
42
|
+
"pubspec.yaml", "mix.exs",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
HIGH_COST_THRESHOLD = 800
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def count_lines(path: Path) -> int:
|
|
49
|
+
try:
|
|
50
|
+
with open(path, "r", encoding="utf-8", errors="replace") as f:
|
|
51
|
+
return sum(1 for _ in f)
|
|
52
|
+
except (OSError, UnicodeDecodeError):
|
|
53
|
+
return 0
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def parse_dependencies(root: Path) -> list[str]:
|
|
57
|
+
deps = []
|
|
58
|
+
pkg = root / "package.json"
|
|
59
|
+
if pkg.is_file():
|
|
60
|
+
try:
|
|
61
|
+
data = json.loads(pkg.read_text(encoding="utf-8"))
|
|
62
|
+
for key in ("dependencies", "devDependencies"):
|
|
63
|
+
if key in data:
|
|
64
|
+
deps.extend(data[key].keys())
|
|
65
|
+
except (json.JSONDecodeError, OSError):
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
req = root / "requirements.txt"
|
|
69
|
+
if req.is_file():
|
|
70
|
+
try:
|
|
71
|
+
for line in req.read_text(encoding="utf-8").splitlines():
|
|
72
|
+
line = line.strip()
|
|
73
|
+
if line and not line.startswith("#") and not line.startswith("-"):
|
|
74
|
+
name = line.split("==")[0].split(">=")[0].split("<=")[0].split("[")[0].strip()
|
|
75
|
+
if name:
|
|
76
|
+
deps.append(name)
|
|
77
|
+
except OSError:
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
pyproject = root / "pyproject.toml"
|
|
81
|
+
if pyproject.is_file():
|
|
82
|
+
try:
|
|
83
|
+
content = pyproject.read_text(encoding="utf-8")
|
|
84
|
+
in_deps = False
|
|
85
|
+
for line in content.splitlines():
|
|
86
|
+
if line.strip().startswith("dependencies"):
|
|
87
|
+
in_deps = True
|
|
88
|
+
continue
|
|
89
|
+
if in_deps:
|
|
90
|
+
if line.strip() == "]":
|
|
91
|
+
in_deps = False
|
|
92
|
+
continue
|
|
93
|
+
dep = line.strip().strip('",').split(">=")[0].split("==")[0].split("<")[0].strip()
|
|
94
|
+
if dep:
|
|
95
|
+
deps.append(dep)
|
|
96
|
+
except OSError:
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
return sorted(set(deps))
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def scan(root: Path) -> dict:
|
|
103
|
+
file_count = 0
|
|
104
|
+
total_lines = 0
|
|
105
|
+
high_cost_files = []
|
|
106
|
+
manifests_found = []
|
|
107
|
+
|
|
108
|
+
for dirpath, dirnames, filenames in os.walk(root):
|
|
109
|
+
dirnames[:] = [d for d in dirnames if d not in SKIP_DIRS]
|
|
110
|
+
|
|
111
|
+
for fname in filenames:
|
|
112
|
+
fpath = Path(dirpath) / fname
|
|
113
|
+
|
|
114
|
+
if fname in MANIFEST_FILES:
|
|
115
|
+
manifests_found.append(str(fpath.relative_to(root)))
|
|
116
|
+
|
|
117
|
+
if fpath.suffix.lower() in CODE_EXTENSIONS:
|
|
118
|
+
file_count += 1
|
|
119
|
+
lines = count_lines(fpath)
|
|
120
|
+
total_lines += lines
|
|
121
|
+
if lines > HIGH_COST_THRESHOLD:
|
|
122
|
+
high_cost_files.append({
|
|
123
|
+
"file": str(fpath.relative_to(root)),
|
|
124
|
+
"lines": lines,
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
deps = parse_dependencies(root)
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
"root": str(root),
|
|
131
|
+
"file_count": file_count,
|
|
132
|
+
"total_lines": total_lines,
|
|
133
|
+
"manifests": manifests_found,
|
|
134
|
+
"dependencies": deps,
|
|
135
|
+
"high_cost_files": sorted(high_cost_files, key=lambda x: -x["lines"]),
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def format_report(result: dict) -> str:
|
|
140
|
+
lines = [
|
|
141
|
+
f"Scope: scan on {result['root']}",
|
|
142
|
+
f"~{result['file_count']} files, ~{result['total_lines']} lines of code",
|
|
143
|
+
]
|
|
144
|
+
|
|
145
|
+
if result["dependencies"]:
|
|
146
|
+
dep_preview = result["dependencies"][:20]
|
|
147
|
+
dep_str = ", ".join(dep_preview)
|
|
148
|
+
if len(result["dependencies"]) > 20:
|
|
149
|
+
dep_str += f" (+{len(result['dependencies']) - 20} more)"
|
|
150
|
+
lines.append(f"Dependencies already available: {dep_str}")
|
|
151
|
+
else:
|
|
152
|
+
lines.append("Dependencies already available: (none detected)")
|
|
153
|
+
|
|
154
|
+
if result["high_cost_files"]:
|
|
155
|
+
hc = "; ".join(f"{f['file']} ({f['lines']} lines)" for f in result["high_cost_files"][:10])
|
|
156
|
+
lines.append(f"Flagged high-cost (>{HIGH_COST_THRESHOLD} lines): {hc}")
|
|
157
|
+
else:
|
|
158
|
+
lines.append(f"Flagged high-cost: none (all files <{HIGH_COST_THRESHOLD} lines)")
|
|
159
|
+
|
|
160
|
+
risk = "low"
|
|
161
|
+
if result["file_count"] > 20:
|
|
162
|
+
risk = "medium"
|
|
163
|
+
if result["file_count"] > 50 or result["total_lines"] > 20000:
|
|
164
|
+
risk = "high"
|
|
165
|
+
|
|
166
|
+
lines.append(f"Estimated breaking-change risk: {risk}")
|
|
167
|
+
|
|
168
|
+
return "\n".join(lines)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def main():
|
|
172
|
+
if len(sys.argv) < 2:
|
|
173
|
+
print(f"Usage: {sys.argv[0]} <path> [--json]", file=sys.stderr)
|
|
174
|
+
sys.exit(1)
|
|
175
|
+
|
|
176
|
+
target = Path(sys.argv[1]).resolve()
|
|
177
|
+
if not target.is_dir():
|
|
178
|
+
print(f"Error: {target} is not a directory", file=sys.stderr)
|
|
179
|
+
sys.exit(1)
|
|
180
|
+
|
|
181
|
+
result = scan(target)
|
|
182
|
+
|
|
183
|
+
if "--json" in sys.argv:
|
|
184
|
+
print(json.dumps(result, indent=2))
|
|
185
|
+
else:
|
|
186
|
+
print(format_report(result))
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
if __name__ == "__main__":
|
|
190
|
+
main()
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cognitive-substrate
|
|
3
|
+
description: >-
|
|
4
|
+
Use before ambiguous, expensive, multi-file, or mutating coding work to run
|
|
5
|
+
Forge's cognitive substrate: assumption gate, model routing, impact
|
|
6
|
+
prediction, scope decomposition, memory/lesson lookup, minimality check, and
|
|
7
|
+
verification planning. Trigger when a user asks to edit/refactor/fix/design
|
|
8
|
+
production code, integrate features, choose model effort, inspect blast
|
|
9
|
+
radius, or avoid agent assumptions.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Cognitive Substrate
|
|
13
|
+
|
|
14
|
+
Wrap the frozen model in a pre-action check: gate assumptions, route model effort, predict
|
|
15
|
+
blast radius, decompose scope, surface past lessons, and plan verification — **before** editing.
|
|
16
|
+
|
|
17
|
+
## When to run it
|
|
18
|
+
|
|
19
|
+
Any ambiguous, expensive, multi-file, or mutating task. Skip one-line, well-specified fixes.
|
|
20
|
+
In Claude Code it fires automatically on every prompt (UserPromptSubmit hook); in other
|
|
21
|
+
agents run the CLI yourself, or call the MCP tool `substrate_check`.
|
|
22
|
+
|
|
23
|
+
## Run
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
forge substrate "<task>" --json # full contract; use the fields below
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Act on the result
|
|
30
|
+
|
|
31
|
+
1. **`okToProceed: false`** → ask the returned `assumption.questions` before editing. Do not guess.
|
|
32
|
+
2. **`route.tier`** → start at that model tier (cheapest capable); escalate only after an external verifier fails.
|
|
33
|
+
3. **`impact.impactedFiles`** → read these before editing a named symbol/file (the blast radius).
|
|
34
|
+
4. **`scope.clusters`** → split independent groups into separate sessions; note coupled files you didn't name.
|
|
35
|
+
5. **`memory.advisory`** → context, not law; tests and human corrections override it.
|
|
36
|
+
6. **`verification.checklist`** → run it and show output before claiming done.
|
|
37
|
+
|
|
38
|
+
## Worked example
|
|
39
|
+
|
|
40
|
+
`forge substrate "make the auth better"` → `proceed: ASK FIRST` + clarify
|
|
41
|
+
questions: ask them instead of editing. A clear task returns `proceed: yes` plus
|
|
42
|
+
the impacted files — including importers you didn't name.
|
|
43
|
+
|
|
44
|
+
## Deeper single checks
|
|
45
|
+
|
|
46
|
+
`forge preflight "<task>"` (assumptions) · `forge route "<task>"` (model tier) ·
|
|
47
|
+
`forge impact <symbol|file>` (blast radius) · `forge scope <file…>` (decomposition) ·
|
|
48
|
+
`forge context "<task>"` (budgeted context assembly; *computes* what's missing) ·
|
|
49
|
+
`forge imagine "<task>" [--run]` (predicted breaks + minimal covering test suite;
|
|
50
|
+
`--run` dry-runs it in a sandboxed worktree) ·
|
|
51
|
+
`forge diagnose "<error>"` (doom-loop: 3× the same failure signature = stop retrying,
|
|
52
|
+
escalate one tier with the minted diagnosis claim at the head of the prompt).
|
|
53
|
+
MCP equivalents: `assumption_gate`, `route_task`, `predict_impact`, `scope_files`.
|
|
54
|
+
|
|
55
|
+
For the full guide (how it works, extending it, the honesty boundary) and the white paper,
|
|
56
|
+
see `docs/cognitive-substrate/README.md`. `references/capability-map.md` maps faculties to commands.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Cognitive substrate capability map
|
|
2
|
+
|
|
3
|
+
| Paper capability | Forge surface | Guarantee |
|
|
4
|
+
| --- | --- | --- |
|
|
5
|
+
| Memory | `forge recall`, `forge cortex`, `forge ledger` | Facts/lessons persist as content-addressed ledger claims; `forge ledger blame` shows provenance. |
|
|
6
|
+
| Learning | `forge cortex`, ledger oracles | External outcomes (tests, CI, human accept/revert) move claim confidence; model weights do not change. |
|
|
7
|
+
| Imagination | `forge imagine [--run]`, `forge impact` | Predicted breaks + minimal covering test suite; `--run` dry-runs it in a sandboxed worktree. |
|
|
8
|
+
| Self-correction | `forge verify`, `forge diagnose` | Tests/builds beat model claims; 3× the same failure signature mints a diagnosis + escalation. |
|
|
9
|
+
| Impact-awareness | `forge atlas`, `forge impact` | Known symbols/files and likely dependents are surfaced. |
|
|
10
|
+
| M1 routing | `forge route` | Transparent model-tier recommendation. |
|
|
11
|
+
| M2 assumption gate | `forge preflight`, `forge context` | Under-specified tasks return *computed* missing-set questions. |
|
|
12
|
+
| M3 decomposition | `forge scope` | Import clusters show independent vs coupled files. |
|
|
13
|
+
| M4 goal anchoring | `forge anchor`, `forge substrate` | Changed files are checked against the stated goal. |
|
|
14
|
+
| M5 anti-over-engineering | `forge lean`, `forge uicheck design` | Footprint vs ask; UI slop-distance + fingerprint conformance gate. |
|
|
15
|
+
| M6 inline verification | `forge verify` | External checks are required before done. |
|
|
16
|
+
|
|
17
|
+
Limits: static graph edges are conservative; memory relevance and model routing are advisory; non-hook tools cannot be forcibly blocked.
|