scriveno 2.0.6 → 2.0.8

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 CHANGED
@@ -2,9 +2,10 @@
2
2
 
3
3
  [![CI](https://github.com/aihxp/scriveno/actions/workflows/ci.yml/badge.svg)](https://github.com/aihxp/scriveno/actions/workflows/ci.yml)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
- [![Version](https://img.shields.io/badge/version-2.0.6-blue)](CHANGELOG.md)
5
+ [![Version](https://img.shields.io/badge/version-2.0.8-blue)](CHANGELOG.md)
6
6
  [![npm](https://img.shields.io/npm/v/scriveno.svg)](https://www.npmjs.com/package/scriveno)
7
7
  [![Downloads](https://img.shields.io/npm/dm/scriveno.svg)](https://www.npmjs.com/package/scriveno)
8
+ [![Status CLI](https://img.shields.io/badge/status%20CLI-scriveno%20status-blue)](docs/runtime-support.md#shared-auto-invoke-engine)
8
9
 
9
10
  **[scriveno on npm](https://www.npmjs.com/package/scriveno)**
10
11
 
@@ -18,6 +19,9 @@ Scriveno is best understood as **AI-native longform writing software built aroun
18
19
 
19
20
  ```bash
20
21
  npx scriveno@latest
22
+
23
+ # Optional project status check
24
+ scriveno status --project .
21
25
  ```
22
26
 
23
27
  ---
@@ -68,6 +72,19 @@ If you want the shortest proof-first route, read [Proof Artifacts](docs/proof-ar
68
72
 
69
73
  ---
70
74
 
75
+ ## Proactive status
76
+
77
+ Scriveno ships a shared read-only status engine for every installer target. The public CLI is:
78
+
79
+ ```bash
80
+ scriveno status --project .
81
+ scriveno status . --json
82
+ ```
83
+
84
+ It inspects disk evidence such as `.manuscript/`, `STATE.md`, `CONTEXT.md`, review files, translation work, exports, and history, then recommends the safest next command. The engine does not mutate files and does not spawn agents by itself. Command surfaces such as `/scr-next`, `/scr:next`, `/scr:progress`, `/scr:session-report`, and `/scr:sync` call it when local command execution is available, then fall back to embedded markdown logic when a host cannot run Node. See [Auto-Invoke Policy](docs/auto-invoke-policy.md) and [Runtime Support](docs/runtime-support.md#shared-auto-invoke-engine).
85
+
86
+ ---
87
+
71
88
  ## The Voice DNA system
72
89
 
73
90
  Scriveno's core insight: drafted prose should sound like *you*, not like an AI. Before drafting begins, `/scr:profile-writer` builds a detailed voice profile across 15+ dimensions:
@@ -171,6 +188,7 @@ Scriveno is built on five principles:
171
188
  - [Contributing](docs/contributing.md) -- How to add commands, agents, work types, and templates
172
189
  - [Architecture](docs/architecture.md) -- How Scriveno works under the hood
173
190
  - [Configuration](docs/configuration.md) -- Package, installer, constraints, and `.manuscript/config.json` surfaces
191
+ - [Auto-Invoke Policy](docs/auto-invoke-policy.md) -- Shared status engine, visible automation status, and agent-spawn boundaries
174
192
  - [Development](docs/development.md) -- Contributor workflow for changing commands, templates, installer logic, and docs
175
193
  - [Testing](docs/testing.md) -- What the test suite covers and which checks to run before shipping
176
194
  - [Release Notes](docs/release-notes.md) -- Public summary of what changed between package releases
@@ -195,7 +213,7 @@ Scriveno currently ships installer targets for these AI tooling environments:
195
213
  - **Perplexity Desktop** (guided local-MCP setup)
196
214
  - **Generic (SKILL.md)** fallback
197
215
 
198
- **Installer baseline:** `Node.js >=20.0.0` for `npx scriveno@latest` and `bin/install.js`. For new installs, use a currently supported LTS such as Node.js 24; Node.js 20 is now a compatibility floor, not the recommended fresh-install target.
216
+ **Installer baseline:** `Node.js >=20.0.0` for `npx scriveno@latest`, `bin/install.js`, and `scriveno status --project .`. For new installs, use a currently supported LTS such as Node.js 24; Node.js 20 is now a compatibility floor, not the recommended fresh-install target.
199
217
 
200
218
  **Support note:** Claude Code is the primary reference runtime and now installs a flat `/scr-*` command surface. The environments listed above are installer targets, not a claim that every host runtime has verified parity today. Codex currently installs a skill-native `$scr-*` surface, while Perplexity Desktop is a guided local-MCP target rather than a writable command runtime. See the [runtime compatibility matrix](docs/runtime-support.md) for install type, support level, and verification status.
201
219
 
@@ -203,11 +221,11 @@ Scriveno currently ships installer targets for these AI tooling environments:
203
221
 
204
222
  ## Status
205
223
 
206
- **Version:** 2.0.6
224
+ **Version:** 2.0.8
207
225
 
208
- Scriveno's core command surface is stable across 112 commands, 50 work types, and 11 installer targets. The current repo baseline includes shipped planning milestones through `v2.0 Publishing Cover Packaging`, plus the creative-context, record-store, branching-next, runtime-sync, adaptive concierge, human-first writing-safeguard, authenticity-diagnostic, domain-grilling, and installer-marker cleanup work through `2.0.6`. See [Shipped Assets](docs/shipped-assets.md) for the canonical asset inventory and [Runtime Support](docs/runtime-support.md) for the runtime compatibility matrix.
226
+ Scriveno's core command surface is stable across 112 commands, 50 work types, and 11 installer targets. The current repo baseline includes shipped planning milestones through `v2.0 Publishing Cover Packaging`, plus the creative-context, record-store, branching-next, runtime-sync, adaptive concierge, human-first writing-safeguard, authenticity-diagnostic, domain-grilling, installer-marker cleanup, cross-runtime agent metadata, visible automation status, and the shared `scriveno status --project .` auto-invoke engine through `2.0.8`. See [Shipped Assets](docs/shipped-assets.md) for the canonical asset inventory and [Runtime Support](docs/runtime-support.md) for the runtime compatibility matrix.
209
227
 
210
- Version `2.0.6` publishes Scriveno under the package name `scriveno`, so the current install command is `npx scriveno@latest`. The older `scriveno-cli` package name is historical and was unpublished during the rename, so npm cannot attach a deprecation notice to it while it has no active registry record. The older `scriven-cli` package remains on npm only as a deprecated legacy name that points users to `scriveno`. Do not treat either legacy package name as active unless a deliberate compatibility shim is republished. See [CHANGELOG](CHANGELOG.md) for the full list and [docs/release-notes.md](docs/release-notes.md) for the public-facing summary.
228
+ Version `2.0.8` publishes Scriveno under the package name `scriveno`, so the current install command is `npx scriveno@latest`. The older `scriveno-cli` package name is historical and was unpublished during the rename, so npm cannot attach a deprecation notice to it while it has no active registry record. The older `scriven-cli` package remains on npm only as a deprecated legacy name that points users to `scriveno`. Do not treat either legacy package name as active unless a deliberate compatibility shim is republished. See [CHANGELOG](CHANGELOG.md) for the full list and [docs/release-notes.md](docs/release-notes.md) for the public-facing summary.
211
229
 
212
230
  Package history is tracked in [CHANGELOG.md](CHANGELOG.md), and the public-facing summary for this release is in [docs/release-notes.md](docs/release-notes.md).
213
231
 
package/bin/install.js CHANGED
@@ -6,6 +6,7 @@ const os = require('os');
6
6
  const readline = require('readline');
7
7
  const crypto = require('crypto');
8
8
  const architecturalProfiles = require('../lib/architectural-profiles.js');
9
+ const autoInvokeEngine = require('../lib/auto-invoke-engine.js');
9
10
 
10
11
  const PKG_ROOT = path.join(__dirname, '..');
11
12
  const PKG = require('../package.json');
@@ -155,6 +156,7 @@ const RUNTIMES = {
155
156
  agents_dir_global: path.join(os.homedir(), '.codex', 'agents'),
156
157
  agents_dir_project: '.codex/agents',
157
158
  skill_style: 'per-command',
159
+ agent_metadata: 'toml',
158
160
  detect: () => fs.existsSync(path.join(os.homedir(), '.codex')),
159
161
  },
160
162
  'opencode': {
@@ -414,6 +416,58 @@ function collectCommandEntries(commandsRoot) {
414
416
  return entries;
415
417
  }
416
418
 
419
+ function stripMarkdownFrontmatter(content) {
420
+ if (typeof content !== 'string' || content.length === 0) return '';
421
+ const stripped = content.charCodeAt(0) === 0xFEFF ? content.slice(1) : content;
422
+ const lines = stripped.split(/\r?\n/);
423
+ if (lines[0] !== '---') return stripped;
424
+ for (let i = 1; i < lines.length; i++) {
425
+ if (lines[i] === '---' || lines[i] === '...') {
426
+ return lines.slice(i + 1).join('\n').replace(/^\n/, '');
427
+ }
428
+ }
429
+ return stripped;
430
+ }
431
+
432
+ function collectAgentEntries(agentsRoot = path.join(PKG_ROOT, 'agents')) {
433
+ if (!fs.existsSync(agentsRoot)) return [];
434
+ const entries = [];
435
+ for (const entry of fs.readdirSync(agentsRoot, { withFileTypes: true })) {
436
+ if (!entry.isFile() || !entry.name.endsWith('.md')) continue;
437
+ const relativePath = entry.name;
438
+ const filePath = path.join(agentsRoot, relativePath);
439
+ const content = fs.readFileSync(filePath, 'utf8');
440
+ const stem = entry.name.replace(/\.md$/, '');
441
+ const frontmatter = readFrontmatterValues(content);
442
+ const name = frontmatter.name || stem;
443
+ const description = frontmatter.description || `${stem.replace(/-/g, ' ')} agent`;
444
+ entries.push({
445
+ name,
446
+ description,
447
+ relativePath,
448
+ metadataFileName: `${name}.toml`,
449
+ content,
450
+ body: stripMarkdownFrontmatter(content),
451
+ });
452
+ }
453
+ entries.sort((a, b) => a.name.localeCompare(b.name));
454
+ return entries;
455
+ }
456
+
457
+ function tomlString(value) {
458
+ return JSON.stringify(String(value));
459
+ }
460
+
461
+ function generateCodexAgentMetadata(entry) {
462
+ return [
463
+ `name = ${tomlString(entry.name)}`,
464
+ `description = ${tomlString(entry.description)}`,
465
+ 'sandbox_mode = "workspace-write"',
466
+ `developer_instructions = ${tomlString(entry.body)}`,
467
+ '',
468
+ ].join('\n');
469
+ }
470
+
417
471
  // Both Claude (flat scr-foo.md filename) and Codex (per-command skill dir
418
472
  // scr-foo/SKILL.md) install commands keyed by the same skill-name function:
419
473
  // /scr:foo and /scr:foo:bar both flatten under commandRefToCodexSkillName by
@@ -767,6 +821,8 @@ function printHelp() {
767
821
  console.log(BANNER);
768
822
  console.log(`Usage:
769
823
  scriveno
824
+ scriveno status --project .
825
+ scriveno status . --json
770
826
  scriveno --runtimes codex,claude-code --global --writer --silent
771
827
 
772
828
  Options:
@@ -781,6 +837,12 @@ Options:
781
837
  --help Show this help text
782
838
  --version Show the Scriveno package version
783
839
 
840
+ Status options:
841
+ status Inspect a project and recommend the next command
842
+ --project <path> Project root to inspect (default: current directory)
843
+ --trigger <name> Status trigger label (default: scriveno status)
844
+ --json Print machine-readable status JSON
845
+
784
846
  Runtime keys:
785
847
  ${Object.keys(RUNTIMES).join(', ')}
786
848
  `);
@@ -788,6 +850,7 @@ Runtime keys:
788
850
 
789
851
  function parseArgs(argv) {
790
852
  const options = {
853
+ command: 'install',
791
854
  runtimeKeys: [],
792
855
  installDetected: false,
793
856
  isGlobal: null,
@@ -795,8 +858,44 @@ function parseArgs(argv) {
795
858
  silent: false,
796
859
  showHelp: false,
797
860
  showVersion: false,
861
+ statusProjectRoot: process.cwd(),
862
+ statusTrigger: 'scriveno status',
863
+ statusJson: false,
798
864
  };
799
865
 
866
+ if (argv[0] === 'status') {
867
+ options.command = 'status';
868
+ for (let i = 1; i < argv.length; i++) {
869
+ const arg = argv[i];
870
+ if (arg === '--help' || arg === '-h') {
871
+ options.showHelp = true;
872
+ } else if (arg === '--version' || arg === '-v') {
873
+ options.showVersion = true;
874
+ } else if (arg === '--json') {
875
+ options.statusJson = true;
876
+ } else if (arg === '--project') {
877
+ const value = argv[i + 1];
878
+ if (!value) throw new Error('--project requires a value for status');
879
+ options.statusProjectRoot = value;
880
+ i++;
881
+ } else if (arg.startsWith('--project=')) {
882
+ options.statusProjectRoot = arg.slice('--project='.length);
883
+ } else if (arg === '--trigger') {
884
+ const value = argv[i + 1];
885
+ if (!value) throw new Error('--trigger requires a value');
886
+ options.statusTrigger = value;
887
+ i++;
888
+ } else if (arg.startsWith('--trigger=')) {
889
+ options.statusTrigger = arg.slice('--trigger='.length);
890
+ } else if (arg.startsWith('-')) {
891
+ throw new Error(`Unknown status argument "${arg}"`);
892
+ } else {
893
+ options.statusProjectRoot = arg;
894
+ }
895
+ }
896
+ return options;
897
+ }
898
+
800
899
  function addRuntimeList(value) {
801
900
  for (const key of String(value).split(',').map((item) => item.trim()).filter(Boolean)) {
802
901
  if (!Object.prototype.hasOwnProperty.call(RUNTIMES, key)) {
@@ -848,6 +947,16 @@ function parseArgs(argv) {
848
947
  return options;
849
948
  }
850
949
 
950
+ function runStatus({ projectRoot, trigger, json }) {
951
+ const analysis = autoInvokeEngine.analyzeProject(projectRoot);
952
+ if (json) {
953
+ console.log(JSON.stringify(analysis, null, 2));
954
+ } else {
955
+ console.log(autoInvokeEngine.formatReport(analysis, { trigger }));
956
+ }
957
+ return analysis;
958
+ }
959
+
851
960
  function resolveInstallRequest(parsed, detectedRuntimeKeys, { isTTY }) {
852
961
  const hasRuntimeDirective = parsed.runtimeKeys.length > 0 || parsed.installDetected;
853
962
  const hasModifierOverrides = parsed.isGlobal !== null || parsed.developerMode !== null;
@@ -1202,6 +1311,64 @@ function writeCodexSkillManifest(skillsDir, skillNames) {
1202
1311
  atomicWriteFileSync(manifestPath, JSON.stringify(manifest, null, 2));
1203
1312
  }
1204
1313
 
1314
+ function isScrivenoCodexAgentMetadataFile(filePath) {
1315
+ if (!fs.existsSync(filePath)) return false;
1316
+ const content = fs.readFileSync(filePath, 'utf8');
1317
+ return content.includes('developer_instructions = ') && (
1318
+ content.includes('# Drafter agent')
1319
+ || content.includes('# Voice checker agent')
1320
+ || content.includes('# Continuity checker agent')
1321
+ || content.includes('# Plan checker agent')
1322
+ || content.includes('# Researcher agent')
1323
+ || content.includes('# Translator agent')
1324
+ );
1325
+ }
1326
+
1327
+ function cleanCodexAgentFiles(agentsDir, currentFileNames) {
1328
+ if (!fs.existsSync(agentsDir)) return 0;
1329
+
1330
+ const manifestPath = path.join(agentsDir, '.scriveno-agents-installed.json');
1331
+ const manifest = readJsonIfExists(manifestPath);
1332
+ const currentFileSet = new Set(currentFileNames);
1333
+ const knownFileNames = new Set(Array.isArray(manifest?.files) ? manifest.files : []);
1334
+
1335
+ for (const entry of fs.readdirSync(agentsDir, { withFileTypes: true })) {
1336
+ if (!entry.isFile() || !entry.name.endsWith('.toml')) continue;
1337
+ const filePath = path.join(agentsDir, entry.name);
1338
+ if (isScrivenoCodexAgentMetadataFile(filePath)) {
1339
+ knownFileNames.add(entry.name);
1340
+ }
1341
+ }
1342
+
1343
+ removePathIfExists(manifestPath);
1344
+
1345
+ let removed = 0;
1346
+ for (const fileName of knownFileNames) {
1347
+ if (!currentFileSet.has(fileName) && removePathIfExists(path.join(agentsDir, fileName))) {
1348
+ removed++;
1349
+ }
1350
+ }
1351
+
1352
+ for (const fileName of currentFileNames) {
1353
+ if (removePathIfExists(path.join(agentsDir, fileName))) {
1354
+ removed++;
1355
+ }
1356
+ }
1357
+
1358
+ return removed;
1359
+ }
1360
+
1361
+ function writeCodexAgentManifest(agentsDir, fileNames) {
1362
+ const manifestPath = path.join(agentsDir, '.scriveno-agents-installed.json');
1363
+ const manifest = {
1364
+ installer: 'scriveno',
1365
+ version: VERSION,
1366
+ files: fileNames,
1367
+ generated_at: new Date().toISOString(),
1368
+ };
1369
+ atomicWriteFileSync(manifestPath, JSON.stringify(manifest, null, 2));
1370
+ }
1371
+
1205
1372
  async function main() {
1206
1373
  const parsed = parseArgs(process.argv.slice(2));
1207
1374
  if (parsed.showHelp) {
@@ -1213,6 +1380,15 @@ async function main() {
1213
1380
  return;
1214
1381
  }
1215
1382
 
1383
+ if (parsed.command === 'status') {
1384
+ runStatus({
1385
+ projectRoot: parsed.statusProjectRoot,
1386
+ trigger: parsed.statusTrigger,
1387
+ json: parsed.statusJson,
1388
+ });
1389
+ return;
1390
+ }
1391
+
1216
1392
  const detectedRuntimeKeys = Object.entries(RUNTIMES).filter(([, runtime]) => runtime.detect()).map(([key]) => key);
1217
1393
  const installRequest = resolveInstallRequest(parsed, detectedRuntimeKeys, { isTTY: Boolean(process.stdin.isTTY) });
1218
1394
 
@@ -1342,6 +1518,26 @@ function installManifestSkillRuntime(runtime, isGlobal, log) {
1342
1518
  log(` ${c('green', 'OK')} ${runtime.label}: ${agentCount} agent prompts -> ${c('dim', path.join(skillsDir, 'agents'))}`);
1343
1519
  }
1344
1520
 
1521
+ function installCodexAgentsWithMetadata(agentsDir) {
1522
+ const agentEntries = collectAgentEntries(path.join(PKG_ROOT, 'agents'));
1523
+ const currentFileNames = agentEntries.flatMap((entry) => [entry.relativePath, entry.metadataFileName]);
1524
+
1525
+ fs.mkdirSync(agentsDir, { recursive: true });
1526
+ const removed = cleanCodexAgentFiles(agentsDir, currentFileNames);
1527
+
1528
+ for (const entry of agentEntries) {
1529
+ atomicWriteFileSync(path.join(agentsDir, entry.relativePath), entry.content);
1530
+ atomicWriteFileSync(path.join(agentsDir, entry.metadataFileName), generateCodexAgentMetadata(entry));
1531
+ }
1532
+ writeCodexAgentManifest(agentsDir, currentFileNames);
1533
+
1534
+ return {
1535
+ agentCount: agentEntries.length,
1536
+ metadataCount: agentEntries.length,
1537
+ removed,
1538
+ };
1539
+ }
1540
+
1345
1541
  function installCodexRuntime(runtime, isGlobal, log) {
1346
1542
  const skillsDir = isGlobal ? runtime.skills_dir_global : path.resolve(runtime.skills_dir_project);
1347
1543
  const commandsDir = isGlobal ? runtime.commands_dir_global : path.resolve(runtime.commands_dir_project);
@@ -1355,7 +1551,6 @@ function installCodexRuntime(runtime, isGlobal, log) {
1355
1551
  removePathIfExists(commandsDir);
1356
1552
  fs.mkdirSync(skillsDir, { recursive: true });
1357
1553
  const removedSkillDirs = cleanCodexSkillDirs(skillsDir, skillNames);
1358
- const removedAgentFiles = cleanMirroredFiles(path.join(PKG_ROOT, 'agents'), agentsDir);
1359
1554
 
1360
1555
  // NOTE: `collectCommandEntries` returns .md files only, and the authoritative
1361
1556
  // `commands/scr/**` tree is .md-only today. No non-.md assets need mirroring.
@@ -1373,7 +1568,7 @@ function installCodexRuntime(runtime, isGlobal, log) {
1373
1568
  commandCount++;
1374
1569
  }
1375
1570
 
1376
- const agentCount = copyDir(path.join(PKG_ROOT, 'agents'), agentsDir);
1571
+ const agentInstall = installCodexAgentsWithMetadata(agentsDir);
1377
1572
 
1378
1573
  for (const entry of commandEntries) {
1379
1574
  const skillDir = path.join(skillsDir, entry.skillName);
@@ -1385,7 +1580,7 @@ function installCodexRuntime(runtime, isGlobal, log) {
1385
1580
 
1386
1581
  log(` ${c('green', 'OK')} ${runtime.label}: ${commandEntries.length} \$scr-* skills -> ${c('dim', skillsDir)}${removedSkillDirs ? c('dim', ` (cleaned ${removedSkillDirs} stale dirs)`) : ''}`);
1387
1582
  log(` ${c('green', 'OK')} ${runtime.label}: ${commandCount} command files -> ${c('dim', commandsDir)}`);
1388
- log(` ${c('green', 'OK')} ${runtime.label}: ${agentCount} agent prompts -> ${c('dim', agentsDir)}${removedAgentFiles ? c('dim', ` (cleaned ${removedAgentFiles} stale files)`) : ''}`);
1583
+ log(` ${c('green', 'OK')} ${runtime.label}: ${agentInstall.agentCount} agent prompts + ${agentInstall.metadataCount} metadata files -> ${c('dim', agentsDir)}${agentInstall.removed ? c('dim', ` (cleaned ${agentInstall.removed} stale files)`) : ''}`);
1389
1584
  }
1390
1585
 
1391
1586
  function installGuidedRuntime(runtime, isGlobal, dataDir, log) {
@@ -1415,11 +1610,13 @@ function installGuidedRuntime(runtime, isGlobal, dataDir, log) {
1415
1610
  function writeSharedAssets(dataDir, runtimeKeys, isGlobal, developerMode, installMode, log) {
1416
1611
  fs.mkdirSync(path.join(dataDir, 'templates'), { recursive: true });
1417
1612
  fs.mkdirSync(path.join(dataDir, 'data'), { recursive: true });
1613
+ fs.mkdirSync(path.join(dataDir, 'lib'), { recursive: true });
1418
1614
  const templateResult = copyDirWithPreservation(path.join(PKG_ROOT, 'templates'), path.join(dataDir, 'templates'));
1419
1615
  const dataResult = copyDirWithPreservation(path.join(PKG_ROOT, 'data'), path.join(dataDir, 'data'));
1616
+ const libResult = copyDirWithPreservation(path.join(PKG_ROOT, 'lib'), path.join(dataDir, 'lib'));
1420
1617
  const sum = (r) => r.fresh + r.replaced + r.backedUp;
1421
- log(` ${c('green', 'OK')} ${sum(templateResult)} templates + ${sum(dataResult)} data files -> ${c('dim', dataDir)}`);
1422
- const totalBackedUp = templateResult.backedUp + dataResult.backedUp;
1618
+ log(` ${c('green', 'OK')} ${sum(templateResult)} templates + ${sum(dataResult)} data files + ${sum(libResult)} lib files -> ${c('dim', dataDir)}`);
1619
+ const totalBackedUp = templateResult.backedUp + dataResult.backedUp + libResult.backedUp;
1423
1620
  if (totalBackedUp > 0) {
1424
1621
  log(` ${c('yellow', 'i')} Preserved ${totalBackedUp} user-modified file(s) as .backup.<timestamp>`);
1425
1622
  }
@@ -1576,17 +1773,25 @@ module.exports = {
1576
1773
  RUNTIMES,
1577
1774
  parseArgs,
1578
1775
  resolveInstallRequest,
1776
+ runStatus,
1579
1777
  collectCommandEntries,
1778
+ collectAgentEntries,
1580
1779
  assertNoSkillNameCollisions,
1581
1780
  cleanCodexSkillDirs,
1781
+ cleanCodexAgentFiles,
1582
1782
  commandRefToCodexSkillName,
1583
1783
  commandRefToClaudeInvocation,
1584
1784
  commandRefToCodexInvocation,
1585
1785
  commandEntryToFlatCommandFileName,
1586
1786
  generateClaudeCommandContent,
1587
1787
  generateCodexCommandContent,
1788
+ generateCodexAgentMetadata,
1588
1789
  rewriteInstalledCommandRefs,
1790
+ installCommandRuntime,
1791
+ installClaudeCommandRuntime,
1792
+ installManifestSkillRuntime,
1589
1793
  installCodexRuntime,
1794
+ installCodexAgentsWithMetadata,
1590
1795
  cleanFlatCommandFiles,
1591
1796
  generateCodexSkill,
1592
1797
  generateSkillManifest,
@@ -1617,4 +1822,10 @@ module.exports = {
1617
1822
  // Per-work-type pitfall packs
1618
1823
  listPitfallPacks: architecturalProfiles.listPitfallPacks,
1619
1824
  getPitfallPackPath: architecturalProfiles.getPitfallPackPath,
1825
+ // Shared proactive status engine
1826
+ autoInvokeEngine,
1827
+ analyzeProject: autoInvokeEngine.analyzeProject,
1828
+ formatAutoInvokeReport: autoInvokeEngine.formatReport,
1829
+ getRuntimeAgentSupport: autoInvokeEngine.getRuntimeAgentSupport,
1830
+ listRuntimeAgentSupport: autoInvokeEngine.listRuntimeAgentSupport,
1620
1831
  };
@@ -182,6 +182,34 @@ If any steps failed, show them in the "Errors" section with actionable fix instr
182
182
 
183
183
  ---
184
184
 
185
+ ## Automation Status
186
+
187
+ Every progress update and final response must include a compact status trail. This is how the writer can tell whether Scriveno auto-chained commands, spawned agents, or only updated local files.
188
+
189
+ ```text
190
+ Automation status:
191
+ Trigger: /scr:autopilot-publish --preset {preset}
192
+ Auto-invoked commands:
193
+ - /scr:voice-check: yes/no
194
+ - /scr:continuity-check: yes/no
195
+ - /scr:front-matter: yes/no
196
+ - /scr:back-matter: yes/no
197
+ - /scr:cover-art: yes/no
198
+ - /scr:export: {count} run(s)
199
+ Spawned agents:
200
+ - voice-checker: {count}
201
+ - continuity-checker: {count}
202
+ Local operations:
203
+ - prerequisite scan: yes/no
204
+ - quality report files written: yes/no
205
+ - export package files written: {count}
206
+ Quality gate:
207
+ - status: warn-only
208
+ - reason: autopilot-publish reports quality findings but continues to export
209
+ ```
210
+
211
+ If a quality command cannot spawn its native agent type, use the installed agent prompt in a fresh context and say `prompt-run fallback used` in the status block.
212
+
185
213
  ## Response Contract
186
214
 
187
215
  Every writer-facing response must end with one to four next-command suggestions. Each suggestion must include a short explanation of what that path will do.
@@ -213,6 +213,35 @@ Next Steps:
213
213
  - **NEVER** continue past a blocking error without logging it -- all errors must appear in the completion summary
214
214
  - **NEVER** modify the source manuscript -- translation works on copies in `.manuscript/translation/{lang}/`
215
215
 
216
+ ## Automation Status
217
+
218
+ Every progress update and final response must include a compact status trail. This is how the writer can tell whether Scriveno auto-chained commands, spawned agents, or only updated local files.
219
+
220
+ ```text
221
+ Automation status:
222
+ Trigger: /scr:autopilot-translate {languages}
223
+ Languages: {count}
224
+ Auto-invoked commands per language:
225
+ - /scr:translation-glossary: yes/no
226
+ - /scr:translate: yes/no
227
+ - /scr:translation-memory --build: yes/no
228
+ - /scr:cultural-adaptation: yes/no
229
+ - /scr:back-translate: yes/no
230
+ - /scr:multi-publish: yes/no
231
+ Spawned agents:
232
+ - translator: {count} fresh-context invocation(s)
233
+ Local operations:
234
+ - glossary files written: {count}
235
+ - translation memory files updated: {count}
236
+ - adaptation reports written: {count}
237
+ - export files written: {count}
238
+ Pause:
239
+ - status: none/blocking-error/writer-requested
240
+ - reason: {one sentence}
241
+ ```
242
+
243
+ If the translator native agent type is unavailable, use the installed `agents/translator.md` prompt in a fresh context per unit and say `prompt-run fallback used` in the status block.
244
+
216
245
  ## Response Contract
217
246
 
218
247
  Every writer-facing response must end with one to four next-command suggestions. Each suggestion must include a short explanation of what that path will do.
@@ -147,6 +147,35 @@ On pause or stop:
147
147
  2. Include what was just completed and what the next action would be
148
148
  3. Record the writer's notes if they provide any
149
149
 
150
+ ## Automation Status
151
+
152
+ Every progress update and final response must include a compact status trail. This is how the writer can tell whether Scriveno auto-chained commands, spawned agents, or only updated local files.
153
+
154
+ ```text
155
+ Automation status:
156
+ Trigger: /scr:autopilot --profile {profile}
157
+ Profile: guided/supervised/full-auto
158
+ Auto-invoked commands:
159
+ - /scr:discuss N: yes/no
160
+ - /scr:plan N: yes/no
161
+ - /scr:draft N: yes/no
162
+ - /scr:editor-review N: yes/no
163
+ - /scr:submit N: yes/no
164
+ Spawned agents:
165
+ - plan-checker: {count}
166
+ - drafter: {count}
167
+ - voice-checker: {count}
168
+ - continuity-checker: {count}
169
+ Local operations:
170
+ - STATE.md updated: yes/no
171
+ - HISTORY.log updated: yes/no
172
+ Pause:
173
+ - status: none/guided/supervised/quality-gate/blocker
174
+ - reason: {one sentence}
175
+ ```
176
+
177
+ If a command in the chain runs local file operations only, say so under `Local operations` rather than listing it as a spawned agent. If a native agent type is unavailable and an installed prompt-run fallback is used, include that in `Spawned agents`.
178
+
150
179
  ## Response Contract
151
180
 
152
181
  Every writer-facing response must end with one to four next-command suggestions. Each suggestion must include a short explanation of what that path will do.
@@ -67,12 +67,34 @@ Load `.manuscript/config.json` to get `work_type`. Load Scriveno's installed/sha
67
67
  </task>
68
68
  </beta_reader_agent>
69
69
 
70
+ If the host runtime cannot spawn a beta reader worker, run the beta reader persona in an isolated fresh context. Report that fallback in the status block.
71
+
70
72
  ### OUTPUT
71
73
 
72
74
  Save to `.manuscript/{act_num}-BETA-READER-NOTES.md`
73
75
 
74
76
  Present findings conversationally to the writer -- this should feel like getting feedback from a trusted reader, not a technical report.
75
77
 
78
+ ## Agent Status
79
+
80
+ Every response must include a short status block that makes invocation visible:
81
+
82
+ ```text
83
+ Agent status:
84
+ Trigger: /scr:beta-reader {scope}
85
+ Spawned agents:
86
+ - beta-reader: 1 fresh-context reader invocation
87
+ Local operations:
88
+ - drafted files checked: {count}
89
+ - focus area applied: yes/no
90
+ - report written: yes/no
91
+ Auto-invoked:
92
+ - none
93
+ Why: beta-reader is experiential feedback only; revision remains a writer decision
94
+ ```
95
+
96
+ If native worker spawning is unavailable, say `prompt-run fallback used` in the status block.
97
+
76
98
  ## Response Contract
77
99
 
78
100
  Every writer-facing response must end with one to four next-command suggestions. Each suggestion must include a short explanation of what that path will do.
@@ -27,6 +27,8 @@ Invoke the installed `continuity-checker.md` agent for the writer's active Scriv
27
27
  - DOCTRINES.md, LINEAGES.md, and CHRONOLOGY.md when present (sacred only)
28
28
  - The previous continuity report if one exists, so the agent can verify resolved issues stayed resolved instead of re-flagging them
29
29
 
30
+ If the host runtime cannot spawn a native `continuity-checker` agent type, load the installed `agents/continuity-checker.md` prompt from the active runtime and run it in an isolated fresh context. Record that fallback in the status block.
31
+
30
32
  The agent reads all drafted scenes and checks:
31
33
 
32
34
  If RECORD.md contradicts the drafted text, flag the mismatch as a RECORD drift finding. If the drafted text reveals established facts or open threads that are missing from RECORD.md, list compact suggested updates under a "Record updates" section without rewriting the file unless the writer asked for fixes.
@@ -105,6 +107,25 @@ For each issue:
105
107
 
106
108
  Save to `.manuscript/{act_num}-CONTINUITY-REPORT.md` or `.manuscript/FULL-CONTINUITY-REPORT.md`. For technical work types, use `CONSISTENCY-REPORT` in the writer-facing title even if the file path stays the same.
107
109
 
110
+ ## Agent Status
111
+
112
+ Every response must include a short status block that makes invocation visible:
113
+
114
+ ```text
115
+ Agent status:
116
+ Trigger: /scr:continuity-check {scope}
117
+ Spawned agents:
118
+ - continuity-checker: 1 fresh-context diagnostic invocation
119
+ Local operations:
120
+ - drafted files checked: {count}
121
+ - RECORD.md checked: yes/no
122
+ - prior report checked: yes/no
123
+ - report written: yes/no
124
+ Auto-invoked:
125
+ - none
126
+ Why: continuity-check is diagnostic only; fixes are writer-chosen handoffs
127
+ ```
128
+
108
129
  ## Response Contract
109
130
 
110
131
  Every writer-facing response must end with one to four next-command suggestions. Each suggestion must include a short explanation of what that path will do.
@@ -53,6 +53,27 @@ Require `.manuscript/plans/{N}-*-PLAN.md` files to exist. If none exist, also ch
53
53
 
54
54
  8. **Tell the writer:** "Drafted {unit} {N}: X words across Y {atomic_units}. Voice consistency: Z%. Updated RECORD.md with what the draft established. Ready for editor review? Run `/scr:editor-review N` or `/scr:next`."
55
55
 
56
+ ## Agent and Automation Status
57
+
58
+ Every response must include a short status block that makes invocation visible:
59
+
60
+ ```text
61
+ Agent status:
62
+ Trigger: /scr:draft N
63
+ Spawned agents:
64
+ - drafter: {count} fresh-context invocation(s)
65
+ - voice-checker: 1 diagnostic pass, or none if no draft was produced
66
+ Local operations:
67
+ - draft files written: {count}
68
+ - RECORD.md updated: yes/no
69
+ - STATE.md updated: yes/no
70
+ Auto-invoked:
71
+ - /scr:editor-review N: yes/no
72
+ Why: {autopilot.enabled true, full-auto profile, supervised pause, or writer-facing manual mode}
73
+ ```
74
+
75
+ If the host runtime cannot spawn a native `drafter` or `voice-checker` agent type, load the installed agent prompt from the active runtime's `agents/` directory and run it in an isolated fresh context. In the status block, write `Spawned agents: native unavailable; prompt-run fallback used` so the writer can see what happened.
76
+
56
77
  ## History log
57
78
 
58
79
  After all atomic units in this invocation are drafted, append one line to `.manuscript/HISTORY.log` per `docs/history-protocol.md`:
@@ -101,6 +101,8 @@ For any issues flagged, spawn a diagnostic agent:
101
101
  </task>
102
102
  </diagnostic_agent>
103
103
 
104
+ If the host runtime cannot spawn a dedicated diagnostic worker, run the revision diagnosis in an isolated fresh context for each flagged issue group. Report that fallback in the status block.
105
+
104
106
  ---
105
107
 
106
108
  ### STEP 4: GENERATE EDITOR NOTES
@@ -408,6 +410,30 @@ Together these form a complete accountability trail. Neither party's work is los
408
410
 
409
411
  ---
410
412
 
413
+ ## Agent and Automation Status
414
+
415
+ Every response must include a short status block that makes invocation visible:
416
+
417
+ ```text
418
+ Agent status:
419
+ Trigger: /scr:editor-review {mode}
420
+ Spawned agents:
421
+ - revision-diagnostic: {count} fresh-context invocation(s)
422
+ Local operations:
423
+ - reviewable units checked: {count}
424
+ - review report written: yes/no
425
+ - proposal decisions written: yes/no
426
+ - editor notes written: yes/no
427
+ - writer responses written: yes/no
428
+ Auto-invoked:
429
+ - none
430
+ Why: editor-review surfaces decisions and recommended handoffs; it does not revise prose without writer choice
431
+ ```
432
+
433
+ If there were no flagged issues in standard review mode, report `revision-diagnostic: none`. In collaboration modes, report `revision-diagnostic: none` unless diagnosis was explicitly requested.
434
+
435
+ ---
436
+
411
437
  ## Writer-Friendly Language Guide
412
438
 
413
439
  This command uses writer-friendly terminology throughout: