@yemi33/minions 0.1.2286 → 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 +51 -12
- 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
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"
|