@natjswenson/devlog 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,29 @@
2
2
 
3
3
  All notable changes to `@natjswenson/devlog` are documented here.
4
4
 
5
+ ## 0.14.0 (2026-09-09) — verified concept guides and AI cover composition
6
+
7
+ - Add an explicit concept-guide draft workflow: select one transferable reader
8
+ outcome, include a copyable implementation prompt and complete reference, exercise
9
+ the build, and independently test adaptation in an existing project.
10
+ - Add `lint-guide` and `prepare-guide` for strict handoff structure and a standalone
11
+ local reading preview with exact full-guide clipboard and manual fallback.
12
+ - Add `compose-art-cover` for offline composition of local raster artwork with
13
+ adopted brand tokens, true-color PNG output, bounded typography and hashed receipts.
14
+ Codex native image generation remains session orchestration; no new credentials,
15
+ image API calls or paid fallback are introduced in the CLI.
16
+ - Package the workflow references for both hosts and install them beside standalone
17
+ SKILL.md copies. Existing Claude generation uses its local cover renderer.
18
+ - Update Sharp from 0.35.3 to 0.35.4 to resolve the dependency audit's
19
+ [libheif advisory](https://github.com/advisories/GHSA-rgj7-g3m4-5g8c).
20
+ - Add optional `generationMode: concept` for at most one complete guide per normal
21
+ run, with `release` and missing settings preserving existing generation behavior.
22
+ - Add `publish-guide` to bind the article, full agent payload, executed output,
23
+ independent adaptation and review records before writing a new entry. Reviewed
24
+ cover hashes and decoded geometry are checked when art is provided.
25
+ - Keep legacy scanning, assembly, rendering, publication identities and existing
26
+ content unchanged. Explicit drafts remain local; catalog backfill is separate.
27
+
5
28
  ## 0.13.0 (2026-07-29) — a publish run now proves the entry is live
6
29
 
7
30
  Publishing reported success on a post that was never reachable. The content, cover
package/README.md CHANGED
@@ -1,71 +1,171 @@
1
1
  # devlog
2
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)
3
+ <!-- >>> press:masthead v0.9.0 sha256:4b48a8031589 GENERATED by @natjswenson/press, do not edit -->
4
+ **NS** · NATE SWENSON · CLAUDE CODE + CODEX SKILL · PRESS v0.9.0 · linkedin.com/in/natejswenson
7
5
 
8
- A Claude Code skill that turns each version release (a git tag) into a published how-to guide, written in your own voice — and a React example for displaying it on your site.
6
+ ---
7
+ <!-- <<< press:masthead -->
9
8
 
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 polished, end-to-end implementation guide in your voice, with cited sources and the gotchas you actually hit — not raw commit messages.
9
+ *Turn each release into a published how-to guide, written in your own voice.*
11
10
 
12
- ## Live example
11
+ > **Never write a detail you did not read from the repository.** Every command, hash and quote in an entry is checked against git, because a plausible one is a fabricated one.
13
12
 
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.
13
+ [![npm](https://img.shields.io/npm/v/@natjswenson/devlog?color=blue)](https://www.npmjs.com/package/@natjswenson/devlog) [![license](https://img.shields.io/npm/l/@natjswenson/devlog)](./LICENSE) [![security](https://img.shields.io/badge/security-audited-green)](./SECURITY.md)
15
14
 
16
- ## How it works
15
+ ## Why install this
16
+
17
+ "Build in public" usually means either nothing, or a changelog nobody reads.
17
18
 
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 runs `devlog scan` to find tags that don't yet have an entry, researches the engineering topic behind each release, and writes a full setup → build → use → verify how-to guide (with a required `## Gotchas` section mined from your real fix commits, and 3+ cited sources). Every draft passes a deterministic lint (`devlog lint-post`) plus a quality self-review before it publishes. It's idempotent re-running does nothing until you cut a new release, and a published entry is never overwritten (`devlog publish-entry` refuses).
20
- 3. **Your site fetches it.** Static `manifest.json` + per-release markdown files served from `raw.githubusercontent.com` no backend needed.
19
+ Tag a release like you always do. Run `/devlog`. Each new version shows up on
20
+ your site as a polished, end-to-end implementation guidein your voice, with
21
+ cited sources and the gotchas you actually hit not raw commit messages.
21
22
 
22
- You can also manage configuration conversationally in Claude Code: "add this repo to devlog", "stop tracking X", "set min sources to 4", or `/devlog status` to see what would be generated without writing anything.
23
+ Each post is a **detailed how-to**: a reader who has never seen your repo can
24
+ follow it. Every draft passes a deterministic lint plus a quality self-review
25
+ before it publishes, and a published entry is never overwritten.
23
26
 
24
- **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.
27
+ It is in production at [natejswenson.com/devlog](https://natejswenson.com/devlog),
28
+ publishing to
29
+ [github.com/natejswenson/daily-dev-log](https://github.com/natejswenson/daily-dev-log).
30
+ What you see on that page is exactly what `devlog preview` renders locally.
31
+
32
+ ## What you get
33
+
34
+ | Path | What it provides |
35
+ |---|---|
36
+ | `skills/devlog/SKILL.md` | The how-to contract every entry must satisfy. |
37
+ | `skills/devlog/bin/` | The CLI: `init`, `scan`, `lint-post`, `publish-entry`, `preview`, `config`. |
38
+ | `skills/devlog/lib/` | The deterministic core: scanning, linting, publishing, config validation. |
39
+ | `skills/devlog/examples/react/` | A drop-in React page for your own site. |
40
+ | `skills/devlog/preview/` | A complete deployable Vite app, if you have no site yet. |
41
+ | `skills/devlog/image-style/` | The PRESS cover-image palette and style guide. |
42
+ | [`SECURITY.md`](skills/devlog/SECURITY.md) | The full threat model and audit history. |
25
43
 
26
44
  ## Quick start
27
45
 
28
- ```sh
29
- npx @natjswenson/devlog init
46
+ Claude Code — run in chat:
47
+
48
+ ```text
49
+ /plugin marketplace add natejswenson/claude-skills
50
+ /plugin install devlog@claude-skills
51
+ /devlog
30
52
  ```
31
53
 
32
- That command:
33
- - Creates `<your-username>/daily-dev-log` on GitHub (or uses an existing one)
34
- - Installs the skill at `~/.claude/skills/devlog/`
35
- - Writes `~/.claude/skills/devlog/config.json` with your answers
36
- - Lets you register one or more projects in a single run
54
+ Codex — run in a terminal from the root of this repository checkout:
37
55
 
38
- Then:
56
+ ```bash
57
+ codex plugin marketplace add "$PWD"
58
+ codex plugin add devlog@claude-skills
59
+ ```
60
+
61
+ Start a new Codex session, then invoke in chat:
62
+
63
+ ```text
64
+ $devlog
65
+ ```
39
66
 
40
67
  ```sh
41
- npx @natjswenson/devlog preview
68
+ npx @natjswenson/devlog init # create the repo, install the skill, write config
69
+ npx @natjswenson/devlog preview # see it rendered at http://localhost:5173
42
70
  ```
43
71
 
44
- to see your dev log rendered locally at `http://localhost:5173`.
72
+ `init` creates `<your-username>/daily-dev-log` on GitHub (or uses an existing
73
+ repo), installs the skill at `~/.claude/skills/devlog/`, writes its config, and
74
+ lets you register one or more projects in a single run.
75
+
76
+ > **Run from any directory *outside* a clone of this repo.** Running inside it
77
+ > causes a `package.json` name collision and `npx` fails with
78
+ > `command not found`.
79
+
80
+ ## Triggers
81
+
82
+ - "/devlog", or "write a devlog entry".
83
+ - A release you just tagged that should become a post.
84
+ - Managing devlog config conversationally: "add this repo to devlog", "stop
85
+ tracking X", "set min sources to 4", "/devlog status".
86
+ - "Draft one concept guide using the agent-friendly method" or "consolidate these
87
+ posts into one guide" selects the new local draft workflow.
88
+ - "Create an AI cover draft for this article" selects the Codex artwork workflow
89
+ without scanning or publishing.
90
+
91
+ ## Requirements
45
92
 
46
- ## Prerequisites
93
+ - **Claude Code:** Authenticate `gh` in the shell and allow web research for release guides.
94
+ - **Codex:** Use the same authenticated `gh` CLI and available web tools. `init` still writes its legacy Claude skill copy; the Codex plugin is installed separately above.
95
+ - **Personal data:** Both hosts retain config, voice fallback and image-style assets in `~/.claude/skills/devlog/`; an existing `~/.claude/ghostwriter/voice` can also supply the voice.
47
96
 
48
- - **Node 18+** for the CLI and preview app
49
- - **GitHub CLI** (`gh`), authenticated with `gh auth login` — used to create your dev-log repo and push entries
50
- - **Claude Code** to run the `/devlog` skill
97
+ See [Codex migration notes](../../docs/codex-migration.md) for host tools and retained data paths.
98
+
99
+ - **Node 18+** for the CLI and preview app.
100
+ - **GitHub CLI** (`gh`), authenticated with `gh auth login` — used to create your
101
+ dev-log repo and push entries.
102
+ - **Claude Code** (`/devlog`) or **Codex** (`$devlog`) to run the skill.
103
+ - Concept drafts use independent agents for review/adaptation when available;
104
+ missing capabilities retain the draft with explicit uncompleted checks.
105
+ - AI artwork requires the session's native image tool. No implicit API fallback.
106
+
107
+ ## How it works
108
+
109
+ 1. **You ship a release** — tag it, like you already do.
110
+ 2. **Run `/devlog` in Claude Code.** The skill runs `devlog scan` to find tags
111
+ with no entry, researches the engineering topic behind each release, and
112
+ writes a full setup → build → use → verify guide, with a required
113
+ `## Gotchas` section mined from your real fix commits and 3+ cited sources.
114
+ It is idempotent: re-running does nothing until you cut a new release.
115
+ 3. **Your site fetches it.** Static `manifest.json` plus per-release markdown
116
+ served from `raw.githubusercontent.com`. No backend needed.
117
+
118
+ **In your voice.** Entries use a voice profile resolved in this order: your
119
+ `config.voicePath` → [ghostwriter](../ghostwriter)'s `voice/` dir if installed →
120
+ a bundled default. devlog reads `voice-profile.md` and `voice-notes.md`
121
+ overrides, never ghostwriter's `algorithm.md`, since LinkedIn reach tuning does
122
+ not apply to a dev log.
51
123
 
52
124
  ## Commands
53
125
 
54
126
  | Command | What it does |
55
127
  |---|---|
56
- | `npx @natjswenson/devlog init` | One-time setup: create dev-log repo, install skill, write config |
57
- | `npx @natjswenson/devlog add-project` | Register a project (interactive; `--yes --path <p>` for non-interactive/agent use) |
58
- | `npx @natjswenson/devlog remove-project <key> --yes` | Unregister a project (published entries stay) |
59
- | `npx @natjswenson/devlog set <field> <value>` | Update one config field (`targetRepo`, `branch`, `gitAuthor`, `githubUser`, `voicePath`, `deepDive.minSources`, `deepDive.topicDomains`) |
60
- | `npx @natjswenson/devlog config [--json]` | Show your current config with validation status |
61
- | `npx @natjswenson/devlog scan [--project <key>]` | JSON plan of new releases needing entries (used by the skill) |
62
- | `npx @natjswenson/devlog lint-post <file>` | Deterministic post-contract check (used by the skill) |
63
- | `npx @natjswenson/devlog publish-entry ...` | Copy a drafted entry into a clone + update the manifest; never overwrites (used by the skill) |
64
- | `npx @natjswenson/devlog preview` | Run a local preview at `http://localhost:5173` |
65
- | `npx @natjswenson/devlog --help` | Usage |
66
- | `npx @natjswenson/devlog --version` | Version |
67
-
68
- > **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`.
128
+ | `devlog init` | One-time setup: create dev-log repo, install skill, write config |
129
+ | `devlog add-project` | Register a project (interactive; `--yes --path <p>` for agent use) |
130
+ | `devlog remove-project <key> --yes` | Unregister a project (published entries stay) |
131
+ | `devlog set <field> <value>` | Update one config field |
132
+ | `devlog config [--json]` | Show current config with validation status |
133
+ | `devlog scan [--project <key>]` | JSON plan of new releases needing entries |
134
+ | `devlog lint-post <file>` | Deterministic post-contract check |
135
+ | `devlog lint-guide <file> [--voice]` | Post checks plus a unique top-of-post implementation handoff |
136
+ | `devlog prepare-guide --article <md> --brand <json> --out <new-dir> [--cover <png>]` | Standalone local preview and complete copyable prompt/reference |
137
+ | `devlog compose-art-cover --spec <json> --out <new-dir>` | Offline raster artwork composition and hashed result; no image-service call |
138
+ | `devlog publish-entry ...` | Copy a drafted entry into a clone and update the manifest; never overwrites |
139
+ | `devlog preview` | Run a local preview at `http://localhost:5173` |
140
+
141
+ ## Concept guides and cover drafts
142
+
143
+ Explicit concept requests produce at most one complete guide teaching implementation
144
+ in the reader's project. The draft includes the full code, verification commands and
145
+ a prompt that preserves the reader's APIs, saved state and supported runtimes. The
146
+ preview's **Copy prompt + guide** button includes the full Markdown reference; denied
147
+ clipboard access provides a manual selection fallback.
148
+
149
+ The [concept workflow](skills/devlog/references/concept-guides.md) separates mechanical
150
+ lint from executed examples and independent adaptation evidence. The
151
+ [art workflow](skills/devlog/references/codex-cover-art.md) uses native Codex generation
152
+ followed by local typography. [Composition specs](skills/devlog/references/cover-spec.md)
153
+ consume generated/adopted brand JSON and local artwork; no installed PRESS dependency
154
+ is required. Sources remain intact, output directories must be new, and result markers
155
+ are written last. Review hashes identify artifacts, not proof of beautiful or correct art.
156
+
157
+ Default `/devlog` and `$devlog` retain release generation. Opt in with
158
+ `devlog set generationMode concept` to produce at most one complete concept guide
159
+ per normal run, including executed examples, an independent adaptation trial,
160
+ reviewed art and deployment verification. `devlog set generationMode release`
161
+ restores the legacy workflow. Explicit draft requests remain local.
162
+
163
+ The [publishing workflow](skills/devlog/references/guide-publishing.md) uses
164
+ `publish-guide --clone <content-root> --article <md> --evidence <json> [--cover <png>]`
165
+ to validate matching evidence before writing an unoccupied release identity.
166
+ It never pushes by itself. Installation does not change user configuration,
167
+ rewrite old posts or add a suppression ledger. Existing-post consolidation and
168
+ backfill remain separate explicitly scoped work.
69
169
 
70
170
  ## What you end up with
71
171
 
@@ -86,21 +186,9 @@ github.com/<you>/daily-dev-log/ # Created by init, populated by /devlog
86
186
  └── ...
87
187
  ```
88
188
 
89
- ## Manual setup (if you prefer)
90
-
91
- ```sh
92
- gh repo create <you>/daily-dev-log --public --add-readme
93
- mkdir -p ~/.claude/skills/devlog/voice
94
- curl -o ~/.claude/skills/devlog/SKILL.md https://raw.githubusercontent.com/natejswenson/devlog/main/SKILL.md
95
- # Optional fallback voice profile (used when voicePath and ghostwriter are both absent):
96
- curl -o ~/.claude/skills/devlog/voice/voice-profile.md https://raw.githubusercontent.com/natejswenson/devlog/main/voice/voice-profile.example.md
97
- curl -o ~/.claude/skills/devlog/voice/voice-notes.md https://raw.githubusercontent.com/natejswenson/devlog/main/voice/voice-notes.example.md
98
- # Then copy config.example.json → ~/.claude/skills/devlog/config.json and fill it in
99
- ```
100
-
101
189
  ## Add to your site
102
190
 
103
- ### React (drop-in)
191
+ **React (drop-in):**
104
192
 
105
193
  ```sh
106
194
  cp -r examples/react/ your-site/src/devlog/
@@ -114,30 +202,30 @@ import DevLogPage from './devlog/DevLogPage.jsx';
114
202
  <DevLogPage project="myproject" />
115
203
  ```
116
204
 
117
- Full instructions: [`examples/react/README.md`](./examples/react/README.md).
205
+ Full instructions: [`examples/react/README.md`](skills/devlog/examples/react/README.md).
118
206
 
119
- ### No site yet?
207
+ **No site yet?** The `preview/` directory is a complete deployable Vite app. Set
208
+ `VITE_DEVLOG_OWNER` / `VITE_DEVLOG_REPO` / `VITE_DEVLOG_PROJECTS` on Vercel,
209
+ Netlify or Cloudflare Pages, build with `vite build`, deploy `dist/`.
120
210
 
121
- 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).
122
-
123
- ### Other stacks (Next, Astro, plain HTML)
124
-
125
- It's static JSON and Markdown on GitHub. Build whatever UI you want — see the **Data contract** below.
211
+ **Other stacks (Next, Astro, plain HTML):** it is static JSON and Markdown on
212
+ GitHub. Build whatever UI you want against the data contract below.
126
213
 
127
214
  ## Data contract
128
215
 
129
- The dev-log repo has this layout, all served as raw files from `https://raw.githubusercontent.com/<owner>/<repo>/<branch>/`:
216
+ The dev-log repo has this layout, all served as raw files from
217
+ `https://raw.githubusercontent.com/<owner>/<repo>/<branch>/`:
130
218
 
131
219
  ```
132
220
  <repo>/
133
221
  └── <project-key>/
134
222
  ├── manifest.json # Index of all entries (newest first)
135
223
  ├── v0.3.0.md # One entry per release (named by version)
136
- ├── v0.2.0.md
137
224
  └── ...
138
225
  ```
139
226
 
140
- **`manifest.json`:**
227
+ `manifest.json`:
228
+
141
229
  ```json
142
230
  {
143
231
  "entries": [
@@ -146,13 +234,15 @@ The dev-log repo has this layout, all served as raw files from `https://raw.gith
146
234
  }
147
235
  ```
148
236
 
149
- Strict validation rules (entries that don't match are silently dropped by the React example):
237
+ Strict validation rules entries that do not match are silently dropped by the
238
+ React example:
239
+
150
240
  - `date` matches `YYYY-MM-DD` (the release/tag date)
151
241
  - `file` matches `^[a-zA-Z0-9._-]+\.md$`
152
242
  - `title` and `summary` are non-empty strings
153
243
  - `version` (optional) matches `^[a-zA-Z0-9._-]+$`
154
244
 
155
- **Entry markdown:**
245
+ Entry markdown:
156
246
 
157
247
  ```markdown
158
248
  ---
@@ -181,7 +271,8 @@ Real traps from the release's history, each: trap → symptom → escape.
181
271
  - commit message ([abc1234](https://github.com/.../commit/abc1234567...))
182
272
  ```
183
273
 
184
- That's the entire contract — `npx @natjswenson/devlog lint-post <file>` checks the mechanical parts of it.
274
+ That is the entire contract — `devlog lint-post <file>` checks the mechanical
275
+ parts of it.
185
276
 
186
277
  ## Configuration reference
187
278
 
@@ -189,84 +280,103 @@ That's the entire contract — `npx @natjswenson/devlog lint-post <file>` checks
189
280
 
190
281
  | Field | Type | Description |
191
282
  |---|---|---|
192
- | `targetRepo` | `"<owner>/<repo>"` | Repo where dev log entries are published. Must match `^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*$`. |
283
+ | `targetRepo` | `"<owner>/<repo>"` | Repo where entries are published. Must match `^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*$`. |
193
284
  | `branch` | string (optional) | Branch in the dev-log repo. Defaults to `main`. Must not contain `..` or start with `-`. |
194
- | `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. |
285
+ | `gitAuthor` | string | Your name. Retained for backward compatibility; **not** currently rendered on entries, but still **required** by config validation. Whitespace OK; no shell metacharacters. |
195
286
  | `githubUser` | string | Your GitHub username. |
196
- | `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. |
287
+ | `voicePath` | string (optional) | Directory holding `voice-profile.md` (and optionally `voice-notes.md`). A leading `~` is expanded. Read only, never shell-interpolated. |
197
288
  | `projects` | array | One entry per project you want dev logs for. |
198
289
  | `projects[].key` | string | Subdirectory name in the dev-log repo. Strict token: `^[a-z0-9][a-z0-9._-]*$`, no `..`. |
199
290
  | `projects[].label` | string (optional) | Display name for the tab. Defaults to `key`. |
200
291
  | `projects[].path` | string | Local filesystem path to the project. Whitespace OK. |
201
- | `projects[].remote` | `"<owner>/<repo>"` | The project's GitHub remote. Used to mark public commits and link them. |
202
- | `projects[].pathFilter` | string (optional) | Repo-relative subdir scoping this project's commits in a monorepo (e.g. `skills/devlog`). |
203
- | `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>*'`. |
204
- | `deepDive` | object (optional) | Controls the researched how-to. `topicDomains` (array, default `["AI", "DevOps/SRE", "software engineering"]`) steers topic selection; `minSources` (integer 1-10, default 3) is the floor of **distinct** cited source URLs per post. |
292
+ | `projects[].remote` | `"<owner>/<repo>"` | The project's GitHub remote, used to mark public commits and link them. |
293
+ | `projects[].pathFilter` | string (optional) | Repo-relative subdir scoping this project's commits in a monorepo. |
294
+ | `projects[].tagPrefix` | string (optional) | Prefix of the git tags marking this project's releases. Defaults to `v`. |
295
+ | `deepDive` | object (optional) | `topicDomains` steers topic selection; `minSources` (110, default 3) is the floor of **distinct** cited source URLs per post. |
296
+
297
+ See [`config.example.json`](skills/devlog/config.example.json) for a complete
298
+ template, or run `devlog config` to inspect yours with validation.
205
299
 
206
- See [`config.example.json`](./config.example.json) for a complete template, or run `npx @natjswenson/devlog config` to inspect your current config with validation.
300
+ ## Customization
301
+
302
+ - **Tweak the entry template:** edit `~/.claude/skills/devlog/SKILL.md` (Generate
303
+ mode, Step 3 — the how-to contract).
304
+ - **Tweak your voice:** edit `voice-profile.md` / `voice-notes.md` in your
305
+ `voicePath`.
306
+ - **Tune research depth:** `devlog set deepDive.minSources 4`, or
307
+ `set deepDive.topicDomains "security, platform engineering"`.
308
+ - **Tweak the UI:** override the `--devlog-*` CSS variables in
309
+ `examples/react/DevLogPage.css`.
310
+ - **Add or remove projects:** `devlog add-project` / `remove-project <key> --yes`,
311
+ or just tell Claude.
207
312
 
208
313
  ## Security
209
314
 
210
- 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.
315
+ The package is designed to be safe to install on a developer's machine holding
316
+ ambient `gh`/git credentials. See
317
+ [`SECURITY.md`](skills/devlog/SECURITY.md) for the full threat model.
318
+
319
+ - All shell calls in the CLI use `spawnSync` with argv arrays — no shell, no
320
+ injection surface.
321
+ - The skill validates every config field against an allowlist before
322
+ interpolating it into a shell command.
323
+ - Markdown rendering uses `react-markdown` with `skipHtml` and an allowlist
324
+ `urlTransform`; `data:`, `javascript:`, `vbscript:`, `file:` and `blob:` are all
325
+ neutralized.
326
+ - Frontmatter parsing uses `Object.create(null)` plus a key allowlist — no
327
+ prototype pollution.
328
+ - All external JSON is schema-validated before use.
329
+ - `config.json` is written atomically, mode 0600. All dependencies are pinned to
330
+ exact versions.
331
+
332
+ **To report a vulnerability:** open a
333
+ [GitHub security advisory](https://github.com/natejswenson/devlog/security/advisories/new).
334
+ Do not open a public issue.
211
335
 
212
- **At a glance:**
213
- - ✓ All shell calls in the CLI use `spawnSync` with argv arrays (no shell, no injection surface)
214
- - ✓ The skill validates every config field against an allowlist before interpolating into shell commands; instructs the LLM to single-quote interpolated values
215
- - ✓ Markdown rendering uses `react-markdown` with `skipHtml` and an allowlist `urlTransform` (only http(s)/mailto allowed; data:, javascript:, vbscript:, file:, blob: all neutralized)
216
- - ✓ Frontmatter parser uses `Object.create(null)` + key allowlist (no prototype pollution)
217
- - ✓ All external JSON is schema-validated before use (manifest, env-var projects array)
218
- - ✓ Vite dev server bound to `localhost`, CORS off
219
- - ✓ Demo-mode `window.fetch` override gated to dev builds only
220
- - ✓ `config.json` written atomically (tmp + rename), mode 0600
221
- - ✓ All dependencies pinned to exact versions
222
- - ✓ `npm audit`: 0 known vulnerabilities
336
+ ## Troubleshooting
223
337
 
224
- **To report a vulnerability:** open a [GitHub security advisory](https://github.com/natejswenson/devlog/security/advisories/new). Do not open a public issue.
338
+ **`sh: devlog: command not found` when running `npx`:** you are inside a checkout
339
+ of this repo. The local `package.json` name collides with the published one. Run
340
+ `npx` from somewhere else.
225
341
 
226
- ## Customization
342
+ **Preview is blank, no tabs, no entries:** check the browser console. CJS interop
343
+ errors on `react-dom/client`, `style-to-js` or `react-markdown` mean a pre-0.1.6
344
+ release — `npm cache clean --force && rm -rf ~/.npm/_npx && npx --yes @natjswenson/devlog@latest preview`.
345
+
346
+ **Init cannot find `gh`:** install it from [cli.github.com](https://cli.github.com/)
347
+ and run `gh auth login`.
227
348
 
228
- - **Tweak the entry template:** edit `~/.claude/skills/devlog/SKILL.md` (Generate mode, Step 3 — the how-to contract).
229
- - **Tweak your voice:** edit the `voice-profile.md` / `voice-notes.md` in your `voicePath` (or `~/.claude/skills/devlog/voice/`).
230
- - **Tune research depth:** `npx @natjswenson/devlog set deepDive.minSources 4`, or `set deepDive.topicDomains "security, platform engineering"`.
231
- - **Tweak the UI:** override the `--devlog-*` CSS variables in `examples/react/DevLogPage.css` to match your theme.
232
- - **Add or remove projects:** `npx @natjswenson/devlog add-project` / `remove-project <key> --yes` — or just tell Claude ("add this repo to devlog").
349
+ **`Config validation failed`:** run `devlog config` for a detailed diagnosis.
350
+ Common causes are shell metacharacters in a field, a project key containing `..`
351
+ or `/`, or a path that does not exist.
233
352
 
234
- ## Testing & evals
353
+ **Preview shows "Setup required":** you deployed the preview app standalone
354
+ without setting `VITE_DEVLOG_OWNER`, `VITE_DEVLOG_REPO` and
355
+ `VITE_DEVLOG_PROJECTS`.
235
356
 
236
- The deterministic core (`lib/`, the CLI) is covered by `npm test` (node:test; scan tests
237
- run against real throwaway git repos). The non-deterministic half — post quality — is
238
- covered by a cost-capped eval harness:
357
+ ## Development
239
358
 
240
359
  ```sh
360
+ cd skills/devlog/skills/devlog
361
+ npm test # deterministic core; scan tests use real throwaway git repos
241
362
  node evals/run_eval.mjs --mock # $0, runs in CI; deterministic layer only
242
363
  node evals/run_eval.mjs --live # LLM judge on golden fixtures; quotes spend, hard cap $0.50
243
364
  ```
244
365
 
245
- The judge scores reproducibility, code completeness, gotcha quality, citations, voice,
246
- and scope honesty (pass ≥ 7/10). Without `ANTHROPIC_API_KEY` in the environment, mock
247
- mode is forced CI can never spend money.
248
-
249
- ## Troubleshooting
250
-
251
- **`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 ...`.
366
+ Node skill. The judge scores reproducibility, code completeness, gotcha quality,
367
+ citations, voice and scope honesty (pass ≥ 7/10). Without `ANTHROPIC_API_KEY` in
368
+ the environment, mock mode is forced, so **CI can never spend money**.
252
369
 
253
- **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.
370
+ The baseline is pinned against 8 entries actually published to natejswenson.io
371
+ a curated subset on purpose, since only 17 of 61 published entries satisfy
372
+ today's contract and asserting over all 61 would encode "the linter must accept
373
+ its own history".
254
374
 
255
- **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`.
256
-
257
- **Init can't find `gh`:** install via [cli.github.com](https://cli.github.com/) and run `gh auth login`.
258
-
259
- **`Config validation failed: ...`** — the validator rejected something in your `config.json`. Run `npx @natjswenson/devlog config` for a detailed diagnosis. Common causes:
260
- - A field has shell metacharacters (`;` `&` `|` `` ` `` `$` etc) — see SKILL.md for the full list
261
- - A project key contains `..` or `/`
262
- - A path doesn't exist on disk
263
-
264
- **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.
265
-
266
- ## Versioning
375
+ ## Changelog
267
376
 
268
- 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).
377
+ See [`CHANGELOG.md`](CHANGELOG.md). Releases are cut by a version bump, tagged
378
+ `devlog-v<version>`, and published to npm.
269
379
 
270
380
  ## License
271
381
 
272
- MIT
382
+ MIT — see [`LICENSE`](LICENSE).
package/SKILL.md CHANGED
@@ -1,12 +1,25 @@
1
1
  ---
2
2
  name: devlog
3
- description: Turn each new version release (git tag) into a polished, researched how-to guide with real gotchas, written in your voice and published to GitHub. Also manages devlog configuration conversationally add/remove tracked repos, change settings, show status.
3
+ description: Turn releases into researched how-to posts, or explicitly draft one complete concept guide readers can implement with their own coding agent. Supports Codex AI cover drafts and conversational devlog configuration.
4
4
  user_invocable: true
5
5
  ---
6
6
 
7
+ ## Codex runtime
8
+
9
+ When running in Codex, invoke this skill as `$devlog`. Resolve scripts, assets,
10
+ and references from the directory containing this SKILL.md, regardless of the
11
+ current working directory. Existing `~/.claude/` personal-data paths remain valid
12
+ and are still used by the bundled scripts; they do not require Claude to run.
13
+ Map `Read`/`Write`/`Edit`/`Bash` to the available file and shell tools, and
14
+ `WebSearch`/`WebFetch` to available web tools. For `AskUserQuestion`, use an
15
+ available question tool or a concise chat question; wait for answers that gate
16
+ action. Use Codex's delegation tools for required subagents when available;
17
+ otherwise disclose that independent execution is unavailable. Discover connected
18
+ apps by capability rather than assuming Claude MCP tool names exist.
19
+
7
20
  # /devlog — Release How-To Generator
8
21
 
9
- You turn each **new version release** (a semver git tag) in the user's projects into a
22
+ In the default Generate mode, you turn each **new version release** (a semver git tag) in the user's projects into a
10
23
  published blog post, written in **the user's own voice**, and pushed to the GitHub repo
11
24
  configured in `~/.claude/skills/devlog/config.json`.
12
25
 
@@ -21,6 +34,34 @@ Every agent-facing command prints JSON.
21
34
 
22
35
  ## Decide which mode you're in
23
36
 
37
+ Explicit draft requests take precedence over the default Generate routing:
38
+
39
+ - **Concept guide draft** — "draft one concept guide", "use the agent-friendly guide
40
+ method", or "consolidate these posts into one guide". Read
41
+ [references/concept-guides.md](references/concept-guides.md). Select at most one
42
+ transferable reader outcome and finish its implementation, handoff and independent
43
+ trial. This mode is draft-only; never fall through to Generate or publish-entry.
44
+ - **AI cover draft** — "create an AI cover draft for this article". Read
45
+ [references/codex-cover-art.md](references/codex-cover-art.md). Work from the supplied
46
+ article, without scanning releases or publishing. Codex native image generation is
47
+ capability-checked; Claude retains its existing local renderer. No implicit API fallback.
48
+
49
+ New helpers are `lint-guide`, `prepare-guide`, `compose-art-cover`, and `publish-guide`.
50
+ Only `publish-guide` writes prepared content to a clone; it requires matching local
51
+ execution, adaptation and review evidence. It does not push or deploy.
52
+ The draft helpers do not change persistent configuration or the content repository.
53
+ Use bundled `bin/devlog.js` relative to this loaded SKILL.md only when its runtime
54
+ dependencies resolve: check `node <absolute-skill-root>/bin/devlog.js --version` first.
55
+ A Git-installed plugin can contain the script without node_modules. If the script or
56
+ dependencies are missing, use `npx -y @natjswenson/devlog@0.14.0 <helper>`; do not install
57
+ dependencies into an internal plugin cache. A standalone SKILL.md installed by `init`
58
+ uses that same exact-version fallback. For a missing Chromium binary, install the
59
+ matching browser with `npx -y --package=@natjswenson/devlog@0.14.0 playwright install chromium`
60
+ and retry the render; do not silently change dependency versions. References are bundled beside
61
+ both host entrypoints and copied with standalone skill installation.
62
+
63
+ Otherwise use the existing modes:
64
+
24
65
  1. **Configure** — the user wants to change what devlog tracks or how it behaves:
25
66
  "add this repo to devlog", "stop tracking X", "set min sources to 4", "show my
26
67
  devlog config". → **Configure mode**.
@@ -28,7 +69,15 @@ Every agent-facing command prints JSON.
28
69
  "any new releases?". → **Status mode**.
29
70
  3. **Generate** (default) — `/devlog` or `/devlog <project-key>`. → **Generate mode**.
30
71
 
31
- An entry corresponds to a **release**, not a day. Re-running Generate only produces
72
+ For a normal Generate request, read validated config first. If the user explicitly
73
+ selected `generationMode: "concept"`, follow
74
+ [references/guide-publishing.md](references/guide-publishing.md): select at most one
75
+ complete guide, finish its draft/trial/review, then publish after all checks. Missing
76
+ `generationMode` or `"release"` retains the existing Generate workflow below. Explicit
77
+ draft requests above always remain draft-only, even with this preference. Set the
78
+ preference only when the user asks; an update must not silently opt existing users in.
79
+
80
+ In Generate mode, an entry corresponds to a **release**, not a day. Re-running Generate only produces
32
81
  entries for tags that don't already have one — it is idempotent, and a published entry is
33
82
  **immutable: never overwrite it** (`publish-entry` refuses; don't work around it).
34
83
 
@@ -42,6 +91,7 @@ Map the user's request onto the CLI — never hand-edit `config.json`:
42
91
  | Add a project | `npx -y @natjswenson/devlog@latest add-project --yes --path <abs-path> [--key K] [--remote O/R] [--label L] [--tag-prefix P] [--path-filter F] [--private]` |
43
92
  | Remove a project | `npx -y @natjswenson/devlog@latest remove-project <key> --yes` |
44
93
  | Change a setting | `npx -y @natjswenson/devlog@latest set <field> <value>` (settable: `targetRepo`, `branch`, `targetDir`, `gitAuthor`, `githubUser`, `siteUrl`, `voicePath`, `deepDive.minSources`, `deepDive.topicDomains`) |
94
+ | Adopt one-guide generation for future normal runs | `npx -y @natjswenson/devlog@latest set generationMode concept` (use `release` to restore legacy generation) |
45
95
 
46
96
  `targetDir` is the subdirectory of `targetRepo` that holds the devlog content tree
47
97
  (e.g. `content/devlog` when the target is the site repo itself); unset/empty means the
@@ -342,7 +392,7 @@ The `--clone` flag always points at the CONTENT ROOT: `<abs-tmp>/<repo-name>` wh
342
392
  `targetDir` is empty, `<abs-tmp>/<repo-name>/<targetDir>` when it's set. Git commands
343
393
  always run against the clone root `<abs-tmp>/<repo-name>` regardless.
344
394
 
345
- Each release also gets a cover image, composed inline in this same loop right before that
395
+ In this legacy Generate path, each release also gets a cover image, composed inline in this same loop right before that
346
396
  release's own `publish-entry` call — a self-contained HTML/CSS (or inline SVG) document,
347
397
  rasterized locally, never sent to any external service:
348
398