@natjswenson/devlog 0.3.1 → 0.4.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.
Files changed (5) hide show
  1. package/SKILL.md +123 -23
  2. package/package.json +2 -2
  3. package/CHANGELOG.md +0 -145
  4. package/LICENSE +0 -21
  5. package/README.md +0 -240
package/SKILL.md CHANGED
@@ -1,14 +1,20 @@
1
1
  ---
2
2
  name: devlog
3
- description: Generate a dev log entry for each new version release from git tags, written in your own voice, and publish to GitHub
3
+ description: Generate a researched, cited blog post for each new version release from git tags, written in your own voice, and publish to GitHub
4
4
  user_invocable: true
5
5
  ---
6
6
 
7
- # /devlog — Release Dev Log Generator
7
+ # /devlog — Release Blog-Post Generator
8
8
 
9
- You are generating a dev log entry **for each new version release** (a semver git tag) in
10
- the user's projects, writing each entry **in the user's own voice**, and publishing them to
11
- a GitHub repo configured in `~/.claude/skills/devlog/config.json`.
9
+ You are generating a **blog post for each new version release** (a semver git tag) in the
10
+ user's projects, writing each in **the user's own voice**, and publishing them to a GitHub
11
+ repo configured in `~/.claude/skills/devlog/config.json`.
12
+
13
+ Each post is more than a changelog: a short "what shipped" hook, then a **deeply researched,
14
+ end-to-end implementation guide** for the engineering topic(s) the work touched — how to build
15
+ and use the technique, grounded in BOTH the user's actual work AND reputable, cited outside
16
+ sources, with multiple copy-paste-reusable code blocks that form a complete, runnable whole
17
+ (see Step 6).
12
18
 
13
19
  Usage: `/devlog` (all configured projects) or `/devlog <project-key>` (single project)
14
20
 
@@ -55,6 +61,11 @@ Optional fields:
55
61
  (e.g. `devlog-v` for tags like `devlog-v0.2.0`). Defaults to `v` (matching tags like
56
62
  `v1.4.0`). In a monorepo, each project sets its own prefix so its releases are detected
57
63
  independently.
64
+ - `deepDive` — optional object controlling the researched deep-dive (Step 6). Repo-agnostic;
65
+ all values have sensible defaults:
66
+ - `topicDomains` — array of domains the deep dive may explore. Default
67
+ `["AI", "DevOps/SRE", "software engineering"]`.
68
+ - `minSources` — minimum reputable external sources to cite per post. Default `2`.
58
69
 
59
70
  ## Step 0: Load and validate config
60
71
 
@@ -266,42 +277,131 @@ git -C '<project.path>' branch --contains '<hash>' -r 2>/dev/null | grep -q 'ori
266
277
  - If the remote URL matches `<project.remote>` (i.e. `github.com/<project.remote>` or the SSH equivalent) and the commit is on the published branch, it's a public commit — link it using `https://github.com/<project.remote>/commit/<hash>`.
267
278
  - Otherwise, describe the change without linking.
268
279
 
269
- ## Step 6: Generate the entry (in the user's voice)
280
+ ## Step 6: Research and write the blog post (in the user's voice)
281
+
282
+ Each entry is a **proper blog post**, not a changelog: a short "what shipped" hook, then a
283
+ **researched deep dive** into the engineering topic(s) the work touched. The release is the
284
+ springboard; the teaching is the point. Every post must be grounded in BOTH the user's actual
285
+ work AND **reputable outside sources, cited**.
286
+
287
+ ### Step 6a: Derive the deep-dive topic(s)
288
+
289
+ From the release's real changes (Step 4), identify the **one** substantive topic the work
290
+ touched (occasionally several, only when the work genuinely spans them) in the configured
291
+ `deepDive.topicDomains` (default: AI, DevOps/SRE, and software engineering). The topic is the
292
+ general concept *behind* what shipped, e.g. shipping a `feature→dev→main` flow → branching
293
+ strategy and release engineering. Never pad with topics the work didn't touch.
294
+
295
+ ### Step 6b: Research — deeply, from reputable sources
296
+
297
+ For each topic, **research before writing**. Use web search/fetch to gather authoritative
298
+ sources and concrete, citable facts. Requirements:
299
+ - **Prefer primary/authoritative sources:** official docs and release notes, standards bodies
300
+ (e.g. semver.org), primary research (e.g. DORA / *Accelerate*), and well-regarded
301
+ engineering writing (e.g. Martin Fowler / Thoughtworks, Atlassian, the project's own docs).
302
+ Avoid SEO content farms and low-signal blog spam.
303
+ - **At least `deepDive.minSources` reputable sources** (default 2; aim for 3+ on a meaty topic).
304
+ - **Every specific external claim must be backed by a source** — version numbers, behaviors,
305
+ research findings, definitions, statistics. If you can't source a specific claim, don't make
306
+ it. Verify facts against the source rather than recalling them.
307
+ - Keep a working list of `(claim, url)` pairs to cite in the post.
308
+
309
+ ### Step 6c: Write the post
270
310
 
271
- For each new release, generate a markdown entry with this structure, writing the prose to
272
- match the voice profile resolved in Step 2 (its openers, rhythm, vocabulary, never-do):
311
+ Structure (write the prose to match the voice profile resolved in Step 2 openers, rhythm,
312
+ vocabulary, never-do):
273
313
 
274
314
  ```markdown
275
315
  ---
276
- title: "<concise title for this release>"
316
+ title: "<essay-style title; NOT 'release vX.Y.Z'>"
277
317
  date: YYYY-MM-DD
278
318
  project: <project.key>
279
319
  version: <version label, e.g. v0.2.0>
280
- summary: "<1-2 sentence summary of what shipped>"
320
+ tags: [<2-5 lowercase topic tags, e.g. git, ci-cd, release-engineering>]
321
+ summary: "<1-2 sentence hook that frames the deep dive, not just what shipped>"
281
322
  ---
282
323
 
283
- ## What Shipped
324
+ ## Shipped
325
+
326
+ <2-4 sentences: what this release actually delivered, plainly. End by pivoting to the topic
327
+ the rest of the post explores. This is the only purely-changelog part.>
328
+
329
+ ## <Descriptive section heading for the deep dive / walkthrough>
330
+
331
+ <The deep dive is an END-TO-END IMPLEMENTATION GUIDE, not a single illustrative snippet. A
332
+ developer should be able to read this post and actually BUILD and USE the thing it's about.
333
+ Weave together THREE threads throughout: (1) what the user actually did (grounded, first-person,
334
+ no fabrication), (2) the general concept backed by the researched sources, cited inline as
335
+ markdown links, and (3) the user's earned take/lesson.
336
+
337
+ Cover the full path, in order, using descriptive section headings (split across multiple `##`
338
+ sections — do not cram the whole build into one):
339
+
340
+ - **Setup / prerequisites** — what's needed before the core code: dependencies, config, the
341
+ relevant data model, types, or interfaces. Include a code block whenever it has real content
342
+ (install command, schema, config file).
343
+ - **Build it, step by step** — the core implementation broken into ordered steps, EACH with its
344
+ own language-tagged code block, that together form a COMPLETE, coherent, runnable whole, not
345
+ one isolated centerpiece function. Show the wiring between the pieces (how they call each
346
+ other), not just the most interesting line.
347
+ - **Use it** — how to actually invoke or run the result, with a code block showing the call site
348
+ and a realistic example of its output or effect.
349
+ - **Verify it / edge cases** — how to confirm it works (a test, an assertion, or what to check),
350
+ plus the one or two real failure modes worth calling out.
351
+
352
+ This is a blog for DEVELOPERS: every code block must be real, correct, idiomatic, language-tagged,
353
+ and copy-paste-reusable, and the blocks must be collectively complete enough to reproduce the
354
+ LOAD-BEARING path of the feature. Aim for the essential blocks (roughly 3-6 for a substantive
355
+ feature, fewer for a small change) — show the pieces that carry the idea and the wiring between
356
+ them, not every helper, import, or obvious glue line. Favor one clear block per step over many
357
+ tiny ones or one giant dump; if a block isn't teaching something, cut it. A clean, general
358
+ version of the concept is the goal; it need not be the user's exact source. Use inline `code`
359
+ for identifiers.>
360
+
361
+ ## <Next build/use section — continue the walkthrough>
284
362
 
285
- <Narrative paragraphs about what this version delivers. Focus on WHAT changed and WHY it
286
- matters to someone using or following the project, not raw commit messages. Group related
287
- commits into the handful of changes that actually matter. Write in the user's voice per the
288
- resolved voice profile.>
363
+ <...>
289
364
 
290
- ## What's Next
365
+ ## Sources
291
366
 
292
- <Brief 1-2 sentence forward-looking note based on the trajectory of the work.>
367
+ - [<source title>](<url>) <one phrase on what it supports>
368
+ - [<source title>](<url>) — <...>
293
369
 
294
- ## Commits
370
+ ## Changelog
295
371
 
296
372
  - <commit message> ([short-hash](https://github.com/<project.remote>/commit/full-hash))
297
373
  ```
298
374
 
299
375
  **Important rules for content generation:**
300
- - "What Shipped" is a NARRATIVE release note, not a commit list. Describe the changes that matter, grouped, with their impact.
301
- - Match the **voice profile** for tone and phrasing; let `voice-notes.md` override it. Do NOT apply any LinkedIn reach rules — this is a dev log.
302
- - Only include the "Commits" section's links for commits on the published branch of a public repo.
303
- - "What's Next" should be a reasonable inference from the release's trajectory never a fabricated roadmap.
304
- - **Never invent** metrics, motivations, or outcomes the commits don't support.
376
+ - **Separate fact from concept.** What the user *did* comes only from the commits/diff never
377
+ invent metrics, motivations, decisions, or outcomes the work doesn't support. What the topic
378
+ *is* comes from the cited sources. Keep the two clearly distinguishable to the reader.
379
+ - **No unsourced external claims.** Any specific fact about the wider world (a version, a
380
+ behavior, a study, a definition) needs a `## Sources` citation. General reasoning and the
381
+ user's own opinions don't need a citation, but must be clearly the user's view.
382
+ - **Match the voice profile + `voice-notes.md`** for tone and phrasing. Apply the voice's
383
+ authenticity, anti-AI-tell, and punctuation rules (e.g. no em dashes if the profile bans
384
+ them; no rhetorical fragment-lists; no clever-symmetry "payoff" closer; end on the last real
385
+ point). Do **NOT** apply the profile's LinkedIn length/reach rules — a blog implementation
386
+ guide needs room (target ~900–1600 words for a substantive feature; shorter for a genuinely
387
+ small change; as long as a complete, honest walkthrough requires, never padded).
388
+ - **Accessible but substantive:** a curious non-expert can follow the entry, an experienced
389
+ engineer still learns something non-obvious.
390
+ - **Write it as an END-TO-END implementation guide, not a snippet showcase.** The post must
391
+ teach a developer how to BUILD and USE the technique, not just glimpse it. Walk the full path:
392
+ setup/prerequisites → the implementation built up step by step across MULTIPLE code blocks
393
+ that form a complete, runnable whole → how to invoke/use it → how to verify it (and the key
394
+ failure modes). A reader should be able to reproduce the feature from the post alone. Every
395
+ code block is real, correct, idiomatic, language-tagged, and copy-paste-reusable; show the
396
+ load-bearing pieces and how they wire together, but keep it lean (roughly 3-6 blocks for a
397
+ substantive feature) — don't block every helper, import, or obvious glue line, and cut any
398
+ block that isn't teaching something. The code need NOT be the user's exact source (a clean, general version
399
+ of the concept is fine), but never claim illustrative code is verbatim production source and
400
+ never put fabricated metrics/results in it. **Right-size honestly:** a genuinely small change
401
+ gets a proportionally shorter guide that still shows the full build-and-use path; never pad a
402
+ thin change to look bigger, and never shrink a real feature to a single teaser block.
403
+ - Only include the `## Changelog` links for commits on the published branch of a public repo
404
+ (per Step 5); omit the section if there are none.
305
405
 
306
406
  ## Step 7: Push to GitHub
307
407
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@natjswenson/devlog",
3
- "version": "0.3.1",
4
- "description": "Release dev log generator Claude Code skill + preview app for publishing version-release dev logs, written in your voice, to your site",
3
+ "version": "0.4.1",
4
+ "description": "Release dev log generator \u2014 Claude Code skill + preview app for publishing version-release dev logs, written in your voice, to your site",
5
5
  "license": "MIT",
6
6
  "author": "Nate Swenson",
7
7
  "homepage": "https://github.com/natejswenson/devlog",
package/CHANGELOG.md DELETED
@@ -1,145 +0,0 @@
1
- # Changelog
2
-
3
- All notable changes to `@natjswenson/devlog` are documented here.
4
-
5
- ## 0.3.1 (2026-06-20) — fetch tags before discovering releases
6
-
7
- **Fixed**
8
- - `/devlog` now runs a best-effort `git fetch --tags --quiet` per project at the
9
- start of release discovery (Step 3), before listing tags. Releases are
10
- commonly cut by CI on the remote (a version-driven GitHub Release on green
11
- `main`/`master`), so the tag is born on the remote; a local clone that hadn't
12
- fetched would list only stale local tags and silently report "no new release"
13
- for a release that was already live. The fetch is best-effort: on failure
14
- (offline, no remote, auth prompt) it notes the failure and proceeds on local
15
- tags rather than aborting. `--tags` takes no untrusted input and `project.path`
16
- is validated + single-quoted per Step 0.5.
17
-
18
- ## 0.3.0 (2026-06-18) — release-focused entries, written in your voice
19
-
20
- **Changed (behavior)**
21
- - `/devlog` now generates one entry **per version release** (a semver git tag) instead of
22
- one entry per day. An entry summarizes the commits in a release's tag range
23
- (`<prevTag>..<thisTag>`), scoped by `pathFilter` when present. The run is **idempotent**:
24
- a release's entry is written once and never overwritten, and re-running produces nothing
25
- until a new tag is cut. The per-day "Update — HH:MM" append mode is removed.
26
- - Entries are keyed by version: `<project-key>/<version>.md` (e.g. `v0.2.0.md`), with a
27
- `version` field added to the frontmatter and to each `manifest.json` entry. Entry sections
28
- are now **What Shipped / What's Next / Commits**. The entry `date` is the tag's commit date.
29
-
30
- **Added**
31
- - **Voice-driven publishing.** Entries are written in the user's voice using a voice profile
32
- resolved in this order: `config.voicePath` → `~/.claude/skills/ghostwriter/voice` (if
33
- installed) → a bundled fallback at `~/.claude/skills/devlog/voice/`. devlog reads
34
- `voice-profile.md` and `voice-notes.md` (overrides) — and never `algorithm.md`, since
35
- LinkedIn reach tuning does not apply to a dev log.
36
- - `voicePath` (top-level, optional) and `projects[].tagPrefix` (optional, default `v`) config
37
- fields, with security validation in both `bin/devlog.js` and SKILL.md. `tagPrefix` lets each
38
- project in a monorepo detect its own releases (e.g. `devlog-v`, `ghostwriter-v`).
39
- - `init` prompts for the voice directory and release tag prefix, and installs the bundled
40
- voice template. `config` shows the voice path and each project's tag pattern.
41
- - The React example carries the optional `version` field through frontmatter parsing and
42
- manifest validation.
43
-
44
- **Migration note:** existing per-day `YYYY-MM-DD.md` entries are left untouched; new entries
45
- are per-release. To detect a monorepo project's releases, set its `tagPrefix`.
46
-
47
- ## 0.2.0 (2026-06-08) — monorepo subdirectory filtering
48
-
49
- **Added**
50
- - `projects[].pathFilter` config field: scope a project's commits to a repo-relative
51
- subdirectory (e.g. `skills/devlog`). Lets several logical projects share one monorepo
52
- `path`/`remote` while each collects only its own subtree's commits. `git log` gains a
53
- `-- <pathFilter>` pathspec; commit links still resolve to `<remote>/commit/<hash>`.
54
- - SKILL.md documents the field, its security validation (no leading `-`/`/`, no `..`,
55
- single-quoted), and the multi-skill monorepo workflow.
56
- - `bin/devlog.js` validates `pathFilter` and shows it as `scope:` in `devlog config`.
57
-
58
- ## 0.1.9 (2026-06-05) — accessibility fix
59
-
60
- **Accessibility**
61
- - 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.
62
- - The header carries `role="button"`, `tabIndex={0}`, `aria-expanded`, and `aria-controls` for screen-reader toggle semantics.
63
- - `Enter`/`Space` toggle the focused entry (`preventDefault` on Space stops page scroll).
64
- - `:focus-visible` outline makes keyboard focus visible.
65
- - 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.
66
- - Visuals are unchanged: padding/hover/cursor moved from `.devlog-entry` to `.devlog-header`, with the redundant content padding zeroed so spacing matches.
67
-
68
- ## 0.1.8 (2026-05-01) — final hardening pass
69
-
70
- Closes the four Low-Hardening findings from the second adversarial verification:
71
-
72
- - **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.
73
- - **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.
74
- - **L-3:** `atomicWriteJSON` tmp filename now also includes `Date.now()` for additional uniqueness across rapid sequential calls.
75
- - **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.
76
-
77
- Verification: a second 6-perspective adversarial agent against HEAD reports zero Critical/High/Medium-Active vulnerabilities remain.
78
-
79
- ## 0.1.7 (2026-05-01) — security hardening + UX improvements
80
-
81
- **Security**
82
- - Tightened `SHELL_METACHARS` to additionally reject whitespace, single-quote, square brackets, equals, and percent
83
- - Project paths now cannot start with `-` (would be parsed as flag)
84
- - Added strict validation of the `branch` field (no leading dash, no `..` as a path component)
85
- - Atomic `config.json` writes (write-to-tmp + rename)
86
- - CLI no longer forwards arbitrary `VITE_*` env vars to the spawned vite — only `VITE_DEVLOG_*` plus `PATH`/`HOME`/etc.
87
- - Added `Content-Security-Policy` meta tag to the preview app
88
- - Explicit `urlTransform` in `react-markdown` rejects `data:`, `blob:`, `javascript:`, `vbscript:`, `file:`, and any non-http(s)/mailto scheme
89
- - `react-markdown` invoked with `skipHtml` for explicit defense-in-depth
90
- - `SKILL.md` instructs the LLM to single-quote every interpolated config value (defense-in-depth on top of validation)
91
- - Production preview builds without env vars show a clear "Setup required" screen instead of attempting demo fetches that would 404
92
- - `config.json` written with mode `0600`, `~/.claude/skills/devlog/` created with mode `0700`
93
- - Pinned all dependencies to exact versions (no `^` ranges) to eliminate resolution drift
94
-
95
- **UX**
96
- - `init` now loops to register multiple projects in a single setup
97
- - New `add-project` subcommand: `npx @natjswenson/devlog add-project` — register a project without editing config.json by hand
98
- - New `config` subcommand: `npx @natjswenson/devlog config` — view current config with validation status
99
- - Init detects when `gh` is authenticated as a different user than `githubUser` and warns
100
- - Better next-step messaging after init (color, concrete commands)
101
- - All error messages now include actionable hints (`log.hint`)
102
-
103
- **Docs**
104
- - New `SECURITY.md` — threat model, audit history, ruled-out attack scenarios, vulnerability reporting flow
105
- - New `CHANGELOG.md` (this file)
106
- - README updated with new subcommands and security guarantees section
107
-
108
- ## 0.1.6 (2026-05-01) — initial security audit fixes
109
-
110
- Addressed 1 Critical + 3 High findings from the first round of the 6-agent siege:
111
- - SKILL.md now requires runtime allowlist validation of every config value before shell interpolation
112
- - CLI switched from `execSync` with template strings to `spawnSync` with argv arrays for any user-input-bearing call
113
- - `gh repo create` regex hardened against leading-dash flag injection
114
- - `gitAuthor` validator now rejects shell metachars
115
- - Schema validation added for `manifest.json`, `VITE_DEVLOG_PROJECTS`, frontmatter (allowlist + `Object.create(null)`)
116
- - `optimizeDeps` includes for vite to fix react-markdown / react-dom CJS interop in npx layouts
117
- - Preview vite server bound to localhost only, CORS disabled
118
-
119
- ## 0.1.5 (2026-05-01)
120
-
121
- - Corrected live-site URL in README (`natejswenson.com` not `.io`)
122
-
123
- ## 0.1.4 (2026-05-01)
124
-
125
- - README troubleshooting section, npm + license badges
126
- - SKILL.md uses `<config.branch || 'main'>` consistently in push/URL output
127
-
128
- ## 0.1.3 (2026-05-01)
129
-
130
- - Expanded `optimizeDeps.include` to cover react/react-dom for npx-installed layouts
131
-
132
- ## 0.1.2 (2026-05-01)
133
-
134
- - First `optimizeDeps` fix for `style-to-js` CJS/ESM interop (react-markdown rendering)
135
-
136
- ## 0.1.1 (2026-05-01)
137
-
138
- - `projects[].label` and `branch` config fields (optional, with safe defaults)
139
-
140
- ## 0.1.0 (2026-05-01)
141
-
142
- - Initial release
143
- - CLI: `init`, `preview`
144
- - React drop-in components: `DevLogPage`, `useDevLogEntries`
145
- - Standalone deployable Vite preview app with snarky demo mode
package/LICENSE DELETED
@@ -1,21 +0,0 @@
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.
package/README.md DELETED
@@ -1,240 +0,0 @@
1
- # devlog
2
-
3
- [![npm](https://img.shields.io/npm/v/@natjswenson/devlog?color=blue)](https://www.npmjs.com/package/@natjswenson/devlog)
4
- [![license](https://img.shields.io/npm/l/@natjswenson/devlog)](./LICENSE)
5
- [![security](https://img.shields.io/badge/security-audited-green)](./SECURITY.md)
6
- [![vulnerabilities](https://img.shields.io/badge/npm%20audit-0%20issues-brightgreen)](#security)
7
-
8
- A Claude Code skill that turns each version release (a git tag) into a published dev log entry, written in your own voice — and a React example for displaying it on your site.
9
-
10
- > **Build in public, by release.** Tag a release like you always do. Run `/devlog`. Each new version shows up on your site as a narrative entry — in your voice — not raw commit messages.
11
-
12
- ## Live example
13
-
14
- The skill is in production at [natejswenson.com/devlog](https://natejswenson.com/devlog), publishing to [github.com/natejswenson/daily-dev-log](https://github.com/natejswenson/daily-dev-log). What you see on that page is exactly what `npx @natjswenson/devlog preview` renders for you locally.
15
-
16
- ## How it works
17
-
18
- 1. **You ship a release** — tag it (e.g. `git tag v0.3.0`), like you already do.
19
- 2. **Run `/devlog` in Claude Code.** The skill finds tags that don't yet have an entry, summarizes each release's changes into a narrative markdown entry written in your voice, and pushes it to your dev-log GitHub repo. It's idempotent — re-running does nothing until you cut a new release.
20
- 3. **Your site fetches it.** Static `manifest.json` + per-release markdown files served from `raw.githubusercontent.com` — no backend needed.
21
-
22
- **In your voice.** Entries are written using a voice profile, resolved in this order: your `config.voicePath` → [ghostwriter](../ghostwriter)'s `voice/` dir if installed → a bundled default. devlog reads `voice-profile.md` (and `voice-notes.md` overrides) — never ghostwriter's `algorithm.md`, since LinkedIn reach tuning doesn't apply to a dev log.
23
-
24
- ## Quick start
25
-
26
- ```sh
27
- npx @natjswenson/devlog init
28
- ```
29
-
30
- That command:
31
- - Creates `<your-username>/daily-dev-log` on GitHub (or uses an existing one)
32
- - Installs the skill at `~/.claude/skills/devlog/`
33
- - Writes `~/.claude/skills/devlog/config.json` with your answers
34
- - Lets you register one or more projects in a single run
35
-
36
- Then:
37
-
38
- ```sh
39
- npx @natjswenson/devlog preview
40
- ```
41
-
42
- to see your dev log rendered locally at `http://localhost:5173`.
43
-
44
- ## Prerequisites
45
-
46
- - **Node 18+** — for the CLI and preview app
47
- - **GitHub CLI** (`gh`), authenticated with `gh auth login` — used to create your dev-log repo and push entries
48
- - **Claude Code** — to run the `/devlog` skill
49
-
50
- ## Commands
51
-
52
- | Command | What it does |
53
- |---|---|
54
- | `npx @natjswenson/devlog init` | One-time setup: create dev-log repo, install skill, write config |
55
- | `npx @natjswenson/devlog add-project` | Register an additional project without editing config.json by hand |
56
- | `npx @natjswenson/devlog config` | Show your current config with validation status |
57
- | `npx @natjswenson/devlog preview` | Run a local preview at `http://localhost:5173` |
58
- | `npx @natjswenson/devlog --help` | Usage |
59
- | `npx @natjswenson/devlog --version` | Version |
60
-
61
- > **Tip:** run from any directory *outside* a clone of this repo. Running inside the repo causes a `package.json` name collision and `npx` fails with `command not found`.
62
-
63
- ## What you end up with
64
-
65
- ```
66
- ~/.claude/skills/devlog/
67
- ├── SKILL.md # The /devlog slash-command instructions
68
- ├── config.json # Your settings (mode 0600)
69
- └── voice/ # Bundled fallback voice profile (last resort)
70
- ├── voice-profile.md
71
- └── voice-notes.md
72
-
73
- github.com/<you>/daily-dev-log/ # Created by init, populated by /devlog
74
- ├── myproject/
75
- │ ├── manifest.json
76
- │ ├── v0.3.0.md
77
- │ ├── v0.2.0.md
78
- │ └── ...
79
- └── ...
80
- ```
81
-
82
- ## Manual setup (if you prefer)
83
-
84
- ```sh
85
- gh repo create <you>/daily-dev-log --public --add-readme
86
- mkdir -p ~/.claude/skills/devlog/voice
87
- curl -o ~/.claude/skills/devlog/SKILL.md https://raw.githubusercontent.com/natejswenson/devlog/main/SKILL.md
88
- # Optional fallback voice profile (used when voicePath and ghostwriter are both absent):
89
- curl -o ~/.claude/skills/devlog/voice/voice-profile.md https://raw.githubusercontent.com/natejswenson/devlog/main/voice/voice-profile.example.md
90
- curl -o ~/.claude/skills/devlog/voice/voice-notes.md https://raw.githubusercontent.com/natejswenson/devlog/main/voice/voice-notes.example.md
91
- # Then copy config.example.json → ~/.claude/skills/devlog/config.json and fill it in
92
- ```
93
-
94
- ## Add to your site
95
-
96
- ### React (drop-in)
97
-
98
- ```sh
99
- cp -r examples/react/ your-site/src/devlog/
100
- ```
101
-
102
- Edit `your-site/src/devlog/devlog-config.js` to point at your repo, then:
103
-
104
- ```jsx
105
- import DevLogPage from './devlog/DevLogPage.jsx';
106
-
107
- <DevLogPage project="myproject" />
108
- ```
109
-
110
- Full instructions: [`examples/react/README.md`](./examples/react/README.md).
111
-
112
- ### No site yet?
113
-
114
- The `preview/` directory is a complete deployable Vite app. Set `VITE_DEVLOG_OWNER` / `VITE_DEVLOG_REPO` / `VITE_DEVLOG_PROJECTS` env vars on Vercel, Netlify, or Cloudflare Pages, build with `vite build`, deploy `dist/`. See [`preview/README.md`](./preview/README.md).
115
-
116
- ### Other stacks (Next, Astro, plain HTML)
117
-
118
- It's static JSON and Markdown on GitHub. Build whatever UI you want — see the **Data contract** below.
119
-
120
- ## Data contract
121
-
122
- The dev-log repo has this layout, all served as raw files from `https://raw.githubusercontent.com/<owner>/<repo>/<branch>/`:
123
-
124
- ```
125
- <repo>/
126
- └── <project-key>/
127
- ├── manifest.json # Index of all entries (newest first)
128
- ├── v0.3.0.md # One entry per release (named by version)
129
- ├── v0.2.0.md
130
- └── ...
131
- ```
132
-
133
- **`manifest.json`:**
134
- ```json
135
- {
136
- "entries": [
137
- { "date": "2026-06-08", "file": "v0.2.0.md", "title": "...", "summary": "...", "version": "v0.2.0" }
138
- ]
139
- }
140
- ```
141
-
142
- Strict validation rules (entries that don't match are silently dropped by the React example):
143
- - `date` matches `YYYY-MM-DD` (the release/tag date)
144
- - `file` matches `^[a-zA-Z0-9._-]+\.md$`
145
- - `title` and `summary` are non-empty strings
146
- - `version` (optional) matches `^[a-zA-Z0-9._-]+$`
147
-
148
- **Entry markdown:**
149
-
150
- ```markdown
151
- ---
152
- title: "Concise release summary"
153
- date: 2026-06-08
154
- project: myproject
155
- version: v0.2.0
156
- summary: "1-2 sentence summary"
157
- ---
158
-
159
- ## What Shipped
160
- Narrative paragraphs, written in your voice.
161
-
162
- ## What's Next
163
- Forward-looking note.
164
-
165
- ## Commits
166
- - commit message ([abc1234](https://github.com/.../commit/abc1234567...))
167
- ```
168
-
169
- That's the entire contract.
170
-
171
- ## Configuration reference
172
-
173
- `~/.claude/skills/devlog/config.json`:
174
-
175
- | Field | Type | Description |
176
- |---|---|---|
177
- | `targetRepo` | `"<owner>/<repo>"` | Repo where dev log entries are published. Must match `^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*$`. |
178
- | `branch` | string (optional) | Branch in the dev-log repo. Defaults to `main`. Must not contain `..` or start with `-`. |
179
- | `gitAuthor` | string | Your name. Retained for backward compatibility; it is **not** currently rendered on entries (the author filter was removed, and release notes summarize all commits in a tag range). Still **required** by config validation — it must be present and non-empty (don't drop it). Whitespace OK; no shell metacharacters. |
180
- | `githubUser` | string | Your GitHub username. |
181
- | `voicePath` | string (optional) | Directory holding `voice-profile.md` (and optionally `voice-notes.md`) used to write entries in your voice. A leading `~` is expanded. If unset, devlog uses ghostwriter's `voice/` if installed, else the bundled default. Read only — never shell-interpolated. |
182
- | `projects` | array | One entry per project you want dev logs for. |
183
- | `projects[].key` | string | Subdirectory name in the dev-log repo. Strict token: `^[a-z0-9][a-z0-9._-]*$`, no `..`. |
184
- | `projects[].label` | string (optional) | Display name for the tab. Defaults to `key`. |
185
- | `projects[].path` | string | Local filesystem path to the project. Whitespace OK. |
186
- | `projects[].remote` | `"<owner>/<repo>"` | The project's GitHub remote. Used to mark public commits and link them. |
187
- | `projects[].pathFilter` | string (optional) | Repo-relative subdir scoping this project's commits in a monorepo (e.g. `skills/devlog`). |
188
- | `projects[].tagPrefix` | string (optional) | Prefix of the git tags that mark this project's releases (e.g. `devlog-v`). Defaults to `v`. Used in `git tag --list '<tagPrefix>*'`. |
189
-
190
- See [`config.example.json`](./config.example.json) for a complete template, or run `npx @natjswenson/devlog config` to inspect your current config with validation.
191
-
192
- ## Security
193
-
194
- The package is designed to be safe to install on a developer's machine and have ambient gh/git credentials. See [SECURITY.md](./SECURITY.md) for the full threat model and audit history.
195
-
196
- **At a glance:**
197
- - ✓ All shell calls in the CLI use `spawnSync` with argv arrays (no shell, no injection surface)
198
- - ✓ The skill validates every config field against an allowlist before interpolating into shell commands; instructs the LLM to single-quote interpolated values
199
- - ✓ Markdown rendering uses `react-markdown` with `skipHtml` and an allowlist `urlTransform` (only http(s)/mailto allowed; data:, javascript:, vbscript:, file:, blob: all neutralized)
200
- - ✓ Frontmatter parser uses `Object.create(null)` + key allowlist (no prototype pollution)
201
- - ✓ All external JSON is schema-validated before use (manifest, env-var projects array)
202
- - ✓ Vite dev server bound to `localhost`, CORS off
203
- - ✓ Demo-mode `window.fetch` override gated to dev builds only
204
- - ✓ `config.json` written atomically (tmp + rename), mode 0600
205
- - ✓ All dependencies pinned to exact versions
206
- - ✓ `npm audit`: 0 known vulnerabilities
207
-
208
- **To report a vulnerability:** open a [GitHub security advisory](https://github.com/natejswenson/devlog/security/advisories/new). Do not open a public issue.
209
-
210
- ## Customization
211
-
212
- - **Tweak the entry template:** edit `~/.claude/skills/devlog/SKILL.md` (Step 6 — generate the entry).
213
- - **Tweak your voice:** edit the `voice-profile.md` / `voice-notes.md` in your `voicePath` (or `~/.claude/skills/devlog/voice/`).
214
- - **Tweak the UI:** override the `--devlog-*` CSS variables in `examples/react/DevLogPage.css` to match your theme.
215
- - **Add more projects:** `npx @natjswenson/devlog add-project` (no manual JSON editing required).
216
-
217
- ## Troubleshooting
218
-
219
- **`sh: devlog: command not found` when running `npx`:** you're inside a checkout of this repo. The local `package.json` name collides with the published one. Run `npx` from somewhere else, e.g. `cd ~ && npx @natjswenson/devlog ...`.
220
-
221
- **Init prompts show `78` after placeholder text:** that's an artifact of how some output capture tools render `\x1b7`/`\x1b8` (cursor save/restore) escape sequences. In a real interactive terminal, you won't see it.
222
-
223
- **Preview is blank / no tabs / no entries:** check the browser console. If you see CJS interop errors related to `react-dom/client`, `style-to-js`, or `react-markdown`, you're on a pre-0.1.6 release — upgrade with `npm cache clean --force && rm -rf ~/.npm/_npx && npx --yes @natjswenson/devlog@latest preview`.
224
-
225
- **Init can't find `gh`:** install via [cli.github.com](https://cli.github.com/) and run `gh auth login`.
226
-
227
- **`Config validation failed: ...`** — the validator rejected something in your `config.json`. Run `npx @natjswenson/devlog config` for a detailed diagnosis. Common causes:
228
- - A field has shell metacharacters (`;` `&` `|` `` ` `` `$` etc) — see SKILL.md for the full list
229
- - A project key contains `..` or `/`
230
- - A path doesn't exist on disk
231
-
232
- **Preview shows "Setup required":** you deployed the preview app standalone but didn't set the env vars. Set `VITE_DEVLOG_OWNER`, `VITE_DEVLOG_REPO`, `VITE_DEVLOG_PROJECTS` (JSON-stringified array) in your hosting environment.
233
-
234
- ## Versioning
235
-
236
- Releases are documented in [CHANGELOG.md](./CHANGELOG.md). The package follows semver — bug fixes/security patches in patch releases (0.1.x), behavior changes in minor (0.x.0).
237
-
238
- ## License
239
-
240
- MIT