@yemi33/minions 0.1.357 → 0.1.358
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 +2 -1
- package/engine/pipeline.js +52 -29
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.358 (2026-04-06)
|
|
4
4
|
|
|
5
5
|
### Features
|
|
6
|
+
- pipeline plan stage uses LLM to generate structured plan from meeting
|
|
6
7
|
- show 'Converting to PRD' status instead of 'In Progress' during plan conversion
|
|
7
8
|
|
|
8
9
|
## 0.1.356 (2026-04-06)
|
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, WI_STATUS, WORK_TYPE, PLAN_STATUS, PR_STATUS } = 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;
|
|
11
11
|
const { parseCronExpr, shouldRunNow } = require('./scheduler');
|
|
12
12
|
|
|
13
13
|
const PIPELINES_DIR = path.join(__dirname, '..', 'pipelines');
|
|
@@ -53,16 +53,16 @@ function savePipelineRuns(runs) {
|
|
|
53
53
|
function getActiveRun(pipelineId) {
|
|
54
54
|
const runs = getPipelineRuns();
|
|
55
55
|
const pipelineRuns = runs[pipelineId] || [];
|
|
56
|
-
return pipelineRuns.find(r => r.status ===
|
|
56
|
+
return pipelineRuns.find(r => r.status === PIPELINE_STATUS.RUNNING || r.status === PIPELINE_STATUS.PAUSED);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
function startRun(pipelineId, pipeline) {
|
|
60
60
|
const runId = `run-${uid()}`;
|
|
61
61
|
const stages = {};
|
|
62
62
|
for (const stage of (pipeline.stages || [])) {
|
|
63
|
-
stages[stage.id] = { status:
|
|
63
|
+
stages[stage.id] = { status: PIPELINE_STATUS.PENDING, artifacts: {} };
|
|
64
64
|
}
|
|
65
|
-
const run = { runId, pipelineId, startedAt: ts(), status:
|
|
65
|
+
const run = { runId, pipelineId, startedAt: ts(), status: PIPELINE_STATUS.RUNNING, stages };
|
|
66
66
|
|
|
67
67
|
mutateJsonFileLocked(PIPELINE_RUNS_PATH, (data) => {
|
|
68
68
|
if (!data[pipelineId]) data[pipelineId] = [];
|
|
@@ -125,7 +125,7 @@ function resolveStageConfig(stage, run) {
|
|
|
125
125
|
|
|
126
126
|
// ── Stage Execution ──────────────────────────────────────────────────────────
|
|
127
127
|
|
|
128
|
-
function executeStage(stage, run, pipeline, config) {
|
|
128
|
+
async function executeStage(stage, run, pipeline, config) {
|
|
129
129
|
const resolved = resolveStageConfig(stage, run);
|
|
130
130
|
const stageState = run.stages[stage.id];
|
|
131
131
|
|
|
@@ -144,12 +144,12 @@ function executeStage(stage, run, pipeline, config) {
|
|
|
144
144
|
return executeScheduleStage(resolved, stageState, config);
|
|
145
145
|
case 'wait':
|
|
146
146
|
// wait stages just sit in waiting-human status until continued via API
|
|
147
|
-
return { status:
|
|
147
|
+
return { status: PIPELINE_STATUS.WAITING_HUMAN };
|
|
148
148
|
case '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}`);
|
|
152
|
-
return { status:
|
|
152
|
+
return { status: PIPELINE_STATUS.FAILED, error: 'unknown stage type' };
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
|
|
@@ -183,7 +183,7 @@ function executeTaskStage(stage, stageState, run, config) {
|
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
safeWrite(wiPath, workItems);
|
|
186
|
-
return { status:
|
|
186
|
+
return { status: PIPELINE_STATUS.RUNNING, artifacts: { workItems: createdIds } };
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
function executeMeetingStage(stage, stageState, run, config) {
|
|
@@ -205,10 +205,10 @@ function executeMeetingStage(stage, stageState, run, config) {
|
|
|
205
205
|
createdIds.push(meeting.id);
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
-
return { status:
|
|
208
|
+
return { status: PIPELINE_STATUS.RUNNING, artifacts: { meetings: createdIds } };
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
function executePlanStage(stage, stageState, run, config) {
|
|
211
|
+
async function executePlanStage(stage, stageState, run, config) {
|
|
212
212
|
// Create a plan .md file from the stage config + previous stage output
|
|
213
213
|
const plansDir = path.join(__dirname, '..', 'plans');
|
|
214
214
|
if (!fs.existsSync(plansDir)) fs.mkdirSync(plansDir, { recursive: true });
|
|
@@ -217,39 +217,62 @@ function executePlanStage(stage, stageState, run, config) {
|
|
|
217
217
|
const filename = `${slug}-${dateStamp()}.md`;
|
|
218
218
|
const filePath = shared.uniquePath(path.join(plansDir, filename));
|
|
219
219
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
content += `**Date:** ${dateStamp()}\n\n---\n\n`;
|
|
223
|
-
|
|
224
|
-
// Include output from dependency stages (for meetings, include full transcript)
|
|
220
|
+
// Build meeting context from dependency stages
|
|
221
|
+
let meetingContext = '';
|
|
225
222
|
if (stage.dependsOn) {
|
|
226
223
|
for (const depId of stage.dependsOn) {
|
|
227
224
|
const depStage = run.stages[depId];
|
|
228
225
|
if (!depStage) continue;
|
|
229
226
|
|
|
230
|
-
// If dependency is a meeting, include the conclusion (which should be concrete and actionable)
|
|
231
227
|
const meetingId = depStage.artifacts?.meetings?.[0];
|
|
232
228
|
if (meetingId) {
|
|
233
229
|
try {
|
|
234
230
|
const mtgPath = path.join(__dirname, '..', 'meetings', meetingId + '.json');
|
|
235
231
|
const mtg = safeJson(mtgPath);
|
|
236
232
|
if (mtg) {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
233
|
+
// Build full meeting document (same as dashboard "Create Plan from Meeting")
|
|
234
|
+
const transcript = (mtg.transcript || []).map(t =>
|
|
235
|
+
'### ' + (t.agent || 'agent') + ' (' + (t.type || '') + ', Round ' + (t.round || '?') + ')\n\n' + (t.content || '')
|
|
236
|
+
).join('\n\n---\n\n');
|
|
237
|
+
meetingContext += '# Meeting: ' + (mtg.title || depId) + '\n\n**Agenda:** ' + (mtg.agenda || '') + '\n\n' + transcript + '\n\n';
|
|
238
|
+
continue;
|
|
242
239
|
}
|
|
243
|
-
} catch { /* fall through
|
|
240
|
+
} catch { /* fall through */ }
|
|
244
241
|
}
|
|
245
242
|
|
|
246
243
|
if (depStage.output) {
|
|
247
|
-
|
|
244
|
+
meetingContext += '## From: ' + depId + '\n\n' + depStage.output + '\n\n';
|
|
248
245
|
}
|
|
249
246
|
}
|
|
250
247
|
}
|
|
251
248
|
|
|
252
|
-
|
|
249
|
+
// Use LLM to generate a structured plan (same approach as dashboard "Create Plan from Meeting" button)
|
|
250
|
+
let content = '';
|
|
251
|
+
try {
|
|
252
|
+
const llm = require('./llm');
|
|
253
|
+
const planPrompt = 'Create an actionable implementation plan from this meeting. ' +
|
|
254
|
+
'Extract concrete action items from the conclusion and debates. ' +
|
|
255
|
+
'For each item include: what to do, which files/areas to change, priority (high/medium/low), and estimated complexity (small/medium/large). ' +
|
|
256
|
+
'Structure it as a plan ready for execution. Do NOT include preamble — start with the plan title.' +
|
|
257
|
+
(stage.description ? '\n\nAdditional instructions: ' + stage.description : '');
|
|
258
|
+
const fullPrompt = meetingContext + '\n\n---\n\n' + planPrompt;
|
|
259
|
+
const result = await llm.callLLM(fullPrompt, '', {
|
|
260
|
+
timeout: 120000, label: 'pipeline-plan', model: 'sonnet', maxTurns: 1,
|
|
261
|
+
});
|
|
262
|
+
if (result.text) {
|
|
263
|
+
content = result.text;
|
|
264
|
+
log('info', `Pipeline plan: LLM generated ${content.length} chars from meeting context`);
|
|
265
|
+
}
|
|
266
|
+
} catch (e) { log('warn', `Pipeline plan LLM failed: ${e.message} — falling back to raw meeting context`); }
|
|
267
|
+
|
|
268
|
+
// Fallback: raw meeting context if LLM failed
|
|
269
|
+
if (!content) {
|
|
270
|
+
content = `# ${stage.title}\n\n`;
|
|
271
|
+
content += `**Created by:** Pipeline ${run.pipelineId}\n`;
|
|
272
|
+
content += `**Date:** ${dateStamp()}\n\n---\n\n`;
|
|
273
|
+
content += meetingContext;
|
|
274
|
+
if (stage.description) content += stage.description + '\n';
|
|
275
|
+
}
|
|
253
276
|
|
|
254
277
|
safeWrite(filePath, content);
|
|
255
278
|
|
|
@@ -303,7 +326,7 @@ function executeApiStage(stage, stageState, run) {
|
|
|
303
326
|
req.end();
|
|
304
327
|
} catch (e) { log('warn', `Pipeline API call failed: ${e.message}`); }
|
|
305
328
|
}
|
|
306
|
-
return { status:
|
|
329
|
+
return { status: PIPELINE_STATUS.COMPLETED, completedAt: ts() };
|
|
307
330
|
}
|
|
308
331
|
|
|
309
332
|
function executeMergePrsStage(stage, stageState, run, config) {
|
|
@@ -313,11 +336,11 @@ function executeMergePrsStage(stage, stageState, run, config) {
|
|
|
313
336
|
if (s.artifacts?.prs) prIds.push(...s.artifacts.prs);
|
|
314
337
|
}
|
|
315
338
|
if (prIds.length === 0) {
|
|
316
|
-
return { status:
|
|
339
|
+
return { status: PIPELINE_STATUS.COMPLETED, completedAt: ts(), output: 'No PRs to merge' };
|
|
317
340
|
}
|
|
318
341
|
// The actual merge will be handled by the PR polling/merge logic
|
|
319
342
|
// We just need to track which PRs to watch
|
|
320
|
-
return { status:
|
|
343
|
+
return { status: PIPELINE_STATUS.RUNNING, artifacts: { prs: prIds } };
|
|
321
344
|
}
|
|
322
345
|
|
|
323
346
|
function executeScheduleStage(stage, stageState, config) {
|
|
@@ -340,7 +363,7 @@ function executeParallelStage(stage, stageState, run, pipeline, config) {
|
|
|
340
363
|
const subResults = {};
|
|
341
364
|
for (const sub of subStages) {
|
|
342
365
|
if (!run.stages[sub.id] || run.stages[sub.id].status === 'pending') {
|
|
343
|
-
const result = executeStage(sub, run, pipeline, config);
|
|
366
|
+
const result = await executeStage(sub, run, pipeline, config);
|
|
344
367
|
subResults[sub.id] = result;
|
|
345
368
|
run.stages[sub.id] = { ...run.stages[sub.id] || {}, ...result, startedAt: ts() };
|
|
346
369
|
}
|
|
@@ -560,7 +583,7 @@ function discoverPipelineWork(config) {
|
|
|
560
583
|
stageState.status = 'failed';
|
|
561
584
|
anyFailed = true;
|
|
562
585
|
} else if (depsReady) {
|
|
563
|
-
const result = executeStage(stage, activeRun, pipeline, config);
|
|
586
|
+
const result = await executeStage(stage, activeRun, pipeline, config);
|
|
564
587
|
updateRunStage(pipeline.id, activeRun.runId, stage.id, { ...result, startedAt: ts() });
|
|
565
588
|
Object.assign(stageState, result, { startedAt: ts() });
|
|
566
589
|
if (result.status === 'running') anyRunning = true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.358",
|
|
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"
|