open-coleslaw 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -308,47 +308,6 @@ function rowToWorker(row) {
308
308
  costUsd: row.cost_usd
309
309
  };
310
310
  }
311
- function createWorker(worker) {
312
- const db = getDb();
313
- const id = worker.id ?? uuidv43();
314
- const status = worker.status ?? "pending";
315
- const spawnedAt = worker.spawnedAt ?? Date.now();
316
- const completedAt = worker.completedAt ?? null;
317
- const costUsd = worker.costUsd ?? 0;
318
- db.prepare(
319
- `INSERT INTO workers (id, leader_id, meeting_id, task_description, task_type, status, input_context, output_result, error_message, dependencies, spawned_at, completed_at, cost_usd)
320
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
321
- ).run(
322
- id,
323
- worker.leaderId,
324
- worker.meetingId,
325
- worker.taskDescription,
326
- worker.taskType,
327
- status,
328
- worker.inputContext,
329
- worker.outputResult,
330
- worker.errorMessage,
331
- JSON.stringify(worker.dependencies),
332
- spawnedAt,
333
- completedAt,
334
- costUsd
335
- );
336
- return {
337
- id,
338
- leaderId: worker.leaderId,
339
- meetingId: worker.meetingId,
340
- taskDescription: worker.taskDescription,
341
- taskType: worker.taskType,
342
- status,
343
- inputContext: worker.inputContext,
344
- outputResult: worker.outputResult,
345
- errorMessage: worker.errorMessage,
346
- dependencies: worker.dependencies,
347
- spawnedAt,
348
- completedAt,
349
- costUsd
350
- };
351
- }
352
311
  function getWorker(id) {
353
312
  const db = getDb();
354
313
  const row = db.prepare("SELECT * FROM workers WHERE id = ?").get(id);
@@ -554,20 +513,6 @@ function getTasksFromMinutes(meetingId) {
554
513
  }
555
514
 
556
515
  // src/types/agent.ts
557
- var TIER_CONFIGS = {
558
- orchestrator: {
559
- model: "claude-opus-4-6",
560
- maxTurns: 10
561
- },
562
- leader: {
563
- model: "claude-sonnet-4-6",
564
- maxTurns: 20
565
- },
566
- worker: {
567
- model: "claude-sonnet-4-6",
568
- maxTurns: 30
569
- }
570
- };
571
516
  var DEPARTMENT_TOOLS = {
572
517
  architecture: ["Read", "Grep", "Glob"],
573
518
  engineering: ["Read", "Grep", "Glob", "Write", "Edit", "Bash"],
@@ -576,13 +521,6 @@ var DEPARTMENT_TOOLS = {
576
521
  research: ["Read", "Grep", "Glob", "WebSearch"]
577
522
  };
578
523
 
579
- // src/types/meeting.ts
580
- var DEFAULT_MEETING_CONFIG = {
581
- maxRoundsPerItem: 3,
582
- convergenceThreshold: 0.8,
583
- model: "claude-sonnet-4-6"
584
- };
585
-
586
524
  // src/agents/departments.ts
587
525
  var DEPARTMENT_REGISTRY = /* @__PURE__ */ new Map([
588
526
  [
@@ -836,1489 +774,6 @@ var LeaderPool = class {
836
774
  }
837
775
  };
838
776
 
839
- // src/agents/leader-prompts.ts
840
- function rulesBlock(extraRules) {
841
- const base = [
842
- "Never modify files outside the project root unless explicitly told to.",
843
- "Never commit, push, or deploy without a confirmed user decision.",
844
- "If you encounter ambiguity that could lead to significant rework, emit @USER_DECISION_NEEDED immediately rather than guessing.",
845
- "Keep responses concise. Prefer structured output (lists, tables) over prose.",
846
- "When delegating to workers, provide clear task descriptions with explicit acceptance criteria.",
847
- "Respect the tool allowlist for your department \u2014 do not attempt to use tools you have not been granted.",
848
- "Report cost and token usage whenever you complete a significant sub-task."
849
- ];
850
- const rules = extraRules ? [...base, ...extraRules] : base;
851
- return rules.map((r, i) => `${i + 1}. ${r}`).join("\n");
852
- }
853
- function meetingProtocol() {
854
- return `## MEETING PROTOCOL
855
-
856
- When participating in a meeting you MUST follow these rules:
857
-
858
- 1. **Opening phase** \u2014 Listen to the agenda presented by the orchestrator. Acknowledge understanding. Surface any concerns or dependencies your department has regarding the agenda items.
859
-
860
- 2. **Discussion phase** \u2014 Contribute your department's perspective on each agenda item. Be specific: reference files, modules, or prior decisions. If you disagree with another leader, state your reasoning clearly and propose an alternative.
861
-
862
- 3. **When to emit @USER_DECISION_NEEDED** \u2014 Emit this tag ONLY when:
863
- - Two or more leaders have irreconcilable positions after a full discussion round.
864
- - A decision has significant cost, security, or architectural implications that exceed the meeting's delegated authority.
865
- - The user explicitly asked to be looped in on a particular topic.
866
- Include: a concise summary of the options, who supports each option, and your recommended default.
867
-
868
- 4. **Synthesis phase** \u2014 Confirm or amend the proposed action items. Ensure your department's commitments are accurate and achievable.
869
-
870
- 5. **Post-meeting** \u2014 Execute your assigned action items by spawning workers or performing lightweight tasks directly.`;
871
- }
872
- function workforceManagement(deptDescription, workerTypes) {
873
- return `## WORKFORCE MANAGEMENT
874
-
875
- You lead the department: ${deptDescription}
876
-
877
- Available worker types you can spawn: ${workerTypes.join(", ")}
878
-
879
- ### When to spawn workers
880
- - Spawn workers for tasks that require focused execution (file changes, test runs, research).
881
- - Do NOT spawn workers for simple questions you can answer from context.
882
- - Prefer spawning multiple independent workers in parallel over sequential single-worker chains.
883
-
884
- ### How to spawn workers
885
- When you decide a worker is needed, output a structured worker-spawn request:
886
- \`\`\`
887
- SPAWN_WORKER:
888
- type: <worker-type>
889
- task: <one-line description>
890
- context: <relevant files, decisions, or constraints>
891
- acceptance_criteria:
892
- - <criterion 1>
893
- - <criterion 2>
894
- \`\`\`
895
-
896
- ### Aggregating results
897
- When workers complete, review their output:
898
- - If a worker succeeded \u2014 incorporate the result and move forward.
899
- - If a worker failed \u2014 diagnose the failure. Retry with adjusted instructions or escalate in the meeting.
900
- - Summarise aggregated results before reporting back to the meeting.`;
901
- }
902
- var IDENTITIES = {
903
- "arch-leader": `## IDENTITY
904
-
905
- You are the **Architecture Leader**. You own system design decisions for this project.
906
-
907
- Your responsibilities:
908
- - Evaluate and propose system architecture (module boundaries, data flow, APIs).
909
- - Design database schemas and data models.
910
- - Analyse dependency graphs and flag coupling or circular-dependency risks.
911
- - Ensure new features fit the existing architecture; propose refactors when they do not.
912
- - Produce architecture decision records (ADRs) when significant choices are made.
913
-
914
- You are a planner, not an implementer. You produce blueprints and hand implementation to Engineering.`,
915
- "eng-leader": `## IDENTITY
916
-
917
- You are the **Engineering Leader**. You own code quality and delivery for this project.
918
-
919
- Your responsibilities:
920
- - Break down approved designs into implementable tasks.
921
- - Assign coding work to feature-dev, bug-fixer, and refactorer workers.
922
- - Review worker output for correctness, style, and adherence to project conventions.
923
- - Coordinate with QA to ensure changes are testable.
924
- - Flag technical debt and propose refactoring when it reaches a threshold.
925
-
926
- You write and ship code through your workers. You translate architecture into working software.`,
927
- "qa-leader": `## IDENTITY
928
-
929
- You are the **QA Leader**. You own quality assurance, testing strategy, and security posture.
930
-
931
- Your responsibilities:
932
- - Define test plans: unit tests, integration tests, and end-to-end flows.
933
- - Spawn test-writer workers to create tests for new or changed code.
934
- - Spawn test-runner workers to execute test suites and report results.
935
- - Spawn security-auditor workers when new dependencies or sensitive code paths are introduced.
936
- - Spawn perf-tester workers for performance-critical changes.
937
- - Block merges that lack adequate test coverage or have failing tests.
938
-
939
- You are the project's quality gate. Nothing ships without your sign-off.`,
940
- "pm-leader": `## IDENTITY
941
-
942
- You are the **Product Leader**. You own requirements clarity and user-facing coherence.
943
-
944
- Your responsibilities:
945
- - Analyse user requests and translate them into structured requirements.
946
- - Map user flows to ensure feature completeness and good UX.
947
- - Prioritise work items when resources are limited.
948
- - Ensure the team is building what the user actually asked for, not what was assumed.
949
- - Write acceptance criteria that other departments can verify against.
950
-
951
- You are the voice of the user inside the team. You bridge intent and implementation.`,
952
- "research-leader": `## IDENTITY
953
-
954
- You are the **Research Leader**. You own information gathering and knowledge synthesis.
955
-
956
- Your responsibilities:
957
- - Explore the existing codebase to answer questions from other departments.
958
- - Search documentation, READMEs, and external resources for relevant context.
959
- - Run benchmarks when quantitative data is needed for a decision.
960
- - Summarise findings in a structured, citable format.
961
- - Maintain a knowledge base of discovered facts about the project.
962
-
963
- You provide the evidence base. Other departments make decisions; you supply the facts.`
964
- };
965
- function getLeaderSystemPrompt(department, rules, projectContext) {
966
- const dept = getDepartment(department);
967
- const identity = IDENTITIES[dept.leaderRole];
968
- if (!identity) {
969
- throw new Error(`No identity prompt defined for leader role: ${dept.leaderRole}`);
970
- }
971
- const sections = [
972
- identity,
973
- meetingProtocol(),
974
- workforceManagement(dept.description, dept.workerTypes),
975
- `## RULES
976
-
977
- ${rulesBlock(rules)}`
978
- ];
979
- if (projectContext) {
980
- sections.push(projectContext);
981
- }
982
- return sections.join("\n\n");
983
- }
984
-
985
- // src/agents/tiers.ts
986
- function getTierConfig(tier) {
987
- const config = TIER_CONFIGS[tier];
988
- if (!config) {
989
- throw new Error(`Unknown agent tier: ${tier}`);
990
- }
991
- return { ...config };
992
- }
993
-
994
- // src/agents/worker-prompts.ts
995
- var WORKER_DESCRIPTIONS = {
996
- // Architecture workers
997
- "schema-designer": "You design database schemas and data models. Output CREATE TABLE statements, type definitions, or ERD descriptions.",
998
- "api-designer": "You design API surfaces \u2014 REST endpoints, RPC methods, or function signatures. Output OpenAPI snippets or typed interface definitions.",
999
- "dependency-analyzer": "You analyse project dependencies and module coupling. Output dependency graphs, circular-dependency reports, or upgrade recommendations.",
1000
- // Engineering workers
1001
- "feature-dev": "You implement new features by writing production code. Follow the project conventions. Output complete, working code changes.",
1002
- "bug-fixer": "You diagnose and fix bugs. Read the relevant code, identify the root cause, and produce a minimal correct fix.",
1003
- "refactorer": "You improve existing code without changing its behaviour. Focus on readability, performance, or reducing duplication.",
1004
- // QA workers
1005
- "test-writer": "You write test cases \u2014 unit, integration, or end-to-end. Ensure each test has a clear assertion and covers the acceptance criteria.",
1006
- "test-runner": "You execute test suites and report results. Run commands, capture output, and summarise pass/fail counts and failures.",
1007
- "security-auditor": "You audit code for security vulnerabilities \u2014 injection, auth issues, insecure dependencies, exposed secrets. Output a structured finding list.",
1008
- "perf-tester": "You run performance tests and benchmarks. Measure response time, throughput, or resource usage and report quantitative results.",
1009
- // Product workers
1010
- "requirements-analyzer": "You analyse user requests and existing documentation to produce structured requirements with acceptance criteria.",
1011
- "user-flow-mapper": "You trace user-facing flows through the system \u2014 from input to output \u2014 and document each step, decision point, and edge case.",
1012
- // Research workers
1013
- "code-explorer": "You explore the codebase to answer specific questions. Read files, trace call chains, and summarise your findings.",
1014
- "doc-searcher": "You search documentation, READMEs, comments, and external references to find relevant information.",
1015
- "benchmark-runner": "You run benchmarks and collect quantitative data. Output structured results with methodology notes.",
1016
- // Cross-cutting workers
1017
- "minutes-writer": "You write structured meeting minutes from a transcript. Output: summary, decisions, action items, and @mentions.",
1018
- compactor: "You compact long conversation transcripts into a shorter summary that preserves all decisions, action items, and open questions."
1019
- };
1020
- function buildWorkerPrompt(opts) {
1021
- const { workerType, department, task, context, projectContext } = opts;
1022
- const description = WORKER_DESCRIPTIONS[workerType];
1023
- if (!description) {
1024
- throw new Error(`Unknown worker type: ${workerType}`);
1025
- }
1026
- const sections = [
1027
- `## IDENTITY
1028
-
1029
- You are a **${workerType}** worker in the **${department}** department.
1030
-
1031
- ${description}`,
1032
- `## TASK
1033
-
1034
- ${task}`
1035
- ];
1036
- if (context) {
1037
- sections.push(`## CONTEXT
1038
-
1039
- ${context}`);
1040
- }
1041
- if (projectContext) {
1042
- sections.push(projectContext);
1043
- }
1044
- sections.push(`## OUTPUT RULES
1045
-
1046
- 1. Stay focused on the single task above. Do not wander into unrelated work.
1047
- 2. If the task is impossible or blocked, explain why clearly and stop \u2014 do not produce partial or guessed output.
1048
- 3. Prefer structured output: code blocks, lists, tables.
1049
- 4. Include file paths (always absolute) when referencing code.
1050
- 5. When your task is complete, end with a brief summary of what you did and any caveats.
1051
- 6. Respect the tool allowlist for the ${department} department \u2014 do not attempt to use tools you have not been granted.
1052
- 7. Do not commit, push, or deploy. Your output will be reviewed by your leader before any permanent action is taken.`);
1053
- return sections.join("\n\n");
1054
- }
1055
-
1056
- // src/agents/agent-factory.ts
1057
- var ORCHESTRATOR_SYSTEM_PROMPT = `## IDENTITY
1058
-
1059
- You are the **Orchestrator** \u2014 a proxy and router, NOT a CEO. Your job is to receive the user's request, decompose it into department-level concerns, convene meetings, and route work to the appropriate leaders. You do not make product, architecture, or engineering decisions yourself.
1060
-
1061
- ## CORE RESPONSIBILITIES
1062
-
1063
- 1. **Request analysis** \u2014 When the user submits a request, determine which departments are relevant. Most requests involve 2-4 departments.
1064
-
1065
- 2. **Meeting convening** \u2014 Create a meeting with an agenda derived from the request. Invite the relevant leaders. You chair the meeting but you do not dominate it.
1066
-
1067
- 3. **Auto-routing** \u2014 For straightforward, single-department tasks (e.g., "run the tests"), skip the full meeting flow and route directly to the responsible leader.
1068
-
1069
- 4. **@USER mentions** \u2014 When a leader emits @USER_DECISION_NEEDED during a meeting:
1070
- - Pause the meeting.
1071
- - Surface the decision to the user with full context (options, trade-offs, supporters).
1072
- - Resume the meeting once the user responds.
1073
-
1074
- 5. **Progress tracking** \u2014 Monitor worker completion events. Nudge leaders if a task is overdue or failed. Report final results back to the user.
1075
-
1076
- ## RULES
1077
-
1078
- 1. You are a facilitator. Do NOT override leader recommendations unless they conflict with an explicit user instruction.
1079
- 2. Keep your own token usage minimal \u2014 delegate analysis and execution to leaders and workers.
1080
- 3. Always preserve the user's exact wording when forwarding a request to a meeting.
1081
- 4. If the user's request is ambiguous, ask a clarifying question BEFORE convening a meeting.
1082
- 5. Never modify files, run tests, or execute code directly. All execution happens through workers spawned by leaders.
1083
- 6. When multiple departments disagree, facilitate resolution. Escalate to the user only when the team cannot converge after a full discussion round.
1084
- 7. After a meeting completes, provide the user with a concise summary: decisions made, action items, and any pending @USER items.
1085
- 8. Respect budget limits. If projected cost approaches the meeting budget, warn the user and request approval before proceeding.
1086
-
1087
- ## DEPARTMENT OVERVIEW
1088
-
1089
- You can route work to these departments:
1090
- - **architecture** \u2014 System design, schemas, API surfaces, dependency analysis.
1091
- - **engineering** \u2014 Feature implementation, bug fixes, refactoring.
1092
- - **qa** \u2014 Testing, security audits, performance testing.
1093
- - **product** \u2014 Requirements analysis, user-flow mapping, prioritisation.
1094
- - **research** \u2014 Codebase exploration, documentation search, benchmarks.
1095
-
1096
- ## OUTPUT FORMAT
1097
-
1098
- When you need to convene a meeting, output:
1099
- \`\`\`
1100
- CONVENE_MEETING:
1101
- topic: <meeting topic>
1102
- agenda:
1103
- - <item 1>
1104
- - <item 2>
1105
- departments:
1106
- - <dept 1>
1107
- - <dept 2>
1108
- \`\`\`
1109
-
1110
- When you route directly to a leader (no meeting), output:
1111
- \`\`\`
1112
- DIRECT_ROUTE:
1113
- department: <department>
1114
- task: <task description>
1115
- \`\`\`
1116
- `;
1117
- function createAgentConfig(opts) {
1118
- const { tier, role, department, task, context } = opts;
1119
- const tierCfg = getTierConfig(tier);
1120
- const allowedTools = [...DEPARTMENT_TOOLS[department] ?? []];
1121
- let systemPrompt;
1122
- switch (tier) {
1123
- case "orchestrator": {
1124
- systemPrompt = ORCHESTRATOR_SYSTEM_PROMPT;
1125
- break;
1126
- }
1127
- case "leader": {
1128
- systemPrompt = getLeaderSystemPrompt(department);
1129
- break;
1130
- }
1131
- case "worker": {
1132
- if (!task) {
1133
- throw new Error("Worker agents require a task description");
1134
- }
1135
- systemPrompt = buildWorkerPrompt({
1136
- workerType: role,
1137
- department,
1138
- task,
1139
- context
1140
- });
1141
- break;
1142
- }
1143
- default: {
1144
- const _exhaustive = tier;
1145
- throw new Error(`Unknown tier: ${_exhaustive}`);
1146
- }
1147
- }
1148
- const model = tier === "worker" && department === "research" ? "claude-haiku-4-5" : tierCfg.model;
1149
- return {
1150
- model,
1151
- maxTurns: tierCfg.maxTurns,
1152
- allowedTools
1153
- };
1154
- }
1155
-
1156
- // src/agents/claude-invoker.ts
1157
- import { spawn } from "child_process";
1158
- import { execSync } from "child_process";
1159
- var _claudeAvailable = null;
1160
- function isClaudeAvailable() {
1161
- if (_claudeAvailable !== null) return _claudeAvailable;
1162
- try {
1163
- execSync("which claude", { stdio: "ignore" });
1164
- _claudeAvailable = true;
1165
- } catch {
1166
- _claudeAvailable = false;
1167
- }
1168
- return _claudeAvailable;
1169
- }
1170
- function isMockMode() {
1171
- if (process.env["COLESLAW_MOCK"] === "1") return true;
1172
- return !isClaudeAvailable();
1173
- }
1174
- async function invokeMock(options) {
1175
- await new Promise((resolve) => setTimeout(resolve, 30 + Math.random() * 70));
1176
- const lower = options.prompt.toLowerCase();
1177
- let output;
1178
- if (lower.includes("opening") || lower.includes("initial position")) {
1179
- output = "[Mock] Acknowledged the agenda. From my department perspective, I see several important considerations. We should ensure proper separation of concerns and define clear boundaries. Key concern: we must avoid tight coupling between new and existing components.";
1180
- } else if (lower.includes("synthesis") || lower.includes("final position")) {
1181
- output = "[Mock] FINAL POSITION: I support the agreed approach. Action items for my department: (1) Deliver the agreed outputs, (2) Coordinate with dependent departments, (3) Report completion status once done.";
1182
- } else if (lower.includes("discussion") || lower.includes("perspective")) {
1183
- output = "[Mock] Building on the previous points, I propose we move forward with the discussed approach. This aligns with existing patterns and allows parallel work across departments. I can have my team start on the deliverables immediately.";
1184
- } else if (lower.includes("schema") || lower.includes("design")) {
1185
- output = "[Mock] Schema analysis complete. Proposed 3 tables with proper foreign-key relationships and indexes. No circular dependencies detected.";
1186
- } else if (lower.includes("test")) {
1187
- output = "[Mock] Test suite generated: 8 unit tests, 2 integration tests. All assertions use strict equality. Coverage target: 90%.";
1188
- } else if (lower.includes("implement") || lower.includes("build") || lower.includes("feature")) {
1189
- output = "[Mock] Implementation complete. Created 2 new files, modified 1 existing file. All changes follow project conventions.";
1190
- } else if (lower.includes("research") || lower.includes("explore")) {
1191
- output = "[Mock] Research complete. Found 5 relevant code references and 2 documentation entries. Summary provided in structured format.";
1192
- } else if (lower.includes("security") || lower.includes("audit")) {
1193
- output = "[Mock] Security audit complete. No critical vulnerabilities found. 1 advisory: ensure input validation on user-facing endpoints.";
1194
- } else if (lower.includes("fix") || lower.includes("bug")) {
1195
- output = "[Mock] Bug fix applied. Root cause identified and corrected. Fix verified with regression test.";
1196
- } else {
1197
- output = "[Mock] Task completed successfully. Output ready for review.";
1198
- }
1199
- return {
1200
- success: true,
1201
- output,
1202
- costUsd: 0
1203
- };
1204
- }
1205
- async function invokeReal(options) {
1206
- const {
1207
- prompt,
1208
- systemPrompt,
1209
- allowedTools,
1210
- maxTurns,
1211
- cwd,
1212
- timeoutMs = 3e5
1213
- } = options;
1214
- const args = [
1215
- "--print",
1216
- "--output-format",
1217
- "json",
1218
- "--no-session-persistence",
1219
- "--bare"
1220
- // CRITICAL: skip hooks, plugins, MCP — prevents infinite recursion
1221
- ];
1222
- if (systemPrompt) {
1223
- args.push("--system-prompt", systemPrompt);
1224
- }
1225
- if (allowedTools.length > 0) {
1226
- args.push("--allowedTools", allowedTools.join(","));
1227
- }
1228
- logger.info("Invoking Claude CLI", {
1229
- promptLength: prompt.length,
1230
- toolCount: allowedTools.length
1231
- });
1232
- return new Promise((resolve) => {
1233
- const child = spawn("claude", args, {
1234
- cwd: cwd ?? process.cwd(),
1235
- stdio: ["pipe", "pipe", "pipe"],
1236
- env: { ...process.env }
1237
- });
1238
- child.stdin.write(prompt);
1239
- child.stdin.end();
1240
- const stdoutChunks = [];
1241
- const stderrChunks = [];
1242
- child.stdout.on("data", (chunk) => {
1243
- stdoutChunks.push(chunk);
1244
- });
1245
- child.stderr.on("data", (chunk) => {
1246
- stderrChunks.push(chunk);
1247
- });
1248
- const timer = setTimeout(() => {
1249
- logger.warn("Claude CLI timed out, killing process", { timeoutMs });
1250
- child.kill("SIGTERM");
1251
- setTimeout(() => {
1252
- if (!child.killed) child.kill("SIGKILL");
1253
- }, 5e3);
1254
- }, timeoutMs);
1255
- child.on("close", (code) => {
1256
- clearTimeout(timer);
1257
- const rawStdout = Buffer.concat(stdoutChunks).toString("utf-8");
1258
- const rawStderr = Buffer.concat(stderrChunks).toString("utf-8");
1259
- if (code !== 0) {
1260
- logger.error("Claude CLI exited with non-zero code", {
1261
- exitCode: String(code)
1262
- });
1263
- resolve({
1264
- success: false,
1265
- output: "",
1266
- error: rawStderr || `Claude CLI exited with code ${code}`
1267
- });
1268
- return;
1269
- }
1270
- const parsed = parseCliOutput(rawStdout);
1271
- logger.info("Claude CLI invocation completed", {
1272
- outputLength: String(parsed.output.length)
1273
- });
1274
- resolve(parsed);
1275
- });
1276
- child.on("error", (err) => {
1277
- clearTimeout(timer);
1278
- logger.error("Failed to spawn Claude CLI", { error: err.message });
1279
- resolve({
1280
- success: false,
1281
- output: "",
1282
- error: `Failed to spawn Claude CLI: ${err.message}`
1283
- });
1284
- });
1285
- });
1286
- }
1287
- function parseCliOutput(raw) {
1288
- const trimmed = raw.trim();
1289
- if (!trimmed) {
1290
- return { success: false, output: "", error: "Empty output from Claude CLI" };
1291
- }
1292
- try {
1293
- const json = JSON.parse(trimmed);
1294
- if (json.is_error || json.error) {
1295
- return {
1296
- success: false,
1297
- output: json.result ?? json.output ?? "",
1298
- error: json.error ?? "Unknown CLI error",
1299
- costUsd: json.cost_usd
1300
- };
1301
- }
1302
- return {
1303
- success: true,
1304
- output: json.result ?? json.output ?? trimmed,
1305
- costUsd: json.cost_usd
1306
- };
1307
- } catch {
1308
- return {
1309
- success: true,
1310
- output: trimmed
1311
- };
1312
- }
1313
- }
1314
- async function invokeClaude(options) {
1315
- if (isMockMode()) {
1316
- if (!isClaudeAvailable()) {
1317
- logger.warn("Claude CLI not found on PATH \u2014 using mock mode");
1318
- } else {
1319
- logger.info("COLESLAW_MOCK=1 set \u2014 using mock mode");
1320
- }
1321
- return invokeMock(options);
1322
- }
1323
- return invokeReal(options);
1324
- }
1325
- function buildInvokeOptions(config, prompt, systemPrompt, cwd) {
1326
- return {
1327
- prompt,
1328
- systemPrompt,
1329
- allowedTools: config.allowedTools,
1330
- maxTurns: config.maxTurns,
1331
- cwd
1332
- };
1333
- }
1334
-
1335
- // src/orchestrator/meeting-runner.ts
1336
- async function queryAgent(config, prompt) {
1337
- const agentConfig = createAgentConfig({
1338
- tier: "leader",
1339
- role: config.role,
1340
- department: config.department
1341
- });
1342
- const invokeOpts = buildInvokeOptions(
1343
- agentConfig,
1344
- prompt,
1345
- config.systemPrompt
1346
- );
1347
- invokeOpts.timeoutMs = 6e5;
1348
- const result = await invokeClaude(invokeOpts);
1349
- if (!result.success) {
1350
- logger.warn(`Agent query failed for ${config.role}: ${result.error}`);
1351
- return `[Error from ${config.role}] ${result.error ?? "Unknown error during agent invocation"}`;
1352
- }
1353
- return result.output;
1354
- }
1355
- function insertTranscriptEntry(meetingId, speakerId, speakerRole, agendaItemIndex, roundNumber, content) {
1356
- const db = getDb();
1357
- const now = Date.now();
1358
- const tokenCount = Math.ceil(content.length / 4);
1359
- const result = db.prepare(
1360
- `INSERT INTO transcript_entries
1361
- (meeting_id, speaker_id, speaker_role, agenda_item_index, round_number, content, token_count, created_at)
1362
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
1363
- ).run(meetingId, speakerId, speakerRole, agendaItemIndex, roundNumber, content, tokenCount, now);
1364
- return {
1365
- id: Number(result.lastInsertRowid),
1366
- meetingId,
1367
- speakerId,
1368
- speakerRole,
1369
- agendaItemIndex,
1370
- roundNumber,
1371
- content,
1372
- tokenCount,
1373
- createdAt: now
1374
- };
1375
- }
1376
- function getTranscript(meetingId) {
1377
- const db = getDb();
1378
- const rows = db.prepare("SELECT * FROM transcript_entries WHERE meeting_id = ? ORDER BY created_at ASC").all(meetingId);
1379
- return rows.map((r) => ({
1380
- id: r.id,
1381
- meetingId: r.meeting_id,
1382
- speakerId: r.speaker_id,
1383
- speakerRole: r.speaker_role,
1384
- agendaItemIndex: r.agenda_item_index,
1385
- roundNumber: r.round_number,
1386
- content: r.content,
1387
- tokenCount: r.token_count,
1388
- createdAt: r.created_at
1389
- }));
1390
- }
1391
- var MeetingRunner = class {
1392
- meetingId;
1393
- leaders;
1394
- maxRoundsPerItem;
1395
- projectContext;
1396
- constructor(meetingId, leaders, projectContext) {
1397
- this.meetingId = meetingId;
1398
- this.leaders = leaders;
1399
- this.maxRoundsPerItem = DEFAULT_MEETING_CONFIG.maxRoundsPerItem;
1400
- this.projectContext = projectContext;
1401
- }
1402
- // ---- public entry point -------------------------------------------------
1403
- /**
1404
- * Run the complete meeting lifecycle: opening -> discussion -> synthesis -> minutes.
1405
- */
1406
- async run() {
1407
- const meeting = getMeeting(this.meetingId);
1408
- if (!meeting) {
1409
- throw new Error(`Meeting not found: ${this.meetingId}`);
1410
- }
1411
- logger.info(`Starting meeting: ${meeting.topic}`, { meetingId: this.meetingId });
1412
- try {
1413
- await this.openingPhase();
1414
- await this.discussionPhase();
1415
- await this.synthesisPhase();
1416
- await this.generateMinutes();
1417
- updateMeeting(this.meetingId, {
1418
- status: "completed",
1419
- completedAt: Date.now()
1420
- });
1421
- logger.info(`Meeting completed: ${meeting.topic}`, { meetingId: this.meetingId });
1422
- } catch (err) {
1423
- const errorMsg = err instanceof Error ? err.message : String(err);
1424
- logger.error(`Meeting failed: ${errorMsg}`, { meetingId: this.meetingId });
1425
- updateMeeting(this.meetingId, {
1426
- status: "failed",
1427
- completedAt: Date.now()
1428
- });
1429
- throw err;
1430
- }
1431
- }
1432
- // ---- phases -------------------------------------------------------------
1433
- /**
1434
- * Opening phase: each leader states their initial position on the meeting
1435
- * topic and agenda.
1436
- */
1437
- async openingPhase() {
1438
- this.setPhase("opening");
1439
- const meeting = getMeeting(this.meetingId);
1440
- const agendaText = meeting.agenda.map((a, i) => ` ${i + 1}. ${a}`).join("\n");
1441
- for (const leader of this.leaders) {
1442
- const prompt = `MEETING OPENING
1443
-
1444
- Topic: ${meeting.topic}
1445
- Agenda:
1446
- ${agendaText}
1447
-
1448
- Please state your initial position on this topic from your department's perspective. Identify any concerns, dependencies, or risks relevant to your area.`;
1449
- const config = {
1450
- role: leader.role,
1451
- department: leader.department,
1452
- systemPrompt: getLeaderSystemPrompt(leader.department, void 0, this.projectContext)
1453
- };
1454
- const response = await queryAgent(config, prompt);
1455
- insertTranscriptEntry(
1456
- this.meetingId,
1457
- leader.id,
1458
- leader.role,
1459
- -1,
1460
- // -1 signals the opening phase (not tied to a specific agenda item)
1461
- 0,
1462
- response
1463
- );
1464
- eventBus.emitAgentEvent({
1465
- kind: "message_sent",
1466
- fromId: leader.id,
1467
- toId: "meeting",
1468
- summary: `[Opening] ${leader.role}: ${response.slice(0, 80)}...`
1469
- });
1470
- logger.debug(`Opening statement from ${leader.role}`, {
1471
- meetingId: this.meetingId,
1472
- agentId: leader.id
1473
- });
1474
- }
1475
- }
1476
- /**
1477
- * Discussion phase: for each agenda item, leaders take turns responding in
1478
- * round-robin fashion for up to `maxRoundsPerItem` rounds.
1479
- */
1480
- async discussionPhase() {
1481
- this.setPhase("discussion");
1482
- const meeting = getMeeting(this.meetingId);
1483
- for (let itemIdx = 0; itemIdx < meeting.agenda.length; itemIdx++) {
1484
- const agendaItem = meeting.agenda[itemIdx];
1485
- logger.info(`Discussing agenda item ${itemIdx + 1}: ${agendaItem}`, {
1486
- meetingId: this.meetingId
1487
- });
1488
- for (let round = 1; round <= this.maxRoundsPerItem; round++) {
1489
- for (const leader of this.leaders) {
1490
- const transcript = getTranscript(this.meetingId);
1491
- const transcriptText = this.formatTranscript(transcript);
1492
- const prompt = `MEETING DISCUSSION \u2014 Round ${round}/${this.maxRoundsPerItem}
1493
-
1494
- Current agenda item (${itemIdx + 1}/${meeting.agenda.length}): ${agendaItem}
1495
-
1496
- Transcript so far:
1497
- ${transcriptText}
1498
-
1499
- Provide your department's perspective on this agenda item. Build on what others have said. If you agree, say so and add specifics. If you disagree, state your reasoning and propose an alternative.`;
1500
- const config = {
1501
- role: leader.role,
1502
- department: leader.department,
1503
- systemPrompt: getLeaderSystemPrompt(leader.department, void 0, this.projectContext)
1504
- };
1505
- const response = await queryAgent(config, prompt);
1506
- insertTranscriptEntry(
1507
- this.meetingId,
1508
- leader.id,
1509
- leader.role,
1510
- itemIdx,
1511
- round,
1512
- response
1513
- );
1514
- eventBus.emitAgentEvent({
1515
- kind: "message_sent",
1516
- fromId: leader.id,
1517
- toId: "meeting",
1518
- summary: `[Item ${itemIdx + 1}, R${round}] ${leader.role}: ${response.slice(0, 80)}...`
1519
- });
1520
- }
1521
- }
1522
- }
1523
- }
1524
- /**
1525
- * Synthesis phase: each leader states their final position, commitments,
1526
- * and action items.
1527
- */
1528
- async synthesisPhase() {
1529
- this.setPhase("synthesis");
1530
- const transcript = getTranscript(this.meetingId);
1531
- const transcriptText = this.formatTranscript(transcript);
1532
- for (const leader of this.leaders) {
1533
- const prompt = `MEETING SYNTHESIS
1534
-
1535
- The discussion is complete. Here is the full transcript:
1536
- ${transcriptText}
1537
-
1538
- State your final position. List the action items your department commits to. Flag any unresolved concerns or items requiring user decision.`;
1539
- const config = {
1540
- role: leader.role,
1541
- department: leader.department,
1542
- systemPrompt: getLeaderSystemPrompt(leader.department, void 0, this.projectContext)
1543
- };
1544
- const response = await queryAgent(config, prompt);
1545
- insertTranscriptEntry(
1546
- this.meetingId,
1547
- leader.id,
1548
- leader.role,
1549
- -2,
1550
- // -2 signals synthesis phase
1551
- 0,
1552
- response
1553
- );
1554
- eventBus.emitAgentEvent({
1555
- kind: "message_sent",
1556
- fromId: leader.id,
1557
- toId: "meeting",
1558
- summary: `[Synthesis] ${leader.role}: ${response.slice(0, 80)}...`
1559
- });
1560
- }
1561
- }
1562
- /**
1563
- * Generate meeting minutes by concatenating and formatting the transcript.
1564
- *
1565
- * In the future this will use a dedicated minutes-writer agent. For now it
1566
- * formats the transcript into a structured summary.
1567
- */
1568
- async generateMinutes() {
1569
- this.setPhase("minutes-generation");
1570
- const meeting = getMeeting(this.meetingId);
1571
- const transcript = getTranscript(this.meetingId);
1572
- const sections = [];
1573
- sections.push(`# Meeting Minutes`);
1574
- sections.push(`## Topic: ${meeting.topic}`);
1575
- sections.push(`## Date: ${(/* @__PURE__ */ new Date()).toISOString()}`);
1576
- sections.push(`## Participants: ${this.leaders.map((l) => l.role).join(", ")}`);
1577
- sections.push("");
1578
- sections.push(`## Agenda`);
1579
- meeting.agenda.forEach((item, i) => {
1580
- sections.push(`${i + 1}. ${item}`);
1581
- });
1582
- sections.push("");
1583
- const openingEntries = transcript.filter((e) => e.agendaItemIndex === -1);
1584
- if (openingEntries.length > 0) {
1585
- sections.push(`## Opening Statements`);
1586
- for (const entry of openingEntries) {
1587
- sections.push(`### ${entry.speakerRole}`);
1588
- sections.push(entry.content);
1589
- sections.push("");
1590
- }
1591
- }
1592
- for (let i = 0; i < meeting.agenda.length; i++) {
1593
- const itemEntries = transcript.filter((e) => e.agendaItemIndex === i);
1594
- if (itemEntries.length > 0) {
1595
- sections.push(`## Discussion: ${meeting.agenda[i]}`);
1596
- for (const entry of itemEntries) {
1597
- sections.push(`**${entry.speakerRole}** (round ${entry.roundNumber}):`);
1598
- sections.push(entry.content);
1599
- sections.push("");
1600
- }
1601
- }
1602
- }
1603
- const synthesisEntries = transcript.filter((e) => e.agendaItemIndex === -2);
1604
- if (synthesisEntries.length > 0) {
1605
- sections.push(`## Final Positions`);
1606
- for (const entry of synthesisEntries) {
1607
- sections.push(`### ${entry.speakerRole}`);
1608
- sections.push(entry.content);
1609
- sections.push("");
1610
- }
1611
- }
1612
- const content = sections.join("\n");
1613
- const actionItems = this.leaders.map((leader, idx) => ({
1614
- id: `action-${this.meetingId}-${idx}`,
1615
- title: `${leader.role} deliverables`,
1616
- description: `Action items committed by ${leader.role} during synthesis phase`,
1617
- assignedDepartment: leader.department,
1618
- assignedRole: leader.role,
1619
- priority: "medium",
1620
- dependencies: [],
1621
- acceptanceCriteria: ["Deliverables completed as stated in final position"]
1622
- }));
1623
- createMinutes({
1624
- meetingId: this.meetingId,
1625
- format: "summary",
1626
- content,
1627
- actionItems
1628
- });
1629
- logger.info("Minutes generated", { meetingId: this.meetingId });
1630
- }
1631
- // ---- helpers ------------------------------------------------------------
1632
- setPhase(phase) {
1633
- const statusMap = {
1634
- "orchestrator-phase": "pending",
1635
- "convening": "convening",
1636
- "opening": "opening",
1637
- "discussion": "discussion",
1638
- "research-break": "discussion",
1639
- "synthesis": "synthesis",
1640
- "minutes-generation": "minutes-generation"
1641
- };
1642
- updateMeeting(this.meetingId, {
1643
- phase,
1644
- status: statusMap[phase] ?? "discussion"
1645
- });
1646
- logger.debug(`Meeting phase: ${phase}`, { meetingId: this.meetingId });
1647
- }
1648
- formatTranscript(entries) {
1649
- if (entries.length === 0) return "(No transcript entries yet)";
1650
- return entries.map((e) => {
1651
- let phaseLabel;
1652
- if (e.agendaItemIndex === -1) phaseLabel = "Opening";
1653
- else if (e.agendaItemIndex === -2) phaseLabel = "Synthesis";
1654
- else phaseLabel = `Item ${e.agendaItemIndex + 1}, Round ${e.roundNumber}`;
1655
- return `[${phaseLabel}] ${e.speakerRole}: ${e.content}`;
1656
- }).join("\n\n");
1657
- }
1658
- };
1659
-
1660
- // src/agents/project-analyzer.ts
1661
- import { existsSync, readFileSync, readdirSync, statSync } from "fs";
1662
- import { join, basename, extname } from "path";
1663
- var CONFIG_FILE_PATTERNS = [
1664
- "tsconfig.json",
1665
- "tsconfig.build.json",
1666
- "tsconfig.node.json",
1667
- ".eslintrc",
1668
- ".eslintrc.js",
1669
- ".eslintrc.cjs",
1670
- ".eslintrc.json",
1671
- ".eslintrc.yml",
1672
- ".eslintrc.yaml",
1673
- "eslint.config.js",
1674
- "eslint.config.mjs",
1675
- "eslint.config.cjs",
1676
- "eslint.config.ts",
1677
- ".prettierrc",
1678
- ".prettierrc.js",
1679
- ".prettierrc.cjs",
1680
- ".prettierrc.json",
1681
- ".prettierrc.yml",
1682
- ".prettierrc.yaml",
1683
- "prettier.config.js",
1684
- "prettier.config.mjs",
1685
- "prettier.config.cjs",
1686
- "biome.json",
1687
- "biome.jsonc",
1688
- "vite.config.ts",
1689
- "vite.config.js",
1690
- "vite.config.mjs",
1691
- "webpack.config.js",
1692
- "webpack.config.ts",
1693
- "webpack.config.mjs",
1694
- "rollup.config.js",
1695
- "rollup.config.ts",
1696
- "rollup.config.mjs",
1697
- "tsup.config.ts",
1698
- "tsup.config.js",
1699
- "esbuild.config.js",
1700
- "esbuild.config.ts",
1701
- "jest.config.js",
1702
- "jest.config.ts",
1703
- "jest.config.mjs",
1704
- "vitest.config.ts",
1705
- "vitest.config.js",
1706
- "vitest.config.mts",
1707
- ".mocharc.yml",
1708
- ".mocharc.json",
1709
- ".mocharc.js",
1710
- ".babelrc",
1711
- "babel.config.js",
1712
- "babel.config.json",
1713
- ".swcrc",
1714
- "turbo.json",
1715
- "nx.json"
1716
- ];
1717
- var UTIL_DIR_NAMES = ["utils", "lib", "helpers", "shared", "common"];
1718
- var MANIFEST_FILES = [
1719
- // -- JavaScript / TypeScript --
1720
- {
1721
- file: "package.json",
1722
- language: "typescript",
1723
- // refined later if no TS config
1724
- parse: (content) => {
1725
- const raw = JSON.parse(content);
1726
- return {
1727
- dependencies: raw.dependencies ?? {},
1728
- devDependencies: raw.devDependencies ?? {},
1729
- scripts: raw.scripts ?? {},
1730
- metadata: { type: raw.type ?? "commonjs", name: raw.name ?? "" }
1731
- };
1732
- }
1733
- },
1734
- // -- Python --
1735
- {
1736
- file: "pyproject.toml",
1737
- language: "python",
1738
- parse: (content) => {
1739
- const deps = {};
1740
- const devDeps = {};
1741
- const scripts = {};
1742
- for (const m of content.matchAll(/^\s*"([^"]+?)(?:[><=!~]+.*)?".*$/gm)) {
1743
- deps[m[1]] = "*";
1744
- }
1745
- for (const m of content.matchAll(/^(\w[\w-]*)\s*=\s*"([^"]+)"/gm)) {
1746
- scripts[m[1]] = m[2];
1747
- }
1748
- return { dependencies: deps, devDependencies: devDeps, scripts, metadata: {} };
1749
- }
1750
- },
1751
- {
1752
- file: "requirements.txt",
1753
- language: "python",
1754
- parse: (content) => {
1755
- const deps = {};
1756
- for (const line of content.split("\n")) {
1757
- const trimmed = line.trim();
1758
- if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("-")) continue;
1759
- const match = trimmed.match(/^([a-zA-Z0-9_-]+)\s*([><=!~].*)?$/);
1760
- if (match) deps[match[1]] = match[2]?.trim() ?? "*";
1761
- }
1762
- return { dependencies: deps, devDependencies: {}, scripts: {}, metadata: {} };
1763
- }
1764
- },
1765
- {
1766
- file: "Pipfile",
1767
- language: "python",
1768
- parse: (content) => {
1769
- const deps = {};
1770
- const inPackages = content.indexOf("[packages]");
1771
- if (inPackages >= 0) {
1772
- const section = content.slice(inPackages);
1773
- for (const m of section.matchAll(/^(\w[\w-]*)\s*=\s*"([^"]+)"/gm)) {
1774
- deps[m[1]] = m[2];
1775
- }
1776
- }
1777
- return { dependencies: deps, devDependencies: {}, scripts: {}, metadata: {} };
1778
- }
1779
- },
1780
- // -- Java / Kotlin (Gradle) --
1781
- {
1782
- file: "build.gradle",
1783
- language: "java",
1784
- parse: (content) => {
1785
- const deps = {};
1786
- for (const m of content.matchAll(/(?:implementation|api|compileOnly)\s+['"]([^'"]+)['"]/g)) {
1787
- const parts = m[1].split(":");
1788
- if (parts.length >= 2) deps[`${parts[0]}:${parts[1]}`] = parts[2] ?? "*";
1789
- }
1790
- return { dependencies: deps, devDependencies: {}, scripts: {}, metadata: { buildSystem: "gradle" } };
1791
- }
1792
- },
1793
- {
1794
- file: "build.gradle.kts",
1795
- language: "kotlin",
1796
- parse: (content) => {
1797
- const deps = {};
1798
- for (const m of content.matchAll(/(?:implementation|api|compileOnly)\s*\(\s*"([^"]+)"\s*\)/g)) {
1799
- const parts = m[1].split(":");
1800
- if (parts.length >= 2) deps[`${parts[0]}:${parts[1]}`] = parts[2] ?? "*";
1801
- }
1802
- return { dependencies: deps, devDependencies: {}, scripts: {}, metadata: { buildSystem: "gradle-kts" } };
1803
- }
1804
- },
1805
- {
1806
- file: "pom.xml",
1807
- language: "java",
1808
- parse: (content) => {
1809
- const deps = {};
1810
- for (const m of content.matchAll(/<dependency>\s*<groupId>([^<]+)<\/groupId>\s*<artifactId>([^<]+)<\/artifactId>(?:\s*<version>([^<]+)<\/version>)?/gs)) {
1811
- deps[`${m[1]}:${m[2]}`] = m[3] ?? "*";
1812
- }
1813
- return { dependencies: deps, devDependencies: {}, scripts: {}, metadata: { buildSystem: "maven" } };
1814
- }
1815
- },
1816
- // -- Go --
1817
- {
1818
- file: "go.mod",
1819
- language: "go",
1820
- parse: (content) => {
1821
- const deps = {};
1822
- for (const m of content.matchAll(/^\s+(\S+)\s+(v[\d.]+\S*)/gm)) {
1823
- deps[m[1]] = m[2];
1824
- }
1825
- const moduleMatch = content.match(/^module\s+(\S+)/m);
1826
- return { dependencies: deps, devDependencies: {}, scripts: {}, metadata: { module: moduleMatch?.[1] ?? "" } };
1827
- }
1828
- },
1829
- // -- Rust --
1830
- {
1831
- file: "Cargo.toml",
1832
- language: "rust",
1833
- parse: (content) => {
1834
- const deps = {};
1835
- const devDeps = {};
1836
- let inDeps = false;
1837
- let inDevDeps = false;
1838
- for (const line of content.split("\n")) {
1839
- if (line.match(/^\[dependencies\]/)) {
1840
- inDeps = true;
1841
- inDevDeps = false;
1842
- continue;
1843
- }
1844
- if (line.match(/^\[dev-dependencies\]/)) {
1845
- inDevDeps = true;
1846
- inDeps = false;
1847
- continue;
1848
- }
1849
- if (line.match(/^\[/)) {
1850
- inDeps = false;
1851
- inDevDeps = false;
1852
- continue;
1853
- }
1854
- const m = line.match(/^(\w[\w-]*)\s*=\s*"([^"]+)"/);
1855
- if (m) {
1856
- if (inDeps) deps[m[1]] = m[2];
1857
- if (inDevDeps) devDeps[m[1]] = m[2];
1858
- }
1859
- }
1860
- return { dependencies: deps, devDependencies: devDeps, scripts: {}, metadata: {} };
1861
- }
1862
- },
1863
- // -- Swift --
1864
- {
1865
- file: "Package.swift",
1866
- language: "swift",
1867
- parse: (content) => {
1868
- const deps = {};
1869
- for (const m of content.matchAll(/\.package\s*\(\s*url:\s*"([^"]+)"/g)) {
1870
- const name = m[1].split("/").pop()?.replace(".git", "") ?? m[1];
1871
- deps[name] = "*";
1872
- }
1873
- return { dependencies: deps, devDependencies: {}, scripts: {}, metadata: {} };
1874
- }
1875
- },
1876
- // -- Dart / Flutter --
1877
- {
1878
- file: "pubspec.yaml",
1879
- language: "dart",
1880
- parse: (content) => {
1881
- const deps = {};
1882
- let inDeps = false;
1883
- for (const line of content.split("\n")) {
1884
- if (line.match(/^dependencies:/)) {
1885
- inDeps = true;
1886
- continue;
1887
- }
1888
- if (line.match(/^\S/) && inDeps) {
1889
- inDeps = false;
1890
- continue;
1891
- }
1892
- if (inDeps) {
1893
- const m = line.match(/^\s+(\w[\w_-]*):\s*(.+)?/);
1894
- if (m) deps[m[1]] = m[2]?.trim() ?? "*";
1895
- }
1896
- }
1897
- return { dependencies: deps, devDependencies: {}, scripts: {}, metadata: {} };
1898
- }
1899
- },
1900
- // -- Ruby --
1901
- {
1902
- file: "Gemfile",
1903
- language: "ruby",
1904
- parse: (content) => {
1905
- const deps = {};
1906
- for (const m of content.matchAll(/gem\s+['"]([^'"]+)['"]/g)) {
1907
- deps[m[1]] = "*";
1908
- }
1909
- return { dependencies: deps, devDependencies: {}, scripts: {}, metadata: {} };
1910
- }
1911
- },
1912
- // -- C# / .NET --
1913
- {
1914
- file: "*.csproj",
1915
- // handled specially in the scanner
1916
- language: "csharp",
1917
- parse: (content) => {
1918
- const deps = {};
1919
- for (const m of content.matchAll(/<PackageReference\s+Include="([^"]+)"\s+Version="([^"]+)"/g)) {
1920
- deps[m[1]] = m[2];
1921
- }
1922
- return { dependencies: deps, devDependencies: {}, scripts: {}, metadata: { buildSystem: "dotnet" } };
1923
- }
1924
- }
1925
- ];
1926
- function detectLanguage(projectDir, manifests) {
1927
- if (manifests.length === 0) return "unknown";
1928
- if (existsSync(join(projectDir, "tsconfig.json"))) return "typescript";
1929
- const languages = manifests.map((m) => m.language);
1930
- if (languages.includes("typescript")) return "typescript";
1931
- if (languages.includes("kotlin")) return "kotlin";
1932
- return languages[0];
1933
- }
1934
- function scanManifests(projectDir) {
1935
- const results = [];
1936
- for (const spec of MANIFEST_FILES) {
1937
- if (spec.file.includes("*")) {
1938
- const ext = spec.file.replace("*", "");
1939
- try {
1940
- const entries = readdirSync(projectDir);
1941
- for (const entry of entries) {
1942
- if (entry.endsWith(ext)) {
1943
- const content = readFileSync(join(projectDir, entry), "utf-8");
1944
- try {
1945
- const parsed = spec.parse(content, projectDir);
1946
- results.push({ file: entry, language: spec.language, ...parsed });
1947
- } catch {
1948
- }
1949
- }
1950
- }
1951
- } catch {
1952
- }
1953
- continue;
1954
- }
1955
- const filePath = join(projectDir, spec.file);
1956
- if (!existsSync(filePath)) continue;
1957
- try {
1958
- const content = readFileSync(filePath, "utf-8");
1959
- const parsed = spec.parse(content, projectDir);
1960
- results.push({ file: spec.file, language: spec.language, ...parsed });
1961
- } catch {
1962
- logger.debug(`Failed to parse manifest: ${spec.file}`);
1963
- }
1964
- }
1965
- return results;
1966
- }
1967
- function readJsonSafe(filePath) {
1968
- try {
1969
- const raw = readFileSync(filePath, "utf-8");
1970
- return JSON.parse(raw);
1971
- } catch {
1972
- return null;
1973
- }
1974
- }
1975
- function extractExports(filePath) {
1976
- try {
1977
- const content = readFileSync(filePath, "utf-8");
1978
- const exports = [];
1979
- for (const m of content.matchAll(/export\s+(?:async\s+)?function\s+(\w+)/g)) {
1980
- exports.push(m[1]);
1981
- }
1982
- for (const m of content.matchAll(/export\s+class\s+(\w+)/g)) {
1983
- exports.push(m[1]);
1984
- }
1985
- for (const m of content.matchAll(/export\s+(?:const|let|var)\s+(\w+)/g)) {
1986
- exports.push(m[1]);
1987
- }
1988
- for (const m of content.matchAll(/export\s+(?:interface|type)\s+(\w+)/g)) {
1989
- exports.push(m[1]);
1990
- }
1991
- for (const m of content.matchAll(/export\s+enum\s+(\w+)/g)) {
1992
- exports.push(m[1]);
1993
- }
1994
- if (/export\s+default\s/.test(content)) {
1995
- exports.push("default");
1996
- }
1997
- return [...new Set(exports)];
1998
- } catch {
1999
- return [];
2000
- }
2001
- }
2002
- function sampleSourceFiles(dir, max) {
2003
- const results = [];
2004
- const extensions = /* @__PURE__ */ new Set([".ts", ".js", ".mts", ".mjs", ".cts", ".cjs"]);
2005
- function walk(currentDir, depth) {
2006
- if (depth > 2 || results.length >= max) return;
2007
- let entries;
2008
- try {
2009
- entries = readdirSync(currentDir);
2010
- } catch {
2011
- return;
2012
- }
2013
- for (const entry of entries) {
2014
- if (results.length >= max) return;
2015
- if (entry.startsWith(".") || entry === "node_modules" || entry === "dist") continue;
2016
- const full = join(currentDir, entry);
2017
- try {
2018
- const stat = statSync(full);
2019
- if (stat.isDirectory()) {
2020
- walk(full, depth + 1);
2021
- } else if (stat.isFile() && extensions.has(extname(entry))) {
2022
- results.push(full);
2023
- }
2024
- } catch {
2025
- }
2026
- }
2027
- }
2028
- walk(dir, 0);
2029
- return results;
2030
- }
2031
- function detectImportStyle(projectDir, packageType) {
2032
- const sourceDir = existsSync(join(projectDir, "src")) ? join(projectDir, "src") : projectDir;
2033
- const files = sampleSourceFiles(sourceDir, 10);
2034
- let esmCount = 0;
2035
- let cjsCount = 0;
2036
- for (const file of files) {
2037
- try {
2038
- const content = readFileSync(file, "utf-8");
2039
- if (/\bimport\s+/.test(content) || /\bexport\s+/.test(content)) {
2040
- esmCount++;
2041
- }
2042
- if (/\brequire\s*\(/.test(content) || /\bmodule\.exports\b/.test(content)) {
2043
- cjsCount++;
2044
- }
2045
- } catch {
2046
- }
2047
- }
2048
- if (esmCount === 0 && cjsCount === 0) {
2049
- return packageType === "module" ? "esm" : "commonjs";
2050
- }
2051
- if (esmCount > 0 && cjsCount > 0) return "mixed";
2052
- if (esmCount > 0) return "esm";
2053
- return "commonjs";
2054
- }
2055
- function detectTestFramework(deps) {
2056
- const candidates = [
2057
- ["vitest", "vitest"],
2058
- ["jest", "jest"],
2059
- ["@jest/core", "jest"],
2060
- ["mocha", "mocha"],
2061
- ["ava", "ava"],
2062
- ["tap", "tap"],
2063
- ["uvu", "uvu"]
2064
- ];
2065
- for (const [pkg, name] of candidates) {
2066
- if (pkg in deps) return name;
2067
- }
2068
- return null;
2069
- }
2070
- function detectLinter(deps, configFiles) {
2071
- if ("biome" in deps || "@biomejs/biome" in deps || configFiles.some((f) => f.startsWith("biome."))) {
2072
- return "biome";
2073
- }
2074
- if ("eslint" in deps || configFiles.some((f) => f.includes("eslint"))) {
2075
- return "eslint";
2076
- }
2077
- return null;
2078
- }
2079
- function detectFormatter(deps, configFiles) {
2080
- if ("prettier" in deps || configFiles.some((f) => f.includes("prettier"))) {
2081
- return "prettier";
2082
- }
2083
- if ("biome" in deps || "@biomejs/biome" in deps || configFiles.some((f) => f.startsWith("biome."))) {
2084
- return "biome";
2085
- }
2086
- return null;
2087
- }
2088
- function detectBuildTool(deps, scripts) {
2089
- const candidates = [
2090
- ["tsup", "tsup"],
2091
- ["esbuild", "esbuild"],
2092
- ["vite", "vite"],
2093
- ["webpack", "webpack"],
2094
- ["rollup", "rollup"],
2095
- ["@swc/core", "swc"],
2096
- ["turbopack", "turbopack"],
2097
- ["parcel", "parcel"]
2098
- ];
2099
- for (const [pkg, name] of candidates) {
2100
- if (pkg in deps) return name;
2101
- }
2102
- const buildScript = scripts["build"] ?? "";
2103
- for (const [, name] of candidates) {
2104
- if (buildScript.includes(name)) return name;
2105
- }
2106
- if (buildScript.includes("tsc")) return "tsc";
2107
- return null;
2108
- }
2109
- function detectEntryPoints(projectDir, packageJson) {
2110
- const entries = [];
2111
- if (packageJson) {
2112
- const raw = readJsonSafe(join(projectDir, "package.json"));
2113
- if (raw) {
2114
- if (typeof raw["main"] === "string") entries.push(raw["main"]);
2115
- if (typeof raw["module"] === "string") entries.push(raw["module"]);
2116
- if (raw["exports"] && typeof raw["exports"] === "object") {
2117
- const exp = raw["exports"];
2118
- const dot = exp["."];
2119
- if (typeof dot === "string") {
2120
- entries.push(dot);
2121
- } else if (dot && typeof dot === "object") {
2122
- const dotObj = dot;
2123
- if (typeof dotObj["import"] === "string") entries.push(dotObj["import"]);
2124
- if (typeof dotObj["require"] === "string") entries.push(dotObj["require"]);
2125
- }
2126
- }
2127
- }
2128
- }
2129
- const commonEntries = ["src/index.ts", "src/main.ts", "src/index.js", "src/main.js", "index.ts", "index.js"];
2130
- for (const candidate of commonEntries) {
2131
- if (existsSync(join(projectDir, candidate)) && !entries.includes(candidate)) {
2132
- entries.push(candidate);
2133
- }
2134
- }
2135
- return [...new Set(entries)];
2136
- }
2137
- function collectUtilFiles(projectDir) {
2138
- const results = [];
2139
- const srcDir = join(projectDir, "src");
2140
- const extensions = /* @__PURE__ */ new Set([".ts", ".js", ".mts", ".mjs"]);
2141
- for (const dirName of UTIL_DIR_NAMES) {
2142
- const utilDir = join(srcDir, dirName);
2143
- if (!existsSync(utilDir)) continue;
2144
- let entries;
2145
- try {
2146
- entries = readdirSync(utilDir);
2147
- } catch {
2148
- continue;
2149
- }
2150
- for (const entry of entries) {
2151
- const ext = extname(entry);
2152
- if (!extensions.has(ext)) continue;
2153
- if (basename(entry, ext) === "index") continue;
2154
- const filePath = join(utilDir, entry);
2155
- try {
2156
- if (!statSync(filePath).isFile()) continue;
2157
- } catch {
2158
- continue;
2159
- }
2160
- const exports = extractExports(filePath);
2161
- if (exports.length > 0) {
2162
- const relPath = filePath.slice(projectDir.length + 1);
2163
- results.push({ file: relPath, exports });
2164
- }
2165
- }
2166
- }
2167
- return results;
2168
- }
2169
- async function analyzeProject(projectDir) {
2170
- logger.debug(`Analyzing project: ${projectDir}`);
2171
- let packageJson = null;
2172
- const pkgPath = join(projectDir, "package.json");
2173
- if (existsSync(pkgPath)) {
2174
- const raw = readJsonSafe(pkgPath);
2175
- if (raw) {
2176
- packageJson = {
2177
- name: raw["name"] ?? "",
2178
- dependencies: raw["dependencies"] ?? {},
2179
- devDependencies: raw["devDependencies"] ?? {},
2180
- scripts: raw["scripts"] ?? {},
2181
- type: raw["type"]
2182
- };
2183
- }
2184
- }
2185
- const configFiles = [];
2186
- for (const pattern of CONFIG_FILE_PATTERNS) {
2187
- if (existsSync(join(projectDir, pattern))) {
2188
- configFiles.push(pattern);
2189
- }
2190
- }
2191
- const hasTypescript = existsSync(join(projectDir, "tsconfig.json")) || configFiles.some((f) => f.startsWith("tsconfig"));
2192
- const hasSrcDir = existsSync(join(projectDir, "src"));
2193
- const hasTestDir = existsSync(join(projectDir, "test")) || existsSync(join(projectDir, "tests")) || existsSync(join(projectDir, "__tests__"));
2194
- const entryPoints = detectEntryPoints(projectDir, packageJson);
2195
- const structure = {
2196
- hasTypescript,
2197
- hasSrcDir,
2198
- hasTestDir,
2199
- configFiles,
2200
- entryPoints
2201
- };
2202
- const allDeps = {
2203
- ...packageJson?.dependencies ?? {},
2204
- ...packageJson?.devDependencies ?? {}
2205
- };
2206
- const patterns = {
2207
- importStyle: detectImportStyle(projectDir, packageJson?.type),
2208
- testFramework: detectTestFramework(allDeps),
2209
- linter: detectLinter(allDeps, configFiles),
2210
- formatter: detectFormatter(allDeps, configFiles),
2211
- buildTool: detectBuildTool(allDeps, packageJson?.scripts ?? {})
2212
- };
2213
- const existingUtils = collectUtilFiles(projectDir);
2214
- const manifests = scanManifests(projectDir);
2215
- const language = detectLanguage(projectDir, manifests);
2216
- logger.debug(`Project analysis complete: ${packageJson?.name ?? "(unnamed)"}, language: ${language}, manifests: ${manifests.length}`, {
2217
- department: "research"
2218
- });
2219
- return {
2220
- language,
2221
- manifests,
2222
- packageJson,
2223
- structure,
2224
- patterns,
2225
- existingUtils
2226
- };
2227
- }
2228
- function formatProjectContext(analysis) {
2229
- const sections = [];
2230
- sections.push("## Project Context");
2231
- sections.push("");
2232
- sections.push(`**Primary Language:** ${analysis.language}`);
2233
- sections.push("");
2234
- const nonJsManifests = analysis.manifests.filter((m) => m.file !== "package.json");
2235
- if (nonJsManifests.length > 0) {
2236
- sections.push("### Dependency Manifests");
2237
- for (const manifest of nonJsManifests) {
2238
- sections.push(`#### ${manifest.file} (${manifest.language})`);
2239
- const depNames = Object.keys(manifest.dependencies);
2240
- if (depNames.length > 0) {
2241
- sections.push(depNames.slice(0, 30).map((d) => `- \`${d}\`: ${manifest.dependencies[d]}`).join("\n"));
2242
- if (depNames.length > 30) sections.push(`- ... and ${depNames.length - 30} more`);
2243
- }
2244
- if (Object.keys(manifest.metadata).length > 0) {
2245
- sections.push(`- Metadata: ${JSON.stringify(manifest.metadata)}`);
2246
- }
2247
- sections.push("");
2248
- }
2249
- }
2250
- if (analysis.packageJson) {
2251
- const pkg = analysis.packageJson;
2252
- sections.push(`**Project:** ${pkg.name || "(unnamed)"}`);
2253
- sections.push(`**Module system:** ${pkg.type ?? "commonjs (default)"}`);
2254
- sections.push("");
2255
- const depNames = Object.keys(pkg.dependencies);
2256
- if (depNames.length > 0) {
2257
- sections.push("### Dependencies");
2258
- sections.push(depNames.map((d) => `- \`${d}\`: ${pkg.dependencies[d]}`).join("\n"));
2259
- sections.push("");
2260
- }
2261
- const devDepNames = Object.keys(pkg.devDependencies);
2262
- if (devDepNames.length > 0) {
2263
- sections.push("### Dev Dependencies");
2264
- sections.push(devDepNames.map((d) => `- \`${d}\`: ${pkg.devDependencies[d]}`).join("\n"));
2265
- sections.push("");
2266
- }
2267
- const scriptEntries = Object.entries(pkg.scripts);
2268
- if (scriptEntries.length > 0) {
2269
- sections.push("### Scripts");
2270
- sections.push(scriptEntries.map(([k, v]) => `- \`${k}\`: \`${v}\``).join("\n"));
2271
- sections.push("");
2272
- }
2273
- } else {
2274
- sections.push("*No package.json found.*");
2275
- sections.push("");
2276
- }
2277
- sections.push("### Project Structure");
2278
- sections.push(`- TypeScript: ${analysis.structure.hasTypescript ? "yes" : "no"}`);
2279
- sections.push(`- src/ directory: ${analysis.structure.hasSrcDir ? "yes" : "no"}`);
2280
- sections.push(`- Test directory: ${analysis.structure.hasTestDir ? "yes" : "no"}`);
2281
- if (analysis.structure.entryPoints.length > 0) {
2282
- sections.push(`- Entry points: ${analysis.structure.entryPoints.map((e) => `\`${e}\``).join(", ")}`);
2283
- }
2284
- if (analysis.structure.configFiles.length > 0) {
2285
- sections.push(`- Config files: ${analysis.structure.configFiles.map((f) => `\`${f}\``).join(", ")}`);
2286
- }
2287
- sections.push("");
2288
- sections.push("### Detected Patterns");
2289
- sections.push(`- Import style: **${analysis.patterns.importStyle}**`);
2290
- sections.push(`- Test framework: ${analysis.patterns.testFramework ?? "none detected"}`);
2291
- sections.push(`- Linter: ${analysis.patterns.linter ?? "none detected"}`);
2292
- sections.push(`- Formatter: ${analysis.patterns.formatter ?? "none detected"}`);
2293
- sections.push(`- Build tool: ${analysis.patterns.buildTool ?? "none detected"}`);
2294
- sections.push("");
2295
- if (analysis.existingUtils.length > 0) {
2296
- sections.push("### Existing Utilities (reuse before creating new ones)");
2297
- for (const util of analysis.existingUtils) {
2298
- sections.push(`- **\`${util.file}\`**: ${util.exports.map((e) => `\`${e}\``).join(", ")}`);
2299
- }
2300
- sections.push("");
2301
- }
2302
- sections.push("### Key Guidance");
2303
- if (analysis.patterns.importStyle === "esm") {
2304
- sections.push("- Use ESM imports (`import`/`export`). Use `.js` extensions in relative imports if TypeScript with bundler resolution.");
2305
- } else if (analysis.patterns.importStyle === "commonjs") {
2306
- sections.push("- Use CommonJS (`require`/`module.exports`).");
2307
- } else {
2308
- sections.push("- Mixed import styles detected. Prefer the dominant style in the module you are editing.");
2309
- }
2310
- if (analysis.packageJson) {
2311
- sections.push("- Do NOT run `npm install` for packages already listed in dependencies or devDependencies.");
2312
- }
2313
- if (analysis.patterns.testFramework) {
2314
- sections.push(`- Write tests using **${analysis.patterns.testFramework}** (already installed).`);
2315
- }
2316
- if (analysis.patterns.buildTool) {
2317
- sections.push(`- Build with **${analysis.patterns.buildTool}** (already configured).`);
2318
- }
2319
- return sections.join("\n");
2320
- }
2321
-
2322
777
  // src/orchestrator/orchestrator.ts
2323
778
  var DEPARTMENT_KEYWORDS = {
2324
779
  architecture: [
@@ -2460,69 +915,46 @@ var Orchestrator = class {
2460
915
  }
2461
916
  // ---- start meeting ------------------------------------------------------
2462
917
  /**
2463
- * Start a new meeting.
918
+ * Create a new meeting record and return meeting info + recommended leaders.
2464
919
  *
2465
- * 1. Selects departments (if not provided).
2466
- * 2. Creates the meeting record in SQLite.
2467
- * 3. Spawns leaders via the LeaderPool.
2468
- * 4. Runs the MeetingRunner lifecycle (opening -> discussion -> synthesis -> minutes).
2469
- * 5. Deactivates leaders after completion.
2470
- *
2471
- * Returns the meeting ID.
920
+ * Does NOT run the meeting. The calling agent (via skill markdown) is
921
+ * responsible for orchestrating the meeting phases using add-transcript
922
+ * and generate-minutes MCP tools.
2472
923
  */
2473
- async startMeeting(opts) {
924
+ startMeeting(opts) {
2474
925
  const { topic, agenda } = opts;
2475
926
  const departments = opts.departments ?? this.selectLeaders(topic, agenda);
2476
- logger.info(`Starting meeting: "${topic}" with departments: [${departments.join(", ")}]`);
2477
- let projectContext;
2478
- try {
2479
- const analysis = await analyzeProject(process.cwd());
2480
- projectContext = formatProjectContext(analysis);
2481
- logger.debug("Project analysis complete", { meetingId: topic });
2482
- } catch (err) {
2483
- const msg = err instanceof Error ? err.message : String(err);
2484
- logger.warn(`Project analysis failed (proceeding without context): ${msg}`);
2485
- }
927
+ logger.info(`Creating meeting: "${topic}" with departments: [${departments.join(", ")}]`);
2486
928
  const meetingId = uuidv46();
2487
- const meeting = createMeeting({
929
+ createMeeting({
2488
930
  id: meetingId,
2489
931
  topic,
2490
932
  agenda,
2491
- participantIds: [],
2492
- // Will be filled in after leader spawning
933
+ participantIds: departments.map((d) => `${d}-leader`),
2493
934
  initiatedBy: this.orchestratorId,
2494
- status: "convening",
2495
- phase: "convening",
935
+ status: "pending",
936
+ phase: "orchestrator-phase",
2496
937
  startedAt: Date.now(),
2497
938
  previousMeetingId: opts.previousMeetingId ?? null
2498
939
  });
2499
- const leaders = [];
2500
- for (const dept of departments) {
2501
- const leader = this.leaderPool.spawnLeader(dept, meetingId);
2502
- leaders.push(leader);
2503
- }
2504
- updateMeeting(meetingId, {
2505
- participantIds: leaders.map((l) => l.id)
2506
- });
2507
- const runner = new MeetingRunner(meetingId, leaders, projectContext);
2508
- try {
2509
- await runner.run();
2510
- } finally {
2511
- for (const leader of leaders) {
2512
- this.leaderPool.deactivateLeader(leader.id);
2513
- }
2514
- }
2515
- return meetingId;
940
+ return {
941
+ meetingId,
942
+ departments,
943
+ agenda,
944
+ topic
945
+ };
2516
946
  }
2517
947
  // ---- chain meeting -------------------------------------------------------
2518
948
  /**
2519
- * Chain a new meeting from the output of a previous meeting.
949
+ * Create a new meeting chained from a previous meeting.
2520
950
  *
2521
951
  * Loads minutes from the previous meeting and includes them as context for
2522
952
  * the new meeting topic. The new meeting's `previousMeetingId` is set for
2523
953
  * traceability.
954
+ *
955
+ * Does NOT run the meeting (same as startMeeting).
2524
956
  */
2525
- async chainMeeting(opts) {
957
+ chainMeeting(opts) {
2526
958
  const previousMeeting = getMeeting(opts.previousMeetingId);
2527
959
  if (!previousMeeting) {
2528
960
  throw new Error(`Previous meeting not found: ${opts.previousMeetingId}`);
@@ -2592,18 +1024,11 @@ async function startMeetingHandler({
2592
1024
  }) {
2593
1025
  try {
2594
1026
  const orchestrator = new Orchestrator();
2595
- const meetingId = await orchestrator.startMeeting({
1027
+ const result = orchestrator.startMeeting({
2596
1028
  topic,
2597
1029
  agenda,
2598
1030
  departments
2599
1031
  });
2600
- const result = {
2601
- meetingId,
2602
- status: "started",
2603
- topic,
2604
- agenda,
2605
- departments: departments ?? orchestrator.selectLeaders(topic, agenda)
2606
- };
2607
1032
  return {
2608
1033
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2609
1034
  };
@@ -3006,219 +1431,6 @@ async function compactMinutesHandler({
3006
1431
 
3007
1432
  // src/tools/execute-tasks.ts
3008
1433
  import { z as z5 } from "zod";
3009
-
3010
- // src/orchestrator/worker-manager.ts
3011
- async function executeWorkerAgent(worker) {
3012
- const department = inferDepartment(worker);
3013
- const workerRole = inferWorkerRole(worker);
3014
- const agentConfig = createAgentConfig({
3015
- tier: "worker",
3016
- role: workerRole,
3017
- department,
3018
- task: worker.taskDescription,
3019
- context: worker.inputContext ?? void 0
3020
- });
3021
- const systemPrompt = buildWorkerPrompt({
3022
- workerType: workerRole,
3023
- department,
3024
- task: worker.taskDescription,
3025
- context: worker.inputContext ?? void 0
3026
- });
3027
- const prompt = `Execute the following task:
3028
-
3029
- Task: ${worker.taskDescription}
3030
- ` + (worker.inputContext ? `
3031
- Context: ${worker.inputContext}
3032
- ` : "") + `
3033
- Provide a clear, structured result.`;
3034
- const invokeOpts = buildInvokeOptions(agentConfig, prompt, systemPrompt);
3035
- invokeOpts.timeoutMs = 3e5;
3036
- const result = await invokeClaude(invokeOpts);
3037
- if (!result.success) {
3038
- throw new Error(result.error ?? "Worker agent invocation failed");
3039
- }
3040
- return result.output;
3041
- }
3042
- function inferDepartment(worker) {
3043
- switch (worker.taskType) {
3044
- case "research":
3045
- return "research";
3046
- case "testing":
3047
- return "qa";
3048
- case "analysis":
3049
- return "architecture";
3050
- case "implementation":
3051
- default:
3052
- return "engineering";
3053
- }
3054
- }
3055
- function inferWorkerRole(worker) {
3056
- const desc = worker.taskDescription.toLowerCase();
3057
- if (desc.includes("schema") || desc.includes("data model")) return "schema-designer";
3058
- if (desc.includes("api") || desc.includes("endpoint")) return "api-designer";
3059
- if (desc.includes("dependency") || desc.includes("coupling")) return "dependency-analyzer";
3060
- if (desc.includes("test")) return "test-writer";
3061
- if (desc.includes("security") || desc.includes("audit")) return "security-auditor";
3062
- if (desc.includes("performance") || desc.includes("benchmark")) return "perf-tester";
3063
- if (desc.includes("research") || desc.includes("explore") || desc.includes("investigate")) return "code-explorer";
3064
- if (desc.includes("document") || desc.includes("search")) return "doc-searcher";
3065
- if (desc.includes("fix") || desc.includes("bug")) return "bug-fixer";
3066
- if (desc.includes("refactor")) return "refactorer";
3067
- return "feature-dev";
3068
- }
3069
- function inferTaskType(description) {
3070
- const lower = description.toLowerCase();
3071
- if (lower.includes("research") || lower.includes("explore") || lower.includes("search")) {
3072
- return "research";
3073
- }
3074
- if (lower.includes("test") || lower.includes("audit") || lower.includes("benchmark")) {
3075
- return "testing";
3076
- }
3077
- if (lower.includes("analys") || lower.includes("design") || lower.includes("schema")) {
3078
- return "analysis";
3079
- }
3080
- return "implementation";
3081
- }
3082
- var WorkerManager = class {
3083
- // ---- spawn workers ------------------------------------------------------
3084
- /**
3085
- * Spawn a batch of workers on behalf of a leader.
3086
- *
3087
- * Each task assignment is persisted to SQLite and an `agent_spawned` event is
3088
- * emitted. The returned records are in `pending` status.
3089
- */
3090
- async spawnWorkers(leaderId, meetingId, tasks) {
3091
- const workers = [];
3092
- for (const task of tasks) {
3093
- const worker = createWorker({
3094
- leaderId,
3095
- meetingId,
3096
- taskDescription: task.description,
3097
- taskType: inferTaskType(task.description),
3098
- inputContext: task.inputPaths.length > 0 ? task.inputPaths.join(", ") : null,
3099
- outputResult: null,
3100
- errorMessage: null,
3101
- dependencies: task.dependencies
3102
- });
3103
- workers.push(worker);
3104
- logger.info(`Spawned worker for leader ${leaderId}`, {
3105
- agentId: worker.id,
3106
- meetingId,
3107
- department: "worker"
3108
- });
3109
- eventBus.emitAgentEvent({
3110
- kind: "agent_spawned",
3111
- agentId: worker.id,
3112
- agentType: "worker",
3113
- parentId: leaderId,
3114
- label: task.description.slice(0, 60),
3115
- department: "engineering"
3116
- // Workers inherit; refined later if needed
3117
- });
3118
- }
3119
- return workers;
3120
- }
3121
- // ---- execute workers ----------------------------------------------------
3122
- /**
3123
- * Execute a list of workers, respecting dependency ordering.
3124
- *
3125
- * - Workers with no unresolved dependencies run in parallel.
3126
- * - Workers whose dependencies are all completed run next.
3127
- * - Continues until all workers are done or a dependency cycle is detected.
3128
- */
3129
- async executeWorkers(workers) {
3130
- const completed = /* @__PURE__ */ new Set();
3131
- const results = [];
3132
- const pending = new Map(workers.map((w) => [w.id, w]));
3133
- while (pending.size > 0) {
3134
- const ready = [];
3135
- for (const worker of pending.values()) {
3136
- const depsReady = worker.dependencies.every((dep) => completed.has(dep));
3137
- if (depsReady) {
3138
- ready.push(worker);
3139
- }
3140
- }
3141
- if (ready.length === 0) {
3142
- logger.warn("Dependency cycle or unsatisfiable deps detected; failing remaining workers");
3143
- for (const worker of pending.values()) {
3144
- const failed = updateWorker(worker.id, {
3145
- status: "failed",
3146
- errorMessage: "Unresolvable dependency",
3147
- completedAt: Date.now()
3148
- });
3149
- results.push(failed ?? worker);
3150
- eventBus.emitAgentEvent({
3151
- kind: "task_completed",
3152
- agentId: worker.id,
3153
- result: "failure"
3154
- });
3155
- }
3156
- break;
3157
- }
3158
- const batchResults = await Promise.allSettled(
3159
- ready.map(async (worker) => {
3160
- updateWorker(worker.id, { status: "running" });
3161
- eventBus.emitAgentEvent({
3162
- kind: "state_changed",
3163
- agentId: worker.id,
3164
- from: "idle",
3165
- to: "working"
3166
- });
3167
- try {
3168
- const output = await executeWorkerAgent(worker);
3169
- const updated = updateWorker(worker.id, {
3170
- status: "completed",
3171
- outputResult: output,
3172
- completedAt: Date.now(),
3173
- costUsd: 0.01
3174
- // Approximate cost; real cost tracked by CLI
3175
- });
3176
- eventBus.emitAgentEvent({
3177
- kind: "task_completed",
3178
- agentId: worker.id,
3179
- result: "success"
3180
- });
3181
- return updated ?? worker;
3182
- } catch (err) {
3183
- const errorMsg = err instanceof Error ? err.message : String(err);
3184
- const updated = updateWorker(worker.id, {
3185
- status: "failed",
3186
- errorMessage: errorMsg,
3187
- completedAt: Date.now()
3188
- });
3189
- eventBus.emitAgentEvent({
3190
- kind: "task_completed",
3191
- agentId: worker.id,
3192
- result: "failure"
3193
- });
3194
- return updated ?? worker;
3195
- }
3196
- })
3197
- );
3198
- for (let i = 0; i < ready.length; i++) {
3199
- const worker = ready[i];
3200
- pending.delete(worker.id);
3201
- completed.add(worker.id);
3202
- const settlement = batchResults[i];
3203
- if (settlement.status === "fulfilled") {
3204
- results.push(settlement.value);
3205
- } else {
3206
- results.push(worker);
3207
- }
3208
- }
3209
- }
3210
- return results;
3211
- }
3212
- // ---- status query -------------------------------------------------------
3213
- /**
3214
- * Get the current status of all workers under a given leader.
3215
- */
3216
- getWorkerStatus(leaderId) {
3217
- return listWorkersByLeader(leaderId);
3218
- }
3219
- };
3220
-
3221
- // src/tools/execute-tasks.ts
3222
1434
  var executeTasksSchema = {
3223
1435
  meetingId: z5.string().describe("Meeting ID"),
3224
1436
  taskIds: z5.array(z5.string()).optional().describe("Specific tasks to execute, or all")
@@ -3270,58 +1482,15 @@ async function executeTasksHandler({
3270
1482
  }
3271
1483
  tasksByDepartment.get(dept).push(task);
3272
1484
  }
3273
- const workerManager = new WorkerManager();
3274
- const executionResults = [];
1485
+ const departments = [];
3275
1486
  for (const [department, tasks] of tasksByDepartment) {
3276
- const assignments = tasks.map((task) => ({
3277
- workerId: task.id,
3278
- description: `${task.title}: ${task.description}`,
3279
- inputPaths: [],
3280
- outputPath: "",
3281
- dependencies: task.dependencies,
3282
- status: "pending",
3283
- result: null
3284
- }));
3285
- const leaderId = `${department}-leader-${meetingId}`;
3286
- const workers = await workerManager.spawnWorkers(
3287
- leaderId,
3288
- meetingId,
3289
- assignments
3290
- );
3291
- const completedWorkers = await workerManager.executeWorkers(workers);
3292
- executionResults.push({
3293
- department,
3294
- taskCount: tasks.length,
3295
- workers: completedWorkers.map((w) => ({
3296
- id: w.id,
3297
- status: w.status,
3298
- taskDescription: w.taskDescription,
3299
- outputResult: w.outputResult,
3300
- errorMessage: w.errorMessage
3301
- }))
3302
- });
1487
+ departments.push({ department, tasks });
3303
1488
  }
3304
- const totalWorkers = executionResults.reduce(
3305
- (acc, r) => acc + r.workers.length,
3306
- 0
3307
- );
3308
- const completedCount = executionResults.reduce(
3309
- (acc, r) => acc + r.workers.filter((w) => w.status === "completed").length,
3310
- 0
3311
- );
3312
- const failedCount = executionResults.reduce(
3313
- (acc, r) => acc + r.workers.filter((w) => w.status === "failed").length,
3314
- 0
3315
- );
3316
1489
  const result = {
3317
1490
  meetingId,
3318
- summary: {
3319
- totalTasks: tasksToExecute.length,
3320
- totalWorkers,
3321
- completed: completedCount,
3322
- failed: failedCount
3323
- },
3324
- departments: executionResults
1491
+ totalTasks: tasksToExecute.length,
1492
+ departments,
1493
+ instructions: "These tasks should be dispatched to implementer agents. The orchestrator agent will use Claude Code Agent tool to run each task."
3325
1494
  };
3326
1495
  return {
3327
1496
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
@@ -3767,19 +1936,19 @@ async function getTaskReportHandler({
3767
1936
  import { z as z11 } from "zod";
3768
1937
 
3769
1938
  // src/extension/capability-registry.ts
3770
- import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2 } from "fs";
3771
- import { join as join3 } from "path";
1939
+ import { readFileSync, writeFileSync, existsSync } from "fs";
1940
+ import { join as join2 } from "path";
3772
1941
 
3773
1942
  // src/utils/config.ts
3774
1943
  import { mkdirSync } from "fs";
3775
1944
  import { homedir } from "os";
3776
- import { join as join2 } from "path";
1945
+ import { join } from "path";
3777
1946
  function buildConfig() {
3778
- const DATA_DIR = join2(homedir(), ".open-coleslaw");
1947
+ const DATA_DIR = join(homedir(), ".open-coleslaw");
3779
1948
  return {
3780
1949
  DATA_DIR,
3781
- DB_PATH: join2(DATA_DIR, "data.db"),
3782
- MINUTES_DIR: join2(DATA_DIR, "minutes"),
1950
+ DB_PATH: join(DATA_DIR, "data.db"),
1951
+ MINUTES_DIR: join(DATA_DIR, "minutes"),
3783
1952
  DASHBOARD_PORT: 35143
3784
1953
  };
3785
1954
  }
@@ -3902,7 +2071,7 @@ var CapabilityRegistry = class {
3902
2071
  registryPath;
3903
2072
  constructor() {
3904
2073
  const { DATA_DIR } = getConfig();
3905
- this.registryPath = join3(DATA_DIR, "registry.json");
2074
+ this.registryPath = join2(DATA_DIR, "registry.json");
3906
2075
  }
3907
2076
  /**
3908
2077
  * Load all capabilities: built-in (hardcoded) + custom (from registry.json).
@@ -3977,11 +2146,11 @@ ${lines.join("\n")}`);
3977
2146
  // Private helpers
3978
2147
  // -------------------------------------------------------------------------
3979
2148
  readCustomEntries() {
3980
- if (!existsSync2(this.registryPath)) {
2149
+ if (!existsSync(this.registryPath)) {
3981
2150
  return [];
3982
2151
  }
3983
2152
  try {
3984
- const raw = readFileSync2(this.registryPath, "utf-8");
2153
+ const raw = readFileSync(this.registryPath, "utf-8");
3985
2154
  const parsed = JSON.parse(raw);
3986
2155
  if (!Array.isArray(parsed)) return [];
3987
2156
  return parsed;
@@ -3996,7 +2165,7 @@ ${lines.join("\n")}`);
3996
2165
 
3997
2166
  // src/extension/generator.ts
3998
2167
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
3999
- import { join as join4 } from "path";
2168
+ import { join as join3 } from "path";
4000
2169
  function sanitizeName(name) {
4001
2170
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
4002
2171
  }
@@ -4013,7 +2182,7 @@ function header(request) {
4013
2182
  }
4014
2183
  function customDir(type) {
4015
2184
  const { DATA_DIR } = getConfig();
4016
- const dir = join4(DATA_DIR, `custom-${type}s`);
2185
+ const dir = join3(DATA_DIR, `custom-${type}s`);
4017
2186
  mkdirSync2(dir, { recursive: true });
4018
2187
  return dir;
4019
2188
  }
@@ -4234,7 +2403,7 @@ function generateCapability(request) {
4234
2403
  ext = ".js";
4235
2404
  }
4236
2405
  const dir = customDir(request.type);
4237
- const filePath = join4(dir, `${safeName}${ext}`);
2406
+ const filePath = join3(dir, `${safeName}${ext}`);
4238
2407
  let code;
4239
2408
  switch (request.type) {
4240
2409
  case "hook":
@@ -4269,11 +2438,11 @@ function generateCapability(request) {
4269
2438
  }
4270
2439
 
4271
2440
  // src/extension/guide-updater.ts
4272
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
4273
- import { join as join5 } from "path";
2441
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
2442
+ import { join as join4 } from "path";
4274
2443
  async function updatePluginGuide(registry) {
4275
2444
  const { DATA_DIR } = getConfig();
4276
- const guidePath = join5(DATA_DIR, "plugin-guide.md");
2445
+ const guidePath = join4(DATA_DIR, "plugin-guide.md");
4277
2446
  const capSection = registry.formatForGuide();
4278
2447
  const guide = [
4279
2448
  "# Open-Coleslaw Plugin Guide",
@@ -4666,19 +2835,249 @@ async function chainMeetingHandler({
4666
2835
  }) {
4667
2836
  try {
4668
2837
  const orchestrator = new Orchestrator();
4669
- const meetingId = await orchestrator.chainMeeting({
2838
+ const result = orchestrator.chainMeeting({
4670
2839
  previousMeetingId,
4671
2840
  topic,
4672
2841
  agenda,
4673
2842
  departments
4674
2843
  });
2844
+ return {
2845
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2846
+ };
2847
+ } catch (error) {
2848
+ const message = error instanceof Error ? error.message : String(error);
2849
+ return {
2850
+ content: [
2851
+ { type: "text", text: JSON.stringify({ error: message }, null, 2) }
2852
+ ],
2853
+ isError: true
2854
+ };
2855
+ }
2856
+ }
2857
+
2858
+ // src/tools/add-transcript.ts
2859
+ import { z as z14 } from "zod";
2860
+
2861
+ // src/orchestrator/meeting-runner.ts
2862
+ function insertTranscriptEntry(meetingId, speakerId, speakerRole, agendaItemIndex, roundNumber, content) {
2863
+ const db = getDb();
2864
+ const now = Date.now();
2865
+ const tokenCount = Math.ceil(content.length / 4);
2866
+ const result = db.prepare(
2867
+ `INSERT INTO transcript_entries
2868
+ (meeting_id, speaker_id, speaker_role, agenda_item_index, round_number, content, token_count, created_at)
2869
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
2870
+ ).run(meetingId, speakerId, speakerRole, agendaItemIndex, roundNumber, content, tokenCount, now);
2871
+ return {
2872
+ id: Number(result.lastInsertRowid),
2873
+ meetingId,
2874
+ speakerId,
2875
+ speakerRole,
2876
+ agendaItemIndex,
2877
+ roundNumber,
2878
+ content,
2879
+ tokenCount,
2880
+ createdAt: now
2881
+ };
2882
+ }
2883
+ function getTranscript(meetingId) {
2884
+ const db = getDb();
2885
+ const rows = db.prepare("SELECT * FROM transcript_entries WHERE meeting_id = ? ORDER BY created_at ASC").all(meetingId);
2886
+ return rows.map((r) => ({
2887
+ id: r.id,
2888
+ meetingId: r.meeting_id,
2889
+ speakerId: r.speaker_id,
2890
+ speakerRole: r.speaker_role,
2891
+ agendaItemIndex: r.agenda_item_index,
2892
+ roundNumber: r.round_number,
2893
+ content: r.content,
2894
+ tokenCount: r.token_count,
2895
+ createdAt: r.created_at
2896
+ }));
2897
+ }
2898
+ var MeetingRunner = class {
2899
+ meetingId;
2900
+ constructor(meetingId) {
2901
+ this.meetingId = meetingId;
2902
+ }
2903
+ // ---- Add transcript entry (called by MCP tool add-transcript) -----------
2904
+ /**
2905
+ * Add a transcript entry for this meeting.
2906
+ * Returns the created TranscriptEntry.
2907
+ */
2908
+ addTranscript(speakerRole, agendaItemIndex, roundNumber, content) {
2909
+ const meeting = getMeeting(this.meetingId);
2910
+ if (!meeting) {
2911
+ throw new Error(`Meeting not found: ${this.meetingId}`);
2912
+ }
2913
+ const entry = insertTranscriptEntry(
2914
+ this.meetingId,
2915
+ speakerRole,
2916
+ speakerRole,
2917
+ agendaItemIndex,
2918
+ roundNumber,
2919
+ content
2920
+ );
2921
+ logger.debug(`Transcript added: ${speakerRole} (item ${agendaItemIndex}, round ${roundNumber})`, {
2922
+ meetingId: this.meetingId
2923
+ });
2924
+ return entry;
2925
+ }
2926
+ // ---- Generate minutes from all transcripts ------------------------------
2927
+ /**
2928
+ * Generate meeting minutes by formatting all stored transcript entries.
2929
+ * Returns the minutesId of the created minutes record.
2930
+ */
2931
+ async generateMinutes() {
2932
+ const meeting = getMeeting(this.meetingId);
2933
+ if (!meeting) {
2934
+ throw new Error(`Meeting not found: ${this.meetingId}`);
2935
+ }
2936
+ updateMeeting(this.meetingId, {
2937
+ phase: "minutes-generation",
2938
+ status: "minutes-generation"
2939
+ });
2940
+ const transcript = getTranscript(this.meetingId);
2941
+ const sections = [];
2942
+ const speakerRoles = [...new Set(transcript.map((e) => e.speakerRole))];
2943
+ sections.push(`# Meeting Minutes`);
2944
+ sections.push(`## Topic: ${meeting.topic}`);
2945
+ sections.push(`## Date: ${(/* @__PURE__ */ new Date()).toISOString()}`);
2946
+ sections.push(`## Participants: ${speakerRoles.join(", ")}`);
2947
+ sections.push("");
2948
+ sections.push(`## Agenda`);
2949
+ meeting.agenda.forEach((item, i) => {
2950
+ sections.push(`${i + 1}. ${item}`);
2951
+ });
2952
+ sections.push("");
2953
+ const openingEntries = transcript.filter((e) => e.agendaItemIndex === -1);
2954
+ if (openingEntries.length > 0) {
2955
+ sections.push(`## Opening Statements`);
2956
+ for (const entry of openingEntries) {
2957
+ sections.push(`### ${entry.speakerRole}`);
2958
+ sections.push(entry.content);
2959
+ sections.push("");
2960
+ }
2961
+ }
2962
+ for (let i = 0; i < meeting.agenda.length; i++) {
2963
+ const itemEntries = transcript.filter((e) => e.agendaItemIndex === i);
2964
+ if (itemEntries.length > 0) {
2965
+ sections.push(`## Discussion: ${meeting.agenda[i]}`);
2966
+ for (const entry of itemEntries) {
2967
+ sections.push(`**${entry.speakerRole}** (round ${entry.roundNumber}):`);
2968
+ sections.push(entry.content);
2969
+ sections.push("");
2970
+ }
2971
+ }
2972
+ }
2973
+ const synthesisEntries = transcript.filter((e) => e.agendaItemIndex === -2);
2974
+ if (synthesisEntries.length > 0) {
2975
+ sections.push(`## Final Positions`);
2976
+ for (const entry of synthesisEntries) {
2977
+ sections.push(`### ${entry.speakerRole}`);
2978
+ sections.push(entry.content);
2979
+ sections.push("");
2980
+ }
2981
+ }
2982
+ const content = sections.join("\n");
2983
+ const actionItems = speakerRoles.map((role, idx) => ({
2984
+ id: `action-${this.meetingId}-${idx}`,
2985
+ title: `${role} deliverables`,
2986
+ description: `Action items committed by ${role} during synthesis phase`,
2987
+ assignedDepartment: "engineering",
2988
+ // default; will be refined by compactor
2989
+ assignedRole: role,
2990
+ priority: "medium",
2991
+ dependencies: [],
2992
+ acceptanceCriteria: ["Deliverables completed as stated in final position"]
2993
+ }));
2994
+ const minutesRecord = createMinutes({
2995
+ meetingId: this.meetingId,
2996
+ format: "summary",
2997
+ content,
2998
+ actionItems
2999
+ });
3000
+ updateMeeting(this.meetingId, {
3001
+ status: "completed",
3002
+ completedAt: Date.now()
3003
+ });
3004
+ logger.info("Minutes generated", { meetingId: this.meetingId });
3005
+ return minutesRecord.id;
3006
+ }
3007
+ // ---- Completion status --------------------------------------------------
3008
+ /**
3009
+ * Check whether the meeting has been completed (minutes generated).
3010
+ */
3011
+ isComplete() {
3012
+ const meeting = getMeeting(this.meetingId);
3013
+ if (!meeting) return false;
3014
+ return ["completed", "compacted", "reported"].includes(meeting.status);
3015
+ }
3016
+ // ---- Transcript access --------------------------------------------------
3017
+ /**
3018
+ * Get all transcript entries for this meeting.
3019
+ */
3020
+ getTranscript() {
3021
+ return getTranscript(this.meetingId);
3022
+ }
3023
+ };
3024
+
3025
+ // src/tools/add-transcript.ts
3026
+ var addTranscriptSchema = {
3027
+ meetingId: z14.string().describe("Meeting ID"),
3028
+ speakerRole: z14.string().describe('Role of the speaker (e.g. "Architecture Lead", "Engineering Lead")'),
3029
+ agendaItemIndex: z14.number().describe("Agenda item index (0-based). Use -1 for opening statements, -2 for synthesis"),
3030
+ roundNumber: z14.number().describe("Discussion round number (0 for opening/synthesis)"),
3031
+ content: z14.string().describe("The transcript content from the speaker")
3032
+ };
3033
+ async function addTranscriptHandler({
3034
+ meetingId,
3035
+ speakerRole,
3036
+ agendaItemIndex,
3037
+ roundNumber,
3038
+ content
3039
+ }) {
3040
+ try {
3041
+ const runner = new MeetingRunner(meetingId);
3042
+ const entry = runner.addTranscript(speakerRole, agendaItemIndex, roundNumber, content);
4675
3043
  const result = {
3044
+ success: true,
3045
+ entryId: entry.id,
4676
3046
  meetingId,
4677
- status: "started",
4678
- chainedFrom: previousMeetingId,
4679
- topic,
4680
- agenda,
4681
- departments: departments ?? orchestrator.selectLeaders(topic, agenda)
3047
+ speakerRole,
3048
+ agendaItemIndex,
3049
+ roundNumber
3050
+ };
3051
+ return {
3052
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
3053
+ };
3054
+ } catch (error) {
3055
+ const message = error instanceof Error ? error.message : String(error);
3056
+ return {
3057
+ content: [
3058
+ { type: "text", text: JSON.stringify({ error: message }, null, 2) }
3059
+ ],
3060
+ isError: true
3061
+ };
3062
+ }
3063
+ }
3064
+
3065
+ // src/tools/generate-minutes.ts
3066
+ import { z as z15 } from "zod";
3067
+ var generateMinutesSchema = {
3068
+ meetingId: z15.string().describe("Meeting ID to generate minutes for")
3069
+ };
3070
+ async function generateMinutesHandler({
3071
+ meetingId
3072
+ }) {
3073
+ try {
3074
+ const runner = new MeetingRunner(meetingId);
3075
+ const minutesId = await runner.generateMinutes();
3076
+ const minutes = getMinutesByMeeting(meetingId);
3077
+ const result = {
3078
+ minutesId,
3079
+ meetingId,
3080
+ content: minutes?.content ?? ""
4682
3081
  };
4683
3082
  return {
4684
3083
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
@@ -4702,7 +3101,7 @@ function createServer() {
4702
3101
  });
4703
3102
  server.tool(
4704
3103
  "start-meeting",
4705
- "Start a new multi-agent meeting on a topic with agenda items",
3104
+ "Create a new meeting record with topic and agenda. Returns meetingId and recommended departments. Does NOT run the meeting.",
4706
3105
  startMeetingSchema,
4707
3106
  startMeetingHandler
4708
3107
  );
@@ -4726,7 +3125,7 @@ function createServer() {
4726
3125
  );
4727
3126
  server.tool(
4728
3127
  "execute-tasks",
4729
- "Execute tasks from compacted minutes by spawning workers per department",
3128
+ "Get the task list from compacted minutes for agent dispatch. Does NOT spawn workers.",
4730
3129
  executeTasksSchema,
4731
3130
  executeTasksHandler
4732
3131
  );
@@ -4779,10 +3178,22 @@ function createServer() {
4779
3178
  );
4780
3179
  server.tool(
4781
3180
  "chain-meeting",
4782
- "Start a new meeting chained from a previous meeting, using its minutes as context",
3181
+ "Create a new meeting chained from a previous meeting, using its minutes as context. Does NOT run the meeting.",
4783
3182
  chainMeetingSchema,
4784
3183
  chainMeetingHandler
4785
3184
  );
3185
+ server.tool(
3186
+ "add-transcript",
3187
+ "Add a transcript entry to a meeting. Used to record speaker contributions during meeting phases.",
3188
+ addTranscriptSchema,
3189
+ addTranscriptHandler
3190
+ );
3191
+ server.tool(
3192
+ "generate-minutes",
3193
+ "Generate PRD minutes from all stored transcripts for a meeting. Marks the meeting as completed.",
3194
+ generateMinutesSchema,
3195
+ generateMinutesHandler
3196
+ );
4786
3197
  return server;
4787
3198
  }
4788
3199