@yemi33/minions 0.1.1104 → 0.1.1106
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 +5 -1
- package/engine/ado.js +16 -3
- package/engine/github.js +13 -3
- package/engine/lifecycle.js +23 -0
- package/engine/queries.js +1 -1
- package/engine/spawn-agent.js +14 -2
- package/engine.js +8 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.1106 (2026-04-18)
|
|
4
4
|
|
|
5
5
|
### Features
|
|
6
6
|
- seed realActivityMap at spawn time, stamp pid in live-output (#1200)
|
|
7
7
|
|
|
8
8
|
### Fixes
|
|
9
|
+
- preserve buildErrorLog through transient build states (#1232) (#1274)
|
|
10
|
+
- annotate fast-exit empty-output failures with diagnostic hint (#1276)
|
|
11
|
+
- invalidate PRD cache on pr-links.json change + guard aggregate PRs (#1220) (#1272)
|
|
12
|
+
- pass --add-dir for minions + ~/.claude to agents (#1271)
|
|
9
13
|
- preserve VERDICT marker by tail-slicing agent output (#1234) (#1270)
|
|
10
14
|
- PRD info cache staleness and aggregate PR bleed-through (#1222)
|
|
11
15
|
- avoid no-op work item writes
|
package/engine/ado.js
CHANGED
|
@@ -465,6 +465,11 @@ async function pollPrStatus(config) {
|
|
|
465
465
|
} catch (e) { log('warn', `ADO build query for ${pr.id}: ${e.message}`); }
|
|
466
466
|
}
|
|
467
467
|
|
|
468
|
+
// Record actual poll time — makes lastBuildCheck reflect when the engine last
|
|
469
|
+
// talked to ADO, not when the agent was dispatched. Issue #1233.
|
|
470
|
+
pr.lastBuildCheck = ts();
|
|
471
|
+
updated = true;
|
|
472
|
+
|
|
468
473
|
if (pr.buildStatus !== buildStatus) {
|
|
469
474
|
log('info', `PR ${pr.id} build: ${pr.buildStatus || 'none'} → ${buildStatus}${buildFailReason ? ' (' + buildFailReason + ')' : ''}`);
|
|
470
475
|
pr.buildStatus = buildStatus;
|
|
@@ -475,9 +480,17 @@ async function pollPrStatus(config) {
|
|
|
475
480
|
if (buildStatus === 'failing') delete pr._autoCompleted;
|
|
476
481
|
if (buildStatus !== 'failing') {
|
|
477
482
|
delete pr._buildFailNotified;
|
|
478
|
-
|
|
479
|
-
//
|
|
480
|
-
|
|
483
|
+
// Preserve buildErrorLog + buildFixAttempts through transient 'none'/'running'
|
|
484
|
+
// transitions — only clear on confirmed 'passing' recovery. Issue #1232: 'none'
|
|
485
|
+
// can also occur when ADO recomputes the merge commit after a target-branch
|
|
486
|
+
// update but no new builds have been triggered yet (filter by sourceVersion
|
|
487
|
+
// returns []), which previously wiped the last known error log and caused
|
|
488
|
+
// fix agents to be dispatched blind.
|
|
489
|
+
if (buildStatus === 'passing') {
|
|
490
|
+
delete pr.buildErrorLog;
|
|
491
|
+
// Reset build fix retry counter on recovery — allows fresh auto-fix cycles if build breaks again
|
|
492
|
+
if (pr.buildFixAttempts) { delete pr.buildFixAttempts; delete pr.buildFixEscalated; }
|
|
493
|
+
}
|
|
481
494
|
}
|
|
482
495
|
updated = true;
|
|
483
496
|
|
package/engine/github.js
CHANGED
|
@@ -422,6 +422,10 @@ async function pollPrStatus(config) {
|
|
|
422
422
|
if (prData.state === 'open' && prData.head?.sha) {
|
|
423
423
|
const checksData = await ghApi(`/commits/${prData.head.sha}/check-runs`, slug);
|
|
424
424
|
if (checksData && checksData.check_runs) {
|
|
425
|
+
// Record actual poll time — makes lastBuildCheck reflect when the engine last
|
|
426
|
+
// talked to GitHub, not when the agent was dispatched. Issue #1233.
|
|
427
|
+
pr.lastBuildCheck = ts();
|
|
428
|
+
updated = true;
|
|
425
429
|
const runs = checksData.check_runs;
|
|
426
430
|
let buildStatus = 'none';
|
|
427
431
|
let buildFailReason = '';
|
|
@@ -452,9 +456,15 @@ async function pollPrStatus(config) {
|
|
|
452
456
|
if (buildStatus === 'failing') delete pr._autoCompleted; // allow re-merge after fix
|
|
453
457
|
if (buildStatus !== 'failing') {
|
|
454
458
|
delete pr._buildFailNotified;
|
|
455
|
-
|
|
456
|
-
//
|
|
457
|
-
|
|
459
|
+
// Preserve buildErrorLog + buildFixAttempts through transient 'none'/'running'
|
|
460
|
+
// transitions — only clear on confirmed 'passing' recovery. Issue #1232:
|
|
461
|
+
// clearing on every non-failing transition blinded the next fix agent
|
|
462
|
+
// while a queued build was still running.
|
|
463
|
+
if (buildStatus === 'passing') {
|
|
464
|
+
delete pr.buildErrorLog;
|
|
465
|
+
// Reset build fix retry counter on recovery — allows fresh auto-fix cycles if build breaks again
|
|
466
|
+
if (pr.buildFixAttempts) { delete pr.buildFixAttempts; delete pr.buildFixEscalated; }
|
|
467
|
+
}
|
|
458
468
|
}
|
|
459
469
|
updated = true;
|
|
460
470
|
|
package/engine/lifecycle.js
CHANGED
|
@@ -2118,6 +2118,28 @@ function classifyFailure(code, stdout = '', stderr = '') {
|
|
|
2118
2118
|
return FAILURE_CLASS.UNKNOWN;
|
|
2119
2119
|
}
|
|
2120
2120
|
|
|
2121
|
+
/**
|
|
2122
|
+
* When a claude CLI agent exits in <3s with code 1 and no output, the raw
|
|
2123
|
+
* EMPTY_OUTPUT class tells us "no meaningful output" but nothing about WHY.
|
|
2124
|
+
* This helper detects the fast-exit pattern and returns a diagnostic hint
|
|
2125
|
+
* listing likely root causes (machine sleep, network loss, auth failure).
|
|
2126
|
+
*
|
|
2127
|
+
* The hint is propagated as the dispatch `reason` and into the work-item
|
|
2128
|
+
* `failReason`, so humans and other agents can triage without digging into
|
|
2129
|
+
* `agents/*\/output-<id>.log`.
|
|
2130
|
+
*
|
|
2131
|
+
* @param {string|undefined} failureClass — one of FAILURE_CLASS values
|
|
2132
|
+
* @param {number} code — process exit code
|
|
2133
|
+
* @param {number} elapsedMs — milliseconds between spawn and close
|
|
2134
|
+
* @returns {string|null} — annotated hint, or null when the pattern doesn't match
|
|
2135
|
+
*/
|
|
2136
|
+
function diagnoseEmptyOutput(failureClass, code, elapsedMs) {
|
|
2137
|
+
if (failureClass !== FAILURE_CLASS.EMPTY_OUTPUT) return null;
|
|
2138
|
+
if (code !== 1) return null;
|
|
2139
|
+
if (!Number.isFinite(elapsedMs) || elapsedMs < 0 || elapsedMs >= 3000) return null;
|
|
2140
|
+
return `[empty-output: process exited in ${elapsedMs}ms \u2014 possible causes: machine sleep, network unavailability, auth failure]`;
|
|
2141
|
+
}
|
|
2142
|
+
|
|
2121
2143
|
module.exports = {
|
|
2122
2144
|
checkPlanCompletion,
|
|
2123
2145
|
archivePlan,
|
|
@@ -2142,6 +2164,7 @@ module.exports = {
|
|
|
2142
2164
|
resolveWorkItemPath,
|
|
2143
2165
|
isItemCompleted,
|
|
2144
2166
|
classifyFailure,
|
|
2167
|
+
diagnoseEmptyOutput,
|
|
2145
2168
|
processPendingRebases,
|
|
2146
2169
|
findDependentActivePrs,
|
|
2147
2170
|
};
|
package/engine/queries.js
CHANGED
|
@@ -1028,7 +1028,7 @@ function getPrdInfo(config) {
|
|
|
1028
1028
|
const pr = prById[prId];
|
|
1029
1029
|
// Skip aggregate / E2E PRs from per-item mapping — they link to multiple items
|
|
1030
1030
|
// (or are typed as verify) and would bleed through as duplicate entries on every
|
|
1031
|
-
// constituent item. They are surfaced via renderE2eSection instead.
|
|
1031
|
+
// constituent item. They are surfaced via renderE2eSection instead. (#1220)
|
|
1032
1032
|
if ((itemIds || []).length > 1 || pr?.itemType === 'verify' || pr?.title?.startsWith('[E2E]')) continue;
|
|
1033
1033
|
const project = projects.find(p => p.name === pr?._project) || projects[0] || null;
|
|
1034
1034
|
const prNumber = shared.getPrNumber(pr || prId);
|
package/engine/spawn-agent.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const fs = require('fs');
|
|
10
|
+
const os = require('os');
|
|
10
11
|
const path = require('path');
|
|
11
12
|
const { exec, runFile, cleanChildEnv, killGracefully, killImmediate, ts, safeJson, safeWrite } = require('./shared');
|
|
12
13
|
|
|
@@ -103,16 +104,27 @@ if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir, { recursive: true });
|
|
|
103
104
|
const debugPath = path.join(tmpDir, 'spawn-debug.log');
|
|
104
105
|
fs.writeFile(debugPath, `spawn-agent.js at ${ts()}\nclaudeBin=${claudeBin || 'not found'}\nnative=${claudeIsNative}\nprompt=${promptFile}\nsysPrompt=${sysPromptFile}\nextraArgs=${extraArgs.join(' ')}\n`, () => {});
|
|
105
106
|
|
|
107
|
+
// Skill discovery dirs — agents run with CWD set to an external repo worktree,
|
|
108
|
+
// so skills in the minions repo and the user's global ~/.claude dir are otherwise
|
|
109
|
+
// invisible. Pass them via --add-dir on every invocation (resume included, since
|
|
110
|
+
// each resume spawns a fresh CLI process with the same CWD). See issue #1231.
|
|
111
|
+
const minionsDir = path.resolve(__dirname, '..');
|
|
112
|
+
const userClaudeDir = path.join(os.homedir(), '.claude');
|
|
113
|
+
const addDirArgs = ['--add-dir', minionsDir];
|
|
114
|
+
if (fs.existsSync(userClaudeDir) && path.resolve(userClaudeDir) !== path.resolve(minionsDir)) {
|
|
115
|
+
addDirArgs.push('--add-dir', userClaudeDir);
|
|
116
|
+
}
|
|
117
|
+
|
|
106
118
|
// When resuming a session, skip system prompt (it's baked into the session)
|
|
107
119
|
const isResume = extraArgs.includes('--resume');
|
|
108
120
|
const sysTmpPath = sysPromptFile + '.tmp';
|
|
109
121
|
let cliArgs;
|
|
110
122
|
if (isResume) {
|
|
111
|
-
cliArgs = ['-p', ...extraArgs];
|
|
123
|
+
cliArgs = ['-p', ...addDirArgs, ...extraArgs];
|
|
112
124
|
} else {
|
|
113
125
|
// Pass system prompt via file to avoid ENAMETOOLONG on Windows (32KB arg limit)
|
|
114
126
|
fs.writeFileSync(sysTmpPath, sysPrompt);
|
|
115
|
-
cliArgs = ['-p', '--system-prompt-file', sysTmpPath, ...extraArgs];
|
|
127
|
+
cliArgs = ['-p', '--system-prompt-file', sysTmpPath, ...addDirArgs, ...extraArgs];
|
|
116
128
|
}
|
|
117
129
|
|
|
118
130
|
if (!claudeBin) {
|
package/engine.js
CHANGED
|
@@ -138,7 +138,7 @@ const { renderPlaybook, validatePlaybookVars, PLAYBOOK_REQUIRED_VARS,
|
|
|
138
138
|
const { runPostCompletionHooks, updateWorkItemStatus, syncPrdItemStatus, reconcilePrdStatuses, handlePostMerge, checkPlanCompletion,
|
|
139
139
|
syncPrsFromOutput, updatePrAfterReview, updatePrAfterFix, checkForLearnings, extractSkillsFromOutput,
|
|
140
140
|
updateAgentHistory, updateMetrics, createReviewFeedbackForAuthor, parseAgentOutput, syncPrdFromPrs,
|
|
141
|
-
isItemCompleted, classifyFailure, processPendingRebases, resolveWorkItemPath } = require('./engine/lifecycle');
|
|
141
|
+
isItemCompleted, classifyFailure, diagnoseEmptyOutput, processPendingRebases, resolveWorkItemPath } = require('./engine/lifecycle');
|
|
142
142
|
|
|
143
143
|
// ─── Agent Spawner ──────────────────────────────────────────────────────────
|
|
144
144
|
|
|
@@ -1231,6 +1231,13 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
1231
1231
|
let errorReason = '';
|
|
1232
1232
|
if (effectiveResult === DISPATCH_RESULT.ERROR) {
|
|
1233
1233
|
errorReason = stderr.split('\n').filter(l => l.trim()).slice(-5).join(' | ').trim().slice(0, 300);
|
|
1234
|
+
// W-mo3zul9pirjb — when claude CLI exits in <3s with code 1 and no output (the
|
|
1235
|
+
// "silent crash" pattern seen during scheduled tasks when the box went to sleep
|
|
1236
|
+
// or lost network), prepend a diagnostic hint so failReason/dispatch log carry
|
|
1237
|
+
// the actual elapsed time and likely root causes rather than a bare "[empty-output]".
|
|
1238
|
+
const elapsedMs = Date.now() - Date.parse(startedAt);
|
|
1239
|
+
const hint = diagnoseEmptyOutput(failureClass, code, elapsedMs);
|
|
1240
|
+
if (hint) errorReason = errorReason ? `${hint} ${errorReason}` : hint;
|
|
1234
1241
|
}
|
|
1235
1242
|
completeDispatch(id, effectiveResult, errorReason, resultSummary, completeOpts);
|
|
1236
1243
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1106",
|
|
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"
|