@open-press/cli 1.0.0 → 1.1.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 (175) hide show
  1. package/README.md +11 -12
  2. package/dist/cli.js +298 -79
  3. package/package.json +9 -7
  4. package/template/core/AGENTS.md +0 -130
  5. package/template/core/CHANGELOG.md +0 -218
  6. package/template/core/README.md +0 -43
  7. package/template/core/engine/cli.mjs +0 -96
  8. package/template/core/engine/commands/_shared.mjs +0 -199
  9. package/template/core/engine/commands/deploy.mjs +0 -31
  10. package/template/core/engine/commands/dev.mjs +0 -49
  11. package/template/core/engine/commands/doctor.mjs +0 -229
  12. package/template/core/engine/commands/export.mjs +0 -8
  13. package/template/core/engine/commands/image.mjs +0 -29
  14. package/template/core/engine/commands/inspect.mjs +0 -35
  15. package/template/core/engine/commands/pdf.mjs +0 -26
  16. package/template/core/engine/commands/preview.mjs +0 -26
  17. package/template/core/engine/commands/render.mjs +0 -17
  18. package/template/core/engine/commands/replace.mjs +0 -41
  19. package/template/core/engine/commands/search.mjs +0 -33
  20. package/template/core/engine/commands/skills-sync.mjs +0 -71
  21. package/template/core/engine/commands/typecheck.mjs +0 -67
  22. package/template/core/engine/commands/upgrade.mjs +0 -159
  23. package/template/core/engine/commands/validate.mjs +0 -17
  24. package/template/core/engine/document-export.mjs +0 -15
  25. package/template/core/engine/output/chrome-pdf.d.mts +0 -34
  26. package/template/core/engine/output/chrome-pdf.mjs +0 -450
  27. package/template/core/engine/output/deploy-sync.mjs +0 -15
  28. package/template/core/engine/output/fonts.mjs +0 -62
  29. package/template/core/engine/output/katex-assets.mjs +0 -45
  30. package/template/core/engine/output/page-block.mjs +0 -30
  31. package/template/core/engine/output/pdf-media.mjs +0 -45
  32. package/template/core/engine/output/public-assets.mjs +0 -19
  33. package/template/core/engine/output/static-server.mjs +0 -571
  34. package/template/core/engine/react/caption-numbering.mjs +0 -73
  35. package/template/core/engine/react/comment-endpoint.d.mts +0 -11
  36. package/template/core/engine/react/comment-endpoint.mjs +0 -102
  37. package/template/core/engine/react/comment-marker.mjs +0 -374
  38. package/template/core/engine/react/document-entry.mjs +0 -331
  39. package/template/core/engine/react/document-export.mjs +0 -512
  40. package/template/core/engine/react/http-json.mjs +0 -24
  41. package/template/core/engine/react/mdx-compile.mjs +0 -629
  42. package/template/core/engine/react/measurement-css.mjs +0 -157
  43. package/template/core/engine/react/object-entities.mjs +0 -204
  44. package/template/core/engine/react/pagination/allocator.mjs +0 -167
  45. package/template/core/engine/react/pagination/regions.mjs +0 -81
  46. package/template/core/engine/react/pagination-constants.mjs +0 -3
  47. package/template/core/engine/react/pagination.mjs +0 -9
  48. package/template/core/engine/react/pipeline/allocate.mjs +0 -217
  49. package/template/core/engine/react/pipeline/final-render.mjs +0 -94
  50. package/template/core/engine/react/pipeline/frame-measurement.mjs +0 -306
  51. package/template/core/engine/react/pipeline/press-tree.mjs +0 -135
  52. package/template/core/engine/react/press-tree-inspection.mjs +0 -172
  53. package/template/core/engine/react/project-asset-endpoint.d.mts +0 -10
  54. package/template/core/engine/react/project-asset-endpoint.mjs +0 -361
  55. package/template/core/engine/react/section-css.mjs +0 -56
  56. package/template/core/engine/react/source-edit-endpoint.d.mts +0 -10
  57. package/template/core/engine/react/source-edit-endpoint.mjs +0 -75
  58. package/template/core/engine/react/sources/heading-numbering.mjs +0 -132
  59. package/template/core/engine/react/sources/mdx-resolver.mjs +0 -439
  60. package/template/core/engine/react/style-discovery.mjs +0 -160
  61. package/template/core/engine/runtime/config.d.mts +0 -48
  62. package/template/core/engine/runtime/config.mjs +0 -172
  63. package/template/core/engine/runtime/file-utils.mjs +0 -114
  64. package/template/core/engine/runtime/file-walk.mjs +0 -22
  65. package/template/core/engine/runtime/inspection.mjs +0 -328
  66. package/template/core/engine/runtime/issue-report.mjs +0 -44
  67. package/template/core/engine/runtime/page-geometry.mjs +0 -131
  68. package/template/core/engine/runtime/path-utils.mjs +0 -20
  69. package/template/core/engine/runtime/source-text-tools.d.mts +0 -102
  70. package/template/core/engine/runtime/source-text-tools.mjs +0 -832
  71. package/template/core/engine/runtime/source-workspace.mjs +0 -168
  72. package/template/core/engine/runtime/validation.mjs +0 -183
  73. package/template/core/index.html +0 -13
  74. package/template/core/openpress.config.mjs +0 -8
  75. package/template/core/package.json +0 -89
  76. package/template/core/src/main.tsx +0 -16
  77. package/template/core/src/openpress/app/OpenPressApp.tsx +0 -296
  78. package/template/core/src/openpress/app/OpenPressRuntime.tsx +0 -102
  79. package/template/core/src/openpress/app/WorkspaceGalleryPage.tsx +0 -219
  80. package/template/core/src/openpress/app/index.ts +0 -2
  81. package/template/core/src/openpress/core/Frame.tsx +0 -91
  82. package/template/core/src/openpress/core/FrameContext.tsx +0 -26
  83. package/template/core/src/openpress/core/MdxArea.tsx +0 -34
  84. package/template/core/src/openpress/core/Press.tsx +0 -55
  85. package/template/core/src/openpress/core/Workspace.tsx +0 -36
  86. package/template/core/src/openpress/core/cn.ts +0 -4
  87. package/template/core/src/openpress/core/index.tsx +0 -47
  88. package/template/core/src/openpress/core/primitives.tsx +0 -91
  89. package/template/core/src/openpress/core/types.ts +0 -236
  90. package/template/core/src/openpress/core/useSource.ts +0 -28
  91. package/template/core/src/openpress/document-model/anchorMapModel.ts +0 -27
  92. package/template/core/src/openpress/document-model/documentIndexes.ts +0 -329
  93. package/template/core/src/openpress/document-model/documentTypes.ts +0 -147
  94. package/template/core/src/openpress/document-model/index.ts +0 -7
  95. package/template/core/src/openpress/document-model/objectEntityModel.ts +0 -55
  96. package/template/core/src/openpress/document-model/projectIdentityModel.ts +0 -15
  97. package/template/core/src/openpress/document-model/reactDocumentMetadataModel.ts +0 -27
  98. package/template/core/src/openpress/document-model/workspaceManifestModel.ts +0 -57
  99. package/template/core/src/openpress/manuscript/index.tsx +0 -238
  100. package/template/core/src/openpress/mdx/index.ts +0 -96
  101. package/template/core/src/openpress/numbering/index.ts +0 -294
  102. package/template/core/src/openpress/reader/PageThumbnailsPanel.tsx +0 -168
  103. package/template/core/src/openpress/reader/PublicReaderPage.tsx +0 -267
  104. package/template/core/src/openpress/reader/ReaderNavigationPanel.tsx +0 -123
  105. package/template/core/src/openpress/reader/index.ts +0 -11
  106. package/template/core/src/openpress/reader/pageViewportScaleModel.ts +0 -73
  107. package/template/core/src/openpress/reader/readerPageRegistry.ts +0 -41
  108. package/template/core/src/openpress/reader/readerPageRoute.ts +0 -21
  109. package/template/core/src/openpress/reader/readerScroll.ts +0 -92
  110. package/template/core/src/openpress/reader/readerStateModel.ts +0 -15
  111. package/template/core/src/openpress/reader/readerTypes.ts +0 -4
  112. package/template/core/src/openpress/reader/usePageViewportScale.ts +0 -119
  113. package/template/core/src/openpress/reader/usePanelState.ts +0 -56
  114. package/template/core/src/openpress/reader/useReaderHashSync.ts +0 -61
  115. package/template/core/src/openpress/reader/useReaderKeyboardNav.ts +0 -48
  116. package/template/core/src/openpress/reader/useReaderRuntime.ts +0 -146
  117. package/template/core/src/openpress/reader/useReaderScrollAnchor.ts +0 -64
  118. package/template/core/src/openpress/shared/Panel.tsx +0 -77
  119. package/template/core/src/openpress/shared/frameScheduler.ts +0 -32
  120. package/template/core/src/openpress/shared/index.ts +0 -4
  121. package/template/core/src/openpress/shared/numberUtils.ts +0 -3
  122. package/template/core/src/openpress/shared/runtimeMode.ts +0 -11
  123. package/template/core/src/openpress/workbench/Workbench.tsx +0 -506
  124. package/template/core/src/openpress/workbench/actions/DeploymentControl.tsx +0 -157
  125. package/template/core/src/openpress/workbench/actions/ExportImageControl.tsx +0 -96
  126. package/template/core/src/openpress/workbench/actions/PageZoomControl.tsx +0 -182
  127. package/template/core/src/openpress/workbench/actions/SearchControl.tsx +0 -345
  128. package/template/core/src/openpress/workbench/actions/deploymentStatusModel.ts +0 -112
  129. package/template/core/src/openpress/workbench/actions/index.ts +0 -6
  130. package/template/core/src/openpress/workbench/actions/useDeploymentWorkbench.ts +0 -136
  131. package/template/core/src/openpress/workbench/dialog/WorkbenchDialog.tsx +0 -72
  132. package/template/core/src/openpress/workbench/dialog/index.ts +0 -1
  133. package/template/core/src/openpress/workbench/document/components/DocumentPanel.tsx +0 -127
  134. package/template/core/src/openpress/workbench/document/components/InlineSourceEditorLayer.tsx +0 -207
  135. package/template/core/src/openpress/workbench/document/components/ReaderStage.tsx +0 -9
  136. package/template/core/src/openpress/workbench/document/hooks/useDocumentWorkbenchModel.ts +0 -34
  137. package/template/core/src/openpress/workbench/document/hooks/useInlineDocumentEditor.ts +0 -525
  138. package/template/core/src/openpress/workbench/document/index.ts +0 -10
  139. package/template/core/src/openpress/workbench/index.ts +0 -2
  140. package/template/core/src/openpress/workbench/inspector/InlineInspectorLayer.tsx +0 -459
  141. package/template/core/src/openpress/workbench/inspector/index.ts +0 -5
  142. package/template/core/src/openpress/workbench/inspector/inlineCommentModel.ts +0 -125
  143. package/template/core/src/openpress/workbench/inspector/inspectorGeometryModel.ts +0 -160
  144. package/template/core/src/openpress/workbench/inspector/inspectorModel.ts +0 -408
  145. package/template/core/src/openpress/workbench/inspector/useInspectorComments.ts +0 -254
  146. package/template/core/src/openpress/workbench/mentions/MentionSuggestionList.tsx +0 -41
  147. package/template/core/src/openpress/workbench/mentions/index.ts +0 -2
  148. package/template/core/src/openpress/workbench/mentions/useComposerMentions.ts +0 -185
  149. package/template/core/src/openpress/workbench/panels/Panel.tsx +0 -1
  150. package/template/core/src/openpress/workbench/panels/PendingCommentsPanel.tsx +0 -80
  151. package/template/core/src/openpress/workbench/panels/WorkbenchControlPanel.tsx +0 -29
  152. package/template/core/src/openpress/workbench/panels/index.ts +0 -3
  153. package/template/core/src/openpress/workbench/project/ProjectEntryPanel.tsx +0 -525
  154. package/template/core/src/openpress/workbench/project/ProjectPreviewDialog.tsx +0 -35
  155. package/template/core/src/openpress/workbench/project/index.ts +0 -2
  156. package/template/core/src/openpress/workbench/project/projectPreviewTypes.ts +0 -11
  157. package/template/core/src/openpress/workbench/project/projectSourceModel.ts +0 -24
  158. package/template/core/src/openpress/workbench/shell/WorkbenchShell.tsx +0 -167
  159. package/template/core/src/openpress/workbench/shell/index.ts +0 -1
  160. package/template/core/src/openpress/workbench/workbenchFormatters.ts +0 -120
  161. package/template/core/src/openpress/workbench/workbenchTypes.ts +0 -35
  162. package/template/core/src/styles/openpress/app-shell.css +0 -251
  163. package/template/core/src/styles/openpress/media-workspace.css +0 -230
  164. package/template/core/src/styles/openpress/print-route.css +0 -184
  165. package/template/core/src/styles/openpress/project-preview-panel.css +0 -924
  166. package/template/core/src/styles/openpress/public-viewer.css +0 -688
  167. package/template/core/src/styles/openpress/reader-runtime.css +0 -989
  168. package/template/core/src/styles/openpress/responsive.css +0 -245
  169. package/template/core/src/styles/openpress/workbench-panels.css +0 -707
  170. package/template/core/src/styles/openpress/workbench.css +0 -1255
  171. package/template/core/src/styles/openpress/workspace-gallery.css +0 -300
  172. package/template/core/src/styles/openpress.css +0 -15
  173. package/template/core/src/vite-env.d.ts +0 -9
  174. package/template/core/tsconfig.json +0 -40
  175. package/template/core/vite.config.ts +0 -584
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @open-press/cli
2
2
 
3
- Scaffolder for [open-press](https://github.com/quan0715/open-press) — an AI-first fixed-layout document workspace.
3
+ Workspace CLI for [open-press](https://github.com/quan0715/open-press) — an AI-first fixed-layout document workspace.
4
4
 
5
5
  ## Prerequisite
6
6
 
@@ -16,7 +16,7 @@ npm run dev
16
16
 
17
17
  Then open the local URL printed by Vite (typically `http://127.0.0.1:5173/?dev=1`).
18
18
 
19
- The CLI creates the OpenPress runtime workspace only. Starters and examples live in skills, installed separately with `npx skills add <owner/repo>`. The starter-bearing skills in the framework repo are just skills; agents can read and use them directly.
19
+ The CLI creates a package-based OpenPress workspace. Runtime files stay inside `@open-press/core`; your project keeps only source files, theme files, media, and npm scripts.
20
20
 
21
21
  ## Usage
22
22
 
@@ -27,16 +27,15 @@ npx @open-press/cli init <target> [flags]
27
27
  | Flag | Description |
28
28
  | -------------------- | --------------------------------------------------------------------------- |
29
29
  | `--title <s>` | Document title (written to workspace config) |
30
- | `--subtitle <s>` | Document subtitle |
31
- | `--organization <s>` | Organization name |
32
- | `--author <s>` | Author name |
33
30
  | `--no-git` | Skip `git init` + initial commit (use when scaffolding inside an existing repo) |
34
31
  | `--no-install` | Skip `npm install` (offline, or you'll run pnpm/bun yourself) |
32
+ | `--skills` | Install OpenPress agent skills after scaffolding |
33
+ | `--no-skills` | Skip agent skill installation |
35
34
  | `--help` | Print help |
36
35
 
37
36
  > The target must be empty. A `.git/` directory or other harmless dotfiles (`.gitignore`, `.gitkeep`, `.DS_Store`) are ignored — common when scaffolding into a fresh repo.
38
37
 
39
- To use an opinionated starter, install a skill and let the agent read that skill's files:
38
+ To use an opinionated starter, install a skill and let the agent copy that starter's `press/` files into the workspace:
40
39
 
41
40
  ```bash
42
41
  npx -y skills@latest add quan0715/openpress-social-card-skill
@@ -46,16 +45,16 @@ npx -y skills@latest add quan0715/openpress-social-card-skill
46
45
 
47
46
  A self-contained workspace with:
48
47
 
49
- - `engine/`, `src/`, `vite.config.ts` the open-press framework (snapshot of `@open-press/core`)
50
- - `.claude/skills/` and `.agents/skills/` agent skill files for Claude Code, Codex, Cursor, Copilot, etc.
51
- - `openpress.config.mjs` — workspace metadata (title, subtitle, organization, author)
52
- - `AGENTS.md` — agent contract
48
+ - `package.json` with `@open-press/core`, `@open-press/cli`, and `open-press ...` scripts
49
+ - `press/index.tsx`the workspace document entry
50
+ - `press/theme/`, `press/media/`, `press/components/` user-owned authoring files
51
+ - `press/design.md` — working design notes for agents and maintainers
53
52
 
54
- The `press/` or transitional `document/` source tree is added by a skill, user-authored code, or a project-specific workflow after init.
53
+ It does **not** create `engine/`, `src/openpress/`, `index.html`, or `vite.config.ts` in your project. Those are package-owned runtime internals.
55
54
 
56
55
  ## After init
57
56
 
58
- Workspace commands (run via `npm run` or `node engine/cli.mjs`):
57
+ Workspace commands (run via `npm run` or `open-press`):
59
58
 
60
59
  ```
61
60
  npm run dev # start workbench
package/dist/cli.js CHANGED
@@ -1,12 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
+ import { spawn as spawn2 } from "child_process";
5
+ import { createRequire } from "module";
6
+ import path2 from "path";
4
7
  import process2 from "process";
5
8
 
6
9
  // src/init.ts
7
10
  import { spawn } from "child_process";
8
11
  import { existsSync } from "fs";
9
- import { cp, mkdir } from "fs/promises";
12
+ import { mkdir, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
10
13
  import path from "path";
11
14
  import process from "process";
12
15
  import { fileURLToPath } from "url";
@@ -43,63 +46,36 @@ async function patchPressTitle(entryPath, title) {
43
46
  function escapeStringForJs(value) {
44
47
  return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
45
48
  }
46
- async function patchPackageJsonName(packagePath, newName) {
47
- const text = await readFile(packagePath, "utf8");
48
- const pkg = JSON.parse(text);
49
- const prevName = typeof pkg.name === "string" ? pkg.name : "";
50
- pkg.name = newName;
51
- pkg.version = "0.0.0";
52
- pkg.private = true;
53
- pkg.description = `open-press workspace: ${newName}`;
54
- for (const field of [
55
- "publishConfig",
56
- "files",
57
- "bin",
58
- "main",
59
- "types",
60
- "exports",
61
- "homepage",
62
- "repository",
63
- "bugs",
64
- "keywords",
65
- "license",
66
- "author"
67
- ]) {
68
- delete pkg[field];
69
- }
70
- await writeFile(packagePath, JSON.stringify(pkg, null, 2) + "\n");
71
- return prevName;
72
- }
73
49
 
74
50
  // src/init.ts
75
51
  var FRAMEWORK_SKILLS_SOURCE = "quan0715/open-press";
76
52
  var __dirname = path.dirname(fileURLToPath(import.meta.url));
77
- var TEMPLATE_ROOT = path.resolve(__dirname, "..", "template");
78
- var TEMPLATE_CORE = path.join(TEMPLATE_ROOT, "core");
53
+ var CLI_PACKAGE_JSON = path.resolve(__dirname, "..", "package.json");
79
54
  async function init(options) {
80
- ensureTemplateBundled();
81
55
  const target = path.resolve(process.cwd(), options.target);
82
56
  await ensureTarget(target);
57
+ const workspaceName = path.basename(target);
58
+ const title = options.title ?? "OpenPress Document";
59
+ const version = await readCliVersion();
83
60
  log(`Creating open-press workspace at ${target}`);
84
- log("Copying framework (engine + runtime + config)\u2026");
85
- await cp(TEMPLATE_CORE, target, { recursive: true });
86
- const pkgPath = path.join(target, "package.json");
87
- if (existsSync(pkgPath)) {
88
- await patchPackageJsonName(pkgPath, path.basename(target));
89
- }
61
+ await writeWorkspacePackageJson(target, workspaceName, version);
62
+ await writeWorkspaceGitignore(target);
63
+ await writeStarterPress(target, title);
90
64
  if (options.title) {
91
65
  const pressEntry = path.join(target, "press", "index.tsx");
92
- if (existsSync(pressEntry)) {
93
- log("Writing title into <Press> in press/index.tsx");
94
- await patchPressTitle(pressEntry, options.title);
95
- }
66
+ log("Writing title into <Press> in press/index.tsx");
67
+ await patchPressTitle(pressEntry, options.title);
96
68
  }
97
- log(`Installing framework skills via \`npx skills add ${FRAMEWORK_SKILLS_SOURCE}\`\u2026`);
98
- try {
99
- await runInTarget(target, "npx", ["-y", "skills@latest", "add", FRAMEWORK_SKILLS_SOURCE]);
100
- } catch (err) {
101
- log(`(framework skills install failed; retry: npx skills add ${FRAMEWORK_SKILLS_SOURCE})`);
102
- log(` reason: ${err instanceof Error ? err.message : String(err)}`);
69
+ if (options.skills) {
70
+ log(`Installing framework skills via \`open-press skills add\`\u2026`);
71
+ try {
72
+ await runInTarget(target, "npx", ["-y", "skills@latest", "add", FRAMEWORK_SKILLS_SOURCE]);
73
+ } catch (err) {
74
+ log(`(framework skills install failed; retry later: open-press skills add)`);
75
+ log(` reason: ${err instanceof Error ? err.message : String(err)}`);
76
+ }
77
+ } else {
78
+ log("Skipping agent skills (--no-skills)");
103
79
  }
104
80
  if (options.install) {
105
81
  log("Installing dependencies (npm install)\u2026");
@@ -121,13 +97,6 @@ async function init(options) {
121
97
  }
122
98
  printNextSteps(target, options);
123
99
  }
124
- function ensureTemplateBundled() {
125
- if (!existsSync(TEMPLATE_CORE)) {
126
- throw new Error(
127
- `Template not bundled at ${TEMPLATE_ROOT}. If running from source, run \`pnpm sync:template\` in packages/cli first.`
128
- );
129
- }
130
- }
131
100
  async function ensureTarget(target) {
132
101
  if (existsSync(target)) {
133
102
  const empty = await pathIsEmpty(target, { ignoreHarmless: true });
@@ -140,6 +109,189 @@ async function ensureTarget(target) {
140
109
  }
141
110
  await mkdir(target, { recursive: true });
142
111
  }
112
+ async function readCliVersion() {
113
+ const pkg = JSON.parse(await readFile2(CLI_PACKAGE_JSON, "utf8"));
114
+ return typeof pkg.version === "string" && pkg.version ? pkg.version : "latest";
115
+ }
116
+ async function writeWorkspacePackageJson(target, workspaceName, version) {
117
+ const pkg = {
118
+ name: workspaceName,
119
+ version: "0.0.0",
120
+ private: true,
121
+ type: "module",
122
+ description: `open-press workspace: ${workspaceName}`,
123
+ scripts: {
124
+ dev: "open-press dev . --renderer react",
125
+ build: "open-press render . --renderer react",
126
+ preview: "open-press preview . --renderer react",
127
+ typecheck: "open-press typecheck .",
128
+ "openpress:image": "open-press image .",
129
+ "openpress:pdf": "open-press pdf .",
130
+ "openpress:deploy": "open-press deploy .",
131
+ "openpress:deploy:dry-run": "open-press deploy . --confirm --dry-run",
132
+ "openpress:skills": "open-press skills update"
133
+ },
134
+ dependencies: {
135
+ "@open-press/core": version
136
+ },
137
+ devDependencies: {
138
+ "@open-press/cli": version,
139
+ "@types/node": "^25.8.0",
140
+ "@types/react": "^19.2.14",
141
+ "@types/react-dom": "^19.2.3",
142
+ typescript: "^6.0.3"
143
+ },
144
+ openpress: {
145
+ pdf: {
146
+ filename: "document.pdf"
147
+ },
148
+ deploy: {
149
+ adapter: "cloudflare-pages",
150
+ source: ".deploy/openpress",
151
+ projectName: null,
152
+ commitDirty: false,
153
+ requiresConfirmation: true
154
+ }
155
+ }
156
+ };
157
+ await writeFile2(path.join(target, "package.json"), `${JSON.stringify(pkg, null, 2)}
158
+ `, "utf8");
159
+ }
160
+ async function writeWorkspaceGitignore(target) {
161
+ const content = [
162
+ "node_modules/",
163
+ ".DS_Store",
164
+ "*.log",
165
+ "",
166
+ "# OpenPress generated artifacts",
167
+ ".openpress/",
168
+ ".deploy/",
169
+ ".turbo/",
170
+ "dist/",
171
+ "dist-react/",
172
+ "public/openpress/",
173
+ "output/",
174
+ ""
175
+ ].join("\n");
176
+ await writeFile2(path.join(target, ".gitignore"), content, "utf8");
177
+ }
178
+ async function writeStarterPress(target, title) {
179
+ const pressRoot = path.join(target, "press");
180
+ await mkdir(path.join(pressRoot, "components"), { recursive: true });
181
+ await mkdir(path.join(pressRoot, "media"), { recursive: true });
182
+ await mkdir(path.join(pressRoot, "theme", "base"), { recursive: true });
183
+ await mkdir(path.join(pressRoot, "theme", "page-surfaces"), { recursive: true });
184
+ await mkdir(path.join(pressRoot, "theme", "shell"), { recursive: true });
185
+ await writeFile2(path.join(pressRoot, "design.md"), `# ${title}
186
+
187
+ Starter OpenPress workspace.
188
+ `, "utf8");
189
+ await writeFile2(path.join(pressRoot, "media", "README.md"), "# Media\n\nPlace project media here.\n", "utf8");
190
+ await writeFile2(
191
+ path.join(pressRoot, "theme", "base", "page-contract.css"),
192
+ `* {
193
+ box-sizing: border-box;
194
+ }
195
+
196
+ html,
197
+ body {
198
+ margin: 0;
199
+ background: #181818;
200
+ }
201
+
202
+ body {
203
+ color: #171717;
204
+ font-family: var(--openpress-font-body, system-ui, sans-serif);
205
+ -webkit-print-color-adjust: exact;
206
+ print-color-adjust: exact;
207
+ }
208
+
209
+ .reader-page {
210
+ background: #ffffff;
211
+ }
212
+ `,
213
+ "utf8"
214
+ );
215
+ await writeFile2(
216
+ path.join(pressRoot, "theme", "base", "typography.css"),
217
+ `h1,
218
+ h2,
219
+ h3,
220
+ p {
221
+ margin: 0;
222
+ }
223
+
224
+ h1 {
225
+ font-family: var(--openpress-font-serif, Georgia, serif);
226
+ font-size: clamp(42px, 8cqw, 72px);
227
+ line-height: 1;
228
+ font-weight: 500;
229
+ }
230
+
231
+ p {
232
+ font-size: clamp(16px, 2cqw, 22px);
233
+ line-height: 1.5;
234
+ }
235
+
236
+ code {
237
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
238
+ }
239
+ `,
240
+ "utf8"
241
+ );
242
+ await writeFile2(
243
+ path.join(pressRoot, "theme", "base", "print.css"),
244
+ `@media print {
245
+ html,
246
+ body {
247
+ background: #ffffff;
248
+ }
249
+ }
250
+ `,
251
+ "utf8"
252
+ );
253
+ await writeFile2(path.join(pressRoot, "theme", "page-surfaces", "cover.css"), "/* Starter cover surface. */\n", "utf8");
254
+ await writeFile2(path.join(pressRoot, "theme", "page-surfaces", "back-cover.css"), "/* Starter back-cover surface. */\n", "utf8");
255
+ await writeFile2(path.join(pressRoot, "theme", "page-surfaces", "toc.css"), "/* Starter TOC surface. */\n", "utf8");
256
+ await writeFile2(path.join(pressRoot, "theme", "shell", "reader-controls.css"), "/* Starter reader controls surface. */\n", "utf8");
257
+ await writeFile2(
258
+ path.join(pressRoot, "theme", "tokens.css"),
259
+ `:root {
260
+ --openpress-font-body: system-ui, sans-serif;
261
+ --openpress-font-serif: Georgia, serif;
262
+ }
263
+ `,
264
+ "utf8"
265
+ );
266
+ await writeFile2(
267
+ path.join(pressRoot, "index.tsx"),
268
+ `import { Frame, Press, Workspace } from "@open-press/core";
269
+
270
+ export default function OpenPressDocument() {
271
+ return (
272
+ <Workspace name="${escapeJsxAttribute(title)}">
273
+ <Press title="${escapeJsxAttribute(title)}">
274
+ <Frame frameKey="cover" role="manuscript.cover">
275
+ <main style={{ padding: "72px", fontFamily: "var(--openpress-font-serif, Georgia, serif)" }}>
276
+ <p style={{ letterSpacing: "0.12em", textTransform: "uppercase" }}>OpenPress</p>
277
+ <h1>${escapeText(title)}</h1>
278
+ <p>Edit <code>press/index.tsx</code>, then run <code>npm run dev</code>.</p>
279
+ </main>
280
+ </Frame>
281
+ </Press>
282
+ </Workspace>
283
+ );
284
+ }
285
+ `,
286
+ "utf8"
287
+ );
288
+ }
289
+ function escapeJsxAttribute(value) {
290
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
291
+ }
292
+ function escapeText(value) {
293
+ return value.replace(/{/g, "&#123;").replace(/}/g, "&#125;").replace(/</g, "&lt;");
294
+ }
143
295
  async function runInTarget(cwd, command, args, opts = {}) {
144
296
  return new Promise((resolve, reject) => {
145
297
  const child = spawn(command, args, {
@@ -170,6 +322,9 @@ function printNextSteps(target, options) {
170
322
  if (!options.install) {
171
323
  lines.push(" npm install");
172
324
  }
325
+ if (!options.skills) {
326
+ lines.push(" npm run openpress:skills");
327
+ }
173
328
  lines.push(
174
329
  "",
175
330
  " # start the workbench:",
@@ -177,30 +332,49 @@ function printNextSteps(target, options) {
177
332
  "",
178
333
  "Then open the local URL printed by Vite (typically http://127.0.0.1:5173/?dev=1).",
179
334
  "",
180
- "Agent skills installed under .agents/skills/ (universal \u2014 read by Claude Code,",
181
- `Cursor, Codex, Gemini CLI, etc.). Update later with: npx skills upgrade`,
182
- "",
183
- "Use an OpenPress-ready skill to add or adapt the press/document source tree.",
335
+ "Use an OpenPress-ready skill to add or adapt the press source tree.",
184
336
  ""
185
337
  );
186
338
  process.stdout.write(lines.join("\n"));
187
339
  }
188
340
 
189
341
  // src/cli.ts
342
+ var require2 = createRequire(import.meta.url);
190
343
  var HELP = `open-press \u2014 AI-first fixed-layout document workspaces.
191
344
 
192
345
  Usage:
193
- npx @open-press/cli init <target> [flags]
346
+ open-press init <target> [flags]
347
+ open-press <command> [path] [options]
348
+ open-press skills <add|update> [--source <owner/repo>]
194
349
 
195
- Flags:
350
+ Init flags:
196
351
  --title <s> Document title (written into <Press title="..."> in press/index.tsx)
197
352
  --no-git Skip git init
198
353
  --no-install Skip npm install
354
+ --skills Install OpenPress agent skills after scaffolding
355
+ --no-skills Skip agent skill installation
199
356
  --help Show this help
200
357
 
358
+ Runtime commands:
359
+ dev Start the local OpenPress workbench
360
+ render Build the static reader
361
+ preview Preview the static reader
362
+ image Export pages to PNG
363
+ pdf Export pages to PDF
364
+ validate Validate source structure
365
+ inspect Inspect rendered output
366
+ search Search workspace source
367
+ replace Replace workspace source text
368
+ doctor Check package and skill freshness
369
+ upgrade Update workspace dependencies and skills
370
+ migrate Alias for upgrade
371
+ skills Install or update OpenPress agent skills
372
+
201
373
  Examples:
202
374
  npx @open-press/cli init my-doc
203
375
  npx @open-press/cli init my-brief --title "Q2 Brief"
376
+ npx open-press dev .
377
+ npx open-press image .
204
378
  `;
205
379
  async function main(argv) {
206
380
  if (argv.length === 0 || argv.includes("--help") || argv.includes("-h")) {
@@ -208,27 +382,26 @@ async function main(argv) {
208
382
  return 0;
209
383
  }
210
384
  const [subcommand, ...rest] = argv;
211
- if (subcommand !== "init") {
212
- process2.stderr.write(`Unknown command: ${subcommand}
213
-
385
+ if (subcommand === "init") {
386
+ const options = parseInitArgs(rest);
387
+ if (!options) {
388
+ process2.stderr.write(HELP);
389
+ return 1;
390
+ }
391
+ try {
392
+ await init(options);
393
+ return 0;
394
+ } catch (err) {
395
+ const msg = err instanceof Error ? err.message : String(err);
396
+ process2.stderr.write(`open-press init failed: ${msg}
214
397
  `);
215
- process2.stderr.write(HELP);
216
- return 1;
217
- }
218
- const options = parseInitArgs(rest);
219
- if (!options) {
220
- process2.stderr.write(HELP);
221
- return 1;
398
+ return 1;
399
+ }
222
400
  }
223
- try {
224
- await init(options);
225
- return 0;
226
- } catch (err) {
227
- const msg = err instanceof Error ? err.message : String(err);
228
- process2.stderr.write(`open-press init failed: ${msg}
229
- `);
230
- return 1;
401
+ if (subcommand === "skills") {
402
+ return delegateToCore(normalizeSkillsArgs(rest));
231
403
  }
404
+ return delegateToCore([subcommand, ...rest]);
232
405
  }
233
406
  function parseInitArgs(args) {
234
407
  if (args.length === 0) {
@@ -239,7 +412,8 @@ function parseInitArgs(args) {
239
412
  target: "",
240
413
  title: void 0,
241
414
  git: true,
242
- install: true
415
+ install: true,
416
+ skills: false
243
417
  };
244
418
  for (let i = 0; i < args.length; i++) {
245
419
  const arg = args[i];
@@ -253,12 +427,18 @@ function parseInitArgs(args) {
253
427
  case "--no-install":
254
428
  options.install = false;
255
429
  break;
430
+ case "--no-skills":
431
+ options.skills = false;
432
+ break;
256
433
  case "--git":
257
434
  options.git = true;
258
435
  break;
259
436
  case "--install":
260
437
  options.install = true;
261
438
  break;
439
+ case "--skills":
440
+ options.skills = true;
441
+ break;
262
442
  default:
263
443
  if (arg.startsWith("--")) {
264
444
  process2.stderr.write(`Unknown flag: ${arg}
@@ -281,6 +461,45 @@ function parseInitArgs(args) {
281
461
  }
282
462
  return options;
283
463
  }
464
+ function normalizeSkillsArgs(args) {
465
+ const [subcommand, ...rest] = args;
466
+ if (!subcommand || subcommand === "add" || subcommand === "update") {
467
+ return ["skills:sync", ...rest];
468
+ }
469
+ return ["skills:sync", subcommand, ...rest];
470
+ }
471
+ async function delegateToCore(args) {
472
+ const coreCli = resolveCoreCli();
473
+ return new Promise((resolve, reject) => {
474
+ const child = spawn2(process2.execPath, [coreCli, ...args], {
475
+ cwd: process2.cwd(),
476
+ stdio: "inherit"
477
+ });
478
+ child.once("error", reject);
479
+ child.once("close", (code) => resolve(code ?? 1));
480
+ });
481
+ }
482
+ function resolveCoreCli() {
483
+ const coreEntry = require2.resolve("@open-press/core");
484
+ const coreRoot = findPackageRoot(coreEntry, "@open-press/core");
485
+ return path2.join(coreRoot, "engine", "cli.mjs");
486
+ }
487
+ function findPackageRoot(startPath, packageName) {
488
+ let dir = path2.dirname(startPath);
489
+ while (true) {
490
+ const pkgPath = path2.join(dir, "package.json");
491
+ try {
492
+ const pkg = require2(pkgPath);
493
+ if (pkg.name === packageName) return dir;
494
+ } catch {
495
+ }
496
+ const parent = path2.dirname(dir);
497
+ if (parent === dir) {
498
+ throw new Error(`Could not find ${packageName} package root from ${startPath}`);
499
+ }
500
+ dir = parent;
501
+ }
502
+ }
284
503
  main(process2.argv.slice(2)).then((code) => process2.exit(code)).catch((err) => {
285
504
  process2.stderr.write(`${err instanceof Error ? err.stack ?? err.message : String(err)}
286
505
  `);
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@open-press/cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
- "description": "Scaffolder for open-press AI-first fixed-layout document workspaces.",
5
+ "description": "Command line interface for OpenPress workspaces.",
6
6
  "license": "MIT",
7
7
  "author": "quan0715",
8
8
  "homepage": "https://github.com/quan0715/open-press#readme",
@@ -24,14 +24,15 @@
24
24
  "ai-first",
25
25
  "scaffold",
26
26
  "init",
27
- "cli"
27
+ "cli",
28
+ "render",
29
+ "pdf"
28
30
  ],
29
31
  "bin": {
30
32
  "open-press": "dist/cli.js"
31
33
  },
32
34
  "files": [
33
35
  "dist",
34
- "template",
35
36
  "README.md"
36
37
  ],
37
38
  "publishConfig": {
@@ -40,15 +41,16 @@
40
41
  "engines": {
41
42
  "node": ">=20"
42
43
  },
43
- "dependencies": {},
44
+ "dependencies": {
45
+ "@open-press/core": "1.1.0"
46
+ },
44
47
  "devDependencies": {
45
48
  "@types/node": "^25.8.0",
46
49
  "tsup": "^8.5.0",
47
50
  "typescript": "^6.0.3"
48
51
  },
49
52
  "scripts": {
50
- "sync:template": "node scripts/sync-template.mjs",
51
- "build": "pnpm sync:template && tsup",
53
+ "build": "tsup",
52
54
  "dev": "tsup --watch",
53
55
  "typecheck": "tsc --noEmit",
54
56
  "test": "pnpm build && node --test tests/*.test.mjs"