@yemi33/minions 0.1.1557 → 0.1.1559

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,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.1559 (2026-04-27)
4
+
5
+ ### Features
6
+ - removeProject also archives plans + PRDs targeting the project
7
+
8
+ ## 0.1.1558 (2026-04-27)
9
+
10
+ ### Fixes
11
+ - show Scan button in empty state for fresh installs
12
+
3
13
  ## 0.1.1557 (2026-04-27)
4
14
 
5
15
  ### Features
@@ -5,7 +5,8 @@ function renderProjects(projects) {
5
5
  const visible = (projects || []).filter(p => typeof isDeleted === 'function' ? !isDeleted('project:' + p.name) : true);
6
6
  if (!visible.length) {
7
7
  list.innerHTML = '<span style="color:var(--muted);font-style:italic">No projects linked.</span>' +
8
- '<span onclick="addProject()" style="background:var(--surface2);border:1px solid var(--border);border-radius:4px;padding:3px 10px;color:var(--green);font-weight:500;cursor:pointer;border-style:dashed;margin-left:8px">+ Add Project</span>';
8
+ '<span onclick="addProject()" style="background:var(--surface2);border:1px solid var(--border);border-radius:4px;padding:3px 10px;color:var(--green);font-weight:500;cursor:pointer;border-style:dashed;margin-left:8px">+ Add Project</span>' +
9
+ '<span onclick="openScanProjectsModal()" style="background:var(--surface2);border:1px solid var(--border);border-radius:4px;padding:3px 10px;color:var(--blue);font-weight:500;cursor:pointer;border-style:dashed;font-size:10px">Scan</span>';
9
10
  return;
10
11
  }
11
12
  list.innerHTML = visible.map(p =>
@@ -43,6 +44,7 @@ async function projectChipRemove(name) {
43
44
  var parts = ['Removed "' + name + '"'];
44
45
  if (d.cancelledItems) parts.push(d.cancelledItems + ' WI cancelled');
45
46
  if (d.drainedDispatches) parts.push(d.drainedDispatches + ' dispatch drained');
47
+ if (d.archivedPlans?.length) parts.push(d.archivedPlans.length + ' plan/PRD archived');
46
48
  if (d.archivedTo) parts.push('archived');
47
49
  if (d.pipelineRefs?.length) parts.push('! pipelines still reference: ' + d.pipelineRefs.join(', '));
48
50
  showToast('cmd-toast', parts.join(' — '), true);
@@ -424,6 +424,7 @@ async function removeProject(name) {
424
424
  if (d.cancelledItems) parts.push(d.cancelledItems + ' WI cancelled');
425
425
  if (d.drainedDispatches) parts.push(d.drainedDispatches + ' dispatch drained');
426
426
  if (d.cleanedWorktrees) parts.push(d.cleanedWorktrees + ' worktree(s) cleaned');
427
+ if (d.archivedPlans?.length) parts.push(d.archivedPlans.length + ' plan/PRD archived');
427
428
  if (d.archivedTo) parts.push('archived to ' + d.archivedTo);
428
429
  if (d.pipelineRefs?.length) parts.push('! pipelines still reference: ' + d.pipelineRefs.join(', '));
429
430
  showToast('cmd-toast', parts.join(' — '), true);
@@ -44,6 +44,7 @@ function removeProject(target, options = {}) {
44
44
  drainedDispatches: 0, // includes active dispatches whose agent processes were killed
45
45
  cleanedWorktrees: 0,
46
46
  disabledSchedules: 0,
47
+ archivedPlans: [],
47
48
  pipelineRefs: [],
48
49
  archivedTo: null,
49
50
  purgedDataDir: false,
@@ -110,7 +111,52 @@ function removeProject(target, options = {}) {
110
111
  }
111
112
  }
112
113
 
113
- // 5. Surface pipelines that reference this project so the user can review
114
+ // 5. Archive plans + PRDs targeting this project so they don't keep
115
+ // showing in the dashboard after removal. Three signals:
116
+ // a) PRD JSON `project` field matches → archive PRD + linked source plan
117
+ // b) plan .md content has `Project: <name>` (any markdown emphasis)
118
+ // c) plan .md filename contains the project name (case-insensitive)
119
+ // The .backup sidecar removal in archivePlan prevents engine restart
120
+ // from re-triggering plan completion (mirror of f28162b0 fix).
121
+ const plansDir = path.join(MINIONS_DIR, 'plans');
122
+ const plansArchive = path.join(plansDir, 'archive');
123
+ const prdDir = path.join(MINIONS_DIR, 'prd');
124
+ const escName = project.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
125
+ const projectLineRe = new RegExp('^[*_>\\s]{0,4}\\*{0,2}_{0,2}Project:?\\*{0,2}_{0,2}\\s*' + escName + '\\b', 'im');
126
+ const archivedSourcePlans = new Set();
127
+ try {
128
+ if (fs.existsSync(prdDir)) {
129
+ const { archivePlan } = require('./lifecycle');
130
+ for (const f of fs.readdirSync(prdDir).filter(f => f.endsWith('.json'))) {
131
+ try {
132
+ const prd = JSON.parse(fs.readFileSync(path.join(prdDir, f), 'utf8'));
133
+ if (prd?.project !== project.name) continue;
134
+ archivePlan(f, prd, [project], config);
135
+ summary.archivedPlans.push('prd/' + f);
136
+ if (prd.source_plan) archivedSourcePlans.add(prd.source_plan);
137
+ } catch { /* skip unreadable PRD */ }
138
+ }
139
+ }
140
+ } catch (e) { summary.warnings.push('archive PRDs: ' + e.message); }
141
+ try {
142
+ if (fs.existsSync(plansDir)) {
143
+ fs.mkdirSync(plansArchive, { recursive: true });
144
+ const lowerName = project.name.toLowerCase();
145
+ for (const f of fs.readdirSync(plansDir).filter(f => f.endsWith('.md'))) {
146
+ if (archivedSourcePlans.has(f)) continue; // already moved by archivePlan above
147
+ const fp = path.join(plansDir, f);
148
+ try {
149
+ const filenameMatch = f.toLowerCase().includes(lowerName);
150
+ const content = filenameMatch ? '' : fs.readFileSync(fp, 'utf8');
151
+ if (!filenameMatch && !projectLineRe.test(content)) continue;
152
+ fs.renameSync(fp, path.join(plansArchive, f));
153
+ summary.archivedPlans.push('plans/' + f);
154
+ } catch { /* skip unreadable plan */ }
155
+ }
156
+ }
157
+ } catch (e) { summary.warnings.push('archive plans: ' + e.message); }
158
+
159
+ // 6. Surface pipelines that reference this project so the user can review
114
160
  // them. Don't auto-modify — user intent there is unclear.
115
161
  try {
116
162
  const { getPipelines } = require('./pipeline');
@@ -125,12 +171,12 @@ function removeProject(target, options = {}) {
125
171
  }
126
172
  } catch { /* pipelines optional */ }
127
173
 
128
- // 6. Remove from config.json (and persist any schedule disables)
174
+ // 7. Remove from config.json (and persist any schedule disables)
129
175
  config.projects = (config.projects || []).filter(p => p.name !== project.name);
130
176
  try { fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); }
131
177
  catch (e) { return { ...summary, error: 'Failed to write config: ' + e.message }; }
132
178
 
133
- // 7. Move (or purge) projects/<name>/ — preserves PR/work-item history by
179
+ // 8. Move (or purge) projects/<name>/ — preserves PR/work-item history by
134
180
  // default so a re-add can pick up where it left off.
135
181
  const dataDir = path.join(MINIONS_DIR, 'projects', project.name);
136
182
  if (fs.existsSync(dataDir)) {
package/minions.js CHANGED
@@ -221,6 +221,7 @@ function removeProject(target, options = {}) {
221
221
  if (result.cancelledItems) lines.push(` cancelled ${result.cancelledItems} pending work item(s)`);
222
222
  if (result.drainedDispatches) lines.push(` drained ${result.drainedDispatches} dispatch entr${result.drainedDispatches === 1 ? 'y' : 'ies'} (active agents killed)`);
223
223
  if (result.cleanedWorktrees) lines.push(` cleaned ${result.cleanedWorktrees} worktree(s)`);
224
+ if (result.archivedPlans?.length) lines.push(` archived ${result.archivedPlans.length} plan/PRD file(s)`);
224
225
  if (result.disabledSchedules) lines.push(` disabled ${result.disabledSchedules} schedule(s)`);
225
226
  if (result.archivedTo) lines.push(` archived data → ${result.archivedTo}`);
226
227
  if (result.purgedDataDir) lines.push(` purged data directory`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.1557",
3
+ "version": "0.1.1559",
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"