@natjswenson/devlog 0.5.2 → 0.8.1
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/SKILL.md +112 -14
- package/bin/devlog.js +441 -7
- package/config.example.json +6 -0
- package/evals/fixtures/good-post.md +10 -6
- package/evals/fixtures/irreproducible-post.md +7 -3
- package/image-style/font.ttf +0 -0
- package/image-style/style-guide.example.md +107 -0
- package/lib/config_ops.mjs +5 -2
- package/lib/core.mjs +12 -1
- package/lib/cover_gen.mjs +124 -0
- package/lib/lint_post.mjs +48 -2
- package/lib/publish_entry.mjs +115 -3
- package/lib/render_cover.mjs +156 -0
- package/lib/scan.mjs +6 -0
- package/package.json +4 -1
- package/skill-invariants.json +26 -1
package/SKILL.md
CHANGED
|
@@ -39,7 +39,7 @@ Map the user's request onto the CLI — never hand-edit `config.json`:
|
|
|
39
39
|
| Intent | Command |
|
|
40
40
|
|---|---|
|
|
41
41
|
| Show config | `npx -y @natjswenson/devlog config --json` |
|
|
42
|
-
| Add a project | `npx -y @natjswenson/devlog add-project --yes --path <abs-path> [--key K] [--remote O/R] [--label L] [--tag-prefix P] [--path-filter F]` |
|
|
42
|
+
| Add a project | `npx -y @natjswenson/devlog add-project --yes --path <abs-path> [--key K] [--remote O/R] [--label L] [--tag-prefix P] [--path-filter F] [--private]` |
|
|
43
43
|
| Remove a project | `npx -y @natjswenson/devlog remove-project <key> --yes` |
|
|
44
44
|
| Change a setting | `npx -y @natjswenson/devlog set <field> <value>` (settable: `targetRepo`, `branch`, `gitAuthor`, `githubUser`, `voicePath`, `deepDive.minSources`, `deepDive.topicDomains`) |
|
|
45
45
|
|
|
@@ -51,6 +51,16 @@ get-url origin`. In a monorepo, suggest a `--path-filter` (the project's subdir)
|
|
|
51
51
|
and show the resulting project list. For **remove-project**, confirm once before running;
|
|
52
52
|
tell the user published entries are not deleted.
|
|
53
53
|
|
|
54
|
+
**Private repos.** If the user says the repo is private (or a source repo happens to be
|
|
55
|
+
private on GitHub even though it's configured normally), pass `--private`. A private
|
|
56
|
+
project's commits are never marked public in the scan (see Generate mode), so no post ever
|
|
57
|
+
links a commit for it, `## Changelog` is always omitted, and `--remote` is optional — the
|
|
58
|
+
tool has no reason to know or use the repo's GitHub location. This is a declared project
|
|
59
|
+
*type*, not something auto-detected from the GitHub API: `remoteMatches` + "on the
|
|
60
|
+
published branch" alone doesn't imply the repo is public, so a project with a real, correctly
|
|
61
|
+
configured remote that happens to sit in a private repo needs this flag or its commits would
|
|
62
|
+
otherwise scan as public.
|
|
63
|
+
|
|
54
64
|
If any command prints `{"error": "config-missing", ...}`, tell the user to run
|
|
55
65
|
`npx @natjswenson/devlog init` first. On `config-invalid`, show the message and offer to
|
|
56
66
|
fix the named field via `set`.
|
|
@@ -109,30 +119,55 @@ For each new release, in order:
|
|
|
109
119
|
|
|
110
120
|
**3a. Understand what actually shipped.** The scan gives you commit subjects and a
|
|
111
121
|
diffstat. When you need more, read the real changes — validate every hash matches
|
|
112
|
-
`^[0-9a-f]{7,40}$` first, then
|
|
122
|
+
`^[0-9a-f]{7,40}$` first, then (when a commit message and its diff disagree on a
|
|
123
|
+
specific fact — a count, a filename, a behavior — the diff wins):
|
|
113
124
|
|
|
114
125
|
```bash
|
|
115
126
|
git -C '<project.path>' show --stat '<hash>'
|
|
116
127
|
git -C '<project.path>' show '<hash>' -- '<project.pathFilter>'
|
|
117
128
|
```
|
|
118
129
|
|
|
130
|
+
Teach the code **as it existed at this tag**, not as it looks today — the repo may have
|
|
131
|
+
moved on since the release. When you need a file's state rather than a diff, use
|
|
132
|
+
`git -C '<project.path>' show '<tag>:<file>'` instead of reading the working tree.
|
|
133
|
+
This anchors the **facts** (what shipped, what it did); the teaching implementation may
|
|
134
|
+
still be a cleaner generalization per the how-to contract — anchor claims at the tag,
|
|
135
|
+
generalize the code.
|
|
136
|
+
|
|
119
137
|
**3b. Derive the topic.** Identify the **one** substantive engineering topic the work
|
|
120
138
|
touched (occasionally more, only when the work genuinely spans them) within
|
|
121
139
|
`deepDive.topicDomains`. The topic is the general concept *behind* what shipped (e.g.
|
|
122
140
|
shipping a `feature→dev→main` flow → branching strategy and release engineering). Never
|
|
123
141
|
pad with topics the work didn't touch.
|
|
124
142
|
|
|
143
|
+
Between candidate topics, pick the one the reader can most plausibly **use**: a
|
|
144
|
+
transferable technique they could apply to their own project this week beats project
|
|
145
|
+
trivia or niche internals. If the obvious topic is repo-specific, step up one level to
|
|
146
|
+
the general pattern behind it — the test is "could a reader finish this how-to and have
|
|
147
|
+
something working of their own?" When a release spans two candidate topics (or two
|
|
148
|
+
releases in one run share one), don't write the same guide twice: give each post the
|
|
149
|
+
most usable topic the run hasn't already covered. In a monorepo, one commit can appear
|
|
150
|
+
in several projects' ranges — it belongs to the post whose release story it is; other
|
|
151
|
+
posts leave it out of their narrative and `## Changelog` (check what sibling entries
|
|
152
|
+
already cover, across all projects).
|
|
153
|
+
|
|
125
154
|
**3c. Research before writing.** Use web search/fetch to gather at least
|
|
126
155
|
`deepDive.minSources` **distinct** reputable sources: official docs and release notes,
|
|
127
156
|
standards bodies, primary research, well-regarded engineering writing. Avoid SEO farms.
|
|
128
157
|
Every specific external claim (a version, a behavior, a study, a definition) must be
|
|
129
158
|
backed by a source you actually verified — if you can't source it, don't claim it. Don't
|
|
130
|
-
lean on one URL for most claims. Keep a working `(claim, url)` list.
|
|
159
|
+
lean on one URL for most claims. Keep a working `(claim, url)` list. Fetch tools can
|
|
160
|
+
summarize a page into quote-shaped text that isn't on it: before putting anything in
|
|
161
|
+
quotation marks, re-fetch asking for the verbatim wording and drop the quotes (paraphrase
|
|
162
|
+
instead) if it can't be confirmed.
|
|
131
163
|
|
|
132
164
|
**3d. Mine the gotchas.** Gotchas are the post's signature — **real traps from the
|
|
133
165
|
user's own experience**, never invented. Look for them in: fix commits that follow the
|
|
134
166
|
feature commits in the range, revert commits, `CHANGELOG` "Fixed" entries for this
|
|
135
|
-
version, and corrections visible in the diffs (an approach that changed mid-range).
|
|
167
|
+
version, and corrections visible in the diffs (an approach that changed mid-range).
|
|
168
|
+
Also follow the code this release introduced **forward** in history (`git log '<tag>..'
|
|
169
|
+
-- <files it touched>`): a later fix to that same code is prime gotcha material, as long
|
|
170
|
+
as the post says plainly when it was discovered ("this bit us a few weeks later"). Each
|
|
136
171
|
gotcha is written as **trap → symptom → escape**, concretely. If the history genuinely
|
|
137
172
|
shows none, the `## Gotchas` section instead covers the sourced failure modes a reader
|
|
138
173
|
will hit first, clearly framed as "what to watch for" rather than as personal war stories.
|
|
@@ -141,11 +176,13 @@ will hit first, clearly framed as "what to watch for" rather than as personal wa
|
|
|
141
176
|
|
|
142
177
|
```markdown
|
|
143
178
|
---
|
|
144
|
-
title: "<essay-style title; NOT 'release vX.Y.Z'>"
|
|
179
|
+
title: "<essay-style title in sentence case (capitalize only the first word and proper nouns); NOT 'release vX.Y.Z'>"
|
|
145
180
|
date: <release date from scan>
|
|
146
181
|
project: <project key>
|
|
147
182
|
version: <version from scan>
|
|
148
|
-
tags: [<
|
|
183
|
+
tags: [<5-10 specific, lowercase tags — tools, techniques, and concepts actually
|
|
184
|
+
present in the post, not just broad topic labels, e.g. git, ci-cd,
|
|
185
|
+
release-engineering, github-actions, semver, changelog-automation>]
|
|
149
186
|
summary: "<1-2 sentence hook that frames the how-to, not just what shipped>"
|
|
150
187
|
---
|
|
151
188
|
|
|
@@ -154,6 +191,11 @@ summary: "<1-2 sentence hook that frames the how-to, not just what shipped>"
|
|
|
154
191
|
<2-4 sentences: what this release delivered, plainly, then pivot to the topic the guide
|
|
155
192
|
teaches. The only purely-changelog part.>
|
|
156
193
|
|
|
194
|
+
<!-- Between Shipped and Gotchas, the headings below are the default shape, not a
|
|
195
|
+
requirement — only Shipped, Gotchas, and Sources are mandatory. Merge or reorder the
|
|
196
|
+
middle sections when the walkthrough flows better that way. Fence command OUTPUT
|
|
197
|
+
blocks as `text`. -->
|
|
198
|
+
|
|
157
199
|
## <Descriptive heading: setup / prerequisites>
|
|
158
200
|
|
|
159
201
|
<What a reader needs before the core build: dependencies, config, data model. Code block
|
|
@@ -189,11 +231,17 @@ the wiring between pieces, not just the interesting line.>
|
|
|
189
231
|
2. **Complete code.** Every symbol a code block references is defined in an earlier
|
|
190
232
|
block or explicitly stubbed with a one-line note ("`load_fixtures()` returns your test
|
|
191
233
|
DB handle"). The blocks compose into a runnable whole — no phantom fixtures or elided
|
|
192
|
-
helpers
|
|
234
|
+
helpers, and if a later block revises an earlier function, show the complete new
|
|
235
|
+
function, never a fragment calling helpers no block defines. Aim for the essential blocks (roughly 3-6 for a substantive feature); a clean,
|
|
193
236
|
general version of the concept is the goal, and never claim illustrative code is
|
|
194
237
|
verbatim production source.
|
|
195
238
|
3. **Reader-side verification.** The verify step gives commands the READER runs against
|
|
196
239
|
THEIR implementation, with expected output — not proof that the author's repo works.
|
|
240
|
+
When the blocks are cheap to execute (scratch dir, no external services), actually
|
|
241
|
+
run them and paste the real output. Never present output as observed if you didn't
|
|
242
|
+
run the command; if you can't run it, frame the expectation ("you should see…").
|
|
243
|
+
Verbose real output (tracebacks, long logs) may be trimmed to the signal lines or
|
|
244
|
+
whitespace-normalized when the post says so.
|
|
197
245
|
4. **Real gotchas** per 3d.
|
|
198
246
|
5. **Source diversity** per 3c, cited inline as markdown links AND in `## Sources`.
|
|
199
247
|
6. **Honest scope.** A single test file is not "end-to-end". Size the title, summary, and
|
|
@@ -202,12 +250,21 @@ the wiring between pieces, not just the interesting line.>
|
|
|
202
250
|
teaser.
|
|
203
251
|
7. **No leaked repo-specific artifacts.** Genericize or explain anything a stranger
|
|
204
252
|
would trip on (`.example` suffixes, monorepo nesting, internal tool names).
|
|
253
|
+
8. **Fun to follow.** The guide reads like a generous colleague walking the reader
|
|
254
|
+
through a build they'll actually finish: give an early runnable win, keep momentum
|
|
255
|
+
between steps, and make the payoff visible at each stage (show real output, not just
|
|
256
|
+
code). Fun comes from quick wins and concrete results — never forced jokes, hype, or
|
|
257
|
+
exclamation points.
|
|
205
258
|
|
|
206
259
|
**Separate fact from concept.** What the user *did* comes only from commits/diffs —
|
|
207
260
|
never invent metrics, motivations, or outcomes. What the topic *is* comes from the cited
|
|
208
261
|
sources. Keep the two distinguishable. Match the voice profile + `voice-notes.md`
|
|
209
|
-
(authenticity, anti-AI-tell, and punctuation rules
|
|
210
|
-
|
|
262
|
+
(authenticity, anti-AI-tell, and punctuation rules — these govern the PROSE; the em dash
|
|
263
|
+
in the `## Sources` template line is fixed template punctuation, and verbatim quoted
|
|
264
|
+
data such as commit subjects in `## Changelog` keeps its original punctuation),
|
|
265
|
+
but NOT any length/reach rules —
|
|
266
|
+
target ~900-1600 words of prose (code blocks don't count) for a substantive feature,
|
|
267
|
+
shorter for a small change. Only link
|
|
211
268
|
commits where `public: true` in the scan; omit `## Changelog` if none are.
|
|
212
269
|
|
|
213
270
|
### Step 4: Self-check before publishing
|
|
@@ -217,10 +274,15 @@ Write each draft with the **Write tool** (never a bash heredoc) to a temp dir
|
|
|
217
274
|
calls). Name it `<version>.md`. Then:
|
|
218
275
|
|
|
219
276
|
1. **Lint:** `npx -y @natjswenson/devlog lint-post '<abs-draft-path>'` — fix every
|
|
220
|
-
finding (missing sections, thin gotchas, too few distinct sources,
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
277
|
+
finding (missing sections, thin gotchas, too few distinct sources, sources listed
|
|
278
|
+
but never cited inline, untagged fences).
|
|
279
|
+
2. **Assemble-and-run check:** when the post's code is runnable without external
|
|
280
|
+
services, copy its code blocks in order into a scratch dir and execute them exactly
|
|
281
|
+
as a reader would. Anything undefined, out of order, or missing an entrypoint fails
|
|
282
|
+
the stranger test mechanically — fix the post, not just the scratch copy.
|
|
283
|
+
3. **Self-review against the how-to contract**, honestly, as a skeptical reader: walk
|
|
284
|
+
points 1-8 above plus voice adherence. Revise the draft for any point that fails.
|
|
285
|
+
4. At most **two** revision passes; then proceed with the best version and carry any
|
|
224
286
|
residual weakness into the final summary (e.g. "v0.5.0: only 2 gotchas had commit
|
|
225
287
|
evidence").
|
|
226
288
|
|
|
@@ -232,15 +294,51 @@ critique, not a rubber stamp.
|
|
|
232
294
|
Clone once, publish each entry through the CLI, push once. `targetRepo` and `branch` come
|
|
233
295
|
from validated config — still single-quote every interpolated value.
|
|
234
296
|
|
|
297
|
+
Each release also gets a cover image, composed inline in this same loop right before that
|
|
298
|
+
release's own `publish-entry` call — a self-contained HTML/CSS (or inline SVG) document,
|
|
299
|
+
rasterized locally, never sent to any external service:
|
|
300
|
+
|
|
235
301
|
```bash
|
|
236
302
|
mktemp -d # → record the absolute path, e.g. /var/folders/.../tmp.abc
|
|
237
303
|
git -C '<abs-tmp>' clone --depth=1 'https://github.com/<targetRepo>.git'
|
|
238
304
|
|
|
239
305
|
# Per release (refuses to overwrite an existing entry — on {"error": ...,
|
|
240
306
|
# "message": "... immutable ..."} skip that release and note it):
|
|
307
|
+
|
|
308
|
+
# 1. Style guide + up to 3 reference images of recently published covers.
|
|
309
|
+
npx -y @natjswenson/devlog cover-context '<key>' '<version>' \
|
|
310
|
+
--clone '<abs-tmp>/<repo-name>'
|
|
311
|
+
# On {"error": "style-guide-missing", ...}: skip cover composition for this release
|
|
312
|
+
# entirely — proceed straight to publish-entry with no --cover flag. Never block
|
|
313
|
+
# publish on a missing style guide.
|
|
314
|
+
|
|
315
|
+
# 2. Compose the cover using ONLY this release's title/tags/summary/`## Shipped` text
|
|
316
|
+
# (never the raw draft file, never `## Changelog`) plus the returned style guide and
|
|
317
|
+
# reference images. A cover that just re-renders the title in large text is a failure —
|
|
318
|
+
# find the one concrete mechanism this release is actually about (not the project name,
|
|
319
|
+
# not "a bug fix") and draw ONE custom inline-SVG illustration of it, sized as the
|
|
320
|
+
# dominant visual element of the canvas; title/kicker stay secondary. Two different
|
|
321
|
+
# releases should never produce visually similar covers — see the style guide's "one
|
|
322
|
+
# custom illustration per post" section before composing. Write the result with the
|
|
323
|
+
# Write tool to '<abs-scratch>/<key>/<version>.html' — a full document starting with
|
|
324
|
+
# `<!DOCTYPE html>`, sized `html, body { margin:0; width:1600px; height:900px; }`,
|
|
325
|
+
# referencing the bundled font only as `font-family: 'DevlogCoverFont', sans-serif`.
|
|
326
|
+
|
|
327
|
+
# 3. Rasterize it. On failure (render timeout / Chromium not installed / font missing),
|
|
328
|
+
# the .html is left in place for debugging — retry composing once with the error text
|
|
329
|
+
# fed back, or give up and proceed with no --cover flag.
|
|
330
|
+
npx -y @natjswenson/devlog render-cover '<abs-scratch>/<key>/<version>.html' \
|
|
331
|
+
--project '<key>' --slug '<version>' --out '<abs-scratch>'
|
|
332
|
+
# Show the rendered <abs-scratch>/<key>/<version>.png in this session before continuing —
|
|
333
|
+
# this interactive review IS the quality gate for the cover, the same way Step 4 is for
|
|
334
|
+
# the prose.
|
|
335
|
+
|
|
241
336
|
npx -y @natjswenson/devlog publish-entry \
|
|
242
337
|
--clone '<abs-tmp>/<repo-name>' --project '<key>' \
|
|
243
|
-
--version '<version>' --entry '<abs-draft-path>'
|
|
338
|
+
--version '<version>' --entry '<abs-draft-path>' \
|
|
339
|
+
--cover '<abs-scratch>/<key>/<version>.png'
|
|
340
|
+
# Omit --cover entirely if no cover was produced for this release (missing style guide,
|
|
341
|
+
# a render failure not worth a second attempt) — publish still proceeds normally.
|
|
244
342
|
|
|
245
343
|
git -C '<abs-tmp>/<repo-name>' add .
|
|
246
344
|
git -C '<abs-tmp>/<repo-name>' commit -m 'devlog: add release entries'
|