@evo-dev/core 0.0.1-alpha.2 → 0.0.1-alpha.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/assets/team/agents/code-reviewer.md +48 -0
- package/assets/team/agents/docs-maintainer.md +51 -0
- package/assets/team/agents/implementation-engineer.md +51 -0
- package/assets/team/agents/product-scope-analyst.md +58 -0
- package/assets/team/agents/release-engineer.md +55 -0
- package/assets/team/agents/security-boundary-reviewer.md +50 -0
- package/assets/team/agents/solution-architect.md +51 -0
- package/assets/team/agents/verification-engineer.md +51 -0
- package/assets/team/team.md +102 -0
- package/dist/config/index.js +17 -0
- package/dist/index.js +447 -16
- package/package.json +1 -1
- package/src/hooks/index.ts +2 -2
- package/src/team/index.ts +575 -2
package/dist/index.js
CHANGED
|
@@ -7360,7 +7360,7 @@ function isRecord6(value) {
|
|
|
7360
7360
|
// packages/core/src/team/index.ts
|
|
7361
7361
|
import { spawn } from "node:child_process";
|
|
7362
7362
|
import { appendFile as appendFile2, cp, mkdir as mkdir6, readFile as readFile7, readdir as readdir5, stat as stat6, writeFile as writeFile5 } from "node:fs/promises";
|
|
7363
|
-
import { basename as basename3, dirname as dirname6, join as join7 } from "node:path";
|
|
7363
|
+
import { basename as basename3, dirname as dirname6, extname, isAbsolute as isAbsolute5, join as join7, relative as relative6, resolve as resolve4 } from "node:path";
|
|
7364
7364
|
|
|
7365
7365
|
// packages/core/src/team/prompts.ts
|
|
7366
7366
|
var TEAM_ROLE_STARTUP_PROMPT_TEMPLATE = [
|
|
@@ -7538,6 +7538,23 @@ var BUILT_IN_ROLE_PROMPTS = {
|
|
|
7538
7538
|
prompt: "You are the implementation executor for this EvoDev team run. Keep changes scoped, avoid taking conductor decisions, and report changed files plus verification evidence."
|
|
7539
7539
|
}
|
|
7540
7540
|
};
|
|
7541
|
+
var BUILT_IN_TEAM_DEFINITION = {
|
|
7542
|
+
version: 1,
|
|
7543
|
+
name: "builtin-minimal-team",
|
|
7544
|
+
description: "Built-in minimal EvoDev team fallback.",
|
|
7545
|
+
agents: {
|
|
7546
|
+
executor: "builtin:executor",
|
|
7547
|
+
reviewer: "builtin:reviewer",
|
|
7548
|
+
tester: "builtin:tester"
|
|
7549
|
+
},
|
|
7550
|
+
body: [
|
|
7551
|
+
"# Built-in Minimal Team",
|
|
7552
|
+
"",
|
|
7553
|
+
"Use role agents only when delegation improves correctness, coverage, safety, or latency.",
|
|
7554
|
+
"Spawn roles on demand and send self-contained assignments through Teams MCP."
|
|
7555
|
+
].join(`
|
|
7556
|
+
`)
|
|
7557
|
+
};
|
|
7541
7558
|
function createTeamRunStore(homeDir) {
|
|
7542
7559
|
const paths = resolveTeamRunPaths(homeDir);
|
|
7543
7560
|
return {
|
|
@@ -8541,9 +8558,188 @@ async function listTeamAgents(input = {}) {
|
|
|
8541
8558
|
isMidTurn: isMidTurnTeamAgentStatus(agent.status)
|
|
8542
8559
|
}));
|
|
8543
8560
|
}
|
|
8561
|
+
async function resolveTeamOverlay(input) {
|
|
8562
|
+
const repoTeamPath = join7(input.repoRoot, ".evodev", "team", "team.md");
|
|
8563
|
+
if (await pathExists5(repoTeamPath)) {
|
|
8564
|
+
return {
|
|
8565
|
+
source: "repo",
|
|
8566
|
+
teamPath: repoTeamPath,
|
|
8567
|
+
definition: parseTeamDefinitionMarkdown(await readFile7(repoTeamPath, "utf8"))
|
|
8568
|
+
};
|
|
8569
|
+
}
|
|
8570
|
+
const globalTeamPath = resolveGlobalTeamMarkdownPath(input.homeDir);
|
|
8571
|
+
if (await pathExists5(globalTeamPath)) {
|
|
8572
|
+
return {
|
|
8573
|
+
source: "global",
|
|
8574
|
+
teamPath: globalTeamPath,
|
|
8575
|
+
definition: parseTeamDefinitionMarkdown(await readFile7(globalTeamPath, "utf8"))
|
|
8576
|
+
};
|
|
8577
|
+
}
|
|
8578
|
+
return {
|
|
8579
|
+
source: "builtin",
|
|
8580
|
+
teamPath: null,
|
|
8581
|
+
definition: BUILT_IN_TEAM_DEFINITION
|
|
8582
|
+
};
|
|
8583
|
+
}
|
|
8584
|
+
async function ensureDefaultTeamOverlay(input) {
|
|
8585
|
+
const assets = await listDefaultTeamOverlayAssets(input.assetsRootDir);
|
|
8586
|
+
const paths = resolveEvoDevPaths(input.homeDir);
|
|
8587
|
+
const files = [];
|
|
8588
|
+
for (const asset of assets) {
|
|
8589
|
+
const targetPath = asset.kind === "team" ? join7(paths.rootDir, "team", "team.md") : join7(paths.rootDir, "team", "agents", asset.name);
|
|
8590
|
+
const content = await readFile7(asset.sourcePath, "utf8");
|
|
8591
|
+
files.push({
|
|
8592
|
+
sourcePath: asset.sourcePath,
|
|
8593
|
+
targetPath,
|
|
8594
|
+
written: await writeTextFileIfMissing(targetPath, content)
|
|
8595
|
+
});
|
|
8596
|
+
}
|
|
8597
|
+
return { files };
|
|
8598
|
+
}
|
|
8599
|
+
function parseTeamDefinitionMarkdown(content) {
|
|
8600
|
+
const markdown = parseMarkdownWithFrontmatter(content);
|
|
8601
|
+
const frontmatter = markdown.frontmatter;
|
|
8602
|
+
const version = frontmatter.version;
|
|
8603
|
+
if (version !== 1)
|
|
8604
|
+
throw new Error("team.md frontmatter version must be 1.");
|
|
8605
|
+
const agents = parseTeamDefinitionAgents(frontmatter.agents);
|
|
8606
|
+
return {
|
|
8607
|
+
version: 1,
|
|
8608
|
+
name: optionalString4(frontmatter.name) ?? "evodev-team",
|
|
8609
|
+
description: optionalString4(frontmatter.description) ?? "EvoDev team overlay.",
|
|
8610
|
+
agents,
|
|
8611
|
+
body: markdown.body.trim()
|
|
8612
|
+
};
|
|
8613
|
+
}
|
|
8614
|
+
function resolveTeamAgentReference(input) {
|
|
8615
|
+
assertSafeId(input.roleId, "roleId");
|
|
8616
|
+
const reference = input.reference.trim();
|
|
8617
|
+
if (reference.startsWith("global:")) {
|
|
8618
|
+
const name = reference.slice("global:".length);
|
|
8619
|
+
assertSafeId(name, "global agent name");
|
|
8620
|
+
return {
|
|
8621
|
+
roleId: input.roleId,
|
|
8622
|
+
reference,
|
|
8623
|
+
sourcePath: join7(resolveEvoDevPaths(input.homeDir).rootDir, "team", "agents", `${name}.md`),
|
|
8624
|
+
scope: "global"
|
|
8625
|
+
};
|
|
8626
|
+
}
|
|
8627
|
+
if (reference.includes(":")) {
|
|
8628
|
+
throw new Error(`Unsupported team agent reference for ${input.roleId}: ${reference}`);
|
|
8629
|
+
}
|
|
8630
|
+
if (isAbsolute5(reference)) {
|
|
8631
|
+
throw new Error(`Team agent path for ${input.roleId} must be repo-relative.`);
|
|
8632
|
+
}
|
|
8633
|
+
const sourcePath = resolve4(input.repoRoot, reference);
|
|
8634
|
+
const relativePath = relative6(input.repoRoot, sourcePath);
|
|
8635
|
+
if (relativePath === "" || relativePath.startsWith("..") || isAbsolute5(relativePath) || extname(sourcePath) !== ".md") {
|
|
8636
|
+
throw new Error(`Team agent path for ${input.roleId} must be a repo-local Markdown file.`);
|
|
8637
|
+
}
|
|
8638
|
+
return {
|
|
8639
|
+
roleId: input.roleId,
|
|
8640
|
+
reference,
|
|
8641
|
+
sourcePath,
|
|
8642
|
+
scope: "repo"
|
|
8643
|
+
};
|
|
8644
|
+
}
|
|
8645
|
+
async function readTeamAgentSummary(input) {
|
|
8646
|
+
const reference = resolveTeamAgentReference(input);
|
|
8647
|
+
const markdown = await readTeamAgentMarkdown(reference);
|
|
8648
|
+
const parsed = parseMarkdownWithFrontmatter(markdown);
|
|
8649
|
+
return {
|
|
8650
|
+
roleId: input.roleId,
|
|
8651
|
+
name: optionalString4(parsed.frontmatter.name) ?? defaultRoleName(input.roleId),
|
|
8652
|
+
description: optionalString4(parsed.frontmatter.description) ?? `EvoDev ${input.roleId} role agent.`,
|
|
8653
|
+
sourcePath: reference.sourcePath
|
|
8654
|
+
};
|
|
8655
|
+
}
|
|
8656
|
+
async function readTeamAgentDefinition(input) {
|
|
8657
|
+
const reference = resolveTeamAgentReference(input);
|
|
8658
|
+
const markdown = await readTeamAgentMarkdown(reference);
|
|
8659
|
+
const parsed = parseMarkdownWithFrontmatter(markdown);
|
|
8660
|
+
const evodev = isRecord7(parsed.frontmatter.evodev) ? parsed.frontmatter.evodev : {};
|
|
8661
|
+
return {
|
|
8662
|
+
roleId: input.roleId,
|
|
8663
|
+
name: optionalString4(parsed.frontmatter.name) ?? defaultRoleName(input.roleId),
|
|
8664
|
+
description: optionalString4(parsed.frontmatter.description) ?? `EvoDev ${input.roleId} role agent.`,
|
|
8665
|
+
runtime: optionalRuntime(evodev.runtime),
|
|
8666
|
+
model: optionalNullableString(parsed.frontmatter.model) ?? null,
|
|
8667
|
+
thinkingLevel: optionalNullableString(evodev.thinking) ?? optionalNullableString(evodev.thinkingLevel) ?? null,
|
|
8668
|
+
writeMode: optionalWriteMode(evodev.writeMode),
|
|
8669
|
+
skills: parseStringList(evodev.skills),
|
|
8670
|
+
sourcePath: reference.sourcePath,
|
|
8671
|
+
markdown
|
|
8672
|
+
};
|
|
8673
|
+
}
|
|
8674
|
+
async function renderMainTeamOverlayContext(input) {
|
|
8675
|
+
const overlay = input.overlay ?? await resolveTeamOverlay(input);
|
|
8676
|
+
const summaries = await readTeamOverlayAgentSummaries({
|
|
8677
|
+
homeDir: input.homeDir,
|
|
8678
|
+
repoRoot: input.repoRoot,
|
|
8679
|
+
overlay
|
|
8680
|
+
});
|
|
8681
|
+
const roleLines = summaries.length === 0 ? ["- none declared"] : summaries.map((summary) => `- ${summary.roleId}: ${summary.name} - ${summary.description}`);
|
|
8682
|
+
const source = overlay.teamPath === null ? `${overlay.source} fallback` : `${overlay.source}: ${overlay.teamPath}`;
|
|
8683
|
+
return [
|
|
8684
|
+
"EvoDev team overlay:",
|
|
8685
|
+
`Team: ${overlay.definition.name}`,
|
|
8686
|
+
`Description: ${overlay.definition.description}`,
|
|
8687
|
+
`Source: ${source}`,
|
|
8688
|
+
"",
|
|
8689
|
+
"Team strategy:",
|
|
8690
|
+
overlay.definition.body || "(none)",
|
|
8691
|
+
"",
|
|
8692
|
+
"Declared role agents:",
|
|
8693
|
+
...roleLines,
|
|
8694
|
+
"",
|
|
8695
|
+
"Overlay rules:",
|
|
8696
|
+
"- Declared role agents are spawned only on demand.",
|
|
8697
|
+
"- Delegate by role id; role agent Markdown is loaded only inside the spawned role."
|
|
8698
|
+
].join(`
|
|
8699
|
+
`);
|
|
8700
|
+
}
|
|
8701
|
+
function renderRoleAgentDefinitionContext(input) {
|
|
8702
|
+
return [
|
|
8703
|
+
"EvoDev role agent Markdown definition:",
|
|
8704
|
+
`Source path: ${input.definition.sourcePath}`,
|
|
8705
|
+
"Use this Markdown as the role-specific operating definition for this role only.",
|
|
8706
|
+
"<evodev-agent-markdown>",
|
|
8707
|
+
input.definition.markdown.trimEnd(),
|
|
8708
|
+
"</evodev-agent-markdown>"
|
|
8709
|
+
].join(`
|
|
8710
|
+
`);
|
|
8711
|
+
}
|
|
8544
8712
|
async function resolveTeamRole(input) {
|
|
8545
8713
|
assertSafeId(input.roleId, "roleId");
|
|
8546
8714
|
const settings = await readSettingsOrDefault(input.homeDir);
|
|
8715
|
+
const overlay = await resolveTeamOverlay({ homeDir: input.homeDir, repoRoot: input.repoRoot });
|
|
8716
|
+
if (overlay.source !== "builtin" && input.roleId !== "main") {
|
|
8717
|
+
const reference = overlay.definition.agents[input.roleId];
|
|
8718
|
+
if (reference === undefined) {
|
|
8719
|
+
throw new Error(`Role ${input.roleId} is not declared in team overlay ${overlay.teamPath ?? overlay.source}.`);
|
|
8720
|
+
}
|
|
8721
|
+
const agent = await readTeamAgentDefinition({
|
|
8722
|
+
homeDir: input.homeDir,
|
|
8723
|
+
repoRoot: input.repoRoot,
|
|
8724
|
+
roleId: input.roleId,
|
|
8725
|
+
reference
|
|
8726
|
+
});
|
|
8727
|
+
return {
|
|
8728
|
+
version: 1,
|
|
8729
|
+
roleId: input.roleId,
|
|
8730
|
+
roleName: agent.name,
|
|
8731
|
+
description: agent.description,
|
|
8732
|
+
runtime: agent.runtime ?? settings.teamRuntime.defaultRuntime,
|
|
8733
|
+
model: agent.model ?? settings.teamRuntime.defaultModel,
|
|
8734
|
+
thinkingLevel: agent.thinkingLevel ?? settings.teamRuntime.defaultThinkingLevel,
|
|
8735
|
+
prompt: renderRoleAgentDefinitionContext({ definition: agent }),
|
|
8736
|
+
permissions: parseRolePermissions({ writeMode: agent.writeMode ?? undefined }, false),
|
|
8737
|
+
teamPolicy: parseRolePolicy(undefined, settings.teamRuntime.recordTranscript),
|
|
8738
|
+
source: "overlay",
|
|
8739
|
+
sourcePath: agent.sourcePath,
|
|
8740
|
+
nativeAgent: null
|
|
8741
|
+
};
|
|
8742
|
+
}
|
|
8547
8743
|
const globalRolePath = join7(resolveEvoDevPaths(input.homeDir).roleAgentsDir, `${input.roleId}.json`);
|
|
8548
8744
|
const candidate = await readRoleCandidate(globalRolePath, "global");
|
|
8549
8745
|
const nativeAgent = await resolveTeamRoleNativeAgentBinding({
|
|
@@ -8562,11 +8758,17 @@ async function resolveTeamRole(input) {
|
|
|
8562
8758
|
defaultThinkingLevel: settings.teamRuntime.defaultThinkingLevel,
|
|
8563
8759
|
recordTranscript: settings.teamRuntime.recordTranscript
|
|
8564
8760
|
});
|
|
8761
|
+
const prompt = input.roleId === "main" ? appendPromptBlock(parsed.prompt, await renderMainTeamOverlayContext({
|
|
8762
|
+
homeDir: input.homeDir,
|
|
8763
|
+
repoRoot: input.repoRoot,
|
|
8764
|
+
overlay
|
|
8765
|
+
})) : parsed.prompt;
|
|
8565
8766
|
return {
|
|
8566
8767
|
...parsed,
|
|
8567
8768
|
runtime: input.overrides?.runtime ?? nativeAgent?.target ?? parsed.runtime,
|
|
8568
8769
|
model: input.overrides?.model ?? parsed.model,
|
|
8569
8770
|
thinkingLevel: input.overrides?.thinkingLevel ?? parsed.thinkingLevel,
|
|
8771
|
+
prompt,
|
|
8570
8772
|
sourcePath,
|
|
8571
8773
|
nativeAgent
|
|
8572
8774
|
};
|
|
@@ -8796,7 +8998,7 @@ class TmuxRuntimeAdapter {
|
|
|
8796
8998
|
|
|
8797
8999
|
class NodeTeamRuntimeCommandRunner {
|
|
8798
9000
|
async run(command, args, options = {}) {
|
|
8799
|
-
return new Promise((
|
|
9001
|
+
return new Promise((resolve5, reject) => {
|
|
8800
9002
|
const child = spawn(command, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
8801
9003
|
let stdout = "";
|
|
8802
9004
|
let stderr = "";
|
|
@@ -8810,7 +9012,7 @@ class NodeTeamRuntimeCommandRunner {
|
|
|
8810
9012
|
});
|
|
8811
9013
|
child.once("error", reject);
|
|
8812
9014
|
child.once("close", (code) => {
|
|
8813
|
-
|
|
9015
|
+
resolve5({ exitCode: code ?? 1, stdout, stderr });
|
|
8814
9016
|
});
|
|
8815
9017
|
child.stdin.end(options.input ?? "");
|
|
8816
9018
|
});
|
|
@@ -9075,6 +9277,206 @@ function createBuiltInRole(roleId, runtime) {
|
|
|
9075
9277
|
prompt: builtin?.prompt ?? defaultRolePrompt(roleId)
|
|
9076
9278
|
};
|
|
9077
9279
|
}
|
|
9280
|
+
async function readTeamOverlayAgentSummaries(input) {
|
|
9281
|
+
const entries = Object.entries(input.overlay.definition.agents);
|
|
9282
|
+
if (input.overlay.source === "builtin") {
|
|
9283
|
+
return entries.map(([roleId]) => {
|
|
9284
|
+
const builtin = BUILT_IN_ROLE_PROMPTS[roleId];
|
|
9285
|
+
return {
|
|
9286
|
+
roleId,
|
|
9287
|
+
name: builtin?.roleName ?? defaultRoleName(roleId),
|
|
9288
|
+
description: builtin?.description ?? `EvoDev ${roleId} role agent.`,
|
|
9289
|
+
sourcePath: "builtin"
|
|
9290
|
+
};
|
|
9291
|
+
});
|
|
9292
|
+
}
|
|
9293
|
+
return Promise.all(entries.map(([roleId, reference]) => readTeamAgentSummary({
|
|
9294
|
+
homeDir: input.homeDir,
|
|
9295
|
+
repoRoot: input.repoRoot,
|
|
9296
|
+
roleId,
|
|
9297
|
+
reference
|
|
9298
|
+
})));
|
|
9299
|
+
}
|
|
9300
|
+
async function listDefaultTeamOverlayAssets(assetsRootDir) {
|
|
9301
|
+
const teamPath = join7(assetsRootDir, "team", "team.md");
|
|
9302
|
+
const agentsDir = join7(assetsRootDir, "team", "agents");
|
|
9303
|
+
await assertReadableFile(teamPath, "Default team asset");
|
|
9304
|
+
let entries;
|
|
9305
|
+
try {
|
|
9306
|
+
entries = await readdir5(agentsDir, { withFileTypes: true });
|
|
9307
|
+
} catch (error) {
|
|
9308
|
+
throw new Error(`Cannot read default team agents directory ${agentsDir}: ${describeError2(error)}`);
|
|
9309
|
+
}
|
|
9310
|
+
const agentFiles = entries.filter((entry) => entry.isFile() && extname(entry.name) === ".md").map((entry) => entry.name).sort();
|
|
9311
|
+
if (agentFiles.length === 0) {
|
|
9312
|
+
throw new Error(`Default team agents directory has no Markdown files: ${agentsDir}`);
|
|
9313
|
+
}
|
|
9314
|
+
return [
|
|
9315
|
+
{ kind: "team", sourcePath: teamPath, name: "team.md" },
|
|
9316
|
+
...agentFiles.map((name) => ({
|
|
9317
|
+
kind: "agent",
|
|
9318
|
+
sourcePath: join7(agentsDir, name),
|
|
9319
|
+
name
|
|
9320
|
+
}))
|
|
9321
|
+
];
|
|
9322
|
+
}
|
|
9323
|
+
async function assertReadableFile(path, label) {
|
|
9324
|
+
try {
|
|
9325
|
+
const info = await stat6(path);
|
|
9326
|
+
if (!info.isFile())
|
|
9327
|
+
throw new Error("not a file");
|
|
9328
|
+
} catch (error) {
|
|
9329
|
+
throw new Error(`${label} is not readable at ${path}: ${describeError2(error)}`);
|
|
9330
|
+
}
|
|
9331
|
+
}
|
|
9332
|
+
async function writeTextFileIfMissing(path, content) {
|
|
9333
|
+
try {
|
|
9334
|
+
await readFile7(path, "utf8");
|
|
9335
|
+
return false;
|
|
9336
|
+
} catch (error) {
|
|
9337
|
+
if (!isNotFoundError2(error)) {
|
|
9338
|
+
throw new Error(`Cannot inspect ${path}: ${describeError2(error)}`);
|
|
9339
|
+
}
|
|
9340
|
+
}
|
|
9341
|
+
await mkdir6(dirname6(path), { recursive: true });
|
|
9342
|
+
await writeFile5(path, content.endsWith(`
|
|
9343
|
+
`) ? content : `${content}
|
|
9344
|
+
`, "utf8");
|
|
9345
|
+
return true;
|
|
9346
|
+
}
|
|
9347
|
+
function parseTeamDefinitionAgents(value) {
|
|
9348
|
+
if (value === undefined)
|
|
9349
|
+
return {};
|
|
9350
|
+
if (!isRecord7(value))
|
|
9351
|
+
throw new Error("team.md agents must be a role-id map.");
|
|
9352
|
+
const agents = {};
|
|
9353
|
+
for (const [roleId, reference] of Object.entries(value)) {
|
|
9354
|
+
assertSafeId(roleId, "team.md agents roleId");
|
|
9355
|
+
if (typeof reference !== "string" || reference.trim() === "") {
|
|
9356
|
+
throw new Error(`team.md agent reference for ${roleId} must be a non-empty string.`);
|
|
9357
|
+
}
|
|
9358
|
+
agents[roleId] = reference.trim();
|
|
9359
|
+
}
|
|
9360
|
+
return agents;
|
|
9361
|
+
}
|
|
9362
|
+
async function readTeamAgentMarkdown(reference) {
|
|
9363
|
+
if (extname(reference.sourcePath) !== ".md") {
|
|
9364
|
+
throw new Error(`Team agent file for ${reference.roleId} must be Markdown.`);
|
|
9365
|
+
}
|
|
9366
|
+
try {
|
|
9367
|
+
return await readFile7(reference.sourcePath, "utf8");
|
|
9368
|
+
} catch (error) {
|
|
9369
|
+
if (isNotFoundError2(error)) {
|
|
9370
|
+
throw new Error(`Team agent file not found for ${reference.roleId}: ${reference.sourcePath}`);
|
|
9371
|
+
}
|
|
9372
|
+
throw new Error(`Cannot read team agent file for ${reference.roleId}: ${reference.sourcePath}: ${describeError2(error)}`);
|
|
9373
|
+
}
|
|
9374
|
+
}
|
|
9375
|
+
function parseMarkdownWithFrontmatter(content) {
|
|
9376
|
+
const text = content.startsWith("\uFEFF") ? content.slice(1) : content;
|
|
9377
|
+
const lines = text.split(/\r?\n/);
|
|
9378
|
+
if (lines[0]?.trim() !== "---")
|
|
9379
|
+
return { frontmatter: {}, body: text };
|
|
9380
|
+
const end = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
|
|
9381
|
+
if (end < 0)
|
|
9382
|
+
throw new Error("Markdown frontmatter is not closed.");
|
|
9383
|
+
return {
|
|
9384
|
+
frontmatter: parseSimpleYaml(lines.slice(1, end).join(`
|
|
9385
|
+
`)),
|
|
9386
|
+
body: lines.slice(end + 1).join(`
|
|
9387
|
+
`)
|
|
9388
|
+
};
|
|
9389
|
+
}
|
|
9390
|
+
function parseSimpleYaml(content) {
|
|
9391
|
+
const root = {};
|
|
9392
|
+
const stack = [
|
|
9393
|
+
{ indent: -1, value: root }
|
|
9394
|
+
];
|
|
9395
|
+
const lines = content.split(/\r?\n/);
|
|
9396
|
+
for (let index = 0;index < lines.length; index += 1) {
|
|
9397
|
+
const rawLine = lines[index] ?? "";
|
|
9398
|
+
if (rawLine.trim() === "" || rawLine.trimStart().startsWith("#"))
|
|
9399
|
+
continue;
|
|
9400
|
+
const indent = rawLine.match(/^ */)?.[0].length ?? 0;
|
|
9401
|
+
const trimmed = rawLine.trim();
|
|
9402
|
+
while (stack.length > 1 && indent <= stack[stack.length - 1].indent)
|
|
9403
|
+
stack.pop();
|
|
9404
|
+
const parent = stack[stack.length - 1].value;
|
|
9405
|
+
if (trimmed.startsWith("- ")) {
|
|
9406
|
+
if (!Array.isArray(parent))
|
|
9407
|
+
throw new Error("Invalid YAML list item placement.");
|
|
9408
|
+
parent.push(parseYamlScalar(trimmed.slice(2).trim()));
|
|
9409
|
+
continue;
|
|
9410
|
+
}
|
|
9411
|
+
const separator = trimmed.indexOf(":");
|
|
9412
|
+
if (separator <= 0)
|
|
9413
|
+
throw new Error(`Invalid YAML line: ${trimmed}`);
|
|
9414
|
+
const key = trimmed.slice(0, separator).trim();
|
|
9415
|
+
const rawValue = trimmed.slice(separator + 1).trim();
|
|
9416
|
+
if (!isRecord7(parent))
|
|
9417
|
+
throw new Error(`Invalid YAML parent for key ${key}.`);
|
|
9418
|
+
if (rawValue === "") {
|
|
9419
|
+
const next = findNextYamlContentLine(lines, index + 1);
|
|
9420
|
+
const value = next !== null && next.indent > indent && next.trimmed.startsWith("- ") ? [] : {};
|
|
9421
|
+
parent[key] = value;
|
|
9422
|
+
stack.push({ indent, value });
|
|
9423
|
+
continue;
|
|
9424
|
+
}
|
|
9425
|
+
parent[key] = parseYamlScalar(rawValue);
|
|
9426
|
+
}
|
|
9427
|
+
return root;
|
|
9428
|
+
}
|
|
9429
|
+
function findNextYamlContentLine(lines, start) {
|
|
9430
|
+
for (let index = start;index < lines.length; index += 1) {
|
|
9431
|
+
const line = lines[index] ?? "";
|
|
9432
|
+
if (line.trim() === "" || line.trimStart().startsWith("#"))
|
|
9433
|
+
continue;
|
|
9434
|
+
return {
|
|
9435
|
+
indent: line.match(/^ */)?.[0].length ?? 0,
|
|
9436
|
+
trimmed: line.trim()
|
|
9437
|
+
};
|
|
9438
|
+
}
|
|
9439
|
+
return null;
|
|
9440
|
+
}
|
|
9441
|
+
function parseYamlScalar(value) {
|
|
9442
|
+
if (value === "")
|
|
9443
|
+
return "";
|
|
9444
|
+
if (value === "true")
|
|
9445
|
+
return true;
|
|
9446
|
+
if (value === "false")
|
|
9447
|
+
return false;
|
|
9448
|
+
if (value === "null" || value === "~")
|
|
9449
|
+
return null;
|
|
9450
|
+
if (/^-?\d+(\.\d+)?$/.test(value))
|
|
9451
|
+
return Number(value);
|
|
9452
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
9453
|
+
const inner = value.slice(1, -1).trim();
|
|
9454
|
+
if (inner === "")
|
|
9455
|
+
return [];
|
|
9456
|
+
return inner.split(",").map((item) => parseYamlScalar(item.trim()));
|
|
9457
|
+
}
|
|
9458
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
9459
|
+
try {
|
|
9460
|
+
return JSON.parse(value);
|
|
9461
|
+
} catch {
|
|
9462
|
+
return value.slice(1, -1);
|
|
9463
|
+
}
|
|
9464
|
+
}
|
|
9465
|
+
if (value.startsWith("'") && value.endsWith("'")) {
|
|
9466
|
+
return value.slice(1, -1).replace(/''/g, "'");
|
|
9467
|
+
}
|
|
9468
|
+
return value;
|
|
9469
|
+
}
|
|
9470
|
+
function appendPromptBlock(prompt, block) {
|
|
9471
|
+
if (block.trim() === "")
|
|
9472
|
+
return prompt;
|
|
9473
|
+
return `${prompt.trimEnd()}
|
|
9474
|
+
|
|
9475
|
+
${block.trim()}`;
|
|
9476
|
+
}
|
|
9477
|
+
function resolveGlobalTeamMarkdownPath(homeDir) {
|
|
9478
|
+
return join7(resolveEvoDevPaths(homeDir).rootDir, "team", "team.md");
|
|
9479
|
+
}
|
|
9078
9480
|
function parseRolePermissions(value, main) {
|
|
9079
9481
|
const input = isRecord7(value) ? value : {};
|
|
9080
9482
|
return {
|
|
@@ -9187,6 +9589,13 @@ function parseRuntime(value, fallback) {
|
|
|
9187
9589
|
return value;
|
|
9188
9590
|
throw new Error("Role runtime must be codex or claude.");
|
|
9189
9591
|
}
|
|
9592
|
+
function optionalRuntime(value) {
|
|
9593
|
+
if (value === undefined || value === null)
|
|
9594
|
+
return null;
|
|
9595
|
+
if (value === "codex" || value === "claude")
|
|
9596
|
+
return value;
|
|
9597
|
+
throw new Error("Role evodev.runtime must be codex or claude.");
|
|
9598
|
+
}
|
|
9190
9599
|
function parseWriteMode(value, fallback) {
|
|
9191
9600
|
if (value === undefined || value === null)
|
|
9192
9601
|
return fallback;
|
|
@@ -9195,6 +9604,20 @@ function parseWriteMode(value, fallback) {
|
|
|
9195
9604
|
}
|
|
9196
9605
|
throw new Error("Role writeMode is invalid.");
|
|
9197
9606
|
}
|
|
9607
|
+
function optionalWriteMode(value) {
|
|
9608
|
+
if (value === undefined || value === null)
|
|
9609
|
+
return null;
|
|
9610
|
+
return parseWriteMode(value, "repo-write");
|
|
9611
|
+
}
|
|
9612
|
+
function parseStringList(value) {
|
|
9613
|
+
if (value === undefined || value === null)
|
|
9614
|
+
return [];
|
|
9615
|
+
if (typeof value === "string" && value.trim() !== "")
|
|
9616
|
+
return [value.trim()];
|
|
9617
|
+
if (!Array.isArray(value))
|
|
9618
|
+
return [];
|
|
9619
|
+
return value.filter((item) => typeof item === "string" && item.trim() !== "");
|
|
9620
|
+
}
|
|
9198
9621
|
function isActiveTeamAgentStatus(status) {
|
|
9199
9622
|
return status === "starting" || status === "running" || status === "busy" || status === "idle" || status === "waiting-input" || status === "recovering" || status === "recreated";
|
|
9200
9623
|
}
|
|
@@ -9933,7 +10356,7 @@ async function handleUserPromptSubmit(input) {
|
|
|
9933
10356
|
const previousBinding = await readSessionBinding(input.homeDir, input.rawPayload);
|
|
9934
10357
|
const teamRuntimeContext = previousBinding?.teamRuntimeContextDeliveredAt === undefined || previousBinding.teamRuntimeContextDeliveredAt === null ? await createTeamRuntimeContextForUserPrompt(input) : null;
|
|
9935
10358
|
const shouldShowDiagnostics = input.teamRuntimeDisplayMode === "development";
|
|
9936
|
-
const teamRuntimeContextDeliveredAt = teamRuntimeContext !== null
|
|
10359
|
+
const teamRuntimeContextDeliveredAt = teamRuntimeContext !== null ? input.receivedAt ?? new Date().toISOString() : previousBinding?.teamRuntimeContextDeliveredAt ?? null;
|
|
9937
10360
|
const binding = {
|
|
9938
10361
|
version: 1,
|
|
9939
10362
|
target: input.target,
|
|
@@ -9955,7 +10378,7 @@ async function handleUserPromptSubmit(input) {
|
|
|
9955
10378
|
let output = visibleContext === null || !shouldShowDiagnostics ? null : hookOutput(input.event.type, {
|
|
9956
10379
|
additionalContext: visibleContext
|
|
9957
10380
|
});
|
|
9958
|
-
if (teamRuntimeContext !== null
|
|
10381
|
+
if (teamRuntimeContext !== null) {
|
|
9959
10382
|
output = appendAdditionalContext(output, input.event.type, teamRuntimeContext);
|
|
9960
10383
|
}
|
|
9961
10384
|
return createRuntimeResult(input, output, {
|
|
@@ -11250,14 +11673,14 @@ async function runDaemonForeground(input) {
|
|
|
11250
11673
|
}));
|
|
11251
11674
|
}
|
|
11252
11675
|
});
|
|
11253
|
-
await new Promise((
|
|
11676
|
+
await new Promise((resolve5, reject) => {
|
|
11254
11677
|
const onError = (error) => {
|
|
11255
11678
|
server.off("listening", onListening);
|
|
11256
11679
|
reject(error);
|
|
11257
11680
|
};
|
|
11258
11681
|
const onListening = () => {
|
|
11259
11682
|
server.off("error", onError);
|
|
11260
|
-
|
|
11683
|
+
resolve5();
|
|
11261
11684
|
};
|
|
11262
11685
|
server.once("error", onError);
|
|
11263
11686
|
server.once("listening", onListening);
|
|
@@ -11274,11 +11697,11 @@ async function runDaemonForeground(input) {
|
|
|
11274
11697
|
processEvolutionTriggers({ homeDir: input.homeDir, limit: 20 }).then(() => clearDaemonEvolutionProcessError(input.homeDir)).catch((error) => recordDaemonEvolutionProcessError(input.homeDir, error));
|
|
11275
11698
|
}, 5000);
|
|
11276
11699
|
evolutionInterval.unref();
|
|
11277
|
-
await new Promise((
|
|
11700
|
+
await new Promise((resolve5, reject) => {
|
|
11278
11701
|
server.once("close", () => {
|
|
11279
11702
|
clearInterval(reconcileInterval);
|
|
11280
11703
|
clearInterval(evolutionInterval);
|
|
11281
|
-
|
|
11704
|
+
resolve5();
|
|
11282
11705
|
});
|
|
11283
11706
|
server.once("error", reject);
|
|
11284
11707
|
});
|
|
@@ -12214,7 +12637,7 @@ function isRecord10(value) {
|
|
|
12214
12637
|
}
|
|
12215
12638
|
// packages/core/src/pack/index.ts
|
|
12216
12639
|
import { readFile as readFile14, readdir as readdir8, stat as stat10 } from "node:fs/promises";
|
|
12217
|
-
import { isAbsolute as
|
|
12640
|
+
import { isAbsolute as isAbsolute6, join as join12, relative as relative7, sep } from "node:path";
|
|
12218
12641
|
|
|
12219
12642
|
// packages/core/src/protected-zones/index.ts
|
|
12220
12643
|
var SENSITIVE_DIRECTORY_SEGMENTS = new Set([
|
|
@@ -12698,7 +13121,7 @@ async function collectPackRelativePaths(packRoot, dir = packRoot) {
|
|
|
12698
13121
|
const paths = [];
|
|
12699
13122
|
for (const entry of entries) {
|
|
12700
13123
|
const absolutePath = join12(dir, entry.name);
|
|
12701
|
-
const relativePath = normalizeRelativePath(
|
|
13124
|
+
const relativePath = normalizeRelativePath(relative7(packRoot, absolutePath));
|
|
12702
13125
|
paths.push(relativePath);
|
|
12703
13126
|
if (entry.isDirectory()) {
|
|
12704
13127
|
paths.push(...await collectPackRelativePaths(packRoot, absolutePath));
|
|
@@ -12713,7 +13136,7 @@ function validatePackRelativePath(path, packRoot) {
|
|
|
12713
13136
|
if (path.includes("\x00")) {
|
|
12714
13137
|
return { severity: "error", code: "path-invalid", message: "Path must not contain NUL bytes." };
|
|
12715
13138
|
}
|
|
12716
|
-
if (
|
|
13139
|
+
if (isAbsolute6(path) || path.startsWith("~")) {
|
|
12717
13140
|
return {
|
|
12718
13141
|
severity: "error",
|
|
12719
13142
|
code: "path-absolute",
|
|
@@ -12729,8 +13152,8 @@ function validatePackRelativePath(path, packRoot) {
|
|
|
12729
13152
|
};
|
|
12730
13153
|
}
|
|
12731
13154
|
const absolute = join12(packRoot, normalized);
|
|
12732
|
-
const rel =
|
|
12733
|
-
if (rel === "" || rel.startsWith("..") ||
|
|
13155
|
+
const rel = relative7(packRoot, absolute);
|
|
13156
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute6(rel)) {
|
|
12734
13157
|
return {
|
|
12735
13158
|
severity: "error",
|
|
12736
13159
|
code: "path-traversal",
|
|
@@ -13145,7 +13568,7 @@ function getEnabledPluginIds(settings) {
|
|
|
13145
13568
|
}
|
|
13146
13569
|
// packages/core/src/project/index.ts
|
|
13147
13570
|
import { mkdir as mkdir13, readFile as readFile16, readdir as readdir9, stat as stat11, writeFile as writeFile12 } from "node:fs/promises";
|
|
13148
|
-
import { basename as basename5, join as join14, relative as
|
|
13571
|
+
import { basename as basename5, join as join14, relative as relative8 } from "node:path";
|
|
13149
13572
|
var PROJECT_CONTEXT_ALLOWED_RELATIVE_PATHS = [
|
|
13150
13573
|
".evodev/project.json",
|
|
13151
13574
|
".evodev/profile.md",
|
|
@@ -13352,7 +13775,7 @@ async function collectProjectFileMetadata(projectDir) {
|
|
|
13352
13775
|
const entries = await readdir9(dir, { withFileTypes: true });
|
|
13353
13776
|
for (const entry of entries) {
|
|
13354
13777
|
const absolutePath = join14(dir, entry.name);
|
|
13355
|
-
const relativePath =
|
|
13778
|
+
const relativePath = relative8(projectDir, absolutePath).replaceAll("\\", "/");
|
|
13356
13779
|
if (shouldExcludePath(relativePath, entry.isDirectory())) {
|
|
13357
13780
|
continue;
|
|
13358
13781
|
}
|
|
@@ -14125,6 +14548,8 @@ export {
|
|
|
14125
14548
|
resolveTraceSessionKey,
|
|
14126
14549
|
resolveTeamRunPaths,
|
|
14127
14550
|
resolveTeamRole,
|
|
14551
|
+
resolveTeamOverlay,
|
|
14552
|
+
resolveTeamAgentReference,
|
|
14128
14553
|
resolveTaskContractOutputPath,
|
|
14129
14554
|
resolveProjectLogKey,
|
|
14130
14555
|
resolveOkfKnowledgePaths,
|
|
@@ -14140,10 +14565,14 @@ export {
|
|
|
14140
14565
|
resolveContextInjectionReceiptPath,
|
|
14141
14566
|
resolveCodexCapabilityArtifactPath,
|
|
14142
14567
|
resolveCodeAgentTraceRefPaths,
|
|
14568
|
+
renderRoleAgentDefinitionContext,
|
|
14569
|
+
renderMainTeamOverlayContext,
|
|
14143
14570
|
recordTeamAgentNativeSession,
|
|
14144
14571
|
recordCodeAgentTraceRefFromHook,
|
|
14145
14572
|
reconcileTeamRun,
|
|
14146
14573
|
rebuildOkfKnowledgeIndexes,
|
|
14574
|
+
readTeamAgentSummary,
|
|
14575
|
+
readTeamAgentDefinition,
|
|
14147
14576
|
readTaskContract,
|
|
14148
14577
|
readRuntimeInjectionSettings,
|
|
14149
14578
|
readPendingTeamMessagesForRole,
|
|
@@ -14168,6 +14597,7 @@ export {
|
|
|
14168
14597
|
pathExists8 as pathExists,
|
|
14169
14598
|
parseWorkflowManifest,
|
|
14170
14599
|
parseTeamRoleDefinition,
|
|
14600
|
+
parseTeamDefinitionMarkdown,
|
|
14171
14601
|
parseSyncState,
|
|
14172
14602
|
parseSkillManifest,
|
|
14173
14603
|
parseSettings,
|
|
@@ -14250,6 +14680,7 @@ export {
|
|
|
14250
14680
|
formatAgentComposeDryRun,
|
|
14251
14681
|
findCandidateConflictOrDuplicate,
|
|
14252
14682
|
ensureOkfKnowledgeBase,
|
|
14683
|
+
ensureDefaultTeamOverlay,
|
|
14253
14684
|
enqueueEvolutionTrigger,
|
|
14254
14685
|
dryRunObservabilityRetentionCleanup,
|
|
14255
14686
|
discardFailedOkfKnowledgePlan,
|
package/package.json
CHANGED
package/src/hooks/index.ts
CHANGED
|
@@ -790,7 +790,7 @@ async function handleUserPromptSubmit(input: HandleHookRuntimeInput): Promise<Ho
|
|
|
790
790
|
: null;
|
|
791
791
|
const shouldShowDiagnostics = input.teamRuntimeDisplayMode === "development";
|
|
792
792
|
const teamRuntimeContextDeliveredAt =
|
|
793
|
-
teamRuntimeContext !== null
|
|
793
|
+
teamRuntimeContext !== null
|
|
794
794
|
? (input.receivedAt ?? new Date().toISOString())
|
|
795
795
|
: (previousBinding?.teamRuntimeContextDeliveredAt ?? null);
|
|
796
796
|
const binding: HookRuntimeSessionBinding = {
|
|
@@ -819,7 +819,7 @@ async function handleUserPromptSubmit(input: HandleHookRuntimeInput): Promise<Ho
|
|
|
819
819
|
: hookOutput(input.event.type, {
|
|
820
820
|
additionalContext: visibleContext,
|
|
821
821
|
});
|
|
822
|
-
if (teamRuntimeContext !== null
|
|
822
|
+
if (teamRuntimeContext !== null) {
|
|
823
823
|
output = appendAdditionalContext(output, input.event.type, teamRuntimeContext);
|
|
824
824
|
}
|
|
825
825
|
|