@yemi33/minions 0.1.1683 → 0.1.1685
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 +10 -0
- package/engine/cooldown.js +1 -0
- package/engine/copilot-models.json +1 -1
- package/engine/playbook.js +3 -2
- package/engine/runtimes/claude.js +28 -23
- package/engine/runtimes/copilot.js +36 -35
- package/engine/spawn-agent.js +66 -12
- package/engine/timeout.js +38 -0
- package/engine.js +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.1685 (2026-05-02)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
- harden agent completion sentinel (#1990)
|
|
7
|
+
|
|
8
|
+
## 0.1.1684 (2026-05-02)
|
|
9
|
+
|
|
10
|
+
### Other
|
|
11
|
+
- test(cooldown): add edge-case unit tests for _truncateContextEntry, loadCooldowns boundaries, isAlreadyDispatched (#1986)
|
|
12
|
+
|
|
3
13
|
## 0.1.1683 (2026-05-02)
|
|
4
14
|
|
|
5
15
|
### Fixes
|
package/engine/cooldown.js
CHANGED
package/engine/playbook.js
CHANGED
|
@@ -395,7 +395,7 @@ function renderPlaybook(type, vars) {
|
|
|
395
395
|
content += '````\n```skill\n';
|
|
396
396
|
content += `---\nname: short-descriptive-name\ndescription: One-line description of what this skill does\nallowed-tools: Bash, Read, Edit\ntrigger: when should an agent use this\nscope: minions\nproject: any\n---\n\n# Skill Title\n\n## Steps\n1. ...\n2. ...\n\n## Notes\n...\n`;
|
|
397
397
|
content += '```\n````\n\n';
|
|
398
|
-
content += `- Set \`scope: minions\` for cross-project or Minions-wide skills; the engine writes them to the selected runtime's native personal skills directory\n`;
|
|
398
|
+
content += `- Set \`scope: minions\` for cross-project or Minions-wide skills; the engine writes them to the selected runtime's native personal skills directory so they are available in normal runtime windows too\n`;
|
|
399
399
|
content += `- Set \`scope: project\` + \`project: <name>\` only for repo-specific skills; the engine queues a PR to the selected runtime's native project skills directory\n`;
|
|
400
400
|
content += `- Emit at most one skill block per task unless you uncovered two clearly distinct reusable workflows\n`;
|
|
401
401
|
content += `- Do NOT create a skill for one-off bug fixes, isolated command output, obvious repo facts, or anything already covered by existing docs/playbooks/skills\n`;
|
|
@@ -546,7 +546,8 @@ function buildAgentContext(agentId, config, project) {
|
|
|
546
546
|
context += `## Your Recent History (last 5 tasks)\n\n${trimmed}\n\n`;
|
|
547
547
|
}
|
|
548
548
|
|
|
549
|
-
// Project conventions
|
|
549
|
+
// Project conventions and instruction files — explicit, inert context for
|
|
550
|
+
// every runtime. Runtime-native skills/commands stay in their native indexes.
|
|
550
551
|
if (project.localPath) {
|
|
551
552
|
appendContextFile('Project Conventions (from CLAUDE.md)', path.join(project.localPath, 'CLAUDE.md'), 8192);
|
|
552
553
|
appendContextFile('Project Agent Instructions (from AGENTS.md)', path.join(project.localPath, 'AGENTS.md'), 8192,
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
* - spawnScript: absolute path of the spawn wrapper (or null if direct-only)
|
|
17
17
|
* - buildArgs(opts) → string[] — CLI args excluding the binary
|
|
18
18
|
* - buildPrompt(promptText, sysPromptText) → string — final prompt delivered
|
|
19
|
+
* - getUserAssetDirs(opts) → string[] — runtime-native global asset roots
|
|
20
|
+
* - getSkillRoots(opts) → {scope,dir,projectName?}[] — skill discovery roots
|
|
21
|
+
* - getSkillWriteTargets(opts) → {personal,project} — extraction targets
|
|
19
22
|
* - resolveModel(input) → string|undefined — shorthand expansion / passthrough
|
|
20
23
|
* - parseOutput(raw) → { text, usage, sessionId, model }
|
|
21
24
|
* - parseStreamChunk(line) → parsed event object or null
|
|
@@ -247,6 +250,31 @@ function buildSpawnFlags(opts = {}) {
|
|
|
247
250
|
return flags;
|
|
248
251
|
}
|
|
249
252
|
|
|
253
|
+
function getUserAssetDirs({ homeDir = os.homedir() } = {}) {
|
|
254
|
+
return [path.join(homeDir, '.claude')];
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function getSkillRoots({ homeDir = os.homedir(), project = null } = {}) {
|
|
258
|
+
const roots = [
|
|
259
|
+
{ scope: 'claude-code', dir: path.join(homeDir, '.claude', 'skills') },
|
|
260
|
+
];
|
|
261
|
+
if (project?.localPath) {
|
|
262
|
+
roots.push({
|
|
263
|
+
scope: 'project',
|
|
264
|
+
projectName: project.name,
|
|
265
|
+
dir: path.join(project.localPath, '.claude', 'skills'),
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
return roots;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function getSkillWriteTargets({ homeDir = os.homedir(), project = null } = {}) {
|
|
272
|
+
return {
|
|
273
|
+
personal: path.join(homeDir, '.claude', 'skills'),
|
|
274
|
+
project: project?.localPath ? path.join(project.localPath, '.claude', 'skills') : null,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
250
278
|
function getResumeSessionId({ agentId, branchName, agentsDir, maxAgeMs = 2 * 60 * 60 * 1000, logger = console } = {}) {
|
|
251
279
|
if (!agentId || agentId.startsWith('temp-') || !agentsDir) return null;
|
|
252
280
|
try {
|
|
@@ -322,29 +350,6 @@ function buildPrompt(promptText, /* sysPromptText */ _sys) {
|
|
|
322
350
|
return String(promptText == null ? '' : promptText);
|
|
323
351
|
}
|
|
324
352
|
|
|
325
|
-
function getUserAssetDirs({ homeDir = os.homedir() } = {}) {
|
|
326
|
-
return [path.join(homeDir, '.claude')];
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
function getSkillRoots({ homeDir = os.homedir(), project = null } = {}) {
|
|
330
|
-
const roots = [{ dir: path.join(homeDir, '.claude', 'skills'), scope: 'claude-code' }];
|
|
331
|
-
if (project?.localPath) {
|
|
332
|
-
roots.push({
|
|
333
|
-
dir: path.resolve(project.localPath, '.claude', 'skills'),
|
|
334
|
-
scope: 'project',
|
|
335
|
-
projectName: project.name,
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
return roots;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
function getSkillWriteTargets({ homeDir = os.homedir(), project = null } = {}) {
|
|
342
|
-
return {
|
|
343
|
-
personal: path.join(homeDir, '.claude', 'skills'),
|
|
344
|
-
project: project?.localPath ? path.resolve(project.localPath, '.claude', 'skills') : null,
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
|
|
348
353
|
// ── Output Parsing ───────────────────────────────────────────────────────────
|
|
349
354
|
|
|
350
355
|
/**
|
|
@@ -270,6 +270,42 @@ function buildSpawnFlags(opts = {}) {
|
|
|
270
270
|
return flags;
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
+
function getUserAssetDirs({ homeDir = os.homedir() } = {}) {
|
|
274
|
+
return [
|
|
275
|
+
path.join(homeDir, '.copilot'),
|
|
276
|
+
path.join(homeDir, '.agents'),
|
|
277
|
+
];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function getSkillRoots({ homeDir = os.homedir(), project = null } = {}) {
|
|
281
|
+
const roots = [
|
|
282
|
+
{ scope: 'copilot', dir: path.join(homeDir, '.copilot', 'skills') },
|
|
283
|
+
{ scope: 'agent-skill', dir: path.join(homeDir, '.agents', 'skills') },
|
|
284
|
+
];
|
|
285
|
+
if (project?.localPath) {
|
|
286
|
+
roots.push(
|
|
287
|
+
{
|
|
288
|
+
scope: 'project',
|
|
289
|
+
projectName: project.name,
|
|
290
|
+
dir: path.join(project.localPath, '.github', 'skills'),
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
scope: 'project',
|
|
294
|
+
projectName: project.name,
|
|
295
|
+
dir: path.join(project.localPath, '.agents', 'skills'),
|
|
296
|
+
},
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
return roots;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function getSkillWriteTargets({ homeDir = os.homedir(), project = null } = {}) {
|
|
303
|
+
return {
|
|
304
|
+
personal: path.join(homeDir, '.copilot', 'skills'),
|
|
305
|
+
project: project?.localPath ? path.join(project.localPath, '.github', 'skills') : null,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
273
309
|
function getResumeSessionId({ agentId, branchName, agentsDir, maxAgeMs = 2 * 60 * 60 * 1000, logger = console } = {}) {
|
|
274
310
|
if (!agentId || agentId.startsWith('temp-') || !agentsDir) return null;
|
|
275
311
|
try {
|
|
@@ -349,41 +385,6 @@ function buildPrompt(promptText, sysPromptText, opts = {}) {
|
|
|
349
385
|
return `<system>\n${String(sysPromptText)}\n</system>\n\n${user}`;
|
|
350
386
|
}
|
|
351
387
|
|
|
352
|
-
function getUserAssetDirs({ homeDir = os.homedir() } = {}) {
|
|
353
|
-
return [
|
|
354
|
-
path.join(homeDir, '.copilot'),
|
|
355
|
-
path.join(homeDir, '.agents'),
|
|
356
|
-
];
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
function getSkillRoots({ homeDir = os.homedir(), project = null } = {}) {
|
|
360
|
-
const roots = [
|
|
361
|
-
{ dir: path.join(homeDir, '.copilot', 'skills'), scope: 'copilot' },
|
|
362
|
-
{ dir: path.join(homeDir, '.agents', 'skills'), scope: 'agent-skill' },
|
|
363
|
-
];
|
|
364
|
-
if (project?.localPath) {
|
|
365
|
-
for (const rel of [
|
|
366
|
-
['.github', 'skills'],
|
|
367
|
-
['.claude', 'skills'],
|
|
368
|
-
['.agents', 'skills'],
|
|
369
|
-
]) {
|
|
370
|
-
roots.push({
|
|
371
|
-
dir: path.resolve(project.localPath, ...rel),
|
|
372
|
-
scope: 'project',
|
|
373
|
-
projectName: project.name,
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
return roots;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
function getSkillWriteTargets({ homeDir = os.homedir(), project = null } = {}) {
|
|
381
|
-
return {
|
|
382
|
-
personal: path.join(homeDir, '.copilot', 'skills'),
|
|
383
|
-
project: project?.localPath ? path.resolve(project.localPath, '.github', 'skills') : null,
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
|
|
387
388
|
// ── Output Parsing ──────────────────────────────────────────────────────────
|
|
388
389
|
//
|
|
389
390
|
// Whitelist of event types observed during the spike (docs/copilot-cli-schema.md
|
package/engine/spawn-agent.js
CHANGED
|
@@ -128,6 +128,57 @@ function normalizeRuntimeExit(code, signal) {
|
|
|
128
128
|
return 1;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
const PROCESS_EXIT_SENTINEL_FLUSH_TIMEOUT_MS = 2000;
|
|
132
|
+
|
|
133
|
+
function formatProcessExitSentinel(exitCode, signal) {
|
|
134
|
+
return `\n[process-exit] code=${exitCode}${signal ? ` signal=${signal}` : ''}\n`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function _appendSentinelFallback(outputPath, sentinel) {
|
|
138
|
+
if (!outputPath) return false;
|
|
139
|
+
try {
|
|
140
|
+
fs.appendFileSync(outputPath, sentinel);
|
|
141
|
+
return true;
|
|
142
|
+
} catch {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function _writeStdoutWithTimeout(stdout, sentinel, timeoutMs) {
|
|
148
|
+
return new Promise((resolve) => {
|
|
149
|
+
let settled = false;
|
|
150
|
+
const finish = (flushed) => {
|
|
151
|
+
if (settled) return;
|
|
152
|
+
settled = true;
|
|
153
|
+
clearTimeout(timer);
|
|
154
|
+
resolve(flushed);
|
|
155
|
+
};
|
|
156
|
+
const timer = setTimeout(() => finish(false), Math.max(0, timeoutMs));
|
|
157
|
+
try {
|
|
158
|
+
if (!stdout || typeof stdout.write !== 'function') {
|
|
159
|
+
finish(false);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
stdout.write(sentinel, () => finish(true));
|
|
163
|
+
} catch {
|
|
164
|
+
finish(false);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function writeProcessExitSentinel({
|
|
170
|
+
exitCode,
|
|
171
|
+
signal = null,
|
|
172
|
+
stdout = process.stdout,
|
|
173
|
+
outputPath = process.env.MINIONS_LIVE_OUTPUT_PATH,
|
|
174
|
+
timeoutMs = PROCESS_EXIT_SENTINEL_FLUSH_TIMEOUT_MS,
|
|
175
|
+
} = {}) {
|
|
176
|
+
const sentinel = formatProcessExitSentinel(exitCode, signal);
|
|
177
|
+
const stdoutFlushed = await _writeStdoutWithTimeout(stdout, sentinel, timeoutMs);
|
|
178
|
+
const outputPathWritten = stdoutFlushed ? false : _appendSentinelFallback(outputPath, sentinel);
|
|
179
|
+
return { sentinel, stdoutFlushed, outputPathWritten };
|
|
180
|
+
}
|
|
181
|
+
|
|
131
182
|
// ─── Main script execution ──────────────────────────────────────────────────
|
|
132
183
|
|
|
133
184
|
function _installHint(name, runtime) {
|
|
@@ -174,17 +225,17 @@ function main() {
|
|
|
174
225
|
opts.sysPromptFile = sysTmpPath;
|
|
175
226
|
}
|
|
176
227
|
|
|
177
|
-
//
|
|
178
|
-
// worktree, so
|
|
179
|
-
//
|
|
228
|
+
// Skill discovery dirs — agents run with CWD set to an external repo
|
|
229
|
+
// worktree, so runtime-native global assets would otherwise be invisible.
|
|
230
|
+
// The adapter owns both where those assets live and how to surface them.
|
|
180
231
|
const minionsDir = path.resolve(__dirname, '..');
|
|
232
|
+
const addDirs = [minionsDir];
|
|
181
233
|
const runtimeAssetDirs = typeof runtime.getUserAssetDirs === 'function'
|
|
182
234
|
? runtime.getUserAssetDirs({ homeDir: os.homedir() })
|
|
183
235
|
: [];
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
addDirs.push(userAssetDir);
|
|
236
|
+
for (const dir of runtimeAssetDirs) {
|
|
237
|
+
if (dir && fs.existsSync(dir) && path.resolve(dir) !== path.resolve(minionsDir)) {
|
|
238
|
+
addDirs.push(dir);
|
|
188
239
|
}
|
|
189
240
|
}
|
|
190
241
|
|
|
@@ -287,20 +338,23 @@ function main() {
|
|
|
287
338
|
}, MCP_STARTUP_TIMEOUT);
|
|
288
339
|
proc.stdout.once('data', () => { gotFirstOutput = true; clearTimeout(startupTimer); });
|
|
289
340
|
|
|
290
|
-
proc.on('close', (code, signal) => {
|
|
341
|
+
proc.on('close', async (code, signal) => {
|
|
291
342
|
clearTimeout(startupTimer);
|
|
292
343
|
const exitCode = normalizeRuntimeExit(code, signal);
|
|
293
|
-
|
|
294
|
-
try { process.stdout.write(`\n[process-exit] code=${exitCode}${signal ? ` signal=${signal}` : ''}\n`); } catch { /* stdout may be closed */ }
|
|
344
|
+
const sentinelResult = await writeProcessExitSentinel({ exitCode, signal });
|
|
295
345
|
fs.appendFileSync(debugPath, `EXIT: code=${exitCode}${signal ? ` signal=${signal}` : ''}\nSTDERR: ${stderrBuf.slice(0, 500)}\n`);
|
|
346
|
+
if (!sentinelResult.stdoutFlushed && sentinelResult.outputPathWritten) {
|
|
347
|
+
fs.appendFileSync(debugPath, `EXIT SENTINEL FALLBACK: ${process.env.MINIONS_LIVE_OUTPUT_PATH}\n`);
|
|
348
|
+
}
|
|
296
349
|
process.exit(exitCode);
|
|
297
350
|
});
|
|
298
|
-
proc.on('error', (err) => {
|
|
351
|
+
proc.on('error', async (err) => {
|
|
299
352
|
fs.appendFileSync(debugPath, `ERROR: ${err.message}\n`);
|
|
353
|
+
await writeProcessExitSentinel({ exitCode: 1 });
|
|
300
354
|
process.exit(1);
|
|
301
355
|
});
|
|
302
356
|
}
|
|
303
357
|
|
|
304
|
-
module.exports = { parseSpawnArgs, buildSpawnInvocation, normalizeRuntimeExit };
|
|
358
|
+
module.exports = { parseSpawnArgs, buildSpawnInvocation, normalizeRuntimeExit, writeProcessExitSentinel };
|
|
305
359
|
|
|
306
360
|
if (require.main === module) main();
|
package/engine/timeout.js
CHANGED
|
@@ -217,6 +217,38 @@ function parseProcessExitCode(logText) {
|
|
|
217
217
|
return lastMatch[1] === 'spawn-failed' ? -1 : parseInt(lastMatch[1], 10);
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
function terminalResultIndicatesError(obj) {
|
|
221
|
+
const subtype = String(obj?.subtype || '');
|
|
222
|
+
const terminalReason = String(obj?.terminal_reason || obj?.terminalReason || '');
|
|
223
|
+
return obj?.is_error === true ||
|
|
224
|
+
/^error/i.test(subtype) ||
|
|
225
|
+
/max[_-]?turns|error|fail|cancel|timeout/i.test(terminalReason);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function parseTerminalResultFallbackExitCode(logText) {
|
|
229
|
+
if (!logText) return null;
|
|
230
|
+
let exitCode = null;
|
|
231
|
+
for (const line of String(logText).split(/\r?\n/)) {
|
|
232
|
+
const trimmed = line.trim();
|
|
233
|
+
if (!trimmed || !trimmed.includes('"result"') ||
|
|
234
|
+
(!trimmed.includes('terminal_reason') && !trimmed.includes('terminalReason'))) continue;
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
const obj = JSON.parse(trimmed);
|
|
238
|
+
if (obj?.type === 'result' && (obj.terminal_reason || obj.terminalReason) && terminalResultIndicatesError(obj)) {
|
|
239
|
+
exitCode = 1;
|
|
240
|
+
}
|
|
241
|
+
continue;
|
|
242
|
+
} catch { /* fall through to regex fallback for diagnostic-prefixed JSON */ }
|
|
243
|
+
|
|
244
|
+
if (/"type"\s*:\s*"result"/.test(trimmed) &&
|
|
245
|
+
/"terminal_?reason"\s*:\s*"[^"]*(?:max[_-]?turns|error|fail|cancel|timeout)[^"]*"/i.test(trimmed)) {
|
|
246
|
+
exitCode = 1;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return exitCode;
|
|
250
|
+
}
|
|
251
|
+
|
|
220
252
|
function checkTimeouts(config) {
|
|
221
253
|
const activeProcesses = engine().activeProcesses;
|
|
222
254
|
const engineRestartGraceUntil = engine().engineRestartGraceUntil;
|
|
@@ -411,6 +443,12 @@ function checkTimeouts(config) {
|
|
|
411
443
|
completeFromOutput(item, liveLogPath, processExitCode, fullLog, hasProcess);
|
|
412
444
|
continue;
|
|
413
445
|
}
|
|
446
|
+
const terminalResultExitCode = parseTerminalResultFallbackExitCode(fullLog);
|
|
447
|
+
if (terminalResultExitCode !== null) {
|
|
448
|
+
log('info', `Agent ${item.agent} (${item.id}) completed via stale terminal result fallback (exit code ${terminalResultExitCode})`);
|
|
449
|
+
completeFromOutput(item, liveLogPath, terminalResultExitCode, fullLog, hasProcess);
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
414
452
|
} catch (e) { log('warn', 'orphan final output completion scan: ' + e.message); }
|
|
415
453
|
|
|
416
454
|
// No tracked process AND no recent output past stale-orphan timeout AND (grace period expired OR confirmed-dead at restart) → orphaned
|
package/engine.js
CHANGED
|
@@ -995,6 +995,7 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
995
995
|
// 2. Log has stub only → process started but died before its first write
|
|
996
996
|
// 3. Log has stub + ... → process alive but hung (the only case that warrants orphan kill+retry)
|
|
997
997
|
const liveOutputPath = path.join(AGENTS_DIR, agentId, 'live-output.log');
|
|
998
|
+
childEnv.MINIONS_LIVE_OUTPUT_PATH = liveOutputPath;
|
|
998
999
|
|
|
999
1000
|
// Rotate previous live output to preserve session history (fixes #543: orphan recovery overwrites)
|
|
1000
1001
|
// Only rotate if the existing file has meaningful content (beyond just the header stub)
|
|
@@ -1186,6 +1187,7 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
1186
1187
|
const spawnScript = path.join(ENGINE_DIR, 'spawn-agent.js');
|
|
1187
1188
|
const childEnv = shared.cleanChildEnv();
|
|
1188
1189
|
if (completionReportPath) childEnv.MINIONS_COMPLETION_REPORT = completionReportPath;
|
|
1190
|
+
childEnv.MINIONS_LIVE_OUTPUT_PATH = liveOutputPath;
|
|
1189
1191
|
// Inject cached ADO token for steering session too (#998)
|
|
1190
1192
|
try {
|
|
1191
1193
|
const adoToken = await getAdoToken();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1685",
|
|
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"
|