@yemi33/minions 0.1.880 → 0.1.882

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,6 +1,6 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.880 (2026-04-11)
3
+ ## 0.1.882 (2026-04-11)
4
4
 
5
5
  ### Features
6
6
  - add 13 missing CC action types for full dashboard parity
@@ -13,6 +13,8 @@
13
13
  - include started_at in done/error agent status so duration renders
14
14
 
15
15
  ### Other
16
+ - simplify: fix CC regenerate-plan field name, remove dead revise-and-regenerate handler
17
+ - remove doc-chat plan auto-pause in favour of stale banner only
16
18
  - simplify: CC actions cleanup — missing wakeEngine, redundant refresh, hardened fallback
17
19
 
18
20
  ## 0.1.873 (2026-04-11)
@@ -1072,7 +1072,7 @@ async function ccExecuteAction(action, targetTabId) {
1072
1072
  break;
1073
1073
  }
1074
1074
  case 'regenerate-plan': {
1075
- await _ccFetch('/api/plans/regenerate', { file: action.file });
1075
+ await _ccFetch('/api/plans/regenerate', { source: action.file });
1076
1076
  status.innerHTML = '&#10003; Plan regenerated: <strong>' + escHtml(action.file) + '</strong>';
1077
1077
  status.style.color = 'var(--green)';
1078
1078
  wakeEngine();
@@ -275,20 +275,6 @@ async function _processQaMessage(message, selection) {
275
275
  }
276
276
  _modalDocContext.content = display;
277
277
  }
278
-
279
- // If editing paused an active PRD, show re-execute actions (only for plan .md files)
280
- if (data.pausedPrd && capturedFilePath && /^plans\/.*\.md$/.test(capturedFilePath)) {
281
- const planFile = capturedFilePath.replace(/^plans\//, '');
282
- const esc = planFile.replace(/'/g, "\\'");
283
- const actionDiv = document.createElement('div');
284
- actionDiv.style.cssText = 'margin:8px 0;padding:8px 12px;background:rgba(210,153,34,0.1);border:1px solid rgba(210,153,34,0.3);border-radius:6px;display:flex;flex-wrap:wrap;align-items:center;gap:8px';
285
- actionDiv.innerHTML =
286
- '<span style="color:var(--orange);font-weight:600;font-size:12px;width:100%">Execution paused — plan was updated</span>' +
287
- '<button onclick="planExecute(\'' + esc + '\', \'\', null)" style="background:var(--green);color:#fff;border:none;border-radius:4px;padding:4px 12px;font-size:11px;font-weight:600;cursor:pointer">Re-execute plan</button>' +
288
- '<button onclick="this.closest(\'div\').innerHTML=\'<span style=color:var(--muted);font-size:11px>Paused. No work dispatched.</span>\'" style="background:var(--surface2);color:var(--text);border:1px solid var(--border);border-radius:4px;padding:4px 12px;font-size:11px;cursor:pointer">Keep paused</button>' +
289
- '<span style="color:var(--muted);font-size:10px;width:100%">Re-execute creates a new PRD from the updated plan.</span>';
290
- thread.appendChild(actionDiv);
291
- }
292
278
  } else {
293
279
  const qaElapsedErr = Math.round((Date.now() - qaStartTime) / 1000);
294
280
  thread.insertAdjacentHTML('beforeend', '<div class="modal-qa-a" style="color:var(--red)">Error: ' + escHtml(data.error || 'Failed') + '<div style="font-size:9px;color:var(--muted);margin-top:4px;text-align:right">' + qaElapsedErr + 's</div></div>');
package/dashboard.js CHANGED
@@ -2697,146 +2697,6 @@ If nothing to do: { "duplicates": [], "reclassify": [], "remove": [] }`;
2697
2697
 
2698
2698
  // POST /api/plans/revise-and-regenerate — REMOVED: plan versioning now handled by /api/doc-chat
2699
2699
  // The "Replace old PRD" flow uses qaReplacePrd (frontend) which calls /api/plans/pause + /api/plans/regenerate + planExecute
2700
- async function handlePlansReviseAndRegenerate(req, res) {
2701
- try {
2702
- const body = await readBody(req);
2703
- if (!body.source || !body.instruction) return jsonReply(res, 400, { error: 'source and instruction required' });
2704
-
2705
- // Find the source plan .md file for this PRD
2706
- // Convention: PRD JSON references plan via plan_summary containing the work item ID,
2707
- // or the .md file has a matching name prefix
2708
- const prdPath = path.join(PRD_DIR, body.source);
2709
- if (!fs.existsSync(prdPath)) return jsonReply(res, 404, { error: 'PRD file not found' });
2710
-
2711
- // Look for corresponding .md plan file
2712
- let sourcePlanFile = null;
2713
- const planFiles = safeReadDir(PLANS_DIR).filter(f => f.endsWith('.md'));
2714
- if (body.sourcePlan) {
2715
- // Explicit source plan provided
2716
- sourcePlanFile = body.sourcePlan;
2717
- } else {
2718
- // Heuristic: find .md plan by matching prefix or by reading PRD's generated_from field
2719
- const prd = safeJsonObj(prdPath);
2720
- if (prd.source_plan) {
2721
- sourcePlanFile = prd.source_plan;
2722
- } else {
2723
- // Match by prefix: officeagent-2026-03-15.json → plan-*officeagent* or plan-w025*.md
2724
- const prdBase = body.source.replace('.json', '');
2725
- for (const f of planFiles) {
2726
- // Check if plan file mentions the same project or was created around same time
2727
- const content = safeRead(path.join(PLANS_DIR, f)) || '';
2728
- if (content.includes(prd.project || '___nomatch___') || content.includes(prd.plan_summary?.slice(0, 40) || '___nomatch___')) {
2729
- sourcePlanFile = f;
2730
- break;
2731
- }
2732
- }
2733
- // Last resort: most recent .md plan
2734
- if (!sourcePlanFile && planFiles.length > 0) {
2735
- sourcePlanFile = planFiles.sort((a, b) => {
2736
- try { return fs.statSync(path.join(PLANS_DIR, b)).mtimeMs - fs.statSync(path.join(PLANS_DIR, a)).mtimeMs; } catch { return 0; }
2737
- })[0];
2738
- }
2739
- }
2740
- }
2741
-
2742
- if (!sourcePlanFile) {
2743
- return jsonReply(res, 404, { error: 'No source plan (.md) found for this PRD. You can edit the PRD JSON directly using "Edit Plan".' });
2744
- }
2745
-
2746
- const sourcePlanPath = path.join(PLANS_DIR, sourcePlanFile);
2747
- const planContent = safeRead(sourcePlanPath);
2748
- if (!planContent) return jsonReply(res, 404, { error: 'Source plan file not readable: ' + sourcePlanFile });
2749
-
2750
- // Step 1: Steer the source plan with the user's instruction via CC
2751
- const result = await ccDocCall({
2752
- message: body.instruction,
2753
- document: planContent,
2754
- title: sourcePlanFile,
2755
- filePath: 'plans/' + sourcePlanFile,
2756
- selection: body.selection || '',
2757
- canEdit: true,
2758
- isJson: false,
2759
- });
2760
-
2761
- if (!result.content) {
2762
- return jsonReply(res, 200, { ok: true, answer: result.answer, updated: false });
2763
- }
2764
-
2765
- // Save the revised plan
2766
- safeWrite(sourcePlanPath, result.content);
2767
-
2768
- // Step 2: Pause the old PRD so it stops materializing items
2769
- const prd = safeJsonObj(prdPath);
2770
- prd.status = 'revision-requested';
2771
- prd.revision_feedback = body.instruction;
2772
- prd.revisionRequestedAt = new Date().toISOString();
2773
- safeWrite(prdPath, prd);
2774
-
2775
- // Step 3: Clean up pending/failed work items from old PRD
2776
- let reset = 0, kept = 0;
2777
- const wiPaths = [{ path: path.join(MINIONS_DIR, 'work-items.json'), label: 'central' }];
2778
- for (const proj of PROJECTS) {
2779
- wiPaths.push({ path: shared.projectWorkItemsPath(proj), label: proj.name });
2780
- }
2781
- const deletedItemIds = [];
2782
- for (const wiInfo of wiPaths) {
2783
- try {
2784
- mutateWorkItems(wiInfo.path, items => {
2785
- const filtered = [];
2786
- for (const w of items) {
2787
- if (w.sourcePlan === body.source) {
2788
- if (w.status === WI_STATUS.PENDING || w.status === WI_STATUS.FAILED) {
2789
- reset++;
2790
- deletedItemIds.push(w.id);
2791
- } else {
2792
- kept++;
2793
- filtered.push(w);
2794
- }
2795
- } else {
2796
- filtered.push(w);
2797
- }
2798
- }
2799
- if (filtered.length < items.length) return filtered;
2800
- });
2801
- } catch (e) { console.error('work item deletion:', e.message); }
2802
- }
2803
- for (const itemId of deletedItemIds) {
2804
- cleanDispatchEntries(d =>
2805
- d.meta?.item?.sourcePlan === body.source && d.meta?.item?.id === itemId
2806
- );
2807
- }
2808
-
2809
- // Step 4: Dispatch plan-to-prd to regenerate PRD from revised plan
2810
- const centralWiPath = path.join(MINIONS_DIR, 'work-items.json');
2811
- const wiId = 'W-' + shared.uid();
2812
- mutateWorkItems(centralWiPath, items => {
2813
- items.push({
2814
- id: wiId,
2815
- title: 'Regenerate PRD from revised plan: ' + sourcePlanFile,
2816
- type: 'plan-to-prd',
2817
- priority: 'high',
2818
- description: `The source plan \`${sourcePlanFile}\` has been revised. Convert it into a fresh PRD JSON.\n\nRevision instruction: ${body.instruction}\n\nRead the revised plan, generate updated PRD items (missing_features), and write to \`prd/${body.source}\`. Set status to "approved". Include \`"source_plan": "${sourcePlanFile}"\` in the JSON root.\n\nPreserve items that are already done (status "implemented" or "complete"). Reset or replace items that were pending/failed.`,
2819
- status: WI_STATUS.PENDING,
2820
- created: new Date().toISOString(),
2821
- createdBy: 'dashboard:revise-and-regenerate',
2822
- project: prd.project || '',
2823
- planFile: sourcePlanFile,
2824
- });
2825
- });
2826
-
2827
- return jsonReply(res, 200, {
2828
- ok: true,
2829
- answer: result.answer,
2830
- updated: true,
2831
- sourcePlan: sourcePlanFile,
2832
- prdPaused: true,
2833
- reset,
2834
- kept,
2835
- workItemId: wiId,
2836
- });
2837
- } catch (e) { return jsonReply(res, 500, { error: e.message }); }
2838
- }
2839
-
2840
2700
  async function handlePlansDiscuss(req, res) {
2841
2701
  try {
2842
2702
  const body = await readBody(req);
@@ -2954,92 +2814,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
2954
2814
 
2955
2815
  safeWrite(fullPath, content);
2956
2816
 
2957
- // If editing a plan .md that has an active PRD, auto-pause execution
2958
- let pausedPrd = null;
2959
- if (body.filePath && body.filePath.startsWith('plans/') && body.filePath.endsWith('.md')) {
2960
- const planFile = body.filePath.replace(/^plans\//, '');
2961
- try {
2962
- const prdDir = path.join(MINIONS_DIR, 'prd');
2963
- if (fs.existsSync(prdDir)) {
2964
- for (const prdFile of fs.readdirSync(prdDir)) {
2965
- if (!prdFile.endsWith('.json')) continue;
2966
- const prd = safeJson(path.join(prdDir, prdFile));
2967
- if (!prd || prd.source_plan !== planFile) continue;
2968
- if (prd.status === 'paused' || prd.status === 'rejected') continue;
2969
- // Found an active PRD linked to this plan — pause it
2970
- prd.status = 'paused';
2971
- prd.pausedAt = new Date().toISOString();
2972
- prd.pausedBy = 'plan-steering';
2973
- safeWrite(path.join(prdDir, prdFile), prd);
2974
- pausedPrd = prdFile;
2975
- // Pause work items linked to this PRD (sourcePlan = PRD filename)
2976
- const wiPaths = [path.join(MINIONS_DIR, 'work-items.json')];
2977
- for (const proj of PROJECTS) wiPaths.push(shared.projectWorkItemsPath(proj));
2978
- const dispatchPath = path.join(MINIONS_DIR, 'engine', 'dispatch.json');
2979
- const dispatch = safeJsonObj(dispatchPath);
2980
- const killedAgents = new Set();
2981
- const resetItemIds = new Set();
2982
- for (const wiPath of wiPaths) {
2983
- try {
2984
- mutateJsonFileLocked(wiPath, (items) => {
2985
- if (!Array.isArray(items)) return items;
2986
- for (const w of items) {
2987
- if (w.sourcePlan !== prdFile) continue;
2988
- if (w.completedAt || DONE_STATUSES.has(w.status)) continue;
2989
- if (w.status === WI_STATUS.DISPATCHED) {
2990
- const activeEntry = (dispatch.active || []).find(d => d.meta?.item?.id === w.id || d.meta?.dispatchKey?.includes(w.id));
2991
- if (activeEntry) {
2992
- const statusPath = path.join(MINIONS_DIR, 'agents', activeEntry.agent, 'status.json');
2993
- try {
2994
- const agentStatus = safeJsonObj(statusPath);
2995
- if (agentStatus.pid) {
2996
- try {
2997
- const safePid = shared.validatePid(agentStatus.pid);
2998
- if (process.platform === 'win32') {
2999
- require('child_process').execFileSync('taskkill', ['/PID', String(safePid), '/F', '/T'], { stdio: 'pipe', timeout: 5000, windowsHide: true });
3000
- } else {
3001
- process.kill(safePid, 'SIGTERM');
3002
- }
3003
- } catch { /* process may be dead or invalid PID */ }
3004
- }
3005
- agentStatus.status = 'idle';
3006
- delete agentStatus.currentTask;
3007
- delete agentStatus.dispatched;
3008
- safeWrite(statusPath, agentStatus);
3009
- } catch { /* agent reset */ }
3010
- killedAgents.add(activeEntry.agent);
3011
- }
3012
- }
3013
- w.status = WI_STATUS.PAUSED;
3014
- w._pausedBy = 'plan-steering';
3015
- delete w.dispatched_at;
3016
- delete w.dispatched_to;
3017
- delete w.failReason;
3018
- delete w.failedAt;
3019
- if (w.id) resetItemIds.add(w.id);
3020
- }
3021
- return items;
3022
- }, { defaultValue: [] });
3023
- } catch { /* reset work items */ }
3024
- }
3025
- if (resetItemIds.size > 0 || killedAgents.size > 0) {
3026
- mutateJsonFileLocked(dispatchPath, (dp) => {
3027
- dp.active = (dp.active || []).filter(d => {
3028
- if (d.meta?.item?.id && resetItemIds.has(d.meta.item.id)) return false;
3029
- if (killedAgents.has(d.agent)) return false;
3030
- return true;
3031
- });
3032
- return dp;
3033
- }, { defaultValue: { pending: [], active: [], completed: [] } });
3034
- }
3035
- invalidateStatusCache();
3036
- break;
3037
- }
3038
- }
3039
- } catch (e) { console.error('auto-pause PRD on plan steer:', e.message); }
3040
- }
3041
-
3042
- return jsonReply(res, 200, { ok: true, answer, edited: true, content, actions, pausedPrd });
2817
+ return jsonReply(res, 200, { ok: true, answer, edited: true, content, actions });
3043
2818
  }
3044
2819
  return jsonReply(res, 200, { ok: true, answer: answer + '\n\n(Read-only — changes not saved)', edited: false, actions });
3045
2820
  } finally { docChatInFlight.delete(docKey); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.880",
3
+ "version": "0.1.882",
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"