stego-cli 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/.cspell.json +14 -0
  2. package/.gitignore +8 -0
  3. package/.markdownlint.json +5 -0
  4. package/.vscode/tasks.json +72 -0
  5. package/README.md +179 -0
  6. package/dist/exporters/exporter-types.js +1 -0
  7. package/dist/exporters/markdown-exporter.js +14 -0
  8. package/dist/exporters/pandoc-exporter.js +65 -0
  9. package/dist/stego-cli.js +1803 -0
  10. package/docs/conventions.md +182 -0
  11. package/docs/workflow.md +78 -0
  12. package/package.json +50 -0
  13. package/projects/docs-demo/README.md +20 -0
  14. package/projects/docs-demo/dist/.gitkeep +0 -0
  15. package/projects/docs-demo/manuscript/100-what-stego-is.md +37 -0
  16. package/projects/docs-demo/manuscript/200-writing-workflow.md +69 -0
  17. package/projects/docs-demo/manuscript/300-quality-gates.md +36 -0
  18. package/projects/docs-demo/manuscript/400-build-and-export.md +42 -0
  19. package/projects/docs-demo/notes/implementation-notes.md +17 -0
  20. package/projects/docs-demo/notes/style-guide.md +7 -0
  21. package/projects/docs-demo/package.json +10 -0
  22. package/projects/docs-demo/stego-project.json +9 -0
  23. package/projects/plague-demo/.markdownlint.json +4 -0
  24. package/projects/plague-demo/README.md +19 -0
  25. package/projects/plague-demo/dist/.gitkeep +0 -0
  26. package/projects/plague-demo/manuscript/100-the-commission.md +24 -0
  27. package/projects/plague-demo/manuscript/200-at-the-wards.md +38 -0
  28. package/projects/plague-demo/manuscript/300-the-hearing.md +38 -0
  29. package/projects/plague-demo/manuscript/400-the-final-account.md +30 -0
  30. package/projects/plague-demo/notes/style-guide.md +7 -0
  31. package/projects/plague-demo/package.json +10 -0
  32. package/projects/plague-demo/spine/characters.md +31 -0
  33. package/projects/plague-demo/spine/locations.md +33 -0
  34. package/projects/plague-demo/spine/sources.md +28 -0
  35. package/projects/plague-demo/stego-project.json +41 -0
  36. package/stego.config.json +56 -0
package/.cspell.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "version": "0.2",
3
+ "language": "en-US",
4
+ "words": [
5
+ "worldbuilding",
6
+ "lineedit",
7
+ "hôtel",
8
+ "dieu",
9
+ "hôtel-dieu"
10
+ ],
11
+ "ignorePaths": [
12
+ "projects/*/dist/**"
13
+ ]
14
+ }
package/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ node_modules/
2
+ /dist/
3
+ .DS_Store
4
+ *.log
5
+ projects/*/dist/*
6
+ !projects/*/dist/.gitkeep
7
+ projects/*/.vscode/settings.json
8
+ .vscode/settings.json
@@ -0,0 +1,5 @@
1
+ {
2
+ "default": true,
3
+ "MD013": false,
4
+ "MD033": false
5
+ }
@@ -0,0 +1,72 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "tasks": [
4
+ {
5
+ "label": "Stego: List Projects",
6
+ "type": "shell",
7
+ "command": "npm run list-projects",
8
+ "problemMatcher": []
9
+ },
10
+ {
11
+ "label": "Stego: Validate Project",
12
+ "type": "shell",
13
+ "command": "npm run validate -- --project ${input:projectId}",
14
+ "problemMatcher": []
15
+ },
16
+ {
17
+ "label": "Stego: Build Project",
18
+ "type": "shell",
19
+ "command": "npm run build -- --project ${input:projectId}",
20
+ "problemMatcher": []
21
+ },
22
+ {
23
+ "label": "Stego: Check Stage",
24
+ "type": "shell",
25
+ "command": "npm run check-stage -- --project ${input:projectId} --stage ${input:stageName}",
26
+ "problemMatcher": []
27
+ },
28
+ {
29
+ "label": "Stego: Export Markdown",
30
+ "type": "shell",
31
+ "command": "npm run export -- --project ${input:projectId} --format md",
32
+ "problemMatcher": []
33
+ },
34
+ {
35
+ "label": "Stego: Export DOCX",
36
+ "type": "shell",
37
+ "command": "npm run export -- --project ${input:projectId} --format docx",
38
+ "problemMatcher": []
39
+ },
40
+ {
41
+ "label": "Stego: Test Compile Structure",
42
+ "type": "shell",
43
+ "command": "npm",
44
+ "args": [
45
+ "run",
46
+ "test:compile-structure"
47
+ ],
48
+ "isBackground": false
49
+ }
50
+ ],
51
+ "inputs": [
52
+ {
53
+ "id": "projectId",
54
+ "type": "promptString",
55
+ "description": "Project id",
56
+ "default": "plague-demo"
57
+ },
58
+ {
59
+ "id": "stageName",
60
+ "type": "pickString",
61
+ "description": "Stage",
62
+ "options": [
63
+ "draft",
64
+ "revise",
65
+ "line-edit",
66
+ "proof",
67
+ "final"
68
+ ],
69
+ "default": "draft"
70
+ }
71
+ ]
72
+ }
package/README.md ADDED
@@ -0,0 +1,179 @@
1
+ # Stego
2
+
3
+ This workspace is a Markdown-first creative writing pipeline for short stories through novels.
4
+
5
+ ## What this includes
6
+
7
+ - Project-per-folder structure inside one monorepo.
8
+ - Flexible manuscript files with per-project metadata requirements.
9
+ - Configurable manuscript grouping via `compileStructure.levels` (for example `part` + `chapter`).
10
+ - Project-defined spine categories (configured in each `stego-project.json`).
11
+ - Deterministic build into one manuscript Markdown file.
12
+ - Stage-based quality gates (`draft` -> `final`).
13
+ - Export abstraction (`md` always, `docx`/`pdf` via optional `pandoc`).
14
+ - TypeScript-based tooling executed directly by Node (`--experimental-strip-types`).
15
+
16
+ ## Quick start
17
+
18
+ ```bash
19
+ cd ~/Code/stego
20
+ npm run list-projects
21
+ npm run new-project -- --project my-new-project --title "My New Project"
22
+ npm run validate -- --project plague-demo
23
+ npm run build -- --project plague-demo
24
+ npm run check-stage -- --project plague-demo --stage revise
25
+ npm run export -- --project plague-demo --format md
26
+ ```
27
+
28
+ `npm run new-project` scaffolds `manuscript/`, `spine/`, `notes/`, and `dist/`, and seeds `stego-project.json` with a default `characters` category plus `spine/characters.md`.
29
+ It also creates `projects/<project-id>/.vscode/settings.json` so markdown font settings apply when opening the project folder directly.
30
+ It also creates a project-local `package.json` so you can run `npm run validate`, `npm run build`, etc. from inside that project directory without `--project`.
31
+
32
+ ## Export requirements (DOCX/PDF)
33
+
34
+ `docx` and `pdf` export require `pandoc` on your system `PATH`.
35
+
36
+ Install:
37
+
38
+ ```bash
39
+ # macOS (Homebrew)
40
+ brew install pandoc
41
+ ```
42
+
43
+ ```bash
44
+ # Ubuntu/Debian
45
+ sudo apt-get update && sudo apt-get install -y pandoc
46
+ ```
47
+
48
+ ```bash
49
+ # Windows (winget)
50
+ winget install --id JohnMacFarlane.Pandoc -e
51
+ ```
52
+
53
+ Verify:
54
+
55
+ ```bash
56
+ pandoc --version
57
+ ```
58
+
59
+ Then run:
60
+
61
+ ```bash
62
+ npm run export -- --project plague-demo --format docx
63
+ npm run export -- --project plague-demo --format pdf
64
+ ```
65
+
66
+ ## Project layout
67
+
68
+ - `projects/<project-id>/manuscript/` source manuscript files
69
+ - `projects/<project-id>/spine/` canonical spine category files (`spineCategories[*].notesFile`)
70
+ - `projects/<project-id>/notes/` regular notes and planning docs
71
+ - `projects/<project-id>/dist/` generated outputs only
72
+ - `docs/` workflow and conventions
73
+ - `tools/` build, checks, export CLI
74
+
75
+ ## Project spine categories
76
+
77
+ Spine categories are not fixed. Each project can declare them in `stego-project.json` under `spineCategories`.
78
+
79
+ Example:
80
+
81
+ ```json
82
+ {
83
+ "spineCategories": [
84
+ { "key": "cast", "prefix": "CHAR", "notesFile": "characters.md" },
85
+ { "key": "places", "prefix": "LOC", "notesFile": "locations.md" },
86
+ { "key": "incidents", "prefix": "EVENT", "notesFile": "timeline.md" },
87
+ { "key": "ordinances", "prefix": "STATUTE", "notesFile": "ordinances.md" }
88
+ ]
89
+ }
90
+ ```
91
+
92
+ Use those keys as metadata arrays in manuscript files (for example `cast`, `places`, `incidents`, `ordinances`).
93
+ Each `notesFile` is a filename resolved in `spine/` (for example `spine/characters.md`).
94
+
95
+ If `spineCategories` is omitted or empty, category-based continuity validation is disabled.
96
+
97
+ ## Project metadata requirements
98
+
99
+ Base config defaults to `status`.
100
+
101
+ Each project can override required keys in `stego-project.json`:
102
+
103
+ ```json
104
+ {
105
+ "requiredMetadata": ["status", "chapter", "pov", "timeline"]
106
+ }
107
+ ```
108
+
109
+ These keys are advisory and reported as warnings when missing; they do not block validate/build/export.
110
+ Files may omit metadata entirely.
111
+
112
+ ## Compile structure (grouped manuscript output)
113
+
114
+ Build grouping is configured per project with `compileStructure.levels`.
115
+
116
+ Example:
117
+
118
+ ```json
119
+ {
120
+ "compileStructure": {
121
+ "levels": [
122
+ {
123
+ "key": "part",
124
+ "label": "Part",
125
+ "titleKey": "part_title",
126
+ "injectHeading": true,
127
+ "headingTemplate": "{label} {value}: {title}",
128
+ "pageBreak": "between-groups"
129
+ },
130
+ {
131
+ "key": "chapter",
132
+ "label": "Chapter",
133
+ "titleKey": "chapter_title",
134
+ "injectHeading": true,
135
+ "headingTemplate": "{label} {value}: {title}",
136
+ "pageBreak": "between-groups"
137
+ }
138
+ ]
139
+ }
140
+ }
141
+ ```
142
+
143
+ Notes:
144
+
145
+ - `pageBreak` currently supports `none` or `between-groups`.
146
+ - TOC entries are nested by level depth.
147
+ - Missing group key/title values inherit from the previous manuscript file, so you only need to set metadata at structural boundaries.
148
+ - `validate` reports configuration errors for invalid `compileStructure` entries.
149
+
150
+ ## Included examples
151
+
152
+ - `plague-demo`: full configuration — rich metadata (`pov`, `timeline`), three spine categories (`characters`, `locations`, `sources`), cross-linked spine with Wikipedia reference links
153
+ - `docs-demo`: nonfiction documentation configuration — no spine categories, freeform notes only, primarily `status` metadata
154
+
155
+ ## Placeholder edit workflow (`{{...}}` + Cmd+I)
156
+
157
+ This repo includes Copilot instruction files to keep placeholder edits scoped:
158
+
159
+ - `.github/copilot-instructions.md`
160
+ - `.github/instructions/placeholder-fill.instructions.md`
161
+
162
+ Placeholder convention:
163
+
164
+ - Write draft placeholders as `{{...}}` in manuscript prose.
165
+ - Select the placeholder text and run Cmd+I.
166
+ - Use short prompts like `fill placeholder` or `replace only inside {{}}`.
167
+
168
+ Expected behavior:
169
+
170
+ - Replace only the content inside braces.
171
+ - Preserve surrounding sentence/paragraph text.
172
+
173
+ ## Next Steps
174
+
175
+ - Add Mermaid graphs of metadata (entity relationships, co-occurrence, chapter sequence).
176
+
177
+ ## VS Code tasks
178
+
179
+ Tasks are defined in `.vscode/tasks.json` for validate/build/stage-check/export.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ export const markdownExporter = {
4
+ id: "md",
5
+ description: "Copy compiled manuscript markdown",
6
+ canRun() {
7
+ return { ok: true };
8
+ },
9
+ run({ inputPath, outputPath }) {
10
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
11
+ fs.copyFileSync(inputPath, outputPath);
12
+ return { outputPath };
13
+ }
14
+ };
@@ -0,0 +1,65 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { spawnSync } from "node:child_process";
4
+ function hasPandoc() {
5
+ const result = spawnSync("pandoc", ["--version"], { stdio: "ignore" });
6
+ return result.status === 0;
7
+ }
8
+ function hasCommand(command) {
9
+ const result = spawnSync("which", [command], { stdio: "ignore" });
10
+ return result.status === 0;
11
+ }
12
+ function resolvePdfEngine() {
13
+ const preferredEngines = ["tectonic", "xelatex", "lualatex", "pdflatex", "wkhtmltopdf", "weasyprint", "prince", "typst"];
14
+ for (const engine of preferredEngines) {
15
+ if (hasCommand(engine)) {
16
+ return engine;
17
+ }
18
+ }
19
+ return null;
20
+ }
21
+ function getMissingPdfEngineReason() {
22
+ return "No PDF engine found. Install one of: tectonic, xelatex, lualatex, pdflatex, wkhtmltopdf, weasyprint, prince, or typst.";
23
+ }
24
+ export function createPandocExporter(format) {
25
+ return {
26
+ id: format,
27
+ description: `Export ${format.toUpperCase()} with pandoc`,
28
+ canRun() {
29
+ if (!hasPandoc()) {
30
+ return {
31
+ ok: false,
32
+ reason: "pandoc is not installed. Install pandoc to enable docx/pdf/epub exports."
33
+ };
34
+ }
35
+ if (format === "pdf" && !resolvePdfEngine()) {
36
+ return {
37
+ ok: false,
38
+ reason: getMissingPdfEngineReason()
39
+ };
40
+ }
41
+ return { ok: true };
42
+ },
43
+ run({ inputPath, outputPath }) {
44
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
45
+ const args = [inputPath, "-o", outputPath];
46
+ if (format === "pdf") {
47
+ const engine = resolvePdfEngine();
48
+ if (!engine) {
49
+ throw new Error(getMissingPdfEngineReason());
50
+ }
51
+ args.push(`--pdf-engine=${engine}`);
52
+ }
53
+ const result = spawnSync("pandoc", args, {
54
+ encoding: "utf8"
55
+ });
56
+ if (result.status !== 0) {
57
+ const stderr = (result.stderr || "").trim();
58
+ const stdout = (result.stdout || "").trim();
59
+ const details = stderr || stdout || "Unknown pandoc error";
60
+ throw new Error(`pandoc export failed: ${details}`);
61
+ }
62
+ return { outputPath };
63
+ }
64
+ };
65
+ }