@teammates/cli 0.5.1 → 0.5.2
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/dist/adapter.js +3 -4
- package/dist/adapters/cli-proxy.js +1 -0
- package/dist/adapters/copilot.js +3 -1
- package/dist/cli.js +175 -70
- package/dist/orchestrator.js +3 -0
- package/dist/types.d.ts +7 -0
- package/package.json +3 -3
package/dist/adapter.js
CHANGED
|
@@ -225,7 +225,6 @@ export function buildTeammatePrompt(teammate, taskPrompt, options) {
|
|
|
225
225
|
"- The `# Subject` line is REQUIRED — it becomes the message title.",
|
|
226
226
|
"- Always write a substantive body. Never return just the subject.",
|
|
227
227
|
"- Use markdown: headings, lists, code blocks, bold, etc.",
|
|
228
|
-
"- **Write your text response FIRST, then update session/memory files.** This ensures visible output even if the agent turn ends early.",
|
|
229
228
|
"",
|
|
230
229
|
"### Handoffs",
|
|
231
230
|
"",
|
|
@@ -245,10 +244,10 @@ export function buildTeammatePrompt(teammate, taskPrompt, options) {
|
|
|
245
244
|
];
|
|
246
245
|
// Session state (conditional)
|
|
247
246
|
if (options?.sessionFile) {
|
|
248
|
-
instrLines.push("", "### Session State", "", `Your session file is at: \`${options.sessionFile}\``, "", "**After
|
|
247
|
+
instrLines.push("", "### Session State", "", `Your session file is at: \`${options.sessionFile}\``, "", "**After completing the task**, append a brief entry to this file with:", "- What you did", "- Key decisions made", "- Files changed", "- Anything the next task should know", "", "This is how you maintain continuity across tasks. Always read it, always update it.");
|
|
249
248
|
}
|
|
250
249
|
// Memory updates
|
|
251
|
-
instrLines.push("", "### Memory Updates", "", "**After
|
|
250
|
+
instrLines.push("", "### Memory Updates", "", "**After completing the task**, update your memory files:", "", `1. **Daily log** — Read \`.teammates/${teammate.name}/memory/${today}.md\` first (it may have entries from earlier tasks today), then write it back with your entry added. Create the file if it doesn't exist.`, " - What you did", " - Key decisions made", " - Files changed", " - Anything the next task should know", "", `2. **Typed memories** — If you learned something durable (a decision, pattern, feedback, or reference), create a typed memory file at \`.teammates/${teammate.name}/memory/<type>_<topic>.md\` with frontmatter (\`name\`, \`description\`, \`type\`). Update existing memory files if the topic already has one.`, "", "3. **WISDOM.md** — Do not edit directly. Wisdom entries are distilled from typed memories during compaction.", "", "These files are your persistent memory. Without them, your next session starts from scratch.");
|
|
252
251
|
// Section Reinforcement — back-references from high-attention bottom edge to each section tag
|
|
253
252
|
instrLines.push("", "### Section Reinforcement", "");
|
|
254
253
|
instrLines.push("- Stay in character as defined in `<IDENTITY>` — never break persona or speak as a generic assistant.");
|
|
@@ -274,7 +273,7 @@ export function buildTeammatePrompt(teammate, taskPrompt, options) {
|
|
|
274
273
|
if (options?.handoffContext) {
|
|
275
274
|
instrLines.push("- When `<HANDOFF_CONTEXT>` is present, address its requirements and open questions directly.");
|
|
276
275
|
}
|
|
277
|
-
instrLines.push("- Your response must answer `<TASK>` — everything else is supporting context.", "", "**REMINDER:
|
|
276
|
+
instrLines.push("- Your response must answer `<TASK>` — everything else is supporting context.", "", "**REMINDER: You MUST end your turn with visible text output. A turn with only file edits and no text is a failed turn.**");
|
|
278
277
|
parts.push(instrLines.join("\n"));
|
|
279
278
|
return parts.join("\n");
|
|
280
279
|
}
|
|
@@ -210,6 +210,7 @@ export class CliProxyAdapter {
|
|
|
210
210
|
: spawn.output;
|
|
211
211
|
const teammateNames = this.roster.map((r) => r.name);
|
|
212
212
|
const result = parseResult(teammate.name, output, teammateNames, prompt);
|
|
213
|
+
result.fullPrompt = fullPrompt;
|
|
213
214
|
result.diagnostics = {
|
|
214
215
|
exitCode: spawn.exitCode,
|
|
215
216
|
signal: spawn.signal,
|
package/dist/adapters/copilot.js
CHANGED
|
@@ -152,7 +152,9 @@ export class CopilotAdapter {
|
|
|
152
152
|
// Use the final assistant message content, fall back to collected deltas
|
|
153
153
|
const output = reply?.data?.content ?? outputParts.join("");
|
|
154
154
|
const teammateNames = this.roster.map((r) => r.name);
|
|
155
|
-
|
|
155
|
+
const result = parseResult(teammate.name, output, teammateNames, prompt);
|
|
156
|
+
result.fullPrompt = fullPrompt;
|
|
157
|
+
return result;
|
|
156
158
|
}
|
|
157
159
|
finally {
|
|
158
160
|
// Disconnect the session (preserves data for potential resume)
|
package/dist/cli.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { exec as execCb, execSync, spawnSync } from "node:child_process";
|
|
11
11
|
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { mkdir, readdir, rm, stat, unlink } from "node:fs/promises";
|
|
13
|
-
import { dirname, join, resolve } from "node:path";
|
|
13
|
+
import { dirname, join, resolve, sep } from "node:path";
|
|
14
14
|
import { createInterface } from "node:readline";
|
|
15
15
|
import { App, ChatView, concat, esc, pen, renderMarkdown, stripAnsi, } from "@teammates/consolonia";
|
|
16
16
|
import chalk from "chalk";
|
|
@@ -217,6 +217,8 @@ class TeammatesREPL {
|
|
|
217
217
|
taskQueue = [];
|
|
218
218
|
/** Per-agent active tasks — one per agent running in parallel. */
|
|
219
219
|
agentActive = new Map();
|
|
220
|
+
/** Active system tasks — multiple can run concurrently per agent. */
|
|
221
|
+
systemActive = new Map();
|
|
220
222
|
/** Agents currently in a silent retry — suppress all events. */
|
|
221
223
|
silentAgents = new Set();
|
|
222
224
|
/** Per-agent drain locks — prevents double-draining a single agent. */
|
|
@@ -330,37 +332,74 @@ class TeammatesREPL {
|
|
|
330
332
|
this.input.setStatus(null);
|
|
331
333
|
}
|
|
332
334
|
}
|
|
335
|
+
/**
|
|
336
|
+
* Truncate a path for display, collapsing middle segments if too long.
|
|
337
|
+
* E.g. C:\source\some\deep\project → C:\source\...\project
|
|
338
|
+
*/
|
|
339
|
+
static truncatePath(fullPath, maxLen = 30) {
|
|
340
|
+
if (fullPath.length <= maxLen)
|
|
341
|
+
return fullPath;
|
|
342
|
+
const parts = fullPath.split(sep);
|
|
343
|
+
if (parts.length <= 2)
|
|
344
|
+
return fullPath;
|
|
345
|
+
const last = parts[parts.length - 1];
|
|
346
|
+
// Keep adding segments from the front until we'd exceed maxLen
|
|
347
|
+
let front = parts[0];
|
|
348
|
+
for (let i = 1; i < parts.length - 1; i++) {
|
|
349
|
+
const candidate = front + sep + parts[i] + sep + "..." + sep + last;
|
|
350
|
+
if (candidate.length > maxLen)
|
|
351
|
+
break;
|
|
352
|
+
front += sep + parts[i];
|
|
353
|
+
}
|
|
354
|
+
return front + sep + "..." + sep + last;
|
|
355
|
+
}
|
|
356
|
+
/** Format elapsed seconds as (Ns), (Nm Ns), or (Nh Nm Ns). */
|
|
357
|
+
static formatElapsed(totalSeconds) {
|
|
358
|
+
const s = totalSeconds % 60;
|
|
359
|
+
const m = Math.floor(totalSeconds / 60) % 60;
|
|
360
|
+
const h = Math.floor(totalSeconds / 3600);
|
|
361
|
+
if (h > 0)
|
|
362
|
+
return `(${h}h ${m}m ${s}s)`;
|
|
363
|
+
if (m > 0)
|
|
364
|
+
return `(${m}m ${s}s)`;
|
|
365
|
+
return `(${s}s)`;
|
|
366
|
+
}
|
|
333
367
|
/** Render one frame of the status animation. */
|
|
334
368
|
renderStatusFrame() {
|
|
335
369
|
if (this.activeTasks.size === 0)
|
|
336
370
|
return;
|
|
337
371
|
const entries = Array.from(this.activeTasks.values());
|
|
338
|
-
const
|
|
339
|
-
const
|
|
372
|
+
const total = entries.length;
|
|
373
|
+
const idx = this.statusRotateIndex % total;
|
|
374
|
+
const { teammate, task, startTime } = entries[idx];
|
|
340
375
|
const displayName = teammate === this.selfName ? this.adapterName : teammate;
|
|
341
376
|
const spinChar = TeammatesREPL.SPINNER[this.statusFrame % TeammatesREPL.SPINNER.length];
|
|
342
|
-
const
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
377
|
+
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
378
|
+
const elapsedStr = TeammatesREPL.formatElapsed(elapsed);
|
|
379
|
+
// Build the tag: (1/3 - 2m 5s) when multiple, (2m 5s) when single
|
|
380
|
+
const tag = total > 1
|
|
381
|
+
? `(${idx + 1}/${total} - ${elapsedStr.slice(1, -1)})`
|
|
382
|
+
: elapsedStr;
|
|
383
|
+
// Target 80 chars total: "<spinner> <name>... <task> <tag>"
|
|
384
|
+
const prefix = `${spinChar} ${displayName}... `;
|
|
385
|
+
const suffix = ` ${tag}`;
|
|
386
|
+
const maxTask = 80 - prefix.length - suffix.length;
|
|
387
|
+
const cleanTask = task.replace(/[\r\n]+/g, " ").trim();
|
|
388
|
+
const taskText = maxTask <= 3
|
|
389
|
+
? ""
|
|
390
|
+
: cleanTask.length > maxTask
|
|
391
|
+
? `${cleanTask.slice(0, maxTask - 1)}…`
|
|
350
392
|
: cleanTask;
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
: "";
|
|
354
|
-
this.chatView.setProgress(concat(tp.accent(`${spinChar} ${displayName}… `), tp.muted(taskText + queueTag)));
|
|
393
|
+
if (this.chatView) {
|
|
394
|
+
this.chatView.setProgress(concat(tp.accent(`${spinChar} ${displayName}... `), tp.muted(`${taskText}${suffix}`)));
|
|
355
395
|
this.app.refresh();
|
|
356
396
|
}
|
|
357
397
|
else {
|
|
358
|
-
// Mostly bright blue, periodically flicker to dark blue
|
|
359
398
|
const spinColor = this.statusFrame % 8 === 0 ? chalk.blue : chalk.blueBright;
|
|
360
399
|
const line = ` ${spinColor(spinChar)} ` +
|
|
361
400
|
chalk.bold(displayName) +
|
|
362
|
-
chalk.gray(
|
|
363
|
-
|
|
401
|
+
chalk.gray(`... ${taskText}`) +
|
|
402
|
+
chalk.gray(suffix);
|
|
364
403
|
this.input.setStatus(line);
|
|
365
404
|
}
|
|
366
405
|
}
|
|
@@ -1032,9 +1071,24 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1032
1071
|
this.taskQueue.push({ type: "agent", teammate: match, task: input });
|
|
1033
1072
|
this.kickDrain();
|
|
1034
1073
|
}
|
|
1035
|
-
/**
|
|
1074
|
+
/** Returns true if the queue entry is a system-initiated (non-blocking) task. */
|
|
1075
|
+
isSystemTask(entry) {
|
|
1076
|
+
return (entry.type === "compact" ||
|
|
1077
|
+
entry.type === "summarize" ||
|
|
1078
|
+
(entry.type === "agent" && entry.system === true));
|
|
1079
|
+
}
|
|
1080
|
+
/** Start draining per-agent queues in parallel. Each agent gets its own drain loop.
|
|
1081
|
+
* System tasks are extracted and run concurrently without blocking user tasks. */
|
|
1036
1082
|
kickDrain() {
|
|
1037
|
-
//
|
|
1083
|
+
// Extract system tasks and fire them concurrently (non-blocking)
|
|
1084
|
+
for (let i = this.taskQueue.length - 1; i >= 0; i--) {
|
|
1085
|
+
const entry = this.taskQueue[i];
|
|
1086
|
+
if (this.isSystemTask(entry)) {
|
|
1087
|
+
this.taskQueue.splice(i, 1);
|
|
1088
|
+
this.runSystemTask(entry);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
// Find agents that have user tasks but no active drain
|
|
1038
1092
|
const agentsWithWork = new Set();
|
|
1039
1093
|
for (const entry of this.taskQueue) {
|
|
1040
1094
|
agentsWithWork.add(entry.teammate);
|
|
@@ -1048,6 +1102,53 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1048
1102
|
}
|
|
1049
1103
|
}
|
|
1050
1104
|
}
|
|
1105
|
+
/**
|
|
1106
|
+
* Run a system-initiated task concurrently without blocking user tasks.
|
|
1107
|
+
* Purely background — no progress bar, no /status. Only reports errors.
|
|
1108
|
+
*/
|
|
1109
|
+
async runSystemTask(entry) {
|
|
1110
|
+
const taskId = `sys-${entry.teammate}-${Date.now()}`;
|
|
1111
|
+
this.systemActive.set(taskId, entry);
|
|
1112
|
+
const startTime = Date.now();
|
|
1113
|
+
try {
|
|
1114
|
+
if (entry.type === "compact") {
|
|
1115
|
+
await this.runCompact(entry.teammate, true);
|
|
1116
|
+
}
|
|
1117
|
+
else if (entry.type === "summarize") {
|
|
1118
|
+
const result = await this.orchestrator.assign({
|
|
1119
|
+
teammate: entry.teammate,
|
|
1120
|
+
task: entry.task,
|
|
1121
|
+
system: true,
|
|
1122
|
+
});
|
|
1123
|
+
const raw = result.rawOutput ?? "";
|
|
1124
|
+
this.conversationSummary = raw
|
|
1125
|
+
.replace(/^TO:\s*\S+\s*\n/im, "")
|
|
1126
|
+
.replace(/^#\s+.+\n*/m, "")
|
|
1127
|
+
.replace(/```json\s*\n\s*\{[\s\S]*?\}\s*\n\s*```\s*$/g, "")
|
|
1128
|
+
.trim();
|
|
1129
|
+
}
|
|
1130
|
+
else {
|
|
1131
|
+
// System agent tasks (e.g. wisdom distillation)
|
|
1132
|
+
const result = await this.orchestrator.assign({
|
|
1133
|
+
teammate: entry.teammate,
|
|
1134
|
+
task: entry.task,
|
|
1135
|
+
system: true,
|
|
1136
|
+
});
|
|
1137
|
+
// Write debug entry for system tasks too
|
|
1138
|
+
this.writeDebugEntry(entry.teammate, entry.task, result, startTime);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
catch (err) {
|
|
1142
|
+
// System task errors always show in feed
|
|
1143
|
+
const msg = err?.message ?? String(err);
|
|
1144
|
+
const displayName = entry.teammate === this.selfName ? this.adapterName : entry.teammate;
|
|
1145
|
+
this.feedLine(tp.error(` ✖ @${displayName} (system): ${msg}`));
|
|
1146
|
+
this.refreshView();
|
|
1147
|
+
}
|
|
1148
|
+
finally {
|
|
1149
|
+
this.systemActive.delete(taskId);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1051
1152
|
// ─── Onboarding ───────────────────────────────────────────────────
|
|
1052
1153
|
/**
|
|
1053
1154
|
* Interactive prompt for team onboarding after user profile is set up.
|
|
@@ -2355,11 +2456,11 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2355
2456
|
progressStyle: { fg: t.progress, italic: true },
|
|
2356
2457
|
dropdownHighlightStyle: { fg: t.accent },
|
|
2357
2458
|
dropdownStyle: { fg: t.textMuted },
|
|
2358
|
-
footer: concat(tp.accent(" Teammates"), tp.dim(` v${PKG_VERSION}`), tp.muted(" "), tp.text(this.adapterName)),
|
|
2459
|
+
footer: concat(tp.accent(" Teammates"), tp.dim(` v${PKG_VERSION}`), tp.muted(" "), tp.text(this.adapterName), tp.muted(" "), tp.dim(TeammatesREPL.truncatePath(dirname(this.teammatesDir)))),
|
|
2359
2460
|
footerRight: tp.muted("? /help "),
|
|
2360
2461
|
footerStyle: { fg: t.textDim },
|
|
2361
2462
|
});
|
|
2362
|
-
this.defaultFooter = concat(tp.accent(" Teammates"), tp.dim(` v${PKG_VERSION}`), tp.muted(" "), tp.text(this.adapterName));
|
|
2463
|
+
this.defaultFooter = concat(tp.accent(" Teammates"), tp.dim(` v${PKG_VERSION}`), tp.muted(" "), tp.text(this.adapterName), tp.muted(" "), tp.dim(TeammatesREPL.truncatePath(dirname(this.teammatesDir))));
|
|
2363
2464
|
this.defaultFooterRight = tp.muted("? /help ");
|
|
2364
2465
|
// Wire ChatView events for input handling
|
|
2365
2466
|
this.chatView.on("submit", (rawLine) => {
|
|
@@ -2968,7 +3069,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2968
3069
|
{
|
|
2969
3070
|
name: "debug",
|
|
2970
3071
|
aliases: ["raw"],
|
|
2971
|
-
usage: "/debug [teammate]",
|
|
3072
|
+
usage: "/debug [teammate] [focus]",
|
|
2972
3073
|
description: "Analyze the last agent task with the coding agent",
|
|
2973
3074
|
run: (args) => this.cmdDebug(args),
|
|
2974
3075
|
},
|
|
@@ -3089,16 +3190,24 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3089
3190
|
return;
|
|
3090
3191
|
switch (event.type) {
|
|
3091
3192
|
case "task_assigned": {
|
|
3193
|
+
// System tasks (compaction, summarization, wisdom distillation) are
|
|
3194
|
+
// invisible — don't track them in the progress bar.
|
|
3195
|
+
if (event.assignment.system)
|
|
3196
|
+
break;
|
|
3092
3197
|
// Track this task and start the animated status bar
|
|
3093
3198
|
const key = event.assignment.teammate;
|
|
3094
3199
|
this.activeTasks.set(key, {
|
|
3095
3200
|
teammate: event.assignment.teammate,
|
|
3096
3201
|
task: event.assignment.task,
|
|
3202
|
+
startTime: Date.now(),
|
|
3097
3203
|
});
|
|
3098
3204
|
this.startStatusAnimation();
|
|
3099
3205
|
break;
|
|
3100
3206
|
}
|
|
3101
3207
|
case "task_completed": {
|
|
3208
|
+
// System task completions — don't touch activeTasks (was never added)
|
|
3209
|
+
if (event.result.system)
|
|
3210
|
+
break;
|
|
3102
3211
|
// Remove from active tasks and stop spinner.
|
|
3103
3212
|
// Result display is deferred to drainAgentQueue() so the defensive
|
|
3104
3213
|
// retry can update rawOutput before anything is shown to the user.
|
|
@@ -3186,10 +3295,13 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3186
3295
|
this.refreshView();
|
|
3187
3296
|
}
|
|
3188
3297
|
async cmdDebug(argsStr) {
|
|
3189
|
-
const
|
|
3298
|
+
const parts = argsStr.trim().split(/\s+/);
|
|
3299
|
+
const firstArg = (parts[0] ?? "").replace(/^@/, "");
|
|
3300
|
+
// Everything after the teammate name is the debug focus
|
|
3301
|
+
const debugFocus = parts.slice(1).join(" ").trim() || undefined;
|
|
3190
3302
|
// Resolve which teammate to debug
|
|
3191
3303
|
let targetName;
|
|
3192
|
-
if (
|
|
3304
|
+
if (firstArg === "everyone") {
|
|
3193
3305
|
// Pick all teammates with debug files, queue one analysis per teammate
|
|
3194
3306
|
const names = [];
|
|
3195
3307
|
for (const [name] of this.lastDebugFiles) {
|
|
@@ -3202,28 +3314,29 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3202
3314
|
return;
|
|
3203
3315
|
}
|
|
3204
3316
|
for (const name of names) {
|
|
3205
|
-
this.queueDebugAnalysis(name);
|
|
3317
|
+
this.queueDebugAnalysis(name, debugFocus);
|
|
3206
3318
|
}
|
|
3207
3319
|
return;
|
|
3208
3320
|
}
|
|
3209
|
-
else if (
|
|
3210
|
-
targetName =
|
|
3321
|
+
else if (firstArg) {
|
|
3322
|
+
targetName = firstArg;
|
|
3211
3323
|
}
|
|
3212
3324
|
else if (this.lastResult) {
|
|
3213
3325
|
targetName = this.lastResult.teammate;
|
|
3214
3326
|
}
|
|
3215
3327
|
else {
|
|
3216
|
-
this.feedLine(tp.muted(" No debug info available. Try: /debug [teammate]"));
|
|
3328
|
+
this.feedLine(tp.muted(" No debug info available. Try: /debug [teammate] [focus]"));
|
|
3217
3329
|
this.refreshView();
|
|
3218
3330
|
return;
|
|
3219
3331
|
}
|
|
3220
|
-
this.queueDebugAnalysis(targetName);
|
|
3332
|
+
this.queueDebugAnalysis(targetName, debugFocus);
|
|
3221
3333
|
}
|
|
3222
3334
|
/**
|
|
3223
3335
|
* Queue a debug analysis task — sends the last request + debug log
|
|
3224
3336
|
* to the base coding agent for analysis.
|
|
3337
|
+
* @param debugFocus Optional focus area the user wants to investigate
|
|
3225
3338
|
*/
|
|
3226
|
-
queueDebugAnalysis(teammate) {
|
|
3339
|
+
queueDebugAnalysis(teammate, debugFocus) {
|
|
3227
3340
|
const debugFile = this.lastDebugFiles.get(teammate);
|
|
3228
3341
|
const lastPrompt = this.lastTaskPrompts.get(teammate);
|
|
3229
3342
|
if (!debugFile) {
|
|
@@ -3241,8 +3354,11 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3241
3354
|
this.refreshView();
|
|
3242
3355
|
return;
|
|
3243
3356
|
}
|
|
3357
|
+
const focusLine = debugFocus
|
|
3358
|
+
? `\n\n**Focus your analysis on:** ${debugFocus}`
|
|
3359
|
+
: "";
|
|
3244
3360
|
const analysisPrompt = [
|
|
3245
|
-
`Analyze the following debug log from @${teammate}'s last task execution. Identify any issues, errors, or anomalies. If the response was empty, explain likely causes. Provide a concise diagnosis and suggest fixes if applicable
|
|
3361
|
+
`Analyze the following debug log from @${teammate}'s last task execution. Identify any issues, errors, or anomalies. If the response was empty, explain likely causes. Provide a concise diagnosis and suggest fixes if applicable.${focusLine}`,
|
|
3246
3362
|
"",
|
|
3247
3363
|
"## Last Request Sent to Agent",
|
|
3248
3364
|
"",
|
|
@@ -3254,6 +3370,9 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3254
3370
|
].join("\n");
|
|
3255
3371
|
// Show the debug log path — ctrl+click to open
|
|
3256
3372
|
this.feedLine(concat(tp.muted(" Debug log: "), tp.accent(debugFile)));
|
|
3373
|
+
if (debugFocus) {
|
|
3374
|
+
this.feedLine(tp.muted(` Focus: ${debugFocus}`));
|
|
3375
|
+
}
|
|
3257
3376
|
this.feedLine(tp.muted(" Queuing analysis…"));
|
|
3258
3377
|
this.refreshView();
|
|
3259
3378
|
this.taskQueue.push({
|
|
@@ -3280,34 +3399,18 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3280
3399
|
this.feedLine(concat(tp.muted(" Cancelled: "), tp.accent(`@${cancelDisplay}`), tp.muted(" — "), tp.text(removed.task.slice(0, 60))));
|
|
3281
3400
|
this.refreshView();
|
|
3282
3401
|
}
|
|
3283
|
-
/** Drain tasks for a single agent — runs in parallel with other agents.
|
|
3402
|
+
/** Drain user tasks for a single agent — runs in parallel with other agents.
|
|
3403
|
+
* System tasks are handled separately by runSystemTask(). */
|
|
3284
3404
|
async drainAgentQueue(agent) {
|
|
3285
3405
|
while (true) {
|
|
3286
|
-
const idx = this.taskQueue.findIndex((e) => e.teammate === agent);
|
|
3406
|
+
const idx = this.taskQueue.findIndex((e) => e.teammate === agent && !this.isSystemTask(e));
|
|
3287
3407
|
if (idx < 0)
|
|
3288
3408
|
break;
|
|
3289
3409
|
const entry = this.taskQueue.splice(idx, 1)[0];
|
|
3290
3410
|
this.agentActive.set(agent, entry);
|
|
3291
3411
|
const startTime = Date.now();
|
|
3292
3412
|
try {
|
|
3293
|
-
|
|
3294
|
-
await this.runCompact(entry.teammate);
|
|
3295
|
-
}
|
|
3296
|
-
else if (entry.type === "summarize") {
|
|
3297
|
-
// Internal housekeeping — summarize older conversation history
|
|
3298
|
-
const result = await this.orchestrator.assign({
|
|
3299
|
-
teammate: entry.teammate,
|
|
3300
|
-
task: entry.task,
|
|
3301
|
-
});
|
|
3302
|
-
// Extract the summary from the agent's output (strip protocol artifacts)
|
|
3303
|
-
const raw = result.rawOutput ?? "";
|
|
3304
|
-
this.conversationSummary = raw
|
|
3305
|
-
.replace(/^TO:\s*\S+\s*\n/im, "")
|
|
3306
|
-
.replace(/^#\s+.+\n*/m, "")
|
|
3307
|
-
.replace(/```json\s*\n\s*\{[\s\S]*?\}\s*\n\s*```\s*$/g, "")
|
|
3308
|
-
.trim();
|
|
3309
|
-
}
|
|
3310
|
-
else {
|
|
3413
|
+
{
|
|
3311
3414
|
// btw and debug tasks skip conversation context (not part of main thread)
|
|
3312
3415
|
const extraContext = entry.type === "btw" || entry.type === "debug"
|
|
3313
3416
|
? ""
|
|
@@ -3418,6 +3521,14 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3418
3521
|
task,
|
|
3419
3522
|
"",
|
|
3420
3523
|
];
|
|
3524
|
+
// Include the full prompt sent to the agent (with identity, memory, etc.)
|
|
3525
|
+
const fullPrompt = result?.fullPrompt;
|
|
3526
|
+
if (fullPrompt) {
|
|
3527
|
+
lines.push("## Full Prompt");
|
|
3528
|
+
lines.push("");
|
|
3529
|
+
lines.push(fullPrompt);
|
|
3530
|
+
lines.push("");
|
|
3531
|
+
}
|
|
3421
3532
|
if (error) {
|
|
3422
3533
|
lines.push("## Result");
|
|
3423
3534
|
lines.push("");
|
|
@@ -3474,7 +3585,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3474
3585
|
lines.push("");
|
|
3475
3586
|
writeFileSync(debugFile, lines.join("\n"), "utf-8");
|
|
3476
3587
|
this.lastDebugFiles.set(teammate, debugFile);
|
|
3477
|
-
this.lastTaskPrompts.set(teammate, task);
|
|
3588
|
+
this.lastTaskPrompts.set(teammate, fullPrompt ?? task);
|
|
3478
3589
|
}
|
|
3479
3590
|
catch {
|
|
3480
3591
|
// Don't let debug logging break task execution
|
|
@@ -3655,12 +3766,12 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3655
3766
|
*/
|
|
3656
3767
|
async runCompact(name, silent = false) {
|
|
3657
3768
|
const teammateDir = join(this.teammatesDir, name);
|
|
3658
|
-
if (this.chatView) {
|
|
3769
|
+
if (!silent && this.chatView) {
|
|
3659
3770
|
this.chatView.setProgress(`Compacting ${name}...`);
|
|
3660
3771
|
this.refreshView();
|
|
3661
3772
|
}
|
|
3662
3773
|
let spinner = null;
|
|
3663
|
-
if (!this.chatView) {
|
|
3774
|
+
if (!silent && !this.chatView) {
|
|
3664
3775
|
spinner = ora({ text: `Compacting ${name}...`, color: "cyan" }).start();
|
|
3665
3776
|
}
|
|
3666
3777
|
try {
|
|
@@ -3698,16 +3809,16 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3698
3809
|
if (this.chatView)
|
|
3699
3810
|
this.feedLine(tp.success(` ✔ ${name}: ${parts.join(", ")}`));
|
|
3700
3811
|
}
|
|
3701
|
-
if (this.chatView)
|
|
3812
|
+
if (!silent && this.chatView)
|
|
3702
3813
|
this.chatView.setProgress(null);
|
|
3703
3814
|
// Sync recall index for this teammate (bundled library call)
|
|
3704
3815
|
try {
|
|
3705
|
-
if (this.chatView) {
|
|
3816
|
+
if (!silent && this.chatView) {
|
|
3706
3817
|
this.chatView.setProgress(`Syncing ${name} index...`);
|
|
3707
3818
|
this.refreshView();
|
|
3708
3819
|
}
|
|
3709
3820
|
let syncSpinner = null;
|
|
3710
|
-
if (!this.chatView) {
|
|
3821
|
+
if (!silent && !this.chatView) {
|
|
3711
3822
|
syncSpinner = ora({
|
|
3712
3823
|
text: `Syncing ${name} index...`,
|
|
3713
3824
|
color: "cyan",
|
|
@@ -3717,7 +3828,8 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3717
3828
|
if (syncSpinner)
|
|
3718
3829
|
syncSpinner.succeed(`${name}: index synced`);
|
|
3719
3830
|
if (this.chatView) {
|
|
3720
|
-
|
|
3831
|
+
if (!silent)
|
|
3832
|
+
this.chatView.setProgress(null);
|
|
3721
3833
|
if (!silent)
|
|
3722
3834
|
this.feedLine(tp.success(` ✔ ${name}: index synced`));
|
|
3723
3835
|
}
|
|
@@ -3734,9 +3846,9 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3734
3846
|
type: "agent",
|
|
3735
3847
|
teammate: name,
|
|
3736
3848
|
task: wisdomPrompt,
|
|
3849
|
+
system: true,
|
|
3737
3850
|
});
|
|
3738
|
-
|
|
3739
|
-
this.feedLine(tp.muted(` ↻ ${name}: queued wisdom distillation`));
|
|
3851
|
+
this.kickDrain();
|
|
3740
3852
|
}
|
|
3741
3853
|
}
|
|
3742
3854
|
catch {
|
|
@@ -3748,7 +3860,8 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3748
3860
|
if (spinner)
|
|
3749
3861
|
spinner.fail(`${name}: ${msg}`);
|
|
3750
3862
|
if (this.chatView) {
|
|
3751
|
-
|
|
3863
|
+
if (!silent)
|
|
3864
|
+
this.chatView.setProgress(null);
|
|
3752
3865
|
// Errors always show in feed
|
|
3753
3866
|
this.feedLine(tp.error(` ✖ ${name}: ${msg}`));
|
|
3754
3867
|
}
|
|
@@ -3879,16 +3992,8 @@ Issues that can't be resolved unilaterally — they need input from other teamma
|
|
|
3879
3992
|
// 1. Run compaction for all teammates (auto-compact + episodic + sync + wisdom)
|
|
3880
3993
|
// Progress bar shows status; feed only shows lines when actual work is done
|
|
3881
3994
|
for (const name of teammates) {
|
|
3882
|
-
if (this.chatView) {
|
|
3883
|
-
this.chatView.setProgress(`Maintaining @${name}...`);
|
|
3884
|
-
this.refreshView();
|
|
3885
|
-
}
|
|
3886
3995
|
await this.runCompact(name, true);
|
|
3887
3996
|
}
|
|
3888
|
-
if (this.chatView) {
|
|
3889
|
-
this.chatView.setProgress(null);
|
|
3890
|
-
this.refreshView();
|
|
3891
|
-
}
|
|
3892
3997
|
// 2. Purge daily logs older than 30 days (disk + Vectra)
|
|
3893
3998
|
const { Indexer } = await import("@teammates/recall");
|
|
3894
3999
|
const indexer = new Indexer({ teammatesDir: this.teammatesDir });
|
package/dist/orchestrator.js
CHANGED
|
@@ -82,6 +82,9 @@ export class Orchestrator {
|
|
|
82
82
|
const result = await this.adapter.executeTask(sessionId, teammate, prompt, {
|
|
83
83
|
raw: assignment.raw,
|
|
84
84
|
});
|
|
85
|
+
// Propagate system flag so event handlers can distinguish system vs user tasks
|
|
86
|
+
if (assignment.system)
|
|
87
|
+
result.system = true;
|
|
85
88
|
this.onEvent({ type: "task_completed", result });
|
|
86
89
|
// Update status (preserve presence)
|
|
87
90
|
const postPresence = this.statuses.get(assignment.teammate)?.presence ?? "online";
|
package/dist/types.d.ts
CHANGED
|
@@ -64,6 +64,8 @@ export interface HandoffEnvelope {
|
|
|
64
64
|
export interface TaskResult {
|
|
65
65
|
/** The teammate that executed the task */
|
|
66
66
|
teammate: string;
|
|
67
|
+
/** Whether this was a system-initiated task */
|
|
68
|
+
system?: boolean;
|
|
67
69
|
/** Whether the task completed successfully */
|
|
68
70
|
success: boolean;
|
|
69
71
|
/** Summary of what was done */
|
|
@@ -74,6 +76,8 @@ export interface TaskResult {
|
|
|
74
76
|
handoffs: HandoffEnvelope[];
|
|
75
77
|
/** Raw output from the agent */
|
|
76
78
|
rawOutput?: string;
|
|
79
|
+
/** The full prompt sent to the agent (for debug logging) */
|
|
80
|
+
fullPrompt?: string;
|
|
77
81
|
/** Process diagnostics for debugging empty/failed responses */
|
|
78
82
|
diagnostics?: {
|
|
79
83
|
/** Process exit code (null if killed by signal) */
|
|
@@ -98,6 +102,8 @@ export interface TaskAssignment {
|
|
|
98
102
|
extraContext?: string;
|
|
99
103
|
/** When true, skip identity/memory prompt wrapping — send task as-is */
|
|
100
104
|
raw?: boolean;
|
|
105
|
+
/** When true, this is a system-initiated task — suppress progress bar */
|
|
106
|
+
system?: boolean;
|
|
101
107
|
}
|
|
102
108
|
/** Orchestrator event for logging/hooks */
|
|
103
109
|
export type OrchestratorEvent = {
|
|
@@ -116,6 +122,7 @@ export type QueueEntry = {
|
|
|
116
122
|
type: "agent";
|
|
117
123
|
teammate: string;
|
|
118
124
|
task: string;
|
|
125
|
+
system?: boolean;
|
|
119
126
|
} | {
|
|
120
127
|
type: "compact";
|
|
121
128
|
teammate: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teammates/cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Agent-agnostic CLI for teammates. Routes tasks, manages handoffs, and plugs into any coding agent backend.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"license": "MIT",
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@github/copilot-sdk": "^0.1.32",
|
|
37
|
-
"@teammates/consolonia": "0.5.
|
|
38
|
-
"@teammates/recall": "0.5.
|
|
37
|
+
"@teammates/consolonia": "0.5.2",
|
|
38
|
+
"@teammates/recall": "0.5.2",
|
|
39
39
|
"chalk": "^5.6.2",
|
|
40
40
|
"ora": "^9.3.0"
|
|
41
41
|
},
|