@natjswenson/devlog 0.14.1 → 0.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  All notable changes to `@natjswenson/devlog` are documented here.
4
4
 
5
+ ## 0.14.2 (2026-09-17)
6
+
7
+ ### Added
8
+
9
+ - Completed issueflow integration and made initialization host-aware.
10
+
11
+ ## Unreleased
12
+
13
+ - Make init host-aware: Claude retains standalone skill installation while
14
+ Codex writes shared personal assets without shadowing the marketplace plugin.
15
+
5
16
  ## 0.14.1 (2026-09-10) — native branded cover generation
6
17
 
7
18
  - Require Codex covers to use native generated raster artwork in the established
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # devlog
2
2
 
3
- <!-- >>> press:masthead v0.10.0 sha256:b950ecf78f98 GENERATED by @natjswenson/press, do not edit -->
4
- **NS** · NATE SWENSON · CLAUDE CODE + CODEX SKILL · PRESS v0.10.0 · linkedin.com/in/natejswenson
3
+ <!-- >>> press:masthead v0.11.0 sha256:582134ecd7a5 GENERATED by @natjswenson/press, do not edit -->
4
+ **NS** · NATE SWENSON · CLAUDE CODE + CODEX SKILL · PRESS v0.11.0 · linkedin.com/in/natejswenson
5
5
 
6
6
  ---
7
7
  <!-- <<< press:masthead -->
@@ -65,13 +65,16 @@ $devlog
65
65
  ```
66
66
 
67
67
  ```sh
68
- npx @natjswenson/devlog init # create the repo, install the skill, write config
68
+ npx @natjswenson/devlog init # create the repo, install the Claude skill, write config
69
69
  npx @natjswenson/devlog preview # see it rendered at http://localhost:5173
70
70
  ```
71
71
 
72
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.
73
+ repo), writes shared config and personal assets to `~/.claude/skills/devlog/`,
74
+ and lets you register one or more projects in a single run. It defaults to the
75
+ Claude standalone installation; use `init --host codex` when configuring the
76
+ Codex plugin. Codex keeps the marketplace-installed $devlog plugin
77
+ authoritative and does not create or update a standalone skill copy.
75
78
 
76
79
  > **Run from any directory *outside* a clone of this repo.** Running inside it
77
80
  > causes a `package.json` name collision and `npx` fails with
@@ -91,7 +94,9 @@ lets you register one or more projects in a single run.
91
94
  ## Requirements
92
95
 
93
96
  - **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.
97
+ - **Codex:** Use the same authenticated `gh` CLI and available web tools. Run
98
+ `init --host codex` to write shared config and personal assets; install or
99
+ update the marketplace plugin separately using the Codex commands above.
95
100
  - **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.
96
101
 
97
102
  See [Codex migration notes](../../docs/codex-migration.md) for host tools and retained data paths.
@@ -125,7 +130,7 @@ not apply to a dev log.
125
130
 
126
131
  | Command | What it does |
127
132
  |---|---|
128
- | `devlog init` | One-time setup: create dev-log repo, install skill, write config |
133
+ | `devlog init [--host claude or codex]` | One-time setup (Claude by default); write config and host-appropriate assets |
129
134
  | `devlog add-project` | Register a project (interactive; `--yes --path <p>` for agent use) |
130
135
  | `devlog remove-project <key> --yes` | Unregister a project (published entries stay) |
131
136
  | `devlog set <field> <value>` | Update one config field |
@@ -211,6 +216,13 @@ Netlify or Cloudflare Pages, build with `vite build`, deploy `dist/`.
211
216
  **Other stacks (Next, Astro, plain HTML):** it is static JSON and Markdown on
212
217
  GitHub. Build whatever UI you want against the data contract below.
213
218
 
219
+ ## Producer and website onboarding
220
+
221
+ Follow the [complete producer and website onboarding guide](https://github.com/natejswenson/claude-skills/blob/main/skills/devlog/skills/devlog/SKILL.md#producer-and-website-onboarding).
222
+ The guide ships inside SKILL.md for plugin, standalone Claude and npm installations.
223
+ It covers inspecting and preserving registration, privacy, monorepo release scoping,
224
+ and the website registry, manifest and verification required before setup is complete.
225
+
214
226
  ## Data contract
215
227
 
216
228
  The dev-log repo has this layout, all served as raw files from
package/SKILL.md CHANGED
@@ -2,6 +2,7 @@
2
2
  name: devlog
3
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
+ version: 0.14.2
5
6
  ---
6
7
 
7
8
  ## Codex runtime
@@ -10,6 +11,11 @@ When running in Codex, invoke this skill as `$devlog`. Resolve scripts, assets,
10
11
  and references from the directory containing this SKILL.md, regardless of the
11
12
  current working directory. Existing `~/.claude/` personal-data paths remain valid
12
13
  and are still used by the bundled scripts; they do not require Claude to run.
14
+ The Codex marketplace plugin is the authoritative $devlog entrypoint. When
15
+ running init from Codex, use --host codex so the CLI writes shared config,
16
+ voice, and image-style assets without creating or updating a standalone skill
17
+ copy in the retained legacy directory. Claude init remains the standalone
18
+ skill-copy installation path.
13
19
  Map `Read`/`Write`/`Edit`/`Bash` to the available file and shell tools, and
14
20
  `WebSearch`/`WebFetch` to available web tools. For `AskUserQuestion`, use an
15
21
  available question tool or a concise chat question; wait for answers that gate
@@ -108,13 +114,24 @@ from a working page, not from the repo. Without it a run can push but cannot con
108
114
  anything went live. Set with `set siteUrl 'https://example.com'`, clear with
109
115
  `set siteUrl ''`.
110
116
 
111
- For **add-project**: resolve the path first (the repo the user named, or the cwd), then
112
- detect what the CLI will use key = directory basename, remote = `git -C '<path>' remote
113
- get-url origin`. In a monorepo, suggest a `--path-filter` (the project's subdir) and a
114
- `--tag-prefix` (e.g. `myproj-v`). Confirm the resolved values with the user in ONE
115
- `AskUserQuestion` (options: "looks right" / sensible alternatives), then run with `--yes`
116
- and show the resulting project list. For **remove-project**, confirm once before running;
117
- tell the user published entries are not deleted.
117
+ For **add-project**: inspect validated config first with `config --json`, then
118
+ resolve the user's absolute repository path and compare key, label, path, remote,
119
+ pathFilter, tagPrefix and the effective private value (omitted means false). A private
120
+ repository requires private: true; report a privacy mismatch for correction before
121
+ considering setup complete. Preserve a matching existing row unchanged and skip the
122
+ add. Run add-project only when the key is absent. Report a mismatch instead of
123
+ removing/recreating the existing row automatically; duplicate-key rejection remains
124
+ intentional. Detect the default key from the directory basename and remote with
125
+ `git -C '<path>' remote get-url origin`. In a monorepo, use the project's subdirectory
126
+ as `--path-filter` and its release namespace as `--tag-prefix`. Confirm unresolved
127
+ values with the user before adding; values already supplied or authorized need no
128
+ second confirmation. For **remove-project**, confirm once before running; tell the
129
+ user published entries are not deleted.
130
+
131
+ Complete the [producer and website onboarding guide](#producer-and-website-onboarding)
132
+ before declaring setup complete. It covers the exact Issue Flow monorepo fields,
133
+ retained shared configuration, and the website registry-plus-manifest requirement.
134
+ Registration does not publish an article; preserve any existing website content.
118
135
 
119
136
  **Private repos.** If the user says the repo is private (or a source repo happens to be
120
137
  private on GitHub even though it's configured normally), pass `--private`. A private
@@ -130,6 +147,106 @@ If any command prints `{"error": "config-missing", ...}`, tell the user to run
130
147
  `npx @natjswenson/devlog init` first. On `config-invalid`, show the message and offer to
131
148
  fix the named field via `set`.
132
149
 
150
+ ## Producer and website onboarding
151
+
152
+ Registering a source project makes releases discoverable; it does not publish an
153
+ article or finish website onboarding. Use this checklist before the first publication
154
+ and when checking an existing integration. Invoke `/devlog` in Claude Code or
155
+ `$devlog` in Codex; both retain personal configuration at
156
+ `~/.claude/skills/devlog/config.json`. Keep that file outside the plugin.
157
+
158
+ ### Inspect and preserve the producer registration
159
+
160
+ Run the host-independent CLI to inspect validated configuration first:
161
+
162
+ ```sh
163
+ npx -y @natjswenson/devlog@0.14.1 config --json
164
+ ```
165
+
166
+ For Issue Flow in the monorepo, compare the existing row with this example.
167
+ Replace the example path with the user's absolute monorepo checkout path:
168
+
169
+ ```json
170
+ {
171
+ "key": "issueflow",
172
+ "label": "Issue Flow",
173
+ "path": "/absolute/path/to/claude-skills",
174
+ "remote": "natejswenson/claude-skills",
175
+ "pathFilter": "skills/issueflow",
176
+ "tagPrefix": "issueflow-v"
177
+ }
178
+ ```
179
+
180
+ Compare all six fields above and the effective private value (omitted means false).
181
+ This public Issue Flow example expects private: false; a private repository requires
182
+ private: true and --private when adding it. If exactly one row matches, preserve it
183
+ unchanged and skip add-project. Report any mismatch, including privacy, for correction
184
+ before considering setup complete;
185
+ do not remove and recreate it automatically. Direct duplicate adds are rejected.
186
+ Only when the key is absent, run:
187
+
188
+ ```sh
189
+ npx -y @natjswenson/devlog@0.14.1 add-project --yes \
190
+ --path '/absolute/path/to/claude-skills' --key issueflow --label 'Issue Flow' \
191
+ --remote natejswenson/claude-skills --path-filter skills/issueflow \
192
+ --tag-prefix issueflow-v
193
+ npx -y @natjswenson/devlog@0.14.1 config --json
194
+ ```
195
+
196
+ Then inspect release discovery with
197
+ `npx -y @natjswenson/devlog@0.14.1 scan --json --project issueflow`.
198
+ Expect only `issueflow-v` release tags and `skills/issueflow` commit/diff history;
199
+ other skills' releases and changes must remain outside this project. An empty
200
+ new-release list may mean entries already exist; inspect scan status and skipped
201
+ reasons before concluding that registration failed.
202
+
203
+ ### Register the website and its manifest together
204
+
205
+ In `natejswenson/natejswenson.io`, inspect the current remote branch before editing:
206
+ a stale local checkout can omit an integration that already exists upstream.
207
+ Check `PROJECTS` in `src/data/site.js` for exactly one `issueflow` row labeled
208
+ `Issue Flow`, and check `content/devlog/issueflow/manifest.json`. Preserve an
209
+ existing registry row and populated manifest; never replace published entries
210
+ with the empty example below.
211
+
212
+ For a new project, add its PROJECTS row and a valid manifest in the same change.
213
+ Match neighboring registry fields. Before any article exists, the manifest is:
214
+
215
+ ```json
216
+ {"entries": []}
217
+ ```
218
+
219
+ The loader visits registered projects only and requires every manifest. A directory
220
+ alone is insufficient; a missing manifest fails the build. An empty entries array
221
+ allows the project route to render its empty state. Source, config and documentation
222
+ changes follow the website's own PR flow, even when content publishing has a separate
223
+ flow. Keep existing content intact and link the companion website PR from the
224
+ producer setup PR. Consult the website README and contributor instructions for its
225
+ current branch policy.
226
+
227
+ For this target, verify `targetRepo`, `targetDir: content/devlog`, and the publishing
228
+ branch in shared config. Set `siteUrl` from a working public page: the repository
229
+ name is not necessarily the served domain. Do not change these settings just to
230
+ register another source project.
231
+
232
+ ### Verify both halves
233
+
234
+ Run the website's `npm run test:ci`, `npm run build`, and relevant
235
+ `npm run test:e2e` checks. In a disposable fixture or preview, verify an empty
236
+ manifest builds with an Issue Flow project route and other projects still available.
237
+ Use an existing article or a temporary representative fixture to check the Issue Flow
238
+ listing/filter and tag search, hash restoration, project/article links, canonical/OG
239
+ metadata and a successful cover response (including the established fallback).
240
+ Inspect generated sitemap routes and RSS; the RSS feed includes only the latest 30
241
+ entries, so use a latest-dated fixture when specifically testing inclusion.
242
+ Never commit or publish a fake article for setup, or empty a populated production
243
+ manifest to test the pre-publication state.
244
+
245
+ Record the checked commits and command outcomes in both PRs. A local producer config
246
+ row alone is not evidence that the website integration is complete. Publishing new
247
+ articles, backfilling old releases and merging/deploying website changes are separate
248
+ actions from this onboarding check.
249
+
133
250
  ## Status mode
134
251
 
135
252
  Run `npx -y @natjswenson/devlog@latest scan --json --summary` and render a compact table:
@@ -478,18 +595,20 @@ content on disk that the registry doesn't list is built right past, and the entr
478
595
  while every command in this skill reports success. Publishing a project's first entry is
479
596
  therefore a two-part job, and the CLI can only do the first part.
480
597
 
481
- When `firstEntryForProject` is true, **before the push**:
482
-
483
- 1. Find the registry in the clone. It is a source file, not content — grep the site's
484
- `src/` for the existing project keys (`grep -rn '<a-known-project-key>' <clone>/src`).
485
- In an Astro/Next-style site it is typically a `PROJECTS` array the entry loader maps
486
- over.
487
- 2. Add the new project, matching the shape of the neighbouring rows exactly.
488
- 3. **Build the site in the clone and confirm the route exists** (`npm ci && npx astro
489
- build`, then check the output directory contains the new `<project>/<version>` path).
490
- A registry edit that doesn't produce a route is not done.
491
- 4. Run the site's own test suite if it has one, and commit the registry change together
492
- with the content in the same push.
598
+ Follow the [producer and website onboarding guide](#producer-and-website-onboarding)
599
+ even before the first article. When `firstEntryForProject` is true, **before the push**:
600
+
601
+ 1. Inspect the current website branch and find its registry in the clone. Search the
602
+ site's `src/` for existing project keys with `rg`. An Astro/Next-style site typically
603
+ maps a `PROJECTS` array. Preserve a matching registration instead of adding a duplicate.
604
+ 2. For a new project, add the registry row and valid manifest in the same change,
605
+ matching neighboring rows. Never replace a populated manifest with an empty example.
606
+ 3. Build the site and confirm the project/article routes exist in its output. Run the
607
+ site's tests and check navigation, tag search, metadata, cover, feed and sitemap
608
+ behavior as described in the paired guide.
609
+ 4. Follow the site's own PR flow for registry/source/config/docs changes and link any
610
+ companion PR. Content publication alone does not authorize bypassing that flow;
611
+ do not report the route complete until the required website checks are observed.
493
612
 
494
613
  If you cannot find a registry, say so plainly rather than assuming there isn't one; the
495
614
  verification in Step 6 is what actually settles it.
package/bin/devlog.js CHANGED
@@ -90,6 +90,10 @@ const log = {
90
90
  hint: (msg) => console.log(kleur.dim(' ' + msg)),
91
91
  };
92
92
 
93
+ async function initPrompt(questions, options) {
94
+ return prompts(questions, options);
95
+ }
96
+
93
97
  function readPackageVersion() {
94
98
  const pkg = JSON.parse(readFileSync(join(PACKAGE_ROOT, 'package.json'), 'utf8'));
95
99
  return pkg.version;
@@ -177,9 +181,10 @@ function detectProjectRemote(path) {
177
181
  return m ? m[1] : null;
178
182
  }
179
183
 
180
- async function confirmOverwrite(label, path) {
184
+ async function confirmOverwrite(label, path, { autoConfirm = false } = {}) {
181
185
  if (!existsSync(path)) return true;
182
- const { ok } = await prompts({
186
+ if (autoConfirm) return true;
187
+ const { ok } = await initPrompt({
183
188
  type: 'confirm',
184
189
  name: 'ok',
185
190
  message: `${label} already exists at ${path}. Overwrite?`,
@@ -296,7 +301,21 @@ async function promptForProject(defaults = {}) {
296
301
  }
297
302
 
298
303
  // ─── init ────────────────────────────────────────────────────────────────────
299
- async function cmdInit() {
304
+ async function cmdInit(rest = []) {
305
+ const { values } = safeParseArgs({
306
+ args: rest,
307
+ options: {
308
+ host: { type: 'string', default: 'claude' },
309
+ yes: { type: 'boolean', default: false },
310
+ },
311
+ allowPositionals: false,
312
+ });
313
+ const host = values.host;
314
+ if (host !== 'claude' && host !== 'codex') {
315
+ log.err(`Unknown host "${host}". Use "claude" or "codex".`);
316
+ process.exit(2);
317
+ }
318
+
300
319
  log.info(kleur.bold('\ndevlog setup\n'));
301
320
  await preflight();
302
321
 
@@ -309,7 +328,12 @@ async function cmdInit() {
309
328
  voicePath: existsSync(GHOSTWRITER_VOICE_DIR) ? GHOSTWRITER_VOICE_DIR : '',
310
329
  };
311
330
 
312
- const answers = await prompts([
331
+ const answers = values.yes ? {
332
+ gitAuthor: defaults.gitAuthor,
333
+ githubUser: defaults.githubUser,
334
+ targetRepoName: defaults.targetRepoName,
335
+ voicePath: '',
336
+ } : await initPrompt([
313
337
  { type: 'text', name: 'gitAuthor', message: 'Your name (retained for backward compatibility; not currently rendered on entries):', initial: defaults.gitAuthor, validate: VALIDATORS.gitAuthor },
314
338
  { type: 'text', name: 'githubUser', message: 'Your GitHub username:', initial: defaults.githubUser, validate: VALIDATORS.githubUser },
315
339
  { type: 'text', name: 'targetRepoName', message: 'Name of the repo where dev logs will be published:', initial: defaults.targetRepoName, validate: VALIDATORS.targetRepoName },
@@ -318,24 +342,26 @@ async function cmdInit() {
318
342
 
319
343
  // Optionally register projects in a loop. First time defaults to "yes".
320
344
  const projects = [];
321
- let firstPrompt = true;
322
- for (;;) {
323
- const { add } = await prompts({
324
- type: 'confirm',
325
- name: 'add',
326
- message: firstPrompt ? 'Register a project now?' : 'Register another project?',
327
- initial: firstPrompt,
328
- }, { onCancel: () => process.exit(1) });
329
- firstPrompt = false;
330
- if (!add) break;
331
- const p = await promptForProject();
332
- if (projects.find((x) => x.key === p.key)) {
333
- log.warn(`Skipped (duplicate key): ${p.key}`);
334
- continue;
345
+ if (!values.yes) {
346
+ let firstPrompt = true;
347
+ for (;;) {
348
+ const { add } = await initPrompt({
349
+ type: 'confirm',
350
+ name: 'add',
351
+ message: firstPrompt ? 'Register a project now?' : 'Register another project?',
352
+ initial: firstPrompt,
353
+ }, { onCancel: () => process.exit(1) });
354
+ firstPrompt = false;
355
+ if (!add) break;
356
+ const p = await promptForProject();
357
+ if (projects.find((x) => x.key === p.key)) {
358
+ log.warn(`Skipped (duplicate key): ${p.key}`);
359
+ continue;
360
+ }
361
+ if (p.tagPrefix === 'v') delete p.tagPrefix;
362
+ projects.push(p);
363
+ log.ok(`Registered: ${p.key}`);
335
364
  }
336
- if (p.tagPrefix === 'v') delete p.tagPrefix;
337
- projects.push(p);
338
- log.ok(`Registered: ${p.key}`);
339
365
  }
340
366
 
341
367
  const targetRepo = `${answers.githubUser}/${answers.targetRepoName}`;
@@ -367,9 +393,16 @@ async function cmdInit() {
367
393
  log.info(` Branch: ${config.branch}`);
368
394
  log.info(` Voice profile: ${config.voicePath || '(ghostwriter if present, else bundled default)'}`);
369
395
  log.info(` Projects: ${config.projects.length === 0 ? '(none — add later with `devlog add-project`)' : config.projects.map((p) => p.key).join(', ')}`);
370
- log.info(` Skill location: ${CONFIG_DIR}`);
396
+ if (host === 'claude') {
397
+ log.info(` Skill location: ${CONFIG_DIR}`);
398
+ } else {
399
+ log.info(` Personal data: ${CONFIG_DIR}`);
400
+ log.info(' Host: Codex (marketplace plugin remains authoritative)');
401
+ }
371
402
 
372
- const { proceed } = await prompts({ type: 'confirm', name: 'proceed', message: 'Continue?', initial: true }, { onCancel: () => process.exit(1) });
403
+ const { proceed } = values.yes
404
+ ? { proceed: true }
405
+ : await initPrompt({ type: 'confirm', name: 'proceed', message: 'Continue?', initial: true }, { onCancel: () => process.exit(1) });
373
406
  if (!proceed) process.exit(0);
374
407
  log.info('');
375
408
 
@@ -392,24 +425,26 @@ async function cmdInit() {
392
425
  log.ok(`Created ${CONFIG_DIR}`);
393
426
  }
394
427
 
395
- if (await confirmOverwrite('SKILL.md', SKILL_DEST)) {
396
- // These are versioned instructions, not personal config/voice/style files.
397
- // Install references with the entrypoint so standalone hosts can resolve them.
398
- const references = join(PACKAGE_ROOT, 'references');
399
- const referenceDest = join(CONFIG_DIR, 'references');
400
- mkdirSync(referenceDest, { recursive: true, mode: 0o700 });
401
- for (const name of readdirSync(references)) {
402
- if (name.endsWith('.md') && statSync(join(references, name)).isFile()) {
403
- copyFileSync(join(references, name), join(referenceDest, name));
428
+ if (host === 'claude') {
429
+ if (await confirmOverwrite('SKILL.md', SKILL_DEST, { autoConfirm: values.yes })) {
430
+ // These are versioned instructions, not personal config/voice/style files.
431
+ // Install references with the entrypoint so standalone hosts can resolve them.
432
+ const references = join(PACKAGE_ROOT, 'references');
433
+ const referenceDest = join(CONFIG_DIR, 'references');
434
+ mkdirSync(referenceDest, { recursive: true, mode: 0o700 });
435
+ for (const name of readdirSync(references)) {
436
+ if (name.endsWith('.md') && statSync(join(references, name)).isFile()) {
437
+ copyFileSync(join(references, name), join(referenceDest, name));
438
+ }
404
439
  }
440
+ copyFileSync(SKILL_SRC, SKILL_DEST);
441
+ log.ok(`Installed SKILL.md → ${SKILL_DEST}`);
442
+ } else {
443
+ log.warn('Skipped SKILL.md');
405
444
  }
406
- copyFileSync(SKILL_SRC, SKILL_DEST);
407
- log.ok(`Installed SKILL.md → ${SKILL_DEST}`);
408
- } else {
409
- log.warn('Skipped SKILL.md');
410
445
  }
411
446
 
412
- if (await confirmOverwrite('config.json', CONFIG_PATH)) {
447
+ if (await confirmOverwrite('config.json', CONFIG_PATH, { autoConfirm: values.yes })) {
413
448
  atomicWriteJSON(CONFIG_PATH, config);
414
449
  log.ok(`Wrote config → ${CONFIG_PATH}`);
415
450
  }
@@ -423,7 +458,7 @@ async function cmdInit() {
423
458
  for (const [src, dest] of [['voice-profile.example.md', 'voice-profile.md'], ['voice-notes.example.md', 'voice-notes.md']]) {
424
459
  const s = join(VOICE_SRC_DIR, src);
425
460
  const d = join(VOICE_DEST_DIR, dest);
426
- if (existsSync(s) && (await confirmOverwrite(`voice/${dest}`, d))) {
461
+ if (existsSync(s) && (await confirmOverwrite(`voice/${dest}`, d, { autoConfirm: values.yes }))) {
427
462
  copyFileSync(s, d);
428
463
  log.ok(`Installed voice/${dest} → ${d}`);
429
464
  }
@@ -436,19 +471,19 @@ async function cmdInit() {
436
471
  }
437
472
  const styleGuideSrc = join(IMAGE_STYLE_SRC_DIR, 'style-guide.example.md');
438
473
  const styleGuideDest = join(IMAGE_STYLE_DEST_DIR, 'style-guide.md');
439
- if (existsSync(styleGuideSrc) && (await confirmOverwrite('image-style/style-guide.md', styleGuideDest))) {
474
+ if (existsSync(styleGuideSrc) && (await confirmOverwrite('image-style/style-guide.md', styleGuideDest, { autoConfirm: values.yes }))) {
440
475
  copyFileSync(styleGuideSrc, styleGuideDest);
441
476
  log.ok(`Installed image-style/style-guide.md → ${styleGuideDest}`);
442
477
  }
443
478
  const fontSrc = join(IMAGE_STYLE_SRC_DIR, 'font.ttf');
444
479
  const fontDest = join(IMAGE_STYLE_DEST_DIR, 'font.ttf');
445
- if (existsSync(fontSrc) && (await confirmOverwrite('image-style/font.ttf', fontDest))) {
480
+ if (existsSync(fontSrc) && (await confirmOverwrite('image-style/font.ttf', fontDest, { autoConfirm: values.yes }))) {
446
481
  copyFileSync(fontSrc, fontDest);
447
482
  log.ok(`Installed image-style/font.ttf → ${fontDest}`);
448
483
  }
449
484
  const iconsSrc = join(IMAGE_STYLE_SRC_DIR, 'icons.md');
450
485
  const iconsDest = join(IMAGE_STYLE_DEST_DIR, 'icons.md');
451
- if (existsSync(iconsSrc) && (await confirmOverwrite('image-style/icons.md', iconsDest))) {
486
+ if (existsSync(iconsSrc) && (await confirmOverwrite('image-style/icons.md', iconsDest, { autoConfirm: values.yes }))) {
452
487
  copyFileSync(iconsSrc, iconsDest);
453
488
  log.ok(`Installed image-style/icons.md → ${iconsDest}`);
454
489
  }
@@ -470,7 +505,13 @@ async function cmdInit() {
470
505
 
471
506
  log.info('\n' + kleur.bold().green('Setup complete.') + '\n');
472
507
  log.info('Next steps:');
473
- if (config.projects.length === 0) {
508
+ if (host === 'codex') {
509
+ log.info(' 1. The marketplace-installed Codex plugin remains the authoritative $devlog entrypoint.');
510
+ log.info(' 2. To install the plugin in Codex:');
511
+ log.info(` ${kleur.cyan('codex plugin marketplace add <marketplace>')}`);
512
+ log.info(` ${kleur.cyan('codex plugin add devlog@claude-skills')}`);
513
+ log.info(` 3. Preview locally: ${kleur.cyan('npx @natjswenson/devlog preview')}`);
514
+ } else if (config.projects.length === 0) {
474
515
  log.info(` 1. Add a project: ${kleur.cyan('npx @natjswenson/devlog add-project')}`);
475
516
  log.info(' 2. Tag a release in the project (e.g. `git tag v0.1.0`)');
476
517
  log.info(` 3. In Claude Code, run: ${kleur.cyan('/devlog')}`);
@@ -1333,7 +1374,7 @@ function printHelp() {
1333
1374
  ${kleur.bold('@natjswenson/devlog')} v${readPackageVersion()} — release dev log generator
1334
1375
 
1335
1376
  Setup & config:
1336
- ${kleur.cyan('npx @natjswenson/devlog init')} One-time setup: create your dev-log repo, install the skill, write config
1377
+ ${kleur.cyan('npx @natjswenson/devlog init [--host claude|codex] [--yes]')} One-time setup (defaults to Claude); Codex plugin is installed separately; --yes is non-interactive
1337
1378
  ${kleur.cyan('npx @natjswenson/devlog add-project')} Register a project (interactive; add --yes --path <p> for non-interactive)
1338
1379
  ${kleur.cyan('npx @natjswenson/devlog remove-project <key> --yes')} Unregister a project (entries stay published)
1339
1380
  ${kleur.cyan('npx @natjswenson/devlog set <field> <value>')} Update one config field (${SETTABLE_FIELDS.join(', ')})
@@ -1388,7 +1429,7 @@ if (isMain) {
1388
1429
  const rest = process.argv.slice(3);
1389
1430
  switch (arg) {
1390
1431
  case 'init':
1391
- cmdInit();
1432
+ cmdInit(rest);
1392
1433
  break;
1393
1434
  case 'add-project':
1394
1435
  cmdAddProject(rest);
@@ -166,7 +166,7 @@ site, not a marketing graphic and not a repeated template.
166
166
 
167
167
  ### Palette
168
168
 
169
- <!-- >>> press:palette v0.10.0 sha256:5904c52d4168 GENERATED by @natjswenson/press, do not edit -->
169
+ <!-- >>> press:palette v0.11.0 sha256:5904c52d4168 GENERATED by @natjswenson/press, do not edit -->
170
170
  - **Paper** `#F5F0E6` — Warm cream. Flat — never gradiented, never textured.
171
171
  - **Ink** `#181510` — Near-black. Text, headlines, and every structural rule.
172
172
  - **Dim** `#6E675C` — Muted secondary text; the serif commentary voice's color.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@natjswenson/devlog",
3
- "version": "0.14.1",
3
+ "version": "0.14.2",
4
4
  "description": "Release dev log generator — 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",