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.
- package/README.md +6 -6
- package/bin/install.js +522 -43
- package/commands/scr/export.md +43 -2
- package/commands/scr/health.md +35 -1
- package/commands/scr/help.md +1 -1
- package/commands/scr/new-work.md +1 -1
- package/commands/scr/proof-unit.md +138 -0
- package/commands/scr/publish.md +45 -2
- package/commands/scr/surface.md +149 -0
- package/data/CONSTRAINTS.json +18 -1
- package/data/proof/first-run/README.md +1 -1
- package/docs/architecture.md +1 -1
- package/docs/command-reference.md +51 -4
- package/docs/configuration.md +1 -1
- package/docs/development.md +1 -1
- package/docs/release-notes.md +15 -1
- package/docs/route-graph.md +2 -2
- package/docs/sacred-texts.md +1 -1
- package/docs/voice-dna.md +1 -1
- package/docs/work-types.md +1 -1
- package/lib/auto-invoke-engine.js +77 -0
- package/package.json +1 -1
- package/templates/config.json +1 -1
package/bin/install.js
CHANGED
|
@@ -218,9 +218,167 @@ const RUNTIMES = {
|
|
|
218
218
|
},
|
|
219
219
|
};
|
|
220
220
|
|
|
221
|
-
|
|
221
|
+
const SURFACE_PROFILES = {
|
|
222
|
+
core: {
|
|
223
|
+
label: 'Core',
|
|
224
|
+
description: 'Main writing loop and orientation commands.',
|
|
225
|
+
commands: [
|
|
226
|
+
'new-work',
|
|
227
|
+
'profile-writer',
|
|
228
|
+
'voice-test',
|
|
229
|
+
'discuss',
|
|
230
|
+
'plan',
|
|
231
|
+
'draft',
|
|
232
|
+
'editor-review',
|
|
233
|
+
'submit',
|
|
234
|
+
'progress',
|
|
235
|
+
'save',
|
|
236
|
+
'next',
|
|
237
|
+
'health',
|
|
238
|
+
'help',
|
|
239
|
+
'surface',
|
|
240
|
+
'proof-unit',
|
|
241
|
+
],
|
|
242
|
+
},
|
|
243
|
+
writing: {
|
|
244
|
+
label: 'Writing',
|
|
245
|
+
description: 'Core workflow plus revision, structure, character, and quality commands.',
|
|
246
|
+
includeProfiles: ['core'],
|
|
247
|
+
categories: ['structure', 'structure_management', 'character_world', 'quality', 'review', 'session', 'navigation'],
|
|
248
|
+
},
|
|
249
|
+
publishing: {
|
|
250
|
+
label: 'Publishing',
|
|
251
|
+
description: 'Core workflow plus export, publishing, metadata, and platform packaging commands.',
|
|
252
|
+
includeProfiles: ['core'],
|
|
253
|
+
categories: ['publishing'],
|
|
254
|
+
},
|
|
255
|
+
translation: {
|
|
256
|
+
label: 'Translation',
|
|
257
|
+
description: 'Core workflow plus translation, localization, glossary, and multi-publish commands.',
|
|
258
|
+
includeProfiles: ['core'],
|
|
259
|
+
categories: ['translation'],
|
|
260
|
+
},
|
|
261
|
+
specialist: {
|
|
262
|
+
label: 'Specialist',
|
|
263
|
+
description: 'Core workflow plus sacred, illustration, collaboration, and utility surfaces.',
|
|
264
|
+
includeProfiles: ['core'],
|
|
265
|
+
categories: ['sacred_exclusive', 'illustration', 'collaboration', 'utility'],
|
|
266
|
+
},
|
|
267
|
+
full: {
|
|
268
|
+
label: 'Full',
|
|
269
|
+
description: 'Every Scriveno command.',
|
|
270
|
+
all: true,
|
|
271
|
+
},
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const DEFAULT_SURFACE_PROFILE = 'full';
|
|
275
|
+
|
|
276
|
+
function normalizeSurfaceProfile(profile) {
|
|
277
|
+
const value = String(profile || DEFAULT_SURFACE_PROFILE).trim().toLowerCase();
|
|
278
|
+
if (!Object.prototype.hasOwnProperty.call(SURFACE_PROFILES, value)) {
|
|
279
|
+
throw new Error(`Unknown profile "${profile}". Expected one of: ${Object.keys(SURFACE_PROFILES).join(', ')}`);
|
|
280
|
+
}
|
|
281
|
+
return value;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function resolveProfileCommandKeys(profile, constraints = loadConstraintsForInstall()) {
|
|
285
|
+
const resolvedProfile = normalizeSurfaceProfile(profile);
|
|
286
|
+
const commands = constraints.commands || {};
|
|
287
|
+
const out = new Set();
|
|
288
|
+
const visiting = new Set();
|
|
289
|
+
|
|
290
|
+
function addProfile(name) {
|
|
291
|
+
const key = normalizeSurfaceProfile(name);
|
|
292
|
+
if (visiting.has(key)) {
|
|
293
|
+
throw new Error(`Profile cycle detected at "${key}"`);
|
|
294
|
+
}
|
|
295
|
+
visiting.add(key);
|
|
296
|
+
const spec = SURFACE_PROFILES[key];
|
|
297
|
+
if (spec.all) {
|
|
298
|
+
for (const commandKey of Object.keys(commands)) out.add(commandKey);
|
|
299
|
+
visiting.delete(key);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
for (const parent of spec.includeProfiles || []) addProfile(parent);
|
|
303
|
+
for (const commandKey of spec.commands || []) {
|
|
304
|
+
if (Object.prototype.hasOwnProperty.call(commands, commandKey)) out.add(commandKey);
|
|
305
|
+
}
|
|
306
|
+
const categorySet = new Set(spec.categories || []);
|
|
307
|
+
if (categorySet.size > 0) {
|
|
308
|
+
for (const [commandKey, command] of Object.entries(commands)) {
|
|
309
|
+
if (categorySet.has(command.category)) out.add(commandKey);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
visiting.delete(key);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
addProfile(resolvedProfile);
|
|
316
|
+
return out;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function loadConstraintsForInstall(constraintsPath = path.join(PKG_ROOT, 'data', 'CONSTRAINTS.json')) {
|
|
320
|
+
return JSON.parse(fs.readFileSync(constraintsPath, 'utf8'));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function commandEntryInProfile(entry, commandKeys) {
|
|
324
|
+
return commandKeys.has(commandRefToConstraintKey(entry.commandRef));
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function collectCommandEntriesForProfile(commandsRoot, profile = DEFAULT_SURFACE_PROFILE, constraintsPath = path.join(PKG_ROOT, 'data', 'CONSTRAINTS.json')) {
|
|
328
|
+
const entries = collectCommandEntries(commandsRoot);
|
|
329
|
+
const commandKeys = resolveProfileCommandKeys(profile, loadConstraintsForInstall(constraintsPath));
|
|
330
|
+
return entries.filter((entry) => commandEntryInProfile(entry, commandKeys));
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function collectInstallCommandEntries(profile = DEFAULT_SURFACE_PROFILE) {
|
|
334
|
+
return collectCommandEntriesForProfile(
|
|
335
|
+
path.join(PKG_ROOT, 'commands', 'scr'),
|
|
336
|
+
profile,
|
|
337
|
+
path.join(PKG_ROOT, 'data', 'CONSTRAINTS.json')
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function writeProfileCommandFiles(commandsRoot, commandsDir, commandEntries, transform = null) {
|
|
342
|
+
let count = 0;
|
|
343
|
+
for (const entry of commandEntries) {
|
|
344
|
+
const sourcePath = path.join(commandsRoot, entry.relativePath);
|
|
345
|
+
const sourceContent = fs.readFileSync(sourcePath, 'utf8');
|
|
346
|
+
const targetPath = path.join(commandsDir, entry.relativePath);
|
|
347
|
+
atomicWriteFileSync(targetPath, transform ? transform(entry, sourceContent) : sourceContent);
|
|
348
|
+
count++;
|
|
349
|
+
}
|
|
350
|
+
return count;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function writeSurfaceProfileMarker(targetDir, profile) {
|
|
354
|
+
if (!targetDir) return;
|
|
355
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
356
|
+
atomicWriteFileSync(path.join(targetDir, '.scriveno-profile'), `${normalizeSurfaceProfile(profile)}\n`);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function surfaceProfileSummary(profile = DEFAULT_SURFACE_PROFILE, constraintsPath = path.join(PKG_ROOT, 'data', 'CONSTRAINTS.json')) {
|
|
360
|
+
const resolvedProfile = normalizeSurfaceProfile(profile);
|
|
361
|
+
const constraints = loadConstraintsForInstall(constraintsPath);
|
|
362
|
+
const commandKeys = resolveProfileCommandKeys(resolvedProfile, constraints);
|
|
363
|
+
return {
|
|
364
|
+
profile: resolvedProfile,
|
|
365
|
+
label: SURFACE_PROFILES[resolvedProfile].label,
|
|
366
|
+
description: SURFACE_PROFILES[resolvedProfile].description,
|
|
367
|
+
commandCount: commandKeys.size,
|
|
368
|
+
commands: [...commandKeys].sort(),
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function listSurfaceProfiles(constraintsPath = path.join(PKG_ROOT, 'data', 'CONSTRAINTS.json')) {
|
|
373
|
+
return Object.keys(SURFACE_PROFILES).map((profile) => surfaceProfileSummary(profile, constraintsPath));
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function generateSkillManifest(constraintsPath, profile = DEFAULT_SURFACE_PROFILE) {
|
|
222
377
|
const commandsRoot = path.join(PKG_ROOT, 'commands', 'scr');
|
|
223
|
-
const
|
|
378
|
+
const profileKeys = resolveProfileCommandKeys(profile, loadConstraintsForInstall(constraintsPath));
|
|
379
|
+
const entries = collectCanonicalCommandInventory(commandsRoot, constraintsPath)
|
|
380
|
+
.filter((entry) => commandEntryInProfile(entry, profileKeys))
|
|
381
|
+
.map((entry) => ({
|
|
224
382
|
name: entry.commandRef,
|
|
225
383
|
category: entry.category,
|
|
226
384
|
description: entry.description,
|
|
@@ -239,6 +397,7 @@ function generateSkillManifest(constraintsPath) {
|
|
|
239
397
|
return `# Scriveno -- AI Creative Writing Skills
|
|
240
398
|
|
|
241
399
|
Version: ${VERSION}
|
|
400
|
+
Profile: ${normalizeSurfaceProfile(profile)}
|
|
242
401
|
|
|
243
402
|
Scriveno is a spec-driven creative writing, publishing, and translation pipeline.
|
|
244
403
|
|
|
@@ -829,12 +988,18 @@ function printHelp() {
|
|
|
829
988
|
scriveno smoke --json
|
|
830
989
|
scriveno agents --json
|
|
831
990
|
scriveno routes --json
|
|
991
|
+
scriveno surface list
|
|
992
|
+
scriveno surface status
|
|
993
|
+
scriveno surface profile core --runtimes codex --project
|
|
832
994
|
scriveno --runtimes codex,claude-code --global --writer --silent
|
|
833
995
|
|
|
834
996
|
Options:
|
|
835
997
|
--runtimes <list> Comma-separated runtime keys to install (for example: codex,claude-code)
|
|
836
998
|
--runtime <key> Repeatable single-runtime selector
|
|
837
999
|
--detected Install to every detected runtime
|
|
1000
|
+
--profile <name> Command profile: ${Object.keys(SURFACE_PROFILES).join(', ')}
|
|
1001
|
+
--dry-run Show planned writes without changing files
|
|
1002
|
+
--json Print machine-readable output for supported commands
|
|
838
1003
|
--global Install for all projects (default)
|
|
839
1004
|
--project Install only in the current directory
|
|
840
1005
|
--writer Use writer mode (default)
|
|
@@ -856,6 +1021,7 @@ Audit commands:
|
|
|
856
1021
|
smoke Smoke-test installed runtime surfaces
|
|
857
1022
|
agents Inspect installed agent prompts and metadata
|
|
858
1023
|
routes Audit route graph and automation lanes
|
|
1024
|
+
surface Inspect or change the installed command profile
|
|
859
1025
|
|
|
860
1026
|
Runtime keys:
|
|
861
1027
|
${Object.keys(RUNTIMES).join(', ')}
|
|
@@ -878,6 +1044,11 @@ function parseArgs(argv) {
|
|
|
878
1044
|
statusApplySafe: false,
|
|
879
1045
|
auditJson: false,
|
|
880
1046
|
syncCheck: false,
|
|
1047
|
+
installProfile: DEFAULT_SURFACE_PROFILE,
|
|
1048
|
+
installDryRun: false,
|
|
1049
|
+
installJson: false,
|
|
1050
|
+
surfaceAction: 'status',
|
|
1051
|
+
surfaceProfile: DEFAULT_SURFACE_PROFILE,
|
|
881
1052
|
};
|
|
882
1053
|
|
|
883
1054
|
if (argv[0] === 'status' || argv[0] === 'first-run') {
|
|
@@ -959,6 +1130,72 @@ function parseArgs(argv) {
|
|
|
959
1130
|
}
|
|
960
1131
|
}
|
|
961
1132
|
|
|
1133
|
+
if (argv[0] === 'surface') {
|
|
1134
|
+
options.command = 'surface';
|
|
1135
|
+
let actionSet = false;
|
|
1136
|
+
for (let i = 1; i < argv.length; i++) {
|
|
1137
|
+
const arg = argv[i];
|
|
1138
|
+
if (arg === '--help' || arg === '-h') {
|
|
1139
|
+
options.showHelp = true;
|
|
1140
|
+
} else if (arg === '--version' || arg === '-v') {
|
|
1141
|
+
options.showVersion = true;
|
|
1142
|
+
} else if (arg === '--json') {
|
|
1143
|
+
options.installJson = true;
|
|
1144
|
+
} else if (arg === '--silent' || arg === '--yes') {
|
|
1145
|
+
options.silent = true;
|
|
1146
|
+
} else if (arg === '--dry-run') {
|
|
1147
|
+
options.installDryRun = true;
|
|
1148
|
+
} else if (arg === '--detected') {
|
|
1149
|
+
options.installDetected = true;
|
|
1150
|
+
} else if (arg === '--global') {
|
|
1151
|
+
options.isGlobal = true;
|
|
1152
|
+
} else if (arg === '--project') {
|
|
1153
|
+
options.isGlobal = false;
|
|
1154
|
+
} else if (arg === '--writer') {
|
|
1155
|
+
options.developerMode = false;
|
|
1156
|
+
} else if (arg === '--developer') {
|
|
1157
|
+
options.developerMode = true;
|
|
1158
|
+
} else if (arg === '--runtime') {
|
|
1159
|
+
const value = argv[i + 1];
|
|
1160
|
+
if (!value) throw new Error('--runtime requires a value');
|
|
1161
|
+
addRuntimeList(value);
|
|
1162
|
+
i++;
|
|
1163
|
+
} else if (arg.startsWith('--runtime=')) {
|
|
1164
|
+
addRuntimeList(arg.slice('--runtime='.length));
|
|
1165
|
+
} else if (arg === '--runtimes') {
|
|
1166
|
+
const value = argv[i + 1];
|
|
1167
|
+
if (!value) throw new Error('--runtimes requires a value');
|
|
1168
|
+
addRuntimeList(value);
|
|
1169
|
+
i++;
|
|
1170
|
+
} else if (arg.startsWith('--runtimes=')) {
|
|
1171
|
+
addRuntimeList(arg.slice('--runtimes='.length));
|
|
1172
|
+
} else if (arg === '--profile') {
|
|
1173
|
+
const value = argv[i + 1];
|
|
1174
|
+
if (!value) throw new Error('--profile requires a value');
|
|
1175
|
+
options.surfaceProfile = normalizeSurfaceProfile(value);
|
|
1176
|
+
options.installProfile = options.surfaceProfile;
|
|
1177
|
+
i++;
|
|
1178
|
+
} else if (arg.startsWith('--profile=')) {
|
|
1179
|
+
options.surfaceProfile = normalizeSurfaceProfile(arg.slice('--profile='.length));
|
|
1180
|
+
options.installProfile = options.surfaceProfile;
|
|
1181
|
+
} else if (arg.startsWith('-')) {
|
|
1182
|
+
throw new Error(`Unknown surface argument "${arg}"`);
|
|
1183
|
+
} else if (!actionSet) {
|
|
1184
|
+
options.surfaceAction = arg;
|
|
1185
|
+
actionSet = true;
|
|
1186
|
+
} else if (options.surfaceAction === 'profile') {
|
|
1187
|
+
options.surfaceProfile = normalizeSurfaceProfile(arg);
|
|
1188
|
+
options.installProfile = options.surfaceProfile;
|
|
1189
|
+
} else {
|
|
1190
|
+
throw new Error(`Unknown surface argument "${arg}"`);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
if (!['status', 'list', 'profile'].includes(options.surfaceAction)) {
|
|
1194
|
+
throw new Error(`Unknown surface action "${options.surfaceAction}". Expected status, list, or profile.`);
|
|
1195
|
+
}
|
|
1196
|
+
return options;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
962
1199
|
for (let i = 0; i < argv.length; i++) {
|
|
963
1200
|
const arg = argv[i];
|
|
964
1201
|
if (arg === '--help' || arg === '-h') {
|
|
@@ -967,6 +1204,10 @@ function parseArgs(argv) {
|
|
|
967
1204
|
options.showVersion = true;
|
|
968
1205
|
} else if (arg === '--silent' || arg === '--yes') {
|
|
969
1206
|
options.silent = true;
|
|
1207
|
+
} else if (arg === '--dry-run') {
|
|
1208
|
+
options.installDryRun = true;
|
|
1209
|
+
} else if (arg === '--json') {
|
|
1210
|
+
options.installJson = true;
|
|
970
1211
|
} else if (arg === '--detected') {
|
|
971
1212
|
options.installDetected = true;
|
|
972
1213
|
} else if (arg === '--global') {
|
|
@@ -991,6 +1232,13 @@ function parseArgs(argv) {
|
|
|
991
1232
|
i++;
|
|
992
1233
|
} else if (arg.startsWith('--runtimes=')) {
|
|
993
1234
|
addRuntimeList(arg.slice('--runtimes='.length));
|
|
1235
|
+
} else if (arg === '--profile') {
|
|
1236
|
+
const value = argv[i + 1];
|
|
1237
|
+
if (!value) throw new Error('--profile requires a value');
|
|
1238
|
+
options.installProfile = normalizeSurfaceProfile(value);
|
|
1239
|
+
i++;
|
|
1240
|
+
} else if (arg.startsWith('--profile=')) {
|
|
1241
|
+
options.installProfile = normalizeSurfaceProfile(arg.slice('--profile='.length));
|
|
994
1242
|
} else {
|
|
995
1243
|
throw new Error(`Unknown argument "${arg}"`);
|
|
996
1244
|
}
|
|
@@ -1144,6 +1392,98 @@ function runRouteAudit({ json }) {
|
|
|
1144
1392
|
return result;
|
|
1145
1393
|
}
|
|
1146
1394
|
|
|
1395
|
+
function resolveSurfaceDataDir(isGlobal) {
|
|
1396
|
+
if (isGlobal === false) return path.resolve('.scriveno');
|
|
1397
|
+
if (isGlobal === true) return path.join(os.homedir(), '.scriveno');
|
|
1398
|
+
const projectDir = path.resolve('.scriveno');
|
|
1399
|
+
const projectSettings = path.join(projectDir, 'settings.json');
|
|
1400
|
+
return fs.existsSync(projectSettings) ? projectDir : path.join(os.homedir(), '.scriveno');
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
function readSurfaceSettings(isGlobal) {
|
|
1404
|
+
const dataDir = resolveSurfaceDataDir(isGlobal);
|
|
1405
|
+
const settingsPath = path.join(dataDir, 'settings.json');
|
|
1406
|
+
const raw = readJsonIfExists(settingsPath);
|
|
1407
|
+
if (!raw) {
|
|
1408
|
+
return { dataDir, settings: null, settingsPath };
|
|
1409
|
+
}
|
|
1410
|
+
try {
|
|
1411
|
+
const settings = migrateSettings(raw);
|
|
1412
|
+
const validation = validateSettings(settings);
|
|
1413
|
+
return { dataDir, settings, settingsPath, validation };
|
|
1414
|
+
} catch (err) {
|
|
1415
|
+
return { dataDir, settings: null, settingsPath, error: err.message };
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
function formatSurfaceList(profiles) {
|
|
1420
|
+
return [
|
|
1421
|
+
'Scriveno command profiles',
|
|
1422
|
+
...profiles.map((profile) => `- ${profile.profile}: ${profile.commandCount} registered commands, ${profile.description}`),
|
|
1423
|
+
].join('\n');
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
function formatSurfaceStatus(status) {
|
|
1427
|
+
const lines = [
|
|
1428
|
+
'Scriveno surface status',
|
|
1429
|
+
`Settings: ${status.settingsPath}`,
|
|
1430
|
+
];
|
|
1431
|
+
if (!status.settings) {
|
|
1432
|
+
lines.push('Installed profile: not found');
|
|
1433
|
+
} else {
|
|
1434
|
+
lines.push(`Installed profile: ${status.settings.profile || DEFAULT_SURFACE_PROFILE}`);
|
|
1435
|
+
lines.push(`Installed runtimes: ${(status.settings.runtimes || []).join(', ') || 'none recorded'}`);
|
|
1436
|
+
lines.push(`Scope: ${status.settings.scope || 'unknown'}`);
|
|
1437
|
+
lines.push(`Mode: ${status.settings.developer_mode ? 'developer' : 'writer'}`);
|
|
1438
|
+
}
|
|
1439
|
+
lines.push('');
|
|
1440
|
+
lines.push('Available profiles:');
|
|
1441
|
+
for (const profile of listSurfaceProfiles()) {
|
|
1442
|
+
lines.push(`- ${profile.profile}: ${profile.commandCount} registered commands`);
|
|
1443
|
+
}
|
|
1444
|
+
lines.push('');
|
|
1445
|
+
lines.push('Use `scriveno surface profile <name> --runtimes <runtime>` to reinstall a smaller or larger surface.');
|
|
1446
|
+
return lines.join('\n');
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
function runSurface(parsed, detectedRuntimeKeys) {
|
|
1450
|
+
if (parsed.surfaceAction === 'list') {
|
|
1451
|
+
const profiles = listSurfaceProfiles();
|
|
1452
|
+
console.log(parsed.installJson ? JSON.stringify(profiles, null, 2) : formatSurfaceList(profiles));
|
|
1453
|
+
return profiles;
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
const surfaceState = readSurfaceSettings(parsed.isGlobal);
|
|
1457
|
+
|
|
1458
|
+
if (parsed.surfaceAction === 'status') {
|
|
1459
|
+
console.log(parsed.installJson ? JSON.stringify(surfaceState, null, 2) : formatSurfaceStatus(surfaceState));
|
|
1460
|
+
return surfaceState;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
const runtimeKeys = parsed.runtimeKeys.length > 0
|
|
1464
|
+
? parsed.runtimeKeys
|
|
1465
|
+
: parsed.installDetected
|
|
1466
|
+
? detectedRuntimeKeys
|
|
1467
|
+
: (surfaceState.settings?.runtimes && surfaceState.settings.runtimes.length > 0)
|
|
1468
|
+
? surfaceState.settings.runtimes
|
|
1469
|
+
: detectedRuntimeKeys;
|
|
1470
|
+
|
|
1471
|
+
if (runtimeKeys.length === 0) {
|
|
1472
|
+
throw new Error('No runtime target found. Re-run with --runtimes <list> or --detected.');
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
return runInstall({
|
|
1476
|
+
runtimeKeys,
|
|
1477
|
+
isGlobal: parsed.isGlobal ?? (surfaceState.settings?.scope !== 'project'),
|
|
1478
|
+
developerMode: parsed.developerMode ?? Boolean(surfaceState.settings?.developer_mode),
|
|
1479
|
+
silent: parsed.silent,
|
|
1480
|
+
installMode: 'non-interactive',
|
|
1481
|
+
profile: parsed.surfaceProfile,
|
|
1482
|
+
dryRun: parsed.installDryRun,
|
|
1483
|
+
json: parsed.installJson,
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1147
1487
|
function resolveInstallRequest(parsed, detectedRuntimeKeys, { isTTY }) {
|
|
1148
1488
|
const hasRuntimeDirective = parsed.runtimeKeys.length > 0 || parsed.installDetected;
|
|
1149
1489
|
const hasModifierOverrides = parsed.isGlobal !== null || parsed.developerMode !== null;
|
|
@@ -1172,6 +1512,9 @@ function resolveInstallRequest(parsed, detectedRuntimeKeys, { isTTY }) {
|
|
|
1172
1512
|
developerMode: parsed.developerMode ?? false,
|
|
1173
1513
|
silent: parsed.silent,
|
|
1174
1514
|
installMode: 'non-interactive',
|
|
1515
|
+
profile: parsed.installProfile,
|
|
1516
|
+
dryRun: parsed.installDryRun,
|
|
1517
|
+
json: parsed.installJson,
|
|
1175
1518
|
};
|
|
1176
1519
|
}
|
|
1177
1520
|
|
|
@@ -1180,6 +1523,9 @@ function resolveInstallRequest(parsed, detectedRuntimeKeys, { isTTY }) {
|
|
|
1180
1523
|
isGlobal: parsed.isGlobal,
|
|
1181
1524
|
developerMode: parsed.developerMode,
|
|
1182
1525
|
hasModifierOverrides,
|
|
1526
|
+
profile: parsed.installProfile,
|
|
1527
|
+
dryRun: parsed.installDryRun,
|
|
1528
|
+
json: parsed.installJson,
|
|
1183
1529
|
};
|
|
1184
1530
|
}
|
|
1185
1531
|
|
|
@@ -1314,6 +1660,7 @@ const SETTINGS_SCHEMA = [
|
|
|
1314
1660
|
{ name: 'developer_mode', type: 'boolean', required: true, owned_by: 'user' },
|
|
1315
1661
|
{ name: 'data_dir', type: 'string', required: true, owned_by: 'installer' },
|
|
1316
1662
|
{ name: 'install_mode', type: 'string', required: true, enum: ['interactive', 'non-interactive'], owned_by: 'installer' },
|
|
1663
|
+
{ name: 'profile', type: 'string', required: false, enum: Object.keys(SURFACE_PROFILES), owned_by: 'installer' },
|
|
1317
1664
|
{ name: 'installed_at', type: 'string', required: true, owned_by: 'installer' },
|
|
1318
1665
|
];
|
|
1319
1666
|
|
|
@@ -1347,6 +1694,9 @@ function migrateSettings(raw) {
|
|
|
1347
1694
|
if (!Object.prototype.hasOwnProperty.call(out, 'install_mode') || out.install_mode === undefined) {
|
|
1348
1695
|
out.install_mode = 'interactive';
|
|
1349
1696
|
}
|
|
1697
|
+
if (!Object.prototype.hasOwnProperty.call(out, 'profile') || out.profile === undefined) {
|
|
1698
|
+
out.profile = DEFAULT_SURFACE_PROFILE;
|
|
1699
|
+
}
|
|
1350
1700
|
return out;
|
|
1351
1701
|
}
|
|
1352
1702
|
|
|
@@ -1609,6 +1959,11 @@ async function main() {
|
|
|
1609
1959
|
}
|
|
1610
1960
|
|
|
1611
1961
|
const detectedRuntimeKeys = Object.entries(RUNTIMES).filter(([, runtime]) => runtime.detect()).map(([key]) => key);
|
|
1962
|
+
if (parsed.command === 'surface') {
|
|
1963
|
+
runSurface(parsed, detectedRuntimeKeys);
|
|
1964
|
+
return;
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1612
1967
|
const installRequest = resolveInstallRequest(parsed, detectedRuntimeKeys, { isTTY: Boolean(process.stdin.isTTY) });
|
|
1613
1968
|
|
|
1614
1969
|
if (installRequest.action === 'usage_error') {
|
|
@@ -1684,25 +2039,31 @@ async function main() {
|
|
|
1684
2039
|
silent: false,
|
|
1685
2040
|
detectedRuntimeKeys,
|
|
1686
2041
|
installMode: 'interactive',
|
|
2042
|
+
profile: installRequest.profile,
|
|
2043
|
+
dryRun: installRequest.dryRun,
|
|
2044
|
+
json: installRequest.json,
|
|
1687
2045
|
});
|
|
1688
2046
|
}
|
|
1689
2047
|
|
|
1690
|
-
function installCommandRuntime(runtime, isGlobal, log) {
|
|
2048
|
+
function installCommandRuntime(runtime, isGlobal, log, profile = DEFAULT_SURFACE_PROFILE) {
|
|
1691
2049
|
const commandsDir = isGlobal ? runtime.commands_dir_global : path.resolve(runtime.commands_dir_project);
|
|
1692
2050
|
const agentsDir = isGlobal ? runtime.agents_dir_global : path.resolve(runtime.agents_dir_project);
|
|
1693
|
-
|
|
2051
|
+
const commandsRoot = path.join(PKG_ROOT, 'commands', 'scr');
|
|
2052
|
+
const commandEntries = collectInstallCommandEntries(profile);
|
|
2053
|
+
const removedCommandFiles = cleanMirroredFiles(commandsRoot, commandsDir);
|
|
1694
2054
|
const removedAgentFiles = cleanMirroredFiles(path.join(PKG_ROOT, 'agents'), agentsDir);
|
|
1695
|
-
const commandCount =
|
|
2055
|
+
const commandCount = writeProfileCommandFiles(commandsRoot, commandsDir, commandEntries);
|
|
2056
|
+
writeSurfaceProfileMarker(commandsDir, profile);
|
|
1696
2057
|
const agentCount = copyDir(path.join(PKG_ROOT, 'agents'), agentsDir);
|
|
1697
|
-
log(` ${c('green', 'OK')} ${runtime.label}: ${commandCount} command files -> ${c('dim', commandsDir)}`);
|
|
2058
|
+
log(` ${c('green', 'OK')} ${runtime.label}: ${commandCount} command files (${normalizeSurfaceProfile(profile)} profile) -> ${c('dim', commandsDir)}${removedCommandFiles ? c('dim', ` (cleaned ${removedCommandFiles} stale files)`) : ''}`);
|
|
1698
2059
|
log(` ${c('green', 'OK')} ${runtime.label}: ${agentCount} agent prompts -> ${c('dim', agentsDir)}${removedAgentFiles ? c('dim', ` (cleaned ${removedAgentFiles} stale files)`) : ''}`);
|
|
1699
2060
|
}
|
|
1700
2061
|
|
|
1701
|
-
function installClaudeCommandRuntime(runtime, isGlobal, log) {
|
|
2062
|
+
function installClaudeCommandRuntime(runtime, isGlobal, log, profile = DEFAULT_SURFACE_PROFILE) {
|
|
1702
2063
|
const commandsDir = isGlobal ? runtime.commands_dir_global : path.resolve(runtime.commands_dir_project);
|
|
1703
2064
|
const agentsDir = isGlobal ? runtime.agents_dir_global : path.resolve(runtime.agents_dir_project);
|
|
1704
2065
|
const commandsRoot = path.join(PKG_ROOT, 'commands', 'scr');
|
|
1705
|
-
const commandEntries =
|
|
2066
|
+
const commandEntries = collectInstallCommandEntries(profile);
|
|
1706
2067
|
const fileNames = commandEntries.map((entry) => commandEntryToFlatCommandFileName(entry));
|
|
1707
2068
|
|
|
1708
2069
|
fs.mkdirSync(commandsDir, { recursive: true });
|
|
@@ -1718,23 +2079,31 @@ function installClaudeCommandRuntime(runtime, isGlobal, log) {
|
|
|
1718
2079
|
}
|
|
1719
2080
|
|
|
1720
2081
|
writeInstalledCommandManifest(commandsDir, 'claude-code', fileNames);
|
|
2082
|
+
writeSurfaceProfileMarker(commandsDir, profile);
|
|
1721
2083
|
const agentCount = copyDir(path.join(PKG_ROOT, 'agents'), agentsDir);
|
|
1722
2084
|
|
|
1723
|
-
log(` ${c('green', 'OK')} ${runtime.label}: ${commandEntries.length} /scr-* command files -> ${c('dim', commandsDir)}${removedCommandFiles ? c('dim', ` (cleaned ${removedCommandFiles} stale items)`) : ''}`);
|
|
2085
|
+
log(` ${c('green', 'OK')} ${runtime.label}: ${commandEntries.length} /scr-* command files (${normalizeSurfaceProfile(profile)} profile) -> ${c('dim', commandsDir)}${removedCommandFiles ? c('dim', ` (cleaned ${removedCommandFiles} stale items)`) : ''}`);
|
|
1724
2086
|
log(` ${c('green', 'OK')} ${runtime.label}: ${agentCount} agent prompts -> ${c('dim', agentsDir)}${removedAgentFiles ? c('dim', ` (cleaned ${removedAgentFiles} stale files)`) : ''}`);
|
|
1725
2087
|
}
|
|
1726
2088
|
|
|
1727
|
-
function installManifestSkillRuntime(runtime, isGlobal, log) {
|
|
2089
|
+
function installManifestSkillRuntime(runtime, isGlobal, log, profile = DEFAULT_SURFACE_PROFILE) {
|
|
1728
2090
|
const skillsDir = isGlobal ? runtime.skills_dir_global : path.resolve(runtime.skills_dir_project);
|
|
1729
|
-
|
|
1730
|
-
const
|
|
2091
|
+
const commandsRoot = path.join(PKG_ROOT, 'commands', 'scr');
|
|
2092
|
+
const commandEntries = collectInstallCommandEntries(profile);
|
|
2093
|
+
const manifest = generateSkillManifest(path.join(PKG_ROOT, 'data', 'CONSTRAINTS.json'), profile);
|
|
1731
2094
|
fs.mkdirSync(skillsDir, { recursive: true });
|
|
2095
|
+
removePathIfExists(path.join(skillsDir, 'SKILL.md'));
|
|
2096
|
+
const commandsTarget = path.join(skillsDir, 'commands', 'scr');
|
|
2097
|
+
const agentsTarget = path.join(skillsDir, 'agents');
|
|
2098
|
+
cleanMirroredFiles(commandsRoot, commandsTarget);
|
|
2099
|
+
cleanMirroredFiles(path.join(PKG_ROOT, 'agents'), agentsTarget);
|
|
1732
2100
|
atomicWriteFileSync(path.join(skillsDir, 'SKILL.md'), manifest);
|
|
1733
|
-
const commandCount =
|
|
1734
|
-
|
|
2101
|
+
const commandCount = writeProfileCommandFiles(commandsRoot, commandsTarget, commandEntries);
|
|
2102
|
+
writeSurfaceProfileMarker(skillsDir, profile);
|
|
2103
|
+
const agentCount = copyDir(path.join(PKG_ROOT, 'agents'), agentsTarget);
|
|
1735
2104
|
log(` ${c('green', 'OK')} ${runtime.label}: SKILL.md manifest -> ${c('dim', path.join(skillsDir, 'SKILL.md'))}`);
|
|
1736
|
-
log(` ${c('green', 'OK')} ${runtime.label}: ${commandCount} command files -> ${c('dim',
|
|
1737
|
-
log(` ${c('green', 'OK')} ${runtime.label}: ${agentCount} agent prompts -> ${c('dim',
|
|
2105
|
+
log(` ${c('green', 'OK')} ${runtime.label}: ${commandCount} command files (${normalizeSurfaceProfile(profile)} profile) -> ${c('dim', commandsTarget)}`);
|
|
2106
|
+
log(` ${c('green', 'OK')} ${runtime.label}: ${agentCount} agent prompts -> ${c('dim', agentsTarget)}`);
|
|
1738
2107
|
}
|
|
1739
2108
|
|
|
1740
2109
|
function installCodexAgentsWithMetadata(agentsDir) {
|
|
@@ -1757,17 +2126,15 @@ function installCodexAgentsWithMetadata(agentsDir) {
|
|
|
1757
2126
|
};
|
|
1758
2127
|
}
|
|
1759
2128
|
|
|
1760
|
-
function installCodexRuntime(runtime, isGlobal, log) {
|
|
2129
|
+
function installCodexRuntime(runtime, isGlobal, log, profile = DEFAULT_SURFACE_PROFILE) {
|
|
1761
2130
|
const skillsDir = isGlobal ? runtime.skills_dir_global : path.resolve(runtime.skills_dir_project);
|
|
1762
2131
|
const commandsDir = isGlobal ? runtime.commands_dir_global : path.resolve(runtime.commands_dir_project);
|
|
1763
2132
|
const agentsDir = isGlobal ? runtime.agents_dir_global : path.resolve(runtime.agents_dir_project);
|
|
1764
2133
|
const sourceCommandsRoot = path.join(PKG_ROOT, 'commands', 'scr');
|
|
1765
|
-
const commandEntries =
|
|
2134
|
+
const commandEntries = collectInstallCommandEntries(profile);
|
|
1766
2135
|
const skillNames = commandEntries.map((entry) => entry.skillName);
|
|
1767
2136
|
|
|
1768
|
-
|
|
1769
|
-
// (removed commands, legacy flat layouts, etc.) do not linger.
|
|
1770
|
-
removePathIfExists(commandsDir);
|
|
2137
|
+
const removedCommandFiles = cleanMirroredFiles(sourceCommandsRoot, commandsDir);
|
|
1771
2138
|
fs.mkdirSync(skillsDir, { recursive: true });
|
|
1772
2139
|
const removedSkillDirs = cleanCodexSkillDirs(skillsDir, skillNames);
|
|
1773
2140
|
|
|
@@ -1779,13 +2146,8 @@ function installCodexRuntime(runtime, isGlobal, log) {
|
|
|
1779
2146
|
// clean content -- not on top of a previously-marked installed file -- so
|
|
1780
2147
|
// re-runs are idempotent (single marker, current prose rewrite).
|
|
1781
2148
|
let commandCount = 0;
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
const sourceContent = fs.readFileSync(sourcePath, 'utf8');
|
|
1785
|
-
const targetPath = path.join(commandsDir, entry.relativePath);
|
|
1786
|
-
atomicWriteFileSync(targetPath, generateCodexCommandContent(entry, sourceContent));
|
|
1787
|
-
commandCount++;
|
|
1788
|
-
}
|
|
2149
|
+
commandCount = writeProfileCommandFiles(sourceCommandsRoot, commandsDir, commandEntries, generateCodexCommandContent);
|
|
2150
|
+
writeSurfaceProfileMarker(commandsDir, profile);
|
|
1789
2151
|
|
|
1790
2152
|
const agentInstall = installCodexAgentsWithMetadata(agentsDir);
|
|
1791
2153
|
|
|
@@ -1796,13 +2158,14 @@ function installCodexRuntime(runtime, isGlobal, log) {
|
|
|
1796
2158
|
atomicWriteFileSync(path.join(skillDir, 'SKILL.md'), generateCodexSkill(entry, commandPath));
|
|
1797
2159
|
}
|
|
1798
2160
|
writeCodexSkillManifest(skillsDir, skillNames);
|
|
2161
|
+
writeSurfaceProfileMarker(skillsDir, profile);
|
|
1799
2162
|
|
|
1800
|
-
log(` ${c('green', 'OK')} ${runtime.label}: ${commandEntries.length} \$scr-* skills -> ${c('dim', skillsDir)}${removedSkillDirs ? c('dim', ` (cleaned ${removedSkillDirs} stale dirs)`) : ''}`);
|
|
1801
|
-
log(` ${c('green', 'OK')} ${runtime.label}: ${commandCount} command files -> ${c('dim', commandsDir)}`);
|
|
2163
|
+
log(` ${c('green', 'OK')} ${runtime.label}: ${commandEntries.length} \$scr-* skills (${normalizeSurfaceProfile(profile)} profile) -> ${c('dim', skillsDir)}${removedSkillDirs ? c('dim', ` (cleaned ${removedSkillDirs} stale dirs)`) : ''}`);
|
|
2164
|
+
log(` ${c('green', 'OK')} ${runtime.label}: ${commandCount} command files -> ${c('dim', commandsDir)}${removedCommandFiles ? c('dim', ` (cleaned ${removedCommandFiles} stale files)`) : ''}`);
|
|
1802
2165
|
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)`) : ''}`);
|
|
1803
2166
|
}
|
|
1804
2167
|
|
|
1805
|
-
function installGuidedRuntime(runtime, isGlobal, dataDir, log) {
|
|
2168
|
+
function installGuidedRuntime(runtime, isGlobal, dataDir, log, profile = DEFAULT_SURFACE_PROFILE) {
|
|
1806
2169
|
const guideDir = isGlobal ? runtime.guide_dir_global : path.resolve(runtime.guide_dir_project);
|
|
1807
2170
|
const currentProjectDir = path.resolve('.');
|
|
1808
2171
|
const setupGuide = generatePerplexitySetupGuide({
|
|
@@ -1821,12 +2184,13 @@ function installGuidedRuntime(runtime, isGlobal, dataDir, log) {
|
|
|
1821
2184
|
atomicWriteFileSync(path.join(guideDir, 'SETUP.md'), setupGuide);
|
|
1822
2185
|
atomicWriteFileSync(path.join(guideDir, 'connector-command.txt'), connectorCommand + '\n');
|
|
1823
2186
|
atomicWriteFileSync(path.join(guideDir, 'connector-command.current-project.txt'), currentProjectCommand + '\n');
|
|
2187
|
+
writeSurfaceProfileMarker(guideDir, profile);
|
|
1824
2188
|
|
|
1825
2189
|
log(` ${c('green', 'OK')} ${runtime.label}: setup guide -> ${c('dim', path.join(guideDir, 'SETUP.md'))}`);
|
|
1826
2190
|
log(` ${c('green', 'OK')} ${runtime.label}: connector recipe -> ${c('dim', path.join(guideDir, 'connector-command.txt'))}`);
|
|
1827
2191
|
}
|
|
1828
2192
|
|
|
1829
|
-
function writeSharedAssets(dataDir, runtimeKeys, isGlobal, developerMode, installMode, log) {
|
|
2193
|
+
function writeSharedAssets(dataDir, runtimeKeys, isGlobal, developerMode, installMode, log, profile = DEFAULT_SURFACE_PROFILE) {
|
|
1830
2194
|
fs.mkdirSync(path.join(dataDir, 'templates'), { recursive: true });
|
|
1831
2195
|
fs.mkdirSync(path.join(dataDir, 'data'), { recursive: true });
|
|
1832
2196
|
fs.mkdirSync(path.join(dataDir, 'lib'), { recursive: true });
|
|
@@ -1873,6 +2237,7 @@ function writeSharedAssets(dataDir, runtimeKeys, isGlobal, developerMode, instal
|
|
|
1873
2237
|
developer_mode: developerMode,
|
|
1874
2238
|
data_dir: dataDir,
|
|
1875
2239
|
install_mode: installMode,
|
|
2240
|
+
profile: normalizeSurfaceProfile(profile),
|
|
1876
2241
|
installed_at: new Date().toISOString(),
|
|
1877
2242
|
};
|
|
1878
2243
|
const mergedSettings = mergeSettings(existingSettings, incomingSettings);
|
|
@@ -1929,14 +2294,101 @@ function collectTargetDirsForSweep(runtimeKeys, isGlobal, dataDir) {
|
|
|
1929
2294
|
return Array.from(dirs);
|
|
1930
2295
|
}
|
|
1931
2296
|
|
|
1932
|
-
function
|
|
2297
|
+
function runtimeInstallTargets(runtimeKey, runtime, isGlobal) {
|
|
2298
|
+
const resolve = (globalPath, projectPath) => isGlobal ? globalPath : (projectPath ? path.resolve(projectPath) : null);
|
|
2299
|
+
return {
|
|
2300
|
+
runtime: runtimeKey,
|
|
2301
|
+
label: runtime.label,
|
|
2302
|
+
type: runtime.type,
|
|
2303
|
+
commandsDir: resolve(runtime.commands_dir_global, runtime.commands_dir_project),
|
|
2304
|
+
skillsDir: resolve(runtime.skills_dir_global, runtime.skills_dir_project),
|
|
2305
|
+
agentsDir: resolve(runtime.agents_dir_global, runtime.agents_dir_project),
|
|
2306
|
+
guideDir: resolve(runtime.guide_dir_global, runtime.guide_dir_project),
|
|
2307
|
+
};
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
function buildInstallDryRun({ runtimeKeys, isGlobal, developerMode, installMode, profile = DEFAULT_SURFACE_PROFILE }) {
|
|
2311
|
+
const resolvedProfile = normalizeSurfaceProfile(profile);
|
|
1933
2312
|
const dataDir = isGlobal ? path.join(os.homedir(), '.scriveno') : path.resolve('.scriveno');
|
|
1934
|
-
const
|
|
2313
|
+
const commandEntries = collectInstallCommandEntries(resolvedProfile);
|
|
2314
|
+
const profileSummary = surfaceProfileSummary(resolvedProfile);
|
|
2315
|
+
const agentCount = collectAgentEntries(path.join(PKG_ROOT, 'agents')).length;
|
|
2316
|
+
const sharedAssets = {
|
|
2317
|
+
templates: listRelativeFiles(path.join(PKG_ROOT, 'templates')).length,
|
|
2318
|
+
data: listRelativeFiles(path.join(PKG_ROOT, 'data')).length,
|
|
2319
|
+
lib: listRelativeFiles(path.join(PKG_ROOT, 'lib')).length,
|
|
2320
|
+
};
|
|
2321
|
+
|
|
2322
|
+
return {
|
|
2323
|
+
version: VERSION,
|
|
2324
|
+
profile: resolvedProfile,
|
|
2325
|
+
profileLabel: profileSummary.label,
|
|
2326
|
+
profileDescription: profileSummary.description,
|
|
2327
|
+
registeredCommands: profileSummary.commandCount,
|
|
2328
|
+
commandFiles: commandEntries.length,
|
|
2329
|
+
agentPrompts: agentCount,
|
|
2330
|
+
scope: isGlobal ? 'global' : 'project',
|
|
2331
|
+
mode: developerMode ? 'developer' : 'writer',
|
|
2332
|
+
installMode,
|
|
2333
|
+
dataDir,
|
|
2334
|
+
sharedAssets,
|
|
2335
|
+
targets: runtimeKeys.map((runtimeKey) => {
|
|
2336
|
+
const runtime = RUNTIMES[runtimeKey];
|
|
2337
|
+
if (!runtime) throw new Error(`Unknown runtime "${runtimeKey}"`);
|
|
2338
|
+
return runtimeInstallTargets(runtimeKey, runtime, isGlobal);
|
|
2339
|
+
}),
|
|
2340
|
+
writes: false,
|
|
2341
|
+
};
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
function formatInstallDryRunReport(plan) {
|
|
2345
|
+
const lines = [
|
|
2346
|
+
'Scriveno install dry run',
|
|
2347
|
+
`Version: ${plan.version}`,
|
|
2348
|
+
`Profile: ${plan.profile} (${plan.profileLabel})`,
|
|
2349
|
+
`Scope: ${plan.scope}`,
|
|
2350
|
+
`Mode: ${plan.mode}`,
|
|
2351
|
+
`Command files selected: ${plan.commandFiles}`,
|
|
2352
|
+
`Registered command entries selected: ${plan.registeredCommands}`,
|
|
2353
|
+
`Agent prompts selected: ${plan.agentPrompts}`,
|
|
2354
|
+
`Shared assets: ${plan.sharedAssets.templates} templates, ${plan.sharedAssets.data} data files, ${plan.sharedAssets.lib} lib files`,
|
|
2355
|
+
`Data directory: ${plan.dataDir}`,
|
|
2356
|
+
'',
|
|
2357
|
+
'Runtime targets:',
|
|
2358
|
+
];
|
|
2359
|
+
for (const target of plan.targets) {
|
|
2360
|
+
lines.push(`- ${target.label} (${target.runtime})`);
|
|
2361
|
+
if (target.commandsDir) lines.push(` commands: ${target.commandsDir}`);
|
|
2362
|
+
if (target.skillsDir) lines.push(` skills: ${target.skillsDir}`);
|
|
2363
|
+
if (target.agentsDir) lines.push(` agents: ${target.agentsDir}`);
|
|
2364
|
+
if (target.guideDir) lines.push(` guide: ${target.guideDir}`);
|
|
2365
|
+
}
|
|
2366
|
+
lines.push('');
|
|
2367
|
+
lines.push('No files were written.');
|
|
2368
|
+
return lines.join('\n');
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
function runInstall({ runtimeKeys, isGlobal, developerMode, silent, installMode, profile = DEFAULT_SURFACE_PROFILE, dryRun = false, json = false }) {
|
|
2372
|
+
const resolvedProfile = normalizeSurfaceProfile(profile);
|
|
2373
|
+
const dataDir = isGlobal ? path.join(os.homedir(), '.scriveno') : path.resolve('.scriveno');
|
|
2374
|
+
const log = (silent || json || dryRun) ? () => {} : (message) => console.log(message);
|
|
1935
2375
|
|
|
1936
2376
|
if (!runtimeKeys.length) {
|
|
1937
2377
|
throw new Error('No runtimes selected for installation');
|
|
1938
2378
|
}
|
|
1939
2379
|
|
|
2380
|
+
if (dryRun) {
|
|
2381
|
+
const plan = buildInstallDryRun({
|
|
2382
|
+
runtimeKeys,
|
|
2383
|
+
isGlobal,
|
|
2384
|
+
developerMode,
|
|
2385
|
+
installMode,
|
|
2386
|
+
profile: resolvedProfile,
|
|
2387
|
+
});
|
|
2388
|
+
console.log(json ? JSON.stringify(plan, null, 2) : formatInstallDryRunReport(plan));
|
|
2389
|
+
return plan;
|
|
2390
|
+
}
|
|
2391
|
+
|
|
1940
2392
|
let totalOrphansRemoved = 0;
|
|
1941
2393
|
for (const dir of collectTargetDirsForSweep(runtimeKeys, isGlobal, dataDir)) {
|
|
1942
2394
|
totalOrphansRemoved += cleanOrphanedTempFiles(dir);
|
|
@@ -1945,7 +2397,7 @@ function runInstall({ runtimeKeys, isGlobal, developerMode, silent, installMode
|
|
|
1945
2397
|
log(c('dim', ` Cleaned ${totalOrphansRemoved} orphaned temp file(s) from prior interrupted install`));
|
|
1946
2398
|
}
|
|
1947
2399
|
|
|
1948
|
-
if (!silent) {
|
|
2400
|
+
if (!silent && !json) {
|
|
1949
2401
|
console.log('\n' + c('bold', 'Installing...'));
|
|
1950
2402
|
}
|
|
1951
2403
|
|
|
@@ -1955,27 +2407,42 @@ function runInstall({ runtimeKeys, isGlobal, developerMode, silent, installMode
|
|
|
1955
2407
|
throw new Error(`Unknown runtime "${runtimeKey}"`);
|
|
1956
2408
|
}
|
|
1957
2409
|
if (runtimeKey === 'codex') {
|
|
1958
|
-
installCodexRuntime(runtime, isGlobal, log);
|
|
2410
|
+
installCodexRuntime(runtime, isGlobal, log, resolvedProfile);
|
|
1959
2411
|
} else if (runtime.command_layout === 'flat-prefixed') {
|
|
1960
|
-
installClaudeCommandRuntime(runtime, isGlobal, log);
|
|
2412
|
+
installClaudeCommandRuntime(runtime, isGlobal, log, resolvedProfile);
|
|
1961
2413
|
} else if (runtime.type === 'skills') {
|
|
1962
|
-
installManifestSkillRuntime(runtime, isGlobal, log);
|
|
2414
|
+
installManifestSkillRuntime(runtime, isGlobal, log, resolvedProfile);
|
|
1963
2415
|
} else if (runtime.type === 'guided-mcp') {
|
|
1964
|
-
installGuidedRuntime(runtime, isGlobal, dataDir, log);
|
|
2416
|
+
installGuidedRuntime(runtime, isGlobal, dataDir, log, resolvedProfile);
|
|
1965
2417
|
} else {
|
|
1966
|
-
installCommandRuntime(runtime, isGlobal, log);
|
|
2418
|
+
installCommandRuntime(runtime, isGlobal, log, resolvedProfile);
|
|
1967
2419
|
}
|
|
1968
2420
|
}
|
|
1969
2421
|
|
|
1970
|
-
writeSharedAssets(dataDir, runtimeKeys, isGlobal, developerMode, installMode, log);
|
|
2422
|
+
writeSharedAssets(dataDir, runtimeKeys, isGlobal, developerMode, installMode, log, resolvedProfile);
|
|
2423
|
+
|
|
2424
|
+
const summary = {
|
|
2425
|
+
version: VERSION,
|
|
2426
|
+
runtimes: runtimeKeys,
|
|
2427
|
+
scope: isGlobal ? 'global' : 'project',
|
|
2428
|
+
mode: developerMode ? 'developer' : 'writer',
|
|
2429
|
+
profile: resolvedProfile,
|
|
2430
|
+
dataDir,
|
|
2431
|
+
};
|
|
2432
|
+
|
|
2433
|
+
if (json) {
|
|
2434
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
2435
|
+
return summary;
|
|
2436
|
+
}
|
|
1971
2437
|
|
|
1972
2438
|
if (silent) {
|
|
1973
|
-
console.log(`Installed Scriveno ${VERSION} to ${runtimeKeys.join(', ')} (${isGlobal ? 'global' : 'project'}, ${developerMode ? 'developer' : 'writer'} mode).`);
|
|
1974
|
-
return;
|
|
2439
|
+
console.log(`Installed Scriveno ${VERSION} to ${runtimeKeys.join(', ')} (${isGlobal ? 'global' : 'project'}, ${developerMode ? 'developer' : 'writer'} mode, ${resolvedProfile} profile).`);
|
|
2440
|
+
return summary;
|
|
1975
2441
|
}
|
|
1976
2442
|
|
|
1977
2443
|
console.log('\n' + c('bold', c('green', 'Installation complete!')));
|
|
1978
2444
|
printNextSteps(runtimeKeys);
|
|
2445
|
+
return summary;
|
|
1979
2446
|
}
|
|
1980
2447
|
|
|
1981
2448
|
// Only run interactive installer when executed directly
|
|
@@ -1990,14 +2457,19 @@ if (require.main === module) {
|
|
|
1990
2457
|
module.exports = {
|
|
1991
2458
|
copyDir,
|
|
1992
2459
|
RUNTIMES,
|
|
2460
|
+
SURFACE_PROFILES,
|
|
2461
|
+
DEFAULT_SURFACE_PROFILE,
|
|
1993
2462
|
parseArgs,
|
|
1994
2463
|
resolveInstallRequest,
|
|
2464
|
+
runInstall,
|
|
1995
2465
|
runStatus,
|
|
1996
2466
|
runSyncCheck,
|
|
1997
2467
|
runRuntimeSmoke,
|
|
1998
2468
|
runAgentAvailability,
|
|
1999
2469
|
runRouteAudit,
|
|
2000
2470
|
collectCommandEntries,
|
|
2471
|
+
collectCommandEntriesForProfile,
|
|
2472
|
+
collectInstallCommandEntries,
|
|
2001
2473
|
collectAgentEntries,
|
|
2002
2474
|
assertNoSkillNameCollisions,
|
|
2003
2475
|
cleanCodexSkillDirs,
|
|
@@ -2015,9 +2487,16 @@ module.exports = {
|
|
|
2015
2487
|
installManifestSkillRuntime,
|
|
2016
2488
|
installCodexRuntime,
|
|
2017
2489
|
installCodexAgentsWithMetadata,
|
|
2490
|
+
runSurface,
|
|
2018
2491
|
cleanFlatCommandFiles,
|
|
2019
2492
|
generateCodexSkill,
|
|
2020
2493
|
generateSkillManifest,
|
|
2494
|
+
normalizeSurfaceProfile,
|
|
2495
|
+
resolveProfileCommandKeys,
|
|
2496
|
+
surfaceProfileSummary,
|
|
2497
|
+
listSurfaceProfiles,
|
|
2498
|
+
buildInstallDryRun,
|
|
2499
|
+
formatInstallDryRunReport,
|
|
2021
2500
|
buildFilesystemMcpCommand,
|
|
2022
2501
|
generatePerplexitySetupGuide,
|
|
2023
2502
|
atomicWriteFileSync,
|