scriveno 2.7.2 → 2.8.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.
@@ -57,7 +57,7 @@ When a writer runs `/scr:new-work`, Scriveno creates `.manuscript/config.json`.
57
57
 
58
58
  ```json
59
59
  {
60
- "scriveno_version": "2.7.2",
60
+ "scriveno_version": "2.8.0",
61
61
  "work_type": "<chosen>",
62
62
  "group": "<group>",
63
63
  "command_unit": "<unit>",
@@ -159,7 +159,7 @@ For release-oriented documentation surfaces, the main files are:
159
159
  - `docs/runtime-support.md`
160
160
  - `docs/route-graph.md`
161
161
  - `templates/*/README.md` when shipped profiles or templates change
162
- - `.planning/` milestone summaries when you are still using the GSD planning layer
162
+ - `.planning/` milestone summaries when you are still using an external planning layer
163
163
 
164
164
  ## Before shipping
165
165
 
@@ -2,6 +2,20 @@
2
2
 
3
3
  This document is the public-facing summary of what changed between package releases. For package history, see the root [CHANGELOG](../CHANGELOG.md).
4
4
 
5
+ ## 2.8.0 - 2026-05-30
6
+
7
+ ### What changed
8
+
9
+ - Scriveno can now install focused command profiles: `core`, `writing`, `publishing`, `translation`, `specialist`, or `full`. The new `/scr:surface` command and `scriveno surface` CLI helpers let writers inspect, dry-run, and switch profiles.
10
+ - Added `/scr:proof-unit`, a one-unit proof path that checks voice context, plans, drafts, reviews, context health, and optional export-tool readiness.
11
+ - Added context-health estimation to `scriveno status`, `/scr:next`, and `/scr:health --context`, with watch, tight, and critical thresholds.
12
+ - Added `/scr:export --check` and `/scr:publish --preflight` so tool readiness and publishing prerequisites can be verified before writing deliverables.
13
+ - Updated the public command inventory to 115 commands and added regression tests for profiles, dry-run installs, context health, proof-unit, and preflight surfaces.
14
+
15
+ ### Why it matters
16
+
17
+ Writers can keep Scriveno small when they only need the active workflow, prove the product on one real unit before trusting a larger run, and check export or publishing readiness before generating packages.
18
+
5
19
  ## 2.7.2 - 2026-05-30
6
20
 
7
21
  ### What changed
@@ -76,7 +90,7 @@ This release closes the gap between what Scriveno documented and what it shipped
76
90
  - The public CLI now supports `scriveno first-run --project .`, giving terminal users the same proof path without relying on host-specific slash-command behavior.
77
91
  - First-run guidance is connected to `/scr:help`, `/scr:demo`, Quick Proof, Starter Sets, Runtime Support, Shipped Assets, Command Reference, README launch copy, and architecture docs.
78
92
  - Scriveno now includes committed first-run and runtime-parity proof bundles under `data/proof/`.
79
- - Runtime smoke now validates the 113-command installed surface across Claude Code, Codex, Cursor, Gemini CLI, OpenCode, GitHub Copilot, Windsurf, Antigravity, Manus, Perplexity Desktop, and the generic fallback.
93
+ - Runtime smoke now validates the 115-command installed surface across Claude Code, Codex, Cursor, Gemini CLI, OpenCode, GitHub Copilot, Windsurf, Antigravity, Manus, Perplexity Desktop, and the generic fallback.
80
94
  - README badges, package metadata, constraints metadata, generated config metadata, changelog, release notes, configuration docs, route graph docs, architecture docs, proof docs, and release tests are aligned on `2.5.0`.
81
95
 
82
96
  ### Why it matters
@@ -13,9 +13,9 @@ The text report summarizes command count, graph edges, agent-capable routes, loc
13
13
 
14
14
  ## Current Shape
15
15
 
16
- As of `2.7.2`, the route graph contains:
16
+ As of `2.8.0`, the route graph contains:
17
17
 
18
- - 113 commands
18
+ - 115 commands
19
19
  - intent-order edges from `command_intents`
20
20
  - dependency-chain edges from `dependencies.core_chain`
21
21
  - automation lanes from `getCommandAutomationPolicy()`
@@ -292,5 +292,5 @@ Here is a quick walkthrough for starting a sacred writing project:
292
292
  ## See Also
293
293
 
294
294
  - [Getting Started](getting-started.md) -- Install Scriveno and create your first project
295
- - [Command Reference](command-reference.md) -- Full reference for all 113 commands, including the [Sacred Exclusive](command-reference.md#sacred-exclusive) section
295
+ - [Command Reference](command-reference.md) -- Full reference for all 115 commands, including the [Sacred Exclusive](command-reference.md#sacred-exclusive) section
296
296
  - [README](../README.md) -- Project overview and feature list
package/docs/voice-dna.md CHANGED
@@ -293,5 +293,5 @@ See [docs/drafter-quality.md](drafter-quality.md) for the full system, including
293
293
  - [Proof Artifacts](proof-artifacts.md) -- inspect the Voice DNA before/after bundle first if you want the fastest concrete evidence
294
294
  - [Getting Started](getting-started.md) -- Install Scriveno and write your first draft
295
295
  - [Drafter Quality](drafter-quality.md) -- the three rule layers, the `draft` config block, and model-tier recommendations
296
- - [Command Reference](command-reference.md) -- Full list of all 113 commands with usage and examples
296
+ - [Command Reference](command-reference.md) -- Full list of all 115 commands with usage and examples
297
297
  - [Work Types Guide](work-types.md) -- How work types adapt Scriveno's vocabulary and commands
@@ -335,5 +335,5 @@ Your work type is stored in `.manuscript/config.json` and can be changed later b
335
335
  ## See Also
336
336
 
337
337
  - [Getting Started](getting-started.md) -- Install Scriveno and write your first draft
338
- - [Command Reference](command-reference.md) -- Full list of all 113 commands with usage and examples
338
+ - [Command Reference](command-reference.md) -- Full list of all 115 commands with usage and examples
339
339
  - [Voice DNA Guide](voice-dna.md) -- How Scriveno profiles and preserves your writing voice
@@ -581,6 +581,72 @@ function detectContextSignal(manuscriptDir, draftFiles) {
581
581
  return { state: 'fresh', suggest: null };
582
582
  }
583
583
 
584
+ const CONTEXT_HEALTH_LIMITS = {
585
+ watch: 45000,
586
+ tight: 80000,
587
+ critical: 120000,
588
+ };
589
+
590
+ function estimateTextTokens(byteCount) {
591
+ return Math.ceil(byteCount / 4);
592
+ }
593
+
594
+ function fileByteSize(filePath) {
595
+ const stat = safeStat(filePath);
596
+ return stat && stat.isFile() ? stat.size : 0;
597
+ }
598
+
599
+ function newestFiles(files, limit) {
600
+ return files
601
+ .map((file) => ({ file, mtime: safeStat(file)?.mtimeMs || 0 }))
602
+ .sort((a, b) => b.mtime - a.mtime)
603
+ .slice(0, limit)
604
+ .map((entry) => entry.file);
605
+ }
606
+
607
+ function detectContextHealth(manuscriptDir, draftFiles) {
608
+ const coreFiles = [
609
+ 'STYLE-GUIDE.md',
610
+ 'CONTEXT.md',
611
+ 'STATE.md',
612
+ 'OUTLINE.md',
613
+ 'RECORD.md',
614
+ 'WORK.md',
615
+ 'config.json',
616
+ ].map((name) => path.join(manuscriptDir, name));
617
+ const reviewFiles = newestFiles(listFiles(path.join(manuscriptDir, 'reviews'), { extensions: ['.md', '.txt'], recursive: true }), 3);
618
+ const planFiles = newestFiles(listFiles(path.join(manuscriptDir, 'plans'), { extensions: ['.md'], recursive: true }), 3);
619
+ const recentDrafts = newestFiles(draftFiles, 5);
620
+ const uniqueFiles = [...new Set([...coreFiles, ...planFiles, ...recentDrafts, ...reviewFiles])].filter(pathExists);
621
+ const files = uniqueFiles.map((file) => ({
622
+ file: path.relative(manuscriptDir, file),
623
+ bytes: fileByteSize(file),
624
+ }));
625
+ const estimatedBytes = files.reduce((sum, file) => sum + file.bytes, 0);
626
+ const estimatedTokens = estimateTextTokens(estimatedBytes);
627
+ let state = 'ok';
628
+ let suggest = null;
629
+ if (estimatedTokens >= CONTEXT_HEALTH_LIMITS.critical) {
630
+ state = 'critical';
631
+ suggest = '/scr:thread';
632
+ } else if (estimatedTokens >= CONTEXT_HEALTH_LIMITS.tight) {
633
+ state = 'tight';
634
+ suggest = '/scr:save';
635
+ } else if (estimatedTokens >= CONTEXT_HEALTH_LIMITS.watch) {
636
+ state = 'watch';
637
+ suggest = '/scr:health --context';
638
+ }
639
+ return {
640
+ state,
641
+ estimatedTokens,
642
+ estimatedBytes,
643
+ fileCount: files.length,
644
+ limits: { ...CONTEXT_HEALTH_LIMITS },
645
+ largestFiles: files.sort((a, b) => b.bytes - a.bytes).slice(0, 5),
646
+ suggest,
647
+ };
648
+ }
649
+
584
650
  function detectExportSignal(manuscriptDir, sourceFiles) {
585
651
  const newestSource = newestMtime(sourceFiles);
586
652
  const newestOutput = findNewestOutput(manuscriptDir);
@@ -757,6 +823,12 @@ function buildAutomationPlan(signals, recommendation) {
757
823
  if (signals.context?.state === 'stale') {
758
824
  localCandidates.push({ command: signals.context.suggest || '/scr:scan', reason: 'refresh stale context before chaining work' });
759
825
  }
826
+ if (signals.contextHealth?.state === 'tight' || signals.contextHealth?.state === 'critical') {
827
+ localCandidates.push({
828
+ command: signals.contextHealth.suggest || '/scr:health --context',
829
+ reason: `loaded context estimate is ${signals.contextHealth.state}`,
830
+ });
831
+ }
760
832
  if (signals.notes?.count > 0) {
761
833
  localCandidates.push({ command: '/scr:check-notes', reason: 'surface unresolved notes before the next writing route' });
762
834
  }
@@ -801,6 +873,7 @@ function analyzeProject(projectRoot = process.cwd(), options = {}) {
801
873
  hasProject: false,
802
874
  hasState: false,
803
875
  context: { state: 'none', suggest: null },
876
+ contextHealth: { state: 'none', estimatedTokens: 0, estimatedBytes: 0, fileCount: 0, largestFiles: [], suggest: null },
804
877
  history: { state: 'none', lastFailed: false },
805
878
  reviews: { state: 'none', count: 0, files: [] },
806
879
  reviewCoverage: { state: 'none', suggest: null },
@@ -844,6 +917,7 @@ function analyzeProject(projectRoot = process.cwd(), options = {}) {
844
917
  hasProject: true,
845
918
  hasState: pathExists(statePath),
846
919
  context: detectContextSignal(manuscriptDir, draftFiles),
920
+ contextHealth: detectContextHealth(manuscriptDir, draftFiles),
847
921
  history: historySignal,
848
922
  readiness: detectProjectReadiness(manuscriptDir),
849
923
  plan: detectPlanSignal(manuscriptDir, draftFiles),
@@ -895,6 +969,7 @@ function formatProactiveChecks(analysis) {
895
969
  progressLine,
896
970
  ` Readiness: ${signals.readiness?.state || 'none'}${signals.readiness?.missing?.length ? `, missing ${signals.readiness.missing.join(', ')}` : ''}`,
897
971
  ` Session: ${signals.context.state}${signals.context.suggest ? `, suggest ${signals.context.suggest}` : ''}`,
972
+ ` Context health: ${signals.contextHealth?.state || 'none'}${signals.contextHealth?.estimatedTokens ? `, about ${signals.contextHealth.estimatedTokens} tokens` : ''}${signals.contextHealth?.suggest ? `, suggest ${signals.contextHealth.suggest}` : ''}`,
898
973
  ` Plans: ${signals.plan?.state || 'none'}${signals.plan?.suggest ? `, suggest ${signals.plan.suggest}` : ''}`,
899
974
  ` Reviews: ${signals.reviews.count ? `${signals.reviews.count} pending, suggest /scr:editor-review` : 'none'}`,
900
975
  ` Review coverage: ${signals.reviewCoverage?.state || 'none'}${signals.reviewCoverage?.suggest ? `, suggest ${signals.reviewCoverage.suggest}` : ''}`,
@@ -1574,4 +1649,6 @@ module.exports = {
1574
1649
  listRuntimeAgentSupport,
1575
1650
  parseCliArgs,
1576
1651
  computeProgressLedger,
1652
+ detectContextHealth,
1653
+ estimateTextTokens,
1577
1654
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scriveno",
3
- "version": "2.7.2",
3
+ "version": "2.8.0",
4
4
  "description": "Spec-driven creative writing, publishing, and translation pipeline for AI coding agents. From blank page to published book.",
5
5
  "bin": {
6
6
  "scriveno": "bin/install.js"
@@ -1,5 +1,5 @@
1
1
  {
2
- "scriveno_version": "2.7.2",
2
+ "scriveno_version": "2.8.0",
3
3
  "work_type": "",
4
4
  "group": "",
5
5
  "command_unit": "",