@yemi33/minions 0.1.102 → 0.1.104

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,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.104 (2026-04-01)
4
+
5
+ ### Engine
6
+ - engine/lifecycle.js
7
+
8
+ ## 0.1.103 (2026-04-01)
9
+
10
+ ### Engine
11
+ - engine/lifecycle.js
12
+ - engine/meeting.js
13
+ - engine/shared.js
14
+
15
+ ### Other
16
+ - test/unit.test.js
17
+
3
18
  ## 0.1.102 (2026-04-01)
4
19
 
5
20
  ### Engine
@@ -137,17 +137,10 @@ function checkPlanCompletion(meta, config) {
137
137
  ...uniquePrs.map(pr => `- ${pr.id}: ${pr.title || ''} ${pr.url || ''}`),
138
138
  ].filter(Boolean).join('\n');
139
139
 
140
- // Write summary to notes/inbox (slug+date dedup same pattern as writeInboxAlert in dispatch.js)
141
- const summarySlug = `prd-completion-${planFile.replace('.json', '')}`;
142
- const summaryFile = `${summarySlug}-${dateStamp()}.md`;
143
- const inboxDir = path.join(MINIONS_DIR, 'notes', 'inbox');
144
- const existing = safeReadDir(inboxDir).find(f => f.startsWith(`${summarySlug}-${dateStamp()}`));
145
- if (!existing) {
146
- shared.safeWrite(path.join(inboxDir, summaryFile), summary);
147
- log('info', `PRD completion summary written to notes/inbox/${summaryFile}`);
148
- } else {
149
- log('info', `PRD completion summary already exists for today: ${existing}, skipping inbox write`);
150
- }
140
+ // Write summary to notes/inbox (slug-based dedup prevents duplicates on same day)
141
+ const slug = `prd-completion-${planFile.replace('.json', '')}`;
142
+ const wrote = shared.writeToInbox('engine', slug, summary);
143
+ if (wrote) log('info', `PRD completion summary written to notes/inbox/`);
151
144
 
152
145
  // Persist _completionNotified flag atomically BEFORE creating work items.
153
146
  // This prevents duplicate inbox notes on re-entry. Work item creation below has its own
@@ -731,8 +724,15 @@ async function handlePostMerge(pr, project, config, newStatus) {
731
724
 
732
725
  if (newStatus !== 'merged') return;
733
726
 
734
- const mergedItemId = getPrLinks()[pr.id];
727
+ // Resolve linked work item from pr-links or PR branch name
728
+ let mergedItemId = getPrLinks()[pr.id];
729
+ if (!mergedItemId && pr.branch) {
730
+ const branchMatch = pr.branch.match(/(P-[a-z0-9]{6,})/i) || pr.branch.match(/(W-[a-z0-9]+)/i);
731
+ if (branchMatch) mergedItemId = branchMatch[1];
732
+ }
733
+
735
734
  if (mergedItemId) {
735
+ // Mark PRD feature as implemented
736
736
  const prdDir = path.join(MINIONS_DIR, 'prd');
737
737
  try {
738
738
  const planFiles = fs.readdirSync(prdDir).filter(f => f.endsWith('.json'));
@@ -749,6 +749,25 @@ async function handlePostMerge(pr, project, config, newStatus) {
749
749
  }
750
750
  if (updated > 0) log('info', `Post-merge: marked ${mergedItemId} as implemented for ${pr.id}`);
751
751
  } catch (err) { log('warn', `Post-merge PRD update: ${err.message}`); }
752
+
753
+ // Mark work item as done
754
+ const wiPaths = [path.join(MINIONS_DIR, 'work-items.json')];
755
+ for (const p of shared.getProjects(config)) wiPaths.push(shared.projectWorkItemsPath(p));
756
+ for (const wiPath of wiPaths) {
757
+ try {
758
+ const items = safeJson(wiPath);
759
+ if (!items) continue;
760
+ const item = items.find(i => i.id === mergedItemId);
761
+ if (item && item.status !== 'done') {
762
+ log('info', `Post-merge: marking work item ${mergedItemId} as done (was ${item.status}) for ${pr.id}`);
763
+ item.status = 'done';
764
+ item.completedAt = e.ts();
765
+ item._mergedVia = pr.id;
766
+ shared.safeWrite(wiPath, items);
767
+ break;
768
+ }
769
+ } catch (err) { log('warn', `Post-merge work item update: ${err.message}`); }
770
+ }
752
771
  }
753
772
 
754
773
  const agentId = (pr.agent || '').toLowerCase();
@@ -892,8 +911,7 @@ function createReviewFeedbackForAuthor(reviewerAgentId, pr, config) {
892
911
  const reviewFiles = inboxFiles.filter(f => f.includes(reviewerAgentId) && f.includes(today));
893
912
  if (reviewFiles.length === 0) return;
894
913
  const reviewContent = reviewFiles.map(f => safeRead(path.join(INBOX_DIR, f))).join('\n\n');
895
- const feedbackFile = `feedback-${authorAgentId}-from-${reviewerAgentId}-${pr.id}-${today}.md`;
896
- const feedbackPath = shared.uniquePath(path.join(INBOX_DIR, feedbackFile));
914
+ const slug = `feedback-from-${reviewerAgentId}-${pr.id}`;
897
915
  const content = `# Review Feedback for ${config.agents[authorAgentId]?.name || authorAgentId}\n\n` +
898
916
  `**PR:** ${pr.id} — ${pr.title || ''}\n` +
899
917
  `**Reviewer:** ${config.agents[reviewerAgentId]?.name || reviewerAgentId}\n` +
@@ -902,8 +920,8 @@ function createReviewFeedbackForAuthor(reviewerAgentId, pr, config) {
902
920
  `## Action Required\n\nRead this feedback carefully. When you work on similar tasks in the future, ` +
903
921
  `avoid the patterns flagged here. If you are assigned to fix this PR, ` +
904
922
  `address every point raised above.\n`;
905
- shared.safeWrite(feedbackPath, content);
906
- log('info', `Created review feedback for ${authorAgentId} from ${reviewerAgentId} on ${pr.id}`);
923
+ const wrote = shared.writeToInbox(authorAgentId, slug, content);
924
+ if (wrote) log('info', `Created review feedback for ${authorAgentId} from ${reviewerAgentId} on ${pr.id}`);
907
925
  }
908
926
 
909
927
  function updateMetrics(agentId, dispatchItem, result, taskUsage, prsCreatedCount, model) {
package/engine/meeting.js CHANGED
@@ -206,15 +206,13 @@ function collectMeetingFindings(meetingId, agentId, roundName, output) {
206
206
  meeting.status = 'completed';
207
207
  meeting.completedAt = new Date().toISOString();
208
208
 
209
- // Write transcript to inbox so agents learn from it
209
+ // Write transcript to inbox so agents learn from it (slug-based dedup)
210
210
  const config = queries.getConfig();
211
211
  const agents = config.agents || {};
212
212
  const transcript = meeting.transcript.map(t =>
213
213
  `### ${agents[t.agent]?.name || t.agent} (${t.type}, Round ${t.round})\n\n${t.content}`
214
214
  ).join('\n\n---\n\n');
215
- const inboxPath = path.join(__dirname, '..', 'notes', 'inbox',
216
- `meeting-${meetingId}-${new Date().toISOString().slice(0, 10)}.md`);
217
- safeWrite(inboxPath, `# Meeting Transcript: ${meeting.title}\n\n${transcript}`);
215
+ shared.writeToInbox('meeting', meetingId, `# Meeting Transcript: ${meeting.title}\n\n${transcript}`);
218
216
 
219
217
  log('info', `Meeting ${meetingId} completed — transcript written to inbox`);
220
218
  saveMeeting(meeting);
package/engine/shared.js CHANGED
@@ -169,6 +169,33 @@ function uniquePath(filePath) {
169
169
  return `${base}-${Date.now()}${ext}`;
170
170
  }
171
171
 
172
+ // ── Inbox Helpers ───────────────────────────────────────────────────────────
173
+
174
+ /**
175
+ * Write a file to notes/inbox/ with slug+date-based dedup.
176
+ * Filename: `{agentId}-{slug}-{YYYY-MM-DD}.md`
177
+ * If a file with the same prefix already exists for today, skip the write.
178
+ * Pattern matches writeInboxAlert() in dispatch.js.
179
+ * @param {string} agentId - Agent or source identifier (e.g. 'engine', 'ralph')
180
+ * @param {string} slug - Short descriptive slug (e.g. 'prd-completion-plan1')
181
+ * @param {string} content - Markdown content to write
182
+ * @returns {boolean} true if a write occurred, false if deduped/skipped
183
+ */
184
+ function writeToInbox(agentId, slug, content, _inboxDir) {
185
+ try {
186
+ const inboxDir = _inboxDir || path.join(MINIONS_DIR, 'notes', 'inbox');
187
+ const prefix = `${agentId}-${slug}-${dateStamp()}`;
188
+ const existing = safeReadDir(inboxDir).find(f => f.startsWith(prefix));
189
+ if (existing) return false;
190
+ const filePath = path.join(inboxDir, `${prefix}.md`);
191
+ safeWrite(filePath, content);
192
+ return true;
193
+ } catch (e) {
194
+ log('warn', `writeToInbox failed: ${e.message}`);
195
+ return false;
196
+ }
197
+ }
198
+
172
199
  // ── Process Spawning ────────────────────────────────────────────────────────
173
200
  // All child process calls go through these to ensure windowsHide: true
174
201
 
@@ -479,6 +506,7 @@ module.exports = {
479
506
  mutateJsonFileLocked,
480
507
  uid,
481
508
  uniquePath,
509
+ writeToInbox,
482
510
  exec,
483
511
  execSilent,
484
512
  run,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.102",
3
+ "version": "0.1.104",
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"