@teammates/cli 0.5.0 → 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.d.ts +8 -0
- package/dist/adapter.js +146 -108
- package/dist/adapter.test.js +29 -16
- package/dist/adapters/cli-proxy.js +59 -2
- package/dist/adapters/copilot.js +14 -2
- package/dist/adapters/echo.js +3 -1
- package/dist/banner.js +5 -1
- package/dist/cli-args.js +23 -23
- package/dist/cli-args.test.d.ts +1 -0
- package/dist/cli-args.test.js +125 -0
- package/dist/cli.js +301 -163
- package/dist/compact.d.ts +23 -0
- package/dist/compact.js +181 -11
- package/dist/compact.test.js +323 -7
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/onboard.js +165 -165
- package/dist/orchestrator.js +7 -1
- package/dist/personas.test.d.ts +1 -0
- package/dist/personas.test.js +88 -0
- package/dist/registry.test.js +23 -23
- package/dist/theme.test.d.ts +1 -0
- package/dist/theme.test.js +113 -0
- package/dist/types.d.ts +7 -0
- package/package.json +3 -3
- package/personas/architect.md +4 -0
- package/personas/backend.md +4 -0
- package/personas/data-engineer.md +4 -0
- package/personas/designer.md +4 -0
- package/personas/devops.md +4 -0
- package/personas/frontend.md +4 -0
- package/personas/ml-ai.md +4 -0
- package/personas/mobile.md +4 -0
- package/personas/performance.md +4 -0
- package/personas/pm.md +4 -0
- package/personas/prompt-engineer.md +122 -0
- package/personas/qa.md +4 -0
- package/personas/security.md +4 -0
- package/personas/sre.md +4 -0
- package/personas/swe.md +4 -0
- package/personas/tech-writer.md +4 -0
package/dist/cli.js
CHANGED
|
@@ -10,21 +10,21 @@
|
|
|
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";
|
|
17
17
|
import ora from "ora";
|
|
18
|
-
import { syncRecallIndex } from "./adapter.js";
|
|
19
|
-
import { AnimatedBanner } from "./banner.js";
|
|
18
|
+
import { DAILY_LOG_BUDGET_TOKENS, syncRecallIndex } from "./adapter.js";
|
|
19
|
+
import { AnimatedBanner, } from "./banner.js";
|
|
20
20
|
import { findTeammatesDir, PKG_VERSION, parseCliArgs, printUsage, resolveAdapter, } from "./cli-args.js";
|
|
21
21
|
import { findAtMention, isImagePath, relativeTime, wrapLine, } from "./cli-utils.js";
|
|
22
|
-
import { buildWisdomPrompt, compactEpisodic } from "./compact.js";
|
|
22
|
+
import { autoCompactForBudget, buildWisdomPrompt, compactEpisodic, purgeStaleDailies, } from "./compact.js";
|
|
23
23
|
import { PromptInput } from "./console/prompt-input.js";
|
|
24
24
|
import { buildTitle } from "./console/startup.js";
|
|
25
25
|
import { buildImportAdaptationPrompt, copyTemplateFiles, getOnboardingPrompt, importTeammates, } from "./onboard.js";
|
|
26
|
-
import { loadPersonas, scaffoldFromPersona } from "./personas.js";
|
|
27
26
|
import { Orchestrator } from "./orchestrator.js";
|
|
27
|
+
import { loadPersonas, scaffoldFromPersona } from "./personas.js";
|
|
28
28
|
import { colorToHex, theme, tp } from "./theme.js";
|
|
29
29
|
// ─── Parsed CLI arguments ────────────────────────────────────────────
|
|
30
30
|
const cliArgs = parseCliArgs();
|
|
@@ -69,9 +69,7 @@ class TeammatesREPL {
|
|
|
69
69
|
.trim();
|
|
70
70
|
// Header: "teammate: subject"
|
|
71
71
|
const subject = result.summary || "Task completed";
|
|
72
|
-
const displayTeammate = result.teammate === this.selfName
|
|
73
|
-
? this.adapterName
|
|
74
|
-
: result.teammate;
|
|
72
|
+
const displayTeammate = result.teammate === this.selfName ? this.adapterName : result.teammate;
|
|
75
73
|
this.feedLine(concat(tp.accent(`${displayTeammate}: `), tp.text(subject)));
|
|
76
74
|
this.lastCleanedOutput = cleaned;
|
|
77
75
|
if (cleaned) {
|
|
@@ -219,6 +217,8 @@ class TeammatesREPL {
|
|
|
219
217
|
taskQueue = [];
|
|
220
218
|
/** Per-agent active tasks — one per agent running in parallel. */
|
|
221
219
|
agentActive = new Map();
|
|
220
|
+
/** Active system tasks — multiple can run concurrently per agent. */
|
|
221
|
+
systemActive = new Map();
|
|
222
222
|
/** Agents currently in a silent retry — suppress all events. */
|
|
223
223
|
silentAgents = new Set();
|
|
224
224
|
/** Per-agent drain locks — prevents double-draining a single agent. */
|
|
@@ -233,7 +233,6 @@ class TeammatesREPL {
|
|
|
233
233
|
ctrlcPending = false; // true after first Ctrl+C, waiting for second
|
|
234
234
|
ctrlcTimer = null;
|
|
235
235
|
lastCleanedOutput = ""; // last teammate output for clipboard copy
|
|
236
|
-
dispatching = false;
|
|
237
236
|
autoApproveHandoffs = false;
|
|
238
237
|
/** Last debug log file path per teammate — for /debug analysis. */
|
|
239
238
|
lastDebugFiles = new Map();
|
|
@@ -333,37 +332,74 @@ class TeammatesREPL {
|
|
|
333
332
|
this.input.setStatus(null);
|
|
334
333
|
}
|
|
335
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
|
+
}
|
|
336
367
|
/** Render one frame of the status animation. */
|
|
337
368
|
renderStatusFrame() {
|
|
338
369
|
if (this.activeTasks.size === 0)
|
|
339
370
|
return;
|
|
340
371
|
const entries = Array.from(this.activeTasks.values());
|
|
341
|
-
const
|
|
342
|
-
const
|
|
372
|
+
const total = entries.length;
|
|
373
|
+
const idx = this.statusRotateIndex % total;
|
|
374
|
+
const { teammate, task, startTime } = entries[idx];
|
|
343
375
|
const displayName = teammate === this.selfName ? this.adapterName : teammate;
|
|
344
376
|
const spinChar = TeammatesREPL.SPINNER[this.statusFrame % TeammatesREPL.SPINNER.length];
|
|
345
|
-
const
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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)}…`
|
|
353
392
|
: cleanTask;
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
: "";
|
|
357
|
-
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}`)));
|
|
358
395
|
this.app.refresh();
|
|
359
396
|
}
|
|
360
397
|
else {
|
|
361
|
-
// Mostly bright blue, periodically flicker to dark blue
|
|
362
398
|
const spinColor = this.statusFrame % 8 === 0 ? chalk.blue : chalk.blueBright;
|
|
363
399
|
const line = ` ${spinColor(spinChar)} ` +
|
|
364
400
|
chalk.bold(displayName) +
|
|
365
|
-
chalk.gray(
|
|
366
|
-
|
|
401
|
+
chalk.gray(`... ${taskText}`) +
|
|
402
|
+
chalk.gray(suffix);
|
|
367
403
|
this.input.setStatus(line);
|
|
368
404
|
}
|
|
369
405
|
}
|
|
@@ -948,14 +984,14 @@ class TeammatesREPL {
|
|
|
948
984
|
const changes = proposals
|
|
949
985
|
.map((p) => `- **Proposal ${p.index}: ${p.title}**\n - Section: ${p.section}\n - Before: ${p.before}\n - After: ${p.after}`)
|
|
950
986
|
.join("\n\n");
|
|
951
|
-
const applyPrompt = `The user approved the following SOUL.md changes from your retrospective. Apply them now.
|
|
952
|
-
|
|
953
|
-
**Edit your SOUL.md file** (\`.teammates/${teammate}/SOUL.md\`) to incorporate these changes:
|
|
954
|
-
|
|
955
|
-
${changes}
|
|
956
|
-
|
|
957
|
-
After editing SOUL.md, record a brief summary of the retro outcome in your daily log: which proposals were approved and what changed.
|
|
958
|
-
|
|
987
|
+
const applyPrompt = `The user approved the following SOUL.md changes from your retrospective. Apply them now.
|
|
988
|
+
|
|
989
|
+
**Edit your SOUL.md file** (\`.teammates/${teammate}/SOUL.md\`) to incorporate these changes:
|
|
990
|
+
|
|
991
|
+
${changes}
|
|
992
|
+
|
|
993
|
+
After editing SOUL.md, record a brief summary of the retro outcome in your daily log: which proposals were approved and what changed.
|
|
994
|
+
|
|
959
995
|
Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily log.`;
|
|
960
996
|
this.taskQueue.push({ type: "agent", teammate, task: applyPrompt });
|
|
961
997
|
this.feedLine(concat(tp.muted(" Queued SOUL.md update for "), tp.accent(`@${teammate}`)));
|
|
@@ -997,7 +1033,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
997
1033
|
mentioned = [];
|
|
998
1034
|
while ((m = mentionRegex.exec(input)) !== null) {
|
|
999
1035
|
// Remap adapter name alias → user avatar for routing
|
|
1000
|
-
const name =
|
|
1036
|
+
const name = m[1] === this.adapterName && this.userAlias ? this.selfName : m[1];
|
|
1001
1037
|
if (allNames.includes(name) && !mentioned.includes(name)) {
|
|
1002
1038
|
mentioned.push(name);
|
|
1003
1039
|
}
|
|
@@ -1035,9 +1071,24 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1035
1071
|
this.taskQueue.push({ type: "agent", teammate: match, task: input });
|
|
1036
1072
|
this.kickDrain();
|
|
1037
1073
|
}
|
|
1038
|
-
/**
|
|
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. */
|
|
1039
1082
|
kickDrain() {
|
|
1040
|
-
//
|
|
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
|
|
1041
1092
|
const agentsWithWork = new Set();
|
|
1042
1093
|
for (const entry of this.taskQueue) {
|
|
1043
1094
|
agentsWithWork.add(entry.teammate);
|
|
@@ -1051,6 +1102,53 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1051
1102
|
}
|
|
1052
1103
|
}
|
|
1053
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
|
+
}
|
|
1054
1152
|
// ─── Onboarding ───────────────────────────────────────────────────
|
|
1055
1153
|
/**
|
|
1056
1154
|
* Interactive prompt for team onboarding after user profile is set up.
|
|
@@ -1168,11 +1266,12 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1168
1266
|
const folderName = name.toLowerCase().replace(/[^a-z0-9_-]/g, "");
|
|
1169
1267
|
await scaffoldFromPersona(teammatesDir, folderName, p);
|
|
1170
1268
|
created.push(folderName);
|
|
1171
|
-
console.log(chalk.green(" ✔ ") +
|
|
1269
|
+
console.log(chalk.green(" ✔ ") +
|
|
1270
|
+
chalk.white(`@${folderName}`) +
|
|
1271
|
+
chalk.gray(` — ${p.persona}`));
|
|
1172
1272
|
}
|
|
1173
1273
|
console.log();
|
|
1174
|
-
console.log(chalk.green(` ✔ Created ${created.length} teammate${created.length > 1 ? "s" : ""}: `) +
|
|
1175
|
-
chalk.white(created.map((n) => `@${n}`).join(", ")));
|
|
1274
|
+
console.log(chalk.green(` ✔ Created ${created.length} teammate${created.length > 1 ? "s" : ""}: `) + chalk.white(created.map((n) => `@${n}`).join(", ")));
|
|
1176
1275
|
console.log(chalk.gray(" Tip: Your agent will adapt ownership and capabilities to this codebase on first task."));
|
|
1177
1276
|
console.log();
|
|
1178
1277
|
}
|
|
@@ -1658,8 +1757,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1658
1757
|
};
|
|
1659
1758
|
this.writeUserProfile(teammatesDir, login, answers);
|
|
1660
1759
|
this.createUserAvatar(teammatesDir, login, answers);
|
|
1661
|
-
console.log(chalk.green(" ✔ ") +
|
|
1662
|
-
chalk.gray(`Profile created — avatar @${login}`));
|
|
1760
|
+
console.log(chalk.green(" ✔ ") + chalk.gray(`Profile created — avatar @${login}`));
|
|
1663
1761
|
console.log();
|
|
1664
1762
|
}
|
|
1665
1763
|
/**
|
|
@@ -1669,7 +1767,10 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1669
1767
|
console.log();
|
|
1670
1768
|
console.log(chalk.gray(" (alias is required, press Enter to skip others)\n"));
|
|
1671
1769
|
const aliasRaw = await this.askInput("Your alias (e.g., alex): ");
|
|
1672
|
-
const alias = aliasRaw
|
|
1770
|
+
const alias = aliasRaw
|
|
1771
|
+
.toLowerCase()
|
|
1772
|
+
.replace(/[^a-z0-9_-]/g, "")
|
|
1773
|
+
.trim();
|
|
1673
1774
|
if (!alias) {
|
|
1674
1775
|
console.log(chalk.yellow(" Alias is required. Run /user to try again.\n"));
|
|
1675
1776
|
return;
|
|
@@ -1692,8 +1793,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1692
1793
|
this.writeUserProfile(teammatesDir, alias, answers);
|
|
1693
1794
|
this.createUserAvatar(teammatesDir, alias, answers);
|
|
1694
1795
|
console.log();
|
|
1695
|
-
console.log(chalk.green(" ✔ ") +
|
|
1696
|
-
chalk.gray(`Profile created — avatar @${alias}`));
|
|
1796
|
+
console.log(chalk.green(" ✔ ") + chalk.gray(`Profile created — avatar @${alias}`));
|
|
1697
1797
|
console.log(chalk.gray(" Update anytime with /user"));
|
|
1698
1798
|
console.log();
|
|
1699
1799
|
}
|
|
@@ -1781,12 +1881,16 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1781
1881
|
if (roleMatch)
|
|
1782
1882
|
role = roleMatch[1].trim();
|
|
1783
1883
|
}
|
|
1784
|
-
catch {
|
|
1884
|
+
catch {
|
|
1885
|
+
/* avatar folder may not exist yet */
|
|
1886
|
+
}
|
|
1785
1887
|
let wisdom = "";
|
|
1786
1888
|
try {
|
|
1787
1889
|
wisdom = readFileSync(join(avatarDir, "WISDOM.md"), "utf-8");
|
|
1788
1890
|
}
|
|
1789
|
-
catch {
|
|
1891
|
+
catch {
|
|
1892
|
+
/* ok */
|
|
1893
|
+
}
|
|
1790
1894
|
registry.register({
|
|
1791
1895
|
name: alias,
|
|
1792
1896
|
type: "human",
|
|
@@ -1799,7 +1903,9 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1799
1903
|
routingKeywords: [],
|
|
1800
1904
|
});
|
|
1801
1905
|
// Set presence to online (local user is always online)
|
|
1802
|
-
this.orchestrator
|
|
1906
|
+
this.orchestrator
|
|
1907
|
+
.getAllStatuses()
|
|
1908
|
+
.set(alias, { state: "idle", presence: "online" });
|
|
1803
1909
|
// Update the adapter name so tasks route to the avatar
|
|
1804
1910
|
this.userAlias = alias;
|
|
1805
1911
|
}
|
|
@@ -1891,9 +1997,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1891
1997
|
if (completedArgs > 0)
|
|
1892
1998
|
return [];
|
|
1893
1999
|
const lower = partial.toLowerCase();
|
|
1894
|
-
return TeammatesREPL.CONFIGURABLE_SERVICES
|
|
1895
|
-
.filter((s) => s.startsWith(lower))
|
|
1896
|
-
.map((s) => ({
|
|
2000
|
+
return TeammatesREPL.CONFIGURABLE_SERVICES.filter((s) => s.startsWith(lower)).map((s) => ({
|
|
1897
2001
|
label: s,
|
|
1898
2002
|
description: `configure ${s}`,
|
|
1899
2003
|
completion: `/${cmdName} ${s} `,
|
|
@@ -2184,7 +2288,9 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2184
2288
|
routingKeywords: [],
|
|
2185
2289
|
cwd: dirname(this.teammatesDir),
|
|
2186
2290
|
});
|
|
2187
|
-
this.orchestrator
|
|
2291
|
+
this.orchestrator
|
|
2292
|
+
.getAllStatuses()
|
|
2293
|
+
.set(this.adapterName, { state: "idle", presence: "online" });
|
|
2188
2294
|
}
|
|
2189
2295
|
// Populate roster on the adapter so prompts include team info
|
|
2190
2296
|
// Exclude the user avatar and adapter fallback — neither is an addressable teammate
|
|
@@ -2210,7 +2316,9 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2210
2316
|
borderStyle: (s) => chalk.gray(s),
|
|
2211
2317
|
colorize: (value) => {
|
|
2212
2318
|
const validNames = new Set([
|
|
2213
|
-
...this.orchestrator
|
|
2319
|
+
...this.orchestrator
|
|
2320
|
+
.listTeammates()
|
|
2321
|
+
.filter((n) => n !== this.adapterName && n !== this.userAlias),
|
|
2214
2322
|
this.adapterName,
|
|
2215
2323
|
"everyone",
|
|
2216
2324
|
]);
|
|
@@ -2258,9 +2366,12 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2258
2366
|
const bannerTeammates = [];
|
|
2259
2367
|
// Add user avatar first (displayed as adapter name alias)
|
|
2260
2368
|
if (this.userAlias) {
|
|
2261
|
-
const ut = reg.get(this.userAlias);
|
|
2262
2369
|
const up = statuses.get(this.userAlias)?.presence ?? "online";
|
|
2263
|
-
bannerTeammates.push({
|
|
2370
|
+
bannerTeammates.push({
|
|
2371
|
+
name: this.adapterName,
|
|
2372
|
+
role: "Coding agent that performs tasks on your behalf.",
|
|
2373
|
+
presence: up,
|
|
2374
|
+
});
|
|
2264
2375
|
}
|
|
2265
2376
|
for (const name of names) {
|
|
2266
2377
|
const t = reg.get(name);
|
|
@@ -2299,7 +2410,9 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2299
2410
|
}
|
|
2300
2411
|
// Colorize @mentions only if they reference a valid teammate or the user
|
|
2301
2412
|
const validNames = new Set([
|
|
2302
|
-
...this.orchestrator
|
|
2413
|
+
...this.orchestrator
|
|
2414
|
+
.listTeammates()
|
|
2415
|
+
.filter((n) => n !== this.adapterName && n !== this.userAlias),
|
|
2303
2416
|
this.adapterName,
|
|
2304
2417
|
"everyone",
|
|
2305
2418
|
]);
|
|
@@ -2343,11 +2456,11 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2343
2456
|
progressStyle: { fg: t.progress, italic: true },
|
|
2344
2457
|
dropdownHighlightStyle: { fg: t.accent },
|
|
2345
2458
|
dropdownStyle: { fg: t.textMuted },
|
|
2346
|
-
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)))),
|
|
2347
2460
|
footerRight: tp.muted("? /help "),
|
|
2348
2461
|
footerStyle: { fg: t.textDim },
|
|
2349
2462
|
});
|
|
2350
|
-
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))));
|
|
2351
2464
|
this.defaultFooterRight = tp.muted("? /help ");
|
|
2352
2465
|
// Wire ChatView events for input handling
|
|
2353
2466
|
this.chatView.on("submit", (rawLine) => {
|
|
@@ -2591,7 +2704,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2591
2704
|
const preMentions = [];
|
|
2592
2705
|
while ((pm = preMentionRegex.exec(rawLine)) !== null) {
|
|
2593
2706
|
// Remap adapter name alias → user avatar for routing
|
|
2594
|
-
const name =
|
|
2707
|
+
const name = pm[1] === this.adapterName && this.userAlias ? this.selfName : pm[1];
|
|
2595
2708
|
if (allNames.includes(name) && !preMentions.includes(name)) {
|
|
2596
2709
|
preMentions.push(name);
|
|
2597
2710
|
}
|
|
@@ -2663,16 +2776,12 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2663
2776
|
}
|
|
2664
2777
|
// Slash commands
|
|
2665
2778
|
if (input.startsWith("/")) {
|
|
2666
|
-
this.dispatching = true;
|
|
2667
2779
|
try {
|
|
2668
2780
|
await this.dispatch(input);
|
|
2669
2781
|
}
|
|
2670
2782
|
catch (err) {
|
|
2671
2783
|
this.feedLine(tp.error(`Error: ${err.message}`));
|
|
2672
2784
|
}
|
|
2673
|
-
finally {
|
|
2674
|
-
this.dispatching = false;
|
|
2675
|
-
}
|
|
2676
2785
|
this.refreshView();
|
|
2677
2786
|
return;
|
|
2678
2787
|
}
|
|
@@ -2710,14 +2819,22 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2710
2819
|
// Show user avatar first (displayed as adapter name alias)
|
|
2711
2820
|
if (this.userAlias) {
|
|
2712
2821
|
const up = statuses.get(this.userAlias)?.presence ?? "online";
|
|
2713
|
-
const udot = up === "online"
|
|
2822
|
+
const udot = up === "online"
|
|
2823
|
+
? tp.success("●")
|
|
2824
|
+
: up === "reachable"
|
|
2825
|
+
? tp.warning("●")
|
|
2826
|
+
: tp.error("●");
|
|
2714
2827
|
this.feedLine(concat(tp.text(" "), udot, tp.accent(` @${this.adapterName.padEnd(14)}`), tp.muted("Coding agent that performs tasks on your behalf.")));
|
|
2715
2828
|
}
|
|
2716
2829
|
for (const name of teammates) {
|
|
2717
2830
|
const t = registry.get(name);
|
|
2718
2831
|
if (t) {
|
|
2719
2832
|
const p = statuses.get(name)?.presence ?? "online";
|
|
2720
|
-
const dot = p === "online"
|
|
2833
|
+
const dot = p === "online"
|
|
2834
|
+
? tp.success("●")
|
|
2835
|
+
: p === "reachable"
|
|
2836
|
+
? tp.warning("●")
|
|
2837
|
+
: tp.error("●");
|
|
2721
2838
|
this.feedLine(concat(tp.text(" "), dot, tp.accent(` @${name.padEnd(14)}`), tp.muted(t.role)));
|
|
2722
2839
|
}
|
|
2723
2840
|
}
|
|
@@ -2910,7 +3027,10 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2910
3027
|
// Get username for confirmation
|
|
2911
3028
|
let username = "";
|
|
2912
3029
|
try {
|
|
2913
|
-
username = execSync("gh api user --jq .login", {
|
|
3030
|
+
username = execSync("gh api user --jq .login", {
|
|
3031
|
+
stdio: "pipe",
|
|
3032
|
+
encoding: "utf-8",
|
|
3033
|
+
}).trim();
|
|
2914
3034
|
}
|
|
2915
3035
|
catch {
|
|
2916
3036
|
// non-critical
|
|
@@ -2949,7 +3069,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2949
3069
|
{
|
|
2950
3070
|
name: "debug",
|
|
2951
3071
|
aliases: ["raw"],
|
|
2952
|
-
usage: "/debug [teammate]",
|
|
3072
|
+
usage: "/debug [teammate] [focus]",
|
|
2953
3073
|
description: "Analyze the last agent task with the coding agent",
|
|
2954
3074
|
run: (args) => this.cmdDebug(args),
|
|
2955
3075
|
},
|
|
@@ -3070,16 +3190,24 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3070
3190
|
return;
|
|
3071
3191
|
switch (event.type) {
|
|
3072
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;
|
|
3073
3197
|
// Track this task and start the animated status bar
|
|
3074
3198
|
const key = event.assignment.teammate;
|
|
3075
3199
|
this.activeTasks.set(key, {
|
|
3076
3200
|
teammate: event.assignment.teammate,
|
|
3077
3201
|
task: event.assignment.task,
|
|
3202
|
+
startTime: Date.now(),
|
|
3078
3203
|
});
|
|
3079
3204
|
this.startStatusAnimation();
|
|
3080
3205
|
break;
|
|
3081
3206
|
}
|
|
3082
3207
|
case "task_completed": {
|
|
3208
|
+
// System task completions — don't touch activeTasks (was never added)
|
|
3209
|
+
if (event.result.system)
|
|
3210
|
+
break;
|
|
3083
3211
|
// Remove from active tasks and stop spinner.
|
|
3084
3212
|
// Result display is deferred to drainAgentQueue() so the defensive
|
|
3085
3213
|
// retry can update rawOutput before anything is shown to the user.
|
|
@@ -3090,7 +3218,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3090
3218
|
}
|
|
3091
3219
|
break;
|
|
3092
3220
|
}
|
|
3093
|
-
case "error":
|
|
3221
|
+
case "error": {
|
|
3094
3222
|
this.activeTasks.delete(event.teammate);
|
|
3095
3223
|
if (this.activeTasks.size === 0)
|
|
3096
3224
|
this.stopStatusAnimation();
|
|
@@ -3100,6 +3228,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3100
3228
|
this.feedLine(tp.error(` ✖ ${displayErr}: ${event.error}`));
|
|
3101
3229
|
this.showPrompt();
|
|
3102
3230
|
break;
|
|
3231
|
+
}
|
|
3103
3232
|
}
|
|
3104
3233
|
}
|
|
3105
3234
|
async cmdStatus() {
|
|
@@ -3166,10 +3295,13 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3166
3295
|
this.refreshView();
|
|
3167
3296
|
}
|
|
3168
3297
|
async cmdDebug(argsStr) {
|
|
3169
|
-
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;
|
|
3170
3302
|
// Resolve which teammate to debug
|
|
3171
3303
|
let targetName;
|
|
3172
|
-
if (
|
|
3304
|
+
if (firstArg === "everyone") {
|
|
3173
3305
|
// Pick all teammates with debug files, queue one analysis per teammate
|
|
3174
3306
|
const names = [];
|
|
3175
3307
|
for (const [name] of this.lastDebugFiles) {
|
|
@@ -3182,28 +3314,29 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3182
3314
|
return;
|
|
3183
3315
|
}
|
|
3184
3316
|
for (const name of names) {
|
|
3185
|
-
this.queueDebugAnalysis(name);
|
|
3317
|
+
this.queueDebugAnalysis(name, debugFocus);
|
|
3186
3318
|
}
|
|
3187
3319
|
return;
|
|
3188
3320
|
}
|
|
3189
|
-
else if (
|
|
3190
|
-
targetName =
|
|
3321
|
+
else if (firstArg) {
|
|
3322
|
+
targetName = firstArg;
|
|
3191
3323
|
}
|
|
3192
3324
|
else if (this.lastResult) {
|
|
3193
3325
|
targetName = this.lastResult.teammate;
|
|
3194
3326
|
}
|
|
3195
3327
|
else {
|
|
3196
|
-
this.feedLine(tp.muted(" No debug info available. Try: /debug [teammate]"));
|
|
3328
|
+
this.feedLine(tp.muted(" No debug info available. Try: /debug [teammate] [focus]"));
|
|
3197
3329
|
this.refreshView();
|
|
3198
3330
|
return;
|
|
3199
3331
|
}
|
|
3200
|
-
this.queueDebugAnalysis(targetName);
|
|
3332
|
+
this.queueDebugAnalysis(targetName, debugFocus);
|
|
3201
3333
|
}
|
|
3202
3334
|
/**
|
|
3203
3335
|
* Queue a debug analysis task — sends the last request + debug log
|
|
3204
3336
|
* to the base coding agent for analysis.
|
|
3337
|
+
* @param debugFocus Optional focus area the user wants to investigate
|
|
3205
3338
|
*/
|
|
3206
|
-
queueDebugAnalysis(teammate) {
|
|
3339
|
+
queueDebugAnalysis(teammate, debugFocus) {
|
|
3207
3340
|
const debugFile = this.lastDebugFiles.get(teammate);
|
|
3208
3341
|
const lastPrompt = this.lastTaskPrompts.get(teammate);
|
|
3209
3342
|
if (!debugFile) {
|
|
@@ -3221,8 +3354,11 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3221
3354
|
this.refreshView();
|
|
3222
3355
|
return;
|
|
3223
3356
|
}
|
|
3357
|
+
const focusLine = debugFocus
|
|
3358
|
+
? `\n\n**Focus your analysis on:** ${debugFocus}`
|
|
3359
|
+
: "";
|
|
3224
3360
|
const analysisPrompt = [
|
|
3225
|
-
`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}`,
|
|
3226
3362
|
"",
|
|
3227
3363
|
"## Last Request Sent to Agent",
|
|
3228
3364
|
"",
|
|
@@ -3234,6 +3370,9 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3234
3370
|
].join("\n");
|
|
3235
3371
|
// Show the debug log path — ctrl+click to open
|
|
3236
3372
|
this.feedLine(concat(tp.muted(" Debug log: "), tp.accent(debugFile)));
|
|
3373
|
+
if (debugFocus) {
|
|
3374
|
+
this.feedLine(tp.muted(` Focus: ${debugFocus}`));
|
|
3375
|
+
}
|
|
3237
3376
|
this.feedLine(tp.muted(" Queuing analysis…"));
|
|
3238
3377
|
this.refreshView();
|
|
3239
3378
|
this.taskQueue.push({
|
|
@@ -3260,34 +3399,18 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3260
3399
|
this.feedLine(concat(tp.muted(" Cancelled: "), tp.accent(`@${cancelDisplay}`), tp.muted(" — "), tp.text(removed.task.slice(0, 60))));
|
|
3261
3400
|
this.refreshView();
|
|
3262
3401
|
}
|
|
3263
|
-
/** 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(). */
|
|
3264
3404
|
async drainAgentQueue(agent) {
|
|
3265
3405
|
while (true) {
|
|
3266
|
-
const idx = this.taskQueue.findIndex((e) => e.teammate === agent);
|
|
3406
|
+
const idx = this.taskQueue.findIndex((e) => e.teammate === agent && !this.isSystemTask(e));
|
|
3267
3407
|
if (idx < 0)
|
|
3268
3408
|
break;
|
|
3269
3409
|
const entry = this.taskQueue.splice(idx, 1)[0];
|
|
3270
3410
|
this.agentActive.set(agent, entry);
|
|
3271
3411
|
const startTime = Date.now();
|
|
3272
3412
|
try {
|
|
3273
|
-
|
|
3274
|
-
await this.runCompact(entry.teammate);
|
|
3275
|
-
}
|
|
3276
|
-
else if (entry.type === "summarize") {
|
|
3277
|
-
// Internal housekeeping — summarize older conversation history
|
|
3278
|
-
const result = await this.orchestrator.assign({
|
|
3279
|
-
teammate: entry.teammate,
|
|
3280
|
-
task: entry.task,
|
|
3281
|
-
});
|
|
3282
|
-
// Extract the summary from the agent's output (strip protocol artifacts)
|
|
3283
|
-
const raw = result.rawOutput ?? "";
|
|
3284
|
-
this.conversationSummary = raw
|
|
3285
|
-
.replace(/^TO:\s*\S+\s*\n/im, "")
|
|
3286
|
-
.replace(/^#\s+.+\n*/m, "")
|
|
3287
|
-
.replace(/```json\s*\n\s*\{[\s\S]*?\}\s*\n\s*```\s*$/g, "")
|
|
3288
|
-
.trim();
|
|
3289
|
-
}
|
|
3290
|
-
else {
|
|
3413
|
+
{
|
|
3291
3414
|
// btw and debug tasks skip conversation context (not part of main thread)
|
|
3292
3415
|
const extraContext = entry.type === "btw" || entry.type === "debug"
|
|
3293
3416
|
? ""
|
|
@@ -3398,6 +3521,14 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3398
3521
|
task,
|
|
3399
3522
|
"",
|
|
3400
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
|
+
}
|
|
3401
3532
|
if (error) {
|
|
3402
3533
|
lines.push("## Result");
|
|
3403
3534
|
lines.push("");
|
|
@@ -3454,7 +3585,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3454
3585
|
lines.push("");
|
|
3455
3586
|
writeFileSync(debugFile, lines.join("\n"), "utf-8");
|
|
3456
3587
|
this.lastDebugFiles.set(teammate, debugFile);
|
|
3457
|
-
this.lastTaskPrompts.set(teammate, task);
|
|
3588
|
+
this.lastTaskPrompts.set(teammate, fullPrompt ?? task);
|
|
3458
3589
|
}
|
|
3459
3590
|
catch {
|
|
3460
3591
|
// Don't let debug logging break task execution
|
|
@@ -3628,20 +3759,30 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3628
3759
|
// Start draining
|
|
3629
3760
|
this.kickDrain();
|
|
3630
3761
|
}
|
|
3631
|
-
/**
|
|
3632
|
-
|
|
3762
|
+
/**
|
|
3763
|
+
* Run compaction + recall index update for a single teammate.
|
|
3764
|
+
* When `silent` is true, routine status messages go to the progress bar
|
|
3765
|
+
* only — the feed is reserved for actual work (weeklies/monthlies created).
|
|
3766
|
+
*/
|
|
3767
|
+
async runCompact(name, silent = false) {
|
|
3633
3768
|
const teammateDir = join(this.teammatesDir, name);
|
|
3634
|
-
if (this.chatView) {
|
|
3769
|
+
if (!silent && this.chatView) {
|
|
3635
3770
|
this.chatView.setProgress(`Compacting ${name}...`);
|
|
3636
3771
|
this.refreshView();
|
|
3637
3772
|
}
|
|
3638
3773
|
let spinner = null;
|
|
3639
|
-
if (!this.chatView) {
|
|
3774
|
+
if (!silent && !this.chatView) {
|
|
3640
3775
|
spinner = ora({ text: `Compacting ${name}...`, color: "cyan" }).start();
|
|
3641
3776
|
}
|
|
3642
3777
|
try {
|
|
3778
|
+
// Auto-compact daily logs if they exceed the token budget (creates partial weeklies)
|
|
3779
|
+
const autoResult = await autoCompactForBudget(teammateDir, DAILY_LOG_BUDGET_TOKENS);
|
|
3780
|
+
// Regular episodic compaction (complete weeks → weeklies, old weeklies → monthlies)
|
|
3643
3781
|
const result = await compactEpisodic(teammateDir, name);
|
|
3644
3782
|
const parts = [];
|
|
3783
|
+
if (autoResult) {
|
|
3784
|
+
parts.push(`${autoResult.created.length} auto-compacted (budget overflow)`);
|
|
3785
|
+
}
|
|
3645
3786
|
if (result.weekliesCreated.length > 0) {
|
|
3646
3787
|
parts.push(`${result.weekliesCreated.length} weekly summaries created`);
|
|
3647
3788
|
}
|
|
@@ -3657,25 +3798,27 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3657
3798
|
if (parts.length === 0) {
|
|
3658
3799
|
if (spinner)
|
|
3659
3800
|
spinner.info(`${name}: nothing to compact`);
|
|
3660
|
-
|
|
3801
|
+
// Silent: progress bar only; verbose: feed line
|
|
3802
|
+
if (this.chatView && !silent)
|
|
3661
3803
|
this.feedLine(tp.muted(` ℹ ${name}: nothing to compact`));
|
|
3662
3804
|
}
|
|
3663
3805
|
else {
|
|
3806
|
+
// Actual work done — always show in feed
|
|
3664
3807
|
if (spinner)
|
|
3665
3808
|
spinner.succeed(`${name}: ${parts.join(", ")}`);
|
|
3666
3809
|
if (this.chatView)
|
|
3667
3810
|
this.feedLine(tp.success(` ✔ ${name}: ${parts.join(", ")}`));
|
|
3668
3811
|
}
|
|
3669
|
-
if (this.chatView)
|
|
3812
|
+
if (!silent && this.chatView)
|
|
3670
3813
|
this.chatView.setProgress(null);
|
|
3671
3814
|
// Sync recall index for this teammate (bundled library call)
|
|
3672
3815
|
try {
|
|
3673
|
-
if (this.chatView) {
|
|
3816
|
+
if (!silent && this.chatView) {
|
|
3674
3817
|
this.chatView.setProgress(`Syncing ${name} index...`);
|
|
3675
3818
|
this.refreshView();
|
|
3676
3819
|
}
|
|
3677
3820
|
let syncSpinner = null;
|
|
3678
|
-
if (!this.chatView) {
|
|
3821
|
+
if (!silent && !this.chatView) {
|
|
3679
3822
|
syncSpinner = ora({
|
|
3680
3823
|
text: `Syncing ${name} index...`,
|
|
3681
3824
|
color: "cyan",
|
|
@@ -3685,8 +3828,10 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3685
3828
|
if (syncSpinner)
|
|
3686
3829
|
syncSpinner.succeed(`${name}: index synced`);
|
|
3687
3830
|
if (this.chatView) {
|
|
3688
|
-
|
|
3689
|
-
|
|
3831
|
+
if (!silent)
|
|
3832
|
+
this.chatView.setProgress(null);
|
|
3833
|
+
if (!silent)
|
|
3834
|
+
this.feedLine(tp.success(` ✔ ${name}: index synced`));
|
|
3690
3835
|
}
|
|
3691
3836
|
}
|
|
3692
3837
|
catch {
|
|
@@ -3701,9 +3846,9 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3701
3846
|
type: "agent",
|
|
3702
3847
|
teammate: name,
|
|
3703
3848
|
task: wisdomPrompt,
|
|
3849
|
+
system: true,
|
|
3704
3850
|
});
|
|
3705
|
-
|
|
3706
|
-
this.feedLine(tp.muted(` ↻ ${name}: queued wisdom distillation`));
|
|
3851
|
+
this.kickDrain();
|
|
3707
3852
|
}
|
|
3708
3853
|
}
|
|
3709
3854
|
catch {
|
|
@@ -3715,7 +3860,9 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3715
3860
|
if (spinner)
|
|
3716
3861
|
spinner.fail(`${name}: ${msg}`);
|
|
3717
3862
|
if (this.chatView) {
|
|
3718
|
-
|
|
3863
|
+
if (!silent)
|
|
3864
|
+
this.chatView.setProgress(null);
|
|
3865
|
+
// Errors always show in feed
|
|
3719
3866
|
this.feedLine(tp.error(` ✖ ${name}: ${msg}`));
|
|
3720
3867
|
}
|
|
3721
3868
|
}
|
|
@@ -3750,34 +3897,34 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3750
3897
|
this.refreshView();
|
|
3751
3898
|
return;
|
|
3752
3899
|
}
|
|
3753
|
-
const retroPrompt = `Run a structured self-retrospective. Review your SOUL.md, WISDOM.md, your last 2-3 weekly summaries (or last 7 daily logs if no weeklies exist), and any typed memories in your memory/ folder.
|
|
3754
|
-
|
|
3755
|
-
Produce a response with these four sections:
|
|
3756
|
-
|
|
3757
|
-
## 1. What's Working
|
|
3758
|
-
Things you do well, based on evidence from recent work. Patterns worth reinforcing or codifying into wisdom. Cite specific examples from daily logs or memories.
|
|
3759
|
-
|
|
3760
|
-
## 2. What's Not Working
|
|
3761
|
-
Friction, recurring issues, or patterns that aren't serving the project. Be specific — cite examples from daily logs or memories if possible.
|
|
3762
|
-
|
|
3763
|
-
## 3. Proposed SOUL.md Changes
|
|
3764
|
-
The core output. Each proposal is a **specific edit** to your SOUL.md. Use this exact format for each proposal:
|
|
3765
|
-
|
|
3766
|
-
**Proposal N: <short title>**
|
|
3767
|
-
- **Section:** <which SOUL.md section to change, e.g. Boundaries, Core Principles, Ownership>
|
|
3768
|
-
- **Before:** <the current text to replace, or "(new entry)" if adding>
|
|
3769
|
-
- **After:** <the exact replacement text>
|
|
3770
|
-
- **Why:** <evidence from recent work justifying the change>
|
|
3771
|
-
|
|
3772
|
-
Only propose changes to your own SOUL.md. If a change affects shared files, note that it needs a handoff.
|
|
3773
|
-
|
|
3774
|
-
## 4. Questions for the Team
|
|
3775
|
-
Issues that can't be resolved unilaterally — they need input from other teammates or the user.
|
|
3776
|
-
|
|
3777
|
-
**Rules:**
|
|
3778
|
-
- This is a self-review of YOUR work. Do not evaluate other teammates.
|
|
3779
|
-
- Evidence over opinion — cite specific examples.
|
|
3780
|
-
- No busywork — if everything is working well, say "all good, no changes." That's a valid outcome.
|
|
3900
|
+
const retroPrompt = `Run a structured self-retrospective. Review your SOUL.md, WISDOM.md, your last 2-3 weekly summaries (or last 7 daily logs if no weeklies exist), and any typed memories in your memory/ folder.
|
|
3901
|
+
|
|
3902
|
+
Produce a response with these four sections:
|
|
3903
|
+
|
|
3904
|
+
## 1. What's Working
|
|
3905
|
+
Things you do well, based on evidence from recent work. Patterns worth reinforcing or codifying into wisdom. Cite specific examples from daily logs or memories.
|
|
3906
|
+
|
|
3907
|
+
## 2. What's Not Working
|
|
3908
|
+
Friction, recurring issues, or patterns that aren't serving the project. Be specific — cite examples from daily logs or memories if possible.
|
|
3909
|
+
|
|
3910
|
+
## 3. Proposed SOUL.md Changes
|
|
3911
|
+
The core output. Each proposal is a **specific edit** to your SOUL.md. Use this exact format for each proposal:
|
|
3912
|
+
|
|
3913
|
+
**Proposal N: <short title>**
|
|
3914
|
+
- **Section:** <which SOUL.md section to change, e.g. Boundaries, Core Principles, Ownership>
|
|
3915
|
+
- **Before:** <the current text to replace, or "(new entry)" if adding>
|
|
3916
|
+
- **After:** <the exact replacement text>
|
|
3917
|
+
- **Why:** <evidence from recent work justifying the change>
|
|
3918
|
+
|
|
3919
|
+
Only propose changes to your own SOUL.md. If a change affects shared files, note that it needs a handoff.
|
|
3920
|
+
|
|
3921
|
+
## 4. Questions for the Team
|
|
3922
|
+
Issues that can't be resolved unilaterally — they need input from other teammates or the user.
|
|
3923
|
+
|
|
3924
|
+
**Rules:**
|
|
3925
|
+
- This is a self-review of YOUR work. Do not evaluate other teammates.
|
|
3926
|
+
- Evidence over opinion — cite specific examples.
|
|
3927
|
+
- No busywork — if everything is working well, say "all good, no changes." That's a valid outcome.
|
|
3781
3928
|
- Number each proposal (Proposal 1, Proposal 2, etc.) so the user can approve or reject individually.`;
|
|
3782
3929
|
const label = targets.length > 1
|
|
3783
3930
|
? targets.map((n) => `@${n}`).join(", ")
|
|
@@ -3842,36 +3989,27 @@ Issues that can't be resolved unilaterally — they need input from other teamma
|
|
|
3842
3989
|
.filter((n) => n !== this.selfName && n !== this.adapterName);
|
|
3843
3990
|
if (teammates.length === 0)
|
|
3844
3991
|
return;
|
|
3845
|
-
// 1.
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3992
|
+
// 1. Run compaction for all teammates (auto-compact + episodic + sync + wisdom)
|
|
3993
|
+
// Progress bar shows status; feed only shows lines when actual work is done
|
|
3994
|
+
for (const name of teammates) {
|
|
3995
|
+
await this.runCompact(name, true);
|
|
3996
|
+
}
|
|
3997
|
+
// 2. Purge daily logs older than 30 days (disk + Vectra)
|
|
3998
|
+
const { Indexer } = await import("@teammates/recall");
|
|
3999
|
+
const indexer = new Indexer({ teammatesDir: this.teammatesDir });
|
|
3850
4000
|
for (const name of teammates) {
|
|
3851
|
-
const memoryDir = join(this.teammatesDir, name, "memory");
|
|
3852
4001
|
try {
|
|
3853
|
-
const
|
|
3854
|
-
const
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
return /^\d{4}-\d{2}-\d{2}$/.test(stem) && stem < cutoff;
|
|
3859
|
-
});
|
|
3860
|
-
if (hasStale)
|
|
3861
|
-
needsCompact.push(name);
|
|
4002
|
+
const purged = await purgeStaleDailies(join(this.teammatesDir, name));
|
|
4003
|
+
for (const file of purged) {
|
|
4004
|
+
const uri = `${name}/memory/${file}`;
|
|
4005
|
+
await indexer.deleteDocument(name, uri).catch(() => { });
|
|
4006
|
+
}
|
|
3862
4007
|
}
|
|
3863
4008
|
catch {
|
|
3864
|
-
/*
|
|
3865
|
-
}
|
|
3866
|
-
}
|
|
3867
|
-
if (needsCompact.length > 0) {
|
|
3868
|
-
this.feedLine(concat(tp.muted(" Compacting stale logs for "), tp.accent(needsCompact.map((n) => `@${n}`).join(", ")), tp.muted("...")));
|
|
3869
|
-
this.refreshView();
|
|
3870
|
-
for (const name of needsCompact) {
|
|
3871
|
-
await this.runCompact(name);
|
|
4009
|
+
/* purge failed — non-fatal */
|
|
3872
4010
|
}
|
|
3873
4011
|
}
|
|
3874
|
-
//
|
|
4012
|
+
// 3. Sync recall indexes (bundled library call)
|
|
3875
4013
|
try {
|
|
3876
4014
|
await syncRecallIndex(this.teammatesDir);
|
|
3877
4015
|
}
|