@yemi33/minions 0.1.2033 → 0.1.2035
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/engine/queries.js +100 -6
- package/package.json +1 -1
package/engine/queries.js
CHANGED
|
@@ -1798,13 +1798,76 @@ function _projectGitStatusCacheKey(localPath, configuredMainBranch) {
|
|
|
1798
1798
|
return norm + '::' + (configuredMainBranch ? String(configuredMainBranch).trim() : '');
|
|
1799
1799
|
}
|
|
1800
1800
|
|
|
1801
|
+
// Resolve the absolute `.git` directory for `localPath`, transparently
|
|
1802
|
+
// handling linked worktrees (where `<localPath>/.git` is a *file* whose
|
|
1803
|
+
// first line reads `gitdir: <abs path>`). Returns null when no valid git
|
|
1804
|
+
// linkage exists. Synchronous and cheap — one statSync + at most one
|
|
1805
|
+
// short readFileSync. Used by both `_projectGitRefsAdvancedSince` and the
|
|
1806
|
+
// fast-state mtime tracker so the same set of ref files is consulted in
|
|
1807
|
+
// both places, including for linked-worktree repos.
|
|
1808
|
+
function _resolveGitDir(localPath) {
|
|
1809
|
+
if (!localPath) return null;
|
|
1810
|
+
const gitPath = path.join(localPath, '.git');
|
|
1811
|
+
let st;
|
|
1812
|
+
try { st = fs.statSync(gitPath); }
|
|
1813
|
+
catch { return null; }
|
|
1814
|
+
if (st.isDirectory()) return gitPath;
|
|
1815
|
+
if (st.isFile()) {
|
|
1816
|
+
let head = '';
|
|
1817
|
+
try { head = fs.readFileSync(gitPath, { encoding: 'utf8', flag: 'r' }).slice(0, 4096); }
|
|
1818
|
+
catch { return null; }
|
|
1819
|
+
const m = /^gitdir:\s*(.+?)\s*$/m.exec(head);
|
|
1820
|
+
if (!m) return null;
|
|
1821
|
+
const target = m[1];
|
|
1822
|
+
return path.isAbsolute(target) ? target : path.resolve(localPath, target);
|
|
1823
|
+
}
|
|
1824
|
+
return null;
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
// Return true when any of the per-project git ref files (logs/HEAD,
|
|
1828
|
+
// FETCH_HEAD, refs/remotes/origin/<comparator>) have mtimeMs > cachedTs.
|
|
1829
|
+
// Lets `getProjectGitStatus` bypass its 15s TTL after `git pull`, `git
|
|
1830
|
+
// fetch`, `git checkout`, etc. so the next /api/status reflects the new
|
|
1831
|
+
// HEAD / ahead-behind within one SPA poll instead of waiting out the TTL
|
|
1832
|
+
// (W-mphdmr8c00030124). Tolerates ENOENT on FETCH_HEAD / refs (never-
|
|
1833
|
+
// fetched repos simply haven't moved those files yet). Cost ≤3 statSync
|
|
1834
|
+
// per project per /api/status build — well under the 'cheap' budget
|
|
1835
|
+
// called out in getStatusFastStateMtimePaths's docstring.
|
|
1836
|
+
function _projectGitRefsAdvancedSince(localPath, cachedTs, configuredMainBranch) {
|
|
1837
|
+
const gitDir = _resolveGitDir(localPath);
|
|
1838
|
+
if (!gitDir) return false;
|
|
1839
|
+
const candidates = [
|
|
1840
|
+
path.join(gitDir, 'logs', 'HEAD'),
|
|
1841
|
+
path.join(gitDir, 'FETCH_HEAD'),
|
|
1842
|
+
];
|
|
1843
|
+
const comparator = configuredMainBranch && String(configuredMainBranch).trim();
|
|
1844
|
+
if (comparator) {
|
|
1845
|
+
candidates.push(path.join(gitDir, 'refs', 'remotes', 'origin', comparator));
|
|
1846
|
+
}
|
|
1847
|
+
for (const file of candidates) {
|
|
1848
|
+
try {
|
|
1849
|
+
const st = fs.statSync(file);
|
|
1850
|
+
if (st.mtimeMs > cachedTs) return true;
|
|
1851
|
+
} catch { /* ENOENT / EPERM — file just hasn't moved */ }
|
|
1852
|
+
}
|
|
1853
|
+
return false;
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1801
1856
|
function getProjectGitStatus(localPath, configuredMainBranch = null) {
|
|
1802
1857
|
const norm = String(localPath || '').replace(/\\/g, '/');
|
|
1803
1858
|
if (!norm) return PROJECT_GIT_STATUS_MISSING;
|
|
1804
1859
|
const key = _projectGitStatusCacheKey(localPath, configuredMainBranch);
|
|
1805
1860
|
const now = Date.now();
|
|
1806
1861
|
const cached = _projectGitStatusCache.get(key);
|
|
1807
|
-
|
|
1862
|
+
// Within TTL: short-circuit ONLY when no tracked git ref has advanced
|
|
1863
|
+
// past cached.ts. Without the mtime gate, a freshly-pulled repo serves
|
|
1864
|
+
// the pre-pull ahead/behind counts for up to 15s + one SPA poll (~19s
|
|
1865
|
+
// user-visible lag) because the rebuilt fast-state still hits this
|
|
1866
|
+
// cache and never schedules a refresh until the TTL itself expires.
|
|
1867
|
+
if (cached && cached.ts && (now - cached.ts) < PROJECT_GIT_STATUS_TTL
|
|
1868
|
+
&& !_projectGitRefsAdvancedSince(localPath, cached.ts, configuredMainBranch)) {
|
|
1869
|
+
return cached.value;
|
|
1870
|
+
}
|
|
1808
1871
|
// Cheap synchronous existsSync — short-circuits a path that just disappeared
|
|
1809
1872
|
// (project removed) without scheduling a useless git probe.
|
|
1810
1873
|
if (!fs.existsSync(localPath)) {
|
|
@@ -1881,6 +1944,17 @@ function resetProjectGitStatusCache() {
|
|
|
1881
1944
|
* directly, and the 10 s TTL covers CLI-driven control-file edits.
|
|
1882
1945
|
* - `engine/state.json` — surfaced via `getEngineState()` but changes
|
|
1883
1946
|
* only on engine startup / reconcile. Negligible benefit.
|
|
1947
|
+
* - `engine/metrics.json` — surfaced via `getMetrics()` but written by
|
|
1948
|
+
* `trackEngineUsage()` / `trackAgentMetric()` / the engine tick itself
|
|
1949
|
+
* (~10 s batched flush plus every per-agent metric event). Like
|
|
1950
|
+
* `log.json` and `control.json`, its mtime advances at noise-floor
|
|
1951
|
+
* cadence — every engine tick (~60 s) plus several per-agent writes
|
|
1952
|
+
* in between, so with multiple agents active it bumps every few
|
|
1953
|
+
* seconds and busts the ETag cache on /api/status before steady-state
|
|
1954
|
+
* 304s can engage (W-mphejzct00065d8c). The engine-page metrics tile
|
|
1955
|
+
* is not the hot path and the 10 s `FAST_STATE_TTL` is plenty for
|
|
1956
|
+
* metric freshness; mutating handlers that need immediate metrics
|
|
1957
|
+
* visibility should call `invalidateStatusCache()` directly.
|
|
1884
1958
|
* - `engine/cooldowns.json`, `engine/pr-links.json`, `engine/pending-
|
|
1885
1959
|
* rebases.json`, `agents/<id>/managed-spawn.json` — not in the
|
|
1886
1960
|
* `/api/status` payload.
|
|
@@ -1902,11 +1976,15 @@ function resetProjectGitStatusCache() {
|
|
|
1902
1976
|
function getStatusFastStateMtimePaths(config) {
|
|
1903
1977
|
const projects = getProjects(config || getConfig());
|
|
1904
1978
|
const files = [
|
|
1905
|
-
// Engine-level state surfaced by getDispatchQueue
|
|
1906
|
-
// `
|
|
1907
|
-
// "Files intentionally NOT tracked" section above
|
|
1979
|
+
// Engine-level state surfaced by getDispatchQueue. `control.json`,
|
|
1980
|
+
// `log.json`, and `metrics.json` are intentionally omitted — see the
|
|
1981
|
+
// "Files intentionally NOT tracked" section above
|
|
1982
|
+
// (W-mpg8aapw001d7e0c, W-mphejzct00065d8c). dispatch.json stays
|
|
1983
|
+
// tracked because the home + engine sidebar activity dots and
|
|
1984
|
+
// renderDispatch() consume dispatch transitions at sub-FAST_STATE_TTL
|
|
1985
|
+
// cadence (active → completed flips must light up within one SPA
|
|
1986
|
+
// poll, not wait up to 10 s for the periodic SSE backstop).
|
|
1908
1987
|
DISPATCH_PATH,
|
|
1909
|
-
path.join(ENGINE_DIR, 'metrics.json'),
|
|
1910
1988
|
// Watches surfaced by watchesMod.getWatches() (W-mpftp7na000td0f4 fix).
|
|
1911
1989
|
path.join(ENGINE_DIR, 'watches.json'),
|
|
1912
1990
|
// Central work-items.json surfaced by getWorkItems().
|
|
@@ -1955,11 +2033,27 @@ function getStatusFastStateMtimePaths(config) {
|
|
|
1955
2033
|
// NOT advanced on a timer — it only moves on user-initiated git
|
|
1956
2034
|
// operations, so it can't dominate legitimate state changes. Cheap (one
|
|
1957
2035
|
// statSync per project per cache miss).
|
|
2036
|
+
//
|
|
2037
|
+
// Per-project `.git/FETCH_HEAD` — bare `git fetch` advances FETCH_HEAD
|
|
2038
|
+
// without touching `.git/logs/HEAD` (no HEAD move) or `.git/index` (no
|
|
2039
|
+
// working-tree change). Without this entry, an external `git fetch`
|
|
2040
|
+
// (or another tool background-fetching) leaves the dashboard's
|
|
2041
|
+
// _statusCache and getProjectGitStatus's inner cache both serving the
|
|
2042
|
+
// pre-fetch ahead/behind counts until BOTH the 10s FAST_STATE_TTL and
|
|
2043
|
+
// the 15s probe TTL expire (W-mphdmr8c00030124).
|
|
2044
|
+
//
|
|
2045
|
+
// For linked worktrees (`<localPath>/.git` is a file pointing to
|
|
2046
|
+
// `<main>/.git/worktrees/<name>/`), `_resolveGitDir` walks the pointer
|
|
2047
|
+
// so logs/HEAD and FETCH_HEAD are tracked at the actual gitdir; the
|
|
2048
|
+
// statSync in dashboard's `_getMtimes` tolerates ENOENT, so falling
|
|
2049
|
+
// back to `<localPath>/.git/...` for non-linked-worktree repos is safe.
|
|
1958
2050
|
for (const p of projects) {
|
|
1959
2051
|
files.push(shared.projectWorkItemsPath(p));
|
|
1960
2052
|
files.push(shared.projectPrPath(p));
|
|
1961
2053
|
if (p && p.localPath) {
|
|
1962
|
-
|
|
2054
|
+
const gitDir = _resolveGitDir(p.localPath) || path.join(p.localPath, '.git');
|
|
2055
|
+
files.push(path.join(gitDir, 'logs', 'HEAD'));
|
|
2056
|
+
files.push(path.join(gitDir, 'FETCH_HEAD'));
|
|
1963
2057
|
}
|
|
1964
2058
|
}
|
|
1965
2059
|
return files;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2035",
|
|
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"
|