@open-press/cli 0.3.0 → 0.6.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.
Files changed (124) hide show
  1. package/dist/cli.js +125 -51
  2. package/package.json +1 -1
  3. package/template/core/CHANGELOG.md +73 -0
  4. package/template/core/engine/cli.mjs +6 -0
  5. package/template/core/engine/commands/_shared.mjs +9 -2
  6. package/template/core/engine/commands/deploy.mjs +3 -3
  7. package/template/core/engine/commands/dev.mjs +25 -2
  8. package/template/core/engine/commands/doctor.mjs +229 -0
  9. package/template/core/engine/commands/pdf.mjs +3 -3
  10. package/template/core/engine/commands/preview.mjs +4 -4
  11. package/template/core/engine/commands/upgrade.mjs +117 -0
  12. package/template/core/package.json +3 -1
  13. package/template/core/vite.config.ts +26 -11
  14. package/template/packs/academic-paper/document/chapters/01-introduction/content/01-introduction.mdx +21 -0
  15. package/template/packs/academic-paper/document/chapters/02-methods/content/01-methods.mdx +30 -0
  16. package/template/packs/academic-paper/document/chapters/03-results-and-discussion/content/01-results.mdx +29 -0
  17. package/template/packs/academic-paper/document/chapters/04-acknowledgment/content/01-acknowledgment.mdx +12 -0
  18. package/template/packs/academic-paper/document/chapters/05-references/content/01-references.mdx +27 -0
  19. package/template/packs/academic-paper/document/index.tsx +107 -0
  20. package/template/packs/academic-paper/document/openpress.config.mjs +26 -0
  21. package/template/packs/academic-paper/document/theme/page-surfaces/cover.css +267 -0
  22. package/template/{skills/editorial-monograph/starter → packs/editorial-monograph}/document/chapters/03-agent-skills-contributors/content/01-agent-skills-contributors.mdx +2 -3
  23. package/template/packs/editorial-monograph/document/components/ChapterOpenerVisual/index.tsx +76 -0
  24. package/template/packs/editorial-monograph/document/components/Page.tsx +27 -0
  25. package/template/packs/editorial-monograph/document/components/TokenSwatchGrid/index.tsx +46 -0
  26. package/template/packs/editorial-monograph/document/components/TokenSwatchGrid/style.css +63 -0
  27. package/template/packs/editorial-monograph/document/components/TypeSpecimen/index.tsx +38 -0
  28. package/template/packs/editorial-monograph/document/components/TypeSpecimen/style.css +111 -0
  29. package/template/packs/editorial-monograph/document/design.md +279 -0
  30. package/template/packs/editorial-monograph/document/media/README.md +13 -0
  31. package/template/packs/editorial-monograph/document/theme/README.md +11 -0
  32. package/template/packs/editorial-monograph/document/theme/base/page-contract.css +505 -0
  33. package/template/packs/editorial-monograph/document/theme/base/print.css +93 -0
  34. package/template/packs/editorial-monograph/document/theme/base/typography.css +336 -0
  35. package/template/packs/editorial-monograph/document/theme/fonts.css +3 -0
  36. package/template/packs/editorial-monograph/document/theme/page-surfaces/back-cover.css +43 -0
  37. package/template/packs/editorial-monograph/document/theme/page-surfaces/chapter-opener.css +205 -0
  38. package/template/packs/editorial-monograph/document/theme/page-surfaces/toc.css +139 -0
  39. package/template/packs/editorial-monograph/document/theme/patterns/_chart-frame.css +49 -0
  40. package/template/packs/editorial-monograph/document/theme/patterns/figure-grid.css +68 -0
  41. package/template/packs/editorial-monograph/document/theme/patterns/table-utilities.css +66 -0
  42. package/template/packs/editorial-monograph/document/theme/shell/reader-controls.css +761 -0
  43. package/template/packs/editorial-monograph/document/theme/tokens.css +80 -0
  44. package/template/packs/editorial-monograph/openpress.config.mjs +5 -0
  45. package/template/core/.turbo/turbo-test.log +0 -341
  46. package/template/skills/chinese-ai-writing-polish/SKILL.md +0 -195
  47. package/template/skills/claude-document/SKILL.md +0 -66
  48. package/template/skills/editorial-monograph/SKILL.md +0 -73
  49. package/template/skills/openpress/SKILL.md +0 -114
  50. package/template/skills/openpress/references/cli-commands.md +0 -31
  51. package/template/skills/openpress/references/local-review.md +0 -43
  52. package/template/skills/openpress-deploy/SKILL.md +0 -69
  53. package/template/skills/openpress-deploy/references/cloudflare-pages.md +0 -51
  54. package/template/skills/openpress-design/SKILL.md +0 -51
  55. package/template/skills/openpress-design/references/pdf-safe-css.md +0 -29
  56. package/template/skills/openpress-design/references/responsive-fixed-layout.md +0 -48
  57. package/template/skills/openpress-design/references/theme-and-components.md +0 -77
  58. package/template/skills/openpress-diagram-drawing/SKILL.md +0 -44
  59. package/template/skills/openpress-diagram-drawing/references/diagram-patterns.md +0 -93
  60. package/template/skills/openpress-document-hierarchy/SKILL.md +0 -81
  61. package/template/skills/openpress-document-hierarchy/agents/openai.yaml +0 -4
  62. package/template/skills/openpress-document-hierarchy/references/data-structures-outline.md +0 -115
  63. package/template/skills/openpress-init/SKILL.md +0 -84
  64. package/template/skills/openpress-style-pack-contributor/SKILL.md +0 -62
  65. package/template/skills/openpress-style-pack-contributor/references/starter-contract.md +0 -49
  66. package/template/skills/openpress-update/SKILL.md +0 -88
  67. package/template/skills/openpress-writing/SKILL.md +0 -68
  68. package/template/skills/openpress-writing/references/source-and-writing-rules.md +0 -120
  69. package/template/skills/teaching-notes-writing/SKILL.md +0 -54
  70. package/template/skills/teaching-notes-writing/references/programming.md +0 -65
  71. package/template/skills/teaching-notes-writing/references/teaching-patterns.md +0 -60
  72. /package/template/{skills/editorial-monograph/starter → packs/academic-paper}/document/components/ChapterOpenerVisual/index.tsx +0 -0
  73. /package/template/{skills/claude-document/starter → packs/academic-paper}/document/components/Page.tsx +0 -0
  74. /package/template/{skills/editorial-monograph/starter → packs/academic-paper}/document/components/TokenSwatchGrid/index.tsx +0 -0
  75. /package/template/{skills/editorial-monograph/starter → packs/academic-paper}/document/components/TokenSwatchGrid/style.css +0 -0
  76. /package/template/{skills/editorial-monograph/starter → packs/academic-paper}/document/components/TypeSpecimen/index.tsx +0 -0
  77. /package/template/{skills/editorial-monograph/starter → packs/academic-paper}/document/components/TypeSpecimen/style.css +0 -0
  78. /package/template/{skills/editorial-monograph/starter → packs/academic-paper}/document/design.md +0 -0
  79. /package/template/{skills/claude-document/starter → packs/academic-paper}/document/media/README.md +0 -0
  80. /package/template/{skills/editorial-monograph/starter → packs/academic-paper}/document/theme/README.md +0 -0
  81. /package/template/{skills/editorial-monograph/starter → packs/academic-paper}/document/theme/base/page-contract.css +0 -0
  82. /package/template/{skills/editorial-monograph/starter → packs/academic-paper}/document/theme/base/print.css +0 -0
  83. /package/template/{skills/editorial-monograph/starter → packs/academic-paper}/document/theme/base/typography.css +0 -0
  84. /package/template/{skills/editorial-monograph/starter → packs/academic-paper}/document/theme/fonts.css +0 -0
  85. /package/template/{skills/editorial-monograph/starter → packs/academic-paper}/document/theme/page-surfaces/back-cover.css +0 -0
  86. /package/template/{skills/editorial-monograph/starter → packs/academic-paper}/document/theme/page-surfaces/chapter-opener.css +0 -0
  87. /package/template/{skills/editorial-monograph/starter → packs/academic-paper}/document/theme/page-surfaces/toc.css +0 -0
  88. /package/template/{skills/editorial-monograph/starter → packs/academic-paper}/document/theme/patterns/_chart-frame.css +0 -0
  89. /package/template/{skills/claude-document/starter → packs/academic-paper}/document/theme/patterns/figure-grid.css +0 -0
  90. /package/template/{skills/editorial-monograph/starter → packs/academic-paper}/document/theme/patterns/table-utilities.css +0 -0
  91. /package/template/{skills/editorial-monograph/starter → packs/academic-paper}/document/theme/shell/reader-controls.css +0 -0
  92. /package/template/{skills/editorial-monograph/starter → packs/academic-paper}/document/theme/tokens.css +0 -0
  93. /package/template/{skills/editorial-monograph/starter → packs/academic-paper}/openpress.config.mjs +0 -0
  94. /package/template/{skills/claude-document/starter → packs/claude-document}/document/chapters/01-document-shape/chapter.tsx +0 -0
  95. /package/template/{skills/claude-document/starter → packs/claude-document}/document/chapters/01-document-shape/content/01-document-shape.mdx +0 -0
  96. /package/template/{skills/claude-document/starter → packs/claude-document}/document/chapters/02-review-loop/chapter.tsx +0 -0
  97. /package/template/{skills/claude-document/starter → packs/claude-document}/document/chapters/02-review-loop/content/01-review-loop.mdx +0 -0
  98. /package/template/{skills/claude-document/starter → packs/claude-document}/document/components/ChapterOpenerVisual.tsx +0 -0
  99. /package/template/{skills/editorial-monograph/starter → packs/claude-document}/document/components/Page.tsx +0 -0
  100. /package/template/{skills/claude-document/starter → packs/claude-document}/document/design.md +0 -0
  101. /package/template/{skills/claude-document/starter → packs/claude-document}/document/index.tsx +0 -0
  102. /package/template/{skills/editorial-monograph/starter → packs/claude-document}/document/media/README.md +0 -0
  103. /package/template/{skills/claude-document/starter → packs/claude-document}/document/openpress.config.mjs +0 -0
  104. /package/template/{skills/claude-document/starter → packs/claude-document}/document/theme/README.md +0 -0
  105. /package/template/{skills/claude-document/starter → packs/claude-document}/document/theme/base/page-contract.css +0 -0
  106. /package/template/{skills/claude-document/starter → packs/claude-document}/document/theme/base/print.css +0 -0
  107. /package/template/{skills/claude-document/starter → packs/claude-document}/document/theme/base/typography.css +0 -0
  108. /package/template/{skills/claude-document/starter → packs/claude-document}/document/theme/fonts.css +0 -0
  109. /package/template/{skills/claude-document/starter → packs/claude-document}/document/theme/page-surfaces/back-cover.css +0 -0
  110. /package/template/{skills/claude-document/starter → packs/claude-document}/document/theme/page-surfaces/chapter-opener.css +0 -0
  111. /package/template/{skills/claude-document/starter → packs/claude-document}/document/theme/page-surfaces/cover.css +0 -0
  112. /package/template/{skills/claude-document/starter → packs/claude-document}/document/theme/page-surfaces/toc.css +0 -0
  113. /package/template/{skills/claude-document/starter → packs/claude-document}/document/theme/patterns/_chart-frame.css +0 -0
  114. /package/template/{skills/editorial-monograph/starter → packs/claude-document}/document/theme/patterns/figure-grid.css +0 -0
  115. /package/template/{skills/claude-document/starter → packs/claude-document}/document/theme/patterns/table-utilities.css +0 -0
  116. /package/template/{skills/claude-document/starter → packs/claude-document}/document/theme/shell/reader-controls.css +0 -0
  117. /package/template/{skills/claude-document/starter → packs/claude-document}/document/theme/tokens.css +0 -0
  118. /package/template/{skills/claude-document/starter → packs/claude-document}/openpress.config.mjs +0 -0
  119. /package/template/{skills/editorial-monograph/starter → packs/editorial-monograph}/document/chapters/01-product-and-use-cases/content/01-product-and-use-cases.mdx +0 -0
  120. /package/template/{skills/editorial-monograph/starter → packs/editorial-monograph}/document/chapters/02-workflow/content/01-workflow.mdx +0 -0
  121. /package/template/{skills/editorial-monograph/starter → packs/editorial-monograph}/document/chapters/04-validation-deploy/content/01-validation-deploy.mdx +0 -0
  122. /package/template/{skills/editorial-monograph/starter → packs/editorial-monograph}/document/index.tsx +0 -0
  123. /package/template/{skills/editorial-monograph/starter → packs/editorial-monograph}/document/openpress.config.mjs +0 -0
  124. /package/template/{skills/editorial-monograph/starter → packs/editorial-monograph}/document/theme/page-surfaces/cover.css +0 -0
package/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import process2 from "process";
6
6
  // src/init.ts
7
7
  import { spawn } from "child_process";
8
8
  import { existsSync } from "fs";
9
- import { cp, mkdir as mkdir2, readdir, rm as rm2 } from "fs/promises";
9
+ import { cp, mkdir as mkdir2, rm as rm2 } from "fs/promises";
10
10
  import path2 from "path";
11
11
  import process from "process";
12
12
  import { fileURLToPath } from "url";
@@ -19,12 +19,47 @@ import path from "path";
19
19
  import { Readable } from "stream";
20
20
  import { pipeline } from "stream/promises";
21
21
  import { x as extract } from "tar";
22
+ async function degit({ owner, repo, ref = "main", dest, subdir }) {
23
+ const url = `https://codeload.github.com/${owner}/${repo}/tar.gz/refs/heads/${ref}`;
24
+ const tmpDir = await mkdir(path.join(tmpdir(), `open-press-degit-${Date.now()}`), { recursive: true });
25
+ const tarballPath = path.join(tmpDir, "repo.tar.gz");
26
+ try {
27
+ await fetchTo(url, tarballPath);
28
+ await mkdir(dest, { recursive: true });
29
+ const subdirSegments = subdir ? subdir.split("/").filter(Boolean).length : 0;
30
+ const totalStrip = 1 + subdirSegments;
31
+ const filterPrefix = subdir ? subdir.replace(/\/$/, "") + "/" : null;
32
+ await extract({
33
+ file: tarballPath,
34
+ cwd: dest,
35
+ strip: totalStrip,
36
+ filter: (filePath) => {
37
+ const segments = filePath.split("/");
38
+ const inside = segments.slice(1).join("/");
39
+ if (filterPrefix) {
40
+ return inside.startsWith(filterPrefix);
41
+ }
42
+ return true;
43
+ }
44
+ });
45
+ } finally {
46
+ await rm(tmpDir, { recursive: true, force: true }).catch(() => {
47
+ });
48
+ }
49
+ }
50
+ async function fetchTo(url, destFile) {
51
+ const res = await fetch(url, { redirect: "follow" });
52
+ if (!res.ok || !res.body) {
53
+ throw new Error(`Failed to fetch ${url}: ${res.status} ${res.statusText}`);
54
+ }
55
+ await pipeline(Readable.fromWeb(res.body), createWriteStream(destFile));
56
+ }
22
57
  async function pathIsEmpty(target) {
23
58
  try {
24
59
  const s = await stat(target);
25
60
  if (!s.isDirectory()) return false;
26
- const { readdir: readdir2 } = await import("fs/promises");
27
- const entries = await readdir2(target);
61
+ const { readdir } = await import("fs/promises");
62
+ const entries = await readdir(target);
28
63
  return entries.length === 0;
29
64
  } catch {
30
65
  return true;
@@ -80,13 +115,14 @@ async function patchPackageJsonName(packagePath, newName) {
80
115
  }
81
116
 
82
117
  // src/init.ts
83
- var KNOWN_PACKS = ["editorial-monograph", "claude-document"];
118
+ var BUNDLED_PACKS = ["editorial-monograph", "claude-document", "academic-paper"];
119
+ var FRAMEWORK_SKILLS_SOURCE = "quan0715/open-press";
84
120
  var __dirname = path2.dirname(fileURLToPath(import.meta.url));
85
121
  var TEMPLATE_ROOT = path2.resolve(__dirname, "..", "template");
86
122
  var TEMPLATE_CORE = path2.join(TEMPLATE_ROOT, "core");
87
- var TEMPLATE_SKILLS = path2.join(TEMPLATE_ROOT, "skills");
123
+ var TEMPLATE_PACKS = path2.join(TEMPLATE_ROOT, "packs");
88
124
  async function init(options) {
89
- validatePack(options.pack);
125
+ const packSpec = options.pack ? parsePackSpec(options.pack) : null;
90
126
  ensureTemplateBundled();
91
127
  const target = path2.resolve(process.cwd(), options.target);
92
128
  await ensureTarget(target, options.force);
@@ -94,20 +130,41 @@ async function init(options) {
94
130
  log("Copying framework (engine + runtime + config)\u2026");
95
131
  await cp(TEMPLATE_CORE, target, { recursive: true });
96
132
  const docDest = path2.join(target, "document");
97
- if (options.pack) {
98
- log(`Applying style pack: ${options.pack}`);
99
- await rm2(docDest, { recursive: true, force: true });
100
- await mkdir2(docDest, { recursive: true });
101
- const packStarter = path2.join(TEMPLATE_SKILLS, options.pack, "starter", "document");
133
+ await rm2(docDest, { recursive: true, force: true });
134
+ await mkdir2(docDest, { recursive: true });
135
+ if (packSpec?.kind === "bundled") {
136
+ log(`Applying bundled style pack: ${packSpec.name}`);
137
+ const packStarter = path2.join(TEMPLATE_PACKS, packSpec.name, "document");
102
138
  if (!existsSync(packStarter)) {
103
- throw new Error(`Style pack starter not found: ${packStarter}`);
139
+ throw new Error(`Bundled style pack starter not found: ${packStarter}`);
104
140
  }
105
141
  await cp(packStarter, docDest, { recursive: true });
106
- } else {
107
- await mkdir2(docDest, { recursive: true });
142
+ } else if (packSpec?.kind === "github") {
143
+ log(`Fetching style pack from github:${packSpec.owner}/${packSpec.repo}${packSpec.ref ? `#${packSpec.ref}` : ""}\u2026`);
144
+ try {
145
+ await degit({
146
+ owner: packSpec.owner,
147
+ repo: packSpec.repo,
148
+ ref: packSpec.ref,
149
+ dest: docDest,
150
+ subdir: "starter/document"
151
+ });
152
+ } catch (err) {
153
+ throw new Error(
154
+ `Failed to fetch pack from github:${packSpec.owner}/${packSpec.repo}: ${err instanceof Error ? err.message : String(err)}`
155
+ );
156
+ }
157
+ if (await pathIsEmpty(docDest)) {
158
+ throw new Error(
159
+ `github:${packSpec.owner}/${packSpec.repo} doesn't contain starter/document/ at the repo root.
160
+ Third-party pack repos should follow this layout:
161
+ <repo>/
162
+ \u251C\u2500\u2500 starter/
163
+ \u2502 \u2514\u2500\u2500 document/ \u2190 cli copies this into your workspace's document/
164
+ \u2514\u2500\u2500 skills/<pack>/SKILL.md \u2190 npx skills add picks this up`
165
+ );
166
+ }
108
167
  }
109
- log("Installing SKILL files\u2026");
110
- await installSkills(target);
111
168
  const pkgPath = path2.join(target, "package.json");
112
169
  if (existsSync(pkgPath)) {
113
170
  await patchPackageJsonName(pkgPath, path2.basename(target));
@@ -122,6 +179,23 @@ async function init(options) {
122
179
  author: options.author
123
180
  });
124
181
  }
182
+ log(`Installing framework skills via \`npx skills add ${FRAMEWORK_SKILLS_SOURCE}\`\u2026`);
183
+ try {
184
+ await runInTarget(target, "npx", ["-y", "skills@latest", "add", FRAMEWORK_SKILLS_SOURCE]);
185
+ } catch (err) {
186
+ log(`(framework skills install failed; retry: npx skills add ${FRAMEWORK_SKILLS_SOURCE})`);
187
+ log(` reason: ${err instanceof Error ? err.message : String(err)}`);
188
+ }
189
+ if (packSpec?.kind === "github") {
190
+ const packSource = `${packSpec.owner}/${packSpec.repo}`;
191
+ log(`Installing pack skills via \`npx skills add ${packSource}\`\u2026`);
192
+ try {
193
+ await runInTarget(target, "npx", ["-y", "skills@latest", "add", packSource]);
194
+ } catch (err) {
195
+ log(`(pack skills install failed; retry: npx skills add ${packSource})`);
196
+ log(` reason: ${err instanceof Error ? err.message : String(err)}`);
197
+ }
198
+ }
125
199
  if (options.install) {
126
200
  log("Installing dependencies (npm install)\u2026");
127
201
  await runInTarget(target, "npm", ["install"]);
@@ -142,44 +216,33 @@ async function init(options) {
142
216
  }
143
217
  printNextSteps(target, options);
144
218
  }
145
- async function installSkills(target) {
146
- const skillDirs = await readdir(TEMPLATE_SKILLS, { withFileTypes: true });
147
- const claudeRoot = path2.join(target, ".claude", "skills");
148
- const agentsRoot = path2.join(target, ".agents", "skills");
149
- await mkdir2(claudeRoot, { recursive: true });
150
- await mkdir2(agentsRoot, { recursive: true });
151
- for (const dir of skillDirs) {
152
- if (!dir.isDirectory()) continue;
153
- const source = path2.join(TEMPLATE_SKILLS, dir.name);
154
- const claudeTarget = path2.join(claudeRoot, dir.name);
155
- const agentsTarget = path2.join(agentsRoot, dir.name);
156
- await copySkillFiles(source, claudeTarget);
157
- await copySkillFiles(source, agentsTarget);
219
+ function parsePackSpec(spec) {
220
+ if (spec.startsWith("github:")) {
221
+ const rest = spec.slice("github:".length);
222
+ const [pathPart, ref] = rest.split("#");
223
+ const segments = pathPart.split("/").filter(Boolean);
224
+ if (segments.length !== 2) {
225
+ throw new Error(
226
+ `Invalid --pack spec: "${spec}". Use github:owner/repo or github:owner/repo#ref.`
227
+ );
228
+ }
229
+ const [owner, repo] = segments;
230
+ return { kind: "github", owner, repo, ref: ref?.trim() || void 0 };
158
231
  }
159
- }
160
- async function copySkillFiles(source, dest) {
161
- await mkdir2(dest, { recursive: true });
162
- const entries = await readdir(source, { withFileTypes: true });
163
- for (const entry of entries) {
164
- if (entry.name === "starter" || entry.name === "agents") continue;
165
- const from = path2.join(source, entry.name);
166
- const to = path2.join(dest, entry.name);
167
- await cp(from, to, { recursive: true });
232
+ if (!BUNDLED_PACKS.includes(spec)) {
233
+ throw new Error(
234
+ `Unknown style pack: "${spec}". Bundled packs: ${BUNDLED_PACKS.join(", ")}. For third-party packs use github:owner/repo (e.g. github:quan0715/openpress-pack-nycu-thesis).`
235
+ );
168
236
  }
237
+ return { kind: "bundled", name: spec };
169
238
  }
170
239
  function ensureTemplateBundled() {
171
- if (!existsSync(TEMPLATE_CORE) || !existsSync(TEMPLATE_SKILLS)) {
240
+ if (!existsSync(TEMPLATE_CORE) || !existsSync(TEMPLATE_PACKS)) {
172
241
  throw new Error(
173
242
  `Template not bundled at ${TEMPLATE_ROOT}. If running from source, run \`pnpm sync:template\` in packages/cli first.`
174
243
  );
175
244
  }
176
245
  }
177
- function validatePack(pack) {
178
- if (pack === void 0) return;
179
- if (!KNOWN_PACKS.includes(pack)) {
180
- throw new Error(`Unknown style pack: ${pack}. Known packs: ${KNOWN_PACKS.join(", ")}`);
181
- }
182
- }
183
246
  async function ensureTarget(target, force) {
184
247
  if (existsSync(target)) {
185
248
  if (force) return;
@@ -216,7 +279,7 @@ function printNextSteps(target, options) {
216
279
  const rel = path2.relative(process.cwd(), target) || ".";
217
280
  const lines = [
218
281
  "",
219
- "\u2713 Done! Your open-press workspace is ready.",
282
+ "\u2713 Done. Your open-press workspace is ready.",
220
283
  "",
221
284
  "Next steps:",
222
285
  ` cd ${rel}`
@@ -229,9 +292,11 @@ function printNextSteps(target, options) {
229
292
  " # start the workbench:",
230
293
  " npm run dev",
231
294
  "",
232
- "Then open the local URL printed by vite (typically http://127.0.0.1:5173/?dev=1).",
295
+ "Then open the local URL printed by Vite (typically http://127.0.0.1:5173/?dev=1).",
296
+ "",
297
+ "Agent skills installed under .agents/skills/ (universal \u2014 read by Claude Code,",
298
+ `Cursor, Codex, Gemini CLI, etc.). Update later with: npx skills upgrade`,
233
299
  "",
234
- "AI agent skills are installed under .claude/skills/ and .agents/skills/.",
235
300
  "Edit content under document/chapters/, or ask your AI agent to help.",
236
301
  ""
237
302
  );
@@ -245,19 +310,28 @@ Usage:
245
310
  npx @open-press/cli init <target> [flags]
246
311
 
247
312
  Flags:
248
- --pack <name> Style pack starter: editorial-monograph | claude-document
313
+ --pack <spec> Style pack source. Either:
314
+ \u2022 a bundled name \u2014 editorial-monograph | claude-document | academic-paper
315
+ \u2022 github:owner/repo (third-party pack)
316
+ \u2022 github:owner/repo#branch-or-tag
249
317
  --title <s> Document title (written to openpress.config.mjs)
250
318
  --subtitle <s> Document subtitle
251
319
  --organization <s> Organization name
252
- --author <s> Author name (defaults to git user.name)
320
+ --author <s> Author name
253
321
  --no-git Skip git init
254
322
  --no-install Skip npm install
255
323
  --force Allow non-empty target
256
324
  --help Show this help
257
325
 
258
326
  Examples:
327
+ # Bundled
259
328
  npx @open-press/cli init my-doc --pack editorial-monograph
260
- npx @open-press/cli init my-doc --pack claude-document --title "Q2 Brief" --author Quan
329
+ npx @open-press/cli init my-brief --pack claude-document --title "Q2 Brief" --author Quan
330
+ npx @open-press/cli init my-paper --pack academic-paper --title "Paper Title" --author "First Author"
331
+
332
+ # Third-party (any GitHub repo with starter/document/ at the root)
333
+ npx @open-press/cli init my-thesis --pack github:quan0715/openpress-pack-nycu-thesis
334
+ npx @open-press/cli init my-paper --pack github:foo/their-pack#v1.2
261
335
  `;
262
336
  async function main(argv) {
263
337
  if (argv.length === 0 || argv.includes("--help") || argv.includes("-h")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-press/cli",
3
- "version": "0.3.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "description": "Scaffolder for open-press — AI-first fixed-layout document workspaces.",
6
6
  "license": "MIT",
@@ -1,5 +1,78 @@
1
1
  # @open-press/core
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - f8fdecd: Third bundled pack: `academic-paper`.
8
+
9
+ A single-column A4 academic / research paper starter — serif title block, abstract band, index terms, numbered sections (I, II, III), italic sub-sections (A, B, C), `[N]` numeric references, sample chapters derived from the IEEE conference template structure (Introduction, Methods, Results & Discussion, Acknowledgment, References).
10
+
11
+ ```bash
12
+ npx @open-press/cli init my-paper --pack academic-paper
13
+ ```
14
+
15
+ Suitable for: draft / preprint / iteration. Not suitable for camera-ready IEEE / ACM submission — those still need LaTeX with the publisher's class file.
16
+
17
+ Two-column body and other paged-document features (footnotes, cross-references with page numbers, running headers) are intentionally **out of scope for this release**. They'll be designed as a self-maintained engine evolution + multi-mode architecture in a separate spec round, rather than depending on a third-party pagination polyfill.
18
+
19
+ - c490653: `@open-press/cli init` accepts third-party style packs via `--pack github:owner/repo`.
20
+
21
+ ```bash
22
+ # bundled (unchanged)
23
+ npx @open-press/cli init my-doc --pack editorial-monograph
24
+
25
+ # third-party (new)
26
+ npx @open-press/cli init my-thesis --pack github:quan0715/openpress-pack-nycu-thesis
27
+ npx @open-press/cli init my-paper --pack github:foo/their-pack#v1.2
28
+ ```
29
+
30
+ The cli fetches `starter/document/` from the named repo (default branch, or `#ref` for a specific branch/tag) and copies it into the new workspace. If the pack repo also publishes SKILL files at `skills/<name>/`, they're installed via `npx skills add <owner>/<repo>` after the framework skills, so the agent picks them up automatically.
31
+
32
+ Repo layout convention for third-party packs is documented in `docs/style-pack-authoring.md`. Empty-result extraction (the named repo exists but has no `starter/document/` at root) fails with a clear error pointing at the expected layout.
33
+
34
+ The two bundled packs (`editorial-monograph`, `claude-document`) keep their current short-name behaviour; only the cli's validator widened to accept the `github:` prefix.
35
+
36
+ ## 0.5.0
37
+
38
+ ### Minor Changes
39
+
40
+ - 0169cba: Agent-driven upgrade flow.
41
+
42
+ **New commands:**
43
+ - `npx open-press doctor` — diagnose workspace against latest framework state. Reports `@open-press/core` version vs npm latest, installed skill count, and any pending `docs/migrations/<version>.md` notes between current and latest. `--json` for machine-readable output, `--no-cache` to bypass the 24h cache. Always exits 0 (informational only).
44
+
45
+ - `npx open-press upgrade` — orchestrate the upgrade. Runs `npm update @open-press/core` (when the workspace declares the dep) and `npx skills upgrade`, then surfaces the list of migration notes for the agent to read. **Does not auto-edit `document/` content** — the agent reads the surfaced `docs/migrations/<version>.md` notes and proposes edits to the user with confirmation. Use `--dry-run` to preview, `--no-deps` / `--no-skills` to target one layer.
46
+
47
+ **Dev startup notice:**
48
+
49
+ `open-press dev` now runs `doctor` before starting Vite. When the workspace is behind, a single line prints: `○ open-press: @open-press/core 0.4.0 → 0.5.0 · 1 migration note(s) — run npx open-press doctor for details.` Cached for 24h, network failure is silent, never blocks dev.
50
+
51
+ **Migration docs:**
52
+ - New `docs/migrations/_template.md` — each release with breaking changes ships a `docs/migrations/<version>.md` file with sections the agent reads.
53
+ - New `docs/migrations/0.4.0.md` — backfilled. Documents the SKILL fold (no document or CLI changes).
54
+
55
+ **SKILL update:**
56
+
57
+ `openpress` skill's "Updating An Existing Workspace" section rewritten around the new commands: detect (`doctor`), apply (`upgrade`), interpret migration notes, propose document edits with user confirmation. Concrete agent workflow + breaking-change reference table.
58
+
59
+ ### Patch Changes
60
+
61
+ - 931d4ac: Support framework root dogfood workspaces and correct CLI script paths outside the core package root.
62
+
63
+ ## 0.4.0
64
+
65
+ ### Minor Changes
66
+
67
+ - 3cb4939: Consolidate internal skills (13 → 11).
68
+ - `openpress-update` folded into `openpress` as an "Updating An Existing Workspace" section. The release-upgrade flow, pre-flight checks, breaking-change reference, and do-not list are now part of the system-operation skill where they naturally belong.
69
+ - `openpress-document-hierarchy` folded into `openpress-writing` as a "Hierarchy" section. Hierarchy decisions (H2/H3/H4 model, TOC depth, appendix placement, H4 granularity) and prose decisions happen in the same workflow; one skill, one routing decision.
70
+ - `references/data-structures-outline.md` moved from the hierarchy skill into `openpress-writing/references/`.
71
+
72
+ Lower maintenance surface: 2 fewer SKILL.md files to keep in sync, ~5 fewer cross-references to police. No content lost — same rules, fewer files.
73
+
74
+ User impact: agents already in workspaces with `openpress-update` or `openpress-document-hierarchy` SKILL files installed should run `npx skills upgrade` to refresh the catalog.
75
+
3
76
  ## 0.3.0
4
77
 
5
78
  ### Minor Changes
@@ -2,6 +2,7 @@
2
2
 
3
3
  import * as deployCmd from "./commands/deploy.mjs";
4
4
  import * as devCmd from "./commands/dev.mjs";
5
+ import * as doctorCmd from "./commands/doctor.mjs";
5
6
  import * as exportCmd from "./commands/export.mjs";
6
7
  import * as initCmd from "./commands/init.mjs";
7
8
  import * as inspectCmd from "./commands/inspect.mjs";
@@ -12,6 +13,7 @@ import * as replaceCmd from "./commands/replace.mjs";
12
13
  import * as renderCmd from "./commands/render.mjs";
13
14
  import * as searchCmd from "./commands/search.mjs";
14
15
  import * as typecheckCmd from "./commands/typecheck.mjs";
16
+ import * as upgradeCmd from "./commands/upgrade.mjs";
15
17
  import * as validateCmd from "./commands/validate.mjs";
16
18
  import { parseOptions } from "./commands/_shared.mjs";
17
19
  import { loadConfig } from "./config.mjs";
@@ -32,6 +34,8 @@ const COMMANDS = {
32
34
  typecheck: typecheckCmd,
33
35
  pdf: pdfCmd,
34
36
  deploy: deployCmd,
37
+ doctor: doctorCmd,
38
+ upgrade: upgradeCmd,
35
39
  };
36
40
 
37
41
  const args = process.argv.slice(2);
@@ -87,6 +91,8 @@ Commands:
87
91
  typecheck
88
92
  pdf [--output <outputDir>/<pdf.filename>] [--no-build] [--dry-run]
89
93
  deploy --confirm [--dry-run]
94
+ doctor [--json] [--no-cache] # version + skill staleness check
95
+ upgrade [--dry-run] [--no-deps] [--no-skills] [--json] # apply updates; agent-driven
90
96
 
91
97
  Style packs available for \`init --skill\`: ${skillList}
92
98
  `);
@@ -7,8 +7,9 @@ import { loadConfig, publicPdfHref } from "../config.mjs";
7
7
  import { exportDocument } from "../document-export.mjs";
8
8
  import { optimizePdfMediaForStaticRoot } from "../pdf-media.mjs";
9
9
 
10
- const ENGINE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
11
- const STATIC_SERVER = path.join(ENGINE_DIR, "static-server.mjs");
10
+ export const ENGINE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
11
+ export const CLI_ENTRY = path.join(ENGINE_DIR, "cli.mjs");
12
+ export const STATIC_SERVER = path.join(ENGINE_DIR, "static-server.mjs");
12
13
 
13
14
  export function parseOptions(argv) {
14
15
  const options = {};
@@ -62,6 +63,12 @@ export function runCommand(commandName, commandArgs, cwd) {
62
63
  return result.status ?? 1;
63
64
  }
64
65
 
66
+ export function formatNodeScriptCommand(root, scriptPath) {
67
+ const relative = path.relative(root, scriptPath).replaceAll("\\", "/");
68
+ const displayPath = relative && !relative.startsWith("../") ? relative : scriptPath;
69
+ return `node ${displayPath}`;
70
+ }
71
+
65
72
  export async function buildReactStatic({ root, noBuild = false, recurse, silent = false }) {
66
73
  if (noBuild) return 0;
67
74
  if (!silent) {
@@ -1,6 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { deploySync } from "../deploy-sync.mjs";
3
- import { buildReactPdf, runCommand, writePdfStageDeployConfig } from "./_shared.mjs";
3
+ import { CLI_ENTRY, buildReactPdf, formatNodeScriptCommand, runCommand, writePdfStageDeployConfig } from "./_shared.mjs";
4
4
 
5
5
  export async function run({ root, config, options, recurse }) {
6
6
  if (config.deploy.requiresConfirmation === true && !options.confirm) {
@@ -12,9 +12,9 @@ export async function run({ root, config, options, recurse }) {
12
12
  const commitDirty = config.deploy.commitDirty;
13
13
  if (options.dryRun) {
14
14
  console.log("OpenPress deploy dry run");
15
- console.log("Command: node engine/cli.mjs render . --renderer react");
15
+ console.log(`Command: ${formatNodeScriptCommand(root, CLI_ENTRY)} render . --renderer react`);
16
16
  console.log(`Step: deploy-sync (copy ${config.outputDir} → ${source})`);
17
- console.log(`Command: node engine/cli.mjs pdf . --output ${source}/${config.pdf.filename}`);
17
+ console.log(`Command: ${formatNodeScriptCommand(root, CLI_ENTRY)} pdf . --output ${source}/${config.pdf.filename}`);
18
18
  console.log(`Step: write ${source}/openpress/deploy.json with deployment metadata`);
19
19
  console.log(`Command: npx wrangler pages deploy ${source}${projectName ? ` --project-name=${projectName}` : ""}${commitDirty ? " --commit-dirty=true" : ""}`);
20
20
  return 0;
@@ -1,5 +1,6 @@
1
1
  import { exportDocument } from "../document-export.mjs";
2
- import { runCommand } from "./_shared.mjs";
2
+ import { diagnose } from "./doctor.mjs";
3
+ import { CLI_ENTRY, formatNodeScriptCommand, runCommand } from "./_shared.mjs";
3
4
 
4
5
  export async function run({ root, options }) {
5
6
  const renderer = options.renderer ?? "react";
@@ -13,7 +14,7 @@ export async function run({ root, options }) {
13
14
  if (options.dryRun) {
14
15
  console.log(`OpenPress dev URL: ${url}`);
15
16
  if (!options.noBuild) {
16
- console.log("Command: node engine/cli.mjs export .");
17
+ console.log(`Command: ${formatNodeScriptCommand(root, CLI_ENTRY)} export .`);
17
18
  }
18
19
  console.log(`Command: npx vite --config vite.config.ts --host ${host} --port ${port}`);
19
20
  return 0;
@@ -21,6 +22,28 @@ export async function run({ root, options }) {
21
22
  if (!options.noBuild) {
22
23
  await exportDocument(root);
23
24
  }
25
+
26
+ // One-line update notice (24h cached, network failure is silent).
27
+ await printDoctorNoticeIfStale(root);
28
+
24
29
  console.log(`OpenPress dev: ${url}`);
25
30
  return runCommand("npx", ["vite", "--config", "vite.config.ts", "--host", host, "--port", port], root);
26
31
  }
32
+
33
+ async function printDoctorNoticeIfStale(root) {
34
+ try {
35
+ const report = await diagnose(root);
36
+ if (!report.stale) return;
37
+ const parts = [];
38
+ if (report.coreUpdateAvailable) {
39
+ parts.push(`@open-press/core ${report.coreVersion} → ${report.coreLatest}`);
40
+ }
41
+ if (report.pendingMigrations.length > 0) {
42
+ parts.push(`${report.pendingMigrations.length} migration note(s)`);
43
+ }
44
+ if (parts.length === 0) return;
45
+ console.log(`○ open-press: ${parts.join(" · ")} — run \`npx open-press doctor\` for details.`);
46
+ } catch {
47
+ // Doctor is informational only; never block dev.
48
+ }
49
+ }