murmur8 3.5.0 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,15 +4,49 @@ const path = require('path');
4
4
  const { execSync, spawn } = require('child_process');
5
5
  const fs = require('fs');
6
6
  const readline = require('readline');
7
+ const theme = require('./theme');
7
8
 
8
- const CONFIG_FILE = '.claude/parallel-config.json';
9
- const LOCK_FILE = '.claude/parallel.lock';
9
+ const CONFIG_FILE = '.claude/murm-config.json';
10
+ const LOCK_FILE = '.claude/murm.lock';
11
+ const QUEUE_FILE = '.claude/murm-queue.json';
12
+
13
+ // Legacy paths for migration
14
+ const LEGACY_CONFIG_FILE = '.claude/parallel-config.json';
15
+ const LEGACY_LOCK_FILE = '.claude/parallel.lock';
16
+ const LEGACY_QUEUE_FILE = '.claude/parallel-queue.json';
10
17
 
11
18
  // Track running processes for abort handling
12
19
  let runningProcesses = new Map();
13
20
  let isAborting = false;
14
21
 
15
- function getDefaultParallelConfig() {
22
+ /**
23
+ * Migrate a legacy file path to the new path.
24
+ * If the old file exists and the new one doesn't, rename it.
25
+ */
26
+ function migrateFile(oldPath, newPath) {
27
+ if (fs.existsSync(oldPath) && !fs.existsSync(newPath)) {
28
+ const dir = path.dirname(newPath);
29
+ if (!fs.existsSync(dir)) {
30
+ fs.mkdirSync(dir, { recursive: true });
31
+ }
32
+ fs.renameSync(oldPath, newPath);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Run all legacy path migrations.
38
+ * Called once per process on first config/queue/lock access.
39
+ */
40
+ let migrationDone = false;
41
+ function ensureMigrated() {
42
+ if (migrationDone) return;
43
+ migrationDone = true;
44
+ migrateFile(LEGACY_CONFIG_FILE, CONFIG_FILE);
45
+ migrateFile(LEGACY_LOCK_FILE, LOCK_FILE);
46
+ migrateFile(LEGACY_QUEUE_FILE, QUEUE_FILE);
47
+ }
48
+
49
+ function getDefaultMurmConfig() {
16
50
  return {
17
51
  maxConcurrency: 3,
18
52
  maxFeatures: 10,
@@ -22,23 +56,29 @@ function getDefaultParallelConfig() {
22
56
  skill: '/implement-feature',
23
57
  skillFlags: '--no-commit',
24
58
  worktreeDir: '.claude/worktrees',
25
- queueFile: '.claude/parallel-queue.json'
59
+ queueFile: QUEUE_FILE
26
60
  };
27
61
  }
28
62
 
29
- function readParallelConfig() {
63
+ function readMurmConfig() {
64
+ ensureMigrated();
30
65
  if (!fs.existsSync(CONFIG_FILE)) {
31
- return getDefaultParallelConfig();
66
+ return getDefaultMurmConfig();
32
67
  }
33
68
  try {
34
69
  const content = fs.readFileSync(CONFIG_FILE, 'utf8');
35
- return { ...getDefaultParallelConfig(), ...JSON.parse(content) };
70
+ const parsed = JSON.parse(content);
71
+ // Migrate legacy queueFile value in config
72
+ if (parsed.queueFile === LEGACY_QUEUE_FILE) {
73
+ parsed.queueFile = QUEUE_FILE;
74
+ }
75
+ return { ...getDefaultMurmConfig(), ...parsed };
36
76
  } catch {
37
- return getDefaultParallelConfig();
77
+ return getDefaultMurmConfig();
38
78
  }
39
79
  }
40
80
 
41
- function writeParallelConfig(config) {
81
+ function writeMurmConfig(config) {
42
82
  const dir = path.dirname(CONFIG_FILE);
43
83
  if (!fs.existsSync(dir)) {
44
84
  fs.mkdirSync(dir, { recursive: true });
@@ -47,13 +87,11 @@ function writeParallelConfig(config) {
47
87
  }
48
88
 
49
89
  function getQueueFile() {
50
- return readParallelConfig().queueFile;
90
+ return readMurmConfig().queueFile;
51
91
  }
52
92
 
53
- const QUEUE_FILE = '.claude/parallel-queue.json'; // Legacy reference
54
-
55
93
  function buildWorktreePath(slug, config = null) {
56
- const cfg = config || readParallelConfig();
94
+ const cfg = config || readMurmConfig();
57
95
  return `${cfg.worktreeDir}/feat-${slug}`;
58
96
  }
59
97
 
@@ -62,7 +100,7 @@ function buildBranchName(slug) {
62
100
  }
63
101
 
64
102
  function getDefaultConfig() {
65
- const cfg = readParallelConfig();
103
+ const cfg = readMurmConfig();
66
104
  return { maxConcurrency: cfg.maxConcurrency };
67
105
  }
68
106
 
@@ -71,7 +109,7 @@ function getQueuePath(worktreePath) {
71
109
  }
72
110
 
73
111
  function shouldCleanupWorktree(state) {
74
- return state.status === 'parallel_complete' || state.status === 'aborted';
112
+ return state.status === 'murm_complete' || state.status === 'aborted';
75
113
  }
76
114
 
77
115
  function validatePreFlight({ isGitRepo, isDirty, gitVersion }) {
@@ -131,7 +169,7 @@ function promoteFromQueue(state) {
131
169
  }
132
170
 
133
171
  function buildPipelineCommand(slug, worktreePath, config = null) {
134
- const cfg = config || readParallelConfig();
172
+ const cfg = config || readMurmConfig();
135
173
  const flags = cfg.skillFlags ? ` ${cfg.skillFlags}` : '';
136
174
  return `${cfg.cli} --cwd ${worktreePath} ${cfg.skill} "${slug}"${flags}`;
137
175
  }
@@ -161,12 +199,12 @@ function orderByCompletion(features) {
161
199
  }
162
200
 
163
201
  const VALID_TRANSITIONS = {
164
- parallel_queued: ['worktree_created', 'aborted'],
165
- worktree_created: ['parallel_running', 'parallel_failed', 'aborted'],
166
- parallel_running: ['merge_pending', 'parallel_failed', 'aborted'],
167
- merge_pending: ['parallel_complete', 'merge_conflict', 'aborted'],
168
- parallel_failed: [],
169
- parallel_complete: [],
202
+ murm_queued: ['worktree_created', 'aborted'],
203
+ worktree_created: ['murm_running', 'murm_failed', 'aborted'],
204
+ murm_running: ['merge_pending', 'murm_failed', 'aborted'],
205
+ merge_pending: ['murm_complete', 'merge_conflict', 'aborted'],
206
+ murm_failed: [],
207
+ murm_complete: [],
170
208
  merge_conflict: [],
171
209
  aborted: []
172
210
  };
@@ -183,23 +221,23 @@ function formatStatus(states) {
183
221
  }
184
222
 
185
223
  function formatFeatureStatus(state) {
186
- const statusDisplay = state.status.replace('parallel_', '');
224
+ const statusDisplay = state.status.replace('murm_', '');
187
225
  const stage = state.stage ? ` (${state.stage})` : '';
188
226
  return `${state.slug}: ${statusDisplay}${stage}`;
189
227
  }
190
228
 
191
229
  function summarizeFinal(results) {
192
230
  return {
193
- completed: results.filter(r => r.status === 'parallel_complete').length,
194
- failed: results.filter(r => r.status === 'parallel_failed').length,
231
+ completed: results.filter(r => r.status === 'murm_complete').length,
232
+ failed: results.filter(r => r.status === 'murm_failed').length,
195
233
  conflicts: results.filter(r => r.status === 'merge_conflict').length
196
234
  };
197
235
  }
198
236
 
199
237
  function aggregateResults(results) {
200
238
  return {
201
- completed: results.filter(r => r.status === 'parallel_complete').length,
202
- failed: results.filter(r => r.status === 'parallel_failed').length,
239
+ completed: results.filter(r => r.status === 'murm_complete').length,
240
+ failed: results.filter(r => r.status === 'murm_failed').length,
203
241
  total: results.length
204
242
  };
205
243
  }
@@ -296,12 +334,12 @@ function promptConfirm(message) {
296
334
  }
297
335
 
298
336
  function buildConfirmMessage(slugs, config) {
299
- const parallelCfg = readParallelConfig();
337
+ const murmCfg = readMurmConfig();
300
338
  const { active, queued } = splitByLimit(slugs, config.maxConcurrency);
301
339
 
302
340
  let msg = '\nThis will:\n';
303
- msg += ` • Create ${slugs.length} git worktree(s) in ${parallelCfg.worktreeDir}/\n`;
304
- msg += ` • Start ${active.length} parallel pipeline(s) (max concurrent: ${config.maxConcurrency})\n`;
341
+ msg += ` • Create ${slugs.length} git worktree(s) in ${murmCfg.worktreeDir}/\n`;
342
+ msg += ` • Start ${active.length} murmuration pipeline(s) (max concurrent: ${config.maxConcurrency})\n`;
305
343
  if (queued.length > 0) {
306
344
  msg += ` • Queue ${queued.length} additional feature(s)\n`;
307
345
  }
@@ -313,6 +351,7 @@ function buildConfirmMessage(slugs, config) {
313
351
  // --- Lock File ---
314
352
 
315
353
  function acquireLock(slugs) {
354
+ ensureMigrated();
316
355
  if (fs.existsSync(LOCK_FILE)) {
317
356
  const lock = JSON.parse(fs.readFileSync(LOCK_FILE, 'utf8'));
318
357
 
@@ -357,6 +396,7 @@ function releaseLock() {
357
396
  }
358
397
 
359
398
  function getLockInfo() {
399
+ ensureMigrated();
360
400
  if (!fs.existsSync(LOCK_FILE)) {
361
401
  return null;
362
402
  }
@@ -370,7 +410,7 @@ function getLockInfo() {
370
410
  // --- Feature Limit ---
371
411
 
372
412
  function validateFeatureLimit(slugs) {
373
- const config = readParallelConfig();
413
+ const config = readMurmConfig();
374
414
  if (slugs.length > config.maxFeatures) {
375
415
  return {
376
416
  valid: false,
@@ -385,7 +425,7 @@ function validateFeatureLimit(slugs) {
385
425
  // --- Disk Space Check ---
386
426
 
387
427
  function checkDiskSpace() {
388
- const config = readParallelConfig();
428
+ const config = readMurmConfig();
389
429
  try {
390
430
  // Get available space on current filesystem
391
431
  const output = execSync('df -m . | tail -1', { encoding: 'utf8' });
@@ -419,8 +459,8 @@ function validateDiskSpace() {
419
459
  // --- Logging ---
420
460
 
421
461
  function createLogStream(slug, config) {
422
- const parallelCfg = config || readParallelConfig();
423
- const worktreePath = buildWorktreePath(slug, parallelCfg);
462
+ const murmCfg = config || readMurmConfig();
463
+ const worktreePath = buildWorktreePath(slug, murmCfg);
424
464
  const logPath = path.join(worktreePath, 'pipeline.log');
425
465
 
426
466
  // Ensure directory exists
@@ -610,7 +650,7 @@ function estimateScope(featureValidations) {
610
650
  });
611
651
  }
612
652
 
613
- function validateParallelBatch(slugs) {
653
+ function validateMurmBatch(slugs) {
614
654
  const featureValidations = slugs.map(validateFeatureSpec);
615
655
  const fileOverlaps = detectFileOverlap(featureValidations);
616
656
  const dependencies = detectDependencies(featureValidations);
@@ -744,7 +784,7 @@ function withTimeout(promise, timeoutMs, slug) {
744
784
  }
745
785
 
746
786
  function getTimeoutMs() {
747
- const config = readParallelConfig();
787
+ const config = readMurmConfig();
748
788
  return config.timeout * 60 * 1000; // Convert minutes to ms
749
789
  }
750
790
 
@@ -807,29 +847,20 @@ function getDetailedStatus() {
807
847
  });
808
848
 
809
849
  return {
810
- active: features.some(f => f.status === 'parallel_running'),
850
+ active: features.some(f => f.status === 'murm_running'),
811
851
  features
812
852
  };
813
853
  }
814
854
 
815
855
  function formatDetailedStatus(details) {
816
856
  if (!details.active && details.features.length === 0) {
817
- return 'No parallel pipelines active.';
857
+ return 'No murmuration pipelines active.';
818
858
  }
819
859
 
820
- let output = 'Parallel Pipeline Status\n\n';
860
+ let output = 'Murmuration Status\n\n';
821
861
 
822
862
  for (const f of details.features) {
823
- const statusIcon = {
824
- 'parallel_queued': '⏳',
825
- 'worktree_created': '📁',
826
- 'parallel_running': '🔄',
827
- 'merge_pending': '🔀',
828
- 'parallel_complete': '✅',
829
- 'parallel_failed': '❌',
830
- 'merge_conflict': '⚠️',
831
- 'aborted': '🛑'
832
- }[f.status] || '❓';
863
+ const statusIcon = theme.STATUS_ICONS[f.status] || '?';
833
864
 
834
865
  const elapsed = f.elapsedSeconds > 0
835
866
  ? ` (${Math.floor(f.elapsedSeconds / 60)}m ${f.elapsedSeconds % 60}s)`
@@ -837,8 +868,8 @@ function formatDetailedStatus(details) {
837
868
 
838
869
  output += `${statusIcon} ${f.slug}${elapsed}\n`;
839
870
 
840
- if (f.status === 'parallel_running') {
841
- const bar = progressBar(f.percent);
871
+ if (f.status === 'murm_running') {
872
+ const bar = theme.progressBar(f.percent);
842
873
  output += ` ${bar} ${f.percent}% - ${f.stage}\n`;
843
874
  }
844
875
  }
@@ -847,9 +878,7 @@ function formatDetailedStatus(details) {
847
878
  }
848
879
 
849
880
  function progressBar(percent, width = 20) {
850
- const filled = Math.round((percent / 100) * width);
851
- const empty = width - filled;
852
- return '[' + '█'.repeat(filled) + '░'.repeat(empty) + ']';
881
+ return theme.progressBar(percent, width);
853
882
  }
854
883
 
855
884
  // --- Abort Handling ---
@@ -859,7 +888,7 @@ function setupAbortHandler(queue) {
859
888
  if (isAborting) return;
860
889
  isAborting = true;
861
890
 
862
- console.log('\n\nReceived interrupt signal. Stopping pipelines...\n');
891
+ console.log(`\n\n${theme.MESSAGES.flockScattering}\n`);
863
892
 
864
893
  // Kill all running processes
865
894
  for (const [slug, procInfo] of runningProcesses) {
@@ -874,7 +903,7 @@ function setupAbortHandler(queue) {
874
903
  // Update queue state
875
904
  if (queue && queue.features) {
876
905
  queue.features.forEach(f => {
877
- if (f.status === 'parallel_running' || f.status === 'worktree_created') {
906
+ if (f.status === 'murm_running' || f.status === 'worktree_created') {
878
907
  f.status = 'aborted';
879
908
  }
880
909
  });
@@ -884,7 +913,7 @@ function setupAbortHandler(queue) {
884
913
  releaseLock();
885
914
 
886
915
  console.log('\nAborted. Worktrees preserved for debugging.');
887
- console.log("Run 'murmur8 parallel cleanup' to remove.\n");
916
+ console.log("Run 'murmur8 murm cleanup' to remove.\n");
888
917
 
889
918
  process.exit(130); // Standard exit code for Ctrl+C
890
919
  };
@@ -895,16 +924,16 @@ function setupAbortHandler(queue) {
895
924
  return handler;
896
925
  }
897
926
 
898
- async function abortParallel(options = {}) {
927
+ async function abortMurm(options = {}) {
899
928
  const lock = getLockInfo();
900
929
  const queue = loadQueue();
901
930
 
902
931
  if (!lock && (!queue.features || queue.features.length === 0)) {
903
- console.log('No parallel pipelines are currently running.');
932
+ console.log('No murmuration pipelines are currently running.');
904
933
  return { success: true };
905
934
  }
906
935
 
907
- console.log('Stopping parallel pipelines...\n');
936
+ console.log('Stopping murmuration pipelines...\n');
908
937
 
909
938
  // Try to kill the main process if we have lock info
910
939
  if (lock && lock.pid !== process.pid) {
@@ -920,7 +949,7 @@ async function abortParallel(options = {}) {
920
949
  let abortedCount = 0;
921
950
  if (queue.features) {
922
951
  queue.features.forEach(f => {
923
- if (f.status === 'parallel_running' || f.status === 'worktree_created' || f.status === 'parallel_queued') {
952
+ if (f.status === 'murm_running' || f.status === 'worktree_created' || f.status === 'murm_queued') {
924
953
  f.status = 'aborted';
925
954
  abortedCount++;
926
955
  console.log(`${f.slug}: Marked as aborted`);
@@ -945,7 +974,7 @@ async function abortParallel(options = {}) {
945
974
  worktrees.forEach(w => console.log(` • ${w}`));
946
975
  }
947
976
  }
948
- console.log("\nTo clean up: murmur8 parallel cleanup");
977
+ console.log("\nTo clean up: murmur8 murm cleanup");
949
978
  }
950
979
 
951
980
  return { success: true, abortedCount };
@@ -970,7 +999,7 @@ function saveQueue(queue) {
970
999
  // --- Pipeline Execution ---
971
1000
 
972
1001
  function runPipelineInWorktree(slug, worktreePath, config = null, options = {}) {
973
- const cfg = config || readParallelConfig();
1002
+ const cfg = config || readMurmConfig();
974
1003
  const cliParts = cfg.cli.split(' ');
975
1004
  const skillParts = cfg.skill.split(' ');
976
1005
  const flagParts = cfg.skillFlags ? cfg.skillFlags.split(' ') : [];
@@ -1027,7 +1056,7 @@ function runPipelineInWorktree(slug, worktreePath, config = null, options = {})
1027
1056
  // --- Main Orchestration ---
1028
1057
 
1029
1058
  function dryRun(slugs, config, baseBranch, gitStatus, validation, batchValidation = null) {
1030
- const parallelCfg = readParallelConfig();
1059
+ const murmCfg = readMurmConfig();
1031
1060
  const { active, queued } = splitByLimit(slugs, config.maxConcurrency);
1032
1061
 
1033
1062
  console.log('\n=== DRY RUN MODE ===\n');
@@ -1042,28 +1071,28 @@ function dryRun(slugs, config, baseBranch, gitStatus, validation, batchValidatio
1042
1071
  validation.errors.forEach(e => console.log(` - ${e}`));
1043
1072
  }
1044
1073
 
1045
- // Show batch validation results (already printed in runParallel if issues found)
1074
+ // Show batch validation results (already printed in runMurm if issues found)
1046
1075
  if (batchValidation && !batchValidation.valid) {
1047
1076
  console.log(`\n⚠️ WARNING: Feature validation failed. Real execution would abort.`);
1048
1077
  }
1049
1078
 
1050
1079
  console.log(`\nConfiguration:`);
1051
1080
  console.log(` Max concurrency: ${config.maxConcurrency}`);
1052
- console.log(` Max features: ${parallelCfg.maxFeatures}`);
1053
- console.log(` Timeout: ${parallelCfg.timeout} min per pipeline`);
1054
- console.log(` Min disk space: ${parallelCfg.minDiskSpaceMB} MB`);
1055
- console.log(` CLI: ${parallelCfg.cli}`);
1056
- console.log(` Skill: ${parallelCfg.skill}`);
1057
- console.log(` Flags: ${parallelCfg.skillFlags || '(none)'}`);
1058
- console.log(` Worktree dir: ${parallelCfg.worktreeDir}`);
1081
+ console.log(` Max features: ${murmCfg.maxFeatures}`);
1082
+ console.log(` Timeout: ${murmCfg.timeout} min per pipeline`);
1083
+ console.log(` Min disk space: ${murmCfg.minDiskSpaceMB} MB`);
1084
+ console.log(` CLI: ${murmCfg.cli}`);
1085
+ console.log(` Skill: ${murmCfg.skill}`);
1086
+ console.log(` Flags: ${murmCfg.skillFlags || '(none)'}`);
1087
+ console.log(` Worktree dir: ${murmCfg.worktreeDir}`);
1059
1088
  console.log(` Total features: ${slugs.length}`);
1060
1089
 
1061
1090
  console.log(`\nInitial batch (${active.length} features):`);
1062
1091
  active.forEach(slug => {
1063
1092
  console.log(` → ${slug}`);
1064
- console.log(` Worktree: ${buildWorktreePath(slug, parallelCfg)}`);
1093
+ console.log(` Worktree: ${buildWorktreePath(slug, murmCfg)}`);
1065
1094
  console.log(` Branch: ${buildBranchName(slug)}`);
1066
- console.log(` Command: ${buildPipelineCommand(slug, buildWorktreePath(slug, parallelCfg), parallelCfg)}`);
1095
+ console.log(` Command: ${buildPipelineCommand(slug, buildWorktreePath(slug, murmCfg), murmCfg)}`);
1067
1096
  });
1068
1097
 
1069
1098
  if (queued.length > 0) {
@@ -1075,7 +1104,7 @@ function dryRun(slugs, config, baseBranch, gitStatus, validation, batchValidatio
1075
1104
 
1076
1105
  console.log(`\nExecution plan:`);
1077
1106
  console.log(` 1. Create ${active.length} git worktrees`);
1078
- console.log(` 2. Spawn ${active.length} parallel pipeline processes`);
1107
+ console.log(` 2. Spawn ${active.length} murmuration pipeline processes`);
1079
1108
  console.log(` 3. As each completes: merge to ${baseBranch}, cleanup worktree`);
1080
1109
  if (queued.length > 0) {
1081
1110
  console.log(` 4. Promote queued features as slots free`);
@@ -1088,7 +1117,7 @@ function dryRun(slugs, config, baseBranch, gitStatus, validation, batchValidatio
1088
1117
  return { success: true, dryRun: true };
1089
1118
  }
1090
1119
 
1091
- async function runParallel(slugs, options = {}) {
1120
+ async function runMurm(slugs, options = {}) {
1092
1121
  const config = { ...getDefaultConfig(), ...options };
1093
1122
  const baseBranch = getCurrentBranch();
1094
1123
 
@@ -1099,7 +1128,7 @@ async function runParallel(slugs, options = {}) {
1099
1128
  // Batch validation (unless skipped)
1100
1129
  let batchValidation = null;
1101
1130
  if (!options.skipPreflight) {
1102
- batchValidation = validateParallelBatch(slugs);
1131
+ batchValidation = validateMurmBatch(slugs);
1103
1132
 
1104
1133
  // Show pre-flight results in dry-run or if there are issues
1105
1134
  if (options.dryRun || !batchValidation.valid || batchValidation.fileOverlaps.length > 0 || batchValidation.dependencies.length > 0) {
@@ -1141,7 +1170,7 @@ async function runParallel(slugs, options = {}) {
1141
1170
  const limitCheck = validateFeatureLimit(slugs);
1142
1171
  if (!limitCheck.valid) {
1143
1172
  console.error(`\nError: ${limitCheck.error}`);
1144
- console.error(`\nTo increase limit: murmur8 parallel-config set maxFeatures <N>\n`);
1173
+ console.error(`\nTo increase limit: murmur8 murm-config set maxFeatures <N>\n`);
1145
1174
  return { success: false, error: 'feature-limit-exceeded' };
1146
1175
  }
1147
1176
 
@@ -1161,14 +1190,14 @@ async function runParallel(slugs, options = {}) {
1161
1190
  const lockResult = acquireLock(slugs);
1162
1191
  if (!lockResult.acquired) {
1163
1192
  const lock = lockResult.existingLock;
1164
- console.error('\nError: Another parallel execution is in progress');
1193
+ console.error('\nError: Another murmuration execution is in progress');
1165
1194
  console.error(` PID: ${lock.pid}`);
1166
1195
  console.error(` Started: ${lock.startedAt}`);
1167
1196
  console.error(` Features: ${lock.features.join(', ')}`);
1168
1197
  console.error('\nOptions:');
1169
1198
  console.error(' • Wait for it to complete');
1170
- console.error(' • Run: murmur8 parallel status');
1171
- console.error(' • Force override: murmur8 parallel ... --force\n');
1199
+ console.error(' • Run: murmur8 murm status');
1200
+ console.error(' • Force override: murmur8 murm ... --force\n');
1172
1201
  return { success: false, error: 'locked' };
1173
1202
  }
1174
1203
  } else {
@@ -1191,7 +1220,9 @@ async function runParallel(slugs, options = {}) {
1191
1220
  }
1192
1221
  }
1193
1222
 
1194
- console.log(`\nStarting parallel pipelines for ${slugs.length} features`);
1223
+ const useColor = process.stdout.isTTY || false;
1224
+ console.log(theme.banner(useColor));
1225
+ console.log(theme.MESSAGES.startingFlock(slugs.length));
1195
1226
  console.log(`Base branch: ${baseBranch}`);
1196
1227
  console.log(`Max concurrency: ${config.maxConcurrency}\n`);
1197
1228
 
@@ -1199,7 +1230,7 @@ async function runParallel(slugs, options = {}) {
1199
1230
  const queue = {
1200
1231
  features: slugs.map(slug => ({
1201
1232
  slug,
1202
- status: 'parallel_queued',
1233
+ status: 'murm_queued',
1203
1234
  worktreePath: null,
1204
1235
  branchName: null,
1205
1236
  startedAt: null,
@@ -1246,30 +1277,30 @@ async function runParallel(slugs, options = {}) {
1246
1277
 
1247
1278
  if (result.success) {
1248
1279
  feature.status = 'merge_pending';
1249
- console.log(`[${timestamp}] ${result.slug}: Completed ✓`);
1280
+ console.log(`[${timestamp}] ${result.slug}: ${theme.MESSAGES.landed} \u2713`);
1250
1281
 
1251
1282
  // Attempt merge
1252
1283
  const mergeResult = mergeBranch(result.slug);
1253
1284
  if (mergeResult.success) {
1254
- feature.status = 'parallel_complete';
1255
- console.log(`[${timestamp}] ${result.slug}: Merged to ${baseBranch} ✓`);
1285
+ feature.status = 'murm_complete';
1286
+ console.log(`[${timestamp}] ${result.slug}: ${theme.MESSAGES.mergedAndLanded} \u2713`);
1256
1287
  removeWorktree(result.slug);
1257
1288
  } else if (mergeResult.conflict) {
1258
1289
  feature.status = 'merge_conflict';
1259
1290
  feature.conflictDetails = mergeResult.output;
1260
- console.log(`[${timestamp}] ${result.slug}: Merge conflict (branch preserved)`);
1291
+ console.log(`[${timestamp}] ${result.slug}: ${theme.MESSAGES.turbulence} \u26a0 (branch preserved)`);
1261
1292
  execSync('git merge --abort', { stdio: 'pipe' });
1262
1293
  } else {
1263
- feature.status = 'parallel_failed';
1264
- console.log(`[${timestamp}] ${result.slug}: Merge failed ✗`);
1294
+ feature.status = 'murm_failed';
1295
+ console.log(`[${timestamp}] ${result.slug}: ${theme.MESSAGES.lostFormation} \u2717`);
1265
1296
  }
1266
1297
  } else {
1267
- feature.status = 'parallel_failed';
1298
+ feature.status = 'murm_failed';
1268
1299
  if (result.timedOut) {
1269
- console.log(`[${timestamp}] ${result.slug}: Timed out (see log: ${feature.logPath})`);
1300
+ console.log(`[${timestamp}] ${result.slug}: ${theme.MESSAGES.timedOut} \u23f1 (see log: ${feature.logPath})`);
1270
1301
  feature.timedOut = true;
1271
1302
  } else {
1272
- console.log(`[${timestamp}] ${result.slug}: Failed (see log: ${feature.logPath})`);
1303
+ console.log(`[${timestamp}] ${result.slug}: ${theme.MESSAGES.lostFormation} \u2717 (see log: ${feature.logPath})`);
1273
1304
  }
1274
1305
  // Preserve worktree for debugging
1275
1306
  }
@@ -1287,22 +1318,22 @@ async function runParallel(slugs, options = {}) {
1287
1318
 
1288
1319
  // Final summary
1289
1320
  const summary = summarizeFinal(queue.features);
1290
- console.log('\n--- Parallel Execution Complete ---');
1321
+ console.log(`\n${theme.MESSAGES.murmurationComplete}`);
1291
1322
  console.log(`Completed: ${summary.completed}`);
1292
1323
  console.log(`Failed: ${summary.failed}`);
1293
1324
  console.log(`Conflicts: ${summary.conflicts}`);
1294
1325
 
1295
1326
  if (summary.conflicts > 0) {
1296
- console.log('\nFeatures with conflicts (branches preserved):');
1327
+ console.log(`\n${theme.MESSAGES.conflictsHeader}`);
1297
1328
  queue.features
1298
1329
  .filter(f => f.status === 'merge_conflict')
1299
1330
  .forEach(f => console.log(` - ${f.branchName}`));
1300
1331
  }
1301
1332
 
1302
1333
  if (summary.failed > 0) {
1303
- console.log('\nFailed features (worktrees preserved for debugging):');
1334
+ console.log(`\n${theme.MESSAGES.failuresHeader}`);
1304
1335
  queue.features
1305
- .filter(f => f.status === 'parallel_failed')
1336
+ .filter(f => f.status === 'murm_failed')
1306
1337
  .forEach(f => {
1307
1338
  console.log(` - ${f.worktreePath}`);
1308
1339
  if (f.logPath) {
@@ -1320,9 +1351,9 @@ async function runParallel(slugs, options = {}) {
1320
1351
 
1321
1352
  async function startFeature(slug, queue, running, options = {}) {
1322
1353
  const feature = queue.features.find(f => f.slug === slug);
1323
- const parallelCfg = readParallelConfig();
1354
+ const murmCfg = readMurmConfig();
1324
1355
 
1325
- console.log(`[${new Date().toISOString().slice(11, 19)}] ${slug}: Creating worktree...`);
1356
+ console.log(`[${new Date().toISOString().slice(11, 19)}] ${slug}: ${theme.MESSAGES.takingFlight}`);
1326
1357
  const { worktreePath, branchName } = createWorktree(slug);
1327
1358
 
1328
1359
  feature.worktreePath = worktreePath;
@@ -1338,27 +1369,27 @@ async function startFeature(slug, queue, running, options = {}) {
1338
1369
  const timeoutMs = getTimeoutMs();
1339
1370
  const timeoutMin = timeoutMs / 60000;
1340
1371
  console.log(`[${new Date().toISOString().slice(11, 19)}] ${slug}: Started (log: ${feature.logPath}, timeout: ${timeoutMin}min)`);
1341
- feature.status = 'parallel_running';
1372
+ feature.status = 'murm_running';
1342
1373
  saveQueue(queue);
1343
1374
 
1344
- const pipelinePromise = runPipelineInWorktree(slug, worktreePath, parallelCfg, options);
1375
+ const pipelinePromise = runPipelineInWorktree(slug, worktreePath, murmCfg, options);
1345
1376
  const promise = withTimeout(pipelinePromise, timeoutMs, slug);
1346
1377
  running.set(slug, promise);
1347
1378
  }
1348
1379
 
1349
1380
  // --- Rollback ---
1350
1381
 
1351
- async function rollbackParallel(options = {}) {
1382
+ async function rollbackMurm(options = {}) {
1352
1383
  const queue = loadQueue();
1353
1384
 
1354
1385
  if (!queue.features || queue.features.length === 0) {
1355
- console.log('No parallel run to rollback.');
1386
+ console.log('No murmuration run to rollback.');
1356
1387
  return { success: true, rolledBack: 0 };
1357
1388
  }
1358
1389
 
1359
- const completedFeatures = queue.features.filter(f => f.status === 'parallel_complete');
1390
+ const completedFeatures = queue.features.filter(f => f.status === 'murm_complete');
1360
1391
  const failedFeatures = queue.features.filter(f =>
1361
- f.status === 'parallel_failed' || f.status === 'merge_conflict'
1392
+ f.status === 'murm_failed' || f.status === 'merge_conflict'
1362
1393
  );
1363
1394
 
1364
1395
  if (completedFeatures.length === 0 && failedFeatures.length === 0) {
@@ -1366,7 +1397,7 @@ async function rollbackParallel(options = {}) {
1366
1397
  return { success: true, rolledBack: 0 };
1367
1398
  }
1368
1399
 
1369
- console.log('\nParallel Run Rollback\n');
1400
+ console.log('\nMurmuration Rollback\n');
1370
1401
 
1371
1402
  if (options.dryRun) {
1372
1403
  console.log('DRY RUN - No changes will be made\n');
@@ -1389,7 +1420,7 @@ async function rollbackParallel(options = {}) {
1389
1420
  if (logOutput) {
1390
1421
  const commitHash = logOutput.split(' ')[0];
1391
1422
  execSync(`git revert --no-commit ${commitHash}`, { stdio: 'pipe' });
1392
- execSync(`git commit -m "Revert: ${f.slug} (parallel rollback)"`, { stdio: 'pipe' });
1423
+ execSync(`git commit -m "Revert: ${f.slug} (murmuration rollback)"`, { stdio: 'pipe' });
1393
1424
  console.log(` ✓ Reverted commit ${commitHash}`);
1394
1425
  rolledBack++;
1395
1426
  } else {
@@ -1464,9 +1495,15 @@ module.exports = {
1464
1495
  // Configuration
1465
1496
  CONFIG_FILE,
1466
1497
  LOCK_FILE,
1467
- getDefaultParallelConfig,
1468
- readParallelConfig,
1469
- writeParallelConfig,
1498
+ QUEUE_FILE,
1499
+ LEGACY_CONFIG_FILE,
1500
+ LEGACY_LOCK_FILE,
1501
+ LEGACY_QUEUE_FILE,
1502
+ migrateFile,
1503
+ ensureMigrated,
1504
+ getDefaultMurmConfig,
1505
+ readMurmConfig,
1506
+ writeMurmConfig,
1470
1507
  getQueueFile,
1471
1508
  // Utility functions
1472
1509
  buildWorktreePath,
@@ -1510,7 +1547,7 @@ module.exports = {
1510
1547
  detectFileOverlap,
1511
1548
  detectDependencies,
1512
1549
  estimateScope,
1513
- validateParallelBatch,
1550
+ validateMurmBatch,
1514
1551
  formatPreflightResults,
1515
1552
  // Timeout
1516
1553
  withTimeout,
@@ -1521,10 +1558,10 @@ module.exports = {
1521
1558
  formatDetailedStatus,
1522
1559
  progressBar,
1523
1560
  // Abort handling
1524
- abortParallel,
1561
+ abortMurm,
1525
1562
  setupAbortHandler,
1526
1563
  // Rollback
1527
- rollbackParallel,
1564
+ rollbackMurm,
1528
1565
  // Git operations
1529
1566
  checkGitStatus,
1530
1567
  createWorktree,
@@ -1534,11 +1571,10 @@ module.exports = {
1534
1571
  // Queue management
1535
1572
  loadQueue,
1536
1573
  saveQueue,
1537
- QUEUE_FILE,
1538
1574
  // Execution
1539
1575
  dryRun,
1540
1576
  runPipelineInWorktree,
1541
- runParallel,
1577
+ runMurm,
1542
1578
  startFeature,
1543
1579
  cleanupWorktrees
1544
1580
  };