stego-cli 0.1.7 → 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.
- package/README.md +3 -1
- package/dist/stego-cli.js +135 -9
- package/package.json +2 -1
- package/projects/fiction-example/package.json +1 -0
- package/projects/stego-docs/manuscript/400-everyday-workflow-and-commands.md +3 -0
- package/projects/stego-docs/package.json +1 -0
- package/projects/stego-docs/spine/commands.md +9 -0
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"));
|
|
@@ -394,18 +409,36 @@ function parseArgs(argv) {
|
|
|
394
409
|
const options = { _: [] };
|
|
395
410
|
for (let i = 0; i < rest.length; i += 1) {
|
|
396
411
|
const token = rest[i];
|
|
397
|
-
if (
|
|
398
|
-
options._.push(
|
|
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;
|
|
399
425
|
continue;
|
|
400
426
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
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);
|
|
405
440
|
continue;
|
|
406
441
|
}
|
|
407
|
-
options[key] = next;
|
|
408
|
-
i += 1;
|
|
409
442
|
}
|
|
410
443
|
return { command, options };
|
|
411
444
|
}
|
|
@@ -607,6 +640,7 @@ function rewriteTemplateProjectPackageScripts(targetRoot) {
|
|
|
607
640
|
scripts.build = "npx --no-install stego build";
|
|
608
641
|
scripts["check-stage"] = "npx --no-install stego check-stage";
|
|
609
642
|
scripts.export = "npx --no-install stego export";
|
|
643
|
+
scripts.new = "npx --no-install stego new";
|
|
610
644
|
projectPackage.scripts = scripts;
|
|
611
645
|
fs.writeFileSync(packageJsonPath, `${JSON.stringify(projectPackage, null, 2)}\n`, "utf8");
|
|
612
646
|
ensureProjectExtensionsRecommendations(projectRoot);
|
|
@@ -697,6 +731,7 @@ function writeInitRootPackageJson(targetRoot) {
|
|
|
697
731
|
scripts: {
|
|
698
732
|
"list-projects": "stego list-projects",
|
|
699
733
|
"new-project": "stego new-project",
|
|
734
|
+
new: "stego new",
|
|
700
735
|
lint: "stego lint",
|
|
701
736
|
validate: "stego validate",
|
|
702
737
|
build: "stego build",
|
|
@@ -712,7 +747,7 @@ function writeInitRootPackageJson(targetRoot) {
|
|
|
712
747
|
fs.writeFileSync(path.join(targetRoot, "package.json"), `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
713
748
|
}
|
|
714
749
|
function printUsage() {
|
|
715
|
-
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 lint --project <project-id> [--manuscript|--spine] [--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`);
|
|
716
751
|
}
|
|
717
752
|
function listProjects() {
|
|
718
753
|
const ids = getProjectIds();
|
|
@@ -774,6 +809,7 @@ async function createProject(projectIdOption, titleOption) {
|
|
|
774
809
|
name: `stego-project-${projectId}`,
|
|
775
810
|
private: true,
|
|
776
811
|
scripts: {
|
|
812
|
+
new: "npx --no-install stego new",
|
|
777
813
|
lint: "npx --no-install stego lint",
|
|
778
814
|
validate: "npx --no-install stego validate",
|
|
779
815
|
build: "npx --no-install stego build",
|
|
@@ -813,6 +849,96 @@ Start writing here.
|
|
|
813
849
|
logLine(`- ${path.relative(repoRoot, projectSettingsPath)}`);
|
|
814
850
|
}
|
|
815
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
|
+
}
|
|
816
942
|
function getProjectIds() {
|
|
817
943
|
const projectsDir = path.join(repoRoot, config.projectsDir);
|
|
818
944
|
if (!fs.existsSync(projectsDir)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stego-cli",
|
|
3
|
-
"version": "0.
|
|
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",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"release:version": "changeset version",
|
|
43
43
|
"list-projects": "node --experimental-strip-types tools/stego-cli.ts list-projects",
|
|
44
44
|
"new-project": "node --experimental-strip-types tools/stego-cli.ts new-project",
|
|
45
|
+
"new": "node --experimental-strip-types tools/stego-cli.ts new",
|
|
45
46
|
"lint": "node --experimental-strip-types tools/stego-cli.ts lint",
|
|
46
47
|
"validate": "node --experimental-strip-types tools/stego-cli.ts validate",
|
|
47
48
|
"build": "node --experimental-strip-types tools/stego-cli.ts build",
|
|
@@ -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",
|
|
@@ -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",
|
|
@@ -25,6 +25,15 @@ label: stego new-project --project <project-id> [--title <title>] [--root <path>
|
|
|
25
25
|
- Related workflows: FLOW-NEW-PROJECT.
|
|
26
26
|
- Related concepts: CON-PROJECT, CON-MANUSCRIPT, CON-NOTES, CON-DIST.
|
|
27
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
|
+
|
|
28
37
|
## CMD-VALIDATE
|
|
29
38
|
label: stego validate --project <project-id> [--file <project-relative-manuscript-path>] [--root <path>]
|
|
30
39
|
|