heyio 1.1.6 → 1.2.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.
@@ -28,11 +28,22 @@ export async function delegateTask(squadId, task, instanceId) {
28
28
  .join("\n");
29
29
  const systemMessage = `# Squad Team Lead: ${lead.character_name}
30
30
 
31
- You are ${lead.character_name}, the team lead for this squad. Your role is to:
32
- 1. Break down tasks into smaller pieces
33
- 2. Route work to the appropriate specialist
31
+ You are ${lead.character_name}, the team lead for this squad. Your role is STRICTLY coordination — you do NOT write code, tests, or implementation of any kind.
32
+
33
+ ## Your Responsibilities:
34
+ 1. Break down tasks into smaller pieces and delegate to specialists
35
+ 2. Route work to the appropriate specialist based on their role
34
36
  3. Coordinate reviews and approvals
35
37
  4. Ensure quality gates are met
38
+ 5. Report progress and blockers
39
+
40
+ ## PROHIBITED — You must NEVER:
41
+ - Write, edit, or generate code directly
42
+ - Create or modify files in the repository
43
+ - Run build/test commands to fix code (only to verify status)
44
+ - Implement any part of a task yourself
45
+
46
+ If no suitable specialist exists for a sub-task, report that back — do NOT attempt it yourself.
36
47
 
37
48
  ## Your Team:
38
49
  ${agentRoster}
@@ -40,7 +51,7 @@ ${agentRoster}
40
51
  ## Workflow Rules:
41
52
  - Peer review: QA + Test + Lead have veto power
42
53
  - Use \`--comment\` with "LGTM" for approvals (not \`--approve\`)
43
- - Always pull latest before starting code work
54
+ - Always use the gh CLI for GitHub interactions
44
55
  - Merge criteria: all veto-capable members have posted approving comments + CI passes + no conflicts
45
56
 
46
57
  ${lead.persona ? `## Personality:\n${lead.persona}` : ""}
@@ -0,0 +1,155 @@
1
+ import { approveAll } from "@github/copilot-sdk";
2
+ import { getClient } from "./client.js";
3
+ import { getLeadForSquad, getAgentsForSquad } from "../store/squads.js";
4
+ import { selectModel } from "./model-router.js";
5
+ import { postFeedItem } from "../store/feed.js";
6
+ function buildFacilitatorPrompt(lead, agents, task) {
7
+ const roster = agents
8
+ .filter((a) => !a.is_lead)
9
+ .map((a) => `- ${a.character_name} (${a.role_title})${a.is_qa ? " [QA]" : ""}${a.is_test ? " [TEST]" : ""}`)
10
+ .join("\n");
11
+ return `# Planning Meeting Facilitator: ${lead.character_name}
12
+
13
+ You are facilitating a planning meeting for your squad. Your job is to gather input from specialists, then synthesize a clear action plan.
14
+
15
+ ## The Task
16
+ ${task}
17
+
18
+ ## Your Team (Specialists)
19
+ ${roster}
20
+
21
+ ## Instructions
22
+ For each specialist who is relevant to this task, think about what their domain expertise would contribute. Then synthesize ALL perspectives into a structured plan.
23
+
24
+ Consider each relevant specialist's likely concerns:
25
+ ${agents
26
+ .filter((a) => !a.is_lead)
27
+ .map((a) => `- ${a.character_name} (${a.role_title}): What risks, technical suggestions, or constraints would they raise?`)
28
+ .join("\n")}
29
+
30
+ ## Output Format
31
+ Produce a plan in this exact format:
32
+
33
+ ### Task Summary
34
+ (One sentence summary of what we're building)
35
+
36
+ ### Plan
37
+ (Numbered list of work items with agent assignments)
38
+
39
+ ### Risks & Concerns
40
+ (Bullet list of risks identified, with mitigation strategies)
41
+
42
+ ### Dependencies
43
+ (What must happen in order, what can be parallel)
44
+
45
+ ### Assignments
46
+ (Clear mapping: Agent → what they own)
47
+
48
+ ## Rules
49
+ - You are ONLY planning — do NOT execute any work
50
+ - Assign work to specialists based on their role titles
51
+ - Identify what can be done in parallel vs what has dependencies
52
+ - Flag if any expertise is missing from the team
53
+ ${lead.persona ? `\n## Your Style:\n${lead.persona}` : ""}
54
+ `;
55
+ }
56
+ function buildSpecialistPrompt(agent, task) {
57
+ return `# Planning Input: ${agent.character_name}
58
+
59
+ You are ${agent.character_name}, a ${agent.role_title}. Your team is planning a new task and needs your expert input.
60
+
61
+ ## The Task
62
+ ${task}
63
+
64
+ ## Your Role
65
+ Provide input ONLY from your area of expertise (${agent.role_title}). Be specific and actionable.
66
+
67
+ ## Respond With
68
+ 1. **Concerns/Risks**: What could go wrong in your domain?
69
+ 2. **Technical Suggestions**: How would you approach your part?
70
+ 3. **Dependencies**: What do you need from other team members before you can start?
71
+ 4. **Estimated Complexity**: Simple / Moderate / Complex for your portion
72
+ 5. **Questions**: Anything unclear that affects your work?
73
+
74
+ Keep your response focused and concise — this is a planning meeting, not implementation.
75
+ ${agent.persona ? `\n## Your Style:\n${agent.persona}` : ""}
76
+ `;
77
+ }
78
+ export async function planningMeeting(squadId, task) {
79
+ const lead = getLeadForSquad(squadId);
80
+ if (!lead) {
81
+ throw new Error("Squad has no team lead. Add a lead agent first.");
82
+ }
83
+ const agents = getAgentsForSquad(squadId);
84
+ const relevantAgents = agents.filter((a) => !a.is_lead);
85
+ if (relevantAgents.length === 0) {
86
+ throw new Error("Squad has no specialists to consult. Add agents first.");
87
+ }
88
+ const client = await getClient();
89
+ // Phase 1: Gather specialist input in parallel
90
+ const specialistInputs = await Promise.allSettled(relevantAgents.map(async (agent) => {
91
+ const model = await selectModel("low");
92
+ const session = await client.createSession({
93
+ model,
94
+ streaming: true,
95
+ workingDirectory: process.cwd(),
96
+ systemMessage: { content: buildSpecialistPrompt(agent, task) },
97
+ onPermissionRequest: approveAll,
98
+ });
99
+ try {
100
+ const response = await session.sendAndWait({ prompt: "Please provide your planning input for this task." }, 60_000);
101
+ return {
102
+ agent: agent.character_name,
103
+ role: agent.role_title,
104
+ input: response?.data?.content ?? "(no response)",
105
+ };
106
+ }
107
+ finally {
108
+ await session.disconnect();
109
+ }
110
+ }));
111
+ // Collect successful inputs
112
+ const inputs = specialistInputs
113
+ .filter((r) => r.status === "fulfilled")
114
+ .map((r) => r.value);
115
+ const inputsSummary = inputs
116
+ .map((i) => `### ${i.agent} (${i.role})\n${i.input}`)
117
+ .join("\n\n");
118
+ // Phase 2: Lead synthesizes the plan
119
+ const facilitatorModel = await selectModel("medium");
120
+ const facilitatorSession = await client.createSession({
121
+ model: facilitatorModel,
122
+ streaming: true,
123
+ workingDirectory: process.cwd(),
124
+ systemMessage: { content: buildFacilitatorPrompt(lead, agents, task) },
125
+ onPermissionRequest: approveAll,
126
+ });
127
+ let plan;
128
+ try {
129
+ const prompt = `Here is the input gathered from your team:\n\n${inputsSummary}\n\nNow synthesize this into a clear, structured action plan.`;
130
+ const response = await facilitatorSession.sendAndWait({ prompt }, 120_000);
131
+ plan = response?.data?.content ?? "Planning meeting completed but no plan was produced.";
132
+ }
133
+ finally {
134
+ await facilitatorSession.disconnect();
135
+ }
136
+ return {
137
+ plan,
138
+ participants: [lead.character_name, ...inputs.map((i) => i.agent)],
139
+ };
140
+ }
141
+ export async function squadMeeting(squadId, task, executeAfter) {
142
+ const result = await planningMeeting(squadId, task);
143
+ const summary = `## Planning Meeting Complete\n\n**Participants:** ${result.participants.join(", ")}\n\n${result.plan}`;
144
+ if (!executeAfter) {
145
+ // Post to feed and wait for user to trigger execution
146
+ postFeedItem(`squad-${squadId}`, "Planning meeting complete — awaiting approval", summary);
147
+ return summary;
148
+ }
149
+ // Execute: delegate with the plan as additional context
150
+ const { delegateTask } = await import("./agents.js");
151
+ const enrichedTask = `${task}\n\n---\n## Approved Plan (from team meeting)\n${result.plan}`;
152
+ const execResult = await delegateTask(squadId, enrichedTask);
153
+ return `Meeting held, then executed.\n\n${summary}\n\n---\n## Execution Result\n${execResult}`;
154
+ }
155
+ //# sourceMappingURL=ceremonies.js.map
@@ -26,13 +26,19 @@ You are IO, a personal AI assistant daemon. You run 24/7 on the user's machine,
26
26
  - Never delegate unless explicitly asked — creating an issue ≠ request to start work
27
27
  - When creating squads, research the universe dynamically — never use hardcoded character lists
28
28
  - **Always use the gh CLI** for all GitHub interactions (repos, issues, PRs, releases, actions, etc.). Only fall back to the GitHub API or other methods if gh is unavailable or cannot accomplish the task.
29
+ - For complex tasks involving multiple specialists, use squad_meeting to have the team plan together before executing. Use squad_delegate for straightforward single-domain tasks.
30
+ - If the user says "plan this" or "have the team meet" → use squad_meeting with execute_after=false
31
+ - If the user says "do this" for a complex task → use squad_meeting with execute_after=true
32
+ - If the user says "just do it" or it's a simple task → use squad_delegate directly
29
33
 
30
34
  ## Squad Coverage Requirements
31
35
  Every squad MUST have:
32
- 1. A dedicated team lead (PM/Senior Engineer, coordination-only)
36
+ 1. A dedicated team lead (PM/Senior Engineer, coordination-only — **never writes code**)
33
37
  2. At least one QA reviewer
34
38
  3. At least one agent with a test/quality role title
35
39
 
40
+ Team leads are strictly managers/delegators/reviewers. They break down tasks, assign work to specialists, coordinate reviews, and report status — but they NEVER write, edit, or generate code themselves.
41
+
36
42
  ## GitHub Self-Review Limitation
37
43
  All squad agents share the repo owner's gh identity. GitHub blocks self-approval. Veto reviewers use --comment with "LGTM" instead of --approve. Merge criteria: all veto-capable members have posted approving comments + CI passes + no conflicts.
38
44
  ${selfEditBlock}
@@ -134,7 +134,7 @@ export function createTools() {
134
134
  },
135
135
  }),
136
136
  defineTool("squad_delegate", {
137
- description: "Delegate a task to a squad's team lead. The lead will break it down and route to specialists.",
137
+ description: "Delegate a task to a squad's team lead. The lead will break it down and route to specialists. Use this for direct execution without a planning meeting.",
138
138
  parameters: z.object({
139
139
  squad_id: z.string().describe("Squad ID"),
140
140
  task: z.string().describe("Detailed task description"),
@@ -146,6 +146,20 @@ export function createTools() {
146
146
  return result;
147
147
  },
148
148
  }),
149
+ defineTool("squad_meeting", {
150
+ description: "Trigger a planning meeting for a squad. All relevant specialists provide input before work begins. Use this for complex multi-agent tasks where team input improves the plan. Set execute_after=true to start work immediately after planning, or false to post the plan to the feed for user review.",
151
+ parameters: z.object({
152
+ squad_id: z.string().describe("Squad ID"),
153
+ task: z.string().describe("Detailed task description for the team to plan"),
154
+ execute_after: z
155
+ .boolean()
156
+ .describe("If true, execute the plan immediately after the meeting. If false, post plan to feed and wait for user approval."),
157
+ }),
158
+ handler: async ({ squad_id, task, execute_after }) => {
159
+ const { squadMeeting } = await import("./ceremonies.js");
160
+ return await squadMeeting(squad_id, task, execute_after);
161
+ },
162
+ }),
149
163
  defineTool("squad_task_status", {
150
164
  description: "Check the status of tasks for a squad",
151
165
  parameters: z.object({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "heyio",
3
- "version": "1.1.6",
3
+ "version": "1.2.0",
4
4
  "description": "IO — a personal AI assistant daemon built on the GitHub Copilot SDK",
5
5
  "bin": {
6
6
  "io": "dist/index.js"