heyio 4.2.1 → 4.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/daemon/cli.js +336 -204
- package/dist/daemon/index.js +334 -203
- package/dist/web/assets/{index-C-ghqdil.js → index-58mhEI4z.js} +126 -126
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
package/dist/daemon/cli.js
CHANGED
|
@@ -80,7 +80,7 @@ var init_constants = __esm({
|
|
|
80
80
|
"packages/shared/dist/constants.js"() {
|
|
81
81
|
"use strict";
|
|
82
82
|
APP_NAME = "io";
|
|
83
|
-
APP_VERSION = "4.2.
|
|
83
|
+
APP_VERSION = "4.2.3";
|
|
84
84
|
API_PORT = 7777;
|
|
85
85
|
API_HOST = "0.0.0.0";
|
|
86
86
|
DEFAULT_MODEL = "gpt-4o";
|
|
@@ -92,6 +92,7 @@ var init_constants = __esm({
|
|
|
92
92
|
SQUAD_CREATED: "squad.created",
|
|
93
93
|
SQUAD_DELETED: "squad.deleted",
|
|
94
94
|
SQUAD_UPDATED: "squad.updated",
|
|
95
|
+
SQUAD_MEMBER_UPDATED: "squad.member_updated",
|
|
95
96
|
INSTANCE_CREATED: "instance.created",
|
|
96
97
|
INSTANCE_STARTED: "instance.started",
|
|
97
98
|
INSTANCE_COMPLETED: "instance.completed",
|
|
@@ -2310,6 +2311,10 @@ async function updateMember(memberId, data, db) {
|
|
|
2310
2311
|
const database = db ?? await getDatabase();
|
|
2311
2312
|
const sets = [];
|
|
2312
2313
|
const args = [];
|
|
2314
|
+
if (data.role !== void 0) {
|
|
2315
|
+
sets.push("role = ?");
|
|
2316
|
+
args.push(data.role);
|
|
2317
|
+
}
|
|
2313
2318
|
if (data.systemPrompt !== void 0) {
|
|
2314
2319
|
sets.push("system_prompt = ?");
|
|
2315
2320
|
args.push(data.systemPrompt);
|
|
@@ -11914,8 +11919,8 @@ async function recordUsage(data, db) {
|
|
|
11914
11919
|
createdAt: data.createdAt ?? nowIso()
|
|
11915
11920
|
};
|
|
11916
11921
|
await database.execute({
|
|
11917
|
-
sql: `INSERT INTO token_usage (id, squad_id, agent_id, model, input_tokens, output_tokens, cost,
|
|
11918
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
11922
|
+
sql: `INSERT INTO token_usage (id, squad_id, agent_id, model, input_tokens, output_tokens, cost, squad_name, agent_name, created_at)
|
|
11923
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
11919
11924
|
args: [
|
|
11920
11925
|
usage.id,
|
|
11921
11926
|
usage.squadId,
|
|
@@ -11924,8 +11929,6 @@ async function recordUsage(data, db) {
|
|
|
11924
11929
|
usage.inputTokens,
|
|
11925
11930
|
usage.outputTokens,
|
|
11926
11931
|
usage.cost,
|
|
11927
|
-
data.premiumRequestCost ?? null,
|
|
11928
|
-
data.tokenUnitCost ?? null,
|
|
11929
11932
|
data.squadName ?? null,
|
|
11930
11933
|
data.agentName ?? null,
|
|
11931
11934
|
usage.createdAt
|
|
@@ -52870,11 +52873,16 @@ var init_squads2 = __esm({
|
|
|
52870
52873
|
res.status(404).json({ error: "Member not found" });
|
|
52871
52874
|
return;
|
|
52872
52875
|
}
|
|
52873
|
-
const { systemPrompt, model } = req.body;
|
|
52876
|
+
const { role, systemPrompt, model } = req.body;
|
|
52874
52877
|
const updated = await updateMember(member.id, {
|
|
52878
|
+
role,
|
|
52875
52879
|
systemPrompt,
|
|
52876
52880
|
model: model === "" ? null : model
|
|
52877
52881
|
});
|
|
52882
|
+
eventBus.emit(EVENT_NAMES.SQUAD_MEMBER_UPDATED, {
|
|
52883
|
+
squadId: member.squadId,
|
|
52884
|
+
member: updated
|
|
52885
|
+
});
|
|
52878
52886
|
res.status(200).json(updated);
|
|
52879
52887
|
} catch (error51) {
|
|
52880
52888
|
res.status(500).json({
|
|
@@ -59918,8 +59926,7 @@ import { join as join5 } from "node:path";
|
|
|
59918
59926
|
function createConsoleStream() {
|
|
59919
59927
|
return (0, import_pino_pretty.default)({
|
|
59920
59928
|
colorize: process.stdout.isTTY ?? false,
|
|
59921
|
-
|
|
59922
|
-
ignore: "pid,hostname",
|
|
59929
|
+
ignore: "pid,hostname,name,time",
|
|
59923
59930
|
singleLine: true
|
|
59924
59931
|
});
|
|
59925
59932
|
}
|
|
@@ -68326,10 +68333,19 @@ async function getModelPricing(modelId) {
|
|
|
68326
68333
|
sql: "SELECT * FROM model_pricing WHERE id = ?",
|
|
68327
68334
|
args: [modelId]
|
|
68328
68335
|
});
|
|
68329
|
-
if (result.rows.length
|
|
68330
|
-
return
|
|
68336
|
+
if (result.rows.length > 0) {
|
|
68337
|
+
return rowToModelPricing(result.rows[0]);
|
|
68338
|
+
}
|
|
68339
|
+
const allModels = await db.execute(
|
|
68340
|
+
"SELECT * FROM model_pricing WHERE token_input_multiplier IS NOT NULL ORDER BY length(id) DESC"
|
|
68341
|
+
);
|
|
68342
|
+
for (const row of allModels.rows) {
|
|
68343
|
+
const storedId = asString(row.id);
|
|
68344
|
+
if (modelId.startsWith(storedId) || storedId.startsWith(modelId)) {
|
|
68345
|
+
return rowToModelPricing(row);
|
|
68346
|
+
}
|
|
68331
68347
|
}
|
|
68332
|
-
return
|
|
68348
|
+
return null;
|
|
68333
68349
|
}
|
|
68334
68350
|
function calculateTokenUnitCost(inputTokens, outputTokens, inputMultiplier, outputMultiplier) {
|
|
68335
68351
|
if (inputMultiplier === null || outputMultiplier === null) {
|
|
@@ -68427,7 +68443,7 @@ function buildSystemPrompt(options2) {
|
|
|
68427
68443
|
"You coordinate work across squads, wiki knowledge, installed skills, and direct execution tools.",
|
|
68428
68444
|
"When a user asks about a project that has a squad, delegate to that squad. When no squad exists, you can do the work directly.",
|
|
68429
68445
|
"Be practical, concise, and execution-oriented. Use tools when they help you produce a more accurate or durable result.",
|
|
68430
|
-
"## Delegation Rules\n- Prefer delegation for project-specific work when a matching squad exists.\n- If no squad exists for the target repository or project, use direct coding and knowledge tools yourself.\n- Use the wiki to remember important information and recover prior context.\n- Use installed skills when they are relevant to the current request.",
|
|
68446
|
+
"## Delegation Rules\n- Prefer delegation for project-specific work when a matching squad exists.\n- If no squad exists for the target repository or project, use direct coding and knowledge tools yourself.\n- When hiring a squad, always pass the user's context about what areas of the codebase or what kind of work the squad will focus on. Extract specific folder paths or project files mentioned by the user and pass them as scanPaths.\n- Use the wiki to remember important information and recover prior context.\n- Use installed skills when they are relevant to the current request.",
|
|
68431
68447
|
`## Squad Roster
|
|
68432
68448
|
${formatSquadRoster(options2.squads)}`,
|
|
68433
68449
|
formatOptionalSection("## Conversation Summary", options2.conversationSummary ?? ""),
|
|
@@ -69493,8 +69509,7 @@ async function persistUsage(member, usageEvents, squadName) {
|
|
|
69493
69509
|
for (const usage of usageEvents) {
|
|
69494
69510
|
const model = usage.model;
|
|
69495
69511
|
const pricing = await getModelPricing(model);
|
|
69496
|
-
const
|
|
69497
|
-
const tokenUnitCost = pricing ? calculateTokenUnitCost(
|
|
69512
|
+
const cost = pricing ? calculateTokenUnitCost(
|
|
69498
69513
|
usage.inputTokens ?? 0,
|
|
69499
69514
|
usage.outputTokens ?? 0,
|
|
69500
69515
|
pricing.tokenInputMultiplier,
|
|
@@ -69508,9 +69523,7 @@ async function persistUsage(member, usageEvents, squadName) {
|
|
|
69508
69523
|
model,
|
|
69509
69524
|
inputTokens: usage.inputTokens ?? 0,
|
|
69510
69525
|
outputTokens: usage.outputTokens ?? 0,
|
|
69511
|
-
cost
|
|
69512
|
-
premiumRequestCost,
|
|
69513
|
-
tokenUnitCost
|
|
69526
|
+
cost
|
|
69514
69527
|
});
|
|
69515
69528
|
}
|
|
69516
69529
|
}
|
|
@@ -69895,9 +69908,10 @@ Review rules:
|
|
|
69895
69908
|
Given repository analysis describing languages, frameworks, file structure, architectural patterns, and operational concerns, propose the smallest effective team that can deliver objectives safely.
|
|
69896
69909
|
|
|
69897
69910
|
Requirements:
|
|
69898
|
-
- Mandatory roles Team Lead and QA must always exist.
|
|
69911
|
+
- Mandatory roles Team Lead and QA must always exist \u2014 do NOT include them in your response.
|
|
69899
69912
|
- Suggest only additional roles that are clearly justified by the repository analysis.
|
|
69900
|
-
-
|
|
69913
|
+
- Role names must reflect Senior or Principal seniority and be specific to the actual technology stack and work scope. Examples: "Principal React Engineer", "Senior DevOps Engineer", "Senior CI/CD Solutions Engineer", "Principal .NET API Engineer", "Senior Data Pipeline Engineer".
|
|
69914
|
+
- Do NOT use generic names like "Frontend Engineer" or "Backend Engineer". Be specific about the technology and domain.
|
|
69901
69915
|
- Each role must have a short human-readable name and a concise description of responsibilities.
|
|
69902
69916
|
- Avoid duplicate or overlapping roles.
|
|
69903
69917
|
- Optimize for implementation, verification, and maintainability.
|
|
@@ -69906,9 +69920,9 @@ Return strict JSON in this shape:
|
|
|
69906
69920
|
{
|
|
69907
69921
|
"roles": [
|
|
69908
69922
|
{
|
|
69909
|
-
"role": "
|
|
69910
|
-
"name": "
|
|
69911
|
-
"description": "Owns UI implementation, client state, and browser-facing tests."
|
|
69923
|
+
"role": "principal-react-engineer",
|
|
69924
|
+
"name": "Principal React Engineer",
|
|
69925
|
+
"description": "Owns UI implementation, client state management, and browser-facing integration tests."
|
|
69912
69926
|
}
|
|
69913
69927
|
]
|
|
69914
69928
|
}`;
|
|
@@ -70754,29 +70768,29 @@ function detectRolesFromAnalysis(repoAnalysis) {
|
|
|
70754
70768
|
const roles = [];
|
|
70755
70769
|
if (/(react|next|vue|angular|frontend|ui|xaml)/.test(analysis)) {
|
|
70756
70770
|
roles.push({
|
|
70757
|
-
role: "frontend-engineer",
|
|
70758
|
-
name: "Frontend Engineer",
|
|
70771
|
+
role: "senior-frontend-engineer",
|
|
70772
|
+
name: "Senior Frontend Engineer",
|
|
70759
70773
|
description: "Implements user-facing experiences, UI state, and presentation-layer changes."
|
|
70760
70774
|
});
|
|
70761
70775
|
}
|
|
70762
70776
|
if (/(node|express|api|backend|server|daemon|database|sql|postgres|sqlite)/.test(analysis)) {
|
|
70763
70777
|
roles.push({
|
|
70764
|
-
role: "backend-engineer",
|
|
70765
|
-
name: "Backend Engineer",
|
|
70778
|
+
role: "senior-backend-engineer",
|
|
70779
|
+
name: "Senior Backend Engineer",
|
|
70766
70780
|
description: "Implements server-side logic, data access, integrations, and execution flow changes."
|
|
70767
70781
|
});
|
|
70768
70782
|
}
|
|
70769
70783
|
if (/(infra|docker|kubernetes|deploy|ci|cd|workflow|github actions)/.test(analysis)) {
|
|
70770
70784
|
roles.push({
|
|
70771
|
-
role: "platform-engineer",
|
|
70772
|
-
name: "Platform Engineer",
|
|
70785
|
+
role: "senior-platform-engineer",
|
|
70786
|
+
name: "Senior Platform Engineer",
|
|
70773
70787
|
description: "Owns automation, pipelines, environments, and operational tooling."
|
|
70774
70788
|
});
|
|
70775
70789
|
}
|
|
70776
70790
|
if (/(security|auth|oauth|secret|policy)/.test(analysis)) {
|
|
70777
70791
|
roles.push({
|
|
70778
|
-
role: "security-engineer",
|
|
70779
|
-
name: "Security Engineer",
|
|
70792
|
+
role: "senior-security-engineer",
|
|
70793
|
+
name: "Senior Security Engineer",
|
|
70780
70794
|
description: "Reviews authentication, authorization, secrets handling, and security-sensitive changes."
|
|
70781
70795
|
});
|
|
70782
70796
|
}
|
|
@@ -70836,13 +70850,17 @@ ${repoContext}`;
|
|
|
70836
70850
|
}
|
|
70837
70851
|
return generateRolePrompt(normalized, repoContext);
|
|
70838
70852
|
}
|
|
70839
|
-
async function proposeSquadComposition(repoUrl, repoAnalysis) {
|
|
70853
|
+
async function proposeSquadComposition(repoUrl, repoAnalysis, context) {
|
|
70840
70854
|
const mandatory = buildMandatoryRoles();
|
|
70841
70855
|
const fallbackAdditional = dedupeRoles(detectRolesFromAnalysis(repoAnalysis));
|
|
70856
|
+
const contextSection = context ? `
|
|
70857
|
+
|
|
70858
|
+
Additional context from user:
|
|
70859
|
+
${context}` : "";
|
|
70842
70860
|
const prompt = `Repository URL: ${repoUrl}
|
|
70843
70861
|
|
|
70844
70862
|
Repository analysis:
|
|
70845
|
-
${repoAnalysis}
|
|
70863
|
+
${repoAnalysis}${contextSection}
|
|
70846
70864
|
|
|
70847
70865
|
${ROLE_GENERATION_PROMPT}`;
|
|
70848
70866
|
let client2 = null;
|
|
@@ -70972,19 +70990,72 @@ function getDefaultSquadConfig(_config) {
|
|
|
70972
70990
|
maxRevisions: QA_MAX_REVISIONS
|
|
70973
70991
|
};
|
|
70974
70992
|
}
|
|
70975
|
-
async function
|
|
70976
|
-
const
|
|
70977
|
-
const
|
|
70978
|
-
|
|
70979
|
-
|
|
70980
|
-
|
|
70981
|
-
|
|
70982
|
-
|
|
70983
|
-
|
|
70984
|
-
|
|
70985
|
-
|
|
70993
|
+
async function readManifests(rootPath, rootLabel) {
|
|
70994
|
+
const lines = [];
|
|
70995
|
+
for (const manifest of MANIFEST_FILES) {
|
|
70996
|
+
const fullPath = join15(rootPath, manifest);
|
|
70997
|
+
try {
|
|
70998
|
+
const fileStat = await stat4(fullPath);
|
|
70999
|
+
if (fileStat.isFile()) {
|
|
71000
|
+
const content = await readFile9(fullPath, "utf8");
|
|
71001
|
+
lines.push(`
|
|
71002
|
+
--- ${rootLabel}${manifest} ---
|
|
71003
|
+
${content.slice(0, 3e3)}`);
|
|
71004
|
+
} else if (fileStat.isDirectory()) {
|
|
71005
|
+
const children = await readdir6(fullPath);
|
|
71006
|
+
lines.push(`
|
|
71007
|
+
--- ${rootLabel}${manifest}/ ---
|
|
71008
|
+
${children.join(", ")}`);
|
|
71009
|
+
}
|
|
71010
|
+
} catch {
|
|
71011
|
+
}
|
|
70986
71012
|
}
|
|
70987
|
-
|
|
71013
|
+
return lines;
|
|
71014
|
+
}
|
|
71015
|
+
async function scanSrcDirs(rootPath, rootLabel, dirs) {
|
|
71016
|
+
const lines = [];
|
|
71017
|
+
const srcDirs = dirs.filter((d) => SRC_DIR_NAMES.includes(d));
|
|
71018
|
+
for (const srcDir of srcDirs) {
|
|
71019
|
+
try {
|
|
71020
|
+
const srcEntries = await readdir6(join15(rootPath, srcDir), { withFileTypes: true });
|
|
71021
|
+
const srcFiles = srcEntries.filter((e) => e.isFile()).map((e) => e.name);
|
|
71022
|
+
const srcSubDirs = srcEntries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
71023
|
+
lines.push(`
|
|
71024
|
+
--- ${rootLabel}${srcDir}/ ---`);
|
|
71025
|
+
if (srcSubDirs.length) lines.push(` Directories: ${srcSubDirs.join(", ")}`);
|
|
71026
|
+
if (srcFiles.length) lines.push(` Files: ${srcFiles.slice(0, 30).join(", ")}`);
|
|
71027
|
+
} catch {
|
|
71028
|
+
}
|
|
71029
|
+
}
|
|
71030
|
+
return lines;
|
|
71031
|
+
}
|
|
71032
|
+
async function findReadme(rootPath, rootLabel) {
|
|
71033
|
+
for (const readme of README_CANDIDATES2) {
|
|
71034
|
+
try {
|
|
71035
|
+
const content = await readFile9(join15(rootPath, readme), "utf8");
|
|
71036
|
+
return `
|
|
71037
|
+
--- ${rootLabel}${readme} (excerpt) ---
|
|
71038
|
+
${content.slice(0, 1500)}`;
|
|
71039
|
+
} catch {
|
|
71040
|
+
}
|
|
71041
|
+
}
|
|
71042
|
+
return null;
|
|
71043
|
+
}
|
|
71044
|
+
async function scanRoot(root) {
|
|
71045
|
+
const lines = [];
|
|
71046
|
+
const rootLabel = root.label ? `${root.label}/` : "";
|
|
71047
|
+
const rootEntries = await readdir6(root.path, { withFileTypes: true });
|
|
71048
|
+
const rootFiles = rootEntries.filter((e) => e.isFile()).map((e) => e.name);
|
|
71049
|
+
const rootDirs = rootEntries.filter((e) => e.isDirectory() && e.name !== ".git").map((e) => e.name);
|
|
71050
|
+
if (rootFiles.length) lines.push(`${rootLabel}Files: ${rootFiles.join(", ")}`);
|
|
71051
|
+
if (rootDirs.length) lines.push(`${rootLabel}Directories: ${rootDirs.join(", ")}`);
|
|
71052
|
+
lines.push(...await readManifests(root.path, rootLabel));
|
|
71053
|
+
lines.push(...await scanSrcDirs(root.path, rootLabel, rootDirs));
|
|
71054
|
+
const readme = await findReadme(root.path, rootLabel);
|
|
71055
|
+
if (readme) lines.push(readme);
|
|
71056
|
+
return lines;
|
|
71057
|
+
}
|
|
71058
|
+
async function cloneOrUpdateRepo(normalized, repoDir) {
|
|
70988
71059
|
try {
|
|
70989
71060
|
await mkdir10(join15(DATA_DIR, "repos"), { recursive: true });
|
|
70990
71061
|
if (await pathExists2(join15(repoDir, ".git"))) {
|
|
@@ -70996,79 +71067,40 @@ async function buildRepoAnalysis(repoUrl) {
|
|
|
70996
71067
|
timeout: 6e4
|
|
70997
71068
|
});
|
|
70998
71069
|
}
|
|
71070
|
+
return true;
|
|
70999
71071
|
} catch {
|
|
71072
|
+
return false;
|
|
71073
|
+
}
|
|
71074
|
+
}
|
|
71075
|
+
async function buildRepoAnalysis(repoUrl, scanPaths) {
|
|
71076
|
+
const normalized = repoUrl.trim();
|
|
71077
|
+
const segments = normalized.replace(/\.git$/i, "").split("/").filter(Boolean);
|
|
71078
|
+
const owner = segments.at(-2) ?? "";
|
|
71079
|
+
const name = segments.at(-1) ?? normalized;
|
|
71080
|
+
const lines = [`Repository URL: ${normalized}`, `Repository name: ${name}`];
|
|
71081
|
+
if (!owner || !name) {
|
|
71082
|
+
lines.push(
|
|
71083
|
+
"Use the repository identity and any available conventions to propose a practical squad composition."
|
|
71084
|
+
);
|
|
71085
|
+
return lines.join("\n");
|
|
71086
|
+
}
|
|
71087
|
+
const repoDir = join15(DATA_DIR, "repos", `${owner}--${name}`);
|
|
71088
|
+
const cloned = await cloneOrUpdateRepo(normalized, repoDir);
|
|
71089
|
+
if (!cloned) {
|
|
71000
71090
|
lines.push("Unable to clone repository locally; falling back to basic analysis.");
|
|
71001
71091
|
lines.push("Based on the repository name, propose roles that match common project patterns.");
|
|
71002
71092
|
return lines.join("\n");
|
|
71003
71093
|
}
|
|
71004
|
-
|
|
71005
|
-
|
|
71006
|
-
|
|
71007
|
-
|
|
71008
|
-
|
|
71009
|
-
|
|
71010
|
-
|
|
71011
|
-
|
|
71012
|
-
|
|
71013
|
-
"go.mod",
|
|
71014
|
-
"requirements.txt",
|
|
71015
|
-
"pyproject.toml",
|
|
71016
|
-
"Gemfile",
|
|
71017
|
-
"pom.xml",
|
|
71018
|
-
"build.gradle",
|
|
71019
|
-
"composer.json",
|
|
71020
|
-
"Makefile",
|
|
71021
|
-
"Dockerfile",
|
|
71022
|
-
"docker-compose.yml",
|
|
71023
|
-
"docker-compose.yaml",
|
|
71024
|
-
".github/workflows"
|
|
71025
|
-
];
|
|
71026
|
-
for (const manifest of manifestFiles) {
|
|
71027
|
-
const fullPath = join15(repoDir, manifest);
|
|
71028
|
-
try {
|
|
71029
|
-
const fileStat = await stat4(fullPath);
|
|
71030
|
-
if (fileStat.isFile()) {
|
|
71031
|
-
const content = await readFile9(fullPath, "utf8");
|
|
71032
|
-
lines.push(`
|
|
71033
|
-
--- ${manifest} ---
|
|
71034
|
-
${content.slice(0, 3e3)}`);
|
|
71035
|
-
} else if (fileStat.isDirectory()) {
|
|
71036
|
-
const children = await readdir6(fullPath);
|
|
71037
|
-
lines.push(`
|
|
71038
|
-
--- ${manifest}/ ---
|
|
71039
|
-
${children.join(", ")}`);
|
|
71040
|
-
}
|
|
71041
|
-
} catch {
|
|
71042
|
-
}
|
|
71043
|
-
}
|
|
71044
|
-
const srcDirs = rootDirs.filter(
|
|
71045
|
-
(d) => ["src", "lib", "app", "packages", "crates", "cmd", "internal"].includes(d)
|
|
71046
|
-
);
|
|
71047
|
-
for (const srcDir of srcDirs) {
|
|
71048
|
-
try {
|
|
71049
|
-
const srcEntries = await readdir6(join15(repoDir, srcDir), { withFileTypes: true });
|
|
71050
|
-
const srcFiles = srcEntries.filter((e) => e.isFile()).map((e) => e.name);
|
|
71051
|
-
const srcSubDirs = srcEntries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
71052
|
-
lines.push(`
|
|
71053
|
-
--- ${srcDir}/ ---`);
|
|
71054
|
-
if (srcSubDirs.length) lines.push(` Directories: ${srcSubDirs.join(", ")}`);
|
|
71055
|
-
if (srcFiles.length) lines.push(` Files: ${srcFiles.slice(0, 30).join(", ")}`);
|
|
71056
|
-
} catch {
|
|
71057
|
-
}
|
|
71058
|
-
}
|
|
71059
|
-
const readmeCandidates = ["README.md", "README.rst", "README.txt", "README"];
|
|
71060
|
-
for (const readme of readmeCandidates) {
|
|
71061
|
-
try {
|
|
71062
|
-
const content = await readFile9(join15(repoDir, readme), "utf8");
|
|
71063
|
-
lines.push(`
|
|
71064
|
-
--- ${readme} (excerpt) ---
|
|
71065
|
-
${content.slice(0, 1500)}`);
|
|
71066
|
-
break;
|
|
71067
|
-
} catch {
|
|
71068
|
-
}
|
|
71094
|
+
const scanRoots = scanPaths && scanPaths.length > 0 ? scanPaths.map((p) => ({ path: join15(repoDir, p), label: p })) : [{ path: repoDir, label: "" }];
|
|
71095
|
+
if (scanPaths && scanPaths.length > 0) {
|
|
71096
|
+
lines.push(`Focused scan paths: ${scanPaths.join(", ")}`);
|
|
71097
|
+
}
|
|
71098
|
+
for (const root of scanRoots) {
|
|
71099
|
+
try {
|
|
71100
|
+
lines.push(...await scanRoot(root));
|
|
71101
|
+
} catch {
|
|
71102
|
+
lines.push(`Filesystem scan failed for ${root.label || "repo root"}; skipping.`);
|
|
71069
71103
|
}
|
|
71070
|
-
} catch {
|
|
71071
|
-
lines.push("Filesystem scan failed; using minimal info.");
|
|
71072
71104
|
}
|
|
71073
71105
|
lines.push(
|
|
71074
71106
|
"\nBased on the above repository structure and contents, propose roles that match the project's actual technology stack and architecture."
|
|
@@ -71083,6 +71115,22 @@ function formatSquadList(squads) {
|
|
|
71083
71115
|
(squad) => `${squad.id}: ${squad.name} (${squad.repoOwner}/${squad.repoName}) [${squad.status}]`
|
|
71084
71116
|
).join("\n");
|
|
71085
71117
|
}
|
|
71118
|
+
async function processQueue2(squadId) {
|
|
71119
|
+
try {
|
|
71120
|
+
const running = await countRunningInstances(squadId);
|
|
71121
|
+
const config2 = (await Promise.resolve().then(() => (init_config(), config_exports))).loadConfig();
|
|
71122
|
+
if (running < config2.maxInstancesPerSquad) {
|
|
71123
|
+
const next = await getNextQueued(squadId);
|
|
71124
|
+
if (next) {
|
|
71125
|
+
const freshSquad = await getSquad(squadId);
|
|
71126
|
+
if (freshSquad) {
|
|
71127
|
+
void startAndExecuteInstance(next.id, freshSquad, next.objectiveId);
|
|
71128
|
+
}
|
|
71129
|
+
}
|
|
71130
|
+
}
|
|
71131
|
+
} catch {
|
|
71132
|
+
}
|
|
71133
|
+
}
|
|
71086
71134
|
async function startAndExecuteInstance(instanceId, squad, objectiveId) {
|
|
71087
71135
|
try {
|
|
71088
71136
|
const repoPath = await resolveRepoPath(squad.repoUrl, squad.repoName);
|
|
@@ -71103,56 +71151,96 @@ async function startAndExecuteInstance(instanceId, squad, objectiveId) {
|
|
|
71103
71151
|
await failInstance(instanceId, message2).catch(() => {
|
|
71104
71152
|
});
|
|
71105
71153
|
} finally {
|
|
71106
|
-
|
|
71107
|
-
|
|
71108
|
-
|
|
71109
|
-
|
|
71110
|
-
|
|
71111
|
-
|
|
71112
|
-
|
|
71113
|
-
|
|
71114
|
-
|
|
71115
|
-
|
|
71116
|
-
|
|
71117
|
-
|
|
71118
|
-
|
|
71119
|
-
|
|
71154
|
+
await processQueue2(squad.id);
|
|
71155
|
+
}
|
|
71156
|
+
}
|
|
71157
|
+
async function handleHireSquad(rawArgs, config2) {
|
|
71158
|
+
const { repoUrl, context, scanPaths } = hireSquadSchema.parse(rawArgs);
|
|
71159
|
+
const composition = await proposeSquadComposition(
|
|
71160
|
+
repoUrl,
|
|
71161
|
+
await buildRepoAnalysis(repoUrl, scanPaths),
|
|
71162
|
+
context
|
|
71163
|
+
);
|
|
71164
|
+
const result = await hireSquad(repoUrl, composition, getDefaultSquadConfig(config2));
|
|
71165
|
+
return {
|
|
71166
|
+
message: `Squad ready for ${repoUrl}.`,
|
|
71167
|
+
squad: result.squad,
|
|
71168
|
+
members: result.members
|
|
71169
|
+
};
|
|
71170
|
+
}
|
|
71171
|
+
async function handleFireSquad(rawArgs) {
|
|
71172
|
+
const { squadId } = squadIdSchema.parse(rawArgs);
|
|
71173
|
+
const squad = await getSquad(squadId);
|
|
71174
|
+
if (!squad) {
|
|
71175
|
+
throw new Error(`Squad ${squadId} was not found.`);
|
|
71176
|
+
}
|
|
71177
|
+
const activeObjectives = await getActiveObjectives(squadId);
|
|
71178
|
+
if (activeObjectives.length > 0) {
|
|
71179
|
+
const updated = await updateSquad(squadId, { status: "inactive" });
|
|
71180
|
+
return {
|
|
71181
|
+
message: `Squad ${squadId} was deactivated because it still has active objectives.`,
|
|
71182
|
+
squad: updated,
|
|
71183
|
+
activeObjectives
|
|
71184
|
+
};
|
|
71185
|
+
}
|
|
71186
|
+
await deleteSquad(squadId);
|
|
71187
|
+
return { message: `Squad ${squadId} was deleted.`, squadId };
|
|
71188
|
+
}
|
|
71189
|
+
async function handleDelegateToSquad(rawArgs) {
|
|
71190
|
+
const { squadId, objective } = delegateToSquadSchema.parse(rawArgs);
|
|
71191
|
+
const squad = await getSquad(squadId);
|
|
71192
|
+
if (!squad) {
|
|
71193
|
+
throw new Error(`Squad ${squadId} was not found.`);
|
|
71194
|
+
}
|
|
71195
|
+
const createdObjective = await createObjective(squadId, objective);
|
|
71196
|
+
const { instance, queued } = await spawnInstance({
|
|
71197
|
+
squadId,
|
|
71198
|
+
objectiveId: createdObjective.id
|
|
71199
|
+
});
|
|
71200
|
+
if (!queued) {
|
|
71201
|
+
void startAndExecuteInstance(instance.id, squad, createdObjective.id);
|
|
71202
|
+
}
|
|
71203
|
+
return {
|
|
71204
|
+
message: queued ? `Objective queued for squad ${squad.name} (at capacity). Instance ${instance.id} will start when a slot opens.` : `Delegated objective to squad ${squad.name}. Instance ${instance.id} started.`,
|
|
71205
|
+
objective: createdObjective,
|
|
71206
|
+
instanceId: instance.id,
|
|
71207
|
+
queued
|
|
71208
|
+
};
|
|
71209
|
+
}
|
|
71210
|
+
async function handleUpdateSquadMember(rawArgs) {
|
|
71211
|
+
const { squadId, memberId, role, systemPrompt, model } = updateSquadMemberSchema.parse(rawArgs);
|
|
71212
|
+
const squad = await getSquad(squadId);
|
|
71213
|
+
if (!squad) {
|
|
71214
|
+
throw new Error(`Squad ${squadId} was not found.`);
|
|
71215
|
+
}
|
|
71216
|
+
const member = await getMember(memberId);
|
|
71217
|
+
if (!member || member.squadId !== squadId) {
|
|
71218
|
+
throw new Error(`Member ${memberId} was not found in squad ${squadId}.`);
|
|
71120
71219
|
}
|
|
71220
|
+
const updated = await updateMember(memberId, {
|
|
71221
|
+
role,
|
|
71222
|
+
systemPrompt,
|
|
71223
|
+
model: model === "" ? null : model
|
|
71224
|
+
});
|
|
71225
|
+
if (!updated) {
|
|
71226
|
+
throw new Error(`Failed to update member ${memberId}.`);
|
|
71227
|
+
}
|
|
71228
|
+
eventBus.emit(EVENT_NAMES.SQUAD_MEMBER_UPDATED, {
|
|
71229
|
+
squadId,
|
|
71230
|
+
member: updated
|
|
71231
|
+
});
|
|
71232
|
+
return {
|
|
71233
|
+
message: `Updated member "${updated.name}" in squad "${squad.name}".`,
|
|
71234
|
+
member: updated
|
|
71235
|
+
};
|
|
71121
71236
|
}
|
|
71122
71237
|
function createSquadToolExecutor(config2) {
|
|
71123
71238
|
return async (toolName, rawArgs) => {
|
|
71124
71239
|
switch (toolName) {
|
|
71125
|
-
case "hire_squad":
|
|
71126
|
-
|
|
71127
|
-
|
|
71128
|
-
|
|
71129
|
-
await buildRepoAnalysis(repoUrl)
|
|
71130
|
-
);
|
|
71131
|
-
const result = await hireSquad(repoUrl, composition, getDefaultSquadConfig(config2));
|
|
71132
|
-
return {
|
|
71133
|
-
message: `Squad ready for ${repoUrl}.`,
|
|
71134
|
-
squad: result.squad,
|
|
71135
|
-
members: result.members
|
|
71136
|
-
};
|
|
71137
|
-
}
|
|
71138
|
-
case "fire_squad": {
|
|
71139
|
-
const { squadId } = squadIdSchema.parse(rawArgs);
|
|
71140
|
-
const squad = await getSquad(squadId);
|
|
71141
|
-
if (!squad) {
|
|
71142
|
-
throw new Error(`Squad ${squadId} was not found.`);
|
|
71143
|
-
}
|
|
71144
|
-
const activeObjectives = await getActiveObjectives(squadId);
|
|
71145
|
-
if (activeObjectives.length > 0) {
|
|
71146
|
-
const updated = await updateSquad(squadId, { status: "inactive" });
|
|
71147
|
-
return {
|
|
71148
|
-
message: `Squad ${squadId} was deactivated because it still has active objectives.`,
|
|
71149
|
-
squad: updated,
|
|
71150
|
-
activeObjectives
|
|
71151
|
-
};
|
|
71152
|
-
}
|
|
71153
|
-
await deleteSquad(squadId);
|
|
71154
|
-
return { message: `Squad ${squadId} was deleted.`, squadId };
|
|
71155
|
-
}
|
|
71240
|
+
case "hire_squad":
|
|
71241
|
+
return handleHireSquad(rawArgs, config2);
|
|
71242
|
+
case "fire_squad":
|
|
71243
|
+
return handleFireSquad(rawArgs);
|
|
71156
71244
|
case "list_squads": {
|
|
71157
71245
|
const squads = await listSquads();
|
|
71158
71246
|
return {
|
|
@@ -71168,39 +71256,35 @@ function createSquadToolExecutor(config2) {
|
|
|
71168
71256
|
...status
|
|
71169
71257
|
};
|
|
71170
71258
|
}
|
|
71171
|
-
case "delegate_to_squad":
|
|
71172
|
-
|
|
71173
|
-
|
|
71174
|
-
|
|
71259
|
+
case "delegate_to_squad":
|
|
71260
|
+
return handleDelegateToSquad(rawArgs);
|
|
71261
|
+
case "rename_squad": {
|
|
71262
|
+
const { squadId, name } = renameSquadSchema.parse(rawArgs);
|
|
71263
|
+
const updated = await updateSquad(squadId, { name });
|
|
71264
|
+
if (!updated) {
|
|
71175
71265
|
throw new Error(`Squad ${squadId} was not found.`);
|
|
71176
71266
|
}
|
|
71177
|
-
|
|
71178
|
-
const { instance, queued } = await spawnInstance({
|
|
71179
|
-
squadId,
|
|
71180
|
-
objectiveId: createdObjective.id
|
|
71181
|
-
});
|
|
71182
|
-
if (!queued) {
|
|
71183
|
-
void startAndExecuteInstance(instance.id, squad, createdObjective.id);
|
|
71184
|
-
}
|
|
71267
|
+
eventBus.emit(EVENT_NAMES.SQUAD_UPDATED, { squad: updated });
|
|
71185
71268
|
return {
|
|
71186
|
-
message:
|
|
71187
|
-
|
|
71188
|
-
instanceId: instance.id,
|
|
71189
|
-
queued
|
|
71269
|
+
message: `Squad renamed to "${updated.name}".`,
|
|
71270
|
+
squad: updated
|
|
71190
71271
|
};
|
|
71191
71272
|
}
|
|
71273
|
+
case "update_squad_member":
|
|
71274
|
+
return handleUpdateSquadMember(rawArgs);
|
|
71192
71275
|
default:
|
|
71193
71276
|
throw new Error(`Unsupported squad tool: ${toolName}`);
|
|
71194
71277
|
}
|
|
71195
71278
|
};
|
|
71196
71279
|
}
|
|
71197
|
-
var execAsync8, hireSquadSchema, squadIdSchema, delegateToSquadSchema, squadToolDefinitions;
|
|
71280
|
+
var execAsync8, hireSquadSchema, squadIdSchema, delegateToSquadSchema, renameSquadSchema, updateSquadMemberSchema, squadToolDefinitions, MANIFEST_FILES, SRC_DIR_NAMES, README_CANDIDATES2;
|
|
71198
71281
|
var init_squad2 = __esm({
|
|
71199
71282
|
"packages/daemon/src/orchestrator/tools/squad.ts"() {
|
|
71200
71283
|
"use strict";
|
|
71201
71284
|
init_dist();
|
|
71202
71285
|
init_paths();
|
|
71203
71286
|
init_zod();
|
|
71287
|
+
init_event_bus();
|
|
71204
71288
|
init_instances2();
|
|
71205
71289
|
init_runner();
|
|
71206
71290
|
init_hiring();
|
|
@@ -71208,7 +71292,13 @@ var init_squad2 = __esm({
|
|
|
71208
71292
|
init_store2();
|
|
71209
71293
|
execAsync8 = promisify8(exec8);
|
|
71210
71294
|
hireSquadSchema = external_exports.object({
|
|
71211
|
-
repoUrl: external_exports.string().trim().min(1)
|
|
71295
|
+
repoUrl: external_exports.string().trim().min(1).describe("The repository URL to hire a squad for"),
|
|
71296
|
+
context: external_exports.string().optional().describe(
|
|
71297
|
+
"Additional context from the user to guide squad composition and role selection. Include any specifics about what areas of the codebase the squad will focus on, what technologies matter most, or what kind of work they will do."
|
|
71298
|
+
),
|
|
71299
|
+
scanPaths: external_exports.array(external_exports.string()).optional().describe(
|
|
71300
|
+
"Relative paths within the repository to focus the analysis on. When provided, only these directories are scanned for technology detection instead of the entire repo. Extract these from the user's context when they mention specific folders, solution files, or project subsets."
|
|
71301
|
+
)
|
|
71212
71302
|
});
|
|
71213
71303
|
squadIdSchema = external_exports.object({
|
|
71214
71304
|
squadId: external_exports.string().trim().min(1)
|
|
@@ -71217,10 +71307,21 @@ var init_squad2 = __esm({
|
|
|
71217
71307
|
squadId: external_exports.string().trim().min(1),
|
|
71218
71308
|
objective: external_exports.string().trim().min(1)
|
|
71219
71309
|
});
|
|
71310
|
+
renameSquadSchema = external_exports.object({
|
|
71311
|
+
squadId: external_exports.string().trim().min(1),
|
|
71312
|
+
name: external_exports.string().trim().min(1)
|
|
71313
|
+
});
|
|
71314
|
+
updateSquadMemberSchema = external_exports.object({
|
|
71315
|
+
squadId: external_exports.string().trim().min(1),
|
|
71316
|
+
memberId: external_exports.string().trim().min(1),
|
|
71317
|
+
role: external_exports.string().trim().min(1).optional(),
|
|
71318
|
+
systemPrompt: external_exports.string().optional(),
|
|
71319
|
+
model: external_exports.string().optional()
|
|
71320
|
+
});
|
|
71220
71321
|
squadToolDefinitions = [
|
|
71221
71322
|
{
|
|
71222
71323
|
name: "hire_squad",
|
|
71223
|
-
description: "Hire or refresh a squad for a repository.",
|
|
71324
|
+
description: "Hire or refresh a squad for a repository. Optionally provide context to guide role selection and scanPaths to focus the repo analysis on specific directories.",
|
|
71224
71325
|
parameters: hireSquadSchema,
|
|
71225
71326
|
skipPermission: true
|
|
71226
71327
|
},
|
|
@@ -71247,8 +71348,38 @@ var init_squad2 = __esm({
|
|
|
71247
71348
|
description: "Create an objective for a squad and start execution.",
|
|
71248
71349
|
parameters: delegateToSquadSchema,
|
|
71249
71350
|
skipPermission: true
|
|
71351
|
+
},
|
|
71352
|
+
{
|
|
71353
|
+
name: "rename_squad",
|
|
71354
|
+
description: "Rename a squad.",
|
|
71355
|
+
parameters: renameSquadSchema,
|
|
71356
|
+
skipPermission: true
|
|
71357
|
+
},
|
|
71358
|
+
{
|
|
71359
|
+
name: "update_squad_member",
|
|
71360
|
+
description: "Update a squad member's role, system prompt, and/or default model. All fields are optional; only provided fields are changed.",
|
|
71361
|
+
parameters: updateSquadMemberSchema,
|
|
71362
|
+
skipPermission: true
|
|
71250
71363
|
}
|
|
71251
71364
|
];
|
|
71365
|
+
MANIFEST_FILES = [
|
|
71366
|
+
"package.json",
|
|
71367
|
+
"Cargo.toml",
|
|
71368
|
+
"go.mod",
|
|
71369
|
+
"requirements.txt",
|
|
71370
|
+
"pyproject.toml",
|
|
71371
|
+
"Gemfile",
|
|
71372
|
+
"pom.xml",
|
|
71373
|
+
"build.gradle",
|
|
71374
|
+
"composer.json",
|
|
71375
|
+
"Makefile",
|
|
71376
|
+
"Dockerfile",
|
|
71377
|
+
"docker-compose.yml",
|
|
71378
|
+
"docker-compose.yaml",
|
|
71379
|
+
".github/workflows"
|
|
71380
|
+
];
|
|
71381
|
+
SRC_DIR_NAMES = ["src", "lib", "app", "packages", "crates", "cmd", "internal"];
|
|
71382
|
+
README_CANDIDATES2 = ["README.md", "README.rst", "README.txt", "README"];
|
|
71252
71383
|
}
|
|
71253
71384
|
});
|
|
71254
71385
|
|
|
@@ -71259,6 +71390,30 @@ function buildMemoryPath(timestamp) {
|
|
|
71259
71390
|
const time3 = iso.slice(11, 19).replace(/:/gu, "-");
|
|
71260
71391
|
return `memory/${date5}/${time3}.md`;
|
|
71261
71392
|
}
|
|
71393
|
+
async function handleWikiWrite(rawArgs) {
|
|
71394
|
+
const { path, title, content, tags } = wikiWriteSchema.parse(rawArgs);
|
|
71395
|
+
const existing = await getPage(path);
|
|
71396
|
+
const page = existing ? await updatePage(path, { title, content, tags: tags ?? [] }) : await createPage(path, title, content, tags ?? []);
|
|
71397
|
+
return {
|
|
71398
|
+
message: `${existing ? "Updated" : "Created"} wiki page ${path}.`,
|
|
71399
|
+
page
|
|
71400
|
+
};
|
|
71401
|
+
}
|
|
71402
|
+
async function handleRecall(rawArgs) {
|
|
71403
|
+
const { query } = recallSchema.parse(rawArgs);
|
|
71404
|
+
const [matches, recents] = await Promise.all([searchPages(query, 1), getRecentPages(1)]);
|
|
71405
|
+
const page = matches[0] ?? recents[0] ?? null;
|
|
71406
|
+
if (!page) {
|
|
71407
|
+
return { message: "No wiki content is available yet." };
|
|
71408
|
+
}
|
|
71409
|
+
return {
|
|
71410
|
+
message: `Best match: ${page.title}`,
|
|
71411
|
+
path: page.path,
|
|
71412
|
+
title: page.title,
|
|
71413
|
+
content: page.content,
|
|
71414
|
+
tags: page.tags
|
|
71415
|
+
};
|
|
71416
|
+
}
|
|
71262
71417
|
var wikiReadSchema, wikiWriteSchema, wikiSearchSchema, rememberSchema, recallSchema, wikiToolDefinitions, executeWikiToolCall;
|
|
71263
71418
|
var init_wiki4 = __esm({
|
|
71264
71419
|
"packages/daemon/src/orchestrator/tools/wiki.ts"() {
|
|
@@ -71328,15 +71483,8 @@ var init_wiki4 = __esm({
|
|
|
71328
71483
|
}
|
|
71329
71484
|
return { page };
|
|
71330
71485
|
}
|
|
71331
|
-
case "wiki_write":
|
|
71332
|
-
|
|
71333
|
-
const existing = await getPage(path);
|
|
71334
|
-
const page = existing ? await updatePage(path, { title, content, tags: tags ?? [] }) : await createPage(path, title, content, tags ?? []);
|
|
71335
|
-
return {
|
|
71336
|
-
message: `${existing ? "Updated" : "Created"} wiki page ${path}.`,
|
|
71337
|
-
page
|
|
71338
|
-
};
|
|
71339
|
-
}
|
|
71486
|
+
case "wiki_write":
|
|
71487
|
+
return handleWikiWrite(rawArgs);
|
|
71340
71488
|
case "wiki_search": {
|
|
71341
71489
|
const { query, limit } = wikiSearchSchema.parse(rawArgs);
|
|
71342
71490
|
const pages = await searchPages(query, limit ?? 5);
|
|
@@ -71356,21 +71504,8 @@ var init_wiki4 = __esm({
|
|
|
71356
71504
|
page
|
|
71357
71505
|
};
|
|
71358
71506
|
}
|
|
71359
|
-
case "recall":
|
|
71360
|
-
|
|
71361
|
-
const [matches, recents] = await Promise.all([searchPages(query, 1), getRecentPages(1)]);
|
|
71362
|
-
const page = matches[0] ?? recents[0] ?? null;
|
|
71363
|
-
if (!page) {
|
|
71364
|
-
return { message: "No wiki content is available yet." };
|
|
71365
|
-
}
|
|
71366
|
-
return {
|
|
71367
|
-
message: `Best match: ${page.title}`,
|
|
71368
|
-
path: page.path,
|
|
71369
|
-
title: page.title,
|
|
71370
|
-
content: page.content,
|
|
71371
|
-
tags: page.tags
|
|
71372
|
-
};
|
|
71373
|
-
}
|
|
71507
|
+
case "recall":
|
|
71508
|
+
return handleRecall(rawArgs);
|
|
71374
71509
|
default:
|
|
71375
71510
|
throw new Error(`Unsupported wiki tool: ${toolName}`);
|
|
71376
71511
|
}
|
|
@@ -71681,8 +71816,7 @@ var init_orchestrator = __esm({
|
|
|
71681
71816
|
}
|
|
71682
71817
|
const model = usage.model || this.activeModel || this.config.defaultModel;
|
|
71683
71818
|
const pricing = await getModelPricing(model);
|
|
71684
|
-
const
|
|
71685
|
-
const tokenUnitCost = pricing ? calculateTokenUnitCost(
|
|
71819
|
+
const cost = pricing ? calculateTokenUnitCost(
|
|
71686
71820
|
usage.inputTokens,
|
|
71687
71821
|
usage.outputTokens,
|
|
71688
71822
|
pricing.tokenInputMultiplier,
|
|
@@ -71692,9 +71826,7 @@ var init_orchestrator = __esm({
|
|
|
71692
71826
|
model,
|
|
71693
71827
|
inputTokens: usage.inputTokens,
|
|
71694
71828
|
outputTokens: usage.outputTokens,
|
|
71695
|
-
cost
|
|
71696
|
-
premiumRequestCost,
|
|
71697
|
-
tokenUnitCost
|
|
71829
|
+
cost
|
|
71698
71830
|
});
|
|
71699
71831
|
}
|
|
71700
71832
|
};
|