@yemi33/minions 0.1.2036 → 0.1.2037

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.
@@ -122,7 +122,7 @@ function _formatCcDrawerLabel(autoMode) {
122
122
 
123
123
  function _processStatusUpdate(data) {
124
124
  // Detect fresh install — clear stale browser state AND reload so module-scoped
125
- // JS caches (_sectionCache, _prevCounts, _kbRefreshCount, _managedProcessesLastItems,
125
+ // JS caches (_sectionCache, _prevCounts, _managedProcessesLastItems,
126
126
  // _pipelinePollHash, _meetingPollHash, etc.) reinitialise against the new install's
127
127
  // data shape. localStorage.clear() alone leaves those caches stale after a
128
128
  // MINIONS_HOME swap that doesn't restart the dashboard (F11 / W-mpgcijjo000ce878).
@@ -252,16 +252,25 @@ function _processStatusUpdate(data) {
252
252
  if (swi) swi.textContent = (data.workItems || []).length || '';
253
253
  const spr = document.getElementById('sidebar-pr');
254
254
  if (spr) spr.textContent = (data.pullRequests || []).length || '';
255
- // Refresh KB and plans less frequently (every 3rd cycle = ~12s)
256
- if (!window._kbRefreshCount) window._kbRefreshCount = 0;
257
- if (window._kbRefreshCount++ % 3 === 0) { refreshKnowledgeBase(); refreshPlans(); }
255
+ // Refresh KB and plans every status cycle (~4s) so plan status flips
256
+ // (approve/archive/complete) and KB additions surface within the SPA's
257
+ // 4s poll target. Server-side caches keep this cheap:
258
+ // - /api/plans: 5s in-memory TTL + invalidation on every mutating endpoint
259
+ // (dashboard.js _plansCache / invalidatePlansCache) — back-to-back polls
260
+ // hit the cache; external file edits surface within 5s.
261
+ // - /api/knowledge: 30s in-memory TTL + invalidation on POST /api/knowledge
262
+ // and after every kb-sweep (engine/queries.js _kbCache / kb-sweep.js).
263
+ // Previously throttled to every 3rd cycle (~12s) — see W-mphfb6ss000a3b9e
264
+ // for the cadence audit + Playwright coverage.
265
+ refreshKnowledgeBase();
266
+ refreshPlans();
258
267
 
259
268
  // Cross-slice render triggers (F1/F3, W-mpgb0xbh000e3b86): renderPrs reads
260
269
  // window._lastWorkItems for the +N follow-up chip count and derivePlanStatus
261
270
  // reads window._lastStatus.pullRequests for verify-follow-up reconciliation.
262
271
  // When workItems OR pullRequests change, re-render the dependents against the
263
- // freshest cached data — without waiting up to 12s for the next refreshPlans
264
- // cycle (F3) or for a PR object to mutate (F1). Runs after the window._last*
272
+ // freshest cached data — without waiting for the next refreshPlans cycle (F3)
273
+ // or for a PR object to mutate (F1). Runs after the window._last*
265
274
  // assignments above so the cached globals these renderers consult are fresh.
266
275
  if (_workItemsChanged && !_prsChanged) {
267
276
  // F1: only the work-item slice moved this tick — renderPrs wasn't called
@@ -271,9 +280,9 @@ function _processStatusUpdate(data) {
271
280
  }
272
281
  if ((_workItemsChanged || _prsChanged) && Array.isArray(window._lastPlans) && typeof renderPlans === 'function') {
273
282
  // F3: derivePlanStatus + _renderVerifyBadge derive from pullRequests +
274
- // workItems. Re-render against the cached plan list so plan status flips
275
- // within one /api/status tick (~4s) instead of one refreshPlans poll
276
- // (~12s). No-op when _lastPlans hasn't been populated yet (first load).
283
+ // workItems. Re-render against cached plans so plan status flips within
284
+ // one /api/status tick (~4s) instead of one refreshPlans poll. No-op
285
+ // until _lastPlans is populated.
277
286
  renderPlans(window._lastPlans);
278
287
  }
279
288
 
@@ -27,10 +27,13 @@ let currentPage = getPageFromUrl();
27
27
 
28
28
  // W-mpgb0xa7000d90d4 (dashboard-refresh-audit.md F5) — switchPage previously
29
29
  // only cleaned intervals and flipped CSS; tabs like /plans and /inbox waited
30
- // up to ~12s for refresh.js's slow-cycle (refreshKnowledgeBase + refreshPlans
31
- // every 3rd status tick) before showing fresh data. The QA tab worked around
32
- // this with its own switchPage monkey-patch in qa.js (__qaWrapped) — that
33
- // per-tab pattern is now generalized into one canonical lifecycle below.
30
+ // for refresh.js's next slow-cycle (refreshKnowledgeBase + refreshPlans) before
31
+ // showing fresh data. The QA tab worked around this with its own switchPage
32
+ // monkey-patch in qa.js (__qaWrapped) — that per-tab pattern is now generalized
33
+ // into one canonical lifecycle below. (As of W-mphfb6ss000a3b9e the slow-cycle
34
+ // modulo was dropped, so refreshPlans/refreshKnowledgeBase now run every 4s
35
+ // status tick — but the page-enter loader still wins on tab-switch because it
36
+ // fires synchronously without waiting for the next interval boundary.)
34
37
  //
35
38
  // PAGE_LAZY_LOADERS: functions to invoke when ENTERING a page (resolved by
36
39
  // name through window so the function body can live alongside its tab UI).
package/dashboard.js CHANGED
@@ -1628,6 +1628,20 @@ function _getSlowMtimes() {
1628
1628
  return result;
1629
1629
  }
1630
1630
 
1631
+ // Reset the per-source caches that outlive the slow-state TTL when a tracked
1632
+ // source file changes (W-mphfdgwv000bf549). The slow-state mtime tracker now
1633
+ // covers skill discovery dirs and MCP config files, but
1634
+ // `queries._skillsCache` (30 s) and the local `_mcpServersCache` (5 min)
1635
+ // would still serve stale data into `_buildStatusSlowState()` — defeating
1636
+ // the <4 s freshness goal. Only call this when an mtime delta is detected;
1637
+ // TTL-driven rebuilds keep using the inner caches so we don't pay disk-scan
1638
+ // cost on every 60 s slow-state rollover.
1639
+ function _invalidateSlowInnerCachesForMtimeChange() {
1640
+ try { queries.invalidateSkillsCache(); } catch { /* optional */ }
1641
+ _mcpServersCache = null;
1642
+ _mcpServersCacheTs = 0;
1643
+ }
1644
+
1631
1645
  function _mtimesChanged(prev, curr) {
1632
1646
  for (const fp of Object.keys(curr)) {
1633
1647
  if (prev[fp] !== curr[fp]) return true;
@@ -1838,9 +1852,13 @@ function getStatus() {
1838
1852
  // so changes surface within one SPA poll (~4 s) instead of waiting up to
1839
1853
  // 60 s for TTL. Same pre-build snapshot semantics as fast-state below.
1840
1854
  let slowStale = !_slowState || (now - _slowStateTs) >= SLOW_STATE_TTL;
1855
+ let slowMtimeChanged = false;
1841
1856
  if (!slowStale) {
1842
1857
  const currSlowMtimes = _getSlowMtimes();
1843
- if (_mtimesChanged(_lastSlowMtimes, currSlowMtimes)) slowStale = true;
1858
+ if (_mtimesChanged(_lastSlowMtimes, currSlowMtimes)) {
1859
+ slowStale = true;
1860
+ slowMtimeChanged = true;
1861
+ }
1844
1862
  }
1845
1863
 
1846
1864
  // If nothing stale, return cached merged result
@@ -1867,6 +1885,7 @@ function getStatus() {
1867
1885
  // Same pre-build snapshot pattern as fast-state — capture mtimes BEFORE
1868
1886
  // disk reads so any write landing mid-build busts the next poll.
1869
1887
  if (slowStale) {
1888
+ if (slowMtimeChanged) _invalidateSlowInnerCachesForMtimeChange();
1870
1889
  const preBuildSlowMtimes = _getSlowMtimes();
1871
1890
  _slowState = _buildStatusSlowState();
1872
1891
  _slowStateTs = now;
@@ -1912,9 +1931,13 @@ function refreshStatusAsync() {
1912
1931
  if (_mtimesChanged(_lastMtimes, currMtimes)) fastStale = true;
1913
1932
  }
1914
1933
  let slowStale = !_slowState || (now - _slowStateTs) >= SLOW_STATE_TTL;
1934
+ let slowMtimeChanged = false;
1915
1935
  if (!slowStale) {
1916
1936
  const currSlowMtimes = _getSlowMtimes();
1917
- if (_mtimesChanged(_lastSlowMtimes, currSlowMtimes)) slowStale = true;
1937
+ if (_mtimesChanged(_lastSlowMtimes, currSlowMtimes)) {
1938
+ slowStale = true;
1939
+ slowMtimeChanged = true;
1940
+ }
1918
1941
  }
1919
1942
 
1920
1943
  if (!fastStale && !slowStale && _statusCache) return _statusCache;
@@ -1944,6 +1967,7 @@ function refreshStatusAsync() {
1944
1967
  let slow = _slowState;
1945
1968
  let preBuildSlowMtimes = null;
1946
1969
  if (slowStale) {
1970
+ if (slowMtimeChanged) _invalidateSlowInnerCachesForMtimeChange();
1947
1971
  preBuildSlowMtimes = _getSlowMtimes();
1948
1972
  slow = _buildStatusSlowState();
1949
1973
  }
package/engine/queries.js CHANGED
@@ -2079,25 +2079,43 @@ function getStatusFastStateMtimePaths(config) {
2079
2079
  * - Per-project paths must use `shared.project*` so newly-added projects
2080
2080
  * are picked up without registry edits.
2081
2081
  *
2082
+ * Skill and MCP source paths (W-mphfdgwv000bf549):
2083
+ * - Skill discovery roots (`~/.claude/skills`, `~/.copilot/skills`,
2084
+ * `~/.agents/skills`, `<project>/.claude/skills`, `<project>/.github/skills`,
2085
+ * `<project>/.agents/skills`) plus plugin registries are tracked here
2086
+ * because manual `SKILL.md` drops bypass the agent-close
2087
+ * `invalidateStatusCache({includeSlow:true})` path that previously
2088
+ * covered the agent-extraction case. Directory mtime advances reliably
2089
+ * on Windows NTFS when a subdirectory is added/removed (verified via
2090
+ * INBOX_DIR). Tracking the user-home dirs means non-Minions skill
2091
+ * installs on this machine also bust the fleet's slow-state — but skill
2092
+ * dirs only mutate on rare events (install / manual edit), not on every
2093
+ * CLI command, so the steady-state noise is negligible.
2094
+ * - MCP config files (`~/.claude.json`, `~/.copilot/mcp-config.json`,
2095
+ * `<project>/.mcp.json`) feed `getMcpServers()`. `~/.claude.json`
2096
+ * stores more than `mcpServers`, but it only flips on intentional
2097
+ * Claude CLI mutations (mcp add/remove, settings edits), not on every
2098
+ * prompt, so whole-file tracking is acceptable.
2099
+ *
2082
2100
  * Files intentionally NOT tracked here:
2083
- * - `mcpServers`, version, autoMode, installId — change only on human/
2084
- * CLI edits, which already pop the slow-state via reloadConfig + the
2085
- * 60 s TTL.
2086
- * - `~/.claude/skills/`, `~/.copilot/skills/`, `<project>/.claude/skills/`,
2087
- * `<project>/.github/skills/` — `extractSkillsFromOutput` writes here
2088
- * from the agent-close path, which already calls
2089
- * `invalidateStatusCache({includeSlow: true})` directly. Tracking the
2090
- * user-home dir is additionally harmful because it's shared with every
2091
- * Claude Code session on the machine; non-Minions activity would
2092
- * otherwise bust this fleet's dashboard cache.
2101
+ * - version, autoMode, installId — change only on human/CLI edits, which
2102
+ * already pop the slow-state via reloadConfig + the 60 s TTL.
2093
2103
  * - project git state — already invalidated via the
2094
2104
  * `_setOnProjectGitStatusChanged` callback into `invalidateStatusCache`
2095
2105
  * (W-mpgrk5cy fix); also tracked in fast-state via `.git/logs/HEAD`.
2106
+ *
2107
+ * NOTE: Detecting a change here busts the dashboard's slow-state cache, but
2108
+ * the inner per-source caches (`queries._skillsCache` 30 s, dashboard's
2109
+ * `_mcpServersCache` 5 min) survive across `_buildStatusSlowState()` calls.
2110
+ * dashboard.js calls `queries.invalidateSkillsCache()` and resets
2111
+ * `_mcpServersCache` whenever this tracker fires, so the rebuild reads
2112
+ * fresh disk state. Keep that invalidation wired up if you add new sources.
2096
2113
  */
2097
- function getStatusSlowStateMtimePaths(_config) {
2098
- // _config accepted for symmetry with getStatusFastStateMtimePaths but
2099
- // unused every entry below is a fleet-global path.
2100
- return [
2114
+ function getStatusSlowStateMtimePaths(config) {
2115
+ config = config || getConfig();
2116
+ const projects = getProjects(config);
2117
+ const homeDir = os.homedir();
2118
+ const files = [
2101
2119
  // prd/*.json (surfaced by getPrdInfo) — engine writes via syncPrdFromPrs,
2102
2120
  // the materializer, and plan-to-prd outputs.
2103
2121
  PRD_DIR,
@@ -2117,6 +2135,42 @@ function getStatusSlowStateMtimePaths(_config) {
2117
2135
  // CLI/editor edit that bypasses the API.
2118
2136
  path.join(MINIONS_DIR, 'pinned.md'),
2119
2137
  ];
2138
+
2139
+ // Skill discovery roots (surfaced by _buildStatusSlowState → getSkills).
2140
+ // Mirrors collectSkillFiles' source enumeration so adding a new runtime
2141
+ // adapter automatically extends the tracker. ENOENT is tolerated by
2142
+ // dashboard._statMtimeMs (returns 0), so absent dirs cost nothing.
2143
+ try {
2144
+ const { listRuntimes, resolveRuntime } = require('./runtimes');
2145
+ for (const runtimeName of listRuntimes()) {
2146
+ const runtime = resolveRuntime(runtimeName);
2147
+ if (typeof runtime.getSkillRoots !== 'function') continue;
2148
+ for (const root of runtime.getSkillRoots({ homeDir })) {
2149
+ if (root && root.dir) files.push(root.dir);
2150
+ }
2151
+ for (const project of projects) {
2152
+ if (!project || !project.localPath) continue;
2153
+ for (const root of runtime.getSkillRoots({ homeDir, project })) {
2154
+ if (root && root.dir) files.push(root.dir);
2155
+ }
2156
+ }
2157
+ }
2158
+ } catch { /* runtime registry optional in partial installs */ }
2159
+
2160
+ // Plugin skill registries (also feed collectSkillFiles).
2161
+ files.push(path.join(homeDir, '.claude', 'plugins', 'installed_plugins.json'));
2162
+ files.push(path.join(homeDir, '.copilot', 'installed-plugins'));
2163
+
2164
+ // MCP server config files (surfaced by _buildStatusSlowState → getMcpServers).
2165
+ files.push(path.join(homeDir, '.claude.json'));
2166
+ files.push(path.join(homeDir, '.copilot', 'mcp-config.json'));
2167
+ for (const project of projects) {
2168
+ if (project && project.localPath) {
2169
+ files.push(path.join(project.localPath, '.mcp.json'));
2170
+ }
2171
+ }
2172
+
2173
+ return files;
2120
2174
  }
2121
2175
 
2122
2176
  // ── Exports ─────────────────────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2036",
3
+ "version": "0.1.2037",
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"