@teammates/cli 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -4
- package/dist/adapter.d.ts +11 -3
- package/dist/adapter.js +44 -11
- package/dist/adapters/cli-proxy.d.ts +3 -1
- package/dist/adapters/cli-proxy.js +7 -4
- package/dist/adapters/copilot.d.ts +3 -1
- package/dist/adapters/copilot.js +5 -2
- package/dist/adapters/echo.d.ts +3 -1
- package/dist/adapters/echo.js +2 -2
- package/dist/cli.js +365 -132
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/orchestrator.js +3 -1
- package/dist/personas.d.ts +42 -0
- package/dist/personas.js +108 -0
- package/dist/types.d.ts +2 -0
- package/package.json +4 -3
- package/personas/architect.md +91 -0
- package/personas/backend.md +93 -0
- package/personas/data-engineer.md +92 -0
- package/personas/designer.md +92 -0
- package/personas/devops.md +93 -0
- package/personas/frontend.md +94 -0
- package/personas/ml-ai.md +96 -0
- package/personas/mobile.md +93 -0
- package/personas/performance.md +92 -0
- package/personas/pm.md +89 -0
- package/personas/qa.md +92 -0
- package/personas/security.md +92 -0
- package/personas/sre.md +93 -0
- package/personas/swe.md +88 -0
- package/personas/tech-writer.md +93 -0
package/dist/cli.js
CHANGED
|
@@ -23,6 +23,7 @@ import { buildWisdomPrompt, compactEpisodic } 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";
|
|
26
27
|
import { Orchestrator } from "./orchestrator.js";
|
|
27
28
|
import { colorToHex, theme, tp } from "./theme.js";
|
|
28
29
|
// ─── Parsed CLI arguments ────────────────────────────────────────────
|
|
@@ -48,6 +49,108 @@ class TeammatesREPL {
|
|
|
48
49
|
text: result.summary,
|
|
49
50
|
});
|
|
50
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Render a task result to the feed. Called from drainAgentQueue() AFTER
|
|
54
|
+
* the defensive retry so the user sees the final (possibly retried) output.
|
|
55
|
+
*/
|
|
56
|
+
displayTaskResult(result, entryType) {
|
|
57
|
+
// Suppress display for internal summarization tasks
|
|
58
|
+
if (entryType === "summarize")
|
|
59
|
+
return;
|
|
60
|
+
if (!this.chatView)
|
|
61
|
+
this.input.deactivateAndErase();
|
|
62
|
+
const raw = result.rawOutput ?? "";
|
|
63
|
+
// Strip protocol artifacts
|
|
64
|
+
const cleaned = raw
|
|
65
|
+
.replace(/^TO:\s*\S+\s*\n/im, "")
|
|
66
|
+
.replace(/^#\s+.+\n*/m, "")
|
|
67
|
+
.replace(/```handoff\s*\n@\w+\s*\n[\s\S]*?```/g, "")
|
|
68
|
+
.replace(/```json\s*\n\s*\{[\s\S]*?\}\s*\n\s*```\s*$/g, "")
|
|
69
|
+
.trim();
|
|
70
|
+
// Header: "teammate: subject"
|
|
71
|
+
const subject = result.summary || "Task completed";
|
|
72
|
+
const displayTeammate = result.teammate === this.selfName
|
|
73
|
+
? this.adapterName
|
|
74
|
+
: result.teammate;
|
|
75
|
+
this.feedLine(concat(tp.accent(`${displayTeammate}: `), tp.text(subject)));
|
|
76
|
+
this.lastCleanedOutput = cleaned;
|
|
77
|
+
if (cleaned) {
|
|
78
|
+
this.feedMarkdown(cleaned);
|
|
79
|
+
}
|
|
80
|
+
else if (result.changedFiles.length > 0 || result.summary) {
|
|
81
|
+
// Agent produced no body text but DID do work — generate a synthetic
|
|
82
|
+
// summary from available metadata so the user sees something useful.
|
|
83
|
+
const syntheticLines = [];
|
|
84
|
+
if (result.summary) {
|
|
85
|
+
syntheticLines.push(result.summary);
|
|
86
|
+
}
|
|
87
|
+
if (result.changedFiles.length > 0) {
|
|
88
|
+
syntheticLines.push("");
|
|
89
|
+
syntheticLines.push("**Files changed:**");
|
|
90
|
+
for (const f of result.changedFiles) {
|
|
91
|
+
syntheticLines.push(`- ${f}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
this.feedMarkdown(syntheticLines.join("\n"));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
this.feedLine(tp.muted(" (no response text — the agent may have only performed tool actions)"));
|
|
98
|
+
this.feedLine(tp.muted(` Use /debug ${result.teammate} to view full output`));
|
|
99
|
+
// Show diagnostic hints for empty responses
|
|
100
|
+
const diag = result.diagnostics;
|
|
101
|
+
if (diag) {
|
|
102
|
+
if (diag.exitCode !== 0 && diag.exitCode !== null) {
|
|
103
|
+
this.feedLine(tp.warning(` ⚠ Process exited with code ${diag.exitCode}`));
|
|
104
|
+
}
|
|
105
|
+
if (diag.signal) {
|
|
106
|
+
this.feedLine(tp.warning(` ⚠ Process killed by signal: ${diag.signal}`));
|
|
107
|
+
}
|
|
108
|
+
if (diag.debugFile) {
|
|
109
|
+
this.feedLine(tp.muted(` Debug log: ${diag.debugFile}`));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Render handoffs
|
|
114
|
+
const handoffs = result.handoffs;
|
|
115
|
+
if (handoffs.length > 0) {
|
|
116
|
+
this.renderHandoffs(result.teammate, handoffs);
|
|
117
|
+
}
|
|
118
|
+
// Clickable [reply] [copy] actions after the response
|
|
119
|
+
if (this.chatView && cleaned) {
|
|
120
|
+
const t = theme();
|
|
121
|
+
const teammate = result.teammate;
|
|
122
|
+
const replyId = `reply-${teammate}-${Date.now()}`;
|
|
123
|
+
this._replyContexts.set(replyId, { teammate, message: cleaned });
|
|
124
|
+
this.chatView.appendActionList([
|
|
125
|
+
{
|
|
126
|
+
id: replyId,
|
|
127
|
+
normalStyle: this.makeSpan({
|
|
128
|
+
text: " [reply]",
|
|
129
|
+
style: { fg: t.textDim },
|
|
130
|
+
}),
|
|
131
|
+
hoverStyle: this.makeSpan({
|
|
132
|
+
text: " [reply]",
|
|
133
|
+
style: { fg: t.accent },
|
|
134
|
+
}),
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: "copy",
|
|
138
|
+
normalStyle: this.makeSpan({
|
|
139
|
+
text: " [copy]",
|
|
140
|
+
style: { fg: t.textDim },
|
|
141
|
+
}),
|
|
142
|
+
hoverStyle: this.makeSpan({
|
|
143
|
+
text: " [copy]",
|
|
144
|
+
style: { fg: t.accent },
|
|
145
|
+
}),
|
|
146
|
+
},
|
|
147
|
+
]);
|
|
148
|
+
}
|
|
149
|
+
this.feedLine();
|
|
150
|
+
// Auto-detect new teammates added during this task
|
|
151
|
+
this.refreshTeammates();
|
|
152
|
+
this.showPrompt();
|
|
153
|
+
}
|
|
51
154
|
/** Token budget for recent conversation history (24k tokens ≈ 96k chars). */
|
|
52
155
|
static CONV_HISTORY_CHARS = 24_000 * 4;
|
|
53
156
|
buildConversationContext() {
|
|
@@ -116,6 +219,8 @@ class TeammatesREPL {
|
|
|
116
219
|
taskQueue = [];
|
|
117
220
|
/** Per-agent active tasks — one per agent running in parallel. */
|
|
118
221
|
agentActive = new Map();
|
|
222
|
+
/** Agents currently in a silent retry — suppress all events. */
|
|
223
|
+
silentAgents = new Set();
|
|
119
224
|
/** Per-agent drain locks — prevents double-draining a single agent. */
|
|
120
225
|
agentDrainLocks = new Map();
|
|
121
226
|
/** Stored pasted text keyed by paste number, expanded on Enter. */
|
|
@@ -235,7 +340,7 @@ class TeammatesREPL {
|
|
|
235
340
|
const entries = Array.from(this.activeTasks.values());
|
|
236
341
|
const idx = this.statusRotateIndex % entries.length;
|
|
237
342
|
const { teammate, task } = entries[idx];
|
|
238
|
-
const displayName = teammate === this.
|
|
343
|
+
const displayName = teammate === this.selfName ? this.adapterName : teammate;
|
|
239
344
|
const spinChar = TeammatesREPL.SPINNER[this.statusFrame % TeammatesREPL.SPINNER.length];
|
|
240
345
|
const taskPreview = task.length > 50 ? `${task.slice(0, 47)}...` : task;
|
|
241
346
|
const queueInfo = this.activeTasks.size > 1 ? ` (${idx + 1}/${this.activeTasks.size})` : "";
|
|
@@ -891,7 +996,8 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
891
996
|
let m;
|
|
892
997
|
mentioned = [];
|
|
893
998
|
while ((m = mentionRegex.exec(input)) !== null) {
|
|
894
|
-
|
|
999
|
+
// Remap adapter name alias → user avatar for routing
|
|
1000
|
+
const name = (m[1] === this.adapterName && this.userAlias) ? this.selfName : m[1];
|
|
895
1001
|
if (allNames.includes(name) && !mentioned.includes(name)) {
|
|
896
1002
|
mentioned.push(name);
|
|
897
1003
|
}
|
|
@@ -921,7 +1027,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
921
1027
|
{
|
|
922
1028
|
const bg = this._userBg;
|
|
923
1029
|
const t = theme();
|
|
924
|
-
const displayName = match === this.
|
|
1030
|
+
const displayName = match === this.selfName ? this.adapterName : match;
|
|
925
1031
|
this.feedUserLine(concat(pen.fg(t.textMuted).bg(bg)(" → "), pen.fg(t.accent).bg(bg)(`@${displayName}`)));
|
|
926
1032
|
}
|
|
927
1033
|
this.feedLine();
|
|
@@ -959,42 +1065,173 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
959
1065
|
console.log(chalk.white(" Set up teammates for this project?\n"));
|
|
960
1066
|
console.log(chalk.cyan(" 1") +
|
|
961
1067
|
chalk.gray(") ") +
|
|
962
|
-
chalk.white("
|
|
963
|
-
chalk.gray(" —
|
|
1068
|
+
chalk.white("Pick teammates") +
|
|
1069
|
+
chalk.gray(" — choose from persona templates"));
|
|
964
1070
|
console.log(chalk.cyan(" 2") +
|
|
1071
|
+
chalk.gray(") ") +
|
|
1072
|
+
chalk.white("Auto-generate") +
|
|
1073
|
+
chalk.gray(" — let your agent analyze the codebase and create teammates"));
|
|
1074
|
+
console.log(chalk.cyan(" 3") +
|
|
965
1075
|
chalk.gray(") ") +
|
|
966
1076
|
chalk.white("Import team") +
|
|
967
1077
|
chalk.gray(" — copy teammates from another project"));
|
|
968
|
-
console.log(chalk.cyan("
|
|
1078
|
+
console.log(chalk.cyan(" 4") +
|
|
969
1079
|
chalk.gray(") ") +
|
|
970
1080
|
chalk.white("Solo mode") +
|
|
971
1081
|
chalk.gray(" — use your agent without teammates"));
|
|
972
|
-
console.log(chalk.cyan("
|
|
1082
|
+
console.log(chalk.cyan(" 5") + chalk.gray(") ") + chalk.white("Exit"));
|
|
973
1083
|
console.log();
|
|
974
|
-
const choice = await this.askChoice("Pick an option (1/2/3/4): ", [
|
|
1084
|
+
const choice = await this.askChoice("Pick an option (1/2/3/4/5): ", [
|
|
975
1085
|
"1",
|
|
976
1086
|
"2",
|
|
977
1087
|
"3",
|
|
978
1088
|
"4",
|
|
1089
|
+
"5",
|
|
979
1090
|
]);
|
|
980
|
-
if (choice === "
|
|
1091
|
+
if (choice === "5") {
|
|
981
1092
|
console.log(chalk.gray(" Goodbye."));
|
|
982
1093
|
return false;
|
|
983
1094
|
}
|
|
984
|
-
if (choice === "
|
|
1095
|
+
if (choice === "4") {
|
|
985
1096
|
console.log(chalk.gray(" Running in solo mode — all tasks go to your agent."));
|
|
986
1097
|
console.log(chalk.gray(" Run /init later to set up teammates."));
|
|
987
1098
|
console.log();
|
|
988
1099
|
return true;
|
|
989
1100
|
}
|
|
990
|
-
if (choice === "
|
|
1101
|
+
if (choice === "3") {
|
|
991
1102
|
await this.runImport(cwd);
|
|
992
1103
|
return true;
|
|
993
1104
|
}
|
|
994
|
-
|
|
995
|
-
|
|
1105
|
+
if (choice === "2") {
|
|
1106
|
+
// Auto-generate via agent
|
|
1107
|
+
await this.runOnboardingAgent(adapter, cwd);
|
|
1108
|
+
return true;
|
|
1109
|
+
}
|
|
1110
|
+
// choice === "1": Pick from persona templates
|
|
1111
|
+
await this.runPersonaOnboarding(teammatesDir);
|
|
996
1112
|
return true;
|
|
997
1113
|
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Persona-based onboarding: show a list of bundled personas, let the user
|
|
1116
|
+
* pick which ones to create, optionally rename them, and scaffold the folders.
|
|
1117
|
+
*/
|
|
1118
|
+
async runPersonaOnboarding(teammatesDir) {
|
|
1119
|
+
const personas = await loadPersonas();
|
|
1120
|
+
if (personas.length === 0) {
|
|
1121
|
+
console.log(chalk.yellow(" No persona templates found."));
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
console.log();
|
|
1125
|
+
console.log(chalk.white(" Available personas:\n"));
|
|
1126
|
+
// Display personas grouped by tier
|
|
1127
|
+
let currentTier = 0;
|
|
1128
|
+
for (let i = 0; i < personas.length; i++) {
|
|
1129
|
+
const p = personas[i];
|
|
1130
|
+
if (p.tier !== currentTier) {
|
|
1131
|
+
currentTier = p.tier;
|
|
1132
|
+
const label = currentTier === 1 ? "Core" : "Specialized";
|
|
1133
|
+
console.log(chalk.gray(` ── ${label} ──`));
|
|
1134
|
+
}
|
|
1135
|
+
const num = String(i + 1).padStart(2, " ");
|
|
1136
|
+
console.log(chalk.cyan(` ${num}`) +
|
|
1137
|
+
chalk.gray(") ") +
|
|
1138
|
+
chalk.white(p.persona) +
|
|
1139
|
+
chalk.gray(` (${p.alias})`) +
|
|
1140
|
+
chalk.gray(` — ${p.description}`));
|
|
1141
|
+
}
|
|
1142
|
+
console.log();
|
|
1143
|
+
console.log(chalk.gray(" Enter numbers separated by commas, e.g. 1,3,5"));
|
|
1144
|
+
console.log();
|
|
1145
|
+
const input = await this.askInput("Personas: ");
|
|
1146
|
+
if (!input) {
|
|
1147
|
+
console.log(chalk.gray(" No personas selected."));
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
// Parse comma-separated numbers
|
|
1151
|
+
const indices = input
|
|
1152
|
+
.split(",")
|
|
1153
|
+
.map((s) => parseInt(s.trim(), 10) - 1)
|
|
1154
|
+
.filter((i) => i >= 0 && i < personas.length);
|
|
1155
|
+
const unique = [...new Set(indices)];
|
|
1156
|
+
if (unique.length === 0) {
|
|
1157
|
+
console.log(chalk.yellow(" No valid selections."));
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
console.log();
|
|
1161
|
+
// Copy framework files first
|
|
1162
|
+
await copyTemplateFiles(teammatesDir);
|
|
1163
|
+
const created = [];
|
|
1164
|
+
for (const idx of unique) {
|
|
1165
|
+
const p = personas[idx];
|
|
1166
|
+
const nameInput = await this.askInput(`Name for ${p.persona} [${p.alias}]: `);
|
|
1167
|
+
const name = nameInput || p.alias;
|
|
1168
|
+
const folderName = name.toLowerCase().replace(/[^a-z0-9_-]/g, "");
|
|
1169
|
+
await scaffoldFromPersona(teammatesDir, folderName, p);
|
|
1170
|
+
created.push(folderName);
|
|
1171
|
+
console.log(chalk.green(" ✔ ") + chalk.white(`@${folderName}`) + chalk.gray(` — ${p.persona}`));
|
|
1172
|
+
}
|
|
1173
|
+
console.log();
|
|
1174
|
+
console.log(chalk.green(` ✔ Created ${created.length} teammate${created.length > 1 ? "s" : ""}: `) +
|
|
1175
|
+
chalk.white(created.map((n) => `@${n}`).join(", ")));
|
|
1176
|
+
console.log(chalk.gray(" Tip: Your agent will adapt ownership and capabilities to this codebase on first task."));
|
|
1177
|
+
console.log();
|
|
1178
|
+
}
|
|
1179
|
+
/**
|
|
1180
|
+
* In-TUI persona picker for /init pick. Uses feedLine + askInline instead
|
|
1181
|
+
* of console.log + askInput.
|
|
1182
|
+
*/
|
|
1183
|
+
async runPersonaOnboardingInline(teammatesDir) {
|
|
1184
|
+
const personas = await loadPersonas();
|
|
1185
|
+
if (personas.length === 0) {
|
|
1186
|
+
this.feedLine(tp.warning(" No persona templates found."));
|
|
1187
|
+
this.refreshView();
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
// Display personas in the feed
|
|
1191
|
+
this.feedLine(tp.text(" Available personas:\n"));
|
|
1192
|
+
let currentTier = 0;
|
|
1193
|
+
for (let i = 0; i < personas.length; i++) {
|
|
1194
|
+
const p = personas[i];
|
|
1195
|
+
if (p.tier !== currentTier) {
|
|
1196
|
+
currentTier = p.tier;
|
|
1197
|
+
const label = currentTier === 1 ? "Core" : "Specialized";
|
|
1198
|
+
this.feedLine(tp.muted(` ── ${label} ──`));
|
|
1199
|
+
}
|
|
1200
|
+
const num = String(i + 1).padStart(2, " ");
|
|
1201
|
+
this.feedLine(concat(tp.text(` ${num}) ${p.persona} `), tp.muted(`(${p.alias}) — ${p.description}`)));
|
|
1202
|
+
}
|
|
1203
|
+
this.feedLine(tp.muted("\n Enter numbers separated by commas, e.g. 1,3,5"));
|
|
1204
|
+
this.refreshView();
|
|
1205
|
+
const input = await this.askInline("Personas: ");
|
|
1206
|
+
if (!input) {
|
|
1207
|
+
this.feedLine(tp.muted(" No personas selected."));
|
|
1208
|
+
this.refreshView();
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
const indices = input
|
|
1212
|
+
.split(",")
|
|
1213
|
+
.map((s) => parseInt(s.trim(), 10) - 1)
|
|
1214
|
+
.filter((i) => i >= 0 && i < personas.length);
|
|
1215
|
+
const unique = [...new Set(indices)];
|
|
1216
|
+
if (unique.length === 0) {
|
|
1217
|
+
this.feedLine(tp.warning(" No valid selections."));
|
|
1218
|
+
this.refreshView();
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
await copyTemplateFiles(teammatesDir);
|
|
1222
|
+
const created = [];
|
|
1223
|
+
for (const idx of unique) {
|
|
1224
|
+
const p = personas[idx];
|
|
1225
|
+
const nameInput = await this.askInline(`Name for ${p.persona} [${p.alias}]: `);
|
|
1226
|
+
const name = nameInput || p.alias;
|
|
1227
|
+
const folderName = name.toLowerCase().replace(/[^a-z0-9_-]/g, "");
|
|
1228
|
+
await scaffoldFromPersona(teammatesDir, folderName, p);
|
|
1229
|
+
created.push(folderName);
|
|
1230
|
+
this.feedLine(concat(tp.success(` ✔ @${folderName}`), tp.muted(` — ${p.persona}`)));
|
|
1231
|
+
}
|
|
1232
|
+
this.feedLine(concat(tp.success(`\n ✔ Created ${created.length} teammate${created.length > 1 ? "s" : ""}: `), tp.text(created.map((n) => `@${n}`).join(", "))));
|
|
1233
|
+
this.refreshView();
|
|
1234
|
+
}
|
|
998
1235
|
/**
|
|
999
1236
|
* Run the onboarding agent to analyze the codebase and create teammates.
|
|
1000
1237
|
* Used by both promptOnboarding (pre-orchestrator) and cmdInit (post-orchestrator).
|
|
@@ -1404,15 +1641,20 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1404
1641
|
chalk.cyan(`@${login}`) +
|
|
1405
1642
|
(name && name !== login ? chalk.gray(` (${name})`) : ""));
|
|
1406
1643
|
console.log();
|
|
1407
|
-
// Ask for
|
|
1644
|
+
// Ask for remaining fields since GitHub doesn't provide them
|
|
1408
1645
|
const role = await this.askInput("Your role (optional, press Enter to skip): ");
|
|
1646
|
+
const experience = await this.askInput("Relevant experience (e.g., 10 years Go, new to React): ");
|
|
1647
|
+
const preferences = await this.askInput("How you like to work (e.g., terse responses): ");
|
|
1648
|
+
// Auto-detect timezone
|
|
1649
|
+
const detectedTz = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
1650
|
+
const timezone = await this.askInput(`Primary timezone${detectedTz ? ` [${detectedTz}]` : ""}: `);
|
|
1409
1651
|
const answers = {
|
|
1410
1652
|
alias: login,
|
|
1411
1653
|
name: name || login,
|
|
1412
1654
|
role: role || "",
|
|
1413
|
-
experience: "",
|
|
1414
|
-
preferences: "",
|
|
1415
|
-
|
|
1655
|
+
experience: experience || "",
|
|
1656
|
+
preferences: preferences || "",
|
|
1657
|
+
timezone: timezone || detectedTz || "",
|
|
1416
1658
|
};
|
|
1417
1659
|
this.writeUserProfile(teammatesDir, login, answers);
|
|
1418
1660
|
this.createUserAvatar(teammatesDir, login, answers);
|
|
@@ -1436,14 +1678,16 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1436
1678
|
const role = await this.askInput("Your role (e.g., senior backend engineer): ");
|
|
1437
1679
|
const experience = await this.askInput("Relevant experience (e.g., 10 years Go, new to React): ");
|
|
1438
1680
|
const preferences = await this.askInput("How you like to work (e.g., terse responses): ");
|
|
1439
|
-
|
|
1681
|
+
// Auto-detect timezone
|
|
1682
|
+
const detectedTz = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
1683
|
+
const timezone = await this.askInput(`Primary timezone${detectedTz ? ` [${detectedTz}]` : ""}: `);
|
|
1440
1684
|
const answers = {
|
|
1441
1685
|
alias,
|
|
1442
1686
|
name,
|
|
1443
1687
|
role,
|
|
1444
1688
|
experience,
|
|
1445
1689
|
preferences,
|
|
1446
|
-
|
|
1690
|
+
timezone: timezone || detectedTz || "",
|
|
1447
1691
|
};
|
|
1448
1692
|
this.writeUserProfile(teammatesDir, alias, answers);
|
|
1449
1693
|
this.createUserAvatar(teammatesDir, alias, answers);
|
|
@@ -1464,7 +1708,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1464
1708
|
lines.push(`- **Role:** ${answers.role || "_not provided_"}`);
|
|
1465
1709
|
lines.push(`- **Experience:** ${answers.experience || "_not provided_"}`);
|
|
1466
1710
|
lines.push(`- **Preferences:** ${answers.preferences || "_not provided_"}`);
|
|
1467
|
-
lines.push(`- **
|
|
1711
|
+
lines.push(`- **Primary Timezone:** ${answers.timezone || "_not provided_"}`);
|
|
1468
1712
|
writeFileSync(userMdPath, `${lines.join("\n")}\n`, "utf-8");
|
|
1469
1713
|
}
|
|
1470
1714
|
/**
|
|
@@ -1477,10 +1721,10 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1477
1721
|
mkdirSync(avatarDir, { recursive: true });
|
|
1478
1722
|
mkdirSync(memoryDir, { recursive: true });
|
|
1479
1723
|
const name = answers.name || alias;
|
|
1480
|
-
const role = answers.role || "
|
|
1724
|
+
const role = answers.role || "I'm a human working on this project";
|
|
1481
1725
|
const experience = answers.experience || "";
|
|
1482
1726
|
const preferences = answers.preferences || "";
|
|
1483
|
-
const
|
|
1727
|
+
const timezone = answers.timezone || "";
|
|
1484
1728
|
// Write SOUL.md
|
|
1485
1729
|
const soulLines = [
|
|
1486
1730
|
`# ${name}`,
|
|
@@ -1495,9 +1739,8 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1495
1739
|
soulLines.push(`**Experience:** ${experience}`);
|
|
1496
1740
|
if (preferences)
|
|
1497
1741
|
soulLines.push(`**Preferences:** ${preferences}`);
|
|
1498
|
-
if (
|
|
1499
|
-
soulLines.push(
|
|
1500
|
-
}
|
|
1742
|
+
if (timezone)
|
|
1743
|
+
soulLines.push(`**Primary Timezone:** ${timezone}`);
|
|
1501
1744
|
soulLines.push("");
|
|
1502
1745
|
const soulPath = join(avatarDir, "SOUL.md");
|
|
1503
1746
|
writeFileSync(soulPath, soulLines.join("\n"), "utf-8");
|
|
@@ -1531,7 +1774,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1531
1774
|
const avatarDir = join(teammatesDir, alias);
|
|
1532
1775
|
// Read the avatar's SOUL.md if it exists
|
|
1533
1776
|
let soul = "";
|
|
1534
|
-
let role = "
|
|
1777
|
+
let role = "I'm a human working on this project";
|
|
1535
1778
|
try {
|
|
1536
1779
|
soul = readFileSync(join(avatarDir, "SOUL.md"), "utf-8");
|
|
1537
1780
|
const roleMatch = soul.match(/\*\*Role:\*\*\s*(.+)/);
|
|
@@ -1744,12 +1987,14 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1744
1987
|
});
|
|
1745
1988
|
}
|
|
1746
1989
|
for (const name of teammates) {
|
|
1747
|
-
|
|
1990
|
+
// For user avatar, display and match using the adapter name alias
|
|
1991
|
+
const display = name === this.userAlias ? this.adapterName : name;
|
|
1992
|
+
if (display.toLowerCase().startsWith(lower)) {
|
|
1748
1993
|
const t = this.orchestrator.getRegistry().get(name);
|
|
1749
1994
|
items.push({
|
|
1750
|
-
label: `@${
|
|
1995
|
+
label: `@${display}`,
|
|
1751
1996
|
description: t?.role ?? "",
|
|
1752
|
-
completion: `${before}@${
|
|
1997
|
+
completion: `${before}@${display} ${after.replace(/^\s+/, "")}`,
|
|
1753
1998
|
});
|
|
1754
1999
|
}
|
|
1755
2000
|
}
|
|
@@ -1930,7 +2175,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1930
2175
|
registry.register({
|
|
1931
2176
|
name: this.adapterName,
|
|
1932
2177
|
type: "ai",
|
|
1933
|
-
role: "
|
|
2178
|
+
role: "Coding agent that performs tasks on your behalf.",
|
|
1934
2179
|
soul: "",
|
|
1935
2180
|
wisdom: "",
|
|
1936
2181
|
dailyLogs: [],
|
|
@@ -1965,8 +2210,8 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1965
2210
|
borderStyle: (s) => chalk.gray(s),
|
|
1966
2211
|
colorize: (value) => {
|
|
1967
2212
|
const validNames = new Set([
|
|
1968
|
-
...this.orchestrator.listTeammates().filter((n) => n !== this.adapterName),
|
|
1969
|
-
this.
|
|
2213
|
+
...this.orchestrator.listTeammates().filter((n) => n !== this.adapterName && n !== this.userAlias),
|
|
2214
|
+
this.adapterName,
|
|
1970
2215
|
"everyone",
|
|
1971
2216
|
]);
|
|
1972
2217
|
return value
|
|
@@ -2011,13 +2256,19 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2011
2256
|
const reg = this.orchestrator.getRegistry();
|
|
2012
2257
|
const statuses = this.orchestrator.getAllStatuses();
|
|
2013
2258
|
const bannerTeammates = [];
|
|
2259
|
+
// Add user avatar first (displayed as adapter name alias)
|
|
2260
|
+
if (this.userAlias) {
|
|
2261
|
+
const ut = reg.get(this.userAlias);
|
|
2262
|
+
const up = statuses.get(this.userAlias)?.presence ?? "online";
|
|
2263
|
+
bannerTeammates.push({ name: this.adapterName, role: "Coding agent that performs tasks on your behalf.", presence: up });
|
|
2264
|
+
}
|
|
2014
2265
|
for (const name of names) {
|
|
2015
2266
|
const t = reg.get(name);
|
|
2016
2267
|
const p = statuses.get(name)?.presence ?? "online";
|
|
2017
2268
|
bannerTeammates.push({ name, role: t?.role ?? "", presence: p });
|
|
2018
2269
|
}
|
|
2019
2270
|
const bannerWidget = new AnimatedBanner({
|
|
2020
|
-
displayName: `@${this.
|
|
2271
|
+
displayName: `@${this.adapterName}`,
|
|
2021
2272
|
teammateCount: names.length,
|
|
2022
2273
|
cwd: process.cwd(),
|
|
2023
2274
|
teammates: bannerTeammates,
|
|
@@ -2048,8 +2299,8 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2048
2299
|
}
|
|
2049
2300
|
// Colorize @mentions only if they reference a valid teammate or the user
|
|
2050
2301
|
const validNames = new Set([
|
|
2051
|
-
...this.orchestrator.listTeammates().filter((n) => n !== this.adapterName),
|
|
2052
|
-
this.
|
|
2302
|
+
...this.orchestrator.listTeammates().filter((n) => n !== this.adapterName && n !== this.userAlias),
|
|
2303
|
+
this.adapterName,
|
|
2053
2304
|
"everyone",
|
|
2054
2305
|
]);
|
|
2055
2306
|
const mentionPattern = /@(\w+)/g;
|
|
@@ -2339,7 +2590,8 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2339
2590
|
let pm;
|
|
2340
2591
|
const preMentions = [];
|
|
2341
2592
|
while ((pm = preMentionRegex.exec(rawLine)) !== null) {
|
|
2342
|
-
|
|
2593
|
+
// Remap adapter name alias → user avatar for routing
|
|
2594
|
+
const name = (pm[1] === this.adapterName && this.userAlias) ? this.selfName : pm[1];
|
|
2343
2595
|
if (allNames.includes(name) && !preMentions.includes(name)) {
|
|
2344
2596
|
preMentions.push(name);
|
|
2345
2597
|
}
|
|
@@ -2436,7 +2688,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2436
2688
|
const termWidth = process.stdout.columns || 100;
|
|
2437
2689
|
this.feedLine();
|
|
2438
2690
|
this.feedLine(concat(tp.bold(" Teammates"), tp.muted(` v${PKG_VERSION}`)));
|
|
2439
|
-
this.feedLine(concat(tp.text(` @${this.
|
|
2691
|
+
this.feedLine(concat(tp.text(` @${this.adapterName}`), tp.muted(` · ${teammates.length} teammate${teammates.length === 1 ? "" : "s"}`)));
|
|
2440
2692
|
this.feedLine(` ${process.cwd()}`);
|
|
2441
2693
|
// Service status rows
|
|
2442
2694
|
for (const svc of this.serviceStatuses) {
|
|
@@ -2455,6 +2707,12 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2455
2707
|
// Roster (with presence indicators)
|
|
2456
2708
|
this.feedLine();
|
|
2457
2709
|
const statuses = this.orchestrator.getAllStatuses();
|
|
2710
|
+
// Show user avatar first (displayed as adapter name alias)
|
|
2711
|
+
if (this.userAlias) {
|
|
2712
|
+
const up = statuses.get(this.userAlias)?.presence ?? "online";
|
|
2713
|
+
const udot = up === "online" ? tp.success("●") : up === "reachable" ? tp.warning("●") : tp.error("●");
|
|
2714
|
+
this.feedLine(concat(tp.text(" "), udot, tp.accent(` @${this.adapterName.padEnd(14)}`), tp.muted("Coding agent that performs tasks on your behalf.")));
|
|
2715
|
+
}
|
|
2458
2716
|
for (const name of teammates) {
|
|
2459
2717
|
const t = registry.get(name);
|
|
2460
2718
|
if (t) {
|
|
@@ -2705,8 +2963,8 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2705
2963
|
{
|
|
2706
2964
|
name: "init",
|
|
2707
2965
|
aliases: ["onboard", "setup"],
|
|
2708
|
-
usage: "/init [from-path]",
|
|
2709
|
-
description: "Set up teammates (or import from another project)",
|
|
2966
|
+
usage: "/init [pick | from-path]",
|
|
2967
|
+
description: "Set up teammates (pick from personas, or import from another project)",
|
|
2710
2968
|
run: (args) => this.cmdInit(args),
|
|
2711
2969
|
},
|
|
2712
2970
|
{
|
|
@@ -2802,6 +3060,14 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2802
3060
|
}
|
|
2803
3061
|
// ─── Event handler ───────────────────────────────────────────────
|
|
2804
3062
|
handleEvent(event) {
|
|
3063
|
+
// Suppress all events for agents in silent retry
|
|
3064
|
+
const evtAgent = event.type === "task_assigned"
|
|
3065
|
+
? event.assignment.teammate
|
|
3066
|
+
: event.type === "task_completed"
|
|
3067
|
+
? event.result.teammate
|
|
3068
|
+
: event.teammate;
|
|
3069
|
+
if (this.silentAgents.has(evtAgent))
|
|
3070
|
+
return;
|
|
2805
3071
|
switch (event.type) {
|
|
2806
3072
|
case "task_assigned": {
|
|
2807
3073
|
// Track this task and start the animated status bar
|
|
@@ -2814,92 +3080,14 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2814
3080
|
break;
|
|
2815
3081
|
}
|
|
2816
3082
|
case "task_completed": {
|
|
2817
|
-
// Remove from active tasks
|
|
3083
|
+
// Remove from active tasks and stop spinner.
|
|
3084
|
+
// Result display is deferred to drainAgentQueue() so the defensive
|
|
3085
|
+
// retry can update rawOutput before anything is shown to the user.
|
|
2818
3086
|
this.activeTasks.delete(event.result.teammate);
|
|
2819
3087
|
// Stop animation if no more active tasks
|
|
2820
3088
|
if (this.activeTasks.size === 0) {
|
|
2821
3089
|
this.stopStatusAnimation();
|
|
2822
3090
|
}
|
|
2823
|
-
// Suppress display for internal summarization tasks
|
|
2824
|
-
const activeEntry = this.agentActive.get(event.result.teammate);
|
|
2825
|
-
if (activeEntry?.type === "summarize")
|
|
2826
|
-
break;
|
|
2827
|
-
if (!this.chatView)
|
|
2828
|
-
this.input.deactivateAndErase();
|
|
2829
|
-
const raw = event.result.rawOutput ?? "";
|
|
2830
|
-
// Strip protocol artifacts
|
|
2831
|
-
const cleaned = raw
|
|
2832
|
-
.replace(/^TO:\s*\S+\s*\n/im, "")
|
|
2833
|
-
.replace(/^#\s+.+\n*/m, "")
|
|
2834
|
-
.replace(/```handoff\s*\n@\w+\s*\n[\s\S]*?```/g, "")
|
|
2835
|
-
.replace(/```json\s*\n\s*\{[\s\S]*?\}\s*\n\s*```\s*$/g, "")
|
|
2836
|
-
.trim();
|
|
2837
|
-
const sizeKB = cleaned ? Buffer.byteLength(cleaned, "utf-8") / 1024 : 0;
|
|
2838
|
-
// Header: "teammate: subject"
|
|
2839
|
-
const subject = event.result.summary || "Task completed";
|
|
2840
|
-
const displayTeammate = event.result.teammate === this.adapterName ? this.selfName : event.result.teammate;
|
|
2841
|
-
this.feedLine(concat(tp.accent(`${displayTeammate}: `), tp.text(subject)));
|
|
2842
|
-
this.lastCleanedOutput = cleaned;
|
|
2843
|
-
if (cleaned) {
|
|
2844
|
-
this.feedMarkdown(cleaned);
|
|
2845
|
-
}
|
|
2846
|
-
else {
|
|
2847
|
-
this.feedLine(tp.muted(" (no response text — the agent may have only performed tool actions)"));
|
|
2848
|
-
this.feedLine(tp.muted(` Use /debug ${event.result.teammate} to view full output`));
|
|
2849
|
-
// Show diagnostic hints for empty responses
|
|
2850
|
-
const diag = event.result.diagnostics;
|
|
2851
|
-
if (diag) {
|
|
2852
|
-
if (diag.exitCode !== 0 && diag.exitCode !== null) {
|
|
2853
|
-
this.feedLine(tp.warning(` ⚠ Process exited with code ${diag.exitCode}`));
|
|
2854
|
-
}
|
|
2855
|
-
if (diag.signal) {
|
|
2856
|
-
this.feedLine(tp.warning(` ⚠ Process killed by signal: ${diag.signal}`));
|
|
2857
|
-
}
|
|
2858
|
-
if (diag.debugFile) {
|
|
2859
|
-
this.feedLine(tp.muted(` Debug log: ${diag.debugFile}`));
|
|
2860
|
-
}
|
|
2861
|
-
}
|
|
2862
|
-
}
|
|
2863
|
-
// Render handoffs
|
|
2864
|
-
const handoffs = event.result.handoffs;
|
|
2865
|
-
if (handoffs.length > 0) {
|
|
2866
|
-
this.renderHandoffs(event.result.teammate, handoffs);
|
|
2867
|
-
}
|
|
2868
|
-
// Clickable [reply] [copy] actions after the response
|
|
2869
|
-
if (this.chatView && cleaned) {
|
|
2870
|
-
const t = theme();
|
|
2871
|
-
const teammate = event.result.teammate;
|
|
2872
|
-
const replyId = `reply-${teammate}-${Date.now()}`;
|
|
2873
|
-
this._replyContexts.set(replyId, { teammate, message: cleaned });
|
|
2874
|
-
this.chatView.appendActionList([
|
|
2875
|
-
{
|
|
2876
|
-
id: replyId,
|
|
2877
|
-
normalStyle: this.makeSpan({
|
|
2878
|
-
text: " [reply]",
|
|
2879
|
-
style: { fg: t.textDim },
|
|
2880
|
-
}),
|
|
2881
|
-
hoverStyle: this.makeSpan({
|
|
2882
|
-
text: " [reply]",
|
|
2883
|
-
style: { fg: t.accent },
|
|
2884
|
-
}),
|
|
2885
|
-
},
|
|
2886
|
-
{
|
|
2887
|
-
id: "copy",
|
|
2888
|
-
normalStyle: this.makeSpan({
|
|
2889
|
-
text: " [copy]",
|
|
2890
|
-
style: { fg: t.textDim },
|
|
2891
|
-
}),
|
|
2892
|
-
hoverStyle: this.makeSpan({
|
|
2893
|
-
text: " [copy]",
|
|
2894
|
-
style: { fg: t.accent },
|
|
2895
|
-
}),
|
|
2896
|
-
},
|
|
2897
|
-
]);
|
|
2898
|
-
}
|
|
2899
|
-
this.feedLine();
|
|
2900
|
-
// Auto-detect new teammates added during this task
|
|
2901
|
-
this.refreshTeammates();
|
|
2902
|
-
this.showPrompt();
|
|
2903
3091
|
break;
|
|
2904
3092
|
}
|
|
2905
3093
|
case "error":
|
|
@@ -2908,7 +3096,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2908
3096
|
this.stopStatusAnimation();
|
|
2909
3097
|
if (!this.chatView)
|
|
2910
3098
|
this.input.deactivateAndErase();
|
|
2911
|
-
const displayErr = event.teammate === this.
|
|
3099
|
+
const displayErr = event.teammate === this.selfName ? this.adapterName : event.teammate;
|
|
2912
3100
|
this.feedLine(tp.error(` ✖ ${displayErr}: ${event.error}`));
|
|
2913
3101
|
this.showPrompt();
|
|
2914
3102
|
break;
|
|
@@ -2920,14 +3108,12 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2920
3108
|
this.feedLine();
|
|
2921
3109
|
this.feedLine(tp.bold(" Status"));
|
|
2922
3110
|
this.feedLine(tp.muted(` ${"─".repeat(50)}`));
|
|
2923
|
-
// Show user avatar first if present
|
|
3111
|
+
// Show user avatar first if present (displayed as adapter name alias)
|
|
2924
3112
|
if (this.userAlias) {
|
|
2925
3113
|
const userStatus = statuses.get(this.userAlias);
|
|
2926
3114
|
if (userStatus) {
|
|
2927
|
-
this.feedLine(concat(tp.success("●"), tp.accent(` @${this.
|
|
2928
|
-
|
|
2929
|
-
if (t)
|
|
2930
|
-
this.feedLine(tp.muted(` ${t.role}`));
|
|
3115
|
+
this.feedLine(concat(tp.success("●"), tp.accent(` @${this.adapterName}`), tp.muted(" (you)")));
|
|
3116
|
+
this.feedLine(tp.muted(" Coding agent that performs tasks on your behalf."));
|
|
2931
3117
|
this.feedLine();
|
|
2932
3118
|
}
|
|
2933
3119
|
}
|
|
@@ -3070,7 +3256,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3070
3256
|
return;
|
|
3071
3257
|
}
|
|
3072
3258
|
const removed = this.taskQueue.splice(n - 1, 1)[0];
|
|
3073
|
-
const cancelDisplay = removed.teammate === this.
|
|
3259
|
+
const cancelDisplay = removed.teammate === this.selfName ? this.adapterName : removed.teammate;
|
|
3074
3260
|
this.feedLine(concat(tp.muted(" Cancelled: "), tp.accent(`@${cancelDisplay}`), tp.muted(" — "), tp.text(removed.task.slice(0, 60))));
|
|
3075
3261
|
this.refreshView();
|
|
3076
3262
|
}
|
|
@@ -3106,11 +3292,54 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3106
3292
|
const extraContext = entry.type === "btw" || entry.type === "debug"
|
|
3107
3293
|
? ""
|
|
3108
3294
|
: this.buildConversationContext();
|
|
3109
|
-
|
|
3295
|
+
let result = await this.orchestrator.assign({
|
|
3110
3296
|
teammate: entry.teammate,
|
|
3111
3297
|
task: entry.task,
|
|
3112
3298
|
extraContext: extraContext || undefined,
|
|
3113
3299
|
});
|
|
3300
|
+
// Defensive retry: if the agent produced no text output but exited
|
|
3301
|
+
// successfully, it likely ended its turn with only file edits.
|
|
3302
|
+
// Retry up to 2 times with progressively simpler prompts.
|
|
3303
|
+
const rawText = (result.rawOutput ?? "").trim();
|
|
3304
|
+
if (!rawText &&
|
|
3305
|
+
result.success &&
|
|
3306
|
+
entry.type !== "btw" &&
|
|
3307
|
+
entry.type !== "debug") {
|
|
3308
|
+
this.silentAgents.add(entry.teammate);
|
|
3309
|
+
// Attempt 1: ask the agent to summarize what it did
|
|
3310
|
+
const retry1 = await this.orchestrator.assign({
|
|
3311
|
+
teammate: entry.teammate,
|
|
3312
|
+
task: `You completed the previous task but produced no visible text output. The user cannot see your work without a text response.\n\nOriginal task: ${entry.task}\n\nPlease respond now with a summary of what you did. Do NOT update session or memory files. Do NOT use any tools. Just produce text output.\n\nFormat:\nTO: user\n# <Subject line>\n\n<Body — what you did, key decisions, files changed>`,
|
|
3313
|
+
raw: true,
|
|
3314
|
+
});
|
|
3315
|
+
const retry1Raw = (retry1.rawOutput ?? "").trim();
|
|
3316
|
+
if (retry1Raw) {
|
|
3317
|
+
result = {
|
|
3318
|
+
...result,
|
|
3319
|
+
rawOutput: retry1.rawOutput,
|
|
3320
|
+
summary: retry1.summary || result.summary,
|
|
3321
|
+
};
|
|
3322
|
+
}
|
|
3323
|
+
else {
|
|
3324
|
+
// Attempt 2: absolute minimum prompt — just ask for one sentence
|
|
3325
|
+
const retry2 = await this.orchestrator.assign({
|
|
3326
|
+
teammate: entry.teammate,
|
|
3327
|
+
task: `Say "Done." followed by one sentence describing what you changed. No tools. No file edits. Just text.`,
|
|
3328
|
+
raw: true,
|
|
3329
|
+
});
|
|
3330
|
+
const retry2Raw = (retry2.rawOutput ?? "").trim();
|
|
3331
|
+
if (retry2Raw) {
|
|
3332
|
+
result = {
|
|
3333
|
+
...result,
|
|
3334
|
+
rawOutput: retry2.rawOutput,
|
|
3335
|
+
summary: retry2.summary || result.summary,
|
|
3336
|
+
};
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
this.silentAgents.delete(entry.teammate);
|
|
3340
|
+
}
|
|
3341
|
+
// Display the (possibly retried) result to the user
|
|
3342
|
+
this.displayTaskResult(result, entry.type);
|
|
3114
3343
|
// Write debug entry — skip for debug analysis tasks (avoid recursion)
|
|
3115
3344
|
if (entry.type !== "debug") {
|
|
3116
3345
|
this.writeDebugEntry(entry.teammate, entry.task, result, startTime);
|
|
@@ -3134,7 +3363,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3134
3363
|
if (this.activeTasks.size === 0)
|
|
3135
3364
|
this.stopStatusAnimation();
|
|
3136
3365
|
const msg = err?.message ?? String(err);
|
|
3137
|
-
const displayAgent = agent === this.
|
|
3366
|
+
const displayAgent = agent === this.selfName ? this.adapterName : agent;
|
|
3138
3367
|
this.feedLine(tp.error(` ✖ @${displayAgent}: ${msg}`));
|
|
3139
3368
|
this.refreshView();
|
|
3140
3369
|
}
|
|
@@ -3236,7 +3465,11 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3236
3465
|
const teammatesDir = join(cwd, ".teammates");
|
|
3237
3466
|
await mkdir(teammatesDir, { recursive: true });
|
|
3238
3467
|
const fromPath = argsStr.trim();
|
|
3239
|
-
if (fromPath) {
|
|
3468
|
+
if (fromPath === "pick") {
|
|
3469
|
+
// Persona picker mode: /init pick
|
|
3470
|
+
await this.runPersonaOnboardingInline(teammatesDir);
|
|
3471
|
+
}
|
|
3472
|
+
else if (fromPath) {
|
|
3240
3473
|
// Import mode: /init <path-to-another-project>
|
|
3241
3474
|
const resolved = resolve(fromPath);
|
|
3242
3475
|
let sourceDir;
|
|
@@ -3777,7 +4010,7 @@ Issues that can't be resolved unilaterally — they need input from other teamma
|
|
|
3777
4010
|
// Has args — queue a task to apply the change
|
|
3778
4011
|
const task = `Update the file ${userMdPath} with the following change:\n\n${change}\n\nKeep the existing content intact unless the change explicitly replaces something. This is the user's profile — be concise and accurate.`;
|
|
3779
4012
|
this.taskQueue.push({ type: "agent", teammate: this.selfName, task });
|
|
3780
|
-
this.feedLine(concat(tp.muted(" Queued USER.md update → "), tp.accent(`@${this.
|
|
4013
|
+
this.feedLine(concat(tp.muted(" Queued USER.md update → "), tp.accent(`@${this.adapterName}`)));
|
|
3781
4014
|
this.feedLine();
|
|
3782
4015
|
this.refreshView();
|
|
3783
4016
|
this.kickDrain();
|
|
@@ -3794,7 +4027,7 @@ Issues that can't be resolved unilaterally — they need input from other teamma
|
|
|
3794
4027
|
teammate: this.selfName,
|
|
3795
4028
|
task: question,
|
|
3796
4029
|
});
|
|
3797
|
-
this.feedLine(concat(tp.muted(" Side question → "), tp.accent(`@${this.
|
|
4030
|
+
this.feedLine(concat(tp.muted(" Side question → "), tp.accent(`@${this.adapterName}`)));
|
|
3798
4031
|
this.feedLine();
|
|
3799
4032
|
this.refreshView();
|
|
3800
4033
|
this.kickDrain();
|