singleton-pipeline 0.4.0-beta.11 → 0.4.0-beta.13

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.
@@ -1,7 +1,9 @@
1
1
  import fs from 'node:fs/promises';
2
+ import fsSync from 'node:fs';
2
3
  import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
3
5
  import { spawn } from 'node:child_process';
4
- import { createShell, C } from '../shell.js';
6
+ import { createShell, S } from '../shell.js';
5
7
  import { scanAgents } from '../scanner.js';
6
8
  import { runPipeline } from '../executor.js';
7
9
  import { newAgentShellCommand } from './new.js';
@@ -13,18 +15,18 @@ const HELP = [
13
15
  '',
14
16
  `{bold}Commands{/}`,
15
17
  '',
16
- ` {${C.violet}-fg}/run <name>{/} run a pipeline`,
17
- ` {${C.violet}-fg}/run <name> --dry{/} dry-run (plan without API calls)`,
18
- ` {${C.violet}-fg}/run <name> --verbose{/} show prompts and outputs`,
19
- ` {${C.violet}-fg}/run <name> --debug{/} pause before each step`,
20
- ` {${C.blue}-fg}/scan{/} scan .md agents`,
21
- ` {${C.blue}-fg}/new{/} create a new agent`,
22
- ` {${C.blue}-fg}/serve{/} start the web server`,
23
- ` {${C.blue}-fg}/stop{/} stop the web server`,
24
- ` {${C.blue}-fg}/commit-last{/} commit deliverables from the last run`,
25
- ` {${C.pink}-fg}/ls{/} list pipelines`,
26
- ` {${C.pink}-fg}/help{/} show help`,
27
- ` {${C.pink}-fg}/quit{/} quit {${C.dimV}-fg}(or Ctrl+C){/}`,
18
+ ` {${S.accent}-fg}{bold}/run <name>{/} run a pipeline`,
19
+ ` {${S.accent}-fg}{bold}/run <name> --dry{/} dry-run (plan without API calls)`,
20
+ ` {${S.accent}-fg}{bold}/run <name> --verbose{/} show prompts and outputs`,
21
+ ` {${S.accent}-fg}{bold}/run <name> --debug{/} pause before each step`,
22
+ ` {${S.accent}-fg}{bold}/scan{/} scan .md agents`,
23
+ ` {${S.accent}-fg}{bold}/new{/} create a new agent`,
24
+ ` {${S.accent}-fg}{bold}/serve{/} start the web server`,
25
+ ` {${S.accent}-fg}{bold}/stop{/} stop the web server`,
26
+ ` {${S.accent}-fg}{bold}/commit-last{/} commit deliverables from the last run`,
27
+ ` {${S.accent}-fg}{bold}/ls{/} list pipelines`,
28
+ ` {${S.accent}-fg}{bold}/help{/} show help`,
29
+ ` {${S.accent}-fg}{bold}/quit{/} quit {${S.muted}-fg}(or Ctrl+C){/}`,
28
30
  '',
29
31
  ].join('\n');
30
32
 
@@ -179,30 +181,22 @@ async function countAgents(root) {
179
181
  } catch { return 0; }
180
182
  }
181
183
 
182
- // Violet peach gradient (4 interpolated steps)
183
- const SINGLETON_GRAD = ['#C084FC', '#D499E8', '#EBB0D8', '#F9A8D4'];
184
-
185
- // Gradient by column position so all rows share the same color mapping
186
- function gradientLine(text, totalWidth, colors = SINGLETON_GRAD) {
187
- return text.split('').map((ch, i) => {
188
- if (ch === ' ') return ch;
189
- const color = colors[Math.min(Math.floor((i / totalWidth) * colors.length), colors.length - 1)];
190
- return `{${color}-fg}${ch}{/}`;
191
- }).join('');
184
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
185
+ const LOGO_PATH = path.join(__dirname, '..', 'assets', 'singleton-logo.txt');
186
+ function loadLogoLines() {
187
+ try {
188
+ const raw = fsSync.readFileSync(LOGO_PATH, 'utf8');
189
+ return raw
190
+ .replace(/\x1b\[\?25[lh]/g, '')
191
+ .split('\n')
192
+ .filter((line) => line.length > 0);
193
+ } catch {
194
+ return [];
195
+ }
192
196
  }
193
197
 
194
- function plainBrightLine(text) {
195
- return text.split('').map((ch) => (ch === ' ' ? ch : `{#FFFFFF-fg}${ch}{/}`)).join('');
196
- }
197
-
198
- const SINGLETON_RAW = [
199
- '▄█████ ▄▄ ▄▄ ▄▄ ▄▄▄▄ ▄▄ ▄▄▄▄▄ ▄▄▄▄▄▄ ▄▄▄ ▄▄ ▄▄ ',
200
- '▀▀▀▄▄▄ ██ ███▄██ ██ ▄▄ ██ ▄▄██ ██ ██▀██ ███▄██ ',
201
- '█████▀ ██ ██ ▀██ ▀███▀ ██▄▄▄ ▄▄▄██ ██ ▀███▀ ██ ▀██ ',
202
- ];
203
-
204
- const ART_WIDTH = Math.max(...SINGLETON_RAW.map((l) => l.length));
205
- const APP_VERSION = 'v0.4.0-beta.11';
198
+ const SINGLETON_LOGO = loadLogoLines();
199
+ const APP_VERSION = 'v0.4.0-beta.12';
206
200
 
207
201
  async function showWelcome(root, shell) {
208
202
  const now = new Date();
@@ -220,27 +214,27 @@ async function showWelcome(root, shell) {
220
214
  const headerLines = [
221
215
  '',
222
216
  ' '.repeat(tw('Welcome back')),
223
- `${dateStr} ${timeStr} {${C.ghost}-fg}${APP_VERSION}{/}`,
217
+ `${dateStr} ${timeStr} {${S.muted}-fg}${APP_VERSION}{/}`,
224
218
  '',
225
219
  `${pipelines.length} pipeline${pipelines.length !== 1 ? 's' : ''}`,
226
220
  `${agentCount} agent${agentCount !== 1 ? 's' : ''}`,
227
221
  '',
228
- `{${C.peach}-fg}{bold}New{/} {#FFFFFF-fg}you can now debug pipeline runs{/}`,
229
- `{${C.ghost}-fg}·{/} {${C.mint}-fg}pause steps{/} {${C.ghost}-fg}·{/} {${C.blue}-fg}inspect prompts{/} {${C.ghost}-fg}·{/} {${C.peach}-fg}edit inputs{/} {${C.ghost}-fg}·{/} {${C.violet}-fg}review diffs{/}`,
230
- `{${C.peach}-fg}{bold}New{/} {#FFFFFF-fg}Copilot runner support{/}`,
231
- `{${C.ghost}-fg}·{/} {${C.mint}-fg}provider copilot{/} {${C.ghost}-fg}·{/} {${C.blue}-fg}runner_agent optional{/} {${C.ghost}-fg}·{/} {${C.peach}-fg}native tool permissions{/}`,
232
- `{${C.peach}-fg}{bold}New{/} {#FFFFFF-fg}experimental OpenCode runner support{/}`,
233
- `{${C.ghost}-fg}·{/} {${C.mint}-fg}provider opencode{/} {${C.ghost}-fg}·{/} {${C.blue}-fg}runner_agent optional{/} {${C.ghost}-fg}·{/} {${C.peach}-fg}post-run security{/}`,
222
+ `{${S.warning}-fg}{bold}New{/} {${S.text}-fg}redesigned /new agent flow{/}`,
223
+ `{${S.subtle}-fg}·{/} {${S.keyword}-fg}sectioned form{/} {${S.subtle}-fg}·{/} {${S.keyword}-fg}field autocomplete{/} {${S.subtle}-fg}·{/} {${S.keyword}-fg}review + :back{/}`,
224
+ `{${S.warning}-fg}{bold}New{/} {${S.text}-fg}safer replay and prompt inputs{/}`,
225
+ `{${S.subtle}-fg}·{/} {${S.keyword}-fg}snapshot restore{/} {${S.subtle}-fg}·{/} {${S.keyword}-fg}escaped user XML{/} {${S.subtle}-fg}·{/} {${S.keyword}-fg}loud rollback limits{/}`,
226
+ `{${S.warning}-fg}{bold}New{/} {${S.text}-fg}cleaner run UI and output{/}`,
227
+ `{${S.subtle}-fg}·{/} {${S.keyword}-fg}framed logs{/} {${S.subtle}-fg}·{/} {${S.keyword}-fg}step reviews{/} {${S.subtle}-fg}·{/} {${S.keyword}-fg}final Copilot output{/}`,
234
228
  '',
235
229
  ];
236
230
  const TAGLINE = 'one to rule them all';
237
231
  const CREDIT = 'Developed by Romain LENTZ';
238
- const bottomBlockHeight = 3 + SINGLETON_RAW.length;
232
+ const bottomBlockHeight = 3 + SINGLETON_LOGO.length;
239
233
  const spacerLines = Math.max(0, contentHeight - headerLines.length - bottomBlockHeight);
240
234
 
241
235
  // Track shimmer positions.
242
236
  const welcomeRow = CONTENT_PAD_TOP + 2;
243
- const creditRow = CONTENT_PAD_TOP + headerLines.length + spacerLines;
237
+ const creditRow = CONTENT_PAD_TOP + headerLines.length + spacerLines + SINGLETON_LOGO.length;
244
238
  const taglineRow = creditRow + 1;
245
239
 
246
240
  for (const line of headerLines) {
@@ -250,12 +244,13 @@ async function showWelcome(root, shell) {
250
244
  shell.log('');
251
245
  }
252
246
 
253
- shell.log(`{${C.dimV}-fg}${CREDIT}{/}`);
247
+ for (const logoLine of SINGLETON_LOGO) {
248
+ shell.log(logoLine);
249
+ }
250
+
251
+ shell.log(`{${S.muted}-fg}${CREDIT}{/}`);
254
252
  shell.log(' '.repeat(TAGLINE.length));
255
253
  shell.log('');
256
- for (const line of SINGLETON_RAW) {
257
- shell.log(plainBrightLine(line));
258
- }
259
254
 
260
255
  const stopWelcome = shell.createShimmer('Welcome back', welcomeRow, CONTENT_PAD_LEFT);
261
256
  const stopTagline = shell.createShimmer(TAGLINE, taglineRow, CONTENT_PAD_LEFT);
@@ -318,14 +313,16 @@ export async function replCommand(opts) {
318
313
  await closeServer(serveState.server);
319
314
  serveState.clear();
320
315
  }
321
- shell.log('{#676498-fg}See you soon.{/}');
316
+ shell.log(`{${S.muted}-fg}See you soon.{/}`);
322
317
  setTimeout(() => { shell.destroy(); process.exit(0); }, 300);
323
318
  return;
324
319
  default:
325
- shell.log(`{${C.peach}-fg}!{/} Unknown command: {bold}${cmd}{/} — type /help`);
320
+ shell.log(`{${S.warning}-fg}!{/} Unknown command: {bold}${cmd}{/} — type /help`);
326
321
  }
327
322
  } catch (err) {
328
- shell.log(`{${C.salmon}-fg}✕{/} ${err.message}`);
323
+ // Safety: if a command (typically /run) threw before resetting the border, reflect the error.
324
+ shell.setMode?.('error');
325
+ shell.log(`{${S.error}-fg}✕{/} ${err.message}`);
329
326
  }
330
327
  shell.enableInput();
331
328
  });
@@ -340,19 +337,19 @@ async function cmdRun(args, root, shell) {
340
337
  if (!name) {
341
338
  const pipelines = await listPipelines(root);
342
339
  if (pipelines.length === 0) {
343
- shell.log(`{${C.peach}-fg}!{/} No pipelines found.`);
340
+ shell.log(`{${S.warning}-fg}!{/} No pipelines found.`);
344
341
  return;
345
342
  }
346
- shell.log(`{${C.dimV}-fg} Pipelines: ${pipelines.join(', ')}{/}`);
347
- shell.log(`{${C.dimV}-fg} Usage: /run <name> [--dry] [--verbose] [--debug]{/}`);
343
+ shell.log(`{${S.muted}-fg} Pipelines: ${pipelines.join(', ')}{/}`);
344
+ shell.log(`{${S.muted}-fg} Usage: /run <name> [--dry] [--verbose] [--debug]{/}`);
348
345
  return;
349
346
  }
350
347
 
351
348
  const filePath = await resolvePipelinePath(name, root);
352
349
  if (!filePath) {
353
- shell.log(`{${C.salmon}-fg}✕{/} Pipeline "{bold}${name}{/}" not found.`);
350
+ shell.log(`{${S.error}-fg}✕{/} Pipeline "{bold}${name}{/}" not found.`);
354
351
  const pipelines = await listPipelines(root);
355
- if (pipelines.length) shell.log(`{${C.dimV}-fg} Available: ${pipelines.join(', ')}{/}`);
352
+ if (pipelines.length) shell.log(`{${S.muted}-fg} Available: ${pipelines.join(', ')}{/}`);
356
353
  return;
357
354
  }
358
355
 
@@ -362,12 +359,12 @@ async function cmdRun(args, root, shell) {
362
359
  async function cmdLs(root, shell) {
363
360
  const pipelines = await listPipelines(root);
364
361
  if (pipelines.length === 0) {
365
- shell.log('{yellow-fg}!{/} No pipelines found.');
362
+ shell.log(`{${S.warning}-fg}!{/} No pipelines found.`);
366
363
  return;
367
364
  }
368
365
  shell.log(`{bold}Pipelines (${pipelines.length}){/}`);
369
366
  shell.log('');
370
- for (const p of pipelines) shell.log(` {${C.dimV}-fg}·{/} {${C.pink}-fg}${p}{/}`);
367
+ for (const p of pipelines) shell.log(` {${S.subtle}-fg}·{/} {${S.string}-fg}${p}{/}`);
371
368
  shell.log('');
372
369
  }
373
370
 
@@ -381,26 +378,26 @@ function groupAgentsByProvider(agents) {
381
378
  }
382
379
 
383
380
  async function cmdScan(root, shell) {
384
- shell.log(`{${C.dimV}-fg}Scanning ${root}…{/}`);
381
+ shell.log(`{${S.muted}-fg}Scanning ${root}…{/}`);
385
382
  const agents = await scanAgents(root);
386
383
  if (agents.length === 0) {
387
- shell.log(`{${C.peach}-fg}!{/} No agents found (no .md files with ## Config).`);
384
+ shell.log(`{${S.warning}-fg}!{/} No agents found (no .md files with ## Config).`);
388
385
  return;
389
386
  }
390
387
  shell.log(`{bold}Agents (${agents.length}){/}`);
391
388
  shell.log('');
392
389
  const groups = groupAgentsByProvider(agents);
393
390
  [...groups.entries()].forEach(([provider, providerAgents]) => {
394
- shell.log(` {${C.dimV}-fg}════════════════════════════════════════{/}`);
395
- shell.log(` {bold}${provider}{/} {${C.dimV}-fg}(${providerAgents.length}){/}`);
396
- shell.log(` {${C.dimV}-fg}════════════════════════════════════════{/}`);
391
+ shell.log(` {${S.muted}-fg}════════════════════════════════════════{/}`);
392
+ shell.log(` {bold}${provider}{/} {${S.muted}-fg}(${providerAgents.length}){/}`);
393
+ shell.log(` {${S.muted}-fg}════════════════════════════════════════{/}`);
397
394
  shell.log('');
398
395
 
399
396
  providerAgents.forEach((a, index) => {
400
- shell.log(` {${C.violet}-fg}{bold}${a.id}{/} {${C.dimV}-fg}${a.description || '(no description)'}{/}`);
401
- shell.log(` {${C.blue}-fg}{bold}source{/}: {${C.dimV}-fg}${a.source || 'repo'}{/}${a.permission_mode ? ` {${C.peach}-fg}{bold}permission{/}: {${C.dimV}-fg}${a.permission_mode}{/}` : ''}`);
402
- shell.log(` {${C.mint}-fg}{bold}in{/}: {${C.dimV}-fg}${a.inputs.join(', ') || '—'}{/} {${C.pink}-fg}{bold}out{/}: {${C.dimV}-fg}${a.outputs.join(', ') || '—'}{/}`);
403
- if (index < providerAgents.length - 1) shell.log(` {${C.dimV}-fg}──────────────────────────────────────{/}`);
397
+ shell.log(` {${S.accent}-fg}{bold}${a.id}{/} {${S.muted}-fg}${a.description || '(no description)'}{/}`);
398
+ shell.log(` {${S.keyword}-fg}{bold}source{/}: {${S.text}-fg}${a.source || 'repo'}{/}${a.permission_mode ? ` {${S.keyword}-fg}{bold}permission{/}: {${S.text}-fg}${a.permission_mode}{/}` : ''}`);
399
+ shell.log(` {${S.keyword}-fg}{bold}in{/}: {${S.text}-fg}${a.inputs.join(', ') || '—'}{/} {${S.keyword}-fg}{bold}out{/}: {${S.text}-fg}${a.outputs.join(', ') || '—'}{/}`);
400
+ if (index < providerAgents.length - 1) shell.log(` {${S.muted}-fg}──────────────────────────────────────{/}`);
404
401
  });
405
402
  shell.log('');
406
403
  });
@@ -408,7 +405,7 @@ async function cmdScan(root, shell) {
408
405
  await fs.mkdir(path.dirname(outPath), { recursive: true });
409
406
  await fs.writeFile(outPath, JSON.stringify({ scannedAt: new Date().toISOString(), root, agents }, null, 2));
410
407
  shell.log('');
411
- shell.log(`{${C.mint}-fg}✓{/} Cache → .singleton/agents.json`);
408
+ shell.log(`{${S.success}-fg}✓{/} Cache → {${S.string}-fg}.singleton/agents.json{/}`);
412
409
  }
413
410
 
414
411
  async function cmdNew(root, shell) {
@@ -429,12 +426,12 @@ function closeServer(server) {
429
426
 
430
427
  async function cmdServe(root, shell, serveState) {
431
428
  if (serveState.server) {
432
- shell.log(`{${C.peach}-fg}!{/} The server is already running on {${C.blue}-fg}${serveState.url}{/}.`);
429
+ shell.log(`{${S.warning}-fg}!{/} The server is already running on {${S.string}-fg}${serveState.url}{/}.`);
433
430
  return;
434
431
  }
435
432
  const { startServer } = await import('../../../server/src/index.js');
436
433
  const serverUrl = 'http://localhost:4317';
437
- shell.log('{#676498-fg}Starting server… (/stop to stop){/}');
434
+ shell.log(`{${S.muted}-fg}Starting server… (/stop to stop){/}`);
438
435
  shell.enableInput();
439
436
  const server = await startServer({
440
437
  port: 4317,
@@ -445,10 +442,10 @@ async function cmdServe(root, shell, serveState) {
445
442
  const url = urlMatch[0];
446
443
  const prefix = message.slice(0, urlMatch.index);
447
444
  const suffix = message.slice(urlMatch.index + url.length);
448
- shell.log(`{#FFFFFF-fg}{bold}${prefix}{/}{${C.blue}-fg}${url}{/}{${C.dimV}-fg}${suffix}{/}`);
445
+ shell.log(`{${S.text}-fg}{bold}${prefix}{/}{${S.string}-fg}${url}{/}{${S.muted}-fg}${suffix}{/}`);
449
446
  return;
450
447
  }
451
- shell.log(`{${C.dimV}-fg}${message}{/}`);
448
+ shell.log(`{${S.muted}-fg}${message}{/}`);
452
449
  },
453
450
  });
454
451
  serveState.server = server;
@@ -456,16 +453,16 @@ async function cmdServe(root, shell, serveState) {
456
453
  server.on('close', () => {
457
454
  const shouldLog = !serveState.suppressCloseLog;
458
455
  serveState.clear();
459
- if (shouldLog) shell.log(`{${C.dimV}-fg}Serve stopped.{/}`);
456
+ if (shouldLog) shell.log(`{${S.muted}-fg}Serve stopped.{/}`);
460
457
  });
461
458
  shell.setFooterCenter(
462
- `{${C.dimV}-fg}serve running{/} {${C.blue}-fg}${serverUrl}{/}`
459
+ `{${S.muted}-fg}serve running{/} {${S.string}-fg}${serverUrl}{/}`
463
460
  );
464
461
  }
465
462
 
466
463
  async function cmdStop(shell, serveState) {
467
464
  if (!serveState.server) {
468
- shell.log(`{${C.peach}-fg}!{/} No running server.`);
465
+ shell.log(`{${S.warning}-fg}!{/} No running server.`);
469
466
  return;
470
467
  }
471
468
  const url = serveState.url;
@@ -475,7 +472,7 @@ async function cmdStop(shell, serveState) {
475
472
  serveState.url = '';
476
473
  shell.setFooterCenter('');
477
474
  await closeServer(server);
478
- shell.log(`{${C.mint}-fg}✓{/} Serve stopped {${C.dimV}-fg}${url}{/}`);
475
+ shell.log(`{${S.success}-fg}✓{/} Serve stopped {${S.string}-fg}${url}{/}`);
479
476
  }
480
477
 
481
478
  async function cmdCommitLast(root, shell) {
@@ -483,7 +480,7 @@ async function cmdCommitLast(root, shell) {
483
480
  try {
484
481
  manifest = await loadLastRunManifest(root);
485
482
  } catch {
486
- shell.log(`{${C.peach}-fg}!{/} No usable latest run found in .singleton/runs/latest.`);
483
+ shell.log(`{${S.warning}-fg}!{/} No usable latest run found in .singleton/runs/latest.`);
487
484
  return;
488
485
  }
489
486
 
@@ -491,7 +488,7 @@ async function cmdCommitLast(root, shell) {
491
488
  try {
492
489
  securityConfig = await loadProjectSecurityConfig(root);
493
490
  } catch (err) {
494
- shell.log(`{${C.salmon}-fg}✕{/} ${err.message}`);
491
+ shell.log(`{${S.error}-fg}✕{/} ${err.message}`);
495
492
  return;
496
493
  }
497
494
 
@@ -505,9 +502,9 @@ async function cmdCommitLast(root, shell) {
505
502
  return true;
506
503
  });
507
504
  if (files.length === 0) {
508
- shell.log(`{${C.peach}-fg}!{/} The last run produced no deliverables to commit.`);
505
+ shell.log(`{${S.warning}-fg}!{/} The last run produced no deliverables to commit.`);
509
506
  if (excluded.length) {
510
- shell.log(`{${C.dimV}-fg}All deliverables were excluded by .singleton/security.json commit rules.{/}`);
507
+ shell.log(`{${S.muted}-fg}All deliverables were excluded by .singleton/security.json commit rules.{/}`);
511
508
  }
512
509
  return;
513
510
  }
@@ -515,27 +512,27 @@ async function cmdCommitLast(root, shell) {
515
512
  try {
516
513
  await runCommand('git', ['rev-parse', '--show-toplevel'], { cwd: root });
517
514
  } catch {
518
- shell.log(`{${C.salmon}-fg}✕{/} This project is not inside a Git repository.`);
515
+ shell.log(`{${S.error}-fg}✕{/} This project is not inside a Git repository.`);
519
516
  return;
520
517
  }
521
518
 
522
- shell.log(`{bold}Commit last preview{/} {${C.dimV}-fg}${manifest.pipeline || 'unknown pipeline'}{/}`);
519
+ shell.log(`{bold}Commit last preview{/} {${S.muted}-fg}${manifest.pipeline || 'unknown pipeline'}{/}`);
523
520
  shell.log('');
524
- shell.log(`{${C.blue}-fg}Files to stage{/}`);
525
- for (const file of files) shell.log(` {${C.dimV}-fg}·{/} ${file.path}`);
521
+ shell.log(`{${S.keyword}-fg}Files to stage{/}`);
522
+ for (const file of files) shell.log(` {${S.subtle}-fg}·{/} {${S.string}-fg}${file.path}{/}`);
526
523
  if (excluded.length) {
527
524
  shell.log('');
528
- shell.log(`{${C.peach}-fg}Excluded by security config{/}`);
529
- for (const { file, excludedBy } of excluded) shell.log(` {${C.dimV}-fg}·{/} ${file.path} {${C.dimV}-fg}(${excludedBy}){/}`);
525
+ shell.log(`{${S.warning}-fg}Excluded by security config{/}`);
526
+ for (const { file, excludedBy } of excluded) shell.log(` {${S.subtle}-fg}·{/} {${S.string}-fg}${file.path}{/} {${S.muted}-fg}(${excludedBy}){/}`);
530
527
  }
531
528
  shell.log('');
532
- shell.log(`{${C.dimV}-fg}.singleton artifacts are not committed by /commit-last.{/}`);
529
+ shell.log(`{${S.muted}-fg}.singleton artifacts are not committed by /commit-last.{/}`);
533
530
  shell.log('');
534
531
 
535
532
  if (securityConfig.commit.requireConfirmation) {
536
533
  const confirmation = (await shell.prompt('Stage and commit these files? (y/N)')).trim().toLowerCase();
537
534
  if (confirmation !== 'y' && confirmation !== 'yes') {
538
- shell.log(`{${C.peach}-fg}!{/} Commit cancelled.`);
535
+ shell.log(`{${S.warning}-fg}!{/} Commit cancelled.`);
539
536
  return;
540
537
  }
541
538
  }
@@ -547,5 +544,5 @@ async function cmdCommitLast(root, shell) {
547
544
  await runCommand('git', ['add', '--', ...relFiles], { cwd: root });
548
545
  await runCommand('git', ['commit', '-m', message], { cwd: root });
549
546
 
550
- shell.log(`{${C.mint}-fg}✓{/} Commit created: {${C.dimV}-fg}${message}{/}`);
547
+ shell.log(`{${S.success}-fg}✓{/} Commit created: {${S.string}-fg}${message}{/}`);
551
548
  }