@yemi33/minions 0.1.1680 → 0.1.1682
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 +16 -0
- package/dashboard/js/render-skills.js +18 -3
- package/dashboard.js +98 -53
- package/docs/plan-lifecycle.md +23 -7
- package/engine/copilot-models.json +1 -1
- package/engine/lifecycle.js +105 -165
- package/engine/meeting.js +36 -13
- package/engine/playbook.js +36 -12
- package/engine/queries.js +254 -67
- package/engine/runtimes/claude.js +52 -0
- package/engine/runtimes/copilot.js +69 -0
- package/engine/shared.js +28 -0
- package/engine/spawn-agent.js +10 -7
- package/engine.js +50 -31
- package/package.json +1 -1
package/engine.js
CHANGED
|
@@ -1767,6 +1767,16 @@ function buildWiDescription(item, planFile) {
|
|
|
1767
1767
|
|
|
1768
1768
|
function materializePlansAsWorkItems(config) {
|
|
1769
1769
|
if (!fs.existsSync(PRD_DIR)) { try { fs.mkdirSync(PRD_DIR, { recursive: true }); } catch (e) { log('warn', 'create PRD directory: ' + e.message); } }
|
|
1770
|
+
const writePrdLocked = (fileName, data) => {
|
|
1771
|
+
return mutateJsonFileLocked(path.join(PRD_DIR, fileName), () => data, { defaultValue: data });
|
|
1772
|
+
};
|
|
1773
|
+
const mutatePrdLocked = (fileName, fallback, mutator, options = {}) => {
|
|
1774
|
+
return mutateJsonFileLocked(path.join(PRD_DIR, fileName), (current) => {
|
|
1775
|
+
if (!current?.missing_features && fallback?.missing_features) current = fallback;
|
|
1776
|
+
if (!current || Array.isArray(current) || typeof current !== 'object') current = {};
|
|
1777
|
+
return mutator(current) || current;
|
|
1778
|
+
}, { defaultValue: fallback || {}, ...options });
|
|
1779
|
+
};
|
|
1770
1780
|
|
|
1771
1781
|
// Enforce: PRDs must be .json — auto-rename .md files that contain valid PRD JSON
|
|
1772
1782
|
// Check both prd/ and plans/ (agents may still write JSON to plans/)
|
|
@@ -1782,7 +1792,7 @@ function materializePlansAsWorkItems(config) {
|
|
|
1782
1792
|
const parsed = JSON.parse(stripped);
|
|
1783
1793
|
if (parsed.missing_features) {
|
|
1784
1794
|
const jsonName = mf.replace(/\.md$/, '.json');
|
|
1785
|
-
|
|
1795
|
+
writePrdLocked(jsonName, parsed);
|
|
1786
1796
|
try { fs.unlinkSync(path.join(checkDir, mf)); } catch { /* cleanup */ }
|
|
1787
1797
|
log('info', `Plan enforcement: moved ${mf} → prd/${jsonName} (PRDs must be .json in prd/)`);
|
|
1788
1798
|
}
|
|
@@ -1797,7 +1807,7 @@ function materializePlansAsWorkItems(config) {
|
|
|
1797
1807
|
try {
|
|
1798
1808
|
const parsed = safeJson(path.join(PLANS_DIR, jf));
|
|
1799
1809
|
if (parsed?.missing_features) {
|
|
1800
|
-
|
|
1810
|
+
writePrdLocked(jf, parsed);
|
|
1801
1811
|
try { fs.unlinkSync(path.join(PLANS_DIR, jf)); } catch { /* cleanup */ }
|
|
1802
1812
|
log('info', `Auto-migrated PRD ${jf} from plans/ to prd/`);
|
|
1803
1813
|
}
|
|
@@ -1814,7 +1824,7 @@ function materializePlansAsWorkItems(config) {
|
|
|
1814
1824
|
const SEQUENTIAL_ID_RE = /^P-?\d+$/;
|
|
1815
1825
|
|
|
1816
1826
|
for (const file of planFiles) {
|
|
1817
|
-
|
|
1827
|
+
let plan = safeJson(path.join(PRD_DIR, file));
|
|
1818
1828
|
if (!plan?.missing_features) continue;
|
|
1819
1829
|
|
|
1820
1830
|
// ID collision prevention: remap sequential IDs (P-001, P-002) to globally unique P-<uid> IDs.
|
|
@@ -1826,19 +1836,25 @@ function materializePlansAsWorkItems(config) {
|
|
|
1826
1836
|
const anyMaterialized = plan.missing_features.some(f =>
|
|
1827
1837
|
SEQUENTIAL_ID_RE.test(f.id) && allWorkItems.some(w => w.id === f.id && w.sourcePlan === file));
|
|
1828
1838
|
if (!anyMaterialized) {
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1839
|
+
let remappedCount = 0;
|
|
1840
|
+
plan = mutatePrdLocked(file, plan, (current) => {
|
|
1841
|
+
const features = Array.isArray(current.missing_features) ? current.missing_features : [];
|
|
1842
|
+
if (!features.some(f => SEQUENTIAL_ID_RE.test(f.id))) return current;
|
|
1843
|
+
const idMap = new Map();
|
|
1844
|
+
for (const f of features) {
|
|
1845
|
+
if (SEQUENTIAL_ID_RE.test(f.id)) {
|
|
1846
|
+
const newId = 'P-' + shared.uid();
|
|
1847
|
+
idMap.set(f.id, newId);
|
|
1848
|
+
f.id = newId;
|
|
1849
|
+
}
|
|
1835
1850
|
}
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1851
|
+
for (const f of features) {
|
|
1852
|
+
if (f.depends_on) f.depends_on = f.depends_on.map(d => idMap.get(d) || d);
|
|
1853
|
+
}
|
|
1854
|
+
remappedCount = idMap.size;
|
|
1855
|
+
return current;
|
|
1856
|
+
});
|
|
1857
|
+
if (remappedCount > 0) log('info', `Remapped ${remappedCount} sequential ID(s) in ${file} to prevent cross-PRD collisions`);
|
|
1842
1858
|
}
|
|
1843
1859
|
}
|
|
1844
1860
|
} catch (e) { log('warn', `Sequential ID remapping failed for ${file}: ${e.message}`); }
|
|
@@ -1851,25 +1867,26 @@ function materializePlansAsWorkItems(config) {
|
|
|
1851
1867
|
const recorded = plan.sourcePlanModifiedAt ? new Date(plan.sourcePlanModifiedAt).getTime() : null;
|
|
1852
1868
|
if (!recorded) {
|
|
1853
1869
|
// First time seeing this plan — record baseline mtime (no clean needed)
|
|
1854
|
-
plan
|
|
1855
|
-
|
|
1870
|
+
plan = mutatePrdLocked(file, plan, (current) => {
|
|
1871
|
+
if (!current.sourcePlanModifiedAt) current.sourcePlanModifiedAt = new Date(sourceMtime).toISOString();
|
|
1872
|
+
return current;
|
|
1873
|
+
}, { skipWriteIfUnchanged: true });
|
|
1856
1874
|
} else if (sourceMtime > recorded) {
|
|
1857
1875
|
// Source plan changed — auto-clean pending/failed items so they re-materialize with updated data
|
|
1858
1876
|
log('info', `Source plan ${plan.source_plan} updated — re-syncing PRD ${file}`);
|
|
1859
1877
|
autoCleanPrdWorkItems(file, config);
|
|
1860
|
-
plan.sourcePlanModifiedAt = new Date(sourceMtime).toISOString();
|
|
1861
|
-
plan.lastSyncedFromPlan = ts();
|
|
1862
1878
|
|
|
1863
1879
|
// Handle PRD based on current status
|
|
1864
1880
|
const prdStatus = plan.status || (plan.requires_approval ? 'awaiting-approval' : null);
|
|
1865
1881
|
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1882
|
+
plan = mutatePrdLocked(file, plan, (current) => {
|
|
1883
|
+
current.sourcePlanModifiedAt = new Date(sourceMtime).toISOString();
|
|
1884
|
+
current.lastSyncedFromPlan = ts();
|
|
1885
|
+
const currentPrdStatus = current.status || (current.requires_approval ? 'awaiting-approval' : null);
|
|
1886
|
+
if (currentPrdStatus) current.planStale = true;
|
|
1887
|
+
return current;
|
|
1888
|
+
});
|
|
1889
|
+
if (prdStatus) log('info', `PRD ${file} flagged as stale (plan revised while ${prdStatus}) — user can regenerate from dashboard`);
|
|
1873
1890
|
}
|
|
1874
1891
|
} catch (e) { log('warn', 'plan staleness check: ' + e.message); }
|
|
1875
1892
|
}
|
|
@@ -1879,10 +1896,12 @@ function materializePlansAsWorkItems(config) {
|
|
|
1879
1896
|
const planStatus = plan.status || (plan.requires_approval ? 'awaiting-approval' : null);
|
|
1880
1897
|
if (planStatus === 'awaiting-approval') {
|
|
1881
1898
|
if (config.engine?.autoApprovePlans) {
|
|
1882
|
-
plan
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1899
|
+
plan = mutatePrdLocked(file, plan, (current) => {
|
|
1900
|
+
current.status = 'approved';
|
|
1901
|
+
current.approvedAt = ts();
|
|
1902
|
+
current.approvedBy = 'auto-mode';
|
|
1903
|
+
return current;
|
|
1904
|
+
});
|
|
1886
1905
|
log('info', `Auto-approved plan: ${file}`);
|
|
1887
1906
|
} else {
|
|
1888
1907
|
continue; // Skip — waiting for human approval
|
|
@@ -3389,7 +3408,7 @@ function discoverCentralWorkItems(config) {
|
|
|
3389
3408
|
vars.plan_file = planFileName;
|
|
3390
3409
|
vars.task_description = item.title;
|
|
3391
3410
|
// Notes already populated by buildWorkItemDispatchVars — no need to re-read
|
|
3392
|
-
// Track expected plan filename
|
|
3411
|
+
// Track expected plan filename for artifacts and follow-up plan-to-prd prompts.
|
|
3393
3412
|
mutations.set(item.id, Object.assign(mutations.get(item.id) || {}, { _planFileName: planFileName }));
|
|
3394
3413
|
}
|
|
3395
3414
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1682",
|
|
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"
|