@yemi33/minions 0.1.2285 → 0.1.2287
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/dashboard/js/render-inbox.js +32 -3
- package/dashboard/js/render-other.js +1 -1
- package/dashboard/js/render-plans.js +3 -3
- package/dashboard/js/render-prs.js +45 -4
- package/dashboard/js/render-schedules.js +2 -2
- package/dashboard/js/render-skills.js +3 -3
- package/dashboard/js/render-watches.js +2 -2
- package/dashboard/js/render-work-items.js +3 -1
- package/dashboard/js/settings.js +45 -0
- package/dashboard/pages/engine.html +3 -3
- package/dashboard/pages/meetings.html +2 -2
- package/dashboard/pages/pipelines.html +1 -1
- package/dashboard/pages/tools.html +3 -3
- package/dashboard/pages/watches.html +1 -1
- package/dashboard/slim/js/status.js +29 -15
- package/dashboard/styles.css +22 -4
- package/dashboard.js +146 -72
- package/docs/deprecated.json +35 -161
- package/docs/live-checkout-mode.md +54 -0
- package/docs/project-skills.md +0 -1
- package/engine/cleanup.js +51 -2
- package/engine/db/migrations/015-plans-prds.js +0 -0
- package/engine/features.js +14 -0
- package/engine/llm.js +1 -0
- package/engine/playbook.js +4 -4
- package/engine/prd-store.js +264 -0
- package/engine/queries.js +97 -59
- package/engine/runtimes/copilot.js +32 -5
- package/engine/shared.js +24 -8
- package/engine/watch-actions.js +4 -1
- package/engine.js +42 -7
- package/package.json +1 -1
|
@@ -433,6 +433,17 @@ function _mapEffort(level) {
|
|
|
433
433
|
// `fallbackModel` opts are silently ignored (their capability flags are false
|
|
434
434
|
// so engine code shouldn't pass them, but we tolerate them gracefully).
|
|
435
435
|
|
|
436
|
+
// Map MIME type to file extension for --attachment temp file naming.
|
|
437
|
+
const _MIME_EXT = {
|
|
438
|
+
'image/png': 'png',
|
|
439
|
+
'image/jpeg': 'jpg',
|
|
440
|
+
'image/gif': 'gif',
|
|
441
|
+
'image/webp': 'webp',
|
|
442
|
+
};
|
|
443
|
+
function _mimeToExt(mimeType) {
|
|
444
|
+
return _MIME_EXT[String(mimeType || '').toLowerCase()] || 'bin';
|
|
445
|
+
}
|
|
446
|
+
|
|
436
447
|
function buildArgs(opts = {}) {
|
|
437
448
|
const {
|
|
438
449
|
model,
|
|
@@ -445,6 +456,8 @@ function buildArgs(opts = {}) {
|
|
|
445
456
|
suppressAgentsMd,
|
|
446
457
|
reasoningSummaries,
|
|
447
458
|
disabledMcpServers,
|
|
459
|
+
images,
|
|
460
|
+
tmpDir,
|
|
448
461
|
} = opts;
|
|
449
462
|
|
|
450
463
|
const args = [
|
|
@@ -498,6 +511,22 @@ function buildArgs(opts = {}) {
|
|
|
498
511
|
// the `=`, commander.js treats the next token as a positional, not the value.
|
|
499
512
|
if (sessionId) args.push(`--resume=${sessionId}`);
|
|
500
513
|
|
|
514
|
+
// W-mqv7324u0021db5d — image attachments. Write each base64 payload to a
|
|
515
|
+
// temp file in tmpDir and emit --attachment <path> (flag is repeatable).
|
|
516
|
+
// tmpDir is required; skip silently when absent so callers without image
|
|
517
|
+
// support don't crash (defense-in-depth on top of the caps.imageInput gate).
|
|
518
|
+
if (Array.isArray(images) && images.length && tmpDir) {
|
|
519
|
+
for (let i = 0; i < images.length; i++) {
|
|
520
|
+
const img = images[i];
|
|
521
|
+
if (!img || !img.dataBase64) continue;
|
|
522
|
+
const ext = _mimeToExt(img.mimeType);
|
|
523
|
+
const filename = `img-${i}-${Date.now().toString(36)}.${ext}`;
|
|
524
|
+
const filePath = path.join(tmpDir, filename);
|
|
525
|
+
fs.writeFileSync(filePath, Buffer.from(img.dataBase64, 'base64'));
|
|
526
|
+
args.push('--attachment', filePath);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
501
530
|
return args;
|
|
502
531
|
}
|
|
503
532
|
|
|
@@ -1214,11 +1243,9 @@ const capabilities = {
|
|
|
1214
1243
|
resumePromptCarryover: true,
|
|
1215
1244
|
// Adapter implements createStreamConsumer(ctx) — required by llm.js accumulator
|
|
1216
1245
|
streamConsumer: true,
|
|
1217
|
-
//
|
|
1218
|
-
//
|
|
1219
|
-
|
|
1220
|
-
// of the base64 payload (a different adapter-local path than Claude's inline).
|
|
1221
|
-
imageInput: false,
|
|
1246
|
+
// `--attachment <path>` works in non-interactive mode (live-tested; see W-mqv7324u0021db5d).
|
|
1247
|
+
// Base64 payloads are materialized to tmp files inside llmTmpDir before spawn.
|
|
1248
|
+
imageInput: true,
|
|
1222
1249
|
};
|
|
1223
1250
|
|
|
1224
1251
|
// Install hint surfaced when `resolveBinary()` returns null. Covers all
|
package/engine/shared.js
CHANGED
|
@@ -1866,6 +1866,17 @@ function mutateJsonFileLocked(filePath, mutateFn, {
|
|
|
1866
1866
|
try { if (fileExists) fs.copyFileSync(filePath, backupPath); } catch { /* backup is best-effort */ }
|
|
1867
1867
|
}
|
|
1868
1868
|
safeWrite(filePath, finalData);
|
|
1869
|
+
// Phase 10 step 2 — dual-write PRD content to SQL. The single chokepoint:
|
|
1870
|
+
// every prd/*.json mutation through mutateJsonFileLocked mirrors into the
|
|
1871
|
+
// prds/prd_items/prd_verify_prs tables. JSON stays canonical (nothing
|
|
1872
|
+
// reads SQL yet); this keeps the mirror trustworthy for the read-flip.
|
|
1873
|
+
// BEST-EFFORT: parsePrdPath fast-rejects non-PRD paths, and a SQLite
|
|
1874
|
+
// failure must never break the JSON write. mirrorPrdToSql swallows
|
|
1875
|
+
// internally; the require is guarded for the (engine-agnostic) load order.
|
|
1876
|
+
try {
|
|
1877
|
+
const prdStore = require('./prd-store');
|
|
1878
|
+
if (prdStore.parsePrdPath(filePath)) prdStore.mirrorPrdToSql(filePath, finalData);
|
|
1879
|
+
} catch { /* mirror is best-effort — never break the canonical JSON write */ }
|
|
1869
1880
|
// Side-effect hook fired only when an actual write happened. Callers
|
|
1870
1881
|
// use this to emit cache-invalidation signals (events table row) so
|
|
1871
1882
|
// skip-write-unchanged paths (dedup, idempotent no-op POSTs) don't
|
|
@@ -2635,12 +2646,21 @@ const CHECKOUT_MODES = Object.freeze({ WORKTREE: 'worktree', LIVE: 'live' });
|
|
|
2635
2646
|
function resolveCheckoutMode(project, workItemType) {
|
|
2636
2647
|
if (!project || typeof project !== 'object') return CHECKOUT_MODES.WORKTREE;
|
|
2637
2648
|
const canonical = project.checkoutMode;
|
|
2638
|
-
//
|
|
2639
|
-
|
|
2649
|
+
// Resolve the effective mode: canonical field first, then legacy worktreeMode fallback.
|
|
2650
|
+
let effectiveMode;
|
|
2651
|
+
if (canonical === CHECKOUT_MODES.LIVE || canonical === CHECKOUT_MODES.WORKTREE) {
|
|
2652
|
+
effectiveMode = canonical;
|
|
2653
|
+
} else if (project.worktreeMode === 'live') {
|
|
2654
|
+
effectiveMode = CHECKOUT_MODES.LIVE;
|
|
2655
|
+
} else {
|
|
2656
|
+
effectiveMode = CHECKOUT_MODES.WORKTREE;
|
|
2657
|
+
}
|
|
2658
|
+
// liveValidation without an effective live mode is a misconfiguration — warn and ignore.
|
|
2659
|
+
if (project.liveValidation && effectiveMode !== CHECKOUT_MODES.LIVE) {
|
|
2640
2660
|
log('warn', 'resolveCheckoutMode: liveValidation is set but checkoutMode is not "live" — liveValidation ignored',
|
|
2641
2661
|
{ projectName: project.name });
|
|
2642
2662
|
}
|
|
2643
|
-
if (
|
|
2663
|
+
if (effectiveMode === CHECKOUT_MODES.LIVE) {
|
|
2644
2664
|
// Apply liveValidation routing when the block is present and workItemType is provided.
|
|
2645
2665
|
if (project.liveValidation && workItemType !== undefined) {
|
|
2646
2666
|
return workItemType === project.liveValidation.type
|
|
@@ -2649,11 +2669,6 @@ function resolveCheckoutMode(project, workItemType) {
|
|
|
2649
2669
|
}
|
|
2650
2670
|
return CHECKOUT_MODES.LIVE;
|
|
2651
2671
|
}
|
|
2652
|
-
if (canonical === CHECKOUT_MODES.WORKTREE) return CHECKOUT_MODES.WORKTREE;
|
|
2653
|
-
// Legacy field fallback (only consulted when checkoutMode is absent/unknown).
|
|
2654
|
-
const legacy = project.worktreeMode;
|
|
2655
|
-
if (legacy === 'live') return CHECKOUT_MODES.LIVE;
|
|
2656
|
-
// legacy 'isolated' (and anything else) → the default worktree behavior.
|
|
2657
2672
|
return CHECKOUT_MODES.WORKTREE;
|
|
2658
2673
|
}
|
|
2659
2674
|
|
|
@@ -4387,6 +4402,7 @@ const FAILURE_CLASS = {
|
|
|
4387
4402
|
SPAWN_PHASE_STALL: 'spawn-phase-stall', // W-mq0e2dae000a003d: process spawned and ran startup-only events (MCP init / hooks) but never emitted real task progress; CPU usage stayed below threshold past the grace window. Engine kills the wedged child and treats this as retryable (fresh-session) so a re-spawn can clear a transient MCP wedge.
|
|
4388
4403
|
OUTPUT_TRUNCATED: 'output-truncated', // P-8e4c2a17: the agent streamed more stdout than the engine's hard capture cap (engine.js AGENT_OUTPUT_CAP_BYTES, 1MB) BEFORE the terminal `result` event arrived. The result (session id, completion block, final text) lives at the END of the stream, so it fell outside the captured window and parseOutput found nothing — the dispatch would otherwise fail as an opaque UNKNOWN and retry to death. Surfaced loudly with an actionable message; non-retryable (mechanical retry just reproduces the overflow — the agent must reduce output volume or the task must be split).
|
|
4389
4404
|
VERIFY_MISSING_PR: 'verify-missing-pr', // W-mqsk1ip00006cbae: a verify WI exited done but no PR was attached (neither _prUrl/_pr on the item nor a matching pull-requests.json entry for the plan). Flipped to failed so the plan doesn't silently advance without an E2E PR. Retryable — agent may have phantom-crashed before pushing the branch.
|
|
4405
|
+
PROJECT_NOT_FOUND: 'project_not_found', // W-mqv2wsy30002e090: a work item's `project` field names a project that is not configured (resolveConfiguredProject / formatUnknownProjectError → `Project "<name>" not found. Known projects: …`). Stamped at discovery time so the dashboard PR-column renderer surfaces the red failReason snippet like other non-retryable failures instead of burying it in the Agent column. Non-retryable — operator must fix the WI's project field or configure the project.
|
|
4390
4406
|
UNKNOWN: 'unknown', // Unclassified failure
|
|
4391
4407
|
};
|
|
4392
4408
|
|
package/engine/watch-actions.js
CHANGED
|
@@ -562,7 +562,10 @@ registerActionType(WATCH_ACTION_TYPE.WEBHOOK, {
|
|
|
562
562
|
// minions-api — first-class loopback API caller for the in-process dashboard.
|
|
563
563
|
// Restricts `endpoint` to paths starting with `/api/` so a watch can't reach
|
|
564
564
|
// external hosts (use `webhook` for that). Always targets
|
|
565
|
-
// `http://127.0.0.1:${
|
|
565
|
+
// `http://127.0.0.1:${shared.readDashboardPortFile(MINIONS_DIR)?.port||7331}${endpoint}`
|
|
566
|
+
// — the *actual* bound dashboard port from the dashboard-port.json beacon, not
|
|
567
|
+
// the frozen MINIONS_PORT env (which stays at the requested value when the
|
|
568
|
+
// dashboard falls back to 7332+ on EADDRINUSE) — and sets
|
|
566
569
|
// `X-Minions-Internal: 1` so dashboard handlers can recognize that the
|
|
567
570
|
// request originated from the engine's own action surface (Origin/Referer
|
|
568
571
|
// are intentionally absent — the dashboard's origin gate already allows
|
package/engine.js
CHANGED
|
@@ -849,6 +849,7 @@ async function findExistingWorktree(repoDir, branchName) {
|
|
|
849
849
|
}
|
|
850
850
|
|
|
851
851
|
function isWorktreeRetryableError(err) {
|
|
852
|
+
if (err?._incompleteCheckout) return true;
|
|
852
853
|
const msg = String(err?.message || '');
|
|
853
854
|
return msg.includes('ETIMEDOUT')
|
|
854
855
|
|| msg.includes('index.lock')
|
|
@@ -957,6 +958,29 @@ async function runWorktreeAdd(rootDir, worktreePath, addArgs, gitOpts, worktreeC
|
|
|
957
958
|
log('warn', `Retrying git worktree add (attempt ${attempt + 1}/${retries + 1}) for ${path.basename(worktreePath)}`);
|
|
958
959
|
}
|
|
959
960
|
await shared.shellSafeGit(['worktree', 'add', worktreePath, ...addArgs], { ...gitOpts, cwd: rootDir });
|
|
961
|
+
// W-mqvaxv65000m76f2 (issue #430, Layer 1) — GVFS partial-checkout guard.
|
|
962
|
+
// On large GVFS repos (e.g. 1JS with 296K files), `git worktree add`
|
|
963
|
+
// exits cleanly but only writes a .git file — no source files are checked
|
|
964
|
+
// out. The engine would otherwise treat the empty worktree as valid for
|
|
965
|
+
// reuse; the subsequent `git status` preflight fails (nothing to stat) and
|
|
966
|
+
// the dispatch loop recurs. Detect the empty checkout and throw a retriable
|
|
967
|
+
// error so the retry loop re-creates the worktree on the next attempt.
|
|
968
|
+
let worktreeEntries;
|
|
969
|
+
try { worktreeEntries = fs.readdirSync(worktreePath).filter(e => e !== '.git'); }
|
|
970
|
+
catch { worktreeEntries = null; } // If the read fails, skip the check — don't block normal cases.
|
|
971
|
+
if (worktreeEntries !== null && worktreeEntries.length === 0) {
|
|
972
|
+
log('warn', `runWorktreeAdd: GVFS partial checkout detected at ${worktreePath} (only .git written) — removing and retrying`);
|
|
973
|
+
try {
|
|
974
|
+
await shared.shellSafeGit(['worktree', 'remove', '--force', worktreePath], { ...gitOpts, cwd: rootDir, timeout: 30000 });
|
|
975
|
+
} catch (rmErr) {
|
|
976
|
+
log('warn', `runWorktreeAdd: worktree remove after partial checkout failed (${rmErr.message}) — falling back to fs-level cleanup`);
|
|
977
|
+
try { fs.rmSync(worktreePath, { recursive: true, force: true }); } catch {}
|
|
978
|
+
try { await shared.shellSafeGit(['worktree', 'prune'], { ...gitOpts, cwd: rootDir, timeout: 15000 }); } catch {}
|
|
979
|
+
}
|
|
980
|
+
const incompleteErr = new Error(`GVFS incomplete checkout at ${worktreePath}: worktree add exited cleanly but no source files were checked out (only .git was written). Retriable.`);
|
|
981
|
+
incompleteErr._incompleteCheckout = true;
|
|
982
|
+
throw incompleteErr;
|
|
983
|
+
}
|
|
960
984
|
// W-mqecdoot — stamp the engine ownership marker so the out-of-root
|
|
961
985
|
// worktree GC can distinguish a worktree we created from one a human
|
|
962
986
|
// developer made by hand. Best-effort; never blocks the spawn.
|
|
@@ -5992,8 +6016,10 @@ function materializePlansAsWorkItems(config) {
|
|
|
5992
6016
|
let planFiles;
|
|
5993
6017
|
try { planFiles = fs.readdirSync(PRD_DIR).filter(f => f.endsWith('.json')); } catch { return; }
|
|
5994
6018
|
|
|
5995
|
-
// Regex for detecting sequential PRD item IDs (P-001,
|
|
5996
|
-
|
|
6019
|
+
// Regex for detecting sequential PRD item IDs (P-001, M001, M-001, WI-001, …) — hoisted outside loop.
|
|
6020
|
+
// Covers all patterns where an alphabetic prefix is followed by an optional dash and pure digits,
|
|
6021
|
+
// e.g. P001, P-001, M001, M-001, WI-001. Does NOT match hex UUIDs like P-a3f9b2c1.
|
|
6022
|
+
const SEQUENTIAL_ID_RE = /^[A-Za-z]+-?\d+$/;
|
|
5997
6023
|
|
|
5998
6024
|
for (let file of planFiles) {
|
|
5999
6025
|
// safeJsonNoRestore — if a PRD was archived between readdir and this
|
|
@@ -6211,7 +6237,9 @@ function materializePlansAsWorkItems(config) {
|
|
|
6211
6237
|
mutateWorkItems(wiPath, existingItems => {
|
|
6212
6238
|
for (const item of projItems) {
|
|
6213
6239
|
// Re-open: 'updated' or 'missing' re-opens a done work item (#906)
|
|
6214
|
-
|
|
6240
|
+
// Use composite (sourcePlan, id) key so a WI from another PRD with the
|
|
6241
|
+
// same id is not treated as already-materialized for this PRD.
|
|
6242
|
+
const existingWi = existingItems.find(w => w.id === item.id && w.sourcePlan === file);
|
|
6215
6243
|
const shouldReopen = item.status === PRD_ITEM_STATUS.UPDATED || item.status === PRD_ITEM_STATUS.MISSING;
|
|
6216
6244
|
if (existingWi && DONE_STATUSES.has(existingWi.status) && shouldReopen) {
|
|
6217
6245
|
shared.reopenWorkItem(existingWi);
|
|
@@ -6222,13 +6250,14 @@ function materializePlansAsWorkItems(config) {
|
|
|
6222
6250
|
continue;
|
|
6223
6251
|
}
|
|
6224
6252
|
|
|
6225
|
-
// Skip if already materialized —
|
|
6253
|
+
// Skip if already materialized — use composite (sourcePlan, id) key to prevent
|
|
6254
|
+
// cross-PRD collision when two plans share the same item id (W-mqv6ql5w001w792f).
|
|
6226
6255
|
let alreadyExists = !!existingWi;
|
|
6227
6256
|
if (!alreadyExists) {
|
|
6228
6257
|
for (const p of allProjects) {
|
|
6229
6258
|
if (String(p.name || '').toLowerCase() === String(projName || '').toLowerCase()) continue;
|
|
6230
6259
|
const otherItems = safeJsonArr(projectWorkItemsPath(p));
|
|
6231
|
-
const otherWi = otherItems.find(w => w.id === item.id);
|
|
6260
|
+
const otherWi = otherItems.find(w => w.id === item.id && w.sourcePlan === file);
|
|
6232
6261
|
if (otherWi) {
|
|
6233
6262
|
if (DONE_STATUSES.has(otherWi.status) && shouldReopen) {
|
|
6234
6263
|
deferredReopens.push({ itemId: item.id, projectName: p.name, item });
|
|
@@ -8510,7 +8539,7 @@ function discoverCentralWorkItems(config) {
|
|
|
8510
8539
|
const itemProjectResolution = shared.resolveConfiguredProject(item.project, projects);
|
|
8511
8540
|
if (itemProjectResolution.error) {
|
|
8512
8541
|
const error = itemProjectResolution.error;
|
|
8513
|
-
mutations.set(item.id, { status: WI_STATUS.FAILED, failReason: error, failedAt: ts() });
|
|
8542
|
+
mutations.set(item.id, { status: WI_STATUS.FAILED, failReason: error, failedAt: ts(), _failureClass: FAILURE_CLASS.PROJECT_NOT_FOUND });
|
|
8514
8543
|
log('warn', `central work item ${item.id}: ${error}`);
|
|
8515
8544
|
continue;
|
|
8516
8545
|
}
|
|
@@ -8648,6 +8677,7 @@ function discoverCentralWorkItems(config) {
|
|
|
8648
8677
|
status: WI_STATUS.FAILED,
|
|
8649
8678
|
failReason: error,
|
|
8650
8679
|
failedAt: ts(),
|
|
8680
|
+
_failureClass: FAILURE_CLASS.PROJECT_NOT_FOUND,
|
|
8651
8681
|
...(declaredPlanProject ? { _declaredPlanProject: declaredPlanProject, _declaredPlanProjectMissing: true } : {}),
|
|
8652
8682
|
});
|
|
8653
8683
|
log('warn', `central work item ${item.id}: ${error}`);
|
|
@@ -8661,6 +8691,7 @@ function discoverCentralWorkItems(config) {
|
|
|
8661
8691
|
status: WI_STATUS.FAILED,
|
|
8662
8692
|
failReason: crossRepoFallbackError,
|
|
8663
8693
|
failedAt: ts(),
|
|
8694
|
+
_failureClass: FAILURE_CLASS.PROJECT_NOT_FOUND,
|
|
8664
8695
|
_crossRepoTargetProjects: planTargetProjects.slice(),
|
|
8665
8696
|
});
|
|
8666
8697
|
log('warn', `central work item ${item.id} (cross-repo plan-to-prd): ${crossRepoFallbackError}`);
|
|
@@ -10288,12 +10319,15 @@ async function tickInner() {
|
|
|
10288
10319
|
} else {
|
|
10289
10320
|
// P-a3f9b205: surface the per-project live-mode gate. Order matters:
|
|
10290
10321
|
// max_concurrency / agent_busy / branch_locked are all more specific
|
|
10291
|
-
// and win when both apply.
|
|
10322
|
+
// and win when both apply. Also check the pending item's own resolved
|
|
10323
|
+
// checkout mode so worktree-mode items on hybrid projects are not
|
|
10324
|
+
// falsely annotated live_checkout_busy (mirrors seeding guard above).
|
|
10292
10325
|
const itemProjName = item.project || item.meta?.project?.name || null;
|
|
10293
10326
|
if (
|
|
10294
10327
|
itemProjName
|
|
10295
10328
|
&& !READ_ONLY_ROOT_TASK_TYPES.has(item.type)
|
|
10296
10329
|
&& postLiveProjectsInUse.has(itemProjName)
|
|
10330
|
+
&& shared.resolveCheckoutMode(shared.findProjectByName(shared.getProjects(config), itemProjName), item.type) === 'live'
|
|
10297
10331
|
) {
|
|
10298
10332
|
reason = 'live_checkout_busy';
|
|
10299
10333
|
}
|
|
@@ -10490,6 +10524,7 @@ module.exports = {
|
|
|
10490
10524
|
resolveDependencyBranches, buildCrossRepoDepsSection, // exported for testing (P-faea3206)
|
|
10491
10525
|
gitOutputToString, gitErrorOutput, classifyDepMergeFailureOutput, listUnmergedFiles, // exported for testing
|
|
10492
10526
|
buildDepConflictFixItem, deriveConflictFixKey, // exported for testing (W-mpcwojgr000a0244)
|
|
10527
|
+
runWorktreeAdd, // exported for testing (W-mqvaxv65000m76f2 — GVFS partial-checkout behavioral test)
|
|
10493
10528
|
isWorktreeRetryableError, removeStaleIndexLock, syncReusedWorktree, assertCleanSharedWorktree, _quarantineDirtyWorktree, // exported for testing
|
|
10494
10529
|
_statusPorcelainCmd, _killGitDescendantsForWorktree, _bumpQuarantineOutcome, // exported for testing (W-mq5n1zx5)
|
|
10495
10530
|
_reapWorktreeHolders, _findTerminalWorktreeOwners, // exported for testing (W-mqila0t5 — CWD-pinned holder reap)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2287",
|
|
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"
|