@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/README.md +38 -17
- package/dashboard.html +438 -203
- package/dashboard.js +349 -66
- package/docs/self-improvement.md +2 -2
- package/engine.js +30 -107
- package/package.json +1 -1
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'
|
|
1787
|
+
// 2. Update PRD item status to 'implemented' in plan files
|
|
1788
1788
|
if (pr.prdItems?.length > 0) {
|
|
1789
|
-
const
|
|
1790
|
-
|
|
1791
|
-
|
|
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
|
|
1794
|
-
const
|
|
1795
|
-
if (
|
|
1796
|
-
|
|
1797
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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