stego-cli 0.1.6 → 0.2.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.
@@ -0,0 +1,8 @@
1
+ {
2
+ "default": true,
3
+ "MD013": false,
4
+ "MD033": false,
5
+ "MD026": false,
6
+ "MD036": false,
7
+ "MD041": false
8
+ }
package/README.md CHANGED
@@ -19,6 +19,7 @@ npm install
19
19
  stego list-projects
20
20
  stego validate --project fiction-example
21
21
  stego build --project fiction-example
22
+ stego new --project fiction-example
22
23
  ```
23
24
 
24
25
  `stego init` scaffolds two example projects:
@@ -26,7 +27,7 @@ stego build --project fiction-example
26
27
  - `stego-docs` (the full documentation project)
27
28
  - `fiction-example` (a fiction-oriented demo with rich Spine usage)
28
29
 
29
- For day-to-day editing, open a project folder in VS Code (for example `projects/stego-docs`) and use the Stego VS Code extension, which is the official UI for Stego projects.
30
+ For day-to-day editing, open a project folder in VS Code (for example `projects/stego-docs`) and use the [Stego VS Code extension](https://github.com/matt-gold/stego-extension), which is the official UI for Stego projects.
30
31
 
31
32
  ## Full documentation
32
33
 
@@ -48,6 +49,7 @@ Run commands from the workspace root and target a project with `--project`.
48
49
  ```bash
49
50
  stego list-projects
50
51
  stego new-project --project my-book --title "My Book"
52
+ stego new --project fiction-example
51
53
  stego validate --project fiction-example
52
54
  stego build --project fiction-example
53
55
  stego check-stage --project fiction-example --stage revise
package/dist/stego-cli.js CHANGED
@@ -16,6 +16,7 @@ const STATUS_RANK = {
16
16
  final: 4
17
17
  };
18
18
  const RESERVED_COMMENT_PREFIX = "CMT";
19
+ const DEFAULT_NEW_MANUSCRIPT_SLUG = "new-document";
19
20
  const ROOT_CONFIG_FILENAME = "stego.config.json";
20
21
  const PROSE_FONT_PROMPT = "Switch workspace to proportional (prose-style) font? (recommended)";
21
22
  const SCAFFOLD_GITIGNORE_CONTENT = `node_modules/
@@ -54,6 +55,7 @@ stego validate --project fiction-example
54
55
  stego build --project fiction-example
55
56
  stego check-stage --project fiction-example --stage revise
56
57
  stego export --project fiction-example --format md
58
+ stego new --project fiction-example
57
59
  \`\`\`
58
60
 
59
61
  ## Work inside one project
@@ -77,6 +79,12 @@ This keeps your editor context focused and applies the project's recommended ext
77
79
  \`\`\`bash
78
80
  stego new-project --project my-book --title "My Book"
79
81
  \`\`\`
82
+
83
+ ## Add a new manuscript file
84
+
85
+ \`\`\`bash
86
+ stego new --project fiction-example
87
+ \`\`\`
80
88
  `;
81
89
  const PROSE_MARKDOWN_EDITOR_SETTINGS = {
82
90
  "[markdown]": {
@@ -117,6 +125,13 @@ async function main() {
117
125
  activateWorkspace(options);
118
126
  await createProject(readStringOption(options, "project"), readStringOption(options, "title"));
119
127
  return;
128
+ case "new": {
129
+ activateWorkspace(options);
130
+ const project = resolveProject(readStringOption(options, "project"));
131
+ const createdPath = createNewManuscript(project, readStringOption(options, "i"));
132
+ logLine(`Created manuscript: ${createdPath}`);
133
+ return;
134
+ }
120
135
  case "validate": {
121
136
  activateWorkspace(options);
122
137
  const project = resolveProject(readStringOption(options, "project"));
@@ -157,6 +172,18 @@ async function main() {
157
172
  }
158
173
  return;
159
174
  }
175
+ case "lint": {
176
+ activateWorkspace(options);
177
+ const project = resolveProject(readStringOption(options, "project"));
178
+ const selection = resolveLintSelection(options);
179
+ const result = runProjectLint(project, selection);
180
+ printReport(result.issues);
181
+ exitIfErrors(result.issues);
182
+ const scopeLabel = formatLintSelection(selection);
183
+ const fileLabel = result.fileCount === 1 ? "file" : "files";
184
+ logLine(`Lint passed for '${project.id}' (${scopeLabel}, ${result.fileCount} ${fileLabel}).`);
185
+ return;
186
+ }
160
187
  case "export": {
161
188
  activateWorkspace(options);
162
189
  const project = resolveProject(readStringOption(options, "project"));
@@ -382,18 +409,36 @@ function parseArgs(argv) {
382
409
  const options = { _: [] };
383
410
  for (let i = 0; i < rest.length; i += 1) {
384
411
  const token = rest[i];
385
- if (!token.startsWith("--")) {
386
- options._.push(token);
412
+ if (token === "--") {
413
+ options._.push(...rest.slice(i + 1));
414
+ break;
415
+ }
416
+ if (token.startsWith("--")) {
417
+ const key = token.slice(2);
418
+ const next = rest[i + 1];
419
+ if (!next || next.startsWith("-")) {
420
+ options[key] = true;
421
+ continue;
422
+ }
423
+ options[key] = next;
424
+ i += 1;
387
425
  continue;
388
426
  }
389
- const key = token.slice(2);
390
- const next = rest[i + 1];
391
- if (!next || next.startsWith("--")) {
392
- options[key] = true;
427
+ if (token.startsWith("-") && token.length > 1) {
428
+ const key = token.slice(1);
429
+ const next = rest[i + 1];
430
+ if (!next || next.startsWith("-")) {
431
+ options[key] = true;
432
+ continue;
433
+ }
434
+ options[key] = next;
435
+ i += 1;
436
+ continue;
437
+ }
438
+ if (!token.startsWith("--")) {
439
+ options._.push(token);
393
440
  continue;
394
441
  }
395
- options[key] = next;
396
- i += 1;
397
442
  }
398
443
  return { command, options };
399
444
  }
@@ -464,6 +509,7 @@ async function initWorkspace(options) {
464
509
  writeScaffoldGitignore(targetRoot, copiedPaths);
465
510
  writeScaffoldReadme(targetRoot, copiedPaths);
466
511
  copyTemplateAsset(".markdownlint.json", targetRoot, copiedPaths);
512
+ copyTemplateAsset(".markdownlint.manuscript.json", targetRoot, copiedPaths);
467
513
  copyTemplateAsset(".cspell.json", targetRoot, copiedPaths);
468
514
  copyTemplateAsset(ROOT_CONFIG_FILENAME, targetRoot, copiedPaths);
469
515
  copyTemplateAsset("projects", targetRoot, copiedPaths);
@@ -594,6 +640,7 @@ function rewriteTemplateProjectPackageScripts(targetRoot) {
594
640
  scripts.build = "npx --no-install stego build";
595
641
  scripts["check-stage"] = "npx --no-install stego check-stage";
596
642
  scripts.export = "npx --no-install stego export";
643
+ scripts.new = "npx --no-install stego new";
597
644
  projectPackage.scripts = scripts;
598
645
  fs.writeFileSync(packageJsonPath, `${JSON.stringify(projectPackage, null, 2)}\n`, "utf8");
599
646
  ensureProjectExtensionsRecommendations(projectRoot);
@@ -684,6 +731,8 @@ function writeInitRootPackageJson(targetRoot) {
684
731
  scripts: {
685
732
  "list-projects": "stego list-projects",
686
733
  "new-project": "stego new-project",
734
+ new: "stego new",
735
+ lint: "stego lint",
687
736
  validate: "stego validate",
688
737
  build: "stego build",
689
738
  "check-stage": "stego check-stage",
@@ -698,7 +747,7 @@ function writeInitRootPackageJson(targetRoot) {
698
747
  fs.writeFileSync(path.join(targetRoot, "package.json"), `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
699
748
  }
700
749
  function printUsage() {
701
- console.log(`Stego CLI\n\nCommands:\n init [--force]\n list-projects [--root <path>]\n new-project --project <project-id> [--title <title>] [--root <path>]\n validate --project <project-id> [--file <project-relative-manuscript-path>] [--root <path>]\n build --project <project-id> [--root <path>]\n check-stage --project <project-id> --stage <draft|revise|line-edit|proof|final> [--file <project-relative-manuscript-path>] [--root <path>]\n export --project <project-id> --format <md|docx|pdf|epub> [--output <path>] [--root <path>]\n`);
750
+ console.log(`Stego CLI\n\nCommands:\n init [--force]\n list-projects [--root <path>]\n new-project --project <project-id> [--title <title>] [--root <path>]\n new --project <project-id> [--i <prefix>|-i <prefix>] [--root <path>]\n validate --project <project-id> [--file <project-relative-manuscript-path>] [--root <path>]\n build --project <project-id> [--root <path>]\n check-stage --project <project-id> --stage <draft|revise|line-edit|proof|final> [--file <project-relative-manuscript-path>] [--root <path>]\n lint --project <project-id> [--manuscript|--spine] [--root <path>]\n export --project <project-id> --format <md|docx|pdf|epub> [--output <path>] [--root <path>]\n`);
702
751
  }
703
752
  function listProjects() {
704
753
  const ids = getProjectIds();
@@ -729,6 +778,7 @@ async function createProject(projectIdOption, titleOption) {
729
778
  const notesDir = path.join(projectRoot, config.notesDir);
730
779
  fs.mkdirSync(notesDir, { recursive: true });
731
780
  fs.mkdirSync(path.join(projectRoot, config.distDir), { recursive: true });
781
+ const manuscriptDir = path.join(projectRoot, config.chapterDir);
732
782
  const projectJson = {
733
783
  id: projectId,
734
784
  title: titleOption?.trim() || toDisplayTitle(projectId),
@@ -759,6 +809,8 @@ async function createProject(projectIdOption, titleOption) {
759
809
  name: `stego-project-${projectId}`,
760
810
  private: true,
761
811
  scripts: {
812
+ new: "npx --no-install stego new",
813
+ lint: "npx --no-install stego lint",
762
814
  validate: "npx --no-install stego validate",
763
815
  build: "npx --no-install stego build",
764
816
  "check-stage": "npx --no-install stego check-stage",
@@ -767,6 +819,17 @@ async function createProject(projectIdOption, titleOption) {
767
819
  };
768
820
  const projectPackagePath = path.join(projectRoot, "package.json");
769
821
  fs.writeFileSync(projectPackagePath, `${JSON.stringify(projectPackage, null, 2)}\n`, "utf8");
822
+ const starterManuscriptPath = path.join(manuscriptDir, "100-hello-world.md");
823
+ fs.writeFileSync(starterManuscriptPath, `---
824
+ status: draft
825
+ chapter: 1
826
+ chapter_title: Hello World
827
+ ---
828
+
829
+ # Hello World
830
+
831
+ Start writing here.
832
+ `, "utf8");
770
833
  const charactersNotesPath = path.join(spineDir, "characters.md");
771
834
  fs.writeFileSync(charactersNotesPath, "# Characters\n\n", "utf8");
772
835
  const projectExtensionsPath = path.join(projectRoot, ".vscode", "extensions.json");
@@ -779,12 +842,103 @@ async function createProject(projectIdOption, titleOption) {
779
842
  logLine(`Created project: ${path.relative(repoRoot, projectRoot)}`);
780
843
  logLine(`- ${path.relative(repoRoot, projectJsonPath)}`);
781
844
  logLine(`- ${path.relative(repoRoot, projectPackagePath)}`);
845
+ logLine(`- ${path.relative(repoRoot, starterManuscriptPath)}`);
782
846
  logLine(`- ${path.relative(repoRoot, charactersNotesPath)}`);
783
847
  logLine(`- ${path.relative(repoRoot, projectExtensionsPath)}`);
784
848
  if (projectSettingsPath) {
785
849
  logLine(`- ${path.relative(repoRoot, projectSettingsPath)}`);
786
850
  }
787
851
  }
852
+ function createNewManuscript(project, requestedPrefixRaw) {
853
+ fs.mkdirSync(project.manuscriptDir, { recursive: true });
854
+ const requiredMetadataState = resolveRequiredMetadata(project, config);
855
+ const requiredMetadataErrors = requiredMetadataState.issues
856
+ .filter((issue) => issue.level === "error")
857
+ .map((issue) => issue.message);
858
+ if (requiredMetadataErrors.length > 0) {
859
+ throw new Error(`Unable to resolve required metadata for project '${project.id}': ${requiredMetadataErrors.join(" ")}`);
860
+ }
861
+ const existingEntries = listManuscriptOrderEntries(project.manuscriptDir);
862
+ const explicitPrefix = parseManuscriptPrefix(requestedPrefixRaw);
863
+ const nextPrefix = explicitPrefix ?? inferNextManuscriptPrefix(existingEntries);
864
+ const collision = existingEntries.find((entry) => entry.order === nextPrefix);
865
+ if (collision) {
866
+ throw new Error(`Manuscript prefix '${nextPrefix}' is already used by '${collision.filename}'. Re-run with --i <number> to choose an unused prefix.`);
867
+ }
868
+ const filename = `${nextPrefix}-${DEFAULT_NEW_MANUSCRIPT_SLUG}.md`;
869
+ const manuscriptPath = path.join(project.manuscriptDir, filename);
870
+ const content = renderNewManuscriptTemplate(requiredMetadataState.requiredMetadata);
871
+ fs.writeFileSync(manuscriptPath, content, "utf8");
872
+ return path.relative(repoRoot, manuscriptPath);
873
+ }
874
+ function listManuscriptOrderEntries(manuscriptDir) {
875
+ if (!fs.existsSync(manuscriptDir)) {
876
+ return [];
877
+ }
878
+ return fs
879
+ .readdirSync(manuscriptDir, { withFileTypes: true })
880
+ .filter((entry) => entry.isFile() && entry.name.endsWith(".md"))
881
+ .map((entry) => {
882
+ const match = entry.name.match(/^(\d+)[-_]/);
883
+ if (!match) {
884
+ return null;
885
+ }
886
+ return {
887
+ order: Number(match[1]),
888
+ filename: entry.name
889
+ };
890
+ })
891
+ .filter((entry) => entry !== null)
892
+ .sort((a, b) => {
893
+ if (a.order === b.order) {
894
+ return a.filename.localeCompare(b.filename);
895
+ }
896
+ return a.order - b.order;
897
+ });
898
+ }
899
+ function parseManuscriptPrefix(raw) {
900
+ if (raw == null) {
901
+ return undefined;
902
+ }
903
+ const normalized = raw.trim();
904
+ if (!normalized) {
905
+ throw new Error("Option --i/-i requires a numeric value.");
906
+ }
907
+ if (!/^\d+$/.test(normalized)) {
908
+ throw new Error(`Invalid manuscript prefix '${raw}'. Use a non-negative integer.`);
909
+ }
910
+ const parsed = Number(normalized);
911
+ if (!Number.isSafeInteger(parsed)) {
912
+ throw new Error(`Invalid manuscript prefix '${raw}'. Use a smaller integer value.`);
913
+ }
914
+ return parsed;
915
+ }
916
+ function inferNextManuscriptPrefix(entries) {
917
+ if (entries.length === 0) {
918
+ return 100;
919
+ }
920
+ if (entries.length === 1) {
921
+ return entries[0].order + 100;
922
+ }
923
+ const previous = entries[entries.length - 2].order;
924
+ const latest = entries[entries.length - 1].order;
925
+ const step = latest - previous;
926
+ return latest + (step > 0 ? step : 1);
927
+ }
928
+ function renderNewManuscriptTemplate(requiredMetadata) {
929
+ const lines = ["---", "status: draft"];
930
+ const seenKeys = new Set(["status"]);
931
+ for (const key of requiredMetadata) {
932
+ const normalized = key.trim();
933
+ if (!normalized || seenKeys.has(normalized)) {
934
+ continue;
935
+ }
936
+ seenKeys.add(normalized);
937
+ lines.push(`${normalized}:`);
938
+ }
939
+ lines.push("---", "", "# New Document", "");
940
+ return `${lines.join("\n")}\n`;
941
+ }
788
942
  function getProjectIds() {
789
943
  const projectsDir = path.join(repoRoot, config.projectsDir);
790
944
  if (!fs.existsSync(projectsDir)) {
@@ -1602,10 +1756,10 @@ function runStageCheck(project, runtimeConfig, stage, onlyFile) {
1602
1756
  const chapterPaths = report.chapters.map((chapter) => chapter.path);
1603
1757
  const spineWords = collectSpineWordsForSpellcheck(report.spineState.ids);
1604
1758
  if (policy.enforceMarkdownlint) {
1605
- issues.push(...runMarkdownlint(project, chapterPaths, true));
1759
+ issues.push(...runMarkdownlint(project, chapterPaths, true, "manuscript"));
1606
1760
  }
1607
1761
  else {
1608
- issues.push(...runMarkdownlint(project, chapterPaths, false));
1762
+ issues.push(...runMarkdownlint(project, chapterPaths, false, "manuscript"));
1609
1763
  }
1610
1764
  if (policy.enforceCSpell) {
1611
1765
  issues.push(...runCSpell(chapterPaths, true, spineWords));
@@ -1615,20 +1769,133 @@ function runStageCheck(project, runtimeConfig, stage, onlyFile) {
1615
1769
  }
1616
1770
  return { chapters: report.chapters, issues };
1617
1771
  }
1618
- function runMarkdownlint(project, files, required) {
1772
+ function resolveLintSelection(options) {
1773
+ const manuscript = readBooleanOption(options, "manuscript");
1774
+ const spine = readBooleanOption(options, "spine");
1775
+ if (!manuscript && !spine) {
1776
+ return { manuscript: true, spine: true };
1777
+ }
1778
+ return { manuscript, spine };
1779
+ }
1780
+ function formatLintSelection(selection) {
1781
+ if (selection.manuscript && selection.spine) {
1782
+ return "manuscript + spine";
1783
+ }
1784
+ if (selection.manuscript) {
1785
+ return "manuscript";
1786
+ }
1787
+ if (selection.spine) {
1788
+ return "spine";
1789
+ }
1790
+ return "none";
1791
+ }
1792
+ function runProjectLint(project, selection) {
1793
+ const issues = [];
1794
+ let fileCount = 0;
1795
+ if (selection.manuscript) {
1796
+ const manuscriptFiles = collectTopLevelMarkdownFiles(project.manuscriptDir);
1797
+ if (manuscriptFiles.length === 0) {
1798
+ issues.push(makeIssue("error", "lint", `No manuscript markdown files found in ${project.manuscriptDir}`));
1799
+ }
1800
+ else {
1801
+ fileCount += manuscriptFiles.length;
1802
+ issues.push(...runMarkdownlint(project, manuscriptFiles, true, "manuscript"));
1803
+ }
1804
+ }
1805
+ if (selection.spine) {
1806
+ const spineLintState = collectSpineLintMarkdownFiles(project);
1807
+ issues.push(...spineLintState.issues);
1808
+ if (spineLintState.files.length > 0) {
1809
+ fileCount += spineLintState.files.length;
1810
+ issues.push(...runMarkdownlint(project, spineLintState.files, true, "default"));
1811
+ }
1812
+ }
1813
+ if (fileCount === 0 && issues.length === 0) {
1814
+ issues.push(makeIssue("error", "lint", `No markdown files found for lint scope '${formatLintSelection(selection)}' in project '${project.id}'.`));
1815
+ }
1816
+ return { issues, fileCount };
1817
+ }
1818
+ function collectSpineLintMarkdownFiles(project) {
1819
+ const issues = [];
1820
+ const files = new Set();
1821
+ addMarkdownFilesFromDirectory(files, project.spineDir, true);
1822
+ if (!fs.existsSync(project.spineDir)) {
1823
+ issues.push(makeIssue("warning", "lint", `Missing spine directory: ${project.spineDir}`));
1824
+ }
1825
+ addMarkdownFilesFromDirectory(files, project.notesDir, true);
1826
+ if (!fs.existsSync(project.notesDir)) {
1827
+ issues.push(makeIssue("warning", "lint", `Missing notes directory: ${project.notesDir}`));
1828
+ }
1829
+ for (const file of collectTopLevelMarkdownFiles(project.root)) {
1830
+ files.add(file);
1831
+ }
1832
+ const sortedFiles = Array.from(files).sort();
1833
+ if (sortedFiles.length === 0) {
1834
+ issues.push(makeIssue("error", "lint", `No spine/notes markdown files found in ${project.spineDir}, ${project.notesDir}, or project root.`));
1835
+ }
1836
+ return { files: sortedFiles, issues };
1837
+ }
1838
+ function collectTopLevelMarkdownFiles(directory) {
1839
+ if (!fs.existsSync(directory) || !fs.statSync(directory).isDirectory()) {
1840
+ return [];
1841
+ }
1842
+ return fs
1843
+ .readdirSync(directory, { withFileTypes: true })
1844
+ .filter((entry) => entry.isFile() && entry.name.endsWith(".md"))
1845
+ .map((entry) => path.join(directory, entry.name))
1846
+ .sort();
1847
+ }
1848
+ function addMarkdownFilesFromDirectory(target, directory, recursive) {
1849
+ if (!fs.existsSync(directory) || !fs.statSync(directory).isDirectory()) {
1850
+ return;
1851
+ }
1852
+ const stack = [directory];
1853
+ while (stack.length > 0) {
1854
+ const current = stack.pop();
1855
+ if (!current) {
1856
+ continue;
1857
+ }
1858
+ const entries = fs.readdirSync(current, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
1859
+ for (const entry of entries) {
1860
+ const fullPath = path.join(current, entry.name);
1861
+ if (entry.isFile() && entry.name.endsWith(".md")) {
1862
+ target.add(fullPath);
1863
+ continue;
1864
+ }
1865
+ if (recursive && entry.isDirectory()) {
1866
+ stack.push(fullPath);
1867
+ }
1868
+ }
1869
+ }
1870
+ }
1871
+ function runMarkdownlint(project, files, required, profile = "default") {
1872
+ if (files.length === 0) {
1873
+ return [];
1874
+ }
1619
1875
  const markdownlintCommand = resolveCommand("markdownlint");
1620
1876
  if (!markdownlintCommand) {
1621
1877
  if (required) {
1622
1878
  return [
1623
- makeIssue("error", "tooling", "markdownlint is required for this stage but not installed. Run 'npm i' in the repo root.")
1879
+ makeIssue("error", "tooling", "markdownlint is required for this command but not installed. Run 'npm i' in the repo root.")
1624
1880
  ];
1625
1881
  }
1626
1882
  return [];
1627
1883
  }
1628
- const projectConfigPath = path.join(project.root, ".markdownlint.json");
1629
- const markdownlintConfigPath = fs.existsSync(projectConfigPath)
1630
- ? projectConfigPath
1631
- : path.join(repoRoot, ".markdownlint.json");
1884
+ const manuscriptProjectConfigPath = path.join(project.root, ".markdownlint.manuscript.json");
1885
+ const manuscriptRepoConfigPath = path.join(repoRoot, ".markdownlint.manuscript.json");
1886
+ const defaultProjectConfigPath = path.join(project.root, ".markdownlint.json");
1887
+ const defaultRepoConfigPath = path.join(repoRoot, ".markdownlint.json");
1888
+ const markdownlintConfigPath = profile === "manuscript"
1889
+ ? (fs.existsSync(manuscriptProjectConfigPath)
1890
+ ? manuscriptProjectConfigPath
1891
+ : fs.existsSync(manuscriptRepoConfigPath)
1892
+ ? manuscriptRepoConfigPath
1893
+ : fs.existsSync(defaultProjectConfigPath)
1894
+ ? defaultProjectConfigPath
1895
+ : defaultRepoConfigPath)
1896
+ : (fs.existsSync(defaultProjectConfigPath)
1897
+ ? defaultProjectConfigPath
1898
+ : defaultRepoConfigPath);
1632
1899
  const prepared = prepareFilesWithoutComments(files);
1633
1900
  try {
1634
1901
  const result = spawnSync(markdownlintCommand, ["--config", markdownlintConfigPath, ...prepared.files], {
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "stego-cli",
3
- "version": "0.1.6",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "Installable CLI for the Stego writing monorepo workflow.",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "https://github.com/matt-gold/stego.git"
9
+ "url": "https://github.com/matt-gold/stego-cli.git"
10
10
  },
11
11
  "bugs": {
12
- "url": "https://github.com/matt-gold/stego/issues"
12
+ "url": "https://github.com/matt-gold/stego-cli/issues"
13
13
  },
14
- "homepage": "https://github.com/matt-gold/stego#readme",
14
+ "homepage": "https://github.com/matt-gold/stego-cli#readme",
15
15
  "bin": {
16
16
  "stego": "dist/stego-cli.js"
17
17
  },
@@ -28,6 +28,7 @@
28
28
  ".vscode/extensions.json",
29
29
  ".gitignore",
30
30
  ".markdownlint.json",
31
+ ".markdownlint.manuscript.json",
31
32
  ".cspell.json",
32
33
  "stego.config.json",
33
34
  "README.md"
@@ -41,6 +42,8 @@
41
42
  "release:version": "changeset version",
42
43
  "list-projects": "node --experimental-strip-types tools/stego-cli.ts list-projects",
43
44
  "new-project": "node --experimental-strip-types tools/stego-cli.ts new-project",
45
+ "new": "node --experimental-strip-types tools/stego-cli.ts new",
46
+ "lint": "node --experimental-strip-types tools/stego-cli.ts lint",
44
47
  "validate": "node --experimental-strip-types tools/stego-cli.ts validate",
45
48
  "build": "node --experimental-strip-types tools/stego-cli.ts build",
46
49
  "check-stage": "node --experimental-strip-types tools/stego-cli.ts check-stage",
@@ -1,4 +1,5 @@
1
1
  {
2
2
  "default": true,
3
- "MD041": false
3
+ "MD013": false,
4
+ "MD033": false
4
5
  }
@@ -0,0 +1,8 @@
1
+ {
2
+ "default": true,
3
+ "MD013": false,
4
+ "MD033": false,
5
+ "MD026": false,
6
+ "MD036": false,
7
+ "MD041": false
8
+ }
@@ -2,6 +2,7 @@
2
2
  "name": "stego-project-fiction-example",
3
3
  "private": true,
4
4
  "scripts": {
5
+ "new": "node --experimental-strip-types ../../tools/stego-cli.ts new",
5
6
  "validate": "node --experimental-strip-types ../../tools/stego-cli.ts validate",
6
7
  "build": "node --experimental-strip-types ../../tools/stego-cli.ts build",
7
8
  "check-stage": "node --experimental-strip-types ../../tools/stego-cli.ts check-stage",
@@ -1,6 +1,7 @@
1
1
  # Characters
2
2
 
3
3
  ## CHAR-MATTHAEUS
4
+ label: Magister Matthaeus de Rota
4
5
 
5
6
  - Magister Matthaeus de Rota
6
7
  - Role: Scholar of medicine and astrology at the University of Paris
@@ -9,6 +10,7 @@
9
10
  - Arc: Builds a total explanation of the {{pestilence}} and dies at peace inside it; the question is whether his peace is understanding or its most sophisticated substitute
10
11
 
11
12
  ## CHAR-ETIENNE
13
+ label: Etienne of Saint-Marcel
12
14
 
13
15
  - Etienne of Saint-Marcel
14
16
  - Role: Young cleric-scribe attached to Matthaeus (CHAR-MATTHAEUS)
@@ -16,6 +18,7 @@
16
18
  - Function: The story's witness; records the system without fully believing it, but also without offering an alternative
17
19
 
18
20
  ## CHAR-AGNES
21
+ label: Agnes the apothecary
19
22
 
20
23
  - Agnes the apothecary
21
24
  - Role: Compounder and practical healer near the Petit Pont; supplies Hôtel-Dieu (LOC-HOTELDIEU)
@@ -24,6 +27,7 @@
24
27
  - Function: The counterweight; asks whether a system that can absorb any evidence without changing is a system or a habit
25
28
 
26
29
  ## CHAR-RAOUL
30
+ label: Canon Raoul de Villiers
27
31
 
28
32
  - Canon Raoul de Villiers
29
33
  - Role: Cathedral canon and royal intermediary overseeing the inquiry
@@ -1,6 +1,7 @@
1
1
  # Locations
2
2
 
3
3
  ## LOC-COLLEGE
4
+ label: College chamber near the Rue Saint-Jacques
4
5
 
5
6
  - College chamber near the Rue Saint-Jacques
6
7
  - Study and disputation room
@@ -9,6 +10,7 @@
9
10
  - Significance: The space where the system is built; physically separated from the wards and streets where the plague actually operates
10
11
 
11
12
  ## LOC-HOTELDIEU
13
+ label: "Hôtel-Dieu"
12
14
 
13
15
  - Hôtel-Dieu
14
16
  - Hospital ward near Notre-Dame
@@ -17,6 +19,7 @@
17
19
  - Significance: Where data is collected; the distance between this room and the college chamber is the distance between observation and explanation
18
20
 
19
21
  ## LOC-QUAY
22
+ label: Grain quay on the Seine
20
23
 
21
24
  - Grain quay on the Seine
22
25
  - River unloading zone
@@ -25,6 +28,7 @@
25
28
  - Significance: Agnes's observations (SRC-WARD-DATA) trace infection along this route; it is the strongest evidence for material transmission and the hardest for the celestial framework (SRC-CONJUNCTION) to accommodate
26
29
 
27
30
  ## LOC-CHARNEL
31
+ label: Charnel precinct at the cemetery edge
28
32
 
29
33
  - Charnel precinct at the cemetery edge
30
34
  - Overflow burial ground
@@ -1,6 +1,7 @@
1
1
  # Sources
2
2
 
3
3
  ## SRC-CONJUNCTION
4
+ label: The Great Conjunction theory
4
5
 
5
6
  - The Great Conjunction theory
6
7
  - Tradition: University astrology ([Paris faculty opinion of 1348](https://en.wikipedia.org/wiki/Black_Death#Medical_knowledge))
@@ -10,6 +11,7 @@
10
11
  - Limitation: Explains everything at once, which means it predicts nothing in particular
11
12
 
12
13
  ## SRC-GALEN
14
+ label: Galenic humoral medicine
13
15
 
14
16
  - Galenic humoral medicine
15
17
  - Tradition: Greco-Arabic medical inheritance
@@ -19,6 +21,7 @@
19
21
  - Limitation: Cannot explain why entire neighborhoods fall at once regardless of individual constitution
20
22
 
21
23
  ## SRC-WARD-DATA
24
+ label: "Agnes's ward observations"
22
25
 
23
26
  - Agnes's ward observations
24
27
  - Tradition: None; empirical record-keeping without institutional backing
@@ -10,6 +10,7 @@ concepts:
10
10
  commands:
11
11
  - CMD-LIST-PROJECTS
12
12
  - CMD-NEW-PROJECT
13
+ - CMD-NEW
13
14
  - CMD-VALIDATE
14
15
  - CMD-BUILD
15
16
  - CMD-CHECK-STAGE
@@ -41,6 +42,7 @@ From the workspace root, target a project with `--project`.
41
42
 
42
43
  ```bash
43
44
  stego list-projects
45
+ stego new --project fiction-example
44
46
  stego validate --project fiction-example
45
47
  stego build --project fiction-example
46
48
  stego check-stage --project fiction-example --stage revise
@@ -53,6 +55,7 @@ Projects also include local npm scripts. These are useful when you want to stay
53
55
 
54
56
  ```bash
55
57
  cd projects/fiction-example
58
+ npm run new
56
59
  npm run validate
57
60
  npm run build
58
61
  npm run check-stage -- --stage revise
@@ -2,6 +2,7 @@
2
2
  "name": "stego-project-stego-docs",
3
3
  "private": true,
4
4
  "scripts": {
5
+ "new": "node --experimental-strip-types ../../tools/stego-cli.ts new",
5
6
  "validate": "node --experimental-strip-types ../../tools/stego-cli.ts validate",
6
7
  "build": "node --experimental-strip-types ../../tools/stego-cli.ts build",
7
8
  "check-stage": "node --experimental-strip-types ../../tools/stego-cli.ts check-stage",
@@ -1,6 +1,7 @@
1
1
  # Commands
2
2
 
3
3
  ## CMD-INIT
4
+ label: stego init [--force]
4
5
 
5
6
  - `stego init [--force]`
6
7
  - Initialize a Stego workspace in the current directory.
@@ -9,6 +10,7 @@
9
10
  - Related integrations: INT-VSCODE.
10
11
 
11
12
  ## CMD-LIST-PROJECTS
13
+ label: stego list-projects [--root <path>]
12
14
 
13
15
  - `stego list-projects [--root <path>]`
14
16
  - List projects found in the current workspace.
@@ -16,13 +18,24 @@
16
18
  - Related concepts: CON-WORKSPACE, CON-PROJECT.
17
19
 
18
20
  ## CMD-NEW-PROJECT
21
+ label: stego new-project --project <project-id> [--title <title>] [--root <path>]
19
22
 
20
23
  - `stego new-project --project <project-id> [--title <title>] [--root <path>]`
21
24
  - Scaffold a new project under `projects/`.
22
25
  - Related workflows: FLOW-NEW-PROJECT.
23
26
  - Related concepts: CON-PROJECT, CON-MANUSCRIPT, CON-NOTES, CON-DIST.
24
27
 
28
+ ## CMD-NEW
29
+ label: stego new --project <project-id> [--i <prefix>|-i <prefix>] [--root <path>]
30
+
31
+ - `stego new --project <project-id> [--i <prefix>|-i <prefix>] [--root <path>]`
32
+ - Create a new manuscript file with an inferred numeric prefix and draft frontmatter.
33
+ - Related workflows: FLOW-DAILY-WRITING.
34
+ - Related concepts: CON-MANUSCRIPT, CON-METADATA.
35
+ - Related configuration: CFG-REQUIRED-METADATA.
36
+
25
37
  ## CMD-VALIDATE
38
+ label: stego validate --project <project-id> [--file <project-relative-manuscript-path>] [--root <path>]
26
39
 
27
40
  - `stego validate --project <project-id> [--file <project-relative-manuscript-path>] [--root <path>]`
28
41
  - Validate project configuration, manuscript structure, metadata, and references.
@@ -31,6 +44,7 @@
31
44
  - Related configuration: CFG-REQUIRED-METADATA, CFG-SPINE-CATEGORIES.
32
45
 
33
46
  ## CMD-BUILD
47
+ label: stego build --project <project-id> [--root <path>]
34
48
 
35
49
  - `stego build --project <project-id> [--root <path>]`
36
50
  - Compile manuscript files into one generated markdown output.
@@ -39,6 +53,7 @@
39
53
  - Related configuration: CFG-COMPILE-STRUCTURE, CFG-COMPILE-LEVELS.
40
54
 
41
55
  ## CMD-CHECK-STAGE
56
+ label: stego check-stage --project <project-id> --stage <draft|revise|line-edit|proof|final> [--file <project-relative-manuscript-path>] [--root <path>]
42
57
 
43
58
  - `stego check-stage --project <project-id> --stage <draft|revise|line-edit|proof|final> [--file <project-relative-manuscript-path>] [--root <path>]`
44
59
  - Run stage-aware checks for a requested editorial stage.
@@ -47,6 +62,7 @@
47
62
  - Related configuration: CFG-STAGE-POLICIES, CFG-ALLOWED-STATUSES.
48
63
 
49
64
  ## CMD-EXPORT
65
+ label: stego export --project <project-id> --format <md|docx|pdf|epub> [--output <path>] [--root <path>]
50
66
 
51
67
  - `stego export --project <project-id> --format <md|docx|pdf|epub> [--output <path>] [--root <path>]`
52
68
  - Export compiled output to target formats.
@@ -1,48 +1,56 @@
1
1
  # Concepts
2
2
 
3
3
  ## CON-WORKSPACE
4
+ label: A Stego workspace is the root directory containing `stego.config.json` and `projects/`.
4
5
 
5
6
  - A Stego workspace is the root directory containing `stego.config.json` and `projects/`.
6
7
  - Related commands: CMD-INIT, CMD-LIST-PROJECTS.
7
8
  - Related workflows: FLOW-INIT-WORKSPACE.
8
9
 
9
10
  ## CON-PROJECT
11
+ label: A project is one writing unit inside `projects/<project-id>/` with its own `stego-project.json` and manuscript files in `/projects/<project-id>/manuscripts`.
10
12
 
11
13
  - A project is one writing unit inside `projects/<project-id>/` with its own `stego-project.json` and manuscript files in `/projects/<project-id>/manuscripts`.
12
14
  - Related commands: CMD-NEW-PROJECT, CMD-VALIDATE, CMD-BUILD.
13
15
  - Related workflows: FLOW-NEW-PROJECT, FLOW-DAILY-WRITING.
14
16
 
15
17
  ## CON-MANUSCRIPT
18
+ label: `manuscript/` contains canonical source writing files ordered by filename prefix.
16
19
 
17
20
  - `manuscript/` contains canonical source writing files ordered by filename prefix.
18
21
  - Related commands: CMD-VALIDATE, CMD-BUILD.
19
22
  - Related configuration: CFG-REQUIRED-METADATA, CFG-COMPILE-STRUCTURE.
20
23
 
21
24
  ## CON-SPINE
25
+ label: `spine/` stores canonical entities used for continuity and navigation.
22
26
 
23
27
  - `spine/` stores canonical entities used for continuity and navigation.
24
28
  - Related configuration: CFG-SPINE-CATEGORIES.
25
29
  - Related integrations: INT-STEGO-EXTENSION.
26
30
 
27
31
  ## CON-NOTES
32
+ label: `notes/` contains supporting material that is not part of compiled manuscript output.
28
33
 
29
34
  - `notes/` contains supporting material that is not part of compiled manuscript output.
30
35
  - Related commands: CMD-NEW-PROJECT.
31
36
  - Related workflows: FLOW-DAILY-WRITING.
32
37
 
33
38
  ## CON-DIST
39
+ label: `dist/` is generated output only and can be rebuilt from sources.
34
40
 
35
41
  - `dist/` is generated output only and can be rebuilt from sources.
36
42
  - Related commands: CMD-BUILD, CMD-EXPORT.
37
43
  - Related workflows: FLOW-BUILD-EXPORT, FLOW-PROOF-RELEASE.
38
44
 
39
45
  ## CON-METADATA
46
+ label: "Frontmatter metadata drives validation, stage checks, grouping, and continuity references."
40
47
 
41
48
  - Frontmatter metadata drives validation, stage checks, grouping, and continuity references.
42
49
  - Related commands: CMD-VALIDATE, CMD-CHECK-STAGE.
43
50
  - Related configuration: CFG-REQUIRED-METADATA, CFG-ALLOWED-STATUSES.
44
51
 
45
52
  ## CON-STAGE-GATE
53
+ label: Stage gates apply stricter checks as work moves from drafting to release.
46
54
 
47
55
  - Stage gates apply stricter checks as work moves from drafting to release.
48
56
  - Related commands: CMD-CHECK-STAGE.
@@ -50,12 +58,14 @@
50
58
  - Related configuration: CFG-STAGE-POLICIES.
51
59
 
52
60
  ## CON-COMPILE-STRUCTURE
61
+ label: Compile structure groups ordered files into larger sections during build.
53
62
 
54
63
  - Compile structure groups ordered files into larger sections during build.
55
64
  - Related commands: CMD-BUILD.
56
65
  - Related configuration: CFG-COMPILE-STRUCTURE, CFG-COMPILE-LEVELS.
57
66
 
58
67
  ## CON-SPINE-CATEGORY
68
+ label: "A spine category defines a metadata key, ID prefix, and canonical notes file."
59
69
 
60
70
  - A spine category defines a metadata key, ID prefix, and canonical notes file.
61
71
  - Related concepts: CON-SPINE, CON-METADATA.
@@ -1,48 +1,56 @@
1
1
  # Configuration
2
2
 
3
3
  ## CFG-STEGO-CONFIG
4
+ label: Workspace-level configuration in `stego.config.json` defines shared directories and stage policies.
4
5
 
5
6
  - Workspace-level configuration in `stego.config.json` defines shared directories and stage policies.
6
7
  - Related concepts: CON-WORKSPACE.
7
8
  - Related commands: CMD-LIST-PROJECTS, CMD-CHECK-STAGE.
8
9
 
9
10
  ## CFG-STEGO-PROJECT
11
+ label: "Project-level configuration in `stego-project.json` defines metadata rules, grouping, and spine categories."
10
12
 
11
13
  - Project-level configuration in `stego-project.json` defines metadata rules, grouping, and spine categories.
12
14
  - Related concepts: CON-PROJECT, CON-METADATA, CON-SPINE.
13
15
  - Related commands: CMD-VALIDATE, CMD-BUILD.
14
16
 
15
17
  ## CFG-REQUIRED-METADATA
18
+ label: Advisory list of frontmatter keys expected in manuscript files.
16
19
 
17
20
  - Advisory list of frontmatter keys expected in manuscript files.
18
21
  - Related concepts: CON-METADATA.
19
22
  - Related commands: CMD-VALIDATE.
20
23
 
21
24
  ## CFG-COMPILE-STRUCTURE
25
+ label: Build grouping configuration that defines structural levels and heading behavior.
22
26
 
23
27
  - Build grouping configuration that defines structural levels and heading behavior.
24
28
  - Related concepts: CON-COMPILE-STRUCTURE.
25
29
  - Related commands: CMD-BUILD.
26
30
 
27
31
  ## CFG-COMPILE-LEVELS
32
+ label: "The ordered `levels` array inside compile structure, with keys, labels, title keys, and page break options."
28
33
 
29
34
  - The ordered `levels` array inside compile structure, with keys, labels, title keys, and page break options.
30
35
  - Related configuration: CFG-COMPILE-STRUCTURE.
31
36
  - Related concepts: CON-COMPILE-STRUCTURE.
32
37
 
33
38
  ## CFG-SPINE-CATEGORIES
39
+ label: Per-project category definitions mapping metadata keys to ID prefixes and spine files.
34
40
 
35
41
  - Per-project category definitions mapping metadata keys to ID prefixes and spine files.
36
42
  - Related concepts: CON-SPINE, CON-SPINE-CATEGORY.
37
43
  - Related commands: CMD-VALIDATE.
38
44
 
39
45
  ## CFG-STAGE-POLICIES
46
+ label: Stage policy settings determine which checks are enforced at each stage.
40
47
 
41
48
  - Stage policy settings determine which checks are enforced at each stage.
42
49
  - Related concepts: CON-STAGE-GATE.
43
50
  - Related commands: CMD-CHECK-STAGE.
44
51
 
45
52
  ## CFG-ALLOWED-STATUSES
53
+ label: Workspace-level list of allowed manuscript statuses.
46
54
 
47
55
  - Workspace-level list of allowed manuscript statuses.
48
56
  - Related concepts: CON-METADATA, CON-STAGE-GATE.
@@ -1,36 +1,42 @@
1
1
  # Integrations
2
2
 
3
3
  ## INT-VSCODE
4
+ label: VS Code is the primary editor environment for Stego projects.
4
5
 
5
6
  - VS Code is the primary editor environment for Stego projects.
6
7
  - Related workflows: FLOW-INIT-WORKSPACE, FLOW-DAILY-WRITING.
7
8
  - Related commands: CMD-INIT.
8
9
 
9
10
  ## INT-STEGO-EXTENSION
11
+ label: "The Stego VS Code extension is the official UI for Stego projects, including status controls, checks, and Spine Browser navigation."
10
12
 
11
13
  - The Stego VS Code extension is the official UI for Stego projects, including status controls, checks, and Spine Browser navigation.
12
14
  - Related concepts: CON-SPINE, CON-STAGE-GATE.
13
15
  - Related workflows: FLOW-DAILY-WRITING, FLOW-STAGE-PROMOTION.
14
16
 
15
17
  ## INT-SAURUS-EXTENSION
18
+ label: The Saurus extension complements prose editing and research workflows in project folders.
16
19
 
17
20
  - The Saurus extension complements prose editing and research workflows in project folders.
18
21
  - Related integrations: INT-VSCODE.
19
22
  - Related workflows: FLOW-DAILY-WRITING.
20
23
 
21
24
  ## INT-PANDOC
25
+ label: "Pandoc is used for optional export formats such as docx, pdf, and epub."
22
26
 
23
27
  - Pandoc is used for optional export formats such as docx, pdf, and epub.
24
28
  - Related commands: CMD-EXPORT.
25
29
  - Related workflows: FLOW-BUILD-EXPORT.
26
30
 
27
31
  ## INT-MARKDOWNLINT
32
+ label: Markdownlint is used in stricter proofreading and release stages.
28
33
 
29
34
  - Markdownlint is used in stricter proofreading and release stages.
30
35
  - Related concepts: CON-STAGE-GATE.
31
36
  - Related workflows: FLOW-PROOF-RELEASE.
32
37
 
33
38
  ## INT-CSPELL
39
+ label: CSpell supports spelling and terminology checks during later-stage quality passes.
34
40
 
35
41
  - CSpell supports spelling and terminology checks during later-stage quality passes.
36
42
  - Related concepts: CON-STAGE-GATE.
@@ -1,12 +1,14 @@
1
1
  # Workflows
2
2
 
3
3
  ## FLOW-INIT-WORKSPACE
4
+ label: "Install the CLI, initialize a workspace, install local dev tools, and inspect scaffolded projects."
4
5
 
5
6
  - Install the CLI, initialize a workspace, install local dev tools, and inspect scaffolded projects.
6
7
  - Related commands: CMD-INIT, CMD-LIST-PROJECTS.
7
8
  - Related concepts: CON-WORKSPACE, CON-PROJECT.
8
9
 
9
10
  ## FLOW-DAILY-WRITING
11
+ label: "Open one project, write in manuscript files, validate, build, and commit progress."
10
12
 
11
13
  - Open one project, write in manuscript files, validate, build, and commit progress.
12
14
  - Related commands: CMD-VALIDATE, CMD-BUILD.
@@ -14,6 +16,7 @@
14
16
  - Related integrations: INT-VSCODE.
15
17
 
16
18
  ## FLOW-NEW-PROJECT
19
+ label: "Create a new project, review generated folders, and configure project metadata rules."
17
20
 
18
21
  - Create a new project, review generated folders, and configure project metadata rules.
19
22
  - Related commands: CMD-NEW-PROJECT, CMD-VALIDATE.
@@ -21,6 +24,7 @@
21
24
  - Related configuration: CFG-STEGO-PROJECT.
22
25
 
23
26
  ## FLOW-STAGE-PROMOTION
27
+ label: Move files through statuses and verify readiness with stage checks.
24
28
 
25
29
  - Move files through statuses and verify readiness with stage checks.
26
30
  - Related commands: CMD-CHECK-STAGE, CMD-VALIDATE.
@@ -28,6 +32,7 @@
28
32
  - Related configuration: CFG-STAGE-POLICIES, CFG-ALLOWED-STATUSES.
29
33
 
30
34
  ## FLOW-BUILD-EXPORT
35
+ label: Build a compiled markdown manuscript and export distribution formats.
31
36
 
32
37
  - Build a compiled markdown manuscript and export distribution formats.
33
38
  - Related commands: CMD-BUILD, CMD-EXPORT.
@@ -35,6 +40,7 @@
35
40
  - Related integrations: INT-PANDOC.
36
41
 
37
42
  ## FLOW-PROOF-RELEASE
43
+ label: "Run strict checks, build outputs, export artifacts, and archive release files."
38
44
 
39
45
  - Run strict checks, build outputs, export artifacts, and archive release files.
40
46
  - Related commands: CMD-CHECK-STAGE, CMD-BUILD, CMD-EXPORT.