@yemi33/minions 0.1.389 → 0.1.390

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/CHANGELOG.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.389 (2026-04-06)
3
+ ## 0.1.390 (2026-04-06)
4
4
 
5
5
  ### Features
6
+ - Replace magic strings in pipeline.js with STAGE_TYPE/PIPELINE_STATUS/MEETING_STATUS constants (#244)
6
7
  - Fix spawn-agent.js direct proc.kill() — use cross-platform helpers (#243)
7
8
 
8
9
  ## 0.1.388 (2026-04-06)
@@ -7,7 +7,7 @@
7
7
  const fs = require('fs');
8
8
  const path = require('path');
9
9
  const shared = require('./shared');
10
- const { safeJson, safeWrite, safeRead, safeReadDir, uid, log, ts, dateStamp, mutateJsonFileLocked, WI_STATUS, WORK_TYPE, PLAN_STATUS, PR_STATUS, PIPELINE_STATUS, MEETING_STATUS } = shared;
10
+ const { safeJson, safeWrite, safeRead, safeReadDir, uid, log, ts, dateStamp, mutateJsonFileLocked, WI_STATUS, WORK_TYPE, PLAN_STATUS, PR_STATUS, PIPELINE_STATUS, STAGE_TYPE, MEETING_STATUS } = shared;
11
11
  const { parseCronExpr, shouldRunNow } = require('./scheduler');
12
12
 
13
13
  const PIPELINES_DIR = path.join(__dirname, '..', 'pipelines');
@@ -130,22 +130,22 @@ async function executeStage(stage, run, pipeline, config) {
130
130
  const stageState = run.stages[stage.id];
131
131
 
132
132
  switch (resolved.type) {
133
- case 'task':
133
+ case STAGE_TYPE.TASK:
134
134
  return executeTaskStage(resolved, stageState, run, config);
135
- case 'meeting':
135
+ case STAGE_TYPE.MEETING:
136
136
  return executeMeetingStage(resolved, stageState, run, config);
137
- case 'plan':
137
+ case STAGE_TYPE.PLAN:
138
138
  return executePlanStage(resolved, stageState, run, config);
139
- case 'api':
139
+ case STAGE_TYPE.API:
140
140
  return executeApiStage(resolved, stageState, run);
141
- case 'merge-prs':
141
+ case STAGE_TYPE.MERGE_PRS:
142
142
  return executeMergePrsStage(resolved, stageState, run, config);
143
- case 'schedule':
143
+ case STAGE_TYPE.SCHEDULE:
144
144
  return executeScheduleStage(resolved, stageState, config);
145
- case 'wait':
145
+ case STAGE_TYPE.WAIT:
146
146
  // wait stages just sit in waiting-human status until continued via API
147
147
  return { status: PIPELINE_STATUS.WAITING_HUMAN };
148
- case 'parallel':
148
+ case STAGE_TYPE.PARALLEL:
149
149
  return executeParallelStage(resolved, stageState, run, pipeline, config);
150
150
  default:
151
151
  log('warn', `Pipeline: unknown stage type '${resolved.type}' in stage ${stage.id}`);
@@ -298,7 +298,7 @@ async function executePlanStage(stage, stageState, run, config) {
298
298
  }
299
299
 
300
300
  return {
301
- status: 'running',
301
+ status: PIPELINE_STATUS.RUNNING,
302
302
  artifacts: {
303
303
  plans: [path.basename(filePath)],
304
304
  workItems: [wiId],
@@ -355,33 +355,33 @@ function executeScheduleStage(stage, stageState, config) {
355
355
  }
356
356
  }
357
357
  safeWrite(path.join(__dirname, '..', 'config.json'), config);
358
- return { status: 'completed', completedAt: ts() };
358
+ return { status: PIPELINE_STATUS.COMPLETED, completedAt: ts() };
359
359
  }
360
360
 
361
361
  async function executeParallelStage(stage, stageState, run, pipeline, config) {
362
362
  const subStages = stage.stages || [];
363
363
  const subResults = {};
364
364
  for (const sub of subStages) {
365
- if (!run.stages[sub.id] || run.stages[sub.id].status === 'pending') {
365
+ if (!run.stages[sub.id] || run.stages[sub.id].status === PIPELINE_STATUS.PENDING) {
366
366
  const result = await executeStage(sub, run, pipeline, config);
367
367
  subResults[sub.id] = result;
368
368
  run.stages[sub.id] = { ...run.stages[sub.id] || {}, ...result, startedAt: ts() };
369
369
  }
370
370
  }
371
371
  // Parent is running until all subs complete
372
- return { status: 'running', artifacts: { subStages: subStages.map(s => s.id) } };
372
+ return { status: PIPELINE_STATUS.RUNNING, artifacts: { subStages: subStages.map(s => s.id) } };
373
373
  }
374
374
 
375
375
  // ── Stage Completion Checks ──────────────────────────────────────────────────
376
376
 
377
377
  function isStageComplete(stage, stageState, run, config) {
378
- if (stageState.status === 'completed' || stageState.status === 'failed') return true;
379
- if (stageState.status === 'pending' || stageState.status === 'waiting-human') return false;
378
+ if (stageState.status === PIPELINE_STATUS.COMPLETED || stageState.status === PIPELINE_STATUS.FAILED) return true;
379
+ if (stageState.status === PIPELINE_STATUS.PENDING || stageState.status === PIPELINE_STATUS.WAITING_HUMAN) return false;
380
380
 
381
381
  const artifacts = stageState.artifacts || {};
382
382
 
383
383
  switch (stage.type) {
384
- case 'task': {
384
+ case STAGE_TYPE.TASK: {
385
385
  const wiPath = path.join(__dirname, '..', 'work-items.json');
386
386
  const workItems = safeJson(wiPath) || [];
387
387
  const ids = artifacts.workItems || [];
@@ -391,16 +391,16 @@ function isStageComplete(stage, stageState, run, config) {
391
391
  return !wi || wi.status === WI_STATUS.DONE || wi.status === WI_STATUS.FAILED; // missing = treat as done
392
392
  });
393
393
  }
394
- case 'meeting': {
394
+ case STAGE_TYPE.MEETING: {
395
395
  const { getMeeting } = require('./meeting');
396
396
  const ids = artifacts.meetings || [];
397
397
  if (ids.length === 0) return false;
398
398
  return ids.every(id => {
399
399
  const m = getMeeting(id);
400
- return m && (m.status === 'completed' || m.status === 'archived');
400
+ return m && (m.status === MEETING_STATUS.COMPLETED || m.status === MEETING_STATUS.ARCHIVED);
401
401
  });
402
402
  }
403
- case 'plan': {
403
+ case STAGE_TYPE.PLAN: {
404
404
  // Plan stage completion: PRD conversion done + all materialized work items done
405
405
  const wiPath = path.join(__dirname, '..', 'work-items.json');
406
406
  const workItems = safeJson(wiPath) || [];
@@ -464,7 +464,7 @@ function isStageComplete(stage, stageState, run, config) {
464
464
  return !wi || wi.status === WI_STATUS.DONE || wi.status === WI_STATUS.FAILED; // missing = treat as done
465
465
  });
466
466
  }
467
- case 'merge-prs': {
467
+ case STAGE_TYPE.MERGE_PRS: {
468
468
  const prIds = artifacts.prs || [];
469
469
  if (prIds.length === 0) return true; // nothing to merge
470
470
  const projects = shared.getProjects(config);
@@ -477,16 +477,16 @@ function isStageComplete(stage, stageState, run, config) {
477
477
  }
478
478
  return true;
479
479
  }
480
- case 'api':
481
- case 'schedule':
480
+ case STAGE_TYPE.API:
481
+ case STAGE_TYPE.SCHEDULE:
482
482
  return true; // fire-and-forget
483
- case 'wait':
484
- return stageState.status === 'completed';
485
- case 'parallel': {
483
+ case STAGE_TYPE.WAIT:
484
+ return stageState.status === PIPELINE_STATUS.COMPLETED;
485
+ case STAGE_TYPE.PARALLEL: {
486
486
  const subIds = artifacts.subStages || [];
487
487
  return subIds.every(id => {
488
488
  const sub = run.stages[id];
489
- return sub && (sub.status === 'completed' || sub.status === 'failed');
489
+ return sub && (sub.status === PIPELINE_STATUS.COMPLETED || sub.status === PIPELINE_STATUS.FAILED);
490
490
  });
491
491
  }
492
492
  default:
@@ -533,18 +533,18 @@ async function discoverPipelineWork(config) {
533
533
  if (!stageState) continue;
534
534
 
535
535
  // Check if running stage completed
536
- if (stageState.status === 'running') {
536
+ if (stageState.status === PIPELINE_STATUS.RUNNING) {
537
537
  if (isStageComplete(stage, stageState, activeRun, config)) {
538
538
  // Collect output
539
539
  let output = '';
540
- if (stage.type === 'task') {
540
+ if (stage.type === STAGE_TYPE.TASK) {
541
541
  const wiPath = path.join(__dirname, '..', 'work-items.json');
542
542
  const workItems = safeJson(wiPath) || [];
543
543
  output = (stageState.artifacts?.workItems || []).map(id => {
544
544
  const wi = workItems.find(w => w.id === id);
545
545
  return wi?.resultSummary || wi?.title || id;
546
546
  }).join('\n');
547
- } else if (stage.type === 'meeting') {
547
+ } else if (stage.type === STAGE_TYPE.MEETING) {
548
548
  const { getMeeting } = require('./meeting');
549
549
  output = (stageState.artifacts?.meetings || []).map(id => {
550
550
  const m = getMeeting(id);
@@ -553,9 +553,9 @@ async function discoverPipelineWork(config) {
553
553
  }
554
554
 
555
555
  updateRunStage(pipeline.id, activeRun.runId, stage.id, {
556
- status: 'completed', completedAt: ts(), output
556
+ status: PIPELINE_STATUS.COMPLETED, completedAt: ts(), output
557
557
  });
558
- stageState.status = 'completed';
558
+ stageState.status = PIPELINE_STATUS.COMPLETED;
559
559
  stageState.output = output;
560
560
  log('info', `Pipeline ${pipeline.id}: stage ${stage.id} completed`);
561
561
  } else {
@@ -564,42 +564,42 @@ async function discoverPipelineWork(config) {
564
564
  }
565
565
  }
566
566
 
567
- if (stageState.status === 'waiting-human') { allComplete = false; continue; }
567
+ if (stageState.status === PIPELINE_STATUS.WAITING_HUMAN) { allComplete = false; continue; }
568
568
 
569
569
  // Check if pending stage is ready to start
570
- if (stageState.status === 'pending') {
570
+ if (stageState.status === PIPELINE_STATUS.PENDING) {
571
571
  allComplete = false;
572
572
  const depsReady = (stage.dependsOn || []).every(depId => {
573
573
  const dep = activeRun.stages[depId];
574
- return dep && dep.status === 'completed';
574
+ return dep && dep.status === PIPELINE_STATUS.COMPLETED;
575
575
  });
576
576
  const depsFailed = (stage.dependsOn || []).some(depId => {
577
577
  const dep = activeRun.stages[depId];
578
- return dep && dep.status === 'failed';
578
+ return dep && dep.status === PIPELINE_STATUS.FAILED;
579
579
  });
580
580
 
581
581
  if (depsFailed) {
582
- updateRunStage(pipeline.id, activeRun.runId, stage.id, { status: 'failed', error: 'dependency failed' });
583
- stageState.status = 'failed';
582
+ updateRunStage(pipeline.id, activeRun.runId, stage.id, { status: PIPELINE_STATUS.FAILED, error: 'dependency failed' });
583
+ stageState.status = PIPELINE_STATUS.FAILED;
584
584
  anyFailed = true;
585
585
  } else if (depsReady) {
586
586
  const result = await executeStage(stage, activeRun, pipeline, config);
587
587
  updateRunStage(pipeline.id, activeRun.runId, stage.id, { ...result, startedAt: ts() });
588
588
  Object.assign(stageState, result, { startedAt: ts() });
589
- if (result.status === 'running') anyRunning = true;
589
+ if (result.status === PIPELINE_STATUS.RUNNING) anyRunning = true;
590
590
  log('info', `Pipeline ${pipeline.id}: started stage ${stage.id} (${stage.type})`);
591
591
  }
592
592
  }
593
593
 
594
- if (stageState.status === 'failed') anyFailed = true;
595
- if (stageState.status !== 'completed') allComplete = false;
594
+ if (stageState.status === PIPELINE_STATUS.FAILED) anyFailed = true;
595
+ if (stageState.status !== PIPELINE_STATUS.COMPLETED) allComplete = false;
596
596
  }
597
597
 
598
598
  // Check if run is done
599
599
  if (allComplete) {
600
- completeRun(pipeline.id, activeRun.runId, 'completed');
600
+ completeRun(pipeline.id, activeRun.runId, PIPELINE_STATUS.COMPLETED);
601
601
  } else if (anyFailed && !anyRunning) {
602
- completeRun(pipeline.id, activeRun.runId, 'failed');
602
+ completeRun(pipeline.id, activeRun.runId, PIPELINE_STATUS.FAILED);
603
603
  }
604
604
  }
605
605
  }
package/engine/shared.js CHANGED
@@ -417,6 +417,18 @@ const PLAN_STATUS = {
417
417
  };
418
418
  const PR_STATUS = { ACTIVE: 'active', MERGED: 'merged', ABANDONED: 'abandoned', CLOSED: 'closed' };
419
419
  const DISPATCH_RESULT = { SUCCESS: 'success', ERROR: 'error', TIMEOUT: 'timeout' };
420
+ const PIPELINE_STATUS = {
421
+ PENDING: 'pending', RUNNING: 'running', COMPLETED: 'completed',
422
+ FAILED: 'failed', PAUSED: 'paused', WAITING_HUMAN: 'waiting-human',
423
+ };
424
+ const STAGE_TYPE = {
425
+ TASK: 'task', MEETING: 'meeting', PLAN: 'plan', API: 'api',
426
+ MERGE_PRS: 'merge-prs', SCHEDULE: 'schedule', WAIT: 'wait', PARALLEL: 'parallel',
427
+ };
428
+ const MEETING_STATUS = {
429
+ INVESTIGATING: 'investigating', DEBATING: 'debating', CONCLUDING: 'concluding',
430
+ COMPLETED: 'completed', ARCHIVED: 'archived',
431
+ };
420
432
 
421
433
  const DEFAULT_AGENTS = {
422
434
  ripley: { name: 'Ripley', emoji: '\u{1F3D7}\uFE0F', role: 'Lead / Explorer', skills: ['architecture', 'codebase-exploration', 'design-review'] },
@@ -648,6 +660,7 @@ module.exports = {
648
660
  classifyInboxItem,
649
661
  ENGINE_DEFAULTS,
650
662
  WI_STATUS, DONE_STATUSES, WORK_TYPE, PLAN_STATUS, PR_STATUS, DISPATCH_RESULT,
663
+ PIPELINE_STATUS, STAGE_TYPE, MEETING_STATUS,
651
664
  DEFAULT_AGENTS,
652
665
  DEFAULT_CLAUDE,
653
666
  getProjects,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.389",
3
+ "version": "0.1.390",
4
4
  "description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
5
5
  "bin": {
6
6
  "minions": "bin/minions.js"