stego-cli 0.4.0 → 0.4.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.
- package/.vscode/extensions.json +7 -0
- package/README.md +35 -0
- package/dist/stego-cli.js +171 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -70,6 +70,41 @@ Spine V2 is directory-inferred:
|
|
|
70
70
|
|
|
71
71
|
Projects also include local npm scripts so you can work from inside a project directory.
|
|
72
72
|
|
|
73
|
+
## Complete CLI command reference
|
|
74
|
+
|
|
75
|
+
The full command surface is available via:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
stego --help
|
|
79
|
+
stego --version
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Current `stego --help` command index:
|
|
83
|
+
|
|
84
|
+
```text
|
|
85
|
+
init [--force]
|
|
86
|
+
list-projects [--root <path>]
|
|
87
|
+
new-project --project <project-id> [--title <title>] [--prose-font <yes|no|prompt>] [--format <text|json>] [--root <path>]
|
|
88
|
+
new --project <project-id> [--i <prefix>|-i <prefix>] [--filename <name>] [--format <text|json>] [--root <path>]
|
|
89
|
+
validate --project <project-id> [--file <project-relative-manuscript-path>] [--root <path>]
|
|
90
|
+
build --project <project-id> [--root <path>]
|
|
91
|
+
check-stage --project <project-id> --stage <draft|revise|line-edit|proof|final> [--file <project-relative-manuscript-path>] [--root <path>]
|
|
92
|
+
lint --project <project-id> [--manuscript|--spine] [--root <path>]
|
|
93
|
+
export --project <project-id> --format <md|docx|pdf|epub> [--output <path>] [--root <path>]
|
|
94
|
+
spine read --project <project-id> [--format <text|json>] [--root <path>]
|
|
95
|
+
spine new-category --project <project-id> --key <category> [--label <label>] [--require-metadata] [--format <text|json>] [--root <path>]
|
|
96
|
+
spine new --project <project-id> --category <category> [--filename <relative-path>] [--format <text|json>] [--root <path>]
|
|
97
|
+
metadata read <markdown-path> [--format <text|json>]
|
|
98
|
+
metadata apply <markdown-path> --input <path|-> [--format <text|json>]
|
|
99
|
+
comments read <manuscript> [--format <text|json>]
|
|
100
|
+
comments add <manuscript> [--message <text> | --input <path|->] [--author <name>] [--start-line <n> --start-col <n> --end-line <n> --end-col <n>] [--cursor-line <n>] [--format <text|json>]
|
|
101
|
+
comments reply <manuscript> --comment-id <CMT-####> [--message <text> | --input <path|->] [--author <name>] [--format <text|json>]
|
|
102
|
+
comments set-status <manuscript> --comment-id <CMT-####> --status <open|resolved> [--thread] [--format <text|json>]
|
|
103
|
+
comments delete <manuscript> --comment-id <CMT-####> [--format <text|json>]
|
|
104
|
+
comments clear-resolved <manuscript> [--format <text|json>]
|
|
105
|
+
comments sync-anchors <manuscript> --input <path|-> [--format <text|json>]
|
|
106
|
+
```
|
|
107
|
+
|
|
73
108
|
## Advanced integration command
|
|
74
109
|
|
|
75
110
|
`stego comments add` is a machine-facing command for editor/tool integrations.
|
package/dist/stego-cli.js
CHANGED
|
@@ -25,6 +25,7 @@ const RESERVED_COMMENT_PREFIX = "CMT";
|
|
|
25
25
|
const DEFAULT_NEW_MANUSCRIPT_SLUG = "new-document";
|
|
26
26
|
const ROOT_CONFIG_FILENAME = "stego.config.json";
|
|
27
27
|
const PROSE_FONT_PROMPT = "Switch workspace to proportional (prose-style) font? (recommended)";
|
|
28
|
+
const COMMENT_AUTHOR_PROMPT = "Default comment author for stego.comments.author?";
|
|
28
29
|
const SCAFFOLD_GITIGNORE_CONTENT = `node_modules/
|
|
29
30
|
/dist/
|
|
30
31
|
.DS_Store
|
|
@@ -92,6 +93,103 @@ stego new-project --project my-book --title "My Book"
|
|
|
92
93
|
stego new --project fiction-example
|
|
93
94
|
\`\`\`
|
|
94
95
|
`;
|
|
96
|
+
const SCAFFOLD_AGENTS_CONTENT = `# AGENTS.md
|
|
97
|
+
|
|
98
|
+
## Purpose
|
|
99
|
+
|
|
100
|
+
This workspace is designed to be AI-friendly for writing workflows.
|
|
101
|
+
|
|
102
|
+
## Canonical CLI Interface
|
|
103
|
+
|
|
104
|
+
- Run \`stego --help\` for the full command reference.
|
|
105
|
+
- Run \`stego --version\` to confirm which CLI is active.
|
|
106
|
+
- Run project docs commands in \`projects/stego-docs\` when available.
|
|
107
|
+
|
|
108
|
+
## CLI Resolution Rules
|
|
109
|
+
|
|
110
|
+
- Prefer local CLI over global CLI:
|
|
111
|
+
- \`npm exec -- stego ...\`
|
|
112
|
+
- \`npx --no-install stego ...\`
|
|
113
|
+
- At the start of mutation tasks, run \`stego --version\` and report the version used.
|
|
114
|
+
|
|
115
|
+
## Workspace Discovery Checklist
|
|
116
|
+
|
|
117
|
+
1. Confirm workspace root contains \`stego.config.json\`.
|
|
118
|
+
2. Run \`stego list-projects\`.
|
|
119
|
+
3. Use explicit \`--project <id>\` for project-scoped commands.
|
|
120
|
+
|
|
121
|
+
## CLI-First Policy (Required)
|
|
122
|
+
|
|
123
|
+
When asked to edit Stego project content, use documented Stego CLI commands first.
|
|
124
|
+
|
|
125
|
+
Typical targets:
|
|
126
|
+
|
|
127
|
+
- manuscript files
|
|
128
|
+
- spine categories and entries
|
|
129
|
+
- frontmatter metadata
|
|
130
|
+
- comments
|
|
131
|
+
- stage/build/export workflows
|
|
132
|
+
|
|
133
|
+
Preferred commands include:
|
|
134
|
+
|
|
135
|
+
- \`stego new\`
|
|
136
|
+
- \`stego spine read\`
|
|
137
|
+
- \`stego spine new-category\`
|
|
138
|
+
- \`stego spine new\`
|
|
139
|
+
- \`stego metadata read\`
|
|
140
|
+
- \`stego metadata apply\`
|
|
141
|
+
- \`stego comments ...\`
|
|
142
|
+
|
|
143
|
+
## Machine-Mode Output
|
|
144
|
+
|
|
145
|
+
- For automation and integrations, prefer \`--format json\` and parse structured output.
|
|
146
|
+
- Use text output only for human-facing summaries.
|
|
147
|
+
|
|
148
|
+
## Mutation Protocol
|
|
149
|
+
|
|
150
|
+
1. Read current state first (\`metadata read\`, \`spine read\`, \`comments read\`).
|
|
151
|
+
2. Mutate via CLI commands.
|
|
152
|
+
3. Verify after writes (\`stego validate --project <id>\` and relevant read commands).
|
|
153
|
+
|
|
154
|
+
## Manual Edit Fallback
|
|
155
|
+
|
|
156
|
+
Manual file edits are a last resort.
|
|
157
|
+
|
|
158
|
+
If manual edits are required, the agent must:
|
|
159
|
+
|
|
160
|
+
1. warn that CLI was bypassed,
|
|
161
|
+
2. explain why CLI could not be used, and
|
|
162
|
+
3. list which files were manually edited.
|
|
163
|
+
|
|
164
|
+
## Failure Contract
|
|
165
|
+
|
|
166
|
+
When CLI fails:
|
|
167
|
+
|
|
168
|
+
1. show the attempted command,
|
|
169
|
+
2. summarize the error briefly,
|
|
170
|
+
3. report the recovery attempt, and
|
|
171
|
+
4. if fallback is required, apply the Manual Edit Fallback policy.
|
|
172
|
+
|
|
173
|
+
## Validation Expectations
|
|
174
|
+
|
|
175
|
+
After mutations, run relevant checks when feasible (for example \`stego validate --project <id>\`) and report results.
|
|
176
|
+
|
|
177
|
+
## Scope Guardrails
|
|
178
|
+
|
|
179
|
+
- Do not manually edit \`dist/\` outputs or compiled export artifacts.
|
|
180
|
+
- Do not modify files outside the requested project scope unless the user explicitly asks.
|
|
181
|
+
|
|
182
|
+
## Task To Command Quick Map
|
|
183
|
+
|
|
184
|
+
- New manuscript: \`stego new --project <id> [--filename <name>]\`
|
|
185
|
+
- Read spine: \`stego spine read --project <id> --format json\`
|
|
186
|
+
- New spine category: \`stego spine new-category --project <id> --key <category>\`
|
|
187
|
+
- New spine entry: \`stego spine new --project <id> --category <category> [--filename <path>]\`
|
|
188
|
+
- Read metadata: \`stego metadata read <markdown-path> --format json\`
|
|
189
|
+
- Apply metadata: \`stego metadata apply <markdown-path> --input <path|-> --format json\`
|
|
190
|
+
- Read comments: \`stego comments read <manuscript> --format json\`
|
|
191
|
+
- Mutate comments: \`stego comments add|reply|set-status|delete|clear-resolved|sync-anchors ... --format json\`
|
|
192
|
+
`;
|
|
95
193
|
const PROSE_MARKDOWN_EDITOR_SETTINGS = {
|
|
96
194
|
"[markdown]": {
|
|
97
195
|
"editor.fontFamily": "Inter, Helvetica Neue, Helvetica, Arial, sans-serif",
|
|
@@ -105,7 +203,8 @@ const PROSE_MARKDOWN_EDITOR_SETTINGS = {
|
|
|
105
203
|
};
|
|
106
204
|
const PROJECT_EXTENSION_RECOMMENDATIONS = [
|
|
107
205
|
"matt-gold.stego-extension",
|
|
108
|
-
"matt-gold.saurus-extension"
|
|
206
|
+
"matt-gold.saurus-extension",
|
|
207
|
+
"streetsidesoftware.code-spell-checker"
|
|
109
208
|
];
|
|
110
209
|
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
111
210
|
const packageRoot = path.resolve(scriptDir, "..");
|
|
@@ -513,6 +612,7 @@ async function initWorkspace(options) {
|
|
|
513
612
|
const copiedPaths = [];
|
|
514
613
|
writeScaffoldGitignore(targetRoot, copiedPaths);
|
|
515
614
|
writeScaffoldReadme(targetRoot, copiedPaths);
|
|
615
|
+
writeScaffoldAgents(targetRoot, copiedPaths);
|
|
516
616
|
copyTemplateAsset(".markdownlint.json", targetRoot, copiedPaths);
|
|
517
617
|
copyTemplateAsset(".markdownlint.manuscript.json", targetRoot, copiedPaths);
|
|
518
618
|
copyTemplateAsset(".cspell.json", targetRoot, copiedPaths);
|
|
@@ -522,8 +622,13 @@ async function initWorkspace(options) {
|
|
|
522
622
|
copyTemplateAsset(path.join(".vscode", "extensions.json"), targetRoot, copiedPaths, { optional: true });
|
|
523
623
|
rewriteTemplateProjectPackageScripts(targetRoot);
|
|
524
624
|
const enableProseFont = await promptYesNo(PROSE_FONT_PROMPT, true);
|
|
525
|
-
|
|
526
|
-
|
|
625
|
+
const suggestedCommentAuthor = resolveSuggestedCommentAuthor(targetRoot);
|
|
626
|
+
const commentAuthor = (await promptText(COMMENT_AUTHOR_PROMPT, suggestedCommentAuthor)).trim();
|
|
627
|
+
if (enableProseFont || commentAuthor) {
|
|
628
|
+
writeProjectProseEditorSettings(targetRoot, copiedPaths, {
|
|
629
|
+
enableProseFont,
|
|
630
|
+
commentAuthor
|
|
631
|
+
});
|
|
527
632
|
}
|
|
528
633
|
writeInitRootPackageJson(targetRoot);
|
|
529
634
|
logLine(`Initialized Stego workspace in ${targetRoot}`);
|
|
@@ -566,6 +671,46 @@ async function promptYesNo(question, defaultYes) {
|
|
|
566
671
|
rl.close();
|
|
567
672
|
}
|
|
568
673
|
}
|
|
674
|
+
async function promptText(question, defaultValue = "") {
|
|
675
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
676
|
+
return defaultValue;
|
|
677
|
+
}
|
|
678
|
+
const rl = createInterface({
|
|
679
|
+
input: process.stdin,
|
|
680
|
+
output: process.stdout
|
|
681
|
+
});
|
|
682
|
+
const suffix = defaultValue ? ` [${defaultValue}] ` : " ";
|
|
683
|
+
try {
|
|
684
|
+
const answer = (await rl.question(`${question}${suffix}`)).trim();
|
|
685
|
+
if (!answer) {
|
|
686
|
+
return defaultValue;
|
|
687
|
+
}
|
|
688
|
+
return answer;
|
|
689
|
+
}
|
|
690
|
+
finally {
|
|
691
|
+
rl.close();
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
function resolveSuggestedCommentAuthor(cwd) {
|
|
695
|
+
const gitAuthor = spawnSync("git", ["config", "--get", "user.name"], {
|
|
696
|
+
cwd,
|
|
697
|
+
encoding: "utf8"
|
|
698
|
+
});
|
|
699
|
+
const fromGit = (gitAuthor.stdout || "").trim();
|
|
700
|
+
if (gitAuthor.status === 0 && fromGit) {
|
|
701
|
+
return fromGit;
|
|
702
|
+
}
|
|
703
|
+
try {
|
|
704
|
+
const username = os.userInfo().username.trim();
|
|
705
|
+
if (username) {
|
|
706
|
+
return username;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
catch {
|
|
710
|
+
// ignore lookup failure and fall back to empty
|
|
711
|
+
}
|
|
712
|
+
return "";
|
|
713
|
+
}
|
|
569
714
|
function copyTemplateAsset(sourceRelativePath, targetRoot, copiedPaths, options) {
|
|
570
715
|
const sourcePath = path.join(packageRoot, sourceRelativePath);
|
|
571
716
|
if (!fs.existsSync(sourcePath)) {
|
|
@@ -600,6 +745,11 @@ function writeScaffoldReadme(targetRoot, copiedPaths) {
|
|
|
600
745
|
fs.writeFileSync(destinationPath, SCAFFOLD_README_CONTENT, "utf8");
|
|
601
746
|
copiedPaths.push("README.md");
|
|
602
747
|
}
|
|
748
|
+
function writeScaffoldAgents(targetRoot, copiedPaths) {
|
|
749
|
+
const destinationPath = path.join(targetRoot, "AGENTS.md");
|
|
750
|
+
fs.writeFileSync(destinationPath, SCAFFOLD_AGENTS_CONTENT, "utf8");
|
|
751
|
+
copiedPaths.push("AGENTS.md");
|
|
752
|
+
}
|
|
603
753
|
function shouldCopyTemplatePath(currentSourcePath) {
|
|
604
754
|
const relativePath = path.relative(packageRoot, currentSourcePath);
|
|
605
755
|
if (!relativePath || relativePath.startsWith("..")) {
|
|
@@ -675,7 +825,7 @@ function ensureProjectExtensionsRecommendations(projectRoot) {
|
|
|
675
825
|
};
|
|
676
826
|
fs.writeFileSync(extensionsPath, `${JSON.stringify(extensionsConfig, null, 2)}\n`, "utf8");
|
|
677
827
|
}
|
|
678
|
-
function writeProjectProseEditorSettings(targetRoot, copiedPaths) {
|
|
828
|
+
function writeProjectProseEditorSettings(targetRoot, copiedPaths, options) {
|
|
679
829
|
const projectsRoot = path.join(targetRoot, "projects");
|
|
680
830
|
if (!fs.existsSync(projectsRoot)) {
|
|
681
831
|
return;
|
|
@@ -685,14 +835,16 @@ function writeProjectProseEditorSettings(targetRoot, copiedPaths) {
|
|
|
685
835
|
continue;
|
|
686
836
|
}
|
|
687
837
|
const projectRoot = path.join(projectsRoot, entry.name);
|
|
688
|
-
const settingsPath = writeProseEditorSettingsForProject(projectRoot);
|
|
838
|
+
const settingsPath = writeProseEditorSettingsForProject(projectRoot, options);
|
|
689
839
|
copiedPaths.push(path.relative(targetRoot, settingsPath));
|
|
690
840
|
}
|
|
691
841
|
}
|
|
692
|
-
function writeProseEditorSettingsForProject(projectRoot) {
|
|
842
|
+
function writeProseEditorSettingsForProject(projectRoot, options) {
|
|
693
843
|
const vscodeDir = path.join(projectRoot, ".vscode");
|
|
694
844
|
const settingsPath = path.join(vscodeDir, "settings.json");
|
|
695
845
|
fs.mkdirSync(vscodeDir, { recursive: true });
|
|
846
|
+
const enableProseFont = options?.enableProseFont ?? true;
|
|
847
|
+
const commentAuthor = (options?.commentAuthor ?? "").trim();
|
|
696
848
|
let existingSettings = {};
|
|
697
849
|
if (fs.existsSync(settingsPath)) {
|
|
698
850
|
try {
|
|
@@ -708,17 +860,22 @@ function writeProseEditorSettingsForProject(projectRoot) {
|
|
|
708
860
|
const proseMarkdownSettings = isPlainObject(PROSE_MARKDOWN_EDITOR_SETTINGS["[markdown]"])
|
|
709
861
|
? PROSE_MARKDOWN_EDITOR_SETTINGS["[markdown]"]
|
|
710
862
|
: {};
|
|
711
|
-
const existingMarkdownSettings = isPlainObject(existingSettings["[markdown]"])
|
|
712
|
-
? existingSettings["[markdown]"]
|
|
713
|
-
: {};
|
|
714
863
|
const nextSettings = {
|
|
715
|
-
...existingSettings
|
|
716
|
-
|
|
864
|
+
...existingSettings
|
|
865
|
+
};
|
|
866
|
+
if (enableProseFont) {
|
|
867
|
+
const existingMarkdownSettings = isPlainObject(existingSettings["[markdown]"])
|
|
868
|
+
? existingSettings["[markdown]"]
|
|
869
|
+
: {};
|
|
870
|
+
nextSettings["[markdown]"] = {
|
|
717
871
|
...existingMarkdownSettings,
|
|
718
872
|
...proseMarkdownSettings
|
|
719
|
-
}
|
|
720
|
-
"markdown.preview.fontFamily"
|
|
721
|
-
}
|
|
873
|
+
};
|
|
874
|
+
nextSettings["markdown.preview.fontFamily"] = PROSE_MARKDOWN_EDITOR_SETTINGS["markdown.preview.fontFamily"];
|
|
875
|
+
}
|
|
876
|
+
if (commentAuthor) {
|
|
877
|
+
nextSettings["stego.comments.author"] = commentAuthor;
|
|
878
|
+
}
|
|
722
879
|
fs.writeFileSync(settingsPath, `${JSON.stringify(nextSettings, null, 2)}\n`, "utf8");
|
|
723
880
|
return settingsPath;
|
|
724
881
|
}
|