@yemi33/minions 0.1.720 → 0.1.722

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.722 (2026-04-09)
4
+
5
+ ### Features
6
+ - server-side KB pin state so CC can pin items
7
+
8
+ ### Fixes
9
+ - remove 200-char truncation from PRD item descriptions in plan viewer (closes #670) (#700)
10
+
3
11
  ## 0.1.720 (2026-04-09)
4
12
 
5
13
  ### Fixes
@@ -126,6 +126,7 @@ async function refresh() {
126
126
  }
127
127
 
128
128
  refresh();
129
+ _syncPinsFromServer(); // Load server-side pins on startup
129
130
 
130
131
  // Poll for status updates (SSE caused HTTP/1.1 connection exhaustion — CC fetch would fail)
131
132
  setInterval(refresh, 4000);
@@ -414,7 +414,7 @@ function _renderPlanModal(normalizedFile, raw, lastMod) {
414
414
  const items = (plan.missing_features || []).map((f, i) =>
415
415
  (i + 1) + '. [' + f.id + '] ' + f.name + ' (' + (f.estimated_complexity || '?') + ', ' + (f.priority || '?') + ')' +
416
416
  (f.depends_on?.length ? ' \u2192 depends on: ' + f.depends_on.join(', ') : '') +
417
- '\n ' + (f.description || '').slice(0, 200) +
417
+ '\n ' + (f.description || '') +
418
418
  (f.acceptance_criteria?.length ? '\n Criteria: ' + f.acceptance_criteria.join('; ') : '')
419
419
  ).join('\n\n');
420
420
  text = 'Project: ' + (plan.project || '?') +
@@ -8,13 +8,13 @@ const _deletedIds = new Map(); // key → expiry timestamp
8
8
  function markDeleted(key) { _deletedIds.set(key, Date.now() + 10000); } // suppress for 10s
9
9
  function isDeleted(key) { const exp = _deletedIds.get(key); if (!exp) return false; if (Date.now() > exp) { _deletedIds.delete(key); return false; } return true; }
10
10
 
11
- // Temporary pin-to-top — UI-only, stored in localStorage, does not affect agents
12
- const PINS_KEY = 'minions-pinned-items';
11
+ // Pin-to-top — persisted server-side so CC and agents can also pin items
13
12
  let _pinsCache = null;
14
13
  function invalidatePinsCache() { _pinsCache = null; }
15
14
  function getPinnedItems() {
16
15
  if (_pinsCache) return _pinsCache;
17
- try { _pinsCache = JSON.parse(localStorage.getItem(PINS_KEY) || '[]'); } catch { _pinsCache = []; }
16
+ // Migrate from localStorage if server hasn't been seeded yet
17
+ try { _pinsCache = JSON.parse(localStorage.getItem('minions-pinned-items') || '[]'); } catch { _pinsCache = []; }
18
18
  return _pinsCache;
19
19
  }
20
20
  function isPinned(key) { return getPinnedItems().includes(key); }
@@ -22,10 +22,20 @@ function togglePin(key) {
22
22
  const pins = getPinnedItems();
23
23
  const idx = pins.indexOf(key);
24
24
  if (idx >= 0) pins.splice(idx, 1); else pins.unshift(key);
25
- localStorage.setItem(PINS_KEY, JSON.stringify(pins));
26
- invalidatePinsCache();
25
+ _pinsCache = pins;
26
+ // Persist to server (fire-and-forget)
27
+ fetch('/api/kb-pins', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ pins }) }).catch(function() {});
27
28
  return idx < 0; // true if now pinned
28
29
  }
30
+ function _syncPinsFromServer() {
31
+ fetch('/api/kb-pins').then(function(r) { return r.json(); }).then(function(d) {
32
+ if (Array.isArray(d.pins) && d.pins.length > 0) {
33
+ _pinsCache = d.pins;
34
+ // Clear legacy localStorage
35
+ localStorage.removeItem('minions-pinned-items');
36
+ }
37
+ }).catch(function() {});
38
+ }
29
39
  function inboxPinKey(name) { return 'notes/inbox/' + name; }
30
40
  function kbPinKey(cat, file) { return 'knowledge/' + cat + '/' + file; }
31
41
  function _togglePinAndRefresh(key, source) {
package/dashboard.html CHANGED
@@ -4273,7 +4273,7 @@ async function planView(file) {
4273
4273
  const items = (plan.missing_features || []).map((f, i) =>
4274
4274
  (i + 1) + '. [' + f.id + '] ' + f.name + ' (' + (f.estimated_complexity || '?') + ', ' + (f.priority || '?') + ')' +
4275
4275
  (f.depends_on?.length ? ' → depends on: ' + f.depends_on.join(', ') : '') +
4276
- '\n ' + (f.description || '').slice(0, 200) +
4276
+ '\n ' + (f.description || '') +
4277
4277
  (f.acceptance_criteria?.length ? '\n Criteria: ' + f.acceptance_criteria.join('; ') : '')
4278
4278
  ).join('\n\n');
4279
4279
  text = 'Project: ' + (plan.project || '?') +
package/dashboard.js CHANGED
@@ -3784,6 +3784,28 @@ What would you like to discuss or change? When you're happy, say "approve" and I
3784
3784
  return jsonReply(res, 200, { ok: true });
3785
3785
  }},
3786
3786
 
3787
+ // KB pin state (server-side so CC can pin items)
3788
+ { method: 'GET', path: '/api/kb-pins', desc: 'Get pinned KB item keys', handler: async (req, res) => {
3789
+ const pins = shared.safeJson(path.join(MINIONS_DIR, 'engine', 'kb-pins.json')) || [];
3790
+ return jsonReply(res, 200, { pins });
3791
+ }},
3792
+ { method: 'POST', path: '/api/kb-pins', desc: 'Set pinned KB item keys', params: 'pins[]', handler: async (req, res) => {
3793
+ const body = await readBody(req);
3794
+ if (!Array.isArray(body.pins)) return jsonReply(res, 400, { error: 'pins array required' });
3795
+ safeWrite(path.join(MINIONS_DIR, 'engine', 'kb-pins.json'), body.pins);
3796
+ return jsonReply(res, 200, { ok: true });
3797
+ }},
3798
+ { method: 'POST', path: '/api/kb-pins/toggle', desc: 'Toggle a single KB pin', params: 'key', handler: async (req, res) => {
3799
+ const body = await readBody(req);
3800
+ if (!body.key) return jsonReply(res, 400, { error: 'key required' });
3801
+ const pinsPath = path.join(MINIONS_DIR, 'engine', 'kb-pins.json');
3802
+ const pins = shared.safeJson(pinsPath) || [];
3803
+ const idx = pins.indexOf(body.key);
3804
+ if (idx >= 0) pins.splice(idx, 1); else pins.unshift(body.key);
3805
+ safeWrite(pinsPath, pins);
3806
+ return jsonReply(res, 200, { ok: true, pinned: idx < 0 });
3807
+ }},
3808
+
3787
3809
  // Notes
3788
3810
  { method: 'POST', path: '/api/notes', desc: 'Write a note to inbox for consolidation', params: 'title, what, why?, author?', handler: handleNotesCreate },
3789
3811
  { method: 'GET', path: '/api/notes-full', desc: 'Return full notes.md content', handler: handleNotesFull },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.720",
3
+ "version": "0.1.722",
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"
@@ -30,7 +30,7 @@ Core action types:
30
30
  - **knowledge**: title, content, category (architecture/conventions/project-notes/build-reports/reviews) — create new KB entry or copy existing doc to KB
31
31
  - **pin-to-pinned**: title, content, level (critical/warning) — write to pinned.md, force-injected into ALL agent prompts (rarely needed)
32
32
 
33
- **IMPORTANT**: When user says "pin", "pin this", "pin a note", or "pin in KB" — they mean save/copy to the **knowledge base** (`knowledge/<category>/`), NOT to `pinned.md`. If a file path is given, read it and write to `knowledge/<category>/<slug>.md`. If no path, search inbox/notes first. Only write to `pinned.md` if user explicitly says "pinned.md" or "critical alert for all agents".
33
+ **IMPORTANT**: When user says "pin", "pin this", "pin a note", or "pin in KB" — they mean **pin an existing KB entry to the top** of the knowledge base list. Do this by calling: `curl -s -X POST http://localhost:7331/api/kb-pins/toggle -H 'Content-Type: application/json' -d '{"key":"knowledge/<category>/<filename>"}'`. If the file isn't in KB yet, first copy it to `knowledge/<category>/<slug>.md`, then pin it. Do NOT write to `pinned.md` unless user explicitly says "pinned.md" or "critical alert for all agents".
34
34
  - **plan**: title, description, project, branchStrategy (parallel/shared-branch)
35
35
  - **cancel**: agent, reason
36
36
  - **retry**: ids[]