@yemi33/minions 0.1.101 → 0.1.103

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,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.103 (2026-04-01)
4
+
5
+ ### Engine
6
+ - engine/lifecycle.js
7
+ - engine/meeting.js
8
+ - engine/shared.js
9
+
10
+ ### Other
11
+ - test/unit.test.js
12
+
13
+ ## 0.1.102 (2026-04-01)
14
+
15
+ ### Engine
16
+ - engine/scheduler.js
17
+
18
+ ### Other
19
+ - test/unit.test.js
20
+
3
21
  ## 0.1.101 (2026-04-01)
4
22
 
5
23
  ### 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
@@ -892,8 +885,7 @@ function createReviewFeedbackForAuthor(reviewerAgentId, pr, config) {
892
885
  const reviewFiles = inboxFiles.filter(f => f.includes(reviewerAgentId) && f.includes(today));
893
886
  if (reviewFiles.length === 0) return;
894
887
  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));
888
+ const slug = `feedback-from-${reviewerAgentId}-${pr.id}`;
897
889
  const content = `# Review Feedback for ${config.agents[authorAgentId]?.name || authorAgentId}\n\n` +
898
890
  `**PR:** ${pr.id} — ${pr.title || ''}\n` +
899
891
  `**Reviewer:** ${config.agents[reviewerAgentId]?.name || reviewerAgentId}\n` +
@@ -902,8 +894,8 @@ function createReviewFeedbackForAuthor(reviewerAgentId, pr, config) {
902
894
  `## Action Required\n\nRead this feedback carefully. When you work on similar tasks in the future, ` +
903
895
  `avoid the patterns flagged here. If you are assigned to fix this PR, ` +
904
896
  `address every point raised above.\n`;
905
- shared.safeWrite(feedbackPath, content);
906
- log('info', `Created review feedback for ${authorAgentId} from ${reviewerAgentId} on ${pr.id}`);
897
+ const wrote = shared.writeToInbox(authorAgentId, slug, content);
898
+ if (wrote) log('info', `Created review feedback for ${authorAgentId} from ${reviewerAgentId} on ${pr.id}`);
907
899
  }
908
900
 
909
901
  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);
@@ -61,11 +61,11 @@ function parseCronField(field, min, max) {
61
61
  function parseCronExpr(expr) {
62
62
  if (!expr || typeof expr !== 'string') return null;
63
63
  const parts = expr.trim().split(/\s+/);
64
- if (parts.length < 2 || parts.length > 3) return null;
64
+ if (parts.length !== 3) return null;
65
65
 
66
66
  const minuteMatcher = parseCronField(parts[0], 0, 59);
67
67
  const hourMatcher = parseCronField(parts[1], 0, 23);
68
- const dowMatcher = parts[2] ? parseCronField(parts[2], 0, 6) : () => true;
68
+ const dowMatcher = parseCronField(parts[2], 0, 6);
69
69
 
70
70
  return {
71
71
  matches(date) {
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.101",
3
+ "version": "0.1.103",
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"