@yemi33/minions 0.1.653 → 0.1.654
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 +4 -1
- package/dashboard.js +4 -4
- package/engine/consolidation.js +2 -2
- package/engine/pipeline.js +28 -16
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.654 (2026-04-09)
|
|
4
4
|
|
|
5
5
|
### Fixes
|
|
6
6
|
- kill process tree (not just parent) before worktree removal
|
|
7
7
|
|
|
8
|
+
### Other
|
|
9
|
+
- refactor: extract slugify/formatTranscriptEntry, fix pipeline completion
|
|
10
|
+
|
|
8
11
|
## 0.1.652 (2026-04-09)
|
|
9
12
|
|
|
10
13
|
### Fixes
|
package/dashboard.js
CHANGED
|
@@ -1266,7 +1266,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
1266
1266
|
fs.mkdirSync(inboxDir, { recursive: true });
|
|
1267
1267
|
const today = new Date().toISOString().slice(0, 10);
|
|
1268
1268
|
const author = body.author || os.userInfo().username;
|
|
1269
|
-
const slug = (body.title || 'note'
|
|
1269
|
+
const slug = shared.slugify(body.title || 'note', 40);
|
|
1270
1270
|
const filename = `${author}-${slug}-${today}-${shared.uid().slice(-4)}.md`;
|
|
1271
1271
|
const content = `# ${body.title}\n\n**By:** ${author}\n**Date:** ${today}\n\n${body.what}\n${body.why ? '\n**Why:** ' + body.why + '\n' : ''}`;
|
|
1272
1272
|
safeWrite(shared.uniquePath(path.join(inboxDir, filename)), content);
|
|
@@ -3362,7 +3362,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3362
3362
|
|
|
3363
3363
|
// Auto-generate ID from title if not provided
|
|
3364
3364
|
if (!id) {
|
|
3365
|
-
id =
|
|
3365
|
+
id = shared.slugify(title, 40);
|
|
3366
3366
|
if (!id) id = 'schedule';
|
|
3367
3367
|
}
|
|
3368
3368
|
|
|
@@ -3888,7 +3888,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3888
3888
|
|
|
3889
3889
|
const plansDir = path.join(MINIONS_DIR, 'plans');
|
|
3890
3890
|
if (!fs.existsSync(plansDir)) fs.mkdirSync(plansDir, { recursive: true });
|
|
3891
|
-
const slug =
|
|
3891
|
+
const slug = shared.slugify(title);
|
|
3892
3892
|
const date = new Date().toISOString().slice(0, 10);
|
|
3893
3893
|
const filename = `${slug}-${date}.md`;
|
|
3894
3894
|
const filePath = shared.uniquePath(path.join(plansDir, filename));
|
|
@@ -3954,7 +3954,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3954
3954
|
if (!category || !title || !content) return jsonReply(res, 400, { error: 'category, title, and content required' });
|
|
3955
3955
|
const validCategories = ['architecture', 'conventions', 'project-notes', 'build-reports', 'reviews'];
|
|
3956
3956
|
if (!validCategories.includes(category)) return jsonReply(res, 400, { error: 'Invalid category. Must be: ' + validCategories.join(', ') });
|
|
3957
|
-
const slug =
|
|
3957
|
+
const slug = shared.slugify(title, 60);
|
|
3958
3958
|
const filePath = path.join(MINIONS_DIR, 'knowledge', category, slug + '.md');
|
|
3959
3959
|
const dir = path.dirname(filePath);
|
|
3960
3960
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
package/engine/consolidation.js
CHANGED
|
@@ -140,7 +140,7 @@ function consolidateWithLLM(items, existingNotes, files, config) {
|
|
|
140
140
|
const agentMatch = item.name.match(/^(\w+)-/);
|
|
141
141
|
const agent = agentMatch ? agentMatch[1] : 'unknown';
|
|
142
142
|
const titleMatch = (item.content || '').match(/^#\s+(.+)/m);
|
|
143
|
-
const titleSlug = titleMatch ? titleMatch[1]
|
|
143
|
+
const titleSlug = titleMatch ? shared.slugify(titleMatch[1]) : item.name.replace(/\.md$/, '');
|
|
144
144
|
return { file: item.name, category: cat, kbPath: path.join('knowledge', cat, `${dateStamp()}-${agent}-${titleSlug}.md`) };
|
|
145
145
|
});
|
|
146
146
|
|
|
@@ -415,7 +415,7 @@ function classifyToKnowledgeBase(items) {
|
|
|
415
415
|
const agent = agentMatch ? agentMatch[1] : 'unknown';
|
|
416
416
|
const titleMatch = content.match(/^#\s+(.+)/m);
|
|
417
417
|
const titleSlug = titleMatch
|
|
418
|
-
? titleMatch[1]
|
|
418
|
+
? shared.slugify(titleMatch[1])
|
|
419
419
|
: item.name.replace(/\.md$/, '');
|
|
420
420
|
const kbFilename = `${dateStamp()}-${agent}-${titleSlug}.md`;
|
|
421
421
|
const kbPath = shared.uniquePath(path.join(categoryDirs[category], kbFilename));
|
package/engine/pipeline.js
CHANGED
|
@@ -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, mutateWorkItems, WI_STATUS, WORK_TYPE, PLAN_STATUS, PR_STATUS, PIPELINE_STATUS, STAGE_TYPE, MEETING_STATUS, ENGINE_DEFAULTS } = shared;
|
|
10
|
+
const { safeJson, safeWrite, safeRead, safeReadDir, uid, log, ts, dateStamp, mutateJsonFileLocked, mutateWorkItems, slugify, formatTranscriptEntry, WI_STATUS, WORK_TYPE, PLAN_STATUS, PR_STATUS, PIPELINE_STATUS, STAGE_TYPE, MEETING_STATUS, ENGINE_DEFAULTS } = shared;
|
|
11
11
|
const http = require('http');
|
|
12
12
|
const { parseCronExpr, shouldRunNow } = require('./scheduler');
|
|
13
13
|
|
|
@@ -304,7 +304,7 @@ function _findExistingPlanForMeeting(meetingIds, plansDir) {
|
|
|
304
304
|
const mtg = safeJson(path.join(__dirname, '..', 'meetings', mid + '.json'));
|
|
305
305
|
if (mtg?.title) {
|
|
306
306
|
// Dashboard convention: "Meeting follow-up: {title}" → slug
|
|
307
|
-
const dashSlug = ('meeting-follow-up-' + mtg.title)
|
|
307
|
+
const dashSlug = slugify('meeting-follow-up-' + mtg.title);
|
|
308
308
|
slugPrefixes.push(dashSlug);
|
|
309
309
|
}
|
|
310
310
|
}
|
|
@@ -324,7 +324,7 @@ async function executePlanStage(stage, stageState, run, config) {
|
|
|
324
324
|
const plansDir = path.join(__dirname, '..', 'plans');
|
|
325
325
|
if (!fs.existsSync(plansDir)) fs.mkdirSync(plansDir, { recursive: true });
|
|
326
326
|
|
|
327
|
-
const slug = (stage.title || 'pipeline-plan')
|
|
327
|
+
const slug = slugify(stage.title || 'pipeline-plan');
|
|
328
328
|
const wiPath = path.join(__dirname, '..', 'work-items.json');
|
|
329
329
|
const wiId = `PL-${run.runId.slice(4, 12)}-${stage.id}-prd`;
|
|
330
330
|
|
|
@@ -364,9 +364,7 @@ async function executePlanStage(stage, stageState, run, config) {
|
|
|
364
364
|
try {
|
|
365
365
|
const mtg = safeJson(path.join(__dirname, '..', 'meetings', mid + '.json'));
|
|
366
366
|
if (mtg) {
|
|
367
|
-
const transcript = (mtg.transcript || []).map(
|
|
368
|
-
'### ' + (t.agent || 'agent') + ' (' + (t.type || '') + ', Round ' + (t.round || '?') + ')\n\n' + (t.content || '')
|
|
369
|
-
).join('\n\n---\n\n');
|
|
367
|
+
const transcript = (mtg.transcript || []).map(formatTranscriptEntry).join('\n\n---\n\n');
|
|
370
368
|
meetingContext += '# Meeting: ' + (mtg.title || mid) + '\n\n**Agenda:** ' + (mtg.agenda || '') + '\n\n' + transcript + '\n\n';
|
|
371
369
|
}
|
|
372
370
|
} catch (e) { log('warn', `Pipeline plan: failed to read meeting ${mid}: ${e.message}`); }
|
|
@@ -600,29 +598,32 @@ function isStageComplete(stage, stageState, run, config) {
|
|
|
600
598
|
});
|
|
601
599
|
if (!prdDone) return false;
|
|
602
600
|
|
|
603
|
-
// Discover PRDs and their work items
|
|
601
|
+
// Discover PRDs and their work items — collect into local arrays, then merge into artifacts
|
|
604
602
|
const prdDir = path.join(__dirname, '..', 'prd');
|
|
605
603
|
const plans = artifacts.plans || [];
|
|
604
|
+
const discoveredPrds = [];
|
|
605
|
+
const discoveredWiIds = [];
|
|
606
606
|
for (const planFile of plans) {
|
|
607
607
|
const prdFiles = fs.existsSync(prdDir) ? safeReadDir(prdDir).filter(f => f.endsWith('.json')) : [];
|
|
608
608
|
for (const pf of prdFiles) {
|
|
609
609
|
const prd = safeJson(path.join(prdDir, pf));
|
|
610
|
-
if (prd?.source_plan === planFile && !(artifacts.prds || []).includes(pf)) {
|
|
611
|
-
|
|
612
|
-
artifacts.prds.push(pf);
|
|
610
|
+
if (prd?.source_plan === planFile && !(artifacts.prds || []).includes(pf) && !discoveredPrds.includes(pf)) {
|
|
611
|
+
discoveredPrds.push(pf);
|
|
613
612
|
}
|
|
614
613
|
}
|
|
615
|
-
|
|
616
|
-
for (const prdFile of
|
|
614
|
+
const allPrds = [...(artifacts.prds || []), ...discoveredPrds];
|
|
615
|
+
for (const prdFile of allPrds) {
|
|
617
616
|
const prdItems = all.filter(w => w.sourcePlan === prdFile && w.type !== WORK_TYPE.PLAN_TO_PRD);
|
|
618
617
|
for (const wi of prdItems) {
|
|
619
|
-
if (!(artifacts.workItems || []).includes(wi.id)) {
|
|
620
|
-
|
|
621
|
-
artifacts.workItems.push(wi.id);
|
|
618
|
+
if (!(artifacts.workItems || []).includes(wi.id) && !discoveredWiIds.includes(wi.id)) {
|
|
619
|
+
discoveredWiIds.push(wi.id);
|
|
622
620
|
}
|
|
623
621
|
}
|
|
624
622
|
}
|
|
625
623
|
}
|
|
624
|
+
// Merge discovered artifacts (caller persists via updateRunStage)
|
|
625
|
+
if (discoveredPrds.length > 0) { artifacts.prds = [...(artifacts.prds || []), ...discoveredPrds]; }
|
|
626
|
+
if (discoveredWiIds.length > 0) { artifacts.workItems = [...(artifacts.workItems || []), ...discoveredWiIds]; }
|
|
626
627
|
|
|
627
628
|
// Auto-approve if configured
|
|
628
629
|
if (stage.autoApprove && artifacts.prds?.length > 0) {
|
|
@@ -792,7 +793,18 @@ async function discoverPipelineWork(config) {
|
|
|
792
793
|
const result = await executeStage(stage, activeRun, pipeline, config);
|
|
793
794
|
updateRunStage(pipeline.id, activeRun.runId, stage.id, { ...result, startedAt: ts() });
|
|
794
795
|
Object.assign(stageState, result, { startedAt: ts() });
|
|
795
|
-
if (result.status === PIPELINE_STATUS.RUNNING)
|
|
796
|
+
if (result.status === PIPELINE_STATUS.RUNNING) {
|
|
797
|
+
// Check if stage is already complete (e.g. reconciled plan with done WI)
|
|
798
|
+
if (isStageComplete(stage, stageState, activeRun, config)) {
|
|
799
|
+
updateRunStage(pipeline.id, activeRun.runId, stage.id, {
|
|
800
|
+
status: PIPELINE_STATUS.COMPLETED, completedAt: ts(), artifacts: stageState.artifacts,
|
|
801
|
+
});
|
|
802
|
+
stageState.status = PIPELINE_STATUS.COMPLETED;
|
|
803
|
+
log('info', `Pipeline ${pipeline.id}: stage ${stage.id} completed immediately after start`);
|
|
804
|
+
} else {
|
|
805
|
+
anyRunning = true;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
796
808
|
// Condition stage signaled pipeline stop — complete the run immediately
|
|
797
809
|
if (result._stopPipeline) {
|
|
798
810
|
completeRun(pipeline.id, activeRun.runId, PIPELINE_STATUS.STOPPED);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.654",
|
|
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"
|