@yemi33/minions 0.1.2025 → 0.1.2027
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/refresh.js +1 -0
- package/dashboard.js +50 -5
- package/engine/queries.js +107 -0
- package/package.json +1 -1
package/dashboard/js/refresh.js
CHANGED
|
@@ -13,6 +13,7 @@ const _pageCounters = {
|
|
|
13
13
|
pipelines: function(d) { return (d.pipelines || []).length + '|' + (d.pipelines || []).reduce(function(s, p) { return s + (p.runs || []).length; }, 0); },
|
|
14
14
|
schedule: function(d) { return (d.schedules || []).length; },
|
|
15
15
|
engine: function(d) { return (d.dispatch?.completed || []).filter(function(c) { return c.result === 'error'; }).length; },
|
|
16
|
+
qa: function(d) { return (d.qaRuns?.total || 0) + '|' + (d.qaRuns?.sig || ''); },
|
|
16
17
|
};
|
|
17
18
|
let _prevCounts = {};
|
|
18
19
|
function _detectPageChanges(data) {
|
package/dashboard.js
CHANGED
|
@@ -1589,7 +1589,9 @@ function _ifNoneMatchHasEtag(headerValue, currentEtag) {
|
|
|
1589
1589
|
// delegate so any module that contributes to `_buildStatusFastState()` can
|
|
1590
1590
|
// register its mtime inputs in one place.
|
|
1591
1591
|
const _mtimeTrackedFiles = () => queries.getStatusFastStateMtimePaths(CONFIG);
|
|
1592
|
-
|
|
1592
|
+
const _slowMtimeTrackedFiles = () => queries.getStatusSlowStateMtimePaths(CONFIG);
|
|
1593
|
+
let _lastMtimes = {}; // { filePath: mtimeMs } — fast-state baseline
|
|
1594
|
+
let _lastSlowMtimes = {}; // { filePath: mtimeMs } — slow-state baseline
|
|
1593
1595
|
|
|
1594
1596
|
function _getMtimes() {
|
|
1595
1597
|
const result = {};
|
|
@@ -1599,6 +1601,14 @@ function _getMtimes() {
|
|
|
1599
1601
|
return result;
|
|
1600
1602
|
}
|
|
1601
1603
|
|
|
1604
|
+
function _getSlowMtimes() {
|
|
1605
|
+
const result = {};
|
|
1606
|
+
for (const fp of _slowMtimeTrackedFiles()) {
|
|
1607
|
+
try { result[fp] = fs.statSync(fp).mtimeMs; } catch { result[fp] = 0; }
|
|
1608
|
+
}
|
|
1609
|
+
return result;
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1602
1612
|
function _mtimesChanged(prev, curr) {
|
|
1603
1613
|
for (const fp of Object.keys(curr)) {
|
|
1604
1614
|
if (prev[fp] !== curr[fp]) return true;
|
|
@@ -1668,6 +1678,21 @@ function _buildStatusFastState() {
|
|
|
1668
1678
|
workItems: getWorkItems(),
|
|
1669
1679
|
watches: watchesMod.getWatches(),
|
|
1670
1680
|
meetings: (() => { try { return require('./engine/meeting').getMeetings(); } catch { return []; } })(),
|
|
1681
|
+
// QA runs — surfaced for the sidebar activity-dot counter and any future
|
|
1682
|
+
// CC/aggregate view. Tab-level rendering keeps its own /api/qa/runs poll
|
|
1683
|
+
// (5 s while the QA page is mounted). qa-runs.json is in the mtime tracker
|
|
1684
|
+
// so a new run lights the dot within one /api/status poll cycle (~4 s).
|
|
1685
|
+
qaRuns: (() => {
|
|
1686
|
+
try {
|
|
1687
|
+
const runs = require('./engine/qa-runs').listRuns({ limit: 50 }) || [];
|
|
1688
|
+
return {
|
|
1689
|
+
total: runs.length,
|
|
1690
|
+
// Signature of (id, status) for the most recent 20 runs so the
|
|
1691
|
+
// sidebar counter advances on status flips AND on new entries.
|
|
1692
|
+
sig: runs.slice(0, 20).map(r => (r && r.id || '') + ':' + (r && r.status || '')).join(','),
|
|
1693
|
+
};
|
|
1694
|
+
} catch { return { total: 0, sig: '' }; }
|
|
1695
|
+
})(),
|
|
1671
1696
|
};
|
|
1672
1697
|
}
|
|
1673
1698
|
|
|
@@ -1772,8 +1797,16 @@ function getStatus() {
|
|
|
1772
1797
|
if (_mtimesChanged(_lastMtimes, currMtimes)) fastStale = true;
|
|
1773
1798
|
}
|
|
1774
1799
|
|
|
1775
|
-
// Slow state: 60s TTL
|
|
1776
|
-
|
|
1800
|
+
// Slow state: 60s TTL with mtime-based validation for early bust.
|
|
1801
|
+
// The mtime tracker covers engine-driven slow-state writes (PRD updates,
|
|
1802
|
+
// pipeline-runs.json, schedule-runs.json, verify guides, project skills)
|
|
1803
|
+
// so changes surface within one SPA poll (~4 s) instead of waiting up to
|
|
1804
|
+
// 60 s for TTL. Same pre-build snapshot semantics as fast-state below.
|
|
1805
|
+
let slowStale = !_slowState || (now - _slowStateTs) >= SLOW_STATE_TTL;
|
|
1806
|
+
if (!slowStale) {
|
|
1807
|
+
const currSlowMtimes = _getSlowMtimes();
|
|
1808
|
+
if (_mtimesChanged(_lastSlowMtimes, currSlowMtimes)) slowStale = true;
|
|
1809
|
+
}
|
|
1777
1810
|
|
|
1778
1811
|
// If nothing stale, return cached merged result
|
|
1779
1812
|
if (!fastStale && !slowStale && _statusCache) return _statusCache;
|
|
@@ -1795,10 +1828,14 @@ function getStatus() {
|
|
|
1795
1828
|
_lastMtimes = preBuildMtimes;
|
|
1796
1829
|
}
|
|
1797
1830
|
|
|
1798
|
-
// Rebuild slow state (rarely-changing data: ~8-15 reads, 60s TTL)
|
|
1831
|
+
// Rebuild slow state (rarely-changing data: ~8-15 reads, 60s TTL).
|
|
1832
|
+
// Same pre-build snapshot pattern as fast-state — capture mtimes BEFORE
|
|
1833
|
+
// disk reads so any write landing mid-build busts the next poll.
|
|
1799
1834
|
if (slowStale) {
|
|
1835
|
+
const preBuildSlowMtimes = _getSlowMtimes();
|
|
1800
1836
|
_slowState = _buildStatusSlowState();
|
|
1801
1837
|
_slowStateTs = now;
|
|
1838
|
+
_lastSlowMtimes = preBuildSlowMtimes;
|
|
1802
1839
|
}
|
|
1803
1840
|
|
|
1804
1841
|
// Merge both tiers — no API contract change
|
|
@@ -1839,7 +1876,11 @@ function refreshStatusAsync() {
|
|
|
1839
1876
|
const currMtimes = _getMtimes();
|
|
1840
1877
|
if (_mtimesChanged(_lastMtimes, currMtimes)) fastStale = true;
|
|
1841
1878
|
}
|
|
1842
|
-
|
|
1879
|
+
let slowStale = !_slowState || (now - _slowStateTs) >= SLOW_STATE_TTL;
|
|
1880
|
+
if (!slowStale) {
|
|
1881
|
+
const currSlowMtimes = _getSlowMtimes();
|
|
1882
|
+
if (_mtimesChanged(_lastSlowMtimes, currSlowMtimes)) slowStale = true;
|
|
1883
|
+
}
|
|
1843
1884
|
|
|
1844
1885
|
if (!fastStale && !slowStale && _statusCache) return _statusCache;
|
|
1845
1886
|
|
|
@@ -1866,7 +1907,9 @@ function refreshStatusAsync() {
|
|
|
1866
1907
|
}
|
|
1867
1908
|
|
|
1868
1909
|
let slow = _slowState;
|
|
1910
|
+
let preBuildSlowMtimes = null;
|
|
1869
1911
|
if (slowStale) {
|
|
1912
|
+
preBuildSlowMtimes = _getSlowMtimes();
|
|
1870
1913
|
slow = _buildStatusSlowState();
|
|
1871
1914
|
}
|
|
1872
1915
|
|
|
@@ -1887,6 +1930,7 @@ function refreshStatusAsync() {
|
|
|
1887
1930
|
if (slowStale) {
|
|
1888
1931
|
_slowState = slow;
|
|
1889
1932
|
_slowStateTs = now;
|
|
1933
|
+
_lastSlowMtimes = preBuildSlowMtimes;
|
|
1890
1934
|
}
|
|
1891
1935
|
_statusCache = { ..._fastState, ..._slowState, timestamp: new Date().toISOString() };
|
|
1892
1936
|
_markStatusCacheBuilt();
|
|
@@ -1921,6 +1965,7 @@ function _resetStatusCacheForTesting() {
|
|
|
1921
1965
|
_statusInvalidationGeneration = 0;
|
|
1922
1966
|
_statusRefreshHook = null;
|
|
1923
1967
|
_lastMtimes = {};
|
|
1968
|
+
_lastSlowMtimes = {};
|
|
1924
1969
|
}
|
|
1925
1970
|
|
|
1926
1971
|
/** Return cached JSON string of status — single stringify, reused by SSE and /api/status */
|
package/engine/queries.js
CHANGED
|
@@ -1907,7 +1907,40 @@ function getStatusFastStateMtimePaths(config) {
|
|
|
1907
1907
|
path.join(ENGINE_DIR, 'watches.json'),
|
|
1908
1908
|
// Central work-items.json surfaced by getWorkItems().
|
|
1909
1909
|
path.join(MINIONS_DIR, 'work-items.json'),
|
|
1910
|
+
// notes.md (surfaced by getNotesWithMeta) — consolidation writes this
|
|
1911
|
+
// when an inbox batch is processed. Single file, mtime advances on every
|
|
1912
|
+
// write. Without this, the dashboard's notes view sat stale for up to
|
|
1913
|
+
// FAST_STATE_TTL (10 s) after each consolidation cycle.
|
|
1914
|
+
NOTES_PATH,
|
|
1915
|
+
// notes/inbox/ (surfaced by getInbox) — every writeToInbox call CREATES
|
|
1916
|
+
// a new file (engine/shared.js#writeToInbox always uses a uid'd path,
|
|
1917
|
+
// never an in-place edit), so the directory's mtime is a reliable
|
|
1918
|
+
// entry-add/remove signal even on Windows NTFS. Without it, PR-comment
|
|
1919
|
+
// notifications, agent-failure summaries, follow-up build alerts, and
|
|
1920
|
+
// meeting-transcript dumps all lagged up to 10 s before appearing on
|
|
1921
|
+
// the dashboard's inbox view.
|
|
1922
|
+
INBOX_DIR,
|
|
1923
|
+
// engine/qa-runs.json (surfaced by listRuns via fast-state qaRuns slice)
|
|
1924
|
+
// — new QA runs and status flips need to light the sidebar activity dot
|
|
1925
|
+
// within one SPA poll cycle. Single file, mtime advances on each write.
|
|
1926
|
+
path.join(ENGINE_DIR, 'qa-runs.json'),
|
|
1910
1927
|
];
|
|
1928
|
+
// meetings/<id>.json (surfaced by meeting.getMeetings) — round transitions
|
|
1929
|
+
// edit each file in-place via mutateMeeting, so the parent dir's mtime
|
|
1930
|
+
// does NOT advance on Windows. Tracking each file individually catches
|
|
1931
|
+
// in-file edits. Bounded by meeting count (typically <50 active); a 50-
|
|
1932
|
+
// meeting fleet adds ~50 statSync calls per cache miss — still cheap.
|
|
1933
|
+
// Filter `.backup` sidecars (from safe-write tempfile pattern) and
|
|
1934
|
+
// non-*.json entries so corrupted state can't pollute the registry.
|
|
1935
|
+
try {
|
|
1936
|
+
const meetingsDir = path.join(MINIONS_DIR, 'meetings');
|
|
1937
|
+
const entries = fs.readdirSync(meetingsDir);
|
|
1938
|
+
for (const f of entries) {
|
|
1939
|
+
if (f.endsWith('.json') && !f.endsWith('.backup.json')) {
|
|
1940
|
+
files.push(path.join(meetingsDir, f));
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
} catch { /* meetings dir absent → no meetings to track */ }
|
|
1911
1944
|
// Per-project work-items (surfaced by getWorkItems) and pull-requests
|
|
1912
1945
|
// (surfaced by getPullRequests). The PR file was the biggest miss in the
|
|
1913
1946
|
// original tracked list — PR status flips (running → passing, waiting →
|
|
@@ -1932,6 +1965,79 @@ function getStatusFastStateMtimePaths(config) {
|
|
|
1932
1965
|
return files;
|
|
1933
1966
|
}
|
|
1934
1967
|
|
|
1968
|
+
/**
|
|
1969
|
+
* Slow-state mtime tracker — symmetric with the fast-state registry above.
|
|
1970
|
+
*
|
|
1971
|
+
* Slow-state slices (`prdProgress`, `prd`, `verifyGuides`, `archivedPrds`,
|
|
1972
|
+
* `skills`, `mcpServers`, `schedules`, `pipelines`, `pinned`, `projects`,
|
|
1973
|
+
* `version`, etc.) live behind a 60 s TTL in `dashboard.js`. Without mtime
|
|
1974
|
+
* tracking, engine-driven writes to `prd/*.json`, `engine/schedule-runs.json`,
|
|
1975
|
+
* `engine/pipeline-runs.json`, or `prd/guides/*.md` waited up to the full
|
|
1976
|
+
* 60 s before surfacing in the dashboard — which produced the visible
|
|
1977
|
+
* "plan status doesn't update, pipelines never advance" symptom.
|
|
1978
|
+
*
|
|
1979
|
+
* The same conventions as fast-state apply:
|
|
1980
|
+
* - Entries must back something `_buildStatusSlowState()` reads; otherwise
|
|
1981
|
+
* the rebuild silently no-ops on detected change.
|
|
1982
|
+
* - Files mutated through a `mutate*` helper are reliable (`safeWrite`
|
|
1983
|
+
* rename advances mtime). Append-only logs reset the cache often but
|
|
1984
|
+
* are not relevant here.
|
|
1985
|
+
* - Per-project paths must use `shared.project*` so newly-added projects
|
|
1986
|
+
* are picked up without registry edits.
|
|
1987
|
+
*
|
|
1988
|
+
* Files intentionally NOT tracked here:
|
|
1989
|
+
* - `mcpServers`, version, autoMode, installId — change only on human/
|
|
1990
|
+
* CLI edits, which already pop the slow-state via reloadConfig + the
|
|
1991
|
+
* 60 s TTL.
|
|
1992
|
+
* - `~/.claude/skills/`, `~/.copilot/skills/` — user-home dirs that
|
|
1993
|
+
* `extractSkillsFromOutput` writes to from the agent-close path,
|
|
1994
|
+
* which already calls `invalidateStatusCache()` directly.
|
|
1995
|
+
* - project git state — already invalidated via the
|
|
1996
|
+
* `_setOnProjectGitStatusChanged` callback into `invalidateStatusCache`
|
|
1997
|
+
* (W-mpgrk5cy fix); also tracked in fast-state via `.git/logs/HEAD`.
|
|
1998
|
+
*/
|
|
1999
|
+
function getStatusSlowStateMtimePaths(config) {
|
|
2000
|
+
const projects = getProjects(config || getConfig());
|
|
2001
|
+
const files = [
|
|
2002
|
+
// prd/*.json (surfaced by getPrdInfo) — engine writes via syncPrdFromPrs,
|
|
2003
|
+
// the materializer, and plan-to-prd outputs. Dir mtime advances on entry
|
|
2004
|
+
// add/remove; in-file edits use getPrdInfo's own per-file mtime cache so
|
|
2005
|
+
// small-content flips still surface via the 10 s TTL backstop, but the
|
|
2006
|
+
// big "new PRD created" event surfaces immediately.
|
|
2007
|
+
PRD_DIR,
|
|
2008
|
+
// prd/archive/*.json — manual archive moves PRDs here; dir mtime catches
|
|
2009
|
+
// the move.
|
|
2010
|
+
path.join(PRD_DIR, 'archive'),
|
|
2011
|
+
// prd/guides/*.md — verify agent writes new files here on E2E completion.
|
|
2012
|
+
path.join(MINIONS_DIR, 'prd', 'guides'),
|
|
2013
|
+
// engine/schedule-runs.json — scheduler rewrites this on every cron fire.
|
|
2014
|
+
path.join(ENGINE_DIR, 'schedule-runs.json'),
|
|
2015
|
+
// engine/pipeline-runs.json — pipeline executor rewrites this on each
|
|
2016
|
+
// stage transition (the most user-visible slow-state lag pre-fix).
|
|
2017
|
+
path.join(ENGINE_DIR, 'pipeline-runs.json'),
|
|
2018
|
+
// pipelines/*.json — pipeline definitions, edited by humans + plan agents.
|
|
2019
|
+
// Dir mtime is fine because pipeline edits are wholesale file replacements
|
|
2020
|
+
// (no in-place tweaks once a pipeline is authored).
|
|
2021
|
+
path.join(MINIONS_DIR, 'pipelines'),
|
|
2022
|
+
// pinned.md — single file, dashboard-side writes already call
|
|
2023
|
+
// invalidateStatusCache({includeSlow:true}); tracker entry catches any
|
|
2024
|
+
// CLI/editor edit that bypasses the API.
|
|
2025
|
+
path.join(MINIONS_DIR, 'pinned.md'),
|
|
2026
|
+
// engine/skill-states/ — engine writes when agents extract new skills.
|
|
2027
|
+
// Dir mtime catches new skill files. Skips per-skill in-place edits which
|
|
2028
|
+
// the agent-close invalidate already covers.
|
|
2029
|
+
SKILLS_DIR,
|
|
2030
|
+
];
|
|
2031
|
+
// Per-project local skill dirs — agents extract project-scoped skills here.
|
|
2032
|
+
for (const p of projects) {
|
|
2033
|
+
if (p && p.localPath) {
|
|
2034
|
+
files.push(path.join(p.localPath, '.claude', 'skills'));
|
|
2035
|
+
files.push(path.join(p.localPath, '.github', 'skills'));
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
return files;
|
|
2039
|
+
}
|
|
2040
|
+
|
|
1935
2041
|
// ── Exports ─────────────────────────────────────────────────────────────────
|
|
1936
2042
|
|
|
1937
2043
|
module.exports = {
|
|
@@ -1952,6 +2058,7 @@ module.exports = {
|
|
|
1952
2058
|
_setOnProjectGitStatusChanged,
|
|
1953
2059
|
// W-mpftp7na000td0f4 — engine→dashboard cache-invalidation registry
|
|
1954
2060
|
getStatusFastStateMtimePaths,
|
|
2061
|
+
getStatusSlowStateMtimePaths,
|
|
1955
2062
|
|
|
1956
2063
|
// Core state
|
|
1957
2064
|
getConfig, getControl, getDispatch, getDispatchQueue, getDispatchCompletionReport, invalidateDispatchCache,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2027",
|
|
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"
|