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