@natjswenson/devlog 0.11.0 → 0.11.2

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/CHANGELOG.md ADDED
@@ -0,0 +1,495 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@natjswenson/devlog` are documented here.
4
+
5
+ ## 0.11.2 (2026-07-28) — actually render the README on npmjs.com
6
+
7
+ - **Fixed: the npm page still showed "No README data found" after 0.11.1.** That
8
+ release put README.md, LICENSE and CHANGELOG.md into the tarball (verified by
9
+ downloading it), which fixed what `npm install` delivers — but not the website.
10
+ npm reads README.md to populate the registry manifest's `readme` field when it
11
+ builds the publish manifest, which happens BEFORE the package's own `prepack`
12
+ hook runs. So the tarball had the file and the registry record did not.
13
+ Worse than merely empty: the stored value was the literal string
14
+ `ERROR: No README data found!`, and npmjs.com falls back to reading the
15
+ tarball only when that field is *absent* (compare `zod`, which renders fine
16
+ with no packument readme at all), so the error string kept winning.
17
+ The release workflow now stages the three files into the package directory
18
+ before `npm publish` is invoked at all, rather than relying on `prepack`.
19
+ Code unchanged.
20
+
21
+ ## 0.11.1 (2026-07-28) — publish the README, LICENSE and CHANGELOG to npm
22
+
23
+ - **Fixed: the npm package shipped with no README, LICENSE or CHANGELOG.**
24
+ `package.json`'s `files` listed all three, but they live at the plugin root
25
+ (beside `.claude-plugin/`), one level above the package directory — and npm only
26
+ includes files from inside the package directory, so the entries silently matched
27
+ nothing. Every release published a tarball without them, and
28
+ `npm view @natjswenson/devlog readme` returned *"No README data found"*, leaving
29
+ the npm page blank. A `prepack` script now stages the three files into the package
30
+ directory before the tarball is built, and `postpack` removes them again so the
31
+ working tree stays clean; they are gitignored at that path so an interrupted pack
32
+ cannot leave committable strays. Code unchanged.
33
+
34
+ ## 0.11.0 (2026-07-19) — everything the six-run audit found
35
+
36
+ A three-way audit of the first six real `/devlog` runs (content graded against git
37
+ ground truth, run archaeology in both target repos, session-transcript mining) drove
38
+ this release end to end.
39
+
40
+ **Added**
41
+ - **Tombstones — entry identity is now project+version in the manifest, not a file
42
+ path.** `devlog tombstone --clone <root> --project <key> --version <v> --reason <why>`
43
+ marks an editorially moved/consolidated/deleted entry's identity as retired
44
+ (`removed: true` row; a dead row's frozen `no` is kept so numbers are never reused).
45
+ `scan` now fetches each project's `manifest.json` (directory-listing fallback for
46
+ legacy dirs) and reports retired versions as `entry-tombstoned`; `publish-entry`
47
+ refuses them. This kills the failure class that re-armed three deleted entries across
48
+ the first six runs (ghostwriter v0.8.1 twice, market-research v0.1.0 — the latter was
49
+ live-armed at audit time: the entry had been hand-moved to `personal/2026-07-17` and
50
+ the next run would have republished it).
51
+ - **`devlog sync-entry`** — the legitimate post-publish edit path: resyncs a manifest
52
+ row's title/summary/date/tags from the published entry's frontmatter (never `no`/
53
+ `version`/`file`/`cover`) and reports `coverStale`. The v0.8.1 reframe left the
54
+ manifest stale for six hours while four "rebuild" commits chased a phantom CDN issue;
55
+ this is the command that should have existed.
56
+ - **`devlog assemble-post <draft> --out <dir>`** — extracts a draft's fenced code blocks,
57
+ in order, as numbered files (`text` fences are expected output, listed but not
58
+ written). SKILL.md Step 4's assemble-and-run check is now mechanical; the audit proved
59
+ the honor-system version was skipped exactly when it mattered (two posts shipped
60
+ claiming "real output" over code that could not run).
61
+ - **Ground-truth gate in SKILL.md Step 4**: every claim about the author's own repo must
62
+ be re-verified with a git command run in-session; a specific number is publishable
63
+ only if it appears in a commit/diff/source; output may be labeled real only if the
64
+ command ran this session. One audited post had published a provably false premise.
65
+ - **`lint-post --voice`** — opt-in deterministic voice rules: `voice-em-dash` (prose em
66
+ dashes; `## Sources`/`## Changelog`/fenced code exempt per the existing carve-outs)
67
+ and `voice-banned-phrase` (the explicit bans from voice-notes.md). Replaces the ad-hoc
68
+ grep pass every run hand-rolled.
69
+ - **Cross-entry Changelog collision refusal**: `publish-entry` refuses a draft whose
70
+ `## Changelog` lists a commit an existing entry already lists (monorepo twin releases
71
+ had shipped identical Changelogs). Hashes are matched by 7-char short form across link
72
+ text and `/commit/` URLs.
73
+ - **`scan --summary`** — the plan-table view: `commitCount` instead of commit lists and
74
+ diffstats, per-reason `skippedTags` tallies. Full detail stays one
75
+ `scan --project <key>` away.
76
+ - `scan` output now includes `cliVersion` (stale-npx tripwire — three real runs hit a
77
+ stale cached install) and per-project `publishedEntries` (`version`/`title`/`tags` of
78
+ every live entry — the catalog-level topic-dedup input; two near-duplicate guides had
79
+ shipped one day apart).
80
+
81
+ **Changed**
82
+ - **`render-cover` re-renders whenever the HTML file is present**, overwriting a stale
83
+ PNG, and **no longer deletes the HTML on success**. The old PNG-exists short-circuit
84
+ silently ignored freshly edited HTML (both real retry loops burned ~6 tool calls
85
+ diagnosing it, including a guessed-at `--force` that didn't exist), and the
86
+ post-success delete broke every later Edit attempt. The no-op path now only applies
87
+ when the HTML is gone and a valid PNG exists.
88
+ - All agent-facing commands now report unknown/malformed flags as
89
+ `{"error": "bad-flag"}` JSON (exit 2) instead of dying with a raw
90
+ `ERR_PARSE_ARGS_UNKNOWN_OPTION` stack trace.
91
+ - SKILL.md: every CLI invocation pins `@latest`; Step 1 leads with `scan --summary`;
92
+ Step 3b dedupes topics against the whole published catalog and forbids inventing a
93
+ second angle for a twin release the history doesn't support; Step 3c disqualifies
94
+ content-farm sources from `minSources`; Step 5 writes a `run-state.json` for
95
+ mid-run-compaction recovery, reads at most one reference cover image, and documents
96
+ the re-render recipe; new edge cases cover `entry-tombstoned`, hand-moved entries
97
+ (tombstone them), and hand-edited entries (sync-entry).
98
+ - `mergeManifestEntries`/`backfill-covers`/the preview manifest validator skip
99
+ `removed: true` rows.
100
+ - Four new prose invariants in `skill-invariants.json` (ground-truth-gate,
101
+ no-unrun-output, tombstone-never-republish, one-commit-one-changelog).
102
+
103
+ ## 0.10.0 (2026-07-19) — publish into a subdirectory of the target repo
104
+
105
+ **Added**
106
+ - Optional `targetDir` config field (`devlog set targetDir content/devlog`, clear with
107
+ `set targetDir ''`): the subdirectory of `targetRepo` that holds the devlog content
108
+ tree. Lets the devlog publish directly into a site repo (e.g.
109
+ `natejswenson.io/content/devlog`) so the publish push itself triggers the site's
110
+ deploy — no separate rebuild commit, no second repo. Validated as a relative,
111
+ traversal-free path since it's interpolated into the publish clone path and the
112
+ `gh api` contents path.
113
+ - `scan` threads `targetDir` through the existence check
114
+ (`repos/<targetRepo>/contents/<targetDir>/<key>`) and echoes it in its JSON output,
115
+ so already-published entries are still filtered correctly when content lives in a
116
+ subfolder. Unset behaves exactly as before (repo root).
117
+ - `SKILL.md`: publish step now targets `<content-root>` (`<clone>/<targetDir>` when
118
+ set); the confirm message notes that a push to an auto-deploying site repo IS the
119
+ rebuild trigger.
120
+
121
+ **Fixed**
122
+ - `image-style/icons.md` still described the pre-PRESS dark palette for accent icons
123
+ (`#ededed` / accent yellow `#fff503`); corrected to PRESS ink/dim, never orange —
124
+ matching the style guide's accent law.
125
+
126
+ ## 0.9.0 (2026-07-17) — cover images: icon catalog + geometry-enforced hero zone
127
+
128
+ **Added**
129
+ - `image-style/icons.md`: a 20-icon catalog (agents/LLM, testing, CI/CD, git,
130
+ accessibility, debugging, CLI, config, deploy, database, API, search, auth,
131
+ monitoring, cover/image tooling, performance, parsing, caching, UI, networking) —
132
+ real inline SVG, stroke-only, for the optional kicker-area accent glyph only. Never
133
+ the hero illustration itself. Installed by `devlog init` alongside the existing
134
+ style guide/font.
135
+ - A fixed hero-zone bounding box (`x:150 y:425 width:1300 height:400`) and 25px
136
+ coordinate grid, documented in `image-style/style-guide.example.md`, with two named
137
+ composition slots: single centered hero, and two-node before/after.
138
+ - `lib/render_cover.mjs` now mechanically enforces the hero zone via a new
139
+ `checkHeroZoneOverlap()` check, run immediately before the screenshot: throws if
140
+ `#hero-zone` is missing, duplicated, positioned/sized outside a 2px tolerance of the
141
+ fixed box, or if any catalog icon's rendered rect overlaps it. This replaces a
142
+ prose-only safeguard (a regex checking one sentence still exists in `SKILL.md`) with
143
+ a real pixel-geometry check against actual rendered layout — a sibling investigation
144
+ found the prose-only check could never catch an agent composing two catalog icons
145
+ connected by a line and calling that the hero. `renderCoverImage()`'s documented
146
+ throw contract widens from three failure modes to four accordingly.
147
+ - `devlog backfill-covers list` gains an `--all` flag: lists every manifest entry
148
+ regardless of cover status (the default stays missing-cover-only). Needed because
149
+ the ~51 real entries in a typical `daily-dev-log` already have covers from a prior
150
+ batch — without `--all`, there is nothing to iterate over for a cover-quality
151
+ backfill.
152
+ - `cover-context`'s underlying `loadStyleGuide()` now also returns the icon catalog
153
+ content (`iconCatalog`, `null` if `icons.md` isn't installed) alongside the style
154
+ guide text — no new CLI flag, just a widened response.
155
+
156
+ **Changed**
157
+ - `skill-invariants.json` gains a `code` array (alongside the existing `prose` array)
158
+ for guardrails checked against arbitrary source files, not just `SKILL.md` — first
159
+ entry: `cover-catalog-hero-overlap-guard`, asserting the new geometry check stays
160
+ wired into `render_cover.mjs`.
161
+
162
+ ## 0.8.1 (2026-07-16) — cover images: illustration over typography
163
+
164
+ **Changed**
165
+ - Rewrote the bundled cover style guide (`image-style/style-guide.example.md`) after the
166
+ first real batch of covers shipped bland and repetitive: a shared text-heavy layout
167
+ with a rotating stock shape (circle/square/slash). The guide now mandates ONE custom
168
+ inline-SVG illustration per post depicting the specific mechanism the release is about
169
+ — sized as the dominant visual element — with the title demoted to a secondary line.
170
+ Explicitly forbids reusing the same shape family/motif across posts and falling back to
171
+ a generic circle/square/checkmark when stuck.
172
+ - `SKILL.md` Step 5's compose sub-step now states this mandate inline (not just "compose
173
+ using the style guide") so the failure mode can't silently regress even if the
174
+ installed style guide is later replaced with something weaker.
175
+ - New `skill-invariants.json` entry (`cover-custom-illustration`) guards the "cover that
176
+ just re-renders the title in large text is a failure" line in `SKILL.md`.
177
+
178
+ ## 0.8.0 (2026-07-16) — cover images
179
+
180
+ **Added**
181
+ - Every post now gets an auto-generated 1600x900 cover image. Claude composes a
182
+ self-contained HTML/CSS (or inline SVG) document from the post's title/tags/summary/
183
+ `## Shipped` text plus an installed style guide and up to 3 reference images of
184
+ recently published covers — then `devlog render-cover` rasterizes it locally via
185
+ headless Chromium (`playwright`), embeds a bundled font, and quantizes the PNG. No
186
+ external API, no credentials, nothing ever leaves the machine.
187
+ - New `SKILL.md` Step 5 sub-steps: `devlog cover-context` (style guide + references) →
188
+ Claude composes the markup → `devlog render-cover` → `publish-entry --cover`. A cover
189
+ render failure (timeout, Chromium not installed, missing font) never blocks publish —
190
+ the post ships with no cover, the same graceful degradation as any other failure.
191
+ - New commands for backfilling covers onto already-published posts: `devlog
192
+ backfill-covers list` (deterministic, cross-project, oldest-first, extracts only
193
+ `## Shipped`) drives an agent loop of `cover-context` + compose + `render-cover` into a
194
+ project-namespaced staging directory, reviewed via a contact sheet, then `devlog
195
+ commit-covers` publishes the approved set (with `--force` for re-covering).
196
+ - `devlog init` now installs the bundled style guide + font (`image-style/`) and checks
197
+ Chromium/font reachability, mirroring its existing SKILL.md/voice-profile install
198
+ pattern.
199
+ - `publishEntry()` gains an optional `coverImageBuffer` param; new
200
+ `addCoverToExistingEntry()` in `lib/publish_entry.mjs` for the backfill path.
201
+
202
+ ## 0.7.0 (2026-07-15) — private projects
203
+
204
+ **Added**
205
+ - New `private` project flag (`add-project --private`, or the interactive "Is this repo
206
+ private?" prompt). A private project's `remote` becomes optional, and `scanProject`'s
207
+ `isPublic` check now short-circuits to `false` unconditionally for it — regardless of
208
+ whether `remote` matches `origin` and the commit is on the published branch. Previously
209
+ those two checks alone decided "public," which is a proxy for "the commit is reachable
210
+ from a correctly-configured remote," not "the GitHub repo is public." A project with a
211
+ real, correctly configured `remote` that happens to live in a private GitHub repo would
212
+ have scanned every commit as public and generated dead `github.com/.../commit/...` links
213
+ in the post. `private` is a declared type, not auto-detected via the GitHub API — no
214
+ extra network call per run, and it stays correct even for a project with no push target
215
+ at all.
216
+ - `config`'s project listing shows `(private — no commit links)` instead of a broken
217
+ `remote: github.com/undefined` line for a private project with no `remote` set.
218
+
219
+ **Fixed**
220
+ - `validateConfig` previously required every project to have a valid `remote`
221
+ unconditionally; a private project can now omit it (still validated for shape if
222
+ supplied anyway).
223
+
224
+ ## 0.6.0 (2026-07-12) — lessons from rewriting the whole back-catalog
225
+
226
+ Every pre-0.5 entry (38 posts across devlog, ghostwriter, resume, local-fitness) was
227
+ rewritten against the 0.5.0 how-to contract, and each rewrite's friction fed a skill
228
+ refinement. What changed:
229
+
230
+ **Added (lint)**
231
+ - New `sources-inline` rule: every URL in `## Sources` must also be cited inline in the
232
+ body — the contract required it, nothing enforced it, and it was the easiest step to
233
+ silently skip. The finding suggests drop-the-source as an alternative fix so the rule
234
+ can't push citation stuffing. Fixtures updated to comply.
235
+
236
+ **Changed (SKILL.md writing contract)**
237
+ - Topics must pass a usability test: prefer the transferable technique a reader could
238
+ apply to their own project, with a tie-break rule for releases spanning several
239
+ topics; cross-project (monorepo) commits belong to one post's story only.
240
+ - New contract point 8, **fun to follow**: early runnable win, momentum between steps,
241
+ payoff visible at each stage — never forced jokes or hype.
242
+ - Reader-side verification must be REAL when cheap: run the blocks, paste actual output
243
+ (trim/normalize only with disclosure); new **assemble-and-run** self-check step
244
+ executes the post's own code blocks in order, catching phantom symbols and ordering
245
+ traps mechanically. Revised functions must be shown as complete redefinitions.
246
+ - Facts anchor at the tag (`git show '<tag>:<file>'`), teaching code may generalize;
247
+ when a commit message and its diff disagree, the diff wins.
248
+ - Gotcha mining follows the release's code FORWARD in history — a later fix to code
249
+ this release introduced is prime material when dated honestly.
250
+ - Research: quotes require verbatim re-verification (fetch tools can return
251
+ quote-shaped summaries that aren't on the page).
252
+ - Punctuation scope settled: voice rules govern prose; the Sources template separator
253
+ and verbatim commit subjects keep their punctuation. Titles are sentence case.
254
+ - Template flexibility: only Shipped/Gotchas/Sources are mandatory sections; output
255
+ blocks fence as `text`; the 900-1600 word target counts prose only.
256
+
257
+ ## 0.5.2 (2026-07-11) — fix: same-date manifest entries buried the newest post
258
+
259
+ **Fixed**
260
+ - `publish-entry` sorted the manifest by date only; several releases cut on the same
261
+ day kept insertion order, so the feed rendered them oldest-version-on-top and the
262
+ newest post landed at the bottom of the day's group (hit live with
263
+ v0.4.2/v0.5.0/v0.5.1, all dated 2026-07-11). Same-date ties now break by version,
264
+ highest first, with numeric component comparison (v0.10.0 > v0.9.0). Date still wins
265
+ overall so a backported tag sorts by its release date. Legacy rows without a
266
+ `version` keep their relative order.
267
+
268
+ ## 0.5.1 (2026-07-11) — fix: npx invocations were a silent no-op
269
+
270
+ **Fixed**
271
+ - Every `npx @natjswenson/devlog <cmd>` invocation exited 0 with no output (present
272
+ since the import-side-effect guard was added, so ≤0.5.0). npm/npx expose the binary
273
+ as a `node_modules/.bin/devlog` **symlink**; `process.argv[1]` is the symlink while
274
+ `import.meta.url` resolves to the real file, so the naive `===` in the `isMain` guard
275
+ never matched and the dispatcher never ran. Both sides are now `realpathSync`'d. A
276
+ regression test invokes the CLI through a symlink exactly like npx does. Running
277
+ `node bin/devlog.js` directly was always unaffected — which is why the test suite
278
+ and local verification missed it.
279
+
280
+ ## 0.5.0 (2026-07-11) — how-to posts with gotchas, deterministic core, agent-native config
281
+
282
+ **Changed (posts)**
283
+ - Every post is now held to an explicit **how-to contract**: the stranger test (a reader
284
+ without the author's repo can build the technique from the post alone), complete code
285
+ (no phantom fixtures — every referenced symbol is defined or explicitly stubbed),
286
+ reader-side verification (commands the reader runs, with expected output), a required
287
+ `## Gotchas` section (trap → symptom → escape, mined from real history: fix-after-feat
288
+ commits, reverts, CHANGELOG "Fixed" entries — never invented), honest scope, and no
289
+ leaked repo-specific artifacts. Generate mode runs a mandatory self-check (lint +
290
+ rubric review, max 2 revision passes) before publishing autonomously.
291
+ - `deepDive.minSources` default raised 2 → 3, and sources must be **distinct** URLs.
292
+
293
+ **Changed (architecture)**
294
+ - Release discovery, post linting, entry publication, and manifest updates moved from
295
+ ~20 hand-rolled SKILL.md bash steps into tested CLI subcommands (`scan`, `lint-post`,
296
+ `publish-entry`) backed by new `lib/` modules. Tag names and semver/range logic no
297
+ longer pass through the LLM's shell at all (spawnSync argv only), entry existence is
298
+ one GitHub API call per project instead of one per tag, and the
299
+ never-overwrite-a-published-entry guard is now code-enforced in `publish-entry`.
300
+ - SKILL.md restructured around three modes — Configure / Status / Generate — and the
301
+ generate flow prints the release plan up front before any research starts.
302
+
303
+ **Added**
304
+ - Agent-native configuration: `add-project --yes` (non-interactive, auto-detects
305
+ key/remote), `remove-project <key> --yes`, `set <field> <value>` (targetRepo, branch,
306
+ gitAuthor, githubUser, voicePath, deepDive.minSources, deepDive.topicDomains), and
307
+ `config --json`. `/devlog` now handles "add this repo", "stop tracking X", "set min
308
+ sources to 4", and `/devlog status` conversationally.
309
+ - `deepDive` config validation (minSources integer 1-10, topicDomains non-empty strings).
310
+ - Deterministic test suites for the new core (fixture git repos for scan; manifest
311
+ ordering incl. backported tags; overwrite refusal; config ops) plus a skill-contract
312
+ suite (`skill-invariants.json`) that pins SKILL.md's prose guardrails, its CLI surface,
313
+ and package.json↔CHANGELOG version agreement.
314
+ - Cost-capped eval harness under `evals/` for the non-deterministic half: a $0
315
+ deterministic layer (lint-post) plus an LLM judge scoring reproducibility, code
316
+ completeness, gotcha quality, citations, voice, and scope honesty against golden
317
+ fixtures. Mock mode runs in CI for free; live runs are hard-capped (default $0.50) and
318
+ refuse to start over budget.
319
+
320
+ ## 0.4.2 (2026-07-10) — ghostwriter voice-fallback path fix
321
+
322
+ **Fixed**
323
+ - The ghostwriter voice-fallback path (`GHOSTWRITER_VOICE_DIR` / Step 2 of voice resolution) now
324
+ points at `~/.claude/ghostwriter/voice`, matching ghostwriter's new shared home-directory
325
+ location (previously `~/.claude/skills/ghostwriter/voice`, which was never ghostwriter's real
326
+ install path). devlog's own config location (`~/.claude/skills/devlog/config.json`) is
327
+ unchanged.
328
+
329
+ ## 0.4.1 (2026-07-11) — plugin marketplace discovery fix
330
+
331
+ **Fixed**
332
+ - The skill was not discoverable when installed via the Claude Code plugin marketplace
333
+ (Claude Desktop's plugin UI showed no skills). `SKILL.md` now lives at the plugin's
334
+ documented `skills/devlog/SKILL.md` auto-discovery path instead of the plugin root.
335
+
336
+ ## 0.4.0 (2026-06-28) — researched, end-to-end implementation-guide posts
337
+
338
+ **Changed**
339
+ - `/devlog` now writes each release entry as a researched, cited, end-to-end
340
+ implementation guide rather than a narrative summary of what shipped. Step 6
341
+ derives the engineering topic(s) the work touched, researches them against
342
+ reputable outside sources (cited inline and in a `## Sources` section), and
343
+ writes a setup → build → use → verify walkthrough with multiple
344
+ copy-paste-reusable, language-tagged code blocks that together form a complete,
345
+ runnable whole (right-sized: roughly 3-6 essential blocks for a substantive
346
+ feature, fewer for a small change, never padded). A short `## Shipped` hook
347
+ still opens the post and `## Changelog` still closes it. Updated the skill
348
+ description and the Step 6 / 6a-6c guidance accordingly.
349
+
350
+ **Added**
351
+ - `deepDive` config block: `topicDomains` (default: AI, DevOps/SRE, software
352
+ engineering) and `minSources` (default 2) to steer topic selection and the
353
+ citation floor. Repo-agnostic; user-supplied values live in `config.json`.
354
+
355
+ ## 0.3.1 (2026-06-20) — fetch tags before discovering releases
356
+
357
+ **Fixed**
358
+ - `/devlog` now runs a best-effort `git fetch --tags --quiet` per project at the
359
+ start of release discovery (Step 3), before listing tags. Releases are
360
+ commonly cut by CI on the remote (a version-driven GitHub Release on green
361
+ `main`/`master`), so the tag is born on the remote; a local clone that hadn't
362
+ fetched would list only stale local tags and silently report "no new release"
363
+ for a release that was already live. The fetch is best-effort: on failure
364
+ (offline, no remote, auth prompt) it notes the failure and proceeds on local
365
+ tags rather than aborting. `--tags` takes no untrusted input and `project.path`
366
+ is validated + single-quoted per Step 0.5.
367
+
368
+ ## 0.3.0 (2026-06-18) — release-focused entries, written in your voice
369
+
370
+ **Changed (behavior)**
371
+ - `/devlog` now generates one entry **per version release** (a semver git tag) instead of
372
+ one entry per day. An entry summarizes the commits in a release's tag range
373
+ (`<prevTag>..<thisTag>`), scoped by `pathFilter` when present. The run is **idempotent**:
374
+ a release's entry is written once and never overwritten, and re-running produces nothing
375
+ until a new tag is cut. The per-day "Update — HH:MM" append mode is removed.
376
+ - Entries are keyed by version: `<project-key>/<version>.md` (e.g. `v0.2.0.md`), with a
377
+ `version` field added to the frontmatter and to each `manifest.json` entry. Entry sections
378
+ are now **What Shipped / What's Next / Commits**. The entry `date` is the tag's commit date.
379
+
380
+ **Added**
381
+ - **Voice-driven publishing.** Entries are written in the user's voice using a voice profile
382
+ resolved in this order: `config.voicePath` → `~/.claude/skills/ghostwriter/voice` (if
383
+ installed) → a bundled fallback at `~/.claude/skills/devlog/voice/`. devlog reads
384
+ `voice-profile.md` and `voice-notes.md` (overrides) — and never `algorithm.md`, since
385
+ LinkedIn reach tuning does not apply to a dev log.
386
+ - `voicePath` (top-level, optional) and `projects[].tagPrefix` (optional, default `v`) config
387
+ fields, with security validation in both `bin/devlog.js` and SKILL.md. `tagPrefix` lets each
388
+ project in a monorepo detect its own releases (e.g. `devlog-v`, `ghostwriter-v`).
389
+ - `init` prompts for the voice directory and release tag prefix, and installs the bundled
390
+ voice template. `config` shows the voice path and each project's tag pattern.
391
+ - The React example carries the optional `version` field through frontmatter parsing and
392
+ manifest validation.
393
+
394
+ **Migration note:** existing per-day `YYYY-MM-DD.md` entries are left untouched; new entries
395
+ are per-release. To detect a monorepo project's releases, set its `tagPrefix`.
396
+
397
+ ## 0.2.0 (2026-06-08) — monorepo subdirectory filtering
398
+
399
+ **Added**
400
+ - `projects[].pathFilter` config field: scope a project's commits to a repo-relative
401
+ subdirectory (e.g. `skills/devlog`). Lets several logical projects share one monorepo
402
+ `path`/`remote` while each collects only its own subtree's commits. `git log` gains a
403
+ `-- <pathFilter>` pathspec; commit links still resolve to `<remote>/commit/<hash>`.
404
+ - SKILL.md documents the field, its security validation (no leading `-`/`/`, no `..`,
405
+ single-quoted), and the multi-skill monorepo workflow.
406
+ - `bin/devlog.js` validates `pathFilter` and shows it as `scope:` in `devlog config`.
407
+
408
+ ## 0.1.9 (2026-06-05) — accessibility fix
409
+
410
+ **Accessibility**
411
+ - The drop-in React component (`examples/react/DevLogPage.jsx`) now exposes the expand/collapse entries as a proper disclosure control. Previously they were mouse-only — a bare `onClick` on `<article>` with no `role`, `tabIndex`, `aria-expanded`, or keyboard handler, so keyboard and screen-reader users could not operate the feed.
412
+ - The header carries `role="button"`, `tabIndex={0}`, `aria-expanded`, and `aria-controls` for screen-reader toggle semantics.
413
+ - `Enter`/`Space` toggle the focused entry (`preventDefault` on Space stops page scroll).
414
+ - `:focus-visible` outline makes keyboard focus visible.
415
+ - The toggle moved from the whole card to the header, so links inside an expanded entry are no longer nested in an interactive ancestor and text selection in the body works normally.
416
+ - Visuals are unchanged: padding/hover/cursor moved from `.devlog-entry` to `.devlog-header`, with the redundant content padding zeroed so spacing matches.
417
+
418
+ ## 0.1.8 (2026-05-01) — final hardening pass
419
+
420
+ Closes the four Low-Hardening findings from the second adversarial verification:
421
+
422
+ - **L-1:** `validateConfig` now bounds `projects[].label` length (≤200 chars) and rejects control characters. Label apostrophes/quotes/etc are intentionally allowed since label is React text content only — never shell-interpolated. The validator includes an explicit invariant comment to keep this guarantee load-bearing.
423
+ - **L-2:** `atomicWriteJSON` uses `wx` (exclusive create) flag, preventing symlink-attack scenarios on shared filesystems where another local user could pre-create the tmp file.
424
+ - **L-3:** `atomicWriteJSON` tmp filename now also includes `Date.now()` for additional uniqueness across rapid sequential calls.
425
+ - **L-4:** SKILL.md Step 5 explicitly instructs the LLM to treat fetched dev-log content as data, not instructions — defense against indirect prompt injection from hostile dev-log markdown.
426
+
427
+ Verification: a second 6-perspective adversarial agent against HEAD reports zero Critical/High/Medium-Active vulnerabilities remain.
428
+
429
+ ## 0.1.7 (2026-05-01) — security hardening + UX improvements
430
+
431
+ **Security**
432
+ - Tightened `SHELL_METACHARS` to additionally reject whitespace, single-quote, square brackets, equals, and percent
433
+ - Project paths now cannot start with `-` (would be parsed as flag)
434
+ - Added strict validation of the `branch` field (no leading dash, no `..` as a path component)
435
+ - Atomic `config.json` writes (write-to-tmp + rename)
436
+ - CLI no longer forwards arbitrary `VITE_*` env vars to the spawned vite — only `VITE_DEVLOG_*` plus `PATH`/`HOME`/etc.
437
+ - Added `Content-Security-Policy` meta tag to the preview app
438
+ - Explicit `urlTransform` in `react-markdown` rejects `data:`, `blob:`, `javascript:`, `vbscript:`, `file:`, and any non-http(s)/mailto scheme
439
+ - `react-markdown` invoked with `skipHtml` for explicit defense-in-depth
440
+ - `SKILL.md` instructs the LLM to single-quote every interpolated config value (defense-in-depth on top of validation)
441
+ - Production preview builds without env vars show a clear "Setup required" screen instead of attempting demo fetches that would 404
442
+ - `config.json` written with mode `0600`, `~/.claude/skills/devlog/` created with mode `0700`
443
+ - Pinned all dependencies to exact versions (no `^` ranges) to eliminate resolution drift
444
+
445
+ **UX**
446
+ - `init` now loops to register multiple projects in a single setup
447
+ - New `add-project` subcommand: `npx @natjswenson/devlog add-project` — register a project without editing config.json by hand
448
+ - New `config` subcommand: `npx @natjswenson/devlog config` — view current config with validation status
449
+ - Init detects when `gh` is authenticated as a different user than `githubUser` and warns
450
+ - Better next-step messaging after init (color, concrete commands)
451
+ - All error messages now include actionable hints (`log.hint`)
452
+
453
+ **Docs**
454
+ - New `SECURITY.md` — threat model, audit history, ruled-out attack scenarios, vulnerability reporting flow
455
+ - New `CHANGELOG.md` (this file)
456
+ - README updated with new subcommands and security guarantees section
457
+
458
+ ## 0.1.6 (2026-05-01) — initial security audit fixes
459
+
460
+ Addressed 1 Critical + 3 High findings from the first round of the 6-agent siege:
461
+ - SKILL.md now requires runtime allowlist validation of every config value before shell interpolation
462
+ - CLI switched from `execSync` with template strings to `spawnSync` with argv arrays for any user-input-bearing call
463
+ - `gh repo create` regex hardened against leading-dash flag injection
464
+ - `gitAuthor` validator now rejects shell metachars
465
+ - Schema validation added for `manifest.json`, `VITE_DEVLOG_PROJECTS`, frontmatter (allowlist + `Object.create(null)`)
466
+ - `optimizeDeps` includes for vite to fix react-markdown / react-dom CJS interop in npx layouts
467
+ - Preview vite server bound to localhost only, CORS disabled
468
+
469
+ ## 0.1.5 (2026-05-01)
470
+
471
+ - Corrected live-site URL in README (`natejswenson.com` not `.io`)
472
+
473
+ ## 0.1.4 (2026-05-01)
474
+
475
+ - README troubleshooting section, npm + license badges
476
+ - SKILL.md uses `<config.branch || 'main'>` consistently in push/URL output
477
+
478
+ ## 0.1.3 (2026-05-01)
479
+
480
+ - Expanded `optimizeDeps.include` to cover react/react-dom for npx-installed layouts
481
+
482
+ ## 0.1.2 (2026-05-01)
483
+
484
+ - First `optimizeDeps` fix for `style-to-js` CJS/ESM interop (react-markdown rendering)
485
+
486
+ ## 0.1.1 (2026-05-01)
487
+
488
+ - `projects[].label` and `branch` config fields (optional, with safe defaults)
489
+
490
+ ## 0.1.0 (2026-05-01)
491
+
492
+ - Initial release
493
+ - CLI: `init`, `preview`
494
+ - React drop-in components: `DevLogPage`, `useDevLogEntries`
495
+ - Standalone deployable Vite preview app with snarky demo mode
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nate Swenson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.