@teammates/cli 0.2.7 → 0.3.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 +12 -6
- package/dist/adapter.d.ts +20 -0
- package/dist/adapter.js +63 -5
- package/dist/adapters/cli-proxy.d.ts +21 -1
- package/dist/adapters/cli-proxy.js +70 -18
- package/dist/adapters/copilot.js +20 -1
- package/dist/cli.js +224 -374
- package/dist/compact.d.ts +7 -0
- package/dist/compact.js +97 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/types.d.ts +13 -0
- package/package.json +3 -2
- package/template/TEMPLATE.md +1 -1
- package/template/example/SOUL.md +1 -1
package/dist/cli.js
CHANGED
|
@@ -7,21 +7,20 @@
|
|
|
7
7
|
* teammates --adapter codex Use a specific agent adapter
|
|
8
8
|
* teammates --dir <path> Override .teammates/ location
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { exec as execCb, } from "node:child_process";
|
|
11
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { mkdir, readdir, rm, stat, unlink } from "node:fs/promises";
|
|
13
13
|
import { tmpdir } from "node:os";
|
|
14
14
|
import { dirname, join, resolve } from "node:path";
|
|
15
15
|
import { createInterface } from "node:readline";
|
|
16
|
-
import { promisify } from "node:util";
|
|
17
|
-
const execAsync = promisify(execCb);
|
|
18
16
|
import { App, ChatView, Control, concat, esc, Interview, pen, renderMarkdown, StyledText, stripAnsi, } from "@teammates/consolonia";
|
|
19
17
|
import chalk from "chalk";
|
|
20
18
|
import ora from "ora";
|
|
19
|
+
import { syncRecallIndex } from "./adapter.js";
|
|
21
20
|
import { CliProxyAdapter, PRESETS } from "./adapters/cli-proxy.js";
|
|
22
21
|
import { EchoAdapter } from "./adapters/echo.js";
|
|
23
22
|
import { findAtMention, isImagePath, relativeTime, wrapLine, } from "./cli-utils.js";
|
|
24
|
-
import { compactEpisodic } from "./compact.js";
|
|
23
|
+
import { buildWisdomPrompt, compactEpisodic } from "./compact.js";
|
|
25
24
|
import { PromptInput } from "./console/prompt-input.js";
|
|
26
25
|
import { buildTitle } from "./console/startup.js";
|
|
27
26
|
import { buildImportAdaptationPrompt, copyTemplateFiles, getOnboardingPrompt, importTeammates, } from "./onboard.js";
|
|
@@ -110,24 +109,6 @@ async function resolveAdapter(name) {
|
|
|
110
109
|
console.error(`Available adapters: ${available}`);
|
|
111
110
|
process.exit(1);
|
|
112
111
|
}
|
|
113
|
-
const SERVICE_REGISTRY = {
|
|
114
|
-
recall: {
|
|
115
|
-
package: "@teammates/recall",
|
|
116
|
-
checkCmd: ["teammates-recall", "--help"],
|
|
117
|
-
indexCmd: ["teammates-recall", "index"],
|
|
118
|
-
description: "Local semantic search for teammate memory",
|
|
119
|
-
wireupTask: [
|
|
120
|
-
"The `teammates-recall` service was just installed globally.",
|
|
121
|
-
"Wire it up so every teammate knows it's available:",
|
|
122
|
-
"",
|
|
123
|
-
"1. Verify `teammates-recall --help` works. If it does, great. If not, figure out the correct path to the binary (check recall/package.json bin field) and note it.",
|
|
124
|
-
"2. Read .teammates/PROTOCOL.md and .teammates/CROSS-TEAM.md.",
|
|
125
|
-
'3. If recall is not already documented there, add a short section explaining that `teammates-recall` is now available for semantic memory search, with basic usage (e.g. `teammates-recall search "query"`).',
|
|
126
|
-
"4. Check each teammate's SOUL.md (under .teammates/*/SOUL.md). If a teammate's role involves memory or search, note in their SOUL.md that recall is installed and available.",
|
|
127
|
-
"5. Do NOT modify code files — only update .teammates/ markdown files.",
|
|
128
|
-
].join("\n"),
|
|
129
|
-
},
|
|
130
|
-
};
|
|
131
112
|
// WordwheelItem is now DropdownItem from @teammates/consolonia
|
|
132
113
|
// ── Themed pen shortcuts ────────────────────────────────────────────
|
|
133
114
|
//
|
|
@@ -215,10 +196,8 @@ class AnimatedBanner extends Control {
|
|
|
215
196
|
lines.push(concat(tp.accent(tmTop), tp.text(gap + info.adapterName), tp.muted(` · ${info.teammateCount} teammate${info.teammateCount === 1 ? "" : "s"}`), tp.muted(` · v${PKG_VERSION}`)));
|
|
216
197
|
// TM logo row 2 + cwd
|
|
217
198
|
lines.push(concat(tp.accent(tmBot), tp.muted(gap + info.cwd)));
|
|
218
|
-
// Recall status (
|
|
219
|
-
lines.push(
|
|
220
|
-
? concat(tp.text(tmPad + gap), tp.success("● "), tp.success("recall"), tp.muted(" installed"))
|
|
221
|
-
: concat(tp.text(tmPad + gap), tp.warning("○ "), tp.warning("recall"), tp.muted(" not installed")));
|
|
199
|
+
// Recall status (bundled as dependency)
|
|
200
|
+
lines.push(concat(tp.text(tmPad + gap), tp.success("● "), tp.success("recall"), tp.muted(" bundled")));
|
|
222
201
|
// blank
|
|
223
202
|
lines.push("");
|
|
224
203
|
this._rosterStart = lines.length;
|
|
@@ -241,10 +220,7 @@ class AnimatedBanner extends Control {
|
|
|
241
220
|
["/retro", "run retrospective"],
|
|
242
221
|
];
|
|
243
222
|
const col3 = [
|
|
244
|
-
[
|
|
245
|
-
info.recallInstalled ? "/copy" : "/install",
|
|
246
|
-
info.recallInstalled ? "copy session text" : "add a service",
|
|
247
|
-
],
|
|
223
|
+
["/copy", "copy session text"],
|
|
248
224
|
["/help", "all commands"],
|
|
249
225
|
["/exit", "exit session"],
|
|
250
226
|
];
|
|
@@ -447,7 +423,6 @@ class TeammatesREPL {
|
|
|
447
423
|
}
|
|
448
424
|
adapterName;
|
|
449
425
|
teammatesDir;
|
|
450
|
-
recallWatchProcess = null;
|
|
451
426
|
taskQueue = [];
|
|
452
427
|
/** Per-agent active tasks — one per agent running in parallel. */
|
|
453
428
|
agentActive = new Map();
|
|
@@ -465,28 +440,10 @@ class TeammatesREPL {
|
|
|
465
440
|
lastCleanedOutput = ""; // last teammate output for clipboard copy
|
|
466
441
|
dispatching = false;
|
|
467
442
|
autoApproveHandoffs = false;
|
|
468
|
-
/**
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
return {
|
|
473
|
-
version: raw.version ?? 1,
|
|
474
|
-
services: Array.isArray(raw.services) ? raw.services : [],
|
|
475
|
-
...raw,
|
|
476
|
-
};
|
|
477
|
-
}
|
|
478
|
-
catch {
|
|
479
|
-
return { version: 1, services: [] };
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
/** Write .teammates/settings.json. */
|
|
483
|
-
writeSettings(settings) {
|
|
484
|
-
writeFileSync(join(this.teammatesDir, "settings.json"), `${JSON.stringify(settings, null, 2)}\n`);
|
|
485
|
-
}
|
|
486
|
-
/** Check whether a specific service is installed. */
|
|
487
|
-
isServiceInstalled(name) {
|
|
488
|
-
return this.readSettings().services.some((s) => s.name === name);
|
|
489
|
-
}
|
|
443
|
+
/** Last debug log file path per teammate — for /debug analysis. */
|
|
444
|
+
lastDebugFiles = new Map();
|
|
445
|
+
/** Last task prompt per teammate — for /debug analysis. */
|
|
446
|
+
lastTaskPrompts = new Map();
|
|
490
447
|
/** Pending handoffs awaiting user approval. */
|
|
491
448
|
pendingHandoffs = [];
|
|
492
449
|
/** Pending retro proposals awaiting user approval. */
|
|
@@ -1239,11 +1196,13 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1239
1196
|
this.kickDrain();
|
|
1240
1197
|
return;
|
|
1241
1198
|
}
|
|
1242
|
-
// No mentions —
|
|
1243
|
-
let match =
|
|
1199
|
+
// No mentions — default to the teammate you're chatting with, then try auto-route
|
|
1200
|
+
let match = null;
|
|
1201
|
+
if (this.lastResult) {
|
|
1202
|
+
match = this.lastResult.teammate;
|
|
1203
|
+
}
|
|
1244
1204
|
if (!match) {
|
|
1245
|
-
|
|
1246
|
-
match = this.adapterName;
|
|
1205
|
+
match = this.orchestrator.route(input) ?? this.adapterName;
|
|
1247
1206
|
}
|
|
1248
1207
|
{
|
|
1249
1208
|
const bg = this._userBg;
|
|
@@ -1717,17 +1676,6 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1717
1676
|
};
|
|
1718
1677
|
/** Build param-completion items for the current line, if any. */
|
|
1719
1678
|
getParamItems(cmdName, argsBefore, partial) {
|
|
1720
|
-
// Service-name completions for /install
|
|
1721
|
-
if (cmdName === "install" && !argsBefore.trim()) {
|
|
1722
|
-
const lower = partial.toLowerCase();
|
|
1723
|
-
return Object.entries(SERVICE_REGISTRY)
|
|
1724
|
-
.filter(([name]) => name.startsWith(lower))
|
|
1725
|
-
.map(([name, svc]) => ({
|
|
1726
|
-
label: name,
|
|
1727
|
-
description: svc.description,
|
|
1728
|
-
completion: `/install ${name} `,
|
|
1729
|
-
}));
|
|
1730
|
-
}
|
|
1731
1679
|
const positions = TeammatesREPL.TEAMMATE_ARG_POSITIONS[cmdName];
|
|
1732
1680
|
if (!positions)
|
|
1733
1681
|
return [];
|
|
@@ -1996,20 +1944,6 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1996
1944
|
return { name: t.name, role: t.role, ownership: t.ownership };
|
|
1997
1945
|
});
|
|
1998
1946
|
}
|
|
1999
|
-
// Detect installed services from settings.json and tell the adapter
|
|
2000
|
-
if ("services" in this.adapter) {
|
|
2001
|
-
const services = [];
|
|
2002
|
-
if (this.isServiceInstalled("recall")) {
|
|
2003
|
-
services.push({
|
|
2004
|
-
name: "recall",
|
|
2005
|
-
description: "Local semantic search across teammate memories and daily logs. Use this to find relevant context before starting a task.",
|
|
2006
|
-
usage: 'teammates-recall search "your query" --dir .teammates',
|
|
2007
|
-
});
|
|
2008
|
-
}
|
|
2009
|
-
this.adapter.services = services;
|
|
2010
|
-
}
|
|
2011
|
-
// Start recall watch mode if recall is installed
|
|
2012
|
-
this.startRecallWatch();
|
|
2013
1947
|
// Background maintenance: compact stale dailies + sync recall indexes
|
|
2014
1948
|
this.startupMaintenance().catch(() => { });
|
|
2015
1949
|
// Register commands
|
|
@@ -2024,6 +1958,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2024
1958
|
const validNames = new Set([
|
|
2025
1959
|
...this.orchestrator.listTeammates(),
|
|
2026
1960
|
this.adapterName,
|
|
1961
|
+
"everyone",
|
|
2027
1962
|
]);
|
|
2028
1963
|
return value
|
|
2029
1964
|
.replace(/@(\w+)/g, (match, name) => validNames.has(name) ? chalk.blue(match) : match)
|
|
@@ -2061,12 +1996,10 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2061
1996
|
// ── Build animated banner for ChatView ─────────────────────────────
|
|
2062
1997
|
const names = this.orchestrator.listTeammates();
|
|
2063
1998
|
const reg = this.orchestrator.getRegistry();
|
|
2064
|
-
const hasRecall = this.isServiceInstalled("recall");
|
|
2065
1999
|
const bannerWidget = new AnimatedBanner({
|
|
2066
2000
|
adapterName: this.adapterName,
|
|
2067
2001
|
teammateCount: names.length,
|
|
2068
2002
|
cwd: process.cwd(),
|
|
2069
|
-
recallInstalled: hasRecall,
|
|
2070
2003
|
teammates: names.map((name) => {
|
|
2071
2004
|
const t = reg.get(name);
|
|
2072
2005
|
return { name, role: t?.role ?? "" };
|
|
@@ -2098,6 +2031,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2098
2031
|
const validNames = new Set([
|
|
2099
2032
|
...this.orchestrator.listTeammates(),
|
|
2100
2033
|
this.adapterName,
|
|
2034
|
+
"everyone",
|
|
2101
2035
|
]);
|
|
2102
2036
|
const mentionPattern = /@(\w+)/g;
|
|
2103
2037
|
while ((m = mentionPattern.exec(value)) !== null) {
|
|
@@ -2233,7 +2167,6 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2233
2167
|
this.ctrlcTimer = null;
|
|
2234
2168
|
}
|
|
2235
2169
|
this.chatView.setFooter(this.defaultFooter);
|
|
2236
|
-
this.stopRecallWatch();
|
|
2237
2170
|
if (this.app)
|
|
2238
2171
|
this.app.stop();
|
|
2239
2172
|
this.orchestrator.shutdown().then(() => process.exit(0));
|
|
@@ -2275,6 +2208,9 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2275
2208
|
}
|
|
2276
2209
|
}
|
|
2277
2210
|
});
|
|
2211
|
+
this.chatView.on("copy", (text) => {
|
|
2212
|
+
this.doCopy(text);
|
|
2213
|
+
});
|
|
2278
2214
|
this.chatView.on("link", (url) => {
|
|
2279
2215
|
const quoted = JSON.stringify(url);
|
|
2280
2216
|
const cmd = process.platform === "darwin"
|
|
@@ -2284,6 +2220,15 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2284
2220
|
: `xdg-open ${quoted}`;
|
|
2285
2221
|
execCb(cmd, () => { });
|
|
2286
2222
|
});
|
|
2223
|
+
this.chatView.on("file", (filePath) => {
|
|
2224
|
+
const quoted = JSON.stringify(filePath);
|
|
2225
|
+
const cmd = process.platform === "darwin"
|
|
2226
|
+
? `open ${quoted}`
|
|
2227
|
+
: process.platform === "win32"
|
|
2228
|
+
? `start "" ${quoted}`
|
|
2229
|
+
: `xdg-open ${quoted}`;
|
|
2230
|
+
execCb(cmd, () => { });
|
|
2231
|
+
});
|
|
2287
2232
|
this.app = new App({
|
|
2288
2233
|
root: this.chatView,
|
|
2289
2234
|
alternateScreen: true,
|
|
@@ -2453,15 +2398,11 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2453
2398
|
printBanner(teammates) {
|
|
2454
2399
|
const registry = this.orchestrator.getRegistry();
|
|
2455
2400
|
const termWidth = process.stdout.columns || 100;
|
|
2456
|
-
// Detect recall from settings.json
|
|
2457
|
-
const recallInstalled = this.isServiceInstalled("recall");
|
|
2458
2401
|
this.feedLine();
|
|
2459
2402
|
this.feedLine(concat(tp.bold(" Teammates"), tp.muted(` v${PKG_VERSION}`)));
|
|
2460
2403
|
this.feedLine(concat(tp.text(` ${this.adapterName}`), tp.muted(` · ${teammates.length} teammate${teammates.length === 1 ? "" : "s"}`)));
|
|
2461
2404
|
this.feedLine(` ${process.cwd()}`);
|
|
2462
|
-
this.feedLine(
|
|
2463
|
-
? tp.success(" ● recall installed")
|
|
2464
|
-
: tp.warning(" ○ recall not installed"));
|
|
2405
|
+
this.feedLine(concat(tp.success(" ● recall"), tp.muted(" bundled")));
|
|
2465
2406
|
// Roster
|
|
2466
2407
|
this.feedLine();
|
|
2467
2408
|
for (const name of teammates) {
|
|
@@ -2480,11 +2421,11 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2480
2421
|
// First run — no teammates yet
|
|
2481
2422
|
col1 = [
|
|
2482
2423
|
["/init", "set up teammates"],
|
|
2483
|
-
["/
|
|
2424
|
+
["/help", "all commands"],
|
|
2484
2425
|
];
|
|
2485
2426
|
col2 = [
|
|
2486
|
-
["/help", "all commands"],
|
|
2487
2427
|
["/exit", "exit session"],
|
|
2428
|
+
["", ""],
|
|
2488
2429
|
];
|
|
2489
2430
|
col3 = [
|
|
2490
2431
|
["", ""],
|
|
@@ -2503,10 +2444,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2503
2444
|
["/retro", "run retrospective"],
|
|
2504
2445
|
];
|
|
2505
2446
|
col3 = [
|
|
2506
|
-
[
|
|
2507
|
-
recallInstalled ? "/copy" : "/install",
|
|
2508
|
-
recallInstalled ? "copy session text" : "add a service",
|
|
2509
|
-
],
|
|
2447
|
+
["/copy", "copy session text"],
|
|
2510
2448
|
["/help", "all commands"],
|
|
2511
2449
|
["/exit", "exit session"],
|
|
2512
2450
|
];
|
|
@@ -2537,7 +2475,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2537
2475
|
name: "debug",
|
|
2538
2476
|
aliases: ["raw"],
|
|
2539
2477
|
usage: "/debug [teammate]",
|
|
2540
|
-
description: "
|
|
2478
|
+
description: "Analyze the last agent task with the coding agent",
|
|
2541
2479
|
run: (args) => this.cmdDebug(args),
|
|
2542
2480
|
},
|
|
2543
2481
|
{
|
|
@@ -2561,13 +2499,6 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2561
2499
|
description: "Clear history and reset the session",
|
|
2562
2500
|
run: () => this.cmdClear(),
|
|
2563
2501
|
},
|
|
2564
|
-
{
|
|
2565
|
-
name: "install",
|
|
2566
|
-
aliases: [],
|
|
2567
|
-
usage: "/install [service]",
|
|
2568
|
-
description: "Install a teammates service (e.g. recall)",
|
|
2569
|
-
run: (args) => this.cmdInstall(args),
|
|
2570
|
-
},
|
|
2571
2502
|
{
|
|
2572
2503
|
name: "compact",
|
|
2573
2504
|
aliases: [],
|
|
@@ -2617,7 +2548,6 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2617
2548
|
description: "Exit the session",
|
|
2618
2549
|
run: async () => {
|
|
2619
2550
|
this.feedLine(tp.muted("Shutting down..."));
|
|
2620
|
-
this.stopRecallWatch();
|
|
2621
2551
|
if (this.app)
|
|
2622
2552
|
this.app.stop();
|
|
2623
2553
|
await this.orchestrator.shutdown();
|
|
@@ -2695,6 +2625,19 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2695
2625
|
else {
|
|
2696
2626
|
this.feedLine(tp.muted(" (no response text — the agent may have only performed tool actions)"));
|
|
2697
2627
|
this.feedLine(tp.muted(` Use /debug ${event.result.teammate} to view full output`));
|
|
2628
|
+
// Show diagnostic hints for empty responses
|
|
2629
|
+
const diag = event.result.diagnostics;
|
|
2630
|
+
if (diag) {
|
|
2631
|
+
if (diag.exitCode !== 0 && diag.exitCode !== null) {
|
|
2632
|
+
this.feedLine(tp.warning(` ⚠ Process exited with code ${diag.exitCode}`));
|
|
2633
|
+
}
|
|
2634
|
+
if (diag.signal) {
|
|
2635
|
+
this.feedLine(tp.warning(` ⚠ Process killed by signal: ${diag.signal}`));
|
|
2636
|
+
}
|
|
2637
|
+
if (diag.debugFile) {
|
|
2638
|
+
this.feedLine(tp.muted(` Debug log: ${diag.debugFile}`));
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2698
2641
|
}
|
|
2699
2642
|
// Render handoffs
|
|
2700
2643
|
const handoffs = event.result.handoffs;
|
|
@@ -2796,99 +2739,81 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2796
2739
|
}
|
|
2797
2740
|
async cmdDebug(argsStr) {
|
|
2798
2741
|
const arg = argsStr.trim().replace(/^@/, "");
|
|
2799
|
-
// Resolve which
|
|
2800
|
-
let
|
|
2742
|
+
// Resolve which teammate to debug
|
|
2743
|
+
let targetName;
|
|
2801
2744
|
if (arg === "everyone") {
|
|
2802
|
-
|
|
2803
|
-
|
|
2745
|
+
// Pick all teammates with debug files, queue one analysis per teammate
|
|
2746
|
+
const names = [];
|
|
2747
|
+
for (const [name] of this.lastDebugFiles) {
|
|
2804
2748
|
if (name !== this.adapterName)
|
|
2805
|
-
|
|
2749
|
+
names.push(name);
|
|
2806
2750
|
}
|
|
2807
|
-
if (
|
|
2751
|
+
if (names.length === 0) {
|
|
2808
2752
|
this.feedLine(tp.muted(" No debug info available from any teammate."));
|
|
2809
2753
|
this.refreshView();
|
|
2810
2754
|
return;
|
|
2811
2755
|
}
|
|
2756
|
+
for (const name of names) {
|
|
2757
|
+
this.queueDebugAnalysis(name);
|
|
2758
|
+
}
|
|
2759
|
+
return;
|
|
2812
2760
|
}
|
|
2813
2761
|
else if (arg) {
|
|
2814
|
-
|
|
2762
|
+
targetName = arg;
|
|
2815
2763
|
}
|
|
2816
2764
|
else if (this.lastResult) {
|
|
2817
|
-
|
|
2765
|
+
targetName = this.lastResult.teammate;
|
|
2818
2766
|
}
|
|
2819
2767
|
else {
|
|
2820
2768
|
this.feedLine(tp.muted(" No debug info available. Try: /debug [teammate]"));
|
|
2821
2769
|
this.refreshView();
|
|
2822
2770
|
return;
|
|
2823
2771
|
}
|
|
2824
|
-
|
|
2825
|
-
for (const name of targetNames) {
|
|
2826
|
-
this.feedLine();
|
|
2827
|
-
this.feedLine(tp.muted(` ── debug for ${name} ──`));
|
|
2828
|
-
// Read the last debug entry from the session file
|
|
2829
|
-
const sessionEntry = this.readLastDebugEntry(name);
|
|
2830
|
-
if (sessionEntry) {
|
|
2831
|
-
this.feedLine();
|
|
2832
|
-
this.feedMarkdown(sessionEntry);
|
|
2833
|
-
debugText += (debugText ? "\n\n" : "") + sessionEntry;
|
|
2834
|
-
}
|
|
2835
|
-
else {
|
|
2836
|
-
// Fall back to raw output from lastResults
|
|
2837
|
-
const result = this.lastResults.get(name);
|
|
2838
|
-
if (result?.rawOutput) {
|
|
2839
|
-
this.feedLine();
|
|
2840
|
-
this.feedMarkdown(result.rawOutput);
|
|
2841
|
-
debugText += (debugText ? "\n\n" : "") + result.rawOutput;
|
|
2842
|
-
}
|
|
2843
|
-
else {
|
|
2844
|
-
this.feedLine(tp.muted(" No debug info available."));
|
|
2845
|
-
}
|
|
2846
|
-
}
|
|
2847
|
-
this.feedLine();
|
|
2848
|
-
this.feedLine(tp.muted(" ── end debug ──"));
|
|
2849
|
-
}
|
|
2850
|
-
// [copy] action for the debug output
|
|
2851
|
-
if (this.chatView && debugText) {
|
|
2852
|
-
const t = theme();
|
|
2853
|
-
this.lastCleanedOutput = debugText;
|
|
2854
|
-
this.chatView.appendActionList([
|
|
2855
|
-
{
|
|
2856
|
-
id: "copy",
|
|
2857
|
-
normalStyle: this.makeSpan({
|
|
2858
|
-
text: " [copy]",
|
|
2859
|
-
style: { fg: t.textDim },
|
|
2860
|
-
}),
|
|
2861
|
-
hoverStyle: this.makeSpan({
|
|
2862
|
-
text: " [copy]",
|
|
2863
|
-
style: { fg: t.accent },
|
|
2864
|
-
}),
|
|
2865
|
-
},
|
|
2866
|
-
]);
|
|
2867
|
-
}
|
|
2868
|
-
this.feedLine();
|
|
2869
|
-
this.refreshView();
|
|
2772
|
+
this.queueDebugAnalysis(targetName);
|
|
2870
2773
|
}
|
|
2871
2774
|
/**
|
|
2872
|
-
*
|
|
2873
|
-
*
|
|
2775
|
+
* Queue a debug analysis task — sends the last request + debug log
|
|
2776
|
+
* to the base coding agent for analysis.
|
|
2874
2777
|
*/
|
|
2875
|
-
|
|
2778
|
+
queueDebugAnalysis(teammate) {
|
|
2779
|
+
const debugFile = this.lastDebugFiles.get(teammate);
|
|
2780
|
+
const lastPrompt = this.lastTaskPrompts.get(teammate);
|
|
2781
|
+
if (!debugFile) {
|
|
2782
|
+
this.feedLine(tp.muted(` No debug log available for @${teammate}.`));
|
|
2783
|
+
this.refreshView();
|
|
2784
|
+
return;
|
|
2785
|
+
}
|
|
2786
|
+
// Read the debug log file
|
|
2787
|
+
let debugContent;
|
|
2876
2788
|
try {
|
|
2877
|
-
|
|
2878
|
-
if (!sessionFile)
|
|
2879
|
-
return null;
|
|
2880
|
-
const content = readFileSync(sessionFile, "utf-8");
|
|
2881
|
-
// Split on debug entry headings and return the last one
|
|
2882
|
-
const entries = content.split(/(?=^## Debug — )/m);
|
|
2883
|
-
const last = entries[entries.length - 1];
|
|
2884
|
-
if (last && last.startsWith("## Debug — ")) {
|
|
2885
|
-
return last.trim();
|
|
2886
|
-
}
|
|
2887
|
-
return null;
|
|
2789
|
+
debugContent = readFileSync(debugFile, "utf-8");
|
|
2888
2790
|
}
|
|
2889
2791
|
catch {
|
|
2890
|
-
|
|
2792
|
+
this.feedLine(tp.muted(` Could not read debug log: ${debugFile}`));
|
|
2793
|
+
this.refreshView();
|
|
2794
|
+
return;
|
|
2891
2795
|
}
|
|
2796
|
+
const analysisPrompt = [
|
|
2797
|
+
`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.`,
|
|
2798
|
+
"",
|
|
2799
|
+
"## Last Request Sent to Agent",
|
|
2800
|
+
"",
|
|
2801
|
+
lastPrompt ?? "(not available)",
|
|
2802
|
+
"",
|
|
2803
|
+
"## Debug Log",
|
|
2804
|
+
"",
|
|
2805
|
+
debugContent,
|
|
2806
|
+
].join("\n");
|
|
2807
|
+
// Show the debug log path — ctrl+click to open
|
|
2808
|
+
this.feedLine(concat(tp.muted(" Debug log: "), tp.accent(debugFile)));
|
|
2809
|
+
this.feedLine(tp.muted(" Queuing analysis…"));
|
|
2810
|
+
this.refreshView();
|
|
2811
|
+
this.taskQueue.push({
|
|
2812
|
+
type: "debug",
|
|
2813
|
+
teammate: this.adapterName,
|
|
2814
|
+
task: analysisPrompt,
|
|
2815
|
+
});
|
|
2816
|
+
this.kickDrain();
|
|
2892
2817
|
}
|
|
2893
2818
|
async cmdCancel(argsStr) {
|
|
2894
2819
|
const n = parseInt(argsStr.trim(), 10);
|
|
@@ -2920,17 +2845,21 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2920
2845
|
await this.runCompact(entry.teammate);
|
|
2921
2846
|
}
|
|
2922
2847
|
else {
|
|
2923
|
-
// btw tasks skip conversation context (
|
|
2924
|
-
const extraContext = entry.type === "btw"
|
|
2848
|
+
// btw and debug tasks skip conversation context (not part of main thread)
|
|
2849
|
+
const extraContext = entry.type === "btw" || entry.type === "debug"
|
|
2850
|
+
? ""
|
|
2851
|
+
: this.buildConversationContext();
|
|
2925
2852
|
const result = await this.orchestrator.assign({
|
|
2926
2853
|
teammate: entry.teammate,
|
|
2927
2854
|
task: entry.task,
|
|
2928
2855
|
extraContext: extraContext || undefined,
|
|
2929
2856
|
});
|
|
2930
|
-
// Write debug entry
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2857
|
+
// Write debug entry — skip for debug analysis tasks (avoid recursion)
|
|
2858
|
+
if (entry.type !== "debug") {
|
|
2859
|
+
this.writeDebugEntry(entry.teammate, entry.task, result, startTime);
|
|
2860
|
+
}
|
|
2861
|
+
// btw and debug results are not stored in conversation history
|
|
2862
|
+
if (entry.type !== "btw" && entry.type !== "debug") {
|
|
2934
2863
|
this.storeResult(result);
|
|
2935
2864
|
}
|
|
2936
2865
|
if (entry.type === "retro") {
|
|
@@ -2953,29 +2882,42 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2953
2882
|
}
|
|
2954
2883
|
}
|
|
2955
2884
|
/**
|
|
2956
|
-
*
|
|
2957
|
-
*
|
|
2885
|
+
* Write a debug log file to .teammates/.tmp/debug/ for the task.
|
|
2886
|
+
* Each task gets its own file. The path is stored in lastDebugFiles for /debug.
|
|
2958
2887
|
*/
|
|
2959
2888
|
writeDebugEntry(teammate, task, result, startTime, error) {
|
|
2960
2889
|
try {
|
|
2961
|
-
const
|
|
2962
|
-
|
|
2890
|
+
const debugDir = join(this.teammatesDir, ".tmp", "debug");
|
|
2891
|
+
try {
|
|
2892
|
+
mkdirSync(debugDir, { recursive: true });
|
|
2893
|
+
}
|
|
2894
|
+
catch {
|
|
2963
2895
|
return;
|
|
2896
|
+
}
|
|
2964
2897
|
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
2965
2898
|
const timestamp = new Date().toISOString();
|
|
2899
|
+
const ts = timestamp.replace(/[:.]/g, "-");
|
|
2900
|
+
const debugFile = join(debugDir, `${teammate}-${ts}.md`);
|
|
2966
2901
|
const lines = [
|
|
2902
|
+
`# Debug — ${teammate}`,
|
|
2967
2903
|
"",
|
|
2968
|
-
|
|
2969
|
-
"",
|
|
2904
|
+
`**Timestamp:** ${timestamp}`,
|
|
2970
2905
|
`**Duration:** ${elapsed}s`,
|
|
2971
|
-
|
|
2906
|
+
"",
|
|
2907
|
+
"## Request",
|
|
2908
|
+
"",
|
|
2909
|
+
task,
|
|
2972
2910
|
"",
|
|
2973
2911
|
];
|
|
2974
2912
|
if (error) {
|
|
2913
|
+
lines.push("## Result");
|
|
2914
|
+
lines.push("");
|
|
2975
2915
|
lines.push(`**Status:** ERROR`);
|
|
2976
2916
|
lines.push(`**Error:** ${error?.message ?? String(error)}`);
|
|
2977
2917
|
}
|
|
2978
2918
|
else if (result) {
|
|
2919
|
+
lines.push("## Result");
|
|
2920
|
+
lines.push("");
|
|
2979
2921
|
lines.push(`**Status:** ${result.success ? "OK" : "FAILED"}`);
|
|
2980
2922
|
lines.push(`**Summary:** ${result.summary || "(no summary)"}`);
|
|
2981
2923
|
if (result.changedFiles.length > 0) {
|
|
@@ -2984,15 +2926,46 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2984
2926
|
if (result.handoffs.length > 0) {
|
|
2985
2927
|
lines.push(`**Handoffs:** ${result.handoffs.map((h) => `@${h.to}`).join(", ")}`);
|
|
2986
2928
|
}
|
|
2929
|
+
// Process diagnostics — exit code, signal, stderr
|
|
2930
|
+
const diag = result.diagnostics;
|
|
2931
|
+
if (diag) {
|
|
2932
|
+
lines.push("");
|
|
2933
|
+
lines.push("### Process");
|
|
2934
|
+
lines.push(`**Exit code:** ${diag.exitCode ?? "(killed by signal)"}`);
|
|
2935
|
+
if (diag.signal)
|
|
2936
|
+
lines.push(`**Signal:** ${diag.signal}`);
|
|
2937
|
+
if (diag.timedOut)
|
|
2938
|
+
lines.push(`**Timed out:** yes`);
|
|
2939
|
+
if (diag.debugFile) {
|
|
2940
|
+
lines.push(`**Agent debug log:** ${diag.debugFile}`);
|
|
2941
|
+
// Inline Claude's debug file content if it exists
|
|
2942
|
+
try {
|
|
2943
|
+
const agentDebugContent = readFileSync(diag.debugFile, "utf-8");
|
|
2944
|
+
lines.push("");
|
|
2945
|
+
lines.push("### Agent Debug Log");
|
|
2946
|
+
lines.push("");
|
|
2947
|
+
lines.push(agentDebugContent);
|
|
2948
|
+
}
|
|
2949
|
+
catch {
|
|
2950
|
+
/* debug file may not exist yet or be unreadable */
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
if (diag.stderr.trim()) {
|
|
2954
|
+
lines.push("");
|
|
2955
|
+
lines.push("### stderr");
|
|
2956
|
+
lines.push("");
|
|
2957
|
+
lines.push(diag.stderr);
|
|
2958
|
+
}
|
|
2959
|
+
}
|
|
2987
2960
|
lines.push("");
|
|
2988
|
-
lines.push("
|
|
2961
|
+
lines.push("### Raw Output");
|
|
2989
2962
|
lines.push("");
|
|
2990
2963
|
lines.push(result.rawOutput ?? "(empty)");
|
|
2991
|
-
lines.push("");
|
|
2992
|
-
lines.push("</details>");
|
|
2993
2964
|
}
|
|
2994
2965
|
lines.push("");
|
|
2995
|
-
|
|
2966
|
+
writeFileSync(debugFile, lines.join("\n"), "utf-8");
|
|
2967
|
+
this.lastDebugFiles.set(teammate, debugFile);
|
|
2968
|
+
this.lastTaskPrompts.set(teammate, task);
|
|
2996
2969
|
}
|
|
2997
2970
|
catch {
|
|
2998
2971
|
// Don't let debug logging break task execution
|
|
@@ -3070,123 +3043,6 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3070
3043
|
this.feedLine(tp.muted(" Run /status to see the roster."));
|
|
3071
3044
|
this.refreshView();
|
|
3072
3045
|
}
|
|
3073
|
-
async cmdInstall(argsStr) {
|
|
3074
|
-
const serviceName = argsStr.trim().toLowerCase();
|
|
3075
|
-
if (!serviceName) {
|
|
3076
|
-
this.feedLine(tp.bold("\n Available services:"));
|
|
3077
|
-
for (const [name, svc] of Object.entries(SERVICE_REGISTRY)) {
|
|
3078
|
-
this.feedLine(concat(tp.accent(name.padEnd(16)), tp.muted(svc.description)));
|
|
3079
|
-
}
|
|
3080
|
-
this.feedLine();
|
|
3081
|
-
this.refreshView();
|
|
3082
|
-
return;
|
|
3083
|
-
}
|
|
3084
|
-
const service = SERVICE_REGISTRY[serviceName];
|
|
3085
|
-
if (!service) {
|
|
3086
|
-
this.feedLine(tp.warning(` Unknown service: ${serviceName}`));
|
|
3087
|
-
this.feedLine(tp.muted(` Available: ${Object.keys(SERVICE_REGISTRY).join(", ")}`));
|
|
3088
|
-
this.refreshView();
|
|
3089
|
-
return;
|
|
3090
|
-
}
|
|
3091
|
-
// Install the package globally
|
|
3092
|
-
if (this.chatView) {
|
|
3093
|
-
this.chatView.setProgress(`Installing ${service.package}...`);
|
|
3094
|
-
this.refreshView();
|
|
3095
|
-
}
|
|
3096
|
-
let installSpinner = null;
|
|
3097
|
-
if (!this.chatView) {
|
|
3098
|
-
installSpinner = ora({
|
|
3099
|
-
text: chalk.blue(serviceName) +
|
|
3100
|
-
chalk.gray(` installing ${service.package}...`),
|
|
3101
|
-
spinner: "dots",
|
|
3102
|
-
}).start();
|
|
3103
|
-
}
|
|
3104
|
-
try {
|
|
3105
|
-
await execAsync(`npm install -g ${service.package}`, {
|
|
3106
|
-
timeout: 5 * 60 * 1000,
|
|
3107
|
-
});
|
|
3108
|
-
if (installSpinner)
|
|
3109
|
-
installSpinner.stop();
|
|
3110
|
-
if (this.chatView)
|
|
3111
|
-
this.chatView.setProgress(null);
|
|
3112
|
-
}
|
|
3113
|
-
catch (err) {
|
|
3114
|
-
if (installSpinner)
|
|
3115
|
-
installSpinner.fail(chalk.red(`Install failed: ${err.message}`));
|
|
3116
|
-
if (this.chatView) {
|
|
3117
|
-
this.chatView.setProgress(null);
|
|
3118
|
-
this.feedLine(tp.error(` ✖ Install failed: ${err.message}`));
|
|
3119
|
-
this.refreshView();
|
|
3120
|
-
}
|
|
3121
|
-
return;
|
|
3122
|
-
}
|
|
3123
|
-
// Verify the binary works
|
|
3124
|
-
const checkCmdStr = service.checkCmd.join(" ");
|
|
3125
|
-
try {
|
|
3126
|
-
execSync(checkCmdStr, { stdio: "ignore" });
|
|
3127
|
-
}
|
|
3128
|
-
catch {
|
|
3129
|
-
this.feedLine(tp.success(` ✔ ${serviceName} installed`));
|
|
3130
|
-
this.feedLine(tp.warning(` ⚠ Restart your terminal to add ${service.checkCmd[0]} to your PATH, then run /install ${serviceName} again to build the index.`));
|
|
3131
|
-
this.refreshView();
|
|
3132
|
-
return;
|
|
3133
|
-
}
|
|
3134
|
-
this.feedLine(tp.success(` ✔ ${serviceName} installed successfully`));
|
|
3135
|
-
// Register in settings.json
|
|
3136
|
-
const settings = this.readSettings();
|
|
3137
|
-
if (!settings.services.some((s) => s.name === serviceName)) {
|
|
3138
|
-
settings.services.push({ name: serviceName });
|
|
3139
|
-
this.writeSettings(settings);
|
|
3140
|
-
this.feedLine(tp.muted(` Registered in settings.json`));
|
|
3141
|
-
}
|
|
3142
|
-
// Build initial index if this service supports it
|
|
3143
|
-
if (service.indexCmd) {
|
|
3144
|
-
if (this.chatView) {
|
|
3145
|
-
this.chatView.setProgress(`Building ${serviceName} index...`);
|
|
3146
|
-
this.refreshView();
|
|
3147
|
-
}
|
|
3148
|
-
let idxSpinner = null;
|
|
3149
|
-
if (!this.chatView) {
|
|
3150
|
-
idxSpinner = ora({
|
|
3151
|
-
text: chalk.blue(serviceName) + chalk.gray(` building index...`),
|
|
3152
|
-
spinner: "dots",
|
|
3153
|
-
}).start();
|
|
3154
|
-
}
|
|
3155
|
-
const indexCmdStr = service.indexCmd.join(" ");
|
|
3156
|
-
try {
|
|
3157
|
-
await execAsync(indexCmdStr, {
|
|
3158
|
-
cwd: resolve(this.teammatesDir, ".."),
|
|
3159
|
-
timeout: 5 * 60 * 1000,
|
|
3160
|
-
});
|
|
3161
|
-
if (idxSpinner)
|
|
3162
|
-
idxSpinner.succeed(chalk.blue(serviceName) + chalk.gray(" index built"));
|
|
3163
|
-
if (this.chatView) {
|
|
3164
|
-
this.chatView.setProgress(null);
|
|
3165
|
-
this.feedLine(tp.success(` ✔ ${serviceName} index built`));
|
|
3166
|
-
}
|
|
3167
|
-
}
|
|
3168
|
-
catch (err) {
|
|
3169
|
-
if (idxSpinner)
|
|
3170
|
-
idxSpinner.warn(chalk.yellow(`Index build failed: ${err.message}`));
|
|
3171
|
-
if (this.chatView) {
|
|
3172
|
-
this.chatView.setProgress(null);
|
|
3173
|
-
this.feedLine(tp.warning(` ⚠ Index build failed: ${err.message}`));
|
|
3174
|
-
}
|
|
3175
|
-
}
|
|
3176
|
-
}
|
|
3177
|
-
// Ask the coding agent to wire the service into the project
|
|
3178
|
-
if (service.wireupTask) {
|
|
3179
|
-
this.feedLine();
|
|
3180
|
-
this.feedLine(tp.muted(` Wiring up ${serviceName}...`));
|
|
3181
|
-
this.refreshView();
|
|
3182
|
-
const result = await this.orchestrator.assign({
|
|
3183
|
-
teammate: this.adapterName,
|
|
3184
|
-
task: service.wireupTask,
|
|
3185
|
-
});
|
|
3186
|
-
this.storeResult(result);
|
|
3187
|
-
}
|
|
3188
|
-
this.refreshView();
|
|
3189
|
-
}
|
|
3190
3046
|
async cmdClear() {
|
|
3191
3047
|
this.conversationHistory.length = 0;
|
|
3192
3048
|
this.lastResult = null;
|
|
@@ -3235,35 +3091,10 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3235
3091
|
})
|
|
3236
3092
|
.catch(() => { });
|
|
3237
3093
|
}
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
if (!this.isServiceInstalled("recall"))
|
|
3241
|
-
return;
|
|
3242
|
-
try {
|
|
3243
|
-
this.recallWatchProcess = cpSpawn("teammates-recall", ["watch", "--dir", this.teammatesDir, "--json"], {
|
|
3244
|
-
stdio: ["ignore", "ignore", "ignore"],
|
|
3245
|
-
detached: false,
|
|
3246
|
-
});
|
|
3247
|
-
this.recallWatchProcess.on("error", () => {
|
|
3248
|
-
// Recall binary not found — silently ignore
|
|
3249
|
-
this.recallWatchProcess = null;
|
|
3250
|
-
});
|
|
3251
|
-
this.recallWatchProcess.on("exit", () => {
|
|
3252
|
-
this.recallWatchProcess = null;
|
|
3253
|
-
});
|
|
3254
|
-
}
|
|
3255
|
-
catch {
|
|
3256
|
-
this.recallWatchProcess = null;
|
|
3257
|
-
}
|
|
3258
|
-
}
|
|
3259
|
-
stopRecallWatch() {
|
|
3260
|
-
if (this.recallWatchProcess) {
|
|
3261
|
-
this.recallWatchProcess.kill("SIGTERM");
|
|
3262
|
-
this.recallWatchProcess = null;
|
|
3263
|
-
}
|
|
3264
|
-
}
|
|
3094
|
+
// Recall is now bundled as a library dependency — no watch process needed.
|
|
3095
|
+
// Sync happens via syncRecallIndex() after every task and on startup.
|
|
3265
3096
|
async cmdCompact(argsStr) {
|
|
3266
|
-
const arg = argsStr.trim();
|
|
3097
|
+
const arg = argsStr.trim().replace(/^@/, "");
|
|
3267
3098
|
const allTeammates = this.orchestrator
|
|
3268
3099
|
.listTeammates()
|
|
3269
3100
|
.filter((n) => n !== this.adapterName);
|
|
@@ -3341,32 +3172,47 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3341
3172
|
}
|
|
3342
3173
|
if (this.chatView)
|
|
3343
3174
|
this.chatView.setProgress(null);
|
|
3344
|
-
//
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
}).start();
|
|
3357
|
-
}
|
|
3358
|
-
await execAsync(`teammates-recall sync --dir "${this.teammatesDir}"`);
|
|
3359
|
-
if (syncSpinner)
|
|
3360
|
-
syncSpinner.succeed(`${name}: index synced`);
|
|
3361
|
-
if (this.chatView) {
|
|
3362
|
-
this.chatView.setProgress(null);
|
|
3363
|
-
this.feedLine(tp.success(` ✔ ${name}: index synced`));
|
|
3364
|
-
}
|
|
3175
|
+
// Sync recall index for this teammate (bundled library call)
|
|
3176
|
+
try {
|
|
3177
|
+
if (this.chatView) {
|
|
3178
|
+
this.chatView.setProgress(`Syncing ${name} index...`);
|
|
3179
|
+
this.refreshView();
|
|
3180
|
+
}
|
|
3181
|
+
let syncSpinner = null;
|
|
3182
|
+
if (!this.chatView) {
|
|
3183
|
+
syncSpinner = ora({
|
|
3184
|
+
text: `Syncing ${name} index...`,
|
|
3185
|
+
color: "cyan",
|
|
3186
|
+
}).start();
|
|
3365
3187
|
}
|
|
3366
|
-
|
|
3367
|
-
|
|
3188
|
+
await syncRecallIndex(this.teammatesDir, name);
|
|
3189
|
+
if (syncSpinner)
|
|
3190
|
+
syncSpinner.succeed(`${name}: index synced`);
|
|
3191
|
+
if (this.chatView) {
|
|
3192
|
+
this.chatView.setProgress(null);
|
|
3193
|
+
this.feedLine(tp.success(` ✔ ${name}: index synced`));
|
|
3194
|
+
}
|
|
3195
|
+
}
|
|
3196
|
+
catch {
|
|
3197
|
+
/* sync failed — non-fatal */
|
|
3198
|
+
}
|
|
3199
|
+
// Queue wisdom distillation agent task
|
|
3200
|
+
try {
|
|
3201
|
+
const teammateDir = join(this.teammatesDir, name);
|
|
3202
|
+
const wisdomPrompt = await buildWisdomPrompt(teammateDir, name);
|
|
3203
|
+
if (wisdomPrompt) {
|
|
3204
|
+
this.taskQueue.push({
|
|
3205
|
+
type: "agent",
|
|
3206
|
+
teammate: name,
|
|
3207
|
+
task: wisdomPrompt,
|
|
3208
|
+
});
|
|
3209
|
+
if (this.chatView)
|
|
3210
|
+
this.feedLine(tp.muted(` ↻ ${name}: queued wisdom distillation`));
|
|
3368
3211
|
}
|
|
3369
3212
|
}
|
|
3213
|
+
catch {
|
|
3214
|
+
/* wisdom prompt build failed — non-fatal */
|
|
3215
|
+
}
|
|
3370
3216
|
}
|
|
3371
3217
|
catch (err) {
|
|
3372
3218
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -3462,9 +3308,9 @@ Issues that can't be resolved unilaterally — they need input from other teamma
|
|
|
3462
3308
|
const fullPath = join(dir, entry.name);
|
|
3463
3309
|
if (entry.isDirectory()) {
|
|
3464
3310
|
await this.cleanOldTempFiles(fullPath, maxAgeMs);
|
|
3465
|
-
// Remove dir if now empty — but skip
|
|
3466
|
-
//
|
|
3467
|
-
if (entry.name !== "sessions") {
|
|
3311
|
+
// Remove dir if now empty — but skip structural dirs that are
|
|
3312
|
+
// recreated concurrently (sessions by startSession, debug by writeDebugEntry).
|
|
3313
|
+
if (entry.name !== "sessions" && entry.name !== "debug") {
|
|
3468
3314
|
const remaining = await readdir(fullPath).catch(() => [""]);
|
|
3469
3315
|
if (remaining.length === 0)
|
|
3470
3316
|
await rm(fullPath, { recursive: true }).catch(() => { });
|
|
@@ -3479,8 +3325,16 @@ Issues that can't be resolved unilaterally — they need input from other teamma
|
|
|
3479
3325
|
}
|
|
3480
3326
|
}
|
|
3481
3327
|
async startupMaintenance() {
|
|
3482
|
-
// Clean up .teammates/.tmp files older than 1 week
|
|
3483
3328
|
const tmpDir = join(this.teammatesDir, ".tmp");
|
|
3329
|
+
// Clean up debug log files older than 1 day
|
|
3330
|
+
const debugDir = join(tmpDir, "debug");
|
|
3331
|
+
try {
|
|
3332
|
+
await this.cleanOldTempFiles(debugDir, 24 * 60 * 60 * 1000);
|
|
3333
|
+
}
|
|
3334
|
+
catch {
|
|
3335
|
+
/* debug dir may not exist yet — non-fatal */
|
|
3336
|
+
}
|
|
3337
|
+
// Clean up other .tmp files older than 1 week
|
|
3484
3338
|
try {
|
|
3485
3339
|
await this.cleanOldTempFiles(tmpDir, 7 * 24 * 60 * 60 * 1000);
|
|
3486
3340
|
}
|
|
@@ -3492,8 +3346,6 @@ Issues that can't be resolved unilaterally — they need input from other teamma
|
|
|
3492
3346
|
.filter((n) => n !== this.adapterName);
|
|
3493
3347
|
if (teammates.length === 0)
|
|
3494
3348
|
return;
|
|
3495
|
-
// Check if recall is installed
|
|
3496
|
-
const recallInstalled = this.isServiceInstalled("recall");
|
|
3497
3349
|
// 1. Check each teammate for stale daily logs (older than 7 days)
|
|
3498
3350
|
const oneWeekAgo = new Date();
|
|
3499
3351
|
oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
|
|
@@ -3523,14 +3375,12 @@ Issues that can't be resolved unilaterally — they need input from other teamma
|
|
|
3523
3375
|
await this.runCompact(name);
|
|
3524
3376
|
}
|
|
3525
3377
|
}
|
|
3526
|
-
// 2. Sync recall indexes
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
/* sync failed — non-fatal */
|
|
3533
|
-
}
|
|
3378
|
+
// 2. Sync recall indexes (bundled library call)
|
|
3379
|
+
try {
|
|
3380
|
+
await syncRecallIndex(this.teammatesDir);
|
|
3381
|
+
}
|
|
3382
|
+
catch {
|
|
3383
|
+
/* sync failed — non-fatal */
|
|
3534
3384
|
}
|
|
3535
3385
|
}
|
|
3536
3386
|
async cmdCopy() {
|