@teammates/cli 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter.d.ts +2 -0
- package/dist/adapters/cli-proxy.d.ts +1 -0
- package/dist/adapters/cli-proxy.js +3 -0
- package/dist/adapters/copilot.d.ts +1 -0
- package/dist/adapters/copilot.js +3 -0
- package/dist/cli.js +223 -150
- package/dist/onboard.d.ts +10 -5
- package/dist/onboard.js +125 -45
- package/dist/registry.js +2 -1
- package/package.json +1 -1
package/dist/adapter.d.ts
CHANGED
|
@@ -24,6 +24,8 @@ export interface AgentAdapter {
|
|
|
24
24
|
* Falls back to startSession if not implemented.
|
|
25
25
|
*/
|
|
26
26
|
resumeSession?(teammate: TeammateConfig, sessionId: string): Promise<string>;
|
|
27
|
+
/** Get the session file path for a teammate (if session is active). */
|
|
28
|
+
getSessionFile?(teammateName: string): string | undefined;
|
|
27
29
|
/** Clean up a session. */
|
|
28
30
|
destroySession?(sessionId: string): Promise<void>;
|
|
29
31
|
/**
|
|
@@ -70,6 +70,7 @@ export declare class CliProxyAdapter implements AgentAdapter {
|
|
|
70
70
|
startSession(teammate: TeammateConfig): Promise<string>;
|
|
71
71
|
executeTask(_sessionId: string, teammate: TeammateConfig, prompt: string): Promise<TaskResult>;
|
|
72
72
|
routeTask(task: string, roster: RosterEntry[]): Promise<string | null>;
|
|
73
|
+
getSessionFile(teammateName: string): string | undefined;
|
|
73
74
|
destroySession(_sessionId: string): Promise<void>;
|
|
74
75
|
/**
|
|
75
76
|
* Spawn the agent, stream its output live, and capture it.
|
|
@@ -269,6 +269,9 @@ export class CliProxyAdapter {
|
|
|
269
269
|
await unlink(promptFile).catch(() => { });
|
|
270
270
|
}
|
|
271
271
|
}
|
|
272
|
+
getSessionFile(teammateName) {
|
|
273
|
+
return this.sessionFiles.get(teammateName);
|
|
274
|
+
}
|
|
272
275
|
async destroySession(_sessionId) {
|
|
273
276
|
// Clean up any leaked temp prompt files
|
|
274
277
|
for (const file of this.pendingTempFiles) {
|
|
@@ -42,6 +42,7 @@ export declare class CopilotAdapter implements AgentAdapter {
|
|
|
42
42
|
startSession(teammate: TeammateConfig): Promise<string>;
|
|
43
43
|
executeTask(_sessionId: string, teammate: TeammateConfig, prompt: string): Promise<TaskResult>;
|
|
44
44
|
routeTask(task: string, roster: RosterEntry[]): Promise<string | null>;
|
|
45
|
+
getSessionFile(teammateName: string): string | undefined;
|
|
45
46
|
destroySession(_sessionId: string): Promise<void>;
|
|
46
47
|
/**
|
|
47
48
|
* Ensure the CopilotClient is started.
|
package/dist/adapters/copilot.js
CHANGED
|
@@ -173,6 +173,9 @@ export class CopilotAdapter {
|
|
|
173
173
|
await session.disconnect().catch(() => { });
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
|
+
getSessionFile(teammateName) {
|
|
177
|
+
return this.sessionFiles.get(teammateName);
|
|
178
|
+
}
|
|
176
179
|
async destroySession(_sessionId) {
|
|
177
180
|
// Disconnect all sessions
|
|
178
181
|
for (const [, session] of this.sessions) {
|
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* teammates --dir <path> Override .teammates/ location
|
|
9
9
|
*/
|
|
10
10
|
import { spawn as cpSpawn, exec as execCb, execSync, } from "node:child_process";
|
|
11
|
-
import { readFileSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { appendFileSync, 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 { join, resolve } from "node:path";
|
|
@@ -19,13 +19,12 @@ import { App, ChatView, Control, concat, esc, Interview, pen, renderMarkdown, St
|
|
|
19
19
|
import chalk from "chalk";
|
|
20
20
|
import ora from "ora";
|
|
21
21
|
import { CliProxyAdapter, PRESETS } from "./adapters/cli-proxy.js";
|
|
22
|
-
import { CopilotAdapter } from "./adapters/copilot.js";
|
|
23
22
|
import { EchoAdapter } from "./adapters/echo.js";
|
|
24
23
|
import { findAtMention, isImagePath, relativeTime, wrapLine, } from "./cli-utils.js";
|
|
25
24
|
import { compactEpisodic } from "./compact.js";
|
|
26
25
|
import { PromptInput } from "./console/prompt-input.js";
|
|
27
26
|
import { buildTitle } from "./console/startup.js";
|
|
28
|
-
import {
|
|
27
|
+
import { buildImportAdaptationPrompt, copyTemplateFiles, getOnboardingPrompt, importTeammates, } from "./onboard.js";
|
|
29
28
|
import { Orchestrator } from "./orchestrator.js";
|
|
30
29
|
import { colorToHex, theme } from "./theme.js";
|
|
31
30
|
// ─── Version ─────────────────────────────────────────────────────────
|
|
@@ -87,11 +86,13 @@ async function findTeammatesDir() {
|
|
|
87
86
|
}
|
|
88
87
|
return null;
|
|
89
88
|
}
|
|
90
|
-
function resolveAdapter(name) {
|
|
89
|
+
async function resolveAdapter(name) {
|
|
91
90
|
if (name === "echo")
|
|
92
91
|
return new EchoAdapter();
|
|
93
|
-
// GitHub Copilot SDK adapter
|
|
92
|
+
// GitHub Copilot SDK adapter — lazy-loaded to avoid pulling in
|
|
93
|
+
// @github/copilot-sdk (and vscode-jsonrpc) when not needed.
|
|
94
94
|
if (name === "copilot") {
|
|
95
|
+
const { CopilotAdapter } = await import("./adapters/copilot.js");
|
|
95
96
|
return new CopilotAdapter({
|
|
96
97
|
model: modelOverride,
|
|
97
98
|
});
|
|
@@ -464,6 +465,28 @@ class TeammatesREPL {
|
|
|
464
465
|
lastCleanedOutput = ""; // last teammate output for clipboard copy
|
|
465
466
|
dispatching = false;
|
|
466
467
|
autoApproveHandoffs = false;
|
|
468
|
+
/** Read .teammates/settings.json (returns { version, services, ... } or defaults). */
|
|
469
|
+
readSettings() {
|
|
470
|
+
try {
|
|
471
|
+
const raw = JSON.parse(readFileSync(join(this.teammatesDir, "settings.json"), "utf-8"));
|
|
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
|
+
}
|
|
467
490
|
/** Pending handoffs awaiting user approval. */
|
|
468
491
|
pendingHandoffs = [];
|
|
469
492
|
/** Pending retro proposals awaiting user approval. */
|
|
@@ -1419,14 +1442,17 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1419
1442
|
chalk.cyan(teammates.join(", ")));
|
|
1420
1443
|
console.log(chalk.gray(` (${files.length} files copied)`));
|
|
1421
1444
|
console.log();
|
|
1445
|
+
// Copy framework files so the agent has TEMPLATE.md etc. available
|
|
1446
|
+
await copyTemplateFiles(teammatesDir);
|
|
1422
1447
|
// Ask if user wants the agent to adapt teammates to this codebase
|
|
1423
1448
|
console.log(chalk.white(" Adapt teammates to this codebase?"));
|
|
1424
|
-
console.log(chalk.gray(" The agent will
|
|
1449
|
+
console.log(chalk.gray(" The agent will scan this project, evaluate which teammates are needed,"));
|
|
1450
|
+
console.log(chalk.gray(" adapt their files, and create any new teammates the project needs."));
|
|
1425
1451
|
console.log(chalk.gray(" You can also do this later with /init."));
|
|
1426
1452
|
console.log();
|
|
1427
1453
|
const adapt = await this.askChoice("Adapt now? (y/n): ", ["y", "n"]);
|
|
1428
1454
|
if (adapt === "y") {
|
|
1429
|
-
await this.runAdaptationAgent(this.adapter, projectDir, teammates);
|
|
1455
|
+
await this.runAdaptationAgent(this.adapter, projectDir, teammates, sourceDir);
|
|
1430
1456
|
}
|
|
1431
1457
|
else {
|
|
1432
1458
|
console.log(chalk.gray(" Skipped adaptation. Run /init to adapt later."));
|
|
@@ -1438,53 +1464,52 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1438
1464
|
console.log();
|
|
1439
1465
|
}
|
|
1440
1466
|
/**
|
|
1441
|
-
* Run the agent to adapt imported teammates
|
|
1442
|
-
*
|
|
1443
|
-
*
|
|
1467
|
+
* Run the agent to adapt imported teammates to the current codebase.
|
|
1468
|
+
* Uses a single comprehensive session that scans the project, evaluates
|
|
1469
|
+
* which teammates to keep/drop/create, adapts kept teammates (with
|
|
1470
|
+
* Previous Projects sections), and creates any new teammates needed.
|
|
1444
1471
|
*/
|
|
1445
|
-
async runAdaptationAgent(adapter, projectDir, teammateNames) {
|
|
1472
|
+
async runAdaptationAgent(adapter, projectDir, teammateNames, sourceProjectPath) {
|
|
1446
1473
|
const teammatesDir = join(projectDir, ".teammates");
|
|
1447
1474
|
console.log();
|
|
1448
|
-
console.log(chalk.blue("
|
|
1449
|
-
chalk.gray(` ${this.adapterName} will adapt
|
|
1475
|
+
console.log(chalk.blue(" Starting adaptation...") +
|
|
1476
|
+
chalk.gray(` ${this.adapterName} will scan this project and adapt the team`));
|
|
1450
1477
|
console.log();
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
console.log(chalk.green(` ✔ @${name} adaptation complete!`));
|
|
1475
|
-
}
|
|
1476
|
-
else {
|
|
1477
|
-
console.log(chalk.yellow(` ⚠ @${name} adaptation finished with issues: ${result.summary}`));
|
|
1478
|
-
}
|
|
1479
|
-
}
|
|
1480
|
-
catch (err) {
|
|
1481
|
-
spinner.fail(chalk.red(`@${name} adaptation failed: ${err.message}`));
|
|
1478
|
+
const prompt = await buildImportAdaptationPrompt(teammatesDir, teammateNames, sourceProjectPath);
|
|
1479
|
+
const tempConfig = {
|
|
1480
|
+
name: this.adapterName,
|
|
1481
|
+
role: "Adaptation agent",
|
|
1482
|
+
soul: "",
|
|
1483
|
+
wisdom: "",
|
|
1484
|
+
dailyLogs: [],
|
|
1485
|
+
weeklyLogs: [],
|
|
1486
|
+
ownership: { primary: [], secondary: [] },
|
|
1487
|
+
routingKeywords: [],
|
|
1488
|
+
};
|
|
1489
|
+
const sessionId = await adapter.startSession(tempConfig);
|
|
1490
|
+
const spinner = ora({
|
|
1491
|
+
text: chalk.blue(this.adapterName) +
|
|
1492
|
+
chalk.gray(" is scanning the project and adapting teammates..."),
|
|
1493
|
+
spinner: "dots",
|
|
1494
|
+
}).start();
|
|
1495
|
+
try {
|
|
1496
|
+
const result = await adapter.executeTask(sessionId, tempConfig, prompt);
|
|
1497
|
+
spinner.stop();
|
|
1498
|
+
this.printAgentOutput(result.rawOutput);
|
|
1499
|
+
if (result.success) {
|
|
1500
|
+
console.log(chalk.green(" ✔ Team adaptation complete!"));
|
|
1482
1501
|
}
|
|
1483
|
-
|
|
1484
|
-
|
|
1502
|
+
else {
|
|
1503
|
+
console.log(chalk.yellow(` ⚠ Adaptation finished with issues: ${result.summary}`));
|
|
1485
1504
|
}
|
|
1486
|
-
console.log();
|
|
1487
1505
|
}
|
|
1506
|
+
catch (err) {
|
|
1507
|
+
spinner.fail(chalk.red(`Adaptation failed: ${err.message}`));
|
|
1508
|
+
}
|
|
1509
|
+
if (adapter.destroySession) {
|
|
1510
|
+
await adapter.destroySession(sessionId);
|
|
1511
|
+
}
|
|
1512
|
+
console.log();
|
|
1488
1513
|
}
|
|
1489
1514
|
/**
|
|
1490
1515
|
* Simple blocking prompt — reads one line from stdin and validates.
|
|
@@ -1919,7 +1944,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1919
1944
|
// ─── Lifecycle ────────────────────────────────────────────────────
|
|
1920
1945
|
async start() {
|
|
1921
1946
|
let teammatesDir = await findTeammatesDir();
|
|
1922
|
-
const adapter = resolveAdapter(this.adapterName);
|
|
1947
|
+
const adapter = await resolveAdapter(this.adapterName);
|
|
1923
1948
|
this.adapter = adapter;
|
|
1924
1949
|
// No .teammates/ found — offer onboarding or solo mode
|
|
1925
1950
|
if (!teammatesDir) {
|
|
@@ -1962,21 +1987,15 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
1962
1987
|
return { name: t.name, role: t.role, ownership: t.ownership };
|
|
1963
1988
|
});
|
|
1964
1989
|
}
|
|
1965
|
-
// Detect installed services from
|
|
1990
|
+
// Detect installed services from settings.json and tell the adapter
|
|
1966
1991
|
if ("services" in this.adapter) {
|
|
1967
1992
|
const services = [];
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
usage: 'teammates-recall search "your query" --dir .teammates',
|
|
1975
|
-
});
|
|
1976
|
-
}
|
|
1977
|
-
}
|
|
1978
|
-
catch {
|
|
1979
|
-
/* no services.json or invalid */
|
|
1993
|
+
if (this.isServiceInstalled("recall")) {
|
|
1994
|
+
services.push({
|
|
1995
|
+
name: "recall",
|
|
1996
|
+
description: "Local semantic search across teammate memories and daily logs. Use this to find relevant context before starting a task.",
|
|
1997
|
+
usage: 'teammates-recall search "your query" --dir .teammates',
|
|
1998
|
+
});
|
|
1980
1999
|
}
|
|
1981
2000
|
this.adapter.services = services;
|
|
1982
2001
|
}
|
|
@@ -2033,14 +2052,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2033
2052
|
// ── Build animated banner for ChatView ─────────────────────────────
|
|
2034
2053
|
const names = this.orchestrator.listTeammates();
|
|
2035
2054
|
const reg = this.orchestrator.getRegistry();
|
|
2036
|
-
|
|
2037
|
-
try {
|
|
2038
|
-
const svcJson = JSON.parse(readFileSync(join(this.teammatesDir, "services.json"), "utf-8"));
|
|
2039
|
-
hasRecall = !!(svcJson && "recall" in svcJson);
|
|
2040
|
-
}
|
|
2041
|
-
catch {
|
|
2042
|
-
/* no services.json */
|
|
2043
|
-
}
|
|
2055
|
+
const hasRecall = this.isServiceInstalled("recall");
|
|
2044
2056
|
const bannerWidget = new AnimatedBanner({
|
|
2045
2057
|
adapterName: this.adapterName,
|
|
2046
2058
|
teammateCount: names.length,
|
|
@@ -2431,15 +2443,8 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2431
2443
|
printBanner(teammates) {
|
|
2432
2444
|
const registry = this.orchestrator.getRegistry();
|
|
2433
2445
|
const termWidth = process.stdout.columns || 100;
|
|
2434
|
-
// Detect recall from
|
|
2435
|
-
|
|
2436
|
-
try {
|
|
2437
|
-
const svcJson = JSON.parse(readFileSync(join(this.teammatesDir, "services.json"), "utf-8"));
|
|
2438
|
-
recallInstalled = !!(svcJson && "recall" in svcJson);
|
|
2439
|
-
}
|
|
2440
|
-
catch {
|
|
2441
|
-
/* no services.json or invalid */
|
|
2442
|
-
}
|
|
2446
|
+
// Detect recall from settings.json
|
|
2447
|
+
const recallInstalled = this.isServiceInstalled("recall");
|
|
2443
2448
|
this.feedLine();
|
|
2444
2449
|
this.feedLine(concat(tp.bold(" Teammates"), tp.muted(` v${PKG_VERSION}`)));
|
|
2445
2450
|
this.feedLine(concat(tp.text(` ${this.adapterName}`), tp.muted(` · ${teammates.length} teammate${teammates.length === 1 ? "" : "s"}`)));
|
|
@@ -2781,45 +2786,61 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2781
2786
|
}
|
|
2782
2787
|
async cmdDebug(argsStr) {
|
|
2783
2788
|
const arg = argsStr.trim().replace(/^@/, "");
|
|
2784
|
-
// Resolve
|
|
2785
|
-
let
|
|
2789
|
+
// Resolve which teammates to show debug info for
|
|
2790
|
+
let targetNames;
|
|
2786
2791
|
if (arg === "everyone") {
|
|
2787
|
-
|
|
2788
|
-
for (const [name
|
|
2789
|
-
if (name !== this.adapterName
|
|
2790
|
-
|
|
2791
|
-
}
|
|
2792
|
+
targetNames = [];
|
|
2793
|
+
for (const [name] of this.lastResults) {
|
|
2794
|
+
if (name !== this.adapterName)
|
|
2795
|
+
targetNames.push(name);
|
|
2792
2796
|
}
|
|
2793
|
-
if (
|
|
2794
|
-
this.feedLine(tp.muted(" No
|
|
2797
|
+
if (targetNames.length === 0) {
|
|
2798
|
+
this.feedLine(tp.muted(" No debug info available from any teammate."));
|
|
2795
2799
|
this.refreshView();
|
|
2796
2800
|
return;
|
|
2797
2801
|
}
|
|
2798
2802
|
}
|
|
2803
|
+
else if (arg) {
|
|
2804
|
+
targetNames = [arg];
|
|
2805
|
+
}
|
|
2806
|
+
else if (this.lastResult) {
|
|
2807
|
+
targetNames = [this.lastResult.teammate];
|
|
2808
|
+
}
|
|
2799
2809
|
else {
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
(arg ? "" : " Try: /debug <teammate>")));
|
|
2804
|
-
this.refreshView();
|
|
2805
|
-
return;
|
|
2806
|
-
}
|
|
2807
|
-
targets = [{ name: result.teammate, result }];
|
|
2810
|
+
this.feedLine(tp.muted(" No debug info available. Try: /debug [teammate]"));
|
|
2811
|
+
this.refreshView();
|
|
2812
|
+
return;
|
|
2808
2813
|
}
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
this.feedLine(tp.muted(` ── raw output from ${name} ──`));
|
|
2814
|
+
let debugText = "";
|
|
2815
|
+
for (const name of targetNames) {
|
|
2812
2816
|
this.feedLine();
|
|
2813
|
-
this.
|
|
2817
|
+
this.feedLine(tp.muted(` ── debug for ${name} ──`));
|
|
2818
|
+
// Read the last debug entry from the session file
|
|
2819
|
+
const sessionEntry = this.readLastDebugEntry(name);
|
|
2820
|
+
if (sessionEntry) {
|
|
2821
|
+
this.feedLine();
|
|
2822
|
+
this.feedMarkdown(sessionEntry);
|
|
2823
|
+
debugText += (debugText ? "\n\n" : "") + sessionEntry;
|
|
2824
|
+
}
|
|
2825
|
+
else {
|
|
2826
|
+
// Fall back to raw output from lastResults
|
|
2827
|
+
const result = this.lastResults.get(name);
|
|
2828
|
+
if (result?.rawOutput) {
|
|
2829
|
+
this.feedLine();
|
|
2830
|
+
this.feedMarkdown(result.rawOutput);
|
|
2831
|
+
debugText += (debugText ? "\n\n" : "") + result.rawOutput;
|
|
2832
|
+
}
|
|
2833
|
+
else {
|
|
2834
|
+
this.feedLine(tp.muted(" No debug info available."));
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2814
2837
|
this.feedLine();
|
|
2815
|
-
this.feedLine(tp.muted(" ── end
|
|
2838
|
+
this.feedLine(tp.muted(" ── end debug ──"));
|
|
2816
2839
|
}
|
|
2817
2840
|
// [copy] action for the debug output
|
|
2818
|
-
if (this.chatView) {
|
|
2841
|
+
if (this.chatView && debugText) {
|
|
2819
2842
|
const t = theme();
|
|
2820
|
-
this.lastCleanedOutput =
|
|
2821
|
-
.map((t) => t.result.rawOutput)
|
|
2822
|
-
.join("\n\n");
|
|
2843
|
+
this.lastCleanedOutput = debugText;
|
|
2823
2844
|
this.chatView.appendActionList([
|
|
2824
2845
|
{
|
|
2825
2846
|
id: "copy",
|
|
@@ -2837,6 +2858,28 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2837
2858
|
this.feedLine();
|
|
2838
2859
|
this.refreshView();
|
|
2839
2860
|
}
|
|
2861
|
+
/**
|
|
2862
|
+
* Read the last debug entry from a teammate's session file.
|
|
2863
|
+
* Debug entries are delimited by "## Debug — " headings.
|
|
2864
|
+
*/
|
|
2865
|
+
readLastDebugEntry(teammate) {
|
|
2866
|
+
try {
|
|
2867
|
+
const sessionFile = this.adapter.getSessionFile?.(teammate);
|
|
2868
|
+
if (!sessionFile)
|
|
2869
|
+
return null;
|
|
2870
|
+
const content = readFileSync(sessionFile, "utf-8");
|
|
2871
|
+
// Split on debug entry headings and return the last one
|
|
2872
|
+
const entries = content.split(/(?=^## Debug — )/m);
|
|
2873
|
+
const last = entries[entries.length - 1];
|
|
2874
|
+
if (last && last.startsWith("## Debug — ")) {
|
|
2875
|
+
return last.trim();
|
|
2876
|
+
}
|
|
2877
|
+
return null;
|
|
2878
|
+
}
|
|
2879
|
+
catch {
|
|
2880
|
+
return null;
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2840
2883
|
async cmdCancel(argsStr) {
|
|
2841
2884
|
const n = parseInt(argsStr.trim(), 10);
|
|
2842
2885
|
if (Number.isNaN(n) || n < 1 || n > this.taskQueue.length) {
|
|
@@ -2861,6 +2904,7 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2861
2904
|
break;
|
|
2862
2905
|
const entry = this.taskQueue.splice(idx, 1)[0];
|
|
2863
2906
|
this.agentActive.set(agent, entry);
|
|
2907
|
+
const startTime = Date.now();
|
|
2864
2908
|
try {
|
|
2865
2909
|
if (entry.type === "compact") {
|
|
2866
2910
|
await this.runCompact(entry.teammate);
|
|
@@ -2873,6 +2917,8 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2873
2917
|
task: entry.task,
|
|
2874
2918
|
extraContext: extraContext || undefined,
|
|
2875
2919
|
});
|
|
2920
|
+
// Write debug entry to session file
|
|
2921
|
+
this.writeDebugEntry(entry.teammate, entry.task, result, startTime);
|
|
2876
2922
|
// btw results are not stored in conversation history
|
|
2877
2923
|
if (entry.type !== "btw") {
|
|
2878
2924
|
this.storeResult(result);
|
|
@@ -2883,6 +2929,8 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2883
2929
|
}
|
|
2884
2930
|
}
|
|
2885
2931
|
catch (err) {
|
|
2932
|
+
// Write error debug entry to session file
|
|
2933
|
+
this.writeDebugEntry(entry.teammate, entry.task, null, startTime, err);
|
|
2886
2934
|
// Handle spawn failures, network errors, etc. gracefully
|
|
2887
2935
|
this.activeTasks.delete(agent);
|
|
2888
2936
|
if (this.activeTasks.size === 0)
|
|
@@ -2894,6 +2942,52 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2894
2942
|
this.agentActive.delete(agent);
|
|
2895
2943
|
}
|
|
2896
2944
|
}
|
|
2945
|
+
/**
|
|
2946
|
+
* Append a debug entry to the teammate's session file.
|
|
2947
|
+
* Captures task prompt, result summary, raw output, timing, and errors.
|
|
2948
|
+
*/
|
|
2949
|
+
writeDebugEntry(teammate, task, result, startTime, error) {
|
|
2950
|
+
try {
|
|
2951
|
+
const sessionFile = this.adapter.getSessionFile?.(teammate);
|
|
2952
|
+
if (!sessionFile)
|
|
2953
|
+
return;
|
|
2954
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
2955
|
+
const timestamp = new Date().toISOString();
|
|
2956
|
+
const lines = [
|
|
2957
|
+
"",
|
|
2958
|
+
`## Debug — ${timestamp}`,
|
|
2959
|
+
"",
|
|
2960
|
+
`**Duration:** ${elapsed}s`,
|
|
2961
|
+
`**Task:** ${task.length > 200 ? `${task.slice(0, 200)}…` : task}`,
|
|
2962
|
+
"",
|
|
2963
|
+
];
|
|
2964
|
+
if (error) {
|
|
2965
|
+
lines.push(`**Status:** ERROR`);
|
|
2966
|
+
lines.push(`**Error:** ${error?.message ?? String(error)}`);
|
|
2967
|
+
}
|
|
2968
|
+
else if (result) {
|
|
2969
|
+
lines.push(`**Status:** ${result.success ? "OK" : "FAILED"}`);
|
|
2970
|
+
lines.push(`**Summary:** ${result.summary || "(no summary)"}`);
|
|
2971
|
+
if (result.changedFiles.length > 0) {
|
|
2972
|
+
lines.push(`**Changed files:** ${result.changedFiles.join(", ")}`);
|
|
2973
|
+
}
|
|
2974
|
+
if (result.handoffs.length > 0) {
|
|
2975
|
+
lines.push(`**Handoffs:** ${result.handoffs.map((h) => `@${h.to}`).join(", ")}`);
|
|
2976
|
+
}
|
|
2977
|
+
lines.push("");
|
|
2978
|
+
lines.push("<details><summary>Raw output</summary>");
|
|
2979
|
+
lines.push("");
|
|
2980
|
+
lines.push(result.rawOutput ?? "(empty)");
|
|
2981
|
+
lines.push("");
|
|
2982
|
+
lines.push("</details>");
|
|
2983
|
+
}
|
|
2984
|
+
lines.push("");
|
|
2985
|
+
appendFileSync(sessionFile, lines.join("\n"), "utf-8");
|
|
2986
|
+
}
|
|
2987
|
+
catch {
|
|
2988
|
+
// Don't let debug logging break task execution
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2897
2991
|
async cmdInit(argsStr) {
|
|
2898
2992
|
const cwd = process.cwd();
|
|
2899
2993
|
const teammatesDir = join(cwd, ".teammates");
|
|
@@ -2923,16 +3017,16 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
2923
3017
|
return;
|
|
2924
3018
|
}
|
|
2925
3019
|
this.feedLine(tp.success(` Imported ${teammates.length} teammate${teammates.length > 1 ? "s" : ""}: ${teammates.join(", ")} (${files.length} files)`));
|
|
2926
|
-
//
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
}
|
|
3020
|
+
// Copy framework files so the agent has TEMPLATE.md etc. available
|
|
3021
|
+
await copyTemplateFiles(teammatesDir);
|
|
3022
|
+
// Queue a single adaptation task that handles all teammates
|
|
3023
|
+
this.feedLine(tp.muted(` Queuing ${this.adapterName} to scan this project and adapt the team...`));
|
|
3024
|
+
const prompt = await buildImportAdaptationPrompt(teammatesDir, teammates, sourceDir);
|
|
3025
|
+
this.taskQueue.push({
|
|
3026
|
+
type: "agent",
|
|
3027
|
+
teammate: this.adapterName,
|
|
3028
|
+
task: prompt,
|
|
3029
|
+
});
|
|
2936
3030
|
this.kickDrain();
|
|
2937
3031
|
}
|
|
2938
3032
|
catch (err) {
|
|
@@ -3021,19 +3115,12 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3021
3115
|
return;
|
|
3022
3116
|
}
|
|
3023
3117
|
this.feedLine(tp.success(` ✔ ${serviceName} installed successfully`));
|
|
3024
|
-
// Register in
|
|
3025
|
-
const
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
catch {
|
|
3031
|
-
/* new file */
|
|
3032
|
-
}
|
|
3033
|
-
if (!(serviceName in svcJson)) {
|
|
3034
|
-
svcJson[serviceName] = {};
|
|
3035
|
-
writeFileSync(svcPath, `${JSON.stringify(svcJson, null, 2)}\n`);
|
|
3036
|
-
this.feedLine(tp.muted(` Registered in services.json`));
|
|
3118
|
+
// Register in settings.json
|
|
3119
|
+
const settings = this.readSettings();
|
|
3120
|
+
if (!settings.services.some((s) => s.name === serviceName)) {
|
|
3121
|
+
settings.services.push({ name: serviceName });
|
|
3122
|
+
this.writeSettings(settings);
|
|
3123
|
+
this.feedLine(tp.muted(` Registered in settings.json`));
|
|
3037
3124
|
}
|
|
3038
3125
|
// Build initial index if this service supports it
|
|
3039
3126
|
if (service.indexCmd) {
|
|
@@ -3132,15 +3219,9 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3132
3219
|
.catch(() => { });
|
|
3133
3220
|
}
|
|
3134
3221
|
startRecallWatch() {
|
|
3135
|
-
// Only start if recall is installed (check
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
if (!svcJson || !("recall" in svcJson))
|
|
3139
|
-
return;
|
|
3140
|
-
}
|
|
3141
|
-
catch {
|
|
3142
|
-
return; // No services.json — recall not installed
|
|
3143
|
-
}
|
|
3222
|
+
// Only start if recall is installed (check settings.json)
|
|
3223
|
+
if (!this.isServiceInstalled("recall"))
|
|
3224
|
+
return;
|
|
3144
3225
|
try {
|
|
3145
3226
|
this.recallWatchProcess = cpSpawn("teammates-recall", ["watch", "--dir", this.teammatesDir, "--json"], {
|
|
3146
3227
|
stdio: ["ignore", "ignore", "ignore"],
|
|
@@ -3244,9 +3325,8 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3244
3325
|
if (this.chatView)
|
|
3245
3326
|
this.chatView.setProgress(null);
|
|
3246
3327
|
// Trigger recall sync if installed
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
if (svcJson && "recall" in svcJson) {
|
|
3328
|
+
if (this.isServiceInstalled("recall")) {
|
|
3329
|
+
try {
|
|
3250
3330
|
if (this.chatView) {
|
|
3251
3331
|
this.chatView.setProgress(`Syncing ${name} index...`);
|
|
3252
3332
|
this.refreshView();
|
|
@@ -3266,9 +3346,9 @@ Do NOT modify any other teammate's files. Only edit your own SOUL.md and daily l
|
|
|
3266
3346
|
this.feedLine(tp.success(` ✔ ${name}: index synced`));
|
|
3267
3347
|
}
|
|
3268
3348
|
}
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3349
|
+
catch {
|
|
3350
|
+
/* sync failed — non-fatal */
|
|
3351
|
+
}
|
|
3272
3352
|
}
|
|
3273
3353
|
}
|
|
3274
3354
|
catch (err) {
|
|
@@ -3393,14 +3473,7 @@ Issues that can't be resolved unilaterally — they need input from other teamma
|
|
|
3393
3473
|
if (teammates.length === 0)
|
|
3394
3474
|
return;
|
|
3395
3475
|
// Check if recall is installed
|
|
3396
|
-
|
|
3397
|
-
try {
|
|
3398
|
-
const svcJson = JSON.parse(readFileSync(join(this.teammatesDir, "services.json"), "utf-8"));
|
|
3399
|
-
recallInstalled = !!(svcJson && "recall" in svcJson);
|
|
3400
|
-
}
|
|
3401
|
-
catch {
|
|
3402
|
-
/* no services.json */
|
|
3403
|
-
}
|
|
3476
|
+
const recallInstalled = this.isServiceInstalled("recall");
|
|
3404
3477
|
// 1. Check each teammate for stale daily logs (older than 7 days)
|
|
3405
3478
|
const oneWeekAgo = new Date();
|
|
3406
3479
|
oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
|
package/dist/onboard.d.ts
CHANGED
|
@@ -28,14 +28,19 @@ export declare function importTeammates(sourceDir: string, targetDir: string): P
|
|
|
28
28
|
files: string[];
|
|
29
29
|
}>;
|
|
30
30
|
/**
|
|
31
|
-
* Build
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* Build a comprehensive import-adaptation prompt that runs as a single agent session.
|
|
32
|
+
* The agent will:
|
|
33
|
+
* 1. Scan the current project
|
|
34
|
+
* 2. Evaluate which imported teammates are needed
|
|
35
|
+
* 3. Adapt kept teammates (add Previous Projects section + rewrite for new project)
|
|
36
|
+
* 4. Create any new teammates the project needs
|
|
37
|
+
* 5. Remove teammates that don't apply
|
|
34
38
|
*
|
|
35
39
|
* @param teammatesDir - The .teammates/ directory in the target project
|
|
36
|
-
* @param
|
|
40
|
+
* @param teammateNames - Names of all imported teammates
|
|
41
|
+
* @param sourceProjectPath - Path to the source project (for Previous Projects section)
|
|
37
42
|
*/
|
|
38
|
-
export declare function
|
|
43
|
+
export declare function buildImportAdaptationPrompt(teammatesDir: string, teammateNames: string[], sourceProjectPath: string): Promise<string>;
|
|
39
44
|
/**
|
|
40
45
|
* Load ONBOARDING.md from the project dir, package root, or built-in fallback.
|
|
41
46
|
*/
|
package/dist/onboard.js
CHANGED
|
@@ -60,7 +60,7 @@ export async function copyTemplateFiles(teammatesDir) {
|
|
|
60
60
|
await stat(gitignoreDest);
|
|
61
61
|
}
|
|
62
62
|
catch {
|
|
63
|
-
const gitignoreContent = "USER.md\n
|
|
63
|
+
const gitignoreContent = "USER.md\n.*/\n";
|
|
64
64
|
const { writeFile } = await import("node:fs/promises");
|
|
65
65
|
await writeFile(gitignoreDest, gitignoreContent, "utf-8");
|
|
66
66
|
copied.push(".gitignore");
|
|
@@ -92,7 +92,7 @@ export async function copyTemplateFiles(teammatesDir) {
|
|
|
92
92
|
* Directories starting with "_" are shared non-teammate folders.
|
|
93
93
|
* Files (non-directories) and special names are also excluded.
|
|
94
94
|
*/
|
|
95
|
-
const NON_TEAMMATE_NAMES = new Set(["example", "
|
|
95
|
+
const NON_TEAMMATE_NAMES = new Set(["example", "settings.json"]);
|
|
96
96
|
function isNonTeammateEntry(name) {
|
|
97
97
|
return (name.startsWith(".") || name.startsWith("_") || NON_TEAMMATE_NAMES.has(name));
|
|
98
98
|
}
|
|
@@ -182,66 +182,146 @@ export async function importTeammates(sourceDir, targetDir) {
|
|
|
182
182
|
await stat(gitignoreDest);
|
|
183
183
|
}
|
|
184
184
|
catch {
|
|
185
|
-
await writeFile(gitignoreDest, "USER.md\n
|
|
185
|
+
await writeFile(gitignoreDest, "USER.md\n.*/\n", "utf-8");
|
|
186
186
|
files.push(".gitignore");
|
|
187
187
|
}
|
|
188
188
|
return { teammates, files };
|
|
189
189
|
}
|
|
190
190
|
/**
|
|
191
|
-
* Build
|
|
192
|
-
*
|
|
193
|
-
*
|
|
191
|
+
* Build a comprehensive import-adaptation prompt that runs as a single agent session.
|
|
192
|
+
* The agent will:
|
|
193
|
+
* 1. Scan the current project
|
|
194
|
+
* 2. Evaluate which imported teammates are needed
|
|
195
|
+
* 3. Adapt kept teammates (add Previous Projects section + rewrite for new project)
|
|
196
|
+
* 4. Create any new teammates the project needs
|
|
197
|
+
* 5. Remove teammates that don't apply
|
|
194
198
|
*
|
|
195
199
|
* @param teammatesDir - The .teammates/ directory in the target project
|
|
196
|
-
* @param
|
|
200
|
+
* @param teammateNames - Names of all imported teammates
|
|
201
|
+
* @param sourceProjectPath - Path to the source project (for Previous Projects section)
|
|
197
202
|
*/
|
|
198
|
-
export async function
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
203
|
+
export async function buildImportAdaptationPrompt(teammatesDir, teammateNames, sourceProjectPath) {
|
|
204
|
+
const teammateSections = [];
|
|
205
|
+
for (const name of teammateNames) {
|
|
206
|
+
const dir = join(teammatesDir, name);
|
|
207
|
+
let soulContent = "";
|
|
208
|
+
let wisdomContent = "";
|
|
209
|
+
try {
|
|
210
|
+
soulContent = await readFile(join(dir, "SOUL.md"), "utf-8");
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
/* missing */
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
wisdomContent = await readFile(join(dir, "WISDOM.md"), "utf-8");
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
/* missing */
|
|
220
|
+
}
|
|
221
|
+
const soulBlock = soulContent
|
|
222
|
+
? `**SOUL.md:**\n\`\`\`markdown\n${soulContent}\n\`\`\``
|
|
223
|
+
: "*No SOUL.md found*";
|
|
224
|
+
const wisdomBlock = wisdomContent
|
|
225
|
+
? `\n**WISDOM.md:**\n\`\`\`markdown\n${wisdomContent}\n\`\`\``
|
|
226
|
+
: "";
|
|
227
|
+
teammateSections.push(`### @${name}\n${soulBlock}${wisdomBlock}`);
|
|
214
228
|
}
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
229
|
+
const projectDir = dirname(teammatesDir);
|
|
230
|
+
return `You are adapting an imported team to a new project.
|
|
231
|
+
|
|
232
|
+
**Source project:** \`${sourceProjectPath}\`
|
|
233
|
+
**Target project:** \`${projectDir}\`
|
|
234
|
+
**Target .teammates/ directory:** \`${teammatesDir}\`
|
|
235
|
+
**Imported teammates:** ${teammateNames.map((n) => `@${n}`).join(", ")}
|
|
236
|
+
|
|
237
|
+
## Imported Teammates (from source project)
|
|
238
|
+
|
|
239
|
+
${teammateSections.join("\n\n---\n\n")}
|
|
222
240
|
|
|
223
|
-
|
|
241
|
+
## Instructions
|
|
224
242
|
|
|
225
|
-
|
|
226
|
-
${soulSection}${wisdomSection}
|
|
243
|
+
Work through these phases in order. **Pause after Phase 1 and Phase 2** to present your analysis and get user approval before making changes.
|
|
227
244
|
|
|
228
|
-
|
|
245
|
+
### Phase 1: Scan This Project
|
|
229
246
|
|
|
230
|
-
|
|
247
|
+
Analyze the current project to understand its structure:
|
|
248
|
+
- Read the project root: package manifest, README, config files
|
|
249
|
+
- Identify major subsystems, languages, frameworks, file patterns
|
|
250
|
+
- Understand the dependency flow and architecture
|
|
231
251
|
|
|
232
|
-
|
|
233
|
-
- **Preserve**: Identity, Core Principles, Ethics, personality, tone
|
|
234
|
-
- **Update**: Ownership patterns (primary/secondary file globs), Boundaries (reference correct teammate names), Capabilities (commands, file patterns, technologies), Routing keywords, Quality Bar
|
|
235
|
-
- **Adapt**: Any codebase-specific references (paths, package names, tools)
|
|
252
|
+
**Present your analysis to the user and wait for confirmation.**
|
|
236
253
|
|
|
237
|
-
|
|
238
|
-
- **Preserve**: Wisdom entries that are universal (principles, patterns, lessons)
|
|
239
|
-
- **Remove or update**: Entries referencing old project paths, file names, or architecture
|
|
240
|
-
- **Add**: A creation entry noting this teammate was imported and adapted
|
|
254
|
+
### Phase 2: Evaluate Imported Teammates
|
|
241
255
|
|
|
242
|
-
|
|
256
|
+
For each imported teammate, decide:
|
|
257
|
+
- **KEEP** — their domain or expertise is relevant to this project (even if specific details need updating)
|
|
258
|
+
- **DROP** — their domain doesn't exist here and their skills aren't transferable
|
|
243
259
|
|
|
244
|
-
|
|
260
|
+
Also identify **gaps** — major subsystems in this project that none of the imported teammates cover. Propose new teammates for these gaps.
|
|
261
|
+
|
|
262
|
+
**Present your evaluation as a structured plan and wait for user approval:**
|
|
263
|
+
\`\`\`
|
|
264
|
+
KEEP: @name1, @name2
|
|
265
|
+
DROP: @name3
|
|
266
|
+
CREATE: @newname (role description)
|
|
267
|
+
\`\`\`
|
|
268
|
+
|
|
269
|
+
### Phase 3: Adapt Kept Teammates
|
|
270
|
+
|
|
271
|
+
For each KEEP teammate, edit their SOUL.md and WISDOM.md:
|
|
272
|
+
|
|
273
|
+
1. **Add a "Previous Projects" section** to SOUL.md (place it after Ethics, before any appendix). Compress what the teammate did in the source project into a portable summary:
|
|
274
|
+
\`\`\`markdown
|
|
275
|
+
## Previous Projects
|
|
276
|
+
|
|
277
|
+
### <project-name>
|
|
278
|
+
- **Role**: <one-line summary of what they did>
|
|
279
|
+
- **Stack**: <key technologies they worked with>
|
|
280
|
+
- **Domains**: <what they owned — file patterns or subsystem names>
|
|
281
|
+
- **Key learnings**: <1-3 bullets of notable patterns, decisions, or lessons>
|
|
282
|
+
\`\`\`
|
|
283
|
+
|
|
284
|
+
2. **Rewrite the rest of SOUL.md** for this project:
|
|
285
|
+
- **Preserve**: Identity (name, personality), Core Principles, Ethics
|
|
286
|
+
- **Rewrite**: Ownership (primary/secondary file globs for THIS project), Boundaries, Capabilities (commands, file patterns, technologies), Routing keywords, Quality Bar
|
|
287
|
+
- **Update**: Any codebase-specific references (paths, package names, tools, teammate names)
|
|
288
|
+
|
|
289
|
+
3. **Update WISDOM.md**:
|
|
290
|
+
- **Add** a "Previous Projects" section at the top with a compressed note:
|
|
291
|
+
\`\`\`markdown
|
|
292
|
+
## Previous Projects
|
|
293
|
+
|
|
294
|
+
### <project-name>
|
|
295
|
+
- Carried over universal wisdom entries from the source project
|
|
296
|
+
- Project-specific entries removed or adapted
|
|
297
|
+
\`\`\`
|
|
298
|
+
- **Keep** wisdom entries that are universal (general principles, patterns, lessons)
|
|
299
|
+
- **Remove** entries that reference source project paths, architecture, or tools not used here
|
|
300
|
+
- **Adapt** entries with transferable knowledge but old-project-specific details
|
|
301
|
+
|
|
302
|
+
### Phase 4: Handle Dropped Teammates
|
|
303
|
+
|
|
304
|
+
For each DROP teammate, delete their directory under \`${teammatesDir}\`.
|
|
305
|
+
|
|
306
|
+
### Phase 5: Create New Teammates
|
|
307
|
+
|
|
308
|
+
For each new teammate proposed in Phase 2 (after user approval):
|
|
309
|
+
- Create \`${teammatesDir}/<name>/\` with SOUL.md, WISDOM.md, and \`memory/\`
|
|
310
|
+
- Use the template at \`${teammatesDir}/TEMPLATE.md\` for structure
|
|
311
|
+
- WISDOM.md starts with one creation entry
|
|
312
|
+
|
|
313
|
+
### Phase 6: Update Framework Files
|
|
314
|
+
|
|
315
|
+
- Update \`${teammatesDir}/README.md\` with the final roster
|
|
316
|
+
- Update \`${teammatesDir}/CROSS-TEAM.md\` ownership table
|
|
317
|
+
|
|
318
|
+
### Phase 7: Verify
|
|
319
|
+
|
|
320
|
+
- Every kept/new teammate has SOUL.md and WISDOM.md
|
|
321
|
+
- Ownership globs cover the codebase without major gaps
|
|
322
|
+
- Boundaries reference the correct owning teammate
|
|
323
|
+
- Previous Projects sections are present for all imported teammates
|
|
324
|
+
- CROSS-TEAM.md has one row per teammate`;
|
|
245
325
|
}
|
|
246
326
|
/**
|
|
247
327
|
* Load ONBOARDING.md from the project dir, package root, or built-in fallback.
|
package/dist/registry.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* (SOUL.md, WISDOM.md, daily logs, ownership rules).
|
|
6
6
|
*/
|
|
7
7
|
import { readdir, readFile, stat } from "node:fs/promises";
|
|
8
|
-
import { basename, join } from "node:path";
|
|
8
|
+
import { basename, dirname, join } from "node:path";
|
|
9
9
|
export class Registry {
|
|
10
10
|
teammatesDir;
|
|
11
11
|
teammates = new Map();
|
|
@@ -50,6 +50,7 @@ export class Registry {
|
|
|
50
50
|
weeklyLogs,
|
|
51
51
|
ownership,
|
|
52
52
|
routingKeywords,
|
|
53
|
+
cwd: dirname(this.teammatesDir),
|
|
53
54
|
};
|
|
54
55
|
this.teammates.set(name, config);
|
|
55
56
|
return config;
|
package/package.json
CHANGED