@yemi33/squad 0.1.19 → 0.1.21

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.js CHANGED
@@ -1784,24 +1784,28 @@ async function handlePostMerge(pr, project, config, newStatus) {
1784
1784
  // Only run remaining hooks for merged PRs (not abandoned)
1785
1785
  if (newStatus !== 'merged') return;
1786
1786
 
1787
- // 2. Update PRD item status to 'implemented' (squad-level PRD)
1787
+ // 2. Update PRD item status to 'implemented' in plan files
1788
1788
  if (pr.prdItems?.length > 0) {
1789
- const prdPath = path.join(SQUAD_DIR, 'prd.json');
1790
- const prd = safeJson(prdPath);
1791
- if (prd?.missing_features) {
1789
+ const plansDir = path.join(SQUAD_DIR, 'plans');
1790
+ try {
1791
+ const planFiles = fs.readdirSync(plansDir).filter(f => f.endsWith('.json'));
1792
1792
  let updated = 0;
1793
- for (const itemId of pr.prdItems) {
1794
- const feature = prd.missing_features.find(f => f.id === itemId);
1795
- if (feature && feature.status !== 'implemented') {
1796
- feature.status = 'implemented';
1797
- updated++;
1793
+ for (const pf of planFiles) {
1794
+ const plan = safeJson(path.join(plansDir, pf));
1795
+ if (!plan?.missing_features) continue;
1796
+ let changed = false;
1797
+ for (const itemId of pr.prdItems) {
1798
+ const feature = plan.missing_features.find(f => f.id === itemId);
1799
+ if (feature && feature.status !== 'implemented') {
1800
+ feature.status = 'implemented';
1801
+ changed = true;
1802
+ updated++;
1803
+ }
1798
1804
  }
1805
+ if (changed) safeWrite(path.join(plansDir, pf), plan);
1799
1806
  }
1800
- if (updated > 0) {
1801
- safeWrite(prdPath, prd);
1802
- log('info', `Post-merge: marked ${updated} PRD item(s) as implemented for ${pr.id}`);
1803
- }
1804
- }
1807
+ if (updated > 0) log('info', `Post-merge: marked ${updated} PRD item(s) as implemented for ${pr.id}`);
1808
+ } catch {}
1805
1809
  }
1806
1810
 
1807
1811
  // 3. Update agent metrics
@@ -2601,9 +2605,17 @@ function checkTimeouts(config) {
2601
2605
  completed_at: ts()
2602
2606
  });
2603
2607
 
2604
- // Run post-completion hooks
2608
+ // Run post-completion hooks (same as proc.on('close') path)
2605
2609
  if (isSuccess) {
2606
2610
  const config = getConfig();
2611
+ // Chain plan → plan-to-prd
2612
+ if (item.type === 'plan' && item.meta?.item?.chain === 'plan-to-prd') {
2613
+ chainPlanToPrd(item, item.meta, config);
2614
+ }
2615
+ // Check shared-branch plan completion
2616
+ if (item.meta?.item?.branchStrategy === 'shared-branch') {
2617
+ checkPlanCompletion(item.meta, config);
2618
+ }
2607
2619
  syncPrsFromOutput(liveLog, item.agent, item.meta, config);
2608
2620
  checkForLearnings(item.agent, config.agents?.[item.agent], item.task);
2609
2621
  updateAgentHistory(item.agent, item, isSuccess ? 'success' : 'error');
@@ -2979,96 +2991,7 @@ function isAlreadyDispatched(key) {
2979
2991
  return recentCompleted.some(d => d.meta?.dispatchKey === key);
2980
2992
  }
2981
2993
 
2982
- /**
2983
- * Scan squad-level PRD (~/.squad/prd.json) for missing/planned items → queue implement tasks.
2984
- * Items can span multiple projects via the `projects` array field.
2985
- */
2986
- function discoverFromPrd(config) {
2987
- const prdPath = path.join(SQUAD_DIR, 'prd.json');
2988
- const prd = safeJson(prdPath);
2989
- if (!prd) return [];
2990
-
2991
- const cooldownMs = (config.workSources?.prd?.cooldownMinutes || 30) * 60 * 1000;
2992
- const statusFilter = config.workSources?.prd?.itemFilter?.status || ['missing', 'planned'];
2993
- const items = (prd.missing_features || []).filter(f => statusFilter.includes(f.status));
2994
- const newWork = [];
2995
- const skipped = { dispatched: 0, cooldown: 0, noAgent: 0, noProject: 0 };
2996
- const allProjects = config.projects || [];
2997
2994
 
2998
- for (const item of items) {
2999
- const key = `prd-squad-${item.id}`;
3000
- if (isAlreadyDispatched(key)) { skipped.dispatched++; continue; }
3001
- if (isOnCooldown(key, cooldownMs)) { skipped.cooldown++; continue; }
3002
-
3003
- // Resolve target projects from item.projects array
3004
- const targetProjects = (item.projects || [])
3005
- .map(name => allProjects.find(p => p.name.toLowerCase() === name.toLowerCase()))
3006
- .filter(Boolean);
3007
- if (targetProjects.length === 0) { skipped.noProject++; continue; }
3008
-
3009
- const workType = item.estimated_complexity === 'large' ? 'implement:large' : 'implement';
3010
- const agentId = resolveAgent(workType, config);
3011
- if (!agentId) { skipped.noAgent++; continue; }
3012
-
3013
- // Primary project = first in the list (used for worktree, branch, PR)
3014
- const primary = targetProjects[0];
3015
- const root = path.resolve(primary.localPath);
3016
- const branchName = `feature/${item.id.toLowerCase()}-${item.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').slice(0, 40)}`;
3017
-
3018
- // Build related projects context for multi-project items
3019
- let relatedProjects = '';
3020
- if (targetProjects.length > 1) {
3021
- relatedProjects = targetProjects.map(p =>
3022
- `- **${p.name}** — ${path.resolve(p.localPath)} (${p.adoOrg}/${p.adoProject}/${p.repoName}, branch: ${p.mainBranch || 'main'})`
3023
- ).join('\n');
3024
- }
3025
-
3026
- const vars = {
3027
- agent_id: agentId,
3028
- agent_name: config.agents[agentId]?.name || agentId,
3029
- agent_role: config.agents[agentId]?.role || 'Agent',
3030
- item_id: item.id,
3031
- item_name: item.name,
3032
- item_priority: item.priority || 'medium',
3033
- item_complexity: item.estimated_complexity || 'medium',
3034
- item_description: item.description || '',
3035
- branch_name: branchName,
3036
- project_path: root,
3037
- main_branch: primary.mainBranch || 'main',
3038
- worktree_path: path.resolve(root, config.engine?.worktreeRoot || '../worktrees', branchName),
3039
- commit_message: `feat(${item.id.toLowerCase()}): ${item.name}`,
3040
- team_root: SQUAD_DIR,
3041
- repo_id: primary.repositoryId || '',
3042
- project_name: targetProjects.map(p => p.name).join(', '),
3043
- ado_org: primary.adoOrg || 'Unknown',
3044
- ado_project: primary.adoProject || 'Unknown',
3045
- repo_name: primary.repoName || 'Unknown',
3046
- related_projects: relatedProjects,
3047
- };
3048
-
3049
- const prompt = renderPlaybook('implement', vars);
3050
- if (!prompt) continue;
3051
-
3052
- newWork.push({
3053
- type: workType,
3054
- agent: agentId,
3055
- agentName: config.agents[agentId]?.name,
3056
- agentRole: config.agents[agentId]?.role,
3057
- task: `[${vars.project_name}] Implement ${item.id}: ${item.name}`,
3058
- prompt,
3059
- meta: { dispatchKey: key, source: 'prd', branch: branchName, item, project: { name: primary.name, localPath: primary.localPath } }
3060
- });
3061
-
3062
- setCooldown(key);
3063
- }
3064
-
3065
- const skipTotal = skipped.dispatched + skipped.cooldown + skipped.noAgent + skipped.noProject;
3066
- if (skipTotal > 0) {
3067
- log('debug', `PRD discovery: skipped ${skipTotal} items (${skipped.dispatched} dispatched, ${skipped.cooldown} cooldown, ${skipped.noAgent} no agent, ${skipped.noProject} no project)`);
3068
- }
3069
-
3070
- return newWork;
3071
- }
3072
2995
 
3073
2996
  /**
3074
2997
  * Scan ~/.squad/plans/ for plan-generated PRD files → queue implement tasks.
@@ -3934,7 +3857,7 @@ function discoverWork(config) {
3934
3857
  }
3935
3858
 
3936
3859
  // Source 2: Squad-level PRD → implements (multi-project, called once outside project loop)
3937
- allImplements.push(...discoverFromPrd(config));
3860
+ // PRD items now flow through plans/*.json → materializePlansAsWorkItems → discoverFromWorkItems
3938
3861
 
3939
3862
  // Central work items (project-agnostic — agent decides where to work)
3940
3863
  const centralWork = discoverCentralWorkItems(config);
@@ -4466,7 +4389,7 @@ const commands = {
4466
4389
  });
4467
4390
 
4468
4391
  console.log(`Dispatched: ${id} → ${config.agents[agentId]?.name} (${agentId})`);
4469
- console.log('The agent will analyze your plan and generate docs/prd-gaps.json as a PR.');
4392
+ console.log('The agent will analyze your plan and generate a PRD in plans/.');
4470
4393
 
4471
4394
  // Immediately dispatch if engine is running
4472
4395
  const control = getControl();
@@ -4611,7 +4534,7 @@ const commands = {
4611
4534
  console.log('\n=== Work Discovery (dry run) ===\n');
4612
4535
 
4613
4536
  materializePlansAsWorkItems(config);
4614
- const prdWork = discoverFromPrd(config);
4537
+ // PRD discovery removed — all items flow through plans/*.json
4615
4538
  const prWork = discoverFromPrs(config);
4616
4539
  const workItemWork = discoverFromWorkItems(config);
4617
4540
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/squad",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "description": "Multi-agent AI dev team that runs from ~/.squad/ — five autonomous agents share a single engine, dashboard, and knowledge base",
5
5
  "bin": {
6
6
  "squad": "bin/squad.js"