@yemi33/squad 0.1.10 → 0.1.12
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/dashboard.html +134 -0
- package/dashboard.js +3 -1
- package/engine/spawn-agent.js +11 -6
- package/engine.js +74 -31
- package/package.json +1 -1
package/dashboard.html
CHANGED
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
.status-badge.working { background: rgba(210,153,34,0.15); color: var(--yellow); border: 1px solid var(--yellow); animation: pulse 1.5s infinite; }
|
|
48
48
|
.status-badge.done { background: rgba(63,185,80,0.15); color: var(--green); border: 1px solid var(--green); }
|
|
49
49
|
.agent-action { font-size: 11px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
|
|
50
|
+
.agent-result { font-size: 10px; color: var(--text); background: var(--surface2); padding: 6px 8px; border-radius: 4px; margin-top: 6px; white-space: pre-wrap; word-break: break-word; max-height: 80px; overflow-y: auto; line-height: 1.4; border-left: 2px solid var(--blue); }
|
|
50
51
|
.agent-card { min-width: 0; }
|
|
51
52
|
.agent-emoji { font-size: 20px; margin-right: 4px; }
|
|
52
53
|
.click-hint { font-size: 10px; color: var(--border); margin-top: 6px; }
|
|
@@ -243,6 +244,28 @@
|
|
|
243
244
|
.cmd-hints::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
|
|
244
245
|
.cmd-hints code { color: var(--blue); font-size: 10px; background: var(--surface2); padding: 1px 5px; border-radius: 3px; }
|
|
245
246
|
|
|
247
|
+
.cmd-history-btn {
|
|
248
|
+
background: none; border: 1px solid var(--border); color: var(--muted); font-size: 11px;
|
|
249
|
+
cursor: pointer; padding: 3px 10px; border-radius: 4px; transition: all 0.2s;
|
|
250
|
+
}
|
|
251
|
+
.cmd-history-btn:hover { color: var(--text); border-color: var(--text); }
|
|
252
|
+
.cmd-history-list { list-style: none; padding: 0; margin: 0; }
|
|
253
|
+
.cmd-history-item {
|
|
254
|
+
display: flex; align-items: flex-start; gap: 10px; padding: 10px 0;
|
|
255
|
+
border-bottom: 1px solid var(--border);
|
|
256
|
+
}
|
|
257
|
+
.cmd-history-item:last-child { border-bottom: none; }
|
|
258
|
+
.cmd-history-item-body { flex: 1; min-width: 0; }
|
|
259
|
+
.cmd-history-item-text { font-size: 12px; color: var(--text); white-space: pre-wrap; word-break: break-word; font-family: Consolas, monospace; }
|
|
260
|
+
.cmd-history-item-meta { font-size: 10px; color: var(--muted); margin-top: 3px; display: flex; gap: 8px; }
|
|
261
|
+
.cmd-history-item-meta .chip { background: var(--surface2); padding: 1px 6px; border-radius: 3px; }
|
|
262
|
+
.cmd-history-resubmit {
|
|
263
|
+
background: var(--surface2); border: 1px solid var(--border); color: var(--blue);
|
|
264
|
+
font-size: 11px; cursor: pointer; padding: 4px 10px; border-radius: 4px; white-space: nowrap;
|
|
265
|
+
transition: all 0.2s; flex-shrink: 0;
|
|
266
|
+
}
|
|
267
|
+
.cmd-history-resubmit:hover { background: rgba(88,166,255,0.1); border-color: var(--blue); }
|
|
268
|
+
.cmd-history-empty { color: var(--muted); font-size: 12px; padding: 20px 0; text-align: center; }
|
|
246
269
|
.cmd-toast {
|
|
247
270
|
display: none; padding: 8px 14px; border-radius: 4px; font-size: 12px;
|
|
248
271
|
margin-top: 10px; animation: fadeIn 0.3s;
|
|
@@ -403,6 +426,7 @@
|
|
|
403
426
|
<span><code>/note</code> team note</span>
|
|
404
427
|
<span><code>/prd</code> PRD item</span>
|
|
405
428
|
<span><code>#project</code> target project</span>
|
|
429
|
+
<button class="cmd-history-btn" onclick="cmdShowHistory()">Past Commands</button>
|
|
406
430
|
</div>
|
|
407
431
|
<div class="cmd-toast" id="cmd-toast"></div>
|
|
408
432
|
</section>
|
|
@@ -648,6 +672,7 @@ function renderAgents(agents) {
|
|
|
648
672
|
</div>
|
|
649
673
|
<div class="agent-role">${a.role}</div>
|
|
650
674
|
<div class="agent-action" title="${escHtml(a.lastAction)}">${escHtml(a.lastAction)}</div>
|
|
675
|
+
${a.resultSummary ? `<div class="agent-result" title="${escHtml(a.resultSummary)}">${escHtml(a.resultSummary.slice(0, 200))}${a.resultSummary.length > 200 ? '...' : ''}</div>` : ''}
|
|
651
676
|
</div>
|
|
652
677
|
`).join('');
|
|
653
678
|
}
|
|
@@ -1662,6 +1687,42 @@ function cmdKeyDown(e) {
|
|
|
1662
1687
|
}
|
|
1663
1688
|
}
|
|
1664
1689
|
|
|
1690
|
+
// ArrowUp/ArrowDown to navigate command history (only when no popup visible)
|
|
1691
|
+
if (e.key === 'ArrowUp' && !isPopupVisible) {
|
|
1692
|
+
const input = document.getElementById('cmd-input');
|
|
1693
|
+
// Only intercept if cursor is at start of input (or input is single-line)
|
|
1694
|
+
if (input.selectionStart === 0 || !input.value.includes('\n')) {
|
|
1695
|
+
const history = cmdGetHistory();
|
|
1696
|
+
if (history.length === 0) return;
|
|
1697
|
+
if (_cmdHistoryIdx === -1) _cmdHistoryDraft = input.value; // Save current draft
|
|
1698
|
+
if (_cmdHistoryIdx < history.length - 1) {
|
|
1699
|
+
_cmdHistoryIdx++;
|
|
1700
|
+
input.value = history[_cmdHistoryIdx].text;
|
|
1701
|
+
cmdAutoResize();
|
|
1702
|
+
cmdRenderMeta();
|
|
1703
|
+
e.preventDefault();
|
|
1704
|
+
// Move cursor to end
|
|
1705
|
+
setTimeout(() => input.setSelectionRange(input.value.length, input.value.length), 0);
|
|
1706
|
+
}
|
|
1707
|
+
return;
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
if (e.key === 'ArrowDown' && !isPopupVisible) {
|
|
1711
|
+
const input = document.getElementById('cmd-input');
|
|
1712
|
+
if (input.selectionStart === input.value.length || !input.value.includes('\n')) {
|
|
1713
|
+
if (_cmdHistoryIdx >= 0) {
|
|
1714
|
+
_cmdHistoryIdx--;
|
|
1715
|
+
const history = cmdGetHistory();
|
|
1716
|
+
input.value = _cmdHistoryIdx >= 0 ? history[_cmdHistoryIdx].text : (_cmdHistoryDraft || '');
|
|
1717
|
+
cmdAutoResize();
|
|
1718
|
+
cmdRenderMeta();
|
|
1719
|
+
e.preventDefault();
|
|
1720
|
+
setTimeout(() => input.setSelectionRange(input.value.length, input.value.length), 0);
|
|
1721
|
+
}
|
|
1722
|
+
return;
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1665
1726
|
// Ctrl+Enter to submit
|
|
1666
1727
|
if (e.key === 'Enter' && e.ctrlKey) {
|
|
1667
1728
|
e.preventDefault();
|
|
@@ -1689,6 +1750,10 @@ async function cmdSubmit() {
|
|
|
1689
1750
|
} else {
|
|
1690
1751
|
await cmdSubmitWorkItem(parsed);
|
|
1691
1752
|
}
|
|
1753
|
+
// Save to history on success
|
|
1754
|
+
cmdSaveHistory(raw, parsed.intent);
|
|
1755
|
+
_cmdHistoryIdx = -1;
|
|
1756
|
+
_cmdHistoryDraft = '';
|
|
1692
1757
|
// Clear on success
|
|
1693
1758
|
input.value = '';
|
|
1694
1759
|
cmdAutoResize();
|
|
@@ -1781,6 +1846,75 @@ async function cmdSubmitPrd(parsed) {
|
|
|
1781
1846
|
const projLabel = (parsed.projects || []).length > 0 ? ' (' + parsed.projects.join(', ') + ')' : '';
|
|
1782
1847
|
showToast('cmd-toast', 'PRD item ' + (data.id || id) + ' added' + projLabel, true);
|
|
1783
1848
|
}
|
|
1849
|
+
// ─── Command History ──────────────────────────────────────────────────────────
|
|
1850
|
+
const CMD_HISTORY_KEY = 'squad-cmd-history';
|
|
1851
|
+
const CMD_HISTORY_MAX = 50;
|
|
1852
|
+
let _cmdHistoryIdx = -1; // -1 = not browsing history
|
|
1853
|
+
let _cmdHistoryDraft = ''; // saves current draft when browsing
|
|
1854
|
+
|
|
1855
|
+
function cmdGetHistory() {
|
|
1856
|
+
try { return JSON.parse(localStorage.getItem(CMD_HISTORY_KEY) || '[]'); } catch { return []; }
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
function cmdSaveHistory(raw, intent) {
|
|
1860
|
+
const history = cmdGetHistory();
|
|
1861
|
+
history.unshift({ text: raw, intent, timestamp: new Date().toISOString() });
|
|
1862
|
+
if (history.length > CMD_HISTORY_MAX) history.length = CMD_HISTORY_MAX;
|
|
1863
|
+
localStorage.setItem(CMD_HISTORY_KEY, JSON.stringify(history));
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
function cmdShowHistory() {
|
|
1867
|
+
const history = cmdGetHistory();
|
|
1868
|
+
const title = document.getElementById('modal-title');
|
|
1869
|
+
const body = document.getElementById('modal-body');
|
|
1870
|
+
title.textContent = 'Past Commands (' + history.length + ')';
|
|
1871
|
+
|
|
1872
|
+
if (history.length === 0) {
|
|
1873
|
+
body.innerHTML = '<div class="cmd-history-empty">No commands yet. Submit something from the command center.</div>';
|
|
1874
|
+
} else {
|
|
1875
|
+
const intentColors = { 'work-item': 'var(--blue)', 'note': 'var(--green)', 'plan': 'var(--purple,#a855f7)', 'prd': 'var(--yellow,#d29922)' };
|
|
1876
|
+
const intentLabels = { 'work-item': 'Work Item', 'note': 'Note', 'plan': 'Plan', 'prd': 'PRD' };
|
|
1877
|
+
body.innerHTML = '<ul class="cmd-history-list">' + history.map((item, i) => {
|
|
1878
|
+
const date = new Date(item.timestamp);
|
|
1879
|
+
const ago = timeSinceStr(date);
|
|
1880
|
+
const intentLabel = intentLabels[item.intent] || item.intent || 'work-item';
|
|
1881
|
+
const intentColor = intentColors[item.intent] || 'var(--blue)';
|
|
1882
|
+
return '<li class="cmd-history-item">' +
|
|
1883
|
+
'<div class="cmd-history-item-body">' +
|
|
1884
|
+
'<div class="cmd-history-item-text">' + escHtml(item.text) + '</div>' +
|
|
1885
|
+
'<div class="cmd-history-item-meta">' +
|
|
1886
|
+
'<span class="chip" style="color:' + intentColor + '">' + intentLabel + '</span>' +
|
|
1887
|
+
'<span>' + ago + '</span>' +
|
|
1888
|
+
'<span>' + date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], {hour:'2-digit',minute:'2-digit'}) + '</span>' +
|
|
1889
|
+
'</div>' +
|
|
1890
|
+
'</div>' +
|
|
1891
|
+
'<button class="cmd-history-resubmit" onclick="cmdResubmit(' + i + ')">Resubmit</button>' +
|
|
1892
|
+
'</li>';
|
|
1893
|
+
}).join('') + '</ul>';
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
document.getElementById('modal').classList.add('open');
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
function cmdResubmit(idx) {
|
|
1900
|
+
const history = cmdGetHistory();
|
|
1901
|
+
const item = history[idx];
|
|
1902
|
+
if (!item) return;
|
|
1903
|
+
document.getElementById('modal').classList.remove('open');
|
|
1904
|
+
const input = document.getElementById('cmd-input');
|
|
1905
|
+
input.value = item.text;
|
|
1906
|
+
cmdAutoResize();
|
|
1907
|
+
cmdRenderMeta();
|
|
1908
|
+
input.focus();
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
function timeSinceStr(date) {
|
|
1912
|
+
const s = Math.floor((Date.now() - date.getTime()) / 1000);
|
|
1913
|
+
if (s < 60) return s + 's ago';
|
|
1914
|
+
if (s < 3600) return Math.floor(s / 60) + 'm ago';
|
|
1915
|
+
if (s < 86400) return Math.floor(s / 3600) + 'h ago';
|
|
1916
|
+
return Math.floor(s / 86400) + 'd ago';
|
|
1917
|
+
}
|
|
1784
1918
|
</script>
|
|
1785
1919
|
</body>
|
|
1786
1920
|
</html>
|
package/dashboard.js
CHANGED
|
@@ -99,6 +99,7 @@ function getAgents() {
|
|
|
99
99
|
let status = 'idle';
|
|
100
100
|
let lastAction = 'Waiting for assignment';
|
|
101
101
|
let currentTask = '';
|
|
102
|
+
let resultSummary = '';
|
|
102
103
|
|
|
103
104
|
const statusFile = safeRead(path.join(SQUAD_DIR, 'agents', a.id, 'status.json'));
|
|
104
105
|
if (statusFile) {
|
|
@@ -106,6 +107,7 @@ function getAgents() {
|
|
|
106
107
|
const sj = JSON.parse(statusFile);
|
|
107
108
|
status = sj.status || 'idle';
|
|
108
109
|
currentTask = sj.task || '';
|
|
110
|
+
resultSummary = sj.resultSummary || '';
|
|
109
111
|
if (sj.status === 'working') {
|
|
110
112
|
lastAction = `Working: ${sj.task}`;
|
|
111
113
|
} else if (sj.status === 'done') {
|
|
@@ -126,7 +128,7 @@ function getAgents() {
|
|
|
126
128
|
const chartered = fs.existsSync(path.join(SQUAD_DIR, 'agents', a.id, 'charter.md'));
|
|
127
129
|
// Truncate lastAction to prevent UI overflow from corrupted data
|
|
128
130
|
if (lastAction.length > 120) lastAction = lastAction.slice(0, 120) + '...';
|
|
129
|
-
return { ...a, status, lastAction, currentTask: (currentTask || '').slice(0, 200), chartered, inboxCount: inboxFiles.length };
|
|
131
|
+
return { ...a, status, lastAction, currentTask: (currentTask || '').slice(0, 200), resultSummary: (resultSummary || '').slice(0, 500), chartered, inboxCount: inboxFiles.length };
|
|
130
132
|
});
|
|
131
133
|
}
|
|
132
134
|
|
package/engine/spawn-agent.js
CHANGED
|
@@ -80,9 +80,13 @@ try {
|
|
|
80
80
|
if (Buffer.byteLength(sysPrompt) < 30000) {
|
|
81
81
|
actualArgs = ['-p', '--system-prompt', sysPrompt, ...extraArgs];
|
|
82
82
|
} else {
|
|
83
|
-
// Too large for inline —
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
// Too large for inline — split: short identity as --system-prompt, rest prepended to user prompt
|
|
84
|
+
// Extract first section (agent identity) as the system prompt, rest goes into user context
|
|
85
|
+
const splitIdx = sysPrompt.indexOf('\n---\n');
|
|
86
|
+
const shortSys = splitIdx > 0 && splitIdx < 2000
|
|
87
|
+
? sysPrompt.slice(0, splitIdx)
|
|
88
|
+
: sysPrompt.slice(0, 1500) + '\n\n[System prompt truncated for CLI arg limit — full context provided below in user message]';
|
|
89
|
+
actualArgs = ['-p', '--system-prompt', shortSys, ...extraArgs];
|
|
86
90
|
}
|
|
87
91
|
}
|
|
88
92
|
} catch {
|
|
@@ -100,9 +104,10 @@ fs.appendFileSync(debugPath, `PID=${proc.pid || 'none'}\nargs=${actualArgs.join(
|
|
|
100
104
|
const pidFile = promptFile.replace(/prompt-/, 'pid-').replace(/\.md$/, '.pid');
|
|
101
105
|
fs.writeFileSync(pidFile, String(proc.pid || ''));
|
|
102
106
|
|
|
103
|
-
// Send prompt via stdin — if system prompt
|
|
104
|
-
if (
|
|
105
|
-
|
|
107
|
+
// Send prompt via stdin — if system prompt was truncated, prepend the full context
|
|
108
|
+
if (Buffer.byteLength(sysPrompt) >= 30000) {
|
|
109
|
+
// System prompt was too large for CLI — prepend full context to user prompt
|
|
110
|
+
proc.stdin.write(`## Full Agent Context\n\n${sysPrompt}\n\n---\n\n## Your Task\n\n${prompt}`);
|
|
106
111
|
} else {
|
|
107
112
|
proc.stdin.write(prompt);
|
|
108
113
|
}
|
package/engine.js
CHANGED
|
@@ -506,10 +506,10 @@ function getRepoHostToolRule(project) {
|
|
|
506
506
|
|
|
507
507
|
// ─── System Prompt Builder ──────────────────────────────────────────────────
|
|
508
508
|
|
|
509
|
+
// Lean system prompt: agent identity + rules only (~2-4KB, never grows)
|
|
509
510
|
function buildSystemPrompt(agentId, config, project) {
|
|
510
511
|
const agent = config.agents[agentId];
|
|
511
512
|
const charter = getAgentCharter(agentId);
|
|
512
|
-
const notes = getNotes();
|
|
513
513
|
project = project || config.project || {};
|
|
514
514
|
|
|
515
515
|
let prompt = '';
|
|
@@ -519,57 +519,66 @@ function buildSystemPrompt(agentId, config, project) {
|
|
|
519
519
|
prompt += `Agent ID: ${agentId}\n`;
|
|
520
520
|
prompt += `Skills: ${(agent.skills || []).join(', ')}\n\n`;
|
|
521
521
|
|
|
522
|
-
// Charter (detailed instructions)
|
|
522
|
+
// Charter (detailed instructions — typically 1-2KB)
|
|
523
523
|
if (charter) {
|
|
524
524
|
prompt += `## Your Charter\n\n${charter}\n\n`;
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
-
//
|
|
528
|
-
const history = safeRead(path.join(AGENTS_DIR, agentId, 'history.md'));
|
|
529
|
-
if (history && history.trim() !== '# Agent History') {
|
|
530
|
-
prompt += `## Your Recent History\n\n${history}\n\n`;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// Project context
|
|
527
|
+
// Project context (fixed size)
|
|
534
528
|
prompt += `## Project: ${project.name || 'Unknown Project'}\n\n`;
|
|
535
529
|
prompt += `- Repo: ${project.repoName || 'Unknown'} (${project.adoOrg || 'Unknown'}/${project.adoProject || 'Unknown'})\n`;
|
|
536
530
|
prompt += `- Repo ID: ${project.repositoryId || ''}\n`;
|
|
537
531
|
prompt += `- Repo host: ${getRepoHostLabel(project)}\n`;
|
|
538
532
|
prompt += `- Main branch: ${project.mainBranch || 'main'}\n\n`;
|
|
539
533
|
|
|
540
|
-
//
|
|
541
|
-
if (project.localPath) {
|
|
542
|
-
const claudeMd = safeRead(path.join(project.localPath, 'CLAUDE.md'));
|
|
543
|
-
if (claudeMd && claudeMd.trim()) {
|
|
544
|
-
// Truncate to 4KB to avoid bloating the system prompt
|
|
545
|
-
const truncated = claudeMd.length > 4096 ? claudeMd.slice(0, 4096) + '\n\n...(truncated)' : claudeMd;
|
|
546
|
-
prompt += `## Project Conventions (from CLAUDE.md)\n\n${truncated}\n\n`;
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
// Critical rules
|
|
534
|
+
// Critical rules (fixed size)
|
|
551
535
|
prompt += `## Critical Rules\n\n`;
|
|
552
536
|
prompt += `1. Use git worktrees — NEVER checkout on main working tree\n`;
|
|
553
537
|
prompt += `2. ${getRepoHostToolRule(project)}\n`;
|
|
554
|
-
prompt += `3. Follow the project conventions
|
|
538
|
+
prompt += `3. Follow the project conventions in CLAUDE.md if present\n`;
|
|
555
539
|
prompt += `4. Write learnings to: ${SQUAD_DIR}/notes/inbox/${agentId}-${dateStamp()}.md\n`;
|
|
556
540
|
prompt += `5. Do NOT write to agents/*/status.json — the engine manages agent status automatically\n`;
|
|
557
541
|
prompt += `6. If you discover a repeatable workflow, save it as a skill:\n`;
|
|
558
542
|
prompt += ` - Squad-wide: \`${SKILLS_DIR}/<name>.md\` (no PR needed)\n`;
|
|
559
543
|
prompt += ` - Project-specific: \`<project>/.claude/skills/<name>.md\` (requires a PR since it modifies the repo)\n\n`;
|
|
560
544
|
|
|
545
|
+
return prompt;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Bulk context: history, notes, conventions, skills — prepended to user/task prompt.
|
|
549
|
+
// This is the content that grows over time and would bloat the system prompt.
|
|
550
|
+
function buildAgentContext(agentId, config, project) {
|
|
551
|
+
project = project || config.project || {};
|
|
552
|
+
const notes = getNotes();
|
|
553
|
+
let context = '';
|
|
554
|
+
|
|
555
|
+
// Agent history (past tasks)
|
|
556
|
+
const history = safeRead(path.join(AGENTS_DIR, agentId, 'history.md'));
|
|
557
|
+
if (history && history.trim() !== '# Agent History') {
|
|
558
|
+
context += `## Your Recent History\n\n${history}\n\n`;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// Project conventions (from CLAUDE.md)
|
|
562
|
+
if (project.localPath) {
|
|
563
|
+
const claudeMd = safeRead(path.join(project.localPath, 'CLAUDE.md'));
|
|
564
|
+
if (claudeMd && claudeMd.trim()) {
|
|
565
|
+
const truncated = claudeMd.length > 8192 ? claudeMd.slice(0, 8192) + '\n\n...(truncated)' : claudeMd;
|
|
566
|
+
context += `## Project Conventions (from CLAUDE.md)\n\n${truncated}\n\n`;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
561
570
|
// Skills
|
|
562
571
|
const skillIndex = getSkillIndex();
|
|
563
572
|
if (skillIndex) {
|
|
564
|
-
|
|
573
|
+
context += skillIndex + '\n';
|
|
565
574
|
}
|
|
566
575
|
|
|
567
|
-
// Team notes
|
|
576
|
+
// Team notes (the big one — can be 50KB)
|
|
568
577
|
if (notes) {
|
|
569
|
-
|
|
578
|
+
context += `## Team Notes (MUST READ)\n\n${notes}\n\n`;
|
|
570
579
|
}
|
|
571
580
|
|
|
572
|
-
return
|
|
581
|
+
return context;
|
|
573
582
|
}
|
|
574
583
|
|
|
575
584
|
function sanitizeBranch(name) {
|
|
@@ -631,12 +640,18 @@ function spawnAgent(dispatchItem, config) {
|
|
|
631
640
|
}
|
|
632
641
|
}
|
|
633
642
|
|
|
634
|
-
// Build
|
|
643
|
+
// Build lean system prompt (identity + rules, ~2-4KB) and bulk context (history, notes, skills)
|
|
635
644
|
const systemPrompt = buildSystemPrompt(agentId, config, project);
|
|
645
|
+
const agentContext = buildAgentContext(agentId, config, project);
|
|
646
|
+
|
|
647
|
+
// Prepend bulk context to task prompt — keeps system prompt small and stable
|
|
648
|
+
const fullTaskPrompt = agentContext
|
|
649
|
+
? `## Agent Context\n\n${agentContext}\n---\n\n## Your Task\n\n${taskPrompt}`
|
|
650
|
+
: taskPrompt;
|
|
636
651
|
|
|
637
652
|
// Write prompt and system prompt to temp files (avoids shell escaping issues)
|
|
638
653
|
const promptPath = path.join(ENGINE_DIR, `prompt-${id}.md`);
|
|
639
|
-
safeWrite(promptPath,
|
|
654
|
+
safeWrite(promptPath, fullTaskPrompt);
|
|
640
655
|
|
|
641
656
|
const sysPromptPath = path.join(ENGINE_DIR, `sysprompt-${id}.md`);
|
|
642
657
|
safeWrite(sysPromptPath, systemPrompt);
|
|
@@ -723,6 +738,23 @@ function spawnAgent(dispatchItem, config) {
|
|
|
723
738
|
safeWrite(archivePath, outputContent);
|
|
724
739
|
safeWrite(latestPath, outputContent); // overwrite latest for dashboard compat
|
|
725
740
|
|
|
741
|
+
// Extract agent's final result text from stream-json output
|
|
742
|
+
let resultSummary = '';
|
|
743
|
+
try {
|
|
744
|
+
const lines = stdout.split('\n');
|
|
745
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
746
|
+
const line = lines[i].trim();
|
|
747
|
+
if (!line || !line.startsWith('{')) continue;
|
|
748
|
+
try {
|
|
749
|
+
const obj = JSON.parse(line);
|
|
750
|
+
if (obj.type === 'result' && obj.result) {
|
|
751
|
+
resultSummary = obj.result.slice(0, 500);
|
|
752
|
+
break;
|
|
753
|
+
}
|
|
754
|
+
} catch {}
|
|
755
|
+
}
|
|
756
|
+
} catch {}
|
|
757
|
+
|
|
726
758
|
// Update agent status
|
|
727
759
|
setAgentStatus(agentId, {
|
|
728
760
|
status: code === 0 ? 'done' : 'error',
|
|
@@ -732,7 +764,8 @@ function spawnAgent(dispatchItem, config) {
|
|
|
732
764
|
branch: branchName,
|
|
733
765
|
exit_code: code,
|
|
734
766
|
started_at: startedAt,
|
|
735
|
-
completed_at: ts()
|
|
767
|
+
completed_at: ts(),
|
|
768
|
+
resultSummary: resultSummary || undefined,
|
|
736
769
|
});
|
|
737
770
|
|
|
738
771
|
// Move from active to completed in dispatch
|
|
@@ -1997,7 +2030,7 @@ function updateMetrics(agentId, dispatchItem, result) {
|
|
|
1997
2030
|
let _consolidationInFlight = false;
|
|
1998
2031
|
|
|
1999
2032
|
function consolidateInbox(config) {
|
|
2000
|
-
const threshold = config.engine?.inboxConsolidateThreshold ||
|
|
2033
|
+
const threshold = config.engine?.inboxConsolidateThreshold || 3;
|
|
2001
2034
|
const files = getInboxFiles();
|
|
2002
2035
|
if (files.length < threshold) return;
|
|
2003
2036
|
if (_consolidationInFlight) return;
|
|
@@ -3286,6 +3319,9 @@ function discoverFromWorkItems(config, project) {
|
|
|
3286
3319
|
item_name: item.title || item.id,
|
|
3287
3320
|
item_priority: item.priority || 'medium',
|
|
3288
3321
|
item_description: item.description || '',
|
|
3322
|
+
item_complexity: item.complexity || item.estimated_complexity || 'medium',
|
|
3323
|
+
task_description: item.title + (item.description ? '\n\n' + item.description : ''),
|
|
3324
|
+
task_id: item.id,
|
|
3289
3325
|
work_type: workType,
|
|
3290
3326
|
additional_context: item.prompt ? `## Additional Context\n\n${item.prompt}` : '',
|
|
3291
3327
|
scope_section: `## Scope: Project — ${project?.name || 'default'}\n\nThis task is scoped to a single project.`,
|
|
@@ -3300,8 +3336,10 @@ function discoverFromWorkItems(config, project) {
|
|
|
3300
3336
|
ado_org: project?.adoOrg || 'Unknown',
|
|
3301
3337
|
ado_project: project?.adoProject || 'Unknown',
|
|
3302
3338
|
repo_name: project?.repoName || 'Unknown',
|
|
3303
|
-
date: dateStamp()
|
|
3339
|
+
date: dateStamp(),
|
|
3340
|
+
notes_content: '',
|
|
3304
3341
|
};
|
|
3342
|
+
try { vars.notes_content = fs.readFileSync(path.join(SQUAD_DIR, 'notes.md'), 'utf8'); } catch {}
|
|
3305
3343
|
|
|
3306
3344
|
// Inject ask-specific variables for the ask playbook
|
|
3307
3345
|
if (workType === 'ask') {
|
|
@@ -3646,6 +3684,9 @@ function discoverCentralWorkItems(config) {
|
|
|
3646
3684
|
item_name: item.title || item.id,
|
|
3647
3685
|
item_priority: item.priority || 'medium',
|
|
3648
3686
|
item_description: item.description || '',
|
|
3687
|
+
item_complexity: item.complexity || item.estimated_complexity || 'medium',
|
|
3688
|
+
task_description: item.title + (item.description ? '\n\n' + item.description : ''),
|
|
3689
|
+
task_id: item.id,
|
|
3649
3690
|
work_type: workType,
|
|
3650
3691
|
additional_context: item.prompt ? `## Additional Context\n\n${item.prompt}` : '',
|
|
3651
3692
|
scope_section: buildProjectContext(projects, null, false, agentName, agentRole),
|
|
@@ -3657,8 +3698,10 @@ function discoverCentralWorkItems(config) {
|
|
|
3657
3698
|
ado_project: firstProject?.adoProject || 'Unknown',
|
|
3658
3699
|
repo_name: firstProject?.repoName || 'Unknown',
|
|
3659
3700
|
team_root: SQUAD_DIR,
|
|
3660
|
-
date: dateStamp()
|
|
3701
|
+
date: dateStamp(),
|
|
3702
|
+
notes_content: '',
|
|
3661
3703
|
};
|
|
3704
|
+
try { vars.notes_content = fs.readFileSync(path.join(SQUAD_DIR, 'notes.md'), 'utf8'); } catch {}
|
|
3662
3705
|
|
|
3663
3706
|
// Inject plan-specific variables for the plan playbook
|
|
3664
3707
|
if (workType === 'plan') {
|
package/package.json
CHANGED