@teammates/cli 0.2.8 → 0.3.1
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.js +34 -5
- package/dist/adapters/copilot.js +20 -1
- package/dist/cli.js +192 -383
- 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/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));
|
|
@@ -2287,6 +2220,15 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2287
2220
|
: `xdg-open ${quoted}`;
|
|
2288
2221
|
execCb(cmd, () => { });
|
|
2289
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
|
+
});
|
|
2290
2232
|
this.app = new App({
|
|
2291
2233
|
root: this.chatView,
|
|
2292
2234
|
alternateScreen: true,
|
|
@@ -2456,15 +2398,11 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2456
2398
|
printBanner(teammates) {
|
|
2457
2399
|
const registry = this.orchestrator.getRegistry();
|
|
2458
2400
|
const termWidth = process.stdout.columns || 100;
|
|
2459
|
-
// Detect recall from settings.json
|
|
2460
|
-
const recallInstalled = this.isServiceInstalled("recall");
|
|
2461
2401
|
this.feedLine();
|
|
2462
2402
|
this.feedLine(concat(tp.bold(" Teammates"), tp.muted(` v${PKG_VERSION}`)));
|
|
2463
2403
|
this.feedLine(concat(tp.text(` ${this.adapterName}`), tp.muted(` · ${teammates.length} teammate${teammates.length === 1 ? "" : "s"}`)));
|
|
2464
2404
|
this.feedLine(` ${process.cwd()}`);
|
|
2465
|
-
this.feedLine(
|
|
2466
|
-
? tp.success(" ● recall installed")
|
|
2467
|
-
: tp.warning(" ○ recall not installed"));
|
|
2405
|
+
this.feedLine(concat(tp.success(" ● recall"), tp.muted(" bundled")));
|
|
2468
2406
|
// Roster
|
|
2469
2407
|
this.feedLine();
|
|
2470
2408
|
for (const name of teammates) {
|
|
@@ -2483,11 +2421,11 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2483
2421
|
// First run — no teammates yet
|
|
2484
2422
|
col1 = [
|
|
2485
2423
|
["/init", "set up teammates"],
|
|
2486
|
-
["/
|
|
2424
|
+
["/help", "all commands"],
|
|
2487
2425
|
];
|
|
2488
2426
|
col2 = [
|
|
2489
|
-
["/help", "all commands"],
|
|
2490
2427
|
["/exit", "exit session"],
|
|
2428
|
+
["", ""],
|
|
2491
2429
|
];
|
|
2492
2430
|
col3 = [
|
|
2493
2431
|
["", ""],
|
|
@@ -2506,10 +2444,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2506
2444
|
["/retro", "run retrospective"],
|
|
2507
2445
|
];
|
|
2508
2446
|
col3 = [
|
|
2509
|
-
[
|
|
2510
|
-
recallInstalled ? "/copy" : "/install",
|
|
2511
|
-
recallInstalled ? "copy session text" : "add a service",
|
|
2512
|
-
],
|
|
2447
|
+
["/copy", "copy session text"],
|
|
2513
2448
|
["/help", "all commands"],
|
|
2514
2449
|
["/exit", "exit session"],
|
|
2515
2450
|
];
|
|
@@ -2540,7 +2475,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2540
2475
|
name: "debug",
|
|
2541
2476
|
aliases: ["raw"],
|
|
2542
2477
|
usage: "/debug [teammate]",
|
|
2543
|
-
description: "
|
|
2478
|
+
description: "Analyze the last agent task with the coding agent",
|
|
2544
2479
|
run: (args) => this.cmdDebug(args),
|
|
2545
2480
|
},
|
|
2546
2481
|
{
|
|
@@ -2564,13 +2499,6 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2564
2499
|
description: "Clear history and reset the session",
|
|
2565
2500
|
run: () => this.cmdClear(),
|
|
2566
2501
|
},
|
|
2567
|
-
{
|
|
2568
|
-
name: "install",
|
|
2569
|
-
aliases: [],
|
|
2570
|
-
usage: "/install [service]",
|
|
2571
|
-
description: "Install a teammates service (e.g. recall)",
|
|
2572
|
-
run: (args) => this.cmdInstall(args),
|
|
2573
|
-
},
|
|
2574
2502
|
{
|
|
2575
2503
|
name: "compact",
|
|
2576
2504
|
aliases: [],
|
|
@@ -2620,7 +2548,6 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2620
2548
|
description: "Exit the session",
|
|
2621
2549
|
run: async () => {
|
|
2622
2550
|
this.feedLine(tp.muted("Shutting down..."));
|
|
2623
|
-
this.stopRecallWatch();
|
|
2624
2551
|
if (this.app)
|
|
2625
2552
|
this.app.stop();
|
|
2626
2553
|
await this.orchestrator.shutdown();
|
|
@@ -2812,99 +2739,81 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2812
2739
|
}
|
|
2813
2740
|
async cmdDebug(argsStr) {
|
|
2814
2741
|
const arg = argsStr.trim().replace(/^@/, "");
|
|
2815
|
-
// Resolve which
|
|
2816
|
-
let
|
|
2742
|
+
// Resolve which teammate to debug
|
|
2743
|
+
let targetName;
|
|
2817
2744
|
if (arg === "everyone") {
|
|
2818
|
-
|
|
2819
|
-
|
|
2745
|
+
// Pick all teammates with debug files, queue one analysis per teammate
|
|
2746
|
+
const names = [];
|
|
2747
|
+
for (const [name] of this.lastDebugFiles) {
|
|
2820
2748
|
if (name !== this.adapterName)
|
|
2821
|
-
|
|
2749
|
+
names.push(name);
|
|
2822
2750
|
}
|
|
2823
|
-
if (
|
|
2751
|
+
if (names.length === 0) {
|
|
2824
2752
|
this.feedLine(tp.muted(" No debug info available from any teammate."));
|
|
2825
2753
|
this.refreshView();
|
|
2826
2754
|
return;
|
|
2827
2755
|
}
|
|
2756
|
+
for (const name of names) {
|
|
2757
|
+
this.queueDebugAnalysis(name);
|
|
2758
|
+
}
|
|
2759
|
+
return;
|
|
2828
2760
|
}
|
|
2829
2761
|
else if (arg) {
|
|
2830
|
-
|
|
2762
|
+
targetName = arg;
|
|
2831
2763
|
}
|
|
2832
2764
|
else if (this.lastResult) {
|
|
2833
|
-
|
|
2765
|
+
targetName = this.lastResult.teammate;
|
|
2834
2766
|
}
|
|
2835
2767
|
else {
|
|
2836
2768
|
this.feedLine(tp.muted(" No debug info available. Try: /debug [teammate]"));
|
|
2837
2769
|
this.refreshView();
|
|
2838
2770
|
return;
|
|
2839
2771
|
}
|
|
2840
|
-
|
|
2841
|
-
for (const name of targetNames) {
|
|
2842
|
-
this.feedLine();
|
|
2843
|
-
this.feedLine(tp.muted(` ── debug for ${name} ──`));
|
|
2844
|
-
// Read the last debug entry from the session file
|
|
2845
|
-
const sessionEntry = this.readLastDebugEntry(name);
|
|
2846
|
-
if (sessionEntry) {
|
|
2847
|
-
this.feedLine();
|
|
2848
|
-
this.feedMarkdown(sessionEntry);
|
|
2849
|
-
debugText += (debugText ? "\n\n" : "") + sessionEntry;
|
|
2850
|
-
}
|
|
2851
|
-
else {
|
|
2852
|
-
// Fall back to raw output from lastResults
|
|
2853
|
-
const result = this.lastResults.get(name);
|
|
2854
|
-
if (result?.rawOutput) {
|
|
2855
|
-
this.feedLine();
|
|
2856
|
-
this.feedMarkdown(result.rawOutput);
|
|
2857
|
-
debugText += (debugText ? "\n\n" : "") + result.rawOutput;
|
|
2858
|
-
}
|
|
2859
|
-
else {
|
|
2860
|
-
this.feedLine(tp.muted(" No debug info available."));
|
|
2861
|
-
}
|
|
2862
|
-
}
|
|
2863
|
-
this.feedLine();
|
|
2864
|
-
this.feedLine(tp.muted(" ── end debug ──"));
|
|
2865
|
-
}
|
|
2866
|
-
// [copy] action for the debug output
|
|
2867
|
-
if (this.chatView && debugText) {
|
|
2868
|
-
const t = theme();
|
|
2869
|
-
this.lastCleanedOutput = debugText;
|
|
2870
|
-
this.chatView.appendActionList([
|
|
2871
|
-
{
|
|
2872
|
-
id: "copy",
|
|
2873
|
-
normalStyle: this.makeSpan({
|
|
2874
|
-
text: " [copy]",
|
|
2875
|
-
style: { fg: t.textDim },
|
|
2876
|
-
}),
|
|
2877
|
-
hoverStyle: this.makeSpan({
|
|
2878
|
-
text: " [copy]",
|
|
2879
|
-
style: { fg: t.accent },
|
|
2880
|
-
}),
|
|
2881
|
-
},
|
|
2882
|
-
]);
|
|
2883
|
-
}
|
|
2884
|
-
this.feedLine();
|
|
2885
|
-
this.refreshView();
|
|
2772
|
+
this.queueDebugAnalysis(targetName);
|
|
2886
2773
|
}
|
|
2887
2774
|
/**
|
|
2888
|
-
*
|
|
2889
|
-
*
|
|
2775
|
+
* Queue a debug analysis task — sends the last request + debug log
|
|
2776
|
+
* to the base coding agent for analysis.
|
|
2890
2777
|
*/
|
|
2891
|
-
|
|
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;
|
|
2892
2788
|
try {
|
|
2893
|
-
|
|
2894
|
-
if (!sessionFile)
|
|
2895
|
-
return null;
|
|
2896
|
-
const content = readFileSync(sessionFile, "utf-8");
|
|
2897
|
-
// Split on debug entry headings and return the last one
|
|
2898
|
-
const entries = content.split(/(?=^## Debug — )/m);
|
|
2899
|
-
const last = entries[entries.length - 1];
|
|
2900
|
-
if (last && last.startsWith("## Debug — ")) {
|
|
2901
|
-
return last.trim();
|
|
2902
|
-
}
|
|
2903
|
-
return null;
|
|
2789
|
+
debugContent = readFileSync(debugFile, "utf-8");
|
|
2904
2790
|
}
|
|
2905
2791
|
catch {
|
|
2906
|
-
|
|
2792
|
+
this.feedLine(tp.muted(` Could not read debug log: ${debugFile}`));
|
|
2793
|
+
this.refreshView();
|
|
2794
|
+
return;
|
|
2907
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();
|
|
2908
2817
|
}
|
|
2909
2818
|
async cmdCancel(argsStr) {
|
|
2910
2819
|
const n = parseInt(argsStr.trim(), 10);
|
|
@@ -2936,17 +2845,21 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2936
2845
|
await this.runCompact(entry.teammate);
|
|
2937
2846
|
}
|
|
2938
2847
|
else {
|
|
2939
|
-
// btw tasks skip conversation context (
|
|
2940
|
-
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();
|
|
2941
2852
|
const result = await this.orchestrator.assign({
|
|
2942
2853
|
teammate: entry.teammate,
|
|
2943
2854
|
task: entry.task,
|
|
2944
2855
|
extraContext: extraContext || undefined,
|
|
2945
2856
|
});
|
|
2946
|
-
// Write debug entry
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
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") {
|
|
2950
2863
|
this.storeResult(result);
|
|
2951
2864
|
}
|
|
2952
2865
|
if (entry.type === "retro") {
|
|
@@ -2969,29 +2882,42 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2969
2882
|
}
|
|
2970
2883
|
}
|
|
2971
2884
|
/**
|
|
2972
|
-
*
|
|
2973
|
-
*
|
|
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.
|
|
2974
2887
|
*/
|
|
2975
2888
|
writeDebugEntry(teammate, task, result, startTime, error) {
|
|
2976
2889
|
try {
|
|
2977
|
-
const
|
|
2978
|
-
|
|
2890
|
+
const debugDir = join(this.teammatesDir, ".tmp", "debug");
|
|
2891
|
+
try {
|
|
2892
|
+
mkdirSync(debugDir, { recursive: true });
|
|
2893
|
+
}
|
|
2894
|
+
catch {
|
|
2979
2895
|
return;
|
|
2896
|
+
}
|
|
2980
2897
|
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
2981
2898
|
const timestamp = new Date().toISOString();
|
|
2899
|
+
const ts = timestamp.replace(/[:.]/g, "-");
|
|
2900
|
+
const debugFile = join(debugDir, `${teammate}-${ts}.md`);
|
|
2982
2901
|
const lines = [
|
|
2902
|
+
`# Debug — ${teammate}`,
|
|
2983
2903
|
"",
|
|
2984
|
-
|
|
2985
|
-
"",
|
|
2904
|
+
`**Timestamp:** ${timestamp}`,
|
|
2986
2905
|
`**Duration:** ${elapsed}s`,
|
|
2987
|
-
|
|
2906
|
+
"",
|
|
2907
|
+
"## Request",
|
|
2908
|
+
"",
|
|
2909
|
+
task,
|
|
2988
2910
|
"",
|
|
2989
2911
|
];
|
|
2990
2912
|
if (error) {
|
|
2913
|
+
lines.push("## Result");
|
|
2914
|
+
lines.push("");
|
|
2991
2915
|
lines.push(`**Status:** ERROR`);
|
|
2992
2916
|
lines.push(`**Error:** ${error?.message ?? String(error)}`);
|
|
2993
2917
|
}
|
|
2994
2918
|
else if (result) {
|
|
2919
|
+
lines.push("## Result");
|
|
2920
|
+
lines.push("");
|
|
2995
2921
|
lines.push(`**Status:** ${result.success ? "OK" : "FAILED"}`);
|
|
2996
2922
|
lines.push(`**Summary:** ${result.summary || "(no summary)"}`);
|
|
2997
2923
|
if (result.changedFiles.length > 0) {
|
|
@@ -3010,30 +2936,36 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3010
2936
|
lines.push(`**Signal:** ${diag.signal}`);
|
|
3011
2937
|
if (diag.timedOut)
|
|
3012
2938
|
lines.push(`**Timed out:** yes`);
|
|
3013
|
-
if (diag.debugFile)
|
|
3014
|
-
lines.push(`**
|
|
3015
|
-
|
|
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
|
+
}
|
|
3016
2953
|
if (diag.stderr.trim()) {
|
|
3017
2954
|
lines.push("");
|
|
3018
|
-
lines.push("
|
|
2955
|
+
lines.push("### stderr");
|
|
3019
2956
|
lines.push("");
|
|
3020
2957
|
lines.push(diag.stderr);
|
|
3021
|
-
lines.push("");
|
|
3022
|
-
lines.push("</details>");
|
|
3023
|
-
}
|
|
3024
|
-
else {
|
|
3025
|
-
lines.push(`**stderr:** (empty)`);
|
|
3026
2958
|
}
|
|
3027
2959
|
}
|
|
3028
2960
|
lines.push("");
|
|
3029
|
-
lines.push("
|
|
2961
|
+
lines.push("### Raw Output");
|
|
3030
2962
|
lines.push("");
|
|
3031
2963
|
lines.push(result.rawOutput ?? "(empty)");
|
|
3032
|
-
lines.push("");
|
|
3033
|
-
lines.push("</details>");
|
|
3034
2964
|
}
|
|
3035
2965
|
lines.push("");
|
|
3036
|
-
|
|
2966
|
+
writeFileSync(debugFile, lines.join("\n"), "utf-8");
|
|
2967
|
+
this.lastDebugFiles.set(teammate, debugFile);
|
|
2968
|
+
this.lastTaskPrompts.set(teammate, task);
|
|
3037
2969
|
}
|
|
3038
2970
|
catch {
|
|
3039
2971
|
// Don't let debug logging break task execution
|
|
@@ -3111,123 +3043,6 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3111
3043
|
this.feedLine(tp.muted(" Run /status to see the roster."));
|
|
3112
3044
|
this.refreshView();
|
|
3113
3045
|
}
|
|
3114
|
-
async cmdInstall(argsStr) {
|
|
3115
|
-
const serviceName = argsStr.trim().toLowerCase();
|
|
3116
|
-
if (!serviceName) {
|
|
3117
|
-
this.feedLine(tp.bold("\n Available services:"));
|
|
3118
|
-
for (const [name, svc] of Object.entries(SERVICE_REGISTRY)) {
|
|
3119
|
-
this.feedLine(concat(tp.accent(name.padEnd(16)), tp.muted(svc.description)));
|
|
3120
|
-
}
|
|
3121
|
-
this.feedLine();
|
|
3122
|
-
this.refreshView();
|
|
3123
|
-
return;
|
|
3124
|
-
}
|
|
3125
|
-
const service = SERVICE_REGISTRY[serviceName];
|
|
3126
|
-
if (!service) {
|
|
3127
|
-
this.feedLine(tp.warning(` Unknown service: ${serviceName}`));
|
|
3128
|
-
this.feedLine(tp.muted(` Available: ${Object.keys(SERVICE_REGISTRY).join(", ")}`));
|
|
3129
|
-
this.refreshView();
|
|
3130
|
-
return;
|
|
3131
|
-
}
|
|
3132
|
-
// Install the package globally
|
|
3133
|
-
if (this.chatView) {
|
|
3134
|
-
this.chatView.setProgress(`Installing ${service.package}...`);
|
|
3135
|
-
this.refreshView();
|
|
3136
|
-
}
|
|
3137
|
-
let installSpinner = null;
|
|
3138
|
-
if (!this.chatView) {
|
|
3139
|
-
installSpinner = ora({
|
|
3140
|
-
text: chalk.blue(serviceName) +
|
|
3141
|
-
chalk.gray(` installing ${service.package}...`),
|
|
3142
|
-
spinner: "dots",
|
|
3143
|
-
}).start();
|
|
3144
|
-
}
|
|
3145
|
-
try {
|
|
3146
|
-
await execAsync(`npm install -g ${service.package}`, {
|
|
3147
|
-
timeout: 5 * 60 * 1000,
|
|
3148
|
-
});
|
|
3149
|
-
if (installSpinner)
|
|
3150
|
-
installSpinner.stop();
|
|
3151
|
-
if (this.chatView)
|
|
3152
|
-
this.chatView.setProgress(null);
|
|
3153
|
-
}
|
|
3154
|
-
catch (err) {
|
|
3155
|
-
if (installSpinner)
|
|
3156
|
-
installSpinner.fail(chalk.red(`Install failed: ${err.message}`));
|
|
3157
|
-
if (this.chatView) {
|
|
3158
|
-
this.chatView.setProgress(null);
|
|
3159
|
-
this.feedLine(tp.error(` ✖ Install failed: ${err.message}`));
|
|
3160
|
-
this.refreshView();
|
|
3161
|
-
}
|
|
3162
|
-
return;
|
|
3163
|
-
}
|
|
3164
|
-
// Verify the binary works
|
|
3165
|
-
const checkCmdStr = service.checkCmd.join(" ");
|
|
3166
|
-
try {
|
|
3167
|
-
execSync(checkCmdStr, { stdio: "ignore" });
|
|
3168
|
-
}
|
|
3169
|
-
catch {
|
|
3170
|
-
this.feedLine(tp.success(` ✔ ${serviceName} installed`));
|
|
3171
|
-
this.feedLine(tp.warning(` ⚠ Restart your terminal to add ${service.checkCmd[0]} to your PATH, then run /install ${serviceName} again to build the index.`));
|
|
3172
|
-
this.refreshView();
|
|
3173
|
-
return;
|
|
3174
|
-
}
|
|
3175
|
-
this.feedLine(tp.success(` ✔ ${serviceName} installed successfully`));
|
|
3176
|
-
// Register in settings.json
|
|
3177
|
-
const settings = this.readSettings();
|
|
3178
|
-
if (!settings.services.some((s) => s.name === serviceName)) {
|
|
3179
|
-
settings.services.push({ name: serviceName });
|
|
3180
|
-
this.writeSettings(settings);
|
|
3181
|
-
this.feedLine(tp.muted(` Registered in settings.json`));
|
|
3182
|
-
}
|
|
3183
|
-
// Build initial index if this service supports it
|
|
3184
|
-
if (service.indexCmd) {
|
|
3185
|
-
if (this.chatView) {
|
|
3186
|
-
this.chatView.setProgress(`Building ${serviceName} index...`);
|
|
3187
|
-
this.refreshView();
|
|
3188
|
-
}
|
|
3189
|
-
let idxSpinner = null;
|
|
3190
|
-
if (!this.chatView) {
|
|
3191
|
-
idxSpinner = ora({
|
|
3192
|
-
text: chalk.blue(serviceName) + chalk.gray(` building index...`),
|
|
3193
|
-
spinner: "dots",
|
|
3194
|
-
}).start();
|
|
3195
|
-
}
|
|
3196
|
-
const indexCmdStr = service.indexCmd.join(" ");
|
|
3197
|
-
try {
|
|
3198
|
-
await execAsync(indexCmdStr, {
|
|
3199
|
-
cwd: resolve(this.teammatesDir, ".."),
|
|
3200
|
-
timeout: 5 * 60 * 1000,
|
|
3201
|
-
});
|
|
3202
|
-
if (idxSpinner)
|
|
3203
|
-
idxSpinner.succeed(chalk.blue(serviceName) + chalk.gray(" index built"));
|
|
3204
|
-
if (this.chatView) {
|
|
3205
|
-
this.chatView.setProgress(null);
|
|
3206
|
-
this.feedLine(tp.success(` ✔ ${serviceName} index built`));
|
|
3207
|
-
}
|
|
3208
|
-
}
|
|
3209
|
-
catch (err) {
|
|
3210
|
-
if (idxSpinner)
|
|
3211
|
-
idxSpinner.warn(chalk.yellow(`Index build failed: ${err.message}`));
|
|
3212
|
-
if (this.chatView) {
|
|
3213
|
-
this.chatView.setProgress(null);
|
|
3214
|
-
this.feedLine(tp.warning(` ⚠ Index build failed: ${err.message}`));
|
|
3215
|
-
}
|
|
3216
|
-
}
|
|
3217
|
-
}
|
|
3218
|
-
// Ask the coding agent to wire the service into the project
|
|
3219
|
-
if (service.wireupTask) {
|
|
3220
|
-
this.feedLine();
|
|
3221
|
-
this.feedLine(tp.muted(` Wiring up ${serviceName}...`));
|
|
3222
|
-
this.refreshView();
|
|
3223
|
-
const result = await this.orchestrator.assign({
|
|
3224
|
-
teammate: this.adapterName,
|
|
3225
|
-
task: service.wireupTask,
|
|
3226
|
-
});
|
|
3227
|
-
this.storeResult(result);
|
|
3228
|
-
}
|
|
3229
|
-
this.refreshView();
|
|
3230
|
-
}
|
|
3231
3046
|
async cmdClear() {
|
|
3232
3047
|
this.conversationHistory.length = 0;
|
|
3233
3048
|
this.lastResult = null;
|
|
@@ -3276,35 +3091,10 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3276
3091
|
})
|
|
3277
3092
|
.catch(() => { });
|
|
3278
3093
|
}
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
if (!this.isServiceInstalled("recall"))
|
|
3282
|
-
return;
|
|
3283
|
-
try {
|
|
3284
|
-
this.recallWatchProcess = cpSpawn("teammates-recall", ["watch", "--dir", this.teammatesDir, "--json"], {
|
|
3285
|
-
stdio: ["ignore", "ignore", "ignore"],
|
|
3286
|
-
detached: false,
|
|
3287
|
-
});
|
|
3288
|
-
this.recallWatchProcess.on("error", () => {
|
|
3289
|
-
// Recall binary not found — silently ignore
|
|
3290
|
-
this.recallWatchProcess = null;
|
|
3291
|
-
});
|
|
3292
|
-
this.recallWatchProcess.on("exit", () => {
|
|
3293
|
-
this.recallWatchProcess = null;
|
|
3294
|
-
});
|
|
3295
|
-
}
|
|
3296
|
-
catch {
|
|
3297
|
-
this.recallWatchProcess = null;
|
|
3298
|
-
}
|
|
3299
|
-
}
|
|
3300
|
-
stopRecallWatch() {
|
|
3301
|
-
if (this.recallWatchProcess) {
|
|
3302
|
-
this.recallWatchProcess.kill("SIGTERM");
|
|
3303
|
-
this.recallWatchProcess = null;
|
|
3304
|
-
}
|
|
3305
|
-
}
|
|
3094
|
+
// Recall is now bundled as a library dependency — no watch process needed.
|
|
3095
|
+
// Sync happens via syncRecallIndex() after every task and on startup.
|
|
3306
3096
|
async cmdCompact(argsStr) {
|
|
3307
|
-
const arg = argsStr.trim();
|
|
3097
|
+
const arg = argsStr.trim().replace(/^@/, "");
|
|
3308
3098
|
const allTeammates = this.orchestrator
|
|
3309
3099
|
.listTeammates()
|
|
3310
3100
|
.filter((n) => n !== this.adapterName);
|
|
@@ -3382,31 +3172,46 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3382
3172
|
}
|
|
3383
3173
|
if (this.chatView)
|
|
3384
3174
|
this.chatView.setProgress(null);
|
|
3385
|
-
//
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
this.refreshView();
|
|
3391
|
-
}
|
|
3392
|
-
let syncSpinner = null;
|
|
3393
|
-
if (!this.chatView) {
|
|
3394
|
-
syncSpinner = ora({
|
|
3395
|
-
text: `Syncing ${name} index...`,
|
|
3396
|
-
color: "cyan",
|
|
3397
|
-
}).start();
|
|
3398
|
-
}
|
|
3399
|
-
await execAsync(`teammates-recall sync --dir "${this.teammatesDir}"`);
|
|
3400
|
-
if (syncSpinner)
|
|
3401
|
-
syncSpinner.succeed(`${name}: index synced`);
|
|
3402
|
-
if (this.chatView) {
|
|
3403
|
-
this.chatView.setProgress(null);
|
|
3404
|
-
this.feedLine(tp.success(` ✔ ${name}: index synced`));
|
|
3405
|
-
}
|
|
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();
|
|
3406
3180
|
}
|
|
3407
|
-
|
|
3408
|
-
|
|
3181
|
+
let syncSpinner = null;
|
|
3182
|
+
if (!this.chatView) {
|
|
3183
|
+
syncSpinner = ora({
|
|
3184
|
+
text: `Syncing ${name} index...`,
|
|
3185
|
+
color: "cyan",
|
|
3186
|
+
}).start();
|
|
3409
3187
|
}
|
|
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`));
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
catch {
|
|
3214
|
+
/* wisdom prompt build failed — non-fatal */
|
|
3410
3215
|
}
|
|
3411
3216
|
}
|
|
3412
3217
|
catch (err) {
|
|
@@ -3503,9 +3308,9 @@ Issues that can't be resolved unilaterally — they need input from other teamma
|
|
|
3503
3308
|
const fullPath = join(dir, entry.name);
|
|
3504
3309
|
if (entry.isDirectory()) {
|
|
3505
3310
|
await this.cleanOldTempFiles(fullPath, maxAgeMs);
|
|
3506
|
-
// Remove dir if now empty — but skip
|
|
3507
|
-
//
|
|
3508
|
-
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") {
|
|
3509
3314
|
const remaining = await readdir(fullPath).catch(() => [""]);
|
|
3510
3315
|
if (remaining.length === 0)
|
|
3511
3316
|
await rm(fullPath, { recursive: true }).catch(() => { });
|
|
@@ -3520,8 +3325,16 @@ Issues that can't be resolved unilaterally — they need input from other teamma
|
|
|
3520
3325
|
}
|
|
3521
3326
|
}
|
|
3522
3327
|
async startupMaintenance() {
|
|
3523
|
-
// Clean up .teammates/.tmp files older than 1 week
|
|
3524
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
|
|
3525
3338
|
try {
|
|
3526
3339
|
await this.cleanOldTempFiles(tmpDir, 7 * 24 * 60 * 60 * 1000);
|
|
3527
3340
|
}
|
|
@@ -3533,8 +3346,6 @@ Issues that can't be resolved unilaterally — they need input from other teamma
|
|
|
3533
3346
|
.filter((n) => n !== this.adapterName);
|
|
3534
3347
|
if (teammates.length === 0)
|
|
3535
3348
|
return;
|
|
3536
|
-
// Check if recall is installed
|
|
3537
|
-
const recallInstalled = this.isServiceInstalled("recall");
|
|
3538
3349
|
// 1. Check each teammate for stale daily logs (older than 7 days)
|
|
3539
3350
|
const oneWeekAgo = new Date();
|
|
3540
3351
|
oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
|
|
@@ -3564,14 +3375,12 @@ Issues that can't be resolved unilaterally — they need input from other teamma
|
|
|
3564
3375
|
await this.runCompact(name);
|
|
3565
3376
|
}
|
|
3566
3377
|
}
|
|
3567
|
-
// 2. Sync recall indexes
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
/* sync failed — non-fatal */
|
|
3574
|
-
}
|
|
3378
|
+
// 2. Sync recall indexes (bundled library call)
|
|
3379
|
+
try {
|
|
3380
|
+
await syncRecallIndex(this.teammatesDir);
|
|
3381
|
+
}
|
|
3382
|
+
catch {
|
|
3383
|
+
/* sync failed — non-fatal */
|
|
3575
3384
|
}
|
|
3576
3385
|
}
|
|
3577
3386
|
async cmdCopy() {
|